nova-terminal
v0.1.5
Published
AI-native terminal server
Readme
nova-terminal
AI-native terminal server — gives AI agents structured access to terminal sessions, processes, and system state via REST API and MCP protocol.
概述
nova-terminal 是一个为 AI agent 设计的终端服务器。它弥补了 bash 工具无法处理的场景:长期运行的进程、交互式 PTY、跨轮次的进程状态,以及对系统的结构化查询。
| 能力 | bash 工具 | nova-terminal | |---|---|---| | 一次性命令 | ✅ | ✅ (Exec) | | 长期运行进程 | ❌ | ✅ (PTY Sessions) | | 交互式 PTY (stdin/TUI) | ❌ | ✅ | | 等待输出模式匹配 | ❌ | ✅ (Wait) | | 跨轮次进程状态 | ❌ | ✅ | | Attach 已有进程 | ❌ | ✅ | | 结构化断言 | ❌ | ✅ (Verify) |
快速开始
npx / 全局安装
npx nova-terminal
# 或
npm install -g nova-terminal
nova-terminal --port 3444Docker
docker run -p 3444:3444 ghcr.io/chovrio/nova-terminal:latest从源码启动
pnpm install
pnpm dev # tsx watch,热重载
# 或
pnpm build && pnpm start默认端口:3444
CLI 选项
Usage: nova-terminal [options]
Options:
--port <number> 监听端口 (默认: 3444)
--log-level <level> 日志级别: trace|debug|info|warn|error (默认: info)
--help 显示帮助MCP 配置
将以下配置加入 Claude Desktop 或其他支持 MCP 的客户端:
{
"mcpServers": {
"nova-terminal": {
"type": "http",
"url": "http://localhost:3444/mcp"
}
}
}MCP 端点实现了所有 18 个工具,与 REST API 完全对应。
Claude Code Skill
项目内置了一个 Claude Code skill 文件(skills/nova-terminal.md),让 Claude 知道何时以及如何使用 nova-terminal。
安装方式: 在你的 CLAUDE.md 里引用它:
@/path/to/nova-terminal/skills/nova-terminal.md或者把文件复制到 Claude Code 的全局 skills 目录:
cp skills/nova-terminal.md ~/.claude/skills/nova-terminal.mdSkill 涵盖:
- 何时用 nova-terminal 而不是 bash 工具
- 常用工作流(启动服务器、运行测试、交互式 CLI)
- 完整工具参考
- Docker 环境下的项目分析与启动流程(自动检测 runtime、端口、volume 挂载)
REST API
Sessions(PTY 会话)
POST /sessions
body: { name, command, args?, cwd?, env?, cols?, rows? }
201: SessionRecord
409: 名称已存在
POST /sessions/attach
body: { name, pid }
201: SessionRecord (type: "attached")
404: 进程不存在
GET /sessions → SessionRecord[]
GET /sessions/:id → SessionRecord
DELETE /sessions/:id → 204
POST /sessions/:id/signal
body: { signal: "SIGTERM"|"SIGKILL"|"SIGHUP"|"SIGINT"|"SIGUSR1"|"SIGUSR2" }
204SessionRecord 结构:
{
"id": "uuid",
"name": "frontend",
"command": "pnpm",
"args": ["dev"],
"cwd": "/app",
"type": "pty",
"pid": 12345,
"status": "running",
"exitCode": null,
"createdAt": 1700000000000,
"lastActiveAt": 1700000000000
}Exec(一次性命令)
POST /exec
body: { command, args?, cwd?, env?, timeout_ms? (max 300000, 默认 30000) }
200: { stdout, stderr, exit_code, duration_ms }与 Sessions 不同,Exec 结果不持久化,适合简单的单次调用。
Output(读取输出缓冲区)
GET /sessions/:id/output
query:
tail? — 最后 N 行
since? — 某时间戳之后的行(毫秒)
search? — 正则过滤
stream? — "stdout"|"stderr"|"all" (默认 "all")
limit? — 最多返回条数 (默认 100, max 500)
clear? — "true" 读取后清空缓冲区
200: { count, dropped, entries: [{ stream, line, timestamp_ms }] }Input(写入 PTY)
POST /sessions/:id/input
body: { data: string } — 原始字符串写入 stdin
204
POST /sessions/:id/key
body: { key: "ctrl_c"|"ctrl_d"|"enter"|"tab"|"escape"|"up"|"down"|... }
204支持的具名按键:ctrl_c, ctrl_d, ctrl_z, ctrl_l, tab, enter, escape, up, down, left, right, backspace, delete, home, end, page_up, page_down
Wait(等待事件)
POST /sessions/:id/wait
body: { pattern?: string (正则), event?: "exit", timeout_ms?: number }
200: { matched: true, line?: string, elapsed_ms: number }
| { matched: false, reason: "timeout", elapsed_ms: number }先检查已有缓冲区,再订阅新行事件。event: "exit" 等待进程退出。
Verify(结构化断言)
POST /sessions/:id/verify
body: { rules: VerifyRule[] }
200: { pass: boolean, results: [{ rule, pass, detail? }] }VerifyRule 类型:
{ "type": "output_contains", "pattern": "server ready" }
{ "type": "output_not_contains", "pattern": "ERROR" }
{ "type": "exit_code", "code": 0 }
{ "type": "process_alive" }
{ "type": "process_exited" }Inspect(进程元数据)
GET /sessions/:id/inspect
200: { pid, ppid, cwd, args, env, children: pid[],
open_ports: number[], cpu_percent, memory_bytes }无法读取的字段返回 null,不报错。
Files(文件监听 & 读取)
POST /files/watch
body: { path, recursive?: boolean }
201: { id, path }
GET /files → { entries: [{ id, path, recursive, created_at }] }
GET /files/tail?path=...&lines= → { lines: string[], total_lines: number }
GET /files/:id/events?limit= → { count, entries: [{ type, path, timestamp_ms }] }
DELETE /files/:id → 204System(系统信息)
GET /system/ports?port=3000 → 单个端口占用情况
GET /system/ports?range=3000-3010 → 端口范围扫描
GET /system/processes?name=node → 进程列表(可按名称过滤)
GET /system/resources → CPU / 内存 / 磁盘
GET /health → { status: "ok", version: "0.1.0" }MCP 工具列表
| 工具 | 对应 REST |
|---|---|
| session_create | POST /sessions |
| session_list | GET /sessions |
| session_get | GET /sessions/:id |
| session_delete | DELETE /sessions/:id |
| session_attach | POST /sessions/attach |
| session_signal | POST /sessions/:id/signal |
| exec | POST /exec |
| output_read | GET /sessions/:id/output |
| input_write | POST /sessions/:id/input |
| input_key | POST /sessions/:id/key |
| wait | POST /sessions/:id/wait |
| verify | POST /sessions/:id/verify |
| inspect | GET /sessions/:id/inspect |
| files_tail | GET /files/tail |
| files_list | GET /files |
| system_ports | GET /system/ports |
| system_processes | GET /system/processes |
| system_resources | GET /system/resources |
典型使用流程
启动开发服务器并等待就绪
# 1. 创建 PTY 会话
curl -X POST http://localhost:3444/sessions \
-H 'Content-Type: application/json' \
-d '{"name": "frontend", "command": "pnpm", "args": ["dev"], "cwd": "/app"}'
# 2. 等待 "ready" 输出
curl -X POST http://localhost:3444/sessions/<id>/wait \
-H 'Content-Type: application/json' \
-d '{"pattern": "ready on port", "timeout_ms": 10000}'
# 3. 读取输出缓冲区
curl http://localhost:3444/sessions/<id>/output?tail=20运行测试并验证结果
# 创建会话运行测试
curl -X POST http://localhost:3444/sessions \
-d '{"name": "tests", "command": "pnpm", "args": ["test"]}'
# 等待测试完成
curl -X POST http://localhost:3444/sessions/<id>/wait \
-d '{"event": "exit", "timeout_ms": 60000}'
# 验证无错误
curl -X POST http://localhost:3444/sessions/<id>/verify \
-d '{"rules": [{"type": "exit_code", "code": 0}, {"type": "output_not_contains", "pattern": "FAIL"}]}'交互式操作(输入密码等)
curl -X POST http://localhost:3444/sessions/<id>/input \
-d '{"data": "mypassword\n"}'
# 或发送 Ctrl+C
curl -X POST http://localhost:3444/sessions/<id>/key \
-d '{"key": "ctrl_c"}'架构
REST/MCP clients
↓
API routes (src/api/)
↓
Core engines (src/core/)
├── SessionManager — 会话注册表,TTL 清理
├── OutputBuffer — 每会话循环缓冲区,EventEmitter
├── WaitEngine — 订阅 OutputBuffer 事件,模式匹配
├── VerifyEngine — 结构化断言
├── ProcessInspector — 进程元数据
├── SystemInspector — 系统查询
└── FileWatcher — fs.watch 封装
↓
Process layer (src/process/)
├── PTYManager — node-pty 封装
└── ExecRunner — child_process.spawn 封装
↓
node-pty / child_process / fs / os设计原则: 路由层不直接调用进程层;引擎层不互相依赖(SessionManager 除外,它是共享状态)。
开发
# 安装依赖
pnpm install
# 开发模式(热重载)
pnpm dev
# 类型检查
pnpm typecheck
# 单元测试
pnpm test:unit
# 集成测试(会启动真实进程)
pnpm test:integration
# 构建
pnpm build技术栈
- Runtime: Node.js 22, TypeScript ESM
- HTTP: Fastify 5
- PTY: node-pty
- MCP: @modelcontextprotocol/sdk (StreamableHTTP,无状态 per-request)
- Schema: Zod 4
- Test: Vitest(unit + integration 双项目)
- Package manager: pnpm
错误格式
{ "error": "Human-readable message" }Zod 校验失败(400):
{ "error": "Validation error", "details": [{ "path": ["field"], "message": "Required" }] }| 状态码 | 含义 | |---|---| | 400 | 请求体或查询参数无效 | | 404 | 会话或监听器不存在 | | 409 | 会话名称已被使用 | | 500 | 服务内部错误 |
注意事项
- Attach 会话为只读: 从 attach 时刻起捕获输出,历史输出不可恢复;不支持写入 stdin
- 会话 TTL: 退出的会话在闲置 30 分钟后自动清理
- Exec vs Session: 一次性命令用
/exec(无 ANSI 码,输出干净);需要交互或长期运行用 Sessions - 速率限制: 200 请求/分钟
License
MIT
