同步完整源码至远程:扁平化重构现状 + 未入库源文件

- 补入库:server/internal/db/dump.go、redis 分页/扫描预算测试、opsxdb-mcp main_test、
  packages/opsxdb-mcp package-lock、docs 审计与对比文档等 37 个新文件
- 含结构扁平化进行中的 80 处修改与 155 处旧结构删除(server/ src/ src-tauri/ 为当前有效源)
- 构建产物(bin/target/dist)与签名私钥仍按规则排除
This commit is contained in:
logan committed 2026-10-16 03:47:33 +08:00
1 parent 5c0be49217
commit 2c0bfafd8d
269 files changed
+10372 -16435

No files matched your search

+115
View File
@@ -183,6 +183,121 @@ func TestRunAgentRejectsWriteViaRuntime(t *testing.T) {
}
}
func TestAgentCannotLeaveCurrentMySQLConnectionOrDatabase(t *testing.T) {
rt := aitools.NewRuntime(aitools.Policy{Mode: aitools.ModeReadOnly, AllowedConnectionIDs: []string{"current", "other"}, DefaultLimit: 10, MaxLimit: 20})
rt.FindConn = func(id string) (*aitools.SavedConnection, error) {
return &aitools.SavedConnection{ID: id, Driver: "mysql", Database: "demo"}, nil
}
var queries int
rt.ExecSQL = func(_ context.Context, req db.ConnReq) db.ExecResult {
queries++
if req.Database != "demo" {
t.Fatalf("wrong database: %s", req.Database)
}
return db.ExecResult{Columns: []string{"id"}, Rows: []map[string]interface{}{{"id": 1}}, RowCount: 1}
}
rt.Audit = func(aitools.AuditEntry) {}
call := func(id, conn, database string) ToolCall {
args, _ := json.Marshal(map[string]any{"connection_id": conn, "database": database, "sql": "SELECT id FROM users LIMIT 1"})
return ToolCall{ID: id, Type: "function", Function: ToolCallFunc{Name: aitools.ToolExecuteQuery, Arguments: string(args)}}
}
llm := &scriptedLLM{turns: []ToolsTurn{
{ToolCalls: []ToolCall{call("other", "other", "demo"), call("db", "current", "secret"), call("ok", "current", "demo")}},
{Content: "只查询了当前库。"},
}}
res, err := RunAgent(context.Background(), llm, rt, AgentReq{Prompt: "查询当前库", ConnectionID: "current", Driver: "mysql", Database: "demo"})
if err != nil {
t.Fatal(err)
}
if len(res.Steps) != 3 || res.Steps[0].OK || res.Steps[1].OK || !res.Steps[2].OK || queries != 1 {
t.Fatalf("Agent escaped scope or rejected valid query: steps=%+v queries=%d", res.Steps, queries)
}
}
func TestAgentRedisKeepOneKeyNeedsConfirmation(t *testing.T) {
rt := aitools.NewRuntime(aitools.Policy{Mode: aitools.ModeReadWrite, AllowedConnectionIDs: []string{"redis"}})
rt.RejectCallerConfirm = true
rt.FindConn = func(id string) (*aitools.SavedConnection, error) {
return &aitools.SavedConnection{ID: id, Driver: "redis", Database: "0"}, nil
}
var executed []string
rt.ExecSQL = func(_ context.Context, req db.ConnReq) db.ExecResult {
executed = append(executed, req.SQL)
return db.ExecResult{Columns: []string{"value"}, Rows: []map[string]interface{}{{"value": 3}}, RowCount: 1}
}
rt.Audit = func(aitools.AuditEntry) {}
call := func(id, command string) ToolCall {
args, _ := json.Marshal(map[string]any{"connection_id": "redis", "command": command, "confirm_danger": true})
return ToolCall{ID: id, Type: "function", Function: ToolCallFunc{Name: aitools.ToolExecuteRedisCommand, Arguments: string(args)}}
}
llm := &scriptedLLM{turns: []ToolsTurn{
{ToolCalls: []ToolCall{call("read", "EXISTS key1 key2 key3")}},
{ToolCalls: []ToolCall{call("delete", "DEL key2 key3")}},
}}
res, err := RunAgent(context.Background(), llm, rt, AgentReq{Prompt: "key1 key2 key3 只保留 key1", ConnectionID: "redis", Driver: "redis", Database: "0"})
if err != nil {
t.Fatal(err)
}
if len(executed) != 1 || executed[0] != "EXISTS key1 key2 key3" || len(res.Steps) != 2 || !res.Steps[1].NeedConfirm {
t.Fatalf("删除必须停在用户确认前: executed=%v steps=%+v", executed, res.Steps)
}
if err := aitools.VerifyAgentBind("redis", "0", "DEL key2 key3", res.Steps[1].ConfirmBind); err != nil {
t.Fatal(err)
}
}
func TestAgentContinueRefreshesWritePolicy(t *testing.T) {
rt := aitools.NewRuntime(aitools.Policy{Mode: aitools.ModeReadOnly})
llm := &scriptedLLM{turns: []ToolsTurn{{Content: "当前只读"}}}
_, err := RunAgent(context.Background(), llm, rt, AgentReq{
Prompt: "继续", Continue: true,
Prior: []Message{{Role: "system", Content: "旧策略:允许写入"}, {Role: "user", Content: "只保留 key1"}},
})
if err != nil {
t.Fatal(err)
}
if len(llm.seen) != 1 || !strings.Contains(llm.seen[0][0].Content, "当前策略为只读") || strings.Contains(llm.seen[0][0].Content, "旧策略") {
t.Fatalf("续聊必须使用当前权限: %+v", llm.seen)
}
}
func TestAgentRedisReadThenWriteNeedsCurrentDBConfirmation(t *testing.T) {
rt := aitools.NewRuntime(aitools.Policy{Mode: aitools.ModeReadWrite, AllowedConnectionIDs: []string{"redis"}, DefaultLimit: 10, MaxLimit: 20})
rt.FindConn = func(id string) (*aitools.SavedConnection, error) {
return &aitools.SavedConnection{ID: id, Driver: "redis", Database: "0"}, nil
}
var commands []string
rt.ExecSQL = func(_ context.Context, req db.ConnReq) db.ExecResult {
if req.Database != "1" {
t.Fatalf("Redis DB drifted: %s", req.Database)
}
commands = append(commands, req.SQL)
return db.ExecResult{Columns: []string{"value"}, Rows: []map[string]interface{}{{"value": "old"}}, RowCount: 1}
}
rt.Audit = func(aitools.AuditEntry) {}
call := func(id, cmd string) ToolCall {
args, _ := json.Marshal(map[string]any{"connection_id": "redis", "command": cmd})
return ToolCall{ID: id, Type: "function", Function: ToolCallFunc{Name: aitools.ToolExecuteRedisCommand, Arguments: string(args)}}
}
llm := &scriptedLLM{turns: []ToolsTurn{
{ToolCalls: []ToolCall{call("read", "GET smoke:key")}},
{ToolCalls: []ToolCall{call("write", "SET smoke:key new")}},
}}
res, err := RunAgent(context.Background(), llm, rt, AgentReq{Prompt: "读取然后修改键", ConnectionID: "redis", Driver: "redis", Database: "1"})
if err != nil {
t.Fatal(err)
}
if len(commands) != 1 || commands[0] != "GET smoke:key" || len(res.Steps) != 2 || !res.Steps[0].OK || !res.Steps[1].NeedConfirm {
t.Fatalf("unexpected Redis execution/confirmation: commands=%v steps=%+v", commands, res.Steps)
}
if err := aitools.VerifyAgentBind("redis", "1", "SET smoke:key new", res.Steps[1].ConfirmBind); err != nil {
t.Fatal(err)
}
if err := aitools.VerifyAgentBind("redis", "0", "SET smoke:key new", res.Steps[1].ConfirmBind); err == nil {
t.Fatal("other DB must not reuse the confirmation")
}
}
func TestParseToolsTurn(t *testing.T) {
content := "hi"
raw, _ := json.Marshal(map[string]any{