fixing ai's fk up

This commit is contained in:
Lukas Parsons 2026-03-19 01:31:07 -04:00
parent 81a9d3c7b3
commit 49e40872a1
7 changed files with 147 additions and 16 deletions

View file

@ -13,7 +13,25 @@ import { config } from "./config.js";
export function createApp() { export function createApp() {
const app = express(); const app = express();
app.use(cors({ origin: config.webBaseUrl })); app.use(cors({
origin: [
"http://localhost:5173",
"http://localhost:5174",
"http://localhost:5175",
"http://localhost:5176",
"http://localhost:5177",
"http://localhost:5178",
"http://localhost:5179",
"http://127.0.0.1:5173",
"http://127.0.0.1:5174",
"http://127.0.0.1:5175",
"http://127.0.0.1:5176",
"http://127.0.0.1:5177",
"http://127.0.0.1:5178",
"http://127.0.0.1:5179",
],
credentials: true
}));
app.use(express.json()); app.use(express.json());
app.get("/health", (_req, res) => res.json({ ok: true })); app.get("/health", (_req, res) => res.json({ ok: true }));

View file

@ -1,8 +1,13 @@
import dotenv from "dotenv"; import dotenv from "dotenv";
import path from "node:path"; import path from "node:path";
import { fileURLToPath } from "node:url";
import { dirname } from "node:path";
// .env lives at the monorepo root, two levels above apps/server/ const __filename = fileURLToPath(import.meta.url);
dotenv.config({ path: path.resolve(process.cwd(), "../../.env") }); const __dirname = dirname(__filename);
const envPath = path.resolve(__dirname, "../../../.env");
dotenv.config({ path: envPath });
const required = (key: string, fallback?: string): string => { const required = (key: string, fallback?: string): string => {
const value = process.env[key] ?? fallback; const value = process.env[key] ?? fallback;

View file

@ -13,9 +13,12 @@ async function bootstrap() {
console.log(`Server listening on :${config.port}`); console.log(`Server listening on :${config.port}`);
}); });
scheduler.start(); scheduler.start();
await discordService.start().catch((err) => { try {
console.error("[discord] Failed to connect — bot features unavailable:", err.message); await discordService.start();
}); } catch (err) {
console.error("[discord] Failed to connect — bot features unavailable:", err);
console.error("[discord] Server will continue running without Discord features.");
}
} }
bootstrap().catch((err) => { bootstrap().catch((err) => {

View file

@ -49,19 +49,41 @@ class TranscriptionBuffer {
const userBuffers = new Map<string, TranscriptionBuffer>(); const userBuffers = new Map<string, TranscriptionBuffer>();
const activeTranscriptions = new Map<string, NodeJS.Timeout>(); const activeTranscriptions = new Map<string, NodeJS.Timeout>();
async function transcribeWithWhisper(audioBuffer: Buffer): Promise<TranscriptionResult> { function createWavBuffer(pcmData: Buffer, sampleRate: number = 48000): Buffer {
if (!config.openaiApiKey) { const numChannels = 1;
throw new Error("OpenAI API key not configured for transcription"); const bitsPerSample = 16;
const byteRate = (sampleRate * numChannels * bitsPerSample) / 8;
const blockAlign = (numChannels * bitsPerSample) / 8;
const dataSize = pcmData.length * 2;
const header = Buffer.alloc(44);
header.write('RIFF', 0);
header.writeUInt32LE(36 + dataSize, 4);
header.write('WAVE', 8);
header.write('fmt ', 12);
header.writeUInt32LE(16, 16);
header.writeUInt16LE(1, 20);
header.writeUInt16LE(numChannels, 22);
header.writeUInt32LE(sampleRate, 24);
header.writeUInt32LE(byteRate, 28);
header.writeUInt16LE(blockAlign, 32);
header.writeUInt16LE(bitsPerSample, 34);
header.write('data', 36);
header.writeUInt32LE(dataSize, 40);
return Buffer.concat([header, pcmData]);
} }
async function transcribeWithWhisper(audioBuffer: Buffer): Promise<TranscriptionResult> {
const wavBuffer = createWavBuffer(audioBuffer);
const form = new FormData(); const form = new FormData();
form.append("file", audioBuffer, { filename: "audio.wav", contentType: "audio/wav" }); form.append("audio_file", wavBuffer, { filename: "audio.wav", contentType: "audio/wav" });
form.append("model", config.whisperModel);
form.append("language", config.whisperLanguage); form.append("language", config.whisperLanguage);
form.append("task", "transcribe");
form.append("output", "json");
const response = await new Promise<Response>((resolve, reject) => { const response = await new Promise<Response>((resolve, reject) => {
const req = form.submit(`${config.whisperBaseUrl}/audio/transcriptions`); const req = form.submit(`${config.whisperBaseUrl}/asr`);
req.setHeader("Authorization", `Bearer ${config.openaiApiKey}`);
req.on("response", (res) => { req.on("response", (res) => {
const chunks: Buffer[] = []; const chunks: Buffer[] = [];

View file

@ -4,7 +4,8 @@ import react from "@vitejs/plugin-react";
export default defineConfig({ export default defineConfig({
plugins: [react()], plugins: [react()],
server: { server: {
port: 5173 port: 5173,
strictPort: true
} }
}); });

View file

@ -7,7 +7,10 @@
"apps/web" "apps/web"
], ],
"scripts": { "scripts": {
"dev": "concurrently -n server,web -c cyan,magenta \"npm run dev -w @dnd-hub/server\" \"npm run dev -w @dnd-hub/web\"", "dev": "bash scripts/dev.sh",
"dev:manual": "concurrently -n server,web -c cyan,magenta --kill-others \"npm run dev -w @dnd-hub/server\" \"npm run dev -w @dnd-hub/web\"",
"dev:server": "cd apps/server && tsx watch src/main.ts",
"dev:web": "cd apps/web && vite",
"build": "npm run build -w @dnd-hub/server && npm run build -w @dnd-hub/web", "build": "npm run build -w @dnd-hub/server && npm run build -w @dnd-hub/web",
"test": "npm run test -w @dnd-hub/server && npm run test -w @dnd-hub/web" "test": "npm run test -w @dnd-hub/server && npm run test -w @dnd-hub/web"
}, },

79
scripts/dev.sh Normal file
View file

@ -0,0 +1,79 @@
#!/bin/bash
# Kill existing processes on our ports using PowerShell - with retry
kill_port() {
local port=$1
for i in 1 2 3; do
powershell -Command "Get-NetTCPConnection -LocalPort $port -ErrorAction SilentlyContinue | ForEach-Object { Stop-Process -Id \$_.OwningProcess -Force -ErrorAction SilentlyContinue }" 2>/dev/null
sleep 1
# Check if port is free
if ! powershell -Command "Get-NetTCPConnection -LocalPort $port -ErrorAction SilentlyContinue" 2>/dev/null | grep -q LISTENING; then
break
fi
done
}
echo "Cleaning up existing processes..."
kill_port 4000
kill_port 5173
sleep 2
# Get the script directory and resolve to absolute Windows path
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
# Start server in background
cd "$ROOT_DIR/apps/server"
nohup npx tsx watch src/main.ts > "$ROOT_DIR/data/server.log" 2>&1 &
SERVER_PID=$!
echo "Server started (PID: $SERVER_PID)"
# Start web in background
cd "$ROOT_DIR/apps/web"
nohup npm run dev > "$ROOT_DIR/data/web.log" 2>&1 &
WEB_PID=$!
echo "Web started (PID: $WEB_PID)"
# Wait for both to be ready
echo "Waiting for services to start..."
sleep 6
# Check health
sleep 2
if curl -s http://127.0.0.1:4000/health 2>/dev/null | grep -q "ok"; then
echo "✓ Server is running on http://localhost:4000"
else
echo "✗ Server failed to start"
echo "Server log:"
cat "$ROOT_DIR/data/server.log" 2>/dev/null | tail -20
exit 1
fi
if curl -s http://127.0.0.1:5173/ 2>/dev/null | grep -qi "campaign"; then
echo "✓ Web is running on http://localhost:5173"
else
# Fallback: check if port is listening
if netstat -ano 2>/dev/null | grep -q ":5173.*LISTENING"; then
echo "✓ Web is running on http://localhost:5173"
else
echo "✗ Web failed to start"
echo "Web log:"
cat "$ROOT_DIR/data/web.log" 2>/dev/null | tail -10
fi
fi
echo ""
echo "Both services running. Press Ctrl+C to stop."
# Handle Ctrl+C
cleanup() {
echo "Stopping services..."
taskkill //F //PID $SERVER_PID 2>/dev/null
taskkill //F //PID $WEB_PID 2>/dev/null
exit 0
}
trap cleanup INT TERM
# Keep script running
wait