use std::path::{Path, PathBuf};
fn ui_dir(manifest: &Path) -> PathBuf {
let packaged = manifest.join("ui-dist");
if packaged.join("index.html").is_file() {
packaged
} else {
manifest.join("../../ui/dist")
}
}
fn has_npm() -> bool {
["npm", "npm.cmd"].iter().any(|c| {
std::process::Command::new(c)
.arg("--version")
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
.status()
.is_ok_and(|s| s.success())
})
}
fn main() {
if std::env::var_os("CARGO_FEATURE_UI").is_none() {
return;
}
let manifest = Path::new(env!("CARGO_MANIFEST_DIR"));
let ui = ui_dir(manifest);
println!("cargo:rustc-env=CYBERBRAIN_UI_DIR={}", ui.display());
println!("cargo:rerun-if-changed={}", ui.display());
println!(
"cargo:rerun-if-changed={}",
manifest.join("ui-dist").display()
);
println!("cargo:rerun-if-env-changed=CYBERBRAIN_ALLOW_MOCK_UI");
if std::env::var("CYBERBRAIN_ALLOW_MOCK_UI").is_ok_and(|v| v == "1") {
println!("cargo:warning=embedding a MOCK web UI on purpose; do not ship this binary");
return;
}
let release = std::env::var("PROFILE").as_deref() == Ok("release");
if !ui.join("index.html").is_file() {
if release {
let advice = if has_npm() {
"Build it first: cd ui && npm ci && npm run build"
} else {
"No npm on this machine. Either install a node toolchain and run\n\
`cd ui && npm ci && npm run build`, or build without the page:\n\
`cargo build --release --no-default-features` — the CLI, the hooks, the MCP\n\
server and the HTTP API are unaffected, and `serve` answers the API while\n\
saying the page was not built in.\n\
In a published crate this means the page was not copied to\n\
crates/cyberbrain/ui-dist before cargo publish"
};
panic!(
"{} is missing or empty; a release binary would embed no web UI at all.\n{}",
ui.display(),
advice
);
}
println!("cargo:warning=ui/dist is missing; `cyberbrain serve` will have no UI");
return;
}
match std::fs::read_to_string(ui.join("transport.txt")) {
Ok(t) if t.trim() == "http" => {}
Ok(t) if release => panic!(
"ui/dist was built with transport {:?}: it renders fabricated data.\n\
Rebuild it: cd ui && npm run build",
t.trim()
),
Ok(t) => println!(
"cargo:warning=ui/dist transport is {:?}, not http",
t.trim()
),
Err(_) if release => panic!(
"ui/dist has no transport.txt, so there is no way to tell whether it renders \
real data or mock data.\nRebuild it: cd ui && npm run build"
),
Err(_) => println!("cargo:warning=ui/dist has no transport.txt; rebuild it"),
}
}