@drakulavich/kesha-voice-kit 1.10.0 → 1.11.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/SKILL.md CHANGED
@@ -45,11 +45,21 @@ kesha --json voice.ogg
45
45
 
46
46
  Use `lang` (or the more detailed `audioLanguage`/`textLanguage`) to decide how to respond.
47
47
 
48
+ Need timestamped transcript segments for navigation, chapters, or downstream editing:
49
+
50
+ ```bash
51
+ kesha --json --timestamps voice.ogg > voice.timestamps.json
52
+ jq '.[0].segments' voice.timestamps.json
53
+ ```
54
+
55
+ Each segment has `start`, `end`, and `text` fields. `--timestamps` is available for machine-readable output (`--json`, `--toon`, or `--format json`).
56
+
48
57
  **Formats:** .ogg, .opus, .mp3, .m4a, .wav, .flac, .webm — decoded via symphonia, no ffmpeg required.
49
58
 
50
59
  **Other output modes:**
51
60
  - `kesha audio.ogg` — plain transcript on stdout
52
61
  - `kesha --format transcript audio.ogg` — transcript + `[lang: ru, confidence: 0.99]` footer
62
+ - `kesha --json --timestamps audio.ogg` — JSON with timestamped `segments`
53
63
  - `kesha --verbose audio.ogg` — human-readable with language info
54
64
  - `kesha --lang en audio.ogg` — warn if detected language differs (useful sanity check)
55
65
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drakulavich/kesha-voice-kit",
3
- "version": "1.10.0",
3
+ "version": "1.11.0",
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": {
@@ -30,7 +30,7 @@
30
30
  ]
31
31
  },
32
32
  "keshaEngine": {
33
- "version": "1.10.0"
33
+ "version": "1.11.0"
34
34
  },
35
35
  "scripts": {
36
36
  "test": "bun test",
package/src/engine.ts CHANGED
@@ -1,8 +1,15 @@
1
1
  import { join } from "path";
2
2
  import { homedir } from "os";
3
- import { existsSync } from "fs";
3
+ import { existsSync, statSync } from "fs";
4
4
  import { log } from "./log";
5
5
 
6
+ /**
7
+ * Capability-flag string surfaced via `kesha-engine --capabilities-json`. Single
8
+ * source of truth so the engine, the TS CLI gate, and the integration tests
9
+ * can't drift. Mirrors `rust/src/transcribe.rs::TRANSCRIBE_SEGMENTS_FEATURE`.
10
+ */
11
+ export const TRANSCRIBE_SEGMENTS_FEATURE = "transcribe.segments";
12
+
6
13
  export interface LangDetectResult {
7
14
  code: string;
8
15
  confidence: number;
@@ -108,7 +115,7 @@ export async function transcribeEngineWithSegments(
108
115
  opts: TranscribeEngineOptions = {},
109
116
  ): Promise<TranscriptionOutput> {
110
117
  const caps = await getEngineCapabilities();
111
- if (!caps?.features.includes("transcribe.segments")) {
118
+ if (!caps?.features.includes(TRANSCRIBE_SEGMENTS_FEATURE)) {
112
119
  throw new Error(
113
120
  "Timestamped segments require a newer kesha-engine. Run `kesha install` after upgrading Kesha Voice Kit.",
114
121
  );
@@ -162,20 +169,33 @@ export interface EngineCapabilities {
162
169
  }
163
170
 
164
171
  let cachedEngineCapabilities:
165
- | { binPath: string; capabilities: EngineCapabilities }
172
+ | { binPath: string; mtime: number; capabilities: EngineCapabilities }
166
173
  | null = null;
167
174
 
168
175
  export async function getEngineCapabilities(): Promise<EngineCapabilities | null> {
169
176
  const binPath = getEngineBinPath();
170
- if (cachedEngineCapabilities?.binPath === binPath) {
177
+ // Cache key includes `mtimeMs` so the cache invalidates when `kesha
178
+ // install` overwrites the binary in-place within a single long-lived
179
+ // process (#248). `statSync` throws on missing-file; the catch returns
180
+ // `null` — same effect as the previous explicit `isEngineInstalled()`
181
+ // pre-flight, one fewer redundant fs call.
182
+ let mtime: number;
183
+ try {
184
+ mtime = statSync(binPath).mtimeMs;
185
+ } catch {
186
+ return null;
187
+ }
188
+ if (
189
+ cachedEngineCapabilities?.binPath === binPath &&
190
+ cachedEngineCapabilities.mtime === mtime
191
+ ) {
171
192
  return cachedEngineCapabilities.capabilities;
172
193
  }
173
- if (!existsSync(binPath)) return null;
174
194
  const { stdout, exitCode } = await runEngine(["--capabilities-json"]);
175
195
  if (exitCode !== 0) return null;
176
196
  try {
177
197
  const capabilities = JSON.parse(stdout) as EngineCapabilities;
178
- cachedEngineCapabilities = { binPath, capabilities };
198
+ cachedEngineCapabilities = { binPath, mtime, capabilities };
179
199
  return capabilities;
180
200
  } catch {
181
201
  return null;
package/src/lib.ts CHANGED
@@ -44,7 +44,7 @@ export async function transcribe(
44
44
  return internalTranscribe(audioPath, { ...options, silent: true });
45
45
  }
46
46
 
47
- export async function transcribeWithSegments(
47
+ export async function transcribeWithTimestamps(
48
48
  audioPath: string,
49
49
  options: TranscribeOptions = {},
50
50
  ) {
@@ -58,3 +58,10 @@ export async function transcribeWithSegments(
58
58
  silent: true,
59
59
  });
60
60
  }
61
+
62
+ /**
63
+ * @deprecated Renamed to {@link transcribeWithTimestamps} (#248). The old
64
+ * name shipped briefly in v1.9.0; this alias keeps existing imports working
65
+ * for one minor-version cycle. Will be removed in v1.12.0.
66
+ */
67
+ export const transcribeWithSegments = transcribeWithTimestamps;