beecork 1.4.11 → 1.6.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/capabilities/index.d.ts +1 -1
- package/dist/capabilities/index.js +1 -1
- package/dist/capabilities/manager.js +13 -9
- package/dist/capabilities/packs.js +3 -1
- 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 +90 -84
- package/dist/channels/discord.d.ts +4 -9
- package/dist/channels/discord.js +59 -42
- package/dist/channels/index.d.ts +1 -1
- package/dist/channels/loader.js +13 -4
- package/dist/channels/pipeline.js +14 -5
- package/dist/channels/registry.d.ts +17 -1
- package/dist/channels/registry.js +33 -4
- package/dist/channels/send-helpers.d.ts +19 -0
- package/dist/channels/send-helpers.js +21 -0
- package/dist/channels/telegram.d.ts +21 -14
- package/dist/channels/telegram.js +214 -104
- package/dist/channels/types.d.ts +13 -38
- package/dist/channels/voice-state.d.ts +29 -0
- package/dist/channels/voice-state.js +45 -0
- package/dist/channels/webhook.d.ts +2 -5
- package/dist/channels/webhook.js +88 -29
- package/dist/channels/whatsapp.d.ts +9 -7
- package/dist/channels/whatsapp.js +141 -100
- package/dist/cli/capabilities.js +4 -4
- package/dist/cli/channel.js +16 -6
- package/dist/cli/commands.js +12 -9
- package/dist/cli/doctor.js +85 -27
- package/dist/cli/handoff.d.ts +7 -14
- package/dist/cli/handoff.js +9 -44
- package/dist/cli/mcp.js +5 -5
- package/dist/cli/media.js +21 -8
- package/dist/cli/setup.js +9 -8
- package/dist/cli/store.js +29 -12
- package/dist/config.d.ts +5 -1
- package/dist/config.js +20 -22
- package/dist/daemon.js +113 -51
- package/dist/dashboard/html.js +100 -20
- package/dist/dashboard/routes.d.ts +17 -0
- package/dist/dashboard/routes.js +623 -0
- package/dist/dashboard/server.js +38 -489
- package/dist/db/connection.d.ts +29 -0
- package/dist/db/connection.js +37 -0
- package/dist/db/index.js +43 -11
- package/dist/db/migrations.js +114 -22
- package/dist/delegation/manager.js +10 -4
- package/dist/index.js +39 -59
- package/dist/knowledge/manager.js +26 -12
- package/dist/mcp/handlers.d.ts +37 -0
- package/dist/mcp/handlers.js +520 -0
- package/dist/mcp/server.js +44 -858
- package/dist/mcp/tool-definitions.d.ts +1225 -0
- package/dist/mcp/tool-definitions.js +412 -0
- package/dist/mcp/validate.d.ts +23 -0
- package/dist/mcp/validate.js +65 -0
- package/dist/media/factory.js +18 -14
- package/dist/media/generators/dall-e.js +2 -2
- package/dist/media/generators/kling.js +4 -4
- package/dist/media/generators/lyria.js +1 -1
- package/dist/media/generators/nano-banana.d.ts +1 -1
- package/dist/media/generators/nano-banana.js +2 -2
- package/dist/media/generators/poll-util.js +4 -4
- package/dist/media/generators/recraft.js +3 -3
- package/dist/media/generators/runway.js +4 -4
- package/dist/media/generators/stable-diffusion.js +2 -2
- package/dist/media/generators/veo.js +1 -1
- package/dist/media/index.d.ts +2 -7
- package/dist/media/index.js +2 -2
- package/dist/media/store.d.ts +7 -0
- package/dist/media/store.js +18 -4
- package/dist/media/types.d.ts +22 -0
- package/dist/notifications/index.d.ts +2 -4
- package/dist/notifications/index.js +6 -19
- package/dist/notifications/ntfy.js +3 -3
- package/dist/observability/analytics.d.ts +1 -1
- package/dist/observability/analytics.js +41 -16
- package/dist/projects/index.d.ts +3 -2
- package/dist/projects/index.js +2 -2
- package/dist/projects/manager.d.ts +1 -7
- package/dist/projects/manager.js +66 -42
- package/dist/projects/router.d.ts +12 -0
- package/dist/projects/router.js +98 -45
- package/dist/service/install.js +15 -5
- package/dist/service/windows.js +1 -1
- package/dist/session/budget-guard.d.ts +20 -0
- package/dist/session/budget-guard.js +31 -0
- package/dist/session/circuit-breaker.d.ts +5 -3
- package/dist/session/circuit-breaker.js +45 -20
- package/dist/session/context-compactor.d.ts +32 -0
- package/dist/session/context-compactor.js +45 -0
- package/dist/session/context-monitor.js +2 -2
- package/dist/session/handoff.d.ts +21 -0
- package/dist/session/handoff.js +50 -0
- package/dist/session/manager.d.ts +21 -5
- package/dist/session/manager.js +166 -153
- package/dist/session/memory-store.d.ts +29 -0
- package/dist/session/memory-store.js +45 -0
- package/dist/session/message-queue.d.ts +28 -0
- package/dist/session/message-queue.js +52 -0
- package/dist/session/pending-dispatcher.d.ts +31 -0
- package/dist/session/pending-dispatcher.js +120 -0
- package/dist/session/pending-store.d.ts +60 -0
- package/dist/session/pending-store.js +118 -0
- package/dist/session/stale-session.d.ts +31 -0
- package/dist/session/stale-session.js +45 -0
- package/dist/session/subprocess.d.ts +3 -0
- package/dist/session/subprocess.js +54 -11
- package/dist/session/tab-store.d.ts +28 -0
- package/dist/session/tab-store.js +78 -0
- package/dist/tasks/scheduler.d.ts +13 -0
- package/dist/tasks/scheduler.js +97 -18
- package/dist/tasks/store.js +26 -12
- package/dist/timeline/logger.js +3 -1
- package/dist/timeline/query.js +15 -5
- package/dist/types.d.ts +49 -9
- package/dist/util/auto-heal.js +15 -5
- package/dist/util/install-info.js +3 -1
- package/dist/util/logger.d.ts +1 -1
- package/dist/util/logger.js +63 -24
- package/dist/util/paths.d.ts +2 -0
- package/dist/util/paths.js +16 -3
- package/dist/util/rate-limiter.js +8 -0
- package/dist/util/retry.js +1 -1
- package/dist/util/text.d.ts +21 -1
- package/dist/util/text.js +38 -8
- package/dist/voice/index.js +5 -1
- package/dist/voice/stt.js +14 -6
- package/dist/voice/tts.js +1 -1
- package/dist/watchers/scheduler.js +11 -5
- package/package.json +6 -1
- package/dist/session/tool-classifier.d.ts +0 -4
- package/dist/session/tool-classifier.js +0 -56
- 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/channels/loader.js
CHANGED
|
@@ -37,7 +37,6 @@ export async function loadCommunityChannels(ctx) {
|
|
|
37
37
|
continue;
|
|
38
38
|
if (!allowlist.includes(dir))
|
|
39
39
|
continue;
|
|
40
|
-
const channelName = dir.slice(CHANNEL_PREFIX.length);
|
|
41
40
|
const pkgPath = path.join(searchPath, dir);
|
|
42
41
|
try {
|
|
43
42
|
// Read package.json to find the main entry
|
|
@@ -48,8 +47,15 @@ export async function loadCommunityChannels(ctx) {
|
|
|
48
47
|
logger.warn(`Community channel ${dir}: entry point not found at ${entryPath}`);
|
|
49
48
|
continue;
|
|
50
49
|
}
|
|
51
|
-
// Dynamic import — community channels run with full daemon access
|
|
52
|
-
|
|
50
|
+
// Dynamic import — community channels run with full daemon access:
|
|
51
|
+
// they can read every config token (Telegram, Discord, WhatsApp,
|
|
52
|
+
// notification provider keys) AND every environment variable the
|
|
53
|
+
// daemon inherits (including SSH-agent sockets, GitHub tokens,
|
|
54
|
+
// anything sourced from your shell profile). A compromised
|
|
55
|
+
// community-channel package is effectively a root credential drop.
|
|
56
|
+
// Pin versions in package.json, audit before opting in, and treat
|
|
57
|
+
// every entry in `communityChannels` as a trust anchor.
|
|
58
|
+
logger.warn(`Loading community channel "${dir}" — this package will have FULL access to your channel tokens (Telegram/Discord/WhatsApp) and every environment variable inherited by the daemon (including SSH keys). Only enable if you trust the package author and have audited the version.`);
|
|
53
59
|
const module = await import(entryPath);
|
|
54
60
|
const ChannelClass = module.default || module[Object.keys(module)[0]];
|
|
55
61
|
if (!ChannelClass || typeof ChannelClass !== 'function') {
|
|
@@ -58,7 +64,10 @@ export async function loadCommunityChannels(ctx) {
|
|
|
58
64
|
}
|
|
59
65
|
const instance = new ChannelClass(ctx);
|
|
60
66
|
// Validate it implements Channel interface (duck typing)
|
|
61
|
-
if (!instance.id ||
|
|
67
|
+
if (!instance.id ||
|
|
68
|
+
!instance.name ||
|
|
69
|
+
typeof instance.start !== 'function' ||
|
|
70
|
+
typeof instance.stop !== 'function') {
|
|
62
71
|
logger.warn(`Community channel ${dir}: does not implement Channel interface`);
|
|
63
72
|
continue;
|
|
64
73
|
}
|
|
@@ -22,9 +22,20 @@ import { logger } from '../util/logger.js';
|
|
|
22
22
|
export async function processInboundMessage(opts) {
|
|
23
23
|
const { text, media, channelId, tabManager, voiceReplyMode, ttsProvider, userId, sendProgress, overrideTabName, onTextChunk, } = opts;
|
|
24
24
|
// 1. Parse tab name and prompt from the message text
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
const parsed = parseTabMessage(text);
|
|
26
|
+
const rawPrompt = parsed.prompt;
|
|
27
|
+
let tabName = parsed.tabName;
|
|
28
|
+
// Allow channel to override tab name (group tabs, thread tabs, etc.).
|
|
29
|
+
// Validate centrally so each channel doesn't need its own check — keeps the
|
|
30
|
+
// CLAUDE.md "validation is centralized" rule honest. `default` is allowed.
|
|
27
31
|
if (overrideTabName) {
|
|
32
|
+
if (overrideTabName !== 'default') {
|
|
33
|
+
const { validateTabName } = await import('../config.js');
|
|
34
|
+
const overrideErr = validateTabName(overrideTabName);
|
|
35
|
+
if (overrideErr) {
|
|
36
|
+
return { responseText: `Invalid tab name: ${overrideErr}`, tabName, isError: true };
|
|
37
|
+
}
|
|
38
|
+
}
|
|
28
39
|
tabName = overrideTabName;
|
|
29
40
|
}
|
|
30
41
|
if (!rawPrompt && media.length === 0) {
|
|
@@ -56,9 +67,7 @@ export async function processInboundMessage(opts) {
|
|
|
56
67
|
progressTracker.stop();
|
|
57
68
|
// 6. Build response text
|
|
58
69
|
const isError = result.error;
|
|
59
|
-
const responseText = isError
|
|
60
|
-
? `Error: ${result.text}`
|
|
61
|
-
: result.text || '(empty response)';
|
|
70
|
+
const responseText = isError ? `Error: ${result.text}` : result.text || '(empty response)';
|
|
62
71
|
// 7. TTS voice reply (shared logic)
|
|
63
72
|
let audioPath;
|
|
64
73
|
let voiceOnly = false;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Channel } from './types.js';
|
|
2
|
+
import type { MediaAttachment } from '../types.js';
|
|
2
3
|
/**
|
|
3
4
|
* ChannelRegistry manages the lifecycle of all channels.
|
|
4
5
|
* Channels register themselves, and the registry handles start/stop/broadcast.
|
|
@@ -7,7 +8,12 @@ export declare class ChannelRegistry {
|
|
|
7
8
|
private channels;
|
|
8
9
|
/** Register a channel */
|
|
9
10
|
register(channel: Channel): void;
|
|
10
|
-
/**
|
|
11
|
+
/**
|
|
12
|
+
* Start all registered channels in parallel. Each channel's start() is an
|
|
13
|
+
* independent network handshake (Telegram getMe, Discord login, WhatsApp
|
|
14
|
+
* Baileys boot) — running them sequentially adds 2-5s to cold start with no
|
|
15
|
+
* coordination benefit.
|
|
16
|
+
*/
|
|
11
17
|
start(): Promise<void>;
|
|
12
18
|
/** Stop all channels */
|
|
13
19
|
stop(): void;
|
|
@@ -17,4 +23,14 @@ export declare class ChannelRegistry {
|
|
|
17
23
|
getAll(): Channel[];
|
|
18
24
|
/** Broadcast a notification to all channels */
|
|
19
25
|
broadcastNotify(text: string, urgent?: boolean): Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* Broadcast a media attachment to every channel that supports it. Used by
|
|
28
|
+
* the pending-message dispatcher to deliver media queued by
|
|
29
|
+
* `beecork_send_media` and the `beecork_generate_*` tools — without this,
|
|
30
|
+
* those tools queue a row whose JSON envelope is never rendered.
|
|
31
|
+
*
|
|
32
|
+
* Channels that don't implement broadcastMedia get a sendNotification
|
|
33
|
+
* fallback so the user at least learns a file was generated.
|
|
34
|
+
*/
|
|
35
|
+
broadcastMedia(media: MediaAttachment): Promise<void>;
|
|
20
36
|
}
|
|
@@ -13,9 +13,14 @@ export class ChannelRegistry {
|
|
|
13
13
|
this.channels.set(channel.id, channel);
|
|
14
14
|
logger.info(`Channel registered: ${channel.name} (${channel.id})`);
|
|
15
15
|
}
|
|
16
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* Start all registered channels in parallel. Each channel's start() is an
|
|
18
|
+
* independent network handshake (Telegram getMe, Discord login, WhatsApp
|
|
19
|
+
* Baileys boot) — running them sequentially adds 2-5s to cold start with no
|
|
20
|
+
* coordination benefit.
|
|
21
|
+
*/
|
|
17
22
|
async start() {
|
|
18
|
-
|
|
23
|
+
await Promise.all(Array.from(this.channels.entries()).map(async ([id, channel]) => {
|
|
19
24
|
try {
|
|
20
25
|
await channel.start();
|
|
21
26
|
logger.info(`Channel started: ${channel.name}`);
|
|
@@ -23,7 +28,7 @@ export class ChannelRegistry {
|
|
|
23
28
|
catch (err) {
|
|
24
29
|
logger.error(`Failed to start channel ${id}:`, err);
|
|
25
30
|
}
|
|
26
|
-
}
|
|
31
|
+
}));
|
|
27
32
|
}
|
|
28
33
|
/** Stop all channels */
|
|
29
34
|
stop() {
|
|
@@ -46,6 +51,30 @@ export class ChannelRegistry {
|
|
|
46
51
|
}
|
|
47
52
|
/** Broadcast a notification to all channels */
|
|
48
53
|
async broadcastNotify(text, urgent) {
|
|
49
|
-
await Promise.all(Array.from(this.channels.values()).map(channel
|
|
54
|
+
await Promise.all(Array.from(this.channels.values()).map((channel) => channel
|
|
55
|
+
.sendNotification(text, urgent)
|
|
56
|
+
.catch((err) => logger.warn(`${channel.name} notify failed:`, err))));
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Broadcast a media attachment to every channel that supports it. Used by
|
|
60
|
+
* the pending-message dispatcher to deliver media queued by
|
|
61
|
+
* `beecork_send_media` and the `beecork_generate_*` tools — without this,
|
|
62
|
+
* those tools queue a row whose JSON envelope is never rendered.
|
|
63
|
+
*
|
|
64
|
+
* Channels that don't implement broadcastMedia get a sendNotification
|
|
65
|
+
* fallback so the user at least learns a file was generated.
|
|
66
|
+
*/
|
|
67
|
+
async broadcastMedia(media) {
|
|
68
|
+
await Promise.all(Array.from(this.channels.values()).map((channel) => {
|
|
69
|
+
if (channel.broadcastMedia) {
|
|
70
|
+
return channel
|
|
71
|
+
.broadcastMedia(media)
|
|
72
|
+
.catch((err) => logger.warn(`${channel.name} broadcastMedia failed:`, err));
|
|
73
|
+
}
|
|
74
|
+
const fallback = `📎 Media generated: ${media.filePath}${media.caption ? `\n${media.caption}` : ''}`;
|
|
75
|
+
return channel
|
|
76
|
+
.sendNotification(fallback)
|
|
77
|
+
.catch((err) => logger.warn(`${channel.name} media-fallback notify failed:`, err));
|
|
78
|
+
}));
|
|
50
79
|
}
|
|
51
80
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Send a tab-prefixed, chunked, retried response through any channel.
|
|
3
|
+
*
|
|
4
|
+
* Each channel just provides:
|
|
5
|
+
* - sendChunk(text): the platform-specific send call for one chunk
|
|
6
|
+
* - maxLength: the platform's per-message limit
|
|
7
|
+
* - retryLabel: label for logger (e.g. "telegram-send")
|
|
8
|
+
*
|
|
9
|
+
* Replaces near-identical per-channel implementations that had already
|
|
10
|
+
* drifted in subtle ways (different prefix gates, different retry envelopes).
|
|
11
|
+
*/
|
|
12
|
+
export declare function sendChunkedResponse(opts: {
|
|
13
|
+
text: string;
|
|
14
|
+
tabName?: string;
|
|
15
|
+
maxLength: number;
|
|
16
|
+
retryLabel: string;
|
|
17
|
+
retryDelays?: number[];
|
|
18
|
+
sendChunk: (chunk: string) => Promise<unknown>;
|
|
19
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { chunkText, formatTabbedResponse } from '../util/text.js';
|
|
2
|
+
import { retryWithBackoff } from '../util/retry.js';
|
|
3
|
+
/**
|
|
4
|
+
* Send a tab-prefixed, chunked, retried response through any channel.
|
|
5
|
+
*
|
|
6
|
+
* Each channel just provides:
|
|
7
|
+
* - sendChunk(text): the platform-specific send call for one chunk
|
|
8
|
+
* - maxLength: the platform's per-message limit
|
|
9
|
+
* - retryLabel: label for logger (e.g. "telegram-send")
|
|
10
|
+
*
|
|
11
|
+
* Replaces near-identical per-channel implementations that had already
|
|
12
|
+
* drifted in subtle ways (different prefix gates, different retry envelopes).
|
|
13
|
+
*/
|
|
14
|
+
export async function sendChunkedResponse(opts) {
|
|
15
|
+
const full = formatTabbedResponse(opts.text, opts.tabName);
|
|
16
|
+
const chunks = chunkText(full, opts.maxLength);
|
|
17
|
+
const delays = opts.retryDelays ?? [1000, 5000, 15000];
|
|
18
|
+
for (const chunk of chunks) {
|
|
19
|
+
await retryWithBackoff(() => opts.sendChunk(chunk), delays, opts.retryLabel);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -1,38 +1,45 @@
|
|
|
1
|
-
import type { Channel, ChannelContext,
|
|
2
|
-
/** Format tab status for Telegram display */
|
|
3
|
-
export declare function formatTabStatus(tabs: Array<{
|
|
4
|
-
name: string;
|
|
5
|
-
status: string;
|
|
6
|
-
lastActivityAt: string;
|
|
7
|
-
}>): string;
|
|
1
|
+
import type { Channel, ChannelContext, MediaAttachment, SendOptions } from './types.js';
|
|
8
2
|
export declare class TelegramChannel implements Channel {
|
|
9
3
|
readonly id = "telegram";
|
|
10
4
|
readonly name = "Telegram";
|
|
11
5
|
readonly maxMessageLength = 4096;
|
|
12
|
-
readonly supportsStreaming = true;
|
|
13
|
-
readonly supportsMedia = true;
|
|
14
6
|
private bot;
|
|
15
7
|
private ctx;
|
|
16
8
|
private activeChatIds;
|
|
17
|
-
private
|
|
18
|
-
private
|
|
9
|
+
private allowedUserIdSet;
|
|
10
|
+
private voice;
|
|
19
11
|
private botUserId;
|
|
20
12
|
private botUsername;
|
|
21
13
|
private mutedGroups;
|
|
22
14
|
private welcomeSent;
|
|
23
|
-
private
|
|
15
|
+
private pollingErrorTimes;
|
|
16
|
+
private pollingDegradedNotified;
|
|
24
17
|
constructor(ctx: ChannelContext);
|
|
25
18
|
start(): Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* Track polling errors over a 60s rolling window. If we see 5+ errors in 60s
|
|
21
|
+
* we surface "polling degraded" exactly once via the notify callback so the
|
|
22
|
+
* user knows Telegram is broken instead of silently failing.
|
|
23
|
+
*/
|
|
24
|
+
private recordPollingError;
|
|
26
25
|
stop(): void;
|
|
27
|
-
sendMessage(peerId: string, text: string,
|
|
26
|
+
sendMessage(peerId: string, text: string, _options?: SendOptions): Promise<void>;
|
|
28
27
|
sendNotification(message: string, _urgent?: boolean): Promise<void>;
|
|
28
|
+
/**
|
|
29
|
+
* Send a media file to every recipient the bot would notify. Used by the
|
|
30
|
+
* pending-message dispatcher to deliver media queued by MCP tools.
|
|
31
|
+
* Routes by attachment type to the right Telegram primitive (sendVoice for
|
|
32
|
+
* voice messages, sendPhoto for images, sendVideo for video, sendDocument
|
|
33
|
+
* for everything else).
|
|
34
|
+
*/
|
|
35
|
+
broadcastMedia(media: MediaAttachment): Promise<void>;
|
|
29
36
|
setTyping(peerId: string, active: boolean): Promise<void>;
|
|
30
|
-
onMessage(_handler: InboundMessageHandler): void;
|
|
31
37
|
private setupHandlers;
|
|
32
38
|
private handleCommand;
|
|
33
39
|
private handleMessage;
|
|
34
40
|
private sendResponse;
|
|
35
41
|
private sendWithRetry;
|
|
42
|
+
private sendWithRetryRaw;
|
|
36
43
|
private downloadTelegramFile;
|
|
37
44
|
private isAllowed;
|
|
38
45
|
private isAdmin;
|