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 `
+ 概览 ${activeTab === "overview" ? `当前 ` : ""}
+ 本机权限获取 ${escapeHtml(status.permissionReadiness.coreReady ? "OK" : "待")}
+ Skill ${escapeHtml(status.skills.total)}
+ 绑定与授权
+ 日志
+ `;
+}
+
+function renderTopbar(status, title = "boss-agent", subtitle = "企业电脑接入端") {
+ return ``;
+}
+
+function renderOverviewTab(status, { bound, heroTitle, heroSubtitle, qrBlock }) {
+ return `${renderTopbar(status)}
+
+
+
+ ${qrBlock}
+
+
${escapeHtml(heroTitle)}
+
${escapeHtml(heroSubtitle)}
+
+ ${escapeHtml(bound ? "查看绑定信息" : "刷新二维码")}
+ ${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, "本机权限获取", "一次性完成本机接管权限配置")}
+
+
+ ${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 = {}) {
? ` `
: renderPseudoQrSvg(status.binding.qrPayload)
}`;
+ const viewModel = { bound, heroTitle, heroSubtitle, qrBlock };
return `
@@ -649,97 +805,10 @@ function renderBossAgentHtmlBase(status, options = {}) {
${escapeHtml(status.device.name)}
-
- 概览 当前
- 本机权限获取 ${escapeHtml(status.permissionReadiness.coreReady ? "OK" : "待")}
- Skill ${escapeHtml(status.skills.total)}
- 绑定与授权
- 日志
-
+ ${renderBossAgentNav(status, activeTab)}
-
-
-
-
- ${qrBlock}
-
-
${escapeHtml(heroTitle)}
-
${escapeHtml(heroSubtitle)}
-
- ${escapeHtml(bound ? "查看绑定信息" : "刷新二维码")}
- ${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)}
-
-
-
-
-
- ${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, /