trending-knowledge 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 ADDED
@@ -0,0 +1,206 @@
1
+ # youKnow
2
+
3
+ `youKnow` 是一个在终端中获取热门信息的命令行工具。目前支持 GitHub Trending,
4
+ 可以按开发语言和时间范围筛选热门仓库,并输出 JSON 或 Markdown。
5
+
6
+ ## 环境要求
7
+
8
+ - Node.js 20.18.1 或更高版本
9
+ - 能够访问 `github.com`
10
+
11
+ ## 安装
12
+
13
+ ### 全局安装
14
+
15
+ ```bash
16
+ npm install --global trending-knowledge
17
+ ```
18
+
19
+ 安装后可以使用 `youKnow` 或小写形式 `youknow`:
20
+
21
+ ```bash
22
+ youKnow github
23
+ ```
24
+
25
+ ### 使用 npx
26
+
27
+ 无需全局安装即可运行:
28
+
29
+ ```bash
30
+ npx trending-knowledge github
31
+ ```
32
+
33
+ 例如获取 Python 今日热门前 5 名:
34
+
35
+ ```bash
36
+ npx trending-knowledge github --since daily --language python --limit 5
37
+ ```
38
+
39
+ ## 获取 GitHub Trending
40
+
41
+ 不带参数时,默认获取 TypeScript 本周热门仓库,最多返回 20 条,并输出 JSON:
42
+
43
+ ```bash
44
+ youKnow github
45
+ ```
46
+
47
+ 常用示例:
48
+
49
+ ```bash
50
+ # Python 今日热门前 5 名
51
+ youKnow github --since daily --language python --limit 5
52
+
53
+ # 中文项目月榜
54
+ youKnow github --since monthly --spoken-language zh
55
+
56
+ # 输出 Markdown
57
+ youKnow github --format md
58
+
59
+ # 输出 Markdown,并保存到指定文件
60
+ youKnow github --format md --output output/github.md
61
+
62
+ # 将请求超时时间调整为 30 秒
63
+ youKnow github --timeout 30000
64
+ ```
65
+
66
+ ### 参数
67
+
68
+ | 参数 | 简写 | 说明 | 默认值 |
69
+ | -------------------------- | ---- | -------------------------------------- | ------------ |
70
+ | `--language <language>` | `-l` | 开发语言,例如 `typescript`、`python` | `typescript` |
71
+ | `--since <since>` | `-s` | 时间范围:`daily`、`weekly`、`monthly` | `weekly` |
72
+ | `--spoken-language <code>` | — | 项目自然语言代码,例如 `zh`、`en` | 不限制 |
73
+ | `--limit <number>` | `-n` | 返回条数,必须大于 0 | `20` |
74
+ | `--format <format>` | `-f` | 输出格式:`json` 或 `md` | `json` |
75
+ | `--output <path>` | `-o` | 将结果同时保存到文件 | 不保存 |
76
+ | `--timeout <ms>` | — | 请求超时时间,单位为毫秒 | `20000` |
77
+
78
+ 查看完整帮助:
79
+
80
+ ```bash
81
+ youKnow github --help
82
+ ```
83
+
84
+ ## 输出格式
85
+
86
+ ### JSON
87
+
88
+ 默认输出包含筛选条件、采集时间和仓库列表:
89
+
90
+ ```json
91
+ {
92
+ "source": "github treeding",
93
+ "period": "weekly",
94
+ "language": "typescript",
95
+ "spokenLanguage": null,
96
+ "collectedAt": "2026-08-30T02:08:44.881Z",
97
+ "total": 1,
98
+ "items": [
99
+ {
100
+ "source": "github",
101
+ "type": "repository",
102
+ "rank": 1,
103
+ "id": "owner/repository",
104
+ "title": "owner/repository",
105
+ "url": "https://github.com/owner/repository",
106
+ "summary": "Repository description",
107
+ "author": "owner",
108
+ "language": "TypeScript",
109
+ "metrics": {
110
+ "stars": 1234,
111
+ "forks": 56,
112
+ "periodStars": 78
113
+ },
114
+ "contributors": ["owner"],
115
+ "period": "weekly",
116
+ "collectedAt": "2026-08-30T02:08:44.881Z"
117
+ }
118
+ ]
119
+ }
120
+ ```
121
+
122
+ `periodStars` 表示所选时间范围内新增的 Star 数量。
123
+
124
+ ### Markdown
125
+
126
+ 使用 `--format md` 可以生成适合保存、分享或粘贴到文档中的 Markdown 表格。
127
+ 搭配 `--output` 时,结果会输出到终端并写入文件。
128
+
129
+ ## 网络代理
130
+
131
+ `youKnow` 会自动使用以下代理环境变量:
132
+
133
+ - `HTTPS_PROXY` / `https_proxy`
134
+ - `HTTP_PROXY` / `http_proxy`
135
+ - `ALL_PROXY` / `all_proxy`,仅作为 HTTP/HTTPS 代理后备
136
+ - `NO_PROXY` / `no_proxy`
137
+
138
+ 无需在命令前添加 `NODE_USE_ENV_PROXY=1`。
139
+
140
+ 在 macOS 上,如果终端没有代理环境变量,`youKnow` 会继续读取系统网络代理。
141
+ 未检测到代理时使用直连。
142
+
143
+ ## 错误输出
144
+
145
+ 请求失败时,错误会写入标准错误流,并保持固定 JSON 结构:
146
+
147
+ ```json
148
+ {
149
+ "name": "TimeoutError",
150
+ "message": "The operation was aborted due to timeout",
151
+ "code": 23
152
+ }
153
+ ```
154
+
155
+ 如果底层错误没有错误码,`code` 为 `"UNKNOWN_ERROR"`。
156
+
157
+ ## 排查请求超时
158
+
159
+ 先确认 GitHub 是否可以通过当前网络或代理访问:
160
+
161
+ ```bash
162
+ curl -L -sS \
163
+ -o /dev/null \
164
+ -w 'HTTP %{http_code}\n耗时 %{time_total}s\n' \
165
+ --max-time 20 \
166
+ 'https://github.com/trending/typescript?since=weekly'
167
+ ```
168
+
169
+ 正常情况下应返回 `HTTP 200`。如果 `curl` 也超时,请检查网络或代理软件;如果
170
+ `curl` 成功但 `youKnow` 失败,可以增加超时时间后重试:
171
+
172
+ ```bash
173
+ youKnow github --timeout 30000
174
+ ```
175
+
176
+ ## 查看数据源
177
+
178
+ 查看已支持及规划中的数据源:
179
+
180
+ ```bash
181
+ youKnow sources
182
+ ```
183
+
184
+ GitHub Trending 数据来自 GitHub 的公开 Trending 页面,不需要 GitHub Token。
185
+ 如果 GitHub 调整页面结构,仓库字段可能暂时无法解析。
186
+
187
+ ## 从源码运行
188
+
189
+ ```bash
190
+ git clone https://github.com/wanglsh97/trending-knowledge.git
191
+ cd trending-knowledge
192
+ npm install
193
+ npm run dev -- github
194
+ ```
195
+
196
+ 开发时如需创建直接指向源码的全局命令:
197
+
198
+ ```bash
199
+ npm run link
200
+ ```
201
+
202
+ 移除源码软链:
203
+
204
+ ```bash
205
+ npm run unlink
206
+ ```
@@ -0,0 +1 @@
1
+ export declare function renderBanner(color?: boolean): string;
package/dist/banner.js ADDED
@@ -0,0 +1,14 @@
1
+ import { Chalk } from 'chalk';
2
+ const bannerLines = [
3
+ '██╗ ██╗ ██████╗ ██╗ ██╗██╗ ██╗███╗ ██╗ ██████╗ ██╗ ██╗',
4
+ '╚██╗ ██╔╝██╔═══██╗██║ ██║██║ ██╔╝████╗ ██║██╔═══██╗██║ ██║',
5
+ ' ╚████╔╝ ██║ ██║██║ ██║█████╔╝ ██╔██╗ ██║██║ ██║██║ █╗ ██║',
6
+ ' ╚██╔╝ ██║ ██║██║ ██║██╔═██╗ ██║╚██╗██║██║ ██║██║███╗██║',
7
+ ' ██║ ╚██████╔╝╚██████╔╝██║ ██╗██║ ╚████║╚██████╔╝╚███╔███╔╝',
8
+ ' ╚═╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═════╝ ╚══╝╚══╝',
9
+ ];
10
+ export function renderBanner(color = Boolean(process.stdout.isTTY)) {
11
+ const paint = new Chalk({ level: color ? 1 : 0 });
12
+ return bannerLines.map((line) => paint.magenta.bold(line)).join('\n');
13
+ }
14
+ //# sourceMappingURL=banner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"banner.js","sourceRoot":"","sources":["../src/banner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAE9B,MAAM,WAAW,GAAG;IAClB,kEAAkE;IAClE,kEAAkE;IAClE,kEAAkE;IAClE,kEAAkE;IAClE,kEAAkE;IAClE,iEAAiE;CAClE,CAAC;AAEF,MAAM,UAAU,YAAY,CAAC,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC;IAChE,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAClD,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACxE,CAAC"}
@@ -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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"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export declare function printCliError(error: unknown): void;
3
+ export declare function main(argv?: string[]): Promise<void>;
package/dist/cli.js ADDED
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env node
2
+ import { realpathSync } from 'node:fs';
3
+ import { createRequire } from 'node:module';
4
+ import { fileURLToPath } from 'node:url';
5
+ import { formatErrorResponse } from './errors.js';
6
+ import { configureNetwork } from './network.js';
7
+ import { createProgram } from './program.js';
8
+ const require = createRequire(import.meta.url);
9
+ const packageJson = require('../package.json');
10
+ export function printCliError(error) {
11
+ process.stderr.write(`${formatErrorResponse(error)}\n`);
12
+ process.exitCode = 1;
13
+ }
14
+ export async function main(argv = process.argv.slice(2)) {
15
+ configureNetwork();
16
+ const program = createProgram(packageJson.version);
17
+ if (argv.length === 0) {
18
+ program.outputHelp();
19
+ return;
20
+ }
21
+ await program.parseAsync(argv, { from: 'user' });
22
+ }
23
+ function isCliEntrypoint() {
24
+ if (!process.argv[1])
25
+ return false;
26
+ try {
27
+ return (realpathSync(process.argv[1]) ===
28
+ realpathSync(fileURLToPath(import.meta.url)));
29
+ }
30
+ catch {
31
+ return false;
32
+ }
33
+ }
34
+ if (isCliEntrypoint()) {
35
+ main().catch(printCliError);
36
+ }
37
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC/C,MAAM,WAAW,GAAG,OAAO,CAAC,iBAAiB,CAAwB,CAAC;AAEtE,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,mBAAmB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACxD,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AACvB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,IAAI,CACxB,OAAiB,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IAEtC,gBAAgB,EAAE,CAAC;IACnB,MAAM,OAAO,GAAG,aAAa,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;IACnD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,OAAO,CAAC,UAAU,EAAE,CAAC;QACrB,OAAO;IACT,CAAC;IACD,MAAM,OAAO,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;AACnD,CAAC;AAED,SAAS,eAAe;IACtB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IACnC,IAAI,CAAC;QACH,OAAO,CACL,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAC7B,YAAY,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAC7C,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,IAAI,eAAe,EAAE,EAAE,CAAC;IACtB,IAAI,EAAE,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;AAC9B,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function runSourcesCommand(): void;
@@ -0,0 +1,40 @@
1
+ import chalk from 'chalk';
2
+ const sourceCatalog = [
3
+ {
4
+ key: 'github',
5
+ name: 'GitHub Trending',
6
+ status: 'active',
7
+ description: 'GitHub 社区每日、每周和每月热门仓库',
8
+ },
9
+ {
10
+ key: 'openai',
11
+ name: 'OpenAI 官网',
12
+ status: 'planned',
13
+ description: '新闻、研究和产品更新',
14
+ },
15
+ {
16
+ key: 'claude',
17
+ name: 'Claude 官网',
18
+ status: 'planned',
19
+ description: 'Anthropic 新闻和产品更新',
20
+ },
21
+ {
22
+ key: 'x',
23
+ name: 'X',
24
+ status: 'planned',
25
+ description: '指定账号和话题热度',
26
+ },
27
+ ];
28
+ export function runSourcesCommand() {
29
+ const lines = sourceCatalog.map((source) => {
30
+ const active = source.status === 'active';
31
+ const marker = active ? chalk.green('●') : chalk.gray('○');
32
+ const key = active
33
+ ? chalk.magenta.bold(source.key.padEnd(8))
34
+ : chalk.gray(source.key.padEnd(8));
35
+ const name = active ? chalk.white(source.name) : chalk.gray(source.name);
36
+ return `${marker} ${key} ${name} ${chalk.dim(source.description)}`;
37
+ });
38
+ process.stdout.write(`${chalk.magenta.bold('数据源')}\n\n${lines.join('\n')}\n`);
39
+ }
40
+ //# sourceMappingURL=sources.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sources.js","sourceRoot":"","sources":["../../src/commands/sources.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAS1B,MAAM,aAAa,GAAwB;IACzC;QACE,GAAG,EAAE,QAAQ;QACb,IAAI,EAAE,iBAAiB;QACvB,MAAM,EAAE,QAAQ;QAChB,WAAW,EAAE,uBAAuB;KACrC;IACD;QACE,GAAG,EAAE,QAAQ;QACb,IAAI,EAAE,WAAW;QACjB,MAAM,EAAE,SAAS;QACjB,WAAW,EAAE,YAAY;KAC1B;IACD;QACE,GAAG,EAAE,QAAQ;QACb,IAAI,EAAE,WAAW;QACjB,MAAM,EAAE,SAAS;QACjB,WAAW,EAAE,mBAAmB;KACjC;IACD;QACE,GAAG,EAAE,GAAG;QACR,IAAI,EAAE,GAAG;QACT,MAAM,EAAE,SAAS;QACjB,WAAW,EAAE,WAAW;KACzB;CACF,CAAC;AAEF,MAAM,UAAU,iBAAiB;IAC/B,MAAM,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE;QACzC,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,KAAK,QAAQ,CAAC;QAC1C,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC3D,MAAM,GAAG,GAAG,MAAM;YAChB,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YAC1C,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QACrC,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACzE,OAAO,GAAG,MAAM,IAAI,GAAG,IAAI,IAAI,KAAK,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC;IACtE,CAAC,CAAC,CAAC;IAEH,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CACxD,CAAC;AACJ,CAAC"}
@@ -0,0 +1,7 @@
1
+ export interface SerializedError {
2
+ name: string;
3
+ message: string;
4
+ code: string | number;
5
+ }
6
+ export declare function serializeError(error: unknown): SerializedError;
7
+ export declare function formatErrorResponse(error: unknown): string;
package/dist/errors.js ADDED
@@ -0,0 +1,45 @@
1
+ function errorProperty(error, property) {
2
+ try {
3
+ return Reflect.get(error, property);
4
+ }
5
+ catch {
6
+ return undefined;
7
+ }
8
+ }
9
+ export function serializeError(error) {
10
+ const seen = new WeakSet();
11
+ let current = error;
12
+ while (typeof current === 'object' && current !== null) {
13
+ if (seen.has(current))
14
+ break;
15
+ seen.add(current);
16
+ const cause = errorProperty(current, 'cause');
17
+ if (cause === undefined)
18
+ break;
19
+ current = cause;
20
+ }
21
+ const rootCause = current;
22
+ if (typeof rootCause !== 'object' || rootCause === null) {
23
+ return {
24
+ name: 'Error',
25
+ message: String(rootCause),
26
+ code: 'UNKNOWN_ERROR',
27
+ };
28
+ }
29
+ const rawName = errorProperty(rootCause, 'name');
30
+ const rawMessage = errorProperty(rootCause, 'message');
31
+ const rawCode = errorProperty(rootCause, 'code');
32
+ return {
33
+ name: typeof rawName === 'string' && rawName ? rawName : 'Error',
34
+ message: typeof rawMessage === 'string' && rawMessage
35
+ ? rawMessage
36
+ : String(rootCause),
37
+ code: typeof rawCode === 'string' || typeof rawCode === 'number'
38
+ ? rawCode
39
+ : 'UNKNOWN_ERROR',
40
+ };
41
+ }
42
+ export function formatErrorResponse(error) {
43
+ return JSON.stringify(serializeError(error), null, 2);
44
+ }
45
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAMA,SAAS,aAAa,CACpB,KAAa,EACb,QAA+C;IAE/C,IAAI,CAAC;QACH,OAAO,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,MAAM,IAAI,GAAG,IAAI,OAAO,EAAU,CAAC;IACnC,IAAI,OAAO,GAAG,KAAK,CAAC;IAEpB,OAAO,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACvD,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC;YAAE,MAAM;QAC7B,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAElB,MAAM,KAAK,GAAG,aAAa,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAC9C,IAAI,KAAK,KAAK,SAAS;YAAE,MAAM;QAC/B,OAAO,GAAG,KAAK,CAAC;IAClB,CAAC;IAED,MAAM,SAAS,GAAG,OAAO,CAAC;IAC1B,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;QACxD,OAAO;YACL,IAAI,EAAE,OAAO;YACb,OAAO,EAAE,MAAM,CAAC,SAAS,CAAC;YAC1B,IAAI,EAAE,eAAe;SACtB,CAAC;IACJ,CAAC;IAED,MAAM,OAAO,GAAG,aAAa,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IACjD,MAAM,UAAU,GAAG,aAAa,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;IACvD,MAAM,OAAO,GAAG,aAAa,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IACjD,OAAO;QACL,IAAI,EAAE,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO;QAChE,OAAO,EACL,OAAO,UAAU,KAAK,QAAQ,IAAI,UAAU;YAC1C,CAAC,CAAC,UAAU;YACZ,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC;QACvB,IAAI,EACF,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,OAAO,KAAK,QAAQ;YACxD,CAAC,CAAC,OAAO;YACT,CAAC,CAAC,eAAe;KACtB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,KAAc;IAChD,OAAO,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;AACxD,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { type Dispatcher } from 'undici';
2
+ type NetworkEnvironment = Partial<Pick<NodeJS.ProcessEnv, 'HTTP_PROXY' | 'http_proxy' | 'HTTPS_PROXY' | 'https_proxy' | 'ALL_PROXY' | 'all_proxy' | 'NO_PROXY' | 'no_proxy'>>;
3
+ type DispatcherSetter = (dispatcher: Dispatcher) => void;
4
+ export declare function parseMacOSProxySettings(output: string): NetworkEnvironment;
5
+ export declare function configureNetworkFromEnvironment(environment?: NetworkEnvironment, setDispatcher?: DispatcherSetter): boolean;
6
+ export declare function configureNetwork(): boolean;
7
+ export {};
@@ -0,0 +1,65 @@
1
+ import { execFileSync } from 'node:child_process';
2
+ import { EnvHttpProxyAgent, setGlobalDispatcher, } from 'undici';
3
+ let proxyAgent;
4
+ function firstValue(...values) {
5
+ return values.find((value) => value !== undefined && value.length > 0);
6
+ }
7
+ function httpCompatibleProxy(value) {
8
+ if (!value)
9
+ return undefined;
10
+ try {
11
+ const protocol = new URL(value).protocol;
12
+ return protocol === 'http:' || protocol === 'https:' ? value : undefined;
13
+ }
14
+ catch {
15
+ return undefined;
16
+ }
17
+ }
18
+ function macOSProxyUrl(output, kind) {
19
+ const enabled = new RegExp(`^\\s*${kind}Enable\\s*:\\s*1\\s*$`, 'm').test(output);
20
+ if (!enabled)
21
+ return undefined;
22
+ const host = output.match(new RegExp(`^\\s*${kind}Proxy\\s*:\\s*(\\S+)\\s*$`, 'm'))?.[1];
23
+ const port = output.match(new RegExp(`^\\s*${kind}Port\\s*:\\s*(\\d+)\\s*$`, 'm'))?.[1];
24
+ if (!host || !port)
25
+ return undefined;
26
+ const urlHost = host.includes(':') ? `[${host}]` : host;
27
+ return `http://${urlHost}:${port}`;
28
+ }
29
+ export function parseMacOSProxySettings(output) {
30
+ return {
31
+ HTTP_PROXY: macOSProxyUrl(output, 'HTTP'),
32
+ HTTPS_PROXY: macOSProxyUrl(output, 'HTTPS'),
33
+ };
34
+ }
35
+ export function configureNetworkFromEnvironment(environment = process.env, setDispatcher = setGlobalDispatcher) {
36
+ const allProxy = httpCompatibleProxy(firstValue(environment.all_proxy, environment.ALL_PROXY));
37
+ const httpProxy = firstValue(environment.http_proxy, environment.HTTP_PROXY, allProxy);
38
+ const httpsProxy = firstValue(environment.https_proxy, environment.HTTPS_PROXY, allProxy);
39
+ if (!httpProxy && !httpsProxy)
40
+ return false;
41
+ proxyAgent = new EnvHttpProxyAgent({
42
+ httpProxy,
43
+ httpsProxy,
44
+ noProxy: firstValue(environment.no_proxy, environment.NO_PROXY),
45
+ });
46
+ setDispatcher(proxyAgent);
47
+ return true;
48
+ }
49
+ export function configureNetwork() {
50
+ if (configureNetworkFromEnvironment())
51
+ return true;
52
+ if (process.platform !== 'darwin')
53
+ return false;
54
+ try {
55
+ const output = execFileSync('/usr/sbin/scutil', ['--proxy'], {
56
+ encoding: 'utf8',
57
+ timeout: 1_000,
58
+ });
59
+ return configureNetworkFromEnvironment(parseMacOSProxySettings(output));
60
+ }
61
+ catch {
62
+ return false;
63
+ }
64
+ }
65
+ //# sourceMappingURL=network.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"network.js","sourceRoot":"","sources":["../src/network.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function createProgram(version: string): Command;
@@ -0,0 +1,60 @@
1
+ import chalk from 'chalk';
2
+ import { Command, InvalidArgumentError, Option } from 'commander';
3
+ import { renderBanner } from './banner.js';
4
+ import { githubDefaults, runGitHubCommand } from './channels/github/action.js';
5
+ import { runSourcesCommand } from './commands/sources.js';
6
+ function positiveInteger(value) {
7
+ const parsed = Number.parseInt(value, 10);
8
+ if (!Number.isInteger(parsed) || parsed < 1) {
9
+ throw new InvalidArgumentError('必须是大于 0 的整数');
10
+ }
11
+ return parsed;
12
+ }
13
+ const examples = `
14
+ 示例:
15
+ ${chalk.magenta('youKnow github')}
16
+ ${chalk.magenta('youKnow github --since daily --language python --limit 5')}
17
+ ${chalk.magenta('youKnow github --format md --output output/github.md')}
18
+ `;
19
+ function localizeHelp(message) {
20
+ return message
21
+ .replace(/^Usage:/gm, chalk.magenta.bold('用法:'))
22
+ .replace(/^Options:/gm, chalk.magenta.bold('选项:'))
23
+ .replace(/^Commands:/gm, chalk.magenta.bold('命令:'));
24
+ }
25
+ export function createProgram(version) {
26
+ const program = new Command()
27
+ .name('youKnow')
28
+ .description('从多个渠道发现值得关注的热点信号')
29
+ .version(version, '-v, --version', '显示版本')
30
+ .helpOption('-h, --help', '显示帮助')
31
+ .helpCommand('help [command]', '显示指定命令的帮助')
32
+ .showHelpAfterError('(运行 youKnow --help 查看完整用法)')
33
+ .configureOutput({
34
+ writeOut: (message) => process.stdout.write(localizeHelp(message)),
35
+ writeErr: (message) => process.stderr.write(chalk.red(message)),
36
+ });
37
+ program.addHelpText('before', `${renderBanner()}\n`);
38
+ program
39
+ .command('github')
40
+ .description('获取 GitHub Trending 热门仓库')
41
+ .option('-l, --language <language>', '开发语言', githubDefaults.language)
42
+ .addOption(new Option('-s, --since <since>', '时间范围')
43
+ .choices(['daily', 'weekly', 'monthly'])
44
+ .default(githubDefaults.period))
45
+ .option('--spoken-language <code>', '项目自然语言代码,如 zh、en')
46
+ .option('-n, --limit <number>', '返回条数', positiveInteger, 20)
47
+ .addOption(new Option('-f, --format <format>', '输出格式')
48
+ .choices(['json', 'md'])
49
+ .default('json'))
50
+ .option('-o, --output <path>', '同时写入文件')
51
+ .option('--timeout <ms>', '请求超时毫秒数', positiveInteger, 20_000)
52
+ .action(runGitHubCommand);
53
+ program
54
+ .command('sources')
55
+ .description('查看当前与规划中的数据源')
56
+ .action(runSourcesCommand);
57
+ program.addHelpText('after', examples);
58
+ return program;
59
+ }
60
+ //# sourceMappingURL=program.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"program.js","sourceRoot":"","sources":["../src/program.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "trending-knowledge",
3
+ "version": "0.1.0",
4
+ "description": "在终端获取 GitHub Trending 热门仓库,支持 JSON 和 Markdown 输出",
5
+ "type": "module",
6
+ "bin": {
7
+ "trending-knowledge": "dist/cli.js",
8
+ "youKnow": "dist/cli.js",
9
+ "youknow": "dist/cli.js"
10
+ },
11
+ "files": [
12
+ "dist",
13
+ "README.md"
14
+ ],
15
+ "scripts": {
16
+ "clean": "node scripts/clean.mjs",
17
+ "build": "npm run clean && tsc -p tsconfig.json",
18
+ "dev": "tsx src/cli.ts",
19
+ "link": "node scripts/link-src.mjs",
20
+ "unlink": "node scripts/link-src.mjs --unlink",
21
+ "start": "node dist/cli.js",
22
+ "test": "node --import tsx --test tests/*.test.ts",
23
+ "format": "prettier --write .",
24
+ "format:check": "prettier --check .",
25
+ "check": "npm run format:check && tsc -p tsconfig.json --noEmit && tsc -p tsconfig.test.json && node --check src/youKnow.mjs && node --check scripts/link-src.mjs",
26
+ "prepublishOnly": "npm test && npm run check",
27
+ "prepare": "npm run build"
28
+ },
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "git+https://github.com/wanglsh97/trending-knowledge.git"
32
+ },
33
+ "homepage": "https://github.com/wanglsh97/trending-knowledge#readme",
34
+ "bugs": {
35
+ "url": "https://github.com/wanglsh97/trending-knowledge/issues"
36
+ },
37
+ "keywords": [
38
+ "github",
39
+ "trending",
40
+ "cli",
41
+ "typescript",
42
+ "markdown"
43
+ ],
44
+ "publishConfig": {
45
+ "access": "public"
46
+ },
47
+ "engines": {
48
+ "node": ">=20.18.1"
49
+ },
50
+ "dependencies": {
51
+ "chalk": "^5.6.2",
52
+ "cheerio": "1.1.2",
53
+ "commander": "^14.0.3",
54
+ "undici": "^7.29.0"
55
+ },
56
+ "devDependencies": {
57
+ "@types/node": "24.3.0",
58
+ "prettier": "3.6.2",
59
+ "tsx": "4.20.5",
60
+ "typescript": "5.9.2"
61
+ }
62
+ }