feat: ship enterprise control and desktop governance

This commit is contained in:
AI Bot
2026-05-11 14:59:26 +08:00
parent 0757d07521
commit a311280238
285 changed files with 48574 additions and 2428 deletions

View File

@@ -0,0 +1,75 @@
# Telegram Gateway Integration Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** 为 Boss 增加可用的 Telegram 对话入口,让 Telegram 用户能安全地与主 Agent 对话,并在主 Agent 异步完成任务后把结果回推回 Telegram。
**Architecture:**`src/lib` 新增一个轻量 Telegram gateway负责 update 归一化、访问控制、消息分流和 Telegram Bot API 调用Next.js 暴露 webhook 与管理员配置接口,仍然复用现有 `boss-master-agent``boss-data` 主链,不复制对话业务。异步回复依赖现有 `/api/v1/master-agent/tasks/[taskId]/complete` 完成回调,在任务落盘后立即尝试发回 Telegram。
**Tech Stack:** Next.js App Router、TypeScript、文件型状态 `data/boss-state.json`、原生 `fetch`、Node test runner + `tsx --test`
---
### Task 1: 定义 Telegram 状态与配置模型
**Files:**
- Create: `src/lib/telegram-gateway.ts`
- Modify: `src/lib/boss-data.ts`
- Test: `tests/telegram-gateway.test.ts`
- [ ] **Step 1: 写 Telegram 配置/状态的失败测试**
- [ ] **Step 2: 运行测试确认因接口缺失而失败**
- [ ] **Step 3: 在 `boss-data.ts` 增加 Telegram 配置与任务外部回推字段**
- [ ] **Step 4: 在 `telegram-gateway.ts` 增加归一化、mask、session key、chunk 等纯函数**
- [ ] **Step 5: 重新运行测试确认通过**
### Task 2: 打通 webhook 与主 Agent 桥接
**Files:**
- Create: `src/app/api/v1/integrations/telegram/webhook/route.ts`
- Modify: `src/lib/telegram-gateway.ts`
- Modify: `src/lib/boss-master-agent.ts`
- Test: `tests/telegram-gateway.test.ts`
- [ ] **Step 1: 写 webhook 接收、secret 校验、allowlist、主 Agent 快速回复 的失败测试**
- [ ] **Step 2: 跑测试确认 RED**
- [ ] **Step 3: 实现 webhook handler把 Telegram 文本桥接到 `master-agent`**
- [ ] **Step 4: 对快速回复直接回 Telegram对排队任务保存外部回推目标**
- [ ] **Step 5: 跑测试确认 GREEN**
### Task 3: 打通任务完成后的 Telegram 异步回推
**Files:**
- Modify: `src/lib/boss-data.ts`
- Modify: `src/app/api/v1/master-agent/tasks/[taskId]/complete/route.ts`
- Modify: `src/lib/telegram-gateway.ts`
- Test: `tests/telegram-gateway.test.ts`
- [ ] **Step 1: 写任务完成后自动回推 Telegram 的失败测试**
- [ ] **Step 2: 跑测试确认 RED**
- [ ] **Step 3: 在任务模型中加入 `externalReplyTarget` 并在 complete route 中触发 Telegram 发信**
- [ ] **Step 4: 补充发送成功/失败去重保护**
- [ ] **Step 5: 跑测试确认 GREEN**
### Task 4: 增加管理员配置接口
**Files:**
- Create: `src/app/api/v1/integrations/telegram/route.ts`
- Modify: `src/lib/telegram-gateway.ts`
- Test: `tests/telegram-integration-route.test.ts`
- [ ] **Step 1: 写 GET/POST 配置接口失败测试**
- [ ] **Step 2: 跑测试确认 RED**
- [ ] **Step 3: 实现管理员鉴权、配置读取、保存、token 掩码与 getMe 探测**
- [ ] **Step 4: 跑测试确认 GREEN**
### Task 5: 文档与回归验证
**Files:**
- Modify: `README.md`
- Modify: `docs/architecture/api_and_service_inventory_cn.md`
- Modify: `docs/architecture/current_runtime_and_deploy_status_cn.md`
- [ ] **Step 1: 更新 Boss 当前能力文档,写清 Telegram 接入方式、能力边界与部署方式**
- [ ] **Step 2: 运行 `tsx --test`、`npm run lint`、`npm run build`**
- [ ] **Step 3: 记录未完成项与后续扩展点群聊策略、pairing、Feishu/Telegram 复用层)**

View File

@@ -0,0 +1,274 @@
# Codex Desktop Thread Sync Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** 让 Boss App 发往单线程 Codex 会话的用户消息,在继续现有 `conversation_reply -> codex exec resume` 主链前,同步镜像进本机 Codex Desktop 的同一个线程历史。
**Architecture:** 服务端继续以 Boss 项目账本为主真相,但给普通单线程 `conversation_reply` 任务补齐 `sourceMessage*` 元数据和显式镜像开关。`local-agent` 在执行 `codex exec resume` 前,按 `targetCodexThreadRef` 解析目标 rollout 文件,先做一次本地 user_message append + 去重再继续原有执行链。rollout 定位优先使用 `state_5.sqlite`,若本机 Codex CLI/Desktop 版本导致状态库不可用,则回退扫描 `~/.codex/sessions`rollout 写入后尽量刷新 `threads.updated_at / updated_at_ms / has_user_event`,但不依赖 GUI 自动化。
**Tech Stack:** Next.js App Router, TypeScript, Node.js, sqlite, local-agent Node runtime, `tsx --test`, Node test runner
---
### Task 1: 给 conversation task 补齐 Desktop 镜像元数据
**Files:**
- Modify: `src/lib/boss-data.ts`
- Modify: `src/lib/boss-master-agent.ts`
- Modify: `src/app/api/v1/projects/[projectId]/messages/route.ts`
- Test: `tests/single-thread-message-execution.test.ts`
- [x] **Step 1: 写失败测试,要求普通单线程消息返回的任务带 sourceMessage 元数据**
```ts
assert.equal(task?.sourceMessageId, message.id);
assert.equal(task?.sourceMessageBody, "请同步一下当前阻塞情况");
assert.equal(task?.sourceMessageSentAt, message.sentAt);
assert.equal(task?.mirrorBossUserMessageToCodexDesktop, true);
```
- [x] **Step 2: 运行测试确认失败**
Run: `npx tsx --test tests/single-thread-message-execution.test.ts`
Expected: FAIL提示 `sourceMessageBody/sourceMessageSentAt/mirrorBossUserMessageToCodexDesktop` 不存在或断言失败。
- [x] **Step 3: 写最小实现**
`MasterAgentTask` 和状态序列化/反序列化里补字段:
```ts
sourceMessageId?: string;
sourceMessageBody?: string;
sourceMessageSentAt?: string;
mirrorBossUserMessageToCodexDesktop?: boolean;
```
`queueThreadConversationReplyTask` 中透传:
```ts
sourceMessageId: params.sourceMessageId,
sourceMessageBody: params.sourceMessageBody,
sourceMessageSentAt: params.sourceMessageSentAt,
mirrorBossUserMessageToCodexDesktop:
params.relayViaMasterAgent ? undefined : true,
```
在消息 route 调用时补:
```ts
const queuedTask = await queueThreadConversationReplyTask({
projectId,
requestMessageId: message.id,
requestText: message.body,
requestedBy: session.displayName || session.account,
requestedByAccount: session.account,
sourceMessageId: message.id,
sourceMessageBody: message.body,
sourceMessageSentAt: message.sentAt,
});
```
- [x] **Step 4: 运行测试确认通过**
Run: `npx tsx --test tests/single-thread-message-execution.test.ts`
Expected: PASS
### Task 2: 新增 rollout writer并确保重复任务不重复写 Desktop 线程
**Files:**
- Create: `local-agent/codex-thread-rollout-writer.mjs`
- Test: `tests/local-agent-codex-rollout-writer.test.mjs`
- [x] **Step 1: 写失败测试,约束 writer 会写入 user_message 且按 sourceMessageId 去重**
```js
test("appendBossUserMessageToCodexThreadRollout writes one user_message event and dedupes by source message id", async () => {
const first = await appendBossUserMessageToCodexThreadRollout({ ... });
const second = await appendBossUserMessageToCodexThreadRollout({ ... });
assert.equal(first.status, "written");
assert.equal(second.status, "duplicate");
});
```
- [x] **Step 2: 运行测试确认失败**
Run: `node --test tests/local-agent-codex-rollout-writer.test.mjs`
Expected: FAIL提示模块不存在或导出函数不存在。
- [x] **Step 3: 写最小实现**
实现 writer 逻辑:
```js
export async function appendBossUserMessageToCodexThreadRollout(params) {
const rolloutPath = await resolveThreadRolloutPath(params);
const duplicate = await hasBossSourceMessageInRolloutTail(rolloutPath, params.sourceMessageId);
if (duplicate) return { status: "duplicate", rolloutPath };
const responseItem = JSON.stringify({
timestamp: params.sentAt,
type: "response_item",
payload: {
type: "message",
role: "user",
content: [{ type: "input_text", text: params.message }],
},
});
const event = JSON.stringify({
timestamp: params.sentAt,
type: "event_msg",
payload: {
type: "user_message",
message: params.message,
images: [],
local_images: [],
text_elements: [],
metadata: {
bossSourceMessageId: params.sourceMessageId,
bossMirroredFrom: "boss-app",
},
},
});
await appendFile(rolloutPath, `${responseItem}\n${event}\n`, "utf8");
return { status: "written", rolloutPath };
}
```
- [x] **Step 4: 运行测试确认通过**
Run: `node --test tests/local-agent-codex-rollout-writer.test.mjs`
Expected: PASS
### Task 3: 在 local-agent 执行 resume 前追加 Desktop 线程镜像
**Files:**
- Modify: `local-agent/codex-task-runner.mjs`
- Modify: `tests/local-agent-codex-task-runner.test.mjs`
- [x] **Step 1: 写失败测试,要求 prepare 阶段保留镜像计划,且 relay task 不启用**
```js
assert.deepEqual(result.execution.desktopMirror, {
enabled: true,
sourceMessageId: "msg-1",
sourceMessageBody: "请继续推进",
});
```
- [x] **Step 2: 运行测试确认失败**
Run: `node --test tests/local-agent-codex-task-runner.test.mjs`
Expected: FAIL提示 `desktopMirror` 不存在。
- [x] **Step 3: 写最小实现**
`buildCodexTaskExecution` 返回值中增加:
```js
desktopMirror: shouldMirrorBossUserMessageToDesktop(task)
? {
enabled: true,
sourceMessageId: task.sourceMessageId,
sourceMessageBody: task.sourceMessageBody,
sourceMessageSentAt: task.sourceMessageSentAt,
targetThreadRef,
}
: { enabled: false }
```
其中 `shouldMirrorBossUserMessageToDesktop(task)` 需要保证:
- `task.taskType === "conversation_reply"`
- `task.mirrorBossUserMessageToCodexDesktop === true`
- `task.relayViaMasterAgent !== true`
- `targetThreadRef/sourceMessageId/sourceMessageBody` 全部存在
- [x] **Step 4: 运行测试确认通过**
Run: `node --test tests/local-agent-codex-task-runner.test.mjs`
Expected: PASS
### Task 4: 在实际任务执行前调用 rollout writer并保持现有 resume/complete 主链不回归
**Files:**
- Modify: `local-agent/server.mjs`
- Modify: `tests/local-agent-codex-task-runner.test.mjs`
- Modify: `tests/single-thread-message-execution.test.ts`
- [x] **Step 1: 写失败测试,要求 server 在 spawn codex 前先执行 rollout 镜像**
```js
assert.equal(writerCalls.length, 1);
assert.equal(writerCalls[0].sourceMessageId, "msg-1");
assert.equal(writerCalls[0].message, "请继续推进");
```
- [x] **Step 2: 运行测试确认失败**
Run: `node --test tests/local-agent-codex-task-runner.test.mjs`
Expected: FAIL提示 writer 未被调用。
- [x] **Step 3: 写最小实现**
`runMasterAgentTask``spawn("codex", ...)` 前增加:
```js
if (codexExecution.desktopMirror?.enabled) {
await appendBossUserMessageToCodexThreadRollout({
stateDbPath: config.codexStateDbPath,
targetThreadRef: codexExecution.desktopMirror.targetThreadRef,
sourceMessageId: codexExecution.desktopMirror.sourceMessageId,
message: codexExecution.desktopMirror.sourceMessageBody,
sentAt: codexExecution.desktopMirror.sourceMessageSentAt ?? new Date().toISOString(),
});
}
```
如果镜像失败:
- 不吞掉错误
- 直接按任务失败返回,让链路保持 fail-closed
- [x] **Step 4: 运行定向测试确认通过**
Run:
```bash
node --test tests/local-agent-codex-rollout-writer.test.mjs
node --test tests/local-agent-codex-task-runner.test.mjs
npx tsx --test tests/single-thread-message-execution.test.ts
```
Expected: PASS
### Task 5: 回归验证与文档同步
**Files:**
- Modify: `README.md`(如需补一句运行时说明)
- Modify: `docs/architecture/current_runtime_and_deploy_status_cn.md`
- Modify: `docs/architecture/api_and_service_inventory_cn.md`
- [x] **Step 1: 跑仓库要求的基线验证**
Run:
```bash
npm run lint
npm run build
```
Expected: PASS
- [x] **Step 2: 补文档**
在运行时/服务清单文档补一句:
- Boss 普通线程单聊现在会在 local-agent 执行 `codex exec resume` 前,把 Boss 用户消息镜像进目标 Codex Desktop 线程 rollout
- 该能力仅针对已绑定 `codexThreadRef` 的单线程会话
- [x] **Step 3: 完成最终自检**
检查:
- 没有把主 Agent 会话或 takeover relay 错写进 Desktop 子线程
- 没有重复写 rollout
- 现有 heartbeat 读取 recent desktop replies 仍可工作

