xuym-codex-collab 0.4.1 → 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 +2 -0
- package/apps/peer/wecom.mjs +6 -4
- package/docs/desktop-poll.md +1 -1
- package/docs/onboarding.md +2 -0
- package/package.json +1 -1
- package/packages/peer/inbox.mjs +4 -2
- package/packages/peer/service.mjs +8 -4
- package/schemas/peer-inbox.schema.json +1 -1
package/README.md
CHANGED
|
@@ -22,6 +22,8 @@ codex-collab setup
|
|
|
22
22
|
|
|
23
23
|
同一个真人可以拥有多个 Agent,每个 Agent 使用独立机器人、签名身份、业务 clone 和 Codex 对话。管理员在项目页点击“给自己增加机器人”,验证同一个企业微信账号后自动接入,无需下载包或填写包密码;给同事分配时仍使用加密接入包,同事可以领取多份。相同 userid 不会合并 Agent 权限,审核通知会指明应使用的机器人。
|
|
24
24
|
|
|
25
|
+
机器人在企业微信后台设为多人可用并加入协作群后,已批准入队的成员可以 `@` 任意团队机器人的自然语言需求;未入队的群成员仍会被工具拒绝。普通成员可提交需求、创建任务和查询状态,审核与取消、完成等控制操作继续受原有身份权限限制。接入完成后,团队成员可直接互相派发,不必再改机器人配置。
|
|
26
|
+
|
|
25
27
|
本机配置完成后,向导只要求在目标 Codex 对话发送一条指令,例如:
|
|
26
28
|
|
|
27
29
|
```text
|
package/apps/peer/wecom.mjs
CHANGED
|
@@ -37,10 +37,12 @@ export async function dispatchPeerWecom(frame, { service, config, reply }) {
|
|
|
37
37
|
const sender = body.from?.userid;
|
|
38
38
|
const owner = !!sender && sender === service.team.agents[service.agentId]?.wecom_userid;
|
|
39
39
|
const admin = config.allow_admin_commands === true && !!sender && Object.values(service.team.agents).some((agent) => agent.role === 'admin' && agent.wecom_userid === sender);
|
|
40
|
+
const member = !!sender && Object.values(service.team.agents).some((agent) => agent.role === 'member' && agent.wecom_userid === sender);
|
|
40
41
|
// Shared owners do not merge agent roles: the receiving bot selects its signer.
|
|
41
|
-
//
|
|
42
|
-
// the
|
|
43
|
-
|
|
42
|
+
// Enrolled team members may submit work to any bot; review/control mutations
|
|
43
|
+
// still require the bot owner. Admin dispatch retains its explicit config gate.
|
|
44
|
+
const dispatchOnly = ['create', 'status', 'natural'].includes(command?.action || 'natural');
|
|
45
|
+
if (!owner && !(dispatchOnly && (member || admin))) return false;
|
|
44
46
|
const key = `wecom:${body.chatid}:${body.msgid}`;
|
|
45
47
|
let text;
|
|
46
48
|
try {
|
|
@@ -57,7 +59,7 @@ export async function dispatchPeerWecom(frame, { service, config, reply }) {
|
|
|
57
59
|
if (config.execution_mode !== 'desktop_poll') {
|
|
58
60
|
await reply(frame, '自然语言任务需要绑定已有 Codex 对话,并启用 desktop_poll 轮询。'); return true;
|
|
59
61
|
}
|
|
60
|
-
const request = await service.receiveRequest({ chatId: body.chatid, messageId: body.msgid, senderId: sender, content, allowAdmin:
|
|
62
|
+
const request = await service.receiveRequest({ chatId: body.chatid, messageId: body.msgid, senderId: sender, content, allowAdmin: admin });
|
|
61
63
|
text = request.state.task_id ? `该消息已创建任务 ${request.state.task_id},不会重复创建。`
|
|
62
64
|
: request.state.status === 'answered' ? '这条消息已处理。'
|
|
63
65
|
: request.state.status === 'clarification' ? `需求 ${request.spec.id} 等待你补充说明,请查看群里的追问。`
|
package/docs/desktop-poll.md
CHANGED
|
@@ -138,7 +138,7 @@ sh test.sh 要通过,完成后在群里发总结。
|
|
|
138
138
|
已有绑定不需要重新创建身份。先处理正在执行的任务,再对每个 Agent 用其**项目 MCP 实际使用的私有配置路径**操作:
|
|
139
139
|
|
|
140
140
|
```sh
|
|
141
|
-
npm install --global xuym-codex-collab@0.4.
|
|
141
|
+
npm install --global xuym-codex-collab@0.4.2
|
|
142
142
|
codex-collab stop /absolute/private-peer.json
|
|
143
143
|
codex-collab refresh-project /absolute/private-peer.json
|
|
144
144
|
codex-collab start /absolute/private-peer.json
|
package/docs/onboarding.md
CHANGED
|
@@ -32,6 +32,8 @@ codex-collab setup
|
|
|
32
32
|
|
|
33
33
|
显示名称保存在本机和加密接入包中;团队签名和群协议仍用内部 Agent ID。真人通过自己的企业微信账号验证接入,以便核对审核权限;额外通知对象不会因此获得审核权限。
|
|
34
34
|
|
|
35
|
+
机器人应在企业微信后台设为多人可用,并把实际协作成员加入可使用范围。平台允许使用后,工具还会核对发言人的 `userid` 是否属于已批准入队的成员;成员可以 `@` 任意团队机器人提交需求、创建任务或查询状态。审核、取消、完成等操作仍按任务和本人权限限制。未完成团队接入的群成员不能派发任务。
|
|
36
|
+
|
|
35
37
|
机器人必须由一个本机 Runner 保持连接。同一个 Bot ID 不要同时运行旧服务、群发现命令和新 Runner。向导检查可识别的本机重复连接;其他电脑上的连接需由团队核实。
|
|
36
38
|
|
|
37
39
|
## 成员操作
|
package/package.json
CHANGED
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
|
}
|