feat: show codex app server summaries on android
This commit is contained in:
@@ -187,6 +187,64 @@ public class DeviceDetailActivity extends BossScreenActivity {
|
||||
null,
|
||||
null
|
||||
));
|
||||
if (WechatSurfaceMapper.hasCodexAppServerMetadata(device)) {
|
||||
appendContent(BossUi.buildWechatMenuRow(
|
||||
this,
|
||||
"模型",
|
||||
WechatSurfaceMapper.deviceCodexModelSummary(device),
|
||||
null,
|
||||
null,
|
||||
null
|
||||
));
|
||||
appendContent(BossUi.buildWechatMenuRow(
|
||||
this,
|
||||
"扩展",
|
||||
WechatSurfaceMapper.deviceCodexExtensionSummary(device),
|
||||
null,
|
||||
null,
|
||||
null
|
||||
));
|
||||
appendContent(BossUi.buildWechatMenuRow(
|
||||
this,
|
||||
"治理",
|
||||
WechatSurfaceMapper.deviceCodexGovernanceSummary(device),
|
||||
null,
|
||||
null,
|
||||
null
|
||||
));
|
||||
appendContent(BossUi.buildWechatMenuRow(
|
||||
this,
|
||||
"账号",
|
||||
WechatSurfaceMapper.deviceCodexAccountSummary(device),
|
||||
null,
|
||||
null,
|
||||
null
|
||||
));
|
||||
appendContent(BossUi.buildWechatMenuRow(
|
||||
this,
|
||||
"线程",
|
||||
WechatSurfaceMapper.deviceCodexThreadSummary(device),
|
||||
null,
|
||||
null,
|
||||
null
|
||||
));
|
||||
appendContent(BossUi.buildWechatMenuRow(
|
||||
this,
|
||||
"轮次",
|
||||
WechatSurfaceMapper.deviceCodexTurnSummary(device),
|
||||
null,
|
||||
null,
|
||||
null
|
||||
));
|
||||
appendContent(BossUi.buildWechatMenuRow(
|
||||
this,
|
||||
"线程操作",
|
||||
WechatSurfaceMapper.deviceCodexThreadActionSummary(device),
|
||||
null,
|
||||
null,
|
||||
null
|
||||
));
|
||||
}
|
||||
appendContent(BossUi.buildWechatMenuRow(
|
||||
this,
|
||||
"线程协作",
|
||||
|
||||
@@ -252,6 +252,10 @@ public final class WechatSurfaceMapper {
|
||||
return resolveDeviceCapability(device, "codexAppServer") != null;
|
||||
}
|
||||
|
||||
public static boolean hasCodexAppServerMetadata(JSONObject device) {
|
||||
return resolveCodexAppServerMetadata(device) != null;
|
||||
}
|
||||
|
||||
public static String deviceCodexAppServerStatusLabel(JSONObject device) {
|
||||
JSONObject capability = resolveDeviceCapability(device, "codexAppServer");
|
||||
boolean connected = capability != null && capability.optBoolean("connected", false);
|
||||
@@ -270,6 +274,64 @@ public final class WechatSurfaceMapper {
|
||||
return "最近上报 " + lastSeenAt;
|
||||
}
|
||||
|
||||
public static String deviceCodexModelSummary(JSONObject device) {
|
||||
JSONObject metadata = resolveCodexAppServerMetadata(device);
|
||||
int modelCount = metadataArrayLength(metadata, "models");
|
||||
if (modelCount <= 0) {
|
||||
return "未发现";
|
||||
}
|
||||
return modelCount + " 个"
|
||||
+ " · 默认 " + metadataText(metadata, "defaultModelId")
|
||||
+ " · 快速 " + metadataText(metadata, "fastModelId")
|
||||
+ " · 深度 " + metadataText(metadata, "deepModelId");
|
||||
}
|
||||
|
||||
public static String deviceCodexExtensionSummary(JSONObject device) {
|
||||
JSONObject metadata = resolveCodexAppServerMetadata(device);
|
||||
return "Skill " + metadataArrayLength(metadata, "skills") + " 个"
|
||||
+ " · Plugin " + metadataArrayLength(metadata, "plugins") + " 个"
|
||||
+ " · App " + metadataArrayLength(metadata, "apps") + " 个";
|
||||
}
|
||||
|
||||
public static String deviceCodexGovernanceSummary(JSONObject device) {
|
||||
JSONObject metadata = resolveCodexAppServerMetadata(device);
|
||||
return "实验特性 " + metadataArrayLength(metadata, "experimentalFeatures") + " 个"
|
||||
+ " · 协作模式 " + metadataArrayLength(metadata, "collaborationModes") + " 个"
|
||||
+ " · MCP " + metadataArrayLength(metadata, "mcpServers") + " 个"
|
||||
+ " · 权限 " + metadataArrayLength(metadata, "permissionProfiles") + " 个";
|
||||
}
|
||||
|
||||
public static String deviceCodexAccountSummary(JSONObject device) {
|
||||
JSONObject metadata = resolveCodexAppServerMetadata(device);
|
||||
JSONObject accountSummary = metadata == null ? null : metadata.optJSONObject("accountSummary");
|
||||
JSONObject rateLimitSummary = metadata == null ? null : metadata.optJSONObject("rateLimitSummary");
|
||||
return objectText(accountSummary, "authMode")
|
||||
+ " · 套餐 " + objectText(accountSummary, "planType")
|
||||
+ " · 额度 " + objectInt(rateLimitSummary, "maxUsedPercent") + "%";
|
||||
}
|
||||
|
||||
public static String deviceCodexThreadSummary(JSONObject device) {
|
||||
JSONObject summary = resolveCodexAppServerMetadataObject(device, "threadSummary");
|
||||
return objectInt(summary, "threadCount") + " 个"
|
||||
+ " · 已加载 " + objectInt(summary, "loadedThreadCount") + " 个"
|
||||
+ " · 活跃 " + objectInt(summary, "activeThreadCount") + " 个";
|
||||
}
|
||||
|
||||
public static String deviceCodexTurnSummary(JSONObject device) {
|
||||
JSONObject summary = resolveCodexAppServerMetadataObject(device, "threadTurnSummary");
|
||||
return objectInt(summary, "totalTurnCount") + " 个"
|
||||
+ " · 运行中 " + objectInt(summary, "runningTurnCount") + " 个"
|
||||
+ " · 完成 " + objectInt(summary, "completedTurnCount") + " 个";
|
||||
}
|
||||
|
||||
public static String deviceCodexThreadActionSummary(JSONObject device) {
|
||||
JSONObject summary = resolveCodexAppServerMetadataObject(device, "threadActionSummary");
|
||||
return objectInt(summary, "actionCount") + " 项"
|
||||
+ " · 生命周期 " + objectInt(summary, "lifecycleActionCount") + " 项"
|
||||
+ " · 活跃干预 " + objectInt(summary, "liveTurnActionCount") + " 项"
|
||||
+ " · " + (summary != null && summary.optBoolean("shellActionAvailable", false) ? "Shell 可用" : "Shell 不可用");
|
||||
}
|
||||
|
||||
public static String deviceCodexThreadCollaborationSummary(JSONObject device) {
|
||||
JSONObject summary = resolveCodexAppServerMetadataObject(device, "threadCollaborationSummary");
|
||||
if (summary == null) {
|
||||
@@ -528,11 +590,43 @@ public final class WechatSurfaceMapper {
|
||||
}
|
||||
|
||||
private static JSONObject resolveCodexAppServerMetadataObject(JSONObject device, String key) {
|
||||
JSONObject capability = resolveDeviceCapability(device, "codexAppServer");
|
||||
JSONObject metadata = capability == null ? null : capability.optJSONObject("metadata");
|
||||
JSONObject metadata = resolveCodexAppServerMetadata(device);
|
||||
return metadata == null ? null : metadata.optJSONObject(key);
|
||||
}
|
||||
|
||||
private static JSONObject resolveCodexAppServerMetadata(JSONObject device) {
|
||||
JSONObject capability = resolveDeviceCapability(device, "codexAppServer");
|
||||
return capability == null ? null : capability.optJSONObject("metadata");
|
||||
}
|
||||
|
||||
private static int metadataArrayLength(JSONObject metadata, String key) {
|
||||
if (metadata == null) {
|
||||
return 0;
|
||||
}
|
||||
JSONArray array = metadata.optJSONArray(key);
|
||||
return array == null ? 0 : array.length();
|
||||
}
|
||||
|
||||
private static String metadataText(JSONObject metadata, String key) {
|
||||
if (metadata == null) {
|
||||
return "未知";
|
||||
}
|
||||
String value = metadata.optString(key, "").trim();
|
||||
return value.isEmpty() ? "未知" : value;
|
||||
}
|
||||
|
||||
private static String objectText(JSONObject object, String key) {
|
||||
if (object == null) {
|
||||
return "未知";
|
||||
}
|
||||
String value = object.optString(key, "").trim();
|
||||
return value.isEmpty() ? "未知" : value;
|
||||
}
|
||||
|
||||
private static int objectInt(JSONObject object, String key) {
|
||||
return object == null ? 0 : object.optInt(key, 0);
|
||||
}
|
||||
|
||||
public static RootTopAction rootTopAction(String activeTab, boolean refreshing) {
|
||||
return rootTopAction(activeTab, refreshing, false);
|
||||
}
|
||||
|
||||
@@ -97,6 +97,20 @@ public class DeviceDetailActivityTest {
|
||||
View content = activity.findViewById(R.id.screen_content);
|
||||
assertTrue(viewTreeContainsText(content, "Codex App Server"));
|
||||
assertTrue(viewTreeContainsText(content, "已连接"));
|
||||
assertTrue(viewTreeContainsText(content, "模型"));
|
||||
assertTrue(viewTreeContainsText(content, "2 个 · 默认 gpt-5.4 · 快速 gpt-5.4-mini · 深度 gpt-5.4"));
|
||||
assertTrue(viewTreeContainsText(content, "扩展"));
|
||||
assertTrue(viewTreeContainsText(content, "Skill 1 个 · Plugin 1 个 · App 1 个"));
|
||||
assertTrue(viewTreeContainsText(content, "治理"));
|
||||
assertTrue(viewTreeContainsText(content, "实验特性 2 个 · 协作模式 2 个 · MCP 2 个 · 权限 1 个"));
|
||||
assertTrue(viewTreeContainsText(content, "账号"));
|
||||
assertTrue(viewTreeContainsText(content, "chatgpt · 套餐 pro · 额度 42%"));
|
||||
assertTrue(viewTreeContainsText(content, "线程"));
|
||||
assertTrue(viewTreeContainsText(content, "3 个 · 已加载 2 个 · 活跃 1 个"));
|
||||
assertTrue(viewTreeContainsText(content, "轮次"));
|
||||
assertTrue(viewTreeContainsText(content, "3 个 · 运行中 1 个 · 完成 2 个"));
|
||||
assertTrue(viewTreeContainsText(content, "线程操作"));
|
||||
assertTrue(viewTreeContainsText(content, "11 项 · 生命周期 5 项 · 活跃干预 2 项 · Shell 可用"));
|
||||
assertTrue(viewTreeContainsText(content, "线程协作"));
|
||||
assertTrue(viewTreeContainsText(content, "Boss Broker 可用 · 协作事件可处理 · 2 种模式 · 非原生私聊"));
|
||||
assertTrue(viewTreeContainsText(content, "协议漂移"));
|
||||
@@ -372,6 +386,42 @@ public class DeviceDetailActivityTest {
|
||||
.put("connected", true)
|
||||
.put("lastSeenAt", "2026-06-04T10:00:00+08:00")
|
||||
.put("metadata", new JSONObject()
|
||||
.put("models", new JSONArray()
|
||||
.put(new JSONObject().put("id", "gpt-5.4"))
|
||||
.put(new JSONObject().put("id", "gpt-5.4-mini")))
|
||||
.put("defaultModelId", "gpt-5.4")
|
||||
.put("fastModelId", "gpt-5.4-mini")
|
||||
.put("deepModelId", "gpt-5.4")
|
||||
.put("skills", new JSONArray().put(new JSONObject().put("name", "image2-ui-prototype")))
|
||||
.put("plugins", new JSONArray().put(new JSONObject().put("id", "github")))
|
||||
.put("apps", new JSONArray().put(new JSONObject().put("id", "canva")))
|
||||
.put("experimentalFeatures", new JSONArray()
|
||||
.put(new JSONObject().put("name", "multi_agent"))
|
||||
.put(new JSONObject().put("name", "apps")))
|
||||
.put("collaborationModes", new JSONArray()
|
||||
.put(new JSONObject().put("id", "solo"))
|
||||
.put(new JSONObject().put("id", "plan")))
|
||||
.put("mcpServers", new JSONArray()
|
||||
.put(new JSONObject().put("name", "github"))
|
||||
.put(new JSONObject().put("name", "figma")))
|
||||
.put("permissionProfiles", new JSONArray().put(new JSONObject().put("id", ":workspace")))
|
||||
.put("accountSummary", new JSONObject()
|
||||
.put("authMode", "chatgpt")
|
||||
.put("planType", "pro"))
|
||||
.put("rateLimitSummary", new JSONObject().put("maxUsedPercent", 42))
|
||||
.put("threadSummary", new JSONObject()
|
||||
.put("threadCount", 3)
|
||||
.put("loadedThreadCount", 2)
|
||||
.put("activeThreadCount", 1))
|
||||
.put("threadTurnSummary", new JSONObject()
|
||||
.put("totalTurnCount", 3)
|
||||
.put("runningTurnCount", 1)
|
||||
.put("completedTurnCount", 2))
|
||||
.put("threadActionSummary", new JSONObject()
|
||||
.put("actionCount", 11)
|
||||
.put("lifecycleActionCount", 5)
|
||||
.put("liveTurnActionCount", 2)
|
||||
.put("shellActionAvailable", true))
|
||||
.put("threadCollaborationSummary", new JSONObject()
|
||||
.put("bossBrokerAvailable", true)
|
||||
.put("collabToolCallHandlerAvailable", true)
|
||||
|
||||
Reference in New Issue
Block a user