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
3 months ago
Downloads
33

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).