process

package
v0.7.12 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 22, 2025 License: MIT Imports: 12 Imported by: 0

Documentation

Index

Constants

View Source
const DefaultBufferSize = 256 * 1024

DefaultBufferSize is the default capacity for ring buffers (256KB)

Variables

View Source
var (
	// ErrProcessExists is returned when trying to register a process with an existing ID.
	ErrProcessExists = errors.New("process already exists")
	// ErrProcessNotFound is returned when a process ID is not found.
	ErrProcessNotFound = errors.New("process not found")
	// ErrInvalidState is returned when an operation is invalid for the current state.
	ErrInvalidState = errors.New("invalid process state for operation")
	// ErrShuttingDown is returned when the manager is shutting down.
	ErrShuttingDown = errors.New("process manager is shutting down")
)

Functions

func CleanupJobObject added in v0.6.3

func CleanupJobObject(pid int)

CleanupJobObject is a no-op on Unix. On Windows, this closes the Job Object handle.

func SetupJobObject added in v0.6.3

func SetupJobObject(cmd *exec.Cmd) error

SetupJobObject is a no-op on Unix. On Windows, this creates a Job Object to manage child processes.

Types

type ManagedProcess

type ManagedProcess struct {
	// ID is the unique identifier for this process.
	ID string

	// ProjectPath is the working directory for the process.
	ProjectPath string

	// Command is the executable to run.
	Command string

	// Args are the command arguments.
	Args []string

	// Env holds environment variables for the process (KEY=VALUE format).
	// If nil, the daemon's environment is used.
	Env []string

	// Labels provide metadata for filtering/grouping.
	Labels map[string]string
	// contains filtered or unexported fields
}

ManagedProcess represents a subprocess managed by the MCP server.

func NewManagedProcess

func NewManagedProcess(cfg ProcessConfig) *ManagedProcess

NewManagedProcess creates a new ManagedProcess from config. The process is in StatePending and must be started with ProcessManager.Start().

func (*ManagedProcess) Cancel

func (p *ManagedProcess) Cancel()

Cancel signals the process to stop via context cancellation.

func (*ManagedProcess) CombinedOutput

func (p *ManagedProcess) CombinedOutput() ([]byte, bool)

CombinedOutput returns both stdout and stderr concatenated. Note: Order is stdout then stderr, not chronological interleaving.

func (*ManagedProcess) CompareAndSwapState

func (p *ManagedProcess) CompareAndSwapState(expected, new ProcessState) bool

CompareAndSwapState atomically updates state if it matches expected. Returns true if the swap succeeded.

func (*ManagedProcess) Done

func (p *ManagedProcess) Done() <-chan struct{}

Done returns a channel that is closed when the process completes.

func (*ManagedProcess) EndTime

func (p *ManagedProcess) EndTime() *time.Time

EndTime returns when the process stopped, or nil if still running.

func (*ManagedProcess) ExitCode

func (p *ManagedProcess) ExitCode() int

ExitCode returns the exit code, or -1 if not finished.

func (*ManagedProcess) IsDone

func (p *ManagedProcess) IsDone() bool

IsDone returns true if the process has finished (stopped or failed).

func (*ManagedProcess) IsRunning

func (p *ManagedProcess) IsRunning() bool

IsRunning returns true if the process is currently running.

func (*ManagedProcess) PID

func (p *ManagedProcess) PID() int

PID returns the OS process ID, or -1 if not started.

func (*ManagedProcess) Runtime

func (p *ManagedProcess) Runtime() time.Duration

Runtime returns how long the process has been running (or ran).

func (*ManagedProcess) SetState

func (p *ManagedProcess) SetState(s ProcessState)

SetState sets the process state.

func (*ManagedProcess) StartTime

func (p *ManagedProcess) StartTime() *time.Time

StartTime returns when the process was started, or nil if not started.

func (*ManagedProcess) State

func (p *ManagedProcess) State() ProcessState

State returns the current process state (lock-free atomic read).

func (*ManagedProcess) Stderr

func (p *ManagedProcess) Stderr() ([]byte, bool)

Stderr returns a snapshot of the stderr buffer.

func (*ManagedProcess) Stdout

func (p *ManagedProcess) Stdout() ([]byte, bool)

Stdout returns a snapshot of the stdout buffer.

func (*ManagedProcess) Summary

func (p *ManagedProcess) Summary() string

Summary returns a brief status summary suitable for LLM consumption.

type ManagerConfig

