feat(client): show digital employee sync status
This commit is contained in:
@@ -71,6 +71,7 @@ interface QimingclawSnapshot {
|
||||
active: number;
|
||||
items: Array<{ id: string; title?: string; status?: string; engineType?: string; projectId?: string }>;
|
||||
};
|
||||
sync?: QimingclawSyncStatus | null;
|
||||
}
|
||||
|
||||
const SKILLS: SkillSpec[] = [
|
||||
@@ -283,7 +284,10 @@ function writeState(state: AdapterState): void {
|
||||
|
||||
async function readQimingclawSnapshot(): Promise<QimingclawSnapshot | null> {
|
||||
try {
|
||||
return await window.QimingClawBridge?.digital?.getSnapshot?.() ?? null;
|
||||
const snapshot = await window.QimingClawBridge?.digital?.getSnapshot?.() ?? null;
|
||||
if (!snapshot) return null;
|
||||
const sync = await window.QimingClawBridge?.digital?.getSyncStatus?.().catch(() => null) ?? null;
|
||||
return { ...snapshot, sync };
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
@@ -357,6 +361,17 @@ function serviceLine(snapshot: QimingclawSnapshot | null, key: string, label: st
|
||||
return `${label}:未运行${service.error ? `,${service.error}` : ''}`;
|
||||
}
|
||||
|
||||
function syncLine(snapshot: QimingclawSnapshot | null): string {
|
||||
const sync = snapshot?.sync;
|
||||
if (!sync) return '管理端同步:未连接';
|
||||
if (!sync.enabled) return '管理端同步:已停用';
|
||||
if (!sync.endpoint) return '管理端同步:未配置地址';
|
||||
if (sync.syncing) return `管理端同步:同步中,待同步 ${sync.pending} 条`;
|
||||
if (sync.failed > 0) return `管理端同步:${sync.failed} 条失败,待同步 ${sync.pending} 条`;
|
||||
if (sync.pending > 0) return `管理端同步:待同步 ${sync.pending} 条`;
|
||||
return sync.lastSyncAt ? `管理端同步:已同步,最近 ${sync.lastSyncAt}` : '管理端同步:等待本地数据';
|
||||
}
|
||||
|
||||
function withRuntimePlan(plan: DebugPlan, snapshot: QimingclawSnapshot | null): DebugPlan {
|
||||
return { ...plan, status: runtimeStatusForPlan(plan, snapshot) };
|
||||
}
|
||||
@@ -511,7 +526,7 @@ function buildEvents(duties: DigitalEmployeeDuty[], confirmed: boolean, snapshot
|
||||
}
|
||||
|
||||
const selectedDuties = duties.filter((duty) => duty.selected);
|
||||
return selectedDuties.map((duty) => ({
|
||||
const events = selectedDuties.map((duty) => ({
|
||||
id: `event-${duty.id}`,
|
||||
time: duty.status === 'running' ? '实时' : '待执行',
|
||||
plan_title: duty.title,
|
||||
@@ -526,6 +541,23 @@ function buildEvents(duties: DigitalEmployeeDuty[], confirmed: boolean, snapshot
|
||||
updated_at: new Date().toISOString(),
|
||||
conversation_id: duty.conversation_id,
|
||||
}));
|
||||
const sync = snapshot?.sync;
|
||||
if (sync && (sync.pending > 0 || sync.failed > 0 || sync.lastSyncAt)) {
|
||||
events.push({
|
||||
id: 'event-management-sync',
|
||||
time: sync.syncing ? '实时' : '同步',
|
||||
plan_title: '管理端联动',
|
||||
task_title: '同步数字员工本地状态',
|
||||
step_title: sync.failed > 0 ? '同步补偿' : 'outbox 推送',
|
||||
detail: `${syncLine(snapshot)}${sync.lastSyncError ? `。错误:${sync.lastSyncError}` : ''}`,
|
||||
status: sync.failed > 0 ? 'failed' : sync.syncing || sync.pending > 0 ? 'running' : 'completed',
|
||||
status_label: sync.failed > 0 ? '异常' : sync.syncing || sync.pending > 0 ? '同步中' : '已同步',
|
||||
status_tone: sync.failed > 0 ? 'danger' : sync.syncing || sync.pending > 0 ? 'running' : 'success',
|
||||
updated_at: sync.lastSyncAt ?? new Date().toISOString(),
|
||||
conversation_id: 'qimingclaw-management-sync',
|
||||
});
|
||||
}
|
||||
return events;
|
||||
}
|
||||
|
||||
function buildExecutionSummaries(duties: DigitalEmployeeDuty[], events: DigitalEmployeeWorkEvent[]): DigitalEmployeeTaskExecutionSummary[] {
|
||||
@@ -578,6 +610,7 @@ function buildWorkday(date: string, snapshot: QimingclawSnapshot | null): Digita
|
||||
const generatedAt = state.reportGeneratedAtByDate[date] ?? null;
|
||||
const selectedCount = duties.filter((duty) => duty.selected).length;
|
||||
const runningCount = events.filter((event) => event.status === 'running').length;
|
||||
const sync = snapshot?.sync;
|
||||
|
||||
return {
|
||||
source: 'live',
|
||||
@@ -614,7 +647,8 @@ function buildWorkday(date: string, snapshot: QimingclawSnapshot | null): Digita
|
||||
{ label: '已选任务', value: String(selectedCount) },
|
||||
{ label: '运行记录', value: String(events.length) },
|
||||
{ label: '执行中', value: String(runningCount) },
|
||||
{ label: '异常', value: '0' },
|
||||
{ label: '待同步', value: String(sync?.pending ?? 0) },
|
||||
{ label: '同步异常', value: String(sync?.failed ?? 0) },
|
||||
],
|
||||
},
|
||||
duties,
|
||||
@@ -631,6 +665,7 @@ function buildWorkday(date: string, snapshot: QimingclawSnapshot | null): Digita
|
||||
serviceLine(snapshot, 'agent', 'Agent'),
|
||||
serviceLine(snapshot, 'mcp', 'MCP'),
|
||||
serviceLine(snapshot, 'computerServer', 'Computer Server'),
|
||||
syncLine(snapshot),
|
||||
`活跃会话:${snapshot?.sessions.active ?? 0} / ${snapshot?.sessions.total ?? 0}`,
|
||||
],
|
||||
artifacts: [
|
||||
@@ -647,7 +682,7 @@ function buildWorkday(date: string, snapshot: QimingclawSnapshot | null): Digita
|
||||
model: 'qimingclaw-adapter',
|
||||
source: 'qimingclaw',
|
||||
summary: generatedAt
|
||||
? '今日已完成数字员工页面到 qimingclaw adapter 的接入,并读取了 qimingclaw 服务状态。'
|
||||
? `今日已完成数字员工页面到 qimingclaw adapter 的接入,并读取了 qimingclaw 服务状态。${syncLine(snapshot)}。`
|
||||
: '日报将在生成后汇总任务选择、运行明细和后续接入建议。',
|
||||
totals: {
|
||||
task_count: selectedCount,
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -13,7 +13,7 @@
|
||||
console.warn("[qimingclaw] digital employee auth bootstrap skipped", error);
|
||||
}
|
||||
</script>
|
||||
<script type="module" crossorigin src="./assets/index-DcEd9LJg.js"></script>
|
||||
<script type="module" crossorigin src="./assets/index-CCHg55lx.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="./assets/index-DqV9Vw1H.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
Reference in New Issue
Block a user