opencode-agora 0.2.2 → 0.4.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 +217 -52
- package/dist/cli/app.d.ts +3 -0
- package/dist/cli/app.d.ts.map +1 -1
- package/dist/cli/app.js +1202 -227
- package/dist/cli/app.js.map +1 -1
- package/dist/cli/chat-renderer.d.ts +31 -0
- package/dist/cli/chat-renderer.d.ts.map +1 -0
- package/dist/cli/chat-renderer.js +275 -0
- package/dist/cli/chat-renderer.js.map +1 -0
- package/dist/cli/commands-meta.d.ts +21 -0
- package/dist/cli/commands-meta.d.ts.map +1 -0
- package/dist/cli/commands-meta.js +600 -0
- package/dist/cli/commands-meta.js.map +1 -0
- package/dist/cli/completions.d.ts +18 -0
- package/dist/cli/completions.d.ts.map +1 -0
- package/dist/cli/completions.js +190 -0
- package/dist/cli/completions.js.map +1 -0
- package/dist/cli/mcp-server.d.ts +4 -0
- package/dist/cli/mcp-server.d.ts.map +1 -0
- package/dist/cli/mcp-server.js +277 -0
- package/dist/cli/mcp-server.js.map +1 -0
- package/dist/cli/menu.d.ts +7 -0
- package/dist/cli/menu.d.ts.map +1 -0
- package/dist/cli/menu.js +164 -0
- package/dist/cli/menu.js.map +1 -0
- package/dist/cli/pages/community.d.ts +3 -0
- package/dist/cli/pages/community.d.ts.map +1 -0
- package/dist/cli/pages/community.js +276 -0
- package/dist/cli/pages/community.js.map +1 -0
- package/dist/cli/pages/helpers.d.ts +32 -0
- package/dist/cli/pages/helpers.d.ts.map +1 -0
- package/dist/cli/pages/helpers.js +67 -0
- package/dist/cli/pages/helpers.js.map +1 -0
- package/dist/cli/pages/home.d.ts +3 -0
- package/dist/cli/pages/home.d.ts.map +1 -0
- package/dist/cli/pages/home.js +148 -0
- package/dist/cli/pages/home.js.map +1 -0
- package/dist/cli/pages/marketplace.d.ts +3 -0
- package/dist/cli/pages/marketplace.d.ts.map +1 -0
- package/dist/cli/pages/marketplace.js +179 -0
- package/dist/cli/pages/marketplace.js.map +1 -0
- package/dist/cli/pages/news.d.ts +3 -0
- package/dist/cli/pages/news.d.ts.map +1 -0
- package/dist/cli/pages/news.js +561 -0
- package/dist/cli/pages/news.js.map +1 -0
- package/dist/cli/pages/settings.d.ts +3 -0
- package/dist/cli/pages/settings.d.ts.map +1 -0
- package/dist/cli/pages/settings.js +166 -0
- package/dist/cli/pages/settings.js.map +1 -0
- package/dist/cli/pages/types.d.ts +67 -0
- package/dist/cli/pages/types.d.ts.map +1 -0
- package/dist/cli/pages/types.js +2 -0
- package/dist/cli/pages/types.js.map +1 -0
- package/dist/cli/prompter.d.ts +135 -0
- package/dist/cli/prompter.d.ts.map +1 -0
- package/dist/cli/prompter.js +675 -0
- package/dist/cli/prompter.js.map +1 -0
- package/dist/cli/shell.d.ts +23 -0
- package/dist/cli/shell.d.ts.map +1 -0
- package/dist/cli/shell.js +819 -0
- package/dist/cli/shell.js.map +1 -0
- package/dist/cli/tui.d.ts +7 -0
- package/dist/cli/tui.d.ts.map +1 -0
- package/dist/cli/tui.js +373 -0
- package/dist/cli/tui.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/commands.d.ts +14 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/commands.js +28 -0
- package/dist/commands.js.map +1 -0
- package/dist/community/client.d.ts +47 -0
- package/dist/community/client.d.ts.map +1 -0
- package/dist/community/client.js +245 -0
- package/dist/community/client.js.map +1 -0
- package/dist/community/types.d.ts +50 -0
- package/dist/community/types.d.ts.map +1 -0
- package/dist/community/types.js +11 -0
- package/dist/community/types.js.map +1 -0
- package/dist/config-files.d.ts.map +1 -1
- package/dist/config-files.js +11 -8
- package/dist/config-files.js.map +1 -1
- package/dist/config.d.ts +8 -8
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +16 -27
- package/dist/config.js.map +1 -1
- package/dist/data.d.ts +1 -1
- package/dist/data.d.ts.map +1 -1
- package/dist/data.js +1013 -545
- package/dist/data.js.map +1 -1
- package/dist/format.d.ts +5 -39
- package/dist/format.d.ts.map +1 -1
- package/dist/format.js +5 -118
- package/dist/format.js.map +1 -1
- package/dist/history.d.ts +13 -0
- package/dist/history.d.ts.map +1 -0
- package/dist/history.js +37 -0
- package/dist/history.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +151 -236
- package/dist/index.js.map +1 -1
- package/dist/init.d.ts +4 -1
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +103 -51
- package/dist/init.js.map +1 -1
- package/dist/live.d.ts +4 -0
- package/dist/live.d.ts.map +1 -1
- package/dist/live.js +87 -19
- package/dist/live.js.map +1 -1
- package/dist/marketplace.d.ts +9 -0
- package/dist/marketplace.d.ts.map +1 -1
- package/dist/marketplace.js +128 -33
- package/dist/marketplace.js.map +1 -1
- package/dist/news/cache.d.ts +13 -0
- package/dist/news/cache.d.ts.map +1 -0
- package/dist/news/cache.js +65 -0
- package/dist/news/cache.js.map +1 -0
- package/dist/news/score.d.ts +4 -0
- package/dist/news/score.d.ts.map +1 -0
- package/dist/news/score.js +43 -0
- package/dist/news/score.js.map +1 -0
- package/dist/news/sources/arxiv.d.ts +9 -0
- package/dist/news/sources/arxiv.d.ts.map +1 -0
- package/dist/news/sources/arxiv.js +103 -0
- package/dist/news/sources/arxiv.js.map +1 -0
- package/dist/news/sources/github-trending.d.ts +9 -0
- package/dist/news/sources/github-trending.d.ts.map +1 -0
- package/dist/news/sources/github-trending.js +93 -0
- package/dist/news/sources/github-trending.js.map +1 -0
- package/dist/news/sources/hn.d.ts +9 -0
- package/dist/news/sources/hn.d.ts.map +1 -0
- package/dist/news/sources/hn.js +53 -0
- package/dist/news/sources/hn.js.map +1 -0
- package/dist/news/sources/reddit.d.ts +9 -0
- package/dist/news/sources/reddit.d.ts.map +1 -0
- package/dist/news/sources/reddit.js +68 -0
- package/dist/news/sources/reddit.js.map +1 -0
- package/dist/news/sources/rss.d.ts +14 -0
- package/dist/news/sources/rss.d.ts.map +1 -0
- package/dist/news/sources/rss.js +102 -0
- package/dist/news/sources/rss.js.map +1 -0
- package/dist/news/types.d.ts +39 -0
- package/dist/news/types.d.ts.map +1 -0
- package/dist/news/types.js +47 -0
- package/dist/news/types.js.map +1 -0
- package/dist/preferences.d.ts +14 -0
- package/dist/preferences.d.ts.map +1 -0
- package/dist/preferences.js +31 -0
- package/dist/preferences.js.map +1 -0
- package/dist/settings.d.ts +26 -0
- package/dist/settings.d.ts.map +1 -0
- package/dist/settings.js +257 -0
- package/dist/settings.js.map +1 -0
- package/dist/state.d.ts.map +1 -1
- package/dist/state.js +7 -6
- package/dist/state.js.map +1 -1
- package/dist/transcript.d.ts +28 -0
- package/dist/transcript.d.ts.map +1 -0
- package/dist/transcript.js +79 -0
- package/dist/transcript.js.map +1 -0
- package/dist/types.d.ts +6 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/ui.d.ts +157 -0
- package/dist/ui.d.ts.map +1 -0
- package/dist/ui.js +296 -0
- package/dist/ui.js.map +1 -0
- package/package.json +21 -10
- package/dist/api.d.ts +0 -69
- package/dist/api.d.ts.map +0 -1
- package/dist/api.js +0 -109
- package/dist/api.js.map +0 -1
- package/dist/logger.d.ts +0 -20
- package/dist/logger.d.ts.map +0 -1
- package/dist/logger.js +0 -59
- package/dist/logger.js.map +0 -1
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
const QUERY_CATEGORIES = ['cs.AI', 'cs.CL', 'cs.SE', 'cs.LG'];
|
|
2
|
+
const MAX_RESULTS = 25;
|
|
3
|
+
export const arxivSource = {
|
|
4
|
+
async fetch(opts) {
|
|
5
|
+
const fetcher = opts.fetcher ?? globalThis.fetch;
|
|
6
|
+
const allItems = [];
|
|
7
|
+
const now = new Date().toISOString();
|
|
8
|
+
const categories = QUERY_CATEGORIES.join('+OR+');
|
|
9
|
+
const url = `http://export.arxiv.org/api/query?search_query=cat:${categories}&sortBy=submittedDate&sortOrder=descending&max_results=${MAX_RESULTS}`;
|
|
10
|
+
try {
|
|
11
|
+
const res = await fetcher(url, {
|
|
12
|
+
signal: opts.signal,
|
|
13
|
+
headers: { 'User-Agent': 'agora-cli/0.5.0' },
|
|
14
|
+
});
|
|
15
|
+
if (!res.ok)
|
|
16
|
+
throw new Error(`arXiv API returned ${res.status}`);
|
|
17
|
+
const xml = await res.text();
|
|
18
|
+
const items = parseArxivAtom(xml, now);
|
|
19
|
+
allItems.push(...items);
|
|
20
|
+
}
|
|
21
|
+
catch (e) {
|
|
22
|
+
if (e instanceof Error && e.name !== 'AbortError')
|
|
23
|
+
throw e;
|
|
24
|
+
}
|
|
25
|
+
return allItems;
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
function parseArxivAtom(xml, now) {
|
|
29
|
+
const items = [];
|
|
30
|
+
const entryRegex = /<entry>([\s\S]*?)<\/entry>/gi;
|
|
31
|
+
let match;
|
|
32
|
+
while ((match = entryRegex.exec(xml)) !== null) {
|
|
33
|
+
try {
|
|
34
|
+
const entry = match[1];
|
|
35
|
+
const idMatch = entry.match(/<id>([^<]+)<\/id>/);
|
|
36
|
+
if (!idMatch)
|
|
37
|
+
continue;
|
|
38
|
+
const paperUrl = idMatch[1].trim();
|
|
39
|
+
const paperId = paperUrl.split('/').pop() || paperUrl;
|
|
40
|
+
const titleMatch = entry.match(/<title>([\s\S]*?)<\/title>/);
|
|
41
|
+
const title = titleMatch ? cleanXml(titleMatch[1]) : '';
|
|
42
|
+
const summaryMatch = entry.match(/<summary>([\s\S]*?)<\/summary>/);
|
|
43
|
+
const summary = summaryMatch ? cleanXml(summaryMatch[1]).slice(0, 300) : '';
|
|
44
|
+
const publishedMatch = entry.match(/<published>([^<]+)<\/published>/);
|
|
45
|
+
const published = publishedMatch ? publishedMatch[1].trim() : now;
|
|
46
|
+
const authorRegex = /<author>[\s\S]*?<name>([^<]+)<\/name>[\s\S]*?<\/author>/gi;
|
|
47
|
+
const authors = [];
|
|
48
|
+
let aMatch;
|
|
49
|
+
while ((aMatch = authorRegex.exec(entry)) !== null) {
|
|
50
|
+
authors.push(cleanXml(aMatch[1]));
|
|
51
|
+
}
|
|
52
|
+
const catRegex = /<category[^>]*term="([^"]+)"[^>]*\/>/gi;
|
|
53
|
+
const categories = [];
|
|
54
|
+
let cMatch;
|
|
55
|
+
while ((cMatch = catRegex.exec(entry)) !== null) {
|
|
56
|
+
categories.push(cMatch[1]);
|
|
57
|
+
}
|
|
58
|
+
items.push({
|
|
59
|
+
id: `arxiv:${paperId}`,
|
|
60
|
+
source: 'arxiv',
|
|
61
|
+
title,
|
|
62
|
+
url: `https://arxiv.org/abs/${paperId}`,
|
|
63
|
+
author: authors[0] || 'Unknown',
|
|
64
|
+
publishedAt: published,
|
|
65
|
+
fetchedAt: now,
|
|
66
|
+
engagement: 0,
|
|
67
|
+
tags: extractArxivTags(categories, title, summary),
|
|
68
|
+
summary: summary || undefined,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return items;
|
|
76
|
+
}
|
|
77
|
+
function extractArxivTags(categories, title, summary) {
|
|
78
|
+
const tags = [];
|
|
79
|
+
const lower = (title + ' ' + summary).toLowerCase();
|
|
80
|
+
for (const cat of categories) {
|
|
81
|
+
tags.push(cat.replace(/\./g, '-').toLowerCase());
|
|
82
|
+
}
|
|
83
|
+
const topicMap = {
|
|
84
|
+
ai: ['artificial intelligence', 'machine learning', 'deep learning'],
|
|
85
|
+
llm: ['language model', 'llm', 'gpt', 'transformer', 'attention'],
|
|
86
|
+
agents: ['agent', 'tool use', 'tool-use', 'function calling'],
|
|
87
|
+
coding: ['code generation', 'program synthesis', 'software engineering'],
|
|
88
|
+
security: ['security', 'safety', 'alignment', 'harmlessness'],
|
|
89
|
+
};
|
|
90
|
+
for (const [topic, keywords] of Object.entries(topicMap)) {
|
|
91
|
+
for (const kw of keywords) {
|
|
92
|
+
if (lower.includes(kw)) {
|
|
93
|
+
tags.push(topic);
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return tags;
|
|
99
|
+
}
|
|
100
|
+
function cleanXml(text) {
|
|
101
|
+
return text.replace(/<[^>]*>/g, '').replace(/\s+/g, ' ').trim();
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=arxiv.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"arxiv.js","sourceRoot":"","sources":["../../../src/news/sources/arxiv.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { NewsItem } from '../types.js';
|
|
2
|
+
export interface SourceAdapter {
|
|
3
|
+
fetch(opts: {
|
|
4
|
+
fetcher?: (url: string, init?: RequestInit) => Promise<Response>;
|
|
5
|
+
signal?: AbortSignal;
|
|
6
|
+
}): Promise<NewsItem[]>;
|
|
7
|
+
}
|
|
8
|
+
export declare const githubTrendingSource: SourceAdapter;
|
|
9
|
+
//# sourceMappingURL=github-trending.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-trending.d.ts","sourceRoot":"","sources":["../../../src/news/sources/github-trending.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAc,MAAM,aAAa,CAAC;AAExD,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,IAAI,EAAE;QAAE,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;QAAC,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;CAC9H;AAID,eAAO,MAAM,oBAAoB,EAAE,aAwBlC,CAAC"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
const LANGUAGES = ['typescript', 'python', 'go', 'rust'];
|
|
2
|
+
export const githubTrendingSource = {
|
|
3
|
+
async fetch(opts) {
|
|
4
|
+
const fetcher = opts.fetcher ?? globalThis.fetch;
|
|
5
|
+
const allItems = [];
|
|
6
|
+
const now = new Date().toISOString();
|
|
7
|
+
for (const lang of LANGUAGES) {
|
|
8
|
+
try {
|
|
9
|
+
const url = `https://github.com/trending/${lang}?since=daily`;
|
|
10
|
+
const res = await fetcher(url, {
|
|
11
|
+
signal: opts.signal,
|
|
12
|
+
headers: { 'User-Agent': 'agora-cli/0.5.0' },
|
|
13
|
+
});
|
|
14
|
+
if (!res.ok)
|
|
15
|
+
continue;
|
|
16
|
+
const html = await res.text();
|
|
17
|
+
const items = parseTrendingHtml(html, lang, now);
|
|
18
|
+
allItems.push(...items);
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return allItems;
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
function parseTrendingHtml(html, lang, now) {
|
|
28
|
+
const items = [];
|
|
29
|
+
const articleRegex = /<article[^>]*>([\s\S]*?)<\/article>/gi;
|
|
30
|
+
let match;
|
|
31
|
+
while ((match = articleRegex.exec(html)) !== null) {
|
|
32
|
+
try {
|
|
33
|
+
const article = match[1];
|
|
34
|
+
const repoMatch = article.match(/<h2[^>]*>[\s\S]*?<a[^>]*href="\/([^"]+)"[^>]*>/i);
|
|
35
|
+
if (!repoMatch)
|
|
36
|
+
continue;
|
|
37
|
+
const repoPath = repoMatch[1];
|
|
38
|
+
const descMatch = article.match(/<p[^>]*class="[^"]*col-9[^"]*"[^>]*>([\s\S]*?)<\/p>/i);
|
|
39
|
+
const description = descMatch ? stripHtml(descMatch[1]).trim() : '';
|
|
40
|
+
const starsMatch = article.match(/<span[^>]*class="[^"]*d-inline-block[^"]*float-sm-right[^"]*"[^>]*>([\s\S]*?)<\/span>/i);
|
|
41
|
+
const starsStr = starsMatch ? stripHtml(starsMatch[1]).trim().replace(/,/g, '') : '0';
|
|
42
|
+
const stars = parseInt(starsStr, 10) || 0;
|
|
43
|
+
const forksMatch = article.match(/<a[^>]*href="\/[^"]+\/fork"[^>]*>([\s\S]*?)<\/a>/i);
|
|
44
|
+
const forksStr = forksMatch ? stripHtml(forksMatch[1]).trim().replace(/,/g, '') : '0';
|
|
45
|
+
const forks = parseInt(forksStr, 10) || 0;
|
|
46
|
+
const [owner, repo] = repoPath.split('/');
|
|
47
|
+
const id = `gh:${repoPath.replace('/', '-')}`;
|
|
48
|
+
const engagement = stars + forks;
|
|
49
|
+
items.push({
|
|
50
|
+
id,
|
|
51
|
+
source: 'github-trending',
|
|
52
|
+
title: `${owner}/${repo}`,
|
|
53
|
+
url: `https://github.com/${repoPath}`,
|
|
54
|
+
author: owner,
|
|
55
|
+
publishedAt: now,
|
|
56
|
+
fetchedAt: now,
|
|
57
|
+
engagement,
|
|
58
|
+
tags: extractGithubTags(repoPath, description, lang),
|
|
59
|
+
summary: description || undefined,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return items;
|
|
67
|
+
}
|
|
68
|
+
function extractGithubTags(repoPath, description, lang) {
|
|
69
|
+
const tags = new Set();
|
|
70
|
+
const lower = (repoPath + ' ' + description).toLowerCase();
|
|
71
|
+
tags.add(lang);
|
|
72
|
+
const topicMap = {
|
|
73
|
+
mcp: ['mcp', 'model-context-protocol', 'modelcontextprotocol'],
|
|
74
|
+
ai: ['ai', 'artificial-intelligence', 'llm', 'machine-learning'],
|
|
75
|
+
agents: ['agent', 'agents', 'autonomous'],
|
|
76
|
+
tools: ['cli', 'tool', 'framework', 'sdk'],
|
|
77
|
+
database: ['database', 'sql', 'nosql', 'postgres', 'redis', 'sqlite'],
|
|
78
|
+
devtools: ['devtools', 'developer-tools', 'github', 'git'],
|
|
79
|
+
};
|
|
80
|
+
for (const [topic, keywords] of Object.entries(topicMap)) {
|
|
81
|
+
for (const kw of keywords) {
|
|
82
|
+
if (lower.includes(kw)) {
|
|
83
|
+
tags.add(topic);
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return Array.from(tags);
|
|
89
|
+
}
|
|
90
|
+
function stripHtml(html) {
|
|
91
|
+
return html.replace(/<[^>]*>/g, '').replace(/[\s\n]+/g, ' ').trim();
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=github-trending.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-trending.js","sourceRoot":"","sources":["../../../src/news/sources/github-trending.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { NewsItem } from '../types.js';
|
|
2
|
+
export interface SourceAdapter {
|
|
3
|
+
fetch(opts: {
|
|
4
|
+
fetcher?: (url: string, init?: RequestInit) => Promise<Response>;
|
|
5
|
+
signal?: AbortSignal;
|
|
6
|
+
}): Promise<NewsItem[]>;
|
|
7
|
+
}
|
|
8
|
+
export declare const hnSource: SourceAdapter;
|
|
9
|
+
//# sourceMappingURL=hn.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hn.d.ts","sourceRoot":"","sources":["../../../src/news/sources/hn.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAc,MAAM,aAAa,CAAC;AAExD,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,IAAI,EAAE;QAAE,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;QAAC,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;CAC9H;AAED,eAAO,MAAM,QAAQ,EAAE,aA0BtB,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export const hnSource = {
|
|
2
|
+
async fetch(opts) {
|
|
3
|
+
const fetcher = opts.fetcher ?? globalThis.fetch;
|
|
4
|
+
const url = 'https://hn.algolia.com/api/v1/search?tags=front_page&numericFilters=points>50&hitsPerPage=30';
|
|
5
|
+
const res = await fetcher(url, { signal: opts.signal });
|
|
6
|
+
if (!res.ok)
|
|
7
|
+
throw new Error(`HN API returned ${res.status}`);
|
|
8
|
+
const data = (await res.json());
|
|
9
|
+
const now = new Date().toISOString();
|
|
10
|
+
return (data.hits ?? []).map((hit) => {
|
|
11
|
+
const item = {
|
|
12
|
+
id: `hn:${hit.objectID}`,
|
|
13
|
+
source: 'hn',
|
|
14
|
+
title: hit.title ?? '',
|
|
15
|
+
url: hit.url || `https://news.ycombinator.com/item?id=${hit.objectID}`,
|
|
16
|
+
author: hit.author,
|
|
17
|
+
publishedAt: new Date(hit.created_at).toISOString(),
|
|
18
|
+
fetchedAt: now,
|
|
19
|
+
engagement: hit.points ?? 0,
|
|
20
|
+
tags: extractHnTags(hit),
|
|
21
|
+
};
|
|
22
|
+
return item;
|
|
23
|
+
});
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
function extractHnTags(hit) {
|
|
27
|
+
const tags = [];
|
|
28
|
+
const title = (hit.title ?? '').toLowerCase();
|
|
29
|
+
const url = (hit.url ?? '').toLowerCase();
|
|
30
|
+
const topicMap = {
|
|
31
|
+
mcp: ['mcp', 'model-context-protocol', 'modelcontextprotocol'],
|
|
32
|
+
ai: ['ai', 'artificial-intelligence'],
|
|
33
|
+
llm: ['llm', 'large-language-model', 'gpt', 'claude', 'gemini'],
|
|
34
|
+
agents: ['agent', 'agents', 'autonomous'],
|
|
35
|
+
coding: ['coding', 'programming', 'software', 'developer'],
|
|
36
|
+
security: ['security', 'vulnerability', 'exploit'],
|
|
37
|
+
devtools: ['devtools', 'developer-tools', 'sdk', 'api'],
|
|
38
|
+
};
|
|
39
|
+
for (const [topic, keywords] of Object.entries(topicMap)) {
|
|
40
|
+
for (const kw of keywords) {
|
|
41
|
+
if (title.includes(kw) || url.includes(kw)) {
|
|
42
|
+
tags.push(topic);
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if ((hit._tags ?? []).includes('show'))
|
|
48
|
+
tags.push('show');
|
|
49
|
+
if ((hit._tags ?? []).includes('ask'))
|
|
50
|
+
tags.push('ask');
|
|
51
|
+
return tags;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=hn.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hn.js","sourceRoot":"","sources":["../../../src/news/sources/hn.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { NewsItem } from '../types.js';
|
|
2
|
+
export interface SourceAdapter {
|
|
3
|
+
fetch(opts: {
|
|
4
|
+
fetcher?: (url: string, init?: RequestInit) => Promise<Response>;
|
|
5
|
+
signal?: AbortSignal;
|
|
6
|
+
}): Promise<NewsItem[]>;
|
|
7
|
+
}
|
|
8
|
+
export declare const redditSource: SourceAdapter;
|
|
9
|
+
//# sourceMappingURL=reddit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reddit.d.ts","sourceRoot":"","sources":["../../../src/news/sources/reddit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAc,MAAM,aAAa,CAAC;AAExD,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,IAAI,EAAE;QAAE,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;QAAC,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;CAC9H;AAID,eAAO,MAAM,YAAY,EAAE,aAuC1B,CAAC"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
const SUBREDDITS = ['mcp', 'LocalLLaMA', 'programming', 'MachineLearning'];
|
|
2
|
+
export const redditSource = {
|
|
3
|
+
async fetch(opts) {
|
|
4
|
+
const fetcher = opts.fetcher ?? globalThis.fetch;
|
|
5
|
+
const allItems = [];
|
|
6
|
+
const now = new Date().toISOString();
|
|
7
|
+
for (const sub of SUBREDDITS) {
|
|
8
|
+
try {
|
|
9
|
+
const url = `https://www.reddit.com/r/${sub}/hot.json?limit=25`;
|
|
10
|
+
const res = await fetcher(url, {
|
|
11
|
+
signal: opts.signal,
|
|
12
|
+
headers: { 'User-Agent': 'agora-cli/0.5.0' },
|
|
13
|
+
});
|
|
14
|
+
if (!res.ok)
|
|
15
|
+
continue;
|
|
16
|
+
const data = (await res.json());
|
|
17
|
+
const children = data?.data?.children ?? [];
|
|
18
|
+
for (const child of children) {
|
|
19
|
+
const post = child?.data;
|
|
20
|
+
if (!post || post.stickied)
|
|
21
|
+
continue;
|
|
22
|
+
allItems.push({
|
|
23
|
+
id: `reddit:${post.id}`,
|
|
24
|
+
source: 'reddit',
|
|
25
|
+
title: post.title ?? '',
|
|
26
|
+
url: `https://reddit.com${post.permalink}`,
|
|
27
|
+
author: post.author,
|
|
28
|
+
publishedAt: new Date(post.created_utc * 1000).toISOString(),
|
|
29
|
+
fetchedAt: now,
|
|
30
|
+
engagement: post.ups ?? 0,
|
|
31
|
+
tags: extractRedditTags(post, sub),
|
|
32
|
+
summary: post.selftext ? post.selftext.slice(0, 200) : undefined,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return allItems;
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
function extractRedditTags(post, subreddit) {
|
|
44
|
+
const tags = [];
|
|
45
|
+
const title = (post.title ?? '').toLowerCase();
|
|
46
|
+
const linkFlair = (post.link_flair_text ?? '').toLowerCase();
|
|
47
|
+
const subLower = subreddit.toLowerCase();
|
|
48
|
+
tags.push(subLower === 'localllama' ? 'llm' : subLower);
|
|
49
|
+
const topicMap = {
|
|
50
|
+
mcp: ['mcp', 'model-context-protocol'],
|
|
51
|
+
ai: ['ai', 'artificial intelligence'],
|
|
52
|
+
agents: ['agent'],
|
|
53
|
+
coding: ['code', 'programming', 'coding', 'developer'],
|
|
54
|
+
tools: ['tool', 'framework', 'library'],
|
|
55
|
+
};
|
|
56
|
+
for (const [topic, keywords] of Object.entries(topicMap)) {
|
|
57
|
+
for (const kw of keywords) {
|
|
58
|
+
if (title.includes(kw) || linkFlair.includes(kw)) {
|
|
59
|
+
tags.push(topic);
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (linkFlair)
|
|
65
|
+
tags.push(linkFlair.replace(/[^a-z0-9-]/g, ''));
|
|
66
|
+
return tags;
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=reddit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reddit.js","sourceRoot":"","sources":["../../../src/news/sources/reddit.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { NewsItem } from '../types.js';
|
|
2
|
+
export interface SourceAdapter {
|
|
3
|
+
fetch(opts: {
|
|
4
|
+
fetcher?: (url: string, init?: RequestInit) => Promise<Response>;
|
|
5
|
+
signal?: AbortSignal;
|
|
6
|
+
}): Promise<NewsItem[]>;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* RSS feed adapter.
|
|
10
|
+
* Reads user-defined RSS feeds from settings. For each configured URL,
|
|
11
|
+
* fetches the feed XML and parses basic RSS 2.0 / Atom entries.
|
|
12
|
+
*/
|
|
13
|
+
export declare const rssSource: SourceAdapter;
|
|
14
|
+
//# sourceMappingURL=rss.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rss.d.ts","sourceRoot":"","sources":["../../../src/news/sources/rss.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAc,MAAM,aAAa,CAAC;AAExD,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,IAAI,EAAE;QAAE,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;QAAC,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;CAC9H;AAED;;;;GAIG;AACH,eAAO,MAAM,SAAS,EAAE,aAqDvB,CAAC"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RSS feed adapter.
|
|
3
|
+
* Reads user-defined RSS feeds from settings. For each configured URL,
|
|
4
|
+
* fetches the feed XML and parses basic RSS 2.0 / Atom entries.
|
|
5
|
+
*/
|
|
6
|
+
export const rssSource = {
|
|
7
|
+
async fetch(opts) {
|
|
8
|
+
const fetcher = opts.fetcher ?? globalThis.fetch;
|
|
9
|
+
const now = new Date().toISOString();
|
|
10
|
+
const items = [];
|
|
11
|
+
const feedUrls = await getConfiguredFeeds();
|
|
12
|
+
for (const feedUrl of feedUrls) {
|
|
13
|
+
try {
|
|
14
|
+
const res = await fetcher(feedUrl, {
|
|
15
|
+
signal: opts.signal,
|
|
16
|
+
headers: { 'User-Agent': 'agora-cli/0.5.0' },
|
|
17
|
+
});
|
|
18
|
+
if (!res.ok)
|
|
19
|
+
continue;
|
|
20
|
+
const xml = await res.text();
|
|
21
|
+
const entryRegex = /<item>([\s\S]*?)<\/item>/gi;
|
|
22
|
+
let match;
|
|
23
|
+
while ((match = entryRegex.exec(xml)) !== null) {
|
|
24
|
+
const entry = match[1];
|
|
25
|
+
const title = extractXmlTag(entry, 'title');
|
|
26
|
+
const link = extractXmlTag(entry, 'link');
|
|
27
|
+
const pubDate = extractXmlTag(entry, 'pubDate');
|
|
28
|
+
const description = extractXmlTag(entry, 'description');
|
|
29
|
+
const creator = extractXmlTag(entry, 'dc:creator');
|
|
30
|
+
if (!title || !link)
|
|
31
|
+
continue;
|
|
32
|
+
const published = pubDate
|
|
33
|
+
? new Date(pubDate).toISOString()
|
|
34
|
+
: now;
|
|
35
|
+
items.push({
|
|
36
|
+
id: `rss:${Buffer.from(link).toString('base64').slice(0, 32)}`,
|
|
37
|
+
source: 'rss',
|
|
38
|
+
title,
|
|
39
|
+
url: link,
|
|
40
|
+
author: creator || undefined,
|
|
41
|
+
publishedAt: published,
|
|
42
|
+
fetchedAt: now,
|
|
43
|
+
engagement: 0,
|
|
44
|
+
tags: ['rss', ...extractRssTags(title, description || '')],
|
|
45
|
+
summary: description ? stripHtml(description).slice(0, 200) : undefined,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return items;
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
async function getConfiguredFeeds() {
|
|
57
|
+
try {
|
|
58
|
+
const { loadSettings } = await import('../../settings.js');
|
|
59
|
+
const dataDir = process.env.AGORA_DATA_DIR || joinHome('.config', 'agora');
|
|
60
|
+
const settings = loadSettings(dataDir);
|
|
61
|
+
if (settings.news?.feeds && Array.isArray(settings.news.feeds)) {
|
|
62
|
+
return settings.news.feeds.filter(Boolean);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
// settings not available
|
|
67
|
+
}
|
|
68
|
+
return [];
|
|
69
|
+
}
|
|
70
|
+
function extractXmlTag(xml, tag) {
|
|
71
|
+
const regex = new RegExp(`<${tag}[^>]*>([\\s\\S]*?)<\\/${tag}>`, 'i');
|
|
72
|
+
const match = regex.exec(xml);
|
|
73
|
+
return match ? match[1].trim() : null;
|
|
74
|
+
}
|
|
75
|
+
function extractRssTags(title, description) {
|
|
76
|
+
const tags = [];
|
|
77
|
+
const lower = (title + ' ' + description).toLowerCase();
|
|
78
|
+
const topicMap = {
|
|
79
|
+
mcp: ['mcp', 'model-context-protocol'],
|
|
80
|
+
ai: ['ai', 'artificial intelligence'],
|
|
81
|
+
llm: ['llm', 'language model', 'gpt', 'claude'],
|
|
82
|
+
coding: ['coding', 'programming', 'software'],
|
|
83
|
+
devtools: ['devtools', 'developer tools'],
|
|
84
|
+
};
|
|
85
|
+
for (const [topic, keywords] of Object.entries(topicMap)) {
|
|
86
|
+
for (const kw of keywords) {
|
|
87
|
+
if (lower.includes(kw)) {
|
|
88
|
+
tags.push(topic);
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return tags;
|
|
94
|
+
}
|
|
95
|
+
function stripHtml(html) {
|
|
96
|
+
return html.replace(/<[^>]*>/g, '').replace(/[\s\n]+/g, ' ').trim();
|
|
97
|
+
}
|
|
98
|
+
function joinHome(...parts) {
|
|
99
|
+
const home = process.env.HOME || process.env.USERPROFILE || '';
|
|
100
|
+
return parts.length ? `${home}/${parts.join('/')}` : home;
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=rss.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rss.js","sourceRoot":"","sources":["../../../src/news/sources/rss.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export type NewsSource = 'hn' | 'reddit' | 'github-trending' | 'arxiv' | 'rss';
|
|
2
|
+
export interface NewsItem {
|
|
3
|
+
id: string;
|
|
4
|
+
source: NewsSource;
|
|
5
|
+
title: string;
|
|
6
|
+
url: string;
|
|
7
|
+
author?: string;
|
|
8
|
+
publishedAt: string;
|
|
9
|
+
fetchedAt: string;
|
|
10
|
+
engagement: number;
|
|
11
|
+
tags: string[];
|
|
12
|
+
summary?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface ScoredNewsItem extends NewsItem {
|
|
15
|
+
score: number;
|
|
16
|
+
scoreBreakdown: {
|
|
17
|
+
recency: number;
|
|
18
|
+
engagement: number;
|
|
19
|
+
topic: number;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export interface NewsConfig {
|
|
23
|
+
sources: Record<NewsSource, {
|
|
24
|
+
enabled: boolean;
|
|
25
|
+
ttlMinutes: number;
|
|
26
|
+
}>;
|
|
27
|
+
topics: string[];
|
|
28
|
+
weights: {
|
|
29
|
+
recency: number;
|
|
30
|
+
engagement: number;
|
|
31
|
+
topic: number;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export declare const DEFAULT_NEWS_CONFIG: NewsConfig;
|
|
35
|
+
export declare const NEWS_SOURCE_LABELS: Record<NewsSource, string>;
|
|
36
|
+
export declare function normalizeNewsSource(s: string): NewsSource | undefined;
|
|
37
|
+
export declare function hostFromUrl(url: string): string;
|
|
38
|
+
export declare function slugFromUrl(url: string): string;
|
|
39
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/news/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,QAAQ,GAAG,iBAAiB,GAAG,OAAO,GAAG,KAAK,CAAC;AAE/E,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,UAAU,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAe,SAAQ,QAAQ;IAC9C,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CACxE;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC,UAAU,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACtE,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,OAAO,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CACjE;AAED,eAAO,MAAM,mBAAmB,EAAE,UAUjC,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,CAMzD,CAAC;AAEF,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CASrE;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAO/C;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAO/C"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export const DEFAULT_NEWS_CONFIG = {
|
|
2
|
+
sources: {
|
|
3
|
+
hn: { enabled: true, ttlMinutes: 10 },
|
|
4
|
+
reddit: { enabled: true, ttlMinutes: 15 },
|
|
5
|
+
'github-trending': { enabled: true, ttlMinutes: 30 },
|
|
6
|
+
arxiv: { enabled: false, ttlMinutes: 60 },
|
|
7
|
+
rss: { enabled: false, ttlMinutes: 60 },
|
|
8
|
+
},
|
|
9
|
+
topics: ['mcp', 'ai', 'agents', 'workflows', 'llm', 'tool-use', 'coding', 'agents', 'security'],
|
|
10
|
+
weights: { recency: 1.0, engagement: 0.6, topic: 0.8 },
|
|
11
|
+
};
|
|
12
|
+
export const NEWS_SOURCE_LABELS = {
|
|
13
|
+
hn: 'Hacker News',
|
|
14
|
+
reddit: 'Reddit',
|
|
15
|
+
'github-trending': 'GitHub Trending',
|
|
16
|
+
arxiv: 'arXiv',
|
|
17
|
+
rss: 'RSS',
|
|
18
|
+
};
|
|
19
|
+
export function normalizeNewsSource(s) {
|
|
20
|
+
const map = {
|
|
21
|
+
hn: 'hn', hackernews: 'hn', 'hacker-news': 'hn',
|
|
22
|
+
reddit: 'reddit',
|
|
23
|
+
gh: 'github-trending', github: 'github-trending', 'github-trending': 'github-trending',
|
|
24
|
+
arxiv: 'arxiv',
|
|
25
|
+
rss: 'rss',
|
|
26
|
+
};
|
|
27
|
+
return map[s.toLowerCase().trim()];
|
|
28
|
+
}
|
|
29
|
+
export function hostFromUrl(url) {
|
|
30
|
+
try {
|
|
31
|
+
const u = new URL(url);
|
|
32
|
+
return u.hostname.replace(/^www\./, '');
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return '';
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export function slugFromUrl(url) {
|
|
39
|
+
try {
|
|
40
|
+
const u = new URL(url);
|
|
41
|
+
return u.pathname.replace(/\/$/, '').split('/').filter(Boolean).slice(-2).join('/');
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return '';
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/news/types.ts"],"names":[],"mappings":"AA0BA,MAAM,CAAC,MAAM,mBAAmB,GAAe;IAC7C,OAAO,EAAE;QACP,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE;QACrC,MAAM,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE;QACzC,iBAAiB,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE;QACpD,KAAK,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE,EAAE;QACzC,GAAG,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE,EAAE;KACxC;IACD,MAAM,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,EAAE,UAAU,CAAC;IAC/F,OAAO,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE;CACvD,CAAC;AAEF,MAAM,CAAC,MAAM,kBAAkB,GAA+B;IAC5D,EAAE,EAAE,aAAa;IACjB,MAAM,EAAE,QAAQ;IAChB,iBAAiB,EAAE,iBAAiB;IACpC,KAAK,EAAE,OAAO;IACd,GAAG,EAAE,KAAK;CACX,CAAC;AAEF,MAAM,UAAU,mBAAmB,CAAC,CAAS;IAC3C,MAAM,GAAG,GAA+B;QACtC,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI;QAC/C,MAAM,EAAE,QAAQ;QAChB,EAAE,EAAE,iBAAiB,EAAE,MAAM,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,iBAAiB;QACtF,KAAK,EAAE,OAAO;QACd,GAAG,EAAE,KAAK;KACX,CAAC;IACF,OAAO,GAAG,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;AACrC,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QACvB,OAAO,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IAC1C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QACvB,OAAO,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACtF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface LocalPreferences {
|
|
2
|
+
theme: 'dark' | 'light' | 'auto';
|
|
3
|
+
verbosity: 'verbose' | 'medium' | 'quiet';
|
|
4
|
+
defaultNewsSource: string;
|
|
5
|
+
defaultNewsCategory: string;
|
|
6
|
+
username: string;
|
|
7
|
+
email: string;
|
|
8
|
+
bio: string;
|
|
9
|
+
lastTab: number;
|
|
10
|
+
}
|
|
11
|
+
export declare function prefsPath(dataDir: string): string;
|
|
12
|
+
export declare function loadPreferences(dataDir: string): LocalPreferences;
|
|
13
|
+
export declare function writePreferences(dataDir: string, prefs: LocalPreferences): void;
|
|
14
|
+
//# sourceMappingURL=preferences.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preferences.d.ts","sourceRoot":"","sources":["../src/preferences.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;IACjC,SAAS,EAAE,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAC;IAC1C,iBAAiB,EAAE,MAAM,CAAC;IAC1B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;CACjB;AAaD,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB,CAQjE;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAG/E"}
|