[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-78550":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":15,"stars7d":17,"stars30d":18,"stars90d":16,"forks30d":16,"starsTrendScore":19,"compositeScore":20,"rankGlobal":10,"rankLanguage":10,"license":21,"archived":22,"fork":22,"defaultBranch":23,"hasWiki":22,"hasPages":24,"topics":25,"createdAt":10,"pushedAt":10,"updatedAt":34,"readmeContent":35,"aiSummary":36,"trendingCount":16,"starSnapshotCount":16,"syncStatus":37,"lastSyncTime":38,"discoverSource":39},78550,"DailyBrief","leiting-eric\u002FDailyBrief","leiting-eric","AI 每日新闻简报 · GitHub 热门 + X 热门文章 + 行情技术分析 · 23 个数据源聚合 + LLM 中文摘要 · 本地或 GitHub Actions 部署","https:\u002F\u002Fleiting-eric.github.io\u002FDailyBrief\u002F",null,"TypeScript",253,118,33,1,0,32,220,16,74.23,"MIT License",false,"main",true,[26,27,28,29,30,31,32,33],"ai","claude-code","claude-skills","daily-digest","llm","news-aggregator","rss-aggregator","self-hosted","2026-06-12 04:01:23","\u003Ca id=\"zh\">\u003C\u002Fa>\n\n# 📰 daily-brief · 10 分钟拥有你自己的 AI 每日简报\n\n**中文** · [English ↓](#en)\n\n[![License: MIT](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Flicense-MIT-blue.svg)](LICENSE)\n[![Node 20+](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fnode-20%2B-brightgreen.svg)](https:\u002F\u002Fnodejs.org\u002F)\n[![TypeScript](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FTypeScript-5-3178c6.svg)](https:\u002F\u002Fwww.typescriptlang.org\u002F)\n[![LLM: pluggable](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FLLM-pluggable%20(5%20backends)-orange.svg)](#-llm-后端配置)\n[![Deploy: GH Actions](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fdeploy-GitHub%20Actions-2088ff.svg)](#a-github-actions--pages零基础设施推荐)\n[![Demo: live](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fdemo-leiting--eric.github.io%2FDailyBrief-brightgreen.svg)](https:\u002F\u002Fleiting-eric.github.io\u002FDailyBrief)\n[![Stars](https:\u002F\u002Fimg.shields.io\u002Fgithub\u002Fstars\u002Fleiting-eric\u002FDailyBrief?style=social)](https:\u002F\u002Fgithub.com\u002Fleiting-eric\u002FDailyBrief)\n\n> **你的私人 AI 每日简报，跑在你自己掌控的基础设施上。** 23 个数据源 · LLM 摘要 · 21 个股票\u002F加密标的**技术指标 + AI 交易点评** · 中英双语 · 5 个 LLM 后端可选。\n>\n> **三种部署任选**：[**🚀 5 分钟 Fork 到 GitHub Actions**](#a-github-actions--pages零基础设施推荐) · [**💻 本地一键装**](#b-本地一键装) · [**🤖 一句话让 AI Agent 帮你装**](#c-给-ai-agent-一句话装)。\n\n**🌐 Live demos** —\n[📰 leiting-eric.github.io\u002FDailyBrief](https:\u002F\u002Fleiting-eric.github.io\u002FDailyBrief)（A 方式 · GitHub Actions + Pages）\n·\n[📰 daily.leiting.tech](https:\u002F\u002Fdaily.leiting.tech)（B 方式 · 本地服务器部署）\n\n---\n\n## ✨ 核心特性\n\n- **🌍 全网多源聚合**：23 个数据源覆盖硅谷科技、AI 前沿、全球财经、国际时政、中文社区，一份报告通吃\n- **📈 21 个标的实时行情**：美股 \u002F 加密 \u002F 港股 \u002F 商品外汇 \u002F 宏观信号，附 SMA \u002F RSI \u002F MACD 技术指标 + LLM 每日交易点评\n- **🤖 5 个 LLM 后端可插拔**：Claude CLI \u002F Anthropic \u002F OpenAI \u002F DeepSeek \u002F MiniMax，一个环境变量切换，不绑死任何家\n- **🌐 中英双语**：`REPORT_LOCALE=en` 一切——数据源、prompt、UI 文案、Bullish\u002FBearish stance 全套切英文\n- **🚀 部署灵活**：GitHub Actions（零基础设施）\u002F 本地系统调度器 \u002F 自托管服务器三选一，互不冲突可并存\n- **🆓 数据源零 API key**：所有源走免费公开端点（RSS \u002F 公开 JSON），不需要付费订阅\n- **🔒 不绑定第三方阅读服务**：Feedly \u002F Inoreader \u002F Pocket 那些都不沾，你看了什么是你自己的事\n- **📁 单文件 HTML**：CSS + JS 全内联，无外部依赖，scp 上服务器直接当首页\n\n---\n\n## 📚 信源图谱\n\n23 个数据源（zh 模式）\u002F 22 个（en 模式），分布如下：\n\n### 🧑‍💻 技术动态\n\n\u003Cp align=\"center\">\u003Cimg src=\"docs\u002Fscreenshots\u002Ftech.png\" alt=\"技术动态 — GitHub Trending \u002F X 推文 \u002F AI 媒体\" width=\"720\">\u003C\u002Fp>\n\n- **GitHub Trending** · 热榜每日刷新\n- **AI 媒体**（merged）：OpenAI \u002F DeepMind \u002F Hugging Face Blog \u002F TLDR AI \u002F Smol AI \u002F Latent Space \u002F MIT Tech Review\n- **X 推文**（attentionvc-ai）：精选 AI 圈大佬动态\n\n### 📈 市场行情（非新闻源，21 个标的）\n\n\u003Cp align=\"center\">\u003Cimg src=\"docs\u002Fscreenshots\u002Ftrading.png\" alt=\"市场行情 — 21 ticker 技术指标 + AI 中文点评\" width=\"720\">\u003C\u002Fp>\n\n- **美股 \u002F ETF**：SPY \u002F QQQ \u002F AAPL \u002F MSFT \u002F NVDA \u002F GOOGL \u002F TSLA \u002F META\n- **加密**：BTC \u002F ETH \u002F SOL（附 alt.me 加密恐慌贪婪指数 + CoinGecko 总览）\n- **中港**：BABA \u002F PDD \u002F JD \u002F 0700.HK\n- **商品外汇**：GC=F（黄金）\u002F CL=F（WTI 原油）\u002F USDCNY=X\n- **宏观信号**：^VIX \u002F ^TNX（10Y 美债）\u002F DXY（美元指数）\n\n### 🌍 时政观察\n\n\u003Cp align=\"center\">\u003Cimg src=\"docs\u002Fscreenshots\u002Fpolitics.png\" alt=\"时政观察 — BBC \u002F Guardian \u002F NYT 等国际要闻\" width=\"720\">\u003C\u002Fp>\n\n**BBC \u002F Guardian \u002F NYT \u002F NPR \u002F DW 中文 \u002F Al Jazeera \u002F The Diplomat** — 7 家主流国际媒体的 World 频道\n\n### 💰 财经要点\n\n\u003Cp align=\"center\">\u003Cimg src=\"docs\u002Fscreenshots\u002Ffinance.png\" alt=\"财经要点 — Bloomberg \u002F WSJ \u002F FT 全球财经\" width=\"720\">\u003C\u002Fp>\n\n**Bloomberg \u002F WSJ \u002F FT \u002F BBC Business \u002F Economist** — 5 家全球财经主力\n\n### 💬 社区讨论\n- **zh 模式**：V2EX 热榜 \u002F LinuxDo 热帖\n- **en 模式**：Hacker News \u002F Reddit r\u002Fstocks（自动替换上面两个）\n\n> 完整列表 + 启用状态：`npm run sources` 查看；改源 → 编辑 [`sources.config.json`](sources.config.json)。\n\n---\n\n## 🚀 三种部署方式选一种\n\n| 方式 | 适合谁 | 你需要 | 几分钟搞定 |\n|---|---|---|---|\n| **A. GitHub Actions + Pages** | 没服务器、不想常开电脑 | 一个 API key（Anthropic \u002F OpenAI \u002F DeepSeek \u002F MiniMax 任一） | ~5 分钟（推荐） |\n| **B. 本地一键装** | 有常开的电脑\u002F服务器、想极致便宜 | Node 20+，可选 Claude Code 登录 | ~3 分钟 |\n| **C. 给 AI Agent 一句话** | 懒、想让 Cursor \u002F Codex \u002F Claude Code 帮你装 | 同上 | 一句话 |\n\n### A. GitHub Actions + Pages（零基础设施，推荐）\n\n1. **Fork 这个 repo**（GitHub 右上角 Fork 按钮）\n2. 进 Fork 的 repo → **Settings → Actions → General → Workflow permissions** 设为 **Read and write permissions**\n3. **Settings → Pages → Build and deployment → Source** 选 \"Deploy from a branch\"，分支 `gh-pages` \u002F 路径 `\u002F (root)`（第一次跑完才会出现 gh-pages 分支，先建 secret 再触发一次即可）\n4. **🔑 配置 LLM 后端** —— 这步是关键。每个后端都要**一个 secret + 对应的 `LLM_BACKEND` variable**（不只是 secret），按下表对照填：\n\n   | 你想用 | Secrets 标签加 | Variables 标签加 `LLM_BACKEND` | 大致成本 |\n   |---|---|---|---|\n   | 🟣 **Anthropic Sonnet**（默认，prompt 按 Sonnet 调优） | `ANTHROPIC_API_KEY` | 不填或填 `anthropic` | ~$0.03-0.05 \u002F 天，月 \u003C $2 |\n   | 🐋 **DeepSeek**（便宜大碗，中文友好） | `DEEPSEEK_API_KEY` | `deepseek` | ~$0.01-0.02 \u002F 天，月 \u003C $1 |\n   | 🟢 **OpenAI** | `OPENAI_API_KEY` | `openai` | gpt-4o-mini ~$0.02 \u002F 天 |\n   | 🔵 **MiniMax** | `MINIMAX_API_KEY` | `minimax` | 类似 DeepSeek 量级 |\n   | 🌀 **中转站 \u002F 反代 \u002F 其他 OpenAI 兼容服务**（Moonshot \u002F SiliconFlow \u002F OpenRouter \u002F 自建 Claude 反代 \u002F 本地 Ollama \u002F LM Studio 等） | `LLM_API_KEY` | `openai`（极少数 Anthropic 协议反代填 `anthropic`） | 看服务方定价 |\n\n   位置：**Settings → Secrets and variables → Actions**，左边切换 Secrets \u002F Variables 两个标签。\n\n   > 🌀 **选了最后一行\"中转站\"的额外步骤**：Variables 还要加 `LLM_BASE_URL`（中转站给的 endpoint，如 `https:\u002F\u002Fapi.moonshot.cn\u002Fv1`）和 `LLM_MODEL`（如 `moonshot-v1-8k`）。不确定协议？默认 `LLM_BACKEND=openai` 覆盖 95% 中转站；如果跑挂报 404 \u002F 协议错误再改成 `anthropic`。\n\n5. （可选）同页 Variables 再加：\n   - `LLM_MODEL` —— 覆盖该 backend 的默认模型（不填用 [`.env.example`](.env.example) 里列的默认）\n   - `LLM_BASE_URL` —— 自定义 endpoint。**选了上面\"中转站\"那行的话必填**；本地 Ollama 填 `http:\u002F\u002Flocalhost:11434\u002Fv1`、LM Studio 填 `http:\u002F\u002Flocalhost:1234\u002Fv1`\n   - `REPORT_LOCALE` —— `zh`（默认）或 `en`，控制数据源 + UI + prompt 全套切英文\n   - `REPORT_TZ` —— IANA 时区名（默认 UTC），例 `Asia\u002FShanghai` \u002F `America\u002FLos_Angeles`。**同时影响触发时间和日期标签**\n   - `REPORT_HOUR` —— 触发的小时（基于 `REPORT_TZ`），默认 `8`（早 8 点）。逗号分隔可多次触发，如 `8,18` = 早 8 + 晚 6\n   - `REPORT_DAYS` —— 触发的星期（cron 风格，`0`=周日 ... `6`=周六），默认 `*`（每天）。例 `1-5` = 工作日；`1,3,5` = 周一三五\n6. **Actions 标签 → 选 \"Daily Brief\" workflow → Run workflow** 手动触发一次\n\n跑完后报告在 `https:\u002F\u002F\u003C你的用户名>.github.io\u002F\u003Crepo-名字>\u002F`。之后**默认每天 `REPORT_TZ` 时区的 08:00 自动更新**（不设 `REPORT_TZ` 就是 UTC 08:00）。\n\n> ⏰ **触发机制**：GitHub Actions 的 cron 只接受 UTC，所以工作流 cron 设置为**每小时跑一次**，里面有一个 `gate` 任务用 `REPORT_TZ` 把当前小时和 `REPORT_HOUR\u002FREPORT_DAYS` 对照——匹配才往下跑 build，否则秒退。这样不论你在哪个时区都能精准命中本地时间，**夏令时也自动跟着切换**（IANA 时区数据库内置）。\n\n**常用 schedule 配方：**\n\n| 想要 | `REPORT_HOUR` | `REPORT_DAYS` |\n|---|---|---|\n| 每天 08:00（默认） | 不填或 `8` | 不填或 `*` |\n| 每天早晚两次（8 + 18 点） | `8,18` | `*` |\n| 工作日 09:00 | `9` | `1-5` |\n| 周一\u002F三\u002F五 早 7 晚 9 两次 | `7,21` | `1,3,5` |\n| 每 6 小时一次 | `0,6,12,18` | `*` |\n\n只想要默认每天 08:00 本地时间，**只填 `REPORT_TZ` 一个变量就够了**（如 `Asia\u002FShanghai`），其他全部留空。\n\n**💸 成本估算**：GitHub Actions 公开 repo 完全免费。Pages 公开 repo 也免费。唯一花钱的就是 LLM API 调用——DeepSeek 月成本不到 $1，Anthropic Sonnet \u003C $2。\n\n> ⚠️ 用 GH Actions 模式就意味着**用不了本地 `claude` CLI**——Claude Code 的 OAuth 登录在你本机，GitHub 的服务器看不到。如果你已经在 Max 订阅里，建议两条路并行：本地装（B 方式）用 Claude CLI 跑你自己的服务器版本，GH Actions 用 DeepSeek 跑 Pages 公开版本。两份报告独立，互不影响。\n\n#### 🐛 A 方式常见坑\n\n- **\"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 里依然加密保护，对其他人不可见\n- **Variable name 报 \"alphanumeric only\"** —— 输入 `LLM_BACKEND` 时下划线被中文输入法替换成了全角 `＿`（U+FF3F）。切到英文输入法 Shift+`-` 重打\n- **第一次跑完才能选 Pages source** —— Pages 设置页要求选已存在的分支，但 `gh-pages` 是首次 workflow 跑成功后才创建出来。顺序：配 secret → 触发 workflow → 跑完 → 回 Settings → Pages 选 `gh-pages`\n- **Action 红 X 怎么看具体原因** —— 点失败的 build → 左边列出每个 step → 找有红 X 的那步点开看 log。最常见两类：`401\u002F402` = API key 拼错或没余额；`403` = workflow permissions 没设成 Read and write\n- **跑了 30 秒就挂** —— 多半是 secret\u002Fvariable 没配对（光填了 secret 没填 `LLM_BACKEND` variable）或者 LLM API 返 400。看 step \"Generate today's report\" 的 log\n\n### B. 本地一键装\n\n```bash\n# Linux \u002F macOS\ncurl -sSL https:\u002F\u002Fraw.githubusercontent.com\u002Fleiting-eric\u002FDailyBrief\u002Fmain\u002Fbootstrap.mjs | node\n\n# Windows PowerShell\nirm https:\u002F\u002Fraw.githubusercontent.com\u002Fleiting-eric\u002FDailyBrief\u002Fmain\u002Fbootstrap.mjs | node -\n```\n\n这条命令会自动：\n1. 检查 Node \u002F git \u002F claude CLI 是否就位（没装 claude CLI 只发警告，可继续走 API 后端）\n2. `git clone` 到 `~\u002Fdaily-brief`（Windows: `%USERPROFILE%\\daily-brief`）\n3. `npm install`\n4. 注册系统定时（Windows Task Scheduler \u002F macOS launchd \u002F Linux cron，默认 16:00 本地时间）\n5. 写 `~\u002F.daily-brief-config` 记录项目路径\n6. 在 `~\u002F.claude\u002F` 建符号链接让 Claude Code 的 skill 和 slash command 全局可用\n7. 跑一次 `npm run dry-run` 烟测\n\n**🎁 Claude Code 用户额外福利**：装完后任意目录都能 `\u002Frun-daily`、`\u002Fcheck-daily`，描述问题（\"日报又挂了\"）也能触发 `daily-brief` skill 自动加载。**其他 agent**（Cursor \u002F Codex）没有 skill 加载机制，但定时任务跑得起来。手动触发用：\n\n| 平台 | 手动触发 |\n|---|---|\n| Windows | `Start-ScheduledTask -TaskName DailyBrief` |\n| macOS | `launchctl start com.daily-brief` |\n| Linux | `node scripts\u002Frun-daily.mjs`（cron 不支持手动触发） |\n\n自定义路径 \u002F 触发时间：\n\n```bash\nnode bootstrap.mjs --target \u002Fcustom\u002Fpath --at 07:30\n```\n\n**LLM 后端**：默认走本地 `claude` CLI（首次需在浏览器登录一次：`echo \"hi\" | claude --print --model sonnet`，登录后永久生效）。不用 Claude Code 就跳过它，复制 `.env.example` 到 `.env.local` 把 `LLM_BACKEND` 切到 OpenAI \u002F Anthropic \u002F DeepSeek \u002F MiniMax —— 见 [LLM 后端配置](#-llm-后端配置)。\n\n### C. 给 AI Agent 一句话装\n\n无论你用哪个 AI Agent（Claude Code \u002F Cursor \u002F Codex \u002F Continue.dev \u002F OpenClaw 等），把下面这段发给它：\n\n> 帮我装这个开源项目，按 README 的\"本地一键装\"流程跑 bootstrap，完成后告诉我下次自动触发的时间：\n> https:\u002F\u002Fgithub.com\u002Fleiting-eric\u002FDailyBrief\n\n项目里有 [`AGENTS.md`](AGENTS.md)（通用 agent 协议）+ [`.claude\u002Fskills\u002Fdaily-brief\u002FSKILL.md`](.claude\u002Fskills\u002Fdaily-brief\u002FSKILL.md)（Claude Code 专属，更详细），Agent 装完后能直接帮你诊断\"今天报告没出来\"、\"加个新数据源\"这类问题。\n\n---\n\n## 📋 前置要求\n\n- **Node.js 20+** + **npm** + **git**（B\u002FC 方式本地需要；A 方式不需要——GH Actions 容器自带）\n- **一个能跑的 LLM**（任选其一）：Claude Code CLI 已登录 \u002F Anthropic \u002F OpenAI \u002F DeepSeek \u002F MiniMax 任一家的 API key\n- 平台：Windows 10\u002F11、macOS 12+、Linux（任一平台都支持，定时机制自动适配）\n\n---\n\n## 🔧 手动安装\n\n```bash\n# 1. clone + 依赖\ngit clone https:\u002F\u002Fgithub.com\u002Fleiting-eric\u002FDailyBrief.git\ncd DailyBrief\nnpm install\n\n# 2. 配置 LLM 后端\n#    默认 claude CLI（如果没登录会引导你登录）：\necho \"say hi in Chinese\" | claude --print --model sonnet\n#    或用其他 backend：cp .env.example .env.local 编辑 LLM_BACKEND 和对应 API key\n\n# 3. 注册定时 + 启用全局 skill\nnode scripts\u002Finstall.mjs --global\n# 也可指定时间：node scripts\u002Finstall.mjs --at 07:30 --global\n\n# 4. 立即触发一次测试\n# Windows:  Start-ScheduledTask -TaskName DailyBrief\n# macOS:    launchctl start com.daily-brief\n# Linux:    node scripts\u002Frun-daily.mjs\n```\n\n下次触发时机：\n- **🪟 Windows** — 系统会自动唤醒电脑（如在睡眠），跑完再回睡\n- **🍎 macOS** — launchd 不会主动唤醒，电脑睡着的话跳过这次（需要 `pmset wake schedule` 配合）\n- **🐧 Linux** — cron 同理，挂起期间不跑\n\n---\n\n## 🛠️ 日常命令\n\n| 命令 | 用途 | 耗时 |\n|---|---|---|\n| `npm run daily` | 手动完整跑一次 | 5-8 min |\n| `npm run dry-run` | 只抓取不调 LLM，验证数据源 | ~30s |\n| `npm run render [date]` | 改了 CSS\u002F排版后重渲染 | \u003C1s |\n| `npm run regen-trading [date]` | 重做交易部分 | ~2 min |\n| `npm run regen-enrich \u003Ccat:sub> [date]` | 补缺失的中文摘要 | ~30s |\n| `npm run open` | 在 Chrome 打开今日报告 | 即时 |\n| `npm run quota-report` | 看各 LLM backend 用量统计 | 即时 |\n| `npm run sources` | 列出所有数据源（按 locale 标注启用\u002F过滤状态）| 即时 |\n| `npm run sources:check` | 仅校验 `sources.config.json` schema（适合 CI \u002F pre-commit）| 即时 |\n\n---\n\n## 📊 数据源配置\n\n数据源以 JSON 数组形式集中存储在项目根的 [`sources.config.json`](sources.config.json) —— **唯一配置入口**，不需要改 TypeScript 代码即可加\u002F禁\u002F调整源。每条记录的字段：\n\n| 字段 | 必填 | 说明 |\n|---|---|---|\n| `id` | ✓ | 全局唯一短标识（dispatch.ts 用 id 路由到对应 fetcher）|\n| `name` | ✓ | UI 显示名 |\n| `type` | ✓ | `rss` \u002F `api` \u002F `scrape` |\n| `url` | ✓ | RSS feed 或 API endpoint |\n| `category` | ✓ | `tech` \u002F `finance` \u002F `politics`，决定 L1 tab |\n| `subcategory` |   | `tech` 下的 L2 分组（`github-trending` \u002F `ai-news` \u002F `x-viral` \u002F `cn-community`）；`finance` 下统一 `news` |\n| `enabled` |   | 默认 `true`，设 `false` 跳过抓取（保留记录便于回滚）|\n| `useCurl` |   | 若该源被 Cloudflare TLS-fingerprint 拦了 Node undici，设 `true` 让 rss.ts 走 curl 子进程 |\n| `lang` |   | `zh` 表示源内容是中文 — enrich 阶段会跳过它（无需把中文翻译成中文）|\n| `locales` |   | 数组，标明该源出现在哪些 report locale 下。默认 `[\"zh\", \"en\"]` |\n| `notes` |   | 任意备注（如\"被废弃因为 feed 死了\"），运行时忽略 |\n\n### 加一个 RSS 源\n\n1. 编辑 `sources.config.json`，追加一条记录\n2. 跑 `npm run sources:check` 校验 schema\n3. `npm run dry-run` 抓一次验证拉取正常\n4. 下次 `npm run daily` 自动包含\n\n### 🌐 Locale 模式（zh \u002F en）\n\n通过 `REPORT_LOCALE` 环境变量切换：\n\n```bash\n# .env.local\nREPORT_LOCALE=zh    # 默认 — 中文 mode，含 V2EX \u002F LinuxDo \u002F DW 中文等中文源\n# REPORT_LOCALE=en  # 英文 mode — 自动过滤掉 zh-only 源，挂上 en-only 源\n```\n\n每个源在 JSON 里的 `locales` 字段决定它在哪些模式下出现：\n\n- `[\"zh\"]` — **仅中文 mode**（V2EX \u002F LinuxDo \u002F DW 中文）。英文用户读不懂，英文 mode 自动 drop\n- `[\"en\"]` — **仅英文 mode**（Hacker News \u002F r\u002Fstocks 英文社区源，替代中文社区源）。zh mode 不显示\n- `[\"zh\", \"en\"]`（默认）— 两 mode 都参与（BBC \u002F Bloomberg \u002F WSJ \u002F NYT 全球英文源 + AI 资讯源）\n\n当前启用源（`enabled: true`）按 locale 分布：\n\n| Locale | 启用源数 | 主要构成 |\n|---|---|---|\n| `zh` | 23 | 20 个全球英文源（附中文摘要）+ 3 个中文专属（V2EX \u002F LinuxDo \u002F DW 中文）|\n| `en` | 22 | 20 个全球英文源 + 2 个英文社区（Hacker News + r\u002Fstocks）|\n\n英文 mode 完整切换：HTML UI 文案、enrichment \u002F digest \u002F trading-commentary 三套 prompt、stance 词（\"偏上行\u002F偏下行\u002F中性\" → Bullish\u002FBearish\u002FNeutral）、日期格式（zh-CN → en-GB）、Markdown 输出 —— 全部跟着 `REPORT_LOCALE` 切。**中文社区源在英文 mode 下被自动过滤掉**。\n\n---\n\n## 🤖 LLM 后端配置\n\n项目通过 `LLM_BACKEND` 环境变量切换后端。**默认 `claude-cli`** —— 直接复用 Claude Code 已登录的认证，不需要额外配 API key。不用 Claude Code、或想走自己的 API key，按下表切换。\n\n把 `.env.example` 复制成 `.env.local`（gitignored），按 backend 解开对应几行：\n\n| backend | API key 环境变量 | 默认 model | base URL |\n|---|---|---|---|\n| 🎯 `claude-cli` （默认）| 不需要，复用 Claude Code OAuth | `sonnet` | — |\n| 🟣 `anthropic` | `ANTHROPIC_API_KEY` | `claude-sonnet-4-6` | `api.anthropic.com` |\n| 🟢 `openai` | `OPENAI_API_KEY` | `gpt-4o-mini` | `api.openai.com\u002Fv1` |\n| 🐋 `deepseek` | `DEEPSEEK_API_KEY` | `deepseek-v4-flash` | `api.deepseek.com\u002Fv1` |\n| 🔵 `minimax` | `MINIMAX_API_KEY` | `MiniMax-M2.7` | `api.minimax.io\u002Fv1` \u003Csup>1\u003C\u002Fsup> |\n\n\u003Csup>1\u003C\u002Fsup> 中国大陆访问设 `MINIMAX_BASE_URL=https:\u002F\u002Fapi.minimaxi.com\u002Fv1`。\n\n**通用覆盖项**：\n- `LLM_MODEL=\u003Cid>` —— 任意 backend 的 model 都能用这个变量覆盖默认（如 `LLM_MODEL=gpt-4o` 走 openai 的更大模型）\n- `\u003CBACKEND>_BASE_URL` —— 走自托管代理 \u002F 兼容服务（如 LM Studio \u002F Ollama 跑 OpenAI 兼容接口 → `LLM_BACKEND=openai` + `OPENAI_BASE_URL=http:\u002F\u002Flocalhost:1234\u002Fv1`）\n- `LLM_API_KEY` \u002F `LLM_BASE_URL` —— **通用别名**，专用变量没设时 fallback 到这里。用 Moonshot \u002F SiliconFlow \u002F OpenRouter \u002F 自建反代等非预设服务时推荐用这对，避免 `OPENAI_API_KEY` 这样的语义错位。例：跑 Moonshot 只要 `LLM_BACKEND=openai` + `LLM_API_KEY=sk-...` + `LLM_BASE_URL=https:\u002F\u002Fapi.moonshot.cn\u002Fv1` + `LLM_MODEL=moonshot-v1-8k`\n\n### 怎么选\n\n| 你的情况 | 推荐 backend |\n|---|---|\n| 已经在用 Claude Code（任意订阅等级）| `claude-cli` — 零配置，按你订阅的等级走 |\n| 不用 Claude Code，只想低成本跑日报 | `openai` 配 `gpt-4o-mini`、或 `deepseek` 配 `deepseek-v4-flash`（更便宜）|\n| 中文摘要质量优先，预算可放宽 | `anthropic` 配 `claude-sonnet-4-6` |\n| 国内网络访问，要规避 GFW | `deepseek` 或 `minimax`（都是国内厂商）|\n\n**切 backend 不需要改代码**：所有 prompt 都在 `lib\u002Fai\u002Fprompts.ts` 抽离，跟 backend 无关；JSON 错误兜底（`jsonrepair`）也是 backend-agnostic。切完后跑一次 `npm run daily`，进 `logs\u002Fllm-calls.jsonl` 看新 backend 的调用记录。\n\n---\n\n## 🌐 自托管部署（可选）\n\n每次 `npm run daily` 跑完，自动把新 HTML 推到自己的服务器，访客打开 `https:\u002F\u002Fyour-domain\u002F` 就能看到当天最新报告。**默认不启用**，环境变量不设就跳过。\n\n### 一次性服务器准备\n\n假设服务器跑 Ubuntu + nginx + 已申请域名，登录用户有 sudo NOPASSWD：\n\n```bash\n# 服务器上\nsudo mkdir -p \u002Fvar\u002Fwww\u002Fyour-domain && sudo chown -R www-data:www-data \u002Fvar\u002Fwww\u002Fyour-domain\n```\n\n`\u002Fetc\u002Fnginx\u002Fsites-available\u002Fyour-domain.conf`：\n\n```nginx\nserver {\n    listen 80;\n    server_name your-domain;\n    root \u002Fvar\u002Fwww\u002Fyour-domain;\n    index index.html;\n    location \u002F { try_files $uri $uri\u002F =404; }\n}\n```\n\n启用 + 自动签 SSL：\n\n```bash\nsudo ln -s \u002Fetc\u002Fnginx\u002Fsites-available\u002Fyour-domain.conf \u002Fetc\u002Fnginx\u002Fsites-enabled\u002F\nsudo nginx -t && sudo systemctl reload nginx\nsudo certbot --nginx -d your-domain --agree-tos --redirect\n```\n\n### 本地启用 auto-deploy\n\n`.env.local`（gitignored）加两行：\n\n```\nDEPLOY_HOST=user@your-server-ip\nDEPLOY_PATH=\u002Fvar\u002Fwww\u002Fyour-domain\n```\n\n之后：\n- ✅ 每次 `npm run daily` 跑完，自动 scp 当天 HTML 到服务器 + 刷新 `index.html`\n- ✅ `npm run deploy [YYYY-MM-DD]` 手动推任意一天\n- ✅ 部署失败不阻断 daily 本身（HTML 已经在本地 `daily_reports\u002F` 落盘，可重跑 `npm run deploy` 补推）\n\n---\n\n## 💡 Claude Code 集成\n\n装好后**任意目录**（不必 cd 进项目）打开 Claude Code 都可用：\n\n| 触发 | 行为 |\n|---|---|\n| `\u002Frun-daily` | 立即触发 daily 并后台监听到完成。从任意目录都行 |\n| `\u002Fcheck-daily` | 查任务状态 + 报告文件 + 配额 |\n| 描述问题（\"日报又挂了\"、\"X 推文为啥没更新\"等）| `daily-brief` skill 的关键词触发自动加载，让 Claude 直接懂这个项目 |\n\n**实现机制**：`scripts\u002Finstall.mjs --global` 在 `~\u002F.claude\u002F` 下建符号链接，指向项目内的 [`.claude\u002Fskills\u002Fdaily-brief\u002FSKILL.md`](.claude\u002Fskills\u002Fdaily-brief\u002FSKILL.md) 和 [`.claude\u002Fcommands\u002F`](.claude\u002Fcommands\u002F) 文件——**单一源**，编辑项目文件等于编辑用户级 skill。当 symlink 因权限受限失败时（如 Windows 无开发者模式），自动 fallback 到 copy。`~\u002F.daily-brief-config` 记录项目实际路径，让 slash command 在任意 cwd 都能找到项目。\n\n---\n\n## 📁 项目结构\n\n```\ndaily-brief\u002F\n├── lib\u002F\n│   ├── sources\u002F        # RSS \u002F API \u002F curl 抓取器；新加源在这里\n│   ├── ai\u002F             # 可插拔 LLM 后端 + 提示词（lib\u002Fai\u002Fbackends\u002F 下每个 backend）\n│   ├── trading\u002F        # Yahoo Finance + 技术指标\n│   ├── output\u002F         # 渲染层 (HTML \u002F Markdown)\n│   └── utils.ts        # 小工具（todayKey \u002F getReportTz）\n├── scripts\u002F\n│   ├── _env.ts         # dotenv 预加载（被所有入口脚本第一个 import）\n│   ├── daily.ts        # 主管线\n│   ├── dry-run.ts      # 只抓取不调 LLM，验证数据源\n│   ├── render.ts       # 重渲染\n│   ├── regen-*.ts      # 局部重跑（trading \u002F enrich）\n│   ├── quota-report.ts # LLM 用量统计\n│   ├── sources.ts      # `npm run sources` — 列源 + 校验 JSON\n│   ├── run-daily.mjs   # OS 调度器调用的包装（含自动 deploy + open）\n│   ├── open-report.mjs # 打开最新报告（跨平台）\n│   ├── build-site.mjs  # 生成 GH Pages 静态站 (index + archive)\n│   ├── deploy.mjs      # scp 到远端 nginx 服务器（可选）\n│   ├── install.mjs     # 注册定时任务（Win\u002FMac\u002FLinux 自适应）\n│   └── uninstall.mjs   # 卸载\n├── sources.config.json # 数据源唯一配置入口\n├── daily_reports\u002F      # 输出 (gitignored)\n│   └── 2026-05-15\u002F     # 每日一个子目录，内含 .html (主) \u002F .json (缓存) \u002F -articles.json (缓存)\n│                       #   .md 默认不生成，可在 .env.local 设 OUTPUT_MARKDOWN=true 开启\n├── logs\u002F               # 运行日志 (gitignored)\n├── .github\u002Fworkflows\u002F  # GitHub Actions 工作流（A 方式部署）\n└── .claude\u002F\n    ├── skills\u002F         # Claude Code 操作 skill\n    └── commands\u002F       # slash commands\n```\n\n---\n\n## 🗑️ 卸载\n\n```bash\nnode scripts\u002Funinstall.mjs\n# 移除：定时任务 (Task Scheduler \u002F launchd \u002F cron) + ~\u002F.claude\u002F 下的链接 + ~\u002F.daily-brief-config\n# 不动：项目文件、daily_reports\u002F、logs\u002F、power plan 设置\n# 想彻底清理就 rm -rf 整个项目目录\n```\n\n---\n\n## 🛠️ 自定义 \u002F Fork\n\n改源、改时间、改排版、加新栏目 —— 见 [FORKING.md](FORKING.md)。\n\n---\n\n## 🙏 致谢\n\n本项目在 [LINUX DO](https:\u002F\u002Flinux.do) 开源社区分享推广，感谢佬友们的反馈与建议。\n\n## 📝 License\n\nMIT\n\n---\n\n\u003Cbr>\n\n\u003Ca id=\"en\">\u003C\u002Fa>\n\n# 📰 daily-brief · your own AI-curated daily news brief in 10 minutes\n\n[↑ 中文](#zh) · **English**\n\n[![License: MIT](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Flicense-MIT-blue.svg)](LICENSE)\n[![Node 20+](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fnode-20%2B-brightgreen.svg)](https:\u002F\u002Fnodejs.org\u002F)\n[![TypeScript](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FTypeScript-5-3178c6.svg)](https:\u002F\u002Fwww.typescriptlang.org\u002F)\n[![LLM: pluggable](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FLLM-pluggable%20(5%20backends)-orange.svg)](#-llm-backend-configuration)\n[![Deploy: GH Actions](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fdeploy-GitHub%20Actions-2088ff.svg)](#a-github-actions--pages-zero-infra-recommended)\n[![Demo: live](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fdemo-leiting--eric.github.io%2FDailyBrief-brightgreen.svg)](https:\u002F\u002Fleiting-eric.github.io\u002FDailyBrief)\n[![Stars](https:\u002F\u002Fimg.shields.io\u002Fgithub\u002Fstars\u002Fleiting-eric\u002FDailyBrief?style=social)](https:\u002F\u002Fgithub.com\u002Fleiting-eric\u002FDailyBrief)\n\n> **Your own AI-curated daily news brief, on infrastructure you control.** 23 sources · LLM summaries · 21-ticker market panel with SMA\u002FRSI\u002FMACD signals + AI commentary · bilingual (zh\u002Fen) · 5 swappable LLM backends.\n>\n> **Three deployment paths, pick one:** [**🚀 5-min GitHub Actions fork**](#a-github-actions--pages-zero-infra-recommended) · [**💻 local one-liner install**](#b-local-one-liner-install) · [**🤖 have an AI agent install it for you**](#c-have-an-ai-agent-install-it-for-you).\n\n**🌐 Live demos** —\n[📰 leiting-eric.github.io\u002FDailyBrief](https:\u002F\u002Fleiting-eric.github.io\u002FDailyBrief) (path A · GitHub Actions + Pages)\n·\n[📰 daily.leiting.tech](https:\u002F\u002Fdaily.leiting.tech) (path B · self-hosted server)\n\n---\n\n## ✨ Core features\n\n- **🌍 Multi-source aggregation** — 23 sources spanning Silicon Valley tech, AI frontier, global finance, international politics, and developer communities. One report covers it all.\n- **📈 21 live tickers** — US stocks \u002F crypto \u002F HK \u002F commodities \u002F macro signals, with SMA \u002F RSI \u002F MACD indicators + daily LLM-written trading commentary\n- **🤖 5 swappable LLM backends** — Claude CLI \u002F Anthropic \u002F OpenAI \u002F DeepSeek \u002F MiniMax. One env var to switch, no vendor lock-in.\n- **🌐 Bilingual (zh\u002Fen)** — set `REPORT_LOCALE=en` to flip the entire stack: sources, prompts, UI text, Bullish\u002FBearish stance labels — all switch.\n- **🚀 Flexible deployment** — GitHub Actions (zero infra) \u002F local OS scheduler \u002F self-hosted server — pick one or run them in parallel\n- **🆓 Zero data-source API keys** — every source uses free public endpoints (RSS \u002F public JSON), no paid subscriptions\n- **🔒 No third-party reader lock-in** — Feedly \u002F Inoreader \u002F Pocket all profile your reading; this project never talks to them. Your reading habits stay yours.\n- **📁 Single-file HTML output** — CSS and JS inlined, no external dependencies, scp'able onto a server as `index.html`\n\n---\n\n## 📚 Source roster\n\n23 sources in zh mode \u002F 22 in en mode, organized as:\n\n### 🧑‍💻 Tech\n\n\u003Cp align=\"center\">\u003Cimg src=\"docs\u002Fscreenshots\u002Ftech.png\" alt=\"Tech panel — GitHub Trending \u002F X posts \u002F AI media\" width=\"720\">\u003C\u002Fp>\n\n- **GitHub Trending** · refreshed daily\n- **AI media** (merged): OpenAI \u002F DeepMind \u002F Hugging Face Blog \u002F TLDR AI \u002F Smol AI \u002F Latent Space \u002F MIT Tech Review\n- **X posts** (attentionvc-ai): curated AI thought-leader feed\n\n### 📈 Markets (21 tickers, not news)\n\n\u003Cp align=\"center\">\u003Cimg src=\"docs\u002Fscreenshots\u002Ftrading.png\" alt=\"Markets — 21-ticker technicals + AI commentary\" width=\"720\">\u003C\u002Fp>\n\n- **US stocks \u002F ETFs**: SPY \u002F QQQ \u002F AAPL \u002F MSFT \u002F NVDA \u002F GOOGL \u002F TSLA \u002F META\n- **Crypto**: BTC \u002F ETH \u002F SOL (+ alt.me Fear & Greed index + CoinGecko macro)\n- **China \u002F HK**: BABA \u002F PDD \u002F JD \u002F 0700.HK\n- **Commodities \u002F FX**: GC=F (gold) \u002F CL=F (WTI crude) \u002F USDCNY=X\n- **Macro signals**: ^VIX \u002F ^TNX (10Y Treasury) \u002F DXY\n\n### 🌍 World\n\n\u003Cp align=\"center\">\u003Cimg src=\"docs\u002Fscreenshots\u002Fpolitics.png\" alt=\"World — BBC \u002F Guardian \u002F NYT etc.\" width=\"720\">\u003C\u002Fp>\n\n**BBC \u002F Guardian \u002F NYT \u002F NPR \u002F DW Chinese \u002F Al Jazeera \u002F The Diplomat** — 7 major international outlets' World feeds\n\n### 💰 Finance\n\n\u003Cp align=\"center\">\u003Cimg src=\"docs\u002Fscreenshots\u002Ffinance.png\" alt=\"Finance — Bloomberg \u002F WSJ \u002F FT\" width=\"720\">\u003C\u002Fp>\n\n**Bloomberg \u002F WSJ \u002F FT \u002F BBC Business \u002F Economist** — 5 global finance heavyweights\n\n### 💬 Community\n- **zh mode**: V2EX top threads \u002F LinuxDo trending\n- **en mode**: Hacker News \u002F Reddit r\u002Fstocks (auto-substituted)\n\n> Full list + enabled status: run `npm run sources`. To edit sources, modify [`sources.config.json`](sources.config.json).\n\n---\n\n## 🚀 Pick one deployment path\n\n| Path | Who it's for | What you need | Setup time |\n|---|---|---|---|\n| **A. GitHub Actions + Pages** | No server, don't want to keep a laptop running | One API key (Anthropic \u002F OpenAI \u002F DeepSeek \u002F MiniMax) | ~5 min (recommended) |\n| **B. Local one-liner** | Have an always-on machine; want it cheapest | Node 20+, optionally Claude Code login | ~3 min |\n| **C. Have an AI agent install it** | Lazy; want Cursor \u002F Codex \u002F Claude Code to handle setup | Same as B | One sentence |\n\n### A. GitHub Actions + Pages (zero-infra, recommended)\n\n1. **Fork this repo** (Fork button, top-right of GitHub)\n2. **Settings → Actions → General → Workflow permissions** → set to **Read and write permissions**\n3. **Settings → Pages → Build and deployment → Source** → \"Deploy from a branch\" → branch `gh-pages` \u002F path `\u002F (root)` (the `gh-pages` branch only exists after the first successful workflow run — configure secrets first, trigger once, then come back)\n4. **🔑 Configure the LLM backend** — this is the critical step. Each backend needs **a secret AND the matching `LLM_BACKEND` variable** (not just the secret). Pick one row:\n\n   | You want | Secret to add | `LLM_BACKEND` variable | Rough cost |\n   |---|---|---|---|\n   | 🟣 **Anthropic Sonnet** (default; prompts tuned for it) | `ANTHROPIC_API_KEY` | leave unset or `anthropic` | ~$0.03-0.05\u002Fday, \u003C$2\u002Fmonth |\n   | 🐋 **DeepSeek** (cheap, China-friendly) | `DEEPSEEK_API_KEY` | `deepseek` | ~$0.01-0.02\u002Fday, \u003C$1\u002Fmonth |\n   | 🟢 **OpenAI** | `OPENAI_API_KEY` | `openai` | gpt-4o-mini ~$0.02\u002Fday |\n   | 🔵 **MiniMax** | `MINIMAX_API_KEY` | `minimax` | Similar to DeepSeek |\n   | 🌀 **Proxy \u002F aggregator \u002F any OpenAI-compatible service** (Moonshot, SiliconFlow, OpenRouter, self-hosted Claude relay, local Ollama \u002F LM Studio, ...) | `LLM_API_KEY` | `openai` (use `anthropic` only if the proxy speaks Anthropic protocol — rare) | Depends on provider |\n\n   Location: **Settings → Secrets and variables → Actions**. The page has two tabs — **Secrets** for keys, **Variables** for `LLM_BACKEND`.\n\n   > 🌀 **Extras if you picked the last \"proxy\" row**: also add `LLM_BASE_URL` (the endpoint your proxy gave you, e.g. `https:\u002F\u002Fapi.moonshot.cn\u002Fv1`) and `LLM_MODEL` (e.g. `moonshot-v1-8k`) under Variables. Not sure which protocol? Default `LLM_BACKEND=openai` — covers 95% of proxies; switch to `anthropic` only if you get 404s or protocol errors.\n\n5. (Optional) On the same Variables tab, add:\n   - `LLM_MODEL` — override the backend's default model (otherwise uses the default listed in [`.env.example`](.env.example))\n   - `LLM_BASE_URL` — custom endpoint. **Required if you picked the \"proxy\" row above.** For local Ollama use `http:\u002F\u002Flocalhost:11434\u002Fv1`, LM Studio `http:\u002F\u002Flocalhost:1234\u002Fv1`\n   - `REPORT_LOCALE` — `zh` (default) or `en` — switches sources + UI + LLM prompts as a set\n   - `REPORT_TZ` — IANA timezone name (default UTC); e.g. `Asia\u002FShanghai` \u002F `America\u002FLos_Angeles`. **Drives both the trigger time and the date label.**\n   - `REPORT_HOUR` — hour(s) to fire in `REPORT_TZ`, default `8` (08:00). Comma-separated for multiple, e.g. `8,18` = 8 AM and 6 PM\n   - `REPORT_DAYS` — day-of-week filter (cron-style, `0`=Sunday ... `6`=Saturday), default `*` (every day). E.g. `1-5` = weekdays; `1,3,5` = Mon\u002FWed\u002FFri\n6. **Actions tab → \"Daily Brief\" workflow → Run workflow** to trigger manually for the first time\n\nOnce the workflow turns green, your report lives at `https:\u002F\u002F\u003Cyour-username>.github.io\u002F\u003Crepo-name>\u002F`. After that, **it refreshes daily at 08:00 in `REPORT_TZ`** (or 08:00 UTC if `REPORT_TZ` is unset).\n\n> ⏰ **How the schedule works**: GitHub Actions cron is UTC-only, so the workflow runs **hourly** and uses a `gate` job to check if the current hour in `REPORT_TZ` matches `REPORT_HOUR` \u002F `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).\n\n**Common schedule recipes:**\n\n| You want | `REPORT_HOUR` | `REPORT_DAYS` |\n|---|---|---|\n| Every day at 08:00 (default) | unset or `8` | unset or `*` |\n| Twice daily (8 AM + 6 PM) | `8,18` | `*` |\n| Weekdays at 09:00 | `9` | `1-5` |\n| Mon\u002FWed\u002FFri at 7 AM + 9 PM | `7,21` | `1,3,5` |\n| Every 6 hours | `0,6,12,18` | `*` |\n\nIf you just want the default (08:00 local daily), **set only `REPORT_TZ`** (e.g. `Asia\u002FShanghai`) and leave the rest at defaults.\n\n**💸 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\u002Fmonth, Anthropic Sonnet under $2.\n\n> ⚠️ **GH Actions mode can't reuse a local `claude` CLI 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.\n\n#### 🐛 Common gotchas\n\n- **\"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.\n- **\"Variable name can only contain alphanumeric characters\"** — most likely the underscore in `LLM_BACKEND` got autocorrected by a CJK input method to a full-width `＿` (U+FF3F). Switch to English input, retype Shift+`-`, or copy-paste.\n- **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.\n- **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`\u002F`402` (API key wrong or out of credit), `403` (workflow permissions still set to \"Read only\").\n- **Fails after ~30 seconds** — usually a secret\u002Fvariable mismatch (added a secret but didn't add the matching `LLM_BACKEND` variable) or the LLM API returned 400. Check the \"Generate today's report\" step.\n\n### B. Local one-liner install\n\n```bash\n# Linux \u002F macOS\ncurl -sSL https:\u002F\u002Fraw.githubusercontent.com\u002Fleiting-eric\u002FDailyBrief\u002Fmain\u002Fbootstrap.mjs | node\n\n# Windows PowerShell\nirm https:\u002F\u002Fraw.githubusercontent.com\u002Fleiting-eric\u002FDailyBrief\u002Fmain\u002Fbootstrap.mjs | node -\n```\n\nThis script will:\n1. Check that Node \u002F git \u002F claude CLI are on PATH (claude CLI missing is a warning, not an error — you can use an API backend instead)\n2. `git clone` to `~\u002Fdaily-brief` (Windows: `%USERPROFILE%\\daily-brief`)\n3. `npm install`\n4. Register the OS scheduler (Windows Task Scheduler \u002F macOS launchd \u002F Linux cron, default 16:00 local time)\n5. Write `~\u002F.daily-brief-config` recording the project path\n6. Symlink the Claude Code skill + slash commands into `~\u002F.claude\u002F` so they work from any directory\n7. Run `npm run dry-run` as a smoke test\n\n**🎁 Claude Code bonus**: after install, any Claude Code session anywhere can use `\u002Frun-daily` and `\u002Fcheck-daily`. Describing a problem in plain English (\"today's report didn't come out\") also auto-loads the `daily-brief` skill. **Other agents** (Cursor \u002F Codex) don't have a skill auto-load mechanism, but the scheduled task still runs at the OS level. Manual triggers:\n\n| Platform | Command |\n|---|---|\n| Windows | `Start-ScheduledTask -TaskName DailyBrief` |\n| macOS | `launchctl start com.daily-brief` |\n| Linux | `node scripts\u002Frun-daily.mjs` (cron doesn't support manual trigger) |\n\nCustom install path \u002F time:\n\n```bash\nnode bootstrap.mjs --target \u002Fcustom\u002Fpath --at 07:30\n```\n\n**LLM 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 \u002F Anthropic \u002F DeepSeek \u002F MiniMax — see [LLM backend configuration](#-llm-backend-configuration).\n\n### C. Have an AI agent install it for you\n\nWhichever AI agent you use (Claude Code \u002F Cursor \u002F Codex \u002F Continue.dev \u002F OpenClaw \u002F etc.), send it this prompt:\n\n> 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:\n> https:\u002F\u002Fgithub.com\u002Fleiting-eric\u002FDailyBrief\n\nThe repo includes [`AGENTS.md`](AGENTS.md) (universal agent protocol) and [`.claude\u002Fskills\u002Fdaily-brief\u002FSKILL.md`](.claude\u002Fskills\u002Fdaily-brief\u002FSKILL.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\".\n\n---\n\n## 📋 Requirements\n\n- **Node.js 20+**, **npm**, **git** (local for paths B\u002FC; path A runs in GitHub's containers — no local install needed)\n- **One working LLM** (any of): Claude Code CLI logged in, OR Anthropic \u002F OpenAI \u002F DeepSeek \u002F MiniMax API key\n- Platform: Windows 10\u002F11, macOS 12+, Linux (any platform — scheduler picks the matching mechanism)\n\n---\n\n## 🔧 Manual install\n\n```bash\n# 1. Clone + dependencies\ngit clone https:\u002F\u002Fgithub.com\u002Fleiting-eric\u002FDailyBrief.git\ncd DailyBrief\nnpm install\n\n# 2. Pick an LLM backend\n#    Default = claude CLI (will guide you through login if not done):\necho \"say hi\" | claude --print --model sonnet\n#    Or use a different backend: cp .env.example .env.local\n#    and set LLM_BACKEND + the matching API key\n\n# 3. Register the scheduler + enable the global skill\nnode scripts\u002Finstall.mjs --global\n\n# 4. Test trigger immediately\n# Windows:  Start-ScheduledTask -TaskName DailyBrief\n# macOS:    launchctl start com.daily-brief\n# Linux:    node scripts\u002Frun-daily.mjs\n```\n\nSleep-wake behavior at next trigger time:\n- **🪟 Windows** — wakes the computer if asleep, runs, returns to sleep\n- **🍎 macOS** — launchd doesn't wake from deep sleep; skipped if asleep (configure `pmset wake schedule` separately if needed)\n- **🐧 Linux** — cron doesn't wake either; skipped if suspended\n\n---\n\n## 🛠️ Daily commands\n\n| Command | Purpose | Time |\n|---|---|---|\n| `npm run daily` | Full pipeline (fetch + LLM + render) | 5-8 min |\n| `npm run dry-run` | Fetch only, no LLM — validates sources | ~30s |\n| `npm run render [date]` | Re-render HTML after editing CSS\u002Flayout | \u003C1s |\n| `npm run regen-trading [date]` | Re-do the trading section only | ~2 min |\n| `npm run regen-enrich \u003Ccat:sub> [date]` | Fill in missing summaries for a subgroup | ~30s |\n| `npm run open` | Open today's report in Chrome | instant |\n| `npm run quota-report` | Per-backend LLM usage summary | instant |\n| `npm run sources` | List all sources with locale \u002F enabled status | instant |\n| `npm run sources:check` | Validate `sources.config.json` schema (good for CI \u002F pre-commit) | instant |\n\n---\n\n## 📊 Source configuration\n\nSources live as a JSON array in [`sources.config.json`](sources.config.json) at the project root — **the single source of truth**. Add \u002F disable \u002F re-categorize feeds without touching TypeScript. Per-entry fields:\n\n| Field | Required | Notes |\n|---|---|---|\n| `id` | ✓ | Short unique identifier (used by `dispatch.ts` to route to the right fetcher) |\n| `name` | ✓ | Display name in the UI |\n| `type` | ✓ | `rss` \u002F `api` \u002F `scrape` |\n| `url` | ✓ | RSS feed URL or API endpoint |\n| `category` | ✓ | `tech` \u002F `finance` \u002F `politics` — drives the L1 tab |\n| `subcategory` |  | L2 grouping (`github-trending` \u002F `ai-news` \u002F `x-viral` \u002F `cn-community` for tech; `news` for finance) |\n| `enabled` |  | Default `true`; set to `false` to skip without deleting the record |\n| `useCurl` |  | `true` if the source's host blocks Node's TLS fingerprint (Cloudflare); the fetcher will shell out to curl |\n| `lang` |  | `zh` means the source is already in Chinese — enrich skips it (no need to translate Chinese into Chinese) |\n| `locales` |  | Array listing which `REPORT_LOCALE` the source appears in. Default `[\"zh\", \"en\"]` |\n| `notes` |  | Free-form (e.g. \"removed because feed died\"); ignored at runtime |\n\n### Adding an RSS source\n\n1. Append an entry to `sources.config.json`\n2. Run `npm run sources:check` to validate schema\n3. `npm run dry-run` to confirm the fetch works\n4. The next `npm run daily` picks it up automatically\n\n### 🌐 Locale mode (zh \u002F en)\n\nSwitch with the `REPORT_LOCALE` environment variable:\n\n```bash\n# .env.local\nREPORT_LOCALE=zh    # default — Chinese mode, includes V2EX \u002F LinuxDo \u002F DW Chinese\n# REPORT_LOCALE=en  # English mode — drops zh-only sources, picks up en-only ones\n```\n\nEach source's `locales` field decides which mode it appears in:\n\n- `[\"zh\"]` — **zh mode only** (V2EX \u002F LinuxDo \u002F DW Chinese). English readers can't read these, so auto-dropped in en mode.\n- `[\"en\"]` — **en mode only** (Hacker News \u002F r\u002Fstocks etc., picked up to replace Chinese community sources). Not shown in zh mode.\n- `[\"zh\", \"en\"]` (default) — appears in both modes (BBC \u002F Bloomberg \u002F WSJ \u002F NYT \u002F AI media)\n\nCurrently enabled sources by locale:\n\n| Locale | Enabled sources | Mix |\n|---|---|---|\n| `zh` | 23 | 20 global \u002F English sources (with LLM-generated Chinese summaries) + 3 Chinese-only (V2EX \u002F LinuxDo \u002F DW Chinese) |\n| `en` | 22 | 20 global \u002F English sources + 2 English community (Hacker News + r\u002Fstocks) |\n\nThe full en-mode switch covers: HTML UI text, the three LLM prompt sets (enrichment \u002F digest \u002F trading commentary), stance labels (Bullish\u002FBearish\u002FNeutral vs. 偏上行\u002F偏下行\u002F中性), date format (`zh-CN` ↔ `en-GB`), Markdown output. **Chinese community sources are auto-hidden in en mode** since the audience can't read them.\n\n---\n\n## 🤖 LLM backend configuration\n\nThe 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`:\n\nCopy `.env.example` to `.env.local` (gitignored), uncomment the section for your chosen backend:\n\n| backend | API key env var | Default model | Base URL |\n|---|---|---|---|\n| 🎯 `claude-cli` (default) | None — reuses Claude Code OAuth | `sonnet` | — |\n| 🟣 `anthropic` | `ANTHROPIC_API_KEY` | `claude-sonnet-4-6` | `api.anthropic.com` |\n| 🟢 `openai` | `OPENAI_API_KEY` | `gpt-4o-mini` | `api.openai.com\u002Fv1` |\n| 🐋 `deepseek` | `DEEPSEEK_API_KEY` | `deepseek-v4-flash` | `api.deepseek.com\u002Fv1` |\n| 🔵 `minimax` | `MINIMAX_API_KEY` | `MiniMax-M2.7` | `api.minimax.io\u002Fv1` \u003Csup>1\u003C\u002Fsup> |\n\n\u003Csup>1\u003C\u002Fsup> Inside mainland China, set `MINIMAX_BASE_URL=https:\u002F\u002Fapi.minimaxi.com\u002Fv1`.\n\n**Universal overrides**:\n- `LLM_MODEL=\u003Cid>` — works for any backend (e.g. `LLM_MODEL=gpt-4o` to use OpenAI's bigger model)\n- `\u003CBACKEND>_BASE_URL` — for self-hosted proxies or OpenAI-compatible services (e.g. LM Studio \u002F Ollama → `LLM_BACKEND=openai` + `OPENAI_BASE_URL=http:\u002F\u002Flocalhost:1234\u002Fv1`)\n- `LLM_API_KEY` \u002F `LLM_BASE_URL` — **generic aliases**, used as fallback when the provider-specific vars aren't set. Use this pair for Moonshot \u002F SiliconFlow \u002F OpenRouter \u002F self-hosted proxies — anything not in the preset list — so you don't have to misuse `OPENAI_API_KEY` just to reach a non-OpenAI service. Example for Moonshot: `LLM_BACKEND=openai` + `LLM_API_KEY=sk-...` + `LLM_BASE_URL=https:\u002F\u002Fapi.moonshot.cn\u002Fv1` + `LLM_MODEL=moonshot-v1-8k`\n\n### How to pick\n\n| Your situation | Recommended backend |\n|---|---|\n| Already using Claude Code (any subscription tier) | `claude-cli` — zero config, billed against your subscription |\n| Not on Claude Code, want it cheapest | `openai` with `gpt-4o-mini`, or `deepseek` (cheaper still) |\n| Chinese summary quality matters most | `anthropic` with `claude-sonnet-4-6` |\n| Need to bypass China's network restrictions | `deepseek` or `minimax` (both are domestic providers) |\n\n**Switching backends needs no code changes**: all prompts are factored out in `lib\u002Fai\u002Fprompts.ts` independently of any backend; JSON repair fallback (`jsonrepair`) is backend-agnostic. After switching, run `npm run daily` once and look at `logs\u002Fllm-calls.jsonl` for the new backend's call log.\n\n---\n\n## 🌐 Self-hosted deployment (optional)\n\nAfter each `npm run daily`, automatically scp the fresh HTML to your own server; visitors hit `https:\u002F\u002Fyour-domain\u002F` and see today's report. **Disabled by default** — leave the env vars unset to skip.\n\n### One-time server setup\n\nAssumes Ubuntu + nginx, domain configured, login user has sudo NOPASSWD:\n\n```bash\n# On the server\nsudo mkdir -p \u002Fvar\u002Fwww\u002Fyour-domain && sudo chown -R www-data:www-data \u002Fvar\u002Fwww\u002Fyour-domain\n```\n\n`\u002Fetc\u002Fnginx\u002Fsites-available\u002Fyour-domain.conf`:\n\n```nginx\nserver {\n    listen 80;\n    server_name your-domain;\n    root \u002Fvar\u002Fwww\u002Fyour-domain;\n    index index.html;\n    location \u002F { try_files $uri $uri\u002F =404; }\n}\n```\n\nEnable + auto-issue SSL:\n\n```bash\nsudo ln -s \u002Fetc\u002Fnginx\u002Fsites-available\u002Fyour-domain.conf \u002Fetc\u002Fnginx\u002Fsites-enabled\u002F\nsudo nginx -t && sudo systemctl reload nginx\nsudo certbot --nginx -d your-domain --agree-tos --redirect\n```\n\n### Enable auto-deploy locally\n\nIn `.env.local` (gitignored), add:\n\n```\nDEPLOY_HOST=user@your-server-ip\nDEPLOY_PATH=\u002Fvar\u002Fwww\u002Fyour-domain\n```\n\nThen:\n- ✅ Every `npm run daily` auto-scp's the new HTML to the server and refreshes `index.html`\n- ✅ `npm run deploy [YYYY-MM-DD]` to push any specific date manually\n- ✅ A deploy failure doesn't break the daily run itself (HTML is already on disk in `daily_reports\u002F`; `npm run deploy` retries it)\n\n---\n\n## 💡 Claude Code integration\n\n**After install, any directory** (no need to `cd` into the project) running Claude Code can use:\n\n| Trigger | Behavior |\n|---|---|\n| `\u002Frun-daily` | Triggers daily immediately, monitors in the background until done. Works from any directory. |\n| `\u002Fcheck-daily` | Checks task state + report files + quota |\n| 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 |\n\n**How it works**: `scripts\u002Finstall.mjs --global` symlinks files in `~\u002F.claude\u002F` pointing at the project's [`.claude\u002Fskills\u002Fdaily-brief\u002FSKILL.md`](.claude\u002Fskills\u002Fdaily-brief\u002FSKILL.md) and [`.claude\u002Fcommands\u002F`](.claude\u002Fcommands\u002F) — **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. `~\u002F.daily-brief-config` records the absolute project path so slash commands find it from any CWD.\n\n---\n\n## 📁 Project structure\n\n```\ndaily-brief\u002F\n├── lib\u002F\n│   ├── sources\u002F        # RSS \u002F API \u002F curl fetchers; add new sources here\n│   ├── ai\u002F             # Pluggable LLM backends + prompts (lib\u002Fai\u002Fbackends\u002F per backend)\n│   ├── trading\u002F        # Yahoo Finance + technical indicators\n│   ├── output\u002F         # Rendering (HTML \u002F Markdown)\n│   └── utils.ts        # Tiny shared helpers (todayKey \u002F getReportTz)\n├── scripts\u002F\n│   ├── _env.ts         # dotenv preload — imported FIRST by every entry script\n│   ├── daily.ts        # Main pipeline\n│   ├── dry-run.ts      # Fetch-only validation (no LLM)\n│   ├── render.ts       # Re-render from cached data\n│   ├── regen-*.ts      # Targeted re-runs (trading \u002F enrich)\n│   ├── quota-report.ts # LLM usage stats\n│   ├── sources.ts      # `npm run sources` — list + validate sources.config.json\n│   ├── run-daily.mjs   # OS scheduler wrapper (daily + log + deploy + open)\n│   ├── open-report.mjs # Cross-platform \"open latest\" helper\n│   ├── build-site.mjs  # GH Pages static-site generator (index + archive)\n│   ├── deploy.mjs      # scp HTML to a remote nginx host (opt-in)\n│   ├── install.mjs     # Cross-platform scheduler registration\n│   └── uninstall.mjs   # Removal\n├── sources.config.json # Single source of truth for the source registry\n├── daily_reports\u002F      # Output (gitignored)\n│   └── 2026-05-15\u002F     # One subdir per day, contains .html (main) \u002F .json (cache) \u002F -articles.json (cache)\n│                       #   .md not generated by default; set OUTPUT_MARKDOWN=true in .env.local to enable\n├── logs\u002F               # Run logs (gitignored)\n├── .github\u002Fworkflows\u002F  # GitHub Actions workflow (path A deployment)\n└── .claude\u002F\n    ├── skills\u002F         # Claude Code operational skill\n    └── commands\u002F       # Slash commands\n```\n\n---\n\n## 🗑️ Uninstall\n\n```bash\nnode scripts\u002Funinstall.mjs\n# Removes: scheduled task (Task Scheduler \u002F launchd \u002F cron) + ~\u002F.claude\u002F symlinks + ~\u002F.daily-brief-config\n# Leaves alone: project files, daily_reports\u002F, logs\u002F, power plan settings\n# For a full cleanup: rm -rf the project directory\n```\n\n---\n\n## 🛠️ Customize \u002F Fork\n\nChange sources, schedule, layout, add new panels — see [FORKING.md](FORKING.md).\n\n---\n\n## 🙏 Acknowledgments\n\nThis project is shared on the [LINUX DO](https:\u002F\u002Flinux.do) open-source community. Thanks to the community members for feedback and suggestions.\n\n## 📝 License\n\nMIT\n","DailyBrief 是一个基于 AI 的每日新闻简报生成工具，它能够聚合来自 23 个数据源的信息，并提供 LLM 生成的中文摘要。项目支持从科技动态、市场行情到时政财经等多个领域的信息整合，特别是针对 21 个股票和加密货币标的提供了实时技术分析与交易点评。其核心功能包括多语言支持（中英双语）、可插拔的 LLM 后端选项以及灵活的部署方式（本地运行或通过 GitHub Actions 自动化）。适合需要定制化且全面覆盖全球资讯和技术分析的专业人士及团队使用，尤其对于关注科技趋势、金融市场动态的用户来说非常实用。",2,"2026-06-11 03:56:56","CREATED_QUERY"]