- Added telemetry utility to capture application events and metrics. - Integrated PostHog for event tracking with distinct user identification. - Implemented telemetry initialization, event capturing, and shutdown procedures. feat: add UV environment setup for Python management - Created utilities to manage Python installation and configuration. - Implemented network optimization checks for Python installation mirrors. - Added functions to set up managed Python environments with error handling. feat: enhance host API communication with token management - Introduced host API token retrieval and management for secure requests. - Updated host API fetch functions to include token in headers. - Added support for creating event sources with authentication. test: add comprehensive tests for gateway protocol and startup helpers - Implemented unit tests for gateway protocol helpers, event dispatching, and state management. - Added tests for startup recovery strategies and process policies. - Ensured coverage for connection monitoring and restart governance logic.
117 lines
3.2 KiB
TypeScript
117 lines
3.2 KiB
TypeScript
import { randomUUID } from 'node:crypto';
|
|
import { app } from 'electron';
|
|
import axios from 'axios';
|
|
import logManager from '@electron/service/logger';
|
|
import configManager from '@electron/service/config-service';
|
|
|
|
const POSTHOG_API_KEY = 'phc_aGNegeJQP5FzNiF2rEoKqQbkuCpiiETMttplibXpB0n';
|
|
const POSTHOG_HOST = 'https://us.i.posthog.com';
|
|
const TELEMETRY_REQUEST_TIMEOUT_MS = 2_500;
|
|
const TELEMETRY_SHUTDOWN_TIMEOUT_MS = 1_500;
|
|
|
|
let telemetryEnabled = false;
|
|
let distinctId = '';
|
|
const pendingCaptures = new Set<Promise<void>>();
|
|
|
|
function getCommonProperties(): Record<string, unknown> {
|
|
return {
|
|
$app_version: app.getVersion(),
|
|
$os: process.platform,
|
|
os_tag: process.platform,
|
|
arch: process.arch,
|
|
};
|
|
}
|
|
|
|
function queueCapture(event: string, properties: Record<string, unknown>): void {
|
|
let capturePromise: Promise<void>;
|
|
const request = axios.post(
|
|
`${POSTHOG_HOST}/capture/`,
|
|
{
|
|
api_key: POSTHOG_API_KEY,
|
|
event,
|
|
properties: {
|
|
distinct_id: distinctId,
|
|
...properties,
|
|
},
|
|
},
|
|
{
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
timeout: TELEMETRY_REQUEST_TIMEOUT_MS,
|
|
validateStatus: () => true,
|
|
},
|
|
).then((response) => {
|
|
if (response.status >= 400) {
|
|
logManager.debug(`Telemetry backend rejected event "${event}" with status ${response.status}`);
|
|
}
|
|
}).catch((error) => {
|
|
logManager.debug(`Failed to capture telemetry event "${event}":`, error);
|
|
}).finally(() => {
|
|
pendingCaptures.delete(capturePromise);
|
|
});
|
|
|
|
capturePromise = request.then(() => {});
|
|
pendingCaptures.add(capturePromise);
|
|
}
|
|
|
|
export async function initTelemetry(): Promise<void> {
|
|
telemetryEnabled = Boolean(configManager.get('telemetryEnabled' as never));
|
|
if (!telemetryEnabled) {
|
|
logManager.info('Telemetry is disabled; observability stays local-only');
|
|
return;
|
|
}
|
|
|
|
const storedDistinctId = configManager.get<string | null>('machineId' as never);
|
|
distinctId = storedDistinctId && storedDistinctId.trim()
|
|
? storedDistinctId
|
|
: randomUUID();
|
|
if (!storedDistinctId) {
|
|
configManager.set('machineId' as never, distinctId);
|
|
}
|
|
|
|
const hasReportedInstall = Boolean(configManager.get('hasReportedInstall' as never));
|
|
if (!hasReportedInstall) {
|
|
captureTelemetryEvent('app_installed');
|
|
configManager.set('hasReportedInstall' as never, true);
|
|
}
|
|
captureTelemetryEvent('app_opened');
|
|
}
|
|
|
|
export function trackMetric(event: string, properties: Record<string, unknown> = {}): void {
|
|
logManager.info(`[metric] ${event}`, properties);
|
|
}
|
|
|
|
export function captureTelemetryEvent(
|
|
event: string,
|
|
properties: Record<string, unknown> = {},
|
|
): void {
|
|
if (!telemetryEnabled || !distinctId) {
|
|
return;
|
|
}
|
|
|
|
const mergedProperties = {
|
|
...getCommonProperties(),
|
|
...properties,
|
|
};
|
|
queueCapture(event, mergedProperties);
|
|
}
|
|
|
|
export async function shutdownTelemetry(): Promise<void> {
|
|
telemetryEnabled = false;
|
|
if (pendingCaptures.size === 0) {
|
|
distinctId = '';
|
|
return;
|
|
}
|
|
|
|
const captures = Array.from(pendingCaptures);
|
|
await Promise.race([
|
|
Promise.allSettled(captures).then(() => undefined),
|
|
new Promise<void>((resolve) => {
|
|
setTimeout(resolve, TELEMETRY_SHUTDOWN_TIMEOUT_MS);
|
|
}),
|
|
]);
|
|
|
|
distinctId = '';
|
|
}
|