@@ -24,11 +24,8 @@ import { patchConfigWithRecovery } from './opencode/config-recovery'
2424import type { OpenCodeClient } from './opencode/client'
2525import { writeFileContent } from './file-operations'
2626
27- const OPENCODE_SERVER_PORT = ENV . OPENCODE . PORT
2827const OPENCODE_SERVER_HOST = ENV . OPENCODE . HOST
29- const OPENCODE_SERVER_PUBLIC_URL = ENV . OPENCODE . PUBLIC_URL
30- const OPENCODE_SERVER_PASSWORD = ENV . OPENCODE . SERVER_PASSWORD
31- const OPENCODE_SERVER_USERNAME = ENV . OPENCODE . SERVER_USERNAME
28+ export const OPENCODE_SERVER_CONNECT_HOST = OPENCODE_SERVER_HOST === '0.0.0.0' ? '127.0.0.1' : OPENCODE_SERVER_HOST
3229const MIN_OPENCODE_VERSION = '1.0.137'
3330const MAX_STDERR_SIZE = 10240
3431const HEALTH_CHECK_TIMEOUT_MS = 3000
@@ -91,6 +88,10 @@ function formatStartupError(stderrOutput: string, fallback: string): string {
9188// This allows proper mocking in tests
9289const getOpenCodeServerDirectory = ( ) => getWorkspacePath ( )
9390const getOpenCodeConfigPath = ( ) => getOpenCodeConfigFilePath ( )
91+ const getOpenCodeServerPort = ( ) => ENV . OPENCODE . PORT
92+ const getOpenCodeServerHost = ( ) => ENV . OPENCODE . HOST
93+ const getOpenCodeServerPublicUrl = ( ) => ENV . OPENCODE . PUBLIC_URL
94+ const getOpenCodeServerUsername = ( ) => ENV . OPENCODE . SERVER_USERNAME
9495
9596class OpenCodeServerManager {
9697 private static instance : OpenCodeServerManager
@@ -113,6 +114,20 @@ class OpenCodeServerManager {
113114 this . openCodeClient = client
114115 }
115116
117+ async rebuildClient ( ) : Promise < void > {
118+ const password = this . getResolvedPassword ( )
119+ const { createOpenCodeClient } = await import ( './opencode/client' )
120+ this . openCodeClient = createOpenCodeClient ( password )
121+ }
122+
123+ private getResolvedPassword ( ) : string {
124+ if ( this . db ) {
125+ const settingsService = new SettingsService ( this . db )
126+ return settingsService . getOpenCodeServerPassword ( )
127+ }
128+ return ENV . OPENCODE . SERVER_PASSWORD
129+ }
130+
116131 private requireClient ( ) : OpenCodeClient {
117132 if ( ! this . openCodeClient ) {
118133 throw new Error ( 'OpenCodeClient not configured on OpenCodeServerManager. Call setOpenCodeClient() during startup.' )
@@ -166,7 +181,18 @@ class OpenCodeServerManager {
166181 return
167182 }
168183
184+ await this . rebuildClient ( )
185+
169186 const isDevelopment = ENV . SERVER . NODE_ENV !== 'production'
187+ const password = this . getResolvedPassword ( )
188+ const openCodeServerHost = getOpenCodeServerHost ( )
189+ const isExposed = openCodeServerHost !== '127.0.0.1' && openCodeServerHost !== 'localhost'
190+ if ( isExposed && ! password ) {
191+ const msg = `OPENCODE_HOST=${ openCodeServerHost } exposes the OpenCode server externally but no password is configured. Set OPENCODE_SERVER_PASSWORD env var or configure a password via Settings → OpenCode → Server Auth.`
192+ this . lastStartupError = msg
193+ logger . error ( msg )
194+ throw new Error ( msg )
195+ }
170196
171197 let gitCredentials : GitCredential [ ] = [ ]
172198 let gitIdentityEnv : Record < string , string > = { }
@@ -186,9 +212,10 @@ class OpenCodeServerManager {
186212 }
187213 }
188214
189- const existingProcesses = await this . findProcessesByPort ( OPENCODE_SERVER_PORT )
215+ const openCodeServerPort = getOpenCodeServerPort ( )
216+ const existingProcesses = await this . findProcessesByPort ( openCodeServerPort )
190217 if ( existingProcesses . length > 0 ) {
191- logger . info ( `OpenCode server already running on port ${ OPENCODE_SERVER_PORT } ` )
218+ logger . info ( `OpenCode server already running on port ${ openCodeServerPort } ` )
192219 const healthy = await this . checkHealth ( )
193220 if ( healthy ) {
194221 if ( isDevelopment ) {
@@ -288,7 +315,7 @@ class OpenCodeServerManager {
288315
289316 this . serverProcess = spawn (
290317 'opencode' ,
291- [ 'serve' , '--port' , OPENCODE_SERVER_PORT . toString ( ) , '--hostname' , OPENCODE_SERVER_HOST ] ,
318+ [ 'serve' , '--port' , openCodeServerPort . toString ( ) , '--hostname' , openCodeServerHost ] ,
292319 {
293320 cwd : openCodeServerDirectory ,
294321 detached : ! isDevelopment ,
@@ -301,11 +328,11 @@ class OpenCodeServerManager {
301328 XDG_DATA_HOME : path . join ( openCodeServerDirectory , '.opencode/state' ) ,
302329 XDG_STATE_HOME : path . join ( openCodeServerDirectory , '.opencode/state' ) ,
303330 XDG_CONFIG_HOME : path . join ( openCodeServerDirectory , '.config' ) ,
304- ...( OPENCODE_SERVER_PUBLIC_URL ? { OPENCODE_PUBLIC_URL : OPENCODE_SERVER_PUBLIC_URL } : { } ) ,
305- ...( OPENCODE_SERVER_PASSWORD
331+ ...( getOpenCodeServerPublicUrl ( ) ? { OPENCODE_PUBLIC_URL : getOpenCodeServerPublicUrl ( ) } : { } ) ,
332+ ...( password
306333 ? {
307- OPENCODE_SERVER_PASSWORD ,
308- OPENCODE_SERVER_USERNAME ,
334+ OPENCODE_SERVER_PASSWORD : password ,
335+ OPENCODE_SERVER_USERNAME : getOpenCodeServerUsername ( ) ,
309336 }
310337 : { } ) ,
311338 OPENCODE_CONFIG : openCodeConfigPath ,
@@ -531,7 +558,7 @@ class OpenCodeServerManager {
531558 }
532559
533560 getPort ( ) : number {
534- return OPENCODE_SERVER_PORT
561+ return getOpenCodeServerPort ( )
535562 }
536563
537564 getVersion ( ) : string | null {
0 commit comments