Wave 2.2: 流式安装输出、软件内授权四模式、本机环境一键装与总览缓存
EOF Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use agentdock_adapter::{AdapterAction, DryRunPlan};
|
||||
use agentdock_core::{ActionEvent, ActionOpts, AuthStatus, ConfigFormState, ConfigVerifyResult, DetectResult, Engine, WriteResult};
|
||||
use agentdock_core::{ActionEvent, ActionOpts, AuthFlowEvent, AuthStatus, ConfigFormState, ConfigVerifyResult, DetectResult, Engine, WriteResult};
|
||||
use agentdock_diag::DiagnosticReport;
|
||||
use serde_json::json;
|
||||
use tauri::Emitter;
|
||||
@@ -125,9 +125,44 @@ pub fn auth_status(id: String, state: tauri::State<'_, Engine>) -> Result<AuthSt
|
||||
state.auth_status(&id).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
/// 软件内授权(authorize):按官方授权方式运行登录命令,事件经 `cli-auth-event` 流式回传。
|
||||
#[tauri::command(rename = "authorize")]
|
||||
pub fn authorize(
|
||||
app: tauri::AppHandle,
|
||||
id: String,
|
||||
mode: String,
|
||||
state: tauri::State<'_, Engine>,
|
||||
) -> Result<(), String> {
|
||||
let engine = state.inner().clone();
|
||||
std::thread::spawn(move || {
|
||||
let cli_id = id.clone();
|
||||
let m = mode.clone();
|
||||
let result = engine.authorize_stream(&id, &mode, |ev| {
|
||||
let _ = app.emit("cli-auth-event", &ev);
|
||||
});
|
||||
if let Err(e) = result {
|
||||
let _ = app.emit("cli-auth-event", AuthFlowEvent::error(&cli_id, &m, e.to_string()));
|
||||
}
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 取消正在进行的授权流程。
|
||||
#[tauri::command(rename = "cancelAuthorize")]
|
||||
pub fn cancel_authorize(id: String, mode: String, state: tauri::State<'_, Engine>) {
|
||||
state.cancel_authorize(&id, &mode);
|
||||
}
|
||||
|
||||
/// 诊断(diagnose):内部实时取本机环境快照。
|
||||
#[tauri::command(rename = "diagnose")]
|
||||
pub fn diagnose(id: String, state: tauri::State<'_, Engine>) -> Result<DiagnosticReport, String> {
|
||||
let env = agentdock_platform::detect::detect_env();
|
||||
state.diagnose(&id, &env).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
/// 批量诊断全部已装工具(总览「立即诊断」入口用),内部实时取本机环境快照。
|
||||
#[tauri::command(rename = "diagnoseAll")]
|
||||
pub fn diagnose_all(state: tauri::State<'_, Engine>) -> Result<Vec<DiagnosticReport>, String> {
|
||||
let env = agentdock_platform::detect::detect_env();
|
||||
state.diagnose_all(&env).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod catalog;
|
||||
pub mod cli;
|
||||
pub mod env;
|
||||
pub mod runtime;
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
//! IPC 命令:本机环境运行时一键安装(Wave 2.2 Req 3)
|
||||
//!
|
||||
//! 下载仅限官方白名单(`RuntimeSource.allowed_hosts`),下载前经
|
||||
//! `is_url_host_allowed` 校验;下载用系统 `curl.exe`;下载成功后用
|
||||
//! `open_with_shell` 打开安装向导(用户在向导里点完即装)。失败兜底
|
||||
//! 提供「打开官方下载页」。应用本身不提权、不静默安装。
|
||||
|
||||
use agentdock_core::{
|
||||
is_url_host_allowed, open_with_shell, source_for, RuntimeSource,
|
||||
};
|
||||
use serde_json::json;
|
||||
use tauri::Emitter;
|
||||
|
||||
/// 预览运行时安装来源(确认弹窗展示官方地址/体积/权限)。
|
||||
#[tauri::command(rename = "previewRuntimeInstall")]
|
||||
pub fn preview_runtime_install(runtime: String) -> Result<RuntimeSource, String> {
|
||||
source_for(&runtime).ok_or_else(|| format!("未知运行时: {runtime}"))
|
||||
}
|
||||
|
||||
/// 兜底:打开运行时官方下载页(默认浏览器)。
|
||||
#[tauri::command(rename = "openRuntimePage")]
|
||||
pub fn open_runtime_page(runtime: String) -> Result<(), String> {
|
||||
let src = source_for(&runtime).ok_or_else(|| format!("未知运行时: {runtime}"))?;
|
||||
open_with_shell(&src.download_page).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
/// 一键安装运行时:下载官方安装包 → 打开安装向导。进度经 `runtime-install-event` 回传。
|
||||
#[tauri::command(rename = "installRuntime")]
|
||||
pub fn install_runtime(app: tauri::AppHandle, runtime: String) -> Result<(), String> {
|
||||
let src = source_for(&runtime).ok_or_else(|| format!("未知运行时: {runtime}"))?;
|
||||
|
||||
std::thread::spawn(move || {
|
||||
let emit = |app: &tauri::AppHandle, payload: serde_json::Value| {
|
||||
let _ = app.emit("runtime-install-event", payload);
|
||||
};
|
||||
|
||||
// 不可直接下载的运行时(如 uv):直接打开官方下载页
|
||||
let Some(url) = src.download_url.clone().filter(|_| src.direct_installable) else {
|
||||
let _ = open_with_shell(&src.download_page);
|
||||
emit(
|
||||
&app,
|
||||
json!({ "runtime": runtime, "phase": "opened_page", "message": format!("已打开{}官方下载页", src.label_zh) }),
|
||||
);
|
||||
return;
|
||||
};
|
||||
|
||||
// 安全红线:来源必须落白名单
|
||||
emit(&app, json!({ "runtime": runtime, "phase": "validate", "message": format!("正在校验来源({})…", src.source_label) }));
|
||||
if !is_url_host_allowed(&url, &src.allowed_hosts) {
|
||||
emit(
|
||||
&app,
|
||||
json!({ "runtime": runtime, "phase": "error", "message": "下载来源不在官方白名单内,已中止(安全红线)", "fallback": true }),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// 下载到临时目录
|
||||
let dir = std::env::temp_dir().join("agentdock-downloads");
|
||||
let _ = std::fs::create_dir_all(&dir);
|
||||
let file_name = url.rsplit('/').next().unwrap_or("installer.bin");
|
||||
let dest = dir.join(file_name);
|
||||
emit(&app, json!({ "runtime": runtime, "phase": "download", "message": format!("正在下载 {}({},来自 {})…", src.label_zh, src.size_approx, src.source_label) }));
|
||||
|
||||
match agentdock_core::download_with_curl(&url, &dest) {
|
||||
Ok(()) => {
|
||||
emit(&app, json!({ "runtime": runtime, "phase": "open", "message": "下载完成,正在打开安装向导(请在向导中完成安装)…" }));
|
||||
if let Err(e) = open_with_shell(&dest.to_string_lossy()) {
|
||||
emit(
|
||||
&app,
|
||||
json!({ "runtime": runtime, "phase": "error", "message": format!("无法打开安装向导:{e}"), "fallback": true }),
|
||||
);
|
||||
return;
|
||||
}
|
||||
emit(&app, json!({ "runtime": runtime, "phase": "done", "message": "安装向导已打开" }));
|
||||
}
|
||||
Err(e) => {
|
||||
emit(
|
||||
&app,
|
||||
json!({ "runtime": runtime, "phase": "error", "message": format!("下载失败:{e}。可点击「打开官方下载页」手动下载。"), "fallback": true }),
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
@@ -56,7 +56,13 @@ pub fn run() {
|
||||
commands::cli::write_config,
|
||||
commands::cli::verify_config,
|
||||
commands::cli::auth_status,
|
||||
commands::cli::authorize,
|
||||
commands::cli::cancel_authorize,
|
||||
commands::cli::diagnose,
|
||||
commands::cli::diagnose_all,
|
||||
commands::runtime::preview_runtime_install,
|
||||
commands::runtime::open_runtime_page,
|
||||
commands::runtime::install_runtime,
|
||||
])
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
|
||||
Reference in New Issue
Block a user