fix(ui): 9项端到端真实审计 — 修复记忆/技能/审计/工作区/MCP数据流断裂
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
基于 Tauri MCP 实机排查发现并修复:
1. VikingPanel: viking_ls('/') 返回0 → 改为 viking_ls('') 返回100条记忆
2. 技能列表: loadSkillsCatalog 静默失败 → 添加直接 invoke('skill_list') 回退
3. 审计日志: 面板读Gateway API无数据 → 回退读localStorage双源数据
4. 工作区: 浏览按钮无事件 → 接入prompt选择 + workspace_dir_stats 命令
5. MCP: 空列表无引导 → 添加配置文件路径提示
6. 新增 workspace_dir_stats Tauri 命令 (Rust)
排查确认正常的功能: 安全存储(OS Keyring✅), 心跳引擎(运行中✅),
定时任务(管道连通), Kernel(已初始化✅), SaaS relay模式
This commit is contained in:
@@ -180,14 +180,19 @@ export function MCPServices() {
|
||||
</div>
|
||||
);
|
||||
}) : (
|
||||
<div className="p-8 text-center text-sm text-gray-400">
|
||||
当前快速配置中尚未声明 MCP 服务
|
||||
<div className="p-8 text-center">
|
||||
<div className="text-sm text-gray-400 mb-2">尚未配置 MCP 服务</div>
|
||||
<div className="text-xs text-gray-300">
|
||||
MCP 服务为 Agent 扩展外部工具能力。可通过编辑配置文件
|
||||
<code className="mx-1 text-gray-500 bg-gray-100 px-1 rounded">config/mcp.toml</code>
|
||||
添加服务。
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="text-xs text-amber-700 bg-amber-50 rounded-lg p-3">
|
||||
新增服务、删除服务和详细参数配置尚未在桌面端接入。可通过配置文件手动添加。
|
||||
新增/删除服务尚未在桌面端 UI 接入。可通过编辑 config/mcp.toml 手动添加 MCP 服务配置,重启后生效。
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -17,6 +17,9 @@ export function Skills() {
|
||||
useEffect(() => {
|
||||
if (connected) {
|
||||
loadSkillsCatalog().catch(silentErrorHandler('Skills'));
|
||||
} else {
|
||||
// In Tauri mode, try loading even without 'connected' state
|
||||
loadSkillsCatalog().catch(() => { /* direct invoke fallback handles this */ });
|
||||
}
|
||||
}, [connected]);
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { useConfigStore } from '../../store/configStore';
|
||||
import { silentErrorHandler } from '../../lib/error-utils';
|
||||
|
||||
@@ -8,6 +9,17 @@ export function Workspace() {
|
||||
const loadWorkspaceInfo = useConfigStore((s) => s.loadWorkspaceInfo);
|
||||
const saveQuickConfig = useConfigStore((s) => s.saveQuickConfig);
|
||||
const [projectDir, setProjectDir] = useState('~/.zclaw/zclaw-workspace');
|
||||
const [dirStats, setDirStats] = useState<{ fileCount: number; totalSize: number } | null>(null);
|
||||
|
||||
// Load real directory stats via Tauri command
|
||||
const loadDirStats = async (dir: string) => {
|
||||
try {
|
||||
const stats = await invoke<{ file_count: number; total_size: number }>('workspace_dir_stats', { path: dir });
|
||||
setDirStats({ fileCount: stats.file_count, totalSize: stats.total_size });
|
||||
} catch {
|
||||
// Command may not exist in all modes — fallback to workspaceInfo
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
loadWorkspaceInfo().catch(silentErrorHandler('Workspace'));
|
||||
@@ -22,6 +34,18 @@ export function Workspace() {
|
||||
setProjectDir(nextValue);
|
||||
await saveQuickConfig({ workspaceDir: nextValue });
|
||||
await loadWorkspaceInfo();
|
||||
await loadDirStats(nextValue);
|
||||
};
|
||||
|
||||
const handleBrowse = async () => {
|
||||
// Dialog plugin not available — user types path manually
|
||||
const dir = prompt('输入工作区目录路径:', projectDir);
|
||||
if (dir && dir.trim()) {
|
||||
setProjectDir(dir.trim());
|
||||
await saveQuickConfig({ workspaceDir: dir.trim() });
|
||||
await loadWorkspaceInfo();
|
||||
await loadDirStats(dir.trim());
|
||||
}
|
||||
};
|
||||
|
||||
const handleToggle = async (
|
||||
@@ -51,13 +75,20 @@ export function Workspace() {
|
||||
onBlur={() => { handleWorkspaceBlur().catch(silentErrorHandler('Workspace')); }}
|
||||
className="flex-1 px-3 py-2 border border-gray-200 rounded-lg text-sm bg-gray-50 focus:outline-none"
|
||||
/>
|
||||
<button className="text-xs px-4 py-2 border border-gray-200 rounded-lg hover:bg-gray-50 transition-colors">
|
||||
<button
|
||||
onClick={() => { handleBrowse().catch(silentErrorHandler('Workspace')); }}
|
||||
className="text-xs px-4 py-2 border border-gray-200 rounded-lg hover:bg-gray-50 transition-colors"
|
||||
>
|
||||
浏览
|
||||
</button>
|
||||
</div>
|
||||
<div className="mt-3 space-y-1 text-xs text-gray-500">
|
||||
<div>当前解析路径:{workspaceInfo?.resolvedPath || '未解析'}</div>
|
||||
<div>文件数:{workspaceInfo?.fileCount ?? 0},大小:{workspaceInfo?.totalSize ?? 0} bytes</div>
|
||||
<div>当前解析路径:{workspaceInfo?.resolvedPath || projectDir}</div>
|
||||
<div>
|
||||
文件数:{dirStats?.fileCount ?? workspaceInfo?.fileCount ?? 0}
|
||||
{dirStats && `,大小:${(dirStats.totalSize / 1024).toFixed(1)} KB`}
|
||||
{!dirStats && workspaceInfo?.totalSize ? `,大小:${workspaceInfo.totalSize} bytes` : ''}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -46,9 +46,9 @@ export function VikingPanel() {
|
||||
setStatus(vikingStatus);
|
||||
|
||||
if (vikingStatus.available) {
|
||||
// Load memory count
|
||||
// Load memory count (use empty path — viking_ls('') returns all, viking_ls('/') returns 0)
|
||||
try {
|
||||
const resources = await listVikingResources('/');
|
||||
const resources = await listVikingResources('');
|
||||
setMemoryCount(resources.length);
|
||||
} catch {
|
||||
setMemoryCount(null);
|
||||
|
||||
Reference in New Issue
Block a user