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:
DEV_DSW
2026-04-23 17:21:57 +08:00
parent 655e7c51d2
commit 71bcc3b3c5
39 changed files with 5504 additions and 313 deletions

View File

@@ -0,0 +1,110 @@
import logManager from '@electron/service/logger';
import { LifecycleSupersededError } from './lifecycle-controller';
import { getGatewayStartupRecoveryAction } from './startup-recovery';
export interface ExistingGatewayInfo {
port: number;
externalToken?: string;
}
type StartupHooks = {
port: number;
shouldWaitForPortFree: boolean;
maxStartAttempts?: number;
hasOwnedProcess?: () => boolean;
assertLifecycle?: (phase: string) => void;
resetStartupStderrLines: () => void;
getStartupStderrLines: () => string[];
findExistingGateway: (port: number) => Promise<ExistingGatewayInfo | null>;
connect: (port: number, externalToken?: string) => Promise<void>;
onConnectedToExistingGateway: () => void;
waitForPortFree: (port: number) => Promise<void>;
startProcess: () => Promise<void>;
waitForReady: (port: number) => Promise<void>;
onConnectedToManagedGateway: () => void;
runDoctorRepair?: () => Promise<boolean>;
onDoctorRepairSuccess?: () => void;
delay: (ms: number) => Promise<void>;
};
export async function runGatewayStartupSequence(hooks: StartupHooks): Promise<void> {
let configRepairAttempted = false;
let startAttempts = 0;
const maxStartAttempts = hooks.maxStartAttempts ?? 3;
while (true) {
startAttempts += 1;
hooks.assertLifecycle?.('start');
hooks.resetStartupStderrLines();
try {
const existing = await hooks.findExistingGateway(hooks.port);
hooks.assertLifecycle?.('start/find-existing');
if (existing) {
logManager.debug(`Found existing Gateway on port ${existing.port}`);
await hooks.connect(existing.port, existing.externalToken);
hooks.assertLifecycle?.('start/connect-existing');
hooks.onConnectedToExistingGateway();
return;
}
if (hooks.hasOwnedProcess?.()) {
logManager.info('Owned Gateway process still alive; waiting for it to become ready');
await hooks.waitForReady(hooks.port);
hooks.assertLifecycle?.('start/wait-ready-owned');
await hooks.connect(hooks.port);
hooks.assertLifecycle?.('start/connect-owned');
hooks.onConnectedToExistingGateway();
return;
}
if (hooks.shouldWaitForPortFree) {
await hooks.waitForPortFree(hooks.port);
hooks.assertLifecycle?.('start/wait-port');
}
await hooks.startProcess();
hooks.assertLifecycle?.('start/start-process');
await hooks.waitForReady(hooks.port);
hooks.assertLifecycle?.('start/wait-ready');
await hooks.connect(hooks.port);
hooks.assertLifecycle?.('start/connect');
hooks.onConnectedToManagedGateway();
return;
} catch (error) {
if (error instanceof LifecycleSupersededError) {
throw error;
}
const recoveryAction = getGatewayStartupRecoveryAction({
startupError: error,
startupStderrLines: hooks.getStartupStderrLines(),
configRepairAttempted,
attempt: startAttempts,
maxAttempts: maxStartAttempts,
});
if (recoveryAction === 'repair' && hooks.runDoctorRepair) {
configRepairAttempted = true;
logManager.warn(
'Detected invalid OpenClaw config during Gateway startup; running doctor repair before retry',
);
const repaired = await hooks.runDoctorRepair();
if (repaired) {
logManager.info('OpenClaw doctor repair completed; retrying Gateway startup');
hooks.onDoctorRepairSuccess?.();
continue;
}
logManager.error('OpenClaw doctor repair failed; not retrying Gateway startup');
}
if (recoveryAction === 'retry') {
logManager.warn(`Transient start error: ${String(error)}. Retrying... (${startAttempts}/${maxStartAttempts})`);
await hooks.delay(1000);
continue;
}
throw error;
}
}
}