diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f749368..4bf1e90 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,6 +17,7 @@ jobs: timeout-minutes: 10 name: lint runs-on: ${{ github.repository == 'stainless-sdks/opencode-go' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} + if: github.event_name == 'push' || github.event.pull_request.head.repo.fork steps: - uses: actions/checkout@v4 @@ -32,6 +33,7 @@ jobs: timeout-minutes: 10 name: test runs-on: ${{ github.repository == 'stainless-sdks/opencode-go' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} + if: github.event_name == 'push' || github.event.pull_request.head.repo.fork steps: - uses: actions/checkout@v4 diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 4f9005e..b5db7ce 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.1.0-alpha.6" + ".": "0.1.0-alpha.7" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 69d1c49..3857fb8 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 16 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/opencode%2Fopencode-b4a3f35e4a44e5a5034508ced15d7b44c1924000062e0f5293797413d26ee412.yml -openapi_spec_hash: f17b1091020f90126e6cefc2d38ff85f -config_hash: 1156f6f6fb7245e7b021daddf23153e3 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/opencode%2Fopencode-384a94f70b48c84af9eddcac72bbe12952c3ae3bd7fededfa1c63b203d12d828.yml +openapi_spec_hash: e47ad28d646736d5d79d2dd1086d517d +config_hash: e2d21e779cfc4e26a99b9e4e75de3f50 diff --git a/CHANGELOG.md b/CHANGELOG.md index 7134467..a883937 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## 0.1.0-alpha.7 (2025-06-30) + +Full Changelog: [v0.1.0-alpha.6...v0.1.0-alpha.7](https://github.com/sst/opencode-sdk-go/compare/v0.1.0-alpha.6...v0.1.0-alpha.7) + +### Features + +* **api:** update via SDK Studio ([13550a5](https://github.com/sst/opencode-sdk-go/commit/13550a5c65d77325e945ed99fe0799cd1107b775)) +* **api:** update via SDK Studio ([7b73730](https://github.com/sst/opencode-sdk-go/commit/7b73730c7fa62ba966dda3541c3e97b49be8d2bf)) + + +### Chores + +* **ci:** only run for pushes and fork pull requests ([bea59b8](https://github.com/sst/opencode-sdk-go/commit/bea59b886800ef555f89c47a9256d6392ed2e53d)) + ## 0.1.0-alpha.6 (2025-06-28) Full Changelog: [v0.1.0-alpha.5...v0.1.0-alpha.6](https://github.com/sst/opencode-sdk-go/compare/v0.1.0-alpha.5...v0.1.0-alpha.6) diff --git a/README.md b/README.md index e6c0488..b0585d1 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Or to pin the version: ```sh -go get -u 'github.com/sst/opencode-sdk-go@v0.1.0-alpha.6' +go get -u 'github.com/sst/opencode-sdk-go@v0.1.0-alpha.7' ``` diff --git a/app.go b/app.go index b77e37e..dc44a74 100644 --- a/app.go +++ b/app.go @@ -50,7 +50,6 @@ type App struct { Git bool `json:"git,required"` Hostname string `json:"hostname,required"` Path AppPath `json:"path,required"` - Project string `json:"project,required"` Time AppTime `json:"time,required"` User string `json:"user,required"` JSON appJSON `json:"-"` @@ -61,7 +60,6 @@ type appJSON struct { Git apijson.Field Hostname apijson.Field Path apijson.Field - Project apijson.Field Time apijson.Field User apijson.Field raw string diff --git a/client_test.go b/client_test.go index 314424e..e75d649 100644 --- a/client_test.go +++ b/client_test.go @@ -5,6 +5,7 @@ package opencode_test import ( "context" "fmt" + "io" "net/http" "reflect" "testing" @@ -233,3 +234,99 @@ 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 := opencode.NewClient( + 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.Event.ListStreaming(deadlineCtx) + 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 := opencode.NewClient( + 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.Event.ListStreaming(context.Background(), 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/config.go b/config.go index 0fb66ef..9fb8f9c 100644 --- a/config.go +++ b/config.go @@ -190,6 +190,8 @@ type ConfigMcp struct { Type ConfigMcpType `json:"type,required"` // This field can have the runtime type of [[]string]. Command interface{} `json:"command"` + // Enable or disable the MCP server on startup + Enabled bool `json:"enabled"` // This field can have the runtime type of [map[string]string]. Environment interface{} `json:"environment"` // URL of the remote MCP server @@ -202,6 +204,7 @@ type ConfigMcp struct { type configMcpJSON struct { Type apijson.Field Command apijson.Field + Enabled apijson.Field Environment apijson.Field URL apijson.Field raw string @@ -307,6 +310,7 @@ type ConfigProviderModel struct { Name string `json:"name"` Options map[string]interface{} `json:"options"` Reasoning bool `json:"reasoning"` + ReleaseDate string `json:"release_date"` Temperature bool `json:"temperature"` ToolCall bool `json:"tool_call"` JSON configProviderModelJSON `json:"-"` @@ -322,6 +326,7 @@ type configProviderModelJSON struct { Name apijson.Field Options apijson.Field Reasoning apijson.Field + ReleaseDate apijson.Field Temperature apijson.Field ToolCall apijson.Field raw string @@ -490,6 +495,8 @@ type McpLocal struct { Command []string `json:"command,required"` // Type of MCP server connection Type McpLocalType `json:"type,required"` + // Enable or disable the MCP server on startup + Enabled bool `json:"enabled"` // Environment variables to set when running the MCP server Environment map[string]string `json:"environment"` JSON mcpLocalJSON `json:"-"` @@ -499,6 +506,7 @@ type McpLocal struct { type mcpLocalJSON struct { Command apijson.Field Type apijson.Field + Enabled apijson.Field Environment apijson.Field raw string ExtraFields map[string]apijson.Field @@ -533,14 +541,17 @@ type McpRemote struct { // Type of MCP server connection Type McpRemoteType `json:"type,required"` // URL of the remote MCP server - URL string `json:"url,required"` - JSON mcpRemoteJSON `json:"-"` + URL string `json:"url,required"` + // Enable or disable the MCP server on startup + Enabled bool `json:"enabled"` + JSON mcpRemoteJSON `json:"-"` } // mcpRemoteJSON contains the JSON metadata for the struct [McpRemote] type mcpRemoteJSON struct { Type apijson.Field URL apijson.Field + Enabled apijson.Field raw string ExtraFields map[string]apijson.Field } @@ -578,6 +589,7 @@ type Model struct { Name string `json:"name,required"` Options map[string]interface{} `json:"options,required"` Reasoning bool `json:"reasoning,required"` + ReleaseDate string `json:"release_date,required"` Temperature bool `json:"temperature,required"` ToolCall bool `json:"tool_call,required"` JSON modelJSON `json:"-"` @@ -592,6 +604,7 @@ type modelJSON struct { Name apijson.Field Options apijson.Field Reasoning apijson.Field + ReleaseDate apijson.Field Temperature apijson.Field ToolCall apijson.Field raw string diff --git a/event.go b/event.go index 63627d6..31253f9 100644 --- a/event.go +++ b/event.go @@ -10,6 +10,7 @@ import ( "github.com/sst/opencode-sdk-go/internal/apijson" "github.com/sst/opencode-sdk-go/internal/requestconfig" "github.com/sst/opencode-sdk-go/option" + "github.com/sst/opencode-sdk-go/packages/ssestream" "github.com/sst/opencode-sdk-go/shared" "github.com/tidwall/gjson" ) @@ -34,23 +35,29 @@ func NewEventService(opts ...option.RequestOption) (r *EventService) { } // Get events -func (r *EventService) List(ctx context.Context, opts ...option.RequestOption) (res *EventListResponse, err error) { +func (r *EventService) ListStreaming(ctx context.Context, opts ...option.RequestOption) (stream *ssestream.Stream[EventListResponse]) { + var ( + raw *http.Response + err error + ) opts = append(r.Options[:], opts...) path := "event" - err = requestconfig.ExecuteNewRequest(ctx, http.MethodGet, path, nil, &res, opts...) - return + err = requestconfig.ExecuteNewRequest(ctx, http.MethodGet, path, nil, &raw, opts...) + return ssestream.NewStream[EventListResponse](ssestream.NewDecoder(raw), err) } type EventListResponse struct { // This field can have the runtime type of - // [EventListResponseEventStorageWriteProperties], - // [EventListResponseEventInstallationUpdatedProperties], // [EventListResponseEventLspClientDiagnosticsProperties], // [EventListResponseEventPermissionUpdatedProperties], + // [EventListResponseEventFileEditedProperties], + // [EventListResponseEventStorageWriteProperties], + // [EventListResponseEventInstallationUpdatedProperties], // [EventListResponseEventMessageUpdatedProperties], // [EventListResponseEventMessagePartUpdatedProperties], // [EventListResponseEventSessionUpdatedProperties], // [EventListResponseEventSessionDeletedProperties], + // [EventListResponseEventSessionIdleProperties], // [EventListResponseEventSessionErrorProperties]. Properties interface{} `json:"properties,required"` Type EventListResponseType `json:"type,required"` @@ -83,26 +90,27 @@ func (r *EventListResponse) UnmarshalJSON(data []byte) (err error) { // AsUnion returns a [EventListResponseUnion] interface which you can cast to the // specific types for more type safety. // -// Possible runtime types of the union are [EventListResponseEventStorageWrite], -// [EventListResponseEventInstallationUpdated], +// Possible runtime types of the union are // [EventListResponseEventLspClientDiagnostics], -// [EventListResponseEventPermissionUpdated], +// [EventListResponseEventPermissionUpdated], [EventListResponseEventFileEdited], +// [EventListResponseEventStorageWrite], +// [EventListResponseEventInstallationUpdated], // [EventListResponseEventMessageUpdated], // [EventListResponseEventMessagePartUpdated], // [EventListResponseEventSessionUpdated], [EventListResponseEventSessionDeleted], -// [EventListResponseEventSessionError]. +// [EventListResponseEventSessionIdle], [EventListResponseEventSessionError]. func (r EventListResponse) AsUnion() EventListResponseUnion { return r.union } -// Union satisfied by [EventListResponseEventStorageWrite], +// Union satisfied by [EventListResponseEventLspClientDiagnostics], +// [EventListResponseEventPermissionUpdated], [EventListResponseEventFileEdited], +// [EventListResponseEventStorageWrite], // [EventListResponseEventInstallationUpdated], -// [EventListResponseEventLspClientDiagnostics], -// [EventListResponseEventPermissionUpdated], // [EventListResponseEventMessageUpdated], // [EventListResponseEventMessagePartUpdated], -// [EventListResponseEventSessionUpdated], [EventListResponseEventSessionDeleted] -// or [EventListResponseEventSessionError]. +// [EventListResponseEventSessionUpdated], [EventListResponseEventSessionDeleted], +// [EventListResponseEventSessionIdle] or [EventListResponseEventSessionError]. type EventListResponseUnion interface { implementsEventListResponse() } @@ -111,16 +119,6 @@ func init() { apijson.RegisterUnion( reflect.TypeOf((*EventListResponseUnion)(nil)).Elem(), "type", - apijson.UnionVariant{ - TypeFilter: gjson.JSON, - Type: reflect.TypeOf(EventListResponseEventStorageWrite{}), - DiscriminatorValue: "storage.write", - }, - apijson.UnionVariant{ - TypeFilter: gjson.JSON, - Type: reflect.TypeOf(EventListResponseEventInstallationUpdated{}), - DiscriminatorValue: "installation.updated", - }, apijson.UnionVariant{ TypeFilter: gjson.JSON, Type: reflect.TypeOf(EventListResponseEventLspClientDiagnostics{}), @@ -131,6 +129,21 @@ func init() { Type: reflect.TypeOf(EventListResponseEventPermissionUpdated{}), DiscriminatorValue: "permission.updated", }, + apijson.UnionVariant{ + TypeFilter: gjson.JSON, + Type: reflect.TypeOf(EventListResponseEventFileEdited{}), + DiscriminatorValue: "file.edited", + }, + apijson.UnionVariant{ + TypeFilter: gjson.JSON, + Type: reflect.TypeOf(EventListResponseEventStorageWrite{}), + DiscriminatorValue: "storage.write", + }, + apijson.UnionVariant{ + TypeFilter: gjson.JSON, + Type: reflect.TypeOf(EventListResponseEventInstallationUpdated{}), + DiscriminatorValue: "installation.updated", + }, apijson.UnionVariant{ TypeFilter: gjson.JSON, Type: reflect.TypeOf(EventListResponseEventMessageUpdated{}), @@ -151,6 +164,11 @@ func init() { Type: reflect.TypeOf(EventListResponseEventSessionDeleted{}), DiscriminatorValue: "session.deleted", }, + apijson.UnionVariant{ + TypeFilter: gjson.JSON, + Type: reflect.TypeOf(EventListResponseEventSessionIdle{}), + DiscriminatorValue: "session.idle", + }, apijson.UnionVariant{ TypeFilter: gjson.JSON, Type: reflect.TypeOf(EventListResponseEventSessionError{}), @@ -159,128 +177,6 @@ func init() { ) } -type EventListResponseEventStorageWrite struct { - Properties EventListResponseEventStorageWriteProperties `json:"properties,required"` - Type EventListResponseEventStorageWriteType `json:"type,required"` - JSON eventListResponseEventStorageWriteJSON `json:"-"` -} - -// eventListResponseEventStorageWriteJSON contains the JSON metadata for the struct -// [EventListResponseEventStorageWrite] -type eventListResponseEventStorageWriteJSON struct { - Properties apijson.Field - Type apijson.Field - raw string - ExtraFields map[string]apijson.Field -} - -func (r *EventListResponseEventStorageWrite) UnmarshalJSON(data []byte) (err error) { - return apijson.UnmarshalRoot(data, r) -} - -func (r eventListResponseEventStorageWriteJSON) RawJSON() string { - return r.raw -} - -func (r EventListResponseEventStorageWrite) implementsEventListResponse() {} - -type EventListResponseEventStorageWriteProperties struct { - Key string `json:"key,required"` - Content interface{} `json:"content"` - JSON eventListResponseEventStorageWritePropertiesJSON `json:"-"` -} - -// eventListResponseEventStorageWritePropertiesJSON contains the JSON metadata for -// the struct [EventListResponseEventStorageWriteProperties] -type eventListResponseEventStorageWritePropertiesJSON struct { - Key apijson.Field - Content apijson.Field - raw string - ExtraFields map[string]apijson.Field -} - -func (r *EventListResponseEventStorageWriteProperties) UnmarshalJSON(data []byte) (err error) { - return apijson.UnmarshalRoot(data, r) -} - -func (r eventListResponseEventStorageWritePropertiesJSON) RawJSON() string { - return r.raw -} - -type EventListResponseEventStorageWriteType string - -const ( - EventListResponseEventStorageWriteTypeStorageWrite EventListResponseEventStorageWriteType = "storage.write" -) - -func (r EventListResponseEventStorageWriteType) IsKnown() bool { - switch r { - case EventListResponseEventStorageWriteTypeStorageWrite: - return true - } - return false -} - -type EventListResponseEventInstallationUpdated struct { - Properties EventListResponseEventInstallationUpdatedProperties `json:"properties,required"` - Type EventListResponseEventInstallationUpdatedType `json:"type,required"` - JSON eventListResponseEventInstallationUpdatedJSON `json:"-"` -} - -// eventListResponseEventInstallationUpdatedJSON contains the JSON metadata for the -// struct [EventListResponseEventInstallationUpdated] -type eventListResponseEventInstallationUpdatedJSON struct { - Properties apijson.Field - Type apijson.Field - raw string - ExtraFields map[string]apijson.Field -} - -func (r *EventListResponseEventInstallationUpdated) UnmarshalJSON(data []byte) (err error) { - return apijson.UnmarshalRoot(data, r) -} - -func (r eventListResponseEventInstallationUpdatedJSON) RawJSON() string { - return r.raw -} - -func (r EventListResponseEventInstallationUpdated) implementsEventListResponse() {} - -type EventListResponseEventInstallationUpdatedProperties struct { - Version string `json:"version,required"` - JSON eventListResponseEventInstallationUpdatedPropertiesJSON `json:"-"` -} - -// eventListResponseEventInstallationUpdatedPropertiesJSON contains the JSON -// metadata for the struct [EventListResponseEventInstallationUpdatedProperties] -type eventListResponseEventInstallationUpdatedPropertiesJSON struct { - Version apijson.Field - raw string - ExtraFields map[string]apijson.Field -} - -func (r *EventListResponseEventInstallationUpdatedProperties) UnmarshalJSON(data []byte) (err error) { - return apijson.UnmarshalRoot(data, r) -} - -func (r eventListResponseEventInstallationUpdatedPropertiesJSON) RawJSON() string { - return r.raw -} - -type EventListResponseEventInstallationUpdatedType string - -const ( - EventListResponseEventInstallationUpdatedTypeInstallationUpdated EventListResponseEventInstallationUpdatedType = "installation.updated" -) - -func (r EventListResponseEventInstallationUpdatedType) IsKnown() bool { - switch r { - case EventListResponseEventInstallationUpdatedTypeInstallationUpdated: - return true - } - return false -} - type EventListResponseEventLspClientDiagnostics struct { Properties EventListResponseEventLspClientDiagnosticsProperties `json:"properties,required"` Type EventListResponseEventLspClientDiagnosticsType `json:"type,required"` @@ -432,6 +328,188 @@ func (r EventListResponseEventPermissionUpdatedType) IsKnown() bool { return false } +type EventListResponseEventFileEdited struct { + Properties EventListResponseEventFileEditedProperties `json:"properties,required"` + Type EventListResponseEventFileEditedType `json:"type,required"` + JSON eventListResponseEventFileEditedJSON `json:"-"` +} + +// eventListResponseEventFileEditedJSON contains the JSON metadata for the struct +// [EventListResponseEventFileEdited] +type eventListResponseEventFileEditedJSON struct { + Properties apijson.Field + Type apijson.Field + raw string + ExtraFields map[string]apijson.Field +} + +func (r *EventListResponseEventFileEdited) UnmarshalJSON(data []byte) (err error) { + return apijson.UnmarshalRoot(data, r) +} + +func (r eventListResponseEventFileEditedJSON) RawJSON() string { + return r.raw +} + +func (r EventListResponseEventFileEdited) implementsEventListResponse() {} + +type EventListResponseEventFileEditedProperties struct { + File string `json:"file,required"` + JSON eventListResponseEventFileEditedPropertiesJSON `json:"-"` +} + +// eventListResponseEventFileEditedPropertiesJSON contains the JSON metadata for +// the struct [EventListResponseEventFileEditedProperties] +type eventListResponseEventFileEditedPropertiesJSON struct { + File apijson.Field + raw string + ExtraFields map[string]apijson.Field +} + +func (r *EventListResponseEventFileEditedProperties) UnmarshalJSON(data []byte) (err error) { + return apijson.UnmarshalRoot(data, r) +} + +func (r eventListResponseEventFileEditedPropertiesJSON) RawJSON() string { + return r.raw +} + +type EventListResponseEventFileEditedType string + +const ( + EventListResponseEventFileEditedTypeFileEdited EventListResponseEventFileEditedType = "file.edited" +) + +func (r EventListResponseEventFileEditedType) IsKnown() bool { + switch r { + case EventListResponseEventFileEditedTypeFileEdited: + return true + } + return false +} + +type EventListResponseEventStorageWrite struct { + Properties EventListResponseEventStorageWriteProperties `json:"properties,required"` + Type EventListResponseEventStorageWriteType `json:"type,required"` + JSON eventListResponseEventStorageWriteJSON `json:"-"` +} + +// eventListResponseEventStorageWriteJSON contains the JSON metadata for the struct +// [EventListResponseEventStorageWrite] +type eventListResponseEventStorageWriteJSON struct { + Properties apijson.Field + Type apijson.Field + raw string + ExtraFields map[string]apijson.Field +} + +func (r *EventListResponseEventStorageWrite) UnmarshalJSON(data []byte) (err error) { + return apijson.UnmarshalRoot(data, r) +} + +func (r eventListResponseEventStorageWriteJSON) RawJSON() string { + return r.raw +} + +func (r EventListResponseEventStorageWrite) implementsEventListResponse() {} + +type EventListResponseEventStorageWriteProperties struct { + Key string `json:"key,required"` + Content interface{} `json:"content"` + JSON eventListResponseEventStorageWritePropertiesJSON `json:"-"` +} + +// eventListResponseEventStorageWritePropertiesJSON contains the JSON metadata for +// the struct [EventListResponseEventStorageWriteProperties] +type eventListResponseEventStorageWritePropertiesJSON struct { + Key apijson.Field + Content apijson.Field + raw string + ExtraFields map[string]apijson.Field +} + +func (r *EventListResponseEventStorageWriteProperties) UnmarshalJSON(data []byte) (err error) { + return apijson.UnmarshalRoot(data, r) +} + +func (r eventListResponseEventStorageWritePropertiesJSON) RawJSON() string { + return r.raw +} + +type EventListResponseEventStorageWriteType string + +const ( + EventListResponseEventStorageWriteTypeStorageWrite EventListResponseEventStorageWriteType = "storage.write" +) + +func (r EventListResponseEventStorageWriteType) IsKnown() bool { + switch r { + case EventListResponseEventStorageWriteTypeStorageWrite: + return true + } + return false +} + +type EventListResponseEventInstallationUpdated struct { + Properties EventListResponseEventInstallationUpdatedProperties `json:"properties,required"` + Type EventListResponseEventInstallationUpdatedType `json:"type,required"` + JSON eventListResponseEventInstallationUpdatedJSON `json:"-"` +} + +// eventListResponseEventInstallationUpdatedJSON contains the JSON metadata for the +// struct [EventListResponseEventInstallationUpdated] +type eventListResponseEventInstallationUpdatedJSON struct { + Properties apijson.Field + Type apijson.Field + raw string + ExtraFields map[string]apijson.Field +} + +func (r *EventListResponseEventInstallationUpdated) UnmarshalJSON(data []byte) (err error) { + return apijson.UnmarshalRoot(data, r) +} + +func (r eventListResponseEventInstallationUpdatedJSON) RawJSON() string { + return r.raw +} + +func (r EventListResponseEventInstallationUpdated) implementsEventListResponse() {} + +type EventListResponseEventInstallationUpdatedProperties struct { + Version string `json:"version,required"` + JSON eventListResponseEventInstallationUpdatedPropertiesJSON `json:"-"` +} + +// eventListResponseEventInstallationUpdatedPropertiesJSON contains the JSON +// metadata for the struct [EventListResponseEventInstallationUpdatedProperties] +type eventListResponseEventInstallationUpdatedPropertiesJSON struct { + Version apijson.Field + raw string + ExtraFields map[string]apijson.Field +} + +func (r *EventListResponseEventInstallationUpdatedProperties) UnmarshalJSON(data []byte) (err error) { + return apijson.UnmarshalRoot(data, r) +} + +func (r eventListResponseEventInstallationUpdatedPropertiesJSON) RawJSON() string { + return r.raw +} + +type EventListResponseEventInstallationUpdatedType string + +const ( + EventListResponseEventInstallationUpdatedTypeInstallationUpdated EventListResponseEventInstallationUpdatedType = "installation.updated" +) + +func (r EventListResponseEventInstallationUpdatedType) IsKnown() bool { + switch r { + case EventListResponseEventInstallationUpdatedTypeInstallationUpdated: + return true + } + return false +} + type EventListResponseEventMessageUpdated struct { Properties EventListResponseEventMessageUpdatedProperties `json:"properties,required"` Type EventListResponseEventMessageUpdatedType `json:"type,required"` @@ -676,6 +754,66 @@ func (r EventListResponseEventSessionDeletedType) IsKnown() bool { return false } +type EventListResponseEventSessionIdle struct { + Properties EventListResponseEventSessionIdleProperties `json:"properties,required"` + Type EventListResponseEventSessionIdleType `json:"type,required"` + JSON eventListResponseEventSessionIdleJSON `json:"-"` +} + +// eventListResponseEventSessionIdleJSON contains the JSON metadata for the struct +// [EventListResponseEventSessionIdle] +type eventListResponseEventSessionIdleJSON struct { + Properties apijson.Field + Type apijson.Field + raw string + ExtraFields map[string]apijson.Field +} + +func (r *EventListResponseEventSessionIdle) UnmarshalJSON(data []byte) (err error) { + return apijson.UnmarshalRoot(data, r) +} + +func (r eventListResponseEventSessionIdleJSON) RawJSON() string { + return r.raw +} + +func (r EventListResponseEventSessionIdle) implementsEventListResponse() {} + +type EventListResponseEventSessionIdleProperties struct { + SessionID string `json:"sessionID,required"` + JSON eventListResponseEventSessionIdlePropertiesJSON `json:"-"` +} + +// eventListResponseEventSessionIdlePropertiesJSON contains the JSON metadata for +// the struct [EventListResponseEventSessionIdleProperties] +type eventListResponseEventSessionIdlePropertiesJSON struct { + SessionID apijson.Field + raw string + ExtraFields map[string]apijson.Field +} + +func (r *EventListResponseEventSessionIdleProperties) UnmarshalJSON(data []byte) (err error) { + return apijson.UnmarshalRoot(data, r) +} + +func (r eventListResponseEventSessionIdlePropertiesJSON) RawJSON() string { + return r.raw +} + +type EventListResponseEventSessionIdleType string + +const ( + EventListResponseEventSessionIdleTypeSessionIdle EventListResponseEventSessionIdleType = "session.idle" +) + +func (r EventListResponseEventSessionIdleType) IsKnown() bool { + switch r { + case EventListResponseEventSessionIdleTypeSessionIdle: + return true + } + return false +} + type EventListResponseEventSessionError struct { Properties EventListResponseEventSessionErrorProperties `json:"properties,required"` Type EventListResponseEventSessionErrorType `json:"type,required"` @@ -865,20 +1003,22 @@ func (r EventListResponseEventSessionErrorType) IsKnown() bool { type EventListResponseType string const ( - EventListResponseTypeStorageWrite EventListResponseType = "storage.write" - EventListResponseTypeInstallationUpdated EventListResponseType = "installation.updated" EventListResponseTypeLspClientDiagnostics EventListResponseType = "lsp.client.diagnostics" EventListResponseTypePermissionUpdated EventListResponseType = "permission.updated" + EventListResponseTypeFileEdited EventListResponseType = "file.edited" + EventListResponseTypeStorageWrite EventListResponseType = "storage.write" + EventListResponseTypeInstallationUpdated EventListResponseType = "installation.updated" EventListResponseTypeMessageUpdated EventListResponseType = "message.updated" EventListResponseTypeMessagePartUpdated EventListResponseType = "message.part.updated" EventListResponseTypeSessionUpdated EventListResponseType = "session.updated" EventListResponseTypeSessionDeleted EventListResponseType = "session.deleted" + EventListResponseTypeSessionIdle EventListResponseType = "session.idle" EventListResponseTypeSessionError EventListResponseType = "session.error" ) func (r EventListResponseType) IsKnown() bool { switch r { - case EventListResponseTypeStorageWrite, EventListResponseTypeInstallationUpdated, EventListResponseTypeLspClientDiagnostics, EventListResponseTypePermissionUpdated, EventListResponseTypeMessageUpdated, EventListResponseTypeMessagePartUpdated, EventListResponseTypeSessionUpdated, EventListResponseTypeSessionDeleted, EventListResponseTypeSessionError: + case EventListResponseTypeLspClientDiagnostics, EventListResponseTypePermissionUpdated, EventListResponseTypeFileEdited, EventListResponseTypeStorageWrite, EventListResponseTypeInstallationUpdated, EventListResponseTypeMessageUpdated, EventListResponseTypeMessagePartUpdated, EventListResponseTypeSessionUpdated, EventListResponseTypeSessionDeleted, EventListResponseTypeSessionIdle, EventListResponseTypeSessionError: return true } return false diff --git a/event_test.go b/event_test.go deleted file mode 100644 index 10c67b1..0000000 --- a/event_test.go +++ /dev/null @@ -1,36 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -package opencode_test - -import ( - "context" - "errors" - "os" - "testing" - - "github.com/sst/opencode-sdk-go" - "github.com/sst/opencode-sdk-go/internal/testutil" - "github.com/sst/opencode-sdk-go/option" -) - -func TestEventList(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 := opencode.NewClient( - option.WithBaseURL(baseURL), - ) - _, err := client.Event.List(context.TODO()) - if err != nil { - var apierr *opencode.Error - if errors.As(err, &apierr) { - t.Log(string(apierr.DumpRequest(true))) - } - t.Fatalf("err should be nil: %s", err.Error()) - } -} diff --git a/internal/version.go b/internal/version.go index 0ff00c6..c0909e1 100644 --- a/internal/version.go +++ b/internal/version.go @@ -2,4 +2,4 @@ package internal -const PackageVersion = "0.1.0-alpha.6" // x-release-please-version +const PackageVersion = "0.1.0-alpha.7" // x-release-please-version diff --git a/packages/ssestream/ssestream.go b/packages/ssestream/ssestream.go new file mode 100644 index 0000000..81adbd6 --- /dev/null +++ b/packages/ssestream/ssestream.go @@ -0,0 +1,181 @@ +// 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 { + scn := bufio.NewScanner(res.Body) + scn.Buffer(nil, bufio.MaxScanTokenSize<<4) + decoder = &eventStreamDecoder{rc: res.Body, scn: scn} + } + 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 + scn *bufio.Scanner + err error +} + +func (s *eventStreamDecoder) Next() bool { + if s.err != nil { + return false + } + + event := "" + data := bytes.NewBuffer(nil) + + for s.scn.Scan() { + txt := s.scn.Bytes() + + // 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 + } + } + } + + if s.scn.Err() != nil { + s.err = s.scn.Err() + } + + 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() +}