beecork 1.7.0 → 2.0.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 (223) hide show
  1. package/README.md +52 -111
  2. package/dist/index.js +1394 -581
  3. package/package.json +26 -54
  4. package/dist/capabilities/index.d.ts +0 -3
  5. package/dist/capabilities/index.js +0 -2
  6. package/dist/capabilities/manager.d.ts +0 -13
  7. package/dist/capabilities/manager.js +0 -115
  8. package/dist/capabilities/packs.d.ts +0 -2
  9. package/dist/capabilities/packs.js +0 -54
  10. package/dist/capabilities/types.d.ts +0 -21
  11. package/dist/capabilities/types.js +0 -1
  12. package/dist/channels/admin.d.ts +0 -10
  13. package/dist/channels/admin.js +0 -20
  14. package/dist/channels/command-handler.d.ts +0 -19
  15. package/dist/channels/command-handler.js +0 -235
  16. package/dist/channels/discord.d.ts +0 -17
  17. package/dist/channels/discord.js +0 -266
  18. package/dist/channels/index.d.ts +0 -7
  19. package/dist/channels/index.js +0 -6
  20. package/dist/channels/loader.d.ts +0 -7
  21. package/dist/channels/loader.js +0 -87
  22. package/dist/channels/pipeline.d.ts +0 -50
  23. package/dist/channels/pipeline.js +0 -92
  24. package/dist/channels/registry.d.ts +0 -36
  25. package/dist/channels/registry.js +0 -80
  26. package/dist/channels/send-helpers.d.ts +0 -19
  27. package/dist/channels/send-helpers.js +0 -21
  28. package/dist/channels/telegram.d.ts +0 -47
  29. package/dist/channels/telegram.js +0 -649
  30. package/dist/channels/types.d.ts +0 -41
  31. package/dist/channels/types.js +0 -1
  32. package/dist/channels/voice-state.d.ts +0 -29
  33. package/dist/channels/voice-state.js +0 -45
  34. package/dist/channels/webhook.d.ts +0 -16
  35. package/dist/channels/webhook.js +0 -222
  36. package/dist/channels/whatsapp.d.ts +0 -28
  37. package/dist/channels/whatsapp.js +0 -323
  38. package/dist/cli/capabilities.d.ts +0 -3
  39. package/dist/cli/capabilities.js +0 -72
  40. package/dist/cli/channel.d.ts +0 -3
  41. package/dist/cli/channel.js +0 -202
  42. package/dist/cli/commands.d.ts +0 -15
  43. package/dist/cli/commands.js +0 -279
  44. package/dist/cli/doctor.d.ts +0 -1
  45. package/dist/cli/doctor.js +0 -208
  46. package/dist/cli/handoff.d.ts +0 -8
  47. package/dist/cli/handoff.js +0 -33
  48. package/dist/cli/helpers.d.ts +0 -2
  49. package/dist/cli/helpers.js +0 -21
  50. package/dist/cli/mcp.d.ts +0 -3
  51. package/dist/cli/mcp.js +0 -47
  52. package/dist/cli/media.d.ts +0 -2
  53. package/dist/cli/media.js +0 -125
  54. package/dist/cli/setup.d.ts +0 -1
  55. package/dist/cli/setup.js +0 -239
  56. package/dist/cli/store.d.ts +0 -3
  57. package/dist/cli/store.js +0 -108
  58. package/dist/config.d.ts +0 -11
  59. package/dist/config.js +0 -115
  60. package/dist/daemon.d.ts +0 -1
  61. package/dist/daemon.js +0 -345
  62. package/dist/dashboard/html.d.ts +0 -1
  63. package/dist/dashboard/html.js +0 -849
  64. package/dist/dashboard/routes.d.ts +0 -17
  65. package/dist/dashboard/routes.js +0 -623
  66. package/dist/dashboard/server.d.ts +0 -1
  67. package/dist/dashboard/server.js +0 -104
  68. package/dist/db/connection.d.ts +0 -29
  69. package/dist/db/connection.js +0 -37
  70. package/dist/db/index.d.ts +0 -14
  71. package/dist/db/index.js +0 -127
  72. package/dist/db/migrations.d.ts +0 -2
  73. package/dist/db/migrations.js +0 -384
  74. package/dist/delegation/manager.d.ts +0 -18
  75. package/dist/delegation/manager.js +0 -89
  76. package/dist/index.d.ts +0 -2
  77. package/dist/knowledge/index.d.ts +0 -2
  78. package/dist/knowledge/index.js +0 -1
  79. package/dist/knowledge/manager.d.ts +0 -17
  80. package/dist/knowledge/manager.js +0 -160
  81. package/dist/knowledge/types.d.ts +0 -7
  82. package/dist/knowledge/types.js +0 -1
  83. package/dist/mcp/handlers.d.ts +0 -37
  84. package/dist/mcp/handlers.js +0 -520
  85. package/dist/mcp/server.d.ts +0 -2
  86. package/dist/mcp/server.js +0 -86
  87. package/dist/mcp/tool-definitions.d.ts +0 -1225
  88. package/dist/mcp/tool-definitions.js +0 -412
  89. package/dist/mcp/validate.d.ts +0 -23
  90. package/dist/mcp/validate.js +0 -65
  91. package/dist/media/factory.d.ts +0 -6
  92. package/dist/media/factory.js +0 -36
  93. package/dist/media/generators/dall-e.d.ts +0 -10
  94. package/dist/media/generators/dall-e.js +0 -41
  95. package/dist/media/generators/elevenlabs-music.d.ts +0 -9
  96. package/dist/media/generators/elevenlabs-music.js +0 -34
  97. package/dist/media/generators/elevenlabs-sfx.d.ts +0 -9
  98. package/dist/media/generators/elevenlabs-sfx.js +0 -33
  99. package/dist/media/generators/kling.d.ts +0 -9
  100. package/dist/media/generators/kling.js +0 -47
  101. package/dist/media/generators/lyria.d.ts +0 -10
  102. package/dist/media/generators/lyria.js +0 -41
  103. package/dist/media/generators/nano-banana.d.ts +0 -10
  104. package/dist/media/generators/nano-banana.js +0 -45
  105. package/dist/media/generators/poll-util.d.ts +0 -12
  106. package/dist/media/generators/poll-util.js +0 -22
  107. package/dist/media/generators/recraft.d.ts +0 -9
  108. package/dist/media/generators/recraft.js +0 -52
  109. package/dist/media/generators/runway.d.ts +0 -9
  110. package/dist/media/generators/runway.js +0 -49
  111. package/dist/media/generators/stable-diffusion.d.ts +0 -9
  112. package/dist/media/generators/stable-diffusion.js +0 -35
  113. package/dist/media/generators/veo.d.ts +0 -9
  114. package/dist/media/generators/veo.js +0 -32
  115. package/dist/media/index.d.ts +0 -6
  116. package/dist/media/index.js +0 -8
  117. package/dist/media/store.d.ts +0 -17
  118. package/dist/media/store.js +0 -69
  119. package/dist/media/types.d.ts +0 -43
  120. package/dist/media/types.js +0 -1
  121. package/dist/notifications/index.d.ts +0 -4
  122. package/dist/notifications/index.js +0 -21
  123. package/dist/notifications/ntfy.d.ts +0 -9
  124. package/dist/notifications/ntfy.js +0 -25
  125. package/dist/notifications/pushover.d.ts +0 -9
  126. package/dist/notifications/pushover.js +0 -26
  127. package/dist/notifications/types.d.ts +0 -5
  128. package/dist/notifications/types.js +0 -1
  129. package/dist/notifications/webhook-provider.d.ts +0 -9
  130. package/dist/notifications/webhook-provider.js +0 -24
  131. package/dist/observability/analytics.d.ts +0 -30
  132. package/dist/observability/analytics.js +0 -118
  133. package/dist/projects/index.d.ts +0 -4
  134. package/dist/projects/index.js +0 -2
  135. package/dist/projects/manager.d.ts +0 -13
  136. package/dist/projects/manager.js +0 -166
  137. package/dist/projects/router.d.ts +0 -17
  138. package/dist/projects/router.js +0 -246
  139. package/dist/projects/types.d.ts +0 -19
  140. package/dist/projects/types.js +0 -1
  141. package/dist/service/install.d.ts +0 -4
  142. package/dist/service/install.js +0 -146
  143. package/dist/service/templates.d.ts +0 -4
  144. package/dist/service/templates.js +0 -52
  145. package/dist/service/windows.d.ts +0 -8
  146. package/dist/service/windows.js +0 -59
  147. package/dist/session/budget-guard.d.ts +0 -20
  148. package/dist/session/budget-guard.js +0 -31
  149. package/dist/session/circuit-breaker.d.ts +0 -19
  150. package/dist/session/circuit-breaker.js +0 -91
  151. package/dist/session/context-compactor.d.ts +0 -32
  152. package/dist/session/context-compactor.js +0 -45
  153. package/dist/session/context-monitor.d.ts +0 -15
  154. package/dist/session/context-monitor.js +0 -44
  155. package/dist/session/handoff.d.ts +0 -21
  156. package/dist/session/handoff.js +0 -50
  157. package/dist/session/manager.d.ts +0 -60
  158. package/dist/session/manager.js +0 -379
  159. package/dist/session/memory-store.d.ts +0 -29
  160. package/dist/session/memory-store.js +0 -45
  161. package/dist/session/message-queue.d.ts +0 -28
  162. package/dist/session/message-queue.js +0 -52
  163. package/dist/session/pending-dispatcher.d.ts +0 -31
  164. package/dist/session/pending-dispatcher.js +0 -120
  165. package/dist/session/pending-store.d.ts +0 -60
  166. package/dist/session/pending-store.js +0 -118
  167. package/dist/session/stale-session.d.ts +0 -31
  168. package/dist/session/stale-session.js +0 -45
  169. package/dist/session/subprocess.d.ts +0 -25
  170. package/dist/session/subprocess.js +0 -189
  171. package/dist/session/tab-store.d.ts +0 -28
  172. package/dist/session/tab-store.js +0 -78
  173. package/dist/tasks/scheduler.d.ts +0 -45
  174. package/dist/tasks/scheduler.js +0 -329
  175. package/dist/tasks/store.d.ts +0 -11
  176. package/dist/tasks/store.js +0 -103
  177. package/dist/timeline/index.d.ts +0 -3
  178. package/dist/timeline/index.js +0 -2
  179. package/dist/timeline/logger.d.ts +0 -8
  180. package/dist/timeline/logger.js +0 -11
  181. package/dist/timeline/query.d.ts +0 -11
  182. package/dist/timeline/query.js +0 -67
  183. package/dist/timeline/types.d.ts +0 -12
  184. package/dist/timeline/types.js +0 -1
  185. package/dist/types.d.ts +0 -216
  186. package/dist/types.js +0 -3
  187. package/dist/util/auto-heal.d.ts +0 -43
  188. package/dist/util/auto-heal.js +0 -107
  189. package/dist/util/install-info.d.ts +0 -37
  190. package/dist/util/install-info.js +0 -80
  191. package/dist/util/logger.d.ts +0 -18
  192. package/dist/util/logger.js +0 -118
  193. package/dist/util/paths.d.ts +0 -13
  194. package/dist/util/paths.js +0 -55
  195. package/dist/util/platform.d.ts +0 -2
  196. package/dist/util/platform.js +0 -11
  197. package/dist/util/progress.d.ts +0 -34
  198. package/dist/util/progress.js +0 -133
  199. package/dist/util/rate-limiter.d.ts +0 -14
  200. package/dist/util/rate-limiter.js +0 -50
  201. package/dist/util/retry.d.ts +0 -2
  202. package/dist/util/retry.js +0 -18
  203. package/dist/util/text.d.ts +0 -32
  204. package/dist/util/text.js +0 -101
  205. package/dist/version.d.ts +0 -1
  206. package/dist/version.js +0 -4
  207. package/dist/voice/index.d.ts +0 -17
  208. package/dist/voice/index.js +0 -42
  209. package/dist/voice/stt.d.ts +0 -16
  210. package/dist/voice/stt.js +0 -57
  211. package/dist/voice/tts.d.ts +0 -23
  212. package/dist/voice/tts.js +0 -84
  213. package/dist/watchers/evaluator.d.ts +0 -5
  214. package/dist/watchers/evaluator.js +0 -45
  215. package/dist/watchers/index.d.ts +0 -4
  216. package/dist/watchers/index.js +0 -3
  217. package/dist/watchers/scheduler.d.ts +0 -20
  218. package/dist/watchers/scheduler.js +0 -183
  219. package/dist/watchers/store.d.ts +0 -10
  220. package/dist/watchers/store.js +0 -56
  221. package/dist/watchers/types.d.ts +0 -15
  222. package/dist/watchers/types.js +0 -1
  223. package/scripts/postinstall.mjs +0 -50
