Home

mweinbach / agent-coworker

publicmweinbach/agent-coworker
Overview Code History Branches Pull requestsIssuesInsights
main
HomeOverview Code PRsIssues
mweinbach/agent-coworker/apps/desktop/electron/services/crashReporting.ts
Raw
1import os from "node:os";2import { app } from "electron";3import { resolveTelemetryConsent } from "../../../../src/telemetry/config";4import {5  type CrashReportingEnv,6  type CrashReportingSdk,7
type CrashReportingStatus,
8 captureError,
9 initCrashReporting,
10 resolveCrashReportingConfig,
11 shutdownCrashReporting,
12} from "../../../../src/telemetry/crashReporting";
13import type { PersistedPrivacyTelemetrySettings } from "../../src/app/types";
14import { writeLocalLog } from "./localLogs";
15 
16let processHandlersRegistered = false;
17 
18function appVersion(): string {
19 return app.getVersion().trim() || "unknown";
20}
21 
22export function resolveDesktopMainCrashReportingConfig(
23 privacyTelemetrySettings?: PersistedPrivacyTelemetrySettings | null,
24 env: CrashReportingEnv = process.env,
25) {
26 const settings = resolveTelemetryConsent({
27 settings: privacyTelemetrySettings,
28 env,
29 isPackaged: app.isPackaged,
30 });
31 return resolveCrashReportingConfig({
32 component: "electron-main",
33 enabled: settings.crashReportsEnabled,
34 env,
35 fallbackRelease: appVersion(),
36 appVersion: appVersion(),
37 isPackaged: app.isPackaged,
38 platform: process.platform,
39 arch: process.arch,
40 });
41}
42 
43export function applyCrashReportingProcessEnv(
44 privacyTelemetrySettings?: PersistedPrivacyTelemetrySettings | null,
45 env: NodeJS.ProcessEnv = process.env,
46): void {
47 const config = resolveDesktopMainCrashReportingConfig(privacyTelemetrySettings, env);
48 env.COWORK_CRASH_REPORTS_ENABLED = config.enabled ? "true" : "false";
49 
50 if (config.dsn) {
51 env.COWORK_SENTRY_DSN = config.dsn;
52 } else {
53 delete env.COWORK_SENTRY_DSN;
54 }
55 
56 if (config.release) {
57 env.COWORK_RELEASE = config.release;
58 }
59 env.COWORK_SENTRY_ENVIRONMENT = config.environment;
60}
61 
62export function registerMainCrashReportingHandlers(): void {
63 if (processHandlersRegistered) {
64 return;
65 }
66 processHandlersRegistered = true;
67 
68 process.on("uncaughtExceptionMonitor", (error) => {
69 captureError(error, {
70 tags: { operation: "unhandled_exception" },
71 });
72 });
73 
74 process.on("unhandledRejection", (reason) => {
75 captureError(reason, {
76 tags: { operation: "unhandled_rejection" },
77 extra: { reasonType: typeof reason },
78 });
79 });
80}
81 
82export async function initElectronMainCrashReporting(
83 privacyTelemetrySettings?: PersistedPrivacyTelemetrySettings | null,
84): Promise<CrashReportingStatus> {
85 applyCrashReportingProcessEnv(privacyTelemetrySettings);
86 const settings = resolveTelemetryConsent({
87 settings: privacyTelemetrySettings,
88 env: process.env,
89 isPackaged: app.isPackaged,
90 });
91 if (!settings.crashReportsEnabled) {
92 await shutdownCrashReporting();
93 }
94 
95 const status = await initCrashReporting({
96 component: "electron-main",
97 enabled: settings.crashReportsEnabled,
98 env: process.env,
99 fallbackRelease: appVersion(),
100 appVersion: appVersion(),
101 isPackaged: app.isPackaged,
102 platform: process.platform,
103 arch: process.arch,
104 homeDir: os.homedir(),
105 tags: {
106 component: "electron-main",
107 platform: process.platform,
108 arch: process.arch,
109 appVersion: appVersion(),
110 packaged: app.isPackaged,
111 },
112 loadSdk: async () => {
113 const sdk = await import("@sentry/electron/main");
114 return sdk as unknown as CrashReportingSdk;
115 },
116 });
117 
118 if (status.initialized) {
119 registerMainCrashReportingHandlers();
120 }
121 writeLocalLog("desktop-main.log", "info", "crash-reporting", "crash reporting status", {
122 initialized: status.initialized,
123 reason: status.reason,
124 detail: status.detail,
125 enabled: status.enabled,
126 dsnConfigured: status.dsnConfigured,
127 });
128 return status;
129}
130 
131export { captureError as captureCrashReportingError };
132