pub(crate) mod adopt;
pub mod catalog;
pub mod expiry;
pub mod facts;
pub mod human;
pub(crate) mod instruction_region;
pub mod probe;
pub(crate) mod software;
pub mod surfaces;
pub mod wire;
pub use catalog::{Catalog, Setup};
pub use facts::{
BACKUP_SLOTS, BUNDLE_FORMAT, Foreign, Harness, LaunchBinding, PreservationSurface, Scoped,
Shadow,
};
pub use crate::setup_core::software::{Artifact, Delivery, Previous, Shape, Software};
#[must_use]
pub fn digest_of_bytes(bytes: &str) -> String {
crate::setup_core::digest::of_bytes(bytes.as_bytes())
}
pub use wire::dispatch;
use std::process::ExitCode;
#[must_use]
pub fn run(harness: &Harness, arguments: Vec<String>) -> ExitCode {
match arguments.first().map(String::as_str) {
Some("--version") => {
println!("{} {}", harness.provider_id, harness.version);
return ExitCode::SUCCESS;
}
Some("--help") | None => {
print_help(harness);
return ExitCode::SUCCESS;
}
_ => {}
}
let first = arguments.first().map(String::as_str).unwrap_or_default();
let human_status = first == "status" && !arguments.iter().any(|a| a == "--json");
if human::is_human_command(first) || human_status {
return match human::parse(arguments).and_then(|command| human::run(harness, command)) {
Ok(()) => ExitCode::SUCCESS,
Err(error) => {
eprintln!("{}: {error}", harness.provider_id);
ExitCode::FAILURE
}
};
}
if let Some(command) = crate::provider_v3::vocabulary::Command::parse(first) {
let mine = arguments
.iter()
.take_while(|token| *token != "--")
.any(|token| token == "--help");
if mine {
print!("{}", crate::provider_v3::argv::render_usage(command));
return ExitCode::SUCCESS;
}
}
let invocation = match crate::provider_v3::argv::parse(arguments) {
Ok(invocation) => invocation,
Err(error) => {
eprintln!("{}: {error}", harness.provider_id);
return ExitCode::FAILURE;
}
};
match wire::dispatch(harness, invocation) {
Ok(answer) => {
println!("{answer}");
ExitCode::SUCCESS
}
Err(error) => {
if let Some(reason) = error.reason() {
println!(
"{}",
serde_json::json!({
"state": "refused",
"rejected": true,
"reason": reason.as_str(),
"detail": error.detail(),
})
);
ExitCode::SUCCESS
} else {
eprintln!("{}: {error}", harness.provider_id);
ExitCode::FAILURE
}
}
}
}
fn print_help(harness: &Harness) {
println!("{} {}", harness.provider_id, harness.version);
println!();
println!(
"Configures {} ({}) in a caller-named target directory.",
harness.product, harness.vendor
);
if harness.config_home_env.is_empty() {
println!(
"Documented configuration home: {} (no environment override documented)",
harness.documented_config_home
);
} else {
println!(
"Documented configuration home: {} ({})",
harness.documented_config_home, harness.config_home_env
);
}
if !harness.config_home_note.is_empty() {
println!("Also honoured: {}", harness.config_home_note);
}
println!();
println!("Provider commands (ai-stp protocol v3):");
println!(" provider-info");
println!(" status --target <dir> --json");
println!(" validate-bundle --target <dir> --json --bundle <path> ...");
println!(" plan-operation --target <dir> --json --operation <op> ...");
println!(" apply-operation --target <dir> --json --plan <path> --plan-digest <d> ...");
println!(" recover-operation --target <dir> --json");
if harness.can_launch() {
println!(" launch --target <dir> --prefix <dir> --json [-- <args>]");
}
println!();
if harness
.operations()
.contains(&crate::provider_v3::Operation::SoftwareInstall)
{
println!(
"This build also installs {} itself. Those operations take a",
harness.product
);
println!("`--prefix` for the program, distinct from the `--target` that holds");
println!("its configuration, and the bytes are fetched between planning and");
println!("applying by whoever holds the network:");
println!();
println!(
" plan-operation --operation software_install --target <dir> --prefix <dir> ..."
);
println!(" apply-operation --prefix <dir> --software-artifact <file> ...");
println!();
}
println!("Your commands:");
println!(" list");
println!(" status --target <dir>");
println!(" install <setup> --target <dir>");
println!(" select <setup> --target <dir>");
println!(" reinstall --target <dir>");
println!(" diff --target <dir>");
println!(" backups --target <dir>");
println!(" restore [--backup <ref>] --target <dir>");
println!(" hold --backup <ref> [--reason <why>] --target <dir>");
println!(" release --backup <ref> --target <dir>");
println!(" remove --target <dir>");
if !harness.predecessor_state_file.is_empty() {
println!(" adopt --target <dir>");
}
if harness.installs_a_program() {
println!(" software --prefix <dir>");
println!(" rollback --to <version> --prefix <dir>");
}
println!();
if harness.installs_a_program() {
println!("`software` reads the program directory and `rollback` points the");
println!("command back at a version already in it -- installing a new one leaves");
println!("the old tree in place and moves only the command. Both need no network,");
println!("which is why they are commands you type; install and update need bytes");
println!("fetched between planning and applying, so they stay on the wire above.");
println!();
}
if !harness.predecessor_state_file.is_empty() {
println!(
"`adopt` takes over a target still carrying {},",
harness.predecessor_state_file
);
println!("written by the estate that came before this one. It is a command you");
println!("type, never something install does behind you, and it deletes nothing.");
println!();
}
println!("Every one takes an explicit --target, and the two program commands an");
println!("explicit --prefix. There is no default: a change aimed at a guessed");
println!("path is a change aimed at someone else's state. `rollback` names its");
println!("version for the same reason -- there is no record of which was");
println!("previous, only what is on disk.");
println!();
println!("A backup is captured before every change, so `restore` always has");
println!("something to return to. The pool rolls, so a long series of changes");
println!("eventually evicts the oldest: `hold` keeps one until `release` lets");
println!("it go, which is how a baseline survives more captures than the pool.");
println!("Over the wire, install and replace arrive as a bundle and refuse --");
println!("this build reads setups from its own catalog.");
}
#[cfg(test)]
mod tests {
#![allow(
clippy::unwrap_used,
clippy::expect_used,
clippy::panic,
clippy::disallowed_types,
reason = "tests drive real executables to check the shipped behaviour"
)]
#[test]
fn every_tool_a_document_names_exists() {
let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../..");
let generated = "CHANGELOG.md";
let mut prose = String::new();
let mut read_all = |from: &std::path::Path| {
if let Ok(entries) = std::fs::read_dir(from) {
for entry in entries.flatten() {
let path = entry.path();
if path.extension().is_some_and(|e| e == "md")
&& path.file_name().is_some_and(|name| name != generated)
&& let Ok(text) = std::fs::read_to_string(&path)
{
prose.push_str(&text);
prose.push('\n');
}
}
}
};
read_all(&root.join("docs"));
read_all(&root);
let mut missing = Vec::new();
for word in prose.split(|c: char| !(c.is_alphanumeric() || "._/-".contains(c))) {
let named = word.trim_matches(|c| c == '.' || c == '-');
if !(named.starts_with("scripts/") || named.starts_with("tools/")) {
continue;
}
let runnable = std::path::Path::new(named)
.extension()
.and_then(std::ffi::OsStr::to_str)
.is_some_and(|extension| {
extension.eq_ignore_ascii_case("py") || extension.eq_ignore_ascii_case("sh")
});
if !runnable {
continue;
}
if !root.join(named).is_file() && !missing.contains(&named.to_owned()) {
missing.push(named.to_owned());
}
}
assert!(
missing.is_empty(),
"these are named in this repository's prose and do not exist: {}",
missing.join(", ")
);
}
}