import { spawn } from "node:child_process"; import { rm, stat } from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { DatabaseSync } from "node:sqlite"; const PERMISSION_DEFS = [ { key: "accessibility", label: "辅助功能", description: "用于点击、输入和读取可访问控件", tier: "core", }, { key: "screenRecording", label: "屏幕录制", description: "用于识别桌面画面和系统弹窗", tier: "core", }, ]; const MACOS_PERMISSION_SETTINGS = { core: "x-apple.systempreferences:com.apple.settings.PrivacySecurity.extension?Privacy_Accessibility", accessibility: "x-apple.systempreferences:com.apple.settings.PrivacySecurity.extension?Privacy_Accessibility", screenRecording: "x-apple.systempreferences:com.apple.settings.PrivacySecurity.extension?Privacy_ScreenCapture", }; const AUTO_PREFLIGHT_PERMISSION_KEYS = new Set(["accessibility", "screenRecording"]); const NATIVE_PERMISSION_QUERY_PARAMS = { accessibility: "nativeAccessibility", screenRecording: "nativeScreenRecording", }; const BOSS_AGENT_BUNDLE_ID = "com.hyzq.boss.agent"; const BOSS_COMPUTER_USE_HELPER_BUNDLE_ID = "site.hyzq.boss.computer-use-helper"; const BOSS_COMPUTER_USE_HELPER_APP_CANDIDATES = [ path.join(os.homedir(), "Applications/BossComputerUseHelper.app"), "/Applications/BossComputerUseHelper.app", ]; const HELPER_SCREEN_RECORDING_CACHE_TTL_MS = 30_000; let helperScreenRecordingCache = { status: "unknown", expiresAt: 0, }; const BOSS_AGENT_DEFAULTS_DOMAIN = "com.hyzq.boss.agent"; const TCC_PERMISSION_SERVICES = { kTCCServiceAccessibility: "accessibility", kTCCServiceScreenCapture: "screenRecording", }; const TCC_PERMISSION_CLIENTS = { kTCCServiceAccessibility: [BOSS_AGENT_BUNDLE_ID], kTCCServiceScreenCapture: [BOSS_AGENT_BUNDLE_ID, BOSS_COMPUTER_USE_HELPER_BUNDLE_ID], }; const TCC_PERMISSION_DATABASES = [ "/Library/Application Support/com.apple.TCC/TCC.db", path.join(os.homedir(), "Library/Application Support/com.apple.TCC/TCC.db"), ]; function nonEmpty(value) { const text = String(value ?? "").trim(); return text || undefined; } function escapeHtml(value) { return String(value ?? "") .replaceAll("&", "&") .replaceAll("<", "<") .replaceAll(">", ">") .replaceAll('"', """) .replaceAll("'", "'"); } function maskValue(value) { const text = nonEmpty(value); if (!text) return ""; if (text.length <= 8) return "••••"; return `${text.slice(0, 4)}••••${text.slice(-4)}`; } function sqlQuote(value) { return `'${String(value ?? "").replaceAll("'", "''")}'`; } function normalizePermissionStatus(value) { return value === "granted" || value === "missing" || value === "unknown" ? value : "unknown"; } function isPermissionStatus(value) { return value === "granted" || value === "missing" || value === "unknown"; } function statusTone(status) { if (status === "granted" || status === "valid" || status === "connected" || status === "bound") { return "good"; } if (status === "missing" || status === "expired" || status === "disconnected") { return "bad"; } return "warn"; } function formatDate(value) { const text = nonEmpty(value); if (!text) return "绑定后显示"; const date = new Date(text); if (Number.isNaN(date.getTime())) return text; return new Intl.DateTimeFormat("zh-CN", { year: "numeric", month: "2-digit", day: "2-digit", }).format(date); } function buildBindingPayload(config) { const controlPlaneUrl = nonEmpty(config.controlPlaneUrl) ?? "https://boss.hyzq.net"; const params = new URLSearchParams({ server: controlPlaneUrl, deviceId: nonEmpty(config.deviceId) ?? "local-device", name: nonEmpty(config.name) ?? "本机电脑", }); const pairingCode = nonEmpty(config.pairingCode); if (pairingCode) params.set("pairingCode", pairingCode); return `boss://agent-bind?${params.toString()}`; } function resolveApiUsage(config) { return { primary: nonEmpty(config.primaryApiLabel) ?? nonEmpty(config.apiUsage?.primary) ?? nonEmpty(config.masterAgentModel) ?? "由 Boss 后台统一配置", backup: nonEmpty(config.backupApiLabel) ?? nonEmpty(config.apiUsage?.backup) ?? "未启用", status: nonEmpty(config.apiUsage?.status) ?? (config.masterAgentEnabled === false ? "未启用主 Agent" : "可用"), }; } function resolveLicense(config, bound) { const license = config.license && typeof config.license === "object" ? config.license : {}; if (!bound) { return { status: "pending_binding", label: "未授权 · 绑定后校验", enterpriseName: "绑定后显示", expiresAt: nonEmpty(config.licenseExpiresAt) ?? "", expiresAtLabel: "绑定后显示", scope: "桌面控制 / 浏览器控制 / Skill 同步", }; } const status = nonEmpty(license.status) ?? "valid"; const expiresAt = nonEmpty(license.expiresAt) ?? nonEmpty(config.licenseExpiresAt) ?? ""; return { status, label: status === "valid" ? "正版授权正常" : status === "expired" ? "授权已过期" : "授权状态待确认", enterpriseName: nonEmpty(license.enterpriseName) ?? nonEmpty(config.enterpriseName) ?? "默认公司", expiresAt, expiresAtLabel: formatDate(expiresAt), scope: nonEmpty(license.scope) ?? "桌面控制 / 浏览器控制 / Skill 同步", }; } function normalizeCommandArgs(value, fallback = []) { if (!Array.isArray(value)) return [...fallback]; return value.map((item) => nonEmpty(item)).filter(Boolean); } function resolveBooleanWithDefault(value, defaultValue = false) { if (value === undefined || value === null || value === "") return defaultValue; if (value === false || value === "false" || value === "0" || value === 0) return false; return true; } function resolveCodexRemoteControl(config, appServerEnabled) { const enabled = resolveBooleanWithDefault(config.codexRemoteControlEnabled, appServerEnabled); const command = nonEmpty(config.codexRemoteControlCommand) ?? nonEmpty(config.codexAppServerCommand) ?? "codex"; const args = normalizeCommandArgs(config.codexRemoteControlArgs, ["remote-control", "start", "--json"]); const startCommandLabel = [command, ...args].join(" "); return { enabled, mode: enabled ? "managed_daemon" : "disabled", command, args, startCommandLabel, statusLabel: enabled ? "可托管启动" : "未启用", summary: enabled ? "Codex Remote Control 会通过 App Server daemon 提供官方远控入口;当前状态页只展示能力,不在刷新时自动启动。" : "Codex Remote Control daemon 未启用;远程控制会继续使用当前 App Server / Computer Use 配置。", }; } function resolveCodexBinding(config) { const appServerEnabled = config.codexAppServerEnabled === true; const codexComputerUseEnabled = config.codexComputerUseEnabled === true; const command = nonEmpty(config.codexAppServerCommand) ?? "codex"; const defaultDesktopProvider = codexComputerUseEnabled ? "codex-computer-use" : "cua-driver-computer-use"; const bindingStatus = appServerEnabled || codexComputerUseEnabled ? "connected" : "not_configured"; return { bindingStatus, statusLabel: bindingStatus === "connected" ? "已默认绑定" : "未默认绑定", command, appServerEnabled, computerUseEnabled: codexComputerUseEnabled, defaultDesktopProvider, desktopProviderLabel: defaultDesktopProvider === "codex-computer-use" ? "Codex Computer Use" : "Boss CUA Driver", fallbackProvider: "cua-driver-computer-use", fallbackLabel: "Boss CUA Driver", remoteControl: resolveCodexRemoteControl(config, appServerEnabled), summary: defaultDesktopProvider === "codex-computer-use" ? "远程控制默认走 Codex Computer Use,失败后回退 Boss CUA Driver。" : "远程控制默认走 Boss CUA Driver。", }; } function resolveAgentOta(config, runtime) { const enabledValue = config.bossAgentOtaEnabled; const enabled = enabledValue === undefined ? true : enabledValue !== false && enabledValue !== "false"; const currentVersion = nonEmpty(config.bossAgentVersion) ?? "dev"; const lastStatus = runtime.lastBossAgentOtaStatus && typeof runtime.lastBossAgentOtaStatus === "object" ? runtime.lastBossAgentOtaStatus : {}; const lastApply = runtime.lastBossAgentOtaApply && typeof runtime.lastBossAgentOtaApply === "object" ? runtime.lastBossAgentOtaApply : {}; const latest = lastStatus.latest && typeof lastStatus.latest === "object" ? lastStatus.latest : null; const hasUpdate = enabled && lastStatus.hasUpdate === true && Boolean(latest); const latestVersion = nonEmpty(latest?.version) ?? ""; const applyStatus = nonEmpty(lastApply.status) ?? ""; return { enabled, currentVersion, hasUpdate, latestVersion, latestFileName: nonEmpty(latest?.fileName) ?? "", latestUpdatedAt: nonEmpty(latest?.updatedAt) ?? "", lastCheckedAt: nonEmpty(lastStatus.checkedAt) ?? "", lastApplyStatus: applyStatus, lastApplyAt: nonEmpty(lastApply.completedAt) ?? "", statusLabel: !enabled ? "未启用" : hasUpdate ? "发现新版本" : lastStatus.error ? "检查失败" : "当前版本", detail: !enabled ? "boss-agent OTA 已关闭" : hasUpdate ? `最新:${latestVersion || "未知版本"}` : lastStatus.error ? String(lastStatus.error) : `当前:${currentVersion}`, }; } function permissionItems(defs, permissions) { return defs.map((item) => ({ ...item, status: normalizePermissionStatus(permissions[item.key]), })); } function resolvePermissionReadiness(coreItems, extendedItems) { const coreGrantedCount = coreItems.filter((item) => item.status === "granted").length; const extendedGrantedCount = extendedItems.filter((item) => item.status === "granted").length; const coreReady = coreGrantedCount === coreItems.length; const fullControlReady = coreReady && extendedGrantedCount === extendedItems.length; const summary = coreReady ? "基础桌面控制已可用" : "基础桌面控制待授权,桌面接管不可用"; return { coreReady, fullControlReady, coreGrantedCount, coreTotal: coreItems.length, extendedGrantedCount, extendedTotal: extendedItems.length, summary, detail: "参考 Codex Computer Use 的最小权限模型,boss-agent 只要求辅助功能和屏幕录制:辅助功能负责点击输入,屏幕录制可由 Boss Computer Use Helper 提供画面识别。", }; } function buildPermissionSetupPlan(coreItems, readiness) { const actions = coreItems.map((item) => ({ key: item.key, label: item.label, description: item.description, tier: item.tier, status: item.status, requiredForSilentControl: item.tier === "core", canPreflight: AUTO_PREFLIGHT_PERMISSION_KEYS.has(item.key), settingsUrl: MACOS_PERMISSION_SETTINGS[item.key] ?? MACOS_PERMISSION_SETTINGS.core, openUrl: `/api/v1/boss-agent/permissions/open?target=${encodeURIComponent(item.key)}&returnTab=permissions`, owner: "boss-agent.app", })); const missingRequiredActions = actions.filter( (action) => action.requiredForSilentControl && action.status !== "granted", ); return { mode: "minimal_computer_use", title: "基础桌面控制授权", goal: "按 Codex Computer Use 的思路,只申请辅助功能和屏幕录制两项最小权限。", silentUseReady: missingRequiredActions.length === 0, primaryAction: { label: "打开基础授权", href: "/api/v1/boss-agent/permissions/open?target=core&returnTab=permissions", settingsUrl: MACOS_PERMISSION_SETTINGS.core, }, actions, missingKeys: missingRequiredActions.map((action) => action.key), missingRequiredKeys: missingRequiredActions.map((action) => action.key), optionalMissingKeys: [], summary: readiness.coreReady ? "基础桌面控制已可用;后续控制只校验这两项权限。" : "仍缺少基础桌面控制权限,请先授权辅助功能和屏幕录制。", persistenceNote: "macOS 会把授权持久写入系统隐私数据库;稳定签名后,后续更新不会因为二进制哈希变化反复丢失授权。", }; } export function mergeBossAgentNativePermissionOverrides(permissions = {}, queryParams = {}) { const getQueryValue = (name) => { if (typeof queryParams.get === "function") return queryParams.get(name); return queryParams[name]; }; const merged = {}; for (const permissionKey of Object.keys(NATIVE_PERMISSION_QUERY_PARAMS)) { if (isPermissionStatus(permissions[permissionKey])) { merged[permissionKey] = permissions[permissionKey]; } } for (const [permissionKey, queryKey] of Object.entries(NATIVE_PERMISSION_QUERY_PARAMS)) { const value = getQueryValue(queryKey); if (isPermissionStatus(value)) { if (merged[permissionKey] === "granted" && value !== "granted") { continue; } merged[permissionKey] = value; } } return merged; } export function mergeBossAgentStoredNativePermissions(permissions = {}, storedPermissions = {}) { const merged = { ...permissions }; for (const permissionKey of Object.keys(NATIVE_PERMISSION_QUERY_PARAMS)) { if (storedPermissions[permissionKey] === "granted") { merged[permissionKey] = "granted"; } } return merged; } export function mergeBossComputerUseHelperScreenRecordingPermission(permissions = {}, helperStatus = "unknown") { if (helperStatus !== "granted") return { ...permissions }; return { ...permissions, screenRecording: "granted", }; } export function mergeBossAgentAppTccPermissions(permissions = {}, tccRows = "") { const merged = { ...permissions }; const granted = new Set(); for (const rawLine of String(tccRows ?? "").split(/\r?\n/)) { const line = rawLine.trim(); if (!line) continue; const parts = line.split("|"); const [service] = parts; const authValue = parts.length >= 3 ? parts[2] : parts[1]; const client = parts.length >= 3 ? parts[1] : BOSS_AGENT_BUNDLE_ID; const permissionKey = TCC_PERMISSION_SERVICES[service]; if (!permissionKey) continue; const allowedClients = TCC_PERMISSION_CLIENTS[service] ?? []; if (client && allowedClients.length > 0 && !allowedClients.includes(client)) continue; if (authValue === "2") { merged[permissionKey] = "granted"; granted.add(permissionKey); } else if (!granted.has(permissionKey) && authValue === "0") { merged[permissionKey] = "missing"; } } return merged; } function resolveSkills(runtime) { const rawSkills = Array.isArray(runtime.lastSkills) ? runtime.lastSkills : []; const items = rawSkills .map((item) => ({ name: nonEmpty(item?.name), category: nonEmpty(item?.category) ?? "本机", path: nonEmpty(item?.path) ?? "", description: nonEmpty(item?.description) ?? "", status: nonEmpty(item?.status) ?? "deployed", })) .filter((item) => item.name); const syncOk = runtime.lastSkillSyncOk === true; return { total: items.length, items: items.slice(0, 8), syncOk, syncStatus: syncOk ? "已同步" : items.length > 0 ? "待确认" : "未同步", syncAt: nonEmpty(runtime.lastSkillSyncAt) ?? "", summary: items.length > 0 ? `已部署 ${items.length} 个 Skill` : "本机暂无已同步 Skill", }; } export function buildBossAgentStatus(config = {}, runtime = {}, options = {}) { const now = nonEmpty(options.now) ?? new Date().toISOString(); const token = nonEmpty(runtime.issuedToken) ?? nonEmpty(config.token); const account = nonEmpty(config.account); const bound = Boolean(token && account); const permissions = options.permissions ?? {}; const serverOk = runtime.lastHeartbeatOk === true; const qrPayload = bound ? "" : buildBindingPayload(config); const corePermissionItems = permissionItems(PERMISSION_DEFS, permissions); const extendedPermissionItems = []; const permissionReadiness = resolvePermissionReadiness(corePermissionItems, extendedPermissionItems); const permissionSetup = buildPermissionSetupPlan(corePermissionItems, permissionReadiness); return { appName: "boss-agent", generatedAt: now, device: { id: nonEmpty(config.deviceId) ?? "local-device", name: nonEmpty(config.name) ?? os.hostname() ?? "本机电脑", avatar: nonEmpty(config.avatar) ?? "B", role: nonEmpty(config.deviceRole) ?? "企业被控节点", endpoint: nonEmpty(config.endpoint) ?? "mac://local", }, binding: { bound, status: bound ? "bound" : "unbound", account: bound ? account : "未绑定", tokenMasked: bound ? maskValue(token) : "", pairingCode: bound ? "" : nonEmpty(config.pairingCode) ?? "", qrPayload, qrExpiresInLabel: bound ? "" : nonEmpty(config.qrExpiresInLabel) ?? "二维码 04:58 后失效", }, server: { ok: serverOk, status: serverOk ? "connected" : "disconnected", endpoint: nonEmpty(config.controlPlaneUrl) ?? "未配置服务器", latencyLabel: nonEmpty(config.serverLatencyLabel) ?? (serverOk ? "延迟 28ms" : "连接异常"), lastHeartbeatAt: nonEmpty(runtime.lastHeartbeatAt) ?? "", lastHeartbeatStatus: runtime.lastHeartbeatStatus ?? null, }, api: resolveApiUsage(config), codex: resolveCodexBinding(config), agentOta: resolveAgentOta(config, runtime), license: resolveLicense(config, bound), permissions: { summary: permissionReadiness.summary, items: corePermissionItems, extendedItems: extendedPermissionItems, }, permissionReadiness, permissionSetup, skills: resolveSkills(runtime), }; } function renderPseudoQrSvg(payload) { const size = 25; let seed = 0; for (const char of String(payload || "boss-agent")) { seed = (seed * 31 + char.charCodeAt(0)) >>> 0; } const cells = []; const finder = (x, y) => x < 7 && y < 7 || x > 17 && y < 7 || x < 7 && y > 17; for (let y = 0; y < size; y += 1) { for (let x = 0; x < size; x += 1) { const finderCell = finder(x, y); const edge = x === 0 || y === 0 || x === size - 1 || y === size - 1; const value = finderCell ? !edge && (x % 6 === 1 || y % 6 === 1 || (x % 6 >= 2 && x % 6 <= 4 && y % 6 >= 2 && y % 6 <= 4)) : ((seed + x * 17 + y * 29 + x * y * 7) % 5) < 2; if (value) { cells.push(``); } } } return `${cells.join("")}`; } function permissionText(status) { if (status === "granted") return "已授权"; if (status === "missing") return "未授权"; return "待确认"; } function setupActionRows(status) { return status.permissionSetup.actions .map((action) => { const tone = statusTone(action.status); const preflight = action.requiredForSilentControl ? "基础必需" : action.canPreflight ? "按场景预触发" : "按场景启用"; return `
${escapeHtml(action.label)}
${escapeHtml(action.description)} · ${escapeHtml(preflight)}
${escapeHtml(permissionText(action.status))} 打开设置
`; }) .join(""); } function skillRows(status) { const skills = status.skills?.items ?? []; if (skills.length === 0) { return `
本机暂无已同步 Skill。绑定账号后可由 Boss 后台下发或同步本机 Codex Skill。
`; } return skills .map((skill) => { const pathLabel = skill.path ? skill.path.replace(os.homedir(), "~") : "未记录路径"; return `
${escapeHtml(skill.name)}
${escapeHtml(skill.category)} · ${escapeHtml(pathLabel)}
已部署
`; }) .join(""); } 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 function renderBossAgentHtml(status, options = {}) { return renderBossAgentHtmlBase(status, options); } export async function renderBossAgentHtmlWithQr(status, options = {}) { let qrImageDataUrl = ""; if (!status.binding.bound && status.binding.qrPayload) { try { const qrModule = await import("qrcode"); const toDataURL = qrModule.toDataURL ?? qrModule.default?.toDataURL; if (typeof toDataURL === "function") { qrImageDataUrl = await toDataURL(status.binding.qrPayload, { errorCorrectionLevel: "M", margin: 1, width: 288, color: { dark: "#18211a", light: "#ffffff", }, }); } } catch { 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)}
Codex 默认接管
${escapeHtml(status.codex.desktopProviderLabel)}
${escapeHtml(status.codex.summary)}
Codex Remote Control
${escapeHtml(status.codex.remoteControl.statusLabel)}
${escapeHtml(status.codex.remoteControl.startCommandLabel)}
boss-agent OTA
${escapeHtml(status.agentOta.statusLabel)}
${escapeHtml(status.agentOta.detail)}
服务器连接
${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)}

boss-agent OTA

当前版本${escapeHtml(status.agentOta.currentVersion)}
最新版本${escapeHtml(status.agentOta.latestVersion || "未发现新版本")}
升级状态${escapeHtml(status.agentOta.statusLabel)}
最近安装${escapeHtml(status.agentOta.lastApplyStatus || "暂无")}
Mac 端 OTA 会先下载并校验安装包,再拉起本机安装器;原配置会被保留,失败不会覆盖当前运行版本。
`; } 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 ? "本机 agent 正在接收企业控制台调度,可用于桌面控制、浏览器控制和 Skill 同步。" : "使用 Boss APP 扫码,将这台电脑加入企业账号。"; const qrBlock = bound ? `
${escapeHtml(status.device.avatar)}
` : `
${ options.qrImageDataUrl ? `Boss APP 绑定二维码` : renderPseudoQrSvg(status.binding.qrPayload) }
`; const viewModel = { bound, heroTitle, heroSubtitle, qrBlock }; return ` boss-agent
${renderBossAgentTabContent(status, activeTab, viewModel)}
`; } function runCommand(command, args, timeoutMs = 2500) { return new Promise((resolve) => { const child = spawn(command, args, { stdio: ["ignore", "pipe", "pipe"], }); let stdout = ""; let stderr = ""; const timer = setTimeout(() => { child.kill("SIGKILL"); }, timeoutMs); child.stdout.setEncoding("utf8"); child.stderr.setEncoding("utf8"); child.stdout.on("data", (chunk) => { stdout += chunk; }); child.stderr.on("data", (chunk) => { stderr += chunk; }); child.on("error", (error) => { clearTimeout(timer); resolve({ ok: false, stdout, stderr: error.message }); }); child.on("close", (code) => { clearTimeout(timer); resolve({ ok: code === 0, stdout: stdout.trim(), stderr: stderr.trim() }); }); }); } async function findBossComputerUseHelperApp() { for (const appPath of BOSS_COMPUTER_USE_HELPER_APP_CANDIDATES) { try { const info = await stat(appPath); if (info.isDirectory()) return appPath; } catch { // Try the next installation location. } } return ""; } async function detectBossComputerUseHelperScreenRecording() { const now = Date.now(); if (helperScreenRecordingCache.expiresAt > now) { return helperScreenRecordingCache.status; } const helperApp = await findBossComputerUseHelperApp(); if (!helperApp) { helperScreenRecordingCache = { status: "unknown", expiresAt: now + HELPER_SCREEN_RECORDING_CACHE_TTL_MS, }; return helperScreenRecordingCache.status; } const screenshotPath = path.join(os.tmpdir(), `boss-helper-screen-permission-${now}.png`); try { const result = await runCommand( "open", ["-W", "-na", helperApp, "--args", "screenshot", "--path", screenshotPath], 5000, ); if (result.ok) { const info = await stat(screenshotPath).catch(() => null); if (info?.size > 1024) { helperScreenRecordingCache = { status: "granted", expiresAt: now + HELPER_SCREEN_RECORDING_CACHE_TTL_MS, }; return helperScreenRecordingCache.status; } } } finally { await rm(screenshotPath, { force: true }).catch(() => {}); } helperScreenRecordingCache = { status: "missing", expiresAt: now + HELPER_SCREEN_RECORDING_CACHE_TTL_MS, }; return helperScreenRecordingCache.status; } function normalizePermissionTarget(target = "core") { return Object.hasOwn(MACOS_PERMISSION_SETTINGS, target) ? target : "core"; } export function resolveBossAgentPermissionSettingsUrl(target = "core") { return MACOS_PERMISSION_SETTINGS[normalizePermissionTarget(target)]; } export async function openBossAgentPermissionSettings(target = "core", platform = process.platform) { const normalizedTarget = normalizePermissionTarget(target); const settingsUrl = resolveBossAgentPermissionSettingsUrl(normalizedTarget); if (platform !== "darwin") { return { ok: false, target: normalizedTarget, settingsUrl, message: "当前平台暂不支持自动打开系统隐私设置,请在系统设置中手动完成授权。", }; } const nativeUrl = `boss-agent://permissions/open?target=${encodeURIComponent(normalizedTarget)}&returnTab=permissions`; const nativeLaunch = await runCommand( "open", [ "-na", "/Applications/boss-agent.app", "--args", "--request-permission", normalizedTarget, "--return-tab", "permissions", ], 2500, ); if (nativeLaunch.ok) { return { ok: true, target: normalizedTarget, settingsUrl, message: "已通过 boss-agent 发起系统权限申请。", nativeRequest: true, nativeUrl, }; } const nativeDeepLink = await runCommand("open", ["-b", "com.hyzq.boss.agent", nativeUrl], 2500); if (nativeDeepLink.ok) { return { ok: true, target: normalizedTarget, settingsUrl, message: "已通过 boss-agent 发起系统权限申请。", nativeRequest: true, nativeUrl, }; } const result = await runCommand("open", [settingsUrl], 2500); return { ok: result.ok, target: normalizedTarget, settingsUrl, message: result.ok ? "已打开系统权限设置。" : nativeLaunch.stderr || nativeLaunch.stdout || nativeDeepLink.stderr || nativeDeepLink.stdout || result.stderr || result.stdout || "打开系统权限设置失败。", nativeRequest: false, nativeUrl, }; } export async function detectLocalComputerPermissions(platform = process.platform) { if (platform !== "darwin") { return { accessibility: "unknown", screenRecording: "unknown", }; } const accessibility = await runCommand("osascript", [ "-e", 'tell application "System Events" to get UI elements enabled', ]); const screenshotPath = path.join(os.tmpdir(), `boss-agent-permission-${Date.now()}.png`); const screen = await runCommand("screencapture", ["-x", "-t", "png", screenshotPath], 3500); let screenRecording = "missing"; if (screen.ok) { try { const info = await stat(screenshotPath); screenRecording = info.size > 1024 ? "granted" : "unknown"; } catch { screenRecording = "unknown"; } finally { await rm(screenshotPath, { force: true }).catch(() => {}); } } const localProcessPermissions = { accessibility: accessibility.ok && /true/i.test(accessibility.stdout) ? "granted" : "missing", screenRecording, }; const helperPermissions = mergeBossComputerUseHelperScreenRecordingPermission( localProcessPermissions, screenRecording === "granted" ? "unknown" : await detectBossComputerUseHelperScreenRecording(), ); const appTccPermissions = mergeBossAgentAppTccPermissions(helperPermissions, await readBossAgentAppTccRows()); return mergeBossAgentStoredNativePermissions(appTccPermissions, await readBossAgentStoredNativePermissions()); } async function readBossAgentAppTccRows() { const outputs = []; for (const dbPath of TCC_PERMISSION_DATABASES) { try { await stat(dbPath); } catch { continue; } for (const service of Object.keys(TCC_PERMISSION_SERVICES)) { const clients = TCC_PERMISSION_CLIENTS[service] ?? [BOSS_AGENT_BUNDLE_ID]; const clientList = clients.map(sqlQuote).join(","); const query = [ "select service,client,auth_value from access", `where client in (${clientList})`, `and service='${service}'`, "order by auth_value desc", ].join(" "); try { const db = new DatabaseSync(dbPath, { readonly: true }); try { const rows = db.prepare(query).all(); if (rows.length > 0) { outputs.push(rows.map((row) => `${row.service}|${row.client}|${row.auth_value}`).join("\n")); } } finally { db.close(); } } catch { // Keep the command-line reader below as a second source of truth. } const result = await runCommand("/usr/bin/sqlite3", [dbPath, `${query};`], 2500); if (result.ok && result.stdout) { outputs.push(result.stdout); } } } return outputs.join("\n"); } async function readBossAgentStoredNativePermissions() { const entries = await Promise.all( Object.keys(NATIVE_PERMISSION_QUERY_PARAMS).map(async (permissionKey) => { const result = await runCommand( "/usr/bin/defaults", ["read", BOSS_AGENT_DEFAULTS_DOMAIN, `native.${permissionKey}`], 1500, ); return [permissionKey, result.ok ? normalizePermissionStatus(result.stdout) : "unknown"]; }), ); return Object.fromEntries(entries); }