consensus-cli 0.1.2 → 0.1.5
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/CHANGELOG.md +16 -0
- package/README.md +22 -10
- package/dist/claudeCli.js +125 -0
- package/dist/cli.js +12 -0
- package/dist/codexLogs.js +34 -13
- package/dist/opencodeApi.js +84 -0
- package/dist/opencodeEvents.js +388 -0
- package/dist/opencodeServer.js +91 -0
- package/dist/opencodeState.js +36 -0
- package/dist/opencodeStorage.js +127 -0
- package/dist/scan.js +341 -5
- package/package.json +1 -1
- package/public/app.js +146 -27
- package/public/index.html +3 -0
- package/public/style.css +21 -4
package/public/app.js
CHANGED
|
@@ -9,8 +9,10 @@ const panelClose = document.getElementById("panel-close");
|
|
|
9
9
|
const statusEl = document.getElementById("status");
|
|
10
10
|
const countEl = document.getElementById("count");
|
|
11
11
|
const activeList = document.getElementById("active-list");
|
|
12
|
+
const serverList = document.getElementById("server-list");
|
|
12
13
|
const searchInput = document.getElementById("search");
|
|
13
14
|
const laneTitle = document.querySelector(".lane-title");
|
|
15
|
+
const serverTitle = document.querySelector(".server-title");
|
|
14
16
|
|
|
15
17
|
const tileW = 96;
|
|
16
18
|
const tileH = 48;
|
|
@@ -19,10 +21,55 @@ const gridScale = 2;
|
|
|
19
21
|
const query = new URLSearchParams(window.location.search);
|
|
20
22
|
const mockMode = query.get("mock") === "1";
|
|
21
23
|
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
const cliPalette = {
|
|
25
|
+
codex: {
|
|
26
|
+
agent: {
|
|
27
|
+
active: { top: "#3d8f7f", left: "#2d6d61", right: "#275b52", stroke: "#54cdb1" },
|
|
28
|
+
idle: { top: "#384a57", left: "#2b3943", right: "#25323b", stroke: "#4f6b7a" },
|
|
29
|
+
error: { top: "#82443c", left: "#6d3530", right: "#5a2c28", stroke: "#d1584b" },
|
|
30
|
+
},
|
|
31
|
+
server: {
|
|
32
|
+
active: { top: "#4e665e", left: "#3d524b", right: "#32453f", stroke: "#79b8a8" },
|
|
33
|
+
idle: { top: "#353f48", left: "#2a323a", right: "#232a30", stroke: "#526577" },
|
|
34
|
+
error: { top: "#82443c", left: "#6d3530", right: "#5a2c28", stroke: "#d1584b" },
|
|
35
|
+
},
|
|
36
|
+
accent: "#57f2c6",
|
|
37
|
+
accentStrong: "rgba(87, 242, 198, 0.6)",
|
|
38
|
+
accentSoft: "rgba(87, 242, 198, 0.35)",
|
|
39
|
+
glow: "87, 242, 198",
|
|
40
|
+
},
|
|
41
|
+
opencode: {
|
|
42
|
+
agent: {
|
|
43
|
+
active: { top: "#8a6a2f", left: "#6f5626", right: "#5b4621", stroke: "#f1bd4f" },
|
|
44
|
+
idle: { top: "#3c3a37", left: "#2f2d2a", right: "#262322", stroke: "#7f6f56" },
|
|
45
|
+
error: { top: "#86443b", left: "#70352f", right: "#5c2c28", stroke: "#e0705c" },
|
|
46
|
+
},
|
|
47
|
+
server: {
|
|
48
|
+
active: { top: "#7d6a2b", left: "#665725", right: "#54481f", stroke: "#f5c453" },
|
|
49
|
+
idle: { top: "#353b42", left: "#272c33", right: "#1f242a", stroke: "#6b7380" },
|
|
50
|
+
error: { top: "#86443b", left: "#70352f", right: "#5c2c28", stroke: "#e0705c" },
|
|
51
|
+
},
|
|
52
|
+
accent: "#f5c453",
|
|
53
|
+
accentStrong: "rgba(245, 196, 83, 0.6)",
|
|
54
|
+
accentSoft: "rgba(245, 196, 83, 0.35)",
|
|
55
|
+
glow: "245, 196, 83",
|
|
56
|
+
},
|
|
57
|
+
claude: {
|
|
58
|
+
agent: {
|
|
59
|
+
active: { top: "#3f6fa3", left: "#2f5580", right: "#25476a", stroke: "#7fb7ff" },
|
|
60
|
+
idle: { top: "#374252", left: "#2a323f", right: "#232a35", stroke: "#5c6f85" },
|
|
61
|
+
error: { top: "#7f4140", left: "#683334", right: "#552a2b", stroke: "#e06b6a" },
|
|
62
|
+
},
|
|
63
|
+
server: {
|
|
64
|
+
active: { top: "#4b5f74", left: "#3a4a5c", right: "#2f3d4d", stroke: "#91b4d6" },
|
|
65
|
+
idle: { top: "#323b47", left: "#262d36", right: "#20262d", stroke: "#556577" },
|
|
66
|
+
error: { top: "#7f4140", left: "#683334", right: "#552a2b", stroke: "#e06b6a" },
|
|
67
|
+
},
|
|
68
|
+
accent: "#7fb7ff",
|
|
69
|
+
accentStrong: "rgba(127, 183, 255, 0.6)",
|
|
70
|
+
accentSoft: "rgba(127, 183, 255, 0.35)",
|
|
71
|
+
glow: "127, 183, 255",
|
|
72
|
+
},
|
|
26
73
|
};
|
|
27
74
|
const stateOpacity = {
|
|
28
75
|
active: 1,
|
|
@@ -119,12 +166,16 @@ function hashString(input) {
|
|
|
119
166
|
return Math.abs(hash);
|
|
120
167
|
}
|
|
121
168
|
|
|
122
|
-
function
|
|
169
|
+
function groupKeyForAgent(agent) {
|
|
123
170
|
return agent.repo || agent.cwd || agent.cmd || agent.id;
|
|
124
171
|
}
|
|
125
172
|
|
|
126
|
-
function
|
|
127
|
-
|
|
173
|
+
function keyForAgent(agent) {
|
|
174
|
+
return `${groupKeyForAgent(agent)}::${agent.id}`;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function assignCoordinate(key, baseKey) {
|
|
178
|
+
const hash = hashString(baseKey || key);
|
|
128
179
|
const baseX = (hash % 16) - 8;
|
|
129
180
|
const baseY = ((hash >> 4) % 16) - 8;
|
|
130
181
|
const maxRadius = 20;
|
|
@@ -152,9 +203,10 @@ function updateLayout(newAgents) {
|
|
|
152
203
|
const activeKeys = new Set();
|
|
153
204
|
for (const agent of newAgents) {
|
|
154
205
|
const key = keyForAgent(agent);
|
|
206
|
+
const baseKey = groupKeyForAgent(agent);
|
|
155
207
|
activeKeys.add(key);
|
|
156
208
|
if (!layout.has(key)) {
|
|
157
|
-
assignCoordinate(key);
|
|
209
|
+
assignCoordinate(key, baseKey);
|
|
158
210
|
}
|
|
159
211
|
}
|
|
160
212
|
|
|
@@ -170,8 +222,14 @@ function setStatus(text) {
|
|
|
170
222
|
statusEl.textContent = text;
|
|
171
223
|
}
|
|
172
224
|
|
|
173
|
-
function setCount(
|
|
174
|
-
|
|
225
|
+
function setCount(agentCount, serverCount) {
|
|
226
|
+
const agentLabel = `${agentCount} agent${agentCount === 1 ? "" : "s"}`;
|
|
227
|
+
if (typeof serverCount === "number") {
|
|
228
|
+
const serverLabel = `${serverCount} server${serverCount === 1 ? "" : "s"}`;
|
|
229
|
+
countEl.textContent = `${agentLabel} • ${serverLabel}`;
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
countEl.textContent = agentLabel;
|
|
175
233
|
}
|
|
176
234
|
|
|
177
235
|
function formatBytes(bytes) {
|
|
@@ -200,6 +258,45 @@ function escapeHtml(value) {
|
|
|
200
258
|
.replace(/'/g, "'");
|
|
201
259
|
}
|
|
202
260
|
|
|
261
|
+
function isServerKind(kind) {
|
|
262
|
+
return kind === "app-server" || kind === "opencode-server";
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function cliForAgent(agent) {
|
|
266
|
+
const kind = agent.kind || "";
|
|
267
|
+
if (kind.startsWith("opencode")) return "opencode";
|
|
268
|
+
if (kind.startsWith("claude")) return "claude";
|
|
269
|
+
return "codex";
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function paletteFor(agent) {
|
|
273
|
+
const cli = cliForAgent(agent);
|
|
274
|
+
const palette = cliPalette[cli] || cliPalette.codex;
|
|
275
|
+
const scope = isServerKind(agent.kind) ? palette.server : palette.agent;
|
|
276
|
+
return scope[agent.state] || scope.idle;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function accentFor(agent) {
|
|
280
|
+
const cli = cliForAgent(agent);
|
|
281
|
+
return (cliPalette[cli] || cliPalette.codex).accent;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function accentStrongFor(agent) {
|
|
285
|
+
const cli = cliForAgent(agent);
|
|
286
|
+
return (cliPalette[cli] || cliPalette.codex).accentStrong;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function accentSoftFor(agent) {
|
|
290
|
+
const cli = cliForAgent(agent);
|
|
291
|
+
return (cliPalette[cli] || cliPalette.codex).accentSoft;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function accentGlow(agent, alpha) {
|
|
295
|
+
const cli = cliForAgent(agent);
|
|
296
|
+
const tint = (cliPalette[cli] || cliPalette.codex).glow;
|
|
297
|
+
return `rgba(${tint}, ${alpha})`;
|
|
298
|
+
}
|
|
299
|
+
|
|
203
300
|
function labelFor(agent) {
|
|
204
301
|
if (agent.title) return agent.title;
|
|
205
302
|
if (agent.repo) return agent.repo;
|
|
@@ -267,10 +364,10 @@ function drawTag(ctx, x, y, text, accent) {
|
|
|
267
364
|
ctx.restore();
|
|
268
365
|
}
|
|
269
366
|
|
|
270
|
-
function
|
|
271
|
-
if (!
|
|
367
|
+
function renderLaneList(items, container, emptyLabel) {
|
|
368
|
+
if (!container) return;
|
|
272
369
|
if (!items.length) {
|
|
273
|
-
|
|
370
|
+
container.innerHTML = `<div class="lane-meta">${emptyLabel}</div>`;
|
|
274
371
|
return;
|
|
275
372
|
}
|
|
276
373
|
const sorted = [...items].sort((a, b) => {
|
|
@@ -281,14 +378,17 @@ function renderActiveList(items) {
|
|
|
281
378
|
return b.cpu - a.cpu;
|
|
282
379
|
});
|
|
283
380
|
|
|
284
|
-
|
|
381
|
+
container.innerHTML = sorted
|
|
285
382
|
.map((agent) => {
|
|
286
383
|
const doingRaw = agent.summary?.current || agent.doing || agent.cmdShort || "";
|
|
287
384
|
const doing = escapeHtml(truncate(doingRaw, 80));
|
|
288
385
|
const selectedClass = selected && selected.id === agent.id ? "is-selected" : "";
|
|
386
|
+
const accent = accentFor(agent);
|
|
387
|
+
const accentGlow = accentSoftFor(agent);
|
|
388
|
+
const cli = cliForAgent(agent);
|
|
289
389
|
const label = escapeHtml(labelFor(agent));
|
|
290
390
|
return `
|
|
291
|
-
<button class="lane-item ${selectedClass}" type="button" data-id="${agent.id}">
|
|
391
|
+
<button class="lane-item ${selectedClass} cli-${cli}" type="button" data-id="${agent.id}" style="--cli-accent: ${accent}; --cli-accent-glow: ${accentGlow};">
|
|
292
392
|
<div class="lane-pill ${agent.state}"></div>
|
|
293
393
|
<div class="lane-copy">
|
|
294
394
|
<div class="lane-label">${label}</div>
|
|
@@ -299,7 +399,7 @@ function renderActiveList(items) {
|
|
|
299
399
|
})
|
|
300
400
|
.join("");
|
|
301
401
|
|
|
302
|
-
Array.from(
|
|
402
|
+
Array.from(container.querySelectorAll(".lane-item")).forEach((item) => {
|
|
303
403
|
item.addEventListener("click", () => {
|
|
304
404
|
const id = item.getAttribute("data-id");
|
|
305
405
|
selected = sorted.find((agent) => agent.id === id) || null;
|
|
@@ -446,10 +546,14 @@ function draw() {
|
|
|
446
546
|
const reducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
447
547
|
|
|
448
548
|
for (const item of drawList) {
|
|
449
|
-
const palette =
|
|
549
|
+
const palette = paletteFor(item.agent);
|
|
450
550
|
const memMB = item.agent.mem / (1024 * 1024);
|
|
451
551
|
const heightBase = Math.min(120, Math.max(18, memMB * 0.4));
|
|
452
552
|
const isActive = item.agent.state === "active";
|
|
553
|
+
const isServer = isServerKind(item.agent.kind);
|
|
554
|
+
const accent = accentFor(item.agent);
|
|
555
|
+
const accentStrong = accentStrongFor(item.agent);
|
|
556
|
+
const accentSoft = accentSoftFor(item.agent);
|
|
453
557
|
const pulse =
|
|
454
558
|
isActive && !reducedMotion
|
|
455
559
|
? 4 + Math.sin(time / 200) * 3
|
|
@@ -490,7 +594,7 @@ function draw() {
|
|
|
490
594
|
y + tileH * 0.02,
|
|
491
595
|
tileW * 0.92,
|
|
492
596
|
tileH * 0.46,
|
|
493
|
-
|
|
597
|
+
accentGlow(item.agent, glowAlpha),
|
|
494
598
|
null
|
|
495
599
|
);
|
|
496
600
|
drawDiamond(
|
|
@@ -499,14 +603,14 @@ function draw() {
|
|
|
499
603
|
y - height - tileH * 0.18,
|
|
500
604
|
roofSize * 0.82,
|
|
501
605
|
roofSize * 0.42,
|
|
502
|
-
|
|
606
|
+
accentGlow(item.agent, capAlpha),
|
|
503
607
|
null
|
|
504
608
|
);
|
|
505
609
|
ctx.restore();
|
|
506
610
|
}
|
|
507
611
|
|
|
508
612
|
if (selected && selected.id === item.agent.id) {
|
|
509
|
-
drawDiamond(ctx, x, y, tileW + 10, tileH + 6, "rgba(0,0,0,0)",
|
|
613
|
+
drawDiamond(ctx, x, y, tileW + 10, tileH + 6, "rgba(0,0,0,0)", accent);
|
|
510
614
|
}
|
|
511
615
|
|
|
512
616
|
ctx.fillStyle = "rgba(10, 12, 15, 0.6)";
|
|
@@ -519,15 +623,21 @@ function draw() {
|
|
|
519
623
|
const showActiveTag = topActiveIds.has(item.agent.id);
|
|
520
624
|
if (isHovered || isSelected) {
|
|
521
625
|
const label = truncate(labelFor(item.agent), 20);
|
|
522
|
-
drawTag(ctx, x, y - height - tileH * 0.6, label,
|
|
626
|
+
drawTag(ctx, x, y - height - tileH * 0.6, label, accentStrong);
|
|
523
627
|
const doing = truncate(item.agent.summary?.current || item.agent.doing || "", 36);
|
|
524
|
-
drawTag(ctx, x, y - height - tileH * 0.9, doing,
|
|
628
|
+
drawTag(ctx, x, y - height - tileH * 0.9, doing, accentSoft);
|
|
629
|
+
if (isServer) {
|
|
630
|
+
drawTag(ctx, x, y + tileH * 0.2, "server", "rgba(79, 107, 122, 0.6)");
|
|
631
|
+
}
|
|
525
632
|
} else if (showActiveTag) {
|
|
526
633
|
const doing = truncate(
|
|
527
634
|
item.agent.summary?.current || item.agent.doing || labelFor(item.agent),
|
|
528
635
|
32
|
|
529
636
|
);
|
|
530
|
-
drawTag(ctx, x, y - height - tileH * 0.7, doing,
|
|
637
|
+
drawTag(ctx, x, y - height - tileH * 0.7, doing, accentSoft);
|
|
638
|
+
if (isServer) {
|
|
639
|
+
drawTag(ctx, x, y + tileH * 0.2, "server", "rgba(79, 107, 122, 0.6)");
|
|
640
|
+
}
|
|
531
641
|
}
|
|
532
642
|
|
|
533
643
|
hitList.push({
|
|
@@ -680,7 +790,9 @@ function connect() {
|
|
|
680
790
|
|
|
681
791
|
function applySnapshot(payload) {
|
|
682
792
|
agents = payload.agents || [];
|
|
683
|
-
|
|
793
|
+
const serverAgents = agents.filter((agent) => isServerKind(agent.kind));
|
|
794
|
+
const agentNodes = agents.filter((agent) => !isServerKind(agent.kind));
|
|
795
|
+
setCount(agentNodes.length, serverAgents.length);
|
|
684
796
|
const query = searchQuery.trim().toLowerCase();
|
|
685
797
|
searchMatches = new Set(
|
|
686
798
|
query ? agents.filter((agent) => matchesQuery(agent, query)).map((agent) => agent.id) : []
|
|
@@ -689,12 +801,19 @@ function applySnapshot(payload) {
|
|
|
689
801
|
? agents.filter((agent) => searchMatches.has(agent.id))
|
|
690
802
|
: agents;
|
|
691
803
|
const listAgents = query
|
|
692
|
-
? visibleAgents
|
|
693
|
-
: visibleAgents.filter((agent) => agent.state !== "idle");
|
|
804
|
+
? visibleAgents.filter((agent) => !isServerKind(agent.kind))
|
|
805
|
+
: visibleAgents.filter((agent) => agent.state !== "idle" && !isServerKind(agent.kind));
|
|
806
|
+
const listServers = query
|
|
807
|
+
? visibleAgents.filter((agent) => isServerKind(agent.kind))
|
|
808
|
+
: visibleAgents.filter((agent) => isServerKind(agent.kind));
|
|
694
809
|
if (laneTitle) {
|
|
695
810
|
laneTitle.textContent = query ? "search results" : "active agents";
|
|
696
811
|
}
|
|
697
|
-
|
|
812
|
+
if (serverTitle) {
|
|
813
|
+
serverTitle.textContent = query ? "server results" : "servers";
|
|
814
|
+
}
|
|
815
|
+
renderLaneList(listAgents, activeList, "No active agents.");
|
|
816
|
+
renderLaneList(listServers, serverList, "No servers detected.");
|
|
698
817
|
if (selected) {
|
|
699
818
|
selected = agents.find((agent) => agent.id === selected.id) || selected;
|
|
700
819
|
renderPanel(selected);
|
package/public/index.html
CHANGED
|
@@ -41,6 +41,9 @@
|
|
|
41
41
|
aria-label="Search metadata"
|
|
42
42
|
/>
|
|
43
43
|
<div id="active-list"></div>
|
|
44
|
+
<div class="lane-divider" aria-hidden="true"></div>
|
|
45
|
+
<div class="lane-title server-title">servers</div>
|
|
46
|
+
<div id="server-list"></div>
|
|
44
47
|
</div>
|
|
45
48
|
<div id="tooltip" class="hidden"></div>
|
|
46
49
|
<aside id="panel" class="collapsed" aria-label="Agent details">
|
package/public/style.css
CHANGED
|
@@ -282,13 +282,28 @@ body {
|
|
|
282
282
|
gap: 8px;
|
|
283
283
|
}
|
|
284
284
|
|
|
285
|
+
#server-list {
|
|
286
|
+
display: flex;
|
|
287
|
+
flex-direction: column;
|
|
288
|
+
gap: 8px;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
.lane-divider {
|
|
292
|
+
height: 1px;
|
|
293
|
+
margin: 12px 0;
|
|
294
|
+
background: rgba(62, 78, 89, 0.6);
|
|
295
|
+
}
|
|
296
|
+
|
|
285
297
|
.lane-item {
|
|
298
|
+
--cli-accent: rgba(87, 242, 198, 0.7);
|
|
299
|
+
--cli-accent-glow: rgba(87, 242, 198, 0.25);
|
|
286
300
|
display: flex;
|
|
287
301
|
align-items: flex-start;
|
|
288
302
|
gap: 10px;
|
|
289
303
|
padding: 10px;
|
|
290
304
|
border-radius: 10px;
|
|
291
305
|
border: 1px solid rgba(62, 78, 89, 0.5);
|
|
306
|
+
border-left: 3px solid var(--cli-accent);
|
|
292
307
|
background: rgba(13, 17, 22, 0.85);
|
|
293
308
|
cursor: pointer;
|
|
294
309
|
text-align: left;
|
|
@@ -300,16 +315,18 @@ body {
|
|
|
300
315
|
}
|
|
301
316
|
|
|
302
317
|
.lane-item:hover {
|
|
303
|
-
border-color:
|
|
318
|
+
border-color: var(--cli-accent);
|
|
319
|
+
border-left-color: var(--cli-accent);
|
|
304
320
|
}
|
|
305
321
|
|
|
306
322
|
.lane-item.is-selected {
|
|
307
|
-
border-color:
|
|
308
|
-
|
|
323
|
+
border-color: var(--cli-accent);
|
|
324
|
+
border-left-color: var(--cli-accent);
|
|
325
|
+
box-shadow: 0 0 12px var(--cli-accent-glow);
|
|
309
326
|
}
|
|
310
327
|
|
|
311
328
|
.lane-item:focus-visible {
|
|
312
|
-
outline: 2px solid
|
|
329
|
+
outline: 2px solid var(--cli-accent);
|
|
313
330
|
outline-offset: 2px;
|
|
314
331
|
}
|
|
315
332
|
|