getgloss 0.12.0 → 0.12.3
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/cli/index.js +2 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/server/daemon.js +125 -13
- package/dist/server/daemon.js.map +1 -1
- package/dist/web/assets/index-DaWYZTKT.js +307 -0
- package/dist/web/assets/{syntax-CEpwlb09.js → syntax-CqkLY5rX.js} +1 -1
- package/dist/web/index.html +1 -1
- package/package.json +1 -1
- package/dist/web/assets/index-Cu9NghTt.js +0 -306
package/dist/server/daemon.js
CHANGED
|
@@ -10,7 +10,7 @@ import path from "path";
|
|
|
10
10
|
// package.json
|
|
11
11
|
var package_default = {
|
|
12
12
|
name: "getgloss",
|
|
13
|
-
version: "0.12.
|
|
13
|
+
version: "0.12.3",
|
|
14
14
|
description: "Local browser-based diff review for coding-agent loops.",
|
|
15
15
|
type: "module",
|
|
16
16
|
packageManager: "pnpm@10.33.2",
|
|
@@ -202,6 +202,7 @@ var DIFF_SCOPE_MODES = ["working", "branch", "explicit"];
|
|
|
202
202
|
var DIFF_FALLBACK_REASONS = ["working-tree-clean", "missing-branch-base"];
|
|
203
203
|
var DIFF_CONTEXT_MAX_LINES = 500;
|
|
204
204
|
var SOURCE_PEEK_MAX_BYTES = 35e4;
|
|
205
|
+
var SOURCE_PEEK_RANGE_MAX_LINES = 240;
|
|
205
206
|
var REVIEW_SCOPE_MODES = ["all", "single", "range"];
|
|
206
207
|
var RESOLUTION_STATUSES = ["partial", "resolved"];
|
|
207
208
|
var OPEN_FILE_SCOPES = ["review", "repo"];
|
|
@@ -272,6 +273,9 @@ function isDiffContextRequest(value) {
|
|
|
272
273
|
function isSourcePeekRequest(value) {
|
|
273
274
|
return isRecord(value) && isString(value.filePath) && isNullableString(value.oldPath) && isOptionalString(value.turnId) && isDiffContextSource(value.source) && isOneOf(value.side, SIDES) && isPositiveInteger(value.line) && isNonNegativeInteger(value.column) && isIdentifier(value.symbol);
|
|
274
275
|
}
|
|
276
|
+
function isSourcePeekRangeRequest(value) {
|
|
277
|
+
return isRecord(value) && isString(value.filePath) && isOptionalString(value.turnId) && isDiffContextSource(value.source) && isOneOf(value.side, SIDES) && isPositiveInteger(value.startLine) && isPositiveInteger(value.lineCount) && value.lineCount <= SOURCE_PEEK_RANGE_MAX_LINES;
|
|
278
|
+
}
|
|
275
279
|
function isSubmitReviewRequest(value) {
|
|
276
280
|
return isRecord(value) && isArrayOf(value.comments, isComment) && isOptional(value.reviewScope, isReviewScope);
|
|
277
281
|
}
|
|
@@ -1013,6 +1017,25 @@ async function resolveSourcePeek({
|
|
|
1013
1017
|
}
|
|
1014
1018
|
throw new Error(`No definition found for ${symbol}`);
|
|
1015
1019
|
}
|
|
1020
|
+
async function readSourcePeekRange({
|
|
1021
|
+
lineCount,
|
|
1022
|
+
repoRoot,
|
|
1023
|
+
sourceFilePath,
|
|
1024
|
+
sourceRef,
|
|
1025
|
+
startLine
|
|
1026
|
+
}) {
|
|
1027
|
+
const content = await readRepoText(repoRoot, sourceRef, sourceFilePath);
|
|
1028
|
+
const limited = limitContentRange(content, startLine, lineCount);
|
|
1029
|
+
return {
|
|
1030
|
+
filePath: sourceFilePath,
|
|
1031
|
+
startLine: limited.startLine,
|
|
1032
|
+
totalLines: limited.totalLines,
|
|
1033
|
+
content: limited.content,
|
|
1034
|
+
truncated: limited.truncated,
|
|
1035
|
+
hasMoreAbove: limited.hasMoreAbove,
|
|
1036
|
+
hasMoreBelow: limited.hasMoreBelow
|
|
1037
|
+
};
|
|
1038
|
+
}
|
|
1016
1039
|
async function resolveImportedTarget({
|
|
1017
1040
|
aliasConfig,
|
|
1018
1041
|
importedName,
|
|
@@ -1191,37 +1214,86 @@ function responseForMatch({
|
|
|
1191
1214
|
language: languageForPath(filePath),
|
|
1192
1215
|
content: limited.content,
|
|
1193
1216
|
truncated: limited.truncated,
|
|
1217
|
+
totalLines: limited.totalLines,
|
|
1218
|
+
hasMoreAbove: limited.hasMoreAbove,
|
|
1219
|
+
hasMoreBelow: limited.hasMoreBelow,
|
|
1194
1220
|
matchReason
|
|
1195
1221
|
};
|
|
1196
1222
|
}
|
|
1197
1223
|
function limitContentAroundLine(content, line) {
|
|
1224
|
+
const lines = splitFileLines2(content);
|
|
1225
|
+
const totalLines = sourceTotalLines(lines);
|
|
1198
1226
|
if (Buffer.byteLength(content, "utf8") <= SOURCE_PEEK_MAX_BYTES) {
|
|
1199
|
-
return {
|
|
1227
|
+
return {
|
|
1228
|
+
content,
|
|
1229
|
+
startLine: 1,
|
|
1230
|
+
totalLines,
|
|
1231
|
+
truncated: false,
|
|
1232
|
+
hasMoreAbove: false,
|
|
1233
|
+
hasMoreBelow: false
|
|
1234
|
+
};
|
|
1235
|
+
}
|
|
1236
|
+
if (lines.length === 0) {
|
|
1237
|
+
return emptySourceWindow();
|
|
1200
1238
|
}
|
|
1201
|
-
const lines = splitFileLines2(content);
|
|
1202
1239
|
const targetIndex = Math.max(0, line - 1);
|
|
1203
1240
|
const preferredStartIndex = Math.max(0, targetIndex - SOURCE_PEEK_TARGET_CONTEXT_LINES);
|
|
1204
1241
|
const preferredEndIndex = Math.min(lines.length, preferredStartIndex + SOURCE_PEEK_CONTEXT_LINES);
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1242
|
+
return limitLinesByIndexes(lines, preferredStartIndex, preferredEndIndex, targetIndex);
|
|
1243
|
+
}
|
|
1244
|
+
function limitContentRange(content, startLine, lineCount) {
|
|
1245
|
+
const lines = splitFileLines2(content);
|
|
1246
|
+
if (lines.length === 0) {
|
|
1247
|
+
return emptySourceWindow();
|
|
1248
|
+
}
|
|
1249
|
+
const cappedLineCount = Math.min(lineCount, SOURCE_PEEK_RANGE_MAX_LINES);
|
|
1250
|
+
const startIndex = clampNumber(startLine - 1, 0, lines.length - 1);
|
|
1251
|
+
const endIndex = Math.min(lines.length, startIndex + cappedLineCount);
|
|
1252
|
+
return limitLinesByIndexes(lines, startIndex, endIndex, null);
|
|
1253
|
+
}
|
|
1254
|
+
function limitLinesByIndexes(lines, startIndex, endIndex, targetIndex) {
|
|
1255
|
+
let limitedStartIndex = startIndex;
|
|
1256
|
+
let limitedEndIndex = endIndex;
|
|
1257
|
+
let limitedContent = lines.slice(limitedStartIndex, limitedEndIndex).join("\n");
|
|
1258
|
+
let truncatedByBytes = false;
|
|
1259
|
+
while (Buffer.byteLength(limitedContent, "utf8") > SOURCE_PEEK_MAX_BYTES && limitedEndIndex - limitedStartIndex > 1) {
|
|
1260
|
+
if (targetIndex !== null && targetIndex - limitedStartIndex > limitedEndIndex - targetIndex - 1) {
|
|
1261
|
+
limitedStartIndex += 1;
|
|
1211
1262
|
} else {
|
|
1212
|
-
|
|
1263
|
+
limitedEndIndex -= 1;
|
|
1213
1264
|
}
|
|
1214
|
-
limitedContent = lines.slice(
|
|
1265
|
+
limitedContent = lines.slice(limitedStartIndex, limitedEndIndex).join("\n");
|
|
1266
|
+
truncatedByBytes = true;
|
|
1215
1267
|
}
|
|
1216
1268
|
if (Buffer.byteLength(limitedContent, "utf8") > SOURCE_PEEK_MAX_BYTES) {
|
|
1217
1269
|
limitedContent = truncateUtf8(limitedContent, SOURCE_PEEK_MAX_BYTES);
|
|
1270
|
+
truncatedByBytes = true;
|
|
1218
1271
|
}
|
|
1219
1272
|
return {
|
|
1220
1273
|
content: limitedContent,
|
|
1221
|
-
startLine:
|
|
1222
|
-
|
|
1274
|
+
startLine: limitedStartIndex + 1,
|
|
1275
|
+
totalLines: sourceTotalLines(lines),
|
|
1276
|
+
truncated: truncatedByBytes || limitedStartIndex > 0 || limitedEndIndex < lines.length,
|
|
1277
|
+
hasMoreAbove: limitedStartIndex > 0,
|
|
1278
|
+
hasMoreBelow: limitedEndIndex < lines.length
|
|
1279
|
+
};
|
|
1280
|
+
}
|
|
1281
|
+
function emptySourceWindow() {
|
|
1282
|
+
return {
|
|
1283
|
+
content: "",
|
|
1284
|
+
startLine: 1,
|
|
1285
|
+
totalLines: 1,
|
|
1286
|
+
truncated: false,
|
|
1287
|
+
hasMoreAbove: false,
|
|
1288
|
+
hasMoreBelow: false
|
|
1223
1289
|
};
|
|
1224
1290
|
}
|
|
1291
|
+
function sourceTotalLines(lines) {
|
|
1292
|
+
return Math.max(1, lines.length);
|
|
1293
|
+
}
|
|
1294
|
+
function clampNumber(value, min, max) {
|
|
1295
|
+
return Math.min(max, Math.max(min, value));
|
|
1296
|
+
}
|
|
1225
1297
|
function truncateUtf8(value, maxBytes) {
|
|
1226
1298
|
const buffer = Buffer.from(value, "utf8");
|
|
1227
1299
|
if (buffer.byteLength <= maxBytes) {
|
|
@@ -2914,6 +2986,46 @@ function createApp(origin2, options = {}) {
|
|
|
2914
2986
|
return c.json({ error: `source peek unavailable: ${formatError(error)}` }, 404);
|
|
2915
2987
|
}
|
|
2916
2988
|
});
|
|
2989
|
+
app.post("/api/reviews/:id/source-peek/range", async (c) => {
|
|
2990
|
+
const id = c.req.param("id");
|
|
2991
|
+
const existing = await reviewStore.get(id);
|
|
2992
|
+
if (!existing) {
|
|
2993
|
+
return c.json({ error: "review not found" }, 404);
|
|
2994
|
+
}
|
|
2995
|
+
const parsed = await readJsonBody(c, isSourcePeekRangeRequest, "source peek range request");
|
|
2996
|
+
if (!parsed.ok) {
|
|
2997
|
+
return parsed.response;
|
|
2998
|
+
}
|
|
2999
|
+
const body = parsed.body;
|
|
3000
|
+
const turn = body.turnId ? await reviewStore.getTurn(id, body.turnId) : null;
|
|
3001
|
+
if (body.turnId && !turn) {
|
|
3002
|
+
return c.json({ error: "turn not found" }, 404);
|
|
3003
|
+
}
|
|
3004
|
+
const diffPayload = turn?.diff ?? existing.diff;
|
|
3005
|
+
const repoRoot = path9.resolve(diffPayload.cwd);
|
|
3006
|
+
const pathError = validateContextPath(repoRoot, body.filePath, "filePath");
|
|
3007
|
+
if (pathError) {
|
|
3008
|
+
return c.json({ error: pathError }, 400);
|
|
3009
|
+
}
|
|
3010
|
+
const source = await resolveContextSource(diffPayload, body.source);
|
|
3011
|
+
if (!source.ok) {
|
|
3012
|
+
return c.json({ error: source.error }, source.status);
|
|
3013
|
+
}
|
|
3014
|
+
const sourceRef = body.side === "L" ? source.oldRef : source.newRef;
|
|
3015
|
+
try {
|
|
3016
|
+
return c.json(
|
|
3017
|
+
await readSourcePeekRange({
|
|
3018
|
+
repoRoot,
|
|
3019
|
+
sourceFilePath: body.filePath,
|
|
3020
|
+
sourceRef,
|
|
3021
|
+
startLine: body.startLine,
|
|
3022
|
+
lineCount: body.lineCount
|
|
3023
|
+
})
|
|
3024
|
+
);
|
|
3025
|
+
} catch (error) {
|
|
3026
|
+
return c.json({ error: `source range unavailable: ${formatError(error)}` }, 404);
|
|
3027
|
+
}
|
|
3028
|
+
});
|
|
2917
3029
|
app.post("/api/reviews/:id/files/content", async (c) => {
|
|
2918
3030
|
const id = c.req.param("id");
|
|
2919
3031
|
const existing = await reviewStore.get(id);
|