← 返回列表
需源码安装
A2S 生态同系列开源仓库
暂不能直接安装(需源码编译或环境不满足):仓库 package.json 标记 private,未发布到 npm,需从源码安装。 · 最近上游提交 2026/9/20 · 已提供中文文档
A2S 中继服务器和 Web 控制台,用于 Claude Code、Codex 和 DeepSeek Harness,支持配对、SSE、REST 和设备控制。
综合分
30.8
GitHub 分
30.8
用户评分
—
★ Stars
1
周下载量
—
安装插件(需先安装 dsh CLI 引擎:npm install -g @deepseek-ai/dsh)
dsh plugin --profile web add 23J1633/server-api仓库 package.json 标记 private,未发布到 npm,需从源码安装,改用 GitHub 源安装
信任档位:已验证本站已于 2 天前真实安装成功(L4 · 真实安装)
- 是什么
- dsh 原生插件 · chat
- 装得上吗
- 本站已真实安装成功(L4 · 真实安装,非静态推断)
- 安全吗
- 本站尚未对该插件做风险分级(暂未覆盖,不等同于无风险)
- 还在维护吗
- 活跃:最近一次提交在 5 天前
档位由下列信号合成:本站实装验证(真实安装,当前最高到 L4)· 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。下方各区块是它的证据明细。 验证判据与等级说明 →
🟢实装验证通过· 2026/9/23
由本站实装验证器在真实 dsh 环境安装成功,非静态推断。
数据截至 2026/9/20(元数据每日更新 · 实装验证按队列轮转,单条结论的验证时间见上方)
安装兼容性检查需源码安装
以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。
✗npm 包@a2s/server-api(未发布到 npm,仅可源码安装)
✓Node 引擎要求 >=20 · 基线 Node 22.19 满足
✓dsh CLI 依赖未声明 dsh 版本约束
✓入口文件main/exports/bin 已声明
仓库 package.json 标记 private,未发布到 npm,需从源码安装
验证方式:npm registry 存在性 + package.json 静态校验 · 最后验证 2026/9/20 20:44:15
用户评分
还没有人投票,来当第一个
订阅周报,不错过优质插件更新
每周一封 · 高评分插件 + 新用户活动
README
由 DeepSeek 最新模型翻译生成A2S server-api
中文
A2S 生态(同系列开源仓库)
A2S 按组件拆分为以下同系列仓库,所有者均为 23J1633。/ A2S is split into the following sibling repositories, all owned by 23J1633.
| 仓库 / Repository | 作用 / Role | GitHub |
|---|---|---|
| A2Switch | Windows 桌面控制中心 / Windows desktop control center | 23J1633/A2Switch |
| cc2server | Claude Code 桥接器 / Claude Code bridge | 23J1633/cc2server |
| codex2server | Codex 桥接器 / Codex bridge | 23J1633/codex2server |
| dsh2server | DeepSeek Harness 插件 / DeepSeek Harness plugin | 23J1633/dsh2server |
| server-api | 中转服务与 Web 控制台 / relay server and Web console | 23J1633/server-api |
| a2s_app | Flutter Android 客户端 / Flutter Android client | 23J1633/a2s_app |
server-api 是 A2S 的统一服务器端:接收本机 Claude Code、Codex、DeepSeek Harness 桥接器的主动连接,为浏览器或其他设备提供同一套控制 API,并附带多 Agent Web 控制台。
默认统一基路径是 /a2s-api。为了兼容已有 dsh2server 部署,/dsh-api 仍作为完整别名工作。
架构
cc2server ───┐
codex2server ├── WSS 或 HTTPS 长轮询 ──► server-api ──► Web 控制台
dsh2server ──┘ │
└──► REST / SSE 客户端
- Agent 始终主动连出,服务器不反向访问本机。
- WebSocket 是首选载体;/events + /inbox 提供功能等价的 HTTP 回退。
- 一个 a2sk_ key 可以绑定同设备的多个 Agent 实例。
- 每个实例仍有独立方法目录、能力位、会话、事件序号和订阅。
- 服务器不持久化会话正文,只持久化 key 白名单、服务器配置和控制台归档索引。
快速启动
要求 Node.js 20+:
cd server-api
npm install
本机明文测试
PowerShell:
$env:A2S_SERVER_NO_TLS = '1'
$env:A2S_SERVER_HOST = '127.0.0.1'
$env:A2S_SERVER_PORT = '50443'
$env:A2S_SERVER_DATA_DIR = "$PWD\.runtime-local"
npm start
Bash:
A2S_SERVER_NO_TLS=1 \
A2S_SERVER_HOST=127.0.0.1 \
A2S_SERVER_PORT=50443 \
A2S_SERVER_DATA_DIR="$PWD/.runtime-local" \
npm start
启动后:
| 用途 | 地址 |
|---|---|
| Web 控制台 | http://127.0.0.1:50443/ |
| 健康检查 | http://127.0.0.1:50443/a2s-api/health |
| Agent endpoint | http://127.0.0.1:50443/a2s-api |
| WebSocket | ws://127.0.0.1:50443/a2s-api/ws |
首次访问管理接口或控制台登录时需要数据目录中的 admin-key.txt。这是服务器所有者凭据,只保存在服务器本地及所有者的浏览器会话中;A2Switch 不保存也无法读取它。
控制台左上角与中央欢迎区会随当前实例一起切换 Claude Code、Codex、DeepSeek Harness 的真实品牌图形、名称和输入提示;尚未选择实例或等待登录时显示项目方提供的 A2S 图标。所有图标均锁定方形尺寸,不会被侧栏布局挤压变形。
生产部署
Linux 一行部署(推荐)
Ubuntu、Debian、RHEL、Rocky Linux、AlmaLinux 等使用 systemd 的 Linux 服务器可直接执行:
bash
sudo bash -c 'set -Eeuo pipefail; t="$(mktemp)"; trap "rm -f \\\"$t\\\"" EXIT; for u in https://raw.githubusercontent.com/23J1633/server-api/main/install.sh https://github.com/23J1633/server-api/raw/refs/heads/main/install.sh https://cdn.jsdelivr.net/gh/23J1633/server-api@v0.4.1/install.sh https://gcore.jsdelivr.net/gh/23J1633/server-api@v0.4.1/install.sh https://fastly.jsdelivr.net/gh/23J1633/server-api@v0.4.1/install.sh; do echo "[A2S] bootstrap: $u" >&2; if curl -4fL --connect-timeout 15 --max-time 120 --show-error --retry 2 --retry-delay 2 -o "$t" "$u" && bash -n "$t"; then bash "$t"; exit $?; fi; done; echo "[A2S] ERROR: no installer source is reachable" >&2; exit 1'
该脚本会自动完成 Node.js 22 检查/安装、GitHub 源码下载、生产依赖安装、低权限 a2s 账号、systemd 自启、启动健康检查和失败回滚。重复执行同一条命令即可升级;/var/lib/a2s-server 中的数据、已登记设备 key 和 admin-key.txt 都会保留。成功后终端会显示大型 A2S 字符标识、访问地址、管理员密钥明文、密钥文件位置和维护命令;管理员密钥属于服务器所有者凭据,请勿把终端输出发给不受信任的人。
重要:一键安装后的 A2S 服务只监听 127.0.0.1:50443 明文 HTTP。要使用公网 https:// / wss://,必须在 Nginx、OpenResty、Caddy 或 1Panel 中配置反向代理并由代理终止 TLS;不要把 https://域名:50443 直接指向 A2S 后端。证书配置在反向代理中完成,A2S 后端不负责申请或读取面板证书。反向代理目标保持为 http://127.0.0.1:50443。
如果需要让反向代理访问其他内网地址,再显式调整监听地址:
bash
set -o pipefail; curl -4fL --connect-timeout 15 --max-time 120 --show-error https://raw.githubusercontent.com/23J1633/server-api/main/install.sh \
| sudo env A2S_SERVER_HOST=0.0.0.0 A2S_SERVER_PORT=50443 bash
如果默认端口已被占用,交互式安装会提供三个选项:关闭占用进程并继续使用原端口、保留原进程并自动选择新端口、取消安装。没有可用 TTY 的无人值守安装默认保留原进程并选择新端口;也可以显式指定:
bash
停止占用 50443 的进程并继续使用原端口(可能中断其他服务)
sudo env A2S_PORT_CONFLICT_ACTION=stop bash /tmp/a2s-install.sh
保留占用进程,自动选择 50444 起的第一个空闲端口
sudo env A2S_PORT_CONFLICT_ACTION=new bash /tmp/a2s-install.sh
发现冲突立即退出
sudo env A2S_PORT_CONFLICT_ACTION=abort bash /tmp/a2s-install.sh
下载阶段会依次尝试 GitHub API、GitHub archive 和 codeload;npm 依赖会尝试官方 registry 与公开镜像,并在日志中显示当前来源。所有失败都会保留原版本并输出具体诊断。完全没有网络时,先把包含 node_modules 的离线归档复制到服务器,再设置 A2S_ARCHIVE_PATH=/path/server-api-offline.tar.gz A2S_OFFLINE=1;远程安装不可能在零网络条件下自行取得文件。
自定义配置与预检:
bash
只输出计划,不修改服务器
set -o pipefail; curl -4fL --connect-timeout 15 --max-time 120 --show-error https://raw.githubusercontent.com/23J1633/server-api/main/install.sh | bash -s -- --dry-run
重写已存在的环境文件(普通升级默认保留它)
set -o pipefail; curl -4fL --connect-timeout 15 --max-time 120 --show-error https://raw.githubusercontent.com/23J1633/server-api/main/install.sh \
| sudo env A2S_RECONFIGURE=1 A2S_SERVER_HOST=127.0.0.1 A2S_SERVER_PORT=50443 bash
部署完成后常用命令:
bash
sudo systemctl status a2s-server
sudo journalctl -u a2s-server -f
sudo cat /var/lib/a2s-server/admin-key.txt
如果服务器出口必须经过 HTTP(S) 或 SOCKS5 代理,先把代理地址替换为实际值;curl 和安装脚本会继承这些环境变量:
bash
sudo env \
HTTPS_PROXY=http://PROXY_HOST:PROXY_PORT \
HTTP_PROXY=http://PROXY_HOST:PROXY_PORT \
ALL_PROXY=http://PROXY_HOST:PROXY_PORT \
bash -c 'set -o pipefail; curl -4fL --connect-timeout 15 --max-time 120 --show-error https://raw.githubusercontent.com/23J1633/server-api/main/install.sh | bash'
SOCKS5 代理将三个值改为 socks5h://PROXY_HOST:PROXY_PORT。不要把包含用户名或密码的代理命令分享给他人。
卸载服务和程序但保留设备数据、管理员密钥及环境配置:
set -o pipefail; curl -4fL --connect-timeout 15 --max-time 120 --show-error https://raw.githubusercontent.com/23J1633/server-api/main/install.sh \
| sudo bash -s -- --uninstall
只有确定不再需要任何设备登记、归档和管理员密钥时,才执行完全清理;该模式也会删除由安装器创建的专用系统用户/用户组,但不会删除同名的预存账号:
set -o pipefail; curl -4fL --connect-timeout 15 --max-time 120 --show-error https://raw.githubusercontent.com/23J1633/server-api/main/install.sh \
| sudo bash -s -- --uninstall --purge
两种卸载模式都可以先追加 --dry-run 查看精确目标而不修改服务器。
只有在 server-api 仓库已公开且 main 分支包含 install.sh 后,上述 raw GitHub 命令才能被新服务器访问。
HTTPS/WSS 反向代理(必需)
公网部署必须由反向代理提供 HTTPS/WSS,A2S Node 服务保持回环 HTTP。证书路径、域名、续期和 443/自定义 HTTPS 端口都配置在反向代理或 1Panel 网站中;后端端口 50443 不应暴露到公网。
Nginx/OpenResty 示例(证书路径替换为你已有的证书):
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:50443;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 600s;
proxy_send_timeout 600s;
proxy_buffering off;
}
}
Caddy 示例(Caddy 自动管理证书和 WebSocket 升级):
example.com {
reverse_proxy 127.0.0.1:50443
}
在 1Panel 中选择“网站 → 反向代理”,目标填写 http://127.0.0.1:50443,绑定已有证书,开启 WebSocket/长连接,并确保 /、/a2s-api/(以及需要兼容旧客户端时的 /dsh-api/)都转发。客户端端点填写 https://example.com/a2s-api,WebSocket 端点由插件自动派生为 /a2s-api/ws。
直接运行:
cd /opt/a2s/server-api
npm ci --omit=dev
mkdir -p /home/a2s/.a2s-server
cp config.example.json /home/a2s/.a2s-server/config.json
export A2S_SERVER_DATA_DIR=/home/a2s/.a2s-server
./start.sh start
./start.sh status
./start.sh logs
start.sh 支持 start、stop、restart、status、logs,PID 与日志都写入 A2S_SERVER_DATA_DIR。旧 DSH_RELAY_ 环境变量仍可使用,但新部署应使用 A2S_SERVER_。
systemd 示例:
[Unit]
Description=A2S unified agent relay
After=network-online.target
[Service]
Type=simple
User=a2s
WorkingDirectory=/opt/a2s/server-api
Environment=A2S_SERVER_DATA_DIR=/home/a2s/.a2s-server
ExecStart=/usr/bin/node /opt/a2s/server-api/server.js
Restart=always
RestartSec=3
[Install]
WantedBy=multi-user.target
反向代理必须允许 WebSocket upgrade,并关闭 SSE/长轮询响应缓冲;同时把读写超时设置为至少 600 秒,避免长任务或断线恢复期间被代理提前关闭。
配置
加载顺序:内置默认值 → 数据目录的 config.json → 环境变量覆盖。模板见 config.example.json。
| 字段 | 默认值 | 说明 |
|---|---|---|
| host | 0.0.0.0 | 监听地址 |
| port | 50443 | 监听端口 |
| basePath | /a2s-api | 统一 API 基路径 |
| legacyBasePaths | [/dsh-api] | 完整兼容别名 |
| tls.cert / tls.key | 空(反向代理部署) | 仅手工直连 Node TLS 模式使用;一键安装不会写入面板证书路径,公网请使用反向代理 |
| tls.watchMs | 600000 | 手工直连 Node TLS 模式的证书热重载周期;反向代理部署由代理负责续期 |
| dataDir | ~/.a2s-server | key、配置、日志与归档索引目录 |
| eventBufferSize | 2000 | 每实例内存事件窗口 |
| maxPayloadBytes | 1048576 | 服务器单帧负载上限 |
| heartbeatMs | 30000 | 建议心跳周期 |
| offlineAfterMs | 90000 | 无入站帧后判离线 |
| helloTimeoutMs | 20000 | 半开连接握手超时 |
| requestTimeoutMs | 120000 | 默认远程方法超时 |
| methodTimeoutMs | 见模板 | 慢方法单独超时 |
| autoSubscribe | 运行中会话 + 流式输出 | 新实例握手后的服务端订阅 |
| consoleBacklogLimit | 500 | 控制台单次事件窗口 |
环境变量:
| 新变量 | 旧兼容变量 | 说明 |
|---|---|---|
| A2S_SERVER_CONFIG | DSH_RELAY_CONFIG | 配置文件完整路径 |
| A2S_SERVER_DATA_DIR | DSH_RELAY_DATA_DIR | 数据目录 |
| A2S_SERVER_HOST | DSH_RELAY_HOST | 监听地址 |
| A2S_SERVER_PORT | DSH_RELAY_PORT | 监听端口 |
| A2S_SERVER_NO_TLS=1 | DSH_RELAY_NO_TLS=1 | 强制明文启动 |
| A2S_SERVER_LOG_LEVEL | DSH_RELAY_LOG_LEVEL | 日志级别 |
数据目录
~/.a2s-server/
├─ admin-key.txt 管理密钥,0600
├─ keys.json 设备 key 白名单,0600
├─ archives.json 控制台归档索引,不含会话正文
├─ config.json 生效配置
├─ relay.log 运行日志
└─ relay.pid start.sh 的 PID 文件
数据目录必须位于 Web 静态目录之外,且只允许服务账号读取。
配对与统一设备 key
使用 A2Switch
在 A2Switch 中填写 endpoint 并复制“本机设备 key”。服务器所有者使用 admin-key.txt 登录云端控制台,在“机器与密钥”页粘贴该本机 key 完成登记。其底层管理请求等价于:
POST /a2s-api/keys
x-admin-key:
content-type: application/json
{
"key": "a2sk_...",
"label": "workstation",
"deviceId": "a2s-0123456789ab"
}
使用 curl
curl -X POST https://example.com/a2s-api/keys \
-H 'content-type: application/json' \
-H 'x-admin-key: YOUR_ADMIN_KEY' \
-d '{"key":"a2sk_...","label":"workstation","deviceId":"a2s-0123456789ab"}'
Claude、Codex 与 DSH 随后用相同 key、相同 deviceId、不同 instanceId 连接。KeyStore 会把新实例 ID 追加到同一条 key 记录,而不是覆盖已有实例。
旧 dshk_ key 仍可登记。管理接口永远只返回 key 指纹,不返回完整 key。
管理 API
除健康检查外,管理端点需要:
x-admin-key:
服务与设备
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /a2s-api/health | 免鉴权健康状态、协议与统计 |
| GET | /a2s-api/stats | 服务统计和近期日志 |
| GET | /a2s-api/devices | 按 key/deviceId 聚合的设备及 Agent |
| GET | /a2s-api/instances | 全部 Agent 实例摘要 |
| GET | /a2s-api/instances/:id | 实例详情、会话、工作区、任务 |
控制与事件
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /instances/:id/request | 下发统一方法 {method, params, timeoutMs?} |
| POST | /instances/:id/subscribe | 调整 topic、session、assistant stream 订阅 |
| POST | /instances/:id/unsubscribe | 取消订阅 |
| GET | /instances/:id/events?since=&limit= | 读取实例事件窗口 |
| GET | /instances/:id/sessions | 读取缓存会话状态和待决交互 |
| GET | /instances/:id/session-events/:sid | 会话事件、流、目标、todo 与快照 |
| GET | /instances/:id/snapshot/:sid | 会话快照 |
| GET | /console/stream | 控制台 SSE:实例和原始帧 |
表中省略的路径均以 /a2s-api 开头。
远程调用示例:
bash
curl -X POST 'https://example.com/a2s-api/instances/a2s-xxx%3Acodex/request' \
-H 'x-admin-key: YOUR_ADMIN_KEY' \
-H 'content-type: application/json' \
-d '{"method":"session.list","params":{"limit":50}}'
服务器不硬编码所有 Agent 方法;它把请求发给目标实例,实例按能力返回结果或稳定错误码。常见错误状态会映射为合理 HTTP 状态,例如 instance_offline→503、timeout→504、invalid_params→400。
归档
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /instances/:id/archives | 控制台归档索引 |
| POST | /instances/:id/sessions/:sid/archive | 归档;scope=server 或 host |
| DELETE | /instances/:id/sessions/:sid/archive | 从控制台归档恢复 |
scope=server 只隐藏控制台条目;scope=host 还会调用 Agent 的 session.archive(目标支持时)。
key 管理
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /keys | 脱敏 key 列表 |
| POST | /keys | 登记/幂等更新一个 key |
| PATCH | /keys/:id | 改备注、绑定 ID 或替换 key |
| DELETE | /keys/:id | 吊销并立即关闭关联 Agent 链路 |
| POST | /instances/:id/rotate-key | 让单实例执行远程 key 轮换 |
共享 key 同时服务三个 Agent 时,不应从某一个实例单独远程轮换,否则其他实例仍持有旧 key。A2Switch 的统一轮换会协调服务器与本机配置,是推荐方式。
Agent 传输协议
WebSocket
连接 /a2s-api/ws?v=1,随后发送 hello,其中包含 instanceId、deviceId、agentType、能力位、插件版本与设备 key。也支持 Bearer header 或 query key 的预鉴权模式。
HTTP 回退
- POST /a2s-api/events:Agent 上行帧;
- GET|POST /a2s-api/inbox:Agent 长轮询下行;
- 两种载体共享同一帧格式和鉴权语义;
- 收到 bye 后立即标离线,无需等待超时。
关键行为
- hello.ack 返回心跳、服务端时间、订阅与恢复水位;
- 每实例保留有限事件窗口,支持断线续传;
- 请求有唯一 ID、超时与稳定错误码;
- 同实例可有多条链路,状态按最近入站活动刷新;
- key 吊销立即断开该 key 关联的所有实例;
- 不同 key 不得抢占已绑定实例 ID。
协议细节可参考 ../dsh2server/docs/API.md。
Web 控制台
浏览器打开服务器根路径,输入管理密钥即可使用。主要功能:
- 机器下拉框按 deviceId 聚合,只切换电脑;切到另一台机器时优先保留当前 Agent 类型;
- 点击左上角原有品牌图标/字标,在当前机器的 Claude Code、Codex、DeepSeek Harness 之间切换;品牌区的真实图标、字标和布局保持不变;
- 会话创建、选择、重命名、分叉、归档、搜索;
- 提示词发送、流式回复、中断、模型和权限选择;
- 本地提交回显会由 requestId/clientMessageId/source.rpcId 与持久消息原位替换,不会在回答结束后重复出现;
- 服务器缓存的流式片段只会在会话仍处于运行状态时恢复;已完成会话以持久事件为准,不会把旧流再次追加成一条助手回复;
- 工具调用、diff、token/时延、轨迹时间线;
- 对话输入栏始终把命令、权限、模型和发送按钮保持在同一工具行;轨迹页是独立检查视图,不显示对话输入框;
- 长对话只先取最新窗口,持续向上滚动会提前并连续加载更早页,同时保持当前阅读锚点;
- 审批与结构化问题就地应答;
- 文件、任务、目标等面板按实例能力位动态出现;
- key 登记、编辑、吊销及调试方法调用;
- 浅色/深色主题、字体和刷新间隔。
- 首次访问时自动识别浏览器/系统语言;可在“设置 → 通用设置 → 语言”中选择自动跟随、简体中文或 English,选择按浏览器保存。
控制台使用 /api-config.js 获取运行时 basePath,部署到自定义基路径时无需重新构建前端。
测试
完整服务器回归:
bash
npm test
包含:
- 5 项 Node relay/key/机器与 Agent 两级选择单元测试;
- 30 项对话转录与 UI 模型回归(包括隐藏 DSH 插件注入的运行上下文和隐藏工作区过滤);
- 46 项真实 HTTP/WebSocket 自检,包括鉴权、订阅、流式输出、审批、重连、HTTP 回退、多机器、key 编辑和吊销。
单独运行:
bash
npm run test:transcript
npm run selftest
对正在运行的本机服务验证手机登录、设备解锁、幂等健康请求、心跳、一次性配对、重复兑换、移动客户端改名和撤销:
powershell
$env:A2S_SERVER_DATA_DIR = Join-Path $env:LOCALAPPDATA 'A2S\server'
npm run test:mobile-smoke
脚本读取数据目录中的管理密钥,但不会把完整密钥或 Bearer token 写入报告。不要省略 A2S_SERVER_DATA_DIR,否则脚本可能读取到另一个开发实例的数据目录。
真实浏览器 UI 验收脚本:
powershell
$env:A2S_ADMIN_KEY = (Get-Content "$env:LOCALAPPDATA\A2S\server\admin-key.txt" -Raw).Trim()
$env:A2S_INSTANCE = 'a2s-xxxxxxxxxxxx:codex'
$env:A2S_AGENT_TYPE = 'codex'
..\A2Switch\node_modules\.bin\electron.cmd .\scripts\ui-live-smoke.cjs
$env:A2S_SESSION = ''
..\A2Switch\node_modules\.bin\electron.cmd .\scripts\ui-history-smoke.cjs
Codex:点击真实“新会话”菜单并确认空白 thread 可打开
..\A2Switch\node_modules\.bin\electron.cmd .\scripts\ui-new-session-smoke.cjs
Claude:恢复旧历史,并通过真实 UI 新建、重命名、移除工作区
$env:A2S_INSTANCE = 'a2s-xxxxxxxxxxxx:claude'
..\A2Switch\node_modules\.bin\electron.cmd .\scripts\ui-claude-history-workspace-smoke.cjs
ui-live-smoke.cjs 会真实创建会话、发送提示、等待流式与最终回复,并检查单一用户气泡、同排输入工具栏、轨迹无输入框及提示层销毁;ui-history-smoke.cjs 用一次持续向上滚动验证至少连续加载两页历史。其余两个脚本分别覆盖 Codex 空白新会话兼容,以及 Claude 原生历史恢复与工作区完整 UI 操作。所有脚本都使用独立 Electron 分区,不持久化管理员密钥。
跨项目真实闭环在仓库根目录执行:
powershell
node .\scripts\full-loop-test.mjs
安全注意事项
- 管理密钥权限高于设备 key,必须单独保护,不应发送给 Agent。
- KeyStore 使用 SHA-256 定长摘要后的恒定时间比较;API 和日志只显示指纹。
- keys.json 与 admin-key.txt 尝试设置为 0600;还应使用独立低权限系统账号。
- 公网明文 HTTP 会泄露 Agent 内容和密钥,禁止使用。
- 控制台静态文件路径有目录穿越防护;未知扩展名不会被提供。
- 反向代理需要限制请求体大小并保留服务器的 maxPayloadBytes 约束。
- server-api 是控制面,不是租户隔离平台;需要多租户时应在外层增加身份、授权、审计和网络隔离。
目录结构
text
server-api/
├─ server.js HTTP/HTTPS、静态资源和 WebSocket 入口
├─ lib/
│ ├─ admin.js REST/SSE 管理面
│ ├─ relay.js 实例注册、调用、key 绑定
│ ├─ instance.js 实例状态与缓存窗口
│ ├─ carriers.js HTTP 长轮询载体
│ ├─ link.js WebSocket/HTTP 链路抽象
│ ├─ keystore.js 设备 key 与管理密钥
│ ├─ archive-store.js 控制台归档索引
│ └─ config.js 配置加载
├─ public/ 多 Agent Web 控制台
│ └─ js/agent-selection.js 机器聚合与 Agent 选择规则
├─ scripts/ 单元、自检和转录回归
├─ sim/ 协议模拟器
├─ config.example.json 生产配置模板
├─ install.sh Linux 一行安装、升级与卸载
└─ start.sh Linux 手工后台管理脚本
许可证
代码采用 MIT License,见 LICENSE。第三方名称和品牌图标属于各自权利人,仅用于兼容性标识。
English
server-api 是统一的 A2S 中继。Claude Code、Codex 和 DeepSeek Harness 桥接以出站方式连接到它;浏览器和移动客户端使用一个 REST/SSE/WebSocket API 以及一个多 Agent Web 控制台。规范基础路径为 /a2s-api;/dsh-api 仍作为完整兼容别名保留。
架构与行为
- WebSocket 是首选的 Agent 载体;/events 加上 /inbox 提供等效的 HTTP 长轮询。
- 一个 a2sk_ 设备 key 可以认证同一物理机上的多个 Agent 实例。
- 每个实例保持独立的能力、方法、会话、事件序列、订阅和存活状态。
- 会话正文会被中继,而不是作为服务器端转录存储。持久化数据包括 key 允许列表、配置、移动端凭据和控制台归档索引。
- 心跳、服务器 ping、离线检测、有界事件缓冲区、重放、请求超时和幂等性支持从临时断连中恢复。
Linux 一行部署
在基于 systemd 的 Ubuntu、Debian、RHEL、Rocky Linux 或 AlmaLinux 服务器上:
bash
sudo bash -c 'set -Eeuo pipefail; t="$(mktemp)"; trap "rm -f \\\"$t\\\"" EXIT; for u in https://raw.githubusercontent.com/23J1633/server-api/main/install.sh https://github.com/23J1633/server-api/raw/refs/heads/main/install.sh https://cdn.jsdelivr.net/gh/23J1633/server-api@v0.4.1/install.sh https://gcore.jsdelivr.net/gh/23J1633/server-api@v0.4.1/install.sh https://fastly.jsdelivr.net/gh/23J1633/server-api@v0.4.1/install.sh; do echo "[A2S] bootstrap: $u" >&2; if curl -4fL --connect-timeout 15 --max-time 120 --show-error --retry 2 --retry-delay 2 -o "$t" "$u" && bash -n "$t"; then bash "$t"; exit $?; fi; done; echo "[A2S] ERROR: no installer source is reachable" >&2; exit 1'
安装程序会验证/配置 Node.js 22,下载 GitHub release,安装生产依赖,创建受限的 a2s 账户,安装/启用可重启的 systemd 单元,执行健康检查,并在发布失败时回滚。重新运行同一命令即可升级,同时保留 /var/lib/a2s-server。
成功后,终端会打印一个大型 A2S 横幅、控制台和 Agent 端点、完整的管理员密钥、其文件路径、状态/日志命令,以及升级/卸载命令。请将该终端输出视为敏感信息,因为管理员密钥可以管理所有已注册设备。
重要:一键安装程序会在 127.0.0.1:50443 上以纯 HTTP 运行 A2S。公网 https:// / wss:// 访问需要反向代理(Nginx、OpenResty、Caddy 或 1Panel)来终止 TLS;不要将 https://your-domain:50443 直接指向 A2S 后端。请在代理中配置证书、续期和公网 HTTPS 端口,并保持后端端口 50443 为私有。仅当代理位于另一台主机或已了解网络边界时,才设置 A2S_SERVER_HOST=0.0.0.0。
Nginx/OpenResty 示例(替换证书路径):
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:50443;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 600s;
proxy_send_timeout 600s;
proxy_buffering off;
}
}
Caddy 示例(Caddy 管理证书和 WebSocket 升级):
example.com {
reverse_proxy 127.0.0.1:50443
}
在 1Panel 中,创建一个反向代理站点,目标为 http://127.0.0.1:50443,绑定现有证书,启用 WebSocket/长连接,并代理 /、/a2s-api/(以及如果需要兼容旧客户端,则代理 /dsh-api/)。为客户端配置 https://example.com/a2s-api;Agent WebSocket 路径为 /a2s-api/ws。
当请求的端口被占用时,交互式安装会提供三个选项:停止进程并保留端口、保留该进程并选择新的空闲端口,或中止。没有可用 TTY 的安装会默认保留现有进程并选择新端口。当自动化需要确定性结果时,请显式设置操作:
停止使用 50443 的进程并保留原端口(可能会中断另一个服务)
sudo env A2S_PORT_CONFLICT_ACTION=stop bash /tmp/a2s-install.sh
保留该进程,并从 50444 开始选择第一个空闲端口
sudo env A2S_PORT_CONFLICT_ACTION=new bash /tmp/a2s-install.sh
发生冲突时立即中止
sudo env A2S_PORT_CONFLICT_ACTION=abort bash /tmp/a2s-install.sh
源码下载会自动尝试 GitHub API、GitHub archive 和 codeload 端点;npm 依赖会尝试官方 registry 和公共备用源,并在日志中显示所选来源。升级失败会保留上一个版本并打印诊断信息。对于完全离线的服务器,请复制一个已包含 node_modules 的归档文件,并设置 A2S_ARCHIVE_PATH=/path/server-api-offline.tar.gz A2S_OFFLINE=1;在零网络访问的情况下,任何远程安装程序都无法获取文件。
如果服务器必须通过代理访问 GitHub,请将代理环境变量同时传递给 curl 和安装程序(将占位符替换为真实端点):
sudo env \
HTTPS_PROXY=http://PROXY_HOST:PROXY_PORT \
HTTP_PROXY=http://PROXY_HOST:PROXY_PORT \
ALL_PROXY=http://PROXY_HOST:PROXY_PORT \
bash -c 'set -o pipefail; curl -4fL --connect-timeout 15 --max-time 120 --show-error https://raw.githubusercontent.com/23J1633/server-api/main/install.sh | bash'
对于 SOCKS5,请为所有三个值使用 socks5h://PROXY_HOST:PROXY_PORT。请妥善保管代理凭据。
预览而不更改服务器
set -o pipefail; curl -4fL --connect-timeout 15 --max-time 120 --show-error https://raw.githubusercontent.com/23J1633/server-api/main/install.sh | bash -s -- --dry-run
移除服务/应用程序,但保留数据、密钥和环境
set -o pipefail; curl -4fL --connect-timeout 15 --max-time 120 --show-error https://raw.githubusercontent.com/23J1633/server-api/main/install.sh | sudo bash -s -- --uninstall
显式移除所有持久化数据、密钥、环境以及安装程序创建的服务用户/组
set -o pipefail; curl -4fL --connect-timeout 15 --max-time 120 --show-error https://raw.githubusercontent.com/23J1633/server-api/main/install.sh | sudo bash -s -- --uninstall --purge
在任一卸载命令后追加 --dry-run 以检查其目标。清除形式有意设计为显式且不可逆。
本地开发
服务器包支持 Node.js 20+。
npm install
A2S_SERVER_NO_TLS=1 \
A2S_SERVER_HOST=127.0.0.1 \
A2S_SERVER_PORT=50443 \
A2S_SERVER_DATA_DIR="$PWD/.runtime-local" \
npm start
对于本地开发,控制台位于 http://127.0.0.1:50443/,健康检查位于 /a2s-api/health,Agent 端点位于 /a2s-api,WebSocket 位于 /a2s-api/ws。首次启动会在数据目录中创建 admin-key.txt、keys.json 和 config.json。
配置与凭据
配置按以下顺序加载:默认值、数据目录中的 config.json,然后是环境变量覆盖。常见变量有 A2S_SERVER_CONFIG、A2S_SERVER_DATA_DIR、A2S_SERVER_HOST、A2S_SERVER_PORT、A2S_SERVER_NO_TLS 和 A2S_SERVER_LOG_LEVEL。对于受支持的公开部署,TLS 证书位于反向代理中;一行安装脚本不会读取面板证书路径。可选的 tls.cert / tls.key 字段仅用于高级手动直接 Node TLS,启用时支持证书热重载。
管理员密钥用于授权服务器管理,必须保留在服务器上或服务器所有者的浏览器会话中。设备密钥用于授权一台工作站,并可由该工作站的三台 Agent 共享。移动端配对会签发一个独立的、可撤销的移动端凭据;它不会将管理员密钥复制到应用中。
API 与协议
公开的健康检查和运行时配置端点不需要管理员密钥。用于实例、密钥、归档、日志、移动设备和配对的管理端点需要 x-admin-key。Agent 载体使用设备密钥进行身份验证,并以 protocol-v1 的 hello/hello.ack 开始。请求和响应使用稳定的 ID;事件帧具有单调递增的序列号以支持重放。完整路由和帧目录请参见上文中文参考以及 lib/ 下的源代码。
测试与运维
npm test
npm run test:mobile-smoke
测试套件涵盖中继身份验证、三 Agent 设备密钥共享、Agent 选择、安装脚本语法/试运行、会话记录渲染、WebSocket 和 HTTP 传输、重放、审批响应、存活检测、密钥轮换/撤销以及多机隔离。生产部署应使用 TLS 反向代理,允许 WebSocket 升级,禁用 SSE/长轮询的缓冲,保护数据目录,并监控 systemd 日志。
许可证
MIT。第三方名称和品牌标识仅用于兼容性识别,其所有权仍归各自的权利持有人所有。