Lines 1-43typescript
2 * Butler 应用入口 — 定时任务 + 多平台消息
4import { dispatch } from '@flue/runtime';
5import { flue } from '@flue/runtime/routing';
6import { Cron } from 'croner';
7import { Hono } from 'hono';
8import { readFileSync } from 'fs';
9import { spawn } from 'child_process';
10import { join } from 'path';
HighChild Process
Package source references child process execution.
src/app.tsView on unpkg · L8 11import { homedir } from 'os';
12import jeevesAgent from './agents/jeeves';
14const app = new Hono();
15app.route('/', flue());
17// ====== Playwright MCP (浏览器自动化) ======
18// 行业标准: 配置在 .env → 启动时拉起 → 退出时自动清理
19if (process.env.JEEVES_MCP_BROWSER === 'true') {
20 const MCP_PORT = 8931;
21 const mcpProcess = spawn('npx', ['@playwright/mcp@latest', '--port', String(MCP_PORT), '--headless'], {
22 stdio: 'ignore',
HighRuntime Package Install
Package source invokes a package manager install command at runtime.
src/app.tsView on unpkg · L20 26 console.log(`[MCP] Playwright MCP 已启动 (port ${MCP_PORT})`);
28 const cleanup = () => { try { mcpProcess.kill(); } catch {} };
29 process.on('exit', cleanup);
30 process.on('SIGINT', cleanup);
31 process.on('SIGTERM', cleanup);
35const schedulesPath = join(homedir(), '.jeeves', 'schedules.yaml');
36let schedules: any[] = [];
38 const content = readFileSync(schedulesPath, 'utf-8');
39 let current: any = null;
40 for (const line of content.split('\n')) {
41 const t = line.trim();
42 if (t.startsWith('#') || !t) continue;
43 if (t.startsWith('- name:')) {