cargo.build, the WASI test runner, the console, and core.notify ordering

- cargo.build (native): one source copy, tests under WASI, release
  build to /state/cargo/builds/<n>/, process groups killed on cancel.
  The host writes the SDK copy to /plugins/cargo/sdk/ at start.
- sausage test-runner: one instance per test, in parallel, with its own
  mount table (empty world, package folder at its path); answers
  core.lock, core.unlock, core.notify, core.log.
- The console: rustyline, notifications while typing, ls/cat through the
  process view, methods, ps, log, activate, revert, stop, kill.
- core.notify on stdout is handled in order, before the response.
This commit is contained in:
2026-10-23 01:42:19 +01:00
parent 0516158883
commit 8d058666dc
23 changed files with 1786 additions and 296 deletions
+27 -7
View File
@@ -4,6 +4,7 @@ use clap::{Parser, Subcommand};
use sausage::{Host, world::World};
use serde_json::json;
use std::{io::Write, path::PathBuf};
mod test_runner;
#[derive(Parser)]
#[command(version)]
struct Cli {
@@ -16,8 +17,18 @@ struct Cli {
}
#[derive(Subcommand)]
enum Cmd {
Init { dir: PathBuf },
Run { dir: PathBuf },
Init {
dir: PathBuf,
},
Run {
dir: PathBuf,
},
/// Run a WASI libtest component in isolated instances.
TestRunner {
wasm: PathBuf,
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
args: Vec<String>,
},
}
fn setting(var: &str, question: &str, default: Option<&str>) -> Result<String> {
if let Ok(v) = std::env::var(var)
@@ -41,6 +52,11 @@ fn setting(var: &str, question: &str, default: Option<&str>) -> Result<String> {
async fn main() -> Result<()> {
let cli = Cli::parse();
match cli.command {
Cmd::TestRunner { wasm, args } => {
if !test_runner::run(&wasm, args).await? {
std::process::exit(1);
}
}
Cmd::Init { dir } => {
let api = setting("SAUSAGE_API", "API shape", Some("openai-responses"))?;
if api != "openai-responses" && api != "anthropic-messages" {
@@ -65,10 +81,12 @@ async fn main() -> Result<()> {
None => Host::start(&dir).await?,
};
let mut notifications = host.subscribe();
let notification_task = tokio::spawn(async move {
while let Ok(notification) = notifications.recv().await {
println!("{notification}");
}
let notification_task = cli.headless.then(|| {
tokio::spawn(async move {
while let Ok(notification) = notifications.recv().await {
println!("{notification}");
}
})
});
let console = sausage::console::run_input(host.clone());
let exit = tokio::select! {
@@ -82,7 +100,9 @@ async fn main() -> Result<()> {
};
let exit = exit || host.restart_requested();
host.stop().await;
notification_task.abort();
if let Some(task) = notification_task {
task.abort();
}
if exit {
std::process::exit(75);
}