feishu-codex-console 1.0.0-beta.6
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/.env.example +101 -0
- package/.feishu-codex-policy.example.json +11 -0
- package/.feishu-codex-runbooks.example.json +36 -0
- package/CHANGELOG.md +129 -0
- package/CODE_OF_CONDUCT.md +7 -0
- package/CONTRIBUTING.md +52 -0
- package/LICENSE +21 -0
- package/README.en.md +81 -0
- package/README.md +398 -0
- package/ROADMAP.md +40 -0
- package/SECURITY.md +53 -0
- package/dist/account-quota-card.js +233 -0
- package/dist/account-quota.js +125 -0
- package/dist/app-server-client.js +281 -0
- package/dist/card-session.js +166 -0
- package/dist/codex-events.js +1 -0
- package/dist/codex-runner.js +875 -0
- package/dist/config.js +198 -0
- package/dist/confirmation-card.js +135 -0
- package/dist/control-card.js +345 -0
- package/dist/conversation-turn-session.js +209 -0
- package/dist/data-maintenance.js +71 -0
- package/dist/device-card.js +460 -0
- package/dist/device-health.js +94 -0
- package/dist/diagnostics.js +253 -0
- package/dist/doctor.js +250 -0
- package/dist/fallback-card-session.js +37 -0
- package/dist/health-file.js +75 -0
- package/dist/index.js +4330 -0
- package/dist/lark-cli.js +558 -0
- package/dist/lark-retry.js +34 -0
- package/dist/maintenance.js +140 -0
- package/dist/model-capabilities.js +31 -0
- package/dist/onboarding-card.js +312 -0
- package/dist/permission-lease.js +22 -0
- package/dist/policy.js +506 -0
- package/dist/progress.js +267 -0
- package/dist/project-card.js +303 -0
- package/dist/project-overview-card.js +182 -0
- package/dist/project-overview.js +278 -0
- package/dist/project-policy.js +160 -0
- package/dist/project-registry.js +259 -0
- package/dist/project-status.js +45 -0
- package/dist/project-workspace.js +55 -0
- package/dist/quota-card.js +94 -0
- package/dist/recovery-policy.js +26 -0
- package/dist/redaction.js +67 -0
- package/dist/remote-ready.js +112 -0
- package/dist/response-card.js +139 -0
- package/dist/result-card.js +166 -0
- package/dist/review-card.js +452 -0
- package/dist/runbook-card.js +272 -0
- package/dist/runbooks.js +191 -0
- package/dist/runtime-card.js +337 -0
- package/dist/session-card.js +128 -0
- package/dist/session-naming.js +14 -0
- package/dist/smoke.js +28 -0
- package/dist/state-backup.js +302 -0
- package/dist/state-store.js +874 -0
- package/dist/task-card.js +640 -0
- package/dist/task-center-card.js +176 -0
- package/dist/task-failure.js +43 -0
- package/dist/task-intent.js +76 -0
- package/dist/task-queue.js +187 -0
- package/dist/task-reconciliation.js +80 -0
- package/dist/task-review.js +497 -0
- package/dist/team-card.js +275 -0
- package/dist/team-directory.js +54 -0
- package/dist/team-policy.js +93 -0
- package/dist/types.js +1 -0
- package/dist/version.js +9 -0
- package/dist/workspace-session.js +64 -0
- package/docs/ARCHITECTURE.md +54 -0
- package/docs/COMPATIBILITY.md +55 -0
- package/docs/CONFIGURATION.md +88 -0
- package/docs/DEMO.md +45 -0
- package/docs/GOOD_FIRST_ISSUES.md +23 -0
- package/docs/INSTALLATION.md +207 -0
- package/docs/OPEN_SOURCE_PRODUCT_PLAN.md +113 -0
- package/docs/PRODUCT_REQUIREMENTS_MAP.md +591 -0
- package/docs/RELEASE_CHECKLIST.md +65 -0
- package/docs/TEAM_DEPLOYMENT.md +35 -0
- package/docs/TROUBLESHOOTING.md +130 -0
- package/docs/V4_WORKSPACE_SESSION_FLOW.md +232 -0
- package/docs/requirements/D10_MAINTENANCE_AND_ECOSYSTEM.md +103 -0
- package/docs/requirements/D1_INSTALLATION_AND_FIRST_CONNECTION.md +479 -0
- package/docs/requirements/D2_DEVICE_AND_CONNECTIVITY.md +54 -0
- package/docs/requirements/D3_PROJECTS_AND_SESSIONS.md +107 -0
- package/docs/requirements/D4_REMOTE_TASK_EXECUTION.md +102 -0
- package/docs/requirements/D5_CODEX_NATIVE_INTERACTIONS.md +99 -0
- package/docs/requirements/D6_RESULTS_AND_CODE_REVIEW.md +100 -0
- package/docs/requirements/D7_SECURITY_GOVERNANCE.md +106 -0
- package/docs/requirements/D8_RELIABILITY_AND_RECOVERY.md +182 -0
- package/docs/requirements/D9_TEAM_COLLABORATION.md +129 -0
- package/package.json +76 -0
- package/scripts/capability-probe.mjs +113 -0
- package/scripts/cli.mjs +919 -0
- package/scripts/config-file.mjs +137 -0
- package/scripts/discovery-lib.mjs +78 -0
- package/scripts/install-card.mjs +37 -0
- package/scripts/install-detection.mjs +126 -0
- package/scripts/install-state.mjs +107 -0
- package/scripts/launchd.mjs +161 -0
- package/scripts/migrate-legacy.mjs +97 -0
- package/scripts/package-smoke.mjs +163 -0
- package/scripts/release-dist-tag.mjs +7 -0
- package/scripts/runbook-template.mjs +36 -0
- package/scripts/service-health.mjs +110 -0
- package/scripts/service.mjs +24 -0
- package/scripts/setup-lib.mjs +118 -0
- package/scripts/systemd.mjs +96 -0
- package/scripts/upgrade-lib.mjs +99 -0
- package/scripts/verify-release.mjs +37 -0
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
import { effectiveTestRuns, testEvidenceState, } from "./progress.js";
|
|
2
|
+
export function renderReviewCard(snapshot) {
|
|
3
|
+
const tests = effectiveTestRuns(snapshot.commandRuns);
|
|
4
|
+
const testState = summarizeTests(tests);
|
|
5
|
+
const body = snapshot.fileDiff
|
|
6
|
+
? renderDiffBody(snapshot)
|
|
7
|
+
: renderOverviewBody(snapshot, tests, testState);
|
|
8
|
+
return {
|
|
9
|
+
schema: "2.0",
|
|
10
|
+
config: {
|
|
11
|
+
update_multi: true,
|
|
12
|
+
width_mode: "default",
|
|
13
|
+
enable_forward: false,
|
|
14
|
+
summary: { content: `任务验证 · ${safePlain(snapshot.projectLabel)}` },
|
|
15
|
+
},
|
|
16
|
+
header: {
|
|
17
|
+
title: { tag: "plain_text", content: "Codex 任务验证" },
|
|
18
|
+
subtitle: {
|
|
19
|
+
tag: "plain_text",
|
|
20
|
+
content: truncatePlain(snapshot.prompt.replace(/\s+/g, " "), 72),
|
|
21
|
+
},
|
|
22
|
+
template: testState.state === "failed"
|
|
23
|
+
? "red"
|
|
24
|
+
: snapshot.review.attribution === "mixed" || snapshot.review.attribution === "unknown"
|
|
25
|
+
? "orange"
|
|
26
|
+
: "green",
|
|
27
|
+
icon: { tag: "standard_icon", token: "tasklist_colorful" },
|
|
28
|
+
text_tag_list: [
|
|
29
|
+
{
|
|
30
|
+
tag: "text_tag",
|
|
31
|
+
text: { tag: "plain_text", content: reviewStatusLabel(snapshot.review) },
|
|
32
|
+
color: snapshot.review.availability === "ready" ? "green" : "neutral",
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
tag: "text_tag",
|
|
36
|
+
text: { tag: "plain_text", content: testState.label },
|
|
37
|
+
color: testState.state === "passed" ? "green" : testState.state === "failed" ? "red" : "neutral",
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
tag: "text_tag",
|
|
41
|
+
text: { tag: "plain_text", content: `#${snapshot.taskId.slice(0, 12)}` },
|
|
42
|
+
color: "neutral",
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
},
|
|
46
|
+
body: {
|
|
47
|
+
direction: "vertical",
|
|
48
|
+
padding: "12px 12px 18px 12px",
|
|
49
|
+
vertical_spacing: "12px",
|
|
50
|
+
elements: body,
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function renderOverviewBody(snapshot, tests, testState) {
|
|
55
|
+
const review = snapshot.review;
|
|
56
|
+
const start = snapshot.page * snapshot.pageSize;
|
|
57
|
+
const pageFiles = review.files.slice(start, start + snapshot.pageSize);
|
|
58
|
+
const pageCount = Math.max(1, Math.ceil(review.files.length / snapshot.pageSize));
|
|
59
|
+
if (review.availability === "no_changes" && tests.length === 0) {
|
|
60
|
+
return [
|
|
61
|
+
...(snapshot.feedback ? [feedbackPanel(snapshot.feedback)] : []),
|
|
62
|
+
{
|
|
63
|
+
tag: "column_set",
|
|
64
|
+
element_id: "review_empty",
|
|
65
|
+
flex_mode: "none",
|
|
66
|
+
columns: [
|
|
67
|
+
{
|
|
68
|
+
tag: "column",
|
|
69
|
+
width: "weighted",
|
|
70
|
+
weight: 1,
|
|
71
|
+
background_style: "grey-50",
|
|
72
|
+
padding: "12px 12px 12px 12px",
|
|
73
|
+
elements: [
|
|
74
|
+
{
|
|
75
|
+
tag: "markdown",
|
|
76
|
+
content: "**这次任务没有可验证的代码结果**\n<font color='grey'>未发现文件变更,也没有运行测试;结果属于分析或说明,无需进入代码审阅。</font>",
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
},
|
|
80
|
+
],
|
|
81
|
+
},
|
|
82
|
+
overviewActions(snapshot.taskId, 0, false, false, snapshot.embedded ?? false),
|
|
83
|
+
];
|
|
84
|
+
}
|
|
85
|
+
const elements = [
|
|
86
|
+
metadataRow(review, testState.label),
|
|
87
|
+
reviewConfidencePanel(review),
|
|
88
|
+
];
|
|
89
|
+
if (snapshot.feedback)
|
|
90
|
+
elements.push(feedbackPanel(snapshot.feedback));
|
|
91
|
+
elements.push(testPanel(tests));
|
|
92
|
+
if (review.availability === "ready" && pageFiles.length > 0) {
|
|
93
|
+
elements.push({
|
|
94
|
+
tag: "markdown",
|
|
95
|
+
element_id: "files_heading",
|
|
96
|
+
content: `**文件变更** <font color='grey'>第 ${snapshot.page + 1} / ${pageCount} 页</font>`,
|
|
97
|
+
});
|
|
98
|
+
pageFiles.forEach((file, index) => {
|
|
99
|
+
elements.push(fileRow(snapshot.taskId, file, start + index, index));
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
elements.push({
|
|
104
|
+
tag: "column_set",
|
|
105
|
+
element_id: "review_empty",
|
|
106
|
+
flex_mode: "none",
|
|
107
|
+
columns: [
|
|
108
|
+
{
|
|
109
|
+
tag: "column",
|
|
110
|
+
width: "weighted",
|
|
111
|
+
weight: 1,
|
|
112
|
+
background_style: "grey-50",
|
|
113
|
+
padding: "12px 12px 12px 12px",
|
|
114
|
+
elements: [
|
|
115
|
+
{
|
|
116
|
+
tag: "markdown",
|
|
117
|
+
content: review.availability === "no_changes"
|
|
118
|
+
? "**未发现代码变更**\n<font color='grey'>任务可能只完成了分析,或修改已经提交且基线不可用。</font>"
|
|
119
|
+
: `**暂时无法生成 Diff**\n<font color='grey'>${safe(review.error ?? "请回到本机确认工作区状态。")}</font>`,
|
|
120
|
+
},
|
|
121
|
+
],
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
elements.push(overviewActions(snapshot.taskId, snapshot.page, snapshot.page > 0, start + pageFiles.length < review.files.length, snapshot.embedded ?? false));
|
|
127
|
+
elements.push({
|
|
128
|
+
tag: "markdown",
|
|
129
|
+
element_id: "review_footer",
|
|
130
|
+
text_size: "notation",
|
|
131
|
+
text_align: "center",
|
|
132
|
+
content: "<font color='grey'>审阅是只读操作;直接发送补充要求可继续修改。提交、推送、部署和 PR 仍需单独确认。</font>",
|
|
133
|
+
});
|
|
134
|
+
return elements;
|
|
135
|
+
}
|
|
136
|
+
function renderDiffBody(snapshot) {
|
|
137
|
+
const diff = snapshot.fileDiff;
|
|
138
|
+
const pageSize = snapshot.diffPageSize ?? 7_000;
|
|
139
|
+
const pageCount = Math.max(1, Math.ceil(diff.content.length / pageSize));
|
|
140
|
+
const diffPage = Math.min(Math.max(0, snapshot.diffPage ?? 0), pageCount - 1);
|
|
141
|
+
const visibleContent = diff.content.slice(diffPage * pageSize, (diffPage + 1) * pageSize);
|
|
142
|
+
const selected = snapshot.selectedFileIndex === undefined
|
|
143
|
+
? undefined
|
|
144
|
+
: snapshot.review.files[snapshot.selectedFileIndex];
|
|
145
|
+
const warnings = [
|
|
146
|
+
...(selected?.attribution === "mixed"
|
|
147
|
+
? ["这个文件在任务开始前已有修改,下面是合并后的工作区 Diff。"]
|
|
148
|
+
: []),
|
|
149
|
+
...(diff.stale ? ["文件在任务完成后又发生了变化,下面显示的是当前工作区内容。"] : []),
|
|
150
|
+
...(diff.sensitive ? ["敏感路径已隐藏正文。"] : []),
|
|
151
|
+
...(diff.truncated ? ["Diff 超过安全预览总量,最后一页会标记截断。"] : []),
|
|
152
|
+
];
|
|
153
|
+
return [
|
|
154
|
+
...(snapshot.feedback ? [feedbackPanel(snapshot.feedback)] : []),
|
|
155
|
+
{
|
|
156
|
+
tag: "markdown",
|
|
157
|
+
element_id: "diff_heading",
|
|
158
|
+
content: `**${safe(diff.path)}** <font color='grey'>Diff 第 ${diffPage + 1} / ${pageCount} 页</font>\n<font color='grey'>${warnings.length > 0 ? warnings.map(safe).join(" ") : "任务完成时的只读差异快照"}</font>`,
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
tag: "column_set",
|
|
162
|
+
element_id: "diff_panel",
|
|
163
|
+
flex_mode: "none",
|
|
164
|
+
columns: [
|
|
165
|
+
{
|
|
166
|
+
tag: "column",
|
|
167
|
+
width: "weighted",
|
|
168
|
+
weight: 1,
|
|
169
|
+
background_style: diff.error || diff.sensitive ? "orange-50" : "grey-50",
|
|
170
|
+
padding: "12px 12px 12px 12px",
|
|
171
|
+
elements: [
|
|
172
|
+
{
|
|
173
|
+
tag: "markdown",
|
|
174
|
+
element_id: "diff_content",
|
|
175
|
+
content: `\`\`\`diff\n${safeCode(visibleContent)}\n\`\`\``,
|
|
176
|
+
},
|
|
177
|
+
],
|
|
178
|
+
},
|
|
179
|
+
],
|
|
180
|
+
},
|
|
181
|
+
diffActions(snapshot.taskId, snapshot.page, snapshot.selectedFileIndex ?? 0, diffPage, pageCount, snapshot.embedded ?? false),
|
|
182
|
+
];
|
|
183
|
+
}
|
|
184
|
+
function metadataRow(review, testLabel) {
|
|
185
|
+
return {
|
|
186
|
+
tag: "column_set",
|
|
187
|
+
element_id: "review_metadata",
|
|
188
|
+
flex_mode: "none",
|
|
189
|
+
horizontal_spacing: "8px",
|
|
190
|
+
columns: [
|
|
191
|
+
metric("文件", String(review.totalFiles), "left"),
|
|
192
|
+
metric("新增 / 删除", `+${review.totalAdditions} / -${review.totalDeletions}`, "center"),
|
|
193
|
+
metric("测试", testLabel, "right"),
|
|
194
|
+
],
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
function metric(label, value, align) {
|
|
198
|
+
return {
|
|
199
|
+
tag: "column",
|
|
200
|
+
width: "weighted",
|
|
201
|
+
weight: 1,
|
|
202
|
+
background_style: "grey-50",
|
|
203
|
+
padding: "8px 4px 8px 4px",
|
|
204
|
+
elements: [
|
|
205
|
+
{
|
|
206
|
+
tag: "markdown",
|
|
207
|
+
text_align: align,
|
|
208
|
+
text_size: "notation",
|
|
209
|
+
content: `<font color='grey'>${label}</font>\n**${safe(value)}**`,
|
|
210
|
+
},
|
|
211
|
+
],
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
function reviewConfidencePanel(review) {
|
|
215
|
+
const mixed = review.attribution !== "task";
|
|
216
|
+
const detail = review.attribution === "task"
|
|
217
|
+
? "任务开始时已建立 Git 基线;列表排除了未被本任务触碰的既有修改。"
|
|
218
|
+
: review.attribution === "mixed"
|
|
219
|
+
? "部分文件在任务开始前已经修改,Diff 可能同时包含你的原有内容;请逐文件确认。"
|
|
220
|
+
: "任务基线不可用,文件列表主要来自 Codex 事件,不能保证完整归因。";
|
|
221
|
+
const excluded = review.preexistingFilesExcluded > 0
|
|
222
|
+
? ` 已排除 ${review.preexistingFilesExcluded} 个未变化的既有脏文件。`
|
|
223
|
+
: "";
|
|
224
|
+
return {
|
|
225
|
+
tag: "column_set",
|
|
226
|
+
element_id: "confidence_panel",
|
|
227
|
+
flex_mode: "none",
|
|
228
|
+
columns: [
|
|
229
|
+
{
|
|
230
|
+
tag: "column",
|
|
231
|
+
width: "weighted",
|
|
232
|
+
weight: 1,
|
|
233
|
+
background_style: mixed ? "orange-50" : "green-50",
|
|
234
|
+
padding: "10px 12px 10px 12px",
|
|
235
|
+
elements: [
|
|
236
|
+
{
|
|
237
|
+
tag: "markdown",
|
|
238
|
+
content: `**${mixed ? "需要人工确认归因" : "任务基线清晰"}**\n${safe(detail + excluded)}`,
|
|
239
|
+
},
|
|
240
|
+
],
|
|
241
|
+
},
|
|
242
|
+
],
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
function testPanel(tests) {
|
|
246
|
+
const visible = tests.slice(-5);
|
|
247
|
+
const lines = visible.length === 0
|
|
248
|
+
? ["<font color='grey'>未检测到测试命令。任务完成不等于测试通过。</font>"]
|
|
249
|
+
: visible.map((run) => {
|
|
250
|
+
const status = run.status === "passed" ? "通过" : run.status === "failed" ? "失败" : "运行中";
|
|
251
|
+
const color = run.status === "passed" ? "green" : run.status === "failed" ? "red" : "blue";
|
|
252
|
+
const output = run.status === "failed" && run.outputSummary
|
|
253
|
+
? `\n<font color='grey'>${safe(truncatePlain(run.outputSummary, 500))}</font>`
|
|
254
|
+
: "";
|
|
255
|
+
return `<font color='${color}'>${status}</font> ${safe(truncatePlain(run.command, 140))}${run.exitCode === undefined ? "" : ` · 退出码 ${run.exitCode}`}${output}`;
|
|
256
|
+
});
|
|
257
|
+
return {
|
|
258
|
+
tag: "column_set",
|
|
259
|
+
element_id: "test_panel",
|
|
260
|
+
flex_mode: "none",
|
|
261
|
+
columns: [
|
|
262
|
+
{
|
|
263
|
+
tag: "column",
|
|
264
|
+
width: "weighted",
|
|
265
|
+
weight: 1,
|
|
266
|
+
background_style: "grey-50",
|
|
267
|
+
padding: "10px 12px 10px 12px",
|
|
268
|
+
vertical_spacing: "4px",
|
|
269
|
+
elements: [
|
|
270
|
+
{ tag: "markdown", content: ["**测试证据**", ...lines].join("\n\n") },
|
|
271
|
+
],
|
|
272
|
+
},
|
|
273
|
+
],
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
function fileRow(taskId, file, absoluteIndex, rowIndex) {
|
|
277
|
+
const stats = file.binary
|
|
278
|
+
? "二进制"
|
|
279
|
+
: file.additions === null || file.deletions === null
|
|
280
|
+
? "行数未知"
|
|
281
|
+
: `+${file.additions} / -${file.deletions}`;
|
|
282
|
+
const kind = file.kind === "add" ? "新增" : file.kind === "delete" ? "删除" : "修改";
|
|
283
|
+
const flags = [
|
|
284
|
+
file.attribution === "mixed" ? "含既有修改" : "任务变更",
|
|
285
|
+
file.sensitive ? "内容受保护" : "",
|
|
286
|
+
].filter(Boolean).join(" · ");
|
|
287
|
+
return {
|
|
288
|
+
tag: "column_set",
|
|
289
|
+
element_id: `review_file_${rowIndex}`,
|
|
290
|
+
flex_mode: "none",
|
|
291
|
+
columns: [
|
|
292
|
+
{
|
|
293
|
+
tag: "column",
|
|
294
|
+
width: "weighted",
|
|
295
|
+
weight: 1,
|
|
296
|
+
background_style: file.attribution === "mixed" ? "orange-50" : "grey-50",
|
|
297
|
+
padding: "9px 10px 9px 10px",
|
|
298
|
+
elements: [
|
|
299
|
+
{
|
|
300
|
+
tag: "markdown",
|
|
301
|
+
content: `**${safe(file.path)}**\n<font color='grey'>${kind} · ${stats} · ${flags}</font>`,
|
|
302
|
+
},
|
|
303
|
+
],
|
|
304
|
+
},
|
|
305
|
+
{
|
|
306
|
+
tag: "column",
|
|
307
|
+
width: "auto",
|
|
308
|
+
vertical_align: "center",
|
|
309
|
+
elements: [
|
|
310
|
+
actionButton(`file_open_${rowIndex}`, file.sensitive ? "说明" : "查看 Diff", "review_file", taskId, { file_index: absoluteIndex }),
|
|
311
|
+
],
|
|
312
|
+
},
|
|
313
|
+
],
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
function overviewActions(taskId, page, hasPrevious, hasNext, embedded) {
|
|
317
|
+
const actions = [];
|
|
318
|
+
if (hasPrevious) {
|
|
319
|
+
actions.push(actionButton("review_prev", "上一页", "review_page", taskId, { page: page - 1 }));
|
|
320
|
+
}
|
|
321
|
+
if (hasNext) {
|
|
322
|
+
actions.push(actionButton("review_next", "下一页", "review_page", taskId, { page: page + 1 }));
|
|
323
|
+
}
|
|
324
|
+
actions.push(actionButton("review_refresh", "刷新快照", "review_refresh", taskId));
|
|
325
|
+
if (embedded) {
|
|
326
|
+
actions.push(actionButton("review_close", "返回结果", "review_close", taskId));
|
|
327
|
+
}
|
|
328
|
+
return actionGrid("review_actions", actions);
|
|
329
|
+
}
|
|
330
|
+
function diffActions(taskId, page, fileIndex, diffPage, pageCount, embedded) {
|
|
331
|
+
const actions = [
|
|
332
|
+
actionButton("review_back", "返回列表", "review_back", taskId, { page }),
|
|
333
|
+
];
|
|
334
|
+
if (diffPage > 0) {
|
|
335
|
+
actions.push(actionButton("diff_prev", "上一页", "review_diff_page", taskId, {
|
|
336
|
+
page,
|
|
337
|
+
file_index: fileIndex,
|
|
338
|
+
diff_page: diffPage - 1,
|
|
339
|
+
}));
|
|
340
|
+
}
|
|
341
|
+
if (diffPage + 1 < pageCount) {
|
|
342
|
+
actions.push(actionButton("diff_next", "下一页", "review_diff_page", taskId, {
|
|
343
|
+
page,
|
|
344
|
+
file_index: fileIndex,
|
|
345
|
+
diff_page: diffPage + 1,
|
|
346
|
+
}));
|
|
347
|
+
}
|
|
348
|
+
if (pageCount === 1) {
|
|
349
|
+
actions.push(actionButton("diff_refresh", "刷新 Diff", "review_refresh", taskId, {
|
|
350
|
+
page,
|
|
351
|
+
file_index: fileIndex,
|
|
352
|
+
diff_page: diffPage,
|
|
353
|
+
}));
|
|
354
|
+
}
|
|
355
|
+
if (embedded) {
|
|
356
|
+
actions.push(actionButton("review_close", "返回结果", "review_close", taskId));
|
|
357
|
+
}
|
|
358
|
+
return actionGrid("diff_actions", actions);
|
|
359
|
+
}
|
|
360
|
+
function actionButton(elementId, label, action, taskId, extra = {}) {
|
|
361
|
+
return {
|
|
362
|
+
tag: "button",
|
|
363
|
+
element_id: elementId,
|
|
364
|
+
text: { tag: "plain_text", content: label },
|
|
365
|
+
type: action === "review_file" ? "primary_filled" : "default",
|
|
366
|
+
width: "fill",
|
|
367
|
+
behaviors: [
|
|
368
|
+
{
|
|
369
|
+
type: "callback",
|
|
370
|
+
value: {
|
|
371
|
+
bridge: "feishu-codex-v6",
|
|
372
|
+
action,
|
|
373
|
+
task_id: taskId,
|
|
374
|
+
...extra,
|
|
375
|
+
},
|
|
376
|
+
},
|
|
377
|
+
],
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
function actionGrid(id, actions) {
|
|
381
|
+
return {
|
|
382
|
+
tag: "column_set",
|
|
383
|
+
element_id: id,
|
|
384
|
+
flex_mode: "none",
|
|
385
|
+
horizontal_spacing: "8px",
|
|
386
|
+
columns: actions.map((action) => ({
|
|
387
|
+
tag: "column",
|
|
388
|
+
width: "weighted",
|
|
389
|
+
weight: 1,
|
|
390
|
+
elements: [action],
|
|
391
|
+
})),
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
function feedbackPanel(feedback) {
|
|
395
|
+
return {
|
|
396
|
+
tag: "column_set",
|
|
397
|
+
element_id: "review_feedback",
|
|
398
|
+
flex_mode: "none",
|
|
399
|
+
columns: [
|
|
400
|
+
{
|
|
401
|
+
tag: "column",
|
|
402
|
+
width: "weighted",
|
|
403
|
+
weight: 1,
|
|
404
|
+
background_style: "blue-50",
|
|
405
|
+
padding: "9px 11px 9px 11px",
|
|
406
|
+
elements: [{ tag: "markdown", content: `<font color='blue'>${safe(feedback)}</font>` }],
|
|
407
|
+
},
|
|
408
|
+
],
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
function summarizeTests(tests) {
|
|
412
|
+
const state = testEvidenceState(tests);
|
|
413
|
+
if (state === "missing")
|
|
414
|
+
return { state, label: "未运行测试" };
|
|
415
|
+
if (state === "failed") {
|
|
416
|
+
return { state, label: `${tests.filter((run) => run.status === "failed").length} 次测试失败` };
|
|
417
|
+
}
|
|
418
|
+
if (state === "running") {
|
|
419
|
+
return { state, label: `${tests.filter((run) => run.status === "running").length} 次测试运行中` };
|
|
420
|
+
}
|
|
421
|
+
return { state, label: `${tests.length} 次测试通过` };
|
|
422
|
+
}
|
|
423
|
+
function reviewStatusLabel(review) {
|
|
424
|
+
if (review.availability === "no_changes")
|
|
425
|
+
return "无代码变更";
|
|
426
|
+
if (review.availability === "unavailable")
|
|
427
|
+
return "审阅不可用";
|
|
428
|
+
return `${review.totalFiles} 个文件`;
|
|
429
|
+
}
|
|
430
|
+
function safe(value) {
|
|
431
|
+
return value
|
|
432
|
+
.replaceAll("&", "&")
|
|
433
|
+
.replaceAll("<", "<")
|
|
434
|
+
.replaceAll(">", ">")
|
|
435
|
+
.replaceAll("*", "*")
|
|
436
|
+
.replaceAll("`", "`")
|
|
437
|
+
.replaceAll("_", "_");
|
|
438
|
+
}
|
|
439
|
+
function safeCode(value) {
|
|
440
|
+
return value
|
|
441
|
+
.replaceAll("<at", "<at")
|
|
442
|
+
.replaceAll("</at>", "</at>")
|
|
443
|
+
.replaceAll("```", "```");
|
|
444
|
+
}
|
|
445
|
+
function safePlain(value) {
|
|
446
|
+
return value.replace(/[\r\n]/g, " ").slice(0, 80);
|
|
447
|
+
}
|
|
448
|
+
function truncatePlain(value, max) {
|
|
449
|
+
if (value.length <= max)
|
|
450
|
+
return value;
|
|
451
|
+
return `${value.slice(0, Math.max(0, max - 1))}…`;
|
|
452
|
+
}
|