package toolsrv import ( "context" "fmt" ) // Addr describes how to reach a tool server. type Addr struct { // Network is the transport: "local", "ssh", or (future) "unix". Network string // Host is the SSH target (user@host or user@host:port). Empty for local. Host string // CWD is the working directory on the target. CWD string } // LocalAddr returns an Addr for an in-process tool server. func LocalAddr(cwd string) Addr { return Addr{Network: "local", CWD: cwd} } // SSHAddr returns an Addr for a remote tool server over SSH. func SSHAddr(host, cwd string) Addr { return Addr{Network: "ssh", Host: host, CWD: cwd} } // Dial connects to a tool server at the given address. // Options (WithStrict, WithBuiltins, etc.) are applied to local servers. // // TODO: Unify local and remote behind a single protocol. Local execution // should eventually go through a Unix socket (same JSON-RPC as remote), // making the agent always a client. The elevation broker already proves // this pattern. Benefits: process isolation, shared tool server across // sessions, independent lifecycle. Transport becomes the only difference: // - Network "local" → unix socket to co-located toolsrv process // - Network "ssh" → SSH tunnel to remote toolsrv process // - Network "unix" → pre-existing unix socket func Dial(ctx context.Context, addr Addr, opts ...Option) (Runner, error) { switch addr.Network { case "", "local": srv := New(addr.CWD) for _, o := range opts { o(srv) } return srv, nil case "ssh": return RemoteDial(ctx, RemoteConfig{ SSHTarget: addr.Host, CWD: addr.CWD, }) default: return nil, fmt.Errorf("toolsrv.Dial: unsupported network %q", addr.Network) } }