Files
oh-my-opencode/src/index.ts
T
Hakancan a2687a8d02 fix(sdk): use dynamic port instead of hardcoded 4096 (fixes #2311)
- TmuxSessionManager resolves server URL with priority:
  ctx.serverUrl.origin > getServerBaseUrl(client) > OPENCODE_PORT env > localhost:4096
- Inject resolved baseUrl into SDK client (root + session subclient) at
  startup so all session tools use the dynamic port
- Extract shared getInternalClient utility to sdk-internal-client.ts;
  prefer candidate with getConfig/setConfig for correct v1/v2 detection
- Support v1 (client._client) and v2 (client.client) SDK structures
  across opencode-server-auth.ts and opencode-http-api.ts
- Use createMockContext factory for serverUrl overrides in tests
2026-03-12 13:37:54 +09:00

133 lines
4.1 KiB
TypeScript

import { initConfigContext } from "./cli/config-manager/config-context"
import type { Plugin } from "@opencode-ai/plugin"
import type { HookName } from "./config"
import { createHooks } from "./create-hooks"
import { createManagers } from "./create-managers"
import { createTools } from "./create-tools"
import { createPluginInterface } from "./plugin-interface"
import { createPluginDispose, type PluginDispose } from "./plugin-dispose"
import { loadPluginConfig } from "./plugin-config"
import { createModelCacheState } from "./plugin-state"
import { createFirstMessageVariantGate } from "./shared/first-message-variant"
import { injectServerAuthIntoClient, injectServerBaseUrlIntoClient, log } from "./shared"
import { startTmuxCheck } from "./tools"
let activePluginDispose: PluginDispose | null = null
const OhMyOpenCodePlugin: Plugin = async (ctx) => {
// Initialize config context for plugin runtime (prevents warnings from hooks)
initConfigContext("opencode", null)
log("[OhMyOpenCodePlugin] ENTRY - plugin loading", {
directory: ctx.directory,
})
// Inject correct server baseUrl to ensure SDK client uses dynamic port
// Priority: ctx.serverUrl > fallback to existing client baseUrl
if (ctx.serverUrl) {
injectServerBaseUrlIntoClient(ctx.client, ctx.serverUrl.toString())
}
injectServerAuthIntoClient(ctx.client)
startTmuxCheck()
await activePluginDispose?.()
const pluginConfig = loadPluginConfig(ctx.directory, ctx)
const disabledHooks = new Set(pluginConfig.disabled_hooks ?? [])
const isHookEnabled = (hookName: HookName): boolean => !disabledHooks.has(hookName)
const safeHookEnabled = pluginConfig.experimental?.safe_hook_creation ?? true
const firstMessageVariantGate = createFirstMessageVariantGate()
const tmuxConfig = {
enabled: pluginConfig.tmux?.enabled ?? false,
layout: pluginConfig.tmux?.layout ?? "main-vertical",
main_pane_size: pluginConfig.tmux?.main_pane_size ?? 60,
main_pane_min_width: pluginConfig.tmux?.main_pane_min_width ?? 120,
agent_pane_min_width: pluginConfig.tmux?.agent_pane_min_width ?? 40,
}
const modelCacheState = createModelCacheState()
const managers = createManagers({
ctx,
pluginConfig,
tmuxConfig,
modelCacheState,
backgroundNotificationHookEnabled: isHookEnabled("background-notification"),
})
const toolsResult = await createTools({
ctx,
pluginConfig,
managers,
})
const hooks = createHooks({
ctx,
pluginConfig,
modelCacheState,
backgroundManager: managers.backgroundManager,
isHookEnabled,
safeHookEnabled,
mergedSkills: toolsResult.mergedSkills,
availableSkills: toolsResult.availableSkills,
})
const dispose = createPluginDispose({
backgroundManager: managers.backgroundManager,
skillMcpManager: managers.skillMcpManager,
disposeHooks: hooks.disposeHooks,
})
const pluginInterface = createPluginInterface({
ctx,
pluginConfig,
firstMessageVariantGate,
managers,
hooks,
tools: toolsResult.filteredTools,
})
activePluginDispose = dispose
return {
...pluginInterface,
"experimental.session.compacting": async (
_input: { sessionID: string },
output: { context: string[] },
): Promise<void> => {
await hooks.compactionContextInjector?.capture(_input.sessionID)
await hooks.compactionTodoPreserver?.capture(_input.sessionID)
await hooks.claudeCodeHooks?.["experimental.session.compacting"]?.(
_input,
output,
)
if (hooks.compactionContextInjector) {
output.context.push(hooks.compactionContextInjector.inject(_input.sessionID))
}
},
}
}
export default OhMyOpenCodePlugin
export type {
OhMyOpenCodeConfig,
AgentName,
AgentOverrideConfig,
AgentOverrides,
McpName,
HookName,
BuiltinCommandName,
} from "./config"
// NOTE: Do NOT export functions from main index.ts!
// OpenCode treats ALL exports as plugin instances and calls them.
// Config error utilities are available via "./shared/config-errors" for internal use only.
export type { ConfigLoadError } from "./shared/config-errors"