host: review fixes for cargo.build, the test runner, the console, and main; tests for every PROTOCOL.md rule
cargo.build and the runner (review T22R): builds use the network again; core.unlock and core.notify in order under the runner; quiet test output; manifest preparation keeps [workspace], target tables, renamed and optional entries; cargo.rpc.discover; -32004 with data.output; retention of 10 builds; light, empty test worlds with an in-memory journal; only CARGO_/RUST_/SAUSAGE_ variables; stdin is null; the runner through --config. A compile cache that cannot be created is not fatal. Activation replaces plugin.wasm without a gap. Console and main: init ships the workspace Cargo.lock and creates the world atomically; each console command runs in its own task, so ps, stop, and kill work during a slow call; SAUSAGE_HEADLESS; the terminal mode is restored; signals during boot; chat text is not taken as a command. Tests: 20 more tests for PROTOCOL.md sections 2 to 10; the cargo tests choose the libtest binary from cargo's JSON output and time out.
This commit is contained in:
+61
-11
@@ -14,7 +14,7 @@ mod test_runner;
|
||||
#[derive(Parser)]
|
||||
#[command(version)]
|
||||
struct Cli {
|
||||
#[arg(long, global = true)]
|
||||
#[arg(long, global = true, env = "SAUSAGE_HEADLESS", value_parser = headless)]
|
||||
headless: bool,
|
||||
#[arg(long, global = true)]
|
||||
cache_dir: Option<PathBuf>,
|
||||
@@ -38,6 +38,28 @@ enum Cmd {
|
||||
args: Vec<String>,
|
||||
},
|
||||
}
|
||||
fn headless(value: &str) -> std::result::Result<bool, String> {
|
||||
match value.to_ascii_lowercase().as_str() {
|
||||
"1" | "true" => Ok(true),
|
||||
"" | "0" | "false" => Ok(false),
|
||||
_ => Err("expected 1, true, 0, false, or an empty value".into()),
|
||||
}
|
||||
}
|
||||
|
||||
// Readline can still wait on its detached thread when the host exits.
|
||||
struct TerminalMode(Option<nix::sys::termios::Termios>);
|
||||
impl Drop for TerminalMode {
|
||||
fn drop(&mut self) {
|
||||
if let Some(mode) = &self.0 {
|
||||
let _ = nix::sys::termios::tcsetattr(
|
||||
std::io::stdin(),
|
||||
nix::sys::termios::SetArg::TCSANOW,
|
||||
mode,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn setting(var: &str, question: &str, default: Option<&str>) -> Result<String> {
|
||||
if let Ok(v) = std::env::var(var)
|
||||
&& !v.is_empty()
|
||||
@@ -65,7 +87,18 @@ async fn init(dir: &Path) -> Result<()> {
|
||||
let base_url = setting("SAUSAGE_BASE_URL", "Base URL", None)?;
|
||||
let model = setting("SAUSAGE_MODEL", "Model", None)?;
|
||||
let token = setting("SAUSAGE_API_TOKEN", "API token", None)?;
|
||||
let world = World::create(dir)?;
|
||||
if dir.exists() {
|
||||
bail!("world already exists: {}", dir.display());
|
||||
}
|
||||
let parent = dir
|
||||
.parent()
|
||||
.filter(|p| !p.as_os_str().is_empty())
|
||||
.unwrap_or(Path::new("."));
|
||||
std::fs::create_dir_all(parent)?;
|
||||
let staging = tempfile::Builder::new()
|
||||
.prefix(".sausage-init-")
|
||||
.tempdir_in(parent)?;
|
||||
let world = World::create(&staging.path().join("world"))?;
|
||||
std::fs::write(
|
||||
world.root.join("config/agent.json"),
|
||||
serde_json::to_vec_pretty(
|
||||
@@ -80,15 +113,21 @@ async fn init(dir: &Path) -> Result<()> {
|
||||
source.join("Cargo.toml"),
|
||||
include_str!("../../agent/Cargo.toml"),
|
||||
)?;
|
||||
std::fs::write(
|
||||
source.join("Cargo.lock"),
|
||||
include_bytes!("../../Cargo.lock"),
|
||||
)?;
|
||||
println!("The first agent build takes a few minutes.");
|
||||
let output = builder.build(&source).await?;
|
||||
std::fs::copy(output.wasm, world.plugin_dir("agent").join("plugin.wasm"))?;
|
||||
std::fs::rename(&world.root, dir)?;
|
||||
println!("Start the world with: sausage run {}", dir.display());
|
||||
Ok(())
|
||||
}
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
async fn main() -> Result<std::process::ExitCode> {
|
||||
let cli = Cli::parse();
|
||||
let _terminal = TerminalMode(nix::sys::termios::tcgetattr(std::io::stdin()).ok());
|
||||
let command = match cli.command {
|
||||
Some(command) => command,
|
||||
None => {
|
||||
@@ -102,16 +141,25 @@ async fn main() -> Result<()> {
|
||||
match command {
|
||||
Cmd::TestRunner { wasm, args } => {
|
||||
if !test_runner::run(&wasm, args).await? {
|
||||
std::process::exit(1);
|
||||
return Ok(std::process::ExitCode::FAILURE);
|
||||
}
|
||||
}
|
||||
Cmd::Init { dir } => init(&dir).await?,
|
||||
Cmd::Run { dir } => {
|
||||
let mut term =
|
||||
tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate())?;
|
||||
let host = match cli.cache_dir {
|
||||
Some(cache) => Host::start_with_cache(&dir, &cache).await?,
|
||||
None => Host::start(&dir).await?,
|
||||
let mut interrupt =
|
||||
tokio::signal::unix::signal(tokio::signal::unix::SignalKind::interrupt())?;
|
||||
let start = async {
|
||||
match cli.cache_dir {
|
||||
Some(cache) => Host::start_with_cache(&dir, &cache).await,
|
||||
None => Host::start(&dir).await,
|
||||
}
|
||||
};
|
||||
let host = tokio::select! {
|
||||
result=start=>result?,
|
||||
_=term.recv()=>return Ok(std::process::ExitCode::SUCCESS),
|
||||
_=interrupt.recv()=>return Ok(std::process::ExitCode::SUCCESS),
|
||||
};
|
||||
let mut notifications = host.subscribe();
|
||||
let notification_task = cli.headless.then(|| {
|
||||
@@ -126,12 +174,13 @@ async fn main() -> Result<()> {
|
||||
})
|
||||
});
|
||||
let console = sausage::console::run_input(host.clone());
|
||||
let mut console_result = Ok(());
|
||||
let exit = tokio::select! {
|
||||
_=host.exit_requested()=>true,
|
||||
_=term.recv()=>false,
|
||||
_=tokio::signal::ctrl_c()=>false,
|
||||
_=interrupt.recv()=>false,
|
||||
result = console, if !cli.headless => {
|
||||
result?;
|
||||
console_result=result;
|
||||
false
|
||||
},
|
||||
};
|
||||
@@ -141,9 +190,10 @@ async fn main() -> Result<()> {
|
||||
task.abort();
|
||||
}
|
||||
if exit {
|
||||
std::process::exit(75);
|
||||
return Ok(std::process::ExitCode::from(75));
|
||||
}
|
||||
console_result?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
Ok(std::process::ExitCode::SUCCESS)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user