agentstudio 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/.env +15 -0
- package/README.md +85 -0
- package/dist/bin/agentstudio.d.ts +3 -0
- package/dist/bin/agentstudio.d.ts.map +1 -0
- package/dist/bin/agentstudio.js +141 -0
- package/dist/bin/agentstudio.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +87 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware/auth.d.ts +7 -0
- package/dist/middleware/auth.d.ts.map +1 -0
- package/dist/middleware/auth.js +21 -0
- package/dist/middleware/auth.js.map +1 -0
- package/dist/routes/agents.d.ts +4 -0
- package/dist/routes/agents.d.ts.map +1 -0
- package/dist/routes/agents.js +804 -0
- package/dist/routes/agents.js.map +1 -0
- package/dist/routes/auth.d.ts +4 -0
- package/dist/routes/auth.d.ts.map +1 -0
- package/dist/routes/auth.js +60 -0
- package/dist/routes/auth.js.map +1 -0
- package/dist/routes/files.d.ts +4 -0
- package/dist/routes/files.d.ts.map +1 -0
- package/dist/routes/files.js +301 -0
- package/dist/routes/files.js.map +1 -0
- package/dist/routes/mcp.d.ts +4 -0
- package/dist/routes/mcp.d.ts.map +1 -0
- package/dist/routes/mcp.js +652 -0
- package/dist/routes/mcp.js.map +1 -0
- package/dist/routes/media.d.ts +5 -0
- package/dist/routes/media.d.ts.map +1 -0
- package/dist/routes/media.js +117 -0
- package/dist/routes/media.js.map +1 -0
- package/dist/routes/slides.d.ts +4 -0
- package/dist/routes/slides.d.ts.map +1 -0
- package/dist/routes/slides.js +146 -0
- package/dist/routes/slides.js.map +1 -0
- package/dist/services/claudeSession.d.ts +83 -0
- package/dist/services/claudeSession.d.ts.map +1 -0
- package/dist/services/claudeSession.js +255 -0
- package/dist/services/claudeSession.js.map +1 -0
- package/dist/services/messageQueue.d.ts +31 -0
- package/dist/services/messageQueue.d.ts.map +1 -0
- package/dist/services/messageQueue.js +67 -0
- package/dist/services/messageQueue.js.map +1 -0
- package/dist/services/sessionManager.d.ts +132 -0
- package/dist/services/sessionManager.d.ts.map +1 -0
- package/dist/services/sessionManager.js +439 -0
- package/dist/services/sessionManager.js.map +1 -0
- package/dist/types/claude-history.d.ts +48 -0
- package/dist/types/claude-history.d.ts.map +1 -0
- package/dist/types/claude-history.js +2 -0
- package/dist/types/claude-history.js.map +1 -0
- package/dist/types/claude-versions.d.ts +31 -0
- package/dist/types/claude-versions.d.ts.map +1 -0
- package/dist/types/claude-versions.js +2 -0
- package/dist/types/claude-versions.js.map +1 -0
- package/dist/types/commands.d.ts +32 -0
- package/dist/types/commands.d.ts.map +1 -0
- package/dist/types/commands.js +2 -0
- package/dist/types/commands.js.map +1 -0
- package/dist/types/index.d.ts +81 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +150 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/subagents.d.ts +88 -0
- package/dist/types/subagents.d.ts.map +1 -0
- package/dist/types/subagents.js +2 -0
- package/dist/types/subagents.js.map +1 -0
- package/dist/utils/agentStorage.d.ts +19 -0
- package/dist/utils/agentStorage.d.ts.map +1 -0
- package/dist/utils/agentStorage.js +110 -0
- package/dist/utils/agentStorage.js.map +1 -0
- package/dist/utils/claudeVersionStorage.d.ts +33 -0
- package/dist/utils/claudeVersionStorage.d.ts.map +1 -0
- package/dist/utils/claudeVersionStorage.js +168 -0
- package/dist/utils/claudeVersionStorage.js.map +1 -0
- package/dist/utils/jwt.d.ts +15 -0
- package/dist/utils/jwt.d.ts.map +1 -0
- package/dist/utils/jwt.js +28 -0
- package/dist/utils/jwt.js.map +1 -0
- package/dist/utils/projectMetadataStorage.d.ts +21 -0
- package/dist/utils/projectMetadataStorage.d.ts.map +1 -0
- package/dist/utils/projectMetadataStorage.js +68 -0
- package/dist/utils/projectMetadataStorage.js.map +1 -0
- package/frontend/dist/index.html +86 -0
- package/package.json +66 -0
- package/src/bin/agentstudio.ts +161 -0
- package/src/index.ts +100 -0
- package/src/middleware/auth.ts +26 -0
- package/src/routes/agents.ts +885 -0
- package/src/routes/auth.ts +73 -0
- package/src/routes/commands.ts.bak +441 -0
- package/src/routes/files.ts +352 -0
- package/src/routes/mcp.ts +751 -0
- package/src/routes/media.ts +140 -0
- package/src/routes/projects.ts.bak +601 -0
- package/src/routes/sessions.ts.bak +809 -0
- package/src/routes/settings.ts.bak +718 -0
- package/src/routes/slides.ts +170 -0
- package/src/routes/subagents.ts.bak +364 -0
- package/src/services/claudeSession.ts +293 -0
- package/src/services/messageQueue.ts +71 -0
- package/src/services/sessionManager.ts +532 -0
- package/src/types/claude-history.ts +50 -0
- package/src/types/claude-versions.ts +33 -0
- package/src/types/commands.ts +35 -0
- package/src/types/index.ts +248 -0
- package/src/types/subagents.ts +106 -0
- package/src/utils/agentStorage.ts +126 -0
- package/src/utils/claudeVersionStorage.ts +199 -0
- package/src/utils/jwt.ts +36 -0
- package/src/utils/projectMetadataStorage.ts +86 -0
- package/tsconfig.json +26 -0
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import fs from 'fs-extra';
|
|
3
|
+
import { existsSync } from 'fs';
|
|
4
|
+
import { join, dirname, resolve, relative } from 'path';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
import * as os from 'os';
|
|
8
|
+
import * as path from 'path';
|
|
9
|
+
import { getProjectId } from './media.js';
|
|
10
|
+
|
|
11
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
12
|
+
const __dirname = dirname(__filename);
|
|
13
|
+
|
|
14
|
+
const router: express.Router = express.Router();
|
|
15
|
+
|
|
16
|
+
// Get working directory (project root or specified project path)
|
|
17
|
+
const getWorkingDir = (projectPath?: string) => {
|
|
18
|
+
if (projectPath) {
|
|
19
|
+
return resolve(projectPath);
|
|
20
|
+
}
|
|
21
|
+
return resolve(__dirname, '../../..');
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// Validation schemas
|
|
25
|
+
const ReadFileSchema = z.object({
|
|
26
|
+
path: z.string()
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const ReadFilesSchema = z.object({
|
|
30
|
+
paths: z.array(z.string())
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const WriteFileSchema = z.object({
|
|
34
|
+
path: z.string(),
|
|
35
|
+
content: z.string()
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Helper function to resolve and validate file path
|
|
39
|
+
const resolveSafePath = (filePath: string, projectPath?: string): string => {
|
|
40
|
+
const workingDir = getWorkingDir(projectPath);
|
|
41
|
+
const resolvedPath = resolve(workingDir, filePath);
|
|
42
|
+
|
|
43
|
+
// Ensure the path is within the working directory for security
|
|
44
|
+
const relativePath = relative(workingDir, resolvedPath);
|
|
45
|
+
if (relativePath.startsWith('..') || resolve(workingDir, relativePath) !== resolvedPath) {
|
|
46
|
+
throw new Error('Path is outside working directory');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return resolvedPath;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// GET /api/files/read - Read a single file
|
|
53
|
+
router.get('/read', async (req, res) => {
|
|
54
|
+
try {
|
|
55
|
+
const { path, projectPath, binary } = req.query;
|
|
56
|
+
|
|
57
|
+
if (!path || typeof path !== 'string') {
|
|
58
|
+
return res.status(400).json({ error: 'File path is required' });
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const fullPath = resolveSafePath(path, typeof projectPath === 'string' ? projectPath : undefined);
|
|
62
|
+
|
|
63
|
+
if (!existsSync(fullPath)) {
|
|
64
|
+
return res.status(404).json({ error: 'File not found' });
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// 如果是二进制文件请求(如图片),直接发送文件内容
|
|
68
|
+
if (binary === 'true') {
|
|
69
|
+
const stats = await fs.stat(fullPath);
|
|
70
|
+
if (!stats.isFile()) {
|
|
71
|
+
return res.status(400).json({ error: 'Path is not a file' });
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// 根据文件扩展名设置正确的Content-Type
|
|
75
|
+
const ext = path.toLowerCase().split('.').pop();
|
|
76
|
+
const mimeTypes: Record<string, string> = {
|
|
77
|
+
'png': 'image/png',
|
|
78
|
+
'jpg': 'image/jpeg',
|
|
79
|
+
'jpeg': 'image/jpeg',
|
|
80
|
+
'gif': 'image/gif',
|
|
81
|
+
'svg': 'image/svg+xml',
|
|
82
|
+
'webp': 'image/webp',
|
|
83
|
+
'ico': 'image/x-icon',
|
|
84
|
+
'bmp': 'image/bmp',
|
|
85
|
+
'tiff': 'image/tiff'
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const mimeType = mimeTypes[ext || ''] || 'application/octet-stream';
|
|
89
|
+
res.setHeader('Content-Type', mimeType);
|
|
90
|
+
res.setHeader('Cache-Control', 'public, max-age=3600'); // 1小时缓存
|
|
91
|
+
|
|
92
|
+
// 直接发送文件流
|
|
93
|
+
const fileStream = fs.createReadStream(fullPath);
|
|
94
|
+
fileStream.pipe(res);
|
|
95
|
+
|
|
96
|
+
fileStream.on('error', (error) => {
|
|
97
|
+
console.error('Error streaming file:', error);
|
|
98
|
+
if (!res.headersSent) {
|
|
99
|
+
res.status(500).json({ error: 'Failed to read file' });
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// 对于文本文件,仍然使用utf-8编码
|
|
107
|
+
const content = await fs.readFile(fullPath, 'utf-8');
|
|
108
|
+
|
|
109
|
+
res.json({
|
|
110
|
+
path,
|
|
111
|
+
content,
|
|
112
|
+
exists: true
|
|
113
|
+
});
|
|
114
|
+
} catch (error) {
|
|
115
|
+
console.error('Error reading file:', error);
|
|
116
|
+
if (error instanceof Error && error.message === 'Path is outside working directory') {
|
|
117
|
+
return res.status(403).json({ error: 'Access denied' });
|
|
118
|
+
}
|
|
119
|
+
res.status(500).json({ error: 'Failed to read file' });
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// POST /api/files/read-multiple - Read multiple files
|
|
124
|
+
router.post('/read-multiple', async (req, res) => {
|
|
125
|
+
try {
|
|
126
|
+
const validation = ReadFilesSchema.safeParse(req.body);
|
|
127
|
+
if (!validation.success) {
|
|
128
|
+
return res.status(400).json({ error: 'Invalid request body', details: validation.error });
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const { paths } = validation.data;
|
|
132
|
+
const { projectPath } = req.query;
|
|
133
|
+
|
|
134
|
+
const results = await Promise.allSettled(
|
|
135
|
+
paths.map(async (path) => {
|
|
136
|
+
try {
|
|
137
|
+
const fullPath = resolveSafePath(path, typeof projectPath === 'string' ? projectPath : undefined);
|
|
138
|
+
const exists = existsSync(fullPath);
|
|
139
|
+
|
|
140
|
+
if (!exists) {
|
|
141
|
+
return {
|
|
142
|
+
path,
|
|
143
|
+
content: null,
|
|
144
|
+
exists: false,
|
|
145
|
+
error: 'File not found'
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const content = await fs.readFile(fullPath, 'utf-8');
|
|
150
|
+
return {
|
|
151
|
+
path,
|
|
152
|
+
content,
|
|
153
|
+
exists: true
|
|
154
|
+
};
|
|
155
|
+
} catch (error) {
|
|
156
|
+
return {
|
|
157
|
+
path,
|
|
158
|
+
content: null,
|
|
159
|
+
exists: false,
|
|
160
|
+
error: error instanceof Error ? error.message : 'Unknown error'
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
})
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
const files = results.map((result, index) => {
|
|
167
|
+
if (result.status === 'fulfilled') {
|
|
168
|
+
return result.value;
|
|
169
|
+
} else {
|
|
170
|
+
return {
|
|
171
|
+
path: paths[index],
|
|
172
|
+
content: null,
|
|
173
|
+
exists: false,
|
|
174
|
+
error: result.reason
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
res.json({ files });
|
|
180
|
+
} catch (error) {
|
|
181
|
+
console.error('Error reading files:', error);
|
|
182
|
+
res.status(500).json({ error: 'Failed to read files' });
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// PUT /api/files/write - Write to a single file
|
|
187
|
+
router.put('/write', async (req, res) => {
|
|
188
|
+
try {
|
|
189
|
+
const validation = WriteFileSchema.safeParse(req.body);
|
|
190
|
+
if (!validation.success) {
|
|
191
|
+
return res.status(400).json({ error: 'Invalid request body', details: validation.error });
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const { path, content } = validation.data;
|
|
195
|
+
const { projectPath } = req.query;
|
|
196
|
+
const fullPath = resolveSafePath(path, typeof projectPath === 'string' ? projectPath : undefined);
|
|
197
|
+
|
|
198
|
+
// Ensure directory exists
|
|
199
|
+
await fs.ensureDir(dirname(fullPath));
|
|
200
|
+
|
|
201
|
+
// Write the file
|
|
202
|
+
await fs.writeFile(fullPath, content, 'utf-8');
|
|
203
|
+
|
|
204
|
+
res.json({
|
|
205
|
+
success: true,
|
|
206
|
+
message: 'File written successfully',
|
|
207
|
+
path
|
|
208
|
+
});
|
|
209
|
+
} catch (error) {
|
|
210
|
+
console.error('Error writing file:', error);
|
|
211
|
+
if (error instanceof Error && error.message === 'Path is outside working directory') {
|
|
212
|
+
return res.status(403).json({ error: 'Access denied' });
|
|
213
|
+
}
|
|
214
|
+
res.status(500).json({ error: 'Failed to write file' });
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
// GET /api/files/project-id - Get project ID for a given project path
|
|
219
|
+
router.get('/project-id', async (req, res) => {
|
|
220
|
+
try {
|
|
221
|
+
const { projectPath } = req.query;
|
|
222
|
+
|
|
223
|
+
if (!projectPath || typeof projectPath !== 'string') {
|
|
224
|
+
return res.status(400).json({ error: 'Project path is required' });
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const projectId = getProjectId(projectPath);
|
|
228
|
+
|
|
229
|
+
res.json({
|
|
230
|
+
projectId,
|
|
231
|
+
projectPath
|
|
232
|
+
});
|
|
233
|
+
} catch (error) {
|
|
234
|
+
console.error('Error getting project ID:', error);
|
|
235
|
+
res.status(500).json({ error: 'Failed to get project ID' });
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
// ========== FILESYSTEM ROUTES MIGRATED FROM AGENTS.TS ==========
|
|
240
|
+
|
|
241
|
+
// GET /api/files/browse - Browse file system
|
|
242
|
+
router.get('/browse', (req, res) => {
|
|
243
|
+
try {
|
|
244
|
+
const { path: requestedPath } = req.query;
|
|
245
|
+
|
|
246
|
+
// Default to home directory if no path provided
|
|
247
|
+
const browsePath = requestedPath ? String(requestedPath) : os.homedir();
|
|
248
|
+
|
|
249
|
+
// Security check: ensure path is safe
|
|
250
|
+
if (browsePath.includes('..') || !path.isAbsolute(browsePath)) {
|
|
251
|
+
return res.status(400).json({ error: 'Invalid path' });
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (!fs.existsSync(browsePath)) {
|
|
255
|
+
return res.status(404).json({ error: 'Path not found' });
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const stats = fs.statSync(browsePath);
|
|
259
|
+
|
|
260
|
+
if (!stats.isDirectory()) {
|
|
261
|
+
return res.status(400).json({ error: 'Path is not a directory' });
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const items = fs.readdirSync(browsePath)
|
|
265
|
+
.map(name => {
|
|
266
|
+
const itemPath = path.join(browsePath, name);
|
|
267
|
+
try {
|
|
268
|
+
const itemStats = fs.statSync(itemPath);
|
|
269
|
+
return {
|
|
270
|
+
name,
|
|
271
|
+
path: itemPath,
|
|
272
|
+
isDirectory: itemStats.isDirectory(),
|
|
273
|
+
size: itemStats.isDirectory() ? null : itemStats.size,
|
|
274
|
+
modified: itemStats.mtime.toISOString(),
|
|
275
|
+
isHidden: name.startsWith('.')
|
|
276
|
+
};
|
|
277
|
+
} catch (error) {
|
|
278
|
+
// Skip items that can't be read
|
|
279
|
+
return null;
|
|
280
|
+
}
|
|
281
|
+
})
|
|
282
|
+
.filter(item => item !== null)
|
|
283
|
+
.sort((a, b) => {
|
|
284
|
+
// Directories first, then by name
|
|
285
|
+
if (a.isDirectory !== b.isDirectory) {
|
|
286
|
+
return a.isDirectory ? -1 : 1;
|
|
287
|
+
}
|
|
288
|
+
return a.name.localeCompare(b.name);
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
// Get parent directory info
|
|
292
|
+
const parentPath = path.dirname(browsePath);
|
|
293
|
+
const canGoUp = browsePath !== parentPath;
|
|
294
|
+
|
|
295
|
+
res.json({
|
|
296
|
+
currentPath: browsePath,
|
|
297
|
+
parentPath: canGoUp ? parentPath : null,
|
|
298
|
+
items
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
} catch (error) {
|
|
302
|
+
console.error('File browser error:', error);
|
|
303
|
+
res.status(500).json({ error: 'Failed to browse directory' });
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
// POST /api/files/create-directory - Create new directory
|
|
308
|
+
router.post('/create-directory', (req, res) => {
|
|
309
|
+
try {
|
|
310
|
+
const { parentPath, directoryName } = req.body;
|
|
311
|
+
|
|
312
|
+
if (!parentPath || !directoryName) {
|
|
313
|
+
return res.status(400).json({ error: 'Parent path and directory name are required' });
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Security checks
|
|
317
|
+
if (directoryName.includes('..') || directoryName.includes('/') || directoryName.includes('\\')) {
|
|
318
|
+
return res.status(400).json({ error: 'Invalid directory name' });
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (parentPath.includes('..') || !path.isAbsolute(parentPath)) {
|
|
322
|
+
return res.status(400).json({ error: 'Invalid parent path' });
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (!fs.existsSync(parentPath)) {
|
|
326
|
+
return res.status(404).json({ error: 'Parent directory not found' });
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const newDirPath = path.join(parentPath, directoryName);
|
|
330
|
+
|
|
331
|
+
if (fs.existsSync(newDirPath)) {
|
|
332
|
+
return res.status(409).json({ error: 'Directory already exists' });
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
fs.mkdirSync(newDirPath, { recursive: true });
|
|
336
|
+
|
|
337
|
+
res.json({
|
|
338
|
+
success: true,
|
|
339
|
+
directoryPath: newDirPath,
|
|
340
|
+
message: `Directory "${directoryName}" created successfully`
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
} catch (error) {
|
|
344
|
+
console.error('Create directory error:', error);
|
|
345
|
+
res.status(500).json({
|
|
346
|
+
error: 'Failed to create directory',
|
|
347
|
+
details: error instanceof Error ? error.message : String(error)
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
export default router;
|