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
15 changes: 11 additions & 4 deletions docs/admin.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ docker compose up -d
1. **创建超级管理员。** 首页显示「创建第一名超级管理员」:填用户名和 12–256 位密码,点「初始化并登录」。初始化只成功一次,之后这里只有登录。
2. **走完接入向导。** 首次登录会自动打开「接入向导」(之后从顶部导航进入),按顶部的六步走:
1. 公网 HTTPS:选「自建 HTTPS 中转」,把一个专用域名整个反向代理到 `http://<这台机器>:8791`,填 `https://<你的域名>`,点「检查并使用」。或者选「Cloudflare Tunnel」,粘贴 tunnel token 点「保存 token」,在 Cloudflare 的 Published application 把 Service URL 设为 `http://webhook:8791`,回到向导点该路由的「检查并使用此域名」。两种方式都不要把 8792 转出去,管理页留在本机 `127.0.0.1:8792`。
2. GitHub App:填「目标组织」和「App 名称」,点「准备 GitHub 确认页」→「前往 GitHub 确认创建」,在 GitHub 上确认后会回到向导。权限固定为 Contents、Issues、Pull requests、Commit statuses、Actions、Checks 写,Metadata 读(Actions、Checks、Commit statuses 让检视和开发任务能用只读令牌查看 CI 状态,这三项的写权限留给控制面发布状态、检查结果和重跑 CI;之前创建的 App 要在 GitHub 上补加),订阅 `issues`、`pull_request` 和 `issue_comment`(已有 App 需由所有者在 Permissions & events 勾选 Issue comment 并保存);Webhook 地址 `https://<你的域名>/hooks/github/<account_id>/<endpoint_id>` 由向导写进 App,不用手填。已在「机器人账号」登记过的 App 点「使用此 App」。
2. GitHub App:填「目标组织」和「App 名称」,点「准备 GitHub 确认页」→「前往 GitHub 确认创建」,在 GitHub 上确认后会回到向导。权限固定为 Contents、Issues、Pull requests、Commit statuses、Actions、Checks 写,Metadata 读(Actions、Checks、Commit statuses 让检视和开发任务能用只读令牌查看 CI 状态,这三项的写权限留给控制面发布状态、检查结果和重跑 CI;之前创建的 App 要在 GitHub 上补加),订阅 `issues`、`pull_request` 和 `issue_comment`(已有 App 需由所有者在 Permissions & events 勾选 Issue comment 并保存);Webhook 地址 `https://<你的域名>/hooks/github/<account_id>/<endpoint_id>` 由向导写进 App,不用手填。已在「全局设置 → 机器人账号」登记过的 App 点「使用此 App」。
3. 安装:点「在 GitHub 安装此 App」,选好仓库后回来点「重新检查安装」。
4. 模型:在「添加模型」填接口 URL、API Key、模型名,点「保存并继续」。URL 下方会显示实际的对话地址和模型列表地址;bot 不会替你补上或去掉 `/v1`,地址不以 `/v1` 结尾时页面会提示。保存前可以点「获取模型列表」从上游选模型名,或点「测试模型」确认 URL、Key 和模型名可用。
5. 规则:看一下内置规则,点「下一步」。
Expand All @@ -40,12 +40,19 @@ docker compose up -d
## 日常使用

