From ee63aba391e6a3024e93eeca28d008ea93b18b58 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 24 Nov 2025 18:52:31 +0000 Subject: [PATCH 1/7] feat: Mason/agent auth api --- .stats.yml | 8 +- agent.go | 28 +++++ agentauth.go | 267 +++++++++++++++++++++++++++++++++++++++++++ agentauth_test.go | 44 +++++++ agentauthrun.go | 150 ++++++++++++++++++++++++ agentauthrun_test.go | 125 ++++++++++++++++++++ api.md | 29 +++++ client.go | 2 + 8 files changed, 649 insertions(+), 4 deletions(-) create mode 100644 agent.go create mode 100644 agentauth.go create mode 100644 agentauth_test.go create mode 100644 agentauthrun.go create mode 100644 agentauthrun_test.go diff --git a/.stats.yml b/.stats.yml index 11b820d..bdbede3 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 66 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-2af1b468584cb44aa9babbbfb82bff4055614fbb5c815084a6b7dacc1cf1a822.yml -openapi_spec_hash: 891affa2849341ea01d62011125f7edc -config_hash: 9421eb86b7f3f4b274f123279da3858e +configured_endpoints: 71 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-bb3f37e55117a56e7a4208bd646d3a68adeb651ced8531e13fbfc1fc9dcb05a4.yml +openapi_spec_hash: 7303ce8ce3130e16a6a5c2bb49e43e9b +config_hash: be146470fb2d4583b6533859f0fa48f5 diff --git a/agent.go b/agent.go new file mode 100644 index 0000000..d91fa4e --- /dev/null +++ b/agent.go @@ -0,0 +1,28 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package kernel + +import ( + "github.com/onkernel/kernel-go-sdk/option" +) + +// AgentService contains methods and other services that help with interacting with +// the kernel API. +// +// Note, unlike clients, this service does not read variables from the environment +// automatically. You should not instantiate this service directly, and instead use +// the [NewAgentService] method instead. +type AgentService struct { + Options []option.RequestOption + Auth AgentAuthService +} + +// NewAgentService generates a new service that applies the given options to each +// request. These options are applied after the parent client's options (if there +// is one), and before any request-specific options. +func NewAgentService(opts ...option.RequestOption) (r AgentService) { + r = AgentService{} + r.Options = opts + r.Auth = NewAgentAuthService(opts...) + return +} diff --git a/agentauth.go b/agentauth.go new file mode 100644 index 0000000..373ecb6 --- /dev/null +++ b/agentauth.go @@ -0,0 +1,267 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package kernel + +import ( + "context" + "net/http" + "slices" + "time" + + "github.com/onkernel/kernel-go-sdk/internal/apijson" + "github.com/onkernel/kernel-go-sdk/internal/requestconfig" + "github.com/onkernel/kernel-go-sdk/option" + "github.com/onkernel/kernel-go-sdk/packages/param" + "github.com/onkernel/kernel-go-sdk/packages/respjson" +) + +// AgentAuthService contains methods and other services that help with interacting +// with the kernel API. +// +// Note, unlike clients, this service does not read variables from the environment +// automatically. You should not instantiate this service directly, and instead use +// the [NewAgentAuthService] method instead. +type AgentAuthService struct { + Options []option.RequestOption + Runs AgentAuthRunService +} + +// NewAgentAuthService generates a new service that applies the given options to +// each request. These options are applied after the parent client's options (if +// there is one), and before any request-specific options. +func NewAgentAuthService(opts ...option.RequestOption) (r AgentAuthService) { + r = AgentAuthService{} + r.Options = opts + r.Runs = NewAgentAuthRunService(opts...) + return +} + +// Creates a browser session and returns a handoff code for the hosted flow. Uses +// standard API key or JWT authentication (not the JWT returned by the exchange +// endpoint). +func (r *AgentAuthService) Start(ctx context.Context, body AgentAuthStartParams, opts ...option.RequestOption) (res *AgentAuthStartResponse, err error) { + opts = slices.Concat(r.Options, opts) + path := "agents/auth/start" + err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, body, &res, opts...) + return +} + +// Response from discover endpoint matching AuthBlueprint schema +type AgentAuthDiscoverResponse struct { + // Whether discovery succeeded + Success bool `json:"success,required"` + // Error message if discovery failed + ErrorMessage string `json:"error_message"` + // Discovered form fields (present when success is true) + Fields []DiscoveredField `json:"fields"` + // Whether user is already logged in + LoggedIn bool `json:"logged_in"` + // URL of the discovered login page + LoginURL string `json:"login_url" format:"uri"` + // Title of the login page + PageTitle string `json:"page_title"` + // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. + JSON struct { + Success respjson.Field + ErrorMessage respjson.Field + Fields respjson.Field + LoggedIn respjson.Field + LoginURL respjson.Field + PageTitle respjson.Field + ExtraFields map[string]respjson.Field + raw string + } `json:"-"` +} + +// Returns the unmodified JSON received from the API +func (r AgentAuthDiscoverResponse) RawJSON() string { return r.JSON.raw } +func (r *AgentAuthDiscoverResponse) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} + +// Response from get run endpoint +type AgentAuthRunResponse struct { + // App name (org name at time of run creation) + AppName string `json:"app_name,required"` + // When the handoff code expires + ExpiresAt time.Time `json:"expires_at,required" format:"date-time"` + // Run status + // + // Any of "ACTIVE", "ENDED", "EXPIRED", "CANCELED". + Status AgentAuthRunResponseStatus `json:"status,required"` + // Target domain for authentication + TargetDomain string `json:"target_domain,required"` + // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. + JSON struct { + AppName respjson.Field + ExpiresAt respjson.Field + Status respjson.Field + TargetDomain respjson.Field + ExtraFields map[string]respjson.Field + raw string + } `json:"-"` +} + +// Returns the unmodified JSON received from the API +func (r AgentAuthRunResponse) RawJSON() string { return r.JSON.raw } +func (r *AgentAuthRunResponse) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} + +// Run status +type AgentAuthRunResponseStatus string + +const ( + AgentAuthRunResponseStatusActive AgentAuthRunResponseStatus = "ACTIVE" + AgentAuthRunResponseStatusEnded AgentAuthRunResponseStatus = "ENDED" + AgentAuthRunResponseStatusExpired AgentAuthRunResponseStatus = "EXPIRED" + AgentAuthRunResponseStatusCanceled AgentAuthRunResponseStatus = "CANCELED" +) + +// Response from starting an agent authentication run +type AgentAuthStartResponse struct { + // When the handoff code expires + ExpiresAt time.Time `json:"expires_at,required" format:"date-time"` + // One-time code for handoff + HandoffCode string `json:"handoff_code,required"` + // URL to redirect user to + HostedURL string `json:"hosted_url,required" format:"uri"` + // Unique identifier for the run + RunID string `json:"run_id,required"` + // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. + JSON struct { + ExpiresAt respjson.Field + HandoffCode respjson.Field + HostedURL respjson.Field + RunID respjson.Field + ExtraFields map[string]respjson.Field + raw string + } `json:"-"` +} + +// Returns the unmodified JSON received from the API +func (r AgentAuthStartResponse) RawJSON() string { return r.JSON.raw } +func (r *AgentAuthStartResponse) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} + +// Response from submit endpoint matching SubmitResult schema +type AgentAuthSubmitResponse struct { + // Whether submission succeeded + Success bool `json:"success,required"` + // Additional fields needed (e.g., OTP) - present when needs_additional_auth is + // true + AdditionalFields []DiscoveredField `json:"additional_fields"` + // App name (only present when logged_in is true) + AppName string `json:"app_name"` + // Error message if submission failed + ErrorMessage string `json:"error_message"` + // Whether user is now logged in + LoggedIn bool `json:"logged_in"` + // Whether additional authentication fields are needed + NeedsAdditionalAuth bool `json:"needs_additional_auth"` + // Target domain (only present when logged_in is true) + TargetDomain string `json:"target_domain"` + // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. + JSON struct { + Success respjson.Field + AdditionalFields respjson.Field + AppName respjson.Field + ErrorMessage respjson.Field + LoggedIn respjson.Field + NeedsAdditionalAuth respjson.Field + TargetDomain respjson.Field + ExtraFields map[string]respjson.Field + raw string + } `json:"-"` +} + +// Returns the unmodified JSON received from the API +func (r AgentAuthSubmitResponse) RawJSON() string { return r.JSON.raw } +func (r *AgentAuthSubmitResponse) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} + +// A discovered form field +type DiscoveredField struct { + // Field label + Label string `json:"label,required"` + // Field name + Name string `json:"name,required"` + // CSS selector for the field + Selector string `json:"selector,required"` + // Field type + // + // Any of "text", "email", "password", "tel", "number", "url", "code", "checkbox". + Type DiscoveredFieldType `json:"type,required"` + // Field placeholder + Placeholder string `json:"placeholder"` + // Whether field is required + Required bool `json:"required"` + // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. + JSON struct { + Label respjson.Field + Name respjson.Field + Selector respjson.Field + Type respjson.Field + Placeholder respjson.Field + Required respjson.Field + ExtraFields map[string]respjson.Field + raw string + } `json:"-"` +} + +// Returns the unmodified JSON received from the API +func (r DiscoveredField) RawJSON() string { return r.JSON.raw } +func (r *DiscoveredField) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} + +// Field type +type DiscoveredFieldType string + +const ( + DiscoveredFieldTypeText DiscoveredFieldType = "text" + DiscoveredFieldTypeEmail DiscoveredFieldType = "email" + DiscoveredFieldTypePassword DiscoveredFieldType = "password" + DiscoveredFieldTypeTel DiscoveredFieldType = "tel" + DiscoveredFieldTypeNumber DiscoveredFieldType = "number" + DiscoveredFieldTypeURL DiscoveredFieldType = "url" + DiscoveredFieldTypeCode DiscoveredFieldType = "code" + DiscoveredFieldTypeCheckbox DiscoveredFieldType = "checkbox" +) + +type AgentAuthStartParams struct { + // Name of the profile to use for this flow + ProfileName string `json:"profile_name,required"` + // Target domain for authentication + TargetDomain string `json:"target_domain,required"` + // Optional logo URL for the application + AppLogoURL param.Opt[string] `json:"app_logo_url,omitzero" format:"uri"` + // Optional proxy configuration + Proxy AgentAuthStartParamsProxy `json:"proxy,omitzero"` + paramObj +} + +func (r AgentAuthStartParams) MarshalJSON() (data []byte, err error) { + type shadow AgentAuthStartParams + return param.MarshalObject(r, (*shadow)(&r)) +} +func (r *AgentAuthStartParams) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} + +// Optional proxy configuration +type AgentAuthStartParamsProxy struct { + // ID of the proxy to use + ProxyID param.Opt[string] `json:"proxy_id,omitzero"` + paramObj +} + +func (r AgentAuthStartParamsProxy) MarshalJSON() (data []byte, err error) { + type shadow AgentAuthStartParamsProxy + return param.MarshalObject(r, (*shadow)(&r)) +} +func (r *AgentAuthStartParamsProxy) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} diff --git a/agentauth_test.go b/agentauth_test.go new file mode 100644 index 0000000..ab734e1 --- /dev/null +++ b/agentauth_test.go @@ -0,0 +1,44 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package kernel_test + +import ( + "context" + "errors" + "os" + "testing" + + "github.com/onkernel/kernel-go-sdk" + "github.com/onkernel/kernel-go-sdk/internal/testutil" + "github.com/onkernel/kernel-go-sdk/option" +) + +func TestAgentAuthStartWithOptionalParams(t *testing.T) { + t.Skip("Prism tests are disabled") + baseURL := "http://localhost:4010" + if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok { + baseURL = envURL + } + if !testutil.CheckTestServer(t, baseURL) { + return + } + client := kernel.NewClient( + option.WithBaseURL(baseURL), + option.WithAPIKey("My API Key"), + ) + _, err := client.Agents.Auth.Start(context.TODO(), kernel.AgentAuthStartParams{ + ProfileName: "auth-abc123", + TargetDomain: "doordash.com", + AppLogoURL: kernel.String("https://example.com/logo.png"), + Proxy: kernel.AgentAuthStartParamsProxy{ + ProxyID: kernel.String("proxy_id"), + }, + }) + if err != nil { + var apierr *kernel.Error + if errors.As(err, &apierr) { + t.Log(string(apierr.DumpRequest(true))) + } + t.Fatalf("err should be nil: %s", err.Error()) + } +} diff --git a/agentauthrun.go b/agentauthrun.go new file mode 100644 index 0000000..da62724 --- /dev/null +++ b/agentauthrun.go @@ -0,0 +1,150 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package kernel + +import ( + "context" + "errors" + "fmt" + "net/http" + "slices" + + "github.com/onkernel/kernel-go-sdk/internal/apijson" + "github.com/onkernel/kernel-go-sdk/internal/requestconfig" + "github.com/onkernel/kernel-go-sdk/option" + "github.com/onkernel/kernel-go-sdk/packages/param" + "github.com/onkernel/kernel-go-sdk/packages/respjson" +) + +// AgentAuthRunService contains methods and other services that help with +// interacting with the kernel API. +// +// Note, unlike clients, this service does not read variables from the environment +// automatically. You should not instantiate this service directly, and instead use +// the [NewAgentAuthRunService] method instead. +type AgentAuthRunService struct { + Options []option.RequestOption +} + +// NewAgentAuthRunService generates a new service that applies the given options to +// each request. These options are applied after the parent client's options (if +// there is one), and before any request-specific options. +func NewAgentAuthRunService(opts ...option.RequestOption) (r AgentAuthRunService) { + r = AgentAuthRunService{} + r.Options = opts + return +} + +// Returns run details including app_name and target_domain. Uses the JWT returned +// by the exchange endpoint, or standard API key or JWT authentication. +func (r *AgentAuthRunService) Get(ctx context.Context, runID string, opts ...option.RequestOption) (res *AgentAuthRunResponse, err error) { + opts = slices.Concat(r.Options, opts) + if runID == "" { + err = errors.New("missing required run_id parameter") + return + } + path := fmt.Sprintf("agents/auth/runs/%s", runID) + err = requestconfig.ExecuteNewRequest(ctx, http.MethodGet, path, nil, &res, opts...) + return +} + +// Inspects the target site to detect logged-in state or discover required fields. +// Returns 200 with success: true when fields are found, or 4xx/5xx for failures. +// Requires the JWT returned by the exchange endpoint. +func (r *AgentAuthRunService) Discover(ctx context.Context, runID string, body AgentAuthRunDiscoverParams, opts ...option.RequestOption) (res *AgentAuthDiscoverResponse, err error) { + opts = slices.Concat(r.Options, opts) + if runID == "" { + err = errors.New("missing required run_id parameter") + return + } + path := fmt.Sprintf("agents/auth/runs/%s/discover", runID) + err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, body, &res, opts...) + return +} + +// Validates the handoff code and returns a JWT token for subsequent requests. No +// authentication required (the handoff code serves as the credential). +func (r *AgentAuthRunService) Exchange(ctx context.Context, runID string, body AgentAuthRunExchangeParams, opts ...option.RequestOption) (res *AgentAuthRunExchangeResponse, err error) { + opts = slices.Concat(r.Options, opts) + if runID == "" { + err = errors.New("missing required run_id parameter") + return + } + path := fmt.Sprintf("agents/auth/runs/%s/exchange", runID) + err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, body, &res, opts...) + return +} + +// Submits field values for the discovered login form and may return additional +// auth fields or success. Requires the JWT returned by the exchange endpoint. +func (r *AgentAuthRunService) Submit(ctx context.Context, runID string, body AgentAuthRunSubmitParams, opts ...option.RequestOption) (res *AgentAuthSubmitResponse, err error) { + opts = slices.Concat(r.Options, opts) + if runID == "" { + err = errors.New("missing required run_id parameter") + return + } + path := fmt.Sprintf("agents/auth/runs/%s/submit", runID) + err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, body, &res, opts...) + return +} + +// Response from exchange endpoint +type AgentAuthRunExchangeResponse struct { + // JWT token with run_id claim (30 minute TTL) + Jwt string `json:"jwt,required"` + // Run ID + RunID string `json:"run_id,required"` + // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. + JSON struct { + Jwt respjson.Field + RunID respjson.Field + ExtraFields map[string]respjson.Field + raw string + } `json:"-"` +} + +// Returns the unmodified JSON received from the API +func (r AgentAuthRunExchangeResponse) RawJSON() string { return r.JSON.raw } +func (r *AgentAuthRunExchangeResponse) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} + +type AgentAuthRunDiscoverParams struct { + paramObj +} + +func (r AgentAuthRunDiscoverParams) MarshalJSON() (data []byte, err error) { + type shadow AgentAuthRunDiscoverParams + return param.MarshalObject(r, (*shadow)(&r)) +} +func (r *AgentAuthRunDiscoverParams) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} + +type AgentAuthRunExchangeParams struct { + // Handoff code from start endpoint + Code string `json:"code,required"` + paramObj +} + +func (r AgentAuthRunExchangeParams) MarshalJSON() (data []byte, err error) { + type shadow AgentAuthRunExchangeParams + return param.MarshalObject(r, (*shadow)(&r)) +} +func (r *AgentAuthRunExchangeParams) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} + +type AgentAuthRunSubmitParams struct { + // Values for the discovered login fields + FieldValues map[string]string `json:"field_values,omitzero,required"` + paramObj +} + +func (r AgentAuthRunSubmitParams) MarshalJSON() (data []byte, err error) { + type shadow AgentAuthRunSubmitParams + return param.MarshalObject(r, (*shadow)(&r)) +} +func (r *AgentAuthRunSubmitParams) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} diff --git a/agentauthrun_test.go b/agentauthrun_test.go new file mode 100644 index 0000000..b0b7603 --- /dev/null +++ b/agentauthrun_test.go @@ -0,0 +1,125 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package kernel_test + +import ( + "context" + "errors" + "os" + "testing" + + "github.com/onkernel/kernel-go-sdk" + "github.com/onkernel/kernel-go-sdk/internal/testutil" + "github.com/onkernel/kernel-go-sdk/option" +) + +func TestAgentAuthRunGet(t *testing.T) { + t.Skip("Prism tests are disabled") + baseURL := "http://localhost:4010" + if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok { + baseURL = envURL + } + if !testutil.CheckTestServer(t, baseURL) { + return + } + client := kernel.NewClient( + option.WithBaseURL(baseURL), + option.WithAPIKey("My API Key"), + ) + _, err := client.Agents.Auth.Runs.Get(context.TODO(), "run_id") + if err != nil { + var apierr *kernel.Error + if errors.As(err, &apierr) { + t.Log(string(apierr.DumpRequest(true))) + } + t.Fatalf("err should be nil: %s", err.Error()) + } +} + +func TestAgentAuthRunDiscover(t *testing.T) { + t.Skip("Prism tests are disabled") + baseURL := "http://localhost:4010" + if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok { + baseURL = envURL + } + if !testutil.CheckTestServer(t, baseURL) { + return + } + client := kernel.NewClient( + option.WithBaseURL(baseURL), + option.WithAPIKey("My API Key"), + ) + _, err := client.Agents.Auth.Runs.Discover( + context.TODO(), + "run_id", + kernel.AgentAuthRunDiscoverParams{}, + ) + if err != nil { + var apierr *kernel.Error + if errors.As(err, &apierr) { + t.Log(string(apierr.DumpRequest(true))) + } + t.Fatalf("err should be nil: %s", err.Error()) + } +} + +func TestAgentAuthRunExchange(t *testing.T) { + t.Skip("Prism tests are disabled") + baseURL := "http://localhost:4010" + if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok { + baseURL = envURL + } + if !testutil.CheckTestServer(t, baseURL) { + return + } + client := kernel.NewClient( + option.WithBaseURL(baseURL), + option.WithAPIKey("My API Key"), + ) + _, err := client.Agents.Auth.Runs.Exchange( + context.TODO(), + "run_id", + kernel.AgentAuthRunExchangeParams{ + Code: "otp_abc123xyz", + }, + ) + if err != nil { + var apierr *kernel.Error + if errors.As(err, &apierr) { + t.Log(string(apierr.DumpRequest(true))) + } + t.Fatalf("err should be nil: %s", err.Error()) + } +} + +func TestAgentAuthRunSubmit(t *testing.T) { + t.Skip("Prism tests are disabled") + baseURL := "http://localhost:4010" + if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok { + baseURL = envURL + } + if !testutil.CheckTestServer(t, baseURL) { + return + } + client := kernel.NewClient( + option.WithBaseURL(baseURL), + option.WithAPIKey("My API Key"), + ) + _, err := client.Agents.Auth.Runs.Submit( + context.TODO(), + "run_id", + kernel.AgentAuthRunSubmitParams{ + FieldValues: map[string]string{ + "email": "user@example.com", + "password": "********", + }, + }, + ) + if err != nil { + var apierr *kernel.Error + if errors.As(err, &apierr) { + t.Log(string(apierr.DumpRequest(true))) + } + t.Fatalf("err should be nil: %s", err.Error()) + } +} diff --git a/api.md b/api.md index 86298d1..0ca64d4 100644 --- a/api.md +++ b/api.md @@ -218,3 +218,32 @@ Methods: - client.Extensions.Download(ctx context.Context, idOrName string) (http.Response, error) - client.Extensions.DownloadFromChromeStore(ctx context.Context, query kernel.ExtensionDownloadFromChromeStoreParams) (http.Response, error) - client.Extensions.Upload(ctx context.Context, body kernel.ExtensionUploadParams) (kernel.ExtensionUploadResponse, error) + +# Agents + +## Auth + +Response Types: + +- kernel.AgentAuthDiscoverResponse +- kernel.AgentAuthRunResponse +- kernel.AgentAuthStartResponse +- kernel.AgentAuthSubmitResponse +- kernel.DiscoveredField + +Methods: + +- client.Agents.Auth.Start(ctx context.Context, body kernel.AgentAuthStartParams) (kernel.AgentAuthStartResponse, error) + +### Runs + +Response Types: + +- kernel.AgentAuthRunExchangeResponse + +Methods: + +- client.Agents.Auth.Runs.Get(ctx context.Context, runID string) (kernel.AgentAuthRunResponse, error) +- client.Agents.Auth.Runs.Discover(ctx context.Context, runID string, body kernel.AgentAuthRunDiscoverParams) (kernel.AgentAuthDiscoverResponse, error) +- client.Agents.Auth.Runs.Exchange(ctx context.Context, runID string, body kernel.AgentAuthRunExchangeParams) (kernel.AgentAuthRunExchangeResponse, error) +- client.Agents.Auth.Runs.Submit(ctx context.Context, runID string, body kernel.AgentAuthRunSubmitParams) (kernel.AgentAuthSubmitResponse, error) diff --git a/client.go b/client.go index 0e27e7b..39e85fe 100644 --- a/client.go +++ b/client.go @@ -24,6 +24,7 @@ type Client struct { Profiles ProfileService Proxies ProxyService Extensions ExtensionService + Agents AgentService } // DefaultClientOptions read from the environment (KERNEL_API_KEY, @@ -55,6 +56,7 @@ func NewClient(opts ...option.RequestOption) (r Client) { r.Profiles = NewProfileService(opts...) r.Proxies = NewProxyService(opts...) r.Extensions = NewExtensionService(opts...) + r.Agents = NewAgentService(opts...) return } From 79d7b82bd4312be0d1a19c60ce20c0380487783f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 25 Nov 2025 18:13:13 +0000 Subject: [PATCH 2/7] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index bdbede3..fa4f900 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 71 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-bb3f37e55117a56e7a4208bd646d3a68adeb651ced8531e13fbfc1fc9dcb05a4.yml -openapi_spec_hash: 7303ce8ce3130e16a6a5c2bb49e43e9b +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-ae9ed0d949aa701dd3873e49080fe923404a8869ffcb69b7c912a3f244d0236d.yml +openapi_spec_hash: 654d6e13a8bfe2103b373c668f43b33d config_hash: be146470fb2d4583b6533859f0fa48f5 From cc2179535a7cc9a8059222b14fc68e085ac6acbd Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 26 Nov 2025 00:27:18 +0000 Subject: [PATCH 3/7] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index fa4f900..541095f 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 71 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-ae9ed0d949aa701dd3873e49080fe923404a8869ffcb69b7c912a3f244d0236d.yml -openapi_spec_hash: 654d6e13a8bfe2103b373c668f43b33d +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-2a63841293bec1bb651c5a24a95b2e9b5c07851dec1164de1aa2f87dafc51046.yml +openapi_spec_hash: d0bb3ca22c10b79758d503f717dd8e2f config_hash: be146470fb2d4583b6533859f0fa48f5 From 2445191106e2a61cea4d72783281e76505dca890 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 2 Dec 2025 01:22:03 +0000 Subject: [PATCH 4/7] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 541095f..a6b35ee 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 71 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-2a63841293bec1bb651c5a24a95b2e9b5c07851dec1164de1aa2f87dafc51046.yml -openapi_spec_hash: d0bb3ca22c10b79758d503f717dd8e2f +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-92b20a9e4650f645d3bb23b64f4ae72287bb41d3922ff1371426a91879186362.yml +openapi_spec_hash: a3c5f41d36734c980bc5313ee60b97cf config_hash: be146470fb2d4583b6533859f0fa48f5 From 69dcf3471d1b8b7b6fb8210da6d9c0b42ab1f1e7 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 2 Dec 2025 20:24:19 +0000 Subject: [PATCH 5/7] feat: Browser pools sdk release --- .stats.yml | 8 +- agent.go | 28 --- agentauth.go | 267 ------------------------ agentauth_test.go | 44 ---- agentauthrun.go | 150 -------------- agentauthrun_test.go | 125 ------------ aliases.go | 50 +++++ api.md | 46 +++-- browser.go | 180 +---------------- browser_test.go | 7 +- browserpool.go | 471 +++++++++++++++++++++++++++++++++++++++++++ browserpool_test.go | 279 +++++++++++++++++++++++++ client.go | 20 +- shared/shared.go | 182 +++++++++++++++++ 14 files changed, 1033 insertions(+), 824 deletions(-) delete mode 100644 agent.go delete mode 100644 agentauth.go delete mode 100644 agentauth_test.go delete mode 100644 agentauthrun.go delete mode 100644 agentauthrun_test.go create mode 100644 browserpool.go create mode 100644 browserpool_test.go diff --git a/.stats.yml b/.stats.yml index a6b35ee..c32f96d 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 71 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-92b20a9e4650f645d3bb23b64f4ae72287bb41d3922ff1371426a91879186362.yml -openapi_spec_hash: a3c5f41d36734c980bc5313ee60b97cf -config_hash: be146470fb2d4583b6533859f0fa48f5 +configured_endpoints: 74 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-340c8f009b71922347d4c238c8715cd752c8965abfa12cbb1ffabe35edc338a8.yml +openapi_spec_hash: efc13ab03ef89cc07333db8ab5345f31 +config_hash: a4124701ae0a474e580d7416adbcfb00 diff --git a/agent.go b/agent.go deleted file mode 100644 index d91fa4e..0000000 --- a/agent.go +++ /dev/null @@ -1,28 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -package kernel - -import ( - "github.com/onkernel/kernel-go-sdk/option" -) - -// AgentService contains methods and other services that help with interacting with -// the kernel API. -// -// Note, unlike clients, this service does not read variables from the environment -// automatically. You should not instantiate this service directly, and instead use -// the [NewAgentService] method instead. -type AgentService struct { - Options []option.RequestOption - Auth AgentAuthService -} - -// NewAgentService generates a new service that applies the given options to each -// request. These options are applied after the parent client's options (if there -// is one), and before any request-specific options. -func NewAgentService(opts ...option.RequestOption) (r AgentService) { - r = AgentService{} - r.Options = opts - r.Auth = NewAgentAuthService(opts...) - return -} diff --git a/agentauth.go b/agentauth.go deleted file mode 100644 index 373ecb6..0000000 --- a/agentauth.go +++ /dev/null @@ -1,267 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -package kernel - -import ( - "context" - "net/http" - "slices" - "time" - - "github.com/onkernel/kernel-go-sdk/internal/apijson" - "github.com/onkernel/kernel-go-sdk/internal/requestconfig" - "github.com/onkernel/kernel-go-sdk/option" - "github.com/onkernel/kernel-go-sdk/packages/param" - "github.com/onkernel/kernel-go-sdk/packages/respjson" -) - -// AgentAuthService contains methods and other services that help with interacting -// with the kernel API. -// -// Note, unlike clients, this service does not read variables from the environment -// automatically. You should not instantiate this service directly, and instead use -// the [NewAgentAuthService] method instead. -type AgentAuthService struct { - Options []option.RequestOption - Runs AgentAuthRunService -} - -// NewAgentAuthService generates a new service that applies the given options to -// each request. These options are applied after the parent client's options (if -// there is one), and before any request-specific options. -func NewAgentAuthService(opts ...option.RequestOption) (r AgentAuthService) { - r = AgentAuthService{} - r.Options = opts - r.Runs = NewAgentAuthRunService(opts...) - return -} - -// Creates a browser session and returns a handoff code for the hosted flow. Uses -// standard API key or JWT authentication (not the JWT returned by the exchange -// endpoint). -func (r *AgentAuthService) Start(ctx context.Context, body AgentAuthStartParams, opts ...option.RequestOption) (res *AgentAuthStartResponse, err error) { - opts = slices.Concat(r.Options, opts) - path := "agents/auth/start" - err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, body, &res, opts...) - return -} - -// Response from discover endpoint matching AuthBlueprint schema -type AgentAuthDiscoverResponse struct { - // Whether discovery succeeded - Success bool `json:"success,required"` - // Error message if discovery failed - ErrorMessage string `json:"error_message"` - // Discovered form fields (present when success is true) - Fields []DiscoveredField `json:"fields"` - // Whether user is already logged in - LoggedIn bool `json:"logged_in"` - // URL of the discovered login page - LoginURL string `json:"login_url" format:"uri"` - // Title of the login page - PageTitle string `json:"page_title"` - // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. - JSON struct { - Success respjson.Field - ErrorMessage respjson.Field - Fields respjson.Field - LoggedIn respjson.Field - LoginURL respjson.Field - PageTitle respjson.Field - ExtraFields map[string]respjson.Field - raw string - } `json:"-"` -} - -// Returns the unmodified JSON received from the API -func (r AgentAuthDiscoverResponse) RawJSON() string { return r.JSON.raw } -func (r *AgentAuthDiscoverResponse) UnmarshalJSON(data []byte) error { - return apijson.UnmarshalRoot(data, r) -} - -// Response from get run endpoint -type AgentAuthRunResponse struct { - // App name (org name at time of run creation) - AppName string `json:"app_name,required"` - // When the handoff code expires - ExpiresAt time.Time `json:"expires_at,required" format:"date-time"` - // Run status - // - // Any of "ACTIVE", "ENDED", "EXPIRED", "CANCELED". - Status AgentAuthRunResponseStatus `json:"status,required"` - // Target domain for authentication - TargetDomain string `json:"target_domain,required"` - // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. - JSON struct { - AppName respjson.Field - ExpiresAt respjson.Field - Status respjson.Field - TargetDomain respjson.Field - ExtraFields map[string]respjson.Field - raw string - } `json:"-"` -} - -// Returns the unmodified JSON received from the API -func (r AgentAuthRunResponse) RawJSON() string { return r.JSON.raw } -func (r *AgentAuthRunResponse) UnmarshalJSON(data []byte) error { - return apijson.UnmarshalRoot(data, r) -} - -// Run status -type AgentAuthRunResponseStatus string - -const ( - AgentAuthRunResponseStatusActive AgentAuthRunResponseStatus = "ACTIVE" - AgentAuthRunResponseStatusEnded AgentAuthRunResponseStatus = "ENDED" - AgentAuthRunResponseStatusExpired AgentAuthRunResponseStatus = "EXPIRED" - AgentAuthRunResponseStatusCanceled AgentAuthRunResponseStatus = "CANCELED" -) - -// Response from starting an agent authentication run -type AgentAuthStartResponse struct { - // When the handoff code expires - ExpiresAt time.Time `json:"expires_at,required" format:"date-time"` - // One-time code for handoff - HandoffCode string `json:"handoff_code,required"` - // URL to redirect user to - HostedURL string `json:"hosted_url,required" format:"uri"` - // Unique identifier for the run - RunID string `json:"run_id,required"` - // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. - JSON struct { - ExpiresAt respjson.Field - HandoffCode respjson.Field - HostedURL respjson.Field - RunID respjson.Field - ExtraFields map[string]respjson.Field - raw string - } `json:"-"` -} - -// Returns the unmodified JSON received from the API -func (r AgentAuthStartResponse) RawJSON() string { return r.JSON.raw } -func (r *AgentAuthStartResponse) UnmarshalJSON(data []byte) error { - return apijson.UnmarshalRoot(data, r) -} - -// Response from submit endpoint matching SubmitResult schema -type AgentAuthSubmitResponse struct { - // Whether submission succeeded - Success bool `json:"success,required"` - // Additional fields needed (e.g., OTP) - present when needs_additional_auth is - // true - AdditionalFields []DiscoveredField `json:"additional_fields"` - // App name (only present when logged_in is true) - AppName string `json:"app_name"` - // Error message if submission failed - ErrorMessage string `json:"error_message"` - // Whether user is now logged in - LoggedIn bool `json:"logged_in"` - // Whether additional authentication fields are needed - NeedsAdditionalAuth bool `json:"needs_additional_auth"` - // Target domain (only present when logged_in is true) - TargetDomain string `json:"target_domain"` - // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. - JSON struct { - Success respjson.Field - AdditionalFields respjson.Field - AppName respjson.Field - ErrorMessage respjson.Field - LoggedIn respjson.Field - NeedsAdditionalAuth respjson.Field - TargetDomain respjson.Field - ExtraFields map[string]respjson.Field - raw string - } `json:"-"` -} - -// Returns the unmodified JSON received from the API -func (r AgentAuthSubmitResponse) RawJSON() string { return r.JSON.raw } -func (r *AgentAuthSubmitResponse) UnmarshalJSON(data []byte) error { - return apijson.UnmarshalRoot(data, r) -} - -// A discovered form field -type DiscoveredField struct { - // Field label - Label string `json:"label,required"` - // Field name - Name string `json:"name,required"` - // CSS selector for the field - Selector string `json:"selector,required"` - // Field type - // - // Any of "text", "email", "password", "tel", "number", "url", "code", "checkbox". - Type DiscoveredFieldType `json:"type,required"` - // Field placeholder - Placeholder string `json:"placeholder"` - // Whether field is required - Required bool `json:"required"` - // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. - JSON struct { - Label respjson.Field - Name respjson.Field - Selector respjson.Field - Type respjson.Field - Placeholder respjson.Field - Required respjson.Field - ExtraFields map[string]respjson.Field - raw string - } `json:"-"` -} - -// Returns the unmodified JSON received from the API -func (r DiscoveredField) RawJSON() string { return r.JSON.raw } -func (r *DiscoveredField) UnmarshalJSON(data []byte) error { - return apijson.UnmarshalRoot(data, r) -} - -// Field type -type DiscoveredFieldType string - -const ( - DiscoveredFieldTypeText DiscoveredFieldType = "text" - DiscoveredFieldTypeEmail DiscoveredFieldType = "email" - DiscoveredFieldTypePassword DiscoveredFieldType = "password" - DiscoveredFieldTypeTel DiscoveredFieldType = "tel" - DiscoveredFieldTypeNumber DiscoveredFieldType = "number" - DiscoveredFieldTypeURL DiscoveredFieldType = "url" - DiscoveredFieldTypeCode DiscoveredFieldType = "code" - DiscoveredFieldTypeCheckbox DiscoveredFieldType = "checkbox" -) - -type AgentAuthStartParams struct { - // Name of the profile to use for this flow - ProfileName string `json:"profile_name,required"` - // Target domain for authentication - TargetDomain string `json:"target_domain,required"` - // Optional logo URL for the application - AppLogoURL param.Opt[string] `json:"app_logo_url,omitzero" format:"uri"` - // Optional proxy configuration - Proxy AgentAuthStartParamsProxy `json:"proxy,omitzero"` - paramObj -} - -func (r AgentAuthStartParams) MarshalJSON() (data []byte, err error) { - type shadow AgentAuthStartParams - return param.MarshalObject(r, (*shadow)(&r)) -} -func (r *AgentAuthStartParams) UnmarshalJSON(data []byte) error { - return apijson.UnmarshalRoot(data, r) -} - -// Optional proxy configuration -type AgentAuthStartParamsProxy struct { - // ID of the proxy to use - ProxyID param.Opt[string] `json:"proxy_id,omitzero"` - paramObj -} - -func (r AgentAuthStartParamsProxy) MarshalJSON() (data []byte, err error) { - type shadow AgentAuthStartParamsProxy - return param.MarshalObject(r, (*shadow)(&r)) -} -func (r *AgentAuthStartParamsProxy) UnmarshalJSON(data []byte) error { - return apijson.UnmarshalRoot(data, r) -} diff --git a/agentauth_test.go b/agentauth_test.go deleted file mode 100644 index ab734e1..0000000 --- a/agentauth_test.go +++ /dev/null @@ -1,44 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -package kernel_test - -import ( - "context" - "errors" - "os" - "testing" - - "github.com/onkernel/kernel-go-sdk" - "github.com/onkernel/kernel-go-sdk/internal/testutil" - "github.com/onkernel/kernel-go-sdk/option" -) - -func TestAgentAuthStartWithOptionalParams(t *testing.T) { - t.Skip("Prism tests are disabled") - baseURL := "http://localhost:4010" - if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok { - baseURL = envURL - } - if !testutil.CheckTestServer(t, baseURL) { - return - } - client := kernel.NewClient( - option.WithBaseURL(baseURL), - option.WithAPIKey("My API Key"), - ) - _, err := client.Agents.Auth.Start(context.TODO(), kernel.AgentAuthStartParams{ - ProfileName: "auth-abc123", - TargetDomain: "doordash.com", - AppLogoURL: kernel.String("https://example.com/logo.png"), - Proxy: kernel.AgentAuthStartParamsProxy{ - ProxyID: kernel.String("proxy_id"), - }, - }) - if err != nil { - var apierr *kernel.Error - if errors.As(err, &apierr) { - t.Log(string(apierr.DumpRequest(true))) - } - t.Fatalf("err should be nil: %s", err.Error()) - } -} diff --git a/agentauthrun.go b/agentauthrun.go deleted file mode 100644 index da62724..0000000 --- a/agentauthrun.go +++ /dev/null @@ -1,150 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -package kernel - -import ( - "context" - "errors" - "fmt" - "net/http" - "slices" - - "github.com/onkernel/kernel-go-sdk/internal/apijson" - "github.com/onkernel/kernel-go-sdk/internal/requestconfig" - "github.com/onkernel/kernel-go-sdk/option" - "github.com/onkernel/kernel-go-sdk/packages/param" - "github.com/onkernel/kernel-go-sdk/packages/respjson" -) - -// AgentAuthRunService contains methods and other services that help with -// interacting with the kernel API. -// -// Note, unlike clients, this service does not read variables from the environment -// automatically. You should not instantiate this service directly, and instead use -// the [NewAgentAuthRunService] method instead. -type AgentAuthRunService struct { - Options []option.RequestOption -} - -// NewAgentAuthRunService generates a new service that applies the given options to -// each request. These options are applied after the parent client's options (if -// there is one), and before any request-specific options. -func NewAgentAuthRunService(opts ...option.RequestOption) (r AgentAuthRunService) { - r = AgentAuthRunService{} - r.Options = opts - return -} - -// Returns run details including app_name and target_domain. Uses the JWT returned -// by the exchange endpoint, or standard API key or JWT authentication. -func (r *AgentAuthRunService) Get(ctx context.Context, runID string, opts ...option.RequestOption) (res *AgentAuthRunResponse, err error) { - opts = slices.Concat(r.Options, opts) - if runID == "" { - err = errors.New("missing required run_id parameter") - return - } - path := fmt.Sprintf("agents/auth/runs/%s", runID) - err = requestconfig.ExecuteNewRequest(ctx, http.MethodGet, path, nil, &res, opts...) - return -} - -// Inspects the target site to detect logged-in state or discover required fields. -// Returns 200 with success: true when fields are found, or 4xx/5xx for failures. -// Requires the JWT returned by the exchange endpoint. -func (r *AgentAuthRunService) Discover(ctx context.Context, runID string, body AgentAuthRunDiscoverParams, opts ...option.RequestOption) (res *AgentAuthDiscoverResponse, err error) { - opts = slices.Concat(r.Options, opts) - if runID == "" { - err = errors.New("missing required run_id parameter") - return - } - path := fmt.Sprintf("agents/auth/runs/%s/discover", runID) - err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, body, &res, opts...) - return -} - -// Validates the handoff code and returns a JWT token for subsequent requests. No -// authentication required (the handoff code serves as the credential). -func (r *AgentAuthRunService) Exchange(ctx context.Context, runID string, body AgentAuthRunExchangeParams, opts ...option.RequestOption) (res *AgentAuthRunExchangeResponse, err error) { - opts = slices.Concat(r.Options, opts) - if runID == "" { - err = errors.New("missing required run_id parameter") - return - } - path := fmt.Sprintf("agents/auth/runs/%s/exchange", runID) - err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, body, &res, opts...) - return -} - -// Submits field values for the discovered login form and may return additional -// auth fields or success. Requires the JWT returned by the exchange endpoint. -func (r *AgentAuthRunService) Submit(ctx context.Context, runID string, body AgentAuthRunSubmitParams, opts ...option.RequestOption) (res *AgentAuthSubmitResponse, err error) { - opts = slices.Concat(r.Options, opts) - if runID == "" { - err = errors.New("missing required run_id parameter") - return - } - path := fmt.Sprintf("agents/auth/runs/%s/submit", runID) - err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, body, &res, opts...) - return -} - -// Response from exchange endpoint -type AgentAuthRunExchangeResponse struct { - // JWT token with run_id claim (30 minute TTL) - Jwt string `json:"jwt,required"` - // Run ID - RunID string `json:"run_id,required"` - // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. - JSON struct { - Jwt respjson.Field - RunID respjson.Field - ExtraFields map[string]respjson.Field - raw string - } `json:"-"` -} - -// Returns the unmodified JSON received from the API -func (r AgentAuthRunExchangeResponse) RawJSON() string { return r.JSON.raw } -func (r *AgentAuthRunExchangeResponse) UnmarshalJSON(data []byte) error { - return apijson.UnmarshalRoot(data, r) -} - -type AgentAuthRunDiscoverParams struct { - paramObj -} - -func (r AgentAuthRunDiscoverParams) MarshalJSON() (data []byte, err error) { - type shadow AgentAuthRunDiscoverParams - return param.MarshalObject(r, (*shadow)(&r)) -} -func (r *AgentAuthRunDiscoverParams) UnmarshalJSON(data []byte) error { - return apijson.UnmarshalRoot(data, r) -} - -type AgentAuthRunExchangeParams struct { - // Handoff code from start endpoint - Code string `json:"code,required"` - paramObj -} - -func (r AgentAuthRunExchangeParams) MarshalJSON() (data []byte, err error) { - type shadow AgentAuthRunExchangeParams - return param.MarshalObject(r, (*shadow)(&r)) -} -func (r *AgentAuthRunExchangeParams) UnmarshalJSON(data []byte) error { - return apijson.UnmarshalRoot(data, r) -} - -type AgentAuthRunSubmitParams struct { - // Values for the discovered login fields - FieldValues map[string]string `json:"field_values,omitzero,required"` - paramObj -} - -func (r AgentAuthRunSubmitParams) MarshalJSON() (data []byte, err error) { - type shadow AgentAuthRunSubmitParams - return param.MarshalObject(r, (*shadow)(&r)) -} -func (r *AgentAuthRunSubmitParams) UnmarshalJSON(data []byte) error { - return apijson.UnmarshalRoot(data, r) -} diff --git a/agentauthrun_test.go b/agentauthrun_test.go deleted file mode 100644 index b0b7603..0000000 --- a/agentauthrun_test.go +++ /dev/null @@ -1,125 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -package kernel_test - -import ( - "context" - "errors" - "os" - "testing" - - "github.com/onkernel/kernel-go-sdk" - "github.com/onkernel/kernel-go-sdk/internal/testutil" - "github.com/onkernel/kernel-go-sdk/option" -) - -func TestAgentAuthRunGet(t *testing.T) { - t.Skip("Prism tests are disabled") - baseURL := "http://localhost:4010" - if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok { - baseURL = envURL - } - if !testutil.CheckTestServer(t, baseURL) { - return - } - client := kernel.NewClient( - option.WithBaseURL(baseURL), - option.WithAPIKey("My API Key"), - ) - _, err := client.Agents.Auth.Runs.Get(context.TODO(), "run_id") - if err != nil { - var apierr *kernel.Error - if errors.As(err, &apierr) { - t.Log(string(apierr.DumpRequest(true))) - } - t.Fatalf("err should be nil: %s", err.Error()) - } -} - -func TestAgentAuthRunDiscover(t *testing.T) { - t.Skip("Prism tests are disabled") - baseURL := "http://localhost:4010" - if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok { - baseURL = envURL - } - if !testutil.CheckTestServer(t, baseURL) { - return - } - client := kernel.NewClient( - option.WithBaseURL(baseURL), - option.WithAPIKey("My API Key"), - ) - _, err := client.Agents.Auth.Runs.Discover( - context.TODO(), - "run_id", - kernel.AgentAuthRunDiscoverParams{}, - ) - if err != nil { - var apierr *kernel.Error - if errors.As(err, &apierr) { - t.Log(string(apierr.DumpRequest(true))) - } - t.Fatalf("err should be nil: %s", err.Error()) - } -} - -func TestAgentAuthRunExchange(t *testing.T) { - t.Skip("Prism tests are disabled") - baseURL := "http://localhost:4010" - if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok { - baseURL = envURL - } - if !testutil.CheckTestServer(t, baseURL) { - return - } - client := kernel.NewClient( - option.WithBaseURL(baseURL), - option.WithAPIKey("My API Key"), - ) - _, err := client.Agents.Auth.Runs.Exchange( - context.TODO(), - "run_id", - kernel.AgentAuthRunExchangeParams{ - Code: "otp_abc123xyz", - }, - ) - if err != nil { - var apierr *kernel.Error - if errors.As(err, &apierr) { - t.Log(string(apierr.DumpRequest(true))) - } - t.Fatalf("err should be nil: %s", err.Error()) - } -} - -func TestAgentAuthRunSubmit(t *testing.T) { - t.Skip("Prism tests are disabled") - baseURL := "http://localhost:4010" - if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok { - baseURL = envURL - } - if !testutil.CheckTestServer(t, baseURL) { - return - } - client := kernel.NewClient( - option.WithBaseURL(baseURL), - option.WithAPIKey("My API Key"), - ) - _, err := client.Agents.Auth.Runs.Submit( - context.TODO(), - "run_id", - kernel.AgentAuthRunSubmitParams{ - FieldValues: map[string]string{ - "email": "user@example.com", - "password": "********", - }, - }, - ) - if err != nil { - var apierr *kernel.Error - if errors.As(err, &apierr) { - t.Log(string(apierr.DumpRequest(true))) - } - t.Fatalf("err should be nil: %s", err.Error()) - } -} diff --git a/aliases.go b/aliases.go index 2621045..d7a4818 100644 --- a/aliases.go +++ b/aliases.go @@ -21,6 +21,56 @@ type Error = apierror.Error // This is an alias to an internal type. type AppAction = shared.AppAction +// Extension selection for the browser session. Provide either id or name of an +// extension uploaded to Kernel. +// +// This is an alias to an internal type. +type BrowserExtension = shared.BrowserExtension + +// Extension selection for the browser session. Provide either id or name of an +// extension uploaded to Kernel. +// +// 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. +// +// This is an alias to an internal type. +type BrowserProfileParam = shared.BrowserProfileParam + +// Initial browser window size in pixels with optional refresh rate. If omitted, +// image defaults apply (commonly 1024x768@60). Only specific viewport +// configurations are supported. The server will reject unsupported combinations. +// Supported resolutions are: 2560x1440@10, 1920x1080@25, 1920x1200@25, +// 1440x900@25, 1024x768@60, 1200x800@60 If refresh_rate is not provided, it will +// be automatically determined from the width and height if they match a supported +// configuration exactly. Note: Higher resolutions may affect the responsiveness of +// live view browser +// +// This is an alias to an internal type. +type BrowserViewport = shared.BrowserViewport + +// Initial browser window size in pixels with optional refresh rate. If omitted, +// image defaults apply (commonly 1024x768@60). Only specific viewport +// configurations are supported. The server will reject unsupported combinations. +// Supported resolutions are: 2560x1440@10, 1920x1080@25, 1920x1200@25, +// 1440x900@25, 1024x768@60, 1200x800@60 If refresh_rate is not provided, it will +// be automatically determined from the width and height if they match a supported +// configuration exactly. Note: Higher resolutions may affect the responsiveness of +// live view browser +// +// This is an alias to an internal type. +type BrowserViewportParam = shared.BrowserViewportParam + // This is an alias to an internal type. type ErrorDetail = shared.ErrorDetail diff --git a/api.md b/api.md index 0ca64d4..aa0d086 100644 --- a/api.md +++ b/api.md @@ -1,6 +1,15 @@ +# Shared Params Types + +- shared.BrowserExtensionParam +- shared.BrowserProfileParam +- shared.BrowserViewportParam + # Shared Response Types - shared.AppAction +- shared.BrowserExtension +- shared.BrowserProfile +- shared.BrowserViewport - shared.ErrorDetail - shared.ErrorEvent - shared.ErrorModel @@ -219,31 +228,28 @@ Methods: - client.Extensions.DownloadFromChromeStore(ctx context.Context, query kernel.ExtensionDownloadFromChromeStoreParams) (http.Response, error) - client.Extensions.Upload(ctx context.Context, body kernel.ExtensionUploadParams) (kernel.ExtensionUploadResponse, error) -# Agents - -## Auth - -Response Types: +# BrowserPools -- kernel.AgentAuthDiscoverResponse -- kernel.AgentAuthRunResponse -- kernel.AgentAuthStartResponse -- kernel.AgentAuthSubmitResponse -- kernel.DiscoveredField - -Methods: - -- client.Agents.Auth.Start(ctx context.Context, body kernel.AgentAuthStartParams) (kernel.AgentAuthStartResponse, error) +Params Types: -### Runs +- kernel.BrowserPoolAcquireRequestParam +- kernel.BrowserPoolReleaseRequestParam +- kernel.BrowserPoolRequestParam +- kernel.BrowserPoolUpdateRequestParam Response Types: -- kernel.AgentAuthRunExchangeResponse +- kernel.BrowserPool +- kernel.BrowserPoolRequest +- kernel.BrowserPoolAcquireResponse Methods: -- client.Agents.Auth.Runs.Get(ctx context.Context, runID string) (kernel.AgentAuthRunResponse, error) -- client.Agents.Auth.Runs.Discover(ctx context.Context, runID string, body kernel.AgentAuthRunDiscoverParams) (kernel.AgentAuthDiscoverResponse, error) -- client.Agents.Auth.Runs.Exchange(ctx context.Context, runID string, body kernel.AgentAuthRunExchangeParams) (kernel.AgentAuthRunExchangeResponse, error) -- client.Agents.Auth.Runs.Submit(ctx context.Context, runID string, body kernel.AgentAuthRunSubmitParams) (kernel.AgentAuthSubmitResponse, error) +- client.BrowserPools.New(ctx context.Context, body kernel.BrowserPoolNewParams) (kernel.BrowserPool, error) +- client.BrowserPools.Get(ctx context.Context, idOrName string) (kernel.BrowserPool, error) +- client.BrowserPools.Update(ctx context.Context, idOrName string, body kernel.BrowserPoolUpdateParams) (kernel.BrowserPool, error) +- client.BrowserPools.List(ctx context.Context) ([]kernel.BrowserPool, error) +- client.BrowserPools.Delete(ctx context.Context, idOrName string, body kernel.BrowserPoolDeleteParams) error +- client.BrowserPools.Acquire(ctx context.Context, idOrName string, body kernel.BrowserPoolAcquireParams) (kernel.BrowserPoolAcquireResponse, error) +- client.BrowserPools.Flush(ctx context.Context, idOrName string) error +- client.BrowserPools.Release(ctx context.Context, idOrName string, body kernel.BrowserPoolReleaseParams) error diff --git a/browser.go b/browser.go index a172b4d..fc9f55c 100644 --- a/browser.go +++ b/browser.go @@ -23,6 +23,7 @@ import ( "github.com/onkernel/kernel-go-sdk/packages/pagination" "github.com/onkernel/kernel-go-sdk/packages/param" "github.com/onkernel/kernel-go-sdk/packages/respjson" + "github.com/onkernel/kernel-go-sdk/shared" ) // BrowserService contains methods and other services that help with interacting @@ -245,7 +246,7 @@ type BrowserNewResponse struct { // be automatically determined from the width and height if they match a supported // configuration exactly. Note: Higher resolutions may affect the responsiveness of // live view browser - Viewport BrowserNewResponseViewport `json:"viewport"` + Viewport shared.BrowserViewport `json:"viewport"` // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. JSON struct { CdpWsURL respjson.Field @@ -272,38 +273,6 @@ func (r *BrowserNewResponse) UnmarshalJSON(data []byte) error { return apijson.UnmarshalRoot(data, r) } -// Initial browser window size in pixels with optional refresh rate. If omitted, -// image defaults apply (commonly 1024x768@60). Only specific viewport -// configurations are supported. The server will reject unsupported combinations. -// Supported resolutions are: 2560x1440@10, 1920x1080@25, 1920x1200@25, -// 1440x900@25, 1024x768@60, 1200x800@60 If refresh_rate is not provided, it will -// be automatically determined from the width and height if they match a supported -// configuration exactly. Note: Higher resolutions may affect the responsiveness of -// live view browser -type BrowserNewResponseViewport struct { - // Browser window height in pixels. - Height int64 `json:"height,required"` - // Browser window width in pixels. - Width int64 `json:"width,required"` - // Display refresh rate in Hz. If omitted, automatically determined from width and - // height. - RefreshRate int64 `json:"refresh_rate"` - // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. - JSON struct { - Height respjson.Field - Width respjson.Field - RefreshRate respjson.Field - ExtraFields map[string]respjson.Field - raw string - } `json:"-"` -} - -// Returns the unmodified JSON received from the API -func (r BrowserNewResponseViewport) RawJSON() string { return r.JSON.raw } -func (r *BrowserNewResponseViewport) UnmarshalJSON(data []byte) error { - return apijson.UnmarshalRoot(data, r) -} - type BrowserGetResponse struct { // Websocket URL for Chrome DevTools Protocol connections to the browser session CdpWsURL string `json:"cdp_ws_url,required"` @@ -338,7 +307,7 @@ type BrowserGetResponse struct { // be automatically determined from the width and height if they match a supported // configuration exactly. Note: Higher resolutions may affect the responsiveness of // live view browser - Viewport BrowserGetResponseViewport `json:"viewport"` + Viewport shared.BrowserViewport `json:"viewport"` // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. JSON struct { CdpWsURL respjson.Field @@ -365,38 +334,6 @@ func (r *BrowserGetResponse) UnmarshalJSON(data []byte) error { return apijson.UnmarshalRoot(data, r) } -// Initial browser window size in pixels with optional refresh rate. If omitted, -// image defaults apply (commonly 1024x768@60). Only specific viewport -// configurations are supported. The server will reject unsupported combinations. -// Supported resolutions are: 2560x1440@10, 1920x1080@25, 1920x1200@25, -// 1440x900@25, 1024x768@60, 1200x800@60 If refresh_rate is not provided, it will -// be automatically determined from the width and height if they match a supported -// configuration exactly. Note: Higher resolutions may affect the responsiveness of -// live view browser -type BrowserGetResponseViewport struct { - // Browser window height in pixels. - Height int64 `json:"height,required"` - // Browser window width in pixels. - Width int64 `json:"width,required"` - // Display refresh rate in Hz. If omitted, automatically determined from width and - // height. - RefreshRate int64 `json:"refresh_rate"` - // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. - JSON struct { - Height respjson.Field - Width respjson.Field - RefreshRate respjson.Field - ExtraFields map[string]respjson.Field - raw string - } `json:"-"` -} - -// Returns the unmodified JSON received from the API -func (r BrowserGetResponseViewport) RawJSON() string { return r.JSON.raw } -func (r *BrowserGetResponseViewport) UnmarshalJSON(data []byte) error { - return apijson.UnmarshalRoot(data, r) -} - type BrowserListResponse struct { // Websocket URL for Chrome DevTools Protocol connections to the browser session CdpWsURL string `json:"cdp_ws_url,required"` @@ -431,7 +368,7 @@ type BrowserListResponse struct { // be automatically determined from the width and height if they match a supported // configuration exactly. Note: Higher resolutions may affect the responsiveness of // live view browser - Viewport BrowserListResponseViewport `json:"viewport"` + Viewport shared.BrowserViewport `json:"viewport"` // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. JSON struct { CdpWsURL respjson.Field @@ -458,38 +395,6 @@ func (r *BrowserListResponse) UnmarshalJSON(data []byte) error { return apijson.UnmarshalRoot(data, r) } -// Initial browser window size in pixels with optional refresh rate. If omitted, -// image defaults apply (commonly 1024x768@60). Only specific viewport -// configurations are supported. The server will reject unsupported combinations. -// Supported resolutions are: 2560x1440@10, 1920x1080@25, 1920x1200@25, -// 1440x900@25, 1024x768@60, 1200x800@60 If refresh_rate is not provided, it will -// be automatically determined from the width and height if they match a supported -// configuration exactly. Note: Higher resolutions may affect the responsiveness of -// live view browser -type BrowserListResponseViewport struct { - // Browser window height in pixels. - Height int64 `json:"height,required"` - // Browser window width in pixels. - Width int64 `json:"width,required"` - // Display refresh rate in Hz. If omitted, automatically determined from width and - // height. - RefreshRate int64 `json:"refresh_rate"` - // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. - JSON struct { - Height respjson.Field - Width respjson.Field - RefreshRate respjson.Field - ExtraFields map[string]respjson.Field - raw string - } `json:"-"` -} - -// Returns the unmodified JSON received from the API -func (r BrowserListResponseViewport) RawJSON() string { return r.JSON.raw } -func (r *BrowserListResponseViewport) UnmarshalJSON(data []byte) error { - return apijson.UnmarshalRoot(data, r) -} - type BrowserNewParams struct { // If true, launches the browser using a headless image (no VNC/GUI). Defaults to // false. @@ -513,13 +418,13 @@ type BrowserNewParams struct { // specified value. TimeoutSeconds param.Opt[int64] `json:"timeout_seconds,omitzero"` // List of browser extensions to load into the session. Provide each by id or name. - Extensions []BrowserNewParamsExtension `json:"extensions,omitzero"` + Extensions []shared.BrowserExtensionParam `json:"extensions,omitzero"` // Optional persistence configuration for the browser session. Persistence BrowserPersistenceParam `json:"persistence,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 BrowserNewParamsProfile `json:"profile,omitzero"` + Profile shared.BrowserProfileParam `json:"profile,omitzero"` // Initial browser window size in pixels with optional refresh rate. If omitted, // image defaults apply (commonly 1024x768@60). Only specific viewport // configurations are supported. The server will reject unsupported combinations. @@ -528,7 +433,7 @@ type BrowserNewParams struct { // be automatically determined from the width and height if they match a supported // configuration exactly. Note: Higher resolutions may affect the responsiveness of // live view browser - Viewport BrowserNewParamsViewport `json:"viewport,omitzero"` + Viewport shared.BrowserViewportParam `json:"viewport,omitzero"` paramObj } @@ -540,77 +445,6 @@ func (r *BrowserNewParams) UnmarshalJSON(data []byte) error { return apijson.UnmarshalRoot(data, r) } -// Extension selection for the browser session. Provide either id or name of an -// extension uploaded to Kernel. -type BrowserNewParamsExtension struct { - // Extension ID to load for this browser session - ID param.Opt[string] `json:"id,omitzero"` - // Extension name to load for this browser session (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 BrowserNewParamsExtension) MarshalJSON() (data []byte, err error) { - type shadow BrowserNewParamsExtension - return param.MarshalObject(r, (*shadow)(&r)) -} -func (r *BrowserNewParamsExtension) 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 BrowserNewParamsProfile struct { - // Profile ID to load for this browser session - ID param.Opt[string] `json:"id,omitzero"` - // Profile name to load for this browser session (instead of id). Must be 1-255 - // characters, using letters, numbers, dots, underscores, or hyphens. - Name param.Opt[string] `json:"name,omitzero"` - // If true, save changes made during the session back to the profile when the - // session ends. - SaveChanges param.Opt[bool] `json:"save_changes,omitzero"` - paramObj -} - -func (r BrowserNewParamsProfile) MarshalJSON() (data []byte, err error) { - type shadow BrowserNewParamsProfile - return param.MarshalObject(r, (*shadow)(&r)) -} -func (r *BrowserNewParamsProfile) UnmarshalJSON(data []byte) error { - return apijson.UnmarshalRoot(data, r) -} - -// Initial browser window size in pixels with optional refresh rate. If omitted, -// image defaults apply (commonly 1024x768@60). Only specific viewport -// configurations are supported. The server will reject unsupported combinations. -// Supported resolutions are: 2560x1440@10, 1920x1080@25, 1920x1200@25, -// 1440x900@25, 1024x768@60, 1200x800@60 If refresh_rate is not provided, it will -// be automatically determined from the width and height if they match a supported -// configuration exactly. Note: Higher resolutions may affect the responsiveness of -// live view browser -// -// The properties Height, Width are required. -type BrowserNewParamsViewport struct { - // Browser window height in pixels. - Height int64 `json:"height,required"` - // Browser window width in pixels. - Width int64 `json:"width,required"` - // Display refresh rate in Hz. If omitted, automatically determined from width and - // height. - RefreshRate param.Opt[int64] `json:"refresh_rate,omitzero"` - paramObj -} - -func (r BrowserNewParamsViewport) MarshalJSON() (data []byte, err error) { - type shadow BrowserNewParamsViewport - return param.MarshalObject(r, (*shadow)(&r)) -} -func (r *BrowserNewParamsViewport) UnmarshalJSON(data []byte) error { - return apijson.UnmarshalRoot(data, r) -} - type BrowserListParams struct { // When true, includes soft-deleted browser sessions in the results alongside // active sessions. diff --git a/browser_test.go b/browser_test.go index 96377fd..33292c5 100644 --- a/browser_test.go +++ b/browser_test.go @@ -13,6 +13,7 @@ import ( "github.com/onkernel/kernel-go-sdk" "github.com/onkernel/kernel-go-sdk/internal/testutil" "github.com/onkernel/kernel-go-sdk/option" + "github.com/onkernel/kernel-go-sdk/shared" ) func TestBrowserNewWithOptionalParams(t *testing.T) { @@ -29,7 +30,7 @@ func TestBrowserNewWithOptionalParams(t *testing.T) { option.WithAPIKey("My API Key"), ) _, err := client.Browsers.New(context.TODO(), kernel.BrowserNewParams{ - Extensions: []kernel.BrowserNewParamsExtension{{ + Extensions: []shared.BrowserExtensionParam{{ ID: kernel.String("id"), Name: kernel.String("name"), }}, @@ -39,7 +40,7 @@ func TestBrowserNewWithOptionalParams(t *testing.T) { Persistence: kernel.BrowserPersistenceParam{ ID: "my-awesome-browser-for-user-1234", }, - Profile: kernel.BrowserNewParamsProfile{ + Profile: shared.BrowserProfileParam{ ID: kernel.String("id"), Name: kernel.String("name"), SaveChanges: kernel.Bool(true), @@ -47,7 +48,7 @@ func TestBrowserNewWithOptionalParams(t *testing.T) { ProxyID: kernel.String("proxy_id"), Stealth: kernel.Bool(true), TimeoutSeconds: kernel.Int(10), - Viewport: kernel.BrowserNewParamsViewport{ + Viewport: shared.BrowserViewportParam{ Height: 800, Width: 1280, RefreshRate: kernel.Int(60), diff --git a/browserpool.go b/browserpool.go new file mode 100644 index 0000000..4001ea7 --- /dev/null +++ b/browserpool.go @@ -0,0 +1,471 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package kernel + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "net/http" + "slices" + "time" + + "github.com/onkernel/kernel-go-sdk/internal/apijson" + shimjson "github.com/onkernel/kernel-go-sdk/internal/encoding/json" + "github.com/onkernel/kernel-go-sdk/internal/requestconfig" + "github.com/onkernel/kernel-go-sdk/option" + "github.com/onkernel/kernel-go-sdk/packages/param" + "github.com/onkernel/kernel-go-sdk/packages/respjson" + "github.com/onkernel/kernel-go-sdk/shared" +) + +// BrowserPoolService contains methods and other services that help with +// interacting with the kernel API. +// +// Note, unlike clients, this service does not read variables from the environment +// automatically. You should not instantiate this service directly, and instead use +// the [NewBrowserPoolService] method instead. +type BrowserPoolService struct { + Options []option.RequestOption +} + +// NewBrowserPoolService generates a new service that applies the given options to +// each request. These options are applied after the parent client's options (if +// there is one), and before any request-specific options. +func NewBrowserPoolService(opts ...option.RequestOption) (r BrowserPoolService) { + r = BrowserPoolService{} + r.Options = opts + return +} + +// Create a new browser pool with the specified configuration and size. +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" + err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, body, &res, opts...) + return +} + +// Retrieve details for a single browser pool by its ID or name. +func (r *BrowserPoolService) Get(ctx context.Context, idOrName string, opts ...option.RequestOption) (res *BrowserPool, err error) { + opts = slices.Concat(r.Options, opts) + if idOrName == "" { + err = errors.New("missing required id_or_name parameter") + return + } + path := fmt.Sprintf("browser_pools/%s", idOrName) + err = requestconfig.ExecuteNewRequest(ctx, http.MethodGet, path, nil, &res, opts...) + return +} + +// Updates the configuration used to create browsers in the pool. +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 == "" { + err = errors.New("missing required id_or_name parameter") + return + } + path := fmt.Sprintf("browser_pools/%s", idOrName) + err = requestconfig.ExecuteNewRequest(ctx, http.MethodPatch, path, body, &res, opts...) + return +} + +// List browser pools owned by the caller's organization. +func (r *BrowserPoolService) List(ctx context.Context, opts ...option.RequestOption) (res *[]BrowserPool, err error) { + opts = slices.Concat(r.Options, opts) + path := "browser_pools" + err = requestconfig.ExecuteNewRequest(ctx, http.MethodGet, path, nil, &res, opts...) + return +} + +// Delete a browser pool and all browsers in it. By default, deletion is blocked if +// browsers are currently leased. Use force=true to terminate leased browsers. +func (r *BrowserPoolService) Delete(ctx context.Context, idOrName string, body BrowserPoolDeleteParams, opts ...option.RequestOption) (err error) { + opts = slices.Concat(r.Options, opts) + opts = append([]option.RequestOption{option.WithHeader("Accept", "*/*")}, opts...) + if idOrName == "" { + err = errors.New("missing required id_or_name parameter") + return + } + path := fmt.Sprintf("browser_pools/%s", idOrName) + err = requestconfig.ExecuteNewRequest(ctx, http.MethodDelete, path, body, nil, opts...) + return +} + +// Long-polling endpoint to acquire a browser from the pool. Returns immediately +// when a browser is available, or returns 204 No Content when the poll times out. +// The client should retry the request to continue waiting for a browser. The +// acquired browser will use the pool's timeout_seconds for its idle timeout. +func (r *BrowserPoolService) Acquire(ctx context.Context, idOrName string, body BrowserPoolAcquireParams, opts ...option.RequestOption) (res *BrowserPoolAcquireResponse, err error) { + opts = slices.Concat(r.Options, opts) + if idOrName == "" { + err = errors.New("missing required id_or_name parameter") + return + } + path := fmt.Sprintf("browser_pools/%s/acquire", idOrName) + err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, body, &res, opts...) + return +} + +// Destroys all idle browsers in the pool; leased browsers are not affected. +func (r *BrowserPoolService) Flush(ctx context.Context, idOrName string, opts ...option.RequestOption) (err error) { + opts = slices.Concat(r.Options, opts) + opts = append([]option.RequestOption{option.WithHeader("Accept", "*/*")}, opts...) + if idOrName == "" { + err = errors.New("missing required id_or_name parameter") + return + } + path := fmt.Sprintf("browser_pools/%s/flush", idOrName) + err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, nil, nil, opts...) + return +} + +// Release a browser back to the pool, optionally recreating the browser instance. +func (r *BrowserPoolService) Release(ctx context.Context, idOrName string, body BrowserPoolReleaseParams, opts ...option.RequestOption) (err error) { + opts = slices.Concat(r.Options, opts) + opts = append([]option.RequestOption{option.WithHeader("Accept", "*/*")}, opts...) + if idOrName == "" { + err = errors.New("missing required id_or_name parameter") + return + } + path := fmt.Sprintf("browser_pools/%s/release", idOrName) + err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, body, nil, opts...) + return +} + +// A browser pool containing multiple identically configured browsers. +type BrowserPool struct { + // Unique identifier for the browser pool + ID string `json:"id,required"` + // Number of browsers currently acquired from the pool + AcquiredCount int64 `json:"acquired_count,required"` + // Number of browsers currently available in the pool + AvailableCount int64 `json:"available_count,required"` + // Configuration used to create all browsers in this pool + BrowserPoolConfig BrowserPoolRequest `json:"browser_pool_config,required"` + // Timestamp when the browser pool was created + CreatedAt time.Time `json:"created_at,required" format:"date-time"` + // Browser pool name, if set + Name string `json:"name"` + // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. + JSON struct { + ID respjson.Field + AcquiredCount respjson.Field + AvailableCount respjson.Field + BrowserPoolConfig respjson.Field + CreatedAt respjson.Field + Name respjson.Field + ExtraFields map[string]respjson.Field + raw string + } `json:"-"` +} + +// Returns the unmodified JSON received from the API +func (r BrowserPool) RawJSON() string { return r.JSON.raw } +func (r *BrowserPool) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} + +// Request body for acquiring a browser from the pool. +type BrowserPoolAcquireRequestParam struct { + // Maximum number of seconds to wait for a browser to be available. Defaults to the + // calculated time it would take to fill the pool at the currently configured fill + // rate. + AcquireTimeoutSeconds param.Opt[int64] `json:"acquire_timeout_seconds,omitzero"` + paramObj +} + +func (r BrowserPoolAcquireRequestParam) MarshalJSON() (data []byte, err error) { + type shadow BrowserPoolAcquireRequestParam + return param.MarshalObject(r, (*shadow)(&r)) +} +func (r *BrowserPoolAcquireRequestParam) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} + +// Request body for releasing a browser back to the pool. +// +// The property SessionID is required. +type BrowserPoolReleaseRequestParam struct { + // Browser session ID to release back to the pool + SessionID string `json:"session_id,required"` + // Whether to reuse the browser instance or destroy it and create a new one. + // Defaults to true. + Reuse param.Opt[bool] `json:"reuse,omitzero"` + paramObj +} + +func (r BrowserPoolReleaseRequestParam) MarshalJSON() (data []byte, err error) { + type shadow BrowserPoolReleaseRequestParam + return param.MarshalObject(r, (*shadow)(&r)) +} +func (r *BrowserPoolReleaseRequestParam) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} + +// Parameters for creating a browser pool. All browsers in the pool will be created +// with the same configuration. +type BrowserPoolRequest struct { + // Number of browsers to create in the pool + Size int64 `json:"size,required"` + // List of browser extensions to load into the session. Provide each by id or name. + Extensions []shared.BrowserExtension `json:"extensions"` + // Percentage of the pool to fill per minute. Defaults to 10%. + FillRatePerMinute int64 `json:"fill_rate_per_minute"` + // If true, launches the browser using a headless image. Defaults to false. + Headless bool `json:"headless"` + // If true, launches the browser in kiosk mode to hide address bar and tabs in live + // view. + KioskMode bool `json:"kiosk_mode"` + // Optional name for the browser pool. Must be unique within the organization. + 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"` + // Optional proxy to associate to the browser session. Must reference a proxy + // belonging to the caller's org. + ProxyID string `json:"proxy_id"` + // If true, launches the browser in stealth mode to reduce detection by anti-bot + // mechanisms. + Stealth bool `json:"stealth"` + // Default idle timeout in seconds for browsers acquired from this pool before they + // are destroyed. Defaults to 600 seconds if not specified + TimeoutSeconds int64 `json:"timeout_seconds"` + // Initial browser window size in pixels with optional refresh rate. If omitted, + // image defaults apply (commonly 1024x768@60). Only specific viewport + // configurations are supported. The server will reject unsupported combinations. + // Supported resolutions are: 2560x1440@10, 1920x1080@25, 1920x1200@25, + // 1440x900@25, 1024x768@60, 1200x800@60 If refresh_rate is not provided, it will + // be automatically determined from the width and height if they match a supported + // configuration exactly. Note: Higher resolutions may affect the responsiveness of + // live view browser + Viewport shared.BrowserViewport `json:"viewport"` + // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. + JSON struct { + Size respjson.Field + Extensions respjson.Field + FillRatePerMinute respjson.Field + Headless respjson.Field + KioskMode respjson.Field + Name respjson.Field + Profile respjson.Field + ProxyID respjson.Field + Stealth respjson.Field + TimeoutSeconds respjson.Field + Viewport respjson.Field + ExtraFields map[string]respjson.Field + raw string + } `json:"-"` +} + +// Returns the unmodified JSON received from the API +func (r BrowserPoolRequest) RawJSON() string { return r.JSON.raw } +func (r *BrowserPoolRequest) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} + +// ToParam converts this BrowserPoolRequest to a BrowserPoolRequestParam. +// +// 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 +// BrowserPoolRequestParam.Overrides() +func (r BrowserPoolRequest) ToParam() BrowserPoolRequestParam { + return param.Override[BrowserPoolRequestParam](json.RawMessage(r.RawJSON())) +} + +// Parameters for creating a browser pool. All browsers in the pool will be created +// with the same configuration. +// +// The property Size is required. +type BrowserPoolRequestParam struct { + // Number of browsers to create in the pool + Size int64 `json:"size,required"` + // Percentage of the pool to fill per minute. Defaults to 10%. + FillRatePerMinute param.Opt[int64] `json:"fill_rate_per_minute,omitzero"` + // If true, launches the browser using a headless image. Defaults to false. + Headless param.Opt[bool] `json:"headless,omitzero"` + // If true, launches the browser in kiosk mode to hide address bar and tabs in live + // view. + KioskMode param.Opt[bool] `json:"kiosk_mode,omitzero"` + // Optional name for the browser pool. Must be unique within the organization. + Name param.Opt[string] `json:"name,omitzero"` + // Optional proxy to associate to the browser session. Must reference a proxy + // belonging to the caller's org. + ProxyID param.Opt[string] `json:"proxy_id,omitzero"` + // If true, launches the browser in stealth mode to reduce detection by anti-bot + // mechanisms. + Stealth param.Opt[bool] `json:"stealth,omitzero"` + // Default idle timeout in seconds for browsers acquired from this pool before they + // are destroyed. Defaults to 600 seconds if not specified + TimeoutSeconds param.Opt[int64] `json:"timeout_seconds,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"` + // Initial browser window size in pixels with optional refresh rate. If omitted, + // image defaults apply (commonly 1024x768@60). Only specific viewport + // configurations are supported. The server will reject unsupported combinations. + // Supported resolutions are: 2560x1440@10, 1920x1080@25, 1920x1200@25, + // 1440x900@25, 1024x768@60, 1200x800@60 If refresh_rate is not provided, it will + // be automatically determined from the width and height if they match a supported + // configuration exactly. Note: Higher resolutions may affect the responsiveness of + // live view browser + Viewport shared.BrowserViewportParam `json:"viewport,omitzero"` + paramObj +} + +func (r BrowserPoolRequestParam) MarshalJSON() (data []byte, err error) { + type shadow BrowserPoolRequestParam + return param.MarshalObject(r, (*shadow)(&r)) +} +func (r *BrowserPoolRequestParam) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} + +// Parameters for updating a browser pool. All browsers in the pool will be created +// with the same configuration. +type BrowserPoolUpdateRequestParam struct { + // Whether to discard all idle browsers and rebuild the pool immediately. Defaults + // to true. + DiscardAllIdle param.Opt[bool] `json:"discard_all_idle,omitzero"` + BrowserPoolRequestParam +} + +func (r BrowserPoolUpdateRequestParam) MarshalJSON() (data []byte, err error) { + type shadow BrowserPoolUpdateRequestParam + return param.MarshalObject(r, (*shadow)(&r)) +} + +type BrowserPoolAcquireResponse struct { + // Websocket URL for Chrome DevTools Protocol connections to the browser session + CdpWsURL string `json:"cdp_ws_url,required"` + // When the browser session was created. + CreatedAt time.Time `json:"created_at,required" format:"date-time"` + // Whether the browser session is running in headless mode. + Headless bool `json:"headless,required"` + // Unique identifier for the browser session + SessionID string `json:"session_id,required"` + // Whether the browser session is running in stealth mode. + Stealth bool `json:"stealth,required"` + // The number of seconds of inactivity before the browser session is terminated. + TimeoutSeconds int64 `json:"timeout_seconds,required"` + // Remote URL for live viewing the browser session. Only available for non-headless + // browsers. + BrowserLiveViewURL string `json:"browser_live_view_url"` + // When the browser session was soft-deleted. Only present for deleted sessions. + DeletedAt time.Time `json:"deleted_at" format:"date-time"` + // Whether the browser session is running in kiosk mode. + KioskMode bool `json:"kiosk_mode"` + // Optional persistence configuration for the browser session. + Persistence BrowserPersistence `json:"persistence"` + // Browser profile metadata. + Profile Profile `json:"profile"` + // ID of the proxy associated with this browser session, if any. + ProxyID string `json:"proxy_id"` + // Initial browser window size in pixels with optional refresh rate. If omitted, + // image defaults apply (commonly 1024x768@60). Only specific viewport + // configurations are supported. The server will reject unsupported combinations. + // Supported resolutions are: 2560x1440@10, 1920x1080@25, 1920x1200@25, + // 1440x900@25, 1024x768@60, 1200x800@60 If refresh_rate is not provided, it will + // be automatically determined from the width and height if they match a supported + // configuration exactly. Note: Higher resolutions may affect the responsiveness of + // live view browser + Viewport shared.BrowserViewport `json:"viewport"` + // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. + JSON struct { + CdpWsURL respjson.Field + CreatedAt respjson.Field + Headless respjson.Field + SessionID respjson.Field + Stealth respjson.Field + TimeoutSeconds respjson.Field + BrowserLiveViewURL respjson.Field + DeletedAt respjson.Field + KioskMode respjson.Field + Persistence respjson.Field + Profile respjson.Field + ProxyID respjson.Field + Viewport respjson.Field + ExtraFields map[string]respjson.Field + raw string + } `json:"-"` +} + +// Returns the unmodified JSON received from the API +func (r BrowserPoolAcquireResponse) RawJSON() string { return r.JSON.raw } +func (r *BrowserPoolAcquireResponse) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} + +type BrowserPoolNewParams struct { + // Parameters for creating a browser pool. All browsers in the pool will be created + // with the same configuration. + BrowserPoolRequest BrowserPoolRequestParam + paramObj +} + +func (r BrowserPoolNewParams) MarshalJSON() (data []byte, err error) { + return shimjson.Marshal(r.BrowserPoolRequest) +} +func (r *BrowserPoolNewParams) UnmarshalJSON(data []byte) error { + return json.Unmarshal(data, &r.BrowserPoolRequest) +} + +type BrowserPoolUpdateParams struct { + // Parameters for updating a browser pool. All browsers in the pool will be created + // with the same configuration. + BrowserPoolUpdateRequest BrowserPoolUpdateRequestParam + paramObj +} + +func (r BrowserPoolUpdateParams) MarshalJSON() (data []byte, err error) { + return shimjson.Marshal(r.BrowserPoolUpdateRequest) +} +func (r *BrowserPoolUpdateParams) UnmarshalJSON(data []byte) error { + return json.Unmarshal(data, &r.BrowserPoolUpdateRequest) +} + +type BrowserPoolDeleteParams struct { + // If true, force delete even if browsers are currently leased. Leased browsers + // will be terminated. + Force param.Opt[bool] `json:"force,omitzero"` + paramObj +} + +func (r BrowserPoolDeleteParams) MarshalJSON() (data []byte, err error) { + type shadow BrowserPoolDeleteParams + return param.MarshalObject(r, (*shadow)(&r)) +} +func (r *BrowserPoolDeleteParams) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} + +type BrowserPoolAcquireParams struct { + // Request body for acquiring a browser from the pool. + BrowserPoolAcquireRequest BrowserPoolAcquireRequestParam + paramObj +} + +func (r BrowserPoolAcquireParams) MarshalJSON() (data []byte, err error) { + return shimjson.Marshal(r.BrowserPoolAcquireRequest) +} +func (r *BrowserPoolAcquireParams) UnmarshalJSON(data []byte) error { + return json.Unmarshal(data, &r.BrowserPoolAcquireRequest) +} + +type BrowserPoolReleaseParams struct { + // Request body for releasing a browser back to the pool. + BrowserPoolReleaseRequest BrowserPoolReleaseRequestParam + paramObj +} + +func (r BrowserPoolReleaseParams) MarshalJSON() (data []byte, err error) { + return shimjson.Marshal(r.BrowserPoolReleaseRequest) +} +func (r *BrowserPoolReleaseParams) UnmarshalJSON(data []byte) error { + return json.Unmarshal(data, &r.BrowserPoolReleaseRequest) +} diff --git a/browserpool_test.go b/browserpool_test.go new file mode 100644 index 0000000..37fa909 --- /dev/null +++ b/browserpool_test.go @@ -0,0 +1,279 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package kernel_test + +import ( + "context" + "errors" + "os" + "testing" + + "github.com/onkernel/kernel-go-sdk" + "github.com/onkernel/kernel-go-sdk/internal/testutil" + "github.com/onkernel/kernel-go-sdk/option" + "github.com/onkernel/kernel-go-sdk/shared" +) + +func TestBrowserPoolNewWithOptionalParams(t *testing.T) { + t.Skip("Prism tests are disabled") + baseURL := "http://localhost:4010" + if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok { + baseURL = envURL + } + if !testutil.CheckTestServer(t, baseURL) { + return + } + client := kernel.NewClient( + option.WithBaseURL(baseURL), + option.WithAPIKey("My API Key"), + ) + _, err := client.BrowserPools.New(context.TODO(), kernel.BrowserPoolNewParams{ + BrowserPoolRequest: kernel.BrowserPoolRequestParam{ + Size: 10, + Extensions: []shared.BrowserExtensionParam{{ + ID: kernel.String("id"), + Name: kernel.String("name"), + }}, + FillRatePerMinute: kernel.Int(0), + 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), + }, + ProxyID: kernel.String("proxy_id"), + Stealth: kernel.Bool(true), + TimeoutSeconds: kernel.Int(60), + Viewport: shared.BrowserViewportParam{ + Height: 800, + Width: 1280, + RefreshRate: kernel.Int(60), + }, + }, + }) + if err != nil { + var apierr *kernel.Error + if errors.As(err, &apierr) { + t.Log(string(apierr.DumpRequest(true))) + } + t.Fatalf("err should be nil: %s", err.Error()) + } +} + +func TestBrowserPoolGet(t *testing.T) { + t.Skip("Prism tests are disabled") + baseURL := "http://localhost:4010" + if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok { + baseURL = envURL + } + if !testutil.CheckTestServer(t, baseURL) { + return + } + client := kernel.NewClient( + option.WithBaseURL(baseURL), + option.WithAPIKey("My API Key"), + ) + _, err := client.BrowserPools.Get(context.TODO(), "id_or_name") + if err != nil { + var apierr *kernel.Error + if errors.As(err, &apierr) { + t.Log(string(apierr.DumpRequest(true))) + } + t.Fatalf("err should be nil: %s", err.Error()) + } +} + +func TestBrowserPoolUpdateWithOptionalParams(t *testing.T) { + t.Skip("Prism tests are disabled") + baseURL := "http://localhost:4010" + if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok { + baseURL = envURL + } + if !testutil.CheckTestServer(t, baseURL) { + return + } + client := kernel.NewClient( + option.WithBaseURL(baseURL), + option.WithAPIKey("My API Key"), + ) + _, err := client.BrowserPools.Update( + context.TODO(), + "id_or_name", + kernel.BrowserPoolUpdateParams{ + BrowserPoolUpdateRequest: kernel.BrowserPoolUpdateRequestParam{ + BrowserPoolRequestParam: kernel.BrowserPoolRequestParam{ + Size: 10, + Extensions: []shared.BrowserExtensionParam{{ + ID: kernel.String("id"), + Name: kernel.String("name"), + }}, + FillRatePerMinute: kernel.Int(0), + 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), + }, + ProxyID: kernel.String("proxy_id"), + Stealth: kernel.Bool(true), + TimeoutSeconds: kernel.Int(60), + Viewport: shared.BrowserViewportParam{ + Height: 800, + Width: 1280, + RefreshRate: kernel.Int(60), + }, + }, + DiscardAllIdle: kernel.Bool(false), + }, + }, + ) + if err != nil { + var apierr *kernel.Error + if errors.As(err, &apierr) { + t.Log(string(apierr.DumpRequest(true))) + } + t.Fatalf("err should be nil: %s", err.Error()) + } +} + +func TestBrowserPoolList(t *testing.T) { + t.Skip("Prism tests are disabled") + baseURL := "http://localhost:4010" + if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok { + baseURL = envURL + } + if !testutil.CheckTestServer(t, baseURL) { + return + } + client := kernel.NewClient( + option.WithBaseURL(baseURL), + option.WithAPIKey("My API Key"), + ) + _, err := client.BrowserPools.List(context.TODO()) + if err != nil { + var apierr *kernel.Error + if errors.As(err, &apierr) { + t.Log(string(apierr.DumpRequest(true))) + } + t.Fatalf("err should be nil: %s", err.Error()) + } +} + +func TestBrowserPoolDeleteWithOptionalParams(t *testing.T) { + t.Skip("Prism tests are disabled") + baseURL := "http://localhost:4010" + if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok { + baseURL = envURL + } + if !testutil.CheckTestServer(t, baseURL) { + return + } + client := kernel.NewClient( + option.WithBaseURL(baseURL), + option.WithAPIKey("My API Key"), + ) + err := client.BrowserPools.Delete( + context.TODO(), + "id_or_name", + kernel.BrowserPoolDeleteParams{ + Force: kernel.Bool(true), + }, + ) + if err != nil { + var apierr *kernel.Error + if errors.As(err, &apierr) { + t.Log(string(apierr.DumpRequest(true))) + } + t.Fatalf("err should be nil: %s", err.Error()) + } +} + +func TestBrowserPoolAcquireWithOptionalParams(t *testing.T) { + t.Skip("Prism tests are disabled") + baseURL := "http://localhost:4010" + if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok { + baseURL = envURL + } + if !testutil.CheckTestServer(t, baseURL) { + return + } + client := kernel.NewClient( + option.WithBaseURL(baseURL), + option.WithAPIKey("My API Key"), + ) + _, err := client.BrowserPools.Acquire( + context.TODO(), + "id_or_name", + kernel.BrowserPoolAcquireParams{ + BrowserPoolAcquireRequest: kernel.BrowserPoolAcquireRequestParam{ + AcquireTimeoutSeconds: kernel.Int(0), + }, + }, + ) + if err != nil { + var apierr *kernel.Error + if errors.As(err, &apierr) { + t.Log(string(apierr.DumpRequest(true))) + } + t.Fatalf("err should be nil: %s", err.Error()) + } +} + +func TestBrowserPoolFlush(t *testing.T) { + t.Skip("Prism tests are disabled") + baseURL := "http://localhost:4010" + if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok { + baseURL = envURL + } + if !testutil.CheckTestServer(t, baseURL) { + return + } + client := kernel.NewClient( + option.WithBaseURL(baseURL), + option.WithAPIKey("My API Key"), + ) + err := client.BrowserPools.Flush(context.TODO(), "id_or_name") + if err != nil { + var apierr *kernel.Error + if errors.As(err, &apierr) { + t.Log(string(apierr.DumpRequest(true))) + } + t.Fatalf("err should be nil: %s", err.Error()) + } +} + +func TestBrowserPoolReleaseWithOptionalParams(t *testing.T) { + t.Skip("Prism tests are disabled") + baseURL := "http://localhost:4010" + if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok { + baseURL = envURL + } + if !testutil.CheckTestServer(t, baseURL) { + return + } + client := kernel.NewClient( + option.WithBaseURL(baseURL), + option.WithAPIKey("My API Key"), + ) + err := client.BrowserPools.Release( + context.TODO(), + "id_or_name", + kernel.BrowserPoolReleaseParams{ + BrowserPoolReleaseRequest: kernel.BrowserPoolReleaseRequestParam{ + SessionID: "ts8iy3sg25ibheguyni2lg9t", + Reuse: kernel.Bool(false), + }, + }, + ) + if err != nil { + var apierr *kernel.Error + if errors.As(err, &apierr) { + t.Log(string(apierr.DumpRequest(true))) + } + t.Fatalf("err should be nil: %s", err.Error()) + } +} diff --git a/client.go b/client.go index 39e85fe..1fac9b9 100644 --- a/client.go +++ b/client.go @@ -16,15 +16,15 @@ import ( // interacting with the kernel API. You should not instantiate this client // directly, and instead use the [NewClient] method instead. type Client struct { - Options []option.RequestOption - Deployments DeploymentService - Apps AppService - Invocations InvocationService - Browsers BrowserService - Profiles ProfileService - Proxies ProxyService - Extensions ExtensionService - Agents AgentService + Options []option.RequestOption + Deployments DeploymentService + Apps AppService + Invocations InvocationService + Browsers BrowserService + Profiles ProfileService + Proxies ProxyService + Extensions ExtensionService + BrowserPools BrowserPoolService } // DefaultClientOptions read from the environment (KERNEL_API_KEY, @@ -56,7 +56,7 @@ func NewClient(opts ...option.RequestOption) (r Client) { r.Profiles = NewProfileService(opts...) r.Proxies = NewProxyService(opts...) r.Extensions = NewExtensionService(opts...) - r.Agents = NewAgentService(opts...) + r.BrowserPools = NewBrowserPoolService(opts...) return } diff --git a/shared/shared.go b/shared/shared.go index a968ca4..755810d 100644 --- a/shared/shared.go +++ b/shared/shared.go @@ -3,6 +3,7 @@ package shared import ( + "encoding/json" "time" "github.com/onkernel/kernel-go-sdk/internal/apijson" @@ -35,6 +36,187 @@ func (r *AppAction) UnmarshalJSON(data []byte) error { return apijson.UnmarshalRoot(data, r) } +// Extension selection for the browser session. Provide either id or name of an +// extension uploaded to Kernel. +type BrowserExtension struct { + // Extension ID to load for this browser session + ID string `json:"id"` + // Extension 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"` + // 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 BrowserExtension) RawJSON() string { return r.JSON.raw } +func (r *BrowserExtension) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} + +// ToParam converts this BrowserExtension to a BrowserExtensionParam. +// +// 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 +// BrowserExtensionParam.Overrides() +func (r BrowserExtension) ToParam() BrowserExtensionParam { + return param.Override[BrowserExtensionParam](json.RawMessage(r.RawJSON())) +} + +// Extension selection for the browser session. Provide either id or name of an +// extension uploaded to Kernel. +type BrowserExtensionParam struct { + // Extension ID to load for this browser session + ID param.Opt[string] `json:"id,omitzero"` + // Extension name to load for this browser session (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 BrowserExtensionParam) MarshalJSON() (data []byte, err error) { + type shadow BrowserExtensionParam + return param.MarshalObject(r, (*shadow)(&r)) +} +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. +type BrowserProfileParam struct { + // Profile ID to load for this browser session + ID param.Opt[string] `json:"id,omitzero"` + // Profile name to load for this browser session (instead of id). Must be 1-255 + // characters, using letters, numbers, dots, underscores, or hyphens. + Name param.Opt[string] `json:"name,omitzero"` + // If true, save changes made during the session back to the profile when the + // session ends. + SaveChanges param.Opt[bool] `json:"save_changes,omitzero"` + paramObj +} + +func (r BrowserProfileParam) MarshalJSON() (data []byte, err error) { + type shadow BrowserProfileParam + return param.MarshalObject(r, (*shadow)(&r)) +} +func (r *BrowserProfileParam) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} + +// Initial browser window size in pixels with optional refresh rate. If omitted, +// image defaults apply (commonly 1024x768@60). Only specific viewport +// configurations are supported. The server will reject unsupported combinations. +// Supported resolutions are: 2560x1440@10, 1920x1080@25, 1920x1200@25, +// 1440x900@25, 1024x768@60, 1200x800@60 If refresh_rate is not provided, it will +// be automatically determined from the width and height if they match a supported +// configuration exactly. Note: Higher resolutions may affect the responsiveness of +// live view browser +type BrowserViewport struct { + // Browser window height in pixels. + Height int64 `json:"height,required"` + // Browser window width in pixels. + Width int64 `json:"width,required"` + // Display refresh rate in Hz. If omitted, automatically determined from width and + // height. + RefreshRate int64 `json:"refresh_rate"` + // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. + JSON struct { + Height respjson.Field + Width respjson.Field + RefreshRate respjson.Field + ExtraFields map[string]respjson.Field + raw string + } `json:"-"` +} + +// Returns the unmodified JSON received from the API +func (r BrowserViewport) RawJSON() string { return r.JSON.raw } +func (r *BrowserViewport) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} + +// ToParam converts this BrowserViewport to a BrowserViewportParam. +// +// 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 +// BrowserViewportParam.Overrides() +func (r BrowserViewport) ToParam() BrowserViewportParam { + return param.Override[BrowserViewportParam](json.RawMessage(r.RawJSON())) +} + +// Initial browser window size in pixels with optional refresh rate. If omitted, +// image defaults apply (commonly 1024x768@60). Only specific viewport +// configurations are supported. The server will reject unsupported combinations. +// Supported resolutions are: 2560x1440@10, 1920x1080@25, 1920x1200@25, +// 1440x900@25, 1024x768@60, 1200x800@60 If refresh_rate is not provided, it will +// be automatically determined from the width and height if they match a supported +// configuration exactly. Note: Higher resolutions may affect the responsiveness of +// live view browser +// +// The properties Height, Width are required. +type BrowserViewportParam struct { + // Browser window height in pixels. + Height int64 `json:"height,required"` + // Browser window width in pixels. + Width int64 `json:"width,required"` + // Display refresh rate in Hz. If omitted, automatically determined from width and + // height. + RefreshRate param.Opt[int64] `json:"refresh_rate,omitzero"` + paramObj +} + +func (r BrowserViewportParam) MarshalJSON() (data []byte, err error) { + type shadow BrowserViewportParam + return param.MarshalObject(r, (*shadow)(&r)) +} +func (r *BrowserViewportParam) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} + type ErrorDetail struct { // Lower-level error code providing more specific detail Code string `json:"code"` From 13eded97c6e7f43b6a86871b7d7048215438bbcf Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 2 Dec 2025 22:14:04 +0000 Subject: [PATCH 6/7] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index c32f96d..25e77f1 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 74 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-340c8f009b71922347d4c238c8715cd752c8965abfa12cbb1ffabe35edc338a8.yml -openapi_spec_hash: efc13ab03ef89cc07333db8ab5345f31 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-3db06d1628149b5ea8303f1c72250664dfd7cb4a14ceb6102f1ae6e85c92c038.yml +openapi_spec_hash: e5b3da2da328eb26d2a70e2521744c62 config_hash: a4124701ae0a474e580d7416adbcfb00 From a4d43505dee0fdfa566e3ed9a0d9d1b37cf8c930 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 2 Dec 2025 22:14:22 +0000 Subject: [PATCH 7/7] release: 0.21.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 9 +++++++++ README.md | 2 +- internal/version.go | 2 +- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 0c2ecec..86b0e83 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.20.0" + ".": "0.21.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e52afc..a54ba21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 0.21.0 (2025-12-02) + +Full Changelog: [v0.20.0...v0.21.0](https://github.com/onkernel/kernel-go-sdk/compare/v0.20.0...v0.21.0) + +### Features + +* Browser pools sdk release ([69dcf34](https://github.com/onkernel/kernel-go-sdk/commit/69dcf3471d1b8b7b6fb8210da6d9c0b42ab1f1e7)) +* Mason/agent auth api ([ee63aba](https://github.com/onkernel/kernel-go-sdk/commit/ee63aba391e6a3024e93eeca28d008ea93b18b58)) + ## 0.20.0 (2025-11-19) Full Changelog: [v0.19.2...v0.20.0](https://github.com/onkernel/kernel-go-sdk/compare/v0.19.2...v0.20.0) diff --git a/README.md b/README.md index 89f902d..07fd868 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Or to pin the version: ```sh -go get -u 'github.com/onkernel/kernel-go-sdk@v0.20.0' +go get -u 'github.com/onkernel/kernel-go-sdk@v0.21.0' ``` diff --git a/internal/version.go b/internal/version.go index 6c84346..b6325aa 100644 --- a/internal/version.go +++ b/internal/version.go @@ -2,4 +2,4 @@ package internal -const PackageVersion = "0.20.0" // x-release-please-version +const PackageVersion = "0.21.0" // x-release-please-version