@drakulavich/kesha-voice-kit 1.15.0 → 1.15.1
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/README.md +1 -1
- package/package.json +1 -1
- package/src/cli/main.ts +85 -11
- package/src/cli.ts +7 -1
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<h1 align="center">Kesha Voice Kit</h1>
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
|
-
<a href="https://
|
|
8
|
+
<a href="https://flakiness.io/Laputa/kesha-voice-kit"><img src="https://img.shields.io/endpoint?url=https%3A%2F%2Fflakiness.io%2Fapi%2Fbadge%3Finput%3D%257B%2522badgeToken%2522%253A%2522badge-2IKMRRqUxh9P3w8Ym3Szf0%2522%257D" alt="Tests"></a>
|
|
9
9
|
<a href="https://www.npmjs.com/package/@drakulavich/kesha-voice-kit"><img src="https://img.shields.io/npm/v/@drakulavich/kesha-voice-kit" alt="npm version"></a>
|
|
10
10
|
<a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="License: MIT"></a>
|
|
11
11
|
<a href="https://bun.sh"><img src="https://img.shields.io/badge/runtime-Bun-f9f1e1?logo=bun" alt="Bun"></a>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@drakulavich/kesha-voice-kit",
|
|
3
|
-
"version": "1.15.
|
|
3
|
+
"version": "1.15.1",
|
|
4
4
|
"description": "Open-source voice toolkit. Speech-to-text (25 langs, ~19x faster than Whisper on Apple Silicon, ONNX fallback on CPU), text-to-speech (Kokoro + Vosk-TTS + ~180 macOS system voices, SSML), voice-activity detection, language detection (107 langs). Rust engine, OpenClaw skill.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/cli/main.ts
CHANGED
|
@@ -34,6 +34,69 @@ export function detectLanguage(text: string): string {
|
|
|
34
34
|
return detect(text);
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
/**
|
|
38
|
+
* Pure validation + normalization of the output-format selection. Pulled
|
|
39
|
+
* out of the citty `run` handler so the contract is unit-testable without
|
|
40
|
+
* spawning the CLI binary; the handler just owns the side effects
|
|
41
|
+
* (log.error + process.exit) when this returns `{ ok: false }`.
|
|
42
|
+
*
|
|
43
|
+
* Inputs accept the three knobs the user can flip:
|
|
44
|
+
* - `--json` (boolean) — long-form alias for `--format json`
|
|
45
|
+
* - `--toon` (boolean) — long-form alias for `--format toon`
|
|
46
|
+
* - `--format <value>` — must be one of `transcript`, `json`, `toon`
|
|
47
|
+
*
|
|
48
|
+
* Mutex: `--json` and `--toon` cannot both be requested. Either via the
|
|
49
|
+
* booleans or `--format` cross-pollination (`--json --format toon` →
|
|
50
|
+
* error). The mutex check happens AFTER format validation, so unknown
|
|
51
|
+
* `--format` still surfaces with its own clearer error first.
|
|
52
|
+
*/
|
|
53
|
+
export type ResolvedOutputFormat =
|
|
54
|
+
| {
|
|
55
|
+
ok: true;
|
|
56
|
+
wantsJson: boolean;
|
|
57
|
+
wantsToon: boolean;
|
|
58
|
+
wantsTranscript: boolean;
|
|
59
|
+
}
|
|
60
|
+
| { ok: false; error: string };
|
|
61
|
+
|
|
62
|
+
const SUPPORTED_FORMATS = ["transcript", "json", "toon"] as const;
|
|
63
|
+
|
|
64
|
+
export function resolveOutputFormat(input: {
|
|
65
|
+
json?: boolean;
|
|
66
|
+
toon?: boolean;
|
|
67
|
+
format?: string;
|
|
68
|
+
}): ResolvedOutputFormat {
|
|
69
|
+
if (input.format !== undefined && !SUPPORTED_FORMATS.includes(input.format as never)) {
|
|
70
|
+
return {
|
|
71
|
+
ok: false,
|
|
72
|
+
error: `unknown --format '${input.format}'. supported: ${SUPPORTED_FORMATS.join(", ")}`,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
const wantsJson = !!input.json || input.format === "json";
|
|
76
|
+
const wantsToon = !!input.toon || input.format === "toon";
|
|
77
|
+
const wantsTranscript = input.format === "transcript";
|
|
78
|
+
if (wantsJson && wantsToon) {
|
|
79
|
+
return {
|
|
80
|
+
ok: false,
|
|
81
|
+
error: "--json and --toon are mutually exclusive (pick one output format).",
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
// `--format transcript` + boolean `--json` / `--toon` was previously
|
|
85
|
+
// accepted and silently produced the boolean's format (because the
|
|
86
|
+
// dispatch checked wantsJson/wantsToon first). Greptile P2 on #300
|
|
87
|
+
// flagged the silent override. Fail loudly with the same shape as
|
|
88
|
+
// the json/toon mutex — symmetric across all three formats.
|
|
89
|
+
if (wantsTranscript && (wantsJson || wantsToon)) {
|
|
90
|
+
return {
|
|
91
|
+
ok: false,
|
|
92
|
+
error:
|
|
93
|
+
"--format transcript is mutually exclusive with --json / --toon " +
|
|
94
|
+
"(pick one output format).",
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
return { ok: true, wantsJson, wantsToon, wantsTranscript };
|
|
98
|
+
}
|
|
99
|
+
|
|
37
100
|
export function checkLanguageMismatch(expected: string | undefined, detected: string): string | null {
|
|
38
101
|
if (!expected || !detected || expected === detected) return null;
|
|
39
102
|
return `warning: expected language "${expected}" but detected "${detected}"`;
|
|
@@ -76,7 +139,7 @@ export const mainCommand = defineCommand({
|
|
|
76
139
|
},
|
|
77
140
|
format: {
|
|
78
141
|
type: "string",
|
|
79
|
-
description: "Output format: transcript (
|
|
142
|
+
description: "Output format: transcript | json | toon (long-form alias for --json / --toon)",
|
|
80
143
|
},
|
|
81
144
|
lang: {
|
|
82
145
|
type: "string",
|
|
@@ -102,21 +165,32 @@ export const mainCommand = defineCommand({
|
|
|
102
165
|
if (args.debug) log.debugEnabled = true;
|
|
103
166
|
const files = args._;
|
|
104
167
|
|
|
105
|
-
|
|
106
|
-
|
|
168
|
+
// Validate `--format <value>` and normalize into the boolean flags
|
|
169
|
+
// that the rest of this handler consults. Routing happens in
|
|
170
|
+
// `resolveOutputFormat` so the contract is unit-testable without
|
|
171
|
+
// spawning the CLI; the handler just owns the side-effect arms
|
|
172
|
+
// (log.error + process.exit).
|
|
173
|
+
const fmt = resolveOutputFormat({
|
|
174
|
+
json: args.json,
|
|
175
|
+
toon: args.toon,
|
|
176
|
+
format: args.format,
|
|
177
|
+
});
|
|
178
|
+
if (!fmt.ok) {
|
|
179
|
+
log.error(fmt.error);
|
|
107
180
|
process.exit(2);
|
|
108
181
|
}
|
|
182
|
+
const { wantsJson, wantsToon, wantsTranscript } = fmt;
|
|
109
183
|
|
|
110
184
|
if (args.vad && args["no-vad"]) {
|
|
111
185
|
log.error("--vad and --no-vad are mutually exclusive.");
|
|
112
186
|
process.exit(2);
|
|
113
187
|
}
|
|
114
|
-
if (args.timestamps && !(
|
|
115
|
-
log.error("--timestamps requires --json, --toon, or --format json.");
|
|
188
|
+
if (args.timestamps && !(wantsJson || wantsToon)) {
|
|
189
|
+
log.error("--timestamps requires --json, --toon, or --format {json,toon}.");
|
|
116
190
|
process.exit(2);
|
|
117
191
|
}
|
|
118
|
-
if (args.speakers && !(
|
|
119
|
-
log.error("--speakers requires --json, --toon, or --format json.");
|
|
192
|
+
if (args.speakers && !(wantsJson || wantsToon)) {
|
|
193
|
+
log.error("--speakers requires --json, --toon, or --format {json,toon}.");
|
|
120
194
|
process.exit(2);
|
|
121
195
|
}
|
|
122
196
|
const vadMode = args.vad ? "on" : args["no-vad"] ? "off" : "auto";
|
|
@@ -129,7 +203,7 @@ export const mainCommand = defineCommand({
|
|
|
129
203
|
let hasError = false;
|
|
130
204
|
const results: TranscribeResult[] = [];
|
|
131
205
|
|
|
132
|
-
const wantsLangId = !!(args.lang || args.verbose ||
|
|
206
|
+
const wantsLangId = !!(args.lang || args.verbose || wantsJson || wantsToon || wantsTranscript);
|
|
133
207
|
|
|
134
208
|
for (const file of files) {
|
|
135
209
|
const startedAt = performance.now();
|
|
@@ -185,11 +259,11 @@ export const mainCommand = defineCommand({
|
|
|
185
259
|
}
|
|
186
260
|
}
|
|
187
261
|
|
|
188
|
-
if (
|
|
262
|
+
if (wantsJson) {
|
|
189
263
|
process.stdout.write(formatJsonOutput(results));
|
|
190
|
-
} else if (
|
|
264
|
+
} else if (wantsToon) {
|
|
191
265
|
process.stdout.write(formatToonOutput(results));
|
|
192
|
-
} else if (
|
|
266
|
+
} else if (wantsTranscript) {
|
|
193
267
|
process.stdout.write(formatTranscriptOutput(results));
|
|
194
268
|
} else if (args.verbose) {
|
|
195
269
|
process.stdout.write(formatVerboseOutput(results));
|
package/src/cli.ts
CHANGED
|
@@ -7,7 +7,13 @@ export { installCommand } from "./cli/install";
|
|
|
7
7
|
export { sayCommand } from "./cli/say";
|
|
8
8
|
export { pickVoiceForLang } from "./voice-routing";
|
|
9
9
|
export { statusCommand } from "./cli/status";
|
|
10
|
-
export {
|
|
10
|
+
export {
|
|
11
|
+
mainCommand,
|
|
12
|
+
detectLanguage,
|
|
13
|
+
checkLanguageMismatch,
|
|
14
|
+
resolveOutputFormat,
|
|
15
|
+
} from "./cli/main";
|
|
16
|
+
export type { ResolvedOutputFormat } from "./cli/main";
|
|
11
17
|
export { runCli } from "./cli/dispatch";
|
|
12
18
|
|
|
13
19
|
export type { TranscribeResult } from "./types";
|