chore(lint): clean up frontend and Rust lint issues
This commit is contained in:
@@ -156,9 +156,7 @@ async fn build_agent(
|
||||
debug_assert_eq!(meta.agent_type, agent_type);
|
||||
|
||||
match meta.distribution {
|
||||
AgentDistribution::Npx {
|
||||
cmd, args, env, ..
|
||||
} => {
|
||||
AgentDistribution::Npx { cmd, args, env, .. } => {
|
||||
let merged_env = merge_agent_env(env, runtime_env);
|
||||
let mut parts: Vec<String> = Vec::new();
|
||||
for (k, v) in &merged_env {
|
||||
@@ -251,10 +249,7 @@ async fn build_agent(
|
||||
))
|
||||
})?;
|
||||
if cached_version == registry_version {
|
||||
eprintln!(
|
||||
"[ACP][{}] Using cached binary {cached_version}",
|
||||
meta.name
|
||||
);
|
||||
eprintln!("[ACP][{}] Using cached binary {cached_version}", meta.name);
|
||||
} else {
|
||||
eprintln!(
|
||||
"[ACP][{}] Using cached binary {cached_version} (registry recommends {registry_version})",
|
||||
@@ -273,8 +268,7 @@ async fn build_agent(
|
||||
server = server.args(cmd_args);
|
||||
}
|
||||
let merged_env = merge_agent_env(env, runtime_env);
|
||||
let env_key_list: Vec<&str> =
|
||||
merged_env.iter().map(|(k, _)| k.as_str()).collect();
|
||||
let env_key_list: Vec<&str> = merged_env.iter().map(|(k, _)| k.as_str()).collect();
|
||||
if !merged_env.is_empty() {
|
||||
let env_vars: Vec<sacp::schema::EnvVariable> = merged_env
|
||||
.iter()
|
||||
@@ -314,34 +308,36 @@ async fn build_agent(
|
||||
.map(|v| v == "1" || v.eq_ignore_ascii_case("true"))
|
||||
.unwrap_or(false);
|
||||
let agent_name = meta.name.to_string();
|
||||
Ok(AcpAgent::new(sacp::schema::McpServer::Stdio(server)).with_debug(
|
||||
move |line, dir| {
|
||||
let (tag, enabled) = match dir {
|
||||
sacp_tokio::LineDirection::Stderr => ("stderr", true),
|
||||
sacp_tokio::LineDirection::Stdout => ("stdout", stdio_debug_enabled),
|
||||
sacp_tokio::LineDirection::Stdin => ("stdin", stdio_debug_enabled),
|
||||
};
|
||||
if !enabled {
|
||||
return;
|
||||
}
|
||||
const MAX: usize = 256;
|
||||
if line.len() > MAX {
|
||||
let head = line
|
||||
.char_indices()
|
||||
.take_while(|(i, _)| *i < MAX)
|
||||
.last()
|
||||
.map(|(i, c)| i + c.len_utf8())
|
||||
.unwrap_or(MAX);
|
||||
eprintln!(
|
||||
"[ACP][{agent_name}][{tag}] {}... <truncated {} bytes>",
|
||||
&line[..head],
|
||||
line.len() - head
|
||||
);
|
||||
} else {
|
||||
eprintln!("[ACP][{agent_name}][{tag}] {line}");
|
||||
}
|
||||
},
|
||||
))
|
||||
Ok(
|
||||
AcpAgent::new(sacp::schema::McpServer::Stdio(server)).with_debug(
|
||||
move |line, dir| {
|
||||
let (tag, enabled) = match dir {
|
||||
sacp_tokio::LineDirection::Stderr => ("stderr", true),
|
||||
sacp_tokio::LineDirection::Stdout => ("stdout", stdio_debug_enabled),
|
||||
sacp_tokio::LineDirection::Stdin => ("stdin", stdio_debug_enabled),
|
||||
};
|
||||
if !enabled {
|
||||
return;
|
||||
}
|
||||
const MAX: usize = 256;
|
||||
if line.len() > MAX {
|
||||
let head = line
|
||||
.char_indices()
|
||||
.take_while(|(i, _)| *i < MAX)
|
||||
.last()
|
||||
.map(|(i, c)| i + c.len_utf8())
|
||||
.unwrap_or(MAX);
|
||||
eprintln!(
|
||||
"[ACP][{agent_name}][{tag}] {}... <truncated {} bytes>",
|
||||
&line[..head],
|
||||
line.len() - head
|
||||
);
|
||||
} else {
|
||||
eprintln!("[ACP][{agent_name}][{tag}] {line}");
|
||||
}
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -574,37 +570,40 @@ fn ensure_codex_mode_option(options: &mut Vec<SessionConfigOptionInfo>) {
|
||||
if options.iter().any(|o| o.id == "mode") {
|
||||
return;
|
||||
}
|
||||
options.insert(0, SessionConfigOptionInfo {
|
||||
id: "mode".to_string(),
|
||||
name: "Approval Preset".to_string(),
|
||||
description: Some(
|
||||
"Choose an approval and sandboxing preset for your session".to_string(),
|
||||
),
|
||||
category: Some("mode".to_string()),
|
||||
kind: SessionConfigKindInfo::Select(SessionConfigSelectInfo {
|
||||
current_value: "auto".to_string(),
|
||||
options: vec![
|
||||
SessionConfigSelectOptionInfo {
|
||||
value: "read-only".to_string(),
|
||||
name: "Read Only".to_string(),
|
||||
description: Some("Codex can only read files".to_string()),
|
||||
},
|
||||
SessionConfigSelectOptionInfo {
|
||||
value: "auto".to_string(),
|
||||
name: "Default".to_string(),
|
||||
description: Some(
|
||||
"Codex can edit files, but asks before running commands".to_string(),
|
||||
),
|
||||
},
|
||||
SessionConfigSelectOptionInfo {
|
||||
value: "full-access".to_string(),
|
||||
name: "Full Access".to_string(),
|
||||
description: Some("Codex runs without asking for approval".to_string()),
|
||||
},
|
||||
],
|
||||
groups: vec![],
|
||||
}),
|
||||
});
|
||||
options.insert(
|
||||
0,
|
||||
SessionConfigOptionInfo {
|
||||
id: "mode".to_string(),
|
||||
name: "Approval Preset".to_string(),
|
||||
description: Some(
|
||||
"Choose an approval and sandboxing preset for your session".to_string(),
|
||||
),
|
||||
category: Some("mode".to_string()),
|
||||
kind: SessionConfigKindInfo::Select(SessionConfigSelectInfo {
|
||||
current_value: "auto".to_string(),
|
||||
options: vec![
|
||||
SessionConfigSelectOptionInfo {
|
||||
value: "read-only".to_string(),
|
||||
name: "Read Only".to_string(),
|
||||
description: Some("Codex can only read files".to_string()),
|
||||
},
|
||||
SessionConfigSelectOptionInfo {
|
||||
value: "auto".to_string(),
|
||||
name: "Default".to_string(),
|
||||
description: Some(
|
||||
"Codex can edit files, but asks before running commands".to_string(),
|
||||
),
|
||||
},
|
||||
SessionConfigSelectOptionInfo {
|
||||
value: "full-access".to_string(),
|
||||
name: "Full Access".to_string(),
|
||||
description: Some("Codex runs without asking for approval".to_string()),
|
||||
},
|
||||
],
|
||||
groups: vec![],
|
||||
}),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn emit_session_config_options_values(
|
||||
@@ -758,7 +757,15 @@ async fn run_connection(
|
||||
async move |req: RequestPermissionRequest,
|
||||
responder: Responder<RequestPermissionResponse>,
|
||||
_cx: ConnectionTo<Agent>| {
|
||||
handle_permission_request(&conn_id, &emitter_inner, &perms, &perm_cwd, req, responder).await;
|
||||
handle_permission_request(
|
||||
&conn_id,
|
||||
&emitter_inner,
|
||||
&perms,
|
||||
&perm_cwd,
|
||||
req,
|
||||
responder,
|
||||
)
|
||||
.await;
|
||||
Ok(())
|
||||
}
|
||||
},
|
||||
@@ -979,7 +986,13 @@ async fn run_connection(
|
||||
notif.update,
|
||||
SessionUpdate::AvailableCommandsUpdate(_)
|
||||
) {
|
||||
emit_conversation_update(&cid, &h, agent_type, notif.update, None);
|
||||
emit_conversation_update(
|
||||
&cid,
|
||||
&h,
|
||||
agent_type,
|
||||
notif.update,
|
||||
None,
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
@@ -1004,7 +1017,12 @@ async fn run_connection(
|
||||
},
|
||||
);
|
||||
emit_session_modes(&conn_id, &emitter_clone, session.modes());
|
||||
emit_session_config_options(&conn_id, &emitter_clone, agent_type, &initial_config_options);
|
||||
emit_session_config_options(
|
||||
&conn_id,
|
||||
&emitter_clone,
|
||||
agent_type,
|
||||
&initial_config_options,
|
||||
);
|
||||
emit_selectors_ready(&conn_id, &emitter_clone);
|
||||
|
||||
let loop_result = run_conversation_loop(
|
||||
@@ -1058,9 +1076,7 @@ async fn run_connection(
|
||||
"acp://event",
|
||||
AcpEvent::Error {
|
||||
connection_id: conn_id.clone(),
|
||||
message: format!(
|
||||
"Failed to load session, starting new: {e}"
|
||||
),
|
||||
message: format!("Failed to load session, starting new: {e}"),
|
||||
agent_type: agent_type.to_string(),
|
||||
code: None,
|
||||
},
|
||||
@@ -1072,8 +1088,7 @@ async fn run_connection(
|
||||
.await?;
|
||||
let fallback_sid = new_resp.session_id.0.to_string();
|
||||
let initial_config_options = new_resp.config_options.clone();
|
||||
let mut session =
|
||||
cx.attach_session(new_resp, Default::default())?;
|
||||
let mut session = cx.attach_session(new_resp, Default::default())?;
|
||||
crate::web::event_bridge::emit_event(
|
||||
&emitter_clone,
|
||||
"acp://event",
|
||||
@@ -1139,7 +1154,12 @@ async fn run_connection(
|
||||
},
|
||||
);
|
||||
emit_session_modes(&conn_id, &emitter_clone, session.modes());
|
||||
emit_session_config_options(&conn_id, &emitter_clone, agent_type, &initial_config_options);
|
||||
emit_session_config_options(
|
||||
&conn_id,
|
||||
&emitter_clone,
|
||||
agent_type,
|
||||
&initial_config_options,
|
||||
);
|
||||
emit_selectors_ready(&conn_id, &emitter_clone);
|
||||
|
||||
let loop_result = run_conversation_loop(
|
||||
@@ -1229,8 +1249,7 @@ async fn handle_permission_request(
|
||||
obj.insert(key.to_string(), parsed);
|
||||
}
|
||||
} else if text.contains("@@\n") || text.contains("@@\r\n") {
|
||||
if let Some(resolved) =
|
||||
crate::parsers::resolve_patch_text(&text, Some(cwd))
|
||||
if let Some(resolved) = crate::parsers::resolve_patch_text(&text, Some(cwd))
|
||||
{
|
||||
obj.insert(key.to_string(), serde_json::Value::String(resolved));
|
||||
}
|
||||
@@ -1759,7 +1778,14 @@ async fn handle_fork_or_exit(
|
||||
|
||||
// Recursively handle nested forks
|
||||
Box::pin(handle_fork_or_exit(
|
||||
loop_result, conn_id, emitter, agent_type, perms, cmd_rx, terminal_runtime, _cwd,
|
||||
loop_result,
|
||||
conn_id,
|
||||
emitter,
|
||||
agent_type,
|
||||
perms,
|
||||
cmd_rx,
|
||||
terminal_runtime,
|
||||
_cwd,
|
||||
cwd_string,
|
||||
))
|
||||
.await
|
||||
@@ -2169,8 +2195,10 @@ async fn run_conversation_loop<'a>(
|
||||
}) => {
|
||||
let cx = session.connection();
|
||||
let sid = session.session_id().clone();
|
||||
if let Err(e) =
|
||||
set_session_config_option(&cx, &sid, conn_id, emitter, agent_type, config_id, value_id).await
|
||||
if let Err(e) = set_session_config_option(
|
||||
&cx, &sid, conn_id, emitter, agent_type, config_id, value_id,
|
||||
)
|
||||
.await
|
||||
{
|
||||
crate::web::event_bridge::emit_event(
|
||||
emitter,
|
||||
@@ -2499,10 +2527,10 @@ fn emit_conversation_update(
|
||||
}
|
||||
SessionUpdate::ToolCall(tc) => {
|
||||
let content = serialize_tool_call_content(&tc.content);
|
||||
let raw_input = json_value_to_text(&tc.raw_input)
|
||||
.map(|text| resolve_live_tool_input(&text, cwd));
|
||||
let raw_output = json_value_to_text(&tc.raw_output)
|
||||
.map(|text| structurize_live_output(&text));
|
||||
let raw_input =
|
||||
json_value_to_text(&tc.raw_input).map(|text| resolve_live_tool_input(&text, cwd));
|
||||
let raw_output =
|
||||
json_value_to_text(&tc.raw_output).map(|text| structurize_live_output(&text));
|
||||
let locations = if tc.locations.is_empty() {
|
||||
None
|
||||
} else {
|
||||
@@ -2581,7 +2609,12 @@ fn emit_conversation_update(
|
||||
);
|
||||
}
|
||||
SessionUpdate::ConfigOptionUpdate(update) => {
|
||||
emit_session_config_options_values(connection_id, emitter, agent_type, update.config_options);
|
||||
emit_session_config_options_values(
|
||||
connection_id,
|
||||
emitter,
|
||||
agent_type,
|
||||
update.config_options,
|
||||
);
|
||||
}
|
||||
SessionUpdate::AvailableCommandsUpdate(update) => {
|
||||
let commands: Vec<AvailableCommandInfo> = update
|
||||
@@ -2661,8 +2694,8 @@ mod tests {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let event = map_claude_sdk_ext_notification("conn-1", &raw)
|
||||
.expect("valid sdk payload should map");
|
||||
let event =
|
||||
map_claude_sdk_ext_notification("conn-1", &raw).expect("valid sdk payload should map");
|
||||
|
||||
match event {
|
||||
AcpEvent::ClaudeSdkMessage {
|
||||
@@ -2700,11 +2733,8 @@ mod tests {
|
||||
.unwrap();
|
||||
assert!(map_claude_sdk_ext_notification("conn-1", &wrong_method).is_none());
|
||||
|
||||
let missing_fields = UntypedMessage::new(
|
||||
"_claude/sdkMessage",
|
||||
serde_json::json!({"sessionId": 1}),
|
||||
)
|
||||
.unwrap();
|
||||
let missing_fields =
|
||||
UntypedMessage::new("_claude/sdkMessage", serde_json::json!({"sessionId": 1})).unwrap();
|
||||
assert!(map_claude_sdk_ext_notification("conn-1", &missing_fields).is_none());
|
||||
}
|
||||
|
||||
|
||||
@@ -219,10 +219,7 @@ impl ConnectionManager {
|
||||
pub async fn disconnect_all(&self) -> usize {
|
||||
let cmd_txs: Vec<_> = {
|
||||
let mut connections = self.connections.lock().await;
|
||||
connections
|
||||
.drain()
|
||||
.map(|(_, conn)| conn.cmd_tx)
|
||||
.collect()
|
||||
connections.drain().map(|(_, conn)| conn.cmd_tx).collect()
|
||||
};
|
||||
let disconnected = cmd_txs.len();
|
||||
for cmd_tx in cmd_txs {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
pub mod binary_cache;
|
||||
pub mod connection;
|
||||
pub mod error;
|
||||
pub mod fork;
|
||||
pub mod file_system_runtime;
|
||||
pub mod fork;
|
||||
pub mod manager;
|
||||
pub mod opencode_plugins;
|
||||
pub mod preflight;
|
||||
pub mod registry;
|
||||
pub mod terminal_runtime;
|
||||
pub mod types;
|
||||
pub mod opencode_plugins;
|
||||
|
||||
@@ -88,9 +88,7 @@ fn has_project_opencode_config(project_root: &Path) -> bool {
|
||||
|
||||
/// Inspect `~/.config/opencode/opencode.json` and `~/.cache/opencode/node_modules/`
|
||||
/// to determine which declared plugins are installed and which are missing.
|
||||
pub fn check_opencode_plugins(
|
||||
project_root: Option<&Path>,
|
||||
) -> Result<PluginCheckSummary, String> {
|
||||
pub fn check_opencode_plugins(project_root: Option<&Path>) -> Result<PluginCheckSummary, String> {
|
||||
let config_path = opencode_config_path()
|
||||
.ok_or_else(|| "Cannot determine opencode config directory".to_string())?;
|
||||
let cache_dir = opencode_cache_dir()
|
||||
@@ -257,12 +255,7 @@ fn write_backup_and_prune(path: &Path, content: &str, keep: usize) -> Result<(),
|
||||
let mut backups: Vec<_> = fs::read_dir(parent)
|
||||
.map_err(|e| e.to_string())?
|
||||
.filter_map(|entry| entry.ok())
|
||||
.filter(|entry| {
|
||||
entry
|
||||
.file_name()
|
||||
.to_string_lossy()
|
||||
.starts_with(&prefix)
|
||||
})
|
||||
.filter(|entry| entry.file_name().to_string_lossy().starts_with(&prefix))
|
||||
.collect();
|
||||
|
||||
// Sort by name descending (timestamp in name → newest first)
|
||||
@@ -280,8 +273,8 @@ pub(crate) fn atomic_rewrite_opencode_json(
|
||||
path: &Path,
|
||||
mutator: impl FnOnce(&mut serde_json::Value) -> Result<(), String>,
|
||||
) -> Result<(), String> {
|
||||
let raw = fs::read_to_string(path)
|
||||
.map_err(|e| format!("Failed to read {}: {e}", path.display()))?;
|
||||
let raw =
|
||||
fs::read_to_string(path).map_err(|e| format!("Failed to read {}: {e}", path.display()))?;
|
||||
|
||||
// Try parsing first. If serde_json succeeds the file is valid JSON
|
||||
// and any "//" or "/*" sequences live inside string values — not real
|
||||
@@ -302,14 +295,12 @@ pub(crate) fn atomic_rewrite_opencode_json(
|
||||
|
||||
mutator(&mut doc)?;
|
||||
|
||||
let new_raw = serde_json::to_string_pretty(&doc)
|
||||
.map_err(|e| format!("Failed to serialize JSON: {e}"))?;
|
||||
let new_raw =
|
||||
serde_json::to_string_pretty(&doc).map_err(|e| format!("Failed to serialize JSON: {e}"))?;
|
||||
|
||||
let tmp_path = path.with_extension("json.tmp");
|
||||
fs::write(&tmp_path, &new_raw)
|
||||
.map_err(|e| format!("Failed to write temp file: {e}"))?;
|
||||
fs::rename(&tmp_path, path)
|
||||
.map_err(|e| format!("Failed to rename temp file: {e}"))?;
|
||||
fs::write(&tmp_path, &new_raw).map_err(|e| format!("Failed to write temp file: {e}"))?;
|
||||
fs::rename(&tmp_path, path).map_err(|e| format!("Failed to rename temp file: {e}"))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -366,12 +357,9 @@ fn pin_latest_specs(
|
||||
for item in arr.iter_mut() {
|
||||
if let Some(spec_str) = item.as_str() {
|
||||
if let Some((parsed_name, _)) = parse_plugin_spec(spec_str) {
|
||||
if let Some((_, version)) =
|
||||
pin_map.iter().find(|(n, _)| *n == parsed_name)
|
||||
if let Some((_, version)) = pin_map.iter().find(|(n, _)| *n == parsed_name)
|
||||
{
|
||||
*item = serde_json::Value::String(format!(
|
||||
"{parsed_name}@{version}"
|
||||
));
|
||||
*item = serde_json::Value::String(format!("{parsed_name}@{version}"));
|
||||
pinned += 1;
|
||||
}
|
||||
}
|
||||
@@ -417,9 +405,9 @@ pub async fn install_missing_plugins(
|
||||
task_id: String,
|
||||
emitter: &EventEmitter,
|
||||
) -> Result<(), String> {
|
||||
let _guard = PLUGIN_OP_LOCK.try_lock().map_err(|_| {
|
||||
"Another plugin operation is in progress".to_string()
|
||||
})?;
|
||||
let _guard = PLUGIN_OP_LOCK
|
||||
.try_lock()
|
||||
.map_err(|_| "Another plugin operation is in progress".to_string())?;
|
||||
|
||||
emit_plugin_event(emitter, &task_id, PluginInstallEventKind::Started, "");
|
||||
|
||||
@@ -597,12 +585,14 @@ pub async fn install_missing_plugins(
|
||||
|
||||
/// Uninstall a single plugin: remove from opencode.json, then `bun remove` from cache.
|
||||
pub async fn uninstall_plugin(name: String) -> Result<PluginCheckSummary, String> {
|
||||
let _guard = PLUGIN_OP_LOCK.try_lock().map_err(|_| {
|
||||
"Another plugin operation is in progress".to_string()
|
||||
})?;
|
||||
let _guard = PLUGIN_OP_LOCK
|
||||
.try_lock()
|
||||
.map_err(|_| "Another plugin operation is in progress".to_string())?;
|
||||
|
||||
if is_protected_package(&name) {
|
||||
return Err(format!("Cannot uninstall {name}: it is an internal opencode package"));
|
||||
return Err(format!(
|
||||
"Cannot uninstall {name}: it is an internal opencode package"
|
||||
));
|
||||
}
|
||||
|
||||
let config_path = opencode_config_path()
|
||||
|
||||
@@ -102,14 +102,30 @@ async fn check_npm_environment(node_required: Option<&str>) -> Vec<CheckItem> {
|
||||
let (node_result, npm_result) = tokio::join!(
|
||||
async {
|
||||
match &node_path {
|
||||
Some(p) => crate::process::tokio_command(p).arg("--version").output().await,
|
||||
None => Err(std::io::Error::new(std::io::ErrorKind::NotFound, "node not found in PATH")),
|
||||
Some(p) => {
|
||||
crate::process::tokio_command(p)
|
||||
.arg("--version")
|
||||
.output()
|
||||
.await
|
||||
}
|
||||
None => Err(std::io::Error::new(
|
||||
std::io::ErrorKind::NotFound,
|
||||
"node not found in PATH",
|
||||
)),
|
||||
}
|
||||
},
|
||||
async {
|
||||
match &npm_path {
|
||||
Some(p) => crate::process::tokio_command(p).arg("--version").output().await,
|
||||
None => Err(std::io::Error::new(std::io::ErrorKind::NotFound, "npm not found in PATH")),
|
||||
Some(p) => {
|
||||
crate::process::tokio_command(p)
|
||||
.arg("--version")
|
||||
.output()
|
||||
.await
|
||||
}
|
||||
None => Err(std::io::Error::new(
|
||||
std::io::ErrorKind::NotFound,
|
||||
"npm not found in PATH",
|
||||
)),
|
||||
}
|
||||
},
|
||||
);
|
||||
@@ -310,47 +326,44 @@ async fn check_binary_environment(
|
||||
// but still pass — the Settings page's version-badge flow is the
|
||||
// canonical place to surface "upgrade available".
|
||||
if platform_supported {
|
||||
let cache_check =
|
||||
match binary_cache::find_best_cached_binary_for_agent(agent_type, cmd) {
|
||||
Ok(Some((_, cached_version))) => {
|
||||
let message = if cached_version == version {
|
||||
"Binary is cached locally".to_string()
|
||||
} else {
|
||||
format!(
|
||||
"Binary {cached_version} is cached locally (recommended: {version})"
|
||||
)
|
||||
};
|
||||
CheckItem {
|
||||
check_id: "binary_cached".into(),
|
||||
label: "Binary cache".into(),
|
||||
status: CheckStatus::Pass,
|
||||
message,
|
||||
fixes: vec![],
|
||||
}
|
||||
let cache_check = match binary_cache::find_best_cached_binary_for_agent(agent_type, cmd) {
|
||||
Ok(Some((_, cached_version))) => {
|
||||
let message = if cached_version == version {
|
||||
"Binary is cached locally".to_string()
|
||||
} else {
|
||||
format!("Binary {cached_version} is cached locally (recommended: {version})")
|
||||
};
|
||||
CheckItem {
|
||||
check_id: "binary_cached".into(),
|
||||
label: "Binary cache".into(),
|
||||
status: CheckStatus::Pass,
|
||||
message,
|
||||
fixes: vec![],
|
||||
}
|
||||
Ok(None) => CheckItem {
|
||||
check_id: "binary_cached".into(),
|
||||
label: "Binary cache".into(),
|
||||
status: CheckStatus::Warn,
|
||||
message:
|
||||
"Binary is not installed. Download it from Agent Settings before connecting."
|
||||
.into(),
|
||||
fixes: vec![],
|
||||
},
|
||||
Err(_) => CheckItem {
|
||||
check_id: "binary_cached".into(),
|
||||
label: "Binary cache".into(),
|
||||
status: CheckStatus::Warn,
|
||||
message: "Cannot determine binary cache path".into(),
|
||||
fixes: vec![],
|
||||
},
|
||||
};
|
||||
}
|
||||
Ok(None) => CheckItem {
|
||||
check_id: "binary_cached".into(),
|
||||
label: "Binary cache".into(),
|
||||
status: CheckStatus::Warn,
|
||||
message:
|
||||
"Binary is not installed. Download it from Agent Settings before connecting."
|
||||
.into(),
|
||||
fixes: vec![],
|
||||
},
|
||||
Err(_) => CheckItem {
|
||||
check_id: "binary_cached".into(),
|
||||
label: "Binary cache".into(),
|
||||
status: CheckStatus::Warn,
|
||||
message: "Cannot determine binary cache path".into(),
|
||||
fixes: vec![],
|
||||
},
|
||||
};
|
||||
checks.push(cache_check);
|
||||
}
|
||||
|
||||
// OpenCode plugin checks
|
||||
if agent_type == AgentType::OpenCode {
|
||||
use crate::acp::opencode_plugins::{self, PluginStatus, spec_has_floating_version};
|
||||
use crate::acp::opencode_plugins::{self, spec_has_floating_version, PluginStatus};
|
||||
match opencode_plugins::check_opencode_plugins(None) {
|
||||
Ok(summary) => {
|
||||
let missing: Vec<_> = summary
|
||||
@@ -376,8 +389,7 @@ async fn check_binary_environment(
|
||||
fixes: vec![],
|
||||
});
|
||||
} else {
|
||||
let names: Vec<&str> =
|
||||
missing.iter().map(|p| p.name.as_str()).collect();
|
||||
let names: Vec<&str> = missing.iter().map(|p| p.name.as_str()).collect();
|
||||
checks.push(CheckItem {
|
||||
check_id: "opencode_plugins".into(),
|
||||
label: "OpenCode plugins".into(),
|
||||
|
||||
@@ -38,8 +38,9 @@ pub struct AcpAgentMeta {
|
||||
impl AcpAgentMeta {
|
||||
pub fn registry_version(&self) -> Option<&'static str> {
|
||||
match &self.distribution {
|
||||
AgentDistribution::Npx { version, .. }
|
||||
| AgentDistribution::Binary { version, .. } => Some(*version),
|
||||
AgentDistribution::Npx { version, .. } | AgentDistribution::Binary { version, .. } => {
|
||||
Some(*version)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,10 +3,9 @@ use std::process::Stdio;
|
||||
use std::sync::Arc;
|
||||
|
||||
use sacp::schema::{
|
||||
CreateTerminalRequest, CreateTerminalResponse, KillTerminalRequest,
|
||||
KillTerminalResponse, ReleaseTerminalRequest, ReleaseTerminalResponse,
|
||||
TerminalExitStatus, TerminalOutputRequest, TerminalOutputResponse, WaitForTerminalExitRequest,
|
||||
WaitForTerminalExitResponse,
|
||||
CreateTerminalRequest, CreateTerminalResponse, KillTerminalRequest, KillTerminalResponse,
|
||||
ReleaseTerminalRequest, ReleaseTerminalResponse, TerminalExitStatus, TerminalOutputRequest,
|
||||
TerminalOutputResponse, WaitForTerminalExitRequest, WaitForTerminalExitResponse,
|
||||
};
|
||||
use tokio::io::{AsyncRead, AsyncReadExt};
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
Reference in New Issue
Block a user