cc-haha/desktop/src/lib/diagnosticsCapture.ts
程序员阿江(Relakkes) f2437bf724 feat: Make diagnostics actionable for runtime failures
Diagnostics exports previously preserved only compact summaries, which made user-uploaded issue bundles hard to debug. This records richer sanitized details from server, CLI, SDK, browser, React, and desktop API failure paths while keeping request bodies and secrets out of client-side reports.

Constraint: Diagnostic bundles must be useful for GitHub issues without including chat contents, file contents, full environments, or API keys.
Rejected: Only expand the Settings UI summary | the exported bundle would still miss hidden runtime failures.
Confidence: high
Scope-risk: moderate
Directive: Keep diagnostics write paths best-effort and non-blocking; do not add request-body capture without a redaction review.
Tested: bun test src/server/__tests__/diagnostics-service.test.ts src/server/__tests__/conversation-service.test.ts
Tested: cd desktop && bun run test src/api/client.test.ts src/__tests__/diagnosticsSettings.test.tsx --run
Tested: bun run check:server
Tested: bun run check:desktop
Tested: agent-browser E2E on ports 37652/41752 captured client_unhandled_rejection and exported a redacted tar.gz bundle
2026-05-05 17:57:50 +08:00

66 lines
1.7 KiB
TypeScript

import React from 'react'
import { rawRecordDiagnosticEvent } from '../api/client'
let installed = false
export function installClientDiagnosticsCapture() {
if (installed || typeof window === 'undefined') return
installed = true
window.addEventListener('error', (event) => {
void reportClientError('client_window_error', event.message || 'Window error', {
filename: event.filename,
lineno: event.lineno,
colno: event.colno,
error: normalizeError(event.error),
})
})
window.addEventListener('unhandledrejection', (event) => {
void reportClientError('client_unhandled_rejection', summarizeUnknown(event.reason), {
reason: normalizeError(event.reason),
})
})
}
export function reportReactError(error: unknown, errorInfo: React.ErrorInfo) {
return reportClientError('client_react_error_boundary', summarizeUnknown(error), {
error: normalizeError(error),
componentStack: errorInfo.componentStack,
})
}
function reportClientError(type: string, summary: string, details: Record<string, unknown>) {
return rawRecordDiagnosticEvent({
type,
severity: 'error',
summary,
details: {
url: window.location.href,
userAgent: navigator.userAgent,
...details,
},
})
}
function summarizeUnknown(value: unknown): string {
if (value instanceof Error) return value.message || value.name
if (typeof value === 'string') return value
try {
return JSON.stringify(value)
} catch {
return String(value)
}
}
function normalizeError(value: unknown): unknown {
if (value instanceof Error) {
return {
name: value.name,
message: value.message,
stack: value.stack,
}
}
return value
}