glassbox 0.5.0 → 0.6.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 +63 -91
- package/dist/client/app.global.js +8 -8
- package/dist/client/styles.css +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -426,7 +426,7 @@ function debugLog(...args) {
|
|
|
426
426
|
}
|
|
427
427
|
|
|
428
428
|
// src/ai/config.ts
|
|
429
|
-
import {
|
|
429
|
+
import { spawnSync } from "child_process";
|
|
430
430
|
import { chmodSync, existsSync, mkdirSync as mkdirSync2, readFileSync, writeFileSync } from "fs";
|
|
431
431
|
import { homedir } from "os";
|
|
432
432
|
import { join as join2 } from "path";
|
|
@@ -525,27 +525,21 @@ function getKeyFromKeychain(platform) {
|
|
|
525
525
|
const account = `${platform}-api-key`;
|
|
526
526
|
try {
|
|
527
527
|
if (os === "darwin") {
|
|
528
|
-
const
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
).trim();
|
|
532
|
-
return result !== "" ? result : null;
|
|
528
|
+
const r = spawnSync("security", ["find-generic-password", "-s", "glassbox", "-a", account, "-w"], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
529
|
+
const result = (r.stdout ?? "").trim();
|
|
530
|
+
return r.status === 0 && result !== "" ? result : null;
|
|
533
531
|
}
|
|
534
532
|
if (os === "linux") {
|
|
535
|
-
const
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
).trim();
|
|
539
|
-
return result !== "" ? result : null;
|
|
533
|
+
const r = spawnSync("secret-tool", ["lookup", "service", "glassbox", "account", account], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
534
|
+
const result = (r.stdout ?? "").trim();
|
|
535
|
+
return r.status === 0 && result !== "" ? result : null;
|
|
540
536
|
}
|
|
541
537
|
if (os === "win32") {
|
|
542
538
|
const target = winCredTarget(platform);
|
|
543
539
|
const script = WIN_CRED_READ_PS + `Write-Output ([CredHelper]::Read('${target}'))`;
|
|
544
|
-
const
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
}).trim();
|
|
548
|
-
return result !== "" ? result : null;
|
|
540
|
+
const r = spawnSync("powershell", ["-NoProfile", "-Command", "-"], { input: script, encoding: "utf-8" });
|
|
541
|
+
const result = (r.stdout ?? "").trim();
|
|
542
|
+
return r.status === 0 && result !== "" ? result : null;
|
|
549
543
|
}
|
|
550
544
|
} catch {
|
|
551
545
|
return null;
|
|
@@ -600,30 +594,19 @@ function saveKeyToKeychain(platform, key) {
|
|
|
600
594
|
const os = process.platform;
|
|
601
595
|
const account = `${platform}-api-key`;
|
|
602
596
|
if (os === "darwin") {
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
} catch {
|
|
606
|
-
}
|
|
607
|
-
execSync(
|
|
608
|
-
`security add-generic-password -s glassbox -a "${account}" -w "${key.replace(/"/g, '\\"')}"`
|
|
609
|
-
);
|
|
597
|
+
spawnSync("security", ["delete-generic-password", "-s", "glassbox", "-a", account], { stdio: "pipe" });
|
|
598
|
+
spawnSync("security", ["add-generic-password", "-s", "glassbox", "-a", account, "-w", key]);
|
|
610
599
|
return;
|
|
611
600
|
}
|
|
612
601
|
if (os === "linux") {
|
|
613
|
-
|
|
614
|
-
`secret-tool store --label='Glassbox API Key' service glassbox account "${account}"`,
|
|
615
|
-
{ input: key, encoding: "utf-8" }
|
|
616
|
-
);
|
|
602
|
+
spawnSync("secret-tool", ["store", "--label=Glassbox API Key", "service", "glassbox", "account", account], { input: key, encoding: "utf-8" });
|
|
617
603
|
return;
|
|
618
604
|
}
|
|
619
605
|
if (os === "win32") {
|
|
620
606
|
const target = winCredTarget(platform);
|
|
621
607
|
const escapedKey = key.replace(/'/g, "''");
|
|
622
608
|
const script = `cmdkey /generic:'${target}' /user:'glassbox' /pass:'${escapedKey}'`;
|
|
623
|
-
|
|
624
|
-
input: script,
|
|
625
|
-
encoding: "utf-8"
|
|
626
|
-
});
|
|
609
|
+
spawnSync("powershell", ["-NoProfile", "-Command", "-"], { input: script, encoding: "utf-8" });
|
|
627
610
|
}
|
|
628
611
|
}
|
|
629
612
|
function deleteAPIKey(platform) {
|
|
@@ -631,15 +614,12 @@ function deleteAPIKey(platform) {
|
|
|
631
614
|
const account = `${platform}-api-key`;
|
|
632
615
|
try {
|
|
633
616
|
if (os === "darwin") {
|
|
634
|
-
|
|
617
|
+
spawnSync("security", ["delete-generic-password", "-s", "glassbox", "-a", account], { stdio: "pipe" });
|
|
635
618
|
} else if (os === "linux") {
|
|
636
|
-
|
|
619
|
+
spawnSync("secret-tool", ["clear", "service", "glassbox", "account", account], { stdio: "pipe" });
|
|
637
620
|
} else if (os === "win32") {
|
|
638
621
|
const target = winCredTarget(platform);
|
|
639
|
-
|
|
640
|
-
input: `cmdkey /delete:'${target}'`,
|
|
641
|
-
encoding: "utf-8"
|
|
642
|
-
});
|
|
622
|
+
spawnSync("powershell", ["-NoProfile", "-Command", "-"], { input: `cmdkey /delete:'${target}'`, encoding: "utf-8" });
|
|
643
623
|
}
|
|
644
624
|
} catch {
|
|
645
625
|
}
|
|
@@ -663,12 +643,7 @@ function isKeychainAvailable() {
|
|
|
663
643
|
const os = process.platform;
|
|
664
644
|
if (os === "darwin" || os === "win32") return true;
|
|
665
645
|
if (os === "linux") {
|
|
666
|
-
|
|
667
|
-
execSync("which secret-tool 2>/dev/null", { encoding: "utf-8" });
|
|
668
|
-
return true;
|
|
669
|
-
} catch {
|
|
670
|
-
return false;
|
|
671
|
-
}
|
|
646
|
+
return spawnSync("which", ["secret-tool"], { stdio: "pipe" }).status === 0;
|
|
672
647
|
}
|
|
673
648
|
return false;
|
|
674
649
|
}
|
|
@@ -1291,20 +1266,21 @@ async function setupAnnotations(fileIdMap) {
|
|
|
1291
1266
|
}
|
|
1292
1267
|
|
|
1293
1268
|
// src/git/diff.ts
|
|
1294
|
-
import {
|
|
1269
|
+
import { spawnSync as spawnSync2 } from "child_process";
|
|
1295
1270
|
import { readFileSync as readFileSync2 } from "fs";
|
|
1296
1271
|
import { resolve } from "path";
|
|
1297
1272
|
function git(args, cwd) {
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1273
|
+
const result = spawnSync2("git", args, { cwd, encoding: "utf-8", maxBuffer: 50 * 1024 * 1024 });
|
|
1274
|
+
if (result.status === 0) return result.stdout;
|
|
1275
|
+
if (result.stdout !== "") return result.stdout;
|
|
1276
|
+
const err = new Error(result.stderr);
|
|
1277
|
+
err.stdout = result.stdout;
|
|
1278
|
+
err.stderr = result.stderr;
|
|
1279
|
+
err.status = result.status;
|
|
1280
|
+
throw err;
|
|
1305
1281
|
}
|
|
1306
1282
|
function getRepoRoot(cwd) {
|
|
1307
|
-
return git("rev-parse --show-toplevel", cwd).trim();
|
|
1283
|
+
return git(["rev-parse", "--show-toplevel"], cwd).trim();
|
|
1308
1284
|
}
|
|
1309
1285
|
function getRepoName(cwd) {
|
|
1310
1286
|
const root = getRepoRoot(cwd);
|
|
@@ -1312,7 +1288,7 @@ function getRepoName(cwd) {
|
|
|
1312
1288
|
}
|
|
1313
1289
|
function isGitRepo(cwd) {
|
|
1314
1290
|
try {
|
|
1315
|
-
git("rev-parse --is-inside-work-tree", cwd);
|
|
1291
|
+
git(["rev-parse", "--is-inside-work-tree"], cwd);
|
|
1316
1292
|
return true;
|
|
1317
1293
|
} catch {
|
|
1318
1294
|
return false;
|
|
@@ -1321,22 +1297,21 @@ function isGitRepo(cwd) {
|
|
|
1321
1297
|
function getDiffArgs(mode) {
|
|
1322
1298
|
switch (mode.type) {
|
|
1323
1299
|
case "uncommitted":
|
|
1324
|
-
return "diff HEAD";
|
|
1300
|
+
return ["diff", "HEAD"];
|
|
1325
1301
|
case "staged":
|
|
1326
|
-
return "diff --cached";
|
|
1302
|
+
return ["diff", "--cached"];
|
|
1327
1303
|
case "unstaged":
|
|
1328
|
-
return "diff";
|
|
1304
|
+
return ["diff"];
|
|
1329
1305
|
case "commit":
|
|
1330
|
-
return
|
|
1306
|
+
return ["diff", `${mode.sha}~1`, mode.sha];
|
|
1331
1307
|
case "range":
|
|
1332
|
-
return
|
|
1333
|
-
case "branch":
|
|
1334
|
-
return
|
|
1335
|
-
}
|
|
1308
|
+
return ["diff", mode.from, mode.to];
|
|
1309
|
+
case "branch":
|
|
1310
|
+
return ["diff", `${mode.name}...HEAD`];
|
|
1336
1311
|
case "files":
|
|
1337
|
-
return
|
|
1312
|
+
return ["diff", "HEAD", "--", ...mode.patterns];
|
|
1338
1313
|
case "all":
|
|
1339
|
-
return "diff --no-index /dev/null .";
|
|
1314
|
+
return ["diff", "--no-index", "/dev/null", "."];
|
|
1340
1315
|
}
|
|
1341
1316
|
}
|
|
1342
1317
|
function getFileDiffs(mode, cwd) {
|
|
@@ -1347,13 +1322,13 @@ function getFileDiffs(mode, cwd) {
|
|
|
1347
1322
|
const diffArgs = getDiffArgs(mode);
|
|
1348
1323
|
let rawDiff;
|
|
1349
1324
|
try {
|
|
1350
|
-
rawDiff = git(
|
|
1325
|
+
rawDiff = git([...diffArgs, "-U3"], repoRoot);
|
|
1351
1326
|
} catch {
|
|
1352
1327
|
rawDiff = "";
|
|
1353
1328
|
}
|
|
1354
1329
|
const diffs = parseDiff(rawDiff);
|
|
1355
1330
|
if (mode.type === "uncommitted") {
|
|
1356
|
-
const untracked = git("ls-files --others --exclude-standard", repoRoot).trim();
|
|
1331
|
+
const untracked = git(["ls-files", "--others", "--exclude-standard"], repoRoot).trim();
|
|
1357
1332
|
if (untracked) {
|
|
1358
1333
|
for (const file of untracked.split("\n").filter(Boolean)) {
|
|
1359
1334
|
if (!diffs.some((d) => d.filePath === file)) {
|
|
@@ -1365,7 +1340,7 @@ function getFileDiffs(mode, cwd) {
|
|
|
1365
1340
|
return diffs;
|
|
1366
1341
|
}
|
|
1367
1342
|
function getAllFiles(repoRoot) {
|
|
1368
|
-
const files = git("ls-files", repoRoot).trim().split("\n").filter(Boolean);
|
|
1343
|
+
const files = git(["ls-files"], repoRoot).trim().split("\n").filter(Boolean);
|
|
1369
1344
|
return files.map((file) => createNewFileDiff(file, repoRoot));
|
|
1370
1345
|
}
|
|
1371
1346
|
function createNewFileDiff(filePath, repoRoot) {
|
|
@@ -1492,15 +1467,15 @@ function getFileContent(filePath, ref, cwd) {
|
|
|
1492
1467
|
const repoRoot = getRepoRoot(cwd);
|
|
1493
1468
|
try {
|
|
1494
1469
|
if (ref === "working") {
|
|
1495
|
-
return
|
|
1470
|
+
return readFileSync2(resolve(repoRoot, filePath), "utf-8");
|
|
1496
1471
|
}
|
|
1497
|
-
return git(
|
|
1472
|
+
return git(["show", `${ref}:${filePath}`], repoRoot);
|
|
1498
1473
|
} catch {
|
|
1499
1474
|
return "";
|
|
1500
1475
|
}
|
|
1501
1476
|
}
|
|
1502
1477
|
function getHeadCommit(cwd) {
|
|
1503
|
-
return
|
|
1478
|
+
return spawnSync2("git", ["rev-parse", "HEAD"], { cwd, encoding: "utf-8" }).stdout.trim();
|
|
1504
1479
|
}
|
|
1505
1480
|
function parseModeString(modeStr) {
|
|
1506
1481
|
if (modeStr === "uncommitted") return { type: "uncommitted" };
|
|
@@ -1521,9 +1496,12 @@ function getSingleFileDiff(mode, filePath, repoRoot, extraFlags = "") {
|
|
|
1521
1496
|
return createNewFileDiff(filePath, repoRoot);
|
|
1522
1497
|
}
|
|
1523
1498
|
const diffArgs = getDiffArgs(mode);
|
|
1499
|
+
const args = [...diffArgs, "-U3"];
|
|
1500
|
+
if (extraFlags) args.push(...extraFlags.split(" ").filter(Boolean));
|
|
1501
|
+
args.push("--", filePath);
|
|
1524
1502
|
let rawDiff;
|
|
1525
1503
|
try {
|
|
1526
|
-
rawDiff = git(
|
|
1504
|
+
rawDiff = git(args, repoRoot);
|
|
1527
1505
|
} catch {
|
|
1528
1506
|
rawDiff = "";
|
|
1529
1507
|
}
|
|
@@ -2943,14 +2921,14 @@ aiApiRoutes.post("/preferences", async (c) => {
|
|
|
2943
2921
|
|
|
2944
2922
|
// src/routes/api.ts
|
|
2945
2923
|
init_queries();
|
|
2946
|
-
import {
|
|
2924
|
+
import { execFileSync, spawnSync as spawnSync5 } from "child_process";
|
|
2947
2925
|
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "fs";
|
|
2948
2926
|
import { Hono as Hono2 } from "hono";
|
|
2949
2927
|
import { join as join6, resolve as resolve3 } from "path";
|
|
2950
2928
|
|
|
2951
2929
|
// src/export/generate.ts
|
|
2952
2930
|
init_queries();
|
|
2953
|
-
import {
|
|
2931
|
+
import { spawnSync as spawnSync3 } from "child_process";
|
|
2954
2932
|
import { appendFileSync, existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync4, unlinkSync, writeFileSync as writeFileSync3 } from "fs";
|
|
2955
2933
|
import { homedir as homedir2 } from "os";
|
|
2956
2934
|
import { join as join4 } from "path";
|
|
@@ -2969,12 +2947,8 @@ function saveDismissals(data) {
|
|
|
2969
2947
|
writeFileSync3(DISMISS_FILE, JSON.stringify(data), "utf-8");
|
|
2970
2948
|
}
|
|
2971
2949
|
function isGlassboxGitignored(repoRoot) {
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
return true;
|
|
2975
|
-
} catch {
|
|
2976
|
-
return false;
|
|
2977
|
-
}
|
|
2950
|
+
const result = spawnSync3("git", ["check-ignore", "-q", ".glassbox"], { cwd: repoRoot, stdio: "pipe" });
|
|
2951
|
+
return result.status === 0;
|
|
2978
2952
|
}
|
|
2979
2953
|
function shouldPromptGitignore(repoRoot) {
|
|
2980
2954
|
if (isGlassboxGitignored(repoRoot)) return false;
|
|
@@ -3102,7 +3076,7 @@ function scheduleAutoExport(reviewId, repoRoot) {
|
|
|
3102
3076
|
}
|
|
3103
3077
|
|
|
3104
3078
|
// src/git/image.ts
|
|
3105
|
-
import {
|
|
3079
|
+
import { spawnSync as spawnSync4 } from "child_process";
|
|
3106
3080
|
import { readFileSync as readFileSync5 } from "fs";
|
|
3107
3081
|
import { resolve as resolve2 } from "path";
|
|
3108
3082
|
var IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([".png", ".jpg", ".jpeg", ".gif", ".webp", ".svg"]);
|
|
@@ -3176,12 +3150,10 @@ function getNewRef(mode) {
|
|
|
3176
3150
|
}
|
|
3177
3151
|
}
|
|
3178
3152
|
function gitShowFile(ref, filePath, repoRoot) {
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
return null;
|
|
3184
|
-
}
|
|
3153
|
+
const spec = ref === ":" ? `:${filePath}` : `${ref}:${filePath}`;
|
|
3154
|
+
const result = spawnSync4("git", ["show", spec], { cwd: repoRoot, maxBuffer: 50 * 1024 * 1024 });
|
|
3155
|
+
if (result.status !== 0 || result.stdout.length === 0) return null;
|
|
3156
|
+
return result.stdout;
|
|
3185
3157
|
}
|
|
3186
3158
|
function readWorkingFile(filePath, repoRoot) {
|
|
3187
3159
|
try {
|
|
@@ -3976,11 +3948,11 @@ apiRoutes.post("/files/:fileId/reveal", async (c) => {
|
|
|
3976
3948
|
const fullPath = resolve3(repoRoot, file.file_path);
|
|
3977
3949
|
try {
|
|
3978
3950
|
if (process.platform === "darwin") {
|
|
3979
|
-
|
|
3951
|
+
execFileSync("open", ["-R", fullPath]);
|
|
3980
3952
|
} else if (process.platform === "win32") {
|
|
3981
|
-
|
|
3953
|
+
execFileSync("explorer", ["/select," + fullPath]);
|
|
3982
3954
|
} else {
|
|
3983
|
-
|
|
3955
|
+
execFileSync("xdg-open", [resolve3(fullPath, "..")]);
|
|
3984
3956
|
}
|
|
3985
3957
|
} catch {
|
|
3986
3958
|
}
|
|
@@ -4084,7 +4056,7 @@ apiRoutes.get("/symbol-definition", async (c) => {
|
|
|
4084
4056
|
}
|
|
4085
4057
|
if (definitions.length === 0) {
|
|
4086
4058
|
try {
|
|
4087
|
-
const allFiles =
|
|
4059
|
+
const allFiles = spawnSync5("git", ["ls-files"], { cwd: repoRoot, encoding: "utf-8" }).stdout.trim().split("\n").filter(Boolean);
|
|
4088
4060
|
for (const filePath of allFiles) {
|
|
4089
4061
|
if (searchedPaths.has(filePath)) continue;
|
|
4090
4062
|
const ext = filePath.slice(filePath.lastIndexOf("."));
|
|
@@ -5368,7 +5340,7 @@ pageRoutes.get("/history", async (c) => {
|
|
|
5368
5340
|
// src/server.ts
|
|
5369
5341
|
function tryServe(fetch2, port) {
|
|
5370
5342
|
return new Promise((resolve6, reject) => {
|
|
5371
|
-
const server = serve({ fetch: fetch2, port });
|
|
5343
|
+
const server = serve({ fetch: fetch2, port, hostname: "127.0.0.1" });
|
|
5372
5344
|
server.on("listening", () => {
|
|
5373
5345
|
resolve6(port);
|
|
5374
5346
|
});
|
|
@@ -5807,7 +5779,7 @@ async function main() {
|
|
|
5807
5779
|
console.log("AI service test mode enabled \u2014 using mock AI responses");
|
|
5808
5780
|
}
|
|
5809
5781
|
if (debug) {
|
|
5810
|
-
console.log(`[debug] Build timestamp: ${"2026-03-
|
|
5782
|
+
console.log(`[debug] Build timestamp: ${"2026-03-27T03:39:52.371Z"}`);
|
|
5811
5783
|
}
|
|
5812
5784
|
if (projectDir) {
|
|
5813
5785
|
process.chdir(projectDir);
|