View File

@@ -0,0 +1,176 @@
# Boss 统一电脑控制中枢 Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** 让 Boss 聊天成为统一电脑控制入口。用户在主 Agent 或线程聊天里提出需求后,系统能在“普通讨论 / Codex 开发 / 浏览器自动化 / 桌面控制”之间自动选路,并通过本机 `local-agent` 执行。
**Architecture:** 继续复用 Boss 现有消息账本、`MasterAgentTask` 队列、执行底座和 `local-agent`。本次新增 `browser_control / desktop_control` 两类正式任务与 runtime并让主 Agent 先做执行意图判断,再把请求路由到 Codex 线程、browser automation、computer use 或直接回复。
**Tech Stack:** Next.js App Router, TypeScript, Node.js local-agent, existing execution abstraction, `tsx --test`, Node test runner
---
### Task 1: 扩展执行类型与任务模型
**Files:**
- Modify: `src/lib/execution/types.ts`
- Modify: `src/lib/execution/tool-registry.ts`
- Modify: `src/lib/boss-data.ts`
- Test: `tests/computer-control-task-model.test.ts`
- [ ] **Step 1: 先写失败测试,要求任务模型支持 browser_control / desktop_control**
- [ ] **Step 2: 运行测试确认失败**
Run:
```bash
npx tsx --test tests/computer-control-task-model.test.ts
```
- [ ] **Step 3: 实现最小模型扩展**
补齐:
- `ExecutionRequestKind`
- `ExecutionToolName`
- `MasterAgentTaskType`
- `MasterAgentTask``intentCategory / runtimeKind / riskLevel / confirmationPolicy` 等字段
- [ ] **Step 4: 再跑测试确认通过**
### Task 2: 给主 Agent 增加控制意图分类
**Files:**
- Modify: `src/lib/boss-master-agent.ts`
- Modify: `src/app/api/v1/projects/[projectId]/messages/route.ts`
- Test: `tests/master-agent-control-intent-routing.test.ts`
- [ ] **Step 1: 先写失败测试,覆盖讨论 / 开发 / 浏览器 / 桌面四类消息**
- [ ] **Step 2: 运行测试确认失败**
Run:
```bash
npx tsx --test tests/master-agent-control-intent-routing.test.ts
```
- [ ] **Step 3: 实现最小路由逻辑**
要求:
- 讨论类继续直接回复
- 开发类仍优先走现有 `conversation_reply`
- 浏览器类排 `browser_control`
- 桌面类排 `desktop_control`
- [ ] **Step 4: 再跑测试确认通过**
### Task 3: 给 local-agent 增加 browser/computer use runtime 分流骨架
**Files:**
- Create: `local-agent/browser-control-task-runner.mjs`
- Create: `local-agent/computer-use-task-runner.mjs`
- Modify: `local-agent/server.mjs`
- Test: `tests/local-agent-browser-control-runner.test.mjs`
- Test: `tests/local-agent-computer-use-runner.test.mjs`
- [ ] **Step 1: 先写失败测试,要求 local-agent 能识别新任务类型并分流**
- [ ] **Step 2: 运行测试确认失败**
Run:
```bash
node --test tests/local-agent-browser-control-runner.test.mjs
node --test tests/local-agent-computer-use-runner.test.mjs
```
- [ ] **Step 3: 实现最小分流骨架**
第一版先做:
- browser_control: 返回标准化占位结果或接入现有 browser runtime
- desktop_control: 返回标准化占位结果或接入 computer-use runtime
要求:
- 不能影响现有 `conversation_reply / dispatch_execution`
- [ ] **Step 4: 再跑测试确认通过**
### Task 4: 增加风险分级与确认策略骨架
**Files:**
- Modify: `src/lib/execution/permission-policy.ts`
- Modify: `src/app/api/v1/projects/[projectId]/messages/route.ts`
- Test: `tests/computer-control-permission-policy.test.ts`
- [ ] **Step 1: 写失败测试,验证 low/medium/high 三档行为**
- [ ] **Step 2: 运行测试确认失败**
Run:
```bash
npx tsx --test tests/computer-control-permission-policy.test.ts
```
- [ ] **Step 3: 实现最小确认策略**
要求:
- `low` 默认可执行
- `medium` 标记需轻确认
- `high` 标记需强确认
- [ ] **Step 4: 再跑测试确认通过**
### Task 5: 前台返回执行模式元数据并做回归
**Files:**
- Modify: `src/app/api/v1/projects/[projectId]/messages/route.ts`
- Modify: `src/lib/boss-data.ts`
- Test: `tests/project-message-execution-mode.test.ts`
- [ ] **Step 1: 写失败测试,要求消息返回 executionMode/riskLevel**
- [ ] **Step 2: 运行测试确认失败**
- [ ] **Step 3: 实现最小返回结构**
- [ ] **Step 4: 再跑测试确认通过**
### Task 6: 基线验证与文档同步
**Files:**
- Modify: `README.md`
- Modify: `docs/architecture/current_runtime_and_deploy_status_cn.md`
- Modify: `docs/architecture/api_and_service_inventory_cn.md`
- [ ] **Step 1: 同步运行时文档**
- [ ] **Step 2: 跑仓库基线验证**
Run:
```bash
npm run lint
npm run build
```
- [ ] **Step 3: 跑新增与相关回归测试**
Run:
```bash
npx tsx --test tests/computer-control-task-model.test.ts
npx tsx --test tests/master-agent-control-intent-routing.test.ts
npx tsx --test tests/computer-control-permission-policy.test.ts
npx tsx --test tests/project-message-execution-mode.test.ts
node --test tests/local-agent-browser-control-runner.test.mjs
node --test tests/local-agent-computer-use-runner.test.mjs
```
- [ ] **Step 4: 如涉及 Android 前台行为,再补真机复核**

View File

