Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/aster-server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 == "" {
Expand Down
32 changes: 16 additions & 16 deletions pkg/config/loader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down Expand Up @@ -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_"))

Expand All @@ -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" {
Expand All @@ -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))

Expand All @@ -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()

Expand Down Expand Up @@ -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")
Expand Down
4 changes: 2 additions & 2 deletions pkg/executionplan/executor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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 {
Expand Down
13 changes: 11 additions & 2 deletions pkg/sandbox/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 != "" {
Expand Down
53 changes: 18 additions & 35 deletions server/handlers/dashboard_events.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/json"
"fmt"
"net/http"
"slices"
"strings"
"sync"
"time"
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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{
Expand Down Expand Up @@ -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{
Expand Down
6 changes: 3 additions & 3 deletions server/handlers/remote_agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()
}()

// 设置读取超时
Expand Down
12 changes: 3 additions & 9 deletions server/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package server
import (
"fmt"
"net/http"
"slices"
"strings"
"time"

Expand Down Expand Up @@ -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
Expand All @@ -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 == "" {
Expand Down
7 changes: 3 additions & 4 deletions server/observability/health.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package observability

import (
"context"
"maps"
"sync"
"time"
)
Expand Down Expand Up @@ -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{
Expand Down
5 changes: 4 additions & 1 deletion server/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down