RFC: Remote Daemon Observability — HTTP API + CLI Remote + Web Dashboard #133

Closed
opened 2026-04-25 04:59:25 +00:00 by xingyue · 2 comments
Owner

Summary

Nerve daemon 目前只通过 Unix socket 暴露 IPC,只能本机查询。需要一种机制让主人在任意设备上查看各 agent 的 daemon 状态。

Motivation

  • 多台设备(鹿鸣、团子、小橘等)各自跑 nerve daemon,无法统一监控
  • 当前 CLI 只能连本地 Unix socket,远程设备只能 SSH 上去手动看
  • 缺少可视化界面,排查问题效率低

Current State

  • Daemon IPC: Unix domain socket, newline-delimited JSON
  • 支持的命令: trigger-workflow, trigger-sense, list-senses, kill-workflow
  • CLI client: packages/cli/src/daemon-client.ts, 通过 node:net connect 本地 socket
  • 缺少的信息: 无法查询 workflow 运行状态、活跃线程列表、daemon 版本/uptime

Proposal

Phase 1: HTTP API + nerve workflow list

给 daemon 加可选的 HTTP server,把现有 IPC 能力包一层 REST:

配置(yaml 优先,CLI override):

# nerve.yaml
api:
  port: 9800        # 不配则不启动 HTTP
  token: "secret"   # 可选,Phase 2 必填
# CLI override
nerve start --port 9800

API endpoints:

GET  /api/health              → { ok, version, uptime, startedAt, hostname }
GET  /api/senses              → { senses: SenseInfo[] }
GET  /api/workflows           → { workflows: WorkflowStatus[] }
POST /api/trigger-sense       → { ok } | { ok, error }
POST /api/trigger-workflow    → { ok } | { ok, error }
POST /api/kill-workflow       → { ok } | { ok, error }

WorkflowStatus (新增):

type WorkflowStatus = {
  name: string;
  activeThreads: number;
  queuedThreads: number;
  config: { concurrency: number; overflow: string };
};

同时新增 nerve workflow list CLI 命令,Phase 1 走 IPC,Phase 2 接 HTTP transport。

实现要点:

  • 复用 daemon-ipc.ts 的 handler 逻辑,HTTP 层只做 JSON ↔ request 转换
  • /api/health 包含 hostnameos.hostname()),多设备场景区分
  • 不配 api.port 则不启动 HTTP(向后兼容)
  • 协议定义放 @uncaged/nerve-core(和现有 IPC types 同层)
  • 定义 transport interface,为 Phase 2 做准备

Phase 2: CLI Remote — nerve --host <addr>

CLI 侧加 --host 全局 flag + bearer token auth:

# 本地(默认,走 Unix socket,行为不变)
nerve sense list

# 远程(走 HTTP + auth)
nerve --host 192.168.2.58:9800 --api-token secret sense list
nerve --host luming:9800 workflow list
nerve --host tuanzi:9800 sense trigger my-sense

# 新增
nerve status                                    # 本地 daemon health
nerve --host luming:9800 status                 # 远程 daemon health

实现要点:

  • daemon-client.ts 基于 Phase 1 定义的 transport interface 实现 HttpTransport
  • --host 存在时用 HttpTransport(带 Authorization: Bearer <token>),否则用现有 UnixTransport
  • 远端 daemon 的 api.token 必须匹配,否则 401

Phase 3: Web Dashboard(可选)

在 Phase 1 的 HTTP server 上挂一个内嵌静态页面:

GET / → 单页 HTML dashboard
  • 纯 HTML + vanilla JS,不引入前端框架
  • 展示:daemon 运行状态(含 hostname)、sense 列表及最后触发时间、workflow 活跃线程
  • 自动轮询(5s interval)或后续加 SSE
  • 单文件内嵌到 daemon 包里,零构建步骤

Implementation Plan

Phase Scope Package Est. Files
1 HTTP API + transport interface + workflow list core (types) + daemon (server) + cli (command) 4-5
2 CLI --host + auth + status command cli + daemon (auth middleware) 3-4
3 Embedded web dashboard daemon 1-2

Decisions

