|
|
@@ -26,14 +26,32 @@ type OpenApiParameter = {
|
|
|
|
|
|
type OpenApiOperation = {
|
|
|
parameters?: OpenApiParameter[]
|
|
|
+ requestBody?: {
|
|
|
+ required?: boolean
|
|
|
+ content?: Record<string, { schema?: OpenApiSchema }>
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
type OpenApiPathItem = Partial<Record<"get" | "post" | "put" | "delete" | "patch", OpenApiOperation>>
|
|
|
|
|
|
type OpenApiSpec = {
|
|
|
+ components?: {
|
|
|
+ schemas?: Record<string, OpenApiSchema>
|
|
|
+ }
|
|
|
paths?: Record<string, OpenApiPathItem>
|
|
|
}
|
|
|
|
|
|
+type OpenApiSchema = {
|
|
|
+ $ref?: string
|
|
|
+ additionalProperties?: OpenApiSchema | boolean
|
|
|
+ allOf?: OpenApiSchema[]
|
|
|
+ anyOf?: OpenApiSchema[]
|
|
|
+ items?: OpenApiSchema
|
|
|
+ oneOf?: OpenApiSchema[]
|
|
|
+ properties?: Record<string, OpenApiSchema>
|
|
|
+ type?: string
|
|
|
+}
|
|
|
+
|
|
|
const InstanceQueryParameters = [
|
|
|
{
|
|
|
name: "directory",
|
|
|
@@ -49,13 +67,28 @@ const InstanceQueryParameters = [
|
|
|
},
|
|
|
] satisfies OpenApiParameter[]
|
|
|
|
|
|
-function documentInstanceQueryParameters(input: Record<string, unknown>) {
|
|
|
+const LegacyBodyRefParameters = new Set(["Auth", "Config", "Part", "WorktreeRemoveInput", "WorktreeResetInput"])
|
|
|
+
|
|
|
+function matchLegacyOpenApi(input: Record<string, unknown>) {
|
|
|
const spec = input as OpenApiSpec
|
|
|
for (const [path, item] of Object.entries(spec.paths ?? {})) {
|
|
|
- if (path.startsWith("/global/") || path.startsWith("/auth/")) continue
|
|
|
+ const isInstanceRoute = !path.startsWith("/global/") && !path.startsWith("/auth/")
|
|
|
for (const method of ["get", "post", "put", "delete", "patch"] as const) {
|
|
|
const operation = item[method]
|
|
|
if (!operation) continue
|
|
|
+ if (operation.requestBody) {
|
|
|
+ delete operation.requestBody.required
|
|
|
+ for (const media of Object.values(operation.requestBody.content ?? {})) {
|
|
|
+ const ref = media.schema?.$ref?.replace("#/components/schemas/", "")
|
|
|
+ if (ref && LegacyBodyRefParameters.has(ref)) continue
|
|
|
+ if (ref && spec.components?.schemas?.[ref]) {
|
|
|
+ media.schema = normalizeRequestSchema(structuredClone(spec.components.schemas[ref]))
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ if (media.schema) media.schema = normalizeRequestSchema(media.schema)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (!isInstanceRoute) continue
|
|
|
operation.parameters = [
|
|
|
...InstanceQueryParameters,
|
|
|
...(operation.parameters ?? []).filter(
|
|
|
@@ -67,6 +100,29 @@ function documentInstanceQueryParameters(input: Record<string, unknown>) {
|
|
|
return input
|
|
|
}
|
|
|
|
|
|
+function normalizeRequestSchema(schema: OpenApiSchema): OpenApiSchema {
|
|
|
+ const options = schema.anyOf ?? schema.oneOf
|
|
|
+ if (options) {
|
|
|
+ const withoutNull = options.filter((item) => item.type !== "null")
|
|
|
+ const finite = withoutNull.find((item) => item.type === "number")
|
|
|
+ if (finite && withoutNull.every((item) => item.type === "number" || item.type === "string")) return finite
|
|
|
+ if (withoutNull.length === 1) return normalizeRequestSchema(withoutNull[0])
|
|
|
+ if (schema.anyOf) schema.anyOf = withoutNull.map(normalizeRequestSchema)
|
|
|
+ if (schema.oneOf) schema.oneOf = withoutNull.map(normalizeRequestSchema)
|
|
|
+ }
|
|
|
+ if (schema.allOf) schema.allOf = schema.allOf.map(normalizeRequestSchema)
|
|
|
+ if (schema.items) schema.items = normalizeRequestSchema(schema.items)
|
|
|
+ if (schema.properties) {
|
|
|
+ for (const [key, value] of Object.entries(schema.properties)) {
|
|
|
+ schema.properties[key] = normalizeRequestSchema(value)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (schema.additionalProperties && typeof schema.additionalProperties === "object") {
|
|
|
+ schema.additionalProperties = normalizeRequestSchema(schema.additionalProperties)
|
|
|
+ }
|
|
|
+ return schema
|
|
|
+}
|
|
|
+
|
|
|
export const PublicApi = HttpApi.make("opencode")
|
|
|
.addHttpApi(ControlApi)
|
|
|
.addHttpApi(GlobalApi)
|
|
|
@@ -91,6 +147,6 @@ export const PublicApi = HttpApi.make("opencode")
|
|
|
title: "opencode",
|
|
|
version: "1.0.0",
|
|
|
description: "opencode api",
|
|
|
- transform: documentInstanceQueryParameters,
|
|
|
+ transform: matchLegacyOpenApi,
|
|
|
}),
|
|
|
)
|