use std::path::{Path, PathBuf};
use std::sync::Arc;
use car_engine::{LocalSubstrate, Substrate};
use car_policy::permission::PermissionTier;
use car_sandbox::{preflight, SandboxPolicy};
pub const DEFAULT_ASSISTANT_IMAGE: &str = "python:3.11";
pub struct BoundEnvironment {
pub substrate: Arc<dyn Substrate>,
pub root: PathBuf,
pub tier: PermissionTier,
pub description: String,
pub sandboxed: bool,
pub fallback_notice: Option<String>,
}
pub async fn bind_default_substrate(
prefer_local: bool,
full_access: bool,
workdir: &Path,
image: Option<&str>,
) -> BoundEnvironment {
if !prefer_local {
let policy =
SandboxPolicy::default().with_image(image.unwrap_or(DEFAULT_ASSISTANT_IMAGE));
let pf = preflight(&policy.image).await;
if pf.is_ok() {
let executor = Arc::new(policy.build_executor(workdir));
let substrate: Arc<dyn Substrate> = executor;
return BoundEnvironment {
substrate,
root: workdir.to_path_buf(),
tier: if full_access {
PermissionTier::FullAccess
} else {
PermissionTier::SandboxEdit
},
description: format!(
"an isolated Docker sandbox (image {}, no network) mounted at {}. \
Files and shell run inside the container; web tools run from the host.",
policy.image,
workdir.display()
),
sandboxed: true,
fallback_notice: None,
};
}
return BoundEnvironment {
substrate: Arc::new(LocalSubstrate::new()),
root: workdir.to_path_buf(),
tier: if full_access {
PermissionTier::FullAccess
} else {
PermissionTier::ReadOnly
},
description: format!(
"the LOCAL host filesystem and shell at {} (sandbox unavailable). \
Writes and shell require approval.",
workdir.display()
),
sandboxed: false,
fallback_notice: Some(pf.message()),
};
}
BoundEnvironment {
substrate: Arc::new(LocalSubstrate::new()),
root: workdir.to_path_buf(),
tier: if full_access {
PermissionTier::FullAccess
} else {
PermissionTier::ReadOnly
},
description: format!(
"the LOCAL host filesystem and shell at {}.{}",
workdir.display(),
if full_access {
" Full access granted."
} else {
" Writes and shell require approval."
}
),
sandboxed: false,
fallback_notice: None,
}
}