Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions packages/opencode/src/plugin/codex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -385,8 +385,8 @@ export async function CodexAuthPlugin(input: PluginInput): Promise<Hooks> {
log.info("refreshing codex access token")
const tokens = await refreshAccessToken(currentAuth.refresh)
await input.client.auth.set({
path: { id: "codex" },
body: {
providerID: "codex",
auth: {
type: "oauth",
refresh: tokens.refresh_token,
access: tokens.access_token,
Expand All @@ -400,7 +400,9 @@ export async function CodexAuthPlugin(input: PluginInput): Promise<Hooks> {
const headers = new Headers()
if (init?.headers) {
if (init.headers instanceof Headers) {
init.headers.forEach((value, key) => headers.set(key, value))
init.headers.forEach((value, key) => {
headers.set(key, value)
})
} else if (Array.isArray(init.headers)) {
for (const [key, value] of init.headers) {
if (value !== undefined) headers.set(key, String(value))
Expand Down
27 changes: 16 additions & 11 deletions packages/opencode/src/plugin/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import type { Hooks, PluginInput, Plugin as PluginInstance } from "@opencode-ai/plugin"
import type { PluginInput, Hooks } from "@opencode-ai/plugin"
import type { Plugin as PluginFn } from "@opencode-ai/plugin"
import { Config } from "../config/config"
import { Bus } from "../bus"
import { Log } from "../util/log"
import { createOpencodeClient } from "@opencode-ai/sdk"
import { createOpencodeClient } from "@opencode-ai/sdk/v2"
import { Server } from "../server/server"
import { BunProc } from "../bun"
import { Instance } from "../project/instance"
Expand All @@ -15,16 +16,18 @@ export namespace Plugin {
const BUILTIN = ["[email protected]", "[email protected]"]

// Built-in plugins that are directly imported (not installed from npm)
const INTERNAL_PLUGINS: PluginInstance[] = [CodexAuthPlugin]
const INTERNAL_PLUGINS = [CodexAuthPlugin]

const state = Instance.state(async () => {
const client = createOpencodeClient({
baseUrl: "http://localhost:4096",
// @ts-ignore - fetch type incompatibility
fetch: async (...args) => Server.App().fetch(...args),
})

const config = await Config.get()
const hooks: Hooks[] = []

const input: PluginInput = {
client,
project: Instance.project,
Expand Down Expand Up @@ -54,23 +57,26 @@ export namespace Plugin {
if (!plugin.startsWith("file://")) {
const lastAtIndex = plugin.lastIndexOf("@")
const pkg = lastAtIndex > 0 ? plugin.substring(0, lastAtIndex) : plugin
const version = lastAtIndex > 0 ? plugin.substring(lastAtIndex + 1) : "latest"
const pkgVersion = lastAtIndex > 0 ? plugin.substring(lastAtIndex + 1) : "latest"
const builtin = BUILTIN.some((x) => x.startsWith(pkg + "@"))
plugin = await BunProc.install(pkg, version).catch((err) => {
plugin = await BunProc.install(pkg, pkgVersion).catch((err) => {
if (builtin) return ""
throw err
})
if (!plugin) continue
}
const mod = await import(plugin)

// Prevent duplicate initialization when plugins export the same function
// as both a named export and default export (e.g., `export const X` and `export default X`).
// Object.entries(mod) would return both entries pointing to the same function reference.
const seen = new Set<PluginInstance>()
for (const [_name, fn] of Object.entries<PluginInstance>(mod)) {
if (seen.has(fn)) continue
seen.add(fn)
const init = await fn(input)
const seen = new Set<PluginFn>()
for (const [_name, fn] of Object.entries(mod)) {
if (typeof fn !== "function") continue
const pluginFn = fn as PluginFn
if (seen.has(pluginFn)) continue
seen.add(pluginFn)
const init = await pluginFn(input)
hooks.push(init)
}
}
Expand Down Expand Up @@ -106,7 +112,6 @@ export namespace Plugin {
const hooks = await state().then((x) => x.hooks)
const config = await Config.get()
for (const hook of hooks) {
// @ts-expect-error this is because we haven't moved plugin to sdk v2
await hook.config?.(config)
}
Bus.subscribeAll(async (input) => {
Expand Down
10 changes: 5 additions & 5 deletions packages/plugin/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import type {
Event,
createOpencodeClient,
Project,
Model,
Provider,
Permission,
PermissionRequest,
UserMessage,
Message,
Part,
Auth,
Config,
} from "@opencode-ai/sdk"
OpencodeClient,
} from "@opencode-ai/sdk/v2"

import type { BunShell } from "./shell"
import { type ToolDefinition } from "./tool"
Expand All @@ -24,7 +24,7 @@ export type ProviderContext = {
}

export type PluginInput = {
client: ReturnType<typeof createOpencodeClient>
client: OpencodeClient
project: Project
directory: string
worktree: string
Expand Down Expand Up @@ -170,7 +170,7 @@ export interface Hooks {
input: { sessionID: string; agent: string; model: Model; provider: ProviderContext; message: UserMessage },
output: { temperature: number; topP: number; topK: number; options: Record<string, any> },
) => Promise<void>
"permission.ask"?: (input: Permission, output: { status: "ask" | "deny" | "allow" }) => Promise<void>
"permission.ask"?: (input: PermissionRequest, output: { status: "ask" | "deny" | "allow" | "reject" }) => Promise<void>
"tool.execute.before"?: (
input: { tool: string; sessionID: string; callID: string },
output: { args: any },
Expand Down