@way_marks/server 2.0.2 → 3.0.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.
- package/dist/api/events.js +45 -0
- package/dist/api/server.js +49 -6
- package/package.json +2 -2
- package/src/ui-dist/assets/ibm-plex-mono-cyrillic-400-normal-BSMlKf0J.woff2 +0 -0
- package/src/ui-dist/assets/ibm-plex-mono-cyrillic-400-normal-CEL4l2ZJ.woff +0 -0
- package/src/ui-dist/assets/ibm-plex-mono-cyrillic-500-normal-Ael50iVv.woff +0 -0
- package/src/ui-dist/assets/ibm-plex-mono-cyrillic-500-normal-Bq9vWWag.woff2 +0 -0
- package/src/ui-dist/assets/ibm-plex-mono-cyrillic-ext-400-normal-DMdlQ8Kv.woff +0 -0
- package/src/ui-dist/assets/ibm-plex-mono-cyrillic-ext-400-normal-xuaO2J-f.woff2 +0 -0
- package/src/ui-dist/assets/ibm-plex-mono-cyrillic-ext-500-normal-BIfNGwUT.woff +0 -0
- package/src/ui-dist/assets/ibm-plex-mono-cyrillic-ext-500-normal-BqneJy0T.woff2 +0 -0
- package/src/ui-dist/assets/ibm-plex-mono-latin-400-normal-CvHOgSBP.woff +0 -0
- package/src/ui-dist/assets/ibm-plex-mono-latin-400-normal-DMJ8VG8y.woff2 +0 -0
- package/src/ui-dist/assets/ibm-plex-mono-latin-500-normal-CB9ihrfo.woff +0 -0
- package/src/ui-dist/assets/ibm-plex-mono-latin-500-normal-DSY6xOcd.woff2 +0 -0
- package/src/ui-dist/assets/ibm-plex-mono-latin-ext-400-normal-BmRBH3aV.woff2 +0 -0
- package/src/ui-dist/assets/ibm-plex-mono-latin-ext-400-normal-D3D2R8hC.woff +0 -0
- package/src/ui-dist/assets/ibm-plex-mono-latin-ext-500-normal-CAhNIIs5.woff2 +0 -0
- package/src/ui-dist/assets/ibm-plex-mono-latin-ext-500-normal-CZ70TYgx.woff +0 -0
- package/src/ui-dist/assets/ibm-plex-mono-vietnamese-400-normal-BulugwFq.woff2 +0 -0
- package/src/ui-dist/assets/ibm-plex-mono-vietnamese-400-normal-DDuiU_S-.woff +0 -0
- package/src/ui-dist/assets/ibm-plex-mono-vietnamese-500-normal-C8zxqsMH.woff +0 -0
- package/src/ui-dist/assets/ibm-plex-mono-vietnamese-500-normal-DZ4AoWbu.woff2 +0 -0
- package/src/ui-dist/assets/ibm-plex-sans-cyrillic-400-normal-BTotfTJu.woff +0 -0
- package/src/ui-dist/assets/ibm-plex-sans-cyrillic-400-normal-DZqxrq2p.woff2 +0 -0
- package/src/ui-dist/assets/ibm-plex-sans-cyrillic-500-normal-ByOcLdNv.woff +0 -0
- package/src/ui-dist/assets/ibm-plex-sans-cyrillic-500-normal-CocWQlwt.woff2 +0 -0
- package/src/ui-dist/assets/ibm-plex-sans-cyrillic-600-normal-71GNu3SW.woff2 +0 -0
- package/src/ui-dist/assets/ibm-plex-sans-cyrillic-600-normal-BGq0mW3O.woff +0 -0
- package/src/ui-dist/assets/ibm-plex-sans-cyrillic-ext-400-normal-Dsrv2Tcn.woff +0 -0
- package/src/ui-dist/assets/ibm-plex-sans-cyrillic-ext-400-normal-g30qAdWV.woff2 +0 -0
- package/src/ui-dist/assets/ibm-plex-sans-cyrillic-ext-500-normal-Cs5J6C77.woff2 +0 -0
- package/src/ui-dist/assets/ibm-plex-sans-cyrillic-ext-500-normal-DB5PtV2g.woff +0 -0
- package/src/ui-dist/assets/ibm-plex-sans-cyrillic-ext-600-normal-Bz0x94Yp.woff +0 -0
- package/src/ui-dist/assets/ibm-plex-sans-cyrillic-ext-600-normal-DUMzJB7m.woff2 +0 -0
- package/src/ui-dist/assets/ibm-plex-sans-greek-400-normal-D9ESIMu3.woff +0 -0
- package/src/ui-dist/assets/ibm-plex-sans-greek-400-normal-_efipK4i.woff2 +0 -0
- package/src/ui-dist/assets/ibm-plex-sans-greek-500-normal-CuWXN6rf.woff +0 -0
- package/src/ui-dist/assets/ibm-plex-sans-greek-500-normal-JMMifIXV.woff2 +0 -0
- package/src/ui-dist/assets/ibm-plex-sans-greek-600-normal-D-CqTdkO.woff +0 -0
- package/src/ui-dist/assets/ibm-plex-sans-greek-600-normal-DzTrcv_p.woff2 +0 -0
- package/src/ui-dist/assets/ibm-plex-sans-latin-400-normal-CDDApCn2.woff2 +0 -0
- package/src/ui-dist/assets/ibm-plex-sans-latin-400-normal-CYLoc0-x.woff +0 -0
- package/src/ui-dist/assets/ibm-plex-sans-latin-500-normal-6ng42L7E.woff2 +0 -0
- package/src/ui-dist/assets/ibm-plex-sans-latin-500-normal-BgVn5rGT.woff +0 -0
- package/src/ui-dist/assets/ibm-plex-sans-latin-600-normal-Cu4Hd6ag.woff +0 -0
- package/src/ui-dist/assets/ibm-plex-sans-latin-600-normal-CuJfVYMP.woff2 +0 -0
- package/src/ui-dist/assets/ibm-plex-sans-latin-ext-400-normal-C5H60-Va.woff2 +0 -0
- package/src/ui-dist/assets/ibm-plex-sans-latin-ext-400-normal-RBey6euL.woff +0 -0
- package/src/ui-dist/assets/ibm-plex-sans-latin-ext-500-normal-D0aIdm-b.woff +0 -0
- package/src/ui-dist/assets/ibm-plex-sans-latin-ext-500-normal-DakdToA3.woff2 +0 -0
- package/src/ui-dist/assets/ibm-plex-sans-latin-ext-600-normal-DIrixKbi.woff +0 -0
- package/src/ui-dist/assets/ibm-plex-sans-latin-ext-600-normal-DOrvGEcy.woff2 +0 -0
- package/src/ui-dist/assets/ibm-plex-sans-vietnamese-400-normal-DG4YqDda.woff2 +0 -0
- package/src/ui-dist/assets/ibm-plex-sans-vietnamese-400-normal-fK1oJ5dG.woff +0 -0
- package/src/ui-dist/assets/ibm-plex-sans-vietnamese-500-normal-BEb3_waV.woff +0 -0
- package/src/ui-dist/assets/ibm-plex-sans-vietnamese-500-normal-e4dixQRQ.woff2 +0 -0
- package/src/ui-dist/assets/ibm-plex-sans-vietnamese-600-normal-DgdngZtN.woff +0 -0
- package/src/ui-dist/assets/ibm-plex-sans-vietnamese-600-normal-DpPYBSTl.woff2 +0 -0
- package/src/ui-dist/assets/index-DNdosrlQ.css +1 -0
- package/src/ui-dist/assets/index-EkwgRogY.js +87 -0
- package/src/ui-dist/index.html +14 -0
- package/src/ui/index.html +0 -1452
- package/src/ui/index.html.bak +0 -429
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.attachSubscriber = attachSubscriber;
|
|
4
|
+
exports.emit = emit;
|
|
5
|
+
const subscribers = new Set();
|
|
6
|
+
function attachSubscriber(res) {
|
|
7
|
+
// SSE headers
|
|
8
|
+
res.setHeader('Content-Type', 'text/event-stream');
|
|
9
|
+
res.setHeader('Cache-Control', 'no-cache, no-transform');
|
|
10
|
+
res.setHeader('Connection', 'keep-alive');
|
|
11
|
+
res.setHeader('X-Accel-Buffering', 'no');
|
|
12
|
+
res.flushHeaders?.();
|
|
13
|
+
const sub = { res, closed: false };
|
|
14
|
+
subscribers.add(sub);
|
|
15
|
+
// Initial hello so the client opens onmessage cleanly
|
|
16
|
+
res.write(`event: hello\ndata: {"ok":true}\n\n`);
|
|
17
|
+
return () => {
|
|
18
|
+
sub.closed = true;
|
|
19
|
+
subscribers.delete(sub);
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function emit(topic, payload = {}) {
|
|
23
|
+
const data = JSON.stringify({ topic, ...payload });
|
|
24
|
+
const frame = `event: ${topic}\ndata: ${data}\n\n`;
|
|
25
|
+
for (const sub of subscribers) {
|
|
26
|
+
if (sub.closed)
|
|
27
|
+
continue;
|
|
28
|
+
try {
|
|
29
|
+
sub.res.write(frame);
|
|
30
|
+
}
|
|
31
|
+
catch { /* client disconnected; cleanup happens via close handler */ }
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
// Heartbeat keeps proxies / browsers from closing the stream.
|
|
35
|
+
const HEARTBEAT_MS = 25000;
|
|
36
|
+
setInterval(() => {
|
|
37
|
+
for (const sub of subscribers) {
|
|
38
|
+
if (sub.closed)
|
|
39
|
+
continue;
|
|
40
|
+
try {
|
|
41
|
+
sub.res.write(`: heartbeat\n\n`);
|
|
42
|
+
}
|
|
43
|
+
catch { /* noop */ }
|
|
44
|
+
}
|
|
45
|
+
}, HEARTBEAT_MS).unref?.();
|
package/dist/api/server.js
CHANGED
|
@@ -46,6 +46,7 @@ const engine_1 = require("../policies/engine");
|
|
|
46
46
|
const handler_1 = require("../approvals/handler");
|
|
47
47
|
const manager_2 = require("../approval/manager");
|
|
48
48
|
const manager_3 = require("../escalation/manager");
|
|
49
|
+
const events_1 = require("./events");
|
|
49
50
|
// Import registry for Phase 2 hub navigation
|
|
50
51
|
const registryPath = path.join(process.env.HOME || process.env.USERPROFILE || '', '.waymark', 'registry.json');
|
|
51
52
|
function getRegistryProjects() {
|
|
@@ -96,9 +97,22 @@ const app = (0, express_1.default)();
|
|
|
96
97
|
const PORT = parseInt(process.env.WAYMARK_PORT || '3001', 10);
|
|
97
98
|
app.use(express_1.default.json());
|
|
98
99
|
app.use(express_1.default.urlencoded({ extended: true }));
|
|
99
|
-
// Serve UI — path works for both ts-node (src/api/) and compiled (dist/api/)
|
|
100
|
-
const UI_DIR = path.resolve(__dirname, '../../src/ui');
|
|
101
|
-
|
|
100
|
+
// Serve UI — path works for both ts-node (src/api/) and compiled (dist/api/).
|
|
101
|
+
const UI_DIR = path.resolve(__dirname, '../../src/ui-dist');
|
|
102
|
+
const UI_INDEX = path.join(UI_DIR, 'index.html');
|
|
103
|
+
const UI_BUILT = fs.existsSync(UI_INDEX);
|
|
104
|
+
if (UI_BUILT) {
|
|
105
|
+
app.use(express_1.default.static(UI_DIR));
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
console.warn('[waymark] ui-dist/ not found — dashboard will return a setup banner. ' +
|
|
109
|
+
'Run `npm run build -w @way_marks/web` to build the dashboard.');
|
|
110
|
+
}
|
|
111
|
+
// GET /api/events — Server-Sent Events stream for live UI updates
|
|
112
|
+
app.get('/api/events', (req, res) => {
|
|
113
|
+
const detach = (0, events_1.attachSubscriber)(res);
|
|
114
|
+
req.on('close', detach);
|
|
115
|
+
});
|
|
102
116
|
// GET /api/actions — list all actions (or ?count=true for pending count)
|
|
103
117
|
app.get('/api/actions', (req, res) => {
|
|
104
118
|
try {
|
|
@@ -192,6 +206,7 @@ app.post('/api/actions/:action_id/approve', async (req, res) => {
|
|
|
192
206
|
const status = result.error === 'Action not found' ? 404 : 400;
|
|
193
207
|
return res.status(status).json({ error: result.error });
|
|
194
208
|
}
|
|
209
|
+
(0, events_1.emit)('actions', { action_id: req.params.action_id, kind: 'approved' });
|
|
195
210
|
res.json(result);
|
|
196
211
|
}
|
|
197
212
|
catch (err) {
|
|
@@ -207,6 +222,7 @@ app.post('/api/actions/:action_id/reject', async (req, res) => {
|
|
|
207
222
|
const status = result.error === 'Action not found' ? 404 : 400;
|
|
208
223
|
return res.status(status).json({ error: result.error });
|
|
209
224
|
}
|
|
225
|
+
(0, events_1.emit)('actions', { action_id: req.params.action_id, kind: 'rejected' });
|
|
210
226
|
res.json(result);
|
|
211
227
|
}
|
|
212
228
|
catch (err) {
|
|
@@ -266,12 +282,14 @@ app.post('/api/actions/:action_id/rollback', (req, res) => {
|
|
|
266
282
|
if (!action.before_snapshot) {
|
|
267
283
|
fs.unlinkSync(action.target_path);
|
|
268
284
|
(0, database_1.markRolledBack)(action.action_id);
|
|
285
|
+
(0, events_1.emit)('actions', { action_id: action.action_id, kind: 'rolled_back' });
|
|
269
286
|
return res.json({ success: true, action: 'deleted', message: `Deleted new file: ${action.target_path}` });
|
|
270
287
|
}
|
|
271
288
|
// Restore file to before_snapshot
|
|
272
289
|
fs.mkdirSync(path.dirname(action.target_path), { recursive: true });
|
|
273
290
|
fs.writeFileSync(action.target_path, action.before_snapshot, 'utf8');
|
|
274
291
|
(0, database_1.markRolledBack)(action.action_id);
|
|
292
|
+
(0, events_1.emit)('actions', { action_id: action.action_id, kind: 'rolled_back' });
|
|
275
293
|
res.json({ success: true, action: 'restored', message: `Restored ${action.target_path} to previous state` });
|
|
276
294
|
}
|
|
277
295
|
catch (err) {
|
|
@@ -384,6 +402,8 @@ app.post('/api/sessions/:session_id/rollback', (req, res) => {
|
|
|
384
402
|
message: 'Rollback failed',
|
|
385
403
|
});
|
|
386
404
|
}
|
|
405
|
+
(0, events_1.emit)('sessions', { session_id, kind: 'rolled_back', count: actions.length });
|
|
406
|
+
(0, events_1.emit)('actions', { session_id, kind: 'session_rolled_back' });
|
|
387
407
|
res.json({
|
|
388
408
|
success: true,
|
|
389
409
|
message: `Rolled back session ${session_id}`,
|
|
@@ -488,6 +508,7 @@ app.post('/api/team/members', (req, res) => {
|
|
|
488
508
|
return res.status(400).json({ error: `Email ${email} already in use` });
|
|
489
509
|
}
|
|
490
510
|
(0, database_1.addTeamMember)(member_id, name, email, added_by, slack_id);
|
|
511
|
+
(0, events_1.emit)('team', { member_id, kind: 'added' });
|
|
491
512
|
res.json({ success: true, member_id, message: `Added team member: ${name}` });
|
|
492
513
|
}
|
|
493
514
|
catch (err) {
|
|
@@ -503,6 +524,7 @@ app.delete('/api/team/members/:member_id', (req, res) => {
|
|
|
503
524
|
return res.status(404).json({ error: `Team member ${member_id} not found` });
|
|
504
525
|
}
|
|
505
526
|
(0, database_1.removeTeamMember)(member_id);
|
|
527
|
+
(0, events_1.emit)('team', { member_id, kind: 'removed' });
|
|
506
528
|
res.json({ success: true, message: `Removed team member: ${member.name}` });
|
|
507
529
|
}
|
|
508
530
|
catch (err) {
|
|
@@ -528,6 +550,7 @@ app.post('/api/approval-routes', (req, res) => {
|
|
|
528
550
|
return res.status(400).json({ error: 'Missing required fields: route_id, name, approver_ids (array)' });
|
|
529
551
|
}
|
|
530
552
|
(0, database_1.addApprovalRoute)(route_id, name, approver_ids, created_by, description, condition_type, condition_json);
|
|
553
|
+
(0, events_1.emit)('approval-routes', { route_id, kind: 'added' });
|
|
531
554
|
res.json({ success: true, route_id, message: `Created approval route: ${name}` });
|
|
532
555
|
}
|
|
533
556
|
catch (err) {
|
|
@@ -544,6 +567,7 @@ app.put('/api/approval-routes/:route_id', (req, res) => {
|
|
|
544
567
|
return res.status(404).json({ error: `Approval route ${route_id} not found` });
|
|
545
568
|
}
|
|
546
569
|
(0, database_1.updateApprovalRoute)(route_id, { name, description, approver_ids });
|
|
570
|
+
(0, events_1.emit)('approval-routes', { route_id, kind: 'updated' });
|
|
547
571
|
res.json({ success: true, message: `Updated approval route: ${route_id}` });
|
|
548
572
|
}
|
|
549
573
|
catch (err) {
|
|
@@ -559,6 +583,7 @@ app.delete('/api/approval-routes/:route_id', (req, res) => {
|
|
|
559
583
|
return res.status(404).json({ error: `Approval route ${route_id} not found` });
|
|
560
584
|
}
|
|
561
585
|
(0, database_1.deleteApprovalRoute)(route_id);
|
|
586
|
+
(0, events_1.emit)('approval-routes', { route_id, kind: 'deleted' });
|
|
562
587
|
res.json({ success: true, message: `Deleted approval route: ${route_id}` });
|
|
563
588
|
}
|
|
564
589
|
catch (err) {
|
|
@@ -608,6 +633,7 @@ app.post('/api/approvals/:request_id/approve', (req, res) => {
|
|
|
608
633
|
return res.status(400).json({ error: 'Missing required field: approver_id' });
|
|
609
634
|
}
|
|
610
635
|
const status = (0, manager_2.submitApprovalDecision)(request_id, approver_id, 'approve', reason);
|
|
636
|
+
(0, events_1.emit)('approvals', { request_id, kind: 'approve', approver_id });
|
|
611
637
|
res.json({
|
|
612
638
|
success: true,
|
|
613
639
|
message: `Approved by ${approver_id}`,
|
|
@@ -627,6 +653,7 @@ app.post('/api/approvals/:request_id/reject', (req, res) => {
|
|
|
627
653
|
return res.status(400).json({ error: 'Missing required field: approver_id' });
|
|
628
654
|
}
|
|
629
655
|
const status = (0, manager_2.submitApprovalDecision)(request_id, approver_id, 'reject', reason);
|
|
656
|
+
(0, events_1.emit)('approvals', { request_id, kind: 'reject', approver_id });
|
|
630
657
|
res.json({
|
|
631
658
|
success: true,
|
|
632
659
|
message: `Rejected by ${approver_id}`,
|
|
@@ -674,6 +701,7 @@ app.post('/api/escalations/rules', (req, res) => {
|
|
|
674
701
|
return res.status(400).json({ error: 'Missing required fields: rule_id, name, escalation_targets (array)' });
|
|
675
702
|
}
|
|
676
703
|
(0, database_1.addEscalationRule)(rule_id, name, escalation_targets, created_by, description, timeout_hours);
|
|
704
|
+
(0, events_1.emit)('escalation-rules', { rule_id, kind: 'added' });
|
|
677
705
|
res.json({ success: true, rule_id, message: `Created escalation rule: ${name}` });
|
|
678
706
|
}
|
|
679
707
|
catch (err) {
|
|
@@ -690,6 +718,7 @@ app.put('/api/escalations/rules/:rule_id', (req, res) => {
|
|
|
690
718
|
return res.status(404).json({ error: `Escalation rule ${rule_id} not found` });
|
|
691
719
|
}
|
|
692
720
|
(0, database_1.updateEscalationRule)(rule_id, { name, description, escalation_targets, timeout_hours });
|
|
721
|
+
(0, events_1.emit)('escalation-rules', { rule_id, kind: 'updated' });
|
|
693
722
|
res.json({ success: true, message: `Updated escalation rule: ${rule_id}` });
|
|
694
723
|
}
|
|
695
724
|
catch (err) {
|
|
@@ -705,6 +734,7 @@ app.delete('/api/escalations/rules/:rule_id', (req, res) => {
|
|
|
705
734
|
return res.status(404).json({ error: `Escalation rule ${rule_id} not found` });
|
|
706
735
|
}
|
|
707
736
|
(0, database_1.deleteEscalationRule)(rule_id);
|
|
737
|
+
(0, events_1.emit)('escalation-rules', { rule_id, kind: 'deleted' });
|
|
708
738
|
res.json({ success: true, message: `Deleted escalation rule: ${rule_id}` });
|
|
709
739
|
}
|
|
710
740
|
catch (err) {
|
|
@@ -756,6 +786,8 @@ app.post('/api/escalations/:request_id/decide', (req, res) => {
|
|
|
756
786
|
return res.status(400).json({ error: 'Decision must be "proceed" or "block"' });
|
|
757
787
|
}
|
|
758
788
|
const status = (0, manager_3.submitEscalationDecision)(request_id, target_id, decision, reason);
|
|
789
|
+
(0, events_1.emit)('escalations', { request_id, kind: 'decided', decision, target_id });
|
|
790
|
+
(0, events_1.emit)('approvals', { request_id, kind: 'escalation_decided' });
|
|
759
791
|
res.json({
|
|
760
792
|
success: true,
|
|
761
793
|
message: `${target_id} decided to ${decision}`,
|
|
@@ -958,9 +990,20 @@ app.delete('/api/remediation/policies/:policy_id', (req, res) => {
|
|
|
958
990
|
res.status(500).json({ error: err.message });
|
|
959
991
|
}
|
|
960
992
|
});
|
|
961
|
-
// Fallback: serve UI for any unmatched route
|
|
962
|
-
|
|
963
|
-
|
|
993
|
+
// Fallback: serve UI for any unmatched route. If the dashboard hasn't been
|
|
994
|
+
// built yet, emit a friendly setup banner instead of a 404.
|
|
995
|
+
app.get('*', (_req, res) => {
|
|
996
|
+
if (UI_BUILT) {
|
|
997
|
+
return res.sendFile(UI_INDEX);
|
|
998
|
+
}
|
|
999
|
+
res.status(503).type('html').send(`<!doctype html>
|
|
1000
|
+
<html><head><meta charset="utf-8"/><title>Waymark — dashboard not built</title>
|
|
1001
|
+
<style>body{font:14px/1.5 -apple-system,system-ui,sans-serif;color:#1d2126;background:#fafaf8;display:grid;place-items:center;min-height:100vh;margin:0}main{max-width:520px;padding:32px}h1{margin:0 0 8px;font-size:18px}code{background:#ebebe8;padding:2px 6px;border-radius:4px;font-family:ui-monospace,Menlo,monospace}</style>
|
|
1002
|
+
</head><body><main>
|
|
1003
|
+
<h1>Dashboard not built</h1>
|
|
1004
|
+
<p>The Waymark API is running, but the React dashboard hasn't been built yet.</p>
|
|
1005
|
+
<p>Run <code>npm run build -w @way_marks/web</code> from the project root, then refresh.</p>
|
|
1006
|
+
</main></body></html>`);
|
|
964
1007
|
});
|
|
965
1008
|
app.listen(PORT, () => {
|
|
966
1009
|
console.log(`Waymark UI + API running at http://localhost:${PORT}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@way_marks/server",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "Waymark MCP server and dashboard",
|
|
5
5
|
"author": "Waymark <hello@waymarks.dev>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
},
|
|
28
28
|
"files": [
|
|
29
29
|
"dist/**",
|
|
30
|
-
"src/ui/**"
|
|
30
|
+
"src/ui-dist/**"
|
|
31
31
|
],
|
|
32
32
|
"scripts": {
|
|
33
33
|
"build": "tsc",
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@font-face{font-family:IBM Plex Sans;font-style:normal;font-display:swap;font-weight:400;src:url(/assets/ibm-plex-sans-cyrillic-ext-400-normal-g30qAdWV.woff2) format("woff2"),url(/assets/ibm-plex-sans-cyrillic-ext-400-normal-Dsrv2Tcn.woff) format("woff");unicode-range:U+0460-052F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:IBM Plex Sans;font-style:normal;font-display:swap;font-weight:400;src:url(/assets/ibm-plex-sans-cyrillic-400-normal-DZqxrq2p.woff2) format("woff2"),url(/assets/ibm-plex-sans-cyrillic-400-normal-BTotfTJu.woff) format("woff");unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:IBM Plex Sans;font-style:normal;font-display:swap;font-weight:400;src:url(/assets/ibm-plex-sans-greek-400-normal-_efipK4i.woff2) format("woff2"),url(/assets/ibm-plex-sans-greek-400-normal-D9ESIMu3.woff) format("woff");unicode-range:U+0370-0377,U+037A-037F,U+0384-038A,U+038C,U+038E-03A1,U+03A3-03FF}@font-face{font-family:IBM Plex Sans;font-style:normal;font-display:swap;font-weight:400;src:url(/assets/ibm-plex-sans-vietnamese-400-normal-DG4YqDda.woff2) format("woff2"),url(/assets/ibm-plex-sans-vietnamese-400-normal-fK1oJ5dG.woff) format("woff");unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB}@font-face{font-family:IBM Plex Sans;font-style:normal;font-display:swap;font-weight:400;src:url(/assets/ibm-plex-sans-latin-ext-400-normal-C5H60-Va.woff2) format("woff2"),url(/assets/ibm-plex-sans-latin-ext-400-normal-RBey6euL.woff) format("woff");unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:IBM Plex Sans;font-style:normal;font-display:swap;font-weight:400;src:url(/assets/ibm-plex-sans-latin-400-normal-CDDApCn2.woff2) format("woff2"),url(/assets/ibm-plex-sans-latin-400-normal-CYLoc0-x.woff) format("woff");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:IBM Plex Sans;font-style:normal;font-display:swap;font-weight:500;src:url(/assets/ibm-plex-sans-cyrillic-ext-500-normal-Cs5J6C77.woff2) format("woff2"),url(/assets/ibm-plex-sans-cyrillic-ext-500-normal-DB5PtV2g.woff) format("woff");unicode-range:U+0460-052F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:IBM Plex Sans;font-style:normal;font-display:swap;font-weight:500;src:url(/assets/ibm-plex-sans-cyrillic-500-normal-CocWQlwt.woff2) format("woff2"),url(/assets/ibm-plex-sans-cyrillic-500-normal-ByOcLdNv.woff) format("woff");unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:IBM Plex Sans;font-style:normal;font-display:swap;font-weight:500;src:url(/assets/ibm-plex-sans-greek-500-normal-JMMifIXV.woff2) format("woff2"),url(/assets/ibm-plex-sans-greek-500-normal-CuWXN6rf.woff) format("woff");unicode-range:U+0370-0377,U+037A-037F,U+0384-038A,U+038C,U+038E-03A1,U+03A3-03FF}@font-face{font-family:IBM Plex Sans;font-style:normal;font-display:swap;font-weight:500;src:url(/assets/ibm-plex-sans-vietnamese-500-normal-e4dixQRQ.woff2) format("woff2"),url(/assets/ibm-plex-sans-vietnamese-500-normal-BEb3_waV.woff) format("woff");unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB}@font-face{font-family:IBM Plex Sans;font-style:normal;font-display:swap;font-weight:500;src:url(/assets/ibm-plex-sans-latin-ext-500-normal-DakdToA3.woff2) format("woff2"),url(/assets/ibm-plex-sans-latin-ext-500-normal-D0aIdm-b.woff) format("woff");unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:IBM Plex Sans;font-style:normal;font-display:swap;font-weight:500;src:url(/assets/ibm-plex-sans-latin-500-normal-6ng42L7E.woff2) format("woff2"),url(/assets/ibm-plex-sans-latin-500-normal-BgVn5rGT.woff) format("woff");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:IBM Plex Sans;font-style:normal;font-display:swap;font-weight:600;src:url(/assets/ibm-plex-sans-cyrillic-ext-600-normal-DUMzJB7m.woff2) format("woff2"),url(/assets/ibm-plex-sans-cyrillic-ext-600-normal-Bz0x94Yp.woff) format("woff");unicode-range:U+0460-052F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:IBM Plex Sans;font-style:normal;font-display:swap;font-weight:600;src:url(/assets/ibm-plex-sans-cyrillic-600-normal-71GNu3SW.woff2) format("woff2"),url(/assets/ibm-plex-sans-cyrillic-600-normal-BGq0mW3O.woff) format("woff");unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:IBM Plex Sans;font-style:normal;font-display:swap;font-weight:600;src:url(/assets/ibm-plex-sans-greek-600-normal-DzTrcv_p.woff2) format("woff2"),url(/assets/ibm-plex-sans-greek-600-normal-D-CqTdkO.woff) format("woff");unicode-range:U+0370-0377,U+037A-037F,U+0384-038A,U+038C,U+038E-03A1,U+03A3-03FF}@font-face{font-family:IBM Plex Sans;font-style:normal;font-display:swap;font-weight:600;src:url(/assets/ibm-plex-sans-vietnamese-600-normal-DpPYBSTl.woff2) format("woff2"),url(/assets/ibm-plex-sans-vietnamese-600-normal-DgdngZtN.woff) format("woff");unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB}@font-face{font-family:IBM Plex Sans;font-style:normal;font-display:swap;font-weight:600;src:url(/assets/ibm-plex-sans-latin-ext-600-normal-DOrvGEcy.woff2) format("woff2"),url(/assets/ibm-plex-sans-latin-ext-600-normal-DIrixKbi.woff) format("woff");unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:IBM Plex Sans;font-style:normal;font-display:swap;font-weight:600;src:url(/assets/ibm-plex-sans-latin-600-normal-CuJfVYMP.woff2) format("woff2"),url(/assets/ibm-plex-sans-latin-600-normal-Cu4Hd6ag.woff) format("woff");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:IBM Plex Mono;font-style:normal;font-display:swap;font-weight:400;src:url(/assets/ibm-plex-mono-cyrillic-ext-400-normal-xuaO2J-f.woff2) format("woff2"),url(/assets/ibm-plex-mono-cyrillic-ext-400-normal-DMdlQ8Kv.woff) format("woff");unicode-range:U+0460-052F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:IBM Plex Mono;font-style:normal;font-display:swap;font-weight:400;src:url(/assets/ibm-plex-mono-cyrillic-400-normal-BSMlKf0J.woff2) format("woff2"),url(/assets/ibm-plex-mono-cyrillic-400-normal-CEL4l2ZJ.woff) format("woff");unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:IBM Plex Mono;font-style:normal;font-display:swap;font-weight:400;src:url(/assets/ibm-plex-mono-vietnamese-400-normal-BulugwFq.woff2) format("woff2"),url(/assets/ibm-plex-mono-vietnamese-400-normal-DDuiU_S-.woff) format("woff");unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB}@font-face{font-family:IBM Plex Mono;font-style:normal;font-display:swap;font-weight:400;src:url(/assets/ibm-plex-mono-latin-ext-400-normal-BmRBH3aV.woff2) format("woff2"),url(/assets/ibm-plex-mono-latin-ext-400-normal-D3D2R8hC.woff) format("woff");unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:IBM Plex Mono;font-style:normal;font-display:swap;font-weight:400;src:url(/assets/ibm-plex-mono-latin-400-normal-DMJ8VG8y.woff2) format("woff2"),url(/assets/ibm-plex-mono-latin-400-normal-CvHOgSBP.woff) format("woff");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:IBM Plex Mono;font-style:normal;font-display:swap;font-weight:500;src:url(/assets/ibm-plex-mono-cyrillic-ext-500-normal-BqneJy0T.woff2) format("woff2"),url(/assets/ibm-plex-mono-cyrillic-ext-500-normal-BIfNGwUT.woff) format("woff");unicode-range:U+0460-052F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:IBM Plex Mono;font-style:normal;font-display:swap;font-weight:500;src:url(/assets/ibm-plex-mono-cyrillic-500-normal-Bq9vWWag.woff2) format("woff2"),url(/assets/ibm-plex-mono-cyrillic-500-normal-Ael50iVv.woff) format("woff");unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:IBM Plex Mono;font-style:normal;font-display:swap;font-weight:500;src:url(/assets/ibm-plex-mono-vietnamese-500-normal-DZ4AoWbu.woff2) format("woff2"),url(/assets/ibm-plex-mono-vietnamese-500-normal-C8zxqsMH.woff) format("woff");unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB}@font-face{font-family:IBM Plex Mono;font-style:normal;font-display:swap;font-weight:500;src:url(/assets/ibm-plex-mono-latin-ext-500-normal-CAhNIIs5.woff2) format("woff2"),url(/assets/ibm-plex-mono-latin-ext-500-normal-CZ70TYgx.woff) format("woff");unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:IBM Plex Mono;font-style:normal;font-display:swap;font-weight:500;src:url(/assets/ibm-plex-mono-latin-500-normal-DSY6xOcd.woff2) format("woff2"),url(/assets/ibm-plex-mono-latin-500-normal-CB9ihrfo.woff) format("woff");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}:root{--font-sans: "IBM Plex Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;--font-mono: "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, monospace;--bg-0: #0a0b0d;--bg-1: #101215;--bg-2: #16191d;--bg-3: #1d2126;--bg-4: #262b31;--line: #22262c;--line-strong: #2d3238;--ink-0: #f5f6f7;--ink-1: #d6d9dd;--ink-2: #a8aeb6;--ink-3: #8c929b;--ink-4: #6a7079;--acc: oklch(.72 .09 195);--acc-dim: oklch(.72 .09 195 / .15);--acc-ink: oklch(.95 .03 195);--ok: oklch(.74 .13 155);--ok-dim: oklch(.74 .13 155 / .14);--warn: oklch(.78 .13 75);--warn-dim: oklch(.78 .13 75 / .14);--err: oklch(.68 .19 25);--err-dim: oklch(.68 .19 25 / .14);--info: oklch(.72 .12 240);--info-dim: oklch(.72 .12 240 / .14);--pending: oklch(.8 .14 85);--pending-dim: oklch(.8 .14 85 / .14);--r-1: 4px;--r-2: 6px;--r-3: 10px;--r-4: 14px;--nav-w: 232px;--drawer-w: 560px;--shadow-drawer: 0 24px 48px -12px rgba(0, 0, 0, .6), 0 8px 16px -4px rgba(0, 0, 0, .4);--shadow-pop: 0 8px 24px -8px rgba(0, 0, 0, .5);--row-py: 10px;--gap: 14px;--focus-ring: 0 0 0 2px var(--bg-0), 0 0 0 4px var(--acc)}[data-theme=light]{--bg-0: #fafaf8;--bg-1: #ffffff;--bg-2: #f4f4f2;--bg-3: #ebebe8;--bg-4: #dcdcd8;--line: #dedcd6;--line-strong: #c4c2bc;--ink-0: #0a0b0d;--ink-1: #1d2126;--ink-2: #3a3e44;--ink-3: #5a5e65;--ink-4: #8c9099;--acc: oklch(.5 .1 195);--acc-dim: oklch(.5 .1 195 / .1);--acc-ink: oklch(.32 .09 195);--ok: oklch(.48 .14 155);--ok-dim: oklch(.48 .14 155 / .12);--warn: oklch(.56 .14 75);--warn-dim: oklch(.56 .14 75 / .12);--err: oklch(.5 .2 25);--err-dim: oklch(.5 .2 25 / .12);--info: oklch(.5 .14 240);--info-dim: oklch(.5 .14 240 / .12);--pending: oklch(.58 .15 85);--pending-dim: oklch(.58 .15 85 / .12)}[data-density=compact]{--row-py: 8px;--gap: 10px}[data-density=comfy]{--row-py: 14px;--gap: 16px}[data-density=spacious]{--row-py: 20px;--gap: 22px}*,*:before,*:after{box-sizing:border-box;margin:0;padding:0}html,body{height:100%}body{font-family:var(--font-sans);font-feature-settings:"ss01","cv11";font-size:13px;line-height:1.5;color:var(--ink-1);background:var(--bg-0);-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}button{font:inherit;color:inherit;background:none;border:none;cursor:pointer}button:focus-visible{outline:none;box-shadow:var(--focus-ring);border-radius:var(--r-2)}input,select,textarea{font:inherit;color:inherit}input:focus-visible{outline:none}a{color:inherit;text-decoration:none}code,kbd,.mono{font-family:var(--font-mono)}@media (prefers-reduced-motion: reduce){*,*:before,*:after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}}#root{min-height:100vh}.app{display:grid;grid-template-columns:var(--nav-w) 1fr;min-height:100vh}.nav{background:var(--bg-0);border-right:1px solid var(--line);padding:18px 14px;display:flex;flex-direction:column;gap:22px;position:sticky;top:0;height:100vh;overflow-y:auto}.brand{display:flex;align-items:center;gap:10px;padding:2px 4px}.brand-mark{width:26px;height:26px;border-radius:var(--r-2);background:linear-gradient(135deg,var(--acc),oklch(.66 .1 230));display:grid;place-items:center;color:var(--bg-0);font-family:var(--font-mono);font-weight:600;font-size:13px;letter-spacing:-.04em}.brand-name{font-weight:600;font-size:14px;color:var(--ink-0);letter-spacing:-.01em}.brand-sub{font-size:10.5px;color:var(--ink-3);font-family:var(--font-mono)}.nav-section-label{font-size:10px;text-transform:uppercase;letter-spacing:.08em;color:var(--ink-3);padding:0 8px;margin-bottom:4px;font-weight:600}.nav-list{list-style:none;display:flex;flex-direction:column;gap:1px}.nav-item{display:flex;align-items:center;gap:10px;padding:6px 8px;border-radius:var(--r-2);color:var(--ink-2);cursor:pointer;font-size:13px;transition:background .1s,color .1s;position:relative;width:100%;text-align:left}.nav-item:hover{background:var(--bg-2);color:var(--ink-1)}.nav-item.active{background:var(--bg-2);color:var(--ink-0)}.nav-item.active:before{content:"";position:absolute;left:-14px;top:6px;bottom:6px;width:2px;background:var(--acc);border-radius:0 2px 2px 0}.nav-icon{width:16px;height:16px;opacity:.85;flex-shrink:0}.nav-label{flex:1}.nav-count{font-family:var(--font-mono);font-size:11px;color:var(--ink-3);background:var(--bg-2);padding:1px 6px;border-radius:10px;min-width:18px;text-align:center}.nav-item.active .nav-count{background:var(--bg-3);color:var(--ink-1)}.nav-count.attn{background:var(--pending-dim);color:var(--pending)}.nav-footer{margin-top:auto;padding-top:16px;border-top:1px solid var(--line);display:flex;flex-direction:column;gap:10px;font-size:11.5px}.status-dot{display:inline-block;width:7px;height:7px;border-radius:50%;background:var(--ok);box-shadow:0 0 0 3px #5ec3862e;animation:pulse 2s ease-in-out infinite;flex-shrink:0}.status-dot.off{background:var(--ink-4);box-shadow:none;animation:none}@keyframes pulse{0%,to{box-shadow:0 0 0 2px #5ec38647}50%{box-shadow:0 0 0 5px #5ec38600}}.server-row{display:flex;align-items:center;gap:8px;color:var(--ink-2)}.server-port{margin-left:auto;color:var(--ink-3);font-family:var(--font-mono)}.main{display:flex;flex-direction:column;min-width:0;background:var(--bg-0)}.topbar{display:flex;align-items:center;gap:14px;padding:12px 28px;border-bottom:1px solid var(--line);background:color-mix(in srgb,var(--bg-0) 85%,transparent);position:sticky;top:0;z-index:40;backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px)}.crumbs{display:flex;align-items:center;gap:8px;min-width:0}.crumb{color:var(--ink-2);font-size:13px;white-space:nowrap}.crumb.current{color:var(--ink-0);font-weight:500}.crumb-sep{color:var(--ink-4)}.crumb-project{display:inline-flex;align-items:center;gap:6px;padding:4px 10px;background:var(--bg-2);border:1px solid var(--line);border-radius:var(--r-2);color:var(--ink-1);font-size:12.5px;cursor:pointer;transition:background .1s}.crumb-project:hover{background:var(--bg-3)}.crumb-project .mono{color:var(--ink-3);font-size:11px}.search{flex:1;max-width:420px;display:flex;align-items:center;gap:8px;padding:6px 10px;background:var(--bg-1);border:1px solid var(--line);border-radius:var(--r-2);color:var(--ink-2);transition:border-color .1s}.search:focus-within{border-color:var(--ink-4)}.search input{flex:1;background:none;border:none;outline:none;font-size:13px;color:var(--ink-0);min-width:0}.search input::placeholder{color:var(--ink-3)}.kbd{font-family:var(--font-mono);font-size:10.5px;color:var(--ink-3);padding:1px 5px;border:1px solid var(--line);border-radius:3px;background:var(--bg-2)}.topbar-right{display:flex;align-items:center;gap:8px;margin-left:auto}.icon-btn{width:30px;height:30px;display:grid;place-items:center;border-radius:var(--r-2);color:var(--ink-2);transition:background .1s,color .1s;position:relative}.icon-btn:hover{background:var(--bg-2);color:var(--ink-0)}.icon-btn svg{width:16px;height:16px}.icon-btn .dot-badge{position:absolute;top:6px;right:6px;width:6px;height:6px;border-radius:50%;background:var(--pending)}.page{padding:28px 28px 40px;max-width:1280px;width:100%;margin:0 auto}.page-header{display:flex;align-items:flex-end;justify-content:space-between;gap:20px;margin-bottom:24px;flex-wrap:wrap}.page-title{font-size:22px;font-weight:600;color:var(--ink-0);letter-spacing:-.015em;line-height:1.1}.page-sub{margin-top:6px;color:var(--ink-2);font-size:13px;max-width:640px}.page-meta{display:flex;gap:16px;align-items:center;font-size:12px;color:var(--ink-3);font-family:var(--font-mono)}.page-meta .live{display:inline-flex;align-items:center;gap:6px;color:var(--ok)}.pill-row{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:18px}.pill{display:inline-flex;align-items:center;gap:6px;padding:5px 11px;background:var(--bg-1);border:1px solid var(--line);border-radius:999px;color:var(--ink-2);font-size:12px;cursor:pointer;transition:background .1s,border-color .1s,color .1s;white-space:nowrap}.pill:hover{background:var(--bg-2);color:var(--ink-1)}.pill.active{background:var(--bg-3);border-color:var(--line-strong);color:var(--ink-0)}.pill-count{font-family:var(--font-mono);font-size:10.5px;color:var(--ink-3);padding:0 5px;background:var(--bg-2);border-radius:10px;min-width:18px;text-align:center}.pill.active .pill-count{background:var(--bg-1);color:var(--ink-1)}.pill.attn{color:var(--pending)}.pill.attn .pill-count{background:var(--pending-dim);color:var(--pending)}.toolbar{display:flex;align-items:center;gap:8px;margin-bottom:14px;flex-wrap:wrap}.toolbar-spacer{flex:1}.select{display:inline-flex;align-items:center;gap:6px;padding:5px 10px;background:var(--bg-1);border:1px solid var(--line);border-radius:var(--r-2);font-size:12px;color:var(--ink-1);cursor:pointer}.select:hover{background:var(--bg-2)}.select svg{width:12px;height:12px;opacity:.7}.approval-banner{margin-bottom:18px;padding:14px 18px;background:linear-gradient(180deg,var(--pending-dim),transparent);border:1px solid oklch(.8 .14 85 / .35);border-radius:var(--r-3);display:flex;align-items:center;gap:16px}.approval-banner-dot{width:8px;height:8px;border-radius:50%;background:var(--pending);box-shadow:0 0 0 4px var(--pending-dim);animation:pulse-p 1.6s ease-in-out infinite;flex-shrink:0}@keyframes pulse-p{0%,to{box-shadow:0 0 0 4px #e7b64347}50%{box-shadow:0 0 0 9px #e7b64300}}.approval-banner-text{flex:1}.approval-banner-title{font-weight:600;color:var(--ink-0);font-size:13.5px}.approval-banner-sub{color:var(--ink-2);font-size:12.5px;margin-top:2px}.session-group{margin-bottom:18px;border:1px solid var(--line);border-radius:var(--r-3);background:var(--bg-1);overflow:hidden}.session-group.live{border-color:#5ec38652}.session-header{display:flex;align-items:center;gap:14px;padding:12px 16px;cursor:pointer;-webkit-user-select:none;user-select:none;transition:background .1s}.session-header:hover{background:var(--bg-2)}.session-chevron{width:14px;height:14px;color:var(--ink-3);transition:transform .15s;flex-shrink:0}.session-group.collapsed .session-chevron{transform:rotate(-90deg)}.session-avatar{width:26px;height:26px;border-radius:50%;background:var(--bg-3);display:grid;place-items:center;font-family:var(--font-mono);font-size:10.5px;color:var(--ink-1);flex-shrink:0}.session-summary{flex:1;min-width:0}.session-summary-top{display:flex;align-items:baseline;gap:10px;color:var(--ink-0);font-size:13.5px;font-weight:500}.session-summary-bottom{font-size:11.5px;color:var(--ink-3);font-family:var(--font-mono);margin-top:1px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.session-live-tag{display:inline-flex;align-items:center;gap:5px;font-size:11px;color:var(--ok);font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.06em}.session-live-tag:before{content:"";width:6px;height:6px;border-radius:50%;background:var(--ok);animation:pulse-s 1.4s ease-in-out infinite}@keyframes pulse-s{0%,to{opacity:1}50%{opacity:.4}}.session-stats{display:flex;gap:14px;font-family:var(--font-mono);font-size:11px;color:var(--ink-3)}.session-stats strong{color:var(--ink-1);font-weight:500}.session-stats .stat.warn{color:var(--pending)}.session-stats .stat.err{color:var(--err)}.session-actions-menu{display:flex;gap:4px}.session-rollback{display:inline-flex;align-items:center;gap:6px;padding:4px 10px;font-size:11.5px;color:var(--ink-2);border:1px solid var(--line);border-radius:var(--r-2);background:var(--bg-2);opacity:0;transition:opacity .1s,color .1s,background .1s}.session-header:hover .session-rollback,.session-header:focus-within .session-rollback{opacity:1}.session-rollback:hover{color:var(--ink-0);background:var(--bg-3)}.session-body{border-top:1px solid var(--line);max-height:3000px;overflow:hidden;transition:max-height .2s ease-out}.session-group.collapsed .session-body{max-height:0;border-top-color:transparent}.action-row{display:grid;grid-template-columns:3px 1fr auto;align-items:center;gap:0 14px;border-top:1px solid var(--line);position:relative;transition:background .1s}.action-row:first-child{border-top:none}.action-row:hover,.action-row.focused,.action-row:focus-within{background:var(--bg-2)}.action-row.obs{opacity:.58}.action-row-open{display:grid;grid-template-columns:auto 110px 1fr auto;align-items:center;gap:14px;padding:var(--row-py, 10px) 0;background:transparent;border:none;text-align:left;width:100%;cursor:pointer;color:inherit;font:inherit}.action-row-open:focus-visible{outline:none;box-shadow:inset 3px 0 0 var(--acc);border-radius:0 var(--r-2) var(--r-2) 0}.action-row>.row-rail{padding:2px 0 2px 16px}.action-row>.row-actions{padding:var(--row-py, 10px) 16px var(--row-py, 10px) 0}.row-rail{align-self:stretch;background:transparent;border-radius:2px;margin:2px 0}.action-row[data-state=pending] .row-rail{background:var(--pending)}.action-row[data-state=blocked] .row-rail{background:var(--err)}.action-row[data-state=rejected] .row-rail{background:var(--err);opacity:.7}.action-row[data-state=error] .row-rail{background:var(--err)}.action-row[data-state=rolledback] .row-rail{background:var(--ink-4)}.action-row[data-state=approved] .row-rail{background:var(--ok)}.action-row[data-state=ok] .row-rail{background:transparent}.row-time{font-family:var(--font-mono);font-size:11px;color:var(--ink-3);white-space:nowrap}.tool-tag{display:inline-flex;align-items:center;gap:6px;padding:2px 8px;border-radius:var(--r-2);font-family:var(--font-mono);font-size:11px;background:var(--bg-2);border:1px solid var(--line);color:var(--ink-1);white-space:nowrap;line-height:1.5}.tool-tag .tdot{width:6px;height:6px;border-radius:50%;background:var(--ink-3)}.tool-tag[data-tool=write_file] .tdot{background:var(--warn)}.tool-tag[data-tool=read_file] .tdot{background:var(--info)}.tool-tag[data-tool=bash] .tdot{background:#a299b4}.tool-tag[data-tool=delete_file] .tdot{background:var(--err)}.row-intent{min-width:0;display:flex;flex-direction:column;gap:2px}.intent-line{display:flex;align-items:baseline;gap:8px;color:var(--ink-0);font-size:13.5px;line-height:1.3;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.intent-path{font-family:var(--font-mono);font-size:12.5px;color:var(--ink-1);overflow:hidden;text-overflow:ellipsis}.intent-path .dim{color:var(--ink-3)}.intent-sub{font-size:11.5px;color:var(--ink-3);font-family:var(--font-mono);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:flex;align-items:center;gap:8px}.policy-chip{display:inline-flex;align-items:center;gap:4px;padding:1px 7px;border-radius:10px;font-size:10.5px;font-family:var(--font-mono);border:1px solid var(--line);background:var(--bg-2);color:var(--ink-2);white-space:nowrap;flex-shrink:0}.policy-chip.allow{color:var(--ok);border-color:#5ec3864d;background:var(--ok-dim)}.policy-chip.block{color:var(--err);border-color:#f75d594d;background:var(--err-dim)}.policy-chip.pending{color:var(--pending);border-color:#e7b64359;background:var(--pending-dim)}.policy-chip.obs{color:var(--info);border-color:#55aee84d;background:var(--info-dim)}.status-tag{font-family:var(--font-mono);font-size:11px;color:var(--ink-2);white-space:nowrap;display:flex;align-items:center;gap:6px}.status-tag .sdot{width:6px;height:6px;border-radius:50%}.status-tag[data-s=success] .sdot{background:var(--ok)}.status-tag[data-s=pending] .sdot{background:var(--pending);animation:pulse-s 1.4s ease-in-out infinite}.status-tag[data-s=error] .sdot,.status-tag[data-s=blocked] .sdot{background:var(--err)}.status-tag[data-s=rejected] .sdot{background:var(--err);opacity:.7}.status-tag[data-s=rolledback] .sdot{background:var(--ink-4)}.status-tag[data-s=approved] .sdot{background:var(--ok)}.row-actions{display:flex;gap:6px;align-items:center}.btn{display:inline-flex;align-items:center;gap:6px;padding:4px 10px;border-radius:var(--r-2);font-size:11.5px;border:1px solid var(--line);background:var(--bg-2);color:var(--ink-1);transition:background .1s,border-color .1s,color .1s;white-space:nowrap}.btn:hover{background:var(--bg-3)}.btn.primary{background:var(--acc);border-color:var(--acc);color:var(--bg-0);font-weight:500}[data-theme=light] .btn.primary{color:#fff}.btn.primary:hover{filter:brightness(1.08)}.btn.ok{background:var(--ok-dim);border-color:#5ec38666;color:var(--ok)}.btn.ok:hover{background:#5ec38638}.btn.danger{background:var(--err-dim);border-color:#f75d5959;color:var(--err)}.btn.danger:hover{background:#f75d5938}.btn.ghost{background:transparent}.btn:disabled{opacity:.5;cursor:default}.btn-lg{padding:7px 14px;font-size:13px}.muted{color:var(--ink-3)}.error-text{color:var(--err);font-family:var(--font-mono);font-size:11px}.empty{padding:56px 24px;text-align:center;color:var(--ink-3);border:1px dashed var(--line);border-radius:var(--r-3);background:var(--bg-1)}.empty-title{color:var(--ink-1);font-size:14px;margin-bottom:4px;font-weight:500}.empty-sub{font-size:12.5px;max-width:420px;margin:0 auto;line-height:1.5}.empty-hint{margin-top:14px;padding:10px 14px;background:var(--bg-0);border:1px solid var(--line);border-radius:var(--r-2);font-family:var(--font-mono);font-size:11.5px;color:var(--ink-2);display:inline-block;text-align:left}.banner{padding:12px 16px;border-radius:var(--r-3);border:1px solid var(--line);background:var(--bg-1);font-size:12.5px;color:var(--ink-2);margin-bottom:18px;display:flex;align-items:center;gap:10px}.banner.err{border-color:#f75d5959;background:var(--err-dim);color:var(--err)}.banner.warn{border-color:#e7b64359;background:var(--pending-dim);color:var(--pending)}.skeleton{background:linear-gradient(90deg,var(--bg-1) 0%,var(--bg-2) 50%,var(--bg-1) 100%);background-size:200% 100%;animation:shimmer 1.2s linear infinite;border-radius:var(--r-2)}@keyframes shimmer{0%{background-position:200% 0}to{background-position:-200% 0}}.scrim{position:fixed;top:0;right:0;bottom:0;left:0;background:#00000073;backdrop-filter:blur(2px);-webkit-backdrop-filter:blur(2px);z-index:80;animation:fadein .15s}.drawer{position:fixed;top:0;right:0;width:var(--drawer-w);max-width:100vw;height:100vh;background:var(--bg-1);border-left:1px solid var(--line);box-shadow:var(--shadow-drawer);z-index:90;display:flex;flex-direction:column;animation:slidein .18s ease-out}@keyframes fadein{0%{opacity:0}to{opacity:1}}@keyframes slidein{0%{transform:translate(24px);opacity:0}to{transform:translate(0);opacity:1}}.drawer-head{padding:16px 20px;border-bottom:1px solid var(--line);display:flex;align-items:center;gap:12px}.drawer-head-title{font-size:14px;font-weight:600;color:var(--ink-0)}.drawer-head-sub{font-family:var(--font-mono);font-size:11px;color:var(--ink-3);margin-top:2px}.drawer-close{width:28px;height:28px;display:grid;place-items:center;border-radius:var(--r-2);color:var(--ink-2);margin-left:auto}.drawer-close:hover{background:var(--bg-2);color:var(--ink-0)}.drawer-body{flex:1;overflow-y:auto;padding:20px}.drawer-section{margin-bottom:24px}.drawer-section-title{font-size:10.5px;text-transform:uppercase;letter-spacing:.09em;color:var(--ink-3);margin-bottom:10px;font-weight:500}.kv-grid{display:grid;grid-template-columns:120px 1fr;gap:6px 16px;font-size:12.5px}.kv-grid dt{color:var(--ink-3);font-family:var(--font-mono);font-size:11.5px}.kv-grid dd{color:var(--ink-1);font-family:var(--font-mono);font-size:12px;word-break:break-all}.code-block{background:var(--bg-0);border:1px solid var(--line);border-radius:var(--r-2);padding:10px 12px;font-family:var(--font-mono);font-size:11.5px;line-height:1.55;color:var(--ink-1);white-space:pre-wrap;word-break:break-all;max-height:320px;overflow-y:auto}.code-block .comment{color:var(--ink-3)}.diff{border:1px solid var(--line);border-radius:var(--r-2);background:var(--bg-0);overflow:hidden}.diff-hdr{display:flex;font-size:10.5px;text-transform:uppercase;letter-spacing:.08em;color:var(--ink-3);background:var(--bg-2);border-bottom:1px solid var(--line)}.diff-hdr>div{flex:1;padding:6px 12px}.diff-hdr>div+div{border-left:1px solid var(--line)}.diff-body{display:flex;font-family:var(--font-mono);font-size:11px;line-height:1.55;max-height:260px;overflow:auto}.diff-body>pre{flex:1;padding:10px 12px;margin:0;white-space:pre-wrap;word-break:break-all;color:var(--ink-2)}.diff-body>pre+pre{border-left:1px solid var(--line)}.diff-body .add{background:#5ec38621;color:var(--ok);display:block;margin:0 -12px;padding:0 12px}.diff-body .del{background:#f75d5921;color:var(--err);display:block;margin:0 -12px;padding:0 12px}.drawer-foot{border-top:1px solid var(--line);padding:14px 20px;display:flex;gap:10px;align-items:center;background:var(--bg-1)}.drawer-foot .spacer{flex:1}.card{background:var(--bg-1);border:1px solid var(--line);border-radius:var(--r-3);overflow:hidden}.card-header{padding:14px 18px;border-bottom:1px solid var(--line);display:flex;align-items:center;gap:12px}.card-title{font-weight:600;color:var(--ink-0);font-size:13.5px}.card-sub{color:var(--ink-3);font-size:12px}.card-body{padding:18px}.card-body.flush{padding:0}.list{list-style:none}.list>li{padding:12px 18px;border-top:1px solid var(--line);display:flex;align-items:center;gap:14px}.list>li:first-child{border-top:none}.list-title{color:var(--ink-0);font-size:13px;font-weight:500}.list-sub{color:var(--ink-3);font-size:11.5px;font-family:var(--font-mono);margin-top:2px}.toast-stack{position:fixed;bottom:20px;left:50%;transform:translate(-50%);z-index:120;display:flex;flex-direction:column;gap:8px;pointer-events:none}.toast{pointer-events:auto;background:var(--bg-1);border:1px solid var(--line-strong);border-radius:var(--r-3);padding:10px 14px;font-size:12.5px;color:var(--ink-1);box-shadow:var(--shadow-pop);min-width:220px;max-width:480px;display:flex;gap:10px;align-items:center;animation:slideup .2s ease-out}@keyframes slideup{0%{transform:translateY(8px);opacity:0}to{transform:translateY(0);opacity:1}}.toast.ok{border-color:#5ec38666}.toast.err{border-color:#f75d5966;color:var(--err)}.modal-scrim{position:fixed;top:0;right:0;bottom:0;left:0;background:#00000080;backdrop-filter:blur(3px);-webkit-backdrop-filter:blur(3px);z-index:110;display:grid;place-items:center;padding:16px;animation:fadein .12s}.modal{width:min(480px,100%);background:var(--bg-1);border:1px solid var(--line-strong);border-radius:var(--r-3);box-shadow:var(--shadow-pop);overflow:hidden}.modal-head{padding:16px 20px;border-bottom:1px solid var(--line)}.modal-title{font-size:14px;font-weight:600;color:var(--ink-0)}.modal-body{padding:16px 20px;color:var(--ink-1);font-size:13px}.modal-body textarea,.modal-body input{margin-top:10px;width:100%;padding:8px 10px;background:var(--bg-0);border:1px solid var(--line);border-radius:var(--r-2);color:var(--ink-0);font-size:12.5px;font-family:var(--font-mono);outline:none;resize:vertical}.modal-body textarea:focus,.modal-body input:focus{border-color:var(--ink-4)}.modal-foot{padding:12px 20px;border-top:1px solid var(--line);display:flex;justify-content:flex-end;gap:8px}.tweaks{position:fixed;bottom:20px;right:20px;z-index:70;background:var(--bg-1);border:1px solid var(--line-strong);border-radius:var(--r-3);box-shadow:var(--shadow-pop);padding:14px 16px;width:260px}.tweaks h4{font-size:11px;text-transform:uppercase;letter-spacing:.08em;color:var(--ink-3);margin-bottom:10px;font-weight:500}.tweak-row{display:flex;align-items:center;justify-content:space-between;padding:5px 0;font-size:12px}.tweak-row label{color:var(--ink-1)}.tweak-group{display:inline-flex;background:var(--bg-2);padding:2px;border-radius:var(--r-2);gap:2px}.tweak-group button{padding:3px 8px;border-radius:4px;font-size:11px;color:var(--ink-2)}.tweak-group button.on{background:var(--bg-4);color:var(--ink-0)}.accent-swatches{display:flex;gap:6px}.accent-swatch{width:20px;height:20px;border-radius:50%;border:2px solid transparent;transition:border-color .1s,transform .1s;cursor:pointer}.accent-swatch.on{border-color:var(--ink-0);transform:scale(1.08)}.settings{display:grid;grid-template-columns:200px 1fr;gap:28px}.settings-nav{display:flex;flex-direction:column;gap:2px;position:sticky;top:84px;align-self:start}.settings-nav a{display:flex;align-items:center;gap:10px;padding:6px 10px;border-radius:var(--r-2);color:var(--ink-2);font-size:13px;transition:background .1s,color .1s;cursor:pointer;text-decoration:none}.settings-nav a:hover{background:var(--bg-2);color:var(--ink-1)}.settings-nav a.active{background:var(--bg-2);color:var(--ink-0)}.settings-nav a svg{opacity:.8;flex-shrink:0}.settings-section{display:flex;flex-direction:column;gap:18px;min-width:0}.settings-section h2{font-size:15px;font-weight:600;color:var(--ink-0);letter-spacing:-.01em}.form-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:10px 14px}.field{display:flex;flex-direction:column;gap:6px}.field label{font-size:11px;text-transform:uppercase;letter-spacing:.08em;color:var(--ink-3);font-weight:500}.field input,.field textarea,.field select{padding:7px 10px;background:var(--bg-0);border:1px solid var(--line);border-radius:var(--r-2);color:var(--ink-0);font-size:12.5px;font-family:var(--font-mono);outline:none;transition:border-color .1s}.field input:focus,.field textarea:focus,.field select:focus{border-color:var(--ink-4)}.field .hint{font-size:11px;color:var(--ink-3)}@media (max-width: 900px){.settings{grid-template-columns:1fr;gap:18px}.settings-nav{position:static;flex-direction:row;overflow-x:auto;border-bottom:1px solid var(--line);padding-bottom:6px}}.queue{display:grid;grid-template-columns:repeat(auto-fit,minmax(420px,1fr));gap:14px}.queue-card{display:flex;flex-direction:column}.queue-card .card-body{flex:1}.queue-card .drawer-foot{padding:12px 18px;border-top:1px solid var(--line)}.policy-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:14px}.policy-card{background:var(--bg-1);border:1px solid var(--line);border-radius:var(--r-3);padding:14px 16px}.policy-card-title{display:flex;align-items:center;gap:8px;font-size:12.5px;font-weight:600;color:var(--ink-0);margin-bottom:10px}.policy-rule{display:flex;align-items:center;gap:8px;font-family:var(--font-mono);font-size:12px;padding:5px 0;border-top:1px dashed var(--line);color:var(--ink-1)}.policy-rule:first-of-type{border-top:none}.policy-rule .tag{margin-left:auto;font-size:9.5px;color:var(--ink-3);text-transform:uppercase;letter-spacing:.08em;font-weight:600}.stat-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:12px;margin-bottom:18px}.stat-card{background:var(--bg-1);border:1px solid var(--line);border-radius:var(--r-3);padding:14px 16px}.stat-label{font-size:10.5px;text-transform:uppercase;letter-spacing:.08em;color:var(--ink-3);margin-bottom:8px}.stat-value{font-size:26px;font-weight:500;color:var(--ink-0);font-family:var(--font-sans);letter-spacing:-.02em;line-height:1}.stat-delta{font-family:var(--font-mono);font-size:11px;margin-top:6px;color:var(--ink-3)}.stat-delta.up{color:var(--ok)}.stat-delta.down{color:var(--err)}.spark{width:100%;height:50px;margin-top:10px}.drawer-body::-webkit-scrollbar,.code-block::-webkit-scrollbar{width:8px}.drawer-body::-webkit-scrollbar-thumb,.code-block::-webkit-scrollbar-thumb{background:var(--bg-3);border-radius:4px}@media (max-width: 900px){.app{grid-template-columns:1fr}.nav{position:static;height:auto;border-right:none;border-bottom:1px solid var(--line)}.page{padding:20px 16px 32px}.topbar{padding:10px 16px;gap:10px}.search{max-width:none}.action-row{grid-template-columns:3px 1fr}.action-row>.row-actions{grid-column:2;padding:0 14px 12px;justify-self:end}.action-row-open{grid-template-columns:auto 1fr;grid-template-areas:"time intent" "tool tool" "status status";gap:8px 12px;padding:12px 14px}.action-row-open>.row-time{grid-area:time}.action-row-open>.tool-tag{grid-area:tool;justify-self:start}.action-row-open>.row-intent{grid-area:intent}.action-row-open>.status-tag{grid-area:status;justify-self:start}.drawer{width:100vw}.kv-grid{grid-template-columns:100px 1fr}}
|