From 58aca753aa84eff05e4c22bf247bcf9dc1415c66 Mon Sep 17 00:00:00 2001 From: iven Date: Sat, 11 Apr 2026 23:51:58 +0800 Subject: [PATCH] =?UTF-8?q?chore:=20=E5=8F=91=E5=B8=83=E5=89=8D=E5=87=86?= =?UTF-8?q?=E5=A4=87=20=E2=80=94=20=E7=89=88=E6=9C=AC=E5=8F=B7=E7=BB=9F?= =?UTF-8?q?=E4=B8=80=20+=20=E5=AE=89=E5=85=A8=E5=8A=A0=E5=9B=BA=20+=20?= =?UTF-8?q?=E6=AD=BB=E7=BB=84=E4=BB=B6=E6=B8=85=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Cargo.toml workspace version 0.1.0 → 0.9.0-beta.1 - CSP 添加 object-src 'none' 防止插件注入 - .env.example 补充 SaaS 关键环境变量模板 - 移除已废弃的 SkillMarket.tsx 组件 --- .env.example | 9 + Cargo.lock | 22 +- Cargo.toml | 2 +- desktop/src-tauri/tauri.conf.json | 2 +- desktop/src/components/SkillMarket.tsx | 427 ------------------------- 5 files changed, 22 insertions(+), 440 deletions(-) delete mode 100644 desktop/src/components/SkillMarket.tsx diff --git a/.env.example b/.env.example index 11fb93e..51de39c 100644 --- a/.env.example +++ b/.env.example @@ -44,3 +44,12 @@ ZCLAW_EMBEDDING_MODEL=text-embedding-3-small # === Logging === # 可选: debug, info, warn, error ZCLAW_LOG_LEVEL=info + +# === SaaS Backend === +ZCLAW_SAAS_JWT_SECRET= +ZCLAW_TOTP_ENCRYPTION_KEY= +ZCLAW_ADMIN_USERNAME= +ZCLAW_ADMIN_PASSWORD= +DB_PASSWORD= +ZCLAW_DATABASE_URL= +ZCLAW_SAAS_DEV=false diff --git a/Cargo.lock b/Cargo.lock index 3e53d22..80ab2d3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1526,7 +1526,7 @@ dependencies = [ [[package]] name = "desktop" -version = "0.1.0" +version = "0.9.0-beta.1" dependencies = [ "aes-gcm", "async-trait", @@ -9421,7 +9421,7 @@ dependencies = [ [[package]] name = "zclaw-growth" -version = "0.1.0" +version = "0.9.0-beta.1" dependencies = [ "anyhow", "async-trait", @@ -9442,7 +9442,7 @@ dependencies = [ [[package]] name = "zclaw-hands" -version = "0.1.0" +version = "0.9.0-beta.1" dependencies = [ "async-trait", "base64 0.22.1", @@ -9460,7 +9460,7 @@ dependencies = [ [[package]] name = "zclaw-kernel" -version = "0.1.0" +version = "0.9.0-beta.1" dependencies = [ "async-trait", "chrono", @@ -9488,7 +9488,7 @@ dependencies = [ [[package]] name = "zclaw-memory" -version = "0.1.0" +version = "0.9.0-beta.1" dependencies = [ "anyhow", "async-trait", @@ -9507,7 +9507,7 @@ dependencies = [ [[package]] name = "zclaw-pipeline" -version = "0.1.0" +version = "0.9.0-beta.1" dependencies = [ "anyhow", "async-trait", @@ -9532,7 +9532,7 @@ dependencies = [ [[package]] name = "zclaw-protocols" -version = "0.1.0" +version = "0.9.0-beta.1" dependencies = [ "async-trait", "reqwest 0.12.28", @@ -9547,7 +9547,7 @@ dependencies = [ [[package]] name = "zclaw-runtime" -version = "0.1.0" +version = "0.9.0-beta.1" dependencies = [ "async-stream", "async-trait", @@ -9579,7 +9579,7 @@ dependencies = [ [[package]] name = "zclaw-saas" -version = "0.1.0" +version = "0.9.0-beta.1" dependencies = [ "aes-gcm", "anyhow", @@ -9627,7 +9627,7 @@ dependencies = [ [[package]] name = "zclaw-skills" -version = "0.1.0" +version = "0.9.0-beta.1" dependencies = [ "async-trait", "regex", @@ -9645,7 +9645,7 @@ dependencies = [ [[package]] name = "zclaw-types" -version = "0.1.0" +version = "0.9.0-beta.1" dependencies = [ "chrono", "serde", diff --git a/Cargo.toml b/Cargo.toml index b844d90..8b80ec2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ members = [ ] [workspace.package] -version = "0.1.0" +version = "0.9.0-beta.1" edition = "2021" license = "Apache-2.0 OR MIT" repository = "https://github.com/zclaw/zclaw" diff --git a/desktop/src-tauri/tauri.conf.json b/desktop/src-tauri/tauri.conf.json index 09f4dc6..0e94ede 100644 --- a/desktop/src-tauri/tauri.conf.json +++ b/desktop/src-tauri/tauri.conf.json @@ -21,7 +21,7 @@ } ], "security": { - "csp": "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; font-src 'self'; img-src 'self' asset: https://asset.localhost data: blob:; connect-src ipc: http://ipc.localhost http://localhost:* https://*; frame-ancestors 'none'; base-uri 'self'; form-action 'self'" + "csp": "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; font-src 'self'; img-src 'self' asset: https://asset.localhost data: blob:; connect-src ipc: http://ipc.localhost http://localhost:* https://*; object-src 'none'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'" } }, "bundle": { diff --git a/desktop/src/components/SkillMarket.tsx b/desktop/src/components/SkillMarket.tsx deleted file mode 100644 index 64bd4ff..0000000 --- a/desktop/src/components/SkillMarket.tsx +++ /dev/null @@ -1,427 +0,0 @@ -/** - * SkillMarket - Skill browsing, search, and management UI - * - * Displays available skills (12 built-in + custom), allows users to: - * - Browse skills by category - * - Search skills by keyword/capability - * - View skill details and capabilities - * - Install/uninstall skills (with L4 autonomy integration) - * - * Part of ZCLAW L4 Self-Evolution capability. - */ - -import { useState, useEffect, useMemo, useCallback } from 'react'; -import { motion, AnimatePresence } from 'framer-motion'; -import { - Search, - Package, - Check, - Plus, - Minus, - Tag, - Layers, - ChevronDown, - ChevronRight, - RefreshCw, -} from 'lucide-react'; -import { useConfigStore } from '../store/configStore'; -import { useConnectionStore } from '../store/connectionStore'; -import { - adaptSkillsCatalog, - type SkillDisplay, -} from '../lib/skill-adapter'; - -// === Types === - -interface SkillMarketProps { - className?: string; - onSkillInstall?: (skill: SkillDisplay) => void; - onSkillUninstall?: (skill: SkillDisplay) => void; -} - -type CategoryFilter = 'all' | 'development' | 'security' | 'analytics' | 'content' | 'ops' | 'management' | 'testing' | 'business' | 'marketing'; - -// === Category Config === - -const CATEGORY_CONFIG: Record = { - development: { label: '开发', color: 'text-blue-600 dark:text-blue-400', bgColor: 'bg-blue-100 dark:bg-blue-900/30' }, - security: { label: '安全', color: 'text-red-600 dark:text-red-400', bgColor: 'bg-red-100 dark:bg-red-900/30' }, - analytics: { label: '分析', color: 'text-purple-600 dark:text-purple-400', bgColor: 'bg-purple-100 dark:bg-purple-900/30' }, - content: { label: '内容', color: 'text-pink-600 dark:text-pink-400', bgColor: 'bg-pink-100 dark:bg-pink-900/30' }, - ops: { label: '运维', color: 'text-orange-600 dark:text-orange-400', bgColor: 'bg-orange-100 dark:bg-orange-900/30' }, - management: { label: '管理', color: 'text-cyan-600 dark:text-cyan-400', bgColor: 'bg-cyan-100 dark:bg-cyan-900/30' }, - testing: { label: '测试', color: 'text-green-600 dark:text-green-400', bgColor: 'bg-green-100 dark:bg-green-900/30' }, - business: { label: '商业', color: 'text-yellow-600 dark:text-yellow-400', bgColor: 'bg-yellow-100 dark:bg-yellow-900/30' }, - marketing: { label: '营销', color: 'text-indigo-600 dark:text-indigo-400', bgColor: 'bg-indigo-100 dark:bg-indigo-900/30' }, -}; - -// === Components === - -function CategoryBadge({ category }: { category?: string }) { - if (!category) return null; - const config = CATEGORY_CONFIG[category] || { - label: category, - color: 'text-gray-600 dark:text-gray-400', - bgColor: 'bg-gray-100 dark:bg-gray-800', - }; - return ( - - - {config.label} - - ); -} - -function SkillCard({ - skill, - isExpanded, - onToggle, - onInstall, - onUninstall, -}: { - skill: SkillDisplay; - isExpanded: boolean; - onToggle: () => void; - onInstall: () => void; - onUninstall: () => void; -}) { - const config = CATEGORY_CONFIG[skill.category || ''] || CATEGORY_CONFIG.development; - - return ( -
- - - - {isExpanded && ( - -
- {/* Triggers */} -
-

- 触发词 -

-
- {skill.triggers.map((trigger) => ( - - {trigger} - - ))} -
-
- - {/* Capabilities */} -
-

- 能力 -

-
- {skill.capabilities.map((cap) => ( - - {cap} - - ))} -
-
- - {/* Tool Dependencies */} - {skill.toolDeps.length > 0 && ( -
-

- 工具依赖 -

-
- {skill.toolDeps.map((dep) => ( - - {dep} - - ))} -
-
- )} - - {/* Actions */} -
- {skill.installed ? ( - - ) : ( - - )} -
-
-
- )} -
-
- ); -} - -// === Main Component === - -export function SkillMarket({ - className = '', - onSkillInstall, - onSkillUninstall, -}: SkillMarketProps) { - // Use configStore instead of SkillDiscoveryEngine - const skillsCatalog = useConfigStore((s) => s.skillsCatalog); - const loadSkillsCatalog = useConfigStore((s) => s.loadSkillsCatalog); - const updateSkill = useConfigStore((s) => s.updateSkill); - - // Watch connection state to reload skills when connected - const connectionState = useConnectionStore((s) => s.connectionState); - - const [searchQuery, setSearchQuery] = useState(''); - const [categoryFilter, setCategoryFilter] = useState('all'); - const [expandedSkillId, setExpandedSkillId] = useState(null); - const [isRefreshing, setIsRefreshing] = useState(false); - - // Adapt skills to display format - const skills = useMemo(() => adaptSkillsCatalog(skillsCatalog), [skillsCatalog]); - - // Load skills on mount and when connection state changes to 'connected' - useEffect(() => { - if (connectionState === 'connected') { - loadSkillsCatalog(); - } - }, [loadSkillsCatalog, connectionState]); - - // Filter skills - const filteredSkills = useMemo(() => { - let result = skills; - - // Category filter - if (categoryFilter !== 'all') { - result = result.filter((s) => s.category === categoryFilter); - } - - // Search filter - if (searchQuery.trim()) { - const queryLower = searchQuery.toLowerCase(); - result = result.filter((s) => - s.name.toLowerCase().includes(queryLower) || - s.description.toLowerCase().includes(queryLower) || - s.triggers.some((t) => t.toLowerCase().includes(queryLower)) || - s.capabilities.some((c) => c.toLowerCase().includes(queryLower)) - ); - } - - return result; - }, [skills, categoryFilter, searchQuery]); - - // Get categories from skills - const categories = useMemo(() => { - const cats = new Set(skills.map((s) => s.category).filter(Boolean)); - return ['all', ...Array.from(cats)] as CategoryFilter[]; - }, [skills]); - - // Stats - const stats = useMemo(() => { - const installed = skills.filter((s) => s.installed).length; - return { total: skills.length, installed }; - }, [skills]); - - const handleRefresh = useCallback(async () => { - setIsRefreshing(true); - await loadSkillsCatalog(); - setIsRefreshing(false); - }, [loadSkillsCatalog]); - - const handleInstall = useCallback( - async (skill: SkillDisplay) => { - // Update skill via configStore (persists to backend) - await updateSkill(skill.id, { enabled: true }); - onSkillInstall?.(skill); - }, - [updateSkill, onSkillInstall] - ); - - const handleUninstall = useCallback( - async (skill: SkillDisplay) => { - // Update skill via configStore (persists to backend) - await updateSkill(skill.id, { enabled: false }); - onSkillUninstall?.(skill); - }, - [updateSkill, onSkillUninstall] - ); - - const handleSearch = useCallback((query: string) => { - setSearchQuery(query); - }, []); - - return ( -
- {/* Header */} -
-
- -

技能市场

-
-
- -
-
- - {/* Stats Bar */} -
- - 总计: {stats.total} - - - 已安装: {stats.installed} - -
- - {/* Search */} -
-
- - handleSearch(e.target.value)} - placeholder="搜索技能、能力、触发词..." - className="w-full pl-9 pr-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-gray-400 focus:border-transparent text-sm" - /> -
- - {/* AI 智能推荐功能开发中 */} -
- AI 智能推荐即将推出 -
-
- - {/* Category Filter */} -
- {categories.map((cat) => ( - - ))} -
- - {/* Skill List */} -
- {filteredSkills.length === 0 ? ( -
- -

- {searchQuery ? '未找到匹配的技能' : '暂无技能'} -

-
- ) : ( - filteredSkills.map((skill) => ( - setExpandedSkillId((prev) => (prev === skill.id ? null : skill.id))} - onInstall={() => handleInstall(skill)} - onUninstall={() => handleUninstall(skill)} - /> - )) - )} -
-
- ); -} - -export default SkillMarket;