@@ -87,18 +87,51 @@ crush --yolo
87crush --data-dir /path/to/custom/.crush
87crush --data-dir /path/to/custom/.crush
89 RunE: func(cmd *cobra.Command, args []string) error {
89 RunE: func(cmd *cobra.Command, args []string) error {
90− app, err := setupAppWithProgressBar(cmd)
90+ hostURL, err := server.ParseHostURL(clientHost)
91 if err != nil {
91 if err != nil {
92+ return fmt.Errorf("invalid host URL: %v", err)
95+ if err := ensureServer(cmd, hostURL); err != nil {
92 return err
96 return err
99+ appInstance, err := setupAppWithProgressBar(cmd)
94− defer app.Shutdown()
103+ defer appInstance.Shutdown()
105+ // Register the workspace with the server so it tracks active
106+ // clients and auto-shuts down when the last one exits.
107+ cwd, _ := ResolveCwd(cmd)
108+ dataDir, _ := cmd.Flags().GetString("data-dir")
109+ debug, _ := cmd.Flags().GetBool("debug")
110+ yolo, _ := cmd.Flags().GetBool("yolo")
112+ c, err := client.NewClient(cwd, hostURL.Scheme, hostURL.Host)
114+ return fmt.Errorf("failed to create client: %v", err)
117+ ws, err := c.CreateWorkspace(cmd.Context(), proto.Workspace{
122+ Version: version.Version,
125+ return fmt.Errorf("failed to register workspace: %v", err)
127+ defer func() { _ = c.DeleteWorkspace(cmd.Context(), ws.ID) }()
96 event.AppInitialized()
129 event.AppInitialized()
98 // Set up the TUI.
131 // Set up the TUI.
99 var env uv.Environ = os.Environ()
132 var env uv.Environ = os.Environ()
101− com := common.DefaultCommon(app)
134+ com := common.DefaultCommon(appInstance)
102 model := ui.New(com)
135 model := ui.New(com)
104 program := tea.NewProgram(
137 program := tea.NewProgram(
@@ -107,7 +140,7 @@ crush --data-dir /path/to/custom/.crush
107 tea.WithContext(cmd.Context()),
140 tea.WithContext(cmd.Context()),
108 tea.WithFilter(ui.MouseEventFilter), // Filter mouse events based on focus state
141 tea.WithFilter(ui.MouseEventFilter), // Filter mouse events based on focus state
110− go app.Subscribe(program)
143+ go appInstance.Subscribe(program)
112 if _, err := program.Run(); err != nil {
145 if _, err := program.Run(); err != nil {
113 event.Error(err)
146 event.Error(err)
@@ -283,7 +316,8 @@ func setupClientApp(cmd *cobra.Command, hostURL *url.URL) (*client.Client, *prot
285// ensureServer auto-starts a detached server if the socket file does not
318// ensureServer auto-starts a detached server if the socket file does not
286−// exist.
319+// exist. When connecting to an existing server, it waits for the health
320+// endpoint to respond.
287func ensureServer(cmd *cobra.Command, hostURL *url.URL) error {
321func ensureServer(cmd *cobra.Command, hostURL *url.URL) error {
288 switch hostURL.Scheme {
322 switch hostURL.Scheme {
289 case "unix", "npipe":
323 case "unix", "npipe":
@@ -308,9 +342,8 @@ func ensureServer(cmd *cobra.Command, hostURL *url.URL) error {
308 if err != nil {
342 if err != nil {
309 return fmt.Errorf("failed to initialize crush server: %v", err)
343 return fmt.Errorf("failed to initialize crush server: %v", err)
312− // TCP: assume server is already running.
314 return nil
347 return nil