pub mod amp;
pub mod antigravity;
pub mod campfire;
pub mod chatgpt;
pub mod claude_chat;
pub mod claude_code;
pub mod codex;
pub mod cowork;
pub mod cursor;
pub mod cursor_desktop;
pub mod fx;
pub mod grok;
pub mod hermes;
pub mod opencode;
pub mod pi;
pub mod simple;
pub(crate) mod jsonl;
pub(crate) fn filter_map_parallel<T, U>(items: &[T], f: impl Fn(&T) -> Option<U> + Sync) -> Vec<U>
where
T: Sync,
U: Send,
{
#[cfg(target_arch = "wasm32")]
{
items.iter().filter_map(f).collect()
}
#[cfg(not(target_arch = "wasm32"))]
{
let workers = std::thread::available_parallelism()
.map_or(1, std::num::NonZero::get)
.min(items.len());
if workers <= 1 || items.len() < 8 {
return items.iter().filter_map(f).collect();
}
let chunk = items.len().div_ceil(workers);
let f = &f;
std::thread::scope(|scope| {
let handles: Vec<_> = items
.chunks(chunk)
.map(|part| scope.spawn(move || part.iter().filter_map(f).collect::<Vec<U>>()))
.collect();
handles
.into_iter()
.flat_map(|h| h.join().unwrap_or_default())
.collect()
})
}
}
pub(crate) fn checked_id_component(harness: &'static str, id: &str) -> crate::Result<()> {
let ok = !id.is_empty()
&& id != "."
&& id != ".."
&& !id.contains(['/', '\\', ':'])
&& !id.chars().any(char::is_control);
if ok {
Ok(())
} else {
Err(crate::Error::Malformed {
harness,
detail: format!(
"session id `{}` is not usable as a file name",
id.escape_debug()
),
})
}
}
pub(crate) fn home_dir() -> Option<std::path::PathBuf> {
#[cfg(windows)]
let home = std::env::var_os("USERPROFILE")
.filter(|v| !v.is_empty())
.or_else(|| std::env::var_os("HOME"));
#[cfg(not(windows))]
let home = std::env::var_os("HOME");
home.filter(|v| !v.is_empty()).map(std::path::PathBuf::from)
}