@virtengine/openfleet 0.25.0

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 (120) hide show
  1. package/.env.example +914 -0
  2. package/LICENSE +190 -0
  3. package/README.md +500 -0
  4. package/agent-endpoint.mjs +918 -0
  5. package/agent-hook-bridge.mjs +230 -0
  6. package/agent-hooks.mjs +1188 -0
  7. package/agent-pool.mjs +2403 -0
  8. package/agent-prompts.mjs +689 -0
  9. package/agent-sdk.mjs +141 -0
  10. package/anomaly-detector.mjs +1195 -0
  11. package/autofix.mjs +1294 -0
  12. package/claude-shell.mjs +708 -0
  13. package/cli.mjs +906 -0
  14. package/codex-config.mjs +1274 -0
  15. package/codex-model-profiles.mjs +135 -0
  16. package/codex-shell.mjs +762 -0
  17. package/config-doctor.mjs +613 -0
  18. package/config.mjs +1720 -0
  19. package/conflict-resolver.mjs +248 -0
  20. package/container-runner.mjs +450 -0
  21. package/copilot-shell.mjs +827 -0
  22. package/daemon-restart-policy.mjs +56 -0
  23. package/diff-stats.mjs +282 -0
  24. package/error-detector.mjs +829 -0
  25. package/fetch-runtime.mjs +34 -0
  26. package/fleet-coordinator.mjs +838 -0
  27. package/get-telegram-chat-id.mjs +71 -0
  28. package/git-safety.mjs +170 -0
  29. package/github-reconciler.mjs +403 -0
  30. package/hook-profiles.mjs +651 -0
  31. package/kanban-adapter.mjs +4491 -0
  32. package/lib/logger.mjs +645 -0
  33. package/maintenance.mjs +828 -0
  34. package/merge-strategy.mjs +1171 -0
  35. package/monitor.mjs +12207 -0
  36. package/openfleet.config.example.json +115 -0
  37. package/openfleet.schema.json +465 -0
  38. package/package.json +203 -0
  39. package/postinstall.mjs +187 -0
  40. package/pr-cleanup-daemon.mjs +978 -0
  41. package/preflight.mjs +408 -0
  42. package/prepublish-check.mjs +90 -0
  43. package/presence.mjs +328 -0
  44. package/primary-agent.mjs +282 -0
  45. package/publish.mjs +151 -0
  46. package/repo-root.mjs +29 -0
  47. package/restart-controller.mjs +100 -0
  48. package/review-agent.mjs +557 -0
  49. package/rotate-agent-logs.sh +133 -0
  50. package/sdk-conflict-resolver.mjs +973 -0
  51. package/session-tracker.mjs +880 -0
  52. package/setup.mjs +3937 -0
  53. package/shared-knowledge.mjs +410 -0
  54. package/shared-state-manager.mjs +841 -0
  55. package/shared-workspace-cli.mjs +199 -0
  56. package/shared-workspace-registry.mjs +537 -0
  57. package/shared-workspaces.json +18 -0
  58. package/startup-service.mjs +1070 -0
  59. package/sync-engine.mjs +1063 -0
  60. package/task-archiver.mjs +801 -0
  61. package/task-assessment.mjs +550 -0
  62. package/task-claims.mjs +924 -0
  63. package/task-complexity.mjs +581 -0
  64. package/task-executor.mjs +5111 -0
  65. package/task-store.mjs +753 -0
  66. package/telegram-bot.mjs +9281 -0
  67. package/telegram-sentinel.mjs +2010 -0
  68. package/ui/app.js +867 -0
  69. package/ui/app.legacy.js +1464 -0
  70. package/ui/app.monolith.js +2488 -0
  71. package/ui/components/charts.js +226 -0
  72. package/ui/components/chat-view.js +567 -0
  73. package/ui/components/command-palette.js +587 -0
  74. package/ui/components/diff-viewer.js +190 -0
  75. package/ui/components/forms.js +327 -0
  76. package/ui/components/kanban-board.js +451 -0
  77. package/ui/components/session-list.js +305 -0
  78. package/ui/components/shared.js +473 -0
  79. package/ui/index.html +70 -0
  80. package/ui/modules/api.js +297 -0
  81. package/ui/modules/icons.js +461 -0
  82. package/ui/modules/router.js +81 -0
  83. package/ui/modules/settings-schema.js +261 -0
  84. package/ui/modules/state.js +679 -0
  85. package/ui/modules/telegram.js +331 -0
  86. package/ui/modules/utils.js +270 -0
  87. package/ui/styles/animations.css +140 -0
  88. package/ui/styles/base.css +98 -0
  89. package/ui/styles/components.css +1915 -0
  90. package/ui/styles/kanban.css +286 -0
  91. package/ui/styles/layout.css +809 -0
  92. package/ui/styles/sessions.css +827 -0
  93. package/ui/styles/variables.css +188 -0
  94. package/ui/styles.css +141 -0
  95. package/ui/styles.monolith.css +1046 -0
  96. package/ui/tabs/agents.js +1417 -0
  97. package/ui/tabs/chat.js +74 -0
  98. package/ui/tabs/control.js +887 -0
  99. package/ui/tabs/dashboard.js +515 -0
  100. package/ui/tabs/infra.js +537 -0
  101. package/ui/tabs/logs.js +783 -0
  102. package/ui/tabs/settings.js +1487 -0
  103. package/ui/tabs/tasks.js +1385 -0
  104. package/ui-server.mjs +4073 -0
  105. package/update-check.mjs +465 -0
  106. package/utils.mjs +172 -0
  107. package/ve-kanban.mjs +654 -0
  108. package/ve-kanban.ps1 +1365 -0
  109. package/ve-kanban.sh +18 -0
  110. package/ve-orchestrator.mjs +340 -0
  111. package/ve-orchestrator.ps1 +6546 -0
  112. package/ve-orchestrator.sh +18 -0
  113. package/vibe-kanban-wrapper.mjs +41 -0
  114. package/vk-error-resolver.mjs +470 -0
  115. package/vk-log-stream.mjs +914 -0
  116. package/whatsapp-channel.mjs +520 -0
  117. package/workspace-monitor.mjs +581 -0
  118. package/workspace-reaper.mjs +405 -0
  119. package/workspace-registry.mjs +238 -0
  120. package/worktree-manager.mjs +1266 -0
