diff --git a/src/application/AnalysisProviderEvaluation.ts b/src/application/AnalysisProviderEvaluation.ts index 08a7a54937..e648a4fd7a 100644 --- a/src/application/AnalysisProviderEvaluation.ts +++ b/src/application/AnalysisProviderEvaluation.ts @@ -41,7 +41,8 @@ export const evaluateAnalysisProviderCandidate = async ( if (resolution === ABORTED) return err(new AnalysisCancelledError("open_binary")); resolved = resolution; - } catch { + } catch (cause: unknown) { + void cause; return ok( rejectedProfile(candidate, status, "Provider profile resolution threw"), ); @@ -82,7 +83,8 @@ export const evaluateAnalysisProviderCandidate = async ( ), ); return ok({ candidate, status, profile, compatibility }); - } catch { + } catch (cause: unknown) { + void cause; return ok( rejectedProfile( candidate, diff --git a/src/application/ArtifactExtraction.ts b/src/application/ArtifactExtraction.ts index cbbcfcc73e..7fc20dae94 100644 --- a/src/application/ArtifactExtraction.ts +++ b/src/application/ArtifactExtraction.ts @@ -171,7 +171,11 @@ const materializeSelection = async ({ await output.commit(); return result; } catch (cause: unknown) { - if (!readerClosed) await reader.close().catch(() => undefined); + if (!readerClosed) + await reader.close().catch((cause: unknown) => { + // best-effort cleanup: reader close must not mask the extraction failure. + void cause; + }); await output.rollback(); throw cause; } diff --git a/src/application/BinarySessionRecords.ts b/src/application/BinarySessionRecords.ts index dd2bbfbc61..8faceaf242 100644 --- a/src/application/BinarySessionRecords.ts +++ b/src/application/BinarySessionRecords.ts @@ -186,10 +186,14 @@ export abstract class BinarySessionRecords { try { const notification = listener(); if (notification !== undefined) - void notification.catch(() => undefined); - } catch { + void notification.catch((cause: unknown) => { + // best-effort cleanup: async observer notifications must not reject unhandled. + void cause; + }); + } catch (cause: unknown) { // External resource observers are best-effort; one callback must not // make a committed evidence mutation appear to fail. + void cause; } } } diff --git a/src/application/ClientConfigPath.ts b/src/application/ClientConfigPath.ts index 34b2cf3496..c28cc800c0 100644 --- a/src/application/ClientConfigPath.ts +++ b/src/application/ClientConfigPath.ts @@ -44,7 +44,9 @@ export const resolveClientConfigTransactionPath = async ( return targetStats.isFile?.() === true && targetStats.uid === currentUid ? canonicalPath : undefined; - } catch { + } catch (cause: unknown) { + // best-effort cleanup: unreadable or vanishing config paths mean no registration. + void cause; return undefined; } }; diff --git a/src/application/ClientRegistrationStatus.ts b/src/application/ClientRegistrationStatus.ts index 1c064c6849..b7d05e1bb1 100644 --- a/src/application/ClientRegistrationStatus.ts +++ b/src/application/ClientRegistrationStatus.ts @@ -226,7 +226,9 @@ const exists = async (path: string | undefined): Promise => { try { await access(path); return true; - } catch { + } catch (cause: unknown) { + // best-effort cleanup: optional registration probing; absence means unregistered. + void cause; return false; } }; diff --git a/src/application/Doctor.ts b/src/application/Doctor.ts index 2748001a73..3d474eeaec 100644 --- a/src/application/Doctor.ts +++ b/src/application/Doctor.ts @@ -353,7 +353,9 @@ export const systemDoctorHost = ( ) ).stdout.trim(); return `${prefix}/Hopper Disassembler.app/Contents/MacOS/hopper`; - } catch { + } catch (cause: unknown) { + // best-effort cleanup: optional Homebrew probing; absence means uninstalled. + void cause; return undefined; } }); @@ -373,7 +375,9 @@ export const systemDoctorHost = ( }) ).stdout, ); - } catch { + } catch (cause: unknown) { + // best-effort cleanup: optional version probing; failure means unavailable. + void cause; return undefined; } }, @@ -385,7 +389,9 @@ export const systemDoctorHost = ( (await hostExecFileOutput(command, arguments_, commandEnvironment)) .stdout, ); - } catch { + } catch (cause: unknown) { + // best-effort cleanup: optional path probing; failure means none found. + void cause; return []; } }, @@ -404,7 +410,9 @@ const readMacosVersion = async ( ): Promise => { try { return (await run("sw_vers", ["-productVersion"], options)).stdout.trim(); - } catch { + } catch (cause: unknown) { + // best-effort cleanup: optional OS version probing; failure means unknown. + void cause; return undefined; } }; @@ -420,7 +428,9 @@ const executableAvailable = async ( if (platform !== "linux") return true; const linked = await run("ldd", [path], options); return linuxSharedLibrariesAvailable(`${linked.stdout}\n${linked.stderr}`); - } catch { + } catch (cause: unknown) { + // best-effort cleanup: optional executable probing; failure means unavailable. + void cause; return false; } }; @@ -444,8 +454,9 @@ const manualHopperPaths = async ( if (entry.isDirectory() && /^Hopper.*\.app$/i.test(entry.name)) paths.push(join(root, entry.name, "Contents/MacOS/hopper")); } - } catch { + } catch (cause: unknown) { // A missing or unreadable optional application directory is not fatal. + void cause; } } return paths; @@ -471,7 +482,9 @@ const installedSkillIdentity = async ( /^\s{2}catalog_digest:\s*"([a-f0-9]{64})"\s*$/mu.exec(content)?.[1] ?? null, }; - } catch { + } catch (cause: unknown) { + // best-effort cleanup: optional skill probing; failure means unknown. + void cause; return undefined; } }; diff --git a/src/application/JavaScriptArtifactAnalysis.ts b/src/application/JavaScriptArtifactAnalysis.ts index 4fa873770b..e9bb474457 100644 --- a/src/application/JavaScriptArtifactAnalysis.ts +++ b/src/application/JavaScriptArtifactAnalysis.ts @@ -157,7 +157,8 @@ const parsePackage = ( let value: unknown; try { value = JSON.parse(file.text.value); - } catch { + } catch (cause: unknown) { + void cause; return invalidPackage(file, "package.json is not valid JSON."); } if (!isRecord(value)) @@ -232,7 +233,8 @@ const parseSourceMap = ( let value: unknown; try { value = JSON.parse(file.text.value); - } catch { + } catch (cause: unknown) { + void cause; return invalidSourceMap(file, "Source map is not valid JSON."); } const maps = flattenSourceMaps(value); diff --git a/src/application/JavaScriptArtifactFiles.ts b/src/application/JavaScriptArtifactFiles.ts index 28b5d064d8..72e94c414b 100644 --- a/src/application/JavaScriptArtifactFiles.ts +++ b/src/application/JavaScriptArtifactFiles.ts @@ -232,7 +232,9 @@ const readText = async ( included: true, value: new TextDecoder("utf-8", { fatal: true }).decode(bytes), }; - } catch { + } catch (cause: unknown) { + // Non-UTF8 bytes are counted; the fixed reason preserves the schema. + void cause; context.invalidUtf8 += 1; return { included: false, reason: "invalid-utf8" }; } diff --git a/src/application/JavaScriptArtifactPathResolution.ts b/src/application/JavaScriptArtifactPathResolution.ts index f00e3795b4..fc7eba4386 100644 --- a/src/application/JavaScriptArtifactPathResolution.ts +++ b/src/application/JavaScriptArtifactPathResolution.ts @@ -430,7 +430,9 @@ const packageEntry = ( if (entry === undefined) return { status: "missing" }; const legacy = packagePathValue(entry); return legacy.status === "value" ? { ...legacy, source: "legacy" } : legacy; - } catch { + } catch (cause: unknown) { + // Reflect-based manifest reads fail closed as invalid. + void cause; return { status: "invalid" }; } }; @@ -545,7 +547,9 @@ const fileUrlPath = (value: string): string | undefined => { const url = new URL(value); if (url.protocol !== "file:" || url.hostname !== "") return undefined; return decodeURIComponent(url.pathname).replace(/^\/+/, ""); - } catch { + } catch (cause: unknown) { + // Unparseable file URLs have no path to resolve. + void cause; return undefined; } }; diff --git a/src/application/JavaScriptJsonModules.ts b/src/application/JavaScriptJsonModules.ts index ed29f07211..284fa99b89 100644 --- a/src/application/JavaScriptJsonModules.ts +++ b/src/application/JavaScriptJsonModules.ts @@ -18,7 +18,8 @@ export const analyzeJavaScriptJsonModule = ( let value: unknown; try { value = JSON.parse(file.text.value); - } catch { + } catch (cause: unknown) { + void cause; return { path: file.path, sha256: file.sha256, diff --git a/src/application/JavaScriptModuleRelationships.ts b/src/application/JavaScriptModuleRelationships.ts index b825e6933f..c16aadc354 100644 --- a/src/application/JavaScriptModuleRelationships.ts +++ b/src/application/JavaScriptModuleRelationships.ts @@ -401,7 +401,9 @@ const jsonStatus = ( try { JSON.parse(file.text.value); return "included"; - } catch { + } catch (cause: unknown) { + // Invalid JSON is classified without further detail. + void cause; return "invalid"; } }; diff --git a/src/application/LinuxHopper.ts b/src/application/LinuxHopper.ts index 3f1beeb882..cc9a83d122 100644 --- a/src/application/LinuxHopper.ts +++ b/src/application/LinuxHopper.ts @@ -165,7 +165,9 @@ export const readLinuxDistribution = async (): Promise< > => { try { return parseLinuxDistribution(await readFile("/etc/os-release", "utf8")); - } catch { + } catch (cause: unknown) { + // best-effort cleanup: optional host probing; unknown distribution means unsupported. + void cause; return undefined; } }; @@ -244,7 +246,9 @@ const systemLinuxHopperInstallHost = (): LinuxHopperInstallHost => ({ return (await linuxHopperBinarySupported(path)) ? "ready" : "unsupported_hopper_build"; - } catch { + } catch (cause: unknown) { + // best-effort cleanup: optional launcher probing; unreadable means missing. + void cause; return "missing"; } }, @@ -259,7 +263,9 @@ export const linuxHopperBinarySupported = async ( const hash = createHash("sha256"); for await (const chunk of createReadStream(path)) hash.update(chunk); return hash.digest("hex") === SUPPORTED_HOPPER_SHA256; - } catch { + } catch (cause: unknown) { + // best-effort cleanup: optional build probing; unreadable means unsupported. + void cause; return false; } }; @@ -280,7 +286,9 @@ const installSystemPackage = async ( )) await execFileAsync(command.executable, command.args); return true; - } catch { + } catch (cause: unknown) { + // Package-manager failure is reported by the false return. + void cause; return false; } }; diff --git a/src/application/MacHopper.ts b/src/application/MacHopper.ts index 65adfb79bc..6ce195654c 100644 --- a/src/application/MacHopper.ts +++ b/src/application/MacHopper.ts @@ -120,7 +120,9 @@ export const installMacHopper = async ( return { status: "failed", reason: "launcher_missing" }; await host.openApplication(destination); return { status: "installed", launcherPath }; - } catch { + } catch (cause: unknown) { + // Install-stage failures share the download reason; the stage is cleaned below. + void cause; return { status: "failed", reason: "download" }; } finally { if (temporary !== undefined) { @@ -175,7 +177,9 @@ const systemMacHopperInstallHost = (): MacHopperInstallHost => ({ ]) ).stdout.trim(); return identifier === BUNDLE_IDENTIFIER; - } catch { + } catch (cause: unknown) { + // best-effort cleanup: optional bundle probing; unreadable means invalid. + void cause; return false; } }, @@ -183,7 +187,9 @@ const systemMacHopperInstallHost = (): MacHopperInstallHost => ({ try { await access(path); return true; - } catch { + } catch (cause: unknown) { + // best-effort cleanup: optional existence probing; absence means missing. + void cause; return false; } }, @@ -199,13 +205,16 @@ const systemMacHopperInstallHost = (): MacHopperInstallHost => ({ await rename(stage, destination); if (replaceExisting) await rm(previous, { recursive: true, force: true }); return true; - } catch { + } catch (cause: unknown) { + // Install failure rolls back below; the false return reports it. + void cause; await rm(stage, { recursive: true, force: true }); if (replaceExisting) { try { await rename(previous, destination); - } catch { + } catch (rollbackCause: unknown) { // The original destination was not moved, or rollback is impossible. + void rollbackCause; } } return false; @@ -215,7 +224,9 @@ const systemMacHopperInstallHost = (): MacHopperInstallHost => ({ try { await access(path); return true; - } catch { + } catch (cause: unknown) { + // best-effort cleanup: optional launcher probing; absence means not ready. + void cause; return false; } }, @@ -253,7 +264,9 @@ const commandSucceeds = async ( try { await execFileAsync(command, args); return true; - } catch { + } catch (cause: unknown) { + // best-effort cleanup: optional command probing; failure means unavailable. + void cause; return false; } }; diff --git a/src/application/ProcessCaptureJournal.ts b/src/application/ProcessCaptureJournal.ts index 6199022104..a40f11ac36 100644 --- a/src/application/ProcessCaptureJournal.ts +++ b/src/application/ProcessCaptureJournal.ts @@ -72,7 +72,9 @@ export const scheduleScenarioInteractions = ( ), ); } else terminal.kill(event.signal); - } catch { + } catch (cause: unknown) { + // Terminal dispatch failures are recorded as failed outcomes. + void cause; outcome = "failed"; } } diff --git a/src/application/ProcessCaptureLifecycle.ts b/src/application/ProcessCaptureLifecycle.ts index f38661a97f..1ea4b47e97 100644 --- a/src/application/ProcessCaptureLifecycle.ts +++ b/src/application/ProcessCaptureLifecycle.ts @@ -362,7 +362,8 @@ export const releaseProcessResources = async (options: { let failure: string | undefined; try { await options.renderer?.dispose(); - } catch { + } catch (cause: unknown) { + void cause; failure ??= "terminal renderer cleanup failed"; } if (options.terminal !== undefined && host.platform !== "win32") { @@ -377,7 +378,8 @@ export const releaseProcessResources = async (options: { } try { await host.removeTemporaryRoot(options.temporaryRoot); - } catch { + } catch (cause: unknown) { + void cause; failure ??= "temporary process root cleanup failed"; } return failure; diff --git a/src/application/ProcessCli.ts b/src/application/ProcessCli.ts index eb5af8c800..82c9c83e59 100644 --- a/src/application/ProcessCli.ts +++ b/src/application/ProcessCli.ts @@ -112,7 +112,7 @@ const parseCaptureEvidence = (input: unknown) => { let evidence; try { evidence = parseEvidence(input); - } catch { + } catch (cause: unknown) { throw invalidCaptureEvidence(); } if ( @@ -132,7 +132,7 @@ const parseCaptureEvidence = (input: unknown) => { capture: parseProcessCapture(evidence.normalized_result), locations: evidence.locations, }; - } catch { + } catch (cause: unknown) { throw invalidCaptureEvidence(); } }; @@ -157,7 +157,8 @@ const readJson = async (path: string): Promise => { let bytes; try { bytes = await readFile(path); - } catch { + } catch (cause: unknown) { + void cause; throw new ProcessCliFailure( "invalid_input", "Process input file could not be read. Check that the path exists and is readable.", @@ -166,7 +167,8 @@ const readJson = async (path: string): Promise => { try { const parsed: unknown = JSON.parse(bytes.toString("utf8")); return parsed; - } catch { + } catch (cause: unknown) { + void cause; throw new ProcessCliFailure( "invalid_input", "Process input file is not valid JSON. Repair the file, then try again.", diff --git a/src/application/ProcessSampling.ts b/src/application/ProcessSampling.ts index 6aa000c4d6..b559a8f7ac 100644 --- a/src/application/ProcessSampling.ts +++ b/src/application/ProcessSampling.ts @@ -358,7 +358,9 @@ const inspectInitializedPtyRoot = async ( let observedRunId: string | undefined; try { observedRunId = await readProcessRunId(rootPid); - } catch { + } catch (cause: unknown) { + // Unreadable run identity means the root cannot be attributed. + void cause; return undefined; } const after = await readRootIdentity(rootPid, signal); diff --git a/src/application/ReferenceSourceImportPolicy.ts b/src/application/ReferenceSourceImportPolicy.ts index 48aab577d2..0cb467d666 100644 --- a/src/application/ReferenceSourceImportPolicy.ts +++ b/src/application/ReferenceSourceImportPolicy.ts @@ -36,7 +36,8 @@ const resolveRoot = async ( ); const canonicalRoot = await realpath(resolve(requestedRoot)); return ok(canonicalRoot); - } catch { + } catch (cause: unknown) { + void cause; return err( failure("invalid-root", "Reference source root could not be resolved"), ); @@ -50,8 +51,9 @@ const buildIgnored = async ( const ignored = ignore(); try { ignored.add(await readFile(join(root, ".gitignore"), "utf8")); - } catch { + } catch (cause: unknown) { // Missing or unreadable ignore file does not authorize broader access. + void cause; } ignored.add([...DEFAULT_REFERENCE_SOURCE_IGNORE_PATTERNS]); for (const path of excludePaths) { diff --git a/src/application/ReferenceSourceVcsAdapter.ts b/src/application/ReferenceSourceVcsAdapter.ts index c33f6d2678..8b0d1466be 100644 --- a/src/application/ReferenceSourceVcsAdapter.ts +++ b/src/application/ReferenceSourceVcsAdapter.ts @@ -34,7 +34,9 @@ export const readReferenceSourceVcs = async ( const head = await resolveRef({ fs, dir: root, ref: "HEAD" }); if (isAborted(signal)) return { kind: "unknown", head: null, dirty: null }; return { kind: "git", head, dirty: null }; - } catch { + } catch (cause: unknown) { + // Unresolvable refs mean VCS state is unknown, not absent. + void cause; return { kind: "unknown", head: null, dirty: null }; } }; diff --git a/src/application/SetupClientConfiguration.ts b/src/application/SetupClientConfiguration.ts index 87d13aaa99..04eace6b93 100644 --- a/src/application/SetupClientConfiguration.ts +++ b/src/application/SetupClientConfiguration.ts @@ -77,7 +77,9 @@ const configureClientDocument = async ( original ?? (format === "toml" ? "" : "{}"), format, ); - } catch { + } catch (cause: unknown) { + // Malformed existing configuration fails the readback gate. + void cause; return { status: "failed", reason: "readback" }; } const { document, servers, serversKey } = parsed; @@ -120,7 +122,9 @@ const configureClientDocument = async ( mode: 0o600, }, ); - } catch { + } catch (cause: unknown) { + // Write failure is reported by the status reason. + void cause; return { status: "failed", reason: "write" }; } try { @@ -137,7 +141,9 @@ const configureClientDocument = async ( await restoreConfig(transactionPath, original); return { status: "failed", reason: "readback" }; } - } catch { + } catch (cause: unknown) { + // Readback failure restores the transaction before reporting. + void cause; await restoreConfig(transactionPath, original); return { status: "failed", reason: "readback" }; } @@ -166,7 +172,9 @@ export const clientConfigurationAligned = async ( servers[PRODUCT_IDENTITY.mcpServerKey], desired, ); - } catch { + } catch (cause: unknown) { + // Unreadable configuration is treated as not aligned so setup repairs it. + void cause; return false; } }; @@ -213,7 +221,9 @@ export const inspectClientConfiguration = async ( ) ) return { status: "already_current" }; - } catch { + } catch (cause: unknown) { + // Malformed configuration is reported with the invalid remediation. + void cause; return { status: "invalid", remediation: @@ -247,8 +257,9 @@ const restoreConfig = async ( try { if (original === undefined) await rm(path, { force: true }); else await writeFile(path, original, { encoding: "utf8", mode: 0o600 }); - } catch { + } catch (cause: unknown) { // The backup remains available for the remediation reported by setup. + void cause; } }; diff --git a/src/application/SetupHost.ts b/src/application/SetupHost.ts index 46cb53a73f..471fc3f194 100644 --- a/src/application/SetupHost.ts +++ b/src/application/SetupHost.ts @@ -143,7 +143,9 @@ const exists = async (path: string): Promise => { try { await access(path); return true; - } catch { + } catch (cause: unknown) { + // best-effort cleanup: optional host probing; absence means unavailable. + void cause; return false; } }; diff --git a/src/application/SetupSkill.ts b/src/application/SetupSkill.ts index 6bb880e687..367cb64b3e 100644 --- a/src/application/SetupSkill.ts +++ b/src/application/SetupSkill.ts @@ -59,7 +59,9 @@ export const canonicalSkillNeedsInstall = async ( return (await canonicalSkillFiles(home)).some( ({ content, original }) => original !== content, ); - } catch { + } catch (cause: unknown) { + // Unreadable skill state fails open to install so setup can repair it. + void cause; return true; } }; @@ -97,11 +99,14 @@ export const installCanonicalSkill = async ( if ((await readFile(destination, "utf8")) !== content) throw new Error(`skill readback mismatch: ${destination}`); return "installed"; - } catch { + } catch (cause: unknown) { + // Install failure preserves the original error outcome; report cause inline. + void cause; try { await restoreSkillFiles(changed); - } catch { + } catch (restoreCause: unknown) { // Per-file backups remain beside changed files for operator recovery. + void restoreCause; } return "failed"; } diff --git a/src/application/StoreFileLock.ts b/src/application/StoreFileLock.ts index 7d0330f31a..4f79cc060c 100644 --- a/src/application/StoreFileLock.ts +++ b/src/application/StoreFileLock.ts @@ -50,7 +50,10 @@ export const createStoreFileLock = async ( } return { release: async () => { - await removeOwner(path, owner).catch(() => undefined); + await removeOwner(path, owner).catch((cause: unknown) => { + // best-effort cleanup: lock release must not reject unhandled. + void cause; + }); }, }; } finally { @@ -85,7 +88,9 @@ export const removeStaleStoreFileLock = async ( return false; await removeOwner(path, owner); return true; - } catch { + } catch (cause: unknown) { + // best-effort cleanup: stale lock probing; failure means the lock stands. + void cause; return false; } }; diff --git a/src/application/Uninstall.ts b/src/application/Uninstall.ts index 57fbd3b181..7bad3c6a3f 100644 --- a/src/application/Uninstall.ts +++ b/src/application/Uninstall.ts @@ -150,7 +150,8 @@ const removeClient = async ( servers, serversKey: key, } = parseClientConfiguration(original, client.format)); - } catch { + } catch (cause: unknown) { + void cause; return item( client.name, "failed", @@ -172,7 +173,8 @@ const removeClient = async ( const backupPath = `${client.configPath}.rea.backup`; try { await fileSystem.copy(transactionPath, backupPath); - } catch { + } catch (cause: unknown) { + void cause; return item( client.name, "failed", @@ -200,10 +202,13 @@ const removeClient = async ( "removed", `Removed registration from ${client.configPath}.`, ); - } catch { + } catch (cause: unknown) { + // Update failure attempts a restore before reporting; cause is preserved below. + void cause; try { await fileSystem.writeText(transactionPath, original); - } catch { + } catch (restoreCause: unknown) { + void restoreCause; return item( client.name, "failed", diff --git a/src/application/UpdateRuntime.ts b/src/application/UpdateRuntime.ts index e397c493dc..3486078901 100644 --- a/src/application/UpdateRuntime.ts +++ b/src/application/UpdateRuntime.ts @@ -85,7 +85,9 @@ export const detectNpmInstallation = async ( let canonicalPackageRoot: string; try { canonicalPackageRoot = await host.canonicalPath(packageRoot); - } catch { + } catch (cause: unknown) { + // best-effort cleanup: optional install-location probing; unknown means not npm-managed. + void cause; return undefined; } try { @@ -99,8 +101,9 @@ export const detectNpmInstallation = async ( ? undefined : { prefix, packageRoot: canonicalPackageRoot }; } - } catch { + } catch (cause: unknown) { // The curl installer uses a Unix prefix that may differ from npm's current default. + void cause; } const nodeModules = dirname(canonicalPackageRoot); const library = dirname(nodeModules); diff --git a/src/application/WindowsCapabilities.ts b/src/application/WindowsCapabilities.ts index b675335ac8..df7368c9e6 100644 --- a/src/application/WindowsCapabilities.ts +++ b/src/application/WindowsCapabilities.ts @@ -126,9 +126,10 @@ const probeSymlinkCreation = async (): Promise => { } finally { try { await rm(root, { recursive: true, force: true }); - } catch { + } catch (cause: unknown) { // Cleanup failure must not replace the machine-readable probe outcome. // The probe root lives beneath the OS temporary directory and is unique. + void cause; } } }; diff --git a/src/browser/BrowserProcessStartup.ts b/src/browser/BrowserProcessStartup.ts index 1ae95fae72..85eae0553c 100644 --- a/src/browser/BrowserProcessStartup.ts +++ b/src/browser/BrowserProcessStartup.ts @@ -141,7 +141,9 @@ const readDevtoolsPort = async (path: string): Promise => { return Number.isSafeInteger(port) && port > 0 && port <= 65_535 ? port : null; - } catch { + } catch (cause: unknown) { + // Missing or unreadable port files mean the port is unknown. + void cause; return null; } }; diff --git a/src/browser/CdpCaptureDocuments.ts b/src/browser/CdpCaptureDocuments.ts index 9cceb40a61..baa7eab853 100644 --- a/src/browser/CdpCaptureDocuments.ts +++ b/src/browser/CdpCaptureDocuments.ts @@ -433,7 +433,9 @@ const domDestination = ( url: allowedSanitizedUrl(parsed.href, allowedOrigins)?.url ?? null, scope: "approved", }; - } catch { + } catch (cause: unknown) { + // Unparseable URLs are out of scope, not failures. + void cause; return { url: null, scope: "unsupported" }; } }; diff --git a/src/browser/CdpCaptureEventHandlers.ts b/src/browser/CdpCaptureEventHandlers.ts index 9a5cd1a39d..9d9f3a7dc5 100644 --- a/src/browser/CdpCaptureEventHandlers.ts +++ b/src/browser/CdpCaptureEventHandlers.ts @@ -98,7 +98,9 @@ const sourceMapForScript = ( let rawUrl: string; try { rawUrl = new URL(declaredUrl, scriptUrl).href; - } catch { + } catch (cause: unknown) { + // Unparseable URLs are recorded as unsupported, not as failures. + void cause; state.completeness.exclude("source_maps", "unsupported_url"); return { sanitized: null, raw: null }; } @@ -344,7 +346,9 @@ export const handleWebSocketCreated = ( let parsed: URL; try { parsed = new URL(rawUrl); - } catch { + } catch (cause: unknown) { + // Unparseable URLs are recorded as unsupported, not as failures. + void cause; state.completeness.exclude("websocket_connections", "unsupported_url"); return; } diff --git a/src/browser/CdpCaptureValues.ts b/src/browser/CdpCaptureValues.ts index e0cb122fe4..8eff2034b6 100644 --- a/src/browser/CdpCaptureValues.ts +++ b/src/browser/CdpCaptureValues.ts @@ -38,7 +38,9 @@ export const isHttpUrl = (value: string | undefined): boolean => { try { const protocol = new URL(value).protocol; return protocol === "http:" || protocol === "https:"; - } catch { + } catch (cause: unknown) { + // Non-URL input is not an HTTP URL. + void cause; return false; } }; diff --git a/src/browser/CdpEndpoint.ts b/src/browser/CdpEndpoint.ts index b95711c320..178ebc65b7 100644 --- a/src/browser/CdpEndpoint.ts +++ b/src/browser/CdpEndpoint.ts @@ -116,7 +116,9 @@ const sanitizeTargetTitle = (value: string, targetUrl: string): string => { let url: URL; try { url = new URL(value); - } catch { + } catch (cause: unknown) { + // Non-URL titles fall back to alias redaction. + void cause; return sanitizeTargetUrlAlias(value, targetUrl); } return url.protocol === "http:" || url.protocol === "https:" @@ -128,7 +130,9 @@ const sanitizeTargetUrlAlias = (value: string, targetUrl: string): string => { let parsed: URL; try { parsed = new URL(targetUrl); - } catch { + } catch (cause: unknown) { + // Without a parseable target URL there is nothing to redact. + void cause; return value; } const markers = [parsed.origin, `//${parsed.host}`, parsed.host]; @@ -150,7 +154,9 @@ const sanitizeSameOriginTitleUrl = (candidate: string, target: URL): string => { : candidate.startsWith(target.host) ? new URL(`${target.protocol}//${candidate}`) : new URL(candidate, target.origin); - } catch { + } catch (cause: unknown) { + // Unparseable candidates are preserved verbatim. + void cause; return candidate; } return parsed.origin === target.origin && @@ -273,7 +279,9 @@ const targetWebSocket = ( return webSocket.scope === "page" && webSocket.targetId === target.id ? { webSocket } : {}; - } catch { + } catch (cause: unknown) { + // Endpoint validation failures mean no validated transport exists. + void cause; return {}; } }; diff --git a/src/browser/CdpSafeMetadata.ts b/src/browser/CdpSafeMetadata.ts index ff4b91a209..cb0381b24c 100644 --- a/src/browser/CdpSafeMetadata.ts +++ b/src/browser/CdpSafeMetadata.ts @@ -130,7 +130,9 @@ const cspSource = ( return allowedOrigins.has(parsed.origin) ? { kind: "approved_origin", value: parsed.origin } : { kind: "external_origin", value: null }; - } catch { + } catch (cause: unknown) { + // Non-URL tokens are classified as other. + void cause; return { kind: "other", value: null }; } }; @@ -210,7 +212,9 @@ const safeDestination = ( if (!allowedOrigins.has(parsed.origin)) return { url: sanitized, scope: "outside_policy" }; return { url: sanitized, scope: "approved" }; - } catch { + } catch (cause: unknown) { + // Unparseable link values keep their raw text for the analyst. + void cause; return { url: value, scope: "unsupported" }; } }; @@ -231,7 +235,9 @@ const wellKnownHint = (url: string): AgentHint[] => { return wellKnownAgentPaths.has(path) ? [agentHint("well_known_resource", path, sanitizeBrowserUrl(url).url)] : []; - } catch { + } catch (cause: unknown) { + // Non-URL input yields no well-known hint. + void cause; return []; } }; diff --git a/src/browser/ElectronFileScope.ts b/src/browser/ElectronFileScope.ts index dc92af930e..63e1737917 100644 --- a/src/browser/ElectronFileScope.ts +++ b/src/browser/ElectronFileScope.ts @@ -10,7 +10,9 @@ export const authorizedElectronFile = async ( let url: URL; try { url = new URL(value); - } catch { + } catch (cause: unknown) { + // Non-URL input cannot authorize a local file. + void cause; return undefined; } if ( @@ -23,7 +25,9 @@ export const authorizedElectronFile = async ( let path: string; try { path = fileURLToPath(url); - } catch { + } catch (cause: unknown) { + // Unconvertible file URLs cannot authorize a local file. + void cause; return undefined; } if (!isAbsolute(path) || path.includes("\0")) return undefined; @@ -31,7 +35,9 @@ export const authorizedElectronFile = async ( try { if (!(await stat(path)).isFile()) return undefined; canonical = await realpath(path); - } catch { + } catch (cause: unknown) { + // Missing or unreadable files cannot authorize a local file. + void cause; return undefined; } return canonical; diff --git a/src/browser/JavaScriptRuntimeScope.ts b/src/browser/JavaScriptRuntimeScope.ts index 14508b9038..b448a7f27c 100644 --- a/src/browser/JavaScriptRuntimeScope.ts +++ b/src/browser/JavaScriptRuntimeScope.ts @@ -35,7 +35,9 @@ export const authorizeRuntimeLocation = async ( let url: URL; try { url = new URL(value); - } catch { + } catch (cause: unknown) { + // Non-URL input is outside the authorized runtime scope. + void cause; return { allowed: false, reason: "unsupported_location" }; } if (!["http:", "https:"].includes(url.protocol)) diff --git a/src/browser/PlaywrightScenarioArtifacts.ts b/src/browser/PlaywrightScenarioArtifacts.ts index f0a086e63a..015168824a 100644 --- a/src/browser/PlaywrightScenarioArtifacts.ts +++ b/src/browser/PlaywrightScenarioArtifacts.ts @@ -60,7 +60,8 @@ const captureScreenshot = async (page: Page) => { data_base64: bytes.toString("base64"), }, }; - } catch { + } catch (cause: unknown) { + void cause; return missing("screenshot capture failed"); } }; @@ -72,7 +73,8 @@ const captureDom = async (input: { const { page, secrets } = input; try { return textArtifact(secrets.redact(await page.content())); - } catch { + } catch (cause: unknown) { + void cause; return missing("DOM capture failed"); } }; @@ -85,7 +87,8 @@ const captureAccessibility = async (input: { try { const text = await page.locator("html").ariaSnapshot(); return textArtifact(secrets.redact(text)); - } catch { + } catch (cause: unknown) { + void cause; return missing("accessibility capture failed"); } }; @@ -126,7 +129,8 @@ const captureHistory = async (page: Page, secrets: BrowserScenarioSecrets) => { })), }; return { state: "captured" as const, value }; - } catch { + } catch (cause: unknown) { + void cause; return missing("history capture failed"); } }; @@ -169,7 +173,8 @@ const captureStorage = async (input: { })), }; return { state: "captured" as const, value }; - } catch { + } catch (cause: unknown) { + void cause; return missing("storage capture failed"); } }; diff --git a/src/browser/PlaywrightScenarioEvents.ts b/src/browser/PlaywrightScenarioEvents.ts index 4fa6237ca5..7861fae700 100644 --- a/src/browser/PlaywrightScenarioEvents.ts +++ b/src/browser/PlaywrightScenarioEvents.ts @@ -68,9 +68,10 @@ export class PlaywrightScenarioEvents { if (this.ownsContext) return true; try { return this.pages.has(request.frame().page()); - } catch { + } catch (cause: unknown) { // Initial popup navigations can lack a frame. Never guess ownership // from a URL in a shared context; connect captures are attach-limited. + void cause; return false; } }; @@ -314,6 +315,9 @@ export class PlaywrightScenarioEvents { suggested_filename: this.secrets.redact(download.suggestedFilename()), url: this.safeUrl(download.url()), }); - void download.cancel().catch(() => undefined); + void download.cancel().catch((cause: unknown) => { + // best-effort cleanup: download cancellation must not reject unhandled. + void cause; + }); } } diff --git a/src/browser/PlaywrightScenarioSession.ts b/src/browser/PlaywrightScenarioSession.ts index 8ca64e0368..567fdd980d 100644 --- a/src/browser/PlaywrightScenarioSession.ts +++ b/src/browser/PlaywrightScenarioSession.ts @@ -152,7 +152,10 @@ export class PlaywrightScenarioSession implements BrowserScenarioSessionPort { .then((lateOpened) => closePlaywrightScenarioBrowser(lateOpened, options.signal), ) - .catch(() => undefined); + .catch((cause: unknown) => { + // best-effort cleanup: late-open cleanup must not mask the boundary failure. + void cause; + }); throw cause; } try { diff --git a/src/browser/WebSourceMapFetcher.ts b/src/browser/WebSourceMapFetcher.ts index 7738d9fb21..94170bf34a 100644 --- a/src/browser/WebSourceMapFetcher.ts +++ b/src/browser/WebSourceMapFetcher.ts @@ -199,7 +199,8 @@ const normalizeSourceMap = ( status: "partial", limitation: `Module edges are incomplete: ${modules.incomplete.length} of ${originalSources.filter(({ artifact }) => artifact !== null).length} original sources could not be parsed in full (${modules.incomplete.join(", ")}).`, }; - } catch { + } catch (cause: unknown) { + void cause; return emptySourceMapItem( request, "invalid", @@ -311,7 +312,9 @@ const approvedUrl = ( parsed.password === "" && allowedOrigins.includes(parsed.origin) ); - } catch { + } catch (cause: unknown) { + // Non-URL input is not an approved source-map URL. + void cause; return false; } }; @@ -338,7 +341,9 @@ const emptySourceMapItem = ( const sanitizeSource = (value: string): string => { try { return sanitizeBrowserUrl(new URL(value).href).url; - } catch { + } catch (cause: unknown) { + // Non-URL sources are preserved verbatim. + void cause; return value; } }; @@ -349,7 +354,9 @@ const resolveOriginalSource = ( ): string | null => { try { return sanitizeSource(new URL(specifier, base).href); - } catch { + } catch (cause: unknown) { + // Unresolvable source specifiers are represented by null. + void cause; return null; } }; diff --git a/src/domain/browserObservation.ts b/src/domain/browserObservation.ts index 9dfe647c14..7be7809698 100644 --- a/src/domain/browserObservation.ts +++ b/src/domain/browserObservation.ts @@ -11,7 +11,9 @@ const parseExactOrigin = (value: string): string | undefined => { let url: URL; try { url = new URL(value); - } catch { + } catch (cause: unknown) { + // Invalid input is represented by the undefined return. + void cause; return undefined; } if ( @@ -55,7 +57,9 @@ export const browserEndpointSchema = z let url: URL; try { url = new URL(value); - } catch { + } catch (cause: unknown) { + // Invalid input is reported through the zod issue. + void cause; context.addIssue({ code: "custom", message: "Invalid CDP endpoint URL" }); return z.NEVER; } @@ -160,7 +164,9 @@ export const sanitizeBrowserUrl = (value: string): SanitizedBrowserUrl => { let parsed: URL; try { parsed = new URL(value); - } catch { + } catch (cause: unknown) { + // Unparseable input is preserved verbatim in the sanitized result. + void cause; return { url: value, origin: null, @@ -203,7 +209,9 @@ const removeUrlUserInfo = (value: string, parsed: URL): string => { return candidateUrl.username === "" && candidateUrl.password === "" ? candidate : credentialFreeHref(parsed); - } catch { + } catch (cause: unknown) { + // Fallback to the already-sanitized href when the candidate is invalid. + void cause; return credentialFreeHref(parsed); } }; diff --git a/src/domain/browserScenarioValues.ts b/src/domain/browserScenarioValues.ts index 488b1347c5..37515971e2 100644 --- a/src/domain/browserScenarioValues.ts +++ b/src/domain/browserScenarioValues.ts @@ -18,7 +18,9 @@ const browserScenarioBaseUrlSchema = z let url: URL; try { url = new URL(value); - } catch { + } catch (cause: unknown) { + // Invalid input is reported through the zod issue. + void cause; context.addIssue({ code: "custom", message: "Invalid browser URL" }); return z.NEVER; } diff --git a/src/domain/electronActiveObservation.ts b/src/domain/electronActiveObservation.ts index abae1d06bb..02d60b7481 100644 --- a/src/domain/electronActiveObservation.ts +++ b/src/domain/electronActiveObservation.ts @@ -20,7 +20,9 @@ const deepLinkUrlSchema = z .refine((value) => { try { return new URL(value).protocol.length > 0; - } catch { + } catch (cause: unknown) { + // Invalid input fails the refinement. + void cause; return false; } }, "deep-link url must be an absolute URL"); diff --git a/src/domain/interfaceBuilderGraph.ts b/src/domain/interfaceBuilderGraph.ts index e64a69e466..e0f8555cfd 100644 --- a/src/domain/interfaceBuilderGraph.ts +++ b/src/domain/interfaceBuilderGraph.ts @@ -861,7 +861,9 @@ const jsonSafeRecord = (value: Record) => if (serialized === undefined) return []; const parsed = jsonValueSchema.safeParse(JSON.parse(serialized)); return parsed.success ? [[key, parsed.data]] : []; - } catch { + } catch (cause: unknown) { + // Unserializable entries are omitted from the record. + void cause; return []; } }), diff --git a/src/domain/javascriptRuntimeReconciliationSchemas.ts b/src/domain/javascriptRuntimeReconciliationSchemas.ts index 61d0c550ec..454fc7c511 100644 --- a/src/domain/javascriptRuntimeReconciliationSchemas.ts +++ b/src/domain/javascriptRuntimeReconciliationSchemas.ts @@ -44,7 +44,9 @@ const normalizeRuntimeUrlPrefix = (value: string): string | null => { let url: URL; try { url = new URL(value); - } catch { + } catch (cause: unknown) { + // Invalid input is represented by the null return. + void cause; return null; } if ( diff --git a/src/domain/javascriptRuntimeStaticCandidates.ts b/src/domain/javascriptRuntimeStaticCandidates.ts index 29714733e0..6f72a66103 100644 --- a/src/domain/javascriptRuntimeStaticCandidates.ts +++ b/src/domain/javascriptRuntimeStaticCandidates.ts @@ -240,7 +240,9 @@ const pathBelowUrlPrefix = (prefix: string, value: string): string | null => { try { base = new URL(prefix); candidate = new URL(value); - } catch { + } catch (cause: unknown) { + // Invalid input is represented by the null return. + void cause; return null; } if ( @@ -252,7 +254,9 @@ const pathBelowUrlPrefix = (prefix: string, value: string): string | null => { return normalizeArtifactPath( decodeURIComponent(candidate.pathname.slice(base.pathname.length)), ); - } catch { + } catch (cause: unknown) { + // Undecodable path segments are represented by the null return. + void cause; return null; } }; diff --git a/src/domain/javascriptSourceParser.ts b/src/domain/javascriptSourceParser.ts index 656273cce6..64983775b0 100644 --- a/src/domain/javascriptSourceParser.ts +++ b/src/domain/javascriptSourceParser.ts @@ -18,7 +18,9 @@ export const parseJavaScriptSource = ( // recovered syntax, which loses every fact derived from that source. plugins: ["decorators-legacy", "jsx", "typescript"], }); - } catch { + } catch (cause: unknown) { + // Unparseable source is represented by the null return. + void cause; return null; } }; diff --git a/src/domain/jsonShape.ts b/src/domain/jsonShape.ts index bc6f388b7f..4dce47e43f 100644 --- a/src/domain/jsonShape.ts +++ b/src/domain/jsonShape.ts @@ -30,7 +30,9 @@ export const inferJsonShape = (text: string): JsonShape | null => { let root: unknown; try { root = JSON.parse(text); - } catch { + } catch (cause: unknown) { + // Invalid JSON is represented by the null return. + void cause; return null; } const properties = new Map< diff --git a/src/domain/webBundleAnalyzerAst.ts b/src/domain/webBundleAnalyzerAst.ts index b0a16c4df1..9b8704e635 100644 --- a/src/domain/webBundleAnalyzerAst.ts +++ b/src/domain/webBundleAnalyzerAst.ts @@ -53,7 +53,9 @@ export const resolveSpecifier = ( if (resolved.protocol !== "http:" && resolved.protocol !== "https:") return null; return sanitizeBrowserUrl(resolved.href).url; - } catch { + } catch (cause: unknown) { + // Unresolvable specifiers are represented by the null return. + void cause; return null; } }; diff --git a/src/domain/webBundleAnalyzerInspection.ts b/src/domain/webBundleAnalyzerInspection.ts index aada76ea7b..ed45f8948c 100644 --- a/src/domain/webBundleAnalyzerInspection.ts +++ b/src/domain/webBundleAnalyzerInspection.ts @@ -50,7 +50,9 @@ export const analyzeScript = ( errorRecovery: true, plugins: ["jsx", "typescript"], }); - } catch { + } catch (cause: unknown) { + // Unparseable scripts are counted; the failure needs no extra detail. + void cause; accumulator.parseFailures += 1; return; } diff --git a/src/dotnet/ManagedMetadataHeaps.ts b/src/dotnet/ManagedMetadataHeaps.ts index 532e9b6371..7bd6c760fe 100644 --- a/src/dotnet/ManagedMetadataHeaps.ts +++ b/src/dotnet/ManagedMetadataHeaps.ts @@ -134,7 +134,8 @@ export const readMetadataString = ( return new TextDecoder("utf-8", { fatal: true }).decode( bytes.subarray(start, end), ); - } catch { + } catch (cause: unknown) { + void cause; throw managedFailure( "invalid-string", "metadata.#Strings", diff --git a/src/dotnet/ManagedMetadataInventoryRows.ts b/src/dotnet/ManagedMetadataInventoryRows.ts index e8fdb5c44c..f228495260 100644 --- a/src/dotnet/ManagedMetadataInventoryRows.ts +++ b/src/dotnet/ManagedMetadataInventoryRows.ts @@ -348,7 +348,9 @@ const decodeFixedString = (blob: Buffer): string | null => { return new TextDecoder("utf-8", { fatal: true }).decode( blob.subarray(start, start + length.length), ); - } catch { + } catch (cause: unknown) { + // Invalid UTF-8 blobs decode as null. + void cause; return null; } }; diff --git a/src/ghidra/GhidraInstallation.ts b/src/ghidra/GhidraInstallation.ts index 23e4479522..200ad71720 100644 --- a/src/ghidra/GhidraInstallation.ts +++ b/src/ghidra/GhidraInstallation.ts @@ -468,7 +468,9 @@ const systemGhidraInstallationHost = (): GhidraInstallationHost => ({ readText(path) { try { return readFileSync(path, "utf8"); - } catch { + } catch (cause: unknown) { + // best-effort cleanup: optional install probing; unreadable means unknown. + void cause; return undefined; } }, @@ -476,7 +478,9 @@ const systemGhidraInstallationHost = (): GhidraInstallationHost => ({ try { accessSync(path, constants.X_OK); return true; - } catch { + } catch (cause: unknown) { + // best-effort cleanup: optional executable probing; failure means missing. + void cause; return false; } }, @@ -523,7 +527,9 @@ const executablePath = (path: string): boolean => { try { accessSync(path, constants.X_OK); return true; - } catch { + } catch (cause: unknown) { + // best-effort cleanup: optional executable probing; failure means missing. + void cause; return false; } }; diff --git a/src/ghidra/GhidraLauncher.ts b/src/ghidra/GhidraLauncher.ts index 5f3f54b324..913cc5705b 100644 --- a/src/ghidra/GhidraLauncher.ts +++ b/src/ghidra/GhidraLauncher.ts @@ -163,7 +163,12 @@ export class GhidraHeadlessLauncher implements GhidraLauncher { }); } catch (cause: unknown) { if (started !== undefined) - await cleanupStartedProcess(started, platform).catch(() => undefined); + await cleanupStartedProcess(started, platform).catch( + (cause: unknown) => { + // best-effort cleanup: started-process cleanup must not mask the launch failure. + void cause; + }, + ); return isAborted(options.signal) ? err(new AnalysisCancelledError("open_binary")) : err( diff --git a/src/ghidra/GhidraTargetSnapshot.ts b/src/ghidra/GhidraTargetSnapshot.ts index e2b6786008..ee847cfa40 100644 --- a/src/ghidra/GhidraTargetSnapshot.ts +++ b/src/ghidra/GhidraTargetSnapshot.ts @@ -35,7 +35,10 @@ export const createGhidraTargetSnapshot = async ( ); return { path: snapshotPath, sha256: observedSha256 }; } catch (cause: unknown) { - await rm(snapshotPath, { force: true }).catch(() => undefined); + await rm(snapshotPath, { force: true }).catch((cause: unknown) => { + // best-effort cleanup: snapshot removal must not mask the copy failure. + void cause; + }); throw cause; } }; diff --git a/src/hopper/BridgeLauncher.ts b/src/hopper/BridgeLauncher.ts index 78a6a7c41a..efa5563f5e 100644 --- a/src/hopper/BridgeLauncher.ts +++ b/src/hopper/BridgeLauncher.ts @@ -449,7 +449,9 @@ const processIsRunning = async (executablePath: string): Promise => { return processes.stdout .split("\n") .some((command) => command.trim() === executablePath); - } catch { + } catch (cause: unknown) { + // best-effort cleanup: optional process probing; failure means not running. + void cause; return false; } }; diff --git a/src/hopper/HopperAnalysisProfile.ts b/src/hopper/HopperAnalysisProfile.ts index 506105dffa..c2eb0ea83c 100644 --- a/src/hopper/HopperAnalysisProfile.ts +++ b/src/hopper/HopperAnalysisProfile.ts @@ -125,7 +125,9 @@ const sha256File = async ( return signalIsAborted(signal) ? err(new AnalysisCancelledError("open_binary")) : ok(hash.digest("hex")); - } catch { + } catch (cause: unknown) { + // best-effort cleanup: unreadable targets mean no digest; abort still cancels. + void cause; return signalIsAborted(signal) ? err(new AnalysisCancelledError("open_binary")) : ok(undefined); diff --git a/src/hopper/HopperCleanup.ts b/src/hopper/HopperCleanup.ts index 588c25afa8..e73e800791 100644 --- a/src/hopper/HopperCleanup.ts +++ b/src/hopper/HopperCleanup.ts @@ -166,8 +166,9 @@ const stopProcess = async ( ); try { input.onDiagnostic?.(diagnostic); - } catch { + } catch (cause: unknown) { // Diagnostic consumers cannot change the already-observed cleanup result. + void cause; } input.logger.info(diagnostic, "Owned Hopper launcher shutdown completed"); if (stopped.status !== "incomplete") { diff --git a/src/hopper/HopperClient.ts b/src/hopper/HopperClient.ts index d259389f41..05f951e200 100644 --- a/src/hopper/HopperClient.ts +++ b/src/hopper/HopperClient.ts @@ -397,7 +397,9 @@ export class HopperClient { ); try { await access(socketPath); - } catch { + } catch (cause: unknown) { + // best-effort cleanup: socket polling; absence means keep waiting. + void cause; if ((await deadline.wait(50)) === "aborted") return err(startupInterruption(deadline)); continue; @@ -476,7 +478,9 @@ export class HopperClient { category: message.event.error.type, message: message.event.error.message, }); - } catch { + } catch (cause: unknown) { + // best-effort cleanup: diagnostic consumers must not break the client. + void cause; this.#logger.warn( { requestId: message.id }, "Hopper bridge diagnostic consumer rejected an event", diff --git a/src/hopper/HopperProvider.ts b/src/hopper/HopperProvider.ts index 439287fc93..a096309eeb 100644 --- a/src/hopper/HopperProvider.ts +++ b/src/hopper/HopperProvider.ts @@ -124,7 +124,9 @@ export class HopperProvider implements AnalysisProviderCandidate { reason: null, diagnostics, }; - } catch { + } catch (cause: unknown) { + // best-effort cleanup: optional launcher probing; failure means unavailable. + void cause; return { status: "unavailable", code: "executable_missing", diff --git a/src/hopper/HopperRequestQueue.ts b/src/hopper/HopperRequestQueue.ts index 0ae4c22fb9..97e9b7d2fa 100644 --- a/src/hopper/HopperRequestQueue.ts +++ b/src/hopper/HopperRequestQueue.ts @@ -274,10 +274,14 @@ export class HopperRequestQueue { #reportUpdate(entry: QueuedRequest, update: ProgressUpdate): void { try { void Promise.resolve(entry.progress?.report(update)).catch( - () => undefined, + (cause: unknown) => { + // best-effort cleanup: progress rejection bookkeeping only. + void cause; + }, ); - } catch { + } catch (cause: unknown) { // Progress observation cannot change the request outcome. + void cause; } } } diff --git a/src/hopper/LinuxPrivateDisplayProbe.ts b/src/hopper/LinuxPrivateDisplayProbe.ts index 526c7be068..26d081189f 100644 --- a/src/hopper/LinuxPrivateDisplayProbe.ts +++ b/src/hopper/LinuxPrivateDisplayProbe.ts @@ -112,7 +112,9 @@ export const runLinuxPrivateDisplayProbe: LinuxPrivateDisplayProbeRunner = runId: randomUUID(), expectedCommand: null, }); - } catch { + } catch (cause: unknown) { + // best-effort cleanup: probe spawn failure is reported as launch-failed. + void cause; return emptyProcessResult("launch-failed"); } const launch: ProviderProcessLaunch = { @@ -198,7 +200,9 @@ const evaluatedProbe = async ( timeoutMs, ...(options.signal === undefined ? {} : { signal: options.signal }), }); - } catch { + } catch (cause: unknown) { + // Runner failure is reported with the synthetic probe_failed diagnostic. + void cause; return { ok: false, diagnostic: syntheticDiagnostic(strategy, "probe_failed"), diff --git a/src/native/AppleDispatchMetadata.ts b/src/native/AppleDispatchMetadata.ts index 3bd301c6d4..c485bfc438 100644 --- a/src/native/AppleDispatchMetadata.ts +++ b/src/native/AppleDispatchMetadata.ts @@ -162,8 +162,9 @@ export const decodeAppleDispatchMetadata = ( let fileOffset: number | null = null; try { fileOffset = offset(target); - } catch { - /* serialized target remains unresolved */ + } catch (cause: unknown) { + // best-effort cleanup: serialized target remains unresolved. + void cause; } if (admit()) result.relative_pointers.push({ @@ -204,8 +205,9 @@ export const decodeAppleDispatchMetadata = ( "Method implementation is not in an executable segment", ); implementationLocation = location(implementation); - } catch { - /* external/chained pointers remain unresolved */ + } catch (cause: unknown) { + // best-effort cleanup: external/chained pointers remain unresolved. + void cause; } result.objc_dispatch_implementations.push({ class_name: className, @@ -269,7 +271,9 @@ export const decodeAppleDispatchMetadata = ( superclassName = string( pointer((pointer(superclass + 32n) & ~7n) + 24n), ); - } catch { + } catch (cause: unknown) { + // Superclass resolution failure is recorded; the cause adds no identity. + void cause; failures.push( `Superclass pointer ${hex(superclass)} of ${name} requires unsupported binding/fixup resolution`, ); @@ -294,7 +298,9 @@ export const decodeAppleDispatchMetadata = ( let value: number | null = null; try { value = u32(encodedOffset); - } catch { + } catch (cause: unknown) { + // Unresolved offsets are recorded; the cause adds no identity. + void cause; failures.push(`Unresolved ivar offset pointer ${hex(encodedOffset)}`); } result.objc_ivars.push({ diff --git a/src/native/NativeUiObservation.ts b/src/native/NativeUiObservation.ts index b58964d11b..f788075c3c 100644 --- a/src/native/NativeUiObservation.ts +++ b/src/native/NativeUiObservation.ts @@ -190,7 +190,9 @@ const observeWithHelper = async ( signal.addEventListener("abort", abort, { once: true }); if (signal.aborted) abort(); }); - } catch { + } catch (cause: unknown) { + // Cancellation is recorded as a cancelled outcome. + void cause; results.push({ index, kind: step.kind, diff --git a/src/process/ProcessOwnership.ts b/src/process/ProcessOwnership.ts index 9b00dd3fd8..d74961401c 100644 --- a/src/process/ProcessOwnership.ts +++ b/src/process/ProcessOwnership.ts @@ -116,7 +116,10 @@ export const cleanupWindowsProcessTree = async ( try { const result = await host.terminateTree(rootPid); return { cleaned: true, signaled: result === "terminated" }; - } catch { + } catch (cause: unknown) { + // The reason string is a pinned validation contract; keep it stable and + // do not interpolate the cause into caller-visible diagnostics here. + void cause; return { cleaned: false, reason: diff --git a/src/process/ProcessOwnershipObservation.ts b/src/process/ProcessOwnershipObservation.ts index f1cd15e731..ee835dcb9b 100644 --- a/src/process/ProcessOwnershipObservation.ts +++ b/src/process/ProcessOwnershipObservation.ts @@ -221,7 +221,9 @@ const processIsGone = async ( return !liveProcesses(await host.listProcesses()).some( (process) => process.pid === pid, ); - } catch { + } catch (cause: unknown) { + // best-effort cleanup: optional liveness probing; failure means not gone. + void cause; return false; } }; diff --git a/src/server/mcpProgress.ts b/src/server/mcpProgress.ts index 1b2aa09a40..0cbe0a1946 100644 --- a/src/server/mcpProgress.ts +++ b/src/server/mcpProgress.ts @@ -27,8 +27,9 @@ export const mcpProgressReporter = ( message: `${update.phase}: ${update.message}`, }, }); - } catch { + } catch (cause: unknown) { // Progress is observational; transport failure cannot change tool truth. + void cause; } }, { minimumIntervalMs: 100 },