吸收数字员工运营预览台
This commit is contained in:
@@ -17,6 +17,17 @@ import type {
|
||||
DigitalEmployeeBusinessViewKind,
|
||||
DigitalEmployeeBusinessViewQuery,
|
||||
DigitalEmployeeBusinessViewRow,
|
||||
DigitalEmployeeBatchActionResult,
|
||||
DigitalEmployeeInterventionWorkbenchResponse,
|
||||
DigitalEmployeeMemoryGovernanceResponse,
|
||||
DigitalEmployeeSkillAuditWorkbenchResponse,
|
||||
DigitalEmployeeWorkbenchBatchResult,
|
||||
DigitalEmployeeOpsWorkbenchResponse,
|
||||
DigitalEmployeeNotificationWorkbenchResponse,
|
||||
DigitalEmployeeDailyReportWorkbenchResponse,
|
||||
DigitalEmployeePolicyCenterResponse,
|
||||
DigitalEmployeePolicyPullBatchResult,
|
||||
DigitalEmployeeSchedulerWorkbenchResponse,
|
||||
PlanStepBusinessRow,
|
||||
PlanStepDispatchPolicy,
|
||||
PlanStepDispatchPolicyPullResult,
|
||||
@@ -295,7 +306,7 @@ export function deleteCronJob(id: string): Promise<void> {
|
||||
}
|
||||
export function patchCronJob(
|
||||
id: string,
|
||||
patch: { name?: string; schedule?: string; command?: string },
|
||||
patch: { name?: string; schedule?: string; command?: string; enabled?: boolean },
|
||||
): Promise<CronJob> {
|
||||
return apiFetch<CronJob | { status: string; job: CronJob }>(
|
||||
`/api/cron/${encodeURIComponent(id)}`,
|
||||
@@ -336,6 +347,40 @@ export function patchCronSettings(
|
||||
});
|
||||
}
|
||||
|
||||
export async function getDigitalEmployeeSchedulerWorkbench(): Promise<DigitalEmployeeSchedulerWorkbenchResponse> {
|
||||
const [jobs, settings] = await Promise.all([
|
||||
getSchedulerJobs(),
|
||||
getCronSettings().catch(() => ({ enabled: false, catch_up_on_startup: false, max_run_history: 20 })),
|
||||
]);
|
||||
const runEntries = await Promise.all(jobs.slice(0, 30).map(async (job) => {
|
||||
const runs = await getCronRuns(job.id, 5).catch(() => []);
|
||||
return [job.id, runs] as const;
|
||||
}));
|
||||
const runsByJob = new Map(runEntries);
|
||||
return {
|
||||
items: jobs.map((job) => {
|
||||
const runs = runsByJob.get(job.id) ?? [];
|
||||
const latest = runs[0];
|
||||
const schedule = job.schedule && typeof job.schedule === 'object' ? job.schedule as Record<string, unknown> : {};
|
||||
return {
|
||||
schedule_id: job.id,
|
||||
name: job.name || job.id,
|
||||
enabled: job.enabled !== false,
|
||||
status: job.enabled === false ? 'paused' : String(job.last_status || schedule.status || 'enabled'),
|
||||
cron_expr: job.expression || String(schedule.expression || ''),
|
||||
next_run: job.next_run || null,
|
||||
last_run: job.last_run || latest?.finished_at || latest?.started_at || null,
|
||||
last_status: job.last_status || latest?.status || null,
|
||||
latest_run_error: job.last_output || latest?.output || null,
|
||||
run_count: runs.length,
|
||||
payload: { job, runs },
|
||||
};
|
||||
}),
|
||||
settings,
|
||||
source: 'qimingclaw-adapter',
|
||||
};
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Integrations
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -521,6 +566,117 @@ export function decideRouteIntervention(
|
||||
});
|
||||
}
|
||||
|
||||
export function decideRouteInterventionsBatch(
|
||||
routeDecisionIds: string[],
|
||||
input: { decision: 'mark_reviewed' | 'dismiss' | 'retry_policy_check'; actor?: string | null; comment?: string | null },
|
||||
): Promise<DigitalEmployeeBatchActionResult> {
|
||||
return apiFetch<DigitalEmployeeBatchActionResult>('/api/route-decisions/interventions/batch', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ route_decision_ids: routeDecisionIds, ...input }),
|
||||
});
|
||||
}
|
||||
|
||||
export function getDigitalEmployeeInterventionWorkbench(
|
||||
params: Record<string, string | number | null | undefined> = {},
|
||||
): Promise<DigitalEmployeeInterventionWorkbenchResponse> {
|
||||
const query = new URLSearchParams();
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
if (value !== undefined && value !== null && `${value}`.trim() !== '') query.set(key, `${value}`);
|
||||
});
|
||||
const suffix = query.toString();
|
||||
return apiFetch<DigitalEmployeeInterventionWorkbenchResponse>(`/api/digital-employee/interventions/workbench${suffix ? `?${suffix}` : ''}`);
|
||||
}
|
||||
|
||||
export function getDigitalEmployeeMemoryGovernance(
|
||||
params: Record<string, string | number | null | undefined> = {},
|
||||
): Promise<DigitalEmployeeMemoryGovernanceResponse> {
|
||||
const query = new URLSearchParams();
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
if (value !== undefined && value !== null && `${value}`.trim() !== '') query.set(key, `${value}`);
|
||||
});
|
||||
const suffix = query.toString();
|
||||
return apiFetch<DigitalEmployeeMemoryGovernanceResponse>(`/api/digital-employee/memory-governance${suffix ? `?${suffix}` : ''}`);
|
||||
}
|
||||
|
||||
export function setMemoryGovernanceStatus(
|
||||
memoryIds: string[],
|
||||
action: 'archive' | 'restore' | 'mark_reviewed',
|
||||
options: { actor?: string | null; reason?: string | null } = {},
|
||||
): Promise<DigitalEmployeeBatchActionResult> {
|
||||
return apiFetch<DigitalEmployeeBatchActionResult>('/api/digital-employee/memory-governance/status/batch', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ memory_ids: memoryIds, action, ...options }),
|
||||
});
|
||||
}
|
||||
|
||||
export function getDigitalEmployeeSkillAuditWorkbench(
|
||||
params: Record<string, string | number | null | undefined> = {},
|
||||
): Promise<DigitalEmployeeSkillAuditWorkbenchResponse> {
|
||||
const query = new URLSearchParams();
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
if (value !== undefined && value !== null && `${value}`.trim() !== '') query.set(key, `${value}`);
|
||||
});
|
||||
const suffix = query.toString();
|
||||
return apiFetch<DigitalEmployeeSkillAuditWorkbenchResponse>(`/api/digital-employee/skill-audit-workbench${suffix ? `?${suffix}` : ''}`);
|
||||
}
|
||||
|
||||
export function getDigitalEmployeeOpsWorkbench(
|
||||
params: Record<string, string | number | null | undefined> = {},
|
||||
): Promise<DigitalEmployeeOpsWorkbenchResponse> {
|
||||
const query = new URLSearchParams();
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
if (value !== undefined && value !== null && `${value}`.trim() !== '') query.set(key, `${value}`);
|
||||
});
|
||||
const suffix = query.toString();
|
||||
return apiFetch<DigitalEmployeeOpsWorkbenchResponse>(`/api/digital-employee/ops-workbench${suffix ? `?${suffix}` : ''}`);
|
||||
}
|
||||
|
||||
export function runDigitalEmployeeOpsBatchAction(
|
||||
opsIds: string[],
|
||||
action: 'mark_reviewed' | 'dismiss' | 'open_related_view' | 'retry_policy_pull' | 'safe_restart_service',
|
||||
options: { actor?: string | null; reason?: string | null } = {},
|
||||
): Promise<DigitalEmployeeWorkbenchBatchResult> {
|
||||
return apiFetch<DigitalEmployeeWorkbenchBatchResult>('/api/digital-employee/ops-workbench/actions/batch', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ ops_ids: opsIds, action, ...options }),
|
||||
timeoutMs: DIGITAL_EMPLOYEE_WORKDAY_TIMEOUT_MS,
|
||||
});
|
||||
}
|
||||
|
||||
export function getDigitalEmployeeNotificationWorkbench(
|
||||
params: Record<string, string | number | null | undefined> = {},
|
||||
): Promise<DigitalEmployeeNotificationWorkbenchResponse> {
|
||||
const query = new URLSearchParams();
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
if (value !== undefined && value !== null && `${value}`.trim() !== '') query.set(key, `${value}`);
|
||||
});
|
||||
const suffix = query.toString();
|
||||
return apiFetch<DigitalEmployeeNotificationWorkbenchResponse>(`/api/digital-employee/notifications/workbench${suffix ? `?${suffix}` : ''}`);
|
||||
}
|
||||
|
||||
export function runDigitalEmployeeNotificationBatchAction(
|
||||
notificationIds: string[],
|
||||
action: 'mark_read' | 'dismiss' | 'pull_updates' | 'open_desktop_settings',
|
||||
options: { actor?: string | null; reason?: string | null } = {},
|
||||
): Promise<DigitalEmployeeWorkbenchBatchResult> {
|
||||
return apiFetch<DigitalEmployeeWorkbenchBatchResult>('/api/digital-employee/notifications/actions/batch', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ notification_ids: notificationIds, action, ...options }),
|
||||
timeoutMs: DIGITAL_EMPLOYEE_WORKDAY_TIMEOUT_MS,
|
||||
});
|
||||
}
|
||||
|
||||
export function getDigitalEmployeeDailyReportWorkbench(
|
||||
params: Record<string, string | number | null | undefined> = {},
|
||||
): Promise<DigitalEmployeeDailyReportWorkbenchResponse> {
|
||||
const query = new URLSearchParams();
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
if (value !== undefined && value !== null && `${value}`.trim() !== '') query.set(key, `${value}`);
|
||||
});
|
||||
const suffix = query.toString();
|
||||
return apiFetch<DigitalEmployeeDailyReportWorkbenchResponse>(`/api/digital-employee/daily-reports/workbench${suffix ? `?${suffix}` : ''}`);
|
||||
}
|
||||
|
||||
export function routeIngress(
|
||||
body: IngressRouteRequest,
|
||||
): Promise<IngressRouteResponse> {
|
||||
@@ -698,6 +854,77 @@ export function pullRouteDecisionPolicy(): Promise<RouteDecisionPolicyPullResult
|
||||
});
|
||||
}
|
||||
|
||||
export async function getDigitalEmployeePolicyCenter(): Promise<DigitalEmployeePolicyCenterResponse> {
|
||||
const [dispatchPolicy, riskPolicy] = await Promise.all([
|
||||
getPlanStepDispatchPolicy(),
|
||||
getRiskApprovalPolicy(),
|
||||
]);
|
||||
return {
|
||||
plan_step_dispatch_policy: dispatchPolicy,
|
||||
risk_approval_policy: riskPolicy,
|
||||
source: 'qimingclaw-adapter',
|
||||
items: [
|
||||
{
|
||||
policy_id: 'plan_step_dispatch',
|
||||
title: 'PlanStep 派发策略',
|
||||
status: dispatchPolicy.enabled ? 'enabled' : 'disabled',
|
||||
source: dispatchPolicy.source,
|
||||
updated_at: dispatchPolicy.updated_at,
|
||||
last_pulled_at: dispatchPolicy.last_pulled_at,
|
||||
last_pull_error: dispatchPolicy.last_pull_error,
|
||||
summary: `max_per_sweep=${dispatchPolicy.max_per_sweep}; auto_dispatch_ready_steps=${dispatchPolicy.auto_dispatch_ready_steps}`,
|
||||
payload: dispatchPolicy,
|
||||
},
|
||||
{
|
||||
policy_id: 'risk_approval',
|
||||
title: '风险审批策略',
|
||||
status: riskPolicy.enabled ? 'enabled' : 'disabled',
|
||||
source: riskPolicy.source,
|
||||
updated_at: riskPolicy.updated_at,
|
||||
last_pulled_at: riskPolicy.last_pulled_at,
|
||||
last_pull_error: riskPolicy.last_pull_error,
|
||||
summary: `approval_required=${riskPolicy.approval_required_risk_levels.join(',')}; auto_reject_actions=${riskPolicy.auto_reject_actions.length}`,
|
||||
payload: riskPolicy,
|
||||
},
|
||||
{
|
||||
policy_id: 'route_decision',
|
||||
title: '入口路由策略',
|
||||
status: 'pull_available',
|
||||
source: 'remote_pull',
|
||||
summary: '通过 /api/route-decision/policy/pull 获取管理端路由策略快照。',
|
||||
},
|
||||
{
|
||||
policy_id: 'skill',
|
||||
title: '技能策略',
|
||||
status: 'pull_available',
|
||||
source: 'remote_pull',
|
||||
summary: '通过 /api/skill/policies/pull 同步远端技能启停策略。',
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
export async function pullDigitalEmployeePoliciesBatch(): Promise<DigitalEmployeePolicyPullBatchResult> {
|
||||
const jobs: Array<{ policy_id: string; run: () => Promise<unknown> }> = [
|
||||
{ policy_id: 'plan_step_dispatch', run: pullPlanStepDispatchPolicy },
|
||||
{ policy_id: 'risk_approval', run: pullRiskApprovalPolicy },
|
||||
{ policy_id: 'route_decision', run: pullRouteDecisionPolicy },
|
||||
{ policy_id: 'skill', run: pullSkillPolicies },
|
||||
];
|
||||
const items = [] as DigitalEmployeePolicyPullBatchResult['items'];
|
||||
for (const job of jobs) {
|
||||
try {
|
||||
const result = await job.run();
|
||||
const record = result as { ok?: boolean; error?: string | null };
|
||||
items.push({ policy_id: job.policy_id, ok: record.ok !== false, error: record.error ?? null, result });
|
||||
} catch (error) {
|
||||
items.push({ policy_id: job.policy_id, ok: false, error: error instanceof Error ? error.message : String(error) });
|
||||
}
|
||||
}
|
||||
const pulled = items.filter((item) => item.ok).length;
|
||||
return { ok: pulled > 0, total: items.length, pulled, failed: items.length - pulled, items };
|
||||
}
|
||||
|
||||
export function getPlanSteps(params: Record<string, string | number | null | undefined> = {}): Promise<BusinessViewResponse<PlanStepBusinessRow>> {
|
||||
const query = new URLSearchParams();
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
@@ -726,6 +953,7 @@ export function getDigitalEmployeeBusinessView(
|
||||
runs: '/api/digital-employee/runs',
|
||||
events: '/api/digital-employee/events',
|
||||
artifacts: '/api/digital-employee/artifacts',
|
||||
'artifact-groups': '/api/digital-employee/artifact-groups',
|
||||
approvals: '/api/digital-employee/approvals',
|
||||
};
|
||||
const query = new URLSearchParams();
|
||||
@@ -961,6 +1189,17 @@ export function setArtifactRetention(
|
||||
});
|
||||
}
|
||||
|
||||
export function setArtifactRetentionBatch(
|
||||
artifactIds: string[],
|
||||
action: 'mark_keep' | 'mark_expire' | 'mark_review' | 'clear_retention',
|
||||
options: Record<string, string | null | undefined> = {},
|
||||
): Promise<any> {
|
||||
return apiFetch('/api/digital-employee/artifacts/retention/batch', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ artifact_ids: artifactIds, action, ...options }),
|
||||
});
|
||||
}
|
||||
|
||||
export function cleanupArtifact(
|
||||
artifactId: string,
|
||||
options: Record<string, string | null | undefined> = {},
|
||||
@@ -992,6 +1231,17 @@ export function resolveApprovalConflict(
|
||||
});
|
||||
}
|
||||
|
||||
export function resolveApprovalConflictsBatch(
|
||||
approvalIds: string[],
|
||||
resolution: 'keep_local' | 'mark_reviewed',
|
||||
options: { actor?: string | null; comment?: string | null } = {},
|
||||
): Promise<DigitalEmployeeBatchActionResult> {
|
||||
return apiFetch<DigitalEmployeeBatchActionResult>('/api/approval/conflicts/resolve/batch', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ approval_ids: approvalIds, resolution, ...options }),
|
||||
});
|
||||
}
|
||||
|
||||
export function pullApprovalUpdates(): Promise<{ ok: boolean; applied?: number; ignored?: number; conflicted?: number; skipped?: boolean; error?: string | null }> {
|
||||
return apiFetch<{ ok: boolean; applied?: number; ignored?: number; conflicted?: number; skipped?: boolean; error?: string | null }>('/api/approval/updates/pull', {
|
||||
method: 'POST',
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user