@drakulavich/kesha-voice-kit 1.24.3 → 1.24.5

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/bin/kesha.js CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env bun
2
- import { runCli } from "../src/cli.ts";
2
+ import { runCli } from "../src/cli/dispatch.ts";
3
3
 
4
4
  await runCli();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drakulavich/kesha-voice-kit",
3
- "version": "1.24.3",
3
+ "version": "1.24.5",
4
4
  "description": "Give your tools a voice — speech to text and back, 25 languages, up to ~19× faster than Whisper. On your machine.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -38,7 +38,7 @@
38
38
  }
39
39
  },
40
40
  "keshaEngine": {
41
- "version": "1.24.3"
41
+ "version": "1.24.5"
42
42
  },
43
43
  "scripts": {
44
44
  "test": "bun test",
@@ -52,7 +52,6 @@
52
52
  "coverage:check:rust": "bun .github/scripts/check-coverage.ts --preset=rust --lcov=coverage/rust/lcov.info --summary=coverage/rust/summary.md",
53
53
  "generate:shell-artifacts": "bun scripts/generate-shell-artifacts.ts",
54
54
  "lint": "bunx tsc --noEmit",
55
- "lint:tsgo": "bunx tsgo --noEmit",
56
55
  "check": "bun run lint && bun run check:versions && bun run test:cli-fast",
57
56
  "check:workflows": "bun .github/scripts/check-workflows.ts",
58
57
  "check:release-manifest": "bun .github/scripts/release-manifest.mjs --check",
@@ -85,9 +84,8 @@
85
84
  },
86
85
  "devDependencies": {
87
86
  "@types/bun": "latest",
88
- "@typescript/native-preview": "^7.0.0-dev.20260423.1",
89
87
  "fast-xml-parser": "^5.8.0",
90
- "typescript": "^6.0.2",
88
+ "typescript": "^7.0.2",
91
89
  "yaml": "^2.9.0"
92
90
  },
93
91
  "dependencies": {
@@ -99,5 +97,8 @@
99
97
  "picocolors": "^1.1.1",
100
98
  "tinyld": "^1.3.4",
101
99
  "zod": "^4.4.3"
100
+ },
101
+ "overrides": {
102
+ "hono": "^4.12.25"
102
103
  }
103
104
  }
@@ -2,37 +2,23 @@ import { runMain, type CommandDef } from "citty";
2
2
  import { existsSync } from "fs";
3
3
  import { log, setColorEnabled } from "../log";
4
4
  import { suggestCommand } from "../suggest-command";
5
- import { completionsCommand } from "./completions";
6
- import { doctorCommand } from "./doctor";
7
- import { initCommand } from "./init";
8
- import { installCommand } from "./install";
9
- import { logsCommand } from "./logs";
10
- import { manpageCommand } from "./manpage";
11
- import { recordCommand } from "./record";
12
- import { sayCommand } from "./say";
13
- import { statsCommand } from "./stats";
14
- import { statusCommand } from "./status";
15
- import { supportBundleCommand } from "./support-bundle";
16
- import { mainCommand } from "./main";
17
- import { mcpCommand } from "./mcp";
18
5
 
