feat: wire daemon, agent, and telegram bot together
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
+41
-1
@@ -1,14 +1,41 @@
|
|||||||
|
import { Bot } from 'grammy';
|
||||||
import { Lifecycle } from './lifecycle.js';
|
import { Lifecycle } from './lifecycle.js';
|
||||||
import type { Config } from '../config/index.js';
|
import type { Config } from '../config/index.js';
|
||||||
|
import { AnthropicClient } from '../models/index.js';
|
||||||
|
import { NativeAgent } from '../backends/index.js';
|
||||||
|
import { createTelegramBot } from '../frontends/telegram/index.js';
|
||||||
|
|
||||||
export interface DaemonContext {
|
export interface DaemonContext {
|
||||||
config: Config;
|
config: Config;
|
||||||
lifecycle: Lifecycle;
|
lifecycle: Lifecycle;
|
||||||
|
bot: Bot;
|
||||||
|
agent: NativeAgent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const SYSTEM_PROMPT = `You are Flynn, a helpful personal AI assistant. You are direct, concise, and helpful. You can help with a variety of tasks including answering questions, providing information, and having conversations.
|
||||||
|
|
||||||
|
Keep responses focused and avoid unnecessary verbosity. Use markdown formatting when it improves readability.`;
|
||||||
|
|
||||||
export async function startDaemon(config: Config): Promise<DaemonContext> {
|
export async function startDaemon(config: Config): Promise<DaemonContext> {
|
||||||
const lifecycle = new Lifecycle();
|
const lifecycle = new Lifecycle();
|
||||||
|
|
||||||
|
// Initialize model client
|
||||||
|
const modelClient = new AnthropicClient({
|
||||||
|
model: config.models.default.model,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initialize native agent
|
||||||
|
const agent = new NativeAgent({
|
||||||
|
modelClient,
|
||||||
|
systemPrompt: SYSTEM_PROMPT,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initialize Telegram bot
|
||||||
|
const bot = createTelegramBot({
|
||||||
|
telegram: config.telegram,
|
||||||
|
agent,
|
||||||
|
});
|
||||||
|
|
||||||
// Register signal handlers
|
// Register signal handlers
|
||||||
const signalHandler = () => {
|
const signalHandler = () => {
|
||||||
lifecycle.shutdown().then(() => process.exit(0));
|
lifecycle.shutdown().then(() => process.exit(0));
|
||||||
@@ -22,9 +49,22 @@ export async function startDaemon(config: Config): Promise<DaemonContext> {
|
|||||||
process.off('SIGTERM', signalHandler);
|
process.off('SIGTERM', signalHandler);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Start bot
|
||||||
|
lifecycle.onShutdown(async () => {
|
||||||
|
await bot.stop();
|
||||||
|
console.log('Telegram bot stopped');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Use long polling (no webhook, no internet exposure)
|
||||||
|
bot.start({
|
||||||
|
onStart: (botInfo) => {
|
||||||
|
console.log(`Telegram bot started: @${botInfo.username}`);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
console.log('Flynn daemon started');
|
console.log('Flynn daemon started');
|
||||||
|
|
||||||
return { config, lifecycle };
|
return { config, lifecycle, bot, agent };
|
||||||
}
|
}
|
||||||
|
|
||||||
export { Lifecycle } from './lifecycle.js';
|
export { Lifecycle } from './lifecycle.js';
|
||||||
|
|||||||
+8
-2
@@ -2,6 +2,7 @@ import { loadConfig } from './config/index.js';
|
|||||||
import { startDaemon } from './daemon/index.js';
|
import { startDaemon } from './daemon/index.js';
|
||||||
import { resolve } from 'path';
|
import { resolve } from 'path';
|
||||||
import { homedir } from 'os';
|
import { homedir } from 'os';
|
||||||
|
import { existsSync } from 'fs';
|
||||||
|
|
||||||
const CONFIG_PATH = process.env.FLYNN_CONFIG
|
const CONFIG_PATH = process.env.FLYNN_CONFIG
|
||||||
?? resolve(homedir(), '.config/flynn/config.yaml');
|
?? resolve(homedir(), '.config/flynn/config.yaml');
|
||||||
@@ -10,12 +11,17 @@ async function main() {
|
|||||||
console.log('Flynn starting...');
|
console.log('Flynn starting...');
|
||||||
console.log(`Loading config from: ${CONFIG_PATH}`);
|
console.log(`Loading config from: ${CONFIG_PATH}`);
|
||||||
|
|
||||||
|
if (!existsSync(CONFIG_PATH)) {
|
||||||
|
console.error(`Config file not found: ${CONFIG_PATH}`);
|
||||||
|
console.error('Copy config/default.yaml to ~/.config/flynn/config.yaml and configure it.');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const config = loadConfig(CONFIG_PATH);
|
const config = loadConfig(CONFIG_PATH);
|
||||||
const daemon = await startDaemon(config);
|
const daemon = await startDaemon(config);
|
||||||
|
|
||||||
console.log(`Telegram bot configured for chat IDs: ${config.telegram.allowed_chat_ids.join(', ')}`);
|
console.log(`Allowed Telegram chat IDs: ${config.telegram.allowed_chat_ids.join(', ')}`);
|
||||||
console.log(`Server port: ${config.server.port}`);
|
|
||||||
|
|
||||||
// Keep process alive
|
// Keep process alive
|
||||||
await new Promise<void>((resolve) => {
|
await new Promise<void>((resolve) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user