botmux 2.93.0 → 2.94.0

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 (227) hide show
  1. package/dist/adapters/cli/claude-code.d.ts.map +1 -1
  2. package/dist/adapters/cli/claude-code.js +6 -1
  3. package/dist/adapters/cli/claude-code.js.map +1 -1
  4. package/dist/bot-registry.d.ts +10 -1
  5. package/dist/bot-registry.d.ts.map +1 -1
  6. package/dist/bot-registry.js +2 -0
  7. package/dist/bot-registry.js.map +1 -1
  8. package/dist/cli/dashboard-endpoint.d.ts +1 -1
  9. package/dist/cli/dashboard-endpoint.d.ts.map +1 -1
  10. package/dist/cli/dashboard-endpoint.js +3 -0
  11. package/dist/cli/dashboard-endpoint.js.map +1 -1
  12. package/dist/cli/workflow.d.ts.map +1 -1
  13. package/dist/cli/workflow.js +11 -4
  14. package/dist/cli/workflow.js.map +1 -1
  15. package/dist/cli.d.ts.map +1 -1
  16. package/dist/cli.js +133 -1
  17. package/dist/cli.js.map +1 -1
  18. package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
  19. package/dist/core/dashboard-ipc-server.js +3 -0
  20. package/dist/core/dashboard-ipc-server.js.map +1 -1
  21. package/dist/core/inherit-peer.d.ts +5 -0
  22. package/dist/core/inherit-peer.d.ts.map +1 -1
  23. package/dist/core/inherit-peer.js +3 -0
  24. package/dist/core/inherit-peer.js.map +1 -1
  25. package/dist/core/trigger-session.d.ts +10 -0
  26. package/dist/core/trigger-session.d.ts.map +1 -1
  27. package/dist/core/trigger-session.js +22 -2
  28. package/dist/core/trigger-session.js.map +1 -1
  29. package/dist/core/worker-pool.d.ts +6 -0
  30. package/dist/core/worker-pool.d.ts.map +1 -1
  31. package/dist/core/worker-pool.js +49 -6
  32. package/dist/core/worker-pool.js.map +1 -1
  33. package/dist/daemon.d.ts.map +1 -1
  34. package/dist/daemon.js +297 -13
  35. package/dist/daemon.js.map +1 -1
  36. package/dist/dashboard/auth.d.ts.map +1 -1
  37. package/dist/dashboard/auth.js +7 -1
  38. package/dist/dashboard/auth.js.map +1 -1
  39. package/dist/dashboard/bot-payload.d.ts +1 -0
  40. package/dist/dashboard/bot-payload.d.ts.map +1 -1
  41. package/dist/dashboard/bot-payload.js +1 -0
  42. package/dist/dashboard/bot-payload.js.map +1 -1
  43. package/dist/dashboard/federation-spoke-api.d.ts +31 -0
  44. package/dist/dashboard/federation-spoke-api.d.ts.map +1 -1
  45. package/dist/dashboard/federation-spoke-api.js +36 -14
  46. package/dist/dashboard/federation-spoke-api.js.map +1 -1
  47. package/dist/dashboard/v3-runs-api.d.ts +26 -0
  48. package/dist/dashboard/v3-runs-api.d.ts.map +1 -0
  49. package/dist/dashboard/v3-runs-api.js +67 -0
  50. package/dist/dashboard/v3-runs-api.js.map +1 -0
  51. package/dist/dashboard/web/app.js +21 -9
  52. package/dist/dashboard/web/app.js.map +1 -1
  53. package/dist/dashboard/web/bot-defaults.d.ts.map +1 -1
  54. package/dist/dashboard/web/bot-defaults.js +24 -1
  55. package/dist/dashboard/web/bot-defaults.js.map +1 -1
  56. package/dist/dashboard/web/i18n.d.ts.map +1 -1
  57. package/dist/dashboard/web/i18n.js +12 -4
  58. package/dist/dashboard/web/i18n.js.map +1 -1
  59. package/dist/dashboard/web/legacy-workflow-link.d.ts +2 -0
  60. package/dist/dashboard/web/legacy-workflow-link.d.ts.map +1 -0
  61. package/dist/dashboard/web/legacy-workflow-link.js +12 -0
  62. package/dist/dashboard/web/legacy-workflow-link.js.map +1 -0
  63. package/dist/dashboard/web/v3-terminal.d.ts +11 -0
  64. package/dist/dashboard/web/v3-terminal.d.ts.map +1 -0
  65. package/dist/dashboard/web/v3-terminal.js +143 -0
  66. package/dist/dashboard/web/v3-terminal.js.map +1 -0
  67. package/dist/dashboard/web/v3.d.ts +2 -0
  68. package/dist/dashboard/web/v3.d.ts.map +1 -0
  69. package/dist/dashboard/web/v3.js +564 -0
  70. package/dist/dashboard/web/v3.js.map +1 -0
  71. package/dist/dashboard/web/workflows.d.ts.map +1 -1
  72. package/dist/dashboard/web/workflows.js +12 -8
  73. package/dist/dashboard/web/workflows.js.map +1 -1
  74. package/dist/dashboard-web/app.js +787 -699
  75. package/dist/dashboard-web/index.html +1 -1
  76. package/dist/dashboard-web/style.css +223 -0
  77. package/dist/dashboard.js +61 -40
  78. package/dist/dashboard.js.map +1 -1
  79. package/dist/i18n/en.d.ts.map +1 -1
  80. package/dist/i18n/en.js +6 -3
  81. package/dist/i18n/en.js.map +1 -1
  82. package/dist/i18n/zh.d.ts.map +1 -1
  83. package/dist/i18n/zh.js +6 -3
  84. package/dist/i18n/zh.js.map +1 -1
  85. package/dist/im/lark/card-handler.d.ts +12 -0
  86. package/dist/im/lark/card-handler.d.ts.map +1 -1
  87. package/dist/im/lark/card-handler.js +52 -2
  88. package/dist/im/lark/card-handler.js.map +1 -1
  89. package/dist/im/lark/doc-comment.d.ts +20 -0
  90. package/dist/im/lark/doc-comment.d.ts.map +1 -1
  91. package/dist/im/lark/doc-comment.js +23 -0
  92. package/dist/im/lark/doc-comment.js.map +1 -1
  93. package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
  94. package/dist/im/lark/event-dispatcher.js +13 -9
  95. package/dist/im/lark/event-dispatcher.js.map +1 -1
  96. package/dist/im/lark/v3-blocked-card-handler.d.ts +28 -0
  97. package/dist/im/lark/v3-blocked-card-handler.d.ts.map +1 -0
  98. package/dist/im/lark/v3-blocked-card-handler.js +134 -0
  99. package/dist/im/lark/v3-blocked-card-handler.js.map +1 -0
  100. package/dist/im/lark/v3-blocked-card.d.ts +75 -0
  101. package/dist/im/lark/v3-blocked-card.d.ts.map +1 -0
  102. package/dist/im/lark/v3-blocked-card.js +206 -0
  103. package/dist/im/lark/v3-blocked-card.js.map +1 -0
  104. package/dist/im/lark/v3-gate-card-handler.d.ts +31 -0
  105. package/dist/im/lark/v3-gate-card-handler.d.ts.map +1 -0
  106. package/dist/im/lark/v3-gate-card-handler.js +110 -0
  107. package/dist/im/lark/v3-gate-card-handler.js.map +1 -0
  108. package/dist/im/lark/v3-gate-card.d.ts +47 -0
  109. package/dist/im/lark/v3-gate-card.d.ts.map +1 -0
  110. package/dist/im/lark/v3-gate-card.js +119 -0
  111. package/dist/im/lark/v3-gate-card.js.map +1 -0
  112. package/dist/im/lark/v3-loop-grant-card-handler.d.ts +28 -0
  113. package/dist/im/lark/v3-loop-grant-card-handler.d.ts.map +1 -0
  114. package/dist/im/lark/v3-loop-grant-card-handler.js +90 -0
  115. package/dist/im/lark/v3-loop-grant-card-handler.js.map +1 -0
  116. package/dist/im/lark/v3-loop-grant-card.d.ts +46 -0
  117. package/dist/im/lark/v3-loop-grant-card.d.ts.map +1 -0
  118. package/dist/im/lark/v3-loop-grant-card.js +119 -0
  119. package/dist/im/lark/v3-loop-grant-card.js.map +1 -0
  120. package/dist/im/lark/v3-revisit-grant-card-handler.d.ts +29 -0
  121. package/dist/im/lark/v3-revisit-grant-card-handler.d.ts.map +1 -0
  122. package/dist/im/lark/v3-revisit-grant-card-handler.js +96 -0
  123. package/dist/im/lark/v3-revisit-grant-card-handler.js.map +1 -0
  124. package/dist/im/lark/v3-revisit-grant-card.d.ts +52 -0
  125. package/dist/im/lark/v3-revisit-grant-card.d.ts.map +1 -0
  126. package/dist/im/lark/v3-revisit-grant-card.js +123 -0
  127. package/dist/im/lark/v3-revisit-grant-card.js.map +1 -0
  128. package/dist/im/lark/workflow-cards.d.ts.map +1 -1
  129. package/dist/im/lark/workflow-cards.js +5 -1
  130. package/dist/im/lark/workflow-cards.js.map +1 -1
  131. package/dist/im/lark/workflow-progress-card.d.ts +1 -1
  132. package/dist/im/lark/workflow-progress-card.js +1 -1
  133. package/dist/im/lark/workflow-slash-command.d.ts +28 -0
  134. package/dist/im/lark/workflow-slash-command.d.ts.map +1 -1
  135. package/dist/im/lark/workflow-slash-command.js +61 -5
  136. package/dist/im/lark/workflow-slash-command.js.map +1 -1
  137. package/dist/platform/bind.d.ts.map +1 -1
  138. package/dist/platform/bind.js +17 -3
  139. package/dist/platform/bind.js.map +1 -1
  140. package/dist/services/card-prefs-store.d.ts +4 -0
  141. package/dist/services/card-prefs-store.d.ts.map +1 -1
  142. package/dist/services/card-prefs-store.js +19 -0
  143. package/dist/services/card-prefs-store.js.map +1 -1
  144. package/dist/services/group-creator.d.ts.map +1 -1
  145. package/dist/services/group-creator.js +18 -2
  146. package/dist/services/group-creator.js.map +1 -1
  147. package/dist/skills/definitions.d.ts.map +1 -1
  148. package/dist/skills/definitions.js +208 -4
  149. package/dist/skills/definitions.js.map +1 -1
  150. package/dist/worker.js +79 -13
  151. package/dist/worker.js.map +1 -1
  152. package/dist/workflows/definition.d.ts +80 -80
  153. package/dist/workflows/events/payloads.d.ts +4 -4
  154. package/dist/workflows/events/schema.d.ts +16 -16
  155. package/dist/workflows/v3/architect.d.ts +37 -0
  156. package/dist/workflows/v3/architect.d.ts.map +1 -0
  157. package/dist/workflows/v3/architect.js +226 -0
  158. package/dist/workflows/v3/architect.js.map +1 -0
  159. package/dist/workflows/v3/bot-resolve.d.ts +21 -0
  160. package/dist/workflows/v3/bot-resolve.d.ts.map +1 -0
  161. package/dist/workflows/v3/bot-resolve.js +47 -0
  162. package/dist/workflows/v3/bot-resolve.js.map +1 -0
  163. package/dist/workflows/v3/cli-run.d.ts +24 -0
  164. package/dist/workflows/v3/cli-run.d.ts.map +1 -0
  165. package/dist/workflows/v3/cli-run.js +265 -0
  166. package/dist/workflows/v3/cli-run.js.map +1 -0
  167. package/dist/workflows/v3/contract.d.ts +302 -0
  168. package/dist/workflows/v3/contract.d.ts.map +1 -0
  169. package/dist/workflows/v3/contract.js +91 -0
  170. package/dist/workflows/v3/contract.js.map +1 -0
  171. package/dist/workflows/v3/daemon-run.d.ts +311 -0
  172. package/dist/workflows/v3/daemon-run.d.ts.map +1 -0
  173. package/dist/workflows/v3/daemon-run.js +775 -0
  174. package/dist/workflows/v3/daemon-run.js.map +1 -0
  175. package/dist/workflows/v3/dag.d.ts +332 -0
  176. package/dist/workflows/v3/dag.d.ts.map +1 -0
  177. package/dist/workflows/v3/dag.js +1040 -0
  178. package/dist/workflows/v3/dag.js.map +1 -0
  179. package/dist/workflows/v3/ephemeral-pool.d.ts +29 -0
  180. package/dist/workflows/v3/ephemeral-pool.d.ts.map +1 -0
  181. package/dist/workflows/v3/ephemeral-pool.js +331 -0
  182. package/dist/workflows/v3/ephemeral-pool.js.map +1 -0
  183. package/dist/workflows/v3/grill-state.d.ts +92 -0
  184. package/dist/workflows/v3/grill-state.d.ts.map +1 -0
  185. package/dist/workflows/v3/grill-state.js +151 -0
  186. package/dist/workflows/v3/grill-state.js.map +1 -0
  187. package/dist/workflows/v3/host.d.ts +88 -0
  188. package/dist/workflows/v3/host.d.ts.map +1 -0
  189. package/dist/workflows/v3/host.js +349 -0
  190. package/dist/workflows/v3/host.js.map +1 -0
  191. package/dist/workflows/v3/human-gate.d.ts +85 -0
  192. package/dist/workflows/v3/human-gate.d.ts.map +1 -0
  193. package/dist/workflows/v3/human-gate.js +153 -0
  194. package/dist/workflows/v3/human-gate.js.map +1 -0
  195. package/dist/workflows/v3/journal.d.ts +207 -0
  196. package/dist/workflows/v3/journal.d.ts.map +1 -0
  197. package/dist/workflows/v3/journal.js +74 -0
  198. package/dist/workflows/v3/journal.js.map +1 -0
  199. package/dist/workflows/v3/manifest.d.ts +17 -0
  200. package/dist/workflows/v3/manifest.d.ts.map +1 -0
  201. package/dist/workflows/v3/manifest.js +266 -0
  202. package/dist/workflows/v3/manifest.js.map +1 -0
  203. package/dist/workflows/v3/ops-projection.d.ts +110 -0
  204. package/dist/workflows/v3/ops-projection.d.ts.map +1 -0
  205. package/dist/workflows/v3/ops-projection.js +0 -0
  206. package/dist/workflows/v3/ops-projection.js.map +1 -0
  207. package/dist/workflows/v3/orchestrator.d.ts +168 -0
  208. package/dist/workflows/v3/orchestrator.d.ts.map +1 -0
  209. package/dist/workflows/v3/orchestrator.js +417 -0
  210. package/dist/workflows/v3/orchestrator.js.map +1 -0
  211. package/dist/workflows/v3/runtime.d.ts +193 -0
  212. package/dist/workflows/v3/runtime.d.ts.map +1 -0
  213. package/dist/workflows/v3/runtime.js +1284 -0
  214. package/dist/workflows/v3/runtime.js.map +1 -0
  215. package/dist/workflows/v3/spec.d.ts +32 -0
  216. package/dist/workflows/v3/spec.d.ts.map +1 -0
  217. package/dist/workflows/v3/spec.js +173 -0
  218. package/dist/workflows/v3/spec.js.map +1 -0
  219. package/dist/workflows/v3/state.d.ts +74 -0
  220. package/dist/workflows/v3/state.d.ts.map +1 -0
  221. package/dist/workflows/v3/state.js +353 -0
  222. package/dist/workflows/v3/state.js.map +1 -0
  223. package/package.json +1 -1
  224. package/dist/dashboard/web/workflow-catalog.d.ts +0 -2
  225. package/dist/dashboard/web/workflow-catalog.d.ts.map +0 -1
  226. package/dist/dashboard/web/workflow-catalog.js +0 -323
  227. package/dist/dashboard/web/workflow-catalog.js.map +0 -1
