working companion prototype

This commit is contained in:
2025-11-24 23:32:36 +01:00
parent e7978024d7
commit e2bea9a126
175 changed files with 23227 additions and 3519 deletions

View File

@@ -0,0 +1,45 @@
import { AuthenticationState } from '../../domain/value-objects/AuthenticationState';
import { Result } from '../../shared/result/Result';
/**
* Port for authentication services implementing zero-knowledge login.
*
* GridPilot never sees, stores, or transmits user credentials.
* Authentication is handled by opening a visible browser window where
* the user logs in directly with iRacing. GridPilot only observes
* URL changes to detect successful authentication.
*/
export interface IAuthenticationService {
/**
* Check if user has a valid session without prompting login.
* Navigates to a protected iRacing page and checks for login redirects.
*
* @returns Result containing the current authentication state
*/
checkSession(): Promise<Result<AuthenticationState>>;
/**
* Open browser for user to login manually.
* The browser window is visible so user can verify they're on the real iRacing site.
* GridPilot waits for URL change indicating successful login.
*
* @returns Result indicating success (login complete) or failure (cancelled/timeout)
*/
initiateLogin(): Promise<Result<void>>;
/**
* Clear the persistent session (logout).
* Removes stored browser context and cookies.
*
* @returns Result indicating success or failure
*/
clearSession(): Promise<Result<void>>;
/**
* Get current authentication state.
* Returns cached state without making network requests.
*
* @returns The current AuthenticationState
*/
getState(): AuthenticationState;
}

View File

