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.
Files changed (115) hide show
  1. package/.env +15 -0
  2. package/README.md +85 -0
  3. package/dist/bin/agentstudio.d.ts +3 -0
  4. package/dist/bin/agentstudio.d.ts.map +1 -0
  5. package/dist/bin/agentstudio.js +141 -0
  6. package/dist/bin/agentstudio.js.map +1 -0
  7. package/dist/index.d.ts +2 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +87 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/middleware/auth.d.ts +7 -0
  12. package/dist/middleware/auth.d.ts.map +1 -0
  13. package/dist/middleware/auth.js +21 -0
  14. package/dist/middleware/auth.js.map +1 -0
  15. package/dist/routes/agents.d.ts +4 -0
  16. package/dist/routes/agents.d.ts.map +1 -0
  17. package/dist/routes/agents.js +804 -0
  18. package/dist/routes/agents.js.map +1 -0
  19. package/dist/routes/auth.d.ts +4 -0
  20. package/dist/routes/auth.d.ts.map +1 -0
  21. package/dist/routes/auth.js +60 -0
  22. package/dist/routes/auth.js.map +1 -0
  23. package/dist/routes/files.d.ts +4 -0
  24. package/dist/routes/files.d.ts.map +1 -0
  25. package/dist/routes/files.js +301 -0
  26. package/dist/routes/files.js.map +1 -0
  27. package/dist/routes/mcp.d.ts +4 -0
  28. package/dist/routes/mcp.d.ts.map +1 -0
  29. package/dist/routes/mcp.js +652 -0
  30. package/dist/routes/mcp.js.map +1 -0
  31. package/dist/routes/media.d.ts +5 -0
  32. package/dist/routes/media.d.ts.map +1 -0
  33. package/dist/routes/media.js +117 -0
  34. package/dist/routes/media.js.map +1 -0
  35. package/dist/routes/slides.d.ts +4 -0
  36. package/dist/routes/slides.d.ts.map +1 -0
  37. package/dist/routes/slides.js +146 -0
  38. package/dist/routes/slides.js.map +1 -0
  39. package/dist/services/claudeSession.d.ts +83 -0
  40. package/dist/services/claudeSession.d.ts.map +1 -0
  41. package/dist/services/claudeSession.js +255 -0
  42. package/dist/services/claudeSession.js.map +1 -0
  43. package/dist/services/messageQueue.d.ts +31 -0
  44. package/dist/services/messageQueue.d.ts.map +1 -0
  45. package/dist/services/messageQueue.js +67 -0
  46. package/dist/services/messageQueue.js.map +1 -0
  47. package/dist/services/sessionManager.d.ts +132 -0
  48. package/dist/services/sessionManager.d.ts.map +1 -0
  49. package/dist/services/sessionManager.js +439 -0
  50. package/dist/services/sessionManager.js.map +1 -0
  51. package/dist/types/claude-history.d.ts +48 -0
  52. package/dist/types/claude-history.d.ts.map +1 -0
  53. package/dist/types/claude-history.js +2 -0
  54. package/dist/types/claude-history.js.map +1 -0
  55. package/dist/types/claude-versions.d.ts +31 -0
  56. package/dist/types/claude-versions.d.ts.map +1 -0
  57. package/dist/types/claude-versions.js +2 -0
  58. package/dist/types/claude-versions.js.map +1 -0
  59. package/dist/types/commands.d.ts +32 -0
  60. package/dist/types/commands.d.ts.map +1 -0
  61. package/dist/types/commands.js +2 -0
  62. package/dist/types/commands.js.map +1 -0
  63. package/dist/types/index.d.ts +81 -0
  64. package/dist/types/index.d.ts.map +1 -0
  65. package/dist/types/index.js +150 -0
  66. package/dist/types/index.js.map +1 -0
  67. package/dist/types/subagents.d.ts +88 -0
  68. package/dist/types/subagents.d.ts.map +1 -0
  69. package/dist/types/subagents.js +2 -0
  70. package/dist/types/subagents.js.map +1 -0
  71. package/dist/utils/agentStorage.d.ts +19 -0
  72. package/dist/utils/agentStorage.d.ts.map +1 -0
  73. package/dist/utils/agentStorage.js +110 -0
  74. package/dist/utils/agentStorage.js.map +1 -0
  75. package/dist/utils/claudeVersionStorage.d.ts +33 -0
  76. package/dist/utils/claudeVersionStorage.d.ts.map +1 -0
  77. package/dist/utils/claudeVersionStorage.js +168 -0
  78. package/dist/utils/claudeVersionStorage.js.map +1 -0
  79. package/dist/utils/jwt.d.ts +15 -0
  80. package/dist/utils/jwt.d.ts.map +1 -0
  81. package/dist/utils/jwt.js +28 -0
  82. package/dist/utils/jwt.js.map +1 -0
  83. package/dist/utils/projectMetadataStorage.d.ts +21 -0
  84. package/dist/utils/projectMetadataStorage.d.ts.map +1 -0
  85. package/dist/utils/projectMetadataStorage.js +68 -0
  86. package/dist/utils/projectMetadataStorage.js.map +1 -0
  87. package/frontend/dist/index.html +86 -0
  88. package/package.json +66 -0
  89. package/src/bin/agentstudio.ts +161 -0
  90. package/src/index.ts +100 -0
  91. package/src/middleware/auth.ts +26 -0
  92. package/src/routes/agents.ts +885 -0
  93. package/src/routes/auth.ts +73 -0
  94. package/src/routes/commands.ts.bak +441 -0
  95. package/src/routes/files.ts +352 -0
  96. package/src/routes/mcp.ts +751 -0
  97. package/src/routes/media.ts +140 -0
  98. package/src/routes/projects.ts.bak +601 -0
  99. package/src/routes/sessions.ts.bak +809 -0
  100. package/src/routes/settings.ts.bak +718 -0
  101. package/src/routes/slides.ts +170 -0
  102. package/src/routes/subagents.ts.bak +364 -0
  103. package/src/services/claudeSession.ts +293 -0
  104. package/src/services/messageQueue.ts +71 -0
  105. package/src/services/sessionManager.ts +532 -0
  106. package/src/types/claude-history.ts +50 -0
  107. package/src/types/claude-versions.ts +33 -0
  108. package/src/types/commands.ts +35 -0
  109. package/src/types/index.ts +248 -0
  110. package/src/types/subagents.ts +106 -0
  111. package/src/utils/agentStorage.ts +126 -0
  112. package/src/utils/claudeVersionStorage.ts +199 -0
  113. package/src/utils/jwt.ts +36 -0
  114. package/src/utils/projectMetadataStorage.ts +86 -0
  115. 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;