- **启用更多仓库**:在「项目」页点「同步安装列表」,在仓库行点「启动」,选好模型后确认。只有需要 bot 自动改代码时才勾选「同时开启自动开发」;开启后,bot 只开发由人加了开发标签(默认 `bot-develop`)且没有指派人的 Issue。仓库启动前收到的事件只存档,不会补跑。不再需要时点「禁用」,数据保留,之后可以再启用。仓库在 GitHub 上已删除,或当前账号已完全不能访问它(移出安装、安装被暂停)时,可以在项目行或项目设置里「删除」:先当场向 GitHub 核对,再停任务、去掉容器和网络并删掉项目记录,工作卷和操作审计保留。仍能看到、但授予的权限低于所需的仓库在列表上标为「权限不足」,不能删除。
- **清理用户和账号**:「用户」「机器人账号」页每行都能删除;不能删最后一名活跃的超级管理员,仍有项目使用的账号要先删掉这些项目。删除账号只从本系统移除,不会在 GitHub 上卸载 App。外部执行客户端在项目「外部执行」或全局「外部执行端」删除。
- **清理用户和账号**:「全局设置」的「用户」「机器人账号」两类里每行都能删除;不能删最后一名活跃的超级管理员,仍有项目使用的账号要先删掉这些项目。删除账号只从本系统移除,不会在 GitHub 上卸载 App。外部执行客户端在项目「外部执行」或全局「外部执行端」删除。
- **调整一个仓库**:在「项目」页点该仓库的「设置」。「检视」里开关 Issue/PR 触发、选评论语言、编辑检视规则;「开发」里打开自动开发、填测试命令;「使用的模型」换模型;「机器配置」改虚拟电脑的资源、软件包和初始化脚本,以及这台电脑同时运行的任务数(项目管理员也能改,留空跟随全局默认);「规则总览」看每份规则文件哪一层生效。仓库也可以在默认分支用 `.bot/` 下的文件覆盖规则,路径和格式见[用仓库文件调整规则](rules.md)。
- **看 bot 在做什么**:「检视」「开发」页列出任务,点开能看到每一步的思考、命令和输出;「Webhook 投递」页列出 GitHub 送来的每次投递。
- **按 Issue / PR 看进展**:「看板」占满整屏,顶部在 Issue 和 PR 两块看板之间切换并显示各自数量;每块按 bot 执行阶段分成六列,列内滚动,同一对象的检视和开发任务放在同一张卡片,卡片上能看到最近一次检视和开发的状态。默认只显示打开或状态未知的对象,工具栏可直接搜编号或标题、选仓库、勾选包含已关闭,再保存或另存个人视图。「加列」会引导创建自定义单选字段;按该字段分列后,拖动卡片或选择选项只修改个人字段值,不会改变执行阶段或触发任务。空列也显示,暂时不看的列可以折叠成窄条。
- **全局设置**:超级管理员在「全局设置」页设置每台虚拟电脑默认同时运行的任务数(1–32,默认 1),并查看当前运行的镜像标签;源码运行时显示为源码运行。接入向导保存的部署配置也在这一页修改:修改并重新检查公网 HTTPS 地址;保存或撤销 Cloudflare Tunnel token(可以粘贴完整的 cloudflared 命令,只保存其中的 token,页面不回显);改选本系统已登记的 GitHub App,并把它的 webhook 改到当前公网地址;更换向导启用仓库时使用的默认共享模型。部署共享模型也在这一页添加、修改和删除。新建 App 和安装到账号仍在接入向导里完成。项目管理员看不到这个页面。
- **给别人权限**:在「用户」页新建项目管理员,从按组织分组的已安装仓库里多选:选「整个组织」时,这个组织已经启用和以后启用的项目都归他管理;只选仓库时不包括这个组织后来启用的仓库。用户列表列出每名项目管理员选的组织和仓库,「修改仓库权限」用同一份列表调整;不在安装列表里的整个组织会保持选中,直到管理员取消。也可以在仓库「设置」→「成员」单独添加或移除某个仓库。项目管理员只能看到和操作分给自己的仓库。
- **全局设置**:超级管理员在「全局设置」里管理整个部署,左侧按类别分开,每个类别有自己的地址(`#/settings/<类别>`),刷新或分享链接都停在同一类:
- 「常规」:每台虚拟电脑默认同时运行的任务数(1–32,默认 1),以及当前运行的镜像标签;源码运行时显示为源码运行。
- 「公网接入」:修改并重新检查公网 HTTPS 地址;保存或撤销 Cloudflare Tunnel token(可以粘贴完整的 cloudflared 命令,只保存其中的 token,页面不回显)。
- 「机器人账号」:改选接入向导使用的 GitHub App,并把它的 webhook 改到当前公网地址;新增、检查权限和删除已登记的 GitHub App 与机器人 Token。
- 「模型」:更换向导启用仓库时使用的默认共享模型;添加、修改和删除部署共享模型。
- 「用户」:新增用户、修改项目管理员的仓库权限、删除用户。

新建 App 和安装到账号仍在接入向导里完成。项目管理员看不到全局设置。
- **给别人权限**:在「全局设置 → 用户」新建项目管理员,从按组织分组的已安装仓库里多选:选「整个组织」时,这个组织已经启用和以后启用的项目都归他管理;只选仓库时不包括这个组织后来启用的仓库。用户列表列出每名项目管理员选的组织和仓库,「修改仓库权限」用同一份列表调整;不在安装列表里的整个组织会保持选中,直到管理员取消。也可以在仓库「设置」→「成员」单独添加或移除某个仓库。项目管理员只能看到和操作分给自己的仓库。
- **看用量和费用**:任务列表和详情显示累计运行时长、输入/输出/缓存命中 token 及加权命中率,运行时长不含排队和等待。在项目「使用的模型」点「计费单价」,填写每百万 token 的输入、输出和缓存命中单价;「成本」页按 UTC 日期用堆叠柱状图展示各项目的估算费用、占比和每日明细,可按项目、日期与币种筛选。单价变更只影响新请求,缺少用量或单价的请求单独提示,历史未记录的数据不作补算。

