diff options
Diffstat (limited to 'packages/sdk/src')
47 files changed, 0 insertions, 5066 deletions
diff --git a/packages/sdk/src/api-promise.ts b/packages/sdk/src/api-promise.ts deleted file mode 100644 index 8c775ee69..000000000 --- a/packages/sdk/src/api-promise.ts +++ /dev/null @@ -1,2 +0,0 @@ -/** @deprecated Import from ./core/api-promise instead */ -export * from './core/api-promise'; diff --git a/packages/sdk/src/client.ts b/packages/sdk/src/client.ts deleted file mode 100644 index 9241bd582..000000000 --- a/packages/sdk/src/client.ts +++ /dev/null @@ -1,862 +0,0 @@ -// 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, - Symbol, -} from './resources/find'; -import { - AssistantMessage, - FilePart, - FilePartInput, - FilePartSource, - FilePartSourceText, - FileSource, - Message, - Part, - Session, - SessionAbortResponse, - SessionChatParams, - SessionDeleteResponse, - SessionInitParams, - SessionInitResponse, - SessionListResponse, - SessionMessagesResponse, - SessionResource, - SessionRevertParams, - 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 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 SessionRevertParams as SessionRevertParams, - 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; -} diff --git a/packages/sdk/src/core/README.md b/packages/sdk/src/core/README.md deleted file mode 100644 index 485fce861..000000000 --- a/packages/sdk/src/core/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# `core` - -This directory holds public modules implementing non-resource-specific SDK functionality. diff --git a/packages/sdk/src/core/api-promise.ts b/packages/sdk/src/core/api-promise.ts deleted file mode 100644 index fc1c6dd76..000000000 --- a/packages/sdk/src/core/api-promise.ts +++ /dev/null @@ -1,92 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { type Opencode } from '../client'; - -import { type PromiseOrValue } from '../internal/types'; -import { APIResponseProps, defaultParseResponse } from '../internal/parse'; - -/** - * A subclass of `Promise` providing additional helper methods - * for interacting with the SDK. - */ -export class APIPromise<T> extends Promise<T> { - private parsedPromise: Promise<T> | undefined; - #client: Opencode; - - constructor( - client: Opencode, - private responsePromise: Promise<APIResponseProps>, - private parseResponse: ( - client: Opencode, - props: APIResponseProps, - ) => PromiseOrValue<T> = defaultParseResponse, - ) { - super((resolve) => { - // this is maybe a bit weird but this has to be a no-op to not implicitly - // parse the response body; instead .then, .catch, .finally are overridden - // to parse the response - resolve(null as any); - }); - this.#client = client; - } - - _thenUnwrap<U>(transform: (data: T, props: APIResponseProps) => U): APIPromise<U> { - return new APIPromise(this.#client, this.responsePromise, async (client, props) => - transform(await this.parseResponse(client, props), props), - ); - } - - /** - * Gets the raw `Response` instance instead of parsing the response - * data. - * - * If you want to parse the response body but still get the `Response` - * instance, you can use {@link withResponse()}. - * - * 👋 Getting the wrong TypeScript type for `Response`? - * Try setting `"moduleResolution": "NodeNext"` or add `"lib": ["DOM"]` - * to your `tsconfig.json`. - */ - asResponse(): Promise<Response> { - return this.responsePromise.then((p) => p.response); - } - - /** - * Gets the parsed response data and the raw `Response` instance. - * - * If you just want to get the raw `Response` instance without parsing it, - * you can use {@link asResponse()}. - * - * 👋 Getting the wrong TypeScript type for `Response`? - * Try setting `"moduleResolution": "NodeNext"` or add `"lib": ["DOM"]` - * to your `tsconfig.json`. - */ - async withResponse(): Promise<{ data: T; response: Response }> { - const [data, response] = await Promise.all([this.parse(), this.asResponse()]); - return { data, response }; - } - - private parse(): Promise<T> { - if (!this.parsedPromise) { - this.parsedPromise = this.responsePromise.then((data) => this.parseResponse(this.#client, data)); - } - return this.parsedPromise; - } - - override then<TResult1 = T, TResult2 = never>( - onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null, - onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null, - ): Promise<TResult1 | TResult2> { - return this.parse().then(onfulfilled, onrejected); - } - - override catch<TResult = never>( - onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null, - ): Promise<T | TResult> { - return this.parse().catch(onrejected); - } - - override finally(onfinally?: (() => void) | undefined | null): Promise<T> { - return this.parse().finally(onfinally); - } -} diff --git a/packages/sdk/src/core/error.ts b/packages/sdk/src/core/error.ts deleted file mode 100644 index 44698c739..000000000 --- a/packages/sdk/src/core/error.ts +++ /dev/null @@ -1,130 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { castToError } from '../internal/errors'; - -export class OpencodeError extends Error {} - -export class APIError< - TStatus extends number | undefined = number | undefined, - THeaders extends Headers | undefined = Headers | undefined, - TError extends Object | undefined = Object | undefined, -> extends OpencodeError { - /** HTTP status for the response that caused the error */ - readonly status: TStatus; - /** HTTP headers for the response that caused the error */ - readonly headers: THeaders; - /** JSON body of the response that caused the error */ - readonly error: TError; - - constructor(status: TStatus, error: TError, message: string | undefined, headers: THeaders) { - super(`${APIError.makeMessage(status, error, message)}`); - this.status = status; - this.headers = headers; - this.error = error; - } - - private static makeMessage(status: number | undefined, error: any, message: string | undefined) { - const msg = - error?.message ? - typeof error.message === 'string' ? - error.message - : JSON.stringify(error.message) - : error ? JSON.stringify(error) - : message; - - if (status && msg) { - return `${status} ${msg}`; - } - if (status) { - return `${status} status code (no body)`; - } - if (msg) { - return msg; - } - return '(no status code or body)'; - } - - static generate( - status: number | undefined, - errorResponse: Object | undefined, - message: string | undefined, - headers: Headers | undefined, - ): APIError { - if (!status || !headers) { - return new APIConnectionError({ message, cause: castToError(errorResponse) }); - } - - const error = errorResponse as Record<string, any>; - - if (status === 400) { - return new BadRequestError(status, error, message, headers); - } - - if (status === 401) { - return new AuthenticationError(status, error, message, headers); - } - - if (status === 403) { - return new PermissionDeniedError(status, error, message, headers); - } - - if (status === 404) { - return new NotFoundError(status, error, message, headers); - } - - if (status === 409) { - return new ConflictError(status, error, message, headers); - } - - if (status === 422) { - return new UnprocessableEntityError(status, error, message, headers); - } - - if (status === 429) { - return new RateLimitError(status, error, message, headers); - } - - if (status >= 500) { - return new InternalServerError(status, error, message, headers); - } - - return new APIError(status, error, message, headers); - } -} - -export class APIUserAbortError extends APIError<undefined, undefined, undefined> { - constructor({ message }: { message?: string } = {}) { - super(undefined, undefined, message || 'Request was aborted.', undefined); - } -} - -export class APIConnectionError extends APIError<undefined, undefined, undefined> { - constructor({ message, cause }: { message?: string | undefined; cause?: Error | undefined }) { - super(undefined, undefined, message || 'Connection error.', undefined); - // in some environments the 'cause' property is already declared - // @ts-ignore - if (cause) this.cause = cause; - } -} - -export class APIConnectionTimeoutError extends APIConnectionError { - constructor({ message }: { message?: string } = {}) { - super({ message: message ?? 'Request timed out.' }); - } -} - -export class BadRequestError extends APIError<400, Headers> {} - -export class AuthenticationError extends APIError<401, Headers> {} - -export class PermissionDeniedError extends APIError<403, Headers> {} - -export class NotFoundError extends APIError<404, Headers> {} - -export class ConflictError extends APIError<409, Headers> {} - -export class UnprocessableEntityError extends APIError<422, Headers> {} - -export class RateLimitError extends APIError<429, Headers> {} - -export class InternalServerError extends APIError<number, Headers> {} diff --git a/packages/sdk/src/core/resource.ts b/packages/sdk/src/core/resource.ts deleted file mode 100644 index ccf9032e4..000000000 --- a/packages/sdk/src/core/resource.ts +++ /dev/null @@ -1,11 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import type { Opencode } from '../client'; - -export abstract class APIResource { - protected _client: Opencode; - - constructor(client: Opencode) { - this._client = client; - } -} diff --git a/packages/sdk/src/core/streaming.ts b/packages/sdk/src/core/streaming.ts deleted file mode 100644 index 33dcfd126..000000000 --- a/packages/sdk/src/core/streaming.ts +++ /dev/null @@ -1,315 +0,0 @@ -import { OpencodeError } from './error'; -import { type ReadableStream } from '../internal/shim-types'; -import { makeReadableStream } from '../internal/shims'; -import { findDoubleNewlineIndex, LineDecoder } from '../internal/decoders/line'; -import { ReadableStreamToAsyncIterable } from '../internal/shims'; -import { isAbortError } from '../internal/errors'; -import { encodeUTF8 } from '../internal/utils/bytes'; -import { loggerFor } from '../internal/utils/log'; -import type { Opencode } from '../client'; - -type Bytes = string | ArrayBuffer | Uint8Array | null | undefined; - -export type ServerSentEvent = { - event: string | null; - data: string; - raw: string[]; -}; - -export class Stream<Item> implements AsyncIterable<Item> { - controller: AbortController; - #client: Opencode | undefined; - - constructor( - private iterator: () => AsyncIterator<Item>, - controller: AbortController, - client?: Opencode, - ) { - this.controller = controller; - this.#client = client; - } - - static fromSSEResponse<Item>( - response: Response, - controller: AbortController, - client?: Opencode, - ): Stream<Item> { - let consumed = false; - const logger = client ? loggerFor(client) : console; - - async function* iterator(): AsyncIterator<Item, any, undefined> { - if (consumed) { - throw new OpencodeError('Cannot iterate over a consumed stream, use `.tee()` to split the stream.'); - } - consumed = true; - let done = false; - try { - for await (const sse of _iterSSEMessages(response, controller)) { - try { - yield JSON.parse(sse.data); - } catch (e) { - logger.error(`Could not parse message into JSON:`, sse.data); - logger.error(`From chunk:`, sse.raw); - throw e; - } - } - done = true; - } catch (e) { - // If the user calls `stream.controller.abort()`, we should exit without throwing. - if (isAbortError(e)) return; - throw e; - } finally { - // If the user `break`s, abort the ongoing request. - if (!done) controller.abort(); - } - } - - return new Stream(iterator, controller, client); - } - - /** - * Generates a Stream from a newline-separated ReadableStream - * where each item is a JSON value. - */ - static fromReadableStream<Item>( - readableStream: ReadableStream, - controller: AbortController, - client?: Opencode, - ): Stream<Item> { - let consumed = false; - - async function* iterLines(): AsyncGenerator<string, void, unknown> { - const lineDecoder = new LineDecoder(); - - const iter = ReadableStreamToAsyncIterable<Bytes>(readableStream); - for await (const chunk of iter) { - for (const line of lineDecoder.decode(chunk)) { - yield line; - } - } - - for (const line of lineDecoder.flush()) { - yield line; - } - } - - async function* iterator(): AsyncIterator<Item, any, undefined> { - if (consumed) { - throw new OpencodeError('Cannot iterate over a consumed stream, use `.tee()` to split the stream.'); - } - consumed = true; - let done = false; - try { - for await (const line of iterLines()) { - if (done) continue; - if (line) yield JSON.parse(line); - } - done = true; - } catch (e) { - // If the user calls `stream.controller.abort()`, we should exit without throwing. - if (isAbortError(e)) return; - throw e; - } finally { - // If the user `break`s, abort the ongoing request. - if (!done) controller.abort(); - } - } - - return new Stream(iterator, controller, client); - } - - [Symbol.asyncIterator](): AsyncIterator<Item> { - return this.iterator(); - } - - /** - * Splits the stream into two streams which can be - * independently read from at different speeds. - */ - tee(): [Stream<Item>, Stream<Item>] { - const left: Array<Promise<IteratorResult<Item>>> = []; - const right: Array<Promise<IteratorResult<Item>>> = []; - const iterator = this.iterator(); - - const teeIterator = (queue: Array<Promise<IteratorResult<Item>>>): AsyncIterator<Item> => { - return { - next: () => { - if (queue.length === 0) { - const result = iterator.next(); - left.push(result); - right.push(result); - } - return queue.shift()!; - }, - }; - }; - - return [ - new Stream(() => teeIterator(left), this.controller, this.#client), - new Stream(() => teeIterator(right), this.controller, this.#client), - ]; - } - - /** - * Converts this stream to a newline-separated ReadableStream of - * JSON stringified values in the stream - * which can be turned back into a Stream with `Stream.fromReadableStream()`. - */ - toReadableStream(): ReadableStream { - const self = this; - let iter: AsyncIterator<Item>; - - return makeReadableStream({ - async start() { - iter = self[Symbol.asyncIterator](); - }, - async pull(ctrl: any) { - try { - const { value, done } = await iter.next(); - if (done) return ctrl.close(); - - const bytes = encodeUTF8(JSON.stringify(value) + '\n'); - - ctrl.enqueue(bytes); - } catch (err) { - ctrl.error(err); - } - }, - async cancel() { - await iter.return?.(); - }, - }); - } -} - -export async function* _iterSSEMessages( - response: Response, - controller: AbortController, -): AsyncGenerator<ServerSentEvent, void, unknown> { - if (!response.body) { - controller.abort(); - if ( - typeof (globalThis as any).navigator !== 'undefined' && - (globalThis as any).navigator.product === 'ReactNative' - ) { - throw new OpencodeError( - `The default react-native fetch implementation does not support streaming. Please use expo/fetch: https://docs.expo.dev/versions/latest/sdk/expo/#expofetch-api`, - ); - } - throw new OpencodeError(`Attempted to iterate over a response with no body`); - } - - const sseDecoder = new SSEDecoder(); - const lineDecoder = new LineDecoder(); - - const iter = ReadableStreamToAsyncIterable<Bytes>(response.body); - for await (const sseChunk of iterSSEChunks(iter)) { - for (const line of lineDecoder.decode(sseChunk)) { - const sse = sseDecoder.decode(line); - if (sse) yield sse; - } - } - - for (const line of lineDecoder.flush()) { - const sse = sseDecoder.decode(line); - if (sse) yield sse; - } -} - -/** - * Given an async iterable iterator, iterates over it and yields full - * SSE chunks, i.e. yields when a double new-line is encountered. - */ -async function* iterSSEChunks(iterator: AsyncIterableIterator<Bytes>): AsyncGenerator<Uint8Array> { - let data = new Uint8Array(); - - for await (const chunk of iterator) { - if (chunk == null) { - continue; - } - - const binaryChunk = - chunk instanceof ArrayBuffer ? new Uint8Array(chunk) - : typeof chunk === 'string' ? encodeUTF8(chunk) - : chunk; - - let newData = new Uint8Array(data.length + binaryChunk.length); - newData.set(data); - newData.set(binaryChunk, data.length); - data = newData; - - let patternIndex; - while ((patternIndex = findDoubleNewlineIndex(data)) !== -1) { - yield data.slice(0, patternIndex); - data = data.slice(patternIndex); - } - } - - if (data.length > 0) { - yield data; - } -} - -class SSEDecoder { - private data: string[]; - private event: string | null; - private chunks: string[]; - - constructor() { - this.event = null; - this.data = []; - this.chunks = []; - } - - decode(line: string) { - if (line.endsWith('\r')) { - line = line.substring(0, line.length - 1); - } - - if (!line) { - // empty line and we didn't previously encounter any messages - if (!this.event && !this.data.length) return null; - - const sse: ServerSentEvent = { - event: this.event, - data: this.data.join('\n'), - raw: this.chunks, - }; - - this.event = null; - this.data = []; - this.chunks = []; - - return sse; - } - - this.chunks.push(line); - - if (line.startsWith(':')) { - return null; - } - - let [fieldname, _, value] = partition(line, ':'); - - if (value.startsWith(' ')) { - value = value.substring(1); - } - - if (fieldname === 'event') { - this.event = value; - } else if (fieldname === 'data') { - this.data.push(value); - } - - return null; - } -} - -function partition(str: string, delimiter: string): [string, string, string] { - const index = str.indexOf(delimiter); - if (index !== -1) { - return [str.substring(0, index), delimiter, str.substring(index + delimiter.length)]; - } - - return [str, '', '']; -} diff --git a/packages/sdk/src/core/uploads.ts b/packages/sdk/src/core/uploads.ts deleted file mode 100644 index 2882ca6d1..000000000 --- a/packages/sdk/src/core/uploads.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { type Uploadable } from '../internal/uploads'; -export { toFile, type ToFileInput } from '../internal/to-file'; diff --git a/packages/sdk/src/error.ts b/packages/sdk/src/error.ts deleted file mode 100644 index fc55f46c0..000000000 --- a/packages/sdk/src/error.ts +++ /dev/null @@ -1,2 +0,0 @@ -/** @deprecated Import from ./core/error instead */ -export * from './core/error'; diff --git a/packages/sdk/src/index.ts b/packages/sdk/src/index.ts deleted file mode 100644 index 43d2f6363..000000000 --- a/packages/sdk/src/index.ts +++ /dev/null @@ -1,22 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -export { Opencode as default } from './client'; - -export { type Uploadable, toFile } from './core/uploads'; -export { APIPromise } from './core/api-promise'; -export { Opencode, type ClientOptions } from './client'; -export { - OpencodeError, - APIError, - APIConnectionError, - APIConnectionTimeoutError, - APIUserAbortError, - NotFoundError, - ConflictError, - RateLimitError, - BadRequestError, - AuthenticationError, - InternalServerError, - PermissionDeniedError, - UnprocessableEntityError, -} from './core/error'; diff --git a/packages/sdk/src/internal/README.md b/packages/sdk/src/internal/README.md deleted file mode 100644 index 3ef5a25ba..000000000 --- a/packages/sdk/src/internal/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# `internal` - -The modules in this directory are not importable outside this package and will change between releases. diff --git a/packages/sdk/src/internal/builtin-types.ts b/packages/sdk/src/internal/builtin-types.ts deleted file mode 100644 index c23d3bded..000000000 --- a/packages/sdk/src/internal/builtin-types.ts +++ /dev/null @@ -1,93 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -export type Fetch = (input: string | URL | Request, init?: RequestInit) => Promise<Response>; - -/** - * An alias to the builtin `RequestInit` type so we can - * easily alias it in import statements if there are name clashes. - * - * https://developer.mozilla.org/docs/Web/API/RequestInit - */ -type _RequestInit = RequestInit; - -/** - * An alias to the builtin `Response` type so we can - * easily alias it in import statements if there are name clashes. - * - * https://developer.mozilla.org/docs/Web/API/Response - */ -type _Response = Response; - -/** - * The type for the first argument to `fetch`. - * - * https://developer.mozilla.org/docs/Web/API/Window/fetch#resource - */ -type _RequestInfo = Request | URL | string; - -/** - * The type for constructing `RequestInit` Headers. - * - * https://developer.mozilla.org/docs/Web/API/RequestInit#setting_headers - */ -type _HeadersInit = RequestInit['headers']; - -/** - * The type for constructing `RequestInit` body. - * - * https://developer.mozilla.org/docs/Web/API/RequestInit#body - */ -type _BodyInit = RequestInit['body']; - -/** - * An alias to the builtin `Array<T>` type so we can - * easily alias it in import statements if there are name clashes. - */ -type _Array<T> = Array<T>; - -/** - * An alias to the builtin `Record<K, T>` type so we can - * easily alias it in import statements if there are name clashes. - */ -type _Record<K extends keyof any, T> = Record<K, T>; - -export type { - _Array as Array, - _BodyInit as BodyInit, - _HeadersInit as HeadersInit, - _Record as Record, - _RequestInfo as RequestInfo, - _RequestInit as RequestInit, - _Response as Response, -}; - -/** - * A copy of the builtin `EndingType` type as it isn't fully supported in certain - * environments and attempting to reference the global version will error. - * - * https://github.com/microsoft/TypeScript/blob/49ad1a3917a0ea57f5ff248159256e12bb1cb705/src/lib/dom.generated.d.ts#L27941 - */ -type EndingType = 'native' | 'transparent'; - -/** - * A copy of the builtin `BlobPropertyBag` type as it isn't fully supported in certain - * environments and attempting to reference the global version will error. - * - * https://github.com/microsoft/TypeScript/blob/49ad1a3917a0ea57f5ff248159256e12bb1cb705/src/lib/dom.generated.d.ts#L154 - * https://developer.mozilla.org/en-US/docs/Web/API/Blob/Blob#options - */ -export interface BlobPropertyBag { - endings?: EndingType; - type?: string; -} - -/** - * A copy of the builtin `FilePropertyBag` type as it isn't fully supported in certain - * environments and attempting to reference the global version will error. - * - * https://github.com/microsoft/TypeScript/blob/49ad1a3917a0ea57f5ff248159256e12bb1cb705/src/lib/dom.generated.d.ts#L503 - * https://developer.mozilla.org/en-US/docs/Web/API/File/File#options - */ -export interface FilePropertyBag extends BlobPropertyBag { - lastModified?: number; -} diff --git a/packages/sdk/src/internal/decoders/line.ts b/packages/sdk/src/internal/decoders/line.ts deleted file mode 100644 index b3bfa97cd..000000000 --- a/packages/sdk/src/internal/decoders/line.ts +++ /dev/null @@ -1,135 +0,0 @@ -import { concatBytes, decodeUTF8, encodeUTF8 } from '../utils/bytes'; - -export type Bytes = string | ArrayBuffer | Uint8Array | null | undefined; - -/** - * A re-implementation of httpx's `LineDecoder` in Python that handles incrementally - * reading lines from text. - * - * https://github.com/encode/httpx/blob/920333ea98118e9cf617f246905d7b202510941c/httpx/_decoders.py#L258 - */ -export class LineDecoder { - // prettier-ignore - static NEWLINE_CHARS = new Set(['\n', '\r']); - static NEWLINE_REGEXP = /\r\n|[\n\r]/g; - - #buffer: Uint8Array; - #carriageReturnIndex: number | null; - - constructor() { - this.#buffer = new Uint8Array(); - this.#carriageReturnIndex = null; - } - - decode(chunk: Bytes): string[] { - if (chunk == null) { - return []; - } - - const binaryChunk = - chunk instanceof ArrayBuffer ? new Uint8Array(chunk) - : typeof chunk === 'string' ? encodeUTF8(chunk) - : chunk; - - this.#buffer = concatBytes([this.#buffer, binaryChunk]); - - const lines: string[] = []; - let patternIndex; - while ((patternIndex = findNewlineIndex(this.#buffer, this.#carriageReturnIndex)) != null) { - if (patternIndex.carriage && this.#carriageReturnIndex == null) { - // skip until we either get a corresponding `\n`, a new `\r` or nothing - this.#carriageReturnIndex = patternIndex.index; - continue; - } - - // we got double \r or \rtext\n - if ( - this.#carriageReturnIndex != null && - (patternIndex.index !== this.#carriageReturnIndex + 1 || patternIndex.carriage) - ) { - lines.push(decodeUTF8(this.#buffer.subarray(0, this.#carriageReturnIndex - 1))); - this.#buffer = this.#buffer.subarray(this.#carriageReturnIndex); - this.#carriageReturnIndex = null; - continue; - } - - const endIndex = - this.#carriageReturnIndex !== null ? patternIndex.preceding - 1 : patternIndex.preceding; - - const line = decodeUTF8(this.#buffer.subarray(0, endIndex)); - lines.push(line); - - this.#buffer = this.#buffer.subarray(patternIndex.index); - this.#carriageReturnIndex = null; - } - - return lines; - } - - flush(): string[] { - if (!this.#buffer.length) { - return []; - } - return this.decode('\n'); - } -} - -/** - * This function searches the buffer for the end patterns, (\r or \n) - * and returns an object with the index preceding the matched newline and the - * index after the newline char. `null` is returned if no new line is found. - * - * ```ts - * findNewLineIndex('abc\ndef') -> { preceding: 2, index: 3 } - * ``` - */ -function findNewlineIndex( - buffer: Uint8Array, - startIndex: number | null, -): { preceding: number; index: number; carriage: boolean } | null { - const newline = 0x0a; // \n - const carriage = 0x0d; // \r - - for (let i = startIndex ?? 0; i < buffer.length; i++) { - if (buffer[i] === newline) { - return { preceding: i, index: i + 1, carriage: false }; - } - - if (buffer[i] === carriage) { - return { preceding: i, index: i + 1, carriage: true }; - } - } - - return null; -} - -export function findDoubleNewlineIndex(buffer: Uint8Array): number { - // This function searches the buffer for the end patterns (\r\r, \n\n, \r\n\r\n) - // and returns the index right after the first occurrence of any pattern, - // or -1 if none of the patterns are found. - const newline = 0x0a; // \n - const carriage = 0x0d; // \r - - for (let i = 0; i < buffer.length - 1; i++) { - if (buffer[i] === newline && buffer[i + 1] === newline) { - // \n\n - return i + 2; - } - if (buffer[i] === carriage && buffer[i + 1] === carriage) { - // \r\r - return i + 2; - } - if ( - buffer[i] === carriage && - buffer[i + 1] === newline && - i + 3 < buffer.length && - buffer[i + 2] === carriage && - buffer[i + 3] === newline - ) { - // \r\n\r\n - return i + 4; - } - } - - return -1; -} diff --git a/packages/sdk/src/internal/detect-platform.ts b/packages/sdk/src/internal/detect-platform.ts deleted file mode 100644 index e82d95c92..000000000 --- a/packages/sdk/src/internal/detect-platform.ts +++ /dev/null @@ -1,196 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { VERSION } from '../version'; - -export const isRunningInBrowser = () => { - return ( - // @ts-ignore - typeof window !== 'undefined' && - // @ts-ignore - typeof window.document !== 'undefined' && - // @ts-ignore - typeof navigator !== 'undefined' - ); -}; - -type DetectedPlatform = 'deno' | 'node' | 'edge' | 'unknown'; - -/** - * Note this does not detect 'browser'; for that, use getBrowserInfo(). - */ -function getDetectedPlatform(): DetectedPlatform { - if (typeof Deno !== 'undefined' && Deno.build != null) { - return 'deno'; - } - if (typeof EdgeRuntime !== 'undefined') { - return 'edge'; - } - if ( - Object.prototype.toString.call( - typeof (globalThis as any).process !== 'undefined' ? (globalThis as any).process : 0, - ) === '[object process]' - ) { - return 'node'; - } - return 'unknown'; -} - -declare const Deno: any; -declare const EdgeRuntime: any; -type Arch = 'x32' | 'x64' | 'arm' | 'arm64' | `other:${string}` | 'unknown'; -type PlatformName = - | 'MacOS' - | 'Linux' - | 'Windows' - | 'FreeBSD' - | 'OpenBSD' - | 'iOS' - | 'Android' - | `Other:${string}` - | 'Unknown'; -type Browser = 'ie' | 'edge' | 'chrome' | 'firefox' | 'safari'; -type PlatformProperties = { - 'X-Stainless-Lang': 'js'; - 'X-Stainless-Package-Version': string; - 'X-Stainless-OS': PlatformName; - 'X-Stainless-Arch': Arch; - 'X-Stainless-Runtime': 'node' | 'deno' | 'edge' | `browser:${Browser}` | 'unknown'; - 'X-Stainless-Runtime-Version': string; -}; -const getPlatformProperties = (): PlatformProperties => { - const detectedPlatform = getDetectedPlatform(); - if (detectedPlatform === 'deno') { - return { - 'X-Stainless-Lang': 'js', - 'X-Stainless-Package-Version': VERSION, - 'X-Stainless-OS': normalizePlatform(Deno.build.os), - 'X-Stainless-Arch': normalizeArch(Deno.build.arch), - 'X-Stainless-Runtime': 'deno', - 'X-Stainless-Runtime-Version': - typeof Deno.version === 'string' ? Deno.version : Deno.version?.deno ?? 'unknown', - }; - } - if (typeof EdgeRuntime !== 'undefined') { - return { - 'X-Stainless-Lang': 'js', - 'X-Stainless-Package-Version': VERSION, - 'X-Stainless-OS': 'Unknown', - 'X-Stainless-Arch': `other:${EdgeRuntime}`, - 'X-Stainless-Runtime': 'edge', - 'X-Stainless-Runtime-Version': (globalThis as any).process.version, - }; - } - // Check if Node.js - if (detectedPlatform === 'node') { - return { - 'X-Stainless-Lang': 'js', - 'X-Stainless-Package-Version': VERSION, - 'X-Stainless-OS': normalizePlatform((globalThis as any).process.platform ?? 'unknown'), - 'X-Stainless-Arch': normalizeArch((globalThis as any).process.arch ?? 'unknown'), - 'X-Stainless-Runtime': 'node', - 'X-Stainless-Runtime-Version': (globalThis as any).process.version ?? 'unknown', - }; - } - - const browserInfo = getBrowserInfo(); - if (browserInfo) { - return { - 'X-Stainless-Lang': 'js', - 'X-Stainless-Package-Version': VERSION, - 'X-Stainless-OS': 'Unknown', - 'X-Stainless-Arch': 'unknown', - 'X-Stainless-Runtime': `browser:${browserInfo.browser}`, - 'X-Stainless-Runtime-Version': browserInfo.version, - }; - } - - // TODO add support for Cloudflare workers, etc. - return { - 'X-Stainless-Lang': 'js', - 'X-Stainless-Package-Version': VERSION, - 'X-Stainless-OS': 'Unknown', - 'X-Stainless-Arch': 'unknown', - 'X-Stainless-Runtime': 'unknown', - 'X-Stainless-Runtime-Version': 'unknown', - }; -}; - -type BrowserInfo = { - browser: Browser; - version: string; -}; - -declare const navigator: { userAgent: string } | undefined; - -// Note: modified from https://github.com/JS-DevTools/host-environment/blob/b1ab79ecde37db5d6e163c050e54fe7d287d7c92/src/isomorphic.browser.ts -function getBrowserInfo(): BrowserInfo | null { - if (typeof navigator === 'undefined' || !navigator) { - return null; - } - - // NOTE: The order matters here! - const browserPatterns = [ - { key: 'edge' as const, pattern: /Edge(?:\W+(\d+)\.(\d+)(?:\.(\d+))?)?/ }, - { key: 'ie' as const, pattern: /MSIE(?:\W+(\d+)\.(\d+)(?:\.(\d+))?)?/ }, - { key: 'ie' as const, pattern: /Trident(?:.*rv\:(\d+)\.(\d+)(?:\.(\d+))?)?/ }, - { key: 'chrome' as const, pattern: /Chrome(?:\W+(\d+)\.(\d+)(?:\.(\d+))?)?/ }, - { key: 'firefox' as const, pattern: /Firefox(?:\W+(\d+)\.(\d+)(?:\.(\d+))?)?/ }, - { key: 'safari' as const, pattern: /(?:Version\W+(\d+)\.(\d+)(?:\.(\d+))?)?(?:\W+Mobile\S*)?\W+Safari/ }, - ]; - - // Find the FIRST matching browser - for (const { key, pattern } of browserPatterns) { - const match = pattern.exec(navigator.userAgent); - if (match) { - const major = match[1] || 0; - const minor = match[2] || 0; - const patch = match[3] || 0; - - return { browser: key, version: `${major}.${minor}.${patch}` }; - } - } - - return null; -} - -const normalizeArch = (arch: string): Arch => { - // Node docs: - // - https://nodejs.org/api/process.html#processarch - // Deno docs: - // - https://doc.deno.land/deno/stable/~/Deno.build - if (arch === 'x32') return 'x32'; - if (arch === 'x86_64' || arch === 'x64') return 'x64'; - if (arch === 'arm') return 'arm'; - if (arch === 'aarch64' || arch === 'arm64') return 'arm64'; - if (arch) return `other:${arch}`; - return 'unknown'; -}; - -const normalizePlatform = (platform: string): PlatformName => { - // Node platforms: - // - https://nodejs.org/api/process.html#processplatform - // Deno platforms: - // - https://doc.deno.land/deno/stable/~/Deno.build - // - https://github.com/denoland/deno/issues/14799 - - platform = platform.toLowerCase(); - - // NOTE: this iOS check is untested and may not work - // Node does not work natively on IOS, there is a fork at - // https://github.com/nodejs-mobile/nodejs-mobile - // however it is unknown at the time of writing how to detect if it is running - if (platform.includes('ios')) return 'iOS'; - if (platform === 'android') return 'Android'; - if (platform === 'darwin') return 'MacOS'; - if (platform === 'win32') return 'Windows'; - if (platform === 'freebsd') return 'FreeBSD'; - if (platform === 'openbsd') return 'OpenBSD'; - if (platform === 'linux') return 'Linux'; - if (platform) return `Other:${platform}`; - return 'Unknown'; -}; - -let _platformHeaders: PlatformProperties; -export const getPlatformHeaders = () => { - return (_platformHeaders ??= getPlatformProperties()); -}; diff --git a/packages/sdk/src/internal/errors.ts b/packages/sdk/src/internal/errors.ts deleted file mode 100644 index 82c7b14d5..000000000 --- a/packages/sdk/src/internal/errors.ts +++ /dev/null @@ -1,33 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -export function isAbortError(err: unknown) { - return ( - typeof err === 'object' && - err !== null && - // Spec-compliant fetch implementations - (('name' in err && (err as any).name === 'AbortError') || - // Expo fetch - ('message' in err && String((err as any).message).includes('FetchRequestCanceledException'))) - ); -} - -export const castToError = (err: any): Error => { - if (err instanceof Error) return err; - if (typeof err === 'object' && err !== null) { - try { - if (Object.prototype.toString.call(err) === '[object Error]') { - // @ts-ignore - not all envs have native support for cause yet - const error = new Error(err.message, err.cause ? { cause: err.cause } : {}); - if (err.stack) error.stack = err.stack; - // @ts-ignore - not all envs have native support for cause yet - if (err.cause && !error.cause) error.cause = err.cause; - if (err.name) error.name = err.name; - return error; - } - } catch {} - try { - return new Error(JSON.stringify(err)); - } catch {} - } - return new Error(err); -}; diff --git a/packages/sdk/src/internal/headers.ts b/packages/sdk/src/internal/headers.ts deleted file mode 100644 index c724a9d22..000000000 --- a/packages/sdk/src/internal/headers.ts +++ /dev/null @@ -1,97 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { isReadonlyArray } from './utils/values'; - -type HeaderValue = string | undefined | null; -export type HeadersLike = - | Headers - | readonly HeaderValue[][] - | Record<string, HeaderValue | readonly HeaderValue[]> - | undefined - | null - | NullableHeaders; - -const brand_privateNullableHeaders = /* @__PURE__ */ Symbol('brand.privateNullableHeaders'); - -/** - * @internal - * Users can pass explicit nulls to unset default headers. When we parse them - * into a standard headers type we need to preserve that information. - */ -export type NullableHeaders = { - /** Brand check, prevent users from creating a NullableHeaders. */ - [brand_privateNullableHeaders]: true; - /** Parsed headers. */ - values: Headers; - /** Set of lowercase header names explicitly set to null. */ - nulls: Set<string>; -}; - -function* iterateHeaders(headers: HeadersLike): IterableIterator<readonly [string, string | null]> { - if (!headers) return; - - if (brand_privateNullableHeaders in headers) { - const { values, nulls } = headers; - yield* values.entries(); - for (const name of nulls) { - yield [name, null]; - } - return; - } - - let shouldClear = false; - let iter: Iterable<readonly (HeaderValue | readonly HeaderValue[])[]>; - if (headers instanceof Headers) { - iter = headers.entries(); - } else if (isReadonlyArray(headers)) { - iter = headers; - } else { - shouldClear = true; - iter = Object.entries(headers ?? {}); - } - for (let row of iter) { - const name = row[0]; - if (typeof name !== 'string') throw new TypeError('expected header name to be a string'); - const values = isReadonlyArray(row[1]) ? row[1] : [row[1]]; - let didClear = false; - for (const value of values) { - if (value === undefined) continue; - - // Objects keys always overwrite older headers, they never append. - // Yield a null to clear the header before adding the new values. - if (shouldClear && !didClear) { - didClear = true; - yield [name, null]; - } - yield [name, value]; - } - } -} - -export const buildHeaders = (newHeaders: HeadersLike[]): NullableHeaders => { - const targetHeaders = new Headers(); - const nullHeaders = new Set<string>(); - for (const headers of newHeaders) { - const seenHeaders = new Set<string>(); - for (const [name, value] of iterateHeaders(headers)) { - const lowerName = name.toLowerCase(); - if (!seenHeaders.has(lowerName)) { - targetHeaders.delete(name); - seenHeaders.add(lowerName); - } - if (value === null) { - targetHeaders.delete(name); - nullHeaders.add(lowerName); - } else { - targetHeaders.append(name, value); - nullHeaders.delete(lowerName); - } - } - } - return { [brand_privateNullableHeaders]: true, values: targetHeaders, nulls: nullHeaders }; -}; - -export const isEmptyHeaders = (headers: HeadersLike) => { - for (const _ of iterateHeaders(headers)) return false; - return true; -}; diff --git a/packages/sdk/src/internal/parse.ts b/packages/sdk/src/internal/parse.ts deleted file mode 100644 index dcb4026e3..000000000 --- a/packages/sdk/src/internal/parse.ts +++ /dev/null @@ -1,64 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import type { FinalRequestOptions } from './request-options'; -import { Stream } from '../core/streaming'; -import { type Opencode } from '../client'; -import { formatRequestDetails, loggerFor } from './utils/log'; - -export type APIResponseProps = { - response: Response; - options: FinalRequestOptions; - controller: AbortController; - requestLogID: string; - retryOfRequestLogID: string | undefined; - startTime: number; -}; - -export async function defaultParseResponse<T>(client: Opencode, props: APIResponseProps): Promise<T> { - const { response, requestLogID, retryOfRequestLogID, startTime } = props; - const body = await (async () => { - if (props.options.stream) { - loggerFor(client).debug('response', response.status, response.url, response.headers, response.body); - - // Note: there is an invariant here that isn't represented in the type system - // that if you set `stream: true` the response type must also be `Stream<T>` - - if (props.options.__streamClass) { - return props.options.__streamClass.fromSSEResponse(response, props.controller, client) as any; - } - - return Stream.fromSSEResponse(response, props.controller, client) as any; - } - - // fetch refuses to read the body when the status code is 204. - if (response.status === 204) { - return null as T; - } - - if (props.options.__binaryResponse) { - return response as unknown as T; - } - - const contentType = response.headers.get('content-type'); - const mediaType = contentType?.split(';')[0]?.trim(); - const isJSON = mediaType?.includes('application/json') || mediaType?.endsWith('+json'); - if (isJSON) { - const json = await response.json(); - return json as T; - } - - const text = await response.text(); - return text as unknown as T; - })(); - loggerFor(client).debug( - `[${requestLogID}] response parsed`, - formatRequestDetails({ - retryOfRequestLogID, - url: response.url, - status: response.status, - body, - durationMs: Date.now() - startTime, - }), - ); - return body; -} diff --git a/packages/sdk/src/internal/request-options.ts b/packages/sdk/src/internal/request-options.ts deleted file mode 100644 index 56765e5aa..000000000 --- a/packages/sdk/src/internal/request-options.ts +++ /dev/null @@ -1,93 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { NullableHeaders } from './headers'; - -import type { BodyInit } from './builtin-types'; -import { Stream } from '../core/streaming'; -import type { HTTPMethod, MergedRequestInit } from './types'; -import { type HeadersLike } from './headers'; - -export type FinalRequestOptions = RequestOptions & { method: HTTPMethod; path: string }; - -export type RequestOptions = { - /** - * The HTTP method for the request (e.g., 'get', 'post', 'put', 'delete'). - */ - method?: HTTPMethod; - - /** - * The URL path for the request. - * - * @example "/v1/foo" - */ - path?: string; - - /** - * Query parameters to include in the request URL. - */ - query?: object | undefined | null; - - /** - * The request body. Can be a string, JSON object, FormData, or other supported types. - */ - body?: unknown; - - /** - * HTTP headers to include with the request. Can be a Headers object, plain object, or array of tuples. - */ - headers?: HeadersLike; - - /** - * 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; - - stream?: boolean | 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. - * - * @unit milliseconds - */ - timeout?: number; - - /** - * Additional `RequestInit` options to be passed to the underlying `fetch` call. - * These options will be merged with the client's default fetch options. - */ - fetchOptions?: MergedRequestInit; - - /** - * An AbortSignal that can be used to cancel the request. - */ - signal?: AbortSignal | undefined | null; - - /** - * A unique key for this request to enable idempotency. - */ - idempotencyKey?: string; - - /** - * Override the default base URL for this specific request. - */ - defaultBaseURL?: string | undefined; - - __binaryResponse?: boolean | undefined; - __streamClass?: typeof Stream; -}; - -export type EncodedContent = { bodyHeaders: HeadersLike; body: BodyInit }; -export type RequestEncoder = (request: { headers: NullableHeaders; body: unknown }) => EncodedContent; - -export const FallbackEncoder: RequestEncoder = ({ headers, body }) => { - return { - bodyHeaders: { - 'content-type': 'application/json', - }, - body: JSON.stringify(body), - }; -}; diff --git a/packages/sdk/src/internal/shim-types.ts b/packages/sdk/src/internal/shim-types.ts deleted file mode 100644 index 8ddf7b0ad..000000000 --- a/packages/sdk/src/internal/shim-types.ts +++ /dev/null @@ -1,26 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -/** - * Shims for types that we can't always rely on being available globally. - * - * Note: these only exist at the type-level, there is no corresponding runtime - * version for any of these symbols. - */ - -type NeverToAny<T> = T extends never ? any : T; - -/** @ts-ignore */ -type _DOMReadableStream<R = any> = globalThis.ReadableStream<R>; - -/** @ts-ignore */ -type _NodeReadableStream<R = any> = import('stream/web').ReadableStream<R>; - -type _ConditionalNodeReadableStream<R = any> = - typeof globalThis extends { ReadableStream: any } ? never : _NodeReadableStream<R>; - -type _ReadableStream<R = any> = NeverToAny< - | ([0] extends [1 & _DOMReadableStream<R>] ? never : _DOMReadableStream<R>) - | ([0] extends [1 & _ConditionalNodeReadableStream<R>] ? never : _ConditionalNodeReadableStream<R>) ->; - -export type { _ReadableStream as ReadableStream }; diff --git a/packages/sdk/src/internal/shims.ts b/packages/sdk/src/internal/shims.ts deleted file mode 100644 index 17a7967aa..000000000 --- a/packages/sdk/src/internal/shims.ts +++ /dev/null @@ -1,107 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -/** - * This module provides internal shims and utility functions for environments where certain Node.js or global types may not be available. - * - * These are used to ensure we can provide a consistent behaviour between different JavaScript environments and good error - * messages in cases where an environment isn't fully supported. - */ - -import type { Fetch } from './builtin-types'; -import type { ReadableStream } from './shim-types'; - -export function getDefaultFetch(): Fetch { - if (typeof fetch !== 'undefined') { - return fetch as any; - } - - throw new Error( - '`fetch` is not defined as a global; Either pass `fetch` to the client, `new Opencode({ fetch })` or polyfill the global, `globalThis.fetch = fetch`', - ); -} - -type ReadableStreamArgs = ConstructorParameters<typeof ReadableStream>; - -export function makeReadableStream(...args: ReadableStreamArgs): ReadableStream { - const ReadableStream = (globalThis as any).ReadableStream; - if (typeof ReadableStream === 'undefined') { - // Note: All of the platforms / runtimes we officially support already define - // `ReadableStream` as a global, so this should only ever be hit on unsupported runtimes. - throw new Error( - '`ReadableStream` is not defined as a global; You will need to polyfill it, `globalThis.ReadableStream = ReadableStream`', - ); - } - - return new ReadableStream(...args); -} - -export function ReadableStreamFrom<T>(iterable: Iterable<T> | AsyncIterable<T>): ReadableStream<T> { - let iter: AsyncIterator<T> | Iterator<T> = - Symbol.asyncIterator in iterable ? iterable[Symbol.asyncIterator]() : iterable[Symbol.iterator](); - - return makeReadableStream({ - start() {}, - async pull(controller: any) { - const { done, value } = await iter.next(); - if (done) { - controller.close(); - } else { - controller.enqueue(value); - } - }, - async cancel() { - await iter.return?.(); - }, - }); -} - -/** - * Most browsers don't yet have async iterable support for ReadableStream, - * and Node has a very different way of reading bytes from its "ReadableStream". - * - * This polyfill was pulled from https://github.com/MattiasBuelens/web-streams-polyfill/pull/122#issuecomment-1627354490 - */ -export function ReadableStreamToAsyncIterable<T>(stream: any): AsyncIterableIterator<T> { - if (stream[Symbol.asyncIterator]) return stream; - - const reader = stream.getReader(); - return { - async next() { - try { - const result = await reader.read(); - if (result?.done) reader.releaseLock(); // release lock when stream becomes closed - return result; - } catch (e) { - reader.releaseLock(); // release lock when stream becomes errored - throw e; - } - }, - async return() { - const cancelPromise = reader.cancel(); - reader.releaseLock(); - await cancelPromise; - return { done: true, value: undefined }; - }, - [Symbol.asyncIterator]() { - return this; - }, - }; -} - -/** - * Cancels a ReadableStream we don't need to consume. - * See https://undici.nodejs.org/#/?id=garbage-collection - */ -export async function CancelReadableStream(stream: any): Promise<void> { - if (stream === null || typeof stream !== 'object') return; - - if (stream[Symbol.asyncIterator]) { - await stream[Symbol.asyncIterator]().return?.(); - return; - } - - const reader = stream.getReader(); - const cancelPromise = reader.cancel(); - reader.releaseLock(); - await cancelPromise; -} diff --git a/packages/sdk/src/internal/to-file.ts b/packages/sdk/src/internal/to-file.ts deleted file mode 100644 index 245e84933..000000000 --- a/packages/sdk/src/internal/to-file.ts +++ /dev/null @@ -1,154 +0,0 @@ -import { BlobPart, getName, makeFile, isAsyncIterable } from './uploads'; -import type { FilePropertyBag } from './builtin-types'; -import { checkFileSupport } from './uploads'; - -type BlobLikePart = string | ArrayBuffer | ArrayBufferView | BlobLike | DataView; - -/** - * Intended to match DOM Blob, node-fetch Blob, node:buffer Blob, etc. - * Don't add arrayBuffer here, node-fetch doesn't have it - */ -interface BlobLike { - /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/size) */ - readonly size: number; - /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/type) */ - readonly type: string; - /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/text) */ - text(): Promise<string>; - /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/slice) */ - slice(start?: number, end?: number): BlobLike; -} - -/** - * This check adds the arrayBuffer() method type because it is available and used at runtime - */ -const isBlobLike = (value: any): value is BlobLike & { arrayBuffer(): Promise<ArrayBuffer> } => - value != null && - typeof value === 'object' && - typeof value.size === 'number' && - typeof value.type === 'string' && - typeof value.text === 'function' && - typeof value.slice === 'function' && - typeof value.arrayBuffer === 'function'; - -/** - * Intended to match DOM File, node:buffer File, undici File, etc. - */ -interface FileLike extends BlobLike { - /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/File/lastModified) */ - readonly lastModified: number; - /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/File/name) */ - readonly name?: string | undefined; -} - -/** - * This check adds the arrayBuffer() method type because it is available and used at runtime - */ -const isFileLike = (value: any): value is FileLike & { arrayBuffer(): Promise<ArrayBuffer> } => - value != null && - typeof value === 'object' && - typeof value.name === 'string' && - typeof value.lastModified === 'number' && - isBlobLike(value); - -/** - * Intended to match DOM Response, node-fetch Response, undici Response, etc. - */ -export interface ResponseLike { - url: string; - blob(): Promise<BlobLike>; -} - -const isResponseLike = (value: any): value is ResponseLike => - value != null && - typeof value === 'object' && - typeof value.url === 'string' && - typeof value.blob === 'function'; - -export type ToFileInput = - | FileLike - | ResponseLike - | Exclude<BlobLikePart, string> - | AsyncIterable<BlobLikePart>; - -/** - * Helper for creating a {@link File} to pass to an SDK upload method from a variety of different data formats - * @param value the raw content of the file. Can be an {@link Uploadable}, {@link BlobLikePart}, or {@link AsyncIterable} of {@link BlobLikePart}s - * @param {string=} name the name of the file. If omitted, toFile will try to determine a file name from bits if possible - * @param {Object=} options additional properties - * @param {string=} options.type the MIME type of the content - * @param {number=} options.lastModified the last modified timestamp - * @returns a {@link File} with the given properties - */ -export async function toFile( - value: ToFileInput | PromiseLike<ToFileInput>, - name?: string | null | undefined, - options?: FilePropertyBag | undefined, -): Promise<File> { - checkFileSupport(); - - // If it's a promise, resolve it. - value = await value; - - // If we've been given a `File` we don't need to do anything - if (isFileLike(value)) { - if (value instanceof File) { - return value; - } - return makeFile([await value.arrayBuffer()], value.name); - } - - if (isResponseLike(value)) { - const blob = await value.blob(); - name ||= new URL(value.url).pathname.split(/[\\/]/).pop(); - - return makeFile(await getBytes(blob), name, options); - } - - const parts = await getBytes(value); - - name ||= getName(value); - - if (!options?.type) { - const type = parts.find((part) => typeof part === 'object' && 'type' in part && part.type); - if (typeof type === 'string') { - options = { ...options, type }; - } - } - - return makeFile(parts, name, options); -} - -async function getBytes(value: BlobLikePart | AsyncIterable<BlobLikePart>): Promise<Array<BlobPart>> { - let parts: Array<BlobPart> = []; - if ( - typeof value === 'string' || - ArrayBuffer.isView(value) || // includes Uint8Array, Buffer, etc. - value instanceof ArrayBuffer - ) { - parts.push(value); - } else if (isBlobLike(value)) { - parts.push(value instanceof Blob ? value : await value.arrayBuffer()); - } else if ( - isAsyncIterable(value) // includes Readable, ReadableStream, etc. - ) { - for await (const chunk of value) { - parts.push(...(await getBytes(chunk as BlobLikePart))); // TODO, consider validating? - } - } else { - const constructor = value?.constructor?.name; - throw new Error( - `Unexpected data type: ${typeof value}${ - constructor ? `; constructor: ${constructor}` : '' - }${propsForError(value)}`, - ); - } - - return parts; -} - -function propsForError(value: unknown): string { - if (typeof value !== 'object' || value === null) return ''; - const props = Object.getOwnPropertyNames(value); - return `; props: [${props.map((p) => `"${p}"`).join(', ')}]`; -} diff --git a/packages/sdk/src/internal/types.ts b/packages/sdk/src/internal/types.ts deleted file mode 100644 index b668dfc0f..000000000 --- a/packages/sdk/src/internal/types.ts +++ /dev/null @@ -1,95 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -export type PromiseOrValue<T> = T | Promise<T>; -export type HTTPMethod = 'get' | 'post' | 'put' | 'patch' | 'delete'; - -export type KeysEnum<T> = { [P in keyof Required<T>]: true }; - -export type FinalizedRequestInit = RequestInit & { headers: Headers }; - -type NotAny<T> = [0] extends [1 & T] ? never : T; - -/** - * Some environments overload the global fetch function, and Parameters<T> only gets the last signature. - */ -type OverloadedParameters<T> = - T extends ( - { - (...args: infer A): unknown; - (...args: infer B): unknown; - (...args: infer C): unknown; - (...args: infer D): unknown; - } - ) ? - A | B | C | D - : T extends ( - { - (...args: infer A): unknown; - (...args: infer B): unknown; - (...args: infer C): unknown; - } - ) ? - A | B | C - : T extends ( - { - (...args: infer A): unknown; - (...args: infer B): unknown; - } - ) ? - A | B - : T extends (...args: infer A) => unknown ? A - : never; - -/* eslint-disable */ -/** - * These imports attempt to get types from a parent package's dependencies. - * Unresolved bare specifiers can trigger [automatic type acquisition][1] in some projects, which - * would cause typescript to show types not present at runtime. To avoid this, we import - * directly from parent node_modules folders. - * - * We need to check multiple levels because we don't know what directory structure we'll be in. - * For example, pnpm generates directories like this: - * ``` - * node_modules - * ├── .pnpm - * │ └── [email protected] - * │ └── node_modules - * │ └── pkg - * │ └── internal - * │ └── types.d.ts - * ├── pkg -> .pnpm/[email protected]/node_modules/pkg - * └── undici - * ``` - * - * [1]: https://www.typescriptlang.org/tsconfig/#typeAcquisition - */ -/** @ts-ignore For users with \@types/node */ -type UndiciTypesRequestInit = NotAny<import('../node_modules/undici-types/index.d.ts').RequestInit> | NotAny<import('../../node_modules/undici-types/index.d.ts').RequestInit> | NotAny<import('../../../node_modules/undici-types/index.d.ts').RequestInit> | NotAny<import('../../../../node_modules/undici-types/index.d.ts').RequestInit> | NotAny<import('../../../../../node_modules/undici-types/index.d.ts').RequestInit> | NotAny<import('../../../../../../node_modules/undici-types/index.d.ts').RequestInit> | NotAny<import('../../../../../../../node_modules/undici-types/index.d.ts').RequestInit> | NotAny<import('../../../../../../../../node_modules/undici-types/index.d.ts').RequestInit> | NotAny<import('../../../../../../../../../node_modules/undici-types/index.d.ts').RequestInit> | NotAny<import('../../../../../../../../../../node_modules/undici-types/index.d.ts').RequestInit>; -/** @ts-ignore For users with undici */ -type UndiciRequestInit = NotAny<import('../node_modules/undici/index.d.ts').RequestInit> | NotAny<import('../../node_modules/undici/index.d.ts').RequestInit> | NotAny<import('../../../node_modules/undici/index.d.ts').RequestInit> | NotAny<import('../../../../node_modules/undici/index.d.ts').RequestInit> | NotAny<import('../../../../../node_modules/undici/index.d.ts').RequestInit> | NotAny<import('../../../../../../node_modules/undici/index.d.ts').RequestInit> | NotAny<import('../../../../../../../node_modules/undici/index.d.ts').RequestInit> | NotAny<import('../../../../../../../../node_modules/undici/index.d.ts').RequestInit> | NotAny<import('../../../../../../../../../node_modules/undici/index.d.ts').RequestInit> | NotAny<import('../../../../../../../../../../node_modules/undici/index.d.ts').RequestInit>; -/** @ts-ignore For users with \@types/bun */ -type BunRequestInit = globalThis.FetchRequestInit; -/** @ts-ignore For users with node-fetch@2 */ -type NodeFetch2RequestInit = NotAny<import('../node_modules/@types/node-fetch/index.d.ts').RequestInit> | NotAny<import('../../node_modules/@types/node-fetch/index.d.ts').RequestInit> | NotAny<import('../../../node_modules/@types/node-fetch/index.d.ts').RequestInit> | NotAny<import('../../../../node_modules/@types/node-fetch/index.d.ts').RequestInit> | NotAny<import('../../../../../node_modules/@types/node-fetch/index.d.ts').RequestInit> | NotAny<import('../../../../../../node_modules/@types/node-fetch/index.d.ts').RequestInit> | NotAny<import('../../../../../../../node_modules/@types/node-fetch/index.d.ts').RequestInit> | NotAny<import('../../../../../../../../node_modules/@types/node-fetch/index.d.ts').RequestInit> | NotAny<import('../../../../../../../../../node_modules/@types/node-fetch/index.d.ts').RequestInit> | NotAny<import('../../../../../../../../../../node_modules/@types/node-fetch/index.d.ts').RequestInit>; -/** @ts-ignore For users with node-fetch@3, doesn't need file extension because types are at ./@types/index.d.ts */ -type NodeFetch3RequestInit = NotAny<import('../node_modules/node-fetch').RequestInit> | NotAny<import('../../node_modules/node-fetch').RequestInit> | NotAny<import('../../../node_modules/node-fetch').RequestInit> | NotAny<import('../../../../node_modules/node-fetch').RequestInit> | NotAny<import('../../../../../node_modules/node-fetch').RequestInit> | NotAny<import('../../../../../../node_modules/node-fetch').RequestInit> | NotAny<import('../../../../../../../node_modules/node-fetch').RequestInit> | NotAny<import('../../../../../../../../node_modules/node-fetch').RequestInit> | NotAny<import('../../../../../../../../../node_modules/node-fetch').RequestInit> | NotAny<import('../../../../../../../../../../node_modules/node-fetch').RequestInit>; -/** @ts-ignore For users who use Deno */ -type FetchRequestInit = NonNullable<OverloadedParameters<typeof fetch>[1]>; -/* eslint-enable */ - -type RequestInits = - | NotAny<UndiciTypesRequestInit> - | NotAny<UndiciRequestInit> - | NotAny<BunRequestInit> - | NotAny<NodeFetch2RequestInit> - | NotAny<NodeFetch3RequestInit> - | NotAny<RequestInit> - | NotAny<FetchRequestInit>; - -/** - * This type contains `RequestInit` options that may be available on the current runtime, - * including per-platform extensions like `dispatcher`, `agent`, `client`, etc. - */ -export type MergedRequestInit = RequestInits & - /** We don't include these in the types as they'll be overridden for every request. */ - Partial<Record<'body' | 'headers' | 'method' | 'signal', never>>; diff --git a/packages/sdk/src/internal/uploads.ts b/packages/sdk/src/internal/uploads.ts deleted file mode 100644 index eb55f834e..000000000 --- a/packages/sdk/src/internal/uploads.ts +++ /dev/null @@ -1,187 +0,0 @@ -import { type RequestOptions } from './request-options'; -import type { FilePropertyBag, Fetch } from './builtin-types'; -import type { Opencode } from '../client'; -import { ReadableStreamFrom } from './shims'; - -export type BlobPart = string | ArrayBuffer | ArrayBufferView | Blob | DataView; -type FsReadStream = AsyncIterable<Uint8Array> & { path: string | { toString(): string } }; - -// https://github.com/oven-sh/bun/issues/5980 -interface BunFile extends Blob { - readonly name?: string | undefined; -} - -export const checkFileSupport = () => { - if (typeof File === 'undefined') { - const { process } = globalThis as any; - const isOldNode = - typeof process?.versions?.node === 'string' && parseInt(process.versions.node.split('.')) < 20; - throw new Error( - '`File` is not defined as a global, which is required for file uploads.' + - (isOldNode ? - " Update to Node 20 LTS or newer, or set `globalThis.File` to `import('node:buffer').File`." - : ''), - ); - } -}; - -/** - * Typically, this is a native "File" class. - * - * We provide the {@link toFile} utility to convert a variety of objects - * into the File class. - * - * For convenience, you can also pass a fetch Response, or in Node, - * the result of fs.createReadStream(). - */ -export type Uploadable = File | Response | FsReadStream | BunFile; - -/** - * Construct a `File` instance. This is used to ensure a helpful error is thrown - * for environments that don't define a global `File` yet. - */ -export function makeFile( - fileBits: BlobPart[], - fileName: string | undefined, - options?: FilePropertyBag, -): File { - checkFileSupport(); - return new File(fileBits as any, fileName ?? 'unknown_file', options); -} - -export function getName(value: any): string | undefined { - return ( - ( - (typeof value === 'object' && - value !== null && - (('name' in value && value.name && String(value.name)) || - ('url' in value && value.url && String(value.url)) || - ('filename' in value && value.filename && String(value.filename)) || - ('path' in value && value.path && String(value.path)))) || - '' - ) - .split(/[\\/]/) - .pop() || undefined - ); -} - -export const isAsyncIterable = (value: any): value is AsyncIterable<any> => - value != null && typeof value === 'object' && typeof value[Symbol.asyncIterator] === 'function'; - -/** - * Returns a multipart/form-data request if any part of the given request body contains a File / Blob value. - * Otherwise returns the request as is. - */ -export const maybeMultipartFormRequestOptions = async ( - opts: RequestOptions, - fetch: Opencode | Fetch, -): Promise<RequestOptions> => { - if (!hasUploadableValue(opts.body)) return opts; - - return { ...opts, body: await createForm(opts.body, fetch) }; -}; - -type MultipartFormRequestOptions = Omit<RequestOptions, 'body'> & { body: unknown }; - -export const multipartFormRequestOptions = async ( - opts: MultipartFormRequestOptions, - fetch: Opencode | Fetch, -): Promise<RequestOptions> => { - return { ...opts, body: await createForm(opts.body, fetch) }; -}; - -const supportsFormDataMap = /* @__PURE__ */ new WeakMap<Fetch, Promise<boolean>>(); - -/** - * node-fetch doesn't support the global FormData object in recent node versions. Instead of sending - * properly-encoded form data, it just stringifies the object, resulting in a request body of "[object FormData]". - * This function detects if the fetch function provided supports the global FormData object to avoid - * confusing error messages later on. - */ -function supportsFormData(fetchObject: Opencode | Fetch): Promise<boolean> { - const fetch: Fetch = typeof fetchObject === 'function' ? fetchObject : (fetchObject as any).fetch; - const cached = supportsFormDataMap.get(fetch); - if (cached) return cached; - const promise = (async () => { - try { - const FetchResponse = ( - 'Response' in fetch ? - fetch.Response - : (await fetch('data:,')).constructor) as typeof Response; - const data = new FormData(); - if (data.toString() === (await new FetchResponse(data).text())) { - return false; - } - return true; - } catch { - // avoid false negatives - return true; - } - })(); - supportsFormDataMap.set(fetch, promise); - return promise; -} - -export const createForm = async <T = Record<string, unknown>>( - body: T | undefined, - fetch: Opencode | Fetch, -): Promise<FormData> => { - if (!(await supportsFormData(fetch))) { - throw new TypeError( - 'The provided fetch function does not support file uploads with the current global FormData class.', - ); - } - const form = new FormData(); - await Promise.all(Object.entries(body || {}).map(([key, value]) => addFormValue(form, key, value))); - return form; -}; - -// We check for Blob not File because Bun.File doesn't inherit from File, -// but they both inherit from Blob and have a `name` property at runtime. -const isNamedBlob = (value: unknown) => value instanceof Blob && 'name' in value; - -const isUploadable = (value: unknown) => - typeof value === 'object' && - value !== null && - (value instanceof Response || isAsyncIterable(value) || isNamedBlob(value)); - -const hasUploadableValue = (value: unknown): boolean => { - if (isUploadable(value)) return true; - if (Array.isArray(value)) return value.some(hasUploadableValue); - if (value && typeof value === 'object') { - for (const k in value) { - if (hasUploadableValue((value as any)[k])) return true; - } - } - return false; -}; - -const addFormValue = async (form: FormData, key: string, value: unknown): Promise<void> => { - if (value === undefined) return; - if (value == null) { - throw new TypeError( - `Received null for "${key}"; to pass null in FormData, you must use the string 'null'`, - ); - } - - // TODO: make nested formats configurable - if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') { - form.append(key, String(value)); - } else if (value instanceof Response) { - form.append(key, makeFile([await value.blob()], getName(value))); - } else if (isAsyncIterable(value)) { - form.append(key, makeFile([await new Response(ReadableStreamFrom(value)).blob()], getName(value))); - } else if (isNamedBlob(value)) { - form.append(key, value, getName(value)); - } else if (Array.isArray(value)) { - await Promise.all(value.map((entry) => addFormValue(form, key + '[]', entry))); - } else if (typeof value === 'object') { - await Promise.all( - Object.entries(value).map(([name, prop]) => addFormValue(form, `${key}[${name}]`, prop)), - ); - } else { - throw new TypeError( - `Invalid value given to form, expected a string, number, boolean, object, Array, File or Blob but got ${value} instead`, - ); - } -}; diff --git a/packages/sdk/src/internal/utils.ts b/packages/sdk/src/internal/utils.ts deleted file mode 100644 index 3cbfacce2..000000000 --- a/packages/sdk/src/internal/utils.ts +++ /dev/null @@ -1,8 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -export * from './utils/values'; -export * from './utils/base64'; -export * from './utils/env'; -export * from './utils/log'; -export * from './utils/uuid'; -export * from './utils/sleep'; diff --git a/packages/sdk/src/internal/utils/base64.ts b/packages/sdk/src/internal/utils/base64.ts deleted file mode 100644 index 05d7bd9b4..000000000 --- a/packages/sdk/src/internal/utils/base64.ts +++ /dev/null @@ -1,40 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { OpencodeError } from '../../core/error'; -import { encodeUTF8 } from './bytes'; - -export const toBase64 = (data: string | Uint8Array | null | undefined): string => { - if (!data) return ''; - - if (typeof (globalThis as any).Buffer !== 'undefined') { - return (globalThis as any).Buffer.from(data).toString('base64'); - } - - if (typeof data === 'string') { - data = encodeUTF8(data); - } - - if (typeof btoa !== 'undefined') { - return btoa(String.fromCharCode.apply(null, data as any)); - } - - throw new OpencodeError('Cannot generate base64 string; Expected `Buffer` or `btoa` to be defined'); -}; - -export const fromBase64 = (str: string): Uint8Array => { - if (typeof (globalThis as any).Buffer !== 'undefined') { - const buf = (globalThis as any).Buffer.from(str, 'base64'); - return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength); - } - - if (typeof atob !== 'undefined') { - const bstr = atob(str); - const buf = new Uint8Array(bstr.length); - for (let i = 0; i < bstr.length; i++) { - buf[i] = bstr.charCodeAt(i); - } - return buf; - } - - throw new OpencodeError('Cannot decode base64 string; Expected `Buffer` or `atob` to be defined'); -}; diff --git a/packages/sdk/src/internal/utils/bytes.ts b/packages/sdk/src/internal/utils/bytes.ts deleted file mode 100644 index 8da627abe..000000000 --- a/packages/sdk/src/internal/utils/bytes.ts +++ /dev/null @@ -1,32 +0,0 @@ -export function concatBytes(buffers: Uint8Array[]): Uint8Array { - let length = 0; - for (const buffer of buffers) { - length += buffer.length; - } - const output = new Uint8Array(length); - let index = 0; - for (const buffer of buffers) { - output.set(buffer, index); - index += buffer.length; - } - - return output; -} - -let encodeUTF8_: (str: string) => Uint8Array; -export function encodeUTF8(str: string) { - let encoder; - return ( - encodeUTF8_ ?? - ((encoder = new (globalThis as any).TextEncoder()), (encodeUTF8_ = encoder.encode.bind(encoder))) - )(str); -} - -let decodeUTF8_: (bytes: Uint8Array) => string; -export function decodeUTF8(bytes: Uint8Array) { - let decoder; - return ( - decodeUTF8_ ?? - ((decoder = new (globalThis as any).TextDecoder()), (decodeUTF8_ = decoder.decode.bind(decoder))) - )(bytes); -} diff --git a/packages/sdk/src/internal/utils/env.ts b/packages/sdk/src/internal/utils/env.ts deleted file mode 100644 index 2d8480077..000000000 --- a/packages/sdk/src/internal/utils/env.ts +++ /dev/null @@ -1,18 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -/** - * Read an environment variable. - * - * Trims beginning and trailing whitespace. - * - * Will return undefined if the environment variable doesn't exist or cannot be accessed. - */ -export const readEnv = (env: string): string | undefined => { - if (typeof (globalThis as any).process !== 'undefined') { - return (globalThis as any).process.env?.[env]?.trim() ?? undefined; - } - if (typeof (globalThis as any).Deno !== 'undefined') { - return (globalThis as any).Deno.env?.get?.(env)?.trim(); - } - return undefined; -}; diff --git a/packages/sdk/src/internal/utils/log.ts b/packages/sdk/src/internal/utils/log.ts deleted file mode 100644 index 44ac16a02..000000000 --- a/packages/sdk/src/internal/utils/log.ts +++ /dev/null @@ -1,126 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { hasOwn } from './values'; -import { type Opencode } from '../../client'; -import { RequestOptions } from '../request-options'; - -type LogFn = (message: string, ...rest: unknown[]) => void; -export type Logger = { - error: LogFn; - warn: LogFn; - info: LogFn; - debug: LogFn; -}; -export type LogLevel = 'off' | 'error' | 'warn' | 'info' | 'debug'; - -const levelNumbers = { - off: 0, - error: 200, - warn: 300, - info: 400, - debug: 500, -}; - -export const parseLogLevel = ( - maybeLevel: string | undefined, - sourceName: string, - client: Opencode, -): LogLevel | undefined => { - if (!maybeLevel) { - return undefined; - } - if (hasOwn(levelNumbers, maybeLevel)) { - return maybeLevel; - } - loggerFor(client).warn( - `${sourceName} was set to ${JSON.stringify(maybeLevel)}, expected one of ${JSON.stringify( - Object.keys(levelNumbers), - )}`, - ); - return undefined; -}; - -function noop() {} - -function makeLogFn(fnLevel: keyof Logger, logger: Logger | undefined, logLevel: LogLevel) { - if (!logger || levelNumbers[fnLevel] > levelNumbers[logLevel]) { - return noop; - } else { - // Don't wrap logger functions, we want the stacktrace intact! - return logger[fnLevel].bind(logger); - } -} - -const noopLogger = { - error: noop, - warn: noop, - info: noop, - debug: noop, -}; - -let cachedLoggers = /* @__PURE__ */ new WeakMap<Logger, [LogLevel, Logger]>(); - -export function loggerFor(client: Opencode): Logger { - const logger = client.logger; - const logLevel = client.logLevel ?? 'off'; - if (!logger) { - return noopLogger; - } - - const cachedLogger = cachedLoggers.get(logger); - if (cachedLogger && cachedLogger[0] === logLevel) { - return cachedLogger[1]; - } - - const levelLogger = { - error: makeLogFn('error', logger, logLevel), - warn: makeLogFn('warn', logger, logLevel), - info: makeLogFn('info', logger, logLevel), - debug: makeLogFn('debug', logger, logLevel), - }; - - cachedLoggers.set(logger, [logLevel, levelLogger]); - - return levelLogger; -} - -export const formatRequestDetails = (details: { - options?: RequestOptions | undefined; - headers?: Headers | Record<string, string> | undefined; - retryOfRequestLogID?: string | undefined; - retryOf?: string | undefined; - url?: string | undefined; - status?: number | undefined; - method?: string | undefined; - durationMs?: number | undefined; - message?: unknown; - body?: unknown; -}) => { - if (details.options) { - details.options = { ...details.options }; - delete details.options['headers']; // redundant + leaks internals - } - if (details.headers) { - details.headers = Object.fromEntries( - (details.headers instanceof Headers ? [...details.headers] : Object.entries(details.headers)).map( - ([name, value]) => [ - name, - ( - name.toLowerCase() === 'authorization' || - name.toLowerCase() === 'cookie' || - name.toLowerCase() === 'set-cookie' - ) ? - '***' - : value, - ], - ), - ); - } - if ('retryOfRequestLogID' in details) { - if (details.retryOfRequestLogID) { - details.retryOf = details.retryOfRequestLogID; - } - delete details.retryOfRequestLogID; - } - return details; -}; diff --git a/packages/sdk/src/internal/utils/path.ts b/packages/sdk/src/internal/utils/path.ts deleted file mode 100644 index 6ca96e0dd..000000000 --- a/packages/sdk/src/internal/utils/path.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { OpencodeError } from '../../core/error'; - -/** - * Percent-encode everything that isn't safe to have in a path without encoding safe chars. - * - * Taken from https://datatracker.ietf.org/doc/html/rfc3986#section-3.3: - * > unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" - * > sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "=" - * > pchar = unreserved / pct-encoded / sub-delims / ":" / "@" - */ -export function encodeURIPath(str: string) { - return str.replace(/[^A-Za-z0-9\-._~!$&'()*+,;=:@]+/g, encodeURIComponent); -} - -const EMPTY = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.create(null)); - -export const createPathTagFunction = (pathEncoder = encodeURIPath) => - function path(statics: readonly string[], ...params: readonly unknown[]): string { - // If there are no params, no processing is needed. - if (statics.length === 1) return statics[0]!; - - let postPath = false; - const invalidSegments = []; - const path = statics.reduce((previousValue, currentValue, index) => { - if (/[?#]/.test(currentValue)) { - postPath = true; - } - const value = params[index]; - let encoded = (postPath ? encodeURIComponent : pathEncoder)('' + value); - if ( - index !== params.length && - (value == null || - (typeof value === 'object' && - // handle values from other realms - value.toString === - Object.getPrototypeOf(Object.getPrototypeOf((value as any).hasOwnProperty ?? EMPTY) ?? EMPTY) - ?.toString)) - ) { - encoded = value + ''; - invalidSegments.push({ - start: previousValue.length + currentValue.length, - length: encoded.length, - error: `Value of type ${Object.prototype.toString - .call(value) - .slice(8, -1)} is not a valid path parameter`, - }); - } - return previousValue + currentValue + (index === params.length ? '' : encoded); - }, ''); - - const pathOnly = path.split(/[?#]/, 1)[0]!; - const invalidSegmentPattern = /(?<=^|\/)(?:\.|%2e){1,2}(?=\/|$)/gi; - let match; - - // Find all invalid segments - while ((match = invalidSegmentPattern.exec(pathOnly)) !== null) { - invalidSegments.push({ - start: match.index, - length: match[0].length, - error: `Value "${match[0]}" can\'t be safely passed as a path parameter`, - }); - } - - invalidSegments.sort((a, b) => a.start - b.start); - - if (invalidSegments.length > 0) { - let lastEnd = 0; - const underline = invalidSegments.reduce((acc, segment) => { - const spaces = ' '.repeat(segment.start - lastEnd); - const arrows = '^'.repeat(segment.length); - lastEnd = segment.start + segment.length; - return acc + spaces + arrows; - }, ''); - - throw new OpencodeError( - `Path parameters result in path with invalid segments:\n${invalidSegments - .map((e) => e.error) - .join('\n')}\n${path}\n${underline}`, - ); - } - - return path; - }; - -/** - * URI-encodes path params and ensures no unsafe /./ or /../ path segments are introduced. - */ -export const path = /* @__PURE__ */ createPathTagFunction(encodeURIPath); diff --git a/packages/sdk/src/internal/utils/sleep.ts b/packages/sdk/src/internal/utils/sleep.ts deleted file mode 100644 index 65e52962b..000000000 --- a/packages/sdk/src/internal/utils/sleep.ts +++ /dev/null @@ -1,3 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -export const sleep = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms)); diff --git a/packages/sdk/src/internal/utils/uuid.ts b/packages/sdk/src/internal/utils/uuid.ts deleted file mode 100644 index b0e53aaf7..000000000 --- a/packages/sdk/src/internal/utils/uuid.ts +++ /dev/null @@ -1,17 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -/** - * https://stackoverflow.com/a/2117523 - */ -export let uuid4 = function () { - const { crypto } = globalThis as any; - if (crypto?.randomUUID) { - uuid4 = crypto.randomUUID.bind(crypto); - return crypto.randomUUID(); - } - const u8 = new Uint8Array(1); - const randomByte = crypto ? () => crypto.getRandomValues(u8)[0]! : () => (Math.random() * 0xff) & 0xff; - return '10000000-1000-4000-8000-100000000000'.replace(/[018]/g, (c) => - (+c ^ (randomByte() & (15 >> (+c / 4)))).toString(16), - ); -}; diff --git a/packages/sdk/src/internal/utils/values.ts b/packages/sdk/src/internal/utils/values.ts deleted file mode 100644 index b2421fd88..000000000 --- a/packages/sdk/src/internal/utils/values.ts +++ /dev/null @@ -1,105 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { OpencodeError } from '../../core/error'; - -// https://url.spec.whatwg.org/#url-scheme-string -const startsWithSchemeRegexp = /^[a-z][a-z0-9+.-]*:/i; - -export const isAbsoluteURL = (url: string): boolean => { - return startsWithSchemeRegexp.test(url); -}; - -export let isArray = (val: unknown): val is unknown[] => ((isArray = Array.isArray), isArray(val)); -export let isReadonlyArray = isArray as (val: unknown) => val is readonly unknown[]; - -/** Returns an object if the given value isn't an object, otherwise returns as-is */ -export function maybeObj(x: unknown): object { - if (typeof x !== 'object') { - return {}; - } - - return x ?? {}; -} - -// https://stackoverflow.com/a/34491287 -export function isEmptyObj(obj: Object | null | undefined): boolean { - if (!obj) return true; - for (const _k in obj) return false; - return true; -} - -// https://eslint.org/docs/latest/rules/no-prototype-builtins -export function hasOwn<T extends object = object>(obj: T, key: PropertyKey): key is keyof T { - return Object.prototype.hasOwnProperty.call(obj, key); -} - -export function isObj(obj: unknown): obj is Record<string, unknown> { - return obj != null && typeof obj === 'object' && !Array.isArray(obj); -} - -export const ensurePresent = <T>(value: T | null | undefined): T => { - if (value == null) { - throw new OpencodeError(`Expected a value to be given but received ${value} instead.`); - } - - return value; -}; - -export const validatePositiveInteger = (name: string, n: unknown): number => { - if (typeof n !== 'number' || !Number.isInteger(n)) { - throw new OpencodeError(`${name} must be an integer`); - } - if (n < 0) { - throw new OpencodeError(`${name} must be a positive integer`); - } - return n; -}; - -export const coerceInteger = (value: unknown): number => { - if (typeof value === 'number') return Math.round(value); - if (typeof value === 'string') return parseInt(value, 10); - - throw new OpencodeError(`Could not coerce ${value} (type: ${typeof value}) into a number`); -}; - -export const coerceFloat = (value: unknown): number => { - if (typeof value === 'number') return value; - if (typeof value === 'string') return parseFloat(value); - - throw new OpencodeError(`Could not coerce ${value} (type: ${typeof value}) into a number`); -}; - -export const coerceBoolean = (value: unknown): boolean => { - if (typeof value === 'boolean') return value; - if (typeof value === 'string') return value === 'true'; - return Boolean(value); -}; - -export const maybeCoerceInteger = (value: unknown): number | undefined => { - if (value === undefined) { - return undefined; - } - return coerceInteger(value); -}; - -export const maybeCoerceFloat = (value: unknown): number | undefined => { - if (value === undefined) { - return undefined; - } - return coerceFloat(value); -}; - -export const maybeCoerceBoolean = (value: unknown): boolean | undefined => { - if (value === undefined) { - return undefined; - } - return coerceBoolean(value); -}; - -export const safeJSON = (text: string) => { - try { - return JSON.parse(text); - } catch (err) { - return undefined; - } -}; diff --git a/packages/sdk/src/lib/.keep b/packages/sdk/src/lib/.keep deleted file mode 100644 index 7554f8b20..000000000 --- a/packages/sdk/src/lib/.keep +++ /dev/null @@ -1,4 +0,0 @@ -File generated from our OpenAPI spec by Stainless. - -This directory can be used to store custom files to expand the SDK. -It is ignored by Stainless code generation and its content (other than this keep file) won't be touched. diff --git a/packages/sdk/src/resource.ts b/packages/sdk/src/resource.ts deleted file mode 100644 index 363e3516b..000000000 --- a/packages/sdk/src/resource.ts +++ /dev/null @@ -1,2 +0,0 @@ -/** @deprecated Import from ./core/resource instead */ -export * from './core/resource'; diff --git a/packages/sdk/src/resources.ts b/packages/sdk/src/resources.ts deleted file mode 100644 index b283d5781..000000000 --- a/packages/sdk/src/resources.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './resources/index'; diff --git a/packages/sdk/src/resources/app.ts b/packages/sdk/src/resources/app.ts deleted file mode 100644 index 8ee56e320..000000000 --- a/packages/sdk/src/resources/app.ts +++ /dev/null @@ -1,192 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { APIResource } from '../core/resource'; -import { APIPromise } from '../core/api-promise'; -import { RequestOptions } from '../internal/request-options'; - -export class AppResource extends APIResource { - /** - * Get app info - */ - get(options?: RequestOptions): APIPromise<App> { - return this._client.get('/app', options); - } - - /** - * Initialize the app - */ - init(options?: RequestOptions): APIPromise<AppInitResponse> { - return this._client.post('/app/init', options); - } - - /** - * Write a log entry to the server logs - */ - log(body: AppLogParams, options?: RequestOptions): APIPromise<AppLogResponse> { - return this._client.post('/log', { body, ...options }); - } - - /** - * List all modes - */ - modes(options?: RequestOptions): APIPromise<AppModesResponse> { - return this._client.get('/mode', options); - } - - /** - * List all providers - */ - providers(options?: RequestOptions): APIPromise<AppProvidersResponse> { - return this._client.get('/config/providers', options); - } -} - -export interface App { - git: boolean; - - hostname: string; - - path: App.Path; - - time: App.Time; -} - -export namespace App { - export interface Path { - config: string; - - cwd: string; - - data: string; - - root: string; - - state: string; - } - - export interface Time { - initialized?: number; - } -} - -export interface Mode { - name: string; - - tools: { [key: string]: boolean }; - - model?: Mode.Model; - - prompt?: string; - - temperature?: number; -} - -export namespace Mode { - export interface Model { - modelID: string; - - providerID: string; - } -} - -export interface Model { - id: string; - - attachment: boolean; - - cost: Model.Cost; - - limit: Model.Limit; - - name: string; - - options: { [key: string]: unknown }; - - reasoning: boolean; - - release_date: string; - - temperature: boolean; - - tool_call: boolean; -} - -export namespace Model { - export interface Cost { - input: number; - - output: number; - - cache_read?: number; - - cache_write?: number; - } - - export interface Limit { - context: number; - - output: number; - } -} - -export interface Provider { - id: string; - - env: Array<string>; - - models: { [key: string]: Model }; - - name: string; - - api?: string; - - npm?: string; -} - -export type AppInitResponse = boolean; - -export type AppLogResponse = boolean; - -export type AppModesResponse = Array<Mode>; - -export interface AppProvidersResponse { - default: { [key: string]: string }; - - providers: Array<Provider>; -} - -export interface AppLogParams { - /** - * Log level - */ - level: 'debug' | 'info' | 'error' | 'warn'; - - /** - * Log message - */ - message: string; - - /** - * Service name for the log entry - */ - service: string; - - /** - * Additional metadata for the log entry - */ - extra?: { [key: string]: unknown }; -} - -export declare namespace AppResource { - export { - 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, - }; -} diff --git a/packages/sdk/src/resources/config.ts b/packages/sdk/src/resources/config.ts deleted file mode 100644 index 11592f4aa..000000000 --- a/packages/sdk/src/resources/config.ts +++ /dev/null @@ -1,492 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { APIResource } from '../core/resource'; -import * as ConfigAPI from './config'; -import { APIPromise } from '../core/api-promise'; -import { RequestOptions } from '../internal/request-options'; - -export class ConfigResource extends APIResource { - /** - * Get config info - */ - get(options?: RequestOptions): APIPromise<Config> { - return this._client.get('/config', options); - } -} - -export interface Config { - /** - * JSON schema reference for configuration validation - */ - $schema?: string; - - /** - * Modes configuration, see https://opencode.ai/docs/modes - */ - agent?: Config.Agent; - - /** - * @deprecated Use 'share' field instead. Share newly created sessions - * automatically - */ - autoshare?: boolean; - - /** - * Automatically update to the latest version - */ - autoupdate?: boolean; - - /** - * Disable providers that are loaded automatically - */ - disabled_providers?: Array<string>; - - experimental?: Config.Experimental; - - /** - * Additional instruction files or patterns to include - */ - instructions?: Array<string>; - - /** - * Custom keybind configurations - */ - keybinds?: KeybindsConfig; - - /** - * @deprecated Always uses stretch layout. - */ - layout?: 'auto' | 'stretch'; - - /** - * MCP (Model Context Protocol) server configurations - */ - mcp?: { [key: string]: McpLocalConfig | McpRemoteConfig }; - - /** - * Modes configuration, see https://opencode.ai/docs/modes - */ - mode?: Config.Mode; - - /** - * Model to use in the format of provider/model, eg anthropic/claude-2 - */ - model?: string; - - /** - * Custom provider configurations and model overrides - */ - provider?: { [key: string]: Config.Provider }; - - /** - * Control sharing behavior:'manual' allows manual sharing via commands, 'auto' - * enables automatic sharing, 'disabled' disables all sharing - */ - share?: 'manual' | 'auto' | 'disabled'; - - /** - * Small model to use for tasks like summarization and title generation in the - * format of provider/model - */ - small_model?: string; - - /** - * Theme name to use for the interface - */ - theme?: string; - - /** - * Custom username to display in conversations instead of system username - */ - username?: string; -} - -export namespace Config { - /** - * Modes configuration, see https://opencode.ai/docs/modes - */ - export interface Agent { - general?: Agent.General; - - [k: string]: Agent.AgentConfig | undefined; - } - - export namespace Agent { - export interface General extends ConfigAPI.ModeConfig { - description: string; - } - - export interface AgentConfig extends ConfigAPI.ModeConfig { - description: string; - } - } - - export interface AgentConfig extends ConfigAPI.ModeConfig { - description: string; - } - - export interface AgentConfig extends ConfigAPI.ModeConfig { - description: string; - } - - export interface Experimental { - hook?: Experimental.Hook; - } - - export namespace Experimental { - export interface Hook { - file_edited?: { [key: string]: Array<Hook.FileEdited> }; - - session_completed?: Array<Hook.SessionCompleted>; - } - - export namespace Hook { - export interface FileEdited { - command: Array<string>; - - environment?: { [key: string]: string }; - } - - export interface SessionCompleted { - command: Array<string>; - - environment?: { [key: string]: string }; - } - } - } - - /** - * Modes configuration, see https://opencode.ai/docs/modes - */ - export interface Mode { - build?: ConfigAPI.ModeConfig; - - plan?: ConfigAPI.ModeConfig; - - [k: string]: ConfigAPI.ModeConfig | undefined; - } - - export interface Provider { - models: { [key: string]: Provider.Models }; - - id?: string; - - api?: string; - - env?: Array<string>; - - name?: string; - - npm?: string; - - options?: Provider.Options; - } - - export namespace Provider { - export interface Models { - id?: string; - - attachment?: boolean; - - cost?: Models.Cost; - - limit?: Models.Limit; - - name?: string; - - options?: { [key: string]: unknown }; - - reasoning?: boolean; - - release_date?: string; - - temperature?: boolean; - - tool_call?: boolean; - } - - export namespace Models { - export interface Cost { - input: number; - - output: number; - - cache_read?: number; - - cache_write?: number; - } - - export interface Limit { - context: number; - - output: number; - } - } - - export interface Options { - apiKey?: string; - - baseURL?: string; - - [k: string]: unknown; - } - } -} - -export interface KeybindsConfig { - /** - * Exit the application - */ - app_exit: string; - - /** - * Show help dialog - */ - app_help: string; - - /** - * Open external editor - */ - editor_open: string; - - /** - * Close file - */ - file_close: string; - - /** - * Split/unified diff - */ - file_diff_toggle: string; - - /** - * List files - */ - file_list: string; - - /** - * Search file - */ - file_search: string; - - /** - * Clear input field - */ - input_clear: string; - - /** - * Insert newline in input - */ - input_newline: string; - - /** - * Paste from clipboard - */ - input_paste: string; - - /** - * Submit input - */ - input_submit: string; - - /** - * Leader key for keybind combinations - */ - leader: string; - - /** - * Copy message - */ - messages_copy: string; - - /** - * Navigate to first message - */ - messages_first: string; - - /** - * Scroll messages down by half page - */ - messages_half_page_down: string; - - /** - * Scroll messages up by half page - */ - messages_half_page_up: string; - - /** - * Navigate to last message - */ - messages_last: string; - - /** - * Toggle layout - */ - messages_layout_toggle: string; - - /** - * Navigate to next message - */ - messages_next: string; - - /** - * Scroll messages down by one page - */ - messages_page_down: string; - - /** - * Scroll messages up by one page - */ - messages_page_up: string; - - /** - * Navigate to previous message - */ - messages_previous: string; - - /** - * Redo message - */ - messages_redo: string; - - /** - * @deprecated use messages_undo. Revert message - */ - messages_revert: string; - - /** - * Undo message - */ - messages_undo: string; - - /** - * List available models - */ - model_list: string; - - /** - * Create/update AGENTS.md - */ - project_init: string; - - /** - * Compact the session - */ - session_compact: string; - - /** - * Export session to editor - */ - session_export: string; - - /** - * Interrupt current session - */ - session_interrupt: string; - - /** - * List all sessions - */ - session_list: string; - - /** - * Create a new session - */ - session_new: string; - - /** - * Share current session - */ - session_share: string; - - /** - * Unshare current session - */ - session_unshare: string; - - /** - * Next mode - */ - switch_mode: string; - - /** - * Previous Mode - */ - switch_mode_reverse: string; - - /** - * List available themes - */ - theme_list: string; - - /** - * Toggle tool details - */ - tool_details: string; -} - -export interface McpLocalConfig { - /** - * Command and arguments to run the MCP server - */ - command: Array<string>; - - /** - * Type of MCP server connection - */ - type: 'local'; - - /** - * Enable or disable the MCP server on startup - */ - enabled?: boolean; - - /** - * Environment variables to set when running the MCP server - */ - environment?: { [key: string]: string }; -} - -export interface 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 }; -} - -export interface ModeConfig { - disable?: boolean; - - model?: string; - - prompt?: string; - - temperature?: number; - - tools?: { [key: string]: boolean }; -} - -export declare namespace ConfigResource { - export { - type Config as Config, - type KeybindsConfig as KeybindsConfig, - type McpLocalConfig as McpLocalConfig, - type McpRemoteConfig as McpRemoteConfig, - type ModeConfig as ModeConfig, - }; -} diff --git a/packages/sdk/src/resources/event.ts b/packages/sdk/src/resources/event.ts deleted file mode 100644 index bd51b22b3..000000000 --- a/packages/sdk/src/resources/event.ts +++ /dev/null @@ -1,267 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { APIResource } from '../core/resource'; -import * as SessionAPI from './session'; -import * as Shared from './shared'; -import { APIPromise } from '../core/api-promise'; -import { Stream } from '../core/streaming'; -import { RequestOptions } from '../internal/request-options'; - -export class Event extends APIResource { - /** - * Get events - */ - list(options?: RequestOptions): APIPromise<Stream<EventListResponse>> { - return this._client.get('/event', { ...options, stream: true }) as APIPromise<Stream<EventListResponse>>; - } -} - -export type EventListResponse = - | EventListResponse.EventInstallationUpdated - | EventListResponse.EventLspClientDiagnostics - | EventListResponse.EventMessageUpdated - | EventListResponse.EventMessageRemoved - | EventListResponse.EventMessagePartUpdated - | EventListResponse.EventMessagePartRemoved - | EventListResponse.EventStorageWrite - | EventListResponse.EventPermissionUpdated - | EventListResponse.EventFileEdited - | EventListResponse.EventSessionUpdated - | EventListResponse.EventSessionDeleted - | EventListResponse.EventSessionIdle - | EventListResponse.EventSessionError - | EventListResponse.EventServerConnected - | EventListResponse.EventFileWatcherUpdated - | EventListResponse.EventIdeInstalled; - -export namespace EventListResponse { - export interface EventInstallationUpdated { - properties: EventInstallationUpdated.Properties; - - type: 'installation.updated'; - } - - export namespace EventInstallationUpdated { - export interface Properties { - version: string; - } - } - - export interface EventLspClientDiagnostics { - properties: EventLspClientDiagnostics.Properties; - - type: 'lsp.client.diagnostics'; - } - - export namespace EventLspClientDiagnostics { - export interface Properties { - path: string; - - serverID: string; - } - } - - export interface EventMessageUpdated { - properties: EventMessageUpdated.Properties; - - type: 'message.updated'; - } - - export namespace EventMessageUpdated { - export interface Properties { - info: SessionAPI.Message; - } - } - - export interface EventMessageRemoved { - properties: EventMessageRemoved.Properties; - - type: 'message.removed'; - } - - export namespace EventMessageRemoved { - export interface Properties { - messageID: string; - - sessionID: string; - } - } - - export interface EventMessagePartUpdated { - properties: EventMessagePartUpdated.Properties; - - type: 'message.part.updated'; - } - - export namespace EventMessagePartUpdated { - export interface Properties { - part: SessionAPI.Part; - } - } - - export interface EventMessagePartRemoved { - properties: EventMessagePartRemoved.Properties; - - type: 'message.part.removed'; - } - - export namespace EventMessagePartRemoved { - export interface Properties { - messageID: string; - - partID: string; - - sessionID: string; - } - } - - export interface EventStorageWrite { - properties: EventStorageWrite.Properties; - - type: 'storage.write'; - } - - export namespace EventStorageWrite { - export interface Properties { - key: string; - - content?: unknown; - } - } - - export interface EventPermissionUpdated { - properties: EventPermissionUpdated.Properties; - - type: 'permission.updated'; - } - - export namespace EventPermissionUpdated { - export interface Properties { - id: string; - - metadata: { [key: string]: unknown }; - - sessionID: string; - - time: Properties.Time; - - title: string; - } - - export namespace Properties { - export interface Time { - created: number; - } - } - } - - export interface EventFileEdited { - properties: EventFileEdited.Properties; - - type: 'file.edited'; - } - - export namespace EventFileEdited { - export interface Properties { - file: string; - } - } - - export interface EventSessionUpdated { - properties: EventSessionUpdated.Properties; - - type: 'session.updated'; - } - - export namespace EventSessionUpdated { - export interface Properties { - info: SessionAPI.Session; - } - } - - export interface EventSessionDeleted { - properties: EventSessionDeleted.Properties; - - type: 'session.deleted'; - } - - export namespace EventSessionDeleted { - export interface Properties { - info: SessionAPI.Session; - } - } - - export interface EventSessionIdle { - properties: EventSessionIdle.Properties; - - type: 'session.idle'; - } - - export namespace EventSessionIdle { - export interface Properties { - sessionID: string; - } - } - - export interface EventSessionError { - properties: EventSessionError.Properties; - - type: 'session.error'; - } - - export namespace EventSessionError { - export interface Properties { - error?: - | Shared.ProviderAuthError - | Shared.UnknownError - | Properties.MessageOutputLengthError - | Shared.MessageAbortedError; - - sessionID?: string; - } - - export namespace Properties { - export interface MessageOutputLengthError { - data: unknown; - - name: 'MessageOutputLengthError'; - } - } - } - - export interface EventServerConnected { - properties: unknown; - - type: 'server.connected'; - } - - export interface EventFileWatcherUpdated { - properties: EventFileWatcherUpdated.Properties; - - type: 'file.watcher.updated'; - } - - export namespace EventFileWatcherUpdated { - export interface Properties { - event: 'rename' | 'change'; - - file: string; - } - } - - export interface EventIdeInstalled { - properties: EventIdeInstalled.Properties; - - type: 'ide.installed'; - } - - export namespace EventIdeInstalled { - export interface Properties { - ide: string; - } - } -} - -export declare namespace Event { - export { type EventListResponse as EventListResponse }; -} diff --git a/packages/sdk/src/resources/file.ts b/packages/sdk/src/resources/file.ts deleted file mode 100644 index fd7bdac69..000000000 --- a/packages/sdk/src/resources/file.ts +++ /dev/null @@ -1,52 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { APIResource } from '../core/resource'; -import { APIPromise } from '../core/api-promise'; -import { RequestOptions } from '../internal/request-options'; - -export class FileResource extends APIResource { - /** - * Read a file - */ - read(query: FileReadParams, options?: RequestOptions): APIPromise<FileReadResponse> { - return this._client.get('/file', { query, ...options }); - } - - /** - * Get file status - */ - status(options?: RequestOptions): APIPromise<FileStatusResponse> { - return this._client.get('/file/status', options); - } -} - -export interface File { - added: number; - - path: string; - - removed: number; - - status: 'added' | 'deleted' | 'modified'; -} - -export interface FileReadResponse { - content: string; - - type: 'raw' | 'patch'; -} - -export type FileStatusResponse = Array<File>; - -export interface FileReadParams { - path: string; -} - -export declare namespace FileResource { - export { - type File as File, - type FileReadResponse as FileReadResponse, - type FileStatusResponse as FileStatusResponse, - type FileReadParams as FileReadParams, - }; -} diff --git a/packages/sdk/src/resources/find.ts b/packages/sdk/src/resources/find.ts deleted file mode 100644 index d9d6597e5..000000000 --- a/packages/sdk/src/resources/find.ts +++ /dev/null @@ -1,134 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { APIResource } from '../core/resource'; -import { APIPromise } from '../core/api-promise'; -import { RequestOptions } from '../internal/request-options'; - -export class Find extends APIResource { - /** - * Find files - */ - files(query: FindFilesParams, options?: RequestOptions): APIPromise<FindFilesResponse> { - return this._client.get('/find/file', { query, ...options }); - } - - /** - * Find workspace symbols - */ - symbols(query: FindSymbolsParams, options?: RequestOptions): APIPromise<FindSymbolsResponse> { - return this._client.get('/find/symbol', { query, ...options }); - } - - /** - * Find text in files - */ - text(query: FindTextParams, options?: RequestOptions): APIPromise<FindTextResponse> { - return this._client.get('/find', { query, ...options }); - } -} - -export interface Symbol { - kind: number; - - location: Symbol.Location; - - name: string; -} - -export namespace Symbol { - export interface Location { - range: Location.Range; - - uri: string; - } - - export namespace Location { - export interface Range { - end: Range.End; - - start: Range.Start; - } - - export namespace Range { - export interface End { - character: number; - - line: number; - } - - export interface Start { - character: number; - - line: number; - } - } - } -} - -export type FindFilesResponse = Array<string>; - -export type FindSymbolsResponse = Array<Symbol>; - -export type FindTextResponse = Array<FindTextResponse.FindTextResponseItem>; - -export namespace FindTextResponse { - export interface FindTextResponseItem { - absolute_offset: number; - - line_number: number; - - lines: FindTextResponseItem.Lines; - - path: FindTextResponseItem.Path; - - submatches: Array<FindTextResponseItem.Submatch>; - } - - export namespace FindTextResponseItem { - export interface Lines { - text: string; - } - - export interface Path { - text: string; - } - - export interface Submatch { - end: number; - - match: Submatch.Match; - - start: number; - } - - export namespace Submatch { - export interface Match { - text: string; - } - } - } -} - -export interface FindFilesParams { - query: string; -} - -export interface FindSymbolsParams { - query: string; -} - -export interface FindTextParams { - pattern: string; -} - -export declare namespace Find { - export { - 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, - }; -} diff --git a/packages/sdk/src/resources/index.ts b/packages/sdk/src/resources/index.ts deleted file mode 100644 index 4e7934af1..000000000 --- a/packages/sdk/src/resources/index.ts +++ /dev/null @@ -1,81 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -export * from './shared'; -export { - AppResource, - type App, - type Mode, - type Model, - type Provider, - type AppInitResponse, - type AppLogResponse, - type AppModesResponse, - type AppProvidersResponse, - type AppLogParams, -} from './app'; -export { - ConfigResource, - type Config, - type KeybindsConfig, - type McpLocalConfig, - type McpRemoteConfig, - type ModeConfig, -} from './config'; -export { Event, type EventListResponse } from './event'; -export { - FileResource, - type File, - type FileReadResponse, - type FileStatusResponse, - type FileReadParams, -} from './file'; -export { - Find, - type Symbol, - type FindFilesResponse, - type FindSymbolsResponse, - type FindTextResponse, - type FindFilesParams, - type FindSymbolsParams, - type FindTextParams, -} from './find'; -export { - SessionResource, - type AssistantMessage, - type FilePart, - type FilePartInput, - type FilePartSource, - type FilePartSourceText, - type FileSource, - type Message, - type Part, - type Session, - type SnapshotPart, - type StepFinishPart, - type StepStartPart, - type SymbolSource, - type TextPart, - type TextPartInput, - type ToolPart, - type ToolStateCompleted, - type ToolStateError, - type ToolStatePending, - type ToolStateRunning, - type UserMessage, - type SessionListResponse, - type SessionDeleteResponse, - type SessionAbortResponse, - type SessionInitResponse, - type SessionMessagesResponse, - type SessionSummarizeResponse, - type SessionChatParams, - type SessionInitParams, - type SessionRevertParams, - type SessionSummarizeParams, -} from './session'; -export { - Tui, - type TuiAppendPromptResponse, - type TuiOpenHelpResponse, - type TuiAppendPromptParams, -} from './tui'; diff --git a/packages/sdk/src/resources/session.ts b/packages/sdk/src/resources/session.ts deleted file mode 100644 index 5dbdc31f8..000000000 --- a/packages/sdk/src/resources/session.ts +++ /dev/null @@ -1,605 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { APIResource } from '../core/resource'; -import * as SessionAPI from './session'; -import * as Shared from './shared'; -import { APIPromise } from '../core/api-promise'; -import { RequestOptions } from '../internal/request-options'; -import { path } from '../internal/utils/path'; - -export class SessionResource extends APIResource { - /** - * Create a new session - */ - create(options?: RequestOptions): APIPromise<Session> { - return this._client.post('/session', options); - } - - /** - * List all sessions - */ - list(options?: RequestOptions): APIPromise<SessionListResponse> { - return this._client.get('/session', options); - } - - /** - * Delete a session and all its data - */ - delete(id: string, options?: RequestOptions): APIPromise<SessionDeleteResponse> { - return this._client.delete(path`/session/${id}`, options); - } - - /** - * Abort a session - */ - abort(id: string, options?: RequestOptions): APIPromise<SessionAbortResponse> { - return this._client.post(path`/session/${id}/abort`, options); - } - - /** - * Create and send a new message to a session - */ - chat(id: string, body: SessionChatParams, options?: RequestOptions): APIPromise<AssistantMessage> { - return this._client.post(path`/session/${id}/message`, { body, ...options }); - } - - /** - * Analyze the app and create an AGENTS.md file - */ - init(id: string, body: SessionInitParams, options?: RequestOptions): APIPromise<SessionInitResponse> { - return this._client.post(path`/session/${id}/init`, { body, ...options }); - } - - /** - * List messages for a session - */ - messages(id: string, options?: RequestOptions): APIPromise<SessionMessagesResponse> { - return this._client.get(path`/session/${id}/message`, options); - } - - /** - * Revert a message - */ - revert(id: string, body: SessionRevertParams, options?: RequestOptions): APIPromise<Session> { - return this._client.post(path`/session/${id}/revert`, { body, ...options }); - } - - /** - * Share a session - */ - share(id: string, options?: RequestOptions): APIPromise<Session> { - return this._client.post(path`/session/${id}/share`, options); - } - - /** - * Summarize the session - */ - summarize( - id: string, - body: SessionSummarizeParams, - options?: RequestOptions, - ): APIPromise<SessionSummarizeResponse> { - return this._client.post(path`/session/${id}/summarize`, { body, ...options }); - } - - /** - * Restore all reverted messages - */ - unrevert(id: string, options?: RequestOptions): APIPromise<Session> { - return this._client.post(path`/session/${id}/unrevert`, options); - } - - /** - * Unshare the session - */ - unshare(id: string, options?: RequestOptions): APIPromise<Session> { - return this._client.delete(path`/session/${id}/share`, options); - } -} - -export interface AssistantMessage { - id: string; - - cost: number; - - mode: string; - - modelID: string; - - path: AssistantMessage.Path; - - providerID: string; - - role: 'assistant'; - - sessionID: string; - - system: Array<string>; - - time: AssistantMessage.Time; - - tokens: AssistantMessage.Tokens; - - error?: - | Shared.ProviderAuthError - | Shared.UnknownError - | AssistantMessage.MessageOutputLengthError - | Shared.MessageAbortedError; - - summary?: boolean; -} - -export namespace AssistantMessage { - export interface Path { - cwd: string; - - root: string; - } - - export interface Time { - created: number; - - completed?: number; - } - - export interface Tokens { - cache: Tokens.Cache; - - input: number; - - output: number; - - reasoning: number; - } - - export namespace Tokens { - export interface Cache { - read: number; - - write: number; - } - } - - export interface MessageOutputLengthError { - data: unknown; - - name: 'MessageOutputLengthError'; - } -} - -export interface FilePart { - id: string; - - messageID: string; - - mime: string; - - sessionID: string; - - type: 'file'; - - url: string; - - filename?: string; - - source?: FilePartSource; -} - -export interface FilePartInput { - mime: string; - - type: 'file'; - - url: string; - - id?: string; - - filename?: string; - - source?: FilePartSource; -} - -export type FilePartSource = FileSource | SymbolSource; - -export interface FilePartSourceText { - end: number; - - start: number; - - value: string; -} - -export interface FileSource { - path: string; - - text: FilePartSourceText; - - type: 'file'; -} - -export type Message = UserMessage | AssistantMessage; - -export type Part = - | TextPart - | FilePart - | ToolPart - | StepStartPart - | StepFinishPart - | SnapshotPart - | Part.PatchPart; - -export namespace Part { - export interface PatchPart { - id: string; - - files: Array<string>; - - hash: string; - - messageID: string; - - sessionID: string; - - type: 'patch'; - } -} - -export interface Session { - id: string; - - time: Session.Time; - - title: string; - - version: string; - - parentID?: string; - - revert?: Session.Revert; - - share?: Session.Share; -} - -export namespace Session { - export interface Time { - created: number; - - updated: number; - } - - export interface Revert { - messageID: string; - - diff?: string; - - partID?: string; - - snapshot?: string; - } - - export interface Share { - url: string; - } -} - -export interface SnapshotPart { - id: string; - - messageID: string; - - sessionID: string; - - snapshot: string; - - type: 'snapshot'; -} - -export interface StepFinishPart { - id: string; - - cost: number; - - messageID: string; - - sessionID: string; - - tokens: StepFinishPart.Tokens; - - type: 'step-finish'; -} - -export namespace StepFinishPart { - export interface Tokens { - cache: Tokens.Cache; - - input: number; - - output: number; - - reasoning: number; - } - - export namespace Tokens { - export interface Cache { - read: number; - - write: number; - } - } -} - -export interface StepStartPart { - id: string; - - messageID: string; - - sessionID: string; - - type: 'step-start'; -} - -export interface SymbolSource { - kind: number; - - name: string; - - path: string; - - range: SymbolSource.Range; - - text: FilePartSourceText; - - type: 'symbol'; -} - -export namespace SymbolSource { - export interface Range { - end: Range.End; - - start: Range.Start; - } - - export namespace Range { - export interface End { - character: number; - - line: number; - } - - export interface Start { - character: number; - - line: number; - } - } -} - -export interface TextPart { - id: string; - - messageID: string; - - sessionID: string; - - text: string; - - type: 'text'; - - synthetic?: boolean; - - time?: TextPart.Time; -} - -export namespace TextPart { - export interface Time { - start: number; - - end?: number; - } -} - -export interface TextPartInput { - text: string; - - type: 'text'; - - id?: string; - - synthetic?: boolean; - - time?: TextPartInput.Time; -} - -export namespace TextPartInput { - export interface Time { - start: number; - - end?: number; - } -} - -export interface ToolPart { - id: string; - - callID: string; - - messageID: string; - - sessionID: string; - - state: ToolStatePending | ToolStateRunning | ToolStateCompleted | ToolStateError; - - tool: string; - - type: 'tool'; -} - -export interface ToolStateCompleted { - input: { [key: string]: unknown }; - - metadata: { [key: string]: unknown }; - - output: string; - - status: 'completed'; - - time: ToolStateCompleted.Time; - - title: string; -} - -export namespace ToolStateCompleted { - export interface Time { - end: number; - - start: number; - } -} - -export interface ToolStateError { - error: string; - - input: { [key: string]: unknown }; - - status: 'error'; - - time: ToolStateError.Time; -} - -export namespace ToolStateError { - export interface Time { - end: number; - - start: number; - } -} - -export interface ToolStatePending { - status: 'pending'; -} - -export interface ToolStateRunning { - status: 'running'; - - time: ToolStateRunning.Time; - - input?: unknown; - - metadata?: { [key: string]: unknown }; - - title?: string; -} - -export namespace ToolStateRunning { - export interface Time { - start: number; - } -} - -export interface UserMessage { - id: string; - - role: 'user'; - - sessionID: string; - - time: UserMessage.Time; -} - -export namespace UserMessage { - export interface Time { - created: number; - } -} - -export type SessionListResponse = Array<Session>; - -export type SessionDeleteResponse = boolean; - -export type SessionAbortResponse = boolean; - -export type SessionInitResponse = boolean; - -export type SessionMessagesResponse = Array<SessionMessagesResponse.SessionMessagesResponseItem>; - -export namespace SessionMessagesResponse { - export interface SessionMessagesResponseItem { - info: SessionAPI.Message; - - parts: Array<SessionAPI.Part>; - } -} - -export type SessionSummarizeResponse = boolean; - -export interface SessionChatParams { - modelID: string; - - parts: Array<TextPartInput | FilePartInput>; - - providerID: string; - - messageID?: string; - - mode?: string; - - system?: string; - - tools?: { [key: string]: boolean }; -} - -export interface SessionInitParams { - messageID: string; - - modelID: string; - - providerID: string; -} - -export interface SessionRevertParams { - messageID: string; - - partID?: string; -} - -export interface SessionSummarizeParams { - modelID: string; - - providerID: string; -} - -export declare namespace SessionResource { - export { - 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 SessionRevertParams as SessionRevertParams, - type SessionSummarizeParams as SessionSummarizeParams, - }; -} diff --git a/packages/sdk/src/resources/shared.ts b/packages/sdk/src/resources/shared.ts deleted file mode 100644 index 31378f6fe..000000000 --- a/packages/sdk/src/resources/shared.ts +++ /dev/null @@ -1,33 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -export interface MessageAbortedError { - data: unknown; - - name: 'MessageAbortedError'; -} - -export interface ProviderAuthError { - data: ProviderAuthError.Data; - - name: 'ProviderAuthError'; -} - -export namespace ProviderAuthError { - export interface Data { - message: string; - - providerID: string; - } -} - -export interface UnknownError { - data: UnknownError.Data; - - name: 'UnknownError'; -} - -export namespace UnknownError { - export interface Data { - message: string; - } -} diff --git a/packages/sdk/src/resources/tui.ts b/packages/sdk/src/resources/tui.ts deleted file mode 100644 index c7efbdd6c..000000000 --- a/packages/sdk/src/resources/tui.ts +++ /dev/null @@ -1,37 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { APIResource } from '../core/resource'; -import { APIPromise } from '../core/api-promise'; -import { RequestOptions } from '../internal/request-options'; - -export class Tui extends APIResource { - /** - * Append prompt to the TUI - */ - appendPrompt(body: TuiAppendPromptParams, options?: RequestOptions): APIPromise<TuiAppendPromptResponse> { - return this._client.post('/tui/append-prompt', { body, ...options }); - } - - /** - * Open the help dialog - */ - openHelp(options?: RequestOptions): APIPromise<TuiOpenHelpResponse> { - return this._client.post('/tui/open-help', options); - } -} - -export type TuiAppendPromptResponse = boolean; - -export type TuiOpenHelpResponse = boolean; - -export interface TuiAppendPromptParams { - text: string; -} - -export declare namespace Tui { - export { - type TuiAppendPromptResponse as TuiAppendPromptResponse, - type TuiOpenHelpResponse as TuiOpenHelpResponse, - type TuiAppendPromptParams as TuiAppendPromptParams, - }; -} diff --git a/packages/sdk/src/streaming.ts b/packages/sdk/src/streaming.ts deleted file mode 100644 index 9e6da1063..000000000 --- a/packages/sdk/src/streaming.ts +++ /dev/null @@ -1,2 +0,0 @@ -/** @deprecated Import from ./core/streaming instead */ -export * from './core/streaming'; diff --git a/packages/sdk/src/uploads.ts b/packages/sdk/src/uploads.ts deleted file mode 100644 index b2ef64710..000000000 --- a/packages/sdk/src/uploads.ts +++ /dev/null @@ -1,2 +0,0 @@ -/** @deprecated Import from ./core/uploads instead */ -export * from './core/uploads'; diff --git a/packages/sdk/src/version.ts b/packages/sdk/src/version.ts deleted file mode 100644 index 48ca0ae07..000000000 --- a/packages/sdk/src/version.ts +++ /dev/null @@ -1 +0,0 @@ -export const VERSION = '0.1.0-alpha.20'; // x-release-please-version |
