Files
heicode-mananger/docs/integration/heicode-desktop-client-api.md
T

24 KiB
Raw Blame History

Heicode 桌面客户端 对接接口文档(模板 Agent 模型 · v1)

更新时间:2026-06-04 适用:Heicode Desktop 对接 Heicode Manager(HM)的模板 Agent 模型。 生产地址:https://code.xinghanlab.com 状态图例:🟢 已实现并生产验证 · 🔴 待建(依赖 AM)

本文取代旧的桌面 sub 任务编排接口文档(描述 tasks/workflow/display_status/git_ref/产物下载的那套已下线,相关 md 已删除)。新客户端一律按本文对接。

2026-06-04 二次复测:端到端已跑通(code.xinghanlab.com,真实账号):部署→running→直连 /health//message/send(带令牌任务 completed)→stop→delete 全通,access_token 为 HM 现签非空 UUID。仅剩 AM 两项待加固(不影响功能):① agent 端尚未真正启用令牌校验(无令牌也被放行);② 子域名目前是 http:// 明文。详见 AM 契约 §0.1。


0. 模型总览(先读)

新模型很简单:

  1. 用户在 HM 网页控制台绑定资源(git/vm/数据库/对象存储)、选「资源 + 模板」部署一个常驻 agent。
  2. HM 把所选资源解出后注入 agent 的 .env(并现签一把 per-agent 访问令牌一起注入),交给 AM 启动;AM 返回唯一子域名。
  3. 桌面客户端从 HM 读 agent 列表 → 拿到子域名 + 访问令牌(HM 现签的 access_token)→ 直连 agent 子域名(SSE)对话使用。HM 不在对话回路里。
  4. 模型调用:客户端自己用模型、以及 agent 用模型,都走 HM /v1/*(鉴权 + 计费)。

客户端在新模型里的核心动作 = 列出我的 agent → 直连用。部署/管理主要在网页台完成(客户端也可调同一接口)。

总原则

  • 客户端调 HM 用 V2 设备签名(与模型调用同一套,见 §1)。
  • 列表里的 access_token 是连 agent 用的专属访问令牌(HM 给每个 agent 现签,只有部署它的你拿得到)。直连时带上它(X-Agent-Access-Token 头);agent 校验「是不是该 agent 的所有者」。它与模型 api_key 职责分离:access_token 管「谁能调这个 agent」,api_key 管「用谁的额度跑模型」。
  • 模型永远走 HM /v1/*,不要直连上游。

1. 认证与加密 🟢

桌面客户端用设备绑定密钥对每个请求做 Ed25519 签名;有 body 的写请求额外用 X25519-ECDH + HKDF + ChaCha20-Poly1305 加密 body。与调模型 /v1/* 用的是同一套(cc-haha/src/services/device/signRequest.ts),无需新增协议。本节自包含,照此即可实现。

每个请求都带的头:

头 含义
X-Heicode-Device-Id 设备 id(配对时获得)
X-Heicode-Timestamp unix 毫秒(服务端校验时钟偏移窗口)
X-Heicode-Nonce 每次随机,防重放
X-Heicode-Fingerprint 设备指纹
X-Heicode-Eph-Pubkey 本次临时 X25519 公钥(base64),每请求新生成
X-Heicode-Signature 下面 canonical 的签名(base64)
Content-Encoding: heicode-aead-v1 仅有加密 body 的写请求带;GET 不带

签名 canonical(字节序固定,服务端按此重建,顺序不可改):

canonical = method            + "\n"   # "GET" / "POST" / "DELETE"
          + path_with_query   + "\n"   # 服务端实际收到的 path(含 query,不含 origin)
          + timestamp_ms      + "\n"
          + nonce             + "\n"
          + fingerprint       + "\n"
          + eph_pubkey_b64    + "\n"
          + hex( sha256(body明文) )      # 无 body(GET) 填 hex(sha256("")) = e3b0c442…b855
signature = base64( ed25519_sign( device_priv, sha256(canonical) ) )
  • GET / 无 body:不发 Content-Encoding、body 不加密,canonical 末项填空串哈希。
  • POST / DELETE 有 body:发 Content-Encoding: heicode-aead-v1;body = 用 eph_priv × 服务器公钥 做 X25519-ECDH → HKDF → ChaCha20-Poly1305 加密;canonical 末项是加密前明文 body 的 sha256(hex)。
  • 服务端中间件 UserOrV2DeviceAuth 解密 + 验签,从设备绑定 token 解析用户身份。

鉴权失败时的响应头(客户端据此给用户准确提示,而非笼统"token 失效"):

  • X-Heicode-Auth-Error:timestamp_drift(时钟偏移)/ nonce_replay / revoked / fingerprint_mismatch / signature_invalid / device_not_found / eph_pubkey_missing …
  • X-Heicode-Server-Time:服务器 unix 毫秒(用来校正本地时钟,解决 timestamp_drift)。

设备如何拿到 device_id + 设备密钥:走与模型调用相同的设备配对/登录流程(cc-haha/src/services/device、/api/heicode-auth/*),本文不重复。

1.1 注销登录(logout)🟢

桌面客户端用设备绑定 token 登录,注销 = 吊销当前设备的 token(不是清会话 cookie):

方法 路径 鉴权 说明
POST /api/devices/logout V2 设备签名 / 会话 吊销当前设备的 token
  • 设备客户端:用 V2 设备签名调用即可(空 body 也行)——服务端按签名头 X-Heicode-Device-Id 找到本设备的 token 并吊销。只能注销自己,动不了用户的其它设备。
  • 会话/JWT 调用方:可在 body 传 {"device_id":"..."} 注销该用户名下某台设备。
  • 幂等:设备已不存在也返回 {"success":true}(注销已达成)。
  • 调用成功后客户端应同时删除本地设备密钥;要再用需重新配对(POST /api/devices/pair)。
// 成功
{ "success": true }

其它设备管理(会话/JWT):GET /api/devices/(列我的设备)、PATCH /api/devices/:id(改名)、DELETE /api/devices/:id(按 id 吊销某设备)。这些走 UserAuth,网页台「设备」页用;客户端自助注销用上面的 /api/devices/logout。


2. 账户与余额 🟢(可选;用用户会话/JWT,非设备签名)

方法 路径 鉴权 说明
GET /api/user/self UserAuth(会话 cookie / 用户 JWT) 当前用户:quota(剩余)、used_quota(累计已用)、request_count、分组
GET /api/user/self/models 同上 当前用户可用模型
// data 摘要
{ "id":22, "username":"chenchen", "quota":..., "used_quota":..., "request_count":... }

⚠️ 注意鉴权不同:/api/user/self* 走 UserAuth,不接受设备签名——客户端要用登录拿到的用户会话/JWT(Authorization: Bearer <jwt> 或会话 cookie)调用。/api/heicode/*(§3–§5)走 UserOrV2DeviceAuth,设备签名即可。 余额仅用于展示用量,非客户端核心流程(部署在网页台);拿不到也不影响"列 agent → 直连用"。


3. 能力发现 🟢

方法 路径 说明
GET /api/heicode/capabilities 模型目录(渲染模型选择);免登录
// 生产实测:models = [{"id":"gpt-5.4",...}](modes 仍返回 sub_agile/swarm,客户端忽略即可)
{ "success": true, "data": { "models": [{"id":"gpt-5.4","name":"gpt-5.4","available":true}] }}

客户端只需 models(当前可用 gpt-5.4);modes 是旧任务模式,新模型已无意义。

⚠️ 能力发现 vs 登录用户模型列表:/api/heicode/capabilities 是免登录的总目录(渲染模型选择用)。登录后客户端展示的「我能用哪些模型」必须走 §3.1 /api/heicode/available-models——它按当前用户的分组/订阅服务端收口,且只有 HM 这一个来源:客户端不得使用本地 preset,也不得从 CodeGW 渠道后台读取模型。


3.1 登录用户可用模型 🟢(模型列表收口)

方法 路径 鉴权 说明
GET /api/heicode/available-models UserOrV2DeviceAuth(会话/JWT 或设备签名) 当前登录用户实际可用的模型列表;服务端按用户可用分组 → 分组启用模型解析
{ "success": true, "data": {
  "available_models": [
    { "model_id": "gpt-5.4", "display_name": "gpt-5.4", "default": true }
  ]
}}
  • 字段仅 model_id / display_name / default(默认模型)。
  • 禁止暴露字段:channel_id、base_url、api_key、供应商类型、价格/倍率等一律不返回。
  • 唯一模型来源:客户端登录后模型列表完全来自此接口,不使用本地 preset / 不读 CodeGW 渠道后台。
  • 鉴权要求登录用户上下文(id>0);未登录返回 authentication required。

4. Agent 模板列表 🟢

供客户端展示"有哪些 agent 角色可用"。模板由 HM 维护(管理员可在控制台增改),展示用中文名 + 中文简介。

方法 路径 说明
GET /api/heicode/agent-templates 列出可部署的 agent 模板
{ "success": true, "data": {
  "total": 19,
  "items": [
    {"template_id":"architect","name":"架构顾问","description":"只读地分析代码、定位缺陷、给出架构与调试建议","model":"opus","status":"active"},
    {"template_id":"code-reviewer","name":"代码审查员","description":"审查代码改动,找出 bug 与质量问题","model":"...","status":"active"}
  ]
}}
  • template_id 是模板的稳定 key(如 architect),部署时回传。
  • name / description 为中文,直接展示。
  • model 是模板的角色层级提示(如 opus),不是实际跑的模型——agent 实际用网关模型 gpt-5.4(部署时 HM 注入)。客户端不用管这个字段。

5. 我的 Agent 🟢(已对接 AM,生产验证)

2026-06-04 生产实测:部署 / 列表 / 详情 / 状态 / 删除均真实可用。

  • subdomain 由 AM 分配(如 dep-xxxx.taijiagnet.com);新建后 status 为 Pending,需等 agent 起来变 running(直连前先确认,见 §6)。
  • access_token 是 HM 现签的 per-agent 专属访问令牌(非空,UUID)——直连 agent 时带上(X-Agent-Access-Token 头),agent 据此判定「是不是本 agent 的所有者」(见 §6)。
  • stop/delete 调 AM:2026-06-04 复测均已通——stop 返回 status:stopped,delete 返回 runtime_cleanup:"ok"(旧版的 404/500 已修)。delete 仍保留容错:即便 AM 删除失败也会清掉 HM 本地记录并以 runtime_cleanup:"failed" 提示。
方法 路径 说明 状态
GET /api/heicode/agents 列出我部署的 agent 🟢
GET /api/heicode/agents/{agent_id} agent 详情(读取时刷新状态) 🟢
GET /api/heicode/agents/{agent_id}/status 主动拉 agent 实时状态(是否运行/挂了) 🟢
POST /api/heicode/agents 部署:{template_id, binding_ids:[...]} 🟢(调 AM 🔴)
POST /api/heicode/agents/{agent_id}/stop 停止 🟢
DELETE /api/heicode/agents/{agent_id} 删除 🟢

列表 / 详情 / 部署成功 返回的 agent 对象:

// 生产实测响应(GET /api/heicode/agents 的一项 / 部署成功返回)
{ "success": true, "data": {
  "items": [
    {
      "agent_id": "dep_4bb07dc1e376",            // HM 侧 agent 记录 id(部署/停删都用它)
      "template_id": "architect",                 // 用了哪个模板
      "subdomain": "dep-4bb07dc1e376.taijiagnet.com",  // ★ 直连地址(AM 分配,主机名)
      "access_token": "550e8400-e29b-41d4-a716-446655440000",  // ★ per-agent 访问令牌(直连带上)
      "binding_ids": [],                          // 挂了哪些资源绑定(网页台绑的)
      "status": "Pending",                        // Pending | running | failed | stopped …
      "runtime_id": "dep-4bb07dc1e376",           // AM 侧运行时 id
      "created_at": "2026-06-04T09:04:15Z",
      "updated_at": "2026-06-04T09:04:15Z"
    }
  ],
  "total": 1
}}

部署请求 POST /api/heicode/agents:

{ "template_id": "architect", "binding_ids": [17] }
  • binding_ids 是网页台「资源绑定」里已绑资源的 id(可空数组,表示不挂资源)。
  • HM 处理:加载模板 .md → 把所选资源按类型解成 env(非密配置 + KV 解出密钥)+ 注入模型环境(OPENAI_BASE_URL/OPENAI_API_KEY/MODEL_NAME=gpt-5.4)→ 交 AM 启动 → 存子域名返回。

状态查询 GET /api/heicode/agents/{id}/status:

{ "success": true, "data": { "agent_id":"dep_…", "status":"running", "updated_at":"2026-06-04T…" }}

5.1 Agent 模型用量 🟢(按部署 Agent 聚合)

方法 路径 鉴权 说明
GET /api/heicode/agents/{deployment_id}/usage UserOrV2DeviceAuth,且只能查自己的 agent 按该 agent 的隐藏模型 token(name=agent:<deployment_id>)在计费 logs 中聚合用量

查询参数(可选):start / end = unix 秒时间窗(缺省=全窗口)。

{ "success": true, "data": {
  "agent_id": "dep_4bb07dc1e376",
  "quota": 12345,            // 消耗的额度(配额单位)
  "prompt_tokens": 8000,
  "completion_tokens": 4000,
  "call_count": 12,
  "quota_per_unit": 500000,   // 额度→货币换算分母(quota/quota_per_unit=美元额度)
  "budget_remaining": 1234567 // ★ 用户钱包剩余额度(预算剩余;-1=读取失败,不阻断展示)
}}
  • 空数据语义:无调用记录时各计数为 0(仍返回 success:true,不是 404)。
  • budget_remaining(#9):用户剩余可用额度(同 quota_per_unit 口径换算)。Agent 模型调用经隐藏 token 计费到 user.Quota,故"本任务预算剩余"= 用户钱包剩余额度。
  • 与 billing logs 的关系:用量来自统一计费 logs(SumAgentUsage),按 token name agent:<deployment_id> 过滤聚合 —— 即 agent 走 HM /v1/* 的真实消耗,与用户钱包/订阅扣费同源。
  • 计费归集语义(#30):每个部署的 agent,HM 为其 mint 一个隐藏、不展示在用户 token 列表的模型 token(UnlimitedQuota:true)。UnlimitedQuota 的含义是「不对该 token 自身设单独的剩余额度上限」——它不绕过用户额度:agent 经此 token 调 /v1/* 时,HM 仍先校验 user.Quota,并在结算时从 user.Quota(钱包)或订阅项扣费、写计费 log,完全经过计费表达式。停止/删除 agent 后该 token 被撤销,旧 token 无法再调 /v1/*。该 token 对普通用户隐藏,但审计/管理员可追踪。

6. 直连 Agent(客户端 ↔ agent,A2A 协议)

客户端直接连 agent 子域名(§5 的 subdomain),不经过 HM。agent 是 AM 的 coding_a2a_agent,走 A2A 协议。

⚠️ 连之前先确认 agent 就绪:新建后 status=Pending(还在拉起)。等 GET /api/heicode/agents/{id}/status 变 running、或 GET {subdomain}/health 返 200 再连。2026-06-04 复测:数秒即 running,/health 200、/message/send 带令牌任务 completed,直连已通。

⚠️ 当前 AM 侧两点(待加固,不影响调通):① 令牌校验尚未真正生效——无 X-Agent-Access-Token 也被放行;客户端仍应规范地每请求都带,等 AM 开启校验即自动生效。② 子域名目前 http:// 明文,令牌/api_key 会明文传输,等 AM 上 HTTPS。

  • 同步:POST {subdomain}/message/send
  • 流式:POST {subdomain}/message/stream(返回 text/event-stream)
  • 发现/健康:GET {subdomain}/.well-known/agent.json、GET {subdomain}/health

请求头:X-Agent-Access-Token: <§5 列表里的 access_token> —— 访问鉴权(证明你是该 agent 的所有者)。

请求体(JSON-RPC,A2A):

{ "jsonrpc":"2.0", "id":"task-1", "method":"message/send",
  "params": {
    "api_key": "sk-…",          // ★ 模型 key,agent 用它调 HM /v1 计费(客户端自带)
    "model": "gpt-5.4",
    "message": { "role":"user", "parts":[ {"kind":"text","text":"…"} ] },
    "configuration": { "workspace": { "root_dir":"/workspace", "allowed_paths":["a.py"] } }
  }
}

两层鉴权,职责分离:

  • 访问鉴权 = access_token(放 X-Agent-Access-Token 头,值取 §5 列表里那把 per-agent 令牌):证明「你是这个 agent 的所有者,有权调它」。每个直连请求都要带(message/send、message/stream 都带)。agent 端把它和自己 env 里的 AGENT_ACCESS_TOKEN 做常量时间比对,不符返回 401/403。
  • 模型鉴权 = params.api_key(模型 sk-,客户端自带的网关 key):agent 凭它调 HM /v1/*,计费到你账上。
  • agent 用 .env 里注入的资源(git/mysql/postgres/azure-blob)自行干活;工具:read_file/write_file/edit_file/run_command/git_*/run_database_query/list_blob_objects 等。缺配置的资源工具调用会返回 resource not configured,不阻塞。
  • 可在 params.configuration.resources 里按需覆盖资源(请求级覆盖启动级)。

ℹ️ 安全:HM 为「客户端↔agent」提供按用户隔离 —— per-agent 访问令牌(access_token,部署时现签、注入 agent env、随列表下发),只有部署该 agent 的你拿得到 ⇒ 只有你能调。接入方式 = agent 本地比对(X-Agent-Access-Token == env AGENT_ACCESS_TOKEN),不回 HM、零往返。客户端职责很简单:直连每个请求都带上 X-Agent-Access-Token 头。通道加密走 agent 子域名的 HTTPS/TLS。详见 AM 契约 §3.1。


7. 模型调用 🟢

  • 客户端自己用模型:POST /v1/chat/completions 等,base_url = HM,鉴权同模型调用。
  • agent 用模型:agent 内部也打 HM /v1/*(用户身份计费)。
  • 两边共用同一套模型接口,HM 统一鉴权 + 计费 + 路由。

7.1 客户端运行时配置 🟢

方法 路径 鉴权 说明
GET /api/heicode/config 无(公开,非敏感全局配置) 客户端 runtime config / telemetry 开关;客户端轮询以便会话内即时生效(无需重登)
{ "success": true, "data": {
  "telemetry": {
    "enabled": false,                       // 默认关闭(kill switch)
    "endpoint": "/api/heicode/telemetry/events",
    "max_batch": 20,
    "flush_interval_sec": 30,
    "retention_days": 30                    // 服务端保留期:超期遥测被清理(#32)
  }
}}
  • 客户端必须以 telemetry.enabled 为准:为 false 时停止上送(摄入端点也会回 410)。

7.2 客户端错误遥测上送 🟡(默认关闭)

方法 路径 鉴权 说明
POST /api/heicode/telemetry/events UserOrV2DeviceAuth + V2 设备签名(需配对设备) 上送客户端错误遥测;诊断流量,绝不计费、不进 consume log
  • 默认关闭:HEICODE_TELEMETRY_ENABLED=false 时返回 410(kill switch),客户端应停止上送。
  • 鉴权:需登录用户 + 已配对设备;请求头带 X-Heicode-Device-Id。会话-only(无设备)调用被拒(403)。
  • Body = 顶层 JSON 数组(不是包裹对象),1–20 条/批,≤256KB。超限 413,非数组 400。
  • 每条事件字段(诊断用,无用户内容):client_id(须等于配对设备 id)、schema_version、app_version、platform、os_version、arch、locale、error_category、error_code、error_message_hash、stack_hash、stack_top(数组)、context(对象)、timestamp、session_seq。
  • 服务端脱敏:stack_top / context 即使客户端已脱敏,服务端仍二次 redaction(剥离 sk-/Bearer/URL token/JSON 密钥字段)。
  • context 字段白名单(#32):context 仅保留 route / retryable / phase / exit_code / duration_ms / attempt;其余键(含 email、完整文件路径、prompt、IP 原文等可识别信息)一律丢弃。stack_top/context 单字段脱敏后截断到 8KiB。
  • 重试语义:4xx(校验失败/超限/kill switch 410)丢弃不重试;5xx(持久化失败)可重试。
  • 保留期(#32):服务端按 HEICODE_TELEMETRY_RETENTION_DAYS(默认 30)定期清理超期遥测。

⚠️ 上线前置门槛(#32):device_id/client_id 可关联账号,属隐私敏感。生产开启 HEICODE_TELEMETRY_ENABLED=true 前必须:隐私文档已如实披露「设备 ID 可关联账号的错误遥测」、产品/法务已确认、kill switch 已验证。隐私文档同步见 heicodeDocs(#34)。

// 请求体(顶层数组,示意一条)
[
  { "client_id":"<paired-device-id>", "app_version":"0.5.0", "platform":"win32",
    "error_category":"ui_crash", "error_code":"RENDERER_ERROR",
    "error_message_hash":"9f2a7c1b4e8d", "stack_hash":"a1b2c3d4e5f6",
    "stack_top":["at MessageList (MessageList.tsx:212:9)"],
    "context":{"route":"chat"}, "timestamp":"2026-06-09T07:21:33.123Z", "session_seq":1 }
]
// 成功:{ "success": true, "accepted": 1 }

8. 响应 envelope 与错误码

成功:{ "success": true, "data": {…} } 失败:{ "success": false, "message":"…", "error": { "code":"…", "message":"…", "retryable": false, "request_id":"…" } }

⚠️ 两个易踩的点:

  • 失败也是 HTTP 200(success:false)——客户端必须读 success 字段判成败,不能只看 HTTP 状态码(否则失败会被当成功)。
  • error.retryable 当前一律为 false,不要据它判重试;按下表的 code 判。request_id 便于排查。
code 场景 处理
POLICY_REJECTED 参数非法 / 未认证 / 未知 template_id 改参数,不重试
RESOURCE_BINDING_INVALID binding_ids 里有不存在/非本人的绑定 检查资源
RUNTIME_UNAVAILABLE 调 AM 失败(含 AM 接口未就绪) AM 侧问题,可稍后重试
DEPLOYMENT_CONFLICT agent 不存在 —
DEPLOYMENT_PERSIST_FAILED HM 落库失败 —

实时刷新:运行中轮询 GET /agents 或 /agents/{id}/status(3–5s);终态降频。


9. 客户端完整流程

0. (设备绑定/登录) — 复用模型调用的 V2 设备签名
1. (可选) GET /api/heicode/capabilities        取模型目录
2. (可选) GET /api/user/self                    取余额
3. GET /api/heicode/agents                      列出我的 agent
   - 想看有哪些角色: GET /api/heicode/agent-templates (中文名/简介)
   - 部署(也可在网页台做): POST /api/heicode/agents {template_id, binding_ids}
4. 选一个 status=running 的 agent → 取 subdomain + access_token
5. 直连 subdomain (SSE) → 与 agent 对话/干活    ← 不经 HM
   - 头带 X-Agent-Access-Token: <access_token>  (访问鉴权,证明是该 agent 所有者)
   - body params.api_key: <模型 sk>             (模型鉴权,计费到你账上)
6. 期间用模型 → HM /v1/*  (客户端与 agent 两端都是)
7. 管理: GET /agents/{id}/status | POST /agents/{id}/stop | DELETE /agents/{id}

10. 接口清单(覆盖核查)

接口 鉴权 状态
GET /api/heicode/capabilities 无(公开) 🟢
GET /api/heicode/available-models 会话/设备 🟢(登录用户模型列表收口,唯一来源)
GET /api/heicode/config 无(公开) 🟢(runtime config / telemetry 开关)
GET /api/user/self、/self/models UserAuth(会话/JWT) 🟢
GET /api/heicode/agent-templates 会话/设备 🟢(生产已验证 19 中文模板)
GET /api/heicode/agents /{id} /{id}/status 会话/设备 🟢
POST /api/heicode/agents 会话/设备 🟢(调 AM,AM 接口 🔴)
POST /api/heicode/agents/{id}/stop、DELETE /{id} 会话/设备 🟢(调 AM 🔴)
GET /api/heicode/agents/{id}/usage 会话/设备(仅自己的 agent) 🟢(按 agent:<id> token 聚合计费 logs)
POST /api/heicode/telemetry/events 会话/设备 + V2 设备签名 🟡 默认关闭(410 kill switch);不计费
模型 /v1/* 同模型调用 🟢
POST /api/heicode/agent-access/verify 公开(令牌即凭据) 🟢(可选兜底;接入用 ① 本地比对,默认不用此端点)
直连 agent SSE(subdomain + X-Agent-Access-Token) agent 本地比对令牌 🟢 直连已通 / ⚠️ 令牌校验待 AM 开启

端到端(部署→running→直连→stop→delete)已生产验证。AM 侧仅剩两项加固:令牌校验真正生效 + 子域名 HTTPS(见 AM 契约 §0.1)。代码隔离层见 controller/agent_template_runtime.go。