Просмотр исходного кода

core: cleaner error output and more flexible custom tool directories

- Removed debug console.log when dependency installation fails so users see clean warning messages instead of raw error dumps
- Fixed database connection cleanup to prevent resource leaks between sessions
- Added support for loading custom tools from both .opencode/tool (singular) and .opencode/tools (plural) directories, matching common naming conventions
Dax Raad 1 месяц назад
Родитель
Сommit
21e72cbf42

+ 0 - 1
packages/opencode/src/config/config.ts

@@ -288,7 +288,6 @@ export namespace Config {
     // Install any additional dependencies defined in the package.json
     // This allows local plugins and custom tools to use external packages
     await Npm.install(dir).catch((err: any) => {
-      console.log(err)
       log.warn("failed to install dependencies", { dir, error: err.message })
     })
   }

+ 1 - 0
packages/opencode/src/storage/db.ts

@@ -109,6 +109,7 @@ export namespace Database {
 
   export function close() {
     Client().$client.close()
+    Client.reset()
   }
 
   export type TxOrDb = Transaction | Client

+ 111 - 41
packages/opencode/test/tool/registry.test.ts

@@ -5,48 +5,118 @@ import { tmpdir } from "../fixture/fixture"
 import { Instance } from "../../src/project/instance"
 import { ToolRegistry } from "../../src/tool/registry"
 
-test("loads tools with external dependencies without crashing", async () => {
-  await using tmp = await tmpdir({
-    init: async (dir) => {
-      const opencodeDir = path.join(dir, ".opencode")
-      await fs.mkdir(opencodeDir, { recursive: true })
-
-      const toolsDir = path.join(opencodeDir, "tools")
-      await fs.mkdir(toolsDir, { recursive: true })
-
-      await Bun.write(
-        path.join(opencodeDir, "package.json"),
-        JSON.stringify({
-          name: "custom-tools",
-          dependencies: {
-            "@opencode-ai/plugin": "^0.0.0",
-            cowsay: "^1.6.0",
-          },
-        }),
-      )
-
-      await Bun.write(
-        path.join(toolsDir, "cowsay.ts"),
-        [
-          "import { say } from 'cowsay'",
-          "export default {",
-          "  description: 'tool that imports cowsay at top level',",
-          "  args: { text: { type: 'string' } },",
-          "  execute: async ({ text }: { text: string }) => {",
-          "    return say({ text })",
-          "  },",
-          "}",
-          "",
-        ].join("\n"),
-      )
-    },
+describe("tool.registry", () => {
+  test("loads tools from .opencode/tool (singular)", async () => {
+    await using tmp = await tmpdir({
+      init: async (dir) => {
+        const opencodeDir = path.join(dir, ".opencode")
+        await fs.mkdir(opencodeDir, { recursive: true })
+
+        const toolDir = path.join(opencodeDir, "tool")
+        await fs.mkdir(toolDir, { recursive: true })
+
+        await Bun.write(
+          path.join(toolDir, "hello.ts"),
+          [
+            "export default {",
+            "  description: 'hello tool',",
+            "  args: {},",
+            "  execute: async () => {",
+            "    return 'hello world'",
+            "  },",
+            "}",
+            "",
+          ].join("\n"),
+        )
+      },
+    })
+
+    await Instance.provide({
+      directory: tmp.path,
+      fn: async () => {
+        const ids = await ToolRegistry.ids()
+        expect(ids).toContain("hello")
+      },
+    })
+  })
+
+  test("loads tools from .opencode/tools (plural)", async () => {
+    await using tmp = await tmpdir({
+      init: async (dir) => {
+        const opencodeDir = path.join(dir, ".opencode")
+        await fs.mkdir(opencodeDir, { recursive: true })
+
+        const toolsDir = path.join(opencodeDir, "tools")
+        await fs.mkdir(toolsDir, { recursive: true })
+
+        await Bun.write(
+          path.join(toolsDir, "hello.ts"),
+          [
+            "export default {",
+            "  description: 'hello tool',",
+            "  args: {},",
+            "  execute: async () => {",
+            "    return 'hello world'",
+            "  },",
+            "}",
+            "",
+          ].join("\n"),
+        )
+      },
+    })
+
+    await Instance.provide({
+      directory: tmp.path,
+      fn: async () => {
+        const ids = await ToolRegistry.ids()
+        expect(ids).toContain("hello")
+      },
+    })
   })
 
-  await Instance.provide({
-    directory: tmp.path,
-    fn: async () => {
-      const ids = await ToolRegistry.ids()
-      expect(ids).toContain("cowsay")
-    },
+  test("loads tools with external dependencies without crashing", async () => {
+    await using tmp = await tmpdir({
+      init: async (dir) => {
+        const opencodeDir = path.join(dir, ".opencode")
+        await fs.mkdir(opencodeDir, { recursive: true })
+
+        const toolsDir = path.join(opencodeDir, "tools")
+        await fs.mkdir(toolsDir, { recursive: true })
+
+        await Bun.write(
+          path.join(opencodeDir, "package.json"),
+          JSON.stringify({
+            name: "custom-tools",
+            dependencies: {
+              "@opencode-ai/plugin": "^0.0.0",
+              cowsay: "^1.6.0",
+            },
+          }),
+        )
+
+        await Bun.write(
+          path.join(toolsDir, "cowsay.ts"),
+          [
+            "import { say } from 'cowsay'",
+            "export default {",
+            "  description: 'tool that imports cowsay at top level',",
+            "  args: { text: { type: 'string' } },",
+            "  execute: async ({ text }: { text: string }) => {",
+            "    return say({ text })",
+            "  },",
+            "}",
+            "",
+          ].join("\n"),
+        )
+      },
+    })
+
+    await Instance.provide({
+      directory: tmp.path,
+      fn: async () => {
+        const ids = await ToolRegistry.ids()
+        expect(ids).toContain("cowsay")
+      },
+    })
   })
 })