mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-10-05 02:41:45 +00:00
SandboxSpec::Local and run reconnect now build a DriverSandbox over the sandbox-driver Host provider instead of fabro's own LocalSandbox, which is deleted. fabro_sandbox::local_sandbox designates the working directory (created when missing, never removed), creates the Host handle in a per-process registry, and learns the platform up front. A local sandbox reports no provider id: it is its directory, which the run record already carries, so reconnect rebuilds the handle over that directory rather than by id. The credential filter for explicit environment variables and the Bash readiness probe now come from the exec layer and the driver's activate helper. Test call sites move to the async constructor; test factories that must stay synchronous share the parent session's sandbox handle. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
83 lines
2.6 KiB
Rust
83 lines
2.6 KiB
Rust
use std::path::Path;
|
|
use std::sync::Arc;
|
|
|
|
use fabro_agent::{Sandbox, local_sandbox};
|
|
use fabro_auth::{CredentialSource, test_support};
|
|
use fabro_hooks::{
|
|
HookContext, HookDecision, HookDefinition, HookEvent, HookExecutionContext, HookRunner,
|
|
HookSettings, InterpString,
|
|
};
|
|
use fabro_model::Catalog;
|
|
use fabro_types::RunId;
|
|
use tokio::fs;
|
|
|
|
fn test_llm_source() -> Arc<dyn CredentialSource> {
|
|
test_support::vault_only_credential_source()
|
|
}
|
|
|
|
fn test_catalog() -> Arc<Catalog> {
|
|
Arc::new(Catalog::from_builtin().expect("default catalog should build"))
|
|
}
|
|
|
|
async fn test_sandbox() -> Arc<dyn Sandbox> {
|
|
Arc::new(
|
|
local_sandbox(std::env::current_dir().expect("test process should have a cwd"))
|
|
.await
|
|
.expect("local sandbox should be created"),
|
|
)
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn host_command_hook_uses_host_workdir_not_sandbox_workdir() {
|
|
let host_work_dir =
|
|
std::env::temp_dir().join(format!("fabro-host-hook-cwd-{}", std::process::id()));
|
|
let _ = fs::remove_dir_all(&host_work_dir).await;
|
|
fs::create_dir_all(&host_work_dir)
|
|
.await
|
|
.expect("test should create host hook cwd");
|
|
let container_only_work_dir = Path::new("/workspace/fabro-host-hook-repro-missing");
|
|
assert!(
|
|
!container_only_work_dir.exists(),
|
|
"reproduction requires a container-only cwd that does not exist on the host"
|
|
);
|
|
|
|
let runner = HookRunner::new(
|
|
HookSettings {
|
|
hooks: vec![HookDefinition {
|
|
name: Some("host-marker".to_string()),
|
|
event: HookEvent::RunStart,
|
|
command: Some(InterpString::parse("printf ran > marker.txt")),
|
|
hook_type: None,
|
|
matcher: None,
|
|
blocking: Some(true),
|
|
timeout_ms: Some(5000),
|
|
sandbox: Some(false),
|
|
}],
|
|
},
|
|
test_llm_source(),
|
|
test_catalog(),
|
|
);
|
|
let context = HookContext::new(
|
|
HookEvent::RunStart,
|
|
RunId::new(),
|
|
"host-hook-cwd".to_string(),
|
|
);
|
|
|
|
let decision = runner
|
|
.run(&context, test_sandbox().await, HookExecutionContext {
|
|
host_source_dir: Some(host_work_dir.clone()),
|
|
sandbox_work_dir: Some(container_only_work_dir.to_path_buf()),
|
|
})
|
|
.await;
|
|
|
|
assert_eq!(decision, HookDecision::Proceed);
|
|
assert_eq!(
|
|
fs::read_to_string(host_work_dir.join("marker.txt"))
|
|
.await
|
|
.expect("host hook should create marker file"),
|
|
"ran"
|
|
);
|
|
fs::remove_dir_all(&host_work_dir)
|
|
.await
|
|
.expect("test should clean up host hook cwd");
|
|
}
|