Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions .github/workflows/qa.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ on:
- 'tests/test597-dashboard-slash-namespace/**'
- 'tests/test679-task-trace/**'
- 'tests/lib/**'
- 'docs-site/**'
- 'docs/doc-source-pins-baseline.txt'
- 'scripts/check-doc-source-pins.py'
- 'tests/test831-doc-source-pins/**'
push:
branches: [main]
paths:
Expand Down Expand Up @@ -70,6 +74,10 @@ on:
- 'tests/test597-dashboard-slash-namespace/**'
- 'tests/test679-task-trace/**'
- 'tests/lib/**'
- 'docs-site/**'
- 'docs/doc-source-pins-baseline.txt'
- 'scripts/check-doc-source-pins.py'
- 'tests/test831-doc-source-pins/**'

# Older runs on the same ref get cancelled — saves minutes when a PR is
# updated rapidly. main pushes run independently.
Expand Down Expand Up @@ -244,3 +252,25 @@ jobs:
echo "================ $f ================"
tail -60 "$f"
done

doc-source-pins:
name: doc source-pin floor (Docker)
runs-on: ubuntu-latest
timeout-minutes: 8
steps:
- uses: actions/checkout@v4

# 🔴 这道门守的是下限,不是正确性:它只保证「已知失效的行号 pin 不再变多」。
# 实测召回率 5/10 —— 抓不到「锚点指着一行正常代码、只是不是它声称的那
# 一行」。见 #831 与 scripts/check-doc-source-pins.py 的文件头。
# 别拿这个 job 的绿色去论证 #831 已解决。
- name: Build test831-doc-source-pins
run: |
docker build \
--build-arg TEST831_SOURCE_COMMIT="$GITHUB_SHA" \
--build-arg TEST831_RUNSH_BLOB="$(git rev-parse "HEAD:tests/test831-doc-source-pins/run.sh")" \
-t anet-test831-doc-source-pins \
-f tests/test831-doc-source-pins/Dockerfile .

- name: Run test831-doc-source-pins
run: docker run --rm --network none anet-test831-doc-source-pins
80 changes: 40 additions & 40 deletions docs-site/docs/api/mcp-tools.md

Large diffs are not rendered by default.

18 changes: 9 additions & 9 deletions docs-site/docs/api/rest.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ curl -X POST http://localhost:9200/api/auth/register \
}
```

`user` 对象 5 字段对照 [`server/src/auth.ts:7-13`](https://github.com/sleep2agi/agent-network/blob/main/server/src/auth.ts#L7) `AuthUser` interface(`display_name` / `email` 可为 `null`);`token` 是 `utok_` 给 CLI/Dashboard 用,`network_token` 是 `ntok_` 给注册时自动创建的那个网络里的 agent 用。
`user` 对象 5 字段对照 [`auth.ts`](https://github.com/sleep2agi/agent-network/blob/main/server/src/auth.ts) 搜 `interface AuthUser` `AuthUser` interface(`display_name` / `email` 可为 `null`);`token` 是 `utok_` 给 CLI/Dashboard 用,`network_token` 是 `ntok_` 给注册时自动创建的那个网络里的 agent 用。

**常见 4xx**(verify [`auth.ts register()`](https://github.com/sleep2agi/agent-network/blob/main/server/src/auth.ts)):

Expand Down Expand Up @@ -194,13 +194,13 @@ curl -X POST http://localhost:9200/api/auth/login \
}
```

