botmux 3.9.0 → 3.9.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.
Files changed (112) hide show
  1. package/dist/.runtime-build-id +1 -1
  2. package/dist/adapters/backend/tmux-pipe-backend.d.ts +7 -0
  3. package/dist/adapters/backend/tmux-pipe-backend.d.ts.map +1 -1
  4. package/dist/adapters/backend/tmux-pipe-backend.js +20 -0
  5. package/dist/adapters/backend/tmux-pipe-backend.js.map +1 -1
  6. package/dist/adapters/backend/types.d.ts +12 -0
  7. package/dist/adapters/backend/types.d.ts.map +1 -1
  8. package/dist/adapters/backend/types.js.map +1 -1
  9. package/dist/adapters/cli/codex.d.ts.map +1 -1
  10. package/dist/adapters/cli/codex.js +40 -7
  11. package/dist/adapters/cli/codex.js.map +1 -1
  12. package/dist/adapters/cli/traex.d.ts.map +1 -1
  13. package/dist/adapters/cli/traex.js +87 -87
  14. package/dist/adapters/cli/traex.js.map +1 -1
  15. package/dist/cli/deferred-topic-send.d.ts +11 -0
  16. package/dist/cli/deferred-topic-send.d.ts.map +1 -1
  17. package/dist/cli/deferred-topic-send.js +21 -4
  18. package/dist/cli/deferred-topic-send.js.map +1 -1
  19. package/dist/cli.d.ts.map +1 -1
  20. package/dist/cli.js +177 -38
  21. package/dist/cli.js.map +1 -1
  22. package/dist/core/command-handler.d.ts.map +1 -1
  23. package/dist/core/command-handler.js +40 -1
  24. package/dist/core/command-handler.js.map +1 -1
  25. package/dist/core/dispatch.d.ts +43 -5
  26. package/dist/core/dispatch.d.ts.map +1 -1
  27. package/dist/core/dispatch.js +104 -6
  28. package/dist/core/dispatch.js.map +1 -1
  29. package/dist/core/passthrough-commands.d.ts.map +1 -1
  30. package/dist/core/passthrough-commands.js +1 -1
  31. package/dist/core/passthrough-commands.js.map +1 -1
  32. package/dist/daemon.d.ts +11 -1
  33. package/dist/daemon.d.ts.map +1 -1
  34. package/dist/daemon.js +32 -3
  35. package/dist/daemon.js.map +1 -1
  36. package/dist/i18n/en.d.ts.map +1 -1
  37. package/dist/i18n/en.js +2 -0
  38. package/dist/i18n/en.js.map +1 -1
  39. package/dist/i18n/zh.d.ts.map +1 -1
  40. package/dist/i18n/zh.js +2 -0
  41. package/dist/i18n/zh.js.map +1 -1
  42. package/dist/im/lark/card-handler.d.ts.map +1 -1
  43. package/dist/im/lark/card-handler.js +9 -0
  44. package/dist/im/lark/card-handler.js.map +1 -1
  45. package/dist/im/lark/issue-card.d.ts +223 -0
  46. package/dist/im/lark/issue-card.d.ts.map +1 -0
  47. package/dist/im/lark/issue-card.js +542 -0
  48. package/dist/im/lark/issue-card.js.map +1 -0
  49. package/dist/im/lark/issue-command-deps.d.ts +41 -0
  50. package/dist/im/lark/issue-command-deps.d.ts.map +1 -0
  51. package/dist/im/lark/issue-command-deps.js +195 -0
  52. package/dist/im/lark/issue-command-deps.js.map +1 -0
  53. package/dist/im/lark/issue-command.d.ts +137 -0
  54. package/dist/im/lark/issue-command.d.ts.map +1 -0
  55. package/dist/im/lark/issue-command.js +335 -0
  56. package/dist/im/lark/issue-command.js.map +1 -0
  57. package/dist/platform/issue-client.d.ts +201 -0
  58. package/dist/platform/issue-client.d.ts.map +1 -0
  59. package/dist/platform/issue-client.js +142 -0
  60. package/dist/platform/issue-client.js.map +1 -0
  61. package/dist/platform/platform-http.d.ts +4 -0
  62. package/dist/platform/platform-http.d.ts.map +1 -1
  63. package/dist/platform/platform-http.js +38 -2
  64. package/dist/platform/platform-http.js.map +1 -1
  65. package/dist/services/codex-composer-state.d.ts +27 -0
  66. package/dist/services/codex-composer-state.d.ts.map +1 -0
  67. package/dist/services/codex-composer-state.js +53 -0
  68. package/dist/services/codex-composer-state.js.map +1 -0
  69. package/dist/services/codex-transcript.d.ts +33 -12
  70. package/dist/services/codex-transcript.d.ts.map +1 -1
  71. package/dist/services/codex-transcript.js +82 -20
  72. package/dist/services/codex-transcript.js.map +1 -1
  73. package/dist/services/issue-board-store.d.ts +214 -0
  74. package/dist/services/issue-board-store.d.ts.map +1 -0
  75. package/dist/services/issue-board-store.js +472 -0
  76. package/dist/services/issue-board-store.js.map +1 -0
  77. package/dist/services/issue-claim-flow.d.ts +160 -0
  78. package/dist/services/issue-claim-flow.d.ts.map +1 -0
  79. package/dist/services/issue-claim-flow.js +222 -0
  80. package/dist/services/issue-claim-flow.js.map +1 -0
  81. package/dist/services/issue-outbox-pump.d.ts +25 -0
  82. package/dist/services/issue-outbox-pump.d.ts.map +1 -0
  83. package/dist/services/issue-outbox-pump.js +102 -0
  84. package/dist/services/issue-outbox-pump.js.map +1 -0
  85. package/dist/services/issue-release.d.ts +78 -0
  86. package/dist/services/issue-release.d.ts.map +1 -0
  87. package/dist/services/issue-release.js +89 -0
  88. package/dist/services/issue-release.js.map +1 -0
  89. package/dist/services/issue-report.d.ts +72 -0
  90. package/dist/services/issue-report.d.ts.map +1 -0
  91. package/dist/services/issue-report.js +84 -0
  92. package/dist/services/issue-report.js.map +1 -0
  93. package/dist/services/issue-status-view.d.ts +52 -0
  94. package/dist/services/issue-status-view.d.ts.map +1 -0
  95. package/dist/services/issue-status-view.js +42 -0
  96. package/dist/services/issue-status-view.js.map +1 -0
  97. package/dist/services/issue-status-writer.d.ts +98 -0
  98. package/dist/services/issue-status-writer.d.ts.map +1 -0
  99. package/dist/services/issue-status-writer.js +123 -0
  100. package/dist/services/issue-status-writer.js.map +1 -0
  101. package/dist/services/traex-paths.d.ts +14 -2
  102. package/dist/services/traex-paths.d.ts.map +1 -1
  103. package/dist/services/traex-paths.js +16 -2
  104. package/dist/services/traex-paths.js.map +1 -1
  105. package/dist/services/traex-transcript.d.ts +33 -0
  106. package/dist/services/traex-transcript.d.ts.map +1 -1
  107. package/dist/services/traex-transcript.js +135 -28
  108. package/dist/services/traex-transcript.js.map +1 -1
  109. package/dist/setup/lark-scopes.json +0 -2
  110. package/dist/worker.js +269 -7
  111. package/dist/worker.js.map +1 -1
  112. package/package.json +1 -1
