fix: address 3 new Bugbot review comments

1. Medium Severity: Wrap getOrSpawnTtyd in try/catch
   - Port exhaustion exception now caught and returns 503 error
   - Prevents unhandled promise rejection that hangs client

2. Low Severity: Change "review" level label to "Needs Investigation"
   - "Pending Review" was misleading - this level means CI failed,
     changes requested, or merge conflicts (active problems)
   - "Needs Investigation" better conveys urgency and action needed

3. Low Severity: Add issue label enrichment to API sessions route
   - Import getTracker and enrichSessionIssue
   - Enrich issue labels synchronously before PR enrichment
   - Matches behavior of SSR page and individual session route

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Prateek 2026-02-15 02:18:50 +05:30
parent be7413338a
commit e04030184a
3 changed files with 27 additions and 8 deletions

View File

@ -1,6 +1,6 @@
import { NextResponse } from "next/server";
import { getServices, getSCM } from "@/lib/services";
import { sessionToDashboard, enrichSessionPR, computeStats } from "@/lib/serialize";
import { getServices, getSCM, getTracker } from "@/lib/services";
import { sessionToDashboard, enrichSessionPR, enrichSessionIssue, computeStats } from "@/lib/serialize";
/** GET /api/sessions — List all sessions with full state */
export async function GET() {
@ -12,6 +12,25 @@ export async function GET() {
const workerSessions = coreSessions.filter((s) => s.id !== "orchestrator");
const dashboardSessions = workerSessions.map(sessionToDashboard);
// Enrich issue labels using tracker plugin (synchronous)
workerSessions.forEach((core, i) => {
if (!dashboardSessions[i].issueUrl) return;
let project = config.projects[core.projectId];
if (!project) {
const entry = Object.entries(config.projects).find(([, p]) =>
core.id.startsWith(p.sessionPrefix),
);
if (entry) project = entry[1];
}
if (!project) {
const firstKey = Object.keys(config.projects)[0];
if (firstKey) project = config.projects[firstKey];
}
const tracker = getTracker(registry, project);
if (!tracker || !project) return;
enrichSessionIssue(dashboardSessions[i], tracker, project);
});
// Enrich sessions that have PRs with live SCM data (CI, reviews, mergeability)
const enrichPromises = workerSessions.map((core, i) => {
if (!core.pr) return Promise.resolve();

View File

@ -57,7 +57,7 @@ function humanizeLevel(level: string): string {
case "respond":
return "Needs Response";
case "review":
return "Pending Review";
return "Needs Investigation";
case "pending":
return "Pending";
case "working":

View File

@ -240,10 +240,9 @@ const server = createServer(async (req, res) => {
return;
}
const instance = getOrSpawnTtyd(sessionId);
// Wait for ttyd to be ready before returning the URL
// Spawn ttyd and wait for it to be ready (catch port exhaustion and startup failures)
try {
const instance = getOrSpawnTtyd(sessionId);
await waitForTtyd(instance.port, sessionId);
// Use the request host to construct the terminal URL (supports remote access)
@ -257,9 +256,10 @@ const server = createServer(async (req, res) => {
sessionId,
}));
} catch (err) {
console.error(`[Terminal] ttyd ${sessionId} failed to become ready:`, err);
const errorMsg = err instanceof Error ? err.message : String(err);
console.error(`[Terminal] Failed to start terminal for ${sessionId}:`, errorMsg);
res.writeHead(503, { "Content-Type": "application/json" });
res.end(JSON.stringify({ error: "Terminal server not ready" }));
res.end(JSON.stringify({ error: errorMsg }));
}
return;
}