type ManagerConfig struct {
	// DefaultTimeout is the default process timeout (0 = no timeout).
	DefaultTimeout time.Duration
	// MaxOutputBuffer is the per-stream buffer size in bytes.
	MaxOutputBuffer int
	// GracefulTimeout is how long to wait for graceful shutdown before SIGKILL.
	GracefulTimeout time.Duration
	// HealthCheckPeriod is how often to check process health (0 = disabled).
	HealthCheckPeriod time.Duration
	// PIDTracker is an optional tracker for persisting PIDs for orphan cleanup.
	PIDTracker PIDTracker
}

ManagerConfig holds configuration for the ProcessManager.

func DefaultManagerConfig

func DefaultManagerConfig() ManagerConfig

DefaultManagerConfig returns a ManagerConfig with sensible defaults.

type PIDTracker added in v0.7.12

type PIDTracker interface {
	Add(id string, pid int, pgid int, projectPath string) error
	Remove(id string, projectPath string) error
}

PIDTracker is an interface for tracking process PIDs for orphan cleanup. The daemon's PIDTracker implements this to persist PIDs to disk.

type ProcessConfig

type ProcessConfig struct {
	ID          string
	ProjectPath string
	Command     string
	Args        []string
	Env         []string // Environment variables (KEY=VALUE format), nil = inherit daemon's env
	Labels      map[string]string
	BufferSize  int           // Per-stream buffer size (0 = default)
	Timeout     time.Duration // 0 = no timeout
}

ProcessConfig holds configuration for creating a new process.

type ProcessManager

type ProcessManager struct {
	// contains filtered or unexported fields
}

ProcessManager manages all spawned processes with lock-free access.

func NewProcessManager

func NewProcessManager(config ManagerConfig) *ProcessManager

NewProcessManager creates a new ProcessManager with the given configuration.

func (*ProcessManager) ActiveCount

func (pm *ProcessManager) ActiveCount() int64

ActiveCount returns the number of registered processes.

func (*ProcessManager) Config

func (pm *ProcessManager) Config() ManagerConfig

Config returns the manager configuration.

func (*ProcessManager) Get

func (pm *ProcessManager) Get(id string) (*ManagedProcess, error)

Get retrieves a process by ID and project path (lock-free read).

func (*ProcessManager) GetByPID

func (pm *ProcessManager) GetByPID(pid int) *ManagedProcess

GetByPID returns the managed process with the given OS PID, or nil if not found.

func (*ProcessManager) GetByPath

func (pm *ProcessManager) GetByPath(id, projectPath string) (*ManagedProcess, error)

GetByPath retrieves a process by ID and project path (lock-free read).

func (*ProcessManager) IncrementFailed

func (pm *ProcessManager) IncrementFailed()

IncrementFailed increments the failed process counter.

func (*ProcessManager) IsManagedPID

func (pm *ProcessManager) IsManagedPID(pid int) bool

IsManagedPID returns true if the given PID belongs to a running managed process.

func (*ProcessManager) IsShuttingDown

func (pm *ProcessManager) IsShuttingDown() bool

IsShuttingDown returns true if the manager is shutting down.

func (*ProcessManager) KillProcessByPort

func (pm *ProcessManager) KillProcessByPort(ctx context.Context, port int) ([]int, error)

KillProcessByPort finds and kills processes listening on the specified port. This is useful for cleaning up orphaned processes that are blocking port reuse. Returns the PIDs of killed processes and any error.

func (*ProcessManager) List

func (pm *ProcessManager) List() []*ManagedProcess

List returns all managed processes. Note: This is not a consistent snapshot due to sync.Map semantics.

func (*ProcessManager) ListByLabel

func (pm *ProcessManager) ListByLabel(key, value string) []*ManagedProcess

ListByLabel returns processes matching the given label key/value.

func (*ProcessManager) Register

func (pm *ProcessManager) Register(proc *ManagedProcess) error

Register adds a new process to the registry. Returns ErrProcessExists if a process with the same ID+ProjectPath already exists.

func (*ProcessManager) Remove

func (pm *ProcessManager) Remove(id string) bool

Remove deletes a process from the registry by ID (searches all paths). Returns true if the process was found and removed.

func (*ProcessManager) RemoveByPath

func (pm *ProcessManager) RemoveByPath(id, projectPath string) bool

RemoveByPath deletes a process from the registry by ID and path. Returns true if the process was found and removed.

func (*ProcessManager) Restart

func (pm *ProcessManager) Restart(ctx context.Context, id string) (*ManagedProcess, error)

Restart stops a process and starts a new one with the same configuration.

func (*ProcessManager) RunSync

func (pm *ProcessManager) RunSync(ctx context.Context, cfg ProcessConfig) (int, error)

RunSync starts a process and waits for it to complete. Returns the exit code and any error.

func (*ProcessManager) Shutdown

func (pm *ProcessManager) Shutdown(ctx context.Context) error

