Appearance
解决方案设计
方案 A:流式输出监听(推荐)
架构概述
使用 Bun 的 spawn API 启动 OpenCode 进程,通过流式读取 stdout/stderr 来实时捕获输出,并根据策略将信息发送到 Telegram。
数据流图
核心组件设计
1. 进程管理器 (ProcessManager)
职责:管理 OpenCode 进程的生命周期,包括启动、追踪、终止和清理。
typescript
interface ProcessInfo {
pid: number;
chatId: number; // 关联的 Telegram 聊天 ID
messageId?: number; // 用于回复的消息 ID
startTime: number;
status: 'running' | 'stopped' | 'failed';
}
class ProcessManager {
private processes: Map<number, ProcessInfo> = new Map();
private store: ProcessStore; // 持久化存储
async start(prompt: string, chatId: number, messageId: number): Promise<number>;
async stop(chatId: number): Promise<boolean>;
async getStatus(chatId: number): Promise<ProcessInfo | null>;
async cleanup(): Promise<void>;
}2. 流监听器 (StreamListener)
职责:实时监听 OpenCode 的 stdout/stderr 流,并将原始数据转发给解析器。
typescript
class StreamListener {
private buffer: string = '';
async listen(stdout: ReadableStream<Uint8Array>, stderr: ReadableStream<Uint8Array>): void;
private async readStream(stream: ReadableStream<Uint8Array>, type: 'stdout' | 'stderr'): Promise<void>;
private processChunk(chunk: string, type: 'stdout' | 'stderr'): void;
}3. 输出解析器 (OutputParser)
职责:解析 OpenCode 的输出,识别关键事件(工具调用、文件操作、错误等)。
typescript
interface ParsedEvent {
type: 'tool_call' | 'file_edit' | 'error' | 'progress' | 'completion' | 'other';
content: string;
timestamp: number;
rawOutput: string;
}
class OutputParser {
parse(output: string): ParsedEvent[];
}4. 通知策略 (NotifyStrategy)
职责:根据预定义规则决定何时发送通知以及如何格式化消息。
typescript
interface NotificationRule {
eventType: ParsedEvent['type'];
throttleMs: number; // 节流时间
includeFullOutput: boolean;
priority: 'high' | 'medium' | 'low';
}
class NotifyStrategy {
private rules: NotificationRule[];
shouldNotify(event: ParsedEvent): boolean;
formatMessage(event: ParsedEvent, fullOutput: string): string;
splitLongMessage(message: string): string[];
}5. Telegram 通知器 (TelegramNotifier)
职责:与 Telegram API 交互,发送消息、编辑消息、处理限流。
typescript
class TelegramNotifier {
private bot: Bot;
private messageQueue: Map<number, QueuedMessage[]>;
async sendMessage(chatId: number, text: string, options?: SendMessageOptions): Promise<void>;
async editMessage(chatId: number, messageId: number, text: string): Promise<void>;
async replyToMessage(chatId: number, messageId: number, text: string): Promise<void>;
private async enforceRateLimit(): Promise<void>;
}通知策略设计
策略等级
| 等级 | 触发条件 | 通知方式 | 示例 |
|---|---|---|---|
| 紧急 | 错误、异常、关键失败 | 立即发送、标记 | OpenCode 崩溃、编译失败 |
| 重要 | 工具调用完成、文件修改 | 实时发送、编辑更新 | 文件写入完成、测试通过 |
| 普通 | 进度信息、状态变化 | 节流发送(如每 30 秒) | 正在执行某操作 |
| 调试 | 详细日志、中间状态 | 可选发送(用户配置) | 工具调用详情、参数 |
消息格式化
typescript
// 紧急消息
🚨 错误:OpenCode 执行失败
原因:编译错误
详情:...
// 重要消息
✅ 文件已修改:src/index.ts
变更:+10 行,-5 行
// 进度消息
⏳ 正在执行:分析代码库...
已扫描 45/100 文件
// 最终结果
🎉 任务完成!
耗时:2m 30s
修改文件:3进程终止流程
方案 B:日志文件监听(备选)
架构概述
配置 OpenCode 将输出写入日志文件,通过文件监听器(如 chokidar)实时读取文件变化并发送通知。
优缺点对比
| 方面 | 方案 A(流式监听) | 方案 B(日志文件监听) |
|---|---|---|
| 实时性 | 高(实时流) | 中(文件轮询) |
| 复杂度 | 中 | 低(依赖文件系统) |
| 资源占用 | 低 | 中(磁盘 I/O) |
| 可靠性 | 高 | 中(可能丢失缓冲内容) |
| 部署难度 | 低 | 中(需要配置日志路径) |
| 容错性 | 中(进程断开需重连) | 高(文件可重读) |
推荐选择
推荐方案 A,原因:
- 更好的实时性和用户体验
- 不需要额外的文件系统依赖
- 更符合 Unix 管道哲学
- 易于调试和维护
方案 B 可作为备选,适用于:
- OpenCode 必须以特定模式运行(如 daemon 模式)
- 需要事后查询历史输出
- 进程和网络不稳定的环境
数据持久化方案
进程信息存储
| 选项 | 实现方式 | 优点 | 缺点 |
|---|---|---|---|
| 内存存储 | Map<string, ProcessInfo> | 简单、快速 | 机器人重启后丢失 |
| JSON 文件 | fs.writeFileSync | 简单、易调试 | 并发问题 |
| SQLite | better-sqlite3 | 成熟、支持查询 | 额外依赖 |
| Redis | 客户端库 | 高性能、支持过期 | 需要额外服务 |
推荐:JSON 文件 + 内存缓存,对于单机器人实例足够使用。
配置存储
typescript
interface BotConfig {
telegramToken: string;
notificationRules: NotificationRule[];
maxConcurrentProcesses: number;
processTimeoutMs: number;
autoKillTimeoutMs: number;
}安全性设计
权限控制
- 用户白名单:只允许特定用户 ID 使用机器人
- 指令限制:kill 指令需要额外验证(如仅限发起 OpenCode 请求的用户)
- 速率限制:防止恶意用户频繁请求
敏感信息过滤
typescript
function sanitizeOutput(output: string): string {
// 移除 API Keys、密码等敏感信息
return output
.replace(/Bearer\s+[A-Za-z0-9\-_]+/g, 'Bearer ***')
.replace(/password["\s]*[:=]["\s]*[^\s"]+/gi, 'password: ***')
.replace(/token["\s]*[:=]["\s]*[^\s"]+/gi, 'token: ***');
}错误处理策略
进程启动失败
- 返回错误消息给用户
- 记录日志
- 不影响其他功能
输出解析失败
- 跳过当前事件
- 记录错误日志
- 继续监听后续输出
Telegram API 失败
- 重试机制(最多 3 次)
- 退避策略(指数退避)
- 失败后记录日志,继续执行
实施建议
分阶段实施
第一阶段:基础功能
- 实现进程启动和基本的 stdout 监听
- 简单的通知(完整输出作为一条消息)
- 基础的 kill 指令
第二阶段:智能通知
- 实现输出解析和事件识别
- 应用通知策略和节流
- 消息格式化和分割
第三阶段:增强功能
- 多进程管理
- 进程持久化和恢复
- 高级通知(编辑消息、文件上传)
测试策略
- 单元测试:解析器、通知策略等独立组件
- 集成测试:完整的 OpenCode 执行流程
- 负载测试:多个并发进程、大量输出
- 故障测试:进程崩溃、网络中断等场景
参考资料
- OpenCode GitHub Repository - OpenCode 开源代码仓库
- Bun Documentation - Child Processes - Bun 进程管理文档
- grammY Documentation - Telegram Bot 库文档
- Node.js child_process Documentation - 进程管理参考