vg-coder-cli 1.0.9 → 1.0.11
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/.vgignore +0 -2
- package/README.md +273 -104
- package/bin/vg.js +11 -0
- package/package.json +8 -4
- package/src/index.js +60 -2
- package/src/server/api-server.js +320 -0
- package/src/server/views/dashboard.html +421 -0
- package/src/utils/bash-executor.js +130 -0
- package/test-large/package.json +0 -1
- package/test-large/src/components/Button.tsx +0 -1
- package/test-large/src/index.ts +0 -1
- package/test-small/package.json +0 -1
- package/test-small/src/index.js +0 -1
- package/vg-coder-cli-1.0.8.tgz +0 -0
- package/vg-coder-cli-1.0.9.tgz +0 -0
- package/vg-projects.txt +0 -5
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const cors = require('cors');
|
|
3
|
+
const bodyParser = require('body-parser');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const fs = require('fs-extra');
|
|
6
|
+
const chalk = require('chalk');
|
|
7
|
+
const packageJson = require('../../package.json');
|
|
8
|
+
|
|
9
|
+
const ProjectDetector = require('../detectors/project-detector');
|
|
10
|
+
const FileScanner = require('../scanner/file-scanner');
|
|
11
|
+
const TokenManager = require('../tokenizer/token-manager');
|
|
12
|
+
const BashExecutor = require('../utils/bash-executor');
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* API Server for VG Coder CLI
|
|
16
|
+
*/
|
|
17
|
+
class ApiServer {
|
|
18
|
+
constructor(port = 6868) {
|
|
19
|
+
this.port = port;
|
|
20
|
+
this.app = express();
|
|
21
|
+
this.server = null;
|
|
22
|
+
this.workingDir = process.cwd(); // Track working directory
|
|
23
|
+
this.setupMiddleware();
|
|
24
|
+
this.setupRoutes();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Setup Express middleware
|
|
29
|
+
*/
|
|
30
|
+
setupMiddleware() {
|
|
31
|
+
this.app.use(cors());
|
|
32
|
+
this.app.use(bodyParser.json());
|
|
33
|
+
this.app.use(bodyParser.urlencoded({ extended: true }));
|
|
34
|
+
|
|
35
|
+
// Request logging
|
|
36
|
+
this.app.use((req, res, next) => {
|
|
37
|
+
console.log(chalk.blue(`[${new Date().toISOString()}] ${req.method} ${req.path}`));
|
|
38
|
+
next();
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Setup API routes
|
|
44
|
+
*/
|
|
45
|
+
setupRoutes() {
|
|
46
|
+
// Dashboard - serve HTML interface
|
|
47
|
+
this.app.get('/', (req, res) => {
|
|
48
|
+
res.sendFile(path.join(__dirname, 'views', 'dashboard.html'));
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// Health check
|
|
52
|
+
this.app.get('/health', (req, res) => {
|
|
53
|
+
res.json({
|
|
54
|
+
status: 'ok',
|
|
55
|
+
version: packageJson.version,
|
|
56
|
+
timestamp: new Date().toISOString()
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// Analyze endpoint - returns project.txt file
|
|
61
|
+
this.app.post('/api/analyze', async (req, res) => {
|
|
62
|
+
try {
|
|
63
|
+
const { path: projectPath, options = {} } = req.body;
|
|
64
|
+
|
|
65
|
+
if (!projectPath) {
|
|
66
|
+
return res.status(400).json({
|
|
67
|
+
error: 'Missing required field: path'
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const resolvedPath = path.resolve(projectPath);
|
|
72
|
+
|
|
73
|
+
// Validate project path
|
|
74
|
+
if (!await fs.pathExists(resolvedPath)) {
|
|
75
|
+
return res.status(404).json({
|
|
76
|
+
error: `Project path does not exist: ${projectPath}`
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
console.log(chalk.yellow(`Analyzing project: ${resolvedPath}`));
|
|
81
|
+
|
|
82
|
+
// Detect project type
|
|
83
|
+
const detector = new ProjectDetector(resolvedPath);
|
|
84
|
+
const projectInfo = await detector.detectAll();
|
|
85
|
+
|
|
86
|
+
// Scan files
|
|
87
|
+
const scannerOptions = {
|
|
88
|
+
maxTokens: parseInt(options.maxTokens || 8000),
|
|
89
|
+
extensions: options.extensions ? options.extensions.split(',').map(ext => ext.trim()) : undefined,
|
|
90
|
+
includeHidden: options.includeHidden || false
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const scanner = new FileScanner(resolvedPath, scannerOptions);
|
|
94
|
+
const scanResult = await scanner.scanProject();
|
|
95
|
+
|
|
96
|
+
// Create AI-friendly content
|
|
97
|
+
const aiContent = await scanner.createCombinedContentForAI(scanResult.files, {
|
|
98
|
+
includeStats: true,
|
|
99
|
+
includeTree: true,
|
|
100
|
+
preserveLineNumbers: true
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// Set response headers for file download
|
|
104
|
+
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
|
|
105
|
+
res.setHeader('Content-Disposition', 'attachment; filename="project.txt"');
|
|
106
|
+
res.send(aiContent);
|
|
107
|
+
|
|
108
|
+
console.log(chalk.green(`✓ Analysis completed: ${scanResult.files.length} files`));
|
|
109
|
+
|
|
110
|
+
} catch (error) {
|
|
111
|
+
console.error(chalk.red('Error during analysis:'), error);
|
|
112
|
+
res.status(500).json({
|
|
113
|
+
error: 'Analysis failed',
|
|
114
|
+
message: error.message
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// Info endpoint
|
|
120
|
+
this.app.get('/api/info', async (req, res) => {
|
|
121
|
+
try {
|
|
122
|
+
const projectPath = req.query.path;
|
|
123
|
+
|
|
124
|
+
if (!projectPath) {
|
|
125
|
+
return res.status(400).json({
|
|
126
|
+
error: 'Missing required query parameter: path'
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const resolvedPath = path.resolve(projectPath);
|
|
131
|
+
|
|
132
|
+
if (!await fs.pathExists(resolvedPath)) {
|
|
133
|
+
return res.status(404).json({
|
|
134
|
+
error: `Project path does not exist: ${projectPath}`
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Detect project
|
|
139
|
+
const detector = new ProjectDetector(resolvedPath);
|
|
140
|
+
const projectInfo = await detector.detectAll();
|
|
141
|
+
|
|
142
|
+
// Quick scan
|
|
143
|
+
const scanner = new FileScanner(resolvedPath);
|
|
144
|
+
const scanResult = await scanner.scanProject();
|
|
145
|
+
|
|
146
|
+
// Token analysis
|
|
147
|
+
const tokenManager = new TokenManager();
|
|
148
|
+
const tokenAnalysis = tokenManager.analyzeFiles(scanResult.files);
|
|
149
|
+
tokenManager.cleanup();
|
|
150
|
+
|
|
151
|
+
const extensions = [...new Set(scanResult.files.map(f => f.extension))].filter(Boolean);
|
|
152
|
+
|
|
153
|
+
res.json({
|
|
154
|
+
path: resolvedPath,
|
|
155
|
+
primaryType: projectInfo.primary,
|
|
156
|
+
detectedTechnologies: projectInfo.detected,
|
|
157
|
+
stats: {
|
|
158
|
+
totalFiles: scanResult.stats.processedFiles,
|
|
159
|
+
totalSize: scanResult.files.reduce((sum, f) => sum + f.size, 0),
|
|
160
|
+
totalLines: scanResult.files.reduce((sum, f) => sum + f.lines, 0),
|
|
161
|
+
extensions: extensions
|
|
162
|
+
},
|
|
163
|
+
tokens: {
|
|
164
|
+
total: tokenAnalysis.summary.totalTokens,
|
|
165
|
+
averagePerFile: tokenAnalysis.summary.averageTokensPerFile,
|
|
166
|
+
filesExceedingLimit: tokenAnalysis.summary.filesExceedingLimit,
|
|
167
|
+
estimatedChunks: tokenAnalysis.summary.estimatedChunks
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
console.log(chalk.green(`✓ Info retrieved for: ${resolvedPath}`));
|
|
172
|
+
|
|
173
|
+
} catch (error) {
|
|
174
|
+
console.error(chalk.red('Error getting info:'), error);
|
|
175
|
+
res.status(500).json({
|
|
176
|
+
error: 'Failed to get project info',
|
|
177
|
+
message: error.message
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
// Clean endpoint
|
|
183
|
+
this.app.delete('/api/clean', async (req, res) => {
|
|
184
|
+
try {
|
|
185
|
+
const { output } = req.body;
|
|
186
|
+
|
|
187
|
+
if (!output) {
|
|
188
|
+
return res.status(400).json({
|
|
189
|
+
error: 'Missing required field: output'
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const outputPath = path.resolve(output);
|
|
194
|
+
|
|
195
|
+
if (await fs.pathExists(outputPath)) {
|
|
196
|
+
await fs.remove(outputPath);
|
|
197
|
+
res.json({
|
|
198
|
+
success: true,
|
|
199
|
+
message: `Cleaned: ${outputPath}`
|
|
200
|
+
});
|
|
201
|
+
console.log(chalk.green(`✓ Cleaned: ${outputPath}`));
|
|
202
|
+
} else {
|
|
203
|
+
res.json({
|
|
204
|
+
success: true,
|
|
205
|
+
message: 'Output directory does not exist'
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
} catch (error) {
|
|
210
|
+
console.error(chalk.red('Error cleaning:'), error);
|
|
211
|
+
res.status(500).json({
|
|
212
|
+
error: 'Failed to clean',
|
|
213
|
+
message: error.message
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
// Execute bash script endpoint
|
|
219
|
+
this.app.post('/api/execute', async (req, res) => {
|
|
220
|
+
try {
|
|
221
|
+
const { bash } = req.body;
|
|
222
|
+
|
|
223
|
+
if (!bash) {
|
|
224
|
+
return res.status(400).json({
|
|
225
|
+
error: 'Missing required field: bash'
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
console.log(chalk.yellow(`Executing bash script (${bash.length} chars)...`));
|
|
230
|
+
|
|
231
|
+
// Create executor with working directory
|
|
232
|
+
const executor = new BashExecutor(this.workingDir);
|
|
233
|
+
|
|
234
|
+
// Execute script (validates syntax first, then executes)
|
|
235
|
+
const result = await executor.execute(bash);
|
|
236
|
+
|
|
237
|
+
if (result.success) {
|
|
238
|
+
console.log(chalk.green(`✓ Bash execution completed in ${result.executionTime}ms`));
|
|
239
|
+
res.json(result);
|
|
240
|
+
} else {
|
|
241
|
+
console.log(chalk.red(`✗ Bash execution failed: ${result.error || 'Exit code ' + result.exitCode}`));
|
|
242
|
+
res.status(400).json(result);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
} catch (error) {
|
|
246
|
+
console.error(chalk.red('Error executing bash:'), error);
|
|
247
|
+
res.status(500).json({
|
|
248
|
+
error: 'Execution failed',
|
|
249
|
+
message: error.message
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
// 404 handler
|
|
255
|
+
this.app.use((req, res) => {
|
|
256
|
+
res.status(404).json({
|
|
257
|
+
error: 'Not found',
|
|
258
|
+
message: `Route ${req.method} ${req.path} not found`
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
// Error handler
|
|
263
|
+
this.app.use((err, req, res, next) => {
|
|
264
|
+
console.error(chalk.red('Server error:'), err);
|
|
265
|
+
res.status(500).json({
|
|
266
|
+
error: 'Internal server error',
|
|
267
|
+
message: err.message
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Start the server
|
|
274
|
+
*/
|
|
275
|
+
async start() {
|
|
276
|
+
return new Promise((resolve, reject) => {
|
|
277
|
+
this.server = this.app.listen(this.port, () => {
|
|
278
|
+
console.log(chalk.green(`\n🚀 VG Coder API Server started!`));
|
|
279
|
+
console.log(chalk.blue(`📡 Listening on: http://localhost:${this.port}`));
|
|
280
|
+
console.log(chalk.cyan(`\n🎨 Dashboard: http://localhost:${this.port}`));
|
|
281
|
+
console.log(chalk.yellow(`\n📚 Available endpoints:`));
|
|
282
|
+
console.log(` GET /health - Health check`);
|
|
283
|
+
console.log(` POST /api/analyze - Analyze project (returns project.txt)`);
|
|
284
|
+
console.log(` GET /api/info?path=. - Get project info`);
|
|
285
|
+
console.log(` DELETE /api/clean - Clean output directory`);
|
|
286
|
+
console.log(` POST /api/execute - Execute bash script`);
|
|
287
|
+
console.log(chalk.gray(`\n💡 Press Ctrl+C to stop the server\n`));
|
|
288
|
+
resolve();
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
this.server.on('error', (err) => {
|
|
292
|
+
if (err.code === 'EADDRINUSE') {
|
|
293
|
+
console.error(chalk.red(`\n❌ Port ${this.port} is already in use!`));
|
|
294
|
+
console.log(chalk.yellow(`Try using a different port with: vg start -p <port>\n`));
|
|
295
|
+
} else {
|
|
296
|
+
console.error(chalk.red('\n❌ Server error:'), err.message);
|
|
297
|
+
}
|
|
298
|
+
reject(err);
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Stop the server
|
|
305
|
+
*/
|
|
306
|
+
async stop() {
|
|
307
|
+
return new Promise((resolve) => {
|
|
308
|
+
if (this.server) {
|
|
309
|
+
this.server.close(() => {
|
|
310
|
+
console.log(chalk.yellow('\n👋 Server stopped\n'));
|
|
311
|
+
resolve();
|
|
312
|
+
});
|
|
313
|
+
} else {
|
|
314
|
+
resolve();
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
module.exports = ApiServer;
|