procs

package
v0.6.2 Latest Latest
Warning

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

Go to latest
Published: Oct 8, 2026 License: MIT Imports: 12 Imported by: 0

Documentation

Overview

Package procs runs commands that outlive the turn's iterations.

A turn-scoped command — the ordinary Shell tool — dies with its iteration. A process started here is detached into its own session, writes to a log file in the session directory, and records its exit status where a later iteration (or, if persistent, a later turn) can read it. Ephemeral processes die with the turn; persistent ones survive it, and are the only thing that does.

Everything the registry knows lives in <session>/proc/, so a new process opening the same directory sees the same set:

<id>.json   what it is: command, working directory, pid, when it started
<id>.log    everything it has written
<id>.exit   the exit status, written by the wrapper when the command ends

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ValidateRemindEvery

func ValidateRemindEvery(remindEvery int) error

ValidateRemindEvery says whether a tick interval is usable. Zero means off; negatives are rejected.

func ValidateReport

func ValidateReport(report string) error

ValidateReport says whether a report policy is known. Empty means the default ("exit") and is valid for callers that fill it in later.

func ValidateUntil

func ValidateUntil(until string) error

ValidateUntil says whether a pattern can be watched for. Empty disables pattern reminders.

Types

type Info

type Info struct {
	ID      string    `json:"id"`
	Name    string    `json:"name,omitempty"`
	Command string    `json:"command"`
	Cwd     string    `json:"cwd"`
	PID     int       `json:"pid"`
	Started time.Time `json:"started"`

	// Every, when non-zero, makes this a periodic probe: the command is run
	// again every Every seconds until it is killed.
	Every int `json:"every,omitempty"`
	// Report is when this process is worth interrupting the model about
	// while it runs: "exit" (the default, nothing until it ends), "changed"
	// (when it writes new output), or "always" (same as changed for now).
	// The ending itself is always reported once, whatever this says.
	Report string `json:"report,omitempty"`
	// Until, when set, is a regex the model wants to be told about: when
	// new output matches, an event fires even if Report is "exit". It is
	// set on Shell.start and adjustable later with Shell.tune.
	Until string `json:"until,omitempty"`
	// RemindEvery, when positive, is seconds between "still running"
	// reminders while the process runs. Each reminder carries new output
	// since the last one, or says there was none. Zero or less means off.
	RemindEvery int `json:"remind_every,omitempty"`
	// LastEventAt is when the model was last told about this running
	// process (match, output, or tick). It throttles RemindEvery and
	// restarts whenever the reminder policy is tuned. Exit reporting uses
	// ExitReported instead.
	LastEventAt time.Time `json:"last_event_at,omitempty"`
	// Persistent marks a process that survives the turn that started it.
	// The default (false) is ephemeral: it is stopped when the turn ends,
	// so an abandoned session leaves nothing behind. Persistent (true) is
	// the opt-in exception — a dev server the next turn still needs — and
	// is shown red instead of the usual blue. It still dies on interrupt
	// (when kill_on_exit holds), on Shell.kill, and on reboot (which
	// reports it as gone); for something that must survive those, write a
	// script and ask the user to run it. Old records without the field
	// decode as ephemeral.
	Persistent bool `json:"persistent,omitempty"`

	State    State     `json:"state"`
	ExitCode int       `json:"exit_code"`
	Ended    time.Time `json:"ended,omitempty"`

	// ReportedOffset is how far into the log the model has already been told
	// about, so the same output is not reported twice across turns.
	ReportedOffset int64 `json:"reported_offset,omitempty"`
	// ExitReported records that the model has been told this one ended.
	ExitReported bool `json:"exit_reported,omitempty"`
}

Info is everything the registry records about one process. It is the whole content of <id>.json, so a later turn reads the same thing this one wrote.

func (Info) Running

func (i Info) Running() bool

Running reports whether this process is still going.

type Registry

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

Registry owns the processes recorded in one session's proc directory.

func New

func New(dir string) (*Registry, error)

New opens the registry over a session's proc directory.

func (*Registry) Configure

func (r *Registry) Configure(id, report, until string, remindEvery int, persistent bool) (Info, error)

Configure adjusts the reminder policy and lifetime of a process that is already running: report, until, the tick interval, and persistent. It is how the model changes its mind after a reminder without restarting the command. Passing the same values back is a no-op except that a changed tick interval restarts its clock from now.