19
- // Single source of truth: keyed lookup also feeds the `did you mean` suggester.
20
- // `CommandDef<any>` is intentional — citty's generic is invariant in the args
21
- // shape, and each subcommand has its own arg schema; the value here is only
22
- // passed back to `runMain`, which re-reads the schema from the def itself.
23
- const SUBCOMMANDS: Record<string, CommandDef<any>> = {
24
- doctor: doctorCommand,
25
- init: initCommand,
26
- install: installCommand,
27
- logs: logsCommand,
28
- status: statusCommand,
29
- record: recordCommand,
30
- say: sayCommand,
31
- stats: statsCommand,
32
- "support-bundle": supportBundleCommand,
33
- completions: completionsCommand,
34
- manpage: manpageCommand,
35
- mcp: mcpCommand,
6
+ // Lazy loaders so a cold CLI spawn transpiles only the invoked command, not the whole graph (#568).
7
+ // `CommandDef<any>`: citty's generic is invariant in the arg shape and each command has its own schema.
8
+ type CommandLoader = () => Promise<CommandDef<any>>;
9
+ const SUBCOMMANDS: Record<string, CommandLoader> = {
10
+ doctor: async () => (await import("./doctor")).doctorCommand,
11
+ init: async () => (await import("./init")).initCommand,
12
+ install: async () => (await import("./install")).installCommand,
13
+ logs: async () => (await import("./logs")).logsCommand,
14
+ status: async () => (await import("./status")).statusCommand,
15
+ record: async () => (await import("./record")).recordCommand,
16
+ say: async () => (await import("./say")).sayCommand,
17
+ stats: async () => (await import("./stats")).statsCommand,
18
+ "support-bundle": async () => (await import("./support-bundle")).supportBundleCommand,
19
+ completions: async () => (await import("./completions")).completionsCommand,
20
+ manpage: async () => (await import("./manpage")).manpageCommand,
21
+ mcp: async () => (await import("./mcp")).mcpCommand,
36
22
  };
37
23
 
38
24
  function isPathLike(arg: string): boolean {
@@ -46,9 +32,7 @@ function isFalsey(v: string): boolean {
46
32
  return FALSEY_VALUES.has(v.trim().toLowerCase());
47
33
  }
48
34
 
49
- // Whether the process was launched with NO_COLOR already forced on. Captured
50
- // once at import (before any runCli) so re-enabling colors never clobbers an
51
- // externally-set NO_COLOR — we only clear the var on re-enable when WE set it.
35
+ // Captured at import so re-enabling colors never clobbers a user-exported NO_COLOR.
52
36
  const USER_FORCED_NO_COLOR =
53
37
  process.env.NO_COLOR !== undefined && !isFalsey(process.env.NO_COLOR);
54
38
 
@@ -105,47 +89,72 @@ export function resolveQuietMode(rawArgs: string[]): { quiet: boolean; rawArgs:
105
89
  return { quiet: value, rawArgs: cleaned };
106
90
  }
107
91
 
108
- export async function runCli(rawArgs = process.argv.slice(2)): Promise<void> {
109
- // Global flags resolved before citty so they apply to every command (and help
110
- // output) and never reach a subcommand's arg schema.
111
- const color = resolveColorMode(rawArgs);
112
- // Reset on EVERY invocation (not just the disable path): an earlier
113
- // --no-color / CI call must not leave colors permanently off for later
114
- // in-process calls (unit tests, `kesha mcp`). picocolors already honors
115
- // NO_COLOR at startup; setting the env var also propagates to the engine.
116
- setColorEnabled(!color.disableColor);
117
- // Keep NO_COLOR symmetric so it doesn't leak to engine subprocesses spawned
118
- // by a later in-process call: set it when WE disable, clear it on re-enable —
119
- // but never clear a NO_COLOR the user exported themselves.
120
- if (color.disableColor) {
92
+ /**
93
+ * Sync NO_COLOR with the resolved color decision so engine subprocesses inherit
94
+ * the right value. Separated from setColorEnabled so it can be tested without
95
+ * picocolors side-effects.
96
+ *
97
+ * Never clears a NO_COLOR the user exported before this process started
98
+ * (USER_FORCED_NO_COLOR guard).
99
+ */
100
+ export function applyColorEnv(disableColor: boolean): void {
101
+ if (disableColor) {
121
102
  process.env.NO_COLOR = "1";
122
103
  } else if (!USER_FORCED_NO_COLOR) {
123
104
  delete process.env.NO_COLOR;
124
105
  }
106
+ }
107
+
108
+ /**
109
+ * Classify the first positional arg for routing.
110
+ *
111
+ * - `"subcommand"` — exact match in the known subcommand set
112
+ * - `"unknown"` — bare token that looks like a typo (not a flag, not path-like)
113
+ * - `"main"` — flags, path-like args, or no arg (→ transcribe / help)
114
+ */
115
+ export function classifyFirstArg(
116
+ firstArg: string | undefined,
117
+ subcommandKeys: string[],
118
+ ): "subcommand" | "unknown" | "main" {
119
+ if (!firstArg) return "main";
120
+ if (subcommandKeys.includes(firstArg)) return "subcommand";
121
+ // Flags and path-like tokens fall through to the main transcribe command.
122
+ if (firstArg.startsWith("-") || isPathLike(firstArg)) return "main";
123
+ return "unknown";
124
+ }
125
+
126
+ export async function runCli(rawArgs = process.argv.slice(2)): Promise<void> {
127
+ // Global flags resolved before citty so they apply to every command and never reach a subcommand's arg schema.
128
+ const color = resolveColorMode(rawArgs);
129
+ // Reset on every invocation so an earlier --no-color/CI call doesn't leave colors off for later in-process calls (unit tests, `kesha mcp`).
130
+ setColorEnabled(!color.disableColor);
131
+ applyColorEnv(color.disableColor);
125
132
 
126
133
  const quiet = resolveQuietMode(color.rawArgs);
127
134
  log.quietEnabled = quiet.quiet;
128
135
 
129
136
  rawArgs = quiet.rawArgs;
130
137
  const [firstArg, ...restArgs] = rawArgs;
138
+ const subcommandKeys = Object.keys(SUBCOMMANDS);
131
139
 
132
- if (firstArg && Object.hasOwn(SUBCOMMANDS, firstArg)) {
133
- await runMain(SUBCOMMANDS[firstArg], { rawArgs: restArgs });
134
- return;
135
- }
140
+ switch (classifyFirstArg(firstArg, subcommandKeys)) {
141
+ case "subcommand":
142
+ await runMain(await SUBCOMMANDS[firstArg!]!(), { rawArgs: restArgs });
143
+ return;
136
144
 
137
- // Check for unknown subcommands (non-flag, non-file-path args).
138
- // Extensionless existing files remain valid transcription inputs; missing
139
- // bare tokens are more likely command typos and should not start the engine.
140
- if (firstArg && !firstArg.startsWith("-") && !isPathLike(firstArg)) {
141
- const suggestion = suggestCommand(firstArg, Object.keys(SUBCOMMANDS));
142
- log.error(`unknown command '${firstArg}'`);
143
- if (suggestion && suggestion !== firstArg) {
144
- log.warn(`(Did you mean ${suggestion}?)`);
145
+ case "unknown": {
146
+ // Extensionless existing files are valid transcription inputs; bare non-path tokens are likely command typos.
147
+ const suggestion = suggestCommand(firstArg!, subcommandKeys);
148
+ log.error(`unknown command '${firstArg}'`);
149
+ if (suggestion && suggestion !== firstArg) {
150
+ log.warn(`(Did you mean ${suggestion}?)`);
151
+ }
152
+ log.warn(`If this is an audio file, pass a path like './${firstArg}'.`);
153
+ process.exit(1);
154
+ break;
145
155
  }
146
- log.warn(`If this is an audio file, pass a path like './${firstArg}'.`);
147
- process.exit(1);
148
- }
149
156
 
150
- await runMain(mainCommand, { rawArgs });
157
+ default:
158
+ await runMain((await import("./main")).mainCommand, { rawArgs });
159
+ }
151
160
  }
package/src/cli/init.ts CHANGED
@@ -5,12 +5,8 @@ import { multiselect, isCancel, cancel } from "@clack/prompts";
5
5
  import { renderInstallPlan } from "../install-plan";
6
6
  import { log } from "../log";
7
7
  import { getEngineCapabilities } from "../engine";
8
- import {
9
- performInstall,
10
- resolveBackendFlag,
11
- resolveNoCacheFlag,
12
- TTS_LANG_FALLBACK,
13
- } from "./install";
8
+ import { performInstall, resolveBackendFlag, resolveNoCacheFlag, TTS_LANG_FALLBACK } from "./install";
9
+ import type { SharedInstallArgs } from "./types";
14
10
 
15
11
  const TTS_LANG_LABELS: Record<string, string> = {
16
12
  en: "English (Kokoro)",
@@ -44,16 +40,7 @@ async function promptTtsLangs(preselect: string[]): Promise<string[]> {
44
40
  return selected as string[];
45
41
  }
46
42
 
47
- export interface InitCommandArgs {
48
- coreml: boolean;
49
- onnx: boolean;
50
- "no-cache": boolean;
51
- noCache?: boolean;
52
- no_cache?: boolean;
53
- tts: boolean;
54
- vad: boolean;
55
- diarize: boolean;
56
- plan: boolean;
43
+ export interface InitCommandArgs extends SharedInstallArgs {
57
44
  yes: boolean;
58
45
  }
59
46
 
@@ -7,19 +7,11 @@ import { maybeAskForStar } from "../star";
7
7
  import { log } from "../log";
8
8
  import { packageVersion } from "../package-info";
9
9
  import { createDiagnosticLogSession, type DiagnosticLogSession } from "../diagnostic-log";
10
+ import type { SharedInstallArgs } from "./types";
10
11
 
11
- export interface InstallCommandArgs {
12
+ export interface InstallCommandArgs extends SharedInstallArgs {
12
13
  /** Positional args after `install` — candidate TTS language codes. */
13
14
  _?: string[];
14
- coreml: boolean;
15
- onnx: boolean;
16
- "no-cache": boolean;
17
- noCache?: boolean;
18
- no_cache?: boolean;
19
- tts: boolean;
20
- vad: boolean;
21
- diarize: boolean;
22
- plan: boolean;
23
15
  }
24
16
 
25
17
  /**
@@ -30,9 +22,7 @@ export interface InstallCommandArgs {
30
22
  export const TTS_LANG_FALLBACK = ["en", "es", "fr", "it", "pt", "ru"];
31
23
 
32
24
  export interface TtsArgInput {
33
- /** Whether --tts was passed. */
34
25
  tts: boolean;
35
- /** Positional args after the install command (candidate language codes). */
36
26
  positionals: string[];
37
27
  }
38
28