fn main() { // INTERIM: both llama-cpp-sys-2 and whisper-rs-sys statically link // their own copy of ggml, so GNU ld / lld see duplicate symbols when // linking the kon binary and its lib-tests. --allow-multiple-definition // makes the linker pick the first definition; safe while both crates // pin compatible ggml revisions. Replace with a system-ggml shared-lib // setup as a follow-up. if std::env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("linux") { println!("cargo:rustc-link-arg=-Wl,--allow-multiple-definition"); } assert_localhost_llm_csp(); tauri_build::build() } /// Regression guard for brief item #2 (pre-emptive localhost LLM scope). /// /// Kon's bundled llama.cpp server and any BYO Ollama install speak HTTP /// on `127.0.0.1:*`. If the `connect-src` CSP ever drops those entries, /// `fetch()` from the webview to the local LLM silently 404s with an /// opaque scope error (Vibe #438 / #487). We keep the current permit /// pinned at build time so a stray edit can't regress it unnoticed. /// /// Parses `tauri.conf.json` properly and inspects the `connect-src` /// directive of the CSP, rather than substring-searching the whole /// file — the latter would both false-pass on unrelated values /// containing a localhost URL and false-fail on JSON-escaped forward /// slashes. fn assert_localhost_llm_csp() { let conf_path = std::path::Path::new("tauri.conf.json"); println!("cargo:rerun-if-changed=tauri.conf.json"); let raw = std::fs::read_to_string(conf_path) .expect("build.rs: failed to read tauri.conf.json for CSP regression guard"); let conf: serde_json::Value = serde_json::from_str(&raw).expect("build.rs: tauri.conf.json is not valid JSON"); let csp = conf .pointer("/app/security/csp") .and_then(|v| v.as_str()) .expect("build.rs: tauri.conf.json missing app.security.csp string"); // Split the CSP into directives (`default-src 'self'; script-src ...`) // and find the one whose directive NAME is exactly "connect-src". // A prefix match would also accept hypothetical future directives // like "connect-src-elem" (2026-04-22 review MINOR). let connect_src = csp .split(';') .map(str::trim) .find_map(|directive| { let (name, rest) = directive.split_once(char::is_whitespace)?; if name == "connect-src" { Some(rest.trim()) } else { None } }) .expect( "build.rs: tauri.conf.json CSP missing connect-src directive — \ local LLM fetches will be blocked (brief item #2)", ); let tokens: Vec<&str> = connect_src.split_whitespace().collect(); for required in ["http://127.0.0.1:*", "ws://127.0.0.1:*"] { assert!( tokens.contains(&required), "build.rs: tauri.conf.json CSP connect-src must permit {required} \ for local LLM connectivity (brief item #2). Current connect-src: \ {connect_src:?}" ); } }