func (*Registry) Dir

func (r *Registry) Dir() string

Dir is the directory the registry keeps its records in.

func (*Registry) Events

func (r *Registry) Events() []string

Events returns what the model has not been told yet: processes that have ended, and running processes whose policy asks for a reminder — a pattern match, new output, or a tick of the clock. Calling it marks what it returns as reported, so the same thing is not raised twice.

func (*Registry) Kill

func (r *Registry) Kill(id string) error

Kill stops a process and everything it started. Killing one that has already finished is not an error: the caller wanted it stopped, and it is stopped. When refresh discovers the process already exited or went away, that transition is saved, so Ended is stamped when it was found rather than drifting to the next Status.

func (*Registry) KillAll

func (r *Registry) KillAll() []string

KillAll stops every running process and returns the ids it stopped. The interrupt handler calls it, so ctrl-c leaves nothing behind.

func (*Registry) KillEphemeral

func (r *Registry) KillEphemeral() []string

KillEphemeral stops every running process that is not persistent and returns the ids it stopped. The turn calls it when it ends, so the default (ephemeral) leaves nothing behind in an abandoned session; persistent processes survive for the next turn.

func (*Registry) List

func (r *Registry) List() []Info

List returns every process this session has started, oldest first.

func (*Registry) LogPath

func (r *Registry) LogPath(id string) string

LogPath is where a process's output is accumulating, for a caller that wants to tell someone where to look.

func (*Registry) Output

func (r *Registry) Output(id string, cursor int64) (string, int64, bool, error)

Output returns what the process has written since the given byte offset, along with the new offset and whether anything was returned. Reading by offset is what lets a caller ask repeatedly and see only what is new.

func (*Registry) Reconcile

func (r *Registry) Reconcile() []Info

Reconcile brings the records up to date with what is actually running and returns the processes whose state changed since anyone last looked. A turn calls it at the start, which is how a process that ended between turns still gets reported.

func (*Registry) RunningCounts

func (r *Registry) RunningCounts() (ephemeral, persistent int)

RunningCounts splits the running processes into ephemeral (blue, die at turn end) and persistent (red, survive). Finished processes count for neither — their ending is reported through the inbox instead.

func (*Registry) Start

func (r *Registry) Start(command, cwd, name string) (string, error)

Start runs a command detached from this turn's iterations and returns the id to refer to it by. The command runs under "sh -c" in its own session, so it survives the iteration and takes its children with it when it is killed. It is ephemeral by default: stopped when the turn ends, so an abandoned session leaves nothing behind. Pass persistent to StartWithOptions for the opt-in exception that survives like the old default.

func (*Registry) StartPeriodic

func (r *Registry) StartPeriodic(command, cwd, name string, every int, report string) (string, error)

StartPeriodic runs a command every `every` seconds until it is killed. It is the same kind of record as any other process, so one set of tools covers both a dev server and a probe that keeps checking something. A periodic probe is persistent by design: "run from now on" means across turns, and it is shown red until killed.

func (*Registry) StartWithOptions

func (r *Registry) StartWithOptions(command, cwd, name, report, until string, remindEvery int, persistent bool) (string, error)

StartWithOptions is Start with the reminder policy and lifetime set up front, so there is no round trip to another tool just to say when to be told: report picks output reminders ("exit" for nothing until it ends), until adds a pattern that reports on match, and remindEvery adds "still running" ticks every that many seconds. Zero remindEvery means off. persistent false (the default) stops the process when the turn ends; persistent true lets it survive into the next turn, shown red, still killed on interrupt, by Shell.kill, or by reboot (reported as gone).

func (*Registry) Status

func (r *Registry) Status(id string) (Info, error)

Status returns what the registry knows about one process, after checking whether it is still alive.

type State

type State string

State is where a process is in its life.

const (
	// StateRunning means the process is alive as far as the kernel is concerned.
	StateRunning State = "running"
	// StateExited means it finished on its own; ExitCode says how.
	StateExited State = "exited"
	// StateKilled means kvit-coder killed it.
	StateKilled State = "killed"
	// StateGone means the process is not alive and left no exit status. It was
	// killed by something outside kvit-coder, or the machine restarted.
	StateGone State = "gone"
)

Jump to

Keyboard shortcuts

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