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,55 +0,0 @@
1
- import path from 'node:path';
2
- import os from 'node:os';
3
- import fs from 'node:fs';
4
- const BEECORK_DIR = '.beecork';
5
- export function getBeecorkHome() {
6
- return process.env.BEECORK_HOME || path.join(os.homedir(), BEECORK_DIR);
7
- }
8
- export function getConfigPath() {
9
- return path.join(getBeecorkHome(), 'config.json');
10
- }
11
- export function getDbPath() {
12
- return path.join(getBeecorkHome(), 'memory.db');
13
- }
14
- export function getCrontabPath() {
15
- return path.join(getBeecorkHome(), 'crontab.json');
16
- }
17
- export function getMcpConfigPath() {
18
- return path.join(getBeecorkHome(), 'mcp-config.json');
19
- }
20
- export function getLogsDir() {
21
- return path.join(getBeecorkHome(), 'logs');
22
- }
23
- export function getPidPath() {
24
- return path.join(getBeecorkHome(), 'beecork.pid');
25
- }
26
- export function getRuntimeInfoPath() {
27
- return path.join(getBeecorkHome(), 'runtime.json');
28
- }
29
- export function getCronReloadSignalPath() {
30
- return path.join(getBeecorkHome(), '.cron-reload');
31
- }
32
- export function getWatcherReloadSignalPath() {
33
- return path.join(getBeecorkHome(), '.watcher-reload');
34
- }
35
- export function getWhatsappSessionPath() {
36
- return path.join(getBeecorkHome(), 'whatsapp-session');
37
- }
38
- export function ensureBeecorkDirs() {
39
- const home = getBeecorkHome();
40
- fs.mkdirSync(home, { recursive: true, mode: 0o700 });
41
- // Upgrade an existing dir that was created with a looser umask before this fix landed.
42
- try {
43
- fs.chmodSync(home, 0o700);
44
- }
45
- catch {
46
- /* not fatal if mode change fails */
47
- }
48
- fs.mkdirSync(getLogsDir(), { recursive: true, mode: 0o700 });
49
- }
50
- export function expandHome(p) {
51
- if (p.startsWith('~/') || p === '~') {
52
- return path.join(os.homedir(), p.slice(1));
53
- }
54
- return p;
55
- }
@@ -1,2 +0,0 @@
1
- export type Platform = 'mac' | 'linux' | 'windows';
2
- export declare function getPlatform(): Platform;
@@ -1,11 +0,0 @@
1
- import os from 'node:os';
2
- export function getPlatform() {
3
- const p = os.platform();
4
- if (p === 'darwin')
5
- return 'mac';
6
- if (p === 'linux')
7
- return 'linux';
8
- if (p === 'win32')
9
- return 'windows';
10
- throw new Error(`Unsupported platform: ${p}. Beecork supports Mac, Linux, and Windows.`);
11
- }
@@ -1,34 +0,0 @@
1
- /**
2
- * Collects tool use events and sends batched progress updates
3
- * with escalating intervals and next-update predictions.
4
- *
5
- * Schedule: 15s → 30s → 1m → 2m → 3m → 4m → ... (grows by 1m after 2m)
6
- * Each update tells the user when the next one will come.
7
- * Works across all channels (Telegram, Discord, WhatsApp, etc.)
8
- */
9
- export declare class ProgressTracker {
10
- private events;
11
- private startTime;
12
- private onReport;
13
- private timer;
14
- private tabName;
15
- private reportCount;
16
- private readonly intervals;
17
- constructor(tabName: string, onReport: (message: string) => void);
18
- /** Record a tool use event */
19
- record(toolName: string, toolInput: Record<string, unknown>): void;
20
- /** Stop the timer (called when task finishes) */
21
- stop(): void;
22
- /** Get the interval for the current report number */
23
- private getInterval;
24
- /** Format a duration in ms to a human-readable string */
25
- private formatDuration;
26
- /** Schedule the next progress report */
27
- private scheduleNext;
28
- /** Flush pending events as a progress report */
29
- private flush;
30
- /** Summarize a tool call into a human-readable one-liner */
31
- private summarizeTool;
32
- private extractPath;
33
- private extractCommand;
34
- }
@@ -1,133 +0,0 @@
1
- import { logger } from './logger.js';
2
- /**
3
- * Collects tool use events and sends batched progress updates
4
- * with escalating intervals and next-update predictions.
5
- *
6
- * Schedule: 15s → 30s → 1m → 2m → 3m → 4m → ... (grows by 1m after 2m)
7
- * Each update tells the user when the next one will come.
8
- * Works across all channels (Telegram, Discord, WhatsApp, etc.)
9
- */
10
- export class ProgressTracker {
11
- events = [];
12
- startTime;
13
- onReport;
14
- timer = null;
15
- tabName;
16
- reportCount = 0;
17
- // Escalating intervals in ms: 15s, 30s, 1m, 2m, then +1m each
18
- intervals = [15000, 30000, 60000, 120000];
19
- constructor(tabName, onReport) {
20
- this.tabName = tabName;
21
- this.startTime = Date.now();
22
- this.onReport = onReport;
23
- this.scheduleNext();
24
- }
25
- /** Record a tool use event */
26
- record(toolName, toolInput) {
27
- this.events.push({ tool: toolName, summary: this.summarizeTool(toolName, toolInput) });
28
- }
29
- /** Stop the timer (called when task finishes) */
30
- stop() {
31
- if (this.timer) {
32
- clearTimeout(this.timer);
33
- this.timer = null;
34
- }
35
- }
36
- /** Get the interval for the current report number */
37
- getInterval() {
38
- if (this.reportCount < this.intervals.length) {
39
- return this.intervals[this.reportCount];
40
- }
41
- // After the fixed intervals, grow by 1 minute each time (3m, 4m, 5m, ...)
42
- const extraMinutes = this.reportCount - this.intervals.length + 3;
43
- return extraMinutes * 60000;
44
- }
45
- /** Format a duration in ms to a human-readable string */
46
- formatDuration(ms) {
47
- const seconds = Math.round(ms / 1000);
48
- if (seconds < 60)
49
- return `${seconds}s`;
50
- const minutes = Math.floor(seconds / 60);
51
- const secs = seconds % 60;
52
- return secs > 0 ? `${minutes}m ${secs}s` : `${minutes}m`;
53
- }
54
- /** Schedule the next progress report */
55
- scheduleNext() {
56
- const interval = this.getInterval();
57
- this.timer = setTimeout(() => {
58
- this.flush();
59
- this.reportCount++;
60
- this.scheduleNext();
61
- }, interval);
62
- }
63
- /** Flush pending events as a progress report */
64
- flush() {
65
- if (this.events.length === 0)
66
- return;
67
- const elapsed = Date.now() - this.startTime;
68
- const elapsedStr = this.formatDuration(elapsed);
69
- // Build the activity summary
70
- const lines = [];
71
- for (const event of this.events) {
72
- lines.push(` ${event.summary}`);
73
- }
74
- this.events = [];
75
- // Calculate when the next update will come
76
- const nextInterval = this.getInterval();
77
- const nextStr = this.formatDuration(nextInterval);
78
- const message = [
79
- `\uD83D\uDD04 Working on "${this.tabName}" (${elapsedStr} elapsed)`,
80
- ...lines,
81
- `\n\u23F3 Next update in ${nextStr} or when finished.`,
82
- ].join('\n');
83
- try {
84
- this.onReport(message);
85
- }
86
- catch (err) {
87
- logger.warn('Progress report failed:', err);
88
- }
89
- }
90
- /** Summarize a tool call into a human-readable one-liner */
91
- summarizeTool(name, input) {
92
- switch (name) {
93
- case 'Write':
94
- case 'write':
95
- return `\uD83D\uDCDD Created ${this.extractPath(input)}`;
96
- case 'Edit':
97
- case 'edit':
98
- return `\uD83D\uDCDD Edited ${this.extractPath(input)}`;
99
- case 'Read':
100
- case 'read':
101
- return `\uD83D\uDC41 Read ${this.extractPath(input)}`;
102
- case 'Bash':
103
- case 'bash':
104
- return `\uD83D\uDD27 Ran: ${this.extractCommand(input)}`;
105
- case 'Glob':
106
- case 'glob':
107
- return `\uD83D\uDD0D Searched for files: ${input.pattern || '...'}`;
108
- case 'Grep':
109
- case 'grep':
110
- return `\uD83D\uDD0D Searched code: ${input.pattern || '...'}`;
111
- case 'WebSearch':
112
- case 'web_search':
113
- return `\uD83C\uDF10 Searched: ${input.query || '...'}`;
114
- case 'WebFetch':
115
- case 'web_fetch':
116
- return `\uD83C\uDF10 Fetched: ${input.url || '...'}`;
117
- default:
118
- return `\u2699\uFE0F ${name}`;
119
- }
120
- }
121
- extractPath(input) {
122
- const p = (input.file_path || input.path || input.filePath || '');
123
- if (p.includes('/'))
124
- return p.split('/').pop() || p;
125
- if (p.includes('\\'))
126
- return p.split('\\').pop() || p;
127
- return p || '...';
128
- }
129
- extractCommand(input) {
130
- const cmd = (input.command || input.cmd || '');
131
- return cmd.length > 60 ? cmd.slice(0, 57) + '...' : cmd || '...';
132
- }
133
- }
@@ -1,14 +0,0 @@
1
- export declare class RateLimiter {
2
- private globalLimit;
3
- private perKeyLimit;
4
- private windowMs;
5
- private global;
6
- private perKey;
7
- constructor(globalLimit?: number, perKeyLimit?: number, windowMs?: number);
8
- /** Returns true if the request is allowed */
9
- check(key: string): boolean;
10
- }
11
- /** Shared singleton rate limiter for inbound messages */
12
- export declare const inboundLimiter: RateLimiter;
13
- /** Rate limiter for group responses */
14
- export declare const groupLimiter: RateLimiter;
@@ -1,50 +0,0 @@
1
- import { logger } from './logger.js';
2
- export class RateLimiter {
3
- globalLimit;
4
- perKeyLimit;
5
- windowMs;
6
- global = { count: 0, resetAt: Date.now() + 60000 };
7
- perKey = new Map();
8
- constructor(globalLimit = 20, perKeyLimit = 10, windowMs = 60000) {
9
- this.globalLimit = globalLimit;
10
- this.perKeyLimit = perKeyLimit;
11
- this.windowMs = windowMs;
12
- }
13
- /** Returns true if the request is allowed */
14
- check(key) {
15
- const now = Date.now();
16
- // Reset global window
17
- if (now > this.global.resetAt) {
18
- this.global = { count: 0, resetAt: now + this.windowMs };
19
- }
20
- if (this.global.count >= this.globalLimit) {
21
- logger.warn(`Rate limit: global limit reached (${this.globalLimit}/min)`);
22
- return false;
23
- }
24
- // Bound the per-key map so a daemon that has seen many one-off keys
25
- // (e.g. thousands of Telegram groups over months) doesn't leak memory.
26
- if (this.perKey.size > 1000) {
27
- for (const [k, w] of this.perKey) {
28
- if (now > w.resetAt)
29
- this.perKey.delete(k);
30
- }
31
- }
32
- // Reset per-key window
33
- let keyWindow = this.perKey.get(key);
34
- if (!keyWindow || now > keyWindow.resetAt) {
35
- keyWindow = { count: 0, resetAt: now + this.windowMs };
36
- this.perKey.set(key, keyWindow);
37
- }
38
- if (keyWindow.count >= this.perKeyLimit) {
39
- logger.warn(`Rate limit: channel ${key} limit reached (${this.perKeyLimit}/min)`);
40
- return false;
41
- }
42
- this.global.count++;
43
- keyWindow.count++;
44
- return true;
45
- }
46
- }
47
- /** Shared singleton rate limiter for inbound messages */
48
- export const inboundLimiter = new RateLimiter(60, 30);
49
- /** Rate limiter for group responses */
50
- export const groupLimiter = new RateLimiter(10, 3, 60000); // 10 global, 3 per group per minute
@@ -1,2 +0,0 @@
1
- /** Retry a function with exponential backoff */
2
- export declare function retryWithBackoff<T>(fn: () => Promise<T>, delays?: number[], label?: string): Promise<T>;
@@ -1,18 +0,0 @@
1
- import { logger } from './logger.js';
2
- /** Retry a function with exponential backoff */
3
- export async function retryWithBackoff(fn, delays = [1000, 5000, 15000], label = 'operation') {
4
- let lastError;
5
- for (let attempt = 0; attempt <= delays.length; attempt++) {
6
- try {
7
- return await fn();
8
- }
9
- catch (err) {
10
- lastError = err instanceof Error ? err : new Error(String(err));
11
- if (attempt < delays.length) {
12
- logger.warn(`${label} failed (attempt ${attempt + 1}/${delays.length + 1}), retrying in ${delays[attempt]}ms: ${lastError.message}`);
13
- await new Promise((resolve) => setTimeout(resolve, delays[attempt]));
14
- }
15
- }
16
- }
17
- throw lastError ?? new Error(`${label} failed after ${delays.length + 1} attempts`);
18
- }
@@ -1,32 +0,0 @@
1
- import type { MediaAttachment } from '../types.js';
2
- /**
3
- * Shared message-size limits. Single source of truth so caps don't drift
4
- * between channels, MCP, and dashboard.
5
- */
6
- export declare const MESSAGE_LIMITS: {
7
- /** Per-chunk send size (Telegram = 4096; other channels chunk to this too). */
8
- readonly CHUNK: 4096;
9
- /** HTTP request body cap (webhook + dashboard endpoints). */
10
- readonly HTTP_BODY: number;
11
- /** Webhook channel single-message payload. */
12
- readonly WEBHOOK_PROMPT: 100000;
13
- /** MCP tool content/message field. Tight because it goes through Claude's tool-result token budget. */
14
- readonly MCP_CONTENT: 10240;
15
- };
16
- /** Split long text into chunks that fit within a message limit */
17
- export declare function chunkText(text: string, maxLength?: number): string[];
18
- /** Format an ISO date as a human-readable relative time */
19
- export declare function timeAgo(isoDate: string): string;
20
- /** Parse a "/tab <name> <prompt>" message into tabName + prompt */
21
- export declare function parseTabMessage(text: string): {
22
- tabName: string;
23
- prompt: string;
24
- };
25
- /**
26
- * Build the channel-side message text with an optional [tabName] prefix.
27
- * Used by all three channels' sendResponse paths so the prefix format is
28
- * consistent and the gate logic (skip "default", skip empty) lives in one place.
29
- */
30
- export declare function formatTabbedResponse(text: string, tabName?: string): string;
31
- /** Build prompt text from media attachments */
32
- export declare function buildMediaPrompt(media: MediaAttachment[], textPrompt: string): string;
package/dist/util/text.js DELETED
@@ -1,101 +0,0 @@
1
- /**
2
- * Shared message-size limits. Single source of truth so caps don't drift
3
- * between channels, MCP, and dashboard.
4
- */
5
- export const MESSAGE_LIMITS = {
6
- /** Per-chunk send size (Telegram = 4096; other channels chunk to this too). */
7
- CHUNK: 4096,
8
- /** HTTP request body cap (webhook + dashboard endpoints). */
9
- HTTP_BODY: 1024 * 1024, // 1 MB
10
- /** Webhook channel single-message payload. */
11
- WEBHOOK_PROMPT: 100_000, // 100 KB
12
- /** MCP tool content/message field. Tight because it goes through Claude's tool-result token budget. */
13
- MCP_CONTENT: 10_240, // 10 KB
14
- };
15
- const DEFAULT_MAX_LENGTH = MESSAGE_LIMITS.CHUNK;
16
- /** Split long text into chunks that fit within a message limit */
17
- export function chunkText(text, maxLength = DEFAULT_MAX_LENGTH) {
18
- if (text.length <= maxLength)
19
- return [text];
20
- const chunks = [];
21
- let remaining = text;
22
- while (remaining.length > 0) {
23
- if (remaining.length <= maxLength) {
24
- chunks.push(remaining);
25
- break;
26
- }
27
- // Try to break at a newline
28
- let breakPoint = remaining.lastIndexOf('\n', maxLength);
29
- if (breakPoint <= 0 || breakPoint < maxLength * 0.5) {
30
- // Try to break at a space
31
- breakPoint = remaining.lastIndexOf(' ', maxLength);
32
- }
33
- if (breakPoint <= 0 || breakPoint < maxLength * 0.5) {
34
- // Hard break
35
- breakPoint = maxLength;
36
- }
37
- chunks.push(remaining.slice(0, breakPoint));
38
- remaining = remaining.slice(breakPoint).trimStart();
39
- }
40
- return chunks;
41
- }
42
- /** Format an ISO date as a human-readable relative time */
43
- export function timeAgo(isoDate) {
44
- const diff = Date.now() - new Date(isoDate).getTime();
45
- const mins = Math.floor(diff / 60000);
46
- if (mins < 1)
47
- return 'just now';
48
- if (mins < 60)
49
- return `${mins}m ago`;
50
- const hours = Math.floor(mins / 60);
51
- if (hours < 24)
52
- return `${hours}h ago`;
53
- const days = Math.floor(hours / 24);
54
- return `${days}d ago`;
55
- }
56
- /** Parse a "/tab <name> <prompt>" message into tabName + prompt */
57
- export function parseTabMessage(text) {
58
- if (text.startsWith('/tab ')) {
59
- const rest = text.slice(5);
60
- const spaceIdx = rest.indexOf(' ');
61
- if (spaceIdx === -1)
62
- return { tabName: rest, prompt: '' };
63
- return { tabName: rest.slice(0, spaceIdx), prompt: rest.slice(spaceIdx + 1).trim() };
64
- }
65
- return { tabName: 'default', prompt: text };
66
- }
67
- /**
68
- * Build the channel-side message text with an optional [tabName] prefix.
69
- * Used by all three channels' sendResponse paths so the prefix format is
70
- * consistent and the gate logic (skip "default", skip empty) lives in one place.
71
- */
72
- export function formatTabbedResponse(text, tabName) {
73
- if (!tabName || tabName === 'default')
74
- return text;
75
- return `[${tabName}] ${text}`;
76
- }
77
- /** Build prompt text from media attachments */
78
- export function buildMediaPrompt(media, textPrompt) {
79
- if (media.length === 0)
80
- return textPrompt;
81
- const descriptions = media.map((m) => {
82
- if (m.type === 'voice' && m.caption?.startsWith('[Transcribed'))
83
- return m.caption;
84
- switch (m.type) {
85
- case 'image':
86
- return `User sent an image: ${m.filePath}`;
87
- case 'voice':
88
- return `User sent a voice message: ${m.filePath}`;
89
- case 'audio':
90
- return `User sent an audio file: ${m.filePath}${m.fileName ? ` (${m.fileName})` : ''}`;
91
- case 'video':
92
- return `User sent a video: ${m.filePath}`;
93
- case 'document':
94
- return `User sent a file: ${m.filePath}${m.fileName ? ` (${m.fileName})` : ''}`;
95
- default:
96
- return `User sent a file: ${m.filePath}`;
97
- }
98
- });
99
- const mediaText = descriptions.join('\n');
100
- return textPrompt ? `${mediaText}\n\n${textPrompt}` : mediaText;
101
- }
package/dist/version.d.ts DELETED
@@ -1 +0,0 @@
1
- export declare const VERSION: string;
package/dist/version.js DELETED
@@ -1,4 +0,0 @@
1
- import { createRequire } from 'node:module';
2
- const require = createRequire(import.meta.url);
3
- const pkg = require('../package.json');
4
- export const VERSION = pkg.version;
@@ -1,17 +0,0 @@
1
- export { createSTTProvider, type STTProvider } from './stt.js';
2
- export { createTTSProvider, type TTSProvider } from './tts.js';
3
- import { type STTProvider } from './stt.js';
4
- import { type TTSProvider } from './tts.js';
5
- import type { VoiceConfig } from '../types.js';
6
- export declare function initVoiceProviders(voice?: VoiceConfig): {
7
- stt: STTProvider | null;
8
- tts: TTSProvider | null;
9
- };
10
- export declare function transcribeVoiceMessages(media: Array<{
11
- type: string;
12
- filePath?: string;
13
- caption?: string;
14
- }>, sttProvider: {
15
- transcribe(path: string): Promise<string>;
16
- warmup?(): void;
17
- }, channelId: string, warmedUp: boolean): Promise<boolean>;
@@ -1,42 +0,0 @@
1
- export { createSTTProvider } from './stt.js';
2
- export { createTTSProvider } from './tts.js';
3
- import { createSTTProvider } from './stt.js';
4
- import { createTTSProvider } from './tts.js';
5
- import { logger } from '../util/logger.js';
6
- export function initVoiceProviders(voice) {
7
- let stt = null;
8
- let tts = null;
9
- if (voice?.sttProvider && voice.sttProvider !== 'none') {
10
- stt = createSTTProvider({ provider: voice.sttProvider, apiKey: voice.sttApiKey });
11
- }
12
- if (voice?.ttsProvider && voice.ttsProvider !== 'none') {
13
- tts = createTTSProvider({
14
- provider: voice.ttsProvider,
15
- apiKey: voice.ttsApiKey,
16
- voice: voice.ttsVoice,
17
- });
18
- }
19
- return { stt, tts };
20
- }
21
- export async function transcribeVoiceMessages(media, sttProvider, channelId, warmedUp) {
22
- if (!sttProvider)
23
- return warmedUp;
24
- if (!warmedUp) {
25
- sttProvider.warmup?.();
26
- warmedUp = true;
27
- }
28
- for (const m of media) {
29
- if (m.type === 'voice' && m.filePath) {
30
- const start = Date.now();
31
- try {
32
- const transcription = await sttProvider.transcribe(m.filePath);
33
- m.caption = `[Transcribed from voice message]: ${transcription}`;
34
- logger.info(`[${channelId}] Voice transcription: ${Date.now() - start}ms`);
35
- }
36
- catch (err) {
37
- logger.warn('Voice transcription failed, passing file path instead:', err);
38
- }
39
- }
40
- }
41
- return warmedUp;
42
- }
@@ -1,16 +0,0 @@
1
- export interface STTProvider {
2
- transcribe(filePath: string): Promise<string>;
3
- warmup?(): Promise<void>;
4
- }
5
- /** OpenAI Whisper API provider */
6
- export declare class WhisperAPIProvider implements STTProvider {
7
- private apiKey;
8
- constructor(apiKey: string);
9
- warmup(): Promise<void>;
10
- transcribe(filePath: string): Promise<string>;
11
- }
12
- /** Create STT provider from config */
13
- export declare function createSTTProvider(config: {
14
- provider: string;
15
- apiKey?: string;
16
- }): STTProvider | null;
package/dist/voice/stt.js DELETED
@@ -1,57 +0,0 @@
1
- import { readFile } from 'node:fs/promises';
2
- import { logger } from '../util/logger.js';
3
- import { assertInsideMediaDir } from '../media/store.js';
4
- /** OpenAI Whisper API provider */
5
- export class WhisperAPIProvider {
6
- apiKey;
7
- constructor(apiKey) {
8
- this.apiKey = apiKey;
9
- }
10
- async warmup() {
11
- try {
12
- // Make a lightweight request to warm up the HTTPS connection
13
- await fetch('https://api.openai.com/v1/models', {
14
- headers: { Authorization: `Bearer ${this.apiKey}` },
15
- signal: AbortSignal.timeout(5000),
16
- });
17
- }
18
- catch {
19
- /* non-critical */
20
- }
21
- }
22
- async transcribe(filePath) {
23
- // Defense-in-depth: callers should already constrain filePath to the media dir,
24
- // but verify here so a leaked/forged MediaAttachment.filePath cannot exfiltrate
25
- // arbitrary files (e.g. ~/.ssh/id_rsa) to the Whisper API.
26
- const safePath = assertInsideMediaDir(filePath);
27
- const buffer = await readFile(safePath);
28
- const formData = new FormData();
29
- formData.append('file', new Blob([buffer]), 'audio.ogg');
30
- formData.append('model', 'whisper-1');
31
- const response = await fetch('https://api.openai.com/v1/audio/transcriptions', {
32
- method: 'POST',
33
- headers: { Authorization: `Bearer ${this.apiKey}` },
34
- body: formData,
35
- signal: AbortSignal.timeout(60000),
36
- });
37
- if (!response.ok) {
38
- throw new Error(`Whisper API error: ${response.status} ${response.statusText}`);
39
- }
40
- const result = (await response.json());
41
- return result.text;
42
- }
43
- }
44
- /** Create STT provider from config */
45
- export function createSTTProvider(config) {
46
- switch (config.provider) {
47
- case 'whisper-api':
48
- if (!config.apiKey) {
49
- logger.warn('Whisper API key not configured, voice transcription disabled');
50
- return null;
51
- }
52
- return new WhisperAPIProvider(config.apiKey);
53
- default:
54
- logger.warn(`Unknown STT provider: ${config.provider}`);
55
- return null;
56
- }
57
- }
@@ -1,23 +0,0 @@
1
- export interface TTSProvider {
2
- synthesize(text: string): Promise<string>;
3
- }
4
- /** OpenAI TTS API provider */
5
- export declare class OpenAITTSProvider implements TTSProvider {
6
- private apiKey;
7
- private voice;
8
- constructor(apiKey: string, voice?: string);
9
- synthesize(text: string): Promise<string>;
10
- }
11
- /** ElevenLabs TTS provider */
12
- export declare class ElevenLabsTTSProvider implements TTSProvider {
13
- private apiKey;
14
- private voiceId;
15
- constructor(apiKey: string, voiceId?: string);
16
- synthesize(text: string): Promise<string>;
17
- }
18
- /** Create TTS provider from config */
19
- export declare function createTTSProvider(config: {
20
- provider: string;
21
- apiKey?: string;
22
- voice?: string;
23
- }): TTSProvider | null;