package main import ( "context" "os" "path/filepath" "MultiClawController/internal/controlplane" ) // App is the deliberately narrow bridge between the Wails frontend and the control plane. type App struct { ctx context.Context control *controlplane.ControlPlane startErr error } func NewApp() *App { return &App{} } func (a *App) startup(ctx context.Context) { a.ctx = ctx a.control, a.startErr = controlplane.Open(controllerConfigPath()) if a.startErr == nil { a.startErr = a.control.StartAgentServer() } } func (a *App) shutdown(context.Context) { if a.control != nil { a.control.Close() } } // Snapshot returns dashboard data. It is safe to expose to the local WebView. func (a *App) Snapshot() (controlplane.Snapshot, error) { if a.startErr != nil { return controlplane.Snapshot{}, a.startErr } return a.control.Snapshot() } // CreatePairingCode returns a short-lived secret. The frontend never persists it. func (a *App) CreatePairingCode() (string, error) { if a.startErr != nil { return "", a.startErr } return a.control.CreatePairingCode() } func (a *App) OnlineAgents() ([]controlplane.Agent, error) { if a.startErr != nil { return nil, a.startErr } return a.control.OnlineAgents() } func (a *App) Agents() ([]controlplane.Agent, error) { if a.startErr != nil { return nil, a.startErr } return a.control.Agents() } // AgentDirectory is the paged, server-filtered Agent list for the management page. func (a *App) AgentDirectory(filter controlplane.AgentDirectoryFilter) (controlplane.AgentDirectoryPage, error) { if a.startErr != nil { return controlplane.AgentDirectoryPage{}, a.startErr } return a.control.AgentDirectory(filter) } // AgentOperationalDetails returns bounded recent operational context for one Agent. func (a *App) AgentOperationalDetails(agentID string) (controlplane.AgentOperationalDetail, error) { if a.startErr != nil { return controlplane.AgentOperationalDetail{}, a.startErr } return a.control.AgentOperationalDetails(agentID) } func (a *App) RenameAgent(agentID, name string) error { if a.startErr != nil { return a.startErr } return a.control.RenameAgent(agentID, name) } func (a *App) RevokeAgent(agentID string) error { if a.startErr != nil { return a.startErr } return a.control.RevokeAgent(agentID) } func (a *App) CreateTask(agentID, instruction string, timeoutSeconds int) (string, error) { if a.startErr != nil { return "", a.startErr } return a.control.CreateTask(agentID, instruction, timeoutSeconds) } func (a *App) CreateTaskWithImages(agentID, instruction string, timeoutSeconds int, images []controlplane.TaskImageInput) (string, error) { if a.startErr != nil { return "", a.startErr } return a.control.CreateTaskWithImages(agentID, instruction, timeoutSeconds, images) } // CreateTasks creates independently-addressed tasks from the multi-Agent task board. func (a *App) CreateTasks(requests []controlplane.TaskRequest) ([]controlplane.TaskDispatchResult, error) { if a.startErr != nil { return nil, a.startErr } return a.control.CreateTasks(requests) } // DispatchMentions expands newline-separated public @AgentName assignments into // independently addressed tasks. func (a *App) DispatchMentions(message string, timeoutSeconds int) (controlplane.MentionDispatchResponse, error) { if a.startErr != nil { return controlplane.MentionDispatchResponse{}, a.startErr } return a.control.DispatchMentions(message, timeoutSeconds) } func (a *App) DispatchMentionsWithImages(message string, timeoutSeconds int, images []controlplane.TaskImageInput) (controlplane.MentionDispatchResponse, error) { if a.startErr != nil { return controlplane.MentionDispatchResponse{}, a.startErr } return a.control.DispatchMentionsWithImages(message, timeoutSeconds, images) } func (a *App) CancelTask(taskID string) error { if a.startErr != nil { return a.startErr } return a.control.CancelTask(taskID) } func (a *App) RetryTask(taskID string) (string, error) { if a.startErr != nil { return "", a.startErr } return a.control.RetryTask(taskID) } func (a *App) RecentTasks() ([]controlplane.Task, error) { if a.startErr != nil { return nil, a.startErr } return a.control.RecentTasks() } func (a *App) ClearTaskHistory() (int, error) { if a.startErr != nil { return 0, a.startErr } return a.control.ClearTaskHistory() } func (a *App) TaskDetails(taskID string) (controlplane.TaskDetail, error) { if a.startErr != nil { return controlplane.TaskDetail{}, a.startErr } return a.control.TaskDetails(taskID) } func (a *App) LLMProfiles() ([]controlplane.LLMProfile, error) { if a.startErr != nil { return nil, a.startErr } return a.control.LLMProfiles() } func (a *App) SaveLLMProfile(input controlplane.LLMProfileInput) (controlplane.LLMProfile, error) { if a.startErr != nil { return controlplane.LLMProfile{}, a.startErr } return a.control.SaveLLMProfile(input) } func (a *App) DeployLLMProfile(profileID string, agentIDs []string) error { if a.startErr != nil { return a.startErr } return a.control.DeployLLMProfile(profileID, agentIDs) } func (a *App) AgentLLMStatuses() ([]controlplane.AgentLLMStatus, error) { if a.startErr != nil { return nil, a.startErr } return a.control.AgentLLMStatuses() } func (a *App) Skills() ([]controlplane.Skill, error) { if a.startErr != nil { return nil, a.startErr } return a.control.Skills() } func (a *App) SkillDocument(skillID string) (controlplane.SkillDocument, error) { if a.startErr != nil { return controlplane.SkillDocument{}, a.startErr } return a.control.SkillDocument(skillID) } func (a *App) SaveSkill(input controlplane.SkillInput) (controlplane.Skill, error) { if a.startErr != nil { return controlplane.Skill{}, a.startErr } return a.control.SaveSkill(input) } func (a *App) DeploySkill(skillID string, agentIDs []string) error { if a.startErr != nil { return a.startErr } return a.control.DeploySkill(skillID, agentIDs) } func (a *App) SetAgentSkills(agentID string, skillIDs []string) error { if a.startErr != nil { return a.startErr } return a.control.SetAgentSkills(agentID, skillIDs) } func (a *App) AgentSkillStatuses() ([]controlplane.AgentSkillStatus, error) { if a.startErr != nil { return nil, a.startErr } return a.control.AgentSkillStatuses() } func (a *App) EnableDesktopFilesystem(agentIDs []string) error { if a.startErr != nil { return a.startErr } return a.control.EnableDesktopFilesystem(agentIDs) } func (a *App) AgentDeploymentStatuses() ([]controlplane.AgentDeploymentStatus, error) { if a.startErr != nil { return nil, a.startErr } return a.control.AgentDeploymentStatuses() } func (a *App) RecentAuditEvents() ([]controlplane.AuditEvent, error) { if a.startErr != nil { return nil, a.startErr } return a.control.RecentAuditEvents() } func (a *App) AuditTrail(filter controlplane.AuditFilter) (controlplane.AuditPage, error) { if a.startErr != nil { return controlplane.AuditPage{}, a.startErr } return a.control.AuditTrail(filter) } func controllerConfigPath() string { if configured := os.Getenv("MULTICLAW_CONTROLLER_CONFIG_PATH"); configured != "" { return configured } if executable, err := os.Executable(); err == nil { candidate := filepath.Join(filepath.Dir(executable), "config", "controller-config.json") if _, err := os.Stat(candidate); err == nil { return candidate } } workingDirectory, err := os.Getwd() if err != nil { return "config/controller-config.json" } return filepath.Join(workingDirectory, "config", "controller-config.json") }