diff --git a/multi-review/src/index.ts b/multi-review/src/index.ts index 3405054..3a5cdfb 100644 --- a/multi-review/src/index.ts +++ b/multi-review/src/index.ts @@ -2,7 +2,7 @@ import { createOpencode } from "@opencode-ai/sdk"; import { readFileSync } from "node:fs"; import { join } from "node:path"; import { loadReviewers, resolveModel, env, intEnv } from "./reviewers.js"; -import { runParallelReviewers, runCoordinator, buildFallbackComment, buildReviewerDetails } from "./orchestrator.js"; +import { runParallelReviewers, runCoordinator, buildFallbackComment, buildReviewerDetails, cleanupAllSessions } from "./orchestrator.js"; import { postPRComment, cleanupErrorComments, parseExtraEnv } from "./comment.js"; async function main(): Promise { @@ -42,6 +42,19 @@ async function main(): Promise { }); console.log("Server ready"); + // Register signal handlers for graceful cleanup (best-effort) + const shutdown = (signal: string) => { + console.log(`Received ${signal}, cleaning up sessions...`); + cleanupAllSessions(client) + .catch(() => { /* ignore */ }) + .finally(() => { + server.close(); + process.exit(signal === "SIGTERM" ? 143 : 130); + }); + }; + process.once("SIGTERM", () => shutdown("SIGTERM")); + process.once("SIGINT", () => shutdown("SIGINT")); + try { // 5. Run reviewers in parallel const globalTimeout = intEnv("MULTI_REVIEW_TIMEOUT_SECONDS", 900); @@ -83,6 +96,7 @@ async function main(): Promise { return 0; } finally { + await cleanupAllSessions(client); server.close(); } } diff --git a/multi-review/src/orchestrator.ts b/multi-review/src/orchestrator.ts index 15d235b..d682695 100644 --- a/multi-review/src/orchestrator.ts +++ b/multi-review/src/orchestrator.ts @@ -1,6 +1,8 @@ import type { OpencodeClient } from "@opencode-ai/sdk"; import type { Reviewer, ReviewResult, OrchestratorOptions } from "./types.js"; +const activeSessions = new Set(); + const DEFAULT_COORDINATOR_PROMPT = `你是一个代码审查协调员。以下审查由独立的专家 reviewer 生成。 你的任务是整合为一个去重后的综合报告。 @@ -60,6 +62,7 @@ export async function runParallelReviewers( reviewer.name, ); sessionId = sessionResult.data.id; + activeSessions.add(sessionId); const promptResult = await withTimeout( client.session.prompt({ @@ -89,6 +92,7 @@ export async function runParallelReviewers( return { reviewer: reviewer.name, content: "", success: false, error: msg }; } finally { if (sessionId) { + activeSessions.delete(sessionId); try { await client.session.delete({ path: { id: sessionId } }); } catch { /* ignore */ } } } @@ -117,6 +121,7 @@ export async function runCoordinator( "coordinator", ); sessionId = sessionResult.data.id; + activeSessions.add(sessionId); console.log("[coordinator] Starting synthesis..."); @@ -142,6 +147,7 @@ export async function runCoordinator( return content; } finally { if (sessionId) { + activeSessions.delete(sessionId); try { await client.session.delete({ path: { id: sessionId } }); } catch { /* ignore */ } } } @@ -162,3 +168,10 @@ export function buildReviewerDetails(reviews: ReviewResult[]): string { }); return `
\n📋 各 Reviewer 详细审查结果\n\n${details.join("\n\n")}\n\n
`; } + +export async function cleanupAllSessions(client: OpencodeClient): Promise { + for (const id of activeSessions) { + try { await client.session.delete({ path: { id } }); } catch { /* ignore */ } + } + activeSessions.clear(); +}