diff --git a/cmd/agentctl/main.go b/cmd/agentctl/main.go index cd3da10..1385f71 100644 --- a/cmd/agentctl/main.go +++ b/cmd/agentctl/main.go @@ -14,6 +14,7 @@ import ( "fmt" "os" "strings" + "syscall" "github.com/spf13/cobra" @@ -47,6 +48,7 @@ func main() { listCmd(mgr), startCmd(mgr, &binPath), stopCmd(mgr), + reloadCmd(mgr), removeCmd(mgr), avatarCmd(), displaynameCmd(), @@ -171,6 +173,48 @@ func stopCmd(mgr *process.Manager) *cobra.Command { } } +// ── reload ──────────────────────────────────────────────────────────────── + +func reloadCmd(mgr *process.Manager) *cobra.Command { + return &cobra.Command{ + Use: "reload [agent-id]", + Short: "Hot-reload an agent (or all agents) without stopping the launcher", + Long: `Sends SIGHUP to the running launcher, which triggers a hot-reload. +If an agent-id is given, only that agent is reloaded. +If no agent-id is given, all agents are reloaded. + +The launcher must be running. Use 'agentctl start' first if needed.`, + Args: cobra.MaximumNArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + pid := mgr.UnifiedPID() + if pid <= 0 { + return fmt.Errorf("launcher is not running") + } + + target := "" + if len(args) == 1 { + target = args[0] + } + + if target != "" { + if err := os.WriteFile("run/reload.txt", []byte(target), 0o644); err != nil { + return fmt.Errorf("write reload target: %w", err) + } + fmt.Printf("reload %-20s sending SIGHUP to PID %d\n", target, pid) + } else { + // Remove any stale reload.txt so SIGHUP reloads all agents. + _ = os.Remove("run/reload.txt") + fmt.Printf("reload %-20s sending SIGHUP to PID %d\n", "(all)", pid) + } + + if err := syscall.Kill(pid, syscall.SIGHUP); err != nil { + return fmt.Errorf("kill -HUP %d: %w", pid, err) + } + return nil + }, + } +} + // ── remove ──────────────────────────────────────────────────────────────── func removeCmd(mgr *process.Manager) *cobra.Command {