lyrravoice 0.1.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.
package/dist/index.js ADDED
@@ -0,0 +1,458 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ APIError: () => APIError,
24
+ AudioProcessing: () => AudioProcessing,
25
+ AudioResponse: () => AudioResponse,
26
+ Jobs: () => Jobs,
27
+ LyrraVoice: () => LyrraVoice,
28
+ LyrraVoiceError: () => LyrraVoiceError,
29
+ Mcp: () => Mcp,
30
+ TimeoutError: () => TimeoutError,
31
+ Voices: () => Voices
32
+ });
33
+ module.exports = __toCommonJS(index_exports);
34
+
35
+ // src/error.ts
36
+ var LyrraVoiceError = class extends Error {
37
+ constructor(message) {
38
+ super(message);
39
+ this.name = "LyrraVoiceError";
40
+ }
41
+ };
42
+ var APIError = class extends LyrraVoiceError {
43
+ status;
44
+ detail;
45
+ body;
46
+ constructor(status, body) {
47
+ const detail = extractDetail(body);
48
+ super(detail ?? `API error ${status}`);
49
+ this.name = "APIError";
50
+ this.status = status;
51
+ this.detail = detail;
52
+ this.body = body;
53
+ }
54
+ };
55
+ function extractDetail(body) {
56
+ if (body && typeof body === "object" && "detail" in body) {
57
+ const d = body.detail;
58
+ if (typeof d === "string") return d;
59
+ if (typeof d === "object") return JSON.stringify(d);
60
+ }
61
+ return void 0;
62
+ }
63
+ var TimeoutError = class extends LyrraVoiceError {
64
+ constructor(message = "Request timed out") {
65
+ super(message);
66
+ this.name = "TimeoutError";
67
+ }
68
+ };
69
+
70
+ // src/audio.ts
71
+ var import_promises = require("fs/promises");
72
+ var AudioResponse = class {
73
+ _response;
74
+ _buffer = null;
75
+ constructor(response) {
76
+ this._response = response;
77
+ }
78
+ /** The raw Response object. */
79
+ get response() {
80
+ return this._response;
81
+ }
82
+ /** Content-Type header value. */
83
+ get contentType() {
84
+ return this._response.headers.get("content-type") ?? "audio/wav";
85
+ }
86
+ /** Read the full response as an ArrayBuffer (cached). */
87
+ async arrayBuffer() {
88
+ if (!this._buffer) {
89
+ this._buffer = await this._response.arrayBuffer();
90
+ }
91
+ return this._buffer;
92
+ }
93
+ /** Read the full response as a Node.js Buffer. */
94
+ async buffer() {
95
+ const ab = await this.arrayBuffer();
96
+ return Buffer.from(ab);
97
+ }
98
+ /** Get a ReadableStream of the response body. Only works if buffer() hasn't been called yet. */
99
+ stream() {
100
+ return this._response.body;
101
+ }
102
+ /** Save audio to a file. */
103
+ async save(path) {
104
+ const buf = await this.buffer();
105
+ await (0, import_promises.writeFile)(path, buf);
106
+ }
107
+ /** Return as a Blob (useful in non-Node environments). */
108
+ async blob() {
109
+ const ab = await this.arrayBuffer();
110
+ return new Blob([ab], { type: this.contentType });
111
+ }
112
+ };
113
+
114
+ // src/resources/voices.ts
115
+ var Voices = class {
116
+ constructor(_client) {
117
+ this._client = _client;
118
+ }
119
+ /** List all voices accessible to the current user. */
120
+ async list() {
121
+ return this._client._get("/api/voices");
122
+ }
123
+ /** List voices with preview URLs. */
124
+ async listWithPreviews(language = "fr") {
125
+ return this._client._get("/api/voices/with-previews", { language });
126
+ }
127
+ /** Get a test/preview audio for a voice. */
128
+ async test(voiceId, language = "fr") {
129
+ return this._client._getRaw(`/api/voices/${encodeURIComponent(voiceId)}/test`, { language });
130
+ }
131
+ /** Clone a voice from an audio sample. */
132
+ async clone(params) {
133
+ const form = new FormData();
134
+ form.set("name", params.name);
135
+ const blob = toBlob(params.audio, params.filename ?? "audio.wav");
136
+ form.set("audio", blob);
137
+ return this._client._postForm("/api/voices/clone", form);
138
+ }
139
+ /** Upload a file (.wav, .mp3, .pth) to an existing voice. */
140
+ async upload(voiceId, file, filename = "audio.wav") {
141
+ const form = new FormData();
142
+ const blob = file instanceof Blob ? file : new Blob([new Uint8Array(file)]);
143
+ form.set("file", blob, filename);
144
+ return this._client._postForm(
145
+ `/api/voices/${encodeURIComponent(voiceId)}`,
146
+ form
147
+ );
148
+ }
149
+ /** Rename a voice. */
150
+ async rename(voiceId, newName) {
151
+ const form = new FormData();
152
+ form.set("new_name", newName);
153
+ return this._client._putForm(
154
+ `/api/voices/${encodeURIComponent(voiceId)}/rename`,
155
+ form
156
+ );
157
+ }
158
+ /** Delete a voice. */
159
+ async delete(voiceId) {
160
+ return this._client._delete(
161
+ `/api/voices/${encodeURIComponent(voiceId)}`
162
+ );
163
+ }
164
+ /** Extract .pth embedding from voice audio references. */
165
+ async extractEmbedding(voiceId, force = false) {
166
+ return this._client._post(
167
+ `/api/voices/${encodeURIComponent(voiceId)}/extract-embedding`,
168
+ void 0,
169
+ { force: force ? "true" : void 0 }
170
+ );
171
+ }
172
+ /** List built-in XTTS v2 speaker presets. */
173
+ async presets() {
174
+ return this._client._get("/api/voices/presets");
175
+ }
176
+ /** Import a built-in preset as a user voice. */
177
+ async importPreset(params) {
178
+ const form = new FormData();
179
+ form.set("speaker_name", params.speaker_name);
180
+ if (params.voice_id) form.set("voice_id", params.voice_id);
181
+ return this._client._postForm("/api/voices/presets/import", form);
182
+ }
183
+ };
184
+ function toBlob(input, filename) {
185
+ if (input instanceof Blob) return input;
186
+ if (Buffer.isBuffer(input)) return new Blob([new Uint8Array(input)], { type: mimeFromName(filename) });
187
+ return input;
188
+ }
189
+ function mimeFromName(name) {
190
+ if (name.endsWith(".mp3")) return "audio/mpeg";
191
+ return "audio/wav";
192
+ }
193
+
194
+ // src/resources/jobs.ts
195
+ var Jobs = class {
196
+ constructor(_client) {
197
+ this._client = _client;
198
+ }
199
+ /** List TTS jobs. */
200
+ async list(params) {
201
+ return this._client._get("/api/tts/jobs", params);
202
+ }
203
+ /** Get a single job by ID. */
204
+ async get(jobId) {
205
+ return this._client._get(`/api/tts/jobs/${encodeURIComponent(jobId)}`);
206
+ }
207
+ /**
208
+ * Poll a job until it reaches a terminal state (completed / failed).
209
+ * Returns the final job object.
210
+ */
211
+ async poll(jobId, options) {
212
+ const interval = options?.interval ?? 1e3;
213
+ const timeout = options?.timeout ?? 3e5;
214
+ const deadline = Date.now() + timeout;
215
+ while (Date.now() < deadline) {
216
+ const job = await this.get(jobId);
217
+ if (job.status === "completed" || job.status === "failed") {
218
+ return job;
219
+ }
220
+ await sleep(interval);
221
+ }
222
+ throw new TimeoutError(`Job ${jobId} did not complete within ${timeout}ms`);
223
+ }
224
+ /** Download the audio output of a completed job. */
225
+ async download(fileId) {
226
+ return this._client._getRaw(`/api/tts/download/${encodeURIComponent(fileId)}`);
227
+ }
228
+ };
229
+ function sleep(ms) {
230
+ return new Promise((resolve) => setTimeout(resolve, ms));
231
+ }
232
+
233
+ // src/resources/audio-processing.ts
234
+ var AudioProcessing = class {
235
+ constructor(_client) {
236
+ this._client = _client;
237
+ }
238
+ /**
239
+ * Clean an audio file (noise reduction + normalization).
240
+ * Returns the cleaned audio as an AudioResponse.
241
+ */
242
+ async clean(params) {
243
+ const form = new FormData();
244
+ const blob = params.file instanceof Blob ? params.file : Buffer.isBuffer(params.file) ? new Blob([new Uint8Array(params.file)]) : params.file;
245
+ form.set("file", blob, params.filename ?? "audio.wav");
246
+ return this._client._postFormRaw("/api/audio/clean", form);
247
+ }
248
+ };
249
+
250
+ // src/resources/mcp.ts
251
+ var Mcp = class {
252
+ constructor(_client) {
253
+ this._client = _client;
254
+ }
255
+ /** List all MCP API keys for the current user. */
256
+ async listKeys() {
257
+ return this._client._get("/api/mcp/keys");
258
+ }
259
+ /**
260
+ * Create a new MCP API key.
261
+ * The full key is returned only once — store it securely.
262
+ */
263
+ async createKey(name) {
264
+ const form = new FormData();
265
+ form.set("name", name);
266
+ return this._client._postForm("/api/mcp/keys", form);
267
+ }
268
+ /** Revoke an MCP API key. */
269
+ async revokeKey(keyId) {
270
+ return this._client._delete(
271
+ `/api/mcp/keys/${encodeURIComponent(keyId)}`
272
+ );
273
+ }
274
+ /** Get MCP server info (endpoint, version, transport). */
275
+ async info() {
276
+ return this._client._get("/api/mcp/info");
277
+ }
278
+ };
279
+
280
+ // src/client.ts
281
+ var LyrraVoice = class {
282
+ _baseUrl;
283
+ _apiKey;
284
+ _token;
285
+ _timeout;
286
+ /** Voice management (list, clone, rename, delete, presets...) */
287
+ voices;
288
+ /** Async TTS job management (list, get, poll, download) */
289
+ jobs;
290
+ /** Audio processing (clean/denoise) */
291
+ audio;
292
+ /** MCP API key management */
293
+ mcp;
294
+ constructor(options = {}) {
295
+ this._baseUrl = (options.baseUrl ?? "http://localhost:5050").replace(/\/+$/, "");
296
+ this._apiKey = options.apiKey;
297
+ this._token = options.token;
298
+ this._timeout = options.timeout ?? 3e4;
299
+ this.voices = new Voices(this);
300
+ this.jobs = new Jobs(this);
301
+ this.audio = new AudioProcessing(this);
302
+ this.mcp = new Mcp(this);
303
+ }
304
+ // ── Public TTS methods ────────────────────────────────────────────
305
+ /** Synchronous TTS — returns audio. */
306
+ async generate(params) {
307
+ return this._postRaw("/api/tts", params);
308
+ }
309
+ /** Async TTS — submits a Celery job, returns job_id for polling. */
310
+ async generateAsync(params) {
311
+ return this._post("/api/tts/async", params);
312
+ }
313
+ /**
314
+ * SSE streaming TTS — yields audio chunks as they are generated.
315
+ *
316
+ * ```ts
317
+ * for await (const event of client.stream({ text: "Hello" })) {
318
+ * // event.audio is base64-encoded WAV chunk
319
+ * }
320
+ * ```
321
+ */
322
+ async *stream(params) {
323
+ const response = await this._fetch("/api/tts/stream", {
324
+ method: "POST",
325
+ headers: { "Content-Type": "application/json" },
326
+ body: JSON.stringify(params)
327
+ });
328
+ if (!response.ok) {
329
+ await throwAPIError(response);
330
+ }
331
+ const body = response.body;
332
+ if (!body) throw new LyrraVoiceError("No response body for SSE stream");
333
+ const reader = body.getReader();
334
+ const decoder = new TextDecoder();
335
+ let buffer = "";
336
+ try {
337
+ while (true) {
338
+ const { done, value } = await reader.read();
339
+ if (done) break;
340
+ buffer += decoder.decode(value, { stream: true });
341
+ const lines = buffer.split("\n");
342
+ buffer = lines.pop() ?? "";
343
+ for (const line of lines) {
344
+ const trimmed = line.trim();
345
+ if (!trimmed.startsWith("data:")) continue;
346
+ const json = trimmed.slice(5).trim();
347
+ if (!json) continue;
348
+ const event = JSON.parse(json);
349
+ if ("error" in event) throw new LyrraVoiceError(event.error);
350
+ if ("done" in event) return;
351
+ yield event;
352
+ }
353
+ }
354
+ } finally {
355
+ reader.releaseLock();
356
+ }
357
+ }
358
+ /** Health check. */
359
+ async health() {
360
+ return this._get("/health");
361
+ }
362
+ // ── Internal HTTP helpers (exposed for resource classes) ──────────
363
+ async _get(path, query) {
364
+ const url = this._url(path, query);
365
+ const res = await this._fetch(url, { method: "GET" });
366
+ return handleJSON(res);
367
+ }
368
+ async _getRaw(path, query) {
369
+ const url = this._url(path, query);
370
+ const res = await this._fetch(url, { method: "GET" });
371
+ if (!res.ok) await throwAPIError(res);
372
+ return new AudioResponse(res);
373
+ }
374
+ async _post(path, body, query) {
375
+ const url = this._url(path, query);
376
+ const res = await this._fetch(url, {
377
+ method: "POST",
378
+ headers: body !== void 0 ? { "Content-Type": "application/json" } : void 0,
379
+ body: body !== void 0 ? JSON.stringify(body) : void 0
380
+ });
381
+ return handleJSON(res);
382
+ }
383
+ async _postRaw(path, body) {
384
+ const res = await this._fetch(this._url(path), {
385
+ method: "POST",
386
+ headers: body !== void 0 ? { "Content-Type": "application/json" } : void 0,
387
+ body: body !== void 0 ? JSON.stringify(body) : void 0
388
+ });
389
+ if (!res.ok) await throwAPIError(res);
390
+ return new AudioResponse(res);
391
+ }
392
+ async _postForm(path, form) {
393
+ const res = await this._fetch(this._url(path), { method: "POST", body: form });
394
+ return handleJSON(res);
395
+ }
396
+ async _postFormRaw(path, form) {
397
+ const res = await this._fetch(this._url(path), { method: "POST", body: form });
398
+ if (!res.ok) await throwAPIError(res);
399
+ return new AudioResponse(res);
400
+ }
401
+ async _putForm(path, form) {
402
+ const res = await this._fetch(this._url(path), { method: "PUT", body: form });
403
+ return handleJSON(res);
404
+ }
405
+ async _delete(path) {
406
+ const res = await this._fetch(this._url(path), { method: "DELETE" });
407
+ return handleJSON(res);
408
+ }
409
+ // ── Private helpers ───────────────────────────────────────────────
410
+ _url(path, query) {
411
+ const url = new URL(path, this._baseUrl);
412
+ if (query) {
413
+ for (const [k, v] of Object.entries(query)) {
414
+ if (v !== void 0) url.searchParams.set(k, String(v));
415
+ }
416
+ }
417
+ return url.toString();
418
+ }
419
+ async _fetch(url, init = {}) {
420
+ const headers = new Headers(init.headers);
421
+ if (this._apiKey) {
422
+ headers.set("X-API-Key", this._apiKey);
423
+ } else if (this._token) {
424
+ headers.set("Authorization", `Bearer ${this._token}`);
425
+ }
426
+ return fetch(url, {
427
+ ...init,
428
+ headers,
429
+ signal: AbortSignal.timeout(this._timeout)
430
+ });
431
+ }
432
+ };
433
+ async function handleJSON(res) {
434
+ if (!res.ok) await throwAPIError(res);
435
+ return await res.json();
436
+ }
437
+ async function throwAPIError(res) {
438
+ let body;
439
+ try {
440
+ body = await res.json();
441
+ } catch {
442
+ body = await res.text().catch(() => null);
443
+ }
444
+ throw new APIError(res.status, body);
445
+ }
446
+ // Annotate the CommonJS export names for ESM import in node:
447
+ 0 && (module.exports = {
448
+ APIError,
449
+ AudioProcessing,
450
+ AudioResponse,
451
+ Jobs,
452
+ LyrraVoice,
453
+ LyrraVoiceError,
454
+ Mcp,
455
+ TimeoutError,
456
+ Voices
457
+ });
458
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/error.ts","../src/audio.ts","../src/resources/voices.ts","../src/resources/jobs.ts","../src/resources/audio-processing.ts","../src/resources/mcp.ts","../src/client.ts"],"sourcesContent":["export { LyrraVoice } from \"./client.js\";\nexport type { LyrraVoiceClient } from \"./client.js\";\nexport { AudioResponse } from \"./audio.js\";\nexport { LyrraVoiceError, APIError, TimeoutError } from \"./error.js\";\n\nexport { Voices } from \"./resources/voices.js\";\nexport { Jobs } from \"./resources/jobs.js\";\nexport { AudioProcessing } from \"./resources/audio-processing.js\";\nexport { Mcp } from \"./resources/mcp.js\";\n\nexport type {\n LyrraVoiceOptions,\n Language,\n AudioFormat,\n GenerateParams,\n AsyncJobResponse,\n StreamEvent,\n StreamDoneEvent,\n StreamErrorEvent,\n TTSJob,\n JobListResponse,\n JobListParams,\n PollOptions,\n Voice,\n VoiceCloneParams,\n VoiceCloneResponse,\n VoiceUploadResponse,\n VoiceRenameResponse,\n VoiceDeleteResponse,\n VoiceExtractEmbeddingResponse,\n VoicePresetsResponse,\n VoicePresetImportParams,\n VoicePresetImportResponse,\n AudioCleanParams,\n McpKey,\n McpKeyCreateResponse,\n McpInfo,\n HealthResponse,\n} from \"./types.js\";\n","export class LyrraVoiceError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"LyrraVoiceError\";\n }\n}\n\nexport class APIError extends LyrraVoiceError {\n readonly status: number;\n readonly detail: string | undefined;\n readonly body: unknown;\n\n constructor(status: number, body: unknown) {\n const detail = extractDetail(body);\n super(detail ?? `API error ${status}`);\n this.name = \"APIError\";\n this.status = status;\n this.detail = detail;\n this.body = body;\n }\n}\n\nfunction extractDetail(body: unknown): string | undefined {\n if (body && typeof body === \"object\" && \"detail\" in body) {\n const d = (body as Record<string, unknown>).detail;\n if (typeof d === \"string\") return d;\n if (typeof d === \"object\") return JSON.stringify(d);\n }\n return undefined;\n}\n\nexport class TimeoutError extends LyrraVoiceError {\n constructor(message = \"Request timed out\") {\n super(message);\n this.name = \"TimeoutError\";\n }\n}\n","import { writeFile } from \"node:fs/promises\";\n\n/**\n * Wraps a binary audio response with convenience methods.\n */\nexport class AudioResponse {\n private readonly _response: Response;\n private _buffer: ArrayBuffer | null = null;\n\n constructor(response: Response) {\n this._response = response;\n }\n\n /** The raw Response object. */\n get response(): Response {\n return this._response;\n }\n\n /** Content-Type header value. */\n get contentType(): string {\n return this._response.headers.get(\"content-type\") ?? \"audio/wav\";\n }\n\n /** Read the full response as an ArrayBuffer (cached). */\n async arrayBuffer(): Promise<ArrayBuffer> {\n if (!this._buffer) {\n this._buffer = await this._response.arrayBuffer();\n }\n return this._buffer;\n }\n\n /** Read the full response as a Node.js Buffer. */\n async buffer(): Promise<Buffer> {\n const ab = await this.arrayBuffer();\n return Buffer.from(ab);\n }\n\n /** Get a ReadableStream of the response body. Only works if buffer() hasn't been called yet. */\n stream(): ReadableStream<Uint8Array> | null {\n return this._response.body;\n }\n\n /** Save audio to a file. */\n async save(path: string): Promise<void> {\n const buf = await this.buffer();\n await writeFile(path, buf);\n }\n\n /** Return as a Blob (useful in non-Node environments). */\n async blob(): Promise<Blob> {\n const ab = await this.arrayBuffer();\n return new Blob([ab], { type: this.contentType });\n }\n}\n","import type { LyrraVoiceClient } from \"../client.js\";\nimport type {\n Voice,\n VoiceCloneParams,\n VoiceCloneResponse,\n VoiceDeleteResponse,\n VoiceExtractEmbeddingResponse,\n VoicePresetsResponse,\n VoicePresetImportParams,\n VoicePresetImportResponse,\n VoiceRenameResponse,\n VoiceUploadResponse,\n} from \"../types.js\";\nimport { AudioResponse } from \"../audio.js\";\n\nexport class Voices {\n constructor(private readonly _client: LyrraVoiceClient) {}\n\n /** List all voices accessible to the current user. */\n async list(): Promise<Voice[]> {\n return this._client._get<Voice[]>(\"/api/voices\");\n }\n\n /** List voices with preview URLs. */\n async listWithPreviews(language = \"fr\"): Promise<Voice[]> {\n return this._client._get<Voice[]>(\"/api/voices/with-previews\", { language });\n }\n\n /** Get a test/preview audio for a voice. */\n async test(voiceId: string, language = \"fr\"): Promise<AudioResponse> {\n return this._client._getRaw(`/api/voices/${encodeURIComponent(voiceId)}/test`, { language });\n }\n\n /** Clone a voice from an audio sample. */\n async clone(params: VoiceCloneParams): Promise<VoiceCloneResponse> {\n const form = new FormData();\n form.set(\"name\", params.name);\n const blob = toBlob(params.audio, params.filename ?? \"audio.wav\");\n form.set(\"audio\", blob);\n return this._client._postForm<VoiceCloneResponse>(\"/api/voices/clone\", form);\n }\n\n /** Upload a file (.wav, .mp3, .pth) to an existing voice. */\n async upload(\n voiceId: string,\n file: Blob | Buffer,\n filename = \"audio.wav\",\n ): Promise<VoiceUploadResponse> {\n const form = new FormData();\n const blob = file instanceof Blob ? file : new Blob([new Uint8Array(file)]);\n form.set(\"file\", blob, filename);\n return this._client._postForm<VoiceUploadResponse>(\n `/api/voices/${encodeURIComponent(voiceId)}`,\n form,\n );\n }\n\n /** Rename a voice. */\n async rename(voiceId: string, newName: string): Promise<VoiceRenameResponse> {\n const form = new FormData();\n form.set(\"new_name\", newName);\n return this._client._putForm<VoiceRenameResponse>(\n `/api/voices/${encodeURIComponent(voiceId)}/rename`,\n form,\n );\n }\n\n /** Delete a voice. */\n async delete(voiceId: string): Promise<VoiceDeleteResponse> {\n return this._client._delete<VoiceDeleteResponse>(\n `/api/voices/${encodeURIComponent(voiceId)}`,\n );\n }\n\n /** Extract .pth embedding from voice audio references. */\n async extractEmbedding(\n voiceId: string,\n force = false,\n ): Promise<VoiceExtractEmbeddingResponse> {\n return this._client._post<VoiceExtractEmbeddingResponse>(\n `/api/voices/${encodeURIComponent(voiceId)}/extract-embedding`,\n undefined,\n { force: force ? \"true\" : undefined },\n );\n }\n\n /** List built-in XTTS v2 speaker presets. */\n async presets(): Promise<VoicePresetsResponse> {\n return this._client._get<VoicePresetsResponse>(\"/api/voices/presets\");\n }\n\n /** Import a built-in preset as a user voice. */\n async importPreset(params: VoicePresetImportParams): Promise<VoicePresetImportResponse> {\n const form = new FormData();\n form.set(\"speaker_name\", params.speaker_name);\n if (params.voice_id) form.set(\"voice_id\", params.voice_id);\n return this._client._postForm<VoicePresetImportResponse>(\"/api/voices/presets/import\", form);\n }\n}\n\nfunction toBlob(input: Blob | Buffer | ReadableStream, filename: string): Blob {\n if (input instanceof Blob) return input;\n if (Buffer.isBuffer(input)) return new Blob([new Uint8Array(input)], { type: mimeFromName(filename) });\n // ReadableStream — wrap in a Blob (requires Node 20+)\n return input as unknown as Blob;\n}\n\nfunction mimeFromName(name: string): string {\n if (name.endsWith(\".mp3\")) return \"audio/mpeg\";\n return \"audio/wav\";\n}\n","import type { LyrraVoiceClient } from \"../client.js\";\nimport type { TTSJob, JobListResponse, JobListParams, PollOptions } from \"../types.js\";\nimport { AudioResponse } from \"../audio.js\";\nimport { TimeoutError } from \"../error.js\";\n\nexport class Jobs {\n constructor(private readonly _client: LyrraVoiceClient) {}\n\n /** List TTS jobs. */\n async list(params?: JobListParams): Promise<JobListResponse> {\n return this._client._get<JobListResponse>(\"/api/tts/jobs\", params as Record<string, string>);\n }\n\n /** Get a single job by ID. */\n async get(jobId: string): Promise<TTSJob> {\n return this._client._get<TTSJob>(`/api/tts/jobs/${encodeURIComponent(jobId)}`);\n }\n\n /**\n * Poll a job until it reaches a terminal state (completed / failed).\n * Returns the final job object.\n */\n async poll(jobId: string, options?: PollOptions): Promise<TTSJob> {\n const interval = options?.interval ?? 1000;\n const timeout = options?.timeout ?? 300_000;\n const deadline = Date.now() + timeout;\n\n while (Date.now() < deadline) {\n const job = await this.get(jobId);\n if (job.status === \"completed\" || job.status === \"failed\") {\n return job;\n }\n await sleep(interval);\n }\n\n throw new TimeoutError(`Job ${jobId} did not complete within ${timeout}ms`);\n }\n\n /** Download the audio output of a completed job. */\n async download(fileId: string): Promise<AudioResponse> {\n return this._client._getRaw(`/api/tts/download/${encodeURIComponent(fileId)}`);\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","import type { LyrraVoiceClient } from \"../client.js\";\nimport type { AudioCleanParams } from \"../types.js\";\nimport { AudioResponse } from \"../audio.js\";\n\nexport class AudioProcessing {\n constructor(private readonly _client: LyrraVoiceClient) {}\n\n /**\n * Clean an audio file (noise reduction + normalization).\n * Returns the cleaned audio as an AudioResponse.\n */\n async clean(params: AudioCleanParams): Promise<AudioResponse> {\n const form = new FormData();\n const blob =\n params.file instanceof Blob\n ? params.file\n : Buffer.isBuffer(params.file)\n ? new Blob([new Uint8Array(params.file)])\n : (params.file as unknown as Blob);\n form.set(\"file\", blob, params.filename ?? \"audio.wav\");\n return this._client._postFormRaw(\"/api/audio/clean\", form);\n }\n}\n","import type { LyrraVoiceClient } from \"../client.js\";\nimport type { McpKey, McpKeyCreateResponse, McpInfo } from \"../types.js\";\n\nexport class Mcp {\n constructor(private readonly _client: LyrraVoiceClient) {}\n\n /** List all MCP API keys for the current user. */\n async listKeys(): Promise<McpKey[]> {\n return this._client._get<McpKey[]>(\"/api/mcp/keys\");\n }\n\n /**\n * Create a new MCP API key.\n * The full key is returned only once — store it securely.\n */\n async createKey(name: string): Promise<McpKeyCreateResponse> {\n const form = new FormData();\n form.set(\"name\", name);\n return this._client._postForm<McpKeyCreateResponse>(\"/api/mcp/keys\", form);\n }\n\n /** Revoke an MCP API key. */\n async revokeKey(keyId: string): Promise<{ success: boolean }> {\n return this._client._delete<{ success: boolean }>(\n `/api/mcp/keys/${encodeURIComponent(keyId)}`,\n );\n }\n\n /** Get MCP server info (endpoint, version, transport). */\n async info(): Promise<McpInfo> {\n return this._client._get<McpInfo>(\"/api/mcp/info\");\n }\n}\n","import type {\n LyrraVoiceOptions,\n GenerateParams,\n AsyncJobResponse,\n StreamEvent,\n StreamDoneEvent,\n StreamErrorEvent,\n HealthResponse,\n} from \"./types.js\";\nimport { APIError, LyrraVoiceError } from \"./error.js\";\nimport { AudioResponse } from \"./audio.js\";\nimport { Voices } from \"./resources/voices.js\";\nimport { Jobs } from \"./resources/jobs.js\";\nimport { AudioProcessing } from \"./resources/audio-processing.js\";\nimport { Mcp } from \"./resources/mcp.js\";\n\n/** Internal interface — exposed for resource classes via `LyrraVoiceClient`. */\nexport interface LyrraVoiceClient {\n _get<T>(path: string, query?: Record<string, string | number | boolean | undefined>): Promise<T>;\n _getRaw(path: string, query?: Record<string, string | number | boolean | undefined>): Promise<AudioResponse>;\n _post<T>(path: string, body?: unknown, query?: Record<string, string | undefined>): Promise<T>;\n _postRaw(path: string, body?: unknown): Promise<AudioResponse>;\n _postForm<T>(path: string, form: FormData): Promise<T>;\n _postFormRaw(path: string, form: FormData): Promise<AudioResponse>;\n _putForm<T>(path: string, form: FormData): Promise<T>;\n _delete<T>(path: string): Promise<T>;\n}\n\nexport class LyrraVoice implements LyrraVoiceClient {\n private readonly _baseUrl: string;\n private readonly _apiKey: string | undefined;\n private readonly _token: string | undefined;\n private readonly _timeout: number;\n\n /** Voice management (list, clone, rename, delete, presets...) */\n readonly voices: Voices;\n /** Async TTS job management (list, get, poll, download) */\n readonly jobs: Jobs;\n /** Audio processing (clean/denoise) */\n readonly audio: AudioProcessing;\n /** MCP API key management */\n readonly mcp: Mcp;\n\n constructor(options: LyrraVoiceOptions = {}) {\n this._baseUrl = (options.baseUrl ?? \"http://localhost:5050\").replace(/\\/+$/, \"\");\n this._apiKey = options.apiKey;\n this._token = options.token;\n this._timeout = options.timeout ?? 30_000;\n\n this.voices = new Voices(this);\n this.jobs = new Jobs(this);\n this.audio = new AudioProcessing(this);\n this.mcp = new Mcp(this);\n }\n\n // ── Public TTS methods ────────────────────────────────────────────\n\n /** Synchronous TTS — returns audio. */\n async generate(params: GenerateParams): Promise<AudioResponse> {\n return this._postRaw(\"/api/tts\", params);\n }\n\n /** Async TTS — submits a Celery job, returns job_id for polling. */\n async generateAsync(params: GenerateParams): Promise<AsyncJobResponse> {\n return this._post<AsyncJobResponse>(\"/api/tts/async\", params);\n }\n\n /**\n * SSE streaming TTS — yields audio chunks as they are generated.\n *\n * ```ts\n * for await (const event of client.stream({ text: \"Hello\" })) {\n * // event.audio is base64-encoded WAV chunk\n * }\n * ```\n */\n async *stream(\n params: GenerateParams,\n ): AsyncGenerator<StreamEvent, void, undefined> {\n const response = await this._fetch(\"/api/tts/stream\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(params),\n });\n\n if (!response.ok) {\n await throwAPIError(response);\n }\n\n const body = response.body;\n if (!body) throw new LyrraVoiceError(\"No response body for SSE stream\");\n\n const reader = body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() ?? \"\";\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed.startsWith(\"data:\")) continue;\n const json = trimmed.slice(5).trim();\n if (!json) continue;\n\n const event = JSON.parse(json) as StreamEvent | StreamDoneEvent | StreamErrorEvent;\n if (\"error\" in event) throw new LyrraVoiceError(event.error);\n if (\"done\" in event) return;\n yield event;\n }\n }\n } finally {\n reader.releaseLock();\n }\n }\n\n /** Health check. */\n async health(): Promise<HealthResponse> {\n return this._get<HealthResponse>(\"/health\");\n }\n\n // ── Internal HTTP helpers (exposed for resource classes) ──────────\n\n async _get<T>(\n path: string,\n query?: Record<string, string | number | boolean | undefined>,\n ): Promise<T> {\n const url = this._url(path, query);\n const res = await this._fetch(url, { method: \"GET\" });\n return handleJSON<T>(res);\n }\n\n async _getRaw(\n path: string,\n query?: Record<string, string | number | boolean | undefined>,\n ): Promise<AudioResponse> {\n const url = this._url(path, query);\n const res = await this._fetch(url, { method: \"GET\" });\n if (!res.ok) await throwAPIError(res);\n return new AudioResponse(res);\n }\n\n async _post<T>(\n path: string,\n body?: unknown,\n query?: Record<string, string | undefined>,\n ): Promise<T> {\n const url = this._url(path, query);\n const res = await this._fetch(url, {\n method: \"POST\",\n headers: body !== undefined ? { \"Content-Type\": \"application/json\" } : undefined,\n body: body !== undefined ? JSON.stringify(body) : undefined,\n });\n return handleJSON<T>(res);\n }\n\n async _postRaw(path: string, body?: unknown): Promise<AudioResponse> {\n const res = await this._fetch(this._url(path), {\n method: \"POST\",\n headers: body !== undefined ? { \"Content-Type\": \"application/json\" } : undefined,\n body: body !== undefined ? JSON.stringify(body) : undefined,\n });\n if (!res.ok) await throwAPIError(res);\n return new AudioResponse(res);\n }\n\n async _postForm<T>(path: string, form: FormData): Promise<T> {\n const res = await this._fetch(this._url(path), { method: \"POST\", body: form });\n return handleJSON<T>(res);\n }\n\n async _postFormRaw(path: string, form: FormData): Promise<AudioResponse> {\n const res = await this._fetch(this._url(path), { method: \"POST\", body: form });\n if (!res.ok) await throwAPIError(res);\n return new AudioResponse(res);\n }\n\n async _putForm<T>(path: string, form: FormData): Promise<T> {\n const res = await this._fetch(this._url(path), { method: \"PUT\", body: form });\n return handleJSON<T>(res);\n }\n\n async _delete<T>(path: string): Promise<T> {\n const res = await this._fetch(this._url(path), { method: \"DELETE\" });\n return handleJSON<T>(res);\n }\n\n // ── Private helpers ───────────────────────────────────────────────\n\n private _url(\n path: string,\n query?: Record<string, string | number | boolean | undefined>,\n ): string {\n const url = new URL(path, this._baseUrl);\n if (query) {\n for (const [k, v] of Object.entries(query)) {\n if (v !== undefined) url.searchParams.set(k, String(v));\n }\n }\n return url.toString();\n }\n\n private async _fetch(url: string, init: RequestInit = {}): Promise<Response> {\n const headers = new Headers(init.headers);\n\n if (this._apiKey) {\n headers.set(\"X-API-Key\", this._apiKey);\n } else if (this._token) {\n headers.set(\"Authorization\", `Bearer ${this._token}`);\n }\n\n return fetch(url, {\n ...init,\n headers,\n signal: AbortSignal.timeout(this._timeout),\n });\n }\n}\n\n// ── Utilities ─────────────────────────────────────────────────────────\n\nasync function handleJSON<T>(res: Response): Promise<T> {\n if (!res.ok) await throwAPIError(res);\n return (await res.json()) as T;\n}\n\nasync function throwAPIError(res: Response): Promise<never> {\n let body: unknown;\n try {\n body = await res.json();\n } catch {\n body = await res.text().catch(() => null);\n }\n throw new APIError(res.status, body);\n}\n"],"mappings":"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","names":[]}