内容
<div align="center">
<img src=".github/assets/banner.png" alt="Vitals — 您的所有可穿戴设备,一览无遗" width="100%">
一个自我托管、注重隐私的个人健康指挥中心。Vitals 将您拥有的所有可穿戴设备统一到一个每日**健康状态**的读取中,为您提供一个**AI 助手**,并显示一个您完全拥有的仪表盘 —— 您的本地 SQLite 数据库中的数据,永远不会与供应商云端接触。
[](LICENSE)




<br>
**新来者?** 只需 5 分钟即可从零开始,体验一个完整的演示仪表盘 —— 无需账号,无需 OAuth。参见 **[docs/QUICKSTART.md](docs/QUICKSTART.md)**。
<br>
<img src="site/assets/screenshot-desktop.png" alt="Vitals 仪表盘(浅色主题)" width="860">
<br><br>
<table>
<tr>
<td align="center"><img src="site/assets/screenshot-dark.png" alt="Soft Midnight 黑暗主题" width="380"><br><sub>Soft Midnight 主题</sub></td>
<td align="center"><img src="site/assets/screenshot-mobile.png" alt="可安装的 PWA 在移动设备上" width="180"><br><sub>可安装的 PWA</sub></td>
</tr>
</table>
</div>
---
## 为什么选择 Vitals
每个可穿戴设备都想成为您的全部画面,但每个设备都生活在自己的封闭式应用中。Vitals 持相反的立场:将来自所有设备的数字提取出来,协调成一个值得信赖的每日健康状态,并将所有内容保留在一个您可控的盒子里。无需订阅,无需数据代理,无需锁定。
## 功能
- **统一您的可穿戴设备** —— 将 Fitbit/Pixel、Oura、WHOOP 和 Apple Health 等设备的数据拉取到一个标准化的时间线中。多个设备测量相同的指标,将被协调成一个加权**共识**,并具有每个指标的**置信度**。
- **每日健康状态** —— 一个友好的读取,显示您今天的恢复情况,基于心率变异性(HRV)、静息心率、睡眠和皮肤温度趋势与您的基线相比。
- **通过 Strava 运行和锻炼** —— 连接 Strava(实时 OAuth)以同步 Apple Watch / 手机运行到您的时间线中。点击任何运行以查看一个包含分段、圈数、区间和重建的跑步/步行**区间的详细模态。
- **每日 AI 简报** —— 一个简短、具体的早晨简报,直接在仪表盘上呈现,分析您的恢复、趋势和最近的运行(甚至每英里区间速度)。切换它开启/关闭,自动生成,或随时点击**重新生成**。另外,还有一个自由形式的**提问**框,用于一次性问题。
- **自带 AI** —— 简报和提问通过一个可配置的提供者链运行。使用云模型或完全**本地化**,使用 Ollama / 任何 OpenAI 兼容的服务器 —— 无需 API 密钥,数据不会离开机器。
- **宁静的 PWA 仪表盘** —— 一个使用 React + Vite 的渐进式网页应用,具有浅色和深色主题,可在手机和桌面上安装。
- **从 Claude 查询** —— 一个内置的 **MCP 服务器** 允许您从 claude.ai 或 Claude Desktop 提问您自己的健康数据(只读公共表面)。
- **完全自我托管和私有** —— 您的数据存储在您自己的机器上的本地 SQLite 文件中(`DB_PATH`)。远程访问是您自己的私有 tailnet,而不是 SaaS。
## 支持的来源
Vitals 有两种来源。**共识设备** 提供每日生命体征 + 睡眠,这些数据将被协调成一个加权的读取。**活动来源** 只提供锻炼数据 —— 它们会填充时间线,但不会参与共识。
| 来源 | 类型 | 连接方式 |
|---|---|---|
| **Fitbit / Pixel / Google Health** | 共识(生命体征 + 睡眠) | Google Health API "桥"(OAuth)—— 还聚合其他同步到它的设备 |
| **Apple Health** | 共识(生命体征 + 睡眠) | [Health Auto Export](https://www.healthexportapp.com/) iOS 应用 → REST 导入 (`POST /api/ingest/apple`) |
| **Oura** | 共识(生命体征 + 睡眠) | Oura 个人访问令牌(无 OAuth) |
| **WHOOP** | 共识(生命体征 + 睡眠) | WHOOP OAuth |
| **Strava** | 活动(仅锻炼) | Strava OAuth —— 将跑步同步到 `workouts` 表格中,包含分段/圈数/区间和重建的跑步/步行区间 |
| **Garmin & 其他** | 共识 | 通过相同的适配器模式,由社区扩展 |
对于共识设备,一个设备**要么**通过 Google Health 桥接,**要么**通过其原生适配器获取 —— 永远不会同时使用两者,以免重复计算。桥接拥有的设备由 `GOOGLE_HEALTH_SOURCES` 设置。参见 **[docs/ADAPTERS.md](docs/ADAPTERS.md)**。
## 快速开始
最快的路径是**先播种**:加载 90 天的模拟演示数据,体验一个完整的仪表盘,无需账号和 OAuth。
```bash
# 1. 克隆 + 配置
git clone https://github.com/8tp/Vitals-Command-Center.git
cd vitals-command-center
cp .env.example .env
# 2. 安装(需要 Node ≥ 20 和 C 工具链 —— 参见下面的先决条件)
npm install
# 3. 播种 90 天的演示数据 —— 无需账号
npm run db:seed
# 4. 运行(API + 仪表盘 + MCP 服务器一起)
npm run dev
# → 打开 http://localhost:5173 以查看实时、已填充的演示仪表盘
```
**先决条件:** Node.js ≥ 20 **和** C 工具链(`better-sqlite3` 编译本地模块)。在 macOS 上:`xcode-select --install`。在 Debian/Ubuntu 上:`sudo apt install build-essential python3`。缺少工具链是最常见的安装失败原因。
**连接真实来源** (当您准备好时):
```bash
# - Oura: 设置 OURA_PAT (个人访问令牌,无 OAuth —— 最容易的第一个来源)
# - Strava: 设置 STRAVA_CLIENT_ID/SECRET,然后访问 /api/auth/strava/authorize (跑步 + 锻炼)
# - Fitbit/Google: 设置 GOOGLE_CLIENT_ID/SECRET,然后访问 /api/auth/google/authorize
# - WHOOP: 设置 WHOOP_CLIENT_ID/SECRET,然后访问 /api/auth/whoop/authorize
# - Apple Health: 设置 APPLE_INGEST_SECRET 并将 Health Auto Export 指向 /api/ingest/apple
```
然后使用 `npm run sync:manual` (或 `POST /api/sync`)触发拉取。
默认情况下,API 在 `http://localhost:3001` 上提供服务,仪表盘(Vite)在 `http://localhost:5173` 上提供服务,MCP HTTP 服务器在 `:8787` 上提供服务。
**配置亮点:** `GOOGLE_HEALTH_SOURCES` (桥接 vs. 原生路由),`AI_PROVIDERS` (提供者链),`DB_PATH`,`VITE_USER_NAME` (仪表盘问候语),以及 `VITE_ALLOWED_HOSTS` (开发服务器接受的额外主机名,在反向代理后面)。
> 完整的演练 —— 演示数据,您的第一个真实来源,以及开启 AI 简报 —— 在 **[docs/QUICKSTART.md](docs/QUICKSTART.md)** 中。
## 架构概览
```
可穿戴设备 ──▶ 同步/标准化 ──▶ SQLite ──┬──▶ REST API ──▶ PWA 仪表盘
(桥接 + (共识 ├──▶ MCP 服务器 ─▶ claude.ai / Desktop
原生适配器) + 置信度)└──▶ AI 简报 (提供者链)
```
Vitals 是一个 TypeScript **npm 工作区 monorepo**:
| 工作区 | 责任 |
|---|---|
| `apps/api` | Fastify REST API,提供内置仪表盘,运行同步 + 简报作业 |
| `apps/web` | React + Vite PWA 仪表盘(浅色/深色) |
| `apps/mcp-server` | 模型上下文协议服务器(stdio + Streamable HTTP) |
| `packages/db` | SQLite 连接,迁移和查询 |
| `packages/shared` | 共享类型,设备定义和 Zod 模式 |
完整的设计在 **[docs/ARCHITECTURE.md](docs/ARCHITECTURE.md)** 中。
## 文档
| 文档 | 内容 |
|---|---|
| [docs/QUICKSTART.md](docs/QUICKSTART.md) | 5 分钟路径:演示数据 → 第一个真实来源 → AI 简报 |
| [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) | 系统设计,数据流,共识模型,部署拓扑 |
| [docs/API.md](docs/API.md) | REST API 参考(`/api/*` 路由 + 响应信封) |
| [docs/SELF_HOSTING.md](docs/SELF_HOSTING.md) | 在始终在线的盒子上运行 Vitals,launchd 作业,备份,Tailscale |
| [docs/CONFIGURATION.md](docs/CONFIGURATION.md) | 每个环境变量的解释 |
| [docs/ADAPTERS.md](docs/ADAPTERS.md) | 桥接 vs. 原生来源;编写新的设备适配器 |
| [docs/AI.md](docs/AI.md) | AI 提供者链(云 + 本地)和简报生成 |
| [docs/MCP.md](docs/MCP.md) | 将 Vitals 连接到 claude.ai / Claude Desktop |
## 隐私
- **您的数据保持本地。** 所有健康数据存储在您自己的机器上的 SQLite 数据库中(`DB_PATH`)。不会将任何数据发送到 Vitals 云 —— 它不存在。
- **您选择什么(如果有)离开盒子。** 原生同步仅与您连接的设备供应商 API 对话。AI 层按提供者选择:设置 `AI_PROVIDERS=ollama` (或 `openai-compat`)以使用完全本地化的推理,**零** 云调用。云 AI 提供者(`claude`,`codex`)仅在您启用它们时运行云模型。
- **远程 AI 接口仅只读。** 当 MCP 服务器暴露给 claude.ai 时,它以只读方式打开数据库,并丢弃所有写入工具。
- **默认环回。** 公共 MCP 服务器绑定 `127.0.0.1`,并且只能通过您自己的 Tailscale Funnel 访问,位于身份验证门后面。
## 自我托管
Vitals 设计用于在始终在线的盒子上运行(Mac mini,NUC,家庭服务器),在进程监视器下(例如 `launchd`):调度同步,每日简报,API + 仪表盘,MCP 服务器和夜间数据库备份。远程访问通过 **Tailscale Serve** (仪表盘,尾网私有)和 **Tailscale Funnel** (MCP,claude.ai 公共)进行。参见 **[docs/SELF_HOSTING.md](docs/SELF_HOSTING.md)**。
## 配置
所有内容都由环境变量驱动 —— 复制 `.env.example` 到 `.env` 并填写您使用的内容。亮点:`GOOGLE_HEALTH_SOURCES` (桥接 vs. 原生路由),`AI_PROVIDERS` (提供者链),`DB_PATH`,每个来源的凭证,以及同步/简报的 cron 时间表。完整的参考资料在 **[docs/CONFIGURATION.md](docs/CONFIGURATION.md)** 中。
## 贡献
欢迎贡献 —— 尤其是新的设备适配器。最干净的起点是 `apps/api/src/services/` 中的适配器模式 + `packages/shared/src/devices.ts` 中的共享设备定义。请在打开 PR 之前运行 `npm run typecheck` 和 `npm run lint`。有关适配器合约,请参见 [docs/ADAPTERS.md](docs/ADAPTERS.md)。
## 许可证
MIT © 8tp. 参见 [LICENSE](LICENSE)。
<sub>社交预览:`site/assets/og-image.png`</sub>