| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188 |
- import { jsxLocPlugin } from "@builder.io/vite-plugin-jsx-loc";
- import tailwindcss from "@tailwindcss/vite";
- import react from "@vitejs/plugin-react";
- import fs from "node:fs";
- import path from "node:path";
- import { defineConfig, type Plugin, type ViteDevServer } from "vite";
- import { vitePluginManusRuntime } from "vite-plugin-manus-runtime";
- // =============================================================================
- // Manus Debug Collector - Vite Plugin
- // Writes browser logs directly to files, trimmed when exceeding size limit
- // =============================================================================
- const PROJECT_ROOT = import.meta.dirname;
- const LOG_DIR = path.join(PROJECT_ROOT, ".manus-logs");
- const MAX_LOG_SIZE_BYTES = 1 * 1024 * 1024; // 1MB per log file
- const TRIM_TARGET_BYTES = Math.floor(MAX_LOG_SIZE_BYTES * 0.6); // Trim to 60% to avoid constant re-trimming
- type LogSource = "browserConsole" | "networkRequests" | "sessionReplay";
- function ensureLogDir() {
- if (!fs.existsSync(LOG_DIR)) {
- fs.mkdirSync(LOG_DIR, { recursive: true });
- }
- }
- function trimLogFile(logPath: string, maxSize: number) {
- try {
- if (!fs.existsSync(logPath) || fs.statSync(logPath).size <= maxSize) {
- return;
- }
- const lines = fs.readFileSync(logPath, "utf-8").split("\n");
- const keptLines: string[] = [];
- let keptBytes = 0;
- // Keep newest lines (from end) that fit within 60% of maxSize
- const targetSize = TRIM_TARGET_BYTES;
- for (let i = lines.length - 1; i >= 0; i--) {
- const lineBytes = Buffer.byteLength(`${lines[i]}\n`, "utf-8");
- if (keptBytes + lineBytes > targetSize) break;
- keptLines.unshift(lines[i]);
- keptBytes += lineBytes;
- }
- fs.writeFileSync(logPath, keptLines.join("\n"), "utf-8");
- } catch {
- /* ignore trim errors */
- }
- }
- function writeToLogFile(source: LogSource, entries: unknown[]) {
- if (entries.length === 0) return;
- ensureLogDir();
- const logPath = path.join(LOG_DIR, `${source}.log`);
- // Format entries with timestamps
- const lines = entries.map((entry) => {
- const ts = new Date().toISOString();
- return `[${ts}] ${JSON.stringify(entry)}`;
- });
- // Append to log file
- fs.appendFileSync(logPath, `${lines.join("\n")}\n`, "utf-8");
- // Trim if exceeds max size
- trimLogFile(logPath, MAX_LOG_SIZE_BYTES);
- }
- /**
- * Vite plugin to collect browser debug logs
- * - POST /__manus__/logs: Browser sends logs, written directly to files
- * - Files: browserConsole.log, networkRequests.log, sessionReplay.log
- * - Auto-trimmed when exceeding 1MB (keeps newest entries)
- */
- function vitePluginManusDebugCollector(): Plugin {
- return {
- name: "manus-debug-collector",
- transformIndexHtml(html) {
- if (process.env.NODE_ENV === "production") {
- return html;
- }
- return {
- html,
- tags: [
- {
- tag: "script",
- attrs: {
- src: "/__manus__/debug-collector.js",
- defer: true,
- },
- injectTo: "head",
- },
- ],
- };
- },
- configureServer(server: ViteDevServer) {
- // POST /__manus__/logs: Browser sends logs (written directly to files)
- server.middlewares.use("/__manus__/logs", (req, res, next) => {
- if (req.method !== "POST") {
- return next();
- }
- const handlePayload = (payload: any) => {
- // Write logs directly to files
- if (payload.consoleLogs?.length > 0) {
- writeToLogFile("browserConsole", payload.consoleLogs);
- }
- if (payload.networkRequests?.length > 0) {
- writeToLogFile("networkRequests", payload.networkRequests);
- }
- if (payload.sessionEvents?.length > 0) {
- writeToLogFile("sessionReplay", payload.sessionEvents);
- }
- res.writeHead(200, { "Content-Type": "application/json" });
- res.end(JSON.stringify({ success: true }));
- };
- const reqBody = (req as { body?: unknown }).body;
- if (reqBody && typeof reqBody === "object") {
- try {
- handlePayload(reqBody);
- } catch (e) {
- res.writeHead(400, { "Content-Type": "application/json" });
- res.end(JSON.stringify({ success: false, error: String(e) }));
- }
- return;
- }
- let body = "";
- req.on("data", (chunk) => {
- body += chunk.toString();
- });
- req.on("end", () => {
- try {
- const payload = JSON.parse(body);
- handlePayload(payload);
- } catch (e) {
- res.writeHead(400, { "Content-Type": "application/json" });
- res.end(JSON.stringify({ success: false, error: String(e) }));
- }
- });
- });
- },
- };
- }
- const plugins = [react(), tailwindcss(), jsxLocPlugin(), vitePluginManusRuntime(), vitePluginManusDebugCollector()];
- export default defineConfig({
- plugins,
- resolve: {
- alias: {
- "@": path.resolve(import.meta.dirname, "client", "src"),
- "@shared": path.resolve(import.meta.dirname, "shared"),
- "@assets": path.resolve(import.meta.dirname, "attached_assets"),
- },
- },
- envDir: path.resolve(import.meta.dirname),
- root: path.resolve(import.meta.dirname, "client"),
- publicDir: path.resolve(import.meta.dirname, "client", "public"),
- base: "/chat/",
- build: {
- outDir: path.resolve(import.meta.dirname, "dist/public"),
- emptyOutDir: true,
- },
- server: {
- host: true,
- allowedHosts: [
- ".manuspre.computer",
- ".manus.computer",
- ".manus-asia.computer",
- ".manuscomputer.ai",
- ".manusvm.computer",
- "localhost",
- "127.0.0.1",
- ],
- fs: {
- strict: true,
- deny: ["**/.*"],
- },
- },
- });
|