leedab 0.1.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 (71) hide show
  1. package/LICENSE +6 -0
  2. package/README.md +85 -0
  3. package/bin/leedab.js +626 -0
  4. package/dist/analytics.d.ts +20 -0
  5. package/dist/analytics.js +57 -0
  6. package/dist/audit.d.ts +15 -0
  7. package/dist/audit.js +46 -0
  8. package/dist/brand.d.ts +9 -0
  9. package/dist/brand.js +57 -0
  10. package/dist/channels/index.d.ts +5 -0
  11. package/dist/channels/index.js +47 -0
  12. package/dist/config/index.d.ts +10 -0
  13. package/dist/config/index.js +49 -0
  14. package/dist/config/schema.d.ts +58 -0
  15. package/dist/config/schema.js +21 -0
  16. package/dist/dashboard/routes.d.ts +5 -0
  17. package/dist/dashboard/routes.js +410 -0
  18. package/dist/dashboard/server.d.ts +2 -0
  19. package/dist/dashboard/server.js +80 -0
  20. package/dist/dashboard/static/app.js +351 -0
  21. package/dist/dashboard/static/console.html +252 -0
  22. package/dist/dashboard/static/favicon.png +0 -0
  23. package/dist/dashboard/static/index.html +815 -0
  24. package/dist/dashboard/static/logo-dark.png +0 -0
  25. package/dist/dashboard/static/logo-light.png +0 -0
  26. package/dist/dashboard/static/sessions.html +182 -0
  27. package/dist/dashboard/static/settings.html +274 -0
  28. package/dist/dashboard/static/style.css +493 -0
  29. package/dist/dashboard/static/team.html +215 -0
  30. package/dist/gateway.d.ts +8 -0
  31. package/dist/gateway.js +213 -0
  32. package/dist/index.d.ts +6 -0
  33. package/dist/index.js +5 -0
  34. package/dist/license.d.ts +27 -0
  35. package/dist/license.js +92 -0
  36. package/dist/memory/index.d.ts +9 -0
  37. package/dist/memory/index.js +41 -0
  38. package/dist/onboard/index.d.ts +4 -0
  39. package/dist/onboard/index.js +263 -0
  40. package/dist/onboard/oauth-server.d.ts +13 -0
  41. package/dist/onboard/oauth-server.js +73 -0
  42. package/dist/onboard/steps/google.d.ts +12 -0
  43. package/dist/onboard/steps/google.js +178 -0
  44. package/dist/onboard/steps/provider.d.ts +10 -0
  45. package/dist/onboard/steps/provider.js +292 -0
  46. package/dist/onboard/steps/teams.d.ts +5 -0
  47. package/dist/onboard/steps/teams.js +51 -0
  48. package/dist/onboard/steps/telegram.d.ts +6 -0
  49. package/dist/onboard/steps/telegram.js +88 -0
  50. package/dist/onboard/steps/welcome.d.ts +1 -0
  51. package/dist/onboard/steps/welcome.js +10 -0
  52. package/dist/onboard/steps/whatsapp.d.ts +2 -0
  53. package/dist/onboard/steps/whatsapp.js +76 -0
  54. package/dist/openclaw.d.ts +9 -0
  55. package/dist/openclaw.js +20 -0
  56. package/dist/team.d.ts +13 -0
  57. package/dist/team.js +49 -0
  58. package/dist/templates/verticals/supply-chain/HEARTBEAT.md +12 -0
  59. package/dist/templates/verticals/supply-chain/SOUL.md +49 -0
  60. package/dist/templates/verticals/supply-chain/WORKFLOWS.md +148 -0
  61. package/dist/templates/verticals/supply-chain/vault-template.json +18 -0
  62. package/dist/templates/workspace/AGENTS.md +181 -0
  63. package/dist/templates/workspace/BOOTSTRAP.md +32 -0
  64. package/dist/templates/workspace/HEARTBEAT.md +9 -0
  65. package/dist/templates/workspace/IDENTITY.md +14 -0
  66. package/dist/templates/workspace/SOUL.md +32 -0
  67. package/dist/templates/workspace/TOOLS.md +40 -0
  68. package/dist/templates/workspace/USER.md +26 -0
  69. package/dist/vault.d.ts +24 -0
  70. package/dist/vault.js +123 -0
  71. package/package.json +58 -0
