diff options
| author | Dax Raad <[email protected]> | 2025-05-18 22:30:41 -0400 |
|---|---|---|
| committer | Dax Raad <[email protected]> | 2025-05-26 12:40:17 -0400 |
| commit | 99af6146d5def31c59993636d60eb75a483a283b (patch) | |
| tree | eb57ed227c15cf9be54bc9f231c83895d812f881 /js | |
| parent | 020e0ca039287b73fa33041fbd1bb214e6ccb396 (diff) | |
| download | opencode-99af6146d5def31c59993636d60eb75a483a283b.tar.gz opencode-99af6146d5def31c59993636d60eb75a483a283b.zip | |
openapi
Diffstat (limited to 'js')
| -rw-r--r-- | js/bun.lock | 31 | ||||
| -rw-r--r-- | js/openapi.json | 90 | ||||
| -rw-r--r-- | js/package.json | 5 | ||||
| -rw-r--r-- | js/src/app/config.ts | 2 | ||||
| -rw-r--r-- | js/src/bus/index.ts | 2 | ||||
| -rw-r--r-- | js/src/id/id.ts | 2 | ||||
| -rw-r--r-- | js/src/index.ts | 35 | ||||
| -rw-r--r-- | js/src/server/server.ts | 60 | ||||
| -rw-r--r-- | js/src/session/session.ts | 22 | ||||
| -rw-r--r-- | js/src/storage/storage.ts | 2 |
10 files changed, 220 insertions, 31 deletions
diff --git a/js/bun.lock b/js/bun.lock index ba45595a4..e6d8f9074 100644 --- a/js/bun.lock +++ b/js/bun.lock @@ -9,8 +9,11 @@ "@flystorage/local-fs": "^1.1.0", "@hono/zod-validator": "^0.5.0", "ai": "^5.0.0-alpha.2", + "clipanion": "^4.0.0-rc.4", "hono": "^4.7.10", - "zod": "^3.25.0-beta.20250518T002810", + "hono-openapi": "^0.4.8", + "zod": "^3.24.4", + "zod-openapi": "^4.2.4", }, "devDependencies": { "@tsconfig/bun": "^1.0.7", @@ -34,6 +37,8 @@ "@alcalzone/ansi-tokenize": ["@alcalzone/[email protected]", "", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^4.0.0" } }, "sha512-3yWxPTq3UQ/FY9p1ErPxIyfT64elWaMvM9lIHnaqpyft63tkxodF5aUElYHrdisWve5cETkh1+KBw1yJuW0aRw=="], + "@apidevtools/json-schema-ref-parser": ["@apidevtools/[email protected]", "", { "dependencies": { "@jsdevtools/ono": "^7.1.3", "@types/json-schema": "^7.0.15", "js-yaml": "^4.1.0" } }, "sha512-60vepv88RwcJtSHrD6MjIL6Ta3SOYbgfnkHb+ppAVK+o9mXprRtulx7VlRl3lN3bbvysAfCS7WMVfhUYemB0IQ=="], + "@flystorage/dynamic-import": ["@flystorage/[email protected]", "", {}, "sha512-CIbIUrBdaPFyKnkVBaqzksvzNtsMSXITR/G/6zlil3MBnPFq2LX+X4Mv5p2XOmv/3OulFs/ff2SNb+5dc2Twtg=="], "@flystorage/file-storage": ["@flystorage/[email protected]", "", {}, "sha512-25Gd5EsXDmhHrK5orpRuVqebQms1Cm9m5ACMZ0sVDX+Sbl1V0G88CbcWt7mEoWRYLvQ1U072htqg6Sav76ZlVA=="], @@ -42,6 +47,8 @@ "@hono/zod-validator": ["@hono/[email protected]", "", { "peerDependencies": { "hono": ">=3.9.0", "zod": "^3.19.1" } }, "sha512-ds5bW6DCgAnNHP33E3ieSbaZFd5dkV52ZjyaXtGoR06APFrCtzAsKZxTHwOrJNBdXsi0e5wNwo5L4nVEVnJUdg=="], + "@jsdevtools/ono": ["@jsdevtools/[email protected]", "", {}, "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg=="], + "@opentelemetry/api": ["@opentelemetry/[email protected]", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="], "@standard-schema/spec": ["@standard-schema/[email protected]", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="], @@ -54,6 +61,8 @@ "@types/bun": ["@types/[email protected]", "", { "dependencies": { "bun-types": "1.2.13" } }, "sha512-u6vXep/i9VBxoJl3GjZsl/BFIsvML8DfVDO0RYLEwtSZSp981kEO1V5NwRcO1CPJ7AmvpbnDCiMKo3JvbDEjAg=="], + "@types/json-schema": ["@types/[email protected]", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], + "@types/node": ["@types/[email protected]", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-v1DKRfUdyW+jJhZNEI1PYy29S2YRxMV5AOO/x/SjKmW0acCIOqmbj6Haf9eHAhsPmrhlHSxEhv/1WszcLWV4cg=="], "@types/prop-types": ["@types/[email protected]", "", {}, "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ=="], @@ -68,6 +77,8 @@ "ansi-styles": ["[email protected]", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="], + "argparse": ["[email protected]", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + "auto-bind": ["[email protected]", "", {}, "sha512-ooviqdwwgfIfNmDwo94wlshcdzfO64XV0Cg6oDsDYBJfITDz1EngD2z7DkbvCWn+XIMsIqW27sEVF6qcpJrRcg=="], "bun-types": ["[email protected]", "", { "dependencies": { "@types/node": "*" } }, "sha512-rRjA1T6n7wto4gxhAO/ErZEtOXyEZEmnIHQfl0Dt1QQSB4QV0iP6BZ9/YB5fZaHFQ2dwHFrmPaRQ9GGMX01k9Q=="], @@ -80,6 +91,10 @@ "cli-truncate": ["[email protected]", "", { "dependencies": { "slice-ansi": "^5.0.0", "string-width": "^7.0.0" } }, "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA=="], + "clipanion": ["[email protected]", "", { "dependencies": { "typanion": "^3.8.0" } }, "sha512-CXkMQxU6s9GklO/1f714dkKBMu1lopS1WFF0B8o4AxPykR1hpozxSiUZ5ZUeBjfPgCWqbcNOtZVFhB8Lkfp1+Q=="], + + "clone": ["[email protected]", "", {}, "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w=="], + "code-excerpt": ["[email protected]", "", { "dependencies": { "convert-to-spaces": "^2.0.1" } }, "sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA=="], "convert-to-spaces": ["[email protected]", "", {}, "sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ=="], @@ -104,6 +119,8 @@ "hono": ["[email protected]", "", {}, "sha512-QkACju9MiN59CKSY5JsGZCYmPZkA6sIW6OFCUp7qDjZu6S6KHtJHhAc9Uy9mV9F8PJ1/HQ3ybZF2yjCa/73fvQ=="], + "hono-openapi": ["[email protected]", "", { "dependencies": { "json-schema-walker": "^2.0.0" }, "peerDependencies": { "@hono/arktype-validator": "^2.0.0", "@hono/effect-validator": "^1.2.0", "@hono/typebox-validator": "^0.2.0 || ^0.3.0", "@hono/valibot-validator": "^0.5.1", "@hono/zod-validator": "^0.4.1", "@sinclair/typebox": "^0.34.9", "@valibot/to-json-schema": "^1.0.0-beta.3", "arktype": "^2.0.0", "effect": "^3.11.3", "hono": "^4.6.13", "openapi-types": "^12.1.3", "valibot": "^1.0.0-beta.9", "zod": "^3.23.8", "zod-openapi": "^4.0.0" }, "optionalPeers": ["@hono/arktype-validator", "@hono/effect-validator", "@hono/typebox-validator", "@hono/valibot-validator", "@hono/zod-validator", "@sinclair/typebox", "@valibot/to-json-schema", "arktype", "effect", "hono", "valibot", "zod", "zod-openapi"] }, "sha512-LYr5xdtD49M7hEAduV1PftOMzuT8ZNvkyWfh1DThkLsIr4RkvDb12UxgIiFbwrJB6FLtFXLoOZL9x4IeDk2+VA=="], + "ieee754": ["[email protected]", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], "indent-string": ["[email protected]", "", {}, "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg=="], @@ -118,8 +135,12 @@ "js-tokens": ["[email protected]", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + "js-yaml": ["[email protected]", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="], + "json-schema": ["[email protected]", "", {}, "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="], + "json-schema-walker": ["[email protected]", "", { "dependencies": { "@apidevtools/json-schema-ref-parser": "^11.1.0", "clone": "^2.1.2" } }, "sha512-nXN2cMky0Iw7Af28w061hmxaPDaML5/bQD9nwm1lOoIKEGjHcRGxqWe4MfrkYThYAPjSUhmsp4bJNoLAyVn9Xw=="], + "loose-envify": ["[email protected]", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="], "mime-db": ["[email protected]", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], @@ -132,6 +153,8 @@ "onetime": ["[email protected]", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="], + "openapi-types": ["[email protected]", "", {}, "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw=="], + "patch-console": ["[email protected]", "", {}, "sha512-0YNdUceMdaQwoKce1gatDScmMo5pu/tfABfnzEqeG0gtTmd7mh/WcwgUjtAeOU7N8nFFlbQBnFK2gXW5fGvmMA=="], "peek-readable": ["[email protected]", "", {}, "sha512-nri2TO5JE3/mRryik9LlHFT53cgHfRK0Lt0BAZQXku/AW3E6XLt2GaY8siWi7dvW/m1z0ecn+J+bpDa9ZN3IsQ=="], @@ -158,6 +181,8 @@ "token-types": ["[email protected]", "", { "dependencies": { "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" } }, "sha512-lbDrTLVsHhOMljPscd0yitpozq7Ga2M5Cvez5AjGg8GASBjtt6iERCAJ93yommPmz62fb45oFIXHEZ3u9bfJEA=="], + "typanion": ["[email protected]", "", {}, "sha512-ZW/lVMRabETuYCd9O9ZvMhAh8GslSqaUjxmK/JLPCh6l73CvLBiuXswj/+7LdnWOgYsQ130FqLzFz5aGT4I3Ug=="], + "type-fest": ["[email protected]", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="], "typescript": ["[email protected]", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="], @@ -174,7 +199,9 @@ "yoga-layout": ["[email protected]", "", {}, "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ=="], - "zod": ["[email protected]", "", {}, "sha512-3/aIqMbUXG9EjTelJkDcWd+izJP5MxFgQEMSYI8n41pwYhRDYYxy2dnbkgfNcnLbFZ9uByZn9XXqHTh05QHqSQ=="], + "zod": ["[email protected]", "", {}, "sha512-OdqJE9UDRPwWsrHjLN2F8bPxvwJBK22EHLWtanu0LSYr5YqzsaaW3RMgmjwr8Rypg5k+meEJdSPXJZXE/yqOMg=="], + + "zod-openapi": ["[email protected]", "", { "peerDependencies": { "zod": "^3.21.4" } }, "sha512-tsrQpbpqFCXqVXUzi3TPwFhuMtLN3oNZobOtYnK6/5VkXsNdnIgyNr4r8no4wmYluaxzN3F7iS+8xCW8BmMQ8g=="], "zod-to-json-schema": ["[email protected]", "", { "peerDependencies": { "zod": "^3.24.1" } }, "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g=="], diff --git a/js/openapi.json b/js/openapi.json new file mode 100644 index 000000000..8dbe78550 --- /dev/null +++ b/js/openapi.json @@ -0,0 +1,90 @@ +{ + "openapi": "3.1.0", + "info": { + "title": "opencode", + "description": "opencode api", + "version": "1.0.0" + }, + "paths": { + "/session_create": { + "post": { + "responses": { + "200": { + "description": "Successfully created session", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "pattern": "^ses" + }, + "title": { + "type": "string" + }, + "tokens": { + "type": "object", + "properties": { + "input": { + "type": "number" + }, + "output": { + "type": "number" + }, + "reasoning": { + "type": "number" + } + }, + "required": [ + "input", + "output", + "reasoning" + ] + } + }, + "required": [ + "id", + "title", + "tokens" + ] + } + } + } + } + }, + "operationId": "postSession_create", + "parameters": [], + "description": "Create a new session" + } + }, + "/session_chat": { + "post": { + "responses": {}, + "operationId": "postSession_chat", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "sessionID": { + "type": "string" + }, + "parts": {} + }, + "required": [ + "sessionID" + ] + } + } + } + } + } + } + }, + "components": { + "schemas": {} + } +}
\ No newline at end of file diff --git a/js/package.json b/js/package.json index 03b9ad035..63429b846 100644 --- a/js/package.json +++ b/js/package.json @@ -19,7 +19,10 @@ "@flystorage/local-fs": "^1.1.0", "@hono/zod-validator": "^0.5.0", "ai": "^5.0.0-alpha.2", + "clipanion": "^4.0.0-rc.4", "hono": "^4.7.10", - "zod": "^3.25.0-beta.20250518T002810" + "hono-openapi": "^0.4.8", + "zod": "^3.24.4", + "zod-openapi": "^4.2.4" } } diff --git a/js/src/app/config.ts b/js/src/app/config.ts index 84960db61..947298c67 100644 --- a/js/src/app/config.ts +++ b/js/src/app/config.ts @@ -1,6 +1,6 @@ import path from "node:path"; import { Log } from "../util/log"; -import { z } from "zod/v4"; +import { z } from "zod"; export namespace Config { const log = Log.create({ service: "config" }); diff --git a/js/src/bus/index.ts b/js/src/bus/index.ts index 5359debd9..4f3b40041 100644 --- a/js/src/bus/index.ts +++ b/js/src/bus/index.ts @@ -1,4 +1,4 @@ -import type { z, ZodSchema } from "zod/v4"; +import type { z, ZodSchema } from "zod"; import { App } from "../app"; import { Log } from "../util/log"; diff --git a/js/src/id/id.ts b/js/src/id/id.ts index 39ee5326f..96ea870a1 100644 --- a/js/src/id/id.ts +++ b/js/src/id/id.ts @@ -1,4 +1,4 @@ -import { z } from "zod/v4"; +import { z } from "zod"; import { randomBytes } from "crypto"; export namespace Identifier { diff --git a/js/src/index.ts b/js/src/index.ts index 8f98310be..10b3fef32 100644 --- a/js/src/index.ts +++ b/js/src/index.ts @@ -1,11 +1,34 @@ import { App } from "./app"; -import process from "node:process"; import { Server } from "./server/server"; +import { Cli, Command, runExit } from "clipanion"; -const app = await App.create({ - directory: process.cwd(), +const cli = new Cli({ + binaryLabel: `opencode`, + binaryName: `opencode`, + binaryVersion: `1.0.0`, }); -App.provide(app, async () => { - const server = Server.listen(); -}); +cli.register( + class Run extends Command { + async execute() { + const app = await App.create({ + directory: process.cwd(), + }); + + await App.provide(app, async () => { + const server = Server.listen(); + }); + } + }, +); +cli.register( + class OpenApi extends Command { + static paths = [["openapi"]]; + async execute() { + const specs = await Server.openapi(); + this.context.stdout.write(JSON.stringify(specs, null, 2)); + } + }, +); +const [_bun, _app, ...args] = process.argv; +cli.runExit(args); diff --git a/js/src/server/server.ts b/js/src/server/server.ts index 7ed67057c..5d9333e45 100644 --- a/js/src/server/server.ts +++ b/js/src/server/server.ts @@ -1,10 +1,10 @@ import { Log } from "../util/log"; import { Bus } from "../bus"; - +import { describeRoute, generateSpecs, openAPISpecs } from "hono-openapi"; import { Hono } from "hono"; import { streamSSE } from "hono/streaming"; import { Session } from "../session/session"; -import { zValidator } from "@hono/zod-validator"; +import { resolver, validator as zValidator } from "hono-openapi/zod"; import { z } from "zod"; export namespace Server { @@ -14,7 +14,21 @@ export namespace Server { export type App = ReturnType<typeof app>; function app() { - return new Hono() + const app = new Hono(); + + const result = app + .get( + "/openapi", + openAPISpecs(app, { + documentation: { + info: { + title: "opencode", + version: "1.0.0", + description: "opencode api", + }, + }, + }), + ) .get("/event", async (c) => { log.info("event connected"); return streamSSE(c, async (stream) => { @@ -32,10 +46,26 @@ export namespace Server { }); }); }) - .post("/session_create", async (c) => { - const session = await Session.create(); - return c.json(session); - }) + .post( + "/session_create", + describeRoute({ + description: "Create a new session", + responses: { + 200: { + description: "Successfully created session", + content: { + "application/json": { + schema: resolver(Session.Info), + }, + }, + }, + }, + }), + async (c) => { + const session = await Session.create(); + return c.json(session); + }, + ) .post( "/session_chat", zValidator( @@ -51,6 +81,22 @@ export namespace Server { return c.json(msg); }, ); + + return result; + } + + export async function openapi() { + const a = app(); + const result = await generateSpecs(a, { + documentation: { + info: { + title: "opencode", + version: "1.0.0", + description: "opencode api", + }, + }, + }); + return result; } export function listen() { diff --git a/js/src/session/session.ts b/js/src/session/session.ts index fb45f0e59..e86048b1c 100644 --- a/js/src/session/session.ts +++ b/js/src/session/session.ts @@ -1,5 +1,4 @@ import path from "path"; -import { z } from "zod/v3"; import { App } from "../app/"; import { Identifier } from "../id/id"; import { LLM } from "../llm/llm"; @@ -8,26 +7,27 @@ import { Log } from "../util/log"; import { convertToModelMessages, streamText, - tool, type TextUIPart, type ToolInvocationUIPart, type UIDataTypes, type UIMessage, type UIMessagePart, } from "ai"; +import { z } from "zod"; export namespace Session { const log = Log.create({ service: "session" }); - export interface Info { - id: string; - title: string; - tokens: { - input: number; - output: number; - reasoning: number; - }; - } + export const Info = z.object({ + id: Identifier.schema("session"), + title: z.string(), + tokens: z.object({ + input: z.number(), + output: z.number(), + reasoning: z.number(), + }), + }); + export type Info = z.output<typeof Info>; export type Message = UIMessage<{ sessionID: string }>; diff --git a/js/src/storage/storage.ts b/js/src/storage/storage.ts index 50364beeb..c24666c9f 100644 --- a/js/src/storage/storage.ts +++ b/js/src/storage/storage.ts @@ -5,7 +5,7 @@ import { Log } from "../util/log"; import { App } from "../app"; import { AppPath } from "../app/path"; import { Bus } from "../bus"; -import z from "zod/v4"; +import z from "zod"; export namespace Storage { const log = Log.create({ service: "storage" }); |
