package domain import ( "errors" "fmt" "net/url" "path/filepath" "strings" "time" ) type BrowserKind string const ( BrowserChrome BrowserKind = "chrome" BrowserEdge BrowserKind = "edge" ) func (k BrowserKind) Valid() bool { return k == BrowserChrome || k == BrowserEdge } type InstanceStatus string const ( StatusCreated InstanceStatus = "created" StatusStarting InstanceStatus = "starting" StatusRunning InstanceStatus = "running" StatusStopping InstanceStatus = "stopping" StatusExited InstanceStatus = "exited" StatusFailed InstanceStatus = "failed" StatusUnknown InstanceStatus = "unknown" ) type LaunchSpec struct { Kind BrowserKind Executable string UserDataDir string ProfileDirectory string TargetURL string ProxyServer string Headless bool ExtraArgs []string } func (s LaunchSpec) Normalize() (LaunchSpec, error) { if !s.Kind.Valid() { return LaunchSpec{}, fmt.Errorf("%w: unsupported browser kind", ErrInvalidLaunchSpec) } userDataDir := strings.TrimSpace(s.UserDataDir) if userDataDir == "" || !filepath.IsAbs(userDataDir) { return LaunchSpec{}, fmt.Errorf("%w: user data dir must be absolute", ErrInvalidLaunchSpec) } target := strings.TrimSpace(s.TargetURL) parsed, err := url.Parse(target) if err != nil || parsed.Host == "" || parsed.User != nil || (parsed.Scheme != "http" && parsed.Scheme != "https") { return LaunchSpec{}, fmt.Errorf("%w: target URL must be absolute http or https", ErrInvalidLaunchSpec) } normalized := s normalized.Executable = strings.TrimSpace(s.Executable) normalized.UserDataDir = filepath.Clean(userDataDir) normalized.ProfileDirectory = strings.TrimSpace(s.ProfileDirectory) normalized.TargetURL = parsed.String() normalized.ProxyServer = strings.TrimSpace(s.ProxyServer) normalized.ExtraArgs = append([]string(nil), s.ExtraArgs...) return normalized, nil } type InstanceView struct { ID string Kind BrowserKind Executable string PID int UserDataDir string Profile string TargetURL string Status InstanceStatus ExitCode *int StartedAt time.Time ChangedAt time.Time IdentityNote string } type EventKind string const ( EventBrowserStarted EventKind = "browser.started" EventBrowserStatusChanged EventKind = "browser.status.changed" EventBrowserExited EventKind = "browser.exited" ) type BrowserEvent struct { Kind EventKind InstanceID string Status InstanceStatus PID int ExitCode *int ErrorCode ErrorCode At time.Time } type ErrorCode string const ( ErrorInvalidLaunchSpec ErrorCode = "invalid_launch_spec" ErrorProfileOccupied ErrorCode = "profile_occupied" ErrorExecutableNotFound ErrorCode = "executable_not_found" ErrorInstanceNotFound ErrorCode = "instance_not_found" ErrorIdentityMismatch ErrorCode = "identity_mismatch" ErrorProcessPermission ErrorCode = "process_permission" ErrorProcessStart ErrorCode = "process_start_failed" ) var ( ErrInvalidLaunchSpec = errors.New("invalid browser launch specification") ErrProfileOccupied = errors.New("browser user data dir is occupied") ErrInstanceNotFound = errors.New("browser instance was not found") ErrIdentityMismatch = errors.New("browser process identity mismatch") )