import { existsSync, readFileSync } from "node:fs"; import { runCommand } from "./command"; import { type UniDeskConfig, repoRoot, rootPath } from "./config"; import { composeConfig } from "./docker"; interface CheckItem { name: string; ok: boolean; detail: unknown; } function fileItem(path: string): CheckItem { const absolute = rootPath(path); return { name: `file:${path}`, ok: existsSync(absolute), detail: absolute }; } function commandItem(name: string, command: string[]): CheckItem { const result = runCommand(command, repoRoot); return { name, ok: result.exitCode === 0, detail: { command, exitCode: result.exitCode, stdoutTail: result.stdout.slice(-4000), stderrTail: result.stderr.slice(-4000), }, }; } function unifiedLogRotationItem(): CheckItem { const serviceFiles = [ "src/components/backend-core/src/logger.ts", "src/components/frontend/src/index.ts", "src/components/provider-gateway/src/index.ts", "src/components/microservices/code-queue/src/index.ts", "src/components/microservices/v3sctl-adapter/src/index.ts", "src/components/microservices/mdtodo/src/index.ts", "src/components/microservices/project-manager/src/index.ts", "src/components/microservices/baidu-netdisk/src/index.ts", "src/components/microservices/oa-event-flow/src/index.ts", ]; const offenders = serviceFiles.flatMap((path) => { const text = readFileSync(rootPath(path), "utf8"); const directLogAppend = /appendFileSync\(\s*(?:config\.)?logFile\b/u.test(text) || /appendFileSync\(\s*process\.env\.LOG_FILE\b/u.test(text); const missingWriter = !text.includes("createHourlyJsonlWriter"); return directLogAppend || missingWriter ? [{ path, directLogAppend, missingWriter }] : []; }); return { name: "logs:unified-hourly-rotation", ok: offenders.length === 0, detail: { sharedWriter: "src/components/shared/src/rotating-jsonl.ts", checkedFiles: serviceFiles, offenders, }, }; } export function runChecks(config: UniDeskConfig): { ok: boolean; items: CheckItem[] } { const items: CheckItem[] = [ { name: "config:validated", ok: true, detail: { project: config.project.name, runtime: config.runtime } }, fileItem("scripts/cli.ts"), fileItem("AGENTS.md"), fileItem("TEST.md"), fileItem("docker-compose.yml"), fileItem("src/components/backend-core/src/index.ts"), fileItem("src/components/frontend/src/index.ts"), fileItem("src/components/provider-gateway/src/index.ts"), fileItem("src/components/microservices/oa-event-flow/src/index.ts"), fileItem("src/components/microservices/v3sctl-adapter/src/index.ts"), fileItem("src/components/microservices/mdtodo/src/index.ts"), fileItem("scripts/src/deploy.ts"), fileItem("scripts/src/e2e.ts"), unifiedLogRotationItem(), commandItem("bun:version", ["bun", "--version"]), commandItem("typescript:scripts", ["bunx", "tsc", "-p", "scripts/tsconfig.json", "--noEmit", "--pretty", "false"]), commandItem("typescript:components", ["bunx", "tsc", "-p", "src/tsconfig.check.json", "--pretty", "false"]), ]; const compose = composeConfig(config); items.push({ name: "docker-compose:config", ok: compose.result.exitCode === 0, detail: { command: compose.command, exitCode: compose.result.exitCode, stdoutTail: compose.result.stdout.slice(-4000), stderrTail: compose.result.stderr.slice(-4000), runtimeEnv: compose.runtimeEnv, }, }); return { ok: items.every((item) => item.ok), items }; }