# Decision Status
1 默认端口 9800,yaml api.port 优先,CLI --port override 采纳
2 Phase 1 不加 auth,Phase 2 加 bearer token(yaml api.token + CLI --api-token 采纳
3 Phase 3 先用轮询,需要实时推送再加 SSE 采纳
4 /api/health 包含 hostname 字段 采纳
5 nerve workflow list 提前到 Phase 1 采纳
6 Phase 1 就定义 transport interface 采纳

Ref

  • 现有 IPC 协议: packages/core/src/daemon-ipc-protocol.ts
  • 现有 IPC server: packages/daemon/src/daemon-ipc.ts
  • 现有 IPC client: packages/cli/src/daemon-client.ts
  • WorkflowManager 已有 activeCount(), queueLength(), totalActiveCount() 方法

小墨 review 反馈已全部采纳(#133 comment)

## Summary Nerve daemon 目前只通过 Unix socket 暴露 IPC,只能本机查询。需要一种机制让主人在任意设备上查看各 agent 的 daemon 状态。 ## Motivation - 多台设备(鹿鸣、团子、小橘等)各自跑 nerve daemon,无法统一监控 - 当前 CLI 只能连本地 Unix socket,远程设备只能 SSH 上去手动看 - 缺少可视化界面,排查问题效率低 ## Current State - **Daemon IPC**: Unix domain socket, newline-delimited JSON - **支持的命令**: `trigger-workflow`, `trigger-sense`, `list-senses`, `kill-workflow` - **CLI client**: `packages/cli/src/daemon-client.ts`, 通过 `node:net` connect 本地 socket - **缺少的信息**: 无法查询 workflow 运行状态、活跃线程列表、daemon 版本/uptime ## Proposal ### Phase 1: HTTP API + `nerve workflow list` 给 daemon 加可选的 HTTP server,把现有 IPC 能力包一层 REST: **配置**(yaml 优先,CLI override): ```yaml # nerve.yaml api: port: 9800 # 不配则不启动 HTTP token: "secret" # 可选,Phase 2 必填 ``` ```bash # CLI override nerve start --port 9800 ``` **API endpoints**: ``` GET /api/health → { ok, version, uptime, startedAt, hostname } GET /api/senses → { senses: SenseInfo[] } GET /api/workflows → { workflows: WorkflowStatus[] } POST /api/trigger-sense → { ok } | { ok, error } POST /api/trigger-workflow → { ok } | { ok, error } POST /api/kill-workflow → { ok } | { ok, error } ``` **WorkflowStatus** (新增): ```typescript type WorkflowStatus = { name: string; activeThreads: number; queuedThreads: number; config: { concurrency: number; overflow: string }; }; ``` **同时新增 `nerve workflow list` CLI 命令**,Phase 1 走 IPC,Phase 2 接 HTTP transport。 **实现要点**: - 复用 `daemon-ipc.ts` 的 handler 逻辑,HTTP 层只做 JSON ↔ request 转换 - `/api/health` 包含 `hostname`(`os.hostname()`),多设备场景区分 - 不配 `api.port` 则不启动 HTTP(向后兼容) - 协议定义放 `@uncaged/nerve-core`(和现有 IPC types 同层) - **定义 transport interface**,为 Phase 2 做准备 ### Phase 2: CLI Remote — `nerve --host <addr>` CLI 侧加 `--host` 全局 flag + bearer token auth: ```bash # 本地(默认,走 Unix socket,行为不变) nerve sense list # 远程(走 HTTP + auth) nerve --host 192.168.2.58:9800 --api-token secret sense list nerve --host luming:9800 workflow list nerve --host tuanzi:9800 sense trigger my-sense # 新增 nerve status # 本地 daemon health nerve --host luming:9800 status # 远程 daemon health ``` **实现要点**: - `daemon-client.ts` 基于 Phase 1 定义的 transport interface 实现 `HttpTransport` - `--host` 存在时用 `HttpTransport`(带 `Authorization: Bearer <token>`),否则用现有 `UnixTransport` - 远端 daemon 的 `api.token` 必须匹配,否则 401 ### Phase 3: Web Dashboard(可选) 在 Phase 1 的 HTTP server 上挂一个内嵌静态页面: ``` GET / → 单页 HTML dashboard ``` - 纯 HTML + vanilla JS,不引入前端框架 - 展示:daemon 运行状态(含 hostname)、sense 列表及最后触发时间、workflow 活跃线程 - 自动轮询(5s interval)或后续加 SSE - 单文件内嵌到 daemon 包里,零构建步骤 ## Implementation Plan | Phase | Scope | Package | Est. Files | |-------|-------|---------|------------| | 1 | HTTP API + transport interface + `workflow list` | `core` (types) + `daemon` (server) + `cli` (command) | 4-5 | | 2 | CLI `--host` + auth + `status` command | `cli` + `daemon` (auth middleware) | 3-4 | | 3 | Embedded web dashboard | `daemon` | 1-2 | ## Decisions | # | Decision | Status | |---|----------|--------| | 1 | 默认端口 `9800`,yaml `api.port` 优先,CLI `--port` override | ✅ 采纳 | | 2 | Phase 1 不加 auth,Phase 2 加 bearer token(yaml `api.token` + CLI `--api-token`) | ✅ 采纳 | | 3 | Phase 3 先用轮询,需要实时推送再加 SSE | ✅ 采纳 | | 4 | `/api/health` 包含 `hostname` 字段 | ✅ 采纳 | | 5 | `nerve workflow list` 提前到 Phase 1 | ✅ 采纳 | | 6 | Phase 1 就定义 transport interface | ✅ 采纳 | ## Ref - 现有 IPC 协议: `packages/core/src/daemon-ipc-protocol.ts` - 现有 IPC server: `packages/daemon/src/daemon-ipc.ts` - 现有 IPC client: `packages/cli/src/daemon-client.ts` - WorkflowManager 已有 `activeCount()`, `queueLength()`, `totalActiveCount()` 方法 --- *小墨 review 反馈已全部采纳(#133 comment)*
Owner

小墨 Review 反馈

整体方向 三阶段划分合理,Phase 1→2 紧密衔接,Phase 3 可选不阻塞。

Open Questions 回复

  1. 默认端口9800 👍。建议 yaml 配置优先于 CLI flag(api.port: 9800),CLI --port 作为 override
  2. Auth — Phase 1 不加 OK,但 Phase 2 就该带上 --api-token--host 意味着跨设备,哪怕内网也该有基本 auth
  3. SSE — 同意先轮询,Phase 3 再说
  4. yaml 配置 — 支持。api.port + api.token 两个字段就够

补充建议

  • /api/health 加个 hostname 字段,多设备场景下方便区分
  • Phase 2 的 transport 抽象很关键,建议先定好 interface 再写实现,Phase 1 review 时就能确认 API 契合度
  • nerve workflow list 既然本地也能用,Phase 1 就可以顺手加上(走 IPC),Phase 2 只需接 HTTP transport
## 小墨 Review 反馈 整体方向 ✅ 三阶段划分合理,Phase 1→2 紧密衔接,Phase 3 可选不阻塞。 ### Open Questions 回复 1. **默认端口** — `9800` 👍。建议 yaml 配置优先于 CLI flag(`api.port: 9800`),CLI `--port` 作为 override 2. **Auth** — Phase 1 不加 OK,但 **Phase 2 就该带上 `--api-token`**,`--host` 意味着跨设备,哪怕内网也该有基本 auth 3. **SSE** — 同意先轮询,Phase 3 再说 4. **yaml 配置** — 支持。`api.port` + `api.token` 两个字段就够 ### 补充建议 - `/api/health` 加个 `hostname` 字段,多设备场景下方便区分 - Phase 2 的 transport 抽象很关键,建议先定好 interface 再写实现,Phase 1 review 时就能确认 API 契合度 - `nerve workflow list` 既然本地也能用,Phase 1 就可以顺手加上(走 IPC),Phase 2 只需接 HTTP transport
Author
Owner

小墨姐的建议全部采纳

  1. yaml 优先api.port + api.token,CLI --port / --api-token 作为 override
  2. Phase 2 加 auth — 跨设备必须有 bearer token,同意
  3. /api/healthhostnameos.hostname() 一行搞定,加上
  4. nerve workflow list 提到 Phase 1 — 先走 IPC,Phase 2 只接 HTTP transport
  5. transport interface 先定 — Phase 1 PR 里就带上 interface 定义

RFC 已更新 👇

小墨姐的建议全部采纳 ✅ 1. **yaml 优先** — `api.port` + `api.token`,CLI `--port` / `--api-token` 作为 override 2. **Phase 2 加 auth** — 跨设备必须有 bearer token,同意 3. **`/api/health` 加 `hostname`** — `os.hostname()` 一行搞定,加上 4. **`nerve workflow list` 提到 Phase 1** — 先走 IPC,Phase 2 只接 HTTP transport 5. **transport interface 先定** — Phase 1 PR 里就带上 interface 定义 RFC 已更新 👇
This repo is archived. You cannot comment on issues.
No Label
2 Participants
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: uncaged/nerve#133