fix: create a new unique client ID for each instance

Ayman Bagabas created

Change summary

internal/client/client.go | 11 +++++------
internal/cmd/root.go      |  7 +++++--
internal/server/proto.go  | 35 +++++++++++++++--------------------
3 files changed, 25 insertions(+), 28 deletions(-)

Detailed changes

internal/client/client.go 🔗

@@ -2,8 +2,6 @@ package client
 
 import (
 	"context"
-	"crypto/sha256"
-	"encoding/hex"
 	"encoding/json"
 	"net"
 	"net/http"
@@ -45,12 +43,8 @@ func NewClient(path, network, address string) (*Client, error) {
 		Transport: tr,
 		Timeout:   0, // we need this to be 0 for long-lived connections and SSE streams
 	}
-	hasher := sha256.New()
-	hasher.Write([]byte(path))
-	id := hex.EncodeToString(hasher.Sum(nil))
 	return &Client{
 		h:    h,
-		id:   id,
 		path: filepath.Clean(path),
 	}, nil
 }
@@ -60,6 +54,11 @@ func (c *Client) ID() string {
 	return c.id
 }
 
+// SetID sets the client's instance unique identifier.
+func (c *Client) SetID(id string) {
+	c.id = id
+}
+
 // Path returns the client's instance filesystem path.
 func (c *Client) Path() string {
 	return c.path

internal/cmd/root.go 🔗

@@ -151,15 +151,18 @@ func setupApp(cmd *cobra.Command) (*client.Client, error) {
 		return nil, err
 	}
 
-	if _, err := c.CreateInstance(ctx, proto.Instance{
+	ins, err := c.CreateInstance(ctx, proto.Instance{
 		Path:    cwd,
 		DataDir: dataDir,
 		Debug:   debug,
 		YOLO:    yolo,
-	}); err != nil {
+	})
+	if err != nil {
 		return nil, fmt.Errorf("failed to create or connect to instance: %v", err)
 	}
 
+	c.SetID(ins.ID)
+
 	return c, nil
 }
 

internal/server/proto.go 🔗

@@ -1,8 +1,6 @@
 package server
 
 import (
-	"crypto/sha256"
-	"encoding/hex"
 	"encoding/json"
 	"fmt"
 	"log/slog"
@@ -16,6 +14,7 @@ import (
 	"github.com/charmbracelet/crush/internal/lsp"
 	"github.com/charmbracelet/crush/internal/proto"
 	"github.com/charmbracelet/crush/internal/session"
+	"github.com/google/uuid"
 )
 
 type controllerV1 struct {
@@ -30,9 +29,11 @@ func (c *controllerV1) handleGetInstances(w http.ResponseWriter, r *http.Request
 	instances := []proto.Instance{}
 	for _, ins := range c.instances.Seq2() {
 		instances = append(instances, proto.Instance{
-			ID:   ins.ID(),
-			Path: ins.Path(),
-			YOLO: ins.cfg.Permissions != nil && ins.cfg.Permissions.SkipRequests,
+			ID:      ins.ID(),
+			Path:    ins.Path(),
+			YOLO:    ins.cfg.Permissions != nil && ins.cfg.Permissions.SkipRequests,
+			DataDir: ins.cfg.Options.DataDirectory,
+			Debug:   ins.cfg.Options.Debug,
 		})
 	}
 	jsonEncode(w, instances)
@@ -502,21 +503,13 @@ func (c *controllerV1) handlePostInstances(w http.ResponseWriter, r *http.Reques
 		return
 	}
 
-	hasher := sha256.New()
-	hasher.Write([]byte(filepath.Clean(args.Path)))
-	id := hex.EncodeToString(hasher.Sum(nil))
-	if existing, ok := c.instances.Get(id); ok {
-		jsonEncode(w, proto.Instance{
-			ID:   existing.ID(),
-			Path: existing.Path(),
-			// TODO: Investigate if this makes sense.
-			YOLO:    existing.cfg.Permissions != nil && existing.cfg.Permissions.SkipRequests,
-			Debug:   existing.cfg.Options.Debug,
-			DataDir: existing.cfg.Options.DataDirectory,
-		})
+	if args.Path == "" {
+		c.logError(r, "path is required")
+		jsonError(w, http.StatusBadRequest, "path is required")
 		return
 	}
 
+	id := uuid.New().String()
 	cfg, err := config.Init(args.Path, args.DataDir, args.Debug)
 	if err != nil {
 		c.logError(r, "failed to initialize config", "error", err)
@@ -560,9 +553,11 @@ func (c *controllerV1) handlePostInstances(w http.ResponseWriter, r *http.Reques
 
 	c.instances.Set(id, ins)
 	jsonEncode(w, proto.Instance{
-		ID:   id,
-		Path: args.Path,
-		YOLO: cfg.Permissions.SkipRequests,
+		ID:      id,
+		Path:    args.Path,
+		DataDir: cfg.Options.DataDirectory,
+		Debug:   cfg.Options.Debug,
+		YOLO:    cfg.Permissions.SkipRequests,
 	})
 }