npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

health-relay-server

v1.0.12

Published

HealthClaw WebSocket Relay Server

Readme

health-relay-server 技术文档

中继服务端 - WebSocket 消息路由


1. 项目信息

| 属性 | 值 | |------|-----| | 项目名 | health-relay-server | | 语言 | Node.js + TypeScript | | 框架 | ws (WebSocket) | | 部署 | 云服务器 (119.45.24.29) | | 端口 | 5201 | | 进程管理 | PM2 |


2. 核心职责

  1. WebSocket 中继:接收 Mac mini 和 iOS 的 WebSocket 连接
  2. 消息路由:在 Mac mini 和 iOS 之间转发消息
  3. 认证管理:验证连接密码 (accessCode)
  4. 连接管理:维护连接状态,断线清理
  5. ⚠️ 不存储任何数据:纯转发

3. 项目结构

health-relay-server/
├── src/
│   ├── index.ts              # 入口
│   ├── server.ts             # WebSocket 服务
│   ├── router.ts             # 消息路由
│   ├── auth.ts               # 认证
│   ├── types.ts              # 类型定义
│   └── utils/
│       └── logger.ts         # 日志
├── package.json
├── tsconfig.json
├── ecosystem.config.js       # PM2 配置
└── README.md

4. 协议设计

4.1 连接 URL

ws://119.45.24.29:5201/relay/{appId}?secret={accessCode}

4.2 appId 命名规则

| 端 | appId 格式 | 示例 | |---|-----------|------| | Mac mini | health-mac-{hostname} | health-mac-LeoMac-mini | | iOS | health-ios-{deviceId} | health-ios-ABC123 |

4.3 预共享密钥

health-secret-code-2024

5. 消息格式

5.1 基础消息

interface BaseMessage {
  type: 'req' | 'res' | 'event' | 'ping' | 'pong';
  id?: string;
  timestamp: number;
}

5.2 请求消息 (iOS → Mac)

interface RequestMessage extends BaseMessage {
  type: 'req';
  method: string;       // 'openclaw.status' | 'health.latest' | ...
  params?: Record<string, any>;
}

5.3 响应消息 (Mac → iOS)

interface ResponseMessage extends BaseMessage {
  type: 'res';
  ok: boolean;
  data?: any;
  error?: { message: string };
}

5.4 事件消息 (Mac → iOS)

interface EventMessage extends BaseMessage {
  type: 'event';
  event: string;        // 'data.update' | 'health.update' | ...
  payload?: any;
}

6. 路由流程

┌─────────────────────────────────────────────────────────────────┐
│                     health-relay-server                         │
│                                                                  │
│  1. Mac mini 连接                                                 │
│     ws://.../relay/health-mac-LeoMac-mini?secret=xxx           │
│     → server 注册: { 'health-mac-LeoMac-mini' → ws1 }          │
│                                                                  │
│  2. iOS 连接                                                      │
│     ws://.../relay/health-ios-ABC123?secret=xxx                │
│     → server 注册: { 'health-ios-ABC123' → ws2 }               │
│                                                                  │
│  3. iOS 发送请求 → server 查找配对的 mac → 转发                   │
│                                                                  │
│  4. Mac 处理 → 发送响应 → server 转发给 iOS                     │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

6.1 配对规则

  • iOS 连接带有 health-ios-* 前缀
  • Mac 连接带有 health-mac-* 前缀
  • 同一 session 下,server 自动路由它们之间的消息

7. 核心代码模块

7.1 auth.ts - 认证模块

// 验证 accessCode
function validateAccessCode(secret: string): boolean {
  const validCodes = process.env.ACCESS_CODES?.split(',') || [];
  return validCodes.includes(secret);
}

// 从 URL 解析 appId 和 secret
function parseConnectionParams(url: string, query: Record<string, string>): {
  appId: string;
  secret: string;
  error?: string;
}

7.2 router.ts - 路由模块

// 连接注册
function registerConnection(appId: string, ws: WebSocket): void;

// 消息转发
function routeMessage(fromAppId: string, message: BaseMessage): void;

// 配对查找(通过 appId 前缀匹配)
function findPairedDevice(appId: string): WebSocket | null;

// 断开清理
function unregisterConnection(appId: string): void;

7.3 server.ts - WebSocket 服务

import { WebSocketServer, WebSocket } from 'ws';

const wss = new WebSocketServer({ port: Number(process.env.PORT) || 5201 });

wss.on('connection', (ws, req) => {
  // 1. 解析 URL 和 query
  const { appId, secret, error } = parseConnectionParams(req.url, query);
  
  // 2. 认证
  if (!validateAccessCode(secret)) {
    ws.close(4001, 'Unauthorized');
    return;
  }
  
  // 3. 注册连接
  registerConnection(appId, ws);
  
  // 4. 监听消息并路由
  ws.on('message', (data) => {
    const msg = JSON.parse(data.toString());
    routeMessage(appId, msg);
  });
  
  // 5. 断开清理
  ws.on('close', () => unregisterConnection(appId));
});

8. 环境变量

# .env
PORT=5201
ACCESS_CODES=health-secret-code-2024
LOG_LEVEL=info
MAX_CONNECTIONS=100
PING_INTERVAL=30000
PING_TIMEOUT=60000

9. PM2 部署

9.1 ecosystem.config.js

module.exports = {
  apps: [{
    name: 'health-relay-server',
    script: './dist/index.js',
    instances: 1,
    autorestart: true,
    watch: false,
    max_memory_restart: '200M',
    env: {
      NODE_ENV: 'production',
      PORT: 5201,
      ACCESS_CODES: 'health-secret-code-2024',
      LOG_LEVEL: 'info'
    }
  }]
};

9.2 部署命令

# 在云服务器上
cd /opt/health-relay-server
npm install
npm run build
pm2 start ecosystem.config.js
pm2 save
pm2 startup  # 开机自启

10. 依赖

{
  "dependencies": {
    "ws": "^8.18.0"
  },
  "devDependencies": {
    "@types/node": "^22.0.0",
    "@types/ws": "^8.5.12",
    "tsx": "^4.19.2",
    "typescript": "^5.9.3"
  }
}

11. 注意事项

  1. 不存储数据:server 是纯中继,不做持久化
  2. 断线重连:client 侧负责重连机制
  3. 心跳保活:client 每 30 秒发 ping
  4. 版本协商:未来可在连接时交换版本号