@drakulavich/kesha-voice-kit 1.4.1 → 1.4.2
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 +9 -0
- package/package.json +2 -2
- package/src/status.ts +86 -1
package/README.md
CHANGED
|
@@ -64,6 +64,15 @@ Your agent receives a voice message in Telegram/WhatsApp/Slack, Kesha transcribe
|
|
|
64
64
|
|
|
65
65
|
Manage the plugin with `openclaw plugins list`, `openclaw plugins disable kesha-voice-kit`, or `openclaw plugins uninstall kesha-voice-kit`.
|
|
66
66
|
|
|
67
|
+
## Raycast Extension (macOS)
|
|
68
|
+
|
|
69
|
+
Transcribe audio and speak clipboard text from Raycast's launcher without opening a terminal. Two commands:
|
|
70
|
+
|
|
71
|
+
- **Transcribe Selected Audio** — pick an audio file in Finder, hit the command, transcript lands on your clipboard.
|
|
72
|
+
- **Speak Clipboard** — synthesize whatever's on your clipboard and play it through the default output.
|
|
73
|
+
|
|
74
|
+
Source + install instructions: [`raycast/`](raycast/). Tracked in [#145](https://github.com/drakulavich/kesha-voice-kit/issues/145); not yet on the Raycast Store — install via `ray develop` from the subdirectory.
|
|
75
|
+
|
|
67
76
|
## CLI Tools
|
|
68
77
|
|
|
69
78
|
```bash
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@drakulavich/kesha-voice-kit",
|
|
3
|
-
"version": "1.4.
|
|
4
|
-
"description": "Open-source voice toolkit
|
|
3
|
+
"version": "1.4.2",
|
|
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 + Piper + ~180 macOS system voices, SSML), voice-activity detection, language detection (107 langs). Rust engine, OpenClaw skill.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"kesha": "bin/kesha.js",
|
package/src/status.ts
CHANGED
|
@@ -1,10 +1,37 @@
|
|
|
1
|
-
import { readdirSync } from "fs";
|
|
1
|
+
import { readdirSync, statSync } from "fs";
|
|
2
2
|
import { homedir } from "os";
|
|
3
3
|
import { join } from "path";
|
|
4
4
|
import { isEngineInstalled, getEngineBinPath, getEngineCapabilities } from "./engine";
|
|
5
5
|
import { log } from "./log";
|
|
6
6
|
import pc from "picocolors";
|
|
7
7
|
|
|
8
|
+
function humanBytes(bytes: number): string {
|
|
9
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
10
|
+
const units = ["KB", "MB", "GB"];
|
|
11
|
+
let n = bytes / 1024;
|
|
12
|
+
let i = 0;
|
|
13
|
+
while (n >= 1024 && i < units.length - 1) {
|
|
14
|
+
n /= 1024;
|
|
15
|
+
i++;
|
|
16
|
+
}
|
|
17
|
+
return `${n.toFixed(n >= 100 ? 0 : 1)} ${units[i]}`;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function dirSizeBytes(path: string): number {
|
|
21
|
+
let total = 0;
|
|
22
|
+
try {
|
|
23
|
+
const st = statSync(path);
|
|
24
|
+
if (st.isFile()) return st.size;
|
|
25
|
+
for (const entry of readdirSync(path, { withFileTypes: true })) {
|
|
26
|
+
const p = join(path, entry.name);
|
|
27
|
+
total += entry.isDirectory() ? dirSizeBytes(p) : statSync(p).size;
|
|
28
|
+
}
|
|
29
|
+
} catch {
|
|
30
|
+
/* missing path — component not installed */
|
|
31
|
+
}
|
|
32
|
+
return total;
|
|
33
|
+
}
|
|
34
|
+
|
|
8
35
|
export function formatStatusLine(
|
|
9
36
|
label: string,
|
|
10
37
|
path: string | null,
|
|
@@ -53,6 +80,8 @@ export async function showStatus(): Promise<void> {
|
|
|
53
80
|
}
|
|
54
81
|
log.info("");
|
|
55
82
|
}
|
|
83
|
+
|
|
84
|
+
showDiskUsage(binPath);
|
|
56
85
|
}
|
|
57
86
|
|
|
58
87
|
if (!installed) {
|
|
@@ -60,6 +89,62 @@ export async function showStatus(): Promise<void> {
|
|
|
60
89
|
}
|
|
61
90
|
}
|
|
62
91
|
|
|
92
|
+
function showDiskUsage(binPath: string): void {
|
|
93
|
+
const cache = kesheCacheDir();
|
|
94
|
+
// Engine binary lives under `<cache>/engine/bin/` (managed by the TS CLI's
|
|
95
|
+
// engine-install) while all models live under `<cache>/models/` (managed by
|
|
96
|
+
// the Rust engine). Point at `engine/` (two levels up from the binary) so
|
|
97
|
+
// any future sibling files under that root (metadata, hooks, etc.) are
|
|
98
|
+
// counted too.
|
|
99
|
+
const engineDir = join(binPath, "..", "..");
|
|
100
|
+
|
|
101
|
+
const components: Array<{ label: string; path: string }> = [
|
|
102
|
+
{ label: "Engine", path: engineDir },
|
|
103
|
+
{ label: "ASR (Parakeet)", path: join(cache, "models/parakeet-tdt-v3") },
|
|
104
|
+
{ label: "Language ID", path: join(cache, "models/lang-id-ecapa") },
|
|
105
|
+
{ label: "VAD (Silero)", path: join(cache, "models/silero-vad") },
|
|
106
|
+
{ label: "TTS (Kokoro)", path: join(cache, "models/kokoro-82m") },
|
|
107
|
+
{ label: "TTS (Piper)", path: join(cache, "models/piper-ru") },
|
|
108
|
+
{ label: "TTS (G2P)", path: join(cache, "models/g2p") },
|
|
109
|
+
];
|
|
110
|
+
|
|
111
|
+
const rows: Array<{ label: string; size: number }> = [];
|
|
112
|
+
for (const c of components) {
|
|
113
|
+
const size = dirSizeBytes(c.path);
|
|
114
|
+
if (size > 0) rows.push({ label: c.label, size });
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (rows.length === 0) return;
|
|
118
|
+
|
|
119
|
+
// Total counts everything under both the model cache root AND the engine
|
|
120
|
+
// dir (which may live outside the cache when `KESHA_ENGINE_BIN` overrides
|
|
121
|
+
// the default layout). That way the number matches what the `rm -rf` hint
|
|
122
|
+
// below would actually free, including any temp downloads or future
|
|
123
|
+
// components not in the per-row list.
|
|
124
|
+
const componentTotal = rows.reduce((n, r) => n + r.size, 0);
|
|
125
|
+
const cacheTotal = dirSizeBytes(cache);
|
|
126
|
+
const engineOutsideCache = engineDir.startsWith(cache)
|
|
127
|
+
? 0
|
|
128
|
+
: dirSizeBytes(engineDir);
|
|
129
|
+
const total = cacheTotal + engineOutsideCache;
|
|
130
|
+
|
|
131
|
+
log.info(`Disk usage (${cache}):`);
|
|
132
|
+
const labelWidth = Math.max(...rows.map((r) => r.label.length), "Total".length);
|
|
133
|
+
for (const r of rows) {
|
|
134
|
+
const pad = " ".repeat(labelWidth - r.label.length + 2);
|
|
135
|
+
log.info(` ${r.label}:${pad}${humanBytes(r.size)}`);
|
|
136
|
+
}
|
|
137
|
+
const totalPad = " ".repeat(labelWidth - "Total".length + 2);
|
|
138
|
+
log.info(` ${pc.bold("Total")}:${totalPad}${pc.bold(humanBytes(total))}`);
|
|
139
|
+
if (total > componentTotal) {
|
|
140
|
+
const other = total - componentTotal;
|
|
141
|
+
log.info(pc.dim(` (includes ${humanBytes(other)} of other cache files)`));
|
|
142
|
+
}
|
|
143
|
+
log.info("");
|
|
144
|
+
log.info(pc.dim(` To reset cache: rm -rf ${cache} — next \`kesha install\` re-downloads.`));
|
|
145
|
+
log.info("");
|
|
146
|
+
}
|
|
147
|
+
|
|
63
148
|
function kesheCacheDir(): string {
|
|
64
149
|
return process.env.KESHA_CACHE_DIR ?? join(homedir(), ".cache", "kesha");
|
|
65
150
|
}
|