ソースを参照

chore: generate

opencode-agent[bot] 1 ヶ月 前
コミット
1b9b260458

+ 1 - 3
packages/codemode/README.md

@@ -165,9 +165,7 @@ const api = OpenAPI.fromSpec({
   spec: await Bun.file("openapi.json").json(), // parsed document (no YAML)
   auth: {
     resolve: ({ name, scopes, operation }) =>
-      name === "BearerAuth"
-        ? Effect.succeed({ type: "bearer", token })
-        : Effect.succeed(undefined),
+      name === "BearerAuth" ? Effect.succeed({ type: "bearer", token }) : Effect.succeed(undefined),
   },
 })
 

+ 14 - 12
packages/codemode/src/openapi/runtime.ts

@@ -46,9 +46,7 @@ export const invoke = (plan: Plan, input: unknown): Effect.Effect<unknown, unkno
       )
     }
     if (json && Option.isNone(decoded)) {
-      return yield* Effect.fail(
-        toolError(`${plan.operation.method} ${plan.operation.path} returned malformed JSON.`),
-      )
+      return yield* Effect.fail(toolError(`${plan.operation.method} ${plan.operation.path} returned malformed JSON.`))
     }
     return parsed
   })
@@ -208,8 +206,9 @@ const buildUrl = (plan: Plan, input: Readonly<Record<string, unknown>>): string
       return toolError(`Missing required path parameter '${field.inputName}'.`)
     }
     const fieldValue = serializeSimple(field, item, (value) =>
-      encodeURIComponent(value).replace(/[!'()*]/g, (character) =>
-        `%${character.charCodeAt(0).toString(16).toUpperCase()}`,
+      encodeURIComponent(value).replace(
+        /[!'()*]/g,
+        (character) => `%${character.charCodeAt(0).toString(16).toUpperCase()}`,
       ),
     )
     if (fieldValue instanceof ToolError) return fieldValue
@@ -271,10 +270,7 @@ const serializeQuery = (
     if (value.some((item) => item === undefined || (item !== null && typeof item === "object"))) {
       return toolError(`Query parameter '${field.inputName}' contains an unsupported nested value.`)
     }
-    return value.reduce(
-      (current, item) => HttpClientRequest.appendUrlParam(current, field.name, String(item)),
-      request,
-    )
+    return value.reduce((current, item) => HttpClientRequest.appendUrlParam(current, field.name, String(item)), request)
   }
   if (isRecord(value) && field.explode) {
     return Object.entries(value).reduce<HttpClientRequest.HttpClientRequest | ToolError>((current, [name, item]) => {
@@ -289,11 +285,15 @@ const serializeQuery = (
   return rendered instanceof ToolError ? rendered : HttpClientRequest.appendUrlParam(request, field.name, rendered)
 }
 
-const readResponseBody = (response: HttpClientResponse.HttpClientResponse, plan: Plan): Effect.Effect<string, ToolError> =>
+const readResponseBody = (
+  response: HttpClientResponse.HttpClientResponse,
+  plan: Plan,
+): Effect.Effect<string, ToolError> =>
   Effect.gen(function* () {
     const contentLength = response.headers["content-length"]
     const parsedSize = contentLength === undefined ? undefined : Number.parseInt(contentLength, 10)
-    const declaredSize = parsedSize !== undefined && Number.isSafeInteger(parsedSize) && parsedSize >= 0 ? parsedSize : undefined
+    const declaredSize =
+      parsedSize !== undefined && Number.isSafeInteger(parsedSize) && parsedSize >= 0 ? parsedSize : undefined
     if (declaredSize !== undefined && declaredSize > maxResponseBodyBytes) {
       return yield* Effect.fail(toolError(`${plan.operation.method} ${plan.operation.path} response exceeds 50 MiB.`))
     }
@@ -304,7 +304,9 @@ const readResponseBody = (response: HttpClientResponse.HttpClientResponse, plan:
         return Effect.fail(toolError(`${plan.operation.method} ${plan.operation.path} response exceeds 50 MiB.`))
       }
       if (size + chunk.byteLength > body.byteLength) {
-        const grown = Buffer.allocUnsafe(Math.min(maxResponseBodyBytes, Math.max(size + chunk.byteLength, body.byteLength * 2)))
+        const grown = Buffer.allocUnsafe(
+          Math.min(maxResponseBodyBytes, Math.max(size + chunk.byteLength, body.byteLength * 2)),
+        )
         body.copy(grown, 0, 0, size)
         body = grown
       }

+ 7 - 3
packages/codemode/src/openapi/spec.ts

@@ -78,7 +78,9 @@ const isBinaryMediaType = (document: Document, mediaType: string, value: unknown
   return isRecord(schema) && schema.format === "binary"
 }
 
-const jsonContent = (content: Record<string, unknown>): { readonly mediaType: string; readonly schema: unknown } | undefined => {
+const jsonContent = (
+  content: Record<string, unknown>,
+): { readonly mediaType: string; readonly schema: unknown } | undefined => {
   const entry = Object.entries(content).find(([mediaType]) => isJsonMediaType(mediaType))
   return entry !== undefined && isRecord(entry[1]) ? { mediaType: entry[0], schema: entry[1].schema } : undefined
 }
@@ -344,7 +346,7 @@ export const operationOutput = (
   if (outcomes.length === 0) return { ok: true, value: undefined }
   return {
     ok: true,
-    value: withDefinitions(outcomes.length === 1 ? outcomes[0] ?? {} : { anyOf: outcomes }, definitions),
+    value: withDefinitions(outcomes.length === 1 ? (outcomes[0] ?? {}) : { anyOf: outcomes }, definitions),
   }
 }
 
@@ -380,7 +382,9 @@ export const operationPath = (
   namespaces: ReadonlySet<string>,
 ): ReadonlyArray<string> => {
   const raw = nonEmptyString(operation.operationId)
-  const segments = (raw === undefined ? [fallbackOperationId(method, path)] : raw.split(".")).map(sanitizeOperationSegment)
+  const segments = (raw === undefined ? [fallbackOperationId(method, path)] : raw.split(".")).map(
+    sanitizeOperationSegment,
+  )
   if (isOperationPathAvailable(segments, used, namespaces)) return segments
   const conflict = segments.slice(0, -1).findIndex((_, index) => used.has(segments.slice(0, index + 1).join(".")))
   if (conflict >= 0 && conflict + 1 < segments.length) {

+ 5 - 20
packages/codemode/test/fixtures/openapi-happy-path.json

@@ -93,16 +93,10 @@
                   },
                   "role": {
                     "type": "string",
-                    "enum": [
-                      "admin",
-                      "member"
-                    ]
+                    "enum": ["admin", "member"]
                   }
                 },
-                "required": [
-                  "name",
-                  "email"
-                ],
+                "required": ["name", "email"],
                 "additionalProperties": false
               }
             }
@@ -143,9 +137,7 @@
                   "type": "integer"
                 }
               },
-              "required": [
-                "query"
-              ],
+              "required": ["query"],
               "additionalProperties": false
             }
           },
@@ -216,17 +208,10 @@
           },
           "role": {
             "type": "string",
-            "enum": [
-              "admin",
-              "member"
-            ]
+            "enum": ["admin", "member"]
           }
         },
-        "required": [
-          "id",
-          "name",
-          "email"
-        ],
+        "required": ["id", "name", "email"],
         "additionalProperties": false
       }
     },

