youknow 0.3.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 (39) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +361 -0
  3. package/dist/banner.d.ts +1 -0
  4. package/dist/banner.js +14 -0
  5. package/dist/banner.js.map +1 -0
  6. package/dist/channels/claude/action.d.ts +12 -0
  7. package/dist/channels/claude/action.js +71 -0
  8. package/dist/channels/claude/action.js.map +1 -0
  9. package/dist/channels/claude/fetch.d.ts +44 -0
  10. package/dist/channels/claude/fetch.js +182 -0
  11. package/dist/channels/claude/fetch.js.map +1 -0
  12. package/dist/channels/github/action.d.ts +18 -0
  13. package/dist/channels/github/action.js +79 -0
  14. package/dist/channels/github/action.js.map +1 -0
  15. package/dist/channels/github/fetch.d.ts +42 -0
  16. package/dist/channels/github/fetch.js +109 -0
  17. package/dist/channels/github/fetch.js.map +1 -0
  18. package/dist/channels/openai/action.d.ts +12 -0
  19. package/dist/channels/openai/action.js +59 -0
  20. package/dist/channels/openai/action.js.map +1 -0
  21. package/dist/channels/openai/fetch.d.ts +33 -0
  22. package/dist/channels/openai/fetch.js +90 -0
  23. package/dist/channels/openai/fetch.js.map +1 -0
  24. package/dist/cli.d.ts +3 -0
  25. package/dist/cli.js +37 -0
  26. package/dist/cli.js.map +1 -0
  27. package/dist/commands/sources.d.ts +1 -0
  28. package/dist/commands/sources.js +40 -0
  29. package/dist/commands/sources.js.map +1 -0
  30. package/dist/errors.d.ts +7 -0
  31. package/dist/errors.js +45 -0
  32. package/dist/errors.js.map +1 -0
  33. package/dist/network.d.ts +7 -0
  34. package/dist/network.js +65 -0
  35. package/dist/network.js.map +1 -0
  36. package/dist/program.d.ts +2 -0
  37. package/dist/program.js +88 -0
  38. package/dist/program.js.map +1 -0
  39. package/package.json +63 -0
