@@ -471,10 +471,12 @@ function handleProtocolUrl(url: string) {
471471function processProtocolUrl ( url : string ) {
472472 const urlObj = new URL ( url ) ;
473473 const code = urlObj . searchParams . get ( 'code' ) ;
474+ const token = urlObj . searchParams . get ( 'token' ) ;
474475 const share_token = urlObj . searchParams . get ( 'share_token' ) ;
475476
476477 log . info ( 'urlObj' , urlObj ) ;
477478 log . info ( 'code' , code ) ;
479+ log . info ( 'token' , token ) ;
478480 log . info ( 'share_token' , share_token ) ;
479481
480482 if ( win && ! win . isDestroyed ( ) ) {
@@ -489,6 +491,12 @@ function processProtocolUrl(url: string) {
489491 return ;
490492 }
491493
494+ if ( token ) {
495+ log . info ( 'protocol token received' ) ;
496+ win . webContents . send ( 'auth-token-received' , token ) ;
497+ return ;
498+ }
499+
492500 if ( code ) {
493501 log . error ( 'protocol code:' , code ) ;
494502 win . webContents . send ( 'auth-code-received' , code ) ;
@@ -524,6 +532,58 @@ function processQueuedProtocolUrls() {
524532 }
525533}
526534
535+ // ==================== auth callback server ====================
536+ // Local HTTP server for receiving auth callbacks from external login (eigent.ai)
537+ // Works in both dev and production mode, avoids eigent:// protocol issues in dev
538+ let authCallbackServer : http . Server | null = null ;
539+ let authCallbackPort : number | null = null ;
540+
541+ async function startAuthCallbackServer ( ) {
542+ if ( authCallbackServer ) return authCallbackPort ;
543+
544+ const port = await findAvailablePort ( 19836 , 19900 ) ;
545+
546+ authCallbackServer = http . createServer ( ( req , res ) => {
547+ const url = new URL ( req . url || '' , `http://localhost:${ port } ` ) ;
548+
549+ if ( url . pathname === '/auth/callback' ) {
550+ const token = url . searchParams . get ( 'token' ) ;
551+ log . info ( 'Auth callback URL:' , req . url ) ;
552+ log . info ( 'Auth callback token present:' , ! ! token ) ;
553+ log . info ( 'Auth callback win available:' , ! ! win && ! win . isDestroyed ( ) ) ;
554+
555+ res . writeHead ( 200 , { 'Content-Type' : 'text/html; charset=utf-8' } ) ;
556+ res . end ( `
557+ <!DOCTYPE html>
558+ <html><head><title>Login Successful</title>
559+ <style>
560+ body { font-family: -apple-system, system-ui, sans-serif; display: flex; justify-content: center; align-items: center; min-height: 100vh; margin: 0; background: #f4f4f9; color: #333; }
561+ .container { padding: 40px; background: white; border-radius: 12px; box-shadow: 0 4px 20px rgba(0,0,0,0.1); text-align: center; }
562+ </style></head>
563+ <body><div class="container">
564+ <h1>Login Successful</h1>
565+ <p>You can close this tab and return to Eigent.</p>
566+ </div></body></html>
567+ ` ) ;
568+
569+ if ( token && win && ! win . isDestroyed ( ) ) {
570+ log . info ( 'Auth callback received token' ) ;
571+ win . webContents . send ( 'auth-token-received' , token ) ;
572+ win . show ( ) ;
573+ win . focus ( ) ;
574+ }
575+ } else {
576+ res . writeHead ( 404 ) ;
577+ res . end ( 'Not Found' ) ;
578+ }
579+ } ) ;
580+
581+ authCallbackServer . listen ( port ) ;
582+ authCallbackPort = port ;
583+ log . info ( `Auth callback server started on port ${ port } ` ) ;
584+ return port ;
585+ }
586+
527587// ==================== single instance lock ====================
528588const setupSingleInstanceLock = ( ) => {
529589 // The lock is already acquired at module level (requestSingleInstanceLock
@@ -603,6 +663,12 @@ const checkManagerInstance = (manager: any, name: string) => {
603663} ;
604664
605665function registerIpcHandlers ( ) {
666+ // ==================== auth callback ====================
667+ ipcMain . handle ( 'get-auth-callback-url' , async ( ) => {
668+ const port = await startAuthCallbackServer ( ) ;
669+ return `http://localhost:${ port } /auth/callback` ;
670+ } ) ;
671+
606672 // ==================== basic info handler ====================
607673 ipcMain . handle ( 'get-browser-port' , ( ) => {
608674 log . info ( 'Getting browser port' ) ;
0 commit comments