From 8d3f68cebed5976febca0d6c172b3bd8d13fb8ee Mon Sep 17 00:00:00 2001 From: AI Bot Date: Tue, 12 May 2026 23:39:13 +0800 Subject: [PATCH] fix: split agent permission and skill tabs --- local-agent/boss-agent-status.mjs | 255 +++++++++++++++++++----------- local-agent/server.mjs | 4 +- tests/boss-agent-status.test.mjs | 59 ++++++- 3 files changed, 216 insertions(+), 102 deletions(-) diff --git a/local-agent/boss-agent-status.mjs b/local-agent/boss-agent-status.mjs index a92e633..6672b54 100644 --- a/local-agent/boss-agent-status.mjs +++ b/local-agent/boss-agent-status.mjs @@ -389,11 +389,18 @@ function skillRows(status) { .join(""); } -export function renderBossAgentHtml(status) { - return renderBossAgentHtmlBase(status); +const BOSS_AGENT_TABS = new Set(["overview", "permissions", "skills", "license", "logs"]); + +export function normalizeBossAgentTab(value = "overview") { + const tab = nonEmpty(value) ?? "overview"; + return BOSS_AGENT_TABS.has(tab) ? tab : "overview"; } -export async function renderBossAgentHtmlWithQr(status) { +export function renderBossAgentHtml(status, options = {}) { + return renderBossAgentHtmlBase(status, options); +} + +export async function renderBossAgentHtmlWithQr(status, options = {}) { let qrImageDataUrl = ""; if (!status.binding.bound && status.binding.qrPayload) { try { @@ -414,10 +421,158 @@ export async function renderBossAgentHtmlWithQr(status) { qrImageDataUrl = ""; } } - return renderBossAgentHtmlBase(status, { qrImageDataUrl }); + return renderBossAgentHtmlBase(status, { ...options, qrImageDataUrl }); +} + +function activeClass(activeTab, tab) { + return activeTab === tab ? ` class="active"` : ""; +} + +function renderBossAgentNav(status, activeTab) { + return ``; +} + +function renderTopbar(status, title = "boss-agent", subtitle = "企业电脑接入端") { + return `
+
+

${escapeHtml(title)}

+
${escapeHtml(subtitle)}
+
+
${escapeHtml(status.server.ok ? `已连接 ${status.server.endpoint}` : "服务器未连接")}
+
`; +} + +function renderOverviewTab(status, { bound, heroTitle, heroSubtitle, qrBlock }) { + return `${renderTopbar(status)} + +
+
+ ${qrBlock} +
+

${escapeHtml(heroTitle)}

+

${escapeHtml(heroSubtitle)}

+
+ + ${escapeHtml(bound ? `账号:${status.binding.account}` : status.binding.qrExpiresInLabel)} +
+
+
+
+

绑定状态

+
+
账号${escapeHtml(status.binding.account)}
+
设备${escapeHtml(status.device.name)}
+
角色${escapeHtml(status.device.role)}
+
设备 ID${escapeHtml(status.device.id)}
+
+
+
+ +
+
+
账号登录状态
+
${escapeHtml(bound ? "已绑定" : "等待绑定")}
+
${escapeHtml(bound ? status.binding.account : "使用 Boss APP 扫码完成绑定")}
+
+
+
当前 API 使用情况
+
${escapeHtml(status.api.primary)}
+
备用 API:${escapeHtml(status.api.backup)}
+
+
+
服务器连接
+
${escapeHtml(status.server.ok ? "正常" : "异常")}
+
${escapeHtml(status.server.latencyLabel)}
+
+
+
正版授权
+
${escapeHtml(status.license.label)}
+
到期:${escapeHtml(status.license.expiresAtLabel)}
+
+
+ +
+

授权信息

+
+
企业${escapeHtml(status.license.enterpriseName)}
+
授权到期${escapeHtml(status.license.expiresAtLabel)}
+
权限范围${escapeHtml(status.license.scope)}
+
+
`; +} + +function renderPermissionsTab(status) { + return `${renderTopbar(status, "本机权限获取", "一次性完成本机接管权限配置")} +
+
+
+

${escapeHtml(status.permissionSetup.title)}

+

${escapeHtml(status.permissionSetup.goal)} 权限结论:${escapeHtml(status.permissions.summary)}。当前状态:${escapeHtml(status.permissionSetup.summary)} 后续静默使用依赖系统持久授权。

+
+ ${escapeHtml(status.permissionSetup.primaryAction.label)} +
+
${setupActionRows(status)}
+
${escapeHtml(status.permissionSetup.persistenceNote)}
+
`; +} + +function renderSkillsTab(status) { + return `${renderTopbar(status, "Skill", "部署在本机并可被 Boss 调用的能力")} +
+

Skill 部署情况

+
${skillRows(status)}
+
Skill 用于把本机可复用能力分发给 Boss APP 和主 Agent;后续企业后台可按账号、设备和权限策略下发。
+
`; +} + +function renderLicenseTab(status) { + return `${renderTopbar(status, "绑定与授权", "账号、设备、服务器和正版授权状态")} +
+
+

绑定状态

+
+
账号${escapeHtml(status.binding.account)}
+
设备${escapeHtml(status.device.name)}
+
角色${escapeHtml(status.device.role)}
+
设备 ID${escapeHtml(status.device.id)}
+
+
+
+

授权信息

+
+
企业${escapeHtml(status.license.enterpriseName)}
+
授权到期${escapeHtml(status.license.expiresAtLabel)}
+
权限范围${escapeHtml(status.license.scope)}
+
+
${escapeHtml(status.permissionReadiness.detail)}
+
+
`; +} + +function renderLogsTab(status) { + return `${renderTopbar(status, "日志", "本机 agent 最近运行状态")} +
+

暂无日志面板

+
当前版本先保留入口;运行日志仍写入本机 LaunchAgent 日志和 Boss 后台事件。
+
`; +} + +function renderBossAgentTabContent(status, activeTab, viewModel) { + if (activeTab === "permissions") return renderPermissionsTab(status); + if (activeTab === "skills") return renderSkillsTab(status); + if (activeTab === "license") return renderLicenseTab(status); + if (activeTab === "logs") return renderLogsTab(status); + return renderOverviewTab(status, viewModel); } function renderBossAgentHtmlBase(status, options = {}) { + const activeTab = normalizeBossAgentTab(options.activeTab); const bound = status.binding.bound; const heroTitle = bound ? "这台电脑已接入 Boss" : "扫码绑定 Boss APP"; const heroSubtitle = bound @@ -430,6 +585,7 @@ function renderBossAgentHtmlBase(status, options = {}) { ? `Boss APP 绑定二维码` : renderPseudoQrSvg(status.binding.qrPayload) }`; + const viewModel = { bound, heroTitle, heroSubtitle, qrBlock }; return ` @@ -649,97 +805,10 @@ function renderBossAgentHtmlBase(status, options = {}) {
${escapeHtml(status.device.name)}
- + ${renderBossAgentNav(status, activeTab)}
-
-
-

boss-agent

-
企业电脑接入端
-
-
${escapeHtml(status.server.ok ? `已连接 ${status.server.endpoint}` : "服务器未连接")}
-
- -
-
- ${qrBlock} -
-

${escapeHtml(heroTitle)}

-

${escapeHtml(heroSubtitle)}

-
- - ${escapeHtml(bound ? `账号:${status.binding.account}` : status.binding.qrExpiresInLabel)} -
-
-
-
-

绑定状态

-
-
账号${escapeHtml(status.binding.account)}
-
设备${escapeHtml(status.device.name)}
-
角色${escapeHtml(status.device.role)}
-
设备 ID${escapeHtml(status.device.id)}
-
-
-
- -
-
-
账号登录状态
-
${escapeHtml(bound ? "已绑定" : "等待绑定")}
-
${escapeHtml(bound ? status.binding.account : "使用 Boss APP 扫码完成绑定")}
-
-
-
当前 API 使用情况
-
${escapeHtml(status.api.primary)}
-
备用 API:${escapeHtml(status.api.backup)}
-
-
-
服务器连接
-
${escapeHtml(status.server.ok ? "正常" : "异常")}
-
${escapeHtml(status.server.latencyLabel)}
-
-
-
正版授权
-
${escapeHtml(status.license.label)}
-
到期:${escapeHtml(status.license.expiresAtLabel)}
-
-
- -
-
-
-

${escapeHtml(status.permissionSetup.title)}

-

${escapeHtml(status.permissionSetup.goal)} 权限结论:${escapeHtml(status.permissions.summary)}。当前状态:${escapeHtml(status.permissionSetup.summary)} 后续静默使用依赖系统持久授权。

-
- ${escapeHtml(status.permissionSetup.primaryAction.label)} -
-
${setupActionRows(status)}
-
${escapeHtml(status.permissionSetup.persistenceNote)}
-
- -
-
-

授权信息

-
-
企业${escapeHtml(status.license.enterpriseName)}
-
授权到期${escapeHtml(status.license.expiresAtLabel)}
-
权限范围${escapeHtml(status.license.scope)}
-
-
${escapeHtml(status.permissionReadiness.detail)}
-
-
-

Skill 部署情况

-
${skillRows(status)}
-
Skill 用于把本机可复用能力分发给 Boss APP 和主 Agent;后续企业后台可按账号、设备和权限策略下发。
-
-
+ ${renderBossAgentTabContent(status, activeTab, viewModel)}
diff --git a/local-agent/server.mjs b/local-agent/server.mjs index 3a715de..4d398eb 100755 --- a/local-agent/server.mjs +++ b/local-agent/server.mjs @@ -41,6 +41,7 @@ import { import { buildBossAgentStatus, detectLocalComputerPermissions, + normalizeBossAgentTab, openBossAgentPermissionSettings, renderBossAgentHtmlWithQr, } from "./boss-agent-status.mjs"; @@ -1011,8 +1012,9 @@ const server = createServer(async (request, response) => { if (requestUrl.pathname === "/" || requestUrl.pathname === "/boss-agent") { const permissions = await detectLocalComputerPermissions(); const status = buildBossAgentStatus(config, runtime, { permissions }); + const activeTab = normalizeBossAgentTab(requestUrl.searchParams.get("tab") ?? "overview"); response.writeHead(200, { "Content-Type": "text/html; charset=utf-8" }); - response.end(await renderBossAgentHtmlWithQr(status)); + response.end(await renderBossAgentHtmlWithQr(status, { activeTab })); return; } diff --git a/tests/boss-agent-status.test.mjs b/tests/boss-agent-status.test.mjs index c2a1653..1141007 100644 --- a/tests/boss-agent-status.test.mjs +++ b/tests/boss-agent-status.test.mjs @@ -120,23 +120,66 @@ test("boss-agent status treats token-backed devices as bound and renders enterpr assert.match(html, /boss-agent/); assert.match(html, /企业电脑接入端/); assert.match(html, /本机权限获取/); - assert.match(html, /完整接管待补齐/); - assert.match(html, /一次完整授权/); - assert.match(html, /后续静默使用/); - assert.match(html, /href="#permissions">本机权限获取<\/span>/); - assert.match(html, /href="#skills">Skill<\/span>/); + assert.match(html, /href="\/boss-agent\?tab=permissions">本机权限获取<\/span>/); + assert.match(html, /href="\/boss-agent\?tab=skills">Skill<\/span>/); + assert.match(html, /class="active" href="\/boss-agent\?tab=overview"/); assert.doesNotMatch(html, /