diff --git a/packages/web/bin/cli.js b/packages/web/bin/cli.js index d0259976..cba35160 100755 --- a/packages/web/bin/cli.js +++ b/packages/web/bin/cli.js @@ -74,7 +74,7 @@ function buildTunnelUrl(baseUrl, password, includePassword) { function parseArgs() { const args = process.argv.slice(2); const envPassword = process.env.OPENCHAMBER_UI_PASSWORD || undefined; - const options = { port: DEFAULT_PORT, daemon: false, uiPassword: envPassword, tryCfTunnel: false, tunnelQr: false, tunnelPasswordUrl: false, remoteUrl: null }; + const options = { port: DEFAULT_PORT, daemon: false, uiPassword: envPassword, tryCfTunnel: false, tunnelQr: false, tunnelPasswordUrl: false }; let command = 'serve'; const consumeValue = (currentIndex, inlineValue) => { @@ -132,12 +132,6 @@ function parseArgs() { options.uiPassword = typeof value === 'string' ? value : ''; break; } - case 'remote-url': { - const { value, nextIndex } = consumeValue(i, inlineValue); - i = nextIndex; - options.remoteUrl = typeof value === 'string' && value.length > 0 ? value : null; - break; - } case 'help': case 'h': showHelp(); @@ -173,7 +167,6 @@ COMMANDS: OPTIONS: -p, --port Web server port (default: ${DEFAULT_PORT}) - --remote-url Connect to remote OpenCode instance instead of starting local --ui-password Protect browser UI with single password --try-cf-tunnel Create a Cloudflare Quick Tunnel for remote access --tunnel-qr Display QR code for tunnel URL (use with --try-cf-tunnel) @@ -184,14 +177,12 @@ OPTIONS: ENVIRONMENT: OPENCHAMBER_UI_PASSWORD Alternative to --ui-password flag - OPENCODE_REMOTE_URL Alternative to --remote-url flag EXAMPLES: openchamber # Start on default port 3000 openchamber --port 8080 # Start on port 8080 openchamber serve --daemon # Start in background openchamber --try-cf-tunnel # Start with Cloudflare Quick Tunnel - openchamber --remote-url http://remote-server:3000 # Connect to remote OpenCode openchamber stop # Stop all running instances openchamber stop --port 3000 # Stop specific instance openchamber status # Check status @@ -465,8 +456,7 @@ const commands = { process.exit(1); } - // When remoteUrl is provided, skip local OpenCode CLI check - const opencodeBinary = options.remoteUrl ? null : await checkOpenCodeCLI(); + const opencodeBinary = await checkOpenCodeCLI(); const serverPath = path.join(__dirname, '..', 'server', 'index.js'); @@ -485,9 +475,6 @@ const commands = { if (options.tryCfTunnel) { serverArgs.push('--try-cf-tunnel'); } - if (options.remoteUrl) { - serverArgs.push('--remote-url', options.remoteUrl); - } const preferredRuntime = getPreferredServerRuntime(); const runtimeBin = preferredRuntime === 'bun' ? BUN_BIN : process.execPath; @@ -500,10 +487,9 @@ const commands = { env: { ...process.env, OPENCHAMBER_PORT: options.port.toString(), - ...(opencodeBinary ? { OPENCODE_BINARY: opencodeBinary } : {}), + OPENCODE_BINARY: opencodeBinary, ...(typeof effectiveUiPassword === 'string' ? { OPENCHAMBER_UI_PASSWORD: effectiveUiPassword } : {}), OPENCHAMBER_TRY_CF_TUNNEL: options.tryCfTunnel ? 'true' : 'false', - ...(options.remoteUrl ? { OPENCODE_REMOTE_URL: options.remoteUrl } : {}), } }); @@ -528,15 +514,10 @@ const commands = { } else { - if (opencodeBinary) { - process.env.OPENCODE_BINARY = opencodeBinary; - } + process.env.OPENCODE_BINARY = opencodeBinary; if (typeof effectiveUiPassword === 'string') { process.env.OPENCHAMBER_UI_PASSWORD = effectiveUiPassword; } - if (options.remoteUrl) { - process.env.OPENCODE_REMOTE_URL = options.remoteUrl; - } if (showAutoGeneratedPassword) { console.log(`\nšŸ” Auto-generated password: \x1b[92m${effectiveUiPassword}\x1b[0m`); console.log('āš ļø Save this password - it won\'t be shown again!\n'); @@ -552,10 +533,9 @@ const commands = { env: { ...process.env, OPENCHAMBER_PORT: options.port.toString(), - ...(opencodeBinary ? { OPENCODE_BINARY: opencodeBinary } : {}), + OPENCODE_BINARY: opencodeBinary, ...(typeof effectiveUiPassword === 'string' ? { OPENCHAMBER_UI_PASSWORD: effectiveUiPassword } : {}), OPENCHAMBER_TRY_CF_TUNNEL: options.tryCfTunnel ? 'true' : 'false', - ...(options.remoteUrl ? { OPENCODE_REMOTE_URL: options.remoteUrl } : {}), }, }); @@ -573,7 +553,6 @@ const commands = { exitOnShutdown: true, uiPassword: typeof effectiveUiPassword === 'string' ? effectiveUiPassword : null, tryCfTunnel: options.tryCfTunnel, - remoteUrl: options.remoteUrl, onTunnelReady: async (url) => { const displayUrl = buildTunnelUrl(url, effectiveUiPassword, options.tunnelPasswordUrl); console.log(`\n🌐 Tunnel URL: \x1b[36m${displayUrl}\x1b[0m\n`); diff --git a/packages/web/server/index.js b/packages/web/server/index.js index 3abf6642..a93945a7 100644 --- a/packages/web/server/index.js +++ b/packages/web/server/index.js @@ -1217,8 +1217,7 @@ function parseArgs(argv = process.argv.slice(2)) { process.env.OPENCODE_UI_PASSWORD || null; const envCfTunnel = process.env.OPENCHAMBER_TRY_CF_TUNNEL === 'true'; - const envRemoteUrl = process.env.OPENCODE_REMOTE_URL || null; - const options = { port: DEFAULT_PORT, uiPassword: envPassword, tryCfTunnel: envCfTunnel, remoteUrl: envRemoteUrl }; + const options = { port: DEFAULT_PORT, uiPassword: envPassword, tryCfTunnel: envCfTunnel }; const consumeValue = (currentIndex, inlineValue) => { if (typeof inlineValue === 'string') { @@ -1260,13 +1259,6 @@ function parseArgs(argv = process.argv.slice(2)) { options.tryCfTunnel = true; continue; } - - if (optionName === 'remote-url') { - const { value, nextIndex } = consumeValue(i, inlineValue); - i = nextIndex; - options.remoteUrl = typeof value === 'string' && value.length > 0 ? value : null; - continue; - } } return options; @@ -1667,20 +1659,14 @@ function setupProxy(app) { next(); }); - const remoteOrigin = process.env.__OPENCODE_REMOTE_ORIGIN || null; - const getProxyTarget = () => { - if (remoteOrigin) { - return remoteOrigin; - } - if (!openCodePort) { - return 'http://127.0.0.1:0'; - } - return `http://localhost:${openCodePort}`; - }; - const proxyMiddleware = createProxyMiddleware({ - target: getProxyTarget(), - router: getProxyTarget, + target: openCodePort ? `http://localhost:${openCodePort}` : 'http://127.0.0.1:0', + router: () => { + if (!openCodePort) { + return 'http://127.0.0.1:0'; + } + return `http://localhost:${openCodePort}`; + }, changeOrigin: true, pathRewrite: (path) => { if (!path.startsWith('/api')) { @@ -1813,7 +1799,6 @@ async function main(options = {}) { const tryCfTunnel = options.tryCfTunnel === true; const attachSignals = options.attachSignals !== false; const onTunnelReady = typeof options.onTunnelReady === 'function' ? options.onTunnelReady : null; - const remoteUrl = typeof options.remoteUrl === 'string' && options.remoteUrl.length > 0 ? options.remoteUrl : null; if (typeof options.exitOnShutdown === 'boolean') { exitOnShutdown = options.exitOnShutdown; } @@ -4715,58 +4700,34 @@ async function main(options = {}) { try { - if (remoteUrl) { - console.log(`Using remote OpenCode instance at: ${remoteUrl}`); - try { - const parsedUrl = new URL(remoteUrl); - const remotePort = parseInt(parsedUrl.port, 10) || (parsedUrl.protocol === 'https:' ? 443 : 80); - const remotePrefix = normalizeApiPrefix(parsedUrl.pathname); - - openCodePort = remotePort; - syncToHmrState(); - - // Remote origin is used by setupProxy to route requests to remote OpenCode - process.env.__OPENCODE_REMOTE_ORIGIN = `${parsedUrl.protocol}//${parsedUrl.host}`; - - setDetectedOpenCodeApiPrefix(remotePrefix); - isOpenCodeReady = true; - lastOpenCodeError = null; - openCodeNotReadySince = 0; - - console.log(`Remote OpenCode configured - port: ${remotePort}, prefix: ${remotePrefix || '(root)'}`); - } catch (urlError) { - throw new Error(`Invalid remote URL "${remoteUrl}": ${urlError.message}`); - } + // Check if we can reuse an existing OpenCode process from a previous HMR cycle + syncFromHmrState(); + if (await isOpenCodeProcessHealthy()) { + console.log(`[HMR] Reusing existing OpenCode process on port ${openCodePort}`); } else { - syncFromHmrState(); - if (await isOpenCodeProcessHealthy()) { - console.log(`[HMR] Reusing existing OpenCode process on port ${openCodePort}`); + // No healthy process, start fresh + if (ENV_CONFIGURED_OPENCODE_PORT) { + console.log(`Using OpenCode port from environment: ${ENV_CONFIGURED_OPENCODE_PORT}`); + setOpenCodePort(ENV_CONFIGURED_OPENCODE_PORT); } else { - if (ENV_CONFIGURED_OPENCODE_PORT) { - console.log(`Using OpenCode port from environment: ${ENV_CONFIGURED_OPENCODE_PORT}`); - setOpenCodePort(ENV_CONFIGURED_OPENCODE_PORT); - } else { - openCodePort = null; - syncToHmrState(); - } - - lastOpenCodeError = null; - openCodeProcess = await startOpenCode(); + openCodePort = null; syncToHmrState(); } - await waitForOpenCodePort(); - try { - await waitForOpenCodeReady(); - } catch (error) { - console.error(`OpenCode readiness check failed: ${error.message}`); - scheduleOpenCodeApiDetection(); - } + + lastOpenCodeError = null; + openCodeProcess = await startOpenCode(); + syncToHmrState(); + } + await waitForOpenCodePort(); + try { + await waitForOpenCodeReady(); + } catch (error) { + console.error(`OpenCode readiness check failed: ${error.message}`); + scheduleOpenCodeApiDetection(); } setupProxy(app); - if (!remoteUrl) { - scheduleOpenCodeApiDetection(); - startHealthMonitoring(); - } + scheduleOpenCodeApiDetection(); + startHealthMonitoring(); } catch (error) { console.error(`Failed to start OpenCode: ${error.message}`); console.log('Continuing without OpenCode integration...'); @@ -4874,8 +4835,7 @@ if (isCliExecution) { tryCfTunnel: cliOptions.tryCfTunnel, attachSignals: true, exitOnShutdown: true, - uiPassword: cliOptions.uiPassword, - remoteUrl: cliOptions.remoteUrl + uiPassword: cliOptions.uiPassword }).catch(error => { console.error('Failed to start server:', error); process.exit(1);