@@ -1,6 +1,4 @@
import { StepId } from '../../domain/value-objects/StepId';
import type { ImageTemplate } from '../../domain/value-objects/ImageTemplate';
import type { ElementLocation, LoginDetectionResult, ScreenRegion } from '../../domain/value-objects/ScreenRegion';
import {
NavigationResult,
FormFillResult,
@@ -11,77 +9,35 @@ import {
} from './AutomationResults';
/**
* Screen capture result containing the captured image data.
* Browser automation interface for Playwright-based automation.
*
* This interface defines the contract for browser automation using
* standard DOM manipulation via Playwright. All automation is done
* through browser DevTools protocol - no OS-level automation.
*/
export interface ScreenCaptureResult {
success: boolean;
data?: Buffer;
width?: number;
height?: number;
error?: string;
}
/**
* Window information for browser window management.
*/
export interface WindowInfo {
title: string;
bounds: ScreenRegion;
handle: number;
isActive: boolean;
}
/**
* Result of window focus operation.
*/
export interface WindowFocusResult {
success: boolean;
window?: WindowInfo;
error?: string;
}
/**
* Extended browser automation interface for OS-level screen automation.
*
* This interface extends the base IBrowserAutomation with screen recognition
* capabilities for TOS-compliant automation that doesn't use browser DevTools.
*
* Key differences from browser-based automation:
* - Uses image template matching instead of CSS selectors
* - Works with screen coordinates instead of DOM elements
* - Requires explicit window focus management
* - No direct access to page content or JavaScript execution
*/
export interface IScreenAutomation {
// ============================================
// LEGACY BROWSER AUTOMATION METHODS
// (Maintained for backward compatibility)
// ============================================
export interface IBrowserAutomation {
/**
* Navigate to a URL using keyboard shortcuts (Cmd/Ctrl+L, type URL, Enter).
* Requires browser window to be focused.
* Navigate to a URL.
*/
navigateToPage(url: string): Promise<NavigationResult>;
/**
* Fill a form field by selecting all text and typing new value.
* Requires the field to already be focused.
* Fill a form field by name or selector.
*/
fillFormField(fieldName: string, value: string): Promise<FormFillResult>;
/**
* Click at a screen position (accepts coordinates or template ID).
* Click an element by selector or action name.
*/
clickElement(target: string): Promise<ClickResult>;
/**
* Wait for a condition (time-based in screen automation mode).
* Wait for an element to appear.
*/
waitForElement(target: string, maxWaitMs?: number): Promise<WaitResult>;
/**
* Handle modal dialogs using keyboard (Enter/Escape).
* Handle modal dialogs.
*/
handleModal(stepId: StepId, action: string): Promise<ModalResult>;
@@ -91,90 +47,24 @@ export interface IScreenAutomation {
executeStep?(stepId: StepId, config: Record<string, unknown>): Promise<AutomationResult>;
/**
* Initialize the automation connection.
* Initialize the browser connection.
* Returns an AutomationResult indicating success or failure.
*/
connect?(): Promise<AutomationResult>;
/**
* Clean up resources.
* Clean up browser resources.
*/
disconnect?(): Promise<void>;
/**
* Check if automation is ready.
* Check if browser is connected and ready.
*/
isConnected?(): boolean;
// ============================================
// SCREEN AUTOMATION METHODS
// (New methods for OS-level automation)
// ============================================
/**
* Detect login state by searching for known UI indicators on screen.
* Uses template matching to find login-related elements.
*
* @returns LoginDetectionResult with confidence and detected indicators
*/
detectLoginState?(): Promise<LoginDetectionResult>;
/**
* Find a UI element on screen using image template matching.
*
* @param template - The image template to search for
* @returns ElementLocation if found, null if not found
*/
findElement?(template: ImageTemplate): Promise<ElementLocation | null>;
/**
* Bring the browser window to the foreground.
* Searches for windows matching a title pattern (e.g., "iRacing").
*
* @param titlePattern - Optional window title pattern to match
* @returns WindowFocusResult indicating success/failure
*/
focusBrowserWindow?(titlePattern?: string): Promise<WindowFocusResult>;
/**
* Capture a region of the screen.
*
* @param region - Optional region to capture (full screen if omitted)
* @returns ScreenCaptureResult with image data
*/
captureScreen?(region?: ScreenRegion): Promise<ScreenCaptureResult>;
/**
* Click on a found element location.
*
* @param location - The element location from findElement
* @returns ClickResult indicating success/failure
*/
clickAtLocation?(location: ElementLocation): Promise<ClickResult>;
/**
* Wait for a template to appear on screen.
*
* @param template - The image template to wait for
* @param maxWaitMs - Maximum time to wait in milliseconds
* @returns WaitResult with timing information
*/
waitForTemplate?(template: ImageTemplate, maxWaitMs?: number): Promise<WaitResult>;
}
/**
* Type alias for backward compatibility.
* IBrowserAutomation is now a subset of IScreenAutomation.
* @deprecated Use IBrowserAutomation directly. IScreenAutomation was for OS-level
* automation which has been removed in favor of browser-only automation.
*/
export type IBrowserAutomation = Pick<
IScreenAutomation,
| 'navigateToPage'
| 'fillFormField'
| 'clickElement'
| 'waitForElement'
| 'handleModal'
| 'executeStep'
| 'connect'
| 'disconnect'
| 'isConnected'
>;
export type IScreenAutomation = IBrowserAutomation;

View File

@@ -0,0 +1,22 @@
import { AuthenticationState } from '../../domain/value-objects/AuthenticationState';
import { Result } from '../../shared/result/Result';
import type { IAuthenticationService } from '../ports/IAuthenticationService';
/**
* Use case for checking if the user has a valid iRacing session.
*
* This validates the session before automation starts, allowing
* the system to prompt for re-authentication if needed.
*/
export class CheckAuthenticationUseCase {
constructor(private readonly authService: IAuthenticationService) {}
/**
* Execute the authentication check.
*
* @returns Result containing the current AuthenticationState
*/
async execute(): Promise<Result<AuthenticationState>> {
return this.authService.checkSession();
}
}

View File

@@ -0,0 +1,21 @@
import { Result } from '../../shared/result/Result';
import type { IAuthenticationService } from '../ports/IAuthenticationService';
/**
* Use case for clearing the user's session (logout).
*
* Removes stored browser context and cookies, effectively logging
* the user out. The next automation attempt will require re-authentication.
*/
export class ClearSessionUseCase {
constructor(private readonly authService: IAuthenticationService) {}
/**
* Execute the session clearing.
*
* @returns Result indicating success or failure
*/
async execute(): Promise<Result<void>> {
return this.authService.clearSession();
}
}

View File

@@ -0,0 +1,23 @@
import { Result } from '../../shared/result/Result';
import type { IAuthenticationService } from '../ports/IAuthenticationService';
/**
* Use case for initiating the manual login flow.
*
* Opens a visible browser window where the user can log into iRacing directly.
* GridPilot never sees the credentials - it only waits for the URL to change
* indicating successful login.
*/
export class InitiateLoginUseCase {
constructor(private readonly authService: IAuthenticationService) {}
/**
* Execute the login flow.
* Opens browser and waits for user to complete manual login.
*
* @returns Result indicating success (login complete) or failure (cancelled/timeout)
*/
async execute(): Promise<Result<void>> {
return this.authService.initiateLogin();
}
}