@rankcli/agent-runtime 0.0.23 → 0.0.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +53 -9
- package/dist/index.d.ts +53 -9
- package/dist/index.js +126 -90
- package/dist/index.mjs +123 -90
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -1631,6 +1631,39 @@ declare function runKeywordResearch(options: KeywordResearchOptions): Promise<Ke
|
|
|
1631
1631
|
declare function formatKeywordReport(result: KeywordResearchResult): string;
|
|
1632
1632
|
declare function extractSeedKeywords(url: string): Promise<string[]>;
|
|
1633
1633
|
|
|
1634
|
+
interface KeywordLLM {
|
|
1635
|
+
/**
|
|
1636
|
+
* A reply to one prompt that asks for a JSON object. `best` asks for the provider's stronger
|
|
1637
|
+
* model where it has one. Replies are parsed with parseJsonReply, never trusted to be clean.
|
|
1638
|
+
*/
|
|
1639
|
+
complete(prompt: string, opts?: {
|
|
1640
|
+
system?: string;
|
|
1641
|
+
maxTokens?: number;
|
|
1642
|
+
temperature?: number;
|
|
1643
|
+
best?: boolean;
|
|
1644
|
+
}): Promise<string>;
|
|
1645
|
+
/** Embeddings for keyword clustering. Without it, clustering falls back to term vectors. */
|
|
1646
|
+
embed?(texts: string[]): Promise<number[][]>;
|
|
1647
|
+
/** Most page text worth sending in one prompt: small-context models get less. */
|
|
1648
|
+
maxContentChars?: number;
|
|
1649
|
+
/** How many tool ideas to judge (two calls each): slow local models get fewer. */
|
|
1650
|
+
maxToolIdeas?: number;
|
|
1651
|
+
}
|
|
1652
|
+
/** The engine's original backend: gpt-4o-mini, gpt-4o for `best`, JSON mode, text-embedding-3-small. */
|
|
1653
|
+
declare function openAIKeywordLLM(apiKey: string, opts?: {
|
|
1654
|
+
model?: string;
|
|
1655
|
+
}): KeywordLLM;
|
|
1656
|
+
/** The model to use: an explicit one, else OpenAI when there's a key, else none. */
|
|
1657
|
+
declare function keywordLLM(options: {
|
|
1658
|
+
llm?: KeywordLLM;
|
|
1659
|
+
openaiApiKey?: string;
|
|
1660
|
+
}): KeywordLLM | undefined;
|
|
1661
|
+
/**
|
|
1662
|
+
* The JSON object in a model reply. Local and small models wrap it in ``` fences, prepend prose
|
|
1663
|
+
* or emit <think> blocks; this takes the outermost {...} and tolerates trailing commas.
|
|
1664
|
+
*/
|
|
1665
|
+
declare function parseJsonReply(text: string): Record<string, any> | null;
|
|
1666
|
+
|
|
1634
1667
|
/**
|
|
1635
1668
|
* Site Crawler for Keyword Research
|
|
1636
1669
|
*
|
|
@@ -1687,7 +1720,7 @@ declare function extractKeyPhrases(crawlResult: SiteCrawlResult): string[];
|
|
|
1687
1720
|
/**
|
|
1688
1721
|
* AI-Powered Site Summarizer
|
|
1689
1722
|
*
|
|
1690
|
-
* Uses
|
|
1723
|
+
* Uses the configured AI provider (llm.ts) to understand what a site does from crawled content.
|
|
1691
1724
|
* Extracts: product, audience, features, industry, positioning.
|
|
1692
1725
|
*/
|
|
1693
1726
|
|
|
@@ -1724,7 +1757,10 @@ interface SiteSummary {
|
|
|
1724
1757
|
uncertainties: string[];
|
|
1725
1758
|
}
|
|
1726
1759
|
interface SummarizerOptions {
|
|
1727
|
-
|
|
1760
|
+
/** The model to ask (see llm.ts); or an OpenAI key, as before. */
|
|
1761
|
+
llm?: KeywordLLM;
|
|
1762
|
+
openaiApiKey?: string;
|
|
1763
|
+
/** OpenAI model override, with openaiApiKey. */
|
|
1728
1764
|
model?: string;
|
|
1729
1765
|
maxTokens?: number;
|
|
1730
1766
|
}
|
|
@@ -1831,10 +1867,12 @@ declare function calculateBM25(documents: string[], query: string, k1?: number,
|
|
|
1831
1867
|
docIndex: number;
|
|
1832
1868
|
score: number;
|
|
1833
1869
|
}>;
|
|
1870
|
+
/** An OpenAI client, or any function returning one embedding per text (llm.ts). */
|
|
1871
|
+
type Embedder = OpenAI | ((texts: string[]) => Promise<number[][]>);
|
|
1834
1872
|
/**
|
|
1835
1873
|
* Cluster keywords using embeddings
|
|
1836
1874
|
*/
|
|
1837
|
-
declare function clusterKeywordsByEmbedding(keywords: string[], openai:
|
|
1875
|
+
declare function clusterKeywordsByEmbedding(keywords: string[], openai: Embedder, numClusters?: number): Promise<KeywordCluster[]>;
|
|
1838
1876
|
/**
|
|
1839
1877
|
* Extract entity-like phrases (capitalized sequences, proper nouns)
|
|
1840
1878
|
*/
|
|
@@ -1848,6 +1886,8 @@ declare function extractTopics(documents: string[], numTopics?: number): TopicMo
|
|
|
1848
1886
|
* Run full NLP analysis on content
|
|
1849
1887
|
*/
|
|
1850
1888
|
declare function runNLPAnalysis(content: string | string[], options?: {
|
|
1889
|
+
/** Embeddings come from here when it has them (see llm.ts); or an OpenAI key, as before. */
|
|
1890
|
+
llm?: KeywordLLM;
|
|
1851
1891
|
openaiApiKey?: string;
|
|
1852
1892
|
numClusters?: number;
|
|
1853
1893
|
numTopics?: number;
|
|
@@ -1871,6 +1911,8 @@ type PlanTier = 'free' | 'solo' | 'pro' | 'agency';
|
|
|
1871
1911
|
interface AIKeywordResearchOptions {
|
|
1872
1912
|
url: string;
|
|
1873
1913
|
tier: PlanTier;
|
|
1914
|
+
/** The model to ask for paid-tier research (see llm.ts); or an OpenAI key, as before. */
|
|
1915
|
+
llm?: KeywordLLM;
|
|
1874
1916
|
openaiApiKey?: string;
|
|
1875
1917
|
maxPages?: number;
|
|
1876
1918
|
userContext?: {
|
|
@@ -2066,6 +2108,8 @@ interface WizardResult {
|
|
|
2066
2108
|
* Start a new wizard session
|
|
2067
2109
|
*/
|
|
2068
2110
|
declare function startWizardSession(url: string, options?: {
|
|
2111
|
+
/** The model for the site summary (see llm.ts); or an OpenAI key, as before. */
|
|
2112
|
+
llm?: KeywordLLM;
|
|
2069
2113
|
openaiApiKey?: string;
|
|
2070
2114
|
existingResponses?: WizardResponse[];
|
|
2071
2115
|
}): Promise<WizardSession>;
|
|
@@ -2370,7 +2414,9 @@ interface EnhancedToolIdea {
|
|
|
2370
2414
|
priority: number;
|
|
2371
2415
|
}
|
|
2372
2416
|
interface LLMJudgeOptions {
|
|
2373
|
-
|
|
2417
|
+
/** The model to ask (see llm.ts); or an OpenAI key, as before. */
|
|
2418
|
+
llm?: KeywordLLM;
|
|
2419
|
+
openaiApiKey?: string;
|
|
2374
2420
|
minFeasibilityScore?: number;
|
|
2375
2421
|
minSpecificityScore?: number;
|
|
2376
2422
|
minProductFitScore?: number;
|
|
@@ -4691,11 +4737,9 @@ declare class RankTracker {
|
|
|
4691
4737
|
*/
|
|
4692
4738
|
private saveRanking;
|
|
4693
4739
|
/**
|
|
4694
|
-
*
|
|
4695
|
-
* Note: Full history requires keyword_ranking_history table with keyword_ranking_id
|
|
4696
|
-
* For now, returns current state as single history entry
|
|
4740
|
+
* Ranking history for a keyword: one entry per check in the last `days`, newest first.
|
|
4697
4741
|
*/
|
|
4698
|
-
getHistory(keywordId: string,
|
|
4742
|
+
getHistory(keywordId: string, days?: number): Promise<RankHistory[]>;
|
|
4699
4743
|
/**
|
|
4700
4744
|
* Get keyword trends for a project
|
|
4701
4745
|
*/
|
|
@@ -5498,4 +5542,4 @@ declare namespace index {
|
|
|
5498
5542
|
export { type index_Framework as Framework, type index_FrameworkCheck as FrameworkCheck, type index_FrameworkInfo as FrameworkInfo, type index_FrameworkRecipe as FrameworkRecipe, index_SUPPORTED_FRAMEWORKS as SUPPORTED_FRAMEWORKS, index_detectFramework as detectFramework, index_detectFromHeaders as detectFromHeaders, index_detectFromHtml as detectFromHtml, index_detectFromPackageJson as detectFromPackageJson, index_embedRecipe as embedRecipe, index_getFrameworkChecks as getFrameworkChecks, index_getFrameworkDisplayName as getFrameworkDisplayName, index_getFrameworkIssues as getFrameworkIssues, index_getFrameworkSuggestions as getFrameworkSuggestions, index_getFrameworkSummary as getFrameworkSummary, index_getMigrationPaths as getMigrationPaths, index_getPerformanceTips as getPerformanceTips, index_getSeoChallenges as getSeoChallenges, index_getTotalFrameworkChecks as getTotalFrameworkChecks, index_loadRecipe as loadRecipe };
|
|
5499
5543
|
}
|
|
5500
5544
|
|
|
5501
|
-
export { type AIContentConfig, type AIKeywordResearchOptions, type AgentDefinition, type AgentTool, type AlertChannel, type AlertConfig, type AlertMessage, type AlertResult, type AlertSeverity, type AlertType, type AuditAlertPayload, type AuditIssue, type AuditIssueData, type AuditOptions, type AuditPRResult, type AuditReport, type AuditReportData, type AuditRunnerOptions, type AuditRunnerResult, type AuditSchedule, type BlogPostConfig, type BrandConfig, type CIAction, type CIKeywordOptions, type CIKeywordResult, COMMIT_TYPES, type ChangelogConfig, type ChangelogResult, type CitationCheckOptions, type CommitConfig, type ComparisonConfig, type ComparisonRow, type CompetitiveInsight, type CompetitiveSearchOptions, type CompetitiveSearchResult, type CompetitorComparison, type CompetitorKeywordResult, type CompetitorOverlap, type CompetitorTool, type CompetitorUrl, type ContentElements, type ContentGeneratorOptions, type ContentRecommendation, type ConventionalCommit, type CrawlResult, type CrawlStats, type CrawledPage, type CreateAuditPROptions, type CreatePROptions, type DataForSEOCredentials, type DiscoveredPage, type EnhancedToolIdea, type ExecOptions, type ExecuteOptions, type ExecutionResult, type FAQItem, type FAQSchemaOptions, type FeaturedSnippetAnalysis, type Fix, type FixFile, type FixGeneratorOptions, type FixResult, type FrameworkInfo$1 as FrameworkInfo, type FreeKeywordResult, type FreeToolIdea, type GA4Config, type GEOAlert, type GEOAlertType, type GEOHistory, type GEOHistoryOptions, type GEOQuery, type GEOReport, type GEOResult, type GEOTrend, type GSCConfig, type GSCCredentials, type GSCPerformanceData, type GSCQueryData, type GSCQueryResult, type GeneratedCode, type GeneratedContent, type GeneratedFix, type HeadingStructure, type HeadlineAnalysis, type HealthScore, ISSUE_DEFINITIONS, type ImageInfo$2 as ImageInfo, type InjectionResult, type IntentAnalysis, type IssueCategory, type IssueDefinition, type IssueSeverity, type KeyFact, type KeyFactsOptions, type KeywordAction, type KeywordCluster, type KeywordData, type KeywordDensityAnalysis, type KeywordOpportunity, type KeywordRecommendation, type KeywordResearchOptions, type KeywordResearchResult, type KeywordStats, type KeywordTopic, type KeywordTrend, type LLMCitationResult, type LLMJudgeOptions, type LLMProvider, LOCATION_CODES, type LSIKeyword, type LinkInfo$1 as LinkInfo, type Mention, type MentionType, type MetaData, type MetaFixOptions, type NGram, type NLPAnalysisResult, OG_IMAGE_SPECS, type OptimizeOptions, type PRConfig, type PRDescription, PRIORITY_WEIGHTS, type PRResult, type PageAudit, type PageData, type PageMeta, type PaidKeywordResult, type ParsedResponse, type PlanTier, type ProviderStats, type QuickWin, type RankCheckOptions, type RankCheckResult, type RankHistory, RankTracker, type RankingResult, type RankingTierLimits, type ReadabilityResult, type ReadmeConfig, type ReadmeResult, type ReportBranding, type ReportConfig, type ReportData, type ReportPageData, type RouteInfo, type SEOAnalysisResult, type SEOFixCommit, type SEOFixSummary, type SEOIssue, type SEORecommendation, type SEOScore, SEO_SCOPES, SITE_PROFILE_QUESTIONS, type ScheduledAuditConfig, type ScheduledAuditResult, type SchemaData, Schemas, type SearchIntent, type Sentiment, type SerpApiConfig, SerpClient, type SerpClientConfig, type SerpFeature, type SiteCrawlResult, type SiteProfile, type SiteSummary, type SnippetRecommendation, type SnippetType, type SocialMetaConfig, type SocialMetaFix, type SummarizerOptions, type TFIDFResult, TIER_LIMITS, type ToolFeasibilityScore, type ToolFunction, type ToolResult, type TopicClusterResult, type TopicModel, type TrackedKeyword, type TrackerConfig, type TrackingOptions, type TrendDirection, type UncertaintyAssessment, type WizardQuestion, type WizardResponse, type WizardResult, type WizardSession, type WorkflowConfig, addTrackingResult, analyzeAnchorText, analyzeCanonicalAdvanced, analyzeClientRendering, analyzeContentFreshness, analyzeDOMStructure, analyzeFeaturedSnippetPotential, analyzeHeadings, analyzeHeadline, analyzeHreflang, analyzeImages$1 as analyzeImages, analyzeKeywordDensity, analyzeKeywords, analyzeLinks, analyzeLocalSEO, analyzeMobile, analyzeModernImages, analyzeOnPage, analyzePagination, analyzePerformance, analyzeReadability, analyzeRedirectChain, analyzeRedirects, analyzeResponsiveImages, analyzeSERPPreview, analyzeSecurity, analyzeSecurityHeaders$1 as analyzeSecurityHeaders, analyzeSocialMeta, analyzeStructuredData$1 as analyzeStructuredData, analyzeTrackerBloat, analyzeUrl, analyzeUrlSafety, index$1 as analyzers, applyFixes, buildGSCApiRequest, buildGSCRequest, calculateAIVisibilityScore, calculateBM25, calculateTFIDF$1 as calculateKeywordTFIDF, calculateNextRun, calculateTFIDF, checkAIBotBlocking, checkAMP, checkAdsTxt, checkAppleTouchIcon, checkBalance, checkCertificate, checkDMARC, checkGitHubCLI, checkInternalRedirects, checkJSRenderingRatio, checkLLMCitations, checkLlmsTxt, checkMobileResources, checkPlaintextEmails, checkRedirects, checkRobots, checkRobotsTxt, checkSPF, checkSitemap, classifyIntent, classifyIntents, clusterKeywordsByEmbedding, compareCompetitorVisibility, comparePeriods, completeWizard, crawlSite, crawlUrl, createAuditPR, createFallbackSummary, createGEOHistory, createPullRequest, createSEOCommit, createSEOCommits, detectDuplicates, detectFramework$1 as detectFramework, detectGitHubPages, detectMentions, detectSoft404, detectTechnologies, detectVisibilityChanges, discoverCompetitorKeywords, discoverPagesFromLinks, discoverRoutesFromRepo, enhanceSummaryWithCompetitors, enhanceToolIdea, enrichKeywordsWithEstimates, ensureGitRepo, evaluateAndEnhanceToolIdeas, evaluateToolFeasibility, executeAgent, extractContentHash, extractEntityPhrases, extractImages, extractKeyPhrases, extractLinks, extractMeta, extractNgrams, extractSchema, extractSeedKeywords, extractTopics, fetchCoreWebVitals, findAlmostPage1Keywords, findCTROpportunities, findHtmlEntry, findLSIKeywords, findPageFiles, findTopPerformers, formatAlertMessage, formatCIResult, formatCompetitorReport, formatConventionalCommit, formatFeaturedSnippetReport, formatHeadlineReport, formatIntentReport, formatKeywordDensityReport, formatKeywordReport, formatPRBody, formatPRTitle, formatReadabilityReport, formatReport, formatSEOCommitMessage, formatTopicReport, formatWizardProgress, index as frameworks, generateAICitableContent, generateAllFixes, generateAngularSEOService, generateAspNetCoreSEO, generateAstroBaseHead, generateAstroMeta, generateBlogPost, generateBranchName, generateChangelog, generateCommitSummary, generateComparisonTable, generateCompleteSocialMetaSetup, generateDjangoSEOHelper, generateDuplicateIssues, generateEleventySEO, generateFAQSchema, generateFixes, generateGA4EnvTemplate, generateGA4ReactComponent, generateGA4Script, generateGA4ViteScript, generateGEOReport, generateGSCVerificationTag, generateGitHubActionSetup, generateGoSEO, generateHTMLReport, generateHTMLSocialMeta, generateHTMXSEO, generateHeadlineVariations, generateHugoSEO, generateJekyllSEO, generateJsonReport, generateKeyFacts, generateLaravelSEOHelper, generateMarkdownReport, generateNextAppMetadata, generateNextJsAppRouterMetadata, generateNextJsPagesRouterHead, generateNextPagesHead, generateNuxtSEOHead, generatePDFReport, generatePRDescription, generatePhoenixSEO, generateRailsSEOHelper, generateReactHelmetSocialMeta, generateReactSEOHead, generateRecommendationQueries, generateRemixMeta, generateRemixSEO, generateSecretsDoc, generateSocialMetaFix, generateSpringBootSEO, generateSvelteKitMeta, generateSvelteKitSEOHead, generateUncertaintyQuestions, generateVueSEOHead, generateWizardQuestions, generateWorkflow, getAIVisibilitySummary, getAutocompleteSuggestions, getDateRange, getExpandedSuggestions, getFrameworkSpecificFix, getFrameworkSpecificFixExtended, getGSCSetupInstructions, getGitUser, getKeywordData, getKeywordSuggestions, getMaxKdThreshold, getNextQuestion, getRelatedKeywords, getVisibilityTrend, groupKeywordsByTopic, identifyQuickWins, injectGA4, injectGSCVerification, interpolatePrompt, isGitRepo, listFiles, loadAgent, loadAgentByName, mergePages, optimizeForAI, optimizeReadme, parseGEOResponse, parseGSCResponse, parseSitemap, prioritizeKeywords, processWizardResponse, readFile, routesToUrls, runAIKeywordResearch, runAIReadinessChecks, runAdditionalChecks, runAuditWithFixes, runCIKeywordResearch, runCrawlabilityChecks, runDirectAnalysis, runFullAudit, runKeywordResearch, runNLPAnalysis, runScheduledAudit, scoreContentSEO, scoreMention, searchCompetitors, searchFormatConverters, searchHackerNews, sendAlert, sendAlerts, sendDiscordAlert, sendSlackAlert, shouldRunAudit, shouldSendAlert, startWizardSession, suggestSchemaTypes, summarizeSite, tokenize, tools, trackLLMVisibility, transformGSCData, urlSafetyDatabase, wizardResponsesToContext, writeFile, writeGitHubActionFiles };
|
|
5545
|
+
export { type AIContentConfig, type AIKeywordResearchOptions, type AgentDefinition, type AgentTool, type AlertChannel, type AlertConfig, type AlertMessage, type AlertResult, type AlertSeverity, type AlertType, type AuditAlertPayload, type AuditIssue, type AuditIssueData, type AuditOptions, type AuditPRResult, type AuditReport, type AuditReportData, type AuditRunnerOptions, type AuditRunnerResult, type AuditSchedule, type BlogPostConfig, type BrandConfig, type CIAction, type CIKeywordOptions, type CIKeywordResult, COMMIT_TYPES, type ChangelogConfig, type ChangelogResult, type CitationCheckOptions, type CommitConfig, type ComparisonConfig, type ComparisonRow, type CompetitiveInsight, type CompetitiveSearchOptions, type CompetitiveSearchResult, type CompetitorComparison, type CompetitorKeywordResult, type CompetitorOverlap, type CompetitorTool, type CompetitorUrl, type ContentElements, type ContentGeneratorOptions, type ContentRecommendation, type ConventionalCommit, type CrawlResult, type CrawlStats, type CrawledPage, type CreateAuditPROptions, type CreatePROptions, type DataForSEOCredentials, type DiscoveredPage, type EnhancedToolIdea, type ExecOptions, type ExecuteOptions, type ExecutionResult, type FAQItem, type FAQSchemaOptions, type FeaturedSnippetAnalysis, type Fix, type FixFile, type FixGeneratorOptions, type FixResult, type FrameworkInfo$1 as FrameworkInfo, type FreeKeywordResult, type FreeToolIdea, type GA4Config, type GEOAlert, type GEOAlertType, type GEOHistory, type GEOHistoryOptions, type GEOQuery, type GEOReport, type GEOResult, type GEOTrend, type GSCConfig, type GSCCredentials, type GSCPerformanceData, type GSCQueryData, type GSCQueryResult, type GeneratedCode, type GeneratedContent, type GeneratedFix, type HeadingStructure, type HeadlineAnalysis, type HealthScore, ISSUE_DEFINITIONS, type ImageInfo$2 as ImageInfo, type InjectionResult, type IntentAnalysis, type IssueCategory, type IssueDefinition, type IssueSeverity, type KeyFact, type KeyFactsOptions, type KeywordAction, type KeywordCluster, type KeywordData, type KeywordDensityAnalysis, type KeywordLLM, type KeywordOpportunity, type KeywordRecommendation, type KeywordResearchOptions, type KeywordResearchResult, type KeywordStats, type KeywordTopic, type KeywordTrend, type LLMCitationResult, type LLMJudgeOptions, type LLMProvider, LOCATION_CODES, type LSIKeyword, type LinkInfo$1 as LinkInfo, type Mention, type MentionType, type MetaData, type MetaFixOptions, type NGram, type NLPAnalysisResult, OG_IMAGE_SPECS, type OptimizeOptions, type PRConfig, type PRDescription, PRIORITY_WEIGHTS, type PRResult, type PageAudit, type PageData, type PageMeta, type PaidKeywordResult, type ParsedResponse, type PlanTier, type ProviderStats, type QuickWin, type RankCheckOptions, type RankCheckResult, type RankHistory, RankTracker, type RankingResult, type RankingTierLimits, type ReadabilityResult, type ReadmeConfig, type ReadmeResult, type ReportBranding, type ReportConfig, type ReportData, type ReportPageData, type RouteInfo, type SEOAnalysisResult, type SEOFixCommit, type SEOFixSummary, type SEOIssue, type SEORecommendation, type SEOScore, SEO_SCOPES, SITE_PROFILE_QUESTIONS, type ScheduledAuditConfig, type ScheduledAuditResult, type SchemaData, Schemas, type SearchIntent, type Sentiment, type SerpApiConfig, SerpClient, type SerpClientConfig, type SerpFeature, type SiteCrawlResult, type SiteProfile, type SiteSummary, type SnippetRecommendation, type SnippetType, type SocialMetaConfig, type SocialMetaFix, type SummarizerOptions, type TFIDFResult, TIER_LIMITS, type ToolFeasibilityScore, type ToolFunction, type ToolResult, type TopicClusterResult, type TopicModel, type TrackedKeyword, type TrackerConfig, type TrackingOptions, type TrendDirection, type UncertaintyAssessment, type WizardQuestion, type WizardResponse, type WizardResult, type WizardSession, type WorkflowConfig, addTrackingResult, analyzeAnchorText, analyzeCanonicalAdvanced, analyzeClientRendering, analyzeContentFreshness, analyzeDOMStructure, analyzeFeaturedSnippetPotential, analyzeHeadings, analyzeHeadline, analyzeHreflang, analyzeImages$1 as analyzeImages, analyzeKeywordDensity, analyzeKeywords, analyzeLinks, analyzeLocalSEO, analyzeMobile, analyzeModernImages, analyzeOnPage, analyzePagination, analyzePerformance, analyzeReadability, analyzeRedirectChain, analyzeRedirects, analyzeResponsiveImages, analyzeSERPPreview, analyzeSecurity, analyzeSecurityHeaders$1 as analyzeSecurityHeaders, analyzeSocialMeta, analyzeStructuredData$1 as analyzeStructuredData, analyzeTrackerBloat, analyzeUrl, analyzeUrlSafety, index$1 as analyzers, applyFixes, buildGSCApiRequest, buildGSCRequest, calculateAIVisibilityScore, calculateBM25, calculateTFIDF$1 as calculateKeywordTFIDF, calculateNextRun, calculateTFIDF, checkAIBotBlocking, checkAMP, checkAdsTxt, checkAppleTouchIcon, checkBalance, checkCertificate, checkDMARC, checkGitHubCLI, checkInternalRedirects, checkJSRenderingRatio, checkLLMCitations, checkLlmsTxt, checkMobileResources, checkPlaintextEmails, checkRedirects, checkRobots, checkRobotsTxt, checkSPF, checkSitemap, classifyIntent, classifyIntents, clusterKeywordsByEmbedding, compareCompetitorVisibility, comparePeriods, completeWizard, crawlSite, crawlUrl, createAuditPR, createFallbackSummary, createGEOHistory, createPullRequest, createSEOCommit, createSEOCommits, detectDuplicates, detectFramework$1 as detectFramework, detectGitHubPages, detectMentions, detectSoft404, detectTechnologies, detectVisibilityChanges, discoverCompetitorKeywords, discoverPagesFromLinks, discoverRoutesFromRepo, enhanceSummaryWithCompetitors, enhanceToolIdea, enrichKeywordsWithEstimates, ensureGitRepo, evaluateAndEnhanceToolIdeas, evaluateToolFeasibility, executeAgent, extractContentHash, extractEntityPhrases, extractImages, extractKeyPhrases, extractLinks, extractMeta, extractNgrams, extractSchema, extractSeedKeywords, extractTopics, fetchCoreWebVitals, findAlmostPage1Keywords, findCTROpportunities, findHtmlEntry, findLSIKeywords, findPageFiles, findTopPerformers, formatAlertMessage, formatCIResult, formatCompetitorReport, formatConventionalCommit, formatFeaturedSnippetReport, formatHeadlineReport, formatIntentReport, formatKeywordDensityReport, formatKeywordReport, formatPRBody, formatPRTitle, formatReadabilityReport, formatReport, formatSEOCommitMessage, formatTopicReport, formatWizardProgress, index as frameworks, generateAICitableContent, generateAllFixes, generateAngularSEOService, generateAspNetCoreSEO, generateAstroBaseHead, generateAstroMeta, generateBlogPost, generateBranchName, generateChangelog, generateCommitSummary, generateComparisonTable, generateCompleteSocialMetaSetup, generateDjangoSEOHelper, generateDuplicateIssues, generateEleventySEO, generateFAQSchema, generateFixes, generateGA4EnvTemplate, generateGA4ReactComponent, generateGA4Script, generateGA4ViteScript, generateGEOReport, generateGSCVerificationTag, generateGitHubActionSetup, generateGoSEO, generateHTMLReport, generateHTMLSocialMeta, generateHTMXSEO, generateHeadlineVariations, generateHugoSEO, generateJekyllSEO, generateJsonReport, generateKeyFacts, generateLaravelSEOHelper, generateMarkdownReport, generateNextAppMetadata, generateNextJsAppRouterMetadata, generateNextJsPagesRouterHead, generateNextPagesHead, generateNuxtSEOHead, generatePDFReport, generatePRDescription, generatePhoenixSEO, generateRailsSEOHelper, generateReactHelmetSocialMeta, generateReactSEOHead, generateRecommendationQueries, generateRemixMeta, generateRemixSEO, generateSecretsDoc, generateSocialMetaFix, generateSpringBootSEO, generateSvelteKitMeta, generateSvelteKitSEOHead, generateUncertaintyQuestions, generateVueSEOHead, generateWizardQuestions, generateWorkflow, getAIVisibilitySummary, getAutocompleteSuggestions, getDateRange, getExpandedSuggestions, getFrameworkSpecificFix, getFrameworkSpecificFixExtended, getGSCSetupInstructions, getGitUser, getKeywordData, getKeywordSuggestions, getMaxKdThreshold, getNextQuestion, getRelatedKeywords, getVisibilityTrend, groupKeywordsByTopic, identifyQuickWins, injectGA4, injectGSCVerification, interpolatePrompt, isGitRepo, keywordLLM, listFiles, loadAgent, loadAgentByName, mergePages, openAIKeywordLLM, optimizeForAI, optimizeReadme, parseGEOResponse, parseGSCResponse, parseJsonReply, parseSitemap, prioritizeKeywords, processWizardResponse, readFile, routesToUrls, runAIKeywordResearch, runAIReadinessChecks, runAdditionalChecks, runAuditWithFixes, runCIKeywordResearch, runCrawlabilityChecks, runDirectAnalysis, runFullAudit, runKeywordResearch, runNLPAnalysis, runScheduledAudit, scoreContentSEO, scoreMention, searchCompetitors, searchFormatConverters, searchHackerNews, sendAlert, sendAlerts, sendDiscordAlert, sendSlackAlert, shouldRunAudit, shouldSendAlert, startWizardSession, suggestSchemaTypes, summarizeSite, tokenize, tools, trackLLMVisibility, transformGSCData, urlSafetyDatabase, wizardResponsesToContext, writeFile, writeGitHubActionFiles };
|
package/dist/index.d.ts
CHANGED
|
@@ -1631,6 +1631,39 @@ declare function runKeywordResearch(options: KeywordResearchOptions): Promise<Ke
|
|
|
1631
1631
|
declare function formatKeywordReport(result: KeywordResearchResult): string;
|
|
1632
1632
|
declare function extractSeedKeywords(url: string): Promise<string[]>;
|
|
1633
1633
|
|
|
1634
|
+
interface KeywordLLM {
|
|
1635
|
+
/**
|
|
1636
|
+
* A reply to one prompt that asks for a JSON object. `best` asks for the provider's stronger
|
|
1637
|
+
* model where it has one. Replies are parsed with parseJsonReply, never trusted to be clean.
|
|
1638
|
+
*/
|
|
1639
|
+
complete(prompt: string, opts?: {
|
|
1640
|
+
system?: string;
|
|
1641
|
+
maxTokens?: number;
|
|
1642
|
+
temperature?: number;
|
|
1643
|
+
best?: boolean;
|
|
1644
|
+
}): Promise<string>;
|
|
1645
|
+
/** Embeddings for keyword clustering. Without it, clustering falls back to term vectors. */
|
|
1646
|
+
embed?(texts: string[]): Promise<number[][]>;
|
|
1647
|
+
/** Most page text worth sending in one prompt: small-context models get less. */
|
|
1648
|
+
maxContentChars?: number;
|
|
1649
|
+
/** How many tool ideas to judge (two calls each): slow local models get fewer. */
|
|
1650
|
+
maxToolIdeas?: number;
|
|
1651
|
+
}
|
|
1652
|
+
/** The engine's original backend: gpt-4o-mini, gpt-4o for `best`, JSON mode, text-embedding-3-small. */
|
|
1653
|
+
declare function openAIKeywordLLM(apiKey: string, opts?: {
|
|
1654
|
+
model?: string;
|
|
1655
|
+
}): KeywordLLM;
|
|
1656
|
+
/** The model to use: an explicit one, else OpenAI when there's a key, else none. */
|
|
1657
|
+
declare function keywordLLM(options: {
|
|
1658
|
+
llm?: KeywordLLM;
|
|
1659
|
+
openaiApiKey?: string;
|
|
1660
|
+
}): KeywordLLM | undefined;
|
|
1661
|
+
/**
|
|
1662
|
+
* The JSON object in a model reply. Local and small models wrap it in ``` fences, prepend prose
|
|
1663
|
+
* or emit <think> blocks; this takes the outermost {...} and tolerates trailing commas.
|
|
1664
|
+
*/
|
|
1665
|
+
declare function parseJsonReply(text: string): Record<string, any> | null;
|
|
1666
|
+
|
|
1634
1667
|
/**
|
|
1635
1668
|
* Site Crawler for Keyword Research
|
|
1636
1669
|
*
|
|
@@ -1687,7 +1720,7 @@ declare function extractKeyPhrases(crawlResult: SiteCrawlResult): string[];
|
|
|
1687
1720
|
/**
|
|
1688
1721
|
* AI-Powered Site Summarizer
|
|
1689
1722
|
*
|
|
1690
|
-
* Uses
|
|
1723
|
+
* Uses the configured AI provider (llm.ts) to understand what a site does from crawled content.
|
|
1691
1724
|
* Extracts: product, audience, features, industry, positioning.
|
|
1692
1725
|
*/
|
|
1693
1726
|
|
|
@@ -1724,7 +1757,10 @@ interface SiteSummary {
|
|
|
1724
1757
|
uncertainties: string[];
|
|
1725
1758
|
}
|
|
1726
1759
|
interface SummarizerOptions {
|
|
1727
|
-
|
|
1760
|
+
/** The model to ask (see llm.ts); or an OpenAI key, as before. */
|
|
1761
|
+
llm?: KeywordLLM;
|
|
1762
|
+
openaiApiKey?: string;
|
|
1763
|
+
/** OpenAI model override, with openaiApiKey. */
|
|
1728
1764
|
model?: string;
|
|
1729
1765
|
maxTokens?: number;
|
|
1730
1766
|
}
|
|
@@ -1831,10 +1867,12 @@ declare function calculateBM25(documents: string[], query: string, k1?: number,
|
|
|
1831
1867
|
docIndex: number;
|
|
1832
1868
|
score: number;
|
|
1833
1869
|
}>;
|
|
1870
|
+
/** An OpenAI client, or any function returning one embedding per text (llm.ts). */
|
|
1871
|
+
type Embedder = OpenAI | ((texts: string[]) => Promise<number[][]>);
|
|
1834
1872
|
/**
|
|
1835
1873
|
* Cluster keywords using embeddings
|
|
1836
1874
|
*/
|
|
1837
|
-
declare function clusterKeywordsByEmbedding(keywords: string[], openai:
|
|
1875
|
+
declare function clusterKeywordsByEmbedding(keywords: string[], openai: Embedder, numClusters?: number): Promise<KeywordCluster[]>;
|
|
1838
1876
|
/**
|
|
1839
1877
|
* Extract entity-like phrases (capitalized sequences, proper nouns)
|
|
1840
1878
|
*/
|
|
@@ -1848,6 +1886,8 @@ declare function extractTopics(documents: string[], numTopics?: number): TopicMo
|
|
|
1848
1886
|
* Run full NLP analysis on content
|
|
1849
1887
|
*/
|
|
1850
1888
|
declare function runNLPAnalysis(content: string | string[], options?: {
|
|
1889
|
+
/** Embeddings come from here when it has them (see llm.ts); or an OpenAI key, as before. */
|
|
1890
|
+
llm?: KeywordLLM;
|
|
1851
1891
|
openaiApiKey?: string;
|
|
1852
1892
|
numClusters?: number;
|
|
1853
1893
|
numTopics?: number;
|
|
@@ -1871,6 +1911,8 @@ type PlanTier = 'free' | 'solo' | 'pro' | 'agency';
|
|
|
1871
1911
|
interface AIKeywordResearchOptions {
|
|
1872
1912
|
url: string;
|
|
1873
1913
|
tier: PlanTier;
|
|
1914
|
+
/** The model to ask for paid-tier research (see llm.ts); or an OpenAI key, as before. */
|
|
1915
|
+
llm?: KeywordLLM;
|
|
1874
1916
|
openaiApiKey?: string;
|
|
1875
1917
|
maxPages?: number;
|
|
1876
1918
|
userContext?: {
|
|
@@ -2066,6 +2108,8 @@ interface WizardResult {
|
|
|
2066
2108
|
* Start a new wizard session
|
|
2067
2109
|
*/
|
|
2068
2110
|
declare function startWizardSession(url: string, options?: {
|
|
2111
|
+
/** The model for the site summary (see llm.ts); or an OpenAI key, as before. */
|
|
2112
|
+
llm?: KeywordLLM;
|
|
2069
2113
|
openaiApiKey?: string;
|
|
2070
2114
|
existingResponses?: WizardResponse[];
|
|
2071
2115
|
}): Promise<WizardSession>;
|
|
@@ -2370,7 +2414,9 @@ interface EnhancedToolIdea {
|
|
|
2370
2414
|
priority: number;
|
|
2371
2415
|
}
|
|
2372
2416
|
interface LLMJudgeOptions {
|
|
2373
|
-
|
|
2417
|
+
/** The model to ask (see llm.ts); or an OpenAI key, as before. */
|
|
2418
|
+
llm?: KeywordLLM;
|
|
2419
|
+
openaiApiKey?: string;
|
|
2374
2420
|
minFeasibilityScore?: number;
|
|
2375
2421
|
minSpecificityScore?: number;
|
|
2376
2422
|
minProductFitScore?: number;
|
|
@@ -4691,11 +4737,9 @@ declare class RankTracker {
|
|
|
4691
4737
|
*/
|
|
4692
4738
|
private saveRanking;
|
|
4693
4739
|
/**
|
|
4694
|
-
*
|
|
4695
|
-
* Note: Full history requires keyword_ranking_history table with keyword_ranking_id
|
|
4696
|
-
* For now, returns current state as single history entry
|
|
4740
|
+
* Ranking history for a keyword: one entry per check in the last `days`, newest first.
|
|
4697
4741
|
*/
|
|
4698
|
-
getHistory(keywordId: string,
|
|
4742
|
+
getHistory(keywordId: string, days?: number): Promise<RankHistory[]>;
|
|
4699
4743
|
/**
|
|
4700
4744
|
* Get keyword trends for a project
|
|
4701
4745
|
*/
|
|
@@ -5498,4 +5542,4 @@ declare namespace index {
|
|
|
5498
5542
|
export { type index_Framework as Framework, type index_FrameworkCheck as FrameworkCheck, type index_FrameworkInfo as FrameworkInfo, type index_FrameworkRecipe as FrameworkRecipe, index_SUPPORTED_FRAMEWORKS as SUPPORTED_FRAMEWORKS, index_detectFramework as detectFramework, index_detectFromHeaders as detectFromHeaders, index_detectFromHtml as detectFromHtml, index_detectFromPackageJson as detectFromPackageJson, index_embedRecipe as embedRecipe, index_getFrameworkChecks as getFrameworkChecks, index_getFrameworkDisplayName as getFrameworkDisplayName, index_getFrameworkIssues as getFrameworkIssues, index_getFrameworkSuggestions as getFrameworkSuggestions, index_getFrameworkSummary as getFrameworkSummary, index_getMigrationPaths as getMigrationPaths, index_getPerformanceTips as getPerformanceTips, index_getSeoChallenges as getSeoChallenges, index_getTotalFrameworkChecks as getTotalFrameworkChecks, index_loadRecipe as loadRecipe };
|
|
5499
5543
|
}
|
|
5500
5544
|
|
|
5501
|
-
export { type AIContentConfig, type AIKeywordResearchOptions, type AgentDefinition, type AgentTool, type AlertChannel, type AlertConfig, type AlertMessage, type AlertResult, type AlertSeverity, type AlertType, type AuditAlertPayload, type AuditIssue, type AuditIssueData, type AuditOptions, type AuditPRResult, type AuditReport, type AuditReportData, type AuditRunnerOptions, type AuditRunnerResult, type AuditSchedule, type BlogPostConfig, type BrandConfig, type CIAction, type CIKeywordOptions, type CIKeywordResult, COMMIT_TYPES, type ChangelogConfig, type ChangelogResult, type CitationCheckOptions, type CommitConfig, type ComparisonConfig, type ComparisonRow, type CompetitiveInsight, type CompetitiveSearchOptions, type CompetitiveSearchResult, type CompetitorComparison, type CompetitorKeywordResult, type CompetitorOverlap, type CompetitorTool, type CompetitorUrl, type ContentElements, type ContentGeneratorOptions, type ContentRecommendation, type ConventionalCommit, type CrawlResult, type CrawlStats, type CrawledPage, type CreateAuditPROptions, type CreatePROptions, type DataForSEOCredentials, type DiscoveredPage, type EnhancedToolIdea, type ExecOptions, type ExecuteOptions, type ExecutionResult, type FAQItem, type FAQSchemaOptions, type FeaturedSnippetAnalysis, type Fix, type FixFile, type FixGeneratorOptions, type FixResult, type FrameworkInfo$1 as FrameworkInfo, type FreeKeywordResult, type FreeToolIdea, type GA4Config, type GEOAlert, type GEOAlertType, type GEOHistory, type GEOHistoryOptions, type GEOQuery, type GEOReport, type GEOResult, type GEOTrend, type GSCConfig, type GSCCredentials, type GSCPerformanceData, type GSCQueryData, type GSCQueryResult, type GeneratedCode, type GeneratedContent, type GeneratedFix, type HeadingStructure, type HeadlineAnalysis, type HealthScore, ISSUE_DEFINITIONS, type ImageInfo$2 as ImageInfo, type InjectionResult, type IntentAnalysis, type IssueCategory, type IssueDefinition, type IssueSeverity, type KeyFact, type KeyFactsOptions, type KeywordAction, type KeywordCluster, type KeywordData, type KeywordDensityAnalysis, type KeywordOpportunity, type KeywordRecommendation, type KeywordResearchOptions, type KeywordResearchResult, type KeywordStats, type KeywordTopic, type KeywordTrend, type LLMCitationResult, type LLMJudgeOptions, type LLMProvider, LOCATION_CODES, type LSIKeyword, type LinkInfo$1 as LinkInfo, type Mention, type MentionType, type MetaData, type MetaFixOptions, type NGram, type NLPAnalysisResult, OG_IMAGE_SPECS, type OptimizeOptions, type PRConfig, type PRDescription, PRIORITY_WEIGHTS, type PRResult, type PageAudit, type PageData, type PageMeta, type PaidKeywordResult, type ParsedResponse, type PlanTier, type ProviderStats, type QuickWin, type RankCheckOptions, type RankCheckResult, type RankHistory, RankTracker, type RankingResult, type RankingTierLimits, type ReadabilityResult, type ReadmeConfig, type ReadmeResult, type ReportBranding, type ReportConfig, type ReportData, type ReportPageData, type RouteInfo, type SEOAnalysisResult, type SEOFixCommit, type SEOFixSummary, type SEOIssue, type SEORecommendation, type SEOScore, SEO_SCOPES, SITE_PROFILE_QUESTIONS, type ScheduledAuditConfig, type ScheduledAuditResult, type SchemaData, Schemas, type SearchIntent, type Sentiment, type SerpApiConfig, SerpClient, type SerpClientConfig, type SerpFeature, type SiteCrawlResult, type SiteProfile, type SiteSummary, type SnippetRecommendation, type SnippetType, type SocialMetaConfig, type SocialMetaFix, type SummarizerOptions, type TFIDFResult, TIER_LIMITS, type ToolFeasibilityScore, type ToolFunction, type ToolResult, type TopicClusterResult, type TopicModel, type TrackedKeyword, type TrackerConfig, type TrackingOptions, type TrendDirection, type UncertaintyAssessment, type WizardQuestion, type WizardResponse, type WizardResult, type WizardSession, type WorkflowConfig, addTrackingResult, analyzeAnchorText, analyzeCanonicalAdvanced, analyzeClientRendering, analyzeContentFreshness, analyzeDOMStructure, analyzeFeaturedSnippetPotential, analyzeHeadings, analyzeHeadline, analyzeHreflang, analyzeImages$1 as analyzeImages, analyzeKeywordDensity, analyzeKeywords, analyzeLinks, analyzeLocalSEO, analyzeMobile, analyzeModernImages, analyzeOnPage, analyzePagination, analyzePerformance, analyzeReadability, analyzeRedirectChain, analyzeRedirects, analyzeResponsiveImages, analyzeSERPPreview, analyzeSecurity, analyzeSecurityHeaders$1 as analyzeSecurityHeaders, analyzeSocialMeta, analyzeStructuredData$1 as analyzeStructuredData, analyzeTrackerBloat, analyzeUrl, analyzeUrlSafety, index$1 as analyzers, applyFixes, buildGSCApiRequest, buildGSCRequest, calculateAIVisibilityScore, calculateBM25, calculateTFIDF$1 as calculateKeywordTFIDF, calculateNextRun, calculateTFIDF, checkAIBotBlocking, checkAMP, checkAdsTxt, checkAppleTouchIcon, checkBalance, checkCertificate, checkDMARC, checkGitHubCLI, checkInternalRedirects, checkJSRenderingRatio, checkLLMCitations, checkLlmsTxt, checkMobileResources, checkPlaintextEmails, checkRedirects, checkRobots, checkRobotsTxt, checkSPF, checkSitemap, classifyIntent, classifyIntents, clusterKeywordsByEmbedding, compareCompetitorVisibility, comparePeriods, completeWizard, crawlSite, crawlUrl, createAuditPR, createFallbackSummary, createGEOHistory, createPullRequest, createSEOCommit, createSEOCommits, detectDuplicates, detectFramework$1 as detectFramework, detectGitHubPages, detectMentions, detectSoft404, detectTechnologies, detectVisibilityChanges, discoverCompetitorKeywords, discoverPagesFromLinks, discoverRoutesFromRepo, enhanceSummaryWithCompetitors, enhanceToolIdea, enrichKeywordsWithEstimates, ensureGitRepo, evaluateAndEnhanceToolIdeas, evaluateToolFeasibility, executeAgent, extractContentHash, extractEntityPhrases, extractImages, extractKeyPhrases, extractLinks, extractMeta, extractNgrams, extractSchema, extractSeedKeywords, extractTopics, fetchCoreWebVitals, findAlmostPage1Keywords, findCTROpportunities, findHtmlEntry, findLSIKeywords, findPageFiles, findTopPerformers, formatAlertMessage, formatCIResult, formatCompetitorReport, formatConventionalCommit, formatFeaturedSnippetReport, formatHeadlineReport, formatIntentReport, formatKeywordDensityReport, formatKeywordReport, formatPRBody, formatPRTitle, formatReadabilityReport, formatReport, formatSEOCommitMessage, formatTopicReport, formatWizardProgress, index as frameworks, generateAICitableContent, generateAllFixes, generateAngularSEOService, generateAspNetCoreSEO, generateAstroBaseHead, generateAstroMeta, generateBlogPost, generateBranchName, generateChangelog, generateCommitSummary, generateComparisonTable, generateCompleteSocialMetaSetup, generateDjangoSEOHelper, generateDuplicateIssues, generateEleventySEO, generateFAQSchema, generateFixes, generateGA4EnvTemplate, generateGA4ReactComponent, generateGA4Script, generateGA4ViteScript, generateGEOReport, generateGSCVerificationTag, generateGitHubActionSetup, generateGoSEO, generateHTMLReport, generateHTMLSocialMeta, generateHTMXSEO, generateHeadlineVariations, generateHugoSEO, generateJekyllSEO, generateJsonReport, generateKeyFacts, generateLaravelSEOHelper, generateMarkdownReport, generateNextAppMetadata, generateNextJsAppRouterMetadata, generateNextJsPagesRouterHead, generateNextPagesHead, generateNuxtSEOHead, generatePDFReport, generatePRDescription, generatePhoenixSEO, generateRailsSEOHelper, generateReactHelmetSocialMeta, generateReactSEOHead, generateRecommendationQueries, generateRemixMeta, generateRemixSEO, generateSecretsDoc, generateSocialMetaFix, generateSpringBootSEO, generateSvelteKitMeta, generateSvelteKitSEOHead, generateUncertaintyQuestions, generateVueSEOHead, generateWizardQuestions, generateWorkflow, getAIVisibilitySummary, getAutocompleteSuggestions, getDateRange, getExpandedSuggestions, getFrameworkSpecificFix, getFrameworkSpecificFixExtended, getGSCSetupInstructions, getGitUser, getKeywordData, getKeywordSuggestions, getMaxKdThreshold, getNextQuestion, getRelatedKeywords, getVisibilityTrend, groupKeywordsByTopic, identifyQuickWins, injectGA4, injectGSCVerification, interpolatePrompt, isGitRepo, listFiles, loadAgent, loadAgentByName, mergePages, optimizeForAI, optimizeReadme, parseGEOResponse, parseGSCResponse, parseSitemap, prioritizeKeywords, processWizardResponse, readFile, routesToUrls, runAIKeywordResearch, runAIReadinessChecks, runAdditionalChecks, runAuditWithFixes, runCIKeywordResearch, runCrawlabilityChecks, runDirectAnalysis, runFullAudit, runKeywordResearch, runNLPAnalysis, runScheduledAudit, scoreContentSEO, scoreMention, searchCompetitors, searchFormatConverters, searchHackerNews, sendAlert, sendAlerts, sendDiscordAlert, sendSlackAlert, shouldRunAudit, shouldSendAlert, startWizardSession, suggestSchemaTypes, summarizeSite, tokenize, tools, trackLLMVisibility, transformGSCData, urlSafetyDatabase, wizardResponsesToContext, writeFile, writeGitHubActionFiles };
|
|
5545
|
+
export { type AIContentConfig, type AIKeywordResearchOptions, type AgentDefinition, type AgentTool, type AlertChannel, type AlertConfig, type AlertMessage, type AlertResult, type AlertSeverity, type AlertType, type AuditAlertPayload, type AuditIssue, type AuditIssueData, type AuditOptions, type AuditPRResult, type AuditReport, type AuditReportData, type AuditRunnerOptions, type AuditRunnerResult, type AuditSchedule, type BlogPostConfig, type BrandConfig, type CIAction, type CIKeywordOptions, type CIKeywordResult, COMMIT_TYPES, type ChangelogConfig, type ChangelogResult, type CitationCheckOptions, type CommitConfig, type ComparisonConfig, type ComparisonRow, type CompetitiveInsight, type CompetitiveSearchOptions, type CompetitiveSearchResult, type CompetitorComparison, type CompetitorKeywordResult, type CompetitorOverlap, type CompetitorTool, type CompetitorUrl, type ContentElements, type ContentGeneratorOptions, type ContentRecommendation, type ConventionalCommit, type CrawlResult, type CrawlStats, type CrawledPage, type CreateAuditPROptions, type CreatePROptions, type DataForSEOCredentials, type DiscoveredPage, type EnhancedToolIdea, type ExecOptions, type ExecuteOptions, type ExecutionResult, type FAQItem, type FAQSchemaOptions, type FeaturedSnippetAnalysis, type Fix, type FixFile, type FixGeneratorOptions, type FixResult, type FrameworkInfo$1 as FrameworkInfo, type FreeKeywordResult, type FreeToolIdea, type GA4Config, type GEOAlert, type GEOAlertType, type GEOHistory, type GEOHistoryOptions, type GEOQuery, type GEOReport, type GEOResult, type GEOTrend, type GSCConfig, type GSCCredentials, type GSCPerformanceData, type GSCQueryData, type GSCQueryResult, type GeneratedCode, type GeneratedContent, type GeneratedFix, type HeadingStructure, type HeadlineAnalysis, type HealthScore, ISSUE_DEFINITIONS, type ImageInfo$2 as ImageInfo, type InjectionResult, type IntentAnalysis, type IssueCategory, type IssueDefinition, type IssueSeverity, type KeyFact, type KeyFactsOptions, type KeywordAction, type KeywordCluster, type KeywordData, type KeywordDensityAnalysis, type KeywordLLM, type KeywordOpportunity, type KeywordRecommendation, type KeywordResearchOptions, type KeywordResearchResult, type KeywordStats, type KeywordTopic, type KeywordTrend, type LLMCitationResult, type LLMJudgeOptions, type LLMProvider, LOCATION_CODES, type LSIKeyword, type LinkInfo$1 as LinkInfo, type Mention, type MentionType, type MetaData, type MetaFixOptions, type NGram, type NLPAnalysisResult, OG_IMAGE_SPECS, type OptimizeOptions, type PRConfig, type PRDescription, PRIORITY_WEIGHTS, type PRResult, type PageAudit, type PageData, type PageMeta, type PaidKeywordResult, type ParsedResponse, type PlanTier, type ProviderStats, type QuickWin, type RankCheckOptions, type RankCheckResult, type RankHistory, RankTracker, type RankingResult, type RankingTierLimits, type ReadabilityResult, type ReadmeConfig, type ReadmeResult, type ReportBranding, type ReportConfig, type ReportData, type ReportPageData, type RouteInfo, type SEOAnalysisResult, type SEOFixCommit, type SEOFixSummary, type SEOIssue, type SEORecommendation, type SEOScore, SEO_SCOPES, SITE_PROFILE_QUESTIONS, type ScheduledAuditConfig, type ScheduledAuditResult, type SchemaData, Schemas, type SearchIntent, type Sentiment, type SerpApiConfig, SerpClient, type SerpClientConfig, type SerpFeature, type SiteCrawlResult, type SiteProfile, type SiteSummary, type SnippetRecommendation, type SnippetType, type SocialMetaConfig, type SocialMetaFix, type SummarizerOptions, type TFIDFResult, TIER_LIMITS, type ToolFeasibilityScore, type ToolFunction, type ToolResult, type TopicClusterResult, type TopicModel, type TrackedKeyword, type TrackerConfig, type TrackingOptions, type TrendDirection, type UncertaintyAssessment, type WizardQuestion, type WizardResponse, type WizardResult, type WizardSession, type WorkflowConfig, addTrackingResult, analyzeAnchorText, analyzeCanonicalAdvanced, analyzeClientRendering, analyzeContentFreshness, analyzeDOMStructure, analyzeFeaturedSnippetPotential, analyzeHeadings, analyzeHeadline, analyzeHreflang, analyzeImages$1 as analyzeImages, analyzeKeywordDensity, analyzeKeywords, analyzeLinks, analyzeLocalSEO, analyzeMobile, analyzeModernImages, analyzeOnPage, analyzePagination, analyzePerformance, analyzeReadability, analyzeRedirectChain, analyzeRedirects, analyzeResponsiveImages, analyzeSERPPreview, analyzeSecurity, analyzeSecurityHeaders$1 as analyzeSecurityHeaders, analyzeSocialMeta, analyzeStructuredData$1 as analyzeStructuredData, analyzeTrackerBloat, analyzeUrl, analyzeUrlSafety, index$1 as analyzers, applyFixes, buildGSCApiRequest, buildGSCRequest, calculateAIVisibilityScore, calculateBM25, calculateTFIDF$1 as calculateKeywordTFIDF, calculateNextRun, calculateTFIDF, checkAIBotBlocking, checkAMP, checkAdsTxt, checkAppleTouchIcon, checkBalance, checkCertificate, checkDMARC, checkGitHubCLI, checkInternalRedirects, checkJSRenderingRatio, checkLLMCitations, checkLlmsTxt, checkMobileResources, checkPlaintextEmails, checkRedirects, checkRobots, checkRobotsTxt, checkSPF, checkSitemap, classifyIntent, classifyIntents, clusterKeywordsByEmbedding, compareCompetitorVisibility, comparePeriods, completeWizard, crawlSite, crawlUrl, createAuditPR, createFallbackSummary, createGEOHistory, createPullRequest, createSEOCommit, createSEOCommits, detectDuplicates, detectFramework$1 as detectFramework, detectGitHubPages, detectMentions, detectSoft404, detectTechnologies, detectVisibilityChanges, discoverCompetitorKeywords, discoverPagesFromLinks, discoverRoutesFromRepo, enhanceSummaryWithCompetitors, enhanceToolIdea, enrichKeywordsWithEstimates, ensureGitRepo, evaluateAndEnhanceToolIdeas, evaluateToolFeasibility, executeAgent, extractContentHash, extractEntityPhrases, extractImages, extractKeyPhrases, extractLinks, extractMeta, extractNgrams, extractSchema, extractSeedKeywords, extractTopics, fetchCoreWebVitals, findAlmostPage1Keywords, findCTROpportunities, findHtmlEntry, findLSIKeywords, findPageFiles, findTopPerformers, formatAlertMessage, formatCIResult, formatCompetitorReport, formatConventionalCommit, formatFeaturedSnippetReport, formatHeadlineReport, formatIntentReport, formatKeywordDensityReport, formatKeywordReport, formatPRBody, formatPRTitle, formatReadabilityReport, formatReport, formatSEOCommitMessage, formatTopicReport, formatWizardProgress, index as frameworks, generateAICitableContent, generateAllFixes, generateAngularSEOService, generateAspNetCoreSEO, generateAstroBaseHead, generateAstroMeta, generateBlogPost, generateBranchName, generateChangelog, generateCommitSummary, generateComparisonTable, generateCompleteSocialMetaSetup, generateDjangoSEOHelper, generateDuplicateIssues, generateEleventySEO, generateFAQSchema, generateFixes, generateGA4EnvTemplate, generateGA4ReactComponent, generateGA4Script, generateGA4ViteScript, generateGEOReport, generateGSCVerificationTag, generateGitHubActionSetup, generateGoSEO, generateHTMLReport, generateHTMLSocialMeta, generateHTMXSEO, generateHeadlineVariations, generateHugoSEO, generateJekyllSEO, generateJsonReport, generateKeyFacts, generateLaravelSEOHelper, generateMarkdownReport, generateNextAppMetadata, generateNextJsAppRouterMetadata, generateNextJsPagesRouterHead, generateNextPagesHead, generateNuxtSEOHead, generatePDFReport, generatePRDescription, generatePhoenixSEO, generateRailsSEOHelper, generateReactHelmetSocialMeta, generateReactSEOHead, generateRecommendationQueries, generateRemixMeta, generateRemixSEO, generateSecretsDoc, generateSocialMetaFix, generateSpringBootSEO, generateSvelteKitMeta, generateSvelteKitSEOHead, generateUncertaintyQuestions, generateVueSEOHead, generateWizardQuestions, generateWorkflow, getAIVisibilitySummary, getAutocompleteSuggestions, getDateRange, getExpandedSuggestions, getFrameworkSpecificFix, getFrameworkSpecificFixExtended, getGSCSetupInstructions, getGitUser, getKeywordData, getKeywordSuggestions, getMaxKdThreshold, getNextQuestion, getRelatedKeywords, getVisibilityTrend, groupKeywordsByTopic, identifyQuickWins, injectGA4, injectGSCVerification, interpolatePrompt, isGitRepo, keywordLLM, listFiles, loadAgent, loadAgentByName, mergePages, openAIKeywordLLM, optimizeForAI, optimizeReadme, parseGEOResponse, parseGSCResponse, parseJsonReply, parseSitemap, prioritizeKeywords, processWizardResponse, readFile, routesToUrls, runAIKeywordResearch, runAIReadinessChecks, runAdditionalChecks, runAuditWithFixes, runCIKeywordResearch, runCrawlabilityChecks, runDirectAnalysis, runFullAudit, runKeywordResearch, runNLPAnalysis, runScheduledAudit, scoreContentSEO, scoreMention, searchCompetitors, searchFormatConverters, searchHackerNews, sendAlert, sendAlerts, sendDiscordAlert, sendSlackAlert, shouldRunAudit, shouldSendAlert, startWizardSession, suggestSchemaTypes, summarizeSite, tokenize, tools, trackLLMVisibility, transformGSCData, urlSafetyDatabase, wizardResponsesToContext, writeFile, writeGitHubActionFiles };
|
package/dist/index.js
CHANGED
|
@@ -3515,14 +3515,17 @@ __export(index_exports, {
|
|
|
3515
3515
|
injectGSCVerification: () => injectGSCVerification,
|
|
3516
3516
|
interpolatePrompt: () => interpolatePrompt,
|
|
3517
3517
|
isGitRepo: () => isGitRepo,
|
|
3518
|
+
keywordLLM: () => keywordLLM,
|
|
3518
3519
|
listFiles: () => listFiles,
|
|
3519
3520
|
loadAgent: () => loadAgent,
|
|
3520
3521
|
loadAgentByName: () => loadAgentByName,
|
|
3521
3522
|
mergePages: () => mergePages,
|
|
3523
|
+
openAIKeywordLLM: () => openAIKeywordLLM,
|
|
3522
3524
|
optimizeForAI: () => optimizeForAI,
|
|
3523
3525
|
optimizeReadme: () => optimizeReadme,
|
|
3524
3526
|
parseGEOResponse: () => parseGEOResponse,
|
|
3525
3527
|
parseGSCResponse: () => parseGSCResponse,
|
|
3528
|
+
parseJsonReply: () => parseJsonReply,
|
|
3526
3529
|
parseSitemap: () => parseSitemap,
|
|
3527
3530
|
prioritizeKeywords: () => prioritizeKeywords,
|
|
3528
3531
|
processWizardResponse: () => processWizardResponse,
|
|
@@ -20111,6 +20114,50 @@ async function extractSeedKeywords(url) {
|
|
|
20111
20114
|
}
|
|
20112
20115
|
}
|
|
20113
20116
|
|
|
20117
|
+
// src/keywords/llm.ts
|
|
20118
|
+
var import_openai = __toESM(require("openai"));
|
|
20119
|
+
function openAIKeywordLLM(apiKey, opts = {}) {
|
|
20120
|
+
const openai = new import_openai.default({ apiKey });
|
|
20121
|
+
const openAIModel = opts.model;
|
|
20122
|
+
return {
|
|
20123
|
+
async complete(prompt, opts2 = {}) {
|
|
20124
|
+
const response = await openai.chat.completions.create({
|
|
20125
|
+
model: opts2.best ? "gpt-4o" : openAIModel ?? "gpt-4o-mini",
|
|
20126
|
+
messages: [
|
|
20127
|
+
...opts2.system ? [{ role: "system", content: opts2.system }] : [],
|
|
20128
|
+
{ role: "user", content: prompt }
|
|
20129
|
+
],
|
|
20130
|
+
response_format: { type: "json_object" },
|
|
20131
|
+
temperature: opts2.temperature ?? 0.3,
|
|
20132
|
+
...opts2.maxTokens ? { max_tokens: opts2.maxTokens } : {}
|
|
20133
|
+
});
|
|
20134
|
+
return response.choices[0]?.message?.content || "";
|
|
20135
|
+
},
|
|
20136
|
+
async embed(texts) {
|
|
20137
|
+
const response = await openai.embeddings.create({ model: "text-embedding-3-small", input: texts });
|
|
20138
|
+
return response.data.map((item) => item.embedding);
|
|
20139
|
+
}
|
|
20140
|
+
};
|
|
20141
|
+
}
|
|
20142
|
+
function keywordLLM(options) {
|
|
20143
|
+
return options.llm ?? (options.openaiApiKey ? openAIKeywordLLM(options.openaiApiKey) : void 0);
|
|
20144
|
+
}
|
|
20145
|
+
function parseJsonReply(text) {
|
|
20146
|
+
const cleaned = text.replace(/<think>[\s\S]*?<\/think>/gi, "");
|
|
20147
|
+
const start = cleaned.indexOf("{");
|
|
20148
|
+
const end = cleaned.lastIndexOf("}");
|
|
20149
|
+
if (start === -1 || end <= start) return null;
|
|
20150
|
+
const candidate = cleaned.slice(start, end + 1);
|
|
20151
|
+
for (const attempt of [candidate, candidate.replace(/,\s*([}\]])/g, "$1")]) {
|
|
20152
|
+
try {
|
|
20153
|
+
const parsed = JSON.parse(attempt);
|
|
20154
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) return parsed;
|
|
20155
|
+
} catch {
|
|
20156
|
+
}
|
|
20157
|
+
}
|
|
20158
|
+
return null;
|
|
20159
|
+
}
|
|
20160
|
+
|
|
20114
20161
|
// src/keywords/site-crawler.ts
|
|
20115
20162
|
var cheerio40 = __toESM(require("cheerio"));
|
|
20116
20163
|
init_http();
|
|
@@ -20321,7 +20368,11 @@ function extractKeyPhrases(crawlResult) {
|
|
|
20321
20368
|
}
|
|
20322
20369
|
|
|
20323
20370
|
// src/keywords/ai-summarizer.ts
|
|
20324
|
-
|
|
20371
|
+
function summarizerLLM(options) {
|
|
20372
|
+
const llm = options.llm ?? (options.openaiApiKey ? openAIKeywordLLM(options.openaiApiKey, { model: options.model }) : void 0);
|
|
20373
|
+
if (!llm) throw new Error("An AI provider is required to summarize the site");
|
|
20374
|
+
return llm;
|
|
20375
|
+
}
|
|
20325
20376
|
var SUMMARIZER_PROMPT = `You are an expert SEO analyst. Analyze the following website content and extract key information for keyword research.
|
|
20326
20377
|
|
|
20327
20378
|
WEBSITE CONTENT:
|
|
@@ -20364,37 +20415,26 @@ IMPORTANT:
|
|
|
20364
20415
|
|
|
20365
20416
|
Respond ONLY with the JSON object, no other text.`;
|
|
20366
20417
|
async function summarizeSite(crawlResult, options) {
|
|
20367
|
-
const
|
|
20368
|
-
const
|
|
20418
|
+
const llm = summarizerLLM(options);
|
|
20419
|
+
const maxContent = llm.maxContentChars ?? 15e3;
|
|
20369
20420
|
let content = crawlResult.aggregatedContent;
|
|
20370
|
-
if (content.length >
|
|
20371
|
-
content = content.substring(0,
|
|
20421
|
+
if (content.length > maxContent) {
|
|
20422
|
+
content = content.substring(0, maxContent) + "\n...[truncated]";
|
|
20372
20423
|
}
|
|
20373
20424
|
const headings = crawlResult.uniqueHeadings.slice(0, 30).join("\n");
|
|
20374
20425
|
const prompt = SUMMARIZER_PROMPT.replace("{content}", content).replace("{headings}", headings).replace("{productPages}", String(crawlResult.detectedPageTypes.product)).replace("{pricingPages}", String(crawlResult.detectedPageTypes.pricing)).replace("{blogPages}", String(crawlResult.detectedPageTypes.blog)).replace("{featurePages}", String(crawlResult.detectedPageTypes.feature));
|
|
20375
20426
|
console.log("\u{1F916} Analyzing site content with AI...");
|
|
20376
20427
|
try {
|
|
20377
|
-
const
|
|
20378
|
-
|
|
20379
|
-
messages: [
|
|
20380
|
-
{
|
|
20381
|
-
role: "system",
|
|
20382
|
-
content: "You are an expert SEO analyst. Always respond with valid JSON only."
|
|
20383
|
-
},
|
|
20384
|
-
{
|
|
20385
|
-
role: "user",
|
|
20386
|
-
content: prompt
|
|
20387
|
-
}
|
|
20388
|
-
],
|
|
20428
|
+
const responseText = await llm.complete(prompt, {
|
|
20429
|
+
system: "You are an expert SEO analyst. Always respond with valid JSON only.",
|
|
20389
20430
|
temperature: 0.3,
|
|
20390
|
-
|
|
20431
|
+
maxTokens: options.maxTokens || 2e3
|
|
20391
20432
|
});
|
|
20392
|
-
const
|
|
20393
|
-
|
|
20394
|
-
if (!jsonMatch) {
|
|
20433
|
+
const parsed = parseJsonReply(responseText);
|
|
20434
|
+
if (!parsed) {
|
|
20395
20435
|
throw new Error("No JSON found in response");
|
|
20396
20436
|
}
|
|
20397
|
-
const summary =
|
|
20437
|
+
const summary = parsed;
|
|
20398
20438
|
return {
|
|
20399
20439
|
productName: summary.productName || "Unknown Product",
|
|
20400
20440
|
productDescription: summary.productDescription || "No description available",
|
|
@@ -20456,7 +20496,7 @@ function createFallbackSummary(crawlResult) {
|
|
|
20456
20496
|
};
|
|
20457
20497
|
}
|
|
20458
20498
|
async function enhanceSummaryWithCompetitors(summary, competitors, options) {
|
|
20459
|
-
const
|
|
20499
|
+
const llm = summarizerLLM(options);
|
|
20460
20500
|
const prompt = `Given this product summary:
|
|
20461
20501
|
Product: ${summary.productName}
|
|
20462
20502
|
Description: ${summary.productDescription}
|
|
@@ -20475,19 +20515,13 @@ Analyze the competitive positioning. If this product focuses on a niche segment
|
|
|
20475
20515
|
"updatedUncertainties": ["uncertainty1", "uncertainty2"]
|
|
20476
20516
|
}`;
|
|
20477
20517
|
try {
|
|
20478
|
-
const
|
|
20479
|
-
|
|
20480
|
-
messages: [
|
|
20481
|
-
{ role: "system", content: "Respond with valid JSON only." },
|
|
20482
|
-
{ role: "user", content: prompt }
|
|
20483
|
-
],
|
|
20518
|
+
const responseText = await llm.complete(prompt, {
|
|
20519
|
+
system: "Respond with valid JSON only.",
|
|
20484
20520
|
temperature: 0.3,
|
|
20485
|
-
|
|
20521
|
+
maxTokens: 1e3
|
|
20486
20522
|
});
|
|
20487
|
-
const
|
|
20488
|
-
|
|
20489
|
-
if (jsonMatch) {
|
|
20490
|
-
const analysis = JSON.parse(jsonMatch[0]);
|
|
20523
|
+
const analysis = parseJsonReply(responseText);
|
|
20524
|
+
if (analysis) {
|
|
20491
20525
|
return {
|
|
20492
20526
|
...summary,
|
|
20493
20527
|
competitivePositioning: analysis.competitivePositioning || "",
|
|
@@ -20574,7 +20608,6 @@ function generateUncertaintyQuestions(summary) {
|
|
|
20574
20608
|
}
|
|
20575
20609
|
|
|
20576
20610
|
// src/keywords/nlp-analysis.ts
|
|
20577
|
-
var import_openai2 = __toESM(require("openai"));
|
|
20578
20611
|
var STOP_WORDS2 = /* @__PURE__ */ new Set([
|
|
20579
20612
|
"a",
|
|
20580
20613
|
"an",
|
|
@@ -20859,19 +20892,18 @@ function createTermVectors(keywords, vocabulary) {
|
|
|
20859
20892
|
return vector;
|
|
20860
20893
|
});
|
|
20861
20894
|
}
|
|
20895
|
+
async function embedBatch(embedder, batch) {
|
|
20896
|
+
if (typeof embedder === "function") return embedder(batch);
|
|
20897
|
+
const response = await embedder.embeddings.create({ model: "text-embedding-3-small", input: batch });
|
|
20898
|
+
return response.data.map((item) => item.embedding);
|
|
20899
|
+
}
|
|
20862
20900
|
async function getEmbeddings(texts, openai) {
|
|
20863
20901
|
const embeddings = [];
|
|
20864
20902
|
const batchSize = 100;
|
|
20865
20903
|
for (let i = 0; i < texts.length; i += batchSize) {
|
|
20866
20904
|
const batch = texts.slice(i, i + batchSize);
|
|
20867
20905
|
try {
|
|
20868
|
-
|
|
20869
|
-
model: "text-embedding-3-small",
|
|
20870
|
-
input: batch
|
|
20871
|
-
});
|
|
20872
|
-
for (const item of response.data) {
|
|
20873
|
-
embeddings.push(item.embedding);
|
|
20874
|
-
}
|
|
20906
|
+
embeddings.push(...await embedBatch(openai, batch));
|
|
20875
20907
|
} catch (error) {
|
|
20876
20908
|
console.warn("Embedding API failed, using fallback BOW vectors");
|
|
20877
20909
|
for (const text of batch) {
|
|
@@ -21026,12 +21058,12 @@ async function runNLPAnalysis(content, options = {}) {
|
|
|
21026
21058
|
const entityPhrases = extractEntityPhrases(combinedText);
|
|
21027
21059
|
let clusters = [];
|
|
21028
21060
|
const keywordsToCluster = tfidfKeywords.slice(0, 50).map((k) => k.term);
|
|
21029
|
-
|
|
21061
|
+
const embed = keywordLLM(options)?.embed;
|
|
21062
|
+
if (embed && keywordsToCluster.length > 0) {
|
|
21030
21063
|
try {
|
|
21031
|
-
const openai = new import_openai2.default({ apiKey: options.openaiApiKey });
|
|
21032
21064
|
clusters = await clusterKeywordsByEmbedding(
|
|
21033
21065
|
keywordsToCluster,
|
|
21034
|
-
|
|
21066
|
+
(texts) => embed(texts),
|
|
21035
21067
|
options.numClusters
|
|
21036
21068
|
);
|
|
21037
21069
|
} catch (error) {
|
|
@@ -21374,9 +21406,13 @@ async function searchHackerNews(query, maxResults = 10) {
|
|
|
21374
21406
|
}
|
|
21375
21407
|
|
|
21376
21408
|
// src/keywords/llm-judge.ts
|
|
21377
|
-
|
|
21409
|
+
function judgeLLM(options) {
|
|
21410
|
+
const llm = keywordLLM(options);
|
|
21411
|
+
if (!llm) throw new Error("An AI provider is required to judge tool ideas");
|
|
21412
|
+
return llm;
|
|
21413
|
+
}
|
|
21378
21414
|
async function evaluateToolFeasibility(toolIdea, siteSummary, competitiveData, options) {
|
|
21379
|
-
const
|
|
21415
|
+
const llm = judgeLLM(options);
|
|
21380
21416
|
const minFeasibility = options.minFeasibilityScore ?? 3;
|
|
21381
21417
|
const minSpecificity = options.minSpecificityScore ?? 3;
|
|
21382
21418
|
const minProductFit = options.minProductFitScore ?? 2;
|
|
@@ -21452,13 +21488,10 @@ Respond in JSON:
|
|
|
21452
21488
|
"blockers": ["<issues that prevent building>"],
|
|
21453
21489
|
"improvements": ["<suggestions to make it more specific/buildable>"]
|
|
21454
21490
|
}`;
|
|
21455
|
-
const
|
|
21456
|
-
|
|
21457
|
-
|
|
21458
|
-
|
|
21459
|
-
temperature: 0.3
|
|
21460
|
-
});
|
|
21461
|
-
const result = JSON.parse(response.choices[0].message.content || "{}");
|
|
21491
|
+
const result = parseJsonReply(await llm.complete(prompt, { temperature: 0.3 })) ?? {};
|
|
21492
|
+
for (const key of ["feasibility", "specificity", "productFit", "marketOpportunity"]) {
|
|
21493
|
+
result[key] = Number(result[key]) || 0;
|
|
21494
|
+
}
|
|
21462
21495
|
const overallScore = result.feasibility * 0.3 + result.specificity * 0.3 + result.productFit * 0.2 + result.marketOpportunity * 0.2;
|
|
21463
21496
|
const passes = result.feasibility >= minFeasibility && result.specificity >= minSpecificity && result.productFit >= minProductFit;
|
|
21464
21497
|
return {
|
|
@@ -21474,7 +21507,7 @@ Respond in JSON:
|
|
|
21474
21507
|
};
|
|
21475
21508
|
}
|
|
21476
21509
|
async function enhanceToolIdea(toolIdea, siteSummary, competitiveData, options) {
|
|
21477
|
-
const
|
|
21510
|
+
const llm = judgeLLM(options);
|
|
21478
21511
|
const feasibilityScore = await evaluateToolFeasibility(
|
|
21479
21512
|
toolIdea,
|
|
21480
21513
|
siteSummary,
|
|
@@ -21519,13 +21552,7 @@ ${competitiveData?.openSourceOptions.slice(0, 3).map((t) => `- OSS: ${t.name} ($
|
|
|
21519
21552
|
"canUseExistingOSS": <true if an OSS library does most of the work>,
|
|
21520
21553
|
"ossLibrary": "<name of OSS library to use, or null>"
|
|
21521
21554
|
}`;
|
|
21522
|
-
const
|
|
21523
|
-
model: "gpt-4o",
|
|
21524
|
-
messages: [{ role: "user", content: prompt }],
|
|
21525
|
-
response_format: { type: "json_object" },
|
|
21526
|
-
temperature: 0.5
|
|
21527
|
-
});
|
|
21528
|
-
const enhanced = JSON.parse(response.choices[0].message.content || "{}");
|
|
21555
|
+
const enhanced = parseJsonReply(await llm.complete(prompt, { temperature: 0.5, best: true })) ?? {};
|
|
21529
21556
|
const priority = Math.round(
|
|
21530
21557
|
feasibilityScore.overallScore * 20 + toolIdea.volume / 1e3 * 10 + (100 - toolIdea.difficulty) / 100 * 20
|
|
21531
21558
|
);
|
|
@@ -21576,7 +21603,7 @@ async function evaluateAndEnhanceToolIdeas(toolIdeas, siteSummary, getCompetitiv
|
|
|
21576
21603
|
|
|
21577
21604
|
// src/keywords/ai-keyword-engine.ts
|
|
21578
21605
|
async function runAIKeywordResearch(options) {
|
|
21579
|
-
const { url, tier,
|
|
21606
|
+
const { url, tier, maxPages = 20 } = options;
|
|
21580
21607
|
console.log(`
|
|
21581
21608
|
\u{1F52C} Starting AI Keyword Research (${tier} tier)...
|
|
21582
21609
|
`);
|
|
@@ -21584,10 +21611,11 @@ async function runAIKeywordResearch(options) {
|
|
|
21584
21611
|
if (tier === "free") {
|
|
21585
21612
|
return runFreeKeywordResearch(crawlResult, options);
|
|
21586
21613
|
} else {
|
|
21587
|
-
|
|
21588
|
-
|
|
21614
|
+
const llm = keywordLLM(options);
|
|
21615
|
+
if (!llm) {
|
|
21616
|
+
throw new Error("An AI provider (or an OpenAI API key) is required for paid tier keyword research");
|
|
21589
21617
|
}
|
|
21590
|
-
return runPaidKeywordResearch(crawlResult, options);
|
|
21618
|
+
return runPaidKeywordResearch(crawlResult, options, llm);
|
|
21591
21619
|
}
|
|
21592
21620
|
}
|
|
21593
21621
|
async function runFreeKeywordResearch(crawlResult, options) {
|
|
@@ -21641,13 +21669,11 @@ async function runFreeKeywordResearch(crawlResult, options) {
|
|
|
21641
21669
|
]
|
|
21642
21670
|
};
|
|
21643
21671
|
}
|
|
21644
|
-
async function runPaidKeywordResearch(crawlResult, options) {
|
|
21645
|
-
const {
|
|
21672
|
+
async function runPaidKeywordResearch(crawlResult, options, llm) {
|
|
21673
|
+
const { userContext, wizardResponses, ciMode, competitiveSearchKeys } = options;
|
|
21646
21674
|
const enableEnhancedToolIdeas = options.enableEnhancedToolIdeas !== false;
|
|
21647
21675
|
console.log("\u{1F680} Running full AI analysis...");
|
|
21648
|
-
let siteSummary = await summarizeSite(crawlResult, {
|
|
21649
|
-
openaiApiKey
|
|
21650
|
-
});
|
|
21676
|
+
let siteSummary = await summarizeSite(crawlResult, { llm });
|
|
21651
21677
|
if (userContext) {
|
|
21652
21678
|
siteSummary = applyUserContext(siteSummary, userContext);
|
|
21653
21679
|
}
|
|
@@ -21656,7 +21682,7 @@ async function runPaidKeywordResearch(crawlResult, options) {
|
|
|
21656
21682
|
}
|
|
21657
21683
|
const documents = crawlResult.pages.map((p) => p.mainContent);
|
|
21658
21684
|
const nlpAnalysis = await runNLPAnalysis(documents, {
|
|
21659
|
-
|
|
21685
|
+
llm,
|
|
21660
21686
|
numClusters: 8,
|
|
21661
21687
|
numTopics: 6
|
|
21662
21688
|
});
|
|
@@ -21683,13 +21709,13 @@ async function runPaidKeywordResearch(crawlResult, options) {
|
|
|
21683
21709
|
nlpAnalysis
|
|
21684
21710
|
);
|
|
21685
21711
|
let freeToolIdeas;
|
|
21686
|
-
if (enableEnhancedToolIdeas
|
|
21712
|
+
if (enableEnhancedToolIdeas) {
|
|
21687
21713
|
console.log("\u{1F52C} Running enhanced tool idea analysis with competitive research...");
|
|
21688
21714
|
freeToolIdeas = await generateEnhancedFreeToolIdeas(
|
|
21689
21715
|
opportunities,
|
|
21690
21716
|
siteSummary,
|
|
21691
21717
|
{
|
|
21692
|
-
|
|
21718
|
+
llm,
|
|
21693
21719
|
...competitiveSearchKeys
|
|
21694
21720
|
}
|
|
21695
21721
|
);
|
|
@@ -21787,7 +21813,7 @@ async function generateEnhancedFreeToolIdeas(opportunities, summary, options) {
|
|
|
21787
21813
|
kw.volume >= 300 && // Slightly lower threshold
|
|
21788
21814
|
(kw.keyword.includes("generator") || kw.keyword.includes("tool") || kw.keyword.includes("maker") || kw.keyword.includes("builder") || kw.keyword.includes("converter") || kw.keyword.includes("calculator") || kw.keyword.includes("checker") || kw.keyword.includes("validator") || kw.keyword.includes("formatter") || kw.keyword.includes("free") || kw.intent === "informational")
|
|
21789
21815
|
);
|
|
21790
|
-
const basicIdeas = toolKeywords.slice(0, 15).map((kw) => ({
|
|
21816
|
+
const basicIdeas = toolKeywords.slice(0, options.llm.maxToolIdeas ?? 15).map((kw) => ({
|
|
21791
21817
|
keyword: kw.keyword,
|
|
21792
21818
|
toolName: generateToolName(kw.keyword),
|
|
21793
21819
|
toolDescription: generateToolDescription(kw.keyword, summary),
|
|
@@ -21817,7 +21843,7 @@ async function generateEnhancedFreeToolIdeas(opportunities, summary, options) {
|
|
|
21817
21843
|
basicIdeas,
|
|
21818
21844
|
summary,
|
|
21819
21845
|
getCompetitiveData,
|
|
21820
|
-
{
|
|
21846
|
+
{ llm: options.llm }
|
|
21821
21847
|
);
|
|
21822
21848
|
return enhancedIdeas.map((enhanced) => ({
|
|
21823
21849
|
keyword: enhanced.keyword,
|
|
@@ -22148,10 +22174,9 @@ async function startWizardSession(url, options = {}) {
|
|
|
22148
22174
|
console.log("\u{1F50D} Analyzing your site...");
|
|
22149
22175
|
const crawlResult = await crawlSite(url, { maxPages: 10 });
|
|
22150
22176
|
let siteSummary;
|
|
22151
|
-
|
|
22152
|
-
|
|
22153
|
-
|
|
22154
|
-
});
|
|
22177
|
+
const llm = keywordLLM(options);
|
|
22178
|
+
if (llm) {
|
|
22179
|
+
siteSummary = await summarizeSite(crawlResult, { llm });
|
|
22155
22180
|
} else {
|
|
22156
22181
|
siteSummary = {
|
|
22157
22182
|
productName: crawlResult.domain,
|
|
@@ -35072,24 +35097,32 @@ var RankTracker = class {
|
|
|
35072
35097
|
}).eq("id", keywordId);
|
|
35073
35098
|
if (updateError) {
|
|
35074
35099
|
console.error(`Failed to save ranking for ${result.keyword}:`, updateError);
|
|
35100
|
+
return;
|
|
35101
|
+
}
|
|
35102
|
+
const { error: historyError } = await this.supabase.from("keyword_position_history").insert({
|
|
35103
|
+
keyword_id: keywordId,
|
|
35104
|
+
position: result.position,
|
|
35105
|
+
url: result.url,
|
|
35106
|
+
checked_at: result.checkedAt.toISOString()
|
|
35107
|
+
});
|
|
35108
|
+
if (historyError) {
|
|
35109
|
+
console.error(`Failed to record history for ${result.keyword}:`, historyError);
|
|
35075
35110
|
}
|
|
35076
35111
|
}
|
|
35077
35112
|
/**
|
|
35078
|
-
*
|
|
35079
|
-
* Note: Full history requires keyword_ranking_history table with keyword_ranking_id
|
|
35080
|
-
* For now, returns current state as single history entry
|
|
35113
|
+
* Ranking history for a keyword: one entry per check in the last `days`, newest first.
|
|
35081
35114
|
*/
|
|
35082
|
-
async getHistory(keywordId,
|
|
35083
|
-
const { data, error } = await this.supabase.from("
|
|
35115
|
+
async getHistory(keywordId, days = 30) {
|
|
35116
|
+
const { data, error } = await this.supabase.from("keyword_position_history").select("position, url, checked_at").eq("keyword_id", keywordId).gte("checked_at", new Date(Date.now() - days * 24 * 60 * 60 * 1e3).toISOString()).order("checked_at", { ascending: false });
|
|
35084
35117
|
if (error || !data) {
|
|
35085
35118
|
return [];
|
|
35086
35119
|
}
|
|
35087
|
-
return
|
|
35088
|
-
position:
|
|
35089
|
-
url:
|
|
35120
|
+
return data.map((row) => ({
|
|
35121
|
+
position: row.position,
|
|
35122
|
+
url: row.url,
|
|
35090
35123
|
serpFeatures: [],
|
|
35091
|
-
recordedAt:
|
|
35092
|
-
}
|
|
35124
|
+
recordedAt: new Date(row.checked_at)
|
|
35125
|
+
}));
|
|
35093
35126
|
}
|
|
35094
35127
|
/**
|
|
35095
35128
|
* Get keyword trends for a project
|
|
@@ -35594,14 +35627,17 @@ var SUPPORTED_FRAMEWORKS = [
|
|
|
35594
35627
|
injectGSCVerification,
|
|
35595
35628
|
interpolatePrompt,
|
|
35596
35629
|
isGitRepo,
|
|
35630
|
+
keywordLLM,
|
|
35597
35631
|
listFiles,
|
|
35598
35632
|
loadAgent,
|
|
35599
35633
|
loadAgentByName,
|
|
35600
35634
|
mergePages,
|
|
35635
|
+
openAIKeywordLLM,
|
|
35601
35636
|
optimizeForAI,
|
|
35602
35637
|
optimizeReadme,
|
|
35603
35638
|
parseGEOResponse,
|
|
35604
35639
|
parseGSCResponse,
|
|
35640
|
+
parseJsonReply,
|
|
35605
35641
|
parseSitemap,
|
|
35606
35642
|
prioritizeKeywords,
|
|
35607
35643
|
processWizardResponse,
|
package/dist/index.mjs
CHANGED
|
@@ -16564,6 +16564,50 @@ async function extractSeedKeywords(url) {
|
|
|
16564
16564
|
}
|
|
16565
16565
|
}
|
|
16566
16566
|
|
|
16567
|
+
// src/keywords/llm.ts
|
|
16568
|
+
import OpenAI from "openai";
|
|
16569
|
+
function openAIKeywordLLM(apiKey, opts = {}) {
|
|
16570
|
+
const openai = new OpenAI({ apiKey });
|
|
16571
|
+
const openAIModel = opts.model;
|
|
16572
|
+
return {
|
|
16573
|
+
async complete(prompt, opts2 = {}) {
|
|
16574
|
+
const response = await openai.chat.completions.create({
|
|
16575
|
+
model: opts2.best ? "gpt-4o" : openAIModel ?? "gpt-4o-mini",
|
|
16576
|
+
messages: [
|
|
16577
|
+
...opts2.system ? [{ role: "system", content: opts2.system }] : [],
|
|
16578
|
+
{ role: "user", content: prompt }
|
|
16579
|
+
],
|
|
16580
|
+
response_format: { type: "json_object" },
|
|
16581
|
+
temperature: opts2.temperature ?? 0.3,
|
|
16582
|
+
...opts2.maxTokens ? { max_tokens: opts2.maxTokens } : {}
|
|
16583
|
+
});
|
|
16584
|
+
return response.choices[0]?.message?.content || "";
|
|
16585
|
+
},
|
|
16586
|
+
async embed(texts) {
|
|
16587
|
+
const response = await openai.embeddings.create({ model: "text-embedding-3-small", input: texts });
|
|
16588
|
+
return response.data.map((item) => item.embedding);
|
|
16589
|
+
}
|
|
16590
|
+
};
|
|
16591
|
+
}
|
|
16592
|
+
function keywordLLM(options) {
|
|
16593
|
+
return options.llm ?? (options.openaiApiKey ? openAIKeywordLLM(options.openaiApiKey) : void 0);
|
|
16594
|
+
}
|
|
16595
|
+
function parseJsonReply(text) {
|
|
16596
|
+
const cleaned = text.replace(/<think>[\s\S]*?<\/think>/gi, "");
|
|
16597
|
+
const start = cleaned.indexOf("{");
|
|
16598
|
+
const end = cleaned.lastIndexOf("}");
|
|
16599
|
+
if (start === -1 || end <= start) return null;
|
|
16600
|
+
const candidate = cleaned.slice(start, end + 1);
|
|
16601
|
+
for (const attempt of [candidate, candidate.replace(/,\s*([}\]])/g, "$1")]) {
|
|
16602
|
+
try {
|
|
16603
|
+
const parsed = JSON.parse(attempt);
|
|
16604
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) return parsed;
|
|
16605
|
+
} catch {
|
|
16606
|
+
}
|
|
16607
|
+
}
|
|
16608
|
+
return null;
|
|
16609
|
+
}
|
|
16610
|
+
|
|
16567
16611
|
// src/keywords/site-crawler.ts
|
|
16568
16612
|
import * as cheerio40 from "cheerio";
|
|
16569
16613
|
var EXCLUDED_PATHS = [
|
|
@@ -16773,7 +16817,11 @@ function extractKeyPhrases(crawlResult) {
|
|
|
16773
16817
|
}
|
|
16774
16818
|
|
|
16775
16819
|
// src/keywords/ai-summarizer.ts
|
|
16776
|
-
|
|
16820
|
+
function summarizerLLM(options) {
|
|
16821
|
+
const llm = options.llm ?? (options.openaiApiKey ? openAIKeywordLLM(options.openaiApiKey, { model: options.model }) : void 0);
|
|
16822
|
+
if (!llm) throw new Error("An AI provider is required to summarize the site");
|
|
16823
|
+
return llm;
|
|
16824
|
+
}
|
|
16777
16825
|
var SUMMARIZER_PROMPT = `You are an expert SEO analyst. Analyze the following website content and extract key information for keyword research.
|
|
16778
16826
|
|
|
16779
16827
|
WEBSITE CONTENT:
|
|
@@ -16816,37 +16864,26 @@ IMPORTANT:
|
|
|
16816
16864
|
|
|
16817
16865
|
Respond ONLY with the JSON object, no other text.`;
|
|
16818
16866
|
async function summarizeSite(crawlResult, options) {
|
|
16819
|
-
const
|
|
16820
|
-
const
|
|
16867
|
+
const llm = summarizerLLM(options);
|
|
16868
|
+
const maxContent = llm.maxContentChars ?? 15e3;
|
|
16821
16869
|
let content = crawlResult.aggregatedContent;
|
|
16822
|
-
if (content.length >
|
|
16823
|
-
content = content.substring(0,
|
|
16870
|
+
if (content.length > maxContent) {
|
|
16871
|
+
content = content.substring(0, maxContent) + "\n...[truncated]";
|
|
16824
16872
|
}
|
|
16825
16873
|
const headings = crawlResult.uniqueHeadings.slice(0, 30).join("\n");
|
|
16826
16874
|
const prompt = SUMMARIZER_PROMPT.replace("{content}", content).replace("{headings}", headings).replace("{productPages}", String(crawlResult.detectedPageTypes.product)).replace("{pricingPages}", String(crawlResult.detectedPageTypes.pricing)).replace("{blogPages}", String(crawlResult.detectedPageTypes.blog)).replace("{featurePages}", String(crawlResult.detectedPageTypes.feature));
|
|
16827
16875
|
console.log("\u{1F916} Analyzing site content with AI...");
|
|
16828
16876
|
try {
|
|
16829
|
-
const
|
|
16830
|
-
|
|
16831
|
-
messages: [
|
|
16832
|
-
{
|
|
16833
|
-
role: "system",
|
|
16834
|
-
content: "You are an expert SEO analyst. Always respond with valid JSON only."
|
|
16835
|
-
},
|
|
16836
|
-
{
|
|
16837
|
-
role: "user",
|
|
16838
|
-
content: prompt
|
|
16839
|
-
}
|
|
16840
|
-
],
|
|
16877
|
+
const responseText = await llm.complete(prompt, {
|
|
16878
|
+
system: "You are an expert SEO analyst. Always respond with valid JSON only.",
|
|
16841
16879
|
temperature: 0.3,
|
|
16842
|
-
|
|
16880
|
+
maxTokens: options.maxTokens || 2e3
|
|
16843
16881
|
});
|
|
16844
|
-
const
|
|
16845
|
-
|
|
16846
|
-
if (!jsonMatch) {
|
|
16882
|
+
const parsed = parseJsonReply(responseText);
|
|
16883
|
+
if (!parsed) {
|
|
16847
16884
|
throw new Error("No JSON found in response");
|
|
16848
16885
|
}
|
|
16849
|
-
const summary =
|
|
16886
|
+
const summary = parsed;
|
|
16850
16887
|
return {
|
|
16851
16888
|
productName: summary.productName || "Unknown Product",
|
|
16852
16889
|
productDescription: summary.productDescription || "No description available",
|
|
@@ -16908,7 +16945,7 @@ function createFallbackSummary(crawlResult) {
|
|
|
16908
16945
|
};
|
|
16909
16946
|
}
|
|
16910
16947
|
async function enhanceSummaryWithCompetitors(summary, competitors, options) {
|
|
16911
|
-
const
|
|
16948
|
+
const llm = summarizerLLM(options);
|
|
16912
16949
|
const prompt = `Given this product summary:
|
|
16913
16950
|
Product: ${summary.productName}
|
|
16914
16951
|
Description: ${summary.productDescription}
|
|
@@ -16927,19 +16964,13 @@ Analyze the competitive positioning. If this product focuses on a niche segment
|
|
|
16927
16964
|
"updatedUncertainties": ["uncertainty1", "uncertainty2"]
|
|
16928
16965
|
}`;
|
|
16929
16966
|
try {
|
|
16930
|
-
const
|
|
16931
|
-
|
|
16932
|
-
messages: [
|
|
16933
|
-
{ role: "system", content: "Respond with valid JSON only." },
|
|
16934
|
-
{ role: "user", content: prompt }
|
|
16935
|
-
],
|
|
16967
|
+
const responseText = await llm.complete(prompt, {
|
|
16968
|
+
system: "Respond with valid JSON only.",
|
|
16936
16969
|
temperature: 0.3,
|
|
16937
|
-
|
|
16970
|
+
maxTokens: 1e3
|
|
16938
16971
|
});
|
|
16939
|
-
const
|
|
16940
|
-
|
|
16941
|
-
if (jsonMatch) {
|
|
16942
|
-
const analysis = JSON.parse(jsonMatch[0]);
|
|
16972
|
+
const analysis = parseJsonReply(responseText);
|
|
16973
|
+
if (analysis) {
|
|
16943
16974
|
return {
|
|
16944
16975
|
...summary,
|
|
16945
16976
|
competitivePositioning: analysis.competitivePositioning || "",
|
|
@@ -17026,7 +17057,6 @@ function generateUncertaintyQuestions(summary) {
|
|
|
17026
17057
|
}
|
|
17027
17058
|
|
|
17028
17059
|
// src/keywords/nlp-analysis.ts
|
|
17029
|
-
import OpenAI2 from "openai";
|
|
17030
17060
|
var STOP_WORDS2 = /* @__PURE__ */ new Set([
|
|
17031
17061
|
"a",
|
|
17032
17062
|
"an",
|
|
@@ -17311,19 +17341,18 @@ function createTermVectors(keywords, vocabulary) {
|
|
|
17311
17341
|
return vector;
|
|
17312
17342
|
});
|
|
17313
17343
|
}
|
|
17344
|
+
async function embedBatch(embedder, batch) {
|
|
17345
|
+
if (typeof embedder === "function") return embedder(batch);
|
|
17346
|
+
const response = await embedder.embeddings.create({ model: "text-embedding-3-small", input: batch });
|
|
17347
|
+
return response.data.map((item) => item.embedding);
|
|
17348
|
+
}
|
|
17314
17349
|
async function getEmbeddings(texts, openai) {
|
|
17315
17350
|
const embeddings = [];
|
|
17316
17351
|
const batchSize = 100;
|
|
17317
17352
|
for (let i = 0; i < texts.length; i += batchSize) {
|
|
17318
17353
|
const batch = texts.slice(i, i + batchSize);
|
|
17319
17354
|
try {
|
|
17320
|
-
|
|
17321
|
-
model: "text-embedding-3-small",
|
|
17322
|
-
input: batch
|
|
17323
|
-
});
|
|
17324
|
-
for (const item of response.data) {
|
|
17325
|
-
embeddings.push(item.embedding);
|
|
17326
|
-
}
|
|
17355
|
+
embeddings.push(...await embedBatch(openai, batch));
|
|
17327
17356
|
} catch (error) {
|
|
17328
17357
|
console.warn("Embedding API failed, using fallback BOW vectors");
|
|
17329
17358
|
for (const text of batch) {
|
|
@@ -17478,12 +17507,12 @@ async function runNLPAnalysis(content, options = {}) {
|
|
|
17478
17507
|
const entityPhrases = extractEntityPhrases(combinedText);
|
|
17479
17508
|
let clusters = [];
|
|
17480
17509
|
const keywordsToCluster = tfidfKeywords.slice(0, 50).map((k) => k.term);
|
|
17481
|
-
|
|
17510
|
+
const embed = keywordLLM(options)?.embed;
|
|
17511
|
+
if (embed && keywordsToCluster.length > 0) {
|
|
17482
17512
|
try {
|
|
17483
|
-
const openai = new OpenAI2({ apiKey: options.openaiApiKey });
|
|
17484
17513
|
clusters = await clusterKeywordsByEmbedding(
|
|
17485
17514
|
keywordsToCluster,
|
|
17486
|
-
|
|
17515
|
+
(texts) => embed(texts),
|
|
17487
17516
|
options.numClusters
|
|
17488
17517
|
);
|
|
17489
17518
|
} catch (error) {
|
|
@@ -17826,9 +17855,13 @@ async function searchHackerNews(query, maxResults = 10) {
|
|
|
17826
17855
|
}
|
|
17827
17856
|
|
|
17828
17857
|
// src/keywords/llm-judge.ts
|
|
17829
|
-
|
|
17858
|
+
function judgeLLM(options) {
|
|
17859
|
+
const llm = keywordLLM(options);
|
|
17860
|
+
if (!llm) throw new Error("An AI provider is required to judge tool ideas");
|
|
17861
|
+
return llm;
|
|
17862
|
+
}
|
|
17830
17863
|
async function evaluateToolFeasibility(toolIdea, siteSummary, competitiveData, options) {
|
|
17831
|
-
const
|
|
17864
|
+
const llm = judgeLLM(options);
|
|
17832
17865
|
const minFeasibility = options.minFeasibilityScore ?? 3;
|
|
17833
17866
|
const minSpecificity = options.minSpecificityScore ?? 3;
|
|
17834
17867
|
const minProductFit = options.minProductFitScore ?? 2;
|
|
@@ -17904,13 +17937,10 @@ Respond in JSON:
|
|
|
17904
17937
|
"blockers": ["<issues that prevent building>"],
|
|
17905
17938
|
"improvements": ["<suggestions to make it more specific/buildable>"]
|
|
17906
17939
|
}`;
|
|
17907
|
-
const
|
|
17908
|
-
|
|
17909
|
-
|
|
17910
|
-
|
|
17911
|
-
temperature: 0.3
|
|
17912
|
-
});
|
|
17913
|
-
const result = JSON.parse(response.choices[0].message.content || "{}");
|
|
17940
|
+
const result = parseJsonReply(await llm.complete(prompt, { temperature: 0.3 })) ?? {};
|
|
17941
|
+
for (const key of ["feasibility", "specificity", "productFit", "marketOpportunity"]) {
|
|
17942
|
+
result[key] = Number(result[key]) || 0;
|
|
17943
|
+
}
|
|
17914
17944
|
const overallScore = result.feasibility * 0.3 + result.specificity * 0.3 + result.productFit * 0.2 + result.marketOpportunity * 0.2;
|
|
17915
17945
|
const passes = result.feasibility >= minFeasibility && result.specificity >= minSpecificity && result.productFit >= minProductFit;
|
|
17916
17946
|
return {
|
|
@@ -17926,7 +17956,7 @@ Respond in JSON:
|
|
|
17926
17956
|
};
|
|
17927
17957
|
}
|
|
17928
17958
|
async function enhanceToolIdea(toolIdea, siteSummary, competitiveData, options) {
|
|
17929
|
-
const
|
|
17959
|
+
const llm = judgeLLM(options);
|
|
17930
17960
|
const feasibilityScore = await evaluateToolFeasibility(
|
|
17931
17961
|
toolIdea,
|
|
17932
17962
|
siteSummary,
|
|
@@ -17971,13 +18001,7 @@ ${competitiveData?.openSourceOptions.slice(0, 3).map((t) => `- OSS: ${t.name} ($
|
|
|
17971
18001
|
"canUseExistingOSS": <true if an OSS library does most of the work>,
|
|
17972
18002
|
"ossLibrary": "<name of OSS library to use, or null>"
|
|
17973
18003
|
}`;
|
|
17974
|
-
const
|
|
17975
|
-
model: "gpt-4o",
|
|
17976
|
-
messages: [{ role: "user", content: prompt }],
|
|
17977
|
-
response_format: { type: "json_object" },
|
|
17978
|
-
temperature: 0.5
|
|
17979
|
-
});
|
|
17980
|
-
const enhanced = JSON.parse(response.choices[0].message.content || "{}");
|
|
18004
|
+
const enhanced = parseJsonReply(await llm.complete(prompt, { temperature: 0.5, best: true })) ?? {};
|
|
17981
18005
|
const priority = Math.round(
|
|
17982
18006
|
feasibilityScore.overallScore * 20 + toolIdea.volume / 1e3 * 10 + (100 - toolIdea.difficulty) / 100 * 20
|
|
17983
18007
|
);
|
|
@@ -18028,7 +18052,7 @@ async function evaluateAndEnhanceToolIdeas(toolIdeas, siteSummary, getCompetitiv
|
|
|
18028
18052
|
|
|
18029
18053
|
// src/keywords/ai-keyword-engine.ts
|
|
18030
18054
|
async function runAIKeywordResearch(options) {
|
|
18031
|
-
const { url, tier,
|
|
18055
|
+
const { url, tier, maxPages = 20 } = options;
|
|
18032
18056
|
console.log(`
|
|
18033
18057
|
\u{1F52C} Starting AI Keyword Research (${tier} tier)...
|
|
18034
18058
|
`);
|
|
@@ -18036,10 +18060,11 @@ async function runAIKeywordResearch(options) {
|
|
|
18036
18060
|
if (tier === "free") {
|
|
18037
18061
|
return runFreeKeywordResearch(crawlResult, options);
|
|
18038
18062
|
} else {
|
|
18039
|
-
|
|
18040
|
-
|
|
18063
|
+
const llm = keywordLLM(options);
|
|
18064
|
+
if (!llm) {
|
|
18065
|
+
throw new Error("An AI provider (or an OpenAI API key) is required for paid tier keyword research");
|
|
18041
18066
|
}
|
|
18042
|
-
return runPaidKeywordResearch(crawlResult, options);
|
|
18067
|
+
return runPaidKeywordResearch(crawlResult, options, llm);
|
|
18043
18068
|
}
|
|
18044
18069
|
}
|
|
18045
18070
|
async function runFreeKeywordResearch(crawlResult, options) {
|
|
@@ -18093,13 +18118,11 @@ async function runFreeKeywordResearch(crawlResult, options) {
|
|
|
18093
18118
|
]
|
|
18094
18119
|
};
|
|
18095
18120
|
}
|
|
18096
|
-
async function runPaidKeywordResearch(crawlResult, options) {
|
|
18097
|
-
const {
|
|
18121
|
+
async function runPaidKeywordResearch(crawlResult, options, llm) {
|
|
18122
|
+
const { userContext, wizardResponses, ciMode, competitiveSearchKeys } = options;
|
|
18098
18123
|
const enableEnhancedToolIdeas = options.enableEnhancedToolIdeas !== false;
|
|
18099
18124
|
console.log("\u{1F680} Running full AI analysis...");
|
|
18100
|
-
let siteSummary = await summarizeSite(crawlResult, {
|
|
18101
|
-
openaiApiKey
|
|
18102
|
-
});
|
|
18125
|
+
let siteSummary = await summarizeSite(crawlResult, { llm });
|
|
18103
18126
|
if (userContext) {
|
|
18104
18127
|
siteSummary = applyUserContext(siteSummary, userContext);
|
|
18105
18128
|
}
|
|
@@ -18108,7 +18131,7 @@ async function runPaidKeywordResearch(crawlResult, options) {
|
|
|
18108
18131
|
}
|
|
18109
18132
|
const documents = crawlResult.pages.map((p) => p.mainContent);
|
|
18110
18133
|
const nlpAnalysis = await runNLPAnalysis(documents, {
|
|
18111
|
-
|
|
18134
|
+
llm,
|
|
18112
18135
|
numClusters: 8,
|
|
18113
18136
|
numTopics: 6
|
|
18114
18137
|
});
|
|
@@ -18135,13 +18158,13 @@ async function runPaidKeywordResearch(crawlResult, options) {
|
|
|
18135
18158
|
nlpAnalysis
|
|
18136
18159
|
);
|
|
18137
18160
|
let freeToolIdeas;
|
|
18138
|
-
if (enableEnhancedToolIdeas
|
|
18161
|
+
if (enableEnhancedToolIdeas) {
|
|
18139
18162
|
console.log("\u{1F52C} Running enhanced tool idea analysis with competitive research...");
|
|
18140
18163
|
freeToolIdeas = await generateEnhancedFreeToolIdeas(
|
|
18141
18164
|
opportunities,
|
|
18142
18165
|
siteSummary,
|
|
18143
18166
|
{
|
|
18144
|
-
|
|
18167
|
+
llm,
|
|
18145
18168
|
...competitiveSearchKeys
|
|
18146
18169
|
}
|
|
18147
18170
|
);
|
|
@@ -18239,7 +18262,7 @@ async function generateEnhancedFreeToolIdeas(opportunities, summary, options) {
|
|
|
18239
18262
|
kw.volume >= 300 && // Slightly lower threshold
|
|
18240
18263
|
(kw.keyword.includes("generator") || kw.keyword.includes("tool") || kw.keyword.includes("maker") || kw.keyword.includes("builder") || kw.keyword.includes("converter") || kw.keyword.includes("calculator") || kw.keyword.includes("checker") || kw.keyword.includes("validator") || kw.keyword.includes("formatter") || kw.keyword.includes("free") || kw.intent === "informational")
|
|
18241
18264
|
);
|
|
18242
|
-
const basicIdeas = toolKeywords.slice(0, 15).map((kw) => ({
|
|
18265
|
+
const basicIdeas = toolKeywords.slice(0, options.llm.maxToolIdeas ?? 15).map((kw) => ({
|
|
18243
18266
|
keyword: kw.keyword,
|
|
18244
18267
|
toolName: generateToolName(kw.keyword),
|
|
18245
18268
|
toolDescription: generateToolDescription(kw.keyword, summary),
|
|
@@ -18269,7 +18292,7 @@ async function generateEnhancedFreeToolIdeas(opportunities, summary, options) {
|
|
|
18269
18292
|
basicIdeas,
|
|
18270
18293
|
summary,
|
|
18271
18294
|
getCompetitiveData,
|
|
18272
|
-
{
|
|
18295
|
+
{ llm: options.llm }
|
|
18273
18296
|
);
|
|
18274
18297
|
return enhancedIdeas.map((enhanced) => ({
|
|
18275
18298
|
keyword: enhanced.keyword,
|
|
@@ -18600,10 +18623,9 @@ async function startWizardSession(url, options = {}) {
|
|
|
18600
18623
|
console.log("\u{1F50D} Analyzing your site...");
|
|
18601
18624
|
const crawlResult = await crawlSite(url, { maxPages: 10 });
|
|
18602
18625
|
let siteSummary;
|
|
18603
|
-
|
|
18604
|
-
|
|
18605
|
-
|
|
18606
|
-
});
|
|
18626
|
+
const llm = keywordLLM(options);
|
|
18627
|
+
if (llm) {
|
|
18628
|
+
siteSummary = await summarizeSite(crawlResult, { llm });
|
|
18607
18629
|
} else {
|
|
18608
18630
|
siteSummary = {
|
|
18609
18631
|
productName: crawlResult.domain,
|
|
@@ -31517,24 +31539,32 @@ var RankTracker = class {
|
|
|
31517
31539
|
}).eq("id", keywordId);
|
|
31518
31540
|
if (updateError) {
|
|
31519
31541
|
console.error(`Failed to save ranking for ${result.keyword}:`, updateError);
|
|
31542
|
+
return;
|
|
31543
|
+
}
|
|
31544
|
+
const { error: historyError } = await this.supabase.from("keyword_position_history").insert({
|
|
31545
|
+
keyword_id: keywordId,
|
|
31546
|
+
position: result.position,
|
|
31547
|
+
url: result.url,
|
|
31548
|
+
checked_at: result.checkedAt.toISOString()
|
|
31549
|
+
});
|
|
31550
|
+
if (historyError) {
|
|
31551
|
+
console.error(`Failed to record history for ${result.keyword}:`, historyError);
|
|
31520
31552
|
}
|
|
31521
31553
|
}
|
|
31522
31554
|
/**
|
|
31523
|
-
*
|
|
31524
|
-
* Note: Full history requires keyword_ranking_history table with keyword_ranking_id
|
|
31525
|
-
* For now, returns current state as single history entry
|
|
31555
|
+
* Ranking history for a keyword: one entry per check in the last `days`, newest first.
|
|
31526
31556
|
*/
|
|
31527
|
-
async getHistory(keywordId,
|
|
31528
|
-
const { data, error } = await this.supabase.from("
|
|
31557
|
+
async getHistory(keywordId, days = 30) {
|
|
31558
|
+
const { data, error } = await this.supabase.from("keyword_position_history").select("position, url, checked_at").eq("keyword_id", keywordId).gte("checked_at", new Date(Date.now() - days * 24 * 60 * 60 * 1e3).toISOString()).order("checked_at", { ascending: false });
|
|
31529
31559
|
if (error || !data) {
|
|
31530
31560
|
return [];
|
|
31531
31561
|
}
|
|
31532
|
-
return
|
|
31533
|
-
position:
|
|
31534
|
-
url:
|
|
31562
|
+
return data.map((row) => ({
|
|
31563
|
+
position: row.position,
|
|
31564
|
+
url: row.url,
|
|
31535
31565
|
serpFeatures: [],
|
|
31536
|
-
recordedAt:
|
|
31537
|
-
}
|
|
31566
|
+
recordedAt: new Date(row.checked_at)
|
|
31567
|
+
}));
|
|
31538
31568
|
}
|
|
31539
31569
|
/**
|
|
31540
31570
|
* Get keyword trends for a project
|
|
@@ -32031,14 +32061,17 @@ export {
|
|
|
32031
32061
|
injectGSCVerification,
|
|
32032
32062
|
interpolatePrompt,
|
|
32033
32063
|
isGitRepo,
|
|
32064
|
+
keywordLLM,
|
|
32034
32065
|
listFiles,
|
|
32035
32066
|
loadAgent,
|
|
32036
32067
|
loadAgentByName,
|
|
32037
32068
|
mergePages,
|
|
32069
|
+
openAIKeywordLLM,
|
|
32038
32070
|
optimizeForAI,
|
|
32039
32071
|
optimizeReadme,
|
|
32040
32072
|
parseGEOResponse,
|
|
32041
32073
|
parseGSCResponse,
|
|
32074
|
+
parseJsonReply,
|
|
32042
32075
|
parseSitemap,
|
|
32043
32076
|
prioritizeKeywords,
|
|
32044
32077
|
processWizardResponse,
|