@@ -120,17 +120,61 @@ func upCmd(wm workspace.Manager) *cobra.Command {
120120 return fmt .Errorf ("devbox up: %w" , err )
121121 }
122122
123- // Apply flag overrides
124- if s , _ := cmd .Flags ().GetString ("server" ); s != "" {
125- cfg .Server = s
126- }
127123 if b , _ := cmd .Flags ().GetString ("branch" ); b != "" {
128124 cfg .Branch = b
129125 }
130126
131- if cfg .Server == "" {
132- return fmt .Errorf ("devbox up: server is required — add 'server:' to devbox.yaml or use --server flag" )
127+ // Load server pool (best-effort).
128+ configPath , _ := server .DefaultConfigPath ()
129+ sshExec , err := devboxssh .New ()
130+ if err != nil {
131+ return fmt .Errorf ("devbox up: %w" , err )
132+ }
133+ defer sshExec .Close ()
134+
135+ pool , poolErr := server .NewFilePool (configPath , sshExec )
136+ var poolServers []server.Server
137+ if poolErr == nil {
138+ poolServers , _ = pool .List ()
139+ }
140+ poolConfigured := len (poolServers ) > 0
141+
142+ // 3-tier server resolution: --server flag → config.Server → auto-select from pool.
143+ targetServer := cfg .Server
144+ serverFlag , _ := cmd .Flags ().GetString ("server" )
145+
146+ if serverFlag != "" {
147+ // Tier 1: --server flag — look up from pool by name.
148+ found := false
149+ for _ , srv := range poolServers {
150+ if srv .Name == serverFlag {
151+ targetServer = server .SSHHost (& srv )
152+ found = true
153+ break
154+ }
155+ }
156+ if ! found {
157+ // Not in pool — use as raw hostname (backward compat).
158+ targetServer = serverFlag
159+ }
160+ } else if targetServer == "" && poolConfigured {
161+ // Tier 3: Auto-select from pool.
162+ selector := server .NewLeastLoaded (sshExec )
163+ selected , err := selector .Select (cmd .Context (), poolServers )
164+ if err != nil {
165+ return fmt .Errorf ("devbox up: %w" , err )
166+ }
167+ targetServer = server .SSHHost (selected )
168+ fmt .Fprintf (os .Stderr , "Using server: %s (%s)\n " , selected .Name , selected .Host )
133169 }
170+ // Tier 2: config.Server is already set in targetServer.
171+
172+ if err := cfg .ValidateForUp (poolConfigured ); err != nil {
173+ if targetServer == "" {
174+ return fmt .Errorf ("devbox up: %w" , err )
175+ }
176+ }
177+ cfg .Server = targetServer
134178
135179 // Merge resource limits: server defaults <- workspace overrides.
136180 globalCfg , err := config .LoadGlobal ()
@@ -173,13 +217,6 @@ func upCmd(wm workspace.Manager) *cobra.Command {
173217 }
174218
175219 // Expose ports via Tailscale on the remote server
176- sshExec , err := devboxssh .New ()
177- if err != nil {
178- ui .StopSpinner (spin , false )
179- return fmt .Errorf ("devbox up: %w" , err )
180- }
181- defer sshExec .Close ()
182-
183220 tm := tailscale .NewManager (remoteRunner (sshExec , cfg .Server ))
184221 for name , port := range cfg .Ports {
185222 if err := tm .Serve (port , ws .Name ); err != nil {
@@ -199,7 +236,7 @@ func upCmd(wm workspace.Manager) *cobra.Command {
199236 },
200237 }
201238 cmd .Flags ().String ("branch" , "" , "Git branch to checkout" )
202- cmd .Flags ().String ("server" , "" , "Target server (overrides devbox.yaml )" )
239+ cmd .Flags ().String ("server" , "" , "Target server name from pool (or hostname )" )
203240 return cmd
204241}
205242
@@ -233,17 +270,42 @@ func stopCmd(wm workspace.Manager) *cobra.Command {
233270}
234271
235272func listCmd (wm workspace.Manager ) * cobra.Command {
236- return & cobra.Command {
273+ cmd := & cobra.Command {
237274 Use : "list" ,
238275 Aliases : []string {"ls" },
239276 Short : "List all workspaces" ,
240- Long : "List all workspaces across all configured servers.\n Shows status, resource limits, and server for each workspace." ,
277+ Long : "List all workspaces across all configured servers.\n Shows status, resource limits, and server for each workspace.\n Use --server to filter to a specific server. " ,
241278 RunE : func (cmd * cobra.Command , args []string ) error {
242279 workspaces , err := wm .List ()
243280 if err != nil {
244281 return fmt .Errorf ("devbox list: %w" , err )
245282 }
246283
284+ // Filter by --server if provided.
285+ serverFilter , _ := cmd .Flags ().GetString ("server" )
286+ if serverFilter != "" {
287+ // Resolve server name from pool if possible.
288+ resolvedHost := serverFilter
289+ configPath , _ := server .DefaultConfigPath ()
290+ if pool , err := server .NewFilePool (configPath , nil ); err == nil {
291+ if servers , err := pool .List (); err == nil {
292+ for _ , srv := range servers {
293+ if srv .Name == serverFilter {
294+ resolvedHost = server .SSHHost (& srv )
295+ break
296+ }
297+ }
298+ }
299+ }
300+ filtered := make ([]workspace.Workspace , 0 )
301+ for _ , ws := range workspaces {
302+ if ws .ServerHost == resolvedHost || ws .ServerHost == serverFilter {
303+ filtered = append (filtered , ws )
304+ }
305+ }
306+ workspaces = filtered
307+ }
308+
247309 if len (workspaces ) == 0 {
248310 fmt .Println ("No workspaces found" )
249311 return nil
@@ -331,6 +393,8 @@ func listCmd(wm workspace.Manager) *cobra.Command {
331393 return nil
332394 },
333395 }
396+ cmd .Flags ().String ("server" , "" , "Filter workspaces by server name or hostname" )
397+ return cmd
334398}
335399
336400func destroyCmd (wm workspace.Manager ) * cobra.Command {
0 commit comments