在项目「使用的模型」中,模型行的「删除」会先列出当前选用它的仓库,确认后只删除本系统的模型配置并销毁封存的 API Key,不会删除 GitHub 上的任何东西。项目管理员可删本项目专属模型;部署共享模型只有超级管理员能删,删除后所有项目都不能再选它。选用中的项目会改回「不使用模型」,之后的检视和开发会停在「配置待补」;没有项目选用时确认框会说明。已经运行的任务不会被取消,已有请求和成本记录保留,仍按当时记下的模型名和单价展示。删除失败会留在确认框中,可重试或取消。部署共享模型的同样操作在「全局设置」也可以做;删掉的若是接入向导的默认模型,这个默认随之清空。
Expand Down
2 changes: 1 addition & 1 deletion docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

账号凭据、入口签名材料和模型 Key 用 AES-256-GCM 加密,密文绑定记录 ID。读取和写入响应均只返回是否已配置,不返回明文或密文。更新时留空或仅空白的凭据保持原值;撤销使用专用动作,不能靠提交空字符串撤销。模型 PATCH 必须带读取到的 `version`,旧版本返回 409。撤销账号凭据不会连带撤销入口签名;要禁用入口需单独撤销该入口。

浏览器管理入口以项目为单位组织:“项目”页列出已安装和已登记的仓库及“未启动/工作中/已禁用/已移出安装”状态,超级管理员可同步安装列表、启动、启用或禁用;点每行的“设置”进入该项目独立的设置页(`#/projects/<仓库 ID>/<类别>`,可直接分享和刷新),左侧类别为基本设置(仓库信息、处理账号、基准分支)、机器配置(虚拟电脑状态、实时资源、软件安装与初始化结果、启停与重置,以及 CPU、内存、进程数、/tmp、外网、额外软件包、环境变量和初始化脚本)、使用的模型(选择部署共享或本项目专属模型,添加模型、更换或撤销 Key)、检视(检视开关、Issue/PR 打开与 PR 新提交三个触发、评论语言,以及 Issue 检视规则、PR 检视规则、检视标签三份规则文件)、开发(自动开发、测试命令、bot PR 修订轮数和开发规则文件)、规则总览(一张表列出每份规则文件的作用、路径、用于哪些任务,以及内置默认/项目控制台/仓库文件哪一层生效;行为指南和仓库 skill 也在这里)、成员(仅超级管理员,分配项目管理员)和运行记录(当前任务、最近任务与命令)。项目管理员只看到自己的项目,机器配置只读。“检视”“开发”两个任务页的任务详情按时间线展示 Agent 工作过程:工作区准备、每一步模型思考与工具选择、读写的文件、在项目容器中执行的命令及退出码和输出、GitHub 发布和故障;运行中每 2 秒刷新,正在执行的命令单独显示并实时追加输出。账号/用户新增在各自页面完成,凭据轮换等其余管理操作通过[管理 API](api.md) 完成。
浏览器管理入口以项目为单位组织:“项目”页列出已安装和已登记的仓库及“未启动/工作中/已禁用/已移出安装”状态,超级管理员可同步安装列表、启动、启用或禁用;点每行的“设置”进入该项目独立的设置页(`#/projects/<仓库 ID>/<类别>`,可直接分享和刷新),左侧类别为基本设置(仓库信息、处理账号、基准分支)、机器配置(虚拟电脑状态、实时资源、软件安装与初始化结果、启停与重置,以及 CPU、内存、进程数、/tmp、外网、额外软件包、环境变量和初始化脚本)、使用的模型(选择部署共享或本项目专属模型,添加模型、更换或撤销 Key)、检视(检视开关、Issue/PR 打开与 PR 新提交三个触发、评论语言,以及 Issue 检视规则、PR 检视规则、检视标签三份规则文件)、开发(自动开发、测试命令、bot PR 修订轮数和开发规则文件)、规则总览(一张表列出每份规则文件的作用、路径、用于哪些任务,以及内置默认/项目控制台/仓库文件哪一层生效;行为指南和仓库 skill 也在这里)、成员(仅超级管理员,分配项目管理员)和运行记录(当前任务、最近任务与命令)。项目管理员只看到自己的项目,机器配置只读。“检视”“开发”两个任务页的任务详情按时间线展示 Agent 工作过程:工作区准备、每一步模型思考与工具选择、读写的文件、在项目容器中执行的命令及退出码和输出、GitHub 发布和故障;运行中每 2 秒刷新,正在执行的命令单独显示并实时追加输出。每个页面都有地址(如 `#/reviews`、`#/settings/users`),刷新后停在原页面;检视和开发列表每 5 秒在后台刷新,只替换内容有变化的行,不清空列表。账号和用户在「全局设置」的对应类别里新增,凭据轮换等其余管理操作通过[管理 API](api.md) 完成。

## Issue 与 PR 看板

