1616 * - Extension assets served from `./output/chrome-mv3-dev` via `/chrome-mv3-dev` route 
1717 */ 
1818import  {  error  }  from  'node:console' 
19+ import  {  spawn  }  from  'node:child_process' 
1920import  fs  from  'node:fs/promises' 
2021import  path  from  'node:path' 
2122import  {  fileURLToPath  }  from  'node:url' 
@@ -27,6 +28,9 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url))
2728const  app  =  express ( ) 
2829const  PORT  =  3001 
2930
31+ // Middleware to parse JSON bodies 
32+ app . use ( express . json ( ) ) 
33+ 
3034// Store HAR json 
3135const  harCache  =  new  Map < keyof  typeof  PAGES ,  Har > ( ) 
3236
@@ -239,6 +243,63 @@ app.get('/asset/:key/*', async (req, res) => {
239243// Serve extension assets from filesystem 
240244app . use ( '/chrome-mv3-dev' ,  express . static ( path . join ( __dirname ,  '..' ,  '.output' ,  'chrome-mv3-dev' ) ) ) 
241245
246+ // Rebuild endpoint 
247+ app . post ( '/rebuild' ,  async  ( req ,  res )  =>  { 
248+   try  { 
249+     console . log ( 'Rebuild triggered via API' ) 
250+     
251+     // Run npx wxt build --mode development 
252+     const  buildProcess  =  spawn ( 'npx' ,  [ 'wxt' ,  'build' ,  '--mode' ,  'development' ] ,  { 
253+       cwd : path . join ( __dirname ,  '..' ) , 
254+       stdio : [ 'pipe' ,  'pipe' ,  'pipe' ] 
255+     } ) 
256+     
257+     let  stdout  =  '' 
258+     let  stderr  =  '' 
259+     
260+     buildProcess . stdout . on ( 'data' ,  ( data )  =>  { 
261+       stdout  +=  data . toString ( ) 
262+       console . log ( '[BUILD]' ,  data . toString ( ) . trim ( ) ) 
263+     } ) 
264+     
265+     buildProcess . stderr . on ( 'data' ,  ( data )  =>  { 
266+       stderr  +=  data . toString ( ) 
267+       console . error ( '[BUILD ERROR]' ,  data . toString ( ) . trim ( ) ) 
268+     } ) 
269+     
270+     buildProcess . on ( 'close' ,  ( code )  =>  { 
271+       if  ( code  ===  0 )  { 
272+         console . log ( 'Build completed successfully' ) 
273+         res . json ( {  success : true ,  message : 'Build completed successfully'  } ) 
274+       }  else  { 
275+         console . error ( 'Build failed with code:' ,  code ) 
276+         res . status ( 500 ) . json ( {  
277+           success : false ,  
278+           message : 'Build failed' ,  
279+           error : stderr  ||  stdout  
280+         } ) 
281+       } 
282+     } ) 
283+     
284+     buildProcess . on ( 'error' ,  ( error )  =>  { 
285+       console . error ( 'Failed to start build process:' ,  error ) 
286+       res . status ( 500 ) . json ( {  
287+         success : false ,  
288+         message : 'Failed to start build process' ,  
289+         error : error . message  
290+       } ) 
291+     } ) 
292+     
293+   }  catch  ( error )  { 
294+     console . error ( 'Rebuild endpoint error:' ,  error ) 
295+     res . status ( 500 ) . json ( {  
296+       success : false ,  
297+       message : 'Internal server error' ,  
298+       error : error  instanceof  Error  ? error . message  : 'Unknown error'  
299+     } ) 
300+   } 
301+ } ) 
302+ 
242303app . listen ( PORT ,  ( )  =>  { 
243304  console . log ( `HAR Page Viewer running at http://localhost:${ PORT }  ) 
244305  console . log ( 'Click the links to view recorded pages' ) 
@@ -296,6 +357,90 @@ function injectGitcassoScript(key: keyof typeof PAGES, html: string) {
296357            .catch(error => { 
297358              console.error('Failed to load and patch content script:', error); 
298359            }); 
360+            
361+           // Create floating rebuild button 
362+           const rebuildButton = document.createElement('div'); 
363+           rebuildButton.id = 'gitcasso-rebuild-btn'; 
364+           rebuildButton.innerHTML = '🔄'; 
365+           rebuildButton.title = 'Rebuild Extension'; 
366+           rebuildButton.style.cssText = \` 
367+             position: fixed; 
368+             top: 20px; 
369+             right: 20px; 
370+             width: 50px; 
371+             height: 50px; 
372+             background: #007acc; 
373+             color: white; 
374+             border-radius: 50%; 
375+             display: flex; 
376+             align-items: center; 
377+             justify-content: center; 
378+             font-size: 20px; 
379+             cursor: pointer; 
380+             box-shadow: 0 4px 12px rgba(0,0,0,0.3); 
381+             z-index: 999999; 
382+             user-select: none; 
383+             transition: all 0.2s ease; 
384+             font-family: system-ui, -apple-system, sans-serif; 
385+           \`; 
386+            
387+           rebuildButton.addEventListener('mouseenter', () => { 
388+             rebuildButton.style.transform = 'scale(1.1)'; 
389+             rebuildButton.style.backgroundColor = '#005a9e'; 
390+           }); 
391+            
392+           rebuildButton.addEventListener('mouseleave', () => { 
393+             rebuildButton.style.transform = 'scale(1)'; 
394+             rebuildButton.style.backgroundColor = '#007acc'; 
395+           }); 
396+            
397+           rebuildButton.addEventListener('click', async () => { 
398+             try { 
399+               rebuildButton.innerHTML = '⏳'; 
400+               rebuildButton.style.backgroundColor = '#ffa500'; 
401+               rebuildButton.title = 'Rebuilding...'; 
402+                
403+               const response = await fetch('/rebuild', { 
404+                 method: 'POST', 
405+                 headers: { 'Content-Type': 'application/json' } 
406+               }); 
407+                
408+               const result = await response.json(); 
409+                
410+               if (result.success) { 
411+                 rebuildButton.innerHTML = '✅'; 
412+                 rebuildButton.style.backgroundColor = '#28a745'; 
413+                 rebuildButton.title = 'Build successful! Reloading...'; 
414+                  
415+                 setTimeout(() => { 
416+                   location.reload(true); 
417+                 }, 1000); 
418+               } else { 
419+                 rebuildButton.innerHTML = '❌'; 
420+                 rebuildButton.style.backgroundColor = '#dc3545'; 
421+                 rebuildButton.title = 'Build failed: ' + (result.error || result.message); 
422+                  
423+                 setTimeout(() => { 
424+                   rebuildButton.innerHTML = '🔄'; 
425+                   rebuildButton.style.backgroundColor = '#007acc'; 
426+                   rebuildButton.title = 'Rebuild Extension'; 
427+                 }, 3000); 
428+               } 
429+             } catch (error) { 
430+               console.error('Rebuild failed:', error); 
431+               rebuildButton.innerHTML = '❌'; 
432+               rebuildButton.style.backgroundColor = '#dc3545'; 
433+               rebuildButton.title = 'Network error: ' + error.message; 
434+                
435+               setTimeout(() => { 
436+                 rebuildButton.innerHTML = '🔄'; 
437+                 rebuildButton.style.backgroundColor = '#007acc'; 
438+                 rebuildButton.title = 'Rebuild Extension'; 
439+               }, 3000); 
440+             } 
441+           }); 
442+            
443+           document.body.appendChild(rebuildButton); 
299444        </script> 
300445      ` 
301446  if  ( ! html . includes ( '</body>' ) )  { 
0 commit comments