#!/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_tasks", "digital_runs", "digital_events", "digital_artifacts", "digital_approvals", "digital_sync_outbox", "digital_sync_attempts", ]; const testFiles = [ "src/main/services/digitalEmployee/stateService.test.ts", "src/main/ipc/eventForwarders.test.ts", "src/main/services/digitalEmployee/syncService.test.ts", "src/main/services/digitalEmployee/skillCatalogService.test.ts", "src/main/services/digitalEmployee/planTemplateService.test.ts", ]; 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 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 result = spawnSync("sqlite3", [dbPath, ".tables"], { encoding: "utf8", shell: process.platform === "win32", }); if (result.error) { console.log(`[DigitalEmployeeCheck] Skip DB schema check; sqlite3 unavailable: ${result.error.message}`); return; } 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(); 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();