Skip to content

Commit 352390f

Browse files
refactor: consolidate server restart logic, extract hooks, remove dead code
1 parent 4b710df commit 352390f

15 files changed

Lines changed: 329 additions & 373 deletions

backend/src/index.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ import { installAssistantWorkspace } from './services/assistant-mode'
5151
import { getOpenCodeImportStatus, syncOpenCodeImport } from './services/opencode-import'
5252
import { OpenCodeSupervisor } from './services/opencode-supervisor'
5353
import { OpenCodeRestartCoordinator } from './services/opencode-restart-coordinator'
54+
import { setOpenCodeRestartCoordinator } from './services/opencode-restart'
5455
import { OpenCodeConfigSchema } from '@opencode-manager/shared/schemas'
5556
import { parse as parseJsonc } from 'jsonc-parser'
5657
import { getModelStatePath, ModelStateSchema } from './routes/providers'
@@ -321,11 +322,12 @@ sseAggregator.setPendingActionsFetcher(openCodeClient)
321322
sseAggregator.setPasswordResolver(() => new SettingsService(db).getOpenCodeServerPassword())
322323
sseAggregator.start()
323324

324-
sseAggregator.setScheduledSessionChecker(
325-
(sessionId) => scheduleService.getActiveRunSessionIds().has(sessionId),
325+
sseAggregator.setScheduledSessionsResolver(
326+
() => scheduleService.getActiveRunSessionIds(),
326327
)
327328

328329
const openCodeRestartCoordinator = new OpenCodeRestartCoordinator(openCodeClient, sseAggregator)
330+
setOpenCodeRestartCoordinator(openCodeRestartCoordinator)
329331

330332
void scheduleRunnerInstance.start()
331333

@@ -343,7 +345,7 @@ const protectedApi = new Hono()
343345
protectedApi.use('/*', requireAuth)
344346

345347
protectedApi.route('/repos', createRepoRoutes(db, gitAuthService, scheduleService, openCodeClient, openCodeSupervisor))
346-
protectedApi.route('/settings', createSettingsRoutes(db, gitAuthService, openCodeClient, openCodeSupervisor, openCodeRestartCoordinator))
348+
protectedApi.route('/settings', createSettingsRoutes(db, gitAuthService, openCodeClient, openCodeSupervisor))
347349
protectedApi.route('/files', createFileRoutes())
348350
protectedApi.route('/providers', createProvidersRoutes(db, openCodeClient, openCodeSupervisor))
349351
protectedApi.route('/oauth', createOAuthRoutes(openCodeClient, openCodeSupervisor))

backend/src/routes/oauth.ts

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,9 @@ import {
77
OAuthAuthorizeResponseSchema,
88
OAuthCallbackRequestSchema
99
} from '../../../shared/src/schemas/auth'
10-
import { opencodeServerManager } from '../services/opencode-single-server'
10+
import { reloadOpenCodeConfig } from '../services/opencode-restart'
1111
import type { OpenCodeSupervisor } from '../services/opencode-supervisor'
1212

13-
async function reloadOpenCodeConfig(openCodeSupervisor?: OpenCodeSupervisor): Promise<void> {
14-
if (openCodeSupervisor) {
15-
await openCodeSupervisor.reloadConfig('settings_reload')
16-
return
17-
}
18-
19-
await opencodeServerManager.reloadConfig()
20-
}
21-
2213
export function createOAuthRoutes(openCodeClient: OpenCodeClient, openCodeSupervisor?: OpenCodeSupervisor) {
2314
const app = new Hono()
2415

backend/src/routes/providers.ts

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { AuthService } from '../services/auth'
55
import { SetCredentialRequestSchema } from '../../../shared/src/schemas/auth'
66
import { logger } from '../utils/logger'
77
import type { OpenCodeClient } from '../services/opencode/client'
8-
import { opencodeServerManager } from '../services/opencode-single-server'
8+
import { reloadOpenCodeConfig } from '../services/opencode-restart'
99
import type { OpenCodeSupervisor } from '../services/opencode-supervisor'
1010
import type { Database } from 'bun:sqlite'
1111
import { getWorkspacePath } from '@opencode-manager/shared/config/env'
@@ -54,15 +54,6 @@ async function mirrorModelStateToFile(state: OpenCodeModelStateRecord): Promise<
5454
}
5555
}
5656

