# Zinit
A lightweight process supervisor with Rhai scripting support.
Zinit manages services (long-running processes) with dependency ordering, health checks, automatic restarts, and a powerful scripting interface.
## Features
- **Rhai Scripting** - Control everything via scripts, not CLI flags
- **Interactive REPL** - Tab completion, syntax highlighting, command history
- **Real-time Output** - `print()` statements stream to client as they execute
- **Single Instance** - Only one zinit runs at a time via socket locking
- **Auto-start** - Running a script automatically starts zinit if needed
- **Service Dependencies** - Start services in order with `after()`
- **Health Checks** - Optional test commands to verify service health
- **Resource Stats** - CPU and memory usage per service
- **Name Normalization** - Service names are case-insensitive, `-` and `_` are equivalent
## Quick Start
```bash
# Build and install
cargo build --release
cp target/release/zinit ~/hero/bin/
# Run a script (auto-starts zinit)
zinit rhaiexamples/01_full_test.rhai
# Or use inline scripts
## Usage
```bash
zinit start # Start daemon (foreground)
zinit start -bg # Start daemon (background)
zinit <script.rhai> # Run script file
zinit <directory> # Run all .rhai files in directory
zinit -i # Read script from stdin
zinit --ui # Interactive REPL with completion
zinit --help # Show API documentation
```
## Interactive REPL
Start the interactive shell with `zinit --ui`:
```
╔═══════════════════════════════════════════════════════════╗
║ Zinit Interactive Shell (Rhai REPL) ║
╠═══════════════════════════════════════════════════════════╣
╚═══════════════════════════════════════════════════════════╝
zinit> zinit_list_md()
| webserver | Running | 1234 | Up |
zinit>
```
### REPL Features
- **Tab Completion** - Type `zinit_` and press Tab to see all functions
- **Syntax Highlighting** - Keywords, functions, strings are colored
- **Command History** - Up/Down arrows, persisted to `~/.zinit_history`
- **Multi-line Input** - Use `{` `}` braces for multi-line scripts
### REPL Commands
| `/help` | Show help |
| `/functions` | List all zinit functions with signatures |
| `/builder` | Show service builder pattern |
| `/load <file>` | Load and execute a .rhai script file |
| `/clear` | Clear screen |
| `/quit` | Exit (or Ctrl+D) |
### Example Session
```
zinit> /functions
Zinit Functions:
Control:
zinit_ping() -> bool Check if zinit is responding
zinit_start() -> bool Start zinit daemon if not running
...
zinit> /load rhaiexamples/01_full_test.rhai
Loading: rhaiexamples/01_full_test.rhai
=== Zinit Full Test ===
Step 1: Creating webserver service...
...
zinit> zinit_stop("webserver")
true
zinit> /quit
Goodbye!
```
## Fixed Paths
Zinit uses fixed paths - no configuration needed:
| `~/hero/cfg/zinit/` | Service YAML files |
| `~/hero/var/zinit.sock` | Unix socket for RPC |
| `~/hero/bin/zinit` | Recommended binary location |
## Example Script
```rhai
// Create and start a web server service
let webserver = zinit_service_new()
.exec("python3 -m http.server 8080")
.log("ring");
zinit_monitor("webserver", webserver);
zinit_start("webserver");
// Wait for it to be running
if zinit_wait_for("webserver", 5) {
print("Web server is running!");
} else {
print("Failed to start web server");
}
// Show status
print(zinit_status_md("webserver"));
```
## API Reference
### Service Management
| `zinit_service_new()` | ServiceBuilder | Create service builder |
| `zinit_monitor(name, builder)` | bool | Register a service |
| `zinit_start(name)` | bool | Start a service |
| `zinit_stop(name)` | bool | Stop a service |
| `zinit_restart(name)` | bool | Restart a service |
| `zinit_forget(name)` | bool | Unregister a stopped service |
| `zinit_kill(name, signal)` | bool | Send signal (e.g., "SIGTERM") |
### Service Builder
```rhai
zinit_service_new()
.exec("command args") // Required: command to run
.test("health-check") // Optional: health check command
.oneshot(true) // Optional: run once, don't restart
.shutdown_timeout(10) // Optional: seconds before SIGKILL
.after("other-service") // Optional: dependency
.signal_stop("SIGTERM") // Optional: stop signal
.log("ring") // Optional: "ring", "stdout", "none"
.env("KEY", "value") // Optional: environment variable
.dir("/path") // Optional: working directory
```
### Query Functions
| `zinit_list()` | array | List service names |
| `zinit_status(name)` | map | Get service status |
| `zinit_stats(name)` | map | Get CPU/memory stats |
| `zinit_is_running(name)` | bool | Check if running |
| `zinit_wait_for(name, secs)` | bool | Wait for service |
### Markdown Output
| `zinit_list_md()` | string | Services as markdown table |
| `zinit_status_md(name)` | string | Status as markdown |
| `zinit_stats_md(name)` | string | Stats as markdown |
### Zinit Control
| `zinit_ping()` | bool | Check if zinit responds |
| `zinit_start()` | bool | Start zinit if not running |
| `zinit_exit()` | void | Graceful shutdown |
| `zinit_stop_all()` | int | Stop all services |
| `zinit_start_all()` | int | Start all services |
### Utilities
| `print(msg)` | Print (streams to client) |
| `sleep(secs)` | Sleep seconds |
| `sleep_ms(ms)` | Sleep milliseconds |
| `get_env(key)` | Get environment variable |
| `set_env(key, val)` | Set environment variable |
| `file_exists(path)` | Check if path exists |
## Socket Protocol
Zinit uses a Unix socket at `~/hero/var/zinit.sock`. You can interact with it directly using netcat or socat.
### Protocol Format
**Send script:**
```
<rhai script lines>
===
```
**Receive response:**
```
ok
<output lines streamed in real-time>
=====
```
### Using Netcat
```bash
# Simple query
printf 'zinit_list_md()\n===\n' | nc -U ~/hero/var/zinit.sock
# Multi-line script
```
### Using Socat (Recommended for Interactive)
```bash
# Install socat (macOS)
brew install socat
# Simple query
printf 'zinit_list_md()\n===\n' | socat - UNIX-CONNECT:$HOME/hero/var/zinit.sock
# Interactive mode with readline support
socat READLINE UNIX-CONNECT:$HOME/hero/var/zinit.sock
# Then type your script and end with ===
# Example:
# zinit_list_md()
# ===
# Send a script file
(cat script.rhai; printf '\n===\n') | socat - UNIX-CONNECT:$HOME/hero/var/zinit.sock
```
### Bash Function for Easy Access
Add this to your `.bashrc` or `.zshrc`:
```bash
# Send Rhai script to zinit
zexec() {
if [ -t 0 ]; then
# Argument mode: zexec 'zinit_list_md()'
printf '%s\n===\n' "$1" | nc -U ~/hero/var/zinit.sock
else
# Pipe mode: echo 'zinit_list_md()' | zexec
(cat; printf '\n===\n') | nc -U ~/hero/var/zinit.sock
fi
}
# Examples:
# zexec 'zinit_list_md()'
# zexec 'zinit_stop("myservice")'
# echo 'print("hello")' | zexec
```
### Python Example
```python
import socket
def send_script(script):
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.connect(f"{os.path.expanduser('~')}/hero/var/zinit.sock")
# Send script with delimiter
sock.sendall(f"{script}\n===\n".encode())
# Read response
response = b""
while True:
chunk = sock.recv(4096)
if not chunk:
break
response += chunk
if b"=====\n" in response:
break
sock.close()
lines = response.decode().split('\n')
status = lines[0] # "ok" or "error"
output = '\n'.join(lines[1:-2]) # Remove status and delimiter
return status, output
# Usage
status, output = send_script('zinit_list_md()')
print(output)
```
## Real-Time Output
Output from `print()` streams to the client in real-time:
```rhai
print("Starting long operation...");
sleep(2);
print("Step 1 complete");
sleep(2);
print("Step 2 complete");
print("Done!");
```
Each line appears as it executes, not all at once when the script finishes.
## Service States
| `Spawned` | Process started, not yet confirmed running |
| `Running` | Process is running |
| `Success` | Oneshot completed successfully |
| `Error` | Process exited with error |
| `Blocked` | Waiting for dependencies |
| `Unknown` | Service registered but not monitored |
## Examples
See the `rhaiexamples/` directory:
- `01_full_test.rhai` - Complete workflow: create, start, query, stop, forget
- `02_stream_test.rhai` - Test real-time output streaming
## Documentation
- `zinit --help` - API documentation (also in `src/instructions.md`)
- `rhaiinstructions.md` - Technical implementation details
- [docs.rs/zinit](https://docs.rs/zinit) - Rust API documentation
## Building
```bash
# Debug build
cargo build
# Release build
cargo build --release
# Run tests
cargo test
```
## License
Apache 2.0