pug-site-core 2.0.22 → 3.0.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.
@@ -0,0 +1,300 @@
1
+ import express from 'express';
2
+ import fs from 'fs-extra';
3
+ import path from 'path';
4
+ import { fileURLToPath } from 'url';
5
+
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = path.dirname(__filename);
8
+ const router = express.Router();
9
+
10
+ // 获取template-debug目录的绝对路径
11
+ const templateDebugPath = path.join(process.cwd(), 'template-debug');
12
+
13
+ // 检查是否设置了OpenAI API密钥
14
+ const hasOpenAIKey = !!process.env.OPENAI_API_KEY;
15
+
16
+ let model = null;
17
+ let agentExecutor = null;
18
+
19
+ // 动态导入langchain模块并初始化
20
+ async function initializeAI() {
21
+ if (!hasOpenAIKey) {
22
+ console.warn('警告: 未设置OPENAI_API_KEY环境变量,AI功能将不可用');
23
+ return false;
24
+ }
25
+
26
+ try {
27
+ const { ChatOpenAI } = await import('@langchain/openai');
28
+ const { AgentExecutor, createOpenAIFunctionsAgent } = await import('langchain/agents');
29
+ const { ChatPromptTemplate, MessagesPlaceholder } = await import('@langchain/core/prompts');
30
+ const { HumanMessage, AIMessage } = await import('@langchain/core/messages');
31
+ const { DynamicTool } = await import('@langchain/core/tools');
32
+
33
+ // 初始化OpenAI模型
34
+ model = new ChatOpenAI({
35
+ modelName: "gpt-3.5-turbo",
36
+ temperature: 0.1,
37
+ });
38
+
39
+ // 定义工具函数
40
+ const tools = [
41
+ new DynamicTool({
42
+ name: "read_file",
43
+ description: "读取template-debug目录下的文件内容。参数: filepath - 相对于template-debug的文件路径",
44
+ func: async (filepath) => {
45
+ try {
46
+ const fullPath = path.join(templateDebugPath, filepath);
47
+
48
+ // 安全检查:确保文件在template-debug目录内
49
+ if (!fullPath.startsWith(templateDebugPath)) {
50
+ return "错误:只能访问template-debug目录下的文件";
51
+ }
52
+
53
+ if (!await fs.pathExists(fullPath)) {
54
+ return `文件不存在: ${filepath}`;
55
+ }
56
+
57
+ const content = await fs.readFile(fullPath, 'utf-8');
58
+ return `文件内容 (${filepath}):\n${content}`;
59
+ } catch (error) {
60
+ return `读取文件失败: ${error.message}`;
61
+ }
62
+ }
63
+ }),
64
+
65
+ new DynamicTool({
66
+ name: "write_file",
67
+ description: "写入或修改template-debug目录下的文件。参数格式: filepath|content (用|分隔文件路径和内容)",
68
+ func: async (input) => {
69
+ try {
70
+ const [filepath, ...contentParts] = input.split('|');
71
+ const content = contentParts.join('|');
72
+
73
+ if (!filepath || content === undefined) {
74
+ return "错误:请提供文件路径和内容,格式: filepath|content";
75
+ }
76
+
77
+ const fullPath = path.join(templateDebugPath, filepath);
78
+
79
+ // 安全检查:确保文件在template-debug目录内
80
+ if (!fullPath.startsWith(templateDebugPath)) {
81
+ return "错误:只能修改template-debug目录下的文件";
82
+ }
83
+
84
+ // 确保目录存在
85
+ await fs.ensureDir(path.dirname(fullPath));
86
+
87
+ // 写入文件
88
+ await fs.writeFile(fullPath, content, 'utf-8');
89
+
90
+ return `文件已成功保存: ${filepath}`;
91
+ } catch (error) {
92
+ return `写入文件失败: ${error.message}`;
93
+ }
94
+ }
95
+ }),
96
+
97
+ new DynamicTool({
98
+ name: "list_files",
99
+ description: "列出template-debug目录下的文件和文件夹。参数: dirpath - 相对于template-debug的目录路径(可选,默认为根目录)",
100
+ func: async (dirpath = '') => {
101
+ try {
102
+ const fullPath = path.join(templateDebugPath, dirpath);
103
+
104
+ // 安全检查
105
+ if (!fullPath.startsWith(templateDebugPath)) {
106
+ return "错误:只能访问template-debug目录下的内容";
107
+ }
108
+
109
+ if (!await fs.pathExists(fullPath)) {
110
+ return `目录不存在: ${dirpath || '/'}`;
111
+ }
112
+
113
+ const items = await fs.readdir(fullPath, { withFileTypes: true });
114
+ const result = items.map(item => {
115
+ const type = item.isDirectory() ? '[目录]' : '[文件]';
116
+ return `${type} ${item.name}`;
117
+ }).join('\n');
118
+
119
+ return `目录内容 (${dirpath || '/'}):\n${result}`;
120
+ } catch (error) {
121
+ return `列出文件失败: ${error.message}`;
122
+ }
123
+ }
124
+ }),
125
+
126
+ new DynamicTool({
127
+ name: "delete_file",
128
+ description: "删除template-debug目录下的文件。参数: filepath - 相对于template-debug的文件路径",
129
+ func: async (filepath) => {
130
+ try {
131
+ const fullPath = path.join(templateDebugPath, filepath);
132
+
133
+ // 安全检查
134
+ if (!fullPath.startsWith(templateDebugPath)) {
135
+ return "错误:只能删除template-debug目录下的文件";
136
+ }
137
+
138
+ if (!await fs.pathExists(fullPath)) {
139
+ return `文件不存在: ${filepath}`;
140
+ }
141
+
142
+ await fs.remove(fullPath);
143
+ return `文件已删除: ${filepath}`;
144
+ } catch (error) {
145
+ return `删除文件失败: ${error.message}`;
146
+ }
147
+ }
148
+ })
149
+ ];
150
+
151
+ // 创建prompt模板
152
+ const prompt = ChatPromptTemplate.fromMessages([
153
+ ["system", `你是一个专业的代码修改助手。你可以帮助用户修改template-debug目录下的代码文件。
154
+
155
+ 你有以下工具可以使用:
156
+ 1. read_file - 读取文件内容
157
+ 2. write_file - 写入或修改文件内容
158
+ 3. list_files - 列出目录内容
159
+ 4. delete_file - 删除文件
160
+
161
+ 请根据用户的需求,使用这些工具来完成代码修改任务。在修改代码时,请:
162
+ - 仔细理解用户的需求
163
+ - 先读取相关文件了解现有代码结构
164
+ - 进行必要的修改
165
+ - 确保代码语法正确
166
+ - 提供清晰的修改说明
167
+
168
+ 始终用中文回复用户。`],
169
+ new MessagesPlaceholder("chat_history"),
170
+ ["human", "{input}"],
171
+ new MessagesPlaceholder("agent_scratchpad")
172
+ ]);
173
+
174
+ // 创建agent
175
+ const agent = await createOpenAIFunctionsAgent({
176
+ llm: model,
177
+ tools,
178
+ prompt
179
+ });
180
+
181
+ // 创建agent执行器
182
+ agentExecutor = new AgentExecutor({
183
+ agent,
184
+ tools,
185
+ verbose: true,
186
+ maxIterations: 10
187
+ });
188
+
189
+ console.log('AI代码修改助手初始化成功');
190
+ return true;
191
+ } catch (error) {
192
+ console.error('初始化AI失败:', error);
193
+ return false;
194
+ }
195
+ }
196
+
197
+ // 存储对话历史
198
+ const chatHistories = new Map();
199
+
200
+ // API路由
201
+ router.post('/chat', async (req, res) => {
202
+ try {
203
+ const { message, sessionId = 'default' } = req.body;
204
+
205
+ if (!message) {
206
+ return res.status(400).json({ error: '请提供消息内容' });
207
+ }
208
+
209
+ // 如果AI未初始化,尝试初始化
210
+ if (!agentExecutor) {
211
+ const initialized = await initializeAI();
212
+ if (!initialized) {
213
+ return res.status(500).json({
214
+ success: false,
215
+ error: 'AI服务不可用,请检查OPENAI_API_KEY环境变量是否设置'
216
+ });
217
+ }
218
+ }
219
+
220
+ // 获取或初始化对话历史
221
+ if (!chatHistories.has(sessionId)) {
222
+ chatHistories.set(sessionId, []);
223
+ }
224
+ const chatHistory = chatHistories.get(sessionId);
225
+
226
+ // 执行agent
227
+ const result = await agentExecutor.invoke({
228
+ input: message,
229
+ chat_history: chatHistory
230
+ });
231
+
232
+ // 更新对话历史
233
+ chatHistory.push(new HumanMessage(message));
234
+ chatHistory.push(new AIMessage(result.output));
235
+
236
+ // 限制历史记录长度
237
+ if (chatHistory.length > 20) {
238
+ chatHistory.splice(0, chatHistory.length - 20);
239
+ }
240
+
241
+ res.json({
242
+ success: true,
243
+ response: result.output,
244
+ sessionId
245
+ });
246
+
247
+ } catch (error) {
248
+ console.error('AI Agent错误:', error);
249
+ res.status(500).json({
250
+ success: false,
251
+ error: '处理请求时发生错误',
252
+ details: error.message
253
+ });
254
+ }
255
+ });
256
+
257
+ // 获取文件列表
258
+ router.get('/files', async (req, res) => {
259
+ try {
260
+ const { path: dirPath = '' } = req.query;
261
+ const fullPath = path.join(templateDebugPath, dirPath);
262
+
263
+ if (!fullPath.startsWith(templateDebugPath)) {
264
+ return res.status(400).json({ error: '无效的路径' });
265
+ }
266
+
267
+ if (!await fs.pathExists(fullPath)) {
268
+ return res.status(404).json({ error: '目录不存在' });
269
+ }
270
+
271
+ const items = await fs.readdir(fullPath, { withFileTypes: true });
272
+ const files = items.map(item => ({
273
+ name: item.name,
274
+ type: item.isDirectory() ? 'directory' : 'file',
275
+ path: path.join(dirPath, item.name).replace(/\\/g, '/')
276
+ }));
277
+
278
+ res.json({ success: true, files });
279
+ } catch (error) {
280
+ res.status(500).json({ error: '获取文件列表失败', details: error.message });
281
+ }
282
+ });
283
+
284
+ // 清除对话历史
285
+ router.delete('/chat/:sessionId', (req, res) => {
286
+ const { sessionId } = req.params;
287
+ chatHistories.delete(sessionId);
288
+ res.json({ success: true, message: '对话历史已清除' });
289
+ });
290
+
291
+ // 健康检查
292
+ router.get('/health', (req, res) => {
293
+ res.json({
294
+ success: true,
295
+ aiAvailable: !!agentExecutor,
296
+ hasOpenAIKey: hasOpenAIKey
297
+ });
298
+ });
299
+
300
+ export default router;