`user` 对象 5 字段同 register 响应(注 `email` 可为 `null`);`network_id` 是该用户作为 owner 的 default network([`auth.ts:113-115`](https://github.com/sleep2agi/agent-network/blob/main/server/src/auth.ts#L113) 取 `ORDER BY role = 'owner' DESC LIMIT 1`)。每次 login 都签发**新的** `utok_`(不撤销已有,多设备登录互不踢,[`auth.ts:102-110`](https://github.com/sleep2agi/agent-network/blob/main/server/src/auth.ts#L102))。
`user` 对象 5 字段同 register 响应(注 `email` 可为 `null`);`network_id` 是该用户作为 owner 的 default network([`auth.ts:113-115`](https://github.com/sleep2agi/agent-network/blob/main/server/src/auth.ts#L113) 取 `ORDER BY role = 'owner' DESC LIMIT 1`)。每次 login 都签发**新的** `utok_`(不撤销已有,多设备登录互不踢,[`auth.ts`](https://github.com/sleep2agi/agent-network/blob/main/server/src/auth.ts) 搜 `// User token (utok_) — not bound to network, for CLI/Dashboard login`)。

**常见 4xx**(verify [`auth.ts login()`](https://github.com/sleep2agi/agent-network/blob/main/server/src/auth.ts)):

| 状态 | `error` 值 | 触发条件 |
|------|------------|---------|
| 401 | `invalid username or password` | 用户名不存在 **或** 密码哈希不匹配([`auth.ts:99-100`](https://github.com/sleep2agi/agent-network/blob/main/server/src/auth.ts#L99) 故意把两种错误合并成同一文案,避免 username enumeration);server 同时写 `login_failed` audit |
| 401 | `invalid username or password` | 用户名不存在 **或** 密码哈希不匹配([`auth.ts`](https://github.com/sleep2agi/agent-network/blob/main/server/src/auth.ts) 搜 `invalid username or password`(全仓 2 处) 故意把两种错误合并成同一文案,避免 username enumeration);server 同时写 `login_failed` audit |
| 429 | `rate_limited` | 超过 10/分 IP rate limit([`server.ts`](https://github.com/sleep2agi/agent-network/blob/main/server/src/server.ts);触发时写 `login_rate_limited` audit + clientIP)|

**速率限制**:10 次/分钟 per IP。
Expand Down Expand Up @@ -337,7 +337,7 @@ curl -X POST http://localhost:9200/api/auth/password \

**关键副作用** (verify [`auth.ts:267-282 changePassword + revokeOtherUserTokens`](https://github.com/sleep2agi/agent-network/blob/main/server/src/auth.ts#L267) + [`server.ts`](https://github.com/sleep2agi/agent-network/blob/main/server/src/server.ts)):
1. **当前调用方的 `utok_`** (`resolved.tokenId`) 立即撤销([`server.ts`](https://github.com/sleep2agi/agent-network/blob/main/server/src/server.ts) `revokeToken(...)` 显式删)
2. **其他设备的所有 `utok_` / `atok_`** 同步撤销([`auth.ts:269-270`](https://github.com/sleep2agi/agent-network/blob/main/server/src/auth.ts#L269) `DELETE ... WHERE user_id=? AND network_id IS NULL AND token_id != ?currentTokenId` 一锅端)—— 计数返回到 `revoked` 字段
2. **其他设备的所有 `utok_` / `atok_`** 同步撤销([`auth.ts`](https://github.com/sleep2agi/agent-network/blob/main/server/src/auth.ts) 搜 `network_id IS NULL AND token_id != ` `DELETE ... WHERE user_id=? AND network_id IS NULL AND token_id != ?currentTokenId` 一锅端)—— 计数返回到 `revoked` 字段
3. **`ntok_` 不受影响**(`revokeOtherUserTokens` 只删 `network_id IS NULL` 的 token,agent node 用 `ntok_` 跑着的不会被改密打断;跟 [account-system 改密码副作用](/guide/account-system#修改密码) ZH 描述一致)
4. **新 `utok_`** (`issued.token`) 颁发给调用方作为响应返回 —— 调用方应立即用新 token 覆盖本地存储
5. 写 audit log: `action='password_changed'`
Expand Down Expand Up @@ -396,7 +396,7 @@ curl http://localhost:9200/api/networks \
}
```

`networks` 数组每行 10 字段:9 个 `networks` 表字段 ([`server/src/db.ts:168-177`](https://github.com/sleep2agi/agent-network/blob/main/server/src/db.ts#L168) 含 v3 migration `visibility` + `max_members`) + 1 个 join 字段 `member_role`([`auth.ts:382-388`](https://github.com/sleep2agi/agent-network/blob/main/server/src/auth.ts#L382) JOIN `network_members`)。排序:owner 在前,其余按 `created_at`(`ORDER BY nm.role = 'owner' DESC, n.created_at`)。`settings` / `description` 可为 `null`。`ntok_` 调用只返回当前 binding 那一个 network(不是全部);`utok_` 返回所有所属网络。
`networks` 数组每行 10 字段:9 个 `networks` 表字段 ([`db.ts`](https://github.com/sleep2agi/agent-network/blob/main/server/src/db.ts) 搜 `CREATE TABLE IF NOT EXISTS networks` 含 v3 migration `visibility` + `max_members`) + 1 个 join 字段 `member_role`([`auth.ts:382-388`](https://github.com/sleep2agi/agent-network/blob/main/server/src/auth.ts#L382) JOIN `network_members`)。排序:owner 在前,其余按 `created_at`(`ORDER BY nm.role = 'owner' DESC, n.created_at`)。`settings` / `description` 可为 `null`。`ntok_` 调用只返回当前 binding 那一个 network(不是全部);`utok_` 返回所有所属网络。

---

Expand Down Expand Up @@ -1559,7 +1559,7 @@ curl -X POST http://localhost:9200/mcp \

> [源码 ↗](https://github.com/sleep2agi/agent-network/blob/main/server/src/server.ts)

SSE 实时推送端点,客户端通过长连接接收事件。路径段 `:name` 是一个**通用 channel 名**(源码里叫 `:session`):Agent 用自己的 **node alias** 订阅、Dashboard 用 **username** 订阅 user channel。SSE 层本身是 per-channel-name 的 `Map`([`push.ts:11` `clients`](https://github.com/sleep2agi/agent-network/blob/main/server/src/push.ts#L11)),不区分 alias / username —— `pushEvent(name, ...)` 推给谁取决于谁注册了那个 name(如 `node.renamed` 同时推 alias 流和成员 username channel,见下表)。
SSE 实时推送端点,客户端通过长连接接收事件。路径段 `:name` 是一个**通用 channel 名**(源码里叫 `:session`):Agent 用自己的 **node alias** 订阅、Dashboard 用 **username** 订阅 user channel。SSE 层本身是 per-channel-name 的 `Map`([`push.ts`](https://github.com/sleep2agi/agent-network/blob/main/server/src/push.ts) 搜 `const clients = new Map<string, SSEClient[]>()`),不区分 alias / username —— `pushEvent(name, ...)` 推给谁取决于谁注册了那个 name(如 `node.renamed` 同时推 alias 流和成员 username channel,见下表)。

```bash
# 推荐:Authorization header(避免 token 写进代理 / 浏览器历史 / access log)
Expand All @@ -1573,7 +1573,7 @@ curl -N "http://localhost:9200/events/代码1号?token=ntok_xxx"

| 事件 | 触发条件 | 数据 |
|------|---------|------|
| `connected` | 初始连接握手([`push.ts:35`](https://github.com/sleep2agi/agent-network/blob/main/server/src/push.ts#L35),每个 client 连上 SSE 时发一次) | `{session, network_id}` |
| `connected` | 初始连接握手([`push.ts`](https://github.com/sleep2agi/agent-network/blob/main/server/src/push.ts) 搜 `{ type: "connected", session: sessionName`,每个 client 连上 SSE 时发一次) | `{session, network_id}` |
| `new_task` | 收到新任务(`send_task` / `retry_task` / `reassign_task` / REST `POST /api/task`) | `{inbox_count, priority, from}` |
| `new_message` | 收到新消息(`send_message`) | `{from, message_id}` |
| `new_reply` | 收到 reply(`send_reply`) | `{from, message_id, in_reply_to, status}` |
Expand Down Expand Up @@ -1711,7 +1711,7 @@ curl -X POST http://localhost:9200/api/auth/tokens \
:::

::: info 这个 endpoint 创建的是 legacy `atok_`
本 endpoint 走 [`auth.ts:243` `generateToken()`](https://github.com/sleep2agi/agent-network/blob/main/server/src/auth.ts#L243) 颁发 `atok_` 前缀 + `scope='full'` token,是 V2 时代的兼容路径,不是 v0.8 主线的 `utok_` / `ntok_`。新代码请用:
本 endpoint 走 [`auth.ts`](https://github.com/sleep2agi/agent-network/blob/main/server/src/auth.ts) 搜 `generateToken`(全仓 3 处) 颁发 `atok_` 前缀 + `scope='full'` token,是 V2 时代的兼容路径,不是 v0.8 主线的 `utok_` / `ntok_`。新代码请用:
- **`utok_`(用户 Token)**:通过 [POST /api/auth/login](#post-api-auth-login) 或 [POST /api/auth/register](#post-api-auth-register) 自动颁发
- **`ntok_`(节点 Token)**:通过 [POST /api/auth/node-token](#post-api-auth-node-token) 创建(绑定到指定 network + 节点 alias)

Expand Down Expand Up @@ -1997,7 +1997,7 @@ curl -X POST http://localhost:9200/api/networks/join \
| 400 | `invite code expired` | `expires_at < now()`(不传 `expires_days` 创建则不会过期) |
| 400 | `already a member of this network` | 调用者已是该网络成员 |

`anet network join` CLI 拿到该响应后会自动切换到加入的 network(即 `~/.anet/config.json` 的 `network_id` 字段更新为 `res.network_id`),并打印 `Joined network as <role>`。同时 server 自动颁发一个 `network_id` 绑定的 token 给加入者([`auth.ts:374-377`](https://github.com/sleep2agi/agent-network/blob/main/server/src/auth.ts#L374) `name='auto-join' scope='full'`),写 audit `network_joined`。
`anet network join` CLI 拿到该响应后会自动切换到加入的 network(即 `~/.anet/config.json` 的 `network_id` 字段更新为 `res.network_id`),并打印 `Joined network as <role>`。同时 server 自动颁发一个 `network_id` 绑定的 token 给加入者([`auth.ts`](https://github.com/sleep2agi/agent-network/blob/main/server/src/auth.ts) 搜 `"auto-join", "full"` `name='auto-join' scope='full'`),写 audit `network_joined`。

---

Expand Down
Loading
Loading