@@ -0,0 +1,168 @@
# Boss Enterprise Hardening Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** 把 Boss 从 MVP 管理后台推进到 To B 可交付的第一批企业化硬化版本。
**Architecture:** 保留当前 `BossState` 文件账本作为兼容层,同时新增可切换的状态存储适配层、企业认证默认值、租户隔离守卫、风险 SLA 扫描和增强审计字段。所有高危行为先用测试锁住,再通过 `/admin``/api/v1/*` 渐进暴露。
**Tech Stack:** Next.js App Router、TypeScript、Node `node:test`、现有文件型状态、可选 PostgreSQL JSONB 单行快照、Ant Design 管理后台。
---
## 执行状态
- 2026-04-27已补齐认证安全默认值、状态存储适配层、租户强隔离、风险 SLA 通知账本、后台高危操作、增强审计字段和文档更新。
- 待最终收口:全量 `tests/*.test.ts``npm run lint``npm run build``npm audit` 和服务器 smoke。
### Task 1: 认证安全默认值
**Files:**
- Modify: `src/app/api/auth/login/route.ts`
- Modify: `src/app/auth/login/page.tsx`
- Modify: `src/components/app-ui.tsx`
- Test: `tests/auth-login-hardening-route.test.ts`
- [x] **Step 1: Write failing tests**
覆盖默认关闭免验证登录、显式 `BOSS_AUTH_AUTO_LOGIN=1` 才允许免验证、账号密码登录仍可用。
- [ ] **Step 2: Implement minimal auth hardening**
`shouldAllowTemporaryAutoLogin()` 改成只接受 `1 / true / yes`;登录页文案从“临时免验证”改成企业登录;固定验证码提示只在 fixed delivery 模式下展示。
- [ ] **Step 3: Verify**
Run: `npx tsx --test tests/auth-login-hardening-route.test.ts tests/auth-session-governance.test.ts`
### Task 2: 状态存储适配层
**Files:**
- Create: `src/lib/boss-state-store.ts`
- Modify: `src/lib/boss-data.ts`
- Create: `scripts/postgres-state-schema.sql`
- Test: `tests/boss-state-store.test.ts`
- [ ] **Step 1: Write failing tests**
覆盖默认 `file` 模式、`BOSS_STATE_STORE=postgres` 但无 `BOSS_DATABASE_URL` 时 fail closed、PostgreSQL SQL schema 包含 `boss_state_snapshots``jsonb`
- [ ] **Step 2: Implement adapter**
新增 `createBossStateStore()`默认文件读写PostgreSQL 模式先通过动态 `pg` 依赖实现单行 JSONB 快照,未安装或未配置时给出明确错误。
- [ ] **Step 3: Wire boss-data**
`readState/writeState/loadPersistedStateRaw` 通过 store 读写,保持 `mutateState` 事务队列不变。
### Task 3: 租户强隔离
**Files:**
- Modify: `src/lib/boss-permissions.ts`
- Modify: `src/lib/boss-admin-overview.ts`
- Test: `tests/rbac-tenant-isolation.test.ts`
- [ ] **Step 1: Write failing tests**
同公司账号能访问授权设备 / 项目;不同公司账号即使误授 grant 也不能访问;未绑定公司历史数据继续按 owner/grant 兼容。
- [ ] **Step 2: Implement tenant guard**
在非 `highest_admin` 路径中加入 `companyId` 比对。设备优先读 `device.companyId`,账号读 `authAccount.companyId`,项目通过绑定设备推导公司集合。
### Task 4: 风险 SLA 和通知账本
**Files:**
- Modify: `src/lib/boss-data.ts`
- Create: `src/lib/boss-risk-notifications.ts`
- Create: `src/app/api/v1/admin/risks/scan/route.ts`
- Modify: `src/components/admin/boss-admin-app.tsx`
- Test: `tests/admin-risk-sla-notifications-route.test.ts`
- [ ] **Step 1: Write failing tests**
设置过期 SLA 后扫描会生成通知;重复扫描不重复生成同一风险通知;通知会进入管理后台总览。
- [x] **Step 2: Implement notification model**
新增 `adminNotifications`,字段包含 `notificationId / kind / severity / companyId / riskId / title / body / status / createdAt / acknowledgedAt`
- [x] **Step 3: Implement scanner**
- [x] **Step 4: Implement dispatch and timeline**
新增 `/api/v1/admin/notifications/dispatch`,支持 sendmail 邮件通道或 disabled 模式状态落账;新增 `adminRiskTimeline` 记录通知生成、派发和人工处置。
扫描 `opsFaults``threadContextAlerts`,对 `slaDueAt < now` 且未关闭的风险生成 `risk_sla_overdue` 通知。
### Task 5: 后台高危操作补齐
**Files:**
- Modify: `src/lib/boss-data.ts`
- Modify: `src/app/api/v1/admin/access/route.ts`
- Modify: `src/components/admin/admin-access-panel.tsx`
- Test: `tests/admin-access-enterprise-ops-route.test.ts`
- [x] **Step 1: Write failing tests**
最高管理员可停用公司、重置子账号密码、批量导入预检;停用公司会禁用该公司普通子账号并撤销会话;最高管理员账号不可被公司停用波及。
- [x] **Step 2: Implement actions**
新增 `set_company_status / reset_account_password / preview_bulk_import_accounts` 三个 action并接到 PC 管理后台。
- [x] **Step 3: Implement enterprise UX polish**
补齐公司套餐、合同到期、客户成功、CSV 导入、危险操作确认和子账号 MFA 开关。
### Task 6: 增强审计字段
**Files:**
- Modify: `src/lib/boss-data.ts`
- Modify: admin action routes under `src/app/api/v1/admin/*`
- Test: `tests/admin-audit-compliance.test.ts`
- [x] **Step 1: Write failing tests**
高危动作写入 `ipAddress / userAgent / beforeJson / afterJson / requestId`API 响应不泄露 `passwordHash / apiKey / sessionToken / restoreToken`
- [x] **Step 2: Implement audit metadata**
扩展 `PermissionAuditLog`,新增 `buildRequestAuditMeta(request)`,所有 admin mutation route 传入审计上下文。
- [x] **Step 3: Implement auth hardening**
补齐浏览器 CSRF 基础防护、restore token 轮换和子账号 MFA 校验。
### Task 7: Docs, regression, deploy
**Files:**
- Modify: `docs/architecture/current_runtime_and_deploy_status_cn.md`
- Modify: `docs/architecture/api_and_service_inventory_cn.md`
- Modify: `docs/architecture/admin_refine_backoffice_cn.md`
- [x] **Step 1: Update docs**
记录企业登录默认值、PostgreSQL 切换方式、租户隔离规则、风险 SLA 扫描和后台高危操作。
- [ ] **Step 2: Full verification**
Run:
```bash
npx tsx --test tests/*.test.ts
npm run lint
npm run build
npm audit
```
- [ ] **Step 3: Deploy and smoke**
Run:
```bash
./scripts/deploy-server.sh
curl -fsS https://boss.hyzq.net/api/health
```
Post-deploy verify `/admin``/api/v1/admin/access``/api/v1/admin/overview``/api/v1/admin/risks/scan`

View File

@@ -0,0 +1,158 @@
# Boss Admin Backoffice Redesign Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Rebuild `/admin` into a PC To B operations backoffice with a dashboard, customer workspace, permission workspace, and risk/governance command center.
**Architecture:** Keep the existing Next.js App Router route and existing admin APIs. Refactor the current client shell into focused React components under `src/components/admin/`, reusing `/api/v1/admin/overview`, `/api/v1/admin/access`, `/api/v1/admin/risks/actions`, and `/api/v1/admin/skills/requests`.
**Tech Stack:** Next.js 16 App Router, React 19, Ant Design, `@refinedev/core`, TypeScript source tests with `node:test`.
---
### Task 1: Source Tests For New Admin Structure
**Files:**
- Modify: `tests/admin-refine-page.test.ts`
- [ ] **Step 1: Update the admin structure assertions**
Replace the old page shell expectations with assertions for these strings:
```ts
for (const title of ["平台运营驾驶舱", "客户与账号", "授权工作台", "风险与治理"]) {
assert.match(source, new RegExp(title));
}
for (const title of ["今日待处理", "客户健康排行", "关键风险队列", "节点健康"]) {
assert.match(source, new RegExp(title));
}
assert.doesNotMatch(source, /window\.prompt/);
```
- [ ] **Step 2: Run the focused test**
Run:
```bash
npx tsx --test tests/admin-refine-page.test.ts
```
Expected: fail until the component is refactored.
### Task 2: Admin Shell And Navigation
**Files:**
- Modify: `src/components/admin/boss-admin-app.tsx`
- [ ] **Step 1: Replace top tabs with PC backoffice navigation**
Implement a left-side navigation with four keys: `dashboard`, `customers`, `permissions`, `governance`.
- [ ] **Step 2: Keep Refine data provider mounted**
Keep:
```tsx
<Refine dataProvider={createBossAdminDataProvider(initialOverview ?? undefined)} resources={resources}>
```
Expected: existing data provider tests continue to pass.
### Task 3: Dashboard
**Files:**
- Modify: `src/components/admin/boss-admin-app.tsx`
- [ ] **Step 1: Build metric cards**
Use existing `summary`, `companies`, `devices`, `risks`, `notifications`.
- [ ] **Step 2: Build key queues**
Dashboard must include:
```tsx
"今日待处理"
"客户健康排行"
"关键风险队列"
"节点健康"
"最近事件"
```
Expected: no big full-width table as the first visual object.
### Task 4: Customer And Permission Workspaces
**Files:**
- Modify: `src/components/admin/boss-admin-app.tsx`
- Reuse: `src/components/admin/admin-access-panel.tsx`
- [ ] **Step 1: Add customer overview section**
Show company table, account table, and customer onboarding hints.
- [ ] **Step 2: Mount `AdminAccessPanel` under 授权工作台**
Keep the existing working mutation path, but wrap it in clearer page copy and narrower visual hierarchy.
### Task 5: Risk Command Center
**Files:**
- Modify: `src/components/admin/boss-admin-app.tsx`
- [ ] **Step 1: Replace prompt-based actions**
Remove `window.prompt` for assigning owner and SLA. Use controlled inline inputs and buttons.
- [ ] **Step 2: Keep existing risk actions**
Continue posting:
```ts
{ riskId, action: "assign_owner", ownerAccount }
{ riskId, action: "set_sla", slaDueAt }
{ riskId, action: "ack" }
{ riskId, action: "resolve" }
{ riskId, action: "create_repair_ticket" }
```
### Task 6: Skill Governance
**Files:**
- Modify: `src/components/admin/boss-admin-app.tsx`
- Reuse: `src/components/admin/admin-skill-lifecycle-panel.tsx`
- [ ] **Step 1: Move Skill lifecycle panel under 风险与治理**
Use a nested Ant Design `Tabs` with `风险战情室` and `Skill 生命周期`.
### Task 7: Verification
**Files:**
- Test: `tests/admin-refine-page.test.ts`
- Test: `tests/admin-overview-route.test.ts`
- Test: `tests/admin-risk-actions-route.test.ts`
- Test: `tests/admin-skill-lifecycle-panel-source.test.ts`
- [ ] **Step 1: Run focused admin tests**
```bash
npx tsx --test tests/admin-refine-page.test.ts tests/admin-overview-route.test.ts tests/admin-risk-actions-route.test.ts tests/admin-skill-lifecycle-panel-source.test.ts
```
Expected: all pass.
- [ ] **Step 2: Run lint/build**
```bash
npm run lint
npm run build
```
Expected: both pass.
## Self-review
- Spec coverage: covers dashboard, customer workspace, permission workspace, risk command center, Skill governance, testing.
- Placeholder scan: no TBD/TODO language.
- Type consistency: component names and existing endpoints match current code.

