fix(electrobun): address all 22 Codex review #2 findings
CRITICAL:
- DocsTab XSS: DOMPurify sanitization on all {@html} output
- File RPC path traversal: guardPath() validates against project CWDs
HIGH:
- SSH injection: spawn /usr/bin/ssh via PTY args, no shell string
- Search XSS: strip HTML, highlight matches client-side with <mark>
- Terminal listener leak: cleanup functions stored + called in onDestroy
- FileBrowser race: request token, discard stale responses
- SearchOverlay race: same request token pattern
- App startup ordering: groups.list chains into active_group restore
- PtyClient timeout: 5-second auth timeout on connect()
- Rule 55: 6 {#if} patterns converted to style:display toggle
MEDIUM:
- Agent persistence: only persist NEW messages (lastPersistedIndex)
- Search errors: typed error response, "Invalid query" UI
- Health store wired: agent events call recordActivity/setProjectStatus
- index.ts SRP: split into 8 domain handler modules (298 lines)
- App.svelte: extracted workspace-store.svelte.ts
- rpc.ts: typed AppRpcHandle, removed `any`
LOW:
- CommandPalette listener wired in App.svelte
- Dead code removed (removeGroup, onDragStart, plugin loaded)
This commit is contained in:
parent
8e756d3523
commit
1cd4558740
28 changed files with 1342 additions and 1164 deletions
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
import { appRpc } from './rpc.ts';
|
||||
import { recordActivity, recordToolDone, recordTokenSnapshot, setProjectStatus } from './health-store.svelte.ts';
|
||||
|
||||
// ── Types ────────────────────────────────────────────────────────────────────
|
||||
|
||||
|
|
@ -118,6 +119,8 @@ const cleanupTimers = new Map<string, ReturnType<typeof setTimeout>>();
|
|||
|
||||
// Debounce timer for message persistence
|
||||
const msgPersistTimers = new Map<string, ReturnType<typeof setTimeout>>();
|
||||
// Fix #12: Track last persisted index per session to avoid re-saving entire history
|
||||
const lastPersistedIndex = new Map<string, number>();
|
||||
|
||||
// ── Session persistence helpers ─────────────────────────────────────────────
|
||||
|
||||
|
|
@ -146,7 +149,11 @@ function persistMessages(session: AgentSession): void {
|
|||
|
||||
const timer = setTimeout(() => {
|
||||
msgPersistTimers.delete(session.sessionId);
|
||||
const msgs = session.messages.map((m) => ({
|
||||
// Fix #12: Only persist NEW messages (from lastPersistedIndex onward)
|
||||
const startIdx = lastPersistedIndex.get(session.sessionId) ?? 0;
|
||||
const newMsgs = session.messages.slice(startIdx);
|
||||
if (newMsgs.length === 0) return;
|
||||
const msgs = newMsgs.map((m) => ({
|
||||
sessionId: session.sessionId,
|
||||
msgId: m.id,
|
||||
role: m.role,
|
||||
|
|
@ -155,8 +162,9 @@ function persistMessages(session: AgentSession): void {
|
|||
toolInput: m.toolInput,
|
||||
timestamp: m.timestamp,
|
||||
}));
|
||||
if (msgs.length === 0) return;
|
||||
appRpc.request['session.messages.save']({ messages: msgs }).catch((err: unknown) => {
|
||||
appRpc.request['session.messages.save']({ messages: msgs }).then(() => {
|
||||
lastPersistedIndex.set(session.sessionId, session.messages.length);
|
||||
}).catch((err: unknown) => {
|
||||
console.error('[session.messages.save] persist error:', err);
|
||||
});
|
||||
}, 2000);
|
||||
|
|
@ -197,6 +205,16 @@ function ensureListeners() {
|
|||
persistMessages(session);
|
||||
// Reset stall timer on activity
|
||||
resetStallTimer(payload.sessionId, session.projectId);
|
||||
// Fix #14: Wire health store — record activity on every message batch
|
||||
for (const msg of converted) {
|
||||
if (msg.role === 'tool-call') {
|
||||
recordActivity(session.projectId, msg.toolName);
|
||||
} else if (msg.role === 'tool-result') {
|
||||
recordToolDone(session.projectId);
|
||||
} else {
|
||||
recordActivity(session.projectId);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -212,6 +230,9 @@ function ensureListeners() {
|
|||
session.status = normalizeStatus(payload.status);
|
||||
if (payload.error) session.error = payload.error;
|
||||
|
||||
// Fix #14: Wire health store — update project status
|
||||
setProjectStatus(session.projectId, session.status === 'done' ? 'done' : session.status === 'error' ? 'error' : session.status === 'running' ? 'running' : 'idle');
|
||||
|
||||
// Persist on every status change
|
||||
persistSession(session);
|
||||
|
||||
|
|
@ -250,6 +271,8 @@ function ensureListeners() {
|
|||
session.costUsd = payload.costUsd;
|
||||
session.inputTokens = payload.inputTokens;
|
||||
session.outputTokens = payload.outputTokens;
|
||||
// Fix #14: Wire health store — record token/cost snapshot
|
||||
recordTokenSnapshot(session.projectId, payload.inputTokens + payload.outputTokens, payload.costUsd);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue