Skip to content

Hub 协议

Hub Protocol 是 DotCraft 本地客户端用来发现和管理工作区 AppServer 的本机协议,面向 Desktop、CLI、编辑器扩展和其他本地客户端。TypeScript 或 .NET 应用优先使用 DotCraft SDK Hub API,它已经实现发现、binary policy、结构化错误和 AppServer 启动流程。只有在实现自定义传输、使用不受支持的语言或调试协议时,才直接使用本页的 raw HTTP/SSE 契约。

Hub 只做本地协调:通过 HTTP JSON API 管理本机工作区 AppServer,通过 SSE 广播生命周期事件。它不是会话代理,不暴露任何 AppServer JSON-RPC 方法。appservers/ensure 返回端点后,客户端直接连接 AppServer 的 WebSocket,会话流量走 AppServer 协议。连接远程 AppServer 或自行管理 AppServer 进程的客户端不需要 Hub。

DotCraft Hub 启动流程

协议

Hub Local API 在回环地址上使用 HTTP JSON。所有 JSON 字段使用 camelCase。

能力说明
发现读取 ~/.craft/hub/hub.lock
API 传输HTTP JSON
事件传输Server-Sent Events (GET /v1/events)
地址默认绑定回环地址
认证受保护端点使用 Authorization: Bearer <token>
状态检查GET /v1/status 不需要认证

hub.lock 的典型内容:

json
{
  "pid": 12345,
  "apiBaseUrl": "http://127.0.0.1:49231",
  "token": "local-random-token",
  "startedAt": "2026-04-30T06:30:00Z",
  "version": "0.1.0",
  "binaryPath": "/path/to/dotcraft"
}

客户端读取锁文件后要逐项验证:

  1. pid 指向的进程仍然存活。
  2. GET {apiBaseUrl}/v1/status 可访问。
  3. 返回的 apiBaseUrl、版本、能力和可选 binaryPath 符合客户端预期。

任何一项不通过,就丢弃这份锁文件,启动 dotcraft hub,再重新走一遍发现流程。

认证

GET /v1/status 外,所有管理端点都需要 Bearer 令牌:

http
Authorization: Bearer <token-from-hub-lock>

未授权响应:

json
{
  "error": {
    "code": "unauthorized",
    "message": "缺少 Hub 令牌或令牌无效。",
    "details": null
  }
}

Hub 是同一操作系统用户下的本地协调器,不是跨用户安全边界。不要把 Hub API 暴露到非回环网络。

API 概览

端点认证说明
GET /v1/status返回 Hub 元数据和能力。
POST /v1/shutdown停止 Hub,并触发托管 AppServer 清理。
POST /v1/appservers/ensure确保工作区 AppServer 可用,必要时启动。
GET /v1/appservers列出运行中和已知的工作区 AppServer。
GET /v1/appservers/by-workspace?path=...查询某个工作区,不启动新进程。
POST /v1/appservers/stop停止一个 Hub 托管的工作区 AppServer。
POST /v1/appservers/restart重启一个工作区 AppServer。
POST /v1/services/ensure启动或复用一个已注册的一方本地服务。
GET /v1/services/by-id?id=...查询一个已注册的本地服务,但不启动它。
POST /v1/services/stop停止一个 Hub 托管的本地服务。
POST /v1/services/restart替换一个已注册的本地服务进程。
GET /v1/events订阅 Hub 生命周期事件。
POST /v1/notifications/request请求本地通知,由 Desktop 或托盘展示。

GET /v1/status

响应示例:

json
{
  "hubVersion": "0.1.0",
  "pid": 12345,
  "startedAt": "2026-04-30T06:30:00Z",
  "statePath": "/Users/me/.craft/hub",
  "apiBaseUrl": "http://127.0.0.1:49231",
  "binaryPath": "/path/to/dotcraft",
  "capabilities": {
    "appServerManagement": true,
    "managedServiceManagement": true,
    "portManagement": true,
    "events": true,
    "notifications": true,
    "tray": false
  }
}

