/ next.config.ts
next.config.ts
1 import type { NextConfig } from "next"; 2 import { execSync } from "child_process"; 3 import { existsSync } from "fs"; 4 import { networkInterfaces } from "os"; 5 import path from "path"; 6 import { fileURLToPath } from "url"; 7 8 const PROJECT_ROOT = path.dirname(fileURLToPath(import.meta.url)) 9 const RUNTIME_STATE_GLOBS = [ 10 'data/**/*', 11 '.tmp-swarmclaw-build/**/*', 12 ] 13 14 function getGitSha(): string { 15 try { 16 if (!existsSync(path.join(PROJECT_ROOT, '.git'))) return 'unknown' 17 return execSync('git rev-parse --short HEAD', { encoding: 'utf-8' }).trim() 18 } catch { 19 return 'unknown' 20 } 21 } 22 23 function getAllowedDevOrigins(): string[] { 24 const allowed = new Set<string>([ 25 'localhost', 26 '127.0.0.1', 27 '0.0.0.0', 28 ]) 29 30 // Include all active local IPv4 interfaces so LAN devices can load /_next assets in dev. 31 for (const interfaces of Object.values(networkInterfaces())) { 32 for (const iface of interfaces ?? []) { 33 if ((iface.family === 'IPv4' || (iface.family as string | number) === 4) && !iface.internal) { 34 allowed.add(iface.address) 35 } 36 } 37 } 38 39 // Optional override for custom origins/hosts, e.g. `NEXT_ALLOWED_DEV_ORIGINS=host1,host2`. 40 const extra = (process.env.NEXT_ALLOWED_DEV_ORIGINS ?? '') 41 .split(',') 42 .map((v) => v.trim()) 43 .filter(Boolean) 44 .map((v) => v.replace(/^https?:\/\//, '').replace(/\/$/, '')) 45 for (const host of extra) allowed.add(host) 46 47 return [...allowed] 48 } 49 50 const nextConfig: NextConfig = { 51 output: 'standalone', 52 outputFileTracingExcludes: { 53 '/*': RUNTIME_STATE_GLOBS, 54 '/api/**': RUNTIME_STATE_GLOBS, 55 instrumentation: RUNTIME_STATE_GLOBS, 56 '/instrumentation': RUNTIME_STATE_GLOBS, 57 'next-server': RUNTIME_STATE_GLOBS, 58 }, 59 turbopack: { 60 // Pin workspace root to the project directory so a stale lockfile 61 // in a parent folder (e.g. ~/) or a nested launch cwd doesn't confuse 62 // native module resolution. 63 root: PROJECT_ROOT, 64 }, 65 experimental: { 66 // Limit build workers to 1 inside Docker to avoid SQLITE_BUSY contention 67 // when multiple workers collect page data concurrently. 68 ...(process.env.SWARMCLAW_BUILD_MODE ? { cpus: 1 } : {}), 69 }, 70 env: { 71 NEXT_PUBLIC_GIT_SHA: getGitSha(), 72 NEXT_PUBLIC_WS_PORT: String((Number(process.env.PORT) || 3456) + 1), 73 }, 74 // Allow external network access 75 serverExternalPackages: [ 76 'ws', 77 'highlight.js', 'better-sqlite3', 78 'discord.js', '@discordjs/ws', '@discordjs/rest', 79 'grammy', 80 '@slack/bolt', '@slack/web-api', '@slack/socket-mode', 81 '@whiskeysockets/baileys', 82 'qrcode', 83 'just-bash', 84 ], 85 allowedDevOrigins: getAllowedDevOrigins(), 86 }; 87 88 export default nextConfig;