From a1d29ddc5289736d706a646449ee8af1b4cb38f9 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste THERY Date: Mon, 29 Jun 2026 01:04:22 +0700 Subject: [PATCH] feat(tts): add edge-quality audio path --- AGENTS.md | 9 +- CHANGELOG.md | 8 + CLAUDE.md | 5 +- README.md | 4 +- SECURITY-HARDENING.md | 26 ++- package.json | 2 +- packages/mimir-tts/LICENSE | 21 -- packages/mimir-tts/README.md | 37 +++- packages/mimir-tts/dist/cli.js | 38 +++- packages/mimir-tts/dist/cli.js.map | 2 +- packages/mimir-tts/dist/index.d.ts | 26 ++- packages/mimir-tts/dist/index.d.ts.map | 2 +- packages/mimir-tts/dist/index.js | 130 +++++++++++- packages/mimir-tts/dist/index.js.map | 2 +- packages/mimir-tts/package.json | 8 +- packages/mimir-tts/src/cli.ts | 44 +++- packages/mimir-tts/src/index.test.ts | 56 ++++- packages/mimir-tts/src/index.ts | 182 +++++++++++++++- packages/mimir/CHANGELOG.md | 47 ----- packages/mimir/CONTRIBUTING.md | 28 --- packages/mimir/LICENSE | 21 -- packages/mimir/README.md | 52 +++-- packages/mimir/SECURITY-HARDENING.md | 194 ------------------ packages/mimir/SECURITY.md | 21 -- packages/mimir/dist/cli.js | 22 +- packages/mimir/dist/cli.js.map | 2 +- packages/mimir/dist/version.d.ts | 2 +- packages/mimir/dist/version.js | 2 +- packages/mimir/package.json | 9 +- .../mimir/skills/mimir-audio-summary/SKILL.md | 38 ++-- .../skills/mimir-audio-summary/forge-voice.sh | 91 ++++---- packages/mimir/src/cli.ts | 29 ++- packages/mimir/src/version.ts | 2 +- scripts/smoke.mjs | 4 +- 34 files changed, 683 insertions(+), 483 deletions(-) delete mode 100644 packages/mimir-tts/LICENSE delete mode 100644 packages/mimir/CHANGELOG.md delete mode 100644 packages/mimir/CONTRIBUTING.md delete mode 100644 packages/mimir/LICENSE delete mode 100644 packages/mimir/SECURITY-HARDENING.md delete mode 100644 packages/mimir/SECURITY.md diff --git a/AGENTS.md b/AGENTS.md index 6434750..3aa0597 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -22,8 +22,10 @@ Avoid claiming universal binary-file support; unsupported proprietary formats need extraction or dedicated parsers. - Keep optional audio summaries separate from core ingestion/query behavior. The - `mimir-audio-summary` skill must prefer `kb audio` / `@jcode.labs/mimir-tts`, support offline - model loading, and keep generated audio under ignored local Mimir state. + `mimir-audio-summary` skill must prefer `kb audio` / `@jcode.labs/mimir-tts`, use the Edge MP3 + path for global Voice Forge quality when online TTS is explicitly acceptable, support the + Transformers.js WAV path for offline/confidential rendering, and keep generated audio under + ignored local Mimir state. - Keep the repository as a simple pnpm workspace monorepo. Add Turbo only if multiple packages or apps start needing task caching/orchestration beyond `pnpm --filter`. - Keep Mimir core free of Ollama. `embeddingProvider: "local-hash"` supports ingestion, search, MCP, @@ -77,7 +79,8 @@ General principles (KISS, DRY, YAGNI, SOLID) as applied in this codebase. Match - `packages/mimir/src/query.ts` performs vector search and returns cited retrieval context; LLM synthesis belongs outside Mimir core. - `packages/mimir/src/mcp.ts` exposes Mimir as an MCP stdio server for agents. -- `packages/mimir-tts` is the standalone JS/ONNX TTS package used by `kb audio`. +- `packages/mimir-tts` is the standalone TTS package used by `kb audio`; it uses `edge-tts` for + high-quality MP3 when available and Transformers.js for offline WAV rendering. - `packages/mimir/src/gitignore.ts` owns target-repository `.gitignore` entries for local generated Mimir state. - `packages/mimir/src/security.ts`, `packages/mimir/src/redaction.ts`, and diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d4ed26..1938db1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 0.4.1 - 2026-06-29 + +- Add an Edge-compatible Mimir TTS engine so `kb audio` can match the global Voice Forge quality + path with `edge-tts`, `fr-FR-DeniseNeural`, and MP3 output. +- Keep Transformers.js WAV rendering as the explicit offline/confidential path. +- Remove duplicated governance documents from package directories; root project docs are the single + source of truth. + ## 0.4.0 - 2026-06-28 - Reposition Mimir as sovereign local RAG for confidential datasets and AI agents. diff --git a/CLAUDE.md b/CLAUDE.md index 29b5e55..49eadda 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -63,8 +63,9 @@ The ingest pipeline (`packages/mimir/src/ingest.ts`) chains single-responsibilit `embeddings.ts` (vectorize) → `store.ts` (LanceDB). `query.ts` embeds the query and runs vector search; `ask` returns cited passages only (no LLM synthesis in core). -`packages/mimir-tts` is a separate ESM package that uses Transformers.js text-to-speech to render -WAV files without Python or ffmpeg. Core `kb audio` imports it dynamically. +`packages/mimir-tts` is a separate ESM package. It uses `edge-tts` for high-quality MP3 when the +external CLI is installed, and Transformers.js for offline WAV rendering without Python or ffmpeg. +Core `kb audio` imports it dynamically. Key behaviors to keep in mind before editing: diff --git a/README.md b/README.md index 581d511..dd71f80 100644 --- a/README.md +++ b/README.md @@ -7,8 +7,8 @@ agents. - [`@jcode.labs/mimir`](./packages/mimir): core CLI, library, MCP server, bundled agent skills, and synthetic examples. -- [`@jcode.labs/mimir-tts`](./packages/mimir-tts): plug-and-play JS/ONNX text-to-speech renderer - used by `kb audio`. +- [`@jcode.labs/mimir-tts`](./packages/mimir-tts): plug-and-play Edge-quality MP3 and offline + Transformers.js WAV renderer used by `kb audio`. ## Development diff --git a/SECURITY-HARDENING.md b/SECURITY-HARDENING.md index f3873b9..4a94aca 100644 --- a/SECURITY-HARDENING.md +++ b/SECURITY-HARDENING.md @@ -19,15 +19,17 @@ built to minimize data movement, but it is not a certified high-assurance system default. - MCP is read-focused: destructive tools are not exposed over MCP, and MCP retrieval is capped by `mcpMaxTopK`. -- Optional audio summaries use `kb audio` / `@jcode.labs/mimir-tts` for local WAV rendering with - Transformers.js. They do not require Python, ffmpeg, Piper, XTTS, or a local TTS server. +- Optional audio summaries use `kb audio` / `@jcode.labs/mimir-tts`. Edge MP3 gives the highest + quality when online TTS is acceptable. Transformers.js WAV is the offline/confidential path and + does not require Python, ffmpeg, Piper, XTTS, or a local TTS server. - npm releases are published with provenance from the protected GitHub Actions workflow. - Release artifacts include a package tarball, SHA256 checksums, SBOM, and manifest. ## Threat Model Mimir protects against accidental repository leaks, accidental built-in LLM usage, accidental online -TTS usage for generated summaries, accidental secret indexing, and weak release traceability. +TTS usage when the offline path is requested, accidental secret indexing, and weak release +traceability. Mimir does not protect against a compromised local machine, malicious dependencies already present in the runtime, a user with filesystem access to the same checkout, or forensic recovery from an @@ -65,7 +67,8 @@ pnpm exec kb ingest For semantic embeddings, preload the Transformers.js-compatible embedding model files inside the offline environment under the configured `embeddingModelPath`. For audio, preload the TTS model -files under `.mimir/models/tts` and render with `pnpm exec kb audio --offline`. +files under `.mimir/models/tts` and render with +`pnpm exec kb audio --engine transformers --offline`. ## Zero Network Posture @@ -130,17 +133,20 @@ Redaction changes the indexed text, not the raw files under `private/`. `kb install-skill` installs an optional `mimir-audio-summary` skill. It is designed for listenable briefings from a local Mimir index. The default renderer is `kb audio`, backed by -`@jcode.labs/mimir-tts` and Transformers.js. +`@jcode.labs/mimir-tts`. Confidentiality defaults: - narration text is written to a temp file outside the repository; -- generated WAV audio should be written under `.mimir/audio/`; +- generated MP3 or WAV audio should be written under `.mimir/audio/`; - `.mimir/` is ignored by Git; -- Python, ffmpeg, Piper, XTTS, and local TTS servers are not required for the default path; -- the first online-enabled render may download public model weights into `.mimir/models/tts`, but - the narration text is processed locally; -- `--offline` disables remote model loading and requires preloaded model files. +- Edge MP3 uses the online Edge TTS service through the external `edge-tts` CLI and should be used + only when sending the narration text to that service is acceptable; +- Transformers.js WAV does not require Python, ffmpeg, Piper, XTTS, or a local TTS server; +- the first online-enabled Transformers render may download public model weights into + `.mimir/models/tts`, but the narration text is processed locally; +- `--engine transformers --offline` disables remote model loading and requires preloaded model + files. Generated audio can still contain sensitive information. Treat it like a derived confidential document. diff --git a/package.json b/package.json index ae99e4e..03e118e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "jcode-mimir", - "version": "0.4.0", + "version": "0.4.1", "private": true, "description": "Monorepo for the Mimir open-source local RAG packages.", "type": "module", diff --git a/packages/mimir-tts/LICENSE b/packages/mimir-tts/LICENSE deleted file mode 100644 index 09c4e55..0000000 --- a/packages/mimir-tts/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2026 Jean-Baptiste Thery - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/packages/mimir-tts/README.md b/packages/mimir-tts/README.md index 2fd8dc0..ff02257 100644 --- a/packages/mimir-tts/README.md +++ b/packages/mimir-tts/README.md @@ -1,10 +1,16 @@ # Mimir TTS -Plug-and-play local text-to-speech for Mimir audio summaries. +Plug-and-play text-to-speech for Mimir audio summaries. -`@jcode.labs/mimir-tts` renders narration text to WAV with Transformers.js. It does not require -Python, ffmpeg, Piper, XTTS, or a local server. The first render can download a public ONNX model -from Hugging Face into `.mimir/models/tts`; the source text is processed locally. +`@jcode.labs/mimir-tts` has two explicit paths: + +- Edge MP3 for the same quality path as the global Voice Forge skill. It uses the external + `edge-tts` CLI, `fr-FR-DeniseNeural`, and `+0%` rate by default. +- Transformers.js WAV for confidential or air-gapped use. It does not require Python, ffmpeg, + Piper, XTTS, or a local server. + +The Edge path sends the narration text to the online Edge TTS service. Use the Transformers.js path +for private content. ## Install @@ -12,16 +18,30 @@ from Hugging Face into `.mimir/models/tts`; the source text is processed locally pnpm add -D @jcode.labs/mimir-tts ``` +Install Edge TTS only when you want the highest-quality online MP3 renderer: + +```bash +pipx install edge-tts +``` + ## Render +High-quality MP3: + ```bash -pnpm exec mimir-tts render /tmp/MIMIR-SUMMARY-tax.txt --out .mimir/audio/tax-summary.wav +pnpm exec mimir-tts render /tmp/MIMIR-SUMMARY-tax.txt \ + --engine edge \ + --out .mimir/audio/tax-summary.mp3 ``` -For offline or air-gapped use, preload the model files and run: +Offline/confidential WAV: ```bash -pnpm exec mimir-tts render summary.txt --offline --model-path .mimir/models/tts +pnpm exec mimir-tts render summary.txt \ + --engine transformers \ + --offline \ + --model-path .mimir/models/tts \ + --out .mimir/audio/summary.wav ``` ## Doctor @@ -30,4 +50,5 @@ pnpm exec mimir-tts render summary.txt --offline --model-path .mimir/models/tts pnpm exec mimir-tts doctor --json ``` -The default model is `Xenova/mms-tts-fra`. Override it with `--model` or `MIMIR_TTS_MODEL`. +The default Transformers.js model is `Xenova/mms-tts-fra`. Override it with `--model` or +`MIMIR_TTS_MODEL`. diff --git a/packages/mimir-tts/dist/cli.js b/packages/mimir-tts/dist/cli.js index 4414ce8..ef26c1f 100755 --- a/packages/mimir-tts/dist/cli.js +++ b/packages/mimir-tts/dist/cli.js @@ -31,8 +31,11 @@ async function runDoctor(args) { return; } printKeyValue("node", report.node); + printKeyValue("defaultEngine", report.defaultEngine); printKeyValue("defaultModel", report.defaultModel); printKeyValue("defaultModelPath", report.defaultModelPath); + printKeyValue("edgeTtsAvailable", String(report.edgeTtsAvailable)); + printKeyValue("edgeDefaultVoice", report.edgeDefaultVoice); printKeyValue("transformersAvailable", String(report.transformersAvailable)); printKeyValue("pythonRequired", String(report.pythonRequired)); printKeyValue("ffmpegRequired", String(report.ffmpegRequired)); @@ -44,10 +47,13 @@ async function runRender(args) { allowPositionals: true, options: { out: { type: "string", short: "o" }, + engine: { type: "string" }, model: { type: "string" }, "model-path": { type: "string" }, offline: { type: "boolean" }, "allow-remote-models": { type: "boolean" }, + voice: { type: "string" }, + rate: { type: "string" }, "speaker-embeddings": { type: "string" }, speed: { type: "string" }, json: { type: "boolean" }, @@ -61,9 +67,12 @@ async function runRender(args) { textFile, }; addStringOption(renderOptions, "outputPath", stringValue(values, "out")); + addEngineOption(renderOptions, engineValue(values)); addStringOption(renderOptions, "model", stringValue(values, "model")); addStringOption(renderOptions, "modelPath", stringValue(values, "model-path")); addBooleanOption(renderOptions, "allowRemoteModels", allowRemoteModels(values)); + addStringOption(renderOptions, "voice", stringValue(values, "voice")); + addStringOption(renderOptions, "rate", stringValue(values, "rate")); addStringOption(renderOptions, "speakerEmbeddings", stringValue(values, "speaker-embeddings")); addNumberOption(renderOptions, "speed", numberValue(values, "speed")); const result = await renderSpeech(renderOptions); @@ -72,9 +81,13 @@ async function runRender(args) { return; } printKeyValue("outputPath", result.outputPath); + printKeyValue("engine", result.engine); + printKeyValue("outputFormat", result.outputFormat); printKeyValue("model", result.model); printKeyValue("modelPath", result.modelPath); printKeyValue("allowRemoteModels", String(result.allowRemoteModels)); + printKeyValue("voice", result.voice ?? "none"); + printKeyValue("rate", result.rate ?? "none"); printKeyValue("samplingRate", String(result.samplingRate ?? "unknown")); printKeyValue("samples", String(result.samples ?? "unknown")); } @@ -91,6 +104,24 @@ function stringValue(values, key) { const value = values[key]; return typeof value === "string" ? value : undefined; } +function engineValue(values) { + if (values.offline === true) { + return "transformers"; + } + const value = stringValue(values, "engine"); + if (value === undefined) { + return undefined; + } + if (value === "auto" || value === "edge" || value === "transformers") { + return value; + } + throw new Error("Expected --engine to be auto, edge, or transformers."); +} +function addEngineOption(target, value) { + if (value !== undefined) { + target.engine = value; + } +} function addStringOption(target, key, value) { if (value !== undefined) { target[key] = value; @@ -125,13 +156,16 @@ function printHelp() { Usage: mimir-tts doctor [--json] - mimir-tts render [--out output.wav] [--offline] + mimir-tts render [--out output.mp3] [--engine edge] Options: + --engine auto, edge, or transformers. Auto uses Edge when available. --model Transformers.js TTS model ID. --model-path Local model/cache path. Defaults to .mimir/models/tts. - --offline Disable remote model downloads. + --offline Force the Transformers.js local/offline WAV path. --allow-remote-models Explicitly allow remote model downloads. + --voice Edge voice. Defaults to fr-FR-DeniseNeural. + --rate Edge rate. Defaults to +0%. --speaker-embeddings Optional model-specific speaker embedding path or URL. --speed Optional model-specific speech speed. --json Print JSON output. diff --git a/packages/mimir-tts/dist/cli.js.map b/packages/mimir-tts/dist/cli.js.map index 0d63bc1..cbdb686 100644 --- a/packages/mimir-tts/dist/cli.js.map +++ b/packages/mimir-tts/dist/cli.js.map @@ -1 +1 @@ -{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/mimir-tts/dist/index.d.ts b/packages/mimir-tts/dist/index.d.ts index 0322e1b..3309acd 100644 --- a/packages/mimir-tts/dist/index.d.ts +++ b/packages/mimir-tts/dist/index.d.ts @@ -1,6 +1,11 @@ export declare const DEFAULT_TTS_MODEL = "Xenova/mms-tts-fra"; export declare const DEFAULT_TTS_MODEL_PATH = ".mimir/models/tts"; export declare const DEFAULT_AUDIO_DIR = ".mimir/audio"; +export declare const DEFAULT_TTS_ENGINE = "auto"; +export declare const DEFAULT_EDGE_VOICE = "fr-FR-DeniseNeural"; +export declare const DEFAULT_EDGE_RATE = "+0%"; +export type TtsEngine = "auto" | "edge" | "transformers"; +export type OutputFormat = "mp3" | "wav"; export interface TextToAudioOutputLike { save(path: string): Promise; sampling_rate?: number; @@ -16,29 +21,48 @@ export interface RenderSpeechOptions { text?: string; textFile?: string; outputPath?: string; + engine?: TtsEngine; model?: string; modelPath?: string; allowRemoteModels?: boolean; + voice?: string; + rate?: string; speakerEmbeddings?: string; speed?: number; synthesizer?: TextToAudioSynthesizer; + edgeRenderer?: EdgeTtsRenderer; + edgeAvailable?: () => boolean; } export interface RenderSpeechResult { outputPath: string; + engine: Exclude; + outputFormat: OutputFormat; model: string; modelPath: string; allowRemoteModels: boolean; + voice: string | null; + rate: string | null; samplingRate: number | null; samples: number | null; } export interface DoctorReport { node: string; + defaultEngine: TtsEngine; defaultModel: string; defaultModelPath: string; transformersAvailable: boolean; + edgeTtsAvailable: boolean; + edgeDefaultVoice: string; pythonRequired: false; ffmpegRequired: false; - outputFormat: "wav"; + outputFormat: "mp3-or-wav"; +} +export type EdgeTtsRenderer = (options: EdgeTtsRenderOptions) => Promise; +export interface EdgeTtsRenderOptions { + text: string; + outputPath: string; + voice: string; + rate: string; } export declare function renderSpeech(options: RenderSpeechOptions): Promise; export declare function doctor(): Promise; diff --git a/packages/mimir-tts/dist/index.d.ts.map b/packages/mimir-tts/dist/index.d.ts.map index ad43cda..37fffc3 100644 --- a/packages/mimir-tts/dist/index.d.ts.map +++ b/packages/mimir-tts/dist/index.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,iBAAiB,uBAAuB,CAAA;AACrD,eAAO,MAAM,sBAAsB,sBAAsB,CAAA;AACzD,eAAO,MAAM,iBAAiB,iBAAiB,CAAA;AAE/C,MAAM,WAAW,qBAAqB;IACpC,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACjC,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,IAAI,CAAC,EAAE,YAAY,CAAA;CACpB;AAED,MAAM,MAAM,sBAAsB,GAAG,CACnC,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,kBAAkB,KACzB,OAAO,CAAC,qBAAqB,CAAC,CAAA;AAEnC,MAAM,WAAW,kBAAkB;IACjC,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,mBAAmB;IAClC,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,WAAW,CAAC,EAAE,sBAAsB,CAAA;CACrC;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;IACjB,iBAAiB,EAAE,OAAO,CAAA;IAC1B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CACvB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,YAAY,EAAE,MAAM,CAAA;IACpB,gBAAgB,EAAE,MAAM,CAAA;IACxB,qBAAqB,EAAE,OAAO,CAAA;IAC9B,cAAc,EAAE,KAAK,CAAA;IACrB,cAAc,EAAE,KAAK,CAAA;IACrB,YAAY,EAAE,KAAK,CAAA;CACpB;AAED,wBAAsB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CA6B5F;AAED,wBAAsB,MAAM,IAAI,OAAO,CAAC,YAAY,CAAC,CAUpD;AAgED,wBAAgB,gBAAgB,CAAC,GAAG,SAAgB,GAAG,OAAO,CAE7D"} \ No newline at end of file +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/mimir-tts/dist/index.js b/packages/mimir-tts/dist/index.js index c77bbf1..2852158 100644 --- a/packages/mimir-tts/dist/index.js +++ b/packages/mimir-tts/dist/index.js @@ -1,25 +1,59 @@ +import { spawn, spawnSync } from "node:child_process"; import { existsSync } from "node:fs"; -import { mkdir, readFile } from "node:fs/promises"; +import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; +import os from "node:os"; import path from "node:path"; export const DEFAULT_TTS_MODEL = "Xenova/mms-tts-fra"; export const DEFAULT_TTS_MODEL_PATH = ".mimir/models/tts"; export const DEFAULT_AUDIO_DIR = ".mimir/audio"; +export const DEFAULT_TTS_ENGINE = "auto"; +export const DEFAULT_EDGE_VOICE = "fr-FR-DeniseNeural"; +export const DEFAULT_EDGE_RATE = "+0%"; export async function renderSpeech(options) { const cwd = path.resolve(options.cwd ?? process.cwd()); const text = await readInputText(options); + const engine = resolveEngine(options); const model = options.model ?? process.env.MIMIR_TTS_MODEL ?? DEFAULT_TTS_MODEL; const modelPath = resolveFromCwd(cwd, options.modelPath ?? process.env.MIMIR_TTS_MODEL_PATH ?? DEFAULT_TTS_MODEL_PATH); - const outputPath = resolveFromCwd(cwd, options.outputPath ?? defaultOutputPath(cwd, options.textFile)); + const outputPath = resolveFromCwd(cwd, options.outputPath ?? defaultOutputPath(cwd, options.textFile, outputFormatForEngine(engine))); const allowRemoteModels = options.allowRemoteModels ?? readBooleanEnv("MIMIR_TTS_ALLOW_REMOTE_MODELS", true); await mkdir(path.dirname(outputPath), { recursive: true }); + if (engine === "edge") { + validateOutputFormat(outputPath, "mp3"); + const voice = options.voice ?? process.env.MIMIR_TTS_EDGE_VOICE ?? DEFAULT_EDGE_VOICE; + const rate = options.rate ?? process.env.MIMIR_TTS_EDGE_RATE ?? DEFAULT_EDGE_RATE; + const renderer = options.edgeRenderer ?? edgeCliRenderer; + const edgeAvailable = options.edgeAvailable ?? edgeTtsAvailable; + if (!options.edgeRenderer && !edgeAvailable()) { + throw new Error("edge-tts is required for the Edge engine. Install it with `pipx install edge-tts`."); + } + await renderer({ text, outputPath, voice, rate }); + return { + outputPath, + engine, + outputFormat: "mp3", + model, + modelPath, + allowRemoteModels, + voice, + rate, + samplingRate: null, + samples: null, + }; + } + validateOutputFormat(outputPath, "wav"); const synthesizer = options.synthesizer ?? (await transformerSynthesizer(model, modelPath, allowRemoteModels)); const output = await synthesizer(text, textToAudioOptions(options)); await output.save(outputPath); return { outputPath, + engine, + outputFormat: "wav", model, modelPath, allowRemoteModels, + voice: null, + rate: null, samplingRate: typeof output.sampling_rate === "number" ? output.sampling_rate : null, samples: output.data instanceof Float32Array ? output.data.length : null, }; @@ -27,12 +61,15 @@ export async function renderSpeech(options) { export async function doctor() { return { node: process.versions.node, + defaultEngine: DEFAULT_TTS_ENGINE, defaultModel: DEFAULT_TTS_MODEL, defaultModelPath: DEFAULT_TTS_MODEL_PATH, transformersAvailable: await canImportTransformers(), + edgeTtsAvailable: edgeTtsAvailable(), + edgeDefaultVoice: DEFAULT_EDGE_VOICE, pythonRequired: false, ffmpegRequired: false, - outputFormat: "wav", + outputFormat: "mp3-or-wav", }; } async function readInputText(options) { @@ -43,9 +80,9 @@ async function readInputText(options) { } return trimmed; } -function defaultOutputPath(cwd, textFile) { +function defaultOutputPath(cwd, textFile, format) { const name = textFile ? path.basename(textFile, path.extname(textFile)) : "mimir-summary"; - return path.join(cwd, DEFAULT_AUDIO_DIR, `${name}.wav`); + return path.join(cwd, DEFAULT_AUDIO_DIR, `${name}.${format}`); } function resolveFromCwd(cwd, input) { return path.isAbsolute(input) ? input : path.resolve(cwd, input); @@ -67,6 +104,89 @@ async function transformerSynthesizer(model, modelPath, allowRemoteModels) { transformers.env.allowRemoteModels = allowRemoteModels; return (await transformers.pipeline("text-to-speech", model)); } +function resolveEngine(options) { + if (options.synthesizer) { + return "transformers"; + } + const requested = options.engine ?? readEngineEnv() ?? DEFAULT_TTS_ENGINE; + if (requested === "edge" || requested === "transformers") { + return requested; + } + const outputFormat = options.outputPath ? formatFromPath(options.outputPath) : null; + if (outputFormat === "wav") { + return "transformers"; + } + if (outputFormat === "mp3") { + return "edge"; + } + const edgeAvailable = options.edgeAvailable ?? edgeTtsAvailable; + return edgeAvailable() ? "edge" : "transformers"; +} +function outputFormatForEngine(engine) { + return engine === "edge" ? "mp3" : "wav"; +} +function formatFromPath(filePath) { + const extension = path.extname(filePath).toLowerCase(); + if (extension === ".mp3") { + return "mp3"; + } + if (extension === ".wav") { + return "wav"; + } + return null; +} +function validateOutputFormat(filePath, expected) { + const actual = formatFromPath(filePath); + if (actual && actual !== expected) { + throw new Error(`The ${expected} engine cannot write ${actual} output. Use a .${expected} path.`); + } +} +function readEngineEnv() { + const raw = process.env.MIMIR_TTS_ENGINE; + if (raw === "auto" || raw === "edge" || raw === "transformers") { + return raw; + } + return undefined; +} +function edgeTtsAvailable() { + return spawnSync("edge-tts", ["--help"], { stdio: "ignore" }).status === 0; +} +async function edgeCliRenderer(options) { + const tempDir = await mkdtemp(path.join(os.tmpdir(), "mimir-tts-edge-")); + const textFile = path.join(tempDir, "input.txt"); + await writeFile(textFile, options.text, "utf8"); + try { + await runEdgeTts([ + "--file", + textFile, + "--voice", + options.voice, + `--rate=${options.rate}`, + "--write-media", + options.outputPath, + ]); + } + finally { + await rm(tempDir, { recursive: true, force: true }); + } +} +async function runEdgeTts(args) { + const child = spawn("edge-tts", args, { + stdio: ["ignore", "ignore", "pipe"], + }); + const stderr = []; + child.stderr.on("data", (chunk) => stderr.push(chunk)); + const code = await new Promise((resolve, reject) => { + child.on("error", reject); + child.on("close", resolve); + }); + if (code !== 0) { + const detail = Buffer.concat(stderr).toString("utf8").trim(); + throw new Error(detail + ? `edge-tts failed with exit code ${code}: ${detail}` + : `edge-tts failed with exit code ${code}.`); + } +} async function canImportTransformers() { try { await import("@huggingface/transformers"); diff --git a/packages/mimir-tts/dist/index.js.map b/packages/mimir-tts/dist/index.js.map index 469d3d4..9c2aa86 100644 --- a/packages/mimir-tts/dist/index.js.map +++ b/packages/mimir-tts/dist/index.js.map @@ -1 +1 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/mimir-tts/package.json b/packages/mimir-tts/package.json index 330ef1e..7721c02 100644 --- a/packages/mimir-tts/package.json +++ b/packages/mimir-tts/package.json @@ -1,7 +1,7 @@ { "name": "@jcode.labs/mimir-tts", - "version": "0.4.0", - "description": "Plug-and-play local text-to-speech for Mimir audio summaries.", + "version": "0.4.1", + "description": "Plug-and-play Edge-quality and offline text-to-speech for Mimir audio summaries.", "type": "module", "license": "MIT", "author": { @@ -14,6 +14,7 @@ "text-to-speech", "tts", "local-first", + "edge-tts", "transformers", "onnx", "audio" @@ -41,8 +42,7 @@ }, "files": [ "dist", - "README.md", - "LICENSE" + "README.md" ], "publishConfig": { "access": "public" diff --git a/packages/mimir-tts/src/cli.ts b/packages/mimir-tts/src/cli.ts index 92fe65a..cd95b86 100644 --- a/packages/mimir-tts/src/cli.ts +++ b/packages/mimir-tts/src/cli.ts @@ -1,6 +1,6 @@ #!/usr/bin/env node import { parseArgs } from "node:util" -import { doctor, type RenderSpeechOptions, renderSpeech } from "./index.js" +import { doctor, type RenderSpeechOptions, renderSpeech, type TtsEngine } from "./index.js" type CliValues = Record @@ -34,8 +34,11 @@ async function runDoctor(args: string[]): Promise { } printKeyValue("node", report.node) + printKeyValue("defaultEngine", report.defaultEngine) printKeyValue("defaultModel", report.defaultModel) printKeyValue("defaultModelPath", report.defaultModelPath) + printKeyValue("edgeTtsAvailable", String(report.edgeTtsAvailable)) + printKeyValue("edgeDefaultVoice", report.edgeDefaultVoice) printKeyValue("transformersAvailable", String(report.transformersAvailable)) printKeyValue("pythonRequired", String(report.pythonRequired)) printKeyValue("ffmpegRequired", String(report.ffmpegRequired)) @@ -48,10 +51,13 @@ async function runRender(args: string[]): Promise { allowPositionals: true, options: { out: { type: "string", short: "o" }, + engine: { type: "string" }, model: { type: "string" }, "model-path": { type: "string" }, offline: { type: "boolean" }, "allow-remote-models": { type: "boolean" }, + voice: { type: "string" }, + rate: { type: "string" }, "speaker-embeddings": { type: "string" }, speed: { type: "string" }, json: { type: "boolean" }, @@ -66,9 +72,12 @@ async function runRender(args: string[]): Promise { textFile, } addStringOption(renderOptions, "outputPath", stringValue(values, "out")) + addEngineOption(renderOptions, engineValue(values)) addStringOption(renderOptions, "model", stringValue(values, "model")) addStringOption(renderOptions, "modelPath", stringValue(values, "model-path")) addBooleanOption(renderOptions, "allowRemoteModels", allowRemoteModels(values)) + addStringOption(renderOptions, "voice", stringValue(values, "voice")) + addStringOption(renderOptions, "rate", stringValue(values, "rate")) addStringOption(renderOptions, "speakerEmbeddings", stringValue(values, "speaker-embeddings")) addNumberOption(renderOptions, "speed", numberValue(values, "speed")) @@ -79,9 +88,13 @@ async function runRender(args: string[]): Promise { return } printKeyValue("outputPath", result.outputPath) + printKeyValue("engine", result.engine) + printKeyValue("outputFormat", result.outputFormat) printKeyValue("model", result.model) printKeyValue("modelPath", result.modelPath) printKeyValue("allowRemoteModels", String(result.allowRemoteModels)) + printKeyValue("voice", result.voice ?? "none") + printKeyValue("rate", result.rate ?? "none") printKeyValue("samplingRate", String(result.samplingRate ?? "unknown")) printKeyValue("samples", String(result.samples ?? "unknown")) } @@ -101,9 +114,29 @@ function stringValue(values: CliValues, key: string): string | undefined { return typeof value === "string" ? value : undefined } +function engineValue(values: CliValues): TtsEngine | undefined { + if (values.offline === true) { + return "transformers" + } + const value = stringValue(values, "engine") + if (value === undefined) { + return undefined + } + if (value === "auto" || value === "edge" || value === "transformers") { + return value + } + throw new Error("Expected --engine to be auto, edge, or transformers.") +} + +function addEngineOption(target: RenderSpeechOptions, value: TtsEngine | undefined): void { + if (value !== undefined) { + target.engine = value + } +} + function addStringOption( target: RenderSpeechOptions, - key: "outputPath" | "model" | "modelPath" | "speakerEmbeddings", + key: "outputPath" | "model" | "modelPath" | "voice" | "rate" | "speakerEmbeddings", value: string | undefined, ): void { if (value !== undefined) { @@ -152,13 +185,16 @@ function printHelp(): void { Usage: mimir-tts doctor [--json] - mimir-tts render [--out output.wav] [--offline] + mimir-tts render [--out output.mp3] [--engine edge] Options: + --engine auto, edge, or transformers. Auto uses Edge when available. --model Transformers.js TTS model ID. --model-path Local model/cache path. Defaults to .mimir/models/tts. - --offline Disable remote model downloads. + --offline Force the Transformers.js local/offline WAV path. --allow-remote-models Explicitly allow remote model downloads. + --voice Edge voice. Defaults to fr-FR-DeniseNeural. + --rate Edge rate. Defaults to +0%. --speaker-embeddings Optional model-specific speaker embedding path or URL. --speed Optional model-specific speech speed. --json Print JSON output. diff --git a/packages/mimir-tts/src/index.test.ts b/packages/mimir-tts/src/index.test.ts index d7b90f1..a63f15a 100644 --- a/packages/mimir-tts/src/index.test.ts +++ b/packages/mimir-tts/src/index.test.ts @@ -2,7 +2,7 @@ import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises" import os from "node:os" import path from "node:path" import { afterEach, describe, expect, it } from "vitest" -import { doctor, renderSpeech, type TextToAudioSynthesizer } from "./index.js" +import { doctor, type EdgeTtsRenderer, renderSpeech, type TextToAudioSynthesizer } from "./index.js" const tempDirs: string[] = [] @@ -37,18 +37,68 @@ describe("renderSpeech", () => { }) expect(result.outputPath).toBe(outputPath) + expect(result.engine).toBe("transformers") + expect(result.outputFormat).toBe("wav") expect(result.allowRemoteModels).toBe(false) expect(result.samplingRate).toBe(16_000) expect(await readFile(outputPath, "utf8")).toBe("RIFF fake wav") }) + + it("renders mp3 output through the Edge-compatible renderer", async () => { + const root = await mkdtemp(path.join(os.tmpdir(), "mimir-tts-edge-")) + tempDirs.push(root) + const textFile = path.join(root, "summary.txt") + const outputPath = path.join(root, ".mimir/audio/summary.mp3") + await writeFile(textFile, "Bonjour depuis Mimir.", "utf8") + + const edgeRenderer: EdgeTtsRenderer = async (options) => { + expect(options.voice).toBe("fr-FR-DeniseNeural") + expect(options.rate).toBe("+0%") + await writeFile(options.outputPath, "ID3 fake mp3", "utf8") + } + + const result = await renderSpeech({ + cwd: root, + textFile, + outputPath, + engine: "edge", + edgeRenderer, + }) + + expect(result.outputPath).toBe(outputPath) + expect(result.engine).toBe("edge") + expect(result.outputFormat).toBe("mp3") + expect(result.voice).toBe("fr-FR-DeniseNeural") + expect(result.rate).toBe("+0%") + expect(await readFile(outputPath, "utf8")).toBe("ID3 fake mp3") + }) + + it("rejects incompatible output formats", async () => { + const root = await mkdtemp(path.join(os.tmpdir(), "mimir-tts-format-")) + tempDirs.push(root) + const textFile = path.join(root, "summary.txt") + await writeFile(textFile, "Bonjour depuis Mimir.", "utf8") + + await expect( + renderSpeech({ + cwd: root, + textFile, + outputPath: path.join(root, "summary.wav"), + engine: "edge", + edgeRenderer: async () => {}, + }), + ).rejects.toThrow("The mp3 engine cannot write wav output") + }) }) describe("doctor", () => { - it("reports a Python-free wav renderer", async () => { + it("reports Python-free renderers and the global-skill quality default", async () => { await expect(doctor()).resolves.toMatchObject({ + defaultEngine: "auto", + edgeDefaultVoice: "fr-FR-DeniseNeural", pythonRequired: false, ffmpegRequired: false, - outputFormat: "wav", + outputFormat: "mp3-or-wav", }) }) }) diff --git a/packages/mimir-tts/src/index.ts b/packages/mimir-tts/src/index.ts index 1a66e08..6ddf4ff 100644 --- a/packages/mimir-tts/src/index.ts +++ b/packages/mimir-tts/src/index.ts @@ -1,10 +1,18 @@ +import { spawn, spawnSync } from "node:child_process" import { existsSync } from "node:fs" -import { mkdir, readFile } from "node:fs/promises" +import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises" +import os from "node:os" import path from "node:path" export const DEFAULT_TTS_MODEL = "Xenova/mms-tts-fra" export const DEFAULT_TTS_MODEL_PATH = ".mimir/models/tts" export const DEFAULT_AUDIO_DIR = ".mimir/audio" +export const DEFAULT_TTS_ENGINE = "auto" +export const DEFAULT_EDGE_VOICE = "fr-FR-DeniseNeural" +export const DEFAULT_EDGE_RATE = "+0%" + +export type TtsEngine = "auto" | "edge" | "transformers" +export type OutputFormat = "mp3" | "wav" export interface TextToAudioOutputLike { save(path: string): Promise @@ -27,36 +35,58 @@ export interface RenderSpeechOptions { text?: string textFile?: string outputPath?: string + engine?: TtsEngine model?: string modelPath?: string allowRemoteModels?: boolean + voice?: string + rate?: string speakerEmbeddings?: string speed?: number synthesizer?: TextToAudioSynthesizer + edgeRenderer?: EdgeTtsRenderer + edgeAvailable?: () => boolean } export interface RenderSpeechResult { outputPath: string + engine: Exclude + outputFormat: OutputFormat model: string modelPath: string allowRemoteModels: boolean + voice: string | null + rate: string | null samplingRate: number | null samples: number | null } export interface DoctorReport { node: string + defaultEngine: TtsEngine defaultModel: string defaultModelPath: string transformersAvailable: boolean + edgeTtsAvailable: boolean + edgeDefaultVoice: string pythonRequired: false ffmpegRequired: false - outputFormat: "wav" + outputFormat: "mp3-or-wav" +} + +export type EdgeTtsRenderer = (options: EdgeTtsRenderOptions) => Promise + +export interface EdgeTtsRenderOptions { + text: string + outputPath: string + voice: string + rate: string } export async function renderSpeech(options: RenderSpeechOptions): Promise { const cwd = path.resolve(options.cwd ?? process.cwd()) const text = await readInputText(options) + const engine = resolveEngine(options) const model = options.model ?? process.env.MIMIR_TTS_MODEL ?? DEFAULT_TTS_MODEL const modelPath = resolveFromCwd( cwd, @@ -64,12 +94,41 @@ export async function renderSpeech(options: RenderSpeechOptions): Promise { return { node: process.versions.node, + defaultEngine: DEFAULT_TTS_ENGINE, defaultModel: DEFAULT_TTS_MODEL, defaultModelPath: DEFAULT_TTS_MODEL_PATH, transformersAvailable: await canImportTransformers(), + edgeTtsAvailable: edgeTtsAvailable(), + edgeDefaultVoice: DEFAULT_EDGE_VOICE, pythonRequired: false, ffmpegRequired: false, - outputFormat: "wav", + outputFormat: "mp3-or-wav", } } @@ -106,9 +172,13 @@ async function readInputText(options: RenderSpeechOptions): Promise { return trimmed } -function defaultOutputPath(cwd: string, textFile: string | undefined): string { +function defaultOutputPath( + cwd: string, + textFile: string | undefined, + format: OutputFormat, +): string { const name = textFile ? path.basename(textFile, path.extname(textFile)) : "mimir-summary" - return path.join(cwd, DEFAULT_AUDIO_DIR, `${name}.wav`) + return path.join(cwd, DEFAULT_AUDIO_DIR, `${name}.${format}`) } function resolveFromCwd(cwd: string, input: string): string { @@ -139,6 +209,106 @@ async function transformerSynthesizer( return (await transformers.pipeline("text-to-speech", model)) as TextToAudioSynthesizer } +function resolveEngine(options: RenderSpeechOptions): Exclude { + if (options.synthesizer) { + return "transformers" + } + + const requested = options.engine ?? readEngineEnv() ?? DEFAULT_TTS_ENGINE + if (requested === "edge" || requested === "transformers") { + return requested + } + + const outputFormat = options.outputPath ? formatFromPath(options.outputPath) : null + if (outputFormat === "wav") { + return "transformers" + } + if (outputFormat === "mp3") { + return "edge" + } + + const edgeAvailable = options.edgeAvailable ?? edgeTtsAvailable + return edgeAvailable() ? "edge" : "transformers" +} + +function outputFormatForEngine(engine: Exclude): OutputFormat { + return engine === "edge" ? "mp3" : "wav" +} + +function formatFromPath(filePath: string): OutputFormat | null { + const extension = path.extname(filePath).toLowerCase() + if (extension === ".mp3") { + return "mp3" + } + if (extension === ".wav") { + return "wav" + } + return null +} + +function validateOutputFormat(filePath: string, expected: OutputFormat): void { + const actual = formatFromPath(filePath) + if (actual && actual !== expected) { + throw new Error( + `The ${expected} engine cannot write ${actual} output. Use a .${expected} path.`, + ) + } +} + +function readEngineEnv(): TtsEngine | undefined { + const raw = process.env.MIMIR_TTS_ENGINE + if (raw === "auto" || raw === "edge" || raw === "transformers") { + return raw + } + return undefined +} + +function edgeTtsAvailable(): boolean { + return spawnSync("edge-tts", ["--help"], { stdio: "ignore" }).status === 0 +} + +async function edgeCliRenderer(options: EdgeTtsRenderOptions): Promise { + const tempDir = await mkdtemp(path.join(os.tmpdir(), "mimir-tts-edge-")) + const textFile = path.join(tempDir, "input.txt") + await writeFile(textFile, options.text, "utf8") + + try { + await runEdgeTts([ + "--file", + textFile, + "--voice", + options.voice, + `--rate=${options.rate}`, + "--write-media", + options.outputPath, + ]) + } finally { + await rm(tempDir, { recursive: true, force: true }) + } +} + +async function runEdgeTts(args: string[]): Promise { + const child = spawn("edge-tts", args, { + stdio: ["ignore", "ignore", "pipe"], + }) + const stderr: Buffer[] = [] + child.stderr.on("data", (chunk: Buffer) => stderr.push(chunk)) + + const code = await new Promise((resolve, reject) => { + child.on("error", reject) + child.on("close", resolve) + }) + + if (code !== 0) { + const detail = Buffer.concat(stderr).toString("utf8").trim() + throw new Error( + detail + ? `edge-tts failed with exit code ${code}: ${detail}` + : `edge-tts failed with exit code ${code}.`, + ) + } +} + async function canImportTransformers(): Promise { try { await import("@huggingface/transformers") diff --git a/packages/mimir/CHANGELOG.md b/packages/mimir/CHANGELOG.md deleted file mode 100644 index 6d4ed26..0000000 --- a/packages/mimir/CHANGELOG.md +++ /dev/null @@ -1,47 +0,0 @@ -# Changelog - -## 0.4.0 - 2026-06-28 - -- Reposition Mimir as sovereign local RAG for confidential datasets and AI agents. -- Expand default ingestion to common text, Office/OpenDocument, data, config, log, and source-code - file types. -- Add `includeExtensions` / `KB_INCLUDE_EXTENSIONS` for custom UTF-8 text file extensions. -- Add the optional `mimir-audio-summary` bundled skill for confidential audio summaries. -- Install both the main Mimir skill and optional audio-summary skill with `kb install-skill`. -- Improve agent guidance for deep multi-query retrieval before synthesis. -- Make Mimir core retrieval-only: `kb ask` now returns cited context for external agents or LLMs - instead of generating answers internally. -- Add optional Transformers.js semantic embeddings through `embeddingProvider: "transformers"`. -- Remove Ollama providers and keep `embeddingProvider: "local-hash"` as the no-model default. -- Move the repository to a simple pnpm workspace monorepo without adding Turbo. -- Move the core `@jcode.labs/mimir` package into `packages/mimir`. -- Add `@jcode.labs/mimir-tts` for plug-and-play JS/ONNX WAV rendering without Python or ffmpeg. -- Add `kb audio` and update the audio-summary skill to use Mimir TTS before advanced fallback - engines. - -## 0.3.0 - 2026-06-28 - -- Add confidentiality hardening defaults: built-in redaction before indexing, metadata-only access - logs, and bounded MCP retrieval. -- Add `kb security-audit` for zero-telemetry, provider, redaction, gitignore, storage, and - MCP posture checks. -- Add `kb destroy-index --yes` to remove generated vector indexes. -- Add release verification artifacts: npm tarball, SHA256 checksums, SBOM, and manifest. -- Document air-gapped operation, threat model, MCP hardening, and secure deletion limits. - -## 0.2.1 - 2026-06-28 - -- Add GitHub Sponsors funding metadata and document suggested sponsor tiers. -- Add maintainer positioning for Jean-Baptiste Thery and JCode Labs in the README. -- Make `kb init` and `kb install-skill` automatically keep `.kb/` and `.mimir/` - ignored by Git. - -## 0.2.0 - 2026-06-28 - -- Rename public product branding to Mimir while keeping the JCode Labs npm scope. -- Add the bundled portable `mimir` agent skill. -- Add the MCP stdio server with `mimir_status`, `mimir_search`, `mimir_ask`, and - `mimir_audit`. -- Add production smoke coverage for the built CLI and MCP server. -- Add Biome, commitlint, publint, CodeQL, Dependabot grouping, protected npm publishing, - and open-source contribution/security documentation. diff --git a/packages/mimir/CONTRIBUTING.md b/packages/mimir/CONTRIBUTING.md deleted file mode 100644 index 3a14d25..0000000 --- a/packages/mimir/CONTRIBUTING.md +++ /dev/null @@ -1,28 +0,0 @@ -# Contributing - -Mimir is an open-source project under the MIT License. Issues and pull requests are welcome. - -## Development - -Use Node.js 20+ and pnpm: - -```bash -pnpm install -pnpm validate -``` - -`pnpm validate` runs Biome, TypeScript, Vitest, the production CLI/MCP smoke test, and npm -package metadata checks. - -## Pull Requests - -- Open pull requests against `main`. -- Keep changes focused and include tests or smoke coverage for behavior changes. -- Do not commit private documents, generated vector stores, environment files, tokens, or - credentials. -- Use conventional commit messages such as `feat: add source parser` or - `fix: handle empty index`. - -## Security - -Do not report vulnerabilities through public issues. Follow [`SECURITY.md`](./SECURITY.md). diff --git a/packages/mimir/LICENSE b/packages/mimir/LICENSE deleted file mode 100644 index 2e2d13f..0000000 --- a/packages/mimir/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2026 Jean-Baptiste Thery and JCode Labs - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/packages/mimir/README.md b/packages/mimir/README.md index f965200..854a912 100644 --- a/packages/mimir/README.md +++ b/packages/mimir/README.md @@ -3,7 +3,7 @@ [![CI](https://github.com/jcode-works/jcode-mimir/actions/workflows/ci.yml/badge.svg)](https://github.com/jcode-works/jcode-mimir/actions/workflows/ci.yml) [![CodeQL](https://github.com/jcode-works/jcode-mimir/actions/workflows/codeql.yml/badge.svg)](https://github.com/jcode-works/jcode-mimir/actions/workflows/codeql.yml) [![npm](https://img.shields.io/npm/v/@jcode.labs/mimir)](https://www.npmjs.com/package/@jcode.labs/mimir) -[![license: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE) +[![license: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/jcode-works/jcode-mimir/blob/main/LICENSE) Open-source, sovereign local RAG for confidential datasets and AI agents. @@ -27,8 +27,10 @@ Built by Jean-Baptiste Thery, freelance full-stack/AI tooling engineer at JCode Mimir is a public open-source project under the MIT License. It is designed to be inspectable, forkable, and usable without a JCode Labs account. -Contributions are welcome through pull requests. Start with [`CONTRIBUTING.md`](./CONTRIBUTING.md). -Security reports should stay private and follow the policy in [`SECURITY.md`](./SECURITY.md). +Contributions are welcome through pull requests. Start with +[`CONTRIBUTING.md`](https://github.com/jcode-works/jcode-mimir/blob/main/CONTRIBUTING.md). +Security reports should stay private and follow the policy in +[`SECURITY.md`](https://github.com/jcode-works/jcode-mimir/blob/main/SECURITY.md). ## Sponsors @@ -56,8 +58,8 @@ Early public package. APIs may evolve before `1.0.0`. retrieval layer. - Retrieve grounded local evidence through CLI, library calls, MCP tools, or the bundled agent skills so your chosen AI agent can produce cited summaries. -- Optionally create listenable WAV summaries with `kb audio`, `@jcode.labs/mimir-tts`, and the - bundled `mimir-audio-summary` skill. +- Optionally create listenable MP3 or WAV summaries with `kb audio`, `@jcode.labs/mimir-tts`, and + the bundled `mimir-audio-summary` skill. Mimir is not a hosted SaaS, not a remote vector database, and not a certified high-assurance system. For regulated or state-grade environments, pair it with encrypted disks, controlled machines, release @@ -79,7 +81,7 @@ context. | Build an internal knowledge base | "What is the policy for incident review?", "Who owns this process?", "Which source says that?" | | Prepare meetings or decisions | "Give me a one-page briefing.", "What is missing before deciding?", "List action items and evidence." | | Ask questions over offline documents | "Which files mention local-only operation?", "What evidence supports this claim?" | -| Generate audio briefings | "Create a listenable summary of the current dossier using offline TTS." | +| Generate audio briefings | "Create a listenable high-quality or offline summary of the current dossier." | ## Requirements @@ -90,8 +92,11 @@ context. default. - Generated answers are intentionally outside Mimir core. Use Claude, Codex, OpenAI, a local model MCP server, or another trusted model runtime to synthesize from Mimir's cited context. -- Optional audio summaries use the separate `@jcode.labs/mimir-tts` workspace package. It renders - WAV files with Transformers.js and does not require Python, ffmpeg, Piper, XTTS, or a local server. +- Optional audio summaries use the separate `@jcode.labs/mimir-tts` workspace package. For the + highest quality, install the external `edge-tts` CLI and render Edge MP3 output with + `fr-FR-DeniseNeural`. For confidential or air-gapped content, use the Transformers.js WAV path + with `--engine transformers --offline`; it does not require Python, ffmpeg, Piper, XTTS, or a + local server. ## Install From npm @@ -289,28 +294,40 @@ Use `embeddingProvider: "local-hash"` for a no-model offline workflow. Use `embeddingProvider: "transformers"` with preloaded model files for semantic offline retrieval. Generated answers should come from a trusted external agent or model runtime. -### Generate A Local Audio Briefing +### Generate An Audio Briefing -Mimir includes a plug-and-play JS text-to-speech path for listenable summaries: +Mimir includes a plug-and-play text-to-speech path for listenable summaries. For the same quality +path as the global Voice Forge skill, install `edge-tts` and render MP3: ```bash pnpm exec kb audio --doctor -pnpm exec kb audio /tmp/MIMIR-SUMMARY-project.txt --out .mimir/audio/project-summary.wav +pipx install edge-tts +pnpm exec kb audio /tmp/MIMIR-SUMMARY-project.txt \ + --engine edge \ + --out .mimir/audio/project-summary.mp3 ``` -The command writes WAV output locally and does not require Python or ffmpeg. The first render can -download a public Transformers.js-compatible model into `.mimir/models/tts`; the narration text is -processed locally. For confidential air-gapped work, preload model files and run: +The Edge path uses the online Microsoft Edge TTS service through the `edge-tts` CLI. Use it only +when sending the narration text to that service is acceptable. + +For confidential or air-gapped work, preload Transformers.js-compatible model files and render WAV +offline: ```bash -pnpm exec kb audio /tmp/MIMIR-SUMMARY-project.txt --out .mimir/audio/project-summary.wav --offline +pnpm exec kb audio /tmp/MIMIR-SUMMARY-project.txt \ + --engine transformers \ + --offline \ + --model-path .mimir/models/tts \ + --out .mimir/audio/project-summary.wav ``` The standalone package can also be installed directly: ```bash pnpm add -D @jcode.labs/mimir-tts -pnpm exec mimir-tts render /tmp/MIMIR-SUMMARY-project.txt --out .mimir/audio/project-summary.wav +pnpm exec mimir-tts render /tmp/MIMIR-SUMMARY-project.txt \ + --engine edge \ + --out .mimir/audio/project-summary.mp3 ``` ## Agent Skills And MCP @@ -405,7 +422,8 @@ pnpm exec kb destroy-index --yes ``` For air-gapped operation, release verification, secure deletion limits, and threat model details, -read [`SECURITY-HARDENING.md`](./SECURITY-HARDENING.md). +read +[`SECURITY-HARDENING.md`](https://github.com/jcode-works/jcode-mimir/blob/main/SECURITY-HARDENING.md). ## Supported Files diff --git a/packages/mimir/SECURITY-HARDENING.md b/packages/mimir/SECURITY-HARDENING.md deleted file mode 100644 index f3873b9..0000000 --- a/packages/mimir/SECURITY-HARDENING.md +++ /dev/null @@ -1,194 +0,0 @@ -# Mimir Security Hardening - -Mimir is a sovereign local RAG knowledge base for confidential project documents and datasets. It is -built to minimize data movement, but it is not a certified high-assurance system. - -## Current Guarantees - -- Zero telemetry: Mimir does not send usage analytics or document content to JCode Labs. -- Retrieval-only core: Mimir does not call a chat model or generate LLM answers. -- No-model retrieval mode: `embeddingProvider: "local-hash"` can ingest, search, and return cited - passages without a model server. -- Optional semantic embeddings: `embeddingProvider: "transformers"` uses Transformers.js, with - remote model loading disabled by default through `transformersAllowRemoteModels: false`. -- Redaction before indexing: built-in DLP patterns redact common secrets and identifiers before - chunks are embedded and stored. -- Metadata-only access logs: access logs contain action metadata and query hashes, not raw - queries or retrieved text. -- Generated local state is ignored by Git: `.kb/`, `.mimir/`, and `private/**` are ignored by - default. -- MCP is read-focused: destructive tools are not exposed over MCP, and MCP retrieval is capped by - `mcpMaxTopK`. -- Optional audio summaries use `kb audio` / `@jcode.labs/mimir-tts` for local WAV rendering with - Transformers.js. They do not require Python, ffmpeg, Piper, XTTS, or a local TTS server. -- npm releases are published with provenance from the protected GitHub Actions workflow. -- Release artifacts include a package tarball, SHA256 checksums, SBOM, and manifest. - -## Threat Model - -Mimir protects against accidental repository leaks, accidental built-in LLM usage, accidental online -TTS usage for generated summaries, accidental secret indexing, and weak release traceability. - -Mimir does not protect against a compromised local machine, malicious dependencies already present -in the runtime, a user with filesystem access to the same checkout, or forensic recovery from an -unencrypted disk. - -## At-Rest Encryption - -Native encrypted LanceDB storage is not implemented yet. For sensitive environments, put the -repository and `.kb/` on an encrypted volume: - -- macOS: FileVault or an encrypted APFS volume. -- Linux: LUKS, fscrypt, or an encrypted VM disk. -- Containers/VMs: mount `.kb/` on an encrypted host volume. - -`kb destroy-index --yes` removes generated index files, but secure deletion on SSDs and copy-on-write -filesystems cannot be guaranteed without encrypted storage and key destruction. - -## Air-Gapped Operation - -Prepare artifacts on an internet-connected build machine: - -```bash -pnpm install --frozen-lockfile -pnpm build -pnpm release:artifacts -``` - -Move the generated tarballs from `release-artifacts/` into the offline environment and install them: - -```bash -pnpm add -D ./jcode.labs-mimir-tts-.tgz ./jcode.labs-mimir-.tgz -pnpm exec kb init -pnpm exec kb ingest -``` - -For semantic embeddings, preload the Transformers.js-compatible embedding model files inside the -offline environment under the configured `embeddingModelPath`. For audio, preload the TTS model -files under `.mimir/models/tts` and render with `pnpm exec kb audio --offline`. - -## Zero Network Posture - -Default no-model config: - -```json -{ - "embeddingProvider": "local-hash" -} -``` - -Optional semantic config: - -```json -{ - "embeddingProvider": "transformers", - "embeddingModel": "mixedbread-ai/mxbai-embed-xsmall-v1", - "embeddingModelPath": ".mimir/models", - "transformersAllowRemoteModels": false -} -``` - -The local-hash mode performs lexical/hash retrieval only. It is useful for smoke tests, -dependency-light offline workflows, and handing cited passages to another trusted LLM. It is not -equivalent to model semantic retrieval. - -Keep `transformersAllowRemoteModels` false for confidential or air-gapped work. If it is true, -Transformers.js may download model files from Hugging Face during model loading. - -Run: - -```bash -pnpm exec kb security-audit --strict -``` - -## DLP Redaction - -Built-in redaction is enabled by default for common secret and identifier shapes: private keys, -JWTs, API tokens, emails, IBANs, and card-like numbers. - -Custom patterns can be added in `.kb/config.json`: - -```json -{ - "redaction": { - "enabled": true, - "builtIn": true, - "patterns": [ - { - "name": "internal_case_id", - "pattern": "CASE-[0-9]+", - "replacement": "[CASE]" - } - ] - } -} -``` - -Redaction changes the indexed text, not the raw files under `private/`. - -## Optional Audio Summaries - -`kb install-skill` installs an optional `mimir-audio-summary` skill. It is designed for listenable -briefings from a local Mimir index. The default renderer is `kb audio`, backed by -`@jcode.labs/mimir-tts` and Transformers.js. - -Confidentiality defaults: - -- narration text is written to a temp file outside the repository; -- generated WAV audio should be written under `.mimir/audio/`; -- `.mimir/` is ignored by Git; -- Python, ffmpeg, Piper, XTTS, and local TTS servers are not required for the default path; -- the first online-enabled render may download public model weights into `.mimir/models/tts`, but - the narration text is processed locally; -- `--offline` disables remote model loading and requires preloaded model files. - -Generated audio can still contain sensitive information. Treat it like a derived confidential -document. - -## MCP Hardening - -MCP gives an agent access to retrieved private context. Use it only for agents running under the -same trust boundary as the repository. - -Mimir MCP defaults: - -- read-focused tools only; -- no index deletion tool exposed over MCP; -- bounded retrieval through `mcpMaxTopK`; -- metadata-only access logging. - -For team use, prefer one checkout per user or per role. Mimir does not implement RBAC. - -## Release Verification - -The protected npm workflow runs validation, generates release artifacts, and publishes both -workspace packages with provenance: - -```bash -pnpm --dir packages/mimir-tts publish --access public --provenance --no-git-checks -pnpm --dir packages/mimir publish --access public --provenance --no-git-checks -``` - -Release artifacts include: - -- npm tarballs for `@jcode.labs/mimir-tts` and `@jcode.labs/mimir`; -- `SHA256SUMS`; -- CycloneDX SBOM; -- `release-manifest.json`. - -Verify checksums offline with: - -```bash -sha256sum -c SHA256SUMS -``` - -On macOS: - -```bash -shasum -a 256 -c SHA256SUMS -``` - -## External Audit Status - -No external security audit has been completed yet. Treat Mimir as useful hardening for private -developer workflows, not as military-grade certified software. diff --git a/packages/mimir/SECURITY.md b/packages/mimir/SECURITY.md deleted file mode 100644 index 4472845..0000000 --- a/packages/mimir/SECURITY.md +++ /dev/null @@ -1,21 +0,0 @@ -# Security Policy - -## Supported Versions - -Only the latest published version of `@jcode.labs/mimir` receives security fixes. - -## Reporting A Vulnerability - -Please report vulnerabilities privately by email: - -```plain text -contact@jcode.works -``` - -Do not open public issues for vulnerabilities, leaked secrets, credential exposure, -or private document disclosure. - -## Data Boundary - -Mimir is designed to index local project documents. Raw project documents, -`.kb/storage/`, environment files, and credentials must remain outside commits. diff --git a/packages/mimir/dist/cli.js b/packages/mimir/dist/cli.js index 6879ed8..28fe1c4 100755 --- a/packages/mimir/dist/cli.js +++ b/packages/mimir/dist/cli.js @@ -171,11 +171,14 @@ program .command("audio") .description("Render a narration text file to local speech audio with Mimir TTS.") .argument("[text-file]", "Narration text file to render.") - .option("-o, --out ", "Output WAV path.") + .option("-o, --out ", "Output MP3 or WAV path.") + .option("--engine ", "TTS engine: auto, edge, or transformers.") .option("--model ", "Transformers.js TTS model ID.") .option("--model-path ", "Local model/cache path.") - .option("--offline", "Disable remote model downloads.") + .option("--offline", "Force the Transformers.js local/offline WAV path.") .option("--allow-remote-models", "Explicitly allow remote model downloads.") + .option("--voice ", "Edge voice. Defaults to fr-FR-DeniseNeural.") + .option("--rate ", "Edge rate. Defaults to +0%.") .option("--speaker-embeddings ", "Optional model-specific speaker embedding path or URL.") .option("--speed ", "Optional model-specific speech speed.", parseNumber) .option("--doctor", "Show TTS runtime readiness instead of rendering.") @@ -197,9 +200,12 @@ program textFile, }; addOption(renderOptions, "outputPath", options.out); + addOption(renderOptions, "engine", audioEngine(options)); addOption(renderOptions, "model", options.model); addOption(renderOptions, "modelPath", options.modelPath); addOption(renderOptions, "allowRemoteModels", audioAllowRemoteModels(options)); + addOption(renderOptions, "voice", options.voice); + addOption(renderOptions, "rate", options.rate); addOption(renderOptions, "speakerEmbeddings", options.speakerEmbeddings); addOption(renderOptions, "speed", options.speed); const result = await tts.renderSpeech(renderOptions); @@ -273,6 +279,18 @@ function audioAllowRemoteModels(options) { } return undefined; } +function audioEngine(options) { + if (options.offline) { + return "transformers"; + } + if (options.engine === undefined) { + return undefined; + } + if (options.engine === "auto" || options.engine === "edge" || options.engine === "transformers") { + return options.engine; + } + throw new Error("Expected --engine to be auto, edge, or transformers."); +} function printMaybeJson(value, json) { if (json) { console.log(JSON.stringify(value, null, 2)); diff --git a/packages/mimir/dist/cli.js.map b/packages/mimir/dist/cli.js.map index 637d2ab..b834932 100644 --- a/packages/mimir/dist/cli.js.map +++ b/packages/mimir/dist/cli.js.map @@ -1 +1 @@ -{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,MAAM,YAAY,CAAA;AAC3B,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAC3C,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AACnC,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAC7C,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAC3D,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAEtC,MAAM,0BAA0B,GAAG,GAAG,CAAA;AACtC,MAAM,gBAAgB,GAAG,uBAAuB,CAAA;AAEhD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAA;AAE7B,OAAO;KACJ,IAAI,CAAC,IAAI,CAAC;KACV,WAAW,CAAC,+DAA+D,CAAC;KAC5E,OAAO,CAAC,OAAO,CAAC,CAAA;AAEnB,OAAO;KACJ,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,iFAAiF,CAAC;KAC9F,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAA;IAChD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC,CAAA;QAC7C,OAAM;IACR,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAA;IACjC,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;QAC3B,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC,CAAA;IAC5B,CAAC;AACH,CAAC,CAAC,CAAA;AAEJ,OAAO;KACJ,OAAO,CAAC,QAAQ,CAAC;KACjB,WAAW,CAAC,oFAAoF,CAAC;KACjG,MAAM,CAAC,WAAW,EAAE,qEAAqE,CAAC;KAC1F,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAA;IAClE,OAAO,CAAC,GAAG,CACT,EAAE,CAAC,KAAK,CACN,sBAAsB,MAAM,CAAC,YAAY,WAAW,MAAM,CAAC,MAAM,iBAAiB,MAAM,CAAC,YAAY,eAAe,MAAM,CAAC,UAAU,WAAW,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,CACvK,CACF,CAAA;IACD,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAClC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,OAAO,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAA;IAC9D,CAAC;IACD,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC7B,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAA;IACtB,CAAC;AACH,CAAC,CAAC,CAAA;AAEJ,OAAO;KACJ,OAAO,CAAC,QAAQ,CAAC;KACjB,WAAW,CAAC,6DAA6D,CAAC;KAC1E,QAAQ,CAAC,SAAS,EAAE,eAAe,CAAC;KACpC,MAAM,CAAC,sBAAsB,EAAE,+BAA+B,EAAE,gBAAgB,CAAC;KACjF,MAAM,CAAC,KAAK,EAAE,KAAa,EAAE,OAA0B,EAAE,EAAE;IAC1D,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAA;IAC3D,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,CAAC,sDAAsD,CAAC,CAAC,CAAA;QAChF,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAA;QACpB,OAAM;IACR,CAAC;IAED,KAAK,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;QAChD,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;QAC9E,OAAO,CAAC,GAAG,CACT,KAAK,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,GAAG,CAAC,KAAK,MAAM,CAAC,YAAY,EAAE,CAAC,UAAU,MAAM,CAAC,UAAU,aAAa,QAAQ,EAAE,CACxG,CAAA;QACD,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,0BAA0B,CAAC,CAAC,CAAA;IAC/D,CAAC;AACH,CAAC,CAAC,CAAA;AAEJ,OAAO;KACJ,OAAO,CAAC,KAAK,CAAC;KACd,WAAW,CAAC,uEAAuE,CAAC;KACpF,QAAQ,CAAC,SAAS,EAAE,qBAAqB,CAAC;KAC1C,MAAM,CAAC,sBAAsB,EAAE,4BAA4B,EAAE,gBAAgB,CAAC;KAC9E,MAAM,CAAC,KAAK,EAAE,KAAa,EAAE,OAA0B,EAAE,EAAE;IAC1D,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAA;IACvD,OAAO,CAAC,GAAG,CAAC,KAAK,MAAM,CAAC,MAAM,IAAI,CAAC,CAAA;IACnC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9B,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAA;QAC/B,KAAK,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;YACvD,OAAO,CAAC,GAAG,CAAC,MAAM,KAAK,GAAG,CAAC,KAAK,MAAM,CAAC,YAAY,UAAU,MAAM,CAAC,UAAU,EAAE,CAAC,CAAA;QACnF,CAAC;IACH,CAAC;AACH,CAAC,CAAC,CAAA;AAEJ,OAAO;KACJ,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CAAC,gEAAgE,CAAC;KAC7E,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAA;IACzC,OAAO,CAAC,GAAG,CAAC,kBAAkB,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,CAAC,CAAA;IAC7D,OAAO,CAAC,GAAG,CAAC,gBAAgB,MAAM,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC,CAAA;IACzD,OAAO,CAAC,GAAG,CAAC,eAAe,MAAM,CAAC,WAAW,EAAE,CAAC,CAAA;IAChD,OAAO,CAAC,GAAG,CAAC,oBAAoB,MAAM,CAAC,gBAAgB,CAAC,MAAM,EAAE,CAAC,CAAA;IACjE,OAAO,CAAC,GAAG,CAAC,gBAAgB,MAAM,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC,CAAA;IAEzD,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,gBAAgB,EAAE,CAAC;QAC3C,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC,CAAA;IAC5C,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;QACvC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,CAAA;IACvC,CAAC;IAED,IAAI,MAAM,CAAC,gBAAgB,CAAC,MAAM,GAAG,CAAC,IAAI,MAAM,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzE,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAA;IACtB,CAAC;AACH,CAAC,CAAC,CAAA;AAEJ,OAAO;KACJ,OAAO,CAAC,QAAQ,CAAC;KACjB,WAAW,CAAC,gDAAgD,CAAC;KAC7D,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAA;IAC9C,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC,CAAA;IACpC,OAAO,CAAC,GAAG,CAAC,eAAe,MAAM,CAAC,WAAW,EAAE,CAAC,CAAA;IAChD,OAAO,CAAC,GAAG,CAAC,UAAU,MAAM,CAAC,MAAM,EAAE,CAAC,CAAA;IACtC,OAAO,CAAC,GAAG,CAAC,cAAc,MAAM,CAAC,UAAU,EAAE,CAAC,CAAA;IAC9C,OAAO,CAAC,GAAG,CAAC,eAAe,MAAM,CAAC,WAAW,EAAE,CAAC,CAAA;IAChD,OAAO,CAAC,GAAG,CAAC,iBAAiB,MAAM,CAAC,aAAa,EAAE,CAAC,CAAA;IACpD,OAAO,CAAC,GAAG,CAAC,sBAAsB,MAAM,CAAC,kBAAkB,EAAE,CAAC,CAAA;IAC9D,OAAO,CAAC,GAAG,CAAC,qBAAqB,MAAM,CAAC,iBAAiB,EAAE,CAAC,CAAA;IAC5D,OAAO,CAAC,GAAG,CAAC,kBAAkB,MAAM,CAAC,cAAc,EAAE,CAAC,CAAA;IACtD,OAAO,CAAC,GAAG,CAAC,iCAAiC,MAAM,CAAC,6BAA6B,EAAE,CAAC,CAAA;IACpF,OAAO,CAAC,GAAG,CAAC,oBAAoB,MAAM,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC,CAAA;IAC3D,OAAO,CAAC,GAAG,CAAC,aAAa,MAAM,CAAC,SAAS,EAAE,CAAC,CAAA;IAC5C,OAAO,CAAC,GAAG,CAAC,cAAc,MAAM,CAAC,UAAU,EAAE,CAAC,CAAA;IAC9C,OAAO,CAAC,GAAG,CAAC,qBAAqB,MAAM,CAAC,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;IACtE,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,EAAE,CAAC,CAAA;AACtC,CAAC,CAAC,CAAA;AAEJ,OAAO;KACJ,OAAO,CAAC,gBAAgB,CAAC;KACzB,WAAW,CAAC,sEAAsE,CAAC;KACnF,MAAM,CAAC,QAAQ,EAAE,8BAA8B,CAAC;KAChD,MAAM,CAAC,UAAU,EAAE,6CAA6C,CAAC;KACjE,MAAM,CAAC,KAAK,EAAE,OAA6C,EAAE,EAAE;IAC9D,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAA;IACjD,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QACjB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAA;IAC9C,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,iBAAiB,MAAM,CAAC,aAAa,EAAE,CAAC,CAAA;QACpD,OAAO,CAAC,GAAG,CAAC,qBAAqB,MAAM,CAAC,SAAS,CAAC,SAAS,EAAE,CAAC,CAAA;QAC9D,OAAO,CAAC,GAAG,CAAC,kBAAkB,MAAM,CAAC,SAAS,CAAC,cAAc,EAAE,CAAC,CAAA;QAChE,OAAO,CAAC,GAAG,CAAC,sBAAsB,MAAM,CAAC,SAAS,CAAC,kBAAkB,EAAE,CAAC,CAAA;QACxE,OAAO,CAAC,GAAG,CAAC,iCAAiC,MAAM,CAAC,SAAS,CAAC,6BAA6B,EAAE,CAAC,CAAA;QAC9F,OAAO,CAAC,GAAG,CAAC,iBAAiB,MAAM,CAAC,SAAS,CAAC,aAAa,EAAE,CAAC,CAAA;QAC9D,OAAO,CAAC,GAAG,CAAC,oBAAoB,MAAM,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC,CAAA;QAC3D,OAAO,CAAC,GAAG,CAAC,oBAAoB,MAAM,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC,CAAA;QAC3D,OAAO,CAAC,GAAG,CAAC,aAAa,MAAM,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC,CAAA;QACpD,OAAO,CAAC,GAAG,CAAC,6BAA6B,MAAM,CAAC,SAAS,CAAC,gBAAgB,EAAE,CAAC,CAAA;QAC7E,OAAO,CAAC,GAAG,CAAC,qBAAqB,MAAM,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC,CAAA;QAC7D,OAAO,CAAC,GAAG,CAAC,cAAc,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,CAAA;QAC/C,OAAO,CAAC,GAAG,CAAC,8BAA8B,MAAM,CAAC,GAAG,CAAC,uBAAuB,EAAE,CAAC,CAAA;QAC/E,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;YACtC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,YAAY,OAAO,EAAE,CAAC,CAAC,CAAA;QAC/C,CAAC;IACH,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACjD,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAA;IACtB,CAAC;AACH,CAAC,CAAC,CAAA;AAEJ,OAAO;KACJ,OAAO,CAAC,eAAe,CAAC;KACxB,WAAW,CAAC,2DAA2D,CAAC;KACxE,MAAM,CAAC,OAAO,EAAE,iDAAiD,CAAC;KAClE,MAAM,CAAC,KAAK,EAAE,OAA0B,EAAE,EAAE;IAC3C,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;QACjB,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,6CAA6C,CAAC,CAAC,CAAA;QACpE,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAA;QACpB,OAAM;IACR,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAA;IAChD,OAAO,CAAC,GAAG,CAAC,cAAc,MAAM,CAAC,UAAU,EAAE,CAAC,CAAA;IAC9C,OAAO,CAAC,GAAG,CAAC,WAAW,MAAM,CAAC,OAAO,EAAE,CAAC,CAAA;IACxC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;AAC1B,CAAC,CAAC,CAAA;AAEJ,OAAO;KACJ,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CAAC,oEAAoE,CAAC;KACjF,QAAQ,CAAC,aAAa,EAAE,gCAAgC,CAAC;KACzD,MAAM,CAAC,kBAAkB,EAAE,yBAAyB,CAAC;KACrD,MAAM,CAAC,mBAAmB,EAAE,0CAA0C,CAAC;KACvE,MAAM,CAAC,cAAc,EAAE,+BAA+B,CAAC;KACvD,MAAM,CAAC,qBAAqB,EAAE,yBAAyB,CAAC;KACxD,MAAM,CAAC,WAAW,EAAE,mDAAmD,CAAC;KACxE,MAAM,CAAC,uBAAuB,EAAE,0CAA0C,CAAC;KAC3E,MAAM,CAAC,iBAAiB,EAAE,6CAA6C,CAAC;KACxE,MAAM,CAAC,eAAe,EAAE,6BAA6B,CAAC;KACtD,MAAM,CAAC,6BAA6B,EAAE,wDAAwD,CAAC;KAC/F,MAAM,CAAC,kBAAkB,EAAE,uCAAuC,EAAE,WAAW,CAAC;KAChF,MAAM,CAAC,UAAU,EAAE,kDAAkD,CAAC;KACtE,MAAM,CAAC,QAAQ,EAAE,8BAA8B,CAAC;KAChD,MAAM,CAAC,KAAK,EAAE,QAA4B,EAAE,OAAqB,EAAE,EAAE;IACpE,MAAM,GAAG,GAAG,MAAM,OAAO,EAAE,CAAA;IAE3B,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACnB,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,MAAM,EAAE,CAAA;QACjC,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;QACpC,OAAM;IACR,CAAC;IAED,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,gDAAgD,CAAC,CAAC,CAAA;QACvE,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAA;QACpB,OAAM;IACR,CAAC;IAED,MAAM,aAAa,GAAqB;QACtC,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE;QAClB,QAAQ;KACT,CAAA;IACD,SAAS,CAAC,aAAa,EAAE,YAAY,EAAE,OAAO,CAAC,GAAG,CAAC,CAAA;IACnD,SAAS,CAAC,aAAa,EAAE,QAAQ,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC,CAAA;IACxD,SAAS,CAAC,aAAa,EAAE,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,CAAA;IAChD,SAAS,CAAC,aAAa,EAAE,WAAW,EAAE,OAAO,CAAC,SAAS,CAAC,CAAA;IACxD,SAAS,CAAC,aAAa,EAAE,mBAAmB,EAAE,sBAAsB,CAAC,OAAO,CAAC,CAAC,CAAA;IAC9E,SAAS,CAAC,aAAa,EAAE,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,CAAA;IAChD,SAAS,CAAC,aAAa,EAAE,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;IAC9C,SAAS,CAAC,aAAa,EAAE,mBAAmB,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAA;IACxE,SAAS,CAAC,aAAa,EAAE,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,CAAA;IAEhD,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,YAAY,CAAC,aAAa,CAAC,CAAA;IACpD,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;AACtC,CAAC,CAAC,CAAA;AAEJ,OAAO;KACJ,OAAO,CAAC,WAAW,CAAC;KACpB,WAAW,CACV,qFAAqF,CACtF;KACA,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,MAAM,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAA;AAC/B,CAAC,CAAC,CAAA;AAEJ,OAAO;KACJ,OAAO,CAAC,YAAY,CAAC;KACrB,WAAW,CAAC,+EAA+E,CAAC;KAC5F,MAAM,CAAC,GAAG,EAAE;IACX,OAAO,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC,CAAA;AACjC,CAAC,CAAC,CAAA;AAEJ,OAAO;KACJ,OAAO,CAAC,eAAe,CAAC;KACxB,WAAW,CAAC,kFAAkF,CAAC;KAC/F,MAAM,CACL,qBAAqB,EACrB,oDAAoD,EACpD,eAAe,CAChB;KACA,MAAM,CAAC,KAAK,EAAE,OAA8B,EAAE,EAAE;IAC/C,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC,CAAA;IACvF,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAA;IACzC,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QAClC,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC,CAAA;IAC5B,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,eAAe,MAAM,CAAC,SAAS,EAAE,CAAC,CAAA;IAC9C,OAAO,CAAC,GAAG,CAAC,8BAA8B,MAAM,CAAC,cAAc,EAAE,CAAC,CAAA;IAClE,OAAO,CAAC,GAAG,CAAC,uBAAuB,MAAM,CAAC,aAAa,EAAE,CAAC,CAAA;AAC5D,CAAC,CAAC,CAAA;AAEJ,MAAM,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;AAEtC,SAAS,gBAAgB,CAAC,KAAa;IACrC,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;IACzC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,MAAM,IAAI,CAAC,EAAE,CAAC;QAC7C,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAA;IACjD,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC;AAED,SAAS,WAAW,CAAC,KAAa;IAChC,MAAM,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAA;IACvC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAA;IACvC,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC;AAED,SAAS,QAAQ,CAAC,IAAwB;IACxC,OAAO,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,CAAA;AACnF,CAAC;AAoCD,KAAK,UAAU,OAAO;IACpB,MAAM,MAAM,GAAY,MAAM,MAAM,CAAC,gBAAgB,CAAC,CAAA;IACtD,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,GAAG,gBAAgB,uCAAuC,CAAC,CAAA;IAC7E,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC;AAED,SAAS,WAAW,CAAC,KAAc;IACjC,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACd,QAAQ,IAAI,KAAK;QACjB,OAAO,KAAK,CAAC,MAAM,KAAK,UAAU;QAClC,cAAc,IAAI,KAAK;QACvB,OAAO,KAAK,CAAC,YAAY,KAAK,UAAU,CACzC,CAAA;AACH,CAAC;AAED,SAAS,sBAAsB,CAAC,OAAqB;IACnD,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QACpB,OAAO,KAAK,CAAA;IACd,CAAC;IACD,IAAI,OAAO,CAAC,iBAAiB,EAAE,CAAC;QAC9B,OAAO,IAAI,CAAA;IACb,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC;AAED,SAAS,WAAW,CAAC,OAAqB;IACxC,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QACpB,OAAO,cAAc,CAAA;IACvB,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QACjC,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,KAAK,MAAM,IAAI,OAAO,CAAC,MAAM,KAAK,MAAM,IAAI,OAAO,CAAC,MAAM,KAAK,cAAc,EAAE,CAAC;QAChG,OAAO,OAAO,CAAC,MAAM,CAAA;IACvB,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAA;AACzE,CAAC;AAED,SAAS,cAAc,CAAC,KAAc,EAAE,IAAyB;IAC/D,IAAI,IAAI,EAAE,CAAC;QACT,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAA;QAC3C,OAAM;IACR,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QAChD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACjD,OAAO,CAAC,GAAG,CAAC,GAAG,GAAG,IAAI,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;QACxC,CAAC;QACD,OAAM;IACR,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAA;AAC5B,CAAC;AAED,SAAS,SAAS,CAChB,MAAS,EACT,GAAM,EACN,KAAuB;IAEvB,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAA;IACrB,CAAC;AACH,CAAC"} \ No newline at end of file diff --git a/packages/mimir/dist/version.d.ts b/packages/mimir/dist/version.d.ts index 72b1961..48eea66 100644 --- a/packages/mimir/dist/version.d.ts +++ b/packages/mimir/dist/version.d.ts @@ -1,2 +1,2 @@ -export declare const VERSION = "0.4.0"; +export declare const VERSION = "0.4.1"; //# sourceMappingURL=version.d.ts.map \ No newline at end of file diff --git a/packages/mimir/dist/version.js b/packages/mimir/dist/version.js index 112a255..8ef4514 100644 --- a/packages/mimir/dist/version.js +++ b/packages/mimir/dist/version.js @@ -1,2 +1,2 @@ -export const VERSION = "0.4.0"; +export const VERSION = "0.4.1"; //# sourceMappingURL=version.js.map \ No newline at end of file diff --git a/packages/mimir/package.json b/packages/mimir/package.json index 94faa7e..58a8fc1 100644 --- a/packages/mimir/package.json +++ b/packages/mimir/package.json @@ -1,6 +1,6 @@ { "name": "@jcode.labs/mimir", - "version": "0.4.0", + "version": "0.4.1", "description": "Mimir: open-source sovereign local RAG for confidential datasets and AI agents.", "type": "module", "license": "MIT", @@ -48,12 +48,7 @@ "dist", "skills", "examples", - "README.md", - "LICENSE", - "CONTRIBUTING.md", - "SECURITY-HARDENING.md", - "CHANGELOG.md", - "SECURITY.md" + "README.md" ], "publishConfig": { "access": "public" diff --git a/packages/mimir/skills/mimir-audio-summary/SKILL.md b/packages/mimir/skills/mimir-audio-summary/SKILL.md index 8d4661f..c5ec4cb 100644 --- a/packages/mimir/skills/mimir-audio-summary/SKILL.md +++ b/packages/mimir/skills/mimir-audio-summary/SKILL.md @@ -17,8 +17,10 @@ The knowledge base stays local; the final audio is a generated artifact and must - Treat the source documents, retrieved passages, generated narration, and final audio as sensitive. - Do not use online TTS for confidential content unless the user explicitly allows it. -- Prefer `pnpm exec kb audio` or `pnpm exec mimir-tts render` for plug-and-play local WAV output. -- Use `--offline` when model files are already present and remote model loading is not allowed. +- Prefer `pnpm exec kb audio` or `pnpm exec mimir-tts render` for plug-and-play output. +- Use `--engine edge` only when online TTS is acceptable and global Voice Forge quality is required. +- Use `--engine transformers --offline` when model files are already present and remote model + loading is not allowed. - Write the narration text to a temp file outside the repository, such as `/tmp/MIMIR-SUMMARY-topic.txt`. - Render audio under `.mimir/audio/` by default. This directory is ignored by Git when Mimir is installed. - Never stage or commit generated audio, temporary text, WAV, AIFF, or intermediate files. @@ -85,27 +87,32 @@ Create the output directory and write the narration to a temp file outside the r mkdir -p .mimir/audio ``` -Then render with the default Mimir TTS path: +For global Voice Forge quality on non-confidential text, render with Edge MP3: ```bash pnpm exec kb audio /tmp/MIMIR-SUMMARY-.txt \ - --out .mimir/audio/MIMIR-SUMMARY-.wav + --engine edge \ + --out .mimir/audio/MIMIR-SUMMARY-.mp3 ``` -For air-gapped operation, preload the model files under `.mimir/models/tts` and run: +The Edge path uses the online Edge TTS service through the `edge-tts` CLI. Use it only when sending +the narration text to that service is acceptable. + +For confidential or air-gapped operation, preload the model files under `.mimir/models/tts` and run: ```bash pnpm exec kb audio /tmp/MIMIR-SUMMARY-.txt \ - --out .mimir/audio/MIMIR-SUMMARY-.wav \ - --offline + --engine transformers \ + --offline \ + --model-path .mimir/models/tts \ + --out .mimir/audio/MIMIR-SUMMARY-.wav ``` -The default renderer uses `@jcode.labs/mimir-tts` and Transformers.js. It does not require Python, -ffmpeg, Piper, XTTS, or a local TTS server. The first non-offline render can download public model -files into `.mimir/models/tts`, but the narration text is processed locally. +The Transformers.js path does not require Python, ffmpeg, Piper, XTTS, or a local TTS server. The +first non-offline Transformers render can download public model files into `.mimir/models/tts`, but +the narration text is processed locally. -Use the legacy voice-forge helper only when the user explicitly wants MP3 output or an engine not -covered by Mimir TTS: +Use the voice-forge helper only when the user explicitly wants XTTS, macOS `say`, or Piper: ```bash OUT_MP3="/.mimir/audio/MIMIR-SUMMARY-.mp3" \ @@ -113,14 +120,13 @@ OUT_MP3="/.mimir/audio/MIMIR-SUMMARY-.mp3" \ bash /forge-voice.sh /tmp/MIMIR-SUMMARY-.txt ``` -Engine selection: +Helper engine selection: -- `auto`: confidentiality-first order: XTTS, macOS `say`, Piper, then online Edge only when - `MIMIR_ALLOW_ONLINE_TTS=1`. +- `auto`: Edge first when installed, then XTTS, macOS `say`, and Piper. +- `edge`: online Edge TTS with the global Voice Forge default voice. - `xtts`: local Coqui XTTS-v2. - `say`: local macOS speech engine, converted to MP3 with `ffmpeg`. - `piper`: local neural TTS, converted to MP3 with `ffmpeg`. -- `edge`: online Edge TTS. Use only with explicit user approval for non-sensitive text. Voice can be selected with `TTS_VOICE`. Speed for Edge can be selected with `TTS_RATE`. diff --git a/packages/mimir/skills/mimir-audio-summary/forge-voice.sh b/packages/mimir/skills/mimir-audio-summary/forge-voice.sh index 36c5bda..b762446 100755 --- a/packages/mimir/skills/mimir-audio-summary/forge-voice.sh +++ b/packages/mimir/skills/mimir-audio-summary/forge-voice.sh @@ -2,11 +2,13 @@ # Render a Mimir audio-summary text file to MP3. The text is a throwaway intermediate written # outside the repository by the skill. The final audio should normally be written under .mimir/audio. # -# Engine via TTS_ENGINE (auto|xtts|say|piper|edge); default "auto" is confidentiality-first: +# Engine via TTS_ENGINE (auto|edge|xtts|say|piper); default "auto" matches the global Voice Forge +# quality path: +# edge - edge-tts neural voices, single request (online). Default; clean on normal text. +# Set TTS_SEGMENT=1 to render sentence-by-sentence for long-text truncation. # xtts - local Coqui XTTS-v2 when installed. # say - macOS built-in, offline, clean but robotic. # piper - local neural TTS. -# edge - online Edge TTS only when TTS_ENGINE=edge or MIMIR_ALLOW_ONLINE_TTS=1. # # Usage: forge-voice.sh [voice] # Env: OUT_MP3 explicit mp3 output path @@ -16,7 +18,6 @@ # TTS_SEGMENT=1 edge: render sentence-by-sentence # XTTS_SPEAKER xtts preset speaker (default Ana Florence) # PIPER_MODEL piper onnx model path -# MIMIR_ALLOW_ONLINE_TTS=1 allow edge in auto mode # KEEP_TEXT=1 keep the source text file after a successful render set -euo pipefail @@ -60,6 +61,44 @@ to_mp3() { exit 1 } +if [ "$ENGINE" = "edge" ] || [ "$ENGINE" = "auto" ]; then + if command -v edge-tts >/dev/null 2>&1; then + OUT="${OUTBASE}.mp3" + SPLIT="$SCRIPT_DIR/split-lines.py" + voice="${VOICE:-fr-FR-DeniseNeural}" + rate="${TTS_RATE:-+0%}" + if [ "${TTS_SEGMENT:-0}" = "1" ] && [ -f "$SPLIT" ] \ + && command -v ffmpeg >/dev/null 2>&1 && command -v python3 >/dev/null 2>&1; then + TMP="$(mktemp -d)" + ffmpeg -y -loglevel error -f lavfi -i anullsrc=r=24000:cl=mono -t 0.28 \ + -c:a libmp3lame -q:a 4 "$TMP/sil.mp3" + i=0 + : > "$TMP/list.txt" + while IFS= read -r line; do + [ -z "$line" ] && continue + i=$((i + 1)) + if edge-tts --text "$line" --voice "$voice" --rate="$rate" \ + --write-media "$TMP/seg_$i.mp3" >/dev/null 2>&1; then + printf "file '%s'\n" "$TMP/seg_$i.mp3" >> "$TMP/list.txt" + printf "file '%s'\n" "$TMP/sil.mp3" >> "$TMP/list.txt" + else + echo "warn: edge-tts failed on a sentence, skipping it" >&2 + fi + done < <(python3 "$SPLIT" "$TXT") + ffmpeg -y -loglevel error -f concat -safe 0 -i "$TMP/list.txt" \ + -ac 1 -c:a libmp3lame -q:a 4 "$OUT" + rm -rf "$TMP" + finish "$OUT" + fi + edge-tts --file "$TXT" --voice "$voice" --rate="$rate" --write-media "$OUT" >/dev/null + finish "$OUT" + fi + if [ "$ENGINE" = "edge" ]; then + echo "error: TTS_ENGINE=edge but edge-tts not installed (pipx install edge-tts)" >&2 + exit 1 + fi +fi + if [ "$ENGINE" = "xtts" ] || { [ "$ENGINE" = "auto" ] && xtts_ready; }; then if ! xtts_ready; then echo "error: TTS_ENGINE=xtts but venv/helper missing ($XTTS_PY)" >&2 @@ -98,56 +137,14 @@ if [ "$ENGINE" = "piper" ] || [ "$ENGINE" = "auto" ]; then fi fi -if [ "$ENGINE" = "edge" ] || { [ "$ENGINE" = "auto" ] && [ "${MIMIR_ALLOW_ONLINE_TTS:-0}" = "1" ]; }; then - if command -v edge-tts >/dev/null 2>&1; then - OUT="${OUTBASE}.mp3" - SPLIT="$SCRIPT_DIR/split-lines.py" - voice="${VOICE:-fr-FR-DeniseNeural}" - rate="${TTS_RATE:-+0%}" - if [ "${TTS_SEGMENT:-0}" = "1" ] && [ -f "$SPLIT" ] \ - && command -v ffmpeg >/dev/null 2>&1 && command -v python3 >/dev/null 2>&1; then - TMP="$(mktemp -d)" - ffmpeg -y -loglevel error -f lavfi -i anullsrc=r=24000:cl=mono -t 0.28 \ - -c:a libmp3lame -q:a 4 "$TMP/sil.mp3" - i=0 - : > "$TMP/list.txt" - while IFS= read -r line; do - [ -z "$line" ] && continue - i=$((i + 1)) - if edge-tts --text "$line" --voice "$voice" --rate="$rate" \ - --write-media "$TMP/seg_$i.mp3" >/dev/null 2>&1; then - printf "file '%s'\n" "$TMP/seg_$i.mp3" >> "$TMP/list.txt" - printf "file '%s'\n" "$TMP/sil.mp3" >> "$TMP/list.txt" - else - echo "warn: edge-tts failed on a sentence, skipping it" >&2 - fi - done < <(python3 "$SPLIT" "$TXT") - ffmpeg -y -loglevel error -f concat -safe 0 -i "$TMP/list.txt" \ - -ac 1 -c:a libmp3lame -q:a 4 "$OUT" - rm -rf "$TMP" - finish "$OUT" - fi - edge-tts --file "$TXT" --voice "$voice" --rate="$rate" --write-media "$OUT" >/dev/null - finish "$OUT" - fi - if [ "$ENGINE" = "edge" ]; then - echo "error: TTS_ENGINE=edge but edge-tts not installed (pipx install edge-tts)" >&2 - exit 1 - fi -fi - cat >&2 <<'EOF' -error: no offline TTS engine available. Install one: +error: no TTS engine available. Install one: + edge-tts (cleanest, online): pipx install edge-tts XTTS-v2 (local): uv venv --python 3.11 ~/.local/share/voice-forge/xtts uv pip install --python ~/.local/share/voice-forge/xtts/bin/python \ coqui-tts 'transformers>=4.57,<5' 'torch==2.8.*' 'torchaudio==2.8.*' piper (local): pip install piper-tts ffmpeg: required for local engine MP3 conversion - -Online fallback: - pipx install edge-tts - MIMIR_ALLOW_ONLINE_TTS=1 TTS_ENGINE=auto ... - or explicitly set TTS_ENGINE=edge for non-sensitive text. EOF exit 127 diff --git a/packages/mimir/src/cli.ts b/packages/mimir/src/cli.ts index 531320c..9ffa268 100644 --- a/packages/mimir/src/cli.ts +++ b/packages/mimir/src/cli.ts @@ -192,11 +192,14 @@ program .command("audio") .description("Render a narration text file to local speech audio with Mimir TTS.") .argument("[text-file]", "Narration text file to render.") - .option("-o, --out ", "Output WAV path.") + .option("-o, --out ", "Output MP3 or WAV path.") + .option("--engine ", "TTS engine: auto, edge, or transformers.") .option("--model ", "Transformers.js TTS model ID.") .option("--model-path ", "Local model/cache path.") - .option("--offline", "Disable remote model downloads.") + .option("--offline", "Force the Transformers.js local/offline WAV path.") .option("--allow-remote-models", "Explicitly allow remote model downloads.") + .option("--voice ", "Edge voice. Defaults to fr-FR-DeniseNeural.") + .option("--rate ", "Edge rate. Defaults to +0%.") .option("--speaker-embeddings ", "Optional model-specific speaker embedding path or URL.") .option("--speed ", "Optional model-specific speech speed.", parseNumber) .option("--doctor", "Show TTS runtime readiness instead of rendering.") @@ -221,9 +224,12 @@ program textFile, } addOption(renderOptions, "outputPath", options.out) + addOption(renderOptions, "engine", audioEngine(options)) addOption(renderOptions, "model", options.model) addOption(renderOptions, "modelPath", options.modelPath) addOption(renderOptions, "allowRemoteModels", audioAllowRemoteModels(options)) + addOption(renderOptions, "voice", options.voice) + addOption(renderOptions, "rate", options.rate) addOption(renderOptions, "speakerEmbeddings", options.speakerEmbeddings) addOption(renderOptions, "speed", options.speed) @@ -290,10 +296,13 @@ function withTopK(topK: number | undefined): { cwd: string; topK?: number } { interface AudioOptions { out?: string + engine?: string model?: string modelPath?: string offline?: boolean allowRemoteModels?: boolean + voice?: string + rate?: string speakerEmbeddings?: string speed?: number doctor?: boolean @@ -309,9 +318,12 @@ interface TtsRenderOptions { cwd: string textFile: string outputPath?: string + engine?: "auto" | "edge" | "transformers" model?: string modelPath?: string allowRemoteModels?: boolean + voice?: string + rate?: string speakerEmbeddings?: string speed?: number } @@ -345,6 +357,19 @@ function audioAllowRemoteModels(options: AudioOptions): boolean | undefined { return undefined } +function audioEngine(options: AudioOptions): TtsRenderOptions["engine"] | undefined { + if (options.offline) { + return "transformers" + } + if (options.engine === undefined) { + return undefined + } + if (options.engine === "auto" || options.engine === "edge" || options.engine === "transformers") { + return options.engine + } + throw new Error("Expected --engine to be auto, edge, or transformers.") +} + function printMaybeJson(value: unknown, json: boolean | undefined): void { if (json) { console.log(JSON.stringify(value, null, 2)) diff --git a/packages/mimir/src/version.ts b/packages/mimir/src/version.ts index 9216d62..3398138 100644 --- a/packages/mimir/src/version.ts +++ b/packages/mimir/src/version.ts @@ -1 +1 @@ -export const VERSION = "0.4.0" +export const VERSION = "0.4.1" diff --git a/scripts/smoke.mjs b/scripts/smoke.mjs index 118e833..2d958b2 100644 --- a/scripts/smoke.mjs +++ b/scripts/smoke.mjs @@ -58,8 +58,8 @@ try { ) assertIncludes( audioDoctor.stdout, - '"outputFormat": "wav"', - "audio doctor should report WAV output", + '"outputFormat": "mp3-or-wav"', + "audio doctor should report MP3 or WAV output", ) await runKb(["install-skill"], tempRoot)