Skip to content

Event Bus 技术文档

Dinotty 使用基于 tokio::sync::broadcast 的全局事件总线,在系统各模块间分发事件。

目录


概述

EventBus 是 Dinotty 的核心事件分发机制:

  • 发布/订阅模式:所有模块都可以发布和订阅事件
  • 广播:每个订阅者收到所有事件,自行过滤
  • 异步:基于 tokio broadcast channel,不阻塞发布者
  • 容量:1024 条事件缓冲,慢消费者会丢失旧事件
rust
use crate::event_bus::{EventBus, BusEvent};

// 订阅
let mut rx = event_bus.subscribe();

// 发布
event_bus.publish(BusEvent::SessionCreated {
    pane_id: "pane-1".into(),
    shell_type: "zsh".into(),
});

// 接收
while let Ok(event) = rx.recv().await {
    match event {
        BusEvent::CommandFinished { exit_code, .. } => { /* 处理 */ }
        _ => {}
    }
}

事件类型

CommandFinished

命令执行完成时触发(通过 OSC 133 或 prompt 检测)。

json
{
  "event": "command_finished",
  "data": {
    "pane_id": "pane-abc123",
    "command": "",
    "exit_code": 0,
    "duration_ms": 150,
    "stdout": "file1.txt\n",
    "method": "shell_integration"
  }
}

触发时机:

  • PTY 读取任务检测到 OSC 133 D 序列
  • Prompt 检测 fallback(100ms 无输出后匹配到 prompt 模式)
  • Agent API 命令超时

SessionCreated

新终端会话创建时触发。

json
{
  "event": "session_created",
  "data": {
    "pane_id": "pane-abc123",
    "shell_type": "zsh"
  }
}

SessionClosed

终端会话关闭时触发(PTY 进程退出)。

json
{
  "event": "session_closed",
  "data": {
    "pane_id": "pane-abc123",
    "exit_code": null
  }
}

TabCreated / TabClosed

标签页创建/关闭时触发。

json
{"event": "tab_created", "data": {"tab_id": "tab-1", "pane_id": "pane-abc"}}
{"event": "tab_closed", "data": {"tab_id": "tab-1"}}

FileChanged

文件监视器检测到文件变更时触发。path 使用服务端平台的原生路径格式;Windows 事件序列化为 JSON 时会转义为 C:\\Users\\dev\\project\\file.txt

json
{
  "event": "file_changed",
  "data": {
    "path": "/Users/dev/project/src/main.rs",
    "change_type": "modified"
  }
}

Custom

插件自定义事件。

json
{
  "event": "custom",
  "data": {
    "plugin_id": "my-plugin",
    "event_name": "build_complete",
    "data": {"success": true, "duration": 5000}
  }
}

订阅方式

Rust 代码

rust
let mut rx = manager.event_bus.subscribe();
tokio::spawn(async move {
    while let Ok(event) = rx.recv().await {
        // 处理事件
    }
});

WebSocket Agent API

连接 WS /ws/agent 后自动接收所有事件:

json
{
  "type": "event",
  "event": {"event": "command_finished", "data": {...}}
}

Webhook

配置 webhook 后,匹配的事件会通过 HTTP POST 推送。


序列化格式

事件使用 serde 标签联合序列化:

rust
#[derive(Serialize)]
#[serde(tag = "event", content = "data", rename_all = "snake_case")]
pub enum BusEvent { ... }

JSON 格式:{"event": "<variant_name>", "data": {...}}


集成方式

在模块中发布事件

rust
manager.event_bus.publish(BusEvent::CommandFinished {
    pane_id: pane_id.clone(),
    command: String::new(),
    exit_code: result.exit_code,
    duration_ms: result.duration_ms,
    stdout: output,
    method: result.method,
});

在模块中订阅事件

rust
let mut rx = manager.event_bus.subscribe();
tokio::spawn(async move {
    while let Ok(event) = rx.recv().await {
        match event {
            BusEvent::SessionCreated { pane_id, .. } => {
                // 初始化新会话的资源
            }
            BusEvent::CommandFinished { exit_code, .. } => {
                // 记录命令执行结果
            }
            _ => {}
        }
    }
});

注意事项

  • 不保证送达:如果消费者处理太慢,旧事件会被丢弃
  • 不保证顺序:broadcast channel 不保证跨订阅者的顺序
  • 幂等处理:消费者应能处理重复或丢失的事件

基于 MIT 许可发布