@cef-ai/cli 0.2.1 → 0.2.2
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/README.md +2 -2
- package/dist/commands/dev/server.js +34 -6
- package/dist/commands/dev/{ui.js → static/app.js} +50 -401
- package/dist/commands/dev/static/index.html +205 -0
- package/dist/commands/dev/static/static/app.js +633 -0
- package/dist/commands/dev/static/static/index.html +205 -0
- package/dist/commands/dev/static/static/style.css +184 -0
- package/dist/commands/dev/static/style.css +184 -0
- package/dist/lib/config.js +14 -1
- package/dist/lib/runtime/agent-router.js +2 -13
- package/dist/lib/runtime/index.js +2 -4
- package/dist/lib/transpile.d.ts +1 -0
- package/dist/lib/transpile.js +9 -0
- package/example/.env.example +2 -2
- package/package.json +6 -5
- package/dist/commands/dev/ui.d.ts +0 -1
|
@@ -0,0 +1,205 @@
|
|
|
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>CEF Dev</title>
|
|
7
|
+
<link rel="stylesheet" href="/style.css">
|
|
8
|
+
<script src="https://unpkg.com/vis-network@9.1.9/standalone/umd/vis-network.min.js"></script>
|
|
9
|
+
</head>
|
|
10
|
+
<body>
|
|
11
|
+
<div class="top-accent"></div>
|
|
12
|
+
<div id="app">
|
|
13
|
+
<div id="header">
|
|
14
|
+
<div class="header-brand">
|
|
15
|
+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none"><path d="M12 2L21 7.5V16.5L12 22L3 16.5V7.5L12 2Z" stroke="var(--accent)" stroke-width="1.5" fill="var(--accent)" fill-opacity="0.1"/><path d="M12 7L17 9.75V15.25L12 18L7 15.25V9.75L12 7Z" stroke="var(--accent)" stroke-width="1.2" fill="var(--accent)" fill-opacity="0.18"/></svg>
|
|
16
|
+
<div class="header-title"><span class="ht-main"><span>CEF</span> AI</span><span class="ht-sub">Dev Console</span></div>
|
|
17
|
+
</div>
|
|
18
|
+
<span class="version-pill">v0.9.0</span>
|
|
19
|
+
<span class="pill" id="status-pill"><span class="status-dot err" id="status-dot"></span>connecting</span>
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
<div id="sidebar"></div>
|
|
23
|
+
|
|
24
|
+
<div id="main">
|
|
25
|
+
<div class="empty-state" id="empty-state">
|
|
26
|
+
<div class="empty-hero">
|
|
27
|
+
<svg width="48" height="48" viewBox="0 0 48 48" fill="none">
|
|
28
|
+
<path d="M24 4L42 14V34L24 44L6 34V14L24 4Z" stroke="var(--accent)" stroke-width="1.5" fill="var(--accent)" fill-opacity="0.08"/>
|
|
29
|
+
<path d="M24 16L32 20.5V29.5L24 34L16 29.5V20.5L24 16Z" stroke="var(--accent)" stroke-width="1.5" fill="var(--accent)" fill-opacity="0.15"/>
|
|
30
|
+
</svg>
|
|
31
|
+
<div class="empty-title">CEF AI Dev Console</div>
|
|
32
|
+
<div class="empty-subtitle">Select an engagement, agent, or cubby from the sidebar to begin testing</div>
|
|
33
|
+
</div>
|
|
34
|
+
<div class="empty-stats" id="empty-stats"></div>
|
|
35
|
+
<div class="empty-shortcuts">
|
|
36
|
+
<div class="shortcut-item"><span class="shortcut-key">Engagements</span><span class="shortcut-desc">Trigger execution flows with payloads</span></div>
|
|
37
|
+
<div class="shortcut-item"><span class="shortcut-key">Agent Tasks</span><span class="shortcut-desc">Call individual agent functions directly</span></div>
|
|
38
|
+
<div class="shortcut-item"><span class="shortcut-key">Cubbies</span><span class="shortcut-desc">Browse tables, run SQL, visualize graphs</span></div>
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
<div class="panel" id="panel-engagement">
|
|
43
|
+
<div class="panel-title"><span class="s-dot s-dot-eng"></span> <span id="eng-name"></span> <span style="font-size:10px;font-weight:400;color:var(--text3);background:var(--surface2);border:1px solid var(--border);border-radius:4px;padding:2px 7px;margin-left:4px">engagement</span></div>
|
|
44
|
+
<div id="eng-desc" style="font-size:11px;color:var(--text3);margin-bottom:14px"></div>
|
|
45
|
+
<div id="eng-schema-hints"></div>
|
|
46
|
+
<div class="field">
|
|
47
|
+
<label class="field-label">Payload</label>
|
|
48
|
+
<textarea id="eng-payload" rows="4">{}</textarea>
|
|
49
|
+
</div>
|
|
50
|
+
<div style="display:flex;gap:8px;align-items:flex-end;margin-bottom:10px;flex-wrap:wrap">
|
|
51
|
+
<div style="display:flex;gap:6px;align-items:flex-end">
|
|
52
|
+
<div class="field" style="margin:0">
|
|
53
|
+
<label class="field-label">Repeat</label>
|
|
54
|
+
<input type="number" id="eng-repeat" value="1" min="1" max="999" style="width:70px;padding:6px 8px">
|
|
55
|
+
</div>
|
|
56
|
+
<div class="field" style="margin:0">
|
|
57
|
+
<label class="field-label">Delay ms</label>
|
|
58
|
+
<input type="number" id="eng-delay" value="300" min="0" style="width:80px;padding:6px 8px">
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
<div class="field" style="margin:0;flex:1;min-width:180px">
|
|
62
|
+
<label class="field-label">or blast from file (JSON array)</label>
|
|
63
|
+
<input type="file" id="eng-file-blast" accept=".json" style="width:100%;padding:5px 0;font-size:11px;color:var(--text2);background:none;border:none;cursor:pointer">
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
<div class="btn-row">
|
|
67
|
+
<button class="btn-primary" id="eng-start">Start</button>
|
|
68
|
+
<button class="btn-danger" id="eng-stop" style="display:none">Stop</button>
|
|
69
|
+
</div>
|
|
70
|
+
<div id="eng-result"></div>
|
|
71
|
+
<div class="stream-area" id="eng-stream-area" style="display:none">
|
|
72
|
+
<div class="panel-title" style="font-size:14px">Stream Data</div>
|
|
73
|
+
<div class="tab-bar">
|
|
74
|
+
<div class="tab active" data-stab="single">Single</div>
|
|
75
|
+
<div class="tab" data-stab="bulk">Bulk</div>
|
|
76
|
+
<div class="tab" data-stab="file">File</div>
|
|
77
|
+
</div>
|
|
78
|
+
<div id="stream-single">
|
|
79
|
+
<div class="field">
|
|
80
|
+
<label class="field-label">Packet</label>
|
|
81
|
+
<textarea id="eng-stream-payload" rows="3">{ }</textarea>
|
|
82
|
+
</div>
|
|
83
|
+
<button class="btn-primary" id="eng-push">Push</button>
|
|
84
|
+
</div>
|
|
85
|
+
<div id="stream-bulk" style="display:none">
|
|
86
|
+
<div class="field">
|
|
87
|
+
<label class="field-label">JSON Lines (one event per line) or JSON array</label>
|
|
88
|
+
<textarea id="eng-bulk-payload" rows="6">{ }
|
|
89
|
+
{ }</textarea>
|
|
90
|
+
</div>
|
|
91
|
+
<div style="display:flex;align-items:center;gap:10px;margin-bottom:10px">
|
|
92
|
+
<label class="field-label" style="margin:0;white-space:nowrap">Delay (ms)</label>
|
|
93
|
+
<input type="text" id="eng-bulk-delay" value="100" style="width:70px;padding:6px 8px">
|
|
94
|
+
</div>
|
|
95
|
+
<div class="btn-row" style="margin-top:0">
|
|
96
|
+
<button class="btn-primary" id="eng-bulk-send">Send All</button>
|
|
97
|
+
<button class="btn-danger" id="eng-bulk-stop" style="display:none">Stop</button>
|
|
98
|
+
</div>
|
|
99
|
+
<div id="eng-bulk-progress"></div>
|
|
100
|
+
</div>
|
|
101
|
+
<div id="stream-file" style="display:none">
|
|
102
|
+
<div id="file-drop-zone" style="border:2px dashed var(--border);border-radius:var(--radius);padding:28px;text-align:center;cursor:pointer;transition:border-color .15s;margin-bottom:12px">
|
|
103
|
+
<div style="color:var(--text3);font-size:12px">Drop a <strong>.json</strong> or <strong>.jsonl</strong> file here, or click to browse</div>
|
|
104
|
+
<input type="file" id="eng-file-input" accept=".json,.jsonl" style="display:none">
|
|
105
|
+
<div id="file-info" style="margin-top:8px;font:11px var(--mono);color:var(--text2)"></div>
|
|
106
|
+
</div>
|
|
107
|
+
<div style="display:flex;align-items:center;gap:10px;margin-bottom:10px">
|
|
108
|
+
<label class="field-label" style="margin:0;white-space:nowrap">Delay (ms)</label>
|
|
109
|
+
<input type="text" id="eng-file-delay" value="100" style="width:70px;padding:6px 8px">
|
|
110
|
+
</div>
|
|
111
|
+
<div class="btn-row" style="margin-top:0">
|
|
112
|
+
<button class="btn-primary" id="eng-file-send" disabled>Send File</button>
|
|
113
|
+
<button class="btn-danger" id="eng-file-stop" style="display:none">Stop</button>
|
|
114
|
+
</div>
|
|
115
|
+
<div id="eng-file-progress"></div>
|
|
116
|
+
</div>
|
|
117
|
+
<div id="eng-push-result"></div>
|
|
118
|
+
</div>
|
|
119
|
+
</div>
|
|
120
|
+
|
|
121
|
+
<div class="panel" id="panel-agent">
|
|
122
|
+
<div class="panel-title"><span class="s-dot s-dot-agent"></span> <span id="agent-label"></span> <span style="font-size:10px;font-weight:400;color:var(--text3);background:var(--surface2);border:1px solid var(--border);border-radius:4px;padding:2px 7px;margin-left:4px">agent task</span></div>
|
|
123
|
+
<div id="agent-desc" style="font-size:11px;color:var(--text3);margin-bottom:14px"></div>
|
|
124
|
+
<div id="agent-schema-hints"></div>
|
|
125
|
+
<div class="field">
|
|
126
|
+
<label class="field-label">Payload</label>
|
|
127
|
+
<textarea id="agent-payload" rows="5">{}</textarea>
|
|
128
|
+
</div>
|
|
129
|
+
<div class="btn-row">
|
|
130
|
+
<button class="btn-primary" id="agent-call">Call</button>
|
|
131
|
+
<button class="btn-secondary" id="agent-batch" style="display:none">Run Batch</button>
|
|
132
|
+
</div>
|
|
133
|
+
<div id="agent-result"></div>
|
|
134
|
+
</div>
|
|
135
|
+
|
|
136
|
+
<div class="panel" id="panel-cubby">
|
|
137
|
+
<div class="panel-title"><span class="s-dot s-dot-cubby"></span> <span id="cubby-label"></span></div>
|
|
138
|
+
<div class="tab-bar">
|
|
139
|
+
<div class="tab active" data-tab="browse">Browse</div>
|
|
140
|
+
<div class="tab" data-tab="sql">SQL</div>
|
|
141
|
+
<div class="tab" data-tab="graph" id="graph-tab" style="display:none">Graph</div>
|
|
142
|
+
</div>
|
|
143
|
+
<div id="cubby-browse"><div id="cubby-tables"></div></div>
|
|
144
|
+
<div id="cubby-sql" style="display:none">
|
|
145
|
+
<div style="display:flex;gap:8px;align-items:flex-end;margin-bottom:10px">
|
|
146
|
+
<div class="field" style="flex:1;margin:0">
|
|
147
|
+
<label class="field-label">Saved Queries</label>
|
|
148
|
+
<select id="query-select" style="width:100%;background:var(--surface2);color:var(--text);border:1px solid var(--border);border-radius:var(--radius);padding:7px 10px;font-size:12px;outline:none">
|
|
149
|
+
<option value="">— select a saved query —</option>
|
|
150
|
+
</select>
|
|
151
|
+
</div>
|
|
152
|
+
<button class="btn-ghost" id="query-delete" title="Delete selected query" style="display:none;padding:7px 10px">✕</button>
|
|
153
|
+
</div>
|
|
154
|
+
<div class="field">
|
|
155
|
+
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:6px">
|
|
156
|
+
<label class="field-label" style="margin:0">Query</label>
|
|
157
|
+
<div style="display:flex;gap:6px;align-items:center">
|
|
158
|
+
<input type="text" id="query-name" placeholder="Name to save as..." style="width:160px;padding:4px 8px;font-size:11px;border-radius:5px;background:var(--surface2);border:1px solid var(--border);color:var(--text)">
|
|
159
|
+
<button class="btn-ghost" id="query-save" style="padding:4px 10px;font-size:11px">Save</button>
|
|
160
|
+
</div>
|
|
161
|
+
</div>
|
|
162
|
+
<textarea id="cubby-sql-input" rows="4">SELECT * FROM </textarea>
|
|
163
|
+
</div>
|
|
164
|
+
<button class="btn-primary" id="cubby-run">Run</button>
|
|
165
|
+
<div id="cubby-sql-result"></div>
|
|
166
|
+
</div>
|
|
167
|
+
<div id="cubby-graph">
|
|
168
|
+
<div id="graph-stats"></div>
|
|
169
|
+
<div style="position:relative">
|
|
170
|
+
<div id="graph-canvas"></div>
|
|
171
|
+
<div id="graph-detail">
|
|
172
|
+
<div id="gd-type-badge" class="gd-type"></div>
|
|
173
|
+
<div id="gd-attrs"></div>
|
|
174
|
+
</div>
|
|
175
|
+
<div id="graph-legend">
|
|
176
|
+
<div class="gl-row"><div class="gl-dot" style="background:#6366f1"></div>artifact</div>
|
|
177
|
+
<div class="gl-row"><div class="gl-dot" style="background:#f59e0b"></div>work</div>
|
|
178
|
+
<div class="gl-row"><div class="gl-dot" style="background:#22c55e"></div>operator</div>
|
|
179
|
+
<div class="gl-row"><div class="gl-dot" style="background:#ec4899"></div>eval</div>
|
|
180
|
+
<div class="gl-row"><div class="gl-dot" style="background:#71717a"></div>artifact_map</div>
|
|
181
|
+
</div>
|
|
182
|
+
</div>
|
|
183
|
+
</div>
|
|
184
|
+
</div>
|
|
185
|
+
</div>
|
|
186
|
+
|
|
187
|
+
<div id="bottom" style="height:220px">
|
|
188
|
+
<div id="resize-handle"></div>
|
|
189
|
+
<div class="bottom-bar">
|
|
190
|
+
<div class="btab active" data-btab="logs">Logs</div>
|
|
191
|
+
<div class="btab" data-btab="flow">Flow</div>
|
|
192
|
+
<div class="bottom-actions">
|
|
193
|
+
<input type="text" id="log-filter" placeholder="Filter...">
|
|
194
|
+
<button class="icon-btn" id="btn-clear" title="Clear">✕</button>
|
|
195
|
+
<button class="icon-btn" id="btn-collapse" title="Collapse">▼</button>
|
|
196
|
+
</div>
|
|
197
|
+
</div>
|
|
198
|
+
<div id="log-container"></div>
|
|
199
|
+
<div id="flow-container" style="display:none"></div>
|
|
200
|
+
</div>
|
|
201
|
+
</div>
|
|
202
|
+
|
|
203
|
+
<script src="/app.js"></script>
|
|
204
|
+
</body>
|
|
205
|
+
</html>
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
*{margin:0;padding:0;box-sizing:border-box}
|
|
2
|
+
:root{
|
|
3
|
+
--bg:#09090b;--surface:#111113;--surface2:#18181b;--surface3:#1e1e22;
|
|
4
|
+
--border:#27272a;--border2:#3f3f46;
|
|
5
|
+
--text:#fafafa;--text2:#a1a1aa;--text3:#71717a;
|
|
6
|
+
--accent:#3b82f6;--accent2:#2563eb;
|
|
7
|
+
--success:#22c55e;--warn:#f59e0b;--error:#ef4444;
|
|
8
|
+
--mono:'SF Mono','Fira Code','JetBrains Mono','Consolas',monospace;
|
|
9
|
+
--sans:system-ui,-apple-system,'Segoe UI',sans-serif;
|
|
10
|
+
--radius:8px;
|
|
11
|
+
}
|
|
12
|
+
html,body{height:100%;background:var(--bg);color:var(--text);font:13px/1.5 var(--sans);overflow:hidden}
|
|
13
|
+
::-webkit-scrollbar{width:6px;height:6px}
|
|
14
|
+
::-webkit-scrollbar-track{background:transparent}
|
|
15
|
+
::-webkit-scrollbar-thumb{background:var(--border);border-radius:3px}
|
|
16
|
+
::-webkit-scrollbar-thumb:hover{background:var(--border2)}
|
|
17
|
+
::selection{background:var(--accent);color:#fff}
|
|
18
|
+
|
|
19
|
+
button{font:12px/1 var(--sans);cursor:pointer;border:none;border-radius:6px;padding:7px 16px;font-weight:500;transition:all .12s ease}
|
|
20
|
+
button:hover{filter:brightness(1.1)}
|
|
21
|
+
button:active{transform:scale(.98)}
|
|
22
|
+
textarea,input[type=text]{font:12px/1.5 var(--mono);background:var(--surface2);color:var(--text);border:1px solid var(--border);border-radius:var(--radius);padding:10px 12px;resize:vertical;width:100%;outline:none;transition:border-color .15s,box-shadow .15s}
|
|
23
|
+
textarea:focus,input[type=text]:focus{border-color:var(--accent);box-shadow:0 0 0 2px rgba(59,130,246,.15)}
|
|
24
|
+
pre{font:12px/1.5 var(--mono);white-space:pre-wrap;word-break:break-all}
|
|
25
|
+
table{width:100%;border-collapse:collapse;font:12px/1.4 var(--mono)}
|
|
26
|
+
th,td{text-align:left;padding:6px 10px;border-bottom:1px solid var(--border)}
|
|
27
|
+
th{color:var(--text3);font-weight:500;font-size:11px;text-transform:uppercase;letter-spacing:.3px;position:sticky;top:0;background:var(--surface2)}
|
|
28
|
+
td{color:var(--text2)}
|
|
29
|
+
tr:hover td{background:rgba(255,255,255,.02)}
|
|
30
|
+
|
|
31
|
+
/* Layout */
|
|
32
|
+
#app{display:grid;grid-template-columns:200px 1fr;grid-template-rows:48px 1fr auto;height:100vh}
|
|
33
|
+
|
|
34
|
+
/* Header */
|
|
35
|
+
#header{grid-column:1/-1;display:flex;align-items:center;padding:0 20px;background:linear-gradient(135deg, var(--surface) 0%, #0d1117 100%);border-bottom:1px solid var(--border);gap:10px}
|
|
36
|
+
.pill{font:10px/1 var(--mono);color:var(--text3);background:var(--surface2);padding:3px 10px;border-radius:12px;border:1px solid var(--border)}
|
|
37
|
+
|
|
38
|
+
/* Sidebar */
|
|
39
|
+
#sidebar{background:var(--surface);border-right:1px solid var(--border);overflow-y:auto;padding:4px 0}
|
|
40
|
+
.s-label{font:10px/1 var(--sans);color:var(--text3);text-transform:uppercase;letter-spacing:.8px;padding:16px 14px 6px;font-weight:600}
|
|
41
|
+
.s-item{display:flex;align-items:center;padding:6px 14px;cursor:pointer;font-size:12px;color:var(--text2);gap:8px;border-left:2px solid transparent;transition:all .1s}
|
|
42
|
+
.s-item:hover{background:var(--surface2);color:var(--text)}
|
|
43
|
+
.s-item.active{background:var(--surface2);color:var(--text);border-left-color:var(--accent);box-shadow:inset 3px 0 8px -3px var(--accent)}
|
|
44
|
+
.s-dot{width:7px;height:7px;border-radius:50%;flex-shrink:0}
|
|
45
|
+
.s-dot-eng{background:var(--accent)}
|
|
46
|
+
.s-dot-agent{background:var(--success)}
|
|
47
|
+
.s-dot-cubby{background:var(--warn)}
|
|
48
|
+
.s-task{color:var(--text3);font-weight:400}
|
|
49
|
+
|
|
50
|
+
/* Main */
|
|
51
|
+
#main{overflow-y:auto;padding:24px 28px;background:var(--bg);background-image:radial-gradient(circle at 25% 25%, rgba(59,130,246,.015) 0%, transparent 50%),radial-gradient(circle at 75% 75%, rgba(99,102,241,.015) 0%, transparent 50%)}
|
|
52
|
+
|
|
53
|
+
/* Panels */
|
|
54
|
+
.panel{display:none;max-width:600px;animation:fadeIn .15s ease}.panel.active{display:block}.panel.active.panel-wide{max-width:none}
|
|
55
|
+
@keyframes fadeIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
|
|
56
|
+
.panel-title{font-size:17px;font-weight:600;margin-bottom:20px;display:flex;align-items:center;gap:10px;padding-bottom:14px;border-bottom:1px solid var(--border)}
|
|
57
|
+
.field{margin-bottom:14px}
|
|
58
|
+
.field-label{display:block;font-size:10px;color:var(--text3);margin-bottom:6px;text-transform:uppercase;letter-spacing:.5px;font-weight:600}
|
|
59
|
+
.btn-primary{background:linear-gradient(135deg, var(--accent) 0%, #6366f1 100%);color:#fff;letter-spacing:.3px;font-weight:600}
|
|
60
|
+
.btn-primary:hover{background:linear-gradient(135deg, var(--accent2) 0%, #4f46e5 100%)}
|
|
61
|
+
.btn-secondary{background:var(--surface2);color:var(--text2);border:1px solid var(--border)}
|
|
62
|
+
.btn-secondary:hover{background:var(--surface3,#3f3f46);color:var(--text1)}
|
|
63
|
+
.btn-danger{background:#991b1b;color:#fca5a5}
|
|
64
|
+
.btn-danger:hover{background:#b91c1c}
|
|
65
|
+
.btn-ghost{background:transparent;color:var(--text2);border:1px solid var(--border);padding:6px 12px}
|
|
66
|
+
.btn-ghost:hover{background:var(--surface2);color:var(--text)}
|
|
67
|
+
.btn-row{display:flex;gap:8px;margin:14px 0}
|
|
68
|
+
.result{margin-top:14px;padding:14px;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);max-height:280px;overflow:auto;box-shadow:0 2px 8px rgba(0,0,0,.3)}
|
|
69
|
+
.result-meta{font-size:11px;color:var(--text3);margin-top:6px;display:flex;gap:12px}
|
|
70
|
+
.badge{display:inline-block;font:10px/1 var(--mono);padding:2px 6px;border-radius:4px}
|
|
71
|
+
.badge-ok{background:#052e16;color:var(--success);border:1px solid #14532d;font-weight:600;letter-spacing:.3px}
|
|
72
|
+
.badge-err{background:#1c0a0a;color:var(--error);border:1px solid #450a0a;font-weight:600;letter-spacing:.3px}
|
|
73
|
+
.error-banner{background:#1c0a0a;border:1px solid #450a0a;color:#fca5a5;padding:10px 14px;border-radius:var(--radius);margin-top:10px;font-size:12px}
|
|
74
|
+
.success-banner{background:#052e16;border:1px solid #14532d;color:#86efac;padding:10px 14px;border-radius:var(--radius);margin-top:10px;font-size:12px}
|
|
75
|
+
.table-wrap{max-height:calc(100vh - 280px);overflow:auto;border:1px solid var(--border);border-radius:var(--radius);background:var(--surface)}
|
|
76
|
+
#cubby-sql-result .table-wrap{max-height:calc(100vh - 600px)}
|
|
77
|
+
|
|
78
|
+
/* Schema cards */
|
|
79
|
+
.schema-pair{display:flex;gap:12px;margin-bottom:16px}
|
|
80
|
+
.schema-card{flex:1;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:12px 14px;box-shadow:0 2px 8px rgba(0,0,0,.3)}
|
|
81
|
+
.schema-card .st-title{font:10px/1 var(--sans);text-transform:uppercase;letter-spacing:.6px;color:var(--text3);margin-bottom:8px;font-weight:600}
|
|
82
|
+
.schema-row{font:11px/1.8 var(--mono);display:flex;align-items:baseline;gap:6px;flex-wrap:wrap}
|
|
83
|
+
.schema-row .sk{color:var(--text)}
|
|
84
|
+
.schema-row .st{color:var(--accent);opacity:.7}
|
|
85
|
+
.schema-row .sr{font:9px/1 var(--sans);color:var(--error);background:#1c0a0a;padding:1px 4px;border-radius:3px}
|
|
86
|
+
.schema-row .sd{color:var(--text3);font-size:10px;font-style:italic}
|
|
87
|
+
|
|
88
|
+
/* Cubby tabs */
|
|
89
|
+
.tab-bar{display:flex;gap:0;margin-bottom:16px;border-bottom:1px solid var(--border)}
|
|
90
|
+
.tab{padding:7px 16px;font-size:12px;color:var(--text3);cursor:pointer;border-bottom:2px solid transparent;font-weight:500;transition:color .1s}
|
|
91
|
+
.tab:hover{color:var(--text2)}
|
|
92
|
+
.tab.active{color:var(--text);border-bottom-color:var(--accent)}
|
|
93
|
+
.cubby-table-item{display:flex;align-items:center;padding:10px 14px;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);margin-bottom:8px;cursor:pointer;transition:border-color .1s}
|
|
94
|
+
.cubby-table-item:hover{border-color:var(--border2)}
|
|
95
|
+
.cubby-table-item .cti-name{font:13px/1 var(--mono);font-weight:500}
|
|
96
|
+
.cubby-table-item .cti-count{font-size:11px;color:var(--text3);margin-left:auto}
|
|
97
|
+
|
|
98
|
+
/* Stream push */
|
|
99
|
+
.stream-area{margin-top:20px;padding-top:20px;border-top:1px solid var(--border)}
|
|
100
|
+
.stream-area .tab-bar{margin-bottom:14px}
|
|
101
|
+
#file-drop-zone.drag-over{border-color:var(--accent);background:rgba(59,130,246,.05)}
|
|
102
|
+
.progress-bar{height:3px;background:var(--surface2);border-radius:2px;margin:8px 0;overflow:hidden}
|
|
103
|
+
.progress-bar .fill{height:100%;background:var(--accent);transition:width .1s;border-radius:2px}
|
|
104
|
+
.progress-text{font:11px var(--mono);color:var(--text3)}
|
|
105
|
+
|
|
106
|
+
/* Bottom panel (resizable) */
|
|
107
|
+
#bottom{grid-column:1/-1;display:flex;flex-direction:column;background:var(--surface);min-height:40px;max-height:60vh}
|
|
108
|
+
#bottom.collapsed{min-height:40px;max-height:40px}
|
|
109
|
+
#bottom.collapsed #log-container,#bottom.collapsed #flow-container{display:none!important}
|
|
110
|
+
|
|
111
|
+
#resize-handle{height:3px;background:transparent;cursor:ns-resize;flex-shrink:0;position:relative}
|
|
112
|
+
#resize-handle::after{content:'';position:absolute;left:50%;top:0;transform:translateX(-50%);width:40px;height:3px;background:var(--border);border-radius:2px;transition:background .15s}
|
|
113
|
+
#resize-handle:hover::after{background:var(--accent)}
|
|
114
|
+
|
|
115
|
+
.bottom-bar{display:flex;align-items:center;padding:0 12px;height:36px;flex-shrink:0;border-top:1px solid var(--border);background:linear-gradient(180deg, var(--surface2) 0%, var(--surface) 100%);gap:2px}
|
|
116
|
+
.btab{padding:4px 12px;font-size:11px;color:var(--text3);cursor:pointer;border-radius:4px;font-weight:500;transition:all .1s;user-select:none}
|
|
117
|
+
.btab:hover{color:var(--text2);background:var(--surface2)}
|
|
118
|
+
.btab.active{color:var(--text);background:var(--surface2)}
|
|
119
|
+
.bottom-actions{margin-left:auto;display:flex;align-items:center;gap:6px}
|
|
120
|
+
#log-filter{width:140px;font-size:11px;padding:4px 8px;border-radius:5px;background:var(--surface2);border:1px solid var(--border)}
|
|
121
|
+
.icon-btn{background:none;border:none;color:var(--text3);cursor:pointer;padding:4px;border-radius:4px;font-size:14px;line-height:1;display:flex;align-items:center}
|
|
122
|
+
.icon-btn:hover{color:var(--text);background:var(--surface2)}
|
|
123
|
+
|
|
124
|
+
#log-container,#flow-container{flex:1;overflow-y:auto;font:11px/1.7 var(--mono);padding:6px 14px}
|
|
125
|
+
.log-line{padding:2px 0;border-bottom:1px solid rgba(255,255,255,.03)}
|
|
126
|
+
.log-line .ts{color:var(--text3);margin-right:6px}
|
|
127
|
+
.log-line .agent{color:var(--accent);margin-right:4px}
|
|
128
|
+
.log-line.warn{color:var(--warn)}
|
|
129
|
+
.log-line.warn .ts{color:var(--warn);opacity:.5}
|
|
130
|
+
.log-line.error{color:var(--error)}
|
|
131
|
+
.log-line.error .ts{color:var(--error);opacity:.5}
|
|
132
|
+
|
|
133
|
+
.flow-entry{padding:2px 0;font-size:11px;line-height:1.6;white-space:pre}
|
|
134
|
+
.fe-time{color:var(--text3)}
|
|
135
|
+
.fe-type{font-weight:500}
|
|
136
|
+
.fe-eng{color:var(--accent)}
|
|
137
|
+
.fe-agent{color:var(--success)}
|
|
138
|
+
.fe-cubby{color:var(--warn)}
|
|
139
|
+
.fe-err{color:var(--error)}
|
|
140
|
+
.fe-ok{color:var(--success)}
|
|
141
|
+
.fe-dim{color:var(--text3)}
|
|
142
|
+
|
|
143
|
+
/* Empty state */
|
|
144
|
+
.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;color:var(--text3);gap:8px}
|
|
145
|
+
.empty-state .empty-icon{font-size:32px;opacity:.3}
|
|
146
|
+
.empty{color:var(--text3);font-size:12px;padding:24px 0;text-align:center}
|
|
147
|
+
|
|
148
|
+
/* Graph panel */
|
|
149
|
+
#cubby-graph{position:relative}
|
|
150
|
+
#graph-canvas{width:100%;height:calc(100vh - 200px);min-height:400px;border:1px solid var(--border);border-radius:var(--radius);background:var(--surface);overflow:hidden}
|
|
151
|
+
#graph-detail{position:absolute;top:8px;right:8px;width:260px;background:var(--surface2);border:1px solid var(--border2);border-radius:var(--radius);padding:12px;display:none;font-size:11px;max-height:400px;overflow-y:auto}
|
|
152
|
+
#graph-detail .gd-type{font:10px/1 var(--sans);text-transform:uppercase;letter-spacing:.5px;font-weight:600;margin-bottom:8px;padding:2px 6px;border-radius:3px;display:inline-block}
|
|
153
|
+
.gd-row{display:flex;gap:6px;margin-bottom:4px;line-height:1.4}
|
|
154
|
+
.gd-key{color:var(--text3);min-width:80px;flex-shrink:0}
|
|
155
|
+
.gd-val{color:var(--text2);word-break:break-all}
|
|
156
|
+
#graph-legend{position:absolute;bottom:8px;left:8px;background:var(--surface2);border:1px solid var(--border);border-radius:6px;padding:8px 12px;font-size:10px}
|
|
157
|
+
#graph-legend .gl-row{display:flex;align-items:center;gap:6px;color:var(--text3);margin-bottom:3px}
|
|
158
|
+
#graph-legend .gl-row:last-child{margin-bottom:0}
|
|
159
|
+
.gl-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0}
|
|
160
|
+
#graph-stats{font:11px var(--mono);color:var(--text3);margin-bottom:8px}
|
|
161
|
+
|
|
162
|
+
/* Polish */
|
|
163
|
+
.top-accent{position:fixed;top:0;left:0;right:0;height:2px;background:linear-gradient(90deg,var(--accent),#6366f1,#8b5cf6);z-index:100}
|
|
164
|
+
.empty-hero{display:flex;flex-direction:column;align-items:center;gap:12px;margin-bottom:28px}
|
|
165
|
+
.empty-title{font-size:18px;font-weight:600;color:var(--text);letter-spacing:.5px}
|
|
166
|
+
.empty-subtitle{font-size:12px;color:var(--text3);max-width:340px;text-align:center;line-height:1.6}
|
|
167
|
+
.empty-stats{display:flex;gap:16px;margin-bottom:28px}
|
|
168
|
+
.stat-card{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:14px 20px;min-width:120px;text-align:center}
|
|
169
|
+
.stat-card .stat-num{font-size:22px;font-weight:700;color:var(--text)}
|
|
170
|
+
.stat-card .stat-label{font-size:10px;color:var(--text3);text-transform:uppercase;letter-spacing:.5px;margin-top:4px}
|
|
171
|
+
.empty-shortcuts{display:flex;flex-direction:column;gap:8px;max-width:380px;width:100%}
|
|
172
|
+
.shortcut-item{display:flex;align-items:center;gap:12px;padding:10px 14px;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);transition:border-color .15s}
|
|
173
|
+
.shortcut-item:hover{border-color:var(--border2)}
|
|
174
|
+
.shortcut-key{font:11px/1 var(--mono);font-weight:600;color:var(--accent);min-width:100px}
|
|
175
|
+
.shortcut-desc{font-size:11px;color:var(--text3)}
|
|
176
|
+
.status-dot{width:7px;height:7px;border-radius:50%;display:inline-block;margin-right:4px;vertical-align:middle}
|
|
177
|
+
.status-dot.ok{background:var(--success);box-shadow:0 0 6px rgba(34,197,94,.4)}
|
|
178
|
+
.status-dot.err{background:var(--error);box-shadow:0 0 6px rgba(239,68,68,.4)}
|
|
179
|
+
.header-brand{display:flex;align-items:center;gap:8px}
|
|
180
|
+
.header-title{display:flex;flex-direction:column;line-height:1}
|
|
181
|
+
.header-title .ht-main{font-size:13px;font-weight:700;letter-spacing:1.2px;text-transform:uppercase;color:var(--text2)}
|
|
182
|
+
.header-title .ht-main span{color:var(--accent)}
|
|
183
|
+
.header-title .ht-sub{font-size:9px;color:var(--text3);letter-spacing:.6px;text-transform:uppercase}
|
|
184
|
+
.version-pill{font:9px/1 var(--mono);color:var(--text3);background:var(--surface2);padding:2px 8px;border-radius:10px;border:1px solid var(--border)}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
*{margin:0;padding:0;box-sizing:border-box}
|
|
2
|
+
:root{
|
|
3
|
+
--bg:#09090b;--surface:#111113;--surface2:#18181b;--surface3:#1e1e22;
|
|
4
|
+
--border:#27272a;--border2:#3f3f46;
|
|
5
|
+
--text:#fafafa;--text2:#a1a1aa;--text3:#71717a;
|
|
6
|
+
--accent:#3b82f6;--accent2:#2563eb;
|
|
7
|
+
--success:#22c55e;--warn:#f59e0b;--error:#ef4444;
|
|
8
|
+
--mono:'SF Mono','Fira Code','JetBrains Mono','Consolas',monospace;
|
|
9
|
+
--sans:system-ui,-apple-system,'Segoe UI',sans-serif;
|
|
10
|
+
--radius:8px;
|
|
11
|
+
}
|
|
12
|
+
html,body{height:100%;background:var(--bg);color:var(--text);font:13px/1.5 var(--sans);overflow:hidden}
|
|
13
|
+
::-webkit-scrollbar{width:6px;height:6px}
|
|
14
|
+
::-webkit-scrollbar-track{background:transparent}
|
|
15
|
+
::-webkit-scrollbar-thumb{background:var(--border);border-radius:3px}
|
|
16
|
+
::-webkit-scrollbar-thumb:hover{background:var(--border2)}
|
|
17
|
+
::selection{background:var(--accent);color:#fff}
|
|
18
|
+
|
|
19
|
+
button{font:12px/1 var(--sans);cursor:pointer;border:none;border-radius:6px;padding:7px 16px;font-weight:500;transition:all .12s ease}
|
|
20
|
+
button:hover{filter:brightness(1.1)}
|
|
21
|
+
button:active{transform:scale(.98)}
|
|
22
|
+
textarea,input[type=text]{font:12px/1.5 var(--mono);background:var(--surface2);color:var(--text);border:1px solid var(--border);border-radius:var(--radius);padding:10px 12px;resize:vertical;width:100%;outline:none;transition:border-color .15s,box-shadow .15s}
|
|
23
|
+
textarea:focus,input[type=text]:focus{border-color:var(--accent);box-shadow:0 0 0 2px rgba(59,130,246,.15)}
|
|
24
|
+
pre{font:12px/1.5 var(--mono);white-space:pre-wrap;word-break:break-all}
|
|
25
|
+
table{width:100%;border-collapse:collapse;font:12px/1.4 var(--mono)}
|
|
26
|
+
th,td{text-align:left;padding:6px 10px;border-bottom:1px solid var(--border)}
|
|
27
|
+
th{color:var(--text3);font-weight:500;font-size:11px;text-transform:uppercase;letter-spacing:.3px;position:sticky;top:0;background:var(--surface2)}
|
|
28
|
+
td{color:var(--text2)}
|
|
29
|
+
tr:hover td{background:rgba(255,255,255,.02)}
|
|
30
|
+
|
|
31
|
+
/* Layout */
|
|
32
|
+
#app{display:grid;grid-template-columns:200px 1fr;grid-template-rows:48px 1fr auto;height:100vh}
|
|
33
|
+
|
|
34
|
+
/* Header */
|
|
35
|
+
#header{grid-column:1/-1;display:flex;align-items:center;padding:0 20px;background:linear-gradient(135deg, var(--surface) 0%, #0d1117 100%);border-bottom:1px solid var(--border);gap:10px}
|
|
36
|
+
.pill{font:10px/1 var(--mono);color:var(--text3);background:var(--surface2);padding:3px 10px;border-radius:12px;border:1px solid var(--border)}
|
|
37
|
+
|
|
38
|
+
/* Sidebar */
|
|
39
|
+
#sidebar{background:var(--surface);border-right:1px solid var(--border);overflow-y:auto;padding:4px 0}
|
|
40
|
+
.s-label{font:10px/1 var(--sans);color:var(--text3);text-transform:uppercase;letter-spacing:.8px;padding:16px 14px 6px;font-weight:600}
|
|
41
|
+
.s-item{display:flex;align-items:center;padding:6px 14px;cursor:pointer;font-size:12px;color:var(--text2);gap:8px;border-left:2px solid transparent;transition:all .1s}
|
|
42
|
+
.s-item:hover{background:var(--surface2);color:var(--text)}
|
|
43
|
+
.s-item.active{background:var(--surface2);color:var(--text);border-left-color:var(--accent);box-shadow:inset 3px 0 8px -3px var(--accent)}
|
|
44
|
+
.s-dot{width:7px;height:7px;border-radius:50%;flex-shrink:0}
|
|
45
|
+
.s-dot-eng{background:var(--accent)}
|
|
46
|
+
.s-dot-agent{background:var(--success)}
|
|
47
|
+
.s-dot-cubby{background:var(--warn)}
|
|
48
|
+
.s-task{color:var(--text3);font-weight:400}
|
|
49
|
+
|
|
50
|
+
/* Main */
|
|
51
|
+
#main{overflow-y:auto;padding:24px 28px;background:var(--bg);background-image:radial-gradient(circle at 25% 25%, rgba(59,130,246,.015) 0%, transparent 50%),radial-gradient(circle at 75% 75%, rgba(99,102,241,.015) 0%, transparent 50%)}
|
|
52
|
+
|
|
53
|
+
/* Panels */
|
|
54
|
+
.panel{display:none;max-width:600px;animation:fadeIn .15s ease}.panel.active{display:block}.panel.active.panel-wide{max-width:none}
|
|
55
|
+
@keyframes fadeIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
|
|
56
|
+
.panel-title{font-size:17px;font-weight:600;margin-bottom:20px;display:flex;align-items:center;gap:10px;padding-bottom:14px;border-bottom:1px solid var(--border)}
|
|
57
|
+
.field{margin-bottom:14px}
|
|
58
|
+
.field-label{display:block;font-size:10px;color:var(--text3);margin-bottom:6px;text-transform:uppercase;letter-spacing:.5px;font-weight:600}
|
|
59
|
+
.btn-primary{background:linear-gradient(135deg, var(--accent) 0%, #6366f1 100%);color:#fff;letter-spacing:.3px;font-weight:600}
|
|
60
|
+
.btn-primary:hover{background:linear-gradient(135deg, var(--accent2) 0%, #4f46e5 100%)}
|
|
61
|
+
.btn-secondary{background:var(--surface2);color:var(--text2);border:1px solid var(--border)}
|
|
62
|
+
.btn-secondary:hover{background:var(--surface3,#3f3f46);color:var(--text1)}
|
|
63
|
+
.btn-danger{background:#991b1b;color:#fca5a5}
|
|
64
|
+
.btn-danger:hover{background:#b91c1c}
|
|
65
|
+
.btn-ghost{background:transparent;color:var(--text2);border:1px solid var(--border);padding:6px 12px}
|
|
66
|
+
.btn-ghost:hover{background:var(--surface2);color:var(--text)}
|
|
67
|
+
.btn-row{display:flex;gap:8px;margin:14px 0}
|
|
68
|
+
.result{margin-top:14px;padding:14px;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);max-height:280px;overflow:auto;box-shadow:0 2px 8px rgba(0,0,0,.3)}
|
|
69
|
+
.result-meta{font-size:11px;color:var(--text3);margin-top:6px;display:flex;gap:12px}
|
|
70
|
+
.badge{display:inline-block;font:10px/1 var(--mono);padding:2px 6px;border-radius:4px}
|
|
71
|
+
.badge-ok{background:#052e16;color:var(--success);border:1px solid #14532d;font-weight:600;letter-spacing:.3px}
|
|
72
|
+
.badge-err{background:#1c0a0a;color:var(--error);border:1px solid #450a0a;font-weight:600;letter-spacing:.3px}
|
|
73
|
+
.error-banner{background:#1c0a0a;border:1px solid #450a0a;color:#fca5a5;padding:10px 14px;border-radius:var(--radius);margin-top:10px;font-size:12px}
|
|
74
|
+
.success-banner{background:#052e16;border:1px solid #14532d;color:#86efac;padding:10px 14px;border-radius:var(--radius);margin-top:10px;font-size:12px}
|
|
75
|
+
.table-wrap{max-height:calc(100vh - 280px);overflow:auto;border:1px solid var(--border);border-radius:var(--radius);background:var(--surface)}
|
|
76
|
+
#cubby-sql-result .table-wrap{max-height:calc(100vh - 600px)}
|
|
77
|
+
|
|
78
|
+
/* Schema cards */
|
|
79
|
+
.schema-pair{display:flex;gap:12px;margin-bottom:16px}
|
|
80
|
+
.schema-card{flex:1;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:12px 14px;box-shadow:0 2px 8px rgba(0,0,0,.3)}
|
|
81
|
+
.schema-card .st-title{font:10px/1 var(--sans);text-transform:uppercase;letter-spacing:.6px;color:var(--text3);margin-bottom:8px;font-weight:600}
|
|
82
|
+
.schema-row{font:11px/1.8 var(--mono);display:flex;align-items:baseline;gap:6px;flex-wrap:wrap}
|
|
83
|
+
.schema-row .sk{color:var(--text)}
|
|
84
|
+
.schema-row .st{color:var(--accent);opacity:.7}
|
|
85
|
+
.schema-row .sr{font:9px/1 var(--sans);color:var(--error);background:#1c0a0a;padding:1px 4px;border-radius:3px}
|
|
86
|
+
.schema-row .sd{color:var(--text3);font-size:10px;font-style:italic}
|
|
87
|
+
|
|
88
|
+
/* Cubby tabs */
|
|
89
|
+
.tab-bar{display:flex;gap:0;margin-bottom:16px;border-bottom:1px solid var(--border)}
|
|
90
|
+
.tab{padding:7px 16px;font-size:12px;color:var(--text3);cursor:pointer;border-bottom:2px solid transparent;font-weight:500;transition:color .1s}
|
|
91
|
+
.tab:hover{color:var(--text2)}
|
|
92
|
+
.tab.active{color:var(--text);border-bottom-color:var(--accent)}
|
|
93
|
+
.cubby-table-item{display:flex;align-items:center;padding:10px 14px;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);margin-bottom:8px;cursor:pointer;transition:border-color .1s}
|
|
94
|
+
.cubby-table-item:hover{border-color:var(--border2)}
|
|
95
|
+
.cubby-table-item .cti-name{font:13px/1 var(--mono);font-weight:500}
|
|
96
|
+
.cubby-table-item .cti-count{font-size:11px;color:var(--text3);margin-left:auto}
|
|
97
|
+
|
|
98
|
+
/* Stream push */
|
|
99
|
+
.stream-area{margin-top:20px;padding-top:20px;border-top:1px solid var(--border)}
|
|
100
|
+
.stream-area .tab-bar{margin-bottom:14px}
|
|
101
|
+
#file-drop-zone.drag-over{border-color:var(--accent);background:rgba(59,130,246,.05)}
|
|
102
|
+
.progress-bar{height:3px;background:var(--surface2);border-radius:2px;margin:8px 0;overflow:hidden}
|
|
103
|
+
.progress-bar .fill{height:100%;background:var(--accent);transition:width .1s;border-radius:2px}
|
|
104
|
+
.progress-text{font:11px var(--mono);color:var(--text3)}
|
|
105
|
+
|
|
106
|
+
/* Bottom panel (resizable) */
|
|
107
|
+
#bottom{grid-column:1/-1;display:flex;flex-direction:column;background:var(--surface);min-height:40px;max-height:60vh}
|
|
108
|
+
#bottom.collapsed{min-height:40px;max-height:40px}
|
|
109
|
+
#bottom.collapsed #log-container,#bottom.collapsed #flow-container{display:none!important}
|
|
110
|
+
|
|
111
|
+
#resize-handle{height:3px;background:transparent;cursor:ns-resize;flex-shrink:0;position:relative}
|
|
112
|
+
#resize-handle::after{content:'';position:absolute;left:50%;top:0;transform:translateX(-50%);width:40px;height:3px;background:var(--border);border-radius:2px;transition:background .15s}
|
|
113
|
+
#resize-handle:hover::after{background:var(--accent)}
|
|
114
|
+
|
|
115
|
+
.bottom-bar{display:flex;align-items:center;padding:0 12px;height:36px;flex-shrink:0;border-top:1px solid var(--border);background:linear-gradient(180deg, var(--surface2) 0%, var(--surface) 100%);gap:2px}
|
|
116
|
+
.btab{padding:4px 12px;font-size:11px;color:var(--text3);cursor:pointer;border-radius:4px;font-weight:500;transition:all .1s;user-select:none}
|
|
117
|
+
.btab:hover{color:var(--text2);background:var(--surface2)}
|
|
118
|
+
.btab.active{color:var(--text);background:var(--surface2)}
|
|
119
|
+
.bottom-actions{margin-left:auto;display:flex;align-items:center;gap:6px}
|
|
120
|
+
#log-filter{width:140px;font-size:11px;padding:4px 8px;border-radius:5px;background:var(--surface2);border:1px solid var(--border)}
|
|
121
|
+
.icon-btn{background:none;border:none;color:var(--text3);cursor:pointer;padding:4px;border-radius:4px;font-size:14px;line-height:1;display:flex;align-items:center}
|
|
122
|
+
.icon-btn:hover{color:var(--text);background:var(--surface2)}
|
|
123
|
+
|
|
124
|
+
#log-container,#flow-container{flex:1;overflow-y:auto;font:11px/1.7 var(--mono);padding:6px 14px}
|
|
125
|
+
.log-line{padding:2px 0;border-bottom:1px solid rgba(255,255,255,.03)}
|
|
126
|
+
.log-line .ts{color:var(--text3);margin-right:6px}
|
|
127
|
+
.log-line .agent{color:var(--accent);margin-right:4px}
|
|
128
|
+
.log-line.warn{color:var(--warn)}
|
|
129
|
+
.log-line.warn .ts{color:var(--warn);opacity:.5}
|
|
130
|
+
.log-line.error{color:var(--error)}
|
|
131
|
+
.log-line.error .ts{color:var(--error);opacity:.5}
|
|
132
|
+
|
|
133
|
+
.flow-entry{padding:2px 0;font-size:11px;line-height:1.6;white-space:pre}
|
|
134
|
+
.fe-time{color:var(--text3)}
|
|
135
|
+
.fe-type{font-weight:500}
|
|
136
|
+
.fe-eng{color:var(--accent)}
|
|
137
|
+
.fe-agent{color:var(--success)}
|
|
138
|
+
.fe-cubby{color:var(--warn)}
|
|
139
|
+
.fe-err{color:var(--error)}
|
|
140
|
+
.fe-ok{color:var(--success)}
|
|
141
|
+
.fe-dim{color:var(--text3)}
|
|
142
|
+
|
|
143
|
+
/* Empty state */
|
|
144
|
+
.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;color:var(--text3);gap:8px}
|
|
145
|
+
.empty-state .empty-icon{font-size:32px;opacity:.3}
|
|
146
|
+
.empty{color:var(--text3);font-size:12px;padding:24px 0;text-align:center}
|
|
147
|
+
|
|
148
|
+
/* Graph panel */
|
|
149
|
+
#cubby-graph{position:relative}
|
|
150
|
+
#graph-canvas{width:100%;height:calc(100vh - 200px);min-height:400px;border:1px solid var(--border);border-radius:var(--radius);background:var(--surface);overflow:hidden}
|
|
151
|
+
#graph-detail{position:absolute;top:8px;right:8px;width:260px;background:var(--surface2);border:1px solid var(--border2);border-radius:var(--radius);padding:12px;display:none;font-size:11px;max-height:400px;overflow-y:auto}
|
|
152
|
+
#graph-detail .gd-type{font:10px/1 var(--sans);text-transform:uppercase;letter-spacing:.5px;font-weight:600;margin-bottom:8px;padding:2px 6px;border-radius:3px;display:inline-block}
|
|
153
|
+
.gd-row{display:flex;gap:6px;margin-bottom:4px;line-height:1.4}
|
|
154
|
+
.gd-key{color:var(--text3);min-width:80px;flex-shrink:0}
|
|
155
|
+
.gd-val{color:var(--text2);word-break:break-all}
|
|
156
|
+
#graph-legend{position:absolute;bottom:8px;left:8px;background:var(--surface2);border:1px solid var(--border);border-radius:6px;padding:8px 12px;font-size:10px}
|
|
157
|
+
#graph-legend .gl-row{display:flex;align-items:center;gap:6px;color:var(--text3);margin-bottom:3px}
|
|
158
|
+
#graph-legend .gl-row:last-child{margin-bottom:0}
|
|
159
|
+
.gl-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0}
|
|
160
|
+
#graph-stats{font:11px var(--mono);color:var(--text3);margin-bottom:8px}
|
|
161
|
+
|
|
162
|
+
/* Polish */
|
|
163
|
+
.top-accent{position:fixed;top:0;left:0;right:0;height:2px;background:linear-gradient(90deg,var(--accent),#6366f1,#8b5cf6);z-index:100}
|
|
164
|
+
.empty-hero{display:flex;flex-direction:column;align-items:center;gap:12px;margin-bottom:28px}
|
|
165
|
+
.empty-title{font-size:18px;font-weight:600;color:var(--text);letter-spacing:.5px}
|
|
166
|
+
.empty-subtitle{font-size:12px;color:var(--text3);max-width:340px;text-align:center;line-height:1.6}
|
|
167
|
+
.empty-stats{display:flex;gap:16px;margin-bottom:28px}
|
|
168
|
+
.stat-card{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:14px 20px;min-width:120px;text-align:center}
|
|
169
|
+
.stat-card .stat-num{font-size:22px;font-weight:700;color:var(--text)}
|
|
170
|
+
.stat-card .stat-label{font-size:10px;color:var(--text3);text-transform:uppercase;letter-spacing:.5px;margin-top:4px}
|
|
171
|
+
.empty-shortcuts{display:flex;flex-direction:column;gap:8px;max-width:380px;width:100%}
|
|
172
|
+
.shortcut-item{display:flex;align-items:center;gap:12px;padding:10px 14px;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);transition:border-color .15s}
|
|
173
|
+
.shortcut-item:hover{border-color:var(--border2)}
|
|
174
|
+
.shortcut-key{font:11px/1 var(--mono);font-weight:600;color:var(--accent);min-width:100px}
|
|
175
|
+
.shortcut-desc{font-size:11px;color:var(--text3)}
|
|
176
|
+
.status-dot{width:7px;height:7px;border-radius:50%;display:inline-block;margin-right:4px;vertical-align:middle}
|
|
177
|
+
.status-dot.ok{background:var(--success);box-shadow:0 0 6px rgba(34,197,94,.4)}
|
|
178
|
+
.status-dot.err{background:var(--error);box-shadow:0 0 6px rgba(239,68,68,.4)}
|
|
179
|
+
.header-brand{display:flex;align-items:center;gap:8px}
|
|
180
|
+
.header-title{display:flex;flex-direction:column;line-height:1}
|
|
181
|
+
.header-title .ht-main{font-size:13px;font-weight:700;letter-spacing:1.2px;text-transform:uppercase;color:var(--text2)}
|
|
182
|
+
.header-title .ht-main span{color:var(--accent)}
|
|
183
|
+
.header-title .ht-sub{font-size:9px;color:var(--text3);letter-spacing:.6px;text-transform:uppercase}
|
|
184
|
+
.version-pill{font:9px/1 var(--mono);color:var(--text3);background:var(--surface2);padding:2px 8px;border-radius:10px;border:1px solid var(--border)}
|
package/dist/lib/config.js
CHANGED
|
@@ -3,6 +3,7 @@ import path from 'path';
|
|
|
3
3
|
import { parse } from 'yaml';
|
|
4
4
|
import dotenv from 'dotenv';
|
|
5
5
|
import { toCamelCase } from './utils.js';
|
|
6
|
+
const VALID_ALIAS_RE = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
|
|
6
7
|
export function validateConfig(config) {
|
|
7
8
|
if (!config || typeof config !== 'object') {
|
|
8
9
|
throw new Error('Config file must contain a YAML object');
|
|
@@ -23,11 +24,22 @@ export function validateConfig(config) {
|
|
|
23
24
|
if (!Array.isArray(agent.tasks)) {
|
|
24
25
|
throw new Error(`Agent "${agent.name ?? '?'}" is missing required "tasks" array`);
|
|
25
26
|
}
|
|
27
|
+
if (agent.alias && !VALID_ALIAS_RE.test(agent.alias)) {
|
|
28
|
+
throw new Error(`Agent "${agent.name ?? '?'}" alias "${agent.alias}" is not a valid JavaScript identifier. Use camelCase (e.g. "myAgent").`);
|
|
29
|
+
}
|
|
30
|
+
for (const task of agent.tasks) {
|
|
31
|
+
if (task.alias && !VALID_ALIAS_RE.test(task.alias)) {
|
|
32
|
+
throw new Error(`Task "${task.name ?? '?'}" alias "${task.alias}" is not a valid JavaScript identifier. Use camelCase (e.g. "myTask").`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
26
35
|
}
|
|
27
36
|
for (const cubby of config.cubbies ?? []) {
|
|
28
37
|
if (!cubby.alias || typeof cubby.alias !== 'string') {
|
|
29
38
|
throw new Error(`Cubby "${cubby.name ?? '?'}" is missing required "alias" field`);
|
|
30
39
|
}
|
|
40
|
+
if (!VALID_ALIAS_RE.test(cubby.alias)) {
|
|
41
|
+
throw new Error(`Cubby "${cubby.name ?? '?'}" alias "${cubby.alias}" is not a valid JavaScript identifier. Use camelCase (e.g. "myCubby").`);
|
|
42
|
+
}
|
|
31
43
|
}
|
|
32
44
|
}
|
|
33
45
|
export function loadEnv() {
|
|
@@ -37,9 +49,10 @@ export function loadEnv() {
|
|
|
37
49
|
if (authToken.toLowerCase().startsWith('bearer ')) {
|
|
38
50
|
authToken = authToken.slice(7).trim();
|
|
39
51
|
}
|
|
40
|
-
|
|
52
|
+
let orchestratorUrl = (process.env['CEF_ORCHESTRATOR_URL'] ?? '').trim();
|
|
41
53
|
if (!orchestratorUrl)
|
|
42
54
|
throw new Error('Missing required env var: CEF_ORCHESTRATOR_URL');
|
|
55
|
+
orchestratorUrl = orchestratorUrl.replace(/\/orchestrator\/?$/, '');
|
|
43
56
|
const robApiUrl = (process.env['CEF_ROB_API_URL'] ?? '').trim();
|
|
44
57
|
const robOrigin = (process.env['CEF_ROB_ORIGIN'] ?? '').trim() || undefined;
|
|
45
58
|
return { authToken, orchestratorUrl, robApiUrl, robOrigin };
|