@@ -0,0 +1,493 @@
1
+ * {
2
+ margin: 0;
3
+ padding: 0;
4
+ box-sizing: border-box;
5
+ }
6
+
7
+ :root {
8
+ --bg: #0a0a0b;
9
+ --surface: #141416;
10
+ --surface-raised: #18181b;
11
+ --border: #232328;
12
+ --border-hover: #333338;
13
+ --text: #e4e4e7;
14
+ --text-secondary: #a1a1aa;
15
+ --text-dim: #71717a;
16
+ --text-faint: #52525b;
17
+ --accent: #AE5630;
18
+ --accent-hover: #c4633a;
19
+ --accent-soft: rgba(174, 86, 48, 0.08);
20
+ --accent-medium: rgba(174, 86, 48, 0.15);
21
+ --success: #22c55e;
22
+ --warning: #f59e0b;
23
+ --error: #ef4444;
24
+ --radius: 12px;
25
+ --radius-sm: 8px;
26
+ }
27
+
28
+ [data-theme="light"] {
29
+ --bg: #f8f8fa;
30
+ --surface: #ffffff;
31
+ --surface-raised: #f0f0f3;
32
+ --border: #d4d4d8;
33
+ --border-hover: #a1a1aa;
34
+ --text: #18181b;
35
+ --text-secondary: #3f3f46;
36
+ --text-dim: #52525b;
37
+ --text-faint: #71717a;
38
+ --accent: #9a4528;
39
+ --accent-hover: #AE5630;
40
+ --accent-soft: rgba(174, 86, 48, 0.06);
41
+ --accent-medium: rgba(174, 86, 48, 0.12);
42
+ --success: #16a34a;
43
+ --warning: #d97706;
44
+ --error: #dc2626;
45
+ }
46
+
47
+ body {
48
+ font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", system-ui, sans-serif;
49
+ background: var(--bg);
50
+ color: var(--text);
51
+ min-height: 100vh;
52
+ line-height: 1.5;
53
+ -webkit-font-smoothing: antialiased;
54
+ -moz-osx-font-smoothing: grayscale;
55
+ }
56
+
57
+ .container {
58
+ max-width: 720px;
59
+ margin: 0 auto;
60
+ padding: 48px 24px;
61
+ }
62
+
63
+ header {
64
+ margin-bottom: 48px;
65
+ }
66
+
67
+ header h1 {
68
+ font-size: 2rem;
69
+ font-weight: 700;
70
+ letter-spacing: -0.02em;
71
+ }
72
+
73
+ .subtitle {
74
+ color: var(--text-dim);
75
+ margin-top: 4px;
76
+ }
77
+
78
+ /* Sections */
79
+ .section {
80
+ margin-bottom: 40px;
81
+ }
82
+
83
+ .section-title {
84
+ font-size: 1rem;
85
+ font-weight: 600;
86
+ margin-bottom: 4px;
87
+ text-transform: uppercase;
88
+ letter-spacing: 0.05em;
89
+ color: var(--text-secondary);
90
+ font-size: 0.75rem;
91
+ }
92
+
93
+ .section-desc {
94
+ color: var(--text-dim);
95
+ font-size: 0.875rem;
96
+ margin-bottom: 16px;
97
+ }
98
+
99
+ /* Info grid */
100
+ .info-grid {
101
+ display: flex;
102
+ gap: 24px;
103
+ flex-wrap: wrap;
104
+ }
105
+
106
+ .info-item {
107
+ display: flex;
108
+ flex-direction: column;
109
+ gap: 2px;
110
+ }
111
+
112
+ .info-label {
113
+ font-size: 0.75rem;
114
+ color: var(--text-faint);
115
+ text-transform: uppercase;
116
+ letter-spacing: 0.05em;
117
+ }
118
+
119
+ .info-value {
120
+ font-size: 0.9rem;
121
+ color: var(--text);
122
+ font-weight: 500;
123
+ }
124
+
125
+ .info-value code {
126
+ background: var(--surface-raised);
127
+ padding: 2px 8px;
128
+ border-radius: 4px;
129
+ font-family: "SF Mono", "Fira Code", monospace;
130
+ font-size: 0.85rem;
131
+ }
132
+
133
+ /* Badge */
134
+ .badge {
135
+ display: inline-flex;
136
+ align-items: center;
137
+ gap: 6px;
138
+ padding: 3px 10px;
139
+ border-radius: 20px;
140
+ font-size: 12px;
141
+ font-weight: 500;
142
+ }
143
+
144
+ .badge-dot {
145
+ width: 6px;
146
+ height: 6px;
147
+ border-radius: 50%;
148
+ }
149
+
150
+ .badge-online {
151
+ background: rgba(34, 197, 94, 0.1);
152
+ color: var(--success);
153
+ }
154
+
155
+ .badge-online .badge-dot {
156
+ background: var(--success);
157
+ }
158
+
159
+ .badge-offline {
160
+ background: rgba(239, 68, 68, 0.1);
161
+ color: var(--error);
162
+ }
163
+
164
+ .badge-offline .badge-dot {
165
+ background: var(--error);
166
+ }
167
+
168
+ /* Cards */
169
+ .cards {
170
+ display: grid;
171
+ grid-template-columns: 1fr 1fr;
172
+ gap: 12px;
173
+ }
174
+
175
+ @media (max-width: 600px) {
176
+ .cards {
177
+ grid-template-columns: 1fr;
178
+ }
179
+ }
180
+
181
+ .card {
182
+ display: flex;
183
+ align-items: flex-start;
184
+ gap: 12px;
185
+ background: var(--surface);
186
+ border: 1px solid var(--border);
187
+ border-radius: var(--radius);
188
+ padding: 16px;
189
+ transition: border-color 0.15s;
190
+ flex-wrap: wrap;
191
+ }
192
+
193
+ .card .btn {
194
+ margin-top: 4px;
195
+ width: 100%;
196
+ text-align: center;
197
+ }
198
+
199
+ .card:hover {
200
+ border-color: var(--border-hover);
201
+ }
202
+
203
+ .card.connected {
204
+ border-color: color-mix(in srgb, var(--success) 40%, transparent);
205
+ }
206
+
207
+ .card-icon {
208
+ flex-shrink: 0;
209
+ width: 48px;
210
+ height: 48px;
211
+ display: flex;
212
+ align-items: center;
213
+ justify-content: center;
214
+ background: var(--accent-soft);
215
+ border-radius: 12px;
216
+ color: var(--accent);
217
+ }
218
+
219
+ .connected .card-icon {
220
+ background: color-mix(in srgb, var(--success) 10%, transparent);
221
+ color: var(--success);
222
+ }
223
+
224
+ .card-body {
225
+ flex: 1;
226
+ min-width: 0;
227
+ }
228
+
229
+ .card-body h3 {
230
+ font-size: 1rem;
231
+ font-weight: 600;
232
+ }
233
+
234
+ .card-body p {
235
+ color: var(--text-dim);
236
+ font-size: 0.875rem;
237
+ margin-top: 2px;
238
+ }
239
+
240
+ .card-status {
241
+ display: flex;
242
+ align-items: center;
243
+ gap: 6px;
244
+ font-size: 0.8rem;
245
+ color: var(--text-dim);
246
+ margin-top: 6px;
247
+ }
248
+
249
+ .dot {
250
+ width: 8px;
251
+ height: 8px;
252
+ border-radius: 50%;
253
+ background: var(--border);
254
+ display: inline-block;
255
+ }
256
+
257
+ .connected .dot {
258
+ background: var(--success);
259
+ }
260
+
261
+ /* Buttons */
262
+ .btn {
263
+ background: var(--accent);
264
+ color: white;
265
+ border: none;
266
+ padding: 8px 20px;
267
+ border-radius: var(--radius-sm);
268
+ font-size: 0.875rem;
269
+ font-weight: 500;
270
+ cursor: pointer;
271
+ transition: background 0.15s;
272
+ white-space: nowrap;
273
+ flex-shrink: 0;
274
+ text-decoration: none;
275
+ }
276
+
277
+ .btn:hover {
278
+ background: var(--accent-hover);
279
+ }
280
+
281
+ .btn:disabled {
282
+ opacity: 0.5;
283
+ cursor: not-allowed;
284
+ }
285
+
286
+ .btn-ghost {
287
+ background: var(--surface-raised);
288
+ color: var(--text-secondary);
289
+ border: 1px solid var(--border-hover);
290
+ }
291
+
292
+ .btn-ghost:hover {
293
+ background: var(--accent-soft);
294
+ color: var(--text);
295
+ border-color: var(--accent);
296
+ }
297
+
298
+ .btn-connected {
299
+ background: color-mix(in srgb, var(--success) 15%, transparent);
300
+ color: var(--success);
301
+ cursor: default;
302
+ }
303
+
304
+ .btn-connected:hover {
305
+ background: color-mix(in srgb, var(--success) 15%, transparent);
306
+ }
307
+
308
+ .btn-danger {
309
+ background: transparent;
310
+ color: var(--error);
311
+ border: 1px solid color-mix(in srgb, var(--error) 30%, transparent);
312
+ padding: 4px 12px;
313
+ font-size: 0.8rem;
314
+ }
315
+
316
+ .btn-danger:hover {
317
+ background: color-mix(in srgb, var(--error) 10%, transparent);
318
+ }
319
+
320
+ /* Form panel */
321
+ .form-panel {
322
+ background: var(--surface);
323
+ border: 1px solid var(--border);
324
+ border-radius: var(--radius);
325
+ padding: 24px;
326
+ margin-top: 16px;
327
+ }
328
+
329
+ .form-panel h3 {
330
+ font-size: 1rem;
331
+ font-weight: 600;
332
+ margin-bottom: 4px;
333
+ }
334
+
335
+ .form-help {
336
+ color: var(--text-dim);
337
+ font-size: 0.85rem;
338
+ margin-bottom: 16px;
339
+ }
340
+
341
+ .form-help a {
342
+ color: var(--accent);
343
+ text-decoration: none;
344
+ }
345
+
346
+ .form-panel label {
347
+ display: block;
348
+ font-size: 0.85rem;
349
+ color: var(--text-dim);
350
+ margin-bottom: 12px;
351
+ }
352
+
353
+ .form-panel input,
354
+ .form-panel textarea {
355
+ display: block;
356
+ width: 100%;
357
+ margin-top: 4px;
358
+ padding: 10px 12px;
359
+ background: var(--bg);
360
+ border: 1px solid var(--border);
361
+ border-radius: 8px;
362
+ color: var(--text);
363
+ font-size: 0.9rem;
364
+ font-family: inherit;
365
+ }
366
+
367
+ .form-panel input:focus,
368
+ .form-panel textarea:focus {
369
+ outline: none;
370
+ border-color: var(--accent);
371
+ }
372
+
373
+ .form-panel textarea {
374
+ resize: vertical;
375
+ min-height: 60px;
376
+ }
377
+
378
+ .form-actions {
379
+ display: flex;
380
+ gap: 8px;
381
+ margin-top: 16px;
382
+ }
383
+
384
+ /* Vault table */
385
+ .vault-table {
386
+ width: 100%;
387
+ border-collapse: collapse;
388
+ margin-top: 16px;
389
+ }
390
+
391
+ .vault-table th,
392
+ .vault-table td {
393
+ text-align: left;
394
+ padding: 10px 12px;
395
+ font-size: 0.875rem;
396
+ border-bottom: 1px solid var(--border);
397
+ }
398
+
399
+ .vault-table th {
400
+ color: var(--text-faint);
401
+ font-weight: 500;
402
+ font-size: 0.75rem;
403
+ text-transform: uppercase;
404
+ letter-spacing: 0.05em;
405
+ }
406
+
407
+ .vault-table td {
408
+ color: var(--text);
409
+ }
410
+
411
+ .vault-empty {
412
+ color: var(--text-dim);
413
+ font-size: 0.875rem;
414
+ padding: 24px 0;
415
+ text-align: center;
416
+ }
417
+
418
+ /* QR area */
419
+ .qr-area {
420
+ background: white;
421
+ border-radius: 8px;
422
+ padding: 24px;
423
+ text-align: center;
424
+ margin: 16px 0;
425
+ color: #333;
426
+ min-height: 200px;
427
+ display: flex;
428
+ align-items: center;
429
+ justify-content: center;
430
+ }
431
+
432
+ /* Toast */
433
+ .toast {
434
+ position: fixed;
435
+ top: 24px;
436
+ right: 24px;
437
+ padding: 12px 20px;
438
+ border-radius: 8px;
439
+ font-size: 0.875rem;
440
+ font-weight: 500;
441
+ z-index: 100;
442
+ transition: opacity 0.3s, transform 0.3s;
443
+ }
444
+
445
+ .toast.success {
446
+ background: color-mix(in srgb, var(--success) 15%, var(--surface));
447
+ color: var(--success);
448
+ border: 1px solid color-mix(in srgb, var(--success) 30%, transparent);
449
+ }
450
+
451
+ .toast.error {
452
+ background: color-mix(in srgb, var(--error) 15%, var(--surface));
453
+ color: var(--error);
454
+ border: 1px solid color-mix(in srgb, var(--error) 30%, transparent);
455
+ }
456
+
457
+ .hidden {
458
+ display: none;
459
+ }
460
+
461
+ /* Theme toggle */
462
+ .theme-toggle {
463
+ background: var(--surface-raised);
464
+ border: 1px solid var(--border-hover);
465
+ border-radius: var(--radius-sm);
466
+ padding: 6px 10px;
467
+ cursor: pointer;
468
+ color: var(--text-secondary);
469
+ display: flex;
470
+ align-items: center;
471
+ gap: 6px;
472
+ font-size: 13px;
473
+ font-family: inherit;
474
+ transition: background 0.15s, border-color 0.15s, color 0.15s;
475
+ }
476
+
477
+ .theme-toggle:hover {
478
+ background: var(--accent-soft);
479
+ border-color: var(--accent);
480
+ color: var(--text);
481
+ }
482
+
483
+ .theme-toggle svg {
484
+ width: 16px;
485
+ height: 16px;
486
+ }
487
+
488
+ footer {
489
+ text-align: center;
490
+ margin-top: 48px;
491
+ color: var(--text-dim);
492
+ font-size: 0.8rem;
493
+ }
@@ -0,0 +1,215 @@
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
+ <link rel="icon" type="image/png" href="/favicon.png">
7
+ <title>LeedAB — Team</title>
8
+ <link rel="stylesheet" href="/style.css">
9
+ </head>
10
+ <body>
11
+ <style>
12
+ .page-header { display:flex; align-items:center; justify-content:space-between; padding:0 20px; height:52px; border-bottom:1px solid var(--border); background:var(--bg); }
13
+ .page-header-left { display:flex; align-items:center; gap:10px; }
14
+ .page-header-title { font-size:14px; font-weight:600; letter-spacing:-0.01em; }
15
+ .page-nav { display:flex; align-items:center; gap:2px; }
16
+ .page-nav a, .page-nav button { color:var(--text-dim); text-decoration:none; display:flex; align-items:center; gap:5px; padding:6px 12px; border-radius:8px; font-size:13px; font-weight:450; transition:all 0.15s; background:none; border:none; cursor:pointer; font-family:inherit; }
17
+ .page-nav a:hover, .page-nav button:hover { color:var(--text-secondary); background:var(--surface-raised); }
18
+ .page-nav .theme-btn { border:1px solid var(--border); padding:5px 8px; }
19
+ </style>
20
+
21
+ <div class="page-header">
22
+ <div class="page-header-left">
23
+ <span class="page-header-title">Team</span>
24
+ </div>
25
+ <div class="page-nav">
26
+ <a href="/">
27
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m22 2-7 20-4-9-9-4 20-7Z"/><path d="M22 2 11 13"/></svg>
28
+ Chat
29
+ </a>
30
+ <a href="/console.html">
31
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="20" height="14" rx="2" ry="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></svg>
32
+ Console
33
+ </a>
34
+ <button class="theme-btn" onclick="toggleTheme()" title="Toggle theme">
35
+ <svg id="theme-icon-sun" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg>
36
+ <svg id="theme-icon-moon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="display:none"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>
37
+ </button>
38
+ </div>
39
+ </div>
40
+
41
+ <div class="container">
42
+
43
+ <main>
44
+ <div id="toast" class="toast hidden"></div>
45
+
46
+ <section class="section">
47
+ <h2 class="section-title">Members</h2>
48
+ <div id="team-list"></div>
49
+ </section>
50
+
51
+ <section class="section">
52
+ <div class="form-panel">
53
+ <h3>Add member</h3>
54
+ <label>Name
55
+ <input type="text" id="member-name" placeholder="e.g. Alice">
56
+ </label>
57
+ <label>Email (optional)
58
+ <input type="email" id="member-email" placeholder="alice@company.com">
59
+ </label>
60
+ <label>Role
61
+ <select id="member-role" style="display:block;width:100%;margin-top:4px;padding:10px 12px;background:var(--bg);border:1px solid var(--border);border-radius:8px;color:var(--text);font-size:0.9rem;font-family:inherit">
62
+ <option value="operator">Operator — can use agent, view logs</option>
63
+ <option value="admin">Admin — full access</option>
64
+ <option value="viewer">Viewer — read-only</option>
65
+ </select>
66
+ </label>
67
+ <div class="form-actions">
68
+ <button class="btn" onclick="addTeamMember()">Add member</button>
69
+ </div>
70
+ </div>
71
+ </section>
72
+ </main>
73
+
74
+ <footer>
75
+ <p>Your files, credentials, and memory stay on this device.</p>
76
+ </footer>
77
+ </div>
78
+
79
+ <style>
80
+ .team-table {
81
+ width: 100%;
82
+ border-collapse: collapse;
83
+ }
84
+ .team-table th,
85
+ .team-table td {
86
+ text-align: left;
87
+ padding: 10px 12px;
88
+ font-size: 0.875rem;
89
+ border-bottom: 1px solid var(--border);
90
+ }
91
+ .team-table th {
92
+ color: var(--text-faint);
93
+ font-weight: 500;
94
+ font-size: 0.75rem;
95
+ text-transform: uppercase;
96
+ letter-spacing: 0.05em;
97
+ }
98
+ .role-badge {
99
+ display: inline-block;
100
+ padding: 2px 8px;
101
+ border-radius: 6px;
102
+ font-size: 11px;
103
+ font-weight: 500;
104
+ }
105
+ .role-admin { background: var(--accent-soft); color: var(--accent); }
106
+ .role-operator { background: rgba(34, 197, 94, 0.1); color: var(--success); }
107
+ .role-viewer { background: rgba(113, 113, 122, 0.1); color: var(--text-dim); }
108
+ </style>
109
+
110
+ <script>
111
+ function initTheme() {
112
+ const saved = localStorage.getItem("leedab-theme") || "dark";
113
+ document.documentElement.setAttribute("data-theme", saved);
114
+ updateThemeIcon(saved);
115
+ }
116
+ function toggleTheme() {
117
+ const current = document.documentElement.getAttribute("data-theme") || "dark";
118
+ const next = current === "dark" ? "light" : "dark";
119
+ document.documentElement.setAttribute("data-theme", next);
120
+ localStorage.setItem("leedab-theme", next);
121
+ updateThemeIcon(next);
122
+ }
123
+ function updateThemeIcon(theme) {
124
+ const sun = document.getElementById("theme-icon-sun");
125
+ const moon = document.getElementById("theme-icon-moon");
126
+ if (sun && moon) {
127
+ sun.style.display = theme === "dark" ? "block" : "none";
128
+ moon.style.display = theme === "light" ? "block" : "none";
129
+ }
130
+ }
131
+ initTheme();
132
+
133
+ document.addEventListener("DOMContentLoaded", loadTeam);
134
+
135
+ async function loadTeam() {
136
+ const container = document.getElementById("team-list");
137
+ try {
138
+ const res = await fetch("/api/team");
139
+ const team = await res.json();
140
+
141
+ if (!team.length) {
142
+ container.innerHTML = '<div class="vault-empty">No team members yet.</div>';
143
+ return;
144
+ }
145
+
146
+ container.innerHTML = `
147
+ <table class="team-table">
148
+ <thead>
149
+ <tr><th>Name</th><th>Email</th><th>Role</th><th>Added</th><th></th></tr>
150
+ </thead>
151
+ <tbody>
152
+ ${team.map(m => `
153
+ <tr>
154
+ <td>${esc(m.name)}</td>
155
+ <td>${m.email ? esc(m.email) : '<span style="color:var(--text-faint)">-</span>'}</td>
156
+ <td><span class="role-badge role-${m.role}">${m.role}</span></td>
157
+ <td style="color:var(--text-dim)">${new Date(m.createdAt).toLocaleDateString()}</td>
158
+ <td><button class="btn btn-danger" onclick="removeMember('${m.id}')">Remove</button></td>
159
+ </tr>
160
+ `).join("")}
161
+ </tbody>
162
+ </table>`;
163
+ } catch {
164
+ container.innerHTML = '<div class="vault-empty">Could not load team.</div>';
165
+ }
166
+ }
167
+
168
+ async function addTeamMember() {
169
+ const name = document.getElementById("member-name").value.trim();
170
+ const email = document.getElementById("member-email").value.trim();
171
+ const role = document.getElementById("member-role").value;
172
+
173
+ if (!name) {
174
+ showToast("Name is required", "error");
175
+ return;
176
+ }
177
+
178
+ try {
179
+ await fetch("/api/team", {
180
+ method: "POST",
181
+ headers: { "Content-Type": "application/json" },
182
+ body: JSON.stringify({ name, email, role }),
183
+ });
184
+ document.getElementById("member-name").value = "";
185
+ document.getElementById("member-email").value = "";
186
+ showToast(`Added ${name}`, "success");
187
+ loadTeam();
188
+ } catch {
189
+ showToast("Failed to add member", "error");
190
+ }
191
+ }
192
+
193
+ async function removeMember(id) {
194
+ try {
195
+ await fetch(`/api/team?id=${encodeURIComponent(id)}`, { method: "DELETE" });
196
+ showToast("Member removed", "success");
197
+ loadTeam();
198
+ } catch {
199
+ showToast("Failed to remove member", "error");
200
+ }
201
+ }
202
+
203
+ function showToast(message, type) {
204
+ const toast = document.getElementById("toast");
205
+ toast.textContent = message;
206
+ toast.className = `toast ${type}`;
207
+ setTimeout(() => toast.classList.add("hidden"), 4000);
208
+ }
209
+
210
+ function esc(str) {
211
+ return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
212
+ }
213
+ </script>
214
+ </body>
215
+ </html>
@@ -0,0 +1,8 @@
1
+ import type { LeedABConfig } from "./config/schema.js";
2
+ /**
3
+ * Start the LeedAB agent gateway.
4
+ */
5
+ export declare function startGateway(config: LeedABConfig): Promise<{
6
+ logPath: string;
7
+ }>;
8
+ export declare function stopGateway(): Promise<void>;