feat: surface codex app-server approval progress
This commit is contained in:
@@ -1296,6 +1296,74 @@ public final class BossUi {
|
||||
return card;
|
||||
}
|
||||
|
||||
JSONArray warnings = progress == null ? null : progress.optJSONArray("warnings");
|
||||
if (warnings != null && warnings.length() > 0) {
|
||||
card.addView(divider(context));
|
||||
card.addView(sectionTitle(context, "安全提醒"));
|
||||
int shown = 0;
|
||||
for (int i = 0; i < warnings.length(); i += 1) {
|
||||
JSONObject warning = warnings.optJSONObject(i);
|
||||
String message = warning == null ? "" : warning.optString("message", "").trim();
|
||||
if (TextUtils.isEmpty(message)) {
|
||||
continue;
|
||||
}
|
||||
card.addView(detailRow(context, "!", message, "", false));
|
||||
shown += 1;
|
||||
if (shown >= 3) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JSONArray approvals = progress == null ? null : progress.optJSONArray("approvals");
|
||||
if (approvals != null && approvals.length() > 0) {
|
||||
card.addView(divider(context));
|
||||
card.addView(sectionTitle(context, "审批状态"));
|
||||
int shown = 0;
|
||||
for (int i = 0; i < approvals.length(); i += 1) {
|
||||
JSONObject approval = approvals.optJSONObject(i);
|
||||
String label = approval == null ? "" : approval.optString("label", "").trim();
|
||||
if (TextUtils.isEmpty(label)) {
|
||||
continue;
|
||||
}
|
||||
String status = approval.optString("status", "").trim();
|
||||
String detail = approval.optString("detail", "").trim();
|
||||
String riskLevel = approval.optString("riskLevel", "").trim();
|
||||
String rowLabel = TextUtils.isEmpty(status) ? label : label + " · " + status;
|
||||
card.addView(detailRow(context, "◇", rowLabel, riskLevel, false));
|
||||
if (!TextUtils.isEmpty(detail)) {
|
||||
card.addView(detailRow(context, "", detail, "", false, true));
|
||||
}
|
||||
shown += 1;
|
||||
if (shown >= 4) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JSONArray fileChanges = progress == null ? null : progress.optJSONArray("fileChanges");
|
||||
if (fileChanges != null && fileChanges.length() > 0) {
|
||||
card.addView(divider(context));
|
||||
card.addView(sectionTitle(context, "文件变更"));
|
||||
int shown = 0;
|
||||
for (int i = 0; i < fileChanges.length(); i += 1) {
|
||||
JSONObject fileChange = fileChanges.optJSONObject(i);
|
||||
String path = fileChange == null ? "" : fileChange.optString("path", "").trim();
|
||||
if (TextUtils.isEmpty(path)) {
|
||||
continue;
|
||||
}
|
||||
String kind = fileChange.optString("kind", "").trim();
|
||||
card.addView(detailRow(context, "▣", path, kind, false));
|
||||
shown += 1;
|
||||
if (shown >= 6) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (fileChanges.length() > shown) {
|
||||
card.addView(detailRow(context, "", "再显示 " + (fileChanges.length() - shown) + " 个", "", false, true));
|
||||
}
|
||||
}
|
||||
|
||||
card.addView(divider(context));
|
||||
card.addView(sectionTitle(context, "分支详情"));
|
||||
JSONObject branch = progress == null ? null : progress.optJSONObject("branch");
|
||||
|
||||
@@ -904,6 +904,60 @@ public class ProjectDetailActivityUiTest {
|
||||
assertTrue(viewTreeContainsText(messageView, "Mendel(explorer)"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void executionProgressMessageRendersCodexApprovalAndFileChangeSections() throws Exception {
|
||||
Intent intent = new Intent()
|
||||
.putExtra(ProjectDetailActivity.EXTRA_PROJECT_ID, "thread-approval")
|
||||
.putExtra(ProjectDetailActivity.EXTRA_PROJECT_NAME, "Boss开发主线程");
|
||||
TestProjectDetailActivity activity = Robolectric
|
||||
.buildActivity(TestProjectDetailActivity.class, intent)
|
||||
.setup()
|
||||
.get();
|
||||
|
||||
JSONObject message = new JSONObject()
|
||||
.put("id", "progress-approval-1")
|
||||
.put("sender", "master")
|
||||
.put("senderLabel", "主 Agent")
|
||||
.put("body", "执行进度")
|
||||
.put("kind", "execution_progress")
|
||||
.put("sentAt", "2026-05-31T10:16:00+08:00")
|
||||
.put("executionProgress", new JSONObject()
|
||||
.put("status", "running")
|
||||
.put("steps", new JSONArray()
|
||||
.put(new JSONObject().put("text", "等待 Codex 审批事件").put("status", "running")))
|
||||
.put("approvals", new JSONArray()
|
||||
.put(new JSONObject()
|
||||
.put("label", "命令执行审批")
|
||||
.put("status", "resolved")
|
||||
.put("detail", "需要确认命令执行")
|
||||
.put("riskLevel", "medium")))
|
||||
.put("warnings", new JSONArray()
|
||||
.put(new JSONObject()
|
||||
.put("message", "检测到需要用户确认的命令执行。")
|
||||
.put("severity", "warning")))
|
||||
.put("fileChanges", new JSONArray()
|
||||
.put(new JSONObject()
|
||||
.put("path", "src/app/page.tsx")
|
||||
.put("kind", "update")
|
||||
.put("status", "updated"))));
|
||||
|
||||
View messageView = ReflectionHelpers.callInstanceMethod(
|
||||
activity,
|
||||
"buildMessageView",
|
||||
ReflectionHelpers.ClassParameter.from(JSONObject.class, message)
|
||||
);
|
||||
|
||||
assertTrue(viewTreeContainsText(messageView, "安全提醒"));
|
||||
assertTrue(viewTreeContainsText(messageView, "检测到需要用户确认的命令执行。"));
|
||||
assertTrue(viewTreeContainsText(messageView, "审批状态"));
|
||||
assertTrue(viewTreeContainsText(messageView, "命令执行审批 · resolved"));
|
||||
assertTrue(viewTreeContainsText(messageView, "需要确认命令执行"));
|
||||
assertTrue(viewTreeContainsText(messageView, "文件变更"));
|
||||
assertTrue(viewTreeContainsText(messageView, "src/app/page.tsx"));
|
||||
assertFalse(viewTreeContainsText(messageView, "sk-secret"));
|
||||
assertFalse(viewTreeContainsText(messageView, "diff"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nativeRemoteExecutionProgressDoesNotRenderCodexSections() throws Exception {
|
||||
Intent intent = new Intent()
|
||||
|
||||
Reference in New Issue
Block a user