From 63984937416bbca0f35cfe728a50567894de0224 Mon Sep 17 00:00:00 2001 From: Sayan Samanta Date: Mon, 18 May 2026 15:33:35 -0700 Subject: [PATCH 1/4] step 1: strip persistent browser handling from browsers.go Drop the --persistent-id flag (deprecated), the Persistent ID list column, the PersistenceID input field and Persistence param plumbing on create, the persistent_id delete fallback, and the persistence arg on printBrowserSessionResult / buildBrowserTableData. Persistent browsers are EOL'd in the API. Part of EOL persistent browsers (KERNEL-1015). Co-authored-by: Cursor --- cmd/browsers.go | 45 +++++++-------------------------------------- 1 file changed, 7 insertions(+), 38 deletions(-) diff --git a/cmd/browsers.go b/cmd/browsers.go index 6d39958..5cb4e90 100644 --- a/cmd/browsers.go +++ b/cmd/browsers.go @@ -175,7 +175,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 @@ -291,7 +290,7 @@ 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") @@ -299,11 +298,6 @@ func (b BrowsersCmd) List(ctx context.Context, in BrowsersListInput) error { 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 @@ -321,7 +315,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), @@ -355,9 +348,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)) } @@ -442,17 +432,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}, @@ -461,9 +451,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 == "" { @@ -478,26 +465,16 @@ 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 + if len(nonNotFoundErrors) > 0 { return util.CleanedUpSdkError{Err: nonNotFoundErrors[0]} } @@ -562,7 +539,6 @@ func (b BrowsersCmd) Get(ctx context.Context, in BrowsersGetInput) error { browser.SessionID, browser.CdpWsURL, browser.BrowserLiveViewURL, - browser.Persistence, browser.Profile, browser.StartURL, ) @@ -2524,8 +2500,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") @@ -2595,10 +2569,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") @@ -2689,7 +2659,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 } @@ -2711,7 +2681,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}, From c82d9bc29ad6b0b6d1357679810281ca616937e9 Mon Sep 17 00:00:00 2001 From: Sayan Samanta Date: Mon, 18 May 2026 15:35:21 -0700 Subject: [PATCH 2/4] step 2: drop persistent browser test fixtures and Delete interface method Remove the deprecated BrowsersService.Delete method (DELETE /browsers ?persistent_id=) from the interface and fake, and drop all Persistence fixture data and assertions from browsers_test.go. Part of EOL persistent browsers (KERNEL-1015). Co-authored-by: Cursor --- cmd/browsers.go | 1 - cmd/browsers_test.go | 28 ++-------------------------- 2 files changed, 2 insertions(+), 27 deletions(-) diff --git a/cmd/browsers.go b/cmd/browsers.go index 5cb4e90..b38bf39 100644 --- a/cmd/browsers.go +++ b/cmd/browsers.go @@ -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) diff --git a/cmd/browsers_test.go b/cmd/browsers_test.go index da4ba05..36190f4 100644 --- a/cmd/browsers_test.go +++ b/cmd/browsers_test.go @@ -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 @@ -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...) @@ -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: ""}, }, } @@ -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) { @@ -469,7 +458,6 @@ 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 }, @@ -477,7 +465,6 @@ func TestBrowsersCreate_PrintsResponse(t *testing.T) { b := BrowsersCmd{browsers: fake} in := BrowsersCreateInput{ - PersistenceID: "pid-new", TimeoutSeconds: 120, Stealth: BoolFlag{Set: true, Value: true}, Headless: BoolFlag{Set: true, Value: false}, @@ -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) { @@ -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 }, @@ -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) { @@ -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 @@ -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") } From b87a3f9a3d76157bc4351b28a3521ff417393df4 Mon Sep 17 00:00:00 2001 From: Sayan Samanta Date: Mon, 18 May 2026 15:36:55 -0700 Subject: [PATCH 3/4] step 3: drop --max-persistent-sessions from projects command Remove the flag, the MaxPersistentSessions input field, the cap-setting plumbing, the "Max Persistent Sessions" limits row, and the matching test fixture field. The cap is moot once persistent browsers are EOL. Part of EOL persistent browsers (KERNEL-1015). Co-authored-by: Cursor --- cmd/projects.go | 14 -------------- cmd/projects_test.go | 1 - 2 files changed, 15 deletions(-) diff --git a/cmd/projects.go b/cmd/projects.go index 327cf81..a223165 100644 --- a/cmd/projects.go +++ b/cmd/projects.go @@ -57,7 +57,6 @@ type ProjectsLimitsGetInput struct { type ProjectsLimitsSetInput struct { Identifier string MaxConcurrentSessions Int64Flag - MaxPersistentSessions Int64Flag MaxConcurrentInvocations Int64Flag MaxPooledSessions Int64Flag Output string @@ -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) @@ -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)}, } @@ -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") @@ -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, @@ -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) diff --git a/cmd/projects_test.go b/cmd/projects_test.go index 7ae7347..33ae60d 100644 --- a/cmd/projects_test.go +++ b/cmd/projects_test.go @@ -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{ From c2440d4299a9f241ed413f4ca174307d88d4686d Mon Sep 17 00:00:00 2001 From: Sayan Samanta Date: Mon, 18 May 2026 15:39:17 -0700 Subject: [PATCH 4/4] step 4: simplify Delete to a single attempt After dropping the persistent-id fallback there's only one possible error, so the nonNotFoundErrors slice was just noise. Collapse to a direct check. Part of EOL persistent browsers (KERNEL-1015). Co-authored-by: Cursor --- cmd/browsers.go | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/cmd/browsers.go b/cmd/browsers.go index b38bf39..7457d51 100644 --- a/cmd/browsers.go +++ b/cmd/browsers.go @@ -465,18 +465,9 @@ func buildBrowserTableData(sessionID, cdpURL, liveViewURL string, profile kernel func (b BrowsersCmd) Delete(ctx context.Context, in BrowsersDeleteInput) error { // Treat not found as a success (idempotent delete) - var nonNotFoundErrors []error - - if err := b.browsers.DeleteByID(ctx, in.Identifier); err != nil { - if !util.IsNotFound(err) { - nonNotFoundErrors = append(nonNotFoundErrors, err) - } - } - - if len(nonNotFoundErrors) > 0 { - 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 }