@@ -0,0 +1,206 @@
1
+ /**
2
+ * v3 blocked-node 重试卡 —— blocked/failed 两档里 blocked(契约性失败,可恢复)
3
+ * 的飞书入口。daemon 驱动的 run 走到 runBlocked 时发这张卡;点「重试」=
4
+ * `requestV3Retry`(append nodeRetryRequested)+ 重驱动,幂等沿 humanGate wait
5
+ * 那套思路(codex 拍的第一版边界:卡只带 runId/nodeId/attemptId/errorCode/
6
+ * message + Retry,不塞 live 终端 / 复杂诊断)。
7
+ *
8
+ * 自带 action namespace(`v3_blocked_retry`),不与 gate 卡混用。纯函数,单测友好。
9
+ */
10
+ import { config } from '../../config.js';
11
+ export const V3_BLOCKED_RETRY_ACTION = 'v3_blocked_retry';
12
+ /** 运行时 human-ask 选项按钮的 action(与「重试」同卡不同 namespace)。 */
13
+ export const V3_BLOCKED_ASK_ANSWER_ACTION = 'v3_blocked_ask_answer';
14
+ export const V3_BLOCKED_ASK_TEXT_FIELD = 'v3_blocked_ask_text';
15
+ const DEFAULT_MESSAGE_MAX_CHARS = 500;
16
+ /** 稳定 nonce:同一 run 同一节点同一 attempt 的卡 nonce 固定(重发卡一致);
17
+ * attempt 入 nonce —— 重试后旧卡的 nonce 对不上新 attempt,不会误触。 */
18
+ export function v3BlockedCardNonce(runId, nodeId, attemptId) {
19
+ return `v3blocked:${runId}:${nodeId}:${attemptId}`;
20
+ }
21
+ function v3RunDetailUrl(runId) {
22
+ return `http://${config.dashboard.externalHost}:${config.dashboard.port}/#/v3/${encodeURIComponent(runId)}`;
23
+ }
24
+ export function buildV3BlockedCard(input) {
25
+ const nonce = input.nonce ?? v3BlockedCardNonce(input.runId, input.nodeId, input.attemptId);
26
+ const webDetailUrl = input.webDetailUrl ?? v3RunDetailUrl(input.runId);
27
+ const msgMax = input.messageMaxChars ?? DEFAULT_MESSAGE_MAX_CHARS;
28
+ const { retried, ask, answered } = input;
29
+ // 四态共用一张卡,header 决定语气:蓝=等人拍板 / 绿=已回答·已重试 / 橙=受阻待重试。
30
+ let title;
31
+ let template;
32
+ if (answered) {
33
+ title = `已回答:节点 ${input.nodeId}`;
34
+ template = 'green';
35
+ }
36
+ else if (ask) {
37
+ title = `需要你拍板:节点 ${input.nodeId}`;
38
+ template = 'blue';
39
+ }
40
+ else if (retried) {
41
+ title = `已重试:节点 ${input.nodeId}`;
42
+ template = 'green';
43
+ }
44
+ else {
45
+ title = `节点受阻:${input.nodeId}`;
46
+ template = 'orange';
47
+ }
48
+ const attemptNNN = input.attemptId.slice(input.attemptId.lastIndexOf('/') + 1);
49
+ const elements = [
50
+ {
51
+ tag: 'div',
52
+ fields: [
53
+ { is_short: true, text: { tag: 'lark_md', content: `**Run**\n${escapeMd(short(input.runId, 24))}` } },
54
+ { is_short: true, text: { tag: 'lark_md', content: `**节点 / attempt**\n${escapeMd(input.nodeId)} · ${escapeMd(attemptNNN)}` } },
55
+ ],
56
+ },
57
+ { tag: 'hr' },
58
+ ];
59
+ if (ask || answered) {
60
+ // ── 运行时 human-ask:渲染问题 + 选项按钮(或冻结的「已回答」)──
61
+ const question = ask?.question;
62
+ if (question) {
63
+ elements.push({
64
+ tag: 'div',
65
+ text: { tag: 'lark_md', content: `**${escapeMd(truncate(question, msgMax))}**` },
66
+ });
67
+ }
68
+ if (answered) {
69
+ const answerPreview = answered.text ?? answered.selected ?? '';
70
+ elements.push({ tag: 'hr' });
71
+ elements.push({
72
+ tag: 'div',
73
+ text: {
74
+ tag: 'lark_md',
75
+ content: `✅ 已回答 → **${escapeMd(truncate(answerPreview, msgMax))}**` +
76
+ (answered.by ? ` · by ${escapeMd(short(answered.by, 20))}` : '') +
77
+ ` · 重跑 ${escapeMd(answered.nextAttemptId.slice(answered.nextAttemptId.lastIndexOf('/') + 1))}`,
78
+ },
79
+ });
80
+ }
81
+ else if (ask?.freeText) {
82
+ elements.push({
83
+ tag: 'form',
84
+ name: 'v3_blocked_ask_text_form',
85
+ elements: [
86
+ {
87
+ tag: 'input',
88
+ name: V3_BLOCKED_ASK_TEXT_FIELD,
89
+ placeholder: { tag: 'plain_text', content: '填写答案' },
90
+ },
91
+ {
92
+ tag: 'button',
93
+ text: { tag: 'plain_text', content: '提交并重跑' },
94
+ type: 'primary',
95
+ name: 'v3_blocked_ask_text_submit',
96
+ action_type: 'form_submit',
97
+ value: {
98
+ action: V3_BLOCKED_ASK_ANSWER_ACTION,
99
+ runId: input.runId,
100
+ nodeId: input.nodeId,
101
+ attemptId: input.attemptId,
102
+ answerKind: 'text',
103
+ nonce,
104
+ },
105
+ },
106
+ ],
107
+ });
108
+ }
109
+ else if (ask) {
110
+ // 一个选项一个按钮,选中文本回传为 answer.selected(按 attempt 入 nonce 防 stale)。
111
+ elements.push({
112
+ tag: 'action',
113
+ actions: (ask.options ?? []).map((opt) => ({
114
+ tag: 'button',
115
+ text: { tag: 'plain_text', content: short(opt, 80) },
116
+ type: 'primary',
117
+ value: {
118
+ action: V3_BLOCKED_ASK_ANSWER_ACTION,
119
+ runId: input.runId,
120
+ nodeId: input.nodeId,
121
+ attemptId: input.attemptId,
122
+ selected: opt,
123
+ nonce,
124
+ },
125
+ })),
126
+ });
127
+ }
128
+ }
129
+ else {
130
+ // ── 普通受阻 / 已重试 ──
131
+ elements.push({
132
+ tag: 'div',
133
+ text: {
134
+ tag: 'lark_md',
135
+ content: `**原因**\n${escapeMd(input.errorClass ?? 'blocked')}` +
136
+ (input.errorCode ? ` · \`${escapeMd(input.errorCode)}\`` : '') +
137
+ (input.message ? `\n${escapeMd(truncate(input.message, msgMax))}` : ''),
138
+ },
139
+ });
140
+ if (retried) {
141
+ elements.push({ tag: 'hr' });
142
+ elements.push({
143
+ tag: 'div',
144
+ text: {
145
+ tag: 'lark_md',
146
+ content: `🔄 已重试 → ${escapeMd(retried.nextAttemptId.slice(retried.nextAttemptId.lastIndexOf('/') + 1))}` +
147
+ (retried.by ? ` · by ${escapeMd(short(retried.by, 20))}` : ''),
148
+ },
149
+ });
150
+ }
151
+ else {
152
+ elements.push({
153
+ tag: 'div',
154
+ text: { tag: 'lark_md', content: '处理掉阻塞原因(如完成鉴权)后点重试,会以新 attempt 重跑该节点。' },
155
+ });
156
+ elements.push({
157
+ tag: 'action',
158
+ actions: [
159
+ {
160
+ tag: 'button',
161
+ text: { tag: 'plain_text', content: '🔄 重试' },
162
+ type: 'primary',
163
+ value: {
164
+ action: V3_BLOCKED_RETRY_ACTION,
165
+ runId: input.runId,
166
+ nodeId: input.nodeId,
167
+ attemptId: input.attemptId,
168
+ nonce,
169
+ },
170
+ },
171
+ ],
172
+ });
173
+ }
174
+ }
175
+ elements.push({
176
+ tag: 'action',
177
+ actions: [
178
+ {
179
+ tag: 'button',
180
+ text: { tag: 'plain_text', content: 'Web 详情' },
181
+ type: 'default',
182
+ multi_url: {
183
+ url: webDetailUrl, pc_url: webDetailUrl, android_url: webDetailUrl, ios_url: webDetailUrl,
184
+ },
185
+ },
186
+ ],
187
+ });
188
+ return JSON.stringify({
189
+ config: { wide_screen_mode: true },
190
+ header: { template, title: { tag: 'plain_text', content: title } },
191
+ elements,
192
+ });
193
+ }
194
+ function truncate(s, max) {
195
+ if (s.length <= max)
196
+ return s;
197
+ return `${s.slice(0, max)}…(截断,完整见 Web 详情)`;
198
+ }
199
+ function short(s, max) {
200
+ return s.length <= max ? s : `${s.slice(0, max)}…`;
201
+ }
202
+ /** 转义 lark_md 里会被解析的字符,防 message 注入破坏卡片结构。 */
203
+ function escapeMd(s) {
204
+ return s.replace(/[\\*_~`\[\]]/g, (c) => `\\${c}`);
205
+ }
206
+ //# sourceMappingURL=v3-blocked-card.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"v3-blocked-card.js","sourceRoot":"","sources":["../../../src/im/lark/v3-blocked-card.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * v3 humanGate 审批卡点击处理 —— card-handler 的 v3 分支(对称于 v0.2 的
3
+ * `workflow-card-handler`,但走 v3 自己的 wait/journal 权威,不复用 v0.2 wait
4
+ * path)。把一次点击翻译成:权限校验 → `resolveV3GateClick`(幂等 + terminal-safe)
5
+ * → 冻结卡 / toast + 触发 `driveV3Run` 续跑。
6
+ *
7
+ * 纯逻辑 + 注入 seam(resolveClick / driveRun / canResolve),单测友好;真正的
8
+ * Lark 发送 / driveV3Run 由 daemon 在 deps 里接。
9
+ */
10
+ import { type V3GateActionValue } from './v3-gate-card.js';
11
+ import { resolveV3GateClick } from '../../workflows/v3/daemon-run.js';
12
+ import { type RunChatBinding } from '../../workflows/v3/grill-state.js';
13
+ export declare function isV3GateAction(action: unknown): boolean;
14
+ export interface V3GateCardHandlerDeps {
15
+ baseDir?: string;
16
+ /** Re-enter the run after a resolved gate. Fire-and-forget; the daemon logs
17
+ * errors (this is the `driveV3Run` wiring with postGateCard/onTerminal). */
18
+ driveRun: (runId: string) => void;
19
+ /** Permission: may this operator resolve gates for this run? Default: allow
20
+ * (MVP — daemon injects a canOperate-backed check). */
21
+ canResolve?: (binding: RunChatBinding | undefined, operatorOpenId: string | undefined) => boolean;
22
+ /** Injectable for tests. Default = real resolveV3GateClick. */
23
+ resolveClick?: typeof resolveV3GateClick;
24
+ }
25
+ /**
26
+ * Handle a v3 gate card click. Returns the Lark card-action response: a frozen
27
+ * card object (replaces the clicked card so its buttons can't re-submit), or a
28
+ * `{ toast }` wrapper. Triggers `driveRun` as a side effect on a real resolve.
29
+ */
30
+ export declare function handleV3GateAction(value: V3GateActionValue, operatorOpenId: string | undefined, deps: V3GateCardHandlerDeps): Promise<unknown>;
31
+ //# sourceMappingURL=v3-gate-card-handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"v3-gate-card-handler.d.ts","sourceRoot":"","sources":["../../../src/im/lark/v3-gate-card-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAKL,KAAK,iBAAiB,EACvB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AACtE,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,mCAAmC,CAAC;AAI3C,wBAAgB,cAAc,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAEvD;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;iFAC6E;IAC7E,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC;4DACwD;IACxD,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,GAAG,SAAS,EAAE,cAAc,EAAE,MAAM,GAAG,SAAS,KAAK,OAAO,CAAC;IAClG,+DAA+D;IAC/D,YAAY,CAAC,EAAE,OAAO,kBAAkB,CAAC;CAC1C;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,iBAAiB,EACxB,cAAc,EAAE,MAAM,GAAG,SAAS,EAClC,IAAI,EAAE,qBAAqB,GAC1B,OAAO,CAAC,OAAO,CAAC,CAwFlB"}
@@ -0,0 +1,110 @@
1
+ /**
2
+ * v3 humanGate 审批卡点击处理 —— card-handler 的 v3 分支(对称于 v0.2 的
3
+ * `workflow-card-handler`,但走 v3 自己的 wait/journal 权威,不复用 v0.2 wait
4
+ * path)。把一次点击翻译成:权限校验 → `resolveV3GateClick`(幂等 + terminal-safe)
5
+ * → 冻结卡 / toast + 触发 `driveV3Run` 续跑。
6
+ *
7
+ * 纯逻辑 + 注入 seam(resolveClick / driveRun / canResolve),单测友好;真正的
8
+ * Lark 发送 / driveV3Run 由 daemon 在 deps 里接。
9
+ */
10
+ import { join } from 'node:path';
11
+ import { V3_GATE_APPROVE_ACTION, V3_GATE_REJECT_ACTION, buildV3GateCard, v3GateCardNonce, } from './v3-gate-card.js';
12
+ import { resolveV3GateClick } from '../../workflows/v3/daemon-run.js';
13
+ import { readGrillState, defaultBaseDir, } from '../../workflows/v3/grill-state.js';
14
+ import { readWait } from '../../workflows/v3/human-gate.js';
15
+ import { isValidRunId, isValidWaitId } from '../../workflows/v3/ops-projection.js';
16
+ export function isV3GateAction(action) {
17
+ return action === V3_GATE_APPROVE_ACTION || action === V3_GATE_REJECT_ACTION;
18
+ }
19
+ /**
20
+ * Handle a v3 gate card click. Returns the Lark card-action response: a frozen
21
+ * card object (replaces the clicked card so its buttons can't re-submit), or a
22
+ * `{ toast }` wrapper. Triggers `driveRun` as a side effect on a real resolve.
23
+ */
24
+ export async function handleV3GateAction(value, operatorOpenId, deps) {
25
+ const baseDir = deps.baseDir ?? defaultBaseDir();
26
+ // Guard BOTH externally-supplied ids before any path join: waitId flows into
27
+ // `runDir/waits/<waitId>.json` (resolveV3GateClick → human-gate), and the
28
+ // reproducible non-secret nonce can't stop a `../..` waitId on its own.
29
+ if (!isValidRunId(value.runId)) {
30
+ return { toast: { type: 'warning', content: 'gate 已失效(非法 run)' } };
31
+ }
32
+ if (typeof value.waitId !== 'string' || !isValidWaitId(value.waitId)) {
33
+ return { toast: { type: 'warning', content: 'gate 已失效(非法 wait)' } };
34
+ }
35
+ // Nonce check (codex medium): the card carries a stable nonce; a value whose
36
+ // nonce doesn't match the run/wait pair is a tampered/foreign card → stale.
37
+ if (value.nonce !== v3GateCardNonce(value.runId, value.waitId)) {
38
+ return { toast: { type: 'warning', content: 'gate 卡已失效(nonce 不匹配)' } };
39
+ }
40
+ const runDir = join(baseDir, value.runId);
41
+ const grill = readGrillState(runDir);
42
+ const binding = grill?.chatBinding;
43
+ if (deps.canResolve && !deps.canResolve(binding, operatorOpenId)) {
44
+ return { toast: { type: 'warning', content: '你没有权限审批这个 gate' } };
45
+ }
46
+ const selected = value.selected ?? (value.action === V3_GATE_APPROVE_ACTION ? 'approve' : 'reject');
47
+ const resolveClick = deps.resolveClick ?? resolveV3GateClick;
48
+ let outcome;
49
+ try {
50
+ outcome = resolveClick(baseDir, value.runId, {
51
+ waitId: value.waitId,
52
+ selected,
53
+ by: operatorOpenId ?? 'unknown',
54
+ });
55
+ }
56
+ catch (err) {
57
+ // journal append failed after resolveWait (codex #5): warn + don't fake
58
+ // success; the card stays clickable, cold-attach reconcile heals on restart.
59
+ return {
60
+ toast: {
61
+ type: 'error',
62
+ content: `处理失败,请重试:${err instanceof Error ? err.message : String(err)}`,
63
+ },
64
+ };
65
+ }
66
+ if (outcome.kind === 'stale-run') {
67
+ return {
68
+ toast: {
69
+ type: 'warning',
70
+ content: outcome.reason === 'terminal' ? '该 run 已结束,gate 失效' : 'gate 已失效',
71
+ },
72
+ };
73
+ }
74
+ if (outcome.kind === 'already-settled') {
75
+ // Idempotent recovery (codex nit #14): if a prior click already APPROVED the
76
+ // gate but its driveRun didn't land (transient), a re-click should re-drive
77
+ // instead of dead-ending at an info toast — mirrors the blocked/loop grant
78
+ // handlers' already-granted branch. driveRun is terminal-safe (short-circuits
79
+ // a finished run), so this is harmless if the run already moved on. A rejected
80
+ // gate is terminal-ish, so don't re-drive it.
81
+ if (outcome.status === 'approved')
82
+ deps.driveRun(value.runId);
83
+ return {
84
+ toast: {
85
+ type: 'info',
86
+ content: `已是「${outcome.status === 'approved' ? '通过' : '拒绝'}」状态`,
87
+ },
88
+ };
89
+ }
90
+ if (outcome.kind === 'unauthorized') {
91
+ return {
92
+ toast: {
93
+ type: 'warning',
94
+ content: '你不在这个 gate 的审批人名单中',
95
+ },
96
+ };
97
+ }
98
+ // resolved → drive the run forward (fresh replay) + freeze this card.
99
+ deps.driveRun(value.runId);
100
+ const prompt = readWait(runDir, value.waitId)?.prompt ?? '';
101
+ const frozen = buildV3GateCard({
102
+ runId: value.runId,
103
+ waitId: value.waitId,
104
+ nodeId: value.nodeId,
105
+ prompt,
106
+ resolution: { kind: outcome.resolution, by: operatorOpenId, selected },
107
+ });
108
+ return JSON.parse(frozen);
109
+ }
110
+ //# sourceMappingURL=v3-gate-card-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"v3-gate-card-handler.js","sourceRoot":"","sources":["../../../src/im/lark/v3-gate-card-handler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * v3 humanGate 审批卡 — 复用 v0.2 审批卡的视觉(header 配色 / 字段 / freeze 态),
3
+ * 但**自带 action namespace + value 形态**(codex review #4):
4
+ * - action: `v3_gate_approve` / `v3_gate_reject`
5
+ * - value: `{ action, runId, waitId, nodeId, nonce, selected }`
6
+ * 刻意不复用 v0.2 `workflow-card-handler` 的 wait path —— v3 的 wait 权威是
7
+ * `waits/<id>.json + journal.ndjson`,跟 v0.2 events schema 不同(见 humanGate
8
+ * daemon-card 设计 §4.3)。本文件**纯函数**,不碰 daemon / IO,单测友好。
9
+ */
10
+ export declare const V3_GATE_APPROVE_ACTION = "v3_gate_approve";
11
+ export declare const V3_GATE_REJECT_ACTION = "v3_gate_reject";
12
+ export type V3GateResolutionKind = 'approved' | 'rejected';
13
+ /** card 按钮回传的 value 形态——v3-gate-card-handler 据此解析。 */
14
+ export interface V3GateActionValue {
15
+ action: typeof V3_GATE_APPROVE_ACTION | typeof V3_GATE_REJECT_ACTION;
16
+ runId: string;
17
+ /** waitId = `${nodeId}-gate`;nodeId 单独带,免得 handler 去 strip 后缀(节点名可能含 -gate)。 */
18
+ waitId: string;
19
+ nodeId: string;
20
+ nonce: string;
21
+ selected?: string;
22
+ }
23
+ export interface V3GateCardInput {
24
+ runId: string;
25
+ waitId: string;
26
+ nodeId: string;
27
+ prompt: string;
28
+ /** 卡 nonce(防 stale 卡重复触发);省略则按 runId/waitId 推导。 */
29
+ nonce?: string;
30
+ webDetailUrl?: string;
31
+ promptMaxChars?: number;
32
+ options?: string[];
33
+ approveOptions?: string[];
34
+ approvers?: string[];
35
+ /** 有值 → 渲染冻结的「已通过 / 已拒绝」卡(无按钮,防 stale UI 重复提交)。 */
36
+ resolution?: {
37
+ kind: V3GateResolutionKind;
38
+ by?: string;
39
+ selected?: string;
40
+ };
41
+ }
42
+ /** 稳定 nonce:同一 run 同一 wait 的卡 nonce 固定,重发卡也一致(幂等校验用)。 */
43
+ export declare function v3GateCardNonce(runId: string, waitId: string): string;
44
+ /** v3 run 在 dashboard 的详情页 URL(跟 v0.2 的 #/workflows 对称,走 #/v3)。 */
45
+ export declare function v3RunDetailUrl(runId: string): string;
46
+ export declare function buildV3GateCard(input: V3GateCardInput): string;
47
+ //# sourceMappingURL=v3-gate-card.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"v3-gate-card.d.ts","sourceRoot":"","sources":["../../../src/im/lark/v3-gate-card.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,eAAO,MAAM,sBAAsB,oBAAoB,CAAC;AACxD,eAAO,MAAM,qBAAqB,mBAAmB,CAAC;AAEtD,MAAM,MAAM,oBAAoB,GAAG,UAAU,GAAG,UAAU,CAAC;AAE3D,sDAAsD;AACtD,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,OAAO,sBAAsB,GAAG,OAAO,qBAAqB,CAAC;IACrE,KAAK,EAAE,MAAM,CAAC;IACd,gFAAgF;IAChF,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,mDAAmD;IACnD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,mDAAmD;IACnD,UAAU,CAAC,EAAE;QAAE,IAAI,EAAE,oBAAoB,CAAC;QAAC,EAAE,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC7E;AAID,yDAAyD;AACzD,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAErE;AAED,mEAAmE;AACnE,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,CAmE9D"}
@@ -0,0 +1,119 @@
1
+ /**
2
+ * v3 humanGate 审批卡 — 复用 v0.2 审批卡的视觉(header 配色 / 字段 / freeze 态),
3
+ * 但**自带 action namespace + value 形态**(codex review #4):
4
+ * - action: `v3_gate_approve` / `v3_gate_reject`
5
+ * - value: `{ action, runId, waitId, nodeId, nonce, selected }`
6
+ * 刻意不复用 v0.2 `workflow-card-handler` 的 wait path —— v3 的 wait 权威是
7
+ * `waits/<id>.json + journal.ndjson`,跟 v0.2 events schema 不同(见 humanGate
8
+ * daemon-card 设计 §4.3)。本文件**纯函数**,不碰 daemon / IO,单测友好。
9
+ */
10
+ import { config } from '../../config.js';
11
+ import { DEFAULT_HUMAN_GATE_OPTIONS } from '../../workflows/v3/dag.js';
12
+ export const V3_GATE_APPROVE_ACTION = 'v3_gate_approve';
13
+ export const V3_GATE_REJECT_ACTION = 'v3_gate_reject';
14
+ const DEFAULT_PROMPT_MAX_CHARS = 500;
15
+ /** 稳定 nonce:同一 run 同一 wait 的卡 nonce 固定,重发卡也一致(幂等校验用)。 */
16
+ export function v3GateCardNonce(runId, waitId) {
17
+ return `v3gate:${runId}:${waitId}`;
18
+ }
19
+ /** v3 run 在 dashboard 的详情页 URL(跟 v0.2 的 #/workflows 对称,走 #/v3)。 */
20
+ export function v3RunDetailUrl(runId) {
21
+ return `http://${config.dashboard.externalHost}:${config.dashboard.port}/#/v3/${encodeURIComponent(runId)}`;
22
+ }
23
+ export function buildV3GateCard(input) {
24
+ const nonce = input.nonce ?? v3GateCardNonce(input.runId, input.waitId);
25
+ const webDetailUrl = input.webDetailUrl ?? v3RunDetailUrl(input.runId);
26
+ const promptMax = input.promptMaxChars ?? DEFAULT_PROMPT_MAX_CHARS;
27
+ const prompt = truncate(input.prompt, promptMax);
28
+ const resolution = input.resolution;
29
+ const options = input.options ?? [...DEFAULT_HUMAN_GATE_OPTIONS];
30
+ const approveOptions = input.approveOptions ?? (options.includes('approve') ? ['approve'] : [options[0]]);
31
+ const title = resolution
32
+ ? `${resolutionPrefix(resolution.kind)}:${titleText(input.nodeId)}`
33
+ : `需要审批:${titleText(input.nodeId)}`;
34
+ const template = resolution ? (resolution.kind === 'approved' ? 'green' : 'red') : 'blue';
35
+ const elements = [
36
+ {
37
+ tag: 'div',
38
+ fields: [
39
+ { is_short: true, text: { tag: 'lark_md', content: `**Run**\n${escapeMd(short(input.runId, 24))}` } },
40
+ { is_short: true, text: { tag: 'lark_md', content: `**节点**\n${escapeMd(input.nodeId)}` } },
41
+ ],
42
+ },
43
+ { tag: 'hr' },
44
+ {
45
+ tag: 'div',
46
+ text: { tag: 'lark_md', content: `**审批内容**\n${escapeMd(prompt)}` },
47
+ },
48
+ ];
49
+ if (resolution) {
50
+ elements.push({ tag: 'hr' });
51
+ elements.push({
52
+ tag: 'div',
53
+ text: {
54
+ tag: 'lark_md',
55
+ content: (resolution.kind === 'approved' ? '✅ 已通过' : '❌ 已拒绝') +
56
+ (resolution.selected ? ` · ${escapeMd(short(resolution.selected, 20))}` : '') +
57
+ (resolution.by ? ` · by ${escapeMd(short(resolution.by, 20))}` : ''),
58
+ },
59
+ });
60
+ }
61
+ else {
62
+ elements.push({
63
+ tag: 'action',
64
+ actions: options.map((opt) => optionButton(opt, approveOptions, input, nonce)),
65
+ });
66
+ }
67
+ elements.push({
68
+ tag: 'action',
69
+ actions: [
70
+ {
71
+ tag: 'button',
72
+ text: { tag: 'plain_text', content: 'Web 详情' },
73
+ type: 'default',
74
+ multi_url: {
75
+ url: webDetailUrl, pc_url: webDetailUrl, android_url: webDetailUrl, ios_url: webDetailUrl,
76
+ },
77
+ },
78
+ ],
79
+ });
80
+ return JSON.stringify({
81
+ config: { wide_screen_mode: true },
82
+ header: { template, title: { tag: 'plain_text', content: title } },
83
+ elements,
84
+ });
85
+ }
86
+ function actionValue(action, runId, waitId, nodeId, nonce, selected) {
87
+ return { action, runId, waitId, nodeId, nonce, selected };
88
+ }
89
+ function optionButton(selected, approveOptions, input, nonce) {
90
+ const approved = approveOptions.includes(selected);
91
+ const label = selected === 'approve' ? '✅ 通过'
92
+ : selected === 'reject' ? '❌ 拒绝'
93
+ : selected;
94
+ return {
95
+ tag: 'button',
96
+ text: { tag: 'plain_text', content: label },
97
+ type: approved ? 'primary' : 'danger',
98
+ value: actionValue(approved ? V3_GATE_APPROVE_ACTION : V3_GATE_REJECT_ACTION, input.runId, input.waitId, input.nodeId, nonce, selected),
99
+ };
100
+ }
101
+ function resolutionPrefix(kind) {
102
+ return kind === 'approved' ? '已通过' : '已拒绝';
103
+ }
104
+ function titleText(nodeId) {
105
+ return `humanGate · ${nodeId}`;
106
+ }
107
+ function truncate(s, max) {
108
+ if (s.length <= max)
109
+ return s;
110
+ return `${s.slice(0, max)}…(截断,完整见 Web 详情)`;
111
+ }
112
+ function short(s, max) {
113
+ return s.length <= max ? s : `${s.slice(0, max)}…`;
114
+ }
115
+ /** 转义 lark_md 里会被解析的字符,防 prompt 注入破坏卡片结构。 */
116
+ function escapeMd(s) {
117
+ return s.replace(/[\\*_~`\[\]]/g, (c) => `\\${c}`);
118
+ }
119
+ //# sourceMappingURL=v3-gate-card.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"v3-gate-card.js","sourceRoot":"","sources":["../../../src/im/lark/v3-gate-card.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * v3 loop 追加一轮卡点击处理 —— card-handler 的 v3 分支(与
3
+ * `v3-blocked-card-handler` 同骨架)。把一次「➕ 追加 1 轮」点击翻译成:
4
+ * 权限校验 → `requestV3LoopGrant`(幂等:pendingGrant → already-granted)
5
+ * → 冻结卡 / toast + 触发 `driveV3Run` 跑新一轮。
6
+ *
7
+ * 纯逻辑 + 注入 seam(requestGrant / driveRun / canResolve),单测友好。
8
+ */
9
+ import { type V3LoopGrantActionValue } from './v3-loop-grant-card.js';
10
+ import { requestV3LoopGrant } from '../../workflows/v3/daemon-run.js';
11
+ import { type RunChatBinding } from '../../workflows/v3/grill-state.js';
12
+ export declare function isV3LoopGrantAction(action: unknown): boolean;
13
+ export interface V3LoopGrantCardHandlerDeps {
14
+ baseDir?: string;
15
+ /** Re-enter the run after a grant append. Fire-and-forget (daemon logs errors). */
16
+ driveRun: (runId: string) => void;
17
+ /** Permission: may this operator grant iterations of this run? Same
18
+ * semantics as the gate/blocked handlers' canResolve. */
19
+ canResolve?: (binding: RunChatBinding | undefined, operatorOpenId: string | undefined) => boolean;
20
+ /** Injectable for tests. Default = real requestV3LoopGrant. */
21
+ requestGrant?: typeof requestV3LoopGrant;
22
+ }
23
+ /**
24
+ * Handle a v3 loop-grant card click. Returns the Lark card-action response:
25
+ * a frozen「已追加」card, or a `{ toast }`. Triggers `driveRun` on success.
26
+ */
27
+ export declare function handleV3LoopGrantAction(value: V3LoopGrantActionValue, operatorOpenId: string | undefined, deps: V3LoopGrantCardHandlerDeps): Promise<unknown>;
28
+ //# sourceMappingURL=v3-loop-grant-card-handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"v3-loop-grant-card-handler.d.ts","sourceRoot":"","sources":["../../../src/im/lark/v3-loop-grant-card-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAIL,KAAK,sBAAsB,EAC5B,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,kBAAkB,EAAwB,MAAM,kCAAkC,CAAC;AAE5F,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,mCAAmC,CAAC;AAG3C,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAE5D;AAED,MAAM,WAAW,0BAA0B;IACzC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,oFAAoF;IACpF,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC;8DAC0D;IAC1D,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,GAAG,SAAS,EAAE,cAAc,EAAE,MAAM,GAAG,SAAS,KAAK,OAAO,CAAC;IAClG,+DAA+D;IAC/D,YAAY,CAAC,EAAE,OAAO,kBAAkB,CAAC;CAC1C;AAED;;;GAGG;AACH,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,sBAAsB,EAC7B,cAAc,EAAE,MAAM,GAAG,SAAS,EAClC,IAAI,EAAE,0BAA0B,GAC/B,OAAO,CAAC,OAAO,CAAC,CAwElB"}