Skip to content

Commit 1de7e36

Browse files
authored
Merge branch 'main' into sayan/bump-actions-node24
2 parents c8e4874 + 905b022 commit 1de7e36

5 files changed

Lines changed: 380 additions & 12 deletions

File tree

README.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,8 @@ Commands with JSON output support:
220220
- `--telemetry=all` - Enable telemetry for all categories
221221
- `--telemetry=off` - Disable telemetry
222222
- `--telemetry=<list>` - Per-category config, e.g. `--telemetry=network=on,page=off`
223+
- `--chrome-policy <json>` - Custom Chrome enterprise policy as a JSON object. Kernel-managed policies (extensions, proxy, automation) are rejected server-side.
224+
- `--chrome-policy-file <path>` - Read the Chrome enterprise policy from a file (use `-` for stdin). Mutually exclusive with `--chrome-policy`.
223225
- `--output json`, `-o json` - Output raw JSON object
224226
- _Note: When a pool is specified, omit other session configuration flags—pool settings determine profile, proxy, viewport, etc._
225227
- `kernel browsers delete <id-or-name>` - Delete a browser by ID or name
@@ -258,11 +260,12 @@ Commands with JSON output support:
258260
- `--timeout <seconds>` - Idle timeout for browsers acquired from the pool
259261
- `--stealth`, `--headless`, `--kiosk` - Default pool configuration
260262
- `--profile-id`, `--profile-name`, `--save-changes`, `--proxy-id`, `--start-url`, `--extension`, `--viewport` - Same semantics as `kernel browsers create`
263+
- `--chrome-policy <json>` / `--chrome-policy-file <path>` - Custom Chrome enterprise policy applied to every browser in the pool, as a JSON object or from a file (`-` for stdin). Same semantics as `kernel browsers create`.
261264
- `--output json`, `-o json` - Output raw JSON object
262265
- `kernel browser-pools get <id-or-name>` - Get pool details
263266
- `--output json`, `-o json` - Output raw JSON object
264267
- `kernel browser-pools update <id-or-name>` - Update pool configuration
265-
- Same flags as create plus `--clear-start-url` (remove the pool's start URL) and `--discard-all-idle` (discard all idle browsers and refill)
268+
- Same flags as create plus `--clear-start-url` (remove the pool's start URL) and `--discard-all-idle` (discard all idle browsers and refill). An empty `--chrome-policy '{}'` is ignored and does not clear an existing policy; recreate the pool to remove one.
266269
- `--output json`, `-o json` - Output raw JSON object
267270
- `kernel browser-pools delete <id-or-name>` - Delete a pool
268271
- `--force` - Force delete even if browsers are leased
@@ -657,6 +660,10 @@ kernel browsers create --kiosk
657660
# Create a browser with a profile for session state
658661
kernel browsers create --profile-name my-profile
659662

663+
# Create a browser with a custom Chrome enterprise policy
664+
kernel browsers create --chrome-policy '{"BookmarkBarEnabled": false}'
665+
kernel browsers create --chrome-policy-file policy.json
666+
660667
# Delete a browser
661668
kernel browsers delete browser123
662669

cmd/browser_pools.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ type BrowserPoolsCreateInput struct {
105105
StartURL string
106106
Extensions []string
107107
Viewport string
108+
ChromePolicy string
109+
ChromePolicyFile string
108110
Output string
109111
}
110112

@@ -166,6 +168,14 @@ func (c BrowserPoolsCmd) Create(ctx context.Context, in BrowserPoolsCreateInput)
166168
params.Viewport = *viewport
167169
}
168170

171+
chromePolicy, err := parseChromePolicy(in.ChromePolicy, in.ChromePolicyFile)
172+
if err != nil {
173+
return err
174+
}
175+
if len(chromePolicy) > 0 {
176+
params.ChromePolicy = chromePolicy
177+
}
178+
169179
pool, err := c.client.New(ctx, params)
170180
if err != nil {
171181
return util.CleanedUpSdkError{Err: err}
@@ -245,6 +255,8 @@ type BrowserPoolsUpdateInput struct {
245255
ClearStartURL bool
246256
Extensions []string
247257
Viewport string
258+
ChromePolicy string
259+
ChromePolicyFile string
248260
DiscardAllIdle BoolFlag
249261
Output string
250262
}
@@ -316,6 +328,19 @@ func (c BrowserPoolsCmd) Update(ctx context.Context, in BrowserPoolsUpdateInput)
316328
params.Viewport = *viewport
317329
}
318330

331+
chromePolicy, err := parseChromePolicy(in.ChromePolicy, in.ChromePolicyFile)
332+
if err != nil {
333+
return err
334+
}
335+
if len(chromePolicy) > 0 {
336+
params.ChromePolicy = chromePolicy
337+
} else if (in.ChromePolicy != "" || in.ChromePolicyFile != "") && in.Output != "json" {
338+
// An empty policy ({}) cannot clear an existing one: omitzero drops it before it
339+
// reaches the server. Warn instead of silently doing nothing, but stay quiet on the
340+
// json path so stdout remains valid JSON.
341+
pterm.Warning.Println("An empty chrome policy is ignored and does not clear the pool's existing policy; recreate the pool to remove a policy.")
342+
}
343+
319344
pool, err := c.client.Update(ctx, in.IDOrName, params)
320345
if err != nil {
321346
return util.CleanedUpSdkError{Err: err}
@@ -541,6 +566,9 @@ func init() {
541566
browserPoolsCreateCmd.Flags().String("start-url", "", "Initial page to open for new browsers")
542567
browserPoolsCreateCmd.Flags().StringSlice("extension", []string{}, "Extension IDs or names")
543568
browserPoolsCreateCmd.Flags().String("viewport", "", "Viewport size (e.g. 1280x800)")
569+
browserPoolsCreateCmd.Flags().String("chrome-policy", "", "Custom Chrome enterprise policy as a JSON object")
570+
browserPoolsCreateCmd.Flags().String("chrome-policy-file", "", "Read Chrome enterprise policy (JSON object) from a file (use '-' for stdin)")
571+
browserPoolsCreateCmd.MarkFlagsMutuallyExclusive("chrome-policy", "chrome-policy-file")
544572

545573
addJSONOutputFlag(browserPoolsGetCmd)
546574

@@ -559,6 +587,9 @@ func init() {
559587
browserPoolsUpdateCmd.Flags().Bool("clear-start-url", false, "Clear the pool start URL")
560588
browserPoolsUpdateCmd.Flags().StringSlice("extension", []string{}, "Extension IDs or names")
561589
browserPoolsUpdateCmd.Flags().String("viewport", "", "Viewport size (e.g. 1280x800)")
590+
browserPoolsUpdateCmd.Flags().String("chrome-policy", "", "Custom Chrome enterprise policy as a JSON object")
591+
browserPoolsUpdateCmd.Flags().String("chrome-policy-file", "", "Read Chrome enterprise policy (JSON object) from a file (use '-' for stdin)")
592+
browserPoolsUpdateCmd.MarkFlagsMutuallyExclusive("chrome-policy", "chrome-policy-file")
562593
browserPoolsUpdateCmd.Flags().Bool("discard-all-idle", false, "Discard all idle browsers")
563594
addJSONOutputFlag(browserPoolsUpdateCmd)
564595

@@ -615,6 +646,8 @@ func runBrowserPoolsCreate(cmd *cobra.Command, args []string) error {
615646
startURL, _ := cmd.Flags().GetString("start-url")
616647
extensions, _ := cmd.Flags().GetStringSlice("extension")
617648
viewport, _ := cmd.Flags().GetString("viewport")
649+
chromePolicy, _ := cmd.Flags().GetString("chrome-policy")
650+
chromePolicyFile, _ := cmd.Flags().GetString("chrome-policy-file")
618651
output, _ := cmd.Flags().GetString("output")
619652

620653
in := BrowserPoolsCreateInput{
@@ -632,6 +665,8 @@ func runBrowserPoolsCreate(cmd *cobra.Command, args []string) error {
632665
StartURL: startURL,
633666
Extensions: extensions,
634667
Viewport: viewport,
668+
ChromePolicy: chromePolicy,
669+
ChromePolicyFile: chromePolicyFile,
635670
Output: output,
636671
}
637672

@@ -664,6 +699,8 @@ func runBrowserPoolsUpdate(cmd *cobra.Command, args []string) error {
664699
clearStartURL, _ := cmd.Flags().GetBool("clear-start-url")
665700
extensions, _ := cmd.Flags().GetStringSlice("extension")
666701
viewport, _ := cmd.Flags().GetString("viewport")
702+
chromePolicy, _ := cmd.Flags().GetString("chrome-policy")
703+
chromePolicyFile, _ := cmd.Flags().GetString("chrome-policy-file")
667704
discardIdle, _ := cmd.Flags().GetBool("discard-all-idle")
668705
output, _ := cmd.Flags().GetString("output")
669706

@@ -684,6 +721,8 @@ func runBrowserPoolsUpdate(cmd *cobra.Command, args []string) error {
684721
ClearStartURL: clearStartURL,
685722
Extensions: extensions,
686723
Viewport: viewport,
724+
ChromePolicy: chromePolicy,
725+
ChromePolicyFile: chromePolicyFile,
687726
DiscardAllIdle: BoolFlag{Set: cmd.Flags().Changed("discard-all-idle"), Value: discardIdle},
688727
Output: output,
689728
}

cmd/browser_pools_test.go

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import (
1414
type FakeBrowserPoolsService struct {
1515
AcquireFunc func(ctx context.Context, id string, body kernel.BrowserPoolAcquireParams, opts ...option.RequestOption) (*kernel.BrowserPoolAcquireResponse, error)
1616
ListFunc func(ctx context.Context, query kernel.BrowserPoolListParams, opts ...option.RequestOption) (*pagination.OffsetPagination[kernel.BrowserPool], error)
17+
NewFunc func(ctx context.Context, body kernel.BrowserPoolNewParams, opts ...option.RequestOption) (*kernel.BrowserPool, error)
18+
UpdateFunc func(ctx context.Context, id string, body kernel.BrowserPoolUpdateParams, opts ...option.RequestOption) (*kernel.BrowserPool, error)
1719
}
1820

1921
func (f *FakeBrowserPoolsService) List(ctx context.Context, query kernel.BrowserPoolListParams, opts ...option.RequestOption) (*pagination.OffsetPagination[kernel.BrowserPool], error) {
@@ -24,6 +26,9 @@ func (f *FakeBrowserPoolsService) List(ctx context.Context, query kernel.Browser
2426
}
2527

2628
func (f *FakeBrowserPoolsService) New(ctx context.Context, body kernel.BrowserPoolNewParams, opts ...option.RequestOption) (*kernel.BrowserPool, error) {
29+
if f.NewFunc != nil {
30+
return f.NewFunc(ctx, body, opts...)
31+
}
2732
return &kernel.BrowserPool{}, nil
2833
}
2934

@@ -32,6 +37,9 @@ func (f *FakeBrowserPoolsService) Get(ctx context.Context, id string, opts ...op
3237
}
3338

3439
func (f *FakeBrowserPoolsService) Update(ctx context.Context, id string, body kernel.BrowserPoolUpdateParams, opts ...option.RequestOption) (*kernel.BrowserPool, error) {
40+
if f.UpdateFunc != nil {
41+
return f.UpdateFunc(ctx, id, body, opts...)
42+
}
3543
return &kernel.BrowserPool{}, nil
3644
}
3745

@@ -128,3 +136,101 @@ func TestBuildAcquireParams(t *testing.T) {
128136
assert.Len(t, empty.Tags, 0)
129137
assert.False(t, empty.AcquireTimeoutSeconds.Valid())
130138
}
139+
140+
func TestBrowserPoolsCreate_WithChromePolicy(t *testing.T) {
141+
setupStdoutCapture(t)
142+
143+
var captured kernel.BrowserPoolNewParams
144+
fake := &FakeBrowserPoolsService{
145+
NewFunc: func(ctx context.Context, body kernel.BrowserPoolNewParams, opts ...option.RequestOption) (*kernel.BrowserPool, error) {
146+
captured = body
147+
return &kernel.BrowserPool{ID: "pool-cp"}, nil
148+
},
149+
}
150+
151+
c := BrowserPoolsCmd{client: fake}
152+
err := c.Create(context.Background(), BrowserPoolsCreateInput{
153+
Size: 1,
154+
ChromePolicy: `{"BookmarkBarEnabled": false}`,
155+
})
156+
assert.NoError(t, err)
157+
assert.Equal(t, map[string]any{"BookmarkBarEnabled": false}, captured.ChromePolicy)
158+
}
159+
160+
func TestBrowserPoolsCreate_ChromePolicyEmptyObjectOmitted(t *testing.T) {
161+
setupStdoutCapture(t)
162+
163+
var captured kernel.BrowserPoolNewParams
164+
fake := &FakeBrowserPoolsService{
165+
NewFunc: func(ctx context.Context, body kernel.BrowserPoolNewParams, opts ...option.RequestOption) (*kernel.BrowserPool, error) {
166+
captured = body
167+
return &kernel.BrowserPool{ID: "pool-cp"}, nil
168+
},
169+
}
170+
171+
c := BrowserPoolsCmd{client: fake}
172+
err := c.Create(context.Background(), BrowserPoolsCreateInput{Size: 1, ChromePolicy: "{}"})
173+
assert.NoError(t, err)
174+
assert.Nil(t, captured.ChromePolicy)
175+
}
176+
177+
func TestBrowserPoolsUpdate_WithChromePolicy(t *testing.T) {
178+
setupStdoutCapture(t)
179+
180+
var captured kernel.BrowserPoolUpdateParams
181+
fake := &FakeBrowserPoolsService{
182+
UpdateFunc: func(ctx context.Context, id string, body kernel.BrowserPoolUpdateParams, opts ...option.RequestOption) (*kernel.BrowserPool, error) {
183+
captured = body
184+
return &kernel.BrowserPool{ID: id}, nil
185+
},
186+
}
187+
188+
c := BrowserPoolsCmd{client: fake}
189+
err := c.Update(context.Background(), BrowserPoolsUpdateInput{
190+
IDOrName: "pool-1",
191+
ChromePolicy: `{"BookmarkBarEnabled": false}`,
192+
})
193+
assert.NoError(t, err)
194+
assert.Equal(t, map[string]any{"BookmarkBarEnabled": false}, captured.ChromePolicy)
195+
}
196+
197+
func TestBrowserPoolsUpdate_EmptyChromePolicyWarnsAndDoesNotClear(t *testing.T) {
198+
setupStdoutCapture(t)
199+
200+
var captured kernel.BrowserPoolUpdateParams
201+
fake := &FakeBrowserPoolsService{
202+
UpdateFunc: func(ctx context.Context, id string, body kernel.BrowserPoolUpdateParams, opts ...option.RequestOption) (*kernel.BrowserPool, error) {
203+
captured = body
204+
return &kernel.BrowserPool{ID: id}, nil
205+
},
206+
}
207+
208+
c := BrowserPoolsCmd{client: fake}
209+
err := c.Update(context.Background(), BrowserPoolsUpdateInput{
210+
IDOrName: "pool-1",
211+
ChromePolicy: "{}",
212+
})
213+
assert.NoError(t, err)
214+
assert.Nil(t, captured.ChromePolicy)
215+
assert.Contains(t, outBuf.String(), "does not clear")
216+
}
217+
218+
func TestBrowserPoolsUpdate_EmptyChromePolicyQuietInJSONMode(t *testing.T) {
219+
setupStdoutCapture(t)
220+
221+
fake := &FakeBrowserPoolsService{
222+
UpdateFunc: func(ctx context.Context, id string, body kernel.BrowserPoolUpdateParams, opts ...option.RequestOption) (*kernel.BrowserPool, error) {
223+
return &kernel.BrowserPool{ID: id}, nil
224+
},
225+
}
226+
227+
c := BrowserPoolsCmd{client: fake}
228+
err := c.Update(context.Background(), BrowserPoolsUpdateInput{
229+
IDOrName: "pool-1",
230+
ChromePolicy: "{}",
231+
Output: "json",
232+
})
233+
assert.NoError(t, err)
234+
// The warning must not leak onto stdout in json mode, where it would corrupt the payload.
235+
assert.NotContains(t, outBuf.String(), "does not clear")
236+
}

0 commit comments

Comments
 (0)