#!/usr/bin/env node "use strict"; /** * dev:hot 启动入口。 * * - 使用 cargo-watch 自动重编译并重启 mnote-web。 * - 通过 MNOTE_WEB_DEV_HOT_RELOAD 启用页面端轻量 reload 轮询。 * - 默认启用 MNOTE_WEB_ALLOW_DEV_FIXTURES,保证依赖 /api/dev/seed 的 smoke 可直接运行; * 可显式设为 0 关闭;prod:start 保持关闭。 * - 实际进程编排在 scripts/mnote-web-hot.js。 */ const { spawn } = require("node:child_process"); const fs = require("node:fs"); const path = require("node:path"); const rootDir = path.resolve(__dirname, ".."); const hotPath = path.join(rootDir, "scripts", "mnote-web-hot.js"); function cargoWatchCommand(env = process.env) { const extraArgs = String(env.MNOTE_WEB_DEV_HOT_CARGO_WATCH_ARGS || "").trim(); const watchArgs = [ "-w crates/mnote-web/src", "-w crates/mnote-web/browser", "-w crates/mnote-web/Cargo.toml", "-w Cargo.toml", "-x \"run -p mnote-web --bin mnote-web\"", ]; if (extraArgs) watchArgs.unshift(extraArgs); return `cargo watch ${watchArgs.join(" ")}`; } function bindHost(bindAddr) { const value = String(bindAddr || "").trim(); if (!value) return ""; const ipv6Match = value.match(/^\[([^\]]+)\]:(\d+)$/); if (ipv6Match) return ipv6Match[1]; const lastColon = value.lastIndexOf(":"); if (lastColon <= 0) return ""; return value.slice(0, lastColon); } function bindPort(bindAddr, fallbackPort) { const value = String(bindAddr || "").trim(); const match = value.match(/:(\d+)$/); if (!match) return fallbackPort; const port = Number(match[1]); return Number.isFinite(port) && port > 0 ? Math.floor(port) : fallbackPort; } function devHotBindAddr(env = process.env) { const frontendPort = bindPort(env.MNOTE_WEB_BIND, Number(env.FRONTEND_PORT || 3000)); const host = bindHost(env.MNOTE_WEB_BIND); if (!host || host === "127.0.0.1" || host === "localhost" || host === "::1") { return `0.0.0.0:${frontendPort}`; } return String(env.MNOTE_WEB_BIND).trim(); } function buildDevHotEnv(baseEnv = process.env) { const controlPlaneBackend = String(baseEnv.MNOTE_CONTROL_PLANE_BACKEND || "libsql-local").trim() || "libsql-local"; if (controlPlaneBackend === "sqlite") { throw new Error("dev:hot 不再支持 SQLite control-plane fallback;请使用 libsql-local/turso-local-replica/turso-remote/turso-synced"); } const defaultControlPlaneDir = "/mnt/Data1T/Mnote_data/control-plane"; const env = { ...baseEnv, MNOTE_WEB_DEV_HOT_RELOAD: "1", MNOTE_WEB_ALLOW_DEV_FIXTURES: String(baseEnv.MNOTE_WEB_ALLOW_DEV_FIXTURES ?? "1").trim() || "1", MNOTE_WEB_BIND: devHotBindAddr(baseEnv), MNOTE_WEB_CMD: String(baseEnv.MNOTE_WEB_CMD || "").trim() || cargoWatchCommand(baseEnv), MNOTE_KNOWLEDGE_PROVIDER: String(baseEnv.MNOTE_KNOWLEDGE_PROVIDER || "lightrag_legacy").trim(), MNOTE_CONTROL_PLANE_BACKEND: controlPlaneBackend, MNOTE_PAGE_AI_PI_WARMUP: String(baseEnv.MNOTE_PAGE_AI_PI_WARMUP ?? "1").trim() || "1", MNOTE_PAGE_AI_PI_WARMUP_SEND: String(baseEnv.MNOTE_PAGE_AI_PI_WARMUP_SEND ?? "0").trim() || "0", }; if (controlPlaneBackend === "libsql-local" || controlPlaneBackend === "turso-local" || controlPlaneBackend === "turso") { env.MNOTE_TURSO_LOCAL_PATH = baseEnv.MNOTE_TURSO_LOCAL_PATH || path.join(defaultControlPlaneDir, "control-plane-dev-hot-libsql.db"); } else if (controlPlaneBackend === "turso-local-replica" || controlPlaneBackend === "turso-remote-replica") { env.MNOTE_TURSO_LOCAL_REPLICA_PATH = baseEnv.MNOTE_TURSO_LOCAL_REPLICA_PATH || path.join(defaultControlPlaneDir, "control-plane-dev-hot-replica.db"); } else if (controlPlaneBackend === "turso-synced") { env.MNOTE_TURSO_SYNCED_PATH = baseEnv.MNOTE_TURSO_SYNCED_PATH || baseEnv.MNOTE_TURSO_LOCAL_REPLICA_PATH || path.join(defaultControlPlaneDir, "control-plane-dev-hot-synced.db"); } return env; } function main() { if (!fs.existsSync(hotPath)) { throw new Error(`缺少 mnote-web-hot 入口:${hotPath}`); } const child = spawn(process.execPath, [hotPath], { cwd: rootDir, env: buildDevHotEnv(process.env), stdio: "inherit", }); child.on("exit", (code, signal) => { if (signal) { process.kill(process.pid, signal); return; } process.exit(code ?? 0); }); child.on("error", (error) => { console.error(`[dev-hot] 启动失败:${error.message}`); process.exit(1); }); } if (require.main === module) { main(); } module.exports = { buildDevHotEnv, cargoWatchCommand, devHotBindAddr, };