# DailyBrief **Repository Path**: MitchellSorin/DailyBrief ## Basic Information - **Project Name**: DailyBrief - **Description**: 股票:科技信息总结 - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-05-28 - **Last Updated**: 2026-07-06 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 📰 daily-brief · 10 分钟拥有你自己的 AI 每日简报 **中文** · [English ↓](#en) [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) [![Node 20+](https://img.shields.io/badge/node-20%2B-brightgreen.svg)](https://nodejs.org/) [![TypeScript](https://img.shields.io/badge/TypeScript-5-3178c6.svg)](https://www.typescriptlang.org/) [![LLM: pluggable](https://img.shields.io/badge/LLM-pluggable%20(6%20backends)-orange.svg)](#-llm-后端配置) [![Deploy: GH Actions](https://img.shields.io/badge/deploy-GitHub%20Actions-2088ff.svg)](#a-github-actions--pages零基础设施推荐) [![Demo: live](https://img.shields.io/badge/demo-leiting--eric.github.io%2FDailyBrief-brightgreen.svg)](https://leiting-eric.github.io/DailyBrief) [![Stars](https://img.shields.io/github/stars/leiting-eric/DailyBrief?style=social)](https://github.com/leiting-eric/DailyBrief) > **你的私人 AI 每日简报,跑在你自己掌控的基础设施上。** 默认启用 26 个数据源 · LLM 摘要 · 21 个股票/加密标的**技术指标 + AI 交易点评** · 中英双语 · 6 个 LLM 后端可选。 > > **三种部署任选**:[**🚀 5 分钟 Fork 到 GitHub Actions**](#a-github-actions--pages零基础设施推荐) · [**💻 本地一键装**](#b-本地一键装) · [**🤖 一句话让 AI Agent 帮你装**](#c-给-ai-agent-一句话装)。 **🌐 Live demos** — [📰 leiting-eric.github.io/DailyBrief](https://leiting-eric.github.io/DailyBrief)(A 方式 · GitHub Actions + Pages) · [📰 daily.leiting.tech](https://daily.leiting.tech)(B 方式 · 本地服务器部署) --- ## ✨ 核心特性 - **🌍 全网多源聚合**:默认启用 26 个数据源,覆盖硅谷科技、AI 前沿、全球财经、国际时政、中文社区,一份报告通吃 - **📈 21 个标的实时行情**:美股 / 加密 / 港股 / 商品外汇 / 宏观信号,附 SMA / RSI / MACD 技术指标 + LLM 每日交易点评 - **🤖 6 个 LLM 后端可插拔**:Claude CLI / Anthropic / OpenAI / DeepSeek / MiniMax / Zhipu,一个环境变量切换,不绑死任何家 - **🌐 中英双语**:`REPORT_LOCALE=en` 一切——数据源、prompt、UI 文案、Bullish/Bearish stance 全套切英文 - **🚀 部署灵活**:GitHub Actions(零基础设施)/ 本地系统调度器 / 自托管服务器三选一,互不冲突可并存 - **🆓 数据源零 API key**:所有源走免费公开端点(RSS / 公开 JSON),不需要付费订阅 - **🔒 不绑定第三方阅读服务**:Feedly / Inoreader / Pocket 那些都不沾,你看了什么是你自己的事 - **📁 单文件 HTML**:CSS + JS 全内联,无外部依赖,scp 上服务器直接当首页 --- ## 📚 信源图谱 当前 registry 共 53 个源,默认启用 26 个;按 locale 过滤后 zh 模式有效 24 个、en 模式有效 23 个,分布如下: ### 🧑‍💻 技术动态

技术动态 — GitHub Trending / X 推文 / AI 媒体

- **GitHub Trending** · 热榜每日刷新 - **AI 媒体**(merged):OpenAI / DeepMind / Hugging Face Blog / TLDR AI / Smol AI / Latent Space / MIT Tech Review - **X 推文**(attentionvc-ai):精选 AI 圈大佬动态 ### 📈 市场行情(非新闻源,21 个标的)

市场行情 — 21 ticker 技术指标 + 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 等国际要闻

**BBC / Guardian / NYT / NPR / DW 中文 / Al Jazeera / The Diplomat** — 7 家主流国际媒体的 World 频道 ### 💰 财经要点

财经要点 — Bloomberg / WSJ / FT 全球财经

