Files
ely-eye/frontend/src/App.tsx
T
ZacharyZhang-NY 9145a23f18 Require a token for writes and quarantine what nobody vouches for
Sixteen mutating routes now refuse a caller who cannot present the token the
server started with; queries stay open, because a question is not a write. The
environment variable is ELY_EYE_AUTH_TOKEN, following the repo's env_prefix
convention — the shorter name I reached for first returned 403 where the smoke
expected 401, and the server printed the reason.

Words from a stranger are quarantined on the way in: an HTTP upload is
third_party by definition, and a sentence carrying an instruction-shaped phrase
is quarantined whoever wrote it. A quarantined atom stays findable and stays out
of the compiled context until a second, live, unquarantined source agrees with
its rarest words. Corroboration is computed at read time because it is a
property of the corpus now, not of the moment a memory was written; it is
term-level, and the docstring says so.

forget now erases the source row and the compiled contexts beside the atoms, so
a forgotten memory leaves nothing that can still speak. The semantic cache key
carries a store revision, which fixes a real staleness: a context compiled
before new evidence arrived kept its old quarantine decision forever.

bench.audit --product still pointed at evals.py, which the p1-6 split moved into
backend/src/ely_eye/proofs/. It failed fast, but the anti-self-justification
rule had quietly stopped auditing the product; it now walks the package, and a
test pins that the package exists and holds no re-roll.

Gates: pytest 306 passed; ruff clean; mypy cold cache 24 errors in 9 files, the
recorded baseline, net zero; pyright 0 errors on the 23 backend files and the 2
bench files; go build/vet/test and gofmt clean; npm run build 221.39 kB with
eslint exit 0; bench/filesize_check clean; bench.audit --product clean over 31
harness files; scripts/mcp_write_smoke.sh PASS with the gate live across two
servers and the MCP child.
2026-10-08 23:05:15 -04:00

147 lines
4.9 KiB
TypeScript

import { Activity, Archive, Database, Gauge, RefreshCw, Server } from 'lucide-react';
import { useEffect, useState } from 'react';
import { getAdapters, getStatus, readToken, writeToken } from './api';
import { AdapterPanel } from './components/AdapterPanel';
import { CartridgePanel } from './components/CartridgePanel';
import { CompilerPanel } from './components/CompilerPanel';
import { IngestPanel } from './components/IngestPanel';
import { MemoryPanel } from './components/MemoryPanel';
import { ProofPanel } from './components/ProofPanel';
import { SensesPanel } from './components/SensesPanel';
import { Kpi, Panel, Quiet } from './components/ui';
import { errorMessage, formatBytes, formatNumber, formatRuntimeDetail, formatVram } from './lib/format';
import type { AdapterStatus, CacheLayerStatus, SystemStatus } from './types';
export function App() {
const [status, setStatus] = useState<SystemStatus | null>(null);
const [adapters, setAdapters] = useState<AdapterStatus[]>([]);
const [error, setError] = useState<string | null>(null);
const [token, setToken] = useState(readToken);
async function refresh() {
const [nextStatus, nextAdapters] = await Promise.all([getStatus(), getAdapters()]);
setStatus(nextStatus);
setAdapters(nextAdapters);
}
useEffect(() => {
let cancelled = false;
async function loadInitial() {
try {
const [nextStatus, nextAdapters] = await Promise.all([getStatus(), getAdapters()]);
if (!cancelled) {
setStatus(nextStatus);
setAdapters(nextAdapters);
}
} catch (err) {
if (!cancelled) {
setError(errorMessage(err));
}
}
}
void loadInitial();
return () => {
cancelled = true;
};
}, []);
async function refreshQuietly() {
try {
await refresh();
} catch (err) {
setError(errorMessage(err));
}
}
const layers: CacheLayerStatus[] = status?.memory.layers ?? [];
const boundAdapters = adapters.filter((adapter) => adapter.cartridge_bound).length;
return (
<main className="app-shell">
<header className="topbar">
<div>
<p className="eyebrow">Ely-Eye v4.0</p>
<h1>Local VLM Context OS</h1>
</div>
<div className="topbar-actions">
<input
className="token-input"
type="password"
value={token}
placeholder="写入令牌"
aria-label="写入令牌"
title="服务器设置了 ELY_EYE_AUTH_TOKEN 时,写入需要它"
onChange={(event) => {
setToken(event.target.value);
writeToken(event.target.value);
}}
/>
<button
className="icon-button"
title="Refresh status"
onClick={() => refreshQuietly()}
>
<RefreshCw size={18} />
</button>
</div>
</header>
{error && <div className="error-line">{error}</div>}
<section className="kpi-grid" aria-label="system status">
<Kpi
icon={<Server size={18} />}
label="Runtime"
value={status?.runtime.available ? 'ready' : 'offline'}
detail={formatRuntimeDetail(status)}
/>
<Kpi
icon={<Gauge size={18} />}
label="GPU"
value={status?.runtime.gpu_name ?? 'unknown'}
detail={formatVram(status)}
/>
<Kpi
icon={<Database size={18} />}
label="Library"
value={formatNumber(status?.memory.library_token_equivalent ?? 0)}
detail={`${formatNumber(status?.memory.active_token_equivalent ?? 0)} token eq open`}
/>
<Kpi
icon={<Archive size={18} />}
label="Cartridges"
value={String(status?.memory.cartridges ?? 0)}
detail={`${boundAdapters} adapters bound`}
/>
</section>
<section className="workspace-grid">
<MemoryPanel onChanged={refreshQuietly} />
<CartridgePanel onChanged={refreshQuietly} />
<CompilerPanel onChanged={refreshQuietly} />
<IngestPanel onChanged={refreshQuietly} />
<SensesPanel senses={status?.senses ?? null} />
<Panel title="Cache Trace" icon={<Activity size={17} />}>
<div className="trace-list">
{layers.map((layer) => (
<div key={layer.layer} className="trace-row">
<span>{layer.layer}</span>
<meter
min={0}
max={Math.max(1, layer.hit_count + layer.miss_count)}
value={layer.hit_count}
/>
<small>{formatBytes(layer.bytes_estimate)}</small>
</div>
))}
{layers.length === 0 && <Quiet>Waiting for retrieval</Quiet>}
</div>
</Panel>
<AdapterPanel adapters={adapters} />
<ProofPanel />
</section>
</main>
);
}