@@ -0,0 +1,182 @@
1
+ import * as cheerio from 'cheerio';
2
+ export const ANTHROPIC_NEWS_URL = 'https://www.anthropic.com/news';
3
+ export const ANTHROPIC_RESEARCH_URL = 'https://www.anthropic.com/research';
4
+ export const CLAUDE_PLATFORM_RELEASES_URL = 'https://platform.claude.com/docs/en/release-notes/overview';
5
+ export const CLAUDE_PLATFORM_FEED_URL = 'https://platform.claude.com/docs/en/release-notes/feed.xml';
6
+ const ANTHROPIC_ROOT = 'https://www.anthropic.com';
7
+ const VALID_SCOPES = new Set([
8
+ 'all',
9
+ 'news',
10
+ 'research',
11
+ 'platform',
12
+ ]);
13
+ function normalizeText(value) {
14
+ return value.replace(/\s+/g, ' ').trim();
15
+ }
16
+ function normalizeHtmlSummary(value) {
17
+ const $ = cheerio.load(value);
18
+ const listItems = $('li')
19
+ .map((_, element) => normalizeText($(element).text()))
20
+ .get()
21
+ .filter(Boolean);
22
+ return listItems.length > 0
23
+ ? listItems.join(' ')
24
+ : normalizeText($.root().text());
25
+ }
26
+ function publishedAt(value, dateOnly) {
27
+ const parsed = new Date(dateOnly ? `${value} 00:00:00 GMT` : value);
28
+ return Number.isNaN(parsed.getTime()) ? null : parsed.toISOString();
29
+ }
30
+ function itemId(type, url) {
31
+ const slug = url.pathname.split('/').filter(Boolean).at(-1);
32
+ const suffix = url.hash.slice(1) || slug || url.toString();
33
+ return `${type}:${suffix}`;
34
+ }
35
+ export function parseAnthropicUpdatesPage(html, scope, collectedAt = new Date().toISOString()) {
36
+ const $ = cheerio.load(html);
37
+ const type = scope;
38
+ const items = [];
39
+ const seenUrls = new Set();
40
+ $(`a[href^="/${scope}/"]:has(time)`).each((_, element) => {
41
+ const link = $(element);
42
+ const rawUrl = link.attr('href')?.trim() ?? '';
43
+ const rawDate = normalizeText(link.find('time').first().text());
44
+ const date = publishedAt(rawDate, true);
45
+ if (!rawUrl || !date)
46
+ return;
47
+ let url;
48
+ try {
49
+ url = new URL(rawUrl, ANTHROPIC_ROOT);
50
+ }
51
+ catch {
52
+ return;
53
+ }
54
+ if (seenUrls.has(url.toString()))
55
+ return;
56
+ const time = link.find('time').first();
57
+ const category = normalizeText(time.siblings('span').first().text());
58
+ const directSpans = link.children('span');
59
+ const title = normalizeText(directSpans.last().text());
60
+ if (!title)
61
+ return;
62
+ seenUrls.add(url.toString());
63
+ items.push({
64
+ source: 'claude',
65
+ type,
66
+ rank: 0,
67
+ id: itemId(type, url),
68
+ title,
69
+ url: url.toString(),
70
+ summary: '',
71
+ category: category || null,
72
+ publishedAt: date,
73
+ collectedAt,
74
+ });
75
+ });
76
+ return items
77
+ .sort((left, right) => right.publishedAt.localeCompare(left.publishedAt))
78
+ .map((item, index) => ({ ...item, rank: index + 1 }));
79
+ }
80
+ export function parseClaudePlatformFeed(xml, collectedAt = new Date().toISOString()) {
81
+ const $ = cheerio.load(xml, { xmlMode: true });
82
+ const items = [];
83
+ $('channel > item').each((_, element) => {
84
+ const item = $(element);
85
+ const title = normalizeText(item.find('title').first().text());
86
+ const rawUrl = normalizeText(item.find('link').first().text());
87
+ const rawPublishedAt = normalizeText(item.find('pubDate').first().text());
88
+ const date = publishedAt(rawPublishedAt, false);
89
+ if (!title || !rawUrl || !date)
90
+ return;
91
+ let url;
92
+ try {
93
+ url = new URL(rawUrl, CLAUDE_PLATFORM_RELEASES_URL);
94
+ }
95
+ catch {
96
+ return;
97
+ }
98
+ items.push({
99
+ source: 'claude',
100
+ type: 'platform-release',
101
+ rank: items.length + 1,
102
+ id: itemId('platform-release', url),
103
+ title,
104
+ url: url.toString(),
105
+ summary: normalizeHtmlSummary(item.find('description').first().text()),
106
+ category: 'Platform',
107
+ publishedAt: date,
108
+ collectedAt,
109
+ });
110
+ });
111
+ return items;
112
+ }
113
+ function scopesFor(scope) {
114
+ return scope === 'all' ? ['news', 'research', 'platform'] : [scope];
115
+ }
116
+ function sourceUrl(scope) {
117
+ if (scope === 'news')
118
+ return ANTHROPIC_NEWS_URL;
119
+ if (scope === 'research')
120
+ return ANTHROPIC_RESEARCH_URL;
121
+ return CLAUDE_PLATFORM_FEED_URL;
122
+ }
123
+ function sourceLabel(scope) {
124
+ if (scope === 'news')
125
+ return 'Anthropic Newsroom';
126
+ if (scope === 'research')
127
+ return 'Anthropic Research';
128
+ return 'Claude Platform RSS';
129
+ }
130
+ async function fetchScope(scope, request, timeout, collectedAt) {
131
+ const response = await request(sourceUrl(scope), {
132
+ headers: {
133
+ accept: scope === 'platform'
134
+ ? 'application/rss+xml,application/xml,text/xml'
135
+ : 'text/html,application/xhtml+xml',
136
+ 'accept-language': 'en-US,en;q=0.8',
137
+ 'user-agent': 'TrendingKnowledge/0.1 (+https://www.anthropic.com/news)',
138
+ },
139
+ signal: AbortSignal.timeout(timeout),
140
+ });
141
+ if (!response.ok) {
142
+ throw new Error(`${sourceLabel(scope)} 请求失败:HTTP ${response.status}`);
143
+ }
144
+ const body = await response.text();
145
+ const items = scope === 'platform'
146
+ ? parseClaudePlatformFeed(body, collectedAt)
147
+ : parseAnthropicUpdatesPage(body, scope, collectedAt);
148
+ if (items.length === 0) {
149
+ throw new Error(`${sourceLabel(scope)} 已返回,但没有解析到动态条目;页面结构可能已变化`);
150
+ }
151
+ return items;
152
+ }
153
+ export async function fetchClaudeUpdates(options = {}, context = {}) {
154
+ const scope = options.scope ?? 'all';
155
+ if (!VALID_SCOPES.has(scope)) {
156
+ throw new Error('--scope 仅支持 all、news、research 或 platform');
157
+ }
158
+ const request = context.fetch ?? globalThis.fetch;
159
+ const collectedAt = new Date().toISOString();
160
+ const batches = await Promise.all(scopesFor(scope).map((requestedScope) => fetchScope(requestedScope, request, options.timeout ?? 20_000, collectedAt)));
161
+ const items = batches
162
+ .flat()
163
+ .sort((left, right) => right.publishedAt.localeCompare(left.publishedAt));
164
+ items.forEach((item, index) => {
165
+ item.rank = index + 1;
166
+ });
167
+ return {
168
+ source: 'claude',
169
+ type: 'updates-feed',
170
+ scope,
171
+ sourceUrls: {
172
+ news: ANTHROPIC_NEWS_URL,
173
+ research: ANTHROPIC_RESEARCH_URL,
174
+ platform: CLAUDE_PLATFORM_RELEASES_URL,
175
+ platformFeed: CLAUDE_PLATFORM_FEED_URL,
176
+ },
177
+ collectedAt,
178
+ total: items.length,
179
+ items,
180
+ };
181
+ }
182
+ //# sourceMappingURL=fetch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch.js","sourceRoot":"","sources":["../../../src/channels/claude/fetch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import { type GitHubTrendingResult } from './fetch.js';
2
+ export type GitHubOutputFormat = 'json' | 'md';
3
+ export declare const githubDefaults: {
4
+ readonly language: "typescript";
5
+ readonly period: "weekly";
6
+ };
7
+ export interface GitHubCommandOptions {
8
+ language: string;
9
+ since: string;
10
+ spokenLanguage?: string;
11
+ limit: number;
12
+ format: GitHubOutputFormat;
13
+ output?: string;
14
+ timeout: number;
15
+ }
16
+ export declare function formatGitHubJson(result: GitHubTrendingResult): string;
17
+ export declare function formatGitHubMarkdown(result: GitHubTrendingResult): string;
18
+ export declare function runGitHubCommand(options: GitHubCommandOptions): Promise<void>;
@@ -0,0 +1,79 @@
1
+ import { mkdir, writeFile } from 'node:fs/promises';
2
+ import { dirname, resolve } from 'node:path';
3
+ import chalk from 'chalk';
4
+ import { fetchTrending } from './fetch.js';
5
+ export const githubDefaults = {
6
+ language: 'typescript',
7
+ period: 'weekly',
8
+ };
9
+ const periodLabels = {
10
+ daily: '今日新增',
11
+ weekly: '本周新增',
12
+ monthly: '本月新增',
13
+ };
14
+ function escapeMarkdown(value) {
15
+ return (value ?? '—')
16
+ .replaceAll('\\', '\\\\')
17
+ .replaceAll('|', '\\|')
18
+ .replaceAll('\r', ' ')
19
+ .replaceAll('\n', ' ')
20
+ .trim();
21
+ }
22
+ function escapeLinkText(value) {
23
+ return escapeMarkdown(value).replaceAll('[', '\\[').replaceAll(']', '\\]');
24
+ }
25
+ export function formatGitHubJson(result) {
26
+ return JSON.stringify(result, null, 2);
27
+ }
28
+ export function formatGitHubMarkdown(result) {
29
+ const gainedLabel = periodLabels[result.period] ?? '周期新增';
30
+ const metadata = [
31
+ `- 开发语言:${escapeMarkdown(result.language)}`,
32
+ `- 时间范围:${escapeMarkdown(result.period)}`,
33
+ `- 采集时间:${escapeMarkdown(result.collectedAt)}`,
34
+ `- 仓库数量:${result.total}`,
35
+ ];
36
+ const header = `| # | 仓库 | 语言 | Stars | ${gainedLabel} | 简介 |`;
37
+ const divider = '|---:|---|---|---:|---:|---|';
38
+ const rows = result.items.map((item) => {
39
+ const repository = `[${escapeLinkText(item.title)}](${item.url})`;
40
+ return `| ${item.rank} | ${repository} | ${escapeMarkdown(item.language)} | ${item.metrics.stars} | ${item.metrics.periodStars} | ${escapeMarkdown(item.summary)} |`;
41
+ });
42
+ return [
43
+ `# ${escapeMarkdown(result.source)}`,
44
+ '',
45
+ ...metadata,
46
+ '',
47
+ header,
48
+ divider,
49
+ ...rows,
50
+ ].join('\n');
51
+ }
52
+ function formatGitHubResult(result, format) {
53
+ return format === 'md'
54
+ ? formatGitHubMarkdown(result)
55
+ : formatGitHubJson(result);
56
+ }
57
+ async function saveGitHubOutput(path, content) {
58
+ const absolutePath = resolve(path);
59
+ await mkdir(dirname(absolutePath), { recursive: true });
60
+ await writeFile(absolutePath, `${content}\n`, 'utf8');
61
+ return absolutePath;
62
+ }
63
+ export async function runGitHubCommand(options) {
64
+ const result = await fetchTrending({
65
+ period: options.since,
66
+ language: options.language,
67
+ spokenLanguage: options.spokenLanguage,
68
+ timeout: options.timeout,
69
+ });
70
+ result.items = result.items.slice(0, options.limit);
71
+ result.total = result.items.length;
72
+ const content = formatGitHubResult(result, options.format);
73
+ process.stdout.write(`${content}\n`);
74
+ if (options.output) {
75
+ const savedTo = await saveGitHubOutput(options.output, content);
76
+ process.stderr.write(`${chalk.green('✓')} 已保存到 ${chalk.magenta(savedTo)}\n`);
77
+ }
78
+ }
79
+ //# sourceMappingURL=action.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"action.js","sourceRoot":"","sources":["../../../src/channels/github/action.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ export interface GitHubTrendingMetrics {
2
+ stars: number;
3
+ forks: number;
4
+ periodStars: number;
5
+ }
6
+ export interface GitHubTrendingItem {
7
+ source: 'github';
8
+ type: 'repository';
9
+ rank: number;
10
+ id: string;
11
+ title: string;
12
+ url: string;
13
+ summary: string;
14
+ author: string;
15
+ language: string | null;
16
+ metrics: GitHubTrendingMetrics;
17
+ contributors: string[];
18
+ period: string;
19
+ collectedAt: string;
20
+ }
21
+ export interface GitHubTrendingResult {
22
+ source: 'github treeding';
23
+ period: string;
24
+ language: string | null;
25
+ spokenLanguage: string | null;
26
+ collectedAt: string;
27
+ total: number;
28
+ items: GitHubTrendingItem[];
29
+ }
30
+ export interface GitHubFetchOptions {
31
+ period?: string;
32
+ language?: string;
33
+ spokenLanguage?: string;
34
+ timeout?: number;
35
+ }
36
+ export interface GitHubFetchContext {
37
+ fetch?: typeof globalThis.fetch;
38
+ }
39
+ export declare function parseCount(value?: string): number;
40
+ export declare function parseTrendingHtml(html: string, { period }?: Pick<GitHubFetchOptions, 'period'>): GitHubTrendingItem[];
41
+ export declare function buildTrendingUrl({ period, language, spokenLanguage, }?: GitHubFetchOptions): URL;
42
+ export declare function fetchTrending(options?: GitHubFetchOptions): Promise<GitHubTrendingResult>;
@@ -0,0 +1,109 @@
1
+ import * as cheerio from 'cheerio';
2
+ const githubDefaults = {
3
+ language: 'typescript',
4
+ period: 'weekly',
5
+ };
6
+ const GITHUB_ROOT = 'https://github.com';
7
+ const VALID_PERIODS = new Set(['daily', 'weekly', 'monthly']);
8
+ export function parseCount(value = '') {
9
+ const normalized = value.trim().toLowerCase().replaceAll(',', '');
10
+ const match = normalized.match(/([0-9]+(?:\.[0-9]+)?)\s*([km]?)/);
11
+ if (!match)
12
+ return 0;
13
+ const numericValue = match[1];
14
+ const suffix = match[2] ?? '';
15
+ if (!numericValue)
16
+ return 0;
17
+ const multipliers = { '': 1, k: 1_000, m: 1_000_000 };
18
+ return Math.round(Number.parseFloat(numericValue) * (multipliers[suffix] ?? 1));
19
+ }
20
+ function normalizedText(element) {
21
+ return element.text().replace(/\s+/g, ' ').trim();
22
+ }
23
+ export function parseTrendingHtml(html, { period = githubDefaults.period } = {}) {
24
+ const $ = cheerio.load(html);
25
+ const collectedAt = new Date().toISOString();
26
+ const items = [];
27
+ $('article.Box-row').each((_, article) => {
28
+ const repoLink = $(article).find('h2 a[href]').first();
29
+ const path = repoLink.attr('href')?.trim() ?? '';
30
+ const [author, repository] = path.split('/').filter(Boolean);
31
+ if (!author || !repository)
32
+ return;
33
+ const language = normalizedText($(article).find('[itemprop="programmingLanguage"]').first());
34
+ const contributorNames = $(article)
35
+ .find('span:contains("Built by") a img[alt]')
36
+ .map((__, image) => ($(image).attr('alt') ?? '').replace(/^@/, '').trim())
37
+ .get()
38
+ .filter(Boolean);
39
+ items.push({
40
+ source: 'github',
41
+ type: 'repository',
42
+ rank: items.length + 1,
43
+ id: `${author}/${repository}`,
44
+ title: `${author}/${repository}`,
45
+ url: `${GITHUB_ROOT}${path}`,
46
+ summary: normalizedText($(article).find('p').first()),
47
+ author,
48
+ language: language || null,
49
+ metrics: {
50
+ stars: parseCount(normalizedText($(article).find('a[href$="/stargazers"]').first())),
51
+ forks: parseCount(normalizedText($(article).find('a[href$="/forks"]').first())),
52
+ periodStars: parseCount(normalizedText($(article).find('span.d-inline-block.float-sm-right').first())),
53
+ },
54
+ contributors: contributorNames,
55
+ period,
56
+ collectedAt,
57
+ });
58
+ });
59
+ return items;
60
+ }
61
+ export function buildTrendingUrl({ period = githubDefaults.period, language = githubDefaults.language, spokenLanguage, } = {}) {
62
+ if (!VALID_PERIODS.has(period)) {
63
+ throw new Error('--since 仅支持 daily、weekly 或 monthly');
64
+ }
65
+ const languagePath = language
66
+ ? `/${encodeURIComponent(language.toLowerCase())}`
67
+ : '';
68
+ const url = new URL(`/trending${languagePath}`, GITHUB_ROOT);
69
+ url.searchParams.set('since', period);
70
+ if (spokenLanguage) {
71
+ url.searchParams.set('spoken_language_code', spokenLanguage.toLowerCase());
72
+ }
73
+ return url;
74
+ }
75
+ export async function fetchTrending(options = {}) {
76
+ const request = globalThis.fetch;
77
+ const url = buildTrendingUrl(options);
78
+ const response = await request(url, {
79
+ headers: {
80
+ accept: 'text/html,application/xhtml+xml',
81
+ 'accept-language': 'en-US,en;q=0.8',
82
+ 'user-agent': 'TrendingKnowledge/0.1 (+https://github.com/trending)',
83
+ },
84
+ signal: AbortSignal.timeout(options.timeout ?? 20_000),
85
+ });
86
+ if (!response.ok) {
87
+ throw new Error(`GitHub 请求失败:HTTP ${response.status}`);
88
+ }
89
+ const items = parseTrendingHtml(await response.text(), {
90
+ period: options.period,
91
+ });
92
+ if (items.length === 0) {
93
+ throw new Error('GitHub 页面已返回,但没有解析到热门仓库;页面结构可能已变化');
94
+ }
95
+ const firstItem = items[0];
96
+ if (!firstItem) {
97
+ throw new Error('GitHub Trending 没有可用数据');
98
+ }
99
+ return {
100
+ source: 'github treeding',
101
+ period: options.period ?? githubDefaults.period,
102
+ language: options.language ?? githubDefaults.language,
103
+ spokenLanguage: options.spokenLanguage ?? null,
104
+ collectedAt: firstItem.collectedAt,
105
+ total: items.length,
106
+ items,
107
+ };
108
+ }
109
+ //# sourceMappingURL=fetch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch.js","sourceRoot":"","sources":["../../../src/channels/github/fetch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import { type OpenAINewsResult } from './fetch.js';
2
+ export type OpenAIOutputFormat = 'json' | 'md';
3
+ export interface OpenAICommandOptions {
4
+ category?: string;
5
+ limit: number;
6
+ format: OpenAIOutputFormat;
7
+ output?: string;
8
+ timeout: number;
9
+ }
10
+ export declare function formatOpenAIJson(result: OpenAINewsResult): string;
11
+ export declare function formatOpenAIMarkdown(result: OpenAINewsResult): string;
12
+ export declare function runOpenAICommand(options: OpenAICommandOptions): Promise<void>;
@@ -0,0 +1,59 @@
1
+ import { mkdir, writeFile } from 'node:fs/promises';
2
+ import { dirname, resolve } from 'node:path';
3
+ import chalk from 'chalk';
4
+ import { fetchOpenAINews } from './fetch.js';
5
+ function escapeMarkdown(value) {
6
+ return (value ?? '—')
7
+ .replaceAll('\\', '\\\\')
8
+ .replaceAll('|', '\\|')
9
+ .replaceAll('\r', ' ')
10
+ .replaceAll('\n', ' ')
11
+ .trim();
12
+ }
13
+ function escapeLinkText(value) {
14
+ return escapeMarkdown(value).replaceAll('[', '\\[').replaceAll(']', '\\]');
15
+ }
16
+ export function formatOpenAIJson(result) {
17
+ return JSON.stringify(result, null, 2);
18
+ }
19
+ export function formatOpenAIMarkdown(result) {
20
+ const metadata = [
21
+ `- 来源页面:${result.pageUrl}`,
22
+ `- 分类:${escapeMarkdown(result.category)}`,
23
+ `- 采集时间:${escapeMarkdown(result.collectedAt)}`,
24
+ `- 新闻数量:${result.total}`,
25
+ ];
26
+ const header = '| # | 标题 | 分类 | 发布日期 | 摘要 |';
27
+ const divider = '|---:|---|---|---|---|';
28
+ const rows = result.items.map((item) => {
29
+ const title = `[${escapeLinkText(item.title)}](${item.url})`;
30
+ return `| ${item.rank} | ${title} | ${escapeMarkdown(item.category)} | ${item.publishedAt.slice(0, 10)} | ${escapeMarkdown(item.summary)} |`;
31
+ });
32
+ return ['# OpenAI 新闻', '', ...metadata, '', header, divider, ...rows].join('\n');
33
+ }
34
+ function formatOpenAIResult(result, format) {
35
+ return format === 'md'
36
+ ? formatOpenAIMarkdown(result)
37
+ : formatOpenAIJson(result);
38
+ }
39
+ async function saveOpenAIOutput(path, content) {
40
+ const absolutePath = resolve(path);
41
+ await mkdir(dirname(absolutePath), { recursive: true });
42
+ await writeFile(absolutePath, `${content}\n`, 'utf8');
43
+ return absolutePath;
44
+ }
45
+ export async function runOpenAICommand(options) {
46
+ const result = await fetchOpenAINews({
47
+ category: options.category,
48
+ timeout: options.timeout,
49
+ });
50
+ result.items = result.items.slice(0, options.limit);
51
+ result.total = result.items.length;
52
+ const content = formatOpenAIResult(result, options.format);
53
+ process.stdout.write(`${content}\n`);
54
+ if (options.output) {
55
+ const savedTo = await saveOpenAIOutput(options.output, content);
56
+ process.stderr.write(`${chalk.green('✓')} 已保存到 ${chalk.magenta(savedTo)}\n`);
57
+ }
58
+ }
59
+ //# sourceMappingURL=action.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"action.js","sourceRoot":"","sources":["../../../src/channels/openai/action.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ export declare const OPENAI_NEWS_PAGE_URL = "https://openai.com/zh-Hans-CN/news/";
2
+ export declare const OPENAI_NEWS_FEED_URL = "https://openai.com/news/rss.xml";
3
+ export interface OpenAINewsItem {
4
+ source: 'openai';
5
+ type: 'news';
6
+ rank: number;
7
+ id: string;
8
+ title: string;
9
+ url: string;
10
+ summary: string;
11
+ category: string | null;
12
+ publishedAt: string;
13
+ collectedAt: string;
14
+ }
15
+ export interface OpenAINewsResult {
16
+ source: 'openai';
17
+ type: 'news-feed';
18
+ pageUrl: string;
19
+ feedUrl: string;
20
+ category: string | null;
21
+ collectedAt: string;
22
+ total: number;
23
+ items: OpenAINewsItem[];
24
+ }
25
+ export interface OpenAIFetchOptions {
26
+ category?: string;
27
+ timeout?: number;
28
+ }
29
+ export interface OpenAIFetchContext {
30
+ fetch?: typeof globalThis.fetch;
31
+ }
32
+ export declare function parseOpenAINewsFeed(xml: string): OpenAINewsItem[];
33
+ export declare function fetchOpenAINews(options?: OpenAIFetchOptions, context?: OpenAIFetchContext): Promise<OpenAINewsResult>;