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,174 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { readdirSync, existsSync, readFileSync, statSync } from 'fs';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { getAvailableSkills, loadSkillInfo } from '@/lib/skill-engine';
|
|
5
|
+
import { listUserSkills } from '@/lib/user-skills';
|
|
6
|
+
import { getBuiltinSkills } from '@/lib/db';
|
|
7
|
+
|
|
8
|
+
interface SkillMetadata {
|
|
9
|
+
name: string;
|
|
10
|
+
displayName?: string;
|
|
11
|
+
description?: string;
|
|
12
|
+
version?: string;
|
|
13
|
+
author?: string;
|
|
14
|
+
type?: string;
|
|
15
|
+
keywords?: string[];
|
|
16
|
+
categories?: string[];
|
|
17
|
+
scriptCount: number;
|
|
18
|
+
apiDocCount: number;
|
|
19
|
+
isBuiltin: boolean;
|
|
20
|
+
builtinEnabled: boolean;
|
|
21
|
+
riskConfig?: {
|
|
22
|
+
default: string;
|
|
23
|
+
overrides: Record<string, string>;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function GET(request: NextRequest) {
|
|
28
|
+
try {
|
|
29
|
+
const skillsDir = join(process.cwd(), '.pi', 'skills');
|
|
30
|
+
const userId = request.nextUrl.searchParams.get('userId');
|
|
31
|
+
const skills: SkillMetadata[] = [];
|
|
32
|
+
|
|
33
|
+
// 获取内置技能配置
|
|
34
|
+
const builtinSkills = getBuiltinSkills();
|
|
35
|
+
const enabledBuiltinSkills = new Set(
|
|
36
|
+
builtinSkills.filter(s => s.enabled).map(s => s.skillName)
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
// 加载系统技能
|
|
40
|
+
if (existsSync(skillsDir)) {
|
|
41
|
+
const entries = readdirSync(skillsDir, { withFileTypes: true });
|
|
42
|
+
|
|
43
|
+
for (const entry of entries) {
|
|
44
|
+
if (!entry.isDirectory()) continue;
|
|
45
|
+
|
|
46
|
+
const skillName = entry.name;
|
|
47
|
+
const skillDir = join(skillsDir, skillName);
|
|
48
|
+
|
|
49
|
+
// Load skill info (from SKILL.json or SKILL.md)
|
|
50
|
+
const skillInfo = loadSkillInfo(skillName);
|
|
51
|
+
if (!skillInfo) continue;
|
|
52
|
+
|
|
53
|
+
// Count scripts and APIs
|
|
54
|
+
const scriptsDir = join(skillDir, 'scripts');
|
|
55
|
+
const apisDir = join(skillDir, 'APIs');
|
|
56
|
+
|
|
57
|
+
let scriptCount = 0;
|
|
58
|
+
let apiDocCount = 0;
|
|
59
|
+
|
|
60
|
+
if (existsSync(scriptsDir)) {
|
|
61
|
+
try {
|
|
62
|
+
scriptCount = readdirSync(scriptsDir).filter(f => f.endsWith('.sh')).length;
|
|
63
|
+
} catch {}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (existsSync(apisDir)) {
|
|
67
|
+
try {
|
|
68
|
+
apiDocCount = readdirSync(apisDir).filter(f => f.endsWith('.md')).length;
|
|
69
|
+
} catch {}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Load risk config from SKILL.json if available
|
|
73
|
+
let riskConfig = undefined;
|
|
74
|
+
const skillJsonPath = join(skillDir, 'SKILL.json');
|
|
75
|
+
if (existsSync(skillJsonPath)) {
|
|
76
|
+
try {
|
|
77
|
+
const jsonContent = readFileSync(skillJsonPath, 'utf-8');
|
|
78
|
+
const skillData = JSON.parse(jsonContent);
|
|
79
|
+
riskConfig = skillData.riskConfig;
|
|
80
|
+
} catch {}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
skills.push({
|
|
84
|
+
name: skillInfo.name,
|
|
85
|
+
displayName: skillInfo.displayName || skillInfo.name,
|
|
86
|
+
description: skillInfo.description,
|
|
87
|
+
version: skillInfo.version,
|
|
88
|
+
type: (skillInfo as any).type,
|
|
89
|
+
author: (skillInfo as any).author,
|
|
90
|
+
scriptCount,
|
|
91
|
+
apiDocCount,
|
|
92
|
+
isBuiltin: true,
|
|
93
|
+
builtinEnabled: enabledBuiltinSkills.has(skillName),
|
|
94
|
+
riskConfig,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// 加载用户技能(如果提供了 userId)
|
|
100
|
+
if (userId) {
|
|
101
|
+
try {
|
|
102
|
+
const userSkills = listUserSkills(userId);
|
|
103
|
+
for (const userSkill of userSkills) {
|
|
104
|
+
// 读取 SKILL.json 获取更多信息
|
|
105
|
+
const userSkillDir = join(process.cwd(), 'data', 'skills', 'users', userId, userSkill.name);
|
|
106
|
+
const skillJsonPath = join(userSkillDir, 'SKILL.json');
|
|
107
|
+
|
|
108
|
+
let displayName = userSkill.displayName || userSkill.name;
|
|
109
|
+
let description = userSkill.description || '';
|
|
110
|
+
let scriptCount = 0;
|
|
111
|
+
let apiDocCount = 0;
|
|
112
|
+
|
|
113
|
+
if (existsSync(skillJsonPath)) {
|
|
114
|
+
try {
|
|
115
|
+
const jsonContent = readFileSync(skillJsonPath, 'utf-8');
|
|
116
|
+
const skillData = JSON.parse(jsonContent);
|
|
117
|
+
displayName = skillData.skillName || skillData.displayName || userSkill.name;
|
|
118
|
+
description = skillData.description || '';
|
|
119
|
+
|
|
120
|
+
// Count scripts and APIs
|
|
121
|
+
const scriptsDir = join(userSkillDir, 'scripts');
|
|
122
|
+
const apisDir = join(userSkillDir, 'APIs');
|
|
123
|
+
if (existsSync(scriptsDir)) {
|
|
124
|
+
scriptCount = readdirSync(scriptsDir).filter(f => f.endsWith('.sh')).length;
|
|
125
|
+
}
|
|
126
|
+
if (existsSync(apisDir)) {
|
|
127
|
+
apiDocCount = readdirSync(apisDir).filter(f => f.endsWith('.md')).length;
|
|
128
|
+
}
|
|
129
|
+
} catch {}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
skills.push({
|
|
133
|
+
name: userSkill.name,
|
|
134
|
+
displayName,
|
|
135
|
+
description,
|
|
136
|
+
version: userSkill.version,
|
|
137
|
+
author: userSkill.author,
|
|
138
|
+
type: 'user',
|
|
139
|
+
scriptCount,
|
|
140
|
+
apiDocCount,
|
|
141
|
+
isBuiltin: false,
|
|
142
|
+
builtinEnabled: false,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
} catch (error) {
|
|
146
|
+
console.error('Error loading user skills:', error);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return NextResponse.json(skills);
|
|
151
|
+
} catch (error) {
|
|
152
|
+
console.error('Error listing skills:', error);
|
|
153
|
+
return NextResponse.json({ error: 'Failed to list skills' }, { status: 500 });
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export async function PUT(request: NextRequest) {
|
|
158
|
+
try {
|
|
159
|
+
const body = await request.json();
|
|
160
|
+
const { skillName, enabled, config } = body;
|
|
161
|
+
|
|
162
|
+
if (!skillName) {
|
|
163
|
+
return NextResponse.json({ error: '缺少skillName' }, { status: 400 });
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const { setBuiltinSkill } = require('@/lib/db');
|
|
167
|
+
setBuiltinSkill(skillName, enabled ?? true, config);
|
|
168
|
+
|
|
169
|
+
return NextResponse.json({ success: true });
|
|
170
|
+
} catch (error) {
|
|
171
|
+
console.error('Update builtin skill failed:', error);
|
|
172
|
+
return NextResponse.json({ error: '更新失败' }, { status: 500 });
|
|
173
|
+
}
|
|
174
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { findDirNameBySkillName, findSkillNameByDirName } from '@/lib/db';
|
|
3
|
+
|
|
4
|
+
export async function POST(request: NextRequest) {
|
|
5
|
+
try {
|
|
6
|
+
const body = await request.json();
|
|
7
|
+
const { skillNames, dirNames, userId } = body;
|
|
8
|
+
|
|
9
|
+
if (!userId) {
|
|
10
|
+
return NextResponse.json({ error: 'userId is required' }, { status: 400 });
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const results: Record<string, string> = {};
|
|
14
|
+
|
|
15
|
+
if (skillNames && Array.isArray(skillNames)) {
|
|
16
|
+
// 批量 skillName -> dirName
|
|
17
|
+
for (const skillName of skillNames) {
|
|
18
|
+
const dirName = findDirNameBySkillName(userId, skillName);
|
|
19
|
+
if (dirName) {
|
|
20
|
+
results[skillName] = dirName;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
} else if (dirNames && Array.isArray(dirNames)) {
|
|
24
|
+
// 批量 dirName -> skillName
|
|
25
|
+
for (const dirName of dirNames) {
|
|
26
|
+
const skillName = findSkillNameByDirName(userId, dirName);
|
|
27
|
+
if (skillName) {
|
|
28
|
+
results[dirName] = skillName;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
} else {
|
|
32
|
+
return NextResponse.json({ error: 'skillNames or dirNames is required' }, { status: 400 });
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return NextResponse.json({ results });
|
|
36
|
+
} catch (error) {
|
|
37
|
+
console.error('[skill-translate] Error:', error);
|
|
38
|
+
return NextResponse.json({ error: 'Internal server error' }, { status: 500 });
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import {
|
|
3
|
+
getUserSkill,
|
|
4
|
+
getUserSkillContent,
|
|
5
|
+
updateUserSkill,
|
|
6
|
+
deleteUserSkill,
|
|
7
|
+
} from '@/lib/user-skills';
|
|
8
|
+
import type { UpdateUserSkillRequest } from '@/lib/user-skill-types';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* GET /api/skills/user/:name?userId=xxx
|
|
12
|
+
* 获取技能详情和内容
|
|
13
|
+
*/
|
|
14
|
+
export async function GET(
|
|
15
|
+
request: NextRequest,
|
|
16
|
+
{ params }: { params: Promise<{ name: string }> }
|
|
17
|
+
) {
|
|
18
|
+
try {
|
|
19
|
+
const { name } = await params;
|
|
20
|
+
const searchParams = request.nextUrl.searchParams;
|
|
21
|
+
const userId = searchParams.get('userId');
|
|
22
|
+
|
|
23
|
+
if (!userId) {
|
|
24
|
+
return NextResponse.json(
|
|
25
|
+
{ error: 'Missing required parameter: userId' },
|
|
26
|
+
{ status: 400 }
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const skill = getUserSkill(userId, name);
|
|
31
|
+
|
|
32
|
+
if (!skill) {
|
|
33
|
+
return NextResponse.json(
|
|
34
|
+
{ error: 'Skill not found' },
|
|
35
|
+
{ status: 404 }
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const content = getUserSkillContent(userId, name);
|
|
40
|
+
|
|
41
|
+
return NextResponse.json({
|
|
42
|
+
...skill,
|
|
43
|
+
content,
|
|
44
|
+
});
|
|
45
|
+
} catch (error) {
|
|
46
|
+
console.error('Error getting user skill:', error);
|
|
47
|
+
return NextResponse.json(
|
|
48
|
+
{ error: 'Failed to get user skill' },
|
|
49
|
+
{ status: 500 }
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* PUT /api/skills/user/:name
|
|
56
|
+
* 更新技能
|
|
57
|
+
*/
|
|
58
|
+
export async function PUT(
|
|
59
|
+
request: NextRequest,
|
|
60
|
+
{ params }: { params: Promise<{ name: string }> }
|
|
61
|
+
) {
|
|
62
|
+
try {
|
|
63
|
+
const { name } = await params;
|
|
64
|
+
const body = await request.json();
|
|
65
|
+
|
|
66
|
+
// 验证必填字段
|
|
67
|
+
const { userId } = body;
|
|
68
|
+
|
|
69
|
+
if (!userId) {
|
|
70
|
+
return NextResponse.json(
|
|
71
|
+
{ error: 'Missing required field: userId' },
|
|
72
|
+
{ status: 400 }
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const updateRequest: UpdateUserSkillRequest = {
|
|
77
|
+
displayName: body.displayName,
|
|
78
|
+
description: body.description,
|
|
79
|
+
content: body.content,
|
|
80
|
+
version: body.version,
|
|
81
|
+
riskConfig: body.riskConfig,
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const skill = updateUserSkill(userId, name, updateRequest);
|
|
85
|
+
|
|
86
|
+
if (!skill) {
|
|
87
|
+
return NextResponse.json(
|
|
88
|
+
{ error: 'Skill not found' },
|
|
89
|
+
{ status: 404 }
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return NextResponse.json(skill);
|
|
94
|
+
} catch (error: any) {
|
|
95
|
+
console.error('Error updating user skill:', error);
|
|
96
|
+
|
|
97
|
+
if (error.message.includes('permission')) {
|
|
98
|
+
return NextResponse.json(
|
|
99
|
+
{ error: error.message },
|
|
100
|
+
{ status: 403 }
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return NextResponse.json(
|
|
105
|
+
{ error: 'Failed to update user skill' },
|
|
106
|
+
{ status: 500 }
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* DELETE /api/skills/user/:name
|
|
113
|
+
* 删除技能
|
|
114
|
+
*/
|
|
115
|
+
export async function DELETE(
|
|
116
|
+
request: NextRequest,
|
|
117
|
+
{ params }: { params: Promise<{ name: string }> }
|
|
118
|
+
) {
|
|
119
|
+
try {
|
|
120
|
+
const { name } = await params;
|
|
121
|
+
const searchParams = request.nextUrl.searchParams;
|
|
122
|
+
const userId = searchParams.get('userId');
|
|
123
|
+
|
|
124
|
+
if (!userId) {
|
|
125
|
+
return NextResponse.json(
|
|
126
|
+
{ error: 'Missing required parameter: userId' },
|
|
127
|
+
{ status: 400 }
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const deleted = deleteUserSkill(userId, name);
|
|
132
|
+
|
|
133
|
+
if (!deleted) {
|
|
134
|
+
return NextResponse.json(
|
|
135
|
+
{ error: 'Skill not found' },
|
|
136
|
+
{ status: 404 }
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return NextResponse.json({
|
|
141
|
+
success: true,
|
|
142
|
+
message: `Skill '${name}' has been deleted`,
|
|
143
|
+
});
|
|
144
|
+
} catch (error: any) {
|
|
145
|
+
console.error('Error deleting user skill:', error);
|
|
146
|
+
|
|
147
|
+
if (error.message.includes('permission')) {
|
|
148
|
+
return NextResponse.json(
|
|
149
|
+
{ error: error.message },
|
|
150
|
+
{ status: 403 }
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return NextResponse.json(
|
|
155
|
+
{ error: 'Failed to delete user skill' },
|
|
156
|
+
{ status: 500 }
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import {
|
|
3
|
+
listUserSkills,
|
|
4
|
+
createUserSkill,
|
|
5
|
+
} from '@/lib/user-skills';
|
|
6
|
+
import { installUserSkill } from '@/lib/db';
|
|
7
|
+
import type { CreateUserSkillRequest } from '@/lib/user-skill-types';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* GET /api/skills/user?userId=xxx
|
|
11
|
+
* 列出用户的私有技能
|
|
12
|
+
*/
|
|
13
|
+
export async function GET(request: NextRequest) {
|
|
14
|
+
try {
|
|
15
|
+
const searchParams = request.nextUrl.searchParams;
|
|
16
|
+
const userId = searchParams.get('userId');
|
|
17
|
+
|
|
18
|
+
if (!userId) {
|
|
19
|
+
return NextResponse.json(
|
|
20
|
+
{ error: 'Missing required parameter: userId' },
|
|
21
|
+
{ status: 400 }
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const skills = listUserSkills(userId);
|
|
26
|
+
return NextResponse.json(skills);
|
|
27
|
+
} catch (error) {
|
|
28
|
+
console.error('Error listing user skills:', error);
|
|
29
|
+
return NextResponse.json(
|
|
30
|
+
{ error: 'Failed to list user skills' },
|
|
31
|
+
{ status: 500 }
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* POST /api/skills/user
|
|
38
|
+
* 创建新的私有技能
|
|
39
|
+
*/
|
|
40
|
+
export async function POST(request: NextRequest) {
|
|
41
|
+
try {
|
|
42
|
+
const body = await request.json();
|
|
43
|
+
|
|
44
|
+
// 验证必填字段
|
|
45
|
+
const { userId, displayName, description, content } = body;
|
|
46
|
+
|
|
47
|
+
if (!userId || !displayName || !description || !content) {
|
|
48
|
+
return NextResponse.json(
|
|
49
|
+
{
|
|
50
|
+
error: 'Missing required fields',
|
|
51
|
+
required: ['userId', 'displayName', 'description', 'content'],
|
|
52
|
+
},
|
|
53
|
+
{ status: 400 }
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const createRequest: CreateUserSkillRequest = {
|
|
58
|
+
name: displayName, // 使用 displayName 作为输入,内部会生成目录名
|
|
59
|
+
displayName,
|
|
60
|
+
description,
|
|
61
|
+
content,
|
|
62
|
+
version: body.version,
|
|
63
|
+
riskConfig: body.riskConfig,
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const skill = createUserSkill(userId, createRequest);
|
|
67
|
+
|
|
68
|
+
// 使用返回的目录名写入数据库
|
|
69
|
+
installUserSkill(userId, skill.name, 'personal', {
|
|
70
|
+
displayName,
|
|
71
|
+
description,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
return NextResponse.json(skill, { status: 201 });
|
|
75
|
+
} catch (error: any) {
|
|
76
|
+
console.error('Error creating user skill:', error);
|
|
77
|
+
|
|
78
|
+
if (error.message.includes('already exists')) {
|
|
79
|
+
return NextResponse.json(
|
|
80
|
+
{ error: error.message },
|
|
81
|
+
{ status: 409 }
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return NextResponse.json(
|
|
86
|
+
{ error: 'Failed to create user skill' },
|
|
87
|
+
{ status: 500 }
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Statistics API - GET
|
|
3
|
+
*
|
|
4
|
+
* 获取系统统计数据
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
8
|
+
import { getSessionStats, getSessionTrends, getUserStats, getSkillStats, getKnowledgeStats, getAllSessions } from '@/lib/db';
|
|
9
|
+
|
|
10
|
+
const statsCache: { data: any; timestamp: number } = { data: null, timestamp: 0 };
|
|
11
|
+
const CACHE_DURATION = 5 * 60 * 1000; // 5分钟缓存
|
|
12
|
+
|
|
13
|
+
function isCacheValid(): boolean {
|
|
14
|
+
return statsCache.data && Date.now() - statsCache.timestamp < CACHE_DURATION;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function setCache(data: any): void {
|
|
18
|
+
statsCache.data = data;
|
|
19
|
+
statsCache.timestamp = Date.now();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* GET /api/statistics
|
|
24
|
+
*
|
|
25
|
+
* Query params:
|
|
26
|
+
* - period: '7d' | '30d' | 'all' (default: '7d')
|
|
27
|
+
*/
|
|
28
|
+
export async function GET(request: NextRequest) {
|
|
29
|
+
try {
|
|
30
|
+
const { searchParams } = new URL(request.url);
|
|
31
|
+
const period = (searchParams.get('period') || '7d') as '7d' | '30d' | 'all';
|
|
32
|
+
|
|
33
|
+
if (!['7d', '30d', 'all'].includes(period)) {
|
|
34
|
+
return NextResponse.json(
|
|
35
|
+
{ error: 'Invalid period. Must be 7d, 30d, or all' },
|
|
36
|
+
{ status: 400 },
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// 尝试从缓存获取
|
|
41
|
+
if (isCacheValid() && statsCache.data?.period === period) {
|
|
42
|
+
return NextResponse.json(statsCache.data);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// 获取各项统计数据
|
|
46
|
+
const overview = getSessionStats(period);
|
|
47
|
+
const trends = getSessionTrends(period);
|
|
48
|
+
const userStats = getUserStats(period);
|
|
49
|
+
const skillStats = getSkillStats(period);
|
|
50
|
+
const knowledgeStats = getKnowledgeStats(period);
|
|
51
|
+
|
|
52
|
+
// 获取会话列表(带分页)
|
|
53
|
+
const sessions = getAllSessions().slice(0, 100).map(s => ({
|
|
54
|
+
sessionId: s.sessionId,
|
|
55
|
+
userId: s.userId,
|
|
56
|
+
createdAt: s.createdAt,
|
|
57
|
+
lastAccessedAt: s.lastAccessedAt,
|
|
58
|
+
messageCount: s.messageCount,
|
|
59
|
+
title: s.title,
|
|
60
|
+
}));
|
|
61
|
+
|
|
62
|
+
const result = {
|
|
63
|
+
period,
|
|
64
|
+
overview,
|
|
65
|
+
trends,
|
|
66
|
+
userStats,
|
|
67
|
+
skillStats,
|
|
68
|
+
knowledgeStats,
|
|
69
|
+
sessions,
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
// 设置缓存
|
|
73
|
+
setCache(result);
|
|
74
|
+
|
|
75
|
+
return NextResponse.json(result);
|
|
76
|
+
} catch (error) {
|
|
77
|
+
console.error('Statistics API error:', error);
|
|
78
|
+
return NextResponse.json(
|
|
79
|
+
{ error: error instanceof Error ? error.message : '获取统计数据失败' },
|
|
80
|
+
{ status: 500 },
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* POST /api/statistics
|
|
87
|
+
*
|
|
88
|
+
* 清除缓存
|
|
89
|
+
*/
|
|
90
|
+
export async function POST() {
|
|
91
|
+
statsCache.data = null;
|
|
92
|
+
statsCache.timestamp = 0;
|
|
93
|
+
return NextResponse.json({ success: true, message: 'Cache cleared' });
|
|
94
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 单条执行记录 API - GET
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
6
|
+
import { getTaskExecution } from '@/lib/db';
|
|
7
|
+
|
|
8
|
+
type RouteContext = {
|
|
9
|
+
params: Promise<{ id: string }>;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* GET /api/task-executions/[id] - 获取单条执行记录
|
|
14
|
+
*/
|
|
15
|
+
export async function GET(request: NextRequest, context: RouteContext) {
|
|
16
|
+
try {
|
|
17
|
+
const { id } = await context.params;
|
|
18
|
+
const execution = await getTaskExecution(id);
|
|
19
|
+
|
|
20
|
+
if (!execution) {
|
|
21
|
+
return NextResponse.json(
|
|
22
|
+
{ error: '执行记录不存在' },
|
|
23
|
+
{ status: 404 },
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return NextResponse.json({ execution });
|
|
28
|
+
} catch (error) {
|
|
29
|
+
return NextResponse.json(
|
|
30
|
+
{ error: error instanceof Error ? error.message : '获取执行记录失败' },
|
|
31
|
+
{ status: 500 },
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 任务执行历史 API - GET
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
6
|
+
import { getTaskExecutions } from '@/lib/db';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* GET /api/task-executions - 获取执行历史列表
|
|
10
|
+
*/
|
|
11
|
+
export async function GET(request: NextRequest) {
|
|
12
|
+
try {
|
|
13
|
+
const { searchParams } = new URL(request.url);
|
|
14
|
+
const filters = {
|
|
15
|
+
taskId: searchParams.get('taskId') ?? undefined,
|
|
16
|
+
status: searchParams.get('status') ?? undefined,
|
|
17
|
+
limit: searchParams.get('limit') ? parseInt(searchParams.get('limit')!) : undefined,
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const executions = await getTaskExecutions(filters);
|
|
21
|
+
|
|
22
|
+
return NextResponse.json({ executions });
|
|
23
|
+
} catch (error) {
|
|
24
|
+
return NextResponse.json(
|
|
25
|
+
{ error: error instanceof Error ? error.message : '获取执行历史失败' },
|
|
26
|
+
{ status: 500 },
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
}
|