**Bloomberg / WSJ / FT / BBC Business / Economist** — 5 家全球财经主力 ### 💬 社区讨论 - **zh 模式**:V2EX 热榜 / LinuxDo 热帖 - **en 模式**:Hacker News / Reddit r/stocks(自动替换上面两个) > 完整列表 + 启用状态:`npm run sources` 查看;改源 → 编辑 [`sources.config.json`](sources.config.json)。 --- ## 🚀 三种部署方式选一种 | 方式 | 适合谁 | 你需要 | 几分钟搞定 | |---|---|---|---| | **A. GitHub Actions + Pages** | 没服务器、不想常开电脑 | 一个 API key(Anthropic / OpenAI / DeepSeek / MiniMax / Zhipu 任一) | ~5 分钟(推荐) | | **B. 本地一键装** | 有常开的电脑/服务器、想极致便宜 | Node 20+,可选 Claude Code 登录 | ~3 分钟 | | **C. 给 AI Agent 一句话** | 懒、想让 Cursor / Codex / Claude Code 帮你装 | 同上 | 一句话 | ### A. GitHub Actions + Pages(零基础设施,推荐) 1. **Fork 这个 repo**(GitHub 右上角 Fork 按钮) 2. 进 Fork 的 repo → **Settings → Actions → General → Workflow permissions** 设为 **Read and write permissions** 3. **Settings → Pages → Build and deployment → Source** 选 "Deploy from a branch",分支 `gh-pages` / 路径 `/ (root)`(第一次跑完才会出现 gh-pages 分支,先建 secret 再触发一次即可) 4. **🔑 配置 LLM 后端** —— 这步是关键。每个后端都要**一个 secret + 对应的 `LLM_BACKEND` variable**(不只是 secret),按下表对照填: > ⚠️ **最常踩的坑**:只加了 secret(比如 `DEEPSEEK_API_KEY`)忘了加 `LLM_BACKEND` variable —— workflow 默认 `LLM_BACKEND=anthropic`,所以无论你填的是哪家的 key,运行时都会报 `ANTHROPIC_API_KEY required`。两个都要加。 | 你想用 | Secrets 标签加 | Variables 标签加 `LLM_BACKEND` | 大致成本 | |---|---|---|---| | 🟣 **Anthropic Sonnet**(默认,prompt 按 Sonnet 调优) | `ANTHROPIC_API_KEY` | 不填或填 `anthropic` | ~$0.03-0.05 / 天,月 < $2 | | 🐋 **DeepSeek**(便宜大碗,中文友好) | `DEEPSEEK_API_KEY` | `deepseek` | ~$0.01-0.02 / 天,月 < $1 | | 🟢 **OpenAI** | `OPENAI_API_KEY` | `openai` | gpt-4o-mini ~$0.02 / 天 | | 🔵 **MiniMax** | `MINIMAX_API_KEY` | `minimax` | 类似 DeepSeek 量级 | | 🧠 **Zhipu / 智谱** | `ZHIPU_API_KEY` | `zhipu` | 看服务方定价 | | 🌀 **中转站 / 反代 / 其他 OpenAI 兼容服务**(Moonshot / SiliconFlow / OpenRouter / 自建 Claude 反代 / 本地 Ollama / LM Studio 等) | `LLM_API_KEY` | `openai`(极少数 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`。 5. (可选)同页 Variables 再加: - `LLM_MODEL` —— 覆盖该 backend 的默认模型(不填用 [`.env.example`](.env.example) 里列的默认) - `LLM_BASE_URL` —— 自定义 endpoint。**选了上面"中转站"那行的话必填**;本地 Ollama 填 `http://localhost:11434/v1`、LM Studio 填 `http://localhost:1234/v1` - `REPORT_LOCALE` —— `zh`(默认)或 `en`,控制数据源 + UI + prompt 全套切英文 - `REPORT_TZ` —— IANA 时区名(默认 UTC),例 `Asia/Shanghai` / `America/Los_Angeles`。**同时影响触发时间和日期标签** - `REPORT_HOUR` —— 触发的小时(基于 `REPORT_TZ`),默认 `8`(早 8 点)。逗号分隔可多次触发,如 `8,18` = 早 8 + 晚 6 - `REPORT_DAYS` —— 触发的星期(cron 风格,`0`=周日 ... `6`=周六),默认 `*`(每天)。例 `1-5` = 工作日;`1,3,5` = 周一三五 6. **Actions 标签 → 选 "Daily Brief" workflow → Run workflow** 手动触发一次 跑完后报告在 `https://<你的用户名>.github.io//`。之后**默认每天 `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 模式就意味着**用不了本地 `claude` CLI**——Claude Code 的 OAuth 登录在你本机,GitHub 的服务器看不到。如果你已经在 Max 订阅里,建议两条路并行:本地装(B 方式)用 Claude CLI 跑你自己的服务器版本,GH Actions 用 DeepSeek 跑 Pages 公开版本。两份报告独立,互不影响。 #### 🐛 A 方式常见坑 - **"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 - **自动触发只跑了 `gate`,后面的 build 被 skipped** —— 这是调度门禁在工作,不一定是失败。点开那次 run → `gate` → `Check schedule`,看 `Now in ...`、`Configured REPORT_HOUR=...` 和 `No match — skipping (...)`。最常见原因是没在 **Repository Variables** 里设置 `REPORT_TZ=Asia/Shanghai`,此时默认按 UTC 08:00 触发(北京时间 16:00)。如果变量放在 **Settings → Environments** 里,默认 workflow 也读不到;请移到 **Settings → Secrets and variables → Actions → Variables**,或给 `gate` / `build` 两个 job 都加同一个 `environment: ` - **报错 `ANTHROPIC_API_KEY (or generic LLM_API_KEY) is required`,但我填的是别家的 key** —— 经典 secret/variable 不配对。Workflow 默认 `LLM_BACKEND=anthropic`,光填 `DEEPSEEK_API_KEY` / `OPENAI_API_KEY` 不够,**必须同时去 Variables 标签加 `LLM_BACKEND=deepseek` / `openai`**。从 v1.x 起启动期会直接告诉你哪个 key 已设、应该把 `LLM_BACKEND` 改成什么 - **配齐了 secret + variable 还是报同样的错** —— 99% 是配置放错了作用域。GitHub 上有两个长得几乎一样的页面: - ✅ **Settings → Secrets and variables → Actions**(页眉是 "Repository secrets" / "Repository variables")—— 本项目默认走这里 - ⚠️ **Settings → Environments → \<某个 environment\>**(页眉是 "Environment secrets" / "Environment variables")—— 这里的值**只有当 workflow job 显式声明了 `environment: ` 才会注入**;本项目默认 workflow 没声明,所以放这里运行时拿不到 排查方法:进配置页面看页眉。如果写的是 "Environment ..." 你有**两种修法**任选其一: - **简单做法(推荐)**:把所有 secret **和** variable 从 Environment scope 移到 Repository scope(路径:Settings 左栏 → "Secrets and variables → Actions",注意**不要从 "Environments" 进**),workflow 不用改。**特别提醒**:secret(如 `DEEPSEEK_API_KEY` / `ANTHROPIC_API_KEY`)因为隐藏了值,最容易被忘掉只搬 variable 不搬 secret,导致换了个新错误("key 未设置")。两边都要搬完整 - **保留 Environment 的做法**:编辑 `.github/workflows/daily.yml`,在 `gate` 和 `build` 两个 job 各加一行 `environment: <你的环境名>`(两个都要加,因为 `gate` 也读 `vars.REPORT_TZ` 等变量),名字必须跟 Settings → Environments 里建的一字不差。适合想用 environment 做审批门禁 / branch 限制的人 - **跑了 30 秒就挂(其他情况)** —— LLM API 返 400 / key 没余额 / 配额耗尽。看 step "Generate today's report" 的 log ### B. 本地一键装 ```bash # 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 - ``` 这条命令会自动: 1. 检查 Node / git / claude CLI 是否就位(没装 claude CLI 只发警告,可继续走 API 后端) 2. `git clone` 到 `~/daily-brief`(Windows: `%USERPROFILE%\daily-brief`) 3. `npm install` 4. 注册系统定时(Windows Task Scheduler / macOS launchd / Linux cron,默认 08:00 本地时间) 5. 写 `~/.daily-brief-config` 记录项目路径 6. 在 `~/.claude/` 建符号链接让 Claude Code 的 skill 和 slash command 全局可用 7. 跑一次 `npm run dry-run` 烟测 **Claude Code 集成**:使用 `--global` 安装后,可以在任意目录运行 `/run-daily` 和 `/check-daily`;描述问题时也会加载项目内的 `daily-brief` skill。Cursor / Codex 等其他 agent 不使用这套 skill/command 机制,但不影响系统定时任务运行。手动触发命令: | 平台 | 手动触发 | |---|---| | Windows | `Start-ScheduledTask -TaskName DailyBrief` | | macOS | `launchctl start com.daily-brief` | | Linux | `node scripts/run-daily.mjs`(cron 不支持手动触发) | 自定义路径 / 触发时间: ```bash node bootstrap.mjs --target /custom/path --at 07:30 ``` **LLM 后端**:默认走本地 `claude` CLI(首次需在浏览器登录一次:`echo "hi" | claude --print --model sonnet`,通常会长期有效;如果遇到 401 / 认证失败,重新运行 `claude` 登录)。不用 Claude Code,或想换模型 / 换家 / 接中转,都在 `.env.local` 里改 —— 完整可复制的配置示例见下方 [🤖 LLM 后端配置](#-llm-后端配置)。 ### C. 给 AI Agent 一句话装 无论你用哪个 AI Agent(Claude Code / Cursor / Codex / Continue.dev / OpenClaw 等),把下面这段发给它: > 帮我装这个开源项目,按 README 的"本地一键装"流程跑 bootstrap,完成后告诉我下次自动触发的时间: > https://github.com/leiting-eric/DailyBrief 项目里有 [`AGENTS.md`](AGENTS.md)(通用 agent 协议)+ [`.claude/skills/daily-brief/SKILL.md`](.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(任一平台都支持,定时机制自动适配) --- ## 🔧 手动安装 ```bash # 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 [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`](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 死了"),运行时忽略 | ### 加一个 RSS 源 1. 编辑 `sources.config.json`,追加一条记录 2. 跑 `npm run sources:check` 校验 schema 3. `npm run dry-run` 抓一次验证拉取正常 4. 下次 `npm run daily` 自动包含 ### 🌐 Locale 模式(zh / en) 通过 `REPORT_LOCALE` 环境变量切换: ```bash # .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` | 24 | 21 个全球英文源(附中文摘要)+ 3 个中文专属(V2EX / LinuxDo / DW 中文)| | `en` | 23 | 21 个全球英文源 + 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 后端配置 项目通过 `LLM_BACKEND` 环境变量切换后端。**默认 `claude-cli`** —— 直接复用 Claude Code 已登录的认证,不需要额外配 API key。不用 Claude Code、或想走自己的 API key,按下表切换。 ### 配置文件在哪 **本地安装**:所有 LLM 配置都在仓库根目录的 `.env.local`(被 gitignore 了,安全存 key)。第一次没有这个文件就 `cp .env.example .env.local`,编辑器打开按 backend 解开对应几行即可。改完保存就生效,不用重启 shell —— 这些 TS pipeline 入口(`npm run daily` / `regen-trading` / `regen-enrich`)第一行都会 dotenv 预加载它。 **GitHub Actions 部署**:不需要 `.env.local`,改成在 Settings → Secrets and variables → Actions 里加(见上面 [A 方式](#a-github-actions--pages零基础设施推荐) 第 4 步)。 ### 支持的 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 | | 🧠 `zhipu` | `ZHIPU_API_KEY` | `claude-sonnet-4-6` | `open.bigmodel.cn/api/anthropic` | 1 中国大陆访问设 `MINIMAX_BASE_URL=https://api.minimaxi.com/v1`。 ### 本地 `.env.local` 最小配置示例(任选一组复制走) ```bash # 默认:复用本机 claude CLI(Max 订阅最划算) # 什么都不用填,留空 .env.local 即可 # Anthropic API LLM_BACKEND=anthropic ANTHROPIC_API_KEY=sk-ant-... # LLM_MODEL=claude-sonnet-4-6 # 可选,默认即此 # DeepSeek(便宜,中文友好) LLM_BACKEND=deepseek DEEPSEEK_API_KEY=sk-... # LLM_MODEL=deepseek-v4-flash # 可选 # OpenAI LLM_BACKEND=openai OPENAI_API_KEY=sk-... # LLM_MODEL=gpt-4o-mini # 可选;想用更大模型填 gpt-4o / gpt-4.1 等 # MiniMax LLM_BACKEND=minimax MINIMAX_API_KEY=... # MINIMAX_BASE_URL=https://api.minimaxi.com/v1 # 国内访问用这条 # Zhipu / 智谱(Anthropic 协议兼容) LLM_BACKEND=zhipu ZHIPU_API_KEY=... # ZHIPU_BASE_URL=https://open.bigmodel.cn/api/anthropic # 中转 / 反代 / OpenAI 兼容服务(Moonshot / SiliconFlow / OpenRouter / Ollama / LM Studio …) LLM_BACKEND=openai # 协议是 openai 兼容就填 openai LLM_API_KEY=<中转给的 key> LLM_BASE_URL=https://api.moonshot.cn/v1 LLM_MODEL=moonshot-v1-8k # 中转/自托管必须显式指定 model # Anthropic 协议反代(claude-relay 之类) LLM_BACKEND=anthropic ANTHROPIC_API_KEY=<反代 key> # 也可写成 LLM_API_KEY ANTHROPIC_BASE_URL=https://你的反代/ # 也可写成 LLM_BASE_URL ``` ### 通用覆盖项 - `LLM_MODEL=` —— 任意 backend 的 model 都能用这个变量覆盖默认(如 `LLM_MODEL=gpt-4o` 走 openai 的更大模型) - `_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` 这样的语义错位 ### 验证配置是否对 改完 `.env.local` 不想等 5-8 分钟的完整 daily 跑完才知道 key 通不通?跑这个 ~30 秒、只用 1 次 LLM 调用的小指令: ```bash npm run regen-enrich -- finance:news ``` 通了会看到 `[regen-enrich] enrichment done in 28s, matched 12/12`;不通会在 1 秒内被启动校验拦下并告诉你具体怎么修(哪个 key 没填 / `LLM_BACKEND` 该写啥)。 ### 怎么选 | 你的情况 | 推荐 backend | |---|---| | 已经在用 Claude Code(任意订阅等级)| `claude-cli` — 零配置,按你订阅的等级走 | | 不用 Claude Code,只想低成本跑日报 | `openai` 配 `gpt-4o-mini`、或 `deepseek` 配 `deepseek-v4-flash`(更便宜)| | 中文摘要质量优先,预算可放宽 | `anthropic` 配 `claude-sonnet-4-6` | | 国内网络访问,要规避 GFW | `deepseek`、`minimax` 或 `zhipu`(都是国内厂商)| **切 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: ```bash # 服务器上 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`: ```nginx server { listen 80; server_name your-domain; root /var/www/your-domain; index index.html; location / { try_files $uri $uri/ =404; } } ``` 启用 + 自动签 SSL: ```bash 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 ``` ### 本地启用 auto-deploy `.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` 补推) --- ## 💡 Claude Code 集成 装好后**任意目录**(不必 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/skills/daily-brief/SKILL.md) 和 [`.claude/commands/`](.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 预加载(被 TS pipeline 入口第一个 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 ``` --- ## 🗑️ 卸载 ```bash node scripts/uninstall.mjs # 移除:定时任务 (Task Scheduler / launchd / cron) + ~/.claude/ 下的链接 + ~/.daily-brief-config # 不动:项目文件、daily_reports/、logs/、power plan 设置 # 想彻底清理就 rm -rf 整个项目目录 ``` --- ## 🛠️ 自定义 / Fork 改源、改时间、改排版、加新栏目 —— 见 [FORKING.md](FORKING.md)。 --- ## 🙏 致谢 本项目在 [LINUX DO](https://linux.do) 开源社区分享推广,感谢佬友们的反馈与建议。 ## 📝 License MIT ---
# 📰 daily-brief · your own AI-curated daily news brief in 10 minutes [↑ 中文](#zh) · **English** [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) [![Node 20+](https://img.shields.io/badge/node-20%2B-brightgreen.svg)](https://nodejs.org/) [![TypeScript](https://img.shields.io/badge/TypeScript-5-3178c6.svg)](https://www.typescriptlang.org/) [![LLM: pluggable](https://img.shields.io/badge/LLM-pluggable%20(6%20backends)-orange.svg)](#-llm-backend-configuration) [![Deploy: GH Actions](https://img.shields.io/badge/deploy-GitHub%20Actions-2088ff.svg)](#a-github-actions--pages-zero-infra-recommended) [![Demo: live](https://img.shields.io/badge/demo-leiting--eric.github.io%2FDailyBrief-brightgreen.svg)](https://leiting-eric.github.io/DailyBrief) [![Stars](https://img.shields.io/github/stars/leiting-eric/DailyBrief?style=social)](https://github.com/leiting-eric/DailyBrief) > **Your own AI-curated daily news brief, on infrastructure you control.** 26 sources enabled by default · LLM summaries · 21-ticker market panel with SMA/RSI/MACD signals + AI commentary · bilingual (zh/en) · 6 swappable LLM backends. > > **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). **🌐 Live demos** — [📰 leiting-eric.github.io/DailyBrief](https://leiting-eric.github.io/DailyBrief) (path A · GitHub Actions + Pages) · [📰 daily.leiting.tech](https://daily.leiting.tech) (path B · self-hosted server) --- ## ✨ Core features - **🌍 Multi-source aggregation** — 26 sources enabled by default, 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 - **🤖 6 swappable LLM backends** — Claude CLI / Anthropic / OpenAI / DeepSeek / MiniMax / Zhipu. One env var to switch, no vendor lock-in. - **🌐 Bilingual (zh/en)** — set `REPORT_LOCALE=en` to 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` --- ## 📚 Source roster The registry currently contains 53 sources, with 26 enabled by default. After locale filtering, zh mode uses 24 active sources and en mode uses 23: ### 🧑‍💻 Tech