Shutdown gracefully stops all managed processes. It blocks until all processes are stopped or the context is cancelled. If the context has a very short deadline (<3s), immediately sends SIGKILL to all processes.

func (*ProcessManager) Start

func (pm *ProcessManager) Start(ctx context.Context, proc *ManagedProcess) error

Start begins execution of a process. The process must be in StatePending.

func (*ProcessManager) StartCommand

func (pm *ProcessManager) StartCommand(ctx context.Context, cfg ProcessConfig) (*ManagedProcess, error)

StartCommand is a convenience method to create and start a process.

func (*ProcessManager) StartOrReuse

func (pm *ProcessManager) StartOrReuse(ctx context.Context, cfg ProcessConfig) (*StartOrReuseResult, error)

StartOrReuse implements idempotent process start with auto port conflict resolution: - If a process with the same ID+ProjectPath is running, return it - If a process with the same ID+ProjectPath is stopped/failed, remove it and start new - If no process exists, start a new one - If process fails quickly due to port conflict (EADDRINUSE), kill blocker and retry once

func (*ProcessManager) Stop

func (pm *ProcessManager) Stop(ctx context.Context, id string) error

Stop terminates a process gracefully, falling back to force kill.

func (*ProcessManager) StopAll added in v0.7.6

func (pm *ProcessManager) StopAll(ctx context.Context) error

StopAll stops all running processes and removes them from the registry. Unlike Shutdown, this does NOT set shuttingDown flag, allowing new processes to be started afterward. This is used for cleanup when the last client disconnects.

func (*ProcessManager) StopByProjectPath added in v0.7.8

func (pm *ProcessManager) StopByProjectPath(ctx context.Context, projectPath string) ([]string, error)

StopByProjectPath stops all running processes for a specific project path and removes them. Returns the list of process IDs that were stopped.

func (*ProcessManager) StopProcess

func (pm *ProcessManager) StopProcess(ctx context.Context, proc *ManagedProcess) error

StopProcess terminates the given process.

func (*ProcessManager) TotalFailed

func (pm *ProcessManager) TotalFailed() int64

TotalFailed returns the total number of processes that failed.

func (*ProcessManager) TotalStarted

func (pm *ProcessManager) TotalStarted() int64

TotalStarted returns the total number of processes ever started.

type ProcessState

type ProcessState uint32

ProcessState represents the lifecycle state of a managed process.

const (
	// StatePending is the initial state before the process is started.
	StatePending ProcessState = iota
	// StateStarting indicates the process is being started.
	StateStarting
	// StateRunning indicates the process is actively running.
	StateRunning
	// StateStopping indicates the process is being stopped.
	StateStopping
	// StateStopped indicates the process has stopped normally.
	StateStopped
	// StateFailed indicates the process exited with an error.
	StateFailed
)

func (ProcessState) String

func (s ProcessState) String() string

String returns a human-readable state name.

type RingBuffer

type RingBuffer struct {
	// contains filtered or unexported fields
}

RingBuffer is a thread-safe circular buffer for process output. It bounds memory usage by discarding oldest data when full.

func NewRingBuffer

func NewRingBuffer(capacity int) *RingBuffer

NewRingBuffer creates a new ring buffer with the specified capacity. If capacity <= 0, DefaultBufferSize is used.

func (*RingBuffer) Cap

func (rb *RingBuffer) Cap() int

Cap returns the buffer capacity.

func (*RingBuffer) Len

func (rb *RingBuffer) Len() int

Len returns the current number of bytes stored (up to capacity).

func (*RingBuffer) Reset

func (rb *RingBuffer) Reset()

Reset clears the buffer.

func (*RingBuffer) Snapshot

func (rb *RingBuffer) Snapshot() (data []byte, truncated bool)

Snapshot returns a copy of the current buffer contents. Returns (data, truncated) where truncated indicates data loss. Thread-safe for concurrent reads.

func (*RingBuffer) Truncated

func (rb *RingBuffer) Truncated() bool

Truncated returns whether any data has been lost due to overflow.

func (*RingBuffer) Write

func (rb *RingBuffer) Write(p []byte) (n int, err error)

Write implements io.Writer interface. Thread-safe. Returns len(p), nil (never fails).

type StartOrReuseResult

type StartOrReuseResult struct {
	Process      *ManagedProcess
	Reused       bool   // True if an existing running process was returned
	Cleaned      bool   // True if a stopped/failed process was cleaned up before starting
	PortRetried  bool   // True if a port conflict was detected and resolved
	PortsCleared []int  // Ports that were cleared due to conflicts
	PortError    string // Non-empty if port conflict couldn't be resolved (e.g., managed process blocking)
}

StartOrReuseResult contains the result of StartOrReuse operation.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL