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
package/lib/workflow.ts
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 工作流引擎 - 工单状态管理和审批流程
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { Ticket, Approval } from './db';
|
|
6
|
+
import { getTicket, updateTicket, addApproval, createAuditLog } from './db';
|
|
7
|
+
|
|
8
|
+
export type TicketStatus = Ticket['status'];
|
|
9
|
+
|
|
10
|
+
// 状态转换规则
|
|
11
|
+
const STATUS_TRANSITIONS: Record<TicketStatus, TicketStatus[]> = {
|
|
12
|
+
draft: ['pending', 'deleted'],
|
|
13
|
+
pending: ['approved', 'rejected'],
|
|
14
|
+
approved: ['executing', 'pending'],
|
|
15
|
+
rejected: ['pending', 'deleted'],
|
|
16
|
+
executing: ['completed', 'failed'],
|
|
17
|
+
completed: [],
|
|
18
|
+
failed: ['pending'],
|
|
19
|
+
deleted: [],
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 检查状态转换是否允许
|
|
24
|
+
*/
|
|
25
|
+
export function canTransition(from: TicketStatus, to: TicketStatus): boolean {
|
|
26
|
+
if (from === 'deleted') return false;
|
|
27
|
+
return STATUS_TRANSITIONS[from]?.includes(to) ?? false;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 转换工单状态
|
|
32
|
+
*/
|
|
33
|
+
export async function transitionTicket(ticketId: string, newStatus: TicketStatus, userId: string): Promise<Ticket | null> {
|
|
34
|
+
const ticket = await getTicket(ticketId);
|
|
35
|
+
if (!ticket) {
|
|
36
|
+
throw new Error(`工单不存在: ${ticketId}`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (!canTransition(ticket.status, newStatus)) {
|
|
40
|
+
throw new Error(`不能将工单从 ${ticket.status} 转换为 ${newStatus}`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const updated = await updateTicket(ticketId, { status: newStatus });
|
|
44
|
+
|
|
45
|
+
// 记录审计日志
|
|
46
|
+
await createAuditLog({
|
|
47
|
+
action: 'status_transition',
|
|
48
|
+
userId,
|
|
49
|
+
ticketId,
|
|
50
|
+
sessionId: ticket.aiSessionId || undefined,
|
|
51
|
+
details: { from: ticket.status, to: newStatus },
|
|
52
|
+
status: 'success',
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
return updated;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* 提交工单审批
|
|
60
|
+
*/
|
|
61
|
+
export async function submitTicket(ticketId: string, userId: string): Promise<Ticket | null> {
|
|
62
|
+
return await transitionTicket(ticketId, 'pending', userId);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* 批准工单
|
|
67
|
+
*/
|
|
68
|
+
export async function approveTicket(ticketId: string, approver: string, comment?: string): Promise<Ticket | null> {
|
|
69
|
+
const ticket = await getTicket(ticketId);
|
|
70
|
+
if (!ticket) {
|
|
71
|
+
throw new Error(`工单不存在: ${ticketId}`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (ticket.status !== 'pending') {
|
|
75
|
+
throw new Error(`只能批准待审核状态的工单,当前状态: ${ticket.status}`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// 添加审批记录
|
|
79
|
+
await addApproval(ticketId, {
|
|
80
|
+
approver,
|
|
81
|
+
decision: 'approved',
|
|
82
|
+
comment,
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// 转换状态
|
|
86
|
+
return await transitionTicket(ticketId, 'approved', approver);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* 拒绝工单
|
|
91
|
+
*/
|
|
92
|
+
export async function rejectTicket(ticketId: string, approver: string, comment?: string): Promise<Ticket | null> {
|
|
93
|
+
const ticket = await getTicket(ticketId);
|
|
94
|
+
if (!ticket) {
|
|
95
|
+
throw new Error(`工单不存在: ${ticketId}`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (ticket.status !== 'pending') {
|
|
99
|
+
throw new Error(`只能拒绝待审核状态的工单,当前状态: ${ticket.status}`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// 添加审批记录
|
|
103
|
+
await addApproval(ticketId, {
|
|
104
|
+
approver,
|
|
105
|
+
decision: 'rejected',
|
|
106
|
+
comment,
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// 转换状态
|
|
110
|
+
return await transitionTicket(ticketId, 'rejected', approver);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* 开始执行
|
|
115
|
+
*/
|
|
116
|
+
export async function startExecution(ticketId: string, userId: string): Promise<Ticket | null> {
|
|
117
|
+
return await transitionTicket(ticketId, 'executing', userId);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* 完成执行
|
|
122
|
+
*/
|
|
123
|
+
export async function completeExecution(ticketId: string, userId: string): Promise<Ticket | null> {
|
|
124
|
+
return await transitionTicket(ticketId, 'completed', userId);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* 执行失败
|
|
129
|
+
*/
|
|
130
|
+
export async function failExecution(ticketId: string, userId: string, error?: string): Promise<Ticket | null> {
|
|
131
|
+
const ticket = await getTicket(ticketId);
|
|
132
|
+
const updated = await transitionTicket(ticketId, 'failed', userId);
|
|
133
|
+
|
|
134
|
+
// 记录错误详情
|
|
135
|
+
if (error && ticket) {
|
|
136
|
+
await createAuditLog({
|
|
137
|
+
action: 'execution_failed',
|
|
138
|
+
userId,
|
|
139
|
+
ticketId,
|
|
140
|
+
sessionId: ticket.aiSessionId || undefined,
|
|
141
|
+
details: { error },
|
|
142
|
+
status: 'failure',
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return updated;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* 重新提交
|
|
151
|
+
*/
|
|
152
|
+
export async function resubmitTicket(ticketId: string, userId: string): Promise<Ticket | null> {
|
|
153
|
+
return await transitionTicket(ticketId, 'pending', userId);
|
|
154
|
+
}
|
package/lib/zip-tool.ts
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ZIP打包和解压工具
|
|
3
|
+
* 用于技能市场的技能打包和安装
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import AdmZip from 'adm-zip';
|
|
7
|
+
import { mkdirSync, existsSync, unlinkSync } from 'fs';
|
|
8
|
+
import { join } from 'path';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* 将技能目录打包为ZIP
|
|
12
|
+
*
|
|
13
|
+
* @param skillPath 技能目录路径
|
|
14
|
+
* @param outputPath 输出ZIP文件路径
|
|
15
|
+
* @returns ZIP文件路径
|
|
16
|
+
*/
|
|
17
|
+
export function packSkillToZip(skillPath: string, outputPath: string): string {
|
|
18
|
+
try {
|
|
19
|
+
const zip = new AdmZip();
|
|
20
|
+
|
|
21
|
+
// 添加技能目录下的所有文件
|
|
22
|
+
zip.addLocalFolder(skillPath);
|
|
23
|
+
|
|
24
|
+
// 确保输出目录存在
|
|
25
|
+
const outputDir = join(outputPath, '..');
|
|
26
|
+
if (!existsSync(outputDir)) {
|
|
27
|
+
mkdirSync(outputDir, { recursive: true });
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// 写入ZIP文件
|
|
31
|
+
zip.writeZip(outputPath);
|
|
32
|
+
|
|
33
|
+
console.log(`[zip-tool] Packed skill to ${outputPath}`);
|
|
34
|
+
return outputPath;
|
|
35
|
+
} catch (error) {
|
|
36
|
+
console.error('[zip-tool] Failed to pack skill:', error);
|
|
37
|
+
throw new Error(`Failed to pack skill: ${error}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* 从ZIP解压技能
|
|
43
|
+
*
|
|
44
|
+
* @param zipPath ZIP文件路径
|
|
45
|
+
* @param outputPath 解压目标路径
|
|
46
|
+
* @returns 解压后的目录路径
|
|
47
|
+
*/
|
|
48
|
+
export function unpackSkillFromZip(zipPath: string, outputPath: string): string {
|
|
49
|
+
try {
|
|
50
|
+
const zip = new AdmZip(zipPath);
|
|
51
|
+
|
|
52
|
+
// 验证ZIP内容(防止路径穿越攻击)
|
|
53
|
+
const entries = zip.getEntries();
|
|
54
|
+
for (const entry of entries) {
|
|
55
|
+
const entryName = entry.entryName;
|
|
56
|
+
|
|
57
|
+
// 检查是否有路径穿越尝试
|
|
58
|
+
if (entryName.includes('..') || entryName.startsWith('/')) {
|
|
59
|
+
throw new Error(`Invalid zip entry: ${entryName}`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// 检查是否包含必要的文件
|
|
63
|
+
const normalized = entryName.replace(/\\/g, '/');
|
|
64
|
+
if (normalized === 'SKILL.md' || normalized === 'SKILL.json') {
|
|
65
|
+
// 必要文件存在
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// 确保输出目录存在
|
|
70
|
+
if (!existsSync(outputPath)) {
|
|
71
|
+
mkdirSync(outputPath, { recursive: true });
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// 解压文件
|
|
75
|
+
zip.extractAllTo(outputPath, true);
|
|
76
|
+
|
|
77
|
+
console.log(`[zip-tool] Unpacked skill to ${outputPath}`);
|
|
78
|
+
return outputPath;
|
|
79
|
+
} catch (error) {
|
|
80
|
+
console.error('[zip-tool] Failed to unpack skill:', error);
|
|
81
|
+
throw new Error(`Failed to unpack skill: ${error}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* 验证ZIP包是否是有效的技能包
|
|
87
|
+
*
|
|
88
|
+
* @param zipPath ZIP文件路径
|
|
89
|
+
* @returns 验证结果
|
|
90
|
+
*/
|
|
91
|
+
export function validateSkillZip(zipPath: string): { valid: boolean; error?: string } {
|
|
92
|
+
try {
|
|
93
|
+
const zip = new AdmZip(zipPath);
|
|
94
|
+
const entries = zip.getEntries();
|
|
95
|
+
|
|
96
|
+
let hasSkillJson = false;
|
|
97
|
+
let hasSkillMd = false;
|
|
98
|
+
|
|
99
|
+
for (const entry of entries) {
|
|
100
|
+
const normalized = entry.entryName.replace(/\\/g, '/');
|
|
101
|
+
|
|
102
|
+
if (normalized === 'SKILL.json') {
|
|
103
|
+
hasSkillJson = true;
|
|
104
|
+
// 验证JSON格式
|
|
105
|
+
try {
|
|
106
|
+
const content = entry.getData().toString('utf-8');
|
|
107
|
+
JSON.parse(content);
|
|
108
|
+
} catch {
|
|
109
|
+
return { valid: false, error: 'SKILL.json is not valid JSON' };
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (normalized === 'SKILL.md') {
|
|
114
|
+
hasSkillMd = true;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (!hasSkillJson) {
|
|
119
|
+
return { valid: false, error: 'SKILL.json not found in zip' };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (!hasSkillMd) {
|
|
123
|
+
return { valid: false, error: 'SKILL.md not found in zip' };
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return { valid: true };
|
|
127
|
+
} catch (error: any) {
|
|
128
|
+
return { valid: false, error: error.message };
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* 从ZIP中读取SKILL.json
|
|
134
|
+
*
|
|
135
|
+
* @param zipPath ZIP文件路径
|
|
136
|
+
* @returns SKILL.json 内容
|
|
137
|
+
*/
|
|
138
|
+
export function readSkillJsonFromZip(zipPath: string): any {
|
|
139
|
+
try {
|
|
140
|
+
const zip = new AdmZip(zipPath);
|
|
141
|
+
const entry = zip.getEntries().find(e => e.entryName.replace(/\\/g, '/') === 'SKILL.json');
|
|
142
|
+
|
|
143
|
+
if (!entry) {
|
|
144
|
+
throw new Error('SKILL.json not found in zip');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const content = entry.getData().toString('utf-8');
|
|
148
|
+
return JSON.parse(content);
|
|
149
|
+
} catch (error) {
|
|
150
|
+
console.error('[zip-tool] Failed to read SKILL.json:', error);
|
|
151
|
+
throw error;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* 从ZIP中读取SKILL.md
|
|
157
|
+
*
|
|
158
|
+
* @param zipPath ZIP文件路径
|
|
159
|
+
* @returns SKILL.md 内容
|
|
160
|
+
*/
|
|
161
|
+
export function readSkillMdFromZip(zipPath: string): string {
|
|
162
|
+
try {
|
|
163
|
+
const zip = new AdmZip(zipPath);
|
|
164
|
+
const entry = zip.getEntries().find(e => e.entryName.replace(/\\/g, '/') === 'SKILL.md');
|
|
165
|
+
|
|
166
|
+
if (!entry) {
|
|
167
|
+
throw new Error('SKILL.md not found in zip');
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return entry.getData().toString('utf-8');
|
|
171
|
+
} catch (error) {
|
|
172
|
+
console.error('[zip-tool] Failed to read SKILL.md:', error);
|
|
173
|
+
throw error;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* 删除临时ZIP文件
|
|
179
|
+
*
|
|
180
|
+
* @param zipPath ZIP文件路径
|
|
181
|
+
*/
|
|
182
|
+
export function cleanupZipFile(zipPath: string): void {
|
|
183
|
+
try {
|
|
184
|
+
if (existsSync(zipPath)) {
|
|
185
|
+
unlinkSync(zipPath);
|
|
186
|
+
console.log(`[zip-tool] Cleaned up zip file: ${zipPath}`);
|
|
187
|
+
}
|
|
188
|
+
} catch (error) {
|
|
189
|
+
console.error('[zip-tool] Failed to cleanup zip file:', error);
|
|
190
|
+
}
|
|
191
|
+
}
|
package/next.config.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "work-agent",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "AI work agent with local file system access, powered by Next.js",
|
|
5
|
+
"bin": {
|
|
6
|
+
"work-agent": "./bin/work-agent.js"
|
|
7
|
+
},
|
|
8
|
+
"scripts": {
|
|
9
|
+
"dev": "next dev",
|
|
10
|
+
"build": "next build && npm rebuild better-sqlite3 --prefix .next/standalone",
|
|
11
|
+
"start": "next start",
|
|
12
|
+
"lint": "next lint",
|
|
13
|
+
"test": "npm run test:sse",
|
|
14
|
+
"test:sse": "node tests/sse-heartbeat/test.js",
|
|
15
|
+
"postinstall": "node bin/postinstall.js"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@kubernetes/client-node": "^0.20.0",
|
|
19
|
+
"@mariozechner/pi-coding-agent": "^0.53.1",
|
|
20
|
+
"@radix-ui/react-accordion": "^1.2.12",
|
|
21
|
+
"@radix-ui/react-avatar": "^1.1.11",
|
|
22
|
+
"@radix-ui/react-collapsible": "^1.1.12",
|
|
23
|
+
"@radix-ui/react-dialog": "^1.1.15",
|
|
24
|
+
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
|
25
|
+
"@radix-ui/react-hover-card": "^1.1.15",
|
|
26
|
+
"@radix-ui/react-label": "^2.1.8",
|
|
27
|
+
"@radix-ui/react-popover": "^1.1.15",
|
|
28
|
+
"@radix-ui/react-progress": "^1.1.8",
|
|
29
|
+
"@radix-ui/react-scroll-area": "^1.2.10",
|
|
30
|
+
"@radix-ui/react-select": "^2.2.6",
|
|
31
|
+
"@radix-ui/react-separator": "^1.1.8",
|
|
32
|
+
"@radix-ui/react-slot": "^1.2.4",
|
|
33
|
+
"@radix-ui/react-switch": "^1.2.6",
|
|
34
|
+
"@radix-ui/react-tabs": "^1.1.13",
|
|
35
|
+
"@radix-ui/react-tooltip": "^1.2.8",
|
|
36
|
+
"@radix-ui/react-use-controllable-state": "^1.2.2",
|
|
37
|
+
"@rive-app/react-webgl2": "^4.27.0",
|
|
38
|
+
"@streamdown/cjk": "^1.0.2",
|
|
39
|
+
"@streamdown/code": "^1.0.3",
|
|
40
|
+
"@streamdown/math": "^1.0.2",
|
|
41
|
+
"@streamdown/mermaid": "^1.0.2",
|
|
42
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
43
|
+
"@uiw/react-md-editor": "^4.0.11",
|
|
44
|
+
"@xyflow/react": "^12.10.1",
|
|
45
|
+
"adm-zip": "^0.5.16",
|
|
46
|
+
"ai": "^6.0.103",
|
|
47
|
+
"ai-elements": "^1.8.4",
|
|
48
|
+
"ansi-to-react": "^6.2.6",
|
|
49
|
+
"axios": "^1.13.5",
|
|
50
|
+
"better-sqlite3": "^12.6.2",
|
|
51
|
+
"class-variance-authority": "^0.7.1",
|
|
52
|
+
"clsx": "^2.1.1",
|
|
53
|
+
"cmdk": "^1.1.1",
|
|
54
|
+
"cron-parser": "^5.5.0",
|
|
55
|
+
"embla-carousel-react": "^8.6.0",
|
|
56
|
+
"highlight.js": "^11.11.1",
|
|
57
|
+
"js-yaml": "^4.1.0",
|
|
58
|
+
"lucide-react": "^0.563.0",
|
|
59
|
+
"media-chrome": "^4.18.0",
|
|
60
|
+
"motion": "^12.34.3",
|
|
61
|
+
"nanoid": "^5.1.6",
|
|
62
|
+
"next": "^15.0.0",
|
|
63
|
+
"open": "^10.2.0",
|
|
64
|
+
"pinyin-pro": "^3.24.5",
|
|
65
|
+
"react": "^19.0.0",
|
|
66
|
+
"react-dom": "^19.0.0",
|
|
67
|
+
"react-jsx-parser": "^2.4.1",
|
|
68
|
+
"recharts": "^3.7.0",
|
|
69
|
+
"shiki": "^3.23.0",
|
|
70
|
+
"streamdown": "^2.3.0",
|
|
71
|
+
"tailwind-merge": "^3.4.0",
|
|
72
|
+
"tailwindcss": "^3.4.19",
|
|
73
|
+
"tailwindcss-animate": "^1.0.7",
|
|
74
|
+
"tokenlens": "^1.3.1",
|
|
75
|
+
"use-stick-to-bottom": "^1.1.3"
|
|
76
|
+
},
|
|
77
|
+
"devDependencies": {
|
|
78
|
+
"@types/adm-zip": "^0.5.7",
|
|
79
|
+
"@types/js-yaml": "^4.0.9",
|
|
80
|
+
"@types/node": "^22.19.11",
|
|
81
|
+
"@types/react": "^19.0.0",
|
|
82
|
+
"@types/react-dom": "^19.0.0",
|
|
83
|
+
"@types/sql.js": "^1.4.9",
|
|
84
|
+
"autoprefixer": "^10.4.20",
|
|
85
|
+
"postcss": "^8.4.49",
|
|
86
|
+
"typescript": "^5.7.3",
|
|
87
|
+
"vitest": "^4.0.18"
|
|
88
|
+
},
|
|
89
|
+
"engines": {
|
|
90
|
+
"node": ">=18.18.0"
|
|
91
|
+
},
|
|
92
|
+
"files": [
|
|
93
|
+
"app",
|
|
94
|
+
"lib",
|
|
95
|
+
"public",
|
|
96
|
+
"bin",
|
|
97
|
+
"components",
|
|
98
|
+
"hooks",
|
|
99
|
+
"types",
|
|
100
|
+
"next.config.js",
|
|
101
|
+
"tailwind.config.ts",
|
|
102
|
+
"postcss.config.mjs",
|
|
103
|
+
"tsconfig.json",
|
|
104
|
+
"components.json"
|
|
105
|
+
]
|
|
106
|
+
}
|
package/public/.gitkeep
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
public directory placeholder
|
package/public/icon.svg
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="14" x="3" y="4" rx="2"/><path d="M12 2v2"/><circle cx="12" cy="12" r="2"/><path d="M8 14v.01"/><path d="M16 14v.01"/><path d="M9 18v.01"/><path d="M15 18v.01"/></svg>
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"lib": [
|
|
5
|
+
"ES2023",
|
|
6
|
+
"DOM",
|
|
7
|
+
"DOM.Iterable"
|
|
8
|
+
],
|
|
9
|
+
"jsx": "preserve",
|
|
10
|
+
"module": "ESNext",
|
|
11
|
+
"moduleResolution": "bundler",
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"allowJs": true,
|
|
14
|
+
"strict": true,
|
|
15
|
+
"noEmit": true,
|
|
16
|
+
"esModuleInterop": true,
|
|
17
|
+
"skipLibCheck": true,
|
|
18
|
+
"forceConsistentCasingInFileNames": true,
|
|
19
|
+
"incremental": true,
|
|
20
|
+
"paths": {
|
|
21
|
+
"@/*": [
|
|
22
|
+
"./*"
|
|
23
|
+
]
|
|
24
|
+
},
|
|
25
|
+
"plugins": [
|
|
26
|
+
{
|
|
27
|
+
"name": "next"
|
|
28
|
+
}
|
|
29
|
+
],
|
|
30
|
+
"isolatedModules": true
|
|
31
|
+
},
|
|
32
|
+
"include": [
|
|
33
|
+
"next-env.d.ts",
|
|
34
|
+
"**/*.ts",
|
|
35
|
+
"**/*.tsx",
|
|
36
|
+
".next/types/**/*.ts"
|
|
37
|
+
],
|
|
38
|
+
"exclude": [
|
|
39
|
+
"node_modules",
|
|
40
|
+
"tools"
|
|
41
|
+
]
|
|
42
|
+
}
|