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 ¶
- func ValidateRemindEvery(remindEvery int) error
- func ValidateReport(report string) error
- func ValidateUntil(until string) error
- type Info
- type Registry
- func (r *Registry) Configure(id, report, until string, remindEvery int, persistent bool) (Info, error)
- func (r *Registry) Dir() string
- func (r *Registry) Events() []string
- func (r *Registry) Kill(id string) error
- func (r *Registry) KillAll() []string
- func (r *Registry) KillEphemeral() []string
- func (r *Registry) List() []Info
- func (r *Registry) LogPath(id string) string
- func (r *Registry) Output(id string, cursor int64) (string, int64, bool, error)
- func (r *Registry) Reconcile() []Info
- func (r *Registry) RunningCounts() (ephemeral, persistent int)
- func (r *Registry) Start(command, cwd, name string) (string, error)
- func (r *Registry) StartPeriodic(command, cwd, name string, every int, report string) (string, error)
- func (r *Registry) StartWithOptions(command, cwd, name, report, until string, remindEvery int, persistent bool) (string, error)
- func (r *Registry) Status(id string) (Info, error)
- type State
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ValidateRemindEvery ¶
ValidateRemindEvery says whether a tick interval is usable. Zero means off; negatives are rejected.
func ValidateReport ¶
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 ¶
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.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry owns the processes recorded in one 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) Events ¶
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 ¶
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 ¶
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 ¶
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) LogPath ¶
LogPath is where a process's output is accumulating, for a caller that wants to tell someone where to look.
func (*Registry) Output ¶
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 ¶
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 ¶
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 ¶
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).
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" )