1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
mod cli;
mod config;
mod daemon;
mod process;
mod tui;
use clap::{Parser, Subcommand};
#[derive(Parser)]
#[command(
name = "proses",
version = "0.1.0",
about = "Proses – Professional Secure Execution System",
long_about = None
)]
struct Cli {
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
/// Start a new process (or replace an existing one with the same name)
#[command(visible_alias = "run")]
Start {
/// Shell command to execute (passed verbatim to sh -c)
command: String,
/// Human-readable name (defaults to the first token of the command)
#[arg(short, long)]
name: Option<String>,
/// Working directory (defaults to the current directory)
#[arg(long)]
cwd: Option<String>,
/// Extra environment variables in KEY=VALUE format
#[arg(long = "env", value_name = "KEY=VALUE")]
env: Vec<String>,
/// Max automatic restarts before marking errored (0 = unlimited)
#[arg(long, default_value_t = 10)]
max_restarts: u32,
},
/// Gracefully stop a process (it will not be auto-restarted)
Stop {
/// Process name or numeric ID
name_or_id: String,
},
/// Stop then immediately re-launch a process
Restart {
/// Process name or numeric ID
name_or_id: String,
},
/// Stop and permanently remove a process from the list
#[command(visible_alias = "rm", visible_alias = "del")]
Delete {
/// Process name or numeric ID
name_or_id: String,
},
/// Open the live process dashboard (interactive Ratatui TUI)
#[command(visible_alias = "ls", visible_alias = "ps", visible_alias = "status")]
List,
/// Dump the last N lines of a process's log to stdout
Logs {
/// Process name or numeric ID
name_or_id: String,
/// Number of log lines to show
#[arg(short = 'n', long, default_value_t = 50)]
lines: usize,
},
/// Flush the in-memory store to ~/.proses/store.json
Save,
/// Re-launch all processes that were running when last saved
Resurrect,
/// Print detailed information about a process
#[command(visible_alias = "info")]
Show {
/// Process name or numeric ID
name_or_id: String,
},
/// Manage the background daemon
Daemon {
#[command(subcommand)]
command: DaemonCommands,
},
}
#[derive(Subcommand)]
enum DaemonCommands {
/// Start the background daemon
Start,
/// Stop the background daemon
Stop,
/// Show whether the daemon is running and its PID
Status,
}
fn main() {
let cli = Cli::parse();
// Initialise ~/.proses/ paths before anything else.
config::init();
match cli.command {
// Daemon sub-commands manage the background process directly — no
// need to auto-start the daemon first.
Commands::Daemon { command } => match command {
DaemonCommands::Start => daemon::start_cmd(),
DaemonCommands::Stop => daemon::stop_cmd(),
DaemonCommands::Status => daemon::status_cmd(),
},
// All other commands talk to the daemon over IPC, so we must ensure
// it is running before proceeding.
other => {
daemon::ensure_running();
dispatch(other);
}
}
}
fn dispatch(command: Commands) {
match command {
Commands::Start {
command,
name,
cwd,
env,
max_restarts,
} => {
cli::start(command, name, cwd, env, max_restarts);
}
Commands::Stop { name_or_id } => cli::stop(name_or_id),
Commands::Restart { name_or_id } => cli::restart(name_or_id),
Commands::Delete { name_or_id } => cli::delete(name_or_id),
Commands::Save => cli::save(),
Commands::Resurrect => cli::resurrect(),
Commands::Show { name_or_id } => cli::show(name_or_id),
Commands::Logs { name_or_id, lines } => cli::logs(name_or_id, lines),
Commands::List => {
tui::run_dashboard().unwrap_or_else(|e| {
eprintln!(" ✗ TUI error: {e}");
std::process::exit(1);
});
}
Commands::Daemon { .. } => unreachable!(),
}
}