diff --git a/packages/plugin/src/example.ts b/packages/plugin/src/example.ts index a13c0fba..04c48c91 100644 --- a/packages/plugin/src/example.ts +++ b/packages/plugin/src/example.ts @@ -1,9 +1,15 @@ import { Plugin } from "./index" -export const ExamplePlugin: Plugin = async ({ client, $ }) => { +export const ExamplePlugin: Plugin = async ({ + client: _client, + $: _shell, + project: _project, + directory: _directory, + worktree: _worktree, +}) => { return { permission: {}, - async "chat.params"(input, output) { + async "chat.params"(_input, output) { output.topP = 1 }, } diff --git a/packages/sdk/js/example/example.ts b/packages/sdk/js/example/example.ts new file mode 100644 index 00000000..481fc424 --- /dev/null +++ b/packages/sdk/js/example/example.ts @@ -0,0 +1,55 @@ +import { createOpencodeClient, createOpencodeServer } from "@opencode-ai/sdk" + +const server = await createOpencodeServer() +const client = createOpencodeClient({ baseUrl: server.url }) + +const input = await Array.fromAsync(new Bun.Glob("packages/core/*.ts").scan()) + +const tasks: Promise[] = [] +for await (const file of input) { + console.log("processing", file) + const session = await client.session.create() + tasks.push( + client.session.prompt({ + path: { id: session.data.id }, + body: { + parts: [ + { + type: "file", + mime: "text/plain", + url: `file://${file}`, + }, + { + type: "text", + text: `Write tests for every public function in this file.`, + }, + ], + }, + }), + ) + console.log("done", file) +} + +await Promise.all( + input.map(async (file) => { + const session = await client.session.create() + console.log("processing", file) + await client.session.prompt({ + path: { id: session.data.id }, + body: { + parts: [ + { + type: "file", + mime: "text/plain", + url: `file://${file}`, + }, + { + type: "text", + text: `Write tests for every public function in this file.`, + }, + ], + }, + }) + console.log("done", file) + }), +) diff --git a/packages/web/src/content/docs/docs/plugins.mdx b/packages/web/src/content/docs/docs/plugins.mdx index 22c78b0b..071f1d42 100644 --- a/packages/web/src/content/docs/docs/plugins.mdx +++ b/packages/web/src/content/docs/docs/plugins.mdx @@ -26,7 +26,7 @@ Plugins are loaded from: ### Basic structure ```js title=".opencode/plugin/example.js" -export const MyPlugin = async ({ app, client, $ }) => { +export const MyPlugin = async ({ project, client, $, directory, worktree }) => { console.log("Plugin initialized!") return { @@ -37,7 +37,9 @@ export const MyPlugin = async ({ app, client, $ }) => { The plugin function receives: -- `app`: The opencode application instance. +- `project`: The current project information. +- `directory`: The current working directory. +- `worktree`: The git worktree path. - `client`: An opencode SDK client for interacting with the AI. - `$`: Bun's [shell API](https://bun.com/docs/runtime/shell) for executing commands. @@ -50,7 +52,7 @@ For TypeScript plugins, you can import types from the plugin package: ```ts title="my-plugin.ts" {1} import type { Plugin } from "@opencode-ai/plugin" -export const MyPlugin: Plugin = async ({ app, client, $ }) => { +export const MyPlugin: Plugin = async ({ project, client, $, directory, worktree }) => { return { // Type-safe hook implementations } @@ -70,7 +72,7 @@ Here are some examples of plugins you can use to extend opencode. Send notifications when certain events occur: ```js title=".opencode/plugin/notification.js" -export const NotificationPlugin = async ({ client, $ }) => { +export const NotificationPlugin = async ({ project, client, $, directory, worktree }) => { return { event: async ({ event }) => { // Send notification on session completion @@ -91,13 +93,13 @@ We are using `osascript` to run AppleScript on macOS. Here we are using it to se Prevent opencode from reading `.env` files: ```javascript title=".opencode/plugin/env-protection.js" -export const EnvProtection = async ({ client, $ }) => { +export const EnvProtection = async ({ project, client, $, directory, worktree }) => { return { "tool.execute.before": async (input, output) => { if (input.tool === "read" && output.args.filePath.includes(".env")) { throw new Error("Do not read .env files") } - } + }, } } ``` diff --git a/packages/web/src/content/docs/docs/sdk.mdx b/packages/web/src/content/docs/docs/sdk.mdx index 3b4353d8..1e537362 100644 --- a/packages/web/src/content/docs/docs/sdk.mdx +++ b/packages/web/src/content/docs/docs/sdk.mdx @@ -10,6 +10,8 @@ The opencode [JS/TS SDK](https://www.npmjs.com/package/@opencode-ai/sdk) provide [Learn more](/docs/server) about how the opencode server works. +> **Note**: Many API endpoints now require a `directory` query parameter to specify the working directory context. + --- ## Install @@ -65,12 +67,12 @@ server.close() #### Options -| Option | Type | Description | Default | -| ---------- | -------------- | ------------------------------ | ------------- | -| `hostname` | `string` | Server hostname | `127.0.0.1` | -| `port` | `number` | Server port | `4096` | -| `signal` | `AbortSignal` | Abort signal for cancellation | `undefined` | -| `timeout` | `number` | Timeout in ms for server start | `5000` | +| Option | Type | Description | Default | +| ---------- | ------------- | ------------------------------ | ----------- | +| `hostname` | `string` | Server hostname | `127.0.0.1` | +| `port` | `number` | Server port | `4096` | +| `signal` | `AbortSignal` | Abort signal for cancellation | `undefined` | +| `timeout` | `number` | Timeout in ms for server start | `5000` | --- @@ -80,7 +82,7 @@ The SDK includes TypeScript definitions for all API types. Import them directly: ```typescript import type { Session, Message, Part } from "@opencode-ai/sdk" -```` +``` All types are generated from the server's OpenAPI specification and available in the types file. @@ -108,18 +110,63 @@ The SDK exposes all server APIs through a type-safe client interface. ### App -| Method | Description | Response | -| ------------ | ------------------ | --------------------------------------- | -| `app.get()` | Get app info | App | -| `app.init()` | Initialize the app | `boolean` | +| Method | Description | Response | +| -------------- | ------------------------- | ------------------------------------------- | +| `app.log()` | Write a log entry | `boolean` | +| `app.agents()` | List all available agents | Agent[] | --- #### Examples ```javascript -const app = await client.app.get() -await client.app.init() +// Log an entry +await client.app.log({ + service: "my-app", + level: "info", + message: "Operation completed", +}) + +// List available agents +const agents = await client.app.agents() +``` + +--- + +### Project + +| Method | Description | Response | +| ------------------- | ------------------- | --------------------------------------------- | +| `project.list()` | List all projects | Project[] | +| `project.current()` | Get current project | Project | + +--- + +#### Examples + +```javascript +// List all projects +const projects = await client.project.list() + +// Get current project +const currentProject = await client.project.current() +``` + +--- + +### Path + +| Method | Description | Response | +| ------------ | ---------------- | ---------------------------------------- | +| `path.get()` | Get current path | Path | + +--- + +#### Examples + +```javascript +// Get current path information +const pathInfo = await client.path.get() ``` --- @@ -159,7 +206,7 @@ const { providers, default: defaults } = await client.config.providers() | `session.summarize({ id, providerID, modelID })` | Summarize session | Returns `boolean` | | `session.messages({ id })` | List messages in a session | Returns `{ info: `Message`, parts: `Part[]`}[]` | | `session.message({ id, messageID })` | Get message details | Returns `{ info: `Message`, parts: `Part[]`}` | -| `session.chat({ id, ...chatInput })` | Send chat message | Returns Message | +| `session.prompt({ id, ...promptInput })` | Send prompt message | Returns Message | | `session.shell({ id, agent, command })` | Run a shell command | Returns Message | | `session.revert({ id, messageID, partID? })` | Revert a message | Returns Session | | `session.unrevert({ id })` | Restore reverted messages | Returns Session | @@ -175,10 +222,12 @@ const session = await client.session.create({ title: "My session" }) const sessions = await client.session.list() // Send messages -const message = await client.session.chat({ +const message = await client.session.prompt({ id: session.id, - providerID: "anthropic", - modelID: "claude-3-5-sonnet-20241022", + model: { + providerID: "anthropic", + modelID: "claude-3-5-sonnet-20241022", + }, parts: [{ type: "text", text: "Hello!" }], }) ```