beecork 1.7.1 → 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/cron/scheduler.d.ts +0 -22
- package/dist/cron/scheduler.js +0 -220
- package/dist/cron/store.d.ts +0 -12
- package/dist/cron/store.js +0 -83
- 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/machines/forwarder.d.ts +0 -7
- package/dist/machines/forwarder.js +0 -35
- package/dist/machines/index.d.ts +0 -1
- package/dist/machines/index.js +0 -1
- package/dist/machines/registry.d.ts +0 -15
- package/dist/machines/registry.js +0 -46
- 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/loader.d.ts +0 -8
- package/dist/media/loader.js +0 -46
- 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/memory/extractor.d.ts +0 -5
- package/dist/memory/extractor.js +0 -157
- 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/pipe/anthropic-client.d.ts +0 -9
- package/dist/pipe/anthropic-client.js +0 -50
- package/dist/pipe/brain.d.ts +0 -16
- package/dist/pipe/brain.js +0 -89
- package/dist/pipe/memory-store.d.ts +0 -8
- package/dist/pipe/memory-store.js +0 -39
- package/dist/pipe/project-scanner.d.ts +0 -6
- package/dist/pipe/project-scanner.js +0 -26
- package/dist/pipe/types.d.ts +0 -34
- package/dist/pipe/types.js +0 -1
- 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 -416
- 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 -32
- package/dist/session/subprocess.js +0 -230
- package/dist/session/tab-store.d.ts +0 -28
- package/dist/session/tab-store.js +0 -78
- package/dist/session/tool-classifier.d.ts +0 -4
- package/dist/session/tool-classifier.js +0 -56
- 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 -225
- package/dist/types.js +0 -3
- 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/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
package/dist/voice/stt.d.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
export interface STTProvider {
|
|
2
|
-
transcribe(filePath: string): Promise<string>;
|
|
3
|
-
warmup?(): Promise<void>;
|
|
4
|
-
}
|
|
5
|
-
/** OpenAI Whisper API provider */
|
|
6
|
-
export declare class WhisperAPIProvider implements STTProvider {
|
|
7
|
-
private apiKey;
|
|
8
|
-
constructor(apiKey: string);
|
|
9
|
-
warmup(): Promise<void>;
|
|
10
|
-
transcribe(filePath: string): Promise<string>;
|
|
11
|
-
}
|
|
12
|
-
/** Create STT provider from config */
|
|
13
|
-
export declare function createSTTProvider(config: {
|
|
14
|
-
provider: string;
|
|
15
|
-
apiKey?: string;
|
|
16
|
-
}): STTProvider | null;
|
package/dist/voice/stt.js
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { readFile } from 'node:fs/promises';
|
|
2
|
-
import { logger } from '../util/logger.js';
|
|
3
|
-
import { assertInsideMediaDir } from '../media/store.js';
|
|
4
|
-
/** OpenAI Whisper API provider */
|
|
5
|
-
export class WhisperAPIProvider {
|
|
6
|
-
apiKey;
|
|
7
|
-
constructor(apiKey) {
|
|
8
|
-
this.apiKey = apiKey;
|
|
9
|
-
}
|
|
10
|
-
async warmup() {
|
|
11
|
-
try {
|
|
12
|
-
// Make a lightweight request to warm up the HTTPS connection
|
|
13
|
-
await fetch('https://api.openai.com/v1/models', {
|
|
14
|
-
headers: { Authorization: `Bearer ${this.apiKey}` },
|
|
15
|
-
signal: AbortSignal.timeout(5000),
|
|
16
|
-
});
|
|
17
|
-
}
|
|
18
|
-
catch {
|
|
19
|
-
/* non-critical */
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
async transcribe(filePath) {
|
|
23
|
-
// Defense-in-depth: callers should already constrain filePath to the media dir,
|
|
24
|
-
// but verify here so a leaked/forged MediaAttachment.filePath cannot exfiltrate
|
|
25
|
-
// arbitrary files (e.g. ~/.ssh/id_rsa) to the Whisper API.
|
|
26
|
-
const safePath = assertInsideMediaDir(filePath);
|
|
27
|
-
const buffer = await readFile(safePath);
|
|
28
|
-
const formData = new FormData();
|
|
29
|
-
formData.append('file', new Blob([buffer]), 'audio.ogg');
|
|
30
|
-
formData.append('model', 'whisper-1');
|
|
31
|
-
const response = await fetch('https://api.openai.com/v1/audio/transcriptions', {
|
|
32
|
-
method: 'POST',
|
|
33
|
-
headers: { Authorization: `Bearer ${this.apiKey}` },
|
|
34
|
-
body: formData,
|
|
35
|
-
signal: AbortSignal.timeout(60000),
|
|
36
|
-
});
|
|
37
|
-
if (!response.ok) {
|
|
38
|
-
throw new Error(`Whisper API error: ${response.status} ${response.statusText}`);
|
|
39
|
-
}
|
|
40
|
-
const result = (await response.json());
|
|
41
|
-
return result.text;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
/** Create STT provider from config */
|
|
45
|
-
export function createSTTProvider(config) {
|
|
46
|
-
switch (config.provider) {
|
|
47
|
-
case 'whisper-api':
|
|
48
|
-
if (!config.apiKey) {
|
|
49
|
-
logger.warn('Whisper API key not configured, voice transcription disabled');
|
|
50
|
-
return null;
|
|
51
|
-
}
|
|
52
|
-
return new WhisperAPIProvider(config.apiKey);
|
|
53
|
-
default:
|
|
54
|
-
logger.warn(`Unknown STT provider: ${config.provider}`);
|
|
55
|
-
return null;
|
|
56
|
-
}
|
|
57
|
-
}
|
package/dist/voice/tts.d.ts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
export interface TTSProvider {
|
|
2
|
-
synthesize(text: string): Promise<string>;
|
|
3
|
-
}
|
|
4
|
-
/** OpenAI TTS API provider */
|
|
5
|
-
export declare class OpenAITTSProvider implements TTSProvider {
|
|
6
|
-
private apiKey;
|
|
7
|
-
private voice;
|
|
8
|
-
constructor(apiKey: string, voice?: string);
|
|
9
|
-
synthesize(text: string): Promise<string>;
|
|
10
|
-
}
|
|
11
|
-
/** ElevenLabs TTS provider */
|
|
12
|
-
export declare class ElevenLabsTTSProvider implements TTSProvider {
|
|
13
|
-
private apiKey;
|
|
14
|
-
private voiceId;
|
|
15
|
-
constructor(apiKey: string, voiceId?: string);
|
|
16
|
-
synthesize(text: string): Promise<string>;
|
|
17
|
-
}
|
|
18
|
-
/** Create TTS provider from config */
|
|
19
|
-
export declare function createTTSProvider(config: {
|
|
20
|
-
provider: string;
|
|
21
|
-
apiKey?: string;
|
|
22
|
-
voice?: string;
|
|
23
|
-
}): TTSProvider | null;
|
package/dist/voice/tts.js
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import { logger } from '../util/logger.js';
|
|
2
|
-
import { saveMedia } from '../media/store.js';
|
|
3
|
-
/** OpenAI TTS API provider */
|
|
4
|
-
export class OpenAITTSProvider {
|
|
5
|
-
apiKey;
|
|
6
|
-
voice;
|
|
7
|
-
constructor(apiKey, voice = 'alloy') {
|
|
8
|
-
this.apiKey = apiKey;
|
|
9
|
-
this.voice = voice;
|
|
10
|
-
}
|
|
11
|
-
async synthesize(text) {
|
|
12
|
-
// Truncate very long text (TTS has limits)
|
|
13
|
-
const truncated = text.slice(0, 4096);
|
|
14
|
-
const response = await fetch('https://api.openai.com/v1/audio/speech', {
|
|
15
|
-
method: 'POST',
|
|
16
|
-
headers: {
|
|
17
|
-
Authorization: `Bearer ${this.apiKey}`,
|
|
18
|
-
'Content-Type': 'application/json',
|
|
19
|
-
},
|
|
20
|
-
body: JSON.stringify({
|
|
21
|
-
model: 'tts-1',
|
|
22
|
-
input: truncated,
|
|
23
|
-
voice: this.voice,
|
|
24
|
-
response_format: 'opus',
|
|
25
|
-
}),
|
|
26
|
-
signal: AbortSignal.timeout(60000),
|
|
27
|
-
});
|
|
28
|
-
if (!response.ok) {
|
|
29
|
-
throw new Error(`TTS API error: ${response.status} ${response.statusText}`);
|
|
30
|
-
}
|
|
31
|
-
const buffer = Buffer.from(await response.arrayBuffer());
|
|
32
|
-
return saveMedia(buffer, 'ogg', 'voice-reply.ogg');
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
/** ElevenLabs TTS provider */
|
|
36
|
-
export class ElevenLabsTTSProvider {
|
|
37
|
-
apiKey;
|
|
38
|
-
voiceId;
|
|
39
|
-
constructor(apiKey, voiceId = '21m00Tcm4TlvDq8ikWAM') {
|
|
40
|
-
this.apiKey = apiKey;
|
|
41
|
-
this.voiceId = voiceId;
|
|
42
|
-
}
|
|
43
|
-
async synthesize(text) {
|
|
44
|
-
const truncated = text.slice(0, 5000);
|
|
45
|
-
const response = await fetch(`https://api.elevenlabs.io/v1/text-to-speech/${this.voiceId}`, {
|
|
46
|
-
method: 'POST',
|
|
47
|
-
headers: {
|
|
48
|
-
'xi-api-key': this.apiKey,
|
|
49
|
-
'Content-Type': 'application/json',
|
|
50
|
-
},
|
|
51
|
-
body: JSON.stringify({
|
|
52
|
-
text: truncated,
|
|
53
|
-
model_id: 'eleven_monolingual_v1',
|
|
54
|
-
output_format: 'mp3_44100_128',
|
|
55
|
-
}),
|
|
56
|
-
signal: AbortSignal.timeout(60000),
|
|
57
|
-
});
|
|
58
|
-
if (!response.ok) {
|
|
59
|
-
throw new Error(`ElevenLabs API error: ${response.status} ${response.statusText}`);
|
|
60
|
-
}
|
|
61
|
-
const buffer = Buffer.from(await response.arrayBuffer());
|
|
62
|
-
return saveMedia(buffer, 'mp3', 'voice-reply.mp3');
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
/** Create TTS provider from config */
|
|
66
|
-
export function createTTSProvider(config) {
|
|
67
|
-
switch (config.provider) {
|
|
68
|
-
case 'openai':
|
|
69
|
-
if (!config.apiKey) {
|
|
70
|
-
logger.warn('OpenAI API key not configured for TTS');
|
|
71
|
-
return null;
|
|
72
|
-
}
|
|
73
|
-
return new OpenAITTSProvider(config.apiKey, config.voice);
|
|
74
|
-
case 'elevenlabs':
|
|
75
|
-
if (!config.apiKey) {
|
|
76
|
-
logger.warn('ElevenLabs API key not configured for TTS');
|
|
77
|
-
return null;
|
|
78
|
-
}
|
|
79
|
-
return new ElevenLabsTTSProvider(config.apiKey, config.voice);
|
|
80
|
-
default:
|
|
81
|
-
logger.warn(`Unknown TTS provider: ${config.provider}`);
|
|
82
|
-
return null;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { execAsync } from '../tasks/scheduler.js';
|
|
2
|
-
export async function evaluateWatcher(watcher) {
|
|
3
|
-
try {
|
|
4
|
-
const { stdout } = await execAsync(watcher.checkCommand, { timeout: 30000 });
|
|
5
|
-
const trimmed = stdout.trim();
|
|
6
|
-
const output = trimmed.length > 10240 ? trimmed.slice(0, 10240) + ' [truncated]' : trimmed;
|
|
7
|
-
const triggered = evaluateCondition(output, watcher.condition);
|
|
8
|
-
return { triggered, output };
|
|
9
|
-
}
|
|
10
|
-
catch (err) {
|
|
11
|
-
if (watcher.condition === 'error' || watcher.condition === 'any') {
|
|
12
|
-
return { triggered: true, output: `Check failed: ${err}` };
|
|
13
|
-
}
|
|
14
|
-
// Command failed but condition isn't 'error' — log warning, don't trigger
|
|
15
|
-
return { triggered: false, output: `Check failed: ${err}` };
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
function evaluateCondition(output, condition) {
|
|
19
|
-
if (condition.startsWith('contains '))
|
|
20
|
-
return output.includes(condition.slice(9).trim());
|
|
21
|
-
if (condition.startsWith('not contains '))
|
|
22
|
-
return !output.includes(condition.slice(13).trim());
|
|
23
|
-
if (condition.startsWith('> ')) {
|
|
24
|
-
const num = parseFloat(output);
|
|
25
|
-
const threshold = parseFloat(condition.slice(2));
|
|
26
|
-
if (Number.isNaN(num)) {
|
|
27
|
-
return true;
|
|
28
|
-
} // Can't parse = something's wrong = trigger
|
|
29
|
-
return num > threshold;
|
|
30
|
-
}
|
|
31
|
-
if (condition.startsWith('< ')) {
|
|
32
|
-
const num = parseFloat(output);
|
|
33
|
-
const threshold = parseFloat(condition.slice(2));
|
|
34
|
-
if (Number.isNaN(num)) {
|
|
35
|
-
return true;
|
|
36
|
-
}
|
|
37
|
-
return num < threshold;
|
|
38
|
-
}
|
|
39
|
-
if (condition === 'any')
|
|
40
|
-
return output.length > 0;
|
|
41
|
-
if (condition === 'error')
|
|
42
|
-
return false; // Only triggers on command failure (caught above)
|
|
43
|
-
// Default: check if output is non-empty and not "ok"
|
|
44
|
-
return output.length > 0 && output.toLowerCase() !== 'ok';
|
|
45
|
-
}
|
package/dist/watchers/index.d.ts
DELETED
package/dist/watchers/index.js
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
export declare class WatcherScheduler {
|
|
2
|
-
private store;
|
|
3
|
-
private nextCheckAt;
|
|
4
|
-
private running;
|
|
5
|
-
private stopping;
|
|
6
|
-
onNotify: ((text: string) => Promise<void>) | null;
|
|
7
|
-
/** Load all watchers from store and schedule them */
|
|
8
|
-
loadAndSchedule(): void;
|
|
9
|
-
/** Check for the reload signal file and reload if present */
|
|
10
|
-
checkForReload(): void;
|
|
11
|
-
/**
|
|
12
|
-
* Fire any watchers whose nextCheckAt has elapsed. Called every ~5s by the daemon
|
|
13
|
-
* poll loop. Sleep-resilient: after wake, the next tick catches all overdue checks.
|
|
14
|
-
*/
|
|
15
|
-
tick(): void;
|
|
16
|
-
/** Stop the scheduler. In-flight runCheck promises detach and resolve naturally. */
|
|
17
|
-
stopAll(): void;
|
|
18
|
-
private runCheck;
|
|
19
|
-
private executeAction;
|
|
20
|
-
}
|
|
@@ -1,183 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { WatcherStore } from './store.js';
|
|
4
|
-
import { evaluateWatcher } from './evaluator.js';
|
|
5
|
-
import { execAsync, intervalToMs } from '../tasks/scheduler.js';
|
|
6
|
-
import { getLogsDir, getWatcherReloadSignalPath } from '../util/paths.js';
|
|
7
|
-
import { logger } from '../util/logger.js';
|
|
8
|
-
import { PendingMessageStore } from '../session/pending-store.js';
|
|
9
|
-
import { logActivity } from '../timeline/index.js';
|
|
10
|
-
export class WatcherScheduler {
|
|
11
|
-
store = new WatcherStore();
|
|
12
|
-
// watcherId -> due time in ms epoch for next check
|
|
13
|
-
nextCheckAt = new Map();
|
|
14
|
-
// watcherIds with an in-flight runCheck
|
|
15
|
-
running = new Set();
|
|
16
|
-
stopping = false;
|
|
17
|
-
onNotify = null;
|
|
18
|
-
/** Load all watchers from store and schedule them */
|
|
19
|
-
loadAndSchedule() {
|
|
20
|
-
this.stopAll();
|
|
21
|
-
this.stopping = false;
|
|
22
|
-
const watchers = this.store.list();
|
|
23
|
-
let scheduled = 0;
|
|
24
|
-
for (const watcher of watchers) {
|
|
25
|
-
if (!watcher.enabled)
|
|
26
|
-
continue;
|
|
27
|
-
const ms = parseScheduleToMs(watcher.schedule);
|
|
28
|
-
if (!ms) {
|
|
29
|
-
logger.error(`Watcher: invalid schedule for "${watcher.name}": ${watcher.schedule}`);
|
|
30
|
-
continue;
|
|
31
|
-
}
|
|
32
|
-
// Seed nextCheckAt: if we have a lastCheckAt, fire `intervalMs` after it
|
|
33
|
-
// (catches up overdue checks after sleep/restart on the next tick).
|
|
34
|
-
// Otherwise wait `intervalMs` before first fire — matches today's setInterval semantics.
|
|
35
|
-
let due;
|
|
36
|
-
if (watcher.lastCheckAt) {
|
|
37
|
-
const last = new Date(watcher.lastCheckAt).getTime();
|
|
38
|
-
due = Number.isFinite(last) ? last + ms : Date.now() + ms;
|
|
39
|
-
}
|
|
40
|
-
else {
|
|
41
|
-
due = Date.now() + ms;
|
|
42
|
-
}
|
|
43
|
-
this.nextCheckAt.set(watcher.id, due);
|
|
44
|
-
scheduled++;
|
|
45
|
-
}
|
|
46
|
-
if (scheduled > 0 || watchers.length > 0) {
|
|
47
|
-
logger.info(`Watchers: loaded ${scheduled} active watchers (${watchers.length} total)`);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
/** Check for the reload signal file and reload if present */
|
|
51
|
-
checkForReload() {
|
|
52
|
-
const signalPath = getWatcherReloadSignalPath();
|
|
53
|
-
if (fs.existsSync(signalPath)) {
|
|
54
|
-
try {
|
|
55
|
-
fs.unlinkSync(signalPath);
|
|
56
|
-
}
|
|
57
|
-
catch {
|
|
58
|
-
/* race condition, ok */
|
|
59
|
-
}
|
|
60
|
-
logger.info('Watchers: reload signal detected, reloading');
|
|
61
|
-
this.loadAndSchedule();
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* Fire any watchers whose nextCheckAt has elapsed. Called every ~5s by the daemon
|
|
66
|
-
* poll loop. Sleep-resilient: after wake, the next tick catches all overdue checks.
|
|
67
|
-
*/
|
|
68
|
-
tick() {
|
|
69
|
-
if (this.stopping)
|
|
70
|
-
return;
|
|
71
|
-
const now = Date.now();
|
|
72
|
-
for (const [id, due] of [...this.nextCheckAt]) {
|
|
73
|
-
if (this.stopping)
|
|
74
|
-
return;
|
|
75
|
-
if (due > now)
|
|
76
|
-
continue;
|
|
77
|
-
if (this.running.has(id))
|
|
78
|
-
continue;
|
|
79
|
-
const watcher = this.store.get(id);
|
|
80
|
-
if (!watcher || !watcher.enabled) {
|
|
81
|
-
this.nextCheckAt.delete(id);
|
|
82
|
-
continue;
|
|
83
|
-
}
|
|
84
|
-
const intervalMs = parseScheduleToMs(watcher.schedule);
|
|
85
|
-
if (!intervalMs) {
|
|
86
|
-
this.nextCheckAt.delete(id);
|
|
87
|
-
continue;
|
|
88
|
-
}
|
|
89
|
-
// Advance BEFORE running — eliminates double-fire race
|
|
90
|
-
this.nextCheckAt.set(id, now + intervalMs);
|
|
91
|
-
this.running.add(id);
|
|
92
|
-
void this.runCheck(watcher).finally(() => this.running.delete(id));
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
/** Stop the scheduler. In-flight runCheck promises detach and resolve naturally. */
|
|
96
|
-
stopAll() {
|
|
97
|
-
this.stopping = true;
|
|
98
|
-
this.nextCheckAt.clear();
|
|
99
|
-
}
|
|
100
|
-
async runCheck(watcher) {
|
|
101
|
-
const logFile = path.join(getLogsDir(), `watcher-${watcher.name}.log`);
|
|
102
|
-
try {
|
|
103
|
-
const result = await evaluateWatcher(watcher);
|
|
104
|
-
this.store.markChecked(watcher.id);
|
|
105
|
-
if (result.triggered) {
|
|
106
|
-
this.store.markTriggered(watcher.id);
|
|
107
|
-
await fs.promises.appendFile(logFile, `[${new Date().toISOString()}] TRIGGERED: ${result.output.slice(0, 500)}\n`);
|
|
108
|
-
logActivity('watcher_triggered', `Watcher "${watcher.name}" triggered`, {
|
|
109
|
-
details: result.output.slice(0, 500),
|
|
110
|
-
});
|
|
111
|
-
await this.executeAction(watcher, result.output);
|
|
112
|
-
}
|
|
113
|
-
else {
|
|
114
|
-
await fs.promises.appendFile(logFile, `[${new Date().toISOString()}] OK: ${result.output.slice(0, 200)}\n`);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
catch (err) {
|
|
118
|
-
const errMsg = err instanceof Error ? err.message : String(err);
|
|
119
|
-
logger.error(`Watcher "${watcher.name}" check failed:`, err);
|
|
120
|
-
await fs.promises.appendFile(logFile, `[${new Date().toISOString()}] ERROR: ${errMsg}\n`);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
async executeAction(watcher, output) {
|
|
124
|
-
switch (watcher.action) {
|
|
125
|
-
case 'notify':
|
|
126
|
-
if (this.onNotify) {
|
|
127
|
-
await this.onNotify(`Watcher "${watcher.name}" triggered:\n${output.slice(0, 500)}`);
|
|
128
|
-
}
|
|
129
|
-
break;
|
|
130
|
-
case 'fix':
|
|
131
|
-
if (watcher.actionDetails) {
|
|
132
|
-
try {
|
|
133
|
-
await execAsync(watcher.actionDetails, { timeout: 60000 });
|
|
134
|
-
if (this.onNotify) {
|
|
135
|
-
await this.onNotify(`Watcher "${watcher.name}" triggered and auto-fixed:\n${output.slice(0, 300)}`);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
catch (err) {
|
|
139
|
-
const errMsg = err instanceof Error ? err.message : String(err);
|
|
140
|
-
logger.error(`Watcher "${watcher.name}" fix command failed:`, err);
|
|
141
|
-
if (this.onNotify) {
|
|
142
|
-
await this.onNotify(`Watcher "${watcher.name}" triggered but fix failed: ${errMsg}`);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
else {
|
|
147
|
-
logger.warn(`Watcher "${watcher.name}" has action=fix but no actionDetails`);
|
|
148
|
-
}
|
|
149
|
-
break;
|
|
150
|
-
case 'delegate':
|
|
151
|
-
// Write to pending_messages for the daemon to pick up
|
|
152
|
-
if (watcher.actionDetails) {
|
|
153
|
-
try {
|
|
154
|
-
const { getDb } = await import('../db/index.js');
|
|
155
|
-
const db = getDb();
|
|
156
|
-
// actionDetails format: "tabName: message" or just a message for default tab
|
|
157
|
-
const colonIdx = watcher.actionDetails.indexOf(':');
|
|
158
|
-
let tabName = 'default';
|
|
159
|
-
let message = watcher.actionDetails;
|
|
160
|
-
if (colonIdx > 0 && colonIdx < 30) {
|
|
161
|
-
tabName = watcher.actionDetails.slice(0, colonIdx).trim();
|
|
162
|
-
message = watcher.actionDetails.slice(colonIdx + 1).trim();
|
|
163
|
-
}
|
|
164
|
-
const fullMessage = `[Watcher "${watcher.name}" triggered] ${message}\n\nWatcher output:\n${output.slice(0, 500)}`;
|
|
165
|
-
PendingMessageStore.enqueueDelegation(tabName, fullMessage, db);
|
|
166
|
-
if (this.onNotify) {
|
|
167
|
-
await this.onNotify(`Watcher "${watcher.name}" triggered -- delegated to tab:${tabName}`);
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
catch (err) {
|
|
171
|
-
logger.error(`Watcher "${watcher.name}" delegation failed:`, err);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
break;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
/** Parse schedule string like "every 5m", "every 1h", or raw interval "30m" to milliseconds */
|
|
179
|
-
function parseScheduleToMs(schedule) {
|
|
180
|
-
// Strip "every " prefix if present
|
|
181
|
-
const raw = schedule.replace(/^every\s+/i, '');
|
|
182
|
-
return intervalToMs(raw);
|
|
183
|
-
}
|
package/dist/watchers/store.d.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type { Watcher } from './types.js';
|
|
2
|
-
export declare class WatcherStore {
|
|
3
|
-
list(): Watcher[];
|
|
4
|
-
get(id: string): Watcher | undefined;
|
|
5
|
-
create(watcher: Omit<Watcher, 'lastCheckAt' | 'lastTriggeredAt' | 'triggerCount' | 'enabled' | 'createdAt'>): void;
|
|
6
|
-
update(id: string, fields: Partial<Watcher>): boolean;
|
|
7
|
-
delete(id: string): boolean;
|
|
8
|
-
markChecked(id: string): void;
|
|
9
|
-
markTriggered(id: string): void;
|
|
10
|
-
}
|
package/dist/watchers/store.js
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { getDb } from '../db/index.js';
|
|
2
|
-
function rowToWatcher(row) {
|
|
3
|
-
return {
|
|
4
|
-
id: row.id,
|
|
5
|
-
name: row.name,
|
|
6
|
-
description: row.description,
|
|
7
|
-
checkCommand: row.check_command,
|
|
8
|
-
condition: row.condition,
|
|
9
|
-
action: row.action,
|
|
10
|
-
actionDetails: row.action_details,
|
|
11
|
-
schedule: row.schedule,
|
|
12
|
-
lastCheckAt: row.last_check_at,
|
|
13
|
-
lastTriggeredAt: row.last_triggered_at,
|
|
14
|
-
triggerCount: row.trigger_count,
|
|
15
|
-
enabled: row.enabled === 1,
|
|
16
|
-
createdAt: row.created_at,
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
export class WatcherStore {
|
|
20
|
-
list() {
|
|
21
|
-
const db = getDb();
|
|
22
|
-
return db.prepare('SELECT * FROM watchers ORDER BY created_at').all().map(rowToWatcher);
|
|
23
|
-
}
|
|
24
|
-
get(id) {
|
|
25
|
-
const db = getDb();
|
|
26
|
-
const row = db.prepare('SELECT * FROM watchers WHERE id = ?').get(id);
|
|
27
|
-
return row ? rowToWatcher(row) : undefined;
|
|
28
|
-
}
|
|
29
|
-
create(watcher) {
|
|
30
|
-
const db = getDb();
|
|
31
|
-
db.prepare(`INSERT INTO watchers (id, name, description, check_command, condition, action, action_details, schedule)
|
|
32
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`).run(watcher.id, watcher.name, watcher.description, watcher.checkCommand, watcher.condition, watcher.action, watcher.actionDetails, watcher.schedule);
|
|
33
|
-
}
|
|
34
|
-
update(id, fields) {
|
|
35
|
-
const db = getDb();
|
|
36
|
-
const existing = this.get(id);
|
|
37
|
-
if (!existing)
|
|
38
|
-
return false;
|
|
39
|
-
const merged = { ...existing, ...fields };
|
|
40
|
-
db.prepare(`UPDATE watchers SET name=?, description=?, check_command=?, condition=?, action=?, action_details=?, schedule=?, enabled=? WHERE id=?`).run(merged.name, merged.description, merged.checkCommand, merged.condition, merged.action, merged.actionDetails, merged.schedule, merged.enabled ? 1 : 0, id);
|
|
41
|
-
return true;
|
|
42
|
-
}
|
|
43
|
-
delete(id) {
|
|
44
|
-
const db = getDb();
|
|
45
|
-
const result = db.prepare('DELETE FROM watchers WHERE id = ?').run(id);
|
|
46
|
-
return result.changes > 0;
|
|
47
|
-
}
|
|
48
|
-
markChecked(id) {
|
|
49
|
-
const db = getDb();
|
|
50
|
-
db.prepare('UPDATE watchers SET last_check_at = ? WHERE id = ?').run(new Date().toISOString(), id);
|
|
51
|
-
}
|
|
52
|
-
markTriggered(id) {
|
|
53
|
-
const db = getDb();
|
|
54
|
-
db.prepare('UPDATE watchers SET last_triggered_at = ?, trigger_count = trigger_count + 1 WHERE id = ?').run(new Date().toISOString(), id);
|
|
55
|
-
}
|
|
56
|
-
}
|
package/dist/watchers/types.d.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
export interface Watcher {
|
|
2
|
-
id: string;
|
|
3
|
-
name: string;
|
|
4
|
-
description: string | null;
|
|
5
|
-
checkCommand: string;
|
|
6
|
-
condition: string;
|
|
7
|
-
action: 'notify' | 'fix' | 'delegate';
|
|
8
|
-
actionDetails: string | null;
|
|
9
|
-
schedule: string;
|
|
10
|
-
lastCheckAt: string | null;
|
|
11
|
-
lastTriggeredAt: string | null;
|
|
12
|
-
triggerCount: number;
|
|
13
|
-
enabled: boolean;
|
|
14
|
-
createdAt: string;
|
|
15
|
-
}
|
package/dist/watchers/types.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/scripts/postinstall.mjs
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// Beecork postinstall hook.
|
|
3
|
-
//
|
|
4
|
-
// Runs after `npm install -g beecork[@<version>]`. Goal: if a beecork daemon
|
|
5
|
-
// is already managed by the OS (launchd plist on mac, systemd unit on linux)
|
|
6
|
-
// from a DIFFERENT install path than this freshly-installed one, rewrite the
|
|
7
|
-
// unit file to point at this install and signal the daemon to restart so the
|
|
8
|
-
// running process picks up the new code without the user having to think about
|
|
9
|
-
// install prefixes.
|
|
10
|
-
//
|
|
11
|
-
// No-ops cleanly in every other case (first install, local install,
|
|
12
|
-
// dependency install, no daemon, dependency install in CI, etc.).
|
|
13
|
-
|
|
14
|
-
import { fileURLToPath } from 'node:url';
|
|
15
|
-
import path from 'node:path';
|
|
16
|
-
import fs from 'node:fs';
|
|
17
|
-
|
|
18
|
-
// Run only for global installs. npm sets npm_config_global="true" for `npm install -g`.
|
|
19
|
-
// `npm ci` / `npm install` (no -g) inside the repo leaves it unset/empty — skip those.
|
|
20
|
-
if (process.env.npm_config_global !== 'true') {
|
|
21
|
-
process.exit(0);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// Resolve the auto-heal module from dist/. If the user installed from a
|
|
25
|
-
// tarball without dist/ (shouldn't happen — `files` whitelists dist/, and
|
|
26
|
-
// `prepublishOnly` builds it — but be defensive), skip silently.
|
|
27
|
-
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
28
|
-
const installRoot = path.dirname(here);
|
|
29
|
-
const autoHealModule = path.join(installRoot, 'dist', 'util', 'auto-heal.js');
|
|
30
|
-
|
|
31
|
-
if (!fs.existsSync(autoHealModule)) {
|
|
32
|
-
// Built artifacts missing — skip silently. This is the npm-publish-with-no-build
|
|
33
|
-
// edge case which is already caught by `prepublishOnly`. Don't fail the install.
|
|
34
|
-
process.exit(0);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
try {
|
|
38
|
-
const { autoHealInstall } = await import(autoHealModule);
|
|
39
|
-
const result = autoHealInstall(import.meta.url);
|
|
40
|
-
if (result.action === 'rewrote-unit' || result.action === 'rewrote-and-signaled') {
|
|
41
|
-
process.stderr.write(`[beecork] launchd/systemd unit re-pointed at ${result.newDaemonScript}\n`);
|
|
42
|
-
}
|
|
43
|
-
if (result.action === 'signaled-daemon' || result.action === 'rewrote-and-signaled') {
|
|
44
|
-
process.stderr.write(`[beecork] running daemon signaled to restart on new code\n`);
|
|
45
|
-
}
|
|
46
|
-
// 'noop' / 'skip' → silent
|
|
47
|
-
} catch (err) {
|
|
48
|
-
// Never fail an install on auto-heal trouble — just leave a breadcrumb.
|
|
49
|
-
process.stderr.write(`[beecork] postinstall auto-heal skipped: ${err?.message ?? err}\n`);
|
|
50
|
-
}
|