@apitap/core 1.0.0

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 (236) hide show
  1. package/LICENSE +60 -0
  2. package/README.md +362 -0
  3. package/SKILL.md +270 -0
  4. package/dist/auth/crypto.d.ts +31 -0
  5. package/dist/auth/crypto.js +66 -0
  6. package/dist/auth/crypto.js.map +1 -0
  7. package/dist/auth/handoff.d.ts +29 -0
  8. package/dist/auth/handoff.js +180 -0
  9. package/dist/auth/handoff.js.map +1 -0
  10. package/dist/auth/manager.d.ts +46 -0
  11. package/dist/auth/manager.js +127 -0
  12. package/dist/auth/manager.js.map +1 -0
  13. package/dist/auth/oauth-refresh.d.ts +16 -0
  14. package/dist/auth/oauth-refresh.js +91 -0
  15. package/dist/auth/oauth-refresh.js.map +1 -0
  16. package/dist/auth/refresh.d.ts +43 -0
  17. package/dist/auth/refresh.js +217 -0
  18. package/dist/auth/refresh.js.map +1 -0
  19. package/dist/capture/anti-bot.d.ts +15 -0
  20. package/dist/capture/anti-bot.js +43 -0
  21. package/dist/capture/anti-bot.js.map +1 -0
  22. package/dist/capture/blocklist.d.ts +6 -0
  23. package/dist/capture/blocklist.js +70 -0
  24. package/dist/capture/blocklist.js.map +1 -0
  25. package/dist/capture/body-diff.d.ts +8 -0
  26. package/dist/capture/body-diff.js +102 -0
  27. package/dist/capture/body-diff.js.map +1 -0
  28. package/dist/capture/body-variables.d.ts +13 -0
  29. package/dist/capture/body-variables.js +142 -0
  30. package/dist/capture/body-variables.js.map +1 -0
  31. package/dist/capture/domain.d.ts +8 -0
  32. package/dist/capture/domain.js +34 -0
  33. package/dist/capture/domain.js.map +1 -0
  34. package/dist/capture/entropy.d.ts +33 -0
  35. package/dist/capture/entropy.js +100 -0
  36. package/dist/capture/entropy.js.map +1 -0
  37. package/dist/capture/filter.d.ts +11 -0
  38. package/dist/capture/filter.js +49 -0
  39. package/dist/capture/filter.js.map +1 -0
  40. package/dist/capture/graphql.d.ts +21 -0
  41. package/dist/capture/graphql.js +99 -0
  42. package/dist/capture/graphql.js.map +1 -0
  43. package/dist/capture/idle.d.ts +23 -0
  44. package/dist/capture/idle.js +44 -0
  45. package/dist/capture/idle.js.map +1 -0
  46. package/dist/capture/monitor.d.ts +26 -0
  47. package/dist/capture/monitor.js +183 -0
  48. package/dist/capture/monitor.js.map +1 -0
  49. package/dist/capture/oauth-detector.d.ts +18 -0
  50. package/dist/capture/oauth-detector.js +96 -0
  51. package/dist/capture/oauth-detector.js.map +1 -0
  52. package/dist/capture/pagination.d.ts +9 -0
  53. package/dist/capture/pagination.js +40 -0
  54. package/dist/capture/pagination.js.map +1 -0
  55. package/dist/capture/parameterize.d.ts +17 -0
  56. package/dist/capture/parameterize.js +63 -0
  57. package/dist/capture/parameterize.js.map +1 -0
  58. package/dist/capture/scrubber.d.ts +5 -0
  59. package/dist/capture/scrubber.js +38 -0
  60. package/dist/capture/scrubber.js.map +1 -0
  61. package/dist/capture/session.d.ts +46 -0
  62. package/dist/capture/session.js +445 -0
  63. package/dist/capture/session.js.map +1 -0
  64. package/dist/capture/token-detector.d.ts +16 -0
  65. package/dist/capture/token-detector.js +62 -0
  66. package/dist/capture/token-detector.js.map +1 -0
  67. package/dist/capture/verifier.d.ts +17 -0
  68. package/dist/capture/verifier.js +147 -0
  69. package/dist/capture/verifier.js.map +1 -0
  70. package/dist/cli.d.ts +2 -0
  71. package/dist/cli.js +930 -0
  72. package/dist/cli.js.map +1 -0
  73. package/dist/discovery/auth.d.ts +17 -0
  74. package/dist/discovery/auth.js +81 -0
  75. package/dist/discovery/auth.js.map +1 -0
  76. package/dist/discovery/fetch.d.ts +17 -0
  77. package/dist/discovery/fetch.js +59 -0
  78. package/dist/discovery/fetch.js.map +1 -0
  79. package/dist/discovery/frameworks.d.ts +11 -0
  80. package/dist/discovery/frameworks.js +249 -0
  81. package/dist/discovery/frameworks.js.map +1 -0
  82. package/dist/discovery/index.d.ts +21 -0
  83. package/dist/discovery/index.js +219 -0
  84. package/dist/discovery/index.js.map +1 -0
  85. package/dist/discovery/openapi.d.ts +13 -0
  86. package/dist/discovery/openapi.js +175 -0
  87. package/dist/discovery/openapi.js.map +1 -0
  88. package/dist/discovery/probes.d.ts +9 -0
  89. package/dist/discovery/probes.js +70 -0
  90. package/dist/discovery/probes.js.map +1 -0
  91. package/dist/index.d.ts +25 -0
  92. package/dist/index.js +25 -0
  93. package/dist/index.js.map +1 -0
  94. package/dist/inspect/report.d.ts +52 -0
  95. package/dist/inspect/report.js +191 -0
  96. package/dist/inspect/report.js.map +1 -0
  97. package/dist/mcp.d.ts +8 -0
  98. package/dist/mcp.js +526 -0
  99. package/dist/mcp.js.map +1 -0
  100. package/dist/orchestration/browse.d.ts +38 -0
  101. package/dist/orchestration/browse.js +198 -0
  102. package/dist/orchestration/browse.js.map +1 -0
  103. package/dist/orchestration/cache.d.ts +15 -0
  104. package/dist/orchestration/cache.js +24 -0
  105. package/dist/orchestration/cache.js.map +1 -0
  106. package/dist/plugin.d.ts +17 -0
  107. package/dist/plugin.js +158 -0
  108. package/dist/plugin.js.map +1 -0
  109. package/dist/read/decoders/deepwiki.d.ts +2 -0
  110. package/dist/read/decoders/deepwiki.js +148 -0
  111. package/dist/read/decoders/deepwiki.js.map +1 -0
  112. package/dist/read/decoders/grokipedia.d.ts +2 -0
  113. package/dist/read/decoders/grokipedia.js +210 -0
  114. package/dist/read/decoders/grokipedia.js.map +1 -0
  115. package/dist/read/decoders/hackernews.d.ts +2 -0
  116. package/dist/read/decoders/hackernews.js +168 -0
  117. package/dist/read/decoders/hackernews.js.map +1 -0
  118. package/dist/read/decoders/index.d.ts +2 -0
  119. package/dist/read/decoders/index.js +12 -0
  120. package/dist/read/decoders/index.js.map +1 -0
  121. package/dist/read/decoders/reddit.d.ts +2 -0
  122. package/dist/read/decoders/reddit.js +142 -0
  123. package/dist/read/decoders/reddit.js.map +1 -0
  124. package/dist/read/decoders/twitter.d.ts +12 -0
  125. package/dist/read/decoders/twitter.js +187 -0
  126. package/dist/read/decoders/twitter.js.map +1 -0
  127. package/dist/read/decoders/wikipedia.d.ts +2 -0
  128. package/dist/read/decoders/wikipedia.js +66 -0
  129. package/dist/read/decoders/wikipedia.js.map +1 -0
  130. package/dist/read/decoders/youtube.d.ts +2 -0
  131. package/dist/read/decoders/youtube.js +69 -0
  132. package/dist/read/decoders/youtube.js.map +1 -0
  133. package/dist/read/extract.d.ts +25 -0
  134. package/dist/read/extract.js +320 -0
  135. package/dist/read/extract.js.map +1 -0
  136. package/dist/read/index.d.ts +14 -0
  137. package/dist/read/index.js +66 -0
  138. package/dist/read/index.js.map +1 -0
  139. package/dist/read/peek.d.ts +9 -0
  140. package/dist/read/peek.js +137 -0
  141. package/dist/read/peek.js.map +1 -0
  142. package/dist/read/types.d.ts +44 -0
  143. package/dist/read/types.js +3 -0
  144. package/dist/read/types.js.map +1 -0
  145. package/dist/replay/engine.d.ts +53 -0
  146. package/dist/replay/engine.js +441 -0
  147. package/dist/replay/engine.js.map +1 -0
  148. package/dist/replay/truncate.d.ts +16 -0
  149. package/dist/replay/truncate.js +92 -0
  150. package/dist/replay/truncate.js.map +1 -0
  151. package/dist/serve.d.ts +31 -0
  152. package/dist/serve.js +149 -0
  153. package/dist/serve.js.map +1 -0
  154. package/dist/skill/generator.d.ts +44 -0
  155. package/dist/skill/generator.js +419 -0
  156. package/dist/skill/generator.js.map +1 -0
  157. package/dist/skill/importer.d.ts +26 -0
  158. package/dist/skill/importer.js +80 -0
  159. package/dist/skill/importer.js.map +1 -0
  160. package/dist/skill/search.d.ts +19 -0
  161. package/dist/skill/search.js +51 -0
  162. package/dist/skill/search.js.map +1 -0
  163. package/dist/skill/signing.d.ts +16 -0
  164. package/dist/skill/signing.js +34 -0
  165. package/dist/skill/signing.js.map +1 -0
  166. package/dist/skill/ssrf.d.ts +27 -0
  167. package/dist/skill/ssrf.js +210 -0
  168. package/dist/skill/ssrf.js.map +1 -0
  169. package/dist/skill/store.d.ts +7 -0
  170. package/dist/skill/store.js +93 -0
  171. package/dist/skill/store.js.map +1 -0
  172. package/dist/stats/report.d.ts +26 -0
  173. package/dist/stats/report.js +157 -0
  174. package/dist/stats/report.js.map +1 -0
  175. package/dist/types.d.ts +214 -0
  176. package/dist/types.js +3 -0
  177. package/dist/types.js.map +1 -0
  178. package/package.json +58 -0
  179. package/src/auth/crypto.ts +92 -0
  180. package/src/auth/handoff.ts +229 -0
  181. package/src/auth/manager.ts +140 -0
  182. package/src/auth/oauth-refresh.ts +120 -0
  183. package/src/auth/refresh.ts +300 -0
  184. package/src/capture/anti-bot.ts +63 -0
  185. package/src/capture/blocklist.ts +75 -0
  186. package/src/capture/body-diff.ts +109 -0
  187. package/src/capture/body-variables.ts +156 -0
  188. package/src/capture/domain.ts +34 -0
  189. package/src/capture/entropy.ts +121 -0
  190. package/src/capture/filter.ts +56 -0
  191. package/src/capture/graphql.ts +124 -0
  192. package/src/capture/idle.ts +45 -0
  193. package/src/capture/monitor.ts +224 -0
  194. package/src/capture/oauth-detector.ts +106 -0
  195. package/src/capture/pagination.ts +49 -0
  196. package/src/capture/parameterize.ts +68 -0
  197. package/src/capture/scrubber.ts +49 -0
  198. package/src/capture/session.ts +502 -0
  199. package/src/capture/token-detector.ts +76 -0
  200. package/src/capture/verifier.ts +171 -0
  201. package/src/cli.ts +1031 -0
  202. package/src/discovery/auth.ts +99 -0
  203. package/src/discovery/fetch.ts +85 -0
  204. package/src/discovery/frameworks.ts +231 -0
  205. package/src/discovery/index.ts +256 -0
  206. package/src/discovery/openapi.ts +230 -0
  207. package/src/discovery/probes.ts +76 -0
  208. package/src/index.ts +26 -0
  209. package/src/inspect/report.ts +247 -0
  210. package/src/mcp.ts +618 -0
  211. package/src/orchestration/browse.ts +250 -0
  212. package/src/orchestration/cache.ts +37 -0
  213. package/src/plugin.ts +188 -0
  214. package/src/read/decoders/deepwiki.ts +180 -0
  215. package/src/read/decoders/grokipedia.ts +246 -0
  216. package/src/read/decoders/hackernews.ts +198 -0
  217. package/src/read/decoders/index.ts +15 -0
  218. package/src/read/decoders/reddit.ts +158 -0
  219. package/src/read/decoders/twitter.ts +211 -0
  220. package/src/read/decoders/wikipedia.ts +75 -0
  221. package/src/read/decoders/youtube.ts +75 -0
  222. package/src/read/extract.ts +396 -0
  223. package/src/read/index.ts +78 -0
  224. package/src/read/peek.ts +175 -0
  225. package/src/read/types.ts +37 -0
  226. package/src/replay/engine.ts +559 -0
  227. package/src/replay/truncate.ts +116 -0
  228. package/src/serve.ts +189 -0
  229. package/src/skill/generator.ts +473 -0
  230. package/src/skill/importer.ts +107 -0
  231. package/src/skill/search.ts +76 -0
  232. package/src/skill/signing.ts +36 -0
  233. package/src/skill/ssrf.ts +238 -0
  234. package/src/skill/store.ts +107 -0
  235. package/src/stats/report.ts +208 -0
  236. package/src/types.ts +233 -0
