release: v0.9.8

This commit is contained in:
opencode
2025-09-17 07:14:33 +00:00
parent abd99aeb7d
commit e618cbc447
19 changed files with 75 additions and 167 deletions

View File

@@ -26,7 +26,7 @@
}, },
"cloud/core": { "cloud/core": {
"name": "@opencode/cloud-core", "name": "@opencode/cloud-core",
"version": "0.9.7", "version": "0.9.8",
"dependencies": { "dependencies": {
"@aws-sdk/client-sts": "3.782.0", "@aws-sdk/client-sts": "3.782.0",
"@opencode/cloud-resource": "workspace:*", "@opencode/cloud-resource": "workspace:*",
@@ -43,7 +43,7 @@
}, },
"cloud/function": { "cloud/function": {
"name": "@opencode/cloud-function", "name": "@opencode/cloud-function",
"version": "0.9.7", "version": "0.9.8",
"dependencies": { "dependencies": {
"@ai-sdk/anthropic": "2.0.0", "@ai-sdk/anthropic": "2.0.0",
"@ai-sdk/openai": "2.0.2", "@ai-sdk/openai": "2.0.2",
@@ -69,7 +69,7 @@
}, },
"cloud/scripts": { "cloud/scripts": {
"name": "@opencode/cloud-scripts", "name": "@opencode/cloud-scripts",
"version": "0.9.7", "version": "0.9.8",
"dependencies": { "dependencies": {
"@opencode/cloud-core": "workspace:*", "@opencode/cloud-core": "workspace:*",
"tsx": "4.20.5", "tsx": "4.20.5",
@@ -81,7 +81,7 @@
}, },
"packages/app": { "packages/app": {
"name": "@opencode/app", "name": "@opencode/app",
"version": "0.9.7", "version": "0.9.8",
"dependencies": { "dependencies": {
"@kobalte/core": "0.13.11", "@kobalte/core": "0.13.11",
"@opencode-ai/sdk": "workspace:*", "@opencode-ai/sdk": "workspace:*",
@@ -113,7 +113,7 @@
}, },
"packages/function": { "packages/function": {
"name": "@opencode/function", "name": "@opencode/function",
"version": "0.9.7", "version": "0.9.8",
"dependencies": { "dependencies": {
"@octokit/auth-app": "8.0.1", "@octokit/auth-app": "8.0.1",
"@octokit/rest": "22.0.0", "@octokit/rest": "22.0.0",
@@ -128,7 +128,7 @@
}, },
"packages/opencode": { "packages/opencode": {
"name": "opencode", "name": "opencode",
"version": "0.9.7", "version": "0.9.8",
"bin": { "bin": {
"opencode": "./bin/opencode", "opencode": "./bin/opencode",
}, },
@@ -179,7 +179,7 @@
}, },
"packages/plugin": { "packages/plugin": {
"name": "@opencode-ai/plugin", "name": "@opencode-ai/plugin",
"version": "0.9.7", "version": "0.9.8",
"dependencies": { "dependencies": {
"@opencode-ai/sdk": "workspace:*", "@opencode-ai/sdk": "workspace:*",
}, },
@@ -190,7 +190,7 @@
}, },
"packages/sdk/js": { "packages/sdk/js": {
"name": "@opencode-ai/sdk", "name": "@opencode-ai/sdk",
"version": "0.9.7", "version": "0.9.8",
"dependencies": { "dependencies": {
"@hey-api/openapi-ts": "0.81.0", "@hey-api/openapi-ts": "0.81.0",
}, },
@@ -202,7 +202,7 @@
}, },
"packages/web": { "packages/web": {
"name": "@opencode/web", "name": "@opencode/web",
"version": "0.9.7", "version": "0.9.8",
"dependencies": { "dependencies": {
"@astrojs/cloudflare": "12.6.3", "@astrojs/cloudflare": "12.6.3",
"@astrojs/markdown-remark": "6.3.1", "@astrojs/markdown-remark": "6.3.1",

View File

@@ -7,7 +7,7 @@
"dev:remote": "VITE_AUTH_URL=https://auth.dev.opencode.ai bun sst shell --stage=dev bun dev", "dev:remote": "VITE_AUTH_URL=https://auth.dev.opencode.ai bun sst shell --stage=dev bun dev",
"build": "vinxi build && ../../packages/opencode/script/schema.ts ./.output/public/config.json", "build": "vinxi build && ../../packages/opencode/script/schema.ts ./.output/public/config.json",
"start": "vinxi start", "start": "vinxi start",
"version": "0.9.7" "version": "0.9.8"
}, },
"dependencies": { "dependencies": {
"@ibm/plex": "6.4.1", "@ibm/plex": "6.4.1",

View File

@@ -1,7 +1,7 @@
{ {
"$schema": "https://json.schemastore.org/package.json", "$schema": "https://json.schemastore.org/package.json",
"name": "@opencode/cloud-core", "name": "@opencode/cloud-core",
"version": "0.9.7", "version": "0.9.8",
"private": true, "private": true,
"type": "module", "type": "module",
"dependencies": { "dependencies": {

View File

@@ -1,6 +1,6 @@
{ {
"name": "@opencode/cloud-function", "name": "@opencode/cloud-function",
"version": "0.9.7", "version": "0.9.8",
"$schema": "https://json.schemastore.org/package.json", "$schema": "https://json.schemastore.org/package.json",
"private": true, "private": true,
"type": "module", "type": "module",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@opencode/cloud-scripts", "name": "@opencode/cloud-scripts",
"version": "0.9.7", "version": "0.9.8",
"$schema": "https://json.schemastore.org/package.json", "$schema": "https://json.schemastore.org/package.json",
"private": true, "private": true,
"type": "module", "type": "module",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@opencode/app", "name": "@opencode/app",
"version": "0.9.7", "version": "0.9.8",
"description": "", "description": "",
"type": "module", "type": "module",
"scripts": { "scripts": {

View File

@@ -1,6 +1,6 @@
{ {
"name": "@opencode/function", "name": "@opencode/function",
"version": "0.9.7", "version": "0.9.8",
"$schema": "https://json.schemastore.org/package.json", "$schema": "https://json.schemastore.org/package.json",
"private": true, "private": true,
"type": "module", "type": "module",

View File

@@ -1,6 +1,6 @@
{ {
"$schema": "https://json.schemastore.org/package.json", "$schema": "https://json.schemastore.org/package.json",
"version": "0.9.7", "version": "0.9.8",
"name": "opencode", "name": "opencode",
"type": "module", "type": "module",
"private": true, "private": true,

View File

@@ -1,7 +1,7 @@
{ {
"$schema": "https://json.schemastore.org/package.json", "$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/plugin", "name": "@opencode-ai/plugin",
"version": "0.9.7", "version": "0.9.8",
"type": "module", "type": "module",
"scripts": { "scripts": {
"typecheck": "tsc --noEmit" "typecheck": "tsc --noEmit"

View File

@@ -1,7 +1,7 @@
{ {
"$schema": "https://json.schemastore.org/package.json", "$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/sdk", "name": "@opencode-ai/sdk",
"version": "0.9.7", "version": "0.9.8",
"type": "module", "type": "module",
"scripts": { "scripts": {
"typecheck": "tsc --noEmit" "typecheck": "tsc --noEmit"

View File

@@ -1,8 +1,6 @@
// This file is auto-generated by @hey-api/openapi-ts // This file is auto-generated by @hey-api/openapi-ts
import { createSseClient } from "../core/serverSentEvents.gen.js" import { createSseClient } from "../core/serverSentEvents.gen.js"
import type { HttpMethod } from "../core/types.gen.js"
import { getValidRequestBody } from "../core/utils.gen.js"
import type { Client, Config, RequestOptions, ResolvedRequestOptions } from "./types.gen.js" import type { Client, Config, RequestOptions, ResolvedRequestOptions } from "./types.gen.js"
import { import {
buildUrl, buildUrl,
@@ -51,12 +49,12 @@ export const createClient = (config: Config = {}): Client => {
await opts.requestValidator(opts) await opts.requestValidator(opts)
} }
if (opts.body !== undefined && opts.bodySerializer) { if (opts.body && opts.bodySerializer) {
opts.serializedBody = opts.bodySerializer(opts.body) opts.serializedBody = opts.bodySerializer(opts.body)
} }
// remove Content-Type header if body is empty to avoid sending invalid requests // remove Content-Type header if body is empty to avoid sending invalid requests
if (opts.body === undefined || opts.serializedBody === "") { if (opts.serializedBody === undefined || opts.serializedBody === "") {
opts.headers.delete("Content-Type") opts.headers.delete("Content-Type")
} }
@@ -71,7 +69,7 @@ export const createClient = (config: Config = {}): Client => {
const requestInit: ReqInit = { const requestInit: ReqInit = {
redirect: "follow", redirect: "follow",
...opts, ...opts,
body: getValidRequestBody(opts), body: opts.serializedBody,
} }
let request = new Request(url, requestInit) let request = new Request(url, requestInit)
@@ -99,36 +97,18 @@ export const createClient = (config: Config = {}): Client => {
} }
if (response.ok) { if (response.ok) {
const parseAs =
(opts.parseAs === "auto" ? getParseAs(response.headers.get("Content-Type")) : opts.parseAs) ?? "json"
if (response.status === 204 || response.headers.get("Content-Length") === "0") { if (response.status === 204 || response.headers.get("Content-Length") === "0") {
let emptyData: any
switch (parseAs) {
case "arrayBuffer":
case "blob":
case "text":
emptyData = await response[parseAs]()
break
case "formData":
emptyData = new FormData()
break
case "stream":
emptyData = response.body
break
case "json":
default:
emptyData = {}
break
}
return opts.responseStyle === "data" return opts.responseStyle === "data"
? emptyData ? {}
: { : {
data: emptyData, data: {},
...result, ...result,
} }
} }
const parseAs =
(opts.parseAs === "auto" ? getParseAs(response.headers.get("Content-Type")) : opts.parseAs) ?? "json"
let data: any let data: any
switch (parseAs) { switch (parseAs) {
case "arrayBuffer": case "arrayBuffer":
@@ -198,53 +178,35 @@ export const createClient = (config: Config = {}): Client => {
} }
} }
const makeMethodFn = (method: Uppercase<HttpMethod>) => (options: RequestOptions) => request({ ...options, method }) const makeMethod = (method: Required<Config>["method"]) => {
const fn = (options: RequestOptions) => request({ ...options, method })
const makeSseFn = (method: Uppercase<HttpMethod>) => async (options: RequestOptions) => { fn.sse = async (options: RequestOptions) => {
const { opts, url } = await beforeRequest(options) const { opts, url } = await beforeRequest(options)
return createSseClient({ return createSseClient({
...opts, ...opts,
body: opts.body as BodyInit | null | undefined, body: opts.body as BodyInit | null | undefined,
headers: opts.headers as unknown as Record<string, string>, headers: opts.headers as unknown as Record<string, string>,
method, method,
onRequest: async (url, init) => { url,
let request = new Request(url, init) })
for (const fn of interceptors.request._fns) { }
if (fn) { return fn
request = await fn(request, opts)
}
}
return request
},
url,
})
} }
return { return {
buildUrl, buildUrl,
connect: makeMethodFn("CONNECT"), connect: makeMethod("CONNECT"),
delete: makeMethodFn("DELETE"), delete: makeMethod("DELETE"),
get: makeMethodFn("GET"), get: makeMethod("GET"),
getConfig, getConfig,
head: makeMethodFn("HEAD"), head: makeMethod("HEAD"),
interceptors, interceptors,
options: makeMethodFn("OPTIONS"), options: makeMethod("OPTIONS"),
patch: makeMethodFn("PATCH"), patch: makeMethod("PATCH"),
post: makeMethodFn("POST"), post: makeMethod("POST"),
put: makeMethodFn("PUT"), put: makeMethod("PUT"),
request, request,
setConfig, setConfig,
sse: { trace: makeMethod("TRACE"),
connect: makeSseFn("CONNECT"),
delete: makeSseFn("DELETE"),
get: makeSseFn("GET"),
head: makeSseFn("HEAD"),
options: makeSseFn("OPTIONS"),
patch: makeSseFn("PATCH"),
post: makeSseFn("POST"),
put: makeSseFn("PUT"),
trace: makeSseFn("TRACE"),
},
trace: makeMethodFn("TRACE"),
} as Client } as Client
} }

View File

@@ -20,7 +20,7 @@ export interface Config<T extends ClientOptions = ClientOptions>
* *
* @default globalThis.fetch * @default globalThis.fetch
*/ */
fetch?: typeof fetch fetch?: (request: Request) => ReturnType<typeof fetch>
/** /**
* Please don't use the Fetch client for Next.js applications. The `next` * Please don't use the Fetch client for Next.js applications. The `next`
* options won't have any effect. * options won't have any effect.
@@ -128,7 +128,7 @@ export interface ClientOptions {
throwOnError?: boolean throwOnError?: boolean
} }
type MethodFn = < type MethodFnBase = <
TData = unknown, TData = unknown,
TError = unknown, TError = unknown,
ThrowOnError extends boolean = false, ThrowOnError extends boolean = false,
@@ -137,7 +137,7 @@ type MethodFn = <
options: Omit<RequestOptions<TData, TResponseStyle, ThrowOnError>, "method">, options: Omit<RequestOptions<TData, TResponseStyle, ThrowOnError>, "method">,
) => RequestResult<TData, TError, ThrowOnError, TResponseStyle> ) => RequestResult<TData, TError, ThrowOnError, TResponseStyle>
type SseFn = < type MethodFnServerSentEvents = <
TData = unknown, TData = unknown,
TError = unknown, TError = unknown,
ThrowOnError extends boolean = false, ThrowOnError extends boolean = false,
@@ -146,6 +146,10 @@ type SseFn = <
options: Omit<RequestOptions<TData, TResponseStyle, ThrowOnError>, "method">, options: Omit<RequestOptions<TData, TResponseStyle, ThrowOnError>, "method">,
) => Promise<ServerSentEventsResult<TData, TError>> ) => Promise<ServerSentEventsResult<TData, TError>>
type MethodFn = MethodFnBase & {
sse: MethodFnServerSentEvents
}
type RequestFn = < type RequestFn = <
TData = unknown, TData = unknown,
TError = unknown, TError = unknown,
@@ -167,7 +171,7 @@ type BuildUrlFn = <
options: Pick<TData, "url"> & Options<TData>, options: Pick<TData, "url"> & Options<TData>,
) => string ) => string
export type Client = CoreClient<RequestFn, Config, MethodFn, BuildUrlFn, SseFn> & { export type Client = CoreClient<RequestFn, Config, MethodFn, BuildUrlFn> & {
interceptors: Middleware<Request, Response, unknown, ResolvedRequestOptions> interceptors: Middleware<Request, Response, unknown, ResolvedRequestOptions>
} }

View File

@@ -162,22 +162,14 @@ export const mergeConfigs = (a: Config, b: Config): Config => {
return config return config
} }
const headersEntries = (headers: Headers): Array<[string, string]> => {
const entries: Array<[string, string]> = []
headers.forEach((value, key) => {
entries.push([key, value])
})
return entries
}
export const mergeHeaders = (...headers: Array<Required<Config>["headers"] | undefined>): Headers => { export const mergeHeaders = (...headers: Array<Required<Config>["headers"] | undefined>): Headers => {
const mergedHeaders = new Headers() const mergedHeaders = new Headers()
for (const header of headers) { for (const header of headers) {
if (!header) { if (!header || typeof header !== "object") {
continue continue
} }
const iterator = header instanceof Headers ? headersEntries(header) : Object.entries(header) const iterator = header instanceof Headers ? header.entries() : Object.entries(header)
for (const [key, value] of iterator) { for (const [key, value] of iterator) {
if (value === null) { if (value === null) {

View File

@@ -4,17 +4,6 @@ import type { Config } from "./types.gen.js"
export type ServerSentEventsOptions<TData = unknown> = Omit<RequestInit, "method"> & export type ServerSentEventsOptions<TData = unknown> = Omit<RequestInit, "method"> &
Pick<Config, "method" | "responseTransformer" | "responseValidator"> & { Pick<Config, "method" | "responseTransformer" | "responseValidator"> & {
/**
* Fetch API implementation. You can use this option to provide a custom
* fetch instance.
*
* @default globalThis.fetch
*/
fetch?: typeof fetch
/**
* Implementing clients can call request interceptors inside this hook.
*/
onRequest?: (url: string, init: RequestInit) => Promise<Request>
/** /**
* Callback invoked when a network or parsing error occurs during streaming. * Callback invoked when a network or parsing error occurs during streaming.
* *
@@ -32,7 +21,6 @@ export type ServerSentEventsOptions<TData = unknown> = Omit<RequestInit, "method
* @returns Nothing (void). * @returns Nothing (void).
*/ */
onSseEvent?: (event: StreamEvent<TData>) => void onSseEvent?: (event: StreamEvent<TData>) => void
serializedBody?: RequestInit["body"]
/** /**
* Default retry delay in milliseconds. * Default retry delay in milliseconds.
* *
@@ -76,7 +64,6 @@ export type ServerSentEventsResult<TData = unknown, TReturn = void, TNext = unkn
} }
export const createSseClient = <TData = unknown>({ export const createSseClient = <TData = unknown>({
onRequest,
onSseError, onSseError,
onSseEvent, onSseEvent,
responseTransformer, responseTransformer,
@@ -112,21 +99,7 @@ export const createSseClient = <TData = unknown>({
} }
try { try {
const requestInit: RequestInit = { const response = await fetch(url, { ...options, headers, signal })
redirect: "follow",
...options,
body: options.serializedBody,
headers,
signal,
}
let request = new Request(url, requestInit)
if (onRequest) {
request = await onRequest(url, requestInit)
}
// fetch must be assigned here, otherwise it would throw the error:
// TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation
const _fetch = options.fetch ?? globalThis.fetch
const response = await _fetch(request)
if (!response.ok) throw new Error(`SSE failed: ${response.status} ${response.statusText}`) if (!response.ok) throw new Error(`SSE failed: ${response.status} ${response.statusText}`)

View File

@@ -3,19 +3,24 @@
import type { Auth, AuthToken } from "./auth.gen.js" import type { Auth, AuthToken } from "./auth.gen.js"
import type { BodySerializer, QuerySerializer, QuerySerializerOptions } from "./bodySerializer.gen.js" import type { BodySerializer, QuerySerializer, QuerySerializerOptions } from "./bodySerializer.gen.js"
export type HttpMethod = "connect" | "delete" | "get" | "head" | "options" | "patch" | "post" | "put" | "trace" export interface Client<RequestFn = never, Config = unknown, MethodFn = never, BuildUrlFn = never> {
export type Client<RequestFn = never, Config = unknown, MethodFn = never, BuildUrlFn = never, SseFn = never> = {
/** /**
* Returns the final request URL. * Returns the final request URL.
*/ */
buildUrl: BuildUrlFn buildUrl: BuildUrlFn
connect: MethodFn
delete: MethodFn
get: MethodFn
getConfig: () => Config getConfig: () => Config
head: MethodFn
options: MethodFn
patch: MethodFn
post: MethodFn
put: MethodFn
request: RequestFn request: RequestFn
setConfig: (config: Config) => Config setConfig: (config: Config) => Config
} & { trace: MethodFn
[K in HttpMethod]: MethodFn }
} & ([SseFn] extends [never] ? { sse?: never } : { sse: { [K in HttpMethod]: SseFn } })
export interface Config { export interface Config {
/** /**
@@ -42,7 +47,7 @@ export interface Config {
* *
* {@link https://developer.mozilla.org/docs/Web/API/fetch#method See more} * {@link https://developer.mozilla.org/docs/Web/API/fetch#method See more}
*/ */
method?: Uppercase<HttpMethod> method?: "CONNECT" | "DELETE" | "GET" | "HEAD" | "OPTIONS" | "PATCH" | "POST" | "PUT" | "TRACE"
/** /**
* A function for serializing request query parameters. By default, arrays * A function for serializing request query parameters. By default, arrays
* will be exploded in form style, objects will be exploded in deepObject * will be exploded in form style, objects will be exploded in deepObject

View File

@@ -1,6 +1,6 @@
// This file is auto-generated by @hey-api/openapi-ts // This file is auto-generated by @hey-api/openapi-ts
import type { BodySerializer, QuerySerializer } from "./bodySerializer.gen.js" import type { QuerySerializer } from "./bodySerializer.gen.js"
import { import {
type ArraySeparatorStyle, type ArraySeparatorStyle,
serializeArrayParam, serializeArrayParam,
@@ -107,31 +107,3 @@ export const getUrl = ({
} }
return url return url
} }
export function getValidRequestBody(options: {
body?: unknown
bodySerializer?: BodySerializer | null
serializedBody?: unknown
}) {
const hasBody = options.body !== undefined
const isSerializedBody = hasBody && options.bodySerializer
if (isSerializedBody) {
if ("serializedBody" in options) {
const hasSerializedBody = options.serializedBody !== undefined && options.serializedBody !== ""
return hasSerializedBody ? options.serializedBody : null
}
// not all clients implement a serializedBody property (i.e. client-axios)
return options.body !== "" ? options.body : null
}
// plain/text body
if (hasBody) {
return options.body
}
// no body was provided
return undefined
}

View File

@@ -664,7 +664,7 @@ class Event extends _HeyApiClient {
* Get events * Get events
*/ */
public subscribe<ThrowOnError extends boolean = false>(options?: Options<EventSubscribeData, ThrowOnError>) { public subscribe<ThrowOnError extends boolean = false>(options?: Options<EventSubscribeData, ThrowOnError>) {
return (options?.client ?? this._client).sse.get<EventSubscribeResponses, unknown, ThrowOnError>({ return (options?.client ?? this._client).get.sse<EventSubscribeResponses, unknown, ThrowOnError>({
url: "/event", url: "/event",
...options, ...options,
}) })

View File

@@ -1,7 +1,7 @@
{ {
"name": "@opencode/web", "name": "@opencode/web",
"type": "module", "type": "module",
"version": "0.9.7", "version": "0.9.8",
"scripts": { "scripts": {
"dev": "astro dev", "dev": "astro dev",
"dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev", "dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev",

View File

@@ -2,7 +2,7 @@
"name": "opencode", "name": "opencode",
"displayName": "opencode", "displayName": "opencode",
"description": "opencode for VS Code", "description": "opencode for VS Code",
"version": "0.9.7", "version": "0.9.8",
"publisher": "sst-dev", "publisher": "sst-dev",
"repository": { "repository": {
"type": "git", "type": "git",