mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-09-21 17:38:10 +00:00
协议面(protocol.go,§3.2 method id 平移为 method 名): - NDJSON 帧,双向复用同一对 stdio;ID>0 需应答,ID==0 为通知(post-and-forget) - 51 个 C ABI method id 全部平移为可读 method 名并标注原编号对照 编号本身扔掉——加能力不用改两边常量表,不再有 47 夹在 7 和 8 之间的痕迹 - case 25(CORE_FREE_STRING) 无对应 method:进程模型下各自 GC,概念消失 - case 23/24(事件订阅) 与 io.setToolBlocks 今日均为空实现「给不了」, 子进程下首次真正可给(§3.8 能力对齐) - 新增 stage.lock/stage.unlock(C ABI 下不存在跨进程锁概念) - StageInvokeParams 不含 StageContext 数据本身——数据在共享段,只带 stage 名 + seq 进程面(process.go,§2.3 保留现有生命周期机制): - Spawn: 启动 + 握手(协议版本不匹配显式拒绝,不半兼容运行) - readLoop: NDJSON 分派应答/插件反向请求,1MB 单帧上限(大 payload 走 arena) - CallContext: ctx 取消时立即返回**且清理 pending 条目** 对比 cgo:超时只让调用方返回,goroutine 永久卡在 C 调用里(现网泄漏 26 次) - Notify: ID=0 不占 pending 表,满足约束 B(流式逐 token 发布不得等待消费者) - markExited: EOF/退出 → 唤醒全部在途调用 → onExit 回调 这是「把 panic 捕获换成进程退出检测」的落点,plugin_health 逻辑完全复用 - Stop: plugin.stop → 宽限期 → 超时 Kill;Kill 后 OS 回收全部资源,零泄漏 - serveRequest 带 panic 隔离:内核 handler panic 不带崩 readLoop 验证(10 项,真实子进程而非 mock,含 -race): - 握手/工具调用/错误上报(插件失败调用方收到 error,非假成功) - 插件反向调用内核(tool.register + settings.get 双向往返) - **崩溃隔离**:插件 panic → 子进程 exit 2,内核存活、收到 onExit、在途调用不挂死 - 优雅停止 / **Kill 卡死插件**(ctx 超时返回 + pending 清零 + 资源回收) - 通知不等应答(100 条 < 1s)/ 50 并发调用应答不串 / 协议版本不匹配拒绝 接口冻结: git diff third_party/homeagent-sdk/sdk/ 为空
121 lines
2.5 KiB
Go
121 lines
2.5 KiB
Go
//go:build ignore
|
||
|
||
// callbackplugin 验证插件 → 内核的反向调用(51 个 core.* method 的机制)。
|
||
package main
|
||
|
||
import (
|
||
"bufio"
|
||
"encoding/json"
|
||
"os"
|
||
"sync"
|
||
)
|
||
|
||
type request struct {
|
||
ID uint64 `json:"id,omitempty"`
|
||
Method string `json:"method"`
|
||
Params json.RawMessage `json:"params,omitempty"`
|
||
}
|
||
|
||
type response struct {
|
||
ID uint64 `json:"id"`
|
||
Result interface{} `json:"result,omitempty"`
|
||
Error string `json:"error,omitempty"`
|
||
}
|
||
|
||
var (
|
||
out = bufio.NewWriter(os.Stdout)
|
||
writeMu sync.Mutex
|
||
nextID uint64
|
||
pending = map[uint64]chan json.RawMessage{}
|
||
pendMu sync.Mutex
|
||
)
|
||
|
||
func send(v interface{}) {
|
||
b, _ := json.Marshal(v)
|
||
writeMu.Lock()
|
||
out.Write(b)
|
||
out.WriteByte('\n')
|
||
out.Flush()
|
||
writeMu.Unlock()
|
||
}
|
||
|
||
// callKernel 反向调用内核并等待应答。
|
||
func callKernel(method string, params interface{}) json.RawMessage {
|
||
pendMu.Lock()
|
||
nextID++
|
||
id := nextID
|
||
ch := make(chan json.RawMessage, 1)
|
||
pending[id] = ch
|
||
pendMu.Unlock()
|
||
|
||
var raw json.RawMessage
|
||
if params != nil {
|
||
b, _ := json.Marshal(params)
|
||
raw = b
|
||
}
|
||
send(request{ID: id, Method: method, Params: raw})
|
||
return <-ch
|
||
}
|
||
|
||
func main() {
|
||
in := bufio.NewScanner(bufio.NewReader(os.Stdin))
|
||
in.Buffer(make([]byte, 0, 64*1024), 1024*1024)
|
||
|
||
for in.Scan() {
|
||
line := make([]byte, len(in.Bytes()))
|
||
copy(line, in.Bytes())
|
||
|
||
var probe struct {
|
||
ID uint64 `json:"id"`
|
||
Method string `json:"method"`
|
||
}
|
||
if err := json.Unmarshal(line, &probe); err != nil {
|
||
continue
|
||
}
|
||
|
||
// 内核对我们反向调用的应答
|
||
if probe.Method == "" {
|
||
var resp struct {
|
||
ID uint64 `json:"id"`
|
||
Result json.RawMessage `json:"result"`
|
||
}
|
||
json.Unmarshal(line, &resp)
|
||
pendMu.Lock()
|
||
ch, ok := pending[resp.ID]
|
||
delete(pending, resp.ID)
|
||
pendMu.Unlock()
|
||
if ok {
|
||
ch <- resp.Result
|
||
}
|
||
continue
|
||
}
|
||
|
||
var req request
|
||
json.Unmarshal(line, &req)
|
||
switch req.Method {
|
||
case "handshake":
|
||
send(response{ID: req.ID, Result: map[string]interface{}{
|
||
"protocol": 1,
|
||
"sdk_version": "test",
|
||
"plugin_name": "cb",
|
||
"pid": os.Getpid(),
|
||
}})
|
||
case "plugin.start":
|
||
// 在独立 goroutine 里回调,避免阻塞读循环
|
||
go func(id uint64) {
|
||
callKernel("tool.register", map[string]interface{}{"name": "cb_tool"})
|
||
callKernel("settings.get", map[string]interface{}{"key": "some_key"})
|
||
send(response{ID: id})
|
||
}(req.ID)
|
||
case "plugin.stop":
|
||
send(response{ID: req.ID})
|
||
out.Flush()
|
||
os.Exit(0)
|
||
default:
|
||
if req.ID != 0 {
|
||
send(response{ID: req.ID})
|
||
}
|
||
}
|
||
}
|
||
}
|