feat(client): project managed services to digital employee
This commit is contained in:
@@ -10,6 +10,7 @@ import type {
|
|||||||
DebugTask,
|
DebugTask,
|
||||||
DigitalEmployeeDecision,
|
DigitalEmployeeDecision,
|
||||||
DigitalEmployeeDuty,
|
DigitalEmployeeDuty,
|
||||||
|
DigitalEmployeeManagedService,
|
||||||
DigitalEmployeeRunDetail,
|
DigitalEmployeeRunDetail,
|
||||||
DigitalEmployeeTaskExecutionSummary,
|
DigitalEmployeeTaskExecutionSummary,
|
||||||
DigitalEmployeeWorkEvent,
|
DigitalEmployeeWorkEvent,
|
||||||
@@ -117,6 +118,7 @@ interface QimingclawSyncFailure {
|
|||||||
interface QimingclawSnapshot {
|
interface QimingclawSnapshot {
|
||||||
generatedAt: string;
|
generatedAt: string;
|
||||||
services: Record<string, QimingclawServiceStatus | null>;
|
services: Record<string, QimingclawServiceStatus | null>;
|
||||||
|
managedServices?: QimingclawManagedService[] | null;
|
||||||
sessions: {
|
sessions: {
|
||||||
total: number;
|
total: number;
|
||||||
active: number;
|
active: number;
|
||||||
@@ -128,6 +130,22 @@ interface QimingclawSnapshot {
|
|||||||
planTemplates?: QimingclawPlanTemplateRecord[] | null;
|
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 {
|
interface QimingclawPlanTemplateList {
|
||||||
generatedAt: string;
|
generatedAt: string;
|
||||||
templateDir: string;
|
templateDir: string;
|
||||||
@@ -655,6 +673,82 @@ function serviceLine(snapshot: QimingclawSnapshot | null, key: string, label: st
|
|||||||
return `${label}:未运行${service.error ? `,${service.error}` : ''}`;
|
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 {
|
function syncLine(snapshot: QimingclawSnapshot | null): string {
|
||||||
const sync = snapshot?.sync;
|
const sync = snapshot?.sync;
|
||||||
if (!sync) return '管理端同步:未连接';
|
if (!sync) return '管理端同步:未连接';
|
||||||
@@ -1703,6 +1797,9 @@ function buildWorkday(date: string, snapshot: QimingclawSnapshot | null): Digita
|
|||||||
const hasLiveRuntime = hasRuntimeRecords(snapshot);
|
const hasLiveRuntime = hasRuntimeRecords(snapshot);
|
||||||
const sync = snapshot?.sync;
|
const sync = snapshot?.sync;
|
||||||
const artifactSources = reportArtifactSources(buildArtifacts(snapshot));
|
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 {
|
return {
|
||||||
source: 'live',
|
source: 'live',
|
||||||
@@ -1723,18 +1820,20 @@ function buildWorkday(date: string, snapshot: QimingclawSnapshot | null): Digita
|
|||||||
current_status: state.profile.currentStatus,
|
current_status: state.profile.currentStatus,
|
||||||
current_status_label: state.profile.currentStatus === 'resting' ? '休息中' : '工作中',
|
current_status_label: state.profile.currentStatus === 'resting' ? '休息中' : '工作中',
|
||||||
status_label: hasLiveRuntime
|
status_label: hasLiveRuntime
|
||||||
? (failureCount > 0 ? '有异常' : runningCount > 0 ? '执行中' : '已同步')
|
? (failureCount > 0 || managedServiceIssues.length > 0 ? '有异常' : runningCount > 0 ? '执行中' : '已同步')
|
||||||
: confirmed ? '运行中' : '待确认',
|
: confirmed ? '运行中' : '待确认',
|
||||||
status_tone: hasLiveRuntime
|
status_tone: hasLiveRuntime
|
||||||
? (failureCount > 0 ? 'danger' : runningCount > 0 ? 'running' : 'success')
|
? (failureCount > 0 || managedServiceIssues.length > 0 ? 'danger' : runningCount > 0 ? 'running' : 'success')
|
||||||
: confirmed ? 'running' : 'waiting',
|
: confirmed ? 'running' : 'waiting',
|
||||||
summary: hasLiveRuntime
|
summary: hasLiveRuntime
|
||||||
? `当前日期 ${displayDate(date)} 已读取 ${liveRecordCount} 条 qimingclaw 本地运行记录。`
|
? `当前日期 ${displayDate(date)} 已读取 ${liveRecordCount} 条 qimingclaw 本地运行记录,${managedServiceSummary(managedServices)}。`
|
||||||
: confirmed
|
: confirmed
|
||||||
? `当前日期 ${displayDate(date)} 已确认 ${selectedCount} 项任务。`
|
? `当前日期 ${displayDate(date)} 已确认 ${selectedCount} 项任务。`
|
||||||
: `当前日期 ${displayDate(date)} 待确认今日任务。`,
|
: `当前日期 ${displayDate(date)} 待确认今日任务。`,
|
||||||
bubble_text: hasLiveRuntime
|
bubble_text: hasLiveRuntime
|
||||||
? '我已接入 qimingclaw 本地运行记录,会持续展示真实任务、事件和同步状态。'
|
? managedServiceIssues.length > 0
|
||||||
|
? `我发现 ${managedServiceIssues.length} 项常驻服务需要处理,会把影响到的任务标出来。`
|
||||||
|
: '我已接入 qimingclaw 本地运行记录,会持续展示真实任务、事件和同步状态。'
|
||||||
: confirmed
|
: confirmed
|
||||||
? '我已接入 qimingclaw 客户端执行链路,正在等待真实 Run/Event 数据接入。'
|
? '我已接入 qimingclaw 客户端执行链路,正在等待真实 Run/Event 数据接入。'
|
||||||
: '请先确认今天要执行的任务,我会把它们映射到 qimingclaw 执行链路。',
|
: '请先确认今天要执行的任务,我会把它们映射到 qimingclaw 执行链路。',
|
||||||
@@ -1751,8 +1850,11 @@ function buildWorkday(date: string, snapshot: QimingclawSnapshot | null): Digita
|
|||||||
{ label: '异常', value: String(failureCount) },
|
{ label: '异常', value: String(failureCount) },
|
||||||
{ label: '待同步', value: String(sync?.pending ?? 0) },
|
{ label: '待同步', value: String(sync?.pending ?? 0) },
|
||||||
{ label: '同步异常', value: String(sync?.failed ?? 0) },
|
{ label: '同步异常', value: String(sync?.failed ?? 0) },
|
||||||
|
{ label: '服务运行', value: `${runningManagedServiceCount}/${managedServices.length}` },
|
||||||
|
{ label: '需处理服务', value: String(managedServiceIssues.length) },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
managed_services: managedServices,
|
||||||
duties,
|
duties,
|
||||||
events,
|
events,
|
||||||
execution_summaries: executionSummaries,
|
execution_summaries: executionSummaries,
|
||||||
@@ -1771,6 +1873,8 @@ function buildWorkday(date: string, snapshot: QimingclawSnapshot | null): Digita
|
|||||||
serviceLine(snapshot, 'agent', 'Agent'),
|
serviceLine(snapshot, 'agent', 'Agent'),
|
||||||
serviceLine(snapshot, 'mcp', 'MCP'),
|
serviceLine(snapshot, 'mcp', 'MCP'),
|
||||||
serviceLine(snapshot, 'computerServer', 'Computer Server'),
|
serviceLine(snapshot, 'computerServer', 'Computer Server'),
|
||||||
|
managedServiceSummary(managedServices),
|
||||||
|
...managedServiceIssues.slice(0, 3).map(managedServiceLine),
|
||||||
syncLine(snapshot),
|
syncLine(snapshot),
|
||||||
`活跃会话:${snapshot?.sessions.active ?? 0} / ${snapshot?.sessions.total ?? 0}`,
|
`活跃会话:${snapshot?.sessions.active ?? 0} / ${snapshot?.sessions.total ?? 0}`,
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import type {
|
|||||||
DigitalEmployeeDailyReport,
|
DigitalEmployeeDailyReport,
|
||||||
DigitalEmployeeDecision,
|
DigitalEmployeeDecision,
|
||||||
DigitalEmployeeDuty,
|
DigitalEmployeeDuty,
|
||||||
|
DigitalEmployeeManagedService,
|
||||||
DigitalEmployeeRunDetail,
|
DigitalEmployeeRunDetail,
|
||||||
DigitalEmployeeTaskExecutionSummary,
|
DigitalEmployeeTaskExecutionSummary,
|
||||||
DigitalEmployeeWorkEvent,
|
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 {
|
function isDecisionOpen(status?: string): boolean {
|
||||||
return ['pending', 'requested'].includes(String(status || '').toLowerCase());
|
return ['pending', 'requested'].includes(String(status || '').toLowerCase());
|
||||||
}
|
}
|
||||||
@@ -990,6 +996,8 @@ export default function CommandDeck({ onNavigate }: { onNavigate: (target: Digit
|
|||||||
const employee = workdayProjection?.employee;
|
const employee = workdayProjection?.employee;
|
||||||
const workday = workdayProjection?.workday;
|
const workday = workdayProjection?.workday;
|
||||||
const delivery = workdayProjection?.delivery;
|
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 dailyReport = isLiveProjection ? workdayProjection?.daily_report : undefined;
|
||||||
const runDetails = isLiveProjection
|
const runDetails = isLiveProjection
|
||||||
? (workdayProjection?.run_details?.length ? workdayProjection.run_details : allEvents.map(runDetailFromEvent))
|
? (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
|
const workdaySummaryText = totalDuties.length > 0
|
||||||
? `已加载 ${totalDuties.length} 项任务,当前选择 ${selectedDuties.length} 项;右侧展示真实执行状态、运行明细和日报。`
|
? `已加载 ${totalDuties.length} 项任务,当前选择 ${selectedDuties.length} 项;右侧展示真实执行状态、运行明细和日报。`
|
||||||
: (workday?.summary ?? '等待同步当天任务计划。');
|
: (workday?.summary ?? '等待同步当天任务计划。');
|
||||||
const currentActionTitle = currentEvent?.task_title ?? (workday?.confirmed ? '等待下一条执行事件' : '等待确认任务设置');
|
const currentActionTitle = currentEvent?.task_title
|
||||||
|
?? (managedServiceIssues[0] ? managedServiceIssues[0].name : workday?.confirmed ? '等待下一条执行事件' : '等待确认任务设置');
|
||||||
const currentActionDetail = currentEvent
|
const currentActionDetail = currentEvent
|
||||||
? `${currentEvent.plan_title}:${eventBusinessResult(currentEvent)}(${eventTimingSummary(currentEvent)})`
|
? `${currentEvent.plan_title}:${eventBusinessResult(currentEvent)}(${eventTimingSummary(currentEvent)})`
|
||||||
|
: managedServiceIssues[0]
|
||||||
|
? managedServiceSummary(managedServiceIssues[0])
|
||||||
: '任务执行后会显示正在处理的任务、步骤或汇总动作。';
|
: '任务执行后会显示正在处理的任务、步骤或汇总动作。';
|
||||||
const nextStepText = pendingDecisions[0]
|
const nextStepText = pendingDecisions[0]
|
||||||
? `等待人工确认:${pendingDecisions[0].title}`
|
? `等待人工确认:${pendingDecisions[0].title}`
|
||||||
|
: managedServiceIssues[0]
|
||||||
|
? `处理服务状态:${managedServiceSummary(managedServiceIssues[0])}`
|
||||||
: workday?.confirmed
|
: workday?.confirmed
|
||||||
? (delivery?.lines[0] ?? '继续按计划轮询新工单,并把结果同步到运行明细。')
|
? (delivery?.lines[0] ?? '继续按计划轮询新工单,并把结果同步到运行明细。')
|
||||||
: '请先确认今天要执行的任务,确认后数字员工会开始执行。';
|
: '请先确认今天要执行的任务,确认后数字员工会开始执行。';
|
||||||
|
|||||||
@@ -589,6 +589,22 @@ export interface DigitalEmployeeOverview {
|
|||||||
metrics: DigitalEmployeeMetric[];
|
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 {
|
export interface DigitalEmployeeWorkEvent {
|
||||||
id: string;
|
id: string;
|
||||||
time: string;
|
time: string;
|
||||||
@@ -711,6 +727,7 @@ export interface DigitalEmployeeWorkdayProjection {
|
|||||||
employee: DigitalEmployeeProfile;
|
employee: DigitalEmployeeProfile;
|
||||||
workday: DigitalEmployeeWorkdayState;
|
workday: DigitalEmployeeWorkdayState;
|
||||||
overview: DigitalEmployeeOverview;
|
overview: DigitalEmployeeOverview;
|
||||||
|
managed_services?: DigitalEmployeeManagedService[];
|
||||||
duties: DigitalEmployeeDuty[];
|
duties: DigitalEmployeeDuty[];
|
||||||
events: DigitalEmployeeWorkEvent[];
|
events: DigitalEmployeeWorkEvent[];
|
||||||
execution_summaries?: DigitalEmployeeTaskExecutionSummary[];
|
execution_summaries?: DigitalEmployeeTaskExecutionSummary[];
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -15,7 +15,7 @@
|
|||||||
console.warn("[qimingclaw] digital employee auth bootstrap skipped", error);
|
console.warn("[qimingclaw] digital employee auth bootstrap skipped", error);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<script type="module" crossorigin src="./assets/index-BZWWD4XK.js"></script>
|
<script type="module" crossorigin src="./assets/index-DALQvQtr.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="./assets/index-BgRGGB2I.css">
|
<link rel="stylesheet" crossorigin href="./assets/index-BgRGGB2I.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
readDigitalEmployeeState,
|
readDigitalEmployeeState,
|
||||||
readDigitalEmployeeUiState,
|
readDigitalEmployeeUiState,
|
||||||
saveDigitalEmployeeUiState,
|
saveDigitalEmployeeUiState,
|
||||||
|
type DigitalEmployeeManagedService,
|
||||||
type DigitalEmployeeGovernanceCommandRecord,
|
type DigitalEmployeeGovernanceCommandRecord,
|
||||||
type DigitalEmployeeServiceStatus,
|
type DigitalEmployeeServiceStatus,
|
||||||
type DigitalEmployeeSnapshot,
|
type DigitalEmployeeSnapshot,
|
||||||
@@ -84,19 +85,22 @@ export function registerDigitalEmployeeHandlers(ctx: HandlerContext): void {
|
|||||||
|
|
||||||
function buildSnapshot(ctx: HandlerContext): DigitalEmployeeSnapshot {
|
function buildSnapshot(ctx: HandlerContext): DigitalEmployeeSnapshot {
|
||||||
const sessions = agentService.listAllSessionsDetailed();
|
const sessions = agentService.listAllSessionsDetailed();
|
||||||
return {
|
const generatedAt = new Date().toISOString();
|
||||||
generatedAt: new Date().toISOString(),
|
const services = {
|
||||||
services: {
|
agent: {
|
||||||
agent: {
|
running: agentService.isReady,
|
||||||
running: agentService.isReady,
|
engineType: agentService.getEngineType(),
|
||||||
engineType: agentService.getEngineType(),
|
|
||||||
},
|
|
||||||
mcp: normalizeServiceStatus(mcpProxyManager.getStatus()),
|
|
||||||
computerServer: normalizeServiceStatus(getComputerServerStatus()),
|
|
||||||
lanproxy: normalizeServiceStatus(ctx.lanproxy.status()),
|
|
||||||
fileServer: normalizeServiceStatus(ctx.fileServer.status()),
|
|
||||||
guiServer: normalizeServiceStatus(getGuiStatus()),
|
|
||||||
},
|
},
|
||||||
|
mcp: normalizeServiceStatus(mcpProxyManager.getStatus()),
|
||||||
|
computerServer: normalizeServiceStatus(getComputerServerStatus()),
|
||||||
|
lanproxy: normalizeServiceStatus(ctx.lanproxy.status()),
|
||||||
|
fileServer: normalizeServiceStatus(ctx.fileServer.status()),
|
||||||
|
guiServer: normalizeServiceStatus(getGuiStatus()),
|
||||||
|
} satisfies Record<string, DigitalEmployeeServiceStatus | null>;
|
||||||
|
return {
|
||||||
|
generatedAt,
|
||||||
|
services,
|
||||||
|
managedServices: buildManagedServices(services, generatedAt),
|
||||||
sessions: {
|
sessions: {
|
||||||
total: sessions.length,
|
total: sessions.length,
|
||||||
active: sessions.filter((session) =>
|
active: sessions.filter((session) =>
|
||||||
@@ -107,6 +111,93 @@ function buildSnapshot(ctx: HandlerContext): DigitalEmployeeSnapshot {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildManagedServices(
|
||||||
|
services: Record<string, DigitalEmployeeServiceStatus | null>,
|
||||||
|
generatedAt: string,
|
||||||
|
): DigitalEmployeeManagedService[] {
|
||||||
|
return MANAGED_SERVICE_DEFINITIONS.map((definition) => {
|
||||||
|
const status = services[definition.serviceId] ?? { running: false };
|
||||||
|
const error = status.error ?? null;
|
||||||
|
const running = status.running === true;
|
||||||
|
const state = error ? "degraded" : running ? "running" : "stopped";
|
||||||
|
return {
|
||||||
|
serviceId: definition.serviceId,
|
||||||
|
name: definition.name,
|
||||||
|
kind: definition.kind,
|
||||||
|
status: state,
|
||||||
|
statusLabel: state === "running" ? "运行中" : state === "degraded" ? "异常" : "未运行",
|
||||||
|
running,
|
||||||
|
error,
|
||||||
|
requiresHumanAction: Boolean(error || (!running && definition.required)),
|
||||||
|
dependentTasks: definition.dependentTasks,
|
||||||
|
restartCount: 0,
|
||||||
|
lastSeenAt: generatedAt,
|
||||||
|
instance: {
|
||||||
|
pid: status.pid,
|
||||||
|
port: status.port,
|
||||||
|
engineType: status.engineType,
|
||||||
|
serverCount: status.serverCount,
|
||||||
|
serverNames: status.serverNames,
|
||||||
|
},
|
||||||
|
lease: {
|
||||||
|
state: state === "running" ? "active" : state,
|
||||||
|
updatedAt: generatedAt,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const MANAGED_SERVICE_DEFINITIONS: Array<{
|
||||||
|
serviceId: string;
|
||||||
|
name: string;
|
||||||
|
kind: DigitalEmployeeManagedService["kind"];
|
||||||
|
required: boolean;
|
||||||
|
dependentTasks: string[];
|
||||||
|
}> = [
|
||||||
|
{
|
||||||
|
serviceId: "agent",
|
||||||
|
name: "Agent",
|
||||||
|
kind: "agent",
|
||||||
|
required: true,
|
||||||
|
dependentTasks: ["ACP 会话执行", "Plan Agent", "Task Agent"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
serviceId: "mcp",
|
||||||
|
name: "MCP Proxy",
|
||||||
|
kind: "mcp",
|
||||||
|
required: true,
|
||||||
|
dependentTasks: ["Skill Catalog", "MCP 工具调用"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
serviceId: "computerServer",
|
||||||
|
name: "Computer Server",
|
||||||
|
kind: "desktop",
|
||||||
|
required: true,
|
||||||
|
dependentTasks: ["/computer/chat", "远程任务接入"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
serviceId: "fileServer",
|
||||||
|
name: "File Server",
|
||||||
|
kind: "file",
|
||||||
|
required: false,
|
||||||
|
dependentTasks: ["Artifact 上传", "文件交付"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
serviceId: "lanproxy",
|
||||||
|
name: "Lanproxy",
|
||||||
|
kind: "network",
|
||||||
|
required: false,
|
||||||
|
dependentTasks: ["远程访问", "管理端回连"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
serviceId: "guiServer",
|
||||||
|
name: "GUI Agent Server",
|
||||||
|
kind: "gui",
|
||||||
|
required: false,
|
||||||
|
dependentTasks: ["GUI 自动化", "桌面操作"],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
function normalizeServiceStatus(
|
function normalizeServiceStatus(
|
||||||
value: object | null | undefined,
|
value: object | null | undefined,
|
||||||
): DigitalEmployeeServiceStatus {
|
): DigitalEmployeeServiceStatus {
|
||||||
|
|||||||
@@ -26,9 +26,35 @@ export interface DigitalEmployeeSessionSummary {
|
|||||||
}>;
|
}>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface DigitalEmployeeManagedService {
|
||||||
|
serviceId: string;
|
||||||
|
name: string;
|
||||||
|
kind: "agent" | "mcp" | "desktop" | "network" | "file" | "gui" | "unknown";
|
||||||
|
status: "running" | "stopped" | "degraded";
|
||||||
|
statusLabel: string;
|
||||||
|
running: boolean;
|
||||||
|
error?: string | null;
|
||||||
|
requiresHumanAction: boolean;
|
||||||
|
dependentTasks: string[];
|
||||||
|
restartCount: number;
|
||||||
|
lastSeenAt: string;
|
||||||
|
instance: {
|
||||||
|
pid?: number;
|
||||||
|
port?: number;
|
||||||
|
engineType?: string | null;
|
||||||
|
serverCount?: number;
|
||||||
|
serverNames?: string[];
|
||||||
|
};
|
||||||
|
lease: {
|
||||||
|
state: "active" | "stopped" | "degraded";
|
||||||
|
updatedAt: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export interface DigitalEmployeeSnapshot {
|
export interface DigitalEmployeeSnapshot {
|
||||||
generatedAt: string;
|
generatedAt: string;
|
||||||
services: Record<string, DigitalEmployeeServiceStatus | null>;
|
services: Record<string, DigitalEmployeeServiceStatus | null>;
|
||||||
|
managedServices?: DigitalEmployeeManagedService[];
|
||||||
sessions: DigitalEmployeeSessionSummary;
|
sessions: DigitalEmployeeSessionSummary;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1048,14 +1048,23 @@ ServiceLease
|
|||||||
- 当前依赖它的任务
|
- 当前依赖它的任务
|
||||||
- 是否需要人工处理
|
- 是否需要人工处理
|
||||||
|
|
||||||
|
当前客户端实现:
|
||||||
|
|
||||||
|
- `digitalEmployee:getSnapshot` 输出 `managedServices`,由 Agent、MCP Proxy、Computer Server、File Server、Lanproxy、GUI Agent Server 状态投射生成。
|
||||||
|
- 每个服务包含 `serviceId`、`kind`、`status`、`requiresHumanAction`、`dependentTasks`、`instance`、`lease`。
|
||||||
|
- embedded adapter 将其转换为 `workday.managed_services`,并写入 overview 指标、首页播报和 delivery lines。
|
||||||
|
- 服务异常或必需服务未运行时,首页“当前动作 / 下一步”会提示对应服务和受影响任务。
|
||||||
|
|
||||||
### 产出
|
### 产出
|
||||||
|
|
||||||
- 服务状态不只在客户端设置页展示,也进入数字员工的运行上下文。
|
- 服务状态不只在客户端设置页展示,也进入数字员工的运行上下文。
|
||||||
|
- 常驻服务具备 ManagedService / ServiceInstance / ServiceLease 的业务投影。
|
||||||
|
|
||||||
### 验收
|
### 验收
|
||||||
|
|
||||||
- 服务异常时,数字员工首页能提示。
|
- 服务异常时,数字员工首页能提示。
|
||||||
- 服务恢复后,事件和状态同步更新。
|
- 服务恢复后,事件和状态同步更新。
|
||||||
|
- 首页 overview 能看到服务运行数和需处理服务数。
|
||||||
|
|
||||||
## Phase 10:Plan Agent / Task Agent
|
## Phase 10:Plan Agent / Task Agent
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user