#![cfg_attr(not(windows), allow(dead_code))]
use std::ffi::OsStr;
use std::path::{Path, PathBuf};
use super::binding::EnvCommand;
use super::bindings::{EnvFileDelivery, ENV_FILE_DELIVERIES};
use super::proxy_env_file::LOOPBACK_NO_PROXY;
pub const GUARD_ENV: &str = "OPENLATCH_CLI_SHIM";
pub const NOT_FOUND_EXIT: i32 = 9009;
pub const REFRESH_PATH: &str = "/admin/wiring/refresh";
pub const REFRESH_BUDGET: std::time::Duration = std::time::Duration::from_secs(5);
pub fn shim_command(exe: &Path) -> Option<&'static str> {
let stem = exe.file_stem()?.to_str()?;
command_owner(stem).map(|(_, cmd)| cmd.name)
}
pub(crate) fn command_owner(name: &str) -> Option<(&'static EnvFileDelivery, &'static EnvCommand)> {
ENV_FILE_DELIVERIES.iter().find_map(|d| {
d.commands
.iter()
.find(|c| c.name.eq_ignore_ascii_case(name))
.map(|c| (d, c))
})
}
fn same_dir(a: &Path, b: &Path) -> bool {
let norm = |p: &Path| {
p.display()
.to_string()
.replace('/', "\\")
.trim_end_matches('\\')
.to_lowercase()
};
norm(a) == norm(b)
}
pub fn resolve_real(name: &str, path: &OsStr, pathext: &str, skip: &[PathBuf]) -> Option<PathBuf> {
let exts: Vec<String> = pathext
.split(';')
.map(str::trim)
.filter(|e| e.starts_with('.') && e.len() > 1)
.map(str::to_string)
.chain(std::iter::once(".ps1".to_string()))
.collect();
std::env::split_paths(path)
.filter(|dir| !dir.as_os_str().is_empty() && !skip.iter().any(|s| same_dir(dir, s)))
.find_map(|dir| {
exts.iter()
.map(|ext| dir.join(format!("{name}{ext}")))
.find(|candidate| candidate.is_file())
})
}
fn guard_allows(cmd: &EnvCommand, real: &Path) -> bool {
match cmd.resolve_guard {
None => true,
Some(guard) => real
.display()
.to_string()
.to_lowercase()
.contains(&super::proxy_env_file::ps1_guard(guard).to_lowercase()),
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct Relay {
pub url: String,
pub ca_pem: PathBuf,
pub no_proxy: String,
}
fn append(prior: Option<String>, entry: &str) -> String {
match prior.filter(|p| !p.is_empty()) {
Some(p) => format!("{p},{entry}"),
None => entry.to_string(),
}
}
pub(crate) fn relay_env(
cmd: &EnvCommand,
defaults: &[(&str, &str)],
relay: Option<&Relay>,
current: &dyn Fn(&str) -> Option<String>,
) -> Vec<(String, String)> {
let mut out: Vec<(String, String)> = Vec::new();
for (name, value) in defaults {
if current(name).filter(|v| !v.is_empty()).is_none() {
out.push((name.to_string(), value.to_string()));
}
}
let mut no_proxy = [
("NO_PROXY", current("NO_PROXY")),
("no_proxy", current("no_proxy")),
];
if cmd.loopback_direct {
for (_, value) in &mut no_proxy {
*value = Some(append(value.take(), LOOPBACK_NO_PROXY));
}
}
if let Some(relay) = relay {
for name in ["HTTPS_PROXY", "HTTP_PROXY", "https_proxy", "http_proxy"] {
out.push((name.to_string(), relay.url.clone()));
}
out.push((
"NODE_EXTRA_CA_CERTS".to_string(),
relay.ca_pem.display().to_string(),
));
for (_, value) in &mut no_proxy {
*value = Some(append(value.take(), &relay.no_proxy));
}
}
if cmd.loopback_direct || relay.is_some() {
for (name, value) in no_proxy {
out.extend(value.map(|v| (name.to_string(), v)));
}
}
out
}
pub(crate) fn current_relay(agent: &str) -> Option<Relay> {
let url = super::proxy_env_file::live_proxy_url(agent)?;
let port: u16 = url.rsplit(':').next()?.parse().ok()?;
let ol = crate::config::openlatch_dir();
Some(Relay {
url,
ca_pem: crate::model_relay::ca::ca_pem_path(&crate::model_relay::ca::ca_dir(&ol)),
no_proxy: super::proxy_env_file::relay_no_proxy(port),
})
}
pub(crate) fn request_wiring(agent: &str) -> bool {
let Ok(cfg) = crate::config::Config::load(None, None, false) else {
return false;
};
let token_path = crate::config::openlatch_dir().join("daemon.token");
let Ok(token) = std::fs::read_to_string(token_path) else {
return false;
};
post_refresh(cfg.port, token.trim(), agent)
}
fn post_refresh(port: u16, token: &str, agent: &str) -> bool {
let Ok(client) = crate::egress::blocking_client_builder()
.connect_timeout(std::time::Duration::from_millis(500))
.timeout(REFRESH_BUDGET + std::time::Duration::from_secs(1))
.no_proxy()
.build()
else {
return false;
};
client
.post(format!("http://127.0.0.1:{port}{REFRESH_PATH}"))
.bearer_auth(token)
.json(&serde_json::json!({ "agent": agent }))
.send()
.ok()
.filter(|r| r.status().is_success())
.and_then(|r| r.json::<serde_json::Value>().ok())
.and_then(|v| v.get("wired").and_then(serde_json::Value::as_bool))
.unwrap_or(false)
}
fn relay_for(agent: &str) -> Option<Relay> {
if let Some(relay) = current_relay(agent) {
return Some(relay);
}
if super::proxy_env_file::has_block(agent) || !request_wiring(agent) {
return None;
}
current_relay(agent)
}
#[cfg(windows)]
pub fn run(name: &'static str) -> i32 {
let Some((delivery, cmd)) = command_owner(name) else {
return NOT_FOUND_EXIT;
};
let own_dir = std::env::current_exe()
.ok()
.and_then(|e| e.parent().map(Path::to_path_buf));
let mut skip: Vec<PathBuf> = own_dir.iter().cloned().collect();
if let Some(prior) = std::env::var_os(GUARD_ENV) {
skip.push(PathBuf::from(prior));
}
let path = std::env::var_os("PATH").unwrap_or_default();
let pathext = std::env::var("PATHEXT").unwrap_or_else(|_| ".COM;.EXE;.BAT;.CMD".to_string());
let Some(real) = resolve_real(name, &path, &pathext, &skip) else {
eprintln!(
"'{name}' is not recognized as an internal or external command,\n\
operable program or batch file."
);
return NOT_FOUND_EXIT;
};
let is_ps1 = real
.extension()
.is_some_and(|e| e.eq_ignore_ascii_case("ps1"));
let mut child = if is_ps1 {
let mut c = std::process::Command::new("powershell");
c.args(["-NoProfile", "-File"]).arg(&real);
c
} else {
std::process::Command::new(&real)
};
child.args(std::env::args_os().skip(1));
if let Some(dir) = &own_dir {
child.env(GUARD_ENV, dir);
}
if guard_allows(cmd, &real) {
let relay = relay_for(delivery.agent);
let current = |n: &str| std::env::var(n).ok();
child.envs(relay_env(cmd, delivery.defaults, relay.as_ref(), ¤t));
}
unsafe {
windows_sys::Win32::System::Console::SetConsoleCtrlHandler(None, 1);
}
match child.status() {
Ok(status) => status.code().unwrap_or(1),
Err(e) => {
eprintln!("{name}: {e}");
1
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn touch(path: &Path) {
std::fs::create_dir_all(path.parent().unwrap()).unwrap();
std::fs::write(path, b"").unwrap();
}
const PATHEXT: &str = ".com;.exe;.bat;.cmd";
#[test]
fn shim_names_map_to_their_command() {
assert_eq!(
shim_command(Path::new("ol/shims/Cursor-Agent.EXE")),
Some("cursor-agent")
);
assert_eq!(shim_command(Path::new("agent.exe")), Some("agent"));
assert_eq!(shim_command(Path::new("opencode.exe")), Some("opencode"));
assert_eq!(shim_command(Path::new("cline.exe")), Some("cline"));
assert_eq!(shim_command(Path::new("openlatch.exe")), None);
assert_eq!(shim_command(Path::new("openlatch")), None);
}
#[test]
fn each_command_names_its_agent() {
assert_eq!(command_owner("cursor-agent").unwrap().0.agent, "cursor");
assert_eq!(command_owner("agent").unwrap().0.agent, "cursor");
assert_eq!(command_owner("opencode").unwrap().0.agent, "opencode");
assert_eq!(command_owner("cline").unwrap().0.agent, "cline");
assert!(command_owner("git").is_none());
}
#[test]
fn resolution_follows_path_then_pathext_order() {
let tmp = tempfile::tempdir().unwrap();
let (a, b) = (tmp.path().join("a"), tmp.path().join("b"));
touch(&a.join("opencode.ps1"));
touch(&a.join("opencode.cmd"));
touch(&b.join("opencode.exe"));
let path = std::env::join_paths([&a, &b]).unwrap();
assert_eq!(
resolve_real("opencode", &path, PATHEXT, &[]),
Some(a.join("opencode.cmd")),
"the first directory wins, and .cmd before .ps1 within it"
);
std::fs::remove_file(a.join("opencode.cmd")).unwrap();
assert_eq!(
resolve_real("opencode", &path, PATHEXT, &[]),
Some(a.join("opencode.ps1"))
);
assert_eq!(resolve_real("missing", &path, PATHEXT, &[]), None);
}
#[test]
fn resolution_skips_the_shims_folder() {
let tmp = tempfile::tempdir().unwrap();
let (shims, real) = (tmp.path().join("shims"), tmp.path().join("real"));
touch(&shims.join("cline.exe"));
touch(&real.join("cline.cmd"));
let path = std::env::join_paths([&shims, &real]).unwrap();
assert_eq!(
resolve_real("cline", &path, PATHEXT, std::slice::from_ref(&shims)),
Some(real.join("cline.cmd"))
);
assert_eq!(
resolve_real("cline", &path, PATHEXT, &[shims, real]),
None,
"a guard-named second folder is skipped too"
);
}
#[test]
fn the_agent_guard_needs_cursors_own_install() {
let (_, agent) = command_owner("agent").unwrap();
assert!(guard_allows(
agent,
Path::new("C:\\Users\\d\\AppData\\Local\\cursor-agent\\agent.cmd")
));
assert!(!guard_allows(
agent,
Path::new("C:\\tools\\other\\agent.exe")
));
let (_, plain) = command_owner("cursor-agent").unwrap();
assert!(guard_allows(
plain,
Path::new("C:\\anywhere\\cursor-agent.exe")
));
}
fn relay() -> Relay {
Relay {
url: "http://127.0.0.1:7600".into(),
ca_pem: PathBuf::from("C:\\ol\\ca.pem"),
no_proxy: "127.0.0.1:7600".into(),
}
}
fn get<'a>(env: &'a [(String, String)], name: &str) -> Option<&'a str> {
env.iter().find(|(n, _)| n == name).map(|(_, v)| v.as_str())
}
#[test]
fn a_live_relay_sets_what_env_ps1_sets() {
let (d, cmd) = command_owner("cursor-agent").unwrap();
let env = relay_env(cmd, d.defaults, Some(&relay()), &|_| None);
for name in ["HTTPS_PROXY", "HTTP_PROXY", "https_proxy", "http_proxy"] {
assert_eq!(get(&env, name), Some("http://127.0.0.1:7600"));
}
assert_eq!(get(&env, "NODE_EXTRA_CA_CERTS"), Some("C:\\ol\\ca.pem"));
assert_eq!(get(&env, "NO_PROXY"), Some("127.0.0.1:7600"));
assert_eq!(get(&env, "no_proxy"), Some("127.0.0.1:7600"));
}
#[test]
fn no_proxy_is_merged_after_the_developers_value() {
let (d, cmd) = command_owner("cline").unwrap();
let current = |n: &str| (n == "NO_PROXY").then(|| "corp.local".to_string());
let env = relay_env(cmd, d.defaults, Some(&relay()), ¤t);
assert_eq!(get(&env, "NO_PROXY"), Some("corp.local,127.0.0.1:7600"));
}
#[test]
fn a_dead_relay_sets_only_the_defaults() {
let (d, cmd) = command_owner("cline").unwrap();
let env = relay_env(cmd, d.defaults, None, &|_| None);
assert_eq!(
env,
vec![(
"CLINE_SESSION_BACKEND_MODE".to_string(),
"local".to_string()
)]
);
let (d, cmd) = command_owner("cursor-agent").unwrap();
assert!(relay_env(cmd, d.defaults, None, &|_| None).is_empty());
}
#[test]
fn a_developer_default_wins() {
let (d, cmd) = command_owner("opencode").unwrap();
let current = |n: &str| (n == "OPENCODE_EXPERIMENTAL_WEBSOCKETS").then(|| "true".into());
let env = relay_env(cmd, d.defaults, None, ¤t);
assert_eq!(get(&env, "OPENCODE_EXPERIMENTAL_WEBSOCKETS"), None);
}
#[test]
fn opencode_exempts_loopback_before_the_relay_ports() {
let (d, cmd) = command_owner("opencode").unwrap();
let env = relay_env(cmd, d.defaults, None, &|_| None);
assert_eq!(get(&env, "OPENCODE_EXPERIMENTAL_WEBSOCKETS"), Some("false"));
assert_eq!(get(&env, "NO_PROXY"), Some(LOOPBACK_NO_PROXY));
let env = relay_env(cmd, d.defaults, Some(&relay()), &|_| None);
assert_eq!(
get(&env, "NO_PROXY"),
Some("localhost,127.0.0.1,127.0.0.1:7600")
);
}
#[test]
fn a_wiring_request_without_a_daemon_returns_promptly() {
let port = std::net::TcpListener::bind("127.0.0.1:0")
.unwrap()
.local_addr()
.unwrap()
.port(); let started = std::time::Instant::now();
assert!(!post_refresh(port, "token", "cursor"));
assert!(
started.elapsed() < REFRESH_BUDGET,
"did not wait out the bound"
);
}
}