// RPC implementation + calling — template // // Docs: https://opencode.ai/v2/docs/build/plugins/rpc/ // // Two files. The contract is importable without loading the implementation, so // other plugins and clients can depend on it. // // Needs templates/rpc.ts copied next to this file as src/rpc.ts, so the // doc's "./rpc.js" specifier resolves. An Effect plugin would import the // Effect-dialect contract instead — see rpc-effect.ts. // // The docs use "@opencode/plugin" and "@opencode/plugin/effect". Install the // package at your OpenCode version and restart the service first — see the // PREREQUISITE block in promise-plugin.ts. import { Plugin } from "@opencode/plugin" import { Acme } from "./rpc.js" export default Plugin.define({ id: "acme-plugin", async setup(ctx) { const registration = await ctx.rpc.register(Acme, { search: async (input, context) => { // JSON Schema input is `unknown` — narrow it. const { query } = input as { query: string } // `signal` cancels with the request; `context.error` declares a failure. const text = await findText(query, { signal: context.signal }) if (!text) return context.error("not_found", "Result not found", { query }) return { text } }, }) // The same plugin can call its own RPC once registered. const acme = ctx.rpc(Acme) // Raw JSON Schema output arrives as `unknown` in the Promise flavor; the // docs say to narrow it. const result = (await acme.search({ query: "hello" })) as { text: string } // Emit to subscribers. await registration.events.emit("updated", { itemID: "item-1", text: result.text }) // ---------------------------------------------------------------- events // Subscribe with the LOCAL name. The received `type` is prefixed // ("rpc.acme.updated"). Every event carries `data` and `location`. // // Subscriptions are LIVE ONLY: a disconnected subscriber misses events. // Plugin unload closes them. // // const unsubscribe = acme.events.on("updated", (event) => { // console.log(event.type, event.location.directory, event.data.text) // }) // unsubscribe() // // for await (const event of acme.events.subscribe("updated")) { // console.log(event.data.text) // } return () => { void registration.dispose() } }, }) // Calling from a client over HTTP: // import { OpenCode } from "@opencode/client" // import { Acme } from "opencode-acme-plugin/rpc" // const client = OpenCode.make({ baseUrl: "http://localhost:4096" }) // const acme = client.rpc(Acme) // const result = await acme.search({ query: "hello" }) // Calling from another plugin (a TUI plugin uses `context.client`): // import { Plugin } from "@opencode/plugin/tui" // const acme = context.client.rpc(Acme) // const result = await acme.search({ query: "hello" }) declare function findText(query: string, options: { signal: AbortSignal }): Promise