@@ -56,6 +56,12 @@ initProjectors()
5656
5757export namespace Server {
5858 const log = Log . create ( { service : "server" } )
59+ const DEFAULT_CSP =
60+ "default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; media-src 'self' data:; connect-src 'self' data:"
61+ const embeddedUIPromise = Flag . OPENCODE_DISABLE_EMBEDDED_WEB_UI
62+ ? Promise . resolve ( null )
63+ : // @ts -expect-error - generated file at build time
64+ import ( "opencode-web-ui.gen.ts" ) . then ( ( module ) => module . default as Record < string , string > ) . catch ( ( ) => null )
5965
6066 export const Default = lazy ( ( ) => createApp ( { } ) )
6167
@@ -504,24 +510,40 @@ export namespace Server {
504510 } ,
505511 )
506512 . all ( "/*" , async ( c ) => {
513+ const embeddedWebUI = await embeddedUIPromise
507514 const path = c . req . path
508515
509- const response = await proxy ( `https://app.opencode.ai${ path } ` , {
510- ...c . req ,
511- headers : {
512- ...c . req . raw . headers ,
513- host : "app.opencode.ai" ,
514- } ,
515- } )
516- const match = response . headers . get ( "content-type" ) ?. includes ( "text/html" )
517- ? ( await response . clone ( ) . text ( ) ) . match (
518- / < s c r i p t \b (? ! [ ^ > ] * \b s r c \s * = ) [ ^ > ] * \b i d = ( [ ' " ] ) o c - t h e m e - p r e l o a d - s c r i p t \1[ ^ > ] * > ( [ \s \S ] * ?) < \/ s c r i p t > / i,
519- )
520- : undefined
521- const hash = match ? createHash ( "sha256" ) . update ( match [ 2 ] ) . digest ( "base64" ) : ""
522- response . headers . set ( "Content-Security-Policy" , csp ( hash ) )
523- return response
524- } )
516+ if ( embeddedWebUI ) {
517+ const match = embeddedWebUI [ path . replace ( / ^ \/ / , "" ) ] ?? embeddedWebUI [ "index.html" ] ?? null
518+ if ( ! match ) return c . json ( { error : "Not Found" } , 404 )
519+ const file = Bun . file ( match )
520+ if ( await file . exists ( ) ) {
521+ c . header ( "Content-Type" , file . type )
522+ if ( file . type . startsWith ( "text/html" ) ) {
523+ c . header ( "Content-Security-Policy" , DEFAULT_CSP )
524+ }
525+ return c . body ( await file . arrayBuffer ( ) )
526+ } else {
527+ return c . json ( { error : "Not Found" } , 404 )
528+ }
529+ } else {
530+ const response = await proxy ( `https://app.opencode.ai${ path } ` , {
531+ ...c . req ,
532+ headers : {
533+ ...c . req . raw . headers ,
534+ host : "app.opencode.ai" ,
535+ } ,
536+ } )
537+ const match = response . headers . get ( "content-type" ) ?. includes ( "text/html" )
538+ ? ( await response . clone ( ) . text ( ) ) . match (
539+ / < s c r i p t \b (? ! [ ^ > ] * \b s r c \s * = ) [ ^ > ] * \b i d = ( [ ' " ] ) o c - t h e m e - p r e l o a d - s c r i p t \1[ ^ > ] * > ( [ \s \S ] * ?) < \/ s c r i p t > / i,
540+ )
541+ : undefined
542+ const hash = match ? createHash ( "sha256" ) . update ( match [ 2 ] ) . digest ( "base64" ) : ""
543+ response . headers . set ( "Content-Security-Policy" , csp ( hash ) )
544+ return response
545+ }
546+ } ) as unknown as Hono
525547 }
526548
527549 export async function openapi ( ) {
0 commit comments