tray: false 表示 Hub 本身无界面。托盘和系统通知 UI 由 Desktop 负责。

POST /v1/appservers/ensure

请求示例:

json
{
  "workspacePath": "/Users/me/project",
  "client": {
    "name": "my-client",
    "version": "0.1.0"
  },
  "startIfMissing": true,
  "runtimeTools": {
    "ripgrepPath": "/absolute/path/to/rg"
  }
}

响应示例:

json
{
  "workspacePath": "/Users/me/project",
  "canonicalWorkspacePath": "/Users/me/project",
  "state": "running",
  "pid": 23456,
  "endpoints": {
    "appServerWebSocket": "ws://127.0.0.1:49300/ws?token=..."
  },
  "serviceStatus": {
    "appServerWebSocket": {
      "state": "allocated",
      "url": "ws://127.0.0.1:49300/ws?token=...",
      "reason": null
    },
    "dashboard": {
      "state": "disabled",
      "url": null,
      "reason": "Dashboard 或追踪已禁用。"
    }
  },
  "serverVersion": "0.1.0",
  "startedByHub": true,
  "exitCode": null,
  "lastError": null,
  "recentStderr": null
}

重要字段:

  • state: 取值为 stoppedstartingrunningunhealthystoppingexited
  • endpoints.appServerWebSocket: 客户端连接 AppServer Protocol 时应使用的 URL。
  • serviceStatus: dashboard 和运行时辅助服务的状态。
  • startedByHub: 当前进程是否由此 Hub 管理。

如果 startIfMissingfalse,客户端可以查看状态,而不会创建新进程。

runtimeTools 是可选的本机运行时提示集合。Desktop 用它把内嵌的 rg、TypeScript 模块运行时和内置插件 roots 传给 AppServer。Hub 只把这些值作为 DOTCRAFT_RG_PATHDOTCRAFT_MODULES_DIRDOTCRAFT_BUILTIN_PLUGIN_ROOTS 等环境变量传给托管进程,不会在状态响应中回显。

如果 Hub 发现工作区 appserver.lock 已经 stale,会删除该锁并继续处理。如果锁指向的 AppServer 仍然存活,且其 appServerWebSocket 端点能完成 initialize 握手,Hub 可以返回该端点,并将 startedByHub 置为 false、相关 serviceStatus 标记为 external。如果这个 live lock 无法安全复用,Hub 会返回 workspaceLocked

停止与重启

停止请求:

json
{
  "workspacePath": "/Users/me/project"
}

重启使用相同的请求体,也可以包含 runtimeTools

一方本地服务

托管本地服务是封闭的 DotCraft 产品能力。插件和 Marketplace manifest 不能通过此 API 注册进程。当前构建注册了用户级 oratorio 服务。

Ensure 请求:

json
{
  "serviceId": "oratorio",
  "startIfMissing": true,
  "executable": "/absolute/path/to/oratorio"
}

宿主负责解析 executable。客户端不能提供参数、环境变量、状态目录或健康检查路径。并发 ensure 会复用同一个健康进程。

json
{
  "serviceId": "oratorio",
  "state": "running",
  "pid": 24567,
  "endpoint": "http://127.0.0.1:49310",
  "accessToken": "ephemeral-service-token",
  "version": "0.5.2",
  "lastError": null,
  "recentStderr": null
}

endpointaccessToken 视为仅供宿主使用的凭据。不要把它们交给 Renderer,也不要记录或持久化。服务状态只存在于当前 Hub 生命周期内。Hub 关闭时停止它拥有的进程,但不会在服务失败后自动重启。

停止请求使用 { "serviceId": "oratorio" }。重启还必须提供解析后的 executable

通知请求

通知请求:

