Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 8 additions & 49 deletions cmd/browsers.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ type BrowsersService interface {
List(ctx context.Context, query kernel.BrowserListParams, opts ...option.RequestOption) (res *pagination.OffsetPagination[kernel.BrowserListResponse], err error)
New(ctx context.Context, body kernel.BrowserNewParams, opts ...option.RequestOption) (res *kernel.BrowserNewResponse, err error)
Update(ctx context.Context, id string, body kernel.BrowserUpdateParams, opts ...option.RequestOption) (res *kernel.BrowserUpdateResponse, err error)
Delete(ctx context.Context, body kernel.BrowserDeleteParams, opts ...option.RequestOption) (err error)
DeleteByID(ctx context.Context, id string, opts ...option.RequestOption) (err error)
HTTPClient(id string, opts ...option.RequestOption) (*http.Client, error)
LoadExtensions(ctx context.Context, id string, body kernel.BrowserLoadExtensionsParams, opts ...option.RequestOption) (err error)
Expand Down Expand Up @@ -175,7 +174,6 @@ func parseViewport(viewport string) (width, height, refreshRate int64, err error

// Inputs for each command
type BrowsersCreateInput struct {
PersistenceID string
TimeoutSeconds int
Stealth BoolFlag
Headless BoolFlag
Expand Down Expand Up @@ -291,19 +289,14 @@ func (b BrowsersCmd) List(ctx context.Context, in BrowsersListInput) error {
}

// Prepare table data
headers := []string{"Browser ID", "Created At", "Persistent ID", "Profile", "Pool", "CDP WS URL", "Live View URL"}
headers := []string{"Browser ID", "Created At", "Profile", "Pool", "CDP WS URL", "Live View URL"}
showDeletedAt := in.IncludeDeleted || in.Status == "deleted" || in.Status == "all"
if showDeletedAt {
headers = append(headers, "Deleted At")
}
tableData := pterm.TableData{headers}

for _, browser := range browsers {
persistentID := "-"
if browser.Persistence.ID != "" {
persistentID = browser.Persistence.ID
}

profile := "-"
if browser.Profile.Name != "" {
profile = browser.Profile.Name
Expand All @@ -321,7 +314,6 @@ func (b BrowsersCmd) List(ctx context.Context, in BrowsersListInput) error {
row := []string{
browser.SessionID,
util.FormatLocal(browser.CreatedAt),
persistentID,
profile,
pool,
truncateURL(browser.CdpWsURL, 50),
Expand Down Expand Up @@ -355,9 +347,6 @@ func (b BrowsersCmd) Create(ctx context.Context, in BrowsersCreateInput) error {
pterm.Info.Println("Creating browser session...")
}
params := kernel.BrowserNewParams{}
if in.PersistenceID != "" {
params.Persistence = kernel.BrowserPersistenceParam{ID: in.PersistenceID}
}
if in.TimeoutSeconds > 0 {
params.TimeoutSeconds = kernel.Opt(int64(in.TimeoutSeconds))
}
Expand Down Expand Up @@ -442,17 +431,17 @@ func (b BrowsersCmd) Create(ctx context.Context, in BrowsersCreateInput) error {
return util.PrintPrettyJSON(browser)
}

printBrowserSessionResult(browser.SessionID, browser.CdpWsURL, browser.BrowserLiveViewURL, browser.Persistence, browser.Profile, browser.StartURL)
printBrowserSessionResult(browser.SessionID, browser.CdpWsURL, browser.BrowserLiveViewURL, browser.Profile, browser.StartURL)
return nil
}

func printBrowserSessionResult(sessionID, cdpURL, liveViewURL string, persistence kernel.BrowserPersistence, profile kernel.Profile, startURL string) {
tableData := buildBrowserTableData(sessionID, cdpURL, liveViewURL, persistence, profile, startURL)
func printBrowserSessionResult(sessionID, cdpURL, liveViewURL string, profile kernel.Profile, startURL string) {
tableData := buildBrowserTableData(sessionID, cdpURL, liveViewURL, profile, startURL)
PrintTableNoPad(tableData, true)
}

// buildBrowserTableData creates a base table with common browser session fields.
func buildBrowserTableData(sessionID, cdpURL, liveViewURL string, persistence kernel.BrowserPersistence, profile kernel.Profile, startURL string) pterm.TableData {
func buildBrowserTableData(sessionID, cdpURL, liveViewURL string, profile kernel.Profile, startURL string) pterm.TableData {
tableData := pterm.TableData{
{"Property", "Value"},
{"Session ID", sessionID},
Expand All @@ -461,9 +450,6 @@ func buildBrowserTableData(sessionID, cdpURL, liveViewURL string, persistence ke
if liveViewURL != "" {
tableData = append(tableData, []string{"Live View URL", liveViewURL})
}
if persistence.ID != "" {
tableData = append(tableData, []string{"Persistent ID", persistence.ID})
}
if profile.ID != "" || profile.Name != "" {
profVal := profile.Name
if profVal == "" {
Expand All @@ -478,29 +464,10 @@ func buildBrowserTableData(sessionID, cdpURL, liveViewURL string, persistence ke
}

func (b BrowsersCmd) Delete(ctx context.Context, in BrowsersDeleteInput) error {
// Try both deletion modes without confirmation
// Treat not found as a success (idempotent delete)
var nonNotFoundErrors []error

// Attempt by session ID
if err := b.browsers.DeleteByID(ctx, in.Identifier); err != nil {
if !util.IsNotFound(err) {
nonNotFoundErrors = append(nonNotFoundErrors, err)
}
}

// Attempt by persistent ID (backward compatibility)
if err := b.browsers.Delete(ctx, kernel.BrowserDeleteParams{PersistentID: in.Identifier}); err != nil {
if !util.IsNotFound(err) {
nonNotFoundErrors = append(nonNotFoundErrors, err)
}
}

if len(nonNotFoundErrors) >= 2 {
// Both failed with meaningful errors; report one
return util.CleanedUpSdkError{Err: nonNotFoundErrors[0]}
if err := b.browsers.DeleteByID(ctx, in.Identifier); err != nil && !util.IsNotFound(err) {
return util.CleanedUpSdkError{Err: err}
}

pterm.Success.Printf("Successfully deleted (or already absent) browser: %s\n", in.Identifier)
return nil
}
Expand Down Expand Up @@ -562,7 +529,6 @@ func (b BrowsersCmd) Get(ctx context.Context, in BrowsersGetInput) error {
browser.SessionID,
browser.CdpWsURL,
browser.BrowserLiveViewURL,
browser.Persistence,
browser.Profile,
browser.StartURL,
)
Expand Down Expand Up @@ -2524,8 +2490,6 @@ func init() {

// Add flags for create command
browsersCreateCmd.Flags().StringP("output", "o", "", "Output format: json for raw API response")
browsersCreateCmd.Flags().StringP("persistent-id", "p", "", "[DEPRECATED] Use --timeout and profiles instead. Unique identifier for browser session persistence")
_ = browsersCreateCmd.Flags().MarkDeprecated("persistent-id", "use --timeout (up to 72 hours) and profiles instead")
browsersCreateCmd.Flags().BoolP("stealth", "s", false, "Launch browser in stealth mode to avoid detection")
browsersCreateCmd.Flags().BoolP("headless", "H", false, "Launch browser without GUI access")
browsersCreateCmd.Flags().Bool("gpu", false, "Launch browser with hardware-accelerated GPU rendering")
Expand Down Expand Up @@ -2595,10 +2559,6 @@ func runBrowsersCreate(cmd *cobra.Command, args []string) error {
client := getKernelClient(cmd)

// Get flag values
persistenceID, _ := cmd.Flags().GetString("persistent-id")
if persistenceID != "" {
pterm.Warning.Println("--persistent-id is deprecated. Use --timeout (up to 72 hours) and profiles instead.")
}
stealthVal, _ := cmd.Flags().GetBool("stealth")
headlessVal, _ := cmd.Flags().GetBool("headless")
gpuVal, _ := cmd.Flags().GetBool("gpu")
Expand Down Expand Up @@ -2689,7 +2649,7 @@ func runBrowsersCreate(cmd *cobra.Command, args []string) error {
if output == "json" {
return util.PrintPrettyJSON(resp)
}
printBrowserSessionResult(resp.SessionID, resp.CdpWsURL, resp.BrowserLiveViewURL, resp.Persistence, resp.Profile, resp.StartURL)
printBrowserSessionResult(resp.SessionID, resp.CdpWsURL, resp.BrowserLiveViewURL, resp.Profile, resp.StartURL)
return nil
}

Expand All @@ -2711,7 +2671,6 @@ func runBrowsersCreate(cmd *cobra.Command, args []string) error {
}

in := BrowsersCreateInput{
PersistenceID: persistenceID,
TimeoutSeconds: timeout,
Stealth: BoolFlag{Set: cmd.Flags().Changed("stealth"), Value: stealthVal},
Headless: BoolFlag{Set: cmd.Flags().Changed("headless"), Value: headlessVal},
Expand Down
28 changes: 2 additions & 26 deletions cmd/browsers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ type FakeBrowsersService struct {
ListFunc func(ctx context.Context, query kernel.BrowserListParams, opts ...option.RequestOption) (*pagination.OffsetPagination[kernel.BrowserListResponse], error)
NewFunc func(ctx context.Context, body kernel.BrowserNewParams, opts ...option.RequestOption) (*kernel.BrowserNewResponse, error)
UpdateFunc func(ctx context.Context, id string, body kernel.BrowserUpdateParams, opts ...option.RequestOption) (*kernel.BrowserUpdateResponse, error)
DeleteFunc func(ctx context.Context, body kernel.BrowserDeleteParams, opts ...option.RequestOption) error
DeleteByIDFunc func(ctx context.Context, id string, opts ...option.RequestOption) error
HTTPClientFunc func(id string, opts ...option.RequestOption) (*http.Client, error)
LoadExtensionsFunc func(ctx context.Context, id string, body kernel.BrowserLoadExtensionsParams, opts ...option.RequestOption) error
Expand Down Expand Up @@ -94,13 +93,6 @@ func (f *FakeBrowsersService) Update(ctx context.Context, id string, body kernel
return &kernel.BrowserUpdateResponse{}, nil
}

func (f *FakeBrowsersService) Delete(ctx context.Context, body kernel.BrowserDeleteParams, opts ...option.RequestOption) error {
if f.DeleteFunc != nil {
return f.DeleteFunc(ctx, body, opts...)
}
return nil
}

func (f *FakeBrowsersService) DeleteByID(ctx context.Context, id string, opts ...option.RequestOption) error {
if f.DeleteByIDFunc != nil {
return f.DeleteByIDFunc(ctx, id, opts...)
Expand Down Expand Up @@ -400,14 +392,12 @@ func TestBrowsersList_PrintsTableWithRows(t *testing.T) {
CdpWsURL: "ws://cdp-1",
BrowserLiveViewURL: "http://view-1",
CreatedAt: created,
Persistence: kernel.BrowserPersistence{ID: "pid-1"},
},
{
SessionID: "sess-2",
CdpWsURL: "ws://cdp-2",
BrowserLiveViewURL: "",
CreatedAt: created,
Persistence: kernel.BrowserPersistence{ID: ""},
},
}

Expand All @@ -422,7 +412,6 @@ func TestBrowsersList_PrintsTableWithRows(t *testing.T) {
out := outBuf.String()
assert.Contains(t, out, "sess-1")
assert.Contains(t, out, "sess-2")
assert.Contains(t, out, "pid-1")
}

func TestBrowsersList_PrintsErrorOnFailure(t *testing.T) {
Expand Down Expand Up @@ -469,15 +458,13 @@ func TestBrowsersCreate_PrintsResponse(t *testing.T) {
SessionID: "sess-new",
CdpWsURL: "ws://cdp-new",
BrowserLiveViewURL: "http://view-new",
Persistence: kernel.BrowserPersistence{ID: "pid-new"},
}
return resp, nil
},
}

b := BrowsersCmd{browsers: fake}
in := BrowsersCreateInput{
PersistenceID: "pid-new",
TimeoutSeconds: 120,
Stealth: BoolFlag{Set: true, Value: true},
Headless: BoolFlag{Set: true, Value: false},
Expand All @@ -491,8 +478,6 @@ func TestBrowsersCreate_PrintsResponse(t *testing.T) {
assert.Contains(t, out, "ws://cdp-new")
assert.Contains(t, out, "Live View URL")
assert.Contains(t, out, "http://view-new")
assert.Contains(t, out, "Persistent ID")
assert.Contains(t, out, "pid-new")
}

func TestBrowsersCreate_WithInvocationID(t *testing.T) {
Expand Down Expand Up @@ -533,9 +518,6 @@ func TestBrowsersDelete_Success(t *testing.T) {
setupStdoutCapture(t)

fake := &FakeBrowsersService{
DeleteFunc: func(ctx context.Context, body kernel.BrowserDeleteParams, opts ...option.RequestOption) error {
return nil
},
DeleteByIDFunc: func(ctx context.Context, id string, opts ...option.RequestOption) error {
return nil
},
Expand All @@ -551,19 +533,15 @@ func TestBrowsersDelete_Failure(t *testing.T) {
setupStdoutCapture(t)

fake := &FakeBrowsersService{
DeleteFunc: func(ctx context.Context, body kernel.BrowserDeleteParams, opts ...option.RequestOption) error {
return errors.New("left failed")
},
DeleteByIDFunc: func(ctx context.Context, id string, opts ...option.RequestOption) error {
return errors.New("right failed")
return errors.New("delete failed")
},
}
b := BrowsersCmd{browsers: fake}
err := b.Delete(context.Background(), BrowsersDeleteInput{Identifier: "any"})

assert.Error(t, err)
errMsg := err.Error()
assert.True(t, strings.Contains(errMsg, "right failed") || strings.Contains(errMsg, "left failed"), "expected error message to contain either 'right failed' or 'left failed', got: %s", errMsg)
assert.Contains(t, err.Error(), "delete failed")
}

func TestBrowsersView_ByID_PrintsURL(t *testing.T) {
Expand Down Expand Up @@ -656,7 +634,6 @@ func TestBrowsersGet_PrintsDetails(t *testing.T) {
Stealth: true,
KioskMode: false,
Viewport: shared.BrowserViewport{Width: 1920, Height: 1080, RefreshRate: 25},
Persistence: kernel.BrowserPersistence{ID: "persist-id"},
Profile: kernel.Profile{ID: "prof-id", Name: "my-profile"},
ProxyID: "proxy-123",
}, nil
Expand All @@ -673,7 +650,6 @@ func TestBrowsersGet_PrintsDetails(t *testing.T) {
assert.Contains(t, out, "false") // Headless
assert.Contains(t, out, "true") // Stealth
assert.Contains(t, out, "1920x1080@25")
assert.Contains(t, out, "persist-id")
assert.Contains(t, out, "my-profile")
assert.Contains(t, out, "proxy-123")
}
Expand Down
14 changes: 0 additions & 14 deletions cmd/projects.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ type ProjectsLimitsGetInput struct {
type ProjectsLimitsSetInput struct {
Identifier string
MaxConcurrentSessions Int64Flag
MaxPersistentSessions Int64Flag
MaxConcurrentInvocations Int64Flag
MaxPooledSessions Int64Flag
Output string
Expand Down Expand Up @@ -193,12 +192,6 @@ func (c ProjectsCmd) LimitsSet(ctx context.Context, in ProjectsLimitsSetInput) e
}
inner.MaxConcurrentSessions = param.NewOpt(in.MaxConcurrentSessions.Value)
}
if in.MaxPersistentSessions.Set {
if in.MaxPersistentSessions.Value < 0 {
return fmt.Errorf("--max-persistent-sessions must be non-negative (got %d); use 0 to remove the cap", in.MaxPersistentSessions.Value)
}
inner.MaxPersistentSessions = param.NewOpt(in.MaxPersistentSessions.Value)
}
if in.MaxConcurrentInvocations.Set {
if in.MaxConcurrentInvocations.Value < 0 {
return fmt.Errorf("--max-concurrent-invocations must be non-negative (got %d); use 0 to remove the cap", in.MaxConcurrentInvocations.Value)
Expand Down Expand Up @@ -243,7 +236,6 @@ func renderProjectLimits(limits *kernel.ProjectLimits) {
rows := pterm.TableData{
{"Limit", "Value"},
{"Max Concurrent Sessions", formatProjectLimitValue(limits.MaxConcurrentSessions, limits.JSON.MaxConcurrentSessions)},
{"Max Persistent Sessions", formatProjectLimitValue(limits.MaxPersistentSessions, limits.JSON.MaxPersistentSessions)},
{"Max Concurrent Invocations", formatProjectLimitValue(limits.MaxConcurrentInvocations, limits.JSON.MaxConcurrentInvocations)},
{"Max Pooled Sessions", formatProjectLimitValue(limits.MaxPooledSessions, limits.JSON.MaxPooledSessions)},
}
Expand Down Expand Up @@ -297,7 +289,6 @@ func runProjectsLimitsGet(cmd *cobra.Command, args []string) error {
func runProjectsLimitsSet(cmd *cobra.Command, args []string) error {
c := getProjectsHandler(cmd)
maxConcurrentSessions, _ := cmd.Flags().GetInt64("max-concurrent-sessions")
maxPersistentSessions, _ := cmd.Flags().GetInt64("max-persistent-sessions")
maxConcurrentInvocations, _ := cmd.Flags().GetInt64("max-concurrent-invocations")
maxPooledSessions, _ := cmd.Flags().GetInt64("max-pooled-sessions")
output, _ := cmd.Flags().GetString("output")
Expand All @@ -308,10 +299,6 @@ func runProjectsLimitsSet(cmd *cobra.Command, args []string) error {
Set: cmd.Flags().Changed("max-concurrent-sessions"),
Value: maxConcurrentSessions,
},
MaxPersistentSessions: Int64Flag{
Set: cmd.Flags().Changed("max-persistent-sessions"),
Value: maxPersistentSessions,
},
MaxConcurrentInvocations: Int64Flag{
Set: cmd.Flags().Changed("max-concurrent-invocations"),
Value: maxConcurrentInvocations,
Expand All @@ -330,7 +317,6 @@ func addProjectsLimitsOutputFlag(cmd *cobra.Command) {

func addProjectsLimitsSetFlags(cmd *cobra.Command) {
cmd.Flags().Int64("max-concurrent-sessions", 0, "Maximum concurrent browser sessions (0 to remove cap)")
cmd.Flags().Int64("max-persistent-sessions", 0, "Maximum persistent browser sessions (0 to remove cap)")
cmd.Flags().Int64("max-concurrent-invocations", 0, "Maximum concurrent app invocations (0 to remove cap)")
cmd.Flags().Int64("max-pooled-sessions", 0, "Maximum pooled sessions capacity (0 to remove cap)")
addProjectsLimitsOutputFlag(cmd)
Expand Down
1 change: 0 additions & 1 deletion cmd/projects_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@ func TestProjectsLimitsGet_DefaultOutput(t *testing.T) {
}
limits.JSON.MaxConcurrentSessions = respjson.NewField("10")
limits.JSON.MaxConcurrentInvocations = respjson.NewField("5")
limits.JSON.MaxPersistentSessions = respjson.NewField(respjson.Null)

fakeProjects := &FakeProjectsService{}
fakeLimits := &FakeProjectLimitsService{
Expand Down
Loading