@@ -1,33 +0,0 @@
1
- import { saveMedia } from '../store.js';
2
- export class ElevenLabsSfxGenerator {
3
- apiKey;
4
- id = 'elevenlabs-sfx';
5
- name = 'ElevenLabs Sound Effects';
6
- supportedTypes = ['audio'];
7
- constructor(apiKey) {
8
- this.apiKey = apiKey;
9
- }
10
- async generate(type, prompt, options) {
11
- if (type !== 'audio')
12
- throw new Error('ElevenLabs SFX only supports audio generation');
13
- const response = await fetch('https://api.elevenlabs.io/v1/sound-generation', {
14
- method: 'POST',
15
- headers: {
16
- 'xi-api-key': this.apiKey,
17
- 'Content-Type': 'application/json',
18
- },
19
- body: JSON.stringify({
20
- text: prompt.slice(0, 500),
21
- duration_seconds: options?.duration,
22
- }),
23
- signal: AbortSignal.timeout(60000),
24
- });
25
- if (!response.ok) {
26
- const err = await response.text();
27
- throw new Error(`ElevenLabs SFX error ${response.status}: ${err.slice(0, 200)}`);
28
- }
29
- const buffer = Buffer.from(await response.arrayBuffer());
30
- const filePath = saveMedia(buffer, 'mp3', 'generated-sfx.mp3');
31
- return { filePath, mimeType: 'audio/mpeg' };
32
- }
33
- }
@@ -1,9 +0,0 @@
1
- import type { MediaGenerator, MediaType, GenerateOptions, GenerateResult } from '../types.js';
2
- export declare class KlingGenerator implements MediaGenerator {
3
- private apiKey;
4
- readonly id = "kling";
5
- readonly name = "Kling AI";
6
- readonly supportedTypes: MediaType[];
7
- constructor(apiKey: string);
8
- generate(type: MediaType, prompt: string, options?: GenerateOptions): Promise<GenerateResult>;
9
- }
@@ -1,47 +0,0 @@
1
- import { saveMedia } from '../store.js';
2
- import { pollForCompletion } from './poll-util.js';
3
- export class KlingGenerator {
4
- apiKey;
5
- id = 'kling';
6
- name = 'Kling AI';
7
- supportedTypes = ['video'];
8
- constructor(apiKey) {
9
- this.apiKey = apiKey;
10
- }
11
- async generate(type, prompt, options) {
12
- if (type !== 'video')
13
- throw new Error('Kling only supports video generation');
14
- const response = await fetch('https://api.klingai.com/v1/videos/text2video', {
15
- method: 'POST',
16
- headers: {
17
- Authorization: `Bearer ${this.apiKey}`,
18
- 'Content-Type': 'application/json',
19
- },
20
- body: JSON.stringify({
21
- prompt: prompt.slice(0, 2500),
22
- duration: options?.duration || 5,
23
- model_name: 'kling-v1',
24
- }),
25
- signal: AbortSignal.timeout(30000),
26
- });
27
- if (!response.ok) {
28
- const err = await response.text();
29
- throw new Error(`Kling error ${response.status}: ${err.slice(0, 200)}`);
30
- }
31
- const { data } = (await response.json());
32
- // Poll for completion
33
- const headers = { Authorization: `Bearer ${this.apiKey}` };
34
- const videoUrl = await pollForCompletion({
35
- statusUrl: `https://api.klingai.com/v1/videos/text2video/${data.task_id}`,
36
- headers,
37
- isComplete: (d) => d.data.task_status === 'succeed' && !!d.data.task_result?.videos[0],
38
- isFailed: (d) => (d.data.task_status === 'failed' ? 'generation failed' : null),
39
- getResultUrl: (d) => d.data.task_result.videos[0].url,
40
- label: 'Kling',
41
- });
42
- const videoResp = await fetch(videoUrl, { signal: AbortSignal.timeout(60000) });
43
- const buffer = Buffer.from(await videoResp.arrayBuffer());
44
- const filePath = saveMedia(buffer, 'mp4', 'generated-video.mp4');
45
- return { filePath, mimeType: 'video/mp4' };
46
- }
47
- }
@@ -1,10 +0,0 @@
1
- import type { MediaGenerator, MediaType, GenerateOptions, GenerateResult } from '../types.js';
2
- export declare class LyriaGenerator implements MediaGenerator {
3
- private apiKey;
4
- private model;
5
- readonly id = "lyria";
6
- readonly name = "Google Lyria";
7
- readonly supportedTypes: MediaType[];
8
- constructor(apiKey: string, model?: string);
9
- generate(type: MediaType, prompt: string, options?: GenerateOptions): Promise<GenerateResult>;
10
- }
@@ -1,41 +0,0 @@
1
- import { saveMedia } from '../store.js';
2
- export class LyriaGenerator {
3
- apiKey;
4
- model;
5
- id = 'lyria';
6
- name = 'Google Lyria';
7
- supportedTypes = ['music'];
8
- constructor(apiKey, model = 'lyria-3-clip') {
9
- this.apiKey = apiKey;
10
- this.model = model;
11
- }
12
- async generate(type, prompt, options) {
13
- if (type !== 'music')
14
- throw new Error('Lyria only supports music generation');
15
- const useProModel = (options?.duration && options.duration > 30) || this.model.includes('pro');
16
- const modelId = useProModel ? 'lyria-3-pro' : 'lyria-3-clip';
17
- const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/${modelId}:generateContent?key=${this.apiKey}`, {
18
- method: 'POST',
19
- headers: { 'Content-Type': 'application/json' },
20
- body: JSON.stringify({
21
- contents: [{ parts: [{ text: prompt.slice(0, 2000) }] }],
22
- generationConfig: {
23
- responseModalities: ['AUDIO'],
24
- },
25
- }),
26
- signal: AbortSignal.timeout(300000), // 5 min for full songs
27
- });
28
- if (!response.ok) {
29
- const err = await response.text();
30
- throw new Error(`Lyria error ${response.status}: ${err.slice(0, 200)}`);
31
- }
32
- const data = (await response.json());
33
- const audioPart = data.candidates?.[0]?.content?.parts?.find((p) => p.inlineData);
34
- if (!audioPart?.inlineData?.data) {
35
- throw new Error('Lyria returned no audio data');
36
- }
37
- const buffer = Buffer.from(audioPart.inlineData.data, 'base64');
38
- const filePath = saveMedia(buffer, 'mp3', 'generated-music.mp3');
39
- return { filePath, mimeType: 'audio/mpeg' };
40
- }
41
- }
@@ -1,10 +0,0 @@
1
- import type { MediaGenerator, MediaType, GenerateOptions, GenerateResult } from '../types.js';
2
- export declare class NanoBananaGenerator implements MediaGenerator {
3
- private apiKey;
4
- private model;
5
- readonly id = "nano-banana";
6
- readonly name = "Google Nano Banana";
7
- readonly supportedTypes: MediaType[];
8
- constructor(apiKey: string, model?: string);
9
- generate(type: MediaType, prompt: string, _options?: GenerateOptions): Promise<GenerateResult>;
10
- }
@@ -1,45 +0,0 @@
1
- import { saveMedia } from '../store.js';
2
- export class NanoBananaGenerator {
3
- apiKey;
4
- model;
5
- id = 'nano-banana';
6
- name = 'Google Nano Banana';
7
- supportedTypes = ['image'];
8
- constructor(apiKey, model = 'gemini-2.5-flash-image') {
9
- this.apiKey = apiKey;
10
- this.model = model;
11
- }
12
- async generate(type, prompt, _options) {
13
- if (type !== 'image')
14
- throw new Error('Nano Banana only supports image generation');
15
- // Gemini image generation API
16
- const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/${this.model}:generateContent?key=${this.apiKey}`, {
17
- method: 'POST',
18
- headers: { 'Content-Type': 'application/json' },
19
- body: JSON.stringify({
20
- contents: [{ parts: [{ text: prompt.slice(0, 2000) }] }],
21
- generationConfig: {
22
- responseModalities: ['IMAGE'],
23
- imageGenerationConfig: {
24
- numberOfImages: 1,
25
- },
26
- },
27
- }),
28
- signal: AbortSignal.timeout(120000),
29
- });
30
- if (!response.ok) {
31
- const err = await response.text();
32
- throw new Error(`Nano Banana error ${response.status}: ${err.slice(0, 200)}`);
33
- }
34
- const data = (await response.json());
35
- const imagePart = data.candidates?.[0]?.content?.parts?.find((p) => p.inlineData);
36
- if (!imagePart?.inlineData?.data) {
37
- throw new Error('Nano Banana returned no image data');
38
- }
39
- const buffer = Buffer.from(imagePart.inlineData.data, 'base64');
40
- const mimeType = imagePart.inlineData.mimeType || 'image/png';
41
- const ext = mimeType.includes('jpeg') ? 'jpg' : 'png';
42
- const filePath = saveMedia(buffer, ext, `generated-image.${ext}`);
43
- return { filePath, mimeType };
44
- }
45
- }
@@ -1,12 +0,0 @@
1
- interface PollOptions<T = unknown> {
2
- statusUrl: string;
3
- headers: Record<string, string>;
4
- isComplete: (data: T) => boolean;
5
- isFailed: (data: T) => string | null;
6
- getResultUrl: (data: T) => string;
7
- interval?: number;
8
- maxAttempts?: number;
9
- label?: string;
10
- }
11
- export declare function pollForCompletion<T = unknown>(opts: PollOptions<T>): Promise<string>;
12
- export {};
@@ -1,22 +0,0 @@
1
- import { logger } from '../../util/logger.js';
2
- export async function pollForCompletion(opts) {
3
- const { statusUrl, headers, isComplete, isFailed, getResultUrl, interval = 5000, maxAttempts = 60, label = 'API', } = opts;
4
- for (let i = 0; i < maxAttempts; i++) {
5
- await new Promise((r) => setTimeout(r, interval));
6
- const resp = await fetch(statusUrl, { headers, signal: AbortSignal.timeout(10000) });
7
- if (!resp.ok) {
8
- logger.warn(`[${label}] Poll failed: ${resp.status}`);
9
- if (resp.status >= 400 && resp.status < 500) {
10
- throw new Error(`${label} API error: ${resp.status}`);
11
- }
12
- continue;
13
- }
14
- const data = (await resp.json());
15
- const failMsg = isFailed(data);
16
- if (failMsg)
17
- throw new Error(`${label} generation failed: ${failMsg}`);
18
- if (isComplete(data))
19
- return getResultUrl(data);
20
- }
21
- throw new Error(`${label} generation timed out after ${(maxAttempts * interval) / 1000}s`);
22
- }
@@ -1,9 +0,0 @@
1
- import type { MediaGenerator, MediaType, GenerateOptions, GenerateResult } from '../types.js';
2
- export declare class RecraftGenerator implements MediaGenerator {
3
- private apiKey;
4
- readonly id = "recraft";
5
- readonly name = "Recraft (Images + Vectors)";
6
- readonly supportedTypes: MediaType[];
7
- constructor(apiKey: string);
8
- generate(type: MediaType, prompt: string, options?: GenerateOptions): Promise<GenerateResult>;
9
- }
@@ -1,52 +0,0 @@
1
- import { saveMedia } from '../store.js';
2
- export class RecraftGenerator {
3
- apiKey;
4
- id = 'recraft';
5
- name = 'Recraft (Images + Vectors)';
6
- supportedTypes = ['image']; // SVG vectors are still "images"
7
- constructor(apiKey) {
8
- this.apiKey = apiKey;
9
- }
10
- async generate(type, prompt, options) {
11
- if (type !== 'image')
12
- throw new Error('Recraft only supports image/vector generation');
13
- const isVector = options?.format === 'svg' || options?.style === 'vector';
14
- const endpoint = isVector
15
- ? 'https://external.api.recraft.ai/v1/images/generations'
16
- : 'https://external.api.recraft.ai/v1/images/generations';
17
- const response = await fetch(endpoint, {
18
- method: 'POST',
19
- headers: {
20
- Authorization: `Bearer ${this.apiKey}`,
21
- 'Content-Type': 'application/json',
22
- },
23
- body: JSON.stringify({
24
- prompt: prompt.slice(0, 2000),
25
- model: isVector ? 'recraftv4' : 'recraftv4',
26
- response_format: isVector ? 'url' : 'b64_json',
27
- style: isVector ? 'vector_illustration' : options?.style || 'realistic_image',
28
- size: options?.width && options?.height ? `${options.width}x${options.height}` : '1024x1024',
29
- }),
30
- signal: AbortSignal.timeout(120000),
31
- });
32
- if (!response.ok) {
33
- const err = await response.text();
34
- throw new Error(`Recraft error ${response.status}: ${err.slice(0, 200)}`);
35
- }
36
- const data = (await response.json());
37
- const image = data.data?.[0];
38
- if (isVector && image?.url) {
39
- // Download SVG from URL
40
- const svgResp = await fetch(image.url, { signal: AbortSignal.timeout(30000) });
41
- const svgBuffer = Buffer.from(await svgResp.arrayBuffer());
42
- const filePath = saveMedia(svgBuffer, 'svg', 'generated-vector.svg');
43
- return { filePath, mimeType: 'image/svg+xml' };
44
- }
45
- else if (image?.b64_json) {
46
- const buffer = Buffer.from(image.b64_json, 'base64');
47
- const filePath = saveMedia(buffer, 'png', 'generated-image.png');
48
- return { filePath, mimeType: 'image/png' };
49
- }
50
- throw new Error('Recraft returned no image data');
51
- }
52
- }
@@ -1,9 +0,0 @@
1
- import type { MediaGenerator, MediaType, GenerateOptions, GenerateResult } from '../types.js';
2
- export declare class RunwayGenerator implements MediaGenerator {
3
- private apiKey;
4
- readonly id = "runway";
5
- readonly name = "Runway Gen-3";
6
- readonly supportedTypes: MediaType[];
7
- constructor(apiKey: string);
8
- generate(type: MediaType, prompt: string, options?: GenerateOptions): Promise<GenerateResult>;
9
- }
@@ -1,49 +0,0 @@
1
- import { saveMedia } from '../store.js';
2
- import { pollForCompletion } from './poll-util.js';
3
- export class RunwayGenerator {
4
- apiKey;
5
- id = 'runway';
6
- name = 'Runway Gen-3';
7
- supportedTypes = ['video'];
8
- constructor(apiKey) {
9
- this.apiKey = apiKey;
10
- }
11
- async generate(type, prompt, options) {
12
- if (type !== 'video')
13
- throw new Error('Runway only supports video generation');
14
- // Start generation
15
- const startResponse = await fetch('https://api.runwayml.com/v1/image_to_video', {
16
- method: 'POST',
17
- headers: {
18
- Authorization: `Bearer ${this.apiKey}`,
19
- 'Content-Type': 'application/json',
20
- 'X-Runway-Version': '2024-11-06',
21
- },
22
- body: JSON.stringify({
23
- model: 'gen3a_turbo',
24
- promptText: prompt.slice(0, 2000),
25
- duration: options?.duration || 5,
26
- }),
27
- signal: AbortSignal.timeout(30000),
28
- });
29
- if (!startResponse.ok) {
30
- const err = await startResponse.text();
31
- throw new Error(`Runway error ${startResponse.status}: ${err.slice(0, 200)}`);
32
- }
33
- const { id: taskId } = (await startResponse.json());
34
- // Poll for completion (max 5 minutes)
35
- const headers = { Authorization: `Bearer ${this.apiKey}`, 'X-Runway-Version': '2024-11-06' };
36
- const videoUrl = await pollForCompletion({
37
- statusUrl: `https://api.runwayml.com/v1/tasks/${taskId}`,
38
- headers,
39
- isComplete: (data) => data.status === 'SUCCEEDED' && !!data.output?.[0],
40
- isFailed: (data) => (data.status === 'FAILED' ? 'generation failed' : null),
41
- getResultUrl: (data) => data.output[0],
42
- label: 'Runway',
43
- });
44
- const videoResponse = await fetch(videoUrl, { signal: AbortSignal.timeout(60000) });
45
- const buffer = Buffer.from(await videoResponse.arrayBuffer());
46
- const filePath = saveMedia(buffer, 'mp4', 'generated-video.mp4');
47
- return { filePath, mimeType: 'video/mp4', durationMs: (options?.duration || 5) * 1000 };
48
- }
49
- }
@@ -1,9 +0,0 @@
1
- import type { MediaGenerator, MediaType, GenerateOptions, GenerateResult } from '../types.js';
2
- export declare class StableDiffusionGenerator implements MediaGenerator {
3
- private apiKey;
4
- readonly id = "stable-diffusion";
5
- readonly name = "Stable Diffusion (Stability AI)";
6
- readonly supportedTypes: MediaType[];
7
- constructor(apiKey: string);
8
- generate(type: MediaType, prompt: string, options?: GenerateOptions): Promise<GenerateResult>;
9
- }
@@ -1,35 +0,0 @@
1
- import { saveMedia } from '../store.js';
2
- export class StableDiffusionGenerator {
3
- apiKey;
4
- id = 'stable-diffusion';
5
- name = 'Stable Diffusion (Stability AI)';
6
- supportedTypes = ['image'];
7
- constructor(apiKey) {
8
- this.apiKey = apiKey;
9
- }
10
- async generate(type, prompt, options) {
11
- if (type !== 'image')
12
- throw new Error('Stable Diffusion only supports image generation');
13
- const formData = new FormData();
14
- formData.append('prompt', prompt.slice(0, 10000));
15
- if (options?.style)
16
- formData.append('style_preset', options.style);
17
- formData.append('output_format', 'png');
18
- const response = await fetch('https://api.stability.ai/v2beta/stable-image/generate/core', {
19
- method: 'POST',
20
- headers: {
21
- Authorization: `Bearer ${this.apiKey}`,
22
- Accept: 'image/*',
23
- },
24
- body: formData,
25
- signal: AbortSignal.timeout(120000),
26
- });
27
- if (!response.ok) {
28
- const err = await response.text();
29
- throw new Error(`Stability AI error ${response.status}: ${err.slice(0, 200)}`);
30
- }
31
- const buffer = Buffer.from(await response.arrayBuffer());
32
- const filePath = saveMedia(buffer, 'png', 'generated-image.png');
33
- return { filePath, mimeType: 'image/png' };
34
- }
35
- }
@@ -1,9 +0,0 @@
1
- import type { MediaGenerator, MediaType, GenerateOptions, GenerateResult } from '../types.js';
2
- export declare class VeoGenerator implements MediaGenerator {
3
- private apiKey;
4
- readonly id = "veo";
5
- readonly name = "Google Veo";
6
- readonly supportedTypes: MediaType[];
7
- constructor(apiKey: string);
8
- generate(type: MediaType, prompt: string, options?: GenerateOptions): Promise<GenerateResult>;
9
- }
@@ -1,32 +0,0 @@
1
- import { saveMedia } from '../store.js';
2
- export class VeoGenerator {
3
- apiKey;
4
- id = 'veo';
5
- name = 'Google Veo';
6
- supportedTypes = ['video'];
7
- constructor(apiKey) {
8
- this.apiKey = apiKey;
9
- }
10
- async generate(type, prompt, options) {
11
- if (type !== 'video')
12
- throw new Error('Veo only supports video generation');
13
- // Use Gemini API for Veo access
14
- const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/veo-2.0-generate-001:predict?key=${this.apiKey}`, {
15
- method: 'POST',
16
- headers: { 'Content-Type': 'application/json' },
17
- body: JSON.stringify({
18
- instances: [{ prompt: prompt.slice(0, 2000) }],
19
- parameters: { sampleCount: 1, durationSeconds: options?.duration || 5 },
20
- }),
21
- signal: AbortSignal.timeout(300000), // 5 min timeout for video
22
- });
23
- if (!response.ok) {
24
- const err = await response.text();
25
- throw new Error(`Veo error ${response.status}: ${err.slice(0, 200)}`);
26
- }
27
- const data = (await response.json());
28
- const buffer = Buffer.from(data.predictions[0].bytesBase64Encoded, 'base64');
29
- const filePath = saveMedia(buffer, 'mp4', 'generated-video.mp4');
30
- return { filePath, mimeType: 'video/mp4', durationMs: (options?.duration || 5) * 1000 };
31
- }
32
- }
@@ -1,6 +0,0 @@
1
- export type { MediaGenerator, MediaType, GenerateOptions, GenerateResult } from './types.js';
2
- export { createMediaGenerator } from './factory.js';
3
- export { saveMedia, cleanupMedia, ensureMediaDir, isOversized } from './store.js';
4
- import type { MediaGenerator } from './types.js';
5
- import type { MediaGeneratorConfig } from '../types.js';
6
- export declare function initMediaGenerators(configs?: MediaGeneratorConfig[]): MediaGenerator[];
@@ -1,8 +0,0 @@
1
- export { createMediaGenerator } from './factory.js';
2
- export { saveMedia, cleanupMedia, ensureMediaDir, isOversized } from './store.js';
3
- import { createMediaGenerator } from './factory.js';
4
- export function initMediaGenerators(configs) {
5
- if (!configs || configs.length === 0)
6
- return [];
7
- return configs.map((c) => createMediaGenerator(c)).filter((g) => g !== null);
8
- }
@@ -1,17 +0,0 @@
1
- /** Ensure media directory exists */
2
- export declare function ensureMediaDir(): string;
3
- /** Save a buffer to the media directory with a unique filename */
4
- export declare function saveMedia(buffer: Buffer, extension: string, originalName?: string): string;
5
- /** Check if a file exceeds the size limit */
6
- export declare function isOversized(sizeBytes: number, maxSizeMb?: number): boolean;
7
- /** Clean up expired media files */
8
- export declare function cleanupMedia(ttlMs?: number): number;
9
- /** Get the media directory path */
10
- export declare function getMediaDir(): string;
11
- /**
12
- * Resolve a file path and confirm it lives inside the beecork media directory.
13
- * Throws otherwise. Used at trust boundaries (MCP tools, voice transcription)
14
- * where a caller could otherwise hand us an arbitrary path to read or upload.
15
- * Returns the resolved (absolute) path on success.
16
- */
17
- export declare function assertInsideMediaDir(filePath: string): string;
@@ -1,69 +0,0 @@
1
- import fs from 'node:fs';
2
- import path from 'node:path';
3
- import { getBeecorkHome } from '../util/paths.js';
4
- import { logger } from '../util/logger.js';
5
- const MEDIA_DIR = path.join(getBeecorkHome(), 'media');
6
- const DEFAULT_TTL_MS = 10 * 60 * 1000; // 10 minutes
7
- const DEFAULT_MAX_SIZE_MB = 25;
8
- /** Ensure media directory exists */
9
- export function ensureMediaDir() {
10
- fs.mkdirSync(MEDIA_DIR, { recursive: true });
11
- return MEDIA_DIR;
12
- }
13
- /** Save a buffer to the media directory with a unique filename */
14
- export function saveMedia(buffer, extension, originalName) {
15
- ensureMediaDir();
16
- const timestamp = Date.now();
17
- const safeName = originalName
18
- ? originalName.replace(/[\/\\]/g, '_').replace(/\.\./g, '_')
19
- : undefined;
20
- const name = safeName ? `${timestamp}-${safeName}` : `${timestamp}.${extension}`;
21
- const filePath = path.join(MEDIA_DIR, name);
22
- fs.writeFileSync(filePath, buffer);
23
- return filePath;
24
- }
25
- /** Check if a file exceeds the size limit */
26
- export function isOversized(sizeBytes, maxSizeMb = DEFAULT_MAX_SIZE_MB) {
27
- return sizeBytes > maxSizeMb * 1024 * 1024;
28
- }
29
- /** Clean up expired media files */
30
- export function cleanupMedia(ttlMs = DEFAULT_TTL_MS) {
31
- if (!fs.existsSync(MEDIA_DIR))
32
- return 0;
33
- const now = Date.now();
34
- let cleaned = 0;
35
- for (const file of fs.readdirSync(MEDIA_DIR)) {
36
- const filePath = path.join(MEDIA_DIR, file);
37
- try {
38
- const stat = fs.statSync(filePath);
39
- if (now - stat.mtimeMs > ttlMs) {
40
- fs.unlinkSync(filePath);
41
- cleaned++;
42
- }
43
- }
44
- catch {
45
- /* file may have been deleted by another process */
46
- }
47
- }
48
- if (cleaned > 0)
49
- logger.info(`Media cleanup: removed ${cleaned} expired files`);
50
- return cleaned;
51
- }
52
- /** Get the media directory path */
53
- export function getMediaDir() {
54
- return MEDIA_DIR;
55
- }
56
- /**
57
- * Resolve a file path and confirm it lives inside the beecork media directory.
58
- * Throws otherwise. Used at trust boundaries (MCP tools, voice transcription)
59
- * where a caller could otherwise hand us an arbitrary path to read or upload.
60
- * Returns the resolved (absolute) path on success.
61
- */
62
- export function assertInsideMediaDir(filePath) {
63
- const resolved = path.resolve(filePath);
64
- const root = path.resolve(MEDIA_DIR) + path.sep;
65
- if (!resolved.startsWith(root)) {
66
- throw new Error(`filePath must be inside the beecork media directory (${MEDIA_DIR})`);
67
- }
68
- return resolved;
69
- }
@@ -1,43 +0,0 @@
1
- export type MediaType = 'image' | 'video' | 'audio' | 'music';
2
- export interface GenerateOptions {
3
- style?: string;
4
- model?: string;
5
- width?: number;
6
- height?: number;
7
- duration?: number;
8
- format?: string;
9
- }
10
- export interface GenerateResult {
11
- filePath: string;
12
- mimeType: string;
13
- durationMs?: number;
14
- metadata?: Record<string, unknown>;
15
- }
16
- export interface MediaGenerator {
17
- readonly id: string;
18
- readonly name: string;
19
- readonly supportedTypes: MediaType[];
20
- generate(type: MediaType, prompt: string, options?: GenerateOptions): Promise<GenerateResult>;
21
- }
22
- /** Gemini `:generateContent` response shape (used by lyria + nano-banana). */
23
- export interface GeminiInlineDataPart {
24
- inlineData?: {
25
- mimeType?: string;
26
- data: string;
27
- };
28
- }
29
- export interface GeminiCandidate {
30
- content?: {
31
- parts?: GeminiInlineDataPart[];
32
- };
33
- }
34
- export interface GeminiGenerateContentResponse {
35
- candidates?: GeminiCandidate[];
36
- }
37
- /** Recraft `/v1/images/generations` response. */
38
- export interface RecraftGenerateResponse {
39
- data?: Array<{
40
- url?: string;
41
- b64_json?: string;
42
- }>;
43
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,4 +0,0 @@
1
- export type { NotificationProvider } from './types.js';
2
- import type { NotificationProvider } from './types.js';
3
- import type { NotificationConfig } from '../types.js';
4
- export declare function createNotificationProvider(config: NotificationConfig): NotificationProvider | null;
@@ -1,21 +0,0 @@
1
- import { logger } from '../util/logger.js';
2
- import { PushoverProvider } from './pushover.js';
3
- import { NtfyProvider } from './ntfy.js';
4
- import { WebhookNotificationProvider } from './webhook-provider.js';
5
- export function createNotificationProvider(config) {
6
- switch (config.type) {
7
- case 'pushover':
8
- return new PushoverProvider(config.userKey, config.appToken);
9
- case 'ntfy':
10
- return new NtfyProvider(config.topic, config.server);
11
- case 'webhook':
12
- return new WebhookNotificationProvider(config.url, config.headers);
13
- default: {
14
- // Exhaustiveness check — TS will error here if NotificationConfig gains
15
- // a new variant without a matching case above.
16
- const _exhaustive = config;
17
- logger.warn(`Unknown notification provider: ${JSON.stringify(_exhaustive)}`);
18
- return null;
19
- }
20
- }
21
- }
@@ -1,9 +0,0 @@
1
- import type { NotificationProvider } from './types.js';
2
- export declare class NtfyProvider implements NotificationProvider {
3
- private topic;
4
- private server;
5
- readonly id = "ntfy";
6
- readonly name = "ntfy.sh";
7
- constructor(topic: string, server?: string);
8
- send(message: string, urgent?: boolean): Promise<void>;
9
- }