egaki 0.5.0 → 0.7.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.
Files changed (280) hide show
  1. package/CHANGELOG.md +188 -0
  2. package/dist/cli/cache-utils.d.ts +33 -0
  3. package/dist/cli/cache-utils.d.ts.map +1 -0
  4. package/dist/cli/cache-utils.js +157 -0
  5. package/dist/cli/cached-generate.d.ts +105 -0
  6. package/dist/cli/cached-generate.d.ts.map +1 -0
  7. package/dist/cli/cached-generate.js +208 -0
  8. package/dist/cli/cached-generate.test.d.ts +11 -0
  9. package/dist/cli/cached-generate.test.d.ts.map +1 -0
  10. package/dist/cli/cached-generate.test.js +431 -0
  11. package/dist/cli/cartesia-provider.d.ts +16 -0
  12. package/dist/cli/cartesia-provider.d.ts.map +1 -0
  13. package/dist/cli/cartesia-provider.js +184 -0
  14. package/dist/cli/chatgpt-auth.d.ts.map +1 -0
  15. package/dist/cli/cli.d.ts +4 -0
  16. package/dist/cli/cli.d.ts.map +1 -0
  17. package/dist/cli/cli.js +1329 -0
  18. package/dist/cli/clone-generate.d.ts +26 -0
  19. package/dist/cli/clone-generate.d.ts.map +1 -0
  20. package/dist/cli/clone-generate.js +148 -0
  21. package/dist/{credentials.d.ts → cli/credentials.d.ts} +11 -2
  22. package/dist/cli/credentials.d.ts.map +1 -0
  23. package/dist/{credentials.js → cli/credentials.js} +88 -5
  24. package/dist/cli/elevenlabs-speech-provider.d.ts +26 -0
  25. package/dist/cli/elevenlabs-speech-provider.d.ts.map +1 -0
  26. package/dist/cli/elevenlabs-speech-provider.js +199 -0
  27. package/dist/cli/generate.d.ts +140 -0
  28. package/dist/cli/generate.d.ts.map +1 -0
  29. package/dist/cli/generate.js +604 -0
  30. package/dist/cli/generate.test.d.ts +2 -0
  31. package/dist/cli/generate.test.d.ts.map +1 -0
  32. package/dist/cli/generate.test.js +131 -0
  33. package/dist/cli/login.d.ts.map +1 -0
  34. package/dist/{login.js → cli/login.js} +45 -4
  35. package/dist/cli/main.d.ts +3 -0
  36. package/dist/cli/main.d.ts.map +1 -0
  37. package/dist/cli/main.js +7 -0
  38. package/dist/cli/model-catalog.d.ts +141 -0
  39. package/dist/cli/model-catalog.d.ts.map +1 -0
  40. package/dist/cli/model-catalog.js +2135 -0
  41. package/dist/{models.d.ts → cli/models.d.ts} +16 -9
  42. package/dist/cli/models.d.ts.map +1 -0
  43. package/dist/cli/models.js +313 -0
  44. package/dist/cli/open-browser.d.ts.map +1 -0
  45. package/dist/cli/openai-speech-provider.d.ts +3 -0
  46. package/dist/cli/openai-speech-provider.d.ts.map +1 -0
  47. package/dist/cli/openai-speech-provider.js +28 -0
  48. package/dist/cli/separation-generate.d.ts +34 -0
  49. package/dist/cli/separation-generate.d.ts.map +1 -0
  50. package/dist/cli/separation-generate.js +117 -0
  51. package/dist/cli/speech-catalog.d.ts +35 -0
  52. package/dist/cli/speech-catalog.d.ts.map +1 -0
  53. package/dist/cli/speech-catalog.js +195 -0
  54. package/dist/cli/speech-generate.d.ts +86 -0
  55. package/dist/cli/speech-generate.d.ts.map +1 -0
  56. package/dist/cli/speech-generate.js +156 -0
  57. package/dist/cli/speech-models.d.ts +8 -0
  58. package/dist/cli/speech-models.d.ts.map +1 -0
  59. package/dist/cli/speech-models.js +109 -0
  60. package/dist/cli/speech-provider.test.d.ts +2 -0
  61. package/dist/cli/speech-provider.test.d.ts.map +1 -0
  62. package/dist/cli/speech-provider.test.js +197 -0
  63. package/dist/cli/subscription.d.ts.map +1 -0
  64. package/dist/cli/transcription-catalog.d.ts +35 -0
  65. package/dist/cli/transcription-catalog.d.ts.map +1 -0
  66. package/dist/cli/transcription-catalog.js +192 -0
  67. package/dist/cli/transcription-generate.d.ts +62 -0
  68. package/dist/cli/transcription-generate.d.ts.map +1 -0
  69. package/dist/cli/transcription-generate.js +144 -0
  70. package/dist/cli/transcription-models.d.ts +9 -0
  71. package/dist/cli/transcription-models.d.ts.map +1 -0
  72. package/dist/cli/transcription-models.js +100 -0
  73. package/dist/cli/transcription-providers.d.ts +22 -0
  74. package/dist/cli/transcription-providers.d.ts.map +1 -0
  75. package/dist/cli/transcription-providers.js +290 -0
  76. package/dist/cli/video-model-catalog.d.ts +2 -0
  77. package/dist/cli/video-model-catalog.d.ts.map +1 -0
  78. package/dist/cli/video-model-catalog.js +5 -0
  79. package/dist/cli/xai-auth.d.ts +29 -0
  80. package/dist/cli/xai-auth.d.ts.map +1 -0
  81. package/dist/cli/xai-auth.js +325 -0
  82. package/dist/vite/angled-screen.d.ts +57 -0
  83. package/dist/vite/angled-screen.d.ts.map +1 -0
  84. package/dist/vite/angled-screen.js +115 -0
  85. package/dist/vite/app.d.ts +51 -0
  86. package/dist/vite/app.d.ts.map +1 -0
  87. package/dist/vite/app.js +150 -0
  88. package/dist/vite/bands-shader.d.ts +47 -0
  89. package/dist/vite/bands-shader.d.ts.map +1 -0
  90. package/dist/vite/bands-shader.js +267 -0
  91. package/dist/vite/code-block.d.ts +107 -0
  92. package/dist/vite/code-block.d.ts.map +1 -0
  93. package/dist/vite/code-block.js +641 -0
  94. package/dist/vite/components.d.ts +36 -0
  95. package/dist/vite/components.d.ts.map +1 -0
  96. package/dist/vite/components.js +113 -0
  97. package/dist/vite/dispersion-rings-shader.d.ts +44 -0
  98. package/dist/vite/dispersion-rings-shader.d.ts.map +1 -0
  99. package/dist/vite/dispersion-rings-shader.js +282 -0
  100. package/dist/vite/easing-curves.d.ts +129 -0
  101. package/dist/vite/easing-curves.d.ts.map +1 -0
  102. package/dist/vite/easing-curves.js +584 -0
  103. package/dist/vite/easing-curves.test.d.ts +2 -0
  104. package/dist/vite/easing-curves.test.d.ts.map +1 -0
  105. package/dist/vite/easing-curves.test.js +200 -0
  106. package/dist/vite/generated-media-client.d.ts +35 -0
  107. package/dist/vite/generated-media-client.d.ts.map +1 -0
  108. package/dist/vite/generated-media-client.js +76 -0
  109. package/dist/vite/keyframes.d.ts +129 -0
  110. package/dist/vite/keyframes.d.ts.map +1 -0
  111. package/dist/vite/keyframes.js +194 -0
  112. package/dist/vite/layout-editor.d.ts +45 -0
  113. package/dist/vite/layout-editor.d.ts.map +1 -0
  114. package/dist/vite/layout-editor.js +587 -0
  115. package/dist/vite/layout-transition.d.ts +136 -0
  116. package/dist/vite/layout-transition.d.ts.map +1 -0
  117. package/dist/vite/layout-transition.js +581 -0
  118. package/dist/vite/liquid-gradient-shader.d.ts +62 -0
  119. package/dist/vite/liquid-gradient-shader.d.ts.map +1 -0
  120. package/dist/vite/liquid-gradient-shader.js +413 -0
  121. package/dist/vite/mdx-client.d.ts +15 -0
  122. package/dist/vite/mdx-client.d.ts.map +1 -0
  123. package/dist/vite/mdx-client.js +265 -0
  124. package/dist/vite/mdx-parse.d.ts +102 -0
  125. package/dist/vite/mdx-parse.d.ts.map +1 -0
  126. package/dist/vite/mdx-parse.js +251 -0
  127. package/dist/vite/mdx-provided-components.d.ts +18 -0
  128. package/dist/vite/mdx-provided-components.d.ts.map +1 -0
  129. package/dist/vite/mdx-provided-components.js +14 -0
  130. package/dist/vite/mdx-video.d.ts +327 -0
  131. package/dist/vite/mdx-video.d.ts.map +1 -0
  132. package/dist/vite/mdx-video.js +596 -0
  133. package/dist/vite/mdx-video.test.d.ts +9 -0
  134. package/dist/vite/mdx-video.test.d.ts.map +1 -0
  135. package/dist/vite/mdx-video.test.js +1945 -0
  136. package/dist/vite/media-components.d.ts +45 -0
  137. package/dist/vite/media-components.d.ts.map +1 -0
  138. package/dist/vite/media-components.js +355 -0
  139. package/dist/vite/media-duration-store.d.ts +66 -0
  140. package/dist/vite/media-duration-store.d.ts.map +1 -0
  141. package/dist/vite/media-duration-store.js +204 -0
  142. package/dist/vite/player-page.d.ts +35 -0
  143. package/dist/vite/player-page.d.ts.map +1 -0
  144. package/dist/vite/player-page.js +720 -0
  145. package/dist/vite/render-client.d.ts +18 -0
  146. package/dist/vite/render-client.d.ts.map +1 -0
  147. package/dist/vite/render-client.js +67 -0
  148. package/dist/vite/sdk.d.ts +221 -0
  149. package/dist/vite/sdk.d.ts.map +1 -0
  150. package/dist/vite/sdk.js +355 -0
  151. package/dist/vite/server-components.d.ts +55 -0
  152. package/dist/vite/server-components.d.ts.map +1 -0
  153. package/dist/vite/server-components.js +156 -0
  154. package/dist/vite/server-mdx.d.ts +74 -0
  155. package/dist/vite/server-mdx.d.ts.map +1 -0
  156. package/dist/vite/server-mdx.js +259 -0
  157. package/dist/vite/shader-renderer.d.ts +48 -0
  158. package/dist/vite/shader-renderer.d.ts.map +1 -0
  159. package/dist/vite/shader-renderer.js +411 -0
  160. package/dist/vite/store-hooks.d.ts +12 -0
  161. package/dist/vite/store-hooks.d.ts.map +1 -0
  162. package/dist/vite/store-hooks.js +93 -0
  163. package/dist/vite/store.d.ts +75 -0
  164. package/dist/vite/store.d.ts.map +1 -0
  165. package/dist/vite/store.js +52 -0
  166. package/dist/vite/store.test.d.ts +6 -0
  167. package/dist/vite/store.test.d.ts.map +1 -0
  168. package/dist/vite/store.test.js +172 -0
  169. package/dist/vite/tweakpane-hook.d.ts +94 -0
  170. package/dist/vite/tweakpane-hook.d.ts.map +1 -0
  171. package/dist/vite/tweakpane-hook.js +450 -0
  172. package/dist/vite/vite-plugin.d.ts +28 -0
  173. package/dist/vite/vite-plugin.d.ts.map +1 -0
  174. package/dist/vite/vite-plugin.js +538 -0
  175. package/dist/vite/wave-gradient-shader.d.ts +41 -0
  176. package/dist/vite/wave-gradient-shader.d.ts.map +1 -0
  177. package/dist/vite/wave-gradient-shader.js +207 -0
  178. package/package.json +103 -25
  179. package/skills/egaki/SKILL.md +162 -1
  180. package/src/cli/cache-utils.ts +169 -0
  181. package/src/cli/cached-generate.test.ts +531 -0
  182. package/src/cli/cached-generate.ts +348 -0
  183. package/src/cli/cartesia-provider.ts +249 -0
  184. package/src/cli/cli.ts +1818 -0
  185. package/src/cli/clone-generate.ts +202 -0
  186. package/src/{credentials.ts → cli/credentials.ts} +90 -7
  187. package/src/cli/elevenlabs-speech-provider.ts +243 -0
  188. package/src/cli/generate.test.ts +166 -0
  189. package/src/cli/generate.ts +912 -0
  190. package/src/{login.ts → cli/login.ts} +48 -3
  191. package/src/cli/main.ts +8 -0
  192. package/src/cli/model-catalog.ts +2336 -0
  193. package/src/cli/models.ts +408 -0
  194. package/src/cli/openai-speech-provider.ts +34 -0
  195. package/src/cli/separation-generate.ts +187 -0
  196. package/src/cli/speech-catalog.ts +253 -0
  197. package/src/cli/speech-generate.ts +275 -0
  198. package/src/cli/speech-models.ts +146 -0
  199. package/src/cli/speech-provider.test.ts +226 -0
  200. package/src/cli/transcription-catalog.ts +250 -0
  201. package/src/cli/transcription-generate.ts +214 -0
  202. package/src/cli/transcription-models.ts +136 -0
  203. package/src/cli/transcription-providers.ts +427 -0
  204. package/src/cli/video-model-catalog.ts +14 -0
  205. package/src/cli/xai-auth.ts +396 -0
  206. package/src/vite/angled-screen.tsx +214 -0
  207. package/src/vite/app.tsx +197 -0
  208. package/src/vite/bands-shader.tsx +319 -0
  209. package/src/vite/code-block.tsx +939 -0
  210. package/src/vite/components.tsx +278 -0
  211. package/src/vite/dispersion-rings-shader.tsx +331 -0
  212. package/src/vite/easing-curves.test.ts +232 -0
  213. package/src/vite/easing-curves.ts +743 -0
  214. package/src/vite/generated-media-client.tsx +121 -0
  215. package/src/vite/keyframes.tsx +347 -0
  216. package/src/vite/layout-editor.tsx +709 -0
  217. package/src/vite/layout-transition.tsx +706 -0
  218. package/src/vite/liquid-gradient-shader.tsx +474 -0
  219. package/src/vite/mdx-client.tsx +420 -0
  220. package/src/vite/mdx-parse.ts +343 -0
  221. package/src/vite/mdx-provided-components.ts +19 -0
  222. package/src/vite/mdx-video.test.tsx +2219 -0
  223. package/src/vite/mdx-video.tsx +976 -0
  224. package/src/vite/media-components.tsx +473 -0
  225. package/src/vite/media-duration-store.ts +225 -0
  226. package/src/vite/motion-timing.ts +194 -0
  227. package/src/vite/player-page.tsx +1107 -0
  228. package/src/vite/render-client.ts +84 -0
  229. package/src/vite/sdk.ts +598 -0
  230. package/src/vite/server-components.tsx +241 -0
  231. package/src/vite/server-mdx.ts +285 -0
  232. package/src/vite/shader-renderer.tsx +535 -0
  233. package/src/vite/store-hooks.ts +117 -0
  234. package/src/vite/store.test.ts +202 -0
  235. package/src/vite/store.ts +115 -0
  236. package/src/vite/styles.css +21 -0
  237. package/src/vite/tweakpane-hook.tsx +593 -0
  238. package/src/vite/virtual-modules.d.ts +30 -0
  239. package/src/vite/vite-plugin.ts +584 -0
  240. package/src/vite/wave-gradient-shader.tsx +253 -0
  241. package/README.md +0 -266
  242. package/dist/antigravity-auth.d.ts +0 -15
  243. package/dist/antigravity-auth.d.ts.map +0 -1
  244. package/dist/antigravity-auth.js +0 -544
  245. package/dist/antigravity-image.d.ts +0 -11
  246. package/dist/antigravity-image.d.ts.map +0 -1
  247. package/dist/antigravity-image.js +0 -285
  248. package/dist/chatgpt-auth.d.ts.map +0 -1
  249. package/dist/cli.d.ts +0 -3
  250. package/dist/cli.d.ts.map +0 -1
  251. package/dist/cli.js +0 -968
  252. package/dist/credentials.d.ts.map +0 -1
  253. package/dist/gateway-model-candidates.generated.d.ts +0 -11
  254. package/dist/gateway-model-candidates.generated.d.ts.map +0 -1
  255. package/dist/gateway-model-candidates.generated.js +0 -64
  256. package/dist/login.d.ts.map +0 -1
  257. package/dist/model-catalog.d.ts +0 -42
  258. package/dist/model-catalog.d.ts.map +0 -1
  259. package/dist/model-catalog.js +0 -1001
  260. package/dist/models.d.ts.map +0 -1
  261. package/dist/models.js +0 -246
  262. package/dist/open-browser.d.ts.map +0 -1
  263. package/dist/subscription.d.ts.map +0 -1
  264. package/dist/video-model-catalog.d.ts +0 -52
  265. package/dist/video-model-catalog.d.ts.map +0 -1
  266. package/dist/video-model-catalog.js +0 -306
  267. package/src/cli.ts +0 -1378
  268. package/src/model-catalog.ts +0 -1077
  269. package/src/models.ts +0 -309
  270. package/src/video-model-catalog.ts +0 -363
  271. /package/dist/{chatgpt-auth.d.ts → cli/chatgpt-auth.d.ts} +0 -0
  272. /package/dist/{chatgpt-auth.js → cli/chatgpt-auth.js} +0 -0
  273. /package/dist/{login.d.ts → cli/login.d.ts} +0 -0
  274. /package/dist/{open-browser.d.ts → cli/open-browser.d.ts} +0 -0
  275. /package/dist/{open-browser.js → cli/open-browser.js} +0 -0
  276. /package/dist/{subscription.d.ts → cli/subscription.d.ts} +0 -0
  277. /package/dist/{subscription.js → cli/subscription.js} +0 -0
  278. /package/src/{chatgpt-auth.ts → cli/chatgpt-auth.ts} +0 -0
  279. /package/src/{open-browser.ts → cli/open-browser.ts} +0 -0
  280. /package/src/{subscription.ts → cli/subscription.ts} +0 -0
