diff options
| author | adamdotdevin <[email protected]> | 2025-07-22 11:50:51 -0500 |
|---|---|---|
| committer | adamdotdevin <[email protected]> | 2025-07-22 11:50:51 -0500 |
| commit | 10c8b495907069461f5dc464f6285321290c8b14 (patch) | |
| tree | 9fed07910dd7d99688c0261a364d810a500e21d3 /packages/sdk/src/client.ts | |
| parent | 500cea5ce7fa635a924cd9abea63aaf672f7645d (diff) | |
| download | opencode-10c8b495907069461f5dc464f6285321290c8b14.tar.gz opencode-10c8b495907069461f5dc464f6285321290c8b14.zip | |
chore: generate sdk into packages/sdk
Diffstat (limited to 'packages/sdk/src/client.ts')
| -rw-r--r-- | packages/sdk/src/client.ts | 862 |
1 files changed, 862 insertions, 0 deletions
diff --git a/packages/sdk/src/client.ts b/packages/sdk/src/client.ts new file mode 100644 index 000000000..c514ca877 --- /dev/null +++ b/packages/sdk/src/client.ts @@ -0,0 +1,862 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import type { RequestInit, RequestInfo, BodyInit } from './internal/builtin-types'; +import type { HTTPMethod, PromiseOrValue, MergedRequestInit, FinalizedRequestInit } from './internal/types'; +import { uuid4 } from './internal/utils/uuid'; +import { validatePositiveInteger, isAbsoluteURL, safeJSON } from './internal/utils/values'; +import { sleep } from './internal/utils/sleep'; +export type { Logger, LogLevel } from './internal/utils/log'; +import { castToError, isAbortError } from './internal/errors'; +import type { APIResponseProps } from './internal/parse'; +import { getPlatformHeaders } from './internal/detect-platform'; +import * as Shims from './internal/shims'; +import * as Opts from './internal/request-options'; +import { VERSION } from './version'; +import * as Errors from './core/error'; +import * as Uploads from './core/uploads'; +import * as API from './resources/index'; +import { APIPromise } from './core/api-promise'; +import { + App, + AppInitResponse, + AppLogParams, + AppLogResponse, + AppModesResponse, + AppProvidersResponse, + AppResource, + Mode, + Model, + Provider, +} from './resources/app'; +import { + Config, + ConfigResource, + KeybindsConfig, + McpLocalConfig, + McpRemoteConfig, + ModeConfig, +} from './resources/config'; +import { Event, EventListResponse } from './resources/event'; +import { File, FileReadParams, FileReadResponse, FileResource, FileStatusResponse } from './resources/file'; +import { + Find, + FindFilesParams, + FindFilesResponse, + FindSymbolsParams, + FindSymbolsResponse, + FindTextParams, + FindTextResponse, + Match, + Symbol, +} from './resources/find'; +import { + AssistantMessage, + FilePart, + FilePartInput, + FilePartSource, + FilePartSourceText, + FileSource, + Message, + Part, + Session, + SessionAbortResponse, + SessionChatParams, + SessionDeleteResponse, + SessionInitParams, + SessionInitResponse, + SessionListResponse, + SessionMessagesResponse, + SessionResource, + SessionSummarizeParams, + SessionSummarizeResponse, + SnapshotPart, + StepFinishPart, + StepStartPart, + SymbolSource, + TextPart, + TextPartInput, + ToolPart, + ToolStateCompleted, + ToolStateError, + ToolStatePending, + ToolStateRunning, + UserMessage, +} from './resources/session'; +import { Tui, TuiAppendPromptParams, TuiAppendPromptResponse, TuiOpenHelpResponse } from './resources/tui'; +import { type Fetch } from './internal/builtin-types'; +import { HeadersLike, NullableHeaders, buildHeaders } from './internal/headers'; +import { FinalRequestOptions, RequestOptions } from './internal/request-options'; +import { readEnv } from './internal/utils/env'; +import { + type LogLevel, + type Logger, + formatRequestDetails, + loggerFor, + parseLogLevel, +} from './internal/utils/log'; +import { isEmptyObj } from './internal/utils/values'; + +export interface ClientOptions { + /** + * Override the default base URL for the API, e.g., "https://api.example.com/v2/" + * + * Defaults to process.env['OPENCODE_BASE_URL']. + */ + baseURL?: string | null | undefined; + + /** + * The maximum amount of time (in milliseconds) that the client should wait for a response + * from the server before timing out a single request. + * + * Note that request timeouts are retried by default, so in a worst-case scenario you may wait + * much longer than this timeout before the promise succeeds or fails. + * + * @unit milliseconds + */ + timeout?: number | undefined; + /** + * Additional `RequestInit` options to be passed to `fetch` calls. + * Properties will be overridden by per-request `fetchOptions`. + */ + fetchOptions?: MergedRequestInit | undefined; + + /** + * Specify a custom `fetch` function implementation. + * + * If not provided, we expect that `fetch` is defined globally. + */ + fetch?: Fetch | undefined; + + /** + * The maximum number of times that the client will retry a request in case of a + * temporary failure, like a network error or a 5XX error from the server. + * + * @default 2 + */ + maxRetries?: number | undefined; + + /** + * Default headers to include with every request to the API. + * + * These can be removed in individual requests by explicitly setting the + * header to `null` in request options. + */ + defaultHeaders?: HeadersLike | undefined; + + /** + * Default query parameters to include with every request to the API. + * + * These can be removed in individual requests by explicitly setting the + * param to `undefined` in request options. + */ + defaultQuery?: Record<string, string | undefined> | undefined; + + /** + * Set the log level. + * + * Defaults to process.env['OPENCODE_LOG'] or 'warn' if it isn't set. + */ + logLevel?: LogLevel | undefined; + + /** + * Set the logger. + * + * Defaults to globalThis.console. + */ + logger?: Logger | undefined; +} + +/** + * API Client for interfacing with the Opencode API. + */ +export class Opencode { + baseURL: string; + maxRetries: number; + timeout: number; + logger: Logger | undefined; + logLevel: LogLevel | undefined; + fetchOptions: MergedRequestInit | undefined; + + private fetch: Fetch; + #encoder: Opts.RequestEncoder; + protected idempotencyHeader?: string; + private _options: ClientOptions; + + /** + * API Client for interfacing with the Opencode API. + * + * @param {string} [opts.baseURL=process.env['OPENCODE_BASE_URL'] ?? http://localhost:54321] - Override the default base URL for the API. + * @param {number} [opts.timeout=1 minute] - The maximum amount of time (in milliseconds) the client will wait for a response before timing out. + * @param {MergedRequestInit} [opts.fetchOptions] - Additional `RequestInit` options to be passed to `fetch` calls. + * @param {Fetch} [opts.fetch] - Specify a custom `fetch` function implementation. + * @param {number} [opts.maxRetries=2] - The maximum number of times the client will retry a request. + * @param {HeadersLike} opts.defaultHeaders - Default headers to include with every request to the API. + * @param {Record<string, string | undefined>} opts.defaultQuery - Default query parameters to include with every request to the API. + */ + constructor({ baseURL = readEnv('OPENCODE_BASE_URL'), ...opts }: ClientOptions = {}) { + const options: ClientOptions = { + ...opts, + baseURL: baseURL || `http://localhost:54321`, + }; + + this.baseURL = options.baseURL!; + this.timeout = options.timeout ?? Opencode.DEFAULT_TIMEOUT /* 1 minute */; + this.logger = options.logger ?? console; + const defaultLogLevel = 'warn'; + // Set default logLevel early so that we can log a warning in parseLogLevel. + this.logLevel = defaultLogLevel; + this.logLevel = + parseLogLevel(options.logLevel, 'ClientOptions.logLevel', this) ?? + parseLogLevel(readEnv('OPENCODE_LOG'), "process.env['OPENCODE_LOG']", this) ?? + defaultLogLevel; + this.fetchOptions = options.fetchOptions; + this.maxRetries = options.maxRetries ?? 2; + this.fetch = options.fetch ?? Shims.getDefaultFetch(); + this.#encoder = Opts.FallbackEncoder; + + this._options = options; + } + + /** + * Create a new client instance re-using the same options given to the current client with optional overriding. + */ + withOptions(options: Partial<ClientOptions>): this { + const client = new (this.constructor as any as new (props: ClientOptions) => typeof this)({ + ...this._options, + baseURL: this.baseURL, + maxRetries: this.maxRetries, + timeout: this.timeout, + logger: this.logger, + logLevel: this.logLevel, + fetch: this.fetch, + fetchOptions: this.fetchOptions, + ...options, + }); + return client; + } + + /** + * Check whether the base URL is set to its default. + */ + #baseURLOverridden(): boolean { + return this.baseURL !== 'http://localhost:54321'; + } + + protected defaultQuery(): Record<string, string | undefined> | undefined { + return this._options.defaultQuery; + } + + protected validateHeaders({ values, nulls }: NullableHeaders) { + return; + } + + /** + * Basic re-implementation of `qs.stringify` for primitive types. + */ + protected stringifyQuery(query: Record<string, unknown>): string { + return Object.entries(query) + .filter(([_, value]) => typeof value !== 'undefined') + .map(([key, value]) => { + if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') { + return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`; + } + if (value === null) { + return `${encodeURIComponent(key)}=`; + } + throw new Errors.OpencodeError( + `Cannot stringify type ${typeof value}; Expected string, number, boolean, or null. If you need to pass nested query parameters, you can manually encode them, e.g. { query: { 'foo[key1]': value1, 'foo[key2]': value2 } }, and please open a GitHub issue requesting better support for your use case.`, + ); + }) + .join('&'); + } + + private getUserAgent(): string { + return `${this.constructor.name}/JS ${VERSION}`; + } + + protected defaultIdempotencyKey(): string { + return `stainless-node-retry-${uuid4()}`; + } + + protected makeStatusError( + status: number, + error: Object, + message: string | undefined, + headers: Headers, + ): Errors.APIError { + return Errors.APIError.generate(status, error, message, headers); + } + + buildURL( + path: string, + query: Record<string, unknown> | null | undefined, + defaultBaseURL?: string | undefined, + ): string { + const baseURL = (!this.#baseURLOverridden() && defaultBaseURL) || this.baseURL; + const url = + isAbsoluteURL(path) ? + new URL(path) + : new URL(baseURL + (baseURL.endsWith('/') && path.startsWith('/') ? path.slice(1) : path)); + + const defaultQuery = this.defaultQuery(); + if (!isEmptyObj(defaultQuery)) { + query = { ...defaultQuery, ...query }; + } + + if (typeof query === 'object' && query && !Array.isArray(query)) { + url.search = this.stringifyQuery(query as Record<string, unknown>); + } + + return url.toString(); + } + + /** + * Used as a callback for mutating the given `FinalRequestOptions` object. + */ + protected async prepareOptions(options: FinalRequestOptions): Promise<void> {} + + /** + * Used as a callback for mutating the given `RequestInit` object. + * + * This is useful for cases where you want to add certain headers based off of + * the request properties, e.g. `method` or `url`. + */ + protected async prepareRequest( + request: RequestInit, + { url, options }: { url: string; options: FinalRequestOptions }, + ): Promise<void> {} + + get<Rsp>(path: string, opts?: PromiseOrValue<RequestOptions>): APIPromise<Rsp> { + return this.methodRequest('get', path, opts); + } + + post<Rsp>(path: string, opts?: PromiseOrValue<RequestOptions>): APIPromise<Rsp> { + return this.methodRequest('post', path, opts); + } + + patch<Rsp>(path: string, opts?: PromiseOrValue<RequestOptions>): APIPromise<Rsp> { + return this.methodRequest('patch', path, opts); + } + + put<Rsp>(path: string, opts?: PromiseOrValue<RequestOptions>): APIPromise<Rsp> { + return this.methodRequest('put', path, opts); + } + + delete<Rsp>(path: string, opts?: PromiseOrValue<RequestOptions>): APIPromise<Rsp> { + return this.methodRequest('delete', path, opts); + } + + private methodRequest<Rsp>( + method: HTTPMethod, + path: string, + opts?: PromiseOrValue<RequestOptions>, + ): APIPromise<Rsp> { + return this.request( + Promise.resolve(opts).then((opts) => { + return { method, path, ...opts }; + }), + ); + } + + request<Rsp>( + options: PromiseOrValue<FinalRequestOptions>, + remainingRetries: number | null = null, + ): APIPromise<Rsp> { + return new APIPromise(this, this.makeRequest(options, remainingRetries, undefined)); + } + + private async makeRequest( + optionsInput: PromiseOrValue<FinalRequestOptions>, + retriesRemaining: number | null, + retryOfRequestLogID: string | undefined, + ): Promise<APIResponseProps> { + const options = await optionsInput; + const maxRetries = options.maxRetries ?? this.maxRetries; + if (retriesRemaining == null) { + retriesRemaining = maxRetries; + } + + await this.prepareOptions(options); + + const { req, url, timeout } = await this.buildRequest(options, { + retryCount: maxRetries - retriesRemaining, + }); + + await this.prepareRequest(req, { url, options }); + + /** Not an API request ID, just for correlating local log entries. */ + const requestLogID = 'log_' + ((Math.random() * (1 << 24)) | 0).toString(16).padStart(6, '0'); + const retryLogStr = retryOfRequestLogID === undefined ? '' : `, retryOf: ${retryOfRequestLogID}`; + const startTime = Date.now(); + + loggerFor(this).debug( + `[${requestLogID}] sending request`, + formatRequestDetails({ + retryOfRequestLogID, + method: options.method, + url, + options, + headers: req.headers, + }), + ); + + if (options.signal?.aborted) { + throw new Errors.APIUserAbortError(); + } + + const controller = new AbortController(); + const response = await this.fetchWithTimeout(url, req, timeout, controller).catch(castToError); + const headersTime = Date.now(); + + if (response instanceof Error) { + const retryMessage = `retrying, ${retriesRemaining} attempts remaining`; + if (options.signal?.aborted) { + throw new Errors.APIUserAbortError(); + } + // detect native connection timeout errors + // deno throws "TypeError: error sending request for url (https://example/): client error (Connect): tcp connect error: Operation timed out (os error 60): Operation timed out (os error 60)" + // undici throws "TypeError: fetch failed" with cause "ConnectTimeoutError: Connect Timeout Error (attempted address: example:443, timeout: 1ms)" + // others do not provide enough information to distinguish timeouts from other connection errors + const isTimeout = + isAbortError(response) || + /timed? ?out/i.test(String(response) + ('cause' in response ? String(response.cause) : '')); + if (retriesRemaining) { + loggerFor(this).info( + `[${requestLogID}] connection ${isTimeout ? 'timed out' : 'failed'} - ${retryMessage}`, + ); + loggerFor(this).debug( + `[${requestLogID}] connection ${isTimeout ? 'timed out' : 'failed'} (${retryMessage})`, + formatRequestDetails({ + retryOfRequestLogID, + url, + durationMs: headersTime - startTime, + message: response.message, + }), + ); + return this.retryRequest(options, retriesRemaining, retryOfRequestLogID ?? requestLogID); + } + loggerFor(this).info( + `[${requestLogID}] connection ${isTimeout ? 'timed out' : 'failed'} - error; no more retries left`, + ); + loggerFor(this).debug( + `[${requestLogID}] connection ${isTimeout ? 'timed out' : 'failed'} (error; no more retries left)`, + formatRequestDetails({ + retryOfRequestLogID, + url, + durationMs: headersTime - startTime, + message: response.message, + }), + ); + if (isTimeout) { + throw new Errors.APIConnectionTimeoutError(); + } + throw new Errors.APIConnectionError({ cause: response }); + } + + const responseInfo = `[${requestLogID}${retryLogStr}] ${req.method} ${url} ${ + response.ok ? 'succeeded' : 'failed' + } with status ${response.status} in ${headersTime - startTime}ms`; + + if (!response.ok) { + const shouldRetry = await this.shouldRetry(response); + if (retriesRemaining && shouldRetry) { + const retryMessage = `retrying, ${retriesRemaining} attempts remaining`; + + // We don't need the body of this response. + await Shims.CancelReadableStream(response.body); + loggerFor(this).info(`${responseInfo} - ${retryMessage}`); + loggerFor(this).debug( + `[${requestLogID}] response error (${retryMessage})`, + formatRequestDetails({ + retryOfRequestLogID, + url: response.url, + status: response.status, + headers: response.headers, + durationMs: headersTime - startTime, + }), + ); + return this.retryRequest( + options, + retriesRemaining, + retryOfRequestLogID ?? requestLogID, + response.headers, + ); + } + + const retryMessage = shouldRetry ? `error; no more retries left` : `error; not retryable`; + + loggerFor(this).info(`${responseInfo} - ${retryMessage}`); + + const errText = await response.text().catch((err: any) => castToError(err).message); + const errJSON = safeJSON(errText); + const errMessage = errJSON ? undefined : errText; + + loggerFor(this).debug( + `[${requestLogID}] response error (${retryMessage})`, + formatRequestDetails({ + retryOfRequestLogID, + url: response.url, + status: response.status, + headers: response.headers, + message: errMessage, + durationMs: Date.now() - startTime, + }), + ); + + const err = this.makeStatusError(response.status, errJSON, errMessage, response.headers); + throw err; + } + + loggerFor(this).info(responseInfo); + loggerFor(this).debug( + `[${requestLogID}] response start`, + formatRequestDetails({ + retryOfRequestLogID, + url: response.url, + status: response.status, + headers: response.headers, + durationMs: headersTime - startTime, + }), + ); + + return { response, options, controller, requestLogID, retryOfRequestLogID, startTime }; + } + + async fetchWithTimeout( + url: RequestInfo, + init: RequestInit | undefined, + ms: number, + controller: AbortController, + ): Promise<Response> { + const { signal, method, ...options } = init || {}; + if (signal) signal.addEventListener('abort', () => controller.abort()); + + const timeout = setTimeout(() => controller.abort(), ms); + + const isReadableBody = + ((globalThis as any).ReadableStream && options.body instanceof (globalThis as any).ReadableStream) || + (typeof options.body === 'object' && options.body !== null && Symbol.asyncIterator in options.body); + + const fetchOptions: RequestInit = { + signal: controller.signal as any, + ...(isReadableBody ? { duplex: 'half' } : {}), + method: 'GET', + ...options, + }; + if (method) { + // Custom methods like 'patch' need to be uppercased + // See https://github.com/nodejs/undici/issues/2294 + fetchOptions.method = method.toUpperCase(); + } + + try { + // use undefined this binding; fetch errors if bound to something else in browser/cloudflare + return await this.fetch.call(undefined, url, fetchOptions); + } finally { + clearTimeout(timeout); + } + } + + private async shouldRetry(response: Response): Promise<boolean> { + // Note this is not a standard header. + const shouldRetryHeader = response.headers.get('x-should-retry'); + + // If the server explicitly says whether or not to retry, obey. + if (shouldRetryHeader === 'true') return true; + if (shouldRetryHeader === 'false') return false; + + // Retry on request timeouts. + if (response.status === 408) return true; + + // Retry on lock timeouts. + if (response.status === 409) return true; + + // Retry on rate limits. + if (response.status === 429) return true; + + // Retry internal errors. + if (response.status >= 500) return true; + + return false; + } + + private async retryRequest( + options: FinalRequestOptions, + retriesRemaining: number, + requestLogID: string, + responseHeaders?: Headers | undefined, + ): Promise<APIResponseProps> { + let timeoutMillis: number | undefined; + + // Note the `retry-after-ms` header may not be standard, but is a good idea and we'd like proactive support for it. + const retryAfterMillisHeader = responseHeaders?.get('retry-after-ms'); + if (retryAfterMillisHeader) { + const timeoutMs = parseFloat(retryAfterMillisHeader); + if (!Number.isNaN(timeoutMs)) { + timeoutMillis = timeoutMs; + } + } + + // About the Retry-After header: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After + const retryAfterHeader = responseHeaders?.get('retry-after'); + if (retryAfterHeader && !timeoutMillis) { + const timeoutSeconds = parseFloat(retryAfterHeader); + if (!Number.isNaN(timeoutSeconds)) { + timeoutMillis = timeoutSeconds * 1000; + } else { + timeoutMillis = Date.parse(retryAfterHeader) - Date.now(); + } + } + + // If the API asks us to wait a certain amount of time (and it's a reasonable amount), + // just do what it says, but otherwise calculate a default + if (!(timeoutMillis && 0 <= timeoutMillis && timeoutMillis < 60 * 1000)) { + const maxRetries = options.maxRetries ?? this.maxRetries; + timeoutMillis = this.calculateDefaultRetryTimeoutMillis(retriesRemaining, maxRetries); + } + await sleep(timeoutMillis); + + return this.makeRequest(options, retriesRemaining - 1, requestLogID); + } + + private calculateDefaultRetryTimeoutMillis(retriesRemaining: number, maxRetries: number): number { + const initialRetryDelay = 0.5; + const maxRetryDelay = 8.0; + + const numRetries = maxRetries - retriesRemaining; + + // Apply exponential backoff, but not more than the max. + const sleepSeconds = Math.min(initialRetryDelay * Math.pow(2, numRetries), maxRetryDelay); + + // Apply some jitter, take up to at most 25 percent of the retry time. + const jitter = 1 - Math.random() * 0.25; + + return sleepSeconds * jitter * 1000; + } + + async buildRequest( + inputOptions: FinalRequestOptions, + { retryCount = 0 }: { retryCount?: number } = {}, + ): Promise<{ req: FinalizedRequestInit; url: string; timeout: number }> { + const options = { ...inputOptions }; + const { method, path, query, defaultBaseURL } = options; + + const url = this.buildURL(path!, query as Record<string, unknown>, defaultBaseURL); + if ('timeout' in options) validatePositiveInteger('timeout', options.timeout); + options.timeout = options.timeout ?? this.timeout; + const { bodyHeaders, body } = this.buildBody({ options }); + const reqHeaders = await this.buildHeaders({ options: inputOptions, method, bodyHeaders, retryCount }); + + const req: FinalizedRequestInit = { + method, + headers: reqHeaders, + ...(options.signal && { signal: options.signal }), + ...((globalThis as any).ReadableStream && + body instanceof (globalThis as any).ReadableStream && { duplex: 'half' }), + ...(body && { body }), + ...((this.fetchOptions as any) ?? {}), + ...((options.fetchOptions as any) ?? {}), + }; + + return { req, url, timeout: options.timeout }; + } + + private async buildHeaders({ + options, + method, + bodyHeaders, + retryCount, + }: { + options: FinalRequestOptions; + method: HTTPMethod; + bodyHeaders: HeadersLike; + retryCount: number; + }): Promise<Headers> { + let idempotencyHeaders: HeadersLike = {}; + if (this.idempotencyHeader && method !== 'get') { + if (!options.idempotencyKey) options.idempotencyKey = this.defaultIdempotencyKey(); + idempotencyHeaders[this.idempotencyHeader] = options.idempotencyKey; + } + + const headers = buildHeaders([ + idempotencyHeaders, + { + Accept: 'application/json', + 'User-Agent': this.getUserAgent(), + 'X-Stainless-Retry-Count': String(retryCount), + ...(options.timeout ? { 'X-Stainless-Timeout': String(Math.trunc(options.timeout / 1000)) } : {}), + ...getPlatformHeaders(), + }, + this._options.defaultHeaders, + bodyHeaders, + options.headers, + ]); + + this.validateHeaders(headers); + + return headers.values; + } + + private buildBody({ options: { body, headers: rawHeaders } }: { options: FinalRequestOptions }): { + bodyHeaders: HeadersLike; + body: BodyInit | undefined; + } { + if (!body) { + return { bodyHeaders: undefined, body: undefined }; + } + const headers = buildHeaders([rawHeaders]); + if ( + // Pass raw type verbatim + ArrayBuffer.isView(body) || + body instanceof ArrayBuffer || + body instanceof DataView || + (typeof body === 'string' && + // Preserve legacy string encoding behavior for now + headers.values.has('content-type')) || + // `Blob` is superset of `File` + body instanceof Blob || + // `FormData` -> `multipart/form-data` + body instanceof FormData || + // `URLSearchParams` -> `application/x-www-form-urlencoded` + body instanceof URLSearchParams || + // Send chunked stream (each chunk has own `length`) + ((globalThis as any).ReadableStream && body instanceof (globalThis as any).ReadableStream) + ) { + return { bodyHeaders: undefined, body: body as BodyInit }; + } else if ( + typeof body === 'object' && + (Symbol.asyncIterator in body || + (Symbol.iterator in body && 'next' in body && typeof body.next === 'function')) + ) { + return { bodyHeaders: undefined, body: Shims.ReadableStreamFrom(body as AsyncIterable<Uint8Array>) }; + } else { + return this.#encoder({ body, headers }); + } + } + + static Opencode = this; + static DEFAULT_TIMEOUT = 60000; // 1 minute + + static OpencodeError = Errors.OpencodeError; + static APIError = Errors.APIError; + static APIConnectionError = Errors.APIConnectionError; + static APIConnectionTimeoutError = Errors.APIConnectionTimeoutError; + static APIUserAbortError = Errors.APIUserAbortError; + static NotFoundError = Errors.NotFoundError; + static ConflictError = Errors.ConflictError; + static RateLimitError = Errors.RateLimitError; + static BadRequestError = Errors.BadRequestError; + static AuthenticationError = Errors.AuthenticationError; + static InternalServerError = Errors.InternalServerError; + static PermissionDeniedError = Errors.PermissionDeniedError; + static UnprocessableEntityError = Errors.UnprocessableEntityError; + + static toFile = Uploads.toFile; + + event: API.Event = new API.Event(this); + app: API.AppResource = new API.AppResource(this); + find: API.Find = new API.Find(this); + file: API.FileResource = new API.FileResource(this); + config: API.ConfigResource = new API.ConfigResource(this); + session: API.SessionResource = new API.SessionResource(this); + tui: API.Tui = new API.Tui(this); +} +Opencode.Event = Event; +Opencode.AppResource = AppResource; +Opencode.Find = Find; +Opencode.FileResource = FileResource; +Opencode.ConfigResource = ConfigResource; +Opencode.SessionResource = SessionResource; +Opencode.Tui = Tui; +export declare namespace Opencode { + export type RequestOptions = Opts.RequestOptions; + + export { Event as Event, type EventListResponse as EventListResponse }; + + export { + AppResource as AppResource, + type App as App, + type Mode as Mode, + type Model as Model, + type Provider as Provider, + type AppInitResponse as AppInitResponse, + type AppLogResponse as AppLogResponse, + type AppModesResponse as AppModesResponse, + type AppProvidersResponse as AppProvidersResponse, + type AppLogParams as AppLogParams, + }; + + export { + Find as Find, + type Match as Match, + type Symbol as Symbol, + type FindFilesResponse as FindFilesResponse, + type FindSymbolsResponse as FindSymbolsResponse, + type FindTextResponse as FindTextResponse, + type FindFilesParams as FindFilesParams, + type FindSymbolsParams as FindSymbolsParams, + type FindTextParams as FindTextParams, + }; + + export { + FileResource as FileResource, + type File as File, + type FileReadResponse as FileReadResponse, + type FileStatusResponse as FileStatusResponse, + type FileReadParams as FileReadParams, + }; + + export { + ConfigResource as ConfigResource, + type Config as Config, + type KeybindsConfig as KeybindsConfig, + type McpLocalConfig as McpLocalConfig, + type McpRemoteConfig as McpRemoteConfig, + type ModeConfig as ModeConfig, + }; + + export { + SessionResource as SessionResource, + type AssistantMessage as AssistantMessage, + type FilePart as FilePart, + type FilePartInput as FilePartInput, + type FilePartSource as FilePartSource, + type FilePartSourceText as FilePartSourceText, + type FileSource as FileSource, + type Message as Message, + type Part as Part, + type Session as Session, + type SnapshotPart as SnapshotPart, + type StepFinishPart as StepFinishPart, + type StepStartPart as StepStartPart, + type SymbolSource as SymbolSource, + type TextPart as TextPart, + type TextPartInput as TextPartInput, + type ToolPart as ToolPart, + type ToolStateCompleted as ToolStateCompleted, + type ToolStateError as ToolStateError, + type ToolStatePending as ToolStatePending, + type ToolStateRunning as ToolStateRunning, + type UserMessage as UserMessage, + type SessionListResponse as SessionListResponse, + type SessionDeleteResponse as SessionDeleteResponse, + type SessionAbortResponse as SessionAbortResponse, + type SessionInitResponse as SessionInitResponse, + type SessionMessagesResponse as SessionMessagesResponse, + type SessionSummarizeResponse as SessionSummarizeResponse, + type SessionChatParams as SessionChatParams, + type SessionInitParams as SessionInitParams, + type SessionSummarizeParams as SessionSummarizeParams, + }; + + export { + Tui as Tui, + type TuiAppendPromptResponse as TuiAppendPromptResponse, + type TuiOpenHelpResponse as TuiOpenHelpResponse, + type TuiAppendPromptParams as TuiAppendPromptParams, + }; + + export type MessageAbortedError = API.MessageAbortedError; + export type ProviderAuthError = API.ProviderAuthError; + export type UnknownError = API.UnknownError; +} |
