@mandible-ai/mandible 0.3.2 → 0.3.4
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.
|
@@ -0,0 +1,802 @@
|
|
|
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>mandible — dashboard</title>
|
|
7
|
+
<script src="https://cdn.jsdelivr.net/npm/d3@7/dist/d3.min.js"></script>
|
|
8
|
+
<style>
|
|
9
|
+
:root {
|
|
10
|
+
--bg: #1a1a2e;
|
|
11
|
+
--bg-card: #22223a;
|
|
12
|
+
--bg-card-hover: #2a2a48;
|
|
13
|
+
--border: #333355;
|
|
14
|
+
--text: #e0e0f0;
|
|
15
|
+
--text-dim: #8888aa;
|
|
16
|
+
--accent: #f0a030;
|
|
17
|
+
--accent-dim: #c08020;
|
|
18
|
+
--green: #40c060;
|
|
19
|
+
--red: #e05050;
|
|
20
|
+
--blue: #5090e0;
|
|
21
|
+
--mono: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', 'SF Mono', Consolas, monospace;
|
|
22
|
+
--sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
|
|
23
|
+
}
|
|
24
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
25
|
+
body { background: var(--bg); color: var(--text); font-family: var(--sans); font-size: 14px; overflow: hidden; height: 100vh; }
|
|
26
|
+
|
|
27
|
+
/* Layout */
|
|
28
|
+
.app { display: grid; grid-template-rows: 48px 1fr; grid-template-columns: 1fr 360px; height: 100vh; }
|
|
29
|
+
.topbar { grid-column: 1 / -1; display: flex; align-items: center; padding: 0 16px; border-bottom: 1px solid var(--border); gap: 16px; }
|
|
30
|
+
.main { display: flex; flex-direction: column; overflow: hidden; }
|
|
31
|
+
.sidebar { border-left: 1px solid var(--border); display: flex; flex-direction: column; overflow: hidden; }
|
|
32
|
+
|
|
33
|
+
/* Topbar */
|
|
34
|
+
.logo { font-family: var(--mono); font-weight: 700; font-size: 18px; color: var(--accent); letter-spacing: -0.5px; }
|
|
35
|
+
.logo span { color: var(--text-dim); font-weight: 400; font-size: 13px; margin-left: 8px; }
|
|
36
|
+
.tabs { display: flex; gap: 2px; margin-left: 24px; }
|
|
37
|
+
.tab { padding: 6px 14px; border-radius: 6px 6px 0 0; cursor: pointer; color: var(--text-dim); font-size: 13px; border: 1px solid transparent; border-bottom: none; transition: all 0.15s; }
|
|
38
|
+
.tab:hover { color: var(--text); background: var(--bg-card); }
|
|
39
|
+
.tab.active { color: var(--accent); background: var(--bg-card); border-color: var(--border); }
|
|
40
|
+
.connection { margin-left: auto; display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--text-dim); }
|
|
41
|
+
.connection .dot { width: 8px; height: 8px; border-radius: 50%; }
|
|
42
|
+
.connection .dot.connected { background: var(--green); box-shadow: 0 0 6px var(--green); }
|
|
43
|
+
.connection .dot.disconnected { background: var(--red); box-shadow: 0 0 6px var(--red); }
|
|
44
|
+
|
|
45
|
+
/* Tab panels */
|
|
46
|
+
.panel { display: none; flex: 1; overflow: hidden; }
|
|
47
|
+
.panel.active { display: flex; flex-direction: column; }
|
|
48
|
+
|
|
49
|
+
/* Colony cards (sidebar) */
|
|
50
|
+
.sidebar-title { padding: 12px 16px 8px; font-size: 11px; text-transform: uppercase; letter-spacing: 1px; color: var(--text-dim); }
|
|
51
|
+
.colony-cards { flex: 1; overflow-y: auto; padding: 0 12px 12px; display: flex; flex-direction: column; gap: 8px; }
|
|
52
|
+
.colony-card { background: var(--bg-card); border: 1px solid var(--border); border-radius: 8px; padding: 12px; }
|
|
53
|
+
.colony-card .name { font-family: var(--mono); font-weight: 600; font-size: 13px; display: flex; align-items: center; gap: 8px; }
|
|
54
|
+
.colony-card .name .state { font-size: 10px; padding: 2px 6px; border-radius: 4px; font-weight: 400; font-family: var(--sans); }
|
|
55
|
+
.colony-card .name .state.running { background: rgba(64,192,96,0.2); color: var(--green); }
|
|
56
|
+
.colony-card .name .state.stopped { background: rgba(224,80,80,0.2); color: var(--red); }
|
|
57
|
+
.colony-card .stats { display: grid; grid-template-columns: 1fr 1fr; gap: 4px; margin-top: 8px; font-size: 12px; }
|
|
58
|
+
.colony-card .stat { color: var(--text-dim); }
|
|
59
|
+
.colony-card .stat b { color: var(--text); font-weight: 500; }
|
|
60
|
+
.colony-card .bar { margin-top: 8px; height: 3px; background: var(--border); border-radius: 2px; overflow: hidden; }
|
|
61
|
+
.colony-card .bar .fill { height: 100%; border-radius: 2px; transition: width 0.5s; }
|
|
62
|
+
|
|
63
|
+
/* Event stream filter */
|
|
64
|
+
.filter-bar { padding: 8px; border-bottom: 1px solid var(--border); display: flex; gap: 8px; align-items: center; }
|
|
65
|
+
.filter-bar input { flex: 1; background: var(--bg); border: 1px solid var(--border); color: var(--text); padding: 5px 10px; border-radius: 4px; font-family: var(--mono); font-size: 12px; }
|
|
66
|
+
.filter-bar input::placeholder { color: var(--text-dim); }
|
|
67
|
+
.filter-bar .count { color: var(--text-dim); font-size: 11px; white-space: nowrap; }
|
|
68
|
+
|
|
69
|
+
/* Event stream */
|
|
70
|
+
.event-stream { flex: 1; overflow-y: auto; padding: 8px; display: flex; flex-direction: column; gap: 2px; }
|
|
71
|
+
.event-item { display: flex; align-items: baseline; gap: 8px; padding: 4px 8px; border-radius: 4px; font-size: 12px; animation: fadeIn 0.3s ease; }
|
|
72
|
+
.event-item:hover { background: var(--bg-card); }
|
|
73
|
+
.event-item .ts { color: var(--text-dim); font-family: var(--mono); font-size: 11px; flex-shrink: 0; }
|
|
74
|
+
.event-item .colony-tag { font-family: var(--mono); font-size: 11px; padding: 1px 6px; border-radius: 3px; flex-shrink: 0; }
|
|
75
|
+
.event-item .etype { font-family: var(--mono); font-size: 11px; color: var(--text-dim); flex-shrink: 0; }
|
|
76
|
+
.event-item .detail { color: var(--text-dim); font-size: 11px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
77
|
+
.event-item .duration { color: var(--accent-dim); font-family: var(--mono); font-size: 11px; margin-left: auto; flex-shrink: 0; }
|
|
78
|
+
@keyframes fadeIn { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: translateY(0); } }
|
|
79
|
+
|
|
80
|
+
/* Signals sidebar section */
|
|
81
|
+
.signals-section { border-top: 1px solid var(--border); max-height: 40%; overflow: hidden; display: flex; flex-direction: column; }
|
|
82
|
+
.signal-list { flex: 1; overflow-y: auto; padding: 0 12px 12px; }
|
|
83
|
+
.signal-item { display: flex; align-items: center; gap: 8px; padding: 6px 8px; border-radius: 4px; font-size: 12px; cursor: pointer; }
|
|
84
|
+
.signal-item:hover { background: var(--bg-card-hover); }
|
|
85
|
+
.signal-item .stype { font-family: var(--mono); font-size: 11px; }
|
|
86
|
+
.signal-item .conc-bar { flex: 1; height: 4px; background: var(--border); border-radius: 2px; min-width: 40px; max-width: 80px; }
|
|
87
|
+
.signal-item .conc-bar .fill { height: 100%; border-radius: 2px; background: var(--accent); transition: width 1s linear; }
|
|
88
|
+
.signal-item .age { color: var(--text-dim); font-size: 10px; }
|
|
89
|
+
.signal-item .payload-preview { color: var(--text-dim); font-size: 10px; font-family: var(--mono); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 160px; }
|
|
90
|
+
.signal-item .signal-info { display: flex; flex-direction: column; gap: 1px; flex: 1; min-width: 0; }
|
|
91
|
+
|
|
92
|
+
/* Signal graph */
|
|
93
|
+
#graph-panel { position: relative; }
|
|
94
|
+
#graph-svg { width: 100%; height: 100%; }
|
|
95
|
+
.graph-node { cursor: pointer; }
|
|
96
|
+
.graph-node circle { transition: r 0.3s; }
|
|
97
|
+
.graph-node text { font-family: var(--mono); font-size: 11px; fill: var(--text); pointer-events: none; }
|
|
98
|
+
.graph-link { stroke: var(--border); stroke-opacity: 0.6; }
|
|
99
|
+
|
|
100
|
+
/* Environment inspector */
|
|
101
|
+
.inspector { flex: 1; overflow: auto; padding: 12px; }
|
|
102
|
+
.inspector table { width: 100%; border-collapse: collapse; font-size: 12px; }
|
|
103
|
+
.inspector th { text-align: left; padding: 6px 8px; border-bottom: 1px solid var(--border); color: var(--text-dim); font-weight: 500; font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px; position: sticky; top: 0; background: var(--bg); cursor: pointer; }
|
|
104
|
+
.inspector th:hover { color: var(--text); }
|
|
105
|
+
.inspector td { padding: 6px 8px; border-bottom: 1px solid rgba(51,51,85,0.3); font-family: var(--mono); font-size: 11px; }
|
|
106
|
+
.inspector tr:hover td { background: var(--bg-card); }
|
|
107
|
+
.inspector .conc-cell { display: flex; align-items: center; gap: 6px; }
|
|
108
|
+
.inspector .conc-cell .bar { width: 40px; height: 4px; background: var(--border); border-radius: 2px; }
|
|
109
|
+
.inspector .conc-cell .bar .fill { height: 100%; border-radius: 2px; background: var(--accent); }
|
|
110
|
+
|
|
111
|
+
/* Deposit form */
|
|
112
|
+
.deposit-form { padding: 12px; border-top: 1px solid var(--border); display: flex; gap: 8px; flex-wrap: wrap; }
|
|
113
|
+
.deposit-form input, .deposit-form textarea { background: var(--bg); border: 1px solid var(--border); color: var(--text); padding: 6px 10px; border-radius: 4px; font-family: var(--mono); font-size: 12px; }
|
|
114
|
+
.deposit-form input { flex: 1; min-width: 100px; }
|
|
115
|
+
.deposit-form textarea { width: 100%; height: 60px; resize: vertical; }
|
|
116
|
+
.deposit-form button { background: var(--accent); color: var(--bg); border: none; padding: 6px 16px; border-radius: 4px; cursor: pointer; font-weight: 600; font-size: 12px; }
|
|
117
|
+
.deposit-form button:hover { background: var(--accent-dim); }
|
|
118
|
+
|
|
119
|
+
/* Signal detail overlay */
|
|
120
|
+
.overlay { display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.6); z-index: 100; justify-content: center; align-items: center; }
|
|
121
|
+
.overlay.active { display: flex; }
|
|
122
|
+
.overlay-content { background: var(--bg-card); border: 1px solid var(--border); border-radius: 12px; padding: 20px; max-width: 600px; width: 90%; max-height: 80vh; overflow-y: auto; }
|
|
123
|
+
.overlay-content h3 { font-family: var(--mono); margin-bottom: 12px; color: var(--accent); }
|
|
124
|
+
.overlay-content pre { background: var(--bg); padding: 12px; border-radius: 6px; font-size: 12px; overflow-x: auto; }
|
|
125
|
+
.overlay-content .close { float: right; cursor: pointer; color: var(--text-dim); font-size: 18px; }
|
|
126
|
+
.overlay-content .close:hover { color: var(--text); }
|
|
127
|
+
|
|
128
|
+
/* Scrollbar */
|
|
129
|
+
::-webkit-scrollbar { width: 6px; }
|
|
130
|
+
::-webkit-scrollbar-track { background: transparent; }
|
|
131
|
+
::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }
|
|
132
|
+
::-webkit-scrollbar-thumb:hover { background: #444466; }
|
|
133
|
+
</style>
|
|
134
|
+
</head>
|
|
135
|
+
<body>
|
|
136
|
+
<div class="app">
|
|
137
|
+
<div class="topbar">
|
|
138
|
+
<div class="logo">mandible<span>dashboard</span></div>
|
|
139
|
+
<div class="tabs">
|
|
140
|
+
<div class="tab active" data-tab="flow">Signal Flow</div>
|
|
141
|
+
<div class="tab" data-tab="graph">Signal Graph</div>
|
|
142
|
+
<div class="tab" data-tab="inspector">Inspector</div>
|
|
143
|
+
</div>
|
|
144
|
+
<div class="connection">
|
|
145
|
+
<div class="dot disconnected" id="conn-dot"></div>
|
|
146
|
+
<span id="conn-text">connecting...</span>
|
|
147
|
+
</div>
|
|
148
|
+
</div>
|
|
149
|
+
|
|
150
|
+
<div class="main">
|
|
151
|
+
<!-- Signal Flow -->
|
|
152
|
+
<div class="panel active" id="panel-flow">
|
|
153
|
+
<div class="filter-bar">
|
|
154
|
+
<input type="text" id="event-filter" placeholder="Filter events (colony, type, signal...)">
|
|
155
|
+
<span class="count" id="event-count"></span>
|
|
156
|
+
</div>
|
|
157
|
+
<div class="event-stream" id="event-stream"></div>
|
|
158
|
+
</div>
|
|
159
|
+
|
|
160
|
+
<!-- Signal Graph -->
|
|
161
|
+
<div class="panel" id="panel-graph">
|
|
162
|
+
<svg id="graph-svg"></svg>
|
|
163
|
+
</div>
|
|
164
|
+
|
|
165
|
+
<!-- Environment Inspector -->
|
|
166
|
+
<div class="panel" id="panel-inspector">
|
|
167
|
+
<div class="inspector" id="inspector-table-wrap"></div>
|
|
168
|
+
<div class="deposit-form">
|
|
169
|
+
<input type="text" id="deposit-type" placeholder="signal type (e.g. task:ready)">
|
|
170
|
+
<textarea id="deposit-payload" placeholder='{"key": "value"}'></textarea>
|
|
171
|
+
<button id="deposit-btn">Deposit Signal</button>
|
|
172
|
+
</div>
|
|
173
|
+
</div>
|
|
174
|
+
</div>
|
|
175
|
+
|
|
176
|
+
<div class="sidebar">
|
|
177
|
+
<div class="sidebar-title">Colonies</div>
|
|
178
|
+
<div class="colony-cards" id="colony-cards"></div>
|
|
179
|
+
<div class="signals-section">
|
|
180
|
+
<div class="sidebar-title">Active Signals</div>
|
|
181
|
+
<div class="signal-list" id="signal-list"></div>
|
|
182
|
+
</div>
|
|
183
|
+
</div>
|
|
184
|
+
</div>
|
|
185
|
+
|
|
186
|
+
<div class="overlay" id="signal-overlay">
|
|
187
|
+
<div class="overlay-content">
|
|
188
|
+
<span class="close" id="overlay-close">×</span>
|
|
189
|
+
<h3 id="overlay-title"></h3>
|
|
190
|
+
<pre id="overlay-body"></pre>
|
|
191
|
+
</div>
|
|
192
|
+
</div>
|
|
193
|
+
|
|
194
|
+
<script>
|
|
195
|
+
(function() {
|
|
196
|
+
// ── State ──────────────────────────────────────────────
|
|
197
|
+
const state = {
|
|
198
|
+
events: [],
|
|
199
|
+
signals: [],
|
|
200
|
+
colonies: [],
|
|
201
|
+
ws: null,
|
|
202
|
+
connected: false,
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
const MAX_EVENTS = 1000;
|
|
206
|
+
let eventFilter = '';
|
|
207
|
+
|
|
208
|
+
// Colony colors — deterministic from name
|
|
209
|
+
const COLONY_COLORS = {};
|
|
210
|
+
const PALETTE = ['#f0a030','#5090e0','#40c060','#e05050','#b060e0','#e07050','#50c0c0','#c0c040'];
|
|
211
|
+
function colonyColor(name) {
|
|
212
|
+
if (!COLONY_COLORS[name]) {
|
|
213
|
+
let h = 0; for (let i = 0; i < name.length; i++) h = (h * 31 + name.charCodeAt(i)) >>> 0;
|
|
214
|
+
COLONY_COLORS[name] = PALETTE[h % PALETTE.length];
|
|
215
|
+
}
|
|
216
|
+
return COLONY_COLORS[name];
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Signal type color — deterministic
|
|
220
|
+
function typeColor(type) {
|
|
221
|
+
let h = 0; for (let i = 0; i < type.length; i++) h = (h * 37 + type.charCodeAt(i)) >>> 0;
|
|
222
|
+
return PALETTE[h % PALETTE.length];
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Concentration-based color ramp: dim slate blue (low) → bright teal-green (high)
|
|
226
|
+
function concentrationColor(conc) {
|
|
227
|
+
const h = 220 - conc * 60;
|
|
228
|
+
const s = 30 + conc * 50;
|
|
229
|
+
const l = 25 + conc * 35;
|
|
230
|
+
return `hsl(${h}, ${s}%, ${l}%)`;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Smart node label — shows issue #number + title when available
|
|
234
|
+
function nodeLabel(d) {
|
|
235
|
+
const p = d.signal.payload;
|
|
236
|
+
if (p.number && p.title) {
|
|
237
|
+
const clean = p.title.replace(/^\[.*?\]\s*/, '');
|
|
238
|
+
const t = clean.length > 25 ? clean.slice(0, 25) + '...' : clean;
|
|
239
|
+
return '#' + p.number + ' ' + t;
|
|
240
|
+
}
|
|
241
|
+
return payloadPreview(d.signal.payload) || d.type;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// ── WebSocket ──────────────────────────────────────────
|
|
245
|
+
function connect() {
|
|
246
|
+
const proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
247
|
+
const ws = new WebSocket(`${proto}//${location.host}`);
|
|
248
|
+
state.ws = ws;
|
|
249
|
+
|
|
250
|
+
ws.onopen = () => {
|
|
251
|
+
state.connected = true;
|
|
252
|
+
updateConnectionUI();
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
ws.onclose = () => {
|
|
256
|
+
state.connected = false;
|
|
257
|
+
updateConnectionUI();
|
|
258
|
+
setTimeout(connect, 2000);
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
ws.onerror = () => ws.close();
|
|
262
|
+
|
|
263
|
+
ws.onmessage = (msg) => {
|
|
264
|
+
const data = JSON.parse(msg.data);
|
|
265
|
+
if (data.type === 'init') {
|
|
266
|
+
for (const ev of data.events) addEvent(ev);
|
|
267
|
+
renderEvents();
|
|
268
|
+
} else if (data.type === 'event') {
|
|
269
|
+
addEvent(data.data);
|
|
270
|
+
renderEventItem(data.data);
|
|
271
|
+
} else if (data.type === 'snapshot') {
|
|
272
|
+
state.signals = data.signals;
|
|
273
|
+
renderSignals();
|
|
274
|
+
if (document.querySelector('.tab[data-tab="graph"]').classList.contains('active')) renderGraph();
|
|
275
|
+
if (document.querySelector('.tab[data-tab="inspector"]').classList.contains('active')) renderInspector();
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function addEvent(ev) {
|
|
281
|
+
state.events.push(ev);
|
|
282
|
+
if (state.events.length > MAX_EVENTS) state.events.shift();
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function updateConnectionUI() {
|
|
286
|
+
const dot = document.getElementById('conn-dot');
|
|
287
|
+
const text = document.getElementById('conn-text');
|
|
288
|
+
if (state.connected) {
|
|
289
|
+
dot.className = 'dot connected';
|
|
290
|
+
text.textContent = 'connected';
|
|
291
|
+
} else {
|
|
292
|
+
dot.className = 'dot disconnected';
|
|
293
|
+
text.textContent = 'reconnecting...';
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// ── Tabs ───────────────────────────────────────────────
|
|
298
|
+
document.querySelectorAll('.tab').forEach(tab => {
|
|
299
|
+
tab.addEventListener('click', () => {
|
|
300
|
+
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
|
|
301
|
+
document.querySelectorAll('.panel').forEach(p => p.classList.remove('active'));
|
|
302
|
+
tab.classList.add('active');
|
|
303
|
+
document.getElementById('panel-' + tab.dataset.tab).classList.add('active');
|
|
304
|
+
if (tab.dataset.tab === 'graph') renderGraph();
|
|
305
|
+
if (tab.dataset.tab === 'inspector') renderInspector();
|
|
306
|
+
});
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
// ── Event Stream ───────────────────────────────────────
|
|
310
|
+
function matchesFilter(ev) {
|
|
311
|
+
if (!eventFilter) return true;
|
|
312
|
+
const f = eventFilter.toLowerCase();
|
|
313
|
+
const searchable = [ev.colony, ev.type, ev.signalType, ev.signalId, ev.rule, ev.error].filter(Boolean).join(' ').toLowerCase();
|
|
314
|
+
return searchable.includes(f);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function renderEvents() {
|
|
318
|
+
const el = document.getElementById('event-stream');
|
|
319
|
+
el.innerHTML = '';
|
|
320
|
+
const visible = state.events.slice(-200).filter(matchesFilter);
|
|
321
|
+
for (const ev of visible) {
|
|
322
|
+
el.appendChild(createEventNode(ev));
|
|
323
|
+
}
|
|
324
|
+
el.scrollTop = el.scrollHeight;
|
|
325
|
+
updateEventCount();
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function renderEventItem(ev) {
|
|
329
|
+
if (!matchesFilter(ev)) { updateEventCount(); return; }
|
|
330
|
+
const el = document.getElementById('event-stream');
|
|
331
|
+
el.appendChild(createEventNode(ev));
|
|
332
|
+
// Auto-scroll if near bottom
|
|
333
|
+
if (el.scrollHeight - el.scrollTop - el.clientHeight < 100) {
|
|
334
|
+
el.scrollTop = el.scrollHeight;
|
|
335
|
+
}
|
|
336
|
+
updateEventCount();
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function updateEventCount() {
|
|
340
|
+
const total = state.events.length;
|
|
341
|
+
const visible = eventFilter ? state.events.filter(matchesFilter).length : total;
|
|
342
|
+
const countEl = document.getElementById('event-count');
|
|
343
|
+
countEl.textContent = eventFilter ? `${visible}/${total}` : `${total}`;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// Filter input — debounced
|
|
347
|
+
let filterTimer = null;
|
|
348
|
+
document.getElementById('event-filter').addEventListener('input', (e) => {
|
|
349
|
+
clearTimeout(filterTimer);
|
|
350
|
+
filterTimer = setTimeout(() => {
|
|
351
|
+
eventFilter = e.target.value.trim();
|
|
352
|
+
renderEvents();
|
|
353
|
+
}, 150);
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
function createEventNode(ev) {
|
|
357
|
+
const div = document.createElement('div');
|
|
358
|
+
div.className = 'event-item';
|
|
359
|
+
|
|
360
|
+
const ts = new Date(ev.timestamp).toISOString().slice(11, 23);
|
|
361
|
+
const color = colonyColor(ev.colony);
|
|
362
|
+
|
|
363
|
+
let detail = '';
|
|
364
|
+
if (ev.signalType) detail = ev.signalType;
|
|
365
|
+
if (ev.signalId) detail += ` (${ev.signalId.slice(0, 12)}...)`;
|
|
366
|
+
if (ev.rule) detail += ` [${ev.rule}]`;
|
|
367
|
+
if (ev.error) detail = ev.error;
|
|
368
|
+
if (ev.metadata) {
|
|
369
|
+
const m = ev.metadata;
|
|
370
|
+
if (m.decayed !== undefined) detail = `decayed:${m.decayed} evaporated:${m.evaporated} claims:${m.claimsReleased}`;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
div.innerHTML = `
|
|
374
|
+
<span class="ts">${ts}</span>
|
|
375
|
+
<span class="colony-tag" style="background:${color}22;color:${color};border:1px solid ${color}44">${ev.colony}</span>
|
|
376
|
+
<span class="etype">${formatEventType(ev.type)}</span>
|
|
377
|
+
<span class="detail">${detail}</span>
|
|
378
|
+
${ev.duration ? `<span class="duration">${ev.duration}ms</span>` : ''}
|
|
379
|
+
`;
|
|
380
|
+
return div;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
function formatEventType(type) {
|
|
384
|
+
return type.replace('colony:', '').replace('signal:', '').replace('action:', '').replace('decay:', '').replace('claim:', '').replace('environment:', '');
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// ── Colony Cards ───────────────────────────────────────
|
|
388
|
+
function renderColonies(colonies) {
|
|
389
|
+
const el = document.getElementById('colony-cards');
|
|
390
|
+
el.innerHTML = '';
|
|
391
|
+
for (const c of colonies) {
|
|
392
|
+
const color = colonyColor(c.name);
|
|
393
|
+
const card = document.createElement('div');
|
|
394
|
+
card.className = 'colony-card';
|
|
395
|
+
card.style.borderLeftColor = color;
|
|
396
|
+
card.style.borderLeftWidth = '3px';
|
|
397
|
+
|
|
398
|
+
const stateClass = c.state === 'running' ? 'running' : 'stopped';
|
|
399
|
+
const concPct = c.stats ? Math.round((c.activeCount / Math.max(1, c.concurrency ?? 1)) * 100) : 0;
|
|
400
|
+
|
|
401
|
+
card.innerHTML = `
|
|
402
|
+
<div class="name">
|
|
403
|
+
${c.name}
|
|
404
|
+
<span class="state ${stateClass}">${c.state}</span>
|
|
405
|
+
</div>
|
|
406
|
+
<div class="stats">
|
|
407
|
+
<div class="stat">processed <b>${c.stats?.signalsProcessed ?? 0}</b></div>
|
|
408
|
+
<div class="stat">deposited <b>${c.stats?.signalsDeposited ?? 0}</b></div>
|
|
409
|
+
<div class="stat">errors <b>${c.stats?.errors ?? 0}</b></div>
|
|
410
|
+
<div class="stat">avg <b>${Math.round(c.stats?.avgProcessingMs ?? 0)}ms</b></div>
|
|
411
|
+
</div>
|
|
412
|
+
<div class="bar"><div class="fill" style="width:${concPct}%;background:${color}"></div></div>
|
|
413
|
+
`;
|
|
414
|
+
el.appendChild(card);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// ── Signals Sidebar ────────────────────────────────────
|
|
419
|
+
function renderSignals() {
|
|
420
|
+
const el = document.getElementById('signal-list');
|
|
421
|
+
el.innerHTML = '';
|
|
422
|
+
const sorted = [...state.signals].sort((a, b) => b.meta.concentration - a.meta.concentration);
|
|
423
|
+
for (const s of sorted.slice(0, 50)) {
|
|
424
|
+
const item = document.createElement('div');
|
|
425
|
+
item.className = 'signal-item';
|
|
426
|
+
const color = typeColor(s.type);
|
|
427
|
+
const concPct = Math.round(s.meta.concentration * 100);
|
|
428
|
+
const age = formatAge(s.meta.deposited_at);
|
|
429
|
+
const preview = payloadPreview(s.payload);
|
|
430
|
+
|
|
431
|
+
item.innerHTML = `
|
|
432
|
+
<div class="signal-info">
|
|
433
|
+
<span class="stype" style="color:${color}">${s.type}</span>
|
|
434
|
+
${preview ? `<span class="payload-preview">${preview}</span>` : ''}
|
|
435
|
+
</div>
|
|
436
|
+
<div class="conc-bar"><div class="fill" style="width:${concPct}%;background:${color}"></div></div>
|
|
437
|
+
<span class="age">${age}</span>
|
|
438
|
+
`;
|
|
439
|
+
item.addEventListener('click', () => showSignalDetail(s));
|
|
440
|
+
el.appendChild(item);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// ── Signal Graph (d3-force) — incremental updates ──────
|
|
445
|
+
let simulation = null;
|
|
446
|
+
let graphNodes = [];
|
|
447
|
+
let graphLinks = [];
|
|
448
|
+
let linkGroup = null;
|
|
449
|
+
let nodeGroup = null;
|
|
450
|
+
let graphInitialized = false;
|
|
451
|
+
|
|
452
|
+
function renderGraph() {
|
|
453
|
+
const svg = d3.select('#graph-svg');
|
|
454
|
+
const panel = document.getElementById('panel-graph');
|
|
455
|
+
if (!panel.classList.contains('active')) return;
|
|
456
|
+
|
|
457
|
+
const width = panel.clientWidth;
|
|
458
|
+
const height = panel.clientHeight;
|
|
459
|
+
svg.attr('width', width).attr('height', height);
|
|
460
|
+
|
|
461
|
+
if (!state.signals.length) {
|
|
462
|
+
svg.selectAll('*').remove();
|
|
463
|
+
graphInitialized = false;
|
|
464
|
+
svg.append('text').attr('x', width/2).attr('y', height/2)
|
|
465
|
+
.attr('text-anchor', 'middle').attr('fill', '#8888aa').attr('font-size', 14)
|
|
466
|
+
.text('No signals — deposit some to see the graph');
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// Adaptive normalization: when the concentration range is wide (>0.3),
|
|
471
|
+
// raw values already give good visual spread. When values are clustered
|
|
472
|
+
// (e.g. all between 0.83–0.91), stretch to fill the visual range.
|
|
473
|
+
// Uses interquartile range to resist outlier distortion.
|
|
474
|
+
const concValues = state.signals.map(s => s.meta.concentration).sort((a, b) => a - b);
|
|
475
|
+
const concMin = concValues[0];
|
|
476
|
+
const concMax = concValues[concValues.length - 1];
|
|
477
|
+
const concRange = concMax - concMin;
|
|
478
|
+
function normalizeConc(c) {
|
|
479
|
+
if (concRange > 0.3) return c; // wide spread — raw values work fine
|
|
480
|
+
if (concRange < 0.01) return 1; // all identical
|
|
481
|
+
// Narrow cluster — normalize but keep a floor so lowest isn't invisible
|
|
482
|
+
const normalized = (c - concMin) / concRange;
|
|
483
|
+
return 0.15 + normalized * 0.85;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
// Build node map preserving existing positions
|
|
487
|
+
const oldNodeMap = new Map(graphNodes.map(n => [n.id, n]));
|
|
488
|
+
const newNodes = state.signals.map(s => {
|
|
489
|
+
const existing = oldNodeMap.get(s.id);
|
|
490
|
+
return {
|
|
491
|
+
id: s.id,
|
|
492
|
+
type: s.type,
|
|
493
|
+
concentration: s.meta.concentration,
|
|
494
|
+
intensity: normalizeConc(s.meta.concentration),
|
|
495
|
+
deposited_by: s.meta.deposited_by,
|
|
496
|
+
signal: s,
|
|
497
|
+
// Preserve position from previous render
|
|
498
|
+
x: existing?.x ?? (width / 2 + (Math.random() - 0.5) * 100),
|
|
499
|
+
y: existing?.y ?? (height / 2 + (Math.random() - 0.5) * 100),
|
|
500
|
+
vx: existing?.vx ?? 0,
|
|
501
|
+
vy: existing?.vy ?? 0,
|
|
502
|
+
fx: existing?.fx ?? null,
|
|
503
|
+
fy: existing?.fy ?? null,
|
|
504
|
+
};
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
const newNodeMap = new Map(newNodes.map(n => [n.id, n]));
|
|
508
|
+
const newLinks = [];
|
|
509
|
+
for (const s of state.signals) {
|
|
510
|
+
if (s.meta.caused_by) {
|
|
511
|
+
for (const parentId of s.meta.caused_by) {
|
|
512
|
+
if (newNodeMap.has(parentId)) {
|
|
513
|
+
newLinks.push({ source: parentId, target: s.id, id: parentId + '->' + s.id });
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
graphNodes = newNodes;
|
|
520
|
+
graphLinks = newLinks;
|
|
521
|
+
|
|
522
|
+
// Initialize SVG groups on first render
|
|
523
|
+
if (!graphInitialized) {
|
|
524
|
+
svg.selectAll('*').remove();
|
|
525
|
+
|
|
526
|
+
const defs = svg.append('defs');
|
|
527
|
+
|
|
528
|
+
defs.append('marker')
|
|
529
|
+
.attr('id', 'arrow').attr('viewBox', '0 -5 10 10')
|
|
530
|
+
.attr('refX', 20).attr('refY', 0)
|
|
531
|
+
.attr('markerWidth', 6).attr('markerHeight', 6)
|
|
532
|
+
.attr('orient', 'auto')
|
|
533
|
+
.append('path').attr('d', 'M0,-5L10,0L0,5')
|
|
534
|
+
.attr('fill', '#333355');
|
|
535
|
+
|
|
536
|
+
const glowFilter = defs.append('filter')
|
|
537
|
+
.attr('id', 'glow')
|
|
538
|
+
.attr('x', '-50%').attr('y', '-50%')
|
|
539
|
+
.attr('width', '200%').attr('height', '200%');
|
|
540
|
+
glowFilter.append('feGaussianBlur')
|
|
541
|
+
.attr('stdDeviation', '4')
|
|
542
|
+
.attr('result', 'blur');
|
|
543
|
+
glowFilter.append('feMerge')
|
|
544
|
+
.selectAll('feMergeNode')
|
|
545
|
+
.data(['blur', 'SourceGraphic'])
|
|
546
|
+
.enter().append('feMergeNode')
|
|
547
|
+
.attr('in', d => d);
|
|
548
|
+
|
|
549
|
+
linkGroup = svg.append('g').attr('class', 'links');
|
|
550
|
+
nodeGroup = svg.append('g').attr('class', 'nodes');
|
|
551
|
+
graphInitialized = true;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
// D3 join pattern for links
|
|
555
|
+
const linkSel = linkGroup.selectAll('line').data(graphLinks, d => d.id);
|
|
556
|
+
linkSel.exit().remove();
|
|
557
|
+
const linkEnter = linkSel.enter().append('line')
|
|
558
|
+
.attr('class', 'graph-link')
|
|
559
|
+
.attr('stroke-width', 1.5)
|
|
560
|
+
.attr('marker-end', 'url(#arrow)');
|
|
561
|
+
const link = linkEnter.merge(linkSel);
|
|
562
|
+
|
|
563
|
+
// D3 join pattern for nodes
|
|
564
|
+
const nodeSel = nodeGroup.selectAll('g.graph-node').data(graphNodes, d => d.id);
|
|
565
|
+
nodeSel.exit().transition().duration(300).attr('opacity', 0).remove();
|
|
566
|
+
|
|
567
|
+
const nodeEnter = nodeSel.enter().append('g')
|
|
568
|
+
.attr('class', 'graph-node')
|
|
569
|
+
.attr('opacity', 0)
|
|
570
|
+
.call(d3.drag()
|
|
571
|
+
.on('start', (event, d) => { if (!event.active) simulation.alphaTarget(0.3).restart(); d.fx = d.x; d.fy = d.y; })
|
|
572
|
+
.on('drag', (event, d) => { d.fx = event.x; d.fy = event.y; })
|
|
573
|
+
.on('end', (event, d) => { if (!event.active) simulation.alphaTarget(0); d.fx = null; d.fy = null; })
|
|
574
|
+
);
|
|
575
|
+
nodeEnter.append('circle');
|
|
576
|
+
nodeEnter.append('text').attr('dy', 4);
|
|
577
|
+
nodeEnter.on('click', (event, d) => showSignalDetail(d.signal));
|
|
578
|
+
nodeEnter.transition().duration(300).attr('opacity', 1);
|
|
579
|
+
|
|
580
|
+
const node = nodeEnter.merge(nodeSel);
|
|
581
|
+
|
|
582
|
+
// Update node visuals — uses normalized intensity for size/glow,
|
|
583
|
+
// raw concentration for color (reflects actual signal strength)
|
|
584
|
+
node.select('circle')
|
|
585
|
+
.transition().duration(500)
|
|
586
|
+
.attr('r', d => 10 + d.intensity * 18)
|
|
587
|
+
.attr('fill', d => concentrationColor(d.concentration))
|
|
588
|
+
.attr('fill-opacity', d => 0.4 + d.intensity * 0.6)
|
|
589
|
+
.attr('stroke', d => concentrationColor(d.concentration))
|
|
590
|
+
.attr('stroke-opacity', d => 0.5 + d.intensity * 0.5)
|
|
591
|
+
.attr('stroke-width', d => 1 + d.intensity * 2)
|
|
592
|
+
.attr('filter', d => d.intensity > 0.3 ? 'url(#glow)' : null);
|
|
593
|
+
|
|
594
|
+
node.select('text')
|
|
595
|
+
.attr('dx', d => 16 + d.intensity * 20)
|
|
596
|
+
.attr('fill', d => d.concentration > 0.3 ? 'var(--text)' : 'var(--text-dim)')
|
|
597
|
+
.text(d => nodeLabel(d));
|
|
598
|
+
|
|
599
|
+
// Detect whether the node set actually changed (not just a poll refresh)
|
|
600
|
+
const oldIds = new Set(oldNodeMap.keys());
|
|
601
|
+
const newIds = new Set(newNodes.map(n => n.id));
|
|
602
|
+
const nodesChanged = oldIds.size !== newIds.size || [...newIds].some(id => !oldIds.has(id));
|
|
603
|
+
|
|
604
|
+
// Update or create simulation
|
|
605
|
+
const pad = 40;
|
|
606
|
+
if (simulation && !nodesChanged) {
|
|
607
|
+
// Same nodes — just update data without reheating
|
|
608
|
+
simulation.nodes(graphNodes);
|
|
609
|
+
simulation.force('link').links(graphLinks);
|
|
610
|
+
simulation.force('x').x(width / 2);
|
|
611
|
+
simulation.force('y').y(height / 2);
|
|
612
|
+
simulation.force('collision').radius(d => 22 + d.intensity * 28);
|
|
613
|
+
} else {
|
|
614
|
+
// Nodes changed — create new simulation
|
|
615
|
+
if (simulation) simulation.stop();
|
|
616
|
+
simulation = d3.forceSimulation(graphNodes)
|
|
617
|
+
.force('link', d3.forceLink(graphLinks).id(d => d.id).distance(120).strength(0.7))
|
|
618
|
+
.force('charge', d3.forceManyBody().strength(-200))
|
|
619
|
+
.force('x', d3.forceX(width / 2).strength(0.05))
|
|
620
|
+
.force('y', d3.forceY(height / 2).strength(0.05))
|
|
621
|
+
.force('collision', d3.forceCollide().radius(d => 22 + d.intensity * 28))
|
|
622
|
+
.alpha(0.6)
|
|
623
|
+
.alphaDecay(0.02);
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
simulation.on('tick', () => {
|
|
627
|
+
// Clamp nodes within viewport bounds
|
|
628
|
+
for (const d of graphNodes) {
|
|
629
|
+
d.x = Math.max(pad, Math.min(width - pad, d.x));
|
|
630
|
+
d.y = Math.max(pad, Math.min(height - pad, d.y));
|
|
631
|
+
}
|
|
632
|
+
link.attr('x1', d => d.source.x).attr('y1', d => d.source.y)
|
|
633
|
+
.attr('x2', d => d.target.x).attr('y2', d => d.target.y);
|
|
634
|
+
node.attr('transform', d => `translate(${d.x},${d.y})`);
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
// ── Environment Inspector ──────────────────────────────
|
|
639
|
+
let sortKey = 'type';
|
|
640
|
+
let sortDir = 1;
|
|
641
|
+
|
|
642
|
+
function renderInspector() {
|
|
643
|
+
const wrap = document.getElementById('inspector-table-wrap');
|
|
644
|
+
const sorted = [...state.signals].sort((a, b) => {
|
|
645
|
+
let va, vb;
|
|
646
|
+
switch (sortKey) {
|
|
647
|
+
case 'type': va = a.type; vb = b.type; break;
|
|
648
|
+
case 'deposited_by': va = a.meta.deposited_by; vb = b.meta.deposited_by; break;
|
|
649
|
+
case 'concentration': va = a.meta.concentration; vb = b.meta.concentration; break;
|
|
650
|
+
case 'age': va = a.meta.deposited_at; vb = b.meta.deposited_at; break;
|
|
651
|
+
case 'claimed_by': va = a.meta.claimed_by ?? ''; vb = b.meta.claimed_by ?? ''; break;
|
|
652
|
+
default: va = a.type; vb = b.type;
|
|
653
|
+
}
|
|
654
|
+
if (typeof va === 'string') return va.localeCompare(vb) * sortDir;
|
|
655
|
+
return ((va ?? 0) - (vb ?? 0)) * sortDir;
|
|
656
|
+
});
|
|
657
|
+
|
|
658
|
+
wrap.innerHTML = `<table>
|
|
659
|
+
<thead><tr>
|
|
660
|
+
<th data-sort="type">type</th>
|
|
661
|
+
<th data-sort="deposited_by">deposited_by</th>
|
|
662
|
+
<th data-sort="concentration">concentration</th>
|
|
663
|
+
<th data-sort="age">age</th>
|
|
664
|
+
<th data-sort="claimed_by">claimed_by</th>
|
|
665
|
+
<th>tags</th>
|
|
666
|
+
</tr></thead>
|
|
667
|
+
<tbody>${sorted.map(s => {
|
|
668
|
+
const concPct = Math.round(s.meta.concentration * 100);
|
|
669
|
+
const color = typeColor(s.type);
|
|
670
|
+
return `<tr data-id="${s.id}" style="cursor:pointer">
|
|
671
|
+
<td style="color:${color}">${s.type}</td>
|
|
672
|
+
<td>${s.meta.deposited_by}</td>
|
|
673
|
+
<td><div class="conc-cell"><div class="bar"><div class="fill" style="width:${concPct}%;background:${color}"></div></div>${concPct}%</div></td>
|
|
674
|
+
<td>${formatAge(s.meta.deposited_at)}</td>
|
|
675
|
+
<td>${s.meta.claimed_by ?? '-'}</td>
|
|
676
|
+
<td>${(s.meta.tags ?? []).join(', ')}</td>
|
|
677
|
+
</tr>`;
|
|
678
|
+
}).join('')}</tbody>
|
|
679
|
+
</table>`;
|
|
680
|
+
|
|
681
|
+
// Sort headers
|
|
682
|
+
wrap.querySelectorAll('th[data-sort]').forEach(th => {
|
|
683
|
+
th.addEventListener('click', () => {
|
|
684
|
+
if (sortKey === th.dataset.sort) sortDir *= -1;
|
|
685
|
+
else { sortKey = th.dataset.sort; sortDir = 1; }
|
|
686
|
+
renderInspector();
|
|
687
|
+
});
|
|
688
|
+
});
|
|
689
|
+
|
|
690
|
+
// Row click
|
|
691
|
+
wrap.querySelectorAll('tr[data-id]').forEach(tr => {
|
|
692
|
+
tr.addEventListener('click', () => {
|
|
693
|
+
const s = state.signals.find(s => s.id === tr.dataset.id);
|
|
694
|
+
if (s) showSignalDetail(s);
|
|
695
|
+
});
|
|
696
|
+
});
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
// ── Signal Detail Overlay ──────────────────────────────
|
|
700
|
+
function showSignalDetail(signal) {
|
|
701
|
+
document.getElementById('signal-overlay').classList.add('active');
|
|
702
|
+
document.getElementById('overlay-title').textContent = `${signal.type} — ${signal.id}`;
|
|
703
|
+
document.getElementById('overlay-body').textContent = JSON.stringify(signal, null, 2);
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
document.getElementById('overlay-close').addEventListener('click', () => {
|
|
707
|
+
document.getElementById('signal-overlay').classList.remove('active');
|
|
708
|
+
});
|
|
709
|
+
document.getElementById('signal-overlay').addEventListener('click', (e) => {
|
|
710
|
+
if (e.target === e.currentTarget) e.currentTarget.classList.remove('active');
|
|
711
|
+
});
|
|
712
|
+
|
|
713
|
+
// ── Deposit Form ───────────────────────────────────────
|
|
714
|
+
document.getElementById('deposit-btn').addEventListener('click', async () => {
|
|
715
|
+
const type = document.getElementById('deposit-type').value.trim();
|
|
716
|
+
if (!type) return;
|
|
717
|
+
|
|
718
|
+
let payload = {};
|
|
719
|
+
const raw = document.getElementById('deposit-payload').value.trim();
|
|
720
|
+
if (raw) {
|
|
721
|
+
try { payload = JSON.parse(raw); } catch { alert('Invalid JSON payload'); return; }
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
try {
|
|
725
|
+
await fetch('/api/signals', {
|
|
726
|
+
method: 'POST',
|
|
727
|
+
headers: { 'Content-Type': 'application/json' },
|
|
728
|
+
body: JSON.stringify({ type, payload }),
|
|
729
|
+
});
|
|
730
|
+
document.getElementById('deposit-type').value = '';
|
|
731
|
+
document.getElementById('deposit-payload').value = '';
|
|
732
|
+
} catch (err) {
|
|
733
|
+
alert('Failed to deposit signal: ' + err.message);
|
|
734
|
+
}
|
|
735
|
+
});
|
|
736
|
+
|
|
737
|
+
// ── Helpers ────────────────────────────────────────────
|
|
738
|
+
function payloadPreview(payload) {
|
|
739
|
+
if (!payload || typeof payload !== 'object') return '';
|
|
740
|
+
// Show #number title for GitHub issues
|
|
741
|
+
if (payload.number && payload.title) {
|
|
742
|
+
const clean = payload.title.replace(/^\[.*?\]\s*/, '');
|
|
743
|
+
const label = '#' + payload.number + ' ' + clean;
|
|
744
|
+
return label.length > 40 ? label.slice(0, 40) + '...' : label;
|
|
745
|
+
}
|
|
746
|
+
// Pick the most informative field
|
|
747
|
+
for (const key of ['name', 'title', 'task', 'description', 'text', 'message']) {
|
|
748
|
+
if (typeof payload[key] === 'string') {
|
|
749
|
+
const v = payload[key];
|
|
750
|
+
return v.length > 40 ? v.slice(0, 40) + '...' : v;
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
const keys = Object.keys(payload);
|
|
754
|
+
if (keys.length === 0) return '';
|
|
755
|
+
const str = JSON.stringify(payload);
|
|
756
|
+
return str.length > 40 ? str.slice(0, 40) + '...' : str;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
function formatAge(depositedAt) {
|
|
760
|
+
const s = Math.floor((Date.now() - depositedAt) / 1000);
|
|
761
|
+
if (s < 60) return s + 's';
|
|
762
|
+
if (s < 3600) return Math.floor(s / 60) + 'm';
|
|
763
|
+
if (s < 86400) return Math.floor(s / 3600) + 'h';
|
|
764
|
+
return Math.floor(s / 86400) + 'd';
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
// ── Polling for colony status ──────────────────────────
|
|
768
|
+
async function pollColonies() {
|
|
769
|
+
try {
|
|
770
|
+
const res = await fetch('/api/colonies');
|
|
771
|
+
const data = await res.json();
|
|
772
|
+
state.colonies = data.colonies;
|
|
773
|
+
renderColonies(data.colonies);
|
|
774
|
+
} catch { /* ignore */ }
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
async function pollState() {
|
|
778
|
+
try {
|
|
779
|
+
const res = await fetch('/api/state');
|
|
780
|
+
const data = await res.json();
|
|
781
|
+
state.signals = data.signals;
|
|
782
|
+
renderSignals();
|
|
783
|
+
if (document.querySelector('.tab[data-tab="graph"]').classList.contains('active')) renderGraph();
|
|
784
|
+
if (document.querySelector('.tab[data-tab="inspector"]').classList.contains('active')) renderInspector();
|
|
785
|
+
} catch { /* ignore */ }
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
// ── Init ───────────────────────────────────────────────
|
|
789
|
+
connect();
|
|
790
|
+
pollColonies();
|
|
791
|
+
pollState();
|
|
792
|
+
setInterval(pollColonies, 3000);
|
|
793
|
+
setInterval(pollState, 5000);
|
|
794
|
+
|
|
795
|
+
// Handle resize for graph
|
|
796
|
+
window.addEventListener('resize', () => {
|
|
797
|
+
if (document.querySelector('.tab[data-tab="graph"]').classList.contains('active')) renderGraph();
|
|
798
|
+
});
|
|
799
|
+
})();
|
|
800
|
+
</script>
|
|
801
|
+
</body>
|
|
802
|
+
</html>
|
package/dist/src/cli/server.d.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import type { ColonyDefinition, Environment } from '../core/types.js';
|
|
2
|
+
export interface SignalServerConfig {
|
|
3
|
+
url: string;
|
|
4
|
+
apiKey: string;
|
|
5
|
+
project: string;
|
|
6
|
+
}
|
|
2
7
|
export interface MandibleConfig {
|
|
3
|
-
environment
|
|
8
|
+
environment?: Environment;
|
|
9
|
+
signalServer?: SignalServerConfig;
|
|
4
10
|
colonies: ColonyDefinition[];
|
|
5
11
|
dashboard?: {
|
|
6
12
|
port?: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../../src/cli/server.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../../src/cli/server.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAU,MAAM,kBAAkB,CAAC;AAI9E,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,cAAc;IAC7B,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,YAAY,CAAC,EAAE,kBAAkB,CAAC;IAClC,QAAQ,EAAE,gBAAgB,EAAE,CAAC;IAC7B,SAAS,CAAC,EAAE;QACV,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,OAAO,CAAC;KAChB,CAAC;CACH;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,CAAC;CACf;AAgID,wBAAsB,cAAc,CAClC,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,IAAI,CAAC,CAsPf"}
|
package/dist/src/cli/server.js
CHANGED
|
@@ -1,31 +1,174 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
// ============================================================
|
|
1
|
+
// PURPOSE: Dashboard Server — HTTP + WebSocket for real-time observability
|
|
2
|
+
// PURPOSE: Supports local mode (direct Environment) and cloud mode (signal server relay)
|
|
4
3
|
import { createServer } from 'node:http';
|
|
5
4
|
import { readFile } from 'node:fs/promises';
|
|
6
5
|
import { join, dirname } from 'node:path';
|
|
7
6
|
import { fileURLToPath } from 'node:url';
|
|
8
|
-
import { WebSocketServer } from 'ws';
|
|
7
|
+
import { WebSocketServer, WebSocket } from 'ws';
|
|
9
8
|
import { createRuntime } from '../core/runtime.js';
|
|
10
9
|
import { EventBus } from '../core/events.js';
|
|
11
10
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
/** Connect to signal server, authenticate, and return the WebSocket + helpers */
|
|
12
|
+
function connectToSignalServer(config, callbacks) {
|
|
13
|
+
const ws = new WebSocket(config.url);
|
|
14
|
+
let msgCounter = 0;
|
|
15
|
+
const pendingRequests = new Map();
|
|
16
|
+
function nextId() {
|
|
17
|
+
return `dash_${++msgCounter}`;
|
|
18
|
+
}
|
|
19
|
+
function send(msg) {
|
|
20
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
21
|
+
ws.send(JSON.stringify(msg));
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function request(msg) {
|
|
25
|
+
const id = nextId();
|
|
26
|
+
return new Promise((resolve, reject) => {
|
|
27
|
+
pendingRequests.set(id, { resolve, reject });
|
|
28
|
+
send({ ...msg, id });
|
|
29
|
+
setTimeout(() => {
|
|
30
|
+
if (pendingRequests.has(id)) {
|
|
31
|
+
pendingRequests.delete(id);
|
|
32
|
+
reject(new Error(`signal server request timed out: ${msg.type}`));
|
|
33
|
+
}
|
|
34
|
+
}, 30_000);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
ws.on('open', () => {
|
|
38
|
+
send({ type: 'auth', apiKey: config.apiKey, project: config.project });
|
|
39
|
+
});
|
|
40
|
+
ws.on('message', (raw) => {
|
|
41
|
+
let msg;
|
|
42
|
+
try {
|
|
43
|
+
msg = JSON.parse(typeof raw === 'string' ? raw : raw.toString());
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
switch (msg.type) {
|
|
49
|
+
case 'authenticated':
|
|
50
|
+
// Auth succeeded — subscribe to all signals and request initial snapshot
|
|
51
|
+
send({ type: 'subscribe', id: nextId(), query: { type: '*' } });
|
|
52
|
+
callbacks.onReady();
|
|
53
|
+
break;
|
|
54
|
+
case 'result':
|
|
55
|
+
if (msg.id && pendingRequests.has(msg.id)) {
|
|
56
|
+
const pending = pendingRequests.get(msg.id);
|
|
57
|
+
pendingRequests.delete(msg.id);
|
|
58
|
+
pending.resolve(msg.data);
|
|
59
|
+
}
|
|
60
|
+
break;
|
|
61
|
+
case 'error':
|
|
62
|
+
if (msg.id && pendingRequests.has(msg.id)) {
|
|
63
|
+
const pending = pendingRequests.get(msg.id);
|
|
64
|
+
pendingRequests.delete(msg.id);
|
|
65
|
+
pending.reject(new Error(`${msg.code}: ${msg.message}`));
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
callbacks.onError(new Error(`signal server error: ${msg.code} — ${msg.message}`));
|
|
69
|
+
}
|
|
70
|
+
break;
|
|
71
|
+
case 'signal':
|
|
72
|
+
// Subscription push — a new signal was deposited
|
|
73
|
+
if (msg.signal) {
|
|
74
|
+
callbacks.onSignalPush(msg.signal);
|
|
75
|
+
}
|
|
76
|
+
break;
|
|
77
|
+
case 'event':
|
|
78
|
+
// Runtime event relayed from a colony in a zone
|
|
79
|
+
if (msg.data) {
|
|
80
|
+
callbacks.onEvent(msg.data);
|
|
81
|
+
}
|
|
82
|
+
break;
|
|
83
|
+
default:
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
ws.on('error', (err) => {
|
|
88
|
+
callbacks.onError(err instanceof Error ? err : new Error(String(err)));
|
|
89
|
+
});
|
|
90
|
+
return {
|
|
91
|
+
ws,
|
|
92
|
+
snapshot() {
|
|
93
|
+
request({ type: 'snapshot' })
|
|
94
|
+
.then(signals => callbacks.onSnapshot(signals))
|
|
95
|
+
.catch(() => { });
|
|
96
|
+
},
|
|
97
|
+
async deposit(type, payload, tags) {
|
|
98
|
+
return request({
|
|
99
|
+
type: 'deposit',
|
|
100
|
+
signal: {
|
|
101
|
+
type,
|
|
102
|
+
payload,
|
|
103
|
+
meta: { deposited_by: 'dashboard', tags },
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
},
|
|
107
|
+
close() {
|
|
108
|
+
ws.close();
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
}
|
|
12
112
|
export async function startDevServer(config, options) {
|
|
113
|
+
const isCloudMode = !!config.signalServer;
|
|
13
114
|
const eventBus = new EventBus();
|
|
14
115
|
const runtimes = [];
|
|
15
|
-
//
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
116
|
+
// In local mode, create runtimes with shared event bus
|
|
117
|
+
if (!isCloudMode) {
|
|
118
|
+
if (!config.environment) {
|
|
119
|
+
throw new Error('MandibleConfig requires either environment or signalServer');
|
|
120
|
+
}
|
|
121
|
+
for (const colonyDef of config.colonies) {
|
|
122
|
+
const runtime = createRuntime(colonyDef, { eventBus });
|
|
123
|
+
runtimes.push(runtime);
|
|
124
|
+
}
|
|
19
125
|
}
|
|
20
126
|
// Collect recent events for reconnecting clients
|
|
21
127
|
const recentEvents = [];
|
|
22
128
|
const MAX_RECENT = 500;
|
|
23
|
-
|
|
129
|
+
function pushEvent(event) {
|
|
24
130
|
recentEvents.push(event);
|
|
25
131
|
if (recentEvents.length > MAX_RECENT) {
|
|
26
132
|
recentEvents.splice(0, recentEvents.length - MAX_RECENT);
|
|
27
133
|
}
|
|
28
|
-
}
|
|
134
|
+
}
|
|
135
|
+
// In local mode, pipe EventBus events into recent events
|
|
136
|
+
if (!isCloudMode) {
|
|
137
|
+
eventBus.on((event) => pushEvent(event));
|
|
138
|
+
}
|
|
139
|
+
// Signal server relay (cloud mode)
|
|
140
|
+
let signalServerRelay = null;
|
|
141
|
+
let latestSnapshot = [];
|
|
142
|
+
if (isCloudMode) {
|
|
143
|
+
signalServerRelay = connectToSignalServer(config.signalServer, {
|
|
144
|
+
onEvent(event) {
|
|
145
|
+
pushEvent(event);
|
|
146
|
+
broadcastToClients(wss, { type: 'event', data: event });
|
|
147
|
+
},
|
|
148
|
+
onSnapshot(signals) {
|
|
149
|
+
latestSnapshot = signals;
|
|
150
|
+
broadcastToClients(wss, { type: 'snapshot', signals });
|
|
151
|
+
},
|
|
152
|
+
onSignalPush(signal) {
|
|
153
|
+
// Update our local snapshot cache
|
|
154
|
+
const idx = latestSnapshot.findIndex(s => s.id === signal.id);
|
|
155
|
+
if (idx >= 0) {
|
|
156
|
+
latestSnapshot[idx] = signal;
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
latestSnapshot.push(signal);
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
onReady() {
|
|
163
|
+
console.log(' connected to signal server');
|
|
164
|
+
// Request initial snapshot
|
|
165
|
+
signalServerRelay.snapshot();
|
|
166
|
+
},
|
|
167
|
+
onError(err) {
|
|
168
|
+
console.error(' signal server error:', err.message);
|
|
169
|
+
},
|
|
170
|
+
});
|
|
171
|
+
}
|
|
29
172
|
// HTTP server
|
|
30
173
|
const server = createServer(async (req, res) => {
|
|
31
174
|
const url = new URL(req.url ?? '/', `http://localhost:${options.port}`);
|
|
@@ -40,25 +183,52 @@ export async function startDevServer(config, options) {
|
|
|
40
183
|
}
|
|
41
184
|
try {
|
|
42
185
|
if (url.pathname === '/api/state') {
|
|
43
|
-
|
|
44
|
-
|
|
186
|
+
if (isCloudMode) {
|
|
187
|
+
sendJson(res, { signals: latestSnapshot });
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
const signals = await config.environment.snapshot();
|
|
191
|
+
sendJson(res, { signals });
|
|
192
|
+
}
|
|
45
193
|
}
|
|
46
194
|
else if (url.pathname === '/api/colonies') {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
195
|
+
if (isCloudMode) {
|
|
196
|
+
// In cloud mode, report colony defs with placeholder state
|
|
197
|
+
const colonies = config.colonies.map(c => ({
|
|
198
|
+
name: c.name,
|
|
199
|
+
state: 'running',
|
|
200
|
+
activeCount: 0,
|
|
201
|
+
concurrency: c.concurrency,
|
|
202
|
+
stats: { signalsSensed: 0, signalsClaimed: 0, signalsProcessed: 0, signalsDeposited: 0, claimConflicts: 0, errors: 0, avgProcessingMs: 0 },
|
|
203
|
+
}));
|
|
204
|
+
sendJson(res, { colonies });
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
const colonies = runtimes.map(rt => ({
|
|
208
|
+
name: rt.name,
|
|
209
|
+
state: rt.state,
|
|
210
|
+
activeCount: rt.activeCount,
|
|
211
|
+
concurrency: rt.concurrency,
|
|
212
|
+
stats: rt.stats,
|
|
213
|
+
}));
|
|
214
|
+
sendJson(res, { colonies });
|
|
215
|
+
}
|
|
55
216
|
}
|
|
56
217
|
else if (url.pathname === '/api/stats') {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
218
|
+
if (isCloudMode) {
|
|
219
|
+
const stats = config.colonies.map(c => ({
|
|
220
|
+
name: c.name,
|
|
221
|
+
signalsSensed: 0, signalsClaimed: 0, signalsProcessed: 0, signalsDeposited: 0, claimConflicts: 0, errors: 0, avgProcessingMs: 0,
|
|
222
|
+
}));
|
|
223
|
+
sendJson(res, { stats });
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
const stats = runtimes.map(rt => ({
|
|
227
|
+
name: rt.name,
|
|
228
|
+
...rt.stats,
|
|
229
|
+
}));
|
|
230
|
+
sendJson(res, { stats });
|
|
231
|
+
}
|
|
62
232
|
}
|
|
63
233
|
else if (url.pathname === '/api/events') {
|
|
64
234
|
sendJson(res, { events: recentEvents });
|
|
@@ -66,15 +236,21 @@ export async function startDevServer(config, options) {
|
|
|
66
236
|
else if (url.pathname === '/api/signals' && req.method === 'POST') {
|
|
67
237
|
const body = await readBody(req);
|
|
68
238
|
const { type, payload, tags } = JSON.parse(body);
|
|
69
|
-
|
|
70
|
-
type,
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
239
|
+
if (isCloudMode) {
|
|
240
|
+
const signal = await signalServerRelay.deposit(type, payload ?? {}, tags);
|
|
241
|
+
sendJson(res, { signal }, 201);
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
const signal = await config.environment.deposit({
|
|
245
|
+
type,
|
|
246
|
+
payload: payload ?? {},
|
|
247
|
+
meta: {
|
|
248
|
+
deposited_by: 'dashboard',
|
|
249
|
+
tags,
|
|
250
|
+
},
|
|
251
|
+
});
|
|
252
|
+
sendJson(res, { signal }, 201);
|
|
253
|
+
}
|
|
78
254
|
}
|
|
79
255
|
else if (url.pathname === '/' || url.pathname === '/index.html') {
|
|
80
256
|
await serveDashboard(res);
|
|
@@ -90,45 +266,34 @@ export async function startDevServer(config, options) {
|
|
|
90
266
|
res.end(JSON.stringify({ error: err.message }));
|
|
91
267
|
}
|
|
92
268
|
});
|
|
93
|
-
// WebSocket server
|
|
269
|
+
// WebSocket server for dashboard UI clients
|
|
94
270
|
const wss = new WebSocketServer({ server });
|
|
95
271
|
wss.on('connection', (ws) => {
|
|
96
272
|
// Send recent events on connect for catchup
|
|
97
273
|
ws.send(JSON.stringify({ type: 'init', events: recentEvents.slice(-100) }));
|
|
98
274
|
});
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
if (client.readyState === 1) { // WebSocket.OPEN
|
|
104
|
-
client.send(message);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
});
|
|
108
|
-
// Listen for remote events relayed through the signal server
|
|
109
|
-
if ('onEvent' in config.environment && typeof config.environment.onEvent === 'function') {
|
|
110
|
-
config.environment.onEvent((event) => {
|
|
111
|
-
recentEvents.push(event);
|
|
112
|
-
if (recentEvents.length > MAX_RECENT) {
|
|
113
|
-
recentEvents.splice(0, recentEvents.length - MAX_RECENT);
|
|
114
|
-
}
|
|
115
|
-
const message = JSON.stringify({ type: 'event', data: event });
|
|
116
|
-
for (const client of wss.clients) {
|
|
117
|
-
if (client.readyState === 1) {
|
|
118
|
-
client.send(message);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
275
|
+
if (!isCloudMode) {
|
|
276
|
+
// Forward runtime events to all connected WebSocket clients
|
|
277
|
+
eventBus.on((event) => {
|
|
278
|
+
broadcastToClients(wss, { type: 'event', data: event });
|
|
121
279
|
});
|
|
280
|
+
// Listen for remote events relayed through the signal server
|
|
281
|
+
if ('onEvent' in config.environment && typeof config.environment.onEvent === 'function') {
|
|
282
|
+
config.environment.onEvent((event) => {
|
|
283
|
+
pushEvent(event);
|
|
284
|
+
broadcastToClients(wss, { type: 'event', data: event });
|
|
285
|
+
});
|
|
286
|
+
}
|
|
122
287
|
}
|
|
123
288
|
// Periodic snapshot broadcast (for concentration decay animation)
|
|
124
289
|
const snapshotInterval = setInterval(async () => {
|
|
125
290
|
try {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
}
|
|
291
|
+
if (isCloudMode) {
|
|
292
|
+
signalServerRelay.snapshot();
|
|
293
|
+
}
|
|
294
|
+
else {
|
|
295
|
+
const signals = await config.environment.snapshot();
|
|
296
|
+
broadcastToClients(wss, { type: 'snapshot', signals });
|
|
132
297
|
}
|
|
133
298
|
}
|
|
134
299
|
catch { /* ignore snapshot errors */ }
|
|
@@ -137,21 +302,30 @@ export async function startDevServer(config, options) {
|
|
|
137
302
|
server.listen(options.port, () => {
|
|
138
303
|
console.log(` dashboard: http://localhost:${options.port}`);
|
|
139
304
|
});
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
305
|
+
if (!isCloudMode) {
|
|
306
|
+
// Start all colony runtimes (local mode only)
|
|
307
|
+
console.log(` starting ${runtimes.length} colonies...`);
|
|
308
|
+
for (const runtime of runtimes) {
|
|
309
|
+
await runtime.start();
|
|
310
|
+
console.log(` + ${runtime.name} [running]`);
|
|
311
|
+
}
|
|
312
|
+
console.log('');
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
console.log(` cloud mode — ${config.colonies.length} colonies running in Edera zones`);
|
|
316
|
+
for (const col of config.colonies) {
|
|
317
|
+
console.log(` + ${col.name} [cloud]`);
|
|
318
|
+
}
|
|
319
|
+
console.log('');
|
|
145
320
|
}
|
|
146
|
-
console.log('');
|
|
147
321
|
// Auto-open browser
|
|
148
322
|
if (options.open) {
|
|
149
|
-
const
|
|
323
|
+
const dashUrl = `http://localhost:${options.port}`;
|
|
150
324
|
try {
|
|
151
325
|
const { exec } = await import('node:child_process');
|
|
152
326
|
const cmd = process.platform === 'darwin' ? 'open' :
|
|
153
327
|
process.platform === 'win32' ? 'start' : 'xdg-open';
|
|
154
|
-
exec(`${cmd} ${
|
|
328
|
+
exec(`${cmd} ${dashUrl}`);
|
|
155
329
|
}
|
|
156
330
|
catch { /* ignore open errors */ }
|
|
157
331
|
}
|
|
@@ -159,9 +333,15 @@ export async function startDevServer(config, options) {
|
|
|
159
333
|
const shutdown = async () => {
|
|
160
334
|
console.log('\n shutting down...');
|
|
161
335
|
clearInterval(snapshotInterval);
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
console.log(
|
|
336
|
+
if (isCloudMode) {
|
|
337
|
+
signalServerRelay?.close();
|
|
338
|
+
console.log(' signal server connection closed');
|
|
339
|
+
}
|
|
340
|
+
else {
|
|
341
|
+
for (const runtime of runtimes) {
|
|
342
|
+
await runtime.stop();
|
|
343
|
+
console.log(` - ${runtime.name} [stopped]`);
|
|
344
|
+
}
|
|
165
345
|
}
|
|
166
346
|
wss.close();
|
|
167
347
|
server.close();
|
|
@@ -171,6 +351,14 @@ export async function startDevServer(config, options) {
|
|
|
171
351
|
process.on('SIGINT', shutdown);
|
|
172
352
|
process.on('SIGTERM', shutdown);
|
|
173
353
|
}
|
|
354
|
+
function broadcastToClients(wss, data) {
|
|
355
|
+
const message = JSON.stringify(data);
|
|
356
|
+
for (const client of wss.clients) {
|
|
357
|
+
if (client.readyState === WebSocket.OPEN) {
|
|
358
|
+
client.send(message);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
174
362
|
function sendJson(res, data, status = 200) {
|
|
175
363
|
res.writeHead(status, { 'Content-Type': 'application/json' });
|
|
176
364
|
res.end(JSON.stringify(data));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../../src/cli/server.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../../src/cli/server.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED