feat(plugin): add promise plugin cleanup
This commit is contained in:
parent
278c510549
commit
c867e66ab5
6 changed files with 74 additions and 15 deletions
|
|
@ -130,7 +130,9 @@ export function fromPromise(plugin: PromisePlugin) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
yield* Effect.promise(() => Promise.resolve(plugin.setup(context2)))
|
const cleanup = yield* Effect.promise(() => Promise.resolve(plugin.setup(context2)))
|
||||||
|
if (!cleanup) return
|
||||||
|
yield* Effect.addFinalizer(() => Effect.promise(() => Promise.resolve(cleanup())))
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,10 @@ Use this guide as the starting point for work involving OpenCode itself. It
|
||||||
covers the core concepts needed to configure and customize OpenCode, extend it
|
covers the core concepts needed to configure and customize OpenCode, extend it
|
||||||
with plugins, and build integrations with the OpenCode SDK, clients, and API.
|
with plugins, and build integrations with the OpenCode SDK, clients, and API.
|
||||||
|
|
||||||
Full documentation is available at <https://opencode.mintlify.site/>. Consult
|
Full documentation is available at <https://v2.opencode.ai/>. Consult
|
||||||
it when this overview does not contain enough detail for the task.
|
it when this overview does not contain enough detail for the task.
|
||||||
|
|
||||||
## Configuration
|
## [Configuration](https://v2.opencode.ai/config)
|
||||||
|
|
||||||
OpenCode configuration uses JSON or JSONC. Include the published schema so the
|
OpenCode configuration uses JSON or JSONC. Include the published schema so the
|
||||||
user's editor can validate fields and provide autocomplete:
|
user's editor can validate fields and provide autocomplete:
|
||||||
|
|
@ -34,14 +34,14 @@ Do not guess field names or shapes. Use
|
||||||
<https://opencode.ai/config.json> as the source of truth and preserve unrelated
|
<https://opencode.ai/config.json> as the source of truth and preserve unrelated
|
||||||
settings when editing an existing file.
|
settings when editing an existing file.
|
||||||
|
|
||||||
See the [full configuration guide](https://opencode.mintlify.site/config) for
|
See the [full configuration guide](https://v2.opencode.ai/config) for
|
||||||
every field, examples, config locations, and links to dedicated feature guides.
|
every field, examples, config locations, and links to dedicated feature guides.
|
||||||
|
|
||||||
## V1 to V2 migration
|
## [V1 to V2 migration](https://v2.opencode.ai/migrate-v1)
|
||||||
|
|
||||||
For any request to migrate OpenCode configuration, agents, commands, skills,
|
For any request to migrate OpenCode configuration, agents, commands, skills,
|
||||||
plugins, integrations, or other behavior from V1 to V2, read the full
|
plugins, integrations, or other behavior from V1 to V2, read the full
|
||||||
[migration guide](https://opencode.mintlify.site/migrate-v1) before acting. In
|
[migration guide](https://v2.opencode.ai/migrate-v1) before acting. In
|
||||||
the repository, its source is `packages/docs/migrate-v1.mdx`.
|
the repository, its source is `packages/docs/migrate-v1.mdx`.
|
||||||
|
|
||||||
V1 config files and `.opencode/` definitions are intended to remain compatible.
|
V1 config files and `.opencode/` definitions are intended to remain compatible.
|
||||||
|
|
@ -54,7 +54,7 @@ not attempt migration yet. Once the V2 plugin API is finalized, OpenCode should
|
||||||
be able to migrate most plugins. If non-API V1 functionality fails in V2, use
|
be able to migrate most plugins. If non-API V1 functionality fails in V2, use
|
||||||
the `report` skill to file it as a compatibility bug.
|
the `report` skill to file it as a compatibility bug.
|
||||||
|
|
||||||
## Service
|
## [Service](https://v2.opencode.ai/troubleshooting#check-the-background-service)
|
||||||
|
|
||||||
OpenCode uses a client-server architecture. Interfaces such as the TUI connect
|
OpenCode uses a client-server architecture. Interfaces such as the TUI connect
|
||||||
to a background OpenCode service, which owns sessions, configuration, plugins,
|
to a background OpenCode service, which owns sessions, configuration, plugins,
|
||||||
|
|
@ -73,7 +73,7 @@ Check its status after restarting:
|
||||||
opencode2 service status
|
opencode2 service status
|
||||||
```
|
```
|
||||||
|
|
||||||
## API
|
## [API](https://v2.opencode.ai/api)
|
||||||
|
|
||||||
OpenCode exposes an HTTP API from its server. The API is described by an
|
OpenCode exposes an HTTP API from its server. The API is described by an
|
||||||
OpenAPI document available from the running server at `/openapi.json`.
|
OpenAPI document available from the running server at `/openapi.json`.
|
||||||
|
|
@ -101,12 +101,12 @@ connected to an explicit server instead of its managed background service, use
|
||||||
the same configured server and authentication context rather than constructing
|
the same configured server and authentication context rather than constructing
|
||||||
an unauthenticated request separately.
|
an unauthenticated request separately.
|
||||||
|
|
||||||
See the [full API reference](https://opencode.mintlify.site/api) for available
|
See the [full API reference](https://v2.opencode.ai/api) for available
|
||||||
endpoints, parameters, request bodies, and response schemas. The
|
endpoints, parameters, request bodies, and response schemas. The
|
||||||
raw [OpenAPI specification](https://opencode.mintlify.site/openapi.json) is also
|
raw [OpenAPI specification](https://v2.opencode.ai/openapi.json) is also
|
||||||
available for code generation and other tooling.
|
available for code generation and other tooling.
|
||||||
|
|
||||||
## Troubleshooting
|
## [Troubleshooting](https://v2.opencode.ai/troubleshooting)
|
||||||
|
|
||||||
OpenCode runs a client and a background server. Start by determining whether a
|
OpenCode runs a client and a background server. Start by determining whether a
|
||||||
problem belongs to the client, the shared server, or one project.
|
problem belongs to the client, the shared server, or one project.
|
||||||
|
|
@ -124,6 +124,6 @@ problem belongs to the client, the shared server, or one project.
|
||||||
- Redact API keys, authorization headers, prompts, file contents, and other
|
- Redact API keys, authorization headers, prompts, file contents, and other
|
||||||
sensitive data before sharing diagnostics.
|
sensitive data before sharing diagnostics.
|
||||||
|
|
||||||
See the [full troubleshooting guide](https://opencode.mintlify.site/troubleshooting)
|
See the [full troubleshooting guide](https://v2.opencode.ai/troubleshooting)
|
||||||
for service lifecycle commands, API inspection, log locations, explicit server
|
for service lifecycle commands, API inspection, log locations, explicit server
|
||||||
connections, issue-reporting details, and local development paths.
|
connections, issue-reporting details, and local development paths.
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,33 @@ describe("fromPromise", () => {
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
it.effect("runs the setup cleanup when the plugin scope closes", () =>
|
||||||
|
Effect.gen(function* () {
|
||||||
|
const plugin = yield* PluginV2.Service
|
||||||
|
const host = yield* PluginHost.make(plugin)
|
||||||
|
const events: string[] = []
|
||||||
|
const promisePlugin = Plugin.define({
|
||||||
|
id: "promise-cleanup",
|
||||||
|
setup: async () => {
|
||||||
|
events.push("setup")
|
||||||
|
return async () => {
|
||||||
|
await Promise.resolve()
|
||||||
|
events.push("cleanup")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
yield* Effect.scoped(
|
||||||
|
Effect.gen(function* () {
|
||||||
|
yield* PluginPromise.fromPromise(promisePlugin).effect(host)
|
||||||
|
expect(events).toEqual(["setup"])
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(events).toEqual(["setup", "cleanup"])
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
it.effect("constructs plain Promise tool declarations in the host", () =>
|
it.effect("constructs plain Promise tool declarations in the host", () =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const plugins = yield* PluginV2.Service
|
const plugins = yield* PluginV2.Service
|
||||||
|
|
|
||||||
23
packages/docs/build/plugins.mdx
vendored
23
packages/docs/build/plugins.mdx
vendored
|
|
@ -148,7 +148,25 @@ export default Plugin.define({
|
||||||
```
|
```
|
||||||
|
|
||||||
`setup` runs each time the plugin is activated. Register long-lived behavior
|
`setup` runs each time the plugin is activated. Register long-lived behavior
|
||||||
during setup; do not wait there on an infinite event stream.
|
during setup; do not wait there on an infinite event stream. It may return a
|
||||||
|
synchronous or asynchronous cleanup function. OpenCode awaits that cleanup
|
||||||
|
when the plugin is disabled, reloaded, or shut down:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
setup: async (ctx) => {
|
||||||
|
const controller = new AbortController()
|
||||||
|
const task = synchronize(ctx, controller.signal)
|
||||||
|
|
||||||
|
return async () => {
|
||||||
|
controller.abort()
|
||||||
|
await task
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Hook registrations are released automatically with the same plugin scope. Use
|
||||||
|
the returned cleanup for resources the plugin owns, such as timers, watchers,
|
||||||
|
connections, and background tasks.
|
||||||
|
|
||||||
### Context
|
### Context
|
||||||
|
|
||||||
|
|
@ -216,7 +234,8 @@ export default Plugin.define({
|
||||||
}
|
}
|
||||||
|
|
||||||
await refresh()
|
await refresh()
|
||||||
setInterval(() => void refresh().catch(console.error), 60_000)
|
const timer = setInterval(() => void refresh().catch(console.error), 60_000)
|
||||||
|
return () => clearInterval(timer)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,15 @@ export default Plugin.define({
|
||||||
```
|
```
|
||||||
|
|
||||||
Plugin setup registers hooks imperatively through each domain's `hook` method.
|
Plugin setup registers hooks imperatively through each domain's `hook` method.
|
||||||
|
It may return a synchronous or asynchronous cleanup function. OpenCode awaits
|
||||||
|
the cleanup when the plugin is unloaded or replaced:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
setup: async (ctx) => {
|
||||||
|
const timer = setInterval(refresh, 60_000)
|
||||||
|
return () => clearInterval(timer)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Configuration supplied for the plugin is available as `ctx.options`.
|
Configuration supplied for the plugin is available as `ctx.options`.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,9 +26,11 @@ export interface Context {
|
||||||
readonly tool: ToolDomain
|
readonly tool: ToolDomain
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type Cleanup = () => Promise<void> | void
|
||||||
|
|
||||||
export interface Plugin {
|
export interface Plugin {
|
||||||
readonly id: string
|
readonly id: string
|
||||||
readonly setup: (context: Context) => Promise<void> | void
|
readonly setup: (context: Context) => Promise<Cleanup | void> | Cleanup | void
|
||||||
}
|
}
|
||||||
|
|
||||||
export function define(plugin: Plugin) {
|
export function define(plugin: Plugin) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue