Haris Gušić 4 месяцев назад
Родитель
Сommit
92bc78a2d3
2 измененных файлов с 94 добавлено и 21 удалено
  1. 55 11
      packages/opencode/src/server/server.ts
  2. 39 10
      packages/opencode/src/storage/storage.ts

+ 55 - 11
packages/opencode/src/server/server.ts

@@ -33,6 +33,8 @@ import { lazy } from "../util/lazy"
 import { Todo } from "../session/todo"
 import { Todo } from "../session/todo"
 import { InstanceBootstrap } from "../project/bootstrap"
 import { InstanceBootstrap } from "../project/bootstrap"
 import { MCP } from "../mcp"
 import { MCP } from "../mcp"
+import { Storage } from "../storage/storage"
+import type { ContentfulStatusCode } from "hono/utils/http-status"
 
 
 const ERRORS = {
 const ERRORS = {
   400: {
   400: {
@@ -42,17 +44,33 @@ const ERRORS = {
         schema: resolver(
         schema: resolver(
           z
           z
             .object({
             .object({
-              data: z.record(z.string(), z.any()),
+              data: z.any().nullable(),
+              errors: z.array(z.record(z.string(), z.any())),
+              success: z.literal(false),
             })
             })
             .meta({
             .meta({
-              ref: "Error",
+              ref: "BadRequestError",
             }),
             }),
         ),
         ),
       },
       },
     },
     },
   },
   },
+  404: {
+    description: "Not found",
+    content: {
+      "application/json": {
+        schema: resolver(
+          Storage.NotFoundError.Schema
+        )
+      },
+    },
+  },
 } as const
 } as const
 
 
+function errors(...codes: number[]) {
+  return Object.fromEntries(codes.map((code) => [code, ERRORS[code as keyof typeof ERRORS]]))
+}
+
 export namespace Server {
 export namespace Server {
   const log = Log.create({ service: "server" })
   const log = Log.create({ service: "server" })
 
 
@@ -68,13 +86,18 @@ export namespace Server {
           error: err,
           error: err,
         })
         })
         if (err instanceof NamedError) {
         if (err instanceof NamedError) {
-          return c.json(err.toObject(), {
-            status: 400,
-          })
+          let status: ContentfulStatusCode
+          if (err instanceof Storage.NotFoundError)
+            status = 404
+          else if (err instanceof Provider.ModelNotFoundError)
+            status = 400
+          else
+            status = 500
+          return c.json(err.toObject(), { status })
         }
         }
         const message = err instanceof Error && err.stack ? err.stack : err.toString()
         const message = err instanceof Error && err.stack ? err.stack : err.toString()
         return c.json(new NamedError.Unknown({ message }).toObject(), {
         return c.json(new NamedError.Unknown({ message }).toObject(), {
-          status: 400,
+          status: 500,
         })
         })
       })
       })
       .use(async (c, next) => {
       .use(async (c, next) => {
@@ -153,7 +176,7 @@ export namespace Server {
                 },
                 },
               },
               },
             },
             },
-            ...ERRORS,
+            ...errors(400),
           },
           },
         }),
         }),
         validator("json", Config.Info),
         validator("json", Config.Info),
@@ -177,7 +200,7 @@ export namespace Server {
                 },
                 },
               },
               },
             },
             },
-            ...ERRORS,
+            ...errors(400),
           },
           },
         }),
         }),
         async (c) => {
         async (c) => {
@@ -210,7 +233,7 @@ export namespace Server {
                 },
                 },
               },
               },
             },
             },
-            ...ERRORS,
+            ...errors(400),
           },
           },
         }),
         }),
         validator(
         validator(
@@ -305,6 +328,7 @@ export namespace Server {
                 },
                 },
               },
               },
             },
             },
+            ...errors(400, 404),
           },
           },
         }),
         }),
         validator(
         validator(
@@ -333,6 +357,7 @@ export namespace Server {
                 },
                 },
               },
               },
             },
             },
+            ...errors(400, 404),
           },
           },
         }),
         }),
         validator(
         validator(
@@ -361,6 +386,7 @@ export namespace Server {
                 },
                 },
               },
               },
             },
             },
