working companion prototype
This commit is contained in:
45
packages/application/ports/IAuthenticationService.ts
Normal file
45
packages/application/ports/IAuthenticationService.ts
Normal 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;
|
||||
}
|
||||
@@ -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;
|
||||
22
packages/application/use-cases/CheckAuthenticationUseCase.ts
Normal file
22
packages/application/use-cases/CheckAuthenticationUseCase.ts
Normal 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();
|
||||
}
|
||||
}
|
||||
21
packages/application/use-cases/ClearSessionUseCase.ts
Normal file
21
packages/application/use-cases/ClearSessionUseCase.ts
Normal 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();
|
||||
}
|
||||
}
|
||||
23
packages/application/use-cases/InitiateLoginUseCase.ts
Normal file
23
packages/application/use-cases/InitiateLoginUseCase.ts
Normal 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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user