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.
- package/README.md +52 -111
- package/dist/index.js +1394 -581
- package/package.json +26 -54
- package/dist/capabilities/index.d.ts +0 -3
- package/dist/capabilities/index.js +0 -2
- package/dist/capabilities/manager.d.ts +0 -13
- package/dist/capabilities/manager.js +0 -115
- package/dist/capabilities/packs.d.ts +0 -2
- package/dist/capabilities/packs.js +0 -54
- package/dist/capabilities/types.d.ts +0 -21
- package/dist/capabilities/types.js +0 -1
- package/dist/channels/admin.d.ts +0 -10
- package/dist/channels/admin.js +0 -20
- package/dist/channels/command-handler.d.ts +0 -19
- package/dist/channels/command-handler.js +0 -235
- package/dist/channels/discord.d.ts +0 -17
- package/dist/channels/discord.js +0 -266
- package/dist/channels/index.d.ts +0 -7
- package/dist/channels/index.js +0 -6
- package/dist/channels/loader.d.ts +0 -7
- package/dist/channels/loader.js +0 -87
- package/dist/channels/pipeline.d.ts +0 -50
- package/dist/channels/pipeline.js +0 -92
- package/dist/channels/registry.d.ts +0 -36
- package/dist/channels/registry.js +0 -80
- package/dist/channels/send-helpers.d.ts +0 -19
- package/dist/channels/send-helpers.js +0 -21
- package/dist/channels/telegram.d.ts +0 -47
- package/dist/channels/telegram.js +0 -649
- package/dist/channels/types.d.ts +0 -41
- package/dist/channels/types.js +0 -1
- package/dist/channels/voice-state.d.ts +0 -29
- package/dist/channels/voice-state.js +0 -45
- package/dist/channels/webhook.d.ts +0 -16
- package/dist/channels/webhook.js +0 -222
- package/dist/channels/whatsapp.d.ts +0 -28
- package/dist/channels/whatsapp.js +0 -323
- package/dist/cli/capabilities.d.ts +0 -3
- package/dist/cli/capabilities.js +0 -72
- package/dist/cli/channel.d.ts +0 -3
- package/dist/cli/channel.js +0 -202
- package/dist/cli/commands.d.ts +0 -15
- package/dist/cli/commands.js +0 -279
- package/dist/cli/doctor.d.ts +0 -1
- package/dist/cli/doctor.js +0 -208
- package/dist/cli/handoff.d.ts +0 -8
- package/dist/cli/handoff.js +0 -33
- package/dist/cli/helpers.d.ts +0 -2
- package/dist/cli/helpers.js +0 -21
- package/dist/cli/mcp.d.ts +0 -3
- package/dist/cli/mcp.js +0 -47
- package/dist/cli/media.d.ts +0 -2
- package/dist/cli/media.js +0 -125
- package/dist/cli/setup.d.ts +0 -1
- package/dist/cli/setup.js +0 -239
- package/dist/cli/store.d.ts +0 -3
- package/dist/cli/store.js +0 -108
- package/dist/config.d.ts +0 -11
- package/dist/config.js +0 -115
- package/dist/daemon.d.ts +0 -1
- package/dist/daemon.js +0 -345
- package/dist/dashboard/html.d.ts +0 -1
- package/dist/dashboard/html.js +0 -849
- package/dist/dashboard/routes.d.ts +0 -17
- package/dist/dashboard/routes.js +0 -623
- package/dist/dashboard/server.d.ts +0 -1
- package/dist/dashboard/server.js +0 -104
- package/dist/db/connection.d.ts +0 -29
- package/dist/db/connection.js +0 -37
- package/dist/db/index.d.ts +0 -14
- package/dist/db/index.js +0 -127
- package/dist/db/migrations.d.ts +0 -2
- package/dist/db/migrations.js +0 -384
- package/dist/delegation/manager.d.ts +0 -18
- package/dist/delegation/manager.js +0 -89
- package/dist/index.d.ts +0 -2
- package/dist/knowledge/index.d.ts +0 -2
- package/dist/knowledge/index.js +0 -1
- package/dist/knowledge/manager.d.ts +0 -17
- package/dist/knowledge/manager.js +0 -160
- package/dist/knowledge/types.d.ts +0 -7
- package/dist/knowledge/types.js +0 -1
- package/dist/mcp/handlers.d.ts +0 -37
- package/dist/mcp/handlers.js +0 -520
- package/dist/mcp/server.d.ts +0 -2
- package/dist/mcp/server.js +0 -86
- package/dist/mcp/tool-definitions.d.ts +0 -1225
- package/dist/mcp/tool-definitions.js +0 -412
- package/dist/mcp/validate.d.ts +0 -23
- package/dist/mcp/validate.js +0 -65
- package/dist/media/factory.d.ts +0 -6
- package/dist/media/factory.js +0 -36
- package/dist/media/generators/dall-e.d.ts +0 -10
- package/dist/media/generators/dall-e.js +0 -41
- package/dist/media/generators/elevenlabs-music.d.ts +0 -9
- package/dist/media/generators/elevenlabs-music.js +0 -34
- package/dist/media/generators/elevenlabs-sfx.d.ts +0 -9
- package/dist/media/generators/elevenlabs-sfx.js +0 -33
- package/dist/media/generators/kling.d.ts +0 -9
- package/dist/media/generators/kling.js +0 -47
- package/dist/media/generators/lyria.d.ts +0 -10
- package/dist/media/generators/lyria.js +0 -41
- package/dist/media/generators/nano-banana.d.ts +0 -10
- package/dist/media/generators/nano-banana.js +0 -45
- package/dist/media/generators/poll-util.d.ts +0 -12
- package/dist/media/generators/poll-util.js +0 -22
- package/dist/media/generators/recraft.d.ts +0 -9
- package/dist/media/generators/recraft.js +0 -52
- package/dist/media/generators/runway.d.ts +0 -9
- package/dist/media/generators/runway.js +0 -49
- package/dist/media/generators/stable-diffusion.d.ts +0 -9
- package/dist/media/generators/stable-diffusion.js +0 -35
- package/dist/media/generators/veo.d.ts +0 -9
- package/dist/media/generators/veo.js +0 -32
- package/dist/media/index.d.ts +0 -6
- package/dist/media/index.js +0 -8
- package/dist/media/store.d.ts +0 -17
- package/dist/media/store.js +0 -69
- package/dist/media/types.d.ts +0 -43
- package/dist/media/types.js +0 -1
- package/dist/notifications/index.d.ts +0 -4
- package/dist/notifications/index.js +0 -21
- package/dist/notifications/ntfy.d.ts +0 -9
- package/dist/notifications/ntfy.js +0 -25
- package/dist/notifications/pushover.d.ts +0 -9
- package/dist/notifications/pushover.js +0 -26
- package/dist/notifications/types.d.ts +0 -5
- package/dist/notifications/types.js +0 -1
- package/dist/notifications/webhook-provider.d.ts +0 -9
- package/dist/notifications/webhook-provider.js +0 -24
- package/dist/observability/analytics.d.ts +0 -30
- package/dist/observability/analytics.js +0 -118
- package/dist/projects/index.d.ts +0 -4
- package/dist/projects/index.js +0 -2
- package/dist/projects/manager.d.ts +0 -13
- package/dist/projects/manager.js +0 -166
- package/dist/projects/router.d.ts +0 -17
- package/dist/projects/router.js +0 -246
- package/dist/projects/types.d.ts +0 -19
- package/dist/projects/types.js +0 -1
- package/dist/service/install.d.ts +0 -4
- package/dist/service/install.js +0 -146
- package/dist/service/templates.d.ts +0 -4
- package/dist/service/templates.js +0 -52
- package/dist/service/windows.d.ts +0 -8
- package/dist/service/windows.js +0 -59
- package/dist/session/budget-guard.d.ts +0 -20
- package/dist/session/budget-guard.js +0 -31
- package/dist/session/circuit-breaker.d.ts +0 -19
- package/dist/session/circuit-breaker.js +0 -91
- package/dist/session/context-compactor.d.ts +0 -32
- package/dist/session/context-compactor.js +0 -45
- package/dist/session/context-monitor.d.ts +0 -15
- package/dist/session/context-monitor.js +0 -44
- package/dist/session/handoff.d.ts +0 -21
- package/dist/session/handoff.js +0 -50
- package/dist/session/manager.d.ts +0 -60
- package/dist/session/manager.js +0 -379
- package/dist/session/memory-store.d.ts +0 -29
- package/dist/session/memory-store.js +0 -45
- package/dist/session/message-queue.d.ts +0 -28
- package/dist/session/message-queue.js +0 -52
- package/dist/session/pending-dispatcher.d.ts +0 -31
- package/dist/session/pending-dispatcher.js +0 -120
- package/dist/session/pending-store.d.ts +0 -60
- package/dist/session/pending-store.js +0 -118
- package/dist/session/stale-session.d.ts +0 -31
- package/dist/session/stale-session.js +0 -45
- package/dist/session/subprocess.d.ts +0 -25
- package/dist/session/subprocess.js +0 -189
- package/dist/session/tab-store.d.ts +0 -28
- package/dist/session/tab-store.js +0 -78
- package/dist/tasks/scheduler.d.ts +0 -45
- package/dist/tasks/scheduler.js +0 -329
- package/dist/tasks/store.d.ts +0 -11
- package/dist/tasks/store.js +0 -103
- package/dist/timeline/index.d.ts +0 -3
- package/dist/timeline/index.js +0 -2
- package/dist/timeline/logger.d.ts +0 -8
- package/dist/timeline/logger.js +0 -11
- package/dist/timeline/query.d.ts +0 -11
- package/dist/timeline/query.js +0 -67
- package/dist/timeline/types.d.ts +0 -12
- package/dist/timeline/types.js +0 -1
- package/dist/types.d.ts +0 -216
- package/dist/types.js +0 -3
- package/dist/util/auto-heal.d.ts +0 -43
- package/dist/util/auto-heal.js +0 -107
- package/dist/util/install-info.d.ts +0 -37
- package/dist/util/install-info.js +0 -80
- package/dist/util/logger.d.ts +0 -18
- package/dist/util/logger.js +0 -118
- package/dist/util/paths.d.ts +0 -13
- package/dist/util/paths.js +0 -55
- package/dist/util/platform.d.ts +0 -2
- package/dist/util/platform.js +0 -11
- package/dist/util/progress.d.ts +0 -34
- package/dist/util/progress.js +0 -133
- package/dist/util/rate-limiter.d.ts +0 -14
- package/dist/util/rate-limiter.js +0 -50
- package/dist/util/retry.d.ts +0 -2
- package/dist/util/retry.js +0 -18
- package/dist/util/text.d.ts +0 -32
- package/dist/util/text.js +0 -101
- package/dist/version.d.ts +0 -1
- package/dist/version.js +0 -4
- package/dist/voice/index.d.ts +0 -17
- package/dist/voice/index.js +0 -42
- package/dist/voice/stt.d.ts +0 -16
- package/dist/voice/stt.js +0 -57
- package/dist/voice/tts.d.ts +0 -23
- package/dist/voice/tts.js +0 -84
- package/dist/watchers/evaluator.d.ts +0 -5
- package/dist/watchers/evaluator.js +0 -45
- package/dist/watchers/index.d.ts +0 -4
- package/dist/watchers/index.js +0 -3
- package/dist/watchers/scheduler.d.ts +0 -20
- package/dist/watchers/scheduler.js +0 -183
- package/dist/watchers/store.d.ts +0 -10
- package/dist/watchers/store.js +0 -56
- package/dist/watchers/types.d.ts +0 -15
- package/dist/watchers/types.js +0 -1
- package/scripts/postinstall.mjs +0 -50
|
@@ -1,323 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* WhatsApp integration via @whiskeysockets/baileys.
|
|
3
|
-
*
|
|
4
|
-
* baileys is a peer-optional dependency loaded by dynamic import, and its
|
|
5
|
-
* runtime types are intentionally loose (the lib uses heavy union/index
|
|
6
|
-
* types that don't survive serialization across the dynamic-import boundary).
|
|
7
|
-
* Trying to type every variant of Baileys' message/connection shapes would
|
|
8
|
-
* either pull baileys into the static graph (bloating non-WhatsApp installs)
|
|
9
|
-
* or require maintaining a parallel shim. We accept `any` at this trust
|
|
10
|
-
* boundary; runtime validation lives in the descriptors table inside
|
|
11
|
-
* messages.upsert.
|
|
12
|
-
*/
|
|
13
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
14
|
-
import fs from 'node:fs';
|
|
15
|
-
import { logger } from '../util/logger.js';
|
|
16
|
-
import { saveMedia, isOversized } from '../media/store.js';
|
|
17
|
-
import { sendChunkedResponse } from './send-helpers.js';
|
|
18
|
-
import { inboundLimiter } from '../util/rate-limiter.js';
|
|
19
|
-
import { processInboundMessage } from './pipeline.js';
|
|
20
|
-
import { isChannelAdmin } from './admin.js';
|
|
21
|
-
import { VoiceState } from './voice-state.js';
|
|
22
|
-
const WHATSAPP_MAX_LENGTH = 8192;
|
|
23
|
-
export class WhatsAppChannel {
|
|
24
|
-
id = 'whatsapp';
|
|
25
|
-
name = 'WhatsApp';
|
|
26
|
-
maxMessageLength = WHATSAPP_MAX_LENGTH;
|
|
27
|
-
sock = null;
|
|
28
|
-
ctx;
|
|
29
|
-
allowedNumbers;
|
|
30
|
-
reconnectAttempts = 0;
|
|
31
|
-
maxReconnectAttempts = 10;
|
|
32
|
-
backoffDelays = [1000, 5000, 15000, 30000, 60000];
|
|
33
|
-
voice = new VoiceState('whatsapp');
|
|
34
|
-
constructor(ctx) {
|
|
35
|
-
this.ctx = ctx;
|
|
36
|
-
this.allowedNumbers = new Set(ctx.config.whatsapp?.allowedNumbers ?? []);
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Schedule the next reconnect with exponential backoff. Unlike the previous
|
|
40
|
-
* inline setTimeout, this path retries when `start()` itself rejects (auth
|
|
41
|
-
* failure, baileys init throw, etc.) instead of going permanently silent
|
|
42
|
-
* after a single failed attempt.
|
|
43
|
-
*/
|
|
44
|
-
scheduleReconnect() {
|
|
45
|
-
this.reconnectAttempts++;
|
|
46
|
-
if (this.reconnectAttempts > this.maxReconnectAttempts) {
|
|
47
|
-
logger.error(`WhatsApp reconnect failed after ${this.maxReconnectAttempts} attempts, giving up`);
|
|
48
|
-
this.ctx
|
|
49
|
-
.notifyCallback?.('⚠️ WhatsApp disconnected after 10 reconnection attempts. Restart daemon to reconnect.')
|
|
50
|
-
.catch((err) => logger.error('Failed to send WhatsApp disconnect notification:', err));
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
const delayIdx = Math.min(this.reconnectAttempts - 1, this.backoffDelays.length - 1);
|
|
54
|
-
const delay = this.backoffDelays[delayIdx];
|
|
55
|
-
logger.warn(`WhatsApp connection closed, reconnecting in ${delay / 1000}s (attempt ${this.reconnectAttempts}/${this.maxReconnectAttempts})...`);
|
|
56
|
-
setTimeout(() => {
|
|
57
|
-
this.start().catch((err) => {
|
|
58
|
-
logger.error('WhatsApp reconnect attempt failed:', err);
|
|
59
|
-
// Recurse via scheduleReconnect so backoff keeps escalating instead of
|
|
60
|
-
// silently dropping the reconnect chain after one failed start().
|
|
61
|
-
this.scheduleReconnect();
|
|
62
|
-
});
|
|
63
|
-
}, delay);
|
|
64
|
-
}
|
|
65
|
-
async start() {
|
|
66
|
-
// Initialize voice providers (STT + TTS)
|
|
67
|
-
this.voice.init(this.ctx.config);
|
|
68
|
-
try {
|
|
69
|
-
const { default: makeWASocket, useMultiFileAuthState, DisconnectReason, downloadMediaMessage, fetchLatestBaileysVersion, } = await import('@whiskeysockets/baileys');
|
|
70
|
-
const { getWhatsappSessionPath } = await import('../util/paths.js');
|
|
71
|
-
// Use the centralized path helper — the previous fallback hard-coded
|
|
72
|
-
// process.env.HOME which bypassed BEECORK_HOME for tests/isolation.
|
|
73
|
-
const sessionPath = this.ctx.config.whatsapp?.sessionPath ?? getWhatsappSessionPath();
|
|
74
|
-
fs.mkdirSync(sessionPath, { recursive: true, mode: 0o700 });
|
|
75
|
-
const { state, saveCreds } = await useMultiFileAuthState(sessionPath);
|
|
76
|
-
const { version } = await fetchLatestBaileysVersion().catch(() => ({ version: undefined }));
|
|
77
|
-
const pino = (await import('pino')).default;
|
|
78
|
-
this.sock = makeWASocket({
|
|
79
|
-
auth: state,
|
|
80
|
-
version,
|
|
81
|
-
logger: pino({ level: 'silent' }),
|
|
82
|
-
});
|
|
83
|
-
const sock = this.sock;
|
|
84
|
-
sock.ev.on('creds.update', saveCreds);
|
|
85
|
-
sock.ev.on('connection.update', async (update) => {
|
|
86
|
-
const { connection, lastDisconnect, qr } = update;
|
|
87
|
-
if (qr) {
|
|
88
|
-
try {
|
|
89
|
-
const qrcodeTerminal = await import('qrcode-terminal');
|
|
90
|
-
(qrcodeTerminal.default || qrcodeTerminal).generate(qr, { small: true });
|
|
91
|
-
logger.info('WhatsApp QR code displayed — scan with your phone');
|
|
92
|
-
}
|
|
93
|
-
catch {
|
|
94
|
-
logger.warn('WhatsApp QR code available but could not render. Install qrcode-terminal.');
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
if (connection === 'close') {
|
|
98
|
-
const reason = lastDisconnect?.error?.output?.statusCode;
|
|
99
|
-
if (reason !== DisconnectReason.loggedOut) {
|
|
100
|
-
this.scheduleReconnect();
|
|
101
|
-
}
|
|
102
|
-
else {
|
|
103
|
-
logger.error('WhatsApp logged out. Please re-scan QR code.');
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
else if (connection === 'open') {
|
|
107
|
-
this.reconnectAttempts = 0;
|
|
108
|
-
logger.info('WhatsApp connected');
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
|
-
sock.ev.on('messages.upsert', async (m) => {
|
|
112
|
-
const msg = m.messages[0];
|
|
113
|
-
if (!msg?.message || msg.key.fromMe)
|
|
114
|
-
return;
|
|
115
|
-
const sender = msg.key.remoteJid;
|
|
116
|
-
if (!sender || !this.isAllowed(sender))
|
|
117
|
-
return;
|
|
118
|
-
// Rate limit check
|
|
119
|
-
if (!inboundLimiter.check(this.id)) {
|
|
120
|
-
await sock
|
|
121
|
-
.sendMessage(sender, {
|
|
122
|
-
text: "I'm receiving too many messages right now. Please wait a moment.",
|
|
123
|
-
})
|
|
124
|
-
.catch(() => { });
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
const text = msg.message.conversation ||
|
|
128
|
-
msg.message.extendedTextMessage?.text ||
|
|
129
|
-
msg.message.imageMessage?.caption ||
|
|
130
|
-
msg.message.videoMessage?.caption ||
|
|
131
|
-
'';
|
|
132
|
-
const descriptors = [
|
|
133
|
-
{
|
|
134
|
-
key: 'imageMessage',
|
|
135
|
-
build: (m, buf) => ({
|
|
136
|
-
type: 'image',
|
|
137
|
-
mimeType: m.imageMessage.mimetype || 'image/jpeg',
|
|
138
|
-
filePath: saveMedia(buf, 'jpg'),
|
|
139
|
-
}),
|
|
140
|
-
},
|
|
141
|
-
{
|
|
142
|
-
key: 'audioMessage',
|
|
143
|
-
build: (m, buf) => {
|
|
144
|
-
const ext = m.audioMessage.ptt ? 'ogg' : 'mp3';
|
|
145
|
-
return {
|
|
146
|
-
type: m.audioMessage.ptt ? 'voice' : 'audio',
|
|
147
|
-
mimeType: m.audioMessage.mimetype || 'audio/ogg',
|
|
148
|
-
filePath: saveMedia(buf, ext),
|
|
149
|
-
duration: m.audioMessage.seconds ?? undefined,
|
|
150
|
-
};
|
|
151
|
-
},
|
|
152
|
-
},
|
|
153
|
-
{
|
|
154
|
-
key: 'documentMessage',
|
|
155
|
-
build: (m, buf) => {
|
|
156
|
-
const ext = m.documentMessage.fileName?.split('.').pop() || 'bin';
|
|
157
|
-
return {
|
|
158
|
-
type: 'document',
|
|
159
|
-
mimeType: m.documentMessage.mimetype || 'application/octet-stream',
|
|
160
|
-
filePath: saveMedia(buf, ext, m.documentMessage.fileName ?? undefined),
|
|
161
|
-
fileName: m.documentMessage.fileName ?? undefined,
|
|
162
|
-
};
|
|
163
|
-
},
|
|
164
|
-
},
|
|
165
|
-
{
|
|
166
|
-
key: 'videoMessage',
|
|
167
|
-
build: (m, buf) => ({
|
|
168
|
-
type: 'video',
|
|
169
|
-
mimeType: m.videoMessage.mimetype || 'video/mp4',
|
|
170
|
-
filePath: saveMedia(buf, 'mp4'),
|
|
171
|
-
duration: m.videoMessage.seconds ?? undefined,
|
|
172
|
-
}),
|
|
173
|
-
},
|
|
174
|
-
];
|
|
175
|
-
const waDownloadTasks = [];
|
|
176
|
-
for (const d of descriptors) {
|
|
177
|
-
if (!msg.message[d.key])
|
|
178
|
-
continue;
|
|
179
|
-
waDownloadTasks.push(downloadMediaMessage(msg, 'buffer', {})
|
|
180
|
-
.then((buffer) => {
|
|
181
|
-
if (!buffer || isOversized(buffer.length))
|
|
182
|
-
return null;
|
|
183
|
-
try {
|
|
184
|
-
return d.build(msg.message, buffer);
|
|
185
|
-
}
|
|
186
|
-
catch {
|
|
187
|
-
return null;
|
|
188
|
-
}
|
|
189
|
-
})
|
|
190
|
-
.catch(() => null));
|
|
191
|
-
}
|
|
192
|
-
const waResults = await Promise.allSettled(waDownloadTasks);
|
|
193
|
-
const media = waResults
|
|
194
|
-
.filter((r) => r.status === 'fulfilled' && r.value !== null)
|
|
195
|
-
.map((r) => r.value);
|
|
196
|
-
// Transcribe voice messages if STT is configured
|
|
197
|
-
await this.voice.transcribe(media);
|
|
198
|
-
if (!text && media.length === 0)
|
|
199
|
-
return;
|
|
200
|
-
try {
|
|
201
|
-
const waUserId = sender.replace('@s.whatsapp.net', '');
|
|
202
|
-
// Shared command handler (covers /tabs, /stop, /projects, /project, /newproject, /close, /fresh, etc.)
|
|
203
|
-
if (text.startsWith('/')) {
|
|
204
|
-
const { handleSharedCommand } = await import('./command-handler.js');
|
|
205
|
-
const cmdResult = await handleSharedCommand({
|
|
206
|
-
userId: waUserId,
|
|
207
|
-
text,
|
|
208
|
-
isAdmin: isChannelAdmin(this.allowedNumbers, waUserId, this.ctx.config.whatsapp?.adminNumber),
|
|
209
|
-
channelId: 'whatsapp',
|
|
210
|
-
}, this.ctx.tabManager);
|
|
211
|
-
if (cmdResult.handled) {
|
|
212
|
-
if (cmdResult.response)
|
|
213
|
-
await sock.sendMessage(sender, { text: cmdResult.response });
|
|
214
|
-
return;
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
await sock.sendPresenceUpdate('composing', sender).catch(() => { });
|
|
218
|
-
// Shared message pipeline
|
|
219
|
-
const pipelineResult = await processInboundMessage({
|
|
220
|
-
text,
|
|
221
|
-
media,
|
|
222
|
-
channelId: 'whatsapp',
|
|
223
|
-
tabManager: this.ctx.tabManager,
|
|
224
|
-
voiceReplyMode: this.ctx.config.voice?.replyMode,
|
|
225
|
-
ttsProvider: this.voice.tts,
|
|
226
|
-
userId: waUserId,
|
|
227
|
-
sendProgress: (msg) => {
|
|
228
|
-
sock.sendMessage(sender, { text: msg }).catch(() => { });
|
|
229
|
-
},
|
|
230
|
-
});
|
|
231
|
-
await sock.sendPresenceUpdate('paused', sender).catch(() => { });
|
|
232
|
-
// Empty result means no prompt and no media
|
|
233
|
-
if (!pipelineResult.responseText)
|
|
234
|
-
return;
|
|
235
|
-
// Send voice reply if TTS generated audio
|
|
236
|
-
if (pipelineResult.audioPath) {
|
|
237
|
-
await sock.sendMessage(sender, {
|
|
238
|
-
audio: { url: pipelineResult.audioPath },
|
|
239
|
-
mimetype: 'audio/ogg; codecs=opus',
|
|
240
|
-
ptt: true,
|
|
241
|
-
});
|
|
242
|
-
if (pipelineResult.voiceOnly)
|
|
243
|
-
return;
|
|
244
|
-
}
|
|
245
|
-
await this.sendResponse(sender, pipelineResult.responseText, pipelineResult.tabName);
|
|
246
|
-
}
|
|
247
|
-
catch (err) {
|
|
248
|
-
logger.error('WhatsApp message handler error:', err);
|
|
249
|
-
await sock
|
|
250
|
-
.sendMessage(sender, {
|
|
251
|
-
text: 'Something went wrong processing your message. Check daemon logs for details.',
|
|
252
|
-
})
|
|
253
|
-
.catch((sendErr) => logger.error('WhatsApp: failed to send fallback error message:', sendErr));
|
|
254
|
-
}
|
|
255
|
-
});
|
|
256
|
-
}
|
|
257
|
-
catch (err) {
|
|
258
|
-
logger.error('Failed to start WhatsApp client:', err);
|
|
259
|
-
throw err;
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
stop() {
|
|
263
|
-
const sock = this.sock;
|
|
264
|
-
if (sock) {
|
|
265
|
-
sock.end(undefined);
|
|
266
|
-
this.sock = null;
|
|
267
|
-
}
|
|
268
|
-
logger.info('WhatsApp client stopped');
|
|
269
|
-
}
|
|
270
|
-
async sendMessage(peerId, text, _options) {
|
|
271
|
-
const sock = this.sock;
|
|
272
|
-
if (!sock)
|
|
273
|
-
return;
|
|
274
|
-
await sendChunkedResponse({
|
|
275
|
-
text,
|
|
276
|
-
maxLength: WHATSAPP_MAX_LENGTH,
|
|
277
|
-
retryLabel: 'whatsapp-send',
|
|
278
|
-
sendChunk: (chunk) => sock.sendMessage(peerId, { text: chunk }),
|
|
279
|
-
});
|
|
280
|
-
}
|
|
281
|
-
async sendNotification(message, _urgent) {
|
|
282
|
-
const sock = this.sock;
|
|
283
|
-
if (!sock)
|
|
284
|
-
return;
|
|
285
|
-
for (const number of this.allowedNumbers) {
|
|
286
|
-
try {
|
|
287
|
-
await sock.sendMessage(`${number}@s.whatsapp.net`, { text: message });
|
|
288
|
-
}
|
|
289
|
-
catch (err) {
|
|
290
|
-
logger.error(`Failed to send WhatsApp notification to ${number}:`, err);
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
async setTyping(peerId, active) {
|
|
295
|
-
const sock = this.sock;
|
|
296
|
-
if (!sock)
|
|
297
|
-
return;
|
|
298
|
-
const status = active ? 'composing' : 'paused';
|
|
299
|
-
await sock.sendPresenceUpdate(status, peerId).catch(() => { });
|
|
300
|
-
}
|
|
301
|
-
// ─── Private ───
|
|
302
|
-
async sendResponse(jid, text, tabName) {
|
|
303
|
-
const sock = this.sock;
|
|
304
|
-
try {
|
|
305
|
-
await sendChunkedResponse({
|
|
306
|
-
text,
|
|
307
|
-
tabName,
|
|
308
|
-
maxLength: WHATSAPP_MAX_LENGTH,
|
|
309
|
-
retryLabel: 'whatsapp-send',
|
|
310
|
-
sendChunk: (chunk) => sock.sendMessage(jid, { text: chunk }),
|
|
311
|
-
});
|
|
312
|
-
}
|
|
313
|
-
catch (err) {
|
|
314
|
-
logger.error(`WhatsApp delivery failed for ${jid}:`, err);
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
isAllowed(jid) {
|
|
318
|
-
if (this.allowedNumbers.size === 0)
|
|
319
|
-
return false;
|
|
320
|
-
const number = jid.replace('@s.whatsapp.net', '');
|
|
321
|
-
return this.allowedNumbers.has(number);
|
|
322
|
-
}
|
|
323
|
-
}
|
package/dist/cli/capabilities.js
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import readline from 'node:readline';
|
|
2
|
-
function ask(rl, question, defaultValue) {
|
|
3
|
-
const prompt = defaultValue ? `${question} [${defaultValue}]: ` : `${question}: `;
|
|
4
|
-
return new Promise((r) => rl.question(prompt, (a) => r(a.trim() || defaultValue || '')));
|
|
5
|
-
}
|
|
6
|
-
export async function enableCapability(packId) {
|
|
7
|
-
const { getAvailablePacks, isEnabled, enablePack } = await import('../capabilities/index.js');
|
|
8
|
-
const packs = getAvailablePacks();
|
|
9
|
-
const pack = packs.find((p) => p.id === packId);
|
|
10
|
-
if (!pack) {
|
|
11
|
-
console.log(`Unknown capability: "${packId}"`);
|
|
12
|
-
console.log('Available: ' + packs.map((p) => p.id).join(', '));
|
|
13
|
-
return;
|
|
14
|
-
}
|
|
15
|
-
if (isEnabled(packId)) {
|
|
16
|
-
console.log(`${pack.name} is already enabled. Re-configuring...`);
|
|
17
|
-
}
|
|
18
|
-
console.log(`\n${pack.name}`);
|
|
19
|
-
console.log(` ${pack.description}\n`);
|
|
20
|
-
let apiKey;
|
|
21
|
-
if (pack.requiresApiKey) {
|
|
22
|
-
if (pack.setupUrl) {
|
|
23
|
-
console.log(` Setup guide: ${pack.setupUrl}\n`);
|
|
24
|
-
}
|
|
25
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
26
|
-
apiKey = await ask(rl, ` ${pack.apiKeyHint || 'API key'}`);
|
|
27
|
-
rl.close();
|
|
28
|
-
if (!apiKey) {
|
|
29
|
-
console.log(' No API key provided. Cancelled.');
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
enablePack(packId, apiKey);
|
|
34
|
-
console.log(`\n✓ ${pack.name} enabled.`);
|
|
35
|
-
console.log(' Restart daemon to activate: beecork stop && beecork start\n');
|
|
36
|
-
}
|
|
37
|
-
export async function listCapabilities() {
|
|
38
|
-
const { getAvailablePacks, isEnabled } = await import('../capabilities/index.js');
|
|
39
|
-
const packs = getAvailablePacks();
|
|
40
|
-
console.log('\nCapabilities:\n');
|
|
41
|
-
const categories = ['productivity', 'development', 'data', 'web'];
|
|
42
|
-
const categoryNames = {
|
|
43
|
-
productivity: 'Productivity',
|
|
44
|
-
development: 'Development',
|
|
45
|
-
data: 'Data',
|
|
46
|
-
web: 'Web',
|
|
47
|
-
};
|
|
48
|
-
for (const category of categories) {
|
|
49
|
-
const categoryPacks = packs.filter((p) => p.category === category);
|
|
50
|
-
if (categoryPacks.length === 0)
|
|
51
|
-
continue;
|
|
52
|
-
console.log(` ${categoryNames[category]}:`);
|
|
53
|
-
for (const pack of categoryPacks) {
|
|
54
|
-
const status = isEnabled(pack.id) ? '✓' : '○';
|
|
55
|
-
const keyNeeded = pack.requiresApiKey ? ' (needs API key)' : '';
|
|
56
|
-
console.log(` ${status} ${pack.id} — ${pack.name}${keyNeeded}`);
|
|
57
|
-
}
|
|
58
|
-
console.log('');
|
|
59
|
-
}
|
|
60
|
-
console.log(' Enable: beecork enable <name>');
|
|
61
|
-
console.log(' Example: beecork enable github\n');
|
|
62
|
-
}
|
|
63
|
-
export async function disableCapability(packId) {
|
|
64
|
-
const { disablePack, isEnabled } = await import('../capabilities/index.js');
|
|
65
|
-
if (!isEnabled(packId)) {
|
|
66
|
-
console.log(`${packId} is not enabled.`);
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
disablePack(packId);
|
|
70
|
-
console.log(`\n✓ ${packId} disabled.`);
|
|
71
|
-
console.log(' Restart daemon to apply: beecork stop && beecork start\n');
|
|
72
|
-
}
|
package/dist/cli/channel.d.ts
DELETED
package/dist/cli/channel.js
DELETED
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
import { execFileSync } from 'node:child_process';
|
|
2
|
-
import fs from 'node:fs';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
const CHANNEL_PREFIX = 'beecork-channel-';
|
|
5
|
-
const SAFE_NPM_PACKAGE = /^[@a-zA-Z0-9_/.-]+$/;
|
|
6
|
-
export function channelInstall(packageName) {
|
|
7
|
-
// Normalize name
|
|
8
|
-
const fullName = packageName.startsWith(CHANNEL_PREFIX)
|
|
9
|
-
? packageName
|
|
10
|
-
: `${CHANNEL_PREFIX}${packageName}`;
|
|
11
|
-
if (!SAFE_NPM_PACKAGE.test(fullName)) {
|
|
12
|
-
console.error(`Invalid package name: ${fullName}`);
|
|
13
|
-
process.exit(1);
|
|
14
|
-
}
|
|
15
|
-
console.log(`Installing channel: ${fullName}...`);
|
|
16
|
-
try {
|
|
17
|
-
execFileSync('npm', ['install', '-g', fullName], { stdio: 'inherit' });
|
|
18
|
-
console.log(`\nChannel "${fullName}" installed.`);
|
|
19
|
-
console.log('Restart the daemon to activate: beecork stop && beecork start');
|
|
20
|
-
}
|
|
21
|
-
catch {
|
|
22
|
-
console.error(`Failed to install ${fullName}. Check the package name and try again.`);
|
|
23
|
-
process.exit(1);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
export function channelCreate(name) {
|
|
27
|
-
const pkgName = `${CHANNEL_PREFIX}${name}`;
|
|
28
|
-
const dir = pkgName;
|
|
29
|
-
if (fs.existsSync(dir)) {
|
|
30
|
-
console.error(`Directory "${dir}" already exists.`);
|
|
31
|
-
process.exit(1);
|
|
32
|
-
}
|
|
33
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
34
|
-
fs.mkdirSync(path.join(dir, 'src'), { recursive: true });
|
|
35
|
-
// package.json
|
|
36
|
-
fs.writeFileSync(path.join(dir, 'package.json'), JSON.stringify({
|
|
37
|
-
name: pkgName,
|
|
38
|
-
version: '0.1.0',
|
|
39
|
-
description: `Beecork channel: ${name}`,
|
|
40
|
-
main: 'dist/index.js',
|
|
41
|
-
type: 'module',
|
|
42
|
-
scripts: {
|
|
43
|
-
build: 'tsc',
|
|
44
|
-
dev: 'tsc --watch',
|
|
45
|
-
},
|
|
46
|
-
keywords: ['beecork', 'beecork-channel', name],
|
|
47
|
-
peerDependencies: {
|
|
48
|
-
beecork: '>=0.4.0',
|
|
49
|
-
},
|
|
50
|
-
devDependencies: {
|
|
51
|
-
typescript: '^5.7.0',
|
|
52
|
-
'@types/node': '^22.0.0',
|
|
53
|
-
},
|
|
54
|
-
}, null, 2) + '\n');
|
|
55
|
-
// tsconfig.json
|
|
56
|
-
fs.writeFileSync(path.join(dir, 'tsconfig.json'), JSON.stringify({
|
|
57
|
-
compilerOptions: {
|
|
58
|
-
target: 'ES2022',
|
|
59
|
-
module: 'NodeNext',
|
|
60
|
-
moduleResolution: 'NodeNext',
|
|
61
|
-
outDir: 'dist',
|
|
62
|
-
rootDir: 'src',
|
|
63
|
-
strict: true,
|
|
64
|
-
esModuleInterop: true,
|
|
65
|
-
declaration: true,
|
|
66
|
-
},
|
|
67
|
-
include: ['src'],
|
|
68
|
-
}, null, 2) + '\n');
|
|
69
|
-
// Main source file
|
|
70
|
-
const className = name.charAt(0).toUpperCase() + name.slice(1);
|
|
71
|
-
fs.writeFileSync(path.join(dir, 'src', 'index.ts'), `// ${pkgName} — Beecork channel implementation
|
|
72
|
-
// See: https://github.com/beecork/beecork for Channel interface docs
|
|
73
|
-
|
|
74
|
-
// Import the Channel interface type from beecork
|
|
75
|
-
// Note: beecork is a peer dependency
|
|
76
|
-
interface Channel {
|
|
77
|
-
readonly id: string;
|
|
78
|
-
readonly name: string;
|
|
79
|
-
readonly maxMessageLength: number;
|
|
80
|
-
readonly supportsStreaming: boolean;
|
|
81
|
-
readonly supportsMedia: boolean;
|
|
82
|
-
start(): Promise<void>;
|
|
83
|
-
stop(): void;
|
|
84
|
-
sendMessage(peerId: string, text: string): Promise<void>;
|
|
85
|
-
sendNotification(message: string, urgent?: boolean): Promise<void>;
|
|
86
|
-
setTyping(peerId: string, active: boolean): Promise<void>;
|
|
87
|
-
onMessage(handler: (msg: any) => Promise<void>): void;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
interface ChannelContext {
|
|
91
|
-
config: any;
|
|
92
|
-
tabManager: any;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export default class ${className}Channel implements Channel {
|
|
96
|
-
readonly id = '${name}';
|
|
97
|
-
readonly name = '${className}';
|
|
98
|
-
readonly maxMessageLength = 4096;
|
|
99
|
-
readonly supportsStreaming = false;
|
|
100
|
-
readonly supportsMedia = false;
|
|
101
|
-
|
|
102
|
-
private ctx: ChannelContext;
|
|
103
|
-
private handler: ((msg: any) => Promise<void>) | null = null;
|
|
104
|
-
|
|
105
|
-
constructor(ctx: ChannelContext) {
|
|
106
|
-
this.ctx = ctx;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
async start(): Promise<void> {
|
|
110
|
-
// TODO: Connect to your service, start polling/websocket
|
|
111
|
-
console.log(\`${name} channel started\`);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
stop(): void {
|
|
115
|
-
// TODO: Disconnect, cleanup
|
|
116
|
-
console.log(\`${name} channel stopped\`);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
onMessage(handler: (msg: any) => Promise<void>): void {
|
|
120
|
-
this.handler = handler;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
async sendMessage(peerId: string, text: string): Promise<void> {
|
|
124
|
-
// TODO: Send a message to the user
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
async sendNotification(message: string, urgent?: boolean): Promise<void> {
|
|
128
|
-
// TODO: Send a notification to all configured users
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
async setTyping(peerId: string, active: boolean): Promise<void> {
|
|
132
|
-
// TODO: Show typing indicator
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
`);
|
|
136
|
-
// README
|
|
137
|
-
fs.writeFileSync(path.join(dir, 'README.md'), `# ${pkgName}
|
|
138
|
-
|
|
139
|
-
A Beecork channel plugin for ${name}.
|
|
140
|
-
|
|
141
|
-
## Installation
|
|
142
|
-
|
|
143
|
-
\`\`\`bash
|
|
144
|
-
beecork channel install ${name}
|
|
145
|
-
# or: npm install -g ${pkgName}
|
|
146
|
-
\`\`\`
|
|
147
|
-
|
|
148
|
-
## Configuration
|
|
149
|
-
|
|
150
|
-
Add to your \`~/.beecork/config.json\`:
|
|
151
|
-
|
|
152
|
-
\`\`\`json
|
|
153
|
-
{
|
|
154
|
-
"${name}": {
|
|
155
|
-
"enabled": true
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
\`\`\`
|
|
159
|
-
|
|
160
|
-
## Development
|
|
161
|
-
|
|
162
|
-
\`\`\`bash
|
|
163
|
-
npm install
|
|
164
|
-
npm run build
|
|
165
|
-
\`\`\`
|
|
166
|
-
|
|
167
|
-
## Publishing
|
|
168
|
-
|
|
169
|
-
\`\`\`bash
|
|
170
|
-
npm publish
|
|
171
|
-
\`\`\`
|
|
172
|
-
`);
|
|
173
|
-
console.log(`\nChannel scaffold created: ${dir}/`);
|
|
174
|
-
console.log(`\nNext steps:`);
|
|
175
|
-
console.log(` cd ${dir}`);
|
|
176
|
-
console.log(` npm install`);
|
|
177
|
-
console.log(` # Edit src/index.ts to implement your channel`);
|
|
178
|
-
console.log(` npm run build`);
|
|
179
|
-
console.log(` npm publish`);
|
|
180
|
-
}
|
|
181
|
-
export function channelList() {
|
|
182
|
-
try {
|
|
183
|
-
const output = execFileSync('npm', ['list', '-g', '--depth=0'], {
|
|
184
|
-
encoding: 'utf-8',
|
|
185
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
186
|
-
});
|
|
187
|
-
const lines = output.split('\n').filter((line) => line.includes(CHANNEL_PREFIX));
|
|
188
|
-
if (lines.length === 0) {
|
|
189
|
-
console.log('No community channels installed.');
|
|
190
|
-
console.log(`Install one: beecork channel install <name>`);
|
|
191
|
-
return;
|
|
192
|
-
}
|
|
193
|
-
console.log(`\n${lines.length} community channel(s):\n`);
|
|
194
|
-
for (const line of lines) {
|
|
195
|
-
console.log(` ${line.trim()}`);
|
|
196
|
-
}
|
|
197
|
-
console.log('');
|
|
198
|
-
}
|
|
199
|
-
catch {
|
|
200
|
-
console.log('No community channels installed.');
|
|
201
|
-
}
|
|
202
|
-
}
|
package/dist/cli/commands.d.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
export declare function startDaemon(): Promise<void>;
|
|
2
|
-
export declare function stopDaemon(): Promise<void>;
|
|
3
|
-
export declare function showStatus(): Promise<void>;
|
|
4
|
-
export declare function listTabs(): Promise<void>;
|
|
5
|
-
export declare function tailLogs(tabName?: string): Promise<void>;
|
|
6
|
-
export declare function listCrons(): Promise<void>;
|
|
7
|
-
export declare function deleteCron(id: string): Promise<void>;
|
|
8
|
-
export declare function listWatchers(): Promise<void>;
|
|
9
|
-
export declare function deleteWatcher(id: string): Promise<void>;
|
|
10
|
-
export declare function listMemories(): Promise<void>;
|
|
11
|
-
export declare function deleteMemory(id: string): Promise<void>;
|
|
12
|
-
export declare function updateBeecork(options: {
|
|
13
|
-
check?: boolean;
|
|
14
|
-
}): Promise<void>;
|
|
15
|
-
export declare function sendMessage(message: string): Promise<void>;
|