# win-agent 接入协议（P1）

服务端已在这台机器上跑起来：`127.0.0.1:8766`（WebSocket）+ `127.0.0.1:8767`（HTTP 控制面）。
对外只暴露一个入口：**`wss://agent.zzd1.com/win-agent`**（Caddy 反代到 127.0.0.1:8766）。

---

## 一、Agent 侧要实现的

### 1. 连接

```
wss://agent.zzd1.com/win-agent
```

出站连接，**不需要入站端口，不需要域名**。断线自动重连（建议 3s → 5s → 10s 退避）。

### 2. 注册（连上后第一帧，10 秒内）

```json
{"type":"register","code":"524E11B2","hostname":"WIN-DEMO","os":"Windows 11 23H2","user":"WIN-DEMO\\tester","agent":"0.1.0"}
```

- `code`：**一次性入网码**，由服务端签发（见下），**用一次即失效**
- 其余字段仅用于在控制台区分节点

成功回执：

```json
{"type":"registered","node":"n_f79bf2665c50","server_time":1792641621429}
```

**`node` 要存下来**，重启重连后重新注册会拿到新 node（P1 不做持久身份）。

### 3. 心跳

```json
{"type":"heartbeat"}
```

建议 20 秒一次。服务端不强制，但控制台的"最近在线"靠它。

### 4. 收任务 / 回结果

服务端下发：

```json
{"type":"task","id":"t_1","op":"exec","args":{"cmd":"whoami","timeout_ms":15000}}
```

Agent 回：

```json
{"type":"result","id":"t_1","ok":true,"out":"win-demo\\tester\r\n","err":""}
```

- `id` 必须原样带回
- `ok:false` 时把错误放进 `err`
- `out` 建议截断到 256 KB（大文件走 `files` 分块）

### 5. P1 要支持的 op

| op | args | 返回 |
| --- | --- | --- |
| `exec` | `{cmd, cwd?, timeout_ms?}` | `out` = stdout+stderr 合并 |
| `files.list` | `{path}` | `out` = JSON 字符串 |
| `files.get` | `{path, offset?, len?}` | `out` = base64 |
| `files.put` | `{path, data_b64, mode?}` | `out` = 写入字节数 |
| `screen` | `{format?}` | `out` = base64 PNG/JPEG |
| `input` | `{kind:"click"|"key"|"text", x?,y?,key?,text?}` | `out` = "" |

P2 再加：`adb.*`、`emulator.*`、`apk.install`、`mitm.*`、`har.pull`。

---

## 二、控制面 API（只监听 127.0.0.1:8767）

### 签发入网码

```bash
curl -s -X POST localhost:8767/codes -H 'content-type: application/json' -d '{"ttl_s":3600}'
# {"ok":true,"code":"A272A954","ttl_s":3600}
```

### 看节点

```bash
curl -s localhost:8767/nodes
# {"ok":true,"nodes":[{"node":"n_f79bf2665c50","info":{...},"remote":"127.0.0.1","last_seen_ms_ago":1926}]}
```

### 下发任务（同步等结果）

```bash
curl -s -X POST localhost:8767/task -H 'content-type: application/json' \
  -d '{"node":"n_f79bf2665c50","op":"exec","args":{"cmd":"whoami"}}'
# {"ok":true,"out":"win-demo\\tester","err":""}
```

### 踢下线

```bash
curl -s -X POST localhost:8767/kick -H 'content-type: application/json' -d '{"node":"n_f79bf2665c50"}'
```

### 健康检查

```bash
curl -s localhost:8767/health
# {"ok":true,"nodes":1,"pending":0,"codes":1,"uptime_s":2}
```

---

## 三、关闭码

| code | 含义 |
| --- | --- |
| 4001 | 10 秒内未注册 |
| 4002 | 首帧不是合法 JSON |
| 4003 | 首帧不是 `register` |
| 4004 | 入网码无效或已过期 |
| 4005 | 被控制面踢下线 |

---

## 四、已实测通过（2026-10-22）

```
入网码 524E11B2 签发
  -> Agent 注册，拿到 n_f79bf2665c50
  -> GET /nodes 可见（hostname=FAKE-PC, os=Windows 11）
  -> POST /task {"op":"exec","args":{"cmd":"whoami"}}
  -> 回包 {"ok":true,"out":"win-demo\\tester"}
  -> 审计日志 4 条（code_issued / register_ok / task_sent / task_result）
```

审计日志位置：`audit/win-agent/logs/agent-audit.jsonl`（一行一条 JSON）。

---

## 五、还没生效的一步

`agent.zzd1.com` 的 **A 记录当前填成了 `199.108.218.162`**，本机是 **`199.102.218.162`**（102，不是 108）。
DNS 修正之后才能加 Caddy 段并自动签 Let's Encrypt 证书；在那之前，Agent 可以先在服务器内网用 `ws://127.0.0.1:8766/win-agent` 联调。
