refactor: 统一项目名称从OpenFang到ZCLAW
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
重构所有代码和文档中的项目名称,将OpenFang统一更新为ZCLAW。包括: - 配置文件中的项目名称 - 代码注释和文档引用 - 环境变量和路径 - 类型定义和接口名称 - 测试用例和模拟数据 同时优化部分代码结构,移除未使用的模块,并更新相关依赖项。
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
* ApprovalsPanel - OpenFang Execution Approvals UI
|
||||
* ApprovalsPanel - ZCLAW Execution Approvals UI
|
||||
*
|
||||
* Displays pending, approved, and rejected approval requests
|
||||
* for Hand executions that require human approval.
|
||||
*
|
||||
* Design based on OpenFang Dashboard v0.4.0
|
||||
* Design based on ZCLAW Dashboard v0.4.0
|
||||
*/
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* AuditLogsPanel - OpenFang Audit Logs UI with Merkle Hash Chain Verification
|
||||
* AuditLogsPanel - ZCLAW Audit Logs UI with Merkle Hash Chain Verification
|
||||
*
|
||||
* Phase 3.4 Enhancement: Full-featured audit log viewer with:
|
||||
* - Complete log entry display
|
||||
@@ -51,7 +51,7 @@ export interface AuditLogFilter {
|
||||
}
|
||||
|
||||
interface EnhancedAuditLogEntry extends AuditLogEntry {
|
||||
// Extended fields from OpenFang
|
||||
// Extended fields from ZCLAW
|
||||
targetResource?: string;
|
||||
operationDetails?: Record<string, unknown>;
|
||||
ipAddress?: string;
|
||||
@@ -633,7 +633,7 @@ export function AuditLogsPanel() {
|
||||
setVerificationResult(null);
|
||||
|
||||
try {
|
||||
// Call OpenFang API to verify the chain
|
||||
// Call ZCLAW API to verify the chain
|
||||
const result = await client.verifyAuditLogChain(log.id);
|
||||
|
||||
const verification: MerkleVerificationResult = {
|
||||
|
||||
@@ -42,7 +42,7 @@ export function CloneManager() {
|
||||
role: '默认助手',
|
||||
nickname: a.name,
|
||||
scenarios: [] as string[],
|
||||
workspaceDir: '~/.openfang/zclaw-workspace',
|
||||
workspaceDir: '~/.zclaw/zclaw-workspace',
|
||||
userName: quickConfig.userName || '未设置',
|
||||
userRole: '',
|
||||
restrictFiles: true,
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Displays the current Gateway connection status with visual indicators.
|
||||
* Supports automatic reconnect and manual reconnect button.
|
||||
* Includes health status indicator for OpenFang backend.
|
||||
* Includes health status indicator for ZCLAW backend.
|
||||
*/
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
@@ -230,7 +230,7 @@ export function ConnectionIndicator({ className = '' }: { className?: string })
|
||||
}
|
||||
|
||||
/**
|
||||
* HealthStatusIndicator - Displays OpenFang backend health status
|
||||
* HealthStatusIndicator - Displays ZCLAW backend health status
|
||||
*/
|
||||
export function HealthStatusIndicator({
|
||||
className = '',
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Supports trigger types:
|
||||
* - webhook: External HTTP request trigger
|
||||
* - event: OpenFang internal event trigger
|
||||
* - event: ZCLAW internal event trigger
|
||||
* - message: Chat message pattern trigger
|
||||
*/
|
||||
|
||||
@@ -119,7 +119,7 @@ const triggerTypeOptions: Array<{
|
||||
{
|
||||
value: 'event',
|
||||
label: 'Event',
|
||||
description: 'OpenFang internal event trigger',
|
||||
description: 'ZCLAW internal event trigger',
|
||||
icon: Bell,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -64,7 +64,7 @@ export function HandList({ selectedHandId, onSelectHand }: HandListProps) {
|
||||
<div className="p-4 text-center">
|
||||
<Zap className="w-8 h-8 mx-auto text-gray-300 mb-2" />
|
||||
<p className="text-xs text-gray-400 mb-1">暂无可用 Hands</p>
|
||||
<p className="text-xs text-gray-300">连接 OpenFang 后显示</p>
|
||||
<p className="text-xs text-gray-300">连接 ZCLAW 后显示</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
* HandsPanel - OpenFang Hands Management UI
|
||||
* HandsPanel - ZCLAW Hands Management UI
|
||||
*
|
||||
* Displays available OpenFang Hands (autonomous capability packages)
|
||||
* Displays available ZCLAW Hands (autonomous capability packages)
|
||||
* with detailed status, requirements, and activation controls.
|
||||
*
|
||||
* Design based on OpenFang Dashboard v0.4.0
|
||||
* Design based on ZCLAW Dashboard v0.4.0
|
||||
*/
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
@@ -528,7 +528,7 @@ export function HandsPanel() {
|
||||
</div>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400 mb-3">暂无可用的 Hands</p>
|
||||
<p className="text-xs text-gray-400 dark:text-gray-500">
|
||||
请连接到 OpenFang 以查看可用的自主能力包。
|
||||
请连接到 ZCLAW 以查看可用的自主能力包。
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -441,7 +441,7 @@ export function RightPanel() {
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<AgentRow label="Workspace" value={selectedClone?.workspaceDir || workspaceInfo?.path || '~/.openfang/zclaw-workspace'} />
|
||||
<AgentRow label="Workspace" value={selectedClone?.workspaceDir || workspaceInfo?.path || '~/.zclaw/zclaw-workspace'} />
|
||||
<AgentRow label="Resolved" value={selectedClone?.workspaceResolvedPath || workspaceInfo?.resolvedPath || '-'} />
|
||||
<AgentRow label="File Restriction" value={selectedClone?.restrictFiles ? 'Enabled' : 'Disabled'} />
|
||||
<AgentRow label="Opt-in" value={selectedClone?.privacyOptIn ? 'Joined' : 'Not joined'} />
|
||||
@@ -739,7 +739,7 @@ function createAgentDraft(
|
||||
nickname: clone.nickname || '',
|
||||
model: clone.model || currentModel,
|
||||
scenarios: clone.scenarios?.join(', ') || '',
|
||||
workspaceDir: clone.workspaceDir || '~/.openfang/zclaw-workspace',
|
||||
workspaceDir: clone.workspaceDir || '~/.zclaw/zclaw-workspace',
|
||||
userName: clone.userName || '',
|
||||
userRole: clone.userRole || '',
|
||||
restrictFiles: clone.restrictFiles ?? true,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/**
|
||||
* SchedulerPanel - OpenFang Scheduler UI
|
||||
* SchedulerPanel - ZCLAW Scheduler UI
|
||||
*
|
||||
* Displays scheduled jobs, event triggers, workflows, and run history.
|
||||
*
|
||||
* Design based on OpenFang Dashboard v0.4.0
|
||||
* Design based on ZCLAW Dashboard v0.4.0
|
||||
*/
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
|
||||
@@ -30,7 +30,7 @@ import type { SecurityLayer, SecurityStatus } from '../store/securityStore';
|
||||
import { useSecurityStore } from '../store/securityStore';
|
||||
import { useConnectionStore } from '../store/connectionStore';
|
||||
|
||||
// OpenFang 16-layer security architecture definitions
|
||||
// ZCLAW 16-layer security architecture definitions
|
||||
export const SECURITY_LAYERS: Array<{
|
||||
id: string;
|
||||
name: string;
|
||||
@@ -482,7 +482,7 @@ export function calculateSecurityScore(layers: SecurityLayer[]): number {
|
||||
return Math.round((activeCount / SECURITY_LAYERS.length) * 100);
|
||||
}
|
||||
|
||||
// ZCLAW 默认安全状态(独立于 OpenFang)
|
||||
// ZCLAW 默认安全状态(本地检测)
|
||||
export function getDefaultSecurityStatus(): SecurityStatus {
|
||||
// ZCLAW 默认启用的安全层
|
||||
const defaultEnabledLayers = [
|
||||
@@ -687,7 +687,7 @@ export function SecurityStatusPanel({ className = '' }: SecurityStatusPanelProps
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-xs text-gray-500 mt-1">
|
||||
{!connected && 'ZCLAW 默认安全配置。连接 OpenFang 后可获取完整安全状态。'}
|
||||
{!connected && 'ZCLAW 默认安全配置。连接后可获取实时安全状态。'}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { useEffect } from 'react';
|
||||
import { Shield, ShieldCheck, ShieldAlert, ShieldX, RefreshCw, Loader2, AlertCircle } from 'lucide-react';
|
||||
import { useConnectionStore } from '../store/connectionStore';
|
||||
import { useSecurityStore } from '../store/securityStore';
|
||||
|
||||
// OpenFang 16-layer security architecture names (Chinese)
|
||||
// ZCLAW 16-layer security architecture names (Chinese)
|
||||
const SECURITY_LAYER_NAMES: Record<string, string> = {
|
||||
// Layer 1: Network
|
||||
'network.firewall': '网络防火墙',
|
||||
@@ -76,30 +75,14 @@ function getSecurityLabel(level: 'critical' | 'high' | 'medium' | 'low') {
|
||||
}
|
||||
|
||||
export function SecurityStatus() {
|
||||
const connectionState = useConnectionStore((s) => s.connectionState);
|
||||
const securityStatus = useSecurityStore((s) => s.securityStatus);
|
||||
const securityStatusLoading = useSecurityStore((s) => s.securityStatusLoading);
|
||||
const securityStatusError = useSecurityStore((s) => s.securityStatusError);
|
||||
const loadSecurityStatus = useSecurityStore((s) => s.loadSecurityStatus);
|
||||
const connected = connectionState === 'connected';
|
||||
|
||||
useEffect(() => {
|
||||
if (connected) {
|
||||
loadSecurityStatus();
|
||||
}
|
||||
}, [connected]);
|
||||
|
||||
if (!connected) {
|
||||
return (
|
||||
<div className="rounded-xl border border-gray-200 bg-white p-4 shadow-sm">
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
<Shield className="w-4 h-4 text-gray-400" />
|
||||
<span className="text-sm font-semibold text-gray-900">安全状态</span>
|
||||
</div>
|
||||
<p className="text-xs text-gray-400">连接后可用</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
loadSecurityStatus();
|
||||
}, [loadSecurityStatus]);
|
||||
|
||||
// Loading state
|
||||
if (securityStatusLoading && !securityStatus) {
|
||||
@@ -131,9 +114,9 @@ export function SecurityStatus() {
|
||||
<RefreshCw className="w-3.5 h-3.5" />
|
||||
</button>
|
||||
</div>
|
||||
<p className="text-xs text-gray-500 mb-2">API 不可用</p>
|
||||
<p className="text-xs text-gray-500 mb-2">安全状态检测失败</p>
|
||||
<p className="text-xs text-gray-400">
|
||||
OpenFang 安全状态 API ({'/api/security/status'}) 在当前版本可能未实现
|
||||
本地安全检测模块加载失败,请检查安全组件是否正确初始化
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -34,10 +34,10 @@ export function About() {
|
||||
</div>
|
||||
|
||||
<div className="mt-12 text-center text-xs text-gray-400">
|
||||
2026 ZCLAW | Powered by OpenFang
|
||||
2026 ZCLAW
|
||||
</div>
|
||||
<div className="text-center text-xs text-gray-400 space-y-1">
|
||||
<p>基于 OpenFang Rust Agent OS 构建</p>
|
||||
<p>基于 Rust Agent OS 构建</p>
|
||||
<div className="flex justify-center gap-4 mt-3">
|
||||
<a href="#" className="text-orange-500 hover:text-orange-600">隐私政策</a>
|
||||
<a href="#" className="text-orange-500 hover:text-orange-600">用户协议</a>
|
||||
|
||||
@@ -382,7 +382,7 @@ export function IMChannels() {
|
||||
<div className="text-xs text-blue-700 dark:text-blue-300">
|
||||
<p className="font-medium mb-1">高级配置</p>
|
||||
<p>账号绑定、消息路由等高级功能需要在 Gateway 配置文件中完成。</p>
|
||||
<p className="mt-1">配置文件路径: <code className="bg-blue-100 dark:bg-blue-800 px-1 rounded">~/.openfang/openfang.toml</code></p>
|
||||
<p className="mt-1">配置文件路径: <code className="bg-blue-100 dark:bg-blue-800 px-1 rounded">~/.zclaw/zclaw.toml</code></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -266,13 +266,30 @@ export function ModelsAPI() {
|
||||
};
|
||||
|
||||
// 保存 Embedding 配置
|
||||
const handleSaveEmbeddingConfig = () => {
|
||||
const handleSaveEmbeddingConfig = async () => {
|
||||
const configToSave = {
|
||||
...embeddingConfig,
|
||||
enabled: embeddingConfig.provider !== 'local' && embeddingConfig.apiKey.trim() !== '',
|
||||
};
|
||||
setEmbeddingConfig(configToSave);
|
||||
saveEmbeddingConfig(configToSave);
|
||||
|
||||
// Push config to Rust backend for semantic memory search
|
||||
if (configToSave.enabled) {
|
||||
try {
|
||||
await invoke('viking_configure_embedding', {
|
||||
provider: configToSave.provider,
|
||||
apiKey: configToSave.apiKey,
|
||||
model: configToSave.model || undefined,
|
||||
endpoint: configToSave.endpoint || undefined,
|
||||
});
|
||||
setEmbeddingTestResult({ success: true, message: 'Embedding 配置已应用到语义记忆搜索' });
|
||||
} catch (error) {
|
||||
setEmbeddingTestResult({ success: false, message: `配置保存成功但应用失败: ${error}` });
|
||||
}
|
||||
} else {
|
||||
setEmbeddingTestResult(null);
|
||||
}
|
||||
};
|
||||
|
||||
// 测试 Embedding API
|
||||
|
||||
@@ -24,7 +24,7 @@ export function Privacy() {
|
||||
<h3 className="font-medium mb-2 text-gray-900">本地数据路径</h3>
|
||||
<div className="text-xs text-gray-500 mb-3">所有工作区文件、对话记录和 Agent 输出均存储在此本地目录。</div>
|
||||
<div className="p-3 bg-gray-50 border border-gray-200 rounded-lg text-xs text-gray-600 font-mono">
|
||||
{workspaceInfo?.resolvedPath || workspaceInfo?.path || quickConfig.workspaceDir || '~/.openfang/zclaw-workspace'}
|
||||
{workspaceInfo?.resolvedPath || workspaceInfo?.path || quickConfig.workspaceDir || '~/.zclaw/zclaw-workspace'}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,19 +1,15 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useAgentStore } from '../../store/agentStore';
|
||||
import { useConnectionStore } from '../../store/connectionStore';
|
||||
import { BarChart3, TrendingUp, Clock, Zap } from 'lucide-react';
|
||||
|
||||
export function UsageStats() {
|
||||
const usageStats = useAgentStore((s) => s.usageStats);
|
||||
const loadUsageStats = useAgentStore((s) => s.loadUsageStats);
|
||||
const connectionState = useConnectionStore((s) => s.connectionState);
|
||||
const [timeRange, setTimeRange] = useState<'7d' | '30d' | 'all'>('7d');
|
||||
|
||||
useEffect(() => {
|
||||
if (connectionState === 'connected') {
|
||||
loadUsageStats();
|
||||
}
|
||||
}, [connectionState]);
|
||||
loadUsageStats();
|
||||
}, [loadUsageStats]);
|
||||
|
||||
const stats = usageStats || { totalSessions: 0, totalMessages: 0, totalTokens: 0, byModel: {} };
|
||||
const models = Object.entries(stats.byModel || {});
|
||||
@@ -56,7 +52,7 @@ export function UsageStats() {
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-xs text-gray-500 mb-4">本设备所有已保存对话的 Token 用量汇总。</div>
|
||||
<div className="text-xs text-gray-500 mb-4">本设备所有已保存对话的使用统计。</div>
|
||||
|
||||
{/* 主要统计卡片 */}
|
||||
<div className="grid grid-cols-4 gap-4 mb-8">
|
||||
@@ -89,6 +85,9 @@ export function UsageStats() {
|
||||
{/* 总 Token 使用量概览 */}
|
||||
<div className="bg-white rounded-xl border border-gray-200 p-5 shadow-sm mb-6">
|
||||
<h3 className="text-sm font-semibold mb-4 text-gray-900">Token 使用概览</h3>
|
||||
{stats.totalTokens === 0 ? (
|
||||
<p className="text-xs text-gray-400">Token 用量将在后续版本中支持</p>
|
||||
) : (
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex-1">
|
||||
<div className="flex justify-between text-xs text-gray-500 mb-1">
|
||||
@@ -111,6 +110,7 @@ export function UsageStats() {
|
||||
<div className="text-xs text-gray-500">总计</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 按模型分组 */}
|
||||
|
||||
@@ -7,18 +7,18 @@ export function Workspace() {
|
||||
const workspaceInfo = useConfigStore((s) => s.workspaceInfo);
|
||||
const loadWorkspaceInfo = useConfigStore((s) => s.loadWorkspaceInfo);
|
||||
const saveQuickConfig = useConfigStore((s) => s.saveQuickConfig);
|
||||
const [projectDir, setProjectDir] = useState('~/.openfang/zclaw-workspace');
|
||||
const [projectDir, setProjectDir] = useState('~/.zclaw/zclaw-workspace');
|
||||
|
||||
useEffect(() => {
|
||||
loadWorkspaceInfo().catch(silentErrorHandler('Workspace'));
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setProjectDir(quickConfig.workspaceDir || workspaceInfo?.path || '~/.openfang/zclaw-workspace');
|
||||
setProjectDir(quickConfig.workspaceDir || workspaceInfo?.path || '~/.zclaw/zclaw-workspace');
|
||||
}, [quickConfig.workspaceDir, workspaceInfo?.path]);
|
||||
|
||||
const handleWorkspaceBlur = async () => {
|
||||
const nextValue = projectDir.trim() || '~/.openfang/zclaw-workspace';
|
||||
const nextValue = projectDir.trim() || '~/.zclaw/zclaw-workspace';
|
||||
setProjectDir(nextValue);
|
||||
await saveQuickConfig({ workspaceDir: nextValue });
|
||||
await loadWorkspaceInfo();
|
||||
|
||||
@@ -375,8 +375,10 @@ export function SkillMarket({
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Suggestions - placeholder for future AI-powered recommendations */}
|
||||
|
||||
{/* AI 智能推荐功能开发中 */}
|
||||
<div className="text-xs text-gray-400 dark:text-gray-500 text-center py-1">
|
||||
AI 智能推荐即将推出
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Category Filter */}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* TriggersPanel - OpenFang Triggers Management UI
|
||||
* TriggersPanel - ZCLAW Triggers Management UI
|
||||
*
|
||||
* Displays available OpenFang Triggers and allows creating and toggling them.
|
||||
* Displays available ZCLAW Triggers and allows creating and toggling them.
|
||||
*/
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/**
|
||||
* VikingPanel - OpenViking Semantic Memory UI
|
||||
* VikingPanel - ZCLAW Semantic Memory UI
|
||||
*
|
||||
* Provides interface for semantic search and knowledge base management.
|
||||
* OpenViking is an optional sidecar for semantic memory operations.
|
||||
* Uses native Rust SqliteStorage with TF-IDF semantic search.
|
||||
*/
|
||||
import { useState, useEffect } from 'react';
|
||||
import {
|
||||
@@ -11,16 +11,13 @@ import {
|
||||
AlertCircle,
|
||||
CheckCircle,
|
||||
FileText,
|
||||
Server,
|
||||
Play,
|
||||
Square,
|
||||
Database,
|
||||
} from 'lucide-react';
|
||||
import {
|
||||
getVikingStatus,
|
||||
findVikingResources,
|
||||
getVikingServerStatus,
|
||||
startVikingServer,
|
||||
stopVikingServer,
|
||||
listVikingResources,
|
||||
readVikingResource,
|
||||
} from '../lib/viking-client';
|
||||
import type { VikingStatus, VikingFindResult } from '../lib/viking-client';
|
||||
|
||||
@@ -30,17 +27,28 @@ export function VikingPanel() {
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [searchResults, setSearchResults] = useState<VikingFindResult[]>([]);
|
||||
const [isSearching, setIsSearching] = useState(false);
|
||||
const [serverRunning, setServerRunning] = useState(false);
|
||||
const [message, setMessage] = useState<{ type: 'success' | 'error'; text: string } | null>(null);
|
||||
const [memoryCount, setMemoryCount] = useState<number | null>(null);
|
||||
const [expandedUri, setExpandedUri] = useState<string | null>(null);
|
||||
const [expandedContent, setExpandedContent] = useState<string | null>(null);
|
||||
const [isLoadingL2, setIsLoadingL2] = useState(false);
|
||||
|
||||
const loadStatus = async () => {
|
||||
setIsLoading(true);
|
||||
setMessage(null);
|
||||
try {
|
||||
const vikingStatus = await getVikingStatus();
|
||||
setStatus(vikingStatus);
|
||||
|
||||
const serverStatus = await getVikingServerStatus();
|
||||
setServerRunning(serverStatus.running);
|
||||
if (vikingStatus.available) {
|
||||
// Load memory count
|
||||
try {
|
||||
const resources = await listVikingResources('/');
|
||||
setMemoryCount(resources.length);
|
||||
} catch {
|
||||
setMemoryCount(null);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load Viking status:', error);
|
||||
setStatus({ available: false, error: String(error) });
|
||||
@@ -74,22 +82,22 @@ export function VikingPanel() {
|
||||
}
|
||||
};
|
||||
|
||||
const handleServerToggle = async () => {
|
||||
const handleExpandL2 = async (uri: string) => {
|
||||
if (expandedUri === uri) {
|
||||
setExpandedUri(null);
|
||||
setExpandedContent(null);
|
||||
return;
|
||||
}
|
||||
|
||||
setExpandedUri(uri);
|
||||
setIsLoadingL2(true);
|
||||
try {
|
||||
if (serverRunning) {
|
||||
await stopVikingServer();
|
||||
setServerRunning(false);
|
||||
setMessage({ type: 'success', text: '服务器已停止' });
|
||||
} else {
|
||||
await startVikingServer();
|
||||
setServerRunning(true);
|
||||
setMessage({ type: 'success', text: '服务器已启动' });
|
||||
}
|
||||
} catch (error) {
|
||||
setMessage({
|
||||
type: 'error',
|
||||
text: `操作失败: ${error instanceof Error ? error.message : '未知错误'}`,
|
||||
});
|
||||
const fullContent = await readVikingResource(uri, 'L2');
|
||||
setExpandedContent(fullContent);
|
||||
} catch {
|
||||
setExpandedContent(null);
|
||||
} finally {
|
||||
setIsLoadingL2(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -100,7 +108,7 @@ export function VikingPanel() {
|
||||
<div>
|
||||
<h1 className="text-xl font-bold text-gray-900 dark:text-white">语义记忆</h1>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400 mt-1">
|
||||
OpenViking 语义搜索引擎
|
||||
ZCLAW 语义记忆搜索引擎
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex gap-2 items-center">
|
||||
@@ -125,10 +133,9 @@ export function VikingPanel() {
|
||||
<div className="flex items-start gap-2">
|
||||
<AlertCircle className="w-4 h-4 text-amber-500 mt-0.5" />
|
||||
<div className="text-xs text-amber-700 dark:text-amber-300">
|
||||
<p className="font-medium">OpenViking CLI 不可用</p>
|
||||
<p className="font-medium">语义记忆存储不可用</p>
|
||||
<p className="mt-1">
|
||||
请安装 OpenViking CLI 或设置{' '}
|
||||
<code className="bg-amber-100 dark:bg-amber-800 px-1 rounded">ZCLAW_VIKING_BIN</code> 环境变量。
|
||||
本地 SQLite 存储初始化失败。请检查数据目录权限后重启应用。
|
||||
</p>
|
||||
{status?.error && (
|
||||
<p className="mt-1 text-amber-600 dark:text-amber-400 font-mono text-xs">
|
||||
@@ -158,47 +165,37 @@ export function VikingPanel() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Server Control */}
|
||||
{/* Storage Info */}
|
||||
{status?.available && (
|
||||
<div className="bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 p-4 mb-6 shadow-sm">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<div
|
||||
className={`w-10 h-10 rounded-xl flex items-center justify-center ${
|
||||
serverRunning
|
||||
? 'bg-gradient-to-br from-green-500 to-emerald-500 text-white'
|
||||
: 'bg-gray-200 dark:bg-gray-700 text-gray-400'
|
||||
}`}
|
||||
>
|
||||
<Server className="w-4 h-4" />
|
||||
<div className="flex items-center gap-3 mb-3">
|
||||
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-blue-500 to-indigo-500 flex items-center justify-center">
|
||||
<Database className="w-4 h-4 text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-sm font-medium text-gray-900 dark:text-white">
|
||||
本地存储
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-sm font-medium text-gray-900 dark:text-white">
|
||||
Viking Server
|
||||
</div>
|
||||
<div className="text-xs text-gray-500 dark:text-gray-400">
|
||||
{serverRunning ? '运行中' : '已停止'}
|
||||
</div>
|
||||
<div className="text-xs text-gray-500 dark:text-gray-400">
|
||||
{status.version || 'Native'} · {status.dataDir || '默认路径'}
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={handleServerToggle}
|
||||
className={`px-4 py-2 rounded-lg flex items-center gap-2 text-sm transition-colors ${
|
||||
serverRunning
|
||||
? 'bg-red-100 text-red-600 hover:bg-red-200 dark:bg-red-900/30 dark:text-red-400'
|
||||
: 'bg-green-100 text-green-600 hover:bg-green-200 dark:bg-green-900/30 dark:text-green-400'
|
||||
}`}
|
||||
>
|
||||
{serverRunning ? (
|
||||
<>
|
||||
<Square className="w-4 h-4" /> 停止
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Play className="w-4 h-4" /> 启动
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex gap-4 text-xs">
|
||||
<div className="flex items-center gap-1.5 text-gray-600 dark:text-gray-300">
|
||||
<CheckCircle className="w-3.5 h-3.5 text-green-500" />
|
||||
<span>SQLite + FTS5</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1.5 text-gray-600 dark:text-gray-300">
|
||||
<CheckCircle className="w-3.5 h-3.5 text-green-500" />
|
||||
<span>TF-IDF 语义评分</span>
|
||||
</div>
|
||||
{memoryCount !== null && (
|
||||
<div className="flex items-center gap-1.5 text-gray-600 dark:text-gray-300">
|
||||
<CheckCircle className="w-3.5 h-3.5 text-green-500" />
|
||||
<span>{memoryCount} 条记忆</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@@ -251,21 +248,43 @@ export function VikingPanel() {
|
||||
<span className="text-sm font-medium text-gray-900 dark:text-white truncate">
|
||||
{result.uri}
|
||||
</span>
|
||||
<span className="text-xs text-gray-400 bg-gray-100 dark:bg-gray-700 px-2 py-0.5 rounded">
|
||||
<span className={`text-xs px-2 py-0.5 rounded ${
|
||||
result.level === 'L1'
|
||||
? 'text-green-600 bg-green-100 dark:bg-green-900/30 dark:text-green-400'
|
||||
: 'text-gray-400 bg-gray-100 dark:bg-gray-700'
|
||||
}`}>
|
||||
{result.level}
|
||||
</span>
|
||||
<span className="text-xs text-blue-600 dark:text-blue-400">
|
||||
{Math.round(result.score * 100)}%
|
||||
</span>
|
||||
</div>
|
||||
{result.overview && (
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400 mt-1 line-clamp-2">
|
||||
{result.overview}
|
||||
</p>
|
||||
)}
|
||||
<p className="text-xs text-gray-600 dark:text-gray-300 mt-2 line-clamp-3 font-mono">
|
||||
<p className="text-xs text-gray-600 dark:text-gray-300 mt-2 line-clamp-3">
|
||||
{result.content}
|
||||
</p>
|
||||
{result.level === 'L1' && (
|
||||
<button
|
||||
onClick={() => handleExpandL2(result.uri)}
|
||||
className="mt-1.5 text-xs text-blue-500 hover:text-blue-600 dark:text-blue-400 dark:hover:text-blue-300 transition-colors"
|
||||
>
|
||||
{expandedUri === result.uri ? '收起全文' : '展开全文'}
|
||||
</button>
|
||||
)}
|
||||
{expandedUri === result.uri && (
|
||||
<div className="mt-2 p-3 bg-gray-50 dark:bg-gray-900/50 rounded-lg border border-gray-200 dark:border-gray-700">
|
||||
{isLoadingL2 ? (
|
||||
<div className="flex items-center gap-2 text-xs text-gray-400">
|
||||
<RefreshCw className="w-3 h-3 animate-spin" /> 加载中...
|
||||
</div>
|
||||
) : expandedContent ? (
|
||||
<p className="text-xs text-gray-600 dark:text-gray-300 whitespace-pre-wrap font-mono">
|
||||
{expandedContent}
|
||||
</p>
|
||||
) : (
|
||||
<p className="text-xs text-gray-400">加载失败</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -275,11 +294,11 @@ export function VikingPanel() {
|
||||
|
||||
{/* Info Section */}
|
||||
<div className="mt-6 p-4 bg-gray-50 dark:bg-gray-800/50 rounded-lg border border-gray-200 dark:border-gray-700">
|
||||
<h3 className="text-sm font-medium text-gray-900 dark:text-white mb-2">关于 OpenViking</h3>
|
||||
<h3 className="text-sm font-medium text-gray-900 dark:text-white mb-2">关于语义记忆</h3>
|
||||
<ul className="text-xs text-gray-500 dark:text-gray-400 space-y-1">
|
||||
<li>• 语义搜索引擎,支持自然语言查询</li>
|
||||
<li>• 自动提取和索引知识资源</li>
|
||||
<li>• 支持多种文档格式和代码文件</li>
|
||||
<li>• 基于本地 SQLite + TF-IDF 的语义搜索引擎</li>
|
||||
<li>• 自动提取和索引对话中的知识资源</li>
|
||||
<li>• 支持自然语言查询知识库</li>
|
||||
<li>• 可作为本地知识库增强 AI 对话</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
* WorkflowEditor - OpenFang Workflow Editor Component
|
||||
* WorkflowEditor - ZCLAW Workflow Editor Component
|
||||
*
|
||||
* Allows creating and editing multi-step workflows that chain
|
||||
* multiple Hands together for complex task automation.
|
||||
*
|
||||
* Design based on OpenFang Dashboard v0.4.0
|
||||
* Design based on ZCLAW Dashboard v0.4.0
|
||||
*/
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
* WorkflowHistory - OpenFang Workflow Execution History Component
|
||||
* WorkflowHistory - ZCLAW Workflow Execution History Component
|
||||
*
|
||||
* Displays the execution history of a specific workflow,
|
||||
* showing run details, status, and results.
|
||||
*
|
||||
* Design based on OpenFang Dashboard v0.4.0
|
||||
* Design based on ZCLAW Dashboard v0.4.0
|
||||
*/
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
/**
|
||||
* WorkflowList - OpenFang Workflow Management UI
|
||||
* WorkflowList - ZCLAW Workflow Management UI
|
||||
*
|
||||
* Displays available OpenFang Workflows and allows executing them.
|
||||
* Displays available ZCLAW Workflows and allows executing them.
|
||||
*
|
||||
* Design based on OpenFang Dashboard v0.4.0
|
||||
* Design based on ZCLAW Dashboard v0.4.0
|
||||
*/
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { useWorkflowStore, type Workflow } from '../store/workflowStore';
|
||||
import { WorkflowEditor } from './WorkflowEditor';
|
||||
import { WorkflowHistory } from './WorkflowHistory';
|
||||
import { WorkflowBuilder } from './WorkflowBuilder';
|
||||
import {
|
||||
Play,
|
||||
Edit,
|
||||
@@ -467,18 +468,8 @@ export function WorkflowList() {
|
||||
</div>
|
||||
)
|
||||
) : (
|
||||
// Visual Builder View (placeholder)
|
||||
<div className="p-8 text-center bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700">
|
||||
<div className="w-12 h-12 bg-gray-100 dark:bg-gray-700 rounded-full flex items-center justify-center mx-auto mb-3">
|
||||
<GitBranch className="w-6 h-6 text-gray-400" />
|
||||
</div>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400 mb-2">
|
||||
可视化工作流编辑器
|
||||
</p>
|
||||
<p className="text-xs text-gray-400 dark:text-gray-500">
|
||||
拖拽式工作流编辑器即将推出!
|
||||
</p>
|
||||
</div>
|
||||
// Visual Builder View
|
||||
<WorkflowBuilder />
|
||||
)}
|
||||
|
||||
{/* Execute Modal */}
|
||||
|
||||
Reference in New Issue
Block a user