为第三方客户端提供 REST + SSE 网关:API 密钥鉴权、token 流式回包、会话工作区分组,并可接管 GUI 会话继续对话。
安装
# GitHub 源码(首次需按提示配置 allowBuilds 构建授权后重试)
dsh plugin --profile web add github:litestartup-com/dsh-api-gateway
装任何插件都等于在你的机器上跑第三方代码,权限和你本人一样大——能读你的文件、用你的凭据、访问网络,工具审批管不到它。GitHub 来源的插件还会在安装时执行构建脚本。请只安装可信来源,并尽量锁定 commit(github:owner/repo#sha)。
README
English | 中文
给 DeepSeek Harness 的 API Gateway 插件:把正在运行的 Harness 变成 HTTP API——任何第三方客户端(curl、Python、浏览器、IM 桥)都能创建 Agent 会话、经 SSE 逐 token 流式接收回复、并继续 UI 里聊到一半的会话,全程 API 密钥鉴权。API 会话与 GUI 会话驱动同一套 Agent 机器(inbox + 会话日志),两边天然同步。
dsh plugin --profile web add dsh-api-gateway
特性
- REST + SSE:9 个端点;token 级流式回包(
assistant/chunk),turn_end后服务端关流 - GUI 设置卡片:设置 → 插件 → 可配置 → API Gateway(状态 / 软开关 / 密钥轮换)
- 工作区归属:API 会话落进真实工作区,侧边栏分组显示,不再进「未分组」
- 会话发现与接管:列出全部会话、只读任意会话完整历史、adopt 接管 GUI 会话继续对话——在线共驾或冷恢复,上下文无缝衔接
- reasoning 分离:回复拆分为
text(正式回答)与reasoning(思考过程),不再混装 - 可扩展:在 Cordis 事件总线上发布
gateway/session-created/gateway/message/gateway/turn-end,供其他宿主插件订阅 - 任意语言客户端:Linux/macOS/Windows 通吃(含 PowerShell;UTF-8 安全、服务端 GBK 兼容)
安装
推荐:dsh plugin add
# npm(预构建产物,无需构建授权)
dsh plugin --profile web add dsh-api-gateway
# 直接从 GitHub 安装(源码)
dsh plugin --profile web add github:litestartup-com/dsh-api-gateway
# 从 tarball 安装
dsh plugin --profile web add ./dsh-api-gateway-0.1.0.tgz
Git 安装拉取的是源码,会执行本包的
prepare(构建)脚本;pnpm ≥ 10 默认拒绝,需把 pnpm 打印的包名键复制进 profile 的pnpm-workspace.yaml的allowBuilds后重试。该授权意味着"安装时执行此包代码"——只放行你信任的源,拿不准就固定 commit。
卸载:dsh plugin --profile web remove dsh-api-gateway。
手动组合行(不用 CLI)
插件本质是普通 Cordis 行,也可手工编排。它发布跨会话共享的 HTTP 面,应挂宿主组合(或 profile 的补丁层)——绝不能放进 agent preset:
- id: api-gateway
name: dsh-api-gateway
config:
prefix: /api-gw/v1 # 路由前缀
enabled: true # 主开关(也可运行时软开关)
apiKeys: [] # 预置静态 API 密钥
allowKeyProvision: true # 首调 POST /key 自助发钥
adminKey: change-me # 启用 admin 端点与卡片控件
maxSessions: 20 # 并发会话上限
workspaceMode: auto # auto(默认,自动挂入工作区)/ ungrouped
defaultWorkspacePath: '' # auto 模式下无 cwd 时的默认归属目录
allowDiscover: true # GET /sessions/discover
allowAdopt: true # POST /sessions/:id/adopt
corsOrigin: '*' # '*' 或具体域/列表
exposeErrors: true # 错误响应是否带内部细节
sseHeartbeatMs: 30000 # SSE 心跳间隔(0 关闭)
bodyTimeoutMs: 30000 # 请求体读取超时
所有键都有 schema 默认值——完整注释版见 examples/cordis.yml。
快速开始
BASE=http://127.0.0.1:3080/api-gw/v1
KEY=$(curl -s -X POST $BASE/key | jq -r .apiKey) # 首调领钥,仅一次有效
SID=$(curl -s -X POST $BASE/sessions -H "Authorization: Bearer $KEY" | jq -r .sessionId)
curl -N $BASE/sessions/$SID/stream -H "Authorization: Bearer $KEY" & # SSE 流式
curl -s -X POST $BASE/sessions/$SID/messages \
-H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{"content":"你好,介绍一下你自己"}'
Windows PowerShell(零依赖、UTF-8 安全):
$BASE = 'http://127.0.0.1:3080/api-gw/v1'
$KEY = (Invoke-RestMethod -Method Post "$BASE/key").apiKey
$SID = (Invoke-RestMethod -Method Post "$BASE/sessions" -Headers @{ Authorization = "Bearer $KEY" }).sessionId
$json = '{"content":"你好,介绍一下你自己"}'
$bytes = [System.Text.Encoding]::UTF8.GetBytes($json)
Invoke-RestMethod -Method Post "$BASE/sessions/$SID/messages" `
-Headers @{ Authorization = "Bearer $KEY" } -ContentType 'application/json; charset=utf-8' -Body $bytes
PowerShell 5.1 默认 ANSI/GBK 发中文会乱码:用上面的 UTF-8 字节写法(或声明
charset=utf-8);PowerShell 7 默认 UTF-8 无此问题。服务端按 Content-Type 的 charset 解码(默认 UTF-8,兼容 GBK)。没有jq?brew install jq,或用 Python/PowerShell 版。
Python(httpx):
import httpx, json
base = "http://127.0.0.1:3080/api-gw/v1"
key = httpx.post(f"{base}/key").json()["apiKey"]
h = {"Authorization": f"Bearer {key}"}
sid = httpx.post(f"{base}/sessions", headers=h).json()["sessionId"]
httpx.post(f"{base}/sessions/{sid}/messages", headers=h, json={"content": "你好,介绍一下你自己"})
with httpx.stream("GET", f"{base}/sessions/{sid}/stream", headers=h) as r:
for line in r.iter_lines():
if line.startswith("data: "):
print(json.loads(line[6:])["kind"])
端点
| 方法 | 路径 | 鉴权 | 说明 |
|---|---|---|---|
| GET | /health |
无 | 状态(停用时仍可访问) |
| POST | /key |
首次调用 | 一次性发放 API 密钥 |
| POST | /sessions |
API Key | 创建会话(provider/model/maxTokens/cwd/workspace) |
| GET | /sessions/discover |
API Key | 会话清单(id/title/cwd/live/persisted),不含内容 |
| POST | /sessions/:id/adopt |
API Key | 接管会话(live 共驾 / resumed 冷恢复),返回完整历史 |
| POST | /sessions/:id/messages |
API Key | 发消息(字符串或块数组) |
| GET | /sessions/:id/stream |
API Key | SSE:hello(回放)→chunk→message→tool_call/tool_result→turn_end |
| GET | /sessions/:id/history |
API Key | 任意会话完整历史(只读) |
| POST | /sessions/:id/cancel |
API Key | 中止当前回合 |
| POST | /admin/enable |
Admin Key | 运行时软开关 {"enabled": bool} |
| POST | /admin/rotate-key |
Admin Key | 轮换密钥 |
鉴权头二选一,等价:Authorization: Bearer <key>(推荐,RFC 6750)或 X-API-Key: <key>。
完整规范:openapi.yaml。
安全模型
POST /key 为什么能直接领到密钥? 它是"首次调用引导",不是常开的发钥匙口:
- 仅当尚无任何密钥时,
POST /key才生成一枚 32 位随机密钥——仅一次有效;此后该端点锁死(无钥 401)。 - 默认网关挂回环地址,第一个触达者只可能是部署者本人——等价于首次开机设密码。
- 不信任这个窗口就焊死:
allowKeyProvision: false,密钥只从apiKeys: [...]预置。
纵深(生产清单):
allowKeyProvision: false+ 预置apiKeys- 保持回环绑定;对外必走反向代理 + TLS
adminKey与 API 密钥分开管理- 每会话独立 Agent 上下文;会话 ID 为密码学随机
- 鉴权头主推
Authorization: Bearer(X-API-Key仅作别名) - 恒定时间密钥比较(
crypto.timingSafeEqual)+ CSPRNG 密钥生成
已知缺口(公开透明):无按密钥限流/配额、吊销清单、多密钥管理 UI、审计——多租户对抗场景等 v0.2+ 或自己前置网关。持有 API Key 即可发现/读取/接管全部会话——单机是特性,多租户是风险,可用 allowDiscover/allowAdopt 关闭(per-key 白名单在 v0.2.0)。
工作区归属
API 会话与 GUI 会话一样落进工作区(侧边栏分组,不进「未分组」)。POST /sessions 的 workspace 三种写法:
{ "workspace": "C:\\projects\\team-a" } // 路径字符串
{ "workspace": { "path": "C:\\projects\\team-a", "title": "团队A" } } // 带标题(新建时生效)
{ "workspace": { "id": "ws-xxx" } } // 已有工作区 ID
规则(服务端确定性执行):
- 路径已有工作区 → 复用;否则自动创建(标题默认取目录名)
id不存在 → 400 附现有工作区清单(id/title/path)- 未给
workspace→workspaceMode:auto(默认,按会话 cwd/defaultWorkspacePath解析或创建并挂载)或ungrouped - 同时给
cwd与workspace→ workspace 优先,会话 cwd 强制为工作区规范路径(持久成员关系前提:会话头 cwd == 工作区路径) - 目录不存在 → 400(网关不代为建目录)
响应与 history 均回带 workspace: { id, path, title }。共享协作工作区(多密钥共用路径)在 v0.2.0。
会话发现与接管(UI 会话 → API 续聊)
# ① 发现会话
curl -s $BASE/sessions/discover -H "Authorization: Bearer $KEY"
# ② 接管:在线共驾 / 冷恢复;返回完整历史
curl -s -X POST $BASE/sessions/$SID/adopt -H "Authorization: Bearer $KEY"
# → { "mode": "live" | "resumed", "history": [...] }
# ③ 继续对话 —— 与网关自建会话用法一致
curl -s -X POST $BASE/sessions/$SID/messages \
-H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{"content":"接着上次继续"}'
| mode | 含义 | 生命周期 |
|---|---|---|
created |
网关自建会话 | 随网关 |
live |
共驾 GUI 在线会话:API 消息直接出现在 GUI 对话流,双方回合排队 | 只借驾;插件停止 = 解除跟踪 |
resumed |
冷恢复离线会话(需 sessionPersistence) |
恢复后归网关持有 |
GET /sessions/:id/history 对任意会话可用(只读,无需先接管);/messages、/stream、/cancel 需先 adopt。
与官方 Python SDK 的关系
DeepSeek Harness 官方另有 Python SDK(快速上手 / SDK 参考)。两者不是同一种东西,也不互相替代:
| 官方 Python SDK | 本网关 | |
|---|---|---|
| 本质 | 嵌入式运行时:pip install deepseek-harness-sdk 自带平台 wheel,以子进程驱动内置 dsh-jsonrpc-agent(JSON-RPC stdio) |
运行中 Harness 的一扇门:宿主组合插件,REST + SSE |
| 模型凭据 | DeepSeek API Key(DEEPSEEK_API_KEY / DEEPSEEK_BASE_URL) |
网关自管 API Key(与模型凭据无关) |
| 会话 | session_root 下私有 JSONL,与部署/GUI 无关 |
部署共享会话库:GUI 可见、工作区分组、可接管 |
| 能力面 | 默认组合极简(本地 bash 等,无 skills、无压缩;可 cordis 定制) |
部署默认 agent preset 全部能力(工具/技能/沙箱策略) |
| 平台 | Linux x64/arm64、macOS 14+ arm64;不支持 Windows | 客户端任意语言/平台(含 Windows PowerShell) |
| 隔离性 | danger-full-access,仅建议容器/可丢弃环境 |
继承部署沙箱与审批策略 |
| 典型场景 | Python 脚本跑一次性隔离任务,无长期部署 | 第三方连接你正在运行的部署:跨语言、统一鉴权限流审计、续聊 UI 会话 |
一次性 Python 任务 → 官方 SDK;持续部署、跨语言、续聊 UI 会话 → 本网关。别混用:DeepSeek 的 sk-… 密钥打不开本网关;pip install deepseek-harness-sdk 也连不上本网关。
扩展性(面向其他插件)
网关在 Cordis 事件总线上发布三个事件,其他宿主插件用 ctx.on(...) 订阅(监听器随 fiber 回收,异常不影响网关):
gateway/session-created→{ sessionId, mode: 'created' | 'live' | 'resumed', workspace, cwd }gateway/message→{ sessionId, messageId, text }(每条助手回复提交时)gateway/turn-end→{ sessionId, turn, reason, detail }
典型用途:审计落盘、外部告警、转发 IM/Webhook、自定义限流旁路。
开发与测试
pnpm install
pnpm build # tsc
pnpm smoke # 对运行中的网关跑端到端冒烟
冒烟 env:DSH_AGW_BASE(默认 http://127.0.0.1:3080/api-gw/v1)、DSH_AGW_KEY(缺省则领一把)、DSH_AGW_PROMPT。CI(.github/workflows/ci.yml)跑构建 + 语法检查,可用仓库 vars 开启可选冒烟。
路线图
按「先安全、再体验、后生态」排序;各版本独立发布。
| 版本 | 主题 | 内容 |
|---|---|---|
| v0.1.0 | 基线(当前) | REST+SSE、设置卡片、reasoning/text 分离、工作区归属、会话接管、跨平台文档 |
| v0.2.0 | 多租户安全 ★ | 多密钥 CRUD/吊销、按密钥限流(429 + Retry-After)、工作区模型:每密钥独立 + 共享协作(shared/isolated)、按密钥审批策略、审计(每密钥请求/会话/token 用量)、会话持久化(重启恢复) |
| v0.3.0 | 管理界面 | 完整 admin 设置页(密钥/限额/工作区绑定、会话监控、用量审计、软开关)+ typert @Remote 配置面 + per-key preset 选择 |
| v0.4.0 | 双工流式 | webServer.registerUpgrade WebSocket 全双工;SSE 保留为轻量选项 |
| v0.5.0 | 生态与运维 | Python/Node HTTP 薄客户端(OpenAPI 生成——不是官方嵌入式 SDK,见上文)、部署指南(反代+TLS、Docker Compose)、指标/遥测导出、OpenAPI 生成进 CI |
不做/缓做:多进程横向扩展、内置 TLS 终止(反代职责)、OAuth/OIDC(按密钥模型稳定后再议)。
License
MIT
链接
同类插件
strukto-ai/mirage#dsh★ 3447
把文件系统与 bash 提供者换成 mirage 虚拟工作区:文件工具与 shell 命令作用于挂载的资源(RAM、S3、Redis、Slack、Gmail、Notion、Postgres)而非宿主磁盘,支持按挂载点设置读/写/执行模式、按命令选择沙箱(进程内 monty、pyodide、quickjs;远程 docker、e2b、daytona),并可在虚拟终端中安装 CLI(git、gh、slack、linear、ntn、gws,或自行注册的程序树)作为命令头词。
hust-open-atom-club/oh-dsh★ 196
社区发行版:TUI、桌面端与 Web UI 统一体验,分层安装、一步到位。
Jayden-X-L/forkprobe★ 66
同一任务并行试跑多个技能,对比结果选出最优。
vlln/plugin-registry★ 43
插件生态基建:浏览器面板管理官方 repository 插件(0 patch)+ make-dsh-plugin 插件开发引导技能。
forrestchang/dsh-multica-runtime★ 37
让 dsh 运行时跑在 Multica 上。
omdsh-dev/dsh-plugin-check★ 20
插件健康检查:扫描清单协议/patch 格式/构建陷阱,零依赖只读。