glassbox 0.7.0 → 0.7.2
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 +145 -11
- package/dist/client/app.global.js +8 -8
- package/package.json +2 -1
package/dist/cli.js
CHANGED
|
@@ -393,7 +393,7 @@ var init_queries = __esm({
|
|
|
393
393
|
// src/cli.ts
|
|
394
394
|
init_connection();
|
|
395
395
|
init_queries();
|
|
396
|
-
import { mkdirSync as mkdirSync8 } from "fs";
|
|
396
|
+
import { mkdirSync as mkdirSync8, realpathSync } from "fs";
|
|
397
397
|
import { tmpdir } from "os";
|
|
398
398
|
import { join as join11, resolve as resolve5 } from "path";
|
|
399
399
|
|
|
@@ -2566,6 +2566,11 @@ async function mockGuidedAnalysisBatch(files) {
|
|
|
2566
2566
|
// src/routes/ai-analysis.ts
|
|
2567
2567
|
init_queries();
|
|
2568
2568
|
var aiAnalysisRoutes = new Hono();
|
|
2569
|
+
var VALID_SORT_MODES = ["folder", "risk", "narrative", "guided"];
|
|
2570
|
+
var VALID_RISK_DIMENSIONS = ["aggregate", "security", "correctness", "error-handling", "maintainability", "architecture", "performance"];
|
|
2571
|
+
var VALID_SVG_VIEW_MODES = ["code", "rendered"];
|
|
2572
|
+
var VALID_IMAGE_MODES = ["metadata", "side-by-side", "difference", "slice"];
|
|
2573
|
+
var VALID_ANALYSIS_TYPES = ["risk", "narrative", "guided"];
|
|
2569
2574
|
var cancelledAnalyses = /* @__PURE__ */ new Set();
|
|
2570
2575
|
aiAnalysisRoutes.post("/analyze", async (c) => {
|
|
2571
2576
|
const reviewId = c.req.query("reviewId") ?? "";
|
|
@@ -2574,8 +2579,8 @@ aiAnalysisRoutes.post("/analyze", async (c) => {
|
|
|
2574
2579
|
const analysisType = body.type;
|
|
2575
2580
|
const invalidateCache = body.invalidateCache === true;
|
|
2576
2581
|
debugLog(`POST /analyze: type=${analysisType}, reviewId=${reviewId}`);
|
|
2577
|
-
if (analysisType
|
|
2578
|
-
return c.json({ error:
|
|
2582
|
+
if (!VALID_ANALYSIS_TYPES.includes(analysisType)) {
|
|
2583
|
+
return c.json({ error: `type must be one of: ${VALID_ANALYSIS_TYPES.join(", ")}` }, 400);
|
|
2579
2584
|
}
|
|
2580
2585
|
const testMode = isAIServiceTest();
|
|
2581
2586
|
const config = loadAIConfig();
|
|
@@ -2822,6 +2827,9 @@ async function runBatchedGuidedAnalysis(analysisId, batches, allFiles, config, r
|
|
|
2822
2827
|
aiAnalysisRoutes.get("/analysis/:type", async (c) => {
|
|
2823
2828
|
const reviewId = c.req.query("reviewId") ?? "";
|
|
2824
2829
|
const analysisType = c.req.param("type");
|
|
2830
|
+
if (!VALID_ANALYSIS_TYPES.includes(analysisType)) {
|
|
2831
|
+
return c.json({ error: `type must be one of: ${VALID_ANALYSIS_TYPES.join(", ")}` }, 400);
|
|
2832
|
+
}
|
|
2825
2833
|
const analysis = await getLatestAnalysis(reviewId, analysisType);
|
|
2826
2834
|
if (analysis === void 0) {
|
|
2827
2835
|
debugLog(`GET /analysis/${analysisType}: no analysis found`);
|
|
@@ -2854,6 +2862,9 @@ aiAnalysisRoutes.get("/analysis/:type", async (c) => {
|
|
|
2854
2862
|
aiAnalysisRoutes.get("/analysis/:type/status", async (c) => {
|
|
2855
2863
|
const reviewId = c.req.query("reviewId") ?? "";
|
|
2856
2864
|
const analysisType = c.req.param("type");
|
|
2865
|
+
if (!VALID_ANALYSIS_TYPES.includes(analysisType)) {
|
|
2866
|
+
return c.json({ error: `type must be one of: ${VALID_ANALYSIS_TYPES.join(", ")}` }, 400);
|
|
2867
|
+
}
|
|
2857
2868
|
const analysis = await getLatestAnalysis(reviewId, analysisType);
|
|
2858
2869
|
if (analysis === void 0) {
|
|
2859
2870
|
debugLog(`GET /analysis/${analysisType}/status: no analysis found`);
|
|
@@ -2881,6 +2892,9 @@ aiAnalysisRoutes.get("/debug-status", (c) => {
|
|
|
2881
2892
|
aiAnalysisRoutes.post("/debug-log", async (c) => {
|
|
2882
2893
|
if (!isDebug()) return c.json({ ok: true });
|
|
2883
2894
|
const body = await c.req.json();
|
|
2895
|
+
if (typeof body.message !== "string") {
|
|
2896
|
+
return c.json({ error: "message must be a string" }, 400);
|
|
2897
|
+
}
|
|
2884
2898
|
debugLog(`[client] ${body.message}`);
|
|
2885
2899
|
return c.json({ ok: true });
|
|
2886
2900
|
});
|
|
@@ -2890,6 +2904,24 @@ aiAnalysisRoutes.get("/preferences", async (c) => {
|
|
|
2890
2904
|
});
|
|
2891
2905
|
aiAnalysisRoutes.post("/preferences", async (c) => {
|
|
2892
2906
|
const body = await c.req.json();
|
|
2907
|
+
if (body.sort_mode !== void 0 && !VALID_SORT_MODES.includes(body.sort_mode)) {
|
|
2908
|
+
return c.json({ error: `sort_mode must be one of: ${VALID_SORT_MODES.join(", ")}` }, 400);
|
|
2909
|
+
}
|
|
2910
|
+
if (body.risk_sort_dimension !== void 0 && !VALID_RISK_DIMENSIONS.includes(body.risk_sort_dimension)) {
|
|
2911
|
+
return c.json({ error: `risk_sort_dimension must be one of: ${VALID_RISK_DIMENSIONS.join(", ")}` }, 400);
|
|
2912
|
+
}
|
|
2913
|
+
if (body.show_risk_scores !== void 0 && typeof body.show_risk_scores !== "boolean") {
|
|
2914
|
+
return c.json({ error: "show_risk_scores must be a boolean" }, 400);
|
|
2915
|
+
}
|
|
2916
|
+
if (body.ignore_whitespace !== void 0 && typeof body.ignore_whitespace !== "boolean") {
|
|
2917
|
+
return c.json({ error: "ignore_whitespace must be a boolean" }, 400);
|
|
2918
|
+
}
|
|
2919
|
+
if (body.svg_view_mode !== void 0 && !VALID_SVG_VIEW_MODES.includes(body.svg_view_mode)) {
|
|
2920
|
+
return c.json({ error: `svg_view_mode must be one of: ${VALID_SVG_VIEW_MODES.join(", ")}` }, 400);
|
|
2921
|
+
}
|
|
2922
|
+
if (body.last_image_mode !== void 0 && !VALID_IMAGE_MODES.includes(body.last_image_mode)) {
|
|
2923
|
+
return c.json({ error: `last_image_mode must be one of: ${VALID_IMAGE_MODES.join(", ")}` }, 400);
|
|
2924
|
+
}
|
|
2893
2925
|
await saveUserPreferences(body);
|
|
2894
2926
|
return c.json({ ok: true });
|
|
2895
2927
|
});
|
|
@@ -2897,6 +2929,8 @@ aiAnalysisRoutes.post("/preferences", async (c) => {
|
|
|
2897
2929
|
// src/routes/ai-config.ts
|
|
2898
2930
|
import { Hono as Hono2 } from "hono";
|
|
2899
2931
|
var aiConfigRoutes = new Hono2();
|
|
2932
|
+
var VALID_PLATFORMS = ["anthropic", "openai", "google"];
|
|
2933
|
+
var VALID_KEY_STORAGES = ["keychain", "config"];
|
|
2900
2934
|
aiConfigRoutes.get("/config", (c) => {
|
|
2901
2935
|
const config = loadAIConfig();
|
|
2902
2936
|
return c.json({
|
|
@@ -2909,6 +2943,25 @@ aiConfigRoutes.get("/config", (c) => {
|
|
|
2909
2943
|
});
|
|
2910
2944
|
aiConfigRoutes.post("/config", async (c) => {
|
|
2911
2945
|
const body = await c.req.json();
|
|
2946
|
+
if (!VALID_PLATFORMS.includes(body.platform)) {
|
|
2947
|
+
return c.json({ error: `platform must be one of: ${VALID_PLATFORMS.join(", ")}` }, 400);
|
|
2948
|
+
}
|
|
2949
|
+
if (typeof body.model !== "string" || body.model.trim() === "") {
|
|
2950
|
+
return c.json({ error: "model must be a non-empty string" }, 400);
|
|
2951
|
+
}
|
|
2952
|
+
if (body.guidedReview !== void 0) {
|
|
2953
|
+
const gr = body.guidedReview;
|
|
2954
|
+
if (typeof gr !== "object" || gr === null || Array.isArray(gr)) {
|
|
2955
|
+
return c.json({ error: "guidedReview must be an object" }, 400);
|
|
2956
|
+
}
|
|
2957
|
+
const grObj = gr;
|
|
2958
|
+
if (typeof grObj.enabled !== "boolean") {
|
|
2959
|
+
return c.json({ error: "guidedReview.enabled must be a boolean" }, 400);
|
|
2960
|
+
}
|
|
2961
|
+
if (!Array.isArray(grObj.topics) || !grObj.topics.every((t) => typeof t === "string")) {
|
|
2962
|
+
return c.json({ error: "guidedReview.topics must be an array of strings" }, 400);
|
|
2963
|
+
}
|
|
2964
|
+
}
|
|
2912
2965
|
saveAIConfigPreferences(body.platform, body.model);
|
|
2913
2966
|
if (body.guidedReview !== void 0) {
|
|
2914
2967
|
saveGuidedReviewConfig(body.guidedReview);
|
|
@@ -2937,6 +2990,15 @@ aiConfigRoutes.get("/key-status", (c) => {
|
|
|
2937
2990
|
});
|
|
2938
2991
|
aiConfigRoutes.post("/key", async (c) => {
|
|
2939
2992
|
const body = await c.req.json();
|
|
2993
|
+
if (!VALID_PLATFORMS.includes(body.platform)) {
|
|
2994
|
+
return c.json({ error: `platform must be one of: ${VALID_PLATFORMS.join(", ")}` }, 400);
|
|
2995
|
+
}
|
|
2996
|
+
if (typeof body.key !== "string" || body.key.trim() === "") {
|
|
2997
|
+
return c.json({ error: "key must be a non-empty string" }, 400);
|
|
2998
|
+
}
|
|
2999
|
+
if (!VALID_KEY_STORAGES.includes(body.storage)) {
|
|
3000
|
+
return c.json({ error: `storage must be one of: ${VALID_KEY_STORAGES.join(", ")}` }, 400);
|
|
3001
|
+
}
|
|
2940
3002
|
saveAPIKey(
|
|
2941
3003
|
body.platform,
|
|
2942
3004
|
body.key,
|
|
@@ -2946,6 +3008,9 @@ aiConfigRoutes.post("/key", async (c) => {
|
|
|
2946
3008
|
});
|
|
2947
3009
|
aiConfigRoutes.delete("/key", (c) => {
|
|
2948
3010
|
const platform = c.req.query("platform") ?? "anthropic";
|
|
3011
|
+
if (!VALID_PLATFORMS.includes(platform)) {
|
|
3012
|
+
return c.json({ error: `platform must be one of: ${VALID_PLATFORMS.join(", ")}` }, 400);
|
|
3013
|
+
}
|
|
2949
3014
|
deleteAPIKey(platform);
|
|
2950
3015
|
return c.json({ ok: true });
|
|
2951
3016
|
});
|
|
@@ -3871,6 +3936,9 @@ function pushIndentSymbol(root, stack, sym, indent, lines, lineIdx) {
|
|
|
3871
3936
|
|
|
3872
3937
|
// src/routes/api.ts
|
|
3873
3938
|
var apiRoutes = new Hono4();
|
|
3939
|
+
var VALID_CATEGORIES = ["bug", "fix", "style", "pattern-follow", "pattern-avoid", "note", "remember"];
|
|
3940
|
+
var VALID_SIDES = ["old", "new"];
|
|
3941
|
+
var VALID_FILE_STATUSES = ["pending", "reviewed"];
|
|
3874
3942
|
function resolveReviewId(c) {
|
|
3875
3943
|
return c.req.query("reviewId") ?? c.get("reviewId");
|
|
3876
3944
|
}
|
|
@@ -3977,6 +4045,9 @@ apiRoutes.get("/files/:fileId", async (c) => {
|
|
|
3977
4045
|
});
|
|
3978
4046
|
apiRoutes.patch("/files/:fileId/status", async (c) => {
|
|
3979
4047
|
const { status } = await c.req.json();
|
|
4048
|
+
if (!VALID_FILE_STATUSES.includes(status)) {
|
|
4049
|
+
return c.json({ error: `status must be one of: ${VALID_FILE_STATUSES.join(", ")}` }, 400);
|
|
4050
|
+
}
|
|
3980
4051
|
await updateFileStatus(c.req.param("fileId"), status);
|
|
3981
4052
|
return c.json({ ok: true });
|
|
3982
4053
|
});
|
|
@@ -4002,6 +4073,21 @@ function autoExport(c) {
|
|
|
4002
4073
|
}
|
|
4003
4074
|
apiRoutes.post("/annotations", async (c) => {
|
|
4004
4075
|
const body = await c.req.json();
|
|
4076
|
+
if (typeof body.reviewFileId !== "string" || body.reviewFileId === "") {
|
|
4077
|
+
return c.json({ error: "reviewFileId must be a non-empty string" }, 400);
|
|
4078
|
+
}
|
|
4079
|
+
if (typeof body.lineNumber !== "number" || !Number.isInteger(body.lineNumber) || body.lineNumber < 1) {
|
|
4080
|
+
return c.json({ error: "lineNumber must be a positive integer" }, 400);
|
|
4081
|
+
}
|
|
4082
|
+
if (!VALID_SIDES.includes(body.side)) {
|
|
4083
|
+
return c.json({ error: `side must be one of: ${VALID_SIDES.join(", ")}` }, 400);
|
|
4084
|
+
}
|
|
4085
|
+
if (!VALID_CATEGORIES.includes(body.category)) {
|
|
4086
|
+
return c.json({ error: `category must be one of: ${VALID_CATEGORIES.join(", ")}` }, 400);
|
|
4087
|
+
}
|
|
4088
|
+
if (typeof body.content !== "string" || body.content.trim() === "") {
|
|
4089
|
+
return c.json({ error: "content must be a non-empty string" }, 400);
|
|
4090
|
+
}
|
|
4005
4091
|
const annotation = await addAnnotation(
|
|
4006
4092
|
body.reviewFileId,
|
|
4007
4093
|
body.lineNumber,
|
|
@@ -4014,6 +4100,12 @@ apiRoutes.post("/annotations", async (c) => {
|
|
|
4014
4100
|
});
|
|
4015
4101
|
apiRoutes.patch("/annotations/:id", async (c) => {
|
|
4016
4102
|
const { content, category } = await c.req.json();
|
|
4103
|
+
if (typeof content !== "string" || content.trim() === "") {
|
|
4104
|
+
return c.json({ error: "content must be a non-empty string" }, 400);
|
|
4105
|
+
}
|
|
4106
|
+
if (!VALID_CATEGORIES.includes(category)) {
|
|
4107
|
+
return c.json({ error: `category must be one of: ${VALID_CATEGORIES.join(", ")}` }, 400);
|
|
4108
|
+
}
|
|
4017
4109
|
await updateAnnotation(c.req.param("id"), content, category);
|
|
4018
4110
|
autoExport(c);
|
|
4019
4111
|
return c.json({ ok: true });
|
|
@@ -4025,6 +4117,12 @@ apiRoutes.delete("/annotations/:id", async (c) => {
|
|
|
4025
4117
|
});
|
|
4026
4118
|
apiRoutes.patch("/annotations/:id/move", async (c) => {
|
|
4027
4119
|
const { lineNumber, side } = await c.req.json();
|
|
4120
|
+
if (typeof lineNumber !== "number" || !Number.isInteger(lineNumber) || lineNumber < 1) {
|
|
4121
|
+
return c.json({ error: "lineNumber must be a positive integer" }, 400);
|
|
4122
|
+
}
|
|
4123
|
+
if (!VALID_SIDES.includes(side)) {
|
|
4124
|
+
return c.json({ error: `side must be one of: ${VALID_SIDES.join(", ")}` }, 400);
|
|
4125
|
+
}
|
|
4028
4126
|
await moveAnnotation(c.req.param("id"), lineNumber, side);
|
|
4029
4127
|
autoExport(c);
|
|
4030
4128
|
return c.json({ ok: true });
|
|
@@ -4173,6 +4271,9 @@ apiRoutes.get("/project-settings", (c) => {
|
|
|
4173
4271
|
apiRoutes.patch("/project-settings", async (c) => {
|
|
4174
4272
|
const repoRoot = c.get("repoRoot");
|
|
4175
4273
|
const body = await c.req.json();
|
|
4274
|
+
if (body.appName !== void 0 && typeof body.appName !== "string") {
|
|
4275
|
+
return c.json({ error: "appName must be a string" }, 400);
|
|
4276
|
+
}
|
|
4176
4277
|
const current = readProjectSettings(repoRoot);
|
|
4177
4278
|
if (body.appName !== void 0) current.appName = body.appName || void 0;
|
|
4178
4279
|
writeProjectSettings(repoRoot, current);
|
|
@@ -4215,7 +4316,7 @@ apiRoutes.get("/image/:fileId/:side", async (c) => {
|
|
|
4215
4316
|
if (isSvgFile(file.file_path)) {
|
|
4216
4317
|
try {
|
|
4217
4318
|
const png = await rasterizeSvg(image.data);
|
|
4218
|
-
return new Response(png, {
|
|
4319
|
+
return new Response(new Uint8Array(png), {
|
|
4219
4320
|
headers: { "Content-Type": "image/png", "Cache-Control": "no-cache" }
|
|
4220
4321
|
});
|
|
4221
4322
|
} catch {
|
|
@@ -4223,7 +4324,7 @@ apiRoutes.get("/image/:fileId/:side", async (c) => {
|
|
|
4223
4324
|
}
|
|
4224
4325
|
}
|
|
4225
4326
|
const contentType = getContentType(file.file_path);
|
|
4226
|
-
return new Response(image.data, {
|
|
4327
|
+
return new Response(new Uint8Array(image.data), {
|
|
4227
4328
|
headers: { "Content-Type": contentType, "Cache-Control": "no-cache" }
|
|
4228
4329
|
});
|
|
4229
4330
|
});
|
|
@@ -5938,6 +6039,21 @@ pageRoutes.get("/history", async (c) => {
|
|
|
5938
6039
|
// src/routes/theme-api.ts
|
|
5939
6040
|
import { Hono as Hono6 } from "hono";
|
|
5940
6041
|
var themeApiRoutes = new Hono6();
|
|
6042
|
+
function validateColors(colors) {
|
|
6043
|
+
if (typeof colors !== "object" || colors === null || Array.isArray(colors)) {
|
|
6044
|
+
return "colors must be an object";
|
|
6045
|
+
}
|
|
6046
|
+
const validKeys = new Set(THEME_VARIABLES);
|
|
6047
|
+
for (const [key, value] of Object.entries(colors)) {
|
|
6048
|
+
if (!validKeys.has(key)) {
|
|
6049
|
+
return `colors contains unknown key: ${key}`;
|
|
6050
|
+
}
|
|
6051
|
+
if (typeof value !== "string") {
|
|
6052
|
+
return `colors.${key} must be a string`;
|
|
6053
|
+
}
|
|
6054
|
+
}
|
|
6055
|
+
return null;
|
|
6056
|
+
}
|
|
5941
6057
|
themeApiRoutes.get("/", (c) => {
|
|
5942
6058
|
const themes = getAllThemes();
|
|
5943
6059
|
const activeId = getActiveThemeId();
|
|
@@ -5958,7 +6074,7 @@ themeApiRoutes.get("/active", (c) => {
|
|
|
5958
6074
|
});
|
|
5959
6075
|
themeApiRoutes.post("/active", async (c) => {
|
|
5960
6076
|
const body = await c.req.json();
|
|
5961
|
-
if (
|
|
6077
|
+
if (typeof body.id !== "string" || body.id === "") return c.json({ error: "id must be a non-empty string" }, 400);
|
|
5962
6078
|
const theme = resolveTheme(body.id);
|
|
5963
6079
|
if (!theme) return c.json({ error: "Theme not found" }, 404);
|
|
5964
6080
|
setActiveThemeId(body.id);
|
|
@@ -5966,7 +6082,10 @@ themeApiRoutes.post("/active", async (c) => {
|
|
|
5966
6082
|
});
|
|
5967
6083
|
themeApiRoutes.post("/", async (c) => {
|
|
5968
6084
|
const body = await c.req.json();
|
|
5969
|
-
if (
|
|
6085
|
+
if (typeof body.sourceId !== "string" || body.sourceId === "") return c.json({ error: "sourceId must be a non-empty string" }, 400);
|
|
6086
|
+
if (body.name !== void 0 && (typeof body.name !== "string" || body.name.trim() === "")) {
|
|
6087
|
+
return c.json({ error: "name must be a non-empty string when provided" }, 400);
|
|
6088
|
+
}
|
|
5970
6089
|
const source = resolveTheme(body.sourceId);
|
|
5971
6090
|
if (!source) return c.json({ error: "Source theme not found" }, 404);
|
|
5972
6091
|
const baseTheme = source.builtIn ? source.id : source.baseTheme;
|
|
@@ -5984,6 +6103,13 @@ themeApiRoutes.post("/", async (c) => {
|
|
|
5984
6103
|
themeApiRoutes.post("/:id/edit", async (c) => {
|
|
5985
6104
|
const id = c.req.param("id");
|
|
5986
6105
|
const body = await c.req.json();
|
|
6106
|
+
if (body.name !== void 0 && (typeof body.name !== "string" || body.name.trim() === "")) {
|
|
6107
|
+
return c.json({ error: "name must be a non-empty string when provided" }, 400);
|
|
6108
|
+
}
|
|
6109
|
+
if (body.colors !== void 0) {
|
|
6110
|
+
const colorsError = validateColors(body.colors);
|
|
6111
|
+
if (colorsError !== null) return c.json({ error: colorsError }, 400);
|
|
6112
|
+
}
|
|
5987
6113
|
const source = resolveTheme(id);
|
|
5988
6114
|
if (!source) return c.json({ error: "Theme not found" }, 404);
|
|
5989
6115
|
if (source.builtIn) {
|
|
@@ -6017,6 +6143,13 @@ themeApiRoutes.patch("/:id", async (c) => {
|
|
|
6017
6143
|
return c.json({ error: "Theme not found" }, 404);
|
|
6018
6144
|
}
|
|
6019
6145
|
const body = await c.req.json();
|
|
6146
|
+
if (body.name !== void 0 && (typeof body.name !== "string" || body.name.trim() === "")) {
|
|
6147
|
+
return c.json({ error: "name must be a non-empty string when provided" }, 400);
|
|
6148
|
+
}
|
|
6149
|
+
if (body.colors !== void 0) {
|
|
6150
|
+
const colorsError = validateColors(body.colors);
|
|
6151
|
+
if (colorsError !== null) return c.json({ error: colorsError }, 400);
|
|
6152
|
+
}
|
|
6020
6153
|
const updated = {
|
|
6021
6154
|
...existing,
|
|
6022
6155
|
name: body.name ?? existing.name,
|
|
@@ -6038,9 +6171,9 @@ themeApiRoutes.delete("/:id", (c) => {
|
|
|
6038
6171
|
});
|
|
6039
6172
|
|
|
6040
6173
|
// src/server.ts
|
|
6041
|
-
function tryServe(
|
|
6174
|
+
function tryServe(appFetch, port) {
|
|
6042
6175
|
return new Promise((resolve6, reject) => {
|
|
6043
|
-
const server = serve({ fetch:
|
|
6176
|
+
const server = serve({ fetch: appFetch, port, hostname: "127.0.0.1" });
|
|
6044
6177
|
server.on("listening", () => {
|
|
6045
6178
|
resolve6(port);
|
|
6046
6179
|
});
|
|
@@ -6480,7 +6613,7 @@ async function main() {
|
|
|
6480
6613
|
console.log("AI service test mode enabled \u2014 using mock AI responses");
|
|
6481
6614
|
}
|
|
6482
6615
|
if (debug) {
|
|
6483
|
-
console.log(`[debug] Build timestamp: ${"2026-04-
|
|
6616
|
+
console.log(`[debug] Build timestamp: ${"2026-04-03T00:13:35.309Z"}`);
|
|
6484
6617
|
}
|
|
6485
6618
|
if (projectDir !== null) {
|
|
6486
6619
|
process.chdir(projectDir);
|
|
@@ -6561,7 +6694,8 @@ async function main() {
|
|
|
6561
6694
|
console.log(`Review ${review.id} created.`);
|
|
6562
6695
|
await startServer(port, review.id, repoRoot, { noOpen, strictPort });
|
|
6563
6696
|
}
|
|
6564
|
-
var
|
|
6697
|
+
var resolvedArg = process.argv[1] ? realpathSync(process.argv[1]) : "";
|
|
6698
|
+
var isDirectRun = resolvedArg.endsWith("cli.js") || resolvedArg.endsWith("cli.ts");
|
|
6565
6699
|
if (isDirectRun) {
|
|
6566
6700
|
main().catch((err) => {
|
|
6567
6701
|
console.error(err);
|