rss-agent-discovery 0.1.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.
- package/README.md +258 -0
- package/dist/find-rss-feeds.d.ts +2 -0
- package/dist/find-rss-feeds.d.ts.map +1 -0
- package/dist/find-rss-feeds.js +345 -0
- package/dist/find-rss-feeds.js.map +1 -0
- package/package.json +40 -0
package/README.md
ADDED
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
# RSS Agent Discovery
|
|
2
|
+
|
|
3
|
+
AI agent-focused RSS feed discovery tool. JSON-only output to stdout, errors to stderr.
|
|
4
|
+
|
|
5
|
+
## Why This Tool?
|
|
6
|
+
|
|
7
|
+
Existing RSS discovery tools (`rss-url-finder`, `rss-finder`) were built for human developers. They output human-readable text and don't validate that feeds actually exist.
|
|
8
|
+
|
|
9
|
+
This tool is designed for **AI agents** (Claude, Cursor, GPT):
|
|
10
|
+
- JSON-only output to stdout (machine-parseable)
|
|
11
|
+
- Errors to stderr (separated channel)
|
|
12
|
+
- Semantic exit codes (0=found, 1=none, 2=error)
|
|
13
|
+
- Fast (10s default timeout, parallel scanning)
|
|
14
|
+
- Discovery-only (returns feed URLs, doesn't parse content)
|
|
15
|
+
- Finds feeds AI agents miss
|
|
16
|
+
|
|
17
|
+
**Proof:** Cursor fails to find `https://vercel.com/atom`. This tool succeeds.
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install
|
|
23
|
+
npm run build
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
### Basic usage:
|
|
29
|
+
```bash
|
|
30
|
+
npm start https://vercel.com
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Or via CLI (after `npm link`):
|
|
34
|
+
```bash
|
|
35
|
+
rss-discover https://vercel.com
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Multiple URLs (parallel processing):
|
|
39
|
+
```bash
|
|
40
|
+
npm start https://vercel.com https://news.ycombinator.com https://stripe.com
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Parse with jq:
|
|
44
|
+
```bash
|
|
45
|
+
npm start https://vercel.com | jq '.results[0].feeds'
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Custom timeout:
|
|
49
|
+
```bash
|
|
50
|
+
npm start --timeout 15000 https://example.com
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Skip blog scanning:
|
|
54
|
+
```bash
|
|
55
|
+
npm start --skip-blogs https://example.com
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Show help:
|
|
59
|
+
```bash
|
|
60
|
+
npm start --help
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Run tests:
|
|
64
|
+
```bash
|
|
65
|
+
npm test
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Output Schema
|
|
69
|
+
|
|
70
|
+
```json
|
|
71
|
+
{
|
|
72
|
+
"success": true,
|
|
73
|
+
"results": [
|
|
74
|
+
{
|
|
75
|
+
"url": "https://vercel.com",
|
|
76
|
+
"feeds": [
|
|
77
|
+
{
|
|
78
|
+
"url": "https://vercel.com/atom",
|
|
79
|
+
"title": "atom",
|
|
80
|
+
"type": "atom"
|
|
81
|
+
}
|
|
82
|
+
],
|
|
83
|
+
"error": null
|
|
84
|
+
}
|
|
85
|
+
]
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Exit Codes
|
|
90
|
+
|
|
91
|
+
- `0` - One or more feeds found
|
|
92
|
+
- `1` - No feeds found
|
|
93
|
+
- `2` - Error occurred
|
|
94
|
+
|
|
95
|
+
## Features
|
|
96
|
+
|
|
97
|
+
- Discovers RSS/Atom feeds from HTML `<link>` tags
|
|
98
|
+
- Tests common paths (`/rss.xml`, `/atom`, `/feed`, etc.)
|
|
99
|
+
- Scans blog subdirectories (`/blog`, `/news`, `/articles`)
|
|
100
|
+
- Parallel processing for multiple URLs
|
|
101
|
+
- Deduplicates feeds across all sources
|
|
102
|
+
- Validates feeds actually exist and return XML
|
|
103
|
+
- JSON-only output to stdout
|
|
104
|
+
- Errors to stderr
|
|
105
|
+
- 10s default timeout per URL (configurable)
|
|
106
|
+
|
|
107
|
+
## Examples
|
|
108
|
+
|
|
109
|
+
### Find feeds for Vercel:
|
|
110
|
+
```bash
|
|
111
|
+
npm start https://vercel.com
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Output:
|
|
115
|
+
```json
|
|
116
|
+
{
|
|
117
|
+
"success": true,
|
|
118
|
+
"results": [
|
|
119
|
+
{
|
|
120
|
+
"url": "https://vercel.com",
|
|
121
|
+
"feeds": [
|
|
122
|
+
{"url": "https://vercel.com/atom", "title": "atom", "type": "atom"}
|
|
123
|
+
],
|
|
124
|
+
"error": null
|
|
125
|
+
}
|
|
126
|
+
]
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Check exit code:
|
|
131
|
+
```bash
|
|
132
|
+
npm start https://vercel.com 2>/dev/null
|
|
133
|
+
echo $? # Outputs: 0
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### No feeds found:
|
|
137
|
+
```bash
|
|
138
|
+
npm start https://example.com 2>/dev/null
|
|
139
|
+
echo $? # Outputs: 1
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Parallel scan:
|
|
143
|
+
```bash
|
|
144
|
+
npm start https://vercel.com https://news.ycombinator.com | jq
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Integration Example
|
|
148
|
+
|
|
149
|
+
### Claude AI:
|
|
150
|
+
```python
|
|
151
|
+
import subprocess
|
|
152
|
+
import json
|
|
153
|
+
|
|
154
|
+
result = subprocess.run(
|
|
155
|
+
['node', 'dist/find-rss-feeds.js', 'https://vercel.com'],
|
|
156
|
+
capture_output=True,
|
|
157
|
+
text=True
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
data = json.loads(result.stdout)
|
|
161
|
+
feeds = data['results'][0]['feeds']
|
|
162
|
+
exit_code = result.returncode
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Shell script:
|
|
166
|
+
```bash
|
|
167
|
+
#!/bin/bash
|
|
168
|
+
result=$(node dist/find-rss-feeds.js "$1" 2>/dev/null)
|
|
169
|
+
exit_code=$?
|
|
170
|
+
|
|
171
|
+
if [ $exit_code -eq 0 ]; then
|
|
172
|
+
echo "Found feeds:"
|
|
173
|
+
echo "$result" | jq '.results[0].feeds'
|
|
174
|
+
fi
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## AI Agent Integration
|
|
178
|
+
|
|
179
|
+
This tool is designed to work seamlessly with AI coding agents:
|
|
180
|
+
|
|
181
|
+
### Opencode / Claude Code
|
|
182
|
+
```bash
|
|
183
|
+
# Opencode can call this tool directly
|
|
184
|
+
npx rss-agent-discovery https://example.com | jq '.results[0].feeds[].url'
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Cursor
|
|
188
|
+
Cursor can integrate this as a custom tool:
|
|
189
|
+
```json
|
|
190
|
+
{
|
|
191
|
+
"name": "rss_discovery",
|
|
192
|
+
"command": "npx rss-agent-discovery {url}",
|
|
193
|
+
"description": "Discover RSS feeds from a website"
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### GitHub Copilot
|
|
198
|
+
```javascript
|
|
199
|
+
// Use in GitHub Actions or workflows
|
|
200
|
+
const { execSync } = require('child_process');
|
|
201
|
+
const result = JSON.parse(
|
|
202
|
+
execSync('npx rss-agent-discovery https://github.com/blog').toString()
|
|
203
|
+
);
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Custom MCP Server
|
|
207
|
+
```typescript
|
|
208
|
+
// Build a Model Context Protocol server
|
|
209
|
+
import { spawn } from 'child_process';
|
|
210
|
+
|
|
211
|
+
async function discoverRSS(url: string) {
|
|
212
|
+
const proc = spawn('npx', ['rss-agent-discovery', url]);
|
|
213
|
+
const chunks: Buffer[] = [];
|
|
214
|
+
|
|
215
|
+
for await (const chunk of proc.stdout) {
|
|
216
|
+
chunks.push(chunk);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return JSON.parse(Buffer.concat(chunks).toString());
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### Why AI Agents Need This Tool
|
|
224
|
+
|
|
225
|
+
AI agents (Claude, Cursor, GPT-4) struggle with RSS discovery because:
|
|
226
|
+
- They rely on web search which may miss feeds
|
|
227
|
+
- They don't systematically parse HTML `<link>` tags
|
|
228
|
+
- They give up after trying 2-3 common paths
|
|
229
|
+
- They can't validate feeds actually return XML
|
|
230
|
+
|
|
231
|
+
This tool solves all of those problems with structured JSON output.
|
|
232
|
+
|
|
233
|
+
## Development
|
|
234
|
+
|
|
235
|
+
### Build:
|
|
236
|
+
```bash
|
|
237
|
+
npm run build
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Test:
|
|
241
|
+
```bash
|
|
242
|
+
npm test
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### Lint:
|
|
246
|
+
```bash
|
|
247
|
+
tsc --noEmit
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
## Project Structure
|
|
251
|
+
|
|
252
|
+
- `find-rss-feeds.ts` - TypeScript source
|
|
253
|
+
- `dist/` - Compiled JavaScript
|
|
254
|
+
- `package.json` - Dependencies and scripts
|
|
255
|
+
|
|
256
|
+
## License
|
|
257
|
+
|
|
258
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"find-rss-feeds.d.ts","sourceRoot":"","sources":["../find-rss-feeds.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
import * as cheerio from 'cheerio';
|
|
2
|
+
const cliOptions = {
|
|
3
|
+
help: false,
|
|
4
|
+
skipBlogs: false,
|
|
5
|
+
maxBlogs: 5,
|
|
6
|
+
customBlogPaths: null,
|
|
7
|
+
timeout: 10000
|
|
8
|
+
};
|
|
9
|
+
const BLOG_KEYWORDS = [
|
|
10
|
+
'blog', 'news', 'articles', 'posts', 'updates',
|
|
11
|
+
'journal', 'insights', 'stories', 'press', 'medium',
|
|
12
|
+
'substack', 'the-edge', 'engineering-blog', 'dev',
|
|
13
|
+
'engineering', 'developers', 'community'
|
|
14
|
+
];
|
|
15
|
+
const COMMON_BLOG_PATHS = [
|
|
16
|
+
'/blog',
|
|
17
|
+
'/news',
|
|
18
|
+
'/articles',
|
|
19
|
+
'/posts',
|
|
20
|
+
'/updates',
|
|
21
|
+
'/journal',
|
|
22
|
+
'/insights',
|
|
23
|
+
'/stories',
|
|
24
|
+
'/press',
|
|
25
|
+
'/medium',
|
|
26
|
+
'/substack',
|
|
27
|
+
'/the-edge',
|
|
28
|
+
'/engineering-blog',
|
|
29
|
+
'/engineering',
|
|
30
|
+
'/developers',
|
|
31
|
+
'/dev',
|
|
32
|
+
'/community'
|
|
33
|
+
];
|
|
34
|
+
function parseArgs(args) {
|
|
35
|
+
const urls = [];
|
|
36
|
+
for (let i = 0; i < args.length; i++) {
|
|
37
|
+
const arg = args[i];
|
|
38
|
+
if (arg === '--help' || arg === '-h') {
|
|
39
|
+
cliOptions.help = true;
|
|
40
|
+
}
|
|
41
|
+
else if (arg === '--no-blogs' || arg === '--skip-blogs') {
|
|
42
|
+
cliOptions.skipBlogs = true;
|
|
43
|
+
}
|
|
44
|
+
else if (arg === '--max-blogs') {
|
|
45
|
+
cliOptions.maxBlogs = parseInt(args[++i], 10);
|
|
46
|
+
}
|
|
47
|
+
else if (arg === '--blog-paths') {
|
|
48
|
+
cliOptions.customBlogPaths = args[++i].split(',').map(p => p.trim());
|
|
49
|
+
}
|
|
50
|
+
else if (arg === '--timeout') {
|
|
51
|
+
cliOptions.timeout = parseInt(args[++i], 10);
|
|
52
|
+
}
|
|
53
|
+
else if (arg.startsWith('http://') || arg.startsWith('https://')) {
|
|
54
|
+
urls.push(arg);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return urls;
|
|
58
|
+
}
|
|
59
|
+
function showHelp() {
|
|
60
|
+
console.log(`
|
|
61
|
+
Usage: node find-rss-feeds.js [options] <URL1> <URL2> ...
|
|
62
|
+
|
|
63
|
+
Discover RSS feeds from websites for AI agent consumption. JSON-only output.
|
|
64
|
+
|
|
65
|
+
Arguments:
|
|
66
|
+
URL(s) One or more website URLs to scan for RSS feeds
|
|
67
|
+
|
|
68
|
+
Options:
|
|
69
|
+
--no-blogs, --skip-blogs
|
|
70
|
+
Skip blog subdirectory scanning
|
|
71
|
+
--max-blogs <num> Maximum number of blog subdirectories to scan (default: 5)
|
|
72
|
+
--blog-paths <paths>
|
|
73
|
+
Comma-separated custom blog paths to try (e.g., '/blog,/news')
|
|
74
|
+
--timeout <ms> Timeout per URL in milliseconds (default: 10000)
|
|
75
|
+
--help, -h Show this help message
|
|
76
|
+
|
|
77
|
+
Exit codes:
|
|
78
|
+
0 One or more feeds found
|
|
79
|
+
1 No feeds found
|
|
80
|
+
2 Error occurred
|
|
81
|
+
|
|
82
|
+
Examples:
|
|
83
|
+
node find-rss-feeds.js https://example.com
|
|
84
|
+
node find-rss-feeds.js https://site1.com https://site2.com
|
|
85
|
+
node find-rss-feeds.js --timeout 15000 https://example.com
|
|
86
|
+
node find-rss-feeds.js --max-blogs 3 https://example.com
|
|
87
|
+
node find-rss-feeds.js --blog-paths '/blog,/updates' https://example.com | jq
|
|
88
|
+
|
|
89
|
+
Output schema:
|
|
90
|
+
{
|
|
91
|
+
"success": true,
|
|
92
|
+
"results": [
|
|
93
|
+
{
|
|
94
|
+
"url": "https://example.com",
|
|
95
|
+
"feeds": [
|
|
96
|
+
{"url": "https://example.com/atom", "title": "Blog", "type": "atom"}
|
|
97
|
+
],
|
|
98
|
+
"error": null
|
|
99
|
+
}
|
|
100
|
+
]
|
|
101
|
+
}
|
|
102
|
+
`);
|
|
103
|
+
}
|
|
104
|
+
function extractBlogLinksFromHTML(html, baseUrl) {
|
|
105
|
+
const $ = cheerio.load(html);
|
|
106
|
+
const blogPaths = new Set();
|
|
107
|
+
const linkTextAndUrls = [];
|
|
108
|
+
$('a[href]').each((i, el) => {
|
|
109
|
+
const href = $(el).attr('href');
|
|
110
|
+
const text = $(el).text().toLowerCase().trim();
|
|
111
|
+
if (href) {
|
|
112
|
+
try {
|
|
113
|
+
const url = new URL(href, baseUrl);
|
|
114
|
+
if (url.origin === new URL(baseUrl).origin) {
|
|
115
|
+
linkTextAndUrls.push({
|
|
116
|
+
text: text,
|
|
117
|
+
path: url.pathname,
|
|
118
|
+
href: url.href
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
catch (e) {
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
const seenPaths = new Set();
|
|
127
|
+
for (const link of linkTextAndUrls) {
|
|
128
|
+
if (seenPaths.has(link.path))
|
|
129
|
+
continue;
|
|
130
|
+
const lowerText = link.text.toLowerCase();
|
|
131
|
+
const lowerPath = link.path.toLowerCase();
|
|
132
|
+
const foundKeyword = BLOG_KEYWORDS.find(keyword => lowerText.includes(keyword) ||
|
|
133
|
+
lowerPath.includes(keyword));
|
|
134
|
+
if (foundKeyword) {
|
|
135
|
+
if (link.path.startsWith('/') && link.path !== '/') {
|
|
136
|
+
const parts = link.path.split('/').filter(Boolean);
|
|
137
|
+
if (parts.length >= 1 && parts.length <= 3) {
|
|
138
|
+
const blogPath = '/' + parts[0];
|
|
139
|
+
if (!seenPaths.has(blogPath)) {
|
|
140
|
+
blogPaths.add(blogPath);
|
|
141
|
+
seenPaths.add(blogPath);
|
|
142
|
+
seenPaths.add(link.path);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return Array.from(blogPaths);
|
|
149
|
+
}
|
|
150
|
+
function discoverBlogSubdirectories(baseUrl, html = null) {
|
|
151
|
+
let blogPaths = [];
|
|
152
|
+
if (html) {
|
|
153
|
+
blogPaths = extractBlogLinksFromHTML(html, baseUrl);
|
|
154
|
+
}
|
|
155
|
+
if (blogPaths.length === 0 || cliOptions.customBlogPaths) {
|
|
156
|
+
const pathsToUse = cliOptions.customBlogPaths ? cliOptions.customBlogPaths : COMMON_BLOG_PATHS;
|
|
157
|
+
for (const path of pathsToUse) {
|
|
158
|
+
if (!blogPaths.includes(path)) {
|
|
159
|
+
blogPaths.push(path);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return blogPaths.slice(0, cliOptions.maxBlogs);
|
|
164
|
+
}
|
|
165
|
+
async function scanURLForFeeds(url) {
|
|
166
|
+
const discoveredFeeds = new Map();
|
|
167
|
+
try {
|
|
168
|
+
const res = await fetch(url, {
|
|
169
|
+
headers: { 'User-Agent': 'rss-agent-discovery/1.0' }
|
|
170
|
+
});
|
|
171
|
+
if (!res.ok) {
|
|
172
|
+
throw new Error(`Failed to fetch ${url}: ${res.status}`);
|
|
173
|
+
}
|
|
174
|
+
const html = await res.text();
|
|
175
|
+
const $ = cheerio.load(html);
|
|
176
|
+
$('link[rel="alternate"]').each((i, el) => {
|
|
177
|
+
const type = $(el).attr('type');
|
|
178
|
+
if (type && (type.includes('rss+xml') || type.includes('atom+xml'))) {
|
|
179
|
+
let href = $(el).attr('href');
|
|
180
|
+
const title = $(el).attr('title') || 'RSS Feed';
|
|
181
|
+
if (href) {
|
|
182
|
+
try {
|
|
183
|
+
const fullHref = new URL(href, url).href;
|
|
184
|
+
const feedType = type.includes('atom') ? 'atom' : 'rss';
|
|
185
|
+
if (!discoveredFeeds.has(fullHref)) {
|
|
186
|
+
discoveredFeeds.set(fullHref, { url: fullHref, title, type: feedType });
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
const commonPaths = [
|
|
195
|
+
'rss.xml', 'feed.xml', 'rss', 'atom', 'atom.xml', 'index.xml',
|
|
196
|
+
'feeds/rss.xml', 'feed', 'rss/feed.xml'
|
|
197
|
+
];
|
|
198
|
+
for (const path of commonPaths) {
|
|
199
|
+
try {
|
|
200
|
+
const candidateUrl = new URL(path, url).href;
|
|
201
|
+
if (!discoveredFeeds.has(candidateUrl)) {
|
|
202
|
+
const type = path.includes('atom') ? 'atom' : 'rss';
|
|
203
|
+
discoveredFeeds.set(candidateUrl, {
|
|
204
|
+
url: candidateUrl,
|
|
205
|
+
title: candidateUrl.split('/').pop() || 'RSS Feed',
|
|
206
|
+
type
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
const validFeeds = [];
|
|
214
|
+
for (const [feedUrl, feed] of Array.from(discoveredFeeds)) {
|
|
215
|
+
try {
|
|
216
|
+
const res = await fetch(feedUrl, {
|
|
217
|
+
headers: { 'User-Agent': 'rss-agent-discovery/1.0' }
|
|
218
|
+
});
|
|
219
|
+
if (res.ok) {
|
|
220
|
+
const contentType = res.headers.get('content-type') || '';
|
|
221
|
+
const text = await res.text();
|
|
222
|
+
if ((text.includes('<rss') || text.includes('<feed')) &&
|
|
223
|
+
(contentType.includes('xml') || contentType.includes('rss'))) {
|
|
224
|
+
validFeeds.push(feed);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
catch {
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return { html, feeds: validFeeds };
|
|
232
|
+
}
|
|
233
|
+
catch (e) {
|
|
234
|
+
console.error(`Error scanning ${url}: ${e.message}`);
|
|
235
|
+
return { html: null, feeds: [] };
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
async function findRSSFeeds(baseUrl) {
|
|
239
|
+
const timeoutMs = cliOptions.timeout;
|
|
240
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
241
|
+
setTimeout(() => reject(new Error('Timeout')), timeoutMs);
|
|
242
|
+
});
|
|
243
|
+
try {
|
|
244
|
+
const scanResult = await Promise.race([
|
|
245
|
+
scanURLForFeeds(baseUrl),
|
|
246
|
+
timeoutPromise
|
|
247
|
+
]);
|
|
248
|
+
const allFeeds = new Map();
|
|
249
|
+
scanResult.feeds.forEach(feed => {
|
|
250
|
+
if (!allFeeds.has(feed.url)) {
|
|
251
|
+
allFeeds.set(feed.url, feed);
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
if (!cliOptions.skipBlogs && scanResult.html) {
|
|
255
|
+
const blogPaths = discoverBlogSubdirectories(baseUrl, scanResult.html);
|
|
256
|
+
if (blogPaths.length > 0) {
|
|
257
|
+
const blogURLs = blogPaths.map(path => {
|
|
258
|
+
try {
|
|
259
|
+
return new URL(path, baseUrl).href;
|
|
260
|
+
}
|
|
261
|
+
catch {
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
}).filter((url) => url !== null);
|
|
265
|
+
const blogResults = await Promise.allSettled(blogURLs.map(blogUrl => Promise.race([
|
|
266
|
+
scanURLForFeeds(blogUrl),
|
|
267
|
+
timeoutPromise
|
|
268
|
+
])));
|
|
269
|
+
blogResults.forEach((result) => {
|
|
270
|
+
if (result.status === 'fulfilled') {
|
|
271
|
+
result.value.feeds.forEach((feed) => {
|
|
272
|
+
if (!allFeeds.has(feed.url)) {
|
|
273
|
+
allFeeds.set(feed.url, feed);
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return {
|
|
281
|
+
url: baseUrl,
|
|
282
|
+
feeds: Array.from(allFeeds.values()),
|
|
283
|
+
error: null
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
catch (e) {
|
|
287
|
+
const errorMessage = e.message;
|
|
288
|
+
console.error(`Error processing ${baseUrl}: ${errorMessage}`);
|
|
289
|
+
return {
|
|
290
|
+
url: baseUrl,
|
|
291
|
+
feeds: [],
|
|
292
|
+
error: errorMessage
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
async function main() {
|
|
297
|
+
const args = process.argv.slice(2);
|
|
298
|
+
const urls = parseArgs(args);
|
|
299
|
+
if (cliOptions.help) {
|
|
300
|
+
showHelp();
|
|
301
|
+
return 2;
|
|
302
|
+
}
|
|
303
|
+
if (urls.length === 0) {
|
|
304
|
+
console.error('Error: No URLs provided. Use --help for usage information.');
|
|
305
|
+
return 2;
|
|
306
|
+
}
|
|
307
|
+
const results = [];
|
|
308
|
+
const scanResults = await Promise.all(urls.map(url => findRSSFeeds(url)));
|
|
309
|
+
let totalFeedsFound = 0;
|
|
310
|
+
let hasError = false;
|
|
311
|
+
for (const result of scanResults) {
|
|
312
|
+
results.push(result);
|
|
313
|
+
if (result.error) {
|
|
314
|
+
hasError = true;
|
|
315
|
+
}
|
|
316
|
+
else {
|
|
317
|
+
totalFeedsFound += result.feeds.length;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
const output = {
|
|
321
|
+
success: !hasError,
|
|
322
|
+
results: results
|
|
323
|
+
};
|
|
324
|
+
console.log(JSON.stringify(output, null, 2));
|
|
325
|
+
if (hasError) {
|
|
326
|
+
return 2;
|
|
327
|
+
}
|
|
328
|
+
else if (totalFeedsFound === 0) {
|
|
329
|
+
return 1;
|
|
330
|
+
}
|
|
331
|
+
else {
|
|
332
|
+
return 0;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
main().then(exitCode => {
|
|
336
|
+
process.exit(exitCode);
|
|
337
|
+
}).catch(e => {
|
|
338
|
+
console.error(JSON.stringify({
|
|
339
|
+
success: false,
|
|
340
|
+
error: e.message,
|
|
341
|
+
results: []
|
|
342
|
+
}, null, 2));
|
|
343
|
+
process.exit(2);
|
|
344
|
+
});
|
|
345
|
+
//# sourceMappingURL=find-rss-feeds.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"find-rss-feeds.js","sourceRoot":"","sources":["../find-rss-feeds.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "rss-agent-discovery",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "AI agent-focused RSS feed discovery. JSON output, semantic exit codes.",
|
|
5
|
+
"main": "dist/find-rss-feeds.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"rss-agent-discovery": "dist/find-rss-feeds.js",
|
|
9
|
+
"rss-discover": "dist/find-rss-feeds.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc",
|
|
16
|
+
"start": "node dist/find-rss-feeds.js",
|
|
17
|
+
"dev": "tsc && node dist/find-rss-feeds.js",
|
|
18
|
+
"test": "npm run build && node dist/find-rss-feeds.js https://vercel.com https://news.ycombinator.com | jq",
|
|
19
|
+
"prepublishOnly": "npm run build"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"rss",
|
|
23
|
+
"atom",
|
|
24
|
+
"feed",
|
|
25
|
+
"discovery",
|
|
26
|
+
"ai",
|
|
27
|
+
"agent",
|
|
28
|
+
"cli",
|
|
29
|
+
"json"
|
|
30
|
+
],
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"cheerio": "^1.0.0"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/cheerio": "^0.22.35",
|
|
37
|
+
"@types/node": "^25.0.10",
|
|
38
|
+
"typescript": "^5.9.3"
|
|
39
|
+
}
|
|
40
|
+
}
|