diffprism 0.15.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.js +2 -2
- package/dist/{chunk-NGHUHDAM.js → chunk-4WN4FIY4.js} +105 -76
- package/dist/mcp-server.js +173 -13
- package/package.json +1 -1
- package/ui-dist/assets/{index-CKJwY3F0.js → index-CNL0CLAo.js} +67 -62
- package/ui-dist/assets/index-ymFelmyA.css +1 -0
- package/ui-dist/index.html +2 -2
- package/ui-dist/assets/index-D39rVNSs.css +0 -1
package/dist/bin.js
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
startGlobalServer,
|
|
7
7
|
startReview,
|
|
8
8
|
startWatch
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-4WN4FIY4.js";
|
|
10
10
|
|
|
11
11
|
// cli/src/index.ts
|
|
12
12
|
import { Command } from "commander";
|
|
@@ -578,7 +578,7 @@ async function serverStop() {
|
|
|
578
578
|
|
|
579
579
|
// cli/src/index.ts
|
|
580
580
|
var program = new Command();
|
|
581
|
-
program.name("diffprism").description("Local-first code review tool for agent-generated changes").version(true ? "0.
|
|
581
|
+
program.name("diffprism").description("Local-first code review tool for agent-generated changes").version(true ? "0.16.0" : "0.0.0-dev");
|
|
582
582
|
program.command("review [ref]").description("Open a browser-based diff review").option("--staged", "Review staged changes").option("--unstaged", "Review unstaged changes").option("-t, --title <title>", "Review title").option("--dev", "Use Vite dev server with HMR instead of static files").action(review);
|
|
583
583
|
program.command("start [ref]").description("Set up DiffPrism and start watching for changes").option("--staged", "Watch staged changes").option("--unstaged", "Watch unstaged changes").option("-t, --title <title>", "Review title").option("--interval <ms>", "Poll interval in milliseconds (default: 1000)").option("--dev", "Use Vite dev server with HMR instead of static files").option("--global", "Install skill globally (~/.claude/skills/)").option("--force", "Overwrite existing configuration files").action(start);
|
|
584
584
|
program.command("watch [ref]").description("Start a persistent diff watcher with live-updating browser UI").option("--staged", "Watch staged changes").option("--unstaged", "Watch unstaged changes").option("-t, --title <title>", "Review title").option("--interval <ms>", "Poll interval in milliseconds (default: 1000)").option("--dev", "Use Vite dev server with HMR instead of static files").action(watch);
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
// packages/core/src/pipeline.ts
|
|
2
|
-
import getPort from "get-port";
|
|
3
|
-
import open from "open";
|
|
4
|
-
|
|
5
1
|
// packages/git/src/local.ts
|
|
6
2
|
import { execSync } from "child_process";
|
|
7
3
|
import { readFileSync } from "fs";
|
|
@@ -717,6 +713,10 @@ function analyze(diffSet) {
|
|
|
717
713
|
};
|
|
718
714
|
}
|
|
719
715
|
|
|
716
|
+
// packages/core/src/pipeline.ts
|
|
717
|
+
import getPort from "get-port";
|
|
718
|
+
import open from "open";
|
|
719
|
+
|
|
720
720
|
// packages/core/src/ws-bridge.ts
|
|
721
721
|
import { WebSocketServer, WebSocket } from "ws";
|
|
722
722
|
function createWsBridge(port) {
|
|
@@ -1061,6 +1061,76 @@ function consumeReviewResult(cwd) {
|
|
|
1061
1061
|
}
|
|
1062
1062
|
}
|
|
1063
1063
|
|
|
1064
|
+
// packages/core/src/server-file.ts
|
|
1065
|
+
import fs3 from "fs";
|
|
1066
|
+
import path5 from "path";
|
|
1067
|
+
import os from "os";
|
|
1068
|
+
function serverDir() {
|
|
1069
|
+
return path5.join(os.homedir(), ".diffprism");
|
|
1070
|
+
}
|
|
1071
|
+
function serverFilePath() {
|
|
1072
|
+
return path5.join(serverDir(), "server.json");
|
|
1073
|
+
}
|
|
1074
|
+
function isPidAlive2(pid) {
|
|
1075
|
+
try {
|
|
1076
|
+
process.kill(pid, 0);
|
|
1077
|
+
return true;
|
|
1078
|
+
} catch {
|
|
1079
|
+
return false;
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
function writeServerFile(info) {
|
|
1083
|
+
const dir = serverDir();
|
|
1084
|
+
if (!fs3.existsSync(dir)) {
|
|
1085
|
+
fs3.mkdirSync(dir, { recursive: true });
|
|
1086
|
+
}
|
|
1087
|
+
fs3.writeFileSync(serverFilePath(), JSON.stringify(info, null, 2) + "\n");
|
|
1088
|
+
}
|
|
1089
|
+
function readServerFile() {
|
|
1090
|
+
const filePath = serverFilePath();
|
|
1091
|
+
if (!fs3.existsSync(filePath)) {
|
|
1092
|
+
return null;
|
|
1093
|
+
}
|
|
1094
|
+
try {
|
|
1095
|
+
const raw = fs3.readFileSync(filePath, "utf-8");
|
|
1096
|
+
const info = JSON.parse(raw);
|
|
1097
|
+
if (!isPidAlive2(info.pid)) {
|
|
1098
|
+
fs3.unlinkSync(filePath);
|
|
1099
|
+
return null;
|
|
1100
|
+
}
|
|
1101
|
+
return info;
|
|
1102
|
+
} catch {
|
|
1103
|
+
return null;
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
function removeServerFile() {
|
|
1107
|
+
try {
|
|
1108
|
+
const filePath = serverFilePath();
|
|
1109
|
+
if (fs3.existsSync(filePath)) {
|
|
1110
|
+
fs3.unlinkSync(filePath);
|
|
1111
|
+
}
|
|
1112
|
+
} catch {
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
async function isServerAlive() {
|
|
1116
|
+
const info = readServerFile();
|
|
1117
|
+
if (!info) {
|
|
1118
|
+
return null;
|
|
1119
|
+
}
|
|
1120
|
+
try {
|
|
1121
|
+
const response = await fetch(`http://localhost:${info.httpPort}/api/status`, {
|
|
1122
|
+
signal: AbortSignal.timeout(2e3)
|
|
1123
|
+
});
|
|
1124
|
+
if (response.ok) {
|
|
1125
|
+
return info;
|
|
1126
|
+
}
|
|
1127
|
+
return null;
|
|
1128
|
+
} catch {
|
|
1129
|
+
removeServerFile();
|
|
1130
|
+
return null;
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1064
1134
|
// packages/core/src/watch.ts
|
|
1065
1135
|
import { createHash } from "crypto";
|
|
1066
1136
|
import getPort2 from "get-port";
|
|
@@ -1375,78 +1445,6 @@ import { randomUUID } from "crypto";
|
|
|
1375
1445
|
import getPort3 from "get-port";
|
|
1376
1446
|
import open3 from "open";
|
|
1377
1447
|
import { WebSocketServer as WebSocketServer3, WebSocket as WebSocket3 } from "ws";
|
|
1378
|
-
|
|
1379
|
-
// packages/core/src/server-file.ts
|
|
1380
|
-
import fs3 from "fs";
|
|
1381
|
-
import path5 from "path";
|
|
1382
|
-
import os from "os";
|
|
1383
|
-
function serverDir() {
|
|
1384
|
-
return path5.join(os.homedir(), ".diffprism");
|
|
1385
|
-
}
|
|
1386
|
-
function serverFilePath() {
|
|
1387
|
-
return path5.join(serverDir(), "server.json");
|
|
1388
|
-
}
|
|
1389
|
-
function isPidAlive2(pid) {
|
|
1390
|
-
try {
|
|
1391
|
-
process.kill(pid, 0);
|
|
1392
|
-
return true;
|
|
1393
|
-
} catch {
|
|
1394
|
-
return false;
|
|
1395
|
-
}
|
|
1396
|
-
}
|
|
1397
|
-
function writeServerFile(info) {
|
|
1398
|
-
const dir = serverDir();
|
|
1399
|
-
if (!fs3.existsSync(dir)) {
|
|
1400
|
-
fs3.mkdirSync(dir, { recursive: true });
|
|
1401
|
-
}
|
|
1402
|
-
fs3.writeFileSync(serverFilePath(), JSON.stringify(info, null, 2) + "\n");
|
|
1403
|
-
}
|
|
1404
|
-
function readServerFile() {
|
|
1405
|
-
const filePath = serverFilePath();
|
|
1406
|
-
if (!fs3.existsSync(filePath)) {
|
|
1407
|
-
return null;
|
|
1408
|
-
}
|
|
1409
|
-
try {
|
|
1410
|
-
const raw = fs3.readFileSync(filePath, "utf-8");
|
|
1411
|
-
const info = JSON.parse(raw);
|
|
1412
|
-
if (!isPidAlive2(info.pid)) {
|
|
1413
|
-
fs3.unlinkSync(filePath);
|
|
1414
|
-
return null;
|
|
1415
|
-
}
|
|
1416
|
-
return info;
|
|
1417
|
-
} catch {
|
|
1418
|
-
return null;
|
|
1419
|
-
}
|
|
1420
|
-
}
|
|
1421
|
-
function removeServerFile() {
|
|
1422
|
-
try {
|
|
1423
|
-
const filePath = serverFilePath();
|
|
1424
|
-
if (fs3.existsSync(filePath)) {
|
|
1425
|
-
fs3.unlinkSync(filePath);
|
|
1426
|
-
}
|
|
1427
|
-
} catch {
|
|
1428
|
-
}
|
|
1429
|
-
}
|
|
1430
|
-
async function isServerAlive() {
|
|
1431
|
-
const info = readServerFile();
|
|
1432
|
-
if (!info) {
|
|
1433
|
-
return null;
|
|
1434
|
-
}
|
|
1435
|
-
try {
|
|
1436
|
-
const response = await fetch(`http://localhost:${info.httpPort}/api/status`, {
|
|
1437
|
-
signal: AbortSignal.timeout(2e3)
|
|
1438
|
-
});
|
|
1439
|
-
if (response.ok) {
|
|
1440
|
-
return info;
|
|
1441
|
-
}
|
|
1442
|
-
return null;
|
|
1443
|
-
} catch {
|
|
1444
|
-
removeServerFile();
|
|
1445
|
-
return null;
|
|
1446
|
-
}
|
|
1447
|
-
}
|
|
1448
|
-
|
|
1449
|
-
// packages/core/src/global-server.ts
|
|
1450
1448
|
var sessions2 = /* @__PURE__ */ new Map();
|
|
1451
1449
|
var clientSessions = /* @__PURE__ */ new Map();
|
|
1452
1450
|
function toSummary(session) {
|
|
@@ -1699,6 +1697,24 @@ async function startGlobalServer(options = {}) {
|
|
|
1699
1697
|
};
|
|
1700
1698
|
ws.send(JSON.stringify(msg));
|
|
1701
1699
|
}
|
|
1700
|
+
} else {
|
|
1701
|
+
const summaries = Array.from(sessions2.values()).map(toSummary);
|
|
1702
|
+
const msg = {
|
|
1703
|
+
type: "session:list",
|
|
1704
|
+
payload: summaries
|
|
1705
|
+
};
|
|
1706
|
+
ws.send(JSON.stringify(msg));
|
|
1707
|
+
if (summaries.length === 1) {
|
|
1708
|
+
const session = sessions2.get(summaries[0].id);
|
|
1709
|
+
if (session) {
|
|
1710
|
+
clientSessions.set(ws, session.id);
|
|
1711
|
+
session.status = "in_review";
|
|
1712
|
+
ws.send(JSON.stringify({
|
|
1713
|
+
type: "review:init",
|
|
1714
|
+
payload: session.payload
|
|
1715
|
+
}));
|
|
1716
|
+
}
|
|
1717
|
+
}
|
|
1702
1718
|
}
|
|
1703
1719
|
ws.on("message", (data) => {
|
|
1704
1720
|
try {
|
|
@@ -1712,6 +1728,16 @@ async function startGlobalServer(options = {}) {
|
|
|
1712
1728
|
session.status = "submitted";
|
|
1713
1729
|
}
|
|
1714
1730
|
}
|
|
1731
|
+
} else if (msg.type === "session:select") {
|
|
1732
|
+
const session = sessions2.get(msg.payload.sessionId);
|
|
1733
|
+
if (session) {
|
|
1734
|
+
clientSessions.set(ws, session.id);
|
|
1735
|
+
session.status = "in_review";
|
|
1736
|
+
ws.send(JSON.stringify({
|
|
1737
|
+
type: "review:init",
|
|
1738
|
+
payload: session.payload
|
|
1739
|
+
}));
|
|
1740
|
+
}
|
|
1715
1741
|
}
|
|
1716
1742
|
} catch {
|
|
1717
1743
|
}
|
|
@@ -1769,6 +1795,9 @@ Waiting for reviews...
|
|
|
1769
1795
|
}
|
|
1770
1796
|
|
|
1771
1797
|
export {
|
|
1798
|
+
getCurrentBranch,
|
|
1799
|
+
getDiff,
|
|
1800
|
+
analyze,
|
|
1772
1801
|
startReview,
|
|
1773
1802
|
readWatchFile,
|
|
1774
1803
|
readReviewResult,
|
package/dist/mcp-server.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
|
+
analyze,
|
|
2
3
|
consumeReviewResult,
|
|
4
|
+
getCurrentBranch,
|
|
5
|
+
getDiff,
|
|
6
|
+
isServerAlive,
|
|
3
7
|
readReviewResult,
|
|
4
8
|
readWatchFile,
|
|
5
9
|
startReview
|
|
6
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-4WN4FIY4.js";
|
|
7
11
|
|
|
8
12
|
// packages/mcp-server/src/index.ts
|
|
9
13
|
import fs from "fs";
|
|
@@ -11,10 +15,68 @@ import path from "path";
|
|
|
11
15
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
12
16
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
13
17
|
import { z } from "zod";
|
|
18
|
+
var lastGlobalSessionId = null;
|
|
19
|
+
var lastGlobalServerInfo = null;
|
|
20
|
+
async function reviewViaGlobalServer(serverInfo, diffRef, options) {
|
|
21
|
+
const cwd = options.cwd ?? process.cwd();
|
|
22
|
+
const { diffSet, rawDiff } = getDiff(diffRef, { cwd });
|
|
23
|
+
const currentBranch = getCurrentBranch({ cwd });
|
|
24
|
+
if (diffSet.files.length === 0) {
|
|
25
|
+
return {
|
|
26
|
+
decision: "approved",
|
|
27
|
+
comments: [],
|
|
28
|
+
summary: "No changes to review."
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
const briefing = analyze(diffSet);
|
|
32
|
+
const payload = {
|
|
33
|
+
reviewId: "",
|
|
34
|
+
// Server assigns the real ID
|
|
35
|
+
diffSet,
|
|
36
|
+
rawDiff,
|
|
37
|
+
briefing,
|
|
38
|
+
metadata: {
|
|
39
|
+
title: options.title,
|
|
40
|
+
description: options.description,
|
|
41
|
+
reasoning: options.reasoning,
|
|
42
|
+
currentBranch
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
const createResponse = await fetch(
|
|
46
|
+
`http://localhost:${serverInfo.httpPort}/api/reviews`,
|
|
47
|
+
{
|
|
48
|
+
method: "POST",
|
|
49
|
+
headers: { "Content-Type": "application/json" },
|
|
50
|
+
body: JSON.stringify({ payload, projectPath: cwd })
|
|
51
|
+
}
|
|
52
|
+
);
|
|
53
|
+
if (!createResponse.ok) {
|
|
54
|
+
throw new Error(`Global server returned ${createResponse.status} on create`);
|
|
55
|
+
}
|
|
56
|
+
const { sessionId } = await createResponse.json();
|
|
57
|
+
lastGlobalSessionId = sessionId;
|
|
58
|
+
lastGlobalServerInfo = serverInfo;
|
|
59
|
+
const pollIntervalMs = 2e3;
|
|
60
|
+
const maxWaitMs = 600 * 1e3;
|
|
61
|
+
const start = Date.now();
|
|
62
|
+
while (Date.now() - start < maxWaitMs) {
|
|
63
|
+
const resultResponse = await fetch(
|
|
64
|
+
`http://localhost:${serverInfo.httpPort}/api/reviews/${sessionId}/result`
|
|
65
|
+
);
|
|
66
|
+
if (resultResponse.ok) {
|
|
67
|
+
const data = await resultResponse.json();
|
|
68
|
+
if (data.result) {
|
|
69
|
+
return data.result;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
73
|
+
}
|
|
74
|
+
throw new Error("Review timed out waiting for submission.");
|
|
75
|
+
}
|
|
14
76
|
async function startMcpServer() {
|
|
15
77
|
const server = new McpServer({
|
|
16
78
|
name: "diffprism",
|
|
17
|
-
version: true ? "0.
|
|
79
|
+
version: true ? "0.16.0" : "0.0.0-dev"
|
|
18
80
|
});
|
|
19
81
|
server.tool(
|
|
20
82
|
"open_review",
|
|
@@ -29,6 +91,23 @@ async function startMcpServer() {
|
|
|
29
91
|
},
|
|
30
92
|
async ({ diff_ref, title, description, reasoning }) => {
|
|
31
93
|
try {
|
|
94
|
+
const serverInfo = await isServerAlive();
|
|
95
|
+
if (serverInfo) {
|
|
96
|
+
const result2 = await reviewViaGlobalServer(serverInfo, diff_ref, {
|
|
97
|
+
title,
|
|
98
|
+
description,
|
|
99
|
+
reasoning,
|
|
100
|
+
cwd: process.cwd()
|
|
101
|
+
});
|
|
102
|
+
return {
|
|
103
|
+
content: [
|
|
104
|
+
{
|
|
105
|
+
type: "text",
|
|
106
|
+
text: JSON.stringify(result2, null, 2)
|
|
107
|
+
}
|
|
108
|
+
]
|
|
109
|
+
};
|
|
110
|
+
}
|
|
32
111
|
const isDev = fs.existsSync(
|
|
33
112
|
path.join(process.cwd(), "packages", "ui", "src", "App.tsx")
|
|
34
113
|
);
|
|
@@ -39,7 +118,6 @@ async function startMcpServer() {
|
|
|
39
118
|
reasoning,
|
|
40
119
|
cwd: process.cwd(),
|
|
41
120
|
silent: true,
|
|
42
|
-
// Suppress stdout — MCP uses stdio
|
|
43
121
|
dev: isDev
|
|
44
122
|
});
|
|
45
123
|
return {
|
|
@@ -66,7 +144,7 @@ async function startMcpServer() {
|
|
|
66
144
|
);
|
|
67
145
|
server.tool(
|
|
68
146
|
"update_review_context",
|
|
69
|
-
"Push reasoning/context to a running DiffPrism
|
|
147
|
+
"Push reasoning/context to a running DiffPrism review session. Non-blocking \u2014 returns immediately. Use this when `diffprism watch` or `diffprism server` is running to update the review UI with agent reasoning without opening a new review.",
|
|
70
148
|
{
|
|
71
149
|
reasoning: z.string().optional().describe("Agent reasoning about the current changes"),
|
|
72
150
|
title: z.string().optional().describe("Updated title for the review"),
|
|
@@ -74,21 +152,44 @@ async function startMcpServer() {
|
|
|
74
152
|
},
|
|
75
153
|
async ({ reasoning, title, description }) => {
|
|
76
154
|
try {
|
|
155
|
+
const payload = {};
|
|
156
|
+
if (reasoning !== void 0) payload.reasoning = reasoning;
|
|
157
|
+
if (title !== void 0) payload.title = title;
|
|
158
|
+
if (description !== void 0) payload.description = description;
|
|
159
|
+
if (lastGlobalSessionId && lastGlobalServerInfo) {
|
|
160
|
+
const serverInfo = await isServerAlive();
|
|
161
|
+
if (serverInfo) {
|
|
162
|
+
const response2 = await fetch(
|
|
163
|
+
`http://localhost:${serverInfo.httpPort}/api/reviews/${lastGlobalSessionId}/context`,
|
|
164
|
+
{
|
|
165
|
+
method: "POST",
|
|
166
|
+
headers: { "Content-Type": "application/json" },
|
|
167
|
+
body: JSON.stringify(payload)
|
|
168
|
+
}
|
|
169
|
+
);
|
|
170
|
+
if (response2.ok) {
|
|
171
|
+
return {
|
|
172
|
+
content: [
|
|
173
|
+
{
|
|
174
|
+
type: "text",
|
|
175
|
+
text: "Context updated in DiffPrism global server session."
|
|
176
|
+
}
|
|
177
|
+
]
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
77
182
|
const watchInfo = readWatchFile();
|
|
78
183
|
if (!watchInfo) {
|
|
79
184
|
return {
|
|
80
185
|
content: [
|
|
81
186
|
{
|
|
82
187
|
type: "text",
|
|
83
|
-
text: "No DiffPrism
|
|
188
|
+
text: "No DiffPrism session is running. Start one with `diffprism watch` or `diffprism server`."
|
|
84
189
|
}
|
|
85
190
|
]
|
|
86
191
|
};
|
|
87
192
|
}
|
|
88
|
-
const payload = {};
|
|
89
|
-
if (reasoning !== void 0) payload.reasoning = reasoning;
|
|
90
|
-
if (title !== void 0) payload.title = title;
|
|
91
|
-
if (description !== void 0) payload.description = description;
|
|
92
193
|
const response = await fetch(
|
|
93
194
|
`http://localhost:${watchInfo.wsPort}/api/context`,
|
|
94
195
|
{
|
|
@@ -114,7 +215,7 @@ async function startMcpServer() {
|
|
|
114
215
|
content: [
|
|
115
216
|
{
|
|
116
217
|
type: "text",
|
|
117
|
-
text: `Error updating
|
|
218
|
+
text: `Error updating review context: ${message}`
|
|
118
219
|
}
|
|
119
220
|
],
|
|
120
221
|
isError: true
|
|
@@ -124,16 +225,75 @@ async function startMcpServer() {
|
|
|
124
225
|
);
|
|
125
226
|
server.tool(
|
|
126
227
|
"get_review_result",
|
|
127
|
-
"Fetch the most recent review result from a DiffPrism
|
|
228
|
+
"Fetch the most recent review result from a DiffPrism session. Returns the reviewer's decision and comments if a review has been submitted, or a message indicating no pending result. The result is marked as consumed after retrieval so it won't be returned again. Use wait=true to block until a result is available (recommended after pushing context to a watch session).",
|
|
128
229
|
{
|
|
129
230
|
wait: z.boolean().optional().describe("If true, poll until a review result is available (blocks up to timeout)"),
|
|
130
231
|
timeout: z.number().optional().describe("Max wait time in seconds when wait=true (default: 300, max: 600)")
|
|
131
232
|
},
|
|
132
233
|
async ({ wait, timeout }) => {
|
|
133
234
|
try {
|
|
235
|
+
const maxWaitMs = Math.min(timeout ?? 300, 600) * 1e3;
|
|
236
|
+
const pollIntervalMs = 2e3;
|
|
237
|
+
if (lastGlobalSessionId && lastGlobalServerInfo) {
|
|
238
|
+
const serverInfo = await isServerAlive();
|
|
239
|
+
if (serverInfo) {
|
|
240
|
+
if (wait) {
|
|
241
|
+
const start = Date.now();
|
|
242
|
+
while (Date.now() - start < maxWaitMs) {
|
|
243
|
+
const response2 = await fetch(
|
|
244
|
+
`http://localhost:${serverInfo.httpPort}/api/reviews/${lastGlobalSessionId}/result`
|
|
245
|
+
);
|
|
246
|
+
if (response2.ok) {
|
|
247
|
+
const data2 = await response2.json();
|
|
248
|
+
if (data2.result) {
|
|
249
|
+
return {
|
|
250
|
+
content: [
|
|
251
|
+
{
|
|
252
|
+
type: "text",
|
|
253
|
+
text: JSON.stringify(data2.result, null, 2)
|
|
254
|
+
}
|
|
255
|
+
]
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
260
|
+
}
|
|
261
|
+
return {
|
|
262
|
+
content: [
|
|
263
|
+
{
|
|
264
|
+
type: "text",
|
|
265
|
+
text: "No review result received within timeout."
|
|
266
|
+
}
|
|
267
|
+
]
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
const response = await fetch(
|
|
271
|
+
`http://localhost:${serverInfo.httpPort}/api/reviews/${lastGlobalSessionId}/result`
|
|
272
|
+
);
|
|
273
|
+
if (response.ok) {
|
|
274
|
+
const data2 = await response.json();
|
|
275
|
+
if (data2.result) {
|
|
276
|
+
return {
|
|
277
|
+
content: [
|
|
278
|
+
{
|
|
279
|
+
type: "text",
|
|
280
|
+
text: JSON.stringify(data2.result, null, 2)
|
|
281
|
+
}
|
|
282
|
+
]
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return {
|
|
287
|
+
content: [
|
|
288
|
+
{
|
|
289
|
+
type: "text",
|
|
290
|
+
text: "No pending review result."
|
|
291
|
+
}
|
|
292
|
+
]
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
}
|
|
134
296
|
if (wait) {
|
|
135
|
-
const maxWaitMs = Math.min(timeout ?? 300, 600) * 1e3;
|
|
136
|
-
const pollIntervalMs = 2e3;
|
|
137
297
|
const start = Date.now();
|
|
138
298
|
while (Date.now() - start < maxWaitMs) {
|
|
139
299
|
const data2 = readReviewResult();
|