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/cli/channel.js
CHANGED
|
@@ -1,17 +1,24 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
const CHANNEL_PREFIX = 'beecork-channel-';
|
|
5
|
+
const SAFE_NPM_PACKAGE = /^[@a-zA-Z0-9_/.-]+$/;
|
|
5
6
|
export function channelInstall(packageName) {
|
|
6
7
|
// Normalize name
|
|
7
|
-
const fullName = packageName.startsWith(CHANNEL_PREFIX)
|
|
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
|
+
}
|
|
8
15
|
console.log(`Installing channel: ${fullName}...`);
|
|
9
16
|
try {
|
|
10
|
-
|
|
17
|
+
execFileSync('npm', ['install', '-g', fullName], { stdio: 'inherit' });
|
|
11
18
|
console.log(`\nChannel "${fullName}" installed.`);
|
|
12
19
|
console.log('Restart the daemon to activate: beecork stop && beecork start');
|
|
13
20
|
}
|
|
14
|
-
catch
|
|
21
|
+
catch {
|
|
15
22
|
console.error(`Failed to install ${fullName}. Check the package name and try again.`);
|
|
16
23
|
process.exit(1);
|
|
17
24
|
}
|
|
@@ -173,8 +180,11 @@ npm publish
|
|
|
173
180
|
}
|
|
174
181
|
export function channelList() {
|
|
175
182
|
try {
|
|
176
|
-
const output =
|
|
177
|
-
|
|
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));
|
|
178
188
|
if (lines.length === 0) {
|
|
179
189
|
console.log('No community channels installed.');
|
|
180
190
|
console.log(`Install one: beecork channel install <name>`);
|
package/dist/cli/commands.js
CHANGED
|
@@ -4,6 +4,7 @@ import { spawn, execSync } from 'node:child_process';
|
|
|
4
4
|
import { getDb, closeDb } from '../db/index.js';
|
|
5
5
|
import { getConfig } from '../config.js';
|
|
6
6
|
import { TaskStore } from '../tasks/store.js';
|
|
7
|
+
import { TabStore } from '../session/tab-store.js';
|
|
7
8
|
import { getDaemonPid, timeAgo } from './helpers.js';
|
|
8
9
|
import { startService, stopService } from '../service/install.js';
|
|
9
10
|
import { getPidPath, getLogsDir } from '../util/paths.js';
|
|
@@ -71,17 +72,17 @@ export async function showStatus() {
|
|
|
71
72
|
console.log(`Daemon: ${pid ? `running (PID ${pid})` : 'stopped'}`);
|
|
72
73
|
console.log(`Deployment: ${config.deployment}`);
|
|
73
74
|
try {
|
|
74
|
-
|
|
75
|
-
const tabs =
|
|
75
|
+
getDb(); // ensure DB initialized
|
|
76
|
+
const tabs = TabStore.listAll();
|
|
76
77
|
console.log(`\nTabs (${tabs.length}):`);
|
|
77
78
|
for (const tab of tabs) {
|
|
78
|
-
const ago = timeAgo(tab.
|
|
79
|
+
const ago = timeAgo(tab.lastActivityAt);
|
|
79
80
|
const pidInfo = tab.pid ? ` (PID ${tab.pid})` : '';
|
|
80
81
|
console.log(` ${tab.name.padEnd(20)} ${tab.status.padEnd(12)} last active: ${ago}${pidInfo}`);
|
|
81
82
|
}
|
|
82
83
|
const store = new TaskStore();
|
|
83
84
|
const jobs = store.list();
|
|
84
|
-
const activeJobs = jobs.filter(j => j.enabled);
|
|
85
|
+
const activeJobs = jobs.filter((j) => j.enabled);
|
|
85
86
|
console.log(`\nTasks: ${activeJobs.length} active (${jobs.length} total)`);
|
|
86
87
|
if (activeJobs.length > 0) {
|
|
87
88
|
for (const job of activeJobs.slice(0, 5)) {
|
|
@@ -97,8 +98,8 @@ export async function showStatus() {
|
|
|
97
98
|
console.log('');
|
|
98
99
|
}
|
|
99
100
|
export async function listTabs() {
|
|
100
|
-
|
|
101
|
-
const tabs =
|
|
101
|
+
requireDb();
|
|
102
|
+
const tabs = TabStore.listAll();
|
|
102
103
|
closeDb();
|
|
103
104
|
if (tabs.length === 0) {
|
|
104
105
|
console.log('No tabs.');
|
|
@@ -106,8 +107,8 @@ export async function listTabs() {
|
|
|
106
107
|
}
|
|
107
108
|
console.log(`\nTabs (${tabs.length}):\n`);
|
|
108
109
|
for (const tab of tabs) {
|
|
109
|
-
const ago = timeAgo(tab.
|
|
110
|
-
console.log(` ${tab.name.padEnd(20)} [${tab.status}] dir:${tab.
|
|
110
|
+
const ago = timeAgo(tab.lastActivityAt);
|
|
111
|
+
console.log(` ${tab.name.padEnd(20)} [${tab.status}] dir:${tab.workingDir} — ${ago}`);
|
|
111
112
|
}
|
|
112
113
|
console.log('');
|
|
113
114
|
}
|
|
@@ -186,7 +187,9 @@ export async function deleteWatcher(id) {
|
|
|
186
187
|
}
|
|
187
188
|
export async function listMemories() {
|
|
188
189
|
const db = requireDb();
|
|
189
|
-
const memories = db
|
|
190
|
+
const memories = db
|
|
191
|
+
.prepare('SELECT * FROM memories ORDER BY created_at DESC LIMIT 50')
|
|
192
|
+
.all();
|
|
190
193
|
closeDb();
|
|
191
194
|
if (memories.length === 0) {
|
|
192
195
|
console.log('No memories stored.');
|
package/dist/cli/doctor.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import { execSync } from 'node:child_process';
|
|
3
3
|
import { getConfig } from '../config.js';
|
|
4
|
-
import { getDbPath, getPidPath, getBeecorkHome } from '../util/paths.js';
|
|
4
|
+
import { getDbPath, getPidPath, getBeecorkHome, getConfigPath, getMcpConfigPath, getWhatsappSessionPath, } from '../util/paths.js';
|
|
5
|
+
import { getMediaDir } from '../media/store.js';
|
|
5
6
|
export async function runDoctor() {
|
|
6
7
|
const checks = [];
|
|
7
8
|
// 1. Check Claude binary
|
|
@@ -19,10 +20,14 @@ export async function runDoctor() {
|
|
|
19
20
|
}
|
|
20
21
|
}
|
|
21
22
|
catch {
|
|
22
|
-
checks.push({
|
|
23
|
+
checks.push({
|
|
24
|
+
name: 'Claude Code',
|
|
25
|
+
status: 'fail',
|
|
26
|
+
message: 'Claude Code binary not found. Install: npm install -g @anthropic-ai/claude-code',
|
|
27
|
+
});
|
|
23
28
|
}
|
|
24
29
|
// 2. Check config file
|
|
25
|
-
const configPath =
|
|
30
|
+
const configPath = getConfigPath();
|
|
26
31
|
if (fs.existsSync(configPath)) {
|
|
27
32
|
try {
|
|
28
33
|
const config = getConfig();
|
|
@@ -30,17 +35,31 @@ export async function runDoctor() {
|
|
|
30
35
|
// 3. Check Telegram token
|
|
31
36
|
if (config.telegram?.token) {
|
|
32
37
|
try {
|
|
33
|
-
const resp = await fetch(`https://api.telegram.org/bot${config.telegram.token}/getMe`, {
|
|
38
|
+
const resp = await fetch(`https://api.telegram.org/bot${config.telegram.token}/getMe`, {
|
|
39
|
+
signal: AbortSignal.timeout(10000),
|
|
40
|
+
});
|
|
34
41
|
if (resp.ok) {
|
|
35
|
-
const data = await resp.json();
|
|
36
|
-
checks.push({
|
|
42
|
+
const data = (await resp.json());
|
|
43
|
+
checks.push({
|
|
44
|
+
name: 'Telegram bot',
|
|
45
|
+
status: 'pass',
|
|
46
|
+
message: `@${data.result.username}`,
|
|
47
|
+
});
|
|
37
48
|
}
|
|
38
49
|
else {
|
|
39
|
-
checks.push({
|
|
50
|
+
checks.push({
|
|
51
|
+
name: 'Telegram bot',
|
|
52
|
+
status: 'fail',
|
|
53
|
+
message: 'Invalid token — getMe returned error',
|
|
54
|
+
});
|
|
40
55
|
}
|
|
41
56
|
}
|
|
42
|
-
catch
|
|
43
|
-
checks.push({
|
|
57
|
+
catch {
|
|
58
|
+
checks.push({
|
|
59
|
+
name: 'Telegram bot',
|
|
60
|
+
status: 'warn',
|
|
61
|
+
message: 'Could not reach Telegram API',
|
|
62
|
+
});
|
|
44
63
|
}
|
|
45
64
|
}
|
|
46
65
|
else {
|
|
@@ -48,12 +67,16 @@ export async function runDoctor() {
|
|
|
48
67
|
}
|
|
49
68
|
// 4. Check WhatsApp session
|
|
50
69
|
if (config.whatsapp?.enabled) {
|
|
51
|
-
const sessionPath = config.whatsapp.sessionPath ||
|
|
70
|
+
const sessionPath = config.whatsapp.sessionPath || getWhatsappSessionPath();
|
|
52
71
|
if (fs.existsSync(sessionPath) && fs.readdirSync(sessionPath).length > 0) {
|
|
53
72
|
checks.push({ name: 'WhatsApp session', status: 'pass', message: sessionPath });
|
|
54
73
|
}
|
|
55
74
|
else {
|
|
56
|
-
checks.push({
|
|
75
|
+
checks.push({
|
|
76
|
+
name: 'WhatsApp session',
|
|
77
|
+
status: 'warn',
|
|
78
|
+
message: 'No session data — QR scan needed',
|
|
79
|
+
});
|
|
57
80
|
}
|
|
58
81
|
}
|
|
59
82
|
}
|
|
@@ -62,14 +85,18 @@ export async function runDoctor() {
|
|
|
62
85
|
}
|
|
63
86
|
}
|
|
64
87
|
else {
|
|
65
|
-
checks.push({
|
|
88
|
+
checks.push({
|
|
89
|
+
name: 'Config',
|
|
90
|
+
status: 'fail',
|
|
91
|
+
message: `Not found at ${configPath}. Run: beecork setup`,
|
|
92
|
+
});
|
|
66
93
|
}
|
|
67
94
|
// 5. Check database
|
|
68
95
|
const dbPath = getDbPath();
|
|
69
96
|
if (fs.existsSync(dbPath)) {
|
|
70
97
|
try {
|
|
71
|
-
const
|
|
72
|
-
const db =
|
|
98
|
+
const { openDb } = await import('../db/connection.js');
|
|
99
|
+
const db = openDb(dbPath, { readonly: true });
|
|
73
100
|
const integrity = db.pragma('integrity_check');
|
|
74
101
|
if (integrity[0]?.integrity_check === 'ok') {
|
|
75
102
|
const size = (fs.statSync(dbPath).size / 1024).toFixed(0);
|
|
@@ -85,7 +112,11 @@ export async function runDoctor() {
|
|
|
85
112
|
}
|
|
86
113
|
}
|
|
87
114
|
else {
|
|
88
|
-
checks.push({
|
|
115
|
+
checks.push({
|
|
116
|
+
name: 'Database',
|
|
117
|
+
status: 'warn',
|
|
118
|
+
message: 'No database yet — starts on first run',
|
|
119
|
+
});
|
|
89
120
|
}
|
|
90
121
|
// 6. Check daemon
|
|
91
122
|
const pidPath = getPidPath();
|
|
@@ -96,7 +127,11 @@ export async function runDoctor() {
|
|
|
96
127
|
checks.push({ name: 'Daemon', status: 'pass', message: `Running (PID ${pid})` });
|
|
97
128
|
}
|
|
98
129
|
catch {
|
|
99
|
-
checks.push({
|
|
130
|
+
checks.push({
|
|
131
|
+
name: 'Daemon',
|
|
132
|
+
status: 'warn',
|
|
133
|
+
message: `Stale PID file (PID ${pid} not running)`,
|
|
134
|
+
});
|
|
100
135
|
}
|
|
101
136
|
}
|
|
102
137
|
else {
|
|
@@ -104,7 +139,7 @@ export async function runDoctor() {
|
|
|
104
139
|
}
|
|
105
140
|
// 7. Check disk space for media
|
|
106
141
|
try {
|
|
107
|
-
const mediaDir =
|
|
142
|
+
const mediaDir = getMediaDir();
|
|
108
143
|
const homeDir = getBeecorkHome();
|
|
109
144
|
// Simple check: can we write a temp file?
|
|
110
145
|
const testPath = `${homeDir}/.doctor-test`;
|
|
@@ -112,38 +147,61 @@ export async function runDoctor() {
|
|
|
112
147
|
fs.unlinkSync(testPath);
|
|
113
148
|
if (fs.existsSync(mediaDir)) {
|
|
114
149
|
const files = fs.readdirSync(mediaDir);
|
|
115
|
-
checks.push({
|
|
150
|
+
checks.push({
|
|
151
|
+
name: 'Media dir',
|
|
152
|
+
status: 'pass',
|
|
153
|
+
message: `${files.length} files in ${mediaDir}`,
|
|
154
|
+
});
|
|
116
155
|
}
|
|
117
156
|
else {
|
|
118
|
-
checks.push({
|
|
157
|
+
checks.push({
|
|
158
|
+
name: 'Media dir',
|
|
159
|
+
status: 'pass',
|
|
160
|
+
message: 'Not created yet (created on first media)',
|
|
161
|
+
});
|
|
119
162
|
}
|
|
120
163
|
}
|
|
121
164
|
catch {
|
|
122
|
-
checks.push({
|
|
165
|
+
checks.push({
|
|
166
|
+
name: 'Disk space',
|
|
167
|
+
status: 'fail',
|
|
168
|
+
message: 'Cannot write to beecork home directory',
|
|
169
|
+
});
|
|
123
170
|
}
|
|
124
171
|
// 8. Check MCP config
|
|
125
|
-
const mcpConfigPath =
|
|
172
|
+
const mcpConfigPath = getMcpConfigPath();
|
|
126
173
|
if (fs.existsSync(mcpConfigPath)) {
|
|
127
174
|
try {
|
|
128
175
|
const mcpConfig = JSON.parse(fs.readFileSync(mcpConfigPath, 'utf-8'));
|
|
129
176
|
const serverCount = Object.keys(mcpConfig.mcpServers || {}).length;
|
|
130
|
-
checks.push({
|
|
177
|
+
checks.push({
|
|
178
|
+
name: 'MCP config',
|
|
179
|
+
status: 'pass',
|
|
180
|
+
message: `${serverCount} server(s) configured`,
|
|
181
|
+
});
|
|
131
182
|
}
|
|
132
183
|
catch {
|
|
133
|
-
checks.push({
|
|
184
|
+
checks.push({
|
|
185
|
+
name: 'MCP config',
|
|
186
|
+
status: 'fail',
|
|
187
|
+
message: 'Invalid JSON in mcp-config.json',
|
|
188
|
+
});
|
|
134
189
|
}
|
|
135
190
|
}
|
|
136
191
|
else {
|
|
137
192
|
checks.push({ name: 'MCP config', status: 'pass', message: 'Default (beecork MCP only)' });
|
|
138
193
|
}
|
|
139
|
-
// Print results
|
|
194
|
+
// Print results — gate ANSI on a real TTY so piping to a file/grep yields plain text.
|
|
140
195
|
console.log('\nBeecork Doctor\n');
|
|
141
|
-
const
|
|
196
|
+
const colored = process.stdout.isTTY && !process.env.NO_COLOR;
|
|
197
|
+
const icons = colored
|
|
198
|
+
? { pass: '\x1b[32m✓\x1b[0m', warn: '\x1b[33m!\x1b[0m', fail: '\x1b[31m✗\x1b[0m' }
|
|
199
|
+
: { pass: '✓', warn: '!', fail: '✗' };
|
|
142
200
|
for (const check of checks) {
|
|
143
201
|
console.log(` ${icons[check.status]} ${check.name}: ${check.message}`);
|
|
144
202
|
}
|
|
145
|
-
const fails = checks.filter(c => c.status === 'fail').length;
|
|
146
|
-
const warns = checks.filter(c => c.status === 'warn').length;
|
|
203
|
+
const fails = checks.filter((c) => c.status === 'fail').length;
|
|
204
|
+
const warns = checks.filter((c) => c.status === 'warn').length;
|
|
147
205
|
console.log(`\n ${checks.length} checks: ${checks.length - fails - warns} passed, ${warns} warnings, ${fails} failures\n`);
|
|
148
206
|
if (fails > 0)
|
|
149
207
|
process.exit(1);
|
package/dist/cli/handoff.d.ts
CHANGED
|
@@ -1,15 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
role: string;
|
|
9
|
-
content: string;
|
|
10
|
-
}>;
|
|
11
|
-
}
|
|
12
|
-
export declare function exportTab(tabName: string): TabInfo | null;
|
|
1
|
+
import { exportTab, formatHandoffInfo, type TabHandoffInfo } from '../session/handoff.js';
|
|
2
|
+
export { exportTab, formatHandoffInfo, type TabHandoffInfo };
|
|
3
|
+
/**
|
|
4
|
+
* CLI-only flow: print the handoff info, spawn claude with `--resume`, and
|
|
5
|
+
* inherit stdio so the user is dropped into an interactive session. Calls
|
|
6
|
+
* `process.exit` on subprocess exit — only safe from the CLI entry point.
|
|
7
|
+
*/
|
|
13
8
|
export declare function attachTab(tabName: string): void;
|
|
14
|
-
export declare function formatHandoffInfo(info: TabInfo): string;
|
|
15
|
-
export {};
|
package/dist/cli/handoff.js
CHANGED
|
@@ -1,21 +1,13 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
|
-
import { getDb } from '../db/index.js';
|
|
3
2
|
import { getConfig } from '../config.js';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
sessionId: tab.session_id,
|
|
13
|
-
workingDir: tab.working_dir,
|
|
14
|
-
status: tab.status,
|
|
15
|
-
lastActivity: tab.last_activity_at,
|
|
16
|
-
recentMessages: messages.reverse(),
|
|
17
|
-
};
|
|
18
|
-
}
|
|
3
|
+
import { exportTab, formatHandoffInfo } from '../session/handoff.js';
|
|
4
|
+
// Re-export the daemon-shared helpers so existing CLI callers keep working.
|
|
5
|
+
export { exportTab, formatHandoffInfo };
|
|
6
|
+
/**
|
|
7
|
+
* CLI-only flow: print the handoff info, spawn claude with `--resume`, and
|
|
8
|
+
* inherit stdio so the user is dropped into an interactive session. Calls
|
|
9
|
+
* `process.exit` on subprocess exit — only safe from the CLI entry point.
|
|
10
|
+
*/
|
|
19
11
|
export function attachTab(tabName) {
|
|
20
12
|
const info = exportTab(tabName);
|
|
21
13
|
if (!info) {
|
|
@@ -30,10 +22,7 @@ export function attachTab(tabName) {
|
|
|
30
22
|
console.log(` Status: ${info.status}`);
|
|
31
23
|
console.log('');
|
|
32
24
|
// Spawn Claude Code in the terminal, resuming the session
|
|
33
|
-
const child = spawn(bin, [
|
|
34
|
-
'--session-id', info.sessionId,
|
|
35
|
-
'--resume',
|
|
36
|
-
], {
|
|
25
|
+
const child = spawn(bin, ['--session-id', info.sessionId, '--resume'], {
|
|
37
26
|
cwd: info.workingDir,
|
|
38
27
|
stdio: 'inherit', // Attach to terminal
|
|
39
28
|
env: { ...process.env },
|
|
@@ -42,27 +31,3 @@ export function attachTab(tabName) {
|
|
|
42
31
|
process.exit(code ?? 0);
|
|
43
32
|
});
|
|
44
33
|
}
|
|
45
|
-
export function formatHandoffInfo(info) {
|
|
46
|
-
const lines = [
|
|
47
|
-
`Session Handoff — tab "${info.name}"`,
|
|
48
|
-
'',
|
|
49
|
-
`Session ID: ${info.sessionId}`,
|
|
50
|
-
`Working dir: ${info.workingDir}`,
|
|
51
|
-
`Status: ${info.status}`,
|
|
52
|
-
`Last activity: ${info.lastActivity}`,
|
|
53
|
-
'',
|
|
54
|
-
'To resume in terminal:',
|
|
55
|
-
` beecork attach ${info.name}`,
|
|
56
|
-
'',
|
|
57
|
-
'Or manually:',
|
|
58
|
-
` cd ${info.workingDir}`,
|
|
59
|
-
` claude --session-id ${info.sessionId} --resume`,
|
|
60
|
-
];
|
|
61
|
-
if (info.recentMessages.length > 0) {
|
|
62
|
-
lines.push('', 'Recent context:');
|
|
63
|
-
for (const msg of info.recentMessages) {
|
|
64
|
-
lines.push(` [${msg.role}] ${msg.content.slice(0, 150)}${msg.content.length > 150 ? '...' : ''}`);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
return lines.join('\n');
|
|
68
|
-
}
|
package/dist/cli/mcp.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
|
-
import {
|
|
3
|
-
const MCP_CONFIG_PATH = `${getBeecorkHome()}/mcp-config.json`;
|
|
2
|
+
import { getMcpConfigPath } from '../util/paths.js';
|
|
4
3
|
function loadMcpConfig() {
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
const path = getMcpConfigPath();
|
|
5
|
+
if (fs.existsSync(path)) {
|
|
6
|
+
return JSON.parse(fs.readFileSync(path, 'utf-8'));
|
|
7
7
|
}
|
|
8
8
|
return { mcpServers: {} };
|
|
9
9
|
}
|
|
10
10
|
function saveMcpConfig(config) {
|
|
11
|
-
fs.writeFileSync(
|
|
11
|
+
fs.writeFileSync(getMcpConfigPath(), JSON.stringify(config, null, 2), { mode: 0o600 });
|
|
12
12
|
}
|
|
13
13
|
export function mcpAdd(name, command, args) {
|
|
14
14
|
const config = loadMcpConfig();
|
package/dist/cli/media.js
CHANGED
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
import readline from 'node:readline';
|
|
2
2
|
function ask(rl, question, defaultValue) {
|
|
3
3
|
const prompt = defaultValue ? `${question} [${defaultValue}]: ` : `${question}: `;
|
|
4
|
-
return new Promise(r => rl.question(prompt, a => r(a.trim() || defaultValue || '')));
|
|
4
|
+
return new Promise((r) => rl.question(prompt, (a) => r(a.trim() || defaultValue || '')));
|
|
5
5
|
}
|
|
6
6
|
const IMAGE_PROVIDERS = [
|
|
7
|
-
{
|
|
7
|
+
{
|
|
8
|
+
id: 'nano-banana',
|
|
9
|
+
name: 'Google Nano Banana',
|
|
10
|
+
keyHint: 'Google AI API key (from ai.google.dev)',
|
|
11
|
+
},
|
|
8
12
|
{ id: 'dall-e', name: 'DALL-E (OpenAI)', keyHint: 'OpenAI API key (sk-...)' },
|
|
9
|
-
{
|
|
13
|
+
{
|
|
14
|
+
id: 'stable-diffusion',
|
|
15
|
+
name: 'Stable Diffusion (Stability AI)',
|
|
16
|
+
keyHint: 'Stability AI API key',
|
|
17
|
+
},
|
|
10
18
|
{ id: 'recraft', name: 'Recraft (Images + SVG Vectors)', keyHint: 'Recraft API key' },
|
|
11
19
|
];
|
|
12
20
|
const VIDEO_PROVIDERS = [
|
|
@@ -17,7 +25,11 @@ const VIDEO_PROVIDERS = [
|
|
|
17
25
|
const AUDIO_PROVIDERS = [
|
|
18
26
|
{ id: 'elevenlabs-music', name: 'ElevenLabs Music', keyHint: 'ElevenLabs API key (xi-...)' },
|
|
19
27
|
{ id: 'lyria', name: 'Google Lyria (Music)', keyHint: 'Google AI API key (from ai.google.dev)' },
|
|
20
|
-
{
|
|
28
|
+
{
|
|
29
|
+
id: 'elevenlabs-sfx',
|
|
30
|
+
name: 'ElevenLabs Sound Effects',
|
|
31
|
+
keyHint: 'ElevenLabs API key (xi-...)',
|
|
32
|
+
},
|
|
21
33
|
];
|
|
22
34
|
export async function mediaSetup() {
|
|
23
35
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
@@ -27,7 +39,8 @@ export async function mediaSetup() {
|
|
|
27
39
|
console.log('\nMedia Generation Setup\n');
|
|
28
40
|
console.log('Configure AI providers for generating images, videos, and audio.');
|
|
29
41
|
console.log('You need API keys from each provider.\n');
|
|
30
|
-
console.log('Already configured: ' +
|
|
42
|
+
console.log('Already configured: ' +
|
|
43
|
+
(generators.length > 0 ? generators.map((g) => g.provider).join(', ') : 'none'));
|
|
31
44
|
console.log('');
|
|
32
45
|
// Image providers
|
|
33
46
|
console.log('Image Generation:');
|
|
@@ -42,7 +55,7 @@ export async function mediaSetup() {
|
|
|
42
55
|
const apiKey = await ask(rl, ` ${provider.keyHint}`);
|
|
43
56
|
if (apiKey) {
|
|
44
57
|
// Remove existing same provider if any
|
|
45
|
-
const filtered = generators.filter(g => g.provider !== provider.id);
|
|
58
|
+
const filtered = generators.filter((g) => g.provider !== provider.id);
|
|
46
59
|
filtered.push({ provider: provider.id, apiKey });
|
|
47
60
|
generators.length = 0;
|
|
48
61
|
generators.push(...filtered);
|
|
@@ -62,7 +75,7 @@ export async function mediaSetup() {
|
|
|
62
75
|
const provider = VIDEO_PROVIDERS[idx];
|
|
63
76
|
const apiKey = await ask(rl, ` ${provider.keyHint}`);
|
|
64
77
|
if (apiKey) {
|
|
65
|
-
const filtered = generators.filter(g => g.provider !== provider.id);
|
|
78
|
+
const filtered = generators.filter((g) => g.provider !== provider.id);
|
|
66
79
|
filtered.push({ provider: provider.id, apiKey });
|
|
67
80
|
generators.length = 0;
|
|
68
81
|
generators.push(...filtered);
|
|
@@ -82,7 +95,7 @@ export async function mediaSetup() {
|
|
|
82
95
|
const provider = AUDIO_PROVIDERS[idx];
|
|
83
96
|
const apiKey = await ask(rl, ` ${provider.keyHint}`);
|
|
84
97
|
if (apiKey) {
|
|
85
|
-
const filtered = generators.filter(g => g.provider !== provider.id);
|
|
98
|
+
const filtered = generators.filter((g) => g.provider !== provider.id);
|
|
86
99
|
filtered.push({ provider: provider.id, apiKey });
|
|
87
100
|
generators.length = 0;
|
|
88
101
|
generators.push(...filtered);
|
package/dist/cli/setup.js
CHANGED
|
@@ -34,11 +34,11 @@ export async function setupWizard() {
|
|
|
34
34
|
console.log('Checking prerequisites...\n');
|
|
35
35
|
try {
|
|
36
36
|
const version = execSync('claude --version 2>&1', { encoding: 'utf-8' }).trim();
|
|
37
|
-
console.log(`
|
|
37
|
+
console.log(` ✓ Claude Code found: ${version}`);
|
|
38
38
|
}
|
|
39
39
|
catch {
|
|
40
40
|
claudeCodeMissing = true;
|
|
41
|
-
console.log('
|
|
41
|
+
console.log(' ✗ Claude Code is not installed yet.');
|
|
42
42
|
console.log('');
|
|
43
43
|
console.log(' Claude Code is the AI brain that Beecork connects to.');
|
|
44
44
|
console.log(' You need a Claude Pro or Max subscription ($20/month) from anthropic.com');
|
|
@@ -74,13 +74,15 @@ export async function setupWizard() {
|
|
|
74
74
|
}
|
|
75
75
|
// Validate token by calling getMe
|
|
76
76
|
try {
|
|
77
|
-
const resp = await fetch(`https://api.telegram.org/bot${token}/getMe`, {
|
|
77
|
+
const resp = await fetch(`https://api.telegram.org/bot${token}/getMe`, {
|
|
78
|
+
signal: AbortSignal.timeout(10000),
|
|
79
|
+
});
|
|
78
80
|
if (resp.ok) {
|
|
79
|
-
const data = await resp.json();
|
|
80
|
-
console.log(`
|
|
81
|
+
const data = (await resp.json());
|
|
82
|
+
console.log(` ✓ Connected to bot: @${data.result.username}\n`);
|
|
81
83
|
}
|
|
82
84
|
else {
|
|
83
|
-
console.log('
|
|
85
|
+
console.log(' ✗ Invalid token. Please check and try again.\n');
|
|
84
86
|
token = '';
|
|
85
87
|
}
|
|
86
88
|
}
|
|
@@ -128,7 +130,6 @@ export async function setupWizard() {
|
|
|
128
130
|
},
|
|
129
131
|
memory: {
|
|
130
132
|
dbPath: '~/.beecork/memory.db',
|
|
131
|
-
maxLongTermEntries: 1000,
|
|
132
133
|
},
|
|
133
134
|
projectScanPaths: scanPaths,
|
|
134
135
|
deployment: 'local',
|
|
@@ -234,5 +235,5 @@ function generateMcpConfig() {
|
|
|
234
235
|
},
|
|
235
236
|
},
|
|
236
237
|
};
|
|
237
|
-
fs.writeFileSync(getMcpConfigPath(), JSON.stringify(mcpConfig, null, 2) + '\n');
|
|
238
|
+
fs.writeFileSync(getMcpConfigPath(), JSON.stringify(mcpConfig, null, 2) + '\n', { mode: 0o600 });
|
|
238
239
|
}
|
package/dist/cli/store.js
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
const BEECORK_PREFIXES = [
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
const BEECORK_PREFIXES = [
|
|
3
|
+
'beecork-capability-',
|
|
4
|
+
'beecork-media-',
|
|
5
|
+
'beecork-channel-',
|
|
6
|
+
'beecork-watcher-',
|
|
7
|
+
];
|
|
8
|
+
const SAFE_NPM_PACKAGE = /^[@a-zA-Z0-9_/.-]+$/;
|
|
3
9
|
export async function storeSearch(query) {
|
|
4
10
|
console.log(`\nSearching for "${query}"...\n`);
|
|
5
11
|
try {
|
|
@@ -9,8 +15,8 @@ export async function storeSearch(query) {
|
|
|
9
15
|
console.log('Failed to search npm registry. Try: npm search beecork');
|
|
10
16
|
return;
|
|
11
17
|
}
|
|
12
|
-
const data = await response.json();
|
|
13
|
-
const packages = data.objects.filter(o => BEECORK_PREFIXES.some(p => o.package.name.startsWith(p)));
|
|
18
|
+
const data = (await response.json());
|
|
19
|
+
const packages = data.objects.filter((o) => BEECORK_PREFIXES.some((p) => o.package.name.startsWith(p)));
|
|
14
20
|
if (packages.length === 0) {
|
|
15
21
|
console.log(`No beecork packages found for "${query}".`);
|
|
16
22
|
console.log('Community packages use naming convention: beecork-capability-*, beecork-media-*, beecork-channel-*');
|
|
@@ -19,7 +25,9 @@ export async function storeSearch(query) {
|
|
|
19
25
|
console.log(`${packages.length} package(s) found:\n`);
|
|
20
26
|
for (const pkg of packages) {
|
|
21
27
|
const p = pkg.package;
|
|
22
|
-
const type = BEECORK_PREFIXES.find(prefix => p.name.startsWith(prefix))
|
|
28
|
+
const type = BEECORK_PREFIXES.find((prefix) => p.name.startsWith(prefix))
|
|
29
|
+
?.replace('beecork-', '')
|
|
30
|
+
.replace('-', '') || '';
|
|
23
31
|
console.log(` ${p.name}@${p.version}`);
|
|
24
32
|
console.log(` ${p.description || 'No description'}`);
|
|
25
33
|
console.log(` Type: ${type}`);
|
|
@@ -35,13 +43,18 @@ export async function storeSearch(query) {
|
|
|
35
43
|
export function storeInstall(packageName) {
|
|
36
44
|
// Normalize: if user types "shopify", try "beecork-capability-shopify" first
|
|
37
45
|
let fullName = packageName;
|
|
38
|
-
if (!BEECORK_PREFIXES.some(p => packageName.startsWith(p)) &&
|
|
46
|
+
if (!BEECORK_PREFIXES.some((p) => packageName.startsWith(p)) &&
|
|
47
|
+
!packageName.startsWith('beecork-')) {
|
|
39
48
|
// Try capability first, then media, then channel
|
|
40
49
|
fullName = `beecork-capability-${packageName}`;
|
|
41
50
|
}
|
|
51
|
+
if (!SAFE_NPM_PACKAGE.test(fullName)) {
|
|
52
|
+
console.error(`Invalid package name: ${fullName}`);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
42
55
|
console.log(`\nInstalling ${fullName}...\n`);
|
|
43
56
|
try {
|
|
44
|
-
|
|
57
|
+
execFileSync('npm', ['install', '-g', fullName], { stdio: 'inherit' });
|
|
45
58
|
console.log(`\n${fullName} installed.`);
|
|
46
59
|
console.log('Restart daemon to activate: beecork stop && beecork start\n');
|
|
47
60
|
}
|
|
@@ -51,8 +64,10 @@ export function storeInstall(packageName) {
|
|
|
51
64
|
const baseName = packageName;
|
|
52
65
|
for (const prefix of ['beecork-media-', 'beecork-channel-', 'beecork-']) {
|
|
53
66
|
const altName = prefix + baseName;
|
|
67
|
+
if (!SAFE_NPM_PACKAGE.test(altName))
|
|
68
|
+
continue;
|
|
54
69
|
try {
|
|
55
|
-
|
|
70
|
+
execFileSync('npm', ['install', '-g', altName], { stdio: 'inherit' });
|
|
56
71
|
console.log(`\n${altName} installed.`);
|
|
57
72
|
console.log('Restart daemon to activate: beecork stop && beecork start\n');
|
|
58
73
|
return;
|
|
@@ -68,14 +83,16 @@ export function storeInstall(packageName) {
|
|
|
68
83
|
}
|
|
69
84
|
export async function storeInfo(packageName) {
|
|
70
85
|
try {
|
|
71
|
-
const response = await fetch(`https://registry.npmjs.org/${encodeURIComponent(packageName)}`, {
|
|
86
|
+
const response = await fetch(`https://registry.npmjs.org/${encodeURIComponent(packageName)}`, {
|
|
87
|
+
signal: AbortSignal.timeout(10000),
|
|
88
|
+
});
|
|
72
89
|
if (!response.ok) {
|
|
73
90
|
console.log(`Package "${packageName}" not found on npm.`);
|
|
74
91
|
return;
|
|
75
92
|
}
|
|
76
|
-
const data = await response.json();
|
|
77
|
-
const latest = data['dist-tags']?.latest;
|
|
78
|
-
const info = data.versions?.[latest];
|
|
93
|
+
const data = (await response.json());
|
|
94
|
+
const latest = data['dist-tags']?.latest ?? '';
|
|
95
|
+
const info = latest ? data.versions?.[latest] : undefined;
|
|
79
96
|
console.log(`\n${data.name}@${latest}`);
|
|
80
97
|
console.log(` ${data.description || 'No description'}`);
|
|
81
98
|
if (info?.homepage)
|
package/dist/config.d.ts
CHANGED
|
@@ -3,5 +3,9 @@ export declare function getConfig(): BeecorkConfig;
|
|
|
3
3
|
export declare function saveConfig(config: BeecorkConfig): void;
|
|
4
4
|
export declare function getTabConfig(tabName: string): TabConfig;
|
|
5
5
|
export declare function resolveWorkingDir(tabName: string): string;
|
|
6
|
-
export declare function getAdminUserId(): number;
|
|
7
6
|
export declare function validateTabName(name: string): string | null;
|
|
7
|
+
/**
|
|
8
|
+
* Like validateTabName but allows the literal name "default" (used by send/update
|
|
9
|
+
* endpoints that reference an existing tab rather than creating one).
|
|
10
|
+
*/
|
|
11
|
+
export declare function validateTabNameOrDefault(name: string): string | null;
|