(
session: OpencodeSession,
opts: OpencodeLocalLauncherOptions
)
| 254 | } |
| 255 | |
| 256 | export async function opencodeLocalLauncher( |
| 257 | session: OpencodeSession, |
| 258 | opts: OpencodeLocalLauncherOptions |
| 259 | ): Promise<'switch' | 'exit'> { |
| 260 | const hookUrl = opts.hookUrl; |
| 261 | |
| 262 | const opencodeConfigDir = resolveOpencodeConfigDir(session); |
| 263 | ensureOpencodeHookPlugin(opencodeConfigDir, hookUrl, opts.hookServer.token); |
| 264 | |
| 265 | // Start the hapi MCP server for change_title support (optional feature) |
| 266 | let happyServer: { url: string; stop: () => void } | null = null; |
| 267 | let opencodeConfigPath: string | null = null; |
| 268 | try { |
| 269 | const bridge = await buildHapiMcpBridge(session.client); |
| 270 | happyServer = bridge.server; |
| 271 | logger.debug(`[opencode-local]: Started hapi MCP server at ${happyServer.url}`); |
| 272 | |
| 273 | // Generate opencode.json config with MCP server and instructions |
| 274 | const { configPath } = ensureOpencodeConfig(opencodeConfigDir, bridge.mcpServers.hapi, TITLE_INSTRUCTION); |
| 275 | opencodeConfigPath = configPath; |
| 276 | } catch (error) { |
| 277 | logger.debug('[opencode-local]: Failed to start hapi MCP server (change_title will be unavailable)', error); |
| 278 | } |
| 279 | |
| 280 | const launcher = new BaseLocalLauncher({ |
| 281 | label: 'opencode-local', |
| 282 | failureLabel: 'Local OpenCode process failed', |
| 283 | queue: session.queue, |
| 284 | rpcHandlerManager: session.client.rpcHandlerManager, |
| 285 | startedBy: session.startedBy, |
| 286 | startingMode: session.startingMode, |
| 287 | launch: async (abortSignal) => { |
| 288 | const env = buildOpencodeEnv(); |
| 289 | env.HAPI_OPENCODE_HOOK_URL = hookUrl; |
| 290 | env.HAPI_OPENCODE_HOOK_TOKEN = opts.hookServer.token; |
| 291 | if (!env.OPENCODE_CONFIG_DIR) { |
| 292 | env.OPENCODE_CONFIG_DIR = opencodeConfigDir; |
| 293 | } |
| 294 | if (!env.OPENCODE_CONFIG && opencodeConfigPath) { |
| 295 | env.OPENCODE_CONFIG = opencodeConfigPath; |
| 296 | } |
| 297 | |
| 298 | await opencodeLocal({ |
| 299 | path: session.path, |
| 300 | abort: abortSignal, |
| 301 | env, |
| 302 | sessionId: session.sessionId ?? undefined |
| 303 | }); |
| 304 | }, |
| 305 | sendFailureMessage: (message) => { |
| 306 | session.sendSessionEvent({ type: 'message', message }); |
| 307 | }, |
| 308 | recordLocalLaunchFailure: (message, exitReason) => { |
| 309 | session.recordLocalLaunchFailure(message, exitReason); |
| 310 | } |
| 311 | }); |
| 312 | |
| 313 | let storageScanner: OpencodeStorageScannerHandle | null = null; |
no test coverage detected