File size: 5,706 Bytes
6a7089a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
package bridge

import (
	"context"
	"net/http"
	"time"

	"github.com/chromedp/cdproto/target"
	"github.com/pinchtab/pinchtab/internal/config"
)

// BridgeAPI abstracts browser tab operations for handler testing.
type BridgeAPI interface {
	BrowserContext() context.Context
	TabContext(tabID string) (ctx context.Context, resolvedID string, err error)
	ListTargets() ([]*target.Info, error)
	// CreateTab creates a new browser tab and returns a hash-based tab ID (e.g., "tab_XXXXXXXX").
	// The raw CDP target ID is stored internally and used for CDP operations.
	CreateTab(url string) (hashTabID string, ctx context.Context, cancel context.CancelFunc, err error)
	CloseTab(tabID string) error
	FocusTab(tabID string) error

	GetRefCache(tabID string) *RefCache
	SetRefCache(tabID string, cache *RefCache)
	DeleteRefCache(tabID string)

	ExecuteAction(ctx context.Context, kind string, req ActionRequest) (map[string]any, error)
	AvailableActions() []string

	// Execute runs a task for a tab with per-tab sequential execution
	// and cross-tab bounded parallelism. If not supported, runs directly.
	Execute(ctx context.Context, tabID string, task func(ctx context.Context) error) error

	TabLockInfo(tabID string) *LockInfo
	Lock(tabID, owner string, ttl time.Duration) error
	Unlock(tabID, owner string) error

	EnsureChrome(cfg *config.RuntimeConfig) error

	// Memory metrics
	GetMemoryMetrics(tabID string) (*MemoryMetrics, error)
	GetBrowserMemoryMetrics() (*MemoryMetrics, error)
	GetAggregatedMemoryMetrics() (*MemoryMetrics, error)

	// Crash monitoring
	GetCrashLogs() []string
}

type LockInfo struct {
	Owner     string
	ExpiresAt time.Time
}

// ProfileService abstracts profile management operations.
type ProfileService interface {
	RegisterHandlers(mux *http.ServeMux)
	List() ([]ProfileInfo, error)
	Create(name string) error
	Import(name, sourcePath string) error
	Reset(name string) error
	Delete(name string) error
	Logs(name string, limit int) []ActionRecord
	Analytics(name string) AnalyticsReport
	RecordAction(profile string, record ActionRecord)
}

// OrchestratorService abstracts instance orchestration operations.
type OrchestratorService interface {
	RegisterHandlers(mux *http.ServeMux)
	Launch(name, port string, headless bool, extensionPaths []string) (*Instance, error)
	Stop(id string) error
	StopProfile(name string) error
	List() []Instance
	Logs(id string) (string, error)
	FirstRunningURL() string
	AllTabs() []InstanceTab
	ScreencastURL(instanceID, tabID string) string
	Shutdown()
	ForceShutdown()
}

// Common types used across packages (migrated from main)

type ProfileInfo struct {
	ID                string    `json:"id,omitempty"`
	Name              string    `json:"name"`
	Path              string    `json:"path,omitempty"`       // File system path to profile directory
	PathExists        bool      `json:"pathExists,omitempty"` // Whether the path exists on disk
	Created           time.Time `json:"created"`
	LastUsed          time.Time `json:"lastUsed"`
	DiskUsage         int64     `json:"diskUsage"`
	Running           bool      `json:"running"`
	Temporary         bool      `json:"temporary,omitempty"` // ephemeral instance profiles (auto-generated)
	Source            string    `json:"source,omitempty"`
	ChromeProfileName string    `json:"chromeProfileName,omitempty"`
	AccountEmail      string    `json:"accountEmail,omitempty"`
	AccountName       string    `json:"accountName,omitempty"`
	HasAccount        bool      `json:"hasAccount,omitempty"`
	UseWhen           string    `json:"useWhen,omitempty"`
	Description       string    `json:"description,omitempty"`
}

type ActionRecord struct {
	Timestamp  time.Time `json:"timestamp"`
	Method     string    `json:"method"`
	Endpoint   string    `json:"endpoint"`
	URL        string    `json:"url"`
	TabID      string    `json:"tabId"`
	DurationMs int64     `json:"durationMs"`
	Status     int       `json:"status"`
}

type AnalyticsReport struct {
	TotalActions   int            `json:"totalActions"`
	Last24h        int            `json:"last24h"`
	CommonHosts    map[string]int `json:"commonHosts"`
	TopEndpoints   map[string]int `json:"topEndpoints,omitempty"`
	RepeatPatterns []string       `json:"repeatPatterns,omitempty"`
	Suggestions    []string       `json:"suggestions,omitempty"`
}

type Instance struct {
	ID          string    `json:"id"`                   // Hash-based ID: inst_XXXXXXXX
	ProfileID   string    `json:"profileId"`            // Hash-based profile ID: prof_XXXXXXXX
	ProfileName string    `json:"profileName"`          // Human-readable profile name (for display only)
	Port        string    `json:"port"`                 // Internal: instance port
	URL         string    `json:"url,omitempty"`        // Canonical base URL for bridge-backed instances
	Headless    bool      `json:"headless"`             // Mode: headless vs headed
	Status      string    `json:"status"`               // Status: starting/running/stopping/stopped/error
	StartTime   time.Time `json:"startTime"`            // When instance was created
	Error       string    `json:"error,omitempty"`      // Error message if status=error
	Attached    bool      `json:"attached"`             // True if attached rather than locally launched
	AttachType  string    `json:"attachType,omitempty"` // "cdp" or "bridge" for attached instances
	CdpURL      string    `json:"cdpUrl,omitempty"`     // CDP WebSocket URL (for CDP-attached instances)
}

type InstanceTab struct {
	ID         string `json:"id"`         // Hash-based tab ID: tab_XXXXXXXX
	InstanceID string `json:"instanceId"` // Hash-based instance ID: inst_XXXXXXXX
	URL        string `json:"url"`
	Title      string `json:"title"`
}

// test