Files
qiming/qimingclaw/crates/agent-electron-client/scripts/check-digital-employee.js
2026-06-15 14:59:29 +08:00

3318 lines
177 KiB
JavaScript

#!/usr/bin/env node
const fs = require("fs");
const os = require("os");
const path = require("path");
const { spawnSync } = require("child_process");
const packageRoot = path.resolve(__dirname, "..");
const requiredDigitalTables = [
"digital_plans",
"digital_plan_steps",
"digital_schedules",
"digital_schedule_runs",
"digital_tasks",
"digital_runs",
"digital_events",
"digital_artifacts",
"digital_approvals",
"digital_memories",
"digital_sync_outbox",
"digital_sync_attempts",
];
const testFiles = [
"src/main/services/digitalEmployee/stateService.test.ts",
"src/main/services/digitalEmployee/artifactAccessService.test.ts",
"src/main/services/digitalEmployee/schedulerService.test.ts",
"src/main/services/digitalEmployee/desktopNotificationService.test.ts",
"src/main/ipc/digitalEmployeeHandlers.test.ts",
"src/main/ipc/eventForwarders.test.ts",
"src/main/services/engines/acp/acpEngine.test.ts",
"src/main/services/digitalEmployee/syncService.test.ts",
"src/main/services/digitalEmployee/skillCatalogService.test.ts",
"src/main/services/digitalEmployee/planTemplateService.test.ts",
];
const selfHealingDigitalSchemaSql = `
CREATE TABLE IF NOT EXISTS digital_plan_steps (
id TEXT PRIMARY KEY,
remote_id TEXT,
plan_id TEXT NOT NULL,
title TEXT NOT NULL,
status TEXT NOT NULL,
seq INTEGER NOT NULL,
depends_on TEXT NOT NULL DEFAULT '[]',
preferred_skill_ids TEXT NOT NULL DEFAULT '[]',
payload TEXT NOT NULL DEFAULT '{}',
sync_status TEXT NOT NULL DEFAULT 'pending',
last_synced_at TEXT,
sync_error TEXT,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
FOREIGN KEY(plan_id) REFERENCES digital_plans(id)
);
CREATE INDEX IF NOT EXISTS idx_digital_plan_steps_plan ON digital_plan_steps(plan_id, seq);
CREATE INDEX IF NOT EXISTS idx_digital_plan_steps_sync ON digital_plan_steps(sync_status);
CREATE TABLE IF NOT EXISTS digital_schedules (
id TEXT PRIMARY KEY,
remote_id TEXT,
plan_id TEXT,
name TEXT NOT NULL,
description TEXT,
cron_expr TEXT NOT NULL,
timezone TEXT NOT NULL DEFAULT 'system',
status TEXT NOT NULL DEFAULT 'enabled',
enabled INTEGER NOT NULL DEFAULT 1,
prompt TEXT,
command TEXT,
payload TEXT NOT NULL DEFAULT '{}',
next_run_at TEXT,
last_run_at TEXT,
catch_up_on_startup INTEGER NOT NULL DEFAULT 1,
max_catch_up_runs INTEGER NOT NULL DEFAULT 5,
max_run_history INTEGER NOT NULL DEFAULT 100,
failure_count INTEGER NOT NULL DEFAULT 0,
last_error TEXT,
sync_status TEXT NOT NULL DEFAULT 'pending',
last_synced_at TEXT,
sync_error TEXT,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
deleted_at TEXT,
FOREIGN KEY(plan_id) REFERENCES digital_plans(id)
);
CREATE TABLE IF NOT EXISTS digital_schedule_runs (
id TEXT PRIMARY KEY,
remote_id TEXT,
schedule_id TEXT NOT NULL,
plan_id TEXT,
run_id TEXT,
due_at TEXT NOT NULL,
started_at TEXT,
finished_at TEXT,
status TEXT NOT NULL,
attempt_no INTEGER NOT NULL DEFAULT 1,
trigger_kind TEXT NOT NULL DEFAULT 'scheduled',
error TEXT,
payload TEXT NOT NULL DEFAULT '{}',
sync_status TEXT NOT NULL DEFAULT 'pending',
last_synced_at TEXT,
sync_error TEXT,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
FOREIGN KEY(schedule_id) REFERENCES digital_schedules(id),
FOREIGN KEY(plan_id) REFERENCES digital_plans(id),
FOREIGN KEY(run_id) REFERENCES digital_runs(id)
);
CREATE INDEX IF NOT EXISTS idx_digital_schedules_due ON digital_schedules(enabled, status, next_run_at);
CREATE INDEX IF NOT EXISTS idx_digital_schedules_plan ON digital_schedules(plan_id);
CREATE INDEX IF NOT EXISTS idx_digital_schedules_sync ON digital_schedules(sync_status);
CREATE INDEX IF NOT EXISTS idx_digital_schedule_runs_schedule ON digital_schedule_runs(schedule_id, due_at);
CREATE UNIQUE INDEX IF NOT EXISTS idx_digital_schedule_runs_attempt ON digital_schedule_runs(schedule_id, due_at, attempt_no);
CREATE INDEX IF NOT EXISTS idx_digital_schedule_runs_sync ON digital_schedule_runs(sync_status);
CREATE TABLE IF NOT EXISTS digital_memories (
id TEXT PRIMARY KEY,
remote_id TEXT,
key TEXT NOT NULL,
content TEXT NOT NULL,
category TEXT NOT NULL DEFAULT 'fact',
source TEXT NOT NULL DEFAULT 'qimingclaw',
source_path TEXT,
session_id TEXT,
score REAL,
status TEXT NOT NULL DEFAULT 'active',
payload TEXT NOT NULL DEFAULT '{}',
sync_status TEXT NOT NULL DEFAULT 'pending',
last_synced_at TEXT,
sync_error TEXT,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
deleted_at TEXT
);
CREATE INDEX IF NOT EXISTS idx_digital_memories_key ON digital_memories(key, status, updated_at);
CREATE INDEX IF NOT EXISTS idx_digital_memories_category ON digital_memories(category, status, updated_at);
CREATE INDEX IF NOT EXISTS idx_digital_memories_sync ON digital_memories(sync_status);
`;
function runStep(title, command, args, options = {}) {
console.log(`\n[DigitalEmployeeCheck] ${title}`);
const result = spawnSync(command, args, {
cwd: packageRoot,
stdio: "inherit",
shell: process.platform === "win32",
...options,
});
if (result.status !== 0) {
process.exitCode = result.status || 1;
throw new Error(`${title} failed`);
}
}
function checkSgrobotDigitalAssets() {
console.log("\n[DigitalEmployeeCheck] Verify embedded digital assets");
const indexPath = path.join(packageRoot, "public", "sgrobot-digital", "index.html");
const html = fs.readFileSync(indexPath, "utf8");
const assetRefs = [...html.matchAll(/(?:src|href)="\.\/(assets\/[^"]+)"/g)]
.map((match) => match[1]);
if (assetRefs.length === 0) {
throw new Error("No embedded digital assets referenced by index.html");
}
for (const assetRef of assetRefs) {
const assetPath = path.join(packageRoot, "public", "sgrobot-digital", assetRef);
if (!fs.existsSync(assetPath)) {
throw new Error(`Missing embedded digital asset: ${assetRef}`);
}
}
console.log(`[DigitalEmployeeCheck] ${assetRefs.length} asset references OK`);
}
function checkDigitalDiagnosticAuditProjection() {
console.log("\n[DigitalEmployeeCheck] Verify diagnostic/audit projection hooks");
const adapterPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "lib", "qimingclawAdapter.ts");
const typesPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "types", "api.ts");
const pagePath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "pages", "digital", "CommandDeck.tsx");
const syncServicePath = path.join(packageRoot, "src", "main", "services", "digitalEmployee", "syncService.ts");
const adapter = fs.readFileSync(adapterPath, "utf8");
const types = fs.readFileSync(typesPath, "utf8");
const page = fs.readFileSync(pagePath, "utf8");
const syncService = fs.readFileSync(syncServicePath, "utf8");
const requiredSnippets = [
[syncService, "sandboxAuditBusinessView", "sandbox audit sync business view"],
[syncService, "tokenCostBusinessView", "token/cost sync business view"],
[syncService, "toolCallAuditBusinessView", "tool call sync business view"],
[syncService, "sandbox_audit", "sandbox audit business category"],
[syncService, "token_cost", "token cost business category"],
[syncService, "tool_call_audit", "tool call business category"],
[syncService, "estimated_cost_usd", "safe estimated cost field"],
[syncService, "total_tokens", "safe total token field"],
[syncService, "input_length", "sensitive input length redaction"],
[adapter, "'audits'", "audit business view kind"],
[adapter, "buildDiagnosticSummary", "diagnostic summary builder"],
[adapter, "buildCostSummary", "cost summary builder"],
[adapter, "costSampleFromPayload", "runtime token/cost aggregation"],
[adapter, "diagnosticAuditPayload", "diagnostic audit payload redaction"],
[adapter, "sandbox_audit", "sandbox audit row category"],
[adapter, "token_cost", "token cost row category"],
[adapter, "tool_call_audit", "tool call row category"],
[adapter, "estimated_cost_usd", "audit estimated cost field"],
[adapter, "total_tokens", "audit total token field"],
[adapter, "filterAuditRows", "audit filters"],
[adapter, "diagnosticNotifications", "diagnostic notifications"],
[adapter, "auditNotifications", "audit notifications"],
[types, "DigitalEmployeeDiagnosticSummary", "diagnostic summary type"],
[types, "diagnostic_summary?: DigitalEmployeeDiagnosticSummary", "workday diagnostic field"],
[types, "estimated_cost_usd", "diagnostic summary estimated cost type"],
[types, "total_tokens", "diagnostic summary token type"],
[page, "de-diagnostic-summary-strip", "dashboard diagnostic strip"],
[page, "Token", "dashboard Token metric"],
[page, "估算成本", "dashboard estimated cost metric"],
];
const missing = requiredSnippets
.filter(([content, snippet]) => !content.includes(snippet))
.map(([, , label]) => label);
if (missing.length > 0) {
throw new Error(`Missing diagnostic/audit projection hooks: ${missing.join(", ")}`);
}
console.log("[DigitalEmployeeCheck] diagnostic/audit projection hooks OK");
}
function checkDigitalNotificationPreferences() {
console.log("\n[DigitalEmployeeCheck] Verify notification preference hooks");
const adapterPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "lib", "qimingclawAdapter.ts");
const apiPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "lib", "api.ts");
const typesPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "types", "api.ts");
const shellPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "pages", "digital", "DigitalEmployeeShell.tsx");
const stateServicePath = path.join(packageRoot, "src", "main", "services", "digitalEmployee", "stateService.ts");
const syncServicePath = path.join(packageRoot, "src", "main", "services", "digitalEmployee", "syncService.ts");
const desktopNotificationServicePath = path.join(packageRoot, "src", "main", "services", "digitalEmployee", "desktopNotificationService.ts");
const ipcHandlersPath = path.join(packageRoot, "src", "main", "ipc", "digitalEmployeeHandlers.ts");
const preloadPath = path.join(packageRoot, "src", "preload", "webviewPerfBridge.ts");
const adapter = fs.readFileSync(adapterPath, "utf8");
const api = fs.readFileSync(apiPath, "utf8");
const types = fs.readFileSync(typesPath, "utf8");
const shell = fs.readFileSync(shellPath, "utf8");
const stateService = fs.readFileSync(stateServicePath, "utf8");
const syncService = fs.readFileSync(syncServicePath, "utf8");
const desktopNotificationService = fs.readFileSync(desktopNotificationServicePath, "utf8");
const ipcHandlers = fs.readFileSync(ipcHandlersPath, "utf8");
const preload = fs.readFileSync(preloadPath, "utf8");
const requiredSnippets = [
[adapter, "/api/notifications/preferences", "adapter notification preference endpoint"],
[adapter, "route-decisions|notifications|audits", "management notification business route"],
[adapter, "notificationRows", "management notification rows"],
[adapter, "filterNotificationRows", "management notification filters"],
[adapter, "muted_by_preferences", "management notification muted marker"],
[adapter, "recordNotificationTaskInput", "notification reply task input bridge"],
[adapter, "notification_reply", "notification reply task input source"],
[adapter, "task_input_recorded", "notification reply task input result"],
[adapter, "provide_task_input", "notification reply governance command"],
[adapter, "handleNotificationPreferencesPatch", "adapter notification preference patch handler"],
[adapter, "/api/notifications/updates/pull", "adapter notification updates pull endpoint"],
[adapter, "pullBridgeNotificationUpdates", "adapter notification updates bridge pull helper"],
[adapter, "submitNotificationAction", "adapter notification action bridge declaration"],
[adapter, "submitBridgeNotificationAction", "adapter notification action bridge submit helper"],
[adapter, "showDesktopNotification", "adapter desktop notification bridge declaration"],
[adapter, "/api/notifications/desktop", "adapter desktop notification endpoint"],
[adapter, "/api/notifications/desktop/status", "adapter desktop notification status endpoint"],
[adapter, "/api/notifications/desktop/settings/open", "adapter desktop notification settings endpoint"],
[adapter, "showBridgeDesktopNotification", "adapter desktop notification helper"],
[adapter, "readBridgeDesktopNotificationStatus", "adapter desktop notification status helper"],
[adapter, "openBridgeDesktopNotificationSettings", "adapter desktop notification settings helper"],
[adapter, "notificationMatchesPreferences", "adapter notification preference filter"],
[adapter, "desktop_enabled", "adapter notification desktop preference"],
[adapter, "update_notification_preferences", "adapter notification preference save action"],
[api, "getNotificationPreferences", "notification preferences API getter"],
[api, "patchNotificationPreferences", "notification preferences API patcher"],
[api, "pullNotificationUpdates", "embedded notification updates API"],
[api, "showDesktopNotification", "embedded desktop notification API"],
[api, "getDesktopNotificationStatus", "embedded desktop notification status API"],
[api, "openDesktopNotificationSettings", "embedded desktop notification settings API"],
[types, "NotificationPreferences", "notification preferences type"],
[types, "DesktopNotificationStatus", "desktop notification status type"],
[types, "DesktopNotificationSettingsOpenResult", "desktop notification settings result type"],
[types, "desktop_enabled", "notification desktop preference type field"],
[types, "NotificationUpdatesPullResult", "notification updates pull result type"],
[shell, "de-notification-preferences", "notification preferences UI"],
[shell, "桌面通知状态", "desktop notification status UI"],
[shell, "测试桌面通知", "desktop notification test UI"],
[shell, "打开系统设置", "desktop notification settings UI"],
[shell, "自动桌面通知发送失败", "automatic desktop notification failure feedback"],
[shell, "handleNotificationSync", "notification manual sync UI action"],
[shell, "showUnreadDesktopNotifications", "notification desktop trigger helper"],
[shell, "qimingclaw:digital-desktop-notified", "notification desktop dedupe key"],
[shell, "桌面通知", "notification desktop preference UI"],
[shell, "NOTIFICATION_KIND_OPTIONS", "notification kind mute UI options"],
[stateService, "update_notification_preferences", "notification preference runtime event"],
[stateService, "desktop_enabled", "notification desktop preference state field"],
[desktopNotificationService, "showDigitalEmployeeDesktopNotification", "desktop notification service"],
[desktopNotificationService, "getDigitalEmployeeDesktopNotificationStatus", "desktop notification status service"],
[desktopNotificationService, "openDigitalEmployeeDesktopNotificationSettings", "desktop notification settings service"],
[desktopNotificationService, "Notification", "Electron Notification usage"],
[desktopNotificationService, "shell.openExternal", "Electron notification settings opener"],
[desktopNotificationService, "notification_unavailable", "desktop notification unavailable fallback"],
[stateService, "notification_updates_pulled", "notification updates pulled runtime event"],
[stateService, "notification_action_submitted", "notification action submitted runtime event"],
[syncService, "pullDigitalEmployeeNotificationUpdates", "remote notification updates pull service"],
[syncService, "submitDigitalEmployeeNotificationAction", "remote notification action submit service"],
[syncService, "/api/digital-employee/notifications/updates", "remote notification updates default path"],
[syncService, "/api/digital-employee/notifications/actions", "remote notification actions default path"],
[syncService, "notificationUpdatesEndpoint", "notification updates endpoint config"],
[syncService, "notificationActionsEndpoint", "notification actions endpoint config"],
[syncService, "notificationBusinessView", "notification sync business view"],
[syncService, "digital_workday_notification_updates_pulled", "notification updates sync event kind"],
[syncService, "digital_workday_notification_action_submitted", "notification action sync event kind"],
[syncService, "governanceCommandBusinessView", "notification reply governance sync view"],
[syncService, "provide_task_input", "task input governance command kind"],
[syncService, "input_length", "task input sync redacted length"],
[syncService, "isNotificationEvent", "notification event classifier"],
[syncService, "return \"notification\"", "notification business category"],
[ipcHandlers, "digitalEmployee:pullNotificationUpdates", "IPC notification updates pull handler"],
[ipcHandlers, "digitalEmployee:submitNotificationAction", "IPC notification action submit handler"],
[ipcHandlers, "digitalEmployee:showDesktopNotification", "IPC desktop notification handler"],
[ipcHandlers, "digitalEmployee:getDesktopNotificationStatus", "IPC desktop notification status handler"],
[ipcHandlers, "digitalEmployee:openDesktopNotificationSettings", "IPC desktop notification settings handler"],
[preload, "pullNotificationUpdates", "preload notification updates bridge"],
[preload, "submitNotificationAction", "preload notification action bridge"],
[preload, "showDesktopNotification", "preload desktop notification bridge"],
[preload, "getDesktopNotificationStatus", "preload desktop notification status bridge"],
[preload, "openDesktopNotificationSettings", "preload desktop notification settings bridge"],
];
const missing = requiredSnippets
.filter(([content, snippet]) => !content.includes(snippet))
.map(([, , label]) => label);
if (missing.length > 0) {
throw new Error(`Missing notification preference hooks: ${missing.join(", ")}`);
}
console.log("[DigitalEmployeeCheck] notification preference hooks OK");
}
function checkDigitalApprovalHistorySubscriptions() {
console.log("\n[DigitalEmployeeCheck] Verify approval history/subscription hooks");
const adapterPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "lib", "qimingclawAdapter.ts");
const apiPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "lib", "api.ts");
const typesPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "types", "api.ts");
const stateServicePath = path.join(packageRoot, "src", "main", "services", "digitalEmployee", "stateService.ts");
const syncServicePath = path.join(packageRoot, "src", "main", "services", "digitalEmployee", "syncService.ts");
const adapter = fs.readFileSync(adapterPath, "utf8");
const api = fs.readFileSync(apiPath, "utf8");
const types = fs.readFileSync(typesPath, "utf8");
const stateService = fs.readFileSync(stateServicePath, "utf8");
const syncService = fs.readFileSync(syncServicePath, "utf8");
const requiredSnippets = [
[adapter, "/api/approval/subscriptions", "adapter approval subscription endpoint"],
[adapter, "/api/digital-employee/approval-history", "adapter approval history endpoint"],
[adapter, "approvalHistoryRows", "management approval history rows"],
[adapter, "filterApprovalHistoryRows", "management approval history filters"],
[adapter, "approvalSubscriptionPreferences", "adapter approval subscription preferences"],
[adapter, "update_approval_subscriptions", "adapter approval subscription save action"],
[adapter, "approval-history", "approval history business route"],
[api, "getApprovalSubscriptionPreferences", "approval subscription API getter"],
[api, "patchApprovalSubscriptionPreferences", "approval subscription API patcher"],
[api, "getApprovalHistory", "approval history API getter"],
[types, "ApprovalSubscriptionPreferences", "approval subscription preferences type"],
[stateService, "approvalSubscriptionPreferences", "approval subscription UI state"],
[stateService, "update_approval_subscriptions", "approval subscription runtime event"],
[syncService, "approvalDecisionBusinessView", "approval decision sync business view"],
[syncService, "approvalSubscriptionBusinessView", "approval subscription sync business view"],
[syncService, "digital_workday_update_approval_subscriptions", "approval subscription sync event kind"],
];
const missing = requiredSnippets
.filter(([content, snippet]) => !content.includes(snippet))
.map(([, , label]) => label);
if (missing.length > 0) {
throw new Error(`Missing approval history/subscription hooks: ${missing.join(", ")}`);
}
console.log("[DigitalEmployeeCheck] approval history/subscription hooks OK");
}
function checkDigitalApprovalWorkflowSync() {
console.log("\n[DigitalEmployeeCheck] Verify approval workflow/sync hooks");
const adapterPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "lib", "qimingclawAdapter.ts");
const apiPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "lib", "api.ts");
const typesPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "types", "api.ts");
const commandDeckPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "pages", "digital", "CommandDeck.tsx");
const stateServicePath = path.join(packageRoot, "src", "main", "services", "digitalEmployee", "stateService.ts");
const syncServicePath = path.join(packageRoot, "src", "main", "services", "digitalEmployee", "syncService.ts");
const ipcHandlersPath = path.join(packageRoot, "src", "main", "ipc", "digitalEmployeeHandlers.ts");
const preloadPath = path.join(packageRoot, "src", "preload", "webviewPerfBridge.ts");
const adapter = fs.readFileSync(adapterPath, "utf8");
const api = fs.readFileSync(apiPath, "utf8");
const types = fs.readFileSync(typesPath, "utf8");
const commandDeck = fs.readFileSync(commandDeckPath, "utf8");
const stateService = fs.readFileSync(stateServicePath, "utf8");
const syncService = fs.readFileSync(syncServicePath, "utf8");
const ipcHandlers = fs.readFileSync(ipcHandlersPath, "utf8");
const preload = fs.readFileSync(preloadPath, "utf8");
const requiredSnippets = [
[stateService, "DigitalEmployeeApprovalWorkflow", "approval workflow type"],
[stateService, "normalizeDigitalEmployeeApprovalWorkflow", "approval workflow normalizer"],
[stateService, "applyApprovalWorkflowDecision", "approval workflow decision advancer"],
[stateService, "applyApprovalWorkflowAction", "approval workflow action updater"],
[stateService, "upsertDigitalEmployeeApprovalFromRemote", "remote approval update upsert helper"],
[stateService, "resolveDigitalEmployeeApprovalConflict", "approval conflict resolution helper"],
[stateService, "approval_conflict_detected", "approval conflict runtime event"],
[stateService, "approval_conflict_resolved", "approval conflict resolved event"],
[stateService, "terminal_status_mismatch", "approval terminal conflict reason"],
[stateService, "decision_history", "approval workflow decision history field"],
[syncService, "pullDigitalEmployeeApprovalUpdates", "remote approval updates pull service"],
[syncService, "conflicted", "approval pull conflict counter"],
[syncService, "submitDigitalEmployeeApprovalDecision", "approval decision submit service"],
[syncService, "/api/digital-employee/approvals/updates", "remote approval updates default path"],
[syncService, "/api/digital-employee/approvals/decisions", "remote approval decisions default path"],
[syncService, "approvalUpdatesEndpoint", "approval updates endpoint config"],
[syncService, "approvalDecisionsEndpoint", "approval decisions endpoint config"],
[syncService, "approvalSyncBusinessView", "approval sync business view"],
[syncService, "approvalConflictResolutionBusinessView", "approval conflict resolution business view"],
[syncService, "workflow_id", "approval workflow sync business field"],
[ipcHandlers, "digitalEmployee:pullApprovalUpdates", "IPC approval updates pull handler"],
[ipcHandlers, "digitalEmployee:submitApprovalDecision", "IPC approval decision submit handler"],
[ipcHandlers, "digitalEmployee:resolveApprovalConflict", "IPC approval conflict resolution handler"],
[preload, "pullApprovalUpdates", "preload approval updates bridge"],
[preload, "submitApprovalDecision", "preload approval decision bridge"],
[preload, "resolveApprovalConflict", "preload approval conflict resolution bridge"],
[adapter, "pullApprovalUpdates", "adapter approval updates bridge declaration"],
[adapter, "submitApprovalDecision", "adapter approval decision bridge declaration"],
[adapter, "resolveApprovalConflict", "adapter approval conflict resolution bridge declaration"],
[adapter, "/api/approval/updates/pull", "adapter approval updates pull endpoint"],
[adapter, "conflict\\/resolve", "adapter approval conflict resolution endpoint"],
[adapter, "approvalWorkflowSummary", "adapter approval workflow summary"],
[adapter, "approvalConflictSummary", "adapter approval conflict summary"],
[adapter, "approval_conflict_active", "approval conflict business field"],
[adapter, "approval_conflict_resolution", "approval conflict resolution business field"],
[api, "pullApprovalUpdates", "embedded approval updates API"],
[api, "resolveApprovalConflict", "embedded approval conflict resolution API"],
[types, "conflict?:", "approval decision conflict type field"],
[types, "current_step_label", "approval decision current step type field"],
[types, "approval_step_count", "approval decision step count type field"],
[commandDeck, "pull_approval_updates", "home approval updates pull action"],
[commandDeck, "审批更新", "home approval updates pull button"],
[commandDeck, "跨端冲突", "home approval conflict badge"],
[commandDeck, "冲突保护", "home approval conflict protection copy"],
[commandDeck, "保留本地", "home approval keep local conflict action"],
[commandDeck, "采纳管理端", "home approval accept remote conflict action"],
[commandDeck, "标记已复核", "home approval mark reviewed conflict action"],
];
const missing = requiredSnippets
.filter(([content, snippet]) => !content.includes(snippet))
.map(([, , label]) => label);
if (missing.length > 0) {
throw new Error(`Missing approval workflow/sync hooks: ${missing.join(", ")}`);
}
console.log("[DigitalEmployeeCheck] approval workflow/sync hooks OK");
}
function checkDigitalPlanStepGraphDispatchPolicy() {
console.log("\n[DigitalEmployeeCheck] Verify PlanStep graph/dispatch policy hooks");
const adapterPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "lib", "qimingclawAdapter.ts");
const apiPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "lib", "api.ts");
const typesPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "types", "api.ts");
const commandDeckPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "pages", "digital", "CommandDeck.tsx");
const cssPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "index.css");
const stateServicePath = path.join(packageRoot, "src", "main", "services", "digitalEmployee", "stateService.ts");
const schedulerServicePath = path.join(packageRoot, "src", "main", "services", "digitalEmployee", "schedulerService.ts");
const syncServicePath = path.join(packageRoot, "src", "main", "services", "digitalEmployee", "syncService.ts");
const ipcHandlersPath = path.join(packageRoot, "src", "main", "ipc", "digitalEmployeeHandlers.ts");
const preloadPath = path.join(packageRoot, "src", "preload", "webviewPerfBridge.ts");
const adapter = fs.readFileSync(adapterPath, "utf8");
const api = fs.readFileSync(apiPath, "utf8");
const types = fs.readFileSync(typesPath, "utf8");
const commandDeck = fs.readFileSync(commandDeckPath, "utf8");
const css = fs.readFileSync(cssPath, "utf8");
const stateService = fs.readFileSync(stateServicePath, "utf8");
const schedulerService = fs.readFileSync(schedulerServicePath, "utf8");
const syncService = fs.readFileSync(syncServicePath, "utf8");
const ipcHandlers = fs.readFileSync(ipcHandlersPath, "utf8");
const preload = fs.readFileSync(preloadPath, "utf8");
const requiredSnippets = [
[adapter, "/api/digital-employee/plan-steps", "adapter plan step endpoint"],
[adapter, "/api/digital-employee/plan-step-graph", "adapter plan step graph endpoint"],
[adapter, "dispatch-ready-steps", "adapter plan-scoped ready step dispatch endpoint"],
[adapter, "dispatchPlanReadySteps", "adapter plan-scoped ready step dispatcher"],
[adapter, "/api/plan-step/dispatch-policy", "adapter plan step dispatch policy endpoint"],
[adapter, "/api/plan-step/dispatch-policy/pull", "adapter plan step dispatch policy pull endpoint"],
[adapter, "pullPlanStepDispatchPolicy", "adapter plan step dispatch policy pull bridge"],
[adapter, "planStepRows", "management plan step rows"],
[adapter, "planStepGraphRows", "management plan step graph rows"],
[adapter, "filterPlanStepRows", "management plan step filters"],
[adapter, "activePlanStepDispatchLease", "adapter active plan step lease detector"],
[adapter, "lease_active", "adapter lease-active readiness skip reason"],
[adapter, "lease_state", "adapter plan step lease state projection"],
[adapter, "blocked_dependency_details", "plan step blocked dependency details"],
[adapter, "planStepDependencyDetails", "plan step blocked dependency detail mapper"],
[adapter, "update_plan_step_dispatch_policy", "adapter plan step dispatch policy save action"],
[api, "getPlanStepDispatchPolicy", "plan step dispatch policy API getter"],
[api, "patchPlanStepDispatchPolicy", "plan step dispatch policy API patcher"],
[api, "pullPlanStepDispatchPolicy", "plan step dispatch policy API puller"],
[api, "getPlanSteps", "plan steps API getter"],
[api, "getPlanStepGraph", "plan step graph API getter"],
[api, "dispatchPlanReadySteps", "plan-scoped ready step dispatch API"],
[api, "BusinessViewResponse<PlanStepBusinessRow>", "typed plan step business view API"],
[types, "trigger_source", "ready step dispatch trigger source type"],
[types, "orchestration_id", "ready step dispatch orchestration id type"],
[types, "PlanStepDispatchPolicy", "plan step dispatch policy type"],
[types, "PlanStepDispatchPolicyPullResult", "plan step dispatch policy pull result type"],
[types, "last_pull_error", "plan step dispatch policy pull error field"],
[types, "PlanStepLeaseSummary", "plan step lease summary type"],
[types, "remote_lease_id", "plan step remote lease type field"],
[types, "PlanStepDependencyDetail", "plan step blocked dependency detail type"],
[types, "lease_active", "plan step lease active type field"],
[types, "PlanStepBusinessRow", "plan step business row type"],
[types, "BusinessViewResponse", "business view response type"],
[commandDeck, "getPlanStepGraph", "home blocked plan step graph fetch"],
[commandDeck, "planStepOrchestrationSummary", "home plan step orchestration summary"],
[commandDeck, "leasedStepCount", "home plan step lease count"],
[commandDeck, "controlPlanReadySteps", "home plan-scoped ready step control"],
[commandDeck, "pull_plan_step_dispatch_policy", "home plan step dispatch policy pull action"],
[commandDeck, "controlBlockedPlanStep", "home blocked plan step control handler"],
[commandDeck, "de-blocked-step-control-strip", "home blocked plan step control strip"],
[commandDeck, "provide_task_input", "home blocked plan step input command"],
[css, "de-blocked-input-field", "home blocked plan step input styles"],
[stateService, "planStepDispatchPolicy", "plan step dispatch policy UI state"],
[stateService, "update_plan_step_dispatch_policy", "plan step dispatch policy runtime event"],
[stateService, "pull_plan_step_dispatch_policy", "plan step dispatch policy pull runtime event"],
[stateService, "last_pulled_at", "plan step dispatch policy last pulled field"],
[stateService, "last_pull_error", "plan step dispatch policy last pull error field"],
[stateService, "planIdFilter", "plan-scoped ready step candidate filter"],
[stateService, "dispatchLease", "plan step payload lease state"],
[stateService, "activeDigitalEmployeePlanStepLease", "plan step active lease helper"],
[stateService, "acquireDigitalEmployeePlanStepLease", "plan step lease acquire helper"],
[stateService, "releaseDigitalEmployeePlanStepLease", "plan step lease release helper"],
[stateService, "recordDigitalEmployeePlanStepLeaseSkipped", "plan step remote lease rejected helper"],
[stateService, "local_fallback", "plan step local fallback lease source"],
[stateService, "remote_release_error", "plan step remote release error state"],
[stateService, "plan_step_lease_acquired", "plan step lease acquired event"],
[stateService, "plan_step_lease_released", "plan step lease released event"],
[stateService, "plan_step_lease_skipped", "plan step lease skipped event"],
[schedulerService, "bypassAutoDispatchSwitch", "manual plan dispatch auto switch bypass"],
[schedulerService, "runDigitalEmployeePlanReadyStepsNow", "manual plan-scoped ready step dispatcher"],
[schedulerService, "acquireDigitalEmployeePlanStepLease", "scheduler acquire plan step lease"],
[schedulerService, "releaseDigitalEmployeePlanStepLease", "scheduler release plan step lease"],
[schedulerService, "acquireDigitalEmployeePlanStepRemoteLease", "scheduler remote plan step lease acquire"],
[schedulerService, "releaseDigitalEmployeePlanStepRemoteLease", "scheduler remote plan step lease release"],
[schedulerService, "remote_lease_rejected", "scheduler remote lease rejected skip reason"],
[schedulerService, "local_fallback", "scheduler remote lease fallback source"],
[schedulerService, "triggerSource", "ready step dispatch trigger source"],
[schedulerService, "readDigitalEmployeePlanStepDispatchPolicy", "scheduler plan step dispatch policy reader"],
[schedulerService, "pullDigitalEmployeePlanStepDispatchPolicy", "scheduler remote plan step dispatch policy pull"],
[schedulerService, "max_per_sweep", "scheduler max per sweep policy"],
[ipcHandlers, "digitalEmployee:dispatchPlanReadySteps", "IPC plan-scoped ready step dispatch handler"],
[ipcHandlers, "digitalEmployee:pullPlanStepDispatchPolicy", "IPC plan step dispatch policy pull handler"],
[preload, "dispatchPlanReadySteps", "preload plan-scoped ready step bridge"],
[preload, "pullPlanStepDispatchPolicy", "preload plan step dispatch policy pull bridge"],
[syncService, "planStepBusinessView", "plan step sync business view"],
[syncService, "pullDigitalEmployeePlanStepDispatchPolicy", "remote plan step dispatch policy pull service"],
[syncService, "planStepDispatchPolicyEndpoint", "remote plan step dispatch policy endpoint config"],
[syncService, "/api/digital-employee/policies/plan-step-dispatch", "remote plan step dispatch policy default path"],
[syncService, "acquireDigitalEmployeePlanStepRemoteLease", "remote plan step dispatch lease acquire service"],
[syncService, "releaseDigitalEmployeePlanStepRemoteLease", "remote plan step dispatch lease release service"],
[syncService, "planStepDispatchLeaseEndpoint", "remote plan step dispatch lease endpoint config"],
[syncService, "/api/digital-employee/leases/plan-step-dispatch", "remote plan step dispatch lease default path"],
[syncService, "lease_active", "plan step sync lease active field"],
[syncService, "remote_lease_error", "plan step sync remote lease error field"],
[syncService, "remote_release_error", "plan step sync remote release error field"],
[syncService, "planStepLeaseBusinessView", "plan step lease sync event view"],
[syncService, "plan_step_lease_acquired", "plan step lease acquired sync event kind"],
[syncService, "trigger_source", "plan step dispatch sync trigger source"],
[syncService, "orchestration_id", "plan step dispatch sync orchestration id"],
[syncService, "planStepDispatchPolicyBusinessView", "plan step dispatch policy sync view"],
[syncService, "digital_workday_update_plan_step_dispatch_policy", "plan step dispatch policy sync event kind"],
[syncService, "digital_workday_pull_plan_step_dispatch_policy", "plan step dispatch policy pull sync event kind"],
];
const missing = requiredSnippets
.filter(([content, snippet]) => !content.includes(snippet))
.map(([, , label]) => label);
if (missing.length > 0) {
throw new Error(`Missing PlanStep graph/dispatch policy hooks: ${missing.join(", ")}`);
}
console.log("[DigitalEmployeeCheck] PlanStep graph/dispatch policy hooks OK");
}
function checkDigitalSkillRemotePolicy() {
console.log("\n[DigitalEmployeeCheck] Verify skill remote policy hooks");
const adapterPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "lib", "qimingclawAdapter.ts");
const apiPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "lib", "api.ts");
const skillLibraryPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "pages", "digital", "SkillLibrary.tsx");
const skillExecutionPolicyPath = path.join(packageRoot, "src", "main", "services", "digitalEmployee", "skillExecutionPolicy.ts");
const skillCatalogPath = path.join(packageRoot, "src", "main", "services", "digitalEmployee", "skillCatalogService.ts");
const syncServicePath = path.join(packageRoot, "src", "main", "services", "digitalEmployee", "syncService.ts");
const ipcHandlersPath = path.join(packageRoot, "src", "main", "ipc", "digitalEmployeeHandlers.ts");
const preloadPath = path.join(packageRoot, "src", "preload", "webviewPerfBridge.ts");
const adapter = fs.readFileSync(adapterPath, "utf8");
const api = fs.readFileSync(apiPath, "utf8");
const skillLibrary = fs.readFileSync(skillLibraryPath, "utf8");
const skillExecutionPolicy = fs.readFileSync(skillExecutionPolicyPath, "utf8");
const skillCatalog = fs.readFileSync(skillCatalogPath, "utf8");
const syncService = fs.readFileSync(syncServicePath, "utf8");
const ipcHandlers = fs.readFileSync(ipcHandlersPath, "utf8");
const preload = fs.readFileSync(preloadPath, "utf8");
const requiredSnippets = [
[skillExecutionPolicy, "local_skills", "local skill policy map"],
[skillExecutionPolicy, "remote_skills", "remote skill policy map"],
[skillExecutionPolicy, "last_pull_error", "skill policy pull error metadata"],
[skillExecutionPolicy, "effectiveSkillPolicyFor", "effective skill policy merge helper"],
[skillExecutionPolicy, "matched_remote_policy", "skill call remote policy audit snapshot"],
[skillCatalog, "remote_policy", "skill catalog remote policy projection"],
[skillCatalog, "local_policy", "skill catalog local policy projection"],
[skillCatalog, "policy_source", "skill catalog policy source projection"],
[syncService, "pullDigitalEmployeeSkillPolicies", "remote skill policy pull service"],
[syncService, "skillPolicyEndpoint", "remote skill policy endpoint config"],
[syncService, "/api/digital-employee/policies/skills", "remote skill policy default path"],
[syncService, "readRemoteSkillPolicyPayload", "remote skill policy response parser"],
[syncService, "recordSkillPolicyPullFailure", "remote skill policy pull failure recorder"],
[ipcHandlers, "digitalEmployee:pullSkillPolicies", "IPC skill policy pull handler"],
[preload, "pullSkillPolicies", "preload skill policy pull bridge"],
[adapter, "pullSkillPolicies", "adapter skill policy pull bridge"],
[adapter, "/api/skill/policies/pull", "adapter skill policy pull endpoint"],
[api, "pullSkillPolicies", "embedded skill policy pull API"],
[skillLibrary, "拉取策略", "skill library pull policy action"],
[skillLibrary, "远端策略", "skill library remote policy marker"],
];
const missing = requiredSnippets
.filter(([content, snippet]) => !content.includes(snippet))
.map(([, , label]) => label);
if (missing.length > 0) {
throw new Error(`Missing skill remote policy hooks: ${missing.join(", ")}`);
}
console.log("[DigitalEmployeeCheck] skill remote policy hooks OK");
}
function checkDigitalRiskApprovalPolicy() {
console.log("\n[DigitalEmployeeCheck] Verify risk approval policy hooks");
const adapterPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "lib", "qimingclawAdapter.ts");
const apiPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "lib", "api.ts");
const commandDeckPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "pages", "digital", "CommandDeck.tsx");
const opsSettingsPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "pages", "digital", "OpsSettings.tsx");
const stateServicePath = path.join(packageRoot, "src", "main", "services", "digitalEmployee", "stateService.ts");
const syncServicePath = path.join(packageRoot, "src", "main", "services", "digitalEmployee", "syncService.ts");
const ipcHandlersPath = path.join(packageRoot, "src", "main", "ipc", "digitalEmployeeHandlers.ts");
const preloadPath = path.join(packageRoot, "src", "preload", "webviewPerfBridge.ts");
const adapter = fs.readFileSync(adapterPath, "utf8");
const api = fs.readFileSync(apiPath, "utf8");
const commandDeck = fs.readFileSync(commandDeckPath, "utf8");
const opsSettings = fs.readFileSync(opsSettingsPath, "utf8");
const stateService = fs.readFileSync(stateServicePath, "utf8");
const syncService = fs.readFileSync(syncServicePath, "utf8");
const ipcHandlers = fs.readFileSync(ipcHandlersPath, "utf8");
const preload = fs.readFileSync(preloadPath, "utf8");
const requiredSnippets = [
[stateService, "riskApprovalPolicy", "risk approval policy UI state"],
[stateService, "normalizeRiskApprovalPolicy", "risk approval policy normalizer"],
[stateService, "evaluateDigitalEmployeeRiskPolicy", "risk approval policy evaluator"],
[stateService, "risk_approval_required", "risk approval-required event kind"],
[stateService, "risk_policy_rejected", "risk policy rejected event kind"],
[stateService, "update_risk_approval_policy", "risk approval policy update UI event"],
[stateService, "pull_risk_approval_policy", "risk approval policy pull UI event"],
[syncService, "pullDigitalEmployeeRiskApprovalPolicy", "remote risk approval policy pull service"],
[syncService, "riskApprovalPolicyEndpoint", "remote risk approval policy endpoint config"],
[syncService, "/api/digital-employee/policies/risk-approval", "remote risk approval policy default path"],
[syncService, "risk_approval_required", "risk approval-required sync event kind"],
[syncService, "risk_policy_rejected", "risk policy rejected sync event kind"],
[syncService, "riskPolicyBusinessView", "risk policy sync business view"],
[ipcHandlers, "digitalEmployee:pullRiskApprovalPolicy", "IPC risk approval policy pull handler"],
[ipcHandlers, "digitalEmployee:evaluateRiskPolicy", "IPC risk policy evaluation handler"],
[preload, "pullRiskApprovalPolicy", "preload risk approval policy pull bridge"],
[preload, "evaluateRiskPolicy", "preload risk policy evaluation bridge"],
[adapter, "pullRiskApprovalPolicy", "adapter risk approval policy pull bridge"],
[adapter, "evaluateRiskPolicy", "adapter risk policy evaluation bridge"],
[adapter, "/api/risk-approval/policy", "adapter risk approval policy local endpoint"],
[adapter, "handleRiskApprovalPolicyPatch", "adapter risk approval policy patch handler"],
[adapter, "/api/risk-approval/policy/pull", "adapter risk approval policy pull endpoint"],
[adapter, "riskPolicyBlockedResponse", "adapter risk policy blocked response"],
[adapter, "governanceCommandRiskLevel", "adapter governance risk level mapper"],
[adapter, "managed_service.restart", "managed service restart risk action"],
[adapter, "artifact.access", "artifact access risk action"],
[adapter, "plan_step.dispatch_ready_steps", "ready PlanStep dispatch risk action"],
[api, "getRiskApprovalPolicy", "embedded risk approval policy getter"],
[api, "patchRiskApprovalPolicy", "embedded risk approval policy patcher"],
[api, "pullRiskApprovalPolicy", "embedded risk approval policy pull API"],
[opsSettings, "riskPolicy", "settings risk policy state"],
[opsSettings, "updateRiskPolicy", "settings risk policy update handler"],
[opsSettings, "风险审批策略", "settings risk approval policy panel"],
[commandDeck, "pull_risk_approval_policy", "home risk approval policy pull action"],
[commandDeck, "风险策略", "home risk approval policy pull button"],
];
const missing = requiredSnippets
.filter(([content, snippet]) => !content.includes(snippet))
.map(([, , label]) => label);
if (missing.length > 0) {
throw new Error(`Missing risk approval policy hooks: ${missing.join(", ")}`);
}
console.log("[DigitalEmployeeCheck] risk approval policy hooks OK");
}
function checkDigitalRouteDecisionPolicy() {
const adapterPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "lib", "qimingclawAdapter.ts");
const apiPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "lib", "api.ts");
const commandDeckPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "pages", "digital", "CommandDeck.tsx");
const typesPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "types", "api.ts");
const stateServicePath = path.join(packageRoot, "src", "main", "services", "digitalEmployee", "stateService.ts");
const syncServicePath = path.join(packageRoot, "src", "main", "services", "digitalEmployee", "syncService.ts");
const ipcHandlersPath = path.join(packageRoot, "src", "main", "ipc", "digitalEmployeeHandlers.ts");
const preloadPath = path.join(packageRoot, "src", "preload", "webviewPerfBridge.ts");
const adapter = fs.readFileSync(adapterPath, "utf8");
const api = fs.readFileSync(apiPath, "utf8");
const commandDeck = fs.readFileSync(commandDeckPath, "utf8");
const types = fs.readFileSync(typesPath, "utf8");
const stateService = fs.readFileSync(stateServicePath, "utf8");
const syncService = fs.readFileSync(syncServicePath, "utf8");
const ipcHandlers = fs.readFileSync(ipcHandlersPath, "utf8");
const preload = fs.readFileSync(preloadPath, "utf8");
const requiredSnippets = [
[stateService, "routeDecisionPolicy", "route decision policy UI state"],
[stateService, "normalizeRouteDecisionPolicy", "route decision policy normalizer"],
[stateService, "evaluateDigitalEmployeeRouteDecisionPolicy", "route decision policy evaluator"],
[stateService, "listDigitalEmployeeRouteInterventions", "route intervention list helper"],
[stateService, "recordDigitalEmployeeRouteInterventionResolution", "route intervention resolution recorder"],
[stateService, "route_decision_blocked", "route decision blocked event kind"],
[stateService, "route_decision_approval_required", "route decision approval-required event kind"],
[stateService, "route_decision_intervention_resolved", "route decision intervention resolved event kind"],
[syncService, "pullDigitalEmployeeRouteDecisionPolicy", "remote route decision policy pull service"],
[syncService, "routeDecisionPolicyEndpoint", "remote route decision policy endpoint config"],
[syncService, "/api/digital-employee/policies/route-decision", "remote route decision policy default path"],
[syncService, "routeDecisionPolicyBusinessView", "route decision policy sync business view"],
[syncService, "routeDecisionInterventionBusinessView", "route decision intervention sync business view"],
[syncService, "governanceCommandBusinessView", "governance command sync business view"],
[syncService, "kind.startsWith(\"governance_\")", "governance event business category"],
[syncService, "input_length", "governance task input redaction field"],
[ipcHandlers, "digitalEmployee:pullRouteDecisionPolicy", "IPC route decision policy pull handler"],
[ipcHandlers, "digitalEmployee:evaluateRouteDecisionPolicy", "IPC route decision policy evaluation handler"],
[ipcHandlers, "digitalEmployee:listRouteInterventions", "IPC route intervention list handler"],
[ipcHandlers, "digitalEmployee:recordRouteInterventionResolution", "IPC route intervention resolution handler"],
[preload, "pullRouteDecisionPolicy", "preload route decision policy pull bridge"],
[preload, "evaluateRouteDecisionPolicy", "preload route decision policy evaluation bridge"],
[preload, "listRouteInterventions", "preload route intervention list bridge"],
[preload, "recordRouteInterventionResolution", "preload route intervention resolution bridge"],
[adapter, "pullRouteDecisionPolicy", "adapter route decision policy pull bridge"],
[adapter, "evaluateRouteDecisionPolicy", "adapter route decision policy evaluation bridge"],
[adapter, "/api/route-decision/policy/pull", "adapter route decision policy pull endpoint"],
[adapter, "/api/route-decisions/interventions", "adapter route intervention list endpoint"],
[adapter, "handleRouteInterventionDecision", "adapter route intervention decision handler"],
[adapter, "resolveApprovedRouteInterventions", "adapter approved route intervention auto resolver"],
[adapter, "auto_create_governance_commands", "adapter route command auto-create switch"],
[adapter, "routeGovernanceActionPolicyReason", "adapter route governance action guard"],
[adapter, "route_action_policy_blocked", "adapter route action policy blocked reason"],
[api, "pullRouteDecisionPolicy", "embedded route decision policy pull API"],
[api, "decideRouteIntervention", "embedded route intervention decision API"],
[types, "RouteDecisionPolicy", "embedded route decision policy type"],
[types, "RouteInterventionRecord", "embedded route intervention type"],
[types, "governance_blocked_count", "route governance blocked summary type"],
[commandDeck, "pull_route_decision_policy", "home route decision policy pull action"],
[commandDeck, "路由策略", "home route decision policy pull button"],
[commandDeck, "治理审计", "home governance audit summary"],
];
const missing = requiredSnippets
.filter(([content, snippet]) => !content.includes(snippet))
.map(([, , label]) => label);
if (missing.length > 0) {
throw new Error(`Missing route decision policy hooks: ${missing.join(", ")}`);
}
console.log("[DigitalEmployeeCheck] route decision policy hooks OK");
}
function checkDigitalDailyReportDelivery() {
console.log("\n[DigitalEmployeeCheck] Verify daily report delivery hooks");
const adapterPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "lib", "qimingclawAdapter.ts");
const apiPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "lib", "api.ts");
const typesPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "types", "api.ts");
const pagePath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "pages", "digital", "DailyReport.tsx");
const stateServicePath = path.join(packageRoot, "src", "main", "services", "digitalEmployee", "stateService.ts");
const syncServicePath = path.join(packageRoot, "src", "main", "services", "digitalEmployee", "syncService.ts");
const ipcHandlersPath = path.join(packageRoot, "src", "main", "ipc", "digitalEmployeeHandlers.ts");
const preloadPath = path.join(packageRoot, "src", "preload", "webviewPerfBridge.ts");
const adapter = fs.readFileSync(adapterPath, "utf8");
const api = fs.readFileSync(apiPath, "utf8");
const types = fs.readFileSync(typesPath, "utf8");
const page = fs.readFileSync(pagePath, "utf8");
const stateService = fs.readFileSync(stateServicePath, "utf8");
const syncService = fs.readFileSync(syncServicePath, "utf8");
const ipcHandlers = fs.readFileSync(ipcHandlersPath, "utf8");
const preload = fs.readFileSync(preloadPath, "utf8");
const requiredSnippets = [
[stateService, "htmlArtifactId", "daily report HTML artifact result"],
[stateService, "pdfArtifactId", "daily report PDF artifact result"],
[stateService, "recordDigitalEmployeeDailyReportDelivery", "daily report delivery recorder"],
[stateService, "daily_report_send_recorded", "daily report send event"],
[stateService, "daily_report_confirmed", "daily report confirm event"],
[stateService, "daily_report_approval_requested", "daily report approval event"],
[syncService, "dailyReportEventBusinessView", "daily report sync business view"],
[syncService, "daily_report", "daily report sync category"],
[ipcHandlers, "digitalEmployee:recordDailyReportDelivery", "IPC daily report delivery handler"],
[preload, "recordDailyReportDelivery", "preload daily report delivery bridge"],
[adapter, "/api/digital-employee/daily-reports/delivery", "adapter daily report delivery endpoint"],
[adapter, "buildDailyReportHtml", "adapter HTML report builder"],
[adapter, "buildDailyReportPdfBase64", "adapter PDF report builder"],
[api, "recordDailyReportDelivery", "embedded daily report delivery API"],
[types, "html_artifact_id", "daily report HTML artifact type"],
[types, "pdf_artifact_id", "daily report PDF artifact type"],
[page, "导出 HTML", "daily report HTML export UI"],
[page, "导出 PDF", "daily report PDF export UI"],
[page, "记录发送", "daily report send UI"],
[page, "确认回执", "daily report confirm UI"],
[page, "发起审批", "daily report approval UI"],
];
const missing = requiredSnippets
.filter(([content, snippet]) => !content.includes(snippet))
.map(([, , label]) => label);
if (missing.length > 0) {
throw new Error(`Missing daily report delivery hooks: ${missing.join(", ")}`);
}
console.log("[DigitalEmployeeCheck] daily report delivery hooks OK");
}
function checkDigitalArtifactCleanup() {
console.log("\n[DigitalEmployeeCheck] Verify artifact cleanup execution hooks");
const adapterPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "lib", "qimingclawAdapter.ts");
const apiPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "lib", "api.ts");
const typesPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "types", "api.ts");
const pagePath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "pages", "digital", "DailyReport.tsx");
const businessViewPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "pages", "digital", "BusinessView.tsx");
const stateServicePath = path.join(packageRoot, "src", "main", "services", "digitalEmployee", "stateService.ts");
const artifactServicePath = path.join(packageRoot, "src", "main", "services", "digitalEmployee", "artifactAccessService.ts");
const syncServicePath = path.join(packageRoot, "src", "main", "services", "digitalEmployee", "syncService.ts");
const ipcHandlersPath = path.join(packageRoot, "src", "main", "ipc", "digitalEmployeeHandlers.ts");
const preloadPath = path.join(packageRoot, "src", "preload", "webviewPerfBridge.ts");
const adapter = fs.readFileSync(adapterPath, "utf8");
const api = fs.readFileSync(apiPath, "utf8");
const types = fs.readFileSync(typesPath, "utf8");
const page = fs.readFileSync(pagePath, "utf8");
const businessView = fs.readFileSync(businessViewPath, "utf8");
const stateService = fs.readFileSync(stateServicePath, "utf8");
const artifactService = fs.readFileSync(artifactServicePath, "utf8");
const syncService = fs.readFileSync(syncServicePath, "utf8");
const ipcHandlers = fs.readFileSync(ipcHandlersPath, "utf8");
const preload = fs.readFileSync(preloadPath, "utf8");
const requiredSnippets = [
[artifactService, "cleanupDigitalEmployeeArtifact", "artifact cleanup service"],
[stateService, "recordDigitalEmployeeArtifactCleanup", "artifact cleanup event recorder"],
[stateService, "artifact_cleanup_executed", "artifact cleanup executed event"],
[stateService, "artifact_cleanup_rejected", "artifact cleanup rejected event"],
[syncService, "cleanup_status", "artifact cleanup sync business field"],
[ipcHandlers, "digitalEmployee:cleanupArtifact", "IPC artifact cleanup handler"],
[preload, "cleanupArtifact", "preload artifact cleanup bridge"],
[adapter, "/cleanup", "adapter artifact cleanup endpoint"],
[adapter, "artifactCleanupRows", "adapter artifact cleanup projection"],
[api, "cleanupArtifact", "embedded artifact cleanup API"],
[types, "cleanup_status", "artifact cleanup type field"],
[page, "清理文件", "daily report cleanup UI"],
[businessView, "cleanup_status", "business view cleanup summary"],
];
const missing = requiredSnippets
.filter(([content, snippet]) => !content.includes(snippet))
.map(([, , label]) => label);
if (missing.length > 0) {
throw new Error(`Missing artifact cleanup execution hooks: ${missing.join(", ")}`);
}
console.log("[DigitalEmployeeCheck] artifact cleanup execution hooks OK");
}
function checkDigitalArtifactBatchGovernance() {
console.log("\n[DigitalEmployeeCheck] Verify artifact batch governance hooks");
const adapterPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "lib", "qimingclawAdapter.ts");
const apiPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "lib", "api.ts");
const typesPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "types", "api.ts");
const adapter = fs.readFileSync(adapterPath, "utf8");
const api = fs.readFileSync(apiPath, "utf8");
const types = fs.readFileSync(typesPath, "utf8");
const businessViewPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "pages", "digital", "BusinessView.tsx");
const businessView = fs.existsSync(businessViewPath) ? fs.readFileSync(businessViewPath, "utf8") : "";
const dailyReportPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "pages", "digital", "DailyReport.tsx");
const dailyReport = fs.readFileSync(dailyReportPath, "utf8");
const docs = fs.readFileSync(path.join(packageRoot, "..", "..", "docs", "digital-employee-frontend-integration-plan.md"), "utf8");
const requiredSnippets = [
[adapter, "handleArtifactRetentionBatch", "adapter artifact retention batch handler"],
[adapter, "/retention/batch", "adapter artifact retention batch endpoint"],
[adapter, "artifactGroupRows", "adapter artifact aggregate rows"],
[adapter, "/artifact-groups", "adapter artifact groups endpoint"],
[api, "setArtifactRetentionBatch", "embedded artifact retention batch API"],
[types, "artifact-groups", "business view artifact groups kind"],
[types, "artifact_count", "artifact aggregate count type field"],
[types, "device_count", "artifact aggregate device count type field"],
[businessView, "产物聚合", "business view artifact groups tab"],
[businessView, "批量保留", "business view batch retention UI"],
[dailyReport, "批量标记到期", "daily report batch retention action"],
[docs, "保留策略批量治理", "docs artifact batch governance absorption"],
[docs, "跨设备聚合视图", "docs artifact aggregate view absorption"],
];
const missing = requiredSnippets
.filter(([content, snippet]) => !content.includes(snippet))
.map(([, , label]) => label);
if (missing.length > 0) {
throw new Error(`Missing artifact batch governance hooks: ${missing.join(", ")}`);
}
console.log("[DigitalEmployeeCheck] artifact batch governance hooks OK");
}
function checkDigitalBusinessViewUi() {
console.log("\n[DigitalEmployeeCheck] Verify management business view UI hooks");
const apiPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "lib", "api.ts");
const typesPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "types", "api.ts");
const mainPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "main.tsx");
const shellPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "pages", "digital", "DigitalEmployeeShell.tsx");
const navPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "pages", "digital", "navigation.ts");
const businessViewPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "pages", "digital", "BusinessView.tsx");
if (!fs.existsSync(businessViewPath)) {
throw new Error("Missing management business view UI: BusinessView.tsx");
}
const api = fs.readFileSync(apiPath, "utf8");
const types = fs.readFileSync(typesPath, "utf8");
const main = fs.readFileSync(mainPath, "utf8");
const shell = fs.readFileSync(shellPath, "utf8");
const nav = fs.readFileSync(navPath, "utf8");
const businessView = fs.readFileSync(businessViewPath, "utf8");
const requiredSnippets = [
[api, "getDigitalEmployeeBusinessView", "embedded business view API helper"],
[api, "/api/digital-employee/plans", "embedded business view plans endpoint"],
[api, "/api/digital-employee/approvals", "embedded business view approvals endpoint"],
[types, "DigitalEmployeeBusinessViewKind", "business view kind type"],
[types, "DigitalEmployeeBusinessViewRow", "business view row type"],
[types, "DigitalEmployeeBusinessViewQuery", "business view query type"],
[main, "path=\"/digital/*\"", "business view deep route"],
[nav, "business", "business navigation tab"],
[nav, "businessKind", "business navigation kind target"],
[nav, "entityId", "business navigation entity target"],
[shell, "BusinessView", "business view shell component"],
[shell, "业务视图", "business view tab label"],
[businessView, "getDigitalEmployeeBusinessView", "business view data loader"],
[businessView, "计划", "business view plans segment"],
[businessView, "审批", "business view approvals segment"],
[businessView, "详情", "business view detail panel"],
];
const missing = requiredSnippets
.filter(([content, snippet]) => !content.includes(snippet))
.map(([, , label]) => label);
if (missing.length > 0) {
throw new Error(`Missing management business view UI hooks: ${missing.join(", ")}`);
}
console.log("[DigitalEmployeeCheck] management business view UI hooks OK");
}
function checkDigitalInterventionWorkbench() {
console.log("\n[DigitalEmployeeCheck] Verify intervention workbench hooks");
const adapterPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "lib", "qimingclawAdapter.ts");
const apiPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "lib", "api.ts");
const typesPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "types", "api.ts");
const shellPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "pages", "digital", "DigitalEmployeeShell.tsx");
const commandDeckPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "pages", "digital", "CommandDeck.tsx");
const workbenchPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "pages", "digital", "InterventionWorkbench.tsx");
const adapter = fs.readFileSync(adapterPath, "utf8");
const api = fs.readFileSync(apiPath, "utf8");
const types = fs.readFileSync(typesPath, "utf8");
const shell = fs.readFileSync(shellPath, "utf8");
const commandDeck = fs.readFileSync(commandDeckPath, "utf8");
const workbench = fs.existsSync(workbenchPath) ? fs.readFileSync(workbenchPath, "utf8") : "";
const docs = fs.readFileSync(path.join(packageRoot, "..", "..", "docs", "digital-employee-frontend-integration-plan.md"), "utf8");
const requiredSnippets = [
[adapter, "interventionWorkbenchRows", "adapter intervention workbench projection"],
[adapter, "/api/digital-employee/interventions/workbench", "adapter intervention workbench endpoint"],
[adapter, "handleApprovalConflictResolutionBatch", "adapter approval conflict batch handler"],
[adapter, "batch_accept_remote_disabled", "adapter approval batch safety guard"],
[adapter, "handleRouteInterventionsBatch", "adapter route intervention batch handler"],
[api, "getDigitalEmployeeInterventionWorkbench", "embedded intervention workbench API"],
[api, "resolveApprovalConflictsBatch", "embedded approval conflict batch API"],
[api, "decideRouteInterventionsBatch", "embedded route intervention batch API"],
[types, "DigitalEmployeeInterventionRow", "intervention row type"],
[types, "DigitalEmployeeInterventionWorkbenchResponse", "intervention response type"],
[shell, "InterventionWorkbench", "intervention shell component"],
[shell, "介入台", "intervention tab label"],
[commandDeck, "打开介入台", "command deck intervention entry"],
[workbench, "批量保留本地", "workbench keep local batch UI"],
[workbench, "批量标记复核", "workbench mark reviewed batch UI"],
[workbench, "批量忽略", "workbench route dismiss batch UI"],
[docs, "跨设备批量冲突排查", "docs approval intervention absorption"],
[docs, "批量干预入口", "docs route intervention absorption"],
];
const missing = requiredSnippets
.filter(([content, snippet]) => !content.includes(snippet))
.map(([, , label]) => label);
if (missing.length > 0) {
throw new Error(`Missing intervention workbench hooks: ${missing.join(", ")}`);
}
console.log("[DigitalEmployeeCheck] intervention workbench hooks OK");
}
function checkDigitalMemoryGovernanceAudit() {
console.log("\n[DigitalEmployeeCheck] Verify memory governance and skill audit hooks");
const adapterPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "lib", "qimingclawAdapter.ts");
const apiPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "lib", "api.ts");
const typesPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "types", "api.ts");
const shellPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "pages", "digital", "DigitalEmployeeShell.tsx");
const skillLibraryPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "pages", "digital", "SkillLibrary.tsx");
const commandDeckPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "pages", "digital", "CommandDeck.tsx");
const memoryGovernancePath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "pages", "digital", "MemoryGovernance.tsx");
const skillAuditPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "pages", "digital", "SkillAuditWorkbench.tsx");
const adapter = fs.readFileSync(adapterPath, "utf8");
const api = fs.readFileSync(apiPath, "utf8");
const types = fs.readFileSync(typesPath, "utf8");
const shell = fs.readFileSync(shellPath, "utf8");
const skillLibrary = fs.readFileSync(skillLibraryPath, "utf8");
const commandDeck = fs.readFileSync(commandDeckPath, "utf8");
const memoryGovernance = fs.existsSync(memoryGovernancePath) ? fs.readFileSync(memoryGovernancePath, "utf8") : "";
const skillAudit = fs.existsSync(skillAuditPath) ? fs.readFileSync(skillAuditPath, "utf8") : "";
const docs = fs.readFileSync(path.join(packageRoot, "..", "..", "docs", "digital-employee-frontend-integration-plan.md"), "utf8");
const requiredSnippets = [
[adapter, "memoryGovernanceRows", "adapter memory governance projection"],
[adapter, "/api/digital-employee/memory-governance", "adapter memory governance endpoint"],
[adapter, "/api/digital-employee/memory-governance/status/batch", "adapter memory governance batch endpoint"],
[adapter, "skillAuditWorkbenchRows", "adapter skill audit workbench projection"],
[adapter, "/api/digital-employee/skill-audit-workbench", "adapter skill audit workbench endpoint"],
[api, "getDigitalEmployeeMemoryGovernance", "embedded memory governance API"],
[api, "setMemoryGovernanceStatus", "embedded memory governance batch API"],
[api, "getDigitalEmployeeSkillAuditWorkbench", "embedded skill audit API"],
[types, "DigitalEmployeeMemoryGovernanceRow", "memory governance row type"],
[types, "DigitalEmployeeSkillAuditWorkbenchRow", "skill audit row type"],
[shell, "MemoryGovernance", "memory governance shell component"],
[shell, "SkillAuditWorkbench", "skill audit shell component"],
[shell, "记忆治理", "memory governance tab label"],
[shell, "技能审计", "skill audit tab label"],
[skillLibrary, "技能审计", "skill library audit entry"],
[commandDeck, "打开技能审计", "home skill audit entry"],
[memoryGovernance, "批量归档", "memory governance archive UI"],
[memoryGovernance, "批量恢复", "memory governance restore UI"],
[memoryGovernance, "重复候选", "memory governance duplicate candidate UI"],
[skillAudit, "技能审计", "skill audit workbench title"],
[docs, "记忆归档/恢复 UI", "docs memory governance absorption"],
[docs, "管理端调用审计视图", "docs skill audit absorption"],
[docs, "管理端正式审计视图预览", "docs audit preview absorption"],
];
const missing = requiredSnippets
.filter(([content, snippet]) => !content.includes(snippet))
.map(([, , label]) => label);
if (missing.length > 0) {
throw new Error(`Missing memory governance and skill audit hooks: ${missing.join(", ")}`);
}
console.log("[DigitalEmployeeCheck] memory governance and skill audit hooks OK");
}
function checkDigitalOpsNotificationReportWorkbenches() {
console.log("\n[DigitalEmployeeCheck] Verify ops, notification and daily report workbench hooks");
const adapterPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "lib", "qimingclawAdapter.ts");
const apiPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "lib", "api.ts");
const typesPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "types", "api.ts");
const shellPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "pages", "digital", "DigitalEmployeeShell.tsx");
const commandDeckPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "pages", "digital", "CommandDeck.tsx");
const businessViewPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "pages", "digital", "BusinessView.tsx");
const opsWorkbenchPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "pages", "digital", "OpsWorkbench.tsx");
const notificationWorkbenchPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "pages", "digital", "NotificationWorkbench.tsx");
const dailyReportWorkbenchPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "pages", "digital", "DailyReportWorkbench.tsx");
const adapter = fs.readFileSync(adapterPath, "utf8");
const api = fs.readFileSync(apiPath, "utf8");
const types = fs.readFileSync(typesPath, "utf8");
const shell = fs.readFileSync(shellPath, "utf8");
const commandDeck = fs.readFileSync(commandDeckPath, "utf8");
const businessView = fs.readFileSync(businessViewPath, "utf8");
const opsWorkbench = fs.existsSync(opsWorkbenchPath) ? fs.readFileSync(opsWorkbenchPath, "utf8") : "";
const notificationWorkbench = fs.existsSync(notificationWorkbenchPath) ? fs.readFileSync(notificationWorkbenchPath, "utf8") : "";
const dailyReportWorkbench = fs.existsSync(dailyReportWorkbenchPath) ? fs.readFileSync(dailyReportWorkbenchPath, "utf8") : "";
const docs = fs.readFileSync(path.join(packageRoot, "..", "..", "docs", "digital-employee-frontend-integration-plan.md"), "utf8");
const requiredSnippets = [
[adapter, "opsWorkbenchRows", "adapter ops workbench projection"],
[adapter, "/api/digital-employee/ops-workbench", "adapter ops workbench endpoint"],
[adapter, "/api/digital-employee/ops-workbench/actions/batch", "adapter ops batch endpoint"],
[adapter, "safe_restart_service", "adapter safe restart action"],
[adapter, "ops_workbench_action_recorded", "adapter ops audit action"],
[adapter, "/api/digital-employee/notifications/workbench", "adapter notification workbench endpoint"],
[adapter, "/api/digital-employee/notifications/actions/batch", "adapter notification batch endpoint"],
[adapter, "notification_workbench_action_recorded", "adapter notification audit action"],
[adapter, "/api/digital-employee/daily-reports/workbench", "adapter daily report workbench endpoint"],
[adapter, "daily_report_workbench_action_recorded", "adapter daily report audit action"],
[api, "getDigitalEmployeeOpsWorkbench", "embedded ops workbench API"],
[api, "runDigitalEmployeeOpsBatchAction", "embedded ops batch API"],
[api, "getDigitalEmployeeNotificationWorkbench", "embedded notification workbench API"],
[api, "runDigitalEmployeeNotificationBatchAction", "embedded notification batch API"],
[api, "getDigitalEmployeeDailyReportWorkbench", "embedded daily report workbench API"],
[types, "DigitalEmployeeOpsWorkbenchRow", "ops workbench row type"],
[types, "DigitalEmployeeNotificationWorkbenchRow", "notification workbench row type"],
[types, "DigitalEmployeeDailyReportWorkbenchRow", "daily report workbench row type"],
[shell, "OpsWorkbench", "ops workbench shell component"],
[shell, "NotificationWorkbench", "notification workbench shell component"],
[shell, "DailyReportWorkbench", "daily report workbench shell component"],
[shell, "处置台", "ops workbench tab label"],
[shell, "通知运营", "notification workbench tab label"],
[shell, "日报运营", "daily report workbench tab label"],
[commandDeck, "打开处置台", "home ops entry"],
[commandDeck, "打开通知运营", "home notification entry"],
[commandDeck, "打开日报运营", "home daily report entry"],
[businessView, "打开处置台", "business view ops related link"],
[opsWorkbench, "安全重启", "ops safe restart UI"],
[opsWorkbench, "策略重拉", "ops policy retry UI"],
[notificationWorkbench, "批量已读", "notification batch read UI"],
[notificationWorkbench, "打开系统设置", "notification settings UI"],
[dailyReportWorkbench, "请求审批", "daily report approval UI"],
[dailyReportWorkbench, "确认回执", "daily report confirmation UI"],
[docs, "完整人机介入入口", "docs managed service ops absorption"],
[docs, "正式管理端通知运营预览", "docs notification workbench absorption"],
[docs, "诊断修复动作预览", "docs diagnostic repair absorption"],
[docs, "管理端正式日报页面预览", "docs daily report workbench absorption"],
];
const missing = requiredSnippets
.filter(([content, snippet]) => !content.includes(snippet))
.map(([, , label]) => label);
if (missing.length > 0) {
throw new Error(`Missing ops/notification/daily report workbench hooks: ${missing.join(", ")}`);
}
console.log("[DigitalEmployeeCheck] ops, notification and daily report workbench hooks OK");
}
function checkDigitalPolicySchedulerWorkbenches() {
console.log("\n[DigitalEmployeeCheck] Verify policy center and scheduler workbench hooks");
const apiPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "lib", "api.ts");
const typesPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "types", "api.ts");
const shellPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "pages", "digital", "DigitalEmployeeShell.tsx");
const commandDeckPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "pages", "digital", "CommandDeck.tsx");
const opsSettingsPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "pages", "digital", "OpsSettings.tsx");
const policyCenterPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "pages", "digital", "PolicyCenter.tsx");
const schedulerWorkbenchPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "pages", "digital", "SchedulerWorkbench.tsx");
const api = fs.readFileSync(apiPath, "utf8");
const types = fs.readFileSync(typesPath, "utf8");
const shell = fs.readFileSync(shellPath, "utf8");
const commandDeck = fs.readFileSync(commandDeckPath, "utf8");
const opsSettings = fs.readFileSync(opsSettingsPath, "utf8");
const policyCenter = fs.existsSync(policyCenterPath) ? fs.readFileSync(policyCenterPath, "utf8") : "";
const schedulerWorkbench = fs.existsSync(schedulerWorkbenchPath) ? fs.readFileSync(schedulerWorkbenchPath, "utf8") : "";
const docs = fs.readFileSync(path.join(packageRoot, "..", "..", "docs", "digital-employee-frontend-integration-plan.md"), "utf8");
const requiredSnippets = [
[api, "getDigitalEmployeePolicyCenter", "embedded policy center API"],
[api, "pullDigitalEmployeePoliciesBatch", "embedded policy batch pull API"],
[api, "getDigitalEmployeeSchedulerWorkbench", "embedded scheduler workbench API"],
[types, "DigitalEmployeePolicyCenterRow", "policy center row type"],
[types, "DigitalEmployeePolicyCenterResponse", "policy center response type"],
[types, "DigitalEmployeeSchedulerWorkbenchRow", "scheduler workbench row type"],
[types, "DigitalEmployeeSchedulerWorkbenchResponse", "scheduler workbench response type"],
[types, "DigitalEmployeePolicyPullBatchResult", "policy pull batch result type"],
[shell, "PolicyCenter", "policy center shell component"],
[shell, "SchedulerWorkbench", "scheduler workbench shell component"],
[shell, "策略中心", "policy center tab label"],
[shell, "调度运营", "scheduler workbench tab label"],
[commandDeck, "打开策略中心", "home policy center entry"],
[commandDeck, "打开调度运营", "home scheduler workbench entry"],
[opsSettings, "策略中心", "settings policy center hint"],
[policyCenter, "批量拉取策略", "policy center batch pull UI"],
[policyCenter, "max_per_sweep", "policy center dispatch policy field"],
[policyCenter, "auto_dispatch_ready_steps", "policy center auto dispatch field"],
[policyCenter, "auto_reject_actions", "policy center risk reject field"],
[schedulerWorkbench, "立即检查调度", "scheduler immediate check UI"],
[schedulerWorkbench, "catch_up_on_startup", "scheduler catch up setting UI"],
[schedulerWorkbench, "max_run_history", "scheduler history setting UI"],
[docs, "派发策略管理 UI", "docs dispatch policy UI absorption"],
[docs, "多调度并发策略 UI", "docs scheduler policy UI absorption"],
[docs, "管理端正式路由 UI 预览", "docs route policy preview absorption"],
];
const missing = requiredSnippets
.filter(([content, snippet]) => !content.includes(snippet))
.map(([, , label]) => label);
if (missing.length > 0) {
throw new Error(`Missing policy center and scheduler workbench hooks: ${missing.join(", ")}`);
}
console.log("[DigitalEmployeeCheck] policy center and scheduler workbench hooks OK");
}
function checkDigitalAdminActionLoop() {
console.log("\n[DigitalEmployeeCheck] Verify admin action pull/apply loop hooks");
const syncServicePath = path.join(packageRoot, "src", "main", "services", "digitalEmployee", "syncService.ts");
const ipcPath = path.join(packageRoot, "src", "main", "ipc", "digitalEmployeeHandlers.ts");
const preloadPath = path.join(packageRoot, "src", "preload", "webviewPerfBridge.ts");
const adapterPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "lib", "qimingclawAdapter.ts");
const apiPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "lib", "api.ts");
const syncService = fs.readFileSync(syncServicePath, "utf8");
const ipc = fs.readFileSync(ipcPath, "utf8");
const preload = fs.readFileSync(preloadPath, "utf8");
const adapter = fs.readFileSync(adapterPath, "utf8");
const api = fs.readFileSync(apiPath, "utf8");
const requiredSnippets = [
[syncService, "pullAndApplyDigitalEmployeeAdminActions", "admin action pull service"],
[syncService, "admin_action_applied", "admin action applied event"],
[syncService, "admin_action_rejected", "admin action rejected event"],
[syncService, "admin_action_failed", "admin action failed event"],
[syncService, "cleanup_file_batch", "high risk cleanup rejection guard"],
[ipc, "digitalEmployee:pullAdminActions", "admin action IPC"],
[preload, "pullAdminActions", "admin action preload bridge"],
[adapter, "/api/digital-employee/admin/actions/pull", "admin action adapter endpoint"],
[api, "pullAdminActions", "embedded admin action API helper"],
];
const missing = requiredSnippets
.filter(([content, snippet]) => !content.includes(snippet))
.map(([, , label]) => label);
if (missing.length > 0) {
throw new Error(`Missing admin action loop hooks: ${missing.join(", ")}`);
}
console.log("[DigitalEmployeeCheck] admin action pull/apply loop hooks OK");
}
function checkDigitalAdminPolicySnapshots() {
console.log("\n[DigitalEmployeeCheck] Verify admin policy snapshot conflict view hooks");
const syncServicePath = path.join(packageRoot, "src", "main", "services", "digitalEmployee", "syncService.ts");
const syncTestPath = path.join(packageRoot, "src", "main", "services", "digitalEmployee", "syncService.test.ts");
const docsPath = path.resolve(packageRoot, "..", "..", "docs", "digital-employee-frontend-integration-plan.md");
const qimingRoot = path.resolve(packageRoot, "..", "..", "..", "qiming");
const backendRoot = path.resolve(packageRoot, "..", "..", "..", "qiming-backend");
const qimingOpsPath = path.join(qimingRoot, "src", "pages", "SystemManagement", "Content", "DigitalEmployeeOps", "index.tsx");
const qimingServicePath = path.join(qimingRoot, "src", "services", "systemManage.ts");
const qimingTypesPath = path.join(qimingRoot, "src", "types", "interfaces", "systemManage.ts");
const backendServicePath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-application",
"src",
"main",
"java",
"com",
"xspaceagi",
"agent",
"core",
"application",
"service",
"DigitalEmployeeSyncApplicationServiceImpl.java",
);
const backendDtoPath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-adapter",
"src",
"main",
"java",
"com",
"xspaceagi",
"agent",
"core",
"adapter",
"dto",
"digital",
"DigitalEmployeePolicySnapshotRowDto.java",
);
const backendTestPath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-application",
"src",
"test",
"java",
"com",
"xspaceagi",
"agent",
"core",
"application",
"service",
"DigitalEmployeeSyncApplicationServiceImplTest.java",
);
const backendControllerPath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-ui",
"src",
"main",
"java",
"com",
"xspaceagi",
"agent",
"web",
"ui",
"controller",
"DigitalEmployeeAdminPolicyController.java",
);
const syncService = fs.readFileSync(syncServicePath, "utf8");
const syncTest = fs.readFileSync(syncTestPath, "utf8");
const docs = fs.readFileSync(docsPath, "utf8");
const qimingOps = fs.readFileSync(qimingOpsPath, "utf8");
const qimingService = fs.readFileSync(qimingServicePath, "utf8");
const qimingTypes = fs.readFileSync(qimingTypesPath, "utf8");
const backendService = fs.readFileSync(backendServicePath, "utf8");
const backendDto = fs.readFileSync(backendDtoPath, "utf8");
const backendTest = fs.readFileSync(backendTestPath, "utf8");
const backendController = fs.readFileSync(backendControllerPath, "utf8");
const requiredSnippets = [
[syncService, "policySnapshotBusinessView", "client policy snapshot business view"],
[syncService, "policy_snapshot", "client policy snapshot category"],
[syncService, "policy_kind", "client policy kind field"],
[syncService, "auto_create_governance_commands", "route policy conflict field"],
[syncService, "max_per_sweep", "dispatch policy conflict field"],
[syncService, "catch_up_on_startup", "scheduler policy conflict field"],
[syncTest, "event-route-policy-pull", "route policy snapshot sync test"],
[syncTest, "event-risk-policy-pull", "risk policy snapshot sync test"],
[backendController, "/snapshots", "backend policy snapshot endpoint"],
[backendService, "routeInterventionConflictStatus", "backend route intervention conflict aggregation"],
[backendService, "route_intervention_state", "backend route intervention conflict key"],
[backendService, "approval_policy_id", "backend approval policy conflict field"],
[backendService, "decision_guard_status", "backend approval decision guard conflict field"],
[backendDto, "intervention_conflict_status", "backend route intervention conflict status field"],
[backendDto, "approval_policy_id", "backend policy snapshot approval policy field"],
[backendDto, "auto_decision_disabled", "backend policy snapshot auto decision field"],
[backendDto, "max_per_sweep", "backend policy snapshot max per sweep field"],
[backendDto, "catch_up_on_startup", "backend policy snapshot catch up field"],
[backendDto, "route_decision_count", "backend route decision count field"],
[backendTest, "queryAdminPolicySnapshotsSummarizesRouteInterventionConflicts", "backend route intervention conflict test"],
[backendTest, "queryAdminPolicySnapshotsSummarizesApprovalPolicyConflicts", "backend approval policy conflict test"],
[backendTest, "queryAdminPolicySnapshotsSummarizesDispatchAndSchedulerPolicyConflicts", "backend dispatch scheduler policy conflict test"],
[qimingService, "apiDigitalEmployeeAdminPolicySnapshots", "qiming policy snapshot service"],
[qimingService, "/api/digital-employee/admin/policies/snapshots", "qiming policy snapshot endpoint"],
[qimingTypes, "DigitalEmployeePolicySnapshotRow", "qiming policy snapshot row type"],
[qimingTypes, "policy_conflicts", "qiming policy conflicts view type"],
[qimingTypes, "approval_policy_id", "qiming approval policy snapshot type field"],
[qimingTypes, "max_per_sweep", "qiming dispatch policy snapshot type field"],
[qimingTypes, "catch_up_on_startup", "qiming scheduler policy snapshot type field"],
[qimingTypes, "intervention_conflict_status", "qiming route intervention conflict type field"],
[qimingOps, "策略冲突", "qiming policy conflict tab text"],
[qimingOps, "conflict_keys", "qiming conflict key display"],
[qimingOps, "跨设备审批策略冲突", "qiming approval policy conflict copy"],
[qimingOps, "跨设备派发策略冲突", "qiming dispatch policy conflict copy"],
[qimingOps, "跨设备调度策略冲突", "qiming scheduler policy conflict copy"],
[qimingOps, "裁决保护", "qiming decision guard conflict copy"],
[qimingOps, "跨设备路由干预冲突", "qiming route intervention conflict copy"],
[qimingOps, "不恢复执行", "qiming route recovery guard copy"],
[qimingOps, "不绕过审批", "qiming approval guard copy"],
[qimingOps, "不远程改策略", "qiming remote policy edit guard copy"],
[docs, "跨设备策略冲突视图", "docs policy conflict absorption"],
[docs, "跨设备审批策略冲突预览", "docs approval policy conflict absorption"],
[docs, "跨设备调度派发策略冲突预览", "docs dispatch scheduler policy conflict absorption"],
[docs, "跨设备路由干预冲突可视化", "docs route intervention conflict absorption"],
];
const missing = requiredSnippets
.filter(([content, snippet]) => !content.includes(snippet))
.map(([, , label]) => label);
if (missing.length > 0) {
throw new Error(`Missing admin policy snapshot hooks: ${missing.join(", ")}`);
}
console.log("[DigitalEmployeeCheck] admin policy snapshot conflict view hooks OK");
}
function checkDigitalBackendMaterializedFacts() {
console.log("\n[DigitalEmployeeCheck] Verify backend materialized business fact hooks");
const backendRoot = path.resolve(packageRoot, "..", "..", "..", "qiming-backend");
const qimingRoot = path.resolve(packageRoot, "..", "..", "..", "qiming");
const servicePath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-application",
"src",
"main",
"java",
"com",
"xspaceagi",
"agent",
"core",
"application",
"service",
"DigitalEmployeeSyncApplicationServiceImpl.java",
);
const sqlPath = path.join(backendRoot, "sql", "update-20260612.sql");
const dtoPath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-adapter",
"src",
"main",
"java",
"com",
"xspaceagi",
"agent",
"core",
"adapter",
"dto",
"digital",
"DigitalEmployeeBusinessViewRowDto.java",
);
const qimingTypesPath = path.join(qimingRoot, "src", "types", "interfaces", "systemManage.ts");
const docsPath = path.resolve(packageRoot, "..", "..", "docs", "digital-employee-frontend-integration-plan.md");
const service = fs.readFileSync(servicePath, "utf8");
const sql = fs.readFileSync(sqlPath, "utf8");
const dto = fs.readFileSync(dtoPath, "utf8");
const qimingTypes = fs.readFileSync(qimingTypesPath, "utf8");
const docs = fs.readFileSync(docsPath, "utf8");
const requiredSnippets = [
[sql, "digital_employee_business_fact", "business fact table migration"],
[sql, "digital_employee_artifact_lifecycle", "artifact lifecycle table migration"],
[sql, "digital_employee_daily_report_fact", "daily report fact table migration"],
[service, "materializeSyncRecord", "sync materialization hook"],
[service, "upsertBySyncRecord", "materialized upsert call"],
[service, "queryMaterializedBusinessRows", "materialized query preference"],
[dto, "materialized", "business row materialized flag"],
[qimingTypes, "materialized", "qiming materialized type field"],
[docs, "业务事实物化查询", "docs materialized fact absorption"],
];
const missing = requiredSnippets
.filter(([content, snippet]) => !content.includes(snippet))
.map(([, , label]) => label);
if (missing.length > 0) {
throw new Error(`Missing backend materialized fact hooks: ${missing.join(", ")}`);
}
console.log("[DigitalEmployeeCheck] backend materialized business fact hooks OK");
}
function checkDigitalAdminRecordBillingArchiveShell() {
console.log("\n[DigitalEmployeeCheck] Verify admin send/billing/audit archive shell hooks");
const backendRoot = path.resolve(packageRoot, "..", "..", "..", "qiming-backend");
const qimingRoot = path.resolve(packageRoot, "..", "..", "..", "qiming");
const docsPath = path.resolve(packageRoot, "..", "..", "docs", "digital-employee-frontend-integration-plan.md");
const servicePath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-application",
"src",
"main",
"java",
"com",
"xspaceagi",
"agent",
"core",
"application",
"service",
"DigitalEmployeeSyncApplicationServiceImpl.java",
);
const controllerPath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-ui",
"src",
"main",
"java",
"com",
"xspaceagi",
"agent",
"web",
"ui",
"controller",
"DigitalEmployeeAdminRecordController.java",
);
const qimingServicePath = path.join(qimingRoot, "src", "services", "systemManage.ts");
const qimingTypesPath = path.join(qimingRoot, "src", "types", "interfaces", "systemManage.ts");
const qimingOpsPath = path.join(qimingRoot, "src", "pages", "SystemManagement", "Content", "DigitalEmployeeOps", "index.tsx");
const service = fs.readFileSync(servicePath, "utf8");
const controller = fs.readFileSync(controllerPath, "utf8");
const qimingService = fs.readFileSync(qimingServicePath, "utf8");
const qimingTypes = fs.readFileSync(qimingTypesPath, "utf8");
const qimingOps = fs.readFileSync(qimingOpsPath, "utf8");
const docs = fs.readFileSync(docsPath, "utf8");
const requiredSnippets = [
[controller, "/daily-reports/send-records", "backend daily report send record endpoint"],
[controller, "/billing/estimates", "backend billing estimate endpoint"],
[controller, "/audit-archives", "backend audit archive endpoint"],
[service, "recordDailyReportSendResult", "backend daily report send record service"],
[service, "queryBillingEstimates", "backend billing estimate service"],
[service, "recordAuditArchive", "backend audit archive service"],
[service, "sanitizeAdminRecordValue", "backend admin record redaction"],
[service, "authorization", "backend sensitive authorization guard"],
[service, "raw_input", "backend sensitive raw input guard"],
[qimingService, "apiDigitalEmployeeAdminDailyReportSendRecord", "qiming daily report send record helper"],
[qimingService, "apiDigitalEmployeeAdminBillingEstimates", "qiming billing estimate helper"],
[qimingService, "apiDigitalEmployeeAdminAuditArchive", "qiming audit archive helper"],
[qimingTypes, "DigitalEmployeeBillingEstimate", "qiming billing estimate type"],
[qimingTypes, "DigitalEmployeeAdminRecordSubmitParams", "qiming admin record submit type"],
[qimingOps, "记录发送结果", "qiming daily report send UI"],
[qimingOps, "标记归档", "qiming audit archive UI"],
[qimingOps, "估算 Token", "qiming billing token UI"],
[qimingOps, "估算成本 USD", "qiming billing cost UI"],
[docs, "发送账单审计外壳", "docs send billing audit shell absorption"],
];
const missing = requiredSnippets
.filter(([content, snippet]) => !content.includes(snippet))
.map(([, , label]) => label);
if (missing.length > 0) {
throw new Error(`Missing admin send/billing/audit archive shell hooks: ${missing.join(", ")}`);
}
console.log("[DigitalEmployeeCheck] admin send/billing/audit archive shell hooks OK");
}
function checkDigitalAdminApprovalGovernanceWorkbench() {
console.log("\n[DigitalEmployeeCheck] Verify admin approval governance workbench hooks");
const backendRoot = path.resolve(packageRoot, "..", "..", "..", "qiming-backend");
const qimingRoot = path.resolve(packageRoot, "..", "..", "..", "qiming");
const servicePath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-application",
"src",
"main",
"java",
"com",
"xspaceagi",
"agent",
"core",
"application",
"service",
"DigitalEmployeeSyncApplicationServiceImpl.java",
);
const testPath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-application",
"src",
"test",
"java",
"com",
"xspaceagi",
"agent",
"core",
"application",
"service",
"DigitalEmployeeSyncApplicationServiceImplTest.java",
);
const qimingOpsPath = path.join(qimingRoot, "src", "pages", "SystemManagement", "Content", "DigitalEmployeeOps", "index.tsx");
const qimingTypesPath = path.join(qimingRoot, "src", "types", "interfaces", "systemManage.ts");
const docsPath = path.resolve(packageRoot, "..", "..", "docs", "digital-employee-frontend-integration-plan.md");
const service = fs.readFileSync(servicePath, "utf8");
const test = fs.readFileSync(testPath, "utf8");
const qimingOps = fs.readFileSync(qimingOpsPath, "utf8");
const qimingTypes = fs.readFileSync(qimingTypesPath, "utf8");
const docs = fs.readFileSync(docsPath, "utf8");
const requiredSnippets = [
[service, "approval_governance", "backend approval governance view"],
[service, "isApprovalGovernanceRow", "backend approval governance matcher"],
[service, "approval_conflict_active", "backend approval conflict classifier"],
[service, "risk_approval", "backend risk approval classifier"],
[service, "approval_action_recorded", "backend approval action history classifier"],
[service, "approval_policy_id", "backend approval policy field extraction"],
[service, "batch_accept_remote_disabled", "backend batch accept remote guard field"],
[service, "auto_decision_disabled", "backend auto decision guard field"],
[test, "queryAdminWorkbenchApprovalGovernanceReturnsApprovalConflictRiskAndHistoryRowsOnly", "backend approval governance test"],
[test, "getApprovalPolicyId", "backend approval policy assertion"],
[test, "getBatchAcceptRemoteDisabled", "backend batch accept disabled assertion"],
[qimingTypes, "approval_governance", "qiming approval governance view type"],
[qimingTypes, "approval_policy_id", "qiming approval policy field"],
[qimingTypes, "decision_guard_status", "qiming decision guard field"],
[qimingOps, "审批治理", "qiming approval governance tab"],
[qimingOps, "保留本地", "qiming approval keep local action"],
[qimingOps, "标记复核", "qiming approval mark reviewed action"],
[qimingOps, "裁决保护", "qiming decision guard copy"],
[qimingOps, "批量采纳远端禁用", "qiming batch accept remote guard copy"],
[qimingOps, "自动裁决禁用", "qiming auto decision guard copy"],
[qimingOps, "批量/高风险裁决不开放", "qiming approval governance safety copy"],
[docs, "正式管理端审批工作台预览", "docs approval governance absorption"],
[docs, "正式管理端审批裁决策略预览", "docs approval decision policy absorption"],
];
const missing = requiredSnippets
.filter(([content, snippet]) => !content.includes(snippet))
.map(([, , label]) => label);
if (missing.length > 0) {
throw new Error(`Missing admin approval governance workbench hooks: ${missing.join(", ")}`);
}
console.log("[DigitalEmployeeCheck] admin approval governance workbench hooks OK");
}
function checkDigitalAdminApprovalPoliciesWorkbench() {
console.log("\n[DigitalEmployeeCheck] Verify admin approval policies workbench hooks");
const backendRoot = path.resolve(packageRoot, "..", "..", "..", "qiming-backend");
const qimingRoot = path.resolve(packageRoot, "..", "..", "..", "qiming");
const servicePath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-application",
"src",
"main",
"java",
"com",
"xspaceagi",
"agent",
"core",
"application",
"service",
"DigitalEmployeeSyncApplicationServiceImpl.java",
);
const testPath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-application",
"src",
"test",
"java",
"com",
"xspaceagi",
"agent",
"core",
"application",
"service",
"DigitalEmployeeSyncApplicationServiceImplTest.java",
);
const qimingOpsPath = path.join(qimingRoot, "src", "pages", "SystemManagement", "Content", "DigitalEmployeeOps", "index.tsx");
const qimingTypesPath = path.join(qimingRoot, "src", "types", "interfaces", "systemManage.ts");
const docsPath = path.resolve(packageRoot, "..", "..", "docs", "digital-employee-frontend-integration-plan.md");
const service = fs.readFileSync(servicePath, "utf8");
const test = fs.readFileSync(testPath, "utf8");
const qimingOps = fs.readFileSync(qimingOpsPath, "utf8");
const qimingTypes = fs.readFileSync(qimingTypesPath, "utf8");
const docs = fs.readFileSync(docsPath, "utf8");
const requiredSnippets = [
[service, "approval_policies", "backend approval policies view"],
[service, "isApprovalPolicyRow", "backend approval policy matcher"],
[service, "approval_policy_id", "backend approval policy id projection"],
[service, "conflict_policy_id", "backend conflict policy id projection"],
[service, "decision_guard_status", "backend decision guard projection"],
[service, "policy_edit_request_id", "backend policy edit request projection"],
[service, "remotePolicyEditDisabled", "backend remote policy edit projection"],
[service, "autoMergeDisabled", "backend auto merge projection"],
[test, "queryAdminWorkbenchApprovalPoliciesReturnsPolicyRowsOnly", "backend approval policies test"],
[qimingTypes, "approval_policies", "qiming approval policies view type"],
[qimingOps, "审批策略", "qiming approval policies tab"],
[qimingOps, "不远程编辑策略", "qiming no remote policy edit copy"],
[qimingOps, "不自动裁决", "qiming no auto decision copy"],
[qimingOps, "不批量采纳远端", "qiming no batch remote accept copy"],
[docs, "正式管理端审批策略预览", "docs approval policy absorption"],
[docs, "远程策略编辑安全壳预览", "docs remote policy edit shell absorption"],
];
const missing = requiredSnippets
.filter(([content, snippet]) => !content.includes(snippet))
.map(([, , label]) => label);
if (missing.length > 0) {
throw new Error(`Missing admin approval policies workbench hooks: ${missing.join(", ")}`);
}
console.log("[DigitalEmployeeCheck] admin approval policies workbench hooks OK");
}
function checkDigitalAdminMemorySkillWorkbench() {
console.log("\n[DigitalEmployeeCheck] Verify admin memory governance and skill audit workbench hooks");
const backendRoot = path.resolve(packageRoot, "..", "..", "..", "qiming-backend");
const qimingRoot = path.resolve(packageRoot, "..", "..", "..", "qiming");
const servicePath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-application",
"src",
"main",
"java",
"com",
"xspaceagi",
"agent",
"core",
"application",
"service",
"DigitalEmployeeSyncApplicationServiceImpl.java",
);
const dtoPath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-adapter",
"src",
"main",
"java",
"com",
"xspaceagi",
"agent",
"core",
"adapter",
"dto",
"digital",
"DigitalEmployeeAdminWorkbenchRowDto.java",
);
const testPath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-application",
"src",
"test",
"java",
"com",
"xspaceagi",
"agent",
"core",
"application",
"service",
"DigitalEmployeeSyncApplicationServiceImplTest.java",
);
const qimingOpsPath = path.join(qimingRoot, "src", "pages", "SystemManagement", "Content", "DigitalEmployeeOps", "index.tsx");
const qimingTypesPath = path.join(qimingRoot, "src", "types", "interfaces", "systemManage.ts");
const docsPath = path.resolve(packageRoot, "..", "..", "docs", "digital-employee-frontend-integration-plan.md");
const service = fs.readFileSync(servicePath, "utf8");
const dto = fs.readFileSync(dtoPath, "utf8");
const test = fs.readFileSync(testPath, "utf8");
const qimingOps = fs.readFileSync(qimingOpsPath, "utf8");
const qimingTypes = fs.readFileSync(qimingTypesPath, "utf8");
const docs = fs.readFileSync(docsPath, "utf8");
const requiredSnippets = [
[service, "memory_governance", "backend memory governance view"],
[service, "memory_policies", "backend memory policies view"],
[service, "skill_audits", "backend skill audits view"],
[service, "isMemoryGovernanceRow", "backend memory governance matcher"],
[service, "isMemoryPolicyRow", "backend memory policies matcher"],
[service, "isSkillAuditRow", "backend skill audit matcher"],
[service, "skill_call_", "backend skill call classifier"],
[service, "tool_call_audit", "backend tool audit classifier"],
[service, "token_cost", "backend token cost classifier"],
[dto, "memory_id", "backend memory id row field"],
[dto, "memory_key", "backend memory key row field"],
[dto, "governance_status", "backend memory governance status row field"],
[dto, "merge_candidate_count", "backend memory merge candidate count row field"],
[dto, "duplicate_memory_ids", "backend duplicate memory ids row field"],
[dto, "memory_policy_id", "backend memory policy id row field"],
[dto, "memory_policy_status", "backend memory policy status row field"],
[dto, "merge_policy_id", "backend merge policy id row field"],
[dto, "merge_action_disabled", "backend merge action disabled row field"],
[dto, "remote_memory_policy_disabled", "backend remote memory policy disabled row field"],
[dto, "skill_id", "backend skill id row field"],
[dto, "skill_version", "backend skill version row field"],
[dto, "install_status", "backend skill install status row field"],
[dto, "permission_scope", "backend skill permission scope row field"],
[dto, "policy_source", "backend skill policy source row field"],
[dto, "skill_install_request_id", "backend skill install request id row field"],
[dto, "skill_upgrade_request_id", "backend skill upgrade request id row field"],
[dto, "permission_policy_id", "backend skill permission policy id row field"],
[dto, "install_action_disabled", "backend skill install action disabled row field"],
[dto, "upgrade_action_disabled", "backend skill upgrade action disabled row field"],
[dto, "permission_edit_disabled", "backend skill permission edit disabled row field"],
[dto, "tool_name", "backend tool name row field"],
[dto, "server_id", "backend server id row field"],
[test, "queryAdminWorkbenchMemoryGovernanceReturnsMemoryRowsOnly", "backend memory governance test"],
[test, "getMergeCandidateCount", "backend memory merge candidate assertion"],
[test, "queryAdminWorkbenchMemoryPoliciesReturnsPolicyRowsOnly", "backend memory policies test"],
[test, "getMemoryPolicyId", "backend memory policy assertion"],
[test, "queryAdminWorkbenchSkillAuditsReturnsSkillAndToolAuditRowsOnly", "backend skill audits test"],
[test, "queryAdminWorkbenchSkillAuditsReturnsVersionPermissionAndPolicyRowsOnly", "backend skill version permission test"],
[test, "getSkillInstallRequestId", "backend skill install request assertion"],
[test, "getPermissionPolicyId", "backend skill permission policy assertion"],
[test, "getInstallActionDisabled", "backend skill install disabled assertion"],
[test, "getUpgradeActionDisabled", "backend skill upgrade disabled assertion"],
[test, "getPermissionEditDisabled", "backend skill permission edit disabled assertion"],
[qimingTypes, "memory_governance", "qiming memory governance view type"],
[qimingTypes, "merge_candidate_count", "qiming memory merge candidate count field"],
[qimingTypes, "duplicate_memory_ids", "qiming duplicate memory ids field"],
[qimingTypes, "memory_policies", "qiming memory policies view type"],
[qimingTypes, "memory_policy_id", "qiming memory policy id field"],
[qimingTypes, "remote_memory_policy_disabled", "qiming remote memory policy disabled field"],
[qimingTypes, "skill_audits", "qiming skill audits view type"],
[qimingTypes, "skill_version", "qiming skill version field"],
[qimingTypes, "permission_scope", "qiming skill permission scope field"],
[qimingTypes, "skill_install_request_id", "qiming skill install request field"],
[qimingTypes, "skill_upgrade_request_id", "qiming skill upgrade request field"],
[qimingTypes, "permission_policy_id", "qiming skill permission policy field"],
[qimingTypes, "install_action_disabled", "qiming skill install disabled field"],
[qimingTypes, "upgrade_action_disabled", "qiming skill upgrade disabled field"],
[qimingTypes, "permission_edit_disabled", "qiming skill permission edit disabled field"],
[qimingOps, "记忆治理", "qiming memory governance tab"],
[qimingOps, "记忆策略", "qiming memory policies tab"],
[qimingOps, "技能审计", "qiming skill audits tab"],
[qimingOps, "安装请求", "qiming skill install request copy"],
[qimingOps, "升级请求", "qiming skill upgrade request copy"],
[qimingOps, "权限策略", "qiming skill permission policy copy"],
[qimingOps, "权限编辑禁用", "qiming skill permission edit safety copy"],
[qimingOps, "权限范围", "qiming skill permission scope copy"],
[qimingOps, "策略来源", "qiming skill policy source copy"],
[qimingOps, "批量归档", "qiming memory archive action"],
[qimingOps, "批量恢复", "qiming memory restore action"],
[qimingOps, "重复候选", "qiming memory duplicate copy"],
[qimingOps, "不自动合并记忆", "qiming memory merge safety copy"],
[qimingOps, "远端记忆策略", "qiming memory policy copy"],
[qimingOps, "不触发自动合并", "qiming memory policy merge safety copy"],
[docs, "正式管理端记忆治理预览", "docs memory governance absorption"],
[docs, "正式管理端记忆合并候选预览", "docs memory merge candidate absorption"],
[docs, "远端记忆策略预览", "docs memory policy absorption"],
[docs, "正式管理端技能审计预览", "docs skill audit absorption"],
[docs, "正式管理端技能版本权限预览", "docs skill version permission absorption"],
[docs, "正式管理端技能安装权限预览", "docs skill install permission absorption"],
];
const missing = requiredSnippets
.filter(([content, snippet]) => !content.includes(snippet))
.map(([, , label]) => label);
if (missing.length > 0) {
throw new Error(`Missing admin memory governance and skill audit hooks: ${missing.join(", ")}`);
}
console.log("[DigitalEmployeeCheck] admin memory governance and skill audit hooks OK");
}
function checkDigitalAdminPolicySchedulerWorkbenches() {
console.log("\n[DigitalEmployeeCheck] Verify admin policy center and scheduler operation workbench hooks");
const backendRoot = path.resolve(packageRoot, "..", "..", "..", "qiming-backend");
const qimingRoot = path.resolve(packageRoot, "..", "..", "..", "qiming");
const servicePath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-application",
"src",
"main",
"java",
"com",
"xspaceagi",
"agent",
"core",
"application",
"service",
"DigitalEmployeeSyncApplicationServiceImpl.java",
);
const dtoPath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-adapter",
"src",
"main",
"java",
"com",
"xspaceagi",
"agent",
"core",
"adapter",
"dto",
"digital",
"DigitalEmployeeAdminWorkbenchRowDto.java",
);
const testPath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-application",
"src",
"test",
"java",
"com",
"xspaceagi",
"agent",
"core",
"application",
"service",
"DigitalEmployeeSyncApplicationServiceImplTest.java",
);
const qimingOpsPath = path.join(qimingRoot, "src", "pages", "SystemManagement", "Content", "DigitalEmployeeOps", "index.tsx");
const qimingTypesPath = path.join(qimingRoot, "src", "types", "interfaces", "systemManage.ts");
const docsPath = path.resolve(packageRoot, "..", "..", "docs", "digital-employee-frontend-integration-plan.md");
const service = fs.readFileSync(servicePath, "utf8");
const dto = fs.readFileSync(dtoPath, "utf8");
const test = fs.readFileSync(testPath, "utf8");
const qimingOps = fs.readFileSync(qimingOpsPath, "utf8");
const qimingTypes = fs.readFileSync(qimingTypesPath, "utf8");
const docs = fs.readFileSync(docsPath, "utf8");
const requiredSnippets = [
[service, "policy_center", "backend policy center view"],
[service, "scheduler_operations", "backend scheduler operations view"],
[service, "isPolicyCenterRow", "backend policy center matcher"],
[service, "isSchedulerOperationsRow", "backend scheduler operations matcher"],
[service, "edit_policy_remote_disabled", "backend policy edit disabled reason"],
[service, "remote_policy_edit_disabled", "backend remote policy edit disabled reason"],
[service, "policy_edit_request_id", "backend policy edit request projection"],
[service, "auto_merge_disabled", "backend policy auto merge guard projection"],
[service, "run_schedule_now_disabled", "backend scheduler run disabled reason"],
[service, "start_scheduler_disabled", "backend scheduler start disabled reason"],
[dto, "policy_kind", "backend policy kind row field"],
[dto, "policy_key", "backend policy key row field"],
[dto, "policy_edit_request_id", "backend policy edit request row field"],
[dto, "remote_policy_edit_disabled", "backend remote policy edit disabled row field"],
[dto, "auto_merge_disabled", "backend auto merge disabled row field"],
[dto, "schedule_id", "backend schedule id row field"],
[dto, "job_id", "backend job id row field"],
[test, "queryAdminWorkbenchPolicyCenterReturnsPolicySnapshotRowsOnly", "backend policy center test"],
[test, "getPolicyEditRequestId", "backend policy edit request assertion"],
[test, "getAutoMergeDisabled", "backend auto merge disabled assertion"],
[test, "queryAdminWorkbenchSchedulerOperationsReturnsSchedulerRowsOnly", "backend scheduler operations test"],
[test, "createAdminActionReturnsSpecificPolicyAndSchedulerDisabledReasons", "backend disabled reason test"],
[qimingTypes, "policy_center", "qiming policy center view type"],
[qimingTypes, "scheduler_operations", "qiming scheduler operations view type"],
[qimingTypes, "policy_edit_request_id", "qiming policy edit request field"],
[qimingTypes, "auto_merge_disabled", "qiming policy auto merge guard field"],
[qimingTypes, "schedule_id", "qiming schedule id field"],
[qimingOps, "策略中心", "qiming policy center tab"],
[qimingOps, "调度运营", "qiming scheduler operations tab"],
[qimingOps, "批量拉取策略", "qiming policy pull action"],
[qimingOps, "远程策略编辑请求", "qiming policy edit request detail"],
[qimingOps, "自动合并禁用", "qiming policy auto merge guard copy"],
[qimingOps, "立即检查调度", "qiming scheduler check action"],
[qimingOps, "不开放远程策略编辑", "qiming policy edit guard copy"],
[qimingOps, "不自动合并策略", "qiming policy auto merge safety copy"],
[qimingOps, "不开放 start/stop", "qiming scheduler start stop guard copy"],
[docs, "正式管理端策略中心预览", "docs policy center absorption"],
[docs, "远程策略编辑安全壳预览", "docs remote policy edit shell absorption"],
[docs, "正式管理端调度运营预览", "docs scheduler operations absorption"],
];
const missing = requiredSnippets
.filter(([content, snippet]) => !content.includes(snippet))
.map(([, , label]) => label);
if (missing.length > 0) {
throw new Error(`Missing admin policy center and scheduler operation hooks: ${missing.join(", ")}`);
}
console.log("[DigitalEmployeeCheck] admin policy center and scheduler operation hooks OK");
}
function checkDigitalAdminPlanGovernanceWorkbenches() {
console.log("\n[DigitalEmployeeCheck] Verify admin plan governance and revision workbench hooks");
const backendRoot = path.resolve(packageRoot, "..", "..", "..", "qiming-backend");
const qimingRoot = path.resolve(packageRoot, "..", "..", "..", "qiming");
const servicePath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-application",
"src",
"main",
"java",
"com",
"xspaceagi",
"agent",
"core",
"application",
"service",
"DigitalEmployeeSyncApplicationServiceImpl.java",
);
const dtoPath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-adapter",
"src",
"main",
"java",
"com",
"xspaceagi",
"agent",
"core",
"adapter",
"dto",
"digital",
"DigitalEmployeeAdminWorkbenchRowDto.java",
);
const testPath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-application",
"src",
"test",
"java",
"com",
"xspaceagi",
"agent",
"core",
"application",
"service",
"DigitalEmployeeSyncApplicationServiceImplTest.java",
);
const qimingOpsPath = path.join(qimingRoot, "src", "pages", "SystemManagement", "Content", "DigitalEmployeeOps", "index.tsx");
const qimingTypesPath = path.join(qimingRoot, "src", "types", "interfaces", "systemManage.ts");
const docsPath = path.resolve(packageRoot, "..", "..", "docs", "digital-employee-frontend-integration-plan.md");
const service = fs.readFileSync(servicePath, "utf8");
const dto = fs.readFileSync(dtoPath, "utf8");
const test = fs.readFileSync(testPath, "utf8");
const qimingOps = fs.readFileSync(qimingOpsPath, "utf8");
const qimingTypes = fs.readFileSync(qimingTypesPath, "utf8");
const docs = fs.readFileSync(docsPath, "utf8");
const requiredSnippets = [
[service, "plan_governance", "backend plan governance view"],
[service, "plan_revisions", "backend plan revisions view"],
[service, "isPlanGovernanceRow", "backend plan governance matcher"],
[service, "isPlanRevisionRow", "backend plan revision matcher"],
[service, "record_escalation", "backend plan escalation action"],
[service, "hard_interrupt_disabled", "backend hard interrupt disabled reason"],
[service, "high_risk_governance_action_disabled", "backend high risk governance disabled reason"],
[dto, "revision_id", "backend revision id row field"],
[test, "queryAdminWorkbenchPlanGovernanceReturnsGovernanceEscalationRowsOnly", "backend plan governance test"],
[test, "queryAdminWorkbenchPlanRevisionsReturnsRevisionRowsOnly", "backend plan revisions test"],
[qimingTypes, "plan_governance", "qiming plan governance view type"],
[qimingTypes, "plan_revisions", "qiming plan revisions view type"],
[qimingTypes, "revision_id", "qiming revision id field"],
[qimingOps, "计划治理", "qiming plan governance tab"],
[qimingOps, "计划版本", "qiming plan revisions tab"],
[qimingOps, "请求版本复核", "qiming revision review action"],
[qimingOps, "记录升级", "qiming escalation action"],
[qimingOps, "不开放 hard interrupt", "qiming hard interrupt guard copy"],
[qimingOps, "不自动激活计划", "qiming high risk execution guard copy"],
[docs, "正式管理端治理执行安全壳预览", "docs plan governance absorption"],
[docs, "正式管理端计划版本预览", "docs plan revision absorption"],
];
const missing = requiredSnippets
.filter(([content, snippet]) => !content.includes(snippet))
.map(([, , label]) => label);
if (missing.length > 0) {
throw new Error(`Missing admin plan governance and revision hooks: ${missing.join(", ")}`);
}
console.log("[DigitalEmployeeCheck] admin plan governance and revision hooks OK");
}
function checkDigitalAdminGovernancePolicyRevisionMaterialization() {
console.log("\n[DigitalEmployeeCheck] Verify admin governance policy and revision materialization hooks");
const backendRoot = path.resolve(packageRoot, "..", "..", "..", "qiming-backend");
const qimingRoot = path.resolve(packageRoot, "..", "..", "..", "qiming");
const servicePath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-application",
"src",
"main",
"java",
"com",
"xspaceagi",
"agent",
"core",
"application",
"service",
"DigitalEmployeeSyncApplicationServiceImpl.java",
);
const dtoPath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-adapter",
"src",
"main",
"java",
"com",
"xspaceagi",
"agent",
"core",
"adapter",
"dto",
"digital",
"DigitalEmployeeAdminWorkbenchRowDto.java",
);
const testPath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-application",
"src",
"test",
"java",
"com",
"xspaceagi",
"agent",
"core",
"application",
"service",
"DigitalEmployeeSyncApplicationServiceImplTest.java",
);
const qimingOpsPath = path.join(qimingRoot, "src", "pages", "SystemManagement", "Content", "DigitalEmployeeOps", "index.tsx");
const qimingTypesPath = path.join(qimingRoot, "src", "types", "interfaces", "systemManage.ts");
const docsPath = path.resolve(packageRoot, "..", "..", "docs", "digital-employee-frontend-integration-plan.md");
const service = fs.readFileSync(servicePath, "utf8");
const dto = fs.readFileSync(dtoPath, "utf8");
const test = fs.readFileSync(testPath, "utf8");
const qimingOps = fs.readFileSync(qimingOpsPath, "utf8");
const qimingTypes = fs.readFileSync(qimingTypesPath, "utf8");
const docs = fs.readFileSync(docsPath, "utf8");
const requiredSnippets = [
[service, "governance_policies", "backend governance policies view"],
[service, "plan_revision_materialization", "backend plan revision materialization view"],
[service, "isGovernancePolicyRow", "backend governance policy matcher"],
[service, "isPlanRevisionMaterializationRow", "backend revision materialization matcher"],
[service, "governance_policy:pull_policy", "backend governance policy pull action"],
[service, "hard_interrupt_disabled", "backend hard interrupt disabled reason"],
[dto, "governance_policy_id", "backend governance policy id field"],
[dto, "policy_guard_status", "backend policy guard status field"],
[dto, "revision_fact_id", "backend revision fact id field"],
[dto, "revision_materialized", "backend revision materialized field"],
[dto, "changed_fields", "backend changed fields row field"],
[test, "queryAdminWorkbenchGovernancePoliciesReturnsPolicyRowsOnly", "backend governance policies test"],
[test, "queryAdminWorkbenchPlanRevisionMaterializationReturnsRevisionRowsOnly", "backend revision materialization test"],
[test, "createAdminActionAllowsGovernancePolicyRevisionLowRiskActions", "backend low risk action test"],
[qimingTypes, "governance_policies", "qiming governance policies view type"],
[qimingTypes, "plan_revision_materialization", "qiming revision materialization view type"],
[qimingTypes, "governance_policy_id", "qiming governance policy id field"],
[qimingTypes, "revision_fact_id", "qiming revision fact id field"],
[qimingOps, "治理策略", "qiming governance policies tab"],
[qimingOps, "版本物化", "qiming revision materialization tab"],
[qimingOps, "策略保护", "qiming policy guard copy"],
[qimingOps, "版本事实来源", "qiming revision fact source copy"],
[qimingOps, "不开放 ACP 硬中断", "qiming ACP hard interrupt guard copy"],
[docs, "正式管理端治理策略预览", "docs governance policy absorption"],
[docs, "计划版本物化预览", "docs revision materialization absorption"],
];
const missing = requiredSnippets
.filter(([content, snippet]) => !content.includes(snippet))
.map(([, , label]) => label);
if (missing.length > 0) {
throw new Error(`Missing admin governance policy and revision materialization hooks: ${missing.join(", ")}`);
}
console.log("[DigitalEmployeeCheck] admin governance policy and revision materialization hooks OK");
}
function checkDigitalAdminPolicyGovernanceClosure() {
console.log("\n[DigitalEmployeeCheck] Verify admin policy governance approval route closure hooks");
const backendRoot = path.resolve(packageRoot, "..", "..", "..", "qiming-backend");
const qimingRoot = path.resolve(packageRoot, "..", "..", "..", "qiming");
const servicePath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-application",
"src",
"main",
"java",
"com",
"xspaceagi",
"agent",
"core",
"application",
"service",
"DigitalEmployeeSyncApplicationServiceImpl.java",
);
const dtoPath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-adapter",
"src",
"main",
"java",
"com",
"xspaceagi",
"agent",
"core",
"adapter",
"dto",
"digital",
"DigitalEmployeeAdminWorkbenchRowDto.java",
);
const testPath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-application",
"src",
"test",
"java",
"com",
"xspaceagi",
"agent",
"core",
"application",
"service",
"DigitalEmployeeSyncApplicationServiceImplTest.java",
);
const qimingOpsPath = path.join(qimingRoot, "src", "pages", "SystemManagement", "Content", "DigitalEmployeeOps", "index.tsx");
const qimingTypesPath = path.join(qimingRoot, "src", "types", "interfaces", "systemManage.ts");
const docsPath = path.resolve(packageRoot, "..", "..", "docs", "digital-employee-frontend-integration-plan.md");
const service = fs.readFileSync(servicePath, "utf8");
const dto = fs.readFileSync(dtoPath, "utf8");
const test = fs.readFileSync(testPath, "utf8");
const qimingOps = fs.readFileSync(qimingOpsPath, "utf8");
const qimingTypes = fs.readFileSync(qimingTypesPath, "utf8");
const docs = fs.readFileSync(docsPath, "utf8");
const requiredSnippets = [
[service, "policy_center", "backend policy center view"],
[service, "governance_policies", "backend governance policies view"],
[service, "approval_policies", "backend approval policies view"],
[service, "route_governance", "backend route governance view"],
[service, "request_policy_edit", "backend low risk policy edit request action"],
[service, "request_policy_merge_review", "backend low risk policy merge review action"],
[service, "auto_merge_policy_disabled", "backend auto merge disabled reason"],
[service, "auto_decision_disabled", "backend auto decision disabled reason"],
[service, "route_recovery_disabled", "backend route recovery disabled reason"],
[dto, "merge_request_id", "backend merge request row field"],
[dto, "merge_recommendation", "backend merge recommendation row field"],
[dto, "approval_policy_edit_disabled", "backend approval policy edit guard field"],
[dto, "route_policy_edit_disabled", "backend route policy edit guard field"],
[dto, "route_recovery_disabled", "backend route recovery guard field"],
[dto, "high_risk_execution_disabled", "backend high risk execution guard field"],
[test, "queryAdminWorkbenchPolicyCenterReturnsPolicyEditAndMergeShellRows", "backend policy center closure test"],
[test, "queryAdminWorkbenchGovernancePoliciesReturnsExecutionGuardRows", "backend governance execution guard test"],
[test, "queryAdminWorkbenchApprovalPoliciesReturnsDecisionGuardShellRows", "backend approval decision guard test"],
[test, "queryAdminWorkbenchRouteGovernanceReturnsRecoveryGuardRows", "backend route recovery guard test"],
[test, "createAdminActionAllowsLowRiskPolicyGovernanceIntents", "backend low risk policy governance actions test"],
[test, "createAdminActionRejectsHighRiskPolicyGovernanceExecution", "backend high risk policy governance rejection test"],
[qimingTypes, "merge_request_id", "qiming merge request type field"],
[qimingTypes, "merge_recommendation", "qiming merge recommendation type field"],
[qimingTypes, "approval_policy_edit_disabled", "qiming approval policy guard type field"],
[qimingTypes, "route_policy_edit_disabled", "qiming route policy guard type field"],
[qimingTypes, "route_recovery_disabled", "qiming route recovery guard type field"],
[qimingTypes, "high_risk_execution_disabled", "qiming high risk guard type field"],
[qimingOps, "策略中心", "qiming policy center tab"],
[qimingOps, "治理策略", "qiming governance policies tab"],
[qimingOps, "审批策略", "qiming approval policies tab"],
[qimingOps, "路由治理", "qiming route governance tab"],
[qimingOps, "请求策略编辑", "qiming policy edit request action"],
[qimingOps, "请求合并复核", "qiming merge review action"],
[qimingOps, "策略编辑/合并安全壳", "qiming policy edit merge shell copy"],
[qimingOps, "合并建议", "qiming merge recommendation copy"],
[qimingOps, "审批策略编辑/裁决安全壳", "qiming approval decision shell copy"],
[qimingOps, "路由策略编辑/恢复安全壳", "qiming route recovery shell copy"],
[qimingOps, "高风险恢复执行不开放", "qiming high risk route recovery guard copy"],
[qimingOps, "真实远程策略强编辑不开放", "qiming remote force edit guard copy"],
[docs, "策略治理审批路由总收口已开始吸收", "docs policy governance closure absorption"],
];
const missing = requiredSnippets
.filter(([content, snippet]) => !content.includes(snippet))
.map(([, , label]) => label);
if (missing.length > 0) {
throw new Error(`Missing admin policy governance closure hooks: ${missing.join(", ")}`);
}
console.log("[DigitalEmployeeCheck] admin policy governance closure hooks OK");
}
function checkDigitalAdminPlanStepDispatchWorkbench() {
console.log("\n[DigitalEmployeeCheck] Verify admin plan step dispatch workbench hooks");
const backendRoot = path.resolve(packageRoot, "..", "..", "..", "qiming-backend");
const qimingRoot = path.resolve(packageRoot, "..", "..", "..", "qiming");
const servicePath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-application",
"src",
"main",
"java",
"com",
"xspaceagi",
"agent",
"core",
"application",
"service",
"DigitalEmployeeSyncApplicationServiceImpl.java",
);
const dtoPath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-adapter",
"src",
"main",
"java",
"com",
"xspaceagi",
"agent",
"core",
"adapter",
"dto",
"digital",
"DigitalEmployeeAdminWorkbenchRowDto.java",
);
const testPath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-application",
"src",
"test",
"java",
"com",
"xspaceagi",
"agent",
"core",
"application",
"service",
"DigitalEmployeeSyncApplicationServiceImplTest.java",
);
const qimingOpsPath = path.join(qimingRoot, "src", "pages", "SystemManagement", "Content", "DigitalEmployeeOps", "index.tsx");
const qimingTypesPath = path.join(qimingRoot, "src", "types", "interfaces", "systemManage.ts");
const docsPath = path.resolve(packageRoot, "..", "..", "docs", "digital-employee-frontend-integration-plan.md");
const service = fs.readFileSync(servicePath, "utf8");
const dto = fs.readFileSync(dtoPath, "utf8");
const test = fs.readFileSync(testPath, "utf8");
const qimingOps = fs.readFileSync(qimingOpsPath, "utf8");
const qimingTypes = fs.readFileSync(qimingTypesPath, "utf8");
const docs = fs.readFileSync(docsPath, "utf8");
const requiredSnippets = [
[service, "plan_step_dispatch", "backend plan step dispatch view"],
[service, "isPlanStepDispatchRow", "backend plan step dispatch matcher"],
[service, "plan_step:retry_policy_check", "backend plan step retry policy action"],
[service, "remote_lease_rejected", "backend remote lease rejection matcher"],
[service, "local_fallback", "backend local fallback matcher"],
[dto, "step_id", "backend step id field"],
[dto, "lease_state", "backend lease state field"],
[dto, "dispatch_status", "backend dispatch status field"],
[dto, "dispatch_policy_key", "backend dispatch policy key field"],
[test, "queryAdminWorkbenchPlanStepDispatchReturnsStepLeaseAndDispatchRowsOnly", "backend plan step dispatch test"],
[qimingTypes, "plan_step_dispatch", "qiming plan step dispatch view type"],
[qimingTypes, "step_id", "qiming step id field"],
[qimingTypes, "lease_state", "qiming lease state field"],
[qimingTypes, "dispatch_status", "qiming dispatch status field"],
[qimingTypes, "dispatch_policy_key", "qiming dispatch policy field"],
[qimingOps, "步骤调度", "qiming plan step dispatch tab"],
[qimingOps, "重试策略检查", "qiming plan step retry policy action"],
[qimingOps, "不远程派发", "qiming remote dispatch guard copy"],
[qimingOps, "不抢占租约", "qiming lease takeover guard copy"],
[qimingOps, "不释放租约", "qiming lease release guard copy"],
[docs, "正式管理端步骤调度预览", "docs plan step dispatch absorption"],
];
const missing = requiredSnippets
.filter(([content, snippet]) => !content.includes(snippet))
.map(([, , label]) => label);
if (missing.length > 0) {
throw new Error(`Missing admin plan step dispatch hooks: ${missing.join(", ")}`);
}
console.log("[DigitalEmployeeCheck] admin plan step dispatch hooks OK");
}
function checkDigitalAdminCollaborationOperationsClosure() {
console.log("\n[DigitalEmployeeCheck] Verify admin artifact, notification and daily report collaboration closure hooks");
const backendRoot = path.resolve(packageRoot, "..", "..", "..", "qiming-backend");
const qimingRoot = path.resolve(packageRoot, "..", "..", "..", "qiming");
const servicePath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-application",
"src",
"main",
"java",
"com",
"xspaceagi",
"agent",
"core",
"application",
"service",
"DigitalEmployeeSyncApplicationServiceImpl.java",
);
const dtoPath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-adapter",
"src",
"main",
"java",
"com",
"xspaceagi",
"agent",
"core",
"adapter",
"dto",
"digital",
"DigitalEmployeeAdminWorkbenchRowDto.java",
);
const testPath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-application",
"src",
"test",
"java",
"com",
"xspaceagi",
"agent",
"core",
"application",
"service",
"DigitalEmployeeSyncApplicationServiceImplTest.java",
);
const qimingOpsPath = path.join(qimingRoot, "src", "pages", "SystemManagement", "Content", "DigitalEmployeeOps", "index.tsx");
const qimingTypesPath = path.join(qimingRoot, "src", "types", "interfaces", "systemManage.ts");
const docsPath = path.resolve(packageRoot, "..", "..", "docs", "digital-employee-frontend-integration-plan.md");
const service = fs.readFileSync(servicePath, "utf8");
const dto = fs.readFileSync(dtoPath, "utf8");
const test = fs.readFileSync(testPath, "utf8");
const qimingOps = fs.readFileSync(qimingOpsPath, "utf8");
const qimingTypes = fs.readFileSync(qimingTypesPath, "utf8");
const docs = fs.readFileSync(docsPath, "utf8");
const requiredSnippets = [
[service, "record_cleanup_request", "backend cleanup request admin action"],
[service, "record_send_request", "backend send request admin action"],
[service, "record_send_result", "backend send result admin action"],
[service, "remote_delete_disabled", "backend remote delete disabled reason"],
[service, "real_push_disabled", "backend real push disabled reason"],
[service, "external_send_disabled", "backend external send disabled reason"],
[service, "third_party_channel_disabled", "backend third party send disabled reason"],
[service, "artifact_graph_id", "backend artifact graph projection"],
[service, "cleanup_request_id", "backend cleanup request projection"],
[service, "batch_cleanup_disabled", "backend batch cleanup disabled projection"],
[service, "retention_conflict_status", "backend retention conflict projection"],
[service, "cleanup_conflict_status", "backend cleanup conflict projection"],
[service, "collaboration_thread_id", "backend collaboration thread projection"],
[service, "subscription_policy_id", "backend subscription policy projection"],
[service, "delivery_attempt_id", "backend delivery attempt projection"],
[service, "send_request_id", "backend send request projection"],
[service, "daily_report_fact_id", "backend daily report fact projection"],
[service, "daily_report_physical_fact_status", "backend daily report physical fact projection"],
[service, "advanced_query_supported", "backend advanced query projection"],
[dto, "cleanup_request_id", "backend cleanup request row field"],
[dto, "artifact_graph_id", "backend artifact graph row field"],
[dto, "related_artifact_ids", "backend related artifact ids row field"],
[dto, "cleanup_eligible_count", "backend cleanup eligible count row field"],
[dto, "batch_cleanup_disabled", "backend batch cleanup disabled row field"],
[dto, "retention_conflict_status", "backend retention conflict row field"],
[dto, "cleanup_conflict_status", "backend cleanup conflict row field"],
[dto, "remote_delete_disabled", "backend remote delete row field"],
[dto, "collaboration_thread_id", "backend collaboration thread row field"],
[dto, "subscription_policy_id", "backend subscription policy row field"],
[dto, "subscription_conflict_status", "backend subscription conflict row field"],
[dto, "delivery_attempt_id", "backend delivery attempt row field"],
[dto, "send_request_id", "backend send request row field"],
[dto, "real_push_disabled", "backend real push disabled row field"],
[dto, "daily_report_fact_id", "backend daily report fact row field"],
[dto, "daily_report_physical_fact_status", "backend daily report physical fact row field"],
[dto, "external_send_disabled", "backend external send disabled row field"],
[dto, "third_party_channel_disabled", "backend third party disabled row field"],
[dto, "advanced_query_supported", "backend advanced query row field"],
[test, "getCleanupRequestId", "backend cleanup request test assertion"],
[test, "queryAdminWorkbenchArtifactLifecycleReturnsGraphAndConflictRows", "backend artifact graph test"],
[test, "queryAdminWorkbenchNotificationsReturnsCollaborationAndDeliveryRows", "backend notification collaboration test"],
[test, "queryAdminWorkbenchDailyReportsReturnsPhysicalFactAndSendShellRows", "backend daily report send shell test"],
[test, "queryBusinessViewsExposeAdvancedQueryAndMaterializedSource", "backend business query shell test"],
[test, "createAdminActionAllowsLowRiskAssetNotificationReportIntents", "backend low risk asset notification report actions test"],
[test, "createAdminActionRejectsHighRiskAssetNotificationReportExecution", "backend high risk asset notification report rejection test"],
[test, "getSubscriptionPolicyId", "backend subscription policy test assertion"],
[test, "getDailyReportFactId", "backend daily report fact test assertion"],
[qimingTypes, "cleanup_request_id", "qiming cleanup request type field"],
[qimingTypes, "artifact_graph_id", "qiming artifact graph type field"],
[qimingTypes, "related_artifact_ids", "qiming related artifacts type field"],
[qimingTypes, "retention_conflict_status", "qiming retention conflict type field"],
[qimingTypes, "cleanup_conflict_status", "qiming cleanup conflict type field"],
[qimingTypes, "remote_delete_disabled", "qiming remote delete guard type field"],
[qimingTypes, "collaboration_thread_id", "qiming collaboration thread type field"],
[qimingTypes, "subscription_policy_id", "qiming subscription policy type field"],
[qimingTypes, "subscription_conflict_status", "qiming subscription conflict type field"],
[qimingTypes, "delivery_attempt_id", "qiming delivery attempt type field"],
[qimingTypes, "send_request_id", "qiming send request type field"],
[qimingTypes, "real_push_disabled", "qiming real push guard type field"],
[qimingTypes, "daily_report_fact_id", "qiming daily report fact type field"],
[qimingTypes, "daily_report_physical_fact_status", "qiming daily physical fact type field"],
[qimingTypes, "external_send_disabled", "qiming external send guard type field"],
[qimingTypes, "third_party_channel_disabled", "qiming third party send guard type field"],
[qimingTypes, "advanced_query_supported", "qiming advanced query type field"],
[qimingOps, "记录批量清理申请", "qiming cleanup request action"],
[qimingOps, "跨设备/跨版本图谱摘要", "qiming artifact graph copy"],
[qimingOps, "保留冲突", "qiming retention conflict copy"],
[qimingOps, "清理冲突", "qiming cleanup conflict copy"],
[qimingOps, "远端删除禁用", "qiming remote delete guard copy"],
[qimingOps, "协作线程", "qiming collaboration thread copy"],
[qimingOps, "订阅策略", "qiming subscription policy copy"],
[qimingOps, "投递尝试", "qiming delivery attempt copy"],
[qimingOps, "发送请求", "qiming send request copy"],
[qimingOps, "真实推送禁用", "qiming real push guard copy"],
[qimingOps, "审批状态", "qiming daily approval status copy"],
[qimingOps, "物理事实状态", "qiming daily physical fact copy"],
[qimingOps, "外部发送禁用", "qiming external send guard copy"],
[qimingOps, "第三方发送", "qiming third party send guard copy"],
[qimingOps, "高级查询", "qiming advanced query copy"],
[qimingOps, "物化来源", "qiming materialized source copy"],
[docs, "正式管理端协作运营收口", "docs collaboration closure absorption"],
[docs, "资产通知日报业务查询总收口已开始吸收", "docs asset notification report closure absorption"],
];
const missing = requiredSnippets
.filter(([content, snippet]) => !content.includes(snippet))
.map(([, , label]) => label);
if (missing.length > 0) {
throw new Error(`Missing admin collaboration operations closure hooks: ${missing.join(", ")}`);
}
console.log("[DigitalEmployeeCheck] admin collaboration operations closure hooks OK");
}
function checkDigitalAdminServiceOperationsWorkbench() {
console.log("\n[DigitalEmployeeCheck] Verify admin service operations workbench hooks");
const backendRoot = path.resolve(packageRoot, "..", "..", "..", "qiming-backend");
const qimingRoot = path.resolve(packageRoot, "..", "..", "..", "qiming");
const servicePath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-application",
"src",
"main",
"java",
"com",
"xspaceagi",
"agent",
"core",
"application",
"service",
"DigitalEmployeeSyncApplicationServiceImpl.java",
);
const dtoPath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-adapter",
"src",
"main",
"java",
"com",
"xspaceagi",
"agent",
"core",
"adapter",
"dto",
"digital",
"DigitalEmployeeAdminWorkbenchRowDto.java",
);
const testPath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-application",
"src",
"test",
"java",
"com",
"xspaceagi",
"agent",
"core",
"application",
"service",
"DigitalEmployeeSyncApplicationServiceImplTest.java",
);
const qimingOpsPath = path.join(qimingRoot, "src", "pages", "SystemManagement", "Content", "DigitalEmployeeOps", "index.tsx");
const qimingTypesPath = path.join(qimingRoot, "src", "types", "interfaces", "systemManage.ts");
const docsPath = path.resolve(packageRoot, "..", "..", "docs", "digital-employee-frontend-integration-plan.md");
const service = fs.readFileSync(servicePath, "utf8");
const dto = fs.readFileSync(dtoPath, "utf8");
const test = fs.readFileSync(testPath, "utf8");
const qimingOps = fs.readFileSync(qimingOpsPath, "utf8");
const qimingTypes = fs.readFileSync(qimingTypesPath, "utf8");
const docs = fs.readFileSync(docsPath, "utf8");
const requiredSnippets = [
[service, "service_operations", "backend service operations view"],
[service, "isServiceOperationsRow", "backend service operations matcher"],
[service, "managed_service", "backend managed service classifier"],
[service, "managed_service_snapshot", "backend managed service snapshot classifier"],
[service, "service_lease", "backend service lease classifier"],
[service, "service_restart", "backend service restart classifier"],
[service, "service_recovery", "backend service recovery classifier"],
[dto, "service_id", "backend service id row field"],
[dto, "service_lease_id", "backend service lease row field"],
[dto, "restart_record_id", "backend service restart record row field"],
[dto, "recovery_policy_id", "backend service recovery policy row field"],
[dto, "auto_recovery_status", "backend auto recovery status row field"],
[dto, "service_start_disabled", "backend service start disabled row field"],
[dto, "service_stop_disabled", "backend service stop disabled row field"],
[dto, "lease_takeover_disabled", "backend lease takeover disabled row field"],
[dto, "auto_recovery_disabled", "backend auto recovery disabled row field"],
[dto, "recovery_action_request_id", "backend recovery action request row field"],
[test, "queryAdminWorkbenchServiceOperationsReturnsServiceHealthAndRestartRowsOnly", "backend service operations test"],
[test, "queryAdminWorkbenchServiceOperationsReturnsLeaseRestartAndRecoveryRowsOnly", "backend service recovery operations test"],
[test, "queryAdminWorkbenchServiceOperationsReturnsSafetyShellFields", "backend service recovery safety shell test"],
[qimingTypes, "service_operations", "qiming service operations view type"],
[qimingTypes, "service_id", "qiming service id field"],
[qimingTypes, "service_lease_id", "qiming service lease id field"],
[qimingTypes, "restart_record_id", "qiming restart record id field"],
[qimingTypes, "recovery_policy_id", "qiming recovery policy id field"],
[qimingTypes, "auto_recovery_status", "qiming auto recovery status field"],
[qimingTypes, "service_start_disabled", "qiming service start disabled field"],
[qimingTypes, "service_stop_disabled", "qiming service stop disabled field"],
[qimingTypes, "lease_takeover_disabled", "qiming lease takeover disabled field"],
[qimingTypes, "auto_recovery_disabled", "qiming auto recovery disabled field"],
[qimingTypes, "recovery_action_request_id", "qiming recovery action request field"],
[qimingOps, "服务运营", "qiming service operations tab"],
[qimingOps, "只记录复核/忽略意图", "qiming service operations safety copy"],
[qimingOps, "不开放 start/stop", "qiming service start stop guard copy"],
[qimingOps, "不接管 lease", "qiming service lease takeover guard copy"],
[qimingOps, "不自动恢复服务", "qiming service auto recovery guard copy"],
[qimingOps, "恢复请求", "qiming service recovery request copy"],
[docs, "正式管理端服务恢复运营预览", "docs service recovery absorption"],
[docs, "恢复请求状态", "docs service recovery request absorption"],
];
const missing = requiredSnippets
.filter(([content, snippet]) => !content.includes(snippet))
.map(([, , label]) => label);
if (missing.length > 0) {
throw new Error(`Missing admin service operations workbench hooks: ${missing.join(", ")}`);
}
console.log("[DigitalEmployeeCheck] admin service operations workbench hooks OK");
}
function checkDigitalAdminRouteGovernanceWorkbench() {
console.log("\n[DigitalEmployeeCheck] Verify admin route governance workbench hooks");
const backendRoot = path.resolve(packageRoot, "..", "..", "..", "qiming-backend");
const qimingRoot = path.resolve(packageRoot, "..", "..", "..", "qiming");
const servicePath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-application",
"src",
"main",
"java",
"com",
"xspaceagi",
"agent",
"core",
"application",
"service",
"DigitalEmployeeSyncApplicationServiceImpl.java",
);
const testPath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-application",
"src",
"test",
"java",
"com",
"xspaceagi",
"agent",
"core",
"application",
"service",
"DigitalEmployeeSyncApplicationServiceImplTest.java",
);
const qimingOpsPath = path.join(qimingRoot, "src", "pages", "SystemManagement", "Content", "DigitalEmployeeOps", "index.tsx");
const qimingTypesPath = path.join(qimingRoot, "src", "types", "interfaces", "systemManage.ts");
const docsPath = path.resolve(packageRoot, "..", "..", "docs", "digital-employee-frontend-integration-plan.md");
const service = fs.readFileSync(servicePath, "utf8");
const test = fs.readFileSync(testPath, "utf8");
const qimingOps = fs.readFileSync(qimingOpsPath, "utf8");
const qimingTypes = fs.readFileSync(qimingTypesPath, "utf8");
const docs = fs.readFileSync(docsPath, "utf8");
const requiredSnippets = [
[service, "route_governance", "backend route governance view"],
[service, "isRouteGovernanceRow", "backend route governance matcher"],
[service, "route_decision_blocked", "backend route blocked classifier"],
[service, "route_action_policy_blocked", "backend route action policy blocked classifier"],
[service, "route_action_missing_context", "backend route action missing context classifier"],
[service, "governance_", "backend governance event classifier"],
[test, "queryAdminWorkbenchRouteGovernanceReturnsRouteAndGovernanceRowsOnly", "backend route governance test"],
[qimingTypes, "route_governance", "qiming route governance view type"],
[qimingOps, "路由治理", "qiming route governance tab"],
[qimingOps, "重试策略检查", "qiming route policy retry action"],
[qimingOps, "标记已看", "qiming route mark reviewed action"],
[qimingOps, "忽略", "qiming route dismiss action"],
[docs, "正式管理端路由治理预览", "docs route governance absorption"],
];
const missing = requiredSnippets
.filter(([content, snippet]) => !content.includes(snippet))
.map(([, , label]) => label);
if (missing.length > 0) {
throw new Error(`Missing admin route governance workbench hooks: ${missing.join(", ")}`);
}
console.log("[DigitalEmployeeCheck] admin route governance workbench hooks OK");
}
function checkDigitalAdminNotificationWorkbench() {
console.log("\n[DigitalEmployeeCheck] Verify admin notification workbench hooks");
const backendRoot = path.resolve(packageRoot, "..", "..", "..", "qiming-backend");
const qimingRoot = path.resolve(packageRoot, "..", "..", "..", "qiming");
const servicePath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-application",
"src",
"main",
"java",
"com",
"xspaceagi",
"agent",
"core",
"application",
"service",
"DigitalEmployeeSyncApplicationServiceImpl.java",
);
const dtoPath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-adapter",
"src",
"main",
"java",
"com",
"xspaceagi",
"agent",
"core",
"adapter",
"dto",
"digital",
"DigitalEmployeeAdminWorkbenchRowDto.java",
);
const testPath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-application",
"src",
"test",
"java",
"com",
"xspaceagi",
"agent",
"core",
"application",
"service",
"DigitalEmployeeSyncApplicationServiceImplTest.java",
);
const qimingOpsPath = path.join(qimingRoot, "src", "pages", "SystemManagement", "Content", "DigitalEmployeeOps", "index.tsx");
const qimingTypesPath = path.join(qimingRoot, "src", "types", "interfaces", "systemManage.ts");
const docsPath = path.resolve(packageRoot, "..", "..", "docs", "digital-employee-frontend-integration-plan.md");
const service = fs.readFileSync(servicePath, "utf8");
const dto = fs.readFileSync(dtoPath, "utf8");
const test = fs.readFileSync(testPath, "utf8");
const qimingOps = fs.readFileSync(qimingOpsPath, "utf8");
const qimingTypes = fs.readFileSync(qimingTypesPath, "utf8");
const docs = fs.readFileSync(docsPath, "utf8");
const requiredSnippets = [
[service, "notifications", "backend notification view"],
[service, "isNotificationRow", "backend notification matcher"],
[dto, "notification_id", "backend notification id row field"],
[service, "action_notification", "backend action notification classifier"],
[service, "notification", "backend notification kind classifier"],
[test, "queryAdminWorkbenchNotificationsReturnsNotificationRowsOnly", "backend notification workbench test"],
[qimingTypes, "notifications", "qiming notification view type"],
[qimingOps, "通知运营", "qiming notification workbench tab"],
[qimingOps, "标记已读", "qiming notification read action"],
[qimingOps, "忽略", "qiming notification dismiss action"],
[docs, "正式管理端通知运营预览", "docs notification workbench absorption"],
];
const missing = requiredSnippets
.filter(([content, snippet]) => !content.includes(snippet))
.map(([, , label]) => label);
if (missing.length > 0) {
throw new Error(`Missing admin notification workbench hooks: ${missing.join(", ")}`);
}
console.log("[DigitalEmployeeCheck] admin notification workbench hooks OK");
}
function checkDigitalAdminNotificationPoliciesWorkbench() {
console.log("\n[DigitalEmployeeCheck] Verify admin notification policies workbench hooks");
const backendRoot = path.resolve(packageRoot, "..", "..", "..", "qiming-backend");
const qimingRoot = path.resolve(packageRoot, "..", "..", "..", "qiming");
const servicePath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-application",
"src",
"main",
"java",
"com",
"xspaceagi",
"agent",
"core",
"application",
"service",
"DigitalEmployeeSyncApplicationServiceImpl.java",
);
const dtoPath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-adapter",
"src",
"main",
"java",
"com",
"xspaceagi",
"agent",
"core",
"adapter",
"dto",
"digital",
"DigitalEmployeeAdminWorkbenchRowDto.java",
);
const testPath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-application",
"src",
"test",
"java",
"com",
"xspaceagi",
"agent",
"core",
"application",
"service",
"DigitalEmployeeSyncApplicationServiceImplTest.java",
);
const qimingOpsPath = path.join(qimingRoot, "src", "pages", "SystemManagement", "Content", "DigitalEmployeeOps", "index.tsx");
const qimingTypesPath = path.join(qimingRoot, "src", "types", "interfaces", "systemManage.ts");
const docsPath = path.resolve(packageRoot, "..", "..", "docs", "digital-employee-frontend-integration-plan.md");
const service = fs.readFileSync(servicePath, "utf8");
const dto = fs.readFileSync(dtoPath, "utf8");
const test = fs.readFileSync(testPath, "utf8");
const qimingOps = fs.readFileSync(qimingOpsPath, "utf8");
const qimingTypes = fs.readFileSync(qimingTypesPath, "utf8");
const docs = fs.readFileSync(docsPath, "utf8");
const requiredSnippets = [
[service, "notification_policies", "backend notification policies view"],
[service, "isNotificationPolicyRow", "backend notification policies matcher"],
[dto, "notification_policy_id", "backend notification policy id row field"],
[dto, "subscription_status", "backend subscription status row field"],
[dto, "desktop_enabled", "backend desktop enabled row field"],
[dto, "minimum_level", "backend minimum level row field"],
[test, "queryAdminWorkbenchNotificationPoliciesReturnsSubscriptionPolicyRowsOnly", "backend notification policies test"],
[qimingTypes, "notification_policies", "qiming notification policies view type"],
[qimingOps, "通知策略", "qiming notification policies tab"],
[qimingOps, "拉取通知策略", "qiming notification policy pull action"],
[qimingOps, "不远程编辑策略", "qiming notification policy safety copy"],
[qimingOps, "不触发真实推送", "qiming notification push safety copy"],
[docs, "正式管理端通知订阅策略预览", "docs notification policies absorption"],
];
const missing = requiredSnippets
.filter(([content, snippet]) => !content.includes(snippet))
.map(([, , label]) => label);
if (missing.length > 0) {
throw new Error(`Missing admin notification policies workbench hooks: ${missing.join(", ")}`);
}
console.log("[DigitalEmployeeCheck] admin notification policies workbench hooks OK");
}
function checkDigitalAdminArtifactLifecycleWorkbench() {
console.log("\n[DigitalEmployeeCheck] Verify admin artifact lifecycle workbench hooks");
const backendRoot = path.resolve(packageRoot, "..", "..", "..", "qiming-backend");
const qimingRoot = path.resolve(packageRoot, "..", "..", "..", "qiming");
const servicePath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-application",
"src",
"main",
"java",
"com",
"xspaceagi",
"agent",
"core",
"application",
"service",
"DigitalEmployeeSyncApplicationServiceImpl.java",
);
const dtoPath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-adapter",
"src",
"main",
"java",
"com",
"xspaceagi",
"agent",
"core",
"adapter",
"dto",
"digital",
"DigitalEmployeeAdminWorkbenchRowDto.java",
);
const testPath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-application",
"src",
"test",
"java",
"com",
"xspaceagi",
"agent",
"core",
"application",
"service",
"DigitalEmployeeSyncApplicationServiceImplTest.java",
);
const qimingOpsPath = path.join(qimingRoot, "src", "pages", "SystemManagement", "Content", "DigitalEmployeeOps", "index.tsx");
const qimingTypesPath = path.join(qimingRoot, "src", "types", "interfaces", "systemManage.ts");
const docsPath = path.resolve(packageRoot, "..", "..", "docs", "digital-employee-frontend-integration-plan.md");
const service = fs.readFileSync(servicePath, "utf8");
const dto = fs.readFileSync(dtoPath, "utf8");
const test = fs.readFileSync(testPath, "utf8");
const qimingOps = fs.readFileSync(qimingOpsPath, "utf8");
const qimingTypes = fs.readFileSync(qimingTypesPath, "utf8");
const docs = fs.readFileSync(docsPath, "utf8");
const requiredSnippets = [
[service, "artifact_lifecycle", "backend artifact lifecycle view"],
[service, "isArtifactLifecycleRow", "backend artifact lifecycle matcher"],
[service, "artifact_", "backend artifact event classifier"],
[dto, "artifact_id", "backend artifact id row field"],
[dto, "artifact_group_id", "backend artifact group id row field"],
[dto, "artifact_count", "backend artifact count row field"],
[dto, "version_count", "backend artifact version count row field"],
[dto, "latest_artifact_id", "backend latest artifact id row field"],
[test, "queryAdminWorkbenchArtifactLifecycleReturnsArtifactRowsOnly", "backend artifact lifecycle test"],
[test, "getArtifactGroupId", "backend artifact group assertion"],
[qimingTypes, "artifact_lifecycle", "qiming artifact lifecycle view type"],
[qimingTypes, "artifact_group_id", "qiming artifact group id field"],
[qimingTypes, "version_count", "qiming artifact version count field"],
[qimingOps, "产物治理", "qiming artifact lifecycle tab"],
[qimingOps, "跨设备/跨版本聚合", "qiming artifact aggregate copy"],
[qimingOps, "标记保留", "qiming artifact keep action"],
[qimingOps, "标记到期", "qiming artifact expire action"],
[qimingOps, "标记复核", "qiming artifact reviewed action"],
[docs, "正式管理端产物生命周期预览", "docs artifact lifecycle absorption"],
[docs, "正式管理端产物跨设备聚合预览", "docs artifact aggregate absorption"],
];
const missing = requiredSnippets
.filter(([content, snippet]) => !content.includes(snippet))
.map(([, , label]) => label);
if (missing.length > 0) {
throw new Error(`Missing admin artifact lifecycle workbench hooks: ${missing.join(", ")}`);
}
console.log("[DigitalEmployeeCheck] admin artifact lifecycle workbench hooks OK");
}
function checkDigitalAdminAuditDiagnosticsWorkbench() {
console.log("\n[DigitalEmployeeCheck] Verify admin audit diagnostics workbench hooks");
const backendRoot = path.resolve(packageRoot, "..", "..", "..", "qiming-backend");
const qimingRoot = path.resolve(packageRoot, "..", "..", "..", "qiming");
const servicePath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-application",
"src",
"main",
"java",
"com",
"xspaceagi",
"agent",
"core",
"application",
"service",
"DigitalEmployeeSyncApplicationServiceImpl.java",
);
const testPath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-application",
"src",
"test",
"java",
"com",
"xspaceagi",
"agent",
"core",
"application",
"service",
"DigitalEmployeeSyncApplicationServiceImplTest.java",
);
const dtoPath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-adapter",
"src",
"main",
"java",
"com",
"xspaceagi",
"agent",
"core",
"adapter",
"dto",
"digital",
"DigitalEmployeeAdminWorkbenchRowDto.java",
);
const qimingOpsPath = path.join(qimingRoot, "src", "pages", "SystemManagement", "Content", "DigitalEmployeeOps", "index.tsx");
const qimingTypesPath = path.join(qimingRoot, "src", "types", "interfaces", "systemManage.ts");
const docsPath = path.resolve(packageRoot, "..", "..", "docs", "digital-employee-frontend-integration-plan.md");
const service = fs.readFileSync(servicePath, "utf8");
const dto = fs.readFileSync(dtoPath, "utf8");
const test = fs.readFileSync(testPath, "utf8");
const qimingOps = fs.readFileSync(qimingOpsPath, "utf8");
const qimingTypes = fs.readFileSync(qimingTypesPath, "utf8");
const docs = fs.readFileSync(docsPath, "utf8");
const requiredSnippets = [
[service, "audits", "backend audits view"],
[service, "isAuditRow", "backend audit matcher"],
[service, "sandbox_audit", "backend sandbox audit category"],
[service, "token_cost", "backend token cost category"],
[service, "tool_call_audit", "backend tool call audit category"],
[service, "sync_failure", "backend sync failure audit category"],
[service, "audit_archive", "backend audit archive category"],
[service, "billing_estimate", "backend billing estimate category"],
[dto, "audit_archive_id", "backend audit archive id row field"],
[dto, "archive_status", "backend archive status row field"],
[dto, "archive_location", "backend archive location row field"],
[dto, "billing_estimate_id", "backend billing estimate id row field"],
[dto, "billing_source", "backend billing source row field"],
[test, "queryAdminWorkbenchAuditsReturnsAuditRowsOnly", "backend audit diagnostics test"],
[test, "getAuditArchiveId", "backend audit archive assertion"],
[test, "getBillingEstimateId", "backend billing estimate assertion"],
[test, "doesNotContainKeys(\"token\", \"authorization\", \"raw_input\", \"command\", \"prompt\")", "backend audit redaction assertion"],
[qimingTypes, "audits", "qiming audits view type"],
[qimingTypes, "audit_archive_id", "qiming audit archive id field"],
[qimingTypes, "billing_source", "qiming billing source field"],
[qimingOps, "审计诊断", "qiming audit diagnostics tab"],
[qimingOps, "归档位置", "qiming audit archive location copy"],
[qimingOps, "估算来源", "qiming billing source copy"],
[qimingOps, "标记复核", "qiming audit reviewed action"],
[qimingOps, "忽略", "qiming audit dismiss action"],
[qimingOps, "标记归档", "qiming audit archive action"],
[docs, "正式管理端审计诊断预览", "docs audit diagnostics absorption"],
[docs, "正式管理端审计归档账单预览", "docs audit archive billing absorption"],
];
const missing = requiredSnippets
.filter(([content, snippet]) => !content.includes(snippet))
.map(([, , label]) => label);
if (missing.length > 0) {
throw new Error(`Missing admin audit diagnostics workbench hooks: ${missing.join(", ")}`);
}
console.log("[DigitalEmployeeCheck] admin audit diagnostics workbench hooks OK");
}
function checkDigitalAdminDiagnosticRepairPreview() {
console.log("\n[DigitalEmployeeCheck] Verify admin diagnostic repair preview hooks");
const backendRoot = path.resolve(packageRoot, "..", "..", "..", "qiming-backend");
const qimingRoot = path.resolve(packageRoot, "..", "..", "..", "qiming");
const servicePath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-application",
"src",
"main",
"java",
"com",
"xspaceagi",
"agent",
"core",
"application",
"service",
"DigitalEmployeeSyncApplicationServiceImpl.java",
);
const dtoPath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-adapter",
"src",
"main",
"java",
"com",
"xspaceagi",
"agent",
"core",
"adapter",
"dto",
"digital",
"DigitalEmployeeAdminWorkbenchRowDto.java",
);
const testPath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-application",
"src",
"test",
"java",
"com",
"xspaceagi",
"agent",
"core",
"application",
"service",
"DigitalEmployeeSyncApplicationServiceImplTest.java",
);
const qimingOpsPath = path.join(qimingRoot, "src", "pages", "SystemManagement", "Content", "DigitalEmployeeOps", "index.tsx");
const qimingTypesPath = path.join(qimingRoot, "src", "types", "interfaces", "systemManage.ts");
const docsPath = path.resolve(packageRoot, "..", "..", "docs", "digital-employee-frontend-integration-plan.md");
const service = fs.readFileSync(servicePath, "utf8");
const dto = fs.readFileSync(dtoPath, "utf8");
const test = fs.readFileSync(testPath, "utf8");
const qimingOps = fs.readFileSync(qimingOpsPath, "utf8");
const qimingTypes = fs.readFileSync(qimingTypesPath, "utf8");
const docs = fs.readFileSync(docsPath, "utf8");
const requiredSnippets = [
[service, "isDiagnosticRepairRow", "backend diagnostic repair matcher"],
[service, "diagnostic:retry_check", "backend diagnostic retry action"],
[service, "diagnostic:record_repair_intent", "backend diagnostic repair intent action"],
[service, "run_shell_repair_disabled", "backend shell repair disabled reason"],
[service, "modify_system_config_disabled", "backend system config disabled reason"],
[dto, "diagnostic_id", "backend diagnostic id field"],
[dto, "issue_code", "backend issue code field"],
[dto, "repair_action", "backend repair action field"],
[dto, "repair_status", "backend repair status field"],
[dto, "safe_repair_disabled", "backend safe repair disabled field"],
[test, "queryAdminWorkbenchAuditsReturnsDiagnosticRepairRowsOnly", "backend diagnostic repair workbench test"],
[test, "createAdminActionAllowsLowRiskDiagnosticRepairIntent", "backend diagnostic low risk action test"],
[test, "createAdminActionRejectsHighRiskDiagnosticRepair", "backend diagnostic high risk action test"],
[qimingTypes, "diagnostic_id", "qiming diagnostic id field"],
[qimingTypes, "repair_action", "qiming repair action field"],
[qimingTypes, "safe_repair_disabled", "qiming safe repair disabled field"],
[qimingOps, "记录修复意图", "qiming repair intent action"],
[qimingOps, "重试诊断", "qiming retry diagnostic action"],
[qimingOps, "不执行 shell", "qiming shell repair guard copy"],
[qimingOps, "不修改系统配置", "qiming system config guard copy"],
[docs, "正式管理端诊断修复预览", "docs diagnostic repair absorption"],
];
const missing = requiredSnippets
.filter(([content, snippet]) => !content.includes(snippet))
.map(([, , label]) => label);
if (missing.length > 0) {
throw new Error(`Missing admin diagnostic repair preview hooks: ${missing.join(", ")}`);
}
console.log("[DigitalEmployeeCheck] admin diagnostic repair preview hooks OK");
}
function checkDigitalAdminDailyReportWorkbench() {
console.log("\n[DigitalEmployeeCheck] Verify admin daily report workbench hooks");
const backendRoot = path.resolve(packageRoot, "..", "..", "..", "qiming-backend");
const qimingRoot = path.resolve(packageRoot, "..", "..", "..", "qiming");
const servicePath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-application",
"src",
"main",
"java",
"com",
"xspaceagi",
"agent",
"core",
"application",
"service",
"DigitalEmployeeSyncApplicationServiceImpl.java",
);
const dtoPath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-adapter",
"src",
"main",
"java",
"com",
"xspaceagi",
"agent",
"core",
"adapter",
"dto",
"digital",
"DigitalEmployeeAdminWorkbenchRowDto.java",
);
const testPath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-application",
"src",
"test",
"java",
"com",
"xspaceagi",
"agent",
"core",
"application",
"service",
"DigitalEmployeeSyncApplicationServiceImplTest.java",
);
const qimingOpsPath = path.join(qimingRoot, "src", "pages", "SystemManagement", "Content", "DigitalEmployeeOps", "index.tsx");
const qimingTypesPath = path.join(qimingRoot, "src", "types", "interfaces", "systemManage.ts");
const docsPath = path.resolve(packageRoot, "..", "..", "docs", "digital-employee-frontend-integration-plan.md");
const service = fs.readFileSync(servicePath, "utf8");
const dto = fs.readFileSync(dtoPath, "utf8");
const test = fs.readFileSync(testPath, "utf8");
const qimingOps = fs.readFileSync(qimingOpsPath, "utf8");
const qimingTypes = fs.readFileSync(qimingTypesPath, "utf8");
const docs = fs.readFileSync(docsPath, "utf8");
const requiredSnippets = [
[service, "daily_reports", "backend daily report view"],
[service, "isDailyReportRow", "backend daily report matcher"],
[service, "daily_report", "backend daily report category"],
[service, "daily_report:record_approval_decision", "backend daily report approval decision action"],
[dto, "report_id", "backend report id row field"],
[dto, "daily_report_approval_id", "backend daily report approval id row field"],
[dto, "daily_report_approval_action", "backend daily report approval action row field"],
[dto, "daily_report_approval_recorded_at", "backend daily report approval recorded at row field"],
[test, "queryAdminWorkbenchDailyReportsReturnsDailyReportRowsOnly", "backend daily report workbench test"],
[test, "getDailyReportApprovalId", "backend daily report approval assertion"],
[qimingTypes, "daily_reports", "qiming daily report view type"],
[qimingTypes, "daily_report_approval_id", "qiming daily report approval id type field"],
[qimingOps, "日报运营", "qiming daily report tab"],
[qimingOps, "记录已送达", "qiming daily report delivered action"],
[qimingOps, "确认回执", "qiming daily report confirmed action"],
[qimingOps, "请求审批", "qiming daily report approval action"],
[qimingOps, "记录审批裁决", "qiming daily report approval decision action"],
[qimingOps, "记录发送结果", "qiming daily report send record action"],
[docs, "正式管理端日报运营预览", "docs daily report absorption"],
[docs, "管理端日报审批裁决回写预览", "docs daily report approval decision absorption"],
];
const missing = requiredSnippets
.filter(([content, snippet]) => !content.includes(snippet))
.map(([, , label]) => label);
if (missing.length > 0) {
throw new Error(`Missing admin daily report workbench hooks: ${missing.join(", ")}`);
}
console.log("[DigitalEmployeeCheck] admin daily report workbench hooks OK");
}
function checkDigitalAdminGovernanceCommandWorkbench() {
console.log("\n[DigitalEmployeeCheck] Verify admin governance command workbench hooks");
const backendRoot = path.resolve(packageRoot, "..", "..", "..", "qiming-backend");
const qimingRoot = path.resolve(packageRoot, "..", "..", "..", "qiming");
const servicePath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-application",
"src",
"main",
"java",
"com",
"xspaceagi",
"agent",
"core",
"application",
"service",
"DigitalEmployeeSyncApplicationServiceImpl.java",
);
const testPath = path.join(
backendRoot,
"app-platform-modules",
"app-platform-agent",
"app-platform-agent-core-application",
"src",
"test",
"java",
"com",
"xspaceagi",
"agent",
"core",
"application",
"service",
"DigitalEmployeeSyncApplicationServiceImplTest.java",
);
const qimingOpsPath = path.join(qimingRoot, "src", "pages", "SystemManagement", "Content", "DigitalEmployeeOps", "index.tsx");
const qimingTypesPath = path.join(qimingRoot, "src", "types", "interfaces", "systemManage.ts");
const docsPath = path.resolve(packageRoot, "..", "..", "docs", "digital-employee-frontend-integration-plan.md");
const service = fs.readFileSync(servicePath, "utf8");
const test = fs.readFileSync(testPath, "utf8");
const qimingOps = fs.readFileSync(qimingOpsPath, "utf8");
const qimingTypes = fs.readFileSync(qimingTypesPath, "utf8");
const docs = fs.readFileSync(docsPath, "utf8");
const requiredSnippets = [
[service, "governance_commands", "backend governance command view"],
[service, "isGovernanceCommandRow", "backend governance command matcher"],
[service, "governance_", "backend governance command kind classifier"],
[service, "command_id", "backend governance command id classifier"],
[test, "queryAdminWorkbenchGovernanceCommandsReturnsGovernanceRowsOnly", "backend governance command test"],
[qimingTypes, "governance_commands", "qiming governance command view type"],
[qimingOps, "治理命令", "qiming governance command tab"],
[qimingOps, "标记已看", "qiming governance reviewed action"],
[qimingOps, "忽略", "qiming governance dismiss action"],
[qimingOps, "ACP 硬中断", "qiming high-risk governance copy"],
[docs, "正式管理端治理命令预览", "docs governance command absorption"],
];
const missing = requiredSnippets
.filter(([content, snippet]) => !content.includes(snippet))
.map(([, , label]) => label);
if (missing.length > 0) {
throw new Error(`Missing admin governance command workbench hooks: ${missing.join(", ")}`);
}
console.log("[DigitalEmployeeCheck] admin governance command workbench hooks OK");
}
function checkLocalDatabaseSchema() {
console.log("\n[DigitalEmployeeCheck] Verify local SQLite digital schema");
const dbPath = path.join(os.homedir(), ".qimingclaw", "qimingclaw.db");
if (!fs.existsSync(dbPath)) {
console.log(`[DigitalEmployeeCheck] Skip DB schema check; database not found: ${dbPath}`);
return;
}
const healResult = spawnSync("sqlite3", [dbPath, selfHealingDigitalSchemaSql], {
encoding: "utf8",
shell: process.platform === "win32",
});
if (healResult.error) {
console.log(`[DigitalEmployeeCheck] Skip DB schema check; sqlite3 unavailable: ${healResult.error.message}`);
return;
}
if (healResult.status !== 0) {
throw new Error(healResult.stderr || "sqlite3 digital schema self-heal failed");
}
const result = spawnSync("sqlite3", [dbPath, ".tables"], {
encoding: "utf8",
shell: process.platform === "win32",
});
if (result.status !== 0) {
throw new Error(result.stderr || "sqlite3 .tables failed");
}
const tables = new Set(result.stdout.split(/\s+/).filter(Boolean));
const missing = requiredDigitalTables.filter((table) => !tables.has(table));
if (missing.length > 0) {
throw new Error(`Missing digital employee tables: ${missing.join(", ")}`);
}
console.log(`[DigitalEmployeeCheck] ${requiredDigitalTables.length} digital tables OK`);
}
function main() {
try {
runStep("Run digital employee unit tests", "npm", ["run", "test:run", "--", ...testFiles]);
runStep("Build Electron main process", "npm", ["run", "build:main:dev"]);
runStep("Build embedded digital employee", "npm", ["run", "build:sgrobot-digital"]);
checkSgrobotDigitalAssets();
checkDigitalDiagnosticAuditProjection();
checkDigitalNotificationPreferences();
checkDigitalApprovalHistorySubscriptions();
checkDigitalApprovalWorkflowSync();
checkDigitalPlanStepGraphDispatchPolicy();
checkDigitalSkillRemotePolicy();
checkDigitalRiskApprovalPolicy();
checkDigitalRouteDecisionPolicy();
checkDigitalDailyReportDelivery();
checkDigitalArtifactCleanup();
checkDigitalArtifactBatchGovernance();
checkDigitalBusinessViewUi();
checkDigitalInterventionWorkbench();
checkDigitalMemoryGovernanceAudit();
checkDigitalOpsNotificationReportWorkbenches();
checkDigitalPolicySchedulerWorkbenches();
checkDigitalAdminActionLoop();
checkDigitalAdminPolicySnapshots();
checkDigitalBackendMaterializedFacts();
checkDigitalAdminRecordBillingArchiveShell();
checkDigitalAdminApprovalGovernanceWorkbench();
checkDigitalAdminApprovalPoliciesWorkbench();
checkDigitalAdminMemorySkillWorkbench();
checkDigitalAdminPolicySchedulerWorkbenches();
checkDigitalAdminPlanGovernanceWorkbenches();
checkDigitalAdminGovernancePolicyRevisionMaterialization();
checkDigitalAdminPolicyGovernanceClosure();
checkDigitalAdminPlanStepDispatchWorkbench();
checkDigitalAdminCollaborationOperationsClosure();
checkDigitalAdminServiceOperationsWorkbench();
checkDigitalAdminRouteGovernanceWorkbench();
checkDigitalAdminNotificationWorkbench();
checkDigitalAdminNotificationPoliciesWorkbench();
checkDigitalAdminArtifactLifecycleWorkbench();
checkDigitalAdminAuditDiagnosticsWorkbench();
checkDigitalAdminDiagnosticRepairPreview();
checkDigitalAdminDailyReportWorkbench();
checkDigitalAdminGovernanceCommandWorkbench();
checkLocalDatabaseSchema();
console.log("\n[DigitalEmployeeCheck] All checks passed");
} catch (error) {
console.error(`\n[DigitalEmployeeCheck] ${error instanceof Error ? error.message : String(error)}`);
process.exit(process.exitCode || 1);
}
}
main();