From 5e31861edf251908db94a60e32f5f15507a6f272 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 26 Jun 2026 18:24:02 +0000 Subject: [PATCH 1/2] fix(api): browser pool profile omits save_changes (BrowserPoolProfile) --- .stats.yml | 4 +- aliases.go | 7 --- api.md | 1 - browserpool.go | 115 ++++++++++++++++++++++++++++++++++++++------ browserpool_test.go | 14 +++--- shared/shared.go | 37 -------------- 6 files changed, 109 insertions(+), 69 deletions(-) diff --git a/.stats.yml b/.stats.yml index ef32d38..10d9022 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 124 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel/kernel-4fb45d71a99648425c84bdc8e5780920105cede4ee2d4eac67276d0609ac1e94.yml -openapi_spec_hash: 1f04cb5b36e92db81dfa264c2a59c32a +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel/kernel-4c243ff089133bd49322d98a6943647589972f71ecadc993fe9e5029972b3995.yml +openapi_spec_hash: a2cb637a19a070d07a1a4343c75444ee config_hash: fb167e754ebb3a14649463725891c9d0 diff --git a/aliases.go b/aliases.go index fb2a35c..4e4c322 100644 --- a/aliases.go +++ b/aliases.go @@ -33,13 +33,6 @@ type BrowserExtension = shared.BrowserExtension // This is an alias to an internal type. type BrowserExtensionParam = shared.BrowserExtensionParam -// Profile selection for the browser session. Provide either id or name. If -// specified, the matching profile will be loaded into the browser session. -// Profiles must be created beforehand. -// -// This is an alias to an internal type. -type BrowserProfile = shared.BrowserProfile - // Profile selection for the browser session. Provide either id or name. If // specified, the matching profile will be loaded into the browser session. // Profiles must be created beforehand. diff --git a/api.md b/api.md index 09422fc..c119a4c 100644 --- a/api.md +++ b/api.md @@ -8,7 +8,6 @@ - shared.AppAction - shared.BrowserExtension -- shared.BrowserProfile - shared.BrowserViewport - shared.ErrorDetail - shared.ErrorEvent diff --git a/browserpool.go b/browserpool.go index 62d2422..651a721 100644 --- a/browserpool.go +++ b/browserpool.go @@ -42,7 +42,10 @@ func NewBrowserPoolService(opts ...option.RequestOption) (r BrowserPoolService) return } -// Create a new browser pool with the specified configuration and size. +// Create a new browser pool with the specified configuration and size. Pooled +// browsers load their profile read-only: any save_changes on the profile is +// ignored (not rejected), so pooled browsers never persist changes back to the +// profile. func (r *BrowserPoolService) New(ctx context.Context, body BrowserPoolNewParams, opts ...option.RequestOption) (res *BrowserPool, err error) { opts = slices.Concat(r.Options, opts) path := "browser_pools" @@ -62,7 +65,9 @@ func (r *BrowserPoolService) Get(ctx context.Context, idOrName string, opts ...o return res, err } -// Updates the configuration used to create browsers in the pool. +// Updates the configuration used to create browsers in the pool. As with creation, +// save_changes on the pool profile is ignored (not rejected); pooled browsers +// never persist changes back to the profile. func (r *BrowserPoolService) Update(ctx context.Context, idOrName string, body BrowserPoolUpdateParams, opts ...option.RequestOption) (res *BrowserPool, err error) { opts = slices.Concat(r.Options, opts) if idOrName == "" { @@ -209,10 +214,13 @@ type BrowserPoolBrowserPoolConfig struct { KioskMode bool `json:"kiosk_mode"` // Optional name for the browser pool. Must be unique within the project. Name string `json:"name"` - // Profile selection for the browser session. Provide either id or name. If - // specified, the matching profile will be loaded into the browser session. - // Profiles must be created beforehand. - Profile shared.BrowserProfile `json:"profile"` + // Profile selection for browsers in a pool. Provide either id or name. The + // matching profile is loaded into every browser in the pool. Profiles must be + // created beforehand. Unlike single browser sessions, pools load the profile + // read-only and never persist changes back to it, so save_changes is omitted here. + // Any save_changes value sent on a pool profile is silently ignored rather than + // rejected, so callers reusing a single-session profile object will not error. + Profile BrowserPoolBrowserPoolConfigProfile `json:"profile"` // Optional proxy to associate to the browser session. Must reference a proxy in // the same project as the browser session. ProxyID string `json:"proxy_id"` @@ -267,6 +275,33 @@ func (r *BrowserPoolBrowserPoolConfig) UnmarshalJSON(data []byte) error { return apijson.UnmarshalRoot(data, r) } +// Profile selection for browsers in a pool. Provide either id or name. The +// matching profile is loaded into every browser in the pool. Profiles must be +// created beforehand. Unlike single browser sessions, pools load the profile +// read-only and never persist changes back to it, so save_changes is omitted here. +// Any save_changes value sent on a pool profile is silently ignored rather than +// rejected, so callers reusing a single-session profile object will not error. +type BrowserPoolBrowserPoolConfigProfile struct { + // Profile ID to load for browsers in this pool + ID string `json:"id"` + // Profile name to load for browsers in this pool (instead of id). Must be 1-255 + // characters, using letters, numbers, dots, underscores, or hyphens. + Name string `json:"name"` + // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. + JSON struct { + ID respjson.Field + Name respjson.Field + ExtraFields map[string]respjson.Field + raw string + } `json:"-"` +} + +// Returns the unmodified JSON received from the API +func (r BrowserPoolBrowserPoolConfigProfile) RawJSON() string { return r.JSON.raw } +func (r *BrowserPoolBrowserPoolConfigProfile) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} + type BrowserPoolAcquireResponse struct { // Websocket URL for Chrome DevTools Protocol connections to the browser session CdpWsURL string `json:"cdp_ws_url" api:"required"` @@ -405,10 +440,13 @@ type BrowserPoolNewParams struct { ChromePolicy map[string]any `json:"chrome_policy,omitzero"` // List of browser extensions to load into the session. Provide each by id or name. Extensions []shared.BrowserExtensionParam `json:"extensions,omitzero"` - // Profile selection for the browser session. Provide either id or name. If - // specified, the matching profile will be loaded into the browser session. - // Profiles must be created beforehand. - Profile shared.BrowserProfileParam `json:"profile,omitzero"` + // Profile selection for browsers in a pool. Provide either id or name. The + // matching profile is loaded into every browser in the pool. Profiles must be + // created beforehand. Unlike single browser sessions, pools load the profile + // read-only and never persist changes back to it, so save_changes is omitted here. + // Any save_changes value sent on a pool profile is silently ignored rather than + // rejected, so callers reusing a single-session profile object will not error. + Profile BrowserPoolNewParamsProfile `json:"profile,omitzero"` // Initial browser window size in pixels with optional refresh rate. If omitted, // image defaults apply (1920x1080@25). For GPU images, the default is // 1920x1080@60. Arbitrary viewport dimensions and refresh rates are accepted. @@ -433,6 +471,29 @@ func (r *BrowserPoolNewParams) UnmarshalJSON(data []byte) error { return apijson.UnmarshalRoot(data, r) } +// Profile selection for browsers in a pool. Provide either id or name. The +// matching profile is loaded into every browser in the pool. Profiles must be +// created beforehand. Unlike single browser sessions, pools load the profile +// read-only and never persist changes back to it, so save_changes is omitted here. +// Any save_changes value sent on a pool profile is silently ignored rather than +// rejected, so callers reusing a single-session profile object will not error. +type BrowserPoolNewParamsProfile struct { + // Profile ID to load for browsers in this pool + ID param.Opt[string] `json:"id,omitzero"` + // Profile name to load for browsers in this pool (instead of id). Must be 1-255 + // characters, using letters, numbers, dots, underscores, or hyphens. + Name param.Opt[string] `json:"name,omitzero"` + paramObj +} + +func (r BrowserPoolNewParamsProfile) MarshalJSON() (data []byte, err error) { + type shadow BrowserPoolNewParamsProfile + return param.MarshalObject(r, (*shadow)(&r)) +} +func (r *BrowserPoolNewParamsProfile) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} + type BrowserPoolUpdateParams struct { // Whether to discard all idle browsers and rebuild the pool immediately. Defaults // to false. @@ -474,10 +535,13 @@ type BrowserPoolUpdateParams struct { ChromePolicy map[string]any `json:"chrome_policy,omitzero"` // List of browser extensions to load into the session. Provide each by id or name. Extensions []shared.BrowserExtensionParam `json:"extensions,omitzero"` - // Profile selection for the browser session. Provide either id or name. If - // specified, the matching profile will be loaded into the browser session. - // Profiles must be created beforehand. - Profile shared.BrowserProfileParam `json:"profile,omitzero"` + // Profile selection for browsers in a pool. Provide either id or name. The + // matching profile is loaded into every browser in the pool. Profiles must be + // created beforehand. Unlike single browser sessions, pools load the profile + // read-only and never persist changes back to it, so save_changes is omitted here. + // Any save_changes value sent on a pool profile is silently ignored rather than + // rejected, so callers reusing a single-session profile object will not error. + Profile BrowserPoolUpdateParamsProfile `json:"profile,omitzero"` // Initial browser window size in pixels with optional refresh rate. If omitted, // image defaults apply (1920x1080@25). For GPU images, the default is // 1920x1080@60. Arbitrary viewport dimensions and refresh rates are accepted. @@ -502,6 +566,29 @@ func (r *BrowserPoolUpdateParams) UnmarshalJSON(data []byte) error { return apijson.UnmarshalRoot(data, r) } +// Profile selection for browsers in a pool. Provide either id or name. The +// matching profile is loaded into every browser in the pool. Profiles must be +// created beforehand. Unlike single browser sessions, pools load the profile +// read-only and never persist changes back to it, so save_changes is omitted here. +// Any save_changes value sent on a pool profile is silently ignored rather than +// rejected, so callers reusing a single-session profile object will not error. +type BrowserPoolUpdateParamsProfile struct { + // Profile ID to load for browsers in this pool + ID param.Opt[string] `json:"id,omitzero"` + // Profile name to load for browsers in this pool (instead of id). Must be 1-255 + // characters, using letters, numbers, dots, underscores, or hyphens. + Name param.Opt[string] `json:"name,omitzero"` + paramObj +} + +func (r BrowserPoolUpdateParamsProfile) MarshalJSON() (data []byte, err error) { + type shadow BrowserPoolUpdateParamsProfile + return param.MarshalObject(r, (*shadow)(&r)) +} +func (r *BrowserPoolUpdateParamsProfile) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} + type BrowserPoolListParams struct { // Limit the number of browser pools to return. Limit param.Opt[int64] `query:"limit,omitzero" json:"-"` diff --git a/browserpool_test.go b/browserpool_test.go index 9de8eea..751cc53 100644 --- a/browserpool_test.go +++ b/browserpool_test.go @@ -40,10 +40,9 @@ func TestBrowserPoolNewWithOptionalParams(t *testing.T) { Headless: kernel.Bool(false), KioskMode: kernel.Bool(true), Name: kernel.String("my-pool"), - Profile: shared.BrowserProfileParam{ - ID: kernel.String("id"), - Name: kernel.String("name"), - SaveChanges: kernel.Bool(true), + Profile: kernel.BrowserPoolNewParamsProfile{ + ID: kernel.String("id"), + Name: kernel.String("name"), }, ProxyID: kernel.String("proxy_id"), StartURL: kernel.String("https://example.com"), @@ -116,10 +115,9 @@ func TestBrowserPoolUpdateWithOptionalParams(t *testing.T) { Headless: kernel.Bool(false), KioskMode: kernel.Bool(true), Name: kernel.String("my-pool"), - Profile: shared.BrowserProfileParam{ - ID: kernel.String("id"), - Name: kernel.String("name"), - SaveChanges: kernel.Bool(true), + Profile: kernel.BrowserPoolUpdateParamsProfile{ + ID: kernel.String("id"), + Name: kernel.String("name"), }, ProxyID: kernel.String("proxy_id"), Size: kernel.Int(10), diff --git a/shared/shared.go b/shared/shared.go index 366ff9e..2d07e6d 100644 --- a/shared/shared.go +++ b/shared/shared.go @@ -95,43 +95,6 @@ func (r *BrowserExtensionParam) UnmarshalJSON(data []byte) error { return apijson.UnmarshalRoot(data, r) } -// Profile selection for the browser session. Provide either id or name. If -// specified, the matching profile will be loaded into the browser session. -// Profiles must be created beforehand. -type BrowserProfile struct { - // Profile ID to load for this browser session - ID string `json:"id"` - // Profile name to load for this browser session (instead of id). Must be 1-255 - // characters, using letters, numbers, dots, underscores, or hyphens. - Name string `json:"name"` - // If true, save changes made during the session back to the profile when the - // session ends. - SaveChanges bool `json:"save_changes"` - // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. - JSON struct { - ID respjson.Field - Name respjson.Field - SaveChanges respjson.Field - ExtraFields map[string]respjson.Field - raw string - } `json:"-"` -} - -// Returns the unmodified JSON received from the API -func (r BrowserProfile) RawJSON() string { return r.JSON.raw } -func (r *BrowserProfile) UnmarshalJSON(data []byte) error { - return apijson.UnmarshalRoot(data, r) -} - -// ToParam converts this BrowserProfile to a BrowserProfileParam. -// -// Warning: the fields of the param type will not be present. ToParam should only -// be used at the last possible moment before sending a request. Test for this with -// BrowserProfileParam.Overrides() -func (r BrowserProfile) ToParam() BrowserProfileParam { - return param.Override[BrowserProfileParam](json.RawMessage(r.RawJSON())) -} - // Profile selection for the browser session. Provide either id or name. If // specified, the matching profile will be loaded into the browser session. // Profiles must be created beforehand. From 6c1d5e51b5824e82e68670edf1f30530a35e2198 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 26 Jun 2026 19:10:48 +0000 Subject: [PATCH 2/2] release: 0.72.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ README.md | 2 +- internal/version.go | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 6772f01..6f257c8 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.71.0" + ".": "0.72.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a23788..014bb49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 0.72.0 (2026-06-26) + +Full Changelog: [v0.71.0...v0.72.0](https://github.com/kernel/kernel-go-sdk/compare/v0.71.0...v0.72.0) + +### Bug Fixes + +* **api:** browser pool profile omits save_changes (BrowserPoolProfile) ([5e31861](https://github.com/kernel/kernel-go-sdk/commit/5e31861edf251908db94a60e32f5f15507a6f272)) + ## 0.71.0 (2026-06-26) Full Changelog: [v0.70.0...v0.71.0](https://github.com/kernel/kernel-go-sdk/compare/v0.70.0...v0.71.0) diff --git a/README.md b/README.md index 5375aa5..14f9663 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Or to pin the version: ```sh -go get -u 'github.com/kernel/kernel-go-sdk@v0.71.0' +go get -u 'github.com/kernel/kernel-go-sdk@v0.72.0' ``` diff --git a/internal/version.go b/internal/version.go index 8f679d4..7b8ed86 100644 --- a/internal/version.go +++ b/internal/version.go @@ -2,4 +2,4 @@ package internal -const PackageVersion = "0.71.0" // x-release-please-version +const PackageVersion = "0.72.0" // x-release-please-version