+            ...errors(400, 404),
           },
           },
         }),
         }),
         validator(
         validator(
@@ -381,7 +407,7 @@ export namespace Server {
           description: "Create a new session",
           description: "Create a new session",
           operationId: "session.create",
           operationId: "session.create",
           responses: {
           responses: {
-            ...ERRORS,
+            ...errors(400),
             200: {
             200: {
               description: "Successfully created session",
               description: "Successfully created session",
               content: {
               content: {
@@ -413,6 +439,7 @@ export namespace Server {
                 },
                 },
               },
               },
             },
             },
+            ...errors(400, 404),
           },
           },
         }),
         }),
         validator(
         validator(
@@ -440,6 +467,7 @@ export namespace Server {
                 },
                 },
               },
               },
             },
             },
+            ...errors(400, 404),
           },
           },
         }),
         }),
         validator(
         validator(
@@ -481,6 +509,7 @@ export namespace Server {
                 },
                 },
               },
               },
             },
             },
+            ...errors(400, 404),
           },
           },
         }),
         }),
         validator(
         validator(
@@ -541,6 +570,7 @@ export namespace Server {
                 },
                 },
               },
               },
             },
             },
+            ...errors(400, 404),
           },
           },
         }),
         }),
         validator(
         validator(
@@ -567,6 +597,7 @@ export namespace Server {
                 },
                 },
               },
               },
             },
             },
+            ...errors(400, 404),
           },
           },
         }),
         }),
         validator(
         validator(
@@ -596,6 +627,7 @@ export namespace Server {
                 },
                 },
               },
               },
             },
             },
+            ...errors(400, 404),
           },
           },
         }),
         }),
         validator(
         validator(
@@ -625,6 +657,7 @@ export namespace Server {
                 },
                 },
               },
               },
             },
             },
+            ...errors(400, 404),
           },
           },
         }),
         }),
         validator(
         validator(
@@ -661,6 +694,7 @@ export namespace Server {
                 },
                 },
               },
               },
             },
             },
+            ...errors(400, 404),
           },
           },
         }),
         }),
         validator(
         validator(
@@ -693,6 +727,7 @@ export namespace Server {
                 },
                 },
               },
               },
             },
             },
+            ...errors(400, 404),
           },
           },
         }),
         }),
         validator(
         validator(
@@ -727,6 +762,7 @@ export namespace Server {
                 },
                 },
               },
               },
             },
             },
+            ...errors(400, 404),
           },
           },
         }),
         }),
         validator(
         validator(
@@ -762,6 +798,7 @@ export namespace Server {
                 },
                 },
               },
               },
             },
             },
+            ...errors(400, 404),
           },
           },
         }),
         }),
         validator(
         validator(
@@ -792,6 +829,7 @@ export namespace Server {
                 },
                 },
               },
               },
             },
             },
+            ...errors(400, 404),
           },
           },
         }),
         }),
         validator(
         validator(
@@ -822,6 +860,7 @@ export namespace Server {
                 },
                 },
               },
               },
             },
             },
+            ...errors(400, 404),
           },
           },
         }),
         }),
         validator(
         validator(
@@ -852,6 +891,7 @@ export namespace Server {
                 },
                 },
               },
               },
             },
             },
+            ...errors(400, 404),
           },
           },
         }),
         }),
         validator(
         validator(
@@ -879,6 +919,7 @@ export namespace Server {
                 },
                 },
               },
               },
             },
             },
+            ...errors(400, 404),
           },
           },
         }),
         }),
         validator(
         validator(
@@ -1132,6 +1173,7 @@ export namespace Server {
                 },
                 },
               },
               },
             },
             },
+            ...errors(400),
           },
           },
         }),
         }),
         validator(
         validator(
@@ -1223,6 +1265,7 @@ export namespace Server {
                 },
                 },
               },
               },
             },
             },
+            ...errors(400),
           },
           },
         }),
         }),
         validator(
         validator(
@@ -1355,6 +1398,7 @@ export namespace Server {
                 },
                 },
               },
               },
             },
             },
+            ...errors(400),
           },
           },
         }),
         }),
         validator(
         validator(
@@ -1406,7 +1450,7 @@ export namespace Server {
                 },
                 },
               },
               },
             },
             },