@@ -0,0 +1,253 @@
1
+ // Speech model catalog — single source of truth for all TTS models.
2
+ // Mirrors the image/video catalog pattern: every model's provider, pricing,
3
+ // features, and default voices live here. Other modules derive their data
4
+ // from this catalog.
5
+ //
6
+ // Pricing sources:
7
+ // OpenAI: https://platform.openai.com/docs/pricing (per 1M characters)
8
+ // ElevenLabs: https://elevenlabs.io/pricing (per 1K characters, varies by plan)
9
+ // Cartesia: https://cartesia.ai/pricing (plan-based, ~1 credit per character)
10
+
11
+ import type { ProviderOption } from './model-catalog.js'
12
+
13
+ // ─── cost types ──────────────────────────────────────────────────────────────
14
+
15
+ export type PerCharacterCost = {
16
+ type: 'per-character'
17
+ /** USD per 1 million characters */
18
+ perMillionChars: number
19
+ }
20
+
21
+ export type SpeechModelCost = PerCharacterCost
22
+
23
+ // ─── feature types ───────────────────────────────────────────────────────────
24
+
25
+ export type SpeechModelFeatures = {
26
+ /** Supported output audio formats */
27
+ outputFormats: string[]
28
+ /** Whether the model supports voice instructions / style prompts */
29
+ instructions: boolean
30
+ /** Whether speed control is supported */
31
+ speed: boolean
32
+ /** Whether language selection is supported */
33
+ language: boolean
34
+ /** Max input characters per request (0 = unknown/unlimited) */
35
+ maxChars: number
36
+ /** Built-in voice names (not exhaustive for providers with voice libraries) */
37
+ defaultVoices: string[]
38
+ }
39
+
40
+ // ─── entry type ──────────────────────────────────────────────────────────────
41
+
42
+ export type SpeechModelEntry = {
43
+ id: string
44
+ name: string
45
+ description?: string
46
+ provider: string
47
+ strategy: 'speech'
48
+ released: string
49
+ cost: SpeechModelCost
50
+ features: SpeechModelFeatures
51
+ providerOptions?: ProviderOption[]
52
+ }
53
+
54
+ // ─── shared fragments ────────────────────────────────────────────────────────
55
+
56
+ const openaiSpeechBase = {
57
+ provider: 'openai',
58
+ strategy: 'speech' as const,
59
+ }
60
+
61
+ const elevenlabsSpeechBase = {
62
+ provider: 'elevenlabs',
63
+ strategy: 'speech' as const,
64
+ }
65
+
66
+ const cartesiaSpeechBase = {
67
+ provider: 'cartesia',
68
+ strategy: 'speech' as const,
69
+ }
70
+
71
+ // ─── catalog ─────────────────────────────────────────────────────────────────
72
+
73
+ export const SPEECH_CATALOG: SpeechModelEntry[] = [
74
+ // ── OpenAI ─────────────────────────────────────────────────────────────
75
+ {
76
+ id: 'tts-1',
77
+ name: 'TTS-1 (Standard)',
78
+ description: 'OpenAI standard text-to-speech. Low latency, good for real-time use.',
79
+ ...openaiSpeechBase,
80
+ released: '2023-11',
81
+ cost: { type: 'per-character', perMillionChars: 15 },
82
+ features: {
83
+ outputFormats: ['mp3', 'opus', 'aac', 'flac', 'wav', 'pcm'],
84
+ instructions: false,
85
+ speed: true,
86
+ language: false,
87
+ maxChars: 4096,
88
+ defaultVoices: ['alloy', 'ash', 'coral', 'echo', 'fable', 'onyx', 'nova', 'sage', 'shimmer'],
89
+ },
90
+ },
91
+ {
92
+ id: 'tts-1-hd',
93
+ name: 'TTS-1 HD',
94
+ description: 'OpenAI high-definition text-to-speech. Higher fidelity audio.',
95
+ ...openaiSpeechBase,
96
+ released: '2023-11',
97
+ cost: { type: 'per-character', perMillionChars: 30 },
98
+ features: {
99
+ outputFormats: ['mp3', 'opus', 'aac', 'flac', 'wav', 'pcm'],
100
+ instructions: false,
101
+ speed: true,
102
+ language: false,
103
+ maxChars: 4096,
104
+ defaultVoices: ['alloy', 'ash', 'coral', 'echo', 'fable', 'onyx', 'nova', 'sage', 'shimmer'],
105
+ },
106
+ },
107
+ {
108
+ id: 'gpt-4o-mini-tts',
109
+ name: 'GPT-4o Mini TTS',
110
+ description:
111
+ 'OpenAI GPT-4o Mini with text-to-speech. Supports style instructions ' +
112
+ 'for expressive speech control.',
113
+ ...openaiSpeechBase,
114
+ released: '2025-03',
115
+ cost: { type: 'per-character', perMillionChars: 12 },
116
+ features: {
117
+ outputFormats: ['mp3', 'opus', 'aac', 'flac', 'wav', 'pcm'],
118
+ instructions: true,
119
+ speed: true,
120
+ language: false,
121
+ maxChars: 2000,
122
+ defaultVoices: [
123
+ 'alloy', 'ash', 'ballad', 'cedar', 'coral', 'echo',
124
+ 'fable', 'marin', 'nova', 'onyx', 'sage', 'shimmer', 'verse',
125
+ ],
126
+ },
127
+ },
128
+
129
+ // ── ElevenLabs ─────────────────────────────────────────────────────────
130
+ {
131
+ id: 'eleven_v3',
132
+ name: 'ElevenLabs v3',
133
+ description: 'Latest ElevenLabs model with best quality and expressiveness.',
134
+ ...elevenlabsSpeechBase,
135
+ released: '2025-06',
136
+ cost: { type: 'per-character', perMillionChars: 100 },
137
+ features: {
138
+ outputFormats: ['mp3_44100_128', 'mp3_22050_32', 'pcm_16000', 'pcm_22050', 'pcm_24000', 'pcm_44100', 'ulaw_8000'],
139
+ instructions: false,
140
+ speed: false,
141
+ language: true,
142
+ maxChars: 0,
143
+ defaultVoices: [],
144
+ },
145
+ },
146
+ {
147
+ id: 'eleven_multilingual_v2',
148
+ name: 'ElevenLabs Multilingual v2',
149
+ description: 'ElevenLabs multilingual model. Supports 29 languages.',
150
+ ...elevenlabsSpeechBase,
151
+ released: '2024-01',
152
+ cost: { type: 'per-character', perMillionChars: 100 },
153
+ features: {
154
+ outputFormats: ['mp3_44100_128', 'mp3_22050_32', 'pcm_16000', 'pcm_22050', 'pcm_24000', 'pcm_44100', 'ulaw_8000'],
155
+ instructions: false,
156
+ speed: false,
157
+ language: true,
158
+ maxChars: 0,
159
+ defaultVoices: [],
160
+ },
161
+ },
162
+ {
163
+ id: 'eleven_flash_v2_5',
164
+ name: 'ElevenLabs Flash v2.5',
165
+ description: 'ElevenLabs low-latency model optimized for speed.',
166
+ ...elevenlabsSpeechBase,
167
+ released: '2024-08',
168
+ cost: { type: 'per-character', perMillionChars: 50 },
169
+ features: {
170
+ outputFormats: ['mp3_44100_128', 'mp3_22050_32', 'pcm_16000', 'pcm_22050', 'pcm_24000', 'pcm_44100', 'ulaw_8000'],
171
+ instructions: false,
172
+ speed: false,
173
+ language: true,
174
+ maxChars: 0,
175
+ defaultVoices: [],
176
+ },
177
+ },
178
+ {
179
+ id: 'eleven_turbo_v2_5',
180
+ name: 'ElevenLabs Turbo v2.5',
181
+ description: 'ElevenLabs turbo model. Balanced quality and speed.',
182
+ ...elevenlabsSpeechBase,
183
+ released: '2024-06',
184
+ cost: { type: 'per-character', perMillionChars: 50 },
185
+ features: {
186
+ outputFormats: ['mp3_44100_128', 'mp3_22050_32', 'pcm_16000', 'pcm_22050', 'pcm_24000', 'pcm_44100', 'ulaw_8000'],
187
+ instructions: false,
188
+ speed: false,
189
+ language: true,
190
+ maxChars: 0,
191
+ defaultVoices: [],
192
+ },
193
+ },
194
+
195
+
196
+ // ── Cartesia ───────────────────────────────────────────────────────────
197
+ // Pricing is plan-based (~1 credit per character). The per-million rate
198
+ // below is estimated from the Startup plan ($49/1.25M credits ≈ $39/M).
199
+ // See https://cartesia.ai/pricing for current plans.
200
+ {
201
+ id: 'sonic-3.5',
202
+ name: 'Cartesia Sonic 3.5',
203
+ description:
204
+ 'Fastest, most natural Cartesia TTS. #1 for naturalness, sub-90ms latency, ' +
205
+ '42 languages. Recommended for production.',
206
+ ...cartesiaSpeechBase,
207
+ released: '2026-05',
208
+ cost: { type: 'per-character', perMillionChars: 39 },
209
+ features: {
210
+ outputFormats: ['mp3', 'wav', 'raw'],
211
+ instructions: false,
212
+ speed: true,
213
+ language: true,
214
+ maxChars: 0,
215
+ defaultVoices: [
216
+ 'f786b574-daa5-4673-aa0c-cbe3e8534c02', // Katie (en-US Female)
217
+ 'db6b0ed5-d5d3-463d-ae85-518a07d3c2b4', // Skylar (en-US Female)
218
+ 'a5136bf9-224c-4d76-b823-52bd5efcffcc', // Jameson (en-US Male)
219
+ '62ae83ad-4f6a-430b-af41-a9bede9286ca', // Gemma (en-GB Female)
220
+ 'ef191366-f52f-447a-a398-ed8c0f2943a1', // Archie (en-GB Male)
221
+ ],
222
+ },
223
+ },
224
+ {
225
+ id: 'sonic-3',
226
+ name: 'Cartesia Sonic 3',
227
+ description:
228
+ 'Previous generation Cartesia TTS. Still high quality with pronunciation ' +
229
+ 'dictionaries support.',
230
+ ...cartesiaSpeechBase,
231
+ released: '2025-01',
232
+ cost: { type: 'per-character', perMillionChars: 39 },
233
+ features: {
234
+ outputFormats: ['mp3', 'wav', 'raw'],
235
+ instructions: false,
236
+ speed: true,
237
+ language: true,
238
+ maxChars: 0,
239
+ defaultVoices: [
240
+ 'f786b574-daa5-4673-aa0c-cbe3e8534c02', // Katie (en-US Female)
241
+ 'db6b0ed5-d5d3-463d-ae85-518a07d3c2b4', // Skylar (en-US Female)
242
+ 'a5136bf9-224c-4d76-b823-52bd5efcffcc', // Jameson (en-US Male)
243
+ ],
244
+ },
245
+ },
246
+
247
+ ]
248
+
249
+ // ─── lookup helpers ──────────────────────────────────────────────────────────
250
+
251
+ export function findSpeechModel(modelId: string): SpeechModelEntry | undefined {
252
+ return SPEECH_CATALOG.find((m) => m.id === modelId)
253
+ }
@@ -0,0 +1,275 @@
1
+ // Programmatic API for egaki speech generation.
2
+ // Returns Error | Result (errore style) instead of process.exit/console.error.
3
+ //
4
+ // Two variants:
5
+ // - `generateSpeech(opts)` — cached, writes to public/generated/audio/, returns { src }
6
+ // - `generateSpeechUncached(opts)` — raw, returns bytes in GenerateSpeechResult
7
+ //
8
+ // Uses our own SpeechProvider interface instead of the Vercel AI SDK's
9
+ // experimental_generateSpeech. This lets providers return word-level timestamps
10
+ // alongside audio (ElevenLabs /with-timestamps, Cartesia /tts/sse).
11
+ //
12
+ // Usage:
13
+ // import { generateSpeech, generateSpeechUncached } from 'egaki/generate'
14
+ // const cached = await generateSpeech({ text: 'Hello world' })
15
+ // if (!(cached instanceof Error)) cached.src // '/generated/audio/hello-world-a1b2c3d4.mp3'
16
+ // const raw = await generateSpeechUncached({ text: 'Hello world' })
17
+ // if (!(raw instanceof Error)) raw.audio.uint8Array // raw bytes
18
+ // if (raw.timestamps) raw.timestamps[0] // { word: 'Hello', startSecond: 0, endSecond: 0.4 }
19
+ import { injectCredentialsToEnv } from './credentials.js'
20
+ import {
21
+ getSpeechModelConfig,
22
+ getSpeechProvider,
23
+ DEFAULT_SPEECH_MODEL,
24
+ } from './speech-models.js'
25
+ import type { GeneratedFile } from './generate.js'
26
+ import type { WordTimestamp } from './transcription-generate.js'
27
+
28
+ export type { WordTimestamp }
29
+
30
+ // ─── SpeechProvider interface ────────────────────────────────────────────────
31
+ // Each provider implements this to generate audio + optional word timestamps.
32
+
33
+ export interface SpeechProviderResult {
34
+ audio: Uint8Array
35
+ mediaType: string
36
+ /** Word-level timestamps, if the provider supports them. */
37
+ timestamps?: WordTimestamp[]
38
+ }
39
+
40
+ export interface SpeechProviderOptions {
41
+ text: string
42
+ modelId: string
43
+ voice?: string
44
+ outputFormat?: string
45
+ instructions?: string
46
+ speed?: number
47
+ language?: string
48
+ abortSignal?: AbortSignal
49
+ }
50
+
51
+ export interface SpeechProvider {
52
+ generate(options: SpeechProviderOptions): Promise<SpeechProviderResult>
53
+ }
54
+
55
+ // ─── autocomplete-friendly union types ───────────────────────────────────────
56
+
57
+ /** Speech model IDs from the catalog. Accepts arbitrary strings too. */
58
+ export type SpeechModelId =
59
+ | 'tts-1' | 'tts-1-hd' | 'gpt-4o-mini-tts'
60
+ | 'eleven_v3' | 'eleven_multilingual_v2' | 'eleven_flash_v2_5' | 'eleven_turbo_v2_5'
61
+ | 'sonic-3.5' | 'sonic-3'
62
+ | (string & {})
63
+
64
+ /** Built-in voice presets across providers. Accepts arbitrary strings too. */
65
+ export type SpeechVoice =
66
+ | 'alloy' | 'ash' | 'coral' | 'echo' | 'fable' | 'onyx' | 'nova' | 'sage' | 'shimmer' | 'ballad' | 'cedar' | 'marin' | 'verse'
67
+ | (string & {})
68
+
69
+ /** Audio output formats. */
70
+ export type AudioOutputFormat = 'mp3' | 'opus' | 'aac' | 'flac' | 'wav' | 'pcm' | 'raw' | (string & {})
71
+
72
+ // ─── public types ────────────────────────────────────────────────────────────
73
+
74
+ export interface GenerateSpeechOptions {
75
+ /** The text to convert to speech. */
76
+ text: string
77
+ /** Speech model ID. Defaults to DEFAULT_SPEECH_MODEL ('tts-1') if omitted. */
78
+ model?: SpeechModelId
79
+ /** Voice ID or name (provider-specific). */
80
+ voice?: SpeechVoice
81
+ /** Output audio format: mp3, wav, pcm, opus, aac, flac, etc. */
82
+ outputFormat?: AudioOutputFormat
83
+ /** Style instructions for models that support it (e.g. gpt-4o-mini-tts). */
84
+ instructions?: string
85
+ /**
86
+ * Playback speed multiplier. `1.0` is normal speed; values below slow
87
+ * down, values above speed up.
88
+ *
89
+ * Accepted ranges vary by provider:
90
+ *
91
+ * | Provider | Range | Notes |
92
+ * |-------------|-------------|--------------------------------------------------------------|
93
+ * | OpenAI | 0.25 – 4.0 | Works with `tts-1`, `tts-1-hd`, `gpt-4o-mini-tts`. |
94
+ * | ElevenLabs | 0.25 – 4.0 | Passed as `voice_settings.speed`. Extreme values degrade |
95
+ * | | | quality. Flash/turbo models may ignore it. |
96
+ * | Cartesia | 0.6 – 1.5 | Passed as `generation_config.speed`. Sonic models treat it |
97
+ * | | | as guidance, not an exact multiplier. |
98
+ *
99
+ * @default 1.0
100
+ */
101
+ speed?: number
102
+ /** ISO 639-1 language code (e.g. "en", "es", "fr"). */
103
+ language?: string
104
+ /** Abort signal for cancelling the request. */
105
+ abortSignal?: AbortSignal
106
+ }
107
+
108
+ export interface GenerateSpeechResult {
109
+ audio: GeneratedFile
110
+ model: string
111
+ cost: number | null
112
+ /** Word-level timestamps, if the provider supports them (ElevenLabs, Cartesia). */
113
+ timestamps?: WordTimestamp[]
114
+ }
115
+
116
+ // ─── cost calculation ────────────────────────────────────────────────────────
117
+
118
+ export function calculateSpeechCost(
119
+ cost: { type: 'per-character'; perMillionChars: number },
120
+ textLength: number,
121
+ ): number {
122
+ return (textLength / 1_000_000) * cost.perMillionChars
123
+ }
124
+
125
+ // ─── media type inference ────────────────────────────────────────────────────
126
+
127
+ function inferAudioMediaType(format?: string): string {
128
+ switch (format) {
129
+ case 'mp3':
130
+ return 'audio/mpeg'
131
+ case 'wav':
132
+ return 'audio/wav'
133
+ case 'pcm':
134
+ case 'raw':
135
+ return 'audio/pcm'
136
+ case 'opus':
137
+ return 'audio/opus'
138
+ case 'aac':
139
+ return 'audio/aac'
140
+ case 'flac':
141
+ return 'audio/flac'
142
+ default:
143
+ return 'audio/mpeg'
144
+ }
145
+ }
146
+
147
+ function inferMediaTypeFromBytes(audio: Uint8Array, requestedFormat?: string): string {
148
+ // Check magic bytes first
149
+ if (audio.length >= 3 && audio[0] === 0x49 && audio[1] === 0x44 && audio[2] === 0x33) {
150
+ return 'audio/mpeg' // ID3 header (MP3)
151
+ }
152
+ if (audio.length >= 2 && audio[0] === 0xff && (audio[1]! & 0xe0) === 0xe0) {
153
+ return 'audio/mpeg' // MP3 sync word
154
+ }
155
+ if (audio.length >= 4 && audio[0] === 0x52 && audio[1] === 0x49 && audio[2] === 0x46 && audio[3] === 0x46) {
156
+ return 'audio/wav' // RIFF header
157
+ }
158
+ if (audio.length >= 4 && audio[0] === 0x4f && audio[1] === 0x67 && audio[2] === 0x67 && audio[3] === 0x53) {
159
+ return 'audio/ogg' // OggS header (could be opus)
160
+ }
161
+ if (audio.length >= 4 && audio[0] === 0x66 && audio[1] === 0x4c && audio[2] === 0x61 && audio[3] === 0x43) {
162
+ return 'audio/flac' // fLaC header
163
+ }
164
+ // Fall back to requested format
165
+ return inferAudioMediaType(requestedFormat)
166
+ }
167
+
168
+ // ─── generateSpeech ──────────────────────────────────────────────────────────
169
+
170
+ /**
171
+ * Generate speech audio from text. Auto-detects which provider to use
172
+ * based on model ID. Shares credentials and subscription with the CLI.
173
+ * Returns word-level timestamps when the provider supports them.
174
+ */
175
+ export async function generateSpeechUncached(opts: GenerateSpeechOptions): Promise<Error | GenerateSpeechResult> {
176
+ injectCredentialsToEnv()
177
+
178
+ const model = opts.model ?? DEFAULT_SPEECH_MODEL
179
+ const config = getSpeechModelConfig(model)
180
+ if (config instanceof Error) return config
181
+
182
+ const provider = getSpeechProvider(config.provider)
183
+ if (provider instanceof Error) return provider
184
+
185
+ let result: SpeechProviderResult
186
+ try {
187
+ result = await provider.generate({
188
+ text: opts.text,
189
+ modelId: model,
190
+ voice: opts.voice,
191
+ outputFormat: opts.outputFormat,
192
+ instructions: opts.instructions,
193
+ speed: opts.speed,
194
+ language: opts.language,
195
+ abortSignal: opts.abortSignal,
196
+ })
197
+ } catch (err) {
198
+ return err instanceof Error ? err : new Error(String(err))
199
+ }
200
+
201
+ const mediaType = result.mediaType || inferMediaTypeFromBytes(result.audio, opts.outputFormat)
202
+ const cost = calculateSpeechCost(config.cost, opts.text.length)
203
+
204
+ return {
205
+ audio: { uint8Array: result.audio, mediaType },
206
+ model,
207
+ cost,
208
+ timestamps: result.timestamps,
209
+ }
210
+ }
211
+
212
+ // ─── cached generateSpeech ──────────────────────────────────────────────────
213
+ // Writes audio to public/generated/audio/ and a sidecar .timestamps.json
214
+ // next to it when the provider returns word timestamps. On cache hit,
215
+ // the sidecar is read back to populate the result.
216
+ //
217
+ // Uses cachedGenerate with postWrite to write the timestamps sidecar after
218
+ // the audio file, and deserialize to read it back on cache hit.
219
+
220
+ import fs from 'node:fs'
221
+ import path from 'node:path'
222
+ import { cachedGenerate } from './cached-generate.js'
223
+ import { extensionFromMediaType } from './cache-utils.js'
224
+
225
+ /** Compute the sidecar timestamps path for an audio file.
226
+ * Stored in a `timestamps/` subdirectory to avoid cache key collision:
227
+ * findCachedFile() matches any filename containing the hash, so a sidecar
228
+ * like `foo-hash.mp3.timestamps.json` in the same directory would be
229
+ * mistakenly returned as the "cached audio" file. */
230
+ function timestampsPathFor(filePath: string): string {
231
+ return path.join(path.dirname(filePath), 'timestamps', path.basename(filePath) + '.json')
232
+ }
233
+
234
+ export interface CachedSpeechResult {
235
+ src: string
236
+ timestamps?: WordTimestamp[]
237
+ }
238
+
239
+ /** Internal result carrying both audio bytes and optional timestamps. */
240
+ interface SpeechGenerateOutput {
241
+ audio: GeneratedFile
242
+ timestamps?: WordTimestamp[]
243
+ }
244
+
245
+ export const generateSpeech = cachedGenerate<GenerateSpeechOptions & { seed?: number }, SpeechGenerateOutput, CachedSpeechResult>({
246
+ namespace: 'audio',
247
+ prefixFrom: (p) => p.text,
248
+ modelFrom: (p) => p.model,
249
+ generate: async (params) => {
250
+ const result = await generateSpeechUncached(params)
251
+ if (result instanceof Error) throw result
252
+ return { audio: result.audio, timestamps: result.timestamps }
253
+ },
254
+ serialize: (output) => ({
255
+ bytes: output.audio.uint8Array,
256
+ extension: extensionFromMediaType(output.audio.mediaType),
257
+ }),
258
+ postWrite: (filePath, output) => {
259
+ if (output.timestamps?.length) {
260
+ const tsPath = timestampsPathFor(filePath)
261
+ fs.mkdirSync(path.dirname(tsPath), { recursive: true })
262
+ fs.writeFileSync(tsPath, JSON.stringify(output.timestamps, null, 2))
263
+ }
264
+ },
265
+ deserialize: ({ urlPath, filePath }) => {
266
+ const tsPath = timestampsPathFor(filePath)
267
+ let timestamps: WordTimestamp[] | undefined
268
+ try {
269
+ if (fs.existsSync(tsPath)) {
270
+ timestamps = JSON.parse(fs.readFileSync(tsPath, 'utf-8'))
271
+ }
272
+ } catch { /* non-fatal: timestamps are optional */ }
273
+ return { src: urlPath, timestamps }
274
+ },
275
+ })
@@ -0,0 +1,146 @@
1
+ // Speech model registry for egaki.
2
+ // Maps speech model IDs to their provider and SpeechProvider factory.
3
+ //
4
+ // Provider resolution priority (same as image/video):
5
+ // 1. Direct provider key → direct provider implementation
6
+ // 2. Egaki API key → route through egaki gateway (future)
7
+ // 3. No key → error with subscription recommendation
8
+ import pc from 'picocolors'
9
+ import type { SpeechProvider } from './speech-generate.js'
10
+ import {
11
+ PROVIDERS,
12
+ injectCredentialsToEnv,
13
+ } from './credentials.js'
14
+ import { ValidationError } from './models.js'
15
+ import {
16
+ SPEECH_CATALOG,
17
+ findSpeechModel,
18
+ type SpeechModelEntry,
19
+ } from './speech-catalog.js'
20
+
21
+ export { type SpeechModelEntry }
22
+
23
+ export const DEFAULT_SPEECH_MODEL = 'tts-1'
24
+
25
+ // ─── provider factory map ────────────────────────────────────────────────────
26
+
27
+ type SpeechProviderFactory = () => Promise<SpeechProvider>
28
+
29
+ const SPEECH_PROVIDER_FACTORIES: Record<string, SpeechProviderFactory> = {
30
+ openai: async () => {
31
+ const { createOpenAISpeechProvider } = await import('./openai-speech-provider.js')
32
+ return createOpenAISpeechProvider()
33
+ },
34
+ elevenlabs: async () => {
35
+ const { createElevenLabsSpeechProvider } = await import('./elevenlabs-speech-provider.js')
36
+ return createElevenLabsSpeechProvider()
37
+ },
38
+ cartesia: async () => {
39
+ const { createCartesiaSpeechProvider } = await import('./cartesia-provider.js')
40
+ return createCartesiaSpeechProvider()
41
+ },
42
+ }
43
+
44
+ // ─── key checking ────────────────────────────────────────────────────────────
45
+
46
+ function hasDirectProviderKey(providerName: string): boolean {
47
+ const info = PROVIDERS[providerName]
48
+ if (!info) return false
49
+ return Boolean(process.env[info.envVar])
50
+ }
51
+
52
+ function hasEgakiKey(): boolean {
53
+ const info = PROVIDERS['egaki']
54
+ if (!info) return false
55
+ return Boolean(process.env[info.envVar])
56
+ }
57
+
58
+ // ─── config lookup ───────────────────────────────────────────────────────────
59
+
60
+ export function getSpeechModelConfig(modelId: string): Error | SpeechModelEntry {
61
+ const entry = findSpeechModel(modelId)
62
+ if (!entry) {
63
+ return new ValidationError(`Unknown speech model: ${modelId}`)
64
+ }
65
+ return entry
66
+ }
67
+
68
+ // ─── auth source detection ───────────────────────────────────────────────────
69
+
70
+ type AuthSource =
71
+ | { type: 'api-key'; label: string }
72
+ | { type: 'egaki-gateway' }
73
+
74
+ function resolveAuthSource(providerName: string): AuthSource {
75
+ const info = PROVIDERS[providerName]
76
+ if (info && process.env[info.envVar]) {
77
+ return { type: 'api-key', label: info.envVar }
78
+ }
79
+ if (hasEgakiKey()) {
80
+ return { type: 'egaki-gateway' }
81
+ }
82
+ return { type: 'api-key', label: 'unknown' }
83
+ }
84
+
85
+ function logAuthSource(source: AuthSource): void {
86
+ switch (source.type) {
87
+ case 'api-key':
88
+ console.error(pc.dim(`Auth: ${source.label}`))
89
+ break
90
+ case 'egaki-gateway':
91
+ console.error(pc.dim('Auth: Egaki subscription (gateway)'))
92
+ break
93
+ }
94
+ }
95
+
96
+ // ─── ensure key ──────────────────────────────────────────────────────────────
97
+
98
+ export function ensureSpeechProviderKey(providerName: string): Error | undefined {
99
+ if (hasDirectProviderKey(providerName)) return undefined
100
+
101
+ // Speech models don't go through the gateway yet (AI Gateway doesn't
102
+ // support speech routing). Direct provider key is required.
103
+ const info = PROVIDERS[providerName]
104
+ const label = info?.label || providerName
105
+ return new ValidationError(
106
+ `Missing API key for ${label}. ` +
107
+ `Run: egaki login --provider ${providerName} --key <key>. ` +
108
+ (info ? info.hint : ''),
109
+ )
110
+ }
111
+
112
+ // ─── provider cache ──────────────────────────────────────────────────────────
113
+
114
+ /** Global cache of provider instances, keyed by provider name. */
115
+ const providerCache: Map<string, Promise<SpeechProvider>> =
116
+ (globalThis as any).__egakiSpeechProviderCache ??= new Map()
117
+
118
+ // ─── public factory ──────────────────────────────────────────────────────────
119
+
120
+ export function getSpeechProvider(providerName: string): Error | SpeechProvider {
121
+ injectCredentialsToEnv()
122
+
123
+ const keyError = ensureSpeechProviderKey(providerName)
124
+ if (keyError) return keyError
125
+
126
+ const authSource = resolveAuthSource(providerName)
127
+ logAuthSource(authSource)
128
+
129
+ const factory = SPEECH_PROVIDER_FACTORIES[providerName]
130
+ if (!factory) {
131
+ return new Error(`Speech generation is not supported for provider: ${providerName}`)
132
+ }
133
+
134
+ // Return a lazy provider: the factory is async but we want getSpeechProvider
135
+ // to be sync for ergonomic use in generateSpeechUncached. The provider promise
136
+ // is cached globally per provider name so the factory runs once across all calls.
137
+ return {
138
+ async generate(options) {
139
+ const cached = providerCache.get(providerName)
140
+ if (cached) return (await cached).generate(options)
141
+ const promise = factory()
142
+ providerCache.set(providerName, promise)
143
+ return (await promise).generate(options)
144
+ },
145
+ }
146
+ }