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,91 @@
const INVALID_CONFIG_PATTERNS: RegExp[] = [
/\binvalid config\b/i,
/\bconfig invalid\b/i,
/\bunrecognized key\b/i,
/\brun:\s*openclaw doctor --fix\b/i,
];
const TRANSIENT_START_ERROR_PATTERNS: RegExp[] = [
/WebSocket closed before handshake/i,
/ECONNREFUSED/i,
/Gateway exited before spawn completed/i,
/Gateway exited before becoming ready/i,
/Gateway failed to become ready on port/i,
/Timed out waiting for connect\.challenge/i,
/Connect handshake timeout/i,
/Port \d+ still occupied after \d+ms/i,
];
function normalizeLogLine(value: string): string {
return value.trim();
}
export function isInvalidConfigSignal(text: string): boolean {
const normalized = normalizeLogLine(text);
if (!normalized) {
return false;
}
return INVALID_CONFIG_PATTERNS.some((pattern) => pattern.test(normalized));
}
export function hasInvalidConfigFailureSignal(
startupError: unknown,
startupStderrLines: string[],
): boolean {
for (const line of startupStderrLines) {
if (isInvalidConfigSignal(line)) {
return true;
}
}
const errorText = startupError instanceof Error
? `${startupError.name}: ${startupError.message}`
: String(startupError ?? '');
return isInvalidConfigSignal(errorText);
}
export function shouldAttemptConfigAutoRepair(
startupError: unknown,
startupStderrLines: string[],
alreadyAttempted: boolean,
): boolean {
if (alreadyAttempted) {
return false;
}
return hasInvalidConfigFailureSignal(startupError, startupStderrLines);
}
export function isTransientGatewayStartError(error: unknown): boolean {
const errorText = error instanceof Error
? `${error.name}: ${error.message}`
: String(error ?? '');
return TRANSIENT_START_ERROR_PATTERNS.some((pattern) => pattern.test(errorText));
}
export type GatewayStartupRecoveryAction = 'repair' | 'retry' | 'fail';
export function getGatewayStartupRecoveryAction(options: {
startupError: unknown;
startupStderrLines: string[];
configRepairAttempted: boolean;
attempt: number;
maxAttempts: number;
}): GatewayStartupRecoveryAction {
if (shouldAttemptConfigAutoRepair(
options.startupError,
options.startupStderrLines,
options.configRepairAttempted,
)) {
return 'repair';
}
if (options.attempt < options.maxAttempts && isTransientGatewayStartError(options.startupError)) {
return 'retry';
}
return 'fail';
}