Expand Down
2 changes: 1 addition & 1 deletion docs/onboarding.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Cloudflare 自带的无主机名 `http_status:404` 兜底规则表示:未匹

优先使用已有 GitHub App。选择「使用此 App」会检查 App 和安装,并将 webhook 更新到验证过的域名及该账号已登记的 webhook 路径。凭据不会返回浏览器。

没有 App 时,填写「App 所属组织」和 App 名称,向导生成 manifest,再点击「前往 GitHub 确认创建」。这里填写的是 App 的归属组织,安装目标在第 3 步选择。默认是组织私有 App;需要安装到其他组织或个人账号时,勾选「公开 App,允许安装到其他账号」。固定权限为 contents、issues、pull_requests、statuses、actions、checks 写,metadata 读(之前创建的 App 在「机器人账号」页或本步「使用已登记的 App」下点「检查权限」:页面列出 App 和各安装缺少的权限,链接到 App 的 Permissions & events 页和各安装的确认页;GitHub 不提供修改 App 权限的接口,所以要 App 所有者在该页改好后保存一次,再由各安装方各接受一次);default_events 为 issues、pull_request、issue_comment。已有 App 的所有者还需在 Permissions & events 中勾选 Issue comment 并保存,代码更新不会自动修改已有订阅。installation 和 installation_repositories 由 GitHub 自动投递,不写入 manifest。不存在创建 App 的 REST 调用,也不要求 fine-grained PAT。
没有 App 时,填写「App 所属组织」和 App 名称,向导生成 manifest,再点击「前往 GitHub 确认创建」。这里填写的是 App 的归属组织,安装目标在第 3 步选择。默认是组织私有 App;需要安装到其他组织或个人账号时,勾选「公开 App,允许安装到其他账号」。固定权限为 contents、issues、pull_requests、statuses、actions、checks 写,metadata 读(之前创建的 App 在「全局设置 → 机器人账号」或本步「使用已登记的 App」下点「检查权限」:页面列出 App 和各安装缺少的权限,链接到 App 的 Permissions & events 页和各安装的确认页;GitHub 不提供修改 App 权限的接口,所以要 App 所有者在该页改好后保存一次,再由各安装方各接受一次);default_events 为 issues、pull_request、issue_comment。已有 App 的所有者还需在 Permissions & events 中勾选 Issue comment 并保存,代码更新不会自动修改已有订阅。installation 和 installation_repositories 由 GitHub 自动投递,不写入 manifest。不存在创建 App 的 REST 调用,也不要求 fine-grained PAT。

GitHub 确认后返回本机管理回调。随机 state 绑定发起的超级管理员,15 分钟有效,一次性使用;manifest code 兑换不带 PAT。App 私钥与 webhook secret 只在控制面加密保存,client secret 不需要则丢弃。回调成功后,浏览器打开向导第 3 步「安装」,显示刚创建的 App 和安装入口。

Expand Down
5 changes: 3 additions & 2 deletions e2e/admin-grants.spec.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ test('超级管理员按组织分组选择项目管理员的仓库,并在用
await page.goto(h.admin);
await page.getByLabel('用户名', { exact: true }).fill('administrator'); await page.getByLabel('密码', { exact: true }).fill(password);
await page.getByRole('button', { name: '登录', exact: true }).click();
await page.getByRole('button', { name: '用户', exact: true }).click(); await page.getByRole('button', { name: '新增', exact: true }).click();
await page.getByRole('button', { name: '全局设置', exact: true }).click(); await page.locator('.project-nav').getByRole('link', { name: '用户', exact: true }).click();
await page.getByRole('button', { name: '新增用户', exact: true }).click();
// One group per organization; its first option is the whole organization, and repositories not enabled yet are listed too.
const editor = page.locator('#editor'), picker = editor.getByLabel('仓库权限');
expect(await picker.evaluate(select => [...select.querySelectorAll('optgroup')].map(group => [group.label, ...[...group.children].map(option => option.textContent)]))).toEqual([
Expand All @@ -23,7 +24,7 @@ test('超级管理员按组织分组选择项目管理员的仓库,并在用
await editor.locator('select[name=role]').selectOption('project_admin');
await editor.getByLabel('用户名', { exact: true }).fill('member'); await editor.getByLabel('初始密码(至少 12 位)', { exact: true }).fill(testPassword());
await picker.selectOption(['organization:10', 'repository:201']); await page.getByRole('button', { name: '保存', exact: true }).click();
const row = page.locator('#rows .row', { hasText: 'member' });
const row = page.locator('#settings-panel .row', { hasText: 'member' });
await expect(row).toContainText('整个组织:fixture · 仓库:other/app(未启动)');
// The same list edits the grants later, starting from what is held now.
await row.getByRole('button', { name: '修改仓库权限', exact: true }).click();
Expand Down
Loading
Loading