xuym-codex-collab 0.4.0 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -5
- package/apps/onboarding/discovery.mjs +4 -0
- package/apps/onboarding/guided.mjs +105 -0
- package/apps/onboarding/manager.mjs +151 -19
- package/apps/onboarding/reset.mjs +247 -0
- package/apps/onboarding/server.mjs +2 -1
- package/apps/onboarding/web/app.js +216 -38
- package/apps/onboarding/web/index.html +30 -7
- package/apps/onboarding/web/style.css +4 -0
- package/apps/peer/desktop.mjs +2 -0
- package/apps/peer/runtime.mjs +2 -1
- package/apps/peer/wecom.mjs +6 -4
- package/docs/desktop-poll.md +1 -1
- package/docs/onboarding-audit.md +27 -8
- package/docs/onboarding.md +49 -24
- package/package.json +1 -1
- package/packages/codex-kit/templates/.agents/skills/team-collab-desktop/SKILL.md +3 -2
- package/packages/peer/inbox.mjs +4 -2
- package/packages/peer/service.mjs +8 -4
- package/schemas/peer-inbox.schema.json +1 -1
- package/scripts/peer/refresh-project.mjs +1 -1
- package/scripts/peer/setup-desktop-project.mjs +3 -0
package/docs/onboarding-audit.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 网页向导功能检查与测试记录
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
最近检查日期:2026-10-14。对象是工具仓库中的网页向导源码,初始记录对应 0.3.1 的发布前验证;后面的补充记录覆盖后续源码变更。
|
|
4
4
|
|
|
5
5
|
## 测试范围
|
|
6
6
|
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
| 访问控制 | 错误令牌、错误 Host/Origin、未登录接口、请求格式与体积 | HTTP 测试通过;拒绝未授权操作,返回安全提示 |
|
|
17
17
|
| 管理员初始化 | 仓库目录、成员名称、职责、固定验证命令、机器人凭据 | 浏览器创建新管理员成功;无效 JSON/非字符串数组在连接前拦截 |
|
|
18
18
|
| 仓库检查 | 缺少 origin、远程空仓库、缺少 main | 自动测试通过;给出具体的 origin/main 提示与推送指令 |
|
|
19
|
-
| 群身份验证 |
|
|
19
|
+
| 群身份验证 | 连接、验证码、复制消息、验证后自动继续 | 浏览器通过;未验证不能继续,自动完成仅提交一次 |
|
|
20
20
|
| 群身份约束 | 准确验证码、指定群、群消息发送人、连接认证前不得验证 | 自动测试通过;不匹配消息忽略 |
|
|
21
21
|
| 验证恢复 | 连接失败重试、断开、验证码过期 | 失败与重试经浏览器验证;过期经自动测试验证 |
|
|
22
22
|
| 团队初始化 | 生成本机身份、发布团队签名、安装 MCP/Skill、启动连接 | 浏览器与本地 Git 测试通过 |
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
| 重复导入 | 已验证、待批准、配置中、已就绪成员再次导入 | 自动测试通过;浏览器重复导入 ready 成员直接打开原项目,不再抢机器人连接 |
|
|
31
31
|
| 入队申请 | 群身份自动生成 userid、提交签名申请、等待管理员 | 浏览器通过;成员页面显示待批准状态 |
|
|
32
32
|
| 管理员批准 | 显示申请身份、确认入队、更新成员与职责路由 | 浏览器与协议测试通过;新增职责自动有审核规则 |
|
|
33
|
-
| 成员完成配置 |
|
|
33
|
+
| 成员完成配置 | 批准后自动继续、安装个人 MCP/Skill、启动机器人 | 浏览器与自动测试通过;人工批准仍是必要前提 |
|
|
34
34
|
| 旧项目导入 | 旧管理员先导入、保留身份/任务/绑定、重复导入 | 浏览器及自动测试通过;兼容原生成的 desktop MCP 配置路径 |
|
|
35
35
|
| 项目状态 | MCP、Skill、机器人、对话绑定、实际轮询、综合就绪 | 浏览器与自动测试通过;缺失/禁用配置、未绑定或轮询过期不会显示综合就绪 |
|
|
36
36
|
| 绑定指令 | 展示、复制、指定身份和项目 | 浏览器通过;桌面对话仍需执行指令,页面不能代替对话创建 heartbeat |
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
| 协作执行 | 两位执行者接力固定提交、运行验证、发阶段通知、真人审批、完成 | 本地自动测试通过;真实群通知呈现仍需实群验证 |
|
|
47
47
|
| 诊断提示 | 配置路径、失败说明、Git 同步失败本机状态回退 | 浏览器确认配置路径展示;安全错误与 Git 同步回退经自动测试覆盖,不输出 SDK 原始异常 |
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
初始检查的完整测试命令是 `npm test`,结果为 **108 passed, 0 failed**;最新补充结果见下文。新增加的页面事件测试运行实际交付的 `app.js`,使用最小 DOM 验证 API 调用顺序与恢复;它不能替代真实浏览器交互。本次已结合两种测试。
|
|
50
50
|
|
|
51
51
|
## 本次修复
|
|
52
52
|
|
|
@@ -65,18 +65,37 @@
|
|
|
65
65
|
|
|
66
66
|
本人邀请带有签名保护的可选 `owner_userid`,要求群验证账号与已验证管理员一致;接入记录沿用正常签名申请和管理员批准。邀请与恢复资料保存在仓库外的 0600 私有文件中。普通同事可以通过多份接入包使用同一个真人账号,但仍需分别验证和批准。新客户端接受旧邀请;旧客户端不识别新所有者字段或共享所有者策略,采用此功能前应统一升级并重启 Runner/MCP。
|
|
67
67
|
|
|
68
|
+
## 管理与重置补充验证
|
|
69
|
+
|
|
70
|
+
本地源码新增“重新绑定对话”“更新机器人凭据”“移除本机接入”,以及首页“已移除的接入”的恢复入口。本次 `npm test` 完整运行结果为 **139 passed, 0 failed**。新增测试覆盖确认输入、运行中租约及预览后认领的竞态、实际停止 Runner、旧对话失效、Secret 的原群本人验证、取消连接、文件回滚、旧版独立 MCP 配置的同步停用与恢复,以及接口鉴权和页面操作顺序。
|
|
71
|
+
|
|
72
|
+
浏览器在临时 Git clone 和模拟机器人环境中实际完成解除绑定、移除项目、首页恢复原身份,并检查凭据管理页面与取消操作。恢复后 MCP、Skill 显示已就绪,机器人保持停止、对话显示待绑定。业务文件、未提交改动、其他配置与共享协议记录的保留由自动测试核验。没有重置实际使用的机器人或项目绑定,也没有对真实企业微信 Secret 轮换作联合验收。
|
|
73
|
+
|
|
74
|
+
`npm pack --dry-run` 确认发布包包含重置模块和向导页面资源,且不包含测试文件。上述功能计划随 `0.4.1` 发布,运行旧版 npm 包的页面不会自动获得这些入口。
|
|
75
|
+
|
|
76
|
+
## 连续接入与批量分配补充验证(2026-10-14)
|
|
77
|
+
|
|
78
|
+
本地源码将管理员创建和成员加入改为连续引导:显示名称与职责预设、已有项目/自动 clone、推荐后明确确认验证命令、群验证后自动继续、批准后自动配置、六项接入进度、短 Skill 启用指令。高级配置折叠,旧详细指令和手动重试保留。创建草稿及成员进度保存在本机私有文件;浏览器 sessionStorage 仅存草稿或项目 ID。
|
|
79
|
+
|
|
80
|
+
完整 `npm test` 对应测试命令运行结果为 **154 passed, 0 failed**。Node.js 20.20.2 下的引导与网页专项测试为 **27 passed, 0 failed**。新增覆盖 literal argv 解析、检测时不执行项目脚本、带空格路径 clone、失败目录清理、保留已有目录、校验接入包后才 clone、私有创建草稿和幂等完成、批量重复项拒绝、密码不进入 Git/下载的明文、自动等待明确批准、已停止 Runner 不被后台检查重启、异常不无限自动重试,以及页面刷新恢复和过期响应不能替换当前项目。
|
|
81
|
+
|
|
82
|
+
浏览器在独立临时 Git 仓库中实际完成:管理员项目检测、群验证中刷新恢复、自动进入项目页、生成 Bob/Carol 两份加密接入包、单独复制密码和实际下载;另一个页面上传 Bob 接入包、查看预填信息、自动 clone 至临时新目录、群验证中刷新恢复、自动提交申请、管理员页面自动显示申请、明确批准后成员自动完成 MCP/Skill 配置。页面未报告 console error/warn;截图保存在本机,未加入发布包。
|
|
83
|
+
|
|
84
|
+
机器人认证与群消息由隔离测试连接模拟,Runner 启停由测试实现记录。本次没有连接真实企业微信、建立真实 Codex heartbeat、修改个人使用中的 Alice/Bob clone 或机器人。页面正确显示待绑定、未观察到轮询,未把本机配置完成显示为能够接任务。
|
|
85
|
+
|
|
86
|
+
Skill 校验通过。`npm pack --dry-run` 确认发布包包含 `guided.mjs`、`reset.mjs`、页面资源和 Skill,共 67 个文件,未包含测试或本机私有资料。签名邀请 schema 保持兼容;显示名称及团队名称仅在本机与加密接入包中使用。本次源码版本为 `0.4.1`,将随本次源码提交推送并发布到 npm;真实企微和跨机器联合验收仍待完成。
|
|
87
|
+
|
|
68
88
|
## 尚缺少的页面功能
|
|
69
89
|
|
|
70
90
|
| 功能遗漏 | 当前影响 |
|
|
71
91
|
| --- | --- |
|
|
72
|
-
|
|
|
92
|
+
| 更换 Bot ID、变更群 | 当前可在页面更新同一机器人的 Secret;更换 Bot ID 或群需移除接入,再使用管理员的新接入包 |
|
|
73
93
|
| 撤销邀请、禁用/移除成员、轮换签名身份 | 当前没有产品化入口;需要补签名协议、权限与历史兼容,不能仅删本机文件 |
|
|
74
94
|
| 修改既有成员职责、重排首选与候补 | 页面只展示已有分工;生成邀请时可以指定新成员优先级 |
|
|
75
95
|
| 缺失 MCP/Skill 的一键修复 | 当前能识别缺失,但现有 Skill、受版本管理的配置等需要检查;不会自动覆盖 |
|
|
76
|
-
| 确认 heartbeat
|
|
96
|
+
| 确认 heartbeat 持续启用 | 页面可解除旧绑定并观察最近轮询;暂停旧 heartbeat、启用新 heartbeat 仍需在对应 Codex 对话完成 |
|
|
77
97
|
| 页面内展示日志与详细 Git 诊断 | 当前只有安全错误提示、私有配置路径;详细日志仍需本机查看 |
|
|
78
|
-
| 多管理员配置冲突的页面引导 |
|
|
79
|
-
| 页面刷新保留正在进行的管理员验证码步骤 | 管理员保存 profile 前的验证码状态在当前页面内存中;刷新需重新填写和验证;成员已导入的项目可从首页恢复 |
|
|
98
|
+
| 多管理员配置冲突的页面引导 | 自动刷新保留未保存编辑,提示策略版本已变化;后端拒绝过时保存,仍需刷新重新确认 |
|
|
80
99
|
|
|
81
100
|
## 仍需联合验收的环境场景
|
|
82
101
|
|
package/docs/onboarding.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
## 每个人先准备一次
|
|
6
6
|
|
|
7
7
|
1. 安装 Node.js 20+、Git 和支持对话 heartbeat 的 Codex 桌面应用。
|
|
8
|
-
2.
|
|
8
|
+
2. 确认业务仓库已有远程 `main` 分支,本机 Git 凭据能读取并 push `agent-collab` 及任务代码分支。已有独立 clone 可以直接使用;没有 clone 时让向导从 Git 地址拉取。
|
|
9
9
|
3. 打开终端执行:
|
|
10
10
|
|
|
11
11
|
```sh
|
|
@@ -13,38 +13,46 @@ npm install --global xuym-codex-collab
|
|
|
13
13
|
codex-collab setup
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
-
网页自动打开;若未打开,执行 `codex-collab setup --no-open
|
|
16
|
+
网页自动打开;若未打开,执行 `codex-collab setup --no-open`,复制终端打印的本机链接。保留此终端以使用工作台。团队成员无需 clone 工具仓库。也可执行 `npx xuym-codex-collab@latest setup` 直接打开向导。
|
|
17
17
|
|
|
18
18
|
同一台电脑模拟两位成员时,使用同一业务仓库的两个独立 clone、两个机器人、两个 Codex 对话;同一个 clone 只能接入一个身份。同一个企业微信账号可以拥有多个 Agent,每个 Agent 的机器人、签名身份、clone 和对话独立。真正同事使用自己的企业微信账号和 Git 凭据。
|
|
19
19
|
|
|
20
20
|
## 管理员操作
|
|
21
21
|
|
|
22
|
-
1. 在企业微信创建独立智能机器人,获取 Bot ID 和 Secret
|
|
23
|
-
2.
|
|
24
|
-
3.
|
|
25
|
-
4.
|
|
26
|
-
5.
|
|
27
|
-
6.
|
|
28
|
-
7.
|
|
22
|
+
1. 在企业微信创建独立智能机器人,获取 Bot ID 和 Secret,拉进协作群。为每位成员分别创建机器人并加入同一个群;真人同事也加入群。
|
|
23
|
+
2. 工作台选择“初始化团队”。填写团队名称,选择本机项目目录或 Git 地址与新目录。点击“检查项目并推荐验证命令”,确认或修改推荐命令,例如 `npm test`、`go test ./...` 或 `sh test.sh`。检查只读取常见测试入口,不执行项目脚本;实际任务使用确认后的固定程序与参数,不支持管道或命令拼接。
|
|
24
|
+
3. 填写自己的显示名称、选择主要职责,输入自己的 Bot ID/Secret。名称可以是 `Alice` 或“张三”;内部 Agent ID 自动生成,自定义身份和标签可在折叠选项中调整。点击“创建团队并连接机器人”。
|
|
25
|
+
4. 用自己的企业微信账号 @ 此机器人,发送页面显示的 `协作 接入 六位数字`。页面自动发现群 `chatid` 与本人 `userid`,创建身份、签名账本,配置项目 MCP/Skill 并启动本机机器人 Runner;无需再点击继续。群验证中刷新页面可以恢复进度。
|
|
26
|
+
5. 项目页面“批量分配机器人给同事”:每行填写显示名称、选择职责、填写独立机器人的 Bot ID/Secret,点击“增加一位成员/机器人”添加更多行。默认设为该职责的首选执行者并允许所有者群命令;候补与自定义 ID/职责在折叠选项中调整。一次最多 25 行。
|
|
27
|
+
6. 点击“生成全部接入包”。密码自动生成;在“已分配的机器人”逐个下载加密 JSON 文件、查看并复制接入说明。把文件和说明交给指定成员,密码通过另一条可信渠道单独发送。说明不包含密码或 Secret。包默认 7 天有效,同一成员或机器人不会同时分发多个有效邀请;重复 ID/Bot 的批次会在发布前拒绝。
|
|
28
|
+
7. 成员群验证后,“待确认的成员”自动出现申请。核对内部 Agent ID、职责和 userid,明确点击“确认此成员入队”。成员页面自动等待批准并继续配置;批准不会由向导替你做。新的签名策略通过 Git 同步到其他在线成员。
|
|
29
|
+
8. 管理员也按下文把自己的 Codex 对话启用。需要调整审核人时,展开“团队分工、审核与通知设置”,默认阶段审核由执行 Agent 的本人处理;可选管理员或已验证身份的同事。
|
|
29
30
|
|
|
30
|
-
|
|
31
|
+
同一位同事可以领取多行分配,用同一个企业微信账号分别验证不同机器人;每份包选择独立 clone,并绑定不同 Codex 对话,不需要关闭群命令权限。
|
|
31
32
|
|
|
32
|
-
|
|
33
|
+
显示名称保存在本机和加密接入包中;团队签名和群协议仍用内部 Agent ID。真人通过自己的企业微信账号验证接入,以便核对审核权限;额外通知对象不会因此获得审核权限。
|
|
33
34
|
|
|
34
|
-
|
|
35
|
+
机器人应在企业微信后台设为多人可用,并把实际协作成员加入可使用范围。平台允许使用后,工具还会核对发言人的 `userid` 是否属于已批准入队的成员;成员可以 `@` 任意团队机器人提交需求、创建任务或查询状态。审核、取消、完成等操作仍按任务和本人权限限制。未完成团队接入的群成员不能派发任务。
|
|
36
|
+
|
|
37
|
+
机器人必须由一个本机 Runner 保持连接。同一个 Bot ID 不要同时运行旧服务、群发现命令和新 Runner。向导检查可识别的本机重复连接;其他电脑上的连接需由团队核实。
|
|
35
38
|
|
|
36
39
|
## 成员操作
|
|
37
40
|
|
|
38
|
-
1.
|
|
39
|
-
2.
|
|
40
|
-
3. 用**自己的企业微信账号** @
|
|
41
|
-
4.
|
|
42
|
-
5. 在 Codex
|
|
43
|
-
|
|
41
|
+
1. 从管理员取得自己的加密接入包、接入说明和单独发送的密码,打开 `codex-collab setup` 或 `npx xuym-codex-collab@latest setup`,选择“加入团队”。
|
|
42
|
+
2. 选择接入包文件、输入密码。可以点击“读取接入信息”查看团队、Agent、职责、仓库和建议目录。默认“帮我拉取到新目录”,可修改保存路径;也可切换“使用本机已有项目”。点击“接入我的机器人”。工具先验证接入包再 clone,使用本机 Git 登录;已有项目必须属于包内同一 remote,支持同一仓库的 SSH/HTTPS 表达。
|
|
43
|
+
3. 用**自己的企业微信账号** @ 分配给你的机器人,发送页面验证码。页面自动生成签名身份,把带公钥的入队申请写进协作分支,并进入等待批准;无需返回点击确认。刷新页面可以恢复当前验证或申请进度。
|
|
44
|
+
4. 管理员明确批准后,页面自动检查 Git 权限、写入项目本地 MCP 和 Skill、启动机器人连接。正常流程无需点击“继续接入”;Git/配置异常时会停在可恢复的步骤,处理后再明确重试。
|
|
45
|
+
5. 在 Codex 打开页面上的业务项目,信任项目并加载 MCP/Skill。点击“复制启用指令”,粘贴到要代表你的**已有对话**,例如:
|
|
46
|
+
|
|
47
|
+
```text
|
|
48
|
+
使用 $team-collab-desktop,启用当前项目的团队协作,将 bob 机器人绑定到当前对话。项目:项目绝对路径
|
|
49
|
+
```
|
|
44
50
|
|
|
45
|
-
|
|
51
|
+
6. Skill 完成身份核对、绑定真实 thread ID、复用或创建该对话的 heartbeat,并立即做一次轮询。工作台自动更新状态;展开“连接检查详情”确认机器人认证、对话绑定和实际轮询。
|
|
46
52
|
|
|
47
|
-
|
|
53
|
+
启用指令不是 Secret。成员无需手工编辑 JSON、生成密钥或复制 userid;无法识别 Skill 时可展开详细指令作为备用。Codex 若没有暴露真实对话 ID,仍需本人提供该对话 ID/链接;工具不会猜测或访问应用私有数据库。若应用不支持 heartbeat,Skill 会明确说明尚未启用自动轮询,不能仅凭已绑定就认定能接单。
|
|
54
|
+
|
|
55
|
+
工作台后台检查只在页面打开时推进向导进度;已经启动的 Runner 和已启用的对话 heartbeat 独立运行。关闭工作台后重新打开,可以从首页继续,不需要重建成员身份。
|
|
48
56
|
|
|
49
57
|
## 已有 Alice/Bob 配置如何迁移
|
|
50
58
|
|
|
@@ -87,7 +95,7 @@ codex-collab setup
|
|
|
87
95
|
|
|
88
96
|
群通知会列出真人姓名对应的成员名称,并尝试带上已验证 userid 的提及。智能机器人长连接原生 @ 是否实际弹出提醒需要实群检查;加密 userid 转明文需要另接企业应用,目前没有集成。请同时检查群通知正文,不能仅以是否收到 @ 提醒判断任务是否在等待审核。
|
|
89
97
|
|
|
90
|
-
群里“任务已创建”和“机器人已认证”不能代替 Codex 已接单证据。最终任务不会自动批准或合并 `main`;管理员需要检查代码、真实测试证据再决定如何合并。消息中的 `npm test 通过`
|
|
98
|
+
群里“任务已创建”和“机器人已认证”不能代替 Codex 已接单证据。最终任务不会自动批准或合并 `main`;管理员需要检查代码、真实测试证据再决定如何合并。消息中的 `npm test 通过` 是验收描述,真正运行的是管理员确认并固定的程序与参数(内部存为 argv)。
|
|
91
99
|
|
|
92
100
|
## 一个人使用多个机器人,或模拟两个同事
|
|
93
101
|
|
|
@@ -96,7 +104,7 @@ codex-collab setup
|
|
|
96
104
|
1. 以 Alice 初始化团队,完成群验证并进入管理员项目。已有团队可直接打开已就绪的管理员项目。
|
|
97
105
|
2. 点击“给自己增加机器人”,填写第二个 clone 的本机目录、新 Agent 名称 `bob`、职责 `backend`,以及新机器人的 Bot ID/Secret。
|
|
98
106
|
3. 保留“首选执行者”时,Bob 成为 backend 首选;Alice 若也有 backend 能力则是候补。点击“连接我的新机器人”。无需包密码或下载接入包。
|
|
99
|
-
4. 用初始化 Alice 时的**同一个企业微信账号** @Bob
|
|
107
|
+
4. 用初始化 Alice 时的**同一个企业微信账号** @Bob,发送页面验证码,页面自动继续。工具校验本人身份,自动签名批准新 Agent、配置 Bob 项目的 MCP/Skill 并启动连接。Bob 仍是 member,不会继承 Alice 的管理员角色。
|
|
100
108
|
5. 在 Alice clone 和 Bob clone 中分别打开要绑定的已有 Codex 对话,向各自对话粘贴该项目的“启用指令”。每个 Agent 各自创建 heartbeat,确认身份与最近轮询。
|
|
101
109
|
6. 在群里 @Alice 创建包含两种职责的任务,例如 `[frontend>backend]`。默认 Alice 阶段提交后你 @Alice 审核,Bob 阶段提交后你 @Bob 审核,最后 @Alice 完成任务。以每次通知指定的审核身份和版本号为准。
|
|
102
110
|
|
|
@@ -114,8 +122,9 @@ codex-collab setup
|
|
|
114
122
|
|
|
115
123
|
- 重复导入同一接入包:已验证或已接入的成员会直接打开原项目,继续原进度;不会重复启动群验证。
|
|
116
124
|
- 项目接单状态:同时检查 MCP、Skill、认证连接、目标对话绑定和最近三分钟内实际轮询。配置文件存在不等于 Codex 已加载,自动化启用状态仍需在绑定对话确认。
|
|
117
|
-
-
|
|
118
|
-
-
|
|
125
|
+
- 群验证断线/过期:点击“重新连接”,使用新的验证码。接入进度保存于本机,页面刷新能恢复原验证码;工作台进程重启后临时连接失效,会重新连接。
|
|
126
|
+
- 管理员群验证尚未完成:从首页“待完成群验证”打开“继续创建团队”,恢复保存的创建资料。
|
|
127
|
+
- Git/MCP 配置中断:从“我的项目”打开原项目,若显示错误,处理后点击“继续接入”;不要反复创建另一身份。
|
|
119
128
|
- 管理员生成邀请时网络失败:回项目页面刷新已生成接入包,再下载;本人机器人点击“继续接入我的机器人”。工具重试发布同一签名邀请。
|
|
120
129
|
- 本机工作台关闭:重新运行 `codex-collab setup`;已启动的机器人 Runner 会继续运行。
|
|
121
130
|
- 页面“停止机器人连接”只断开群机器人。暂停接单需在对应 Codex 对话暂停 heartbeat;已有任务仍需完成、报告失败或按租约恢复处理。
|
|
@@ -133,3 +142,19 @@ Secret 若曾发到聊天或 Git,应在企业微信重新生成后更新本机
|
|
|
133
142
|
旧命令兼容:`codex-collab setup --terminal` 或 `setup --role admin|member` 继续使用 [旧终端工作流](terminal-onboarding.md),不走加密包与动态签名入队。
|
|
134
143
|
|
|
135
144
|
网页向导的逐项覆盖、故障恢复与功能遗漏见 [功能检查与测试记录](onboarding-audit.md)。
|
|
145
|
+
|
|
146
|
+
## 管理、重绑与移除本机接入
|
|
147
|
+
|
|
148
|
+
在“我的项目”打开一个 Agent,展开“管理与重置”。所有操作先预览当前 Agent、clone、对话 ID 和影响,再输入当前 Agent 名称确认。重新绑定或移除时先在原对话暂停协作 heartbeat;网页无法控制 Codex 应用自动化,也不会删除聊天。
|
|
149
|
+
|
|
150
|
+
**重新绑定对话**保留成员私钥和机器人配置,停止 Runner 并清除本机对话绑定及轮询观察状态。复制项目页面的启用指令到目标对话,完成绑定后启动连接。旧对话的轮询因绑定失效无法再领取任务。
|
|
151
|
+
|
|
152
|
+
**更新机器人凭据**只修改当前 Bot ID 的 Secret。点击“验证新凭据”后,向导先停止原连接,再临时连接机器人;用原绑定本人账号在原协作群发送验证码。验证和确认成功后同步当前 Runner/MCP 实际使用的私有配置,并保留对话绑定。点击“启动机器人连接”恢复。取消验证会关闭临时连接,原配置不变,Runner 不会自动重启。
|
|
153
|
+
|
|
154
|
+
**移除本机接入**用于当前 clone 重新接入或换身份。清除对话绑定、停用旧私有配置,仅移除可识别的生成 MCP 区块与 Skill,保留其他配置和业务文件。未完成接入的项目也可移除。首页“已移除的接入”可以恢复原成员身份;已绑定其他身份的 clone 会阻止恢复,避免覆盖新接入。恢复不会恢复旧对话绑定或启动 Runner。
|
|
155
|
+
|
|
156
|
+
如果要换平台机器人、群或成员本人,先移除本机接入,再由管理员提供正确的新 Agent 接入包并按加入流程验证。移除不会撤销团队签名策略中的旧成员,管理员应另行管理分工;同一 Agent 不能通过重新初始化获得另一个已批准私钥。
|
|
157
|
+
|
|
158
|
+
重置预览有效期为十分钟。绑定、任务或文件变化后需重新预览;空闲轮询不会影响新 Secret 的群验证。当前 Agent 有有效任务租约时会拒绝重置,需先结束本阶段或在原对话报告失败;Git 无法访问时也不强行清理。自定义 Skill、自定义 MCP、Git 已跟踪或符号链接文件会阻止自动移除。
|
|
159
|
+
|
|
160
|
+
操作前的私有备份位于向导配置目录的 `reset-backups/`,完成提示会显示完整路径。它可能包含私钥、旧 Secret 与本机任务信息,权限为 `0600`;目录权限为 `0700`,不要分享或提交。业务代码、共享任务分支、未提交改动和任务 worktree 保留。页面“移除”提供可恢复的解绑,并非清除所有私人数据;不再需要恢复时按本机配置路径自行清理备份与旧身份。
|
package/package.json
CHANGED
|
@@ -7,11 +7,12 @@ description: Execute Git-backed collaboration stages in this bound Codex desktop
|
|
|
7
7
|
|
|
8
8
|
## Binding once in the intended conversation
|
|
9
9
|
|
|
10
|
-
-
|
|
10
|
+
- A request such as ‘启用当前项目协作,将机器人绑定到当前对话’ starts this binding workflow. Read the current project's generated `.codex/config.toml` to identify its `team-collab-<agent_id>` entry and private config path. Do not display secrets or modify unrelated configuration. Use that MCP's `get_identity`; verify the agent ID, project path and `execution_mode=desktop_poll`. If the project MCP is missing or stale, tell the user to trust the project and reload its MCP before continuing.
|
|
11
11
|
- Obtain the actual current Codex conversation ID using a supported app context or the current shell's `CODEX_THREAD_ID` if exposed. If unavailable, ask the user for the target thread's copied ID/link. Do not guess an ID or scrape/modify app session databases.
|
|
12
12
|
- Call `bind_desktop_thread(thread_id)` once. Binding is local client routing, not app-attested caller authentication. Other conversations with access to the same local credentials are not a security boundary.
|
|
13
|
-
- Create a recurring **heartbeat attached to this existing conversation** using the app's automation tool. Use its supported minimum/selected interval (start with every minute when accepted). Do not create standalone cron/new SDK threads as a substitute.
|
|
13
|
+
- Inspect existing collaboration automations first. Create or update a recurring **heartbeat attached to this existing conversation** using the app's automation tool. Use its supported minimum/selected interval (start with every minute when accepted). Do not create standalone cron/new SDK threads as a substitute or modify unrelated automations or provider settings. Save a prompt containing the verified agent ID, actual bound thread ID, and absolute path to this Skill. The prompt must instruct each run to verify identity, poll, handle natural-language requests or the assigned stage according to this Skill, renew leases and report results; remain quiet when nothing actionable changes. Reuse this conversation's collaboration heartbeat to avoid duplicates.
|
|
14
14
|
- If the automation tool/interval is unavailable, report that binding alone does not wake this conversation; do not claim automatic execution is enabled.
|
|
15
|
+
- After configuring the heartbeat, immediately call `poll_desktop_task` with the verified thread ID and handle any returned work using the workflow below. Do not invent a test task. Report the verified agent ID, thread ID, heartbeat status and actual polling result so the local wizard can observe readiness.
|
|
15
16
|
|
|
16
17
|
## On each heartbeat
|
|
17
18
|
|
package/packages/peer/inbox.mjs
CHANGED
|
@@ -9,8 +9,10 @@ const fields = (value, allowed) => value && typeof value === 'object' && !Array.
|
|
|
9
9
|
export function validateInboxSpec(spec, team) {
|
|
10
10
|
if (!fields(spec, ['schema_version', 'id', 'repo', 'target_agent', 'chat_id', 'sender_id', 'content', 'message_key', 'admin_dispatch']) || spec.schema_version !== 1 || !inboxIdPattern.test(spec.id || '') || spec.repo !== team.repo || !text(spec.target_agent, 64) || !/^[a-zA-Z0-9_-]+$/.test(spec.target_agent) || !Object.hasOwn(team.agents, spec.target_agent) || !text(spec.chat_id, 200) || !text(spec.sender_id, 200) || !text(spec.content, 4000) || !text(spec.message_key, 300) || typeof spec.admin_dispatch !== 'boolean' || spec.id !== `N-${digest([spec.target_agent, spec.message_key]).slice(0, 32).toUpperCase()}`) throw new Error('invalid natural-language inbox specification');
|
|
11
11
|
const owner = team.agents[spec.target_agent].wecom_userid === spec.sender_id;
|
|
12
|
-
const
|
|
13
|
-
|
|
12
|
+
const senders = Object.values(team.agents).filter((agent) => agent.wecom_userid === spec.sender_id);
|
|
13
|
+
const member = senders.some((agent) => agent.role === 'member');
|
|
14
|
+
const admin = spec.admin_dispatch && senders.some((agent) => agent.role === 'admin');
|
|
15
|
+
if (!owner && !member && !admin) throw new Denied('only the bot owner or an enrolled team member may submit requests');
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
/** Append-only requests: only the receiving agent signs interpretation/replies. */
|
|
@@ -24,8 +24,10 @@ export class PeerService {
|
|
|
24
24
|
const key = inboxMessageKey(this.agentId, chatId, messageId);
|
|
25
25
|
return this.store.transactInbox(key, (requests) => {
|
|
26
26
|
const owner = this.team.agents[this.agentId]?.wecom_userid === senderId;
|
|
27
|
-
const
|
|
28
|
-
|
|
27
|
+
const senderAgents = Object.values(this.team.agents).filter((agent) => agent.wecom_userid === senderId);
|
|
28
|
+
const member = senderAgents.some((agent) => agent.role === 'member');
|
|
29
|
+
const admin = allowAdmin && senderAgents.some((agent) => agent.role === 'admin');
|
|
30
|
+
if (!senderId || !owner && !member && !admin) throw new Denied('sender is not authorized for this bot');
|
|
29
31
|
const referencedId = content.match(/\bN-[A-F0-9]{32}\b/)?.[0];
|
|
30
32
|
const mine = requests.filter((request) => request.spec.target_agent === this.agentId && request.spec.chat_id === chatId && request.spec.sender_id === senderId);
|
|
31
33
|
const waiting = mine.filter((request) => request.state.status === 'clarification');
|
|
@@ -41,8 +43,10 @@ export class PeerService {
|
|
|
41
43
|
const original = event.type === 'received' ? request.spec.content : event.content;
|
|
42
44
|
if (request.spec.chat_id !== chatId || request.spec.sender_id !== senderId || original !== content) throw new Conflict('group message ID already used with different content or sender');
|
|
43
45
|
const owner = this.team.agents[this.agentId]?.wecom_userid === senderId;
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
+
const senderAgents = Object.values(this.team.agents).filter((agent) => agent.wecom_userid === senderId);
|
|
47
|
+
const member = senderAgents.some((agent) => agent.role === 'member');
|
|
48
|
+
const admin = allowAdmin && senderAgents.some((agent) => agent.role === 'admin');
|
|
49
|
+
if (!owner && !member && !admin) throw new Denied('sender is not authorized for this bot');
|
|
46
50
|
} });
|
|
47
51
|
}
|
|
48
52
|
async resolveRequest(id, expectedSeq, resolution, { defaultWorkflow } = {}) {
|
|
@@ -59,5 +59,5 @@
|
|
|
59
59
|
"type": "boolean"
|
|
60
60
|
}
|
|
61
61
|
},
|
|
62
|
-
"$comment": "Runtime also verifies the repo and target against the pinned team, a deterministic request ID derived from target_agent and message_key, and bot
|
|
62
|
+
"$comment": "Runtime also verifies the repo and target against the pinned team, a deterministic request ID derived from target_agent and message_key, and authorization by the bot owner, an enrolled team member, or an explicitly allowed administrator. The original group request is shared in Git; private Codex transcripts, bindings and credentials stay local."
|
|
63
63
|
}
|
|
@@ -6,7 +6,7 @@ import { readPeerConfig } from '../../apps/peer/runtime.mjs';
|
|
|
6
6
|
import { git } from '../../packages/peer/store.mjs';
|
|
7
7
|
import { bindingPrompt } from './setup-desktop-project.mjs';
|
|
8
8
|
const root = fileURLToPath(new URL('../../', import.meta.url));
|
|
9
|
-
const legacyHashes = new Set(['f98ad425e2ea3c8ab6fb86febef7d9cb288029ef55bea6937dda6de8b467fece']);
|
|
9
|
+
const legacyHashes = new Set(['6e4d1ea6a644fb3078c541e40bcc9e8bf5069462994152f3ec2fc3aa5fedd5fa', 'f98ad425e2ea3c8ab6fb86febef7d9cb288029ef55bea6937dda6de8b467fece']);
|
|
10
10
|
const hash = (value) => createHash('sha256').update(value).digest('hex');
|
|
11
11
|
const inside = (parent, child) => { const path = relative(parent, child); return !path || path !== '..' && !path.startsWith('../') && !isAbsolute(path); };
|
|
12
12
|
async function plainPath(base, path) {
|
|
@@ -8,6 +8,9 @@ const absent = async (path) => { try { await stat(path); return false; } catch (
|
|
|
8
8
|
const readOptional = async (path) => { try { return await readFile(path, 'utf8'); } catch (error) { if (error.code === 'ENOENT') return null; throw error; } };
|
|
9
9
|
|
|
10
10
|
export function bindingPrompt(agentId, repo) {
|
|
11
|
+
return `使用 $team-collab-desktop,启用当前项目的团队协作,将 ${agentId} 机器人绑定到当前对话。项目:${repo}`;
|
|
12
|
+
}
|
|
13
|
+
export function detailedBindingPrompt(agentId, repo) {
|
|
11
14
|
return `请把当前已有 Codex 对话设为 ${agentId} 的企微协作执行对话,不要新建独立执行线程。\n项目:${repo}\n读取 ${join(repo, '.agents/skills/team-collab-desktop/SKILL.md')}。\n1. 用本项目 MCP 的 get_identity 确认身份是 ${agentId},execution_mode 是 desktop_poll;若不匹配,停止并提示重新加载 MCP。\n2. 获取当前真实对话 ID:优先使用已提供的应用上下文,或当前终端 CODEX_THREAD_ID(如果存在);不可猜 ID 或读取/修改应用数据库。没有可用 ID 时请让我复制当前对话 ID/链接。\n3. 调用 bind_desktop_thread 绑定当前真实对话,然后用应用 automation_update 工具在当前已有对话创建一个 heartbeat:每分钟检查(仅当工具接受该周期;否则说明限制),不要创建 cron、新对话或后台 SDK。先查现有 automation,避免重复;不要修改无关 automation 或 CC Switch。\n4. heartbeat 的保存提示词必须包含 ${agentId}、当前真实 thread_id 和该 skill 路径;每次 get_identity → poll_desktop_task。job=null 且 requests 为空 时保持安静;有任务就只在本对话和返回的隔离 worktree 执行本阶段,主动续租、测试、明确提交,然后 finish_desktop_task;失败时按 skill 处理。requests 非空时按 Skill 理解群消息,调用 resolve_desktop_request 创建任务、追问或回答;使用 team_context 的现有路由、能力与默认分工,不能猜测不存在的同事。解决后再次 poll 开始工作。自然语言“通过/完成”不能代替人工审核命令。复用或更新当前对话已有的协作 heartbeat,避免重复创建。禁止启动 SDK/CLI 代替当前对话。仅新任务、结果、失败或需我操作时通知。\n5. 创建完成后立即调用一次 poll_desktop_task 验证,无任务应正常返回,不要创建测试任务。告诉我绑定身份、对话 ID 和 heartbeat 状态;若工具不可用明确说明未启用自动轮询。\n`;
|
|
12
15
|
}
|
|
13
16
|
|