Sign in

    redstart

    Lightweight process manager for local development

    process-manager
    cli
    native
    devtools
    Download zip
    Author
    Version
    0.1.4
    License
    Apache-2.0
    Last updated
    5 months ago
    Downloads
    48

    Dependencies

    #tiye/redstart

    Lightweight process manager for local development, written in MoonBit.

    Redstart manages named processes defined in a redstart.json5 file, keeps logs in .redstart-monitor/, and exposes a simple CLI suitable for both humans and agents.

    #Installation

    moon install tiye/redstart --path ./cmd/redstart

    After installation, redstart is available as ~/.moon/bin/redstart.

    #Quick Start

    # Initialize config in your project root redstart init # Add process definitions redstart add server "yarn dev" redstart add worker "yarn worker" --cwd ./packages/worker redstart add api "python -m uvicorn main:app" --env "PORT=8000,DEBUG=1" # Start everything redstart start # Watch status redstart list # Stream recent logs redstart logs server redstart logs server --stderr -n 100 # Stop everything redstart stop

    #CLI Reference

    CommandDescription
    redstart initInitialize redstart.json5 in current directory
    redstart add <alias> <command>Add a process definition (--cwd, --env KEY=VAL,...)
    redstart remove <alias\|id>Remove a stopped process
    redstart start [alias\|id]Start one or all stopped processes
    redstart stop [alias\|id]Stop one or all running processes
    redstart restart <alias\|id>Restart a process
    redstart list [--json]List all processes and their status
    redstart status [alias\|id] [--json]Show status (all or specific)
    redstart logs <alias\|id> [-n N] [--stdout\|--stderr]Show recent log output
    redstart inspect <alias\|id>Dump full process state as JSON
    redstart wait <alias\|id> [-t seconds]Block until process is running (useful in scripts/agents)
    redstart doc [topic]Show documentation overview or per-command reference

    #Config Format

    redstart.json5 uses JSON5 syntax and is human-editable:

    { version: "1", processes: [ { id: "abc123", proc_name: "server", command: "yarn dev", cwd: "./packages/server", env: { PORT: "3000" }, status: "stopped", }, ], }

    #Logs and PIDs

    Redstart stores runtime data in .redstart-monitor/<alias>/:

    • stdout.log — captured stdout
    • stderr.log — captured stderr
    • pid — PID file while process is alive

    Add .redstart-monitor/ to your .gitignore.

    #Machine-Readable Output

    For use in scripts and AI agents:

    # JSON list of all processes redstart list --json # Full state of one process as JSON redstart inspect server # Wait up to 60 seconds for a process to start, then proceed redstart wait server -t 60 && curl http://localhost:3000/health

    #Library API

    The core library is importable as tiye/redstart:

    import "tiye/redstart"

    let config = @redstart.load_config()!
    let entry = @redstart.find_process(config, "server")

    Key types and functions:

    • ProcessEntry — process definition with id, proc_name, command, status, pid, etc.
    • Config — collection of ProcessEntry values
    • load_config() -> Config raise RedstartError
    • save_config(config) -> Unit raise RedstartError
    • spawn_process(entry) -> Int raise RedstartError — returns PID
    • start_with_retry(entry, retries~, delay_ms~) -> Int raise RedstartError
    • stop_gracefully(pid) -> Unit raise RedstartError
    • is_process_alive(pid) -> Bool
    • wait <alias\|id> [-t seconds] — block until running

    #Requirements

    • MoonBit native target (uses C FFI via posix_spawn)
    • macOS or Linux

    #License

    Apache-2.0

    RedstartError

    pub suberror RedstartError {
    RedstartError(String)
    }

    Error type for all redstart operations.

    Config

    pub struct Config {
    version : String
    processes : Array[ProcessEntry]
    } derive(ToJson,
    Debug
    ,
    FromJson
    )

    Root configuration / state file structure.
    impl Show for Config

    Config::add_process

    fn Config::add_process(self : Config, entry : ProcessEntry) -> Unit

    Add a process entry to the config.

    Config::remove_process

    fn Config::remove_process(self : Config, target : String) -> Bool

    Remove a process entry by name or id.

    ProcessEntry

    pub struct ProcessEntry {
    id : String
    proc_name : String
    command : String
    cwd : String?
    env : Map[String, String]
    created_at : String
    status : String
    pid : Int?
    started_at : String?
    stopped_at : String?
    exit_code : Int?
    retry_count : Int
    } derive(ToJson,
    Debug
    ,
    FromJson
    )

    A single managed process entry, combining its definition and runtime state. Note: proc_name is used internally (MoonBit reserves alias).

    ProcessEntry::mark_failed

    fn ProcessEntry::mark_failed(self : ProcessEntry) -> Unit

    Update entry to reflect a failed start.

    ProcessEntry::mark_running

    fn ProcessEntry::mark_running(self : ProcessEntry, pid : Int, ts : String) -> Unit

    Update entry to reflect a successful start.

    ProcessEntry::mark_stopped

    fn ProcessEntry::mark_stopped(self : ProcessEntry, ts : String, code : Int?) -> Unit

    Update entry to reflect a clean stop.

    ProcessEntry::mark_unknown

    fn ProcessEntry::mark_unknown(self : ProcessEntry) -> Unit

    Update entry status to unknown (lost track of process).

    ProcessGroupMember

    pub struct ProcessGroupMember {
    pid : Int
    ppid : Int
    pgid : Int
    command : String
    } derive(ToJson,
    Debug
    )

    ProcessInspectSnapshot

    pub struct ProcessInspectSnapshot {
    process : ProcessEntry
    process_group_members : Array[ProcessGroupMember]
    child_processes : Array[ProcessGroupMember]
    } derive(ToJson,
    Debug
    )

    config_file

    let config_file : String

    config_file_exists

    fn config_file_exists() -> Bool

    Check if the config file exists in the current directory.

    ensure_monitor_dir

    fn ensure_monitor_dir(proc : String) -> Unit raise RedstartError

    Ensure the monitor directory for a process exists.

    ensure_root_monitor_dir

    fn ensure_root_monitor_dir() -> Unit raise RedstartError

    Ensure the root .redstart-monitor directory exists.

    find_process

    fn find_process(config : Config, target : String) -> ProcessEntry?

    Find a process entry by proc_name (alias) or id.

    force_kill_process

    fn force_kill_process(pid : Int) -> Bool

    Send SIGKILL (9) to a process.

    format_pid

    fn format_pid(pid : Int?) -> String

    Format an optional PID for display.

    generate_id

    fn generate_id(now_ts : Int64, seed : String) -> String

    Generate a unique 8-character ID from a timestamp mixed with a seed string. Mixing in the seed ensures IDs differ even when called multiple times per second.

    inspect_process

    fn inspect_process(entry : ProcessEntry) -> ProcessInspectSnapshot

    is_process_alive

    fn is_process_alive(pid : Int) -> Bool

    Check if a process with the given PID is alive.

    json_to_pretty_string

    fn json_to_pretty_string(json : Json) -> String

    Serialize Json to a pretty-printed JSON5 string with 2-space indentation. Object keys that are valid JSON5 identifiers are written unquoted.

    json_to_string

    fn json_to_string(json : Json) -> String

    Serialize Json to compact JSON5 string (for machine-readable / inspect output).

    kill_process

    fn kill_process(pid : Int) -> Bool

    Send SIGTERM (15) to a process.

    list_process_group_members

    fn list_process_group_members(proc : String, pgid : Int) -> Array[ProcessGroupMember] raise RedstartError

    load_config

    fn load_config() -> Config raise RedstartError

    Load config from redstart.json5 in current directory.

    monitor_dir

    let monitor_dir : String

    monitor_path

    fn monitor_path(proc : String) -> String

    new_config

    fn new_config() -> Config

    Create an empty Config.

    new_process_entry

    fn new_process_entry(id : String, proc_name : String, command : String, cwd : String?, env : Map[String, String], created_at : String) -> ProcessEntry

    Create a new ProcessEntry in "stopped" state.

    new_process_group_member

    fn new_process_group_member(pid : Int, ppid : Int, pgid : Int, command : String) -> ProcessGroupMember

    now

    fn now() -> Int64

    Get the current Unix timestamp in seconds.

    pid_file_path

    fn pid_file_path(proc : String) -> String

    process_group_snapshot_path

    fn process_group_snapshot_path(proc : String) -> String

    read_last_lines

    fn read_last_lines(path : String, n : Int) -> Array[String] raise RedstartError

    Read the last N lines of a file.

    save_config

    fn save_config(config : Config) -> Unit raise RedstartError

    Save config to redstart.json5 in pretty-printed form.

    sleep_ms

    fn sleep_ms(ms : Int) -> Unit

    Sleep for ms milliseconds.

    spawn_process

    fn spawn_process(command : String, cwd : String, stdout_path : String, stderr_path : String) -> Int?

    Spawn a shell command as a background process. Returns the PID on success, or None on failure.

    start_with_retry

    fn start_with_retry(entry : ProcessEntry, max_retries : Int) -> (Int, String) raise RedstartError

    Start a process with retry logic (up to max_retries attempts). Returns (pid, timestamp) on success, raises RedstartError on failure.

    stderr_log_path

    fn stderr_log_path(proc : String) -> String

    stdout_log_path

    fn stdout_log_path(proc : String) -> String

    stop_gracefully

    fn stop_gracefully(pid : Int) -> Unit

    Stop a process group gracefully (SIGTERM, then SIGKILL after timeout).