View File

@@ -0,0 +1,24 @@
# YuDao 风格企业后台独立化实施计划
日期2026-04-30
## 执行批次
第一批只做独立后台骨架和 BFF 契约,确保可以继续迭代而不影响现有 `/admin`
## 步骤
1. 新增 `/api/v1/admin/backoffice` 的测试,覆盖 `highest_admin` 鉴权、YuDao 风格菜单、租户/账号/角色/资源/风险/审计数据,以及敏感字段不泄露。
2. 新增 `apps/boss-admin-web` 源码测试,覆盖 Vue/Vite/Ant Design Vue 工程骨架、API 地址、登录态携带、核心页面文案和根工程隔离。
3. 实现 Admin BFF`buildAdminOverview(state)``BOSS_PERMISSION_TEMPLATES`、设备、项目、Skill、审计记录聚合为独立后台契约。
4. 搭建独立 Vue 后台工程提供工作台、租户、账号、角色权限、资源授权、Skill 中心、风险告警和审计日志页面骨架。
5. 修改根工程 `tsconfig.json``eslint.config.mjs`,避免尚未安装 Vue 依赖时影响 Next 主站构建。
6. 更新架构文档,说明独立后台、现有 `/admin` fallback、BFF 契约和后续部署方向。
7. 运行专项测试、lint 和 build如失败修到通过再交付。
## 成功标准
- `/api/v1/admin/backoffice` 可被测试调用并返回稳定结构。
- `apps/boss-admin-web` 具备可独立安装运行的 Vue/Vite 工程文件。
- 根工程 lint/build 不受新独立前端影响。
- 文档能说明为什么不整套引入 YuDao 后端,以及后续如何独立部署。

View File

@@ -0,0 +1,405 @@
# Desktop Dialog Guard Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Build a cross-platform dialog guard foundation so Boss desktop control can classify macOS and Windows popups, auto-handle safe prompts, and pause for user confirmation on risky prompts.
**Architecture:** Add a local-agent dialog policy engine with platform-neutral snapshots and signatures, expose `needs_user_action` from the computer-use runner, and wire the smoke runtime through the guard before executing desktop actions. macOS and Windows share policy/consent semantics while platform adapters provide snapshots through the same JSON shape.
**Tech Stack:** Node.js ESM, `node:test`, local-agent runtime scripts, Boss `desktop_control` payloads.
---
### Task 1: Policy Engine
**Files:**
- Create: `local-agent/desktop-dialog-guard.mjs`
- Test: `tests/local-agent-desktop-dialog-guard.test.mjs`
- [ ] **Step 1: Write failing tests**
```js
import test from "node:test";
import assert from "node:assert/strict";
import {
buildDialogInterventionResult,
createDialogSignature,
evaluateDialogSnapshot,
normalizeDialogSnapshot,
} from "../local-agent/desktop-dialog-guard.mjs";
test("dialog guard auto-handles safe welcome prompts on macOS and Windows", () => {
for (const platform of ["darwin", "win32"]) {
const decision = evaluateDialogSnapshot({
platform,
appName: platform === "darwin" ? "Google Chrome" : "Microsoft Edge",
title: "Welcome",
text: "Welcome. Not now",
buttons: ["Get started", "Not now"],
});
assert.equal(decision.disposition, "auto_action");
assert.equal(decision.action, "click_button");
assert.equal(decision.button, "Not now");
assert.equal(decision.risk, "safe");
}
});
test("dialog guard pauses for sensitive permission prompts", () => {
const decision = evaluateDialogSnapshot({
platform: "darwin",
appName: "System Settings",
title: "Screen Recording",
text: "BossComputerUseHelper would like to record this computer's screen",
buttons: ["Allow", "Don't Allow"],
});
assert.equal(decision.disposition, "needs_user_action");
assert.equal(decision.risk, "blocked");
assert.equal(decision.kind, "permission_required");
});
test("dialog guard generates stable signatures from normalized content", () => {
const a = createDialogSignature({
platform: "darwin",
deviceId: "macbook-air",
appBundleId: "com.google.Chrome",
title: " Welcome ",
text: "Not now",
buttons: ["Not now", "OK"],
});
const b = createDialogSignature({
platform: "darwin",
deviceId: "macbook-air",
appBundleId: "com.google.Chrome",
title: "Welcome",
text: " Not now ",
buttons: ["Not now", "OK"],
});
assert.equal(a.id, b.id);
assert.equal(a.scopeKey, "darwin:macbook-air:com.google.Chrome");
});
test("dialog guard emits app-safe intervention payload", () => {
const snapshot = normalizeDialogSnapshot({
platform: "win32",
deviceId: "win-node",
appName: "Installer",
title: "User Account Control",
text: "Do you want to allow this app to make changes to your device?",
buttons: ["Yes", "No"],
});
const decision = evaluateDialogSnapshot(snapshot);
const result = buildDialogInterventionResult({
requestId: "desktop-task-1",
snapshot,
decision,
});
assert.equal(result.status, "needs_user_action");
assert.equal(result.kind, "dialog_intervention_required");
assert.equal(result.risk, "blocked");
assert.deepEqual(result.availableActions, ["handled_on_device", "cancel_task"]);
assert.match(result.summary, /Installer/);
});
```
- [ ] **Step 2: Run test to verify it fails**
Run: `node --test tests/local-agent-desktop-dialog-guard.test.mjs`
Expected: FAIL because `local-agent/desktop-dialog-guard.mjs` does not exist.
- [ ] **Step 3: Implement minimal policy engine**
Create `local-agent/desktop-dialog-guard.mjs` with:
```js
import { createHash } from "node:crypto";
const SAFE_DISMISS_BUTTONS = ["稍后", "跳过", "以后再说", "Not now", "Skip", "Later", "Cancel"];
const BLOCKED_TEXT_PATTERNS = [
/screen recording/i,
/accessibility/i,
/input monitoring/i,
/full disk access/i,
/keychain/i,
/administrator/i,
/apple id/i,
/user account control/i,
/make changes to your device/i,
/屏幕录制/,
/辅助功能/,
/输入监控/,
/完整磁盘访问/,
/钥匙串/,
/管理员密码/,
];
export function normalizeDialogText(value) {
return String(value || "").replace(/\s+/g, " ").trim();
}
export function normalizeDialogSnapshot(input = {}) {
const buttons = Array.isArray(input.buttons)
? input.buttons.map(normalizeDialogText).filter(Boolean)
: [];
return {
platform: normalizeDialogText(input.platform || process.platform || "unknown"),
deviceId: normalizeDialogText(input.deviceId || "unknown-device"),
appName: normalizeDialogText(input.appName || input.app || "Unknown App"),
appBundleId: normalizeDialogText(input.appBundleId || input.appId || input.appName || input.app || "unknown-app"),
title: normalizeDialogText(input.title),
text: normalizeDialogText(input.text),
buttons,
};
}
function hash(value) {
return createHash("sha256").update(value).digest("hex").slice(0, 16);
}
export function createDialogSignature(snapshotInput = {}) {
const snapshot = normalizeDialogSnapshot(snapshotInput);
const titleHash = hash(snapshot.title.toLowerCase());
const textHash = hash(snapshot.text.toLowerCase());
const buttonHash = hash(snapshot.buttons.join("|").toLowerCase());
return {
id: hash([snapshot.platform, snapshot.deviceId, snapshot.appBundleId, titleHash, textHash, buttonHash].join("|")),
scopeKey: [snapshot.platform, snapshot.deviceId, snapshot.appBundleId].join(":"),
platform: snapshot.platform,
deviceId: snapshot.deviceId,
appBundleId: snapshot.appBundleId,
titleHash,
textHash,
buttonHash,
};
}
function findSafeDismissButton(buttons) {
return buttons.find((button) =>
SAFE_DISMISS_BUTTONS.some((candidate) => candidate.toLowerCase() === button.toLowerCase()),
);
}
function isBlockedPrompt(snapshot) {
const combined = `${snapshot.title} ${snapshot.text}`;
return BLOCKED_TEXT_PATTERNS.some((pattern) => pattern.test(combined));
}
export function evaluateDialogSnapshot(snapshotInput = {}) {
const snapshot = normalizeDialogSnapshot(snapshotInput);
const signature = createDialogSignature(snapshot);
if (isBlockedPrompt(snapshot)) {
return {
disposition: "needs_user_action",
kind: "permission_required",
risk: "blocked",
action: "pause_for_user",
signature,
};
}
const safeButton = findSafeDismissButton(snapshot.buttons);
if (safeButton) {
return {
disposition: "auto_action",
kind: "safe_dismiss",
risk: "safe",
action: "click_button",
button: safeButton,
signature,
};
}
return {
disposition: "needs_user_action",
kind: "unknown_dialog",
risk: "medium",
action: "pause_for_user",
signature,
};
}
export function buildDialogInterventionResult({ requestId, snapshot: snapshotInput, decision }) {
const snapshot = normalizeDialogSnapshot(snapshotInput);
const signature = decision?.signature || createDialogSignature(snapshot);
const blocked = decision?.risk === "blocked";
return {
status: "needs_user_action",
requestId: requestId || undefined,
kind: "dialog_intervention_required",
dialogId: signature.id,
risk: decision?.risk || "medium",
summary: `${snapshot.appName} 弹窗需要确认:${snapshot.title || snapshot.text || "未知弹窗"}`,
recommendedAction: blocked ? "handle_on_device" : "review",
availableActions: blocked
? ["handled_on_device", "cancel_task"]
: ["allow_once", "allow_for_device_dialog", "deny"],
platform: snapshot.platform,
appName: snapshot.appName,
};
}
```
- [ ] **Step 4: Run test to verify it passes**
Run: `node --test tests/local-agent-desktop-dialog-guard.test.mjs`
Expected: PASS.
### Task 2: Runner Result Support
**Files:**
- Modify: `local-agent/computer-use-task-runner.mjs`
- Test: `tests/local-agent-computer-use-runner.test.mjs`
- [ ] **Step 1: Write failing parser test**
Append this test:
```js
test("computer use runner parses dialog intervention runtime payload", () => {
const result = parseComputerUseTaskResult(
JSON.stringify({
status: "needs_user_action",
requestId: "desktop-task-dialog",
kind: "dialog_intervention_required",
dialogId: "dialog-1",
risk: "medium",
summary: "QQ 弹窗需要确认",
recommendedAction: "review",
availableActions: ["allow_once", "deny"],
platform: "darwin",
appName: "QQ",
}),
);
assert.equal(result.status, "needs_user_action");
assert.equal(result.requestId, "desktop-task-dialog");
assert.equal(result.kind, "dialog_intervention_required");
assert.equal(result.dialogId, "dialog-1");
assert.equal(result.risk, "medium");
assert.equal(result.summary, "QQ 弹窗需要确认");
assert.deepEqual(result.availableActions, ["allow_once", "deny"]);
});
```
- [ ] **Step 2: Run test to verify it fails**
Run: `node --test tests/local-agent-computer-use-runner.test.mjs`
Expected: FAIL because `needs_user_action` is not parsed.
- [ ] **Step 3: Implement parser branch**
Update `parseComputerUseTaskResult` so `status === "needs_user_action"` returns a structured object containing requestId, kind, dialogId, risk, summary, recommendedAction, availableActions, platform and appName.
- [ ] **Step 4: Run test to verify it passes**
Run: `node --test tests/local-agent-computer-use-runner.test.mjs`
Expected: PASS.
### Task 3: Runtime Guard Integration
**Files:**
- Modify: `scripts/computer-use-smoke.mjs`
- Test: `tests/browser-desktop-smoke-runtime-scripts.test.mjs`
- [ ] **Step 1: Write failing runtime tests**
Add tests that run `scripts/computer-use-smoke.mjs` with `BOSS_DIALOG_GUARD_ENABLED=true` and `BOSS_DIALOG_GUARD_SNAPSHOT_JSON`.
The safe test should assert the runtime completes and includes a `dialogGuard` artifact entry. The blocked test should assert the runtime returns `needs_user_action` before executing the desktop action.
- [ ] **Step 2: Run tests to verify failure**
Run: `node --test tests/browser-desktop-smoke-runtime-scripts.test.mjs`
Expected: FAIL because the smoke runtime ignores dialog guard env vars.
- [ ] **Step 3: Implement runtime preflight**
Import the dialog guard module, parse `BOSS_DIALOG_GUARD_ENABLED`, parse `BOSS_DIALOG_GUARD_SNAPSHOT_JSON`, evaluate it before desktop automation, return `needs_user_action` for pause decisions, and include auto-action audit info in artifacts for safe decisions.
- [ ] **Step 4: Run tests to verify pass**
Run: `node --test tests/browser-desktop-smoke-runtime-scripts.test.mjs`
Expected: PASS.
### Task 4: Config Defaults
**Files:**
- Modify: `local-agent/config.example.json`
- Modify: `local-agent/config.cloud.json`
- Test: `tests/browser-desktop-runtime-config-defaults.test.mjs`
- [ ] **Step 1: Write failing config test**
Extend the default config test to assert `dialogGuardEnabled`, `dialogGuardPlatformAdapters`, and `dialogGuardConsentRequired` are present.
- [ ] **Step 2: Run test to verify failure**
Run: `node --test tests/browser-desktop-runtime-config-defaults.test.mjs`
Expected: FAIL because the config keys are absent.
- [ ] **Step 3: Add defaults**
Add:
```json
"dialogGuardEnabled": true,
"dialogGuardConsentRequired": true,
"dialogGuardPlatformAdapters": ["darwin", "win32"]
```
- [ ] **Step 4: Run test to verify pass**
Run: `node --test tests/browser-desktop-runtime-config-defaults.test.mjs`
Expected: PASS.
### Task 5: Focused Verification
**Files:**
- No production files.
- [ ] **Step 1: Run focused tests**
Run:
```bash
node --test tests/local-agent-desktop-dialog-guard.test.mjs \
tests/local-agent-computer-use-runner.test.mjs \
tests/browser-desktop-smoke-runtime-scripts.test.mjs \
tests/browser-desktop-runtime-config-defaults.test.mjs
```
Expected: PASS.
- [ ] **Step 2: Run project checks**
Run:
```bash
npm run lint
npm run build
```
Expected: both PASS.
## Self-review
Spec coverage:
1. macOS and Windows are represented by platform-neutral snapshots and adapter-ready config keys.
2. Safe auto handling, sensitive pause, signatures, APP-friendly intervention payloads and audit artifacts are covered.
3. Full native AX/UIA helpers are intentionally deferred behind the adapter interface because this batch establishes the runtime contract first.
Placeholder scan: no unresolved placeholders.
Type consistency: `needs_user_action`, `dialogId`, `risk`, `summary`, `availableActions`, `platform`, and `appName` are consistent across plan tasks.