57-
async function reloadOpenCodeConfig(openCodeSupervisor?: OpenCodeSupervisor): Promise<void> {
58-
if (openCodeSupervisor) {
59-
await openCodeSupervisor.reloadConfig('settings_reload')
60-
return
61-
}
62-
63-
await opencodeServerManager.reloadConfig()
64-
}
65-
6657
export function createProvidersRoutes(db: Database, openCodeClient: OpenCodeClient, openCodeSupervisor?: OpenCodeSupervisor) {
6758
const app = new Hono()
6859
const authService = new AuthService()

backend/src/routes/repos.ts

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import * as repoService from '../services/repo'
88
import * as archiveService from '../services/archive'
99
import { SettingsService } from '../services/settings'
1010
import { writeFileContent } from '../services/file-operations'
11-
import { opencodeServerManager } from '../services/opencode-single-server'
11+
import { restartOpenCode } from '../services/opencode-restart'
1212
import type { OpenCodeSupervisor } from '../services/opencode-supervisor'
1313
import type { OpenCodeClient } from '../services/opencode/client'
1414
import { logger } from '../utils/logger'
@@ -22,16 +22,6 @@ import { ScheduleService } from '../services/schedules'
2222
import { ensureAssistantMode, getAssistantModeStatus, buildAssistantRepo } from '../services/assistant-mode'
2323
import path from 'path'
2424

25-
async function restartOpenCode(openCodeSupervisor?: OpenCodeSupervisor): Promise<void> {
26-
if (openCodeSupervisor) {
27-
await openCodeSupervisor.restart('settings_restart')
28-
return
29-
}
30-
31-
opencodeServerManager.clearStartupError()
32-
await opencodeServerManager.restart()
33-
}
34-
3525
function resolveRepo(database: Database, id: number): Repo | null {
3626
return getRepoById(database, id) ?? (id === ASSISTANT_REPO_ID ? buildAssistantRepo() : null)
3727
}

backend/src/routes/settings.test.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import type { GitAuthService } from '../services/git-auth'
1212
import type { OpenCodeSupervisor } from '../services/opencode-supervisor'
1313
import { createStubOpenCodeClient } from '../../test/helpers/stub-opencode-client'
1414
import type { OpenCodeRestartCoordinator, ResumableSession } from '../services/opencode-restart-coordinator'
15+
import { setOpenCodeRestartCoordinator } from '../services/opencode-restart'
1516

1617
interface TestUserPreferenceRow {
1718
preferences: string
@@ -211,9 +212,9 @@ function createTestDb(): Database {
211212
return db
212213
}
213214

214-
function createTestApp(db: Database, openCodeSupervisor?: OpenCodeSupervisor, openCodeRestartCoordinator?: OpenCodeRestartCoordinator): Hono {
215+
function createTestApp(db: Database, openCodeSupervisor?: OpenCodeSupervisor): Hono {
215216
const app = new Hono()
216-
app.route('/settings', createSettingsRoutes(db, mockGitAuthService, createStubOpenCodeClient(), openCodeSupervisor, openCodeRestartCoordinator))
217+
app.route('/settings', createSettingsRoutes(db, mockGitAuthService, createStubOpenCodeClient(), openCodeSupervisor))
217218
return app
218219
}
219220

@@ -462,6 +463,7 @@ describe('settings routes — restart coordinator wiring', () => {
462463

463464
afterEach(() => {
464465
db.close()
466+
setOpenCodeRestartCoordinator(null)
465467
})
466468

467469
it('GET /opencode-active-sessions returns count and sessions from coordinator', async () => {
@@ -474,8 +476,9 @@ describe('settings routes — restart coordinator wiring', () => {
474476
resumeSessions: vi.fn(),
475477
runWithResume: vi.fn(),
476478
} as unknown as OpenCodeRestartCoordinator
479+
setOpenCodeRestartCoordinator(fakeCoordinator)
477480

478-
app = createTestApp(db, undefined, fakeCoordinator)
481+
app = createTestApp(db)
479482
const res = await app.request('/settings/opencode-active-sessions')
480483
expect(res.status).toBe(200)
481484
const body = (await res.json()) as { count: number; sessions: ResumableSession[] }
@@ -485,6 +488,7 @@ describe('settings routes — restart coordinator wiring', () => {
485488
})
486489

487490
it('GET /opencode-active-sessions returns empty when no coordinator', async () => {
491+
setOpenCodeRestartCoordinator(null)
488492
app = createTestApp(db)
489493
const res = await app.request('/settings/opencode-active-sessions')
490494
expect(res.status).toBe(200)
@@ -501,8 +505,9 @@ describe('settings routes — restart coordinator wiring', () => {
501505
resumeSessions: vi.fn(),
502506
runWithResume,
503507
} as unknown as OpenCodeRestartCoordinator
508+
setOpenCodeRestartCoordinator(fakeCoordinator)
504509

505-
app = createTestApp(db, undefined, fakeCoordinator)
510+
app = createTestApp(db)
506511
const res = await app.request('/settings/opencode-restart', { method: 'POST' })
507512
expect(res.status).toBe(200)
508513
const body = (await res.json()) as { success: boolean; resumedSessions: string[] }

backend/src/routes/settings.ts

Lines changed: 7 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import { opencodeServerManager, ConfigReloadError } from '../services/opencode-s
3131
import { getOrCreateInternalToken, rotateInternalToken } from '../services/internal-token'
3232
import { sseAggregator } from '../services/sse-aggregator'
3333
import type { OpenCodeSupervisor } from '../services/opencode-supervisor'
34-
import type { OpenCodeRestartCoordinator } from '../services/opencode-restart-coordinator'
34+
import { restartOpenCode, reloadOpenCodeConfig, getOpenCodeRestartCoordinator } from '../services/opencode-restart'
3535
import type { GitAuthService } from '../services/git-auth'
3636
import { DEFAULT_AGENTS_MD } from '../constants'
3737
import { validateSSHPrivateKey } from '../utils/ssh-validation'
@@ -93,59 +93,6 @@ function getOpenCodeConfigContentToWrite(
9393
return JSON.stringify(appliedConfig, null, 2)
9494
}
9595

96-
async function reloadOpenCodeConfig(openCodeSupervisor?: OpenCodeSupervisor): Promise<void> {
97-
if (openCodeSupervisor) {
98-
await openCodeSupervisor.reloadConfig('settings_reload')
99-
return
100-
}
101-
102-
await opencodeServerManager.reloadConfig()
103-
}
104-
105-
async function restartOpenCode(openCodeSupervisor?: OpenCodeSupervisor): Promise<void> {
106-
if (openCodeSupervisor) {
107-
await openCodeSupervisor.restart('settings_restart')
108-
return
109-
}
110-
111-
opencodeServerManager.clearStartupError()
112-
await opencodeServerManager.restart()
113-
}
114-
115-
async function restartOpenCodeWithResume(
116-
supervisor: OpenCodeSupervisor | undefined,
117-
coordinator: OpenCodeRestartCoordinator | undefined,
118-
): Promise<{ resumedSessionIDs: string[] } | null> {
119-
if (!coordinator) {
120-
await restartOpenCode(supervisor)
121-
return null
122-
}
123-
const result = await coordinator.runWithResume(async () => {
124-
if (supervisor) return (await supervisor.restart('settings_restart')).healthy
125-
opencodeServerManager.clearStartupError()
126-
await opencodeServerManager.restart()
127-
return opencodeServerManager.checkHealth()
128-
})
129-
return { resumedSessionIDs: result.resumedSessionIDs }
130-
}
131-
132-
async function reloadOpenCodeConfigWithResume(
133-
supervisor: OpenCodeSupervisor | undefined,
134-
coordinator: OpenCodeRestartCoordinator | undefined,
135-
): Promise<{ resumedSessionIDs: string[] } | null> {
136-
if (!coordinator) {
137-
await reloadOpenCodeConfig(supervisor)
138-
return null
139-
}
140-
const result = await coordinator.runWithResume(async () => {
141-
if (supervisor) return (await supervisor.reloadConfig('settings_reload')).healthy
142-
opencodeServerManager.clearStartupError()
143-
await opencodeServerManager.reloadConfig()
144-
return opencodeServerManager.checkHealth()
145-
})
146-
return { resumedSessionIDs: result.resumedSessionIDs }
147-
}
148-
14996
async function restartOpenCodeSafe(openCodeSupervisor: OpenCodeSupervisor | undefined, context: string): Promise<void> {
15097
try {
15198
await restartOpenCode(openCodeSupervisor)
@@ -401,7 +348,7 @@ async function extractOpenCodeError(response: Response, defaultError: string): P
401348
: defaultError
402349
}
403350

404-
export function createSettingsRoutes(db: Database, gitAuthService: GitAuthService, openCodeClient: OpenCodeClient, openCodeSupervisor?: OpenCodeSupervisor, openCodeRestartCoordinator?: OpenCodeRestartCoordinator) {
351+
export function createSettingsRoutes(db: Database, gitAuthService: GitAuthService, openCodeClient: OpenCodeClient, openCodeSupervisor?: OpenCodeSupervisor) {
405352
const app = new Hono()
406353
const settingsService = new SettingsService(db)
407354

@@ -763,11 +710,11 @@ export function createSettingsRoutes(db: Database, gitAuthService: GitAuthServic
763710
try {
764711
logger.info('Manual OpenCode server restart requested')
765712
opencodeServerManager.clearStartupError()
766-
const resume = await restartOpenCodeWithResume(openCodeSupervisor, openCodeRestartCoordinator)
713+
const { resumedSessionIDs } = await restartOpenCode(openCodeSupervisor)
767714
return c.json({
768715
success: true,
769716
message: 'OpenCode server restarted successfully',
770-
resumedSessions: resume?.resumedSessionIDs ?? [],
717+
resumedSessions: resumedSessionIDs,
771718
})
772719
} catch (error) {
773720
logger.error('Failed to restart OpenCode server:', error)
@@ -857,12 +804,8 @@ export function createSettingsRoutes(db: Database, gitAuthService: GitAuthServic
857804
app.post('/opencode-reload', async (c) => {
858805
try {
859806
logger.info('OpenCode configuration reload requested')
860-
const resume = await reloadOpenCodeConfigWithResume(openCodeSupervisor, openCodeRestartCoordinator)
861-
return c.json({
862-
success: true,
863-
message: 'OpenCode configuration reloaded successfully',
864-
resumedSessions: resume?.resumedSessionIDs ?? [],
865-
})
807+
await reloadOpenCodeConfig(openCodeSupervisor)
808+
return c.json({ success: true, message: 'OpenCode configuration reloaded successfully' })
866809
} catch (error) {
867810
logger.error('Failed to reload OpenCode config:', error)
868811
if (error instanceof ConfigReloadError) {
@@ -1923,7 +1866,7 @@ export function createSettingsRoutes(db: Database, gitAuthService: GitAuthServic
19231866
})
19241867

19251868
app.get('/opencode-active-sessions', (c) => {
1926-
const sessions = openCodeRestartCoordinator?.captureResumableSessions() ?? []
1869+
const sessions = getOpenCodeRestartCoordinator()?.captureResumableSessions() ?? []
19271870
return c.json({ count: sessions.length, sessions })
19281871
})
19291872

backend/src/services/opencode-restart-coordinator.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ function createFakeActiveSessionsProvider(overrides: Partial<ActiveSessionsProvi
1515
return {
1616
getActiveSessions: vi.fn(() => ({})),
1717
isSubagentSession: vi.fn(() => false),
18-
isScheduledSession: vi.fn(() => false),
18+
getScheduledSessionIds: vi.fn(() => new Set<string>()),
1919
...overrides,
2020
}
2121
}
@@ -73,7 +73,7 @@ describe('OpenCodeRestartCoordinator', () => {
7373
vi.mocked(activeSessions.getActiveSessions).mockReturnValue({
7474
'/a': ['s1', 'sched1'],
7575
})
76-
vi.mocked(activeSessions.isScheduledSession).mockImplementation((id: string) => id === 'sched1')
76+
vi.mocked(activeSessions.getScheduledSessionIds).mockReturnValue(new Set(['sched1']))
7777

7878
const result = coordinator.captureResumableSessions()
7979

@@ -85,7 +85,7 @@ describe('OpenCodeRestartCoordinator', () => {
8585
'/p': ['manual', 'sub', 'sched'],
8686
})
8787
vi.mocked(activeSessions.isSubagentSession).mockImplementation((id: string) => id === 'sub')
88-
vi.mocked(activeSessions.isScheduledSession).mockImplementation((id: string) => id === 'sched')
88+
vi.mocked(activeSessions.getScheduledSessionIds).mockReturnValue(new Set(['sched']))
8989

9090
const result = coordinator.captureResumableSessions()
9191

@@ -209,7 +209,7 @@ describe('OpenCodeRestartCoordinator', () => {
209209
vi.mocked(activeSessions.getActiveSessions).mockReturnValue({
210210
'/a': ['manual1', 'sched1'],
211211
})
212-
vi.mocked(activeSessions.isScheduledSession).mockImplementation((id: string) => id === 'sched1')
212+
vi.mocked(activeSessions.getScheduledSessionIds).mockReturnValue(new Set(['sched1']))
213213

214214
const restart = vi.fn(async () => true)
215215

0 commit comments

Comments
 (0)