diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index d2d60a3..a36746b 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "0.24.0"
+ ".": "0.25.0"
}
\ No newline at end of file
diff --git a/.stats.yml b/.stats.yml
index 99bb6c4..434275e 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 89
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-13214b99e392aab631aa1ca99b6a51a58df81e34156d21b8d639bea779566123.yml
-openapi_spec_hash: a88d175fc3980de3097ac1411d8dcbff
-config_hash: 179f33af31ece83563163d5b3d751d13
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-8d66dbedea5b240936b338809f272568ca84a452fc13dbda835479f2ec068b41.yml
+openapi_spec_hash: 7c499bfce2e996f1fff5e7791cea390e
+config_hash: fcc2db3ed48ab4e8d1b588d31d394a23
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3f01456..fdeefc5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,27 @@
# Changelog
+## 0.25.0 (2026-01-07)
+
+Full Changelog: [v0.24.0...v0.25.0](https://github.com/onkernel/kernel-go-sdk/compare/v0.24.0...v0.25.0)
+
+### Features
+
+* **api:** add health check endpoint for proxies ([c7b8728](https://github.com/onkernel/kernel-go-sdk/commit/c7b8728369482de76bdf143a59f16b0de8bc03bb))
+* **auth:** add auto_login credential flow ([2eec1a1](https://github.com/onkernel/kernel-go-sdk/commit/2eec1a147279fe64a060c2066b30a2f0a13e74ab))
+* Enhance AuthAgentInvocation with step and last activity tracking ([ccb1425](https://github.com/onkernel/kernel-go-sdk/commit/ccb1425ea205fbb18129f341ae36bfc55c70ca64))
+
+
+### Bug Fixes
+
+* skip usage tests that don't work with Prism ([8872193](https://github.com/onkernel/kernel-go-sdk/commit/88721930052bff16639f371a9a7e46e7e34e7ff4))
+
+
+### Chores
+
+* add float64 to valid types for RegisterFieldValidator ([1e23b39](https://github.com/onkernel/kernel-go-sdk/commit/1e23b39aca94dd1a98d6ec6669d2c8b87f8ccf02))
+* **internal:** codegen related update ([e07718d](https://github.com/onkernel/kernel-go-sdk/commit/e07718dbecbbc00c19e9d0725c04297fe48f2121))
+* **internal:** codegen related update ([0320876](https://github.com/onkernel/kernel-go-sdk/commit/0320876c8f64185768a3ad562b9656f96ab7e935))
+
## 0.24.0 (2025-12-17)
Full Changelog: [v0.23.0...v0.24.0](https://github.com/onkernel/kernel-go-sdk/compare/v0.23.0...v0.24.0)
diff --git a/LICENSE b/LICENSE
index b32a077..3b7d20d 100644
--- a/LICENSE
+++ b/LICENSE
@@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
- Copyright 2025 Kernel
+ Copyright 2026 Kernel
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/README.md b/README.md
index 3ff9520..124904d 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.24.0'
+go get -u 'github.com/onkernel/kernel-go-sdk@v0.25.0'
```
diff --git a/agentauth.go b/agentauth.go
index 9a61ac5..e9a7e79 100644
--- a/agentauth.go
+++ b/agentauth.go
@@ -20,7 +20,6 @@ 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/constant"
)
// AgentAuthService contains methods and other services that help with interacting
@@ -68,7 +67,7 @@ func (r *AgentAuthService) Get(ctx context.Context, id string, opts ...option.Re
return
}
-// List auth agents with optional filters for profile_name and target_domain.
+// List auth agents with optional filters for profile_name and domain.
func (r *AgentAuthService) List(ctx context.Context, query AgentAuthListParams, opts ...option.RequestOption) (res *pagination.OffsetPagination[AuthAgent], err error) {
var raw *http.Response
opts = slices.Concat(r.Options, opts)
@@ -86,7 +85,7 @@ func (r *AgentAuthService) List(ctx context.Context, query AgentAuthListParams,
return res, nil
}
-// List auth agents with optional filters for profile_name and target_domain.
+// List auth agents with optional filters for profile_name and domain.
func (r *AgentAuthService) ListAutoPaging(ctx context.Context, query AgentAuthListParams, opts ...option.RequestOption) *pagination.OffsetPaginationAutoPager[AuthAgent] {
return pagination.NewOffsetPaginationAutoPager(r.List(ctx, query, opts...))
}
@@ -108,74 +107,60 @@ func (r *AgentAuthService) Delete(ctx context.Context, id string, opts ...option
return
}
-// Triggers automatic re-authentication for an auth agent using stored credentials.
-// Requires the auth agent to have a linked credential, stored selectors, and
-// login_url. Returns immediately with status indicating whether re-auth was
-// started.
-func (r *AgentAuthService) Reauth(ctx context.Context, id string, opts ...option.RequestOption) (res *ReauthResponse, err error) {
- opts = slices.Concat(r.Options, opts)
- if id == "" {
- err = errors.New("missing required id parameter")
- return
- }
- path := fmt.Sprintf("agents/auth/%s/reauth", id)
- err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, nil, &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"`
+ // Domain for authentication
+ Domain string `json:"domain,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".
+ // Any of "IN_PROGRESS", "SUCCESS", "EXPIRED", "CANCELED", "FAILED".
Status AgentAuthInvocationResponseStatus `json:"status,required"`
- // Target domain for authentication
- TargetDomain string `json:"target_domain,required"`
+ // Current step in the invocation workflow
+ //
+ // Any of "initialized", "discovering", "awaiting_input",
+ // "awaiting_external_action", "submitting", "completed", "expired".
+ Step AgentAuthInvocationResponseStep `json:"step,required"`
+ // The invocation type:
+ //
+ // - login: First-time authentication
+ // - reauth: Re-authentication for previously authenticated agents
+ // - auto_login: Legacy type (no longer created, kept for backward compatibility)
+ //
+ // Any of "login", "auto_login", "reauth".
+ Type AgentAuthInvocationResponseType `json:"type,required"`
+ // Error message explaining why the invocation failed (present when status=FAILED)
+ ErrorMessage string `json:"error_message,nullable"`
+ // Instructions for user when external action is required (present when
+ // step=awaiting_external_action)
+ ExternalActionMessage string `json:"external_action_message,nullable"`
+ // Browser live view URL for debugging the invocation
+ LiveViewURL string `json:"live_view_url,nullable"`
+ // Fields currently awaiting input (present when step=awaiting_input)
+ PendingFields []DiscoveredField `json:"pending_fields,nullable"`
+ // SSO buttons available on the page (present when step=awaiting_input)
+ PendingSSOButtons []AgentAuthInvocationResponsePendingSSOButton `json:"pending_sso_buttons,nullable"`
+ // Names of fields that have been submitted (present when step=submitting or later)
+ SubmittedFields []string `json:"submitted_fields,nullable"`
// 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
+ AppName respjson.Field
+ Domain respjson.Field
+ ExpiresAt respjson.Field
+ Status respjson.Field
+ Step respjson.Field
+ Type respjson.Field
+ ErrorMessage respjson.Field
+ ExternalActionMessage respjson.Field
+ LiveViewURL respjson.Field
+ PendingFields respjson.Field
+ PendingSSOButtons respjson.Field
+ SubmittedFields respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
} `json:"-"`
}
@@ -193,36 +178,68 @@ const (
AgentAuthInvocationResponseStatusSuccess AgentAuthInvocationResponseStatus = "SUCCESS"
AgentAuthInvocationResponseStatusExpired AgentAuthInvocationResponseStatus = "EXPIRED"
AgentAuthInvocationResponseStatusCanceled AgentAuthInvocationResponseStatus = "CANCELED"
+ AgentAuthInvocationResponseStatusFailed AgentAuthInvocationResponseStatus = "FAILED"
+)
+
+// Current step in the invocation workflow
+type AgentAuthInvocationResponseStep string
+
+const (
+ AgentAuthInvocationResponseStepInitialized AgentAuthInvocationResponseStep = "initialized"
+ AgentAuthInvocationResponseStepDiscovering AgentAuthInvocationResponseStep = "discovering"
+ AgentAuthInvocationResponseStepAwaitingInput AgentAuthInvocationResponseStep = "awaiting_input"
+ AgentAuthInvocationResponseStepAwaitingExternalAction AgentAuthInvocationResponseStep = "awaiting_external_action"
+ AgentAuthInvocationResponseStepSubmitting AgentAuthInvocationResponseStep = "submitting"
+ AgentAuthInvocationResponseStepCompleted AgentAuthInvocationResponseStep = "completed"
+ AgentAuthInvocationResponseStepExpired AgentAuthInvocationResponseStep = "expired"
+)
+
+// The invocation type:
+//
+// - login: First-time authentication
+// - reauth: Re-authentication for previously authenticated agents
+// - auto_login: Legacy type (no longer created, kept for backward compatibility)
+type AgentAuthInvocationResponseType string
+
+const (
+ AgentAuthInvocationResponseTypeLogin AgentAuthInvocationResponseType = "login"
+ AgentAuthInvocationResponseTypeAutoLogin AgentAuthInvocationResponseType = "auto_login"
+ AgentAuthInvocationResponseTypeReauth AgentAuthInvocationResponseType = "reauth"
)
-// Response from submit endpoint matching SubmitResult schema
+// An SSO button for signing in with an external identity provider
+type AgentAuthInvocationResponsePendingSSOButton struct {
+ // Visible button text
+ Label string `json:"label,required"`
+ // Identity provider name
+ Provider string `json:"provider,required"`
+ // XPath selector for the button
+ Selector string `json:"selector,required"`
+ // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
+ JSON struct {
+ Label respjson.Field
+ Provider respjson.Field
+ Selector respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// Returns the unmodified JSON received from the API
+func (r AgentAuthInvocationResponsePendingSSOButton) RawJSON() string { return r.JSON.raw }
+func (r *AgentAuthInvocationResponsePendingSSOButton) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
+// Response from submit endpoint - returns immediately after submission is accepted
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"`
+ // Whether the submission was accepted for processing
+ Accepted bool `json:"accepted,required"`
// 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
+ Accepted respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
} `json:"-"`
}
@@ -245,6 +262,10 @@ type AuthAgent struct {
//
// Any of "AUTHENTICATED", "NEEDS_AUTH".
Status AuthAgentStatus `json:"status,required"`
+ // Additional domains that are valid for this auth agent's authentication flow
+ // (besides the primary domain). Useful when login pages redirect to different
+ // domains.
+ AllowedDomains []string `json:"allowed_domains"`
// Whether automatic re-authentication is possible (has credential_id, selectors,
// and login_url)
CanReauth bool `json:"can_reauth"`
@@ -262,6 +283,7 @@ type AuthAgent struct {
Domain respjson.Field
ProfileName respjson.Field
Status respjson.Field
+ AllowedDomains respjson.Field
CanReauth respjson.Field
CredentialID respjson.Field
CredentialName respjson.Field
@@ -288,12 +310,12 @@ const (
// Request to create or find an auth agent
//
-// The properties ProfileName, TargetDomain are required.
+// The properties Domain, ProfileName are required.
type AuthAgentCreateRequestParam struct {
+ // Domain for authentication
+ Domain string `json:"domain,required"`
// Name of the profile to use for this auth agent
ProfileName string `json:"profile_name,required"`
- // Target domain for authentication
- TargetDomain string `json:"target_domain,required"`
// Optional name of an existing credential to use for this auth agent. If provided,
// the credential will be linked to the agent and its values will be used to
// auto-fill the login form on invocation.
@@ -301,6 +323,10 @@ type AuthAgentCreateRequestParam struct {
// Optional login page URL. If provided, will be stored on the agent and used to
// skip discovery in future invocations.
LoginURL param.Opt[string] `json:"login_url,omitzero" format:"uri"`
+ // Additional domains that are valid for this auth agent's authentication flow
+ // (besides the primary domain). Useful when login pages redirect to different
+ // domains.
+ AllowedDomains []string `json:"allowed_domains,omitzero"`
// Optional proxy configuration
Proxy AuthAgentCreateRequestProxyParam `json:"proxy,omitzero"`
paramObj
@@ -350,102 +376,8 @@ func (r *AuthAgentInvocationCreateRequestParam) UnmarshalJSON(data []byte) error
return apijson.UnmarshalRoot(data, r)
}
-// AuthAgentInvocationCreateResponseUnion contains all possible properties and
-// values from [AuthAgentInvocationCreateResponseAlreadyAuthenticated],
-// [AuthAgentInvocationCreateResponseInvocationCreated].
-//
-// Use the [AuthAgentInvocationCreateResponseUnion.AsAny] method to switch on the
-// variant.
-//
-// Use the methods beginning with 'As' to cast the union to one of its variants.
-type AuthAgentInvocationCreateResponseUnion struct {
- // Any of "already_authenticated", "invocation_created".
- Status string `json:"status"`
- // This field is from variant [AuthAgentInvocationCreateResponseInvocationCreated].
- ExpiresAt time.Time `json:"expires_at"`
- // This field is from variant [AuthAgentInvocationCreateResponseInvocationCreated].
- HandoffCode string `json:"handoff_code"`
- // This field is from variant [AuthAgentInvocationCreateResponseInvocationCreated].
- HostedURL string `json:"hosted_url"`
- // This field is from variant [AuthAgentInvocationCreateResponseInvocationCreated].
- InvocationID string `json:"invocation_id"`
- JSON struct {
- Status respjson.Field
- ExpiresAt respjson.Field
- HandoffCode respjson.Field
- HostedURL respjson.Field
- InvocationID respjson.Field
- raw string
- } `json:"-"`
-}
-
-// anyAuthAgentInvocationCreateResponse is implemented by each variant of
-// [AuthAgentInvocationCreateResponseUnion] to add type safety for the return type
-// of [AuthAgentInvocationCreateResponseUnion.AsAny]
-type anyAuthAgentInvocationCreateResponse interface {
- implAuthAgentInvocationCreateResponseUnion()
-}
-
-func (AuthAgentInvocationCreateResponseAlreadyAuthenticated) implAuthAgentInvocationCreateResponseUnion() {
-}
-func (AuthAgentInvocationCreateResponseInvocationCreated) implAuthAgentInvocationCreateResponseUnion() {
-}
-
-// Use the following switch statement to find the correct variant
-//
-// switch variant := AuthAgentInvocationCreateResponseUnion.AsAny().(type) {
-// case kernel.AuthAgentInvocationCreateResponseAlreadyAuthenticated:
-// case kernel.AuthAgentInvocationCreateResponseInvocationCreated:
-// default:
-// fmt.Errorf("no variant present")
-// }
-func (u AuthAgentInvocationCreateResponseUnion) AsAny() anyAuthAgentInvocationCreateResponse {
- switch u.Status {
- case "already_authenticated":
- return u.AsAlreadyAuthenticated()
- case "invocation_created":
- return u.AsInvocationCreated()
- }
- return nil
-}
-
-func (u AuthAgentInvocationCreateResponseUnion) AsAlreadyAuthenticated() (v AuthAgentInvocationCreateResponseAlreadyAuthenticated) {
- apijson.UnmarshalRoot(json.RawMessage(u.JSON.raw), &v)
- return
-}
-
-func (u AuthAgentInvocationCreateResponseUnion) AsInvocationCreated() (v AuthAgentInvocationCreateResponseInvocationCreated) {
- apijson.UnmarshalRoot(json.RawMessage(u.JSON.raw), &v)
- return
-}
-
-// Returns the unmodified JSON received from the API
-func (u AuthAgentInvocationCreateResponseUnion) RawJSON() string { return u.JSON.raw }
-
-func (r *AuthAgentInvocationCreateResponseUnion) UnmarshalJSON(data []byte) error {
- return apijson.UnmarshalRoot(data, r)
-}
-
-// Response when the agent is already authenticated.
-type AuthAgentInvocationCreateResponseAlreadyAuthenticated struct {
- // Indicates the agent is already authenticated and no invocation was created.
- Status constant.AlreadyAuthenticated `json:"status,required"`
- // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
- JSON struct {
- Status respjson.Field
- ExtraFields map[string]respjson.Field
- raw string
- } `json:"-"`
-}
-
-// Returns the unmodified JSON received from the API
-func (r AuthAgentInvocationCreateResponseAlreadyAuthenticated) RawJSON() string { return r.JSON.raw }
-func (r *AuthAgentInvocationCreateResponseAlreadyAuthenticated) UnmarshalJSON(data []byte) error {
- return apijson.UnmarshalRoot(data, r)
-}
-
-// Response when a new invocation was created.
-type AuthAgentInvocationCreateResponseInvocationCreated struct {
+// Response from creating an invocation. Always returns an invocation_id.
+type AuthAgentInvocationCreateResponse struct {
// When the handoff code expires.
ExpiresAt time.Time `json:"expires_at,required" format:"date-time"`
// One-time code for handoff.
@@ -454,26 +386,45 @@ type AuthAgentInvocationCreateResponseInvocationCreated struct {
HostedURL string `json:"hosted_url,required" format:"uri"`
// Unique identifier for the invocation.
InvocationID string `json:"invocation_id,required"`
- // Indicates an invocation was created.
- Status constant.InvocationCreated `json:"status,required"`
+ // The invocation type:
+ //
+ // - login: First-time authentication
+ // - reauth: Re-authentication for previously authenticated agents
+ // - auto_login: Legacy type (no longer created, kept for backward compatibility)
+ //
+ // Any of "login", "auto_login", "reauth".
+ Type AuthAgentInvocationCreateResponseType `json:"type,required"`
// JSON contains metadata for fields, check presence with [respjson.Field.Valid].
JSON struct {
ExpiresAt respjson.Field
HandoffCode respjson.Field
HostedURL respjson.Field
InvocationID respjson.Field
- Status respjson.Field
+ Type respjson.Field
ExtraFields map[string]respjson.Field
raw string
} `json:"-"`
}
// Returns the unmodified JSON received from the API
-func (r AuthAgentInvocationCreateResponseInvocationCreated) RawJSON() string { return r.JSON.raw }
-func (r *AuthAgentInvocationCreateResponseInvocationCreated) UnmarshalJSON(data []byte) error {
+func (r AuthAgentInvocationCreateResponse) RawJSON() string { return r.JSON.raw }
+func (r *AuthAgentInvocationCreateResponse) UnmarshalJSON(data []byte) error {
return apijson.UnmarshalRoot(data, r)
}
+// The invocation type:
+//
+// - login: First-time authentication
+// - reauth: Re-authentication for previously authenticated agents
+// - auto_login: Legacy type (no longer created, kept for backward compatibility)
+type AuthAgentInvocationCreateResponseType string
+
+const (
+ AuthAgentInvocationCreateResponseTypeLogin AuthAgentInvocationCreateResponseType = "login"
+ AuthAgentInvocationCreateResponseTypeAutoLogin AuthAgentInvocationCreateResponseType = "auto_login"
+ AuthAgentInvocationCreateResponseTypeReauth AuthAgentInvocationCreateResponseType = "reauth"
+)
+
// A discovered form field
type DiscoveredField struct {
// Field label
@@ -484,7 +435,7 @@ type DiscoveredField struct {
Selector string `json:"selector,required"`
// Field type
//
- // Any of "text", "email", "password", "tel", "number", "url", "code".
+ // Any of "text", "email", "password", "tel", "number", "url", "code", "totp".
Type DiscoveredFieldType `json:"type,required"`
// Field placeholder
Placeholder string `json:"placeholder"`
@@ -520,41 +471,7 @@ const (
DiscoveredFieldTypeNumber DiscoveredFieldType = "number"
DiscoveredFieldTypeURL DiscoveredFieldType = "url"
DiscoveredFieldTypeCode DiscoveredFieldType = "code"
-)
-
-// Response from triggering re-authentication
-type ReauthResponse struct {
- // Result of the re-authentication attempt
- //
- // Any of "reauth_started", "already_authenticated", "cannot_reauth".
- Status ReauthResponseStatus `json:"status,required"`
- // ID of the re-auth invocation if one was created
- InvocationID string `json:"invocation_id"`
- // Human-readable description of the result
- Message string `json:"message"`
- // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
- JSON struct {
- Status respjson.Field
- InvocationID respjson.Field
- Message respjson.Field
- ExtraFields map[string]respjson.Field
- raw string
- } `json:"-"`
-}
-
-// Returns the unmodified JSON received from the API
-func (r ReauthResponse) RawJSON() string { return r.JSON.raw }
-func (r *ReauthResponse) UnmarshalJSON(data []byte) error {
- return apijson.UnmarshalRoot(data, r)
-}
-
-// Result of the re-authentication attempt
-type ReauthResponseStatus string
-
-const (
- ReauthResponseStatusReauthStarted ReauthResponseStatus = "reauth_started"
- ReauthResponseStatusAlreadyAuthenticated ReauthResponseStatus = "already_authenticated"
- ReauthResponseStatusCannotReauth ReauthResponseStatus = "cannot_reauth"
+ DiscoveredFieldTypeTotp DiscoveredFieldType = "totp"
)
type AgentAuthNewParams struct {
@@ -571,14 +488,14 @@ func (r *AgentAuthNewParams) UnmarshalJSON(data []byte) error {
}
type AgentAuthListParams struct {
+ // Filter by domain
+ Domain param.Opt[string] `query:"domain,omitzero" json:"-"`
// Maximum number of results to return
Limit param.Opt[int64] `query:"limit,omitzero" json:"-"`
// Number of results to skip
Offset param.Opt[int64] `query:"offset,omitzero" json:"-"`
// Filter by profile name
ProfileName param.Opt[string] `query:"profile_name,omitzero" json:"-"`
- // Filter by target domain
- TargetDomain param.Opt[string] `query:"target_domain,omitzero" json:"-"`
paramObj
}
diff --git a/agentauth_test.go b/agentauth_test.go
index 6368098..72a1a95 100644
--- a/agentauth_test.go
+++ b/agentauth_test.go
@@ -28,8 +28,9 @@ func TestAgentAuthNewWithOptionalParams(t *testing.T) {
)
_, err := client.Agents.Auth.New(context.TODO(), kernel.AgentAuthNewParams{
AuthAgentCreateRequest: kernel.AuthAgentCreateRequestParam{
+ Domain: "netflix.com",
ProfileName: "user-123",
- TargetDomain: "netflix.com",
+ AllowedDomains: []string{"login.netflix.com", "auth.netflix.com"},
CredentialName: kernel.String("my-netflix-login"),
LoginURL: kernel.String("https://netflix.com/login"),
Proxy: kernel.AuthAgentCreateRequestProxyParam{
@@ -83,10 +84,10 @@ func TestAgentAuthListWithOptionalParams(t *testing.T) {
option.WithAPIKey("My API Key"),
)
_, err := client.Agents.Auth.List(context.TODO(), kernel.AgentAuthListParams{
- Limit: kernel.Int(100),
- Offset: kernel.Int(0),
- ProfileName: kernel.String("profile_name"),
- TargetDomain: kernel.String("target_domain"),
+ Domain: kernel.String("domain"),
+ Limit: kernel.Int(100),
+ Offset: kernel.Int(0),
+ ProfileName: kernel.String("profile_name"),
})
if err != nil {
var apierr *kernel.Error
@@ -119,26 +120,3 @@ func TestAgentAuthDelete(t *testing.T) {
t.Fatalf("err should be nil: %s", err.Error())
}
}
-
-func TestAgentAuthReauth(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.Reauth(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())
- }
-}
diff --git a/agentauthinvocation.go b/agentauthinvocation.go
index 878146f..c8d5ec8 100644
--- a/agentauthinvocation.go
+++ b/agentauthinvocation.go
@@ -40,15 +40,15 @@ func NewAgentAuthInvocationService(opts ...option.RequestOption) (r AgentAuthInv
// Creates a new authentication invocation for the specified auth agent. This
// starts the auth flow and returns a hosted URL for the user to complete
// authentication.
-func (r *AgentAuthInvocationService) New(ctx context.Context, body AgentAuthInvocationNewParams, opts ...option.RequestOption) (res *AuthAgentInvocationCreateResponseUnion, err error) {
+func (r *AgentAuthInvocationService) New(ctx context.Context, body AgentAuthInvocationNewParams, opts ...option.RequestOption) (res *AuthAgentInvocationCreateResponse, err error) {
opts = slices.Concat(r.Options, opts)
path := "agents/auth/invocations"
err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, body, &res, 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.
+// Returns invocation details including status, app_name, and domain. Supports both
+// API key and JWT (from exchange endpoint) 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 == "" {
@@ -60,20 +60,6 @@ func (r *AgentAuthInvocationService) Get(ctx context.Context, invocationID strin
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) {
@@ -87,8 +73,9 @@ func (r *AgentAuthInvocationService) Exchange(ctx context.Context, invocationID
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.
+// Submits field values for the discovered login form. Returns immediately after
+// submission is accepted. Poll the invocation endpoint to track progress and get
+// results.
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 == "" {
@@ -134,21 +121,6 @@ func (r *AgentAuthInvocationNewParams) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, &r.AuthAgentInvocationCreateRequest)
}
-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"`
@@ -164,15 +136,52 @@ func (r *AgentAuthInvocationExchangeParams) UnmarshalJSON(data []byte) error {
}
type AgentAuthInvocationSubmitParams struct {
+
+ //
+ // Request body variants
+ //
+
+ // This field is a request body variant, only one variant field can be set.
+ OfFieldValues *AgentAuthInvocationSubmitParamsBodyFieldValues `json:",inline"`
+ // This field is a request body variant, only one variant field can be set.
+ OfSSOButton *AgentAuthInvocationSubmitParamsBodySSOButton `json:",inline"`
+
+ paramObj
+}
+
+func (u AgentAuthInvocationSubmitParams) MarshalJSON() ([]byte, error) {
+ return param.MarshalUnion(u, u.OfFieldValues, u.OfSSOButton)
+}
+func (r *AgentAuthInvocationSubmitParams) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
+// The property FieldValues is required.
+type AgentAuthInvocationSubmitParamsBodyFieldValues 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
+func (r AgentAuthInvocationSubmitParamsBodyFieldValues) MarshalJSON() (data []byte, err error) {
+ type shadow AgentAuthInvocationSubmitParamsBodyFieldValues
return param.MarshalObject(r, (*shadow)(&r))
}
-func (r *AgentAuthInvocationSubmitParams) UnmarshalJSON(data []byte) error {
+func (r *AgentAuthInvocationSubmitParamsBodyFieldValues) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
+// The property SSOButton is required.
+type AgentAuthInvocationSubmitParamsBodySSOButton struct {
+ // Selector of SSO button to click
+ SSOButton string `json:"sso_button,required"`
+ paramObj
+}
+
+func (r AgentAuthInvocationSubmitParamsBodySSOButton) MarshalJSON() (data []byte, err error) {
+ type shadow AgentAuthInvocationSubmitParamsBodySSOButton
+ return param.MarshalObject(r, (*shadow)(&r))
+}
+func (r *AgentAuthInvocationSubmitParamsBodySSOButton) UnmarshalJSON(data []byte) error {
return apijson.UnmarshalRoot(data, r)
}
diff --git a/agentauthinvocation_test.go b/agentauthinvocation_test.go
index c0f6090..1e7186a 100644
--- a/agentauthinvocation_test.go
+++ b/agentauthinvocation_test.go
@@ -64,35 +64,6 @@ func TestAgentAuthInvocationGet(t *testing.T) {
}
}
-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"
@@ -139,9 +110,11 @@ func TestAgentAuthInvocationSubmit(t *testing.T) {
context.TODO(),
"invocation_id",
kernel.AgentAuthInvocationSubmitParams{
- FieldValues: map[string]string{
- "email": "user@example.com",
- "password": "********",
+ OfFieldValues: &kernel.AgentAuthInvocationSubmitParamsBodyFieldValues{
+ FieldValues: map[string]string{
+ "email": "user@example.com",
+ "password": "********",
+ },
},
},
)
diff --git a/api.md b/api.md
index 44374eb..e849839 100644
--- a/api.md
+++ b/api.md
@@ -205,6 +205,7 @@ Response Types:
- kernel.ProxyNewResponse
- kernel.ProxyGetResponse
- kernel.ProxyListResponse
+- kernel.ProxyCheckResponse
Methods:
@@ -212,6 +213,7 @@ Methods:
- client.Proxies.Get(ctx context.Context, id string) (kernel.ProxyGetResponse, error)
- client.Proxies.List(ctx context.Context) ([]kernel.ProxyListResponse, error)
- client.Proxies.Delete(ctx context.Context, id string) error
+- client.Proxies.Check(ctx context.Context, id string) (kernel.ProxyCheckResponse, error)
# Extensions
@@ -257,13 +259,11 @@ Params Types:
Response Types:
-- kernel.AgentAuthDiscoverResponse
- kernel.AgentAuthInvocationResponse
- kernel.AgentAuthSubmitResponse
- kernel.AuthAgent
-- kernel.AuthAgentInvocationCreateResponseUnion
+- kernel.AuthAgentInvocationCreateResponse
- kernel.DiscoveredField
-- kernel.ReauthResponse
Methods:
@@ -271,7 +271,6 @@ Methods:
- client.Agents.Auth.Get(ctx context.Context, id string) (kernel.AuthAgent, error)
- client.Agents.Auth.List(ctx context.Context, query kernel.AgentAuthListParams) (pagination.OffsetPagination[kernel.AuthAgent], error)
- client.Agents.Auth.Delete(ctx context.Context, id string) error
-- client.Agents.Auth.Reauth(ctx context.Context, id string) (kernel.ReauthResponse, error)
### Invocations
@@ -281,9 +280,8 @@ Response Types:
Methods:
-- client.Agents.Auth.Invocations.New(ctx context.Context, body kernel.AgentAuthInvocationNewParams) (kernel.AuthAgentInvocationCreateResponseUnion, error)
+- client.Agents.Auth.Invocations.New(ctx context.Context, body kernel.AgentAuthInvocationNewParams) (kernel.AuthAgentInvocationCreateResponse, error)
- 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)
@@ -297,11 +295,13 @@ Params Types:
Response Types:
- kernel.Credential
+- kernel.CredentialTotpCodeResponse
Methods:
- client.Credentials.New(ctx context.Context, body kernel.CredentialNewParams) (kernel.Credential, error)
-- client.Credentials.Get(ctx context.Context, id string) (kernel.Credential, error)
-- client.Credentials.Update(ctx context.Context, id string, body kernel.CredentialUpdateParams) (kernel.Credential, error)
+- client.Credentials.Get(ctx context.Context, idOrName string) (kernel.Credential, error)
+- client.Credentials.Update(ctx context.Context, idOrName string, body kernel.CredentialUpdateParams) (kernel.Credential, error)
- client.Credentials.List(ctx context.Context, query kernel.CredentialListParams) (pagination.OffsetPagination[kernel.Credential], error)
-- client.Credentials.Delete(ctx context.Context, id string) error
+- client.Credentials.Delete(ctx context.Context, idOrName string) error
+- client.Credentials.TotpCode(ctx context.Context, idOrName string) (kernel.CredentialTotpCodeResponse, error)
diff --git a/credential.go b/credential.go
index 2eb2c0c..774a7d2 100644
--- a/credential.go
+++ b/credential.go
@@ -41,8 +41,7 @@ func NewCredentialService(opts ...option.RequestOption) (r CredentialService) {
return
}
-// Create a new credential for storing login information. Values are encrypted at
-// rest.
+// Create a new credential for storing login information.
func (r *CredentialService) New(ctx context.Context, body CredentialNewParams, opts ...option.RequestOption) (res *Credential, err error) {
opts = slices.Concat(r.Options, opts)
path := "credentials"
@@ -50,26 +49,27 @@ func (r *CredentialService) New(ctx context.Context, body CredentialNewParams, o
return
}
-// Retrieve a credential by its ID. Credential values are not returned.
-func (r *CredentialService) Get(ctx context.Context, id string, opts ...option.RequestOption) (res *Credential, err error) {
+// Retrieve a credential by its ID or name. Credential values are not returned.
+func (r *CredentialService) Get(ctx context.Context, idOrName string, opts ...option.RequestOption) (res *Credential, err error) {
opts = slices.Concat(r.Options, opts)
- if id == "" {
- err = errors.New("missing required id parameter")
+ if idOrName == "" {
+ err = errors.New("missing required id_or_name parameter")
return
}
- path := fmt.Sprintf("credentials/%s", id)
+ path := fmt.Sprintf("credentials/%s", idOrName)
err = requestconfig.ExecuteNewRequest(ctx, http.MethodGet, path, nil, &res, opts...)
return
}
-// Update a credential's name or values. Values are encrypted at rest.
-func (r *CredentialService) Update(ctx context.Context, id string, body CredentialUpdateParams, opts ...option.RequestOption) (res *Credential, err error) {
+// Update a credential's name or values. When values are provided, they are merged
+// with existing values (new keys are added, existing keys are overwritten).
+func (r *CredentialService) Update(ctx context.Context, idOrName string, body CredentialUpdateParams, opts ...option.RequestOption) (res *Credential, err error) {
opts = slices.Concat(r.Options, opts)
- if id == "" {
- err = errors.New("missing required id parameter")
+ if idOrName == "" {
+ err = errors.New("missing required id_or_name parameter")
return
}
- path := fmt.Sprintf("credentials/%s", id)
+ path := fmt.Sprintf("credentials/%s", idOrName)
err = requestconfig.ExecuteNewRequest(ctx, http.MethodPatch, path, body, &res, opts...)
return
}
@@ -99,19 +99,33 @@ func (r *CredentialService) ListAutoPaging(ctx context.Context, query Credential
return pagination.NewOffsetPaginationAutoPager(r.List(ctx, query, opts...))
}
-// Delete a credential by its ID.
-func (r *CredentialService) Delete(ctx context.Context, id string, opts ...option.RequestOption) (err error) {
+// Delete a credential by its ID or name.
+func (r *CredentialService) Delete(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 id == "" {
- err = errors.New("missing required id parameter")
+ if idOrName == "" {
+ err = errors.New("missing required id_or_name parameter")
return
}
- path := fmt.Sprintf("credentials/%s", id)
+ path := fmt.Sprintf("credentials/%s", idOrName)
err = requestconfig.ExecuteNewRequest(ctx, http.MethodDelete, path, nil, nil, opts...)
return
}
+// Returns the current 6-digit TOTP code for a credential with a configured
+// totp_secret. Use this to complete 2FA setup on sites or when you need a fresh
+// code.
+func (r *CredentialService) TotpCode(ctx context.Context, idOrName string, opts ...option.RequestOption) (res *CredentialTotpCodeResponse, err error) {
+ opts = slices.Concat(r.Options, opts)
+ if idOrName == "" {
+ err = errors.New("missing required id_or_name parameter")
+ return
+ }
+ path := fmt.Sprintf("credentials/%s/totp-code", idOrName)
+ err = requestconfig.ExecuteNewRequest(ctx, http.MethodGet, path, nil, &res, opts...)
+ return
+}
+
// Request to create a new credential
//
// The properties Domain, Name, Values are required.
@@ -122,6 +136,14 @@ type CreateCredentialRequestParam struct {
Name string `json:"name,required"`
// Field name to value mapping (e.g., username, password)
Values map[string]string `json:"values,omitzero,required"`
+ // If set, indicates this credential should be used with the specified SSO provider
+ // (e.g., google, github, microsoft). When the target site has a matching SSO
+ // button, it will be clicked first before filling credential values on the
+ // identity provider's login page.
+ SSOProvider param.Opt[string] `json:"sso_provider,omitzero"`
+ // Base32-encoded TOTP secret for generating one-time passwords. Used for automatic
+ // 2FA during login.
+ TotpSecret param.Opt[string] `json:"totp_secret,omitzero"`
paramObj
}
@@ -145,15 +167,31 @@ type Credential struct {
Name string `json:"name,required"`
// When the credential was last updated
UpdatedAt time.Time `json:"updated_at,required" format:"date-time"`
+ // Whether this credential has a TOTP secret configured for automatic 2FA
+ HasTotpSecret bool `json:"has_totp_secret"`
+ // If set, indicates this credential should be used with the specified SSO provider
+ // (e.g., google, github, microsoft). When the target site has a matching SSO
+ // button, it will be clicked first before filling credential values on the
+ // identity provider's login page.
+ SSOProvider string `json:"sso_provider,nullable"`
+ // Current 6-digit TOTP code. Only included in create/update responses when
+ // totp_secret was just set.
+ TotpCode string `json:"totp_code"`
+ // When the totp_code expires. Only included when totp_code is present.
+ TotpCodeExpiresAt time.Time `json:"totp_code_expires_at" format:"date-time"`
// JSON contains metadata for fields, check presence with [respjson.Field.Valid].
JSON struct {
- ID respjson.Field
- CreatedAt respjson.Field
- Domain respjson.Field
- Name respjson.Field
- UpdatedAt respjson.Field
- ExtraFields map[string]respjson.Field
- raw string
+ ID respjson.Field
+ CreatedAt respjson.Field
+ Domain respjson.Field
+ Name respjson.Field
+ UpdatedAt respjson.Field
+ HasTotpSecret respjson.Field
+ SSOProvider respjson.Field
+ TotpCode respjson.Field
+ TotpCodeExpiresAt respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
} `json:"-"`
}
@@ -165,10 +203,16 @@ func (r *Credential) UnmarshalJSON(data []byte) error {
// Request to update an existing credential
type UpdateCredentialRequestParam struct {
+ // If set, indicates this credential should be used with the specified SSO
+ // provider. Set to empty string or null to remove.
+ SSOProvider param.Opt[string] `json:"sso_provider,omitzero"`
// New name for the credential
Name param.Opt[string] `json:"name,omitzero"`
- // Field name to value mapping (e.g., username, password). Replaces all existing
- // values.
+ // Base32-encoded TOTP secret for generating one-time passwords. Spaces and
+ // formatting are automatically normalized. Set to empty string to remove.
+ TotpSecret param.Opt[string] `json:"totp_secret,omitzero"`
+ // Field name to value mapping. Values are merged with existing values (new keys
+ // added, existing keys overwritten).
Values map[string]string `json:"values,omitzero"`
paramObj
}
@@ -181,6 +225,26 @@ func (r *UpdateCredentialRequestParam) UnmarshalJSON(data []byte) error {
return apijson.UnmarshalRoot(data, r)
}
+type CredentialTotpCodeResponse struct {
+ // Current 6-digit TOTP code
+ Code string `json:"code,required"`
+ // When this code expires (ISO 8601 timestamp)
+ ExpiresAt time.Time `json:"expires_at,required" format:"date-time"`
+ // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
+ JSON struct {
+ Code respjson.Field
+ ExpiresAt respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// Returns the unmodified JSON received from the API
+func (r CredentialTotpCodeResponse) RawJSON() string { return r.JSON.raw }
+func (r *CredentialTotpCodeResponse) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
type CredentialNewParams struct {
// Request to create a new credential
CreateCredentialRequest CreateCredentialRequestParam
diff --git a/credential_test.go b/credential_test.go
index 810805f..3e66392 100644
--- a/credential_test.go
+++ b/credential_test.go
@@ -13,7 +13,7 @@ import (
"github.com/onkernel/kernel-go-sdk/option"
)
-func TestCredentialNew(t *testing.T) {
+func TestCredentialNewWithOptionalParams(t *testing.T) {
t.Skip("Prism tests are disabled")
baseURL := "http://localhost:4010"
if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok {
@@ -34,6 +34,8 @@ func TestCredentialNew(t *testing.T) {
"username": "user@example.com",
"password": "mysecretpassword",
},
+ SSOProvider: kernel.String("google"),
+ TotpSecret: kernel.String("JBSWY3DPEHPK3PXP"),
},
})
if err != nil {
@@ -58,7 +60,7 @@ func TestCredentialGet(t *testing.T) {
option.WithBaseURL(baseURL),
option.WithAPIKey("My API Key"),
)
- _, err := client.Credentials.Get(context.TODO(), "id")
+ _, err := client.Credentials.Get(context.TODO(), "id_or_name")
if err != nil {
var apierr *kernel.Error
if errors.As(err, &apierr) {
@@ -83,10 +85,12 @@ func TestCredentialUpdateWithOptionalParams(t *testing.T) {
)
_, err := client.Credentials.Update(
context.TODO(),
- "id",
+ "id_or_name",
kernel.CredentialUpdateParams{
UpdateCredentialRequest: kernel.UpdateCredentialRequestParam{
- Name: kernel.String("my-updated-login"),
+ Name: kernel.String("my-updated-login"),
+ SSOProvider: kernel.String("google"),
+ TotpSecret: kernel.String("JBSWY3DPEHPK3PXP"),
Values: map[string]string{
"username": "user@example.com",
"password": "newpassword",
@@ -143,7 +147,30 @@ func TestCredentialDelete(t *testing.T) {
option.WithBaseURL(baseURL),
option.WithAPIKey("My API Key"),
)
- err := client.Credentials.Delete(context.TODO(), "id")
+ err := client.Credentials.Delete(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 TestCredentialTotpCode(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.Credentials.TotpCode(context.TODO(), "id_or_name")
if err != nil {
var apierr *kernel.Error
if errors.As(err, &apierr) {
diff --git a/internal/apijson/enum.go b/internal/apijson/enum.go
index 18b218a..5bef11c 100644
--- a/internal/apijson/enum.go
+++ b/internal/apijson/enum.go
@@ -29,7 +29,7 @@ type validatorFunc func(reflect.Value) exactness
var validators sync.Map
var validationRegistry = map[reflect.Type][]validationEntry{}
-func RegisterFieldValidator[T any, V string | bool | int](fieldName string, values ...V) {
+func RegisterFieldValidator[T any, V string | bool | int | float64](fieldName string, values ...V) {
var t T
parentType := reflect.TypeOf(t)
diff --git a/internal/version.go b/internal/version.go
index 5f694dc..de6cef8 100644
--- a/internal/version.go
+++ b/internal/version.go
@@ -2,4 +2,4 @@
package internal
-const PackageVersion = "0.24.0" // x-release-please-version
+const PackageVersion = "0.25.0" // x-release-please-version
diff --git a/proxy.go b/proxy.go
index 5d254e6..dfb074e 100644
--- a/proxy.go
+++ b/proxy.go
@@ -78,6 +78,18 @@ func (r *ProxyService) Delete(ctx context.Context, id string, opts ...option.Req
return
}
+// Run a health check on the proxy to verify it's working.
+func (r *ProxyService) Check(ctx context.Context, id string, opts ...option.RequestOption) (res *ProxyCheckResponse, err error) {
+ opts = slices.Concat(r.Options, opts)
+ if id == "" {
+ err = errors.New("missing required id parameter")
+ return
+ }
+ path := fmt.Sprintf("proxies/%s/check", id)
+ err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, nil, &res, opts...)
+ return
+}
+
// Configuration for routing traffic through a proxy.
type ProxyNewResponse struct {
// Proxy type to use. In terms of quality for avoiding bot-detection, from best to
@@ -942,6 +954,294 @@ const (
ProxyListResponseStatusUnavailable ProxyListResponseStatus = "unavailable"
)
+// Configuration for routing traffic through a proxy.
+type ProxyCheckResponse struct {
+ // Proxy type to use. In terms of quality for avoiding bot-detection, from best to
+ // worst: `mobile` > `residential` > `isp` > `datacenter`.
+ //
+ // Any of "datacenter", "isp", "residential", "mobile", "custom".
+ Type ProxyCheckResponseType `json:"type,required"`
+ ID string `json:"id"`
+ // Configuration specific to the selected proxy `type`.
+ Config ProxyCheckResponseConfigUnion `json:"config"`
+ // Timestamp of the last health check performed on this proxy.
+ LastChecked time.Time `json:"last_checked" format:"date-time"`
+ // Readable name of the proxy.
+ Name string `json:"name"`
+ // Protocol to use for the proxy connection.
+ //
+ // Any of "http", "https".
+ Protocol ProxyCheckResponseProtocol `json:"protocol"`
+ // Current health status of the proxy.
+ //
+ // Any of "available", "unavailable".
+ Status ProxyCheckResponseStatus `json:"status"`
+ // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
+ JSON struct {
+ Type respjson.Field
+ ID respjson.Field
+ Config respjson.Field
+ LastChecked respjson.Field
+ Name respjson.Field
+ Protocol respjson.Field
+ Status respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// Returns the unmodified JSON received from the API
+func (r ProxyCheckResponse) RawJSON() string { return r.JSON.raw }
+func (r *ProxyCheckResponse) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
+// Proxy type to use. In terms of quality for avoiding bot-detection, from best to
+// worst: `mobile` > `residential` > `isp` > `datacenter`.
+type ProxyCheckResponseType string
+
+const (
+ ProxyCheckResponseTypeDatacenter ProxyCheckResponseType = "datacenter"
+ ProxyCheckResponseTypeIsp ProxyCheckResponseType = "isp"
+ ProxyCheckResponseTypeResidential ProxyCheckResponseType = "residential"
+ ProxyCheckResponseTypeMobile ProxyCheckResponseType = "mobile"
+ ProxyCheckResponseTypeCustom ProxyCheckResponseType = "custom"
+)
+
+// ProxyCheckResponseConfigUnion contains all possible properties and values from
+// [ProxyCheckResponseConfigDatacenterProxyConfig],
+// [ProxyCheckResponseConfigIspProxyConfig],
+// [ProxyCheckResponseConfigResidentialProxyConfig],
+// [ProxyCheckResponseConfigMobileProxyConfig],
+// [ProxyCheckResponseConfigCustomProxyConfig].
+//
+// Use the methods beginning with 'As' to cast the union to one of its variants.
+type ProxyCheckResponseConfigUnion struct {
+ Country string `json:"country"`
+ Asn string `json:"asn"`
+ City string `json:"city"`
+ // This field is from variant [ProxyCheckResponseConfigResidentialProxyConfig].
+ Os string `json:"os"`
+ State string `json:"state"`
+ Zip string `json:"zip"`
+ // This field is from variant [ProxyCheckResponseConfigMobileProxyConfig].
+ Carrier string `json:"carrier"`
+ // This field is from variant [ProxyCheckResponseConfigCustomProxyConfig].
+ Host string `json:"host"`
+ // This field is from variant [ProxyCheckResponseConfigCustomProxyConfig].
+ Port int64 `json:"port"`
+ // This field is from variant [ProxyCheckResponseConfigCustomProxyConfig].
+ HasPassword bool `json:"has_password"`
+ // This field is from variant [ProxyCheckResponseConfigCustomProxyConfig].
+ Username string `json:"username"`
+ JSON struct {
+ Country respjson.Field
+ Asn respjson.Field
+ City respjson.Field
+ Os respjson.Field
+ State respjson.Field
+ Zip respjson.Field
+ Carrier respjson.Field
+ Host respjson.Field
+ Port respjson.Field
+ HasPassword respjson.Field
+ Username respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+func (u ProxyCheckResponseConfigUnion) AsProxyCheckResponseConfigDatacenterProxyConfig() (v ProxyCheckResponseConfigDatacenterProxyConfig) {
+ apijson.UnmarshalRoot(json.RawMessage(u.JSON.raw), &v)
+ return
+}
+
+func (u ProxyCheckResponseConfigUnion) AsProxyCheckResponseConfigIspProxyConfig() (v ProxyCheckResponseConfigIspProxyConfig) {
+ apijson.UnmarshalRoot(json.RawMessage(u.JSON.raw), &v)
+ return
+}
+
+func (u ProxyCheckResponseConfigUnion) AsProxyCheckResponseConfigResidentialProxyConfig() (v ProxyCheckResponseConfigResidentialProxyConfig) {
+ apijson.UnmarshalRoot(json.RawMessage(u.JSON.raw), &v)
+ return
+}
+
+func (u ProxyCheckResponseConfigUnion) AsProxyCheckResponseConfigMobileProxyConfig() (v ProxyCheckResponseConfigMobileProxyConfig) {
+ apijson.UnmarshalRoot(json.RawMessage(u.JSON.raw), &v)
+ return
+}
+
+func (u ProxyCheckResponseConfigUnion) AsProxyCheckResponseConfigCustomProxyConfig() (v ProxyCheckResponseConfigCustomProxyConfig) {
+ apijson.UnmarshalRoot(json.RawMessage(u.JSON.raw), &v)
+ return
+}
+
+// Returns the unmodified JSON received from the API
+func (u ProxyCheckResponseConfigUnion) RawJSON() string { return u.JSON.raw }
+
+func (r *ProxyCheckResponseConfigUnion) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
+// Configuration for a datacenter proxy.
+type ProxyCheckResponseConfigDatacenterProxyConfig struct {
+ // ISO 3166 country code. Defaults to US if not provided.
+ Country string `json:"country"`
+ // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
+ JSON struct {
+ Country respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// Returns the unmodified JSON received from the API
+func (r ProxyCheckResponseConfigDatacenterProxyConfig) RawJSON() string { return r.JSON.raw }
+func (r *ProxyCheckResponseConfigDatacenterProxyConfig) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
+// Configuration for an ISP proxy.
+type ProxyCheckResponseConfigIspProxyConfig struct {
+ // ISO 3166 country code. Defaults to US if not provided.
+ Country string `json:"country"`
+ // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
+ JSON struct {
+ Country respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// Returns the unmodified JSON received from the API
+func (r ProxyCheckResponseConfigIspProxyConfig) RawJSON() string { return r.JSON.raw }
+func (r *ProxyCheckResponseConfigIspProxyConfig) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
+// Configuration for residential proxies.
+type ProxyCheckResponseConfigResidentialProxyConfig struct {
+ // Autonomous system number. See https://bgp.potaroo.net/cidr/autnums.html
+ Asn string `json:"asn"`
+ // City name (no spaces, e.g. `sanfrancisco`). If provided, `country` must also be
+ // provided.
+ City string `json:"city"`
+ // ISO 3166 country code.
+ Country string `json:"country"`
+ // Operating system of the residential device.
+ //
+ // Any of "windows", "macos", "android".
+ //
+ // Deprecated: deprecated
+ Os string `json:"os"`
+ // Two-letter state code.
+ State string `json:"state"`
+ // US ZIP code.
+ Zip string `json:"zip"`
+ // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
+ JSON struct {
+ Asn respjson.Field
+ City respjson.Field
+ Country respjson.Field
+ Os respjson.Field
+ State respjson.Field
+ Zip respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// Returns the unmodified JSON received from the API
+func (r ProxyCheckResponseConfigResidentialProxyConfig) RawJSON() string { return r.JSON.raw }
+func (r *ProxyCheckResponseConfigResidentialProxyConfig) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
+// Configuration for mobile proxies.
+type ProxyCheckResponseConfigMobileProxyConfig struct {
+ // Autonomous system number. See https://bgp.potaroo.net/cidr/autnums.html
+ Asn string `json:"asn"`
+ // Mobile carrier.
+ //
+ // Any of "a1", "aircel", "airtel", "att", "celcom", "chinamobile", "claro",
+ // "comcast", "cox", "digi", "dt", "docomo", "dtac", "etisalat", "idea",
+ // "kyivstar", "meo", "megafon", "mtn", "mtnza", "mts", "optus", "orange", "qwest",
+ // "reliance_jio", "robi", "sprint", "telefonica", "telstra", "tmobile", "tigo",
+ // "tim", "verizon", "vimpelcom", "vodacomza", "vodafone", "vivo", "zain",
+ // "vivabo", "telenormyanmar", "kcelljsc", "swisscom", "singtel", "asiacell",
+ // "windit", "cellc", "ooredoo", "drei", "umobile", "cableone", "proximus",
+ // "tele2", "mobitel", "o2", "bouygues", "free", "sfr", "digicel".
+ Carrier string `json:"carrier"`
+ // City name (no spaces, e.g. `sanfrancisco`). If provided, `country` must also be
+ // provided.
+ City string `json:"city"`
+ // ISO 3166 country code
+ Country string `json:"country"`
+ // Two-letter state code.
+ State string `json:"state"`
+ // US ZIP code.
+ Zip string `json:"zip"`
+ // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
+ JSON struct {
+ Asn respjson.Field
+ Carrier respjson.Field
+ City respjson.Field
+ Country respjson.Field
+ State respjson.Field
+ Zip respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// Returns the unmodified JSON received from the API
+func (r ProxyCheckResponseConfigMobileProxyConfig) RawJSON() string { return r.JSON.raw }
+func (r *ProxyCheckResponseConfigMobileProxyConfig) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
+// Configuration for a custom proxy (e.g., private proxy server).
+type ProxyCheckResponseConfigCustomProxyConfig struct {
+ // Proxy host address or IP.
+ Host string `json:"host,required"`
+ // Proxy port.
+ Port int64 `json:"port,required"`
+ // Whether the proxy has a password.
+ HasPassword bool `json:"has_password"`
+ // Username for proxy authentication.
+ Username string `json:"username"`
+ // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
+ JSON struct {
+ Host respjson.Field
+ Port respjson.Field
+ HasPassword respjson.Field
+ Username respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// Returns the unmodified JSON received from the API
+func (r ProxyCheckResponseConfigCustomProxyConfig) RawJSON() string { return r.JSON.raw }
+func (r *ProxyCheckResponseConfigCustomProxyConfig) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
+// Protocol to use for the proxy connection.
+type ProxyCheckResponseProtocol string
+
+const (
+ ProxyCheckResponseProtocolHTTP ProxyCheckResponseProtocol = "http"
+ ProxyCheckResponseProtocolHTTPS ProxyCheckResponseProtocol = "https"
+)
+
+// Current health status of the proxy.
+type ProxyCheckResponseStatus string
+
+const (
+ ProxyCheckResponseStatusAvailable ProxyCheckResponseStatus = "available"
+ ProxyCheckResponseStatusUnavailable ProxyCheckResponseStatus = "unavailable"
+)
+
type ProxyNewParams struct {
// Proxy type to use. In terms of quality for avoiding bot-detection, from best to
// worst: `mobile` > `residential` > `isp` > `datacenter`.
diff --git a/proxy_test.go b/proxy_test.go
index 31d5f96..b8ffa5a 100644
--- a/proxy_test.go
+++ b/proxy_test.go
@@ -113,3 +113,26 @@ func TestProxyDelete(t *testing.T) {
t.Fatalf("err should be nil: %s", err.Error())
}
}
+
+func TestProxyCheck(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.Proxies.Check(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())
+ }
+}
diff --git a/shared/constant/constants.go b/shared/constant/constants.go
index c2d9711..7909c29 100644
--- a/shared/constant/constants.go
+++ b/shared/constant/constants.go
@@ -18,35 +18,29 @@ func ValueOf[T Constant[T]]() T {
return t.Default()
}
-type AlreadyAuthenticated string // Always "already_authenticated"
-type AppVersionSummary string // Always "app_version_summary"
-type AwsUsEast1a string // Always "aws.us-east-1a"
-type DeploymentState string // Always "deployment_state"
-type Error string // Always "error"
-type InvocationCreated string // Always "invocation_created"
-type InvocationState string // Always "invocation_state"
-type Log string // Always "log"
-type SseHeartbeat string // Always "sse_heartbeat"
-
-func (c AlreadyAuthenticated) Default() AlreadyAuthenticated { return "already_authenticated" }
-func (c AppVersionSummary) Default() AppVersionSummary { return "app_version_summary" }
-func (c AwsUsEast1a) Default() AwsUsEast1a { return "aws.us-east-1a" }
-func (c DeploymentState) Default() DeploymentState { return "deployment_state" }
-func (c Error) Default() Error { return "error" }
-func (c InvocationCreated) Default() InvocationCreated { return "invocation_created" }
-func (c InvocationState) Default() InvocationState { return "invocation_state" }
-func (c Log) Default() Log { return "log" }
-func (c SseHeartbeat) Default() SseHeartbeat { return "sse_heartbeat" }
-
-func (c AlreadyAuthenticated) MarshalJSON() ([]byte, error) { return marshalString(c) }
-func (c AppVersionSummary) MarshalJSON() ([]byte, error) { return marshalString(c) }
-func (c AwsUsEast1a) MarshalJSON() ([]byte, error) { return marshalString(c) }
-func (c DeploymentState) MarshalJSON() ([]byte, error) { return marshalString(c) }
-func (c Error) MarshalJSON() ([]byte, error) { return marshalString(c) }
-func (c InvocationCreated) MarshalJSON() ([]byte, error) { return marshalString(c) }
-func (c InvocationState) MarshalJSON() ([]byte, error) { return marshalString(c) }
-func (c Log) MarshalJSON() ([]byte, error) { return marshalString(c) }
-func (c SseHeartbeat) MarshalJSON() ([]byte, error) { return marshalString(c) }
+type AppVersionSummary string // Always "app_version_summary"
+type AwsUsEast1a string // Always "aws.us-east-1a"
+type DeploymentState string // Always "deployment_state"
+type Error string // Always "error"
+type InvocationState string // Always "invocation_state"
+type Log string // Always "log"
+type SseHeartbeat string // Always "sse_heartbeat"
+
+func (c AppVersionSummary) Default() AppVersionSummary { return "app_version_summary" }
+func (c AwsUsEast1a) Default() AwsUsEast1a { return "aws.us-east-1a" }
+func (c DeploymentState) Default() DeploymentState { return "deployment_state" }
+func (c Error) Default() Error { return "error" }
+func (c InvocationState) Default() InvocationState { return "invocation_state" }
+func (c Log) Default() Log { return "log" }
+func (c SseHeartbeat) Default() SseHeartbeat { return "sse_heartbeat" }
+
+func (c AppVersionSummary) MarshalJSON() ([]byte, error) { return marshalString(c) }
+func (c AwsUsEast1a) MarshalJSON() ([]byte, error) { return marshalString(c) }
+func (c DeploymentState) MarshalJSON() ([]byte, error) { return marshalString(c) }
+func (c Error) MarshalJSON() ([]byte, error) { return marshalString(c) }
+func (c InvocationState) MarshalJSON() ([]byte, error) { return marshalString(c) }
+func (c Log) MarshalJSON() ([]byte, error) { return marshalString(c) }
+func (c SseHeartbeat) MarshalJSON() ([]byte, error) { return marshalString(c) }
type constant[T any] interface {
Constant[T]
diff --git a/usage_test.go b/usage_test.go
index 5be681f..bab979d 100644
--- a/usage_test.go
+++ b/usage_test.go
@@ -24,6 +24,7 @@ func TestUsage(t *testing.T) {
option.WithBaseURL(baseURL),
option.WithAPIKey("My API Key"),
)
+ t.Skip("Prism tests are disabled")
browser, err := client.Browsers.New(context.TODO(), kernel.BrowserNewParams{
Stealth: kernel.Bool(true),
})