6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 1 | use std::{io, os::unix::net::UnixStream, path::PathBuf}; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 2 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 3 | use pound::Parse; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 4 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 5 | use crate::{ |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 6 | artifacts, client, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 7 | config::Config, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 8 | daemon, history, hook, paths, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 9 | proto::{self, Event, Reader, Request}, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 10 | run, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 11 | units::{Amount, Bytes, Dur}, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 12 | }; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 13 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 14 | /// one build at a time, please |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 15 | #[derive(Parse)] |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 16 | #[pound(name = "rabt", version = env!("CARGO_PKG_VERSION"))] |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 17 | pub enum Rabt { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 18 | /// wait your turn, then run the thing |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 19 | Run { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 20 | /// preset from config, s m l xl out of the box |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 21 | #[pound(short, long)] |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 22 | size: Option<String>, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 23 | #[pound(long)] |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 24 | ram: Option<Bytes>, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 25 | #[pound(long)] |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 26 | disk: Option<Bytes>, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 27 | /// count or percentage of the machine |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 28 | #[pound(long)] |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 29 | cores: Option<Amount<u32>>, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 30 | /// how long you reckon it'll take |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 31 | #[pound(short, long)] |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 32 | time: Option<Dur>, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 33 | /// kill it past this, exit 124 |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 34 | #[pound(long)] |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 35 | deadline: Option<Dur>, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 36 | /// stop queueing past this, exit 75 |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 37 | #[pound(long)] |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 38 | max_wait: Option<Dur>, |
4e5ca2e20401 | atagen | 2026-09-28 11:42 UTC | 39 | #[pound(long, hidden)] |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 40 | session: Option<String>, |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 41 | /// exactly the cores asked for, no spares |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 42 | #[pound(long)] |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 43 | exact: bool, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 44 | #[pound(trailing)] |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 45 | cmd: Vec<String>, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 46 | }, |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 47 | /// say you'll be building soon, so others leave you room |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 48 | Expect { |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 49 | #[pound(short, long)] |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 50 | size: Option<String>, |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 51 | #[pound(long)] |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 52 | ram: Option<Bytes>, |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 53 | #[pound(long)] |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 54 | cores: Option<Amount<u32>>, |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 55 | /// changed your mind |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 56 | #[pound(long)] |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 57 | done: bool, |
4e5ca2e20401 | atagen | 2026-09-28 11:42 UTC | 58 | #[pound(long, hidden)] |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 59 | session: Option<String>, |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 60 | }, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 61 | /// who's running, who's waiting |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 62 | Status { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 63 | #[pound(long)] |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 64 | json: bool, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 65 | }, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 66 | /// a size guess from past runs |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 67 | Suggest { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 68 | #[pound(trailing)] |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 69 | cmd: Vec<String>, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 70 | }, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 71 | /// stop a lease's build |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 72 | Kill { lease: u64 }, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 73 | /// buy an overdue build more time |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 74 | Extend { lease: u64, by: Dur }, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 75 | /// exit when a lease goes overdue or finishes |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 76 | Watch { lease: u64 }, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 77 | /// build output and how much of it there is |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 78 | Artifacts { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 79 | #[pound(subcommand)] |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 80 | action: Artifacts, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 81 | }, |
4e5ca2e20401 | atagen | 2026-09-28 11:42 UTC | 82 | /// agent hook entrypoint, json on stdin |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 83 | Hook { |
4e5ca2e20401 | atagen | 2026-09-28 11:42 UTC | 84 | /// pre-tool-use post-tool-use prompt-submit stop subagent-stop session-end, or codex-prefixed variants |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 85 | event: String, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 86 | }, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 87 | #[pound(hidden)] |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 88 | Daemon, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 89 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 90 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 91 | #[derive(Parse)] |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 92 | pub enum Artifacts { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 93 | /// sizes against the caps, exits 1 when over |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 94 | Check { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 95 | /// re-measure instead of trusting the cache |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 96 | #[pound(long)] |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 97 | fresh: bool, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 98 | }, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 99 | /// everything tracked, biggest first |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 100 | List, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 101 | /// delete idle artifacts |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 102 | Gc { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 103 | /// keep going, oldest first, until under the caps |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 104 | #[pound(long)] |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 105 | cap: bool, |
e9a5f5a41749 | atagen | 2026-10-25 11:09 UTC | 106 | /// leave other projects alone |
e9a5f5a41749 | atagen | 2026-10-25 11:09 UTC | 107 | #[pound(long)] |
e9a5f5a41749 | atagen | 2026-10-25 11:09 UTC | 108 | project: bool, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 109 | #[pound(short = 'n', long)] |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 110 | dry_run: bool, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 111 | }, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 112 | /// never gc this |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 113 | Pin { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 114 | path: PathBuf, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 115 | }, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 116 | Unpin { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 117 | path: PathBuf, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 118 | }, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 119 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 120 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 121 | fn artifacts(cfg: &Config, action: Artifacts) -> i32 { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 122 | let cwd = std::env::current_dir().unwrap_or_else(|_| "/".into()); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 123 | let root = paths::project_root(&cwd); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 124 | match action { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 125 | Artifacts::Check { fresh } => { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 126 | let r = artifacts::check(cfg, Some(&root), fresh); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 127 | if let Some((root, size)) = &r.project { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 128 | println!( |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 129 | "{}: {size} of {}", |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 130 | root.display(), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 131 | cfg.artifacts.project_cap |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 132 | ); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 133 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 134 | println!("everything: {} of {}", r.global, cfg.artifacts.global_cap); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 135 | if r.over() { |
e9a5f5a41749 | atagen | 2026-10-25 11:09 UTC | 136 | println!("over. `{}`", r.remedy()); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 137 | 1 |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 138 | } else { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 139 | 0 |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 140 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 141 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 142 | Artifacts::List => { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 143 | let r = artifacts::check(cfg, Some(&root), false); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 144 | let mut rows: Vec<_> = r.entries.iter().collect(); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 145 | rows.sort_by_key(|(_, e)| std::cmp::Reverse(e.size)); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 146 | for (path, e) in rows { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 147 | println!( |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 148 | "{:>10} {}{}", |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 149 | e.size.to_string(), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 150 | path.display(), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 151 | if e.pinned { " (pinned)" } else { "" } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 152 | ); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 153 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 154 | 0 |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 155 | } |
e9a5f5a41749 | atagen | 2026-10-25 11:09 UTC | 156 | Artifacts::Gc { |
e9a5f5a41749 | atagen | 2026-10-25 11:09 UTC | 157 | cap, |
e9a5f5a41749 | atagen | 2026-10-25 11:09 UTC | 158 | project, |
e9a5f5a41749 | atagen | 2026-10-25 11:09 UTC | 159 | dry_run, |
e9a5f5a41749 | atagen | 2026-10-25 11:09 UTC | 160 | } => { |
e9a5f5a41749 | atagen | 2026-10-25 11:09 UTC | 161 | let g = artifacts::gc(cfg, project.then_some(root.as_path()), cap, dry_run); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 162 | for p in &g.removed { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 163 | println!( |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 164 | "{}{}", |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 165 | if dry_run { "would remove " } else { "removed " }, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 166 | p.display() |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 167 | ); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 168 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 169 | for f in &g.failed { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 170 | eprintln!("rabt: {f}"); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 171 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 172 | println!( |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 173 | "{} {}", |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 174 | if dry_run { "would free" } else { "freed" }, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 175 | g.freed |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 176 | ); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 177 | i32::from(!g.failed.is_empty()) |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 178 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 179 | Artifacts::Pin { path } | Artifacts::Unpin { path } if !path.exists() => { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 180 | eprintln!("rabt: {} doesn't exist", path.display()); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 181 | 1 |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 182 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 183 | Artifacts::Pin { path } => report(artifacts::pin(&path, true)), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 184 | Artifacts::Unpin { path } => report(artifacts::pin(&path, false)), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 185 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 186 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 187 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 188 | fn report(r: Result<(), String>) -> i32 { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 189 | r.map_or_else( |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 190 | |e| { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 191 | eprintln!("rabt: {e}"); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 192 | 1 |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 193 | }, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 194 | |()| 0, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 195 | ) |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 196 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 197 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 198 | fn ask(req: &Request) -> io::Result<Option<Event>> { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 199 | let Some(mut s) = client::connect() else { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 200 | return Ok(None); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 201 | }; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 202 | proto::send(&mut s, req)?; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 203 | Reader::new(&s)?.recv() |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 204 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 205 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 206 | fn reply(ev: io::Result<Option<Event>>) -> i32 { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 207 | match ev { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 208 | Ok(Some(Event::Ok)) => 0, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 209 | Ok(Some(Event::Err { msg })) => { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 210 | eprintln!("rabt: {msg}"); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 211 | 1 |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 212 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 213 | Ok(None) => { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 214 | eprintln!("rabt: nothing running"); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 215 | 1 |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 216 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 217 | Ok(Some(_)) => 1, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 218 | Err(e) => { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 219 | eprintln!("rabt: {e}"); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 220 | 1 |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 221 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 222 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 223 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 224 | |
4e5ca2e20401 | atagen | 2026-09-28 11:42 UTC | 225 | fn agent_session(session: Option<String>) -> Option<String> { |
4e5ca2e20401 | atagen | 2026-09-28 11:42 UTC | 226 | session.or_else(|| { |
4e5ca2e20401 | atagen | 2026-09-28 11:42 UTC | 227 | [ |
4e5ca2e20401 | atagen | 2026-09-28 11:42 UTC | 228 | "ROUNDABOUT_SESSION_ID", |
4e5ca2e20401 | atagen | 2026-09-28 11:42 UTC | 229 | "CLAUDE_CODE_SESSION_ID", |
4e5ca2e20401 | atagen | 2026-09-28 11:42 UTC | 230 | "CODEX_THREAD_ID", |
4e5ca2e20401 | atagen | 2026-09-28 11:42 UTC | 231 | "CODEX_SESSION_ID", |
4e5ca2e20401 | atagen | 2026-09-28 11:42 UTC | 232 | ] |
4e5ca2e20401 | atagen | 2026-09-28 11:42 UTC | 233 | .into_iter() |
4e5ca2e20401 | atagen | 2026-09-28 11:42 UTC | 234 | .find_map(|key| std::env::var(key).ok().filter(|value| !value.is_empty())) |
4e5ca2e20401 | atagen | 2026-09-28 11:42 UTC | 235 | }) |
4e5ca2e20401 | atagen | 2026-09-28 11:42 UTC | 236 | } |
4e5ca2e20401 | atagen | 2026-09-28 11:42 UTC | 237 | |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 238 | fn expect(cfg: &Config, session: Option<String>, done: bool, a: &run::Args) -> i32 { |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 239 | let Some(session) = session.filter(|s| !s.is_empty()) else { |
4e5ca2e20401 | atagen | 2026-09-28 11:42 UTC | 240 | eprintln!("rabt: no agent session id, use --session to name one"); |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 241 | return 2; |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 242 | }; |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 243 | let need = if done { |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 244 | None |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 245 | } else { |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 246 | match run::need(cfg, a) { |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 247 | Ok(n) => Some(n), |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 248 | Err(e) => { |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 249 | eprintln!("rabt: {e}"); |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 250 | return 2; |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 251 | } |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 252 | } |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 253 | }; |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 254 | let mut s = match client::connect_or_spawn() { |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 255 | Ok(s) => s, |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 256 | Err(e) => { |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 257 | eprintln!("rabt: can't reach the daemon: {e}"); |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 258 | return 1; |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 259 | } |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 260 | }; |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 261 | if let Err(e) = proto::send(&mut s, &Request::Expect { session, need }) { |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 262 | eprintln!("rabt: {e}"); |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 263 | return 1; |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 264 | } |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 265 | reply(Reader::new(&s).and_then(|mut r| r.recv())) |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 266 | } |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 267 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 268 | fn status(json: bool) -> i32 { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 269 | let Ok(Some(Event::Status(st))) = ask(&Request::Status) else { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 270 | if json { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 271 | println!("null"); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 272 | } else { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 273 | println!("idle"); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 274 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 275 | return 0; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 276 | }; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 277 | if json { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 278 | println!("{}", serde_json::to_string(&st).unwrap_or_default()); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 279 | return 0; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 280 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 281 | println!( |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 282 | "ram {} free of {}, cores {} free of {}", |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 283 | st.ram_free, st.ram_budget, st.cores_free, st.cores |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 284 | ); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 285 | for l in &st.leases { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 286 | let state = if l.running { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 287 | format!( |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 288 | "running {}{}", |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 289 | Dur::secs(l.elapsed), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 290 | if l.overdue { " OVERDUE" } else { "" } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 291 | ) |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 292 | } else { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 293 | format!( |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 294 | "#{} {}", |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 295 | l.position, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 296 | l.waiting.map(|r| r.to_string()).unwrap_or_default() |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 297 | ) |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 298 | }; |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 299 | let cores = if l.granted > l.need.cores { |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 300 | format!("{}+{}", l.need.cores, l.granted - l.need.cores) |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 301 | } else { |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 302 | l.need.cores.to_string() |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 303 | }; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 304 | println!( |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 305 | "{:>4} {}{} {} ram {} cores {} rss {} {}", |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 306 | l.id, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 307 | if l.heavy { "H " } else { "" }, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 308 | state, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 309 | Dur(l.need.time), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 310 | l.need.ram, |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 311 | cores, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 312 | l.rss, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 313 | l.cmd.join(" ") |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 314 | ); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 315 | } |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 316 | for i in &st.intents { |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 317 | println!( |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 318 | " expect {} ram {} cores {}, {} left", |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 319 | &i.session[..i.session.len().min(8)], |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 320 | i.need.ram, |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 321 | i.need.cores, |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 322 | Dur::secs(i.expires_in) |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 323 | ); |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 324 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 325 | 0 |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 326 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 327 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 328 | fn suggest(cfg: &Config, cmd: &[String]) -> i32 { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 329 | let cwd = std::env::current_dir().unwrap_or_else(|_| "/".into()); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 330 | let key = history::key(&paths::project_root(&cwd), cmd); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 331 | let Some(rec) = history::lookup(&key) else { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 332 | println!("never seen it, pick a --size"); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 333 | return 1; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 334 | }; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 335 | let base = match run::base(cfg) { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 336 | Ok(b) => b, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 337 | Err(e) => { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 338 | eprintln!("rabt: {e}"); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 339 | return 2; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 340 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 341 | }; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 342 | let n = history::suggest(&rec, base, cmd); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 343 | println!( |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 344 | "--ram {} --cores {} --time {} --disk {}", |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 345 | n.ram, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 346 | n.cores, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 347 | Dur(n.time), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 348 | n.disk |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 349 | ); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 350 | 0 |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 351 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 352 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 353 | fn watch(lease: u64) -> i32 { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 354 | let Some(mut s): Option<UnixStream> = client::connect() else { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 355 | return 0; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 356 | }; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 357 | if proto::send(&mut s, &Request::Watch { lease }).is_err() { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 358 | return 0; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 359 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 360 | if matches!( |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 361 | Reader::new(&s).and_then(|mut r| r.recv::<Event>()), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 362 | Ok(Some(Event::Fired { overdue: true })) |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 363 | ) { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 364 | println!("lease {lease} is overdue, `rabt kill {lease}` or `rabt extend {lease} <dur>`"); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 365 | 3 |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 366 | } else { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 367 | println!("lease {lease} is done"); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 368 | 0 |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 369 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 370 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 371 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 372 | pub fn dispatch(cfg: &Config, cmd: Rabt) -> i32 { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 373 | match cmd { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 374 | Rabt::Run { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 375 | size, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 376 | ram, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 377 | disk, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 378 | cores, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 379 | time, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 380 | deadline, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 381 | max_wait, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 382 | session, |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 383 | exact, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 384 | cmd, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 385 | } => run::run( |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 386 | cfg, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 387 | &run::Args { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 388 | size, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 389 | ram, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 390 | disk, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 391 | cores, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 392 | time, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 393 | deadline, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 394 | max_wait, |
4e5ca2e20401 | atagen | 2026-09-28 11:42 UTC | 395 | session: agent_session(session), |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 396 | exact, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 397 | cmd, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 398 | }, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 399 | ), |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 400 | Rabt::Expect { |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 401 | size, |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 402 | ram, |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 403 | cores, |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 404 | done, |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 405 | session, |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 406 | } => expect( |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 407 | cfg, |
4e5ca2e20401 | atagen | 2026-09-28 11:42 UTC | 408 | agent_session(session), |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 409 | done, |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 410 | &run::Args { |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 411 | size, |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 412 | ram, |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 413 | cores, |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 414 | ..run::Args::default() |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 415 | }, |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 416 | ), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 417 | Rabt::Status { json } => status(json), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 418 | Rabt::Suggest { cmd } => suggest(cfg, &cmd), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 419 | Rabt::Kill { lease } => reply(ask(&Request::Kill { lease })), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 420 | Rabt::Extend { lease, by } => reply(ask(&Request::Extend { lease, by })), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 421 | Rabt::Watch { lease } => watch(lease), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 422 | Rabt::Artifacts { action } => artifacts(cfg, action), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 423 | Rabt::Hook { event } => hook::main(cfg, &event), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 424 | Rabt::Daemon => daemon::main(Config::load().unwrap_or_default()), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 425 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 426 | } |