@black942026/computer-board1.0.5
手工维护机器与软件清单、周期性探活、只读查询的 HTTP MCP 服务,附带 pi extension
Untested on omp
This is a pi package. No one has reported whether it works on omp yet. If you try it, add your result below.
Reports 0
No reports yet.
Readme
Computer Board
手工维护机器与软件清单、周期性探活、只读查询的后台服务,同时以 HTTP MCP 和 pi extension(四个同名只读工具)提供给 agent。
服务只回答「现在有哪些机器和软件可用、怎么接入」;它不执行任何业务操作,不做 SSH 转发或端口转发,不代理远程调用,也不修改配置。
安装与运行
要求 Node.js 22+。
npm install -g @black942026/computer-board
computer-board init # 按样例创建 ~/.computer/config.json,已存在则不改动
$EDITOR ~/.computer/config.json
computer-board validate # 校验配置
computer-board # 前台启动服务,默认 http://127.0.0.1:3000
computer-board ensure # 后台启动服务,已在运行则复用;pi extension 用它自动拉起
computer-board stop # 停止后台服务
同一份配置下服务是单例:ensure 用配置目录下的 daemon.lock 串行化并发启动,服务端独占 server.port 并记录自己的 pid,所以重复调用、多个 pi 会话同时拉起都只会得到一个实例;换 COMPUTER_BOARD_CONFIG 或改端口则是另一个实例,两者不共享探活结果。后台实例的地址与 pid 记在 daemon.json,输出写 daemon.log,两者都与配置同目录。
源码调试:
npm install
npm run dev # 页面 http://127.0.0.1:5173,服务端 http://127.0.0.1:3000
npm run build && npm start # 页面与 HTTP MCP 都由 127.0.0.1:3000 提供(被后台实例占用时先 computer-board stop)
服务没有登录和权限控制,默认只监听 server.host 指定的回环地址,不要直接暴露公网。
配置
配置文件默认在 ~/.computer/config.json,样例见 config.example.json;服务与 pi extension 都读同一路径,用 COMPUTER_BOARD_CONFIG=/absolute/path/config.json 覆盖。
服务运行期间监听配置文件,保存后自动重新解析校验并生效(revision、machines、defaults 都包括),搜索索引也随新配置同步重建,不需要重启;切换立即生效,重新探活接着在后台跑,不阻塞查询;解析或校验不通过时保留上一份可用配置与对应索引继续服务,只在前台输出或 daemon.log 里给出原因,改好保存即恢复。server.host、server.port 决定监听地址,无法在运行中变更,改动后要 computer-board stop && computer-board ensure 才生效(服务日志会提示)。探活结果只在内存,重启后重新检查。
| 字段 | 机器 | 软件 |
|---|---|---|
id |
全局唯一标识 | 全局唯一标识 |
name |
展示名称,可重复 | 展示名称,可重复 |
host |
localhost 或 account@ip:port(IPv6 用 [addr]) |
— |
desc |
用途、使用场景 | 能力、使用场景 |
instruction |
使用、登录说明 | 如何接入(bash、http、mcp、acp 等自由文本) |
tips |
注意事项,字符串或字符串数组 | 同左 |
dependOn |
依赖说明(文本,不做依赖解析) | 同左 |
enabled |
是否进入列表与探活 | 同左 |
healthChecks |
探活配置数组 | 同左 |
software |
软件数组 | — |
配置顶层的 server 决定服务监听地址与 HTTP MCP 路径(host、port、path),defaults 决定探活间隔、超时、失败阈值与并发上限(intervalSeconds、timeoutSeconds、failureThreshold、maxConcurrency),单条检查可以用同名字段覆盖间隔、超时和阈值。
约定:
- 机器与软件的
id全局唯一(包含停用项),空白 ID 不合法;最多一台localhost机器,local类型探活只能配在它身上。 - 账户、地址、端口只写在
host里,不单独重复配置。 desc、instruction、tips、dependOn和探活命令、参数都会返回给 agent,不要在这些字符串里写 secret;凭据应由各软件自己的配置管理,不放进本服务。instruction、tips、dependOn是给 agent 看的说明,不会被执行,也不参与探活。
探活
服务启动后按配置的间隔周期性执行 healthChecks:
local:Board 进程所在机器本身即视为在线,只能配在localhost机器上。bash:在 Board 上执行的固定命令与参数,退出码为 0 记为成功,带--version或-v时还会从输出里观测版本号。命令可以是ping、curl、ssh等只读命令;需要检查远端时写成一次性的ssh host <命令>,服务本身不做端口转发或驻留会话。
配置热重载后会立即按新配置重跑一轮探活,未改动项沿用已有结果与失败计数,因此不会先掉回 unknown 再恢复。
结果为 healthy(最近一次成功)、degraded(失败但未达阈值)、unhealthy(失败达到阈值)、unknown(未检查或未配置检查)、disabled(已停用)。机器状态由其必需检查聚合而成;默认列表严格按 healthy 过滤,不使用宽限期。
Agent 接入
HTTP MCP
地址为 http://<server.host>:<server.port><server.path>(默认 http://127.0.0.1:3000/mcp),Streamable HTTP、JSON 响应、无会话,无权限控制。
| 工具 | 入参 | 返回 |
|---|---|---|
list_machines |
{ showAll?: boolean } |
机器简要列表:name、host 与软件名,showAll=true 时每项带 status |
search_machine |
{ query: string, showAll?: boolean, limit?: number } |
按关键词搜索机器:machineId、name、host、score(BM25 分数,降序),最多返回 min(limit, 命中数) 条,showAll=true 时每项带 status |
get_machine |
{ machine: { machineId } | { host } } |
单台机器详情:描述、使用说明、注意事项、依赖、探活与软件索引 |
get_software |
{ machine, software: { softwareId } | { name } } |
软件详情:描述、接入方式、注意事项、依赖、观测版本、探活与所属机器 |
定位顺序:先用 list_machines 或 search_machine 拿到 machineId,再用 get_machine / get_software 取详情。get_machine 的 host 必须与配置逐字符一致(含账号与端口);只拿到 IP 或 IP 的一部分时直接按 host 查会返回 NOT_FOUND,应先 search_machine 按片段定位——分词会把 root@192.168.0.1:22 切成 root/192/168/0/1/22,所以片段也能命中。
search_machine 是唯一的模糊查询:只用关键词在机器的 id、name、host 三个字段上做 BM25 相关性排序,不搜索 desc、instruction、software 等字段,不做子串/正则匹配。query 必填;空串、纯空白、错误类型或未知字段都会被拒绝,但两条路径的表现不同——HTTP 与 pi extension 走内部路由,返回 { "error": { "code": "INVALID_ARGUMENT" } };HTTP MCP 由 SDK 用同一份严格 schema 在入参校验阶段拒绝,返回 isError: true 的工具错误(文本形如 Input validation error: ...),不会进入服务端 handler。默认只搜探活成功的机器,showAll=true 搜索全部并附 status;无匹配返回空数组。limit 控制返回条数:省略时为 3,显式给出时必须是正整数(0、负数、小数、字符串、null、boolean、NaN/Infinity 都会被拒绝),不设人为上限,实际返回 min(limit, 命中数) 条。排序先按健康状态过滤候选,再用全部候选语料做 BM25 评分、降序并保持同分配置顺序,最后截取前 limit 条:不会先截候选而改变分数或排名,截取也不改变被保留命中项的 score。分词先做大小写归一化,再按连续字母数字与单个汉字切分(-、_、.、:、/、@ 等 id/host 分隔符都算边界),因此 remote-example、root@192.168.0.1:22 的用户名/IP/端口、以及中文名本机都能命中。分数始终为正的有限值,同分按配置顺序稳定排列;搜索只读内存缓存,不触发探活。BM25 使用 k1=1.2、b=0.75,IDF 取恒正形式避免常见词出现负分。
搜索索引的建立时机与存储:服务在启动(构造 HealthEngine)与每次配置热重载时各建立一次,配置统一从 HealthEngine 进入,所以 CLI 与进程内测试走的是同一条路径;首个查询不会临时构建。索引只放在内存里(Bm25Index 实例,构建时预计算每台机器 id/name/host 的词频、文档长度与倒排表,并整体替换旧实例而非累积),不落盘、不做磁盘缓存、也不引入新依赖。查询时只对关键词分词,按当前候选集合复用这些缓存计算 N/df/avgdl,不重新分词机器文本、不重建词频/长度/倒排表;showAll 与健康状态过滤只改变候选集合,健康状态变化不重建索引。
接口说明随 MCP 协议一并给出,不额外提供 describe 之类的工具:每个工具自带描述与入参 schema,initialize 返回的 instructions 说明服务边界、返回约定、状态含义与错误码。
pi extension
同一个包提供 pi extension,注册四个只读工具 list_machines、search_machine、get_machine、get_software:工具名、入参 schema 与有效调用的返回与上面的 HTTP MCP 一致。区别在错误路径——pi extension 走内部 HTTP 路由,非法入参返回 INVALID_ARGUMENT 错误体;HTTP MCP 的非法入参由 SDK 在入参校验阶段拒绝(isError: true),见上面对 search_machine 的说明:
pi install npm:@black942026/computer-board
扩展加载时会在后台执行 computer-board ensure,工具调用时如果服务不在运行也会先拉起再重试一次,所以 pi 启动即拉起服务,不必另开终端;多个 pi 会话共享同一个实例,探活也只有一份。服务确实起不来时,工具返回 { error: { code: "INTERNAL_ERROR", message } },message 给出原因(配置缺失、日志路径等)。
扩展读取 ~/.computer/config.json(可用 COMPUTER_BOARD_CONFIG 覆盖),每次调用都重新读取,因此只改 server.port 时按 computer-board stop && computer-board ensure 重新拉起即可,工具定义不变,不用重启 pi 会话。
返回约定
- 只返回配置里确实存在的字段:空字符串、空数组、未设置的字段一律省略,不用
null占位。 - 列表只返回定位用的简单字段,说明文本与探活细节由
get_machine、get_software提供。 - 默认只列出探活成功的机器和软件;
showAll=true返回全部并附status。 - 查询只读内存缓存,不触发探活;被默认列表隐藏的资源仍可按 ID 查询详情。
- 错误统一为
{ error: { code, message } },code为INVALID_ARGUMENT、NOT_FOUND、AMBIGUOUS、INTERNAL_ERROR;同名软件或相同 host 命中多个资源时返回AMBIGUOUS与候选 ID。
列表形状(省略了不存在的字段):
[
{ "name": "本机", "host": "localhost", "software": [{ "name": "Node.js" }] },
{ "name": "remote-example", "host": "root@192.168.0.1:22", "software": [{ "name": "comfyui" }] }
]
页面
computer-board 启动后,dist/web 存在时会在 / 提供只读页面,展示机器、软件、接入说明与探活状态。页面和 pi extension 都用同一组只读内部接口(/api/discovery、/api/query/*、/api/health/refresh),它们不是对外契约,只跟着页面与扩展的需要变化。
开发与发布
npm run typecheck
npm test
npm run build
npm pack --dry-run # 确认发布内容
npm publish # 发布前自动跑 check 与 build
推送 v*.*.* tag 会触发 .github/workflows/publish.yml:校验 package.json、package-lock.json 与 tag 三者版本一致,跑 npm ci 与 npm run check,然后 npm stage publish 只暂存不上架。需要配置 repository secret NPM_TOKEN(Read and write (stage only));暂存成功后由启用 2FA 的维护者在 npmjs.com 或通过 npm stage approve 批准。已暂存的版本不要直接重跑 workflow,先批准,或 npm stage reject 后再重跑。
发版前要确认 package.json 与 package-lock.json(version 与 packages[''].version)都已同步到待发版本,否则工作流会在校验步骤失败。
发布内容由 files 白名单限定为 index.ts、dist、extension、config.example.json、README.md、LICENSE;git 忽略 dist、node_modules、.computer 与本地运行数据,配置与探活状态不会进入仓库。
本地基准(node_modules/tsx 直接跑源码,仅示意、非性能承诺):2000 台机器建索引约 10ms;对 2000 台机器各跑 10000 次关键词查询,复用索引约 4.3s,而每次临时重算的旧公式约 42s。
pi extension 以根目录 index.ts 为导出入口,转导出 extension/index.ts 的默认扩展函数;安装后显示为 @black942026/computer-board,不带 :extension 后缀。扩展实现不 import 服务端源码,只用宿主提供的 typebox 与 @earendil-works/pi-coding-agent(见 peerDependencies),靠 dist/server/cli.js 拉起服务。所以本地路径安装前要先 npm run build;源码调试时可以用 COMPUTER_BOARD_CLI 指定 CLI 的启动命令,例如 COMPUTER_BOARD_CLI="node node_modules/tsx/dist/cli.mjs server/cli.ts"。