View File

@@ -0,0 +1,75 @@
# Ruflo Governance And Dialog Guard Completion Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Finish the next Boss control-plane layer: Desktop Dialog Guard user confirmation/audit, plus Ruflo-inspired task ownership, capability grouping, and device trust foundations.
**Architecture:** Keep Ruflo as a reference only, not a runtime dependency. Add small Boss-native modules and route contracts that fit the current file-backed state store, Android realtime channel, local-agent desktop runtime, and existing RBAC/audit patterns.
**Tech Stack:** Next.js route handlers, TypeScript state helpers, Node test runner/tsx tests, Android Java/Robolectric, Boss SSE events, local-agent runtime payloads.
---
## Task A: Dialog Guard Backend Completion
**Files:**
- Modify: `src/lib/boss-data.ts`
- Modify: `src/app/api/v1/master-agent/tasks/[taskId]/complete/route.ts`
- Create: `src/app/api/v1/dialog-guard/interventions/[interventionId]/decision/route.ts`
- Test: `tests/dialog-guard-interventions-route.test.ts`
**Steps:**
- [ ] Add a failing route test proving `status: "needs_user_action"` with `kind: "dialog_intervention_required"` creates a pending intervention and publishes `desktop.dialog_guard.intervention_required`.
- [ ] Add a failing route test proving a user decision updates the intervention, writes a permission audit log, and publishes `desktop.dialog_guard.intervention_resolved`.
- [ ] Add `DialogGuardIntervention` state shape and migration default.
- [ ] Extend the master-agent completion route to preserve `needs_user_action` instead of normalizing it to completed.
- [ ] Implement decision route with allowed decisions: `allow_once`, `allow_for_device_dialog`, `deny`, `handled_on_device`, `cancel_task`.
- [ ] Run the focused backend tests.
## Task B: Android Dialog Guard Confirmation UI
**Files:**
- Modify: `android/app/src/main/java/com/hyzq/boss/BossApiClient.java`
- Modify: `android/app/src/main/java/com/hyzq/boss/BossNotificationRouter.java`
- Modify: `android/app/src/main/java/com/hyzq/boss/ProjectDetailActivity.java`
- Modify: `android/app/src/main/java/com/hyzq/boss/BossUi.java`
- Test: `android/app/src/test/java/com/hyzq/boss/DialogGuardInterventionUiTest.java`
**Steps:**
- [ ] Add failing Robolectric test for handling `desktop.dialog_guard.intervention_required`.
- [ ] Render a compact confirmation card/dialog using the current Boss/微信-style visual system.
- [ ] For `blocked` risk, show only `我已在电脑上处理` and `取消任务`.
- [ ] For medium/safe risk, show `允许本次`, `当前设备此弹窗允许`, `拒绝` based on `availableActions`.
- [ ] Call `POST /api/v1/dialog-guard/interventions/{interventionId}/decision`.
- [ ] Remove/refresh the card on `desktop.dialog_guard.intervention_resolved`.
- [ ] Run the focused Android test.
## Task C: Ruflo-Inspired Governance Foundation
**Files:**
- Create: `src/lib/boss-work-claims.ts`
- Create: `src/lib/boss-capability-groups.ts`
- Create: `src/lib/boss-device-trust.ts`
- Test: `tests/ruflo-governance-foundation.test.ts`
**Steps:**
- [ ] Add failing tests for claim, handoff, stale claim detection, and stealable work.
- [ ] Add failing tests for grouped capabilities: computer control, Codex development, browser automation, skill operations, admin ops.
- [ ] Add failing tests for device trust tiers and budget/hop limit checks.
- [ ] Implement pure Boss-native modules without importing Ruflo.
- [ ] Keep the API persistence-ready but not UI-bound.
- [ ] Run the focused governance test.
## Integration Verification
- [ ] Run focused backend tests.
- [ ] Run focused Android test if local Gradle supports it.
- [ ] Run `npm run lint`.
- [ ] Run `npm run build`.
## Notes
Ruflo is used as architecture reference only. The Boss implementation must stay deterministic, auditable, RBAC-aware, and safe for multi-tenant enterprise deployment.

View File

