diff --git a/cmd/browsers.go b/cmd/browsers.go index 6cc7b8d..badd879 100644 --- a/cmd/browsers.go +++ b/cmd/browsers.go @@ -207,6 +207,7 @@ type BrowsersUpdateInput struct { ProfileName string ProfileSaveChanges BoolFlag Viewport string + Force bool Output string } @@ -589,6 +590,11 @@ func (b BrowsersCmd) Update(ctx context.Context, in BrowsersUpdateInput) error { return fmt.Errorf("--save-changes requires --profile-id or --profile-name") } + // Validate --force is only used with a viewport change + if in.Force && !hasViewportChange { + return fmt.Errorf("--force requires --viewport") + } + // Validate that at least one update option is provided if !hasProxyChange && !hasProfileChange && !hasViewportChange { return fmt.Errorf("must specify at least one of: --proxy-id, --clear-proxy, --profile-id, --profile-name, or --viewport") @@ -622,13 +628,18 @@ func (b BrowsersCmd) Update(ctx context.Context, in BrowsersUpdateInput) error { if err != nil { return fmt.Errorf("invalid viewport format: %v", err) } - params.Viewport = shared.BrowserViewportParam{ - Width: width, - Height: height, + params.Viewport = kernel.BrowserUpdateParamsViewport{ + BrowserViewportParam: shared.BrowserViewportParam{ + Width: width, + Height: height, + }, } if refreshRate > 0 { params.Viewport.RefreshRate = kernel.Opt(refreshRate) } + if in.Force { + params.Viewport.Force = kernel.Opt(true) + } } if in.Output != "json" { @@ -2122,6 +2133,7 @@ Supported operations: - Change or remove proxy (--proxy-id or --clear-proxy) - Load a profile into a session that doesn't have one (--profile-id or --profile-name) - Change viewport dimensions (--viewport) + - Force viewport resize during active live view or recording (--force with --viewport) Note: Profiles can only be loaded into sessions that don't already have a profile.`, Args: func(cmd *cobra.Command, args []string) error { @@ -2159,6 +2171,7 @@ func init() { browsersUpdateCmd.Flags().String("profile-name", "", "Profile name to load into the browser session (mutually exclusive with --profile-id)") browsersUpdateCmd.Flags().Bool("save-changes", false, "If set, save changes back to the profile when the session ends") browsersUpdateCmd.Flags().String("viewport", "", "Browser viewport size (e.g., 1920x1080@25). Supported: 2560x1440@10, 1920x1080@25, 1920x1200@25, 1440x900@25, 1024x768@60, 1200x800@60, 1280x800@60") + browsersUpdateCmd.Flags().Bool("force", false, "Force viewport resize even when a live view or recording/replay is active") browsersCmd.AddCommand(browsersListCmd) browsersCmd.AddCommand(browsersCreateCmd) @@ -2619,6 +2632,7 @@ func runBrowsersUpdate(cmd *cobra.Command, args []string) error { profileName, _ := cmd.Flags().GetString("profile-name") saveChanges, _ := cmd.Flags().GetBool("save-changes") viewport, _ := cmd.Flags().GetString("viewport") + force, _ := cmd.Flags().GetBool("force") svc := client.Browsers b := BrowsersCmd{browsers: &svc} @@ -2630,6 +2644,7 @@ func runBrowsersUpdate(cmd *cobra.Command, args []string) error { ProfileName: profileName, ProfileSaveChanges: BoolFlag{Set: cmd.Flags().Changed("save-changes"), Value: saveChanges}, Viewport: viewport, + Force: force, Output: out, }) } diff --git a/cmd/browsers_test.go b/cmd/browsers_test.go index 1099caf..0f88054 100644 --- a/cmd/browsers_test.go +++ b/cmd/browsers_test.go @@ -1242,3 +1242,77 @@ func TestBrowsersCreate_WithInvalidViewport(t *testing.T) { out := outBuf.String() assert.Contains(t, out, "Invalid viewport format") } + +func TestBrowsersUpdate_WithViewportAndForce(t *testing.T) { + setupStdoutCapture(t) + var captured kernel.BrowserUpdateParams + fake := &FakeBrowsersService{UpdateFunc: func(ctx context.Context, id string, body kernel.BrowserUpdateParams, opts ...option.RequestOption) (*kernel.BrowserUpdateResponse, error) { + captured = body + return &kernel.BrowserUpdateResponse{SessionID: "session123"}, nil + }} + b := BrowsersCmd{browsers: fake} + + err := b.Update(context.Background(), BrowsersUpdateInput{ + Identifier: "session123", + Viewport: "1920x1080@25", + Force: true, + }) + + assert.NoError(t, err) + assert.Equal(t, int64(1920), captured.Viewport.Width) + assert.Equal(t, int64(1080), captured.Viewport.Height) + assert.True(t, captured.Viewport.RefreshRate.Valid()) + assert.Equal(t, int64(25), captured.Viewport.RefreshRate.Value) + assert.True(t, captured.Viewport.Force.Valid()) + assert.True(t, captured.Viewport.Force.Value) +} + +func TestBrowsersUpdate_WithViewportNoForce(t *testing.T) { + setupStdoutCapture(t) + var captured kernel.BrowserUpdateParams + fake := &FakeBrowsersService{UpdateFunc: func(ctx context.Context, id string, body kernel.BrowserUpdateParams, opts ...option.RequestOption) (*kernel.BrowserUpdateResponse, error) { + captured = body + return &kernel.BrowserUpdateResponse{SessionID: "session123"}, nil + }} + b := BrowsersCmd{browsers: fake} + + err := b.Update(context.Background(), BrowsersUpdateInput{ + Identifier: "session123", + Viewport: "1920x1080@25", + Force: false, + }) + + assert.NoError(t, err) + assert.Equal(t, int64(1920), captured.Viewport.Width) + assert.Equal(t, int64(1080), captured.Viewport.Height) + assert.False(t, captured.Viewport.Force.Valid()) +} + +func TestBrowsersUpdate_ForceWithoutViewport_Errors(t *testing.T) { + setupStdoutCapture(t) + fake := &FakeBrowsersService{} + b := BrowsersCmd{browsers: fake} + + err := b.Update(context.Background(), BrowsersUpdateInput{ + Identifier: "session123", + Force: true, + }) + + assert.Error(t, err) + assert.Contains(t, err.Error(), "--force requires --viewport") +} + +func TestBrowsersUpdate_ForceWithProxyButNoViewport_Errors(t *testing.T) { + setupStdoutCapture(t) + fake := &FakeBrowsersService{} + b := BrowsersCmd{browsers: fake} + + err := b.Update(context.Background(), BrowsersUpdateInput{ + Identifier: "session123", + ProxyID: "proxy-123", + Force: true, + }) + + assert.Error(t, err) + assert.Contains(t, err.Error(), "--force requires --viewport") +} diff --git a/go.mod b/go.mod index 241a31c..b21379c 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/charmbracelet/lipgloss/v2 v2.0.0-beta.1 github.com/golang-jwt/jwt/v5 v5.2.2 github.com/joho/godotenv v1.5.1 - github.com/kernel/kernel-go-sdk v0.40.0 + github.com/kernel/kernel-go-sdk v0.42.1 github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c github.com/pterm/pterm v0.12.80 github.com/samber/lo v1.51.0 diff --git a/go.sum b/go.sum index e2304ce..a8af996 100644 --- a/go.sum +++ b/go.sum @@ -64,8 +64,8 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= -github.com/kernel/kernel-go-sdk v0.40.0 h1:RQON4dE9HwvEcF5wM3WVKs/Om0PCH0eTDEB3iwjOvy4= -github.com/kernel/kernel-go-sdk v0.40.0/go.mod h1:EeZzSuHZVeHKxKCPUzxou2bovNGhXaz0RXrSqKNf1AQ= +github.com/kernel/kernel-go-sdk v0.42.1 h1:uDoMNXyfS59fkZ9laJO/qKFAysRrqdg06zNB3uLEdEk= +github.com/kernel/kernel-go-sdk v0.42.1/go.mod h1:EeZzSuHZVeHKxKCPUzxou2bovNGhXaz0RXrSqKNf1AQ= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.10/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=