Files
HomeAgent/internal/plugins/webui/handler_terminal.go
JianFeeeee e70d2171ee refactor(terminal): 内核开终端/命令历史权威视图,WebUI 与 CLI 都改接内核
按「内核开,两个插件接」重构终端与命令历史的数据归属。

背景:此前 WebUI 与 CLI 各订 EventToolCall/EventTerminalOutput 攅一份状态,
同一件事两份推导,还各自踩过同一个坑——工具 result 是 Go 的 map 文本
(map[cols:80 ... id:term_2 ...]),断言成 map[string]interface{} 永远失败,
terminal_create 的 id 回填不生效,/terminals 因此恒空(WebUI 也一样)。
实测确认:WebUI 自己的 /api/v1/terminals 与 /api/v1/cmd/history 同样是空的。

内核开(权威唯一真相):
- internal/agent/core/terminal_registry.go:TerminalRegistry 归并两类事件——
  EventToolCall(terminal_create/close、cmd_run,id/command 从 args 或 Go map
  文本回填)与 EventTerminalOutput(agentcli 生命周期 + 输出,含 64KB 缓冲上限、
  100 条命令历史、50 个终端上限)。
- internal/sdk/terminal.go:新增 TerminalAPI(ListTerminals/CmdHistory)与
  TerminalStatus/CmdExecStatus DTO。**不塞进 KernelStatus**:那是全量快照,
  前端每 3 秒轮询 /kernel,背上每终端最多 64KB 输出会让轮询成本爆炸;
  终端输出是按需拉取的明细,另开接口。
- Agent 订阅自己的事件总线(subscribeTerminalRegistry),且**只根 agent 建**
  (驻留子共用同一总线,每个子都建会 N+1 份重复记账)。
- SDKConfig/Registry/bootstrap 接线:pluginReg.SetTerminalAPI(agent)。

生产者补全(agentcli):终端无输出时 ticker 不发事件,内核就无从知道终端
存在。新增 emitTermState,在 handleCreate/handleClose/readLoop 退出(超时/
进程结束/读取错误/stopCh)显式上报 running 状态,并给输出事件补 command 字段。
handleClose 改为接收 *sdk.PluginSDK 以便上报。

两个插件接(消费方):
- WebUI:删掉本地 termStates/cmdHistory/subscribeTerminalStream/handleToolEvent
  及不再使用的 getStr;/terminals 与 /cmd/history 直接读 s.Terminal()。
- CLI:删掉上一轮刚加的 subscribeToolEvents 与 cliTermState/cliCmdExec;
  /terminals 与 /cmd/history 直接读 s.Terminal()。两条路(local/remote)都通。

测试:新增 terminal_registry_test.go,锁死 Go map 文本解析(旧缺陷根因)、
生命周期、CLI 直调路径(无 EventToolCall 仅凭 output 事件建条目)、历史与
终端数量上限。全量 go test ./internal/... ./cmd/... 通过。
2026-09-17 18:56:15 +08:00

45 lines
1.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package webui
import (
"net/http"
sdk "gitcode.com/JianFeeeee/HomeAgent/internal/sdk"
)
// 终端面:终端会话列表与命令历史接口。
//
// 「内核开,两个插件接」后,终端会话与命令历史的权威视图在内核
// internal/agent/core/terminal_registry.go内核订阅自己的事件总线
// 归并 EventToolCallterminal_create/close、cmd_run与 EventTerminalOutput
// agentcli 生命周期 + 输出。WebUI 不再自己订阅事件攒一份——直接读内核
// 开放的 TerminalAPIs.Terminal()),与 CLI 同源同口径。
//
// 为什么不塞进 KernelStatus那是全量快照前端每 3 秒轮询 /kernel
// 把每终端最多 64KB 的输出缓冲背进去会让轮询成本爆炸。
// handleTerminals 返回内核权威的终端会话快照。
func (h *Handler) handleTerminals(w http.ResponseWriter, r *http.Request) {
if h.term == nil {
writeJSON(w, http.StatusOK, map[string]interface{}{"terminals": []interface{}{}})
return
}
terms := h.term.ListTerminals()
if terms == nil {
terms = []sdk.TerminalStatus{}
}
writeJSON(w, http.StatusOK, map[string]interface{}{"terminals": terms})
}
// handleCmdHistory 返回内核权威的命令执行历史快照。
func (h *Handler) handleCmdHistory(w http.ResponseWriter, r *http.Request) {
if h.term == nil {
writeJSON(w, http.StatusOK, map[string]interface{}{"history": []interface{}{}})
return
}
cmds := h.term.CmdHistory()
if cmds == nil {
cmds = []sdk.CmdExecStatus{}
}
writeJSON(w, http.StatusOK, map[string]interface{}{"history": cmds})
}