json
{
  "workspacePath": "/Users/me/project",
  "kind": "turn.completed",
  "title": "任务完成",
  "body": "Agent 已完成请求的更改。",
  "severity": "success",
  "source": "appserver",
  "threadId": "thread_abc",
  "actionUrl": "dotcraft://workspace/open?path=/Users/me/project&threadId=thread_abc",
  "openDesktopOnClick": true
}

响应:

json
{
  "accepted": true
}

severity 会被规范化为 infosuccesswarningerror

threadIdactionUrlopenDesktopOnClick 是可选字段。AppServer 发送的 turn 通知只有在线程来源为 Desktop 时才会使用 dotcraft://workspace/open。其他来源的通知应设置 openDesktopOnClick: false,避免点击通知拉起 Desktop。

事件

订阅方式:

http
GET /v1/events
Authorization: Bearer <token>
Accept: text/event-stream

Hub 发送标准 SSE 记录:

text
event: appserver.running
data: {"kind":"appserver.running","at":"2026-04-30T06:31:00Z","workspacePath":"/Users/me/project","data":{"pid":23456,"endpoints":{"appServerWebSocket":"ws://127.0.0.1:49300/ws?token=..."}}}

已知事件类型包括:

事件说明
hub.startedHub 启动完成。
hub.stoppingHub 正在停止。
port.allocatedHub 为某个服务分配了本地端口。
appserver.starting工作区 AppServer 正在启动。
appserver.running工作区 AppServer 已可用。
appserver.exited工作区 AppServer 已退出。
appserver.unhealthy健康检查失败。
notification.requested有本地通知请求等待 UI 展示。

事件负载可扩展。按 kind 渲染已知字段,忽略未知字段。

连接 AppServer

拿到 endpoints.appServerWebSocket 后,客户端应打开 WebSocket,并按 AppServer Protocol 进行初始化:

json
{
  "jsonrpc": "2.0",
  "id": 0,
  "method": "initialize",
  "params": {
    "clientInfo": {
      "name": "my-client",
      "title": "我的客户端",
      "version": "0.1.0"
    },
    "capabilities": {
      "approvalSupport": true,
      "streamingSupport": true
    }
  }
}

然后发送:

json
{
  "jsonrpc": "2.0",
  "method": "initialized",
  "params": {}
}

更多会话方法见 AppServer 协议

错误

错误响应统一为:

json
{
  "error": {
    "code": "workspaceLocked",
    "message": "似乎已有运行中的进程持有工作区 AppServer 锁。",
    "details": {
      "workspacePath": "/Users/me/project",
      "pid": 23456
    }
  }
}

常见错误码:

错误码HTTP说明
unauthorized401令牌缺失或不匹配。
workspaceNotFound400/404工作区路径缺失、不存在,或不是 DotCraft 工作区。
workspaceLocked409另一个运行中的 AppServer 拥有该工作区锁,且无法安全复用。
appServerStartFailed500托管 AppServer 启动失败。
appServerUnhealthy500托管 AppServer 未通过就绪检查或健康检查。
portUnavailable500Hub 无法分配需要的本地端口。
invalidNotification400通知请求无效。
managedServiceNotRegistered404当前 DotCraft 构建没有注册该服务 ID。
managedServiceExecutableRequired400启动或重启需要宿主解析后的 executable。
managedServiceExecutableNotFound400解析后的 executable 不存在。
managedServiceStartFailed503服务未通过 ready 或健康检查。
hubInternalError500Hub 遇到未预期的内部错误。

客户端实现建议

  • 默认使用 Hub 管理本地工作区,并保留显式远程 AppServer 模式作为高级路径。
  • 启动 Hub 后应重读 hub.lock 并验证 /v1/status,不要假设进程已立即可用。
  • appserver.unhealthyappserver.exited 事件显示可操作状态,例如“重启工作区运行时”。
  • 不要把 Hub 令牌或 AppServer 令牌写入日志。
  • 对未知端点、服务状态和事件类型保持兼容。

相关文档