beecork 0.1.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/cli/commands.d.ts +14 -0
- package/dist/cli/commands.d.ts.map +1 -0
- package/dist/cli/commands.js +215 -0
- package/dist/cli/commands.js.map +1 -0
- package/dist/cli/helpers.d.ts +4 -0
- package/dist/cli/helpers.d.ts.map +1 -0
- package/dist/cli/helpers.js +46 -0
- package/dist/cli/helpers.js.map +1 -0
- package/dist/cli/setup.d.ts +2 -0
- package/dist/cli/setup.d.ts.map +1 -0
- package/dist/cli/setup.js +145 -0
- package/dist/cli/setup.js.map +1 -0
- package/dist/config.d.ts +9 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +97 -0
- package/dist/config.js.map +1 -0
- package/dist/cron/scheduler.d.ts +20 -0
- package/dist/cron/scheduler.d.ts.map +1 -0
- package/dist/cron/scheduler.js +151 -0
- package/dist/cron/scheduler.js.map +1 -0
- package/dist/cron/store.d.ts +12 -0
- package/dist/cron/store.d.ts.map +1 -0
- package/dist/cron/store.js +82 -0
- package/dist/cron/store.js.map +1 -0
- package/dist/daemon.d.ts +2 -0
- package/dist/daemon.d.ts.map +1 -0
- package/dist/daemon.js +140 -0
- package/dist/daemon.js.map +1 -0
- package/dist/db/index.d.ts +4 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +66 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/migrations.d.ts +3 -0
- package/dist/db/migrations.d.ts.map +1 -0
- package/dist/db/migrations.js +85 -0
- package/dist/db/migrations.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +66 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/server.d.ts +3 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +304 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/memory/extractor.d.ts +6 -0
- package/dist/memory/extractor.d.ts.map +1 -0
- package/dist/memory/extractor.js +105 -0
- package/dist/memory/extractor.js.map +1 -0
- package/dist/service/install.d.ts +5 -0
- package/dist/service/install.d.ts.map +1 -0
- package/dist/service/install.js +118 -0
- package/dist/service/install.js.map +1 -0
- package/dist/service/templates.d.ts +5 -0
- package/dist/service/templates.d.ts.map +1 -0
- package/dist/service/templates.js +53 -0
- package/dist/service/templates.js.map +1 -0
- package/dist/session/approval.d.ts +20 -0
- package/dist/session/approval.d.ts.map +1 -0
- package/dist/session/approval.js +59 -0
- package/dist/session/approval.js.map +1 -0
- package/dist/session/circuit-breaker.d.ts +18 -0
- package/dist/session/circuit-breaker.d.ts.map +1 -0
- package/dist/session/circuit-breaker.js +67 -0
- package/dist/session/circuit-breaker.js.map +1 -0
- package/dist/session/context-monitor.d.ts +16 -0
- package/dist/session/context-monitor.d.ts.map +1 -0
- package/dist/session/context-monitor.js +44 -0
- package/dist/session/context-monitor.js.map +1 -0
- package/dist/session/manager.d.ts +42 -0
- package/dist/session/manager.d.ts.map +1 -0
- package/dist/session/manager.js +291 -0
- package/dist/session/manager.js.map +1 -0
- package/dist/session/subprocess.d.ts +23 -0
- package/dist/session/subprocess.d.ts.map +1 -0
- package/dist/session/subprocess.js +119 -0
- package/dist/session/subprocess.js.map +1 -0
- package/dist/session/tool-classifier.d.ts +4 -0
- package/dist/session/tool-classifier.d.ts.map +1 -0
- package/dist/session/tool-classifier.js +57 -0
- package/dist/session/tool-classifier.js.map +1 -0
- package/dist/telegram/bot.d.ts +25 -0
- package/dist/telegram/bot.d.ts.map +1 -0
- package/dist/telegram/bot.js +293 -0
- package/dist/telegram/bot.js.map +1 -0
- package/dist/telegram/formatter.d.ts +11 -0
- package/dist/telegram/formatter.d.ts.map +1 -0
- package/dist/telegram/formatter.js +54 -0
- package/dist/telegram/formatter.js.map +1 -0
- package/dist/types.d.ts +131 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/util/logger.d.ts +17 -0
- package/dist/util/logger.d.ts.map +1 -0
- package/dist/util/logger.js +56 -0
- package/dist/util/logger.js.map +1 -0
- package/dist/util/paths.d.ts +11 -0
- package/dist/util/paths.d.ts.map +1 -0
- package/dist/util/paths.js +40 -0
- package/dist/util/paths.js.map +1 -0
- package/dist/util/platform.d.ts +3 -0
- package/dist/util/platform.d.ts.map +1 -0
- package/dist/util/platform.js +10 -0
- package/dist/util/platform.js.map +1 -0
- package/dist/util/retry.d.ts +3 -0
- package/dist/util/retry.d.ts.map +1 -0
- package/dist/util/retry.js +19 -0
- package/dist/util/retry.js.map +1 -0
- package/dist/whatsapp/client.d.ts +23 -0
- package/dist/whatsapp/client.d.ts.map +1 -0
- package/dist/whatsapp/client.js +125 -0
- package/dist/whatsapp/client.js.map +1 -0
- package/dist/whatsapp/formatter.d.ts +3 -0
- package/dist/whatsapp/formatter.d.ts.map +1 -0
- package/dist/whatsapp/formatter.js +25 -0
- package/dist/whatsapp/formatter.js.map +1 -0
- package/package.json +53 -0
- package/templates/CLAUDE.md +25 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import cron from 'node-cron';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { CronStore } from './store.js';
|
|
5
|
+
import { getCronReloadSignalPath, getLogsDir } from '../util/paths.js';
|
|
6
|
+
import { logger } from '../util/logger.js';
|
|
7
|
+
export class CronScheduler {
|
|
8
|
+
tabManager;
|
|
9
|
+
telegramBot;
|
|
10
|
+
scheduledJobs = new Map();
|
|
11
|
+
store = new CronStore();
|
|
12
|
+
constructor(tabManager, telegramBot) {
|
|
13
|
+
this.tabManager = tabManager;
|
|
14
|
+
this.telegramBot = telegramBot;
|
|
15
|
+
}
|
|
16
|
+
/** Load all cron jobs from store and schedule them */
|
|
17
|
+
loadAndSchedule() {
|
|
18
|
+
// Cancel existing
|
|
19
|
+
for (const [, task] of this.scheduledJobs) {
|
|
20
|
+
task.stop();
|
|
21
|
+
}
|
|
22
|
+
this.scheduledJobs.clear();
|
|
23
|
+
const jobs = this.store.list();
|
|
24
|
+
let scheduled = 0;
|
|
25
|
+
for (const job of jobs) {
|
|
26
|
+
if (!job.enabled)
|
|
27
|
+
continue;
|
|
28
|
+
this.scheduleJob(job);
|
|
29
|
+
scheduled++;
|
|
30
|
+
}
|
|
31
|
+
logger.info(`Cron: loaded ${scheduled} active jobs (${jobs.length} total)`);
|
|
32
|
+
}
|
|
33
|
+
/** Check for the reload signal file and reload if present */
|
|
34
|
+
checkForReload() {
|
|
35
|
+
const signalPath = getCronReloadSignalPath();
|
|
36
|
+
if (fs.existsSync(signalPath)) {
|
|
37
|
+
try {
|
|
38
|
+
fs.unlinkSync(signalPath);
|
|
39
|
+
}
|
|
40
|
+
catch { /* race condition, ok */ }
|
|
41
|
+
logger.info('Cron: reload signal detected, reloading schedules');
|
|
42
|
+
this.loadAndSchedule();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/** Stop all scheduled jobs */
|
|
46
|
+
stopAll() {
|
|
47
|
+
for (const [, task] of this.scheduledJobs) {
|
|
48
|
+
task.stop();
|
|
49
|
+
}
|
|
50
|
+
this.scheduledJobs.clear();
|
|
51
|
+
}
|
|
52
|
+
scheduleJob(job) {
|
|
53
|
+
switch (job.scheduleType) {
|
|
54
|
+
case 'cron': {
|
|
55
|
+
if (!cron.validate(job.schedule)) {
|
|
56
|
+
logger.error(`Cron: invalid expression for "${job.name}": ${job.schedule}`);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const task = cron.schedule(job.schedule, () => this.fireJob(job));
|
|
60
|
+
this.scheduledJobs.set(job.id, task);
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
case 'every': {
|
|
64
|
+
const cronExpr = intervalToCron(job.schedule);
|
|
65
|
+
if (!cronExpr || !cron.validate(cronExpr)) {
|
|
66
|
+
logger.error(`Cron: invalid interval for "${job.name}": ${job.schedule}`);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const task = cron.schedule(cronExpr, () => this.fireJob(job));
|
|
70
|
+
this.scheduledJobs.set(job.id, task);
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
case 'at': {
|
|
74
|
+
const targetTime = new Date(job.schedule).getTime();
|
|
75
|
+
const delay = targetTime - Date.now();
|
|
76
|
+
if (delay <= 0) {
|
|
77
|
+
logger.warn(`Cron: one-time job "${job.name}" is in the past, skipping`);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const timer = setTimeout(() => {
|
|
81
|
+
this.fireJob(job);
|
|
82
|
+
this.store.update(job.id, { enabled: false });
|
|
83
|
+
}, delay);
|
|
84
|
+
this.scheduledJobs.set(job.id, { stop: () => clearTimeout(timer) });
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
async fireJob(job) {
|
|
90
|
+
logger.info(`Cron firing: "${job.name}" → tab:${job.tabName}`);
|
|
91
|
+
const logFile = path.join(getLogsDir(), `cron-${job.name}.log`);
|
|
92
|
+
try {
|
|
93
|
+
this.tabManager.ensureTab(job.tabName);
|
|
94
|
+
const result = await this.tabManager.sendMessage(job.tabName, job.message);
|
|
95
|
+
this.store.update(job.id, { lastRunAt: new Date().toISOString() });
|
|
96
|
+
const firstLine = result.text.split('\n')[0]?.slice(0, 200) || '(no output)';
|
|
97
|
+
// Log result
|
|
98
|
+
fs.appendFileSync(logFile, `[${new Date().toISOString()}] SUCCESS: ${firstLine}\n`);
|
|
99
|
+
// Notify via Telegram
|
|
100
|
+
if (this.telegramBot) {
|
|
101
|
+
if (result.error) {
|
|
102
|
+
await this.telegramBot.sendNotification(`❌ [${job.name}] Failed — ${firstLine}`);
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
await this.telegramBot.sendNotification(`✅ [${job.name}] Done — ${firstLine}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
catch (err) {
|
|
110
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
111
|
+
logger.error(`Cron job "${job.name}" failed:`, err);
|
|
112
|
+
fs.appendFileSync(logFile, `[${new Date().toISOString()}] ERROR: ${errMsg}\n`);
|
|
113
|
+
if (this.telegramBot) {
|
|
114
|
+
await this.telegramBot.sendNotification(`❌ [${job.name}] Failed — ${errMsg}`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/** Convert human interval (30m, 2h, 1d, 1h30m, 2w) to cron expression */
|
|
120
|
+
export function intervalToCron(interval) {
|
|
121
|
+
// Try combined format: 1h30m, 2h, 30m, 1d, 2w
|
|
122
|
+
const match = interval.match(/^(?:(\d+)w)?(?:(\d+)d)?(?:(\d+)h)?(?:(\d+)m)?$/);
|
|
123
|
+
if (!match || match.slice(1).every(g => g === undefined))
|
|
124
|
+
return null;
|
|
125
|
+
const weeks = parseInt(match[1] || '0', 10);
|
|
126
|
+
const days = parseInt(match[2] || '0', 10);
|
|
127
|
+
const hours = parseInt(match[3] || '0', 10);
|
|
128
|
+
const mins = parseInt(match[4] || '0', 10);
|
|
129
|
+
// Convert to total minutes for simple intervals
|
|
130
|
+
const totalMins = weeks * 7 * 24 * 60 + days * 24 * 60 + hours * 60 + mins;
|
|
131
|
+
if (totalMins <= 0)
|
|
132
|
+
return null;
|
|
133
|
+
// Simple minute interval
|
|
134
|
+
if (totalMins <= 59)
|
|
135
|
+
return `*/${totalMins} * * * *`;
|
|
136
|
+
// Hourly intervals
|
|
137
|
+
if (mins === 0 && days === 0 && weeks === 0 && hours > 0 && hours <= 23)
|
|
138
|
+
return `0 */${hours} * * *`;
|
|
139
|
+
// Daily intervals
|
|
140
|
+
if (mins === 0 && hours === 0 && weeks === 0 && days > 0)
|
|
141
|
+
return `0 0 */${days} * *`;
|
|
142
|
+
// Weekly intervals
|
|
143
|
+
if (mins === 0 && hours === 0 && days === 0 && weeks > 0)
|
|
144
|
+
return `0 0 * * 0`;
|
|
145
|
+
// Combined intervals (e.g., 1h30m = every 90 minutes)
|
|
146
|
+
if (totalMins <= 1440)
|
|
147
|
+
return `*/${totalMins} * * * *`; // up to 24h as minutes
|
|
148
|
+
// Fallback for very large intervals: daily
|
|
149
|
+
return `0 0 */${Math.ceil(totalMins / 1440)} * *`;
|
|
150
|
+
}
|
|
151
|
+
//# sourceMappingURL=scheduler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduler.js","sourceRoot":"","sources":["../../src/cron/scheduler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { CronJob } from '../types.js';
|
|
2
|
+
export declare class CronStore {
|
|
3
|
+
constructor();
|
|
4
|
+
list(): CronJob[];
|
|
5
|
+
get(id: string): CronJob | undefined;
|
|
6
|
+
add(job: CronJob): void;
|
|
7
|
+
update(id: string, updates: Partial<CronJob>): boolean;
|
|
8
|
+
delete(id: string): boolean;
|
|
9
|
+
/** One-time migration from crontab.json to SQLite */
|
|
10
|
+
private migrateFromJson;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/cron/store.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAmB3C,qBAAa,SAAS;;IAKpB,IAAI,IAAI,OAAO,EAAE;IAKjB,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS;IAMpC,GAAG,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI;IASvB,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO;IAatD,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAM3B,qDAAqD;IACrD,OAAO,CAAC,eAAe;CAkCxB"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import { getDb } from '../db/index.js';
|
|
3
|
+
import { getCrontabPath } from '../util/paths.js';
|
|
4
|
+
import { logger } from '../util/logger.js';
|
|
5
|
+
function rowToJob(row) {
|
|
6
|
+
return {
|
|
7
|
+
id: row.id, name: row.name,
|
|
8
|
+
scheduleType: row.schedule_type,
|
|
9
|
+
schedule: row.schedule, tabName: row.tab_name, message: row.message,
|
|
10
|
+
enabled: row.enabled === 1, createdAt: row.created_at,
|
|
11
|
+
lastRunAt: row.last_run_at, nextRunAt: row.next_run_at,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export class CronStore {
|
|
15
|
+
constructor() {
|
|
16
|
+
this.migrateFromJson();
|
|
17
|
+
}
|
|
18
|
+
list() {
|
|
19
|
+
const db = getDb();
|
|
20
|
+
return db.prepare('SELECT * FROM cron_jobs WHERE user_id = ? ORDER BY created_at').all('local').map(rowToJob);
|
|
21
|
+
}
|
|
22
|
+
get(id) {
|
|
23
|
+
const db = getDb();
|
|
24
|
+
const row = db.prepare('SELECT * FROM cron_jobs WHERE id = ?').get(id);
|
|
25
|
+
return row ? rowToJob(row) : undefined;
|
|
26
|
+
}
|
|
27
|
+
add(job) {
|
|
28
|
+
const db = getDb();
|
|
29
|
+
db.prepare(`INSERT INTO cron_jobs (id, name, schedule_type, schedule, tab_name, message, enabled, user_id, created_at, last_run_at, next_run_at)
|
|
30
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(job.id, job.name, job.scheduleType, job.schedule, job.tabName, job.message, job.enabled ? 1 : 0, 'local', job.createdAt, job.lastRunAt, job.nextRunAt);
|
|
31
|
+
}
|
|
32
|
+
update(id, updates) {
|
|
33
|
+
const db = getDb();
|
|
34
|
+
const existing = this.get(id);
|
|
35
|
+
if (!existing)
|
|
36
|
+
return false;
|
|
37
|
+
const merged = { ...existing, ...updates };
|
|
38
|
+
db.prepare(`UPDATE cron_jobs SET name=?, schedule_type=?, schedule=?, tab_name=?, message=?, enabled=?, last_run_at=?, next_run_at=? WHERE id=?`).run(merged.name, merged.scheduleType, merged.schedule, merged.tabName, merged.message, merged.enabled ? 1 : 0, merged.lastRunAt, merged.nextRunAt, id);
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
delete(id) {
|
|
42
|
+
const db = getDb();
|
|
43
|
+
const result = db.prepare('DELETE FROM cron_jobs WHERE id = ?').run(id);
|
|
44
|
+
return result.changes > 0;
|
|
45
|
+
}
|
|
46
|
+
/** One-time migration from crontab.json to SQLite */
|
|
47
|
+
migrateFromJson() {
|
|
48
|
+
const jsonPath = getCrontabPath();
|
|
49
|
+
if (!fs.existsSync(jsonPath))
|
|
50
|
+
return;
|
|
51
|
+
const db = getDb();
|
|
52
|
+
const count = db.prepare('SELECT COUNT(*) as count FROM cron_jobs').get();
|
|
53
|
+
if (count.count > 0) {
|
|
54
|
+
// Already migrated, clean up JSON
|
|
55
|
+
try {
|
|
56
|
+
fs.renameSync(jsonPath, jsonPath + '.bak');
|
|
57
|
+
}
|
|
58
|
+
catch { /* ok */ }
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
try {
|
|
62
|
+
const data = JSON.parse(fs.readFileSync(jsonPath, 'utf-8'));
|
|
63
|
+
const jobs = data.jobs || [];
|
|
64
|
+
if (jobs.length === 0)
|
|
65
|
+
return;
|
|
66
|
+
const insert = db.prepare(`INSERT OR IGNORE INTO cron_jobs (id, name, schedule_type, schedule, tab_name, message, enabled, user_id, created_at, last_run_at, next_run_at)
|
|
67
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
68
|
+
const tx = db.transaction(() => {
|
|
69
|
+
for (const j of jobs) {
|
|
70
|
+
insert.run(j.id, j.name, j.scheduleType, j.schedule, j.tabName || 'default', j.message, j.enabled ? 1 : 0, 'local', j.createdAt, j.lastRunAt, j.nextRunAt);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
tx();
|
|
74
|
+
fs.renameSync(jsonPath, jsonPath + '.bak');
|
|
75
|
+
logger.info(`Migrated ${jobs.length} cron jobs from JSON to SQLite`);
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
logger.error('Failed to migrate cron jobs from JSON:', err);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/cron/store.ts"],"names":[],"mappings":"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"}
|
package/dist/daemon.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":""}
|
package/dist/daemon.js
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import { getConfig } from './config.js';
|
|
5
|
+
import { getDb, closeDb } from './db/index.js';
|
|
6
|
+
import { TabManager } from './session/manager.js';
|
|
7
|
+
import { BeecorkTelegramBot } from './telegram/bot.js';
|
|
8
|
+
import { CronScheduler } from './cron/scheduler.js';
|
|
9
|
+
import { ensureBeecorkDirs, getPidPath, getBeecorkHome } from './util/paths.js';
|
|
10
|
+
import { logger } from './util/logger.js';
|
|
11
|
+
let tabManager;
|
|
12
|
+
let telegramBot = null;
|
|
13
|
+
let cronScheduler;
|
|
14
|
+
let pollInterval;
|
|
15
|
+
/** Migrate data from old ~/.clawd to ~/.beecork if needed */
|
|
16
|
+
function migrateFromClawd() {
|
|
17
|
+
const oldHome = path.join(os.homedir(), '.clawd');
|
|
18
|
+
const newHome = getBeecorkHome();
|
|
19
|
+
if (fs.existsSync(oldHome) && !fs.existsSync(newHome)) {
|
|
20
|
+
// Copy old data to new location
|
|
21
|
+
fs.cpSync(oldHome, newHome, { recursive: true });
|
|
22
|
+
logger.info(`Migrated data directory from ${oldHome} to ${newHome}`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
async function main() {
|
|
26
|
+
migrateFromClawd();
|
|
27
|
+
ensureBeecorkDirs();
|
|
28
|
+
logger.setLogFile('daemon.log');
|
|
29
|
+
logger.info('Beecork daemon starting...');
|
|
30
|
+
// 1. Load config
|
|
31
|
+
const config = getConfig();
|
|
32
|
+
// 2. Initialize database
|
|
33
|
+
getDb();
|
|
34
|
+
// 3. Write PID file
|
|
35
|
+
fs.writeFileSync(getPidPath(), String(process.pid));
|
|
36
|
+
logger.info(`PID file written: ${process.pid}`);
|
|
37
|
+
// 4. Create TabManager
|
|
38
|
+
tabManager = new TabManager(config);
|
|
39
|
+
// 5. Ensure default tab
|
|
40
|
+
tabManager.ensureTab('default');
|
|
41
|
+
// 6. Recover crashed tabs
|
|
42
|
+
await recoverCrashedTabs();
|
|
43
|
+
// 7. Start Telegram bot
|
|
44
|
+
if (config.telegram?.token) {
|
|
45
|
+
try {
|
|
46
|
+
telegramBot = new BeecorkTelegramBot(config, tabManager);
|
|
47
|
+
// Wire up notifications so TabManager can send Telegram alerts (loop detection, etc.)
|
|
48
|
+
tabManager.setNotifyCallback((text) => telegramBot.sendNotification(text));
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
logger.error('Failed to start Telegram bot:', err);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
logger.warn('No Telegram token configured. Bot not started.');
|
|
56
|
+
}
|
|
57
|
+
// 8. Start cron scheduler
|
|
58
|
+
cronScheduler = new CronScheduler(tabManager, telegramBot);
|
|
59
|
+
cronScheduler.loadAndSchedule();
|
|
60
|
+
// 9. Start IPC polling
|
|
61
|
+
pollInterval = setInterval(() => {
|
|
62
|
+
try {
|
|
63
|
+
cronScheduler.checkForReload();
|
|
64
|
+
tabManager.processPendingMessages();
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
logger.error('Poll error:', err);
|
|
68
|
+
}
|
|
69
|
+
}, 5000);
|
|
70
|
+
// 10. Handle shutdown
|
|
71
|
+
const shutdown = async () => {
|
|
72
|
+
logger.info('Beecork daemon shutting down...');
|
|
73
|
+
// Send shutdown notification before stopping
|
|
74
|
+
if (telegramBot) {
|
|
75
|
+
try {
|
|
76
|
+
await telegramBot.sendNotification('🔴 Beecork stopping');
|
|
77
|
+
}
|
|
78
|
+
catch { /* ok */ }
|
|
79
|
+
}
|
|
80
|
+
clearInterval(pollInterval);
|
|
81
|
+
tabManager.stopAll();
|
|
82
|
+
if (telegramBot)
|
|
83
|
+
telegramBot.stop();
|
|
84
|
+
cronScheduler.stopAll();
|
|
85
|
+
closeDb();
|
|
86
|
+
const pidPath = getPidPath();
|
|
87
|
+
if (fs.existsSync(pidPath))
|
|
88
|
+
fs.unlinkSync(pidPath);
|
|
89
|
+
logger.info('Beecork daemon stopped.');
|
|
90
|
+
logger.close();
|
|
91
|
+
process.exit(0);
|
|
92
|
+
};
|
|
93
|
+
process.on('SIGTERM', shutdown);
|
|
94
|
+
process.on('SIGINT', shutdown);
|
|
95
|
+
logger.info(`Beecork daemon ready (home: ${getBeecorkHome()})`);
|
|
96
|
+
// Send detailed startup notification
|
|
97
|
+
if (telegramBot) {
|
|
98
|
+
const tabs = tabManager.listTabs();
|
|
99
|
+
const cronJobs = new (await import('./cron/store.js')).CronStore().list().filter(j => j.enabled);
|
|
100
|
+
await telegramBot.sendNotification(`🟢 Beecork started — ${cronJobs.length} cron job${cronJobs.length !== 1 ? 's' : ''}, ${tabs.length} tab${tabs.length !== 1 ? 's' : ''}`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
async function recoverCrashedTabs() {
|
|
104
|
+
const db = getDb();
|
|
105
|
+
const crashedRows = db.prepare(`SELECT * FROM tabs WHERE status = 'running'`).all();
|
|
106
|
+
if (crashedRows.length === 0)
|
|
107
|
+
return;
|
|
108
|
+
logger.info(`Found ${crashedRows.length} tabs that were running when daemon stopped`);
|
|
109
|
+
for (const row of crashedRows) {
|
|
110
|
+
logger.info(`Recovering tab: ${row.name} (session: ${row.session_id})`);
|
|
111
|
+
// Get last few messages for context
|
|
112
|
+
const recentMessages = db.prepare(`SELECT role, content FROM messages
|
|
113
|
+
WHERE tab_id = ? ORDER BY created_at DESC LIMIT 5`).all(row.id);
|
|
114
|
+
// Build recovery prompt
|
|
115
|
+
const contextSummary = recentMessages
|
|
116
|
+
.reverse()
|
|
117
|
+
.map(m => `${m.role}: ${m.content.slice(0, 200)}`)
|
|
118
|
+
.join('\n');
|
|
119
|
+
const recoveryPrompt = [
|
|
120
|
+
`[SYSTEM: Session recovered after restart. Here is your recent conversation context:]`,
|
|
121
|
+
contextSummary,
|
|
122
|
+
`[SYSTEM: Please acknowledge you are back and ready for new instructions.]`,
|
|
123
|
+
].join('\n');
|
|
124
|
+
// Reset status so TabManager can use it
|
|
125
|
+
db.prepare(`UPDATE tabs SET status = 'idle', pid = NULL WHERE id = ?`).run(row.id);
|
|
126
|
+
// Resume the session
|
|
127
|
+
tabManager.sendMessage(row.name, recoveryPrompt, { resume: true }).catch(err => {
|
|
128
|
+
logger.error(`Failed to recover tab ${row.name}:`, err);
|
|
129
|
+
});
|
|
130
|
+
// Notify via Telegram
|
|
131
|
+
if (telegramBot) {
|
|
132
|
+
await telegramBot.sendNotification(`Beecork restarted. Recovered tab "${row.name}" — session resumed.`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
main().catch(err => {
|
|
137
|
+
logger.error('Fatal error:', err);
|
|
138
|
+
process.exit(1);
|
|
139
|
+
});
|
|
140
|
+
//# sourceMappingURL=daemon.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon.js","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/db/index.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AAkDtC,wBAAgB,KAAK,IAAI,QAAQ,CAAC,QAAQ,CAYzC;AAED,wBAAgB,OAAO,IAAI,IAAI,CAK9B"}
|
package/dist/db/index.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import Database from 'better-sqlite3';
|
|
2
|
+
import { getDbPath, ensureBeecorkDirs } from '../util/paths.js';
|
|
3
|
+
import { runMigrations } from './migrations.js';
|
|
4
|
+
const SCHEMA = `
|
|
5
|
+
CREATE TABLE IF NOT EXISTS tabs (
|
|
6
|
+
id TEXT PRIMARY KEY,
|
|
7
|
+
name TEXT UNIQUE NOT NULL,
|
|
8
|
+
session_id TEXT NOT NULL,
|
|
9
|
+
status TEXT NOT NULL DEFAULT 'idle',
|
|
10
|
+
working_dir TEXT NOT NULL,
|
|
11
|
+
pid INTEGER,
|
|
12
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
13
|
+
last_activity_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
CREATE TABLE IF NOT EXISTS messages (
|
|
17
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
18
|
+
tab_id TEXT NOT NULL REFERENCES tabs(id),
|
|
19
|
+
role TEXT NOT NULL,
|
|
20
|
+
content TEXT NOT NULL,
|
|
21
|
+
cost_usd REAL,
|
|
22
|
+
tokens_in INTEGER,
|
|
23
|
+
tokens_out INTEGER,
|
|
24
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
CREATE INDEX IF NOT EXISTS idx_messages_tab ON messages(tab_id, created_at);
|
|
28
|
+
|
|
29
|
+
CREATE TABLE IF NOT EXISTS memories (
|
|
30
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
31
|
+
content TEXT NOT NULL,
|
|
32
|
+
tab_name TEXT,
|
|
33
|
+
source TEXT NOT NULL DEFAULT 'tool',
|
|
34
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
CREATE TABLE IF NOT EXISTS pending_messages (
|
|
38
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
39
|
+
tab_name TEXT NOT NULL,
|
|
40
|
+
message TEXT NOT NULL,
|
|
41
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
42
|
+
processed INTEGER NOT NULL DEFAULT 0
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
CREATE INDEX IF NOT EXISTS idx_pending_unprocessed ON pending_messages(processed, created_at);
|
|
46
|
+
`;
|
|
47
|
+
let db = null;
|
|
48
|
+
export function getDb() {
|
|
49
|
+
if (db)
|
|
50
|
+
return db;
|
|
51
|
+
ensureBeecorkDirs();
|
|
52
|
+
const dbPath = getDbPath();
|
|
53
|
+
db = new Database(dbPath);
|
|
54
|
+
db.pragma('journal_mode = WAL');
|
|
55
|
+
db.pragma('foreign_keys = ON');
|
|
56
|
+
db.exec(SCHEMA);
|
|
57
|
+
runMigrations(db);
|
|
58
|
+
return db;
|
|
59
|
+
}
|
|
60
|
+
export function closeDb() {
|
|
61
|
+
if (db) {
|
|
62
|
+
db.close();
|
|
63
|
+
db = null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/db/index.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD,MAAM,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0Cd,CAAC;AAEF,IAAI,EAAE,GAA6B,IAAI,CAAC;AAExC,MAAM,UAAU,KAAK;IACnB,IAAI,EAAE;QAAE,OAAO,EAAE,CAAC;IAElB,iBAAiB,EAAE,CAAC;IACpB,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,EAAE,GAAG,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC1B,EAAE,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;IAChC,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC;IAC/B,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAChB,aAAa,CAAC,EAAE,CAAC,CAAC;IAElB,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,MAAM,UAAU,OAAO;IACrB,IAAI,EAAE,EAAE,CAAC;QACP,EAAE,CAAC,KAAK,EAAE,CAAC;QACX,EAAE,GAAG,IAAI,CAAC;IACZ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../../src/db/migrations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAqD3C,wBAAgB,aAAa,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,GAAG,IAAI,CAwCzD"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { logger } from '../util/logger.js';
|
|
2
|
+
const MIGRATIONS = [
|
|
3
|
+
{
|
|
4
|
+
version: 2,
|
|
5
|
+
description: 'Add user_id column for multi-user support',
|
|
6
|
+
up: `
|
|
7
|
+
ALTER TABLE tabs ADD COLUMN user_id TEXT NOT NULL DEFAULT 'local';
|
|
8
|
+
ALTER TABLE memories ADD COLUMN user_id TEXT NOT NULL DEFAULT 'local';
|
|
9
|
+
`,
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
version: 3,
|
|
13
|
+
description: 'Add schema_version table',
|
|
14
|
+
up: '', // Already handled by bootstrap
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
version: 4,
|
|
18
|
+
description: 'Add cron_jobs table in SQLite',
|
|
19
|
+
up: `
|
|
20
|
+
CREATE TABLE IF NOT EXISTS cron_jobs (
|
|
21
|
+
id TEXT PRIMARY KEY,
|
|
22
|
+
name TEXT NOT NULL,
|
|
23
|
+
schedule_type TEXT NOT NULL,
|
|
24
|
+
schedule TEXT NOT NULL,
|
|
25
|
+
tab_name TEXT NOT NULL DEFAULT 'default',
|
|
26
|
+
message TEXT NOT NULL,
|
|
27
|
+
enabled INTEGER NOT NULL DEFAULT 1,
|
|
28
|
+
user_id TEXT NOT NULL DEFAULT 'local',
|
|
29
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
30
|
+
last_run_at TEXT,
|
|
31
|
+
next_run_at TEXT
|
|
32
|
+
);
|
|
33
|
+
CREATE INDEX IF NOT EXISTS idx_cron_jobs_user ON cron_jobs(user_id, enabled);
|
|
34
|
+
`,
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
version: 5,
|
|
38
|
+
description: 'Add type column to pending_messages for notifications',
|
|
39
|
+
up: `
|
|
40
|
+
ALTER TABLE pending_messages ADD COLUMN type TEXT NOT NULL DEFAULT 'message';
|
|
41
|
+
ALTER TABLE pending_messages ADD COLUMN user_id TEXT NOT NULL DEFAULT 'local';
|
|
42
|
+
`,
|
|
43
|
+
},
|
|
44
|
+
];
|
|
45
|
+
export function runMigrations(db) {
|
|
46
|
+
// Ensure schema_version table exists
|
|
47
|
+
db.exec(`
|
|
48
|
+
CREATE TABLE IF NOT EXISTS schema_version (
|
|
49
|
+
version INTEGER NOT NULL DEFAULT 1
|
|
50
|
+
);
|
|
51
|
+
`);
|
|
52
|
+
// Get current version
|
|
53
|
+
let row = db.prepare('SELECT version FROM schema_version').get();
|
|
54
|
+
if (!row) {
|
|
55
|
+
db.prepare('INSERT INTO schema_version (version) VALUES (?)').run(1);
|
|
56
|
+
row = { version: 1 };
|
|
57
|
+
}
|
|
58
|
+
const currentVersion = row.version;
|
|
59
|
+
// Apply pending migrations
|
|
60
|
+
for (const migration of MIGRATIONS) {
|
|
61
|
+
if (migration.version <= currentVersion)
|
|
62
|
+
continue;
|
|
63
|
+
if (!migration.up) {
|
|
64
|
+
db.prepare('UPDATE schema_version SET version = ?').run(migration.version);
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
logger.info(`DB migration v${migration.version}: ${migration.description}`);
|
|
68
|
+
try {
|
|
69
|
+
db.exec(migration.up);
|
|
70
|
+
db.prepare('UPDATE schema_version SET version = ?').run(migration.version);
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
// Column might already exist from a previous partial migration
|
|
74
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
75
|
+
if (msg.includes('duplicate column name')) {
|
|
76
|
+
logger.info(`Migration v${migration.version}: columns already exist, skipping`);
|
|
77
|
+
db.prepare('UPDATE schema_version SET version = ?').run(migration.version);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
throw err;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=migrations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrations.js","sourceRoot":"","sources":["../../src/db/migrations.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAQ3C,MAAM,UAAU,GAAgB;IAC9B;QACE,OAAO,EAAE,CAAC;QACV,WAAW,EAAE,2CAA2C;QACxD,EAAE,EAAE;;;KAGH;KACF;IACD;QACE,OAAO,EAAE,CAAC;QACV,WAAW,EAAE,0BAA0B;QACvC,EAAE,EAAE,EAAE,EAAE,+BAA+B;KACxC;IACD;QACE,OAAO,EAAE,CAAC;QACV,WAAW,EAAE,+BAA+B;QAC5C,EAAE,EAAE;;;;;;;;;;;;;;;KAeH;KACF;IACD;QACE,OAAO,EAAE,CAAC;QACV,WAAW,EAAE,uDAAuD;QACpE,EAAE,EAAE;;;KAGH;KACF;CACF,CAAC;AAEF,MAAM,UAAU,aAAa,CAAC,EAAqB;IACjD,qCAAqC;IACrC,EAAE,CAAC,IAAI,CAAC;;;;GAIP,CAAC,CAAC;IAEH,sBAAsB;IACtB,IAAI,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,oCAAoC,CAAC,CAAC,GAAG,EAAqC,CAAC;IACpG,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,EAAE,CAAC,OAAO,CAAC,iDAAiD,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACrE,GAAG,GAAG,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;IACvB,CAAC;IAED,MAAM,cAAc,GAAG,GAAG,CAAC,OAAO,CAAC;IAEnC,2BAA2B;IAC3B,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,IAAI,SAAS,CAAC,OAAO,IAAI,cAAc;YAAE,SAAS;QAClD,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,CAAC;YAClB,EAAE,CAAC,OAAO,CAAC,uCAAuC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;YAC3E,SAAS;QACX,CAAC;QAED,MAAM,CAAC,IAAI,CAAC,iBAAiB,SAAS,CAAC,OAAO,KAAK,SAAS,CAAC,WAAW,EAAE,CAAC,CAAC;QAC5E,IAAI,CAAC;YACH,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;YACtB,EAAE,CAAC,OAAO,CAAC,uCAAuC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QAC7E,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,+DAA+D;YAC/D,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC7D,IAAI,GAAG,CAAC,QAAQ,CAAC,uBAAuB,CAAC,EAAE,CAAC;gBAC1C,MAAM,CAAC,IAAI,CAAC,cAAc,SAAS,CAAC,OAAO,mCAAmC,CAAC,CAAC;gBAChF,EAAE,CAAC,OAAO,CAAC,uCAAuC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;YAC7E,CAAC;iBAAM,CAAC;gBACN,MAAM,GAAG,CAAC;YACZ,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|