feat(02-03): client socket, autostart daemon, switch handler, workspace wiring
- Client struct sends JSON requests to daemon over Unix socket - EnsureDaemon auto-starts the daemon if not running (retry 50ms x 20) - Switch handler uses FuzzyMatch + SwitchToWorkspace via i3 IPC - InitWorkspaceResolver wires BuildTerminalWorkspaceMap + ResolveWorkspace - sysattr_linux.go for Setsid detach on daemon spawn
This commit is contained in:
42
daemon.go
42
daemon.go
@@ -151,6 +151,7 @@ type Daemon struct {
|
||||
procDir string
|
||||
claudeDir string
|
||||
workspaceResolver func(claudePID int) string // nil = no workspace resolution
|
||||
i3commander I3Commander
|
||||
pollInterval time.Duration
|
||||
stopCh chan struct{}
|
||||
listener net.Listener
|
||||
@@ -169,6 +170,21 @@ func NewDaemon(sockPath, procDir, claudeDir string, labels *LabelStore) *Daemon
|
||||
}
|
||||
}
|
||||
|
||||
// InitWorkspaceResolver sets up i3+X11 workspace resolution.
|
||||
// If i3 or X11 is unavailable, logs a warning and continues without workspace info.
|
||||
func (d *Daemon) InitWorkspaceResolver(treeProvider I3TreeProvider, x11 X11PIDResolver) {
|
||||
if treeProvider == nil || x11 == nil {
|
||||
return
|
||||
}
|
||||
d.workspaceResolver = func(claudePID int) string {
|
||||
termMap, err := BuildTerminalWorkspaceMap(treeProvider, x11)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return ResolveWorkspace(d.procDir, claudePID, termMap)
|
||||
}
|
||||
}
|
||||
|
||||
// Start runs the daemon: initial scan, then listens on the Unix socket
|
||||
// and polls for sessions in the background.
|
||||
func (d *Daemon) Start() error {
|
||||
@@ -329,6 +345,32 @@ func (d *Daemon) handleConnection(conn net.Conn) {
|
||||
d.registry.mu.Unlock()
|
||||
writeResponse(conn, Response{OK: true})
|
||||
|
||||
case "switch":
|
||||
var args SwitchArgs
|
||||
if err := json.Unmarshal(req.Args, &args); err != nil {
|
||||
writeResponse(conn, Response{Error: "invalid switch args: " + err.Error()})
|
||||
return
|
||||
}
|
||||
sessions := d.registry.List()
|
||||
match := FuzzyMatch(args.Query, sessions)
|
||||
if match == nil {
|
||||
writeResponse(conn, Response{Error: "no session matching: " + args.Query})
|
||||
return
|
||||
}
|
||||
if match.Workspace == "" {
|
||||
writeResponse(conn, Response{Error: "no workspace for session " + match.SessionID})
|
||||
return
|
||||
}
|
||||
if d.i3commander == nil {
|
||||
writeResponse(conn, Response{Error: "i3 commander not available"})
|
||||
return
|
||||
}
|
||||
if err := SwitchToWorkspace(d.i3commander, match.Workspace); err != nil {
|
||||
writeResponse(conn, Response{Error: "switch workspace: " + err.Error()})
|
||||
return
|
||||
}
|
||||
writeResponse(conn, Response{OK: true})
|
||||
|
||||
case "stop":
|
||||
writeResponse(conn, Response{OK: true})
|
||||
d.Stop()
|
||||
|
||||
Reference in New Issue
Block a user