-            ...ERRORS,
+            ...errors(400),
           },
           },
         }),
         }),
         validator(
         validator(

+ 39 - 10
packages/opencode/src/storage/storage.ts

@@ -5,12 +5,21 @@ import { Global } from "../global"
 import { lazy } from "../util/lazy"
 import { lazy } from "../util/lazy"
 import { Lock } from "../util/lock"
 import { Lock } from "../util/lock"
 import { $ } from "bun"
 import { $ } from "bun"
+import { NamedError } from "@/util/error"
+import z from "zod"
 
 
 export namespace Storage {
 export namespace Storage {
   const log = Log.create({ service: "storage" })
   const log = Log.create({ service: "storage" })
 
 
   type Migration = (dir: string) => Promise<void>
   type Migration = (dir: string) => Promise<void>
 
 
+  export const NotFoundError = NamedError.create(
+    "NotFoundError",
+    z.object({
+      message: z.string(),
+    }),
+  )
+
   const MIGRATIONS: Migration[] = [
   const MIGRATIONS: Migration[] = [
     async (dir) => {
     async (dir) => {
       const project = path.resolve(dir, "../project")
       const project = path.resolve(dir, "../project")
@@ -131,31 +140,51 @@ export namespace Storage {
   export async function remove(key: string[]) {
   export async function remove(key: string[]) {
     const dir = await state().then((x) => x.dir)
     const dir = await state().then((x) => x.dir)
     const target = path.join(dir, ...key) + ".json"
     const target = path.join(dir, ...key) + ".json"
-    await fs.unlink(target).catch(() => {})
+    return withErrorHandling(async () => {
+      await fs.unlink(target).catch(() => {})
+    })
   }
   }
 
 
   export async function read<T>(key: string[]) {
   export async function read<T>(key: string[]) {
     const dir = await state().then((x) => x.dir)
     const dir = await state().then((x) => x.dir)
     const target = path.join(dir, ...key) + ".json"
     const target = path.join(dir, ...key) + ".json"
-    using _ = await Lock.read(target)
-    return Bun.file(target).json() as Promise<T>
+    return withErrorHandling(async () => {
+      using _ = await Lock.read(target)
+      return Bun.file(target).json() as Promise<T>
+    })
   }
   }
 
 
   export async function update<T>(key: string[], fn: (draft: T) => void) {
   export async function update<T>(key: string[], fn: (draft: T) => void) {
     const dir = await state().then((x) => x.dir)
     const dir = await state().then((x) => x.dir)
     const target = path.join(dir, ...key) + ".json"
     const target = path.join(dir, ...key) + ".json"
-    using _ = await Lock.write("storage")
-    const content = await Bun.file(target).json()
-    fn(content)
-    await Bun.write(target, JSON.stringify(content, null, 2))
-    return content as T
+    return withErrorHandling(async () => {
+      using _ = await Lock.write("storage")
+      const content = await Bun.file(target).json()
+      fn(content)
+      await Bun.write(target, JSON.stringify(content, null, 2))
+      return content as T
+    })
   }
   }
 
 
   export async function write<T>(key: string[], content: T) {
   export async function write<T>(key: string[], content: T) {
     const dir = await state().then((x) => x.dir)
     const dir = await state().then((x) => x.dir)
     const target = path.join(dir, ...key) + ".json"
     const target = path.join(dir, ...key) + ".json"
-    using _ = await Lock.write("storage")
-    await Bun.write(target, JSON.stringify(content, null, 2))
+    return withErrorHandling(async () => {
+      using _ = await Lock.write("storage")
+      await Bun.write(target, JSON.stringify(content, null, 2))
+    })
+  }
+
+  async function withErrorHandling<T>(body: () => Promise<T>) {
+    return body().catch((e) => {
+      if (!(e instanceof Error))
+        throw e
+      const errnoException = e as NodeJS.ErrnoException
+      if (errnoException.code === "ENOENT") {
+        throw new NotFoundError({ message: `Resource not found: ${errnoException.path}` })
+      }
+      throw e
+    })
   }
   }
 
 
   const glob = new Bun.Glob("**/*")
   const glob = new Bun.Glob("**/*")