vite.config.ts 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. import { jsxLocPlugin } from "@builder.io/vite-plugin-jsx-loc";
  2. import tailwindcss from "@tailwindcss/vite";
  3. import react from "@vitejs/plugin-react";
  4. import fs from "node:fs";
  5. import path from "node:path";
  6. import { defineConfig, type Plugin, type ViteDevServer } from "vite";
  7. import { vitePluginManusRuntime } from "vite-plugin-manus-runtime";
  8. // =============================================================================
  9. // Manus Debug Collector - Vite Plugin
  10. // Writes browser logs directly to files, trimmed when exceeding size limit
  11. // =============================================================================
  12. const PROJECT_ROOT = import.meta.dirname;
  13. const LOG_DIR = path.join(PROJECT_ROOT, ".manus-logs");
  14. const MAX_LOG_SIZE_BYTES = 1 * 1024 * 1024; // 1MB per log file
  15. const TRIM_TARGET_BYTES = Math.floor(MAX_LOG_SIZE_BYTES * 0.6); // Trim to 60% to avoid constant re-trimming
  16. type LogSource = "browserConsole" | "networkRequests" | "sessionReplay";
  17. function ensureLogDir() {
  18. if (!fs.existsSync(LOG_DIR)) {
  19. fs.mkdirSync(LOG_DIR, { recursive: true });
  20. }
  21. }
  22. function trimLogFile(logPath: string, maxSize: number) {
  23. try {
  24. if (!fs.existsSync(logPath) || fs.statSync(logPath).size <= maxSize) {
  25. return;
  26. }
  27. const lines = fs.readFileSync(logPath, "utf-8").split("\n");
  28. const keptLines: string[] = [];
  29. let keptBytes = 0;
  30. // Keep newest lines (from end) that fit within 60% of maxSize
  31. const targetSize = TRIM_TARGET_BYTES;
  32. for (let i = lines.length - 1; i >= 0; i--) {
  33. const lineBytes = Buffer.byteLength(`${lines[i]}\n`, "utf-8");
  34. if (keptBytes + lineBytes > targetSize) break;
  35. keptLines.unshift(lines[i]);
  36. keptBytes += lineBytes;
  37. }
  38. fs.writeFileSync(logPath, keptLines.join("\n"), "utf-8");
  39. } catch {
  40. /* ignore trim errors */
  41. }
  42. }
  43. function writeToLogFile(source: LogSource, entries: unknown[]) {
  44. if (entries.length === 0) return;
  45. ensureLogDir();
  46. const logPath = path.join(LOG_DIR, `${source}.log`);
  47. // Format entries with timestamps
  48. const lines = entries.map((entry) => {
  49. const ts = new Date().toISOString();
  50. return `[${ts}] ${JSON.stringify(entry)}`;
  51. });
  52. // Append to log file
  53. fs.appendFileSync(logPath, `${lines.join("\n")}\n`, "utf-8");
  54. // Trim if exceeds max size
  55. trimLogFile(logPath, MAX_LOG_SIZE_BYTES);
  56. }
  57. /**
  58. * Vite plugin to collect browser debug logs
  59. * - POST /__manus__/logs: Browser sends logs, written directly to files
  60. * - Files: browserConsole.log, networkRequests.log, sessionReplay.log
  61. * - Auto-trimmed when exceeding 1MB (keeps newest entries)
  62. */
  63. function vitePluginManusDebugCollector(): Plugin {
  64. return {
  65. name: "manus-debug-collector",
  66. transformIndexHtml(html) {
  67. if (process.env.NODE_ENV === "production") {
  68. return html;
  69. }
  70. return {
  71. html,
  72. tags: [
  73. {
  74. tag: "script",
  75. attrs: {
  76. src: "/__manus__/debug-collector.js",
  77. defer: true,
  78. },
  79. injectTo: "head",
  80. },
  81. ],
  82. };
  83. },
  84. configureServer(server: ViteDevServer) {
  85. // POST /__manus__/logs: Browser sends logs (written directly to files)
  86. server.middlewares.use("/__manus__/logs", (req, res, next) => {
  87. if (req.method !== "POST") {
  88. return next();
  89. }
  90. const handlePayload = (payload: any) => {
  91. // Write logs directly to files
  92. if (payload.consoleLogs?.length > 0) {
  93. writeToLogFile("browserConsole", payload.consoleLogs);
  94. }
  95. if (payload.networkRequests?.length > 0) {
  96. writeToLogFile("networkRequests", payload.networkRequests);
  97. }
  98. if (payload.sessionEvents?.length > 0) {
  99. writeToLogFile("sessionReplay", payload.sessionEvents);
  100. }
  101. res.writeHead(200, { "Content-Type": "application/json" });
  102. res.end(JSON.stringify({ success: true }));
  103. };
  104. const reqBody = (req as { body?: unknown }).body;
  105. if (reqBody && typeof reqBody === "object") {
  106. try {
  107. handlePayload(reqBody);
  108. } catch (e) {
  109. res.writeHead(400, { "Content-Type": "application/json" });
  110. res.end(JSON.stringify({ success: false, error: String(e) }));
  111. }
  112. return;
  113. }
  114. let body = "";
  115. req.on("data", (chunk) => {
  116. body += chunk.toString();
  117. });
  118. req.on("end", () => {
  119. try {
  120. const payload = JSON.parse(body);
  121. handlePayload(payload);
  122. } catch (e) {
  123. res.writeHead(400, { "Content-Type": "application/json" });
  124. res.end(JSON.stringify({ success: false, error: String(e) }));
  125. }
  126. });
  127. });
  128. },
  129. };
  130. }
  131. const plugins = [react(), tailwindcss(), jsxLocPlugin(), vitePluginManusRuntime(), vitePluginManusDebugCollector()];
  132. export default defineConfig({
  133. plugins,
  134. resolve: {
  135. alias: {
  136. "@": path.resolve(import.meta.dirname, "client", "src"),
  137. "@shared": path.resolve(import.meta.dirname, "shared"),
  138. "@assets": path.resolve(import.meta.dirname, "attached_assets"),
  139. },
  140. },
  141. envDir: path.resolve(import.meta.dirname),
  142. root: path.resolve(import.meta.dirname, "client"),
  143. publicDir: path.resolve(import.meta.dirname, "client", "public"),
  144. base: "/chat/",
  145. build: {
  146. outDir: path.resolve(import.meta.dirname, "dist/public"),
  147. emptyOutDir: true,
  148. },
  149. server: {
  150. host: true,
  151. allowedHosts: [
  152. ".manuspre.computer",
  153. ".manus.computer",
  154. ".manus-asia.computer",
  155. ".manuscomputer.ai",
  156. ".manusvm.computer",
  157. "localhost",
  158. "127.0.0.1",
  159. ],
  160. fs: {
  161. strict: true,
  162. deny: ["**/.*"],
  163. },
  164. },
  165. });