中文 · English ↓
你的私人 AI 每日简报,跑在你自己掌控的基础设施上。 23 个数据源 · LLM 摘要 · 21 个股票/加密标的技术指标 + AI 交易点评 · 中英双语 · 5 个 LLM 后端可选。
三种部署任选:🚀 5 分钟 Fork 到 GitHub Actions · 💻 本地一键装 · 🤖 一句话让 AI Agent 帮你装。
🌐 Live demos — 📰 leiting-eric.github.io/DailyBrief(A 方式 · GitHub Actions + Pages) · 📰 daily.leiting.tech(B 方式 · 本地服务器部署)
- 🌍 全网多源聚合:23 个数据源覆盖硅谷科技、AI 前沿、全球财经、国际时政、中文社区,一份报告通吃
- 📈 21 个标的实时行情:美股 / 加密 / 港股 / 商品外汇 / 宏观信号,附 SMA / RSI / MACD 技术指标 + LLM 每日交易点评
- 🤖 5 个 LLM 后端可插拔:Claude CLI / Anthropic / OpenAI / DeepSeek / MiniMax,一个环境变量切换,不绑死任何家
- 🌐 中英双语:
REPORT_LOCALE=en一切——数据源、prompt、UI 文案、Bullish/Bearish stance 全套切英文 - 🚀 部署灵活:GitHub Actions(零基础设施)/ 本地系统调度器 / 自托管服务器三选一,互不冲突可并存
- 🆓 数据源零 API key:所有源走免费公开端点(RSS / 公开 JSON),不需要付费订阅
- 🔒 不绑定第三方阅读服务:Feedly / Inoreader / Pocket 那些都不沾,你看了什么是你自己的事
- 📁 单文件 HTML:CSS + JS 全内联,无外部依赖,scp 上服务器直接当首页
23 个数据源(zh 模式)/ 22 个(en 模式),分布如下:
- GitHub Trending · 热榜每日刷新
- AI 媒体(merged):OpenAI / DeepMind / Hugging Face Blog / TLDR AI / Smol AI / Latent Space / MIT Tech Review
- X 推文(attentionvc-ai):精选 AI 圈大佬动态
- 美股 / ETF:SPY / QQQ / AAPL / MSFT / NVDA / GOOGL / TSLA / META
- 加密:BTC / ETH / SOL(附 alt.me 加密恐慌贪婪指数 + CoinGecko 总览)
- 中港:BABA / PDD / JD / 0700.HK
- 商品外汇:GC=F(黄金)/ CL=F(WTI 原油)/ USDCNY=X
- 宏观信号:^VIX / ^TNX(10Y 美债)/ DXY(美元指数)
BBC / Guardian / NYT / NPR / DW 中文 / Al Jazeera / The Diplomat — 7 家主流国际媒体的 World 频道
Bloomberg / WSJ / FT / BBC Business / Economist — 5 家全球财经主力
- zh 模式:V2EX 热榜 / LinuxDo 热帖
- en 模式:Hacker News / Reddit r/stocks(自动替换上面两个)
完整列表 + 启用状态:
npm run sources查看;改源 → 编辑sources.config.json。
| 方式 | 适合谁 | 你需要 | 几分钟搞定 |
|---|---|---|---|
| A. GitHub Actions + Pages | 没服务器、不想常开电脑 | 一个 API key(Anthropic / OpenAI / DeepSeek / MiniMax 任一) | ~5 分钟(推荐) |
| B. 本地一键装 | 有常开的电脑/服务器、想极致便宜 | Node 20+,可选 Claude Code 登录 | ~3 分钟 |
| C. 给 AI Agent 一句话 | 懒、想让 Cursor / Codex / Claude Code 帮你装 | 同上 | 一句话 |
-
Fork 这个 repo(GitHub 右上角 Fork 按钮)
-
进 Fork 的 repo → Settings → Actions → General → Workflow permissions 设为 Read and write permissions
-
Settings → Pages → Build and deployment → Source 选 "Deploy from a branch",分支
gh-pages/ 路径/ (root)(第一次跑完才会出现 gh-pages 分支,先建 secret 再触发一次即可) -
🔑 配置 LLM 后端 —— 这步是关键。每个后端都要一个 secret + 对应的
LLM_BACKENDvariable(不只是 secret),按下表对照填:你想用 Secrets 标签加 Variables 标签加 LLM_BACKEND大致成本 🟣 Anthropic Sonnet(默认,prompt 按 Sonnet 调优) ANTHROPIC_API_KEY不填或填 anthropic~$0.03-0.05 / 天,月 < $2 🐋 DeepSeek(便宜大碗,中文友好) DEEPSEEK_API_KEYdeepseek~$0.01-0.02 / 天,月 < $1 🟢 OpenAI OPENAI_API_KEYopenaigpt-4o-mini ~$0.02 / 天 🔵 MiniMax MINIMAX_API_KEYminimax类似 DeepSeek 量级 🌀 中转站 / 反代 / 其他 OpenAI 兼容服务(Moonshot / SiliconFlow / OpenRouter / 自建 Claude 反代 / 本地 Ollama / LM Studio 等) LLM_API_KEYopenai(极少数 Anthropic 协议反代填anthropic)看服务方定价 位置:Settings → Secrets and variables → Actions,左边切换 Secrets / Variables 两个标签。
🌀 选了最后一行"中转站"的额外步骤:Variables 还要加
LLM_BASE_URL(中转站给的 endpoint,如https://api.moonshot.cn/v1)和LLM_MODEL(如moonshot-v1-8k)。不确定协议?默认LLM_BACKEND=openai覆盖 95% 中转站;如果跑挂报 404 / 协议错误再改成anthropic。 -
(可选)同页 Variables 再加:
LLM_MODEL—— 覆盖该 backend 的默认模型(不填用.env.example里列的默认)LLM_BASE_URL—— 自定义 endpoint。选了上面"中转站"那行的话必填;本地 Ollama 填http://localhost:11434/v1、LM Studio 填http://localhost:1234/v1REPORT_LOCALE——zh(默认)或en,控制数据源 + UI + prompt 全套切英文REPORT_TZ—— IANA 时区名(默认 UTC),例Asia/Shanghai/America/Los_Angeles。同时影响触发时间和日期标签REPORT_HOUR—— 触发的小时(基于REPORT_TZ),默认8(早 8 点)。逗号分隔可多次触发,如8,18= 早 8 + 晚 6REPORT_DAYS—— 触发的星期(cron 风格,0=周日 ...6=周六),默认*(每天)。例1-5= 工作日;1,3,5= 周一三五
-
Actions 标签 → 选 "Daily Brief" workflow → Run workflow 手动触发一次
跑完后报告在 https://<你的用户名>.github.io/<repo-名字>/。之后默认每天 REPORT_TZ 时区的 08:00 自动更新(不设 REPORT_TZ 就是 UTC 08:00)。
⏰ 触发机制:GitHub Actions 的 cron 只接受 UTC,所以工作流 cron 设置为每小时跑一次,里面有一个
gate任务用REPORT_TZ把当前小时和REPORT_HOUR/REPORT_DAYS对照——匹配才往下跑 build,否则秒退。这样不论你在哪个时区都能精准命中本地时间,夏令时也自动跟着切换(IANA 时区数据库内置)。
常用 schedule 配方:
| 想要 | REPORT_HOUR |
REPORT_DAYS |
|---|---|---|
| 每天 08:00(默认) | 不填或 8 |
不填或 * |
| 每天早晚两次(8 + 18 点) | 8,18 |
* |
| 工作日 09:00 | 9 |
1-5 |
| 周一/三/五 早 7 晚 9 两次 | 7,21 |
1,3,5 |
| 每 6 小时一次 | 0,6,12,18 |
* |
只想要默认每天 08:00 本地时间,只填 REPORT_TZ 一个变量就够了(如 Asia/Shanghai),其他全部留空。
💸 成本估算:GitHub Actions 公开 repo 完全免费。Pages 公开 repo 也免费。唯一花钱的就是 LLM API 调用——DeepSeek 月成本不到 $1,Anthropic Sonnet < $2。
⚠️ 用 GH Actions 模式就意味着用不了本地claudeCLI——Claude Code 的 OAuth 登录在你本机,GitHub 的服务器看不到。如果你已经在 Max 订阅里,建议两条路并行:本地装(B 方式)用 Claude CLI 跑你自己的服务器版本,GH Actions 用 DeepSeek 跑 Pages 公开版本。两份报告独立,互不影响。
- "Upgrade or make this repository public to enable Pages" —— GitHub Pages 在 Free 账户的 Private repo 上不可用。Settings → General → Danger Zone → Change visibility 改 Public。Actions Secrets 在 Public repo 里依然加密保护,对其他人不可见
- Variable name 报 "alphanumeric only" —— 输入
LLM_BACKEND时下划线被中文输入法替换成了全角_(U+FF3F)。切到英文输入法 Shift+-重打 - 第一次跑完才能选 Pages source —— Pages 设置页要求选已存在的分支,但
gh-pages是首次 workflow 跑成功后才创建出来。顺序:配 secret → 触发 workflow → 跑完 → 回 Settings → Pages 选gh-pages - Action 红 X 怎么看具体原因 —— 点失败的 build → 左边列出每个 step → 找有红 X 的那步点开看 log。最常见两类:
401/402= API key 拼错或没余额;403= workflow permissions 没设成 Read and write - 跑了 30 秒就挂 —— 多半是 secret/variable 没配对(光填了 secret 没填
LLM_BACKENDvariable)或者 LLM API 返 400。看 step "Generate today's report" 的 log
# Linux / macOS
curl -sSL https://raw.githubusercontent.com/leiting-eric/DailyBrief/main/bootstrap.mjs | node
# Windows PowerShell
irm https://raw.githubusercontent.com/leiting-eric/DailyBrief/main/bootstrap.mjs | node -这条命令会自动:
- 检查 Node / git / claude CLI 是否就位(没装 claude CLI 只发警告,可继续走 API 后端)
git clone到~/daily-brief(Windows:%USERPROFILE%\daily-brief)npm install- 注册系统定时(Windows Task Scheduler / macOS launchd / Linux cron,默认 16:00 本地时间)
- 写
~/.daily-brief-config记录项目路径 - 在
~/.claude/建符号链接让 Claude Code 的 skill 和 slash command 全局可用 - 跑一次
npm run dry-run烟测
🎁 Claude Code 用户额外福利:装完后任意目录都能 /run-daily、/check-daily,描述问题("日报又挂了")也能触发 daily-brief skill 自动加载。其他 agent(Cursor / Codex)没有 skill 加载机制,但定时任务跑得起来。手动触发用:
| 平台 | 手动触发 |
|---|---|
| Windows | Start-ScheduledTask -TaskName DailyBrief |
| macOS | launchctl start com.daily-brief |
| Linux | node scripts/run-daily.mjs(cron 不支持手动触发) |
自定义路径 / 触发时间:
node bootstrap.mjs --target /custom/path --at 07:30LLM 后端:默认走本地 claude CLI(首次需在浏览器登录一次:echo "hi" | claude --print --model sonnet,登录后永久生效)。不用 Claude Code 就跳过它,复制 .env.example 到 .env.local 把 LLM_BACKEND 切到 OpenAI / Anthropic / DeepSeek / MiniMax —— 见 LLM 后端配置。
无论你用哪个 AI Agent(Claude Code / Cursor / Codex / Continue.dev / OpenClaw 等),把下面这段发给它:
帮我装这个开源项目,按 README 的"本地一键装"流程跑 bootstrap,完成后告诉我下次自动触发的时间: https://github.com/leiting-eric/DailyBrief
项目里有 AGENTS.md(通用 agent 协议)+ .claude/skills/daily-brief/SKILL.md(Claude Code 专属,更详细),Agent 装完后能直接帮你诊断"今天报告没出来"、"加个新数据源"这类问题。
- Node.js 20+ + npm + git(B/C 方式本地需要;A 方式不需要——GH Actions 容器自带)
- 一个能跑的 LLM(任选其一):Claude Code CLI 已登录 / Anthropic / OpenAI / DeepSeek / MiniMax 任一家的 API key
- 平台:Windows 10/11、macOS 12+、Linux(任一平台都支持,定时机制自动适配)
# 1. clone + 依赖
git clone https://github.com/leiting-eric/DailyBrief.git
cd DailyBrief
npm install
# 2. 配置 LLM 后端
# 默认 claude CLI(如果没登录会引导你登录):
echo "say hi in Chinese" | claude --print --model sonnet
# 或用其他 backend:cp .env.example .env.local 编辑 LLM_BACKEND 和对应 API key
# 3. 注册定时 + 启用全局 skill
node scripts/install.mjs --global
# 也可指定时间:node scripts/install.mjs --at 07:30 --global
# 4. 立即触发一次测试
# Windows: Start-ScheduledTask -TaskName DailyBrief
# macOS: launchctl start com.daily-brief
# Linux: node scripts/run-daily.mjs下次触发时机:
- 🪟 Windows — 系统会自动唤醒电脑(如在睡眠),跑完再回睡
- 🍎 macOS — launchd 不会主动唤醒,电脑睡着的话跳过这次(需要
pmset wake schedule配合) - 🐧 Linux — cron 同理,挂起期间不跑
| 命令 | 用途 | 耗时 |
|---|---|---|
npm run daily |
手动完整跑一次 | 5-8 min |
npm run dry-run |
只抓取不调 LLM,验证数据源 | ~30s |
npm run render [date] |
改了 CSS/排版后重渲染 | <1s |
npm run regen-trading [date] |
重做交易部分 | ~2 min |
npm run regen-enrich <cat:sub> [date] |
补缺失的中文摘要 | ~30s |
npm run open |
在 Chrome 打开今日报告 | 即时 |
npm run quota-report |
看各 LLM backend 用量统计 | 即时 |
npm run sources |
列出所有数据源(按 locale 标注启用/过滤状态) | 即时 |
npm run sources:check |
仅校验 sources.config.json schema(适合 CI / pre-commit) |
即时 |
数据源以 JSON 数组形式集中存储在项目根的 sources.config.json —— 唯一配置入口,不需要改 TypeScript 代码即可加/禁/调整源。每条记录的字段:
| 字段 | 必填 | 说明 |
|---|---|---|
id |
✓ | 全局唯一短标识(dispatch.ts 用 id 路由到对应 fetcher) |
name |
✓ | UI 显示名 |
type |
✓ | rss / api / scrape |
url |
✓ | RSS feed 或 API endpoint |
category |
✓ | tech / finance / politics,决定 L1 tab |
subcategory |
tech 下的 L2 分组(github-trending / ai-news / x-viral / cn-community);finance 下统一 news |
|
enabled |
默认 true,设 false 跳过抓取(保留记录便于回滚) |
|
useCurl |
若该源被 Cloudflare TLS-fingerprint 拦了 Node undici,设 true 让 rss.ts 走 curl 子进程 |
|
lang |
zh 表示源内容是中文 — enrich 阶段会跳过它(无需把中文翻译成中文) |
|
locales |
数组,标明该源出现在哪些 report locale 下。默认 ["zh", "en"] |
|
notes |
任意备注(如"被废弃因为 feed 死了"),运行时忽略 |
- 编辑
sources.config.json,追加一条记录 - 跑
npm run sources:check校验 schema npm run dry-run抓一次验证拉取正常- 下次
npm run daily自动包含
通过 REPORT_LOCALE 环境变量切换:
# .env.local
REPORT_LOCALE=zh # 默认 — 中文 mode,含 V2EX / LinuxDo / DW 中文等中文源
# REPORT_LOCALE=en # 英文 mode — 自动过滤掉 zh-only 源,挂上 en-only 源每个源在 JSON 里的 locales 字段决定它在哪些模式下出现:
["zh"]— 仅中文 mode(V2EX / LinuxDo / DW 中文)。英文用户读不懂,英文 mode 自动 drop["en"]— 仅英文 mode(Hacker News / r/stocks 英文社区源,替代中文社区源)。zh mode 不显示["zh", "en"](默认)— 两 mode 都参与(BBC / Bloomberg / WSJ / NYT 全球英文源 + AI 资讯源)
当前启用源(enabled: true)按 locale 分布:
| Locale | 启用源数 | 主要构成 |
|---|---|---|
zh |
23 | 20 个全球英文源(附中文摘要)+ 3 个中文专属(V2EX / LinuxDo / DW 中文) |
en |
22 | 20 个全球英文源 + 2 个英文社区(Hacker News + r/stocks) |
英文 mode 完整切换:HTML UI 文案、enrichment / digest / trading-commentary 三套 prompt、stance 词("偏上行/偏下行/中性" → Bullish/Bearish/Neutral)、日期格式(zh-CN → en-GB)、Markdown 输出 —— 全部跟着 REPORT_LOCALE 切。中文社区源在英文 mode 下被自动过滤掉。
项目通过 LLM_BACKEND 环境变量切换后端。默认 claude-cli —— 直接复用 Claude Code 已登录的认证,不需要额外配 API key。不用 Claude Code、或想走自己的 API key,按下表切换。
把 .env.example 复制成 .env.local(gitignored),按 backend 解开对应几行:
| backend | API key 环境变量 | 默认 model | base URL |
|---|---|---|---|
🎯 claude-cli (默认) |
不需要,复用 Claude Code OAuth | sonnet |
— |
🟣 anthropic |
ANTHROPIC_API_KEY |
claude-sonnet-4-6 |
api.anthropic.com |
🟢 openai |
OPENAI_API_KEY |
gpt-4o-mini |
api.openai.com/v1 |
🐋 deepseek |
DEEPSEEK_API_KEY |
deepseek-v4-flash |
api.deepseek.com/v1 |
🔵 minimax |
MINIMAX_API_KEY |
MiniMax-M2.7 |
api.minimax.io/v1 1 |
1 中国大陆访问设 MINIMAX_BASE_URL=https://api.minimaxi.com/v1。
通用覆盖项:
LLM_MODEL=<id>—— 任意 backend 的 model 都能用这个变量覆盖默认(如LLM_MODEL=gpt-4o走 openai 的更大模型)<BACKEND>_BASE_URL—— 走自托管代理 / 兼容服务(如 LM Studio / Ollama 跑 OpenAI 兼容接口 →LLM_BACKEND=openai+OPENAI_BASE_URL=http://localhost:1234/v1)LLM_API_KEY/LLM_BASE_URL—— 通用别名,专用变量没设时 fallback 到这里。用 Moonshot / SiliconFlow / OpenRouter / 自建反代等非预设服务时推荐用这对,避免OPENAI_API_KEY这样的语义错位。例:跑 Moonshot 只要LLM_BACKEND=openai+LLM_API_KEY=sk-...+LLM_BASE_URL=https://api.moonshot.cn/v1+LLM_MODEL=moonshot-v1-8k
| 你的情况 | 推荐 backend |
|---|---|
| 已经在用 Claude Code(任意订阅等级) | claude-cli — 零配置,按你订阅的等级走 |
| 不用 Claude Code,只想低成本跑日报 | openai 配 gpt-4o-mini、或 deepseek 配 deepseek-v4-flash(更便宜) |
| 中文摘要质量优先,预算可放宽 | anthropic 配 claude-sonnet-4-6 |
| 国内网络访问,要规避 GFW | deepseek 或 minimax(都是国内厂商) |
切 backend 不需要改代码:所有 prompt 都在 lib/ai/prompts.ts 抽离,跟 backend 无关;JSON 错误兜底(jsonrepair)也是 backend-agnostic。切完后跑一次 npm run daily,进 logs/llm-calls.jsonl 看新 backend 的调用记录。
每次 npm run daily 跑完,自动把新 HTML 推到自己的服务器,访客打开 https://your-domain/ 就能看到当天最新报告。默认不启用,环境变量不设就跳过。
假设服务器跑 Ubuntu + nginx + 已申请域名,登录用户有 sudo NOPASSWD:
# 服务器上
sudo mkdir -p /var/www/your-domain && sudo chown -R www-data:www-data /var/www/your-domain/etc/nginx/sites-available/your-domain.conf:
server {
listen 80;
server_name your-domain;
root /var/www/your-domain;
index index.html;
location / { try_files $uri $uri/ =404; }
}启用 + 自动签 SSL:
sudo ln -s /etc/nginx/sites-available/your-domain.conf /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
sudo certbot --nginx -d your-domain --agree-tos --redirect.env.local(gitignored)加两行:
DEPLOY_HOST=user@your-server-ip
DEPLOY_PATH=/var/www/your-domain
之后:
- ✅ 每次
npm run daily跑完,自动 scp 当天 HTML 到服务器 + 刷新index.html - ✅
npm run deploy [YYYY-MM-DD]手动推任意一天 - ✅ 部署失败不阻断 daily 本身(HTML 已经在本地
daily_reports/落盘,可重跑npm run deploy补推)
装好后任意目录(不必 cd 进项目)打开 Claude Code 都可用:
| 触发 | 行为 |
|---|---|
/run-daily |
立即触发 daily 并后台监听到完成。从任意目录都行 |
/check-daily |
查任务状态 + 报告文件 + 配额 |
| 描述问题("日报又挂了"、"X 推文为啥没更新"等) | daily-brief skill 的关键词触发自动加载,让 Claude 直接懂这个项目 |
实现机制:scripts/install.mjs --global 在 ~/.claude/ 下建符号链接,指向项目内的 .claude/skills/daily-brief/SKILL.md 和 .claude/commands/ 文件——单一源,编辑项目文件等于编辑用户级 skill。当 symlink 因权限受限失败时(如 Windows 无开发者模式),自动 fallback 到 copy。~/.daily-brief-config 记录项目实际路径,让 slash command 在任意 cwd 都能找到项目。
daily-brief/
├── lib/
│ ├── sources/ # RSS / API / curl 抓取器;新加源在这里
│ ├── ai/ # 可插拔 LLM 后端 + 提示词(lib/ai/backends/ 下每个 backend)
│ ├── trading/ # Yahoo Finance + 技术指标
│ ├── output/ # 渲染层 (HTML / Markdown)
│ └── utils.ts # 小工具(todayKey / getReportTz)
├── scripts/
│ ├── _env.ts # dotenv 预加载(被所有入口脚本第一个 import)
│ ├── daily.ts # 主管线
│ ├── dry-run.ts # 只抓取不调 LLM,验证数据源
│ ├── render.ts # 重渲染
│ ├── regen-*.ts # 局部重跑(trading / enrich)
│ ├── quota-report.ts # LLM 用量统计
│ ├── sources.ts # `npm run sources` — 列源 + 校验 JSON
│ ├── run-daily.mjs # OS 调度器调用的包装(含自动 deploy + open)
│ ├── open-report.mjs # 打开最新报告(跨平台)
│ ├── build-site.mjs # 生成 GH Pages 静态站 (index + archive)
│ ├── deploy.mjs # scp 到远端 nginx 服务器(可选)
│ ├── install.mjs # 注册定时任务(Win/Mac/Linux 自适应)
│ └── uninstall.mjs # 卸载
├── sources.config.json # 数据源唯一配置入口
├── daily_reports/ # 输出 (gitignored)
│ └── 2026-05-15/ # 每日一个子目录,内含 .html (主) / .json (缓存) / -articles.json (缓存)
│ # .md 默认不生成,可在 .env.local 设 OUTPUT_MARKDOWN=true 开启
├── logs/ # 运行日志 (gitignored)
├── .github/workflows/ # GitHub Actions 工作流(A 方式部署)
└── .claude/
├── skills/ # Claude Code 操作 skill
└── commands/ # slash commands
node scripts/uninstall.mjs
# 移除:定时任务 (Task Scheduler / launchd / cron) + ~/.claude/ 下的链接 + ~/.daily-brief-config
# 不动:项目文件、daily_reports/、logs/、power plan 设置
# 想彻底清理就 rm -rf 整个项目目录改源、改时间、改排版、加新栏目 —— 见 FORKING.md。
本项目在 LINUX DO 开源社区分享推广,感谢佬友们的反馈与建议。
MIT
↑ 中文 · English
Your own AI-curated daily news brief, on infrastructure you control. 23 sources · LLM summaries · 21-ticker market panel with SMA/RSI/MACD signals + AI commentary · bilingual (zh/en) · 5 swappable LLM backends.
Three deployment paths, pick one: 🚀 5-min GitHub Actions fork · 💻 local one-liner install · 🤖 have an AI agent install it for you.
🌐 Live demos — 📰 leiting-eric.github.io/DailyBrief (path A · GitHub Actions + Pages) · 📰 daily.leiting.tech (path B · self-hosted server)
- 🌍 Multi-source aggregation — 23 sources spanning Silicon Valley tech, AI frontier, global finance, international politics, and developer communities. One report covers it all.
- 📈 21 live tickers — US stocks / crypto / HK / commodities / macro signals, with SMA / RSI / MACD indicators + daily LLM-written trading commentary
- 🤖 5 swappable LLM backends — Claude CLI / Anthropic / OpenAI / DeepSeek / MiniMax. One env var to switch, no vendor lock-in.
- 🌐 Bilingual (zh/en) — set
REPORT_LOCALE=ento flip the entire stack: sources, prompts, UI text, Bullish/Bearish stance labels — all switch. - 🚀 Flexible deployment — GitHub Actions (zero infra) / local OS scheduler / self-hosted server — pick one or run them in parallel
- 🆓 Zero data-source API keys — every source uses free public endpoints (RSS / public JSON), no paid subscriptions
- 🔒 No third-party reader lock-in — Feedly / Inoreader / Pocket all profile your reading; this project never talks to them. Your reading habits stay yours.
- 📁 Single-file HTML output — CSS and JS inlined, no external dependencies, scp'able onto a server as
index.html
23 sources in zh mode / 22 in en mode, organized as:
- GitHub Trending · refreshed daily
- AI media (merged): OpenAI / DeepMind / Hugging Face Blog / TLDR AI / Smol AI / Latent Space / MIT Tech Review
- X posts (attentionvc-ai): curated AI thought-leader feed
- US stocks / ETFs: SPY / QQQ / AAPL / MSFT / NVDA / GOOGL / TSLA / META
- Crypto: BTC / ETH / SOL (+ alt.me Fear & Greed index + CoinGecko macro)
- China / HK: BABA / PDD / JD / 0700.HK
- Commodities / FX: GC=F (gold) / CL=F (WTI crude) / USDCNY=X
- Macro signals: ^VIX / ^TNX (10Y Treasury) / DXY
BBC / Guardian / NYT / NPR / DW Chinese / Al Jazeera / The Diplomat — 7 major international outlets' World feeds
Bloomberg / WSJ / FT / BBC Business / Economist — 5 global finance heavyweights
- zh mode: V2EX top threads / LinuxDo trending
- en mode: Hacker News / Reddit r/stocks (auto-substituted)
Full list + enabled status: run
npm run sources. To edit sources, modifysources.config.json.
| Path | Who it's for | What you need | Setup time |
|---|---|---|---|
| A. GitHub Actions + Pages | No server, don't want to keep a laptop running | One API key (Anthropic / OpenAI / DeepSeek / MiniMax) | ~5 min (recommended) |
| B. Local one-liner | Have an always-on machine; want it cheapest | Node 20+, optionally Claude Code login | ~3 min |
| C. Have an AI agent install it | Lazy; want Cursor / Codex / Claude Code to handle setup | Same as B | One sentence |
-
Fork this repo (Fork button, top-right of GitHub)
-
Settings → Actions → General → Workflow permissions → set to Read and write permissions
-
Settings → Pages → Build and deployment → Source → "Deploy from a branch" → branch
gh-pages/ path/ (root)(thegh-pagesbranch only exists after the first successful workflow run — configure secrets first, trigger once, then come back) -
🔑 Configure the LLM backend — this is the critical step. Each backend needs a secret AND the matching
LLM_BACKENDvariable (not just the secret). Pick one row:You want Secret to add LLM_BACKENDvariableRough cost 🟣 Anthropic Sonnet (default; prompts tuned for it) ANTHROPIC_API_KEYleave unset or anthropic~$0.03-0.05/day, <$2/month 🐋 DeepSeek (cheap, China-friendly) DEEPSEEK_API_KEYdeepseek~$0.01-0.02/day, <$1/month 🟢 OpenAI OPENAI_API_KEYopenaigpt-4o-mini ~$0.02/day 🔵 MiniMax MINIMAX_API_KEYminimaxSimilar to DeepSeek 🌀 Proxy / aggregator / any OpenAI-compatible service (Moonshot, SiliconFlow, OpenRouter, self-hosted Claude relay, local Ollama / LM Studio, ...) LLM_API_KEYopenai(useanthropiconly if the proxy speaks Anthropic protocol — rare)Depends on provider Location: Settings → Secrets and variables → Actions. The page has two tabs — Secrets for keys, Variables for
LLM_BACKEND.🌀 Extras if you picked the last "proxy" row: also add
LLM_BASE_URL(the endpoint your proxy gave you, e.g.https://api.moonshot.cn/v1) andLLM_MODEL(e.g.moonshot-v1-8k) under Variables. Not sure which protocol? DefaultLLM_BACKEND=openai— covers 95% of proxies; switch toanthropiconly if you get 404s or protocol errors. -
(Optional) On the same Variables tab, add:
LLM_MODEL— override the backend's default model (otherwise uses the default listed in.env.example)LLM_BASE_URL— custom endpoint. Required if you picked the "proxy" row above. For local Ollama usehttp://localhost:11434/v1, LM Studiohttp://localhost:1234/v1REPORT_LOCALE—zh(default) oren— switches sources + UI + LLM prompts as a setREPORT_TZ— IANA timezone name (default UTC); e.g.Asia/Shanghai/America/Los_Angeles. Drives both the trigger time and the date label.REPORT_HOUR— hour(s) to fire inREPORT_TZ, default8(08:00). Comma-separated for multiple, e.g.8,18= 8 AM and 6 PMREPORT_DAYS— day-of-week filter (cron-style,0=Sunday ...6=Saturday), default*(every day). E.g.1-5= weekdays;1,3,5= Mon/Wed/Fri
-
Actions tab → "Daily Brief" workflow → Run workflow to trigger manually for the first time
Once the workflow turns green, your report lives at https://<your-username>.github.io/<repo-name>/. After that, it refreshes daily at 08:00 in REPORT_TZ (or 08:00 UTC if REPORT_TZ is unset).
⏰ How the schedule works: GitHub Actions cron is UTC-only, so the workflow runs hourly and uses a
gatejob to check if the current hour inREPORT_TZmatchesREPORT_HOUR/REPORT_DAYS. If so, the build job proceeds; otherwise it exits in seconds. This lets the schedule track any local timezone precisely, and handles DST transitions automatically (via the IANA tz database).
Common schedule recipes:
| You want | REPORT_HOUR |
REPORT_DAYS |
|---|---|---|
| Every day at 08:00 (default) | unset or 8 |
unset or * |
| Twice daily (8 AM + 6 PM) | 8,18 |
* |
| Weekdays at 09:00 | 9 |
1-5 |
| Mon/Wed/Fri at 7 AM + 9 PM | 7,21 |
1,3,5 |
| Every 6 hours | 0,6,12,18 |
* |
If you just want the default (08:00 local daily), set only REPORT_TZ (e.g. Asia/Shanghai) and leave the rest at defaults.
💸 Cost summary: GitHub Actions on public repos is free. Pages on public repos is free. The only thing you pay for is LLM API calls — DeepSeek runs under $1/month, Anthropic Sonnet under $2.
⚠️ GH Actions mode can't reuse a localclaudeCLI login — your Claude Code OAuth token lives on your machine, GitHub's runners can't see it. If you have a Max subscription, run both paths side by side: path B locally (uses Claude CLI), path A on GitHub Actions (uses DeepSeek). Independent reports, no interference.
- "Upgrade or make this repository public to enable Pages" — Free-tier GitHub Pages requires a public repo. Settings → General → Danger Zone → Change visibility → Public. Your Actions Secrets remain encrypted and invisible to others even on public repos.
- "Variable name can only contain alphanumeric characters" — most likely the underscore in
LLM_BACKENDgot autocorrected by a CJK input method to a full-width_(U+FF3F). Switch to English input, retype Shift+-, or copy-paste. - Pages source dropdown doesn't show
gh-pages— that branch only exists after the first successful workflow run. Order: configure secret → trigger workflow → wait for green → go back to Settings → Pages. - Where to read a failed run — Actions tab → click the red X → left sidebar lists each step → click the failing one to expand its log. Most common causes:
401/402(API key wrong or out of credit),403(workflow permissions still set to "Read only"). - Fails after ~30 seconds — usually a secret/variable mismatch (added a secret but didn't add the matching
LLM_BACKENDvariable) or the LLM API returned 400. Check the "Generate today's report" step.
# Linux / macOS
curl -sSL https://raw.githubusercontent.com/leiting-eric/DailyBrief/main/bootstrap.mjs | node
# Windows PowerShell
irm https://raw.githubusercontent.com/leiting-eric/DailyBrief/main/bootstrap.mjs | node -This script will:
- Check that Node / git / claude CLI are on PATH (claude CLI missing is a warning, not an error — you can use an API backend instead)
git cloneto~/daily-brief(Windows:%USERPROFILE%\daily-brief)npm install- Register the OS scheduler (Windows Task Scheduler / macOS launchd / Linux cron, default 16:00 local time)
- Write
~/.daily-brief-configrecording the project path - Symlink the Claude Code skill + slash commands into
~/.claude/so they work from any directory - Run
npm run dry-runas a smoke test
🎁 Claude Code bonus: after install, any Claude Code session anywhere can use /run-daily and /check-daily. Describing a problem in plain English ("today's report didn't come out") also auto-loads the daily-brief skill. Other agents (Cursor / Codex) don't have a skill auto-load mechanism, but the scheduled task still runs at the OS level. Manual triggers:
| Platform | Command |
|---|---|
| Windows | Start-ScheduledTask -TaskName DailyBrief |
| macOS | launchctl start com.daily-brief |
| Linux | node scripts/run-daily.mjs (cron doesn't support manual trigger) |
Custom install path / time:
node bootstrap.mjs --target /custom/path --at 07:30LLM backend: defaults to the local claude CLI (first time you'll need to log in once in a browser: echo "hi" | claude --print --model sonnet — once is forever). If you don't use Claude Code, skip it: copy .env.example to .env.local and set LLM_BACKEND to OpenAI / Anthropic / DeepSeek / MiniMax — see LLM backend configuration.
Whichever AI agent you use (Claude Code / Cursor / Codex / Continue.dev / OpenClaw / etc.), send it this prompt:
Please install this open-source project following the README's "local one-liner" path with bootstrap, and tell me when the next auto-trigger will fire: https://github.com/leiting-eric/DailyBrief
The repo includes AGENTS.md (universal agent protocol) and .claude/skills/daily-brief/SKILL.md (Claude Code-specific, more detailed). After install, the agent can help diagnose things like "today's report didn't come out" or "add a new source".
- Node.js 20+, npm, git (local for paths B/C; path A runs in GitHub's containers — no local install needed)
- One working LLM (any of): Claude Code CLI logged in, OR Anthropic / OpenAI / DeepSeek / MiniMax API key
- Platform: Windows 10/11, macOS 12+, Linux (any platform — scheduler picks the matching mechanism)
# 1. Clone + dependencies
git clone https://github.com/leiting-eric/DailyBrief.git
cd DailyBrief
npm install
# 2. Pick an LLM backend
# Default = claude CLI (will guide you through login if not done):
echo "say hi" | claude --print --model sonnet
# Or use a different backend: cp .env.example .env.local
# and set LLM_BACKEND + the matching API key
# 3. Register the scheduler + enable the global skill
node scripts/install.mjs --global
# 4. Test trigger immediately
# Windows: Start-ScheduledTask -TaskName DailyBrief
# macOS: launchctl start com.daily-brief
# Linux: node scripts/run-daily.mjsSleep-wake behavior at next trigger time:
- 🪟 Windows — wakes the computer if asleep, runs, returns to sleep
- 🍎 macOS — launchd doesn't wake from deep sleep; skipped if asleep (configure
pmset wake scheduleseparately if needed) - 🐧 Linux — cron doesn't wake either; skipped if suspended
| Command | Purpose | Time |
|---|---|---|
npm run daily |
Full pipeline (fetch + LLM + render) | 5-8 min |
npm run dry-run |
Fetch only, no LLM — validates sources | ~30s |
npm run render [date] |
Re-render HTML after editing CSS/layout | <1s |
npm run regen-trading [date] |
Re-do the trading section only | ~2 min |
npm run regen-enrich <cat:sub> [date] |
Fill in missing summaries for a subgroup | ~30s |
npm run open |
Open today's report in Chrome | instant |
npm run quota-report |
Per-backend LLM usage summary | instant |
npm run sources |
List all sources with locale / enabled status | instant |
npm run sources:check |
Validate sources.config.json schema (good for CI / pre-commit) |
instant |
Sources live as a JSON array in sources.config.json at the project root — the single source of truth. Add / disable / re-categorize feeds without touching TypeScript. Per-entry fields:
| Field | Required | Notes |
|---|---|---|
id |
✓ | Short unique identifier (used by dispatch.ts to route to the right fetcher) |
name |
✓ | Display name in the UI |
type |
✓ | rss / api / scrape |
url |
✓ | RSS feed URL or API endpoint |
category |
✓ | tech / finance / politics — drives the L1 tab |
subcategory |
L2 grouping (github-trending / ai-news / x-viral / cn-community for tech; news for finance) |
|
enabled |
Default true; set to false to skip without deleting the record |
|
useCurl |
true if the source's host blocks Node's TLS fingerprint (Cloudflare); the fetcher will shell out to curl |
|
lang |
zh means the source is already in Chinese — enrich skips it (no need to translate Chinese into Chinese) |
|
locales |
Array listing which REPORT_LOCALE the source appears in. Default ["zh", "en"] |
|
notes |
Free-form (e.g. "removed because feed died"); ignored at runtime |
- Append an entry to
sources.config.json - Run
npm run sources:checkto validate schema npm run dry-runto confirm the fetch works- The next
npm run dailypicks it up automatically
Switch with the REPORT_LOCALE environment variable:
# .env.local
REPORT_LOCALE=zh # default — Chinese mode, includes V2EX / LinuxDo / DW Chinese
# REPORT_LOCALE=en # English mode — drops zh-only sources, picks up en-only onesEach source's locales field decides which mode it appears in:
["zh"]— zh mode only (V2EX / LinuxDo / DW Chinese). English readers can't read these, so auto-dropped in en mode.["en"]— en mode only (Hacker News / r/stocks etc., picked up to replace Chinese community sources). Not shown in zh mode.["zh", "en"](default) — appears in both modes (BBC / Bloomberg / WSJ / NYT / AI media)
Currently enabled sources by locale:
| Locale | Enabled sources | Mix |
|---|---|---|
zh |
23 | 20 global / English sources (with LLM-generated Chinese summaries) + 3 Chinese-only (V2EX / LinuxDo / DW Chinese) |
en |
22 | 20 global / English sources + 2 English community (Hacker News + r/stocks) |
The full en-mode switch covers: HTML UI text, the three LLM prompt sets (enrichment / digest / trading commentary), stance labels (Bullish/Bearish/Neutral vs. 偏上行/偏下行/中性), date format (zh-CN ↔ en-GB), Markdown output. Chinese community sources are auto-hidden in en mode since the audience can't read them.
The project switches backends via the LLM_BACKEND environment variable. Default is claude-cli — it reuses your existing Claude Code login, no API key needed. To use your own API key with another provider, set up .env.local:
Copy .env.example to .env.local (gitignored), uncomment the section for your chosen backend:
| backend | API key env var | Default model | Base URL |
|---|---|---|---|
🎯 claude-cli (default) |
None — reuses Claude Code OAuth | sonnet |
— |
🟣 anthropic |
ANTHROPIC_API_KEY |
claude-sonnet-4-6 |
api.anthropic.com |
🟢 openai |
OPENAI_API_KEY |
gpt-4o-mini |
api.openai.com/v1 |
🐋 deepseek |
DEEPSEEK_API_KEY |
deepseek-v4-flash |
api.deepseek.com/v1 |
🔵 minimax |
MINIMAX_API_KEY |
MiniMax-M2.7 |
api.minimax.io/v1 1 |
1 Inside mainland China, set MINIMAX_BASE_URL=https://api.minimaxi.com/v1.
Universal overrides:
LLM_MODEL=<id>— works for any backend (e.g.LLM_MODEL=gpt-4oto use OpenAI's bigger model)<BACKEND>_BASE_URL— for self-hosted proxies or OpenAI-compatible services (e.g. LM Studio / Ollama →LLM_BACKEND=openai+OPENAI_BASE_URL=http://localhost:1234/v1)LLM_API_KEY/LLM_BASE_URL— generic aliases, used as fallback when the provider-specific vars aren't set. Use this pair for Moonshot / SiliconFlow / OpenRouter / self-hosted proxies — anything not in the preset list — so you don't have to misuseOPENAI_API_KEYjust to reach a non-OpenAI service. Example for Moonshot:LLM_BACKEND=openai+LLM_API_KEY=sk-...+LLM_BASE_URL=https://api.moonshot.cn/v1+LLM_MODEL=moonshot-v1-8k
| Your situation | Recommended backend |
|---|---|
| Already using Claude Code (any subscription tier) | claude-cli — zero config, billed against your subscription |
| Not on Claude Code, want it cheapest | openai with gpt-4o-mini, or deepseek (cheaper still) |
| Chinese summary quality matters most | anthropic with claude-sonnet-4-6 |
| Need to bypass China's network restrictions | deepseek or minimax (both are domestic providers) |
Switching backends needs no code changes: all prompts are factored out in lib/ai/prompts.ts independently of any backend; JSON repair fallback (jsonrepair) is backend-agnostic. After switching, run npm run daily once and look at logs/llm-calls.jsonl for the new backend's call log.
After each npm run daily, automatically scp the fresh HTML to your own server; visitors hit https://your-domain/ and see today's report. Disabled by default — leave the env vars unset to skip.
Assumes Ubuntu + nginx, domain configured, login user has sudo NOPASSWD:
# On the server
sudo mkdir -p /var/www/your-domain && sudo chown -R www-data:www-data /var/www/your-domain/etc/nginx/sites-available/your-domain.conf:
server {
listen 80;
server_name your-domain;
root /var/www/your-domain;
index index.html;
location / { try_files $uri $uri/ =404; }
}Enable + auto-issue SSL:
sudo ln -s /etc/nginx/sites-available/your-domain.conf /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
sudo certbot --nginx -d your-domain --agree-tos --redirectIn .env.local (gitignored), add:
DEPLOY_HOST=user@your-server-ip
DEPLOY_PATH=/var/www/your-domain
Then:
- ✅ Every
npm run dailyauto-scp's the new HTML to the server and refreshesindex.html - ✅
npm run deploy [YYYY-MM-DD]to push any specific date manually - ✅ A deploy failure doesn't break the daily run itself (HTML is already on disk in
daily_reports/;npm run deployretries it)
After install, any directory (no need to cd into the project) running Claude Code can use:
| Trigger | Behavior |
|---|---|
/run-daily |
Triggers daily immediately, monitors in the background until done. Works from any directory. |
/check-daily |
Checks task state + report files + quota |
| Describing a problem ("today's report didn't come out", "why didn't X posts update") | Auto-loads the daily-brief skill so Claude understands the project context |
How it works: scripts/install.mjs --global symlinks files in ~/.claude/ pointing at the project's .claude/skills/daily-brief/SKILL.md and .claude/commands/ — single source, editing the project files is editing the user-level skill. If symlinks aren't permitted (Windows without Developer Mode), it falls back to copying. ~/.daily-brief-config records the absolute project path so slash commands find it from any CWD.
daily-brief/
├── lib/
│ ├── sources/ # RSS / API / curl fetchers; add new sources here
│ ├── ai/ # Pluggable LLM backends + prompts (lib/ai/backends/ per backend)
│ ├── trading/ # Yahoo Finance + technical indicators
│ ├── output/ # Rendering (HTML / Markdown)
│ └── utils.ts # Tiny shared helpers (todayKey / getReportTz)
├── scripts/
│ ├── _env.ts # dotenv preload — imported FIRST by every entry script
│ ├── daily.ts # Main pipeline
│ ├── dry-run.ts # Fetch-only validation (no LLM)
│ ├── render.ts # Re-render from cached data
│ ├── regen-*.ts # Targeted re-runs (trading / enrich)
│ ├── quota-report.ts # LLM usage stats
│ ├── sources.ts # `npm run sources` — list + validate sources.config.json
│ ├── run-daily.mjs # OS scheduler wrapper (daily + log + deploy + open)
│ ├── open-report.mjs # Cross-platform "open latest" helper
│ ├── build-site.mjs # GH Pages static-site generator (index + archive)
│ ├── deploy.mjs # scp HTML to a remote nginx host (opt-in)
│ ├── install.mjs # Cross-platform scheduler registration
│ └── uninstall.mjs # Removal
├── sources.config.json # Single source of truth for the source registry
├── daily_reports/ # Output (gitignored)
│ └── 2026-05-15/ # One subdir per day, contains .html (main) / .json (cache) / -articles.json (cache)
│ # .md not generated by default; set OUTPUT_MARKDOWN=true in .env.local to enable
├── logs/ # Run logs (gitignored)
├── .github/workflows/ # GitHub Actions workflow (path A deployment)
└── .claude/
├── skills/ # Claude Code operational skill
└── commands/ # Slash commands
node scripts/uninstall.mjs
# Removes: scheduled task (Task Scheduler / launchd / cron) + ~/.claude/ symlinks + ~/.daily-brief-config
# Leaves alone: project files, daily_reports/, logs/, power plan settings
# For a full cleanup: rm -rf the project directoryChange sources, schedule, layout, add new panels — see FORKING.md.
This project is shared on the LINUX DO open-source community. Thanks to the community members for feedback and suggestions.
MIT



