@@ -570,13 +570,13 @@ export function SovereignDashboard({ demoMode = false }: { demoMode?: boolean }
570570 return Math . min ( 100 , score ) ;
571571 }
572572
573- /** Run a single scrape against one specific provider (trigger + poll) */
573+ /** Run a single scrape against one specific provider */
574574 async function callScrapeOne ( prompt : string , provider : Provider ) : Promise < ScrapeRun | null > {
575575 if ( demoMode ) { setMessage ( "Demo mode — API calls are disabled" ) ; return null ; }
576576 try {
577577 console . log ( `[scrape] callScrapeOne: START provider=${ provider } prompt="${ prompt . slice ( 0 , 80 ) } ..."` ) ;
578578
579- // Trigger — returns immediately with a jobId
579+ // Trigger — waits for full scrape completion, returns result directly
580580 const triggerRes = await fetch ( `${ BASE_PATH } /api/scrape/trigger` , {
581581 method : "POST" ,
582582 headers : { "Content-Type" : "application/json" } ,
@@ -592,101 +592,74 @@ export function SovereignDashboard({ demoMode = false }: { demoMode?: boolean }
592592 console . error ( `[scrape] callScrapeOne: Trigger FAILED provider=${ provider } status=${ triggerRes . status } error="${ triggerData . error } "` ) ;
593593 throw new Error ( triggerData . error || "Trigger failed" ) ;
594594 }
595- const { jobId } = triggerData as { jobId : string } ;
596- console . log ( `[scrape] callScrapeOne: Triggered provider=${ provider } jobId=${ jobId } ` ) ;
597-
598- // Poll until ready, failed, or 90s timeout
599- const POLL_INTERVAL_MS = 2000 ;
600- const TIMEOUT_MS = 240_000 ;
601- const deadline = Date . now ( ) + TIMEOUT_MS ;
602- let pollCount = 0 ;
603-
604- while ( Date . now ( ) < deadline ) {
605- await new Promise ( ( r ) => setTimeout ( r , POLL_INTERVAL_MS ) ) ;
606- pollCount ++ ;
607-
608- const statusRes = await fetch ( `${ BASE_PATH } /api/scrape/status/${ jobId } ` ) ;
609- const statusData = await statusRes . json ( ) as {
610- status : "pending" | "ready" | "failed" ;
611- result ?: { provider : string ; prompt : string ; answer : string ; sources : string [ ] ; createdAt : string } ;
612- error ?: string ;
613- } ;
614-
615- console . log ( `[scrape] callScrapeOne: Poll #${ pollCount } jobId=${ jobId } provider=${ provider } status=${ statusData . status } ` ) ;
616595
617- if ( statusData . status === "failed" ) {
618- console . error ( `[scrape] callScrapeOne: Job FAILED jobId=${ jobId } provider=${ provider } error="${ statusData . error } "` ) ;
619- throw new Error ( statusData . error || "Scrape job failed" ) ;
620- }
596+ const { jobId, result : jobResult } = triggerData as {
597+ jobId : string ;
598+ status : "ready" ;
599+ result : { provider : string ; prompt : string ; answer : string ; sources : string [ ] ; createdAt : string } ;
600+ } ;
621601
622- if ( statusData . status === "ready" && statusData . result ) {
623- const { answer : answerText , sources : sourceList , provider : p , prompt : pr , createdAt } = statusData . result ;
624- console . log ( `[scrape] callScrapeOne: READY provider=${ provider } jobId=${ jobId } answer.length=${ answerText . length } sources=${ sourceList . length } ` ) ;
602+ const { answer : answerText , sources : sourceList , provider : p , prompt : pr , createdAt } = jobResult ;
603+ console . log ( `[scrape] callScrapeOne: READY provider=${ provider } jobId=${ jobId } answer.length=${ answerText . length } sources=${ sourceList . length } ` ) ;
625604
626- // AI-powered analysis (accumulation: only new runs get analyzed)
627- let visibilityScore : number ;
628- let sentiment : ScrapeRun [ "sentiment" ] ;
629- let brandMentions : string [ ] ;
630- let competitorMentions : string [ ] ;
631- let aiAnalyzed = false ;
605+ // AI-powered analysis
606+ let visibilityScore : number ;
607+ let sentiment : ScrapeRun [ "sentiment" ] ;
608+ let brandMentions : string [ ] ;
609+ let competitorMentions : string [ ] ;
610+ let aiAnalyzed = false ;
632611
633- try {
634- const analysisRes = await fetch ( `${ BASE_PATH } /api/analyze-run` , {
635- method : "POST" ,
636- headers : { "Content-Type" : "application/json" } ,
637- body : JSON . stringify ( {
638- answer : answerText ,
639- brandName : state . brand . brandName ,
640- brandAliases : state . brand . brandAliases ,
641- brandWebsite : state . brand . website ,
642- competitors : state . competitors ,
643- } ) ,
644- } ) ;
645- if ( analysisRes . ok ) {
646- const analysis = await analysisRes . json ( ) as {
647- visibilityScore : number ;
648- sentiment : ScrapeRun [ "sentiment" ] ;
649- brandMentioned : boolean ;
650- brandMentions : string [ ] ;
651- competitorMentions : string [ ] ;
652- } ;
653- visibilityScore = analysis . visibilityScore ;
654- sentiment = analysis . sentiment ;
655- brandMentions = analysis . brandMentions ;
656- competitorMentions = analysis . competitorMentions ;
657- aiAnalyzed = true ;
658- console . log ( `[scrape] callScrapeOne: AI analysis done provider=${ provider } score=${ visibilityScore } sentiment=${ sentiment } ` ) ;
659- } else {
660- throw new Error ( `analyze-run HTTP ${ analysisRes . status } ` ) ;
661- }
662- } catch ( analysisErr ) {
663- console . warn ( `[scrape] callScrapeOne: AI analysis failed, falling back to heuristics. Error:` , analysisErr instanceof Error ? analysisErr . message : analysisErr ) ;
664- const brandTerms = getBrandTerms ( ) ;
665- const competitorTerms = getCompetitorTerms ( ) ;
666- visibilityScore = calcVisibilityScore ( answerText , sourceList , brandTerms ) ;
667- sentiment = detectSentiment ( answerText , brandTerms ) ;
668- brandMentions = findMentions ( answerText , brandTerms ) ;
669- competitorMentions = findMentions ( answerText , competitorTerms ) ;
670- }
671-
672- return {
673- provider : p as Provider ,
674- prompt : pr ,
612+ try {
613+ const analysisRes = await fetch ( `${ BASE_PATH } /api/analyze-run` , {
614+ method : "POST" ,
615+ headers : { "Content-Type" : "application/json" } ,
616+ body : JSON . stringify ( {
675617 answer : answerText ,
676- sources : sourceList ,
677- createdAt : createdAt || new Date ( ) . toISOString ( ) ,
678- visibilityScore,
679- sentiment,
680- brandMentions,
681- competitorMentions,
682- aiAnalyzed,
618+ brandName : state . brand . brandName ,
619+ brandAliases : state . brand . brandAliases ,
620+ brandWebsite : state . brand . website ,
621+ competitors : state . competitors ,
622+ } ) ,
623+ } ) ;
624+ if ( analysisRes . ok ) {
625+ const analysis = await analysisRes . json ( ) as {
626+ visibilityScore : number ;
627+ sentiment : ScrapeRun [ "sentiment" ] ;
628+ brandMentioned : boolean ;
629+ brandMentions : string [ ] ;
630+ competitorMentions : string [ ] ;
683631 } ;
632+ visibilityScore = analysis . visibilityScore ;
633+ sentiment = analysis . sentiment ;
634+ brandMentions = analysis . brandMentions ;
635+ competitorMentions = analysis . competitorMentions ;
636+ aiAnalyzed = true ;
637+ console . log ( `[scrape] callScrapeOne: AI analysis done provider=${ provider } score=${ visibilityScore } sentiment=${ sentiment } ` ) ;
638+ } else {
639+ throw new Error ( `analyze-run HTTP ${ analysisRes . status } ` ) ;
684640 }
685- // status === "pending" → keep polling
641+ } catch ( analysisErr ) {
642+ console . warn ( `[scrape] callScrapeOne: AI analysis failed, falling back to heuristics. Error:` , analysisErr instanceof Error ? analysisErr . message : analysisErr ) ;
643+ const brandTerms = getBrandTerms ( ) ;
644+ const competitorTerms = getCompetitorTerms ( ) ;
645+ visibilityScore = calcVisibilityScore ( answerText , sourceList , brandTerms ) ;
646+ sentiment = detectSentiment ( answerText , brandTerms ) ;
647+ brandMentions = findMentions ( answerText , brandTerms ) ;
648+ competitorMentions = findMentions ( answerText , competitorTerms ) ;
686649 }
687650
688- console . error ( `[scrape] callScrapeOne: TIMED OUT provider=${ provider } jobId=${ jobId } after ${ pollCount } polls` ) ;
689- throw new Error ( "Timed out waiting for scrape result" ) ;
651+ return {
652+ provider : p as Provider ,
653+ prompt : pr ,
654+ answer : answerText ,
655+ sources : sourceList ,
656+ createdAt : createdAt || new Date ( ) . toISOString ( ) ,
657+ visibilityScore,
658+ sentiment,
659+ brandMentions,
660+ competitorMentions,
661+ aiAnalyzed,
662+ } ;
690663 } catch ( err ) {
691664 console . error ( `[scrape] callScrapeOne: CAUGHT ERROR provider=${ provider } ` , err instanceof Error ? err . message : err ) ;
692665 return null ;
0 commit comments