From 588ea12a2189d3d40ece7c202f2f5c1211ef8831 Mon Sep 17 00:00:00 2001 From: lwmacct Date: Sun, 14 Dec 2025 01:11:48 +0800 Subject: [PATCH] =?UTF-8?q?refactor(server):=20=E4=BB=A3=E7=A0=81=E8=B4=A8?= =?UTF-8?q?=E9=87=8F=E6=94=B9=E8=BF=9B=E5=92=8C=20Go=201.21+=20=E7=8E=B0?= =?UTF-8?q?=E4=BB=A3=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 使用 slices.Contains 替换手写循环查找逻辑 - 使用 maps.Copy 替换手写 map 复制循环 - 修复 errcheck 警告: 显式忽略 fmt.Sscanf 和 conn.Close 返回值 - 为预留的 registerDashboardRoutes 函数添加 nolint:unused 指令 - 修复测试文件中的 errcheck 警告 - 修复 sandbox 使用 bash 代替 sh 以支持 ulimit -u --- cmd/aster-server/main.go | 2 +- pkg/config/loader_test.go | 32 ++++++++--------- pkg/executionplan/executor_test.go | 4 +-- pkg/sandbox/local.go | 13 +++++-- server/handlers/dashboard_events.go | 53 ++++++++++------------------- server/handlers/remote_agent.go | 6 ++-- server/middleware.go | 12 ++----- server/observability/health.go | 7 ++-- server/routes.go | 5 ++- 9 files changed, 61 insertions(+), 73 deletions(-) diff --git a/cmd/aster-server/main.go b/cmd/aster-server/main.go index 11b1dbd..594ef04 100644 --- a/cmd/aster-server/main.go +++ b/cmd/aster-server/main.go @@ -134,7 +134,7 @@ func main() { redisPassword := os.Getenv("ASTER_REDIS_PASSWORD") redisDB := 0 if dbStr := os.Getenv("ASTER_REDIS_DB"); dbStr != "" { - fmt.Sscanf(dbStr, "%d", &redisDB) + _, _ = fmt.Sscanf(dbStr, "%d", &redisDB) } redisPrefix := os.Getenv("ASTER_REDIS_PREFIX") if redisPrefix == "" { diff --git a/pkg/config/loader_test.go b/pkg/config/loader_test.go index 9d23f60..cb86856 100644 --- a/pkg/config/loader_test.go +++ b/pkg/config/loader_test.go @@ -42,10 +42,10 @@ func TestNewLoaderWithOptions(t *testing.T) { func TestExpandVariables(t *testing.T) { // Set test environment variables - os.Setenv("TEST_VAR", "test_value") - os.Setenv("ANOTHER_VAR", "another_value") - defer os.Unsetenv("TEST_VAR") - defer os.Unsetenv("ANOTHER_VAR") + _ = os.Setenv("TEST_VAR", "test_value") + _ = os.Setenv("ANOTHER_VAR", "another_value") + defer func() { _ = os.Unsetenv("TEST_VAR") }() + defer func() { _ = os.Unsetenv("ANOTHER_VAR") }() loader := NewLoader() @@ -107,8 +107,8 @@ func TestExpandVariables(t *testing.T) { } func TestExpandVariablesWithPrefix(t *testing.T) { - os.Setenv("APP_TEST_VAR", "prefixed_value") - defer os.Unsetenv("APP_TEST_VAR") + _ = os.Setenv("APP_TEST_VAR", "prefixed_value") + defer func() { _ = os.Unsetenv("APP_TEST_VAR") }() loader := NewLoader(WithEnvPrefix("APP_")) @@ -129,8 +129,8 @@ func TestExpandVariablesWithCustomVariables(t *testing.T) { } // Custom variables should take precedence over environment - os.Setenv("CUSTOM_VAR", "env_value") - defer os.Unsetenv("CUSTOM_VAR") + _ = os.Setenv("CUSTOM_VAR", "env_value") + defer func() { _ = os.Unsetenv("CUSTOM_VAR") }() result = loader.expandVariables("${CUSTOM_VAR}") if result != "custom_value" { @@ -139,8 +139,8 @@ func TestExpandVariablesWithCustomVariables(t *testing.T) { } func TestExpandVariablesDisabled(t *testing.T) { - os.Setenv("TEST_VAR", "test_value") - defer os.Unsetenv("TEST_VAR") + _ = os.Setenv("TEST_VAR", "test_value") + defer func() { _ = os.Unsetenv("TEST_VAR") }() loader := NewLoader(WithEnvExpansion(false)) @@ -153,8 +153,8 @@ func TestExpandVariablesDisabled(t *testing.T) { } func TestLoadFromString(t *testing.T) { - os.Setenv("API_KEY", "sk-test-key") - defer os.Unsetenv("API_KEY") + _ = os.Setenv("API_KEY", "sk-test-key") + defer func() { _ = os.Unsetenv("API_KEY") }() loader := NewLoader() @@ -225,10 +225,10 @@ model_config: } func TestLoadAgentConfigWithEnvVars(t *testing.T) { - os.Setenv("TEST_API_KEY", "sk-env-key") - os.Setenv("TEST_MODEL", "claude-3-opus") - defer os.Unsetenv("TEST_API_KEY") - defer os.Unsetenv("TEST_MODEL") + _ = os.Setenv("TEST_API_KEY", "sk-env-key") + _ = os.Setenv("TEST_MODEL", "claude-3-opus") + defer func() { _ = os.Unsetenv("TEST_API_KEY") }() + defer func() { _ = os.Unsetenv("TEST_MODEL") }() tmpDir := t.TempDir() configPath := filepath.Join(tmpDir, "agent.yaml") diff --git a/pkg/executionplan/executor_test.go b/pkg/executionplan/executor_test.go index f1ab35c..d5127f4 100644 --- a/pkg/executionplan/executor_test.go +++ b/pkg/executionplan/executor_test.go @@ -691,7 +691,7 @@ func TestOnStepFailedCallback(t *testing.T) { ctx := context.Background() toolCtx := &tools.ToolContext{AgentID: "test-agent"} - executor.Execute(ctx, plan, toolCtx) + _ = executor.Execute(ctx, plan, toolCtx) if failedStep == nil { t.Fatal("OnStepFailed callback not called") @@ -784,7 +784,7 @@ func TestDependencyNotSatisfied(t *testing.T) { ctx := context.Background() toolCtx := &tools.ToolContext{AgentID: "test-agent"} - executor.Execute(ctx, plan, toolCtx) + _ = executor.Execute(ctx, plan, toolCtx) // Step 2 should be skipped because its dependency failed if plan.Steps[1].Status != StepStatusSkipped { diff --git a/pkg/sandbox/local.go b/pkg/sandbox/local.go index 59ec1b0..a16c797 100644 --- a/pkg/sandbox/local.go +++ b/pkg/sandbox/local.go @@ -21,6 +21,15 @@ import ( var sandboxLogger = logging.ForComponent("sandbox") +// getShell returns the preferred shell for command execution. +// Prefers bash (which supports all ulimit options), falls back to /bin/sh. +func getShell() string { + if shell, err := exec.LookPath("bash"); err == nil { + return shell + } + return "/bin/sh" +} + // SecurityLevel 安全级别 type SecurityLevel int @@ -447,7 +456,7 @@ func (ls *LocalSandbox) execWithLimits(ctx context.Context, cmd string, opts *Ex // 构建命令(带资源限制) shellCmd := ls.buildSecureCommand(cmd) - command := exec.CommandContext(execCtx, "sh", "-c", shellCmd) + command := exec.CommandContext(execCtx, getShell(), "-c", shellCmd) // 设置工作目录 workDir := ls.workDir @@ -1078,7 +1087,7 @@ func (ls *LocalSandbox) execDirect(ctx context.Context, cmd string, opts *ExecOp execCtx, cancel := context.WithTimeout(ctx, timeout) defer cancel() - command := exec.CommandContext(execCtx, "sh", "-c", cmd) + command := exec.CommandContext(execCtx, getShell(), "-c", cmd) workDir := ls.workDir if opts != nil && opts.WorkDir != "" { diff --git a/server/handlers/dashboard_events.go b/server/handlers/dashboard_events.go index da14ed5..69b1cca 100644 --- a/server/handlers/dashboard_events.go +++ b/server/handlers/dashboard_events.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "net/http" + "slices" "strings" "sync" "time" @@ -316,17 +317,8 @@ func (c *DashboardEventConnection) subscribeToAgent(ag *agent.Agent) { agentID := ag.ID() // Check if filters allow this agent - if len(c.filters.AgentIDs) > 0 { - found := false - for _, id := range c.filters.AgentIDs { - if id == agentID { - found = true - break - } - } - if !found { - return - } + if len(c.filters.AgentIDs) > 0 && !slices.Contains(c.filters.AgentIDs, agentID) { + return } c.subMu.Lock() @@ -383,17 +375,8 @@ func (c *DashboardEventConnection) subscribeToRemoteAgent(ra *agent.RemoteAgent) agentID := ra.ID() // Check if filters allow this agent - if len(c.filters.AgentIDs) > 0 { - found := false - for _, id := range c.filters.AgentIDs { - if id == agentID { - found = true - break - } - } - if !found { - return - } + if len(c.filters.AgentIDs) > 0 && !slices.Contains(c.filters.AgentIDs, agentID) { + return } c.subMu.Lock() @@ -604,7 +587,7 @@ func (c *DashboardEventConnection) shouldForward(envelope types.AgentEventEnvelo func (c *DashboardEventConnection) extractEventInfo(agentID string, envelope types.AgentEventEnvelope) map[string]any { // 将 Unix 秒级时间戳转换为 ISO 8601 格式字符串 timestamp := time.Unix(envelope.Bookmark.Timestamp, 0).Format(time.RFC3339) - + info := map[string]any{ "cursor": envelope.Cursor, "timestamp": timestamp, @@ -641,14 +624,14 @@ func (c *DashboardEventConnection) extractEventInfo(agentID string, envelope typ } case *types.MonitorToolExecutedEvent: info["data"] = map[string]any{ - "agent_id": agentID, - "tool_id": e.Call.ID, - "tool_name": e.Call.Name, - "state": string(e.Call.State), - "progress": e.Call.Progress, - "error": e.Call.Error, - "arguments": e.Call.Arguments, - "result": e.Call.Result, + "agent_id": agentID, + "tool_id": e.Call.ID, + "tool_name": e.Call.Name, + "state": string(e.Call.State), + "progress": e.Call.Progress, + "error": e.Call.Error, + "arguments": e.Call.Arguments, + "result": e.Call.Result, } case *types.MonitorStepCompleteEvent: info["data"] = map[string]any{ @@ -699,10 +682,10 @@ func (c *DashboardEventConnection) extractEventInfo(agentID string, envelope typ } case *types.ProgressToolEndEvent: info["data"] = map[string]any{ - "tool_id": e.Call.ID, - "state": string(e.Call.State), - "result": e.Call.Result, - "error": e.Call.Error, + "tool_id": e.Call.ID, + "state": string(e.Call.State), + "result": e.Call.Result, + "error": e.Call.Error, } case *types.ProgressToolProgressEvent: info["data"] = map[string]any{ diff --git a/server/handlers/remote_agent.go b/server/handlers/remote_agent.go index b348212..bd28163 100644 --- a/server/handlers/remote_agent.go +++ b/server/handlers/remote_agent.go @@ -49,8 +49,8 @@ type RegisterSessionPayload struct { // EventPayload 事件消息的 Payload type EventPayload struct { - AgentID string `json:"agent_id"` - Envelope types.AgentEventEnvelope `json:"envelope"` + AgentID string `json:"agent_id"` + Envelope types.AgentEventEnvelope `json:"envelope"` } // NewRemoteAgentHandler 创建 RemoteAgentHandler @@ -92,7 +92,7 @@ func (h *RemoteAgentHandler) HandleConnect(c *gin.Context) { func (h *RemoteAgentHandler) handleConnection(ctx context.Context, conn *websocket.Conn) { defer func() { h.cleanupConnection(conn) - conn.Close() + _ = conn.Close() }() // 设置读取超时 diff --git a/server/middleware.go b/server/middleware.go index 50b6c3b..201bcb0 100644 --- a/server/middleware.go +++ b/server/middleware.go @@ -3,6 +3,7 @@ package server import ( "fmt" "net/http" + "slices" "strings" "time" @@ -86,14 +87,7 @@ func apiKeyAuthMiddleware(config APIKeyConfig) gin.HandlerFunc { c.Abort() return } - valid := false - for _, key := range config.Keys { - if key == apiKey { - valid = true - break - } - } - if !valid { + if !slices.Contains(config.Keys, apiKey) { c.JSON(http.StatusUnauthorized, gin.H{"success": false, "error": gin.H{"code": "invalid_api_key"}}) c.Abort() return @@ -104,7 +98,7 @@ func apiKeyAuthMiddleware(config APIKeyConfig) gin.HandlerFunc { } // jwtAuthMiddleware validates JWT tokens -func jwtAuthMiddleware(config JWTConfig) gin.HandlerFunc { +func jwtAuthMiddleware(_ JWTConfig) gin.HandlerFunc { return func(c *gin.Context) { authHeader := c.GetHeader("Authorization") if authHeader == "" { diff --git a/server/observability/health.go b/server/observability/health.go index fbc0ebc..a1d0081 100644 --- a/server/observability/health.go +++ b/server/observability/health.go @@ -2,6 +2,7 @@ package observability import ( "context" + "maps" "sync" "time" ) @@ -66,10 +67,8 @@ func (h *HealthChecker) RegisterCheck(check HealthCheck) { // Check 执行所有健康检查 func (h *HealthChecker) Check(ctx context.Context) *HealthInfo { h.mu.RLock() - checks := make(map[string]HealthCheck) - for name, check := range h.checks { - checks[name] = check - } + checks := make(map[string]HealthCheck, len(h.checks)) + maps.Copy(checks, h.checks) h.mu.RUnlock() info := &HealthInfo{ diff --git a/server/routes.go b/server/routes.go index f5c8472..20e423e 100644 --- a/server/routes.go +++ b/server/routes.go @@ -328,7 +328,10 @@ func (s *Server) registerA2ARoutes(rg *gin.RouterGroup) { h.RegisterRoutes(rg) } -// registerDashboardRoutes registers all dashboard-related routes (with auth) +// registerDashboardRoutes registers all dashboard-related routes (with auth). +// Reserved for future use when dashboard authentication is enabled. +// +//nolint:unused func (s *Server) registerDashboardRoutes(rg *gin.RouterGroup) { // Create dashboard handler with agent registry h := handlers.NewDashboardHandlerWithRegistry(s.agentRegistry, s.store)