@burdenoff/vibe-agent 1.0.0 → 1.0.2

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 (94) hide show
  1. package/README.md +14 -14
  2. package/dist/app.d.ts +4 -4
  3. package/dist/app.d.ts.map +1 -1
  4. package/dist/app.js +144 -130
  5. package/dist/app.js.map +1 -1
  6. package/dist/cli.d.ts +1 -1
  7. package/dist/cli.js +342 -332
  8. package/dist/cli.js.map +1 -1
  9. package/dist/db/schema.d.ts +15 -15
  10. package/dist/db/schema.d.ts.map +1 -1
  11. package/dist/db/schema.js +65 -62
  12. package/dist/db/schema.js.map +1 -1
  13. package/dist/index.d.ts +1 -1
  14. package/dist/index.js +18 -18
  15. package/dist/index.js.map +1 -1
  16. package/dist/middleware/ModuleAuth.d.ts +2 -2
  17. package/dist/middleware/ModuleAuth.d.ts.map +1 -1
  18. package/dist/middleware/ModuleAuth.js +32 -29
  19. package/dist/middleware/ModuleAuth.js.map +1 -1
  20. package/dist/middleware/auth.d.ts +1 -1
  21. package/dist/middleware/auth.d.ts.map +1 -1
  22. package/dist/middleware/auth.js +4 -4
  23. package/dist/middleware/auth.js.map +1 -1
  24. package/dist/migrations/remove-notes-prompts.d.ts.map +1 -1
  25. package/dist/migrations/remove-notes-prompts.js +26 -26
  26. package/dist/migrations/remove-notes-prompts.js.map +1 -1
  27. package/dist/routes/bookmarks.d.ts +1 -1
  28. package/dist/routes/bookmarks.d.ts.map +1 -1
  29. package/dist/routes/bookmarks.js +53 -44
  30. package/dist/routes/bookmarks.js.map +1 -1
  31. package/dist/routes/config.d.ts +1 -1
  32. package/dist/routes/config.d.ts.map +1 -1
  33. package/dist/routes/config.js +29 -27
  34. package/dist/routes/config.js.map +1 -1
  35. package/dist/routes/files.d.ts +1 -1
  36. package/dist/routes/files.d.ts.map +1 -1
  37. package/dist/routes/files.js +175 -134
  38. package/dist/routes/files.js.map +1 -1
  39. package/dist/routes/git.d.ts +1 -1
  40. package/dist/routes/git.d.ts.map +1 -1
  41. package/dist/routes/git.js +183 -169
  42. package/dist/routes/git.js.map +1 -1
  43. package/dist/routes/moduleRegistry.d.ts +3 -3
  44. package/dist/routes/moduleRegistry.d.ts.map +1 -1
  45. package/dist/routes/moduleRegistry.js +58 -58
  46. package/dist/routes/moduleRegistry.js.map +1 -1
  47. package/dist/routes/notifications.d.ts +1 -1
  48. package/dist/routes/notifications.d.ts.map +1 -1
  49. package/dist/routes/notifications.js +69 -64
  50. package/dist/routes/notifications.js.map +1 -1
  51. package/dist/routes/port-forward.d.ts +1 -1
  52. package/dist/routes/port-forward.d.ts.map +1 -1
  53. package/dist/routes/port-forward.js +59 -50
  54. package/dist/routes/port-forward.js.map +1 -1
  55. package/dist/routes/projects.d.ts +1 -1
  56. package/dist/routes/projects.d.ts.map +1 -1
  57. package/dist/routes/projects.js +134 -120
  58. package/dist/routes/projects.js.map +1 -1
  59. package/dist/routes/ssh.d.ts +1 -1
  60. package/dist/routes/ssh.d.ts.map +1 -1
  61. package/dist/routes/ssh.js +47 -47
  62. package/dist/routes/ssh.js.map +1 -1
  63. package/dist/routes/tasks.d.ts +1 -1
  64. package/dist/routes/tasks.d.ts.map +1 -1
  65. package/dist/routes/tasks.js +53 -49
  66. package/dist/routes/tasks.js.map +1 -1
  67. package/dist/routes/tmux.d.ts +1 -1
  68. package/dist/routes/tmux.d.ts.map +1 -1
  69. package/dist/routes/tmux.js +337 -241
  70. package/dist/routes/tmux.js.map +1 -1
  71. package/dist/routes/tunnel.d.ts +2 -2
  72. package/dist/routes/tunnel.d.ts.map +1 -1
  73. package/dist/routes/tunnel.js +115 -74
  74. package/dist/routes/tunnel.js.map +1 -1
  75. package/dist/services/ModulePermissions.d.ts +2 -2
  76. package/dist/services/ModulePermissions.d.ts.map +1 -1
  77. package/dist/services/ModulePermissions.js +50 -40
  78. package/dist/services/ModulePermissions.js.map +1 -1
  79. package/dist/services/ModuleRegistryService.d.ts +10 -10
  80. package/dist/services/ModuleRegistryService.d.ts.map +1 -1
  81. package/dist/services/ModuleRegistryService.js +156 -131
  82. package/dist/services/ModuleRegistryService.js.map +1 -1
  83. package/dist/services/agent.service.d.ts.map +1 -1
  84. package/dist/services/agent.service.js +24 -21
  85. package/dist/services/agent.service.js.map +1 -1
  86. package/dist/services/bootstrap.d.ts +1 -1
  87. package/dist/services/bootstrap.d.ts.map +1 -1
  88. package/dist/services/bootstrap.js +146 -69
  89. package/dist/services/bootstrap.js.map +1 -1
  90. package/dist/services/service-manager.d.ts +2 -2
  91. package/dist/services/service-manager.d.ts.map +1 -1
  92. package/dist/services/service-manager.js +75 -63
  93. package/dist/services/service-manager.js.map +1 -1
  94. package/package.json +2 -2
