diff --git a/CLAUDE.md b/CLAUDE.md index 28c6898..31d40a8 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -32,7 +32,7 @@ shelli provides persistent interactive shell sessions via PTY-backed processes m - `storage_memory.go`: In-memory storage with circular buffer (default, 10MB limit) - `storage_file.go`: File-based persistent storage - `constants.go`: Shared constants (buffer sizes, timeouts) -- Socket at `~/.shelli/shelli.sock`, auto-started on first command +- Socket at `/tmp/shelli-{uid}/shelli.sock`, auto-started on first command **MCP Server** (`internal/mcp/`) - `server.go`: JSON-RPC stdio server implementing MCP protocol diff --git a/README.md b/README.md index 203240c..befb9b4 100644 --- a/README.md +++ b/README.md @@ -358,10 +358,10 @@ Sessions have explicit states with clear transitions: ## Storage -By default, shelli stores session output in files at `~/.shelli/data/`: +By default, shelli stores session output in files at `/tmp/shelli-{uid}/data/`: ``` -~/.shelli/data/ +/tmp/shelli-{uid}/data/ ├── mysession.out # raw PTY output (0600 permissions) └── mysession.meta # JSON metadata (state, pid, timestamps) ``` @@ -379,7 +379,7 @@ shelli daemon [flags] | Flag | Default | Description | |------|---------|-------------| -| `--data-dir` | `~/.shelli/data` | Directory for session files | +| `--data-dir` | `/tmp/shelli-{uid}/data` | Directory for session files | | `--memory-backend` | `false` | Use in-memory storage (no persistence) | | `--stopped-ttl` | (disabled) | Auto-delete stopped sessions after duration | | `--max-output` | `10MB` | Buffer size limit (memory backend only) | @@ -387,7 +387,7 @@ shelli daemon [flags] Examples: ```bash # Use custom storage location -shelli daemon --data-dir ~/.shelli/sessions +shelli daemon --data-dir /tmp/shelli-sessions # Memory-only mode (v0.3 behavior) shelli daemon --memory-backend --max-output 50MB @@ -454,7 +454,7 @@ shelli uses a daemon process to maintain PTY handles across CLI invocations: │ │ │ ┌────────────────────┐ ┌────────────────────────────────┐ │ │ │ MCP Server │ │ Socket Server │ │ -│ │ (--mcp flag) │ │ (~/.shelli/shelli.sock) │ │ +│ │ (--mcp flag) │ │ (/tmp/shelli-{uid}/shelli.sock)│ │ │ └─────────┬──────────┘ └─────────────┬──────────────────┘ │ │ └───────────┬───────────────────┘ │ │ ▼ │ diff --git a/cmd/daemon.go b/cmd/daemon.go index 2d7437a..e9c16ff 100644 --- a/cmd/daemon.go +++ b/cmd/daemon.go @@ -39,7 +39,7 @@ func init() { daemonCmd.Flags().BoolVar(&daemonMCPFlag, "mcp", false, "Run as MCP server (JSON-RPC over stdio)") daemonCmd.Flags().StringVar(&daemonDataDirFlag, "data-dir", "", - "Directory for session output files (default: ~/.shelli/data)") + "Directory for session output files (default: /tmp/shelli-{uid}/data)") daemonCmd.Flags().BoolVar(&daemonMemoryBackend, "memory-backend", false, "Use in-memory storage instead of file-based (no persistence)") daemonCmd.Flags().StringVar(&daemonStoppedTTLFlag, "stopped-ttl", "", @@ -68,11 +68,11 @@ func runDaemon(cmd *cobra.Command, args []string) error { var opts []daemon.ServerOption if daemonDataDirFlag == "" { - homeDir, err := os.UserHomeDir() + runtimeDir, err := daemon.RuntimeDir() if err != nil { - return fmt.Errorf("get home dir: %w", err) + return fmt.Errorf("get runtime dir: %w", err) } - daemonDataDirFlag = filepath.Join(homeDir, ".shelli", "data") + daemonDataDirFlag = filepath.Join(runtimeDir, "data") } if daemonMemoryBackend { diff --git a/internal/daemon/server.go b/internal/daemon/server.go index 2e04668..6abdf51 100644 --- a/internal/daemon/server.go +++ b/internal/daemon/server.go @@ -99,20 +99,22 @@ func WithMaxOutputSize(size int) ServerOption { } } +func RuntimeDir() (string, error) { + return filepath.Join(os.TempDir(), fmt.Sprintf("shelli-%d", os.Getuid())), nil +} + func NewServer(opts ...ServerOption) (*Server, error) { - homeDir, err := os.UserHomeDir() + runtimeDir, err := RuntimeDir() if err != nil { return nil, err } - - socketDir := filepath.Join(homeDir, ".shelli") - if err := os.MkdirAll(socketDir, 0700); err != nil { + if err := os.MkdirAll(runtimeDir, 0700); err != nil { return nil, err } s := &Server{ handles: make(map[string]*sessionHandle), - socketDir: socketDir, + socketDir: runtimeDir, storage: NewMemoryStorage(DefaultMaxOutputSize), cleanupStopChan: make(chan struct{}), } @@ -161,11 +163,11 @@ func (s *Server) recoverSessions() error { } func SocketPath() (string, error) { - homeDir, err := os.UserHomeDir() + runtimeDir, err := RuntimeDir() if err != nil { - return "", fmt.Errorf("get home dir: %w", err) + return "", err } - return filepath.Join(homeDir, ".shelli", "shelli.sock"), nil + return filepath.Join(runtimeDir, "shelli.sock"), nil } func (s *Server) socketPath() string {