vg-coder-cli 1.0.10 → 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/README.md +273 -104
- package/package.json +5 -2
- package/src/index.js +57 -0
- package/src/server/api-server.js +320 -0
- package/src/server/views/dashboard.html +421 -0
- package/src/utils/bash-executor.js +130 -0
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>VG Coder API Dashboard</title>
|
|
7
|
+
<style>
|
|
8
|
+
* {
|
|
9
|
+
margin: 0;
|
|
10
|
+
padding: 0;
|
|
11
|
+
box-sizing: border-box;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
body {
|
|
15
|
+
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
16
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
17
|
+
min-height: 100vh;
|
|
18
|
+
padding: 20px;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.container {
|
|
22
|
+
max-width: 1200px;
|
|
23
|
+
margin: 0 auto;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.header {
|
|
27
|
+
background: white;
|
|
28
|
+
border-radius: 12px;
|
|
29
|
+
padding: 30px;
|
|
30
|
+
margin-bottom: 30px;
|
|
31
|
+
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.header h1 {
|
|
35
|
+
color: #667eea;
|
|
36
|
+
font-size: 2.5em;
|
|
37
|
+
margin-bottom: 10px;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.header p {
|
|
41
|
+
color: #666;
|
|
42
|
+
font-size: 1.1em;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.status {
|
|
46
|
+
display: inline-block;
|
|
47
|
+
padding: 8px 16px;
|
|
48
|
+
background: #10b981;
|
|
49
|
+
color: white;
|
|
50
|
+
border-radius: 20px;
|
|
51
|
+
font-size: 0.9em;
|
|
52
|
+
margin-top: 10px;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.endpoints {
|
|
56
|
+
display: grid;
|
|
57
|
+
grid-template-columns: repeat(auto-fit, minmax(500px, 1fr));
|
|
58
|
+
gap: 20px;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.endpoint-card {
|
|
62
|
+
background: white;
|
|
63
|
+
border-radius: 12px;
|
|
64
|
+
padding: 25px;
|
|
65
|
+
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
|
|
66
|
+
transition: transform 0.3s ease;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.endpoint-card:hover {
|
|
70
|
+
transform: translateY(-5px);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.endpoint-header {
|
|
74
|
+
display: flex;
|
|
75
|
+
align-items: center;
|
|
76
|
+
gap: 15px;
|
|
77
|
+
margin-bottom: 15px;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.method {
|
|
81
|
+
padding: 6px 12px;
|
|
82
|
+
border-radius: 6px;
|
|
83
|
+
font-weight: bold;
|
|
84
|
+
font-size: 0.85em;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.method.get { background: #3b82f6; color: white; }
|
|
88
|
+
.method.post { background: #10b981; color: white; }
|
|
89
|
+
.method.delete { background: #ef4444; color: white; }
|
|
90
|
+
|
|
91
|
+
.endpoint-path {
|
|
92
|
+
font-family: 'Courier New', monospace;
|
|
93
|
+
color: #333;
|
|
94
|
+
font-size: 1.1em;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.endpoint-desc {
|
|
98
|
+
color: #666;
|
|
99
|
+
margin-bottom: 15px;
|
|
100
|
+
line-height: 1.6;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.form-group {
|
|
104
|
+
margin-bottom: 15px;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.form-group label {
|
|
108
|
+
display: block;
|
|
109
|
+
margin-bottom: 5px;
|
|
110
|
+
color: #333;
|
|
111
|
+
font-weight: 500;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.form-group input,
|
|
115
|
+
.form-group textarea {
|
|
116
|
+
width: 100%;
|
|
117
|
+
padding: 10px;
|
|
118
|
+
border: 2px solid #e5e7eb;
|
|
119
|
+
border-radius: 6px;
|
|
120
|
+
font-size: 0.95em;
|
|
121
|
+
font-family: 'Courier New', monospace;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.form-group textarea {
|
|
125
|
+
min-height: 120px;
|
|
126
|
+
resize: vertical;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.btn {
|
|
130
|
+
background: #667eea;
|
|
131
|
+
color: white;
|
|
132
|
+
border: none;
|
|
133
|
+
padding: 12px 24px;
|
|
134
|
+
border-radius: 6px;
|
|
135
|
+
cursor: pointer;
|
|
136
|
+
font-size: 1em;
|
|
137
|
+
font-weight: 600;
|
|
138
|
+
transition: background 0.3s ease;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.btn:hover {
|
|
142
|
+
background: #5568d3;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.btn:disabled {
|
|
146
|
+
background: #9ca3af;
|
|
147
|
+
cursor: not-allowed;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.response {
|
|
151
|
+
margin-top: 15px;
|
|
152
|
+
padding: 15px;
|
|
153
|
+
border-radius: 6px;
|
|
154
|
+
background: #f9fafb;
|
|
155
|
+
border-left: 4px solid #667eea;
|
|
156
|
+
display: none;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.response.show {
|
|
160
|
+
display: block;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.response.success {
|
|
164
|
+
border-left-color: #10b981;
|
|
165
|
+
background: #f0fdf4;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.response.error {
|
|
169
|
+
border-left-color: #ef4444;
|
|
170
|
+
background: #fef2f2;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.response pre {
|
|
174
|
+
margin: 0;
|
|
175
|
+
font-family: 'Courier New', monospace;
|
|
176
|
+
font-size: 0.9em;
|
|
177
|
+
white-space: pre-wrap;
|
|
178
|
+
word-wrap: break-word;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.loading {
|
|
182
|
+
display: inline-block;
|
|
183
|
+
width: 16px;
|
|
184
|
+
height: 16px;
|
|
185
|
+
border: 3px solid rgba(255,255,255,.3);
|
|
186
|
+
border-radius: 50%;
|
|
187
|
+
border-top-color: white;
|
|
188
|
+
animation: spin 1s ease-in-out infinite;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
@keyframes spin {
|
|
192
|
+
to { transform: rotate(360deg); }
|
|
193
|
+
}
|
|
194
|
+
</style>
|
|
195
|
+
</head>
|
|
196
|
+
<body>
|
|
197
|
+
<div class="container">
|
|
198
|
+
<div class="header">
|
|
199
|
+
<h1>🚀 VG Coder API Dashboard</h1>
|
|
200
|
+
<p>Test your API endpoints directly from the browser</p>
|
|
201
|
+
<span class="status" id="status">● Server Running</span>
|
|
202
|
+
</div>
|
|
203
|
+
|
|
204
|
+
<div class="endpoints">
|
|
205
|
+
<!-- Health Check -->
|
|
206
|
+
<div class="endpoint-card">
|
|
207
|
+
<div class="endpoint-header">
|
|
208
|
+
<span class="method get">GET</span>
|
|
209
|
+
<span class="endpoint-path">/health</span>
|
|
210
|
+
</div>
|
|
211
|
+
<p class="endpoint-desc">Check server health status</p>
|
|
212
|
+
<button class="btn" onclick="testHealth()">Test Health Check</button>
|
|
213
|
+
<div class="response" id="health-response"></div>
|
|
214
|
+
</div>
|
|
215
|
+
|
|
216
|
+
<!-- Analyze -->
|
|
217
|
+
<div class="endpoint-card">
|
|
218
|
+
<div class="endpoint-header">
|
|
219
|
+
<span class="method post">POST</span>
|
|
220
|
+
<span class="endpoint-path">/api/analyze</span>
|
|
221
|
+
</div>
|
|
222
|
+
<p class="endpoint-desc">Analyze project and download project.txt</p>
|
|
223
|
+
<div class="form-group">
|
|
224
|
+
<label>Project Path:</label>
|
|
225
|
+
<input type="text" id="analyze-path" value="." placeholder=".">
|
|
226
|
+
</div>
|
|
227
|
+
<div class="form-group">
|
|
228
|
+
<label>Max Tokens:</label>
|
|
229
|
+
<input type="number" id="analyze-tokens" value="8000" placeholder="8000">
|
|
230
|
+
</div>
|
|
231
|
+
<button class="btn" onclick="testAnalyze()">Analyze & Download</button>
|
|
232
|
+
<div class="response" id="analyze-response"></div>
|
|
233
|
+
</div>
|
|
234
|
+
|
|
235
|
+
<!-- Info -->
|
|
236
|
+
<div class="endpoint-card">
|
|
237
|
+
<div class="endpoint-header">
|
|
238
|
+
<span class="method get">GET</span>
|
|
239
|
+
<span class="endpoint-path">/api/info</span>
|
|
240
|
+
</div>
|
|
241
|
+
<p class="endpoint-desc">Get project information and statistics</p>
|
|
242
|
+
<div class="form-group">
|
|
243
|
+
<label>Project Path:</label>
|
|
244
|
+
<input type="text" id="info-path" value="." placeholder=".">
|
|
245
|
+
</div>
|
|
246
|
+
<button class="btn" onclick="testInfo()">Get Info</button>
|
|
247
|
+
<div class="response" id="info-response"></div>
|
|
248
|
+
</div>
|
|
249
|
+
|
|
250
|
+
<!-- Execute Bash -->
|
|
251
|
+
<div class="endpoint-card">
|
|
252
|
+
<div class="endpoint-header">
|
|
253
|
+
<span class="method post">POST</span>
|
|
254
|
+
<span class="endpoint-path">/api/execute</span>
|
|
255
|
+
</div>
|
|
256
|
+
<p class="endpoint-desc">Execute bash script with syntax validation</p>
|
|
257
|
+
<div class="form-group">
|
|
258
|
+
<label>Bash Script:</label>
|
|
259
|
+
<textarea id="execute-bash" placeholder="echo 'Hello World' date"></textarea>
|
|
260
|
+
</div>
|
|
261
|
+
<button class="btn" onclick="testExecute()">Execute Script</button>
|
|
262
|
+
<div class="response" id="execute-response"></div>
|
|
263
|
+
</div>
|
|
264
|
+
|
|
265
|
+
<!-- Clean -->
|
|
266
|
+
<div class="endpoint-card">
|
|
267
|
+
<div class="endpoint-header">
|
|
268
|
+
<span class="method delete">DELETE</span>
|
|
269
|
+
<span class="endpoint-path">/api/clean</span>
|
|
270
|
+
</div>
|
|
271
|
+
<p class="endpoint-desc">Clean output directory</p>
|
|
272
|
+
<div class="form-group">
|
|
273
|
+
<label>Output Path:</label>
|
|
274
|
+
<input type="text" id="clean-output" value="./vg-output" placeholder="./vg-output">
|
|
275
|
+
</div>
|
|
276
|
+
<button class="btn" onclick="testClean()">Clean Directory</button>
|
|
277
|
+
<div class="response" id="clean-response"></div>
|
|
278
|
+
</div>
|
|
279
|
+
</div>
|
|
280
|
+
</div>
|
|
281
|
+
|
|
282
|
+
<script>
|
|
283
|
+
const API_BASE = window.location.origin;
|
|
284
|
+
|
|
285
|
+
function showResponse(elementId, data, isError = false) {
|
|
286
|
+
const el = document.getElementById(elementId);
|
|
287
|
+
el.className = 'response show ' + (isError ? 'error' : 'success');
|
|
288
|
+
el.innerHTML = '<pre>' + JSON.stringify(data, null, 2) + '</pre>';
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function showLoading(button) {
|
|
292
|
+
button.disabled = true;
|
|
293
|
+
button.innerHTML = '<span class="loading"></span> Loading...';
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function resetButton(button, text) {
|
|
297
|
+
button.disabled = false;
|
|
298
|
+
button.textContent = text;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
async function testHealth() {
|
|
302
|
+
const btn = event.target;
|
|
303
|
+
showLoading(btn);
|
|
304
|
+
try {
|
|
305
|
+
const res = await fetch(`${API_BASE}/health`);
|
|
306
|
+
const data = await res.json();
|
|
307
|
+
showResponse('health-response', data);
|
|
308
|
+
} catch (err) {
|
|
309
|
+
showResponse('health-response', { error: err.message }, true);
|
|
310
|
+
}
|
|
311
|
+
resetButton(btn, 'Test Health Check');
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
async function testAnalyze() {
|
|
315
|
+
const btn = event.target;
|
|
316
|
+
const path = document.getElementById('analyze-path').value;
|
|
317
|
+
const maxTokens = document.getElementById('analyze-tokens').value;
|
|
318
|
+
|
|
319
|
+
showLoading(btn);
|
|
320
|
+
try {
|
|
321
|
+
const res = await fetch(`${API_BASE}/api/analyze`, {
|
|
322
|
+
method: 'POST',
|
|
323
|
+
headers: { 'Content-Type': 'application/json' },
|
|
324
|
+
body: JSON.stringify({
|
|
325
|
+
path,
|
|
326
|
+
options: { maxTokens: parseInt(maxTokens) }
|
|
327
|
+
})
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
if (res.ok) {
|
|
331
|
+
const blob = await res.blob();
|
|
332
|
+
const url = window.URL.createObjectURL(blob);
|
|
333
|
+
const a = document.createElement('a');
|
|
334
|
+
a.href = url;
|
|
335
|
+
a.download = 'project.txt';
|
|
336
|
+
a.click();
|
|
337
|
+
showResponse('analyze-response', { success: true, message: 'File downloaded!' });
|
|
338
|
+
} else {
|
|
339
|
+
const data = await res.json();
|
|
340
|
+
showResponse('analyze-response', data, true);
|
|
341
|
+
}
|
|
342
|
+
} catch (err) {
|
|
343
|
+
showResponse('analyze-response', { error: err.message }, true);
|
|
344
|
+
}
|
|
345
|
+
resetButton(btn, 'Analyze & Download');
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
async function testInfo() {
|
|
349
|
+
const btn = event.target;
|
|
350
|
+
const path = document.getElementById('info-path').value;
|
|
351
|
+
|
|
352
|
+
showLoading(btn);
|
|
353
|
+
try {
|
|
354
|
+
const res = await fetch(`${API_BASE}/api/info?path=${encodeURIComponent(path)}`);
|
|
355
|
+
const data = await res.json();
|
|
356
|
+
showResponse('info-response', data, !res.ok);
|
|
357
|
+
} catch (err) {
|
|
358
|
+
showResponse('info-response', { error: err.message }, true);
|
|
359
|
+
}
|
|
360
|
+
resetButton(btn, 'Get Info');
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
async function testExecute() {
|
|
364
|
+
const btn = event.target;
|
|
365
|
+
const bash = document.getElementById('execute-bash').value;
|
|
366
|
+
|
|
367
|
+
if (!bash.trim()) {
|
|
368
|
+
showResponse('execute-response', { error: 'Bash script is required' }, true);
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
showLoading(btn);
|
|
373
|
+
try {
|
|
374
|
+
const res = await fetch(`${API_BASE}/api/execute`, {
|
|
375
|
+
method: 'POST',
|
|
376
|
+
headers: { 'Content-Type': 'application/json' },
|
|
377
|
+
body: JSON.stringify({ bash })
|
|
378
|
+
});
|
|
379
|
+
const data = await res.json();
|
|
380
|
+
showResponse('execute-response', data, !res.ok || !data.success);
|
|
381
|
+
} catch (err) {
|
|
382
|
+
showResponse('execute-response', { error: err.message }, true);
|
|
383
|
+
}
|
|
384
|
+
resetButton(btn, 'Execute Script');
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
async function testClean() {
|
|
388
|
+
const btn = event.target;
|
|
389
|
+
const output = document.getElementById('clean-output').value;
|
|
390
|
+
|
|
391
|
+
showLoading(btn);
|
|
392
|
+
try {
|
|
393
|
+
const res = await fetch(`${API_BASE}/api/clean`, {
|
|
394
|
+
method: 'DELETE',
|
|
395
|
+
headers: { 'Content-Type': 'application/json' },
|
|
396
|
+
body: JSON.stringify({ output })
|
|
397
|
+
});
|
|
398
|
+
const data = await res.json();
|
|
399
|
+
showResponse('clean-response', data, !res.ok);
|
|
400
|
+
} catch (err) {
|
|
401
|
+
showResponse('clean-response', { error: err.message }, true);
|
|
402
|
+
}
|
|
403
|
+
resetButton(btn, 'Clean Directory');
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// Check server status periodically
|
|
407
|
+
setInterval(async () => {
|
|
408
|
+
try {
|
|
409
|
+
const res = await fetch(`${API_BASE}/health`);
|
|
410
|
+
if (res.ok) {
|
|
411
|
+
document.getElementById('status').textContent = '● Server Running';
|
|
412
|
+
document.getElementById('status').style.background = '#10b981';
|
|
413
|
+
}
|
|
414
|
+
} catch {
|
|
415
|
+
document.getElementById('status').textContent = '● Server Offline';
|
|
416
|
+
document.getElementById('status').style.background = '#ef4444';
|
|
417
|
+
}
|
|
418
|
+
}, 5000);
|
|
419
|
+
</script>
|
|
420
|
+
</body>
|
|
421
|
+
</html>
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
const { exec } = require('child_process');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const fs = require('fs-extra');
|
|
4
|
+
const { promisify } = require('util');
|
|
5
|
+
|
|
6
|
+
const execAsync = promisify(exec);
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Bash Script Executor
|
|
10
|
+
* Validates and executes bash scripts safely
|
|
11
|
+
*/
|
|
12
|
+
class BashExecutor {
|
|
13
|
+
constructor(workingDir) {
|
|
14
|
+
this.workingDir = workingDir || process.cwd();
|
|
15
|
+
this.tempDir = path.join(this.workingDir, '.vg', 'temp-execute');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Ensure temp directory exists
|
|
20
|
+
*/
|
|
21
|
+
async ensureTempDir() {
|
|
22
|
+
await fs.ensureDir(this.tempDir);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Cleanup temp directory
|
|
27
|
+
*/
|
|
28
|
+
async cleanup() {
|
|
29
|
+
try {
|
|
30
|
+
if (await fs.pathExists(this.tempDir)) {
|
|
31
|
+
await fs.remove(this.tempDir);
|
|
32
|
+
}
|
|
33
|
+
} catch (error) {
|
|
34
|
+
console.error('Cleanup error:', error.message);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Validate bash script syntax
|
|
40
|
+
* @param {string} bashScript - The bash script to validate
|
|
41
|
+
* @returns {Promise<{valid: boolean, error: string|null}>}
|
|
42
|
+
*/
|
|
43
|
+
async validateSyntax(bashScript) {
|
|
44
|
+
await this.ensureTempDir();
|
|
45
|
+
|
|
46
|
+
const scriptPath = path.join(this.tempDir, 'validate.sh');
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
// Write script to temp file
|
|
50
|
+
await fs.writeFile(scriptPath, bashScript, 'utf8');
|
|
51
|
+
|
|
52
|
+
// Validate syntax using bash -n
|
|
53
|
+
await execAsync(`bash -n "${scriptPath}"`);
|
|
54
|
+
|
|
55
|
+
return { valid: true, error: null };
|
|
56
|
+
|
|
57
|
+
} catch (error) {
|
|
58
|
+
return {
|
|
59
|
+
valid: false,
|
|
60
|
+
error: error.stderr || error.message
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Execute bash script in working directory
|
|
67
|
+
* @param {string} bashScript - The bash script to execute
|
|
68
|
+
* @returns {Promise<{success: boolean, stdout: string, stderr: string, exitCode: number}>}
|
|
69
|
+
*/
|
|
70
|
+
async execute(bashScript) {
|
|
71
|
+
const startTime = Date.now();
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
// First validate syntax
|
|
75
|
+
const validation = await this.validateSyntax(bashScript);
|
|
76
|
+
|
|
77
|
+
if (!validation.valid) {
|
|
78
|
+
return {
|
|
79
|
+
success: false,
|
|
80
|
+
error: 'Syntax validation failed',
|
|
81
|
+
details: validation.error,
|
|
82
|
+
executionTime: Date.now() - startTime
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Execute script in working directory
|
|
87
|
+
const result = await execAsync(bashScript, {
|
|
88
|
+
cwd: this.workingDir,
|
|
89
|
+
maxBuffer: 10 * 1024 * 1024, // 10MB buffer
|
|
90
|
+
shell: '/bin/bash'
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// Cleanup temp directory
|
|
94
|
+
await this.cleanup();
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
success: true,
|
|
98
|
+
stdout: result.stdout || '',
|
|
99
|
+
stderr: result.stderr || '',
|
|
100
|
+
exitCode: 0,
|
|
101
|
+
executionTime: Date.now() - startTime
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
} catch (error) {
|
|
105
|
+
// Cleanup even on error
|
|
106
|
+
await this.cleanup();
|
|
107
|
+
|
|
108
|
+
// Check if it's an execution error (not syntax)
|
|
109
|
+
if (error.code !== undefined) {
|
|
110
|
+
return {
|
|
111
|
+
success: false,
|
|
112
|
+
stdout: error.stdout || '',
|
|
113
|
+
stderr: error.stderr || error.message,
|
|
114
|
+
exitCode: error.code || 1,
|
|
115
|
+
executionTime: Date.now() - startTime
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Unknown error
|
|
120
|
+
return {
|
|
121
|
+
success: false,
|
|
122
|
+
error: 'Execution failed',
|
|
123
|
+
details: error.message,
|
|
124
|
+
executionTime: Date.now() - startTime
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
module.exports = BashExecutor;
|