feat(client): project managed services to digital employee

This commit is contained in:
baiyanyun
2026-06-08 11:40:03 +08:00
parent 8dc9ce73b9
commit 710bf72353
9 changed files with 406 additions and 146 deletions

View File

@@ -10,6 +10,7 @@ import type {
DebugTask,
DigitalEmployeeDecision,
DigitalEmployeeDuty,
DigitalEmployeeManagedService,
DigitalEmployeeRunDetail,
DigitalEmployeeTaskExecutionSummary,
DigitalEmployeeWorkEvent,
@@ -117,6 +118,7 @@ interface QimingclawSyncFailure {
interface QimingclawSnapshot {
generatedAt: string;
services: Record<string, QimingclawServiceStatus | null>;
managedServices?: QimingclawManagedService[] | null;
sessions: {
total: number;
active: number;
@@ -128,6 +130,22 @@ interface QimingclawSnapshot {
planTemplates?: QimingclawPlanTemplateRecord[] | null;
}
interface QimingclawManagedService {
serviceId: string;
name: string;
kind: string;
status: string;
statusLabel: string;
running: boolean;
error?: string | null;
requiresHumanAction: boolean;
dependentTasks: string[];
restartCount: number;
lastSeenAt: string;
instance?: Record<string, unknown>;
lease?: Record<string, unknown>;
}
interface QimingclawPlanTemplateList {
generatedAt: string;
templateDir: string;
@@ -655,6 +673,82 @@ function serviceLine(snapshot: QimingclawSnapshot | null, key: string, label: st
return `${label}:未运行${service.error ? `${service.error}` : ''}`;
}
function buildManagedServices(snapshot: QimingclawSnapshot | null): DigitalEmployeeManagedService[] {
const fromSnapshot = snapshot?.managedServices ?? [];
if (fromSnapshot.length > 0) {
return fromSnapshot.map((service) => ({
service_id: service.serviceId,
name: service.name,
kind: service.kind,
status: service.status,
status_label: service.statusLabel,
running: service.running,
error: service.error ?? null,
requires_human_action: service.requiresHumanAction,
dependent_tasks: service.dependentTasks ?? [],
restart_count: service.restartCount ?? 0,
last_seen_at: service.lastSeenAt,
instance: service.instance,
lease: service.lease,
}));
}
return [
fallbackManagedService(snapshot, 'agent', 'Agent', 'agent', ['ACP 会话执行', 'Plan Agent']),
fallbackManagedService(snapshot, 'mcp', 'MCP Proxy', 'mcp', ['Skill Catalog', 'MCP 工具调用']),
fallbackManagedService(snapshot, 'computerServer', 'Computer Server', 'desktop', ['/computer/chat', '远程任务接入']),
fallbackManagedService(snapshot, 'fileServer', 'File Server', 'file', ['Artifact 上传', '文件交付']),
fallbackManagedService(snapshot, 'lanproxy', 'Lanproxy', 'network', ['远程访问', '管理端回连']),
fallbackManagedService(snapshot, 'guiServer', 'GUI Agent Server', 'gui', ['GUI 自动化']),
];
}
function fallbackManagedService(
snapshot: QimingclawSnapshot | null,
key: string,
name: string,
kind: string,
dependentTasks: string[],
): DigitalEmployeeManagedService {
const service = snapshot?.services[key];
const running = service?.running === true;
const status = service?.error ? 'degraded' : running ? 'running' : 'stopped';
return {
service_id: key,
name,
kind,
status,
status_label: status === 'running' ? '运行中' : status === 'degraded' ? '异常' : '未运行',
running,
error: service?.error ?? null,
requires_human_action: Boolean(service?.error),
dependent_tasks: dependentTasks,
restart_count: 0,
last_seen_at: snapshot?.generatedAt ?? new Date().toISOString(),
instance: service ? {
port: service.port,
engineType: service.engineType,
serverCount: service.serverCount,
serverNames: service.serverNames,
} : undefined,
lease: {
state: status === 'running' ? 'active' : status,
updatedAt: snapshot?.generatedAt ?? new Date().toISOString(),
},
};
}
function managedServiceLine(service: DigitalEmployeeManagedService): string {
const deps = service.dependent_tasks.length > 0 ? `;依赖任务:${service.dependent_tasks.slice(0, 2).join('、')}` : '';
return `${service.name}${service.status_label}${service.error ? `${service.error}` : ''}${deps}`;
}
function managedServiceSummary(services: DigitalEmployeeManagedService[]): string {
const runningCount = services.filter((service) => service.running).length;
const issueCount = services.filter((service) => service.requires_human_action || service.status === 'degraded').length;
return `ManagedService${runningCount}/${services.length} 运行,${issueCount} 项需处理`;
}
function syncLine(snapshot: QimingclawSnapshot | null): string {
const sync = snapshot?.sync;
if (!sync) return '管理端同步:未连接';
@@ -1703,6 +1797,9 @@ function buildWorkday(date: string, snapshot: QimingclawSnapshot | null): Digita
const hasLiveRuntime = hasRuntimeRecords(snapshot);
const sync = snapshot?.sync;
const artifactSources = reportArtifactSources(buildArtifacts(snapshot));
const managedServices = buildManagedServices(snapshot);
const managedServiceIssues = managedServices.filter((service) => service.requires_human_action || service.status === 'degraded');
const runningManagedServiceCount = managedServices.filter((service) => service.running).length;
return {
source: 'live',
@@ -1723,18 +1820,20 @@ function buildWorkday(date: string, snapshot: QimingclawSnapshot | null): Digita
current_status: state.profile.currentStatus,
current_status_label: state.profile.currentStatus === 'resting' ? '休息中' : '工作中',
status_label: hasLiveRuntime
? (failureCount > 0 ? '有异常' : runningCount > 0 ? '执行中' : '已同步')
? (failureCount > 0 || managedServiceIssues.length > 0 ? '有异常' : runningCount > 0 ? '执行中' : '已同步')
: confirmed ? '运行中' : '待确认',
status_tone: hasLiveRuntime
? (failureCount > 0 ? 'danger' : runningCount > 0 ? 'running' : 'success')
? (failureCount > 0 || managedServiceIssues.length > 0 ? 'danger' : runningCount > 0 ? 'running' : 'success')
: confirmed ? 'running' : 'waiting',
summary: hasLiveRuntime
? `当前日期 ${displayDate(date)} 已读取 ${liveRecordCount} 条 qimingclaw 本地运行记录。`
? `当前日期 ${displayDate(date)} 已读取 ${liveRecordCount} 条 qimingclaw 本地运行记录${managedServiceSummary(managedServices)}`
: confirmed
? `当前日期 ${displayDate(date)} 已确认 ${selectedCount} 项任务。`
: `当前日期 ${displayDate(date)} 待确认今日任务。`,
bubble_text: hasLiveRuntime
? '我已接入 qimingclaw 本地运行记录,会持续展示真实任务、事件和同步状态。'
? managedServiceIssues.length > 0
? `我发现 ${managedServiceIssues.length} 项常驻服务需要处理,会把影响到的任务标出来。`
: '我已接入 qimingclaw 本地运行记录,会持续展示真实任务、事件和同步状态。'
: confirmed
? '我已接入 qimingclaw 客户端执行链路,正在等待真实 Run/Event 数据接入。'
: '请先确认今天要执行的任务,我会把它们映射到 qimingclaw 执行链路。',
@@ -1751,8 +1850,11 @@ function buildWorkday(date: string, snapshot: QimingclawSnapshot | null): Digita
{ label: '异常', value: String(failureCount) },
{ label: '待同步', value: String(sync?.pending ?? 0) },
{ label: '同步异常', value: String(sync?.failed ?? 0) },
{ label: '服务运行', value: `${runningManagedServiceCount}/${managedServices.length}` },
{ label: '需处理服务', value: String(managedServiceIssues.length) },
],
},
managed_services: managedServices,
duties,
events,
execution_summaries: executionSummaries,
@@ -1771,6 +1873,8 @@ function buildWorkday(date: string, snapshot: QimingclawSnapshot | null): Digita
serviceLine(snapshot, 'agent', 'Agent'),
serviceLine(snapshot, 'mcp', 'MCP'),
serviceLine(snapshot, 'computerServer', 'Computer Server'),
managedServiceSummary(managedServices),
...managedServiceIssues.slice(0, 3).map(managedServiceLine),
syncLine(snapshot),
`活跃会话:${snapshot?.sessions.active ?? 0} / ${snapshot?.sessions.total ?? 0}`,
],

View File

@@ -16,6 +16,7 @@ import type {
DigitalEmployeeDailyReport,
DigitalEmployeeDecision,
DigitalEmployeeDuty,
DigitalEmployeeManagedService,
DigitalEmployeeRunDetail,
DigitalEmployeeTaskExecutionSummary,
DigitalEmployeeWorkEvent,
@@ -711,6 +712,11 @@ function projectionSelectedDutyIds(projection: DigitalEmployeeWorkdayProjection)
);
}
function managedServiceSummary(service: DigitalEmployeeManagedService): string {
const deps = service.dependent_tasks.length > 0 ? `,影响 ${service.dependent_tasks.slice(0, 2).join('、')}` : '';
return `${service.name} ${service.status_label}${service.error ? `${service.error}` : deps}`;
}
function isDecisionOpen(status?: string): boolean {
return ['pending', 'requested'].includes(String(status || '').toLowerCase());
}
@@ -990,6 +996,8 @@ export default function CommandDeck({ onNavigate }: { onNavigate: (target: Digit
const employee = workdayProjection?.employee;
const workday = workdayProjection?.workday;
const delivery = workdayProjection?.delivery;
const managedServices = isLiveProjection ? (workdayProjection?.managed_services ?? []) : [];
const managedServiceIssues = managedServices.filter((service) => service.requires_human_action || service.status === 'degraded');
const dailyReport = isLiveProjection ? workdayProjection?.daily_report : undefined;
const runDetails = isLiveProjection
? (workdayProjection?.run_details?.length ? workdayProjection.run_details : allEvents.map(runDetailFromEvent))
@@ -1046,12 +1054,17 @@ export default function CommandDeck({ onNavigate }: { onNavigate: (target: Digit
const workdaySummaryText = totalDuties.length > 0
? `已加载 ${totalDuties.length} 项任务,当前选择 ${selectedDuties.length} 项;右侧展示真实执行状态、运行明细和日报。`
: (workday?.summary ?? '等待同步当天任务计划。');
const currentActionTitle = currentEvent?.task_title ?? (workday?.confirmed ? '等待下一条执行事件' : '等待确认任务设置');
const currentActionTitle = currentEvent?.task_title
?? (managedServiceIssues[0] ? managedServiceIssues[0].name : workday?.confirmed ? '等待下一条执行事件' : '等待确认任务设置');
const currentActionDetail = currentEvent
? `${currentEvent.plan_title}${eventBusinessResult(currentEvent)}${eventTimingSummary(currentEvent)}`
: managedServiceIssues[0]
? managedServiceSummary(managedServiceIssues[0])
: '任务执行后会显示正在处理的任务、步骤或汇总动作。';
const nextStepText = pendingDecisions[0]
? `等待人工确认:${pendingDecisions[0].title}`
: managedServiceIssues[0]
? `处理服务状态:${managedServiceSummary(managedServiceIssues[0])}`
: workday?.confirmed
? (delivery?.lines[0] ?? '继续按计划轮询新工单,并把结果同步到运行明细。')
: '请先确认今天要执行的任务,确认后数字员工会开始执行。';

View File

@@ -589,6 +589,22 @@ export interface DigitalEmployeeOverview {
metrics: DigitalEmployeeMetric[];
}
export interface DigitalEmployeeManagedService {
service_id: string;
name: string;
kind: string;
status: string;
status_label: string;
running: boolean;
error?: string | null;
requires_human_action: boolean;
dependent_tasks: string[];
restart_count: number;
last_seen_at: string;
instance?: Record<string, unknown>;
lease?: Record<string, unknown>;
}
export interface DigitalEmployeeWorkEvent {
id: string;
time: string;
@@ -711,6 +727,7 @@ export interface DigitalEmployeeWorkdayProjection {
employee: DigitalEmployeeProfile;
workday: DigitalEmployeeWorkdayState;
overview: DigitalEmployeeOverview;
managed_services?: DigitalEmployeeManagedService[];
duties: DigitalEmployeeDuty[];
events: DigitalEmployeeWorkEvent[];
execution_summaries?: DigitalEmployeeTaskExecutionSummary[];