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)
69 lines
2.1 KiB
TypeScript
69 lines
2.1 KiB
TypeScript
/**
|
|
* PTY RPC handlers — create, write, resize, unsubscribe, close sessions.
|
|
*/
|
|
|
|
import type { PtyClient } from "../pty-client.ts";
|
|
|
|
export function createPtyHandlers(ptyClient: PtyClient) {
|
|
return {
|
|
"pty.create": async ({ sessionId, cols, rows, cwd, shell, args }: {
|
|
sessionId: string; cols: number; rows: number; cwd?: string;
|
|
shell?: string; args?: string[];
|
|
}) => {
|
|
if (!ptyClient.isConnected) {
|
|
return { ok: false, error: "PTY daemon not connected" };
|
|
}
|
|
try {
|
|
ptyClient.createSession({ id: sessionId, cols, rows, cwd, shell, args });
|
|
return { ok: true };
|
|
} catch (err) {
|
|
const error = err instanceof Error ? err.message : String(err);
|
|
console.error(`[pty.create] ${sessionId}: ${error}`);
|
|
return { ok: false, error };
|
|
}
|
|
},
|
|
|
|
"pty.write": ({ sessionId, data }: { sessionId: string; data: string }) => {
|
|
if (!ptyClient.isConnected) {
|
|
console.error(`[pty.write] ${sessionId}: daemon not connected`);
|
|
return { ok: false };
|
|
}
|
|
try {
|
|
ptyClient.writeInput(sessionId, data);
|
|
return { ok: true };
|
|
} catch (err) {
|
|
const error = err instanceof Error ? err.message : String(err);
|
|
console.error(`[pty.write] ${sessionId}: ${error}`);
|
|
return { ok: false };
|
|
}
|
|
},
|
|
|
|
"pty.resize": ({ sessionId, cols, rows }: { sessionId: string; cols: number; rows: number }) => {
|
|
if (!ptyClient.isConnected) return { ok: true };
|
|
try {
|
|
ptyClient.resize(sessionId, cols, rows);
|
|
} catch (err) {
|
|
console.error(`[pty.resize] ${sessionId}:`, err);
|
|
}
|
|
return { ok: true };
|
|
},
|
|
|
|
"pty.unsubscribe": ({ sessionId }: { sessionId: string }) => {
|
|
try {
|
|
ptyClient.unsubscribe(sessionId);
|
|
} catch (err) {
|
|
console.error(`[pty.unsubscribe] ${sessionId}:`, err);
|
|
}
|
|
return { ok: true };
|
|
},
|
|
|
|
"pty.close": ({ sessionId }: { sessionId: string }) => {
|
|
try {
|
|
ptyClient.closeSession(sessionId);
|
|
} catch (err) {
|
|
console.error(`[pty.close] ${sessionId}:`, err);
|
|
}
|
|
return { ok: true };
|
|
},
|
|
};
|
|
}
|