@@ -557,12 +557,50 @@ export namespace Server {
557557 )
558558 . all ( "/*" , async ( c ) => {
559559 const path = c . req . path
560+ const appDir = Flag . OPENCODE_WEB_URL
560561
561- const response = await proxy ( `https://app.opencode.ai${ path } ` , {
562+ // Serve from local dist directory if OPENCODE_WEB_URL is a file path
563+ if ( appDir && ! appDir . startsWith ( "http" ) ) {
564+ const fs = await import ( "fs" )
565+ const nodePath = await import ( "path" )
566+ const mimeTypes : Record < string , string > = {
567+ ".html" : "text/html" ,
568+ ".js" : "application/javascript" ,
569+ ".mjs" : "application/javascript" ,
570+ ".css" : "text/css" ,
571+ ".json" : "application/json" ,
572+ ".svg" : "image/svg+xml" ,
573+ ".png" : "image/png" ,
574+ ".woff" : "font/woff" ,
575+ ".woff2" : "font/woff2" ,
576+ ".wasm" : "application/wasm" ,
577+ ".onnx" : "application/octet-stream" ,
578+ }
579+ const getMime = ( p : string ) => mimeTypes [ nodePath . default . extname ( p ) ] || "application/octet-stream"
580+ const filePath = nodePath . default . join ( appDir , path === "/" ? "index.html" : path )
581+ if ( fs . existsSync ( filePath ) && fs . statSync ( filePath ) . isFile ( ) ) {
582+ return new Response ( Bun . file ( filePath ) , { headers : { "Content-Type" : getMime ( filePath ) } } )
583+ }
584+ // SPA fallback — only for paths that look like client-side routes
585+ const apiPrefixes = [ "/global" , "/project" , "/pty" , "/config" , "/experimental" , "/session" , "/permission" , "/question" , "/provider" , "/mcp" , "/tui" , "/voice" , "/tts" ]
586+ const isApiPath = apiPrefixes . some ( ( prefix ) => path . startsWith ( prefix ) )
587+ if ( ! isApiPath ) {
588+ const indexPath = nodePath . default . join ( appDir , "index.html" )
589+ if ( fs . existsSync ( indexPath ) ) {
590+ return new Response ( Bun . file ( indexPath ) , { headers : { "Content-Type" : "text/html" } } )
591+ }
592+ }
593+ return c . notFound ( )
594+ }
595+
596+ const appHost = appDir || "https://app.opencode.ai"
597+ const appHostname = new URL ( appHost ) . hostname
598+
599+ const response = await proxy ( `${ appHost } ${ path } ` , {
562600 ...c . req ,
563601 headers : {
564602 ...c . req . raw . headers ,
565- host : "app.opencode.ai" ,
603+ host : appHostname ,
566604 } ,
567605 } )
568606 response . headers . set (
0 commit comments