@rankcli/agent-runtime 0.0.1

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.
Files changed (178) hide show
  1. package/README.md +242 -0
  2. package/dist/analyzer-2CSWIQGD.mjs +6 -0
  3. package/dist/chunk-YNZYHEYM.mjs +774 -0
  4. package/dist/index.d.mts +4012 -0
  5. package/dist/index.d.ts +4012 -0
  6. package/dist/index.js +29672 -0
  7. package/dist/index.mjs +28602 -0
  8. package/package.json +53 -0
  9. package/scripts/build-deno.ts +134 -0
  10. package/src/audit/ai/analyzer.ts +347 -0
  11. package/src/audit/ai/index.ts +29 -0
  12. package/src/audit/ai/prompts/content-analysis.ts +271 -0
  13. package/src/audit/ai/types.ts +179 -0
  14. package/src/audit/checks/additional-checks.ts +439 -0
  15. package/src/audit/checks/ai-citation-worthiness.ts +399 -0
  16. package/src/audit/checks/ai-content-structure.ts +325 -0
  17. package/src/audit/checks/ai-readiness.ts +339 -0
  18. package/src/audit/checks/anchor-text.ts +179 -0
  19. package/src/audit/checks/answer-conciseness.ts +322 -0
  20. package/src/audit/checks/asset-minification.ts +270 -0
  21. package/src/audit/checks/bing-optimization.ts +206 -0
  22. package/src/audit/checks/brand-mention-optimization.ts +349 -0
  23. package/src/audit/checks/caching-headers.ts +305 -0
  24. package/src/audit/checks/canonical-advanced.ts +150 -0
  25. package/src/audit/checks/canonical-domain.ts +196 -0
  26. package/src/audit/checks/citation-quality.ts +358 -0
  27. package/src/audit/checks/client-rendering.ts +542 -0
  28. package/src/audit/checks/color-contrast.ts +342 -0
  29. package/src/audit/checks/content-freshness.ts +170 -0
  30. package/src/audit/checks/content-science.ts +589 -0
  31. package/src/audit/checks/conversion-elements.ts +526 -0
  32. package/src/audit/checks/crawlability.ts +220 -0
  33. package/src/audit/checks/directory-listing.ts +172 -0
  34. package/src/audit/checks/dom-analysis.ts +191 -0
  35. package/src/audit/checks/dom-size.ts +246 -0
  36. package/src/audit/checks/duplicate-content.ts +194 -0
  37. package/src/audit/checks/eeat-signals.ts +990 -0
  38. package/src/audit/checks/entity-seo.ts +396 -0
  39. package/src/audit/checks/featured-snippet.ts +473 -0
  40. package/src/audit/checks/freshness-signals.ts +443 -0
  41. package/src/audit/checks/funnel-intent.ts +463 -0
  42. package/src/audit/checks/hreflang.ts +174 -0
  43. package/src/audit/checks/html-compliance.ts +302 -0
  44. package/src/audit/checks/image-dimensions.ts +167 -0
  45. package/src/audit/checks/images.ts +160 -0
  46. package/src/audit/checks/indexnow.ts +275 -0
  47. package/src/audit/checks/interactive-tools.ts +475 -0
  48. package/src/audit/checks/internal-link-graph.ts +436 -0
  49. package/src/audit/checks/keyword-analysis.ts +239 -0
  50. package/src/audit/checks/keyword-cannibalization.ts +385 -0
  51. package/src/audit/checks/keyword-placement.ts +471 -0
  52. package/src/audit/checks/links.ts +203 -0
  53. package/src/audit/checks/llms-txt.ts +224 -0
  54. package/src/audit/checks/local-seo.ts +296 -0
  55. package/src/audit/checks/mobile.ts +167 -0
  56. package/src/audit/checks/modern-images.ts +226 -0
  57. package/src/audit/checks/navboost-signals.ts +395 -0
  58. package/src/audit/checks/on-page.ts +209 -0
  59. package/src/audit/checks/page-resources.ts +285 -0
  60. package/src/audit/checks/pagination.ts +180 -0
  61. package/src/audit/checks/performance.ts +153 -0
  62. package/src/audit/checks/platform-presence.ts +580 -0
  63. package/src/audit/checks/redirect-analysis.ts +153 -0
  64. package/src/audit/checks/redirect-chain.ts +389 -0
  65. package/src/audit/checks/resource-hints.ts +420 -0
  66. package/src/audit/checks/responsive-css.ts +247 -0
  67. package/src/audit/checks/responsive-images.ts +396 -0
  68. package/src/audit/checks/review-ecosystem.ts +415 -0
  69. package/src/audit/checks/robots-validation.ts +373 -0
  70. package/src/audit/checks/security-headers.ts +172 -0
  71. package/src/audit/checks/security.ts +144 -0
  72. package/src/audit/checks/serp-preview.ts +251 -0
  73. package/src/audit/checks/site-maturity.ts +444 -0
  74. package/src/audit/checks/social-meta.test.ts +275 -0
  75. package/src/audit/checks/social-meta.ts +134 -0
  76. package/src/audit/checks/soft-404.ts +151 -0
  77. package/src/audit/checks/structured-data.ts +238 -0
  78. package/src/audit/checks/tech-detection.ts +496 -0
  79. package/src/audit/checks/topical-clusters.ts +435 -0
  80. package/src/audit/checks/tracker-bloat.ts +462 -0
  81. package/src/audit/checks/tracking-verification.test.ts +371 -0
  82. package/src/audit/checks/tracking-verification.ts +636 -0
  83. package/src/audit/checks/url-safety.ts +682 -0
  84. package/src/audit/deno-entry.ts +66 -0
  85. package/src/audit/discovery/index.ts +15 -0
  86. package/src/audit/discovery/link-crawler.ts +232 -0
  87. package/src/audit/discovery/repo-routes.ts +347 -0
  88. package/src/audit/engine.ts +620 -0
  89. package/src/audit/fixes/index.ts +209 -0
  90. package/src/audit/fixes/social-meta-fixes.test.ts +329 -0
  91. package/src/audit/fixes/social-meta-fixes.ts +463 -0
  92. package/src/audit/index.ts +74 -0
  93. package/src/audit/runner.test.ts +299 -0
  94. package/src/audit/runner.ts +130 -0
  95. package/src/audit/types.ts +1953 -0
  96. package/src/content/featured-snippet.ts +367 -0
  97. package/src/content/generator.test.ts +534 -0
  98. package/src/content/generator.ts +501 -0
  99. package/src/content/headline.ts +317 -0
  100. package/src/content/index.ts +62 -0
  101. package/src/content/intent.ts +258 -0
  102. package/src/content/keyword-density.ts +349 -0
  103. package/src/content/readability.ts +262 -0
  104. package/src/executor.ts +336 -0
  105. package/src/fixer.ts +416 -0
  106. package/src/frameworks/detector.test.ts +248 -0
  107. package/src/frameworks/detector.ts +371 -0
  108. package/src/frameworks/index.ts +68 -0
  109. package/src/frameworks/recipes/angular.yaml +171 -0
  110. package/src/frameworks/recipes/astro.yaml +206 -0
  111. package/src/frameworks/recipes/django.yaml +180 -0
  112. package/src/frameworks/recipes/laravel.yaml +137 -0
  113. package/src/frameworks/recipes/nextjs.yaml +268 -0
  114. package/src/frameworks/recipes/nuxt.yaml +175 -0
  115. package/src/frameworks/recipes/rails.yaml +188 -0
  116. package/src/frameworks/recipes/react.yaml +202 -0
  117. package/src/frameworks/recipes/sveltekit.yaml +154 -0
  118. package/src/frameworks/recipes/vue.yaml +137 -0
  119. package/src/frameworks/recipes/wordpress.yaml +209 -0
  120. package/src/frameworks/suggestion-engine.ts +320 -0
  121. package/src/geo/geo-content.test.ts +305 -0
  122. package/src/geo/geo-content.ts +266 -0
  123. package/src/geo/geo-history.test.ts +473 -0
  124. package/src/geo/geo-history.ts +433 -0
  125. package/src/geo/geo-tracker.test.ts +359 -0
  126. package/src/geo/geo-tracker.ts +411 -0
  127. package/src/geo/index.ts +10 -0
  128. package/src/git/commit-helper.test.ts +261 -0
  129. package/src/git/commit-helper.ts +329 -0
  130. package/src/git/index.ts +12 -0
  131. package/src/git/pr-helper.test.ts +284 -0
  132. package/src/git/pr-helper.ts +307 -0
  133. package/src/index.ts +66 -0
  134. package/src/keywords/ai-keyword-engine.ts +1062 -0
  135. package/src/keywords/ai-summarizer.ts +387 -0
  136. package/src/keywords/ci-mode.ts +555 -0
  137. package/src/keywords/engine.ts +359 -0
  138. package/src/keywords/index.ts +151 -0
  139. package/src/keywords/llm-judge.ts +357 -0
  140. package/src/keywords/nlp-analysis.ts +706 -0
  141. package/src/keywords/prioritizer.ts +295 -0
  142. package/src/keywords/site-crawler.ts +342 -0
  143. package/src/keywords/sources/autocomplete.ts +139 -0
  144. package/src/keywords/sources/competitive-search.ts +450 -0
  145. package/src/keywords/sources/competitor-analysis.ts +374 -0
  146. package/src/keywords/sources/dataforseo.ts +206 -0
  147. package/src/keywords/sources/free-sources.ts +294 -0
  148. package/src/keywords/sources/gsc.ts +123 -0
  149. package/src/keywords/topic-grouping.ts +327 -0
  150. package/src/keywords/types.ts +144 -0
  151. package/src/keywords/wizard.ts +457 -0
  152. package/src/loader.ts +40 -0
  153. package/src/reports/index.ts +7 -0
  154. package/src/reports/report-generator.test.ts +293 -0
  155. package/src/reports/report-generator.ts +713 -0
  156. package/src/scheduler/alerts.test.ts +458 -0
  157. package/src/scheduler/alerts.ts +328 -0
  158. package/src/scheduler/index.ts +8 -0
  159. package/src/scheduler/scheduled-audit.test.ts +377 -0
  160. package/src/scheduler/scheduled-audit.ts +149 -0
  161. package/src/test/integration-test.ts +325 -0
  162. package/src/tools/analyzer.ts +373 -0
  163. package/src/tools/crawl.ts +293 -0
  164. package/src/tools/files.ts +301 -0
  165. package/src/tools/h1-fixer.ts +249 -0
  166. package/src/tools/index.ts +67 -0
  167. package/src/tracking/github-action.ts +326 -0
  168. package/src/tracking/google-analytics.ts +265 -0
  169. package/src/tracking/index.ts +45 -0
  170. package/src/tracking/report-generator.ts +386 -0
  171. package/src/tracking/search-console.ts +335 -0
  172. package/src/types.ts +134 -0
  173. package/src/utils/http.ts +302 -0
  174. package/src/wasm-adapter.ts +297 -0
  175. package/src/wasm-entry.ts +14 -0
  176. package/tsconfig.json +17 -0
  177. package/tsup.wasm.config.ts +26 -0
  178. package/vitest.config.ts +15 -0
