diff --git a/desktop/src-tauri/Cargo.lock b/desktop/src-tauri/Cargo.lock index 843f694..97fe206 100644 --- a/desktop/src-tauri/Cargo.lock +++ b/desktop/src-tauri/Cargo.lock @@ -735,6 +735,7 @@ dependencies = [ "dirs 5.0.1", "fantoccini", "futures", + "keyring", "regex", "reqwest 0.11.27", "serde", @@ -2117,6 +2118,16 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "keyring" +version = "3.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eebcc3aff044e5944a8fbaf69eb277d11986064cba30c468730e8b9909fb551c" +dependencies = [ + "log", + "zeroize", +] + [[package]] name = "kuchikiki" version = "0.8.8-speedreader" @@ -5913,6 +5924,12 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" + [[package]] name = "zerotrie" version = "0.2.3" diff --git a/desktop/src-tauri/Cargo.toml b/desktop/src-tauri/Cargo.toml index 3551ace..06f1b74 100644 --- a/desktop/src-tauri/Cargo.toml +++ b/desktop/src-tauri/Cargo.toml @@ -35,3 +35,6 @@ base64 = "0.22" thiserror = "2" uuid = { version = "1", features = ["v4", "serde"] } +# Secure storage (OS keyring/keychain) +keyring = "3" + diff --git a/desktop/src-tauri/src/lib.rs b/desktop/src-tauri/src/lib.rs index e8e761f..7b276b0 100644 --- a/desktop/src-tauri/src/lib.rs +++ b/desktop/src-tauri/src/lib.rs @@ -15,6 +15,9 @@ mod llm; // Browser automation module (Fantoccini-based Browser Hand) mod browser; +// Secure storage module for OS keyring/keychain +mod secure_storage; + use serde::Serialize; use serde_json::{json, Value}; use std::fs; @@ -1066,7 +1069,12 @@ pub fn run() { browser::commands::browser_element_screenshot, browser::commands::browser_get_source, browser::commands::browser_scrape_page, - browser::commands::browser_fill_form + browser::commands::browser_fill_form, + // Secure storage commands (OS keyring/keychain) + secure_storage::secure_store_set, + secure_storage::secure_store_get, + secure_storage::secure_store_delete, + secure_storage::secure_store_is_available ]) .run(tauri::generate_context!()) .expect("error while running tauri application"); diff --git a/desktop/src/App.tsx b/desktop/src/App.tsx index 4920ad9..0835b17 100644 --- a/desktop/src/App.tsx +++ b/desktop/src/App.tsx @@ -5,19 +5,21 @@ import { Sidebar, MainViewType } from './components/Sidebar'; import { ChatArea } from './components/ChatArea'; import { RightPanel } from './components/RightPanel'; import { SettingsLayout } from './components/Settings/SettingsLayout'; -import { HandTaskPanel } from './components/HandTaskPanel'; -import { SchedulerPanel } from './components/SchedulerPanel'; +import { AutomationPanel } from './components/Automation'; import { TeamCollaborationView } from './components/TeamCollaborationView'; +import { TeamOrchestrator } from './components/TeamOrchestrator'; import { SwarmDashboard } from './components/SwarmDashboard'; import { SkillMarket } from './components/SkillMarket'; import { AgentOnboardingWizard } from './components/AgentOnboardingWizard'; import { HandApprovalModal } from './components/HandApprovalModal'; +import { TopBar } from './components/TopBar'; +import { DetailDrawer } from './components/DetailDrawer'; import { useGatewayStore, type HandRun } from './store/gatewayStore'; import { useTeamStore } from './store/teamStore'; import { useChatStore } from './store/chatStore'; import { getStoredGatewayToken } from './lib/gateway-client'; import { pageVariants, defaultTransition, fadeInVariants } from './lib/animations'; -import { Bot, Users, Loader2 } from 'lucide-react'; +import { Users, Loader2, Settings } from 'lucide-react'; import { EmptyState } from './components/ui'; import { isTauriRuntime, getLocalGatewayStatus, startLocalGateway } from './lib/tauri-gateway'; import { useOnboarding } from './lib/use-onboarding'; @@ -40,15 +42,16 @@ function BootstrapScreen({ status }: { status: string }) { function App() { const [view, setView] = useState('main'); const [mainContentView, setMainContentView] = useState('chat'); - const [selectedHandId, setSelectedHandId] = useState(undefined); const [selectedTeamId, setSelectedTeamId] = useState(undefined); const [bootstrapping, setBootstrapping] = useState(true); const [bootstrapStatus, setBootstrapStatus] = useState('Initializing...'); const [showOnboarding, setShowOnboarding] = useState(false); + const [showDetailDrawer, setShowDetailDrawer] = useState(false); // Hand Approval state const [pendingApprovalRun, setPendingApprovalRun] = useState(null); const [showApprovalModal, setShowApprovalModal] = useState(false); + const [teamViewMode, setTeamViewMode] = useState<'collaboration' | 'orchestrator'>('collaboration'); const { connect, hands, approveHand, loadHands } = useGatewayStore(); const { activeTeam, setActiveTeam, teams } = useTeamStore(); @@ -182,13 +185,9 @@ function App() { setShowOnboarding(false); }; - // 当切换到非 hands 视图时清除选中的 Hand + // 处理主视图切换 const handleMainViewChange = (view: MainViewType) => { setMainContentView(view); - if (view !== 'hands') { - // 可选:清除选中的 Hand - // setSelectedHandId(undefined); - } }; const handleSelectTeam = (teamId: string) => { @@ -227,84 +226,120 @@ function App() { } return ( -
+
{/* 左侧边栏 */} setView('settings')} onMainViewChange={handleMainViewChange} - selectedHandId={selectedHandId} - onSelectHand={setSelectedHandId} selectedTeamId={selectedTeamId} onSelectTeam={handleSelectTeam} /> - {/* 中间区域 */} - - - {mainContentView === 'hands' && selectedHandId ? ( - setSelectedHandId(undefined)} - /> - ) : mainContentView === 'hands' ? ( - } - title="Select a Hand" - description="Choose an autonomous capability package from the list on the left to view its task list and execution results." - /> - ) : mainContentView === 'workflow' ? ( - - - - ) : mainContentView === 'team' ? ( - activeTeam ? ( - - ) : ( - } - title="Select or Create a Team" - description="Choose a team from the list on the left, or click + to create a new multi-Agent collaboration team." - /> - ) - ) : mainContentView === 'swarm' ? ( - - - - ) : mainContentView === 'skills' ? ( - - - - ) : ( - - )} - - + {/* 主内容区 */} +
+ {/* 顶部工具栏 */} + setShowDetailDrawer(true)} + /> - {/* 右侧边栏 */} - + {/* 内容区域 */} + + + {mainContentView === 'automation' ? ( + + + + ) : mainContentView === 'team' ? ( + activeTeam ? ( +
+ {/* Team View Tabs */} +
+ + +
+ {/* Tab Content */} +
+ {teamViewMode === 'orchestrator' ? ( + setTeamViewMode('collaboration')} /> + ) : ( + + )} +
+
+ ) : ( + } + title="选择或创建团队" + description="从左侧列表中选择一个团队,或点击 + 创建新的多 Agent 协作团队。" + /> + ) + ) : mainContentView === 'swarm' ? ( + + + + ) : mainContentView === 'skills' ? ( + + + + ) : ( + + )} +
+
+
+ + {/* 详情抽屉 - 按需显示 */} + setShowDetailDrawer(false)} + title="详情" + > + + {/* Hand Approval Modal (global) */}
@@ -70,11 +74,11 @@ function EventItem({ event, onAcknowledge }: EventItemProps) { {typeInfo.label} - {timeAgo} + {timeAgo}
-

{event.observation}

+

{event.observation}

{event.inferredPreference && ( -

→ {event.inferredPreference}

+

→ {event.inferredPreference}

)}
@@ -85,7 +89,7 @@ function EventItem({ event, onAcknowledge }: EventItemProps) { )}
-
+
置信度: {(event.confidence * 100).toFixed(0)}% {event.appliedCount > 0 && ( • 应用 {event.appliedCount} 次 @@ -111,13 +115,15 @@ function SuggestionCard({ suggestion, onApply, onDismiss }: SuggestionCardProps) initial={{ opacity: 0, scale: 0.95 }} animate={{ opacity: 1, scale: 1 }} exit={{ opacity: 0, scale: 0.95 }} - className="p-4 bg-gradient-to-r from-amber-900/20 to-transparent rounded-lg border border-amber-700/50" + whileHover={cardHover} + transition={defaultTransition} + className="p-4 bg-gradient-to-r from-amber-50 to-transparent dark:from-amber-900/20 dark:to-transparent rounded-lg border border-amber-200 dark:border-amber-700/50" >
- +
-

{suggestion.suggestion}

-
+

{suggestion.suggestion}

+
置信度: {(suggestion.confidence * 100).toFixed(0)}% {daysLeft > 0 && • {daysLeft} 天后过期}
@@ -204,63 +210,77 @@ export function ActiveLearningPanel({ className = '' }: ActiveLearningPanelProps }, [agentId, clearEvents]); return ( -
- {/* 夨览栏 */} -
-
- -

主动学习

-
- -
-