@@ -0,0 +1,210 @@
1
+ import { safeFetch } from '../../discovery/fetch.js';
2
+ function estimateTokens(text) {
3
+ return Math.ceil(text.length / 4);
4
+ }
5
+ /**
6
+ * Grokipedia decoder — xAI's open knowledge base (6M+ articles)
7
+ *
8
+ * API endpoints (all public, no auth):
9
+ * /api/page?slug=X&includeContent=true — Full article with citations
10
+ * /api/full-text-search?query=X&limit=N — Search with relevance scoring
11
+ * /api/stats — Site-wide stats
12
+ * /api/typeahead?query=X — Autocomplete
13
+ * /api/list-pages?limit=N — Browse articles
14
+ * /api/top-contributors?limit=N — Top editors
15
+ * /api/list-edit-requests?limit=N — Recent edits
16
+ */
17
+ const GROKIPEDIA_API = 'https://grokipedia.com/api';
18
+ export const grokipediaDecoder = {
19
+ name: 'grokipedia',
20
+ patterns: [
21
+ /grokipedia\.com\/wiki\/([^#?]+)/,
22
+ /grokipedia\.com\/article\/([^#?]+)/,
23
+ /grokipedia\.com\/search\?/,
24
+ /grokipedia\.com\/?$/,
25
+ /grokipedia\.com\/?(?:\?|#|$)/,
26
+ ],
27
+ async decode(url, options = {}) {
28
+ try {
29
+ const apiBase = options._apiBaseUrl || GROKIPEDIA_API;
30
+ // Search URL: /search?q=query
31
+ const searchMatch = url.match(/grokipedia\.com\/search\?.*q=([^&#]+)/);
32
+ if (searchMatch) {
33
+ return decodeSearch(apiBase, decodeURIComponent(searchMatch[1]), url, options);
34
+ }
35
+ // Article URL: /wiki/Slug or /article/Slug
36
+ const articleMatch = url.match(/grokipedia\.com\/(?:wiki|article)\/([^#?]+)/);
37
+ if (articleMatch) {
38
+ return decodeArticle(apiBase, articleMatch[1], url, options);
39
+ }
40
+ // Homepage: return stats + trending/recent
41
+ if (/grokipedia\.com\/?(?:\?|#|$)/.test(url)) {
42
+ return decodeHomepage(apiBase, url, options);
43
+ }
44
+ return null;
45
+ }
46
+ catch {
47
+ return null;
48
+ }
49
+ },
50
+ };
51
+ async function decodeArticle(apiBase, slug, url, options) {
52
+ const apiUrl = `${apiBase}/page?slug=${encodeURIComponent(slug)}&includeContent=true`;
53
+ // Grokipedia articles can be very large (743KB+ for Elon Musk) — raise body limit to 2MB
54
+ const result = await safeFetch(apiUrl, { skipSsrf: options.skipSsrf, maxBodySize: 2 * 1024 * 1024 });
55
+ if (!result || result.status !== 200)
56
+ return null;
57
+ let data;
58
+ try {
59
+ data = JSON.parse(result.body);
60
+ }
61
+ catch {
62
+ return null;
63
+ }
64
+ const page = data?.page;
65
+ if (!page)
66
+ return null;
67
+ const title = page.title || decodeURIComponent(slug).replace(/_/g, ' ');
68
+ const content = page.content || page.description || '';
69
+ const citations = page.citations || [];
70
+ const images = page.images || [];
71
+ const metadata = page.metadata || {};
72
+ const stats = page.stats || {};
73
+ // Truncate content if maxBytes specified
74
+ const maxChars = options.maxBytes ? options.maxBytes : 20000;
75
+ const truncatedContent = content.length > maxChars
76
+ ? content.slice(0, maxChars) + `\n\n[Truncated — full article is ${content.length} chars. ${citations.length} citations available.]`
77
+ : content;
78
+ // Build citations section (top 10)
79
+ const topCitations = citations.slice(0, 10);
80
+ const citationBlock = topCitations.length > 0
81
+ ? '\n\n## Sources\n' + topCitations.map((c, i) => `${i + 1}. [${c.title || 'Source'}](${c.url})`).join('\n')
82
+ : '';
83
+ // Build stats line
84
+ const statsLine = stats.totalViews
85
+ ? `\n\nViews: ${Number(stats.totalViews).toLocaleString()} | Quality: ${stats.qualityScore || 'N/A'} | Language: ${metadata.language || 'en'}`
86
+ : '';
87
+ const resultImages = images.slice(0, 5).map((img) => ({
88
+ alt: img.caption || title,
89
+ src: img.url || '',
90
+ }));
91
+ const resultLinks = [
92
+ { text: 'Full article', href: `https://grokipedia.com/wiki/${slug}` },
93
+ ];
94
+ // Add citation links
95
+ topCitations.forEach((c) => {
96
+ if (c.url) {
97
+ resultLinks.push({ text: c.title || 'Source', href: c.url });
98
+ }
99
+ });
100
+ return {
101
+ url,
102
+ title,
103
+ author: metadata.lastEditor || null,
104
+ description: page.description || null,
105
+ content: truncatedContent + citationBlock + statsLine,
106
+ links: resultLinks,
107
+ images: resultImages,
108
+ metadata: {
109
+ type: 'article',
110
+ publishedAt: metadata.lastModified ? new Date(metadata.lastModified * 1000).toISOString() : null,
111
+ source: 'grokipedia-api',
112
+ canonical: `https://grokipedia.com/wiki/${slug}`,
113
+ siteName: 'Grokipedia',
114
+ },
115
+ cost: { tokens: estimateTokens(truncatedContent + citationBlock + statsLine) },
116
+ };
117
+ }
118
+ async function decodeSearch(apiBase, query, url, options) {
119
+ const apiUrl = `${apiBase}/full-text-search?query=${encodeURIComponent(query)}&limit=10`;
120
+ const result = await safeFetch(apiUrl, { skipSsrf: options.skipSsrf });
121
+ if (!result || result.status !== 200)
122
+ return null;
123
+ let data;
124
+ try {
125
+ data = JSON.parse(result.body);
126
+ }
127
+ catch {
128
+ return null;
129
+ }
130
+ const results = data?.results || [];
131
+ if (results.length === 0)
132
+ return null;
133
+ const content = results.map((r, i) => {
134
+ const views = r.viewCount ? ` (${Number(r.viewCount).toLocaleString()} views)` : '';
135
+ const snippet = (r.snippet || '').replace(/<\/?em>/g, '**').replace(/\n/g, ' ').trim();
136
+ return `${i + 1}. **[${r.title}](https://grokipedia.com/wiki/${r.slug})**${views}\n ${snippet}`;
137
+ }).join('\n\n');
138
+ const links = results.map((r) => ({
139
+ text: r.title || r.slug,
140
+ href: `https://grokipedia.com/wiki/${r.slug}`,
141
+ }));
142
+ return {
143
+ url,
144
+ title: `Grokipedia search: "${query}"`,
145
+ author: null,
146
+ description: `${results.length} results for "${query}"`,
147
+ content,
148
+ links,
149
+ images: [],
150
+ metadata: {
151
+ type: 'search-results',
152
+ publishedAt: null,
153
+ source: 'grokipedia-api',
154
+ canonical: null,
155
+ siteName: 'Grokipedia',
156
+ },
157
+ cost: { tokens: estimateTokens(content) },
158
+ };
159
+ }
160
+ async function decodeHomepage(apiBase, url, options) {
161
+ // Fetch stats
162
+ const statsResult = await safeFetch(`${apiBase}/stats`, { skipSsrf: options.skipSsrf });
163
+ let statsData = {};
164
+ if (statsResult?.status === 200) {
165
+ try {
166
+ statsData = JSON.parse(statsResult.body);
167
+ }
168
+ catch { }
169
+ }
170
+ // Fetch recent edits
171
+ const editsResult = await safeFetch(`${apiBase}/list-edit-requests?limit=5`, { skipSsrf: options.skipSsrf });
172
+ let editsData = {};
173
+ if (editsResult?.status === 200) {
174
+ try {
175
+ editsData = JSON.parse(editsResult.body);
176
+ }
177
+ catch { }
178
+ }
179
+ const totalPages = Number(statsData.totalPages || 0).toLocaleString();
180
+ const indexGB = (Number(statsData.indexSizeBytes || 0) / (1024 ** 3)).toFixed(1);
181
+ let content = `# Grokipedia\n\nAn open source, comprehensive collection of all knowledge.\n\n`;
182
+ content += `**${totalPages} articles** | **${indexGB} GB index**\n\n`;
183
+ const edits = editsData.editRequests || [];
184
+ if (edits.length > 0) {
185
+ content += `## Recent Activity\n`;
186
+ for (const edit of edits) {
187
+ const article = edit.slug?.replace(/_/g, ' ') || 'Unknown';
188
+ const editor = edit.userId || 'Anonymous';
189
+ content += `- **${article}** — edited by ${editor} (${edit.type?.replace('EDIT_REQUEST_TYPE_', '').toLowerCase().replace(/_/g, ' ')})\n`;
190
+ }
191
+ }
192
+ return {
193
+ url,
194
+ title: 'Grokipedia',
195
+ author: null,
196
+ description: `Open knowledge base with ${totalPages} articles`,
197
+ content,
198
+ links: [],
199
+ images: [],
200
+ metadata: {
201
+ type: 'website',
202
+ publishedAt: null,
203
+ source: 'grokipedia-api',
204
+ canonical: 'https://grokipedia.com',
205
+ siteName: 'Grokipedia',
206
+ },
207
+ cost: { tokens: estimateTokens(content) },
208
+ };
209
+ }
210
+ //# sourceMappingURL=grokipedia.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grokipedia.js","sourceRoot":"","sources":["../../../src/read/decoders/grokipedia.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { Decoder } from '../types.js';
2
+ export declare const hackernewsDecoder: Decoder;
@@ -0,0 +1,168 @@
1
+ import { safeFetch } from '../../discovery/fetch.js';
2
+ const DEFAULT_API_BASE = 'https://hacker-news.firebaseio.com';
3
+ function estimateTokens(text) {
4
+ return Math.ceil(text.length / 4);
5
+ }
6
+ export const hackernewsDecoder = {
7
+ name: 'hackernews',
8
+ patterns: [
9
+ /news\.ycombinator\.com\/item\?id=\d+/,
10
+ /news\.ycombinator\.com\/?(?:\?|$)/,
11
+ ],
12
+ async decode(url, options = {}) {
13
+ try {
14
+ const apiBase = options._apiBaseUrl || DEFAULT_API_BASE;
15
+ const fetchOpts = { skipSsrf: options.skipSsrf };
16
+ // Check if this is an item page or front page
17
+ const itemMatch = url.match(/item\?id=(\d+)/);
18
+ if (itemMatch) {
19
+ return decodeItem(url, itemMatch[1], apiBase, fetchOpts);
20
+ }
21
+ return decodeFrontPage(url, apiBase, fetchOpts);
22
+ }
23
+ catch {
24
+ return null;
25
+ }
26
+ },
27
+ };
28
+ async function decodeItem(url, id, apiBase, fetchOpts) {
29
+ try {
30
+ const result = await safeFetch(`${apiBase}/v0/item/${id}.json`, fetchOpts);
31
+ if (!result || result.status !== 200)
32
+ return null;
33
+ let item;
34
+ try {
35
+ item = JSON.parse(result.body);
36
+ }
37
+ catch {
38
+ return null;
39
+ }
40
+ if (!item)
41
+ return null;
42
+ const title = item.title || null;
43
+ const author = item.by || null;
44
+ const score = item.score ?? 0;
45
+ const itemUrl = item.url || null;
46
+ const text = item.text || '';
47
+ // Fetch top 10 comments
48
+ const kids = item.kids || [];
49
+ const commentIds = kids.slice(0, 10);
50
+ const comments = await fetchComments(commentIds, apiBase, fetchOpts);
51
+ const commentText = comments
52
+ .map((c) => `${c.by || '[deleted]'}: ${c.text || '[deleted]'}`)
53
+ .join('\n\n');
54
+ const contentParts = [];
55
+ if (text)
56
+ contentParts.push(text);
57
+ contentParts.push(`Score: ${score} | ${kids.length} comments`);
58
+ if (commentText)
59
+ contentParts.push(`---\n${commentText}`);
60
+ const content = contentParts.join('\n\n');
61
+ const links = [];
62
+ if (itemUrl) {
63
+ links.push({ text: title || 'Link', href: itemUrl });
64
+ }
65
+ return {
66
+ url,
67
+ title,
68
+ author,
69
+ description: `HN ${item.type || 'story'} by ${author} (${score} points)`,
70
+ content,
71
+ links,
72
+ images: [],
73
+ metadata: {
74
+ type: item.type || 'story',
75
+ publishedAt: item.time ? new Date(item.time * 1000).toISOString() : null,
76
+ source: 'hackernews-firebase',
77
+ canonical: `https://news.ycombinator.com/item?id=${id}`,
78
+ siteName: 'Hacker News',
79
+ },
80
+ cost: { tokens: estimateTokens(content) },
81
+ };
82
+ }
83
+ catch {
84
+ return null;
85
+ }
86
+ }
87
+ async function decodeFrontPage(url, apiBase, fetchOpts) {
88
+ try {
89
+ const result = await safeFetch(`${apiBase}/v0/topstories.json`, fetchOpts);
90
+ if (!result || result.status !== 200)
91
+ return null;
92
+ let storyIds;
93
+ try {
94
+ storyIds = JSON.parse(result.body);
95
+ }
96
+ catch {
97
+ return null;
98
+ }
99
+ if (!Array.isArray(storyIds))
100
+ return null;
101
+ // Fetch first 10 stories
102
+ const topIds = storyIds.slice(0, 10);
103
+ const stories = await fetchStories(topIds, apiBase, fetchOpts);
104
+ const content = stories
105
+ .map((s, i) => `${i + 1}. ${s.title || '[untitled]'} (${s.score ?? 0} pts, ${(s.descendants ?? 0)} comments) by ${s.by || '[deleted]'}`)
106
+ .join('\n');
107
+ const links = stories
108
+ .filter((s) => s.url)
109
+ .map((s) => ({ text: s.title || 'Link', href: s.url }));
110
+ return {
111
+ url,
112
+ title: 'Hacker News — Top Stories',
113
+ author: null,
114
+ description: `Top ${stories.length} stories`,
115
+ content,
116
+ links,
117
+ images: [],
118
+ metadata: {
119
+ type: 'listing',
120
+ publishedAt: null,
121
+ source: 'hackernews-firebase',
122
+ canonical: 'https://news.ycombinator.com/',
123
+ siteName: 'Hacker News',
124
+ },
125
+ cost: { tokens: estimateTokens(content) },
126
+ };
127
+ }
128
+ catch {
129
+ return null;
130
+ }
131
+ }
132
+ async function fetchComments(ids, apiBase, fetchOpts) {
133
+ const comments = [];
134
+ for (const id of ids) {
135
+ try {
136
+ const result = await safeFetch(`${apiBase}/v0/item/${id}.json`, fetchOpts);
137
+ if (result && result.status === 200) {
138
+ const comment = JSON.parse(result.body);
139
+ if (comment && !comment.deleted) {
140
+ comments.push(comment);
141
+ }
142
+ }
143
+ }
144
+ catch {
145
+ // skip failed comments
146
+ }
147
+ }
148
+ return comments;
149
+ }
150
+ async function fetchStories(ids, apiBase, fetchOpts) {
151
+ const stories = [];
152
+ for (const id of ids) {
153
+ try {
154
+ const result = await safeFetch(`${apiBase}/v0/item/${id}.json`, fetchOpts);
155
+ if (result && result.status === 200) {
156
+ const story = JSON.parse(result.body);
157
+ if (story) {
158
+ stories.push(story);
159
+ }
160
+ }
161
+ }
162
+ catch {
163
+ // skip failed stories
164
+ }
165
+ }
166
+ return stories;
167
+ }
168
+ //# sourceMappingURL=hackernews.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hackernews.js","sourceRoot":"","sources":["../../../src/read/decoders/hackernews.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { Decoder } from '../types.js';
2
+ export declare function findDecoder(url: string): Decoder | null;
@@ -0,0 +1,12 @@
1
+ import { redditDecoder } from './reddit.js';
2
+ import { youtubeDecoder } from './youtube.js';
3
+ import { wikipediaDecoder } from './wikipedia.js';
4
+ import { hackernewsDecoder } from './hackernews.js';
5
+ import { grokipediaDecoder } from './grokipedia.js';
6
+ import { twitterDecoder } from './twitter.js';
7
+ import { deepwikiDecoder } from './deepwiki.js';
8
+ const decoders = [redditDecoder, youtubeDecoder, wikipediaDecoder, hackernewsDecoder, grokipediaDecoder, twitterDecoder, deepwikiDecoder];
9
+ export function findDecoder(url) {
10
+ return decoders.find(d => d.patterns.some(p => p.test(url))) ?? null;
11
+ }
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/read/decoders/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEhD,MAAM,QAAQ,GAAc,CAAC,aAAa,EAAE,cAAc,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,cAAc,EAAE,eAAe,CAAC,CAAC;AAErJ,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;AACvE,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { Decoder } from '../types.js';
2
+ export declare const redditDecoder: Decoder;
@@ -0,0 +1,142 @@
1
+ import { safeFetch } from '../../discovery/fetch.js';
2
+ function estimateTokens(text) {
3
+ return Math.ceil(text.length / 4);
4
+ }
5
+ export const redditDecoder = {
6
+ name: 'reddit',
7
+ patterns: [
8
+ /reddit\.com\/r\/[^/]+\/comments\//,
9
+ /reddit\.com\/r\/[^/]+\/?$/,
10
+ /reddit\.com\/r\/[^/]+\/?(?:\?|$)/,
11
+ /reddit\.com\/user\/[^/]+/,
12
+ ],
13
+ async decode(url, options = {}) {
14
+ try {
15
+ // Append .json to the URL to get JSON response
16
+ const jsonUrl = url.replace(/\/?(\?|$)/, '.json$1');
17
+ const result = await safeFetch(jsonUrl, { skipSsrf: options.skipSsrf });
18
+ if (!result || result.status !== 200)
19
+ return null;
20
+ let data;
21
+ try {
22
+ data = JSON.parse(result.body);
23
+ }
24
+ catch {
25
+ return null;
26
+ }
27
+ // Post page: response is an array [post, comments]
28
+ if (Array.isArray(data) && data.length >= 1) {
29
+ return decodePostPage(url, data);
30
+ }
31
+ // Subreddit/user listing: response has data.children
32
+ if (data && data.data && Array.isArray(data.data.children)) {
33
+ return decodeListingPage(url, data);
34
+ }
35
+ return null;
36
+ }
37
+ catch {
38
+ return null;
39
+ }
40
+ },
41
+ };
42
+ function decodePostPage(url, data) {
43
+ try {
44
+ const postData = data[0]?.data?.children?.[0]?.data;
45
+ if (!postData)
46
+ return null;
47
+ const title = postData.title || null;
48
+ const author = postData.author || null;
49
+ const selftext = postData.selftext || '';
50
+ const score = postData.score ?? 0;
51
+ const subreddit = postData.subreddit || '';
52
+ // Extract comments
53
+ const commentChildren = data[1]?.data?.children || [];
54
+ const comments = commentChildren
55
+ .filter((c) => c.kind === 't1' && c.data)
56
+ .slice(0, 25)
57
+ .map((c) => ({
58
+ author: c.data.author || '[deleted]',
59
+ body: c.data.body || '',
60
+ score: c.data.score ?? 0,
61
+ }));
62
+ const commentText = comments
63
+ .map((c) => `${c.author} (${c.score} pts): ${c.body}`)
64
+ .join('\n\n');
65
+ const content = selftext
66
+ ? `${selftext}\n\n---\nScore: ${score} | ${comments.length} comments\n\n${commentText}`
67
+ : `Score: ${score} | ${comments.length} comments\n\n${commentText}`;
68
+ const links = [];
69
+ if (postData.url && postData.url !== postData.permalink) {
70
+ links.push({ text: 'Link', href: postData.url });
71
+ }
72
+ return {
73
+ url,
74
+ title,
75
+ author,
76
+ description: `r/${subreddit} post by u/${author} (${score} points)`,
77
+ content,
78
+ links,
79
+ images: [],
80
+ metadata: {
81
+ type: 'discussion',
82
+ publishedAt: postData.created_utc ? new Date(postData.created_utc * 1000).toISOString() : null,
83
+ source: 'reddit-json',
84
+ canonical: postData.permalink ? `https://www.reddit.com${postData.permalink}` : null,
85
+ siteName: 'Reddit',
86
+ },
87
+ cost: { tokens: estimateTokens(content) },
88
+ };
89
+ }
90
+ catch {
91
+ return null;
92
+ }
93
+ }
94
+ function decodeListingPage(url, data) {
95
+ try {
96
+ const children = data.data.children || [];
97
+ const posts = children
98
+ .filter((c) => c.data)
99
+ .slice(0, 25)
100
+ .map((c) => ({
101
+ title: c.data.title || c.data.link_title || '',
102
+ author: c.data.author || '[deleted]',
103
+ score: c.data.score ?? 0,
104
+ numComments: c.data.num_comments ?? 0,
105
+ permalink: c.data.permalink || '',
106
+ subreddit: c.data.subreddit || '',
107
+ }));
108
+ const content = posts
109
+ .map((p, i) => `${i + 1}. ${p.title} (${p.score} pts, ${p.numComments} comments) by u/${p.author}`)
110
+ .join('\n');
111
+ const links = posts
112
+ .filter((p) => p.permalink)
113
+ .map((p) => ({ text: p.title, href: `https://www.reddit.com${p.permalink}` }));
114
+ // Try to determine subreddit name from URL
115
+ const subMatch = url.match(/\/r\/([^/]+)/);
116
+ const subreddit = subMatch ? subMatch[1] : null;
117
+ const userMatch = url.match(/\/user\/([^/]+)/);
118
+ const user = userMatch ? userMatch[1] : null;
119
+ const title = subreddit ? `r/${subreddit}` : user ? `u/${user}` : 'Reddit listing';
120
+ return {
121
+ url,
122
+ title,
123
+ author: null,
124
+ description: `${posts.length} posts`,
125
+ content,
126
+ links,
127
+ images: [],
128
+ metadata: {
129
+ type: 'listing',
130
+ publishedAt: null,
131
+ source: 'reddit-json',
132
+ canonical: null,
133
+ siteName: 'Reddit',
134
+ },
135
+ cost: { tokens: estimateTokens(content) },
136
+ };
137
+ }
138
+ catch {
139
+ return null;
140
+ }
141
+ }
142
+ //# sourceMappingURL=reddit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reddit.js","sourceRoot":"","sources":["../../../src/read/decoders/reddit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { Decoder } from '../types.js';
2
+ /**
3
+ * Twitter/X decoder — uses fxtwitter.com public API as side channel.
4
+ *
5
+ * Handles:
6
+ * - Individual tweets/posts (with text, media, quotes, articles)
7
+ * - Profile URLs (basic profile info)
8
+ *
9
+ * fxtwitter API returns full tweet JSON including embedded articles,
10
+ * media URLs, quote tweets, and engagement metrics — all without auth.
11
+ */
12
+ export declare const twitterDecoder: Decoder;