mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-19 13:33:35 +08:00
Desktop failures were previously hard to debug from issue reports because server-side and CLI startup details were only partially visible in the UI. This adds a dedicated cc-haha diagnostics store with sanitized structured events, runtime error summaries, a Settings diagnostics view, and an exportable bundle that users can attach to reports. Constraint: Diagnostic exports must not include chat content, file contents, full environment variables, API keys, bearer tokens, cookies, or OAuth tokens. Rejected: Export raw server logs | easier to debug but too likely to leak secrets and private workspace data. Rejected: Keep diagnostics only in transient UI errors | still leaves maintainers unable to diagnose later GitHub issues. Confidence: high Scope-risk: moderate Directive: Do not add raw transcript, prompt, attachment, or environment dumps to diagnostics without a separate privacy review. Tested: ANTHROPIC_API_KEY=test-key bun test src/server/__tests__/diagnostics-service.test.ts Tested: ANTHROPIC_API_KEY=test-key bun test src/server/__tests__/conversation-service.test.ts Tested: ANTHROPIC_API_KEY=test-key bun test src/server/__tests__/conversations.test.ts Tested: cd desktop && bun run test src/__tests__/diagnosticsSettings.test.tsx --run Tested: cd desktop && bun run lint Tested: cd desktop && bun run build Tested: agent-browser E2E on local server/dev UI for CLI startup failure, provider test failure, diagnostics tab, copy summary, export bundle, and tar/secret scan Not-tested: Destructive clear-logs button in browser E2E; local deletion was intentionally not clicked.
54 lines
1.3 KiB
TypeScript
54 lines
1.3 KiB
TypeScript
/**
|
|
* Unified error handling utilities
|
|
*/
|
|
|
|
import { diagnosticsService } from '../services/diagnosticsService.js'
|
|
|
|
export class ApiError extends Error {
|
|
constructor(
|
|
public statusCode: number,
|
|
message: string,
|
|
public code?: string
|
|
) {
|
|
super(message)
|
|
this.name = 'ApiError'
|
|
}
|
|
|
|
static badRequest(message: string) {
|
|
return new ApiError(400, message, 'BAD_REQUEST')
|
|
}
|
|
|
|
static notFound(message: string) {
|
|
return new ApiError(404, message, 'NOT_FOUND')
|
|
}
|
|
|
|
static conflict(message: string) {
|
|
return new ApiError(409, message, 'CONFLICT')
|
|
}
|
|
|
|
static internal(message: string) {
|
|
return new ApiError(500, message, 'INTERNAL_ERROR')
|
|
}
|
|
}
|
|
|
|
export function errorResponse(error: unknown): Response {
|
|
if (error instanceof ApiError) {
|
|
return Response.json(
|
|
{ error: error.code || 'ERROR', message: error.message },
|
|
{ status: error.statusCode }
|
|
)
|
|
}
|
|
|
|
void diagnosticsService.recordEvent({
|
|
type: 'api_unhandled_error',
|
|
severity: 'error',
|
|
summary: error instanceof Error ? error.message : String(error),
|
|
details: error,
|
|
})
|
|
console.error('[Server] Unexpected error:', error)
|
|
return Response.json(
|
|
{ error: 'INTERNAL_ERROR', message: 'An unexpected error occurred' },
|
|
{ status: 500 }
|
|
)
|
|
}
|