export const registerOpenCodeRoutes = (app, dependencies) => { const { crypto, clientReloadDelayMs, getOpenCodeResolutionSnapshot, formatSettingsResponse, readSettingsFromDisk, readSettingsFromDiskMigrated, persistSettings, sanitizeProjects, validateDirectoryPath, resolveProjectDirectory, getProviderSources, removeProviderConfig, refreshOpenCodeAfterConfigChange, } = dependencies; let authLibrary = null; const getAuthLibrary = async () => { if (!authLibrary) { authLibrary = await import('./auth.js'); } return authLibrary; }; app.get('/api/config/settings', async (_req, res) => { try { const settings = await readSettingsFromDiskMigrated(); res.json(formatSettingsResponse(settings)); } catch (error) { console.error('Failed to read settings:', error); res.status(500).json({ error: 'Failed to read settings' }); } }); app.get('/api/config/opencode-resolution', async (_req, res) => { try { const settings = await readSettingsFromDiskMigrated(); const resolution = await getOpenCodeResolutionSnapshot(settings); res.json(resolution); } catch (error) { console.error('Failed to resolve OpenCode binary:', error); res.status(500).json({ error: 'Failed to resolve OpenCode binary' }); } }); app.put('/api/config/settings', async (req, res) => { console.log('[API:PUT /api/config/settings] Received request'); try { const updated = await persistSettings(req.body ?? {}); console.log(`[API:PUT /api/config/settings] Success, returning ${updated.projects?.length || 0} projects`); res.json(updated); } catch (error) { console.error('[API:PUT /api/config/settings] Failed to save settings:', error); console.error('[API:PUT /api/config/settings] Error stack:', error.stack); res.status(500).json({ error: 'Failed to save settings' }); } }); app.get('/api/provider/:providerId/source', async (req, res) => { try { const { providerId } = req.params; if (!providerId) { return res.status(400).json({ error: 'Provider ID is required' }); } const headerDirectory = typeof req.get === 'function' ? req.get('x-opencode-directory') : null; const queryDirectory = Array.isArray(req.query?.directory) ? req.query.directory[0] : req.query?.directory; const requestedDirectory = headerDirectory || queryDirectory || null; let directory = null; const resolved = await resolveProjectDirectory(req); if (resolved.directory) { directory = resolved.directory; } else if (requestedDirectory) { return res.status(400).json({ error: resolved.error }); } const sources = getProviderSources(providerId, directory); const { getProviderAuth } = await getAuthLibrary(); const auth = getProviderAuth(providerId); sources.sources.auth.exists = Boolean(auth); return res.json({ providerId, sources: sources.sources, }); } catch (error) { console.error('Failed to get provider sources:', error); return res.status(500).json({ error: error.message || 'Failed to get provider sources' }); } }); app.delete('/api/provider/:providerId/auth', async (req, res) => { try { const { providerId } = req.params; if (!providerId) { return res.status(400).json({ error: 'Provider ID is required' }); } const scope = typeof req.query?.scope === 'string' ? req.query.scope : 'auth'; const headerDirectory = typeof req.get === 'function' ? req.get('x-opencode-directory') : null; const queryDirectory = Array.isArray(req.query?.directory) ? req.query.directory[0] : req.query?.directory; const requestedDirectory = headerDirectory || queryDirectory || null; let directory = null; if (scope === 'project' || requestedDirectory) { const resolved = await resolveProjectDirectory(req); if (!resolved.directory) { return res.status(400).json({ error: resolved.error }); } directory = resolved.directory; } else { const resolved = await resolveProjectDirectory(req); if (resolved.directory) { directory = resolved.directory; } } let removed = false; if (scope === 'auth') { const { removeProviderAuth } = await getAuthLibrary(); removed = removeProviderAuth(providerId); } else if (scope === 'user' || scope === 'project' || scope === 'custom') { removed = removeProviderConfig(providerId, directory, scope); } else if (scope === 'all') { const { removeProviderAuth } = await getAuthLibrary(); const authRemoved = removeProviderAuth(providerId); const userRemoved = removeProviderConfig(providerId, directory, 'user'); const projectRemoved = directory ? removeProviderConfig(providerId, directory, 'project') : false; const customRemoved = removeProviderConfig(providerId, directory, 'custom'); removed = authRemoved || userRemoved || projectRemoved || customRemoved; } else { return res.status(400).json({ error: 'Invalid scope' }); } if (removed) { await refreshOpenCodeAfterConfigChange(`provider ${providerId} disconnected (${scope})`); } return res.json({ success: true, removed, requiresReload: removed, message: removed ? 'Provider disconnected successfully' : 'Provider was not connected', reloadDelayMs: removed ? clientReloadDelayMs : undefined, }); } catch (error) { console.error('Failed to disconnect provider:', error); return res.status(500).json({ error: error.message || 'Failed to disconnect provider' }); } }); app.post('/api/opencode/directory', async (req, res) => { try { const requestedPath = typeof req.body?.path === 'string' ? req.body.path.trim() : ''; if (!requestedPath) { return res.status(400).json({ error: 'Path is required' }); } const validated = await validateDirectoryPath(requestedPath); if (!validated.ok) { return res.status(400).json({ error: validated.error }); } const resolvedPath = validated.directory; const currentSettings = await readSettingsFromDisk(); const existingProjects = sanitizeProjects(currentSettings.projects) || []; const existing = existingProjects.find((project) => project.path === resolvedPath) || null; const nextProjects = existing ? existingProjects : [ ...existingProjects, { id: crypto.randomUUID(), path: resolvedPath, addedAt: Date.now(), lastOpenedAt: Date.now(), }, ]; const activeProjectId = existing ? existing.id : nextProjects[nextProjects.length - 1].id; const updated = await persistSettings({ projects: nextProjects, activeProjectId, lastDirectory: resolvedPath, }); return res.json({ success: true, restarted: false, path: resolvedPath, settings: updated, }); } catch (error) { console.error('Failed to update OpenCode working directory:', error); return res.status(500).json({ error: error.message || 'Failed to update working directory' }); } }); };