diff --git a/.release-please-manifest.json b/.release-please-manifest.json index ba6c348..f14b480 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.1.0-alpha.1" + ".": "0.1.0-alpha.2" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index d6d797a..4dfbf42 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 4 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-07d481d1498bf9677437b555e9ec2d843d50107faa7501e4c430a32b1f3c3343.yml -openapi_spec_hash: 296f78d82afbac95fad12c5eabd71f18 -config_hash: 2c8351ba6611ce4a352e248405783846 +configured_endpoints: 5 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-1fe396b957ced73281fc0a61a69b630836aa5c89a8dccce2c5a1716bc9775e80.yml +openapi_spec_hash: 9a0d67fb0781be034b77839584109638 +config_hash: df889df131f7438197abd59faace3c77 diff --git a/CHANGELOG.md b/CHANGELOG.md index 636539e..65d63d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## 0.1.0-alpha.2 (2025-05-19) + +Full Changelog: [v0.1.0-alpha.1...v0.1.0-alpha.2](https://github.com/onkernel/kernel-go-sdk/compare/v0.1.0-alpha.1...v0.1.0-alpha.2) + +### Features + +* **api:** update via SDK Studio ([72da2c4](https://github.com/onkernel/kernel-go-sdk/commit/72da2c4b53d8f47d6cc0dab3cfbe8707edb989d2)) +* **api:** update via SDK Studio ([58da3af](https://github.com/onkernel/kernel-go-sdk/commit/58da3af97eb8b295a54e22e53d8035f00fe09215)) +* **api:** update via SDK Studio ([ebab506](https://github.com/onkernel/kernel-go-sdk/commit/ebab506e3b5f8dbe8d30a47b3734c64024341d87)) + ## 0.1.0-alpha.1 (2025-05-14) Full Changelog: [v0.0.1-alpha.0...v0.1.0-alpha.1](https://github.com/onkernel/kernel-go-sdk/compare/v0.0.1-alpha.0...v0.1.0-alpha.1) diff --git a/README.md b/README.md index f002383..532eb7e 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Or to pin the version: ```sh -go get -u 'github.com/onkernel/kernel-go-sdk@v0.1.0-alpha.1' +go get -u 'github.com/onkernel/kernel-go-sdk@v0.1.0-alpha.2' ``` @@ -55,15 +55,18 @@ func main() { option.WithAPIKey("My API Key"), // defaults to os.LookupEnv("KERNEL_API_KEY") option.WithEnvironmentDevelopment(), // defaults to option.WithEnvironmentProduction() ) - response, err := client.Apps.Deploy(context.TODO(), kernel.AppDeployParams{ - EntrypointRelPath: "app.py", + deployment, err := client.Apps.Deployments.New(context.TODO(), kernel.AppDeploymentNewParams{ + EntrypointRelPath: "main.ts", File: io.Reader(bytes.NewBuffer([]byte("REPLACE_ME"))), - Version: kernel.String("REPLACE_ME"), + EnvVars: map[string]string{ + "OPENAI_API_KEY": "x", + }, + Version: kernel.String("1.0.0"), }) if err != nil { panic(err.Error()) } - fmt.Printf("%+v\n", response.Apps) + fmt.Printf("%+v\n", deployment.Apps) } ``` @@ -269,7 +272,7 @@ client := kernel.NewClient( option.WithHeader("X-Some-Header", "custom_header_info"), ) -client.Apps.Deploy(context.TODO(), ..., +client.Browsers.New(context.TODO(), ..., // Override the header option.WithHeader("X-Some-Header", "some_other_custom_header_info"), // Add an undocumented field to the request body, using sjson syntax @@ -298,10 +301,8 @@ When the API returns a non-success status code, we return an error with type To handle errors, we recommend that you use the `errors.As` pattern: ```go -_, err := client.Apps.Deploy(context.TODO(), kernel.AppDeployParams{ - EntrypointRelPath: "app.py", - File: io.Reader(bytes.NewBuffer([]byte("REPLACE_ME"))), - Version: kernel.String("REPLACE_ME"), +_, err := client.Browsers.New(context.TODO(), kernel.BrowserNewParams{ + InvocationID: "REPLACE_ME", }) if err != nil { var apierr *kernel.Error @@ -309,7 +310,7 @@ if err != nil { println(string(apierr.DumpRequest(true))) // Prints the serialized HTTP request println(string(apierr.DumpResponse(true))) // Prints the serialized HTTP response } - panic(err.Error()) // GET "/apps/deploy": 400 Bad Request { ... } + panic(err.Error()) // GET "/browsers": 400 Bad Request { ... } } ``` @@ -327,12 +328,10 @@ To set a per-retry timeout, use `option.WithRequestTimeout()`. // This sets the timeout for the request, including all the retries. ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) defer cancel() -client.Apps.Deploy( +client.Browsers.New( ctx, - kernel.AppDeployParams{ - EntrypointRelPath: "app.py", - File: io.Reader(bytes.NewBuffer([]byte("REPLACE_ME"))), - Version: kernel.String("REPLACE_ME"), + kernel.BrowserNewParams{ + InvocationID: "REPLACE_ME", }, // This sets the per-retry timeout option.WithRequestTimeout(20*time.Second), @@ -355,20 +354,20 @@ which can be used to wrap any `io.Reader` with the appropriate file name and con ```go // A file from the file system file, err := os.Open("/path/to/file") -kernel.AppDeployParams{ - EntrypointRelPath: "app.py", +kernel.AppDeploymentNewParams{ + EntrypointRelPath: "src/app.py", File: file, } // A file from a string -kernel.AppDeployParams{ - EntrypointRelPath: "app.py", +kernel.AppDeploymentNewParams{ + EntrypointRelPath: "src/app.py", File: strings.NewReader("my file contents"), } // With a custom filename and contentType -kernel.AppDeployParams{ - EntrypointRelPath: "app.py", +kernel.AppDeploymentNewParams{ + EntrypointRelPath: "src/app.py", File: kernel.File(strings.NewReader(`{"hello": "foo"}`), "file.go", "application/json"), } ``` @@ -388,12 +387,10 @@ client := kernel.NewClient( ) // Override per-request: -client.Apps.Deploy( +client.Browsers.New( context.TODO(), - kernel.AppDeployParams{ - EntrypointRelPath: "app.py", - File: io.Reader(bytes.NewBuffer([]byte("REPLACE_ME"))), - Version: kernel.String("REPLACE_ME"), + kernel.BrowserNewParams{ + InvocationID: "REPLACE_ME", }, option.WithMaxRetries(5), ) @@ -407,19 +404,17 @@ you need to examine response headers, status codes, or other details. ```go // Create a variable to store the HTTP response var response *http.Response -response, err := client.Apps.Deploy( +browser, err := client.Browsers.New( context.TODO(), - kernel.AppDeployParams{ - EntrypointRelPath: "app.py", - File: io.Reader(bytes.NewBuffer([]byte("REPLACE_ME"))), - Version: kernel.String("REPLACE_ME"), + kernel.BrowserNewParams{ + InvocationID: "REPLACE_ME", }, option.WithResponseInto(&response), ) if err != nil { // handle error } -fmt.Printf("%+v\n", response) +fmt.Printf("%+v\n", browser) fmt.Printf("Status Code: %d\n", response.StatusCode) fmt.Printf("Headers: %+#v\n", response.Header) diff --git a/api.md b/api.md index 9fc096a..afe95d1 100644 --- a/api.md +++ b/api.md @@ -1,23 +1,35 @@ # Apps +## Deployments + +Response Types: + +- kernel.AppDeploymentNewResponse + +Methods: + +- client.Apps.Deployments.New(ctx context.Context, body kernel.AppDeploymentNewParams) (kernel.AppDeploymentNewResponse, error) + +## Invocations + Response Types: -- kernel.AppDeployResponse -- kernel.AppInvokeResponse -- kernel.AppGetInvocationResponse +- kernel.AppInvocationNewResponse +- kernel.AppInvocationGetResponse Methods: -- client.Apps.Deploy(ctx context.Context, body kernel.AppDeployParams) (kernel.AppDeployResponse, error) -- client.Apps.Invoke(ctx context.Context, body kernel.AppInvokeParams) (kernel.AppInvokeResponse, error) -- client.Apps.GetInvocation(ctx context.Context, id string) (kernel.AppGetInvocationResponse, error) +- client.Apps.Invocations.New(ctx context.Context, body kernel.AppInvocationNewParams) (kernel.AppInvocationNewResponse, error) +- client.Apps.Invocations.Get(ctx context.Context, id string) (kernel.AppInvocationGetResponse, error) -# Browser +# Browsers Response Types: -- kernel.BrowserNewSessionResponse +- kernel.BrowserNewResponse +- kernel.BrowserGetResponse Methods: -- client.Browser.NewSession(ctx context.Context, body kernel.BrowserNewSessionParams) (kernel.BrowserNewSessionResponse, error) +- client.Browsers.New(ctx context.Context, body kernel.BrowserNewParams) (kernel.BrowserNewResponse, error) +- client.Browsers.Get(ctx context.Context, id string) (kernel.BrowserGetResponse, error) diff --git a/app.go b/app.go index 8a3738d..a5d6f7a 100644 --- a/app.go +++ b/app.go @@ -3,20 +3,7 @@ package kernel import ( - "bytes" - "context" - "errors" - "fmt" - "io" - "mime/multipart" - "net/http" - - "github.com/onkernel/kernel-go-sdk/internal/apiform" - "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" ) // AppService contains methods and other services that help with interacting with @@ -26,7 +13,9 @@ import ( // automatically. You should not instantiate this service directly, and instead use // the [NewAppService] method instead. type AppService struct { - Options []option.RequestOption + Options []option.RequestOption + Deployments AppDeploymentService + Invocations AppInvocationService } // NewAppService generates a new service that applies the given options to each @@ -35,225 +24,7 @@ type AppService struct { func NewAppService(opts ...option.RequestOption) (r AppService) { r = AppService{} r.Options = opts + r.Deployments = NewAppDeploymentService(opts...) + r.Invocations = NewAppInvocationService(opts...) return } - -// Deploy a new application -func (r *AppService) Deploy(ctx context.Context, body AppDeployParams, opts ...option.RequestOption) (res *AppDeployResponse, err error) { - opts = append(r.Options[:], opts...) - path := "apps/deploy" - err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, body, &res, opts...) - return -} - -// Invoke an application -func (r *AppService) Invoke(ctx context.Context, body AppInvokeParams, opts ...option.RequestOption) (res *AppInvokeResponse, err error) { - opts = append(r.Options[:], opts...) - path := "apps/invoke" - err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, body, &res, opts...) - return -} - -// Get an app invocation by id -func (r *AppService) GetInvocation(ctx context.Context, id string, opts ...option.RequestOption) (res *AppGetInvocationResponse, err error) { - opts = append(r.Options[:], opts...) - if id == "" { - err = errors.New("missing required id parameter") - return - } - path := fmt.Sprintf("apps/invocations/%s", id) - err = requestconfig.ExecuteNewRequest(ctx, http.MethodGet, path, nil, &res, opts...) - return -} - -type AppDeployResponse struct { - Apps []AppDeployResponseApp `json:"apps,required"` - // Success message - Message string `json:"message,required"` - // Status of the deployment - Success bool `json:"success,required"` - // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. - JSON struct { - Apps respjson.Field - Message respjson.Field - Success respjson.Field - ExtraFields map[string]respjson.Field - raw string - } `json:"-"` -} - -// Returns the unmodified JSON received from the API -func (r AppDeployResponse) RawJSON() string { return r.JSON.raw } -func (r *AppDeployResponse) UnmarshalJSON(data []byte) error { - return apijson.UnmarshalRoot(data, r) -} - -type AppDeployResponseApp struct { - // ID for the app version deployed - ID string `json:"id,required"` - Actions []AppDeployResponseAppAction `json:"actions,required"` - // Name of the app - Name string `json:"name,required"` - // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. - JSON struct { - ID respjson.Field - Actions respjson.Field - Name respjson.Field - ExtraFields map[string]respjson.Field - raw string - } `json:"-"` -} - -// Returns the unmodified JSON received from the API -func (r AppDeployResponseApp) RawJSON() string { return r.JSON.raw } -func (r *AppDeployResponseApp) UnmarshalJSON(data []byte) error { - return apijson.UnmarshalRoot(data, r) -} - -type AppDeployResponseAppAction struct { - // Name of the action - Name string `json:"name,required"` - // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. - JSON struct { - Name respjson.Field - ExtraFields map[string]respjson.Field - raw string - } `json:"-"` -} - -// Returns the unmodified JSON received from the API -func (r AppDeployResponseAppAction) RawJSON() string { return r.JSON.raw } -func (r *AppDeployResponseAppAction) UnmarshalJSON(data []byte) error { - return apijson.UnmarshalRoot(data, r) -} - -type AppInvokeResponse struct { - // ID of the invocation - ID string `json:"id,required"` - // Status of the invocation - // - // Any of "QUEUED", "RUNNING", "SUCCEEDED", "FAILED". - Status AppInvokeResponseStatus `json:"status,required"` - // Output from the invocation (if available) - Output string `json:"output"` - // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. - JSON struct { - ID respjson.Field - Status respjson.Field - Output respjson.Field - ExtraFields map[string]respjson.Field - raw string - } `json:"-"` -} - -// Returns the unmodified JSON received from the API -func (r AppInvokeResponse) RawJSON() string { return r.JSON.raw } -func (r *AppInvokeResponse) UnmarshalJSON(data []byte) error { - return apijson.UnmarshalRoot(data, r) -} - -// Status of the invocation -type AppInvokeResponseStatus string - -const ( - AppInvokeResponseStatusQueued AppInvokeResponseStatus = "QUEUED" - AppInvokeResponseStatusRunning AppInvokeResponseStatus = "RUNNING" - AppInvokeResponseStatusSucceeded AppInvokeResponseStatus = "SUCCEEDED" - AppInvokeResponseStatusFailed AppInvokeResponseStatus = "FAILED" -) - -type AppGetInvocationResponse struct { - ID string `json:"id,required"` - AppName string `json:"appName,required"` - FinishedAt string `json:"finishedAt,required"` - Input string `json:"input,required"` - Output string `json:"output,required"` - StartedAt string `json:"startedAt,required"` - Status string `json:"status,required"` - // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. - JSON struct { - ID respjson.Field - AppName respjson.Field - FinishedAt respjson.Field - Input respjson.Field - Output respjson.Field - StartedAt respjson.Field - Status respjson.Field - ExtraFields map[string]respjson.Field - raw string - } `json:"-"` -} - -// Returns the unmodified JSON received from the API -func (r AppGetInvocationResponse) RawJSON() string { return r.JSON.raw } -func (r *AppGetInvocationResponse) UnmarshalJSON(data []byte) error { - return apijson.UnmarshalRoot(data, r) -} - -type AppDeployParams struct { - // Relative path to the entrypoint of the application - EntrypointRelPath string `json:"entrypointRelPath,required"` - // ZIP file containing the application source directory - File io.Reader `json:"file,omitzero,required" format:"binary"` - // Version of the application. Can be any string. - Version param.Opt[string] `json:"version,omitzero"` - // Allow overwriting an existing app version - // - // Any of "true", "false". - Force AppDeployParamsForce `json:"force,omitzero"` - // Region for deployment. Currently we only support "aws.us-east-1a" - // - // Any of "aws.us-east-1a". - Region AppDeployParamsRegion `json:"region,omitzero"` - paramObj -} - -func (r AppDeployParams) MarshalMultipart() (data []byte, contentType string, err error) { - buf := bytes.NewBuffer(nil) - writer := multipart.NewWriter(buf) - err = apiform.MarshalRoot(r, writer) - if err != nil { - writer.Close() - return nil, "", err - } - err = writer.Close() - if err != nil { - return nil, "", err - } - return buf.Bytes(), writer.FormDataContentType(), nil -} - -// Allow overwriting an existing app version -type AppDeployParamsForce string - -const ( - AppDeployParamsForceTrue AppDeployParamsForce = "true" - AppDeployParamsForceFalse AppDeployParamsForce = "false" -) - -// Region for deployment. Currently we only support "aws.us-east-1a" -type AppDeployParamsRegion string - -const ( - AppDeployParamsRegionAwsUsEast1a AppDeployParamsRegion = "aws.us-east-1a" -) - -type AppInvokeParams struct { - // Name of the action to invoke - ActionName string `json:"actionName,required"` - // Name of the application - AppName string `json:"appName,required"` - // Input data for the application - Payload any `json:"payload,omitzero,required"` - // Version of the application - Version string `json:"version,required"` - paramObj -} - -func (r AppInvokeParams) MarshalJSON() (data []byte, err error) { - type shadow AppInvokeParams - return param.MarshalObject(r, (*shadow)(&r)) -} -func (r *AppInvokeParams) UnmarshalJSON(data []byte) error { - return apijson.UnmarshalRoot(data, r) -} diff --git a/appdeployment.go b/appdeployment.go new file mode 100644 index 0000000..1e9b914 --- /dev/null +++ b/appdeployment.go @@ -0,0 +1,161 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package kernel + +import ( + "bytes" + "context" + "io" + "mime/multipart" + "net/http" + + "github.com/onkernel/kernel-go-sdk/internal/apiform" + "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" +) + +// AppDeploymentService 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 [NewAppDeploymentService] method instead. +type AppDeploymentService struct { + Options []option.RequestOption +} + +// NewAppDeploymentService 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 NewAppDeploymentService(opts ...option.RequestOption) (r AppDeploymentService) { + r = AppDeploymentService{} + r.Options = opts + return +} + +// Deploy a new application +func (r *AppDeploymentService) New(ctx context.Context, body AppDeploymentNewParams, opts ...option.RequestOption) (res *AppDeploymentNewResponse, err error) { + opts = append(r.Options[:], opts...) + path := "deploy" + err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, body, &res, opts...) + return +} + +type AppDeploymentNewResponse struct { + // List of apps deployed + Apps []AppDeploymentNewResponseApp `json:"apps,required"` + // Current status of the deployment + // + // Any of "queued", "deploying", "succeeded", "failed". + Status AppDeploymentNewResponseStatus `json:"status,required"` + // Status reason + StatusReason string `json:"status_reason"` + // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. + JSON struct { + Apps respjson.Field + Status respjson.Field + StatusReason respjson.Field + ExtraFields map[string]respjson.Field + raw string + } `json:"-"` +} + +// Returns the unmodified JSON received from the API +func (r AppDeploymentNewResponse) RawJSON() string { return r.JSON.raw } +func (r *AppDeploymentNewResponse) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} + +type AppDeploymentNewResponseApp struct { + // ID for the app version deployed + ID string `json:"id,required"` + // List of actions available on the app + Actions []AppDeploymentNewResponseAppAction `json:"actions,required"` + // Name of the app + Name string `json:"name,required"` + // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. + JSON struct { + ID respjson.Field + Actions respjson.Field + Name respjson.Field + ExtraFields map[string]respjson.Field + raw string + } `json:"-"` +} + +// Returns the unmodified JSON received from the API +func (r AppDeploymentNewResponseApp) RawJSON() string { return r.JSON.raw } +func (r *AppDeploymentNewResponseApp) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} + +type AppDeploymentNewResponseAppAction struct { + // Name of the action + Name string `json:"name,required"` + // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. + JSON struct { + Name respjson.Field + ExtraFields map[string]respjson.Field + raw string + } `json:"-"` +} + +// Returns the unmodified JSON received from the API +func (r AppDeploymentNewResponseAppAction) RawJSON() string { return r.JSON.raw } +func (r *AppDeploymentNewResponseAppAction) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} + +// Current status of the deployment +type AppDeploymentNewResponseStatus string + +const ( + AppDeploymentNewResponseStatusQueued AppDeploymentNewResponseStatus = "queued" + AppDeploymentNewResponseStatusDeploying AppDeploymentNewResponseStatus = "deploying" + AppDeploymentNewResponseStatusSucceeded AppDeploymentNewResponseStatus = "succeeded" + AppDeploymentNewResponseStatusFailed AppDeploymentNewResponseStatus = "failed" +) + +type AppDeploymentNewParams struct { + // Relative path to the entrypoint of the application + EntrypointRelPath string `json:"entrypoint_rel_path,required"` + // ZIP file containing the application source directory + File io.Reader `json:"file,omitzero,required" format:"binary"` + // Allow overwriting an existing app version + Force param.Opt[bool] `json:"force,omitzero"` + // Version of the application. Can be any string. + Version param.Opt[string] `json:"version,omitzero"` + // Map of environment variables to set for the deployed application. Each key-value + // pair represents an environment variable. + EnvVars map[string]string `json:"env_vars,omitzero"` + // Region for deployment. Currently we only support "aws.us-east-1a" + // + // Any of "aws.us-east-1a". + Region AppDeploymentNewParamsRegion `json:"region,omitzero"` + paramObj +} + +func (r AppDeploymentNewParams) MarshalMultipart() (data []byte, contentType string, err error) { + buf := bytes.NewBuffer(nil) + writer := multipart.NewWriter(buf) + err = apiform.MarshalRoot(r, writer) + if err != nil { + writer.Close() + return nil, "", err + } + err = writer.Close() + if err != nil { + return nil, "", err + } + return buf.Bytes(), writer.FormDataContentType(), nil +} + +// Region for deployment. Currently we only support "aws.us-east-1a" +type AppDeploymentNewParamsRegion string + +const ( + AppDeploymentNewParamsRegionAwsUsEast1a AppDeploymentNewParamsRegion = "aws.us-east-1a" +) diff --git a/appdeployment_test.go b/appdeployment_test.go new file mode 100644 index 0000000..f836631 --- /dev/null +++ b/appdeployment_test.go @@ -0,0 +1,48 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package kernel_test + +import ( + "bytes" + "context" + "errors" + "io" + "os" + "testing" + + "github.com/onkernel/kernel-go-sdk" + "github.com/onkernel/kernel-go-sdk/internal/testutil" + "github.com/onkernel/kernel-go-sdk/option" +) + +func TestAppDeploymentNewWithOptionalParams(t *testing.T) { + t.Skip("skipped: tests are disabled for the time being") + 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.Apps.Deployments.New(context.TODO(), kernel.AppDeploymentNewParams{ + EntrypointRelPath: "src/app.py", + File: io.Reader(bytes.NewBuffer([]byte("some file contents"))), + EnvVars: map[string]string{ + "foo": "string", + }, + Force: kernel.Bool(false), + Region: kernel.AppDeploymentNewParamsRegionAwsUsEast1a, + Version: kernel.String("1.0.0"), + }) + if err != nil { + var apierr *kernel.Error + if errors.As(err, &apierr) { + t.Log(string(apierr.DumpRequest(true))) + } + t.Fatalf("err should be nil: %s", err.Error()) + } +} diff --git a/appinvocation.go b/appinvocation.go new file mode 100644 index 0000000..b44e221 --- /dev/null +++ b/appinvocation.go @@ -0,0 +1,170 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package kernel + +import ( + "context" + "errors" + "fmt" + "net/http" + "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" +) + +// AppInvocationService 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 [NewAppInvocationService] method instead. +type AppInvocationService struct { + Options []option.RequestOption +} + +// NewAppInvocationService 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 NewAppInvocationService(opts ...option.RequestOption) (r AppInvocationService) { + r = AppInvocationService{} + r.Options = opts + return +} + +// Invoke an application +func (r *AppInvocationService) New(ctx context.Context, body AppInvocationNewParams, opts ...option.RequestOption) (res *AppInvocationNewResponse, err error) { + opts = append(r.Options[:], opts...) + path := "invocations" + err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, body, &res, opts...) + return +} + +// Get an app invocation by id +func (r *AppInvocationService) Get(ctx context.Context, id string, opts ...option.RequestOption) (res *AppInvocationGetResponse, err error) { + opts = append(r.Options[:], opts...) + if id == "" { + err = errors.New("missing required id parameter") + return + } + path := fmt.Sprintf("invocations/%s", id) + err = requestconfig.ExecuteNewRequest(ctx, http.MethodGet, path, nil, &res, opts...) + return +} + +type AppInvocationNewResponse struct { + // ID of the invocation + ID string `json:"id,required"` + // Status of the invocation + // + // Any of "queued", "running", "succeeded", "failed". + Status AppInvocationNewResponseStatus `json:"status,required"` + // The return value of the action that was invoked, rendered as a JSON string. This + // could be: string, number, boolean, array, object, or null. + Output string `json:"output"` + // Status reason + StatusReason string `json:"status_reason"` + // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. + JSON struct { + ID respjson.Field + Status respjson.Field + Output respjson.Field + StatusReason respjson.Field + ExtraFields map[string]respjson.Field + raw string + } `json:"-"` +} + +// Returns the unmodified JSON received from the API +func (r AppInvocationNewResponse) RawJSON() string { return r.JSON.raw } +func (r *AppInvocationNewResponse) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} + +// Status of the invocation +type AppInvocationNewResponseStatus string + +const ( + AppInvocationNewResponseStatusQueued AppInvocationNewResponseStatus = "queued" + AppInvocationNewResponseStatusRunning AppInvocationNewResponseStatus = "running" + AppInvocationNewResponseStatusSucceeded AppInvocationNewResponseStatus = "succeeded" + AppInvocationNewResponseStatusFailed AppInvocationNewResponseStatus = "failed" +) + +type AppInvocationGetResponse struct { + // ID of the invocation + ID string `json:"id,required"` + // Name of the action invoked + ActionName string `json:"action_name,required"` + // Name of the application + AppName string `json:"app_name,required"` + // RFC 3339 Nanoseconds timestamp when the invocation started + StartedAt time.Time `json:"started_at,required" format:"date-time"` + // Status of the invocation + // + // Any of "queued", "running", "succeeded", "failed". + Status AppInvocationGetResponseStatus `json:"status,required"` + // RFC 3339 Nanoseconds timestamp when the invocation finished (null if still + // running) + FinishedAt time.Time `json:"finished_at,nullable" format:"date-time"` + // Output produced by the action, rendered as a JSON string. This could be: string, + // number, boolean, array, object, or null. + Output string `json:"output"` + // Payload provided to the invocation. This is a string that can be parsed as JSON. + Payload string `json:"payload"` + // Status reason + StatusReason string `json:"status_reason"` + // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. + JSON struct { + ID respjson.Field + ActionName respjson.Field + AppName respjson.Field + StartedAt respjson.Field + Status respjson.Field + FinishedAt respjson.Field + Output respjson.Field + Payload respjson.Field + StatusReason respjson.Field + ExtraFields map[string]respjson.Field + raw string + } `json:"-"` +} + +// Returns the unmodified JSON received from the API +func (r AppInvocationGetResponse) RawJSON() string { return r.JSON.raw } +func (r *AppInvocationGetResponse) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} + +// Status of the invocation +type AppInvocationGetResponseStatus string + +const ( + AppInvocationGetResponseStatusQueued AppInvocationGetResponseStatus = "queued" + AppInvocationGetResponseStatusRunning AppInvocationGetResponseStatus = "running" + AppInvocationGetResponseStatusSucceeded AppInvocationGetResponseStatus = "succeeded" + AppInvocationGetResponseStatusFailed AppInvocationGetResponseStatus = "failed" +) + +type AppInvocationNewParams struct { + // Name of the action to invoke + ActionName string `json:"action_name,required"` + // Name of the application + AppName string `json:"app_name,required"` + // Version of the application + Version string `json:"version,required"` + // Input data for the action, sent as a JSON string. + Payload param.Opt[string] `json:"payload,omitzero"` + paramObj +} + +func (r AppInvocationNewParams) MarshalJSON() (data []byte, err error) { + type shadow AppInvocationNewParams + return param.MarshalObject(r, (*shadow)(&r)) +} +func (r *AppInvocationNewParams) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} diff --git a/app_test.go b/appinvocation_test.go similarity index 51% rename from app_test.go rename to appinvocation_test.go index 1a6fdf2..9e151b2 100644 --- a/app_test.go +++ b/appinvocation_test.go @@ -3,10 +3,8 @@ package kernel_test import ( - "bytes" "context" "errors" - "io" "os" "testing" @@ -15,7 +13,7 @@ import ( "github.com/onkernel/kernel-go-sdk/option" ) -func TestAppDeployWithOptionalParams(t *testing.T) { +func TestAppInvocationNewWithOptionalParams(t *testing.T) { t.Skip("skipped: tests are disabled for the time being") baseURL := "http://localhost:4010" if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok { @@ -28,42 +26,11 @@ func TestAppDeployWithOptionalParams(t *testing.T) { option.WithBaseURL(baseURL), option.WithAPIKey("My API Key"), ) - _, err := client.Apps.Deploy(context.TODO(), kernel.AppDeployParams{ - EntrypointRelPath: "app.py", - File: io.Reader(bytes.NewBuffer([]byte("some file contents"))), - Force: kernel.AppDeployParamsForceFalse, - Region: kernel.AppDeployParamsRegionAwsUsEast1a, - Version: kernel.String("1.0.0"), - }) - if err != nil { - var apierr *kernel.Error - if errors.As(err, &apierr) { - t.Log(string(apierr.DumpRequest(true))) - } - t.Fatalf("err should be nil: %s", err.Error()) - } -} - -func TestAppInvoke(t *testing.T) { - t.Skip("skipped: tests are disabled for the time being") - 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.Apps.Invoke(context.TODO(), kernel.AppInvokeParams{ + _, err := client.Apps.Invocations.New(context.TODO(), kernel.AppInvocationNewParams{ ActionName: "analyze", - AppName: "my-awesome-app", - Payload: map[string]interface{}{ - "data": "example input", - }, - Version: "1.0.0", + AppName: "my-app", + Version: "1.0.0", + Payload: kernel.String(`{"data":"example input"}`), }) if err != nil { var apierr *kernel.Error @@ -74,7 +41,7 @@ func TestAppInvoke(t *testing.T) { } } -func TestAppGetInvocation(t *testing.T) { +func TestAppInvocationGet(t *testing.T) { t.Skip("skipped: tests are disabled for the time being") baseURL := "http://localhost:4010" if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok { @@ -87,7 +54,7 @@ func TestAppGetInvocation(t *testing.T) { option.WithBaseURL(baseURL), option.WithAPIKey("My API Key"), ) - _, err := client.Apps.GetInvocation(context.TODO(), "ckqwer3o20000jb9s7abcdef") + _, err := client.Apps.Invocations.Get(context.TODO(), "ckqwer3o20000jb9s7abcdef") if err != nil { var apierr *kernel.Error if errors.As(err, &apierr) { diff --git a/browser.go b/browser.go index 38762a9..51049d2 100644 --- a/browser.go +++ b/browser.go @@ -4,6 +4,8 @@ package kernel import ( "context" + "errors" + "fmt" "net/http" "github.com/onkernel/kernel-go-sdk/internal/apijson" @@ -33,46 +35,81 @@ func NewBrowserService(opts ...option.RequestOption) (r BrowserService) { } // Create Browser Session -func (r *BrowserService) NewSession(ctx context.Context, body BrowserNewSessionParams, opts ...option.RequestOption) (res *BrowserNewSessionResponse, err error) { +func (r *BrowserService) New(ctx context.Context, body BrowserNewParams, opts ...option.RequestOption) (res *BrowserNewResponse, err error) { opts = append(r.Options[:], opts...) - path := "browser" + path := "browsers" err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, body, &res, opts...) return } -type BrowserNewSessionResponse struct { +// Get Browser Session by ID +func (r *BrowserService) Get(ctx context.Context, id string, opts ...option.RequestOption) (res *BrowserGetResponse, err error) { + opts = append(r.Options[:], opts...) + if id == "" { + err = errors.New("missing required id parameter") + return + } + path := fmt.Sprintf("browsers/%s", id) + err = requestconfig.ExecuteNewRequest(ctx, http.MethodGet, path, nil, &res, opts...) + return +} + +type BrowserNewResponse struct { + // Remote URL for live viewing the browser session + BrowserLiveViewURL string `json:"browser_live_view_url,required"` // Websocket URL for Chrome DevTools Protocol connections to the browser session CdpWsURL string `json:"cdp_ws_url,required"` + // Unique identifier for the browser session + SessionID string `json:"session_id,required"` + // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. + JSON struct { + BrowserLiveViewURL respjson.Field + CdpWsURL respjson.Field + SessionID respjson.Field + ExtraFields map[string]respjson.Field + raw string + } `json:"-"` +} + +// Returns the unmodified JSON received from the API +func (r BrowserNewResponse) RawJSON() string { return r.JSON.raw } +func (r *BrowserNewResponse) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} + +type BrowserGetResponse struct { // Remote URL for live viewing the browser session - RemoteURL string `json:"remote_url,required"` + BrowserLiveViewURL string `json:"browser_live_view_url,required"` + // Websocket URL for Chrome DevTools Protocol connections to the browser session + CdpWsURL string `json:"cdp_ws_url,required"` // Unique identifier for the browser session - SessionID string `json:"sessionId,required"` + SessionID string `json:"session_id,required"` // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. JSON struct { - CdpWsURL respjson.Field - RemoteURL respjson.Field - SessionID respjson.Field - ExtraFields map[string]respjson.Field - raw string + BrowserLiveViewURL respjson.Field + CdpWsURL respjson.Field + SessionID respjson.Field + ExtraFields map[string]respjson.Field + raw string } `json:"-"` } // Returns the unmodified JSON received from the API -func (r BrowserNewSessionResponse) RawJSON() string { return r.JSON.raw } -func (r *BrowserNewSessionResponse) UnmarshalJSON(data []byte) error { +func (r BrowserGetResponse) RawJSON() string { return r.JSON.raw } +func (r *BrowserGetResponse) UnmarshalJSON(data []byte) error { return apijson.UnmarshalRoot(data, r) } -type BrowserNewSessionParams struct { - // Kernel App invocation ID - InvocationID string `json:"invocationId,required"` +type BrowserNewParams struct { + // action invocation ID + InvocationID string `json:"invocation_id,required"` paramObj } -func (r BrowserNewSessionParams) MarshalJSON() (data []byte, err error) { - type shadow BrowserNewSessionParams +func (r BrowserNewParams) MarshalJSON() (data []byte, err error) { + type shadow BrowserNewParams return param.MarshalObject(r, (*shadow)(&r)) } -func (r *BrowserNewSessionParams) UnmarshalJSON(data []byte) error { +func (r *BrowserNewParams) UnmarshalJSON(data []byte) error { return apijson.UnmarshalRoot(data, r) } diff --git a/browser_test.go b/browser_test.go index 00a7928..0c33b02 100644 --- a/browser_test.go +++ b/browser_test.go @@ -13,7 +13,7 @@ import ( "github.com/onkernel/kernel-go-sdk/option" ) -func TestBrowserNewSession(t *testing.T) { +func TestBrowserNew(t *testing.T) { t.Skip("skipped: tests are disabled for the time being") baseURL := "http://localhost:4010" if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok { @@ -26,8 +26,8 @@ func TestBrowserNewSession(t *testing.T) { option.WithBaseURL(baseURL), option.WithAPIKey("My API Key"), ) - _, err := client.Browser.NewSession(context.TODO(), kernel.BrowserNewSessionParams{ - InvocationID: "invocationId", + _, err := client.Browsers.New(context.TODO(), kernel.BrowserNewParams{ + InvocationID: "ckqwer3o20000jb9s7abcdef", }) if err != nil { var apierr *kernel.Error @@ -37,3 +37,26 @@ func TestBrowserNewSession(t *testing.T) { t.Fatalf("err should be nil: %s", err.Error()) } } + +func TestBrowserGet(t *testing.T) { + t.Skip("skipped: tests are disabled for the time being") + 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.Browsers.Get(context.TODO(), "e5bf36fe-9247-4e2b-8b5a-2f594cc1c073") + if err != nil { + var apierr *kernel.Error + if errors.As(err, &apierr) { + t.Log(string(apierr.DumpRequest(true))) + } + t.Fatalf("err should be nil: %s", err.Error()) + } +} diff --git a/client.go b/client.go index 9a46809..fe8f57a 100644 --- a/client.go +++ b/client.go @@ -15,9 +15,9 @@ import ( // interacting with the kernel API. You should not instantiate this client // directly, and instead use the [NewClient] method instead. type Client struct { - Options []option.RequestOption - Apps AppService - Browser BrowserService + Options []option.RequestOption + Apps AppService + Browsers BrowserService } // DefaultClientOptions read from the environment (KERNEL_API_KEY, @@ -43,7 +43,7 @@ func NewClient(opts ...option.RequestOption) (r Client) { r = Client{Options: opts} r.Apps = NewAppService(opts...) - r.Browser = NewBrowserService(opts...) + r.Browsers = NewBrowserService(opts...) return } diff --git a/client_test.go b/client_test.go index fe85a60..6a04877 100644 --- a/client_test.go +++ b/client_test.go @@ -3,10 +3,8 @@ package kernel_test import ( - "bytes" "context" "fmt" - "io" "net/http" "reflect" "testing" @@ -40,10 +38,8 @@ func TestUserAgentHeader(t *testing.T) { }, }), ) - client.Apps.Deploy(context.Background(), kernel.AppDeployParams{ - EntrypointRelPath: "app.py", - File: io.Reader(bytes.NewBuffer([]byte("REPLACE_ME"))), - Version: kernel.String("REPLACE_ME"), + client.Browsers.New(context.Background(), kernel.BrowserNewParams{ + InvocationID: "REPLACE_ME", }) if userAgent != fmt.Sprintf("Kernel/Go %s", internal.PackageVersion) { t.Errorf("Expected User-Agent to be correct, but got: %#v", userAgent) @@ -68,10 +64,8 @@ func TestRetryAfter(t *testing.T) { }, }), ) - _, err := client.Apps.Deploy(context.Background(), kernel.AppDeployParams{ - EntrypointRelPath: "app.py", - File: io.Reader(bytes.NewBuffer([]byte("REPLACE_ME"))), - Version: kernel.String("REPLACE_ME"), + _, err := client.Browsers.New(context.Background(), kernel.BrowserNewParams{ + InvocationID: "REPLACE_ME", }) if err == nil { t.Error("Expected there to be a cancel error") @@ -107,10 +101,8 @@ func TestDeleteRetryCountHeader(t *testing.T) { }), option.WithHeaderDel("X-Stainless-Retry-Count"), ) - _, err := client.Apps.Deploy(context.Background(), kernel.AppDeployParams{ - EntrypointRelPath: "app.py", - File: io.Reader(bytes.NewBuffer([]byte("REPLACE_ME"))), - Version: kernel.String("REPLACE_ME"), + _, err := client.Browsers.New(context.Background(), kernel.BrowserNewParams{ + InvocationID: "REPLACE_ME", }) if err == nil { t.Error("Expected there to be a cancel error") @@ -141,10 +133,8 @@ func TestOverwriteRetryCountHeader(t *testing.T) { }), option.WithHeader("X-Stainless-Retry-Count", "42"), ) - _, err := client.Apps.Deploy(context.Background(), kernel.AppDeployParams{ - EntrypointRelPath: "app.py", - File: io.Reader(bytes.NewBuffer([]byte("REPLACE_ME"))), - Version: kernel.String("REPLACE_ME"), + _, err := client.Browsers.New(context.Background(), kernel.BrowserNewParams{ + InvocationID: "REPLACE_ME", }) if err == nil { t.Error("Expected there to be a cancel error") @@ -174,10 +164,8 @@ func TestRetryAfterMs(t *testing.T) { }, }), ) - _, err := client.Apps.Deploy(context.Background(), kernel.AppDeployParams{ - EntrypointRelPath: "app.py", - File: io.Reader(bytes.NewBuffer([]byte("REPLACE_ME"))), - Version: kernel.String("REPLACE_ME"), + _, err := client.Browsers.New(context.Background(), kernel.BrowserNewParams{ + InvocationID: "REPLACE_ME", }) if err == nil { t.Error("Expected there to be a cancel error") @@ -201,10 +189,8 @@ func TestContextCancel(t *testing.T) { ) cancelCtx, cancel := context.WithCancel(context.Background()) cancel() - _, err := client.Apps.Deploy(cancelCtx, kernel.AppDeployParams{ - EntrypointRelPath: "app.py", - File: io.Reader(bytes.NewBuffer([]byte("REPLACE_ME"))), - Version: kernel.String("REPLACE_ME"), + _, err := client.Browsers.New(cancelCtx, kernel.BrowserNewParams{ + InvocationID: "REPLACE_ME", }) if err == nil { t.Error("Expected there to be a cancel error") @@ -225,10 +211,8 @@ func TestContextCancelDelay(t *testing.T) { ) cancelCtx, cancel := context.WithTimeout(context.Background(), 2*time.Millisecond) defer cancel() - _, err := client.Apps.Deploy(cancelCtx, kernel.AppDeployParams{ - EntrypointRelPath: "app.py", - File: io.Reader(bytes.NewBuffer([]byte("REPLACE_ME"))), - Version: kernel.String("REPLACE_ME"), + _, err := client.Browsers.New(cancelCtx, kernel.BrowserNewParams{ + InvocationID: "REPLACE_ME", }) if err == nil { t.Error("expected there to be a cancel error") @@ -255,10 +239,8 @@ func TestContextDeadline(t *testing.T) { }, }), ) - _, err := client.Apps.Deploy(deadlineCtx, kernel.AppDeployParams{ - EntrypointRelPath: "app.py", - File: io.Reader(bytes.NewBuffer([]byte("REPLACE_ME"))), - Version: kernel.String("REPLACE_ME"), + _, err := client.Browsers.New(deadlineCtx, kernel.BrowserNewParams{ + InvocationID: "REPLACE_ME", }) if err == nil { t.Error("expected there to be a deadline error") diff --git a/internal/version.go b/internal/version.go index 5ed22d7..d6f40b3 100644 --- a/internal/version.go +++ b/internal/version.go @@ -2,4 +2,4 @@ package internal -const PackageVersion = "0.1.0-alpha.1" // x-release-please-version +const PackageVersion = "0.1.0-alpha.2" // x-release-please-version diff --git a/usage_test.go b/usage_test.go index 2e0a784..3110ba6 100644 --- a/usage_test.go +++ b/usage_test.go @@ -26,13 +26,16 @@ func TestUsage(t *testing.T) { option.WithBaseURL(baseURL), option.WithAPIKey("My API Key"), ) - response, err := client.Apps.Deploy(context.TODO(), kernel.AppDeployParams{ - EntrypointRelPath: "app.py", + deployment, err := client.Apps.Deployments.New(context.TODO(), kernel.AppDeploymentNewParams{ + EntrypointRelPath: "main.ts", File: io.Reader(bytes.NewBuffer([]byte("REPLACE_ME"))), - Version: kernel.String("REPLACE_ME"), + EnvVars: map[string]string{ + "OPENAI_API_KEY": "x", + }, + Version: kernel.String("1.0.0"), }) if err != nil { t.Fatalf("err should be nil: %s", err.Error()) } - t.Logf("%+v\n", response.Apps) + t.Logf("%+v\n", deployment.Apps) }