glassbox 0.2.7 → 0.3.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/cli.js +419 -45
- package/dist/client/app.global.js +8 -8
- package/dist/client/styles.css +1 -1
- package/package.json +3 -2
package/dist/cli.js
CHANGED
|
@@ -87,7 +87,8 @@ var init_schema = __esm({
|
|
|
87
87
|
id TEXT PRIMARY KEY DEFAULT 'singleton',
|
|
88
88
|
sort_mode TEXT NOT NULL DEFAULT 'folder',
|
|
89
89
|
risk_sort_dimension TEXT NOT NULL DEFAULT 'aggregate',
|
|
90
|
-
show_risk_scores BOOLEAN NOT NULL DEFAULT FALSE
|
|
90
|
+
show_risk_scores BOOLEAN NOT NULL DEFAULT FALSE,
|
|
91
|
+
ignore_whitespace BOOLEAN NOT NULL DEFAULT FALSE
|
|
91
92
|
);
|
|
92
93
|
`;
|
|
93
94
|
}
|
|
@@ -142,6 +143,7 @@ async function initSchema(db2) {
|
|
|
142
143
|
await addColumnIfMissing(db2, "ai_file_scores", "notes", "TEXT");
|
|
143
144
|
await addColumnIfMissing(db2, "ai_analyses", "progress_completed", "INTEGER NOT NULL DEFAULT 0");
|
|
144
145
|
await addColumnIfMissing(db2, "ai_analyses", "progress_total", "INTEGER NOT NULL DEFAULT 0");
|
|
146
|
+
await addColumnIfMissing(db2, "user_preferences", "ignore_whitespace", "BOOLEAN NOT NULL DEFAULT FALSE");
|
|
145
147
|
await db2.exec(
|
|
146
148
|
`UPDATE ai_analyses SET status = 'failed', error_message = 'Interrupted (server restarted)' WHERE status = 'running'`
|
|
147
149
|
);
|
|
@@ -389,7 +391,7 @@ init_queries();
|
|
|
389
391
|
init_connection();
|
|
390
392
|
import { mkdirSync as mkdirSync7 } from "fs";
|
|
391
393
|
import { tmpdir } from "os";
|
|
392
|
-
import { join as join9, resolve as
|
|
394
|
+
import { join as join9, resolve as resolve3 } from "path";
|
|
393
395
|
|
|
394
396
|
// src/debug.ts
|
|
395
397
|
var debugEnabled = false;
|
|
@@ -798,7 +800,7 @@ async function getUserPreferences() {
|
|
|
798
800
|
["singleton"]
|
|
799
801
|
);
|
|
800
802
|
if (result.rows.length === 0) {
|
|
801
|
-
return { sort_mode: "folder", risk_sort_dimension: "aggregate", show_risk_scores: false };
|
|
803
|
+
return { sort_mode: "folder", risk_sort_dimension: "aggregate", show_risk_scores: false, ignore_whitespace: false };
|
|
802
804
|
}
|
|
803
805
|
return result.rows[0];
|
|
804
806
|
}
|
|
@@ -807,13 +809,14 @@ async function saveUserPreferences(prefs) {
|
|
|
807
809
|
const current = await getUserPreferences();
|
|
808
810
|
const merged = { ...current, ...prefs };
|
|
809
811
|
await db2.query(
|
|
810
|
-
`INSERT INTO user_preferences (id, sort_mode, risk_sort_dimension, show_risk_scores)
|
|
811
|
-
VALUES ($1, $2, $3, $4)
|
|
812
|
+
`INSERT INTO user_preferences (id, sort_mode, risk_sort_dimension, show_risk_scores, ignore_whitespace)
|
|
813
|
+
VALUES ($1, $2, $3, $4, $5)
|
|
812
814
|
ON CONFLICT (id) DO UPDATE SET
|
|
813
815
|
sort_mode = EXCLUDED.sort_mode,
|
|
814
816
|
risk_sort_dimension = EXCLUDED.risk_sort_dimension,
|
|
815
|
-
show_risk_scores = EXCLUDED.show_risk_scores
|
|
816
|
-
|
|
817
|
+
show_risk_scores = EXCLUDED.show_risk_scores,
|
|
818
|
+
ignore_whitespace = EXCLUDED.ignore_whitespace`,
|
|
819
|
+
["singleton", merged.sort_mode, merged.risk_sort_dimension, merged.show_risk_scores, merged.ignore_whitespace]
|
|
817
820
|
);
|
|
818
821
|
}
|
|
819
822
|
|
|
@@ -1493,6 +1496,34 @@ function getFileContent(filePath, ref, cwd) {
|
|
|
1493
1496
|
function getHeadCommit(cwd) {
|
|
1494
1497
|
return execSync2("git rev-parse HEAD", { cwd, encoding: "utf-8" }).trim();
|
|
1495
1498
|
}
|
|
1499
|
+
function parseModeString(modeStr) {
|
|
1500
|
+
if (modeStr === "uncommitted") return { type: "uncommitted" };
|
|
1501
|
+
if (modeStr === "staged") return { type: "staged" };
|
|
1502
|
+
if (modeStr === "unstaged") return { type: "unstaged" };
|
|
1503
|
+
if (modeStr === "all") return { type: "all" };
|
|
1504
|
+
if (modeStr.startsWith("commit:")) return { type: "commit", sha: modeStr.slice(7) };
|
|
1505
|
+
if (modeStr.startsWith("range:")) {
|
|
1506
|
+
const parts = modeStr.slice(6).split("..");
|
|
1507
|
+
return { type: "range", from: parts[0], to: parts[1] || "HEAD" };
|
|
1508
|
+
}
|
|
1509
|
+
if (modeStr.startsWith("branch:")) return { type: "branch", name: modeStr.slice(7) };
|
|
1510
|
+
if (modeStr.startsWith("files:")) return { type: "files", patterns: modeStr.slice(6).split(",") };
|
|
1511
|
+
return { type: "uncommitted" };
|
|
1512
|
+
}
|
|
1513
|
+
function getSingleFileDiff(mode, filePath, repoRoot, extraFlags = "") {
|
|
1514
|
+
if (mode.type === "all") {
|
|
1515
|
+
return createNewFileDiff(filePath, repoRoot);
|
|
1516
|
+
}
|
|
1517
|
+
const diffArgs = getDiffArgs(mode);
|
|
1518
|
+
let rawDiff;
|
|
1519
|
+
try {
|
|
1520
|
+
rawDiff = git(`${diffArgs} -U3 ${extraFlags} -- ${filePath}`, repoRoot);
|
|
1521
|
+
} catch {
|
|
1522
|
+
rawDiff = "";
|
|
1523
|
+
}
|
|
1524
|
+
const diffs = parseDiff(rawDiff);
|
|
1525
|
+
return diffs[0] ?? null;
|
|
1526
|
+
}
|
|
1496
1527
|
function getModeString(mode) {
|
|
1497
1528
|
switch (mode.type) {
|
|
1498
1529
|
case "uncommitted":
|
|
@@ -1686,7 +1717,7 @@ async function updateReviewDiffs(reviewId, newDiffs, headCommit) {
|
|
|
1686
1717
|
// src/server.ts
|
|
1687
1718
|
import { serve } from "@hono/node-server";
|
|
1688
1719
|
import { exec } from "child_process";
|
|
1689
|
-
import { existsSync as existsSync5, readFileSync as
|
|
1720
|
+
import { existsSync as existsSync5, readFileSync as readFileSync7 } from "fs";
|
|
1690
1721
|
import { Hono as Hono4 } from "hono";
|
|
1691
1722
|
import { dirname, join as join6 } from "path";
|
|
1692
1723
|
import { fileURLToPath } from "url";
|
|
@@ -2408,8 +2439,8 @@ function isRetriable(err) {
|
|
|
2408
2439
|
return msg.includes("429") || msg.includes("500") || msg.includes("502") || msg.includes("503") || msg.includes("504") || msg.includes("rate_limit");
|
|
2409
2440
|
}
|
|
2410
2441
|
function sleep(ms) {
|
|
2411
|
-
return new Promise((
|
|
2412
|
-
setTimeout(
|
|
2442
|
+
return new Promise((resolve4) => {
|
|
2443
|
+
setTimeout(resolve4, ms);
|
|
2413
2444
|
});
|
|
2414
2445
|
}
|
|
2415
2446
|
|
|
@@ -2453,8 +2484,8 @@ function randomLines(count) {
|
|
|
2453
2484
|
return lines.sort((a, b) => a.line - b.line);
|
|
2454
2485
|
}
|
|
2455
2486
|
function sleep2(ms) {
|
|
2456
|
-
return new Promise((
|
|
2457
|
-
setTimeout(
|
|
2487
|
+
return new Promise((resolve4) => {
|
|
2488
|
+
setTimeout(resolve4, ms);
|
|
2458
2489
|
});
|
|
2459
2490
|
}
|
|
2460
2491
|
async function mockRiskAnalysisBatch(files) {
|
|
@@ -2906,7 +2937,7 @@ aiApiRoutes.post("/preferences", async (c) => {
|
|
|
2906
2937
|
|
|
2907
2938
|
// src/routes/api.ts
|
|
2908
2939
|
init_queries();
|
|
2909
|
-
import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as
|
|
2940
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
|
|
2910
2941
|
import { Hono as Hono2 } from "hono";
|
|
2911
2942
|
import { join as join5 } from "path";
|
|
2912
2943
|
|
|
@@ -3052,6 +3083,198 @@ async function generateReviewExport(reviewId, repoRoot, isCurrent) {
|
|
|
3052
3083
|
return archivePath;
|
|
3053
3084
|
}
|
|
3054
3085
|
|
|
3086
|
+
// src/git/image.ts
|
|
3087
|
+
import { execSync as execSync4 } from "child_process";
|
|
3088
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
3089
|
+
import { resolve as resolve2 } from "path";
|
|
3090
|
+
var IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([".png", ".jpg", ".jpeg", ".gif", ".webp", ".svg"]);
|
|
3091
|
+
function isImageFile(filePath) {
|
|
3092
|
+
const ext = filePath.slice(filePath.lastIndexOf(".")).toLowerCase();
|
|
3093
|
+
return IMAGE_EXTENSIONS.has(ext);
|
|
3094
|
+
}
|
|
3095
|
+
function getContentType(filePath) {
|
|
3096
|
+
const ext = filePath.slice(filePath.lastIndexOf(".")).toLowerCase();
|
|
3097
|
+
switch (ext) {
|
|
3098
|
+
case ".png":
|
|
3099
|
+
return "image/png";
|
|
3100
|
+
case ".jpg":
|
|
3101
|
+
case ".jpeg":
|
|
3102
|
+
return "image/jpeg";
|
|
3103
|
+
case ".gif":
|
|
3104
|
+
return "image/gif";
|
|
3105
|
+
case ".webp":
|
|
3106
|
+
return "image/webp";
|
|
3107
|
+
case ".svg":
|
|
3108
|
+
return "image/svg+xml";
|
|
3109
|
+
default:
|
|
3110
|
+
return "application/octet-stream";
|
|
3111
|
+
}
|
|
3112
|
+
}
|
|
3113
|
+
function getOldRef(mode) {
|
|
3114
|
+
switch (mode.type) {
|
|
3115
|
+
case "uncommitted":
|
|
3116
|
+
return "HEAD";
|
|
3117
|
+
case "staged":
|
|
3118
|
+
return "HEAD";
|
|
3119
|
+
case "unstaged":
|
|
3120
|
+
return null;
|
|
3121
|
+
// old = index, use ':'
|
|
3122
|
+
case "commit":
|
|
3123
|
+
return `${mode.sha}~1`;
|
|
3124
|
+
case "range":
|
|
3125
|
+
return mode.from;
|
|
3126
|
+
case "branch":
|
|
3127
|
+
return mode.name;
|
|
3128
|
+
case "files":
|
|
3129
|
+
return "HEAD";
|
|
3130
|
+
case "all":
|
|
3131
|
+
return null;
|
|
3132
|
+
}
|
|
3133
|
+
}
|
|
3134
|
+
function getNewRef(mode) {
|
|
3135
|
+
switch (mode.type) {
|
|
3136
|
+
case "uncommitted":
|
|
3137
|
+
return null;
|
|
3138
|
+
// working tree
|
|
3139
|
+
case "staged":
|
|
3140
|
+
return null;
|
|
3141
|
+
// index, but git show : works
|
|
3142
|
+
case "unstaged":
|
|
3143
|
+
return null;
|
|
3144
|
+
// working tree
|
|
3145
|
+
case "commit":
|
|
3146
|
+
return mode.sha;
|
|
3147
|
+
case "range":
|
|
3148
|
+
return mode.to;
|
|
3149
|
+
case "branch":
|
|
3150
|
+
return "HEAD";
|
|
3151
|
+
case "files":
|
|
3152
|
+
return null;
|
|
3153
|
+
case "all":
|
|
3154
|
+
return null;
|
|
3155
|
+
}
|
|
3156
|
+
}
|
|
3157
|
+
function gitShowFile(ref, filePath, repoRoot) {
|
|
3158
|
+
try {
|
|
3159
|
+
const spec = ref === ":" ? `:${filePath}` : `${ref}:${filePath}`;
|
|
3160
|
+
return execSync4(`git show "${spec}"`, { cwd: repoRoot, maxBuffer: 50 * 1024 * 1024 });
|
|
3161
|
+
} catch {
|
|
3162
|
+
return null;
|
|
3163
|
+
}
|
|
3164
|
+
}
|
|
3165
|
+
function readWorkingFile(filePath, repoRoot) {
|
|
3166
|
+
try {
|
|
3167
|
+
return readFileSync5(resolve2(repoRoot, filePath));
|
|
3168
|
+
} catch {
|
|
3169
|
+
return null;
|
|
3170
|
+
}
|
|
3171
|
+
}
|
|
3172
|
+
function getOldImage(mode, filePath, oldPath, repoRoot) {
|
|
3173
|
+
const ref = getOldRef(mode);
|
|
3174
|
+
const path = oldPath ?? filePath;
|
|
3175
|
+
if (ref === null) {
|
|
3176
|
+
const data2 = readWorkingFile(path, repoRoot);
|
|
3177
|
+
if (!data2) return null;
|
|
3178
|
+
return { data: data2, size: data2.length };
|
|
3179
|
+
}
|
|
3180
|
+
const actualRef = mode.type === "unstaged" ? ":" : ref;
|
|
3181
|
+
const data = gitShowFile(actualRef, path, repoRoot);
|
|
3182
|
+
if (!data) return null;
|
|
3183
|
+
return { data, size: data.length };
|
|
3184
|
+
}
|
|
3185
|
+
function getNewImage(mode, filePath, repoRoot) {
|
|
3186
|
+
const ref = getNewRef(mode);
|
|
3187
|
+
if (ref === null) {
|
|
3188
|
+
if (mode.type === "staged") {
|
|
3189
|
+
const data3 = gitShowFile(":", filePath, repoRoot);
|
|
3190
|
+
if (!data3) return null;
|
|
3191
|
+
return { data: data3, size: data3.length };
|
|
3192
|
+
}
|
|
3193
|
+
const data2 = readWorkingFile(filePath, repoRoot);
|
|
3194
|
+
if (!data2) return null;
|
|
3195
|
+
return { data: data2, size: data2.length };
|
|
3196
|
+
}
|
|
3197
|
+
const data = gitShowFile(ref, filePath, repoRoot);
|
|
3198
|
+
if (!data) return null;
|
|
3199
|
+
return { data, size: data.length };
|
|
3200
|
+
}
|
|
3201
|
+
async function extractMetadata(data, filePath) {
|
|
3202
|
+
const ext = filePath.slice(filePath.lastIndexOf(".")).toLowerCase();
|
|
3203
|
+
if (ext === ".svg") {
|
|
3204
|
+
const text = data.toString("utf-8");
|
|
3205
|
+
const widthMatch = text.match(/\bwidth\s*=\s*["']([^"']+)["']/);
|
|
3206
|
+
const heightMatch = text.match(/\bheight\s*=\s*["']([^"']+)["']/);
|
|
3207
|
+
const viewBoxMatch = text.match(/\bviewBox\s*=\s*["']([^"']+)["']/);
|
|
3208
|
+
let width = widthMatch ? parseFloat(widthMatch[1]) : null;
|
|
3209
|
+
let height = heightMatch ? parseFloat(heightMatch[1]) : null;
|
|
3210
|
+
if (width === null && height === null && viewBoxMatch) {
|
|
3211
|
+
const parts = viewBoxMatch[1].split(/[\s,]+/);
|
|
3212
|
+
if (parts.length >= 4) {
|
|
3213
|
+
width = parseFloat(parts[2]);
|
|
3214
|
+
height = parseFloat(parts[3]);
|
|
3215
|
+
}
|
|
3216
|
+
}
|
|
3217
|
+
return {
|
|
3218
|
+
format: "svg",
|
|
3219
|
+
width: width !== null && !isNaN(width) ? width : null,
|
|
3220
|
+
height: height !== null && !isNaN(height) ? height : null,
|
|
3221
|
+
fileSize: data.length,
|
|
3222
|
+
colorSpace: null,
|
|
3223
|
+
channels: null,
|
|
3224
|
+
depth: null,
|
|
3225
|
+
hasAlpha: null,
|
|
3226
|
+
density: null,
|
|
3227
|
+
exif: null
|
|
3228
|
+
};
|
|
3229
|
+
}
|
|
3230
|
+
const sharp = (await import("sharp")).default;
|
|
3231
|
+
const meta = await sharp(data).metadata();
|
|
3232
|
+
let exif = null;
|
|
3233
|
+
if (meta.exif) {
|
|
3234
|
+
try {
|
|
3235
|
+
exif = {};
|
|
3236
|
+
if (meta.orientation) exif["Orientation"] = String(meta.orientation);
|
|
3237
|
+
} catch {
|
|
3238
|
+
}
|
|
3239
|
+
}
|
|
3240
|
+
return {
|
|
3241
|
+
format: meta.format ?? ext.slice(1),
|
|
3242
|
+
width: meta.width ?? null,
|
|
3243
|
+
height: meta.height ?? null,
|
|
3244
|
+
fileSize: data.length,
|
|
3245
|
+
colorSpace: meta.space ?? null,
|
|
3246
|
+
channels: meta.channels ?? null,
|
|
3247
|
+
depth: meta.depth ?? null,
|
|
3248
|
+
hasAlpha: meta.hasAlpha ?? null,
|
|
3249
|
+
density: meta.density ?? null,
|
|
3250
|
+
exif
|
|
3251
|
+
};
|
|
3252
|
+
}
|
|
3253
|
+
function formatMetadataLines(meta) {
|
|
3254
|
+
const lines = [];
|
|
3255
|
+
lines.push(`Format: ${meta.format}`);
|
|
3256
|
+
if (meta.width !== null && meta.height !== null) {
|
|
3257
|
+
lines.push(`Dimensions: ${meta.width} \xD7 ${meta.height}`);
|
|
3258
|
+
}
|
|
3259
|
+
lines.push(`File size: ${formatBytes(meta.fileSize)}`);
|
|
3260
|
+
if (meta.colorSpace) lines.push(`Color space: ${meta.colorSpace}`);
|
|
3261
|
+
if (meta.channels !== null) lines.push(`Channels: ${meta.channels}`);
|
|
3262
|
+
if (meta.depth) lines.push(`Bit depth: ${meta.depth}`);
|
|
3263
|
+
if (meta.hasAlpha !== null) lines.push(`Alpha: ${meta.hasAlpha ? "yes" : "no"}`);
|
|
3264
|
+
if (meta.density !== null) lines.push(`Density: ${meta.density} DPI`);
|
|
3265
|
+
if (meta.exif) {
|
|
3266
|
+
for (const [key, value] of Object.entries(meta.exif)) {
|
|
3267
|
+
lines.push(`EXIF ${key}: ${value}`);
|
|
3268
|
+
}
|
|
3269
|
+
}
|
|
3270
|
+
return lines;
|
|
3271
|
+
}
|
|
3272
|
+
function formatBytes(bytes) {
|
|
3273
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
3274
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
3275
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
3276
|
+
}
|
|
3277
|
+
|
|
3055
3278
|
// src/outline/parser.ts
|
|
3056
3279
|
var BRACE_LANGS = /* @__PURE__ */ new Set([
|
|
3057
3280
|
"javascript",
|
|
@@ -3410,6 +3633,22 @@ apiRoutes.post("/review/reopen", async (c) => {
|
|
|
3410
3633
|
await updateReviewStatus(reviewId, "in_progress");
|
|
3411
3634
|
return c.json({ status: "in_progress" });
|
|
3412
3635
|
});
|
|
3636
|
+
apiRoutes.post("/review/refresh", async (c) => {
|
|
3637
|
+
const reviewId = resolveReviewId(c);
|
|
3638
|
+
const repoRoot = c.get("repoRoot");
|
|
3639
|
+
const review = await getReview(reviewId);
|
|
3640
|
+
if (!review) return c.json({ error: "Review not found" }, 404);
|
|
3641
|
+
const mode = parseModeString(review.mode);
|
|
3642
|
+
const headCommit = getHeadCommit(repoRoot);
|
|
3643
|
+
const diffs = getFileDiffs(mode, repoRoot);
|
|
3644
|
+
const result = await updateReviewDiffs(reviewId, diffs, headCommit);
|
|
3645
|
+
return c.json({
|
|
3646
|
+
updated: result.updated,
|
|
3647
|
+
added: result.added,
|
|
3648
|
+
stale: result.stale,
|
|
3649
|
+
fileCount: diffs.length
|
|
3650
|
+
});
|
|
3651
|
+
});
|
|
3413
3652
|
apiRoutes.delete("/review/:id", async (c) => {
|
|
3414
3653
|
const reviewId = c.req.param("id");
|
|
3415
3654
|
const currentReviewId = c.get("currentReviewId");
|
|
@@ -3548,7 +3787,7 @@ function readProjectSettings(repoRoot) {
|
|
|
3548
3787
|
const settingsPath = join5(repoRoot, ".glassbox", "settings.json");
|
|
3549
3788
|
try {
|
|
3550
3789
|
if (existsSync4(settingsPath)) {
|
|
3551
|
-
return JSON.parse(
|
|
3790
|
+
return JSON.parse(readFileSync6(settingsPath, "utf-8"));
|
|
3552
3791
|
}
|
|
3553
3792
|
} catch {
|
|
3554
3793
|
}
|
|
@@ -3571,6 +3810,47 @@ apiRoutes.patch("/project-settings", async (c) => {
|
|
|
3571
3810
|
writeProjectSettings(repoRoot, current);
|
|
3572
3811
|
return c.json(current);
|
|
3573
3812
|
});
|
|
3813
|
+
apiRoutes.get("/image/:fileId/metadata", async (c) => {
|
|
3814
|
+
const fileId = c.req.param("fileId");
|
|
3815
|
+
const file = await getReviewFile(fileId);
|
|
3816
|
+
if (!file) return c.json({ error: "Not found" }, 404);
|
|
3817
|
+
const repoRoot = c.get("repoRoot");
|
|
3818
|
+
const review = await getReview(file.review_id);
|
|
3819
|
+
if (!review) return c.json({ error: "Review not found" }, 404);
|
|
3820
|
+
const mode = parseModeString(review.mode);
|
|
3821
|
+
const diff = JSON.parse(file.diff_data ?? "{}");
|
|
3822
|
+
const oldPath = diff.oldPath ?? null;
|
|
3823
|
+
const status = diff.status ?? "modified";
|
|
3824
|
+
const oldImage = status !== "added" ? getOldImage(mode, file.file_path, oldPath, repoRoot) : null;
|
|
3825
|
+
const newImage = status !== "deleted" ? getNewImage(mode, file.file_path, repoRoot) : null;
|
|
3826
|
+
const [oldMeta, newMeta] = await Promise.all([
|
|
3827
|
+
oldImage ? extractMetadata(oldImage.data, oldPath ?? file.file_path) : null,
|
|
3828
|
+
newImage ? extractMetadata(newImage.data, file.file_path) : null
|
|
3829
|
+
]);
|
|
3830
|
+
return c.json({
|
|
3831
|
+
old: oldMeta ? formatMetadataLines(oldMeta) : null,
|
|
3832
|
+
new: newMeta ? formatMetadataLines(newMeta) : null
|
|
3833
|
+
});
|
|
3834
|
+
});
|
|
3835
|
+
apiRoutes.get("/image/:fileId/:side", async (c) => {
|
|
3836
|
+
const fileId = c.req.param("fileId");
|
|
3837
|
+
const side = c.req.param("side");
|
|
3838
|
+
if (side !== "old" && side !== "new") return c.text("Invalid side", 400);
|
|
3839
|
+
const file = await getReviewFile(fileId);
|
|
3840
|
+
if (!file) return c.text("Not found", 404);
|
|
3841
|
+
const repoRoot = c.get("repoRoot");
|
|
3842
|
+
const review = await getReview(file.review_id);
|
|
3843
|
+
if (!review) return c.text("Review not found", 404);
|
|
3844
|
+
const mode = parseModeString(review.mode);
|
|
3845
|
+
const diff = JSON.parse(file.diff_data ?? "{}");
|
|
3846
|
+
const oldPath = diff.oldPath ?? null;
|
|
3847
|
+
const image = side === "old" ? getOldImage(mode, file.file_path, oldPath, repoRoot) : getNewImage(mode, file.file_path, repoRoot);
|
|
3848
|
+
if (!image) return c.text("Image not available", 404);
|
|
3849
|
+
const contentType = getContentType(file.file_path);
|
|
3850
|
+
return new Response(image.data, {
|
|
3851
|
+
headers: { "Content-Type": contentType, "Cache-Control": "no-cache" }
|
|
3852
|
+
});
|
|
3853
|
+
});
|
|
3574
3854
|
|
|
3575
3855
|
// src/routes/pages.tsx
|
|
3576
3856
|
import { Hono as Hono3 } from "hono";
|
|
@@ -3644,6 +3924,52 @@ function jsx(tag, props) {
|
|
|
3644
3924
|
return new SafeHtml(`<${tag}${attrStr}>${childStr}</${tag}>`);
|
|
3645
3925
|
}
|
|
3646
3926
|
|
|
3927
|
+
// src/components/imageDiff.tsx
|
|
3928
|
+
function ImageDiff({ file, diff }) {
|
|
3929
|
+
const fileId = file.id;
|
|
3930
|
+
const isAdded = diff.status === "added";
|
|
3931
|
+
const isDeleted = diff.status === "deleted";
|
|
3932
|
+
const hasOld = !isAdded;
|
|
3933
|
+
const hasNew = !isDeleted;
|
|
3934
|
+
const hasComparison = hasOld && hasNew;
|
|
3935
|
+
return /* @__PURE__ */ jsx(
|
|
3936
|
+
"div",
|
|
3937
|
+
{
|
|
3938
|
+
className: "image-diff",
|
|
3939
|
+
"data-file-id": fileId,
|
|
3940
|
+
"data-file-path": file.file_path,
|
|
3941
|
+
"data-has-old": String(hasOld),
|
|
3942
|
+
"data-has-new": String(hasNew),
|
|
3943
|
+
children: [
|
|
3944
|
+
/* @__PURE__ */ jsx("div", { className: "image-diff-panel image-diff-metadata active", "data-panel": "metadata", children: /* @__PURE__ */ jsx("div", { className: "image-metadata-loading", children: "Loading metadata..." }) }),
|
|
3945
|
+
hasComparison && /* @__PURE__ */ jsx("div", { className: "image-diff-panel image-diff-visual", "data-panel": "difference", children: /* @__PURE__ */ jsx("div", { className: "image-visual-canvas", "data-zoomable": "true", children: /* @__PURE__ */ jsx("div", { className: "image-zoom-wrap", children: [
|
|
3946
|
+
/* @__PURE__ */ jsx("img", { className: "image-layer image-layer-old", src: `/api/image/${fileId}/old`, alt: "Old version" }),
|
|
3947
|
+
/* @__PURE__ */ jsx("img", { className: "image-layer image-layer-new image-blend", src: `/api/image/${fileId}/new`, alt: "New version" })
|
|
3948
|
+
] }) }) }),
|
|
3949
|
+
hasComparison && /* @__PURE__ */ jsx("div", { className: "image-diff-panel image-diff-visual", "data-panel": "slice", children: /* @__PURE__ */ jsx("div", { className: "image-visual-canvas", "data-zoomable": "true", children: [
|
|
3950
|
+
/* @__PURE__ */ jsx("div", { className: "image-zoom-wrap", children: [
|
|
3951
|
+
/* @__PURE__ */ jsx("img", { className: "image-layer image-layer-old", src: `/api/image/${fileId}/old`, alt: "Old version" }),
|
|
3952
|
+
/* @__PURE__ */ jsx("img", { className: "image-layer image-layer-new image-slice-clipped", src: `/api/image/${fileId}/new`, alt: "New version" })
|
|
3953
|
+
] }),
|
|
3954
|
+
/* @__PURE__ */ jsx("div", { className: "slice-line" }),
|
|
3955
|
+
/* @__PURE__ */ jsx("div", { className: "slice-handle slice-handle-a" }),
|
|
3956
|
+
/* @__PURE__ */ jsx("div", { className: "slice-handle slice-handle-b" })
|
|
3957
|
+
] }) }),
|
|
3958
|
+
!hasComparison && /* @__PURE__ */ jsx("div", { className: "image-diff-single", children: [
|
|
3959
|
+
/* @__PURE__ */ jsx(
|
|
3960
|
+
"img",
|
|
3961
|
+
{
|
|
3962
|
+
src: `/api/image/${fileId}/${isAdded ? "new" : "old"}`,
|
|
3963
|
+
alt: isAdded ? "New image" : "Deleted image"
|
|
3964
|
+
}
|
|
3965
|
+
),
|
|
3966
|
+
/* @__PURE__ */ jsx("p", { className: "image-diff-status", children: isAdded ? "New file" : "Deleted file" })
|
|
3967
|
+
] })
|
|
3968
|
+
]
|
|
3969
|
+
}
|
|
3970
|
+
);
|
|
3971
|
+
}
|
|
3972
|
+
|
|
3647
3973
|
// src/components/diffView.tsx
|
|
3648
3974
|
function DiffView({ file, diff, annotations, mode }) {
|
|
3649
3975
|
const annotationsByLine = {};
|
|
@@ -3657,7 +3983,7 @@ function DiffView({ file, diff, annotations, mode }) {
|
|
|
3657
3983
|
/* @__PURE__ */ jsx("span", { className: "file-path", children: diff.filePath }),
|
|
3658
3984
|
/* @__PURE__ */ jsx("div", { className: "diff-header-actions", children: /* @__PURE__ */ jsx("span", { className: `file-status ${diff.status}`, children: diff.status }) })
|
|
3659
3985
|
] }),
|
|
3660
|
-
diff.isBinary ? /* @__PURE__ */ jsx("div", { className: "hunk-separator", children: "Binary file" }) : diff.status === "added" || diff.status === "deleted" || mode === "unified" ? /* @__PURE__ */ jsx(UnifiedDiff, { hunks: diff.hunks, annotationsByLine }) : /* @__PURE__ */ jsx(SplitDiff, { hunks: diff.hunks, annotationsByLine })
|
|
3986
|
+
diff.isBinary && isImageFile(diff.filePath) ? /* @__PURE__ */ jsx(ImageDiff, { file, diff }) : diff.isBinary ? /* @__PURE__ */ jsx("div", { className: "hunk-separator", children: "Binary file" }) : diff.status === "added" || diff.status === "deleted" || mode === "unified" ? /* @__PURE__ */ jsx(UnifiedDiff, { hunks: diff.hunks, annotationsByLine }) : /* @__PURE__ */ jsx(SplitDiff, { hunks: diff.hunks, annotationsByLine })
|
|
3661
3987
|
] });
|
|
3662
3988
|
}
|
|
3663
3989
|
function getAnnotations(pair, annotationsByLine) {
|
|
@@ -4177,6 +4503,10 @@ function getHistoryScript() {
|
|
|
4177
4503
|
|
|
4178
4504
|
// src/routes/pages.tsx
|
|
4179
4505
|
init_queries();
|
|
4506
|
+
var zoomOutSvg = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="5" y1="12" x2="19" y2="12"/></svg>';
|
|
4507
|
+
var zoomInSvg = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>';
|
|
4508
|
+
var actualSizeSvg = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><text x="12" y="15.5" text-anchor="middle" font-size="9" font-weight="bold" fill="currentColor" stroke="none">1:1</text></svg>';
|
|
4509
|
+
var fitSvg = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6"/><path d="M9 21H3v-6"/><path d="M21 3l-7 7"/><path d="M3 21l7-7"/></svg>';
|
|
4180
4510
|
var pageRoutes = new Hono3();
|
|
4181
4511
|
pageRoutes.get("/", async (c) => {
|
|
4182
4512
|
const reviewId = c.get("reviewId");
|
|
@@ -4223,14 +4553,30 @@ pageRoutes.get("/", async (c) => {
|
|
|
4223
4553
|
] }),
|
|
4224
4554
|
/* @__PURE__ */ jsx("div", { className: "diff-container", id: "diff-container", style: "display:none" }),
|
|
4225
4555
|
/* @__PURE__ */ jsx("div", { className: "diff-toolbar", id: "diff-toolbar", style: "display:none", children: [
|
|
4226
|
-
/* @__PURE__ */ jsx("div", { className: "diff-toolbar-
|
|
4227
|
-
/* @__PURE__ */ jsx("div", { className: "
|
|
4228
|
-
/* @__PURE__ */ jsx("
|
|
4229
|
-
|
|
4556
|
+
/* @__PURE__ */ jsx("div", { className: "diff-toolbar-text", children: [
|
|
4557
|
+
/* @__PURE__ */ jsx("div", { className: "diff-toolbar-left", children: [
|
|
4558
|
+
/* @__PURE__ */ jsx("div", { className: "segmented-control", children: [
|
|
4559
|
+
/* @__PURE__ */ jsx("button", { className: "segment active", "data-diff-mode": "split", children: "Split" }),
|
|
4560
|
+
/* @__PURE__ */ jsx("button", { className: "segment", "data-diff-mode": "unified", children: "Unified" })
|
|
4561
|
+
] }),
|
|
4562
|
+
/* @__PURE__ */ jsx("button", { className: "toolbar-btn", id: "wrap-toggle", children: "Wrap" }),
|
|
4563
|
+
/* @__PURE__ */ jsx("button", { className: "toolbar-btn", id: "whitespace-toggle", children: "Ignore Whitespace" })
|
|
4230
4564
|
] }),
|
|
4231
|
-
/* @__PURE__ */ jsx("button", { className: "toolbar-btn", id: "
|
|
4565
|
+
/* @__PURE__ */ jsx("div", { className: "diff-toolbar-right", children: /* @__PURE__ */ jsx("button", { className: "toolbar-btn", id: "language-btn", children: "Plain Text" }) })
|
|
4232
4566
|
] }),
|
|
4233
|
-
/* @__PURE__ */ jsx("div", { className: "diff-toolbar-
|
|
4567
|
+
/* @__PURE__ */ jsx("div", { className: "diff-toolbar-image", style: "display:none", children: [
|
|
4568
|
+
/* @__PURE__ */ jsx("div", { className: "diff-toolbar-left", children: /* @__PURE__ */ jsx("div", { className: "segmented-control", children: [
|
|
4569
|
+
/* @__PURE__ */ jsx("button", { className: "segment active", "data-image-mode": "metadata", children: "Metadata" }),
|
|
4570
|
+
/* @__PURE__ */ jsx("button", { className: "segment", "data-image-mode": "difference", children: "Difference" }),
|
|
4571
|
+
/* @__PURE__ */ jsx("button", { className: "segment", "data-image-mode": "slice", children: "Slice" })
|
|
4572
|
+
] }) }),
|
|
4573
|
+
/* @__PURE__ */ jsx("div", { className: "diff-toolbar-right", children: [
|
|
4574
|
+
/* @__PURE__ */ jsx("button", { className: "image-zoom-btn", "data-zoom-action": "out", title: "Zoom out", children: raw(zoomOutSvg) }),
|
|
4575
|
+
/* @__PURE__ */ jsx("button", { className: "image-zoom-btn", "data-zoom-action": "fit", title: "Fit to view", children: raw(fitSvg) }),
|
|
4576
|
+
/* @__PURE__ */ jsx("button", { className: "image-zoom-btn", "data-zoom-action": "actual", title: "Actual size (1:1)", children: raw(actualSizeSvg) }),
|
|
4577
|
+
/* @__PURE__ */ jsx("button", { className: "image-zoom-btn", "data-zoom-action": "in", title: "Zoom in", children: raw(zoomInSvg) })
|
|
4578
|
+
] })
|
|
4579
|
+
] })
|
|
4234
4580
|
] })
|
|
4235
4581
|
] })
|
|
4236
4582
|
] }) });
|
|
@@ -4239,10 +4585,22 @@ pageRoutes.get("/", async (c) => {
|
|
|
4239
4585
|
pageRoutes.get("/file/:fileId", async (c) => {
|
|
4240
4586
|
const fileId = c.req.param("fileId");
|
|
4241
4587
|
const mode = c.req.query("mode") === "unified" ? "unified" : "split";
|
|
4588
|
+
const ignoreWhitespace = c.req.query("ignoreWhitespace") === "1";
|
|
4242
4589
|
const file = await getReviewFile(fileId);
|
|
4243
4590
|
if (!file) return c.text("File not found", 404);
|
|
4244
4591
|
const annotations = await getAnnotationsForFile(fileId);
|
|
4245
|
-
|
|
4592
|
+
let diff = JSON.parse(file.diff_data ?? "{}");
|
|
4593
|
+
if (ignoreWhitespace) {
|
|
4594
|
+
const repoRoot = c.get("repoRoot");
|
|
4595
|
+
const review = await getReview(file.review_id);
|
|
4596
|
+
if (review) {
|
|
4597
|
+
const reviewMode = parseModeString(review.mode);
|
|
4598
|
+
const regenerated = getSingleFileDiff(reviewMode, file.file_path, repoRoot, "-w");
|
|
4599
|
+
if (regenerated) {
|
|
4600
|
+
diff = regenerated;
|
|
4601
|
+
}
|
|
4602
|
+
}
|
|
4603
|
+
}
|
|
4246
4604
|
const html = /* @__PURE__ */ jsx(DiffView, { file, diff, annotations, mode });
|
|
4247
4605
|
return c.html(html.toString());
|
|
4248
4606
|
});
|
|
@@ -4296,14 +4654,30 @@ pageRoutes.get("/review/:reviewId", async (c) => {
|
|
|
4296
4654
|
] }),
|
|
4297
4655
|
/* @__PURE__ */ jsx("div", { className: "diff-container", id: "diff-container", style: "display:none" }),
|
|
4298
4656
|
/* @__PURE__ */ jsx("div", { className: "diff-toolbar", id: "diff-toolbar", style: "display:none", children: [
|
|
4299
|
-
/* @__PURE__ */ jsx("div", { className: "diff-toolbar-
|
|
4300
|
-
/* @__PURE__ */ jsx("div", { className: "
|
|
4301
|
-
/* @__PURE__ */ jsx("
|
|
4302
|
-
|
|
4657
|
+
/* @__PURE__ */ jsx("div", { className: "diff-toolbar-text", children: [
|
|
4658
|
+
/* @__PURE__ */ jsx("div", { className: "diff-toolbar-left", children: [
|
|
4659
|
+
/* @__PURE__ */ jsx("div", { className: "segmented-control", children: [
|
|
4660
|
+
/* @__PURE__ */ jsx("button", { className: "segment active", "data-diff-mode": "split", children: "Split" }),
|
|
4661
|
+
/* @__PURE__ */ jsx("button", { className: "segment", "data-diff-mode": "unified", children: "Unified" })
|
|
4662
|
+
] }),
|
|
4663
|
+
/* @__PURE__ */ jsx("button", { className: "toolbar-btn", id: "wrap-toggle", children: "Wrap" }),
|
|
4664
|
+
/* @__PURE__ */ jsx("button", { className: "toolbar-btn", id: "whitespace-toggle", children: "Ignore Whitespace" })
|
|
4303
4665
|
] }),
|
|
4304
|
-
/* @__PURE__ */ jsx("button", { className: "toolbar-btn", id: "
|
|
4666
|
+
/* @__PURE__ */ jsx("div", { className: "diff-toolbar-right", children: /* @__PURE__ */ jsx("button", { className: "toolbar-btn", id: "language-btn", children: "Plain Text" }) })
|
|
4305
4667
|
] }),
|
|
4306
|
-
/* @__PURE__ */ jsx("div", { className: "diff-toolbar-
|
|
4668
|
+
/* @__PURE__ */ jsx("div", { className: "diff-toolbar-image", style: "display:none", children: [
|
|
4669
|
+
/* @__PURE__ */ jsx("div", { className: "diff-toolbar-left", children: /* @__PURE__ */ jsx("div", { className: "segmented-control", children: [
|
|
4670
|
+
/* @__PURE__ */ jsx("button", { className: "segment active", "data-image-mode": "metadata", children: "Metadata" }),
|
|
4671
|
+
/* @__PURE__ */ jsx("button", { className: "segment", "data-image-mode": "difference", children: "Difference" }),
|
|
4672
|
+
/* @__PURE__ */ jsx("button", { className: "segment", "data-image-mode": "slice", children: "Slice" })
|
|
4673
|
+
] }) }),
|
|
4674
|
+
/* @__PURE__ */ jsx("div", { className: "diff-toolbar-right", children: [
|
|
4675
|
+
/* @__PURE__ */ jsx("button", { className: "image-zoom-btn", "data-zoom-action": "out", title: "Zoom out", children: raw(zoomOutSvg) }),
|
|
4676
|
+
/* @__PURE__ */ jsx("button", { className: "image-zoom-btn", "data-zoom-action": "fit", title: "Fit to view", children: raw(fitSvg) }),
|
|
4677
|
+
/* @__PURE__ */ jsx("button", { className: "image-zoom-btn", "data-zoom-action": "actual", title: "Actual size (1:1)", children: raw(actualSizeSvg) }),
|
|
4678
|
+
/* @__PURE__ */ jsx("button", { className: "image-zoom-btn", "data-zoom-action": "in", title: "Zoom in", children: raw(zoomInSvg) })
|
|
4679
|
+
] })
|
|
4680
|
+
] })
|
|
4307
4681
|
] })
|
|
4308
4682
|
] })
|
|
4309
4683
|
] }) });
|
|
@@ -4319,10 +4693,10 @@ pageRoutes.get("/history", async (c) => {
|
|
|
4319
4693
|
|
|
4320
4694
|
// src/server.ts
|
|
4321
4695
|
function tryServe(fetch2, port) {
|
|
4322
|
-
return new Promise((
|
|
4696
|
+
return new Promise((resolve4, reject) => {
|
|
4323
4697
|
const server = serve({ fetch: fetch2, port });
|
|
4324
4698
|
server.on("listening", () => {
|
|
4325
|
-
|
|
4699
|
+
resolve4(port);
|
|
4326
4700
|
});
|
|
4327
4701
|
server.on("error", (err) => {
|
|
4328
4702
|
if (err.code === "EADDRINUSE") {
|
|
@@ -4344,11 +4718,11 @@ async function startServer(port, reviewId, repoRoot, options) {
|
|
|
4344
4718
|
const selfDir = dirname(fileURLToPath(import.meta.url));
|
|
4345
4719
|
const distDir = existsSync5(join6(selfDir, "client", "styles.css")) ? join6(selfDir, "client") : join6(selfDir, "..", "dist", "client");
|
|
4346
4720
|
app.get("/static/styles.css", (c) => {
|
|
4347
|
-
const css =
|
|
4721
|
+
const css = readFileSync7(join6(distDir, "styles.css"), "utf-8");
|
|
4348
4722
|
return c.text(css, 200, { "Content-Type": "text/css", "Cache-Control": "no-cache" });
|
|
4349
4723
|
});
|
|
4350
4724
|
app.get("/static/app.js", (c) => {
|
|
4351
|
-
const js =
|
|
4725
|
+
const js = readFileSync7(join6(distDir, "app.global.js"), "utf-8");
|
|
4352
4726
|
return c.text(js, 200, { "Content-Type": "application/javascript", "Cache-Control": "no-cache" });
|
|
4353
4727
|
});
|
|
4354
4728
|
app.route("/api", apiRoutes);
|
|
@@ -4384,7 +4758,7 @@ async function startServer(port, reviewId, repoRoot, options) {
|
|
|
4384
4758
|
}
|
|
4385
4759
|
|
|
4386
4760
|
// src/skills.ts
|
|
4387
|
-
import { existsSync as existsSync6, mkdirSync as mkdirSync5, readFileSync as
|
|
4761
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "fs";
|
|
4388
4762
|
import { join as join7 } from "path";
|
|
4389
4763
|
var SKILL_VERSION = 1;
|
|
4390
4764
|
function versionHeader() {
|
|
@@ -4397,7 +4771,7 @@ function parseVersionHeader(content) {
|
|
|
4397
4771
|
}
|
|
4398
4772
|
function updateFile(path, content) {
|
|
4399
4773
|
if (existsSync6(path)) {
|
|
4400
|
-
const existing =
|
|
4774
|
+
const existing = readFileSync8(path, "utf-8");
|
|
4401
4775
|
const version = parseVersionHeader(existing);
|
|
4402
4776
|
if (version !== null && version >= SKILL_VERSION) {
|
|
4403
4777
|
return false;
|
|
@@ -4501,7 +4875,7 @@ function ensureSkills() {
|
|
|
4501
4875
|
}
|
|
4502
4876
|
|
|
4503
4877
|
// src/update-check.ts
|
|
4504
|
-
import { existsSync as existsSync7, mkdirSync as mkdirSync6, readFileSync as
|
|
4878
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync6, readFileSync as readFileSync9, writeFileSync as writeFileSync6 } from "fs";
|
|
4505
4879
|
import { get } from "https";
|
|
4506
4880
|
import { homedir as homedir3 } from "os";
|
|
4507
4881
|
import { dirname as dirname2, join as join8 } from "path";
|
|
@@ -4512,7 +4886,7 @@ var PACKAGE_NAME = "glassbox";
|
|
|
4512
4886
|
function getCurrentVersion() {
|
|
4513
4887
|
try {
|
|
4514
4888
|
const dir = dirname2(fileURLToPath2(import.meta.url));
|
|
4515
|
-
const pkg = JSON.parse(
|
|
4889
|
+
const pkg = JSON.parse(readFileSync9(join8(dir, "..", "package.json"), "utf-8"));
|
|
4516
4890
|
return pkg.version;
|
|
4517
4891
|
} catch {
|
|
4518
4892
|
return "0.0.0";
|
|
@@ -4521,7 +4895,7 @@ function getCurrentVersion() {
|
|
|
4521
4895
|
function getLastCheckDate() {
|
|
4522
4896
|
try {
|
|
4523
4897
|
if (existsSync7(CHECK_FILE)) {
|
|
4524
|
-
return
|
|
4898
|
+
return readFileSync9(CHECK_FILE, "utf-8").trim();
|
|
4525
4899
|
}
|
|
4526
4900
|
} catch {
|
|
4527
4901
|
}
|
|
@@ -4538,10 +4912,10 @@ function isFirstUseToday() {
|
|
|
4538
4912
|
return last !== today;
|
|
4539
4913
|
}
|
|
4540
4914
|
function fetchLatestVersion() {
|
|
4541
|
-
return new Promise((
|
|
4915
|
+
return new Promise((resolve4) => {
|
|
4542
4916
|
const req = get(`https://registry.npmjs.org/${PACKAGE_NAME}/latest`, { timeout: 5e3 }, (res) => {
|
|
4543
4917
|
if (res.statusCode !== 200) {
|
|
4544
|
-
|
|
4918
|
+
resolve4(null);
|
|
4545
4919
|
return;
|
|
4546
4920
|
}
|
|
4547
4921
|
let data = "";
|
|
@@ -4550,18 +4924,18 @@ function fetchLatestVersion() {
|
|
|
4550
4924
|
});
|
|
4551
4925
|
res.on("end", () => {
|
|
4552
4926
|
try {
|
|
4553
|
-
|
|
4927
|
+
resolve4(JSON.parse(data).version);
|
|
4554
4928
|
} catch {
|
|
4555
|
-
|
|
4929
|
+
resolve4(null);
|
|
4556
4930
|
}
|
|
4557
4931
|
});
|
|
4558
4932
|
});
|
|
4559
4933
|
req.on("error", () => {
|
|
4560
|
-
|
|
4934
|
+
resolve4(null);
|
|
4561
4935
|
});
|
|
4562
4936
|
req.on("timeout", () => {
|
|
4563
4937
|
req.destroy();
|
|
4564
|
-
|
|
4938
|
+
resolve4(null);
|
|
4565
4939
|
});
|
|
4566
4940
|
});
|
|
4567
4941
|
}
|
|
@@ -4699,7 +5073,7 @@ function parseArgs(argv) {
|
|
|
4699
5073
|
port = parseInt(args[++i], 10);
|
|
4700
5074
|
break;
|
|
4701
5075
|
case "--data-dir":
|
|
4702
|
-
dataDir =
|
|
5076
|
+
dataDir = resolve3(args[++i]);
|
|
4703
5077
|
break;
|
|
4704
5078
|
case "--resume":
|
|
4705
5079
|
resume = true;
|
|
@@ -4755,7 +5129,7 @@ async function main() {
|
|
|
4755
5129
|
console.log("AI service test mode enabled \u2014 using mock AI responses");
|
|
4756
5130
|
}
|
|
4757
5131
|
if (debug) {
|
|
4758
|
-
console.log(`[debug] Build timestamp: ${"2026-03-
|
|
5132
|
+
console.log(`[debug] Build timestamp: ${"2026-03-16T07:44:26.425Z"}`);
|
|
4759
5133
|
}
|
|
4760
5134
|
if (projectDir) {
|
|
4761
5135
|
process.chdir(projectDir);
|