application-tools.ts 1.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758
  1. export * as ApplicationTools from "./application-tools"
  2. import { Context, Effect, Layer, Scope } from "effect"
  3. import { enableMapSet } from "immer"
  4. import { State } from "../state"
  5. import { Tool } from "./tool"
  6. type Data = {
  7. readonly entries: Map<string, Entry>
  8. }
  9. type Editor = {
  10. readonly set: (name: string, entry: Entry) => void
  11. }
  12. export interface Entry {
  13. readonly identity: object
  14. readonly tool: Tool.AnyTool
  15. }
  16. export interface Interface {
  17. readonly register: (
  18. tools: Readonly<Record<string, Tool.AnyTool>>,
  19. ) => Effect.Effect<void, Tool.RegistrationError, Scope.Scope>
  20. readonly entries: () => ReadonlyMap<string, Entry>
  21. }
  22. export class Service extends Context.Service<Service, Interface>()("@opencode/ApplicationTools") {}
  23. enableMapSet()
  24. export const layer = Layer.effect(
  25. Service,
  26. Effect.gen(function* () {
  27. const state = State.create<Data, Editor>({
  28. initial: () => ({ entries: new Map() }),
  29. editor: (draft) => ({
  30. set: (name, tool) => {
  31. draft.entries.set(name, tool)
  32. },
  33. }),
  34. })
  35. return Service.of({
  36. register: Effect.fn("ApplicationTools.register")(function* (tools) {
  37. const entries = Object.entries(tools)
  38. if (entries.length === 0) return
  39. yield* Effect.forEach(entries, ([name]) => Tool.validateName(name), { discard: true })
  40. const registrations = entries.map(([name, tool]) => [name, { identity: {}, tool }] as const)
  41. const transform = yield* state.transform()
  42. yield* transform((editor) => {
  43. for (const [name, entry] of registrations) editor.set(name, entry)
  44. })
  45. }),
  46. entries: () => state.get().entries,
  47. })
  48. }),
  49. )