feat: AutoZero 通用 Agent 框架(从 AutoGo 五阶段拆分完成)
llm(三协议 Provider)/ session(JSONL 持久化)/ tools(Registry、围栏、 builtin、computer)/ cdp(browser)/ skills / mcp / agent(循环、两级压缩、 编排、审批门控)+ 底座 jsonl/textx/execx/diag/scratch/spill/imagecache。 产品装配代码(config 桥接、schedule 工具、memory/plan/retro/prompt) 留在 AutoGo,宿主接入方式见 MIGRATION.md。
This commit is contained in:
commit
d5dac5a6fc
140 files changed
+26497
No files matched your search
+113
@@ -0,0 +1,113 @@
|
||||
package cdp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/coder/websocket"
|
||||
)
|
||||
|
||||
// tinyPNG 是 1x1 红色 PNG 的 base64(截图测试用)。
|
||||
const tinyPNG = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=="
|
||||
|
||||
// fakeCDPHandler 处理一条 CDP 命令并回响应;navigate 先发事件再回响应,
|
||||
// 用于验证"先订阅后触发"的事件时序。
|
||||
func fakeCDPHandler(t *testing.T, title string) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
c, err := websocket.Accept(w, r, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer c.Close(websocket.StatusInternalError, "")
|
||||
ctx := r.Context()
|
||||
for {
|
||||
_, data, err := c.Read(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var req struct {
|
||||
ID int64 `json:"id"`
|
||||
Method string `json:"method"`
|
||||
Params json.RawMessage `json:"params"`
|
||||
}
|
||||
if err := json.Unmarshal(data, &req); err != nil {
|
||||
continue
|
||||
}
|
||||
reply := func(v map[string]any) {
|
||||
b, _ := json.Marshal(map[string]any{"id": req.ID, "result": v})
|
||||
_ = c.Write(ctx, websocket.MessageText, b)
|
||||
}
|
||||
switch req.Method {
|
||||
case "Page.enable":
|
||||
reply(map[string]any{})
|
||||
case "Page.navigate":
|
||||
// 事件先于响应到达:验证 Subscribe 必须先行
|
||||
ev, _ := json.Marshal(map[string]any{"method": "Page.loadEventFired", "params": map[string]any{}})
|
||||
_ = c.Write(ctx, websocket.MessageText, ev)
|
||||
reply(map[string]any{"frameId": "f1"})
|
||||
case "Runtime.evaluate":
|
||||
reply(map[string]any{"result": map[string]any{"type": "string", "value": title}})
|
||||
case "Page.captureScreenshot":
|
||||
reply(map[string]any{"data": tinyPNG})
|
||||
default:
|
||||
b, _ := json.Marshal(map[string]any{"id": req.ID, "error": map[string]any{"code": -32601, "message": "unknown method"}})
|
||||
_ = c.Write(ctx, websocket.MessageText, b)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestConnCallAndEventTiming:事件先到时 Call 仍能配对响应、Subscribe 先行能等到事件。
|
||||
func TestConnCallAndEventTiming(t *testing.T) {
|
||||
srv := httptest.NewServer(fakeCDPHandler(t, "Fake Page"))
|
||||
defer srv.Close()
|
||||
wsURL := "ws" + strings.TrimPrefix(srv.URL, "http") + "/devtools/page/t1"
|
||||
|
||||
ctx := context.Background()
|
||||
conn, err := Dial(ctx, wsURL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
loaded := conn.Subscribe("Page.loadEventFired")
|
||||
if _, err := conn.Call(ctx, "Page.enable", nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := conn.Call(ctx, "Page.navigate", map[string]any{"url": "https://example.com"}); err != nil {
|
||||
t.Fatalf("navigate: %v", err)
|
||||
}
|
||||
// 事件在响应前已发出:订阅先行则必然能等到
|
||||
if _, err := loaded.Wait(ctx, 2*time.Second); err != nil {
|
||||
t.Fatalf("event wait: %v", err)
|
||||
}
|
||||
title, err := evaluateString(ctx, conn, "document.title")
|
||||
if err != nil || title != "Fake Page" {
|
||||
t.Fatalf("evaluate: %q %v", title, err)
|
||||
}
|
||||
// 服务端错误响应 → Go error
|
||||
if _, err := conn.Call(ctx, "NoSuch.Method", nil); err == nil || !strings.Contains(err.Error(), "unknown method") {
|
||||
t.Fatalf("expected rpc error, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestConnClosed:连接断开后 Call 立即失败。
|
||||
func TestConnClosed(t *testing.T) {
|
||||
srv := httptest.NewServer(fakeCDPHandler(t, "x"))
|
||||
defer srv.Close()
|
||||
wsURL := "ws" + strings.TrimPrefix(srv.URL, "http") + "/devtools/page/t1"
|
||||
ctx := context.Background()
|
||||
conn, err := Dial(ctx, wsURL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
conn.Close()
|
||||
if _, err := conn.Call(ctx, "Page.enable", nil); err == nil {
|
||||
t.Fatal("call on closed connection should fail")
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user