mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-06-23 00:21:27 +08:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 07f61ceb7f |
@@ -0,0 +1,191 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Self-contained 3D "agent airspace" visualization, served by the control pane.
|
||||
*
|
||||
* Renders each agent as a point in code-space (positions from the proximity
|
||||
* embedding), sized by working-set size and colored by collision risk, with
|
||||
* links between converging pairs (amber = transmit advisory, red = steer). The
|
||||
* scene auto-rotates so you can read the cloud. Dependency-free: a hand-rolled
|
||||
* 3D2D projection on a <canvas>, no external scripts (CSP/offline friendly).
|
||||
*
|
||||
* This is the operator/Enterprise view of Layer 4: multi-agent observability:
|
||||
* literally watch the swarm and watch one agent steer away from a collision.
|
||||
*/
|
||||
|
||||
function renderProximityVizHtml() {
|
||||
return `<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>ECC Agent Airspace</title>
|
||||
<style>
|
||||
:root { color-scheme: dark; }
|
||||
* { box-sizing: border-box; }
|
||||
body { margin: 0; font: 14px/1.4 -apple-system, system-ui, sans-serif; background: #0b0e14; color: #e6edf3; }
|
||||
header { display: flex; align-items: baseline; gap: 12px; padding: 12px 16px; border-bottom: 1px solid #1f2630; }
|
||||
header h1 { font-size: 15px; margin: 0; }
|
||||
header .sub { color: #8b949e; font-size: 12px; }
|
||||
#wrap { display: grid; grid-template-columns: 1fr 320px; height: calc(100vh - 49px); }
|
||||
#stage { position: relative; }
|
||||
canvas { width: 100%; height: 100%; display: block; }
|
||||
#side { border-left: 1px solid #1f2630; padding: 12px 14px; overflow-y: auto; }
|
||||
#side h2 { font-size: 12px; text-transform: uppercase; letter-spacing: .04em; color: #8b949e; margin: 0 0 8px; }
|
||||
.adv { border: 1px solid #1f2630; border-radius: 8px; padding: 8px 10px; margin-bottom: 8px; }
|
||||
.adv.resolution { border-color: #b3402f; }
|
||||
.adv.advisory { border-color: #9a6700; }
|
||||
.adv .lv { font-size: 11px; text-transform: uppercase; letter-spacing: .04em; }
|
||||
.adv.resolution .lv { color: #ff7b72; }
|
||||
.adv.advisory .lv { color: #e3b341; }
|
||||
.adv .who { color: #c9d1d9; }
|
||||
.adv .act { color: #8b949e; font-size: 12px; margin-top: 3px; }
|
||||
.empty { color: #6e7681; }
|
||||
#legend { position: absolute; left: 12px; bottom: 12px; font-size: 11px; color: #8b949e; background: rgba(11,14,20,.7); padding: 6px 8px; border-radius: 6px; }
|
||||
.dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 5px; vertical-align: middle; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>ECC - Agent Airspace</h1>
|
||||
<span class="sub" id="status">connecting...</span>
|
||||
</header>
|
||||
<div id="wrap">
|
||||
<div id="stage">
|
||||
<canvas id="c"></canvas>
|
||||
<div id="legend">
|
||||
<div><span class="dot" style="background:#3fb950"></span>clear</div>
|
||||
<div><span class="dot" style="background:#e3b341"></span>traffic advisory (transmit)</div>
|
||||
<div><span class="dot" style="background:#ff7b72"></span>resolution (steer)</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="side">
|
||||
<h2>Advisories</h2>
|
||||
<div id="advisories"><div class="empty">No advisories - airspace clear.</div></div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
(function () {
|
||||
var canvas = document.getElementById('c');
|
||||
var ctx = canvas.getContext('2d');
|
||||
var state = { positions: [], links: [], advisories: [], riskByAgent: {} };
|
||||
var angle = 0;
|
||||
|
||||
function resize() {
|
||||
var r = canvas.parentElement.getBoundingClientRect();
|
||||
var dpr = window.devicePixelRatio || 1;
|
||||
canvas.width = Math.max(1, Math.floor(r.width * dpr));
|
||||
canvas.height = Math.max(1, Math.floor(r.height * dpr));
|
||||
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
||||
}
|
||||
window.addEventListener('resize', resize);
|
||||
|
||||
function riskColor(risk) {
|
||||
if (risk >= 0.7) return '#ff7b72';
|
||||
if (risk >= 0.35) return '#e3b341';
|
||||
return '#3fb950';
|
||||
}
|
||||
|
||||
// 3D to 2D: rotate around Y, simple perspective.
|
||||
function project(p, w, h) {
|
||||
var x = p[0], y = p[1] || 0, z = p[2] || 0;
|
||||
var ca = Math.cos(angle), sa = Math.sin(angle);
|
||||
var rx = x * ca - z * sa;
|
||||
var rz = x * sa + z * ca;
|
||||
var scale = 2.4 / (3.2 + rz); // perspective
|
||||
return [w / 2 + rx * scale * (Math.min(w, h) * 0.32), h / 2 + y * scale * (Math.min(w, h) * 0.32), scale];
|
||||
}
|
||||
|
||||
function draw() {
|
||||
var w = canvas.clientWidth, h = canvas.clientHeight;
|
||||
ctx.clearRect(0, 0, w, h);
|
||||
var pos = {};
|
||||
for (var i = 0; i < state.positions.length; i++) {
|
||||
var a = state.positions[i];
|
||||
pos[a.agentId] = project(a.position || [0, 0, 0], w, h);
|
||||
}
|
||||
// links first (under the points)
|
||||
for (var l = 0; l < state.links.length; l++) {
|
||||
var link = state.links[l];
|
||||
if (link.risk < 0.2) continue;
|
||||
var pa = pos[link.a], pb = pos[link.b];
|
||||
if (!pa || !pb) continue;
|
||||
ctx.strokeStyle = riskColor(link.risk);
|
||||
ctx.globalAlpha = Math.min(1, 0.25 + link.risk * 0.7);
|
||||
ctx.lineWidth = 1 + link.risk * 3;
|
||||
ctx.beginPath(); ctx.moveTo(pa[0], pa[1]); ctx.lineTo(pb[0], pb[1]); ctx.stroke();
|
||||
}
|
||||
ctx.globalAlpha = 1;
|
||||
// points
|
||||
for (var k = 0; k < state.positions.length; k++) {
|
||||
var ag = state.positions[k];
|
||||
var p = pos[ag.agentId];
|
||||
var radius = (6 + Math.sqrt(ag.fileCount || 1) * 3) * p[2];
|
||||
ctx.fillStyle = riskColor(state.riskByAgent[ag.agentId] || 0);
|
||||
ctx.beginPath(); ctx.arc(p[0], p[1], radius, 0, Math.PI * 2); ctx.fill();
|
||||
ctx.fillStyle = '#c9d1d9';
|
||||
ctx.font = '11px -apple-system, system-ui, sans-serif';
|
||||
ctx.fillText(String(ag.agentId).slice(0, 18), p[0] + radius + 4, p[1] + 3);
|
||||
}
|
||||
angle += 0.0035;
|
||||
requestAnimationFrame(draw);
|
||||
}
|
||||
|
||||
function renderAdvisories() {
|
||||
var box = document.getElementById('advisories');
|
||||
box.textContent = '';
|
||||
if (!state.advisories.length) {
|
||||
var e = document.createElement('div'); e.className = 'empty';
|
||||
e.textContent = 'No advisories - airspace clear.'; box.appendChild(e); return;
|
||||
}
|
||||
state.advisories.forEach(function (adv) {
|
||||
var el = document.createElement('div');
|
||||
el.className = 'adv ' + (adv.level === 'resolution' ? 'resolution' : 'advisory');
|
||||
var lv = document.createElement('div'); lv.className = 'lv';
|
||||
lv.textContent = Math.round(adv.risk * 100) + '% - ' + adv.level; el.appendChild(lv);
|
||||
var who = document.createElement('div'); who.className = 'who';
|
||||
who.textContent = (adv.aLabel || adv.a) + ' <-> ' + (adv.bLabel || adv.b); el.appendChild(who);
|
||||
var act = document.createElement('div'); act.className = 'act';
|
||||
act.textContent = adv.level === 'resolution'
|
||||
? (adv.steer + ' steers - ' + adv.hold + ' holds')
|
||||
: 'both transmit intent';
|
||||
el.appendChild(act);
|
||||
box.appendChild(el);
|
||||
});
|
||||
}
|
||||
|
||||
function applySnapshot(prox) {
|
||||
state.positions = prox.positions || [];
|
||||
state.links = prox.links || [];
|
||||
state.advisories = prox.advisories || [];
|
||||
var risk = {};
|
||||
state.links.forEach(function (l) {
|
||||
risk[l.a] = Math.max(risk[l.a] || 0, l.risk);
|
||||
risk[l.b] = Math.max(risk[l.b] || 0, l.risk);
|
||||
});
|
||||
state.riskByAgent = risk;
|
||||
renderAdvisories();
|
||||
var c = prox.counts || {};
|
||||
document.getElementById('status').textContent =
|
||||
(c.agents || 0) + ' agents - ' + (c.advisories || 0) + ' advisories - ' + (c.resolutions || 0) + ' steering';
|
||||
}
|
||||
|
||||
function poll() {
|
||||
fetch('/api/proximity').then(function (r) { return r.json(); }).then(function (data) {
|
||||
applySnapshot(data && data.enabled ? data : (data || {}));
|
||||
}).catch(function () {
|
||||
document.getElementById('status').textContent = 'offline';
|
||||
});
|
||||
}
|
||||
|
||||
resize();
|
||||
poll();
|
||||
setInterval(poll, 5000);
|
||||
requestAnimationFrame(draw);
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>`;
|
||||
}
|
||||
|
||||
module.exports = { renderProximityVizHtml };
|
||||
@@ -8,6 +8,7 @@ const { spawn } = require('child_process');
|
||||
const { buildControlPaneAction } = require('./actions');
|
||||
const { buildControlPaneSnapshot, resolveControlPaneConfig } = require('./state');
|
||||
const { renderControlPaneHtml } = require('./ui');
|
||||
const { renderProximityVizHtml } = require('./proximity-viz');
|
||||
const { claimWorkItem, moveWorkItem } = require('./work-item-mutations');
|
||||
|
||||
// Run a single write against the local work-item store, then close it. Kept
|
||||
@@ -265,6 +266,25 @@ function createControlPaneServer(options = {}) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 3D agent-airspace visualization (Layer 4 observability).
|
||||
if (req.method === 'GET' && requestUrl.pathname === '/proximity') {
|
||||
sendText(res, 200, renderProximityVizHtml(), 'text/html; charset=utf-8');
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.method === 'GET' && requestUrl.pathname === '/api/proximity') {
|
||||
const snapshot = await buildControlPaneSnapshot({
|
||||
repoRoot,
|
||||
dbPath: resolvedConfig.dbPath,
|
||||
stateDbPath: resolvedConfig.stateDbPath,
|
||||
config: resolvedConfig,
|
||||
allowActions,
|
||||
includeProximity: true
|
||||
});
|
||||
sendJson(res, 200, snapshot.proximity || { enabled: true, advisories: [], positions: [], links: [], counts: {} });
|
||||
return;
|
||||
}
|
||||
|
||||
const actionMatch = requestUrl.pathname.match(/^\/api\/actions\/([^/]+)$/);
|
||||
if (req.method === 'POST' && actionMatch) {
|
||||
if (!allowActions) {
|
||||
|
||||
@@ -27,7 +27,6 @@ import ipaddress
|
||||
import socket
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
from contextlib import contextmanager
|
||||
from pathlib import Path
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from collections import defaultdict
|
||||
@@ -395,36 +394,22 @@ def detect_project() -> dict:
|
||||
}
|
||||
|
||||
|
||||
@contextmanager
|
||||
def _registry_lock():
|
||||
"""Serialize registry read-modify-write across concurrent sessions.
|
||||
|
||||
Acquires the same advisory lock for every registry writer (``_update_registry``
|
||||
and ``_write_registry``) so ``projects delete/gc/merge`` cannot interleave with
|
||||
a concurrent observe-time update and corrupt ``projects.json``. No-op on
|
||||
platforms without ``fcntl`` (Windows).
|
||||
"""
|
||||
REGISTRY_FILE.parent.mkdir(parents=True, exist_ok=True)
|
||||
lock_path = REGISTRY_FILE.parent / f".{REGISTRY_FILE.name}.lock"
|
||||
lock_fd = None
|
||||
try:
|
||||
if _HAS_FCNTL:
|
||||
lock_fd = open(lock_path, "w")
|
||||
fcntl.flock(lock_fd, fcntl.LOCK_EX)
|
||||
yield
|
||||
finally:
|
||||
if lock_fd is not None:
|
||||
fcntl.flock(lock_fd, fcntl.LOCK_UN)
|
||||
lock_fd.close()
|
||||
|
||||
|
||||
def _update_registry(pid: str, pname: str, proot: str, premote: str) -> None:
|
||||
"""Update the projects.json registry.
|
||||
|
||||
Uses file locking (where available) to prevent concurrent sessions from
|
||||
overwriting each other's updates.
|
||||
"""
|
||||
with _registry_lock():
|
||||
REGISTRY_FILE.parent.mkdir(parents=True, exist_ok=True)
|
||||
lock_path = REGISTRY_FILE.parent / f".{REGISTRY_FILE.name}.lock"
|
||||
lock_fd = None
|
||||
|
||||
try:
|
||||
# Acquire advisory lock to serialize read-modify-write
|
||||
if _HAS_FCNTL:
|
||||
lock_fd = open(lock_path, "w")
|
||||
fcntl.flock(lock_fd, fcntl.LOCK_EX)
|
||||
|
||||
try:
|
||||
with open(REGISTRY_FILE, encoding="utf-8") as f:
|
||||
registry = json.load(f)
|
||||
@@ -444,6 +429,10 @@ def _update_registry(pid: str, pname: str, proot: str, premote: str) -> None:
|
||||
f.flush()
|
||||
os.fsync(f.fileno())
|
||||
os.replace(tmp_file, REGISTRY_FILE)
|
||||
finally:
|
||||
if lock_fd is not None:
|
||||
fcntl.flock(lock_fd, fcntl.LOCK_UN)
|
||||
lock_fd.close()
|
||||
|
||||
|
||||
def load_registry() -> dict:
|
||||
@@ -456,19 +445,15 @@ def load_registry() -> dict:
|
||||
|
||||
|
||||
def _write_registry(registry: dict) -> None:
|
||||
"""Write the project registry atomically.
|
||||
|
||||
Holds the same advisory lock as ``_update_registry`` so concurrent
|
||||
``projects delete/gc/merge`` and observe-time updates cannot corrupt the file.
|
||||
"""
|
||||
with _registry_lock():
|
||||
tmp_file = REGISTRY_FILE.parent / f".{REGISTRY_FILE.name}.tmp.{os.getpid()}"
|
||||
with open(tmp_file, "w", encoding="utf-8") as f:
|
||||
json.dump(registry, f, indent=2)
|
||||
f.write("\n")
|
||||
f.flush()
|
||||
os.fsync(f.fileno())
|
||||
os.replace(tmp_file, REGISTRY_FILE)
|
||||
"""Write the project registry atomically."""
|
||||
REGISTRY_FILE.parent.mkdir(parents=True, exist_ok=True)
|
||||
tmp_file = REGISTRY_FILE.parent / f".{REGISTRY_FILE.name}.tmp.{os.getpid()}"
|
||||
with open(tmp_file, "w", encoding="utf-8") as f:
|
||||
json.dump(registry, f, indent=2)
|
||||
f.write("\n")
|
||||
f.flush()
|
||||
os.fsync(f.fileno())
|
||||
os.replace(tmp_file, REGISTRY_FILE)
|
||||
|
||||
|
||||
def _validate_project_id(project_id: str) -> bool:
|
||||
@@ -588,14 +573,7 @@ def _project_counts(project_id: str) -> dict:
|
||||
|
||||
|
||||
def _remove_project_storage(project_id: str) -> None:
|
||||
# Defense-in-depth: resolve and confirm the target is contained within
|
||||
# PROJECTS_DIR before recursively deleting, even though callers validate the
|
||||
# project id. A relaxed validator or a future caller must never be able to
|
||||
# turn this into an arbitrary-directory delete.
|
||||
projects_root = PROJECTS_DIR.resolve()
|
||||
project_dir = (PROJECTS_DIR / project_id).resolve()
|
||||
if project_dir == projects_root or projects_root not in project_dir.parents:
|
||||
raise ValueError(f"refusing to remove {project_dir}: escapes {projects_root}")
|
||||
project_dir = PROJECTS_DIR / project_id
|
||||
if project_dir.exists():
|
||||
shutil.rmtree(project_dir)
|
||||
|
||||
|
||||
@@ -46,8 +46,6 @@ load_registry = _mod.load_registry
|
||||
_validate_instinct_id = _mod._validate_instinct_id
|
||||
_validate_import_url = _mod._validate_import_url
|
||||
_update_registry = _mod._update_registry
|
||||
_write_registry = _mod._write_registry
|
||||
_remove_project_storage = _mod._remove_project_storage
|
||||
_confidence_bar = _mod._confidence_bar
|
||||
|
||||
|
||||
@@ -1045,41 +1043,3 @@ def test_update_registry_atomic_replaces_file(patch_globals):
|
||||
assert "abc123" in data
|
||||
leftovers = list(tree["registry_file"].parent.glob(".projects.json.tmp.*"))
|
||||
assert leftovers == []
|
||||
|
||||
|
||||
def test_write_registry_atomic_no_tmp_leftovers(patch_globals):
|
||||
# Issue #2294: _write_registry now holds the registry lock like
|
||||
# _update_registry. It must still write atomically with no stray tmp files.
|
||||
tree = patch_globals
|
||||
_write_registry({"keep": {"name": "demo", "root": "/repo", "remote": ""}})
|
||||
data = json.loads(tree["registry_file"].read_text())
|
||||
assert data == {"keep": {"name": "demo", "root": "/repo", "remote": ""}}
|
||||
leftovers = list(tree["registry_file"].parent.glob(".projects.json.tmp.*"))
|
||||
assert leftovers == []
|
||||
|
||||
|
||||
def test_remove_project_storage_deletes_contained_dir(patch_globals):
|
||||
tree = patch_globals
|
||||
target = tree["projects_dir"] / "proj-1"
|
||||
(target / "instincts").mkdir(parents=True)
|
||||
(target / "instincts" / "x.md").write_text("hi", encoding="utf-8")
|
||||
_remove_project_storage("proj-1")
|
||||
assert not target.exists()
|
||||
|
||||
|
||||
def test_remove_project_storage_missing_dir_is_noop(patch_globals):
|
||||
# No raise when the contained dir simply does not exist.
|
||||
_remove_project_storage("never-created")
|
||||
|
||||
|
||||
def test_remove_project_storage_blocks_traversal(patch_globals):
|
||||
# Issue #2297: defense-in-depth — a traversal id must be refused even when a
|
||||
# caller skips _validate_project_id, so this can never delete outside
|
||||
# PROJECTS_DIR.
|
||||
with pytest.raises(ValueError):
|
||||
_remove_project_storage("../../etc")
|
||||
|
||||
|
||||
def test_remove_project_storage_blocks_root_itself(patch_globals):
|
||||
with pytest.raises(ValueError):
|
||||
_remove_project_storage(".")
|
||||
|
||||
@@ -226,6 +226,49 @@ async function runTests() {
|
||||
passed++;
|
||||
else failed++;
|
||||
|
||||
if (
|
||||
await test('serves the 3D agent-airspace page and the proximity JSON feed', async () => {
|
||||
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ecc-control-pane-proximity-'));
|
||||
const dbPath = path.join(tempDir, 'ecc2.db');
|
||||
|
||||
try {
|
||||
await writeMinimalDatabase(dbPath);
|
||||
const app = await createControlPaneServer({
|
||||
host: '127.0.0.1',
|
||||
port: 0,
|
||||
dbPath,
|
||||
repoRoot: REPO_ROOT,
|
||||
allowActions: false
|
||||
});
|
||||
|
||||
await app.listen();
|
||||
try {
|
||||
// The Enterprise/Pro 3D observability view: a self-contained HTML page.
|
||||
const page = await fetchLocal(`${app.url}/proximity`);
|
||||
assert.strictEqual(page.status, 200);
|
||||
assert.ok((page.headers.get('content-type') || '').includes('text/html'));
|
||||
const html = await page.text();
|
||||
assert.ok(html.includes('Agent Airspace'), 'page is titled Agent Airspace');
|
||||
assert.ok(html.includes('<canvas'), 'page renders a canvas');
|
||||
assert.ok(html.includes('/api/proximity'), 'page polls the proximity feed');
|
||||
|
||||
// The feed the page polls: shape must carry the airspace arrays.
|
||||
const prox = await fetchLocal(`${app.url}/api/proximity`).then(r => r.json());
|
||||
assert.ok(Array.isArray(prox.positions), 'positions array present');
|
||||
assert.ok(Array.isArray(prox.links), 'links array present');
|
||||
assert.ok(Array.isArray(prox.advisories), 'advisories array present');
|
||||
assert.ok(prox.counts && typeof prox.counts === 'object', 'counts present');
|
||||
} finally {
|
||||
await app.close();
|
||||
}
|
||||
} finally {
|
||||
fs.rmSync(tempDir, { recursive: true, force: true });
|
||||
}
|
||||
})
|
||||
)
|
||||
passed++;
|
||||
else failed++;
|
||||
|
||||
if (
|
||||
await test('serves health, asset, not-found, invalid body, and read-only action responses', async () => {
|
||||
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ecc-control-pane-routes-'));
|
||||
|
||||
Reference in New Issue
Block a user