@leiboss/dsh-tts 0.0.0-stage → 0.3.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/CHANGELOG.md +111 -0
- package/LICENSE +21 -0
- package/README.md +374 -2
- package/cordis.patch.yml +4 -0
- package/dist/audio/mp3-duration.d.ts +23 -0
- package/dist/audio/mp3-duration.d.ts.map +1 -0
- package/dist/audio/mp3-duration.js +140 -0
- package/dist/audio/mp3-duration.js.map +1 -0
- package/dist/audio-route.d.ts +122 -0
- package/dist/audio-route.d.ts.map +1 -0
- package/dist/audio-route.js +408 -0
- package/dist/audio-route.js.map +1 -0
- package/dist/cache.d.ts +50 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +105 -0
- package/dist/cache.js.map +1 -0
- package/dist/chunked-tts.d.ts +34 -0
- package/dist/chunked-tts.d.ts.map +1 -0
- package/dist/chunked-tts.js +121 -0
- package/dist/chunked-tts.js.map +1 -0
- package/dist/chunker.d.ts +19 -0
- package/dist/chunker.d.ts.map +1 -0
- package/dist/chunker.js +98 -0
- package/dist/chunker.js.map +1 -0
- package/dist/client.js +90 -0
- package/dist/concurrency.d.ts +33 -0
- package/dist/concurrency.d.ts.map +1 -0
- package/dist/concurrency.js +95 -0
- package/dist/concurrency.js.map +1 -0
- package/dist/config.d.ts +174 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +186 -0
- package/dist/config.js.map +1 -0
- package/dist/edge-tts.d.ts +74 -0
- package/dist/edge-tts.d.ts.map +1 -0
- package/dist/edge-tts.js +161 -0
- package/dist/edge-tts.js.map +1 -0
- package/dist/engine-health.d.ts +108 -0
- package/dist/engine-health.d.ts.map +1 -0
- package/dist/engine-health.js +170 -0
- package/dist/engine-health.js.map +1 -0
- package/dist/engines/edge-js.d.ts +51 -0
- package/dist/engines/edge-js.d.ts.map +1 -0
- package/dist/engines/edge-js.js +98 -0
- package/dist/engines/edge-js.js.map +1 -0
- package/dist/errors.d.ts +66 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +110 -0
- package/dist/errors.js.map +1 -0
- package/dist/fallback.d.ts +69 -0
- package/dist/fallback.d.ts.map +1 -0
- package/dist/fallback.js +89 -0
- package/dist/fallback.js.map +1 -0
- package/dist/index.d.ts +72 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +723 -0
- package/dist/index.js.map +1 -0
- package/dist/probe.d.ts +42 -0
- package/dist/probe.d.ts.map +1 -0
- package/dist/probe.js +63 -0
- package/dist/probe.js.map +1 -0
- package/dist/provider.d.ts +33 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +7 -0
- package/dist/provider.js.map +1 -0
- package/dist/register-service.d.ts +25 -0
- package/dist/register-service.d.ts.map +1 -0
- package/dist/register-service.js +33 -0
- package/dist/register-service.js.map +1 -0
- package/dist/sapi-tts.d.ts +29 -0
- package/dist/sapi-tts.d.ts.map +1 -0
- package/dist/sapi-tts.js +131 -0
- package/dist/sapi-tts.js.map +1 -0
- package/dist/self-check.d.ts +32 -0
- package/dist/self-check.d.ts.map +1 -0
- package/dist/self-check.js +75 -0
- package/dist/self-check.js.map +1 -0
- package/dist/service.d.ts +119 -0
- package/dist/service.d.ts.map +1 -0
- package/dist/service.js +404 -0
- package/dist/service.js.map +1 -0
- package/dist/voices.d.ts +21 -0
- package/dist/voices.d.ts.map +1 -0
- package/dist/voices.js +74 -0
- package/dist/voices.js.map +1 -0
- package/icon.svg +6 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +93 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { homedir } from 'node:os';
|
|
2
|
+
import { join, resolve } from 'node:path';
|
|
3
|
+
import z from '@deepseek-ai/schemastery';
|
|
4
|
+
import { DEFAULT_ENGINE_HEALTH, } from "./engine-health.js";
|
|
5
|
+
import { ErrorCode, TtsError } from "./errors.js";
|
|
6
|
+
/** Voice Edge-TTS uses when neither config nor the call specifies one. */
|
|
7
|
+
export const DEFAULT_ENGINE = 'auto';
|
|
8
|
+
export const DEFAULT_VOICE = 'zh-CN-XiaoxiaoNeural';
|
|
9
|
+
/** Rate Edge-TTS uses when the call omits one. */
|
|
10
|
+
export const DEFAULT_RATE = '+0%';
|
|
11
|
+
/** Per-synthesis subprocess deadline. */
|
|
12
|
+
export const DEFAULT_TIMEOUT_MS = 30_000;
|
|
13
|
+
/** CLI name resolved through PATH unless overridden. */
|
|
14
|
+
export const DEFAULT_EDGE_TTS_BINARY = 'edge-tts';
|
|
15
|
+
/** Subdirectory of `DSH_HOME` that owns every generated audio file. */
|
|
16
|
+
export const OUTPUT_SUBDIR = 'tts-output';
|
|
17
|
+
/** Target characters per chunk, and the one-shot threshold. */
|
|
18
|
+
export const DEFAULT_CHUNK_SIZE = 1_200;
|
|
19
|
+
/** Long texts are chunked by default. */
|
|
20
|
+
export const DEFAULT_CHUNKING_ENABLED = true;
|
|
21
|
+
/** Engines the fallback chain understands, in default priority order. */
|
|
22
|
+
export const DEFAULT_FALLBACK_ORDER = [
|
|
23
|
+
'edge-tts',
|
|
24
|
+
'sapi',
|
|
25
|
+
'speechSynthesis',
|
|
26
|
+
];
|
|
27
|
+
/** Fallback is enabled unless configured otherwise. */
|
|
28
|
+
export const DEFAULT_FALLBACK_ENABLED = true;
|
|
29
|
+
/** Syntheses allowed to run at the same time. */
|
|
30
|
+
export const DEFAULT_MAX_CONCURRENT = 2;
|
|
31
|
+
/** Callers allowed to wait for a synthesis slot. */
|
|
32
|
+
export const DEFAULT_QUEUE_SIZE = 4;
|
|
33
|
+
/** How long a caller may wait for a slot before TTS_BUSY. */
|
|
34
|
+
export const DEFAULT_QUEUE_TIMEOUT_MS = 10_000;
|
|
35
|
+
/** Upper bound for one complete `tts_generate` call, across all chunks. */
|
|
36
|
+
export const DEFAULT_TOTAL_TIMEOUT_MS = 180_000;
|
|
37
|
+
/** Extra Host names accepted by the audio route (reverse proxies). */
|
|
38
|
+
export const DEFAULT_TRUSTED_HOSTS = [];
|
|
39
|
+
/** Narrow an arbitrary config string to a known engine. */
|
|
40
|
+
export function isTtsEngine(value) {
|
|
41
|
+
return DEFAULT_FALLBACK_ORDER.includes(value);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Config schema exposed to the DSH configuration layer. Defaults live here (not
|
|
45
|
+
* in call sites) so the settings surface and runtime behaviour agree.
|
|
46
|
+
*/
|
|
47
|
+
export const Config = z.object({
|
|
48
|
+
outputDir: z.string(),
|
|
49
|
+
defaultVoice: z.string().default(DEFAULT_VOICE),
|
|
50
|
+
timeoutMs: z.number().default(DEFAULT_TIMEOUT_MS),
|
|
51
|
+
edgeTtsBinary: z.string().default(DEFAULT_EDGE_TTS_BINARY),
|
|
52
|
+
chunkSize: z.number().default(DEFAULT_CHUNK_SIZE),
|
|
53
|
+
chunkingEnabled: z.boolean().default(DEFAULT_CHUNKING_ENABLED),
|
|
54
|
+
fallbackEnabled: z.boolean().default(DEFAULT_FALLBACK_ENABLED),
|
|
55
|
+
fallbackOrder: z.array(z.string()).default([...DEFAULT_FALLBACK_ORDER]),
|
|
56
|
+
maxConcurrent: z.number().default(DEFAULT_MAX_CONCURRENT),
|
|
57
|
+
queueSize: z.number().default(DEFAULT_QUEUE_SIZE),
|
|
58
|
+
queueTimeoutMs: z.number().default(DEFAULT_QUEUE_TIMEOUT_MS),
|
|
59
|
+
totalTimeoutMs: z.number().default(DEFAULT_TOTAL_TIMEOUT_MS),
|
|
60
|
+
trustedHosts: z.array(z.string()).default([...DEFAULT_TRUSTED_HOSTS]),
|
|
61
|
+
proxy: z.string(),
|
|
62
|
+
engine: z
|
|
63
|
+
.union([z.const('auto'), z.const('edge-js'), z.const('edge-py'), z.const('sapi')])
|
|
64
|
+
.default(DEFAULT_ENGINE),
|
|
65
|
+
engineHealth: z
|
|
66
|
+
.object({
|
|
67
|
+
enabled: z.boolean().default(DEFAULT_ENGINE_HEALTH.enabled),
|
|
68
|
+
failureThreshold: z
|
|
69
|
+
.number()
|
|
70
|
+
.default(DEFAULT_ENGINE_HEALTH.failureThreshold),
|
|
71
|
+
driftCooldownMs: z.number().default(DEFAULT_ENGINE_HEALTH.driftCooldownMs),
|
|
72
|
+
transientCooldownMs: z
|
|
73
|
+
.number()
|
|
74
|
+
.default(DEFAULT_ENGINE_HEALTH.transientCooldownMs),
|
|
75
|
+
maxCooldownMs: z.number().default(DEFAULT_ENGINE_HEALTH.maxCooldownMs),
|
|
76
|
+
})
|
|
77
|
+
.default({ ...DEFAULT_ENGINE_HEALTH }),
|
|
78
|
+
});
|
|
79
|
+
function nonEmpty(value) {
|
|
80
|
+
const trimmed = value?.trim();
|
|
81
|
+
return trimmed ? trimmed : undefined;
|
|
82
|
+
}
|
|
83
|
+
function resolveEngineHealth(raw) {
|
|
84
|
+
return {
|
|
85
|
+
enabled: raw?.enabled ?? DEFAULT_ENGINE_HEALTH.enabled,
|
|
86
|
+
failureThreshold: raw?.failureThreshold ?? DEFAULT_ENGINE_HEALTH.failureThreshold,
|
|
87
|
+
driftCooldownMs: raw?.driftCooldownMs ?? DEFAULT_ENGINE_HEALTH.driftCooldownMs,
|
|
88
|
+
transientCooldownMs: raw?.transientCooldownMs ?? DEFAULT_ENGINE_HEALTH.transientCooldownMs,
|
|
89
|
+
maxCooldownMs: raw?.maxCooldownMs ?? DEFAULT_ENGINE_HEALTH.maxCooldownMs,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Resolve config defaults without touching the filesystem. A missing
|
|
94
|
+
* `DSH_HOME` is not fatal here: explicit `outputDir` still works, and the
|
|
95
|
+
* self-check needs to report the missing value instead of failing to load.
|
|
96
|
+
*/
|
|
97
|
+
export function resolveConfig(raw, env = process.env) {
|
|
98
|
+
const dshHome = nonEmpty(env.DSH_HOME) ?? '';
|
|
99
|
+
const configuredDir = nonEmpty(raw?.outputDir);
|
|
100
|
+
const outputDir = configuredDir
|
|
101
|
+
? resolve(configuredDir)
|
|
102
|
+
: dshHome
|
|
103
|
+
? resolve(dshHome, OUTPUT_SUBDIR)
|
|
104
|
+
: join(homedir(), '.dsh', OUTPUT_SUBDIR);
|
|
105
|
+
return {
|
|
106
|
+
dshHome,
|
|
107
|
+
outputDir,
|
|
108
|
+
defaultVoice: nonEmpty(raw?.defaultVoice) ?? DEFAULT_VOICE,
|
|
109
|
+
timeoutMs: raw?.timeoutMs ?? DEFAULT_TIMEOUT_MS,
|
|
110
|
+
edgeTtsBinary: nonEmpty(raw?.edgeTtsBinary) ?? DEFAULT_EDGE_TTS_BINARY,
|
|
111
|
+
chunkSize: raw?.chunkSize ?? DEFAULT_CHUNK_SIZE,
|
|
112
|
+
chunkingEnabled: raw?.chunkingEnabled ?? DEFAULT_CHUNKING_ENABLED,
|
|
113
|
+
fallbackEnabled: raw?.fallbackEnabled ?? DEFAULT_FALLBACK_ENABLED,
|
|
114
|
+
fallbackOrder: raw?.fallbackOrder === undefined
|
|
115
|
+
? [...DEFAULT_FALLBACK_ORDER]
|
|
116
|
+
: raw.fallbackOrder.filter(isTtsEngine),
|
|
117
|
+
maxConcurrent: raw?.maxConcurrent ?? DEFAULT_MAX_CONCURRENT,
|
|
118
|
+
queueSize: raw?.queueSize ?? DEFAULT_QUEUE_SIZE,
|
|
119
|
+
queueTimeoutMs: raw?.queueTimeoutMs ?? DEFAULT_QUEUE_TIMEOUT_MS,
|
|
120
|
+
totalTimeoutMs: raw?.totalTimeoutMs ?? DEFAULT_TOTAL_TIMEOUT_MS,
|
|
121
|
+
trustedHosts: (raw?.trustedHosts ?? [...DEFAULT_TRUSTED_HOSTS])
|
|
122
|
+
.map((host) => host.trim().toLowerCase())
|
|
123
|
+
.filter((host) => host.length > 0),
|
|
124
|
+
proxy: nonEmpty(raw?.proxy),
|
|
125
|
+
engine: raw?.engine ?? DEFAULT_ENGINE,
|
|
126
|
+
engineHealth: resolveEngineHealth(raw?.engineHealth),
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Validate the settings a synthesis actually needs. Kept separate from
|
|
131
|
+
* {@link resolveConfig} so loading the plugin never fails at mount time.
|
|
132
|
+
*/
|
|
133
|
+
export function requireSynthesisConfig(config) {
|
|
134
|
+
if (!config.outputDir) {
|
|
135
|
+
throw new TtsError(ErrorCode.DshHomeNotSet, 'No output directory is available: DSH_HOME is not set and `outputDir` is not configured.', 'Set DSH_HOME in the DSH runtime, or set the plugin config field `outputDir`.');
|
|
136
|
+
}
|
|
137
|
+
if (!Number.isFinite(config.timeoutMs) || config.timeoutMs <= 0) {
|
|
138
|
+
throw new TtsError(ErrorCode.InvalidConfig, `Invalid config: timeoutMs must be a positive number, got ${JSON.stringify(config.timeoutMs)}.`, 'Set `timeoutMs` to a positive millisecond value (default 30000).');
|
|
139
|
+
}
|
|
140
|
+
if (config.chunkingEnabled &&
|
|
141
|
+
(!Number.isInteger(config.chunkSize) || config.chunkSize <= 0)) {
|
|
142
|
+
throw new TtsError(ErrorCode.InvalidConfig, `Invalid config: chunkSize must be a positive integer, got ${JSON.stringify(config.chunkSize)}.`, 'Set `chunkSize` to a positive character count (default 1200), or disable chunking.');
|
|
143
|
+
}
|
|
144
|
+
if (!Number.isInteger(config.maxConcurrent) ||
|
|
145
|
+
config.maxConcurrent < 1) {
|
|
146
|
+
throw new TtsError(ErrorCode.InvalidConfig, `Invalid config: maxConcurrent must be a positive integer, got ${JSON.stringify(config.maxConcurrent)}.`, 'Set `maxConcurrent` to at least 1 (default 2).');
|
|
147
|
+
}
|
|
148
|
+
if (!Number.isInteger(config.queueSize) || config.queueSize < 0) {
|
|
149
|
+
throw new TtsError(ErrorCode.InvalidConfig, `Invalid config: queueSize must be a non-negative integer, got ${JSON.stringify(config.queueSize)}.`, 'Set `queueSize` to 0 or more (default 4).');
|
|
150
|
+
}
|
|
151
|
+
if (!Number.isFinite(config.queueTimeoutMs) || config.queueTimeoutMs <= 0) {
|
|
152
|
+
throw new TtsError(ErrorCode.InvalidConfig, `Invalid config: queueTimeoutMs must be a positive number, got ${JSON.stringify(config.queueTimeoutMs)}.`, 'Set `queueTimeoutMs` to a positive millisecond value (default 10000).');
|
|
153
|
+
}
|
|
154
|
+
if (!Number.isFinite(config.totalTimeoutMs) || config.totalTimeoutMs <= 0) {
|
|
155
|
+
throw new TtsError(ErrorCode.InvalidConfig, `Invalid config: totalTimeoutMs must be a positive number, got ${JSON.stringify(config.totalTimeoutMs)}.`, 'Set `totalTimeoutMs` to a positive millisecond value (default 180000).');
|
|
156
|
+
}
|
|
157
|
+
const health = config.engineHealth;
|
|
158
|
+
if (!Number.isInteger(health.failureThreshold) || health.failureThreshold < 1) {
|
|
159
|
+
throw new TtsError(ErrorCode.InvalidConfig, `Invalid config: engineHealth.failureThreshold must be a positive integer, got ${JSON.stringify(health.failureThreshold)}.`, 'Set `engineHealth.failureThreshold` to at least 1 (default 3).');
|
|
160
|
+
}
|
|
161
|
+
for (const [field, value] of [
|
|
162
|
+
['driftCooldownMs', health.driftCooldownMs],
|
|
163
|
+
['transientCooldownMs', health.transientCooldownMs],
|
|
164
|
+
['maxCooldownMs', health.maxCooldownMs],
|
|
165
|
+
]) {
|
|
166
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
167
|
+
throw new TtsError(ErrorCode.InvalidConfig, `Invalid config: engineHealth.${field} must be a positive number, got ${JSON.stringify(value)}.`, `Set \`engineHealth.${field}\` to a positive millisecond value.`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return {
|
|
171
|
+
outputDir: config.outputDir,
|
|
172
|
+
timeoutMs: config.timeoutMs,
|
|
173
|
+
chunkSize: config.chunkSize,
|
|
174
|
+
chunkingEnabled: config.chunkingEnabled,
|
|
175
|
+
fallbackEnabled: config.fallbackEnabled,
|
|
176
|
+
fallbackOrder: config.fallbackOrder,
|
|
177
|
+
maxConcurrent: config.maxConcurrent,
|
|
178
|
+
queueSize: config.queueSize,
|
|
179
|
+
queueTimeoutMs: config.queueTimeoutMs,
|
|
180
|
+
totalTimeoutMs: config.totalTimeoutMs,
|
|
181
|
+
trustedHosts: config.trustedHosts,
|
|
182
|
+
proxy: config.proxy,
|
|
183
|
+
engineHealth: health,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/** Minimal child-process surface used by the runner, kept injectable for tests. */
|
|
2
|
+
export interface ChildStream {
|
|
3
|
+
on(event: 'data', listener: (chunk: Buffer | string) => void): unknown;
|
|
4
|
+
}
|
|
5
|
+
/** Structural view of the parts of `ChildProcess` this module relies on. */
|
|
6
|
+
export interface ChildProcessLike {
|
|
7
|
+
stdout: ChildStream | null;
|
|
8
|
+
stderr: ChildStream | null;
|
|
9
|
+
on(event: 'error', listener: (error: Error) => void): unknown;
|
|
10
|
+
on(event: 'close', listener: (code: number | null, signal: NodeJS.Signals | null) => void): unknown;
|
|
11
|
+
kill(signal?: NodeJS.Signals | number): boolean;
|
|
12
|
+
}
|
|
13
|
+
/** Options passed to the injectable spawn implementation. */
|
|
14
|
+
export interface SpawnOptions {
|
|
15
|
+
stdio: ['ignore', 'pipe', 'pipe'];
|
|
16
|
+
windowsHide: boolean;
|
|
17
|
+
env: NodeJS.ProcessEnv;
|
|
18
|
+
}
|
|
19
|
+
/** Injection point for the real `child_process.spawn` (or a test double). */
|
|
20
|
+
export type SpawnFunction = (command: string, args: readonly string[], options: SpawnOptions) => ChildProcessLike;
|
|
21
|
+
/** Captured outcome of one child process. A non-zero exit is not an error here. */
|
|
22
|
+
export interface ProcessResult {
|
|
23
|
+
code: number | null;
|
|
24
|
+
signal: NodeJS.Signals | null;
|
|
25
|
+
stdout: string;
|
|
26
|
+
stderr: string;
|
|
27
|
+
}
|
|
28
|
+
/** Controls shared by every child-process call. */
|
|
29
|
+
export interface RunProcessOptions {
|
|
30
|
+
timeoutMs: number;
|
|
31
|
+
signal?: AbortSignal;
|
|
32
|
+
spawnImpl?: SpawnFunction;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Run one child process, consuming stdout and stderr concurrently so neither
|
|
36
|
+
* pipe can deadlock. Resolves for any completed exit status; throws structured
|
|
37
|
+
* errors for spawn denial, cancellation, and timeout (killing the child).
|
|
38
|
+
*/
|
|
39
|
+
export declare function runProcess(command: string, args: readonly string[], options: RunProcessOptions): Promise<ProcessResult>;
|
|
40
|
+
/** Settings for one synthesis call. */
|
|
41
|
+
export interface SynthesizeOptions {
|
|
42
|
+
voice: string;
|
|
43
|
+
rate: string;
|
|
44
|
+
/** Pitch adjustment such as `+10Hz`; omitted for Edge-TTS's own default. */
|
|
45
|
+
pitch?: string;
|
|
46
|
+
/** Volume adjustment such as `-10%`; omitted for Edge-TTS's own default. */
|
|
47
|
+
volume?: string;
|
|
48
|
+
/**
|
|
49
|
+
* HTTP proxy for the WSS connection, forwarded as `--proxy`. Edge-TTS does
|
|
50
|
+
* not honour `HTTPS_PROXY`, so this is the only way to route it.
|
|
51
|
+
*/
|
|
52
|
+
proxy?: string;
|
|
53
|
+
binary: string;
|
|
54
|
+
outputDir: string;
|
|
55
|
+
/** Stable cache key; also names the output file. */
|
|
56
|
+
hash: string;
|
|
57
|
+
timeoutMs: number;
|
|
58
|
+
signal?: AbortSignal;
|
|
59
|
+
spawnImpl?: SpawnFunction;
|
|
60
|
+
/** Injectable clock for deterministic tests. */
|
|
61
|
+
now?: Date;
|
|
62
|
+
}
|
|
63
|
+
/** Canonical audio description returned to the tool layer. */
|
|
64
|
+
export interface SynthesizedAudio {
|
|
65
|
+
filePath: string;
|
|
66
|
+
format: string;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Invoke the Edge-TTS CLI for `text` and return the finished file. Writes to a
|
|
70
|
+
* `.partial` sibling and renames on success, so a crashed or concurrent run
|
|
71
|
+
* never leaves a file that the cache would treat as complete.
|
|
72
|
+
*/
|
|
73
|
+
export declare function synthesize(text: string, options: SynthesizeOptions): Promise<SynthesizedAudio>;
|
|
74
|
+
//# sourceMappingURL=edge-tts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edge-tts.d.ts","sourceRoot":"","sources":["../src/edge-tts.ts"],"names":[],"mappings":"AAiBA,mFAAmF;AACnF,MAAM,WAAW,WAAW;IAC1B,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,KAAK,IAAI,GAAG,OAAO,CAAA;CACvE;AAED,4EAA4E;AAC5E,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,WAAW,GAAG,IAAI,CAAA;IAC1B,MAAM,EAAE,WAAW,GAAG,IAAI,CAAA;IAC1B,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,GAAG,OAAO,CAAA;IAC7D,EAAE,CACA,KAAK,EAAE,OAAO,EACd,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,KAAK,IAAI,GACrE,OAAO,CAAA;IACV,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,OAAO,GAAG,MAAM,GAAG,OAAO,CAAA;CAChD;AAED,6DAA6D;AAC7D,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IACjC,WAAW,EAAE,OAAO,CAAA;IACpB,GAAG,EAAE,MAAM,CAAC,UAAU,CAAA;CACvB;AAED,6EAA6E;AAC7E,MAAM,MAAM,aAAa,GAAG,CAC1B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,OAAO,EAAE,YAAY,KAClB,gBAAgB,CAAA;AAKrB,mFAAmF;AACnF,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,CAAA;IAC7B,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;CACf;AAED,mDAAmD;AACnD,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,SAAS,CAAC,EAAE,aAAa,CAAA;CAC1B;AAOD;;;;GAIG;AACH,wBAAgB,UAAU,CACxB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,aAAa,CAAC,CAqFxB;AAED,uCAAuC;AACvC,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,4EAA4E;IAC5E,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,4EAA4E;IAC5E,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;IACjB,oDAAoD;IACpD,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,SAAS,CAAC,EAAE,aAAa,CAAA;IACzB,gDAAgD;IAChD,GAAG,CAAC,EAAE,IAAI,CAAA;CACX;AAED,8DAA8D;AAC9D,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;CACf;AAiBD;;;;GAIG;AACH,wBAAsB,UAAU,CAC9B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,gBAAgB,CAAC,CA6D3B"}
|
package/dist/edge-tts.js
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { mkdir, rename, rm, stat } from 'node:fs/promises';
|
|
3
|
+
import { dirname } from 'node:path';
|
|
4
|
+
import { AUDIO_FORMAT, outputFilePath } from "./cache.js";
|
|
5
|
+
import { ErrorCode, TtsError, spawnFailure, looksLikeProtocolDrift } from "./errors.js";
|
|
6
|
+
/** Upper bound on captured stderr/stdout text, keeping the tail. */
|
|
7
|
+
const MAX_CAPTURE = 8_000;
|
|
8
|
+
/** Suggestion for a rejected WebSocket handshake / 403, not a transient stall. */
|
|
9
|
+
const PROTOCOL_DRIFT_SUGGESTION = 'Edge-TTS 握手被拒绝(403 / handshake)。这通常意味着上游协议发生了变更(例如 Sec-MS-GEC 令牌代次更新),当前安装的 edge-tts 版本可能无法完成握手。请尝试升级 edge-tts(pip install --upgrade edge-tts),或检查是否需要提高本插件要求的 edge-tts 版本下限;若升级后仍失败,只能等待上游修复或先降级到 SAPI / speechSynthesis。';
|
|
10
|
+
/** Suggestion for timeouts and other transient network failures. */
|
|
11
|
+
const NETWORK_SUGGESTION = '若网络正常但持续超时,请检查是否需要对 speech.platform.bing.com:443 配置代理。注意:环境变量 HTTPS_PROXY 对 edge-tts 的 WSS 连接无效,必须通过本插件的 `proxy` 配置项透传 `--proxy` 参数。详见 README 的「已知限制」章节。';
|
|
12
|
+
const defaultSpawn = (command, args, options) => spawn(command, [...args], options);
|
|
13
|
+
function appendTail(current, chunk) {
|
|
14
|
+
const next = current + chunk.toString();
|
|
15
|
+
return next.length > MAX_CAPTURE ? next.slice(next.length - MAX_CAPTURE) : next;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Run one child process, consuming stdout and stderr concurrently so neither
|
|
19
|
+
* pipe can deadlock. Resolves for any completed exit status; throws structured
|
|
20
|
+
* errors for spawn denial, cancellation, and timeout (killing the child).
|
|
21
|
+
*/
|
|
22
|
+
export function runProcess(command, args, options) {
|
|
23
|
+
const spawnImpl = options.spawnImpl ?? defaultSpawn;
|
|
24
|
+
const signal = options.signal;
|
|
25
|
+
return new Promise((resolve, reject) => {
|
|
26
|
+
let child;
|
|
27
|
+
try {
|
|
28
|
+
child = spawnImpl(command, args, {
|
|
29
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
30
|
+
windowsHide: true,
|
|
31
|
+
env: process.env,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
reject(spawnFailure(error));
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
let settled = false;
|
|
39
|
+
let timer;
|
|
40
|
+
let stdout = '';
|
|
41
|
+
let stderr = '';
|
|
42
|
+
const cleanup = () => {
|
|
43
|
+
if (timer !== undefined)
|
|
44
|
+
clearTimeout(timer);
|
|
45
|
+
signal?.removeEventListener('abort', onAbort);
|
|
46
|
+
};
|
|
47
|
+
const fail = (error) => {
|
|
48
|
+
if (settled)
|
|
49
|
+
return;
|
|
50
|
+
settled = true;
|
|
51
|
+
cleanup();
|
|
52
|
+
reject(error);
|
|
53
|
+
};
|
|
54
|
+
const settle = (code, exitSignal) => {
|
|
55
|
+
if (settled)
|
|
56
|
+
return;
|
|
57
|
+
settled = true;
|
|
58
|
+
cleanup();
|
|
59
|
+
resolve({ code, signal: exitSignal, stdout, stderr });
|
|
60
|
+
};
|
|
61
|
+
const stop = () => {
|
|
62
|
+
try {
|
|
63
|
+
child.kill('SIGTERM');
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
// The process already exited; nothing to stop.
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
const onAbort = () => {
|
|
70
|
+
fail(new TtsError(ErrorCode.Aborted, 'Edge-TTS synthesis was aborted.'));
|
|
71
|
+
stop();
|
|
72
|
+
};
|
|
73
|
+
child.stdout?.on('data', (chunk) => {
|
|
74
|
+
stdout = appendTail(stdout, chunk);
|
|
75
|
+
});
|
|
76
|
+
child.stderr?.on('data', (chunk) => {
|
|
77
|
+
stderr = appendTail(stderr, chunk);
|
|
78
|
+
});
|
|
79
|
+
child.on('error', (error) => fail(spawnFailure(error)));
|
|
80
|
+
child.on('close', (code, exitSignal) => {
|
|
81
|
+
if (signal?.aborted) {
|
|
82
|
+
fail(new TtsError(ErrorCode.Aborted, 'Edge-TTS synthesis was aborted.'));
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
settle(code, exitSignal);
|
|
86
|
+
});
|
|
87
|
+
timer = setTimeout(() => {
|
|
88
|
+
fail(new TtsError(ErrorCode.SynthesisTimeout, `Edge-TTS did not finish within ${options.timeoutMs} ms.`, 'Increase `timeoutMs` or check network connectivity to the Edge-TTS service. 若网络正常但持续超时,请检查是否需要对 speech.platform.bing.com:443 配置代理。注意:环境变量 HTTPS_PROXY 对 edge-tts 的 WSS 连接无效,必须通过本插件的 `proxy` 配置项透传 `--proxy` 参数。详见 README 的「已知限制」章节。'));
|
|
89
|
+
stop();
|
|
90
|
+
}, options.timeoutMs);
|
|
91
|
+
if (signal) {
|
|
92
|
+
if (signal.aborted)
|
|
93
|
+
onAbort();
|
|
94
|
+
else
|
|
95
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
function outputFailure(target, error) {
|
|
100
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
101
|
+
return new TtsError(ErrorCode.OutputDirNotWritable, `Cannot write the audio file ${target}: ${detail}`, 'Check the DSH sandbox file policy and the output directory permissions. This is a filesystem denial, not a spawn EPERM.', { cause: error });
|
|
102
|
+
}
|
|
103
|
+
function stderrSuffix(stderr) {
|
|
104
|
+
const trimmed = stderr.trim();
|
|
105
|
+
return trimmed ? `\nstderr:\n${trimmed}` : '';
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Invoke the Edge-TTS CLI for `text` and return the finished file. Writes to a
|
|
109
|
+
* `.partial` sibling and renames on success, so a crashed or concurrent run
|
|
110
|
+
* never leaves a file that the cache would treat as complete.
|
|
111
|
+
*/
|
|
112
|
+
export async function synthesize(text, options) {
|
|
113
|
+
const filePath = outputFilePath(options.outputDir, options.hash, options.now);
|
|
114
|
+
const partialPath = `${filePath}.partial`;
|
|
115
|
+
try {
|
|
116
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
throw outputFailure(dirname(filePath), error);
|
|
120
|
+
}
|
|
121
|
+
const args = [
|
|
122
|
+
`--text=${text}`,
|
|
123
|
+
`--voice=${options.voice}`,
|
|
124
|
+
`--rate=${options.rate}`,
|
|
125
|
+
...(options.pitch === undefined ? [] : [`--pitch=${options.pitch}`]),
|
|
126
|
+
...(options.volume === undefined ? [] : [`--volume=${options.volume}`]),
|
|
127
|
+
...(options.proxy === undefined ? [] : [`--proxy=${options.proxy}`]),
|
|
128
|
+
`--write-media=${partialPath}`,
|
|
129
|
+
];
|
|
130
|
+
try {
|
|
131
|
+
const result = await runProcess(options.binary, args, {
|
|
132
|
+
timeoutMs: options.timeoutMs,
|
|
133
|
+
signal: options.signal,
|
|
134
|
+
spawnImpl: options.spawnImpl,
|
|
135
|
+
});
|
|
136
|
+
if (result.code !== 0) {
|
|
137
|
+
const status = result.code === null ? 'null' : String(result.code);
|
|
138
|
+
const signal = result.signal ? ` (signal ${result.signal})` : '';
|
|
139
|
+
const drift = looksLikeProtocolDrift(result.stderr);
|
|
140
|
+
throw new TtsError(ErrorCode.EdgeTtsFailed, `Edge-TTS exited with code ${status}${signal}.${stderrSuffix(result.stderr)}`, drift
|
|
141
|
+
? PROTOCOL_DRIFT_SUGGESTION
|
|
142
|
+
: `Check the voice and rate values and network connectivity, then run \`tts_self_check\`. ${NETWORK_SUGGESTION}`, drift ? { protocolDrift: true } : undefined);
|
|
143
|
+
}
|
|
144
|
+
const info = await stat(partialPath).catch(() => undefined);
|
|
145
|
+
if (!info?.isFile() || info.size === 0) {
|
|
146
|
+
throw new TtsError(ErrorCode.EmptyAudio, 'Edge-TTS reported success but produced no audio bytes.', 'Retry the call; if it persists, run `tts_self_check` and inspect Edge-TTS output.');
|
|
147
|
+
}
|
|
148
|
+
try {
|
|
149
|
+
await rename(partialPath, filePath);
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
throw outputFailure(filePath, error);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
await rm(partialPath, { force: true }).catch(() => { });
|
|
157
|
+
throw error;
|
|
158
|
+
}
|
|
159
|
+
return { filePath, format: AUDIO_FORMAT };
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=edge-tts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edge-tts.js","sourceRoot":"","sources":["../src/edge-tts.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory engine health state machine for the fallback chain.
|
|
3
|
+
*
|
|
4
|
+
* It never decides which engine is *first*, and it never changes the fallback
|
|
5
|
+
* trigger — it only answers "should this engine be skipped right now?". A
|
|
6
|
+
* skipped engine is one that recently failed hard enough to open its circuit;
|
|
7
|
+
* once the cooldown elapses a single half-open probe is allowed through, and a
|
|
8
|
+
* success closes the circuit again.
|
|
9
|
+
*
|
|
10
|
+
* State is deliberately process-local. Persisting "unhealthy" across restarts
|
|
11
|
+
* could keep blocking a recovered network, so a plugin restart resets every
|
|
12
|
+
* engine to `healthy`.
|
|
13
|
+
*/
|
|
14
|
+
import type { TtsEngine } from './config.ts';
|
|
15
|
+
/** Circuit state exposed to callers. Half-open is implicit (`probePending`). */
|
|
16
|
+
export type EngineHealthState = 'healthy' | 'degraded' | 'open';
|
|
17
|
+
/** Tunables, all overridable through the DSH config layer. */
|
|
18
|
+
export interface EngineHealthConfig {
|
|
19
|
+
/** When false every engine is always attempted. */
|
|
20
|
+
enabled: boolean;
|
|
21
|
+
/** Consecutive transient failures that open the circuit. */
|
|
22
|
+
failureThreshold: number;
|
|
23
|
+
/** Cooldown after a handshake/403 failure. */
|
|
24
|
+
driftCooldownMs: number;
|
|
25
|
+
/** Cooldown after the transient failure threshold is reached. */
|
|
26
|
+
transientCooldownMs: number;
|
|
27
|
+
/** Upper bound for the doubled cooldown after a failed half-open probe. */
|
|
28
|
+
maxCooldownMs: number;
|
|
29
|
+
}
|
|
30
|
+
/** Defaults shared by the config schema and `resolveConfig`. */
|
|
31
|
+
export declare const DEFAULT_ENGINE_HEALTH: Readonly<EngineHealthConfig>;
|
|
32
|
+
/** Mutable per-engine record. */
|
|
33
|
+
export interface EngineHealth {
|
|
34
|
+
state: EngineHealthState;
|
|
35
|
+
consecutiveFailures: number;
|
|
36
|
+
/** When the circuit opened; the cooldown is measured from here. */
|
|
37
|
+
openedAt?: number;
|
|
38
|
+
lastErrorCode?: string;
|
|
39
|
+
/** Cooldown currently in force for this engine. */
|
|
40
|
+
cooldownMs: number;
|
|
41
|
+
/** Set when a half-open probe has been let through. */
|
|
42
|
+
probePending: boolean;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* JSON-safe snapshot shape. `lastErrorCode` is a separate branch instead of an
|
|
46
|
+
* optional property so every variant is plain JSON.
|
|
47
|
+
*/
|
|
48
|
+
export type EngineHealthSnapshot = {
|
|
49
|
+
state: EngineHealthState;
|
|
50
|
+
consecutiveFailures: number;
|
|
51
|
+
} | {
|
|
52
|
+
state: EngineHealthState;
|
|
53
|
+
consecutiveFailures: number;
|
|
54
|
+
lastErrorCode: string;
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Whether a failure should move the circuit. Validation, config, cache and
|
|
58
|
+
* abort errors describe the request, not the engine; `SPAWN_EPERM` and a
|
|
59
|
+
* missing binary are environment/install problems that a circuit cannot fix.
|
|
60
|
+
*/
|
|
61
|
+
export declare function countsAsEngineFailure(error: unknown): boolean;
|
|
62
|
+
/** Result of planning one attempt against the current health. */
|
|
63
|
+
export interface EngineHealthPlan {
|
|
64
|
+
/** Engines to skip for this attempt. Never contains the forced last engine. */
|
|
65
|
+
skip: ReadonlySet<TtsEngine>;
|
|
66
|
+
/** Clock reading used for the plan, in epoch milliseconds. */
|
|
67
|
+
observedAt: number;
|
|
68
|
+
}
|
|
69
|
+
/** Reading side used by the fallback chain. */
|
|
70
|
+
export interface EngineHealthHooks {
|
|
71
|
+
recordSuccess(engine: TtsEngine): void;
|
|
72
|
+
recordFailure(engine: TtsEngine, error: unknown): void;
|
|
73
|
+
stateOf(engine: TtsEngine): EngineHealthState;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* One mutable registry per tool instance. `now` is injectable so tests can
|
|
77
|
+
* advance the cooldown without sleeping.
|
|
78
|
+
*/
|
|
79
|
+
export declare class EngineHealthRegistry implements EngineHealthHooks {
|
|
80
|
+
#private;
|
|
81
|
+
constructor(config?: EngineHealthConfig, now?: () => Date);
|
|
82
|
+
/** Current state without side effects; unknown engines are `healthy`. */
|
|
83
|
+
stateOf(engine: TtsEngine): EngineHealthState;
|
|
84
|
+
/** Full record, exposed for diagnostics and unit tests. */
|
|
85
|
+
healthOf(engine: TtsEngine): Readonly<EngineHealth>;
|
|
86
|
+
/**
|
|
87
|
+
* Whether `engine` must be skipped for this attempt. When an open circuit's
|
|
88
|
+
* cooldown has elapsed the call is let through and marked as a probe.
|
|
89
|
+
*/
|
|
90
|
+
shouldSkip(engine: TtsEngine): boolean;
|
|
91
|
+
/**
|
|
92
|
+
* Plan a whole chain at once. If every engine is open the last one is forced
|
|
93
|
+
* through: a fully open chain must still attempt something rather than fail
|
|
94
|
+
* silently with an empty result.
|
|
95
|
+
*/
|
|
96
|
+
plan(chain: readonly TtsEngine[]): EngineHealthPlan;
|
|
97
|
+
/** A successful attempt closes the circuit and clears the failure streak. */
|
|
98
|
+
recordSuccess(engine: TtsEngine): void;
|
|
99
|
+
/**
|
|
100
|
+
* Record a failed attempt. Drift opens immediately with the long cooldown;
|
|
101
|
+
* transient failures accumulate until `failureThreshold`. A failed half-open
|
|
102
|
+
* probe re-opens with a doubled cooldown, capped at `maxCooldownMs`.
|
|
103
|
+
*/
|
|
104
|
+
recordFailure(engine: TtsEngine, error: unknown): void;
|
|
105
|
+
/** Snapshot for `tts_self_check`; never performs I/O. */
|
|
106
|
+
snapshot(): Record<string, EngineHealthSnapshot>;
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=engine-health.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine-health.d.ts","sourceRoot":"","sources":["../src/engine-health.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAG5C,gFAAgF;AAChF,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,UAAU,GAAG,MAAM,CAAA;AAE/D,8DAA8D;AAC9D,MAAM,WAAW,kBAAkB;IACjC,mDAAmD;IACnD,OAAO,EAAE,OAAO,CAAA;IAChB,4DAA4D;IAC5D,gBAAgB,EAAE,MAAM,CAAA;IACxB,8CAA8C;IAC9C,eAAe,EAAE,MAAM,CAAA;IACvB,iEAAiE;IACjE,mBAAmB,EAAE,MAAM,CAAA;IAC3B,2EAA2E;IAC3E,aAAa,EAAE,MAAM,CAAA;CACtB;AAED,gEAAgE;AAChE,eAAO,MAAM,qBAAqB,EAAE,QAAQ,CAAC,kBAAkB,CAM7D,CAAA;AAEF,iCAAiC;AACjC,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,iBAAiB,CAAA;IACxB,mBAAmB,EAAE,MAAM,CAAA;IAC3B,mEAAmE;IACnE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,mDAAmD;IACnD,UAAU,EAAE,MAAM,CAAA;IAClB,uDAAuD;IACvD,YAAY,EAAE,OAAO,CAAA;CACtB;AAED;;;GAGG;AACH,MAAM,MAAM,oBAAoB,GAC5B;IAAE,KAAK,EAAE,iBAAiB,CAAC;IAAC,mBAAmB,EAAE,MAAM,CAAA;CAAE,GACzD;IACE,KAAK,EAAE,iBAAiB,CAAA;IACxB,mBAAmB,EAAE,MAAM,CAAA;IAC3B,aAAa,EAAE,MAAM,CAAA;CACtB,CAAA;AAaL;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAI7D;AAED,iEAAiE;AACjE,MAAM,WAAW,gBAAgB;IAC/B,+EAA+E;IAC/E,IAAI,EAAE,WAAW,CAAC,SAAS,CAAC,CAAA;IAC5B,8DAA8D;IAC9D,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,+CAA+C;AAC/C,MAAM,WAAW,iBAAiB;IAChC,aAAa,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAAA;IACtC,aAAa,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAAA;IACtD,OAAO,CAAC,MAAM,EAAE,SAAS,GAAG,iBAAiB,CAAA;CAC9C;AAWD;;;GAGG;AACH,qBAAa,oBAAqB,YAAW,iBAAiB;;gBAM1D,MAAM,GAAE,kBAAiD,EACzD,GAAG,GAAE,MAAM,IAAuB;IAMpC,yEAAyE;IACzE,OAAO,CAAC,MAAM,EAAE,SAAS,GAAG,iBAAiB;IAI7C,2DAA2D;IAC3D,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,QAAQ,CAAC,YAAY,CAAC;IAInD;;;OAGG;IACH,UAAU,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO;IAYtC;;;;OAIG;IACH,IAAI,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,GAAG,gBAAgB;IAkBnD,6EAA6E;IAC7E,aAAa,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI;IAStC;;;;OAIG;IACH,aAAa,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI;IAoCtD,yDAAyD;IACzD,QAAQ,IAAI,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC;CA+BjD"}
|