@@ -0,0 +1,335 @@
1
+ /**
2
+ * `/issue` 的命令入口与卡片回调。
3
+ *
4
+ * 卡片渲染在 [[issue-card]](纯数据、无副作用),这里负责取数、鉴权、跑领取流程。
5
+ *
6
+ * ## 谁能操作
7
+ *
8
+ * 两道门,都 fail-closed:
9
+ * 1. **allowedUsers**——`/issue` 会真的领任务、建群、起 agent,不是只读命令,按 bot 的
10
+ * 管理员名单来(与 `/skills attach` 同一套)。
11
+ * 2. **invoker lock**——卡片带 `invoker_open_id`,只有发起那个人能点。群里别人点了不算。
12
+ * 这条不是洁癖:平台的 claim 是按**本机 owner** 记的,不是按点击者记的。谁点都能领的
13
+ * 话,任务会记在 owner 头上而点的人是别人,归属直接错位。
14
+ *
15
+ * 身份只认 `operator.*`(Lark 校验过),绝不从 `action.value` 里读——那份是卡片里带回来的,
16
+ * 未经校验。
17
+ */
18
+ import { logger } from '../../utils/logger.js';
19
+ import { scanMultipleProjects } from '../../services/project-scanner.js';
20
+ import { configuredWorkingDirs, expandHomePath } from '../../utils/working-dir.js';
21
+ import { ISSUE_ACTION_CLAIM_CANCEL, ISSUE_ACTION_CLAIM_CONFIRM, ISSUE_ACTION_CLAIM_DIR, ISSUE_ACTION_CLAIM_OPEN, ISSUE_ACTION_PAGE, ISSUE_ACTION_REFRESH, ISSUE_ACTION_TEAM, buildClaimConfirmCard, buildClaimResultCard, buildIssueBoardCard, claimFailureHint, matchRepo, } from './issue-card.js';
22
+ function toast(content) {
23
+ return { toast: { type: 'error', content } };
24
+ }
25
+ /** 把内部统一产出的卡片字符串包成 Lark 回调要的信封。 */
26
+ function asCallback(r) {
27
+ if ('toast' in r)
28
+ return r;
29
+ return { card: { type: 'raw', data: JSON.parse(r.card) } };
30
+ }
31
+ /** 平台 issue → 卡片行。只留卡片用得到的字段,别把整个 doc 塞进 action.value。 */
32
+ function toRow(i) {
33
+ return {
34
+ issueId: i._id,
35
+ title: i.title,
36
+ ...(i.targetRepoLabel ? { repoLabel: i.targetRepoLabel } : {}),
37
+ stateRev: i.stateRev,
38
+ ...(i.claim?.name ? { claimedByName: i.claim.name } : {}),
39
+ };
40
+ }
41
+ /**
42
+ * 扫出该 bot 可选的仓库。
43
+ *
44
+ * 不能直接用 `workingDirs`:它常常配的是一个工作区父目录(`~/claude-code-workspace`),
45
+ * 直接当候选,选中的会是工作区根目录。扫一层才拿得到真正的仓库和 worktree。
46
+ */
47
+ export function reposFor(larkAppId, deps) {
48
+ const dirs = configuredWorkingDirs({ workingDirs: deps.workingDirs(larkAppId) }).map(expandHomePath);
49
+ if (!dirs.length)
50
+ return [];
51
+ try {
52
+ return scanMultipleProjects(dirs, 3, { includeWorktrees: true }).map((p) => ({
53
+ name: p.name,
54
+ path: p.path,
55
+ ...(p.branch ? { branch: p.branch } : {}),
56
+ }));
57
+ }
58
+ catch (e) {
59
+ // 扫描失败不该让整个命令挂掉:回落到空候选,确认卡会说明"没扫到仓库"。
60
+ logger.warn(`[issue] 扫描仓库失败: ${String(e?.message ?? e)}`);
61
+ return [];
62
+ }
63
+ }
64
+ async function buildBoard(larkAppId, deps, opts) {
65
+ const teams = await deps.fetchTeams();
66
+ if (!teams.ok || !teams.value) {
67
+ return toast(teams.reason === 'unbound' ? '本机还没有绑定 botmux 平台' : `拉取团队失败:${teams.reason}`);
68
+ }
69
+ if (!teams.value.length)
70
+ return toast('你不在任何 botmux 平台团队里');
71
+ const teamId = opts.teamId && teams.value.some((t) => t.teamId === opts.teamId) ? opts.teamId : teams.value[0].teamId;
72
+ const team = teams.value.find((t) => t.teamId === teamId);
73
+ const issues = await deps.fetchIssues(teamId);
74
+ if (!issues.ok || !issues.value)
75
+ return toast(`拉取任务失败:${issues.reason}`);
76
+ const s = issues.value;
77
+ const data = {
78
+ teamId,
79
+ teamName: team.teamName,
80
+ teams: teams.value,
81
+ sections: {
82
+ needsAttention: (s.needsAttention ?? []).map(toRow),
83
+ todo: (s.todo ?? []).map(toRow),
84
+ inProgress: (s.inProgress ?? []).map(toRow),
85
+ inReview: (s.inReview ?? []).map(toRow),
86
+ done: (s.done ?? []).map(toRow),
87
+ },
88
+ page: opts.page ?? 0,
89
+ invokerOpenId: opts.invokerOpenId,
90
+ };
91
+ return { card: buildIssueBoardCard(data) };
92
+ }
93
+ /** `/issue`(裸)→ 看板卡片。 */
94
+ export async function handleIssueCommand(larkAppId, senderOpenId, deps) {
95
+ if (!senderOpenId)
96
+ return toast('无法识别操作者身份');
97
+ const admins = deps.allowedUsers(larkAppId);
98
+ if (!admins.length)
99
+ return toast('这个 bot 还没有配置管理员');
100
+ if (!admins.includes(senderOpenId))
101
+ return toast('只有管理员可以操作 Issue Board');
102
+ return buildBoard(larkAppId, deps, { invokerOpenId: senderOpenId });
103
+ }
104
+ function info(content) {
105
+ return { toast: { type: 'info', content } };
106
+ }
107
+ function err(content) {
108
+ return { toast: { type: 'error', content } };
109
+ }
110
+ /** 管理员门。三个群内命令共用;通过时返回 undefined。 */
111
+ function adminGate(larkAppId, senderOpenId, deps) {
112
+ if (!senderOpenId)
113
+ return err('无法识别操作者身份');
114
+ const admins = deps.allowedUsers(larkAppId);
115
+ if (!admins.length)
116
+ return err('这个 bot 还没有配置管理员');
117
+ if (!admins.includes(senderOpenId))
118
+ return err('只有管理员可以操作 Issue Board');
119
+ return undefined;
120
+ }
121
+ /**
122
+ * 依次试锚点,返回第一个「不是 no_binding」的结果。
123
+ *
124
+ * 锚点候选按 [[core/types]] 的 `sessionAnchorId` 语义给:拉群会话锚在 chatId、话题会话锚在
125
+ * rootMessageId。调用方两个都传、这里依次试——比让调用方先判 scope 简单,两个地址空间
126
+ * (`oc_` / `om_`)不会撞,试错没有歧义。
127
+ *
128
+ * 只要有一个锚点给出了非 no_binding 的结果就停:继续试下去会把真实失败盖成"没有领取记录"。
129
+ */
130
+ async function tryAnchors(anchorCandidates, run) {
131
+ let last;
132
+ for (const anchorId of anchorCandidates.filter((a) => !!a)) {
133
+ last = await run(anchorId);
134
+ if (last.ok || last.reason !== 'no_binding')
135
+ break;
136
+ }
137
+ return last;
138
+ }
139
+ /** 三个终态 bindState 各自的说法。糊成一句「已结束」人会以为自己记错了。 */
140
+ function terminalHint(bindState) {
141
+ if (bindState === 'done')
142
+ return '这条任务已经验收完成(done),平台上已经终结了。';
143
+ if (bindState === 'void')
144
+ return '这条领取此前被平台拒绝、已作废,无需再操作。';
145
+ return '这条领取此前就已经释放了,无需重复操作。';
146
+ }
147
+ /**
148
+ * `/issue release` → 释放当前会话领取的那个 issue,退回平台「待领取」。
149
+ *
150
+ * 在**领取时建出来的那个群里**发。返回文本而不是卡片:这是个一次性动作,结果就一句话,
151
+ * 发张卡反而重。
152
+ */
153
+ export async function handleIssueRelease(larkAppId, senderOpenId, anchorCandidates, deps) {
154
+ const denied = adminGate(larkAppId, senderOpenId, deps);
155
+ if (denied)
156
+ return denied;
157
+ const last = await tryAnchors(anchorCandidates, deps.runRelease);
158
+ if (!last)
159
+ return err('拿不到当前会话的锚点,无法定位领取记录');
160
+ if (last.ok) {
161
+ logger.info(`[issue] 释放成功 issue=${last.issueId} platform_already=${last.alreadyReleasedOnPlatform}`);
162
+ return info(last.alreadyReleasedOnPlatform
163
+ ? '✅ 已释放。平台上这条任务此前就已经不归本机了(可能被回收或已被别人领走),本机记录已同步。\n\n群和会话都还在,需要的话自己停会话或退群。'
164
+ : '✅ 已释放,任务已退回平台「待领取」,别人可以领了。\n\n群和会话都还在,**不会自动解散**——里面的对话记录还留着,要停会话发 `/stop`。');
165
+ }
166
+ if (last.reason === 'no_binding')
167
+ return err('这个会话没有领取任何平台任务,没什么可释放的。');
168
+ if (last.reason === 'already_released')
169
+ return info(terminalHint(last.bindState));
170
+ logger.warn(`[issue] 释放失败 detail=${last.detail} permanent=${last.permanent === true}`);
171
+ // 劝人重试之前先看这次失败是不是永久性的:401/403/404 再发一百次也一样,那句"稍后再试"
172
+ // 会让人一直重试到放弃,而真正该做的是去平台上看这条任务还在不在。
173
+ return err(last.permanent
174
+ ? `❌ 释放失败:${last.detail}\n\n平台明确拒绝了,重试也不会好转(凭证失效、或这条任务已被删除/归档)。去平台上确认这条任务的状态。`
175
+ : `❌ 释放失败:${last.detail}\n\n本机记录保持不变(平台仍认为这台机器持有),稍后可以再发一次 \`/issue release\`。`);
176
+ }
177
+ /**
178
+ * `/issue done` → 验收通过,把任务推到平台的终态。
179
+ *
180
+ * 这一步是**人的决策**,不是 agent 能自己走的:agent 交付只到「待验收」([[issue-report]]),
181
+ * 之后由人看过产出再决定完成还是打回。所以入口是群里的管理员命令,而不是 `botmux` 的某个
182
+ * 子命令——放在 CLI 里 agent 就能自己盖章验收,那这个状态就没有意义了。
183
+ *
184
+ * 平台侧 `done` 会清掉 claim(`clearsClaim`),这条领取就此终结、不能再释放。
185
+ */
186
+ export async function handleIssueDone(larkAppId, senderOpenId, anchorCandidates, deps) {
187
+ const denied = adminGate(larkAppId, senderOpenId, deps);
188
+ if (denied)
189
+ return denied;
190
+ const last = await tryAnchors(anchorCandidates, deps.runDone);
191
+ if (!last)
192
+ return err('拿不到当前会话的锚点,无法定位领取记录');
193
+ if (last.ok) {
194
+ logger.info(`[issue] 验收完成 issue=${last.issueId} platform_already=${last.alreadyReleasedOnPlatform}`);
195
+ return info(last.alreadyReleasedOnPlatform
196
+ ? '✅ 已标记完成。平台上这条任务此前就已经不归本机了,本机记录已同步。\n\n群和会话都还在,需要的话自己停会话或退群。'
197
+ : '🎉 已验收完成,平台上这条任务已终结(不能再释放了)。\n\n群和会话都还在,**不会自动解散**——要停会话发 `/stop`。');
198
+ }
199
+ if (last.reason === 'no_binding')
200
+ return err('这个会话没有领取任何平台任务,没什么可验收的。');
201
+ if (last.reason === 'already_released')
202
+ return info(terminalHint(last.bindState));
203
+ logger.warn(`[issue] 验收完成失败 detail=${last.detail} permanent=${last.permanent === true}`);
204
+ // 平台的追赶白名单不允许 needs_attention → done。这是设计而不是故障,得说清楚下一步。
205
+ const blocked = /invalid_transition/.test(last.detail ?? '');
206
+ return err(blocked
207
+ ? `❌ 平台拒绝了这次状态变更:${last.detail}\n\n多半是这条任务当前在「需要关注」——平台不允许从那里直接标完成。先去平台上处理(或发 \`/issue release\` 退回待领取)。`
208
+ : last.permanent
209
+ ? `❌ 标记完成失败:${last.detail}\n\n平台明确拒绝了,重试也不会好转(凭证失效、或这条任务已被删除/归档)。去平台上确认这条任务的状态。`
210
+ : `❌ 标记完成失败:${last.detail}\n\n本机记录保持不变,稍后可以再发一次 \`/issue done\`。`);
211
+ }
212
+ /**
213
+ * `/issue status` → 摊开本机与平台两边的现状。
214
+ *
215
+ * 只读,所以**不设终态门**:这个命令的用处恰恰是在「这个群是不是已经结束了」存疑的时候
216
+ * 回答它,把终态 binding 当成"没有绑定"是把唯一的答案藏起来。
217
+ */
218
+ export async function handleIssueStatus(larkAppId, senderOpenId, anchorCandidates, deps) {
219
+ const denied = adminGate(larkAppId, senderOpenId, deps);
220
+ if (denied)
221
+ return denied;
222
+ const last = await tryAnchors(anchorCandidates, deps.runStatus);
223
+ if (!last)
224
+ return toast('拿不到当前会话的锚点,无法定位领取记录');
225
+ if (!last.ok)
226
+ return toast('这个会话没有领取任何平台任务。');
227
+ return { card: last.card };
228
+ }
229
+ /** 卡片回调。所有 `issue_*` action 都走这里。 */
230
+ export async function handleIssueCardAction(data, larkAppId, deps) {
231
+ return asCallback(await handleIssueCardActionInner(data, larkAppId, deps));
232
+ }
233
+ async function handleIssueCardActionInner(data, larkAppId, deps) {
234
+ const value = (data.action?.value ?? {});
235
+ const operatorOpenId = data.operator?.open_id;
236
+ const action = value.action;
237
+ // ── invoker lock(fail-closed)─────────────────────────────────────────
238
+ const invokerOpenId = value.invoker_open_id;
239
+ if (!invokerOpenId || !operatorOpenId || invokerOpenId !== operatorOpenId) {
240
+ return toast('这张卡片只有发起人能操作');
241
+ }
242
+ // 每次回调都重跑权限门:发卡之后管理员名单可能已经改了。
243
+ const admins = deps.allowedUsers(larkAppId);
244
+ if (!admins.includes(operatorOpenId))
245
+ return toast('只有管理员可以操作 Issue Board');
246
+ const teamId = value.teamId;
247
+ if (action === ISSUE_ACTION_REFRESH || action === ISSUE_ACTION_PAGE || action === ISSUE_ACTION_TEAM) {
248
+ // 团队切换的新 teamId 来自下拉的选中值,不在 value 里。
249
+ const picked = action === ISSUE_ACTION_TEAM ? selectedOption(data) ?? teamId : teamId;
250
+ const page = action === ISSUE_ACTION_PAGE ? Number(value.page) || 0 : 0;
251
+ return buildBoard(larkAppId, deps, { teamId: picked, page, invokerOpenId });
252
+ }
253
+ if (action === ISSUE_ACTION_CLAIM_CANCEL) {
254
+ return buildBoard(larkAppId, deps, { teamId, invokerOpenId });
255
+ }
256
+ if (action === ISSUE_ACTION_CLAIM_OPEN || action === ISSUE_ACTION_CLAIM_DIR) {
257
+ const issue = await findIssue(teamId, value.issueId, deps);
258
+ if (!issue)
259
+ return toast('这条任务已经不在待领取列表里了,刷新看看');
260
+ const repos = reposFor(larkAppId, deps);
261
+ // 下拉切换时以人选的为准;首次打开才用标签自动匹配。
262
+ const picked = action === ISSUE_ACTION_CLAIM_DIR ? selectedOption(data) : matchRepo(issue.targetRepoLabel, repos);
263
+ return {
264
+ card: buildClaimConfirmCard({
265
+ teamId,
266
+ issueId: issue._id,
267
+ title: issue.title,
268
+ ...(issue.targetRepoLabel ? { repoLabel: issue.targetRepoLabel } : {}),
269
+ stateRev: issue.stateRev,
270
+ repos,
271
+ ...(picked ? { selectedDir: picked } : {}),
272
+ invokerOpenId,
273
+ }),
274
+ };
275
+ }
276
+ if (action === ISSUE_ACTION_CLAIM_CONFIRM) {
277
+ const workingDir = value.dir;
278
+ if (!workingDir)
279
+ return toast('没有选择工作仓库');
280
+ // 重新拉一次而不是用卡片里的 stateRev:卡片可能已经放了很久,用陈旧的 CAS 基线
281
+ // 只会白白撞一次 409。拉不到就说明这条已经不可领了。
282
+ const issue = await findIssue(teamId, value.issueId, deps);
283
+ if (!issue)
284
+ return toast('这条任务已经不在待领取列表里了,刷新看看');
285
+ const r = await deps.runClaim({ issue, teamId, larkAppId, workingDir, invokerOpenId });
286
+ if (r.ok) {
287
+ logger.info(`[issue] 领取成功 issue=${issue._id} chat=${r.chatId}`);
288
+ return {
289
+ card: buildClaimResultCard({
290
+ ok: true,
291
+ title: issue.title,
292
+ chatId: r.chatId,
293
+ chatName: r.chatName,
294
+ ...(r.shareLink ? { shareLink: r.shareLink } : {}),
295
+ }),
296
+ };
297
+ }
298
+ logger.warn(`[issue] 领取失败 issue=${issue._id} stage=${r.stage} reason=${r.reason}`);
299
+ return {
300
+ card: buildClaimResultCard({
301
+ ok: false,
302
+ title: issue.title,
303
+ stage: r.stage,
304
+ reason: r.reason,
305
+ ...(claimFailureHint(r.stage) ? { hint: claimFailureHint(r.stage) } : {}),
306
+ }),
307
+ };
308
+ }
309
+ return toast(`未知操作:${action}`);
310
+ }
311
+ /** 从回调里取下拉选中值。Lark 在不同卡片版本下字段名不一致,两种都认。 */
312
+ function selectedOption(data) {
313
+ const opt = data.action?.option;
314
+ if (typeof opt === 'string' && opt)
315
+ return opt;
316
+ const opts = data.action?.options;
317
+ if (Array.isArray(opts) && typeof opts[0] === 'string' && opts[0])
318
+ return opts[0];
319
+ return undefined;
320
+ }
321
+ /** 按 issueId 在最新的分段列表里找。平台没有「按 id 单查」的机器入口,只能从列表里捞。 */
322
+ async function findIssue(teamId, issueId, deps) {
323
+ if (!teamId || !issueId)
324
+ return null;
325
+ const r = await deps.fetchIssues(teamId);
326
+ if (!r.ok || !r.value)
327
+ return null;
328
+ for (const list of Object.values(r.value)) {
329
+ const hit = list.find((i) => i._id === issueId);
330
+ if (hit)
331
+ return hit;
332
+ }
333
+ return null;
334
+ }
335
+ //# sourceMappingURL=issue-command.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"issue-command.js","sourceRoot":"","sources":["../../../src/im/lark/issue-command.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,201 @@
1
+ /**
2
+ * 平台 Issue Board 的 machine-auth 客户端(打 `/v1/machine/*`,Bearer = machineToken)。
3
+ *
4
+ * 平台侧入口见 platform 仓 `src/server/machine-issues-http.ts` / DESIGN-issue-board.md §三之二。
5
+ * 只有五个端点:teams / issues(list) / claim / bind / status。创建、编辑、force-detach、
6
+ * 归档是**人的决策**,只在平台 Web 上做,这里没有也不该有。
7
+ *
8
+ * 鉴权材料全部来自 `~/.botmux/platform.json`([[platform/binding]]):未绑定平台时所有调用
9
+ * 直接返回 `{ ok:false, reason:'unbound' }`,调用方据此把整个 issue board 功能关掉。
10
+ *
11
+ * 错误分型是这层的重点——上层 outbox pump 要据此决定「退避重试」还是「就此打住」:
12
+ * - `network` :连不上/超时 → **必须重试**(daemon 离线期间状态照样变,回来要补上)
13
+ * - `conflict` :平台返回 409(state_rev_conflict / stale_epoch / claim_expired…)
14
+ * → 重新投影后再发,不是简单重试
15
+ * - `forbidden`:401/403(token 失效、解绑、machine_mismatch)→ **停手**,重试无意义
16
+ * - `server` :5xx → 退避重试(含平台 ByteDoc 降级的 503)
17
+ */
18
+ import { getJson, postJson } from './platform-http.js';
19
+ import type { AttentionReason, IssueStatus } from '../services/issue-board-store.js';
20
+ export interface PlatformIssueClaim {
21
+ claimId: string;
22
+ claimEpoch: number;
23
+ unionId: string;
24
+ name: string;
25
+ machineId: string;
26
+ localTaskRef?: string;
27
+ /** 本地任务的可读名(本仓传 issue 群的群名)。平台详情页优先显示它而不是 localTaskRef。 */
28
+ localTaskLabel?: string;
29
+ /** 本地任务深链(本仓传飞书群 applink)。平台只接受 https,非法值会被静默丢弃。 */
30
+ localTaskUrl?: string;
31
+ agent?: string;
32
+ repoLabel?: string;
33
+ claimedAt: number;
34
+ leaseExpiresAt: number;
35
+ bound: boolean;
36
+ lastSourceSeq: number;
37
+ }
38
+ export interface PlatformIssue {
39
+ _id: string;
40
+ teamId: string;
41
+ title: string;
42
+ body: string;
43
+ status: IssueStatus;
44
+ labels?: string[];
45
+ targetRepoId?: string;
46
+ targetRepoLabel?: string;
47
+ suggestedAssigneeUnionId?: string;
48
+ claim?: PlatformIssueClaim;
49
+ attentionSince?: number;
50
+ attentionReason?: AttentionReason;
51
+ createdBy: string;
52
+ createdByName: string;
53
+ createdAt: number;
54
+ updatedAt: number;
55
+ rev: number;
56
+ stateRev: number;
57
+ claimEpochCounter: number;
58
+ }
59
+ export interface PlatformIssueSections {
60
+ needsAttention: PlatformIssue[];
61
+ todo: PlatformIssue[];
62
+ inProgress: PlatformIssue[];
63
+ inReview: PlatformIssue[];
64
+ done: PlatformIssue[];
65
+ }
66
+ export type IssueClientFailure = {
67
+ ok: false;
68
+ reason: 'unbound';
69
+ } | {
70
+ ok: false;
71
+ reason: 'network';
72
+ error: string;
73
+ } | {
74
+ ok: false;
75
+ reason: 'conflict';
76
+ status: number;
77
+ error: string;
78
+ } | {
79
+ ok: false;
80
+ reason: 'forbidden';
81
+ status: number;
82
+ error: string;
83
+ }
84
+ /** 其余 4xx(400 invalid / 404 not_found …):请求本身有问题,重发多少次都一样。 */
85
+ | {
86
+ ok: false;
87
+ reason: 'client';
88
+ status: number;
89
+ error: string;
90
+ } | {
91
+ ok: false;
92
+ reason: 'server';
93
+ status: number;
94
+ error: string;
95
+ };
96
+ export type IssueClientResult<T> = {
97
+ ok: true;
98
+ value: T;
99
+ } | IssueClientFailure;
100
+ /**
101
+ * 该失败是否值得退避后重投。
102
+ *
103
+ * 只有 `network` 与**真正的 5xx** 值得:4xx 全都是「再发一次结果不变」——401/403 是凭证/
104
+ * 归属问题,409 要先重新投影再发(不是盲重试),400/404 是请求本身错了。把它们混进可重试
105
+ * 一类,pump 会对着一个永远不会成功的请求指数退避到天荒地老,日志里还只看得到"在重试"。
106
+ */
107
+ export declare function isRetriable(f: IssueClientFailure): boolean;
108
+ /**
109
+ * 这次失败是否**永远**不会成功——发件箱据此把行标 fatal,彻底停止重投。
110
+ *
111
+ * 注意它**不是** `!isRetriable`,那两个的补集差着两类,混用会把好行判死:
112
+ * - `conflict`(409):是竞争不是错误。上层先重新对账再重发,对账本身可能因为网络问题
113
+ * 没做成,此时判死等于把一条完全正常的回写扔了。留给下一轮。
114
+ * - `unbound`:本机当前没绑平台。绑回来之后这条行照样该发,判死就再也发不出去了。
115
+ *
116
+ * 剩下的两类才是真死:`forbidden`(401/403,凭证失效 / machine_mismatch / claim 被 revoke)
117
+ * 与 `client`(400 参数错 / 404 issue 被删或归档)。对它们退避重投,就是每 5 分钟朝一个
118
+ * 永远不会变的死端点打一发,同时 `/issue status` 上那句「N 条回写没发出去」永不清零。
119
+ */
120
+ export declare function isPermanentFailure(f: IssueClientFailure): boolean;
121
+ export interface IssueClientOptions {
122
+ /** 覆盖平台地址与凭证(测试用;缺省读 ~/.botmux/platform.json)。 */
123
+ binding?: {
124
+ platformUrl: string;
125
+ machineToken: string;
126
+ machineId: string;
127
+ } | null;
128
+ timeoutMs?: number;
129
+ /** 注入 HTTP 实现(测试用)。 */
130
+ http?: {
131
+ get: typeof getJson;
132
+ post: typeof postJson;
133
+ };
134
+ }
135
+ /** 本机 owner 所属的平台团队。注意:日常判断「本机在不在团队里」应读本地
136
+ * `platform-team-sync.json`([[platform-team-store]]),这个接口是不依赖 team-sync
137
+ * 是否已收敛的权威兜底。 */
138
+ export declare function fetchTeams(opts?: IssueClientOptions): Promise<IssueClientResult<Array<{
139
+ teamId: string;
140
+ teamName: string;
141
+ }>>>;
142
+ export declare function fetchIssues(teamId: string, opts?: IssueClientOptions): Promise<IssueClientResult<PlatformIssueSections>>;
143
+ /**
144
+ * 按 id 单查一条 issue。
145
+ *
146
+ * 平台**没有**「按 id 单查」的机器接口,只能从分段列表里捞——这是唯一的实现方式,所以收口
147
+ * 在这里,别让每个调用方各抄一遍(回写的 409 对账、领取确认、交付都要用它)。
148
+ *
149
+ * 返回 null 有两种含义且**无法区分**:拉列表失败,或 issue 不在活跃分段里(已归档)。
150
+ * 调用方一律按「无法判定」处理,不要把 null 当成「平台上没有/不是我的」——那会让
151
+ * 409 对账做出相反的决定。
152
+ */
153
+ export declare function findIssueById(teamId: string, issueId: string, opts?: IssueClientOptions): Promise<PlatformIssue | null>;
154
+ /**
155
+ * 领取。`claimId` 必须是**高熵随机**(`crypto.randomBytes(16).toString('hex')`),
156
+ * 绝不能由 issueId 派生——平台的幂等分支现在会连 machineId/unionId 一起校验,派生键在
157
+ * 多机场景下会直接撞 409 `already_claimed`(早失败,好过静默把别人的 claim 当成自己的)。
158
+ *
159
+ * `machineId` 不用传:平台以 token 为准(传了且不一致会 403 machine_mismatch)。
160
+ */
161
+ export declare function claimIssue(issueId: string, args: {
162
+ claimId: string;
163
+ agent?: string;
164
+ repoLabel?: string;
165
+ expectedStateRev: number;
166
+ }, opts?: IssueClientOptions): Promise<IssueClientResult<{
167
+ claim: PlatformIssueClaim;
168
+ issue: PlatformIssue;
169
+ }>>;
170
+ /**
171
+ * 绑定本地任务。`localTaskRef` = `<anchorId>::<larkAppId>`(见 [[issue-board-store]] 的
172
+ * `buildLocalTaskRef`)。
173
+ *
174
+ * `localTaskLabel` / `localTaskUrl` 是**纯展示**的可选项:前者给人看(群名),后者是点进去
175
+ * 的深链。平台只接受 https 深链,其余静默丢弃且**不会**让 bind 失败——展示字段不该有能力
176
+ * 弄砸一次真实绑定。
177
+ */
178
+ export declare function bindIssue(issueId: string, args: {
179
+ claimId: string;
180
+ localTaskRef: string;
181
+ expectedStateRev: number;
182
+ localTaskLabel?: string;
183
+ localTaskUrl?: string;
184
+ }, opts?: IssueClientOptions): Promise<IssueClientResult<{
185
+ issue: PlatformIssue;
186
+ }>>;
187
+ /**
188
+ * 状态回写。幂等只认 `sourceSeq`(每 binding 单调、串行发送),所以同一条 outbox 行重发
189
+ * 是安全的:平台见到 `sourceSeq <= lastSourceSeq` 直接回当前态、零副作用。
190
+ */
191
+ export declare function writeIssueStatus(issueId: string, args: {
192
+ claimId: string;
193
+ claimEpoch: number;
194
+ sourceSeq: number;
195
+ status: IssueStatus;
196
+ attentionReason?: AttentionReason;
197
+ expectedStateRev: number;
198
+ }, opts?: IssueClientOptions): Promise<IssueClientResult<{
199
+ issue: PlatformIssue;
200
+ }>>;
201
+ //# sourceMappingURL=issue-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"issue-client.d.ts","sourceRoot":"","sources":["../../src/platform/issue-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEvD,OAAO,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAC;AAErF,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,2DAA2D;IAC3D,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,oDAAoD;IACpD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,OAAO,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,KAAK,CAAC,EAAE,kBAAkB,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,qBAAqB;IACpC,cAAc,EAAE,aAAa,EAAE,CAAC;IAChC,IAAI,EAAE,aAAa,EAAE,CAAC;IACtB,UAAU,EAAE,aAAa,EAAE,CAAC;IAC5B,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,IAAI,EAAE,aAAa,EAAE,CAAC;CACvB;AAED,MAAM,MAAM,kBAAkB,GAC1B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,SAAS,CAAA;CAAE,GAChC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC/C;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAChE;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,WAAW,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE;AACnE,8DAA8D;GAC5D;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC9D;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnE,MAAM,MAAM,iBAAiB,CAAC,CAAC,IAAI;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,GAAG,kBAAkB,CAAC;AAE/E;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAE1D;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAEjE;AAED,MAAM,WAAW,kBAAkB;IACjC,kDAAkD;IAClD,OAAO,CAAC,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAClF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uBAAuB;IACvB,IAAI,CAAC,EAAE;QACL,GAAG,EAAE,OAAO,OAAO,CAAC;QACpB,IAAI,EAAE,OAAO,QAAQ,CAAC;KACvB,CAAC;CACH;AA2CD;;kBAEkB;AAClB,wBAAgB,UAAU,CACxB,IAAI,GAAE,kBAAuB,GAC5B,OAAO,CAAC,iBAAiB,CAAC,KAAK,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC,CAAC,CAEzE;AAED,wBAAgB,WAAW,CACzB,MAAM,EAAE,MAAM,EACd,IAAI,GAAE,kBAAuB,GAC5B,OAAO,CAAC,iBAAiB,CAAC,qBAAqB,CAAC,CAAC,CAQnD;AAED;;;;;;;;;GASG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,kBAAuB,GAC5B,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAQ/B;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CACxB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,gBAAgB,EAAE,MAAM,CAAA;CAAE,EACvF,IAAI,GAAE,kBAAuB,GAC5B,OAAO,CAAC,iBAAiB,CAAC;IAAE,KAAK,EAAE,kBAAkB,CAAC;IAAC,KAAK,EAAE,aAAa,CAAA;CAAE,CAAC,CAAC,CAEjF;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CACvB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE;IACJ,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,EACD,IAAI,GAAE,kBAAuB,GAC5B,OAAO,CAAC,iBAAiB,CAAC;IAAE,KAAK,EAAE,aAAa,CAAA;CAAE,CAAC,CAAC,CAEtD;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE;IACJ,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,WAAW,CAAC;IACpB,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,gBAAgB,EAAE,MAAM,CAAC;CAC1B,EACD,IAAI,GAAE,kBAAuB,GAC5B,OAAO,CAAC,iBAAiB,CAAC;IAAE,KAAK,EAAE,aAAa,CAAA;CAAE,CAAC,CAAC,CAEtD"}