Tech panel — GitHub Trending / X posts / AI media

- **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 ### 📈 Markets (21 tickers, not news)

Markets — 21-ticker technicals + AI commentary

- **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 ### 🌍 World

World — BBC / Guardian / NYT etc.

**BBC / Guardian / NYT / NPR / DW Chinese / Al Jazeera / The Diplomat** — 7 major international outlets' World feeds ### 💰 Finance

Finance — Bloomberg / WSJ / FT

**Bloomberg / WSJ / FT / BBC Business / Economist** — 5 global finance heavyweights ### 💬 Community - **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, modify [`sources.config.json`](sources.config.json). --- ## 🚀 Pick one deployment path | 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 / Zhipu) | ~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 | ### A. GitHub Actions + Pages (zero-infra, recommended) 1. **Fork this repo** (Fork button, top-right of GitHub) 2. **Settings → Actions → General → Workflow permissions** → set to **Read and write permissions** 3. **Settings → Pages → Build and deployment → Source** → "Deploy from a branch" → branch `gh-pages` / path `/ (root)` (the `gh-pages` branch only exists after the first successful workflow run — configure secrets first, trigger once, then come back) 4. **🔑 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: > ⚠️ **Most common mistake**: adding only the secret (e.g. `DEEPSEEK_API_KEY`) and forgetting the `LLM_BACKEND` variable. The workflow defaults `LLM_BACKEND` to `anthropic`, so whatever key you set, the run will crash with `ANTHROPIC_API_KEY required`. Both pieces are required. | You want | Secret to add | `LLM_BACKEND` variable | Rough cost | |---|---|---|---| | 🟣 **Anthropic Sonnet** (default; prompts tuned for it) | `ANTHROPIC_API_KEY` | leave unset or `anthropic` | ~$0.03-0.05/day, <$2/month | | 🐋 **DeepSeek** (cheap, China-friendly) | `DEEPSEEK_API_KEY` | `deepseek` | ~$0.01-0.02/day, <$1/month | | 🟢 **OpenAI** | `OPENAI_API_KEY` | `openai` | gpt-4o-mini ~$0.02/day | | 🔵 **MiniMax** | `MINIMAX_API_KEY` | `minimax` | Similar to DeepSeek | | 🧠 **Zhipu** | `ZHIPU_API_KEY` | `zhipu` | Depends on provider | | 🌀 **Proxy / aggregator / any OpenAI-compatible service** (Moonshot, SiliconFlow, OpenRouter, self-hosted Claude relay, local Ollama / LM Studio, ...) | `LLM_API_KEY` | `openai` (use `anthropic` only 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`) 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. 5. (Optional) On the same Variables tab, add: - `LLM_MODEL` — override the backend's default model (otherwise uses the default listed in [`.env.example`](.env.example)) - `LLM_BASE_URL` — custom endpoint. **Required if you picked the "proxy" row above.** For local Ollama use `http://localhost:11434/v1`, LM Studio `http://localhost:1234/v1` - `REPORT_LOCALE` — `zh` (default) or `en` — switches sources + UI + LLM prompts as a set - `REPORT_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 in `REPORT_TZ`, default `8` (08:00). Comma-separated for multiple, e.g. `8,18` = 8 AM and 6 PM - `REPORT_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 6. **Actions tab → "Daily Brief" workflow → Run workflow** to trigger manually for the first time Once the workflow turns green, your report lives at `https://.github.io//`. 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 probes **twice per hour** and uses a `gate` job to check if the current hour in `REPORT_TZ` matches `REPORT_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 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. #### 🐛 Common gotchas - **"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_BACKEND` got 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"). - **Scheduled runs only execute `gate`, then build is skipped** — this is the schedule gate doing its job, not necessarily a failure. Open that run → `gate` → `Check schedule`, then inspect `Now in ...`, `Configured REPORT_HOUR=...`, and `No match — skipping (...)`. The most common cause is missing `REPORT_TZ=Asia/Shanghai` in **Repository Variables**, so the default is UTC 08:00 (16:00 in Beijing). Variables stored under **Settings → Environments** are invisible to the default workflow; move them to **Settings → Secrets and variables → Actions → Variables**, or add the same `environment: ` to both the `gate` and `build` jobs. - **Error: `ANTHROPIC_API_KEY (or generic LLM_API_KEY) is required` — but I set a different provider's key** — classic secret-without-variable mismatch. The workflow defaults `LLM_BACKEND=anthropic`; setting `DEEPSEEK_API_KEY` / `OPENAI_API_KEY` alone is not enough — you **also need to add the matching `LLM_BACKEND=deepseek` / `openai` under Variables**. As of v1.x the startup check prints exactly which key it found and which `LLM_BACKEND` value to set. - **I added both the secret and variable, still the same error** — 99% sure your values went into the wrong scope. GitHub has two near-identical-looking pages: - ✅ **Settings → Secrets and variables → Actions** (header reads "Repository secrets" / "Repository variables") — this is the default this project uses - ⚠️ **Settings → Environments → \** (header reads "Environment secrets" / "Environment variables") — values here are **only injected when a workflow job declares `environment: `**; the default workflow doesn't, so anything stored here is invisible at runtime Open your config page and check the section header. If it says "Environment ..." you have **two fixes**, pick one: - **Simple fix (recommended)**: move every secret **and** variable from Environment scope to Repository scope (Settings sidebar → "Secrets and variables → Actions" — **don't enter via "Environments"**). No workflow edit needed. **Watch out**: secrets like `DEEPSEEK_API_KEY` / `ANTHROPIC_API_KEY` hide their values, so it's easy to "move" only the visible variables and forget the secrets — then you trade the old error for a new one ("key is not set"). Move both, completely. - **Keep-the-environment fix**: edit `.github/workflows/daily.yml` and add `environment: ` to BOTH the `gate` and `build` jobs (both, because `gate` reads `vars.REPORT_TZ` etc. too). The name must exactly match the environment you created under Settings → Environments. Useful if you want environment-based approval gates or branch restrictions. - **Fails after ~30s (other cases)** — LLM API returned 400, the key has no credit, or quota is exhausted. Check the "Generate today's report" step's log. ### B. Local one-liner install ```bash # 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: 1. 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) 2. `git clone` to `~/daily-brief` (Windows: `%USERPROFILE%\daily-brief`) 3. `npm install` 4. Register the OS scheduler (Windows Task Scheduler / macOS launchd / Linux cron, default 08:00 local time) 5. Write `~/.daily-brief-config` recording the project path 6. Symlink the Claude Code skill + slash commands into `~/.claude/` so they work from any directory 7. Run `npm run dry-run` as a smoke test **Claude Code integration**: after installing with `--global`, Claude Code can use `/run-daily` and `/check-daily` from any directory, and plain-language troubleshooting prompts can load the bundled `daily-brief` skill. Other agents such as Cursor and Codex do not use this skill/command mechanism, but the OS scheduled task still works. 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: ```bash node bootstrap.mjs --target /custom/path --at 07:30 ``` **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`; it usually stays valid for a long time, but rerun `claude` login if you see 401 / authentication failures). To skip Claude Code, change the model, switch providers, or point at a proxy — edit `.env.local`. Copy-paste recipes are in [🤖 LLM backend configuration](#-llm-backend-configuration) below. ### C. Have an AI agent install it for you 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`](AGENTS.md) (universal agent protocol) and [`.claude/skills/daily-brief/SKILL.md`](.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". --- ## 📋 Requirements - **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) --- ## 🔧 Manual install ```bash # 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.mjs ``` Sleep-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 schedule` separately if needed) - **🐧 Linux** — cron doesn't wake either; skipped if suspended --- ## 🛠️ Daily commands | 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 [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 | --- ## 📊 Source configuration Sources live as a JSON array in [`sources.config.json`](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 | ### Adding an RSS source 1. Append an entry to `sources.config.json` 2. Run `npm run sources:check` to validate schema 3. `npm run dry-run` to confirm the fetch works 4. The next `npm run daily` picks it up automatically ### 🌐 Locale mode (zh / en) Switch with the `REPORT_LOCALE` environment variable: ```bash # .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 ones ``` Each 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` | 24 | 21 global / English sources (with LLM-generated Chinese summaries) + 3 Chinese-only (V2EX / LinuxDo / DW Chinese) | | `en` | 23 | 21 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 filtered out in en mode and replaced by en-only community sources** such as Hacker News and r/stocks. --- ## 🤖 LLM backend configuration 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`: ### Where the config lives **Local install**: everything goes in `.env.local` at the repo root (gitignored — safe place for keys). First time? `cp .env.example .env.local`, open it in your editor, uncomment the rows you need. Changes take effect on next run, no shell restart — these TS pipeline entry scripts (`npm run daily` / `regen-trading` / `regen-enrich`) dotenv-load the file on their first line. **GitHub Actions deploy**: no `.env.local` needed — set values under Settings → Secrets and variables → Actions instead (see [Path A](#a-github-actions--pages-zero-infra-recommended) step 4 above). ### Supported backends | 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 | | 🧠 `zhipu` | `ZHIPU_API_KEY` | `claude-sonnet-4-6` | `open.bigmodel.cn/api/anthropic` | 1 Inside mainland China, set `MINIMAX_BASE_URL=https://api.minimaxi.com/v1`. ### Minimal `.env.local` recipes (pick one and copy) ```bash # Default: reuse local claude CLI (best deal if you have a Max subscription) # Leave .env.local empty — done. # Anthropic API LLM_BACKEND=anthropic ANTHROPIC_API_KEY=sk-ant-... # LLM_MODEL=claude-sonnet-4-6 # optional — this is the default # DeepSeek (cheap, strong on Chinese) LLM_BACKEND=deepseek DEEPSEEK_API_KEY=sk-... # LLM_MODEL=deepseek-v4-flash # optional # OpenAI LLM_BACKEND=openai OPENAI_API_KEY=sk-... # LLM_MODEL=gpt-4o-mini # optional; use gpt-4o / gpt-4.1 for bigger # MiniMax LLM_BACKEND=minimax MINIMAX_API_KEY=... # MINIMAX_BASE_URL=https://api.minimaxi.com/v1 # use this from mainland China # Zhipu (Anthropic-compatible protocol) LLM_BACKEND=zhipu ZHIPU_API_KEY=... # ZHIPU_BASE_URL=https://open.bigmodel.cn/api/anthropic # Proxy / aggregator / OpenAI-compatible service (Moonshot / SiliconFlow / OpenRouter / Ollama / LM Studio …) LLM_BACKEND=openai # most proxies speak OpenAI protocol LLM_API_KEY= LLM_BASE_URL=https://api.moonshot.cn/v1 LLM_MODEL=moonshot-v1-8k # always explicit for proxies / self-hosted # Anthropic-protocol relay (claude-relay etc.) LLM_BACKEND=anthropic ANTHROPIC_API_KEY= # or use LLM_API_KEY ANTHROPIC_BASE_URL=https://your-relay/ # or use LLM_BASE_URL ``` ### Universal overrides - `LLM_MODEL=` — works for any backend (e.g. `LLM_MODEL=gpt-4o` to use OpenAI's bigger model) - `_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 misuse `OPENAI_API_KEY` just to reach a non-OpenAI service ### Verify the config is good Don't want to wait 5-8 min for a full `npm run daily` just to learn your key is wrong? This 30-second, 1-LLM-call command tells you fast: ```bash npm run regen-enrich -- finance:news ``` Working: `[regen-enrich] enrichment done in 28s, matched 12/12`. Broken: the startup check stops it within 1 second and prints exactly what to fix (which key is missing, which `LLM_BACKEND` value to use). ### How to pick | 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`, `minimax`, or `zhipu` (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. --- ## 🌐 Self-hosted deployment (optional) 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. ### One-time server setup Assumes Ubuntu + nginx, domain configured, login user has sudo NOPASSWD: ```bash # 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`: ```nginx 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: ```bash 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 ``` ### Enable auto-deploy locally In `.env.local` (gitignored), add: ``` DEPLOY_HOST=user@your-server-ip DEPLOY_PATH=/var/www/your-domain ``` Then: - ✅ Every `npm run daily` auto-scp's the new HTML to the server and refreshes `index.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 deploy` retries it) --- ## 💡 Claude Code integration **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`](.claude/skills/daily-brief/SKILL.md) and [`.claude/commands/`](.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. --- ## 📁 Project structure ``` 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 TS pipeline entry scripts │ ├── 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 ``` --- ## 🗑️ Uninstall ```bash 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 directory ``` --- ## 🛠️ Customize / Fork Change sources, schedule, layout, add new panels — see [FORKING.md](FORKING.md). --- ## 🙏 Acknowledgments This project is shared on the [LINUX DO](https://linux.do) open-source community. Thanks to the community members for feedback and suggestions. ## 📝 License MIT