work-agent 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +234 -0
- package/app/(admin)/approvals/page.tsx +16 -0
- package/app/(admin)/audit/page.tsx +18 -0
- package/app/(admin)/layout.tsx +47 -0
- package/app/(admin)/scheduled-tasks/page.tsx +17 -0
- package/app/(admin)/settings/page.tsx +46 -0
- package/app/(admin)/skills/[name]/page.tsx +378 -0
- package/app/(admin)/skills/page.tsx +406 -0
- package/app/(admin)/statistics/page.tsx +416 -0
- package/app/(admin)/tickets/[id]/page.tsx +348 -0
- package/app/(admin)/tickets/new/page.tsx +309 -0
- package/app/(admin)/tickets/page.tsx +27 -0
- package/app/api/audit/route.ts +30 -0
- package/app/api/auth/feishu/callback/route.ts +72 -0
- package/app/api/auth/feishu/login/route.ts +17 -0
- package/app/api/auth/feishu/sso/route.ts +78 -0
- package/app/api/auth/login/route.ts +85 -0
- package/app/api/auth/oauth/route.ts +168 -0
- package/app/api/config/providers/route.ts +105 -0
- package/app/api/config/route.ts +115 -0
- package/app/api/config/status/route.ts +56 -0
- package/app/api/config/test/route.ts +212 -0
- package/app/api/documents/[id]/route.ts +88 -0
- package/app/api/documents/route.ts +53 -0
- package/app/api/health/route.ts +32 -0
- package/app/api/knowledge/[id]/route.ts +152 -0
- package/app/api/knowledge/from-session/route.ts +27 -0
- package/app/api/knowledge/route.ts +100 -0
- package/app/api/market/knowledge/[id]/route.ts +92 -0
- package/app/api/market/knowledge/route.ts +130 -0
- package/app/api/marketplace/skills/[id]/approve/route.ts +68 -0
- package/app/api/marketplace/skills/[id]/certify/route.ts +54 -0
- package/app/api/marketplace/skills/[id]/install/route.ts +180 -0
- package/app/api/marketplace/skills/[id]/promote-to-system/route.ts +219 -0
- package/app/api/marketplace/skills/[id]/rate/route.ts +90 -0
- package/app/api/marketplace/skills/[id]/ratings/route.ts +55 -0
- package/app/api/marketplace/skills/[id]/reject/route.ts +68 -0
- package/app/api/marketplace/skills/[id]/route.ts +177 -0
- package/app/api/marketplace/skills/route.ts +235 -0
- package/app/api/memory/route.ts +40 -0
- package/app/api/my/files/[id]/route.ts +52 -0
- package/app/api/my/files/route.ts +230 -0
- package/app/api/my/knowledge/route.ts +36 -0
- package/app/api/pi-chat/route.ts +443 -0
- package/app/api/recommend/route.ts +38 -0
- package/app/api/scheduled-tasks/[id]/execute/route.ts +132 -0
- package/app/api/scheduled-tasks/[id]/route.ts +165 -0
- package/app/api/scheduled-tasks/[id]/toggle/route.ts +53 -0
- package/app/api/scheduled-tasks/route.ts +101 -0
- package/app/api/sessions/[id]/messages/route.ts +212 -0
- package/app/api/sessions/route.ts +101 -0
- package/app/api/share/file/[id]/route.ts +37 -0
- package/app/api/skills/[name]/execute/route.ts +121 -0
- package/app/api/skills/[name]/route.ts +167 -0
- package/app/api/skills/create/route.ts +65 -0
- package/app/api/skills/generate/route.ts +405 -0
- package/app/api/skills/installed/route.ts +151 -0
- package/app/api/skills/route.ts +174 -0
- package/app/api/skills/translate/route.ts +40 -0
- package/app/api/skills/user/[name]/route.ts +159 -0
- package/app/api/skills/user/route.ts +90 -0
- package/app/api/statistics/route.ts +94 -0
- package/app/api/task-executions/[id]/route.ts +34 -0
- package/app/api/task-executions/route.ts +29 -0
- package/app/api/tickets/[id]/approve/route.ts +129 -0
- package/app/api/tickets/[id]/execute/route.ts +201 -0
- package/app/api/tickets/[id]/route.ts +127 -0
- package/app/api/tickets/route.ts +103 -0
- package/app/api/user/skills/route.ts +175 -0
- package/app/api/users/route.ts +80 -0
- package/app/chat/page.tsx +5 -0
- package/app/globals.css +84 -0
- package/app/h5/layout.tsx +5 -0
- package/app/h5/mobile-approvals-page.tsx +167 -0
- package/app/h5/mobile-chat-page.tsx +951 -0
- package/app/h5/mobile-profile-page.tsx +147 -0
- package/app/h5/mobile-tickets-page.tsx +121 -0
- package/app/h5/page.tsx +23 -0
- package/app/h5/ticket-action-buttons.tsx +80 -0
- package/app/layout.tsx +26 -0
- package/app/login/page.tsx +318 -0
- package/app/market/knowledge/[id]/page.tsx +77 -0
- package/app/market/knowledge/page.tsx +358 -0
- package/app/market/layout.tsx +29 -0
- package/app/market/page.tsx +18 -0
- package/app/market/skills/page.tsx +397 -0
- package/app/my/files/page.tsx +511 -0
- package/app/my/knowledge/[id]/page.tsx +271 -0
- package/app/my/knowledge/new/page.tsx +234 -0
- package/app/my/knowledge/page.tsx +248 -0
- package/app/my/layout.tsx +32 -0
- package/app/my/memory/page.tsx +164 -0
- package/app/my/page.tsx +18 -0
- package/app/my/scheduled-tasks/[id]/edit/page.tsx +290 -0
- package/app/my/scheduled-tasks/[id]/executions/page.tsx +275 -0
- package/app/my/scheduled-tasks/[id]/page.tsx +284 -0
- package/app/my/scheduled-tasks/new/page.tsx +230 -0
- package/app/my/scheduled-tasks/page.tsx +27 -0
- package/app/my/skills/[name]/page.tsx +320 -0
- package/app/my/skills/new/page.tsx +394 -0
- package/app/my/skills/page.tsx +303 -0
- package/app/page.tsx +2288 -0
- package/app/share/[sessionId]/page.tsx +226 -0
- package/app/share/file/[id]/page.tsx +140 -0
- package/bin/README.md +63 -0
- package/bin/generate-api-system +300 -0
- package/bin/postinstall.js +95 -0
- package/bin/work-agent.js +173 -0
- package/components/ai-elements/agent.tsx +142 -0
- package/components/ai-elements/artifact.tsx +149 -0
- package/components/ai-elements/attachments.tsx +427 -0
- package/components/ai-elements/audio-player.tsx +232 -0
- package/components/ai-elements/canvas.tsx +26 -0
- package/components/ai-elements/chain-of-thought.tsx +223 -0
- package/components/ai-elements/checkpoint.tsx +72 -0
- package/components/ai-elements/code-block.tsx +555 -0
- package/components/ai-elements/commit.tsx +449 -0
- package/components/ai-elements/confirmation.tsx +173 -0
- package/components/ai-elements/connection.tsx +28 -0
- package/components/ai-elements/context.tsx +410 -0
- package/components/ai-elements/controls.tsx +19 -0
- package/components/ai-elements/conversation.tsx +167 -0
- package/components/ai-elements/edge.tsx +144 -0
- package/components/ai-elements/environment-variables.tsx +325 -0
- package/components/ai-elements/file-tree.tsx +298 -0
- package/components/ai-elements/image.tsx +25 -0
- package/components/ai-elements/inline-citation.tsx +294 -0
- package/components/ai-elements/jsx-preview.tsx +250 -0
- package/components/ai-elements/message.tsx +367 -0
- package/components/ai-elements/mic-selector.tsx +372 -0
- package/components/ai-elements/model-selector.tsx +214 -0
- package/components/ai-elements/node.tsx +72 -0
- package/components/ai-elements/open-in-chat.tsx +367 -0
- package/components/ai-elements/package-info.tsx +235 -0
- package/components/ai-elements/panel.tsx +16 -0
- package/components/ai-elements/persona.tsx +280 -0
- package/components/ai-elements/plan.tsx +144 -0
- package/components/ai-elements/prompt-input.tsx +1341 -0
- package/components/ai-elements/queue.tsx +275 -0
- package/components/ai-elements/reasoning.tsx +355 -0
- package/components/ai-elements/sandbox.tsx +133 -0
- package/components/ai-elements/schema-display.tsx +473 -0
- package/components/ai-elements/shimmer.tsx +78 -0
- package/components/ai-elements/snippet.tsx +141 -0
- package/components/ai-elements/sources.tsx +78 -0
- package/components/ai-elements/speech-input.tsx +324 -0
- package/components/ai-elements/stack-trace.tsx +531 -0
- package/components/ai-elements/suggestion.tsx +58 -0
- package/components/ai-elements/task.tsx +88 -0
- package/components/ai-elements/terminal.tsx +277 -0
- package/components/ai-elements/test-results.tsx +497 -0
- package/components/ai-elements/tool.tsx +174 -0
- package/components/ai-elements/toolbar.tsx +17 -0
- package/components/ai-elements/transcription.tsx +126 -0
- package/components/ai-elements/voice-selector.tsx +525 -0
- package/components/ai-elements/web-preview.tsx +282 -0
- package/components/audit-log-list.tsx +114 -0
- package/components/chat/EmptyPreviewState.tsx +12 -0
- package/components/chat/KnowledgePickerDialog.tsx +464 -0
- package/components/chat/KnowledgePreview.tsx +70 -0
- package/components/chat/KnowledgePreviewPanel.tsx +86 -0
- package/components/chat/MentionInput.tsx +309 -0
- package/components/chat/OrganizeDialog.tsx +258 -0
- package/components/chat/RecommendationBanner.tsx +94 -0
- package/components/chat/SaveToKnowledgeDialog.tsx +193 -0
- package/components/chat/SkillSelector.tsx +305 -0
- package/components/chat/SkillSwitcher.tsx +163 -0
- package/components/client-layout.tsx +15 -0
- package/components/knowledge/KnowledgeMetadataPanel.tsx +293 -0
- package/components/layout-wrapper.tsx +18 -0
- package/components/mobile-layout.tsx +62 -0
- package/components/scheduled-task-list.tsx +356 -0
- package/components/setup-guide.tsx +484 -0
- package/components/sub-nav.tsx +54 -0
- package/components/ticket-detail-content.tsx +383 -0
- package/components/ticket-list.tsx +366 -0
- package/components/top-nav.tsx +132 -0
- package/components/ui/accordion.tsx +58 -0
- package/components/ui/alert.tsx +59 -0
- package/components/ui/avatar.tsx +50 -0
- package/components/ui/badge.tsx +36 -0
- package/components/ui/button-group.tsx +83 -0
- package/components/ui/button.tsx +57 -0
- package/components/ui/card.tsx +91 -0
- package/components/ui/carousel.tsx +262 -0
- package/components/ui/collapsible.tsx +11 -0
- package/components/ui/command.tsx +153 -0
- package/components/ui/dialog.tsx +122 -0
- package/components/ui/dropdown-menu.tsx +200 -0
- package/components/ui/hover-card.tsx +29 -0
- package/components/ui/input-group.tsx +170 -0
- package/components/ui/input.tsx +22 -0
- package/components/ui/label.tsx +26 -0
- package/components/ui/popover.tsx +31 -0
- package/components/ui/progress.tsx +28 -0
- package/components/ui/scroll-area.tsx +48 -0
- package/components/ui/select.tsx +174 -0
- package/components/ui/separator.tsx +31 -0
- package/components/ui/spinner.tsx +16 -0
- package/components/ui/switch.tsx +29 -0
- package/components/ui/table.tsx +120 -0
- package/components/ui/tabs.tsx +55 -0
- package/components/ui/textarea.tsx +22 -0
- package/components/ui/tooltip.tsx +30 -0
- package/components/welcome-guide.tsx +182 -0
- package/components.json +24 -0
- package/lib/command-parser.ts +331 -0
- package/lib/dangerous-commands.ts +672 -0
- package/lib/db.ts +2250 -0
- package/lib/feishu-auth.ts +135 -0
- package/lib/file-storage.ts +306 -0
- package/lib/file-tool.ts +583 -0
- package/lib/knowledge-tool.ts +152 -0
- package/lib/knowledge-types.ts +66 -0
- package/lib/market-client.ts +313 -0
- package/lib/market-db.ts +736 -0
- package/lib/market-types.ts +51 -0
- package/lib/memory-tool.ts +211 -0
- package/lib/memory.ts +197 -0
- package/lib/pi-config.ts +436 -0
- package/lib/pi-session.ts +799 -0
- package/lib/pinyin.ts +13 -0
- package/lib/recommendation.ts +227 -0
- package/lib/risk-estimator.ts +350 -0
- package/lib/scheduled-task-tool.ts +184 -0
- package/lib/scheduler-init.ts +43 -0
- package/lib/scheduler.ts +416 -0
- package/lib/secure-bash-tool.ts +413 -0
- package/lib/skill-engine.ts +396 -0
- package/lib/skill-generator.ts +269 -0
- package/lib/skill-loader.ts +234 -0
- package/lib/skill-tool.ts +188 -0
- package/lib/skill-types.ts +82 -0
- package/lib/skills-init.ts +58 -0
- package/lib/ticket-tool.ts +246 -0
- package/lib/user-skill-types.ts +30 -0
- package/lib/user-skills.ts +362 -0
- package/lib/utils.ts +6 -0
- package/lib/workflow.ts +154 -0
- package/lib/zip-tool.ts +191 -0
- package/next.config.js +8 -0
- package/package.json +106 -0
- package/public/.gitkeep +1 -0
- package/public/icon.svg +1 -0
- package/tsconfig.json +42 -0
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useEffect, useState } from 'react';
|
|
4
|
+
import Link from 'next/link';
|
|
5
|
+
import {
|
|
6
|
+
Plus,
|
|
7
|
+
Puzzle,
|
|
8
|
+
Trash2,
|
|
9
|
+
Code,
|
|
10
|
+
FileText,
|
|
11
|
+
Shield,
|
|
12
|
+
RefreshCw
|
|
13
|
+
} from 'lucide-react';
|
|
14
|
+
import { Button } from '@/components/ui/button';
|
|
15
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
|
16
|
+
import { Badge } from '@/components/ui/badge';
|
|
17
|
+
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog';
|
|
18
|
+
import { Terminal } from '@/components/ai-elements/terminal';
|
|
19
|
+
|
|
20
|
+
interface Skill {
|
|
21
|
+
name: string;
|
|
22
|
+
displayName?: string;
|
|
23
|
+
description?: string;
|
|
24
|
+
version?: string;
|
|
25
|
+
author?: string;
|
|
26
|
+
type?: string;
|
|
27
|
+
categories?: string[];
|
|
28
|
+
scriptCount: number;
|
|
29
|
+
apiDocCount: number;
|
|
30
|
+
isBuiltin: boolean;
|
|
31
|
+
builtinEnabled: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export default function SkillsPage() {
|
|
35
|
+
const [skills, setSkills] = useState<Skill[]>([]);
|
|
36
|
+
const [loading, setLoading] = useState(true);
|
|
37
|
+
const [deleteTarget, setDeleteTarget] = useState<Skill | null>(null);
|
|
38
|
+
const [deleting, setDeleting] = useState(false);
|
|
39
|
+
const [savingBuiltin, setSavingBuiltin] = useState<string | null>(null);
|
|
40
|
+
const [message, setMessage] = useState<{ type: 'success' | 'error'; text: string } | null>(null);
|
|
41
|
+
|
|
42
|
+
const fetchSkills = async () => {
|
|
43
|
+
try {
|
|
44
|
+
const res = await fetch('/api/skills');
|
|
45
|
+
if (res.ok) {
|
|
46
|
+
const data = await res.json();
|
|
47
|
+
setSkills(data);
|
|
48
|
+
}
|
|
49
|
+
} catch (error) {
|
|
50
|
+
console.error('Failed to fetch skills:', error);
|
|
51
|
+
} finally {
|
|
52
|
+
setLoading(false);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
fetchSkills();
|
|
58
|
+
}, []);
|
|
59
|
+
|
|
60
|
+
const handleDelete = async () => {
|
|
61
|
+
if (!deleteTarget) return;
|
|
62
|
+
setDeleting(true);
|
|
63
|
+
try {
|
|
64
|
+
const res = await fetch(`/api/skills/${deleteTarget.name}`, { method: 'DELETE' });
|
|
65
|
+
if (res.ok) {
|
|
66
|
+
setMessage({ type: 'success', text: `Skill "${deleteTarget.displayName || deleteTarget.name}" 已删除` });
|
|
67
|
+
fetchSkills();
|
|
68
|
+
} else {
|
|
69
|
+
const data = await res.json();
|
|
70
|
+
setMessage({ type: 'error', text: data.error || '删除失败' });
|
|
71
|
+
}
|
|
72
|
+
} catch (error) {
|
|
73
|
+
setMessage({ type: 'error', text: '删除失败' });
|
|
74
|
+
} finally {
|
|
75
|
+
setDeleting(false);
|
|
76
|
+
setDeleteTarget(null);
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const toggleBuiltin = async (skillName: string, enabled: boolean) => {
|
|
81
|
+
setSavingBuiltin(skillName);
|
|
82
|
+
try {
|
|
83
|
+
const res = await fetch('/api/skills', {
|
|
84
|
+
method: 'PUT',
|
|
85
|
+
headers: { 'Content-Type': 'application/json' },
|
|
86
|
+
body: JSON.stringify({ skillName, enabled })
|
|
87
|
+
});
|
|
88
|
+
if (res.ok) {
|
|
89
|
+
setSkills(prev => prev.map(s =>
|
|
90
|
+
s.name === skillName ? { ...s, builtinEnabled: enabled } : s
|
|
91
|
+
));
|
|
92
|
+
setMessage({
|
|
93
|
+
type: 'success',
|
|
94
|
+
text: enabled ? `已启用内置 Skill "${skillName}"` : `已禁用内置 Skill "${skillName}"`
|
|
95
|
+
});
|
|
96
|
+
} else {
|
|
97
|
+
setMessage({ type: 'error', text: '操作失败' });
|
|
98
|
+
}
|
|
99
|
+
} catch (error) {
|
|
100
|
+
setMessage({ type: 'error', text: '操作失败' });
|
|
101
|
+
} finally {
|
|
102
|
+
setSavingBuiltin(null);
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
// 分类skills:
|
|
107
|
+
// 1. API Skills: 从Swagger生成的,有scripts或APIs目录
|
|
108
|
+
// 2. 通用Skills: 只有SKILL.json/SKILL.md的skill(知识型、命令型等)
|
|
109
|
+
const apiSkills = skills.filter(s => !s.isBuiltin && (s.scriptCount > 0 || s.apiDocCount > 0));
|
|
110
|
+
const generalSkills = skills.filter(s =>
|
|
111
|
+
!s.isBuiltin &&
|
|
112
|
+
s.scriptCount === 0 &&
|
|
113
|
+
s.apiDocCount === 0 &&
|
|
114
|
+
(s.type === 'documentation' || s.type === 'command' || s.type === 'workflow' || s.type === 'hybrid' || s.version)
|
|
115
|
+
);
|
|
116
|
+
const otherSkills = skills.filter(s =>
|
|
117
|
+
!s.isBuiltin &&
|
|
118
|
+
s.scriptCount === 0 &&
|
|
119
|
+
s.apiDocCount === 0 &&
|
|
120
|
+
!generalSkills.includes(s)
|
|
121
|
+
);
|
|
122
|
+
const systemSkills = skills.filter(s => s.isBuiltin);
|
|
123
|
+
|
|
124
|
+
return (
|
|
125
|
+
<div className="p-6">
|
|
126
|
+
<div className="max-w-7xl mx-auto">
|
|
127
|
+
<div className="flex items-center justify-between mb-8">
|
|
128
|
+
<div>
|
|
129
|
+
<h1 className="text-3xl font-bold">Skill 管理</h1>
|
|
130
|
+
<p className="text-muted-foreground mt-1">管理 Skills,支持通用类型和 Swagger/API 生成</p>
|
|
131
|
+
</div>
|
|
132
|
+
<div className="flex gap-3">
|
|
133
|
+
<Button variant="outline" onClick={fetchSkills}>
|
|
134
|
+
<RefreshCw className="w-4 h-4 mr-2" />
|
|
135
|
+
刷新
|
|
136
|
+
</Button>
|
|
137
|
+
<Link href="/skills/new">
|
|
138
|
+
<Button>
|
|
139
|
+
<Plus className="w-4 h-4 mr-2" />
|
|
140
|
+
创建 Skill
|
|
141
|
+
</Button>
|
|
142
|
+
</Link>
|
|
143
|
+
</div>
|
|
144
|
+
</div>
|
|
145
|
+
|
|
146
|
+
{message && (
|
|
147
|
+
<div className={`mb-6 p-4 rounded-lg ${message.type === 'success' ? 'bg-green-50 text-green-700 border border-green-200' : 'bg-red-50 text-red-700 border border-red-200'}`}>
|
|
148
|
+
{message.text}
|
|
149
|
+
</div>
|
|
150
|
+
)}
|
|
151
|
+
|
|
152
|
+
{loading ? (
|
|
153
|
+
<div className="flex items-center justify-center h-64">
|
|
154
|
+
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary"></div>
|
|
155
|
+
</div>
|
|
156
|
+
) : (
|
|
157
|
+
<div className="space-y-8">
|
|
158
|
+
{systemSkills.length > 0 && (
|
|
159
|
+
<div>
|
|
160
|
+
<h2 className="text-lg font-semibold mb-4 flex items-center gap-2">
|
|
161
|
+
<Shield className="w-5 h-5 text-primary" />
|
|
162
|
+
系统内置 Skills ({systemSkills.length})
|
|
163
|
+
</h2>
|
|
164
|
+
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
|
|
165
|
+
{systemSkills.map((skill) => (
|
|
166
|
+
<Card key={skill.name} className={skill.builtinEnabled ? "border-primary/20 bg-primary/5" : "border-dashed opacity-60"}>
|
|
167
|
+
<CardHeader>
|
|
168
|
+
<div className="flex items-start justify-between">
|
|
169
|
+
<div className="flex items-center gap-3">
|
|
170
|
+
<div className="w-10 h-10 rounded-lg bg-primary/10 flex items-center justify-center">
|
|
171
|
+
<Code className="w-5 h-5 text-primary" />
|
|
172
|
+
</div>
|
|
173
|
+
<div>
|
|
174
|
+
<CardTitle className="text-base">{skill.displayName || skill.name}</CardTitle>
|
|
175
|
+
<CardDescription>v{skill.version}</CardDescription>
|
|
176
|
+
</div>
|
|
177
|
+
</div>
|
|
178
|
+
<div className="flex items-center gap-2">
|
|
179
|
+
<Button
|
|
180
|
+
variant={skill.builtinEnabled ? "default" : "outline"}
|
|
181
|
+
size="sm"
|
|
182
|
+
disabled={savingBuiltin === skill.name}
|
|
183
|
+
onClick={() => toggleBuiltin(skill.name, !skill.builtinEnabled)}
|
|
184
|
+
>
|
|
185
|
+
{skill.builtinEnabled ? "已启用" : "启用"}
|
|
186
|
+
</Button>
|
|
187
|
+
</div>
|
|
188
|
+
</div>
|
|
189
|
+
</CardHeader>
|
|
190
|
+
<CardContent>
|
|
191
|
+
<p className="text-sm text-muted-foreground mb-4 line-clamp-2">{skill.description}</p>
|
|
192
|
+
<div className="flex gap-2">
|
|
193
|
+
<Link href={`/skills/${skill.name}`} className="flex-1">
|
|
194
|
+
<Button variant="outline" size="sm" className="w-full">
|
|
195
|
+
查看详情
|
|
196
|
+
</Button>
|
|
197
|
+
</Link>
|
|
198
|
+
</div>
|
|
199
|
+
</CardContent>
|
|
200
|
+
</Card>
|
|
201
|
+
))}
|
|
202
|
+
</div>
|
|
203
|
+
</div>
|
|
204
|
+
)}
|
|
205
|
+
|
|
206
|
+
{apiSkills.length > 0 && (
|
|
207
|
+
<div>
|
|
208
|
+
<h2 className="text-lg font-semibold mb-4 flex items-center gap-2">
|
|
209
|
+
<Puzzle className="w-5 h-5" />
|
|
210
|
+
API Skills ({apiSkills.length})
|
|
211
|
+
</h2>
|
|
212
|
+
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
|
|
213
|
+
{apiSkills.map((skill) => (
|
|
214
|
+
<Card key={skill.name} className="hover:shadow-md transition-shadow">
|
|
215
|
+
<CardHeader>
|
|
216
|
+
<div className="flex items-start justify-between">
|
|
217
|
+
<div className="flex items-center gap-3">
|
|
218
|
+
<div className="w-10 h-10 rounded-lg bg-muted flex items-center justify-center">
|
|
219
|
+
<Puzzle className="w-5 h-5 text-muted-foreground" />
|
|
220
|
+
</div>
|
|
221
|
+
<div>
|
|
222
|
+
<CardTitle className="text-base">{skill.displayName || skill.name}</CardTitle>
|
|
223
|
+
<CardDescription>v{skill.version}</CardDescription>
|
|
224
|
+
</div>
|
|
225
|
+
</div>
|
|
226
|
+
<Button
|
|
227
|
+
variant="ghost"
|
|
228
|
+
size="icon"
|
|
229
|
+
className="text-muted-foreground hover:text-destructive"
|
|
230
|
+
onClick={() => setDeleteTarget(skill)}
|
|
231
|
+
>
|
|
232
|
+
<Trash2 className="w-4 h-4" />
|
|
233
|
+
</Button>
|
|
234
|
+
</div>
|
|
235
|
+
</CardHeader>
|
|
236
|
+
<CardContent>
|
|
237
|
+
<p className="text-sm text-muted-foreground mb-4 line-clamp-2">{skill.description}</p>
|
|
238
|
+
<div className="flex flex-wrap gap-2 mb-4">
|
|
239
|
+
{skill.categories?.slice(0, 3).map((cat) => (
|
|
240
|
+
<Badge key={cat} variant="outline" className="text-xs">
|
|
241
|
+
{cat}
|
|
242
|
+
</Badge>
|
|
243
|
+
))}
|
|
244
|
+
</div>
|
|
245
|
+
<div className="flex items-center gap-4 text-sm text-muted-foreground mb-4">
|
|
246
|
+
<span className="flex items-center gap-1">
|
|
247
|
+
<Code className="w-4 h-4" />
|
|
248
|
+
{skill.scriptCount} 脚本
|
|
249
|
+
</span>
|
|
250
|
+
<span className="flex items-center gap-1">
|
|
251
|
+
<FileText className="w-4 h-4" />
|
|
252
|
+
{skill.apiDocCount} API
|
|
253
|
+
</span>
|
|
254
|
+
</div>
|
|
255
|
+
<div className="flex gap-2">
|
|
256
|
+
<Link href={`/skills/${skill.name}`} className="flex-1">
|
|
257
|
+
<Button variant="outline" size="sm" className="w-full">
|
|
258
|
+
查看详情
|
|
259
|
+
</Button>
|
|
260
|
+
</Link>
|
|
261
|
+
<Button
|
|
262
|
+
variant="ghost"
|
|
263
|
+
size="sm"
|
|
264
|
+
className="text-destructive hover:text-destructive"
|
|
265
|
+
onClick={() => setDeleteTarget(skill)}
|
|
266
|
+
>
|
|
267
|
+
<Trash2 className="w-4 h-4" />
|
|
268
|
+
</Button>
|
|
269
|
+
</div>
|
|
270
|
+
</CardContent>
|
|
271
|
+
</Card>
|
|
272
|
+
))}
|
|
273
|
+
</div>
|
|
274
|
+
</div>
|
|
275
|
+
)}
|
|
276
|
+
|
|
277
|
+
{generalSkills.length > 0 && (
|
|
278
|
+
<div>
|
|
279
|
+
<h2 className="text-lg font-semibold mb-4 flex items-center gap-2">
|
|
280
|
+
<FileText className="w-5 h-5" />
|
|
281
|
+
通用 Skills ({generalSkills.length})
|
|
282
|
+
</h2>
|
|
283
|
+
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
|
|
284
|
+
{generalSkills.map((skill) => (
|
|
285
|
+
<Card key={skill.name}>
|
|
286
|
+
<CardHeader>
|
|
287
|
+
<div className="flex items-start justify-between">
|
|
288
|
+
<div className="flex items-center gap-3">
|
|
289
|
+
<div className="w-10 h-10 rounded-lg bg-muted flex items-center justify-center">
|
|
290
|
+
<FileText className="w-5 h-5 text-muted-foreground" />
|
|
291
|
+
</div>
|
|
292
|
+
<div>
|
|
293
|
+
<CardTitle className="text-base">{skill.displayName || skill.name}</CardTitle>
|
|
294
|
+
<CardDescription>{skill.type || '通用'} · v{skill.version}</CardDescription>
|
|
295
|
+
</div>
|
|
296
|
+
</div>
|
|
297
|
+
<Button
|
|
298
|
+
variant="ghost"
|
|
299
|
+
size="icon"
|
|
300
|
+
className="text-muted-foreground hover:text-destructive"
|
|
301
|
+
onClick={() => setDeleteTarget(skill)}
|
|
302
|
+
>
|
|
303
|
+
<Trash2 className="w-4 h-4" />
|
|
304
|
+
</Button>
|
|
305
|
+
</div>
|
|
306
|
+
</CardHeader>
|
|
307
|
+
<CardContent>
|
|
308
|
+
<p className="text-sm text-muted-foreground mb-4 line-clamp-2">{skill.description}</p>
|
|
309
|
+
<div className="flex gap-2">
|
|
310
|
+
<Link href={`/skills/${skill.name}`} className="flex-1">
|
|
311
|
+
<Button variant="outline" size="sm" className="w-full">
|
|
312
|
+
查看详情
|
|
313
|
+
</Button>
|
|
314
|
+
</Link>
|
|
315
|
+
</div>
|
|
316
|
+
</CardContent>
|
|
317
|
+
</Card>
|
|
318
|
+
))}
|
|
319
|
+
</div>
|
|
320
|
+
</div>
|
|
321
|
+
)}
|
|
322
|
+
|
|
323
|
+
{otherSkills.length > 0 && (
|
|
324
|
+
<div>
|
|
325
|
+
<h2 className="text-lg font-semibold mb-4 flex items-center gap-2">
|
|
326
|
+
<Puzzle className="w-5 h-5" />
|
|
327
|
+
其他 Skills ({otherSkills.length})
|
|
328
|
+
</h2>
|
|
329
|
+
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
|
|
330
|
+
{otherSkills.map((skill) => (
|
|
331
|
+
<Card key={skill.name} className="border-dashed">
|
|
332
|
+
<CardHeader>
|
|
333
|
+
<div className="flex items-start justify-between">
|
|
334
|
+
<div>
|
|
335
|
+
<CardTitle className="text-base">{skill.displayName || skill.name}</CardTitle>
|
|
336
|
+
<CardDescription>v{skill.version || '未知'}</CardDescription>
|
|
337
|
+
</div>
|
|
338
|
+
<Button
|
|
339
|
+
variant="ghost"
|
|
340
|
+
size="icon"
|
|
341
|
+
className="text-muted-foreground hover:text-destructive"
|
|
342
|
+
onClick={() => setDeleteTarget(skill)}
|
|
343
|
+
>
|
|
344
|
+
<Trash2 className="w-4 h-4" />
|
|
345
|
+
</Button>
|
|
346
|
+
</div>
|
|
347
|
+
</CardHeader>
|
|
348
|
+
<CardContent>
|
|
349
|
+
<p className="text-sm text-muted-foreground mb-4">{skill.description}</p>
|
|
350
|
+
<div className="flex gap-2">
|
|
351
|
+
<Link href={`/skills/${skill.name}`} className="flex-1">
|
|
352
|
+
<Button variant="outline" size="sm" className="w-full">
|
|
353
|
+
查看详情
|
|
354
|
+
</Button>
|
|
355
|
+
</Link>
|
|
356
|
+
</div>
|
|
357
|
+
</CardContent>
|
|
358
|
+
</Card>
|
|
359
|
+
))}
|
|
360
|
+
</div>
|
|
361
|
+
</div>
|
|
362
|
+
)}
|
|
363
|
+
|
|
364
|
+
{skills.length === 0 && (
|
|
365
|
+
<Card className="border-dashed">
|
|
366
|
+
<CardContent className="flex flex-col items-center justify-center py-12">
|
|
367
|
+
<div className="w-16 h-16 rounded-full bg-muted flex items-center justify-center mb-4">
|
|
368
|
+
<Puzzle className="w-8 h-8 text-muted-foreground" />
|
|
369
|
+
</div>
|
|
370
|
+
<h3 className="text-lg font-medium mb-2">暂无 Skill</h3>
|
|
371
|
+
<p className="text-muted-foreground text-center mb-4">
|
|
372
|
+
创建通用 Skill 或从 Swagger/OpenAPI 生成 API Skill
|
|
373
|
+
</p>
|
|
374
|
+
<Link href="/skills/new">
|
|
375
|
+
<Button>
|
|
376
|
+
<Plus className="w-4 h-4 mr-2" />
|
|
377
|
+
创建第一个 Skill
|
|
378
|
+
</Button>
|
|
379
|
+
</Link>
|
|
380
|
+
</CardContent>
|
|
381
|
+
</Card>
|
|
382
|
+
)}
|
|
383
|
+
</div>
|
|
384
|
+
)}
|
|
385
|
+
|
|
386
|
+
<Dialog open={!!deleteTarget} onOpenChange={() => setDeleteTarget(null)}>
|
|
387
|
+
<DialogContent>
|
|
388
|
+
<DialogHeader>
|
|
389
|
+
<DialogTitle>确认删除</DialogTitle>
|
|
390
|
+
<DialogDescription>
|
|
391
|
+
确定要删除 Skill "{deleteTarget?.displayName || deleteTarget?.name}" 吗?
|
|
392
|
+
此操作不可恢复,将删除该 skill 的所有文件。
|
|
393
|
+
</DialogDescription>
|
|
394
|
+
</DialogHeader>
|
|
395
|
+
<DialogFooter>
|
|
396
|
+
<Button variant="outline" onClick={() => setDeleteTarget(null)}>取消</Button>
|
|
397
|
+
<Button onClick={handleDelete} disabled={deleting}>
|
|
398
|
+
{deleting ? '删除中...' : '确认删除'}
|
|
399
|
+
</Button>
|
|
400
|
+
</DialogFooter>
|
|
401
|
+
</DialogContent>
|
|
402
|
+
</Dialog>
|
|
403
|
+
</div>
|
|
404
|
+
</div>
|
|
405
|
+
);
|
|
406
|
+
}
|