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 +206 -0
- package/dist/banner.d.ts +1 -0
- package/dist/banner.js +14 -0
- package/dist/banner.js.map +1 -0
- package/dist/channels/github/action.d.ts +18 -0
- package/dist/channels/github/action.js +79 -0
- package/dist/channels/github/action.js.map +1 -0
- package/dist/channels/github/fetch.d.ts +42 -0
- package/dist/channels/github/fetch.js +109 -0
- package/dist/channels/github/fetch.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +37 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/sources.d.ts +1 -0
- package/dist/commands/sources.js +40 -0
- package/dist/commands/sources.js.map +1 -0
- package/dist/errors.d.ts +7 -0
- package/dist/errors.js +45 -0
- package/dist/errors.js.map +1 -0
- package/dist/network.d.ts +7 -0
- package/dist/network.js +65 -0
- package/dist/network.js.map +1 -0
- package/dist/program.d.ts +2 -0
- package/dist/program.js +60 -0
- package/dist/program.js.map +1 -0
- package/package.json +62 -0
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
|
+
```
|
package/dist/banner.d.ts
ADDED
|
@@ -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
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
|
package/dist/cli.js.map
ADDED
|
@@ -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"}
|
package/dist/errors.d.ts
ADDED
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 {};
|
package/dist/network.js
ADDED
|
@@ -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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"}
|
package/dist/program.js
ADDED
|
@@ -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
|
+
}
|