From b0543d7aa2441a04b6ad591a3fa5175618f26c02 Mon Sep 17 00:00:00 2001 From: almogdepaz Date: Sun, 17 May 2026 16:46:17 +0300 Subject: [PATCH 1/2] fix(types): eliminate any casts in app-ralph, routes, websocket, backend - app-ralph.ts: add RalphLoop/RalphDeps/StartRalphBody interfaces, type all function params, replace 5x `as any` with proper DOM types, extract getCheckedRadioValue helper to replace `{} as any` radio pattern - routes.ts: replace 5x `catch (e: any)` with `catch (e: unknown)` + errMsg, remove worktree `as any` include check - websocket.ts: remove `as any` on activePtySessions.set - backend.ts: add DuplicateSessionError class, replace monkey-patched `(err as any).code` pattern - mock-backend.ts: use DuplicateSessionError for test consistency --- public/app-ralph.ts | 150 ++++++++++++++++++++++++++----------- src/public-assets.ts | 2 +- src/server/backend.ts | 12 ++- src/server/mock-backend.ts | 5 +- src/server/routes.ts | 27 +++---- src/server/websocket.ts | 2 +- 6 files changed, 134 insertions(+), 64 deletions(-) diff --git a/public/app-ralph.ts b/public/app-ralph.ts index 6e6f22c7..d2832bdf 100644 --- a/public/app-ralph.ts +++ b/public/app-ralph.ts @@ -4,19 +4,71 @@ import { esc, escAttr, state, wpSettings, haptic } from "./app-state"; +// ── Types ── + +export interface RalphLoop { + readonly project: string; + readonly active: boolean; + readonly completed: boolean; + readonly finished?: string; + readonly started?: string; + readonly audit?: boolean; + readonly cleanup?: boolean; + readonly cleanupEnabled?: boolean; + readonly auditFixEnabled?: boolean; + readonly iteration?: number; + readonly totalIterations?: number; + readonly tasksDone?: number; + readonly tasksTotal?: number; + readonly agent?: string; + readonly planFile?: string; + readonly progressFile?: string; + readonly lastOutput?: string; + readonly worktreeMode?: string; + readonly worktreeBranch?: string; +} + +interface RalphStatusResult { + readonly hitLimit: boolean; + readonly status: string; + readonly statusLabel: string; + readonly dotClass: string; + readonly dotTitle: string; +} + +interface MachineEntry { + readonly url: string; + readonly name: string; +} + +interface StartRalphBody { + project: string; + iterations: number; + planFile?: string; + agent: string; + format: boolean; + cleanup: boolean; + auditFix: boolean; + worktree: false | "plan" | "task"; + worktreeBranch?: string; + worktreeBase?: string; + newBranch?: string; + sourceBranch?: string; +} + // ── Dependency injection ── interface RalphDeps { - api: (path: string, opts?: any, machineUrl?: string) => Promise; - errorMessage: (err: any) => string; + api: (path: string, opts?: RequestInit, machineUrl?: string) => Promise; + errorMessage: (err: unknown) => string; showView: (name: string) => void; - getMachines: () => any[]; + getMachines: () => MachineEntry[]; backToSessions: () => void; loadSessions: () => Promise; renderSidebar: () => void; startSidebarRefresh: () => void; - getSidebarRefreshTimer: () => any; - setSidebarRefreshTimer: (v: any) => void; + getSidebarRefreshTimer: () => ReturnType | null; + setSidebarRefreshTimer: (v: ReturnType | null) => void; } let deps: RalphDeps; @@ -27,8 +79,8 @@ export function initRalphDeps(d: RalphDeps) { // ── Status helpers ── -export function getRalphStatus(loop) { - const hitLimit = !loop.active && !loop.completed && loop.finished; +export function getRalphStatus(loop: RalphLoop): RalphStatusResult { + const hitLimit = !loop.active && !loop.completed && !!loop.finished; return { hitLimit, status: loop.audit ? "audit" : loop.cleanup ? "cleanup" : loop.active ? "running" : loop.completed ? "done" : hitLimit ? "limit" : "idle", @@ -40,7 +92,7 @@ export function getRalphStatus(loop) { // ── Card rendering ── -export function renderRalphCardHtml(loop, machineUrl) { +export function renderRalphCardHtml(loop: RalphLoop, machineUrl: string): string { const { status, statusLabel, dotClass, dotTitle } = getRalphStatus(loop); const taskPct = loop.tasksTotal > 0 ? Math.round((loop.tasksDone / loop.tasksTotal) * 100) : 0; const taskLabel = loop.tasksDone + '/' + loop.tasksTotal + ' tasks'; @@ -62,7 +114,7 @@ export function renderRalphCardHtml(loop, machineUrl) { ''; } -export function sidebarRalphCardHtml(loop, machineUrl) { +export function sidebarRalphCardHtml(loop: RalphLoop, machineUrl: string): string { const { status, statusLabel, dotClass, dotTitle } = getRalphStatus(loop); const mUrl = escAttr(machineUrl || ''); return '