kyber-chat 1.0.0__py3-none-any.whl

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. kyber/__init__.py +6 -0
  2. kyber/__main__.py +8 -0
  3. kyber/agent/__init__.py +8 -0
  4. kyber/agent/context.py +224 -0
  5. kyber/agent/loop.py +687 -0
  6. kyber/agent/memory.py +109 -0
  7. kyber/agent/skills.py +244 -0
  8. kyber/agent/subagent.py +379 -0
  9. kyber/agent/tools/__init__.py +6 -0
  10. kyber/agent/tools/base.py +102 -0
  11. kyber/agent/tools/filesystem.py +191 -0
  12. kyber/agent/tools/message.py +86 -0
  13. kyber/agent/tools/registry.py +73 -0
  14. kyber/agent/tools/shell.py +141 -0
  15. kyber/agent/tools/spawn.py +65 -0
  16. kyber/agent/tools/task_status.py +53 -0
  17. kyber/agent/tools/web.py +163 -0
  18. kyber/bridge/package.json +26 -0
  19. kyber/bridge/src/index.ts +50 -0
  20. kyber/bridge/src/server.ts +104 -0
  21. kyber/bridge/src/types.d.ts +3 -0
  22. kyber/bridge/src/whatsapp.ts +185 -0
  23. kyber/bridge/tsconfig.json +16 -0
  24. kyber/bus/__init__.py +6 -0
  25. kyber/bus/events.py +37 -0
  26. kyber/bus/queue.py +81 -0
  27. kyber/channels/__init__.py +6 -0
  28. kyber/channels/base.py +121 -0
  29. kyber/channels/discord.py +304 -0
  30. kyber/channels/feishu.py +263 -0
  31. kyber/channels/manager.py +161 -0
  32. kyber/channels/telegram.py +302 -0
  33. kyber/channels/whatsapp.py +141 -0
  34. kyber/cli/__init__.py +1 -0
  35. kyber/cli/commands.py +736 -0
  36. kyber/config/__init__.py +6 -0
  37. kyber/config/loader.py +95 -0
  38. kyber/config/schema.py +205 -0
  39. kyber/cron/__init__.py +6 -0
  40. kyber/cron/service.py +346 -0
  41. kyber/cron/types.py +59 -0
  42. kyber/dashboard/__init__.py +5 -0
  43. kyber/dashboard/server.py +122 -0
  44. kyber/dashboard/static/app.js +458 -0
  45. kyber/dashboard/static/favicon.png +0 -0
  46. kyber/dashboard/static/index.html +107 -0
  47. kyber/dashboard/static/kyber_logo.png +0 -0
  48. kyber/dashboard/static/styles.css +608 -0
  49. kyber/heartbeat/__init__.py +5 -0
  50. kyber/heartbeat/service.py +130 -0
  51. kyber/providers/__init__.py +6 -0
  52. kyber/providers/base.py +69 -0
  53. kyber/providers/litellm_provider.py +227 -0
  54. kyber/providers/transcription.py +65 -0
  55. kyber/session/__init__.py +5 -0
  56. kyber/session/manager.py +202 -0
  57. kyber/skills/README.md +47 -0
  58. kyber/skills/github/SKILL.md +48 -0
  59. kyber/skills/skill-creator/SKILL.md +371 -0
  60. kyber/skills/summarize/SKILL.md +67 -0
  61. kyber/skills/tmux/SKILL.md +121 -0
  62. kyber/skills/tmux/scripts/find-sessions.sh +112 -0
  63. kyber/skills/tmux/scripts/wait-for-text.sh +83 -0
  64. kyber/skills/weather/SKILL.md +49 -0
  65. kyber/utils/__init__.py +5 -0
  66. kyber/utils/helpers.py +91 -0
  67. kyber_chat-1.0.0.dist-info/METADATA +35 -0
  68. kyber_chat-1.0.0.dist-info/RECORD +71 -0
  69. kyber_chat-1.0.0.dist-info/WHEEL +4 -0
  70. kyber_chat-1.0.0.dist-info/entry_points.txt +2 -0
  71. kyber_chat-1.0.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,107 @@
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" />
6
+ <title>Kyber Dashboard</title>
7
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
8
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
9
+ <link
10
+ href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap"
11
+ rel="stylesheet"
12
+ />
13
+ <link rel="stylesheet" href="/static/styles.css" />
14
+ <link rel="icon" type="image/png" href="/static/favicon.png" />
15
+ <script defer src="/static/app.js"></script>
16
+ </head>
17
+ <body>
18
+ <!-- Login Modal -->
19
+ <div id="loginModal" class="modal-overlay hidden">
20
+ <div class="modal-card">
21
+ <div class="modal-logo">
22
+ <img src="/static/kyber_logo.png" alt="Kyber" />
23
+ </div>
24
+ <h2>Kyber Dashboard</h2>
25
+ <p class="modal-desc">Enter your dashboard token to continue.</p>
26
+ <div class="input-group">
27
+ <input id="tokenInput" type="password" placeholder="Paste token here…" autocomplete="off" />
28
+ </div>
29
+ <button id="tokenSubmit" class="btn btn-primary btn-full">Unlock</button>
30
+ </div>
31
+ </div>
32
+
33
+ <!-- Main App -->
34
+ <div id="app" class="app">
35
+ <!-- Sidebar -->
36
+ <aside class="sidebar">
37
+ <div class="sidebar-top">
38
+ <div class="sidebar-brand">
39
+ <img src="/static/kyber_logo.png" alt="Kyber" class="sidebar-logo" />
40
+ </div>
41
+ <nav class="sidebar-nav" id="sidebarNav">
42
+ <button class="nav-item active" data-section="providers">
43
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M8 1L14.5 4.75V11.25L8 15L1.5 11.25V4.75L8 1Z" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/></svg>
44
+ Providers
45
+ </button>
46
+ <button class="nav-item" data-section="agents">
47
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none"><circle cx="8" cy="5" r="3" stroke="currentColor" stroke-width="1.5"/><path d="M2 14c0-3.3 2.7-6 6-6s6 2.7 6 6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
48
+ Agent
49
+ </button>
50
+ <button class="nav-item" data-section="channels">
51
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 4h12M2 8h12M2 12h12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
52
+ Channels
53
+ </button>
54
+ <button class="nav-item" data-section="tools">
55
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M9.5 1.5L14.5 6.5L6.5 14.5H1.5V9.5L9.5 1.5Z" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/></svg>
56
+ Tools
57
+ </button>
58
+ <button class="nav-item" data-section="gateway">
59
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="1.5" y="3" width="13" height="4" rx="1" stroke="currentColor" stroke-width="1.5"/><rect x="1.5" y="9" width="13" height="4" rx="1" stroke="currentColor" stroke-width="1.5"/><circle cx="4.5" cy="5" r="0.75" fill="currentColor"/><circle cx="4.5" cy="11" r="0.75" fill="currentColor"/></svg>
60
+ Gateway
61
+ </button>
62
+ <button class="nav-item" data-section="dashboard">
63
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="1.5" y="1.5" width="5" height="5" rx="1" stroke="currentColor" stroke-width="1.5"/><rect x="9.5" y="1.5" width="5" height="5" rx="1" stroke="currentColor" stroke-width="1.5"/><rect x="1.5" y="9.5" width="5" height="5" rx="1" stroke="currentColor" stroke-width="1.5"/><rect x="9.5" y="9.5" width="5" height="5" rx="1" stroke="currentColor" stroke-width="1.5"/></svg>
64
+ Dashboard
65
+ </button>
66
+ <div class="nav-divider"></div>
67
+ <button class="nav-item" data-section="json">
68
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M5 1H3C1.9 1 1 1.9 1 3v2M11 1h2c1.1 0 2 .9 2 2v2M5 15H3c-1.1 0-2-.9-2-2v-2M11 15h2c1.1 0 2-.9 2-2v-2" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
69
+ Raw JSON
70
+ </button>
71
+ </nav>
72
+ </div>
73
+ <div class="sidebar-bottom">
74
+ <div class="status-pill" id="statusPill">
75
+ <span class="status-dot"></span>
76
+ <span id="statusText">Connecting…</span>
77
+ </div>
78
+ </div>
79
+ </aside>
80
+
81
+ <!-- Content -->
82
+ <main class="content">
83
+ <header class="topbar">
84
+ <div class="topbar-left">
85
+ <h1 id="pageTitle">Providers</h1>
86
+ <p id="pageDesc" class="topbar-desc">Configure your LLM provider API keys and endpoints.</p>
87
+ </div>
88
+ <div class="topbar-actions">
89
+ <span id="savedAt" class="saved-label"></span>
90
+ <button id="refreshBtn" class="btn btn-ghost">
91
+ <svg width="14" height="14" viewBox="0 0 16 16" fill="none"><path d="M1 8a7 7 0 0113.1-3.5M15 8A7 7 0 011.9 11.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><path d="M14 1v4h-4M2 15v-4h4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
92
+ Refresh
93
+ </button>
94
+ <button id="saveBtn" class="btn btn-primary">Save Changes</button>
95
+ </div>
96
+ </header>
97
+
98
+ <div class="content-body" id="contentBody">
99
+ <!-- Sections rendered by JS -->
100
+ </div>
101
+ </main>
102
+ </div>
103
+
104
+ <!-- Toast -->
105
+ <div id="toast" class="toast hidden"></div>
106
+ </body>
107
+ </html>
Binary file
@@ -0,0 +1,608 @@
1
+ /* ── Reset & Tokens ── */
2
+ :root {
3
+ color-scheme: dark;
4
+ --bg: #0a0a0a;
5
+ --bg-elevated: #111111;
6
+ --bg-card: #171717;
7
+ --bg-input: #0a0a0a;
8
+ --border: #262626;
9
+ --border-hover: #404040;
10
+ --text: #ededed;
11
+ --text-secondary: #888888;
12
+ --text-tertiary: #666666;
13
+ --accent: #ffffff;
14
+ --blue: #3b82f6;
15
+ --green: #22c55e;
16
+ --red: #ef4444;
17
+ --amber: #f59e0b;
18
+ --font: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
19
+ --mono: 'JetBrains Mono', 'SF Mono', 'Fira Code', monospace;
20
+ --radius: 8px;
21
+ --radius-lg: 12px;
22
+ --sidebar-w: 240px;
23
+ --transition: 150ms cubic-bezier(0.4, 0, 0.2, 1);
24
+ }
25
+
26
+ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
27
+
28
+ body {
29
+ font-family: var(--font);
30
+ background: var(--bg);
31
+ color: var(--text);
32
+ min-height: 100vh;
33
+ -webkit-font-smoothing: antialiased;
34
+ -moz-osx-font-smoothing: grayscale;
35
+ }
36
+
37
+ /* ── App Layout ── */
38
+ .app {
39
+ display: flex;
40
+ min-height: 100vh;
41
+ }
42
+
43
+ /* ── Sidebar ── */
44
+ .sidebar {
45
+ width: var(--sidebar-w);
46
+ background: var(--bg);
47
+ border-right: 1px solid var(--border);
48
+ display: flex;
49
+ flex-direction: column;
50
+ justify-content: space-between;
51
+ position: fixed;
52
+ top: 0;
53
+ left: 0;
54
+ bottom: 0;
55
+ z-index: 10;
56
+ overflow-y: auto;
57
+ }
58
+
59
+ .sidebar-top { padding: 20px 12px 12px; }
60
+
61
+ .sidebar-brand {
62
+ display: flex;
63
+ align-items: center;
64
+ gap: 10px;
65
+ padding: 0 8px 20px;
66
+ }
67
+
68
+ .sidebar-logo {
69
+ height: 36px;
70
+ width: auto;
71
+ object-fit: contain;
72
+ }
73
+
74
+ .sidebar-title {
75
+ font-size: 15px;
76
+ font-weight: 700;
77
+ letter-spacing: -0.02em;
78
+ }
79
+
80
+ .sidebar-nav {
81
+ display: flex;
82
+ flex-direction: column;
83
+ gap: 2px;
84
+ }
85
+
86
+ .nav-item {
87
+ display: flex;
88
+ align-items: center;
89
+ gap: 10px;
90
+ width: 100%;
91
+ padding: 8px 12px;
92
+ border: none;
93
+ background: transparent;
94
+ color: var(--text-secondary);
95
+ font-family: var(--font);
96
+ font-size: 13px;
97
+ font-weight: 500;
98
+ border-radius: 6px;
99
+ cursor: pointer;
100
+ transition: all var(--transition);
101
+ text-align: left;
102
+ }
103
+
104
+ .nav-item:hover {
105
+ color: var(--text);
106
+ background: var(--bg-elevated);
107
+ }
108
+
109
+ .nav-item.active {
110
+ color: var(--text);
111
+ background: var(--bg-card);
112
+ }
113
+
114
+ .nav-item svg {
115
+ flex-shrink: 0;
116
+ opacity: 0.7;
117
+ }
118
+
119
+ .nav-item.active svg { opacity: 1; }
120
+
121
+ .nav-divider {
122
+ height: 1px;
123
+ background: var(--border);
124
+ margin: 8px 12px;
125
+ }
126
+
127
+ .sidebar-bottom {
128
+ padding: 12px;
129
+ border-top: 1px solid var(--border);
130
+ }
131
+
132
+ .status-pill {
133
+ display: flex;
134
+ align-items: center;
135
+ gap: 8px;
136
+ padding: 8px 12px;
137
+ font-size: 12px;
138
+ color: var(--text-secondary);
139
+ border-radius: 6px;
140
+ background: var(--bg-elevated);
141
+ }
142
+
143
+ .status-dot {
144
+ width: 6px;
145
+ height: 6px;
146
+ border-radius: 50%;
147
+ background: var(--text-tertiary);
148
+ flex-shrink: 0;
149
+ }
150
+
151
+ .status-pill.connected .status-dot { background: var(--green); }
152
+ .status-pill.error .status-dot { background: var(--red); }
153
+
154
+ /* ── Content ── */
155
+ .content {
156
+ flex: 1;
157
+ margin-left: var(--sidebar-w);
158
+ display: flex;
159
+ flex-direction: column;
160
+ min-height: 100vh;
161
+ }
162
+
163
+ .topbar {
164
+ display: flex;
165
+ align-items: center;
166
+ justify-content: space-between;
167
+ padding: 20px 32px;
168
+ border-bottom: 1px solid var(--border);
169
+ background: var(--bg);
170
+ position: sticky;
171
+ top: 0;
172
+ z-index: 5;
173
+ gap: 16px;
174
+ }
175
+
176
+ .topbar h1 {
177
+ font-size: 18px;
178
+ font-weight: 600;
179
+ letter-spacing: -0.02em;
180
+ }
181
+
182
+ .topbar-desc {
183
+ font-size: 13px;
184
+ color: var(--text-secondary);
185
+ margin-top: 2px;
186
+ }
187
+
188
+ .topbar-actions {
189
+ display: flex;
190
+ align-items: center;
191
+ gap: 10px;
192
+ flex-shrink: 0;
193
+ }
194
+
195
+ .saved-label {
196
+ font-size: 12px;
197
+ color: var(--text-tertiary);
198
+ }
199
+
200
+ .content-body {
201
+ flex: 1;
202
+ padding: 24px 32px 48px;
203
+ max-width: 800px;
204
+ }
205
+
206
+ /* ── Cards ── */
207
+ .card {
208
+ background: var(--bg-card);
209
+ border: 1px solid var(--border);
210
+ border-radius: var(--radius-lg);
211
+ overflow: hidden;
212
+ }
213
+
214
+ .card + .card { margin-top: 16px; }
215
+
216
+ .card-header {
217
+ display: flex;
218
+ align-items: center;
219
+ justify-content: space-between;
220
+ padding: 16px 20px;
221
+ border-bottom: 1px solid var(--border);
222
+ }
223
+
224
+ .card-title {
225
+ font-size: 14px;
226
+ font-weight: 600;
227
+ letter-spacing: -0.01em;
228
+ }
229
+
230
+ .card-badge {
231
+ font-size: 11px;
232
+ font-weight: 500;
233
+ padding: 2px 8px;
234
+ border-radius: 999px;
235
+ background: var(--bg-elevated);
236
+ color: var(--text-secondary);
237
+ border: 1px solid var(--border);
238
+ }
239
+
240
+ .card-badge.on {
241
+ background: rgba(34, 197, 94, 0.1);
242
+ color: var(--green);
243
+ border-color: rgba(34, 197, 94, 0.2);
244
+ }
245
+
246
+ .card-body { padding: 16px 20px; }
247
+
248
+ /* ── Form Fields ── */
249
+ .field-row {
250
+ display: flex;
251
+ align-items: center;
252
+ gap: 16px;
253
+ padding: 10px 0;
254
+ }
255
+
256
+ .field-row + .field-row {
257
+ border-top: 1px solid var(--border);
258
+ }
259
+
260
+ .field-label {
261
+ width: 160px;
262
+ flex-shrink: 0;
263
+ font-size: 13px;
264
+ font-weight: 500;
265
+ color: var(--text-secondary);
266
+ }
267
+
268
+ .field-input {
269
+ flex: 1;
270
+ min-width: 0;
271
+ }
272
+
273
+ .field-input input,
274
+ .field-input textarea,
275
+ .field-input select {
276
+ width: 100%;
277
+ padding: 8px 12px;
278
+ font-family: var(--font);
279
+ font-size: 13px;
280
+ color: var(--text);
281
+ background: var(--bg-input);
282
+ border: 1px solid var(--border);
283
+ border-radius: var(--radius);
284
+ outline: none;
285
+ transition: border-color var(--transition);
286
+ }
287
+
288
+ .field-input input[type="checkbox"] {
289
+ width: 18px;
290
+ padding: 0;
291
+ }
292
+
293
+ .field-input input:focus,
294
+ .field-input textarea:focus {
295
+ border-color: var(--text-tertiary);
296
+ }
297
+
298
+ .field-input input[type="password"] {
299
+ font-family: var(--mono);
300
+ font-size: 12px;
301
+ letter-spacing: 0.05em;
302
+ }
303
+
304
+ .field-input input[type="number"] {
305
+ font-family: var(--mono);
306
+ font-size: 13px;
307
+ }
308
+
309
+ .field-input textarea {
310
+ min-height: 80px;
311
+ resize: vertical;
312
+ font-family: var(--mono);
313
+ font-size: 12px;
314
+ line-height: 1.6;
315
+ }
316
+
317
+ /* Checkbox */
318
+ .checkbox-wrap {
319
+ display: flex;
320
+ align-items: center;
321
+ gap: 8px;
322
+ }
323
+
324
+ .checkbox-wrap input[type="checkbox"] {
325
+ appearance: none;
326
+ -webkit-appearance: none;
327
+ width: 18px;
328
+ height: 18px;
329
+ border: 2px solid var(--border-hover);
330
+ border-radius: 4px;
331
+ background: var(--bg-input);
332
+ cursor: pointer;
333
+ position: relative;
334
+ flex-shrink: 0;
335
+ transition: all var(--transition);
336
+ }
337
+
338
+ .checkbox-wrap input[type="checkbox"]:checked {
339
+ background: var(--accent);
340
+ border-color: var(--accent);
341
+ }
342
+
343
+ .checkbox-wrap input[type="checkbox"]:checked::after {
344
+ content: '';
345
+ position: absolute;
346
+ left: 4px;
347
+ top: 1px;
348
+ width: 5px;
349
+ height: 9px;
350
+ border: solid var(--bg);
351
+ border-width: 0 2px 2px 0;
352
+ transform: rotate(45deg);
353
+ }
354
+
355
+ .checkbox-label {
356
+ font-size: 13px;
357
+ color: var(--text-secondary);
358
+ cursor: pointer;
359
+ user-select: none;
360
+ }
361
+
362
+ /* Array fields */
363
+ .array-field { display: flex; flex-direction: column; gap: 6px; }
364
+
365
+ .array-row {
366
+ display: flex;
367
+ gap: 6px;
368
+ align-items: center;
369
+ }
370
+
371
+ .array-row input { flex: 1; }
372
+
373
+ .btn-icon {
374
+ display: inline-flex;
375
+ align-items: center;
376
+ justify-content: center;
377
+ width: 32px;
378
+ height: 32px;
379
+ border: 1px solid var(--border);
380
+ background: transparent;
381
+ color: var(--text-secondary);
382
+ border-radius: var(--radius);
383
+ cursor: pointer;
384
+ transition: all var(--transition);
385
+ flex-shrink: 0;
386
+ }
387
+
388
+ .btn-icon:hover {
389
+ color: var(--text);
390
+ border-color: var(--border-hover);
391
+ background: var(--bg-elevated);
392
+ }
393
+
394
+ .btn-icon.danger:hover {
395
+ color: var(--red);
396
+ border-color: rgba(239, 68, 68, 0.3);
397
+ background: rgba(239, 68, 68, 0.08);
398
+ }
399
+
400
+ .btn-add {
401
+ display: inline-flex;
402
+ align-items: center;
403
+ gap: 4px;
404
+ padding: 4px 10px;
405
+ font-size: 12px;
406
+ font-weight: 500;
407
+ color: var(--text-secondary);
408
+ background: transparent;
409
+ border: 1px dashed var(--border);
410
+ border-radius: var(--radius);
411
+ cursor: pointer;
412
+ font-family: var(--font);
413
+ transition: all var(--transition);
414
+ }
415
+
416
+ .btn-add:hover {
417
+ color: var(--text);
418
+ border-color: var(--border-hover);
419
+ }
420
+
421
+ /* ── Buttons ── */
422
+ .btn {
423
+ display: inline-flex;
424
+ align-items: center;
425
+ gap: 6px;
426
+ padding: 8px 16px;
427
+ font-family: var(--font);
428
+ font-size: 13px;
429
+ font-weight: 500;
430
+ border-radius: var(--radius);
431
+ border: none;
432
+ cursor: pointer;
433
+ transition: all var(--transition);
434
+ white-space: nowrap;
435
+ }
436
+
437
+ .btn-primary {
438
+ background: var(--accent);
439
+ color: var(--bg);
440
+ }
441
+
442
+ .btn-primary:hover { opacity: 0.9; }
443
+
444
+ .btn-primary:disabled,
445
+ .btn-primary.disabled {
446
+ background: var(--border);
447
+ color: var(--text-tertiary);
448
+ cursor: default;
449
+ opacity: 1;
450
+ }
451
+
452
+ .btn-primary:disabled:hover,
453
+ .btn-primary.disabled:hover {
454
+ opacity: 1;
455
+ }
456
+
457
+ .btn-ghost {
458
+ background: transparent;
459
+ color: var(--text-secondary);
460
+ border: 1px solid var(--border);
461
+ }
462
+
463
+ .btn-ghost:hover {
464
+ color: var(--text);
465
+ border-color: var(--border-hover);
466
+ background: var(--bg-elevated);
467
+ }
468
+
469
+ .btn-full { width: 100%; justify-content: center; }
470
+
471
+ /* ── Raw JSON ── */
472
+ .json-editor {
473
+ width: 100%;
474
+ min-height: 500px;
475
+ padding: 16px;
476
+ font-family: var(--mono);
477
+ font-size: 12px;
478
+ line-height: 1.7;
479
+ color: var(--text);
480
+ background: var(--bg-input);
481
+ border: 1px solid var(--border);
482
+ border-radius: var(--radius-lg);
483
+ outline: none;
484
+ resize: vertical;
485
+ tab-size: 2;
486
+ }
487
+
488
+ .json-editor:focus { border-color: var(--text-tertiary); }
489
+
490
+ /* ── Modal ── */
491
+ .modal-overlay {
492
+ position: fixed;
493
+ inset: 0;
494
+ display: flex;
495
+ align-items: center;
496
+ justify-content: center;
497
+ background: rgba(0, 0, 0, 0.8);
498
+ backdrop-filter: blur(8px);
499
+ z-index: 100;
500
+ }
501
+
502
+ .modal-card {
503
+ background: var(--bg-card);
504
+ border: 1px solid var(--border);
505
+ border-radius: var(--radius-lg);
506
+ padding: 24px 32px 28px;
507
+ width: 440px;
508
+ max-width: 90vw;
509
+ text-align: center;
510
+ }
511
+
512
+ .modal-logo {
513
+ margin-bottom: 16px;
514
+ }
515
+
516
+ .modal-logo img {
517
+ width: 280px;
518
+ height: auto;
519
+ max-height: 140px;
520
+ object-fit: contain;
521
+ border-radius: 16px;
522
+ }
523
+
524
+ .modal-card h2 {
525
+ font-size: 18px;
526
+ font-weight: 600;
527
+ margin-bottom: 4px;
528
+ letter-spacing: -0.02em;
529
+ }
530
+
531
+ .modal-desc {
532
+ font-size: 13px;
533
+ color: var(--text-secondary);
534
+ margin-bottom: 16px;
535
+ }
536
+
537
+ .input-group {
538
+ margin-bottom: 12px;
539
+ }
540
+
541
+ .input-group input {
542
+ width: 100%;
543
+ padding: 10px 14px;
544
+ font-family: var(--mono);
545
+ font-size: 13px;
546
+ color: var(--text);
547
+ background: var(--bg-input);
548
+ border: 1px solid var(--border);
549
+ border-radius: var(--radius);
550
+ outline: none;
551
+ transition: border-color var(--transition);
552
+ }
553
+
554
+ .input-group input:focus { border-color: var(--text-tertiary); }
555
+
556
+ /* ── Toast ── */
557
+ .toast {
558
+ position: fixed;
559
+ bottom: 24px;
560
+ right: 24px;
561
+ padding: 10px 18px;
562
+ font-size: 13px;
563
+ font-weight: 500;
564
+ background: var(--bg-card);
565
+ color: var(--text);
566
+ border: 1px solid var(--border);
567
+ border-radius: var(--radius);
568
+ box-shadow: 0 8px 30px rgba(0, 0, 0, 0.4);
569
+ z-index: 200;
570
+ animation: toastIn 0.2s ease;
571
+ }
572
+
573
+ .toast.success { border-color: rgba(34, 197, 94, 0.3); }
574
+ .toast.error { border-color: rgba(239, 68, 68, 0.3); }
575
+
576
+ @keyframes toastIn {
577
+ from { opacity: 0; transform: translateY(8px); }
578
+ to { opacity: 1; transform: translateY(0); }
579
+ }
580
+
581
+ .hidden { display: none !important; }
582
+
583
+ /* ── Empty state ── */
584
+ .empty-state {
585
+ text-align: center;
586
+ padding: 48px 24px;
587
+ color: var(--text-tertiary);
588
+ font-size: 13px;
589
+ }
590
+
591
+ /* ── Responsive ── */
592
+ @media (max-width: 768px) {
593
+ .sidebar {
594
+ position: relative;
595
+ width: 100%;
596
+ border-right: none;
597
+ border-bottom: 1px solid var(--border);
598
+ }
599
+ .sidebar-top { padding: 12px; }
600
+ .sidebar-nav { flex-direction: row; flex-wrap: wrap; gap: 4px; }
601
+ .sidebar-bottom { display: none; }
602
+ .content { margin-left: 0; }
603
+ .topbar { padding: 16px 20px; flex-wrap: wrap; }
604
+ .content-body { padding: 16px 20px 40px; }
605
+ .field-row { flex-direction: column; align-items: stretch; }
606
+ .field-label { width: auto; }
607
+ .app { flex-direction: column; }
608
+ }
@@ -0,0 +1,5 @@
1
+ """Heartbeat service for periodic agent wake-ups."""
2
+
3
+ from kyber.heartbeat.service import HeartbeatService
4
+
5
+ __all__ = ["HeartbeatService"]