diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 86b0e83..cb9d254 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.21.0" + ".": "0.22.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 25e77f1..0c474bb 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-3db06d1628149b5ea8303f1c72250664dfd7cb4a14ceb6102f1ae6e85c92c038.yml -openapi_spec_hash: e5b3da2da328eb26d2a70e2521744c62 -config_hash: a4124701ae0a474e580d7416adbcfb00 +configured_endpoints: 80 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-8a37652fa586b8932466d16285359a89988505f850787f8257d0c4c7053da173.yml +openapi_spec_hash: 042765a113f6d08109e8146b302323ec +config_hash: 113f1e5bc3567628a5d51c70bc00969d diff --git a/CHANGELOG.md b/CHANGELOG.md index a54ba21..2abbe96 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## 0.22.0 (2025-12-06) + +Full Changelog: [v0.21.0...v0.22.0](https://github.com/onkernel/kernel-go-sdk/compare/v0.21.0...v0.22.0) + +### Features + +* [wip] Browser pools polish pass ([628410a](https://github.com/onkernel/kernel-go-sdk/commit/628410a6fc946e6adf5d7a9593b6ebb01385f918)) +* Add `async_timeout_seconds` to PostInvocations ([4ee4d7c](https://github.com/onkernel/kernel-go-sdk/commit/4ee4d7c60817d8d4a9e6460e7d04c19f309095d3)) +* Enhance agent authentication with optional login page URL and auth ch… ([a2fc4ac](https://github.com/onkernel/kernel-go-sdk/commit/a2fc4acfb2bf88a5cc209d5430fcfb0eee4f751b)) + + +### Refactors + +* **browser:** remove persistence option UI ([7ea9ccf](https://github.com/onkernel/kernel-go-sdk/commit/7ea9ccfb9a6c8acb3fd9d8d04b7bf8b9cfb4fef1)) + ## 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) diff --git a/README.md b/README.md index 07fd868..43fd6a5 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.21.0' +go get -u 'github.com/onkernel/kernel-go-sdk@v0.22.0' ``` @@ -58,9 +58,7 @@ func main() { option.WithEnvironmentDevelopment(), // defaults to option.WithEnvironmentProduction() ) browser, err := client.Browsers.New(context.TODO(), kernel.BrowserNewParams{ - Persistence: kernel.BrowserPersistenceParam{ - ID: "browser-for-user-1234", - }, + Stealth: kernel.Bool(true), }) if err != nil { panic(err.Error()) @@ -334,9 +332,7 @@ To handle errors, we recommend that you use the `errors.As` pattern: ```go _, err := client.Browsers.New(context.TODO(), kernel.BrowserNewParams{ - Persistence: kernel.BrowserPersistenceParam{ - ID: "browser-for-user-1234", - }, + Stealth: kernel.Bool(true), }) if err != nil { var apierr *kernel.Error @@ -365,9 +361,7 @@ defer cancel() client.Browsers.New( ctx, kernel.BrowserNewParams{ - Persistence: kernel.BrowserPersistenceParam{ - ID: "browser-for-user-1234", - }, + Stealth: kernel.Bool(true), }, // This sets the per-retry timeout option.WithRequestTimeout(20*time.Second), @@ -423,9 +417,7 @@ client := kernel.NewClient( client.Browsers.New( context.TODO(), kernel.BrowserNewParams{ - Persistence: kernel.BrowserPersistenceParam{ - ID: "browser-for-user-1234", - }, + Stealth: kernel.Bool(true), }, option.WithMaxRetries(5), ) @@ -442,9 +434,7 @@ var response *http.Response browser, err := client.Browsers.New( context.TODO(), kernel.BrowserNewParams{ - Persistence: kernel.BrowserPersistenceParam{ - ID: "browser-for-user-1234", - }, + Stealth: kernel.Bool(true), }, option.WithResponseInto(&response), ) 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..16311d1 --- /dev/null +++ b/agentauth.go @@ -0,0 +1,325 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package kernel + +import ( + "context" + "errors" + "fmt" + "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 + Invocations AgentAuthInvocationService +} + +// 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.Invocations = NewAgentAuthInvocationService(opts...) + return +} + +// Retrieve an auth agent by its ID. Returns the current authentication status of +// the managed profile. +func (r *AgentAuthService) Get(ctx context.Context, id string, opts ...option.RequestOption) (res *AuthAgent, err error) { + opts = slices.Concat(r.Options, opts) + if id == "" { + err = errors.New("missing required id parameter") + return + } + path := fmt.Sprintf("agents/auth/%s", id) + err = requestconfig.ExecuteNewRequest(ctx, http.MethodGet, path, nil, &res, 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 invocation endpoint +type AgentAuthInvocationResponse struct { + // App name (org name at time of invocation creation) + AppName string `json:"app_name,required"` + // When the handoff code expires + ExpiresAt time.Time `json:"expires_at,required" format:"date-time"` + // Invocation status + // + // Any of "IN_PROGRESS", "SUCCESS", "EXPIRED", "CANCELED". + Status AgentAuthInvocationResponseStatus `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 AgentAuthInvocationResponse) RawJSON() string { return r.JSON.raw } +func (r *AgentAuthInvocationResponse) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} + +// Invocation status +type AgentAuthInvocationResponseStatus string + +const ( + AgentAuthInvocationResponseStatusInProgress AgentAuthInvocationResponseStatus = "IN_PROGRESS" + AgentAuthInvocationResponseStatusSuccess AgentAuthInvocationResponseStatus = "SUCCESS" + AgentAuthInvocationResponseStatusExpired AgentAuthInvocationResponseStatus = "EXPIRED" + AgentAuthInvocationResponseStatusCanceled AgentAuthInvocationResponseStatus = "CANCELED" +) + +// Response from starting an agent authentication invocation +type AgentAuthStartResponse struct { + // Unique identifier for the auth agent managing this domain/profile + AuthAgentID string `json:"auth_agent_id,required"` + // 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 invocation + InvocationID string `json:"invocation_id,required"` + // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. + JSON struct { + AuthAgentID respjson.Field + ExpiresAt respjson.Field + HandoffCode respjson.Field + HostedURL respjson.Field + InvocationID 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) +} + +// An auth agent that manages authentication for a specific domain and profile +// combination +type AuthAgent struct { + // Unique identifier for the auth agent + ID string `json:"id,required"` + // Target domain for authentication + Domain string `json:"domain,required"` + // Name of the profile associated with this auth agent + ProfileName string `json:"profile_name,required"` + // Current authentication status of the managed profile + // + // Any of "AUTHENTICATED", "NEEDS_AUTH". + Status AuthAgentStatus `json:"status,required"` + // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. + JSON struct { + ID respjson.Field + Domain respjson.Field + ProfileName respjson.Field + Status respjson.Field + ExtraFields map[string]respjson.Field + raw string + } `json:"-"` +} + +// Returns the unmodified JSON received from the API +func (r AuthAgent) RawJSON() string { return r.JSON.raw } +func (r *AuthAgent) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} + +// Current authentication status of the managed profile +type AuthAgentStatus string + +const ( + AuthAgentStatusAuthenticated AuthAgentStatus = "AUTHENTICATED" + AuthAgentStatusNeedsAuth AuthAgentStatus = "NEEDS_AUTH" +) + +// 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". + 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" +) + +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 login page URL. If provided, will be stored on the agent and used to + // skip Phase 1 discovery in future invocations. + LoginURL param.Opt[string] `json:"login_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..aa21286 --- /dev/null +++ b/agentauth_test.go @@ -0,0 +1,68 @@ +// 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 TestAgentAuthGet(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.Get(context.TODO(), "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 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"), + LoginURL: kernel.String("https://doordash.com/account/login"), + 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/agentauthinvocation.go b/agentauthinvocation.go new file mode 100644 index 0000000..e9b8ccb --- /dev/null +++ b/agentauthinvocation.go @@ -0,0 +1,153 @@ +// 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" +) + +// AgentAuthInvocationService 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 [NewAgentAuthInvocationService] method instead. +type AgentAuthInvocationService struct { + Options []option.RequestOption +} + +// NewAgentAuthInvocationService 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 NewAgentAuthInvocationService(opts ...option.RequestOption) (r AgentAuthInvocationService) { + r = AgentAuthInvocationService{} + r.Options = opts + return +} + +// Returns invocation details including app_name and target_domain. Uses the JWT +// returned by the exchange endpoint, or standard API key or JWT authentication. +func (r *AgentAuthInvocationService) Get(ctx context.Context, invocationID string, opts ...option.RequestOption) (res *AgentAuthInvocationResponse, err error) { + opts = slices.Concat(r.Options, opts) + if invocationID == "" { + err = errors.New("missing required invocation_id parameter") + return + } + path := fmt.Sprintf("agents/auth/invocations/%s", invocationID) + 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 *AgentAuthInvocationService) Discover(ctx context.Context, invocationID string, body AgentAuthInvocationDiscoverParams, opts ...option.RequestOption) (res *AgentAuthDiscoverResponse, err error) { + opts = slices.Concat(r.Options, opts) + if invocationID == "" { + err = errors.New("missing required invocation_id parameter") + return + } + path := fmt.Sprintf("agents/auth/invocations/%s/discover", invocationID) + 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 *AgentAuthInvocationService) Exchange(ctx context.Context, invocationID string, body AgentAuthInvocationExchangeParams, opts ...option.RequestOption) (res *AgentAuthInvocationExchangeResponse, err error) { + opts = slices.Concat(r.Options, opts) + if invocationID == "" { + err = errors.New("missing required invocation_id parameter") + return + } + path := fmt.Sprintf("agents/auth/invocations/%s/exchange", invocationID) + 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 *AgentAuthInvocationService) Submit(ctx context.Context, invocationID string, body AgentAuthInvocationSubmitParams, opts ...option.RequestOption) (res *AgentAuthSubmitResponse, err error) { + opts = slices.Concat(r.Options, opts) + if invocationID == "" { + err = errors.New("missing required invocation_id parameter") + return + } + path := fmt.Sprintf("agents/auth/invocations/%s/submit", invocationID) + err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, body, &res, opts...) + return +} + +// Response from exchange endpoint +type AgentAuthInvocationExchangeResponse struct { + // Invocation ID + InvocationID string `json:"invocation_id,required"` + // JWT token with invocation_id claim (30 minute TTL) + Jwt string `json:"jwt,required"` + // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. + JSON struct { + InvocationID respjson.Field + Jwt respjson.Field + ExtraFields map[string]respjson.Field + raw string + } `json:"-"` +} + +// Returns the unmodified JSON received from the API +func (r AgentAuthInvocationExchangeResponse) RawJSON() string { return r.JSON.raw } +func (r *AgentAuthInvocationExchangeResponse) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} + +type AgentAuthInvocationDiscoverParams struct { + // Optional login page URL. If provided, will override the stored login URL for + // this discovery invocation and skip Phase 1 discovery. + LoginURL param.Opt[string] `json:"login_url,omitzero" format:"uri"` + paramObj +} + +func (r AgentAuthInvocationDiscoverParams) MarshalJSON() (data []byte, err error) { + type shadow AgentAuthInvocationDiscoverParams + return param.MarshalObject(r, (*shadow)(&r)) +} +func (r *AgentAuthInvocationDiscoverParams) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} + +type AgentAuthInvocationExchangeParams struct { + // Handoff code from start endpoint + Code string `json:"code,required"` + paramObj +} + +func (r AgentAuthInvocationExchangeParams) MarshalJSON() (data []byte, err error) { + type shadow AgentAuthInvocationExchangeParams + return param.MarshalObject(r, (*shadow)(&r)) +} +func (r *AgentAuthInvocationExchangeParams) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} + +type AgentAuthInvocationSubmitParams struct { + // Values for the discovered login fields + FieldValues map[string]string `json:"field_values,omitzero,required"` + paramObj +} + +func (r AgentAuthInvocationSubmitParams) MarshalJSON() (data []byte, err error) { + type shadow AgentAuthInvocationSubmitParams + return param.MarshalObject(r, (*shadow)(&r)) +} +func (r *AgentAuthInvocationSubmitParams) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} diff --git a/agentauthinvocation_test.go b/agentauthinvocation_test.go new file mode 100644 index 0000000..367ff15 --- /dev/null +++ b/agentauthinvocation_test.go @@ -0,0 +1,127 @@ +// 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 TestAgentAuthInvocationGet(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.Invocations.Get(context.TODO(), "invocation_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 TestAgentAuthInvocationDiscoverWithOptionalParams(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.Invocations.Discover( + context.TODO(), + "invocation_id", + kernel.AgentAuthInvocationDiscoverParams{ + LoginURL: kernel.String("https://doordash.com/account/login"), + }, + ) + 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 TestAgentAuthInvocationExchange(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.Invocations.Exchange( + context.TODO(), + "invocation_id", + kernel.AgentAuthInvocationExchangeParams{ + Code: "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 TestAgentAuthInvocationSubmit(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.Invocations.Submit( + context.TODO(), + "invocation_id", + kernel.AgentAuthInvocationSubmitParams{ + 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 d7a4818..7df59e9 100644 --- a/aliases.go +++ b/aliases.go @@ -48,11 +48,11 @@ type BrowserProfile = shared.BrowserProfile 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 +// image defaults apply (1920x1080@25). 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 // @@ -60,11 +60,11 @@ type BrowserProfileParam = shared.BrowserProfileParam 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 +// image defaults apply (1920x1080@25). 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 // diff --git a/api.md b/api.md index aa0d086..80ddb73 100644 --- a/api.md +++ b/api.md @@ -253,3 +253,34 @@ Methods: - 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 + +# Agents + +## Auth + +Response Types: + +- kernel.AgentAuthDiscoverResponse +- kernel.AgentAuthInvocationResponse +- kernel.AgentAuthStartResponse +- kernel.AgentAuthSubmitResponse +- kernel.AuthAgent +- kernel.DiscoveredField + +Methods: + +- client.Agents.Auth.Get(ctx context.Context, id string) (kernel.AuthAgent, error) +- client.Agents.Auth.Start(ctx context.Context, body kernel.AgentAuthStartParams) (kernel.AgentAuthStartResponse, error) + +### Invocations + +Response Types: + +- kernel.AgentAuthInvocationExchangeResponse + +Methods: + +- client.Agents.Auth.Invocations.Get(ctx context.Context, invocationID string) (kernel.AgentAuthInvocationResponse, error) +- client.Agents.Auth.Invocations.Discover(ctx context.Context, invocationID string, body kernel.AgentAuthInvocationDiscoverParams) (kernel.AgentAuthDiscoverResponse, error) +- client.Agents.Auth.Invocations.Exchange(ctx context.Context, invocationID string, body kernel.AgentAuthInvocationExchangeParams) (kernel.AgentAuthInvocationExchangeResponse, error) +- client.Agents.Auth.Invocations.Submit(ctx context.Context, invocationID string, body kernel.AgentAuthInvocationSubmitParams) (kernel.AgentAuthSubmitResponse, error) diff --git a/browser.go b/browser.go index fc9f55c..2ae047c 100644 --- a/browser.go +++ b/browser.go @@ -102,7 +102,10 @@ func (r *BrowserService) ListAutoPaging(ctx context.Context, query BrowserListPa return pagination.NewOffsetPaginationAutoPager(r.List(ctx, query, opts...)) } -// Delete a persistent browser session by its persistent_id. +// DEPRECATED: Use DELETE /browsers/{id} instead. Delete a persistent browser +// session by its persistent_id. +// +// Deprecated: deprecated func (r *BrowserService) Delete(ctx context.Context, body BrowserDeleteParams, opts ...option.RequestOption) (err error) { opts = slices.Concat(r.Options, opts) opts = append([]option.RequestOption{option.WithHeader("Accept", "*/*")}, opts...) @@ -138,9 +141,11 @@ func (r *BrowserService) LoadExtensions(ctx context.Context, id string, body Bro return } -// Optional persistence configuration for the browser session. +// DEPRECATED: Use timeout_seconds (up to 72 hours) and Profiles instead. +// +// Deprecated: deprecated type BrowserPersistence struct { - // Unique identifier for the persistent browser session. + // DEPRECATED: Unique identifier for the persistent browser session. ID string `json:"id,required"` // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. JSON struct { @@ -165,11 +170,13 @@ func (r BrowserPersistence) ToParam() BrowserPersistenceParam { return param.Override[BrowserPersistenceParam](json.RawMessage(r.RawJSON())) } -// Optional persistence configuration for the browser session. +// DEPRECATED: Use timeout_seconds (up to 72 hours) and Profiles instead. +// +// Deprecated: deprecated // // The property ID is required. type BrowserPersistenceParam struct { - // Unique identifier for the persistent browser session. + // DEPRECATED: Unique identifier for the persistent browser session. ID string `json:"id,required"` paramObj } @@ -232,18 +239,20 @@ type BrowserNewResponse struct { 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. + // DEPRECATED: Use timeout_seconds (up to 72 hours) and Profiles instead. + // + // Deprecated: deprecated 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 + // image defaults apply (1920x1080@25). 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"` @@ -293,18 +302,20 @@ type BrowserGetResponse struct { 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. + // DEPRECATED: Use timeout_seconds (up to 72 hours) and Profiles instead. + // + // Deprecated: deprecated 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 + // image defaults apply (1920x1080@25). 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"` @@ -354,18 +365,20 @@ type BrowserListResponse struct { 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. + // DEPRECATED: Use timeout_seconds (up to 72 hours) and Profiles instead. + // + // Deprecated: deprecated 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 + // image defaults apply (1920x1080@25). 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"` @@ -411,26 +424,25 @@ type BrowserNewParams struct { // mechanisms. Stealth param.Opt[bool] `json:"stealth,omitzero"` // The number of seconds of inactivity before the browser session is terminated. - // Only applicable to non-persistent browsers. Activity includes CDP connections - // and live view connections. Defaults to 60 seconds. Minimum allowed is 10 - // seconds. Maximum allowed is 259200 (72 hours). We check for inactivity every 5 - // seconds, so the actual timeout behavior you will see is +/- 5 seconds around the - // specified value. + // Activity includes CDP connections and live view connections. Defaults to 60 + // seconds. Minimum allowed is 10 seconds. Maximum allowed is 259200 (72 hours). We + // check for inactivity every 5 seconds, so the actual timeout behavior you will + // see is +/- 5 seconds around the 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 []shared.BrowserExtensionParam `json:"extensions,omitzero"` - // Optional persistence configuration for the browser session. + // DEPRECATED: Use timeout_seconds (up to 72 hours) and Profiles instead. 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 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 + // image defaults apply (1920x1080@25). 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"` diff --git a/browserpool.go b/browserpool.go index 4001ea7..f77cd23 100644 --- a/browserpool.go +++ b/browserpool.go @@ -234,11 +234,11 @@ type BrowserPoolRequest struct { // 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 + // image defaults apply (1920x1080@25). 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"` @@ -307,11 +307,11 @@ type BrowserPoolRequestParam struct { // 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 + // image defaults apply (1920x1080@25). 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"` @@ -330,7 +330,7 @@ func (r *BrowserPoolRequestParam) UnmarshalJSON(data []byte) error { // with the same configuration. type BrowserPoolUpdateRequestParam struct { // Whether to discard all idle browsers and rebuild the pool immediately. Defaults - // to true. + // to false. DiscardAllIdle param.Opt[bool] `json:"discard_all_idle,omitzero"` BrowserPoolRequestParam } @@ -360,18 +360,20 @@ type BrowserPoolAcquireResponse struct { 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. + // DEPRECATED: Use timeout_seconds (up to 72 hours) and Profiles instead. + // + // Deprecated: deprecated 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 + // image defaults apply (1920x1080@25). 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"` diff --git a/client.go b/client.go index 1fac9b9..eca243d 100644 --- a/client.go +++ b/client.go @@ -25,6 +25,7 @@ type Client struct { Proxies ProxyService Extensions ExtensionService BrowserPools BrowserPoolService + Agents AgentService } // DefaultClientOptions read from the environment (KERNEL_API_KEY, @@ -57,6 +58,7 @@ func NewClient(opts ...option.RequestOption) (r Client) { r.Proxies = NewProxyService(opts...) r.Extensions = NewExtensionService(opts...) r.BrowserPools = NewBrowserPoolService(opts...) + r.Agents = NewAgentService(opts...) return } diff --git a/client_test.go b/client_test.go index 824eab6..facacc9 100644 --- a/client_test.go +++ b/client_test.go @@ -40,9 +40,7 @@ func TestUserAgentHeader(t *testing.T) { }), ) client.Browsers.New(context.Background(), kernel.BrowserNewParams{ - Persistence: kernel.BrowserPersistenceParam{ - ID: "browser-for-user-1234", - }, + Stealth: kernel.Bool(true), }) if userAgent != fmt.Sprintf("Kernel/Go %s", internal.PackageVersion) { t.Errorf("Expected User-Agent to be correct, but got: %#v", userAgent) @@ -68,9 +66,7 @@ func TestRetryAfter(t *testing.T) { }), ) _, err := client.Browsers.New(context.Background(), kernel.BrowserNewParams{ - Persistence: kernel.BrowserPersistenceParam{ - ID: "browser-for-user-1234", - }, + Stealth: kernel.Bool(true), }) if err == nil { t.Error("Expected there to be a cancel error") @@ -107,9 +103,7 @@ func TestDeleteRetryCountHeader(t *testing.T) { option.WithHeaderDel("X-Stainless-Retry-Count"), ) _, err := client.Browsers.New(context.Background(), kernel.BrowserNewParams{ - Persistence: kernel.BrowserPersistenceParam{ - ID: "browser-for-user-1234", - }, + Stealth: kernel.Bool(true), }) if err == nil { t.Error("Expected there to be a cancel error") @@ -141,9 +135,7 @@ func TestOverwriteRetryCountHeader(t *testing.T) { option.WithHeader("X-Stainless-Retry-Count", "42"), ) _, err := client.Browsers.New(context.Background(), kernel.BrowserNewParams{ - Persistence: kernel.BrowserPersistenceParam{ - ID: "browser-for-user-1234", - }, + Stealth: kernel.Bool(true), }) if err == nil { t.Error("Expected there to be a cancel error") @@ -174,9 +166,7 @@ func TestRetryAfterMs(t *testing.T) { }), ) _, err := client.Browsers.New(context.Background(), kernel.BrowserNewParams{ - Persistence: kernel.BrowserPersistenceParam{ - ID: "browser-for-user-1234", - }, + Stealth: kernel.Bool(true), }) if err == nil { t.Error("Expected there to be a cancel error") @@ -201,9 +191,7 @@ func TestContextCancel(t *testing.T) { cancelCtx, cancel := context.WithCancel(context.Background()) cancel() _, err := client.Browsers.New(cancelCtx, kernel.BrowserNewParams{ - Persistence: kernel.BrowserPersistenceParam{ - ID: "browser-for-user-1234", - }, + Stealth: kernel.Bool(true), }) if err == nil { t.Error("Expected there to be a cancel error") @@ -225,9 +213,7 @@ func TestContextCancelDelay(t *testing.T) { cancelCtx, cancel := context.WithTimeout(context.Background(), 2*time.Millisecond) defer cancel() _, err := client.Browsers.New(cancelCtx, kernel.BrowserNewParams{ - Persistence: kernel.BrowserPersistenceParam{ - ID: "browser-for-user-1234", - }, + Stealth: kernel.Bool(true), }) if err == nil { t.Error("expected there to be a cancel error") @@ -255,9 +241,7 @@ func TestContextDeadline(t *testing.T) { }), ) _, err := client.Browsers.New(deadlineCtx, kernel.BrowserNewParams{ - Persistence: kernel.BrowserPersistenceParam{ - ID: "browser-for-user-1234", - }, + Stealth: kernel.Bool(true), }) if err == nil { t.Error("expected there to be a deadline error") diff --git a/internal/version.go b/internal/version.go index b6325aa..e482b6b 100644 --- a/internal/version.go +++ b/internal/version.go @@ -2,4 +2,4 @@ package internal -const PackageVersion = "0.21.0" // x-release-please-version +const PackageVersion = "0.22.0" // x-release-please-version diff --git a/invocation.go b/invocation.go index 2fdbc66..45b1045 100644 --- a/invocation.go +++ b/invocation.go @@ -517,6 +517,9 @@ type InvocationNewParams struct { // If true, invoke asynchronously. When set, the API responds 202 Accepted with // status "queued". Async param.Opt[bool] `json:"async,omitzero"` + // Timeout in seconds for async invocations (min 10, max 3600). Only applies when + // async is true. + AsyncTimeoutSeconds param.Opt[int64] `json:"async_timeout_seconds,omitzero"` // Input data for the action, sent as a JSON string. Payload param.Opt[string] `json:"payload,omitzero"` paramObj diff --git a/invocation_test.go b/invocation_test.go index c99ca1a..fda50fd 100644 --- a/invocation_test.go +++ b/invocation_test.go @@ -27,11 +27,12 @@ func TestInvocationNewWithOptionalParams(t *testing.T) { option.WithAPIKey("My API Key"), ) _, err := client.Invocations.New(context.TODO(), kernel.InvocationNewParams{ - ActionName: "analyze", - AppName: "my-app", - Version: "1.0.0", - Async: kernel.Bool(true), - Payload: kernel.String(`{"data":"example input"}`), + ActionName: "analyze", + AppName: "my-app", + Version: "1.0.0", + Async: kernel.Bool(true), + AsyncTimeoutSeconds: kernel.Int(600), + Payload: kernel.String(`{"data":"example input"}`), }) if err != nil { var apierr *kernel.Error diff --git a/shared/shared.go b/shared/shared.go index 755810d..3aa9907 100644 --- a/shared/shared.go +++ b/shared/shared.go @@ -148,11 +148,11 @@ func (r *BrowserProfileParam) UnmarshalJSON(data []byte) error { } // 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 +// image defaults apply (1920x1080@25). 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 { @@ -189,11 +189,11 @@ func (r BrowserViewport) ToParam() BrowserViewportParam { } // 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 +// image defaults apply (1920x1080@25). 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 // diff --git a/usage_test.go b/usage_test.go index 302a5b9..5be681f 100644 --- a/usage_test.go +++ b/usage_test.go @@ -25,9 +25,7 @@ func TestUsage(t *testing.T) { option.WithAPIKey("My API Key"), ) browser, err := client.Browsers.New(context.TODO(), kernel.BrowserNewParams{ - Persistence: kernel.BrowserPersistenceParam{ - ID: "browser-for-user-1234", - }, + Stealth: kernel.Bool(true), }) if err != nil { t.Fatalf("err should be nil: %s", err.Error())