@@ -1,14 +1,16 @@
1
- import { promises as fs, readFileSync } from 'fs';
2
- import path from 'path';
1
+ import { promises as fs, readFileSync } from "fs";
2
+ import path from "path";
3
3
  export const fileRoutes = async (fastify) => {
4
4
  // Read file
5
- fastify.post('/read', async (request, reply) => {
6
- const { path: filePath, projectId, connectionId } = request.body;
5
+ fastify.post("/read", async (request, reply) => {
6
+ const { path: filePath, projectId, connectionId, } = request.body;
7
7
  try {
8
8
  // Security check - prevent directory traversal
9
9
  const normalizedPath = path.normalize(filePath);
10
- if (normalizedPath.includes('..')) {
11
- return reply.code(403).send({ error: 'Directory traversal not allowed' });
10
+ if (normalizedPath.includes("..")) {
11
+ return reply
12
+ .code(403)
13
+ .send({ error: "Directory traversal not allowed" });
12
14
  }
13
15
  // Check for sensitive files
14
16
  const sensitivePatterns = [
@@ -18,10 +20,12 @@ export const fileRoutes = async (fastify) => {
18
20
  /id_rsa/,
19
21
  /\.ssh\//,
20
22
  /\.git\/config$/,
21
- /\.npmrc$/
23
+ /\.npmrc$/,
22
24
  ];
23
- if (sensitivePatterns.some(pattern => pattern.test(normalizedPath))) {
24
- return reply.code(403).send({ error: 'Access to sensitive files not allowed' });
25
+ if (sensitivePatterns.some((pattern) => pattern.test(normalizedPath))) {
26
+ return reply
27
+ .code(403)
28
+ .send({ error: "Access to sensitive files not allowed" });
25
29
  }
26
30
  if (connectionId) {
27
31
  // Read remote file via SSH
@@ -29,81 +33,85 @@ export const fileRoutes = async (fastify) => {
29
33
  }
30
34
  else {
31
35
  // Read local file
32
- const content = await fs.readFile(normalizedPath, 'utf-8');
36
+ const content = await fs.readFile(normalizedPath, "utf-8");
33
37
  // Emit file read event
34
- fastify.io.emit('file:read', {
38
+ fastify.io.emit("file:read", {
35
39
  path: normalizedPath,
36
40
  projectId,
37
- size: content.length
41
+ size: content.length,
38
42
  });
39
43
  return {
40
44
  content,
41
45
  path: normalizedPath,
42
- size: content.length
46
+ size: content.length,
43
47
  };
44
48
  }
45
49
  }
46
50
  catch (error) {
47
51
  const nodeError = error;
48
- if (nodeError.code === 'ENOENT') {
49
- return reply.code(404).send({ error: 'File not found' });
52
+ if (nodeError.code === "ENOENT") {
53
+ return reply.code(404).send({ error: "File not found" });
50
54
  }
51
- if (nodeError.code === 'EACCES') {
52
- return reply.code(403).send({ error: 'Permission denied' });
55
+ if (nodeError.code === "EACCES") {
56
+ return reply.code(403).send({ error: "Permission denied" });
53
57
  }
54
58
  return reply.code(500).send({
55
- error: 'Failed to read file',
56
- details: error instanceof Error ? error.message : 'Unknown error'
59
+ error: "Failed to read file",
60
+ details: error instanceof Error ? error.message : "Unknown error",
57
61
  });
58
62
  }
59
63
  });
60
64
  // Write file
61
- fastify.post('/write', async (request, reply) => {
62
- const { path: filePath, content, projectId } = request.body;
65
+ fastify.post("/write", async (request, reply) => {
66
+ const { path: filePath, content, projectId, } = request.body;
63
67
  try {
64
68
  // Security check - prevent directory traversal
65
69
  const normalizedPath = path.normalize(filePath);
66
- if (normalizedPath.includes('..')) {
67
- return reply.code(403).send({ error: 'Directory traversal not allowed' });
70
+ if (normalizedPath.includes("..")) {
71
+ return reply
72
+ .code(403)
73
+ .send({ error: "Directory traversal not allowed" });
68
74
  }
69
75
  // Create directory if it doesn't exist
70
76
  const dir = path.dirname(normalizedPath);
71
77
  await fs.mkdir(dir, { recursive: true });
72
78
  // Write file
73
- await fs.writeFile(normalizedPath, content, 'utf-8');
79
+ await fs.writeFile(normalizedPath, content, "utf-8");
74
80
  // Get file stats
75
81
  const stats = await fs.stat(normalizedPath);
76
82
  // Emit file write event
77
- fastify.io.emit('file:write', {
83
+ fastify.io.emit("file:write", {
78
84
  path: normalizedPath,
79
85
  projectId,
80
- size: stats.size
86
+ size: stats.size,
81
87
  });
82
88
  return {
83
89
  success: true,
84
90
  path: normalizedPath,
85
- size: stats.size
91
+ size: stats.size,
86
92
  };
87
93
  }
88
94
  catch (error) {
89
95
  const nodeError = error;
90
- if (nodeError.code === 'EACCES') {
91
- return reply.code(403).send({ error: 'Permission denied' });
96
+ if (nodeError.code === "EACCES") {
97
+ return reply.code(403).send({ error: "Permission denied" });
92
98
  }
93
99
  return reply.code(500).send({
94
- error: 'Failed to write file',
95
- details: error instanceof Error ? error.message : 'Unknown error'
100
+ error: "Failed to write file",
101
+ details: error instanceof Error ? error.message : "Unknown error",
96
102
  });
97
103
  }
98
104
  });
99
105
  // List directory
100
- fastify.post('/list', async (request, reply) => {
106
+ fastify.post("/list", async (request, reply) => {
101
107
  const { path: dirPath, connectionId } = request.body;
102
108
  try {
103
109
  // Security check - prevent directory traversal
104
110
  const normalizedPath = path.normalize(dirPath);
105
- if (normalizedPath.includes('..')) {
106
- return reply.code(403).send({ error: 'Directory traversal not allowed' });
111
+ if (normalizedPath.includes("..")) {
112
+ return reply
113
+ .code(403)
114
+ .send({ error: "Directory traversal not allowed" });
107
115
  }
108
116
  if (connectionId) {
109
117
  // List remote directory via SSH
@@ -111,7 +119,9 @@ export const fileRoutes = async (fastify) => {
111
119
  }
112
120
  else {
113
121
  // List local directory
114
- const entries = await fs.readdir(normalizedPath, { withFileTypes: true });
122
+ const entries = await fs.readdir(normalizedPath, {
123
+ withFileTypes: true,
124
+ });
115
125
  const files = await Promise.all(entries.map(async (entry) => {
116
126
  const fullPath = path.join(normalizedPath, entry.name);
117
127
  let stats;
@@ -125,94 +135,115 @@ export const fileRoutes = async (fastify) => {
125
135
  return {
126
136
  name: entry.name,
127
137
  path: fullPath,
128
- type: entry.isDirectory() ? 'directory' :
129
- entry.isFile() ? 'file' :
130
- entry.isSymbolicLink() ? 'symlink' : 'other',
138
+ type: entry.isDirectory()
139
+ ? "directory"
140
+ : entry.isFile()
141
+ ? "file"
142
+ : entry.isSymbolicLink()
143
+ ? "symlink"
144
+ : "other",
131
145
  size: stats?.size || 0,
132
146
  modified: stats?.mtime || null,
133
- permissions: stats?.mode || null
147
+ permissions: stats?.mode || null,
134
148
  };
135
149
  }));
136
150
  // Sort: directories first, then files
137
151
  files.sort((a, b) => {
138
- if (a.type === 'directory' && b.type !== 'directory')
152
+ if (a.type === "directory" && b.type !== "directory")
139
153
  return -1;
140
- if (a.type !== 'directory' && b.type === 'directory')
154
+ if (a.type !== "directory" && b.type === "directory")
141
155
  return 1;
142
156
  return a.name.localeCompare(b.name);
143
157
  });
144
158
  return {
145
159
  path: normalizedPath,
146
160
  files,
147
- count: files.length
161
+ count: files.length,
148
162
  };
149
163
  }
150
164
  }
151
165
  catch (error) {
152
166
  const nodeError = error;
153
- if (nodeError.code === 'ENOENT') {
154
- return reply.code(404).send({ error: 'Directory not found' });
167
+ if (nodeError.code === "ENOENT") {
168
+ return reply.code(404).send({ error: "Directory not found" });
155
169
  }
156
- if (nodeError.code === 'ENOTDIR') {
157
- return reply.code(400).send({ error: 'Path is not a directory' });
170
+ if (nodeError.code === "ENOTDIR") {
171
+ return reply.code(400).send({ error: "Path is not a directory" });
158
172
  }
159
- if (nodeError.code === 'EACCES') {
160
- return reply.code(403).send({ error: 'Permission denied' });
173
+ if (nodeError.code === "EACCES") {
174
+ return reply.code(403).send({ error: "Permission denied" });
161
175
  }
162
176
  return reply.code(500).send({
163
- error: 'Failed to list directory',
164
- details: error instanceof Error ? error.message : 'Unknown error'
177
+ error: "Failed to list directory",
178
+ details: error instanceof Error ? error.message : "Unknown error",
165
179
  });
166
180
  }
167
181
  });
168
182
  // Check if path exists
169
- fastify.post('/exists', async (request, reply) => {
183
+ fastify.post("/exists", async (request, reply) => {
170
184
  const { path: checkPath } = request.body;
171
185
  try {
172
186
  // Security check - prevent directory traversal
173
187
  const normalizedPath = path.normalize(checkPath);
174
- if (normalizedPath.includes('..')) {
175
- return reply.code(403).send({ error: 'Directory traversal not allowed' });
188
+ if (normalizedPath.includes("..")) {
189
+ return reply
190
+ .code(403)
191
+ .send({ error: "Directory traversal not allowed" });
176
192
  }
177
193
  await fs.access(normalizedPath);
178
194
  const stats = await fs.stat(normalizedPath);
179
195
  return {
180
196
  exists: true,
181
197
  path: normalizedPath,
182
- type: stats.isDirectory() ? 'directory' :
183
- stats.isFile() ? 'file' : 'other',
198
+ type: stats.isDirectory()
199
+ ? "directory"
200
+ : stats.isFile()
201
+ ? "file"
202
+ : "other",
184
203
  size: stats.size,
185
- modified: stats.mtime
204
+ modified: stats.mtime,
186
205
  };
187
206
  }
188
207
  catch (error) {
189
208
  const nodeError = error;
190
- if (nodeError.code === 'ENOENT') {
209
+ if (nodeError.code === "ENOENT") {
191
210
  return {
192
211
  exists: false,
193
- path: path.normalize(checkPath)
212
+ path: path.normalize(checkPath),
194
213
  };
195
214
  }
196
215
  return reply.code(500).send({
197
- error: 'Failed to check path',
198
- details: error instanceof Error ? error.message : 'Unknown error'
216
+ error: "Failed to check path",
217
+ details: error instanceof Error ? error.message : "Unknown error",
199
218
  });
200
219
  }
201
220
  });
202
221
  // Delete file or directory
203
- fastify.post('/delete', async (request, reply) => {
222
+ fastify.post("/delete", async (request, reply) => {
204
223
  const { path: deletePath } = request.body;
205
224
  try {
206
225
  // Security check - prevent directory traversal
207
226
  const normalizedPath = path.normalize(deletePath);
208
- if (normalizedPath.includes('..')) {
209
- return reply.code(403).send({ error: 'Directory traversal not allowed' });
227
+ if (normalizedPath.includes("..")) {
228
+ return reply
229
+ .code(403)
230
+ .send({ error: "Directory traversal not allowed" });
210
231
  }
211
232
  // Additional security check for important paths
212
- const protectedPaths = ['/', '/etc', '/usr', '/bin', '/sbin', '/var', '/opt'];
233
+ const protectedPaths = [
234
+ "/",
235
+ "/etc",
236
+ "/usr",
237
+ "/bin",
238
+ "/sbin",
239
+ "/var",
240
+ "/opt",
241
+ ];
213
242
  if (protectedPaths.includes(normalizedPath) ||
214
- protectedPaths.some(p => normalizedPath.startsWith(p + '/'))) {
215
- return reply.code(403).send({ error: 'Cannot delete system directories' });
243
+ protectedPaths.some((p) => normalizedPath.startsWith(p + "/"))) {
244
+ return reply
245
+ .code(403)
246
+ .send({ error: "Cannot delete system directories" });
216
247
  }
217
248
  const stats = await fs.stat(normalizedPath);
218
249
  if (stats.isDirectory()) {
@@ -222,49 +253,51 @@ export const fileRoutes = async (fastify) => {
222
253
  await fs.unlink(normalizedPath);
223
254
  }
224
255
  // Emit file delete event
225
- fastify.io.emit('file:delete', {
256
+ fastify.io.emit("file:delete", {
226
257
  path: normalizedPath,
227
- type: stats.isDirectory() ? 'directory' : 'file'
258
+ type: stats.isDirectory() ? "directory" : "file",
228
259
  });
229
260
  return {
230
261
  success: true,
231
- path: normalizedPath
262
+ path: normalizedPath,
232
263
  };
233
264
  }
234
265
  catch (error) {
235
266
  const nodeError = error;
236
- if (nodeError.code === 'ENOENT') {
237
- return reply.code(404).send({ error: 'Path not found' });
267
+ if (nodeError.code === "ENOENT") {
268
+ return reply.code(404).send({ error: "Path not found" });
238
269
  }
239
- if (nodeError.code === 'EACCES') {
240
- return reply.code(403).send({ error: 'Permission denied' });
270
+ if (nodeError.code === "EACCES") {
271
+ return reply.code(403).send({ error: "Permission denied" });
241
272
  }
242
273
  return reply.code(500).send({
243
- error: 'Failed to delete path',
244
- details: error instanceof Error ? error.message : 'Unknown error'
274
+ error: "Failed to delete path",
275
+ details: error instanceof Error ? error.message : "Unknown error",
245
276
  });
246
277
  }
247
278
  });
248
279
  // Read README file
249
- fastify.post('/readme', async (request, reply) => {
280
+ fastify.post("/readme", async (request, reply) => {
250
281
  const { path: projectPath, connectionId } = request.body;
251
282
  try {
252
283
  // Security check - prevent directory traversal
253
284
  const normalizedPath = path.normalize(projectPath);
254
- if (normalizedPath.includes('..')) {
255
- return reply.code(403).send({ error: 'Directory traversal not allowed' });
285
+ if (normalizedPath.includes("..")) {
286
+ return reply
287
+ .code(403)
288
+ .send({ error: "Directory traversal not allowed" });
256
289
  }
257
290
  // README file variants to check
258
291
  const readmeVariants = [
259
- 'README.md',
260
- 'readme.md',
261
- 'README.MD',
262
- 'README',
263
- 'readme',
264
- 'README.txt',
265
- 'readme.txt',
266
- 'README.rst',
267
- 'readme.rst'
292
+ "README.md",
293
+ "readme.md",
294
+ "README.MD",
295
+ "README",
296
+ "readme",
297
+ "README.txt",
298
+ "readme.txt",
299
+ "README.rst",
300
+ "readme.rst",
268
301
  ];
269
302
  if (connectionId) {
270
303
  // Check remote README
@@ -275,25 +308,25 @@ export const fileRoutes = async (fastify) => {
275
308
  for (const variant of readmeVariants) {
276
309
  const readmePath = path.join(normalizedPath, variant);
277
310
  try {
278
- const content = await fs.readFile(readmePath, 'utf-8');
311
+ const content = await fs.readFile(readmePath, "utf-8");
279
312
  return {
280
313
  content,
281
314
  path: readmePath,
282
315
  variant,
283
- size: content.length
316
+ size: content.length,
284
317
  };
285
318
  }
286
319
  catch {
287
320
  // Continue to next variant
288
321
  }
289
322
  }
290
- return reply.code(404).send({ error: 'No README file found' });
323
+ return reply.code(404).send({ error: "No README file found" });
291
324
  }
292
325
  }
293
326
  catch (error) {
294
327
  return reply.code(500).send({
295
- error: 'Failed to read README',
296
- details: error instanceof Error ? error.message : 'Unknown error'
328
+ error: "Failed to read README",
329
+ details: error instanceof Error ? error.message : "Unknown error",
297
330
  });
298
331
  }
299
332
  });
@@ -302,46 +335,47 @@ export const fileRoutes = async (fastify) => {
302
335
  async function readRemoteFile(fastify, connectionId, filePath, reply) {
303
336
  const connectionConfig = fastify.db.getSSHConnection(connectionId);
304
337
  if (!connectionConfig) {
305
- return reply.code(404).send({ error: 'SSH connection not found' });
338
+ return reply.code(404).send({ error: "SSH connection not found" });
306
339
  }
307
- const { Client } = await import('ssh2');
340
+ const { Client } = await import("ssh2");
308
341
  const conn = new Client();
309
342
  return new Promise((resolve) => {
310
- conn.on('ready', () => {
343
+ conn.on("ready", () => {
311
344
  conn.sftp((err, sftp) => {
312
345
  if (err) {
313
346
  conn.end();
314
347
  return resolve(reply.code(500).send({
315
- error: 'Failed to establish SFTP connection',
316
- details: err.message
348
+ error: "Failed to establish SFTP connection",
349
+ details: err.message,
317
350
  }));
318
351
  }
319
352
  sftp.readFile(filePath, (err, buffer) => {
320
353
  conn.end();
321
354
  if (err) {
322
355
  const sshError = err;
323
- if (sshError.code === 2) { // ENOENT
324
- return resolve(reply.code(404).send({ error: 'File not found' }));
356
+ if (sshError.code === 2) {
357
+ // ENOENT
358
+ return resolve(reply.code(404).send({ error: "File not found" }));
325
359
  }
326
360
  return resolve(reply.code(500).send({
327
- error: 'Failed to read remote file',
328
- details: err.message
361
+ error: "Failed to read remote file",
362
+ details: err.message,
329
363
  }));
330
364
  }
331
- const content = buffer.toString('utf-8');
365
+ const content = buffer.toString("utf-8");
332
366
  resolve({
333
367
  content,
334
368
  path: filePath,
335
369
  connectionId,
336
- size: content.length
370
+ size: content.length,
337
371
  });
338
372
  });
339
373
  });
340
374
  });
341
- conn.on('error', (err) => {
375
+ conn.on("error", (err) => {
342
376
  resolve(reply.code(500).send({
343
- error: 'SSH connection failed',
344
- details: err.message
377
+ error: "SSH connection failed",
378
+ details: err.message,
345
379
  }));
346
380
  });
347
381
  // Connect with appropriate auth
@@ -359,78 +393,85 @@ async function readRemoteFile(fastify, connectionId, filePath, reply) {
359
393
  conn.connect(connectConfig);
360
394
  });
361
395
  }
362
- // Helper function to list remote directory
396
+ // Helper function to list remote directory
363
397
  async function listRemoteDirectory(fastify, connectionId, dirPath, reply) {
364
398
  const connectionConfig = fastify.db.getSSHConnection(connectionId);
365
399
  if (!connectionConfig) {
366
- return reply.code(404).send({ error: 'SSH connection not found' });
400
+ return reply.code(404).send({ error: "SSH connection not found" });
367
401
  }
368
- const { Client } = await import('ssh2');
402
+ const { Client } = await import("ssh2");
369
403
  const conn = new Client();
370
404
  return new Promise((resolve) => {
371
- conn.on('ready', () => {
405
+ conn.on("ready", () => {
372
406
  conn.exec(`ls -la "${dirPath}"`, (err, stream) => {
373
407
  if (err) {
374
408
  conn.end();
375
409
  return resolve(reply.code(500).send({
376
- error: 'Failed to execute remote command',
377
- details: err.message
410
+ error: "Failed to execute remote command",
411
+ details: err.message,
378
412
  }));
379
413
  }
380
- let output = '';
381
- let errorOutput = '';
382
- stream.on('data', (data) => {
414
+ let output = "";
415
+ let errorOutput = "";
416
+ stream.on("data", (data) => {
383
417
  output += data.toString();
384
418
  });
385
- stream.stderr.on('data', (data) => {
419
+ stream.stderr.on("data", (data) => {
386
420
  errorOutput += data.toString();
387
421
  });
388
- stream.on('close', (code) => {
422
+ stream.on("close", (code) => {
389
423
  conn.end();
390
424
  if (code !== 0) {
391
- if (errorOutput.includes('No such file or directory')) {
392
- return resolve(reply.code(404).send({ error: 'Directory not found' }));
425
+ if (errorOutput.includes("No such file or directory")) {
426
+ return resolve(reply.code(404).send({ error: "Directory not found" }));
393
427
  }
394
428
  return resolve(reply.code(500).send({
395
- error: 'Failed to list directory',
396
- details: errorOutput
429
+ error: "Failed to list directory",
430
+ details: errorOutput,
397
431
  }));
398
432
  }
399
433
  // Parse ls output
400
- const lines = output.split('\n').filter(line => line.trim());
401
- const files = lines.slice(1).map(line => {
434
+ const lines = output.split("\n").filter((line) => line.trim());
435
+ const files = lines
436
+ .slice(1)
437
+ .map((line) => {
438
+ // Skip "total" line
402
439
  const parts = line.split(/\s+/);
403
440
  if (parts.length >= 9) {
404
441
  const permissions = parts[0];
405
442
  const size = parseInt(parts[4], 10);
406
- const name = parts.slice(8).join(' ');
443
+ const name = parts.slice(8).join(" ");
407
444
  return {
408
445
  name,
409
446
  path: path.join(dirPath, name),
410
- type: permissions.startsWith('d') ? 'directory' :
411
- permissions.startsWith('l') ? 'symlink' : 'file',
447
+ type: permissions.startsWith("d")
448
+ ? "directory"
449
+ : permissions.startsWith("l")
450
+ ? "symlink"
451
+ : "file",
412
452
  size,
413
453
  permissions: parts[0],
414
454
  owner: parts[2],
415
455
  group: parts[3],
416
- modified: `${parts[5]} ${parts[6]} ${parts[7]}`
456
+ modified: `${parts[5]} ${parts[6]} ${parts[7]}`,
417
457
  };
418
458
  }
419
459
  return null;
420
- }).filter(f => f && f.name !== '.' && f.name !== '..');
460
+ })
461
+ .filter((f) => f && f.name !== "." && f.name !== "..");
421
462
  resolve({
422
463
  path: dirPath,
423
464
  connectionId,
424
465
  files,
425
- count: files.length
466
+ count: files.length,
426
467
  });
427
468
  });
428
469
  });
429
470
  });
430
- conn.on('error', (err) => {
471
+ conn.on("error", (err) => {
431
472
  resolve(reply.code(500).send({
432
- error: 'SSH connection failed',
433
- details: err.message
473
+ error: "SSH connection failed",
474
+ details: err.message,
434
475
  }));
435
476
  });
436
477
  // Connect with appropriate auth
@@ -452,7 +493,7 @@ async function listRemoteDirectory(fastify, connectionId, dirPath, reply) {
452
493
  async function findRemoteReadme(fastify, connectionId, dirPath, variants, reply) {
453
494
  const connectionConfig = fastify.db.getSSHConnection(connectionId);
454
495
  if (!connectionConfig) {
455
- return reply.code(404).send({ error: 'SSH connection not found' });
496
+ return reply.code(404).send({ error: "SSH connection not found" });
456
497
  }
457
498
  // Try each variant
458
499
  for (const variant of variants) {
@@ -466,6 +507,6 @@ async function findRemoteReadme(fastify, connectionId, dirPath, variants, reply)
466
507
  continue;
467
508
  }
468
509
  }
469
- return reply.code(404).send({ error: 'No README file found' });
510
+ return reply.code(404).send({ error: "No README file found" });
470
511
  }
471
512
  //# sourceMappingURL=files.js.map