@@ -0,0 +1,181 @@
# Codex Desktop 同线程消息镜像设计
目标:当用户在 Boss App 里对一个已绑定 `codexThreadRef` 的单线程会话发消息时,这条用户消息不仅进入 Boss 自己的项目账本和 `conversation_reply` 执行队列,也要被镜像进本机 Codex Desktop 的同一个线程历史里。这样用户稍后回到 Codex Desktop看见的是同一个线程下连续的聊天记录而不是 Boss 与 Desktop 两套割裂历史。
## 背景与现状
当前 Boss 的普通线程单聊主链是:
- Web / Android 调 `POST /api/v1/projects/[projectId]/messages`
- 服务端写入 Boss 项目消息账本
- 服务端排一个 `conversation_reply` 任务
- 本机 `local-agent` 认领任务后调用 `codex exec resume <targetCodexThreadRef>`
- Codex 线程完成后,再把线程回复回写到 Boss 项目账本
这条链现在已经能做到“Desktop 回复被 Boss 看见”,因为 heartbeat 扫描 `~/.codex/sessions/.../rollout-*.jsonl` 时,会把最近桌面 assistant 回复镜像回 Boss。缺口在反方向
- Boss App 发起的用户消息只存在于 Boss 项目账本
- `codex exec resume` 虽然会把 prompt 交给目标线程继续执行,但 Boss 发起的这条消息并不会先出现在 Desktop 线程历史
- 结果就是用户在 APP 和 Desktop 里看到的“同一个线程”并不是同一份完整聊天记录
## 方案对比
### 方案 1直接操控 Codex Desktop GUI 输入并发送
优点:
- 理论上最贴近“像用户在桌面端亲自发了一条消息”
缺点:
- 依赖窗口前台、焦点、输入法、系统权限
- 极易被 Codex Desktop UI 更新打断
- 无法稳定支持后台运行和多线程并发
不推荐作为主方案。
### 方案 2直接把 Boss 用户消息写入对应 Codex rollout JSONL再继续现有 `codex exec resume`
优点:
- 与当前 Desktop/CLI 共用的真实线程存储一致
- 不需要操控 GUI
- 可以保持现有 `local-agent -> codex exec resume` 主链不变
- 能与现有 heartbeat 读取 rollout 的能力形成闭环
缺点:
- 需要谨慎贴合 Codex rollout 事件格式
- 需要处理重复写入和 Desktop 刷新感知
这是本次推荐方案。
### 方案 3单独给 Desktop 再建一条镜像线程
优点:
- 对现有线程文件侵入最小
缺点:
- 用户要的是“同一个线程”,不是“另一个镜像线程”
- 历史会继续分叉
不满足目标。
## 本次设计
### 1. 保持 Boss 账本为移动端/UI 主真相
Boss 的项目消息账本、会话排序、未读数、主 Agent 协同逻辑继续基于现有 `boss-state.json`。这次不把 Boss UI 改成直接读取 `~/.codex`
### 2. 对单线程 `conversation_reply` 任务增加“写入 Desktop 线程历史”的镜像步骤
当任务满足以下条件时,在 `local-agent` 侧做一次 rollout 镜像:
- `task.taskType === "conversation_reply"`
- 存在 `targetCodexThreadRef`
- 存在用户原始消息文本
- 不属于 `relayViaMasterAgent === true` 的接管中转任务
镜像行为:
- 优先通过 `state_5.sqlite``threads.rollout_path` 定位目标 rollout 文件
- 如果本机 Codex 因版本/迁移差异无法稳定解析 `state_5.sqlite`,则回退扫描 `~/.codex/sessions/**/rollout-*-<threadId>.jsonl`
- 向该 rollout 文件追加一组 Codex 用户消息记录:`response_item / message(role=user)``event_msg / user_message`
- 事件内容使用 Boss 原始用户消息文本,而不是执行 prompt
- 事件时间优先使用 Boss 消息的 `sentAt`
- 事件写入成功后,再继续现有 `codex exec resume`
### 3. 任务负载补齐“Boss 原始消息”字段
现在任务里只有:
- `requestMessageId`
- `requestText`
- `executionPrompt`
这还不够稳,因为后续去重和 Desktop 镜像需要区分:
- 哪条 Boss 用户消息已经镜像过
- 这次镜像的真实显示文本是什么
- 这条消息的原始时间戳是什么
因此为 `MasterAgentTask` 增加:
- `sourceMessageId?: string`
- `sourceMessageBody?: string`
- `sourceMessageSentAt?: string`
- `mirrorBossUserMessageToCodexDesktop?: boolean`
对普通线程单聊:
- `sourceMessageId = message.id`
- `sourceMessageBody = message.body`
- `sourceMessageSentAt = message.sentAt`
- `mirrorBossUserMessageToCodexDesktop = true`
对主 Agent 直聊、`@主Agent`、托管中转等不应写进子线程 Desktop 历史的场景,不开启这个标记。
### 4. 去重策略
同一条 Boss 消息可能因为:
- 任务重试
- local-agent 重启
- claim / complete 重放
而被多次处理。为避免在 Desktop 线程里重复写入同一条用户消息本次采用“rollout 末尾去重”:
- 生成稳定镜像 key`boss-user:<threadRef>:<sourceMessageId>`
- 写入的 `event_msg` 中带上 `payload.metadata.bossSourceMessageId`
- 写入前读取 rollout 尾部固定窗口,检查最近是否已经存在同一 `bossSourceMessageId`
- 若存在,则跳过写入,仅继续 `codex exec resume`
这样不需要引入新的状态库,也能与 Codex 原始线程文件保持局部自洽。
### 5. 刷新感知
第一版只写 rollout 还不够稳,因为 Desktop 的线程列表排序和“最近活跃”判断通常还依赖 `threads.updated_at / updated_at_ms / has_user_event`。因此本次实现改为:
- rollout append 成功后,若 `state_5.sqlite` 可写且能命中该 thread则同步刷新
- `updated_at`
- `updated_at_ms`
- `has_user_event = 1`
- 如果当前机器上的 Codex 状态库不可用、字段不兼容或压根没有这条 thread 记录,则只保留 rollout 写入,不把整条消息链路判成失败
这样做的取舍是:
- 先保证 Boss -> Codex Desktop 同线程历史不丢
- 再尽可能提升 Desktop 侧的列表刷新和最近活跃感知
- 不引入 GUI 自动化,不依赖桌面窗口前台
## 涉及文件
- 新增 `local-agent/codex-thread-rollout-writer.mjs`
- 修改 `local-agent/codex-task-runner.mjs`
- 修改 `local-agent/server.mjs`
- 修改 `src/lib/boss-data.ts`
- 修改 `src/app/api/v1/projects/[projectId]/messages/route.ts`
- 修改 `src/lib/boss-master-agent.ts`(如果当前普通线程任务创建逻辑在这里有共用 helper也一起补齐
- 新增测试 `tests/local-agent-codex-rollout-writer.test.mjs`
- 修改测试 `tests/local-agent-codex-task-runner.test.mjs`
- 修改测试 `tests/single-thread-message-execution.test.ts`
## 边界
- 本次只处理“Boss App -> 已绑定 Codex Desktop 同线程”的用户消息镜像
- 不处理群聊镜像到 Desktop
- 不处理主 Agent 自己的回复写入 Desktop 子线程
- 不做 Codex Desktop GUI 自动输入
- 不把 Boss 会话列表直接改成读取 Desktop 原始线程文件
## 验收标准
- 普通单线程会话发消息后,生成的 `conversation_reply` 任务带有完整 `sourceMessage*` 字段
- local-agent 在执行 `codex exec resume` 前,能把这条 Boss 用户消息写进目标 rollout
- 同一 `sourceMessageId` 重试时不会重复写入 rollout
- 若状态库可用,镜像后会同步刷新 thread 的活跃时间和 `has_user_event`
- 若状态库不可用或这台机器上的线程索引不完整,仍可通过 `sessions` 回退找到 rollout 并完成消息镜像
- 现有普通线程回复链不回归Boss 仍能收到 Codex 线程回复
- 若目标线程缺失、只读或 cwd 不合法,仍保持现有 fail-closed 行为

View File

@@ -0,0 +1,399 @@
# Boss 聊天统一电脑控制中枢设计
目标:让用户在 Boss App 里,无论是和 `主 Agent` 对话,还是和某个具体线程对话,都可以稳定驱动这台 Mac/Windows 设备完成三类事情:
- 项目开发与代码执行
- 浏览器/桌面 GUI 操作
- 普通产品讨论、调研和任务协同
并且这三类能力不再割裂成几条旁路,而是统一挂在 Boss 现有的聊天、执行底座和设备心跳体系下面。
## 背景与现状
当前 Boss 已经具备几条关键基础链路:
- `master-agent` 单聊可以通过 `local-agent -> codex exec` 真实产出回复
- 普通单线程聊天已经可以排 `conversation_reply` 任务,并恢复到真实 Codex 线程执行
- 群聊已有 `group_dispatch_plan -> dispatch_execution` 的编排链
- 设备模型已经支持同一台机器的 `GUI + CLI` 双能力声明与默认执行模式切换
- 本机 `local-agent` 已能做 Codex 线程发现、task claim、task complete、Desktop rollout 镜像
但目前缺的不是“再加一个按钮”,而是统一控制中枢:
- `conversation_reply` 只适合“把消息转给 Codex 线程继续聊”
- `dispatch_execution` 主要面向群聊下发和线程编排
- 还没有正式的“桌面控制 / 浏览器控制”任务类型
- 主 Agent 也没有显式的能力路由模型,无法稳定判断当前消息应该走:
- Codex 开发
- Browser automation
- Computer Use
- 单纯讨论/总结
这会导致现在的体验像“能做一些事”,但还不是“可以靠 Boss 聊天控制电脑做事”。
## 目标边界
### 本次要达到的能力
1. 主 Agent 能把用户消息识别成四类执行意图:
- `project_development`
- `thread_collaboration`
- `browser_control`
- `desktop_control`
2. Boss 执行底座能显式表达这四类请求,并把它们路由到正确 runtime。
3. `local-agent` 增加两条新 runtime
- `browser-automation-runtime`
- `computer-use-runtime`
4. Web / Android 前台至少能拿到任务执行方式和当前状态,知道这条消息是:
- 交给 Codex 线程
- 交给浏览器自动化
- 交给桌面控制
- 仅由主 Agent 直接回复
5. 对高风险桌面动作建立最小确认机制,避免“发一句话就直接在电脑上乱点/乱删”。
### 本次不做的事情
- 不做完整远控桌面产品
- 不做视频流式屏幕回传
- 不做跨设备键鼠镜像
- 不把 Codex Desktop 自己纳入 Computer Use 自动点击目标
- 不依赖 GUI 自动化去操控 Codex 自己的窗口
## 方案原则
### 原则 1复用 Boss 现有执行底座,不另起一套“远控系统”
如果我们再单独造一层 `remote-control service`,会把:
- 会话账本
- 任务队列
- 权限与确认
- 前台状态展示
- 设备能力发现
全部再复制一遍。成本高,而且会和当前 Boss 的“聊天即控制入口”相冲突。
所以本次明确采用:
- 用户入口仍然是 Boss 聊天
- 任务记录仍然是 `MasterAgentTask`
- 路由仍然收敛进 `src/lib/execution`
- 执行仍然由绑定设备上的 `local-agent` 落地
### 原则 2先把“控制判断”标准化再扩 runtime
现在最大的问题不是工具不够,而是没有统一的“这条消息该怎么执行”判断结果。
因此要先补一层执行意图:
- `discussion_only`
- `thread_reply`
- `browser_control`
- `desktop_control`
- `development_execution`
然后让不同后端只关心自己该执行哪一种。
### 原则 3危险动作永远要显式分级
Boss 最终要能“做任何事”,但不能把“任何事”理解成“任何时候都自动执行”。
所以本次引入最小风险分级:
- `low`
- 打开页面
- 搜索信息
- 读取项目文件
- 运行只读检查
- `medium`
- 登录态网页操作
- 浏览器表单提交
- 桌面应用点击导航
- 修改非代码业务内容
- `high`
- 删除/覆盖文件
- 系统设置改动
- 批量提交/发布
- 不可逆外部操作
策略:
- `low`:默认直接执行
- `medium`:默认轻确认,可在项目/会话级放行
- `high`:必须明确确认
## 控制中枢设计
## 1. 新的执行意图模型
在当前 `ExecutionRequestKind` 基础上新增:
- `browser_control`
- `desktop_control`
并补充一个统一意图字段,供主 Agent 和前台共用:
- `intentCategory`
- `discussion_only`
- `project_development`
- `thread_collaboration`
- `browser_control`
- `desktop_control`
其中:
- `project_development` 继续优先走现有 Codex 线程 / CLI 执行链
- `thread_collaboration` 继续走 `conversation_reply`
- `browser_control` 新增浏览器自动化 runtime
- `desktop_control` 新增 Computer Use runtime
## 2. 新的 runtime 层
### 2.1 browser-automation-runtime
用途:
- 打开网页
- 登录指定后台
- 提交表单
- 抓取页面信息
- 复现 Web bug
第一版实现直接复用现有 Playwright 能力,不重新造驱动协议。
建议协议:
- 输入:
- `taskId`
- `projectId`
- `requestText`
- `executionPrompt`
- `targetUrl?`
- `riskLevel`
- 输出:
- `status`
- `replyBody`
- `structuredResult?`
- `artifacts?`
落地约束:
- `local-agent/browser-control-task-runner.mjs` 先收口成外部 runtime 桥,不把 Playwright 逻辑硬编码进 `server.mjs`
- 通过 `browserControlEnabled / browserControlCommand / browserControlArgs / browserControlWorkdir / browserControlTimeoutMs` 配置启用
- runtime 进程只需要遵守单行 JSON stdout 协议,后续可以平滑替换成真实 Playwright/OpenClaw/browser adapter
### 2.2 computer-use-runtime
用途:
- 打开本机应用
- 在桌面 GUI 上点击、输入、切换
- 配合浏览器外的桌面软件完成操作
第一版实现直接对接 Codex App 现有的 Computer Use 能力约束:
- 只能操作普通桌面应用
- 需要系统 Screen Recording + Accessibility
- 不把终端/Codex 自己当作自动点击目标
这意味着:
- 项目开发仍然优先走 Codex CLI/线程
- Computer Use 负责 GUI 世界
- 两者由主 Agent 在同一条聊天链里自动选择
落地约束:
- `local-agent/computer-use-task-runner.mjs` 同样先做成外部 runtime 桥
- 通过 `computerUseEnabled / computerUseCommand / computerUseArgs / computerUseWorkdir / computerUseTimeoutMs` 配置启用
- 先统一 Boss 与 runtime 的协议,再按设备情况接 Codex App Computer Use、OpenClaw 或其他 GUI runtime
## 3. MasterAgentTask 扩展
当前 `MasterAgentTaskType` 只有:
- `conversation_reply`
- `attachment_analysis`
- `group_dispatch_plan`
- `dispatch_execution`
- `device_import_resolution`
本次新增:
- `browser_control`
- `desktop_control`
新增字段:
- `intentCategory?`
- `runtimeKind?`
- `riskLevel?`
- `confirmationPolicy?`
- `requiresUserConfirmation?`
- `confirmationScopeKey?`
目的:
- 前台能展示“当前这条消息要走哪条执行链”
- 服务端能统一处理确认/拦截
- `local-agent` 能按 runtimeKind 正确分流
## 4. 主 Agent 路由逻辑
主 Agent 不再简单分成“自己答”或“排 conversation_reply”而是多一步意图判断。
推荐判断顺序:
1. 如果是明显的项目讨论、总结、目标/版本记录、普通问答
- `discussion_only`
2. 如果是“继续开发 / 改代码 / 跑测试 / 看项目状态 / 接手某线程”
- `project_development``thread_collaboration`
3. 如果是“打开网站 / 点网页 / 查后台 / 提交表单 / 看页面”
- `browser_control`
4. 如果是“打开电脑软件 / 操作桌面 / 系统 GUI / 非浏览器界面”
- `desktop_control`
路由结果:
- `discussion_only`
- 主 Agent 直接回复
- `thread_collaboration`
- 继续 `conversation_reply`
- `project_development`
- 优先真实 Codex 线程 / CLI
- `browser_control`
-`browser_control` 任务
- `desktop_control`
-`desktop_control` 任务
## 5. 设备能力模型
当前设备只有:
- `gui`
- `cli`
这对“统一控制电脑”不够精确,所以建议在设备 heartbeat 能力里细化为:
- `cli`
- `gui`
- `browserAutomation`
- `computerUse`
其中:
- `browserAutomation` 可由本机 Playwright/runtime 探测
- `computerUse` 由本机配置和权限状态探测
这样前台与主 Agent 都能知道:
- 当前机器只能写代码
- 还是也能控浏览器
- 还是能做完整桌面 GUI 操作
## 6. 前台产品表现
### 会话页 / 聊天页
每条“触发执行”的用户消息,服务端返回时增加:
- `executionMode`
- `discussion`
- `thread`
- `development`
- `browser`
- `desktop`
- `riskLevel`
- `requiresConfirmation`
前台展示原则:
- 不做厚重控制台 UI
- 保持当前微信式聊天界面
- 只在消息下方补一条轻状态:
- `已交给主 Agent`
- `正在调用浏览器自动化`
- `正在调用桌面控制`
- `等待你确认后执行`
### 会话信息 / 设备详情
补一个轻量能力区:
- `默认开发模式CLI / GUI`
- `浏览器自动化:可用 / 不可用`
- `桌面控制:可用 / 不可用`
不把这些塞回聊天主界面。
## 7. 风险确认设计
### 会话级别
如果当前会话在某个项目下已经对中风险动作做过一次确认,则可以对这个项目保留:
- `禁止`
- `允许本次`
- `当前项目永久放行`
这和现有 GUI/CLI 并行冲突的项目级策略一致,避免用户多学一套规则。
### 任务级别
当主 Agent 判断为高风险时:
- 不直接执行
- 先在聊天里给出极简确认卡
- 用户点确认后再排任务
## 8. 本次实施顺序
### 第一批
- 写设计与计划文档
- 扩展任务类型、执行请求类型、设备能力类型
- 接入 `browser_control / desktop_control` 两类任务基础骨架
- `local-agent` 增加 runtime 分流占位
- 前台返回 `executionMode/riskLevel` 元数据
### 第二批
- 接入 browser automation 真执行
- 接入 computer use 真执行
- 完成确认链
### 第三批
- Android/Web 前台补状态展示
- 真机回归
- 文档回写
## 涉及文件
- 修改 `src/lib/execution/types.ts`
- 修改 `src/lib/execution/tool-registry.ts`
- 修改 `src/lib/execution/permission-policy.ts`
- 修改 `src/lib/boss-data.ts`
- 修改 `src/lib/boss-master-agent.ts`
- 修改 `src/app/api/v1/projects/[projectId]/messages/route.ts`
- 修改 `local-agent/codex-task-runner.mjs`
- 修改 `local-agent/server.mjs`
- 新增 `local-agent/browser-control-task-runner.mjs`
- 新增 `local-agent/computer-use-task-runner.mjs`
- 新增对应 tests
## 验收标准
- 主 Agent 能把聊天输入稳定区分成讨论、开发、浏览器控制、桌面控制四类
- `browser_control / desktop_control` 能以正式任务进入 Boss 队列
- `local-agent` 能识别并分流这两类任务
- 前台能看到当前消息是走哪条执行链
- 中高风险动作不会静默直接执行
- 现有 `conversation_reply / dispatch_execution` 主链不回归

View File

@@ -0,0 +1,151 @@
# Boss To B 总后台重构设计
日期2026-04-30
## 背景
当前 `/admin` 已经具备最高管理员访问控制、总览聚合、账号授权、风险处理和 Skill 生命周期治理能力,但页面仍像“几个数据表拼在一起”。对于 To B 交付场景,平台侧需要的是一套能服务客户成功、运维值守和权限开通的 PC 总后台,而不是一个调试看板。
本次重构不新增大业务边界,优先重组现有 `/api/v1/admin/overview``/api/v1/admin/access``/api/v1/admin/risks/actions``/api/v1/admin/skills/requests` 数据和动作,把后台做成可用、可读、可处置的运营控制台。
## 目标
1. 最高管理员进入后台后,能在 10 秒内看出哪些客户、设备、主 Agent 任务或线程风险需要处理。
2. 客户开通从“多个分散表单”收口成可理解的工作台公司、老板账号、子账号、设备、项目、Skill 授权有清晰入口和状态。
3. 风险处理从“表格按钮”升级为战情室按严重程度、客户影响、负责人、SLA 和下一步动作组织。
4. Skill 治理保留安全约束,但展示成可追踪的生命周期队列。
5. UI 风格从移动端微信效率风改为 PC To B 管理后台:高密度、强层级、清晰状态、少装饰。
## 非目标
- 不引入新的 Umi / Ant Design Pro 工程。
- 不切换 PostgreSQL 或重写状态存储。
- 不改 Android APP 端交互。
- 不绕过 local-agent 的 Skill allowlist、checksum、备份和回滚约束。
- 不把客户侧 Web 控制台和平台总后台混成一个产品。
## 信息架构
后台改为 4 个一级区:
1. `驾驶舱`:平台全局健康、关键风险、客户影响、在线设备、主 Agent 失败、待处理通知。
2. `客户与账号`:公司列表、客户详情、账号开通、角色状态、登录与安全概览。
3. `授权工作台`设备、项目、Skill 授权,权限模板,过期授权,离职回收和审计。
4. `风险与治理`风险战情室、SLA、负责人、修复工单、风险时间线、Skill 生命周期请求。
现有 `账号与授权``Skill 治理` 不是删除,而是拆到更合理的上下文里:账号归客户,授权归权限,风险和 Skill 请求归治理。
## 页面设计
### 驾驶舱
顶部保留平台身份和刷新动作但标题从“Boss 管理后台”升级为“平台运营驾驶舱”。主区域按优先级展示:
- `今日待处理`:关键风险数、超 SLA 通知、离线设备、主 Agent 失败。
- `客户健康排行`:按开放风险、在线设备比例、合同/套餐状态排序。
- `关键风险队列`只展示最值得处理的风险提供负责人、SLA、确认、关闭、工单动作。
- `设备与节点健康`GUI/CLI、Browser、Computer Use 能力状态集中展示。
- `最近事件`:风险时间线和权限审计摘要。
驾驶舱默认不展示大分页表,避免用户一打开就被表格淹没。
### 客户与账号
采用左侧客户列表 + 右侧详情的结构:
- 客户列表显示公司名、套餐、账号数、设备数、开放风险、客户成功负责人。
- 右侧详情显示老板账号、子账号、绑定设备、项目数量和最近风险。
- 新建客户流程拆成三步:创建公司、创建老板账号、绑定设备/项目。
- 子账号管理支持启用/停用、重置密码、MFA 状态和登录会话摘要。
这部分复用现有 `/api/v1/admin/access`,但前台从表单堆叠改成任务流。
### 授权工作台
授权页面按“给谁授权”而不是“授权类型”组织:
- 先选择账号或客户。
- 再选择设备、项目、Skill。
- 最后套用权限模板或手动勾选权限。
页面底部保留最近授权审计和过期授权提醒。高危动作继续二次确认。
### 风险与治理
风险页面采用“战情室”结构:
- 左侧风险队列:按 `critical / warning / info`、客户、负责人、SLA 筛选。
- 中间风险详情:影响对象、错误摘要、最近时间线、建议动作。
- 右侧处理面板:指派、设置 SLA、确认、关闭、创建工单。
Skill 生命周期治理放在同一区域的第二页签,展示为请求队列:
- 待认领、执行中、成功、失败分栏。
- 每条请求展示设备、Skill、动作、来源、checksum、结果摘要。
- 创建请求表单保留,但根据动作动态收敛字段。
## 组件边界
建议拆出以下组件,降低当前 `boss-admin-app.tsx` 的复杂度:
- `AdminShell`PC 后台壳、顶部栏、一级导航。
- `AdminDashboard`:驾驶舱。
- `AdminCustomerWorkspace`:客户与账号工作台。
- `AdminPermissionWorkspace`:授权工作台。
- `AdminRiskCommandCenter`:风险战情室。
- `AdminSkillGovernance`Skill 生命周期治理,可复用并改造当前组件。
- `AdminStatusBadge``AdminMetricCard``AdminActionRail`:统一状态、指标和动作区。
数据请求先继续使用现有 fetch不强制引入新的客户端状态库。
## 数据和接口
第一批不要求新增后端字段,但前台应完整使用现有字段:
- `summary`
- `companies`
- `accounts`
- `devices`
- `risks`
- `notifications`
- `riskTimeline`
- `grantsSummary`
如果发现页面需要客户健康分数,可先在前端由 `openRiskCount / onlineDeviceCount / deviceCount / status` 计算,不改状态 schema。
## 错误处理
- 后台总览读取失败时展示一张明确的恢复卡,提供重试按钮。
- 风险动作失败时保留原行状态,不做乐观关闭。
- 指派负责人和 SLA 不再使用 `window.prompt`,改成右侧处理面板或弹窗表单。
- 空状态要表达下一步,例如“暂无风险,可以查看设备在线情况”,不要只写“暂无数据”。
## 测试策略
- 保留并更新 `tests/admin-refine-page.test.ts`,验证新的一级区和关键文案。
- 增加组件 source 测试,确认不再使用 `window.prompt` 做风险指派和 SLA。
- 复跑 `tests/admin-overview-route.test.ts``tests/admin-risk-actions-route.test.ts``tests/admin-skill-lifecycle-panel-source.test.ts`
- 最后跑 `npm run lint` 和相关 Node 测试。
## 分批落地
第一批直接做到可用:
1. 重构 `BossAdminApp` 外壳和一级导航。
2. 做新版驾驶舱。
3. 做风险战情室,替换 `window.prompt`
4. 账号授权和 Skill 治理先迁入新结构,并压缩视觉层级。
第二批再增强:
1. 客户详情抽屉。
2. 新建客户三步流程。
3. 风险筛选和搜索。
4. 客户健康分数和趋势。
## 自检
- 无 TBD / TODO。
- 范围聚焦 `/admin` PC 总后台,不触碰 APP。
- 没有要求新增大后端能力,优先复用现有接口。
- 关键交互从数据表改成工作台与战情室,解决“后台管理不太好”的主要问题。

View File

@@ -0,0 +1,75 @@
# YuDao 风格企业后台独立化设计
日期2026-04-30
## 背景
Boss 需要从“客户也能用的 Web 页面”升级为平台侧 To B 总后台。这个后台用于平台运营人员管理公司、老板账号、子账号、电脑节点、Skill 授权、风险告警和审计记录。现有 `/admin` 已能展示核心数据,但仍运行在 Next 主站内,信息架构不够像成熟企业后台,后续不适合承载更复杂的租户、权限和治理能力。
调研 `YunaiV/yudao-cloud` 后,结论是:不直接引入它的 Spring Cloud 微服务后端;借鉴它的租户、用户、角色、菜单、日志、工作台和独立前端思路。前端形态参考 YuDao 的 Vben/Vue 管理后台,数据仍由 Boss 现有状态账本和 Admin BFF 提供。
## 目标
第一批目标是完成企业后台独立化的可运行骨架:
- 新增独立 PC 后台工程 `apps/boss-admin-web`,使用 Vue + Vite + Ant Design Vue。
- 新增 `/api/v1/admin/backoffice` 聚合接口,输出 YuDao 风格的菜单、工作台、租户、账号、角色权限、资源授权、风险和审计数据。
- 保留现有 `/admin`,作为 Boss 主站内 fallback不和独立后台互相替代。
- 后台权限继续只允许 `highest_admin` 访问不暴露密码哈希、MFA 密钥和会话令牌。
- 新后台先复用 Boss Cookie 登录态,后续再接独立域名 `admin.boss.hyzq.net`
## 非目标
- 不引入 YuDao Java 后端、MySQL 表结构或微服务网关。
- 不在第一批替换所有现有后台 mutation 页面。
- 不重新设计 Android APP。
- 不改变当前 Boss 文件存储运行时。
## 架构
```mermaid
flowchart LR
A["Boss Admin Web\nVue + Ant Design Vue"] --> B["/api/v1/admin/backoffice\nAdmin BFF"]
B --> C["boss-state.json\n当前状态账本"]
B --> D["buildAdminOverview\n现有后台聚合"]
B --> E["BOSS_PERMISSION_TEMPLATES\n权限模板"]
F["现有 /admin\nNext fallback"] --> G["/api/v1/admin/overview"]
```
`apps/boss-admin-web` 是独立前端工程。它只消费 BFF不直接读取本地文件也不复制业务规则。`/api/v1/admin/backoffice` 是企业后台的新契约层,负责把 Boss 当前状态翻译为更稳定的后台管理模型。
## 数据模型
第一批 BFF 返回:
- `menuTree`工作台、租户管理、账号管理、角色权限、资源授权、Skill 中心、风险告警、审计日志、系统设置。
- `workbench`:总览指标、客户健康、设备健康、风险摘要。
- `tenants`:公司列表、套餐、负责人、账号数、设备数、风险数。
- `users`:账号、昵称、角色、状态、公司、最近登录。
- `roles`:内置角色和权限模板。
- `resourceGroups`:设备、项目线程和 Skill 目录。
- `audit`:风险、风险时间线和权限审计。
- `yudaoMapping`Boss 账本字段到后台概念的映射,便于后续迁移数据库或接 YuDao 风格模块。
## UI 方向
第一批 UI 只做高保真骨架,不新增业务动作:
- 左侧固定菜单,右侧工作区。
- 顶部展示当前账号、后台说明和刷新入口。
- 工作台使用指标卡、风险横幅、客户健康和节点表。
- 租户、账号、角色、资源、风险、审计分别使用独立区块或表格。
- Skill 中心聚合展示 Skill 目录、来源、设备数和治理状态,后续再接完整安装向导。
## 权限与安全
- 未登录返回 `401`
-`highest_admin` 返回 `403`
- BFF 只返回安全账号字段,不返回 `passwordHash``mfaSecret``authSessions` 或任何 session token。
- 所有返回头使用 `private, no-store`,避免后台数据被缓存。
## 验证
- 新增 BFF 路由测试,验证鉴权、菜单结构、数据聚合和敏感字段过滤。
- 新增独立前端源代码测试验证工程骨架、API 契约、核心页面模块和根工程隔离。
-`npm run lint``npm run build`,确认不会破坏现有 Next 主站。