|
@@ -34,6 +34,10 @@ use runtime::{
|
|
|
parse_oauth_callback_request_target, save_oauth_credentials, ApiClient, ApiRequest,
|
|
parse_oauth_callback_request_target, save_oauth_credentials, ApiClient, ApiRequest,
|
|
|
AssistantEvent, CompactionConfig, ConfigLoader, ConfigSource, ContentBlock,
|
|
AssistantEvent, CompactionConfig, ConfigLoader, ConfigSource, ContentBlock,
|
|
|
ConversationMessage, ConversationRuntime, MessageRole, OAuthAuthorizationRequest, OAuthConfig,
|
|
ConversationMessage, ConversationRuntime, MessageRole, OAuthAuthorizationRequest, OAuthConfig,
|
|
|
|
|
+ parse_oauth_callback_request_target, resolve_sandbox_status, save_oauth_credentials,
|
|
|
|
|
+ ApiClient, ApiRequest, AssistantEvent, CompactionConfig, ConfigLoader, ConfigSource,
|
|
|
|
|
+ ContentBlock, ConversationMessage, ConversationRuntime, MessageRole,
|
|
|
|
|
+ OAuthAuthorizationRequest, OAuthConfig,
|
|
|
OAuthTokenExchangeRequest, PermissionMode, PermissionPolicy, ProjectContext, RuntimeError,
|
|
OAuthTokenExchangeRequest, PermissionMode, PermissionPolicy, ProjectContext, RuntimeError,
|
|
|
Session, TokenUsage, ToolError, ToolExecutor, UsageTracker,
|
|
Session, TokenUsage, ToolError, ToolExecutor, UsageTracker,
|
|
|
};
|
|
};
|
|
@@ -656,6 +660,7 @@ struct StatusContext {
|
|
|
memory_file_count: usize,
|
|
memory_file_count: usize,
|
|
|
project_root: Option<PathBuf>,
|
|
project_root: Option<PathBuf>,
|
|
|
git_branch: Option<String>,
|
|
git_branch: Option<String>,
|
|
|
|
|
+ sandbox_status: runtime::SandboxStatus,
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
#[derive(Debug, Clone, Copy)]
|
|
@@ -928,6 +933,18 @@ fn run_resume_command(
|
|
|
)),
|
|
)),
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
|
|
+ SlashCommand::Sandbox => {
|
|
|
|
|
+ let cwd = env::current_dir()?;
|
|
|
|
|
+ let loader = ConfigLoader::default_for(&cwd);
|
|
|
|
|
+ let runtime_config = loader.load()?;
|
|
|
|
|
+ Ok(ResumeCommandOutcome {
|
|
|
|
|
+ session: session.clone(),
|
|
|
|
|
+ message: Some(format_sandbox_report(&resolve_sandbox_status(
|
|
|
|
|
+ runtime_config.sandbox(),
|
|
|
|
|
+ &cwd,
|
|
|
|
|
+ ))),
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
SlashCommand::Cost => {
|
|
SlashCommand::Cost => {
|
|
|
let usage = UsageTracker::from_session(session).cumulative_usage();
|
|
let usage = UsageTracker::from_session(session).cumulative_usage();
|
|
|
Ok(ResumeCommandOutcome {
|
|
Ok(ResumeCommandOutcome {
|
|
@@ -1237,6 +1254,10 @@ impl LiveCli {
|
|
|
self.run_debug_tool_call()?;
|
|
self.run_debug_tool_call()?;
|
|
|
false
|
|
false
|
|
|
}
|
|
}
|
|
|
|
|
+ SlashCommand::Sandbox => {
|
|
|
|
|
+ Self::print_sandbox_status();
|
|
|
|
|
+ false
|
|
|
|
|
+ }
|
|
|
SlashCommand::Compact => {
|
|
SlashCommand::Compact => {
|
|
|
self.compact()?;
|
|
self.compact()?;
|
|
|
false
|
|
false
|
|
@@ -1319,6 +1340,18 @@ impl LiveCli {
|
|
|
);
|
|
);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ fn print_sandbox_status() {
|
|
|
|
|
+ let cwd = env::current_dir().expect("current dir");
|
|
|
|
|
+ let loader = ConfigLoader::default_for(&cwd);
|
|
|
|
|
+ let runtime_config = loader
|
|
|
|
|
+ .load()
|
|
|
|
|
+ .unwrap_or_else(|_| runtime::RuntimeConfig::empty());
|
|
|
|
|
+ println!(
|
|
|
|
|
+ "{}",
|
|
|
|
|
+ format_sandbox_report(&resolve_sandbox_status(runtime_config.sandbox(), &cwd))
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
fn set_model(&mut self, model: Option<String>) -> Result<bool, Box<dyn std::error::Error>> {
|
|
fn set_model(&mut self, model: Option<String>) -> Result<bool, Box<dyn std::error::Error>> {
|
|
|
let Some(model) = model else {
|
|
let Some(model) = model else {
|
|
|
println!(
|
|
println!(
|
|
@@ -1922,6 +1955,7 @@ fn status_context(
|
|
|
let project_context = ProjectContext::discover_with_git(&cwd, DEFAULT_DATE)?;
|
|
let project_context = ProjectContext::discover_with_git(&cwd, DEFAULT_DATE)?;
|
|
|
let (project_root, git_branch) =
|
|
let (project_root, git_branch) =
|
|
|
parse_git_status_metadata(project_context.git_status.as_deref());
|
|
parse_git_status_metadata(project_context.git_status.as_deref());
|
|
|
|
|
+ let sandbox_status = resolve_sandbox_status(runtime_config.sandbox(), &cwd);
|
|
|
Ok(StatusContext {
|
|
Ok(StatusContext {
|
|
|
cwd,
|
|
cwd,
|
|
|
session_path: session_path.map(Path::to_path_buf),
|
|
session_path: session_path.map(Path::to_path_buf),
|
|
@@ -1930,6 +1964,7 @@ fn status_context(
|
|
|
memory_file_count: project_context.instruction_files.len(),
|
|
memory_file_count: project_context.instruction_files.len(),
|
|
|
project_root,
|
|
project_root,
|
|
|
git_branch,
|
|
git_branch,
|
|
|
|
|
+ sandbox_status,
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -1982,6 +2017,7 @@ fn format_status_report(
|
|
|
context.discovered_config_files,
|
|
context.discovered_config_files,
|
|
|
context.memory_file_count,
|
|
context.memory_file_count,
|
|
|
),
|
|
),
|
|
|
|
|
+ format_sandbox_report(&context.sandbox_status),
|
|
|
]
|
|
]
|
|
|
.join(
|
|
.join(
|
|
|
"
|
|
"
|
|
@@ -1990,6 +2026,49 @@ fn format_status_report(
|
|
|
)
|
|
)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+fn format_sandbox_report(status: &runtime::SandboxStatus) -> String {
|
|
|
|
|
+ format!(
|
|
|
|
|
+ "Sandbox
|
|
|
|
|
+ Enabled {}
|
|
|
|
|
+ Active {}
|
|
|
|
|
+ Supported {}
|
|
|
|
|
+ In container {}
|
|
|
|
|
+ Requested ns {}
|
|
|
|
|
+ Active ns {}
|
|
|
|
|
+ Requested net {}
|
|
|
|
|
+ Active net {}
|
|
|
|
|
+ Filesystem mode {}
|
|
|
|
|
+ Filesystem active {}
|
|
|
|
|
+ Allowed mounts {}
|
|
|
|
|
+ Markers {}
|
|
|
|
|
+ Fallback reason {}",
|
|
|
|
|
+ status.enabled,
|
|
|
|
|
+ status.active,
|
|
|
|
|
+ status.supported,
|
|
|
|
|
+ status.in_container,
|
|
|
|
|
+ status.requested.namespace_restrictions,
|
|
|
|
|
+ status.namespace_active,
|
|
|
|
|
+ status.requested.network_isolation,
|
|
|
|
|
+ status.network_active,
|
|
|
|
|
+ status.filesystem_mode.as_str(),
|
|
|
|
|
+ status.filesystem_active,
|
|
|
|
|
+ if status.allowed_mounts.is_empty() {
|
|
|
|
|
+ "<none>".to_string()
|
|
|
|
|
+ } else {
|
|
|
|
|
+ status.allowed_mounts.join(", ")
|
|
|
|
|
+ },
|
|
|
|
|
+ if status.container_markers.is_empty() {
|
|
|
|
|
+ "<none>".to_string()
|
|
|
|
|
+ } else {
|
|
|
|
|
+ status.container_markers.join(", ")
|
|
|
|
|
+ },
|
|
|
|
|
+ status
|
|
|
|
|
+ .fallback_reason
|
|
|
|
|
+ .clone()
|
|
|
|
|
+ .unwrap_or_else(|| "<none>".to_string()),
|
|
|
|
|
+ )
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
fn render_config_report(section: Option<&str>) -> Result<String, Box<dyn std::error::Error>> {
|
|
fn render_config_report(section: Option<&str>) -> Result<String, Box<dyn std::error::Error>> {
|
|
|
let cwd = env::current_dir()?;
|
|
let cwd = env::current_dir()?;
|
|
|
let loader = ConfigLoader::default_for(&cwd);
|
|
let loader = ConfigLoader::default_for(&cwd);
|
|
@@ -4249,6 +4328,7 @@ mod tests {
|
|
|
assert!(help.contains("REPL"));
|
|
assert!(help.contains("REPL"));
|
|
|
assert!(help.contains("/help"));
|
|
assert!(help.contains("/help"));
|
|
|
assert!(help.contains("/status"));
|
|
assert!(help.contains("/status"));
|
|
|
|
|
+ assert!(help.contains("/sandbox"));
|
|
|
assert!(help.contains("/model [model]"));
|
|
assert!(help.contains("/model [model]"));
|
|
|
assert!(help.contains("/permissions [read-only|workspace-write|danger-full-access]"));
|
|
assert!(help.contains("/permissions [read-only|workspace-write|danger-full-access]"));
|
|
|
assert!(help.contains("/clear [--confirm]"));
|
|
assert!(help.contains("/clear [--confirm]"));
|
|
@@ -4279,8 +4359,8 @@ mod tests {
|
|
|
assert_eq!(
|
|
assert_eq!(
|
|
|
names,
|
|
names,
|
|
|
vec![
|
|
vec![
|
|
|
- "help", "status", "compact", "clear", "cost", "config", "memory", "init", "diff",
|
|
|
|
|
- "version", "export", "agents", "skills",
|
|
|
|
|
|
|
+ "help", "status", "sandbox", "compact", "clear", "cost", "config", "memory",
|
|
|
|
|
+ "init", "diff", "version", "export", "agents", "skills",
|
|
|
]
|
|
]
|
|
|
);
|
|
);
|
|
|
}
|
|
}
|
|
@@ -4400,6 +4480,7 @@ mod tests {
|
|
|
memory_file_count: 4,
|
|
memory_file_count: 4,
|
|
|
project_root: Some(PathBuf::from("/tmp")),
|
|
project_root: Some(PathBuf::from("/tmp")),
|
|
|
git_branch: Some("main".to_string()),
|
|
git_branch: Some("main".to_string()),
|
|
|
|
|
+ sandbox_status: runtime::SandboxStatus::default(),
|
|
|
},
|
|
},
|
|
|
);
|
|
);
|
|
|
assert!(status.contains("Status"));
|
|
assert!(status.contains("Status"));
|
|
@@ -4996,3 +5077,17 @@ mod tests {
|
|
|
assert!(!String::from_utf8(out).expect("utf8").contains("step 1"));
|
|
assert!(!String::from_utf8(out).expect("utf8").contains("step 1"));
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+#[cfg(test)]
|
|
|
|
|
+mod sandbox_report_tests {
|
|
|
|
|
+ use super::format_sandbox_report;
|
|
|
|
|
+
|
|
|
|
|
+ #[test]
|
|
|
|
|
+ fn sandbox_report_renders_expected_fields() {
|
|
|
|
|
+ let report = format_sandbox_report(&runtime::SandboxStatus::default());
|
|
|
|
|
+ assert!(report.contains("Sandbox"));
|
|
|
|
|
+ assert!(report.contains("Enabled"));
|
|
|
|
|
+ assert!(report.contains("Filesystem mode"));
|
|
|
|
|
+ assert!(report.contains("Fallback reason"));
|
|
|
|
|
+ }
|
|
|
|
|
+}
|