Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion multi-review/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<number> {
Expand Down Expand Up @@ -42,6 +42,19 @@ async function main(): Promise<number> {
});
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);
Expand Down Expand Up @@ -83,6 +96,7 @@ async function main(): Promise<number> {

return 0;
} finally {
await cleanupAllSessions(client);
server.close();
}
}
Expand Down
13 changes: 13 additions & 0 deletions multi-review/src/orchestrator.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import type { OpencodeClient } from "@opencode-ai/sdk";
import type { Reviewer, ReviewResult, OrchestratorOptions } from "./types.js";

const activeSessions = new Set<string>();

const DEFAULT_COORDINATOR_PROMPT = `你是一个代码审查协调员。以下审查由独立的专家 reviewer 生成。
你的任务是整合为一个去重后的综合报告。

Expand Down Expand Up @@ -60,6 +62,7 @@ export async function runParallelReviewers(
reviewer.name,
);
sessionId = sessionResult.data.id;
activeSessions.add(sessionId);

const promptResult = await withTimeout(
client.session.prompt({
Expand Down Expand Up @@ -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 */ }
}
}
Expand Down Expand Up @@ -117,6 +121,7 @@ export async function runCoordinator(
"coordinator",
);
sessionId = sessionResult.data.id;
activeSessions.add(sessionId);

console.log("[coordinator] Starting synthesis...");

Expand All @@ -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 */ }
}
}
Expand All @@ -162,3 +168,10 @@ export function buildReviewerDetails(reviews: ReviewResult[]): string {
});
return `<details>\n<summary>📋 各 Reviewer 详细审查结果</summary>\n\n${details.join("\n\n")}\n\n</details>`;
}

export async function cleanupAllSessions(client: OpencodeClient): Promise<void> {
for (const id of activeSessions) {
try { await client.session.delete({ path: { id } }); } catch { /* ignore */ }
}
activeSessions.clear();
}
Loading