@@ -0,0 +1,461 @@
1
+ /* ─────────────────────────────────────────────────────────────
2
+ * VirtEngine Control Center – Icon Library
3
+ * SVG icons as htm tagged template literals
4
+ * ────────────────────────────────────────────────────────────── */
5
+
6
+ import { h } from "preact";
7
+ import htm from "htm";
8
+
9
+ const html = htm.bind(h);
10
+
11
+ /**
12
+ * All SVG icons share these base attributes:
13
+ * viewBox="0 0 24 24" fill="none" stroke="currentColor"
14
+ * stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
15
+ *
16
+ * Small inline icons additionally set width="16" height="16".
17
+ */
18
+ export const ICONS = {
19
+ /* ── Navigation / Layout ── */
20
+ grid: html`<svg
21
+ viewBox="0 0 24 24"
22
+ fill="none"
23
+ stroke="currentColor"
24
+ stroke-width="2"
25
+ stroke-linecap="round"
26
+ stroke-linejoin="round"
27
+ >
28
+ <rect x="3" y="3" width="7" height="7" />
29
+ <rect x="14" y="3" width="7" height="7" />
30
+ <rect x="3" y="14" width="7" height="7" />
31
+ <rect x="14" y="14" width="7" height="7" />
32
+ </svg>`,
33
+
34
+ home: html`<svg
35
+ viewBox="0 0 24 24"
36
+ fill="none"
37
+ stroke="currentColor"
38
+ stroke-width="2"
39
+ stroke-linecap="round"
40
+ stroke-linejoin="round"
41
+ >
42
+ <path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" />
43
+ <polyline points="9 22 9 12 15 12 15 22" />
44
+ </svg>`,
45
+
46
+ chat: html`<svg
47
+ viewBox="0 0 24 24"
48
+ fill="none"
49
+ stroke="currentColor"
50
+ stroke-width="2"
51
+ stroke-linecap="round"
52
+ stroke-linejoin="round"
53
+ >
54
+ <path d="M21 11.5a8.38 8.38 0 0 1-1.9 5.4 8.5 8.5 0 0 1-6.6 3.1 8.38 8.38 0 0 1-5.4-1.9L3 21l1.9-4.1A8.38 8.38 0 0 1 3 11.5 8.5 8.5 0 0 1 11.5 3h.5A8.5 8.5 0 0 1 21 11.5z" />
55
+ </svg>`,
56
+
57
+ /* ── Status / Feedback ── */
58
+ check: html`<svg
59
+ viewBox="0 0 24 24"
60
+ fill="none"
61
+ stroke="currentColor"
62
+ stroke-width="2"
63
+ stroke-linecap="round"
64
+ stroke-linejoin="round"
65
+ >
66
+ <path d="M22 11.08V12a10 10 0 1 1-5.93-9.14" />
67
+ <polyline points="22 4 12 14.01 9 11.01" />
68
+ </svg>`,
69
+
70
+ star: html`<svg
71
+ viewBox="0 0 24 24"
72
+ fill="none"
73
+ stroke="currentColor"
74
+ stroke-width="2"
75
+ stroke-linecap="round"
76
+ stroke-linejoin="round"
77
+ >
78
+ <polygon
79
+ points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"
80
+ />
81
+ </svg>`,
82
+
83
+ bell: html`<svg
84
+ viewBox="0 0 24 24"
85
+ fill="none"
86
+ stroke="currentColor"
87
+ stroke-width="2"
88
+ stroke-linecap="round"
89
+ stroke-linejoin="round"
90
+ >
91
+ <path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9" />
92
+ <path d="M13.73 21a2 2 0 0 1-3.46 0" />
93
+ </svg>`,
94
+
95
+ shield: html`<svg
96
+ viewBox="0 0 24 24"
97
+ fill="none"
98
+ stroke="currentColor"
99
+ stroke-width="2"
100
+ stroke-linecap="round"
101
+ stroke-linejoin="round"
102
+ >
103
+ <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" />
104
+ </svg>`,
105
+
106
+ zap: html`<svg
107
+ viewBox="0 0 24 24"
108
+ fill="none"
109
+ stroke="currentColor"
110
+ stroke-width="2"
111
+ stroke-linecap="round"
112
+ stroke-linejoin="round"
113
+ >
114
+ <polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2" />
115
+ </svg>`,
116
+
117
+ clock: html`<svg
118
+ viewBox="0 0 24 24"
119
+ fill="none"
120
+ stroke="currentColor"
121
+ stroke-width="2"
122
+ stroke-linecap="round"
123
+ stroke-linejoin="round"
124
+ >
125
+ <circle cx="12" cy="12" r="10" />
126
+ <polyline points="12 6 12 12 16 14" />
127
+ </svg>`,
128
+
129
+ close: html`<svg
130
+ viewBox="0 0 24 24"
131
+ fill="none"
132
+ stroke="currentColor"
133
+ stroke-width="2"
134
+ stroke-linecap="round"
135
+ stroke-linejoin="round"
136
+ >
137
+ <line x1="18" y1="6" x2="6" y2="18" />
138
+ <line x1="6" y1="6" x2="18" y2="18" />
139
+ </svg>`,
140
+
141
+ /* ── Hardware / Tech ── */
142
+ cpu: html`<svg
143
+ viewBox="0 0 24 24"
144
+ fill="none"
145
+ stroke="currentColor"
146
+ stroke-width="2"
147
+ stroke-linecap="round"
148
+ stroke-linejoin="round"
149
+ >
150
+ <rect x="4" y="4" width="16" height="16" rx="2" ry="2" />
151
+ <rect x="9" y="9" width="6" height="6" />
152
+ <line x1="9" y1="1" x2="9" y2="4" />
153
+ <line x1="15" y1="1" x2="15" y2="4" />
154
+ <line x1="9" y1="20" x2="9" y2="23" />
155
+ <line x1="15" y1="20" x2="15" y2="23" />
156
+ <line x1="20" y1="9" x2="23" y2="9" />
157
+ <line x1="20" y1="14" x2="23" y2="14" />
158
+ <line x1="1" y1="9" x2="4" y2="9" />
159
+ <line x1="1" y1="14" x2="4" y2="14" />
160
+ </svg>`,
161
+
162
+ server: html`<svg
163
+ viewBox="0 0 24 24"
164
+ fill="none"
165
+ stroke="currentColor"
166
+ stroke-width="2"
167
+ stroke-linecap="round"
168
+ stroke-linejoin="round"
169
+ >
170
+ <rect x="2" y="2" width="20" height="8" rx="2" ry="2" />
171
+ <rect x="2" y="14" width="20" height="8" rx="2" ry="2" />
172
+ <line x1="6" y1="6" x2="6.01" y2="6" />
173
+ <line x1="6" y1="18" x2="6.01" y2="18" />
174
+ </svg>`,
175
+
176
+ /* ── Controls ── */
177
+ sliders: html`<svg
178
+ viewBox="0 0 24 24"
179
+ fill="none"
180
+ stroke="currentColor"
181
+ stroke-width="2"
182
+ stroke-linecap="round"
183
+ stroke-linejoin="round"
184
+ >
185
+ <line x1="4" y1="21" x2="4" y2="14" />
186
+ <line x1="4" y1="10" x2="4" y2="3" />
187
+ <line x1="12" y1="21" x2="12" y2="12" />
188
+ <line x1="12" y1="8" x2="12" y2="3" />
189
+ <line x1="20" y1="21" x2="20" y2="16" />
190
+ <line x1="20" y1="12" x2="20" y2="3" />
191
+ <line x1="1" y1="14" x2="7" y2="14" />
192
+ <line x1="9" y1="8" x2="15" y2="8" />
193
+ <line x1="17" y1="16" x2="23" y2="16" />
194
+ </svg>`,
195
+
196
+ settings: html`<svg
197
+ viewBox="0 0 24 24"
198
+ fill="none"
199
+ stroke="currentColor"
200
+ stroke-width="2"
201
+ stroke-linecap="round"
202
+ stroke-linejoin="round"
203
+ >
204
+ <circle cx="12" cy="12" r="3" />
205
+ <path
206
+ d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1
207
+ 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33
208
+ 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65
209
+ 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83
210
+ 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0
211
+ 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0
212
+ 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0
213
+ 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0
214
+ 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1
215
+ 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0
216
+ 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0
217
+ 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"
218
+ />
219
+ </svg>`,
220
+
221
+ filter: html`<svg
222
+ viewBox="0 0 24 24"
223
+ fill="none"
224
+ stroke="currentColor"
225
+ stroke-width="2"
226
+ stroke-linecap="round"
227
+ stroke-linejoin="round"
228
+ >
229
+ <polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3" />
230
+ </svg>`,
231
+
232
+ /* ── Terminal / Code ── */
233
+ terminal: html`<svg
234
+ viewBox="0 0 24 24"
235
+ fill="none"
236
+ stroke="currentColor"
237
+ stroke-width="2"
238
+ stroke-linecap="round"
239
+ stroke-linejoin="round"
240
+ >
241
+ <polyline points="4 17 10 11 4 5" />
242
+ <line x1="12" y1="19" x2="20" y2="19" />
243
+ </svg>`,
244
+
245
+ git: html`<svg
246
+ viewBox="0 0 24 24"
247
+ fill="none"
248
+ stroke="currentColor"
249
+ stroke-width="2"
250
+ stroke-linecap="round"
251
+ stroke-linejoin="round"
252
+ >
253
+ <line x1="6" y1="3" x2="6" y2="15" />
254
+ <circle cx="18" cy="6" r="3" />
255
+ <circle cx="6" cy="18" r="3" />
256
+ <path d="M18 9a9 9 0 0 1-9 9" />
257
+ </svg>`,
258
+
259
+ /* ── Actions (small / inline icons) ── */
260
+ plus: html`<svg
261
+ viewBox="0 0 24 24"
262
+ fill="none"
263
+ stroke="currentColor"
264
+ stroke-width="2"
265
+ stroke-linecap="round"
266
+ stroke-linejoin="round"
267
+ >
268
+ <line x1="12" y1="5" x2="12" y2="19" />
269
+ <line x1="5" y1="12" x2="19" y2="12" />
270
+ </svg>`,
271
+
272
+ chevronDown: html`<svg
273
+ viewBox="0 0 24 24"
274
+ fill="none"
275
+ stroke="currentColor"
276
+ stroke-width="2"
277
+ stroke-linecap="round"
278
+ stroke-linejoin="round"
279
+ width="16"
280
+ height="16"
281
+ >
282
+ <polyline points="6 9 12 15 18 9" />
283
+ </svg>`,
284
+
285
+ send: html`<svg
286
+ viewBox="0 0 24 24"
287
+ fill="none"
288
+ stroke="currentColor"
289
+ stroke-width="2"
290
+ stroke-linecap="round"
291
+ stroke-linejoin="round"
292
+ width="16"
293
+ height="16"
294
+ >
295
+ <line x1="22" y1="2" x2="11" y2="13" />
296
+ <polygon points="22 2 15 22 11 13 2 9 22 2" />
297
+ </svg>`,
298
+
299
+ refresh: html`<svg
300
+ viewBox="0 0 24 24"
301
+ fill="none"
302
+ stroke="currentColor"
303
+ stroke-width="2"
304
+ stroke-linecap="round"
305
+ stroke-linejoin="round"
306
+ width="16"
307
+ height="16"
308
+ >
309
+ <polyline points="23 4 23 10 17 10" />
310
+ <path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10" />
311
+ </svg>`,
312
+
313
+ search: html`<svg
314
+ viewBox="0 0 24 24"
315
+ fill="none"
316
+ stroke="currentColor"
317
+ stroke-width="2"
318
+ stroke-linecap="round"
319
+ stroke-linejoin="round"
320
+ width="16"
321
+ height="16"
322
+ >
323
+ <circle cx="11" cy="11" r="8" />
324
+ <line x1="21" y1="21" x2="16.65" y2="16.65" />
325
+ </svg>`,
326
+
327
+ copy: html`<svg
328
+ viewBox="0 0 24 24"
329
+ fill="none"
330
+ stroke="currentColor"
331
+ stroke-width="2"
332
+ stroke-linecap="round"
333
+ stroke-linejoin="round"
334
+ width="16"
335
+ height="16"
336
+ >
337
+ <rect x="9" y="9" width="13" height="13" rx="2" ry="2" />
338
+ <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
339
+ </svg>`,
340
+
341
+ trash: html`<svg
342
+ viewBox="0 0 24 24"
343
+ fill="none"
344
+ stroke="currentColor"
345
+ stroke-width="2"
346
+ stroke-linecap="round"
347
+ stroke-linejoin="round"
348
+ width="16"
349
+ height="16"
350
+ >
351
+ <polyline points="3 6 5 6 21 6" />
352
+ <path
353
+ d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"
354
+ />
355
+ <line x1="10" y1="11" x2="10" y2="17" />
356
+ <line x1="14" y1="11" x2="14" y2="17" />
357
+ </svg>`,
358
+
359
+ edit: html`<svg
360
+ viewBox="0 0 24 24"
361
+ fill="none"
362
+ stroke="currentColor"
363
+ stroke-width="2"
364
+ stroke-linecap="round"
365
+ stroke-linejoin="round"
366
+ width="16"
367
+ height="16"
368
+ >
369
+ <path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" />
370
+ <path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z" />
371
+ </svg>`,
372
+
373
+ eye: html`<svg
374
+ viewBox="0 0 24 24"
375
+ fill="none"
376
+ stroke="currentColor"
377
+ stroke-width="2"
378
+ stroke-linecap="round"
379
+ stroke-linejoin="round"
380
+ width="16"
381
+ height="16"
382
+ >
383
+ <path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" />
384
+ <circle cx="12" cy="12" r="3" />
385
+ </svg>`,
386
+
387
+ link: html`<svg
388
+ viewBox="0 0 24 24"
389
+ fill="none"
390
+ stroke="currentColor"
391
+ stroke-width="2"
392
+ stroke-linecap="round"
393
+ stroke-linejoin="round"
394
+ width="16"
395
+ height="16"
396
+ >
397
+ <path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" />
398
+ <path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" />
399
+ </svg>`,
400
+
401
+ download: html`<svg
402
+ viewBox="0 0 24 24"
403
+ fill="none"
404
+ stroke="currentColor"
405
+ stroke-width="2"
406
+ stroke-linecap="round"
407
+ stroke-linejoin="round"
408
+ width="16"
409
+ height="16"
410
+ >
411
+ <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
412
+ <polyline points="7 10 12 15 17 10" />
413
+ <line x1="12" y1="15" x2="12" y2="3" />
414
+ </svg>`,
415
+
416
+ upload: html`<svg
417
+ viewBox="0 0 24 24"
418
+ fill="none"
419
+ stroke="currentColor"
420
+ stroke-width="2"
421
+ stroke-linecap="round"
422
+ stroke-linejoin="round"
423
+ width="16"
424
+ height="16"
425
+ >
426
+ <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
427
+ <polyline points="17 8 12 3 7 8" />
428
+ <line x1="12" y1="3" x2="12" y2="15" />
429
+ </svg>`,
430
+
431
+ users: html`<svg
432
+ viewBox="0 0 24 24"
433
+ fill="none"
434
+ stroke="currentColor"
435
+ stroke-width="2"
436
+ stroke-linecap="round"
437
+ stroke-linejoin="round"
438
+ width="16"
439
+ height="16"
440
+ >
441
+ <path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" />
442
+ <circle cx="9" cy="7" r="4" />
443
+ <path d="M23 21v-2a4 4 0 0 0-3-3.87" />
444
+ <path d="M16 3.13a4 4 0 0 1 0 7.75" />
445
+ </svg>`,
446
+
447
+ folder: html`<svg
448
+ viewBox="0 0 24 24"
449
+ fill="none"
450
+ stroke="currentColor"
451
+ stroke-width="2"
452
+ stroke-linecap="round"
453
+ stroke-linejoin="round"
454
+ width="16"
455
+ height="16"
456
+ >
457
+ <path
458
+ d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"
459
+ />
460
+ </svg>`,
461
+ };
@@ -0,0 +1,81 @@
1
+ /* ─────────────────────────────────────────────────────────────
2
+ * VirtEngine Control Center – Router / Tab Navigation
3
+ * Manages active tab, history stack, and Telegram BackButton
4
+ * ────────────────────────────────────────────────────────────── */
5
+
6
+ import { signal } from "@preact/signals";
7
+ import { haptic, showBackButton, hideBackButton } from "./telegram.js";
8
+ import { refreshTab } from "./state.js";
9
+
10
+ /** Currently active tab ID */
11
+ export const activeTab = signal("dashboard");
12
+
13
+ /** Navigation history stack (for back button) */
14
+ const tabHistory = [];
15
+
16
+ /**
17
+ * Navigate to a new tab. Pushes current tab onto the history stack
18
+ * and refreshes data for the target tab.
19
+ * @param {string} tab
20
+ * @param {{ resetHistory?: boolean, forceRefresh?: boolean }} [opts]
21
+ */
22
+ export function navigateTo(tab, opts = {}) {
23
+ const { resetHistory = false, forceRefresh = false } = opts;
24
+ const goingHome = tab === "dashboard";
25
+ const shouldReset = resetHistory || goingHome;
26
+
27
+ if (tab === activeTab.value) {
28
+ if (forceRefresh) refreshTab(tab, { force: true });
29
+ if (shouldReset) {
30
+ tabHistory.length = 0;
31
+ hideBackButton();
32
+ }
33
+ return;
34
+ }
35
+
36
+ haptic("light");
37
+ if (shouldReset) {
38
+ tabHistory.length = 0;
39
+ } else {
40
+ tabHistory.push(activeTab.value);
41
+ }
42
+ activeTab.value = tab;
43
+ refreshTab(tab, forceRefresh ? { force: true } : undefined);
44
+
45
+ // Show Telegram BackButton when there is history
46
+ if (tabHistory.length > 0) {
47
+ showBackButton(goBack);
48
+ } else {
49
+ hideBackButton();
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Go back to the previous tab (from history stack).
55
+ */
56
+ export function goBack() {
57
+ const prev = tabHistory.pop();
58
+ if (prev) {
59
+ haptic("light");
60
+ activeTab.value = prev;
61
+ refreshTab(prev);
62
+ }
63
+ if (tabHistory.length === 0) {
64
+ hideBackButton();
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Ordered list of tabs with metadata for rendering the navigation UI.
70
+ * The `icon` key maps to a property on the ICONS object in modules/icons.js.
71
+ */
72
+ export const TAB_CONFIG = [
73
+ { id: "dashboard", label: "Home", icon: "grid" },
74
+ { id: "tasks", label: "Tasks", icon: "check" },
75
+ { id: "chat", label: "Chat", icon: "chat" },
76
+ { id: "agents", label: "Agents", icon: "cpu" },
77
+ { id: "infra", label: "Infra", icon: "server" },
78
+ { id: "control", label: "Control", icon: "sliders" },
79
+ { id: "logs", label: "Logs", icon: "terminal" },
80
+ { id: "settings", label: "Settings", icon: "settings" },
81
+ ];