@opencode-ai/sdk
Advanced tools
+1
-1
@@ -8,3 +8,3 @@ { | ||
| }, | ||
| "version": "0.0.1", | ||
| "version": "0.0.2", | ||
| "files": [ | ||
@@ -11,0 +11,0 @@ "dist" |
| #!/usr/bin/env bun | ||
| import { $ } from "bun"; | ||
| import fs from "fs/promises"; | ||
| import path from "path"; | ||
| console.log("=== Generating JS SDK ==="); | ||
| console.log(); | ||
| import { createClient } from "@hey-api/openapi-ts"; | ||
| const dir = new URL("..", import.meta.url).pathname; | ||
| await fs.rm(path.join(dir, "src/gen"), { recursive: true, force: true }); | ||
| await $ `bun run ../../opencode/src/index.ts generate > openapi.json`.cwd(dir); | ||
| await createClient({ | ||
| input: "./openapi.json", | ||
| output: "./src/gen", | ||
| plugins: [ | ||
| { | ||
| name: "@hey-api/typescript", | ||
| exportFromIndex: false, | ||
| }, | ||
| { | ||
| name: "@hey-api/sdk", | ||
| instance: "OpencodeClient", | ||
| exportFromIndex: false, | ||
| auth: false, | ||
| }, | ||
| { | ||
| name: "@hey-api/client-fetch", | ||
| exportFromIndex: false, | ||
| baseUrl: "http://localhost:4096", | ||
| }, | ||
| ], | ||
| }); |
| #!/usr/bin/env bun | ||
| import { $ } from "bun"; | ||
| await import("./generate.js"); | ||
| await $ `bun tsc`; |
| // This file is auto-generated by @hey-api/openapi-ts | ||
| import { createClient, createConfig } from './client'; | ||
| export const client = createClient(createConfig({ | ||
| baseUrl: 'http://localhost:4096' | ||
| })); |
| import { buildUrl, createConfig, createInterceptors, getParseAs, mergeConfigs, mergeHeaders, setAuthParams, } from './utils'; | ||
| export const createClient = (config = {}) => { | ||
| let _config = mergeConfigs(createConfig(), config); | ||
| const getConfig = () => ({ ..._config }); | ||
| const setConfig = (config) => { | ||
| _config = mergeConfigs(_config, config); | ||
| return getConfig(); | ||
| }; | ||
| const interceptors = createInterceptors(); | ||
| const request = async (options) => { | ||
| const opts = { | ||
| ..._config, | ||
| ...options, | ||
| fetch: options.fetch ?? _config.fetch ?? globalThis.fetch, | ||
| headers: mergeHeaders(_config.headers, options.headers), | ||
| }; | ||
| if (opts.security) { | ||
| await setAuthParams({ | ||
| ...opts, | ||
| security: opts.security, | ||
| }); | ||
| } | ||
| if (opts.requestValidator) { | ||
| await opts.requestValidator(opts); | ||
| } | ||
| if (opts.body && opts.bodySerializer) { | ||
| opts.body = opts.bodySerializer(opts.body); | ||
| } | ||
| // remove Content-Type header if body is empty to avoid sending invalid requests | ||
| if (opts.body === undefined || opts.body === '') { | ||
| opts.headers.delete('Content-Type'); | ||
| } | ||
| const url = buildUrl(opts); | ||
| const requestInit = { | ||
| redirect: 'follow', | ||
| ...opts, | ||
| }; | ||
| let request = new Request(url, requestInit); | ||
| for (const fn of interceptors.request._fns) { | ||
| if (fn) { | ||
| request = await fn(request, opts); | ||
| } | ||
| } | ||
| // fetch must be assigned here, otherwise it would throw the error: | ||
| // TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation | ||
| const _fetch = opts.fetch; | ||
| let response = await _fetch(request); | ||
| for (const fn of interceptors.response._fns) { | ||
| if (fn) { | ||
| response = await fn(response, request, opts); | ||
| } | ||
| } | ||
| const result = { | ||
| request, | ||
| response, | ||
| }; | ||
| if (response.ok) { | ||
| if (response.status === 204 || | ||
| response.headers.get('Content-Length') === '0') { | ||
| return opts.responseStyle === 'data' | ||
| ? {} | ||
| : { | ||
| data: {}, | ||
| ...result, | ||
| }; | ||
| } | ||
| const parseAs = (opts.parseAs === 'auto' | ||
| ? getParseAs(response.headers.get('Content-Type')) | ||
| : opts.parseAs) ?? 'json'; | ||
| let data; | ||
| switch (parseAs) { | ||
| case 'arrayBuffer': | ||
| case 'blob': | ||
| case 'formData': | ||
| case 'json': | ||
| case 'text': | ||
| data = await response[parseAs](); | ||
| break; | ||
| case 'stream': | ||
| return opts.responseStyle === 'data' | ||
| ? response.body | ||
| : { | ||
| data: response.body, | ||
| ...result, | ||
| }; | ||
| } | ||
| if (parseAs === 'json') { | ||
| if (opts.responseValidator) { | ||
| await opts.responseValidator(data); | ||
| } | ||
| if (opts.responseTransformer) { | ||
| data = await opts.responseTransformer(data); | ||
| } | ||
| } | ||
| return opts.responseStyle === 'data' | ||
| ? data | ||
| : { | ||
| data, | ||
| ...result, | ||
| }; | ||
| } | ||
| const textError = await response.text(); | ||
| let jsonError; | ||
| try { | ||
| jsonError = JSON.parse(textError); | ||
| } | ||
| catch { | ||
| // noop | ||
| } | ||
| const error = jsonError ?? textError; | ||
| let finalError = error; | ||
| for (const fn of interceptors.error._fns) { | ||
| if (fn) { | ||
| finalError = (await fn(error, response, request, opts)); | ||
| } | ||
| } | ||
| finalError = finalError || {}; | ||
| if (opts.throwOnError) { | ||
| throw finalError; | ||
| } | ||
| // TODO: we probably want to return error and improve types | ||
| return opts.responseStyle === 'data' | ||
| ? undefined | ||
| : { | ||
| error: finalError, | ||
| ...result, | ||
| }; | ||
| }; | ||
| return { | ||
| buildUrl, | ||
| connect: (options) => request({ ...options, method: 'CONNECT' }), | ||
| delete: (options) => request({ ...options, method: 'DELETE' }), | ||
| get: (options) => request({ ...options, method: 'GET' }), | ||
| getConfig, | ||
| head: (options) => request({ ...options, method: 'HEAD' }), | ||
| interceptors, | ||
| options: (options) => request({ ...options, method: 'OPTIONS' }), | ||
| patch: (options) => request({ ...options, method: 'PATCH' }), | ||
| post: (options) => request({ ...options, method: 'POST' }), | ||
| put: (options) => request({ ...options, method: 'PUT' }), | ||
| request, | ||
| setConfig, | ||
| trace: (options) => request({ ...options, method: 'TRACE' }), | ||
| }; | ||
| }; |
| export { formDataBodySerializer, jsonBodySerializer, urlSearchParamsBodySerializer, } from '../core/bodySerializer'; | ||
| export { buildClientParams } from '../core/params'; | ||
| export { createClient } from './client'; | ||
| export { createConfig, mergeHeaders } from './utils'; |
| import { getAuthToken } from '../core/auth'; | ||
| import { jsonBodySerializer } from '../core/bodySerializer'; | ||
| import { serializeArrayParam, serializeObjectParam, serializePrimitiveParam, } from '../core/pathSerializer'; | ||
| const PATH_PARAM_RE = /\{[^{}]+\}/g; | ||
| const defaultPathSerializer = ({ path, url: _url }) => { | ||
| let url = _url; | ||
| const matches = _url.match(PATH_PARAM_RE); | ||
| if (matches) { | ||
| for (const match of matches) { | ||
| let explode = false; | ||
| let name = match.substring(1, match.length - 1); | ||
| let style = 'simple'; | ||
| if (name.endsWith('*')) { | ||
| explode = true; | ||
| name = name.substring(0, name.length - 1); | ||
| } | ||
| if (name.startsWith('.')) { | ||
| name = name.substring(1); | ||
| style = 'label'; | ||
| } | ||
| else if (name.startsWith(';')) { | ||
| name = name.substring(1); | ||
| style = 'matrix'; | ||
| } | ||
| const value = path[name]; | ||
| if (value === undefined || value === null) { | ||
| continue; | ||
| } | ||
| if (Array.isArray(value)) { | ||
| url = url.replace(match, serializeArrayParam({ explode, name, style, value })); | ||
| continue; | ||
| } | ||
| if (typeof value === 'object') { | ||
| url = url.replace(match, serializeObjectParam({ | ||
| explode, | ||
| name, | ||
| style, | ||
| value: value, | ||
| valueOnly: true, | ||
| })); | ||
| continue; | ||
| } | ||
| if (style === 'matrix') { | ||
| url = url.replace(match, `;${serializePrimitiveParam({ | ||
| name, | ||
| value: value, | ||
| })}`); | ||
| continue; | ||
| } | ||
| const replaceValue = encodeURIComponent(style === 'label' ? `.${value}` : value); | ||
| url = url.replace(match, replaceValue); | ||
| } | ||
| } | ||
| return url; | ||
| }; | ||
| export const createQuerySerializer = ({ allowReserved, array, object, } = {}) => { | ||
| const querySerializer = (queryParams) => { | ||
| const search = []; | ||
| if (queryParams && typeof queryParams === 'object') { | ||
| for (const name in queryParams) { | ||
| const value = queryParams[name]; | ||
| if (value === undefined || value === null) { | ||
| continue; | ||
| } | ||
| if (Array.isArray(value)) { | ||
| const serializedArray = serializeArrayParam({ | ||
| allowReserved, | ||
| explode: true, | ||
| name, | ||
| style: 'form', | ||
| value, | ||
| ...array, | ||
| }); | ||
| if (serializedArray) | ||
| search.push(serializedArray); | ||
| } | ||
| else if (typeof value === 'object') { | ||
| const serializedObject = serializeObjectParam({ | ||
| allowReserved, | ||
| explode: true, | ||
| name, | ||
| style: 'deepObject', | ||
| value: value, | ||
| ...object, | ||
| }); | ||
| if (serializedObject) | ||
| search.push(serializedObject); | ||
| } | ||
| else { | ||
| const serializedPrimitive = serializePrimitiveParam({ | ||
| allowReserved, | ||
| name, | ||
| value: value, | ||
| }); | ||
| if (serializedPrimitive) | ||
| search.push(serializedPrimitive); | ||
| } | ||
| } | ||
| } | ||
| return search.join('&'); | ||
| }; | ||
| return querySerializer; | ||
| }; | ||
| /** | ||
| * Infers parseAs value from provided Content-Type header. | ||
| */ | ||
| export const getParseAs = (contentType) => { | ||
| if (!contentType) { | ||
| // If no Content-Type header is provided, the best we can do is return the raw response body, | ||
| // which is effectively the same as the 'stream' option. | ||
| return 'stream'; | ||
| } | ||
| const cleanContent = contentType.split(';')[0]?.trim(); | ||
| if (!cleanContent) { | ||
| return; | ||
| } | ||
| if (cleanContent.startsWith('application/json') || | ||
| cleanContent.endsWith('+json')) { | ||
| return 'json'; | ||
| } | ||
| if (cleanContent === 'multipart/form-data') { | ||
| return 'formData'; | ||
| } | ||
| if (['application/', 'audio/', 'image/', 'video/'].some((type) => cleanContent.startsWith(type))) { | ||
| return 'blob'; | ||
| } | ||
| if (cleanContent.startsWith('text/')) { | ||
| return 'text'; | ||
| } | ||
| return; | ||
| }; | ||
| export const setAuthParams = async ({ security, ...options }) => { | ||
| for (const auth of security) { | ||
| const token = await getAuthToken(auth, options.auth); | ||
| if (!token) { | ||
| continue; | ||
| } | ||
| const name = auth.name ?? 'Authorization'; | ||
| switch (auth.in) { | ||
| case 'query': | ||
| if (!options.query) { | ||
| options.query = {}; | ||
| } | ||
| options.query[name] = token; | ||
| break; | ||
| case 'cookie': | ||
| options.headers.append('Cookie', `${name}=${token}`); | ||
| break; | ||
| case 'header': | ||
| default: | ||
| options.headers.set(name, token); | ||
| break; | ||
| } | ||
| return; | ||
| } | ||
| }; | ||
| export const buildUrl = (options) => { | ||
| const url = getUrl({ | ||
| baseUrl: options.baseUrl, | ||
| path: options.path, | ||
| query: options.query, | ||
| querySerializer: typeof options.querySerializer === 'function' | ||
| ? options.querySerializer | ||
| : createQuerySerializer(options.querySerializer), | ||
| url: options.url, | ||
| }); | ||
| return url; | ||
| }; | ||
| export const getUrl = ({ baseUrl, path, query, querySerializer, url: _url, }) => { | ||
| const pathUrl = _url.startsWith('/') ? _url : `/${_url}`; | ||
| let url = (baseUrl ?? '') + pathUrl; | ||
| if (path) { | ||
| url = defaultPathSerializer({ path, url }); | ||
| } | ||
| let search = query ? querySerializer(query) : ''; | ||
| if (search.startsWith('?')) { | ||
| search = search.substring(1); | ||
| } | ||
| if (search) { | ||
| url += `?${search}`; | ||
| } | ||
| return url; | ||
| }; | ||
| export const mergeConfigs = (a, b) => { | ||
| const config = { ...a, ...b }; | ||
| if (config.baseUrl?.endsWith('/')) { | ||
| config.baseUrl = config.baseUrl.substring(0, config.baseUrl.length - 1); | ||
| } | ||
| config.headers = mergeHeaders(a.headers, b.headers); | ||
| return config; | ||
| }; | ||
| export const mergeHeaders = (...headers) => { | ||
| const mergedHeaders = new Headers(); | ||
| for (const header of headers) { | ||
| if (!header || typeof header !== 'object') { | ||
| continue; | ||
| } | ||
| const iterator = header instanceof Headers ? header.entries() : Object.entries(header); | ||
| for (const [key, value] of iterator) { | ||
| if (value === null) { | ||
| mergedHeaders.delete(key); | ||
| } | ||
| else if (Array.isArray(value)) { | ||
| for (const v of value) { | ||
| mergedHeaders.append(key, v); | ||
| } | ||
| } | ||
| else if (value !== undefined) { | ||
| // assume object headers are meant to be JSON stringified, i.e. their | ||
| // content value in OpenAPI specification is 'application/json' | ||
| mergedHeaders.set(key, typeof value === 'object' ? JSON.stringify(value) : value); | ||
| } | ||
| } | ||
| } | ||
| return mergedHeaders; | ||
| }; | ||
| class Interceptors { | ||
| _fns; | ||
| constructor() { | ||
| this._fns = []; | ||
| } | ||
| clear() { | ||
| this._fns = []; | ||
| } | ||
| getInterceptorIndex(id) { | ||
| if (typeof id === 'number') { | ||
| return this._fns[id] ? id : -1; | ||
| } | ||
| else { | ||
| return this._fns.indexOf(id); | ||
| } | ||
| } | ||
| exists(id) { | ||
| const index = this.getInterceptorIndex(id); | ||
| return !!this._fns[index]; | ||
| } | ||
| eject(id) { | ||
| const index = this.getInterceptorIndex(id); | ||
| if (this._fns[index]) { | ||
| this._fns[index] = null; | ||
| } | ||
| } | ||
| update(id, fn) { | ||
| const index = this.getInterceptorIndex(id); | ||
| if (this._fns[index]) { | ||
| this._fns[index] = fn; | ||
| return id; | ||
| } | ||
| else { | ||
| return false; | ||
| } | ||
| } | ||
| use(fn) { | ||
| this._fns = [...this._fns, fn]; | ||
| return this._fns.length - 1; | ||
| } | ||
| } | ||
| // do not add `Middleware` as return type so we can use _fns internally | ||
| export const createInterceptors = () => ({ | ||
| error: new Interceptors(), | ||
| request: new Interceptors(), | ||
| response: new Interceptors(), | ||
| }); | ||
| const defaultQuerySerializer = createQuerySerializer({ | ||
| allowReserved: false, | ||
| array: { | ||
| explode: true, | ||
| style: 'form', | ||
| }, | ||
| object: { | ||
| explode: true, | ||
| style: 'deepObject', | ||
| }, | ||
| }); | ||
| const defaultHeaders = { | ||
| 'Content-Type': 'application/json', | ||
| }; | ||
| export const createConfig = (override = {}) => ({ | ||
| ...jsonBodySerializer, | ||
| headers: defaultHeaders, | ||
| parseAs: 'auto', | ||
| querySerializer: defaultQuerySerializer, | ||
| ...override, | ||
| }); |
| export const getAuthToken = async (auth, callback) => { | ||
| const token = typeof callback === 'function' ? await callback(auth) : callback; | ||
| if (!token) { | ||
| return; | ||
| } | ||
| if (auth.scheme === 'bearer') { | ||
| return `Bearer ${token}`; | ||
| } | ||
| if (auth.scheme === 'basic') { | ||
| return `Basic ${btoa(token)}`; | ||
| } | ||
| return token; | ||
| }; |
| const serializeFormDataPair = (data, key, value) => { | ||
| if (typeof value === 'string' || value instanceof Blob) { | ||
| data.append(key, value); | ||
| } | ||
| else { | ||
| data.append(key, JSON.stringify(value)); | ||
| } | ||
| }; | ||
| const serializeUrlSearchParamsPair = (data, key, value) => { | ||
| if (typeof value === 'string') { | ||
| data.append(key, value); | ||
| } | ||
| else { | ||
| data.append(key, JSON.stringify(value)); | ||
| } | ||
| }; | ||
| export const formDataBodySerializer = { | ||
| bodySerializer: (body) => { | ||
| const data = new FormData(); | ||
| Object.entries(body).forEach(([key, value]) => { | ||
| if (value === undefined || value === null) { | ||
| return; | ||
| } | ||
| if (Array.isArray(value)) { | ||
| value.forEach((v) => serializeFormDataPair(data, key, v)); | ||
| } | ||
| else { | ||
| serializeFormDataPair(data, key, value); | ||
| } | ||
| }); | ||
| return data; | ||
| }, | ||
| }; | ||
| export const jsonBodySerializer = { | ||
| bodySerializer: (body) => JSON.stringify(body, (_key, value) => typeof value === 'bigint' ? value.toString() : value), | ||
| }; | ||
| export const urlSearchParamsBodySerializer = { | ||
| bodySerializer: (body) => { | ||
| const data = new URLSearchParams(); | ||
| Object.entries(body).forEach(([key, value]) => { | ||
| if (value === undefined || value === null) { | ||
| return; | ||
| } | ||
| if (Array.isArray(value)) { | ||
| value.forEach((v) => serializeUrlSearchParamsPair(data, key, v)); | ||
| } | ||
| else { | ||
| serializeUrlSearchParamsPair(data, key, value); | ||
| } | ||
| }); | ||
| return data.toString(); | ||
| }, | ||
| }; |
| const extraPrefixesMap = { | ||
| $body_: 'body', | ||
| $headers_: 'headers', | ||
| $path_: 'path', | ||
| $query_: 'query', | ||
| }; | ||
| const extraPrefixes = Object.entries(extraPrefixesMap); | ||
| const buildKeyMap = (fields, map) => { | ||
| if (!map) { | ||
| map = new Map(); | ||
| } | ||
| for (const config of fields) { | ||
| if ('in' in config) { | ||
| if (config.key) { | ||
| map.set(config.key, { | ||
| in: config.in, | ||
| map: config.map, | ||
| }); | ||
| } | ||
| } | ||
| else if (config.args) { | ||
| buildKeyMap(config.args, map); | ||
| } | ||
| } | ||
| return map; | ||
| }; | ||
| const stripEmptySlots = (params) => { | ||
| for (const [slot, value] of Object.entries(params)) { | ||
| if (value && typeof value === 'object' && !Object.keys(value).length) { | ||
| delete params[slot]; | ||
| } | ||
| } | ||
| }; | ||
| export const buildClientParams = (args, fields) => { | ||
| const params = { | ||
| body: {}, | ||
| headers: {}, | ||
| path: {}, | ||
| query: {}, | ||
| }; | ||
| const map = buildKeyMap(fields); | ||
| let config; | ||
| for (const [index, arg] of args.entries()) { | ||
| if (fields[index]) { | ||
| config = fields[index]; | ||
| } | ||
| if (!config) { | ||
| continue; | ||
| } | ||
| if ('in' in config) { | ||
| if (config.key) { | ||
| const field = map.get(config.key); | ||
| const name = field.map || config.key; | ||
| params[field.in][name] = arg; | ||
| } | ||
| else { | ||
| params.body = arg; | ||
| } | ||
| } | ||
| else { | ||
| for (const [key, value] of Object.entries(arg ?? {})) { | ||
| const field = map.get(key); | ||
| if (field) { | ||
| const name = field.map || key; | ||
| params[field.in][name] = value; | ||
| } | ||
| else { | ||
| const extra = extraPrefixes.find(([prefix]) => key.startsWith(prefix)); | ||
| if (extra) { | ||
| const [prefix, slot] = extra; | ||
| params[slot][key.slice(prefix.length)] = value; | ||
| } | ||
| else { | ||
| for (const [slot, allowed] of Object.entries(config.allowExtra ?? {})) { | ||
| if (allowed) { | ||
| params[slot][key] = value; | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| stripEmptySlots(params); | ||
| return params; | ||
| }; |
| export const separatorArrayExplode = (style) => { | ||
| switch (style) { | ||
| case 'label': | ||
| return '.'; | ||
| case 'matrix': | ||
| return ';'; | ||
| case 'simple': | ||
| return ','; | ||
| default: | ||
| return '&'; | ||
| } | ||
| }; | ||
| export const separatorArrayNoExplode = (style) => { | ||
| switch (style) { | ||
| case 'form': | ||
| return ','; | ||
| case 'pipeDelimited': | ||
| return '|'; | ||
| case 'spaceDelimited': | ||
| return '%20'; | ||
| default: | ||
| return ','; | ||
| } | ||
| }; | ||
| export const separatorObjectExplode = (style) => { | ||
| switch (style) { | ||
| case 'label': | ||
| return '.'; | ||
| case 'matrix': | ||
| return ';'; | ||
| case 'simple': | ||
| return ','; | ||
| default: | ||
| return '&'; | ||
| } | ||
| }; | ||
| export const serializeArrayParam = ({ allowReserved, explode, name, style, value, }) => { | ||
| if (!explode) { | ||
| const joinedValues = (allowReserved ? value : value.map((v) => encodeURIComponent(v))).join(separatorArrayNoExplode(style)); | ||
| switch (style) { | ||
| case 'label': | ||
| return `.${joinedValues}`; | ||
| case 'matrix': | ||
| return `;${name}=${joinedValues}`; | ||
| case 'simple': | ||
| return joinedValues; | ||
| default: | ||
| return `${name}=${joinedValues}`; | ||
| } | ||
| } | ||
| const separator = separatorArrayExplode(style); | ||
| const joinedValues = value | ||
| .map((v) => { | ||
| if (style === 'label' || style === 'simple') { | ||
| return allowReserved ? v : encodeURIComponent(v); | ||
| } | ||
| return serializePrimitiveParam({ | ||
| allowReserved, | ||
| name, | ||
| value: v, | ||
| }); | ||
| }) | ||
| .join(separator); | ||
| return style === 'label' || style === 'matrix' | ||
| ? separator + joinedValues | ||
| : joinedValues; | ||
| }; | ||
| export const serializePrimitiveParam = ({ allowReserved, name, value, }) => { | ||
| if (value === undefined || value === null) { | ||
| return ''; | ||
| } | ||
| if (typeof value === 'object') { | ||
| throw new Error('Deeply-nested arrays/objects aren’t supported. Provide your own `querySerializer()` to handle these.'); | ||
| } | ||
| return `${name}=${allowReserved ? value : encodeURIComponent(value)}`; | ||
| }; | ||
| export const serializeObjectParam = ({ allowReserved, explode, name, style, value, valueOnly, }) => { | ||
| if (value instanceof Date) { | ||
| return valueOnly ? value.toISOString() : `${name}=${value.toISOString()}`; | ||
| } | ||
| if (style !== 'deepObject' && !explode) { | ||
| let values = []; | ||
| Object.entries(value).forEach(([key, v]) => { | ||
| values = [ | ||
| ...values, | ||
| key, | ||
| allowReserved ? v : encodeURIComponent(v), | ||
| ]; | ||
| }); | ||
| const joinedValues = values.join(','); | ||
| switch (style) { | ||
| case 'form': | ||
| return `${name}=${joinedValues}`; | ||
| case 'label': | ||
| return `.${joinedValues}`; | ||
| case 'matrix': | ||
| return `;${name}=${joinedValues}`; | ||
| default: | ||
| return joinedValues; | ||
| } | ||
| } | ||
| const separator = separatorObjectExplode(style); | ||
| const joinedValues = Object.entries(value) | ||
| .map(([key, v]) => serializePrimitiveParam({ | ||
| allowReserved, | ||
| name: style === 'deepObject' ? `${name}[${key}]` : key, | ||
| value: v, | ||
| })) | ||
| .join(separator); | ||
| return style === 'label' || style === 'matrix' | ||
| ? separator + joinedValues | ||
| : joinedValues; | ||
| }; |
| // This file is auto-generated by @hey-api/openapi-ts | ||
| import { client as _heyApiClient } from './client.gen'; | ||
| class _HeyApiClient { | ||
| _client = _heyApiClient; | ||
| constructor(args) { | ||
| if (args?.client) { | ||
| this._client = args.client; | ||
| } | ||
| } | ||
| } | ||
| class Event extends _HeyApiClient { | ||
| /** | ||
| * Get events | ||
| */ | ||
| subscribe(options) { | ||
| return (options?.client ?? this._client).get({ | ||
| url: '/event', | ||
| ...options | ||
| }); | ||
| } | ||
| } | ||
| class App extends _HeyApiClient { | ||
| /** | ||
| * Get app info | ||
| */ | ||
| get(options) { | ||
| return (options?.client ?? this._client).get({ | ||
| url: '/app', | ||
| ...options | ||
| }); | ||
| } | ||
| /** | ||
| * Initialize the app | ||
| */ | ||
| init(options) { | ||
| return (options?.client ?? this._client).post({ | ||
| url: '/app/init', | ||
| ...options | ||
| }); | ||
| } | ||
| /** | ||
| * Write a log entry to the server logs | ||
| */ | ||
| log(options) { | ||
| return (options?.client ?? this._client).post({ | ||
| url: '/log', | ||
| ...options, | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| ...options?.headers | ||
| } | ||
| }); | ||
| } | ||
| /** | ||
| * List all modes | ||
| */ | ||
| modes(options) { | ||
| return (options?.client ?? this._client).get({ | ||
| url: '/mode', | ||
| ...options | ||
| }); | ||
| } | ||
| } | ||
| class Config extends _HeyApiClient { | ||
| /** | ||
| * Get config info | ||
| */ | ||
| get(options) { | ||
| return (options?.client ?? this._client).get({ | ||
| url: '/config', | ||
| ...options | ||
| }); | ||
| } | ||
| /** | ||
| * List all providers | ||
| */ | ||
| providers(options) { | ||
| return (options?.client ?? this._client).get({ | ||
| url: '/config/providers', | ||
| ...options | ||
| }); | ||
| } | ||
| } | ||
| class Session extends _HeyApiClient { | ||
| /** | ||
| * List all sessions | ||
| */ | ||
| list(options) { | ||
| return (options?.client ?? this._client).get({ | ||
| url: '/session', | ||
| ...options | ||
| }); | ||
| } | ||
| /** | ||
| * Create a new session | ||
| */ | ||
| create(options) { | ||
| return (options?.client ?? this._client).post({ | ||
| url: '/session', | ||
| ...options | ||
| }); | ||
| } | ||
| /** | ||
| * Delete a session and all its data | ||
| */ | ||
| delete(options) { | ||
| return (options.client ?? this._client).delete({ | ||
| url: '/session/{id}', | ||
| ...options | ||
| }); | ||
| } | ||
| /** | ||
| * Analyze the app and create an AGENTS.md file | ||
| */ | ||
| init(options) { | ||
| return (options.client ?? this._client).post({ | ||
| url: '/session/{id}/init', | ||
| ...options, | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| ...options.headers | ||
| } | ||
| }); | ||
| } | ||
| /** | ||
| * Abort a session | ||
| */ | ||
| abort(options) { | ||
| return (options.client ?? this._client).post({ | ||
| url: '/session/{id}/abort', | ||
| ...options | ||
| }); | ||
| } | ||
| /** | ||
| * Unshare the session | ||
| */ | ||
| unshare(options) { | ||
| return (options.client ?? this._client).delete({ | ||
| url: '/session/{id}/share', | ||
| ...options | ||
| }); | ||
| } | ||
| /** | ||
| * Share a session | ||
| */ | ||
| share(options) { | ||
| return (options.client ?? this._client).post({ | ||
| url: '/session/{id}/share', | ||
| ...options | ||
| }); | ||
| } | ||
| /** | ||
| * Summarize the session | ||
| */ | ||
| summarize(options) { | ||
| return (options.client ?? this._client).post({ | ||
| url: '/session/{id}/summarize', | ||
| ...options, | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| ...options.headers | ||
| } | ||
| }); | ||
| } | ||
| /** | ||
| * List messages for a session | ||
| */ | ||
| messages(options) { | ||
| return (options.client ?? this._client).get({ | ||
| url: '/session/{id}/message', | ||
| ...options | ||
| }); | ||
| } | ||
| /** | ||
| * Create and send a new message to a session | ||
| */ | ||
| chat(options) { | ||
| return (options.client ?? this._client).post({ | ||
| url: '/session/{id}/message', | ||
| ...options, | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| ...options.headers | ||
| } | ||
| }); | ||
| } | ||
| /** | ||
| * Revert a message | ||
| */ | ||
| revert(options) { | ||
| return (options.client ?? this._client).post({ | ||
| url: '/session/{id}/revert', | ||
| ...options, | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| ...options.headers | ||
| } | ||
| }); | ||
| } | ||
| /** | ||
| * Restore all reverted messages | ||
| */ | ||
| unrevert(options) { | ||
| return (options.client ?? this._client).post({ | ||
| url: '/session/{id}/unrevert', | ||
| ...options | ||
| }); | ||
| } | ||
| } | ||
| class Find extends _HeyApiClient { | ||
| /** | ||
| * Find text in files | ||
| */ | ||
| text(options) { | ||
| return (options.client ?? this._client).get({ | ||
| url: '/find', | ||
| ...options | ||
| }); | ||
| } | ||
| /** | ||
| * Find files | ||
| */ | ||
| files(options) { | ||
| return (options.client ?? this._client).get({ | ||
| url: '/find/file', | ||
| ...options | ||
| }); | ||
| } | ||
| /** | ||
| * Find workspace symbols | ||
| */ | ||
| symbols(options) { | ||
| return (options.client ?? this._client).get({ | ||
| url: '/find/symbol', | ||
| ...options | ||
| }); | ||
| } | ||
| } | ||
| class File extends _HeyApiClient { | ||
| /** | ||
| * Read a file | ||
| */ | ||
| read(options) { | ||
| return (options.client ?? this._client).get({ | ||
| url: '/file', | ||
| ...options | ||
| }); | ||
| } | ||
| /** | ||
| * Get file status | ||
| */ | ||
| status(options) { | ||
| return (options?.client ?? this._client).get({ | ||
| url: '/file/status', | ||
| ...options | ||
| }); | ||
| } | ||
| } | ||
| class Tui extends _HeyApiClient { | ||
| /** | ||
| * Append prompt to the TUI | ||
| */ | ||
| appendPrompt(options) { | ||
| return (options?.client ?? this._client).post({ | ||
| url: '/tui/append-prompt', | ||
| ...options, | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| ...options?.headers | ||
| } | ||
| }); | ||
| } | ||
| /** | ||
| * Open the help dialog | ||
| */ | ||
| openHelp(options) { | ||
| return (options?.client ?? this._client).post({ | ||
| url: '/tui/open-help', | ||
| ...options | ||
| }); | ||
| } | ||
| } | ||
| export class OpencodeClient extends _HeyApiClient { | ||
| event = new Event({ client: this._client }); | ||
| app = new App({ client: this._client }); | ||
| config = new Config({ client: this._client }); | ||
| session = new Session({ client: this._client }); | ||
| find = new Find({ client: this._client }); | ||
| file = new File({ client: this._client }); | ||
| tui = new Tui({ client: this._client }); | ||
| } |
| // This file is auto-generated by @hey-api/openapi-ts |
| import { createClient } from "./gen/client/client"; | ||
| import { OpencodeClient } from "./gen/sdk.gen"; | ||
| export function createOpencodeClient(config) { | ||
| const client = createClient(config); | ||
| return new OpencodeClient({ client }); | ||
| } |
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
1
-50%7
-46.15%83699
-27.07%27
-35.71%2831
-26.81%