11import { defineEventHandler , readBody , setResponseStatus } from 'h3' ;
22
3- const MCP_URL = ( process . env . BROWSER_ECHO_MCP_URL || '' ) . replace ( / \/ $ / , '' ) . replace ( / \/ m c p $ / i, '' ) ;
4- const MCP_LOGS_ROUTE = process . env . BROWSER_ECHO_MCP_LOGS_ROUTE || '/__client-logs' ;
3+ // Simplified: resolve MCP from project-local JSON once; fallback to 5179
54
65type Level = 'log' | 'info' | 'warn' | 'error' | 'debug' ;
76type Entry = { level : Level | string ; text : string ; time ?: number ; stack ?: string ; source ?: string ; } ;
@@ -16,31 +15,14 @@ export default defineEventHandler(async (event) => {
1615 setResponseStatus ( event , 400 ) ; return 'invalid payload' ;
1716 }
1817
19- // Resolve MCP URL: env var (if healthy) → port 5179 → local discovery file (dev only)
20- let mcp = { url : '' , token : '' , routeLogs : '' as `/${string } ` | '' } as { url : string ; token ?: string ; routeLogs ?: `/${string } ` } ;
21- if ( MCP_URL ) {
22- if ( await __pingHealthNuxt ( `${ MCP_URL } /health` , 300 ) ) {
23- mcp = { url : MCP_URL } ;
24- }
25- }
26- // Prefer project-local discovery over port scan
27- if ( ! mcp . url && process . env . NODE_ENV === 'development' ) {
28- mcp = await __resolveMcpUrlNuxt ( ) ;
29- }
30- if ( ! mcp . url && process . env . NODE_ENV === 'development' && process . env . BROWSER_ECHO_ALLOW_PORT_SCAN === '1' ) {
31- // Try default port 5179 only as a last resort (gated)
32- const candidates = [ 'http://127.0.0.1:5179' , 'http://localhost:5179' ] ;
33- for ( const base of candidates ) {
34- if ( await __pingHealthNuxt ( `${ base } /health` , 300 ) ) { mcp = { url : base } ; break ; }
35- }
36- }
18+ // Resolve MCP once: project JSON → 5179 fallback
19+ const mcp = await __resolveMcpFromProjectNuxt ( ) ;
3720
3821 // Forward to MCP server if available (fire-and-forget)
3922 if ( mcp . url ) {
4023 try {
41- const route = ( MCP_LOGS_ROUTE as `/${ string } ` ) || ( mcp . routeLogs as `/${string } `) || '/__client-logs' ;
24+ const route = ( mcp . routeLogs as `/${string } `) || '/__client-logs' ;
4225 const headers : Record < string , string > = { 'content-type' : 'application/json' } ;
43- if ( mcp . token ) headers [ 'x-be-token' ] = mcp . token ;
4426 fetch ( `${ mcp . url } ${ route } ` , {
4527 method : 'POST' ,
4628 headers,
@@ -51,11 +33,8 @@ export default defineEventHandler(async (event) => {
5133 } catch { }
5234 }
5335
54- // Dynamically decide whether to print to terminal
55- const envVal = process . env . BROWSER_ECHO_SUPPRESS_TERMINAL ;
56- const forceSuppress = envVal === '1' ;
57- const forcePrint = envVal === '0' ;
58- const shouldPrint = forcePrint ? true : ( forceSuppress ? false : ! mcp . url ) ;
36+ // Suppress when forwarding active
37+ const shouldPrint = ! mcp . url ;
5938
6039 const sid = ( payload . sessionId ?? 'anon' ) . slice ( 0 , 8 ) ;
6140 for ( const entry of payload . entries ) {
@@ -97,57 +76,29 @@ function color(level: Level, msg: string) {
9776}
9877function dim ( s : string ) { return c . dim + s + c . reset ; }
9978
100- let __mcpDiscoveryCacheNuxt : { url : string ; token ?: string ; routeLogs ?: `/${string } `; ts : number } | null = null ;
101-
102- async function __resolveMcpUrlNuxt ( ) : Promise < { url : string ; token ?: string ; routeLogs ?: `/${string } ` } > {
103- const now = Date . now ( ) ;
104- const CACHE_TTL_MS = 10_000 ;
105-
106- if ( __mcpDiscoveryCacheNuxt && ( now - __mcpDiscoveryCacheNuxt . ts ) < CACHE_TTL_MS ) {
107- return { url : __mcpDiscoveryCacheNuxt . url , token : __mcpDiscoveryCacheNuxt . token , routeLogs : __mcpDiscoveryCacheNuxt . routeLogs } ;
108- }
109-
110- const fromFile = await __readDiscoveryFromFileNuxt ( ) ;
111- if ( fromFile . url ) {
112- // Require health and, if provided, project scoping match
113- const healthy = await __pingHealthNuxt ( `${ fromFile . url } /health` , 300 ) ;
114- const scopedOk = await __isInsideProjectNuxt ( fromFile . projectRoot ) ;
115- if ( healthy && scopedOk ) {
116- __mcpDiscoveryCacheNuxt = { url : fromFile . url , token : fromFile . token , routeLogs : fromFile . routeLogs , ts : now } ;
117- return fromFile ;
118- }
119- }
120-
121- __mcpDiscoveryCacheNuxt = { url : '' , ts : now } as any ;
122- return { url : '' } ;
123- }
79+ let __mcpProjectCacheNuxt : { url : string ; routeLogs ?: `/${string } ` } | null = null ;
12480
125- async function __readDiscoveryFromFileNuxt ( ) : Promise < { url : string ; token ?: string ; routeLogs ?: `/${string } `; projectRoot ?: string } > {
81+ async function __resolveMcpFromProjectNuxt ( ) : Promise < { url : string ; routeLogs ?: `/${string } ` } > {
82+ if ( __mcpProjectCacheNuxt ) return __mcpProjectCacheNuxt ;
12683 try {
12784 const { readFileSync, existsSync } = await import ( 'node:fs' ) ;
128- const { join, dirname } = await import ( 'node:path' ) ;
129- let dir = process . cwd ( ) ;
130- const root = dirname ( '/' ) ;
131- while ( true ) {
132- const p = join ( dir , '.browser-echo-mcp.json' ) ;
133- if ( existsSync ( p ) ) {
134- try {
135- const raw = readFileSync ( p , 'utf-8' ) ;
136- const data = JSON . parse ( raw ) ;
137- const url = ( data ?. url ? String ( data . url ) : '' ) . replace ( / \/ $ / , '' ) ;
138- const ts = typeof data ?. timestamp === 'number' ? data . timestamp : 0 ;
139- const token = data ?. token ? String ( data . token ) : undefined ;
140- const routeLogs = data ?. routeLogs ? String ( data . routeLogs ) as `/${string } ` : undefined ;
141- const projectRoot = data ?. projectRoot ? String ( data . projectRoot ) : undefined ;
142- if ( url && ( Date . now ( ) - ts ) < 60_000 ) return { url, token, routeLogs, projectRoot } ;
143- } catch { }
85+ const { join } = await import ( 'node:path' ) ;
86+ const p = join ( process . cwd ( ) , '.browser-echo.json' ) ;
87+ if ( existsSync ( p ) ) {
88+ const raw = readFileSync ( p , 'utf-8' ) ;
89+ const data = JSON . parse ( raw ) ;
90+ const url = ( data ?. url ? String ( data . url ) : '' ) . replace ( / \/ $ / , '' ) ;
91+ const routeLogs = ( data ?. route ? String ( data . route ) as `/${string } ` : '/__client-logs' ) ;
92+ if ( url && await __pingHealthNuxt ( `${ url } /health` , 300 ) ) {
93+ __mcpProjectCacheNuxt = { url, routeLogs } ;
94+ return __mcpProjectCacheNuxt ;
14495 }
145- const parent = dirname ( dir ) ;
146- if ( parent === dir || parent === root ) break ;
147- dir = parent ;
14896 }
14997 } catch { }
150- return { url : '' } ;
98+ for ( const base of [ 'http://127.0.0.1:5179' , 'http://localhost:5179' ] ) {
99+ if ( await __pingHealthNuxt ( `${ base } /health` , 300 ) ) { __mcpProjectCacheNuxt = { url : base , routeLogs : '/__client-logs' } ; return __mcpProjectCacheNuxt ; }
100+ }
101+ __mcpProjectCacheNuxt = { url : '' } as any ; return __mcpProjectCacheNuxt ;
151102}
152103
153104async function __pingHealthNuxt ( url : string , timeoutMs : number ) : Promise < boolean > {
@@ -161,17 +112,3 @@ async function __pingHealthNuxt(url: string, timeoutMs: number): Promise<boolean
161112 return false ;
162113 }
163114}
164-
165- async function __isInsideProjectNuxt ( root ?: string ) : Promise < boolean > {
166- if ( ! root ) return true ;
167- try {
168- const { realpathSync } = await import ( 'node:fs' ) ;
169- const { relative, isAbsolute, sep, dirname } = await import ( 'node:path' ) ;
170- const realRoot = realpathSync ( root ) ;
171- const realCwd = realpathSync ( process . cwd ( ) ) ;
172- const rel = relative ( realRoot , realCwd ) ;
173- return rel === '' || ( ! rel . startsWith ( '..' + sep ) && ! isAbsolute ( rel ) ) ;
174- } catch {
175- return false ;
176- }
177- }
0 commit comments