loop

package
v0.11.0 Latest Latest
Warning

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

Go to latest
Published: Sep 24, 2026 License: MIT Imports: 16 Imported by: 0

Documentation

Overview

Package loop provides a zero-dependency cron parser and an injectable-clock job registry backing nib's /loop feature.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Diff added in v0.11.0

func Diff(old, new string) string

Diff returns a unified-diff-style string showing changed lines between old and new. Returns "" when old is empty (first run). The output is capped at maxDiffChars.

Types

type Job

type Job struct {
	ID        string    `json:"id"`
	Expr      string    `json:"expr"`
	Prompt    string    `json:"prompt"`
	Recurring bool      `json:"recurring"`
	Durable   bool      `json:"durable"`
	Paused    bool      `json:"paused,omitempty"`
	Created   time.Time `json:"created"`

	// Monitor fields (optional): when set, the job runs a script or fetches a
	// URL at fire time and only dispatches the agent when the output changed
	// (its SHA-256 differs from LastOutputHash). LastOutputHash/LastOutput/
	// LastChangedAt persist across restarts for durable jobs.
	MonitorScript  string    `json:"monitor_script,omitempty"`
	MonitorURL     string    `json:"monitor_url,omitempty"`
	LastOutputHash string    `json:"last_output_hash,omitempty"`
	LastOutput     string    `json:"last_output,omitempty"`
	LastChangedAt  time.Time `json:"last_changed_at,omitempty"`
	// contains filtered or unexported fields
}

Job is a registered recurring (or one-shot) task. Prompt is the payload to run when the job fires — a slash command or plain prompt, resolved by the host through slash.Resolve at fire time.

type MonitorConfig added in v0.11.0

type MonitorConfig struct {
	Script string
	URL    string
}

MonitorConfig is the monitor mode of a job: at most one of Script or URL is set. An empty config means monitor mode is off and the job fires normally.

type MonitorResult added in v0.11.0

type MonitorResult struct {
	Output string
	Hash   string
	Err    error
}

MonitorResult is the output of a monitor check.

func RunMonitor added in v0.11.0

func RunMonitor(m MonitorConfig) MonitorResult

RunMonitor executes the monitor source (script or URL) and returns the capped output with its SHA-256 hash. Returns an error result when no source is configured or the check fails.

type Registry

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

Registry is a thread-safe store of cron jobs with an injectable clock.

func NewRegistry

func NewRegistry() *Registry

NewRegistry returns an empty registry using time.Now as its clock.

func (*Registry) Add

func (r *Registry) Add(expr, prompt string, recurring, durable bool, monitor MonitorConfig) (Job, error)

Add parses expr, registers a job, and returns it. Returns an error if the expression is invalid. monitor (optional) attaches monitor mode: at most one of Script/URL may be set; both set returns an error.

func (*Registry) Delete

func (r *Registry) Delete(id string) bool

Delete removes the job with the given id; returns whether it existed.

func (*Registry) Due

func (r *Registry) Due() []Job

Due returns the jobs whose next-fire time has arrived (<= now), advancing recurring jobs to their next slot and removing fired one-shots. Paused jobs are skipped. The registry changes before the caller dispatches, so a caller that saves right after Due fires each durable slot at most once, even if the process dies mid-dispatch.

func (*Registry) Get added in v0.9.2

func (r *Registry) Get(id string) (Job, bool)

Get returns a copy of the job with the given id.

func (*Registry) List

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

List returns a copy of the current jobs, sorted by next-fire time.

func (*Registry) Load

func (r *Registry) Load(path string) (int, error)

Load reads durable jobs from path and registers them (re-parsing exprs and recomputing next-fire from the current clock). A missing file is not an error. Returns the number of jobs loaded. Jobs are re-registered with next-fire recomputed from the current clock; jobs whose expression no longer parses (or can never fire) are skipped. Job IDs are reassigned on load (use List to see current IDs); the original Created timestamp is preserved.

func (*Registry) Pause added in v0.9.2

func (r *Registry) Pause(id string) bool

Pause stops the job from firing until Resume; it stays registered. Returns whether the job exists.

func (*Registry) Resume added in v0.9.2

func (r *Registry) Resume(id string) bool

Resume lets a paused job fire again. Its next fire is recomputed from now, so the slots it missed while paused do not fire at once. Returns whether the job exists.

func (*Registry) Save

func (r *Registry) Save(path string) error

Save writes the durable jobs to path as JSON, creating parent dirs. Jobs with Durable == false are skipped. Re-parses are deferred to Load.

func (*Registry) SetClock

func (r *Registry) SetClock(now func() time.Time)

SetClock overrides the clock (tests). Must be called before Add.

func (*Registry) SetMonitorState added in v0.11.0

func (r *Registry) SetMonitorState(id, hash, output string, changedAt time.Time) bool

SetMonitorState records the latest monitor output for the job with id and returns whether the output changed (the new hash differs from the stored one). changedAt is the timestamp to record as the last change time when the output changed. Returns false when the job does not exist.

type Schedule

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

Schedule is a parsed cron expression: [second] minute hour day-of-month month day-of-week, all evaluated in the timezone of the time passed to Next. The leading seconds field is optional; when absent it defaults to second 0.

func Parse

func Parse(expr string) (Schedule, error)

Parse parses a 5- or 6-field cron expression. With 6 fields the leading field is seconds (0-59); with 5 fields seconds defaults to 0 (backward compatible). Supports '*', '*/n', 'a-b', 'a,b,c', and single values per field. Day-of-week 0 = Sunday.

All fields are ANDed together — including day-of-month and day-of-week. This differs from POSIX/Vixie cron, which ORs day-of-month and day-of-week when both are restricted. The deviation is acceptable here because nib's /loop never restricts both fields at once.

func (Schedule) Next

func (s Schedule) Next(after time.Time) (time.Time, bool)

Next returns the first instant strictly after `after` that matches the schedule, at second granularity. ok is false if none is found within ~4 years (guards bad exprs).

Jump to

Keyboard shortcuts

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