vg-coder-cli 2.0.45 → 2.0.47

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.
@@ -0,0 +1,112 @@
1
+ const path = require('path');
2
+ const fs = require('fs-extra');
3
+ const crypto = require('crypto');
4
+
5
+ function newTaskId() {
6
+ const ts = Date.now();
7
+ const rand = crypto.randomBytes(3).toString('hex');
8
+ return `t_${ts}_${rand}`;
9
+ }
10
+
11
+ function sanitizeTaskId(id) {
12
+ return String(id || '').replace(/[^a-zA-Z0-9_-]/g, '').slice(0, 80);
13
+ }
14
+
15
+ function tasksRoot(workingDir) {
16
+ return path.join(workingDir, '.vg', 'tasks');
17
+ }
18
+
19
+ function taskDir(workingDir, taskId) {
20
+ return path.join(tasksRoot(workingDir), sanitizeTaskId(taskId));
21
+ }
22
+
23
+ function taskJsonPath(workingDir, taskId) {
24
+ return path.join(taskDir(workingDir, taskId), 'task.json');
25
+ }
26
+
27
+ function resultMdPath(workingDir, taskId) {
28
+ return path.join(taskDir(workingDir, taskId), 'result.md');
29
+ }
30
+
31
+ function filesDir(workingDir, taskId) {
32
+ return path.join(taskDir(workingDir, taskId), 'files');
33
+ }
34
+
35
+ async function saveTask(task) {
36
+ const file = taskJsonPath(task.workingDir, task.id);
37
+ await fs.ensureDir(path.dirname(file));
38
+ task.timing = task.timing || {};
39
+ await fs.writeJson(file, task, { spaces: 2 });
40
+ return task;
41
+ }
42
+
43
+ async function loadTask(workingDir, taskId) {
44
+ const file = taskJsonPath(workingDir, taskId);
45
+ if (!await fs.pathExists(file)) return null;
46
+ return fs.readJson(file);
47
+ }
48
+
49
+ async function writeResult(workingDir, taskId, markdown) {
50
+ const file = resultMdPath(workingDir, taskId);
51
+ await fs.ensureDir(path.dirname(file));
52
+ await fs.writeFile(file, markdown || '', 'utf8');
53
+ }
54
+
55
+ async function readResult(workingDir, taskId) {
56
+ const file = resultMdPath(workingDir, taskId);
57
+ if (!await fs.pathExists(file)) return '';
58
+ return fs.readFile(file, 'utf8');
59
+ }
60
+
61
+ async function listTasks(workingDir, filter = {}) {
62
+ const root = tasksRoot(workingDir);
63
+ if (!await fs.pathExists(root)) return [];
64
+ const ids = (await fs.readdir(root)).filter(n => n.startsWith('t_'));
65
+ const out = [];
66
+ for (const id of ids) {
67
+ try {
68
+ const t = await fs.readJson(taskJsonPath(workingDir, id));
69
+ if (filter.status && t.status !== filter.status) continue;
70
+ out.push({
71
+ id: t.id,
72
+ status: t.status,
73
+ prompt: (t.prompt || '').slice(0, 200),
74
+ createdAt: t.timing?.createdAt,
75
+ finishedAt: t.timing?.finishedAt,
76
+ durationMs: t.timing?.durationMs,
77
+ webhookUrl: t.webhookUrl || null
78
+ });
79
+ } catch (_) { /* skip */ }
80
+ }
81
+ out.sort((a, b) => (b.createdAt || 0) - (a.createdAt || 0));
82
+ if (filter.limit) return out.slice(0, filter.limit);
83
+ return out;
84
+ }
85
+
86
+ async function rehydrate(workingDir) {
87
+ const root = tasksRoot(workingDir);
88
+ if (!await fs.pathExists(root)) return [];
89
+ const ids = (await fs.readdir(root)).filter(n => n.startsWith('t_'));
90
+ const tasks = [];
91
+ for (const id of ids) {
92
+ try { tasks.push(await fs.readJson(taskJsonPath(workingDir, id))); }
93
+ catch (_) { /* skip */ }
94
+ }
95
+ return tasks;
96
+ }
97
+
98
+ module.exports = {
99
+ newTaskId,
100
+ sanitizeTaskId,
101
+ tasksRoot,
102
+ taskDir,
103
+ taskJsonPath,
104
+ resultMdPath,
105
+ filesDir,
106
+ saveTask,
107
+ loadTask,
108
+ writeResult,
109
+ readResult,
110
+ listTasks,
111
+ rehydrate
112
+ };
@@ -0,0 +1,48 @@
1
+ const chalk = require('chalk');
2
+ const store = require('./task-store');
3
+
4
+ const RETRY_DELAYS_MS = [1000, 4000, 16000];
5
+
6
+ function sleep(ms) { return new Promise(r => setTimeout(r, ms)); }
7
+
8
+ async function deliver(task) {
9
+ if (!task?.webhookUrl) return;
10
+
11
+ const markdown = task.status === 'done' ? await store.readResult(task.workingDir, task.id) : '';
12
+ const body = {
13
+ taskId: task.id,
14
+ status: task.status,
15
+ result: { markdown, chatId: task.result?.chatId || null },
16
+ error: task.error || null,
17
+ durationMs: task.timing?.durationMs || null,
18
+ meta: task.meta || null
19
+ };
20
+
21
+ task.webhook = task.webhook || { attempts: [], deliveredAt: null };
22
+
23
+ for (let i = 0; i < RETRY_DELAYS_MS.length; i++) {
24
+ try {
25
+ const res = await fetch(task.webhookUrl, {
26
+ method: 'POST',
27
+ headers: { 'Content-Type': 'application/json' },
28
+ body: JSON.stringify(body)
29
+ });
30
+ const ok = res.status >= 200 && res.status < 300;
31
+ task.webhook.attempts.push({ at: Date.now(), status: res.status, ok });
32
+ if (ok) {
33
+ task.webhook.deliveredAt = Date.now();
34
+ await store.saveTask(task);
35
+ return true;
36
+ }
37
+ console.log(chalk.yellow(`[Webhook] ${task.id} attempt ${i + 1} → HTTP ${res.status}`));
38
+ } catch (err) {
39
+ task.webhook.attempts.push({ at: Date.now(), error: err.message });
40
+ console.log(chalk.yellow(`[Webhook] ${task.id} attempt ${i + 1} → ${err.message}`));
41
+ }
42
+ if (i < RETRY_DELAYS_MS.length - 1) await sleep(RETRY_DELAYS_MS[i]);
43
+ }
44
+ await store.saveTask(task);
45
+ return false;
46
+ }
47
+
48
+ module.exports = { deliver };
@@ -354,17 +354,172 @@
354
354
  /* Mermaid Diagrams */
