registry.ts 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. import { BashTool } from "./bash"
  2. import { EditTool } from "./edit"
  3. import { GlobTool } from "./glob"
  4. import { GrepTool } from "./grep"
  5. import { ListTool } from "./ls"
  6. import { PatchTool } from "./patch"
  7. import { ReadTool } from "./read"
  8. import { TaskTool } from "./task"
  9. import { TodoWriteTool, TodoReadTool } from "./todo"
  10. import { WebFetchTool } from "./webfetch"
  11. import { WriteTool } from "./write"
  12. import { InvalidTool } from "./invalid"
  13. import type { Agent } from "../agent/agent"
  14. import { Tool } from "./tool"
  15. import { Instance } from "../project/instance"
  16. import { Config } from "../config/config"
  17. import path from "path"
  18. import { type ToolDefinition } from "@opencode-ai/plugin"
  19. import z from "zod/v4"
  20. import { Plugin } from "../plugin"
  21. export namespace ToolRegistry {
  22. export const state = Instance.state(async () => {
  23. const custom = [] as Tool.Info[]
  24. const glob = new Bun.Glob("tool/*.{js,ts}")
  25. for (const dir of await Config.directories()) {
  26. for await (const match of glob.scan({ cwd: dir, absolute: true, followSymlinks: true, dot: true })) {
  27. const namespace = path.basename(match, path.extname(match))
  28. const mod = await import(match)
  29. for (const [id, def] of Object.entries<ToolDefinition>(mod)) {
  30. custom.push(fromPlugin(id === "default" ? namespace : `${namespace}_${id}`, def))
  31. }
  32. }
  33. }
  34. const plugins = await Plugin.list()
  35. for (const plugin of plugins) {
  36. for (const [id, def] of Object.entries(plugin.tool ?? {})) {
  37. custom.push(fromPlugin(id, def))
  38. }
  39. }
  40. return { custom }
  41. })
  42. function fromPlugin(id: string, def: ToolDefinition): Tool.Info {
  43. return {
  44. id,
  45. init: async () => ({
  46. parameters: z.object(def.args),
  47. description: def.description,
  48. execute: async (args, ctx) => {
  49. const result = await def.execute(args as any, ctx)
  50. return {
  51. title: "",
  52. output: result,
  53. metadata: {},
  54. }
  55. },
  56. }),
  57. }
  58. }
  59. export async function register(tool: Tool.Info) {
  60. const { custom } = await state()
  61. const idx = custom.findIndex((t) => t.id === tool.id)
  62. if (idx >= 0) {
  63. custom.splice(idx, 1, tool)
  64. return
  65. }
  66. custom.push(tool)
  67. }
  68. async function all(): Promise<Tool.Info[]> {
  69. const custom = await state().then((x) => x.custom)
  70. return [
  71. InvalidTool,
  72. BashTool,
  73. EditTool,
  74. WebFetchTool,
  75. GlobTool,
  76. GrepTool,
  77. ListTool,
  78. PatchTool,
  79. ReadTool,
  80. WriteTool,
  81. TodoWriteTool,
  82. TodoReadTool,
  83. TaskTool,
  84. ...custom,
  85. ]
  86. }
  87. export async function ids() {
  88. return all().then((x) => x.map((t) => t.id))
  89. }
  90. export async function tools(_providerID: string, _modelID: string) {
  91. const tools = await all()
  92. const result = await Promise.all(
  93. tools.map(async (t) => ({
  94. id: t.id,
  95. ...(await t.init()),
  96. })),
  97. )
  98. return result
  99. }
  100. export async function enabled(
  101. _providerID: string,
  102. _modelID: string,
  103. agent: Agent.Info,
  104. ): Promise<Record<string, boolean>> {
  105. const result: Record<string, boolean> = {}
  106. result["patch"] = false
  107. if (agent.permission.edit === "deny") {
  108. result["edit"] = false
  109. result["patch"] = false
  110. result["write"] = false
  111. }
  112. if (agent.permission.bash["*"] === "deny" && Object.keys(agent.permission.bash).length === 1) {
  113. result["bash"] = false
  114. }
  115. if (agent.permission.webfetch === "deny") {
  116. result["webfetch"] = false
  117. }
  118. return result
  119. }
  120. }