import { readFileSync } from "node:fs"; import { fileURLToPath } from "node:url"; import { defineConfig, loadEnv, type Plugin } from "vite"; import react from "@vitejs/plugin-react"; import { VitePWA } from "vite-plugin-pwa"; /** Emits the repo's LICENSE file into the build output. */ function licensePlugin(): Plugin { return { name: "hermes-pwa-license", apply: "build", generateBundle() { this.emitFile({ type: "asset", fileName: "LICENSE", source: readFileSync(fileURLToPath(new URL("../LICENSE", import.meta.url)), "utf-8"), }); }, }; } // During `npm run dev` you can point the dev server at a Hermes host to avoid // CORS entirely: set HERMES_DEV_TARGET=https://hermes.example.com in a .env file // (or the environment) and the dev server proxies /api/* to it. export default defineConfig(({ mode }) => { const env = loadEnv(mode, ".", ""); const target = env.HERMES_DEV_TARGET; return { server: { port: 5173, proxy: target ? Object.fromEntries( ["/api", "/v1", "/health"].map((prefix) => [ prefix, { target, changeOrigin: true, secure: false, // SSE needs buffering disabled. configure: (proxy) => { proxy.on("proxyRes", (proxyRes) => { proxyRes.headers["x-accel-buffering"] = "no"; }); }, }, ]), ) : undefined, }, plugins: [ react(), licensePlugin(), VitePWA({ registerType: "autoUpdate", includeAssets: ["icons/apple-touch-icon.png", "favicon.svg"], manifest: { name: "Hermes", short_name: "Hermes", description: "Chat with your Hermes Agent profiles, sessions, voice and images.", theme_color: "#000810", background_color: "#000810", display: "standalone", orientation: "portrait-primary", start_url: "/", scope: "/", icons: [ { src: "icons/icon-192.png", sizes: "192x192", type: "image/png" }, { src: "icons/icon-512.png", sizes: "512x512", type: "image/png" }, { src: "icons/maskable-512.png", sizes: "512x512", type: "image/png", purpose: "maskable", }, ], }, workbox: { // Never cache API traffic — it is authenticated and dynamic. navigateFallbackDenylist: [/^\/api\//], runtimeCaching: [ { urlPattern: ({ url }) => url.pathname.startsWith("/api/"), handler: "NetworkOnly", }, ], }, devOptions: { enabled: false, }, }), ], }; });