feat: implement telemetry system for application usage tracking
- 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.
This commit is contained in:
107
electron/gateway/process-policy.ts
Normal file
107
electron/gateway/process-policy.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
export interface ReconnectConfig {
|
||||
maxAttempts: number;
|
||||
baseDelay: number;
|
||||
maxDelay: number;
|
||||
}
|
||||
|
||||
export const DEFAULT_RECONNECT_CONFIG: ReconnectConfig = {
|
||||
maxAttempts: 10,
|
||||
baseDelay: 1000,
|
||||
maxDelay: 30000,
|
||||
};
|
||||
|
||||
export function nextLifecycleEpoch(currentEpoch: number): number {
|
||||
return currentEpoch + 1;
|
||||
}
|
||||
|
||||
export function isLifecycleSuperseded(expectedEpoch: number, currentEpoch: number): boolean {
|
||||
return expectedEpoch !== currentEpoch;
|
||||
}
|
||||
|
||||
export interface ReconnectAttemptContext {
|
||||
scheduledEpoch: number;
|
||||
currentEpoch: number;
|
||||
shouldReconnect: boolean;
|
||||
}
|
||||
|
||||
export function getReconnectSkipReason(context: ReconnectAttemptContext): string | null {
|
||||
if (!context.shouldReconnect) {
|
||||
return 'auto-reconnect disabled';
|
||||
}
|
||||
if (isLifecycleSuperseded(context.scheduledEpoch, context.currentEpoch)) {
|
||||
return `stale reconnect callback (scheduledEpoch=${context.scheduledEpoch}, currentEpoch=${context.currentEpoch})`;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export interface ReconnectScheduleContext {
|
||||
shouldReconnect: boolean;
|
||||
hasReconnectTimer: boolean;
|
||||
reconnectAttempts: number;
|
||||
maxAttempts: number;
|
||||
baseDelay: number;
|
||||
maxDelay: number;
|
||||
}
|
||||
|
||||
export type ReconnectScheduleDecision =
|
||||
| { action: 'skip'; reason: string }
|
||||
| { action: 'already-scheduled' }
|
||||
| { action: 'fail'; attempts: number; maxAttempts: number }
|
||||
| { action: 'schedule'; nextAttempt: number; maxAttempts: number; delay: number };
|
||||
|
||||
export function getReconnectScheduleDecision(
|
||||
context: ReconnectScheduleContext,
|
||||
): ReconnectScheduleDecision {
|
||||
if (!context.shouldReconnect) {
|
||||
return { action: 'skip', reason: 'auto-reconnect disabled' };
|
||||
}
|
||||
|
||||
if (context.hasReconnectTimer) {
|
||||
return { action: 'already-scheduled' };
|
||||
}
|
||||
|
||||
if (context.reconnectAttempts >= context.maxAttempts) {
|
||||
return {
|
||||
action: 'fail',
|
||||
attempts: context.reconnectAttempts,
|
||||
maxAttempts: context.maxAttempts,
|
||||
};
|
||||
}
|
||||
|
||||
const delay = Math.min(
|
||||
context.baseDelay * Math.pow(2, context.reconnectAttempts),
|
||||
context.maxDelay,
|
||||
);
|
||||
|
||||
return {
|
||||
action: 'schedule',
|
||||
nextAttempt: context.reconnectAttempts + 1,
|
||||
maxAttempts: context.maxAttempts,
|
||||
delay,
|
||||
};
|
||||
}
|
||||
|
||||
export type GatewayLifecycleState = 'stopped' | 'starting' | 'running' | 'error' | 'reconnecting';
|
||||
|
||||
export interface RestartDeferralContext {
|
||||
state: GatewayLifecycleState;
|
||||
startLock: boolean;
|
||||
}
|
||||
|
||||
export function shouldDeferRestart(context: RestartDeferralContext): boolean {
|
||||
return context.startLock || context.state === 'starting' || context.state === 'reconnecting';
|
||||
}
|
||||
|
||||
export interface DeferredRestartActionContext extends RestartDeferralContext {
|
||||
hasPendingRestart: boolean;
|
||||
shouldReconnect: boolean;
|
||||
}
|
||||
|
||||
export type DeferredRestartAction = 'none' | 'wait' | 'drop' | 'execute';
|
||||
|
||||
export function getDeferredRestartAction(context: DeferredRestartActionContext): DeferredRestartAction {
|
||||
if (!context.hasPendingRestart) return 'none';
|
||||
if (shouldDeferRestart(context)) return 'wait';
|
||||
if (!context.shouldReconnect) return 'drop';
|
||||
return 'execute';
|
||||
}
|
||||
Reference in New Issue
Block a user