会话意图识别层(intent.rs / df-ai crate):
- Intent 枚举 11 态 + recognize 规则识别(关键词+权重+优先级)+ tool_subset_for 工具子集映射
- 模态接口预留 None,独立模块待接入 loop,36 单测
文件访问权限模型 Phase B+C(F-260619-03):
- Phase B 会话临时白名单 + 循环挂起 + DirAuthDialog 弹窗(仅本次/未来都允许/拒绝)
- Phase C 系统目录黑名单(Win System32/Program Files;Unix /etc /usr...)分段匹配 + 写操作约束
- 已审 CR-260619-09 ✅ PASS
198 lines
9.9 KiB
Rust
198 lines
9.9 KiB
Rust
//! DevFlow Tauri 入口 — 初始化 AppState 并注册全部 IPC 命令
|
||
|
||
mod commands;
|
||
mod state;
|
||
|
||
use tauri::{Emitter, Listener, Manager};
|
||
|
||
use state::AppState;
|
||
|
||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||
pub fn run() {
|
||
tauri::Builder::default()
|
||
.plugin(tauri_plugin_opener::init())
|
||
.plugin(tauri_plugin_dialog::init())
|
||
.plugin(tauri_plugin_window_state::Builder::default().build())
|
||
.setup(|app| {
|
||
// 数据库放在系统应用数据目录 <app_data_dir> 下(dev/release 拆分):
|
||
// Dev 模式 devflow-dev.db(可随意改动/清空),Build 模式 devflow.db(长期保留真实运行数据)
|
||
let db_name = if cfg!(debug_assertions) { "devflow-dev.db" } else { "devflow.db" };
|
||
let data_dir = app.path().app_data_dir()?;
|
||
std::fs::create_dir_all(&data_dir)?;
|
||
let db_path = data_dir.join(db_name);
|
||
|
||
// 初始化全局状态(打开数据库 + 执行迁移)
|
||
let app_state = tauri::async_runtime::block_on(AppState::init(&db_path))?;
|
||
// FR-S1:启动一次性迁移 DB 明文 api_key → OS keyring(失败保留明文下次重试,非阻断)
|
||
if let Err(e) = tauri::async_runtime::block_on(
|
||
commands::ai::secret::migrate_secrets_to_keyring(&app_state.ai_providers),
|
||
) {
|
||
tracing::warn!("[FR-S1] 启动密钥迁移失败(非阻断): {}", e);
|
||
}
|
||
// B-260616-01: L0 握手 — 提前 clone ai_session(Arc),因 manage() 会 move app_state
|
||
let session_for_handshake = app_state.ai_session.clone();
|
||
app.manage(app_state);
|
||
|
||
// B-260616-01: L0 握手 — 监听前端就绪事件,清除 HMR/刷新导致的残留 generating 状态
|
||
let app_handle = app.handle().clone();
|
||
app.listen("ai-client-ready", move |_event| {
|
||
let session_arc = session_for_handshake.clone();
|
||
let app_h = app_handle.clone();
|
||
tauri::async_runtime::spawn(async move {
|
||
let mut session = session_arc.lock().await;
|
||
// F-260616-09 B 批8(设计 §3 batch8 + §5.2):遍历 per_conv(HashMap)清多 conv
|
||
// 残留 generating(HMR/dev 热载场景多 conv 并发跑 loop 致多 conv 卡 generating)。
|
||
// 批4:per_conv 唯一真相源,删顶层 session.generating 双写复位(顶层字段已退役)。
|
||
let dirty_convs: Vec<String> = session
|
||
.per_conv
|
||
.iter()
|
||
.filter(|(_, c)| c.generating)
|
||
.map(|(id, _)| id.clone())
|
||
.collect();
|
||
let was_generating = !dirty_convs.is_empty();
|
||
// 复位每个残留 generating 的 conv(批8 多 conv 全覆盖)。
|
||
for cid in &dirty_convs {
|
||
if let Some(conv) = session.per_conv.get_mut(cid) {
|
||
conv.generating = false;
|
||
}
|
||
}
|
||
// BUG-260619-06 修复: clear 致冷启动 restore 重建审批丢失(restore 填充后 clear 无条件清空,
|
||
// 重启后待审批工具全丢)。改 retain 仅清非 recovered(本次会话/HMR 死 pending),
|
||
// 保留 restore 重建(recovered=true,audit.rs:331),对齐 switchConversation retain 保护意图。
|
||
session.pending_approvals.retain(|_, a| !a.recovered);
|
||
drop(session);
|
||
if was_generating {
|
||
// 补偿事件:每个残留 conv 各发一个 AiCompleted(按 conversation_id 路由),
|
||
// 前端 useAiEvents.ts:133-140 按 conv_id 各归各复位 streaming/generatingConvId。
|
||
for cid in &dirty_convs {
|
||
let _ = app_h.emit(
|
||
"ai-chat-event",
|
||
commands::ai::AiChatEvent::AiCompleted {
|
||
total_tokens: 0,
|
||
prompt_tokens: 0,
|
||
completion_tokens: 0,
|
||
incomplete: None,
|
||
conversation_id: Some(cid.clone()),
|
||
},
|
||
);
|
||
}
|
||
}
|
||
tracing::info!(
|
||
"[L0-handshake] 前端重连握手完成, was_generating={}, dirty_convs={:?}",
|
||
was_generating,
|
||
dirty_convs
|
||
);
|
||
});
|
||
});
|
||
|
||
Ok(())
|
||
})
|
||
.invoke_handler(tauri::generate_handler![
|
||
// 项目
|
||
commands::project::list_projects,
|
||
commands::project::create_project,
|
||
commands::project::import_project,
|
||
commands::project::get_project,
|
||
commands::project::update_project,
|
||
commands::project::delete_project,
|
||
commands::project::list_deleted_projects,
|
||
commands::project::restore_project,
|
||
commands::project::purge_project,
|
||
commands::project::scan_project_stack,
|
||
commands::project::check_path_binding,
|
||
commands::project::relocate_project_path,
|
||
commands::project::check_path_exists,
|
||
commands::project::scan_project_with_ai,
|
||
commands::project::scan_directory_for_projects,
|
||
commands::project::import_projects_batch,
|
||
// 任务
|
||
commands::task::list_tasks,
|
||
commands::task::create_task,
|
||
commands::task::update_task,
|
||
commands::task::delete_task,
|
||
commands::task::restore_task,
|
||
commands::task::get_task_by_id,
|
||
commands::task::advance_task,
|
||
// 灵感
|
||
commands::idea::list_ideas,
|
||
commands::idea::create_idea,
|
||
commands::idea::update_idea,
|
||
commands::idea::delete_idea,
|
||
commands::idea::evaluate_idea,
|
||
commands::idea::promote_idea,
|
||
// 工作流
|
||
commands::workflow::run_workflow,
|
||
commands::workflow::list_workflow_executions,
|
||
commands::workflow::get_workflow_execution,
|
||
commands::workflow::approve_human_approval,
|
||
commands::workflow::cancel_workflow_node,
|
||
// AI 聊天
|
||
commands::ai::ai_chat_send,
|
||
commands::ai::ai_regenerate,
|
||
commands::ai::ai_chat_edit,
|
||
commands::ai::ai_chat_force_send,
|
||
commands::ai::ai_chat_stop,
|
||
commands::ai::ai_approve,
|
||
// F-260616-03:达 max_iterations 暂停态续/停(消费 AiMaxRoundsReached,前端操作卡留 batch45)
|
||
commands::ai::ai_continue_loop,
|
||
commands::ai::ai_stop_loop,
|
||
commands::ai::ai_pending_tool_calls,
|
||
commands::ai::ai_chat_clear,
|
||
commands::ai::ai_is_generating,
|
||
// F-15 阶段2 手动上下文管理:分段归档 + LLM 压缩
|
||
commands::ai::ai_chat_clear_context,
|
||
commands::ai::ai_chat_compress_context,
|
||
commands::ai::ai_list_providers,
|
||
commands::ai::ai_save_provider,
|
||
commands::ai::ai_set_provider,
|
||
commands::ai::ai_delete_provider,
|
||
// F-260614-04c: 负载均衡池可编辑层(enabled/weight 轻量更新 + caps 重建)
|
||
commands::ai::ai_update_provider_pool,
|
||
// F-01 阶段5:测试连接拉取模型列表 + 单模型探测
|
||
commands::ai::ai_fetch_models,
|
||
commands::ai::ai_probe_model,
|
||
// AI 对话管理
|
||
commands::ai::ai_conversation_create,
|
||
commands::ai::ai_conversation_list,
|
||
commands::ai::ai_conversation_switch,
|
||
commands::ai::ai_conversation_delete,
|
||
commands::ai::ai_conversation_rename,
|
||
commands::ai::ai_conversation_archive,
|
||
commands::ai::ai_conversation_set_pinned,
|
||
commands::ai::ai_conversation_export,
|
||
commands::ai::ai_list_skills,
|
||
commands::ai::ai_set_concurrency_config,
|
||
commands::ai::ai_set_agent_max_iterations,
|
||
commands::ai::ai_set_agent_max_retries,
|
||
// F-260619-03 Phase A: AI 工具文件访问授权目录白名单
|
||
commands::ai::ai_get_allowed_dirs,
|
||
commands::ai::ai_set_allowed_dirs,
|
||
// F-260619-03 Phase B: 路径授权弹窗决策(消费 AiDirAuthRequired 挂起)
|
||
commands::ai::ai_authorize_dir,
|
||
// 审批历史面板(AE-2025-08:查 ai_tool_executions 表,敏感字段截断)
|
||
commands::ai::audit::list_tool_executions,
|
||
// 知识库
|
||
commands::knowledge::knowledge_list,
|
||
commands::knowledge::knowledge_get,
|
||
commands::knowledge::knowledge_search,
|
||
commands::knowledge::knowledge_create,
|
||
commands::knowledge::knowledge_update_status,
|
||
commands::knowledge::knowledge_record_reuse,
|
||
commands::knowledge::knowledge_list_candidates,
|
||
commands::knowledge::knowledge_archive,
|
||
commands::knowledge::knowledge_get_config,
|
||
commands::knowledge::knowledge_save_config,
|
||
commands::knowledge::knowledge_extract_now,
|
||
commands::knowledge::knowledge_get_detail,
|
||
commands::knowledge::knowledge_update,
|
||
commands::knowledge::knowledge_events,
|
||
// 通用应用设置 KV(前端 localStorage 迁移目标)
|
||
commands::settings::settings_get,
|
||
commands::settings::settings_set,
|
||
commands::settings::settings_get_all,
|
||
commands::settings::settings_delete,
|
||
])
|
||
.run(tauri::generate_context!())
|
||
.expect("error while running tauri application");
|
||
}
|