355
355
  .agent-mermaid {
356
356
  margin: 16px 0;
357
- padding: 20px;
358
357
  background-color: var(--code-block-bg, #161b22);
359
358
  border-radius: 6px;
359
+ overflow: hidden;
360
+ min-height: 100px;
361
+ position: relative;
362
+ }
363
+
364
+ .agent-mermaid-toolbar {
365
+ display: flex;
366
+ justify-content: flex-end;
367
+ gap: 4px;
368
+ padding: 8px 12px;
369
+ background: rgba(0, 0, 0, 0.3);
370
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
371
+ }
372
+
373
+ .agent-mermaid-btn {
374
+ display: inline-flex;
375
+ align-items: center;
376
+ gap: 4px;
377
+ padding: 4px 8px;
378
+ background: rgba(255, 255, 255, 0.1);
379
+ border: none;
380
+ border-radius: 4px;
381
+ color: #a1a1aa;
382
+ cursor: pointer;
383
+ font-size: 11px;
384
+ transition: all 0.2s ease;
385
+ }
386
+
387
+ .agent-mermaid-btn:hover {
388
+ background: rgba(255, 255, 255, 0.2);
389
+ color: #ededed;
390
+ }
391
+
392
+ .agent-mermaid-btn:active {
393
+ transform: scale(0.95);
394
+ }
395
+
396
+ .agent-mermaid-diagram {
397
+ padding: 20px;
360
398
  display: flex;
361
399
  justify-content: center;
362
400
  align-items: center;
363
401
  overflow-x: auto;
364
- min-height: 100px;
365
402
  }
366
403
 
367
- .agent-mermaid svg {
404
+ .agent-mermaid-diagram svg {
405
+ max-width: 100%;
406
+ height: auto;
407
+ }
408
+
409
+ /* Mermaid Toast Notification */
410
+ .agent-mermaid-toast {
411
+ position: absolute;
412
+ top: 50%;
413
+ left: 50%;
414
+ transform: translate(-50%, -50%);
415
+ padding: 8px 16px;
416
+ border-radius: 6px;
417
+ font-size: 12px;
418
+ font-weight: 500;
419
+ z-index: 100;
420
+ animation: mermaid-toast-fade 2s ease-out forwards;
421
+ }
422
+
423
+ .agent-mermaid-toast.success {
424
+ background: rgba(74, 222, 128, 0.9);
425
+ color: #000;
426
+ }
427
+
428
+ .agent-mermaid-toast.error {
429
+ background: rgba(239, 68, 68, 0.9);
430
+ color: #fff;
431
+ }
432
+
433
+ @keyframes mermaid-toast-fade {
434
+ 0% { opacity: 0; transform: translate(-50%, -50%) scale(0.8); }
435
+ 15% { opacity: 1; transform: translate(-50%, -50%) scale(1); }
436
+ 85% { opacity: 1; transform: translate(-50%, -50%) scale(1); }
437
+ 100% { opacity: 0; transform: translate(-50%, -50%) scale(0.8); }
438
+ }
439
+
440
+ /* Mermaid Fullscreen Modal */
441
+ .agent-mermaid-modal {
442
+ position: fixed;
443
+ top: 0;
444
+ left: 0;
445
+ right: 0;
446
+ bottom: 0;
447
+ background: rgba(0, 0, 0, 0.85);
448
+ z-index: 10000;
449
+ display: flex;
450
+ align-items: center;
451
+ justify-content: center;
452
+ padding: 40px;
453
+ opacity: 0;
454
+ transition: opacity 0.2s ease;
455
+ }
456
+
457
+ .agent-mermaid-modal.open {
458
+ opacity: 1;
459
+ }
460
+
461
+ .agent-mermaid-modal.closing {
462
+ opacity: 0;
463
+ }
464
+
465
+ .agent-mermaid-modal-content {
466
+ background: #1a1a1a;
467
+ border-radius: 12px;
468
+ max-width: 95vw;
469
+ max-height: 90vh;
470
+ overflow: hidden;
471
+ display: flex;
472
+ flex-direction: column;
473
+ box-shadow: 0 25px 50px rgba(0, 0, 0, 0.5);
474
+ transform: scale(0.95);
475
+ transition: transform 0.2s ease;
476
+ }
477
+
478
+ .agent-mermaid-modal.open .agent-mermaid-modal-content {
479
+ transform: scale(1);
480
+ }
481
+
482
+ .agent-mermaid-modal-header {
483
+ display: flex;
484
+ justify-content: space-between;
485
+ align-items: center;
486
+ padding: 16px 20px;
487
+ background: #252525;
488
+ border-bottom: 1px solid #333;
489
+ color: #ededed;
490
+ font-weight: 600;
491
+ font-size: 14px;
492
+ }
493
+
494
+ .agent-mermaid-modal-actions {
495
+ display: flex;
496
+ gap: 8px;
497
+ }
498
+
499
+ .agent-mermaid-modal-actions .agent-mermaid-btn {
500
+ padding: 6px 12px;
501
+ }
502
+
503
+ .agent-mermaid-close {
504
+ background: rgba(239, 68, 68, 0.2);
505
+ color: #ef4444;
506
+ }
507
+
508
+ .agent-mermaid-close:hover {
509
+ background: rgba(239, 68, 68, 0.3);
510
+ color: #f87171;
511
+ }
512
+
513
+ .agent-mermaid-modal-body {
514
+ padding: 40px;
515
+ overflow: auto;
516
+ display: flex;
517
+ align-items: center;
518
+ justify-content: center;
519
+ background: #161b22;
520
+ }
521
+
522
+ .agent-mermaid-modal-body svg {
368
523
  max-width: 100%;
369
524
  height: auto;
370
525
  }
@@ -449,3 +604,104 @@
449
604
  .agent-retry-btn:hover {
450
605
  background: #b91c1c;
451
606
  }
607
+
608
+ /* History Pane — mounted inside #tool-panel-agent */
609
+ #tool-panel-agent { position: relative; }
610
+
611
+ .agent-history-modal-inline {
612
+ position: absolute;
613
+ inset: 0;
614
+ z-index: 50;
615
+ background: #18181b;
616
+ color: #ededed;
617
+ display: flex;
618
+ flex-direction: column;
619
+ overflow: hidden;
620
+ }
621
+
622
+ .agent-history-pane {
623
+ flex: 1;
624
+ display: flex;
625
+ flex-direction: column;
626
+ min-height: 0;
627
+ }
628
+
629
+ .agent-history-header {
630
+ display: flex;
631
+ align-items: center;
632
+ justify-content: space-between;
633
+ padding: 12px 16px;
634
+ border-bottom: 1px solid #27272a;
635
+ font-weight: 600;
636
+ font-size: 14px;
637
+ }
638
+
639
+ .agent-history-close {
640
+ background: transparent;
641
+ border: none;
642
+ color: #a1a1aa;
643
+ font-size: 22px;
644
+ line-height: 1;
645
+ cursor: pointer;
646
+ padding: 0 4px;
647
+ }
648
+ .agent-history-close:hover { color: #ededed; }
649
+
650
+ .agent-history-list {
651
+ overflow-y: auto;
652
+ padding: 6px;
653
+ }
654
+
655
+ .agent-history-empty {
656
+ padding: 24px;
657
+ text-align: center;
658
+ color: #71717a;
659
+ font-size: 13px;
660
+ }
661
+
662
+ .agent-history-row {
663
+ display: flex;
664
+ align-items: center;
665
+ gap: 8px;
666
+ padding: 10px 12px;
667
+ border-radius: 6px;
668
+ cursor: pointer;
669
+ transition: background 0.15s;
670
+ }
671
+ .agent-history-row:hover { background: #27272a; }
672
+
673
+ .agent-history-row-main { flex: 1; min-width: 0; }
674
+
675
+ .agent-history-title {
676
+ font-size: 13px;
677
+ font-weight: 500;
678
+ overflow: hidden;
679
+ text-overflow: ellipsis;
680
+ white-space: nowrap;
681
+ }
682
+
683
+ .agent-history-meta {
684
+ display: flex;
685
+ gap: 10px;
686
+ margin-top: 4px;
687
+ font-size: 11px;
688
+ color: #71717a;
689
+ }
690
+
691
+ .agent-history-delete {
692
+ background: transparent;
693
+ border: none;
694
+ color: #71717a;
695
+ font-size: 18px;
696
+ cursor: pointer;
697
+ padding: 2px 8px;
698
+ border-radius: 4px;
699
+ }
700
+ .agent-history-delete:hover { color: #ef4444; background: rgba(239, 68, 68, 0.1); }
701
+
702
+ [data-theme="light"] .agent-history-modal-inline {
703
+ background: #ffffff;
704
+ color: #18181b;
705
+ }
706
+ [data-theme="light"] .agent-history-header { border-bottom-color: #e5e5ea; }
707
+ [data-theme="light"] .agent-history-row:hover { background: #f2f2f7; }
@@ -245,17 +245,172 @@
245
245
  /* Mermaid Diagrams */
246
246
  .markdown-body .mermaid-diagram {
247
247
  margin: 16px 0;
248
- padding: 20px;
249
248
  background-color: var(--code-block-bg, #161b22);
250
249
  border-radius: 6px;
250
+ overflow: hidden;
251
+ min-height: 100px;
252
+ position: relative;
253
+ }
254
+
255
+ .mermaid-toolbar {
256
+ display: flex;
257
+ justify-content: flex-end;
258
+ gap: 4px;
259
+ padding: 8px 12px;
260
+ background: rgba(0, 0, 0, 0.3);
261
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
262
+ }
263
+
264
+ .mermaid-btn {
265
+ display: inline-flex;
266
+ align-items: center;
267
+ gap: 4px;
268
+ padding: 4px 8px;
269
+ background: rgba(255, 255, 255, 0.1);
270
+ border: none;
271
+ border-radius: 4px;
272
+ color: #a1a1aa;
273
+ cursor: pointer;
274
+ font-size: 11px;
275
+ transition: all 0.2s ease;
276
+ }
277
+
278
+ .mermaid-btn:hover {
279
+ background: rgba(255, 255, 255, 0.2);
280
+ color: #ededed;
281
+ }
282
+
283
+ .mermaid-btn:active {
284
+ transform: scale(0.95);
285
+ }
286
+
287
+ .mermaid-diagram-content {
288
+ padding: 20px;
251
289
  display: flex;
252
290
  justify-content: center;
253
291
  align-items: center;
254
292
  overflow-x: auto;
255
- min-height: 100px;
256
293
  }
257
294
 
258
- .markdown-body .mermaid-diagram svg {
295
+ .mermaid-diagram-content svg {
296
+ max-width: 100%;
297
+ height: auto;
298
+ }
299
+
300
+ /* Mermaid Toast Notification */
301
+ .mermaid-toast {
302
+ position: absolute;
303
+ top: 50%;
304
+ left: 50%;
305
+ transform: translate(-50%, -50%);
306
+ padding: 8px 16px;
307
+ border-radius: 6px;
308
+ font-size: 12px;
309
+ font-weight: 500;
310
+ z-index: 100;
311
+ animation: mermaid-toast-fade 2s ease-out forwards;
312
+ }
313
+
314
+ .mermaid-toast.success {
315
+ background: rgba(74, 222, 128, 0.9);
316
+ color: #000;
317
+ }
318
+
319
+ .mermaid-toast.error {
320
+ background: rgba(239, 68, 68, 0.9);
321
+ color: #fff;
322
+ }
323
+
324
+ @keyframes mermaid-toast-fade {
325
+ 0% { opacity: 0; transform: translate(-50%, -50%) scale(0.8); }
326
+ 15% { opacity: 1; transform: translate(-50%, -50%) scale(1); }
327
+ 85% { opacity: 1; transform: translate(-50%, -50%) scale(1); }
328
+ 100% { opacity: 0; transform: translate(-50%, -50%) scale(0.8); }
329
+ }
330
+
331
+ /* Mermaid Fullscreen Modal */
332
+ .mermaid-modal {
333
+ position: fixed;
334
+ top: 0;
335
+ left: 0;
336
+ right: 0;
337
+ bottom: 0;
338
+ background: rgba(0, 0, 0, 0.85);
339
+ z-index: 10000;
340
+ display: flex;
341
+ align-items: center;
342
+ justify-content: center;
343
+ padding: 40px;
344
+ opacity: 0;
345
+ transition: opacity 0.2s ease;
346
+ }
347
+
348
+ .mermaid-modal.open {
349
+ opacity: 1;
350
+ }
351
+
352
+ .mermaid-modal.closing {
353
+ opacity: 0;
354
+ }
355
+
356
+ .mermaid-modal-content {
357
+ background: #1a1a1a;
358
+ border-radius: 12px;
359
+ max-width: 95vw;
360
+ max-height: 90vh;
361
+ overflow: hidden;
362
+ display: flex;
363
+ flex-direction: column;
364
+ box-shadow: 0 25px 50px rgba(0, 0, 0, 0.5);
365
+ transform: scale(0.95);
366
+ transition: transform 0.2s ease;
367
+ }
368
+
369
+ .mermaid-modal.open .mermaid-modal-content {
370
+ transform: scale(1);
371
+ }
372
+
373
+ .mermaid-modal-header {
374
+ display: flex;
375
+ justify-content: space-between;
376
+ align-items: center;
377
+ padding: 16px 20px;
378
+ background: #252525;
379
+ border-bottom: 1px solid #333;
380
+ color: #ededed;
381
+ font-weight: 600;
382
+ font-size: 14px;
383
+ }
384
+
385
+ .mermaid-modal-actions {
386
+ display: flex;
387
+ gap: 8px;
388
+ }
389
+
390
+ .mermaid-modal-actions .mermaid-btn {
391
+ padding: 6px 12px;
392
+ }
393
+
394
+ .mermaid-close {
395
+ background: rgba(239, 68, 68, 0.2);
396
+ color: #ef4444;
397
+ }
398
+
399
+ .mermaid-close:hover {
400
+ background: rgba(239, 68, 68, 0.3);
401
+ color: #f87171;
402
+ }
403
+
404
+ .mermaid-modal-body {
405
+ padding: 40px;
406
+ overflow: auto;
407
+ display: flex;
408
+ align-items: center;
409
+ justify-content: center;
410
+ background: #161b22;
411
+ }
412
+
413
+ .mermaid-modal-body svg {
259
414
  max-width: 100%;
260
415
  height: auto;
261
416
  }