summaryrefslogtreecommitdiffhomepage
path: root/packages/sdk/js/src/v2
diff options
context:
space:
mode:
authorDax <[email protected]>2025-12-07 19:04:14 -0500
committerGitHub <[email protected]>2025-12-07 19:04:14 -0500
commitea7ec60f51f9fe3c6382f644c328188a43545b7b (patch)
tree5204cbc059fe10b87c54ffd25782ad83710bdcdb /packages/sdk/js/src/v2
parent6667856ba5dac3e5dd77c7008cee2d09be894472 (diff)
downloadopencode-ea7ec60f51f9fe3c6382f644c328188a43545b7b.tar.gz
opencode-ea7ec60f51f9fe3c6382f644c328188a43545b7b.zip
v2 SDK (#5216)
Co-authored-by: GitHub Action <[email protected]>
Diffstat (limited to 'packages/sdk/js/src/v2')
-rw-r--r--packages/sdk/js/src/v2/client.ts30
-rw-r--r--packages/sdk/js/src/v2/gen/client.gen.ts18
-rw-r--r--packages/sdk/js/src/v2/gen/client/client.gen.ts278
-rw-r--r--packages/sdk/js/src/v2/gen/client/index.ts25
-rw-r--r--packages/sdk/js/src/v2/gen/client/types.gen.ts202
-rw-r--r--packages/sdk/js/src/v2/gen/client/utils.gen.ts289
-rw-r--r--packages/sdk/js/src/v2/gen/core/auth.gen.ts41
-rw-r--r--packages/sdk/js/src/v2/gen/core/bodySerializer.gen.ts82
-rw-r--r--packages/sdk/js/src/v2/gen/core/params.gen.ts169
-rw-r--r--packages/sdk/js/src/v2/gen/core/pathSerializer.gen.ts167
-rw-r--r--packages/sdk/js/src/v2/gen/core/queryKeySerializer.gen.ts111
-rw-r--r--packages/sdk/js/src/v2/gen/core/serverSentEvents.gen.ts237
-rw-r--r--packages/sdk/js/src/v2/gen/core/types.gen.ts86
-rw-r--r--packages/sdk/js/src/v2/gen/core/utils.gen.ts137
-rw-r--r--packages/sdk/js/src/v2/gen/sdk.gen.ts2343
-rw-r--r--packages/sdk/js/src/v2/gen/types.gen.ts3845
-rw-r--r--packages/sdk/js/src/v2/index.ts21
-rw-r--r--packages/sdk/js/src/v2/server.ts120
18 files changed, 8201 insertions, 0 deletions
diff --git a/packages/sdk/js/src/v2/client.ts b/packages/sdk/js/src/v2/client.ts
new file mode 100644
index 000000000..806ad26e5
--- /dev/null
+++ b/packages/sdk/js/src/v2/client.ts
@@ -0,0 +1,30 @@
+export * from "./gen/types.gen.js"
+
+import { createClient } from "./gen/client/client.gen.js"
+import { type Config } from "./gen/client/types.gen.js"
+import { OpencodeClient } from "./gen/sdk.gen.js"
+export { type Config as OpencodeClientConfig, OpencodeClient }
+
+export function createOpencodeClient(config?: Config & { directory?: string }) {
+ if (!config?.fetch) {
+ const customFetch: any = (req: any) => {
+ // @ts-ignore
+ req.timeout = false
+ return fetch(req)
+ }
+ config = {
+ ...config,
+ fetch: customFetch,
+ }
+ }
+
+ if (config?.directory) {
+ config.headers = {
+ ...config.headers,
+ "x-opencode-directory": config.directory,
+ }
+ }
+
+ const client = createClient(config)
+ return new OpencodeClient({ client })
+}
diff --git a/packages/sdk/js/src/v2/gen/client.gen.ts b/packages/sdk/js/src/v2/gen/client.gen.ts
new file mode 100644
index 000000000..0c110eca3
--- /dev/null
+++ b/packages/sdk/js/src/v2/gen/client.gen.ts
@@ -0,0 +1,18 @@
+// This file is auto-generated by @hey-api/openapi-ts
+
+import { type ClientOptions, type Config, createClient, createConfig } from "./client/index.js"
+import type { ClientOptions as ClientOptions2 } from "./types.gen.js"
+
+/**
+ * The `createClientConfig()` function will be called on client initialization
+ * and the returned object will become the client's initial configuration.
+ *
+ * You may want to initialize your client this way instead of calling
+ * `setConfig()`. This is useful for example if you're using Next.js
+ * to ensure your client always has the correct values.
+ */
+export type CreateClientConfig<T extends ClientOptions = ClientOptions2> = (
+ override?: Config<ClientOptions & T>,
+) => Config<Required<ClientOptions> & T>
+
+export const client = createClient(createConfig<ClientOptions2>({ baseUrl: "http://localhost:4096" }))
diff --git a/packages/sdk/js/src/v2/gen/client/client.gen.ts b/packages/sdk/js/src/v2/gen/client/client.gen.ts
new file mode 100644
index 000000000..47f140342
--- /dev/null
+++ b/packages/sdk/js/src/v2/gen/client/client.gen.ts
@@ -0,0 +1,278 @@
+// This file is auto-generated by @hey-api/openapi-ts
+
+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 {
+ buildUrl,
+ createConfig,
+ createInterceptors,
+ getParseAs,
+ mergeConfigs,
+ mergeHeaders,
+ setAuthParams,
+} from "./utils.gen.js"
+
+type ReqInit = Omit<RequestInit, "body" | "headers"> & {
+ body?: any
+ headers: ReturnType<typeof mergeHeaders>
+}
+
+export const createClient = (config: Config = {}): Client => {
+ let _config = mergeConfigs(createConfig(), config)
+
+ const getConfig = (): Config => ({ ..._config })
+
+ const setConfig = (config: Config): Config => {
+ _config = mergeConfigs(_config, config)
+ return getConfig()
+ }
+
+ const interceptors = createInterceptors<Request, Response, unknown, ResolvedRequestOptions>()
+
+ const beforeRequest = async (options: RequestOptions) => {
+ const opts = {
+ ..._config,
+ ...options,
+ fetch: options.fetch ?? _config.fetch ?? globalThis.fetch,
+ headers: mergeHeaders(_config.headers, options.headers),
+ serializedBody: undefined,
+ }
+
+ if (opts.security) {
+ await setAuthParams({
+ ...opts,
+ security: opts.security,
+ })
+ }
+
+ if (opts.requestValidator) {
+ await opts.requestValidator(opts)
+ }
+
+ if (opts.body !== undefined && opts.bodySerializer) {
+ opts.serializedBody = opts.bodySerializer(opts.body)
+ }
+
+ // remove Content-Type header if body is empty to avoid sending invalid requests
+ if (opts.body === undefined || opts.serializedBody === "") {
+ opts.headers.delete("Content-Type")
+ }
+
+ const url = buildUrl(opts)
+
+ return { opts, url }
+ }
+
+ const request: Client["request"] = async (options) => {
+ // @ts-expect-error
+ const { opts, url } = await beforeRequest(options)
+ const requestInit: ReqInit = {
+ redirect: "follow",
+ ...opts,
+ body: getValidRequestBody(opts),
+ }
+
+ let request = new Request(url, requestInit)
+
+ for (const fn of interceptors.request.fns) {
+ if (fn) {
+ request = await fn(request, opts)
+ }
+ }
+
+ // fetch must be assigned here, otherwise it would throw the error:
+ // TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation
+ const _fetch = opts.fetch!
+ let response: Response
+
+ try {
+ response = await _fetch(request)
+ } catch (error) {
+ // Handle fetch exceptions (AbortError, network errors, etc.)
+ let finalError = error
+
+ for (const fn of interceptors.error.fns) {
+ if (fn) {
+ finalError = (await fn(error, undefined as any, request, opts)) as unknown
+ }
+ }
+
+ finalError = finalError || ({} as unknown)
+
+ if (opts.throwOnError) {
+ throw finalError
+ }
+
+ // Return error response
+ return opts.responseStyle === "data"
+ ? undefined
+ : {
+ error: finalError,
+ request,
+ response: undefined as any,
+ }
+ }
+
+ for (const fn of interceptors.response.fns) {
+ if (fn) {
+ response = await fn(response, request, opts)
+ }
+ }
+
+ const result = {
+ request,
+ response,
+ }
+
+ 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") {
+ 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"
+ ? emptyData
+ : {
+ data: emptyData,
+ ...result,
+ }
+ }
+
+ let data: any
+ switch (parseAs) {
+ case "arrayBuffer":
+ case "blob":
+ case "formData":
+ case "json":
+ case "text":
+ data = await response[parseAs]()
+ break
+ case "stream":
+ return opts.responseStyle === "data"
+ ? response.body
+ : {
+ data: response.body,
+ ...result,
+ }
+ }
+
+ if (parseAs === "json") {
+ if (opts.responseValidator) {
+ await opts.responseValidator(data)
+ }
+
+ if (opts.responseTransformer) {
+ data = await opts.responseTransformer(data)
+ }
+ }
+
+ return opts.responseStyle === "data"
+ ? data
+ : {
+ data,
+ ...result,
+ }
+ }
+
+ const textError = await response.text()
+ let jsonError: unknown
+
+ try {
+ jsonError = JSON.parse(textError)
+ } catch {
+ // noop
+ }
+
+ const error = jsonError ?? textError
+ let finalError = error
+
+ for (const fn of interceptors.error.fns) {
+ if (fn) {
+ finalError = (await fn(error, response, request, opts)) as string
+ }
+ }
+
+ finalError = finalError || ({} as string)
+
+ if (opts.throwOnError) {
+ throw finalError
+ }
+
+ // TODO: we probably want to return error and improve types
+ return opts.responseStyle === "data"
+ ? undefined
+ : {
+ error: finalError,
+ ...result,
+ }
+ }
+
+ const makeMethodFn = (method: Uppercase<HttpMethod>) => (options: RequestOptions) => request({ ...options, method })
+
+ const makeSseFn = (method: Uppercase<HttpMethod>) => async (options: RequestOptions) => {
+ const { opts, url } = await beforeRequest(options)
+ return createSseClient({
+ ...opts,
+ body: opts.body as BodyInit | null | undefined,
+ headers: opts.headers as unknown as Record<string, string>,
+ method,
+ onRequest: async (url, init) => {
+ let request = new Request(url, init)
+ for (const fn of interceptors.request.fns) {
+ if (fn) {
+ request = await fn(request, opts)
+ }
+ }
+ return request
+ },
+ url,
+ })
+ }
+
+ return {
+ buildUrl,
+ connect: makeMethodFn("CONNECT"),
+ delete: makeMethodFn("DELETE"),
+ get: makeMethodFn("GET"),
+ getConfig,
+ head: makeMethodFn("HEAD"),
+ interceptors,
+ options: makeMethodFn("OPTIONS"),
+ patch: makeMethodFn("PATCH"),
+ post: makeMethodFn("POST"),
+ put: makeMethodFn("PUT"),
+ request,
+ setConfig,
+ sse: {
+ 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
+}
diff --git a/packages/sdk/js/src/v2/gen/client/index.ts b/packages/sdk/js/src/v2/gen/client/index.ts
new file mode 100644
index 000000000..0af63f330
--- /dev/null
+++ b/packages/sdk/js/src/v2/gen/client/index.ts
@@ -0,0 +1,25 @@
+// This file is auto-generated by @hey-api/openapi-ts
+
+export type { Auth } from "../core/auth.gen.js"
+export type { QuerySerializerOptions } from "../core/bodySerializer.gen.js"
+export {
+ formDataBodySerializer,
+ jsonBodySerializer,
+ urlSearchParamsBodySerializer,
+} from "../core/bodySerializer.gen.js"
+export { buildClientParams } from "../core/params.gen.js"
+export { serializeQueryKeyValue } from "../core/queryKeySerializer.gen.js"
+export { createClient } from "./client.gen.js"
+export type {
+ Client,
+ ClientOptions,
+ Config,
+ CreateClientConfig,
+ Options,
+ RequestOptions,
+ RequestResult,
+ ResolvedRequestOptions,
+ ResponseStyle,
+ TDataShape,
+} from "./types.gen.js"
+export { createConfig, mergeHeaders } from "./utils.gen.js"
diff --git a/packages/sdk/js/src/v2/gen/client/types.gen.ts b/packages/sdk/js/src/v2/gen/client/types.gen.ts
new file mode 100644
index 000000000..e053aa406
--- /dev/null
+++ b/packages/sdk/js/src/v2/gen/client/types.gen.ts
@@ -0,0 +1,202 @@
+// This file is auto-generated by @hey-api/openapi-ts
+
+import type { Auth } from "../core/auth.gen.js"
+import type { ServerSentEventsOptions, ServerSentEventsResult } from "../core/serverSentEvents.gen.js"
+import type { Client as CoreClient, Config as CoreConfig } from "../core/types.gen.js"
+import type { Middleware } from "./utils.gen.js"
+
+export type ResponseStyle = "data" | "fields"
+
+export interface Config<T extends ClientOptions = ClientOptions>
+ extends Omit<RequestInit, "body" | "headers" | "method">,
+ CoreConfig {
+ /**
+ * Base URL for all requests made by this client.
+ */
+ baseUrl?: T["baseUrl"]
+ /**
+ * Fetch API implementation. You can use this option to provide a custom
+ * fetch instance.
+ *
+ * @default globalThis.fetch
+ */
+ fetch?: typeof fetch
+ /**
+ * Please don't use the Fetch client for Next.js applications. The `next`
+ * options won't have any effect.
+ *
+ * Install {@link https://www.npmjs.com/package/@hey-api/client-next `@hey-api/client-next`} instead.
+ */
+ next?: never
+ /**
+ * Return the response data parsed in a specified format. By default, `auto`
+ * will infer the appropriate method from the `Content-Type` response header.
+ * You can override this behavior with any of the {@link Body} methods.
+ * Select `stream` if you don't want to parse response data at all.
+ *
+ * @default 'auto'
+ */
+ parseAs?: "arrayBuffer" | "auto" | "blob" | "formData" | "json" | "stream" | "text"
+ /**
+ * Should we return only data or multiple fields (data, error, response, etc.)?
+ *
+ * @default 'fields'
+ */
+ responseStyle?: ResponseStyle
+ /**
+ * Throw an error instead of returning it in the response?
+ *
+ * @default false
+ */
+ throwOnError?: T["throwOnError"]
+}
+
+export interface RequestOptions<
+ TData = unknown,
+ TResponseStyle extends ResponseStyle = "fields",
+ ThrowOnError extends boolean = boolean,
+ Url extends string = string,
+> extends Config<{
+ responseStyle: TResponseStyle
+ throwOnError: ThrowOnError
+ }>,
+ Pick<
+ ServerSentEventsOptions<TData>,
+ "onSseError" | "onSseEvent" | "sseDefaultRetryDelay" | "sseMaxRetryAttempts" | "sseMaxRetryDelay"
+ > {
+ /**
+ * Any body that you want to add to your request.
+ *
+ * {@link https://developer.mozilla.org/docs/Web/API/fetch#body}
+ */
+ body?: unknown
+ path?: Record<string, unknown>
+ query?: Record<string, unknown>
+ /**
+ * Security mechanism(s) to use for the request.
+ */
+ security?: ReadonlyArray<Auth>
+ url: Url
+}
+
+export interface ResolvedRequestOptions<
+ TResponseStyle extends ResponseStyle = "fields",
+ ThrowOnError extends boolean = boolean,
+ Url extends string = string,
+> extends RequestOptions<unknown, TResponseStyle, ThrowOnError, Url> {
+ serializedBody?: string
+}
+
+export type RequestResult<
+ TData = unknown,
+ TError = unknown,
+ ThrowOnError extends boolean = boolean,
+ TResponseStyle extends ResponseStyle = "fields",
+> = ThrowOnError extends true
+ ? Promise<
+ TResponseStyle extends "data"
+ ? TData extends Record<string, unknown>
+ ? TData[keyof TData]
+ : TData
+ : {
+ data: TData extends Record<string, unknown> ? TData[keyof TData] : TData
+ request: Request
+ response: Response
+ }
+ >
+ : Promise<
+ TResponseStyle extends "data"
+ ? (TData extends Record<string, unknown> ? TData[keyof TData] : TData) | undefined
+ : (
+ | {
+ data: TData extends Record<string, unknown> ? TData[keyof TData] : TData
+ error: undefined
+ }
+ | {
+ data: undefined
+ error: TError extends Record<string, unknown> ? TError[keyof TError] : TError
+ }
+ ) & {
+ request: Request
+ response: Response
+ }
+ >
+
+export interface ClientOptions {
+ baseUrl?: string
+ responseStyle?: ResponseStyle
+ throwOnError?: boolean
+}
+
+type MethodFn = <
+ TData = unknown,
+ TError = unknown,
+ ThrowOnError extends boolean = false,
+ TResponseStyle extends ResponseStyle = "fields",
+>(
+ options: Omit<RequestOptions<TData, TResponseStyle, ThrowOnError>, "method">,
+) => RequestResult<TData, TError, ThrowOnError, TResponseStyle>
+
+type SseFn = <
+ TData = unknown,
+ TError = unknown,
+ ThrowOnError extends boolean = false,
+ TResponseStyle extends ResponseStyle = "fields",
+>(
+ options: Omit<RequestOptions<TData, TResponseStyle, ThrowOnError>, "method">,
+) => Promise<ServerSentEventsResult<TData, TError>>
+
+type RequestFn = <
+ TData = unknown,
+ TError = unknown,
+ ThrowOnError extends boolean = false,
+ TResponseStyle extends ResponseStyle = "fields",
+>(
+ options: Omit<RequestOptions<TData, TResponseStyle, ThrowOnError>, "method"> &
+ Pick<Required<RequestOptions<TData, TResponseStyle, ThrowOnError>>, "method">,
+) => RequestResult<TData, TError, ThrowOnError, TResponseStyle>
+
+type BuildUrlFn = <
+ TData extends {
+ body?: unknown
+ path?: Record<string, unknown>
+ query?: Record<string, unknown>
+ url: string
+ },
+>(
+ options: TData & Options<TData>,
+) => string
+
+export type Client = CoreClient<RequestFn, Config, MethodFn, BuildUrlFn, SseFn> & {
+ interceptors: Middleware<Request, Response, unknown, ResolvedRequestOptions>
+}
+
+/**
+ * The `createClientConfig()` function will be called on client initialization
+ * and the returned object will become the client's initial configuration.
+ *
+ * You may want to initialize your client this way instead of calling
+ * `setConfig()`. This is useful for example if you're using Next.js
+ * to ensure your client always has the correct values.
+ */
+export type CreateClientConfig<T extends ClientOptions = ClientOptions> = (
+ override?: Config<ClientOptions & T>,
+) => Config<Required<ClientOptions> & T>
+
+export interface TDataShape {
+ body?: unknown
+ headers?: unknown
+ path?: unknown
+ query?: unknown
+ url: string
+}
+
+type OmitKeys<T, K> = Pick<T, Exclude<keyof T, K>>
+
+export type Options<
+ TData extends TDataShape = TDataShape,
+ ThrowOnError extends boolean = boolean,
+ TResponse = unknown,
+ TResponseStyle extends ResponseStyle = "fields",
+> = OmitKeys<RequestOptions<TResponse, TResponseStyle, ThrowOnError>, "body" | "path" | "query" | "url"> &
+ ([TData] extends [never] ? unknown : Omit<TData, "url">)
diff --git a/packages/sdk/js/src/v2/gen/client/utils.gen.ts b/packages/sdk/js/src/v2/gen/client/utils.gen.ts
new file mode 100644
index 000000000..3b1dfb787
--- /dev/null
+++ b/packages/sdk/js/src/v2/gen/client/utils.gen.ts
@@ -0,0 +1,289 @@
+// This file is auto-generated by @hey-api/openapi-ts
+
+import { getAuthToken } from "../core/auth.gen.js"
+import type { QuerySerializerOptions } from "../core/bodySerializer.gen.js"
+import { jsonBodySerializer } from "../core/bodySerializer.gen.js"
+import { serializeArrayParam, serializeObjectParam, serializePrimitiveParam } from "../core/pathSerializer.gen.js"
+import { getUrl } from "../core/utils.gen.js"
+import type { Client, ClientOptions, Config, RequestOptions } from "./types.gen.js"
+
+export const createQuerySerializer = <T = unknown>({ parameters = {}, ...args }: QuerySerializerOptions = {}) => {
+ const querySerializer = (queryParams: T) => {
+ const search: string[] = []
+ if (queryParams && typeof queryParams === "object") {
+ for (const name in queryParams) {
+ const value = queryParams[name]
+
+ if (value === undefined || value === null) {
+ continue
+ }
+
+ const options = parameters[name] || args
+
+ if (Array.isArray(value)) {
+ const serializedArray = serializeArrayParam({
+ allowReserved: options.allowReserved,
+ explode: true,
+ name,
+ style: "form",
+ value,
+ ...options.array,
+ })
+ if (serializedArray) search.push(serializedArray)
+ } else if (typeof value === "object") {
+ const serializedObject = serializeObjectParam({
+ allowReserved: options.allowReserved,
+ explode: true,
+ name,
+ style: "deepObject",
+ value: value as Record<string, unknown>,
+ ...options.object,
+ })
+ if (serializedObject) search.push(serializedObject)
+ } else {
+ const serializedPrimitive = serializePrimitiveParam({
+ allowReserved: options.allowReserved,
+ name,
+ value: value as string,
+ })
+ if (serializedPrimitive) search.push(serializedPrimitive)
+ }
+ }
+ }
+ return search.join("&")
+ }
+ return querySerializer
+}
+
+/**
+ * Infers parseAs value from provided Content-Type header.
+ */
+export const getParseAs = (contentType: string | null): Exclude<Config["parseAs"], "auto"> => {
+ if (!contentType) {
+ // If no Content-Type header is provided, the best we can do is return the raw response body,
+ // which is effectively the same as the 'stream' option.
+ return "stream"
+ }
+
+ const cleanContent = contentType.split(";")[0]?.trim()
+
+ if (!cleanContent) {
+ return
+ }
+
+ if (cleanContent.startsWith("application/json") || cleanContent.endsWith("+json")) {
+ return "json"
+ }
+
+ if (cleanContent === "multipart/form-data") {
+ return "formData"
+ }
+
+ if (["application/", "audio/", "image/", "video/"].some((type) => cleanContent.startsWith(type))) {
+ return "blob"
+ }
+
+ if (cleanContent.startsWith("text/")) {
+ return "text"
+ }
+
+ return
+}
+
+const checkForExistence = (
+ options: Pick<RequestOptions, "auth" | "query"> & {
+ headers: Headers
+ },
+ name?: string,
+): boolean => {
+ if (!name) {
+ return false
+ }
+ if (options.headers.has(name) || options.query?.[name] || options.headers.get("Cookie")?.includes(`${name}=`)) {
+ return true
+ }
+ return false
+}
+
+export const setAuthParams = async ({
+ security,
+ ...options
+}: Pick<Required<RequestOptions>, "security"> &
+ Pick<RequestOptions, "auth" | "query"> & {
+ headers: Headers
+ }) => {
+ for (const auth of security) {
+ if (checkForExistence(options, auth.name)) {
+ continue
+ }
+
+ const token = await getAuthToken(auth, options.auth)
+
+ if (!token) {
+ continue
+ }
+
+ const name = auth.name ?? "Authorization"
+
+ switch (auth.in) {
+ case "query":
+ if (!options.query) {
+ options.query = {}
+ }
+ options.query[name] = token
+ break
+ case "cookie":
+ options.headers.append("Cookie", `${name}=${token}`)
+ break
+ case "header":
+ default:
+ options.headers.set(name, token)
+ break
+ }
+ }
+}
+
+export const buildUrl: Client["buildUrl"] = (options) =>
+ getUrl({
+ baseUrl: options.baseUrl as string,
+ path: options.path,
+ query: options.query,
+ querySerializer:
+ typeof options.querySerializer === "function"
+ ? options.querySerializer
+ : createQuerySerializer(options.querySerializer),
+ url: options.url,
+ })
+
+export const mergeConfigs = (a: Config, b: Config): Config => {
+ const config = { ...a, ...b }
+ if (config.baseUrl?.endsWith("/")) {
+ config.baseUrl = config.baseUrl.substring(0, config.baseUrl.length - 1)
+ }
+ config.headers = mergeHeaders(a.headers, b.headers)
+ 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 => {
+ const mergedHeaders = new Headers()
+ for (const header of headers) {
+ if (!header) {
+ continue
+ }
+
+ const iterator = header instanceof Headers ? headersEntries(header) : Object.entries(header)
+
+ for (const [key, value] of iterator) {
+ if (value === null) {
+ mergedHeaders.delete(key)
+ } else if (Array.isArray(value)) {
+ for (const v of value) {
+ mergedHeaders.append(key, v as string)
+ }
+ } else if (value !== undefined) {
+ // assume object headers are meant to be JSON stringified, i.e. their
+ // content value in OpenAPI specification is 'application/json'
+ mergedHeaders.set(key, typeof value === "object" ? JSON.stringify(value) : (value as string))
+ }
+ }
+ }
+ return mergedHeaders
+}
+
+type ErrInterceptor<Err, Res, Req, Options> = (
+ error: Err,
+ response: Res,
+ request: Req,
+ options: Options,
+) => Err | Promise<Err>
+
+type ReqInterceptor<Req, Options> = (request: Req, options: Options) => Req | Promise<Req>
+
+type ResInterceptor<Res, Req, Options> = (response: Res, request: Req, options: Options) => Res | Promise<Res>
+
+class Interceptors<Interceptor> {
+ fns: Array<Interceptor | null> = []
+
+ clear(): void {
+ this.fns = []
+ }
+
+ eject(id: number | Interceptor): void {
+ const index = this.getInterceptorIndex(id)
+ if (this.fns[index]) {
+ this.fns[index] = null
+ }
+ }
+
+ exists(id: number | Interceptor): boolean {
+ const index = this.getInterceptorIndex(id)
+ return Boolean(this.fns[index])
+ }
+
+ getInterceptorIndex(id: number | Interceptor): number {
+ if (typeof id === "number") {
+ return this.fns[id] ? id : -1
+ }
+ return this.fns.indexOf(id)
+ }
+
+ update(id: number | Interceptor, fn: Interceptor): number | Interceptor | false {
+ const index = this.getInterceptorIndex(id)
+ if (this.fns[index]) {
+ this.fns[index] = fn
+ return id
+ }
+ return false
+ }
+
+ use(fn: Interceptor): number {
+ this.fns.push(fn)
+ return this.fns.length - 1
+ }
+}
+
+export interface Middleware<Req, Res, Err, Options> {
+ error: Interceptors<ErrInterceptor<Err, Res, Req, Options>>
+ request: Interceptors<ReqInterceptor<Req, Options>>
+ response: Interceptors<ResInterceptor<Res, Req, Options>>
+}
+
+export const createInterceptors = <Req, Res, Err, Options>(): Middleware<Req, Res, Err, Options> => ({
+ error: new Interceptors<ErrInterceptor<Err, Res, Req, Options>>(),
+ request: new Interceptors<ReqInterceptor<Req, Options>>(),
+ response: new Interceptors<ResInterceptor<Res, Req, Options>>(),
+})
+
+const defaultQuerySerializer = createQuerySerializer({
+ allowReserved: false,
+ array: {
+ explode: true,
+ style: "form",
+ },
+ object: {
+ explode: true,
+ style: "deepObject",
+ },
+})
+
+const defaultHeaders = {
+ "Content-Type": "application/json",
+}
+
+export const createConfig = <T extends ClientOptions = ClientOptions>(
+ override: Config<Omit<ClientOptions, keyof T> & T> = {},
+): Config<Omit<ClientOptions, keyof T> & T> => ({
+ ...jsonBodySerializer,
+ headers: defaultHeaders,
+ parseAs: "auto",
+ querySerializer: defaultQuerySerializer,
+ ...override,
+})
diff --git a/packages/sdk/js/src/v2/gen/core/auth.gen.ts b/packages/sdk/js/src/v2/gen/core/auth.gen.ts
new file mode 100644
index 000000000..bc7b230f4
--- /dev/null
+++ b/packages/sdk/js/src/v2/gen/core/auth.gen.ts
@@ -0,0 +1,41 @@
+// This file is auto-generated by @hey-api/openapi-ts
+
+export type AuthToken = string | undefined
+
+export interface Auth {
+ /**
+ * Which part of the request do we use to send the auth?
+ *
+ * @default 'header'
+ */
+ in?: "header" | "query" | "cookie"
+ /**
+ * Header or query parameter name.
+ *
+ * @default 'Authorization'
+ */
+ name?: string
+ scheme?: "basic" | "bearer"
+ type: "apiKey" | "http"
+}
+
+export const getAuthToken = async (
+ auth: Auth,
+ callback: ((auth: Auth) => Promise<AuthToken> | AuthToken) | AuthToken,
+): Promise<string | undefined> => {
+ const token = typeof callback === "function" ? await callback(auth) : callback
+
+ if (!token) {
+ return
+ }
+
+ if (auth.scheme === "bearer") {
+ return `Bearer ${token}`
+ }
+
+ if (auth.scheme === "basic") {
+ return `Basic ${btoa(token)}`
+ }
+
+ return token
+}
diff --git a/packages/sdk/js/src/v2/gen/core/bodySerializer.gen.ts b/packages/sdk/js/src/v2/gen/core/bodySerializer.gen.ts
new file mode 100644
index 000000000..9678fb08e
--- /dev/null
+++ b/packages/sdk/js/src/v2/gen/core/bodySerializer.gen.ts
@@ -0,0 +1,82 @@
+// This file is auto-generated by @hey-api/openapi-ts
+
+import type { ArrayStyle, ObjectStyle, SerializerOptions } from "./pathSerializer.gen.js"
+
+export type QuerySerializer = (query: Record<string, unknown>) => string
+
+export type BodySerializer = (body: any) => any
+
+type QuerySerializerOptionsObject = {
+ allowReserved?: boolean
+ array?: Partial<SerializerOptions<ArrayStyle>>
+ object?: Partial<SerializerOptions<ObjectStyle>>
+}
+
+export type QuerySerializerOptions = QuerySerializerOptionsObject & {
+ /**
+ * Per-parameter serialization overrides. When provided, these settings
+ * override the global array/object settings for specific parameter names.
+ */
+ parameters?: Record<string, QuerySerializerOptionsObject>
+}
+
+const serializeFormDataPair = (data: FormData, key: string, value: unknown): void => {
+ if (typeof value === "string" || value instanceof Blob) {
+ data.append(key, value)
+ } else if (value instanceof Date) {
+ data.append(key, value.toISOString())
+ } else {
+ data.append(key, JSON.stringify(value))
+ }
+}
+
+const serializeUrlSearchParamsPair = (data: URLSearchParams, key: string, value: unknown): void => {
+ if (typeof value === "string") {
+ data.append(key, value)
+ } else {
+ data.append(key, JSON.stringify(value))
+ }
+}
+
+export const formDataBodySerializer = {
+ bodySerializer: <T extends Record<string, any> | Array<Record<string, any>>>(body: T): FormData => {
+ const data = new FormData()
+
+ Object.entries(body).forEach(([key, value]) => {
+ if (value === undefined || value === null) {
+ return
+ }
+ if (Array.isArray(value)) {
+ value.forEach((v) => serializeFormDataPair(data, key, v))
+ } else {
+ serializeFormDataPair(data, key, value)
+ }
+ })
+
+ return data
+ },
+}
+
+export const jsonBodySerializer = {
+ bodySerializer: <T>(body: T): string =>
+ JSON.stringify(body, (_key, value) => (typeof value === "bigint" ? value.toString() : value)),
+}
+
+export const urlSearchParamsBodySerializer = {
+ bodySerializer: <T extends Record<string, any> | Array<Record<string, any>>>(body: T): string => {
+ const data = new URLSearchParams()
+
+ Object.entries(body).forEach(([key, value]) => {
+ if (value === undefined || value === null) {
+ return
+ }
+ if (Array.isArray(value)) {
+ value.forEach((v) => serializeUrlSearchParamsPair(data, key, v))
+ } else {
+ serializeUrlSearchParamsPair(data, key, value)
+ }
+ })
+
+ return data.toString()
+ },
+}
diff --git a/packages/sdk/js/src/v2/gen/core/params.gen.ts b/packages/sdk/js/src/v2/gen/core/params.gen.ts
new file mode 100644
index 000000000..6e9d0b9ad
--- /dev/null
+++ b/packages/sdk/js/src/v2/gen/core/params.gen.ts
@@ -0,0 +1,169 @@
+// This file is auto-generated by @hey-api/openapi-ts
+
+type Slot = "body" | "headers" | "path" | "query"
+
+export type Field =
+ | {
+ in: Exclude<Slot, "body">
+ /**
+ * Field name. This is the name we want the user to see and use.
+ */
+ key: string
+ /**
+ * Field mapped name. This is the name we want to use in the request.
+ * If omitted, we use the same value as `key`.
+ */
+ map?: string
+ }
+ | {
+ in: Extract<Slot, "body">
+ /**
+ * Key isn't required for bodies.
+ */
+ key?: string
+ map?: string
+ }
+ | {
+ /**
+ * Field name. This is the name we want the user to see and use.
+ */
+ key: string
+ /**
+ * Field mapped name. This is the name we want to use in the request.
+ * If `in` is omitted, `map` aliases `key` to the transport layer.
+ */
+ map: Slot
+ }
+
+export interface Fields {
+ allowExtra?: Partial<Record<Slot, boolean>>
+ args?: ReadonlyArray<Field>
+}
+
+export type FieldsConfig = ReadonlyArray<Field | Fields>
+
+const extraPrefixesMap: Record<string, Slot> = {
+ $body_: "body",
+ $headers_: "headers",
+ $path_: "path",
+ $query_: "query",
+}
+const extraPrefixes = Object.entries(extraPrefixesMap)
+
+type KeyMap = Map<
+ string,
+ | {
+ in: Slot
+ map?: string
+ }
+ | {
+ in?: never
+ map: Slot
+ }
+>
+
+const buildKeyMap = (fields: FieldsConfig, map?: KeyMap): KeyMap => {
+ if (!map) {
+ map = new Map()
+ }
+
+ for (const config of fields) {
+ if ("in" in config) {
+ if (config.key) {
+ map.set(config.key, {
+ in: config.in,
+ map: config.map,
+ })
+ }
+ } else if ("key" in config) {
+ map.set(config.key, {
+ map: config.map,
+ })
+ } else if (config.args) {
+ buildKeyMap(config.args, map)
+ }
+ }
+
+ return map
+}
+
+interface Params {
+ body: unknown
+ headers: Record<string, unknown>
+ path: Record<string, unknown>
+ query: Record<string, unknown>
+}
+
+const stripEmptySlots = (params: Params) => {
+ for (const [slot, value] of Object.entries(params)) {
+ if (value && typeof value === "object" && !Object.keys(value).length) {
+ delete params[slot as Slot]
+ }
+ }
+}
+
+export const buildClientParams = (args: ReadonlyArray<unknown>, fields: FieldsConfig) => {
+ const params: Params = {
+ body: {},
+ headers: {},
+ path: {},
+ query: {},
+ }
+
+ const map = buildKeyMap(fields)
+
+ let config: FieldsConfig[number] | undefined
+
+ for (const [index, arg] of args.entries()) {
+ if (fields[index]) {
+ config = fields[index]
+ }
+
+ if (!config) {
+ continue
+ }
+
+ if ("in" in config) {
+ if (config.key) {
+ const field = map.get(config.key)!
+ const name = field.map || config.key
+ if (field.in) {
+ ;(params[field.in] as Record<string, unknown>)[name] = arg
+ }
+ } else {
+ params.body = arg
+ }
+ } else {
+ for (const [key, value] of Object.entries(arg ?? {})) {
+ const field = map.get(key)
+
+ if (field) {
+ if (field.in) {
+ const name = field.map || key
+ ;(params[field.in] as Record<string, unknown>)[name] = value
+ } else {
+ params[field.map] = value
+ }
+ } else {
+ const extra = extraPrefixes.find(([prefix]) => key.startsWith(prefix))
+
+ if (extra) {
+ const [prefix, slot] = extra
+ ;(params[slot] as Record<string, unknown>)[key.slice(prefix.length)] = value
+ } else if ("allowExtra" in config && config.allowExtra) {
+ for (const [slot, allowed] of Object.entries(config.allowExtra)) {
+ if (allowed) {
+ ;(params[slot as Slot] as Record<string, unknown>)[key] = value
+ break
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ stripEmptySlots(params)
+
+ return params
+}
diff --git a/packages/sdk/js/src/v2/gen/core/pathSerializer.gen.ts b/packages/sdk/js/src/v2/gen/core/pathSerializer.gen.ts
new file mode 100644
index 000000000..96be3bc5a
--- /dev/null
+++ b/packages/sdk/js/src/v2/gen/core/pathSerializer.gen.ts
@@ -0,0 +1,167 @@
+// This file is auto-generated by @hey-api/openapi-ts
+
+interface SerializeOptions<T> extends SerializePrimitiveOptions, SerializerOptions<T> {}
+
+interface SerializePrimitiveOptions {
+ allowReserved?: boolean
+ name: string
+}
+
+export interface SerializerOptions<T> {
+ /**
+ * @default true
+ */
+ explode: boolean
+ style: T
+}
+
+export type ArrayStyle = "form" | "spaceDelimited" | "pipeDelimited"
+export type ArraySeparatorStyle = ArrayStyle | MatrixStyle
+type MatrixStyle = "label" | "matrix" | "simple"
+export type ObjectStyle = "form" | "deepObject"
+type ObjectSeparatorStyle = ObjectStyle | MatrixStyle
+
+interface SerializePrimitiveParam extends SerializePrimitiveOptions {
+ value: string
+}
+
+export const separatorArrayExplode = (style: ArraySeparatorStyle) => {
+ switch (style) {
+ case "label":
+ return "."
+ case "matrix":
+ return ";"
+ case "simple":
+ return ","
+ default:
+ return "&"
+ }
+}
+
+export const separatorArrayNoExplode = (style: ArraySeparatorStyle) => {
+ switch (style) {
+ case "form":
+ return ","
+ case "pipeDelimited":
+ return "|"
+ case "spaceDelimited":
+ return "%20"
+ default:
+ return ","
+ }
+}
+
+export const separatorObjectExplode = (style: ObjectSeparatorStyle) => {
+ switch (style) {
+ case "label":
+ return "."
+ case "matrix":
+ return ";"
+ case "simple":
+ return ","
+ default:
+ return "&"
+ }
+}
+
+export const serializeArrayParam = ({
+ allowReserved,
+ explode,
+ name,
+ style,
+ value,
+}: SerializeOptions<ArraySeparatorStyle> & {
+ value: unknown[]
+}) => {
+ if (!explode) {
+ const joinedValues = (allowReserved ? value : value.map((v) => encodeURIComponent(v as string))).join(
+ separatorArrayNoExplode(style),
+ )
+ switch (style) {
+ case "label":
+ return `.${joinedValues}`
+ case "matrix":
+ return `;${name}=${joinedValues}`
+ case "simple":
+ return joinedValues
+ default:
+ return `${name}=${joinedValues}`
+ }
+ }
+
+ const separator = separatorArrayExplode(style)
+ const joinedValues = value
+ .map((v) => {
+ if (style === "label" || style === "simple") {
+ return allowReserved ? v : encodeURIComponent(v as string)
+ }
+
+ return serializePrimitiveParam({
+ allowReserved,
+ name,
+ value: v as string,
+ })
+ })
+ .join(separator)
+ return style === "label" || style === "matrix" ? separator + joinedValues : joinedValues
+}
+
+export const serializePrimitiveParam = ({ allowReserved, name, value }: SerializePrimitiveParam) => {
+ if (value === undefined || value === null) {
+ return ""
+ }
+
+ if (typeof value === "object") {
+ throw new Error(
+ "Deeply-nested arrays/objects aren’t supported. Provide your own `querySerializer()` to handle these.",
+ )
+ }
+
+ return `${name}=${allowReserved ? value : encodeURIComponent(value)}`
+}
+
+export const serializeObjectParam = ({
+ allowReserved,
+ explode,
+ name,
+ style,
+ value,
+ valueOnly,
+}: SerializeOptions<ObjectSeparatorStyle> & {
+ value: Record<string, unknown> | Date
+ valueOnly?: boolean
+}) => {
+ if (value instanceof Date) {
+ return valueOnly ? value.toISOString() : `${name}=${value.toISOString()}`
+ }
+
+ if (style !== "deepObject" && !explode) {
+ let values: string[] = []
+ Object.entries(value).forEach(([key, v]) => {
+ values = [...values, key, allowReserved ? (v as string) : encodeURIComponent(v as string)]
+ })
+ const joinedValues = values.join(",")
+ switch (style) {
+ case "form":
+ return `${name}=${joinedValues}`
+ case "label":
+ return `.${joinedValues}`
+ case "matrix":
+ return `;${name}=${joinedValues}`
+ default:
+ return joinedValues
+ }
+ }
+
+ const separator = separatorObjectExplode(style)
+ const joinedValues = Object.entries(value)
+ .map(([key, v]) =>
+ serializePrimitiveParam({
+ allowReserved,
+ name: style === "deepObject" ? `${name}[${key}]` : key,
+ value: v as string,
+ }),
+ )
+ .join(separator)
+ return style === "label" || style === "matrix" ? separator + joinedValues : joinedValues
+}
diff --git a/packages/sdk/js/src/v2/gen/core/queryKeySerializer.gen.ts b/packages/sdk/js/src/v2/gen/core/queryKeySerializer.gen.ts
new file mode 100644
index 000000000..320204aef
--- /dev/null
+++ b/packages/sdk/js/src/v2/gen/core/queryKeySerializer.gen.ts
@@ -0,0 +1,111 @@
+// This file is auto-generated by @hey-api/openapi-ts
+
+/**
+ * JSON-friendly union that mirrors what Pinia Colada can hash.
+ */
+export type JsonValue = null | string | number | boolean | JsonValue[] | { [key: string]: JsonValue }
+
+/**
+ * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes.
+ */
+export const queryKeyJsonReplacer = (_key: string, value: unknown) => {
+ if (value === undefined || typeof value === "function" || typeof value === "symbol") {
+ return undefined
+ }
+ if (typeof value === "bigint") {
+ return value.toString()
+ }
+ if (value instanceof Date) {
+ return value.toISOString()
+ }
+ return value
+}
+
+/**
+ * Safely stringifies a value and parses it back into a JsonValue.
+ */
+export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => {
+ try {
+ const json = JSON.stringify(input, queryKeyJsonReplacer)
+ if (json === undefined) {
+ return undefined
+ }
+ return JSON.parse(json) as JsonValue
+ } catch {
+ return undefined
+ }
+}
+
+/**
+ * Detects plain objects (including objects with a null prototype).
+ */
+const isPlainObject = (value: unknown): value is Record<string, unknown> => {
+ if (value === null || typeof value !== "object") {
+ return false
+ }
+ const prototype = Object.getPrototypeOf(value as object)
+ return prototype === Object.prototype || prototype === null
+}
+
+/**
+ * Turns URLSearchParams into a sorted JSON object for deterministic keys.
+ */
+const serializeSearchParams = (params: URLSearchParams): JsonValue => {
+ const entries = Array.from(params.entries()).sort(([a], [b]) => a.localeCompare(b))
+ const result: Record<string, JsonValue> = {}
+
+ for (const [key, value] of entries) {
+ const existing = result[key]
+ if (existing === undefined) {
+ result[key] = value
+ continue
+ }
+
+ if (Array.isArray(existing)) {
+ ;(existing as string[]).push(value)
+ } else {
+ result[key] = [existing, value]
+ }
+ }
+
+ return result
+}
+
+/**
+ * Normalizes any accepted value into a JSON-friendly shape for query keys.
+ */
+export const serializeQueryKeyValue = (value: unknown): JsonValue | undefined => {
+ if (value === null) {
+ return null
+ }
+
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
+ return value
+ }
+
+ if (value === undefined || typeof value === "function" || typeof value === "symbol") {
+ return undefined
+ }
+
+ if (typeof value === "bigint") {
+ return value.toString()
+ }
+
+ if (value instanceof Date) {
+ return value.toISOString()
+ }
+
+ if (Array.isArray(value)) {
+ return stringifyToJsonValue(value)
+ }
+
+ if (typeof URLSearchParams !== "undefined" && value instanceof URLSearchParams) {
+ return serializeSearchParams(value)
+ }
+
+ if (isPlainObject(value)) {
+ return stringifyToJsonValue(value)
+ }
+
+ return undefined
+}
diff --git a/packages/sdk/js/src/v2/gen/core/serverSentEvents.gen.ts b/packages/sdk/js/src/v2/gen/core/serverSentEvents.gen.ts
new file mode 100644
index 000000000..09ef3fb39
--- /dev/null
+++ b/packages/sdk/js/src/v2/gen/core/serverSentEvents.gen.ts
@@ -0,0 +1,237 @@
+// This file is auto-generated by @hey-api/openapi-ts
+
+import type { Config } from "./types.gen.js"
+
+export type ServerSentEventsOptions<TData = unknown> = Omit<RequestInit, "method"> &
+ 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.
+ *
+ * This option applies only if the endpoint returns a stream of events.
+ *
+ * @param error The error that occurred.
+ */
+ onSseError?: (error: unknown) => void
+ /**
+ * Callback invoked when an event is streamed from the server.
+ *
+ * This option applies only if the endpoint returns a stream of events.
+ *
+ * @param event Event streamed from the server.
+ * @returns Nothing (void).
+ */
+ onSseEvent?: (event: StreamEvent<TData>) => void
+ serializedBody?: RequestInit["body"]
+ /**
+ * Default retry delay in milliseconds.
+ *
+ * This option applies only if the endpoint returns a stream of events.
+ *
+ * @default 3000
+ */
+ sseDefaultRetryDelay?: number
+ /**
+ * Maximum number of retry attempts before giving up.
+ */
+ sseMaxRetryAttempts?: number
+ /**
+ * Maximum retry delay in milliseconds.
+ *
+ * Applies only when exponential backoff is used.
+ *
+ * This option applies only if the endpoint returns a stream of events.
+ *
+ * @default 30000
+ */
+ sseMaxRetryDelay?: number
+ /**
+ * Optional sleep function for retry backoff.
+ *
+ * Defaults to using `setTimeout`.
+ */
+ sseSleepFn?: (ms: number) => Promise<void>
+ url: string
+ }
+
+export interface StreamEvent<TData = unknown> {
+ data: TData
+ event?: string
+ id?: string
+ retry?: number
+}
+
+export type ServerSentEventsResult<TData = unknown, TReturn = void, TNext = unknown> = {
+ stream: AsyncGenerator<TData extends Record<string, unknown> ? TData[keyof TData] : TData, TReturn, TNext>
+}
+
+export const createSseClient = <TData = unknown>({
+ onRequest,
+ onSseError,
+ onSseEvent,
+ responseTransformer,
+ responseValidator,
+ sseDefaultRetryDelay,
+ sseMaxRetryAttempts,
+ sseMaxRetryDelay,
+ sseSleepFn,
+ url,
+ ...options
+}: ServerSentEventsOptions): ServerSentEventsResult<TData> => {
+ let lastEventId: string | undefined
+
+ const sleep = sseSleepFn ?? ((ms: number) => new Promise((resolve) => setTimeout(resolve, ms)))
+
+ const createStream = async function* () {
+ let retryDelay: number = sseDefaultRetryDelay ?? 3000
+ let attempt = 0
+ const signal = options.signal ?? new AbortController().signal
+
+ while (true) {
+ if (signal.aborted) break
+
+ attempt++
+
+ const headers =
+ options.headers instanceof Headers
+ ? options.headers
+ : new Headers(options.headers as Record<string, string> | undefined)
+
+ if (lastEventId !== undefined) {
+ headers.set("Last-Event-ID", lastEventId)
+ }
+
+ try {
+ const requestInit: RequestInit = {
+ 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.body) throw new Error("No body in SSE response")
+
+ const reader = response.body.pipeThrough(new TextDecoderStream()).getReader()
+
+ let buffer = ""
+
+ const abortHandler = () => {
+ try {
+ reader.cancel()
+ } catch {
+ // noop
+ }
+ }
+
+ signal.addEventListener("abort", abortHandler)
+
+ try {
+ while (true) {
+ const { done, value } = await reader.read()
+ if (done) break
+ buffer += value
+
+ const chunks = buffer.split("\n\n")
+ buffer = chunks.pop() ?? ""
+
+ for (const chunk of chunks) {
+ const lines = chunk.split("\n")
+ const dataLines: Array<string> = []
+ let eventName: string | undefined
+
+ for (const line of lines) {
+ if (line.startsWith("data:")) {
+ dataLines.push(line.replace(/^data:\s*/, ""))
+ } else if (line.startsWith("event:")) {
+ eventName = line.replace(/^event:\s*/, "")
+ } else if (line.startsWith("id:")) {
+ lastEventId = line.replace(/^id:\s*/, "")
+ } else if (line.startsWith("retry:")) {
+ const parsed = Number.parseInt(line.replace(/^retry:\s*/, ""), 10)
+ if (!Number.isNaN(parsed)) {
+ retryDelay = parsed
+ }
+ }
+ }
+
+ let data: unknown
+ let parsedJson = false
+
+ if (dataLines.length) {
+ const rawData = dataLines.join("\n")
+ try {
+ data = JSON.parse(rawData)
+ parsedJson = true
+ } catch {
+ data = rawData
+ }
+ }
+
+ if (parsedJson) {
+ if (responseValidator) {
+ await responseValidator(data)
+ }
+
+ if (responseTransformer) {
+ data = await responseTransformer(data)
+ }
+ }
+
+ onSseEvent?.({
+ data,
+ event: eventName,
+ id: lastEventId,
+ retry: retryDelay,
+ })
+
+ if (dataLines.length) {
+ yield data as any
+ }
+ }
+ }
+ } finally {
+ signal.removeEventListener("abort", abortHandler)
+ reader.releaseLock()
+ }
+
+ break // exit loop on normal completion
+ } catch (error) {
+ // connection failed or aborted; retry after delay
+ onSseError?.(error)
+
+ if (sseMaxRetryAttempts !== undefined && attempt >= sseMaxRetryAttempts) {
+ break // stop after firing error
+ }
+
+ // exponential backoff: double retry each attempt, cap at 30s
+ const backoff = Math.min(retryDelay * 2 ** (attempt - 1), sseMaxRetryDelay ?? 30000)
+ await sleep(backoff)
+ }
+ }
+ }
+
+ const stream = createStream()
+
+ return { stream }
+}
diff --git a/packages/sdk/js/src/v2/gen/core/types.gen.ts b/packages/sdk/js/src/v2/gen/core/types.gen.ts
new file mode 100644
index 000000000..bfa77b8ac
--- /dev/null
+++ b/packages/sdk/js/src/v2/gen/core/types.gen.ts
@@ -0,0 +1,86 @@
+// This file is auto-generated by @hey-api/openapi-ts
+
+import type { Auth, AuthToken } from "./auth.gen.js"
+import type { BodySerializer, QuerySerializer, QuerySerializerOptions } from "./bodySerializer.gen.js"
+
+export type HttpMethod = "connect" | "delete" | "get" | "head" | "options" | "patch" | "post" | "put" | "trace"
+
+export type Client<RequestFn = never, Config = unknown, MethodFn = never, BuildUrlFn = never, SseFn = never> = {
+ /**
+ * Returns the final request URL.
+ */
+ buildUrl: BuildUrlFn
+ getConfig: () => Config
+ request: RequestFn
+ setConfig: (config: Config) => Config
+} & {
+ [K in HttpMethod]: MethodFn
+} & ([SseFn] extends [never] ? { sse?: never } : { sse: { [K in HttpMethod]: SseFn } })
+
+export interface Config {
+ /**
+ * Auth token or a function returning auth token. The resolved value will be
+ * added to the request payload as defined by its `security` array.
+ */
+ auth?: ((auth: Auth) => Promise<AuthToken> | AuthToken) | AuthToken
+ /**
+ * A function for serializing request body parameter. By default,
+ * {@link JSON.stringify()} will be used.
+ */
+ bodySerializer?: BodySerializer | null
+ /**
+ * An object containing any HTTP headers that you want to pre-populate your
+ * `Headers` object with.
+ *
+ * {@link https://developer.mozilla.org/docs/Web/API/Headers/Headers#init See more}
+ */
+ headers?:
+ | RequestInit["headers"]
+ | Record<string, string | number | boolean | (string | number | boolean)[] | null | undefined | unknown>
+ /**
+ * The request method.
+ *
+ * {@link https://developer.mozilla.org/docs/Web/API/fetch#method See more}
+ */
+ method?: Uppercase<HttpMethod>
+ /**
+ * A function for serializing request query parameters. By default, arrays
+ * will be exploded in form style, objects will be exploded in deepObject
+ * style, and reserved characters are percent-encoded.
+ *
+ * This method will have no effect if the native `paramsSerializer()` Axios
+ * API function is used.
+ *
+ * {@link https://swagger.io/docs/specification/serialization/#query View examples}
+ */
+ querySerializer?: QuerySerializer | QuerySerializerOptions
+ /**
+ * A function validating request data. This is useful if you want to ensure
+ * the request conforms to the desired shape, so it can be safely sent to
+ * the server.
+ */
+ requestValidator?: (data: unknown) => Promise<unknown>
+ /**
+ * A function transforming response data before it's returned. This is useful
+ * for post-processing data, e.g. converting ISO strings into Date objects.
+ */
+ responseTransformer?: (data: unknown) => Promise<unknown>
+ /**
+ * A function validating response data. This is useful if you want to ensure
+ * the response conforms to the desired shape, so it can be safely passed to
+ * the transformers and returned to the user.
+ */
+ responseValidator?: (data: unknown) => Promise<unknown>
+}
+
+type IsExactlyNeverOrNeverUndefined<T> = [T] extends [never]
+ ? true
+ : [T] extends [never | undefined]
+ ? [undefined] extends [T]
+ ? false
+ : true
+ : false
+
+export type OmitNever<T extends Record<string, unknown>> = {
+ [K in keyof T as IsExactlyNeverOrNeverUndefined<T[K]> extends true ? never : K]: T[K]
+}
diff --git a/packages/sdk/js/src/v2/gen/core/utils.gen.ts b/packages/sdk/js/src/v2/gen/core/utils.gen.ts
new file mode 100644
index 000000000..8a45f7269
--- /dev/null
+++ b/packages/sdk/js/src/v2/gen/core/utils.gen.ts
@@ -0,0 +1,137 @@
+// This file is auto-generated by @hey-api/openapi-ts
+
+import type { BodySerializer, QuerySerializer } from "./bodySerializer.gen.js"
+import {
+ type ArraySeparatorStyle,
+ serializeArrayParam,
+ serializeObjectParam,
+ serializePrimitiveParam,
+} from "./pathSerializer.gen.js"
+
+export interface PathSerializer {
+ path: Record<string, unknown>
+ url: string
+}
+
+export const PATH_PARAM_RE = /\{[^{}]+\}/g
+
+export const defaultPathSerializer = ({ path, url: _url }: PathSerializer) => {
+ let url = _url
+ const matches = _url.match(PATH_PARAM_RE)
+ if (matches) {
+ for (const match of matches) {
+ let explode = false
+ let name = match.substring(1, match.length - 1)
+ let style: ArraySeparatorStyle = "simple"
+
+ if (name.endsWith("*")) {
+ explode = true
+ name = name.substring(0, name.length - 1)
+ }
+
+ if (name.startsWith(".")) {
+ name = name.substring(1)
+ style = "label"
+ } else if (name.startsWith(";")) {
+ name = name.substring(1)
+ style = "matrix"
+ }
+
+ const value = path[name]
+
+ if (value === undefined || value === null) {
+ continue
+ }
+
+ if (Array.isArray(value)) {
+ url = url.replace(match, serializeArrayParam({ explode, name, style, value }))
+ continue
+ }
+
+ if (typeof value === "object") {
+ url = url.replace(
+ match,
+ serializeObjectParam({
+ explode,
+ name,
+ style,
+ value: value as Record<string, unknown>,
+ valueOnly: true,
+ }),
+ )
+ continue
+ }
+
+ if (style === "matrix") {
+ url = url.replace(
+ match,
+ `;${serializePrimitiveParam({
+ name,
+ value: value as string,
+ })}`,
+ )
+ continue
+ }
+
+ const replaceValue = encodeURIComponent(style === "label" ? `.${value as string}` : (value as string))
+ url = url.replace(match, replaceValue)
+ }
+ }
+ return url
+}
+
+export const getUrl = ({
+ baseUrl,
+ path,
+ query,
+ querySerializer,
+ url: _url,
+}: {
+ baseUrl?: string
+ path?: Record<string, unknown>
+ query?: Record<string, unknown>
+ querySerializer: QuerySerializer
+ url: string
+}) => {
+ const pathUrl = _url.startsWith("/") ? _url : `/${_url}`
+ let url = (baseUrl ?? "") + pathUrl
+ if (path) {
+ url = defaultPathSerializer({ path, url })
+ }
+ let search = query ? querySerializer(query) : ""
+ if (search.startsWith("?")) {
+ search = search.substring(1)
+ }
+ if (search) {
+ url += `?${search}`
+ }
+ 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
+}
diff --git a/packages/sdk/js/src/v2/gen/sdk.gen.ts b/packages/sdk/js/src/v2/gen/sdk.gen.ts
new file mode 100644
index 000000000..caf6c9d00
--- /dev/null
+++ b/packages/sdk/js/src/v2/gen/sdk.gen.ts
@@ -0,0 +1,2343 @@
+// This file is auto-generated by @hey-api/openapi-ts
+
+import { client } from "./client.gen.js"
+import { buildClientParams, type Client, type Options as Options2, type TDataShape } from "./client/index.js"
+import type {
+ AgentPartInput,
+ AppAgentsResponses,
+ AppLogErrors,
+ AppLogResponses,
+ Auth as Auth2,
+ AuthSetErrors,
+ AuthSetResponses,
+ CommandListResponses,
+ Config as Config2,
+ ConfigGetResponses,
+ ConfigProvidersResponses,
+ ConfigUpdateErrors,
+ ConfigUpdateResponses,
+ EventSubscribeResponses,
+ EventTuiCommandExecute,
+ EventTuiPromptAppend,
+ EventTuiToastShow,
+ FileListResponses,
+ FilePartInput,
+ FileReadResponses,
+ FileStatusResponses,
+ FindFilesResponses,
+ FindSymbolsResponses,
+ FindTextResponses,
+ FormatterStatusResponses,
+ GlobalEventResponses,
+ InstanceDisposeResponses,
+ LspStatusResponses,
+ McpAddErrors,
+ McpAddResponses,
+ McpAuthAuthenticateErrors,
+ McpAuthAuthenticateResponses,
+ McpAuthCallbackErrors,
+ McpAuthCallbackResponses,
+ McpAuthRemoveErrors,
+ McpAuthRemoveResponses,
+ McpAuthStartErrors,
+ McpAuthStartResponses,
+ McpLocalConfig,
+ McpRemoteConfig,
+ McpStatusResponses,
+ PathGetResponses,
+ PermissionRespondErrors,
+ PermissionRespondResponses,
+ ProjectCurrentResponses,
+ ProjectListResponses,
+ ProviderAuthResponses,
+ ProviderListResponses,
+ ProviderOauthAuthorizeErrors,
+ ProviderOauthAuthorizeResponses,
+ ProviderOauthCallbackErrors,
+ ProviderOauthCallbackResponses,
+ PtyConnectErrors,
+ PtyConnectResponses,
+ PtyCreateErrors,
+ PtyCreateResponses,
+ PtyGetErrors,
+ PtyGetResponses,
+ PtyListResponses,
+ PtyRemoveErrors,
+ PtyRemoveResponses,
+ PtyUpdateErrors,
+ PtyUpdateResponses,
+ SessionAbortErrors,
+ SessionAbortResponses,
+ SessionChildrenErrors,
+ SessionChildrenResponses,
+ SessionCommandErrors,
+ SessionCommandResponses,
+ SessionCreateErrors,
+ SessionCreateResponses,
+ SessionDeleteErrors,
+ SessionDeleteResponses,
+ SessionDiffErrors,
+ SessionDiffResponses,
+ SessionForkResponses,
+ SessionGetErrors,
+ SessionGetResponses,
+ SessionInitErrors,
+ SessionInitResponses,
+ SessionListResponses,
+ SessionMessageErrors,
+ SessionMessageResponses,
+ SessionMessagesErrors,
+ SessionMessagesResponses,
+ SessionPromptAsyncErrors,
+ SessionPromptAsyncResponses,
+ SessionPromptErrors,
+ SessionPromptResponses,
+ SessionRevertErrors,
+ SessionRevertResponses,
+ SessionShareErrors,
+ SessionShareResponses,
+ SessionShellErrors,
+ SessionShellResponses,
+ SessionStatusErrors,
+ SessionStatusResponses,
+ SessionSummarizeErrors,
+ SessionSummarizeResponses,
+ SessionTodoErrors,
+ SessionTodoResponses,
+ SessionUnrevertErrors,
+ SessionUnrevertResponses,
+ SessionUnshareErrors,
+ SessionUnshareResponses,
+ SessionUpdateErrors,
+ SessionUpdateResponses,
+ SubtaskPartInput,
+ TextPartInput,
+ ToolIdsErrors,
+ ToolIdsResponses,
+ ToolListErrors,
+ ToolListResponses,
+ TuiAppendPromptErrors,
+ TuiAppendPromptResponses,
+ TuiClearPromptResponses,
+ TuiControlNextResponses,
+ TuiControlResponseResponses,
+ TuiExecuteCommandErrors,
+ TuiExecuteCommandResponses,
+ TuiOpenHelpResponses,
+ TuiOpenModelsResponses,
+ TuiOpenSessionsResponses,
+ TuiOpenThemesResponses,
+ TuiPublishErrors,
+ TuiPublishResponses,
+ TuiShowToastResponses,
+ TuiSubmitPromptResponses,
+ VcsGetResponses,
+} from "./types.gen.js"
+
+export type Options<TData extends TDataShape = TDataShape, ThrowOnError extends boolean = boolean> = Options2<
+ TData,
+ ThrowOnError
+> & {
+ /**
+ * You can provide a client instance returned by `createClient()` instead of
+ * individual options. This might be also useful if you want to implement a
+ * custom client.
+ */
+ client?: Client
+ /**
+ * You can pass arbitrary values through the `meta` object. This can be
+ * used to access values that aren't defined as part of the SDK function.
+ */
+ meta?: Record<string, unknown>
+}
+
+class HeyApiClient {
+ protected client: Client
+
+ constructor(args?: { client?: Client }) {
+ this.client = args?.client ?? client
+ }
+}
+
+class HeyApiRegistry<T> {
+ private readonly defaultKey = "default"
+
+ private readonly instances: Map<string, T> = new Map()
+
+ get(key?: string): T {
+ const instance = this.instances.get(key ?? this.defaultKey)
+ if (!instance) {
+ throw new Error(`No SDK client found. Create one with "new OpencodeClient()" to fix this error.`)
+ }
+ return instance
+ }
+
+ set(value: T, key?: string): void {
+ this.instances.set(key ?? this.defaultKey, value)
+ }
+}
+
+export class Global extends HeyApiClient {
+ /**
+ * Get events
+ */
+ public event<ThrowOnError extends boolean = false>(options?: Options<never, ThrowOnError>) {
+ return (options?.client ?? this.client).sse.get<GlobalEventResponses, unknown, ThrowOnError>({
+ url: "/global/event",
+ ...options,
+ })
+ }
+}
+
+export class Project extends HeyApiClient {
+ /**
+ * List all projects
+ */
+ public list<ThrowOnError extends boolean = false>(
+ parameters?: {
+ directory?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+ return (options?.client ?? this.client).get<ProjectListResponses, unknown, ThrowOnError>({
+ url: "/project",
+ ...options,
+ ...params,
+ })
+ }
+
+ /**
+ * Get the current project
+ */
+ public current<ThrowOnError extends boolean = false>(
+ parameters?: {
+ directory?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+ return (options?.client ?? this.client).get<ProjectCurrentResponses, unknown, ThrowOnError>({
+ url: "/project/current",
+ ...options,
+ ...params,
+ })
+ }
+}
+
+export class Pty extends HeyApiClient {
+ /**
+ * List all PTY sessions
+ */
+ public list<ThrowOnError extends boolean = false>(
+ parameters?: {
+ directory?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+ return (options?.client ?? this.client).get<PtyListResponses, unknown, ThrowOnError>({
+ url: "/pty",
+ ...options,
+ ...params,
+ })
+ }
+
+ /**
+ * Create a new PTY session
+ */
+ public create<ThrowOnError extends boolean = false>(
+ parameters?: {
+ directory?: string
+ command?: string
+ args?: Array<string>
+ cwd?: string
+ title?: string
+ env?: {
+ [key: string]: string
+ }
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams(
+ [parameters],
+ [
+ {
+ args: [
+ { in: "query", key: "directory" },
+ { in: "body", key: "command" },
+ { in: "body", key: "args" },
+ { in: "body", key: "cwd" },
+ { in: "body", key: "title" },
+ { in: "body", key: "env" },
+ ],
+ },
+ ],
+ )
+ return (options?.client ?? this.client).post<PtyCreateResponses, PtyCreateErrors, ThrowOnError>({
+ url: "/pty",
+ ...options,
+ ...params,
+ headers: {
+ "Content-Type": "application/json",
+ ...options?.headers,
+ ...params.headers,
+ },
+ })
+ }
+
+ /**
+ * Remove a PTY session
+ */
+ public remove<ThrowOnError extends boolean = false>(
+ parameters: {
+ ptyID: string
+ directory?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams(
+ [parameters],
+ [
+ {
+ args: [
+ { in: "path", key: "ptyID" },
+ { in: "query", key: "directory" },
+ ],
+ },
+ ],
+ )
+ return (options?.client ?? this.client).delete<PtyRemoveResponses, PtyRemoveErrors, ThrowOnError>({
+ url: "/pty/{ptyID}",
+ ...options,
+ ...params,
+ })
+ }
+
+ /**
+ * Get PTY session info
+ */
+ public get<ThrowOnError extends boolean = false>(
+ parameters: {
+ ptyID: string
+ directory?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams(
+ [parameters],
+ [
+ {
+ args: [
+ { in: "path", key: "ptyID" },
+ { in: "query", key: "directory" },
+ ],
+ },
+ ],
+ )
+ return (options?.client ?? this.client).get<PtyGetResponses, PtyGetErrors, ThrowOnError>({
+ url: "/pty/{ptyID}",
+ ...options,
+ ...params,
+ })
+ }
+
+ /**
+ * Update PTY session
+ */
+ public update<ThrowOnError extends boolean = false>(
+ parameters: {
+ ptyID: string
+ directory?: string
+ title?: string
+ size?: {
+ rows: number
+ cols: number
+ }
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams(
+ [parameters],
+ [
+ {
+ args: [
+ { in: "path", key: "ptyID" },
+ { in: "query", key: "directory" },
+ { in: "body", key: "title" },
+ { in: "body", key: "size" },
+ ],
+ },
+ ],
+ )
+ return (options?.client ?? this.client).put<PtyUpdateResponses, PtyUpdateErrors, ThrowOnError>({
+ url: "/pty/{ptyID}",
+ ...options,
+ ...params,
+ headers: {
+ "Content-Type": "application/json",
+ ...options?.headers,
+ ...params.headers,
+ },
+ })
+ }
+
+ /**
+ * Connect to a PTY session
+ */
+ public connect<ThrowOnError extends boolean = false>(
+ parameters: {
+ ptyID: string
+ directory?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams(
+ [parameters],
+ [
+ {
+ args: [
+ { in: "path", key: "ptyID" },
+ { in: "query", key: "directory" },
+ ],
+ },
+ ],
+ )
+ return (options?.client ?? this.client).get<PtyConnectResponses, PtyConnectErrors, ThrowOnError>({
+ url: "/pty/{ptyID}/connect",
+ ...options,
+ ...params,
+ })
+ }
+}
+
+export class Config extends HeyApiClient {
+ /**
+ * Get config info
+ */
+ public get<ThrowOnError extends boolean = false>(
+ parameters?: {
+ directory?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+ return (options?.client ?? this.client).get<ConfigGetResponses, unknown, ThrowOnError>({
+ url: "/config",
+ ...options,
+ ...params,
+ })
+ }
+
+ /**
+ * Update config
+ */
+ public update<ThrowOnError extends boolean = false>(
+ parameters?: {
+ directory?: string
+ config?: Config2
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams(
+ [parameters],
+ [
+ {
+ args: [
+ { in: "query", key: "directory" },
+ { key: "config", map: "body" },
+ ],
+ },
+ ],
+ )
+ return (options?.client ?? this.client).patch<ConfigUpdateResponses, ConfigUpdateErrors, ThrowOnError>({
+ url: "/config",
+ ...options,
+ ...params,
+ headers: {
+ "Content-Type": "application/json",
+ ...options?.headers,
+ ...params.headers,
+ },
+ })
+ }
+
+ /**
+ * List all providers
+ */
+ public providers<ThrowOnError extends boolean = false>(
+ parameters?: {
+ directory?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+ return (options?.client ?? this.client).get<ConfigProvidersResponses, unknown, ThrowOnError>({
+ url: "/config/providers",
+ ...options,
+ ...params,
+ })
+ }
+}
+
+export class Tool extends HeyApiClient {
+ /**
+ * List all tool IDs (including built-in and dynamically registered)
+ */
+ public ids<ThrowOnError extends boolean = false>(
+ parameters?: {
+ directory?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+ return (options?.client ?? this.client).get<ToolIdsResponses, ToolIdsErrors, ThrowOnError>({
+ url: "/experimental/tool/ids",
+ ...options,
+ ...params,
+ })
+ }
+
+ /**
+ * List tools with JSON schema parameters for a provider/model
+ */
+ public list<ThrowOnError extends boolean = false>(
+ parameters: {
+ directory?: string
+ provider: string
+ model: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams(
+ [parameters],
+ [
+ {
+ args: [
+ { in: "query", key: "directory" },
+ { in: "query", key: "provider" },
+ { in: "query", key: "model" },
+ ],
+ },
+ ],
+ )
+ return (options?.client ?? this.client).get<ToolListResponses, ToolListErrors, ThrowOnError>({
+ url: "/experimental/tool",
+ ...options,
+ ...params,
+ })
+ }
+}
+
+export class Instance extends HeyApiClient {
+ /**
+ * Dispose the current instance
+ */
+ public dispose<ThrowOnError extends boolean = false>(
+ parameters?: {
+ directory?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+ return (options?.client ?? this.client).post<InstanceDisposeResponses, unknown, ThrowOnError>({
+ url: "/instance/dispose",
+ ...options,
+ ...params,
+ })
+ }
+}
+
+export class Path extends HeyApiClient {
+ /**
+ * Get the current path
+ */
+ public get<ThrowOnError extends boolean = false>(
+ parameters?: {
+ directory?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+ return (options?.client ?? this.client).get<PathGetResponses, unknown, ThrowOnError>({
+ url: "/path",
+ ...options,
+ ...params,
+ })
+ }
+}
+
+export class Vcs extends HeyApiClient {
+ /**
+ * Get VCS info for the current instance
+ */
+ public get<ThrowOnError extends boolean = false>(
+ parameters?: {
+ directory?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+ return (options?.client ?? this.client).get<VcsGetResponses, unknown, ThrowOnError>({
+ url: "/vcs",
+ ...options,
+ ...params,
+ })
+ }
+}
+
+export class Session extends HeyApiClient {
+ /**
+ * List all sessions
+ */
+ public list<ThrowOnError extends boolean = false>(
+ parameters?: {
+ directory?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+ return (options?.client ?? this.client).get<SessionListResponses, unknown, ThrowOnError>({
+ url: "/session",
+ ...options,
+ ...params,
+ })
+ }
+
+ /**
+ * Create a new session
+ */
+ public create<ThrowOnError extends boolean = false>(
+ parameters?: {
+ directory?: string
+ parentID?: string
+ title?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams(
+ [parameters],
+ [
+ {
+ args: [
+ { in: "query", key: "directory" },
+ { in: "body", key: "parentID" },
+ { in: "body", key: "title" },
+ ],
+ },
+ ],
+ )
+ return (options?.client ?? this.client).post<SessionCreateResponses, SessionCreateErrors, ThrowOnError>({
+ url: "/session",
+ ...options,
+ ...params,
+ headers: {
+ "Content-Type": "application/json",
+ ...options?.headers,
+ ...params.headers,
+ },
+ })
+ }
+
+ /**
+ * Get session status
+ */
+ public status<ThrowOnError extends boolean = false>(
+ parameters?: {
+ directory?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+ return (options?.client ?? this.client).get<SessionStatusResponses, SessionStatusErrors, ThrowOnError>({
+ url: "/session/status",
+ ...options,
+ ...params,
+ })
+ }
+
+ /**
+ * Delete a session and all its data
+ */
+ public delete<ThrowOnError extends boolean = false>(
+ parameters: {
+ sessionID: string
+ directory?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams(
+ [parameters],
+ [
+ {
+ args: [
+ { in: "path", key: "sessionID" },
+ { in: "query", key: "directory" },
+ ],
+ },
+ ],
+ )
+ return (options?.client ?? this.client).delete<SessionDeleteResponses, SessionDeleteErrors, ThrowOnError>({
+ url: "/session/{sessionID}",
+ ...options,
+ ...params,
+ })
+ }
+
+ /**
+ * Get session
+ */
+ public get<ThrowOnError extends boolean = false>(
+ parameters: {
+ sessionID: string
+ directory?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams(
+ [parameters],
+ [
+ {
+ args: [
+ { in: "path", key: "sessionID" },
+ { in: "query", key: "directory" },
+ ],
+ },
+ ],
+ )
+ return (options?.client ?? this.client).get<SessionGetResponses, SessionGetErrors, ThrowOnError>({
+ url: "/session/{sessionID}",
+ ...options,
+ ...params,
+ })
+ }
+
+ /**
+ * Update session properties
+ */
+ public update<ThrowOnError extends boolean = false>(
+ parameters: {
+ sessionID: string
+ directory?: string
+ title?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams(
+ [parameters],
+ [
+ {
+ args: [
+ { in: "path", key: "sessionID" },
+ { in: "query", key: "directory" },
+ { in: "body", key: "title" },
+ ],
+ },
+ ],
+ )
+ return (options?.client ?? this.client).patch<SessionUpdateResponses, SessionUpdateErrors, ThrowOnError>({
+ url: "/session/{sessionID}",
+ ...options,
+ ...params,
+ headers: {
+ "Content-Type": "application/json",
+ ...options?.headers,
+ ...params.headers,
+ },
+ })
+ }
+
+ /**
+ * Get a session's children
+ */
+ public children<ThrowOnError extends boolean = false>(
+ parameters: {
+ sessionID: string
+ directory?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams(
+ [parameters],
+ [
+ {
+ args: [
+ { in: "path", key: "sessionID" },
+ { in: "query", key: "directory" },
+ ],
+ },
+ ],
+ )
+ return (options?.client ?? this.client).get<SessionChildrenResponses, SessionChildrenErrors, ThrowOnError>({
+ url: "/session/{sessionID}/children",
+ ...options,
+ ...params,
+ })
+ }
+
+ /**
+ * Get the todo list for a session
+ */
+ public todo<ThrowOnError extends boolean = false>(
+ parameters: {
+ sessionID: string
+ directory?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams(
+ [parameters],
+ [
+ {
+ args: [
+ { in: "path", key: "sessionID" },
+ { in: "query", key: "directory" },
+ ],
+ },
+ ],
+ )
+ return (options?.client ?? this.client).get<SessionTodoResponses, SessionTodoErrors, ThrowOnError>({
+ url: "/session/{sessionID}/todo",
+ ...options,
+ ...params,
+ })
+ }
+
+ /**
+ * Analyze the app and create an AGENTS.md file
+ */
+ public init<ThrowOnError extends boolean = false>(
+ parameters: {
+ sessionID: string
+ directory?: string
+ modelID?: string
+ providerID?: string
+ messageID?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams(
+ [parameters],
+ [
+ {
+ args: [
+ { in: "path", key: "sessionID" },
+ { in: "query", key: "directory" },
+ { in: "body", key: "modelID" },
+ { in: "body", key: "providerID" },
+ { in: "body", key: "messageID" },
+ ],
+ },
+ ],
+ )
+ return (options?.client ?? this.client).post<SessionInitResponses, SessionInitErrors, ThrowOnError>({
+ url: "/session/{sessionID}/init",
+ ...options,
+ ...params,
+ headers: {
+ "Content-Type": "application/json",
+ ...options?.headers,
+ ...params.headers,
+ },
+ })
+ }
+
+ /**
+ * Fork an existing session at a specific message
+ */
+ public fork<ThrowOnError extends boolean = false>(
+ parameters: {
+ sessionID: string
+ directory?: string
+ messageID?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams(
+ [parameters],
+ [
+ {
+ args: [
+ { in: "path", key: "sessionID" },
+ { in: "query", key: "directory" },
+ { in: "body", key: "messageID" },
+ ],
+ },
+ ],
+ )
+ return (options?.client ?? this.client).post<SessionForkResponses, unknown, ThrowOnError>({
+ url: "/session/{sessionID}/fork",
+ ...options,
+ ...params,
+ headers: {
+ "Content-Type": "application/json",
+ ...options?.headers,
+ ...params.headers,
+ },
+ })
+ }
+
+ /**
+ * Abort a session
+ */
+ public abort<ThrowOnError extends boolean = false>(
+ parameters: {
+ sessionID: string
+ directory?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams(
+ [parameters],
+ [
+ {
+ args: [
+ { in: "path", key: "sessionID" },
+ { in: "query", key: "directory" },
+ ],
+ },
+ ],
+ )
+ return (options?.client ?? this.client).post<SessionAbortResponses, SessionAbortErrors, ThrowOnError>({
+ url: "/session/{sessionID}/abort",
+ ...options,
+ ...params,
+ })
+ }
+
+ /**
+ * Unshare the session
+ */
+ public unshare<ThrowOnError extends boolean = false>(
+ parameters: {
+ sessionID: string
+ directory?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams(
+ [parameters],
+ [
+ {
+ args: [
+ { in: "path", key: "sessionID" },
+ { in: "query", key: "directory" },
+ ],
+ },
+ ],
+ )
+ return (options?.client ?? this.client).delete<SessionUnshareResponses, SessionUnshareErrors, ThrowOnError>({
+ url: "/session/{sessionID}/share",
+ ...options,
+ ...params,
+ })
+ }
+
+ /**
+ * Share a session
+ */
+ public share<ThrowOnError extends boolean = false>(
+ parameters: {
+ sessionID: string
+ directory?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams(
+ [parameters],
+ [
+ {
+ args: [
+ { in: "path", key: "sessionID" },
+ { in: "query", key: "directory" },
+ ],
+ },
+ ],
+ )
+ return (options?.client ?? this.client).post<SessionShareResponses, SessionShareErrors, ThrowOnError>({
+ url: "/session/{sessionID}/share",
+ ...options,
+ ...params,
+ })
+ }
+
+ /**
+ * Get the diff for this session
+ */
+ public diff<ThrowOnError extends boolean = false>(
+ parameters: {
+ sessionID: string
+ directory?: string
+ messageID?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams(
+ [parameters],
+ [
+ {
+ args: [
+ { in: "path", key: "sessionID" },
+ { in: "query", key: "directory" },
+ { in: "query", key: "messageID" },
+ ],
+ },
+ ],
+ )
+ return (options?.client ?? this.client).get<SessionDiffResponses, SessionDiffErrors, ThrowOnError>({
+ url: "/session/{sessionID}/diff",
+ ...options,
+ ...params,
+ })
+ }
+
+ /**
+ * Summarize the session
+ */
+ public summarize<ThrowOnError extends boolean = false>(
+ parameters: {
+ sessionID: string
+ directory?: string
+ providerID?: string
+ modelID?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams(
+ [parameters],
+ [
+ {
+ args: [
+ { in: "path", key: "sessionID" },
+ { in: "query", key: "directory" },
+ { in: "body", key: "providerID" },
+ { in: "body", key: "modelID" },
+ ],
+ },
+ ],
+ )
+ return (options?.client ?? this.client).post<SessionSummarizeResponses, SessionSummarizeErrors, ThrowOnError>({
+ url: "/session/{sessionID}/summarize",
+ ...options,
+ ...params,
+ headers: {
+ "Content-Type": "application/json",
+ ...options?.headers,
+ ...params.headers,
+ },
+ })
+ }
+
+ /**
+ * List messages for a session
+ */
+ public messages<ThrowOnError extends boolean = false>(
+ parameters: {
+ sessionID: string
+ directory?: string
+ limit?: number
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams(
+ [parameters],
+ [
+ {
+ args: [
+ { in: "path", key: "sessionID" },
+ { in: "query", key: "directory" },
+ { in: "query", key: "limit" },
+ ],
+ },
+ ],
+ )
+ return (options?.client ?? this.client).get<SessionMessagesResponses, SessionMessagesErrors, ThrowOnError>({
+ url: "/session/{sessionID}/message",
+ ...options,
+ ...params,
+ })
+ }
+
+ /**
+ * Create and send a new message to a session
+ */
+ public prompt<ThrowOnError extends boolean = false>(
+ parameters: {
+ sessionID: string
+ directory?: string
+ messageID?: string
+ model?: {
+ providerID: string
+ modelID: string
+ }
+ agent?: string
+ noReply?: boolean
+ system?: string
+ tools?: {
+ [key: string]: boolean
+ }
+ parts?: Array<TextPartInput | FilePartInput | AgentPartInput | SubtaskPartInput>
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams(
+ [parameters],
+ [
+ {
+ args: [
+ { in: "path", key: "sessionID" },
+ { in: "query", key: "directory" },
+ { in: "body", key: "messageID" },
+ { in: "body", key: "model" },
+ { in: "body", key: "agent" },
+ { in: "body", key: "noReply" },
+ { in: "body", key: "system" },
+ { in: "body", key: "tools" },
+ { in: "body", key: "parts" },
+ ],
+ },
+ ],
+ )
+ return (options?.client ?? this.client).post<SessionPromptResponses, SessionPromptErrors, ThrowOnError>({
+ url: "/session/{sessionID}/message",
+ ...options,
+ ...params,
+ headers: {
+ "Content-Type": "application/json",
+ ...options?.headers,
+ ...params.headers,
+ },
+ })
+ }
+
+ /**
+ * Get a message from a session
+ */
+ public message<ThrowOnError extends boolean = false>(
+ parameters: {
+ sessionID: string
+ messageID: string
+ directory?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams(
+ [parameters],
+ [
+ {
+ args: [
+ { in: "path", key: "sessionID" },
+ { in: "path", key: "messageID" },
+ { in: "query", key: "directory" },
+ ],
+ },
+ ],
+ )
+ return (options?.client ?? this.client).get<SessionMessageResponses, SessionMessageErrors, ThrowOnError>({
+ url: "/session/{sessionID}/message/{messageID}",
+ ...options,
+ ...params,
+ })
+ }
+
+ /**
+ * Create and send a new message to a session, start if needed and return immediately
+ */
+ public promptAsync<ThrowOnError extends boolean = false>(
+ parameters: {
+ sessionID: string
+ directory?: string
+ messageID?: string
+ model?: {
+ providerID: string
+ modelID: string
+ }
+ agent?: string
+ noReply?: boolean
+ system?: string
+ tools?: {
+ [key: string]: boolean
+ }
+ parts?: Array<TextPartInput | FilePartInput | AgentPartInput | SubtaskPartInput>
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams(
+ [parameters],
+ [
+ {
+ args: [
+ { in: "path", key: "sessionID" },
+ { in: "query", key: "directory" },
+ { in: "body", key: "messageID" },
+ { in: "body", key: "model" },
+ { in: "body", key: "agent" },
+ { in: "body", key: "noReply" },
+ { in: "body", key: "system" },
+ { in: "body", key: "tools" },
+ { in: "body", key: "parts" },
+ ],
+ },
+ ],
+ )
+ return (options?.client ?? this.client).post<SessionPromptAsyncResponses, SessionPromptAsyncErrors, ThrowOnError>({
+ url: "/session/{sessionID}/prompt_async",
+ ...options,
+ ...params,
+ headers: {
+ "Content-Type": "application/json",
+ ...options?.headers,
+ ...params.headers,
+ },
+ })
+ }
+
+ /**
+ * Send a new command to a session
+ */
+ public command<ThrowOnError extends boolean = false>(
+ parameters: {
+ sessionID: string
+ directory?: string
+ messageID?: string
+ agent?: string
+ model?: string
+ arguments?: string
+ command?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams(
+ [parameters],
+ [
+ {
+ args: [
+ { in: "path", key: "sessionID" },
+ { in: "query", key: "directory" },
+ { in: "body", key: "messageID" },
+ { in: "body", key: "agent" },
+ { in: "body", key: "model" },
+ { in: "body", key: "arguments" },
+ { in: "body", key: "command" },
+ ],
+ },
+ ],
+ )
+ return (options?.client ?? this.client).post<SessionCommandResponses, SessionCommandErrors, ThrowOnError>({
+ url: "/session/{sessionID}/command",
+ ...options,
+ ...params,
+ headers: {
+ "Content-Type": "application/json",
+ ...options?.headers,
+ ...params.headers,
+ },
+ })
+ }
+
+ /**
+ * Run a shell command
+ */
+ public shell<ThrowOnError extends boolean = false>(
+ parameters: {
+ sessionID: string
+ directory?: string
+ agent?: string
+ model?: {
+ providerID: string
+ modelID: string
+ }
+ command?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams(
+ [parameters],
+ [
+ {
+ args: [
+ { in: "path", key: "sessionID" },
+ { in: "query", key: "directory" },
+ { in: "body", key: "agent" },
+ { in: "body", key: "model" },
+ { in: "body", key: "command" },
+ ],
+ },
+ ],
+ )
+ return (options?.client ?? this.client).post<SessionShellResponses, SessionShellErrors, ThrowOnError>({
+ url: "/session/{sessionID}/shell",
+ ...options,
+ ...params,
+ headers: {
+ "Content-Type": "application/json",
+ ...options?.headers,
+ ...params.headers,
+ },
+ })
+ }
+
+ /**
+ * Revert a message
+ */
+ public revert<ThrowOnError extends boolean = false>(
+ parameters: {
+ sessionID: string
+ directory?: string
+ messageID?: string
+ partID?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams(
+ [parameters],
+ [
+ {
+ args: [
+ { in: "path", key: "sessionID" },
+ { in: "query", key: "directory" },
+ { in: "body", key: "messageID" },
+ { in: "body", key: "partID" },
+ ],
+ },
+ ],
+ )
+ return (options?.client ?? this.client).post<SessionRevertResponses, SessionRevertErrors, ThrowOnError>({
+ url: "/session/{sessionID}/revert",
+ ...options,
+ ...params,
+ headers: {
+ "Content-Type": "application/json",
+ ...options?.headers,
+ ...params.headers,
+ },
+ })
+ }
+
+ /**
+ * Restore all reverted messages
+ */
+ public unrevert<ThrowOnError extends boolean = false>(
+ parameters: {
+ sessionID: string
+ directory?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams(
+ [parameters],
+ [
+ {
+ args: [
+ { in: "path", key: "sessionID" },
+ { in: "query", key: "directory" },
+ ],
+ },
+ ],
+ )
+ return (options?.client ?? this.client).post<SessionUnrevertResponses, SessionUnrevertErrors, ThrowOnError>({
+ url: "/session/{sessionID}/unrevert",
+ ...options,
+ ...params,
+ })
+ }
+}
+
+export class Permission extends HeyApiClient {
+ /**
+ * Respond to a permission request
+ */
+ public respond<ThrowOnError extends boolean = false>(
+ parameters: {
+ sessionID: string
+ permissionID: string
+ directory?: string
+ response?: "once" | "always" | "reject"
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams(
+ [parameters],
+ [
+ {
+ args: [
+ { in: "path", key: "sessionID" },
+ { in: "path", key: "permissionID" },
+ { in: "query", key: "directory" },
+ { in: "body", key: "response" },
+ ],
+ },
+ ],
+ )
+ return (options?.client ?? this.client).post<PermissionRespondResponses, PermissionRespondErrors, ThrowOnError>({
+ url: "/session/{sessionID}/permissions/{permissionID}",
+ ...options,
+ ...params,
+ headers: {
+ "Content-Type": "application/json",
+ ...options?.headers,
+ ...params.headers,
+ },
+ })
+ }
+}
+
+export class Command extends HeyApiClient {
+ /**
+ * List all commands
+ */
+ public list<ThrowOnError extends boolean = false>(
+ parameters?: {
+ directory?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+ return (options?.client ?? this.client).get<CommandListResponses, unknown, ThrowOnError>({
+ url: "/command",
+ ...options,
+ ...params,
+ })
+ }
+}
+
+export class Oauth extends HeyApiClient {
+ /**
+ * Authorize a provider using OAuth
+ */
+ public authorize<ThrowOnError extends boolean = false>(
+ parameters: {
+ providerID: string
+ directory?: string
+ method?: number
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams(
+ [parameters],
+ [
+ {
+ args: [
+ { in: "path", key: "providerID" },
+ { in: "query", key: "directory" },
+ { in: "body", key: "method" },
+ ],
+ },
+ ],
+ )
+ return (options?.client ?? this.client).post<
+ ProviderOauthAuthorizeResponses,
+ ProviderOauthAuthorizeErrors,
+ ThrowOnError
+ >({
+ url: "/provider/{providerID}/oauth/authorize",
+ ...options,
+ ...params,
+ headers: {
+ "Content-Type": "application/json",
+ ...options?.headers,
+ ...params.headers,
+ },
+ })
+ }
+
+ /**
+ * Handle OAuth callback for a provider
+ */
+ public callback<ThrowOnError extends boolean = false>(
+ parameters: {
+ providerID: string
+ directory?: string
+ method?: number
+ code?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams(
+ [parameters],
+ [
+ {
+ args: [
+ { in: "path", key: "providerID" },
+ { in: "query", key: "directory" },
+ { in: "body", key: "method" },
+ { in: "body", key: "code" },
+ ],
+ },
+ ],
+ )
+ return (options?.client ?? this.client).post<
+ ProviderOauthCallbackResponses,
+ ProviderOauthCallbackErrors,
+ ThrowOnError
+ >({
+ url: "/provider/{providerID}/oauth/callback",
+ ...options,
+ ...params,
+ headers: {
+ "Content-Type": "application/json",
+ ...options?.headers,
+ ...params.headers,
+ },
+ })
+ }
+}
+
+export class Provider extends HeyApiClient {
+ /**
+ * List all providers
+ */
+ public list<ThrowOnError extends boolean = false>(
+ parameters?: {
+ directory?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+ return (options?.client ?? this.client).get<ProviderListResponses, unknown, ThrowOnError>({
+ url: "/provider",
+ ...options,
+ ...params,
+ })
+ }
+
+ /**
+ * Get provider authentication methods
+ */
+ public auth<ThrowOnError extends boolean = false>(
+ parameters?: {
+ directory?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+ return (options?.client ?? this.client).get<ProviderAuthResponses, unknown, ThrowOnError>({
+ url: "/provider/auth",
+ ...options,
+ ...params,
+ })
+ }
+
+ oauth = new Oauth({ client: this.client })
+}
+
+export class Find extends HeyApiClient {
+ /**
+ * Find text in files
+ */
+ public text<ThrowOnError extends boolean = false>(
+ parameters: {
+ directory?: string
+ pattern: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams(
+ [parameters],
+ [
+ {
+ args: [
+ { in: "query", key: "directory" },
+ { in: "query", key: "pattern" },
+ ],
+ },
+ ],
+ )
+ return (options?.client ?? this.client).get<FindTextResponses, unknown, ThrowOnError>({
+ url: "/find",
+ ...options,
+ ...params,
+ })
+ }
+
+ /**
+ * Find files
+ */
+ public files<ThrowOnError extends boolean = false>(
+ parameters: {
+ directory?: string
+ query: string
+ dirs?: "true" | "false"
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams(
+ [parameters],
+ [
+ {
+ args: [
+ { in: "query", key: "directory" },
+ { in: "query", key: "query" },
+ { in: "query", key: "dirs" },
+ ],
+ },
+ ],
+ )
+ return (options?.client ?? this.client).get<FindFilesResponses, unknown, ThrowOnError>({
+ url: "/find/file",
+ ...options,
+ ...params,
+ })
+ }
+
+ /**
+ * Find workspace symbols
+ */
+ public symbols<ThrowOnError extends boolean = false>(
+ parameters: {
+ directory?: string
+ query: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams(
+ [parameters],
+ [
+ {
+ args: [
+ { in: "query", key: "directory" },
+ { in: "query", key: "query" },
+ ],
+ },
+ ],
+ )
+ return (options?.client ?? this.client).get<FindSymbolsResponses, unknown, ThrowOnError>({
+ url: "/find/symbol",
+ ...options,
+ ...params,
+ })
+ }
+}
+
+export class File extends HeyApiClient {
+ /**
+ * List files and directories
+ */
+ public list<ThrowOnError extends boolean = false>(
+ parameters: {
+ directory?: string
+ path: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams(
+ [parameters],
+ [
+ {
+ args: [
+ { in: "query", key: "directory" },
+ { in: "query", key: "path" },
+ ],
+ },
+ ],
+ )
+ return (options?.client ?? this.client).get<FileListResponses, unknown, ThrowOnError>({
+ url: "/file",
+ ...options,
+ ...params,
+ })
+ }
+
+ /**
+ * Read a file
+ */
+ public read<ThrowOnError extends boolean = false>(
+ parameters: {
+ directory?: string
+ path: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams(
+ [parameters],
+ [
+ {
+ args: [
+ { in: "query", key: "directory" },
+ { in: "query", key: "path" },
+ ],
+ },
+ ],
+ )
+ return (options?.client ?? this.client).get<FileReadResponses, unknown, ThrowOnError>({
+ url: "/file/content",
+ ...options,
+ ...params,
+ })
+ }
+
+ /**
+ * Get file status
+ */
+ public status<ThrowOnError extends boolean = false>(
+ parameters?: {
+ directory?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+ return (options?.client ?? this.client).get<FileStatusResponses, unknown, ThrowOnError>({
+ url: "/file/status",
+ ...options,
+ ...params,
+ })
+ }
+}
+
+export class App extends HeyApiClient {
+ /**
+ * Write a log entry to the server logs
+ */
+ public log<ThrowOnError extends boolean = false>(
+ parameters?: {
+ directory?: string
+ service?: string
+ level?: "debug" | "info" | "error" | "warn"
+ message?: string
+ extra?: {
+ [key: string]: unknown
+ }
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams(
+ [parameters],
+ [
+ {
+ args: [
+ { in: "query", key: "directory" },
+ { in: "body", key: "service" },
+ { in: "body", key: "level" },
+ { in: "body", key: "message" },
+ { in: "body", key: "extra" },
+ ],
+ },
+ ],
+ )
+ return (options?.client ?? this.client).post<AppLogResponses, AppLogErrors, ThrowOnError>({
+ url: "/log",
+ ...options,
+ ...params,
+ headers: {
+ "Content-Type": "application/json",
+ ...options?.headers,
+ ...params.headers,
+ },
+ })
+ }
+
+ /**
+ * List all agents
+ */
+ public agents<ThrowOnError extends boolean = false>(
+ parameters?: {
+ directory?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+ return (options?.client ?? this.client).get<AppAgentsResponses, unknown, ThrowOnError>({
+ url: "/agent",
+ ...options,
+ ...params,
+ })
+ }
+}
+
+export class Auth extends HeyApiClient {
+ /**
+ * Remove OAuth credentials for an MCP server
+ */
+ public remove<ThrowOnError extends boolean = false>(
+ parameters: {
+ name: string
+ directory?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams(
+ [parameters],
+ [
+ {
+ args: [
+ { in: "path", key: "name" },
+ { in: "query", key: "directory" },
+ ],
+ },
+ ],
+ )
+ return (options?.client ?? this.client).delete<McpAuthRemoveResponses, McpAuthRemoveErrors, ThrowOnError>({
+ url: "/mcp/{name}/auth",
+ ...options,
+ ...params,
+ })
+ }
+
+ /**
+ * Start OAuth authentication flow for an MCP server
+ */
+ public start<ThrowOnError extends boolean = false>(
+ parameters: {
+ name: string
+ directory?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams(
+ [parameters],
+ [
+ {
+ args: [
+ { in: "path", key: "name" },
+ { in: "query", key: "directory" },
+ ],
+ },
+ ],
+ )
+ return (options?.client ?? this.client).post<McpAuthStartResponses, McpAuthStartErrors, ThrowOnError>({
+ url: "/mcp/{name}/auth",
+ ...options,
+ ...params,
+ })
+ }
+
+ /**
+ * Complete OAuth authentication with authorization code
+ */
+ public callback<ThrowOnError extends boolean = false>(
+ parameters: {
+ name: string
+ directory?: string
+ code?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams(
+ [parameters],
+ [
+ {
+ args: [
+ { in: "path", key: "name" },
+ { in: "query", key: "directory" },
+ { in: "body", key: "code" },
+ ],
+ },
+ ],
+ )
+ return (options?.client ?? this.client).post<McpAuthCallbackResponses, McpAuthCallbackErrors, ThrowOnError>({
+ url: "/mcp/{name}/auth/callback",
+ ...options,
+ ...params,
+ headers: {
+ "Content-Type": "application/json",
+ ...options?.headers,
+ ...params.headers,
+ },
+ })
+ }
+
+ /**
+ * Start OAuth flow and wait for callback (opens browser)
+ */
+ public authenticate<ThrowOnError extends boolean = false>(
+ parameters: {
+ name: string
+ directory?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams(
+ [parameters],
+ [
+ {
+ args: [
+ { in: "path", key: "name" },
+ { in: "query", key: "directory" },
+ ],
+ },
+ ],
+ )
+ return (options?.client ?? this.client).post<McpAuthAuthenticateResponses, McpAuthAuthenticateErrors, ThrowOnError>(
+ {
+ url: "/mcp/{name}/auth/authenticate",
+ ...options,
+ ...params,
+ },
+ )
+ }
+
+ /**
+ * Set authentication credentials
+ */
+ public set<ThrowOnError extends boolean = false>(
+ parameters: {
+ providerID: string
+ directory?: string
+ auth?: Auth2
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams(
+ [parameters],
+ [
+ {
+ args: [
+ { in: "path", key: "providerID" },
+ { in: "query", key: "directory" },
+ { key: "auth", map: "body" },
+ ],
+ },
+ ],
+ )
+ return (options?.client ?? this.client).put<AuthSetResponses, AuthSetErrors, ThrowOnError>({
+ url: "/auth/{providerID}",
+ ...options,
+ ...params,
+ headers: {
+ "Content-Type": "application/json",
+ ...options?.headers,
+ ...params.headers,
+ },
+ })
+ }
+}
+
+export class Mcp extends HeyApiClient {
+ /**
+ * Get MCP server status
+ */
+ public status<ThrowOnError extends boolean = false>(
+ parameters?: {
+ directory?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+ return (options?.client ?? this.client).get<McpStatusResponses, unknown, ThrowOnError>({
+ url: "/mcp",
+ ...options,
+ ...params,
+ })
+ }
+
+ /**
+ * Add MCP server dynamically
+ */
+ public add<ThrowOnError extends boolean = false>(
+ parameters?: {
+ directory?: string
+ name?: string
+ config?: McpLocalConfig | McpRemoteConfig
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams(
+ [parameters],
+ [
+ {
+ args: [
+ { in: "query", key: "directory" },
+ { in: "body", key: "name" },
+ { in: "body", key: "config" },
+ ],
+ },
+ ],
+ )
+ return (options?.client ?? this.client).post<McpAddResponses, McpAddErrors, ThrowOnError>({
+ url: "/mcp",
+ ...options,
+ ...params,
+ headers: {
+ "Content-Type": "application/json",
+ ...options?.headers,
+ ...params.headers,
+ },
+ })
+ }
+
+ auth = new Auth({ client: this.client })
+}
+
+export class Lsp extends HeyApiClient {
+ /**
+ * Get LSP server status
+ */
+ public status<ThrowOnError extends boolean = false>(
+ parameters?: {
+ directory?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+ return (options?.client ?? this.client).get<LspStatusResponses, unknown, ThrowOnError>({
+ url: "/lsp",
+ ...options,
+ ...params,
+ })
+ }
+}
+
+export class Formatter extends HeyApiClient {
+ /**
+ * Get formatter status
+ */
+ public status<ThrowOnError extends boolean = false>(
+ parameters?: {
+ directory?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+ return (options?.client ?? this.client).get<FormatterStatusResponses, unknown, ThrowOnError>({
+ url: "/formatter",
+ ...options,
+ ...params,
+ })
+ }
+}
+
+export class Control extends HeyApiClient {
+ /**
+ * Get the next TUI request from the queue
+ */
+ public next<ThrowOnError extends boolean = false>(
+ parameters?: {
+ directory?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+ return (options?.client ?? this.client).get<TuiControlNextResponses, unknown, ThrowOnError>({
+ url: "/tui/control/next",
+ ...options,
+ ...params,
+ })
+ }
+
+ /**
+ * Submit a response to the TUI request queue
+ */
+ public response<ThrowOnError extends boolean = false>(
+ parameters?: {
+ directory?: string
+ body?: unknown
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }, { in: "body" }] }])
+ return (options?.client ?? this.client).post<TuiControlResponseResponses, unknown, ThrowOnError>({
+ url: "/tui/control/response",
+ ...options,
+ ...params,
+ headers: {
+ "Content-Type": "application/json",
+ ...options?.headers,
+ ...params.headers,
+ },
+ })
+ }
+}
+
+export class Tui extends HeyApiClient {
+ /**
+ * Append prompt to the TUI
+ */
+ public appendPrompt<ThrowOnError extends boolean = false>(
+ parameters?: {
+ directory?: string
+ text?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams(
+ [parameters],
+ [
+ {
+ args: [
+ { in: "query", key: "directory" },
+ { in: "body", key: "text" },
+ ],
+ },
+ ],
+ )
+ return (options?.client ?? this.client).post<TuiAppendPromptResponses, TuiAppendPromptErrors, ThrowOnError>({
+ url: "/tui/append-prompt",
+ ...options,
+ ...params,
+ headers: {
+ "Content-Type": "application/json",
+ ...options?.headers,
+ ...params.headers,
+ },
+ })
+ }
+
+ /**
+ * Open the help dialog
+ */
+ public openHelp<ThrowOnError extends boolean = false>(
+ parameters?: {
+ directory?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+ return (options?.client ?? this.client).post<TuiOpenHelpResponses, unknown, ThrowOnError>({
+ url: "/tui/open-help",
+ ...options,
+ ...params,
+ })
+ }
+
+ /**
+ * Open the session dialog
+ */
+ public openSessions<ThrowOnError extends boolean = false>(
+ parameters?: {
+ directory?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+ return (options?.client ?? this.client).post<TuiOpenSessionsResponses, unknown, ThrowOnError>({
+ url: "/tui/open-sessions",
+ ...options,
+ ...params,
+ })
+ }
+
+ /**
+ * Open the theme dialog
+ */
+ public openThemes<ThrowOnError extends boolean = false>(
+ parameters?: {
+ directory?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+ return (options?.client ?? this.client).post<TuiOpenThemesResponses, unknown, ThrowOnError>({
+ url: "/tui/open-themes",
+ ...options,
+ ...params,
+ })
+ }
+
+ /**
+ * Open the model dialog
+ */
+ public openModels<ThrowOnError extends boolean = false>(
+ parameters?: {
+ directory?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+ return (options?.client ?? this.client).post<TuiOpenModelsResponses, unknown, ThrowOnError>({
+ url: "/tui/open-models",
+ ...options,
+ ...params,
+ })
+ }
+
+ /**
+ * Submit the prompt
+ */
+ public submitPrompt<ThrowOnError extends boolean = false>(
+ parameters?: {
+ directory?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+ return (options?.client ?? this.client).post<TuiSubmitPromptResponses, unknown, ThrowOnError>({
+ url: "/tui/submit-prompt",
+ ...options,
+ ...params,
+ })
+ }
+
+ /**
+ * Clear the prompt
+ */
+ public clearPrompt<ThrowOnError extends boolean = false>(
+ parameters?: {
+ directory?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+ return (options?.client ?? this.client).post<TuiClearPromptResponses, unknown, ThrowOnError>({
+ url: "/tui/clear-prompt",
+ ...options,
+ ...params,
+ })
+ }
+
+ /**
+ * Execute a TUI command (e.g. agent_cycle)
+ */
+ public executeCommand<ThrowOnError extends boolean = false>(
+ parameters?: {
+ directory?: string
+ command?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams(
+ [parameters],
+ [
+ {
+ args: [
+ { in: "query", key: "directory" },
+ { in: "body", key: "command" },
+ ],
+ },
+ ],
+ )
+ return (options?.client ?? this.client).post<TuiExecuteCommandResponses, TuiExecuteCommandErrors, ThrowOnError>({
+ url: "/tui/execute-command",
+ ...options,
+ ...params,
+ headers: {
+ "Content-Type": "application/json",
+ ...options?.headers,
+ ...params.headers,
+ },
+ })
+ }
+
+ /**
+ * Show a toast notification in the TUI
+ */
+ public showToast<ThrowOnError extends boolean = false>(
+ parameters?: {
+ directory?: string
+ title?: string
+ message?: string
+ variant?: "info" | "success" | "warning" | "error"
+ duration?: number
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams(
+ [parameters],
+ [
+ {
+ args: [
+ { in: "query", key: "directory" },
+ { in: "body", key: "title" },
+ { in: "body", key: "message" },
+ { in: "body", key: "variant" },
+ { in: "body", key: "duration" },
+ ],
+ },
+ ],
+ )
+ return (options?.client ?? this.client).post<TuiShowToastResponses, unknown, ThrowOnError>({
+ url: "/tui/show-toast",
+ ...options,
+ ...params,
+ headers: {
+ "Content-Type": "application/json",
+ ...options?.headers,
+ ...params.headers,
+ },
+ })
+ }
+
+ /**
+ * Publish a TUI event
+ */
+ public publish<ThrowOnError extends boolean = false>(
+ parameters?: {
+ directory?: string
+ body?: EventTuiPromptAppend | EventTuiCommandExecute | EventTuiToastShow
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }, { in: "body" }] }])
+ return (options?.client ?? this.client).post<TuiPublishResponses, TuiPublishErrors, ThrowOnError>({
+ url: "/tui/publish",
+ ...options,
+ ...params,
+ headers: {
+ "Content-Type": "application/json",
+ ...options?.headers,
+ ...params.headers,
+ },
+ })
+ }
+
+ control = new Control({ client: this.client })
+}
+
+export class Event extends HeyApiClient {
+ /**
+ * Get events
+ */
+ public subscribe<ThrowOnError extends boolean = false>(
+ parameters?: {
+ directory?: string
+ },
+ options?: Options<never, ThrowOnError>,
+ ) {
+ const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+ return (options?.client ?? this.client).sse.get<EventSubscribeResponses, unknown, ThrowOnError>({
+ url: "/event",
+ ...options,
+ ...params,
+ })
+ }
+}
+
+export class OpencodeClient extends HeyApiClient {
+ public static readonly __registry = new HeyApiRegistry<OpencodeClient>()
+
+ constructor(args?: { client?: Client; key?: string }) {
+ super(args)
+ OpencodeClient.__registry.set(this, args?.key)
+ }
+
+ global = new Global({ client: this.client })
+
+ project = new Project({ client: this.client })
+
+ pty = new Pty({ client: this.client })
+
+ config = new Config({ client: this.client })
+
+ tool = new Tool({ client: this.client })
+
+ instance = new Instance({ client: this.client })
+
+ path = new Path({ client: this.client })
+
+ vcs = new Vcs({ client: this.client })
+
+ session = new Session({ client: this.client })
+
+ permission = new Permission({ client: this.client })
+
+ command = new Command({ client: this.client })
+
+ provider = new Provider({ client: this.client })
+
+ find = new Find({ client: this.client })
+
+ file = new File({ client: this.client })
+
+ app = new App({ client: this.client })
+
+ mcp = new Mcp({ client: this.client })
+
+ lsp = new Lsp({ client: this.client })
+
+ formatter = new Formatter({ client: this.client })
+
+ tui = new Tui({ client: this.client })
+
+ auth = new Auth({ client: this.client })
+
+ event = new Event({ client: this.client })
+}
diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts
new file mode 100644
index 000000000..8bbfa40a3
--- /dev/null
+++ b/packages/sdk/js/src/v2/gen/types.gen.ts
@@ -0,0 +1,3845 @@
+// This file is auto-generated by @hey-api/openapi-ts
+
+export type ClientOptions = {
+ baseUrl: `${string}://${string}` | (string & {})
+}
+
+export type EventServerInstanceDisposed = {
+ type: "server.instance.disposed"
+ properties: {
+ directory: string
+ }
+}
+
+export type EventInstallationUpdated = {
+ type: "installation.updated"
+ properties: {
+ version: string
+ }
+}
+
+export type EventInstallationUpdateAvailable = {
+ type: "installation.update-available"
+ properties: {
+ version: string
+ }
+}
+
+export type EventLspClientDiagnostics = {
+ type: "lsp.client.diagnostics"
+ properties: {
+ serverID: string
+ path: string
+ }
+}
+
+export type EventLspUpdated = {
+ type: "lsp.updated"
+ properties: {
+ [key: string]: unknown
+ }
+}
+
+export type FileDiff = {
+ file: string
+ before: string
+ after: string
+ additions: number
+ deletions: number
+}
+
+export type UserMessage = {
+ id: string
+ sessionID: string
+ role: "user"
+ time: {
+ created: number
+ }
+ summary?: {
+ title?: string
+ body?: string
+ diffs: Array<FileDiff>
+ }
+ agent: string
+ model: {
+ providerID: string
+ modelID: string
+ }
+ system?: string
+ tools?: {
+ [key: string]: boolean
+ }
+}
+
+export type ProviderAuthError = {
+ name: "ProviderAuthError"
+ data: {
+ providerID: string
+ message: string
+ }
+}
+
+export type UnknownError = {
+ name: "UnknownError"
+ data: {
+ message: string
+ }
+}
+
+export type MessageOutputLengthError = {
+ name: "MessageOutputLengthError"
+ data: {
+ [key: string]: unknown
+ }
+}
+
+export type MessageAbortedError = {
+ name: "MessageAbortedError"
+ data: {
+ message: string
+ }
+}
+
+export type ApiError = {
+ name: "APIError"
+ data: {
+ message: string
+ statusCode?: number
+ isRetryable: boolean
+ responseHeaders?: {
+ [key: string]: string
+ }
+ responseBody?: string
+ }
+}
+
+export type AssistantMessage = {
+ id: string
+ sessionID: string
+ role: "assistant"
+ time: {
+ created: number
+ completed?: number
+ }
+ error?: ProviderAuthError | UnknownError | MessageOutputLengthError | MessageAbortedError | ApiError
+ parentID: string
+ modelID: string
+ providerID: string
+ mode: string
+ path: {
+ cwd: string
+ root: string
+ }
+ summary?: boolean
+ cost: number
+ tokens: {
+ input: number
+ output: number
+ reasoning: number
+ cache: {
+ read: number
+ write: number
+ }
+ }
+ finish?: string
+}
+
+export type Message = UserMessage | AssistantMessage
+
+export type EventMessageUpdated = {
+ type: "message.updated"
+ properties: {
+ info: Message
+ }
+}
+
+export type EventMessageRemoved = {
+ type: "message.removed"
+ properties: {
+ sessionID: string
+ messageID: string
+ }
+}
+
+export type TextPart = {
+ id: string
+ sessionID: string
+ messageID: string
+ type: "text"
+ text: string
+ synthetic?: boolean
+ ignored?: boolean
+ time?: {
+ start: number
+ end?: number
+ }
+ metadata?: {
+ [key: string]: unknown
+ }
+}
+
+export type ReasoningPart = {
+ id: string
+ sessionID: string
+ messageID: string
+ type: "reasoning"
+ text: string
+ metadata?: {
+ [key: string]: unknown
+ }
+ time: {
+ start: number
+ end?: number
+ }
+}
+
+export type FilePartSourceText = {
+ value: string
+ start: number
+ end: number
+}
+
+export type FileSource = {
+ text: FilePartSourceText
+ type: "file"
+ path: string
+}
+
+export type Range = {
+ start: {
+ line: number
+ character: number
+ }
+ end: {
+ line: number
+ character: number
+ }
+}
+
+export type SymbolSource = {
+ text: FilePartSourceText
+ type: "symbol"
+ path: string
+ range: Range
+ name: string
+ kind: number
+}
+
+export type FilePartSource = FileSource | SymbolSource
+
+export type FilePart = {
+ id: string
+ sessionID: string
+ messageID: string
+ type: "file"
+ mime: string
+ filename?: string
+ url: string
+ source?: FilePartSource
+}
+
+export type ToolStatePending = {
+ status: "pending"
+ input: {
+ [key: string]: unknown
+ }
+ raw: string
+}
+
+export type ToolStateRunning = {
+ status: "running"
+ input: {
+ [key: string]: unknown
+ }
+ title?: string
+ metadata?: {
+ [key: string]: unknown
+ }
+ time: {
+ start: number
+ }
+}
+
+export type ToolStateCompleted = {
+ status: "completed"
+ input: {
+ [key: string]: unknown
+ }
+ output: string
+ title: string
+ metadata: {
+ [key: string]: unknown
+ }
+ time: {
+ start: number
+ end: number
+ compacted?: number
+ }
+ attachments?: Array<FilePart>
+}
+
+export type ToolStateError = {
+ status: "error"
+ input: {
+ [key: string]: unknown
+ }
+ error: string
+ metadata?: {
+ [key: string]: unknown
+ }
+ time: {
+ start: number
+ end: number
+ }
+}
+
+export type ToolState = ToolStatePending | ToolStateRunning | ToolStateCompleted | ToolStateError
+
+export type ToolPart = {
+ id: string
+ sessionID: string
+ messageID: string
+ type: "tool"
+ callID: string
+ tool: string
+ state: ToolState
+ metadata?: {
+ [key: string]: unknown
+ }
+}
+
+export type StepStartPart = {
+ id: string
+ sessionID: string
+ messageID: string
+ type: "step-start"
+ snapshot?: string
+}
+
+export type StepFinishPart = {
+ id: string
+ sessionID: string
+ messageID: string
+ type: "step-finish"
+ reason: string
+ snapshot?: string
+ cost: number
+ tokens: {
+ input: number
+ output: number
+ reasoning: number
+ cache: {
+ read: number
+ write: number
+ }
+ }
+}
+
+export type SnapshotPart = {
+ id: string
+ sessionID: string
+ messageID: string
+ type: "snapshot"
+ snapshot: string
+}
+
+export type PatchPart = {
+ id: string
+ sessionID: string
+ messageID: string
+ type: "patch"
+ hash: string
+ files: Array<string>
+}
+
+export type AgentPart = {
+ id: string
+ sessionID: string
+ messageID: string
+ type: "agent"
+ name: string
+ source?: {
+ value: string
+ start: number
+ end: number
+ }
+}
+
+export type RetryPart = {
+ id: string
+ sessionID: string
+ messageID: string
+ type: "retry"
+ attempt: number
+ error: ApiError
+ time: {
+ created: number
+ }
+}
+
+export type CompactionPart = {
+ id: string
+ sessionID: string
+ messageID: string
+ type: "compaction"
+ auto: boolean
+}
+
+export type Part =
+ | TextPart
+ | {
+ id: string
+ sessionID: string
+ messageID: string
+ type: "subtask"
+ prompt: string
+ description: string
+ agent: string
+ }
+ | ReasoningPart
+ | FilePart
+ | ToolPart
+ | StepStartPart
+ | StepFinishPart
+ | SnapshotPart
+ | PatchPart
+ | AgentPart
+ | RetryPart
+ | CompactionPart
+
+export type EventMessagePartUpdated = {
+ type: "message.part.updated"
+ properties: {
+ part: Part
+ delta?: string
+ }
+}
+
+export type EventMessagePartRemoved = {
+ type: "message.part.removed"
+ properties: {
+ sessionID: string
+ messageID: string
+ partID: string
+ }
+}
+
+export type Permission = {
+ id: string
+ type: string
+ pattern?: string | Array<string>
+ sessionID: string
+ messageID: string
+ callID?: string
+ title: string
+ metadata: {
+ [key: string]: unknown
+ }
+ time: {
+ created: number
+ }
+}
+
+export type EventPermissionUpdated = {
+ type: "permission.updated"
+ properties: Permission
+}
+
+export type EventPermissionReplied = {
+ type: "permission.replied"
+ properties: {
+ sessionID: string
+ permissionID: string
+ response: string
+ }
+}
+
+export type SessionStatus =
+ | {
+ type: "idle"
+ }
+ | {
+ type: "retry"
+ attempt: number
+ message: string
+ next: number
+ }
+ | {
+ type: "busy"
+ }
+
+export type EventSessionStatus = {
+ type: "session.status"
+ properties: {
+ sessionID: string
+ status: SessionStatus
+ }
+}
+
+export type EventSessionIdle = {
+ type: "session.idle"
+ properties: {
+ sessionID: string
+ }
+}
+
+export type EventSessionCompacted = {
+ type: "session.compacted"
+ properties: {
+ sessionID: string
+ }
+}
+
+export type EventFileEdited = {
+ type: "file.edited"
+ properties: {
+ file: string
+ }
+}
+
+export type Todo = {
+ /**
+ * Brief description of the task
+ */
+ content: string
+ /**
+ * Current status of the task: pending, in_progress, completed, cancelled
+ */
+ status: string
+ /**
+ * Priority level of the task: high, medium, low
+ */
+ priority: string
+ /**
+ * Unique identifier for the todo item
+ */
+ id: string
+}
+
+export type EventTodoUpdated = {
+ type: "todo.updated"
+ properties: {
+ sessionID: string
+ todos: Array<Todo>
+ }
+}
+
+export type EventCommandExecuted = {
+ type: "command.executed"
+ properties: {
+ name: string
+ sessionID: string
+ arguments: string
+ messageID: string
+ }
+}
+
+export type Session = {
+ id: string
+ projectID: string
+ directory: string
+ parentID?: string
+ summary?: {
+ additions: number
+ deletions: number
+ files: number
+ diffs?: Array<FileDiff>
+ }
+ share?: {
+ url: string
+ }
+ title: string
+ version: string
+ time: {
+ created: number
+ updated: number
+ compacting?: number
+ }
+ revert?: {
+ messageID: string
+ partID?: string
+ snapshot?: string
+ diff?: string
+ }
+}
+
+export type EventSessionCreated = {
+ type: "session.created"
+ properties: {
+ info: Session
+ }
+}
+
+export type EventSessionUpdated = {
+ type: "session.updated"
+ properties: {
+ info: Session
+ }
+}
+
+export type EventSessionDeleted = {
+ type: "session.deleted"
+ properties: {
+ info: Session
+ }
+}
+
+export type EventSessionDiff = {
+ type: "session.diff"
+ properties: {
+ sessionID: string
+ diff: Array<FileDiff>
+ }
+}
+
+export type EventSessionError = {
+ type: "session.error"
+ properties: {
+ sessionID?: string
+ error?: ProviderAuthError | UnknownError | MessageOutputLengthError | MessageAbortedError | ApiError
+ }
+}
+
+export type EventFileWatcherUpdated = {
+ type: "file.watcher.updated"
+ properties: {
+ file: string
+ event: "add" | "change" | "unlink"
+ }
+}
+
+export type EventVcsBranchUpdated = {
+ type: "vcs.branch.updated"
+ properties: {
+ branch?: string
+ }
+}
+
+export type EventTuiPromptAppend = {
+ type: "tui.prompt.append"
+ properties: {
+ text: string
+ }
+}
+
+export type EventTuiCommandExecute = {
+ type: "tui.command.execute"
+ properties: {
+ command:
+ | "session.list"
+ | "session.new"
+ | "session.share"
+ | "session.interrupt"
+ | "session.compact"
+ | "session.page.up"
+ | "session.page.down"
+ | "session.half.page.up"
+ | "session.half.page.down"
+ | "session.first"
+ | "session.last"
+ | "prompt.clear"
+ | "prompt.submit"
+ | "agent.cycle"
+ | string
+ }
+}
+
+export type EventTuiToastShow = {
+ type: "tui.toast.show"
+ properties: {
+ title?: string
+ message: string
+ variant: "info" | "success" | "warning" | "error"
+ /**
+ * Duration in milliseconds
+ */
+ duration?: number
+ }
+}
+
+export type Pty = {
+ id: string
+ title: string
+ command: string
+ args: Array<string>
+ cwd: string
+ status: "running" | "exited"
+ pid: number
+}
+
+export type EventPtyCreated = {
+ type: "pty.created"
+ properties: {
+ info: Pty
+ }
+}
+
+export type EventPtyUpdated = {
+ type: "pty.updated"
+ properties: {
+ info: Pty
+ }
+}
+
+export type EventPtyExited = {
+ type: "pty.exited"
+ properties: {
+ id: string
+ exitCode: number
+ }
+}
+
+export type EventPtyDeleted = {
+ type: "pty.deleted"
+ properties: {
+ id: string
+ }
+}
+
+export type EventServerConnected = {
+ type: "server.connected"
+ properties: {
+ [key: string]: unknown
+ }
+}
+
+export type Event =
+ | EventServerInstanceDisposed
+ | EventInstallationUpdated
+ | EventInstallationUpdateAvailable
+ | EventLspClientDiagnostics
+ | EventLspUpdated
+ | EventMessageUpdated
+ | EventMessageRemoved
+ | EventMessagePartUpdated
+ | EventMessagePartRemoved
+ | EventPermissionUpdated
+ | EventPermissionReplied
+ | EventSessionStatus
+ | EventSessionIdle
+ | EventSessionCompacted
+ | EventFileEdited
+ | EventTodoUpdated
+ | EventCommandExecuted
+ | EventSessionCreated
+ | EventSessionUpdated
+ | EventSessionDeleted
+ | EventSessionDiff
+ | EventSessionError
+ | EventFileWatcherUpdated
+ | EventVcsBranchUpdated
+ | EventTuiPromptAppend
+ | EventTuiCommandExecute
+ | EventTuiToastShow
+ | EventPtyCreated
+ | EventPtyUpdated
+ | EventPtyExited
+ | EventPtyDeleted
+ | EventServerConnected
+
+export type GlobalEvent = {
+ directory: string
+ payload: Event
+}
+
+export type Project = {
+ id: string
+ worktree: string
+ vcsDir?: string
+ vcs?: "git"
+ time: {
+ created: number
+ initialized?: number
+ }
+}
+
+export type BadRequestError = {
+ data: unknown
+ errors: Array<{
+ [key: string]: unknown
+ }>
+ success: false
+}
+
+export type NotFoundError = {
+ name: "NotFoundError"
+ data: {
+ message: string
+ }
+}
+
+/**
+ * Custom keybind configurations
+ */
+export type KeybindsConfig = {
+ /**
+ * Leader key for keybind combinations
+ */
+ leader?: string
+ /**
+ * Exit the application
+ */
+ app_exit?: string
+ /**
+ * Open external editor
+ */
+ editor_open?: string
+ /**
+ * List available themes
+ */
+ theme_list?: string
+ /**
+ * Toggle sidebar
+ */
+ sidebar_toggle?: string
+ /**
+ * Toggle session scrollbar
+ */
+ scrollbar_toggle?: string
+ /**
+ * Toggle username visibility
+ */
+ username_toggle?: string
+ /**
+ * View status
+ */
+ status_view?: string
+ /**
+ * Export session to editor
+ */
+ session_export?: string
+ /**
+ * Create a new session
+ */
+ session_new?: string
+ /**
+ * List all sessions
+ */
+ session_list?: string
+ /**
+ * Show session timeline
+ */
+ session_timeline?: string
+ /**
+ * Share current session
+ */
+ session_share?: string
+ /**
+ * Unshare current session
+ */
+ session_unshare?: string
+ /**
+ * Interrupt current session
+ */
+ session_interrupt?: string
+ /**
+ * Compact the session
+ */
+ session_compact?: string
+ /**
+ * Scroll messages up by one page
+ */
+ messages_page_up?: string
+ /**
+ * Scroll messages down by one page
+ */
+ messages_page_down?: string
+ /**
+ * Scroll messages up by half page
+ */
+ messages_half_page_up?: string
+ /**
+ * Scroll messages down by half page
+ */
+ messages_half_page_down?: string
+ /**
+ * Navigate to first message
+ */
+ messages_first?: string
+ /**
+ * Navigate to last message
+ */
+ messages_last?: string
+ /**
+ * Navigate to last user message
+ */
+ messages_last_user?: string
+ /**
+ * Copy message
+ */
+ messages_copy?: string
+ /**
+ * Undo message
+ */
+ messages_undo?: string
+ /**
+ * Redo message
+ */
+ messages_redo?: string
+ /**
+ * Toggle code block concealment in messages
+ */
+ messages_toggle_conceal?: string
+ /**
+ * Toggle tool details visibility
+ */
+ tool_details?: string
+ /**
+ * List available models
+ */
+ model_list?: string
+ /**
+ * Next recently used model
+ */
+ model_cycle_recent?: string
+ /**
+ * Previous recently used model
+ */
+ model_cycle_recent_reverse?: string
+ /**
+ * List available commands
+ */
+ command_list?: string
+ /**
+ * List agents
+ */
+ agent_list?: string
+ /**
+ * Next agent
+ */
+ agent_cycle?: string
+ /**
+ * Previous agent
+ */
+ agent_cycle_reverse?: string
+ /**
+ * Clear input field
+ */
+ input_clear?: string
+ /**
+ * Forward delete
+ */
+ input_forward_delete?: string
+ /**
+ * Paste from clipboard
+ */
+ input_paste?: string
+ /**
+ * Submit input
+ */
+ input_submit?: string
+ /**
+ * Insert newline in input
+ */
+ input_newline?: string
+ /**
+ * Previous history item
+ */
+ history_previous?: string
+ /**
+ * Next history item
+ */
+ history_next?: string
+ /**
+ * Next child session
+ */
+ session_child_cycle?: string
+ /**
+ * Previous child session
+ */
+ session_child_cycle_reverse?: string
+ /**
+ * Suspend terminal
+ */
+ terminal_suspend?: string
+}
+
+export type AgentConfig = {
+ model?: string
+ temperature?: number
+ top_p?: number
+ prompt?: string
+ tools?: {
+ [key: string]: boolean
+ }
+ disable?: boolean
+ /**
+ * Description of when to use the agent
+ */
+ description?: string
+ mode?: "subagent" | "primary" | "all"
+ /**
+ * Hex color code for the agent (e.g., #FF5733)
+ */
+ color?: string
+ /**
+ * Maximum number of agentic iterations before forcing text-only response
+ */
+ maxSteps?: number
+ permission?: {
+ edit?: "ask" | "allow" | "deny"
+ bash?:
+ | "ask"
+ | "allow"
+ | "deny"
+ | {
+ [key: string]: "ask" | "allow" | "deny"
+ }
+ webfetch?: "ask" | "allow" | "deny"
+ doom_loop?: "ask" | "allow" | "deny"
+ external_directory?: "ask" | "allow" | "deny"
+ }
+ [key: string]:
+ | unknown
+ | string
+ | number
+ | {
+ [key: string]: boolean
+ }
+ | boolean
+ | "subagent"
+ | "primary"
+ | "all"
+ | string
+ | number
+ | {
+ edit?: "ask" | "allow" | "deny"
+ bash?:
+ | "ask"
+ | "allow"
+ | "deny"
+ | {
+ [key: string]: "ask" | "allow" | "deny"
+ }
+ webfetch?: "ask" | "allow" | "deny"
+ doom_loop?: "ask" | "allow" | "deny"
+ external_directory?: "ask" | "allow" | "deny"
+ }
+ | undefined
+}
+
+export type ProviderConfig = {
+ api?: string
+ name?: string
+ env?: Array<string>
+ id?: string
+ npm?: string
+ models?: {
+ [key: string]: {
+ id?: string
+ name?: string
+ release_date?: string
+ attachment?: boolean
+ reasoning?: boolean
+ temperature?: boolean
+ tool_call?: boolean
+ cost?: {
+ input: number
+ output: number
+ cache_read?: number
+ cache_write?: number
+ context_over_200k?: {
+ input: number
+ output: number
+ cache_read?: number
+ cache_write?: number
+ }
+ }
+ limit?: {
+ context: number
+ output: number
+ }
+ modalities?: {
+ input: Array<"text" | "audio" | "image" | "video" | "pdf">
+ output: Array<"text" | "audio" | "image" | "video" | "pdf">
+ }
+ experimental?: boolean
+ status?: "alpha" | "beta" | "deprecated"
+ options?: {
+ [key: string]: unknown
+ }
+ headers?: {
+ [key: string]: string
+ }
+ provider?: {
+ npm: string
+ }
+ }
+ }
+ whitelist?: Array<string>
+ blacklist?: Array<string>
+ options?: {
+ apiKey?: string
+ baseURL?: string
+ /**
+ * GitHub Enterprise URL for copilot authentication
+ */
+ enterpriseUrl?: string
+ /**
+ * Enable promptCacheKey for this provider (default false)
+ */
+ setCacheKey?: boolean
+ /**
+ * Timeout in milliseconds for requests to this provider. Default is 300000 (5 minutes). Set to false to disable timeout.
+ */
+ timeout?: number | false
+ [key: string]: unknown | string | boolean | number | false | undefined
+ }
+}
+
+export type McpLocalConfig = {
+ /**
+ * Type of MCP server connection
+ */
+ type: "local"
+ /**
+ * Command and arguments to run the MCP server
+ */
+ command: Array<string>
+ /**
+ * Environment variables to set when running the MCP server
+ */
+ environment?: {
+ [key: string]: string
+ }
+ /**
+ * Enable or disable the MCP server on startup
+ */
+ enabled?: boolean
+ /**
+ * Timeout in ms for fetching tools from the MCP server. Defaults to 5000 (5 seconds) if not specified.
+ */
+ timeout?: number
+}
+
+export type McpOAuthConfig = {
+ /**
+ * OAuth client ID. If not provided, dynamic client registration (RFC 7591) will be attempted.
+ */
+ clientId?: string
+ /**
+ * OAuth client secret (if required by the authorization server)
+ */
+ clientSecret?: string
+ /**
+ * OAuth scopes to request during authorization
+ */
+ scope?: string
+}
+
+export type McpRemoteConfig = {
+ /**
+ * Type of MCP server connection
+ */
+ type: "remote"
+ /**
+ * URL of the remote MCP server
+ */
+ url: string
+ /**
+ * Enable or disable the MCP server on startup
+ */
+ enabled?: boolean
+ /**
+ * Headers to send with the request
+ */
+ headers?: {
+ [key: string]: string
+ }
+ /**
+ * OAuth authentication configuration for the MCP server. Set to false to disable OAuth auto-detection.
+ */
+ oauth?: McpOAuthConfig | false
+ /**
+ * Timeout in ms for fetching tools from the MCP server. Defaults to 5000 (5 seconds) if not specified.
+ */
+ timeout?: number
+}
+
+/**
+ * @deprecated Always uses stretch layout.
+ */
+export type LayoutConfig = "auto" | "stretch"
+
+export type Config = {
+ /**
+ * JSON schema reference for configuration validation
+ */
+ $schema?: string
+ /**
+ * Theme name to use for the interface
+ */
+ theme?: string
+ keybinds?: KeybindsConfig
+ /**
+ * TUI specific settings
+ */
+ tui?: {
+ /**
+ * TUI scroll speed
+ */
+ scroll_speed?: number
+ /**
+ * Scroll acceleration settings
+ */
+ scroll_acceleration?: {
+ /**
+ * Enable scroll acceleration
+ */
+ enabled: boolean
+ }
+ /**
+ * Control diff rendering style: 'auto' adapts to terminal width, 'stacked' always shows single column
+ */
+ diff_style?: "auto" | "stacked"
+ }
+ /**
+ * Command configuration, see https://opencode.ai/docs/commands
+ */
+ command?: {
+ [key: string]: {
+ template: string
+ description?: string
+ agent?: string
+ model?: string
+ subtask?: boolean
+ }
+ }
+ watcher?: {
+ ignore?: Array<string>
+ }
+ plugin?: Array<string>
+ snapshot?: boolean
+ /**
+ * Control sharing behavior:'manual' allows manual sharing via commands, 'auto' enables automatic sharing, 'disabled' disables all sharing
+ */
+ share?: "manual" | "auto" | "disabled"
+ /**
+ * @deprecated Use 'share' field instead. Share newly created sessions automatically
+ */
+ autoshare?: boolean
+ /**
+ * Automatically update to the latest version. Set to true to auto-update, false to disable, or 'notify' to show update notifications
+ */
+ autoupdate?: boolean | "notify"
+ /**
+ * Disable providers that are loaded automatically
+ */
+ disabled_providers?: Array<string>
+ /**
+ * When set, ONLY these providers will be enabled. All other providers will be ignored
+ */
+ enabled_providers?: Array<string>
+ /**
+ * Model to use in the format of provider/model, eg anthropic/claude-2
+ */
+ model?: string
+ /**
+ * Small model to use for tasks like title generation in the format of provider/model
+ */
+ small_model?: string
+ /**
+ * Custom username to display in conversations instead of system username
+ */
+ username?: string
+ /**
+ * @deprecated Use `agent` field instead.
+ */
+ mode?: {
+ build?: AgentConfig
+ plan?: AgentConfig
+ [key: string]: AgentConfig | undefined
+ }
+ /**
+ * Agent configuration, see https://opencode.ai/docs/agent
+ */
+ agent?: {
+ plan?: AgentConfig
+ build?: AgentConfig
+ general?: AgentConfig
+ explore?: AgentConfig
+ [key: string]: AgentConfig | undefined
+ }
+ /**
+ * Custom provider configurations and model overrides
+ */
+ provider?: {
+ [key: string]: ProviderConfig
+ }
+ /**
+ * MCP (Model Context Protocol) server configurations
+ */
+ mcp?: {
+ [key: string]: McpLocalConfig | McpRemoteConfig
+ }
+ formatter?:
+ | false
+ | {
+ [key: string]: {
+ disabled?: boolean
+ command?: Array<string>
+ environment?: {
+ [key: string]: string
+ }
+ extensions?: Array<string>
+ }
+ }
+ lsp?:
+ | false
+ | {
+ [key: string]:
+ | {
+ disabled: true
+ }
+ | {
+ command: Array<string>
+ extensions?: Array<string>
+ disabled?: boolean
+ env?: {
+ [key: string]: string
+ }
+ initialization?: {
+ [key: string]: unknown
+ }
+ }
+ }
+ /**
+ * Additional instruction files or patterns to include
+ */
+ instructions?: Array<string>
+ layout?: LayoutConfig
+ permission?: {
+ edit?: "ask" | "allow" | "deny"
+ bash?:
+ | "ask"
+ | "allow"
+ | "deny"
+ | {
+ [key: string]: "ask" | "allow" | "deny"
+ }
+ webfetch?: "ask" | "allow" | "deny"
+ doom_loop?: "ask" | "allow" | "deny"
+ external_directory?: "ask" | "allow" | "deny"
+ }
+ tools?: {
+ [key: string]: boolean
+ }
+ enterprise?: {
+ /**
+ * Enterprise URL
+ */
+ url?: string
+ }
+ experimental?: {
+ hook?: {
+ file_edited?: {
+ [key: string]: Array<{
+ command: Array<string>
+ environment?: {
+ [key: string]: string
+ }
+ }>
+ }
+ session_completed?: Array<{
+ command: Array<string>
+ environment?: {
+ [key: string]: string
+ }
+ }>
+ }
+ /**
+ * Number of retries for chat completions on failure
+ */
+ chatMaxRetries?: number
+ disable_paste_summary?: boolean
+ /**
+ * Enable the batch tool
+ */
+ batch_tool?: boolean
+ /**
+ * Enable OpenTelemetry spans for AI SDK calls (using the 'experimental_telemetry' flag)
+ */
+ openTelemetry?: boolean
+ /**
+ * Tools that should only be available to primary agents.
+ */
+ primary_tools?: Array<string>
+ }
+}
+
+export type ToolIds = Array<string>
+
+export type ToolListItem = {
+ id: string
+ description: string
+ parameters: unknown
+}
+
+export type ToolList = Array<ToolListItem>
+
+export type Path = {
+ state: string
+ config: string
+ worktree: string
+ directory: string
+}
+
+export type VcsInfo = {
+ branch: string
+}
+
+export type TextPartInput = {
+ id?: string
+ type: "text"
+ text: string
+ synthetic?: boolean
+ ignored?: boolean
+ time?: {
+ start: number
+ end?: number
+ }
+ metadata?: {
+ [key: string]: unknown
+ }
+}
+
+export type FilePartInput = {
+ id?: string
+ type: "file"
+ mime: string
+ filename?: string
+ url: string
+ source?: FilePartSource
+}
+
+export type AgentPartInput = {
+ id?: string
+ type: "agent"
+ name: string
+ source?: {
+ value: string
+ start: number
+ end: number
+ }
+}
+
+export type SubtaskPartInput = {
+ id?: string
+ type: "subtask"
+ prompt: string
+ description: string
+ agent: string
+}
+
+export type Command = {
+ name: string
+ description?: string
+ agent?: string
+ model?: string
+ template: string
+ subtask?: boolean
+}
+
+export type Model = {
+ id: string
+ providerID: string
+ api: {
+ id: string
+ url: string
+ npm: string
+ }
+ name: string
+ capabilities: {
+ temperature: boolean
+ reasoning: boolean
+ attachment: boolean
+ toolcall: boolean
+ input: {
+ text: boolean
+ audio: boolean
+ image: boolean
+ video: boolean
+ pdf: boolean
+ }
+ output: {
+ text: boolean
+ audio: boolean
+ image: boolean
+ video: boolean
+ pdf: boolean
+ }
+ }
+ cost: {
+ input: number
+ output: number
+ cache: {
+ read: number
+ write: number
+ }
+ experimentalOver200K?: {
+ input: number
+ output: number
+ cache: {
+ read: number
+ write: number
+ }
+ }
+ }
+ limit: {
+ context: number
+ output: number
+ }
+ status: "alpha" | "beta" | "deprecated" | "active"
+ options: {
+ [key: string]: unknown
+ }
+ headers: {
+ [key: string]: string
+ }
+}
+
+export type Provider = {
+ id: string
+ name: string
+ source: "env" | "config" | "custom" | "api"
+ env: Array<string>
+ key?: string
+ options: {
+ [key: string]: unknown
+ }
+ models: {
+ [key: string]: Model
+ }
+}
+
+export type ProviderAuthMethod = {
+ type: "oauth" | "api"
+ label: string
+}
+
+export type ProviderAuthAuthorization = {
+ url: string
+ method: "auto" | "code"
+ instructions: string
+}
+
+export type Symbol = {
+ name: string
+ kind: number
+ location: {
+ uri: string
+ range: Range
+ }
+}
+
+export type FileNode = {
+ name: string
+ path: string
+ absolute: string
+ type: "file" | "directory"
+ ignored: boolean
+}
+
+export type FileContent = {
+ type: "text"
+ content: string
+ diff?: string
+ patch?: {
+ oldFileName: string
+ newFileName: string
+ oldHeader?: string
+ newHeader?: string
+ hunks: Array<{
+ oldStart: number
+ oldLines: number
+ newStart: number
+ newLines: number
+ lines: Array<string>
+ }>
+ index?: string
+ }
+ encoding?: "base64"
+ mimeType?: string
+}
+
+export type File = {
+ path: string
+ added: number
+ removed: number
+ status: "added" | "deleted" | "modified"
+}
+
+export type Agent = {
+ name: string
+ description?: string
+ mode: "subagent" | "primary" | "all"
+ builtIn: boolean
+ topP?: number
+ temperature?: number
+ color?: string
+ permission: {
+ edit: "ask" | "allow" | "deny"
+ bash: {
+ [key: string]: "ask" | "allow" | "deny"
+ }
+ webfetch?: "ask" | "allow" | "deny"
+ doom_loop?: "ask" | "allow" | "deny"
+ external_directory?: "ask" | "allow" | "deny"
+ }
+ model?: {
+ modelID: string
+ providerID: string
+ }
+ prompt?: string
+ tools: {
+ [key: string]: boolean
+ }
+ options: {
+ [key: string]: unknown
+ }
+ maxSteps?: number
+}
+
+export type McpStatusConnected = {
+ status: "connected"
+}
+
+export type McpStatusDisabled = {
+ status: "disabled"
+}
+
+export type McpStatusFailed = {
+ status: "failed"
+ error: string
+}
+
+export type McpStatusNeedsAuth = {
+ status: "needs_auth"
+}
+
+export type McpStatusNeedsClientRegistration = {
+ status: "needs_client_registration"
+ error: string
+}
+
+export type McpStatus =
+ | McpStatusConnected
+ | McpStatusDisabled
+ | McpStatusFailed
+ | McpStatusNeedsAuth
+ | McpStatusNeedsClientRegistration
+
+export type LspStatus = {
+ id: string
+ name: string
+ root: string
+ status: "connected" | "error"
+}
+
+export type FormatterStatus = {
+ name: string
+ extensions: Array<string>
+ enabled: boolean
+}
+
+export type OAuth = {
+ type: "oauth"
+ refresh: string
+ access: string
+ expires: number
+ enterpriseUrl?: string
+}
+
+export type ApiAuth = {
+ type: "api"
+ key: string
+}
+
+export type WellKnownAuth = {
+ type: "wellknown"
+ key: string
+ token: string
+}
+
+export type Auth = OAuth | ApiAuth | WellKnownAuth
+
+export type GlobalEventData = {
+ body?: never
+ path?: never
+ query?: never
+ url: "/global/event"
+}
+
+export type GlobalEventResponses = {
+ /**
+ * Event stream
+ */
+ 200: GlobalEvent
+}
+
+export type GlobalEventResponse = GlobalEventResponses[keyof GlobalEventResponses]
+
+export type ProjectListData = {
+ body?: never
+ path?: never
+ query?: {
+ directory?: string
+ }
+ url: "/project"
+}
+
+export type ProjectListResponses = {
+ /**
+ * List of projects
+ */
+ 200: Array<Project>
+}
+
+export type ProjectListResponse = ProjectListResponses[keyof ProjectListResponses]
+
+export type ProjectCurrentData = {
+ body?: never
+ path?: never
+ query?: {
+ directory?: string
+ }
+ url: "/project/current"
+}
+
+export type ProjectCurrentResponses = {
+ /**
+ * Current project
+ */
+ 200: Project
+}
+
+export type ProjectCurrentResponse = ProjectCurrentResponses[keyof ProjectCurrentResponses]
+
+export type PtyListData = {
+ body?: never
+ path?: never
+ query?: {
+ directory?: string
+ }
+ url: "/pty"
+}
+
+export type PtyListResponses = {
+ /**
+ * List of sessions
+ */
+ 200: Array<Pty>
+}
+
+export type PtyListResponse = PtyListResponses[keyof PtyListResponses]
+
+export type PtyCreateData = {
+ body?: {
+ command?: string
+ args?: Array<string>
+ cwd?: string
+ title?: string
+ env?: {
+ [key: string]: string
+ }
+ }
+ path?: never
+ query?: {
+ directory?: string
+ }
+ url: "/pty"
+}
+
+export type PtyCreateErrors = {
+ /**
+ * Bad request
+ */
+ 400: BadRequestError
+}
+
+export type PtyCreateError = PtyCreateErrors[keyof PtyCreateErrors]
+
+export type PtyCreateResponses = {
+ /**
+ * Created session
+ */
+ 200: Pty
+}
+
+export type PtyCreateResponse = PtyCreateResponses[keyof PtyCreateResponses]
+
+export type PtyRemoveData = {
+ body?: never
+ path: {
+ ptyID: string
+ }
+ query?: {
+ directory?: string
+ }
+ url: "/pty/{ptyID}"
+}
+
+export type PtyRemoveErrors = {
+ /**
+ * Not found
+ */
+ 404: NotFoundError
+}
+
+export type PtyRemoveError = PtyRemoveErrors[keyof PtyRemoveErrors]
+
+export type PtyRemoveResponses = {
+ /**
+ * Session removed
+ */
+ 200: boolean
+}
+
+export type PtyRemoveResponse = PtyRemoveResponses[keyof PtyRemoveResponses]
+
+export type PtyGetData = {
+ body?: never
+ path: {
+ ptyID: string
+ }
+ query?: {
+ directory?: string
+ }
+ url: "/pty/{ptyID}"
+}
+
+export type PtyGetErrors = {
+ /**
+ * Not found
+ */
+ 404: NotFoundError
+}
+
+export type PtyGetError = PtyGetErrors[keyof PtyGetErrors]
+
+export type PtyGetResponses = {
+ /**
+ * Session info
+ */
+ 200: Pty
+}
+
+export type PtyGetResponse = PtyGetResponses[keyof PtyGetResponses]
+
+export type PtyUpdateData = {
+ body?: {
+ title?: string
+ size?: {
+ rows: number
+ cols: number
+ }
+ }
+ path: {
+ ptyID: string
+ }
+ query?: {
+ directory?: string
+ }
+ url: "/pty/{ptyID}"
+}
+
+export type PtyUpdateErrors = {
+ /**
+ * Bad request
+ */
+ 400: BadRequestError
+}
+
+export type PtyUpdateError = PtyUpdateErrors[keyof PtyUpdateErrors]
+
+export type PtyUpdateResponses = {
+ /**
+ * Updated session
+ */
+ 200: Pty
+}
+
+export type PtyUpdateResponse = PtyUpdateResponses[keyof PtyUpdateResponses]
+
+export type PtyConnectData = {
+ body?: never
+ path: {
+ ptyID: string
+ }
+ query?: {
+ directory?: string
+ }
+ url: "/pty/{ptyID}/connect"
+}
+
+export type PtyConnectErrors = {
+ /**
+ * Not found
+ */
+ 404: NotFoundError
+}
+
+export type PtyConnectError = PtyConnectErrors[keyof PtyConnectErrors]
+
+export type PtyConnectResponses = {
+ /**
+ * Connected session
+ */
+ 200: boolean
+}
+
+export type PtyConnectResponse = PtyConnectResponses[keyof PtyConnectResponses]
+
+export type ConfigGetData = {
+ body?: never
+ path?: never
+ query?: {
+ directory?: string
+ }
+ url: "/config"
+}
+
+export type ConfigGetResponses = {
+ /**
+ * Get config info
+ */
+ 200: Config
+}
+
+export type ConfigGetResponse = ConfigGetResponses[keyof ConfigGetResponses]
+
+export type ConfigUpdateData = {
+ body?: Config
+ path?: never
+ query?: {
+ directory?: string
+ }
+ url: "/config"
+}
+
+export type ConfigUpdateErrors = {
+ /**
+ * Bad request
+ */
+ 400: BadRequestError
+}
+
+export type ConfigUpdateError = ConfigUpdateErrors[keyof ConfigUpdateErrors]
+
+export type ConfigUpdateResponses = {
+ /**
+ * Successfully updated config
+ */
+ 200: Config
+}
+
+export type ConfigUpdateResponse = ConfigUpdateResponses[keyof ConfigUpdateResponses]
+
+export type ToolIdsData = {
+ body?: never
+ path?: never
+ query?: {
+ directory?: string
+ }
+ url: "/experimental/tool/ids"
+}
+
+export type ToolIdsErrors = {
+ /**
+ * Bad request
+ */
+ 400: BadRequestError
+}
+
+export type ToolIdsError = ToolIdsErrors[keyof ToolIdsErrors]
+
+export type ToolIdsResponses = {
+ /**
+ * Tool IDs
+ */
+ 200: ToolIds
+}
+
+export type ToolIdsResponse = ToolIdsResponses[keyof ToolIdsResponses]
+
+export type ToolListData = {
+ body?: never
+ path?: never
+ query: {
+ directory?: string
+ provider: string
+ model: string
+ }
+ url: "/experimental/tool"
+}
+
+export type ToolListErrors = {
+ /**
+ * Bad request
+ */
+ 400: BadRequestError
+}
+
+export type ToolListError = ToolListErrors[keyof ToolListErrors]
+
+export type ToolListResponses = {
+ /**
+ * Tools
+ */
+ 200: ToolList
+}
+
+export type ToolListResponse = ToolListResponses[keyof ToolListResponses]
+
+export type InstanceDisposeData = {
+ body?: never
+ path?: never
+ query?: {
+ directory?: string
+ }
+ url: "/instance/dispose"
+}
+
+export type InstanceDisposeResponses = {
+ /**
+ * Instance disposed
+ */
+ 200: boolean
+}
+
+export type InstanceDisposeResponse = InstanceDisposeResponses[keyof InstanceDisposeResponses]
+
+export type PathGetData = {
+ body?: never
+ path?: never
+ query?: {
+ directory?: string
+ }
+ url: "/path"
+}
+
+export type PathGetResponses = {
+ /**
+ * Path
+ */
+ 200: Path
+}
+
+export type PathGetResponse = PathGetResponses[keyof PathGetResponses]
+
+export type VcsGetData = {
+ body?: never
+ path?: never
+ query?: {
+ directory?: string
+ }
+ url: "/vcs"
+}
+
+export type VcsGetResponses = {
+ /**
+ * VCS info
+ */
+ 200: VcsInfo
+}
+
+export type VcsGetResponse = VcsGetResponses[keyof VcsGetResponses]
+
+export type SessionListData = {
+ body?: never
+ path?: never
+ query?: {
+ directory?: string
+ }
+ url: "/session"
+}
+
+export type SessionListResponses = {
+ /**
+ * List of sessions
+ */
+ 200: Array<Session>
+}
+
+export type SessionListResponse = SessionListResponses[keyof SessionListResponses]
+
+export type SessionCreateData = {
+ body?: {
+ parentID?: string
+ title?: string
+ }
+ path?: never
+ query?: {
+ directory?: string
+ }
+ url: "/session"
+}
+
+export type SessionCreateErrors = {
+ /**
+ * Bad request
+ */
+ 400: BadRequestError
+}
+
+export type SessionCreateError = SessionCreateErrors[keyof SessionCreateErrors]
+
+export type SessionCreateResponses = {
+ /**
+ * Successfully created session
+ */
+ 200: Session
+}
+
+export type SessionCreateResponse = SessionCreateResponses[keyof SessionCreateResponses]
+
+export type SessionStatusData = {
+ body?: never
+ path?: never
+ query?: {
+ directory?: string
+ }
+ url: "/session/status"
+}
+
+export type SessionStatusErrors = {
+ /**
+ * Bad request
+ */
+ 400: BadRequestError
+}
+
+export type SessionStatusError = SessionStatusErrors[keyof SessionStatusErrors]
+
+export type SessionStatusResponses = {
+ /**
+ * Get session status
+ */
+ 200: {
+ [key: string]: SessionStatus
+ }
+}
+
+export type SessionStatusResponse = SessionStatusResponses[keyof SessionStatusResponses]
+
+export type SessionDeleteData = {
+ body?: never
+ path: {
+ sessionID: string
+ }
+ query?: {
+ directory?: string
+ }
+ url: "/session/{sessionID}"
+}
+
+export type SessionDeleteErrors = {
+ /**
+ * Bad request
+ */
+ 400: BadRequestError
+ /**
+ * Not found
+ */
+ 404: NotFoundError
+}
+
+export type SessionDeleteError = SessionDeleteErrors[keyof SessionDeleteErrors]
+
+export type SessionDeleteResponses = {
+ /**
+ * Successfully deleted session
+ */
+ 200: boolean
+}
+
+export type SessionDeleteResponse = SessionDeleteResponses[keyof SessionDeleteResponses]
+
+export type SessionGetData = {
+ body?: never
+ path: {
+ sessionID: string
+ }
+ query?: {
+ directory?: string
+ }
+ url: "/session/{sessionID}"
+}
+
+export type SessionGetErrors = {
+ /**
+ * Bad request
+ */
+ 400: BadRequestError
+ /**
+ * Not found
+ */
+ 404: NotFoundError
+}
+
+export type SessionGetError = SessionGetErrors[keyof SessionGetErrors]
+
+export type SessionGetResponses = {
+ /**
+ * Get session
+ */
+ 200: Session
+}
+
+export type SessionGetResponse = SessionGetResponses[keyof SessionGetResponses]
+
+export type SessionUpdateData = {
+ body?: {
+ title?: string
+ }
+ path: {
+ sessionID: string
+ }
+ query?: {
+ directory?: string
+ }
+ url: "/session/{sessionID}"
+}
+
+export type SessionUpdateErrors = {
+ /**
+ * Bad request
+ */
+ 400: BadRequestError
+ /**
+ * Not found
+ */
+ 404: NotFoundError
+}
+
+export type SessionUpdateError = SessionUpdateErrors[keyof SessionUpdateErrors]
+
+export type SessionUpdateResponses = {
+ /**
+ * Successfully updated session
+ */
+ 200: Session
+}
+
+export type SessionUpdateResponse = SessionUpdateResponses[keyof SessionUpdateResponses]
+
+export type SessionChildrenData = {
+ body?: never
+ path: {
+ sessionID: string
+ }
+ query?: {
+ directory?: string
+ }
+ url: "/session/{sessionID}/children"
+}
+
+export type SessionChildrenErrors = {
+ /**
+ * Bad request
+ */
+ 400: BadRequestError
+ /**
+ * Not found
+ */
+ 404: NotFoundError
+}
+
+export type SessionChildrenError = SessionChildrenErrors[keyof SessionChildrenErrors]
+
+export type SessionChildrenResponses = {
+ /**
+ * List of children
+ */
+ 200: Array<Session>
+}
+
+export type SessionChildrenResponse = SessionChildrenResponses[keyof SessionChildrenResponses]
+
+export type SessionTodoData = {
+ body?: never
+ path: {
+ /**
+ * Session ID
+ */
+ sessionID: string
+ }
+ query?: {
+ directory?: string
+ }
+ url: "/session/{sessionID}/todo"
+}
+
+export type SessionTodoErrors = {
+ /**
+ * Bad request
+ */
+ 400: BadRequestError
+ /**
+ * Not found
+ */
+ 404: NotFoundError
+}
+
+export type SessionTodoError = SessionTodoErrors[keyof SessionTodoErrors]
+
+export type SessionTodoResponses = {
+ /**
+ * Todo list
+ */
+ 200: Array<Todo>
+}
+
+export type SessionTodoResponse = SessionTodoResponses[keyof SessionTodoResponses]
+
+export type SessionInitData = {
+ body?: {
+ modelID: string
+ providerID: string
+ messageID: string
+ }
+ path: {
+ /**
+ * Session ID
+ */
+ sessionID: string
+ }
+ query?: {
+ directory?: string
+ }
+ url: "/session/{sessionID}/init"
+}
+
+export type SessionInitErrors = {
+ /**
+ * Bad request
+ */
+ 400: BadRequestError
+ /**
+ * Not found
+ */
+ 404: NotFoundError
+}
+
+export type SessionInitError = SessionInitErrors[keyof SessionInitErrors]
+
+export type SessionInitResponses = {
+ /**
+ * 200
+ */
+ 200: boolean
+}
+
+export type SessionInitResponse = SessionInitResponses[keyof SessionInitResponses]
+
+export type SessionForkData = {
+ body?: {
+ messageID?: string
+ }
+ path: {
+ sessionID: string
+ }
+ query?: {
+ directory?: string
+ }
+ url: "/session/{sessionID}/fork"
+}
+
+export type SessionForkResponses = {
+ /**
+ * 200
+ */
+ 200: Session
+}
+
+export type SessionForkResponse = SessionForkResponses[keyof SessionForkResponses]
+
+export type SessionAbortData = {
+ body?: never
+ path: {
+ sessionID: string
+ }
+ query?: {
+ directory?: string
+ }
+ url: "/session/{sessionID}/abort"
+}
+
+export type SessionAbortErrors = {
+ /**
+ * Bad request
+ */
+ 400: BadRequestError
+ /**
+ * Not found
+ */
+ 404: NotFoundError
+}
+
+export type SessionAbortError = SessionAbortErrors[keyof SessionAbortErrors]
+
+export type SessionAbortResponses = {
+ /**
+ * Aborted session
+ */
+ 200: boolean
+}
+
+export type SessionAbortResponse = SessionAbortResponses[keyof SessionAbortResponses]
+
+export type SessionUnshareData = {
+ body?: never
+ path: {
+ sessionID: string
+ }
+ query?: {
+ directory?: string
+ }
+ url: "/session/{sessionID}/share"
+}
+
+export type SessionUnshareErrors = {
+ /**
+ * Bad request
+ */
+ 400: BadRequestError
+ /**
+ * Not found
+ */
+ 404: NotFoundError
+}
+
+export type SessionUnshareError = SessionUnshareErrors[keyof SessionUnshareErrors]
+
+export type SessionUnshareResponses = {
+ /**
+ * Successfully unshared session
+ */
+ 200: Session
+}
+
+export type SessionUnshareResponse = SessionUnshareResponses[keyof SessionUnshareResponses]
+
+export type SessionShareData = {
+ body?: never
+ path: {
+ sessionID: string
+ }
+ query?: {
+ directory?: string
+ }
+ url: "/session/{sessionID}/share"
+}
+
+export type SessionShareErrors = {
+ /**
+ * Bad request
+ */
+ 400: BadRequestError
+ /**
+ * Not found
+ */
+ 404: NotFoundError
+}
+
+export type SessionShareError = SessionShareErrors[keyof SessionShareErrors]
+
+export type SessionShareResponses = {
+ /**
+ * Successfully shared session
+ */
+ 200: Session
+}
+
+export type SessionShareResponse = SessionShareResponses[keyof SessionShareResponses]
+
+export type SessionDiffData = {
+ body?: never
+ path: {
+ /**
+ * Session ID
+ */
+ sessionID: string
+ }
+ query?: {
+ directory?: string
+ messageID?: string
+ }
+ url: "/session/{sessionID}/diff"
+}
+
+export type SessionDiffErrors = {
+ /**
+ * Bad request
+ */
+ 400: BadRequestError
+ /**
+ * Not found
+ */
+ 404: NotFoundError
+}
+
+export type SessionDiffError = SessionDiffErrors[keyof SessionDiffErrors]
+
+export type SessionDiffResponses = {
+ /**
+ * List of diffs
+ */
+ 200: Array<FileDiff>
+}
+
+export type SessionDiffResponse = SessionDiffResponses[keyof SessionDiffResponses]
+
+export type SessionSummarizeData = {
+ body?: {
+ providerID: string
+ modelID: string
+ }
+ path: {
+ /**
+ * Session ID
+ */
+ sessionID: string
+ }
+ query?: {
+ directory?: string
+ }
+ url: "/session/{sessionID}/summarize"
+}
+
+export type SessionSummarizeErrors = {
+ /**
+ * Bad request
+ */
+ 400: BadRequestError
+ /**
+ * Not found
+ */
+ 404: NotFoundError
+}
+
+export type SessionSummarizeError = SessionSummarizeErrors[keyof SessionSummarizeErrors]
+
+export type SessionSummarizeResponses = {
+ /**
+ * Summarized session
+ */
+ 200: boolean
+}
+
+export type SessionSummarizeResponse = SessionSummarizeResponses[keyof SessionSummarizeResponses]
+
+export type SessionMessagesData = {
+ body?: never
+ path: {
+ /**
+ * Session ID
+ */
+ sessionID: string
+ }
+ query?: {
+ directory?: string
+ limit?: number
+ }
+ url: "/session/{sessionID}/message"
+}
+
+export type SessionMessagesErrors = {
+ /**
+ * Bad request
+ */
+ 400: BadRequestError
+ /**
+ * Not found
+ */
+ 404: NotFoundError
+}
+
+export type SessionMessagesError = SessionMessagesErrors[keyof SessionMessagesErrors]
+
+export type SessionMessagesResponses = {
+ /**
+ * List of messages
+ */
+ 200: Array<{
+ info: Message
+ parts: Array<Part>
+ }>
+}
+
+export type SessionMessagesResponse = SessionMessagesResponses[keyof SessionMessagesResponses]
+
+export type SessionPromptData = {
+ body?: {
+ messageID?: string
+ model?: {
+ providerID: string
+ modelID: string
+ }
+ agent?: string
+ noReply?: boolean
+ system?: string
+ tools?: {
+ [key: string]: boolean
+ }
+ parts: Array<TextPartInput | FilePartInput | AgentPartInput | SubtaskPartInput>
+ }
+ path: {
+ /**
+ * Session ID
+ */
+ sessionID: string
+ }
+ query?: {
+ directory?: string
+ }
+ url: "/session/{sessionID}/message"
+}
+
+export type SessionPromptErrors = {
+ /**
+ * Bad request
+ */
+ 400: BadRequestError
+ /**
+ * Not found
+ */
+ 404: NotFoundError
+}
+
+export type SessionPromptError = SessionPromptErrors[keyof SessionPromptErrors]
+
+export type SessionPromptResponses = {
+ /**
+ * Created message
+ */
+ 200: {
+ info: AssistantMessage
+ parts: Array<Part>
+ }
+}
+
+export type SessionPromptResponse = SessionPromptResponses[keyof SessionPromptResponses]
+
+export type SessionMessageData = {
+ body?: never
+ path: {
+ /**
+ * Session ID
+ */
+ sessionID: string
+ /**
+ * Message ID
+ */
+ messageID: string
+ }
+ query?: {
+ directory?: string
+ }
+ url: "/session/{sessionID}/message/{messageID}"
+}
+
+export type SessionMessageErrors = {
+ /**
+ * Bad request
+ */
+ 400: BadRequestError
+ /**
+ * Not found
+ */
+ 404: NotFoundError
+}
+
+export type SessionMessageError = SessionMessageErrors[keyof SessionMessageErrors]
+
+export type SessionMessageResponses = {
+ /**
+ * Message
+ */
+ 200: {
+ info: Message
+ parts: Array<Part>
+ }
+}
+
+export type SessionMessageResponse = SessionMessageResponses[keyof SessionMessageResponses]
+
+export type SessionPromptAsyncData = {
+ body?: {
+ messageID?: string
+ model?: {
+ providerID: string
+ modelID: string
+ }
+ agent?: string
+ noReply?: boolean
+ system?: string
+ tools?: {
+ [key: string]: boolean
+ }
+ parts: Array<TextPartInput | FilePartInput | AgentPartInput | SubtaskPartInput>
+ }
+ path: {
+ /**
+ * Session ID
+ */
+ sessionID: string
+ }
+ query?: {
+ directory?: string
+ }
+ url: "/session/{sessionID}/prompt_async"
+}
+
+export type SessionPromptAsyncErrors = {
+ /**
+ * Bad request
+ */
+ 400: BadRequestError
+ /**
+ * Not found
+ */
+ 404: NotFoundError
+}
+
+export type SessionPromptAsyncError = SessionPromptAsyncErrors[keyof SessionPromptAsyncErrors]
+
+export type SessionPromptAsyncResponses = {
+ /**
+ * Prompt accepted
+ */
+ 204: void
+}
+
+export type SessionPromptAsyncResponse = SessionPromptAsyncResponses[keyof SessionPromptAsyncResponses]
+
+export type SessionCommandData = {
+ body?: {
+ messageID?: string
+ agent?: string
+ model?: string
+ arguments: string
+ command: string
+ }
+ path: {
+ /**
+ * Session ID
+ */
+ sessionID: string
+ }
+ query?: {
+ directory?: string
+ }
+ url: "/session/{sessionID}/command"
+}
+
+export type SessionCommandErrors = {
+ /**
+ * Bad request
+ */
+ 400: BadRequestError
+ /**
+ * Not found
+ */
+ 404: NotFoundError
+}
+
+export type SessionCommandError = SessionCommandErrors[keyof SessionCommandErrors]
+
+export type SessionCommandResponses = {
+ /**
+ * Created message
+ */
+ 200: {
+ info: AssistantMessage
+ parts: Array<Part>
+ }
+}
+
+export type SessionCommandResponse = SessionCommandResponses[keyof SessionCommandResponses]
+
+export type SessionShellData = {
+ body?: {
+ agent: string
+ model?: {
+ providerID: string
+ modelID: string
+ }
+ command: string
+ }
+ path: {
+ /**
+ * Session ID
+ */
+ sessionID: string
+ }
+ query?: {
+ directory?: string
+ }
+ url: "/session/{sessionID}/shell"
+}
+
+export type SessionShellErrors = {
+ /**
+ * Bad request
+ */
+ 400: BadRequestError
+ /**
+ * Not found
+ */
+ 404: NotFoundError
+}
+
+export type SessionShellError = SessionShellErrors[keyof SessionShellErrors]
+
+export type SessionShellResponses = {
+ /**
+ * Created message
+ */
+ 200: AssistantMessage
+}
+
+export type SessionShellResponse = SessionShellResponses[keyof SessionShellResponses]
+
+export type SessionRevertData = {
+ body?: {
+ messageID: string
+ partID?: string
+ }
+ path: {
+ sessionID: string
+ }
+ query?: {
+ directory?: string
+ }
+ url: "/session/{sessionID}/revert"
+}
+
+export type SessionRevertErrors = {
+ /**
+ * Bad request
+ */
+ 400: BadRequestError
+ /**
+ * Not found
+ */
+ 404: NotFoundError
+}
+
+export type SessionRevertError = SessionRevertErrors[keyof SessionRevertErrors]
+
+export type SessionRevertResponses = {
+ /**
+ * Updated session
+ */
+ 200: Session
+}
+
+export type SessionRevertResponse = SessionRevertResponses[keyof SessionRevertResponses]
+
+export type SessionUnrevertData = {
+ body?: never
+ path: {
+ sessionID: string
+ }
+ query?: {
+ directory?: string
+ }
+ url: "/session/{sessionID}/unrevert"
+}
+
+export type SessionUnrevertErrors = {
+ /**
+ * Bad request
+ */
+ 400: BadRequestError
+ /**
+ * Not found
+ */
+ 404: NotFoundError
+}
+
+export type SessionUnrevertError = SessionUnrevertErrors[keyof SessionUnrevertErrors]
+
+export type SessionUnrevertResponses = {
+ /**
+ * Updated session
+ */
+ 200: Session
+}
+
+export type SessionUnrevertResponse = SessionUnrevertResponses[keyof SessionUnrevertResponses]
+
+export type PermissionRespondData = {
+ body?: {
+ response: "once" | "always" | "reject"
+ }
+ path: {
+ sessionID: string
+ permissionID: string
+ }
+ query?: {
+ directory?: string
+ }
+ url: "/session/{sessionID}/permissions/{permissionID}"
+}
+
+export type PermissionRespondErrors = {
+ /**
+ * Bad request
+ */
+ 400: BadRequestError
+ /**
+ * Not found
+ */
+ 404: NotFoundError
+}
+
+export type PermissionRespondError = PermissionRespondErrors[keyof PermissionRespondErrors]
+
+export type PermissionRespondResponses = {
+ /**
+ * Permission processed successfully
+ */
+ 200: boolean
+}
+
+export type PermissionRespondResponse = PermissionRespondResponses[keyof PermissionRespondResponses]
+
+export type CommandListData = {
+ body?: never
+ path?: never
+ query?: {
+ directory?: string
+ }
+ url: "/command"
+}
+
+export type CommandListResponses = {
+ /**
+ * List of commands
+ */
+ 200: Array<Command>
+}
+
+export type CommandListResponse = CommandListResponses[keyof CommandListResponses]
+
+export type ConfigProvidersData = {
+ body?: never
+ path?: never
+ query?: {
+ directory?: string
+ }
+ url: "/config/providers"
+}
+
+export type ConfigProvidersResponses = {
+ /**
+ * List of providers
+ */
+ 200: {
+ providers: Array<Provider>
+ default: {
+ [key: string]: string
+ }
+ }
+}
+
+export type ConfigProvidersResponse = ConfigProvidersResponses[keyof ConfigProvidersResponses]
+
+export type ProviderListData = {
+ body?: never
+ path?: never
+ query?: {
+ directory?: string
+ }
+ url: "/provider"
+}
+
+export type ProviderListResponses = {
+ /**
+ * List of providers
+ */
+ 200: {
+ all: Array<{
+ api?: string
+ name: string
+ env: Array<string>
+ id: string
+ npm?: string
+ models: {
+ [key: string]: {
+ id: string
+ name: string
+ release_date: string
+ attachment: boolean
+ reasoning: boolean
+ temperature: boolean
+ tool_call: boolean
+ cost?: {
+ input: number
+ output: number
+ cache_read?: number
+ cache_write?: number
+ context_over_200k?: {
+ input: number
+ output: number
+ cache_read?: number
+ cache_write?: number
+ }
+ }
+ limit: {
+ context: number
+ output: number
+ }
+ modalities?: {
+ input: Array<"text" | "audio" | "image" | "video" | "pdf">
+ output: Array<"text" | "audio" | "image" | "video" | "pdf">
+ }
+ experimental?: boolean
+ status?: "alpha" | "beta" | "deprecated"
+ options: {
+ [key: string]: unknown
+ }
+ headers?: {
+ [key: string]: string
+ }
+ provider?: {
+ npm: string
+ }
+ }
+ }
+ }>
+ default: {
+ [key: string]: string
+ }
+ connected: Array<string>
+ }
+}
+
+export type ProviderListResponse = ProviderListResponses[keyof ProviderListResponses]
+
+export type ProviderAuthData = {
+ body?: never
+ path?: never
+ query?: {
+ directory?: string
+ }
+ url: "/provider/auth"
+}
+
+export type ProviderAuthResponses = {
+ /**
+ * Provider auth methods
+ */
+ 200: {
+ [key: string]: Array<ProviderAuthMethod>
+ }
+}
+
+export type ProviderAuthResponse = ProviderAuthResponses[keyof ProviderAuthResponses]
+
+export type ProviderOauthAuthorizeData = {
+ body?: {
+ /**
+ * Auth method index
+ */
+ method: number
+ }
+ path: {
+ /**
+ * Provider ID
+ */
+ providerID: string
+ }
+ query?: {
+ directory?: string
+ }
+ url: "/provider/{providerID}/oauth/authorize"
+}
+
+export type ProviderOauthAuthorizeErrors = {
+ /**
+ * Bad request
+ */
+ 400: BadRequestError
+}
+
+export type ProviderOauthAuthorizeError = ProviderOauthAuthorizeErrors[keyof ProviderOauthAuthorizeErrors]
+
+export type ProviderOauthAuthorizeResponses = {
+ /**
+ * Authorization URL and method
+ */
+ 200: ProviderAuthAuthorization
+}
+
+export type ProviderOauthAuthorizeResponse = ProviderOauthAuthorizeResponses[keyof ProviderOauthAuthorizeResponses]
+
+export type ProviderOauthCallbackData = {
+ body?: {
+ /**
+ * Auth method index
+ */
+ method: number
+ /**
+ * OAuth authorization code
+ */
+ code?: string
+ }
+ path: {
+ /**
+ * Provider ID
+ */
+ providerID: string
+ }
+ query?: {
+ directory?: string
+ }
+ url: "/provider/{providerID}/oauth/callback"
+}
+
+export type ProviderOauthCallbackErrors = {
+ /**
+ * Bad request
+ */
+ 400: BadRequestError
+}
+
+export type ProviderOauthCallbackError = ProviderOauthCallbackErrors[keyof ProviderOauthCallbackErrors]
+
+export type ProviderOauthCallbackResponses = {
+ /**
+ * OAuth callback processed successfully
+ */
+ 200: boolean
+}
+
+export type ProviderOauthCallbackResponse = ProviderOauthCallbackResponses[keyof ProviderOauthCallbackResponses]
+
+export type FindTextData = {
+ body?: never
+ path?: never
+ query: {
+ directory?: string
+ pattern: string
+ }
+ url: "/find"
+}
+
+export type FindTextResponses = {
+ /**
+ * Matches
+ */
+ 200: Array<{
+ path: {
+ text: string
+ }
+ lines: {
+ text: string
+ }
+ line_number: number
+ absolute_offset: number
+ submatches: Array<{
+ match: {
+ text: string
+ }
+ start: number
+ end: number
+ }>
+ }>
+}
+
+export type FindTextResponse = FindTextResponses[keyof FindTextResponses]
+
+export type FindFilesData = {
+ body?: never
+ path?: never
+ query: {
+ directory?: string
+ query: string
+ dirs?: "true" | "false"
+ }
+ url: "/find/file"
+}
+
+export type FindFilesResponses = {
+ /**
+ * File paths
+ */
+ 200: Array<string>
+}
+
+export type FindFilesResponse = FindFilesResponses[keyof FindFilesResponses]
+
+export type FindSymbolsData = {
+ body?: never
+ path?: never
+ query: {
+ directory?: string
+ query: string
+ }
+ url: "/find/symbol"
+}
+
+export type FindSymbolsResponses = {
+ /**
+ * Symbols
+ */
+ 200: Array<Symbol>
+}
+
+export type FindSymbolsResponse = FindSymbolsResponses[keyof FindSymbolsResponses]
+
+export type FileListData = {
+ body?: never
+ path?: never
+ query: {
+ directory?: string
+ path: string
+ }
+ url: "/file"
+}
+
+export type FileListResponses = {
+ /**
+ * Files and directories
+ */
+ 200: Array<FileNode>
+}
+
+export type FileListResponse = FileListResponses[keyof FileListResponses]
+
+export type FileReadData = {
+ body?: never
+ path?: never
+ query: {
+ directory?: string
+ path: string
+ }
+ url: "/file/content"
+}
+
+export type FileReadResponses = {
+ /**
+ * File content
+ */
+ 200: FileContent
+}
+
+export type FileReadResponse = FileReadResponses[keyof FileReadResponses]
+
+export type FileStatusData = {
+ body?: never
+ path?: never
+ query?: {
+ directory?: string
+ }
+ url: "/file/status"
+}
+
+export type FileStatusResponses = {
+ /**
+ * File status
+ */
+ 200: Array<File>
+}
+
+export type FileStatusResponse = FileStatusResponses[keyof FileStatusResponses]
+
+export type AppLogData = {
+ body?: {
+ /**
+ * Service name for the log entry
+ */
+ service: string
+ /**
+ * Log level
+ */
+ level: "debug" | "info" | "error" | "warn"
+ /**
+ * Log message
+ */
+ message: string
+ /**
+ * Additional metadata for the log entry
+ */
+ extra?: {
+ [key: string]: unknown
+ }
+ }
+ path?: never
+ query?: {
+ directory?: string
+ }
+ url: "/log"
+}
+
+export type AppLogErrors = {
+ /**
+ * Bad request
+ */
+ 400: BadRequestError
+}
+
+export type AppLogError = AppLogErrors[keyof AppLogErrors]
+
+export type AppLogResponses = {
+ /**
+ * Log entry written successfully
+ */
+ 200: boolean
+}
+
+export type AppLogResponse = AppLogResponses[keyof AppLogResponses]
+
+export type AppAgentsData = {
+ body?: never
+ path?: never
+ query?: {
+ directory?: string
+ }
+ url: "/agent"
+}
+
+export type AppAgentsResponses = {
+ /**
+ * List of agents
+ */
+ 200: Array<Agent>
+}
+
+export type AppAgentsResponse = AppAgentsResponses[keyof AppAgentsResponses]
+
+export type McpStatusData = {
+ body?: never
+ path?: never
+ query?: {
+ directory?: string
+ }
+ url: "/mcp"
+}
+
+export type McpStatusResponses = {
+ /**
+ * MCP server status
+ */
+ 200: {
+ [key: string]: McpStatus
+ }
+}
+
+export type McpStatusResponse = McpStatusResponses[keyof McpStatusResponses]
+
+export type McpAddData = {
+ body?: {
+ name: string
+ config: McpLocalConfig | McpRemoteConfig
+ }
+ path?: never
+ query?: {
+ directory?: string
+ }
+ url: "/mcp"
+}
+
+export type McpAddErrors = {
+ /**
+ * Bad request
+ */
+ 400: BadRequestError
+}
+
+export type McpAddError = McpAddErrors[keyof McpAddErrors]
+
+export type McpAddResponses = {
+ /**
+ * MCP server added successfully
+ */
+ 200: {
+ [key: string]: McpStatus
+ }
+}
+
+export type McpAddResponse = McpAddResponses[keyof McpAddResponses]
+
+export type McpAuthRemoveData = {
+ body?: never
+ path: {
+ name: string
+ }
+ query?: {
+ directory?: string
+ }
+ url: "/mcp/{name}/auth"
+}
+
+export type McpAuthRemoveErrors = {
+ /**
+ * Not found
+ */
+ 404: NotFoundError
+}
+
+export type McpAuthRemoveError = McpAuthRemoveErrors[keyof McpAuthRemoveErrors]
+
+export type McpAuthRemoveResponses = {
+ /**
+ * OAuth credentials removed
+ */
+ 200: {
+ success: true
+ }
+}
+
+export type McpAuthRemoveResponse = McpAuthRemoveResponses[keyof McpAuthRemoveResponses]
+
+export type McpAuthStartData = {
+ body?: never
+ path: {
+ name: string
+ }
+ query?: {
+ directory?: string
+ }
+ url: "/mcp/{name}/auth"
+}
+
+export type McpAuthStartErrors = {
+ /**
+ * Bad request
+ */
+ 400: BadRequestError
+ /**
+ * Not found
+ */
+ 404: NotFoundError
+}
+
+export type McpAuthStartError = McpAuthStartErrors[keyof McpAuthStartErrors]
+
+export type McpAuthStartResponses = {
+ /**
+ * OAuth flow started
+ */
+ 200: {
+ /**
+ * URL to open in browser for authorization
+ */
+ authorizationUrl: string
+ }
+}
+
+export type McpAuthStartResponse = McpAuthStartResponses[keyof McpAuthStartResponses]
+
+export type McpAuthCallbackData = {
+ body?: {
+ /**
+ * Authorization code from OAuth callback
+ */
+ code: string
+ }
+ path: {
+ name: string
+ }
+ query?: {
+ directory?: string
+ }
+ url: "/mcp/{name}/auth/callback"
+}
+
+export type McpAuthCallbackErrors = {
+ /**
+ * Bad request
+ */
+ 400: BadRequestError
+ /**
+ * Not found
+ */
+ 404: NotFoundError
+}
+
+export type McpAuthCallbackError = McpAuthCallbackErrors[keyof McpAuthCallbackErrors]
+
+export type McpAuthCallbackResponses = {
+ /**
+ * OAuth authentication completed
+ */
+ 200: McpStatus
+}
+
+export type McpAuthCallbackResponse = McpAuthCallbackResponses[keyof McpAuthCallbackResponses]
+
+export type McpAuthAuthenticateData = {
+ body?: never
+ path: {
+ name: string
+ }
+ query?: {
+ directory?: string
+ }
+ url: "/mcp/{name}/auth/authenticate"
+}
+
+export type McpAuthAuthenticateErrors = {
+ /**
+ * Bad request
+ */
+ 400: BadRequestError
+ /**
+ * Not found
+ */
+ 404: NotFoundError
+}
+
+export type McpAuthAuthenticateError = McpAuthAuthenticateErrors[keyof McpAuthAuthenticateErrors]
+
+export type McpAuthAuthenticateResponses = {
+ /**
+ * OAuth authentication completed
+ */
+ 200: McpStatus
+}
+
+export type McpAuthAuthenticateResponse = McpAuthAuthenticateResponses[keyof McpAuthAuthenticateResponses]
+
+export type LspStatusData = {
+ body?: never
+ path?: never
+ query?: {
+ directory?: string
+ }
+ url: "/lsp"
+}
+
+export type LspStatusResponses = {
+ /**
+ * LSP server status
+ */
+ 200: Array<LspStatus>
+}
+
+export type LspStatusResponse = LspStatusResponses[keyof LspStatusResponses]
+
+export type FormatterStatusData = {
+ body?: never
+ path?: never
+ query?: {
+ directory?: string
+ }
+ url: "/formatter"
+}
+
+export type FormatterStatusResponses = {
+ /**
+ * Formatter status
+ */
+ 200: Array<FormatterStatus>
+}
+
+export type FormatterStatusResponse = FormatterStatusResponses[keyof FormatterStatusResponses]
+
+export type TuiAppendPromptData = {
+ body?: {
+ text: string
+ }
+ path?: never
+ query?: {
+ directory?: string
+ }
+ url: "/tui/append-prompt"
+}
+
+export type TuiAppendPromptErrors = {
+ /**
+ * Bad request
+ */
+ 400: BadRequestError
+}
+
+export type TuiAppendPromptError = TuiAppendPromptErrors[keyof TuiAppendPromptErrors]
+
+export type TuiAppendPromptResponses = {
+ /**
+ * Prompt processed successfully
+ */
+ 200: boolean
+}
+
+export type TuiAppendPromptResponse = TuiAppendPromptResponses[keyof TuiAppendPromptResponses]
+
+export type TuiOpenHelpData = {
+ body?: never
+ path?: never
+ query?: {
+ directory?: string
+ }
+ url: "/tui/open-help"
+}
+
+export type TuiOpenHelpResponses = {
+ /**
+ * Help dialog opened successfully
+ */
+ 200: boolean
+}
+
+export type TuiOpenHelpResponse = TuiOpenHelpResponses[keyof TuiOpenHelpResponses]
+
+export type TuiOpenSessionsData = {
+ body?: never
+ path?: never
+ query?: {
+ directory?: string
+ }
+ url: "/tui/open-sessions"
+}
+
+export type TuiOpenSessionsResponses = {
+ /**
+ * Session dialog opened successfully
+ */
+ 200: boolean
+}
+
+export type TuiOpenSessionsResponse = TuiOpenSessionsResponses[keyof TuiOpenSessionsResponses]
+
+export type TuiOpenThemesData = {
+ body?: never
+ path?: never
+ query?: {
+ directory?: string
+ }
+ url: "/tui/open-themes"
+}
+
+export type TuiOpenThemesResponses = {
+ /**
+ * Theme dialog opened successfully
+ */
+ 200: boolean
+}
+
+export type TuiOpenThemesResponse = TuiOpenThemesResponses[keyof TuiOpenThemesResponses]
+
+export type TuiOpenModelsData = {
+ body?: never
+ path?: never
+ query?: {
+ directory?: string
+ }
+ url: "/tui/open-models"
+}
+
+export type TuiOpenModelsResponses = {
+ /**
+ * Model dialog opened successfully
+ */
+ 200: boolean
+}
+
+export type TuiOpenModelsResponse = TuiOpenModelsResponses[keyof TuiOpenModelsResponses]
+
+export type TuiSubmitPromptData = {
+ body?: never
+ path?: never
+ query?: {
+ directory?: string
+ }
+ url: "/tui/submit-prompt"
+}
+
+export type TuiSubmitPromptResponses = {
+ /**
+ * Prompt submitted successfully
+ */
+ 200: boolean
+}
+
+export type TuiSubmitPromptResponse = TuiSubmitPromptResponses[keyof TuiSubmitPromptResponses]
+
+export type TuiClearPromptData = {
+ body?: never
+ path?: never
+ query?: {
+ directory?: string
+ }
+ url: "/tui/clear-prompt"
+}
+
+export type TuiClearPromptResponses = {
+ /**
+ * Prompt cleared successfully
+ */
+ 200: boolean
+}
+
+export type TuiClearPromptResponse = TuiClearPromptResponses[keyof TuiClearPromptResponses]
+
+export type TuiExecuteCommandData = {
+ body?: {
+ command: string
+ }
+ path?: never
+ query?: {
+ directory?: string
+ }
+ url: "/tui/execute-command"
+}
+
+export type TuiExecuteCommandErrors = {
+ /**
+ * Bad request
+ */
+ 400: BadRequestError
+}
+
+export type TuiExecuteCommandError = TuiExecuteCommandErrors[keyof TuiExecuteCommandErrors]
+
+export type TuiExecuteCommandResponses = {
+ /**
+ * Command executed successfully
+ */
+ 200: boolean
+}
+
+export type TuiExecuteCommandResponse = TuiExecuteCommandResponses[keyof TuiExecuteCommandResponses]
+
+export type TuiShowToastData = {
+ body?: {
+ title?: string
+ message: string
+ variant: "info" | "success" | "warning" | "error"
+ /**
+ * Duration in milliseconds
+ */
+ duration?: number
+ }
+ path?: never
+ query?: {
+ directory?: string
+ }
+ url: "/tui/show-toast"
+}
+
+export type TuiShowToastResponses = {
+ /**
+ * Toast notification shown successfully
+ */
+ 200: boolean
+}
+
+export type TuiShowToastResponse = TuiShowToastResponses[keyof TuiShowToastResponses]
+
+export type TuiPublishData = {
+ body?: EventTuiPromptAppend | EventTuiCommandExecute | EventTuiToastShow
+ path?: never
+ query?: {
+ directory?: string
+ }
+ url: "/tui/publish"
+}
+
+export type TuiPublishErrors = {
+ /**
+ * Bad request
+ */
+ 400: BadRequestError
+}
+
+export type TuiPublishError = TuiPublishErrors[keyof TuiPublishErrors]
+
+export type TuiPublishResponses = {
+ /**
+ * Event published successfully
+ */
+ 200: boolean
+}
+
+export type TuiPublishResponse = TuiPublishResponses[keyof TuiPublishResponses]
+
+export type TuiControlNextData = {
+ body?: never
+ path?: never
+ query?: {
+ directory?: string
+ }
+ url: "/tui/control/next"
+}
+
+export type TuiControlNextResponses = {
+ /**
+ * Next TUI request
+ */
+ 200: {
+ path: string
+ body: unknown
+ }
+}
+
+export type TuiControlNextResponse = TuiControlNextResponses[keyof TuiControlNextResponses]
+
+export type TuiControlResponseData = {
+ body?: unknown
+ path?: never
+ query?: {
+ directory?: string
+ }
+ url: "/tui/control/response"
+}
+
+export type TuiControlResponseResponses = {
+ /**
+ * Response submitted successfully
+ */
+ 200: boolean
+}
+
+export type TuiControlResponseResponse = TuiControlResponseResponses[keyof TuiControlResponseResponses]
+
+export type AuthSetData = {
+ body?: Auth
+ path: {
+ providerID: string
+ }
+ query?: {
+ directory?: string
+ }
+ url: "/auth/{providerID}"
+}
+
+export type AuthSetErrors = {
+ /**
+ * Bad request
+ */
+ 400: BadRequestError
+}
+
+export type AuthSetError = AuthSetErrors[keyof AuthSetErrors]
+
+export type AuthSetResponses = {
+ /**
+ * Successfully set authentication credentials
+ */
+ 200: boolean
+}
+
+export type AuthSetResponse = AuthSetResponses[keyof AuthSetResponses]
+
+export type EventSubscribeData = {
+ body?: never
+ path?: never
+ query?: {
+ directory?: string
+ }
+ url: "/event"
+}
+
+export type EventSubscribeResponses = {
+ /**
+ * Event stream
+ */
+ 200: Event
+}
+
+export type EventSubscribeResponse = EventSubscribeResponses[keyof EventSubscribeResponses]
diff --git a/packages/sdk/js/src/v2/index.ts b/packages/sdk/js/src/v2/index.ts
new file mode 100644
index 000000000..d044f5ad6
--- /dev/null
+++ b/packages/sdk/js/src/v2/index.ts
@@ -0,0 +1,21 @@
+export * from "./client.js"
+export * from "./server.js"
+
+import { createOpencodeClient } from "./client.js"
+import { createOpencodeServer } from "./server.js"
+import type { ServerOptions } from "./server.js"
+
+export async function createOpencode(options?: ServerOptions) {
+ const server = await createOpencodeServer({
+ ...options,
+ })
+
+ const client = createOpencodeClient({
+ baseUrl: server.url,
+ })
+
+ return {
+ client,
+ server,
+ }
+}
diff --git a/packages/sdk/js/src/v2/server.ts b/packages/sdk/js/src/v2/server.ts
new file mode 100644
index 000000000..a09e14ab2
--- /dev/null
+++ b/packages/sdk/js/src/v2/server.ts
@@ -0,0 +1,120 @@
+import { spawn } from "node:child_process"
+import { type Config } from "./gen/types.gen.js"
+
+export type ServerOptions = {
+ hostname?: string
+ port?: number
+ signal?: AbortSignal
+ timeout?: number
+ config?: Config
+}
+
+export type TuiOptions = {
+ project?: string
+ model?: string
+ session?: string
+ agent?: string
+ signal?: AbortSignal
+ config?: Config
+}
+
+export async function createOpencodeServer(options?: ServerOptions) {
+ options = Object.assign(
+ {
+ hostname: "127.0.0.1",
+ port: 4096,
+ timeout: 5000,
+ },
+ options ?? {},
+ )
+
+ const proc = spawn(`opencode`, [`serve`, `--hostname=${options.hostname}`, `--port=${options.port}`], {
+ signal: options.signal,
+ env: {
+ ...process.env,
+ OPENCODE_CONFIG_CONTENT: JSON.stringify(options.config ?? {}),
+ },
+ })
+
+ const url = await new Promise<string>((resolve, reject) => {
+ const id = setTimeout(() => {
+ reject(new Error(`Timeout waiting for server to start after ${options.timeout}ms`))
+ }, options.timeout)
+ let output = ""
+ proc.stdout?.on("data", (chunk) => {
+ output += chunk.toString()
+ const lines = output.split("\n")
+ for (const line of lines) {
+ if (line.startsWith("opencode server listening")) {
+ const match = line.match(/on\s+(https?:\/\/[^\s]+)/)
+ if (!match) {
+ throw new Error(`Failed to parse server url from output: ${line}`)
+ }
+ clearTimeout(id)
+ resolve(match[1]!)
+ return
+ }
+ }
+ })
+ proc.stderr?.on("data", (chunk) => {
+ output += chunk.toString()
+ })
+ proc.on("exit", (code) => {
+ clearTimeout(id)
+ let msg = `Server exited with code ${code}`
+ if (output.trim()) {
+ msg += `\nServer output: ${output}`
+ }
+ reject(new Error(msg))
+ })
+ proc.on("error", (error) => {
+ clearTimeout(id)
+ reject(error)
+ })
+ if (options.signal) {
+ options.signal.addEventListener("abort", () => {
+ clearTimeout(id)
+ reject(new Error("Aborted"))
+ })
+ }
+ })
+
+ return {
+ url,
+ close() {
+ proc.kill()
+ },
+ }
+}
+
+export function createOpencodeTui(options?: TuiOptions) {
+ const args = []
+
+ if (options?.project) {
+ args.push(`--project=${options.project}`)
+ }
+ if (options?.model) {
+ args.push(`--model=${options.model}`)
+ }
+ if (options?.session) {
+ args.push(`--session=${options.session}`)
+ }
+ if (options?.agent) {
+ args.push(`--agent=${options.agent}`)
+ }
+
+ const proc = spawn(`opencode`, args, {
+ signal: options?.signal,
+ stdio: "inherit",
+ env: {
+ ...process.env,
+ OPENCODE_CONFIG_CONTENT: JSON.stringify(options?.config ?? {}),
+ },
+ })
+
+ return {
+ close() {
+ proc.kill()
+ },
+ }
+}