ファイルの差分が大きいため隠しています
+ 90 - 292
packages/codemode/test/fixtures/opencode-v2-openapi.json


+ 26 - 20
packages/codemode/test/openapi.test.ts

@@ -54,7 +54,9 @@ const json = (value: unknown, status = 200) =>
 
 const singleOperation = (operation: Record<string, unknown>, method = "get"): Document => ({
   openapi: "3.1.0",
-  paths: { "/test": { [method]: { operationId: "test", responses: { 200: { description: "Success" } }, ...operation } } },
+  paths: {
+    "/test": { [method]: { operationId: "test", responses: { 200: { description: "Success" } }, ...operation } },
+  },
 })
 
 describe("OpenAPI.fromSpec", () => {
@@ -94,7 +96,12 @@ describe("OpenAPI.fromSpec", () => {
     const remove = toolAt(api.tools, "users.remove")
 
     expect(api.skipped).toEqual([])
-    if (!Tool.isDefinition(get) || !Tool.isDefinition(create) || !Tool.isDefinition(search) || !Tool.isDefinition(remove)) {
+    if (
+      !Tool.isDefinition(get) ||
+      !Tool.isDefinition(create) ||
+      !Tool.isDefinition(search) ||
+      !Tool.isDefinition(remove)
+    ) {
       throw new Error("happy-path fixture did not generate every operation")
     }
     expect(inputTypeScript(get)).toBe(
@@ -110,7 +117,8 @@ describe("OpenAPI.fromSpec", () => {
 
     const result = await Effect.runPromise(
       CodeMode.make({ tools: { api: api.tools } })
-        .execute(`
+        .execute(
+          `
           const user = await tools.api.users.get({
             userId: "user-1",
             include: ["profile", "permissions"],
@@ -128,7 +136,8 @@ describe("OpenAPI.fromSpec", () => {
           })
           const removed = await tools.api.users.remove({ userId: "user-1" })
           return { user, created, summary, removed }
-        `)
+        `,
+        )
         .pipe(Effect.provide(client.layer)),
     )
 
@@ -482,9 +491,9 @@ describe("OpenAPI.fromSpec", () => {
     expect(url.searchParams.get("nullable")).toBe("null")
     expect(url.searchParams.get("constructor")).toBe("safe")
     expect(client.requests[0]!.headers.meta).toBe("a=b,c=d")
-    await expect(
-      Effect.runPromise(tool.run({ keys: [undefined] }).pipe(Effect.provide(client.layer))),
-    ).rejects.toThrow("unsupported nested value")
+    await expect(Effect.runPromise(tool.run({ keys: [undefined] }).pipe(Effect.provide(client.layer)))).rejects.toThrow(
+      "unsupported nested value",
+    )
   })
 
   test("skips unsupported parameter encodings and malformed security", () => {
@@ -586,7 +595,10 @@ describe("OpenAPI.fromSpec", () => {
 
   test("applies authentication carriers without prototype or collision loss", async () => {
     const client = recordingClient(() => json({ ok: true }))
-    const authenticated = (security: ReadonlyArray<Record<string, ReadonlyArray<string>>>, schemes: Record<string, unknown>) =>
+    const authenticated = (
+      security: ReadonlyArray<Record<string, ReadonlyArray<string>>>,
+      schemes: Record<string, unknown>,
+    ) =>
       OpenAPI.fromSpec({
         baseUrl,
         spec: { ...singleOperation({}), security, components: { securitySchemes: schemes } },
@@ -602,13 +614,10 @@ describe("OpenAPI.fromSpec", () => {
     expect(new URL(client.requests[0]!.url).searchParams.get("__proto__")).toBe("secret")
 
     const duplicate = toolAt(
-      authenticated(
-        [{ first: [], second: [] }],
-        {
-          first: { type: "apiKey", in: "header", name: "x-key" },
-          second: { type: "apiKey", in: "header", name: "x-key" },
-        },
-      ).tools,
+      authenticated([{ first: [], second: [] }], {
+        first: { type: "apiKey", in: "header", name: "x-key" },
+        second: { type: "apiKey", in: "header", name: "x-key" },
+      }).tools,
       "test",
     )
     if (!Tool.isDefinition(duplicate)) throw new Error("duplicate auth tool was not generated")
@@ -633,8 +642,7 @@ describe("OpenAPI.fromSpec", () => {
         },
       },
       auth: {
-        resolve: ({ name }) =>
-          Effect.succeed(name === "bearer" ? { type: "bearer", token: "secret" } : undefined),
+        resolve: ({ name }) => Effect.succeed(name === "bearer" ? { type: "bearer", token: "secret" } : undefined),
       },
     })
     const alternativeTool = toolAt(alternative.tools, "test")
@@ -766,9 +774,7 @@ describe("OpenAPI.fromSpec", () => {
     const oversized = recordingClient(
       () => new Response(null, { headers: { "content-length": String(50 * 1024 * 1024 + 1) } }),
     )
-    const malformed = recordingClient(
-      () => new Response("{", { headers: { "content-type": "application/json" } }),
-    )
+    const malformed = recordingClient(() => new Response("{", { headers: { "content-type": "application/json" } }))
     const chunked = recordingClient(() => new Response(new Uint8Array(50 * 1024 * 1024 + 1)))
 
     await expect(Effect.runPromise(tool.run({}).pipe(Effect.provide(oversized.layer)))).rejects.toThrow(

+ 4 - 5
packages/codemode/test/signature.test.ts

@@ -308,10 +308,7 @@ describe("union schemas render every alternative", () => {
 
   test("allOf renders intersections with parenthesized union members", () => {
     const schema = {
-      allOf: [
-        { type: "object", properties: { id: { type: "string" } } },
-        { type: ["string", "null"] },
-      ],
+      allOf: [{ type: "object", properties: { id: { type: "string" } } }, { type: ["string", "null"] }],
     } as const
     expect(jsonSchemaToTypeScript(schema)).toBe("{ id?: string } & (string | null)")
   })
@@ -321,7 +318,9 @@ describe("union schemas render every alternative", () => {
       "unknown",
     )
     expect(
-      jsonSchemaToTypeScript({ allOf: [{ type: "string" }, { allOf: [{ $ref: "https://example.com/external.json" }] }] }),
+      jsonSchemaToTypeScript({
+        allOf: [{ type: "string" }, { allOf: [{ $ref: "https://example.com/external.json" }] }],
+      }),
     ).toBe("unknown")
     expect(
       jsonSchemaToTypeScript({

+ 1 - 0
packages/sdk/openapi.json

@@ -14507,6 +14507,7 @@
         },
         "description": "Establish a WebSocket connection streaming PTY output and accepting terminal input.",
         "summary": "Connect to PTY session",
+        "x-websocket": true,
         "x-codeSamples": [
           {
             "lang": "js",

この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません