package coredeno import ( "context" "fmt" "os" "os/exec" "path/filepath" ) // Start launches the Deno sidecar process with the given entrypoint args. func (s *Sidecar) Start(ctx context.Context, args ...string) error { s.mu.Lock() defer s.mu.Unlock() if s.cmd != nil { return fmt.Errorf("coredeno: already running") } // Ensure socket directory exists sockDir := filepath.Dir(s.opts.SocketPath) if err := os.MkdirAll(sockDir, 0755); err != nil { return fmt.Errorf("coredeno: mkdir %s: %w", sockDir, err) } // Remove stale socket os.Remove(s.opts.SocketPath) s.ctx, s.cancel = context.WithCancel(ctx) s.cmd = exec.CommandContext(s.ctx, s.opts.DenoPath, args...) s.done = make(chan struct{}) if err := s.cmd.Start(); err != nil { s.cmd = nil s.cancel() return fmt.Errorf("coredeno: start: %w", err) } // Monitor in background — waits for exit, then signals done go func() { s.cmd.Wait() s.mu.Lock() s.cmd = nil s.mu.Unlock() close(s.done) }() return nil } // Stop cancels the context and waits for the process to exit. func (s *Sidecar) Stop() error { s.mu.RLock() if s.cmd == nil { s.mu.RUnlock() return nil } done := s.done s.mu.RUnlock() s.cancel() <-done return nil } // IsRunning returns true if the sidecar process is alive. func (s *Sidecar) IsRunning() bool { s.mu.RLock() defer s.mu.RUnlock() return s.cmd != nil }