@@ -0,0 +1,328 @@
1
+ /**
2
+ * Alerts Module
3
+ *
4
+ * Sends notifications via Slack, Discord, and email webhooks when SEO events occur.
5
+ */
6
+
7
+ export type AlertType = 'error' | 'score_drop' | 'weekly_report' | 'pr_created';
8
+ export type AlertChannel = 'slack' | 'discord' | 'email';
9
+ export type AlertSeverity = 'info' | 'success' | 'warning' | 'error';
10
+
11
+ export interface AlertConfig {
12
+ type: AlertChannel;
13
+ webhookUrl: string;
14
+ on: AlertType[];
15
+ scoreDropThreshold?: number;
16
+ }
17
+
18
+ export interface AuditAlertPayload {
19
+ projectId: string;
20
+ projectName: string;
21
+ url: string;
22
+ score: number;
23
+ previousScore?: number;
24
+ issuesFound: number;
25
+ errorCount?: number;
26
+ prUrl?: string;
27
+ prNumber?: number;
28
+ timestamp: string;
29
+ }
30
+
31
+ export interface AlertMessage {
32
+ title: string;
33
+ body: string;
34
+ severity: AlertSeverity;
35
+ url?: string;
36
+ }
37
+
38
+ export interface AlertResult {
39
+ success: boolean;
40
+ skipped?: boolean;
41
+ error?: string;
42
+ }
43
+
44
+ const SEVERITY_COLORS: Record<AlertSeverity, { slack: string; discord: number }> = {
45
+ info: { slack: '#2196F3', discord: 0x2196f3 },
46
+ success: { slack: '#4CAF50', discord: 0x4caf50 },
47
+ warning: { slack: '#FF9800', discord: 0xff9800 },
48
+ error: { slack: '#F44336', discord: 0xf44336 },
49
+ };
50
+
51
+ /**
52
+ * Formats an alert message based on type and payload
53
+ */
54
+ export function formatAlertMessage(type: AlertType, payload: AuditAlertPayload): AlertMessage {
55
+ const { projectName, url, score, previousScore, issuesFound, errorCount, prUrl, prNumber } = payload;
56
+
57
+ switch (type) {
58
+ case 'score_drop': {
59
+ const change = previousScore !== undefined ? score - previousScore : 0;
60
+ return {
61
+ title: `⚠️ Score Drop Alert: ${projectName}`,
62
+ body: `The SEO score for ${url} dropped from ${previousScore} to ${score} (${change} points).\n\n` +
63
+ `Issues found: ${issuesFound}`,
64
+ severity: 'warning',
65
+ url,
66
+ };
67
+ }
68
+
69
+ case 'error': {
70
+ return {
71
+ title: `🚨 Critical Issues Detected: ${projectName}`,
72
+ body: `The SEO audit for ${url} found ${errorCount || issuesFound} errors that need immediate attention.\n\n` +
73
+ `Current score: ${score}/100`,
74
+ severity: 'error',
75
+ url,
76
+ };
77
+ }
78
+
79
+ case 'weekly_report': {
80
+ const change = previousScore !== undefined ? score - previousScore : 0;
81
+ const changeText = change > 0 ? `+${change}` : change.toString();
82
+ return {
83
+ title: `📊 Weekly SEO Report: ${projectName}`,
84
+ body: `Your weekly SEO summary for ${url}:\n\n` +
85
+ `• Score: ${score}/100 (${previousScore !== undefined ? changeText + ' from last week' : 'baseline'})\n` +
86
+ `• Issues: ${issuesFound}`,
87
+ severity: 'info',
88
+ url,
89
+ };
90
+ }
91
+
92
+ case 'pr_created': {
93
+ return {
94
+ title: `✅ PR Created: ${projectName}`,
95
+ body: `A new pull request (#${prNumber}) has been created to fix SEO issues for ${url}.\n\n` +
96
+ `View PR: ${prUrl?.replace('https://', '')}`,
97
+ severity: 'success',
98
+ url: prUrl,
99
+ };
100
+ }
101
+
102
+ default:
103
+ return {
104
+ title: `SEO Alert: ${projectName}`,
105
+ body: `Score: ${score}/100, Issues: ${issuesFound}`,
106
+ severity: 'info',
107
+ url,
108
+ };
109
+ }
110
+ }
111
+
112
+ /**
113
+ * Determines if an alert should be sent based on config and payload
114
+ */
115
+ export function shouldSendAlert(
116
+ config: AlertConfig,
117
+ type: AlertType,
118
+ payload?: AuditAlertPayload
119
+ ): boolean {
120
+ // Check if type is in config.on
121
+ if (!config.on.includes(type)) {
122
+ return false;
123
+ }
124
+
125
+ // Special handling for score_drop with threshold
126
+ if (type === 'score_drop' && config.scoreDropThreshold && payload) {
127
+ const drop = (payload.previousScore ?? 0) - payload.score;
128
+ return drop >= config.scoreDropThreshold;
129
+ }
130
+
131
+ return true;
132
+ }
133
+
134
+ /**
135
+ * Formats a Slack message with blocks
136
+ */
137
+ function formatSlackMessage(message: AlertMessage): object {
138
+ const color = SEVERITY_COLORS[message.severity].slack;
139
+
140
+ return {
141
+ attachments: [
142
+ {
143
+ color,
144
+ blocks: [
145
+ {
146
+ type: 'header',
147
+ text: {
148
+ type: 'plain_text',
149
+ text: message.title,
150
+ emoji: true,
151
+ },
152
+ },
153
+ {
154
+ type: 'section',
155
+ text: {
156
+ type: 'mrkdwn',
157
+ text: message.body,
158
+ },
159
+ },
160
+ ...(message.url
161
+ ? [
162
+ {
163
+ type: 'section',
164
+ text: {
165
+ type: 'mrkdwn',
166
+ text: `<${message.url}|View Details>`,
167
+ },
168
+ },
169
+ ]
170
+ : []),
171
+ {
172
+ type: 'context',
173
+ elements: [
174
+ {
175
+ type: 'mrkdwn',
176
+ text: `🤖 Sent by SEO Autopilot`,
177
+ },
178
+ ],
179
+ },
180
+ ],
181
+ },
182
+ ],
183
+ blocks: [
184
+ {
185
+ type: 'header',
186
+ text: {
187
+ type: 'plain_text',
188
+ text: message.title,
189
+ emoji: true,
190
+ },
191
+ },
192
+ {
193
+ type: 'section',
194
+ text: {
195
+ type: 'mrkdwn',
196
+ text: message.body,
197
+ },
198
+ },
199
+ ],
200
+ };
201
+ }
202
+
203
+ /**
204
+ * Formats a Discord message with embeds
205
+ */
206
+ function formatDiscordMessage(message: AlertMessage): object {
207
+ const color = SEVERITY_COLORS[message.severity].discord;
208
+
209
+ return {
210
+ embeds: [
211
+ {
212
+ title: message.title,
213
+ description: message.body,
214
+ color,
215
+ url: message.url,
216
+ footer: {
217
+ text: '🤖 Sent by SEO Autopilot',
218
+ },
219
+ timestamp: new Date().toISOString(),
220
+ },
221
+ ],
222
+ };
223
+ }
224
+
225
+ /**
226
+ * Sends an alert to Slack webhook
227
+ */
228
+ export async function sendSlackAlert(
229
+ webhookUrl: string,
230
+ type: AlertType,
231
+ payload: AuditAlertPayload
232
+ ): Promise<AlertResult> {
233
+ const message = formatAlertMessage(type, payload);
234
+ const slackMessage = formatSlackMessage(message);
235
+
236
+ try {
237
+ const response = await fetch(webhookUrl, {
238
+ method: 'POST',
239
+ headers: { 'Content-Type': 'application/json' },
240
+ body: JSON.stringify(slackMessage),
241
+ });
242
+
243
+ if (!response.ok) {
244
+ return {
245
+ success: false,
246
+ error: `Slack webhook failed with status ${response.status}`,
247
+ };
248
+ }
249
+
250
+ return { success: true };
251
+ } catch (error) {
252
+ return {
253
+ success: false,
254
+ error: (error as Error).message,
255
+ };
256
+ }
257
+ }
258
+
259
+ /**
260
+ * Sends an alert to Discord webhook
261
+ */
262
+ export async function sendDiscordAlert(
263
+ webhookUrl: string,
264
+ type: AlertType,
265
+ payload: AuditAlertPayload
266
+ ): Promise<AlertResult> {
267
+ const message = formatAlertMessage(type, payload);
268
+ const discordMessage = formatDiscordMessage(message);
269
+
270
+ try {
271
+ const response = await fetch(webhookUrl, {
272
+ method: 'POST',
273
+ headers: { 'Content-Type': 'application/json' },
274
+ body: JSON.stringify(discordMessage),
275
+ });
276
+
277
+ if (!response.ok) {
278
+ return {
279
+ success: false,
280
+ error: `Discord webhook failed with status ${response.status}`,
281
+ };
282
+ }
283
+
284
+ return { success: true };
285
+ } catch (error) {
286
+ return {
287
+ success: false,
288
+ error: (error as Error).message,
289
+ };
290
+ }
291
+ }
292
+
293
+ /**
294
+ * Sends an alert based on config
295
+ */
296
+ export async function sendAlert(
297
+ config: AlertConfig,
298
+ type: AlertType,
299
+ payload: AuditAlertPayload
300
+ ): Promise<AlertResult> {
301
+ // Check if we should send this alert
302
+ if (!shouldSendAlert(config, type, payload)) {
303
+ return { success: true, skipped: true };
304
+ }
305
+
306
+ switch (config.type) {
307
+ case 'slack':
308
+ return sendSlackAlert(config.webhookUrl, type, payload);
309
+ case 'discord':
310
+ return sendDiscordAlert(config.webhookUrl, type, payload);
311
+ case 'email':
312
+ // Email would require SMTP setup - skip for now
313
+ return { success: true, skipped: true };
314
+ default:
315
+ return { success: false, error: `Unknown alert type: ${config.type}` };
316
+ }
317
+ }
318
+
319
+ /**
320
+ * Sends alerts to multiple configs
321
+ */
322
+ export async function sendAlerts(
323
+ configs: AlertConfig[],
324
+ type: AlertType,
325
+ payload: AuditAlertPayload
326
+ ): Promise<AlertResult[]> {
327
+ return Promise.all(configs.map((config) => sendAlert(config, type, payload)));
328
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Scheduler Module
3
+ *
4
+ * Provides scheduled execution of SEO audits with alerts and notifications.
5
+ */
6
+
7
+ export * from './scheduled-audit.js';
8
+ export * from './alerts.js';