vg-coder-cli 1.0.12 → 1.0.14
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/package.json +1 -1
- package/src/server/api-server.js +1 -2
- package/src/server/views/dashboard.html +327 -275
package/package.json
CHANGED
package/src/server/api-server.js
CHANGED
|
@@ -83,9 +83,8 @@ class ApiServer {
|
|
|
83
83
|
const detector = new ProjectDetector(resolvedPath);
|
|
84
84
|
const projectInfo = await detector.detectAll();
|
|
85
85
|
|
|
86
|
-
// Scan files
|
|
86
|
+
// Scan files (no token limit, get all files)
|
|
87
87
|
const scannerOptions = {
|
|
88
|
-
maxTokens: parseInt(options.maxTokens || 8000),
|
|
89
88
|
extensions: options.extensions ? options.extensions.split(',').map(ext => ext.trim()) : undefined,
|
|
90
89
|
includeHidden: options.includeHidden || false
|
|
91
90
|
};
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
.container {
|
|
23
|
-
max-width:
|
|
23
|
+
max-width: 1000px;
|
|
24
24
|
margin: 0 auto;
|
|
25
25
|
}
|
|
26
26
|
|
|
@@ -53,41 +53,87 @@
|
|
|
53
53
|
margin-top: 10px;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
.system-prompt-card {
|
|
57
|
+
background: white;
|
|
58
|
+
border-radius: 12px;
|
|
59
|
+
padding: 25px;
|
|
60
|
+
margin-bottom: 30px;
|
|
61
|
+
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.system-prompt-header {
|
|
65
|
+
display: flex;
|
|
66
|
+
justify-content: space-between;
|
|
67
|
+
align-items: center;
|
|
68
|
+
margin-bottom: 15px;
|
|
69
|
+
cursor: pointer;
|
|
70
|
+
user-select: none;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.system-prompt-header h2 {
|
|
74
|
+
color: #667eea;
|
|
75
|
+
font-size: 1.5em;
|
|
76
|
+
display: flex;
|
|
77
|
+
align-items: center;
|
|
78
|
+
gap: 10px;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.toggle-icon {
|
|
82
|
+
transition: transform 0.3s ease;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.toggle-icon.open {
|
|
86
|
+
transform: rotate(180deg);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.system-prompt-content {
|
|
90
|
+
max-height: 0;
|
|
91
|
+
overflow: hidden;
|
|
92
|
+
transition: max-height 0.3s ease;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.system-prompt-content.open {
|
|
96
|
+
max-height: 2000px;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.prompt-text {
|
|
100
|
+
background: #f9fafb;
|
|
101
|
+
border: 2px solid #e5e7eb;
|
|
102
|
+
border-radius: 8px;
|
|
103
|
+
padding: 20px;
|
|
104
|
+
font-family: 'Courier New', monospace;
|
|
105
|
+
font-size: 0.9em;
|
|
106
|
+
line-height: 1.6;
|
|
107
|
+
white-space: pre-wrap;
|
|
108
|
+
max-height: 400px;
|
|
109
|
+
overflow-y: auto;
|
|
110
|
+
margin-bottom: 15px;
|
|
111
|
+
}
|
|
112
|
+
|
|
56
113
|
.endpoints {
|
|
57
114
|
display: grid;
|
|
58
|
-
grid-template-columns: repeat(auto-fit, minmax(500px, 1fr));
|
|
59
115
|
gap: 20px;
|
|
60
116
|
}
|
|
61
117
|
|
|
62
118
|
.endpoint-card {
|
|
63
119
|
background: white;
|
|
64
120
|
border-radius: 12px;
|
|
65
|
-
padding:
|
|
121
|
+
padding: 30px;
|
|
66
122
|
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
|
|
67
|
-
transition: transform 0.3s ease;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
.endpoint-card:hover {
|
|
71
|
-
transform: translateY(-5px);
|
|
72
123
|
}
|
|
73
124
|
|
|
74
125
|
.endpoint-header {
|
|
75
126
|
display: flex;
|
|
76
127
|
align-items: center;
|
|
77
128
|
gap: 15px;
|
|
78
|
-
margin-bottom:
|
|
129
|
+
margin-bottom: 20px;
|
|
79
130
|
}
|
|
80
131
|
|
|
81
132
|
.method {
|
|
82
|
-
padding:
|
|
133
|
+
padding: 8px 16px;
|
|
83
134
|
border-radius: 6px;
|
|
84
135
|
font-weight: bold;
|
|
85
|
-
font-size: 0.
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
.method.get {
|
|
89
|
-
background: #3b82f6;
|
|
90
|
-
color: white;
|
|
136
|
+
font-size: 0.9em;
|
|
91
137
|
}
|
|
92
138
|
|
|
93
139
|
.method.post {
|
|
@@ -95,74 +141,104 @@
|
|
|
95
141
|
color: white;
|
|
96
142
|
}
|
|
97
143
|
|
|
98
|
-
.method.delete {
|
|
99
|
-
background: #ef4444;
|
|
100
|
-
color: white;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
144
|
.endpoint-path {
|
|
104
145
|
font-family: 'Courier New', monospace;
|
|
105
146
|
color: #333;
|
|
106
|
-
font-size: 1.
|
|
147
|
+
font-size: 1.2em;
|
|
148
|
+
font-weight: 600;
|
|
107
149
|
}
|
|
108
150
|
|
|
109
151
|
.endpoint-desc {
|
|
110
152
|
color: #666;
|
|
111
|
-
margin-bottom:
|
|
153
|
+
margin-bottom: 20px;
|
|
112
154
|
line-height: 1.6;
|
|
155
|
+
font-size: 1.05em;
|
|
113
156
|
}
|
|
114
157
|
|
|
115
158
|
.form-group {
|
|
116
|
-
margin-bottom:
|
|
159
|
+
margin-bottom: 20px;
|
|
117
160
|
}
|
|
118
161
|
|
|
119
162
|
.form-group label {
|
|
120
163
|
display: block;
|
|
121
|
-
margin-bottom:
|
|
164
|
+
margin-bottom: 8px;
|
|
122
165
|
color: #333;
|
|
123
|
-
font-weight:
|
|
166
|
+
font-weight: 600;
|
|
167
|
+
font-size: 1em;
|
|
124
168
|
}
|
|
125
169
|
|
|
126
170
|
.form-group input,
|
|
127
171
|
.form-group textarea {
|
|
128
172
|
width: 100%;
|
|
129
|
-
padding:
|
|
173
|
+
padding: 12px;
|
|
130
174
|
border: 2px solid #e5e7eb;
|
|
131
|
-
border-radius:
|
|
132
|
-
font-size:
|
|
175
|
+
border-radius: 8px;
|
|
176
|
+
font-size: 1em;
|
|
133
177
|
font-family: 'Courier New', monospace;
|
|
178
|
+
transition: border-color 0.3s ease;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.form-group input:focus,
|
|
182
|
+
.form-group textarea:focus {
|
|
183
|
+
outline: none;
|
|
184
|
+
border-color: #667eea;
|
|
134
185
|
}
|
|
135
186
|
|
|
136
187
|
.form-group textarea {
|
|
137
|
-
min-height:
|
|
188
|
+
min-height: 150px;
|
|
138
189
|
resize: vertical;
|
|
139
190
|
}
|
|
140
191
|
|
|
192
|
+
.btn-group {
|
|
193
|
+
display: flex;
|
|
194
|
+
gap: 10px;
|
|
195
|
+
margin-top: 20px;
|
|
196
|
+
}
|
|
197
|
+
|
|
141
198
|
.btn {
|
|
142
199
|
background: #667eea;
|
|
143
200
|
color: white;
|
|
144
201
|
border: none;
|
|
145
|
-
padding:
|
|
146
|
-
border-radius:
|
|
202
|
+
padding: 14px 28px;
|
|
203
|
+
border-radius: 8px;
|
|
147
204
|
cursor: pointer;
|
|
148
205
|
font-size: 1em;
|
|
149
206
|
font-weight: 600;
|
|
150
|
-
transition:
|
|
207
|
+
transition: all 0.3s ease;
|
|
208
|
+
display: inline-flex;
|
|
209
|
+
align-items: center;
|
|
210
|
+
gap: 8px;
|
|
151
211
|
}
|
|
152
212
|
|
|
153
213
|
.btn:hover {
|
|
154
214
|
background: #5568d3;
|
|
215
|
+
transform: translateY(-2px);
|
|
216
|
+
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
|
|
155
217
|
}
|
|
156
218
|
|
|
157
219
|
.btn:disabled {
|
|
158
220
|
background: #9ca3af;
|
|
159
221
|
cursor: not-allowed;
|
|
222
|
+
transform: none;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.btn-copy {
|
|
226
|
+
background: #10b981;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
.btn-copy:hover {
|
|
230
|
+
background: #059669;
|
|
231
|
+
box-shadow: 0 4px 12px rgba(16, 185, 129, 0.4);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
.btn-copy.copied {
|
|
235
|
+
background: #6366f1;
|
|
160
236
|
}
|
|
161
237
|
|
|
162
238
|
.response {
|
|
163
|
-
margin-top:
|
|
164
|
-
padding:
|
|
165
|
-
border-radius:
|
|
239
|
+
margin-top: 20px;
|
|
240
|
+
padding: 20px;
|
|
241
|
+
border-radius: 8px;
|
|
166
242
|
background: #f9fafb;
|
|
167
243
|
border-left: 4px solid #667eea;
|
|
168
244
|
display: none;
|
|
@@ -185,7 +261,7 @@
|
|
|
185
261
|
.response pre {
|
|
186
262
|
margin: 0;
|
|
187
263
|
font-family: 'Courier New', monospace;
|
|
188
|
-
font-size: 0.
|
|
264
|
+
font-size: 0.95em;
|
|
189
265
|
white-space: pre-wrap;
|
|
190
266
|
word-wrap: break-word;
|
|
191
267
|
}
|
|
@@ -206,76 +282,36 @@
|
|
|
206
282
|
}
|
|
207
283
|
}
|
|
208
284
|
|
|
209
|
-
.
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
cursor: pointer;
|
|
223
|
-
user-select: none;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
.system-prompt-header h2 {
|
|
227
|
-
color: #667eea;
|
|
228
|
-
font-size: 1.5em;
|
|
229
|
-
display: flex;
|
|
230
|
-
align-items: center;
|
|
231
|
-
gap: 10px;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
.toggle-icon {
|
|
235
|
-
transition: transform 0.3s ease;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
.toggle-icon.open {
|
|
239
|
-
transform: rotate(180deg);
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
.system-prompt-content {
|
|
243
|
-
max-height: 0;
|
|
244
|
-
overflow: hidden;
|
|
245
|
-
transition: max-height 0.3s ease;
|
|
285
|
+
.toast {
|
|
286
|
+
position: fixed;
|
|
287
|
+
top: 20px;
|
|
288
|
+
right: 20px;
|
|
289
|
+
padding: 16px 24px;
|
|
290
|
+
border-radius: 8px;
|
|
291
|
+
color: white;
|
|
292
|
+
font-weight: 600;
|
|
293
|
+
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
|
|
294
|
+
opacity: 0;
|
|
295
|
+
transform: translateY(-20px);
|
|
296
|
+
transition: all 0.3s ease;
|
|
297
|
+
z-index: 1000;
|
|
246
298
|
}
|
|
247
299
|
|
|
248
|
-
.
|
|
249
|
-
|
|
300
|
+
.toast.show {
|
|
301
|
+
opacity: 1;
|
|
302
|
+
transform: translateY(0);
|
|
250
303
|
}
|
|
251
304
|
|
|
252
|
-
.
|
|
253
|
-
background: #f9fafb;
|
|
254
|
-
border: 2px solid #e5e7eb;
|
|
255
|
-
border-radius: 8px;
|
|
256
|
-
padding: 20px;
|
|
257
|
-
font-family: 'Courier New', monospace;
|
|
258
|
-
font-size: 0.9em;
|
|
259
|
-
line-height: 1.6;
|
|
260
|
-
white-space: pre-wrap;
|
|
261
|
-
max-height: 400px;
|
|
262
|
-
overflow-y: auto;
|
|
263
|
-
margin-bottom: 15px;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
.btn-copy {
|
|
305
|
+
.toast.success {
|
|
267
306
|
background: #10b981;
|
|
268
|
-
display: inline-flex;
|
|
269
|
-
align-items: center;
|
|
270
|
-
gap: 8px;
|
|
271
307
|
}
|
|
272
308
|
|
|
273
|
-
.
|
|
274
|
-
background: #
|
|
309
|
+
.toast.error {
|
|
310
|
+
background: #ef4444;
|
|
275
311
|
}
|
|
276
312
|
|
|
277
|
-
.
|
|
278
|
-
background: #
|
|
313
|
+
.toast.info {
|
|
314
|
+
background: #3b82f6;
|
|
279
315
|
}
|
|
280
316
|
</style>
|
|
281
317
|
</head>
|
|
@@ -284,7 +320,7 @@
|
|
|
284
320
|
<div class="container">
|
|
285
321
|
<div class="header">
|
|
286
322
|
<h1>🚀 VG Coder API Dashboard</h1>
|
|
287
|
-
<p>
|
|
323
|
+
<p>Phân tích dự án và thực thi bash scripts</p>
|
|
288
324
|
<span class="status" id="status">● Server Running</span>
|
|
289
325
|
</div>
|
|
290
326
|
|
|
@@ -307,206 +343,56 @@
|
|
|
307
343
|
</div>
|
|
308
344
|
|
|
309
345
|
<div class="endpoints">
|
|
310
|
-
<!-- Health Check -->
|
|
311
|
-
<div class="endpoint-card">
|
|
312
|
-
<div class="endpoint-header">
|
|
313
|
-
<span class="method get">GET</span>
|
|
314
|
-
<span class="endpoint-path">/health</span>
|
|
315
|
-
</div>
|
|
316
|
-
<p class="endpoint-desc">Check server health status</p>
|
|
317
|
-
<button class="btn" onclick="testHealth()">Test Health Check</button>
|
|
318
|
-
<div class="response" id="health-response"></div>
|
|
319
|
-
</div>
|
|
320
|
-
|
|
321
346
|
<!-- Analyze -->
|
|
322
347
|
<div class="endpoint-card">
|
|
323
348
|
<div class="endpoint-header">
|
|
324
349
|
<span class="method post">POST</span>
|
|
325
350
|
<span class="endpoint-path">/api/analyze</span>
|
|
326
351
|
</div>
|
|
327
|
-
<p class="endpoint-desc"
|
|
352
|
+
<p class="endpoint-desc">📊 Phân tích dự án và lấy toàn bộ source code</p>
|
|
328
353
|
<div class="form-group">
|
|
329
|
-
<label
|
|
354
|
+
<label>📁 Project Path:</label>
|
|
330
355
|
<input type="text" id="analyze-path" value="." placeholder=".">
|
|
331
356
|
</div>
|
|
332
|
-
<div class="
|
|
333
|
-
<
|
|
334
|
-
|
|
357
|
+
<div class="btn-group">
|
|
358
|
+
<button class="btn" onclick="testAnalyze()">
|
|
359
|
+
<span>📥</span>
|
|
360
|
+
<span>Download File</span>
|
|
361
|
+
</button>
|
|
362
|
+
<button class="btn btn-copy" onclick="copyAnalyzeResult()">
|
|
363
|
+
<span id="analyze-copy-icon">📋</span>
|
|
364
|
+
<span id="analyze-copy-text">Copy to Clipboard</span>
|
|
365
|
+
</button>
|
|
335
366
|
</div>
|
|
336
|
-
<button class="btn" onclick="testAnalyze()">Analyze & Download</button>
|
|
337
367
|
<div class="response" id="analyze-response"></div>
|
|
338
368
|
</div>
|
|
339
369
|
|
|
340
|
-
<!-- Info -->
|
|
341
|
-
<div class="endpoint-card">
|
|
342
|
-
<div class="endpoint-header">
|
|
343
|
-
<span class="method get">GET</span>
|
|
344
|
-
<span class="endpoint-path">/api/info</span>
|
|
345
|
-
</div>
|
|
346
|
-
<p class="endpoint-desc">Get project information and statistics</p>
|
|
347
|
-
<div class="form-group">
|
|
348
|
-
<label>Project Path:</label>
|
|
349
|
-
<input type="text" id="info-path" value="." placeholder=".">
|
|
350
|
-
</div>
|
|
351
|
-
<button class="btn" onclick="testInfo()">Get Info</button>
|
|
352
|
-
<div class="response" id="info-response"></div>
|
|
353
|
-
</div>
|
|
354
|
-
|
|
355
370
|
<!-- Execute Bash -->
|
|
356
371
|
<div class="endpoint-card">
|
|
357
372
|
<div class="endpoint-header">
|
|
358
373
|
<span class="method post">POST</span>
|
|
359
374
|
<span class="endpoint-path">/api/execute</span>
|
|
360
375
|
</div>
|
|
361
|
-
<p class="endpoint-desc"
|
|
376
|
+
<p class="endpoint-desc">⚡ Thực thi bash script với syntax validation</p>
|
|
362
377
|
<div class="form-group">
|
|
363
|
-
<label
|
|
364
|
-
<textarea id="execute-bash"
|
|
378
|
+
<label>💻 Bash Script:</label>
|
|
379
|
+
<textarea id="execute-bash"
|
|
380
|
+
placeholder="mkdir -p $(dirname "src/test.js") cat <<'EOF' > src/test.js console.log('Hello World'); EOF"></textarea>
|
|
365
381
|
</div>
|
|
366
|
-
<button class="btn" onclick="testExecute()">
|
|
382
|
+
<button class="btn" onclick="testExecute()">
|
|
383
|
+
<span>▶️</span>
|
|
384
|
+
<span>Execute Script</span>
|
|
385
|
+
</button>
|
|
367
386
|
<div class="response" id="execute-response"></div>
|
|
368
387
|
</div>
|
|
369
|
-
|
|
370
|
-
<!-- Clean -->
|
|
371
|
-
<div class="endpoint-card">
|
|
372
|
-
<div class="endpoint-header">
|
|
373
|
-
<span class="method delete">DELETE</span>
|
|
374
|
-
<span class="endpoint-path">/api/clean</span>
|
|
375
|
-
</div>
|
|
376
|
-
<p class="endpoint-desc">Clean output directory</p>
|
|
377
|
-
<div class="form-group">
|
|
378
|
-
<label>Output Path:</label>
|
|
379
|
-
<input type="text" id="clean-output" value="./vg-output" placeholder="./vg-output">
|
|
380
|
-
</div>
|
|
381
|
-
<button class="btn" onclick="testClean()">Clean Directory</button>
|
|
382
|
-
<div class="response" id="clean-response"></div>
|
|
383
|
-
</div>
|
|
384
388
|
</div>
|
|
385
389
|
</div>
|
|
386
390
|
|
|
391
|
+
<div class="toast" id="toast"></div>
|
|
392
|
+
|
|
387
393
|
<script>
|
|
388
394
|
const API_BASE = window.location.origin;
|
|
389
|
-
|
|
390
|
-
function showResponse(elementId, data, isError = false) {
|
|
391
|
-
const el = document.getElementById(elementId);
|
|
392
|
-
el.className = 'response show ' + (isError ? 'error' : 'success');
|
|
393
|
-
el.innerHTML = '<pre>' + JSON.stringify(data, null, 2) + '</pre>';
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
function showLoading(button) {
|
|
397
|
-
button.disabled = true;
|
|
398
|
-
button.innerHTML = '<span class="loading"></span> Loading...';
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
function resetButton(button, text) {
|
|
402
|
-
button.disabled = false;
|
|
403
|
-
button.textContent = text;
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
async function testHealth() {
|
|
407
|
-
const btn = event.target;
|
|
408
|
-
showLoading(btn);
|
|
409
|
-
try {
|
|
410
|
-
const res = await fetch(`${API_BASE}/health`);
|
|
411
|
-
const data = await res.json();
|
|
412
|
-
showResponse('health-response', data);
|
|
413
|
-
} catch (err) {
|
|
414
|
-
showResponse('health-response', { error: err.message }, true);
|
|
415
|
-
}
|
|
416
|
-
resetButton(btn, 'Test Health Check');
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
async function testAnalyze() {
|
|
420
|
-
const btn = event.target;
|
|
421
|
-
const path = document.getElementById('analyze-path').value;
|
|
422
|
-
const maxTokens = document.getElementById('analyze-tokens').value;
|
|
423
|
-
|
|
424
|
-
showLoading(btn);
|
|
425
|
-
try {
|
|
426
|
-
const res = await fetch(`${API_BASE}/api/analyze`, {
|
|
427
|
-
method: 'POST',
|
|
428
|
-
headers: { 'Content-Type': 'application/json' },
|
|
429
|
-
body: JSON.stringify({
|
|
430
|
-
path,
|
|
431
|
-
options: { maxTokens: parseInt(maxTokens) }
|
|
432
|
-
})
|
|
433
|
-
});
|
|
434
|
-
|
|
435
|
-
if (res.ok) {
|
|
436
|
-
const blob = await res.blob();
|
|
437
|
-
const url = window.URL.createObjectURL(blob);
|
|
438
|
-
const a = document.createElement('a');
|
|
439
|
-
a.href = url;
|
|
440
|
-
a.download = 'project.txt';
|
|
441
|
-
a.click();
|
|
442
|
-
showResponse('analyze-response', { success: true, message: 'File downloaded!' });
|
|
443
|
-
} else {
|
|
444
|
-
const data = await res.json();
|
|
445
|
-
showResponse('analyze-response', data, true);
|
|
446
|
-
}
|
|
447
|
-
} catch (err) {
|
|
448
|
-
showResponse('analyze-response', { error: err.message }, true);
|
|
449
|
-
}
|
|
450
|
-
resetButton(btn, 'Analyze & Download');
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
async function testInfo() {
|
|
454
|
-
const btn = event.target;
|
|
455
|
-
const path = document.getElementById('info-path').value;
|
|
456
|
-
|
|
457
|
-
showLoading(btn);
|
|
458
|
-
try {
|
|
459
|
-
const res = await fetch(`${API_BASE}/api/info?path=${encodeURIComponent(path)}`);
|
|
460
|
-
const data = await res.json();
|
|
461
|
-
showResponse('info-response', data, !res.ok);
|
|
462
|
-
} catch (err) {
|
|
463
|
-
showResponse('info-response', { error: err.message }, true);
|
|
464
|
-
}
|
|
465
|
-
resetButton(btn, 'Get Info');
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
async function testExecute() {
|
|
469
|
-
const btn = event.target;
|
|
470
|
-
const bash = document.getElementById('execute-bash').value;
|
|
471
|
-
|
|
472
|
-
if (!bash.trim()) {
|
|
473
|
-
showResponse('execute-response', { error: 'Bash script is required' }, true);
|
|
474
|
-
return;
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
showLoading(btn);
|
|
478
|
-
try {
|
|
479
|
-
const res = await fetch(`${API_BASE}/api/execute`, {
|
|
480
|
-
method: 'POST',
|
|
481
|
-
headers: { 'Content-Type': 'application/json' },
|
|
482
|
-
body: JSON.stringify({ bash })
|
|
483
|
-
});
|
|
484
|
-
const data = await res.json();
|
|
485
|
-
showResponse('execute-response', data, !res.ok || !data.success);
|
|
486
|
-
} catch (err) {
|
|
487
|
-
showResponse('execute-response', { error: err.message }, true);
|
|
488
|
-
}
|
|
489
|
-
resetButton(btn, 'Execute Script');
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
async function testClean() {
|
|
493
|
-
const btn = event.target;
|
|
494
|
-
const output = document.getElementById('clean-output').value;
|
|
495
|
-
|
|
496
|
-
showLoading(btn);
|
|
497
|
-
try {
|
|
498
|
-
const res = await fetch(`${API_BASE}/api/clean`, {
|
|
499
|
-
method: 'DELETE',
|
|
500
|
-
headers: { 'Content-Type': 'application/json' },
|
|
501
|
-
body: JSON.stringify({ output })
|
|
502
|
-
});
|
|
503
|
-
const data = await res.json();
|
|
504
|
-
showResponse('clean-response', data, !res.ok);
|
|
505
|
-
} catch (err) {
|
|
506
|
-
showResponse('clean-response', { error: err.message }, true);
|
|
507
|
-
}
|
|
508
|
-
resetButton(btn, 'Clean Directory');
|
|
509
|
-
}
|
|
395
|
+
let lastAnalyzeResult = null;
|
|
510
396
|
|
|
511
397
|
// System Prompt
|
|
512
398
|
const SYSTEM_PROMPT = `# VG Coder AI System Prompt
|
|
@@ -645,6 +531,7 @@ API sẽ:
|
|
|
645
531
|
copyBtn.classList.add('copied');
|
|
646
532
|
copyIcon.textContent = '✓';
|
|
647
533
|
copyText.textContent = 'Copied!';
|
|
534
|
+
showToast('✅ Đã copy System Prompt!', 'success');
|
|
648
535
|
|
|
649
536
|
setTimeout(() => {
|
|
650
537
|
copyBtn.classList.remove('copied');
|
|
@@ -652,20 +539,185 @@ API sẽ:
|
|
|
652
539
|
copyText.textContent = 'Copy System Prompt';
|
|
653
540
|
}, 2000);
|
|
654
541
|
}).catch(err => {
|
|
655
|
-
|
|
542
|
+
showToast('❌ Lỗi copy: ' + err.message, 'error');
|
|
656
543
|
});
|
|
657
544
|
}
|
|
658
545
|
|
|
546
|
+
function showResponse(elementId, data, isError = false) {
|
|
547
|
+
const el = document.getElementById(elementId);
|
|
548
|
+
el.className = 'response show ' + (isError ? 'error' : 'success');
|
|
549
|
+
el.innerHTML = '<pre>' + JSON.stringify(data, null, 2) + '</pre>';
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
function showLoading(button, originalText) {
|
|
553
|
+
button.disabled = true;
|
|
554
|
+
button.innerHTML = '<span class="loading"></span> Loading...';
|
|
555
|
+
button.dataset.originalText = originalText;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
function resetButton(button) {
|
|
559
|
+
button.disabled = false;
|
|
560
|
+
const originalText = button.dataset.originalText;
|
|
561
|
+
button.innerHTML = originalText;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
async function testAnalyze() {
|
|
565
|
+
const btn = event.target.closest('.btn');
|
|
566
|
+
const path = document.getElementById('analyze-path').value;
|
|
567
|
+
|
|
568
|
+
showLoading(btn, '<span>📥</span><span>Download File</span>');
|
|
569
|
+
try {
|
|
570
|
+
const res = await fetch(`${API_BASE}/api/analyze`, {
|
|
571
|
+
method: 'POST',
|
|
572
|
+
headers: { 'Content-Type': 'application/json' },
|
|
573
|
+
body: JSON.stringify({ path })
|
|
574
|
+
});
|
|
575
|
+
|
|
576
|
+
if (res.ok) {
|
|
577
|
+
const text = await res.text();
|
|
578
|
+
lastAnalyzeResult = text;
|
|
579
|
+
|
|
580
|
+
// Download file
|
|
581
|
+
const blob = new Blob([text], { type: 'text/plain' });
|
|
582
|
+
const url = window.URL.createObjectURL(blob);
|
|
583
|
+
const a = document.createElement('a');
|
|
584
|
+
a.href = url;
|
|
585
|
+
a.download = 'project.txt';
|
|
586
|
+
a.click();
|
|
587
|
+
|
|
588
|
+
showResponse('analyze-response', {
|
|
589
|
+
success: true,
|
|
590
|
+
message: 'File downloaded!',
|
|
591
|
+
files: text.split('\n').filter(l => l.includes('===== FILE:')).length,
|
|
592
|
+
size: (text.length / 1024).toFixed(2) + ' KB'
|
|
593
|
+
});
|
|
594
|
+
showToast('✅ Đã download file!', 'success');
|
|
595
|
+
} else {
|
|
596
|
+
const data = await res.json();
|
|
597
|
+
showResponse('analyze-response', data, true);
|
|
598
|
+
showToast('❌ Lỗi analyze!', 'error');
|
|
599
|
+
}
|
|
600
|
+
} catch (err) {
|
|
601
|
+
showResponse('analyze-response', { error: err.message }, true);
|
|
602
|
+
showToast('❌ Lỗi: ' + err.message, 'error');
|
|
603
|
+
}
|
|
604
|
+
resetButton(btn);
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
async function copyAnalyzeResult() {
|
|
608
|
+
const copyBtn = event.target.closest('.btn-copy');
|
|
609
|
+
const copyIcon = document.getElementById('analyze-copy-icon');
|
|
610
|
+
const copyText = document.getElementById('analyze-copy-text');
|
|
611
|
+
|
|
612
|
+
if (!lastAnalyzeResult) {
|
|
613
|
+
// Fetch if not already analyzed
|
|
614
|
+
const path = document.getElementById('analyze-path').value;
|
|
615
|
+
showLoading(copyBtn, '<span id="analyze-copy-icon">📋</span><span id="analyze-copy-text">Copy to Clipboard</span>');
|
|
616
|
+
|
|
617
|
+
try {
|
|
618
|
+
const res = await fetch(`${API_BASE}/api/analyze`, {
|
|
619
|
+
method: 'POST',
|
|
620
|
+
headers: { 'Content-Type': 'application/json' },
|
|
621
|
+
body: JSON.stringify({ path })
|
|
622
|
+
});
|
|
623
|
+
|
|
624
|
+
if (res.ok) {
|
|
625
|
+
lastAnalyzeResult = await res.text();
|
|
626
|
+
} else {
|
|
627
|
+
showToast('❌ Lỗi analyze!', 'error');
|
|
628
|
+
resetButton(copyBtn);
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
} catch (err) {
|
|
632
|
+
showToast('❌ Lỗi: ' + err.message, 'error');
|
|
633
|
+
resetButton(copyBtn);
|
|
634
|
+
return;
|
|
635
|
+
}
|
|
636
|
+
resetButton(copyBtn);
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
// Copy to clipboard using ClipboardItem (like reference code)
|
|
640
|
+
try {
|
|
641
|
+
const blob = new Blob([lastAnalyzeResult], { type: 'text/plain' });
|
|
642
|
+
const item = new ClipboardItem({ 'text/plain': blob });
|
|
643
|
+
await navigator.clipboard.write([item]);
|
|
644
|
+
|
|
645
|
+
copyBtn.classList.add('copied');
|
|
646
|
+
copyIcon.textContent = '✓';
|
|
647
|
+
copyText.textContent = 'Copied!';
|
|
648
|
+
showToast('✅ Đã copy project.txt vào clipboard!', 'success');
|
|
649
|
+
|
|
650
|
+
setTimeout(() => {
|
|
651
|
+
copyBtn.classList.remove('copied');
|
|
652
|
+
copyIcon.textContent = '📋';
|
|
653
|
+
copyText.textContent = 'Copy to Clipboard';
|
|
654
|
+
}, 2000);
|
|
655
|
+
} catch (err) {
|
|
656
|
+
// Fallback to writeText if ClipboardItem fails
|
|
657
|
+
try {
|
|
658
|
+
await navigator.clipboard.writeText(lastAnalyzeResult);
|
|
659
|
+
copyBtn.classList.add('copied');
|
|
660
|
+
copyIcon.textContent = '✓';
|
|
661
|
+
copyText.textContent = 'Copied!';
|
|
662
|
+
showToast('✅ Đã copy project.txt vào clipboard!', 'success');
|
|
663
|
+
|
|
664
|
+
setTimeout(() => {
|
|
665
|
+
copyBtn.classList.remove('copied');
|
|
666
|
+
copyIcon.textContent = '📋';
|
|
667
|
+
copyText.textContent = 'Copy to Clipboard';
|
|
668
|
+
}, 2000);
|
|
669
|
+
} catch (fallbackErr) {
|
|
670
|
+
showToast('❌ Lỗi copy: ' + fallbackErr.message, 'error');
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
async function testExecute() {
|
|
676
|
+
const btn = event.target.closest('.btn');
|
|
677
|
+
const bash = document.getElementById('execute-bash').value;
|
|
678
|
+
|
|
679
|
+
if (!bash.trim()) {
|
|
680
|
+
showToast('⚠️ Vui lòng nhập bash script!', 'error');
|
|
681
|
+
return;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
showLoading(btn, '<span>▶️</span><span>Execute Script</span>');
|
|
685
|
+
try {
|
|
686
|
+
const res = await fetch(`${API_BASE}/api/execute`, {
|
|
687
|
+
method: 'POST',
|
|
688
|
+
headers: { 'Content-Type': 'application/json' },
|
|
689
|
+
body: JSON.stringify({ bash })
|
|
690
|
+
});
|
|
691
|
+
const data = await res.json();
|
|
692
|
+
showResponse('execute-response', data, !res.ok || !data.success);
|
|
693
|
+
|
|
694
|
+
if (data.success) {
|
|
695
|
+
showToast('✅ Thực thi thành công!', 'success');
|
|
696
|
+
} else {
|
|
697
|
+
showToast('❌ Thực thi thất bại!', 'error');
|
|
698
|
+
}
|
|
699
|
+
} catch (err) {
|
|
700
|
+
showResponse('execute-response', { error: err.message }, true);
|
|
701
|
+
showToast('❌ Lỗi: ' + err.message, 'error');
|
|
702
|
+
}
|
|
703
|
+
resetButton(btn);
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
function showToast(message, type = 'success') {
|
|
707
|
+
const toast = document.getElementById('toast');
|
|
708
|
+
toast.textContent = message;
|
|
709
|
+
toast.className = `toast ${type}`;
|
|
710
|
+
toast.classList.add('show');
|
|
711
|
+
setTimeout(() => toast.classList.remove('show'), 3000);
|
|
712
|
+
}
|
|
713
|
+
|
|
659
714
|
// Check server status ONCE on page load
|
|
660
715
|
(async () => {
|
|
661
716
|
try {
|
|
662
717
|
const res = await fetch(`${API_BASE}/health`);
|
|
663
718
|
if (res.ok) {
|
|
664
|
-
const data = await res.json();
|
|
665
719
|
document.getElementById('status').textContent = '● Server Running';
|
|
666
720
|
document.getElementById('status').style.background = '#10b981';
|
|
667
|
-
// Auto-show health check result
|
|
668
|
-
showResponse('health-response', data);
|
|
669
721
|
}
|
|
670
722
|
} catch {
|
|
671
723
|
document.getElementById('status').textContent = '● Server Offline';
|