chore: sync repo

This commit is contained in:
stainless-app[bot] 2025-08-31 05:32:21 +00:00
parent e3338fbc97
commit 2f34d5d53e
94 changed files with 14462 additions and 5780 deletions

266
README.md
View file

@ -1,34 +1,26 @@
# Opencode Go API Library
<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go"><img src="https://pkg.go.dev/badge/github.com/sst/opencode-sdk-go.svg" alt="Go Reference"></a>
<a href="https://pkg.go.dev/github.com/stainless-sdks/opencode-go"><img src="https://pkg.go.dev/badge/github.com/stainless-sdks/opencode-go.svg" alt="Go Reference"></a>
The Opencode Go library provides convenient access to the [Opencode REST API](https://opencode.ai/docs)
The Opencode Go library provides convenient access to the Opencode REST API
from applications written in Go.
It is generated with [Stainless](https://www.stainless.com/).
## Installation
<!-- x-release-please-start-version -->
```go
import (
"github.com/sst/opencode-sdk-go" // imported as opencode
"github.com/stainless-sdks/opencode-go" // imported as opencode
)
```
<!-- x-release-please-end -->
Or to pin the version:
<!-- x-release-please-start-version -->
```sh
go get -u 'github.com/sst/opencode-sdk-go@v0.1.0-alpha.29'
go get -u 'github.com/stainless-sdks/opencode-go@v0.0.1'
```
<!-- x-release-please-end -->
## Requirements
This library requires Go 1.18+.
@ -44,83 +36,164 @@ import (
"context"
"fmt"
"github.com/sst/opencode-sdk-go"
"github.com/stainless-sdks/opencode-go"
"github.com/stainless-sdks/opencode-go/option"
)
func main() {
client := opencode.NewClient()
sessions, err := client.Session.List(context.TODO())
client := opencode.NewClient(
option.WithAPIKey("My API Key"), // defaults to os.LookupEnv("OPENCODE_API_KEY")
)
projects, err := client.Project.List(context.TODO())
if err != nil {
panic(err.Error())
}
fmt.Printf("%+v\n", sessions)
fmt.Printf("%+v\n", projects)
}
```
### Request fields
All request parameters are wrapped in a generic `Field` type,
which we use to distinguish zero values from null or omitted fields.
The opencode library uses the [`omitzero`](https://tip.golang.org/doc/go1.24#encodingjsonpkgencodingjson)
semantics from the Go 1.24+ `encoding/json` release for request fields.
This prevents accidentally sending a zero value if you forget a required parameter,
and enables explicitly sending `null`, `false`, `''`, or `0` on optional parameters.
Any field not specified is not sent.
Required primitive fields (`int64`, `string`, etc.) feature the tag <code>\`json:"...,required"\`</code>. These
fields are always serialized, even their zero values.
To construct fields with values, use the helpers `String()`, `Int()`, `Float()`, or most commonly, the generic `F[T]()`.
To send a null, use `Null[T]()`, and to send a nonconforming value, use `Raw[T](any)`. For example:
Optional primitive types are wrapped in a `param.Opt[T]`. These fields can be set with the provided constructors, `opencode.String(string)`, `opencode.Int(int64)`, etc.
Any `param.Opt[T]`, map, slice, struct or string enum uses the
tag <code>\`json:"...,omitzero"\`</code>. Its zero value is considered omitted.
The `param.IsOmitted(any)` function can confirm the presence of any `omitzero` field.
```go
params := FooParams{
Name: opencode.F("hello"),
p := opencode.ExampleParams{
ID: "id_xxx", // required property
Name: opencode.String("..."), // optional property
// Explicitly send `"description": null`
Description: opencode.Null[string](),
Point: opencode.Point{
X: 0, // required field will serialize as 0
Y: opencode.Int(1), // optional field will serialize as 1
// ... omitted non-required fields will not be serialized
},
Point: opencode.F(opencode.Point{
X: opencode.Int(0),
Y: opencode.Int(1),
Origin: opencode.Origin{}, // the zero value of [Origin] is considered omitted
}
```
// In cases where the API specifies a given type,
// but you want to send something else, use `Raw`:
Z: opencode.Raw[int64](0.01), // sends a float
}),
To send `null` instead of a `param.Opt[T]`, use `param.Null[T]()`.
To send `null` instead of a struct `T`, use `param.NullStruct[T]()`.
```go
p.Name = param.Null[string]() // 'null' instead of string
p.Point = param.NullStruct[Point]() // 'null' instead of struct
param.IsNull(p.Name) // true
param.IsNull(p.Point) // true
```
Request structs contain a `.SetExtraFields(map[string]any)` method which can send non-conforming
fields in the request body. Extra fields overwrite any struct fields with a matching
key. For security reasons, only use `SetExtraFields` with trusted data.
To send a custom value instead of a struct, use `param.Override[T](value)`.
```go
// In cases where the API specifies a given type,
// but you want to send something else, use [SetExtraFields]:
p.SetExtraFields(map[string]any{
"x": 0.01, // send "x" as a float instead of int
})
// Send a number instead of an object
custom := param.Override[opencode.FooParams](12)
```
### Request unions
Unions are represented as a struct with fields prefixed by "Of" for each of it's variants,
only one field can be non-zero. The non-zero field will be serialized.
Sub-properties of the union can be accessed via methods on the union struct.
These methods return a mutable pointer to the underlying data, if present.
```go
// Only one field can be non-zero, use param.IsOmitted() to check if a field is set
type AnimalUnionParam struct {
OfCat *Cat `json:",omitzero,inline`
OfDog *Dog `json:",omitzero,inline`
}
animal := AnimalUnionParam{
OfCat: &Cat{
Name: "Whiskers",
Owner: PersonParam{
Address: AddressParam{Street: "3333 Coyote Hill Rd", Zip: 0},
},
},
}
// Mutating a field
if address := animal.GetOwner().GetAddress(); address != nil {
address.ZipCode = 94304
}
```
### Response objects
All fields in response structs are value types (not pointers or wrappers).
If a given field is `null`, not present, or invalid, the corresponding field
will simply be its zero value.
All response structs also include a special `JSON` field, containing more detailed
information about each property, which you can use like so:
All fields in response structs are ordinary value types (not pointers or wrappers).
Response structs also include a special `JSON` field containing metadata about
each property.
```go
if res.Name == "" {
// true if `"name"` is either not present or explicitly null
res.JSON.Name.IsNull()
// true if the `"name"` key was not present in the response JSON at all
res.JSON.Name.IsMissing()
// When the API returns data that cannot be coerced to the expected type:
if res.JSON.Name.IsInvalid() {
raw := res.JSON.Name.Raw()
legacyName := struct{
First string `json:"first"`
Last string `json:"last"`
}{}
json.Unmarshal([]byte(raw), &legacyName)
name = legacyName.First + " " + legacyName.Last
}
type Animal struct {
Name string `json:"name,nullable"`
Owners int `json:"owners"`
Age int `json:"age"`
JSON struct {
Name respjson.Field
Owner respjson.Field
Age respjson.Field
ExtraFields map[string]respjson.Field
} `json:"-"`
}
```
These `.JSON` structs also include an `Extras` map containing
To handle optional data, use the `.Valid()` method on the JSON field.
`.Valid()` returns true if a field is not `null`, not present, or couldn't be marshaled.
If `.Valid()` is false, the corresponding field will simply be its zero value.
```go
raw := `{"owners": 1, "name": null}`
var res Animal
json.Unmarshal([]byte(raw), &res)
// Accessing regular fields
res.Owners // 1
res.Name // ""
res.Age // 0
// Optional field checks
res.JSON.Owners.Valid() // true
res.JSON.Name.Valid() // false
res.JSON.Age.Valid() // false
// Raw JSON values
res.JSON.Owners.Raw() // "1"
res.JSON.Name.Raw() == "null" // true
res.JSON.Name.Raw() == respjson.Null // true
res.JSON.Age.Raw() == "" // true
res.JSON.Age.Raw() == respjson.Omitted // true
```
These `.JSON` structs also include an `ExtraFields` map containing
any properties in the json response that were not specified
in the struct. This can be useful for API features not yet
present in the SDK.
@ -129,6 +202,45 @@ present in the SDK.
body := res.JSON.ExtraFields["my_unexpected_field"].Raw()
```
### Response Unions
In responses, unions are represented by a flattened struct containing all possible fields from each of the
object variants.
To convert it to a variant use the `.AsFooVariant()` method or the `.AsAny()` method if present.
If a response value union contains primitive values, primitive fields will be alongside
the properties but prefixed with `Of` and feature the tag `json:"...,inline"`.
```go
type AnimalUnion struct {
// From variants [Dog], [Cat]
Owner Person `json:"owner"`
// From variant [Dog]
DogBreed string `json:"dog_breed"`
// From variant [Cat]
CatBreed string `json:"cat_breed"`
// ...
JSON struct {
Owner respjson.Field
// ...
} `json:"-"`
}
// If animal variant
if animal.Owner.Address.ZipCode == "" {
panic("missing zip code")
}
// Switch on the variant
switch variant := animal.AsAny().(type) {
case Dog:
case Cat:
default:
panic("unexpected type")
}
```
### RequestOptions
This library uses the functional options pattern. Functions defined in the
@ -142,7 +254,7 @@ client := opencode.NewClient(
option.WithHeader("X-Some-Header", "custom_header_info"),
)
client.Session.List(context.TODO(), ...,
client.Project.List(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
@ -150,7 +262,9 @@ client.Session.List(context.TODO(), ...,
)
```
See the [full list of request options](https://pkg.go.dev/github.com/sst/opencode-sdk-go/option).
The request option `option.WithDebugLog(nil)` may be helpful while debugging.
See the [full list of request options](https://pkg.go.dev/github.com/stainless-sdks/opencode-go/option).
### Pagination
@ -171,14 +285,14 @@ 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.Session.List(context.TODO())
_, err := client.Project.List(context.TODO())
if err != nil {
var apierr *opencode.Error
if errors.As(err, &apierr) {
println(string(apierr.DumpRequest(true))) // Prints the serialized HTTP request
println(string(apierr.DumpResponse(true))) // Prints the serialized HTTP response
}
panic(err.Error()) // GET "/session": 400 Bad Request { ... }
panic(err.Error()) // GET "/project": 400 Bad Request { ... }
}
```
@ -196,7 +310,7 @@ 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.Session.List(
client.Project.List(
ctx,
// This sets the per-retry timeout
option.WithRequestTimeout(20*time.Second),
@ -206,14 +320,14 @@ client.Session.List(
### File uploads
Request parameters that correspond to file uploads in multipart requests are typed as
`param.Field[io.Reader]`. The contents of the `io.Reader` will by default be sent as a multipart form
`io.Reader`. The contents of the `io.Reader` will by default be sent as a multipart form
part with the file name of "anonymous_file" and content-type of "application/octet-stream".
The file name and content-type can be customized by implementing `Name() string` or `ContentType()
string` on the run-time type of `io.Reader`. Note that `os.File` implements `Name() string`, so a
file returned by `os.Open` will be sent with the file name on disk.
We also provide a helper `opencode.FileParam(reader io.Reader, filename string, contentType string)`
We also provide a helper `opencode.File(reader io.Reader, filename string, contentType string)`
which can be used to wrap any `io.Reader` with the appropriate file name and content type.
### Retries
@ -231,7 +345,7 @@ client := opencode.NewClient(
)
// Override per-request:
client.Session.List(context.TODO(), option.WithMaxRetries(5))
client.Project.List(context.TODO(), option.WithMaxRetries(5))
```
### Accessing raw response data (e.g. response headers)
@ -242,11 +356,11 @@ you need to examine response headers, status codes, or other details.
```go
// Create a variable to store the HTTP response
var response *http.Response
sessions, err := client.Session.List(context.TODO(), option.WithResponseInto(&response))
projects, err := client.Project.List(context.TODO(), option.WithResponseInto(&response))
if err != nil {
// handle error
}
fmt.Printf("%+v\n", sessions)
fmt.Printf("%+v\n", projects)
fmt.Printf("Status Code: %d\n", response.StatusCode)
fmt.Printf("Headers: %+#v\n", response.Header)
@ -266,7 +380,7 @@ To make requests to undocumented endpoints, you can use `client.Get`, `client.Po
var (
// params can be an io.Reader, a []byte, an encoding/json serializable object,
// or a "…Params" struct defined in this library.
params map[string]interface{}
params map[string]any
// result can be an []byte, *http.Response, a encoding/json deserializable object,
// or a model defined in this library.
@ -285,10 +399,10 @@ or the `option.WithJSONSet()` methods.
```go
params := FooNewParams{
ID: opencode.F("id_xxxx"),
Data: opencode.F(FooNewParamsData{
FirstName: opencode.F("John"),
}),
ID: "id_xxxx",
Data: FooNewParamsData{
FirstName: opencode.String("John"),
},
}
client.Foo.New(context.Background(), params, option.WithJSONSet("data.last_name", "Doe"))
```
@ -347,7 +461,7 @@ This package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) con
We take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience.
We are keen for your feedback; please open an [issue](https://www.github.com/sst/opencode-sdk-go/issues) with questions, bugs, or suggestions.
We are keen for your feedback; please open an [issue](https://www.github.com/stainless-sdks/opencode-go/issues) with questions, bugs, or suggestions.
## Contributing