beecork 1.4.10 → 1.5.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.
- package/dist/channels/admin.d.ts +10 -0
- package/dist/channels/admin.js +20 -0
- package/dist/channels/command-handler.d.ts +2 -10
- package/dist/channels/command-handler.js +47 -73
- package/dist/channels/discord.d.ts +1 -3
- package/dist/channels/discord.js +28 -28
- package/dist/channels/loader.js +0 -1
- package/dist/channels/send-helpers.d.ts +19 -0
- package/dist/channels/send-helpers.js +21 -0
- package/dist/channels/telegram.d.ts +1 -9
- package/dist/channels/telegram.js +46 -71
- package/dist/channels/types.d.ts +2 -10
- package/dist/channels/voice-state.d.ts +29 -0
- package/dist/channels/voice-state.js +43 -0
- package/dist/channels/webhook.d.ts +1 -1
- package/dist/channels/webhook.js +68 -24
- package/dist/channels/whatsapp.d.ts +1 -3
- package/dist/channels/whatsapp.js +79 -74
- package/dist/cli/doctor.js +5 -2
- package/dist/cli/handoff.js +6 -6
- package/dist/config.d.ts +5 -1
- package/dist/config.js +17 -14
- package/dist/daemon.js +29 -17
- package/dist/dashboard/html.js +20 -8
- package/dist/dashboard/routes.d.ts +17 -0
- package/dist/dashboard/routes.js +559 -0
- package/dist/dashboard/server.js +33 -488
- package/dist/db/index.js +16 -2
- package/dist/db/migrations.js +44 -8
- package/dist/mcp/handlers.d.ts +37 -0
- package/dist/mcp/handlers.js +451 -0
- package/dist/mcp/server.js +25 -849
- package/dist/mcp/tool-definitions.d.ts +1225 -0
- package/dist/mcp/tool-definitions.js +364 -0
- package/dist/media/index.d.ts +2 -7
- package/dist/media/index.js +1 -1
- package/dist/observability/analytics.d.ts +7 -1
- package/dist/observability/analytics.js +25 -7
- package/dist/projects/index.d.ts +3 -2
- package/dist/projects/index.js +2 -2
- package/dist/projects/manager.d.ts +1 -3
- package/dist/projects/manager.js +26 -25
- package/dist/projects/router.d.ts +10 -0
- package/dist/projects/router.js +28 -0
- package/dist/session/manager.d.ts +4 -0
- package/dist/session/manager.js +48 -42
- package/dist/session/subprocess.d.ts +1 -0
- package/dist/session/subprocess.js +21 -0
- package/dist/session/tab-store.d.ts +28 -0
- package/dist/session/tab-store.js +77 -0
- package/dist/tasks/scheduler.d.ts +6 -0
- package/dist/tasks/scheduler.js +52 -13
- package/dist/tasks/store.js +6 -6
- package/dist/timeline/query.js +6 -2
- package/dist/types.d.ts +15 -0
- package/dist/util/paths.d.ts +1 -0
- package/dist/util/paths.js +4 -1
- package/dist/util/rate-limiter.js +8 -0
- package/dist/util/text.d.ts +21 -1
- package/dist/util/text.js +25 -1
- package/dist/watchers/scheduler.js +2 -3
- package/package.json +1 -1
- package/dist/users/index.d.ts +0 -2
- package/dist/users/index.js +0 -1
- package/dist/users/service.d.ts +0 -17
- package/dist/users/service.js +0 -46
package/dist/daemon.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import { getConfig } from './config.js';
|
|
3
3
|
import { getDb, closeDb } from './db/index.js';
|
|
4
|
+
import { TabStore } from './session/tab-store.js';
|
|
4
5
|
import { TabManager } from './session/manager.js';
|
|
5
6
|
import { ChannelRegistry, TelegramChannel, WhatsAppChannel } from './channels/index.js';
|
|
6
7
|
import { TaskScheduler } from './tasks/scheduler.js';
|
|
@@ -188,7 +189,11 @@ async function main() {
|
|
|
188
189
|
}
|
|
189
190
|
}, 5000);
|
|
190
191
|
// 11. Handle shutdown
|
|
191
|
-
|
|
192
|
+
let shuttingDown = false;
|
|
193
|
+
const shutdown = async (exitCode = 0) => {
|
|
194
|
+
if (shuttingDown)
|
|
195
|
+
return;
|
|
196
|
+
shuttingDown = true;
|
|
192
197
|
logger.info('Beecork daemon shutting down...');
|
|
193
198
|
// Send shutdown notification before stopping (with timeout to prevent hanging)
|
|
194
199
|
try {
|
|
@@ -202,17 +207,20 @@ async function main() {
|
|
|
202
207
|
watcherScheduler.stopAll();
|
|
203
208
|
closeDb();
|
|
204
209
|
const pidPath = getPidPath();
|
|
205
|
-
|
|
206
|
-
fs.
|
|
210
|
+
try {
|
|
211
|
+
if (fs.existsSync(pidPath))
|
|
212
|
+
fs.unlinkSync(pidPath);
|
|
213
|
+
}
|
|
214
|
+
catch { /* race or already gone */ }
|
|
207
215
|
removeRuntimeInfo();
|
|
208
216
|
logActivity('system_event', 'Beecork daemon stopped');
|
|
209
217
|
logger.info('Beecork daemon stopped.');
|
|
210
218
|
logger.close();
|
|
211
|
-
process.exit(
|
|
219
|
+
process.exit(exitCode);
|
|
212
220
|
};
|
|
213
221
|
shutdownFn = shutdown;
|
|
214
|
-
process.on('SIGTERM', shutdown);
|
|
215
|
-
process.on('SIGINT', shutdown);
|
|
222
|
+
process.on('SIGTERM', () => shutdown(0));
|
|
223
|
+
process.on('SIGINT', () => shutdown(0));
|
|
216
224
|
// Resilience: catch unhandled errors to prevent silent daemon death
|
|
217
225
|
process.on('unhandledRejection', (reason) => {
|
|
218
226
|
logger.error('Unhandled rejection:', reason);
|
|
@@ -220,9 +228,8 @@ async function main() {
|
|
|
220
228
|
});
|
|
221
229
|
process.on('uncaughtException', async (err) => {
|
|
222
230
|
logger.error('Uncaught exception — shutting down gracefully:', err);
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
process.exit(1);
|
|
231
|
+
// Exit non-zero so the supervisor (launchd/systemd/Task Scheduler) restarts us.
|
|
232
|
+
await shutdown(1);
|
|
226
233
|
});
|
|
227
234
|
logger.info(`Beecork daemon ready (home: ${getBeecorkHome()})`);
|
|
228
235
|
logActivity('system_event', 'Beecork daemon started');
|
|
@@ -247,12 +254,13 @@ async function main() {
|
|
|
247
254
|
}
|
|
248
255
|
async function recoverCrashedTabs() {
|
|
249
256
|
const db = getDb();
|
|
250
|
-
|
|
257
|
+
// Find tabs that were running when daemon stopped
|
|
258
|
+
const crashedRows = TabStore.findRunning(db);
|
|
251
259
|
if (crashedRows.length === 0)
|
|
252
260
|
return;
|
|
253
261
|
logger.info(`Found ${crashedRows.length} tabs that were running when daemon stopped`);
|
|
254
262
|
for (const row of crashedRows) {
|
|
255
|
-
logger.info(`Recovering tab: ${row.name} (session: ${row.
|
|
263
|
+
logger.info(`Recovering tab: ${row.name} (session: ${row.sessionId})`);
|
|
256
264
|
// Get last few messages for context
|
|
257
265
|
const recentMessages = db.prepare(`SELECT role, content FROM messages
|
|
258
266
|
WHERE tab_id = ? ORDER BY created_at DESC LIMIT 5`).all(row.id);
|
|
@@ -267,13 +275,17 @@ async function recoverCrashedTabs() {
|
|
|
267
275
|
`[SYSTEM: Please acknowledge you are back and ready for new instructions.]`,
|
|
268
276
|
].join('\n');
|
|
269
277
|
// Reset status so TabManager can use it
|
|
270
|
-
|
|
271
|
-
//
|
|
272
|
-
|
|
278
|
+
TabStore.setIdleById(row.id, db);
|
|
279
|
+
// Await the resume + notify on outcome. The previous fire-and-forget
|
|
280
|
+
// pattern told users "recovered" before the resume actually succeeded.
|
|
281
|
+
try {
|
|
282
|
+
await tabManager.sendMessage(row.name, recoveryPrompt, { resume: true });
|
|
283
|
+
await broadcastNotify(`Beecork restarted. Recovered tab "${row.name}" — session resumed.`).catch(() => { });
|
|
284
|
+
}
|
|
285
|
+
catch (err) {
|
|
273
286
|
logger.error(`Failed to recover tab ${row.name}:`, err);
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
await broadcastNotify(`Beecork restarted. Recovered tab "${row.name}" — session resumed.`).catch(() => { });
|
|
287
|
+
await broadcastNotify(`Beecork restarted. Tab "${row.name}" recovery FAILED — ${err instanceof Error ? err.message : String(err)}.`).catch(() => { });
|
|
288
|
+
}
|
|
277
289
|
}
|
|
278
290
|
}
|
|
279
291
|
main().catch(err => {
|
package/dist/dashboard/html.js
CHANGED
|
@@ -314,8 +314,10 @@ export function getDashboardHtml(token) {
|
|
|
314
314
|
dot.className = 'status-dot status-error';
|
|
315
315
|
status.textContent = 'stopped';
|
|
316
316
|
}
|
|
317
|
+
// Server returns both new "tasks" and legacy "cronJobs" — prefer the new one.
|
|
318
|
+
const taskCount = s.tasks ?? s.cronJobs ?? 0;
|
|
317
319
|
document.getElementById('stats').textContent =
|
|
318
|
-
s.tabs + ' tabs | ' +
|
|
320
|
+
s.tabs + ' tabs | ' + taskCount + ' tasks | ' + s.memories + ' mem';
|
|
319
321
|
} catch {}
|
|
320
322
|
}
|
|
321
323
|
|
|
@@ -349,7 +351,8 @@ export function getDashboardHtml(token) {
|
|
|
349
351
|
}).join('');
|
|
350
352
|
}
|
|
351
353
|
|
|
352
|
-
async function selectTab(name) {
|
|
354
|
+
async function selectTab(name, opts) {
|
|
355
|
+
const fromUser = !opts || opts.fromUser !== false;
|
|
353
356
|
selectedTab = name;
|
|
354
357
|
document.getElementById('msg-title').textContent = name;
|
|
355
358
|
document.getElementById('btn-delete-tab').classList.remove('hidden');
|
|
@@ -382,19 +385,28 @@ export function getDashboardHtml(token) {
|
|
|
382
385
|
if (m.tokens_in) meta.push(m.tokens_in.toLocaleString() + ' in');
|
|
383
386
|
if (m.tokens_out) meta.push(m.tokens_out.toLocaleString() + ' out');
|
|
384
387
|
const metaStr = meta.length ? '<span class="text-xs text-gray-600 ml-2">' + meta.join(' | ') + '</span>' : '';
|
|
385
|
-
const
|
|
388
|
+
const truncated = m.content.length > 2000;
|
|
389
|
+
const preview = truncated ? m.content.slice(0, 2000) : m.content;
|
|
390
|
+
const rest = truncated ? m.content.slice(2000) : '';
|
|
391
|
+
const body = truncated
|
|
392
|
+
? esc(preview) + '<details class="mt-1"><summary class="text-xs text-honey-400 cursor-pointer">Show ' + (m.content.length - 2000).toLocaleString() + ' more chars</summary>' + esc(rest) + '</details>'
|
|
393
|
+
: esc(preview);
|
|
386
394
|
|
|
387
395
|
return '<div class="' + cls + ' rounded-lg p-3">' +
|
|
388
396
|
'<div class="flex items-center justify-between mb-1">' +
|
|
389
397
|
'<span class="text-xs font-semibold ' + (m.role === 'user' ? 'text-honey-400' : 'text-gray-400') + '">' + label + metaStr + '</span>' +
|
|
390
398
|
'<span class="text-xs text-gray-600">' + timeAgo(m.created_at) + '</span>' +
|
|
391
399
|
'</div>' +
|
|
392
|
-
'<pre class="text-sm text-gray-300 whitespace-pre-wrap break-words font-sans leading-relaxed">' +
|
|
400
|
+
'<pre class="text-sm text-gray-300 whitespace-pre-wrap break-words font-sans leading-relaxed">' + body + '</pre>' +
|
|
393
401
|
'</div>';
|
|
394
402
|
}).join('');
|
|
395
403
|
|
|
396
|
-
|
|
397
|
-
|
|
404
|
+
// Only jump to bottom + steal focus on user-initiated selection,
|
|
405
|
+
// not on the 8s background refresh, so typing isn't interrupted.
|
|
406
|
+
if (fromUser) {
|
|
407
|
+
list.scrollTop = list.scrollHeight;
|
|
408
|
+
document.getElementById('msg-input').focus();
|
|
409
|
+
}
|
|
398
410
|
}
|
|
399
411
|
|
|
400
412
|
// --- Send message ---
|
|
@@ -683,7 +695,7 @@ export function getDashboardHtml(token) {
|
|
|
683
695
|
const day = c.day.slice(5);
|
|
684
696
|
return '<div class="flex-1 flex flex-col items-center gap-1">' +
|
|
685
697
|
'<span class="text-xs text-gray-500 font-mono">$' + c.total_cost.toFixed(3) + '</span>' +
|
|
686
|
-
'<div class="w-full cost-bar" style="height:' + Math.max(pct, 2) + '%" title="' + c.day + ': $' + c.total_cost.toFixed(4) + ' (' + c.message_count + ' msgs)"></div>' +
|
|
698
|
+
'<div class="w-full cost-bar" style="height:' + Math.max(pct, 2) + '%" title="' + esc(c.day) + ': $' + c.total_cost.toFixed(4) + ' (' + c.message_count + ' msgs)"></div>' +
|
|
687
699
|
'<span class="text-xs text-gray-600 font-mono">' + day + '</span>' +
|
|
688
700
|
'</div>';
|
|
689
701
|
}).join('') +
|
|
@@ -762,7 +774,7 @@ export function getDashboardHtml(token) {
|
|
|
762
774
|
loadTabs();
|
|
763
775
|
setInterval(loadStatus, 10000);
|
|
764
776
|
// Periodically reload messages for selected tab
|
|
765
|
-
setInterval(() => { if (selectedTab) selectTab(selectedTab); }, 8000);
|
|
777
|
+
setInterval(() => { if (selectedTab) selectTab(selectedTab, { fromUser: false }); }, 8000);
|
|
766
778
|
</script>
|
|
767
779
|
</body>
|
|
768
780
|
</html>`;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import http from 'node:http';
|
|
2
|
+
declare function json(res: http.ServerResponse, data: unknown, status?: number): void;
|
|
3
|
+
export interface RouteCtx {
|
|
4
|
+
req: http.IncomingMessage;
|
|
5
|
+
res: http.ServerResponse;
|
|
6
|
+
url: URL;
|
|
7
|
+
path: string;
|
|
8
|
+
}
|
|
9
|
+
type RouteHandler = (ctx: RouteCtx) => Promise<void> | void;
|
|
10
|
+
interface RouteEntry {
|
|
11
|
+
method: string;
|
|
12
|
+
test: (path: string) => boolean;
|
|
13
|
+
handler: RouteHandler;
|
|
14
|
+
}
|
|
15
|
+
export declare const ROUTES: RouteEntry[];
|
|
16
|
+
export declare function dispatch(method: string, path: string): RouteEntry | null;
|
|
17
|
+
export { json };
|