From 5adf2a69de3e0d29a2da29badf98e3b3ca54229c Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Mon, 19 May 2025 20:44:33 +0000
Subject: [PATCH 1/4] codegen metadata
---
.stats.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.stats.yml b/.stats.yml
index 4dfbf42..6d03c39 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 5
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-1fe396b957ced73281fc0a61a69b630836aa5c89a8dccce2c5a1716bc9775e80.yml
-openapi_spec_hash: 9a0d67fb0781be034b77839584109638
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-3b19f5e2b96ede3193aa7a24d3f82d1406b8a16ea25e98ba3956e4a1a2376ad7.yml
+openapi_spec_hash: b62a6e73ddcec71674973f795a5790ac
config_hash: df889df131f7438197abd59faace3c77
From 4a122041257d78d3b583408e912d4d8a5b10cdc0 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Mon, 19 May 2025 21:18:13 +0000
Subject: [PATCH 2/4] feat(api): update via SDK Studio
---
.stats.yml | 8 +-
api.md | 10 ++
app.go | 64 +++++++++
app_test.go | 40 ++++++
appdeployment.go | 175 +++++++++++++++++++++++++
client_test.go | 103 +++++++++++++++
packages/ssestream/ssestream.go | 221 ++++++++++++++++++++++++++++++++
shared/constant/constants.go | 12 ++
8 files changed, 629 insertions(+), 4 deletions(-)
create mode 100644 app_test.go
create mode 100644 packages/ssestream/ssestream.go
diff --git a/.stats.yml b/.stats.yml
index 6d03c39..01c41ad 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
-configured_endpoints: 5
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-3b19f5e2b96ede3193aa7a24d3f82d1406b8a16ea25e98ba3956e4a1a2376ad7.yml
-openapi_spec_hash: b62a6e73ddcec71674973f795a5790ac
-config_hash: df889df131f7438197abd59faace3c77
+configured_endpoints: 7
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-c9d64df733f286f09d2203f4e3d820ce57e8d4c629c5e2db4e2bfac91fbc1598.yml
+openapi_spec_hash: fa407611fc566d55f403864fbfaa6c23
+config_hash: 7f67c5b95af1e4b39525515240b72275
diff --git a/api.md b/api.md
index afe95d1..eb9c93c 100644
--- a/api.md
+++ b/api.md
@@ -1,14 +1,24 @@
# Apps
+Response Types:
+
+- kernel.AppListResponse
+
+Methods:
+
+- client.Apps.List(ctx context.Context, query kernel.AppListParams) ([]kernel.AppListResponse, error)
+
## Deployments
Response Types:
- kernel.AppDeploymentNewResponse
+- kernel.AppDeploymentFollowResponseUnion
Methods:
- client.Apps.Deployments.New(ctx context.Context, body kernel.AppDeploymentNewParams) (kernel.AppDeploymentNewResponse, error)
+- client.Apps.Deployments.Follow(ctx context.Context, id string) ([]kernel.AppDeploymentFollowResponseUnion, error)
## Invocations
diff --git a/app.go b/app.go
index a5d6f7a..c3d26dc 100644
--- a/app.go
+++ b/app.go
@@ -3,7 +3,16 @@
package kernel
import (
+ "context"
+ "net/http"
+ "net/url"
+
+ "github.com/onkernel/kernel-go-sdk/internal/apijson"
+ "github.com/onkernel/kernel-go-sdk/internal/apiquery"
+ "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
@@ -28,3 +37,58 @@ func NewAppService(opts ...option.RequestOption) (r AppService) {
r.Invocations = NewAppInvocationService(opts...)
return
}
+
+// List application versions for the authenticated user. Optionally filter by app
+// name and/or version label.
+func (r *AppService) List(ctx context.Context, query AppListParams, opts ...option.RequestOption) (res *[]AppListResponse, err error) {
+ opts = append(r.Options[:], opts...)
+ path := "apps"
+ err = requestconfig.ExecuteNewRequest(ctx, http.MethodGet, path, query, &res, opts...)
+ return
+}
+
+// Summary of an application version.
+type AppListResponse struct {
+ // Unique identifier for the app version
+ ID string `json:"id,required"`
+ // Name of the application
+ AppName string `json:"app_name,required"`
+ // Deployment region code
+ Region string `json:"region,required"`
+ // Version label for the application
+ Version string `json:"version,required"`
+ // Environment variables configured for this app version
+ EnvVars map[string]string `json:"env_vars"`
+ // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
+ JSON struct {
+ ID respjson.Field
+ AppName respjson.Field
+ Region respjson.Field
+ Version respjson.Field
+ EnvVars respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// Returns the unmodified JSON received from the API
+func (r AppListResponse) RawJSON() string { return r.JSON.raw }
+func (r *AppListResponse) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
+type AppListParams struct {
+ // Filter results by application name.
+ AppName param.Opt[string] `query:"app_name,omitzero" json:"-"`
+ // Filter results by version label.
+ Version param.Opt[string] `query:"version,omitzero" json:"-"`
+ paramObj
+}
+
+// URLQuery serializes [AppListParams]'s query parameters as `url.Values`.
+func (r AppListParams) URLQuery() (v url.Values, err error) {
+ return apiquery.MarshalWithSettings(r, apiquery.QuerySettings{
+ ArrayFormat: apiquery.ArrayQueryFormatComma,
+ NestedFormat: apiquery.NestedQueryFormatBrackets,
+ })
+}
diff --git a/app_test.go b/app_test.go
new file mode 100644
index 0000000..0b9b741
--- /dev/null
+++ b/app_test.go
@@ -0,0 +1,40 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+package kernel_test
+
+import (
+ "context"
+ "errors"
+ "os"
+ "testing"
+
+ "github.com/onkernel/kernel-go-sdk"
+ "github.com/onkernel/kernel-go-sdk/internal/testutil"
+ "github.com/onkernel/kernel-go-sdk/option"
+)
+
+func TestAppListWithOptionalParams(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.List(context.TODO(), kernel.AppListParams{
+ AppName: kernel.String("app_name"),
+ Version: kernel.String("version"),
+ })
+ 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/appdeployment.go b/appdeployment.go
index 1e9b914..67ebe32 100644
--- a/appdeployment.go
+++ b/appdeployment.go
@@ -5,9 +5,13 @@ package kernel
import (
"bytes"
"context"
+ "encoding/json"
+ "errors"
+ "fmt"
"io"
"mime/multipart"
"net/http"
+ "time"
"github.com/onkernel/kernel-go-sdk/internal/apiform"
"github.com/onkernel/kernel-go-sdk/internal/apijson"
@@ -15,6 +19,8 @@ import (
"github.com/onkernel/kernel-go-sdk/option"
"github.com/onkernel/kernel-go-sdk/packages/param"
"github.com/onkernel/kernel-go-sdk/packages/respjson"
+ "github.com/onkernel/kernel-go-sdk/packages/ssestream"
+ "github.com/onkernel/kernel-go-sdk/shared/constant"
)
// AppDeploymentService contains methods and other services that help with
@@ -44,6 +50,25 @@ func (r *AppDeploymentService) New(ctx context.Context, body AppDeploymentNewPar
return
}
+// Establishes a Server-Sent Events (SSE) stream that delivers real-time logs and
+// status updates for a deployed application. The stream terminates automatically
+// once the application reaches a terminal state.
+func (r *AppDeploymentService) FollowStreaming(ctx context.Context, id string, opts ...option.RequestOption) (stream *ssestream.Stream[[]AppDeploymentFollowResponseUnion]) {
+ var (
+ raw *http.Response
+ err error
+ )
+ opts = append(r.Options[:], opts...)
+ opts = append([]option.RequestOption{option.WithHeader("Accept", "text/event-stream")}, opts...)
+ if id == "" {
+ err = errors.New("missing required id parameter")
+ return
+ }
+ path := fmt.Sprintf("apps/%s/events", id)
+ err = requestconfig.ExecuteNewRequest(ctx, http.MethodGet, path, nil, &raw, opts...)
+ return ssestream.NewStream[[]AppDeploymentFollowResponseUnion](ssestream.NewDecoder(raw), err)
+}
+
type AppDeploymentNewResponse struct {
// List of apps deployed
Apps []AppDeploymentNewResponseApp `json:"apps,required"`
@@ -119,6 +144,156 @@ const (
AppDeploymentNewResponseStatusFailed AppDeploymentNewResponseStatus = "failed"
)
+// AppDeploymentFollowResponseUnion contains all possible properties and values
+// from [AppDeploymentFollowResponseState],
+// [AppDeploymentFollowResponseStateUpdate], [AppDeploymentFollowResponseLog].
+//
+// Use the [AppDeploymentFollowResponseUnion.AsAny] method to switch on the
+// variant.
+//
+// Use the methods beginning with 'As' to cast the union to one of its variants.
+type AppDeploymentFollowResponseUnion struct {
+ // Any of "state", "state_update", "log".
+ Event string `json:"event"`
+ State string `json:"state"`
+ Timestamp time.Time `json:"timestamp"`
+ // This field is from variant [AppDeploymentFollowResponseLog].
+ Message string `json:"message"`
+ JSON struct {
+ Event respjson.Field
+ State respjson.Field
+ Timestamp respjson.Field
+ Message respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// anyAppDeploymentFollowResponse is implemented by each variant of
+// [AppDeploymentFollowResponseUnion] to add type safety for the return type of
+// [AppDeploymentFollowResponseUnion.AsAny]
+type anyAppDeploymentFollowResponse interface {
+ implAppDeploymentFollowResponseUnion()
+}
+
+func (AppDeploymentFollowResponseState) implAppDeploymentFollowResponseUnion() {}
+func (AppDeploymentFollowResponseStateUpdate) implAppDeploymentFollowResponseUnion() {}
+func (AppDeploymentFollowResponseLog) implAppDeploymentFollowResponseUnion() {}
+
+// Use the following switch statement to find the correct variant
+//
+// switch variant := AppDeploymentFollowResponseUnion.AsAny().(type) {
+// case kernel.AppDeploymentFollowResponseState:
+// case kernel.AppDeploymentFollowResponseStateUpdate:
+// case kernel.AppDeploymentFollowResponseLog:
+// default:
+// fmt.Errorf("no variant present")
+// }
+func (u AppDeploymentFollowResponseUnion) AsAny() anyAppDeploymentFollowResponse {
+ switch u.Event {
+ case "state":
+ return u.AsState()
+ case "state_update":
+ return u.AsStateUpdate()
+ case "log":
+ return u.AsLog()
+ }
+ return nil
+}
+
+func (u AppDeploymentFollowResponseUnion) AsState() (v AppDeploymentFollowResponseState) {
+ apijson.UnmarshalRoot(json.RawMessage(u.JSON.raw), &v)
+ return
+}
+
+func (u AppDeploymentFollowResponseUnion) AsStateUpdate() (v AppDeploymentFollowResponseStateUpdate) {
+ apijson.UnmarshalRoot(json.RawMessage(u.JSON.raw), &v)
+ return
+}
+
+func (u AppDeploymentFollowResponseUnion) AsLog() (v AppDeploymentFollowResponseLog) {
+ apijson.UnmarshalRoot(json.RawMessage(u.JSON.raw), &v)
+ return
+}
+
+// Returns the unmodified JSON received from the API
+func (u AppDeploymentFollowResponseUnion) RawJSON() string { return u.JSON.raw }
+
+func (r *AppDeploymentFollowResponseUnion) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
+// Initial state of the application, emitted once when subscribing.
+type AppDeploymentFollowResponseState struct {
+ // Event type identifier (always "state").
+ Event constant.State `json:"event,required"`
+ // Current application state (e.g., "deploying", "running", "succeeded", "failed").
+ State string `json:"state,required"`
+ // Time the state was reported.
+ Timestamp time.Time `json:"timestamp" format:"date-time"`
+ // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
+ JSON struct {
+ Event respjson.Field
+ State respjson.Field
+ Timestamp respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// Returns the unmodified JSON received from the API
+func (r AppDeploymentFollowResponseState) RawJSON() string { return r.JSON.raw }
+func (r *AppDeploymentFollowResponseState) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
+// An update emitted when the application's state changes.
+type AppDeploymentFollowResponseStateUpdate struct {
+ // Event type identifier (always "state_update").
+ Event constant.StateUpdate `json:"event,required"`
+ // New application state (e.g., "running", "succeeded", "failed").
+ State string `json:"state,required"`
+ // Time the state change occurred.
+ Timestamp time.Time `json:"timestamp" format:"date-time"`
+ // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
+ JSON struct {
+ Event respjson.Field
+ State respjson.Field
+ Timestamp respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// Returns the unmodified JSON received from the API
+func (r AppDeploymentFollowResponseStateUpdate) RawJSON() string { return r.JSON.raw }
+func (r *AppDeploymentFollowResponseStateUpdate) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
+// A log entry from the application.
+type AppDeploymentFollowResponseLog struct {
+ // Event type identifier (always "log").
+ Event constant.Log `json:"event,required"`
+ // Log message text.
+ Message string `json:"message,required"`
+ // Time the log entry was produced.
+ Timestamp time.Time `json:"timestamp" format:"date-time"`
+ // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
+ JSON struct {
+ Event respjson.Field
+ Message respjson.Field
+ Timestamp respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// Returns the unmodified JSON received from the API
+func (r AppDeploymentFollowResponseLog) RawJSON() string { return r.JSON.raw }
+func (r *AppDeploymentFollowResponseLog) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
type AppDeploymentNewParams struct {
// Relative path to the entrypoint of the application
EntrypointRelPath string `json:"entrypoint_rel_path,required"`
diff --git a/client_test.go b/client_test.go
index 6a04877..d9846bd 100644
--- a/client_test.go
+++ b/client_test.go
@@ -5,6 +5,7 @@ package kernel_test
import (
"context"
"fmt"
+ "io"
"net/http"
"reflect"
"testing"
@@ -257,3 +258,105 @@ func TestContextDeadline(t *testing.T) {
}
}
}
+
+func TestContextDeadlineStreaming(t *testing.T) {
+ testTimeout := time.After(3 * time.Second)
+ testDone := make(chan struct{})
+
+ deadline := time.Now().Add(100 * time.Millisecond)
+ deadlineCtx, cancel := context.WithDeadline(context.Background(), deadline)
+ defer cancel()
+
+ go func() {
+ client := kernel.NewClient(
+ option.WithAPIKey("My API Key"),
+ option.WithHTTPClient(&http.Client{
+ Transport: &closureTransport{
+ fn: func(req *http.Request) (*http.Response, error) {
+ return &http.Response{
+ StatusCode: 200,
+ Status: "200 OK",
+ Body: io.NopCloser(
+ io.Reader(readerFunc(func([]byte) (int, error) {
+ <-req.Context().Done()
+ return 0, req.Context().Err()
+ })),
+ ),
+ }, nil
+ },
+ },
+ }),
+ )
+ stream := client.Apps.Deployments.FollowStreaming(deadlineCtx, "id")
+ for stream.Next() {
+ _ = stream.Current()
+ }
+ if stream.Err() == nil {
+ t.Error("expected there to be a deadline error")
+ }
+ close(testDone)
+ }()
+
+ select {
+ case <-testTimeout:
+ t.Fatal("client didn't finish in time")
+ case <-testDone:
+ if diff := time.Since(deadline); diff < -30*time.Millisecond || 30*time.Millisecond < diff {
+ t.Fatalf("client did not return within 30ms of context deadline, got %s", diff)
+ }
+ }
+}
+
+func TestContextDeadlineStreamingWithRequestTimeout(t *testing.T) {
+ testTimeout := time.After(3 * time.Second)
+ testDone := make(chan struct{})
+ deadline := time.Now().Add(100 * time.Millisecond)
+
+ go func() {
+ client := kernel.NewClient(
+ option.WithAPIKey("My API Key"),
+ option.WithHTTPClient(&http.Client{
+ Transport: &closureTransport{
+ fn: func(req *http.Request) (*http.Response, error) {
+ return &http.Response{
+ StatusCode: 200,
+ Status: "200 OK",
+ Body: io.NopCloser(
+ io.Reader(readerFunc(func([]byte) (int, error) {
+ <-req.Context().Done()
+ return 0, req.Context().Err()
+ })),
+ ),
+ }, nil
+ },
+ },
+ }),
+ )
+ stream := client.Apps.Deployments.FollowStreaming(
+ context.Background(),
+ "id",
+ option.WithRequestTimeout((100 * time.Millisecond)),
+ )
+ for stream.Next() {
+ _ = stream.Current()
+ }
+ if stream.Err() == nil {
+ t.Error("expected there to be a deadline error")
+ }
+ close(testDone)
+ }()
+
+ select {
+ case <-testTimeout:
+ t.Fatal("client didn't finish in time")
+ case <-testDone:
+ if diff := time.Since(deadline); diff < -30*time.Millisecond || 30*time.Millisecond < diff {
+ t.Fatalf("client did not return within 30ms of context deadline, got %s", diff)
+ }
+ }
+}
+
+type readerFunc func([]byte) (int, error)
+
+func (f readerFunc) Read(p []byte) (int, error) { return f(p) }
+func (f readerFunc) Close() error { return nil }
diff --git a/packages/ssestream/ssestream.go b/packages/ssestream/ssestream.go
new file mode 100644
index 0000000..d6f6f3e
--- /dev/null
+++ b/packages/ssestream/ssestream.go
@@ -0,0 +1,221 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+package ssestream
+
+import (
+ "bufio"
+ "bytes"
+ "encoding/json"
+ "io"
+ "net/http"
+ "strings"
+)
+
+type Decoder interface {
+ Event() Event
+ Next() bool
+ Close() error
+ Err() error
+}
+
+func NewDecoder(res *http.Response) Decoder {
+ if res == nil || res.Body == nil {
+ return nil
+ }
+
+ var decoder Decoder
+ contentType := res.Header.Get("content-type")
+ if t, ok := decoderTypes[contentType]; ok {
+ decoder = t(res.Body)
+ } else {
+ decoder = &eventStreamDecoder{rc: res.Body, rdr: bufio.NewReader(res.Body)}
+ }
+ return decoder
+}
+
+var decoderTypes = map[string](func(io.ReadCloser) Decoder){}
+
+func RegisterDecoder(contentType string, decoder func(io.ReadCloser) Decoder) {
+ decoderTypes[strings.ToLower(contentType)] = decoder
+}
+
+type Event struct {
+ Type string
+ Data []byte
+}
+
+// A base implementation of a Decoder for text/event-stream.
+type eventStreamDecoder struct {
+ evt Event
+ rc io.ReadCloser
+ rdr *bufio.Reader
+ err error
+}
+
+func line(r *bufio.Reader) ([]byte, error) {
+ var overflow bytes.Buffer
+
+ // To prevent infinite loops, the failsafe stops when a line is
+ // 100 times longer than the [io.Reader] default buffer size,
+ // or after 20 failed attempts to find an end of line.
+ for f := 0; f < 100; f++ {
+ part, isPrefix, err := r.ReadLine()
+ if err != nil {
+ return nil, err
+ }
+
+ // Happy case, the line fits in the default buffer.
+ if !isPrefix && overflow.Len() == 0 {
+ return part, nil
+ }
+
+ // Overflow case, append to the buffer.
+ if isPrefix || overflow.Len() > 0 {
+ n, err := overflow.Write(part)
+ if err != nil {
+ return nil, err
+ }
+
+ // Didn't find an end of line, heavily increment the failsafe.
+ if n != r.Size() {
+ f += 5
+ }
+ }
+
+ if !isPrefix {
+ return overflow.Bytes(), nil
+ }
+ }
+
+ return nil, fmt.Errorf("ssestream: too many attempts to read a line")
+}
+
+func (s *eventStreamDecoder) Next() bool {
+ if s.err != nil {
+ return false
+ }
+
+ event := ""
+ data := bytes.NewBuffer(nil)
+
+ for {
+ txt, err := line(s.rdr)
+ if err == io.EOF {
+ return false
+ }
+
+ if err != nil {
+ s.err = err
+ break
+ }
+
+ // Dispatch event on an empty line
+ if len(txt) == 0 {
+ s.evt = Event{
+ Type: event,
+ Data: data.Bytes(),
+ }
+ return true
+ }
+
+ // Split a string like "event: bar" into name="event" and value=" bar".
+ name, value, _ := bytes.Cut(txt, []byte(":"))
+
+ // Consume an optional space after the colon if it exists.
+ if len(value) > 0 && value[0] == ' ' {
+ value = value[1:]
+ }
+
+ switch string(name) {
+ case "":
+ // An empty line in the for ": something" is a comment and should be ignored.
+ continue
+ case "event":
+ event = string(value)
+ case "data":
+ _, s.err = data.Write(value)
+ if s.err != nil {
+ break
+ }
+ _, s.err = data.WriteRune('\n')
+ if s.err != nil {
+ break
+ }
+ }
+ }
+
+ return false
+}
+
+func (s *eventStreamDecoder) Event() Event {
+ return s.evt
+}
+
+func (s *eventStreamDecoder) Close() error {
+ return s.rc.Close()
+}
+
+func (s *eventStreamDecoder) Err() error {
+ return s.err
+}
+
+type Stream[T any] struct {
+ decoder Decoder
+ cur T
+ err error
+}
+
+func NewStream[T any](decoder Decoder, err error) *Stream[T] {
+ return &Stream[T]{
+ decoder: decoder,
+ err: err,
+ }
+}
+
+// Next returns false if the stream has ended or an error occurred.
+// Call Stream.Current() to get the current value.
+// Call Stream.Err() to get the error.
+//
+// for stream.Next() {
+// data := stream.Current()
+// }
+//
+// if stream.Err() != nil {
+// ...
+// }
+func (s *Stream[T]) Next() bool {
+ if s.err != nil {
+ return false
+ }
+
+ for s.decoder.Next() {
+ var nxt T
+ s.err = json.Unmarshal(s.decoder.Event().Data, &nxt)
+ if s.err != nil {
+ return false
+ }
+ s.cur = nxt
+ return true
+ }
+
+ // decoder.Next() may be false because of an error
+ s.err = s.decoder.Err()
+
+ return false
+}
+
+func (s *Stream[T]) Current() T {
+ return s.cur
+}
+
+func (s *Stream[T]) Err() error {
+ return s.err
+}
+
+func (s *Stream[T]) Close() error {
+ if s.decoder == nil {
+ // already closed
+ return nil
+ }
+ return s.decoder.Close()
+}
diff --git a/shared/constant/constants.go b/shared/constant/constants.go
index 60d880f..b109097 100644
--- a/shared/constant/constants.go
+++ b/shared/constant/constants.go
@@ -18,6 +18,18 @@ func ValueOf[T Constant[T]]() T {
return t.Default()
}
+type Log string // Always "log"
+type State string // Always "state"
+type StateUpdate string // Always "state_update"
+
+func (c Log) Default() Log { return "log" }
+func (c State) Default() State { return "state" }
+func (c StateUpdate) Default() StateUpdate { return "state_update" }
+
+func (c Log) MarshalJSON() ([]byte, error) { return marshalString(c) }
+func (c State) MarshalJSON() ([]byte, error) { return marshalString(c) }
+func (c StateUpdate) MarshalJSON() ([]byte, error) { return marshalString(c) }
+
type constant[T any] interface {
Constant[T]
*T
From b963ddcc96d51998117db01f916127ffd2a0338c Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Mon, 19 May 2025 21:18:27 +0000
Subject: [PATCH 3/4] release: 0.1.0-alpha.3
---
.release-please-manifest.json | 2 +-
CHANGELOG.md | 8 ++++++++
README.md | 2 +-
internal/version.go | 2 +-
4 files changed, 11 insertions(+), 3 deletions(-)
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index f14b480..aaf968a 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "0.1.0-alpha.2"
+ ".": "0.1.0-alpha.3"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 65d63d5..a8f222a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,13 @@
# Changelog
+## 0.1.0-alpha.3 (2025-05-19)
+
+Full Changelog: [v0.1.0-alpha.2...v0.1.0-alpha.3](https://github.com/onkernel/kernel-go-sdk/compare/v0.1.0-alpha.2...v0.1.0-alpha.3)
+
+### Features
+
+* **api:** update via SDK Studio ([4a12204](https://github.com/onkernel/kernel-go-sdk/commit/4a122041257d78d3b583408e912d4d8a5b10cdc0))
+
## 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)
diff --git a/README.md b/README.md
index 532eb7e..b2add49 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.2'
+go get -u 'github.com/onkernel/kernel-go-sdk@v0.1.0-alpha.3'
```
diff --git a/internal/version.go b/internal/version.go
index d6f40b3..2d1d85e 100644
--- a/internal/version.go
+++ b/internal/version.go
@@ -2,4 +2,4 @@
package internal
-const PackageVersion = "0.1.0-alpha.2" // x-release-please-version
+const PackageVersion = "0.1.0-alpha.3" // x-release-please-version
From c39e15485c44c2291b939cce3d77632c06ef0551 Mon Sep 17 00:00:00 2001
From: Rafael
Date: Mon, 19 May 2025 17:21:43 -0400
Subject: [PATCH 4/4] fix ssestream fmt import
---
packages/ssestream/ssestream.go | 1 +
1 file changed, 1 insertion(+)
diff --git a/packages/ssestream/ssestream.go b/packages/ssestream/ssestream.go
index d6f6f3e..02962ca 100644
--- a/packages/ssestream/ssestream.go
+++ b/packages/ssestream/ssestream.go
@@ -9,6 +9,7 @@ import (
"io"
"net/http"
"strings"
+ "fmt"
)
type Decoder interface {