import { $ } from "bun" import { writeFileSync } from "fs" const AP_SERVICE_FILE = "/etc/systemd/system/phone-ap.service" const WEB_SERVICE_FILE = "/etc/systemd/system/phone-web.service" const PHONE_SERVICE_FILE = "/etc/systemd/system/phone.service" export const setupServices = async (installDir: string) => { console.log("\nInstalling systemd services...") // Detect user from environment or use default // SUDO_USER is set when running with sudo, which is what we want const serviceUser = process.env.SERVICE_USER || process.env.SUDO_USER || process.env.USER || "corey" const userUid = await $`id -u ${serviceUser}`.text().then((s) => s.trim()) console.log(`Setting up services for user: ${serviceUser} (UID: ${userUid})`) // Find where bun is installed const bunPath = await $`which bun` .quiet() .nothrow() .text() .then((p) => p.trim()) if (!bunPath) { console.error("Error: bun not found in PATH. Please ensure bun is available system-wide.") process.exit(1) } console.log(`Using bun at: ${bunPath}`) // Create AP monitor service const apServiceContent = `[Unit] Description=Phone WiFi AP Monitor After=network.target Before=phone-web.service [Service] Type=simple ExecStart=${bunPath} ${installDir}/src/services/ap-monitor.ts Restart=on-failure RestartSec=5 StandardOutput=journal StandardError=journal [Install] WantedBy=multi-user.target ` writeFileSync(AP_SERVICE_FILE, apServiceContent, "utf8") console.log("✓ Created phone-ap.service") // Create web server service const webServiceContent = `[Unit] Description=Phone Web Server After=network.target phone-ap.service [Service] Type=simple ExecStart=${bunPath} ${installDir}/src/services/server/server.tsx WorkingDirectory=${installDir} Restart=on-failure RestartSec=5 StandardOutput=journal StandardError=journal [Install] WantedBy=multi-user.target ` writeFileSync(WEB_SERVICE_FILE, webServiceContent, "utf8") console.log("✓ Created phone-web.service") // Create phone service (system service with environment variables for audio access) const phoneServiceContent = `[Unit] Description=Phone Application After=network.target sound.target Requires=sound.target [Service] Type=simple User=${serviceUser} Group=audio Environment=XDG_RUNTIME_DIR=/run/user/${userUid} Environment=DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/${userUid}/bus ExecStart=${bunPath} ${installDir}/src/main.ts WorkingDirectory=${installDir} EnvironmentFile=${installDir}/.env Restart=on-failure RestartSec=5 StandardOutput=journal StandardError=journal [Install] WantedBy=multi-user.target ` writeFileSync(PHONE_SERVICE_FILE, phoneServiceContent, "utf8") console.log("✓ Created phone.service") await $`systemctl daemon-reload` await $`systemctl enable phone-ap.service` await $`systemctl enable phone-web.service` await $`systemctl enable phone.service` console.log("✓ Services enabled") console.log("\nRestarting the services...") await $`systemctl restart phone-ap.service` await $`systemctl restart phone-web.service` await $`systemctl restart phone.service` console.log("✓ Services restarted") }