glassbox 0.20.0-beta.1 → 0.20.0-beta.4
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 +1882 -414
- package/dist/client/app.global.js +18 -18
- package/dist/client/styles.css +1 -1
- package/dist/plugins/graphviz/index.js +7227 -0
- package/dist/plugins/graphviz/manifest.json +31 -0
- package/dist/plugins/image-codecs/index.js +3 -0
- package/dist/plugins/image-codecs/manifest.json +10 -0
- package/dist/plugins/mermaid/index.js +1 -0
- package/dist/plugins/mermaid/manifest.json +31 -0
- package/dist/plugins/plantuml/index.js +1 -0
- package/dist/plugins/plantuml/manifest.json +30 -0
- package/package.json +9 -5
package/dist/cli.js
CHANGED
|
@@ -11937,21 +11937,21 @@ var init_to_json_schema = __esm({
|
|
|
11937
11937
|
// node_modules/zod/v4/core/json-schema-processors.js
|
|
11938
11938
|
function toJSONSchema(input, params) {
|
|
11939
11939
|
if ("_idmap" in input) {
|
|
11940
|
-
const
|
|
11940
|
+
const registry3 = input;
|
|
11941
11941
|
const ctx2 = initializeContext({ ...params, processors: allProcessors });
|
|
11942
11942
|
const defs = {};
|
|
11943
|
-
for (const entry of
|
|
11943
|
+
for (const entry of registry3._idmap.entries()) {
|
|
11944
11944
|
const [_, schema] = entry;
|
|
11945
11945
|
process2(schema, ctx2);
|
|
11946
11946
|
}
|
|
11947
11947
|
const schemas = {};
|
|
11948
11948
|
const external = {
|
|
11949
|
-
registry:
|
|
11949
|
+
registry: registry3,
|
|
11950
11950
|
uri: params?.uri,
|
|
11951
11951
|
defs
|
|
11952
11952
|
};
|
|
11953
11953
|
ctx2.external = external;
|
|
11954
|
-
for (const entry of
|
|
11954
|
+
for (const entry of registry3._idmap.entries()) {
|
|
11955
11955
|
const [key, schema] = entry;
|
|
11956
11956
|
extractDefs(ctx2, schema);
|
|
11957
11957
|
schemas[key] = finalize(ctx2, schema);
|
|
@@ -17254,6 +17254,178 @@ var init_debug = __esm({
|
|
|
17254
17254
|
}
|
|
17255
17255
|
});
|
|
17256
17256
|
|
|
17257
|
+
// src/global-config.ts
|
|
17258
|
+
import { chmodSync, existsSync as existsSync4, mkdirSync as mkdirSync5, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
17259
|
+
import { homedir } from "os";
|
|
17260
|
+
import { join as join4 } from "path";
|
|
17261
|
+
function resolveGlobalConfigDir() {
|
|
17262
|
+
const override = process.env.GLASSBOX_CONFIG_DIR;
|
|
17263
|
+
if (override !== void 0 && override.trim() !== "") return override;
|
|
17264
|
+
return join4(homedir(), ".glassbox");
|
|
17265
|
+
}
|
|
17266
|
+
function readGlobalConfig() {
|
|
17267
|
+
try {
|
|
17268
|
+
if (existsSync4(GLOBAL_CONFIG_PATH)) {
|
|
17269
|
+
const raw2 = JSON.parse(readFileSync3(GLOBAL_CONFIG_PATH, "utf-8"));
|
|
17270
|
+
const parsed = GlobalConfigSchema.safeParse(raw2);
|
|
17271
|
+
if (parsed.success) return parsed.data;
|
|
17272
|
+
}
|
|
17273
|
+
} catch {
|
|
17274
|
+
}
|
|
17275
|
+
return {};
|
|
17276
|
+
}
|
|
17277
|
+
function writeGlobalConfig(config2) {
|
|
17278
|
+
mkdirSync5(GLOBAL_CONFIG_DIR, { recursive: true });
|
|
17279
|
+
writeFileSync3(GLOBAL_CONFIG_PATH, JSON.stringify(config2, null, 2), "utf-8");
|
|
17280
|
+
try {
|
|
17281
|
+
chmodSync(GLOBAL_CONFIG_PATH, 384);
|
|
17282
|
+
} catch {
|
|
17283
|
+
}
|
|
17284
|
+
}
|
|
17285
|
+
function updateGlobalConfig(mutator) {
|
|
17286
|
+
const cfg = readGlobalConfig();
|
|
17287
|
+
const result = mutator(cfg);
|
|
17288
|
+
writeGlobalConfig(result === void 0 ? cfg : result);
|
|
17289
|
+
}
|
|
17290
|
+
var GlobalConfigSchema, GLOBAL_CONFIG_DIR, GLOBAL_CONFIG_PATH;
|
|
17291
|
+
var init_global_config = __esm({
|
|
17292
|
+
"src/global-config.ts"() {
|
|
17293
|
+
"use strict";
|
|
17294
|
+
init_zod();
|
|
17295
|
+
GlobalConfigSchema = external_exports.record(external_exports.string(), external_exports.unknown());
|
|
17296
|
+
GLOBAL_CONFIG_DIR = resolveGlobalConfigDir();
|
|
17297
|
+
GLOBAL_CONFIG_PATH = join4(GLOBAL_CONFIG_DIR, "config.json");
|
|
17298
|
+
}
|
|
17299
|
+
});
|
|
17300
|
+
|
|
17301
|
+
// src/ai/keychain.ts
|
|
17302
|
+
import { spawnSync as spawnSync5 } from "child_process";
|
|
17303
|
+
function winTargetForAccount(account) {
|
|
17304
|
+
return `glassbox-${account}`;
|
|
17305
|
+
}
|
|
17306
|
+
function winCredTarget(platform) {
|
|
17307
|
+
return winTargetForAccount(`${platform}-api-key`);
|
|
17308
|
+
}
|
|
17309
|
+
function getSecretFromKeychain(account) {
|
|
17310
|
+
const os = process.platform;
|
|
17311
|
+
try {
|
|
17312
|
+
if (os === "darwin") {
|
|
17313
|
+
const r = spawnSync5("security", ["find-generic-password", "-s", "glassbox", "-a", account, "-w"], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
17314
|
+
const result = r.stdout.trim();
|
|
17315
|
+
return r.status === 0 && result !== "" ? result : null;
|
|
17316
|
+
}
|
|
17317
|
+
if (os === "linux") {
|
|
17318
|
+
const r = spawnSync5("secret-tool", ["lookup", "service", "glassbox", "account", account], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
17319
|
+
const result = r.stdout.trim();
|
|
17320
|
+
return r.status === 0 && result !== "" ? result : null;
|
|
17321
|
+
}
|
|
17322
|
+
if (os === "win32") {
|
|
17323
|
+
const target = winTargetForAccount(account);
|
|
17324
|
+
const list = spawnSync5("cmdkey", ["/list:" + target], { encoding: "utf-8" });
|
|
17325
|
+
if (list.status !== 0 || (list.stdout || "").includes("* NONE *")) return null;
|
|
17326
|
+
const script = WIN_CRED_READ_PS + `Write-Output ([CredHelper]::Read('${target}'))`;
|
|
17327
|
+
const r = spawnSync5("powershell", ["-NoProfile", "-Command", "-"], { input: script, encoding: "utf-8" });
|
|
17328
|
+
const result = r.stdout.trim();
|
|
17329
|
+
return r.status === 0 && result !== "" ? result : null;
|
|
17330
|
+
}
|
|
17331
|
+
} catch {
|
|
17332
|
+
return null;
|
|
17333
|
+
}
|
|
17334
|
+
return null;
|
|
17335
|
+
}
|
|
17336
|
+
function getKeyFromKeychain(platform) {
|
|
17337
|
+
return getSecretFromKeychain(`${platform}-api-key`);
|
|
17338
|
+
}
|
|
17339
|
+
function assertSpawnOk(label, r) {
|
|
17340
|
+
if (r.error) throw new Error(`${label} failed: ${r.error.message}`);
|
|
17341
|
+
if (r.status !== 0) {
|
|
17342
|
+
const detail = (String(r.stderr) || String(r.stdout)).trim();
|
|
17343
|
+
throw new Error(`${label} failed (exit ${String(r.status)})${detail ? `: ${detail}` : ""}`);
|
|
17344
|
+
}
|
|
17345
|
+
}
|
|
17346
|
+
function saveSecretToKeychain(account, value, label = "Glassbox") {
|
|
17347
|
+
const os = process.platform;
|
|
17348
|
+
if (os === "darwin") {
|
|
17349
|
+
spawnSync5("security", ["delete-generic-password", "-s", "glassbox", "-a", account], { stdio: "pipe" });
|
|
17350
|
+
assertSpawnOk("Keychain write", spawnSync5("security", ["add-generic-password", "-s", "glassbox", "-a", account, "-w", value], { encoding: "utf-8" }));
|
|
17351
|
+
return;
|
|
17352
|
+
}
|
|
17353
|
+
if (os === "linux") {
|
|
17354
|
+
assertSpawnOk("System keyring write", spawnSync5("secret-tool", ["store", `--label=${label}`, "service", "glassbox", "account", account], { input: value, encoding: "utf-8" }));
|
|
17355
|
+
return;
|
|
17356
|
+
}
|
|
17357
|
+
if (os === "win32") {
|
|
17358
|
+
const target = winTargetForAccount(account);
|
|
17359
|
+
const escapedValue = value.replace(/'/g, "''");
|
|
17360
|
+
const script = `cmdkey /generic:'${target}' /user:'glassbox' /pass:'${escapedValue}'`;
|
|
17361
|
+
assertSpawnOk("Credential Manager write", spawnSync5("powershell", ["-NoProfile", "-Command", "-"], { input: script, encoding: "utf-8" }));
|
|
17362
|
+
}
|
|
17363
|
+
}
|
|
17364
|
+
function deleteSecretFromKeychain(account) {
|
|
17365
|
+
const os = process.platform;
|
|
17366
|
+
try {
|
|
17367
|
+
if (os === "darwin") {
|
|
17368
|
+
spawnSync5("security", ["delete-generic-password", "-s", "glassbox", "-a", account], { stdio: "pipe" });
|
|
17369
|
+
} else if (os === "linux") {
|
|
17370
|
+
spawnSync5("secret-tool", ["clear", "service", "glassbox", "account", account], { stdio: "pipe" });
|
|
17371
|
+
} else if (os === "win32") {
|
|
17372
|
+
spawnSync5("powershell", ["-NoProfile", "-Command", "-"], { input: `cmdkey /delete:'${winTargetForAccount(account)}'`, encoding: "utf-8" });
|
|
17373
|
+
}
|
|
17374
|
+
} catch {
|
|
17375
|
+
}
|
|
17376
|
+
}
|
|
17377
|
+
function saveKeyToKeychain(platform, key) {
|
|
17378
|
+
saveSecretToKeychain(`${platform}-api-key`, key, "Glassbox API Key");
|
|
17379
|
+
}
|
|
17380
|
+
function isKeychainAvailable() {
|
|
17381
|
+
const os = process.platform;
|
|
17382
|
+
if (os === "darwin" || os === "win32") return true;
|
|
17383
|
+
if (os === "linux") {
|
|
17384
|
+
return spawnSync5("which", ["secret-tool"], { stdio: "pipe" }).status === 0;
|
|
17385
|
+
}
|
|
17386
|
+
return false;
|
|
17387
|
+
}
|
|
17388
|
+
function getKeychainLabel() {
|
|
17389
|
+
const os = process.platform;
|
|
17390
|
+
if (os === "darwin") return "Keychain";
|
|
17391
|
+
if (os === "linux") return "System Keyring";
|
|
17392
|
+
if (os === "win32") return "Credential Manager";
|
|
17393
|
+
return "System Keychain";
|
|
17394
|
+
}
|
|
17395
|
+
var WIN_CRED_READ_PS;
|
|
17396
|
+
var init_keychain = __esm({
|
|
17397
|
+
"src/ai/keychain.ts"() {
|
|
17398
|
+
"use strict";
|
|
17399
|
+
WIN_CRED_READ_PS = `
|
|
17400
|
+
Add-Type -TypeDefinition @'
|
|
17401
|
+
using System;
|
|
17402
|
+
using System.Runtime.InteropServices;
|
|
17403
|
+
public class CredHelper {
|
|
17404
|
+
[DllImport("advapi32", SetLastError = true, CharSet = CharSet.Unicode)]
|
|
17405
|
+
static extern bool CredRead(string t, int type, int f, out IntPtr p);
|
|
17406
|
+
[DllImport("advapi32")]
|
|
17407
|
+
static extern void CredFree(IntPtr p);
|
|
17408
|
+
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
|
17409
|
+
struct CRED {
|
|
17410
|
+
public int Flags; public int Type; public string TargetName; public string Comment;
|
|
17411
|
+
public long LastWritten; public int CredentialBlobSize; public IntPtr CredentialBlob;
|
|
17412
|
+
public int Persist; public int AttributeCount; public IntPtr Attributes;
|
|
17413
|
+
public string TargetAlias; public string UserName;
|
|
17414
|
+
}
|
|
17415
|
+
public static string Read(string target) {
|
|
17416
|
+
IntPtr ptr;
|
|
17417
|
+
if (!CredRead(target, 1, 0, out ptr)) return "";
|
|
17418
|
+
CRED c = (CRED)Marshal.PtrToStructure(ptr, typeof(CRED));
|
|
17419
|
+
string r = Marshal.PtrToStringUni(c.CredentialBlob, c.CredentialBlobSize / 2);
|
|
17420
|
+
CredFree(ptr);
|
|
17421
|
+
return r;
|
|
17422
|
+
}
|
|
17423
|
+
}
|
|
17424
|
+
'@
|
|
17425
|
+
`;
|
|
17426
|
+
}
|
|
17427
|
+
});
|
|
17428
|
+
|
|
17257
17429
|
// src/git/image-blobs.ts
|
|
17258
17430
|
var image_blobs_exports = {};
|
|
17259
17431
|
__export(image_blobs_exports, {
|
|
@@ -17266,33 +17438,1004 @@ import { join as join5 } from "path";
|
|
|
17266
17438
|
function blobDir(dataDir) {
|
|
17267
17439
|
return join5(dataDir, "image-blobs");
|
|
17268
17440
|
}
|
|
17269
|
-
function blobName(fileId, side) {
|
|
17270
|
-
return `${fileId.replace(/[^a-z0-9]/gi, "")}-${side}`;
|
|
17441
|
+
function blobName(fileId, side) {
|
|
17442
|
+
return `${fileId.replace(/[^a-z0-9]/gi, "")}-${side}`;
|
|
17443
|
+
}
|
|
17444
|
+
function writeImageBlob(dataDir, fileId, side, bytes) {
|
|
17445
|
+
if (bytes.length === 0) return;
|
|
17446
|
+
const dir = blobDir(dataDir);
|
|
17447
|
+
mkdirSync6(dir, { recursive: true });
|
|
17448
|
+
writeFileSync4(join5(dir, blobName(fileId, side)), bytes);
|
|
17449
|
+
}
|
|
17450
|
+
function readImageBlob(dataDir, fileId, side) {
|
|
17451
|
+
const path = join5(blobDir(dataDir), blobName(fileId, side));
|
|
17452
|
+
if (!existsSync5(path)) return null;
|
|
17453
|
+
try {
|
|
17454
|
+
return readFileSync4(path);
|
|
17455
|
+
} catch {
|
|
17456
|
+
return null;
|
|
17457
|
+
}
|
|
17458
|
+
}
|
|
17459
|
+
function clearImageBlobs(dataDir) {
|
|
17460
|
+
try {
|
|
17461
|
+
rmSync3(blobDir(dataDir), { recursive: true, force: true });
|
|
17462
|
+
} catch {
|
|
17463
|
+
}
|
|
17464
|
+
}
|
|
17465
|
+
var init_image_blobs = __esm({
|
|
17466
|
+
"src/git/image-blobs.ts"() {
|
|
17467
|
+
"use strict";
|
|
17468
|
+
}
|
|
17469
|
+
});
|
|
17470
|
+
|
|
17471
|
+
// src/feature-flags.ts
|
|
17472
|
+
var PLUGINS_ENABLED;
|
|
17473
|
+
var init_feature_flags = __esm({
|
|
17474
|
+
"src/feature-flags.ts"() {
|
|
17475
|
+
"use strict";
|
|
17476
|
+
PLUGINS_ENABLED = process.env.GLASSBOX_PLUGINS_DISABLED === "1" ? false : true;
|
|
17477
|
+
}
|
|
17478
|
+
});
|
|
17479
|
+
|
|
17480
|
+
// src/api/_runner.ts
|
|
17481
|
+
function currentReviewId() {
|
|
17482
|
+
if (typeof document === "undefined") return "";
|
|
17483
|
+
return document.body.dataset.reviewId ?? "";
|
|
17484
|
+
}
|
|
17485
|
+
async function apiCall(responseSchema, path, opts = {}) {
|
|
17486
|
+
const separator = path.includes("?") ? "&" : "?";
|
|
17487
|
+
const url2 = "/api" + path + separator + "reviewId=" + encodeURIComponent(currentReviewId());
|
|
17488
|
+
const res = await fetch(url2, {
|
|
17489
|
+
headers: { "Content-Type": "application/json" },
|
|
17490
|
+
method: opts.method,
|
|
17491
|
+
body: opts.body !== void 0 ? JSON.stringify(opts.body) : void 0
|
|
17492
|
+
});
|
|
17493
|
+
const json2 = await res.json();
|
|
17494
|
+
const result = responseSchema.safeParse(json2);
|
|
17495
|
+
if (!result.success) {
|
|
17496
|
+
const summary = result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
|
|
17497
|
+
throw new Error(`API response from ${path} failed validation: ${summary}`);
|
|
17498
|
+
}
|
|
17499
|
+
return result.data;
|
|
17500
|
+
}
|
|
17501
|
+
function qs(params) {
|
|
17502
|
+
const entries = Object.entries(params).filter(([, v]) => v !== void 0 && v !== null);
|
|
17503
|
+
if (entries.length === 0) return "";
|
|
17504
|
+
const usp = new URLSearchParams();
|
|
17505
|
+
for (const [k, v] of entries) usp.set(k, String(v));
|
|
17506
|
+
return "?" + usp.toString();
|
|
17507
|
+
}
|
|
17508
|
+
var OkResponseSchema;
|
|
17509
|
+
var init_runner = __esm({
|
|
17510
|
+
"src/api/_runner.ts"() {
|
|
17511
|
+
"use strict";
|
|
17512
|
+
init_zod();
|
|
17513
|
+
OkResponseSchema = external_exports.object({ ok: external_exports.literal(true) });
|
|
17514
|
+
}
|
|
17515
|
+
});
|
|
17516
|
+
|
|
17517
|
+
// src/api/project-settings.ts
|
|
17518
|
+
var project_settings_exports = {};
|
|
17519
|
+
__export(project_settings_exports, {
|
|
17520
|
+
GetProjectSettingsRespSchema: () => GetProjectSettingsRespSchema,
|
|
17521
|
+
ProjectSettingsSchema: () => ProjectSettingsSchema,
|
|
17522
|
+
UpdateProjectSettingsReqSchema: () => UpdateProjectSettingsReqSchema,
|
|
17523
|
+
UpdateProjectSettingsRespSchema: () => UpdateProjectSettingsRespSchema,
|
|
17524
|
+
getProjectSettings: () => getProjectSettings,
|
|
17525
|
+
updateProjectSettings: () => updateProjectSettings
|
|
17526
|
+
});
|
|
17527
|
+
async function getProjectSettings() {
|
|
17528
|
+
return apiCall(GetProjectSettingsRespSchema, "/project-settings");
|
|
17529
|
+
}
|
|
17530
|
+
async function updateProjectSettings(req) {
|
|
17531
|
+
return apiCall(UpdateProjectSettingsRespSchema, "/project-settings", { method: "PATCH", body: req });
|
|
17532
|
+
}
|
|
17533
|
+
var ProjectSettingsSchema, GetProjectSettingsRespSchema, UpdateProjectSettingsReqSchema, UpdateProjectSettingsRespSchema;
|
|
17534
|
+
var init_project_settings = __esm({
|
|
17535
|
+
"src/api/project-settings.ts"() {
|
|
17536
|
+
"use strict";
|
|
17537
|
+
init_zod();
|
|
17538
|
+
init_runner();
|
|
17539
|
+
ProjectSettingsSchema = external_exports.object({
|
|
17540
|
+
appName: external_exports.string().optional(),
|
|
17541
|
+
/** Content-plugin ids disabled for THIS project (doc 29 FR-29.16). A plugin is
|
|
17542
|
+
* enabled unless it appears here or in the global disabled list. */
|
|
17543
|
+
disabledPlugins: external_exports.array(external_exports.string()).optional(),
|
|
17544
|
+
/** Per-project plugin preference values (doc 29 FR-29.12): pluginId → key → value. */
|
|
17545
|
+
pluginSettings: external_exports.record(external_exports.string(), external_exports.record(external_exports.string(), external_exports.string())).optional()
|
|
17546
|
+
});
|
|
17547
|
+
GetProjectSettingsRespSchema = ProjectSettingsSchema;
|
|
17548
|
+
UpdateProjectSettingsReqSchema = ProjectSettingsSchema.partial();
|
|
17549
|
+
UpdateProjectSettingsRespSchema = ProjectSettingsSchema;
|
|
17550
|
+
}
|
|
17551
|
+
});
|
|
17552
|
+
|
|
17553
|
+
// src/project-settings-store.ts
|
|
17554
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync8, readFileSync as readFileSync9, writeFileSync as writeFileSync9 } from "fs";
|
|
17555
|
+
import { join as join10 } from "path";
|
|
17556
|
+
function settingsPath(repoRoot) {
|
|
17557
|
+
return join10(repoRoot, ".glassbox", "settings.json");
|
|
17558
|
+
}
|
|
17559
|
+
function readProjectSettings(repoRoot) {
|
|
17560
|
+
try {
|
|
17561
|
+
const path = settingsPath(repoRoot);
|
|
17562
|
+
if (existsSync10(path)) {
|
|
17563
|
+
const raw2 = JSON.parse(readFileSync9(path, "utf-8"));
|
|
17564
|
+
const parsed = ProjectSettingsSchema.safeParse(raw2);
|
|
17565
|
+
if (parsed.success) return parsed.data;
|
|
17566
|
+
}
|
|
17567
|
+
} catch {
|
|
17568
|
+
}
|
|
17569
|
+
return {};
|
|
17570
|
+
}
|
|
17571
|
+
function updateProjectSettings2(repoRoot, mutator) {
|
|
17572
|
+
const current = readProjectSettings(repoRoot);
|
|
17573
|
+
const result = mutator(current);
|
|
17574
|
+
const next = result === void 0 ? current : result;
|
|
17575
|
+
const dir = join10(repoRoot, ".glassbox");
|
|
17576
|
+
mkdirSync8(dir, { recursive: true });
|
|
17577
|
+
writeFileSync9(join10(dir, "settings.json"), JSON.stringify(next, null, 2), "utf-8");
|
|
17578
|
+
}
|
|
17579
|
+
var init_project_settings_store = __esm({
|
|
17580
|
+
"src/project-settings-store.ts"() {
|
|
17581
|
+
"use strict";
|
|
17582
|
+
init_project_settings();
|
|
17583
|
+
}
|
|
17584
|
+
});
|
|
17585
|
+
|
|
17586
|
+
// src/plugins/enablement.ts
|
|
17587
|
+
function toStringArray(v) {
|
|
17588
|
+
return Array.isArray(v) ? v.filter((x) => typeof x === "string") : [];
|
|
17589
|
+
}
|
|
17590
|
+
function readGlobalDisabled() {
|
|
17591
|
+
return toStringArray(readGlobalConfig().disabledPlugins);
|
|
17592
|
+
}
|
|
17593
|
+
function setGlobalDisabled(id, disabled) {
|
|
17594
|
+
updateGlobalConfig((cfg) => {
|
|
17595
|
+
const cur = toStringArray(cfg.disabledPlugins);
|
|
17596
|
+
const next = disabled ? [.../* @__PURE__ */ new Set([...cur, id])] : cur.filter((x) => x !== id);
|
|
17597
|
+
return { ...cfg, disabledPlugins: next };
|
|
17598
|
+
});
|
|
17599
|
+
}
|
|
17600
|
+
function readProjectDisabled(repoRoot) {
|
|
17601
|
+
return toStringArray(readProjectSettings(repoRoot).disabledPlugins);
|
|
17602
|
+
}
|
|
17603
|
+
function setProjectDisabled(repoRoot, id, disabled) {
|
|
17604
|
+
updateProjectSettings2(repoRoot, (s) => {
|
|
17605
|
+
const cur = toStringArray(s.disabledPlugins);
|
|
17606
|
+
s.disabledPlugins = disabled ? [.../* @__PURE__ */ new Set([...cur, id])] : cur.filter((x) => x !== id);
|
|
17607
|
+
});
|
|
17608
|
+
}
|
|
17609
|
+
function disabledScope(id, lists) {
|
|
17610
|
+
if (lists.globalDisabled.includes(id)) return "global";
|
|
17611
|
+
if (lists.projectDisabled.includes(id)) return "project";
|
|
17612
|
+
return null;
|
|
17613
|
+
}
|
|
17614
|
+
function readEnablementLists(repoRoot) {
|
|
17615
|
+
return { globalDisabled: readGlobalDisabled(), projectDisabled: readProjectDisabled(repoRoot) };
|
|
17616
|
+
}
|
|
17617
|
+
var init_enablement = __esm({
|
|
17618
|
+
"src/plugins/enablement.ts"() {
|
|
17619
|
+
"use strict";
|
|
17620
|
+
init_global_config();
|
|
17621
|
+
init_project_settings_store();
|
|
17622
|
+
}
|
|
17623
|
+
});
|
|
17624
|
+
|
|
17625
|
+
// src/plugins/manifest.ts
|
|
17626
|
+
function parseManifest(raw2) {
|
|
17627
|
+
const parsed = PluginManifestSchema.safeParse(raw2);
|
|
17628
|
+
return parsed.success ? parsed.data : null;
|
|
17629
|
+
}
|
|
17630
|
+
var ContentTypeSchema, PluginPreferenceSchema, ConfigLabelColorSchema, ConfigLayoutItemSchema, PluginRequirementSchema, PluginProvisionStepSchema, PluginInstallSchema, PluginManifestSchema;
|
|
17631
|
+
var init_manifest2 = __esm({
|
|
17632
|
+
"src/plugins/manifest.ts"() {
|
|
17633
|
+
"use strict";
|
|
17634
|
+
init_zod();
|
|
17635
|
+
ContentTypeSchema = external_exports.object({
|
|
17636
|
+
extensions: external_exports.array(external_exports.string()).optional(),
|
|
17637
|
+
mimeTypes: external_exports.array(external_exports.string()).optional()
|
|
17638
|
+
}).loose();
|
|
17639
|
+
PluginPreferenceSchema = external_exports.object({
|
|
17640
|
+
key: external_exports.string().min(1),
|
|
17641
|
+
label: external_exports.string().min(1),
|
|
17642
|
+
type: external_exports.enum(["string", "number", "boolean", "select"]),
|
|
17643
|
+
/** Stored as a string; the plugin coerces. */
|
|
17644
|
+
default: external_exports.string().optional(),
|
|
17645
|
+
description: external_exports.string().optional(),
|
|
17646
|
+
/** `select` options (value === label unless an object form is used later). */
|
|
17647
|
+
options: external_exports.array(external_exports.string()).optional(),
|
|
17648
|
+
/** Per-project vs global storage (default global). Ignored for secrets
|
|
17649
|
+
* (always keychain). */
|
|
17650
|
+
scope: external_exports.enum(["global", "project"]).optional(),
|
|
17651
|
+
/** Secret prefs are stored in the OS keychain, never in config (GB-1054). */
|
|
17652
|
+
secret: external_exports.boolean().optional()
|
|
17653
|
+
}).loose();
|
|
17654
|
+
ConfigLabelColorSchema = external_exports.enum(["default", "success", "error", "warning", "transient"]);
|
|
17655
|
+
ConfigLayoutItemSchema = external_exports.lazy(
|
|
17656
|
+
() => external_exports.object({
|
|
17657
|
+
type: external_exports.enum(["preference", "divider", "spacer", "label", "button", "group"]),
|
|
17658
|
+
key: external_exports.string().optional(),
|
|
17659
|
+
id: external_exports.string().optional(),
|
|
17660
|
+
text: external_exports.string().optional(),
|
|
17661
|
+
color: ConfigLabelColorSchema.optional(),
|
|
17662
|
+
label: external_exports.string().optional(),
|
|
17663
|
+
action: external_exports.string().optional(),
|
|
17664
|
+
style: external_exports.string().optional(),
|
|
17665
|
+
title: external_exports.string().optional(),
|
|
17666
|
+
collapsed: external_exports.boolean().optional(),
|
|
17667
|
+
items: external_exports.array(ConfigLayoutItemSchema).optional()
|
|
17668
|
+
}).loose()
|
|
17669
|
+
);
|
|
17670
|
+
PluginRequirementSchema = external_exports.object({
|
|
17671
|
+
id: external_exports.string().min(1),
|
|
17672
|
+
label: external_exports.string().min(1),
|
|
17673
|
+
/** Executable whose presence proves the requirement (run with `checkArgs`). */
|
|
17674
|
+
command: external_exports.string().min(1),
|
|
17675
|
+
/** Args for the presence check (default `['--version']`). */
|
|
17676
|
+
checkArgs: external_exports.array(external_exports.string()).optional(),
|
|
17677
|
+
/** Human remediation shown when the requirement is missing. */
|
|
17678
|
+
hint: external_exports.string().min(1),
|
|
17679
|
+
docUrl: external_exports.string().optional()
|
|
17680
|
+
}).loose();
|
|
17681
|
+
PluginProvisionStepSchema = external_exports.union([
|
|
17682
|
+
external_exports.object({
|
|
17683
|
+
kind: external_exports.literal("fetch"),
|
|
17684
|
+
url: external_exports.string().min(1),
|
|
17685
|
+
/** Destination filename inside the install dir. */
|
|
17686
|
+
dest: external_exports.string().min(1),
|
|
17687
|
+
/** Optional expected sha-256 (hex) of the downloaded bytes. */
|
|
17688
|
+
sha256: external_exports.string().optional()
|
|
17689
|
+
}).loose(),
|
|
17690
|
+
external_exports.object({
|
|
17691
|
+
kind: external_exports.literal("npm-install"),
|
|
17692
|
+
packages: external_exports.array(external_exports.string().min(1)).min(1),
|
|
17693
|
+
/** Requirement id whose command must be present to run this (default `npm`). */
|
|
17694
|
+
requires: external_exports.string().optional(),
|
|
17695
|
+
note: external_exports.string().optional()
|
|
17696
|
+
}).loose()
|
|
17697
|
+
]);
|
|
17698
|
+
PluginInstallSchema = external_exports.object({
|
|
17699
|
+
requirements: external_exports.array(PluginRequirementSchema).optional(),
|
|
17700
|
+
provision: external_exports.array(PluginProvisionStepSchema).optional(),
|
|
17701
|
+
/** Fallback command to run by hand when auto-provisioning can't complete. */
|
|
17702
|
+
cliHint: external_exports.string().optional()
|
|
17703
|
+
}).loose();
|
|
17704
|
+
PluginManifestSchema = external_exports.object({
|
|
17705
|
+
id: external_exports.string().min(1),
|
|
17706
|
+
name: external_exports.string().min(1),
|
|
17707
|
+
version: external_exports.string().min(1),
|
|
17708
|
+
/** Entry module relative to the plugin dir; defaults to `index.js`. */
|
|
17709
|
+
entry: external_exports.string().min(1).optional(),
|
|
17710
|
+
description: external_exports.string().optional(),
|
|
17711
|
+
author: external_exports.string().optional(),
|
|
17712
|
+
/** The content types this plugin handles (informational for the UI; the
|
|
17713
|
+
* authoritative match lives in the registered renderer/differ). */
|
|
17714
|
+
contentTypes: external_exports.array(ContentTypeSchema).optional(),
|
|
17715
|
+
/** User-configurable preferences (doc 29 FR-29.12); rendered in Settings. */
|
|
17716
|
+
preferences: external_exports.array(PluginPreferenceSchema).optional(),
|
|
17717
|
+
/** Optional arrangement of the preferences into groups/dividers/labels/
|
|
17718
|
+
* buttons (doc 29 FR-29.18). Omitted → flat preference list. */
|
|
17719
|
+
configLayout: external_exports.array(ConfigLayoutItemSchema).optional(),
|
|
17720
|
+
/** `false` marks a **separately-installable** plugin (e.g. one with a system
|
|
17721
|
+
* requirement like Java): it's built + may ship in the bundle, but
|
|
17722
|
+
* `installBundledPlugins` does NOT auto-install it — the user opts in
|
|
17723
|
+
* (GB-1046). Defaults to auto-install (true) when omitted. */
|
|
17724
|
+
autoInstall: external_exports.boolean().optional(),
|
|
17725
|
+
/** How to install an opt-in plugin from the UI (doc 29 §29.2, GB-1069):
|
|
17726
|
+
* system requirements + auto-run provisioning steps. Absent → self-contained. */
|
|
17727
|
+
install: PluginInstallSchema.optional()
|
|
17728
|
+
}).loose();
|
|
17729
|
+
}
|
|
17730
|
+
});
|
|
17731
|
+
|
|
17732
|
+
// src/plugins/registry.ts
|
|
17733
|
+
import { extname } from "path";
|
|
17734
|
+
function matchSpecificity(match, input) {
|
|
17735
|
+
if (match.sniff && input.bytes.length > 0) {
|
|
17736
|
+
try {
|
|
17737
|
+
if (match.sniff(input.bytes)) return 3;
|
|
17738
|
+
} catch {
|
|
17739
|
+
}
|
|
17740
|
+
}
|
|
17741
|
+
if (match.mimeTypes && input.mime !== void 0 && match.mimeTypes.includes(input.mime)) return 2;
|
|
17742
|
+
const ext = extname(input.path).toLowerCase();
|
|
17743
|
+
if (ext !== "" && match.extensions?.some((e) => e.toLowerCase() === ext) === true) return 1;
|
|
17744
|
+
return 0;
|
|
17745
|
+
}
|
|
17746
|
+
function pathMatches(match, path, mime) {
|
|
17747
|
+
const ext = extname(path).toLowerCase();
|
|
17748
|
+
if (ext !== "" && match.extensions?.some((e) => e.toLowerCase() === ext) === true) return true;
|
|
17749
|
+
if (mime !== void 0 && match.mimeTypes?.includes(mime) === true) return true;
|
|
17750
|
+
return false;
|
|
17751
|
+
}
|
|
17752
|
+
function pickBest(handlers, input) {
|
|
17753
|
+
let best;
|
|
17754
|
+
let bestScore = 0;
|
|
17755
|
+
for (const h of handlers) {
|
|
17756
|
+
const spec = matchSpecificity(h.match, input);
|
|
17757
|
+
if (spec === 0) continue;
|
|
17758
|
+
const score = (h.priority ?? 0) * 10 + spec;
|
|
17759
|
+
if (score > bestScore) {
|
|
17760
|
+
bestScore = score;
|
|
17761
|
+
best = h;
|
|
17762
|
+
}
|
|
17763
|
+
}
|
|
17764
|
+
return best;
|
|
17765
|
+
}
|
|
17766
|
+
var ContentPluginRegistry;
|
|
17767
|
+
var init_registry = __esm({
|
|
17768
|
+
"src/plugins/registry.ts"() {
|
|
17769
|
+
"use strict";
|
|
17770
|
+
ContentPluginRegistry = class {
|
|
17771
|
+
renderers = [];
|
|
17772
|
+
differs = [];
|
|
17773
|
+
imageDecoders = [];
|
|
17774
|
+
addRenderers(rs) {
|
|
17775
|
+
if (rs) this.renderers.push(...rs);
|
|
17776
|
+
}
|
|
17777
|
+
addDiffers(ds) {
|
|
17778
|
+
if (ds) this.differs.push(...ds);
|
|
17779
|
+
}
|
|
17780
|
+
addImageDecoders(ds) {
|
|
17781
|
+
if (ds) this.imageDecoders.push(...ds);
|
|
17782
|
+
}
|
|
17783
|
+
/** The best-matching renderer for a single content blob, or undefined. */
|
|
17784
|
+
findRenderer(input) {
|
|
17785
|
+
return pickBest(this.renderers, input);
|
|
17786
|
+
}
|
|
17787
|
+
/** The best-matching differ for a pair; matching keys off the new side. */
|
|
17788
|
+
findDiffer(input) {
|
|
17789
|
+
return pickBest(this.differs, input.new);
|
|
17790
|
+
}
|
|
17791
|
+
/** The best-matching image decoder (bytes → RGBA) for a blob, or undefined. */
|
|
17792
|
+
findImageDecoder(input) {
|
|
17793
|
+
return pickBest(this.imageDecoders, input);
|
|
17794
|
+
}
|
|
17795
|
+
/** Cheap path pre-check: could any renderer/differ handle this path (by ext /
|
|
17796
|
+
* MIME)? Skips sniff so no bytes are read. */
|
|
17797
|
+
mightHandleByPath(path, mime) {
|
|
17798
|
+
for (const h of this.renderers) if (pathMatches(h.match, path, mime)) return true;
|
|
17799
|
+
for (const h of this.differs) if (pathMatches(h.match, path, mime)) return true;
|
|
17800
|
+
return false;
|
|
17801
|
+
}
|
|
17802
|
+
get rendererCount() {
|
|
17803
|
+
return this.renderers.length;
|
|
17804
|
+
}
|
|
17805
|
+
get differCount() {
|
|
17806
|
+
return this.differs.length;
|
|
17807
|
+
}
|
|
17808
|
+
get imageDecoderCount() {
|
|
17809
|
+
return this.imageDecoders.length;
|
|
17810
|
+
}
|
|
17811
|
+
};
|
|
17812
|
+
}
|
|
17813
|
+
});
|
|
17814
|
+
|
|
17815
|
+
// src/plugins/settings.ts
|
|
17816
|
+
function secretAccount(pluginId, key) {
|
|
17817
|
+
return `plugin-${pluginId}-${key}`;
|
|
17818
|
+
}
|
|
17819
|
+
function coerceSettingsMap(raw2) {
|
|
17820
|
+
const out = {};
|
|
17821
|
+
if (raw2 === null || typeof raw2 !== "object") return out;
|
|
17822
|
+
for (const [pluginId, byKey] of Object.entries(raw2)) {
|
|
17823
|
+
if (byKey === null || typeof byKey !== "object") continue;
|
|
17824
|
+
const entry = {};
|
|
17825
|
+
for (const [k, v] of Object.entries(byKey)) {
|
|
17826
|
+
if (typeof v === "string") entry[k] = v;
|
|
17827
|
+
}
|
|
17828
|
+
out[pluginId] = entry;
|
|
17829
|
+
}
|
|
17830
|
+
return out;
|
|
17831
|
+
}
|
|
17832
|
+
function prefFor(manifest, key) {
|
|
17833
|
+
return manifest.preferences?.find((p) => p.key === key);
|
|
17834
|
+
}
|
|
17835
|
+
function scopeOf(pref) {
|
|
17836
|
+
return pref?.scope === "project" ? "project" : "global";
|
|
17837
|
+
}
|
|
17838
|
+
function readGlobalSettings() {
|
|
17839
|
+
return coerceSettingsMap(readGlobalConfig().pluginSettings);
|
|
17840
|
+
}
|
|
17841
|
+
function readProjectSettingsMap(repoRoot) {
|
|
17842
|
+
return coerceSettingsMap(readProjectSettings(repoRoot).pluginSettings);
|
|
17843
|
+
}
|
|
17844
|
+
function lookup(map2, pluginId, key) {
|
|
17845
|
+
return map2[pluginId]?.[key];
|
|
17846
|
+
}
|
|
17847
|
+
function readPluginSetting(manifest, repoRoot, key) {
|
|
17848
|
+
const pref = prefFor(manifest, key);
|
|
17849
|
+
if (pref?.secret === true) {
|
|
17850
|
+
return getSecretFromKeychain(secretAccount(manifest.id, key)) ?? pref.default ?? null;
|
|
17851
|
+
}
|
|
17852
|
+
const map2 = scopeOf(pref) === "project" ? readProjectSettingsMap(repoRoot) : readGlobalSettings();
|
|
17853
|
+
const stored = lookup(map2, manifest.id, key);
|
|
17854
|
+
if (stored !== void 0) return stored;
|
|
17855
|
+
return pref?.default ?? null;
|
|
17856
|
+
}
|
|
17857
|
+
function writePluginSetting(manifest, repoRoot, key, value) {
|
|
17858
|
+
const pref = prefFor(manifest, key);
|
|
17859
|
+
if (pref?.secret === true) {
|
|
17860
|
+
if (value === "") deleteSecretFromKeychain(secretAccount(manifest.id, key));
|
|
17861
|
+
else saveSecretToKeychain(secretAccount(manifest.id, key), value, `Glassbox plugin: ${manifest.id}`);
|
|
17862
|
+
return;
|
|
17863
|
+
}
|
|
17864
|
+
if (scopeOf(pref) === "project") {
|
|
17865
|
+
updateProjectSettings2(repoRoot, (s) => {
|
|
17866
|
+
const map2 = coerceSettingsMap(s.pluginSettings);
|
|
17867
|
+
map2[manifest.id] = { ...map2[manifest.id], [key]: value };
|
|
17868
|
+
s.pluginSettings = map2;
|
|
17869
|
+
});
|
|
17870
|
+
} else {
|
|
17871
|
+
updateGlobalConfig((cfg) => {
|
|
17872
|
+
const map2 = coerceSettingsMap(cfg.pluginSettings);
|
|
17873
|
+
map2[manifest.id] = { ...map2[manifest.id], [key]: value };
|
|
17874
|
+
return { ...cfg, pluginSettings: map2 };
|
|
17875
|
+
});
|
|
17876
|
+
}
|
|
17877
|
+
}
|
|
17878
|
+
function readPluginPreferenceDisplay(manifest, repoRoot) {
|
|
17879
|
+
const values = {};
|
|
17880
|
+
const secretConfigured = [];
|
|
17881
|
+
for (const p of manifest.preferences ?? []) {
|
|
17882
|
+
if (p.secret === true) {
|
|
17883
|
+
const stored = getSecretFromKeychain(secretAccount(manifest.id, p.key));
|
|
17884
|
+
if (stored !== null && stored !== "") secretConfigured.push(p.key);
|
|
17885
|
+
values[p.key] = "";
|
|
17886
|
+
} else {
|
|
17887
|
+
values[p.key] = readPluginSetting(manifest, repoRoot, p.key) ?? "";
|
|
17888
|
+
}
|
|
17889
|
+
}
|
|
17890
|
+
return { values, secretConfigured };
|
|
17891
|
+
}
|
|
17892
|
+
var init_settings = __esm({
|
|
17893
|
+
"src/plugins/settings.ts"() {
|
|
17894
|
+
"use strict";
|
|
17895
|
+
init_keychain();
|
|
17896
|
+
init_global_config();
|
|
17897
|
+
init_project_settings_store();
|
|
17898
|
+
}
|
|
17899
|
+
});
|
|
17900
|
+
|
|
17901
|
+
// src/plugins/loader.ts
|
|
17902
|
+
import { existsSync as existsSync11, readdirSync as readdirSync2, readFileSync as readFileSync10, statSync as statSync3 } from "fs";
|
|
17903
|
+
import { join as join11 } from "path";
|
|
17904
|
+
import { pathToFileURL } from "url";
|
|
17905
|
+
function getConfigLabelOverride(pluginId, labelId) {
|
|
17906
|
+
return configLabelOverrides.get(`${pluginId}:${labelId}`);
|
|
17907
|
+
}
|
|
17908
|
+
function clearConfigLabelOverrides(pluginId) {
|
|
17909
|
+
const prefix = `${pluginId}:`;
|
|
17910
|
+
for (const key of configLabelOverrides.keys()) {
|
|
17911
|
+
if (key.startsWith(prefix)) configLabelOverrides.delete(key);
|
|
17912
|
+
}
|
|
17913
|
+
}
|
|
17914
|
+
function getPluginUIElements() {
|
|
17915
|
+
return Array.from(pluginUIElements.entries()).map(([pluginId, elements]) => ({ pluginId, elements }));
|
|
17916
|
+
}
|
|
17917
|
+
function clearPluginUIElements(pluginId) {
|
|
17918
|
+
pluginUIElements.delete(pluginId);
|
|
17919
|
+
}
|
|
17920
|
+
function clearAllPluginUIElements() {
|
|
17921
|
+
pluginUIElements.clear();
|
|
17922
|
+
}
|
|
17923
|
+
function pluginsDir() {
|
|
17924
|
+
return join11(GLOBAL_CONFIG_DIR, "plugins");
|
|
17925
|
+
}
|
|
17926
|
+
function discoverPluginDirs(root = pluginsDir()) {
|
|
17927
|
+
if (!existsSync11(root)) return [];
|
|
17928
|
+
let entries;
|
|
17929
|
+
try {
|
|
17930
|
+
entries = readdirSync2(root);
|
|
17931
|
+
} catch {
|
|
17932
|
+
return [];
|
|
17933
|
+
}
|
|
17934
|
+
const out = [];
|
|
17935
|
+
for (const name of entries) {
|
|
17936
|
+
if (name.startsWith(".")) continue;
|
|
17937
|
+
const dir = join11(root, name);
|
|
17938
|
+
try {
|
|
17939
|
+
if (statSync3(dir).isDirectory()) out.push(dir);
|
|
17940
|
+
} catch {
|
|
17941
|
+
}
|
|
17942
|
+
}
|
|
17943
|
+
return out.sort();
|
|
17944
|
+
}
|
|
17945
|
+
function readManifest(dir) {
|
|
17946
|
+
const manifestPath = join11(dir, "manifest.json");
|
|
17947
|
+
if (existsSync11(manifestPath)) {
|
|
17948
|
+
try {
|
|
17949
|
+
return parseManifest(JSON.parse(readFileSync10(manifestPath, "utf-8")));
|
|
17950
|
+
} catch {
|
|
17951
|
+
return null;
|
|
17952
|
+
}
|
|
17953
|
+
}
|
|
17954
|
+
const pkgPath = join11(dir, "package.json");
|
|
17955
|
+
if (existsSync11(pkgPath)) {
|
|
17956
|
+
try {
|
|
17957
|
+
const pkg = JSON.parse(readFileSync10(pkgPath, "utf-8"));
|
|
17958
|
+
const gb = pkg.glassbox;
|
|
17959
|
+
if (gb !== null && typeof gb === "object") {
|
|
17960
|
+
return parseManifest({ id: pkg.name, version: pkg.version, entry: pkg.main, ...gb });
|
|
17961
|
+
}
|
|
17962
|
+
} catch {
|
|
17963
|
+
return null;
|
|
17964
|
+
}
|
|
17965
|
+
}
|
|
17966
|
+
return null;
|
|
17967
|
+
}
|
|
17968
|
+
function makeContext(manifest, repoRoot) {
|
|
17969
|
+
const id = manifest.id;
|
|
17970
|
+
return {
|
|
17971
|
+
log: (level, message) => {
|
|
17972
|
+
const line = `[plugin:${id}] ${message}`;
|
|
17973
|
+
if (level === "error") console.error(line);
|
|
17974
|
+
else if (level === "warn") console.warn(line);
|
|
17975
|
+
else console.log(line);
|
|
17976
|
+
},
|
|
17977
|
+
getSetting: (key) => Promise.resolve(readPluginSetting(manifest, repoRoot, key)),
|
|
17978
|
+
setSetting: (key, value) => {
|
|
17979
|
+
writePluginSetting(manifest, repoRoot, key, value);
|
|
17980
|
+
return Promise.resolve();
|
|
17981
|
+
},
|
|
17982
|
+
updateConfigLabel: (labelId, text, color) => {
|
|
17983
|
+
configLabelOverrides.set(`${id}:${labelId}`, { text, color });
|
|
17984
|
+
},
|
|
17985
|
+
registerUI: (elements) => {
|
|
17986
|
+
pluginUIElements.set(id, elements);
|
|
17987
|
+
}
|
|
17988
|
+
};
|
|
17989
|
+
}
|
|
17990
|
+
async function loadPluginDir(dir, registry3, isEnabled = ALWAYS_ENABLED, repoRoot = "") {
|
|
17991
|
+
const manifest = readManifest(dir);
|
|
17992
|
+
if (manifest === null) {
|
|
17993
|
+
return { id: dir, dir, manifest: null, status: "error", error: "invalid or missing manifest" };
|
|
17994
|
+
}
|
|
17995
|
+
const enablement = isEnabled(manifest.id);
|
|
17996
|
+
if (enablement.disabled) {
|
|
17997
|
+
return { id: manifest.id, dir, manifest, status: "disabled", disabledScope: enablement.scope };
|
|
17998
|
+
}
|
|
17999
|
+
try {
|
|
18000
|
+
const entry = join11(dir, manifest.entry ?? "index.js");
|
|
18001
|
+
if (!existsSync11(entry)) throw new Error(`entry not found: ${manifest.entry ?? "index.js"}`);
|
|
18002
|
+
const mod = await import(pathToFileURL(entry).href);
|
|
18003
|
+
const plugin = mod.default ?? (typeof mod.activate === "function" ? { activate: mod.activate, onAction: mod.onAction } : void 0);
|
|
18004
|
+
if (plugin === void 0 || typeof plugin.activate !== "function") {
|
|
18005
|
+
throw new Error("plugin exports no activate()");
|
|
18006
|
+
}
|
|
18007
|
+
const context = makeContext(manifest, repoRoot);
|
|
18008
|
+
const registration = await plugin.activate(context) ?? void 0;
|
|
18009
|
+
if (registration !== void 0) {
|
|
18010
|
+
registry3.addRenderers(registration.renderers);
|
|
18011
|
+
registry3.addDiffers(registration.differs);
|
|
18012
|
+
registry3.addImageDecoders(registration.imageDecoders);
|
|
18013
|
+
}
|
|
18014
|
+
return { id: manifest.id, dir, manifest, status: "loaded", registration, instance: plugin, context };
|
|
18015
|
+
} catch (e) {
|
|
18016
|
+
return { id: manifest.id, dir, manifest, status: "error", error: e instanceof Error ? e.message : String(e) };
|
|
18017
|
+
}
|
|
18018
|
+
}
|
|
18019
|
+
async function loadAllPlugins(root = pluginsDir(), isEnabled = ALWAYS_ENABLED, repoRoot = "") {
|
|
18020
|
+
const registry3 = new ContentPluginRegistry();
|
|
18021
|
+
if (!PLUGINS_ENABLED) return { registry: registry3, loaded: [] };
|
|
18022
|
+
const loaded = [];
|
|
18023
|
+
for (const dir of discoverPluginDirs(root)) {
|
|
18024
|
+
loaded.push(await loadPluginDir(dir, registry3, isEnabled, repoRoot));
|
|
18025
|
+
}
|
|
18026
|
+
return { registry: registry3, loaded };
|
|
18027
|
+
}
|
|
18028
|
+
var configLabelOverrides, pluginUIElements, ALWAYS_ENABLED;
|
|
18029
|
+
var init_loader = __esm({
|
|
18030
|
+
"src/plugins/loader.ts"() {
|
|
18031
|
+
"use strict";
|
|
18032
|
+
init_feature_flags();
|
|
18033
|
+
init_global_config();
|
|
18034
|
+
init_manifest2();
|
|
18035
|
+
init_registry();
|
|
18036
|
+
init_settings();
|
|
18037
|
+
configLabelOverrides = /* @__PURE__ */ new Map();
|
|
18038
|
+
pluginUIElements = /* @__PURE__ */ new Map();
|
|
18039
|
+
ALWAYS_ENABLED = () => ({ disabled: false });
|
|
18040
|
+
}
|
|
18041
|
+
});
|
|
18042
|
+
|
|
18043
|
+
// src/plugins/install.ts
|
|
18044
|
+
import { createHash as createHash3 } from "crypto";
|
|
18045
|
+
import { cpSync, existsSync as existsSync12, mkdirSync as mkdirSync9, readdirSync as readdirSync3, readFileSync as readFileSync11, rmSync as rmSync6, statSync as statSync4, symlinkSync, writeFileSync as writeFileSync10 } from "fs";
|
|
18046
|
+
import { dirname as dirname7, join as join12 } from "path";
|
|
18047
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
18048
|
+
function dismissedPathFor(userDir) {
|
|
18049
|
+
return join12(dirname7(userDir), DISMISSED_FILE);
|
|
18050
|
+
}
|
|
18051
|
+
function bundledPluginsDir() {
|
|
18052
|
+
const override = process.env.GLASSBOX_BUNDLED_PLUGINS_DIR;
|
|
18053
|
+
if (override !== void 0 && override.trim() !== "") return override;
|
|
18054
|
+
const sibling = join12(dirname7(fileURLToPath2(import.meta.url)), "plugins");
|
|
18055
|
+
if (existsSync12(sibling)) return sibling;
|
|
18056
|
+
return join12(process.cwd(), "dist", "plugins");
|
|
18057
|
+
}
|
|
18058
|
+
function discoverBundledPlugins(bundledDir = bundledPluginsDir()) {
|
|
18059
|
+
if (!existsSync12(bundledDir)) return [];
|
|
18060
|
+
let entries;
|
|
18061
|
+
try {
|
|
18062
|
+
entries = readdirSync3(bundledDir);
|
|
18063
|
+
} catch {
|
|
18064
|
+
return [];
|
|
18065
|
+
}
|
|
18066
|
+
const out = [];
|
|
18067
|
+
for (const name of entries.sort()) {
|
|
18068
|
+
const dir = join12(bundledDir, name);
|
|
18069
|
+
try {
|
|
18070
|
+
if (!statSync4(dir).isDirectory()) continue;
|
|
18071
|
+
const manifest = readManifest(dir);
|
|
18072
|
+
if (manifest !== null) out.push({ dir, manifest });
|
|
18073
|
+
} catch {
|
|
18074
|
+
}
|
|
18075
|
+
}
|
|
18076
|
+
return out;
|
|
18077
|
+
}
|
|
18078
|
+
function readDismissed(userDir = pluginsDir()) {
|
|
18079
|
+
try {
|
|
18080
|
+
const raw2 = JSON.parse(readFileSync11(dismissedPathFor(userDir), "utf-8"));
|
|
18081
|
+
return Array.isArray(raw2) ? raw2.filter((x) => typeof x === "string") : [];
|
|
18082
|
+
} catch {
|
|
18083
|
+
return [];
|
|
18084
|
+
}
|
|
17271
18085
|
}
|
|
17272
|
-
function
|
|
17273
|
-
|
|
17274
|
-
|
|
17275
|
-
|
|
17276
|
-
writeFileSync4(join5(dir, blobName(fileId, side)), bytes);
|
|
18086
|
+
function writeDismissed(ids, userDir) {
|
|
18087
|
+
const path = dismissedPathFor(userDir);
|
|
18088
|
+
mkdirSync9(dirname7(path), { recursive: true });
|
|
18089
|
+
writeFileSync10(path, JSON.stringify([...new Set(ids)], null, 2), "utf-8");
|
|
17277
18090
|
}
|
|
17278
|
-
function
|
|
17279
|
-
const
|
|
17280
|
-
|
|
18091
|
+
function compareVersions(a, b) {
|
|
18092
|
+
const pa = a.split(".").map((p) => parseInt(p, 10) || 0);
|
|
18093
|
+
const pb = b.split(".").map((p) => parseInt(p, 10) || 0);
|
|
18094
|
+
for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
|
|
18095
|
+
const d = (pa[i] ?? 0) - (pb[i] ?? 0);
|
|
18096
|
+
if (d !== 0) return d > 0 ? 1 : -1;
|
|
18097
|
+
}
|
|
18098
|
+
return 0;
|
|
18099
|
+
}
|
|
18100
|
+
function hashPluginDir(dir) {
|
|
18101
|
+
const h = createHash3("sha1");
|
|
18102
|
+
const walk = (d, rel) => {
|
|
18103
|
+
for (const name of readdirSync3(d).sort()) {
|
|
18104
|
+
const abs = join12(d, name);
|
|
18105
|
+
const r = rel === "" ? name : `${rel}/${name}`;
|
|
18106
|
+
if (statSync4(abs).isDirectory()) walk(abs, r);
|
|
18107
|
+
else {
|
|
18108
|
+
h.update(r);
|
|
18109
|
+
h.update(readFileSync11(abs));
|
|
18110
|
+
}
|
|
18111
|
+
}
|
|
18112
|
+
};
|
|
18113
|
+
walk(dir, "");
|
|
18114
|
+
return h.digest("hex");
|
|
18115
|
+
}
|
|
18116
|
+
function shouldInstall(src, dest, bundledVersion) {
|
|
18117
|
+
if (!existsSync12(dest)) return true;
|
|
18118
|
+
const destManifest = readManifest(dest);
|
|
18119
|
+
const destVersion = destManifest?.version ?? "0";
|
|
18120
|
+
const cmp = compareVersions(bundledVersion, destVersion);
|
|
18121
|
+
if (cmp > 0) return true;
|
|
18122
|
+
if (cmp < 0) return false;
|
|
17281
18123
|
try {
|
|
17282
|
-
return
|
|
18124
|
+
return hashPluginDir(src) !== hashPluginDir(dest);
|
|
18125
|
+
} catch {
|
|
18126
|
+
return false;
|
|
18127
|
+
}
|
|
18128
|
+
}
|
|
18129
|
+
function installBundledPlugins(opts) {
|
|
18130
|
+
const bundledDir = opts?.bundledDir ?? bundledPluginsDir();
|
|
18131
|
+
const userDir = opts?.userDir ?? pluginsDir();
|
|
18132
|
+
if (!existsSync12(bundledDir)) return;
|
|
18133
|
+
const dismissed = new Set(readDismissed(userDir));
|
|
18134
|
+
let entries;
|
|
18135
|
+
try {
|
|
18136
|
+
entries = readdirSync3(bundledDir);
|
|
18137
|
+
} catch {
|
|
18138
|
+
return;
|
|
18139
|
+
}
|
|
18140
|
+
for (const name of entries) {
|
|
18141
|
+
const src = join12(bundledDir, name);
|
|
18142
|
+
try {
|
|
18143
|
+
if (!statSync4(src).isDirectory()) continue;
|
|
18144
|
+
const manifest = readManifest(src);
|
|
18145
|
+
if (manifest === null || dismissed.has(manifest.id)) continue;
|
|
18146
|
+
if (manifest.autoInstall === false) continue;
|
|
18147
|
+
const dest = join12(userDir, manifest.id);
|
|
18148
|
+
if (!shouldInstall(src, dest, manifest.version)) continue;
|
|
18149
|
+
mkdirSync9(userDir, { recursive: true });
|
|
18150
|
+
rmSync6(dest, { recursive: true, force: true });
|
|
18151
|
+
cpSync(src, dest, { recursive: true });
|
|
18152
|
+
} catch {
|
|
18153
|
+
}
|
|
18154
|
+
}
|
|
18155
|
+
}
|
|
18156
|
+
function installPluginFromDisk(sourceDir, opts) {
|
|
18157
|
+
const manifest = readManifest(sourceDir);
|
|
18158
|
+
if (manifest === null) throw new Error("The selected folder is not a Glassbox plugin (no manifest.json).");
|
|
18159
|
+
const userDir = opts?.userDir ?? pluginsDir();
|
|
18160
|
+
mkdirSync9(userDir, { recursive: true });
|
|
18161
|
+
const dest = join12(userDir, manifest.id);
|
|
18162
|
+
rmSync6(dest, { recursive: true, force: true });
|
|
18163
|
+
try {
|
|
18164
|
+
symlinkSync(sourceDir, dest, "dir");
|
|
18165
|
+
} catch {
|
|
18166
|
+
cpSync(sourceDir, dest, { recursive: true });
|
|
18167
|
+
}
|
|
18168
|
+
writeDismissed(readDismissed(userDir).filter((x) => x !== manifest.id), userDir);
|
|
18169
|
+
return { id: manifest.id };
|
|
18170
|
+
}
|
|
18171
|
+
function undismissPlugin(id, userDir = pluginsDir()) {
|
|
18172
|
+
writeDismissed(readDismissed(userDir).filter((x) => x !== id), userDir);
|
|
18173
|
+
}
|
|
18174
|
+
function uninstallPlugin(id, opts) {
|
|
18175
|
+
const userDir = opts?.userDir ?? pluginsDir();
|
|
18176
|
+
rmSync6(join12(userDir, id), { recursive: true, force: true });
|
|
18177
|
+
writeDismissed([...readDismissed(userDir), id], userDir);
|
|
18178
|
+
}
|
|
18179
|
+
var DISMISSED_FILE;
|
|
18180
|
+
var init_install = __esm({
|
|
18181
|
+
"src/plugins/install.ts"() {
|
|
18182
|
+
"use strict";
|
|
18183
|
+
init_loader();
|
|
18184
|
+
DISMISSED_FILE = "dismissed-plugins.json";
|
|
18185
|
+
}
|
|
18186
|
+
});
|
|
18187
|
+
|
|
18188
|
+
// src/plugins/index.ts
|
|
18189
|
+
var plugins_exports = {};
|
|
18190
|
+
__export(plugins_exports, {
|
|
18191
|
+
__resetContentPluginsForTest: () => __resetContentPluginsForTest,
|
|
18192
|
+
__setContentRegistryForTest: () => __setContentRegistryForTest,
|
|
18193
|
+
decodeImageWithPlugin: () => decodeImageWithPlugin,
|
|
18194
|
+
describeInstalledPlugins: () => describeInstalledPlugins,
|
|
18195
|
+
diffContent: () => diffContent,
|
|
18196
|
+
getLoadedPlugins: () => getLoadedPlugins,
|
|
18197
|
+
getPluginManifest: () => getPluginManifest,
|
|
18198
|
+
initContentPlugins: () => initContentPlugins,
|
|
18199
|
+
listPluginUIElements: () => listPluginUIElements,
|
|
18200
|
+
mightHandleFile: () => mightHandleFile,
|
|
18201
|
+
notifyReviewCompleted: () => notifyReviewCompleted,
|
|
18202
|
+
notifyReviewCreated: () => notifyReviewCreated,
|
|
18203
|
+
persistPluginUIState: () => persistPluginUIState,
|
|
18204
|
+
pluginsEnabled: () => pluginsEnabled,
|
|
18205
|
+
reloadContentPlugins: () => reloadContentPlugins,
|
|
18206
|
+
renderContent: () => renderContent,
|
|
18207
|
+
runPluginAction: () => runPluginAction
|
|
18208
|
+
});
|
|
18209
|
+
function pluginsEnabled() {
|
|
18210
|
+
return PLUGINS_ENABLED;
|
|
18211
|
+
}
|
|
18212
|
+
function enablementCheckFor(repoRoot) {
|
|
18213
|
+
const lists = readEnablementLists(repoRoot);
|
|
18214
|
+
return (id) => {
|
|
18215
|
+
const scope = disabledScope(id, lists);
|
|
18216
|
+
return scope === null ? { disabled: false } : { disabled: true, scope };
|
|
18217
|
+
};
|
|
18218
|
+
}
|
|
18219
|
+
async function loadFor(repoRoot) {
|
|
18220
|
+
installBundledPlugins();
|
|
18221
|
+
clearAllPluginUIElements();
|
|
18222
|
+
const res = await loadAllPlugins(void 0, enablementCheckFor(repoRoot), repoRoot);
|
|
18223
|
+
registry2 = res.registry;
|
|
18224
|
+
loadedPlugins = res.loaded;
|
|
18225
|
+
currentRepoRoot = repoRoot;
|
|
18226
|
+
const failed = loadedPlugins.filter((p) => p.status === "error").length;
|
|
18227
|
+
const disabled = loadedPlugins.filter((p) => p.status === "disabled").length;
|
|
18228
|
+
if (loadedPlugins.length > 0) {
|
|
18229
|
+
const parts = [`${loadedPlugins.length - failed - disabled} loaded`];
|
|
18230
|
+
if (disabled > 0) parts.push(`${disabled} disabled`);
|
|
18231
|
+
if (failed > 0) parts.push(`${failed} failed`);
|
|
18232
|
+
console.log(` Content plugins: ${parts.join(", ")}.`);
|
|
18233
|
+
}
|
|
18234
|
+
}
|
|
18235
|
+
async function initContentPlugins(repoRoot = "") {
|
|
18236
|
+
if (initialized) return;
|
|
18237
|
+
initialized = true;
|
|
18238
|
+
if (!PLUGINS_ENABLED) return;
|
|
18239
|
+
try {
|
|
18240
|
+
await loadFor(repoRoot);
|
|
18241
|
+
} catch (e) {
|
|
18242
|
+
console.warn(` Content plugins failed to load: ${e instanceof Error ? e.message : String(e)}`);
|
|
18243
|
+
}
|
|
18244
|
+
}
|
|
18245
|
+
async function reloadContentPlugins(repoRoot = currentRepoRoot) {
|
|
18246
|
+
if (!PLUGINS_ENABLED) return;
|
|
18247
|
+
initialized = true;
|
|
18248
|
+
try {
|
|
18249
|
+
await loadFor(repoRoot);
|
|
18250
|
+
} catch (e) {
|
|
18251
|
+
console.warn(` Content plugins failed to reload: ${e instanceof Error ? e.message : String(e)}`);
|
|
18252
|
+
}
|
|
18253
|
+
}
|
|
18254
|
+
function getLoadedPlugins() {
|
|
18255
|
+
return loadedPlugins;
|
|
18256
|
+
}
|
|
18257
|
+
function resolveConfigLabels(manifest) {
|
|
18258
|
+
const out = {};
|
|
18259
|
+
const walk = (items) => {
|
|
18260
|
+
for (const item of items ?? []) {
|
|
18261
|
+
if (item.type === "label" && item.id !== void 0 && item.id !== "") {
|
|
18262
|
+
const override = getConfigLabelOverride(manifest.id, item.id);
|
|
18263
|
+
out[item.id] = override ?? { text: item.text ?? "", color: item.color };
|
|
18264
|
+
}
|
|
18265
|
+
if (item.type === "group") walk(item.items);
|
|
18266
|
+
}
|
|
18267
|
+
};
|
|
18268
|
+
walk(manifest.configLayout);
|
|
18269
|
+
return out;
|
|
18270
|
+
}
|
|
18271
|
+
function describeInstalledPlugins(repoRoot) {
|
|
18272
|
+
const globalDisabled = new Set(readGlobalDisabled());
|
|
18273
|
+
const projectDisabled = new Set(readProjectDisabled(repoRoot));
|
|
18274
|
+
return loadedPlugins.map((p) => {
|
|
18275
|
+
const extensions = (p.manifest?.contentTypes ?? []).flatMap((ct) => ct.extensions ?? []);
|
|
18276
|
+
const display = p.manifest ? readPluginPreferenceDisplay(p.manifest, repoRoot) : { values: {}, secretConfigured: [] };
|
|
18277
|
+
return {
|
|
18278
|
+
id: p.id,
|
|
18279
|
+
name: p.manifest?.name ?? p.id,
|
|
18280
|
+
version: p.manifest?.version ?? "0",
|
|
18281
|
+
extensions,
|
|
18282
|
+
status: p.status,
|
|
18283
|
+
error: p.error,
|
|
18284
|
+
enabled: p.status === "loaded",
|
|
18285
|
+
disabledScope: p.disabledScope,
|
|
18286
|
+
globalDisabled: globalDisabled.has(p.id),
|
|
18287
|
+
projectDisabled: projectDisabled.has(p.id),
|
|
18288
|
+
preferences: p.manifest?.preferences ?? [],
|
|
18289
|
+
preferenceValues: display.values,
|
|
18290
|
+
secretConfigured: display.secretConfigured,
|
|
18291
|
+
configLayout: p.manifest?.configLayout,
|
|
18292
|
+
configLabels: p.manifest ? resolveConfigLabels(p.manifest) : {}
|
|
18293
|
+
};
|
|
18294
|
+
});
|
|
18295
|
+
}
|
|
18296
|
+
function getPluginManifest(id) {
|
|
18297
|
+
return loadedPlugins.find((p) => p.id === id)?.manifest ?? void 0;
|
|
18298
|
+
}
|
|
18299
|
+
async function runPluginAction(id, actionId, value) {
|
|
18300
|
+
if (!PLUGINS_ENABLED) throw new Error("Plugins are disabled");
|
|
18301
|
+
const loaded = loadedPlugins.find((p) => p.id === id);
|
|
18302
|
+
if (loaded === void 0 || loaded.status !== "loaded" || loaded.instance === void 0 || loaded.context === void 0) {
|
|
18303
|
+
throw new Error("Plugin not active");
|
|
18304
|
+
}
|
|
18305
|
+
if (typeof loaded.instance.onAction !== "function") {
|
|
18306
|
+
throw new Error("Plugin does not handle actions");
|
|
18307
|
+
}
|
|
18308
|
+
return await loaded.instance.onAction(actionId, loaded.context, value);
|
|
18309
|
+
}
|
|
18310
|
+
function statefulKey(e) {
|
|
18311
|
+
return e.type === "toggle" || e.type === "switch" || e.type === "segmented-control" ? e.stateKey : void 0;
|
|
18312
|
+
}
|
|
18313
|
+
function elementAction(e) {
|
|
18314
|
+
return e.type === "link" ? void 0 : e.action;
|
|
18315
|
+
}
|
|
18316
|
+
function persistPluginUIState(id, actionId, value, repoRoot) {
|
|
18317
|
+
const manifest = loadedPlugins.find((p) => p.id === id && p.status === "loaded")?.manifest;
|
|
18318
|
+
if (manifest === null || manifest === void 0) return;
|
|
18319
|
+
const group = getPluginUIElements().find((g) => g.pluginId === id);
|
|
18320
|
+
const el = group?.elements.find((e) => elementAction(e) === actionId && statefulKey(e) !== void 0 && statefulKey(e) !== "");
|
|
18321
|
+
const key = el ? statefulKey(el) : void 0;
|
|
18322
|
+
if (key === void 0 || key === "") return;
|
|
18323
|
+
try {
|
|
18324
|
+
writePluginSetting(manifest, repoRoot, key, value);
|
|
18325
|
+
} catch {
|
|
18326
|
+
}
|
|
18327
|
+
}
|
|
18328
|
+
function toReviewHookInfo(review) {
|
|
18329
|
+
return { id: review.id, repoPath: review.repo_path, repoName: review.repo_name, mode: review.mode, status: review.status };
|
|
18330
|
+
}
|
|
18331
|
+
function toAnnotationHookInfo(a) {
|
|
18332
|
+
return { id: a.id, filePath: a.file_path, lineNumber: a.line_number, side: a.side, category: a.category, content: a.content };
|
|
18333
|
+
}
|
|
18334
|
+
async function notifyReviewCreated(review) {
|
|
18335
|
+
if (!PLUGINS_ENABLED) return;
|
|
18336
|
+
const info = toReviewHookInfo(review);
|
|
18337
|
+
for (const p of loadedPlugins) {
|
|
18338
|
+
const hook = p.registration?.reviewHooks?.onReviewCreated;
|
|
18339
|
+
if (p.status !== "loaded" || hook === void 0 || p.context === void 0) continue;
|
|
18340
|
+
try {
|
|
18341
|
+
await hook(info, p.context);
|
|
18342
|
+
} catch (e) {
|
|
18343
|
+
console.warn(` [plugin:${p.id}] onReviewCreated hook failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
18344
|
+
}
|
|
18345
|
+
}
|
|
18346
|
+
}
|
|
18347
|
+
async function notifyReviewCompleted(review, annotations, exportPath) {
|
|
18348
|
+
if (!PLUGINS_ENABLED) return;
|
|
18349
|
+
const info = toReviewHookInfo(review);
|
|
18350
|
+
const anns = annotations.map(toAnnotationHookInfo);
|
|
18351
|
+
for (const p of loadedPlugins) {
|
|
18352
|
+
const hook = p.registration?.reviewHooks?.onReviewCompleted;
|
|
18353
|
+
if (p.status !== "loaded" || hook === void 0 || p.context === void 0) continue;
|
|
18354
|
+
try {
|
|
18355
|
+
await hook(info, anns, exportPath, p.context);
|
|
18356
|
+
} catch (e) {
|
|
18357
|
+
console.warn(` [plugin:${p.id}] onReviewCompleted hook failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
18358
|
+
}
|
|
18359
|
+
}
|
|
18360
|
+
}
|
|
18361
|
+
function listPluginUIElements(repoRoot = "") {
|
|
18362
|
+
if (!PLUGINS_ENABLED) return [];
|
|
18363
|
+
const loaded = new Map(loadedPlugins.filter((p) => p.status === "loaded").map((p) => [p.id, p]));
|
|
18364
|
+
const out = [];
|
|
18365
|
+
for (const { pluginId, elements } of getPluginUIElements()) {
|
|
18366
|
+
const plugin = loaded.get(pluginId);
|
|
18367
|
+
if (plugin === void 0) continue;
|
|
18368
|
+
for (const el of elements) {
|
|
18369
|
+
const key = statefulKey(el);
|
|
18370
|
+
const value = key !== void 0 && key !== "" && plugin.manifest !== null ? readPluginSetting(plugin.manifest, repoRoot, key) ?? void 0 : void 0;
|
|
18371
|
+
out.push({ ...el, pluginId, value });
|
|
18372
|
+
}
|
|
18373
|
+
}
|
|
18374
|
+
return out;
|
|
18375
|
+
}
|
|
18376
|
+
function mightHandleFile(path, mime) {
|
|
18377
|
+
return PLUGINS_ENABLED && registry2.mightHandleByPath(path, mime);
|
|
18378
|
+
}
|
|
18379
|
+
function usableView(view) {
|
|
18380
|
+
if (view === void 0) return null;
|
|
18381
|
+
const svg = typeof view.svg === "string" && view.svg !== "";
|
|
18382
|
+
const html = typeof view.html === "string" && view.html !== "";
|
|
18383
|
+
return svg || html ? view : null;
|
|
18384
|
+
}
|
|
18385
|
+
async function renderContent(input) {
|
|
18386
|
+
if (!PLUGINS_ENABLED) return null;
|
|
18387
|
+
const renderer = registry2.findRenderer(input);
|
|
18388
|
+
if (renderer === void 0) return null;
|
|
18389
|
+
try {
|
|
18390
|
+
return usableView(await renderer.render(input));
|
|
17283
18391
|
} catch {
|
|
17284
18392
|
return null;
|
|
17285
18393
|
}
|
|
17286
18394
|
}
|
|
17287
|
-
function
|
|
18395
|
+
async function decodeImageWithPlugin(bytes, path, mime) {
|
|
18396
|
+
if (!PLUGINS_ENABLED) return null;
|
|
18397
|
+
const decoder = registry2.findImageDecoder({ bytes, path, mime });
|
|
18398
|
+
if (decoder === void 0) return null;
|
|
17288
18399
|
try {
|
|
17289
|
-
|
|
18400
|
+
return await decoder.decode({ bytes, path }) ?? null;
|
|
17290
18401
|
} catch {
|
|
18402
|
+
return null;
|
|
17291
18403
|
}
|
|
17292
18404
|
}
|
|
17293
|
-
|
|
17294
|
-
|
|
18405
|
+
async function diffContent(input) {
|
|
18406
|
+
if (!PLUGINS_ENABLED) return null;
|
|
18407
|
+
const differ = registry2.findDiffer(input);
|
|
18408
|
+
if (differ === void 0) return null;
|
|
18409
|
+
try {
|
|
18410
|
+
return usableView(await differ.diff(input));
|
|
18411
|
+
} catch {
|
|
18412
|
+
return null;
|
|
18413
|
+
}
|
|
18414
|
+
}
|
|
18415
|
+
function __setContentRegistryForTest(r, loaded = []) {
|
|
18416
|
+
registry2 = r;
|
|
18417
|
+
loadedPlugins = loaded;
|
|
18418
|
+
initialized = true;
|
|
18419
|
+
}
|
|
18420
|
+
function __resetContentPluginsForTest() {
|
|
18421
|
+
registry2 = new ContentPluginRegistry();
|
|
18422
|
+
loadedPlugins = [];
|
|
18423
|
+
initialized = false;
|
|
18424
|
+
}
|
|
18425
|
+
var registry2, loadedPlugins, initialized, currentRepoRoot;
|
|
18426
|
+
var init_plugins = __esm({
|
|
18427
|
+
"src/plugins/index.ts"() {
|
|
17295
18428
|
"use strict";
|
|
18429
|
+
init_feature_flags();
|
|
18430
|
+
init_enablement();
|
|
18431
|
+
init_install();
|
|
18432
|
+
init_loader();
|
|
18433
|
+
init_registry();
|
|
18434
|
+
init_settings();
|
|
18435
|
+
registry2 = new ContentPluginRegistry();
|
|
18436
|
+
loadedPlugins = [];
|
|
18437
|
+
initialized = false;
|
|
18438
|
+
currentRepoRoot = "";
|
|
17296
18439
|
}
|
|
17297
18440
|
});
|
|
17298
18441
|
|
|
@@ -17449,17 +18592,17 @@ __export(difftool_discovery_exports, {
|
|
|
17449
18592
|
tryAcquireStartingLock: () => tryAcquireStartingLock,
|
|
17450
18593
|
writeDiscovery: () => writeDiscovery
|
|
17451
18594
|
});
|
|
17452
|
-
import { existsSync as
|
|
18595
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync16, readFileSync as readFileSync22, rmSync as rmSync7, statSync as statSync7, writeFileSync as writeFileSync16 } from "fs";
|
|
17453
18596
|
import { homedir as homedir3 } from "os";
|
|
17454
|
-
import { join as
|
|
18597
|
+
import { join as join23 } from "path";
|
|
17455
18598
|
function difftoolHome() {
|
|
17456
|
-
return
|
|
18599
|
+
return join23(homedir3(), ".glassbox");
|
|
17457
18600
|
}
|
|
17458
18601
|
function discoveryPath(home = difftoolHome()) {
|
|
17459
|
-
return
|
|
18602
|
+
return join23(home, "difftool.lock");
|
|
17460
18603
|
}
|
|
17461
18604
|
function startingLockPath(home = difftoolHome()) {
|
|
17462
|
-
return
|
|
18605
|
+
return join23(home, "difftool-starting.lock");
|
|
17463
18606
|
}
|
|
17464
18607
|
function parseDiscovery(raw2) {
|
|
17465
18608
|
let parsed;
|
|
@@ -17473,35 +18616,35 @@ function parseDiscovery(raw2) {
|
|
|
17473
18616
|
}
|
|
17474
18617
|
function readDiscovery(home = difftoolHome()) {
|
|
17475
18618
|
const path = discoveryPath(home);
|
|
17476
|
-
if (!
|
|
18619
|
+
if (!existsSync19(path)) return null;
|
|
17477
18620
|
try {
|
|
17478
|
-
return parseDiscovery(
|
|
18621
|
+
return parseDiscovery(readFileSync22(path, "utf-8"));
|
|
17479
18622
|
} catch {
|
|
17480
18623
|
return null;
|
|
17481
18624
|
}
|
|
17482
18625
|
}
|
|
17483
18626
|
function writeDiscovery(port, home = difftoolHome()) {
|
|
17484
|
-
|
|
17485
|
-
|
|
18627
|
+
mkdirSync16(home, { recursive: true });
|
|
18628
|
+
writeFileSync16(discoveryPath(home), JSON.stringify({ port, pid: process.pid }));
|
|
17486
18629
|
}
|
|
17487
18630
|
function clearDiscovery(home = difftoolHome()) {
|
|
17488
18631
|
try {
|
|
17489
|
-
|
|
18632
|
+
rmSync7(discoveryPath(home), { force: true });
|
|
17490
18633
|
} catch {
|
|
17491
18634
|
}
|
|
17492
18635
|
}
|
|
17493
18636
|
function tryAcquireStartingLock(home = difftoolHome()) {
|
|
17494
|
-
|
|
18637
|
+
mkdirSync16(home, { recursive: true });
|
|
17495
18638
|
const path = startingLockPath(home);
|
|
17496
18639
|
try {
|
|
17497
|
-
|
|
18640
|
+
writeFileSync16(path, String(process.pid), { flag: "wx" });
|
|
17498
18641
|
return true;
|
|
17499
18642
|
} catch {
|
|
17500
18643
|
try {
|
|
17501
|
-
const ageMs = Date.now() -
|
|
18644
|
+
const ageMs = Date.now() - statSync7(path).mtimeMs;
|
|
17502
18645
|
if (ageMs > STARTING_LOCK_STALE_MS) {
|
|
17503
|
-
|
|
17504
|
-
|
|
18646
|
+
rmSync7(path, { force: true });
|
|
18647
|
+
writeFileSync16(path, String(process.pid), { flag: "wx" });
|
|
17505
18648
|
return true;
|
|
17506
18649
|
}
|
|
17507
18650
|
} catch {
|
|
@@ -17511,7 +18654,7 @@ function tryAcquireStartingLock(home = difftoolHome()) {
|
|
|
17511
18654
|
}
|
|
17512
18655
|
function releaseStartingLock(home = difftoolHome()) {
|
|
17513
18656
|
try {
|
|
17514
|
-
|
|
18657
|
+
rmSync7(startingLockPath(home), { force: true });
|
|
17515
18658
|
} catch {
|
|
17516
18659
|
}
|
|
17517
18660
|
}
|
|
@@ -21966,16 +23109,16 @@ __export(perceptual_diff_exports, {
|
|
|
21966
23109
|
decodeImage: () => decodeImage,
|
|
21967
23110
|
isIdentical: () => isIdentical
|
|
21968
23111
|
});
|
|
21969
|
-
import { readFileSync as
|
|
21970
|
-
import { extname as
|
|
23112
|
+
import { readFileSync as readFileSync23 } from "fs";
|
|
23113
|
+
import { extname as extname3 } from "path";
|
|
21971
23114
|
function decodeImage(path) {
|
|
21972
23115
|
let buf;
|
|
21973
23116
|
try {
|
|
21974
|
-
buf =
|
|
23117
|
+
buf = readFileSync23(path);
|
|
21975
23118
|
} catch {
|
|
21976
23119
|
return null;
|
|
21977
23120
|
}
|
|
21978
|
-
const ext =
|
|
23121
|
+
const ext = extname3(path).toLowerCase();
|
|
21979
23122
|
try {
|
|
21980
23123
|
if (ext === ".png") {
|
|
21981
23124
|
const png = import_pngjs.PNG.sync.read(buf);
|
|
@@ -21990,9 +23133,24 @@ function decodeImage(path) {
|
|
|
21990
23133
|
}
|
|
21991
23134
|
return null;
|
|
21992
23135
|
}
|
|
21993
|
-
function
|
|
21994
|
-
const
|
|
21995
|
-
|
|
23136
|
+
async function decodeWithFallback(path, pluginDecode) {
|
|
23137
|
+
const core = decodeImage(path);
|
|
23138
|
+
if (core !== null) return core;
|
|
23139
|
+
let buf;
|
|
23140
|
+
try {
|
|
23141
|
+
buf = readFileSync23(path);
|
|
23142
|
+
} catch {
|
|
23143
|
+
return null;
|
|
23144
|
+
}
|
|
23145
|
+
try {
|
|
23146
|
+
return await pluginDecode(new Uint8Array(buf), path);
|
|
23147
|
+
} catch {
|
|
23148
|
+
return null;
|
|
23149
|
+
}
|
|
23150
|
+
}
|
|
23151
|
+
async function comparePerceptual(actualPath, expectedPath, pluginDecode = decodeImageWithPlugin) {
|
|
23152
|
+
const actual = await decodeWithFallback(actualPath, pluginDecode);
|
|
23153
|
+
const expected = await decodeWithFallback(expectedPath, pluginDecode);
|
|
21996
23154
|
if (actual === null || expected === null) {
|
|
21997
23155
|
return { scorable: false, score: null, reason: "undecodable" };
|
|
21998
23156
|
}
|
|
@@ -22023,14 +23181,15 @@ var init_perceptual_diff = __esm({
|
|
|
22023
23181
|
import_jpeg_js = __toESM(require_jpeg_js(), 1);
|
|
22024
23182
|
init_pixelmatch();
|
|
22025
23183
|
import_pngjs = __toESM(require_png(), 1);
|
|
23184
|
+
init_plugins();
|
|
22026
23185
|
PERCEPTUAL_THRESHOLD = 0.1;
|
|
22027
23186
|
}
|
|
22028
23187
|
});
|
|
22029
23188
|
|
|
22030
23189
|
// src/cli.ts
|
|
22031
|
-
import { existsSync as
|
|
23190
|
+
import { existsSync as existsSync20, mkdirSync as mkdirSync17, realpathSync, statSync as statSync8 } from "fs";
|
|
22032
23191
|
import { tmpdir as tmpdir2 } from "os";
|
|
22033
|
-
import { basename as basename5, join as
|
|
23192
|
+
import { basename as basename5, join as join24, resolve as resolve12 } from "path";
|
|
22034
23193
|
|
|
22035
23194
|
// src/cli-subcommands.ts
|
|
22036
23195
|
import { resolve as resolve3 } from "path";
|
|
@@ -22093,162 +23252,25 @@ async function handleDifftoolRegistration(action, local, force) {
|
|
|
22093
23252
|
if (res.removed) {
|
|
22094
23253
|
console.log(`Glassbox unregistered as git difftool at --${scope} scope.`);
|
|
22095
23254
|
} else {
|
|
22096
|
-
console.log(`Nothing to unregister at --${scope} scope (current tool: ${status.tool ?? "none"}).`);
|
|
22097
|
-
}
|
|
22098
|
-
process.exit(0);
|
|
22099
|
-
}
|
|
22100
|
-
|
|
22101
|
-
// src/cli.ts
|
|
22102
|
-
init_connection();
|
|
22103
|
-
init_queries();
|
|
22104
|
-
init_debug();
|
|
22105
|
-
|
|
22106
|
-
// src/ai/config.ts
|
|
22107
|
-
init_zod();
|
|
22108
|
-
|
|
22109
|
-
// src/global-config.ts
|
|
22110
|
-
init_zod();
|
|
22111
|
-
import { chmodSync, existsSync as existsSync4, mkdirSync as mkdirSync5, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
22112
|
-
import { homedir } from "os";
|
|
22113
|
-
import { join as join4 } from "path";
|
|
22114
|
-
var GlobalConfigSchema = external_exports.record(external_exports.string(), external_exports.unknown());
|
|
22115
|
-
function resolveGlobalConfigDir() {
|
|
22116
|
-
const override = process.env.GLASSBOX_CONFIG_DIR;
|
|
22117
|
-
if (override !== void 0 && override.trim() !== "") return override;
|
|
22118
|
-
return join4(homedir(), ".glassbox");
|
|
22119
|
-
}
|
|
22120
|
-
var GLOBAL_CONFIG_DIR = resolveGlobalConfigDir();
|
|
22121
|
-
var GLOBAL_CONFIG_PATH = join4(GLOBAL_CONFIG_DIR, "config.json");
|
|
22122
|
-
function readGlobalConfig() {
|
|
22123
|
-
try {
|
|
22124
|
-
if (existsSync4(GLOBAL_CONFIG_PATH)) {
|
|
22125
|
-
const raw2 = JSON.parse(readFileSync3(GLOBAL_CONFIG_PATH, "utf-8"));
|
|
22126
|
-
const parsed = GlobalConfigSchema.safeParse(raw2);
|
|
22127
|
-
if (parsed.success) return parsed.data;
|
|
22128
|
-
}
|
|
22129
|
-
} catch {
|
|
22130
|
-
}
|
|
22131
|
-
return {};
|
|
22132
|
-
}
|
|
22133
|
-
function writeGlobalConfig(config2) {
|
|
22134
|
-
mkdirSync5(GLOBAL_CONFIG_DIR, { recursive: true });
|
|
22135
|
-
writeFileSync3(GLOBAL_CONFIG_PATH, JSON.stringify(config2, null, 2), "utf-8");
|
|
22136
|
-
try {
|
|
22137
|
-
chmodSync(GLOBAL_CONFIG_PATH, 384);
|
|
22138
|
-
} catch {
|
|
22139
|
-
}
|
|
22140
|
-
}
|
|
22141
|
-
function updateGlobalConfig(mutator) {
|
|
22142
|
-
const cfg = readGlobalConfig();
|
|
22143
|
-
const result = mutator(cfg);
|
|
22144
|
-
writeGlobalConfig(result === void 0 ? cfg : result);
|
|
22145
|
-
}
|
|
22146
|
-
|
|
22147
|
-
// src/ai/api-keys.ts
|
|
22148
|
-
import { spawnSync as spawnSync6 } from "child_process";
|
|
22149
|
-
|
|
22150
|
-
// src/ai/keychain.ts
|
|
22151
|
-
import { spawnSync as spawnSync5 } from "child_process";
|
|
22152
|
-
var WIN_CRED_READ_PS = `
|
|
22153
|
-
Add-Type -TypeDefinition @'
|
|
22154
|
-
using System;
|
|
22155
|
-
using System.Runtime.InteropServices;
|
|
22156
|
-
public class CredHelper {
|
|
22157
|
-
[DllImport("advapi32", SetLastError = true, CharSet = CharSet.Unicode)]
|
|
22158
|
-
static extern bool CredRead(string t, int type, int f, out IntPtr p);
|
|
22159
|
-
[DllImport("advapi32")]
|
|
22160
|
-
static extern void CredFree(IntPtr p);
|
|
22161
|
-
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
|
22162
|
-
struct CRED {
|
|
22163
|
-
public int Flags; public int Type; public string TargetName; public string Comment;
|
|
22164
|
-
public long LastWritten; public int CredentialBlobSize; public IntPtr CredentialBlob;
|
|
22165
|
-
public int Persist; public int AttributeCount; public IntPtr Attributes;
|
|
22166
|
-
public string TargetAlias; public string UserName;
|
|
22167
|
-
}
|
|
22168
|
-
public static string Read(string target) {
|
|
22169
|
-
IntPtr ptr;
|
|
22170
|
-
if (!CredRead(target, 1, 0, out ptr)) return "";
|
|
22171
|
-
CRED c = (CRED)Marshal.PtrToStructure(ptr, typeof(CRED));
|
|
22172
|
-
string r = Marshal.PtrToStringUni(c.CredentialBlob, c.CredentialBlobSize / 2);
|
|
22173
|
-
CredFree(ptr);
|
|
22174
|
-
return r;
|
|
22175
|
-
}
|
|
22176
|
-
}
|
|
22177
|
-
'@
|
|
22178
|
-
`;
|
|
22179
|
-
function winCredTarget(platform) {
|
|
22180
|
-
return `glassbox-${platform}-api-key`;
|
|
22181
|
-
}
|
|
22182
|
-
function getKeyFromKeychain(platform) {
|
|
22183
|
-
const os = process.platform;
|
|
22184
|
-
const account = `${platform}-api-key`;
|
|
22185
|
-
try {
|
|
22186
|
-
if (os === "darwin") {
|
|
22187
|
-
const r = spawnSync5("security", ["find-generic-password", "-s", "glassbox", "-a", account, "-w"], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
22188
|
-
const result = r.stdout.trim();
|
|
22189
|
-
return r.status === 0 && result !== "" ? result : null;
|
|
22190
|
-
}
|
|
22191
|
-
if (os === "linux") {
|
|
22192
|
-
const r = spawnSync5("secret-tool", ["lookup", "service", "glassbox", "account", account], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
22193
|
-
const result = r.stdout.trim();
|
|
22194
|
-
return r.status === 0 && result !== "" ? result : null;
|
|
22195
|
-
}
|
|
22196
|
-
if (os === "win32") {
|
|
22197
|
-
const target = winCredTarget(platform);
|
|
22198
|
-
const list = spawnSync5("cmdkey", ["/list:" + target], { encoding: "utf-8" });
|
|
22199
|
-
if (list.status !== 0 || (list.stdout || "").includes("* NONE *")) return null;
|
|
22200
|
-
const script = WIN_CRED_READ_PS + `Write-Output ([CredHelper]::Read('${target}'))`;
|
|
22201
|
-
const r = spawnSync5("powershell", ["-NoProfile", "-Command", "-"], { input: script, encoding: "utf-8" });
|
|
22202
|
-
const result = r.stdout.trim();
|
|
22203
|
-
return r.status === 0 && result !== "" ? result : null;
|
|
22204
|
-
}
|
|
22205
|
-
} catch {
|
|
22206
|
-
return null;
|
|
22207
|
-
}
|
|
22208
|
-
return null;
|
|
22209
|
-
}
|
|
22210
|
-
function assertSpawnOk(label, r) {
|
|
22211
|
-
if (r.error) throw new Error(`${label} failed: ${r.error.message}`);
|
|
22212
|
-
if (r.status !== 0) {
|
|
22213
|
-
const detail = (String(r.stderr) || String(r.stdout)).trim();
|
|
22214
|
-
throw new Error(`${label} failed (exit ${String(r.status)})${detail ? `: ${detail}` : ""}`);
|
|
22215
|
-
}
|
|
22216
|
-
}
|
|
22217
|
-
function saveKeyToKeychain(platform, key) {
|
|
22218
|
-
const os = process.platform;
|
|
22219
|
-
const account = `${platform}-api-key`;
|
|
22220
|
-
if (os === "darwin") {
|
|
22221
|
-
spawnSync5("security", ["delete-generic-password", "-s", "glassbox", "-a", account], { stdio: "pipe" });
|
|
22222
|
-
assertSpawnOk("Keychain write", spawnSync5("security", ["add-generic-password", "-s", "glassbox", "-a", account, "-w", key], { encoding: "utf-8" }));
|
|
22223
|
-
return;
|
|
22224
|
-
}
|
|
22225
|
-
if (os === "linux") {
|
|
22226
|
-
assertSpawnOk("System keyring write", spawnSync5("secret-tool", ["store", "--label=Glassbox API Key", "service", "glassbox", "account", account], { input: key, encoding: "utf-8" }));
|
|
22227
|
-
return;
|
|
22228
|
-
}
|
|
22229
|
-
if (os === "win32") {
|
|
22230
|
-
const target = winCredTarget(platform);
|
|
22231
|
-
const escapedKey = key.replace(/'/g, "''");
|
|
22232
|
-
const script = `cmdkey /generic:'${target}' /user:'glassbox' /pass:'${escapedKey}'`;
|
|
22233
|
-
assertSpawnOk("Credential Manager write", spawnSync5("powershell", ["-NoProfile", "-Command", "-"], { input: script, encoding: "utf-8" }));
|
|
22234
|
-
}
|
|
22235
|
-
}
|
|
22236
|
-
function isKeychainAvailable() {
|
|
22237
|
-
const os = process.platform;
|
|
22238
|
-
if (os === "darwin" || os === "win32") return true;
|
|
22239
|
-
if (os === "linux") {
|
|
22240
|
-
return spawnSync5("which", ["secret-tool"], { stdio: "pipe" }).status === 0;
|
|
23255
|
+
console.log(`Nothing to unregister at --${scope} scope (current tool: ${status.tool ?? "none"}).`);
|
|
22241
23256
|
}
|
|
22242
|
-
|
|
22243
|
-
}
|
|
22244
|
-
function getKeychainLabel() {
|
|
22245
|
-
const os = process.platform;
|
|
22246
|
-
if (os === "darwin") return "Keychain";
|
|
22247
|
-
if (os === "linux") return "System Keyring";
|
|
22248
|
-
if (os === "win32") return "Credential Manager";
|
|
22249
|
-
return "System Keychain";
|
|
23257
|
+
process.exit(0);
|
|
22250
23258
|
}
|
|
22251
23259
|
|
|
23260
|
+
// src/cli.ts
|
|
23261
|
+
init_connection();
|
|
23262
|
+
init_queries();
|
|
23263
|
+
init_debug();
|
|
23264
|
+
|
|
23265
|
+
// src/ai/config.ts
|
|
23266
|
+
init_zod();
|
|
23267
|
+
init_global_config();
|
|
23268
|
+
|
|
23269
|
+
// src/ai/api-keys.ts
|
|
23270
|
+
init_global_config();
|
|
23271
|
+
import { spawnSync as spawnSync6 } from "child_process";
|
|
23272
|
+
init_keychain();
|
|
23273
|
+
|
|
22252
23274
|
// src/ai/models.ts
|
|
22253
23275
|
init_zod();
|
|
22254
23276
|
var AIModelSchema = external_exports.object({
|
|
@@ -22340,6 +23362,7 @@ function resolveModelId(platform, modelId) {
|
|
|
22340
23362
|
}
|
|
22341
23363
|
|
|
22342
23364
|
// src/ai/api-keys.ts
|
|
23365
|
+
init_keychain();
|
|
22343
23366
|
function getKeyFromEnv(platform) {
|
|
22344
23367
|
const envName = ENV_KEY_NAMES[platform];
|
|
22345
23368
|
return process.env[envName] ?? null;
|
|
@@ -23952,10 +24975,10 @@ async function updateReviewDiffs(reviewId, newDiffs, headCommit) {
|
|
|
23952
24975
|
|
|
23953
24976
|
// src/server.ts
|
|
23954
24977
|
import { serve } from "@hono/node-server";
|
|
23955
|
-
import { existsSync as
|
|
23956
|
-
import { Hono as
|
|
23957
|
-
import { dirname as
|
|
23958
|
-
import { fileURLToPath as
|
|
24978
|
+
import { existsSync as existsSync16, readFileSync as readFileSync19 } from "fs";
|
|
24979
|
+
import { Hono as Hono21 } from "hono";
|
|
24980
|
+
import { dirname as dirname8, join as join20 } from "path";
|
|
24981
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
23959
24982
|
|
|
23960
24983
|
// src/channel-config.ts
|
|
23961
24984
|
init_zod();
|
|
@@ -24055,6 +25078,11 @@ async function triggerChannel(dataDir, message) {
|
|
|
24055
25078
|
}
|
|
24056
25079
|
}
|
|
24057
25080
|
|
|
25081
|
+
// src/server.ts
|
|
25082
|
+
init_queries();
|
|
25083
|
+
init_global_config();
|
|
25084
|
+
init_plugins();
|
|
25085
|
+
|
|
24058
25086
|
// src/routes/ai-api.ts
|
|
24059
25087
|
import { Hono as Hono3 } from "hono";
|
|
24060
25088
|
|
|
@@ -25296,39 +26324,7 @@ __export(ai_exports, {
|
|
|
25296
26324
|
startAnalysis: () => startAnalysis
|
|
25297
26325
|
});
|
|
25298
26326
|
init_zod();
|
|
25299
|
-
|
|
25300
|
-
// src/api/_runner.ts
|
|
25301
|
-
init_zod();
|
|
25302
|
-
var OkResponseSchema = external_exports.object({ ok: external_exports.literal(true) });
|
|
25303
|
-
function currentReviewId() {
|
|
25304
|
-
if (typeof document === "undefined") return "";
|
|
25305
|
-
return document.body.dataset.reviewId ?? "";
|
|
25306
|
-
}
|
|
25307
|
-
async function apiCall(responseSchema, path, opts = {}) {
|
|
25308
|
-
const separator = path.includes("?") ? "&" : "?";
|
|
25309
|
-
const url2 = "/api" + path + separator + "reviewId=" + encodeURIComponent(currentReviewId());
|
|
25310
|
-
const res = await fetch(url2, {
|
|
25311
|
-
headers: { "Content-Type": "application/json" },
|
|
25312
|
-
method: opts.method,
|
|
25313
|
-
body: opts.body !== void 0 ? JSON.stringify(opts.body) : void 0
|
|
25314
|
-
});
|
|
25315
|
-
const json2 = await res.json();
|
|
25316
|
-
const result = responseSchema.safeParse(json2);
|
|
25317
|
-
if (!result.success) {
|
|
25318
|
-
const summary = result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
|
|
25319
|
-
throw new Error(`API response from ${path} failed validation: ${summary}`);
|
|
25320
|
-
}
|
|
25321
|
-
return result.data;
|
|
25322
|
-
}
|
|
25323
|
-
function qs(params) {
|
|
25324
|
-
const entries = Object.entries(params).filter(([, v]) => v !== void 0 && v !== null);
|
|
25325
|
-
if (entries.length === 0) return "";
|
|
25326
|
-
const usp = new URLSearchParams();
|
|
25327
|
-
for (const [k, v] of entries) usp.set(k, String(v));
|
|
25328
|
-
return "?" + usp.toString();
|
|
25329
|
-
}
|
|
25330
|
-
|
|
25331
|
-
// src/api/ai.ts
|
|
26327
|
+
init_runner();
|
|
25332
26328
|
var KeySourceSchema = external_exports.enum(["env", "keychain", "config"]).nullable();
|
|
25333
26329
|
var KeyStorageSchema = external_exports.enum(["keychain", "config"]);
|
|
25334
26330
|
var GuidedReviewConfigShapeSchema = external_exports.object({
|
|
@@ -25541,6 +26537,7 @@ __export(annotations_exports, {
|
|
|
25541
26537
|
});
|
|
25542
26538
|
init_zod();
|
|
25543
26539
|
init_schemas3();
|
|
26540
|
+
init_runner();
|
|
25544
26541
|
var AnnotationCategorySchema = external_exports.enum([
|
|
25545
26542
|
"bug",
|
|
25546
26543
|
"fix",
|
|
@@ -25646,6 +26643,7 @@ __export(channel_exports, {
|
|
|
25646
26643
|
triggerChannel: () => triggerChannel2
|
|
25647
26644
|
});
|
|
25648
26645
|
init_zod();
|
|
26646
|
+
init_runner();
|
|
25649
26647
|
var GetChannelStatusRespSchema = external_exports.object({
|
|
25650
26648
|
enabled: external_exports.boolean(),
|
|
25651
26649
|
connected: external_exports.boolean()
|
|
@@ -25689,6 +26687,7 @@ __export(context_exports, {
|
|
|
25689
26687
|
getContextLines: () => getContextLines
|
|
25690
26688
|
});
|
|
25691
26689
|
init_zod();
|
|
26690
|
+
init_runner();
|
|
25692
26691
|
var ContextLineSchema = external_exports.object({
|
|
25693
26692
|
num: external_exports.number().int(),
|
|
25694
26693
|
content: external_exports.string()
|
|
@@ -25738,6 +26737,7 @@ __export(files_exports, {
|
|
|
25738
26737
|
});
|
|
25739
26738
|
init_zod();
|
|
25740
26739
|
init_schemas3();
|
|
26740
|
+
init_runner();
|
|
25741
26741
|
var FileStatusSchema = external_exports.enum(["pending", "reviewed"]);
|
|
25742
26742
|
var GroundTruthMetaSchema = external_exports.object({
|
|
25743
26743
|
label: external_exports.string().optional(),
|
|
@@ -25752,6 +26752,10 @@ var GroundTruthMetaSchema = external_exports.object({
|
|
|
25752
26752
|
});
|
|
25753
26753
|
var ListFilesRespSchema = external_exports.object({
|
|
25754
26754
|
files: external_exports.array(ReviewFileSchema),
|
|
26755
|
+
/** Ids of files a content plugin renders (doc 29, GB-1052) — the client shows
|
|
26756
|
+
* the Code/Rendered toggle and routes the Rendered view through the image
|
|
26757
|
+
* viewer for these, like an SVG file. */
|
|
26758
|
+
pluginRendered: external_exports.array(external_exports.string()).optional(),
|
|
25755
26759
|
annotationCounts: external_exports.record(external_exports.string(), external_exports.number()),
|
|
25756
26760
|
staleCounts: external_exports.record(external_exports.string(), external_exports.number()),
|
|
25757
26761
|
/** Keyed by review-file id; omitted/empty for non-ground-truth reviews. */
|
|
@@ -25809,6 +26813,7 @@ __export(image_exports, {
|
|
|
25809
26813
|
imageUrl: () => imageUrl
|
|
25810
26814
|
});
|
|
25811
26815
|
init_zod();
|
|
26816
|
+
init_runner();
|
|
25812
26817
|
var ImageSideSchema = external_exports.enum(["old", "new"]);
|
|
25813
26818
|
var GetImageMetadataReqSchema = external_exports.object({ fileId: external_exports.string() });
|
|
25814
26819
|
var GetImageMetadataRespSchema = external_exports.object({
|
|
@@ -25835,6 +26840,7 @@ __export(outline_exports, {
|
|
|
25835
26840
|
getOutline: () => getOutline
|
|
25836
26841
|
});
|
|
25837
26842
|
init_zod();
|
|
26843
|
+
init_runner();
|
|
25838
26844
|
var baseOutlineSymbol = external_exports.object({
|
|
25839
26845
|
name: external_exports.string(),
|
|
25840
26846
|
kind: external_exports.enum(["class", "function"]),
|
|
@@ -25870,29 +26876,8 @@ async function findSymbolDefinition(req) {
|
|
|
25870
26876
|
);
|
|
25871
26877
|
}
|
|
25872
26878
|
|
|
25873
|
-
// src/api/
|
|
25874
|
-
|
|
25875
|
-
__export(project_settings_exports, {
|
|
25876
|
-
GetProjectSettingsRespSchema: () => GetProjectSettingsRespSchema,
|
|
25877
|
-
ProjectSettingsSchema: () => ProjectSettingsSchema,
|
|
25878
|
-
UpdateProjectSettingsReqSchema: () => UpdateProjectSettingsReqSchema,
|
|
25879
|
-
UpdateProjectSettingsRespSchema: () => UpdateProjectSettingsRespSchema,
|
|
25880
|
-
getProjectSettings: () => getProjectSettings,
|
|
25881
|
-
updateProjectSettings: () => updateProjectSettings
|
|
25882
|
-
});
|
|
25883
|
-
init_zod();
|
|
25884
|
-
var ProjectSettingsSchema = external_exports.object({
|
|
25885
|
-
appName: external_exports.string().optional()
|
|
25886
|
-
});
|
|
25887
|
-
var GetProjectSettingsRespSchema = ProjectSettingsSchema;
|
|
25888
|
-
var UpdateProjectSettingsReqSchema = ProjectSettingsSchema.partial();
|
|
25889
|
-
var UpdateProjectSettingsRespSchema = ProjectSettingsSchema;
|
|
25890
|
-
async function getProjectSettings() {
|
|
25891
|
-
return apiCall(GetProjectSettingsRespSchema, "/project-settings");
|
|
25892
|
-
}
|
|
25893
|
-
async function updateProjectSettings(req) {
|
|
25894
|
-
return apiCall(UpdateProjectSettingsRespSchema, "/project-settings", { method: "PATCH", body: req });
|
|
25895
|
-
}
|
|
26879
|
+
// src/api/index.ts
|
|
26880
|
+
init_project_settings();
|
|
25896
26881
|
|
|
25897
26882
|
// src/api/reviews.ts
|
|
25898
26883
|
var reviews_exports = {};
|
|
@@ -25918,6 +26903,7 @@ __export(reviews_exports, {
|
|
|
25918
26903
|
});
|
|
25919
26904
|
init_zod();
|
|
25920
26905
|
init_schemas3();
|
|
26906
|
+
init_runner();
|
|
25921
26907
|
var ListReviewsRespSchema = external_exports.array(ReviewSchema);
|
|
25922
26908
|
var GetCurrentReviewRespSchema = ReviewSchema.nullable();
|
|
25923
26909
|
var OnCompleteHookResultSchema = external_exports.object({
|
|
@@ -25981,6 +26967,7 @@ __export(share_prompt_exports, {
|
|
|
25981
26967
|
tickSharePrompt: () => tickSharePrompt
|
|
25982
26968
|
});
|
|
25983
26969
|
init_zod();
|
|
26970
|
+
init_runner();
|
|
25984
26971
|
var GetSharePromptStateRespSchema = external_exports.object({
|
|
25985
26972
|
dismissedAt: external_exports.number().nullable(),
|
|
25986
26973
|
totalOpenMs: external_exports.number()
|
|
@@ -26008,6 +26995,7 @@ __export(system_exports, {
|
|
|
26008
26995
|
openExternal: () => openExternal
|
|
26009
26996
|
});
|
|
26010
26997
|
init_zod();
|
|
26998
|
+
init_runner();
|
|
26011
26999
|
var OpenExternalReqSchema = external_exports.object({
|
|
26012
27000
|
// Restricted to http(s): this hands the value to the OS "open" handler, so
|
|
26013
27001
|
// we don't want to let it launch arbitrary schemes (file:, custom apps).
|
|
@@ -26521,6 +27509,7 @@ function themeToInlineStyle(colors) {
|
|
|
26521
27509
|
}
|
|
26522
27510
|
|
|
26523
27511
|
// src/api/themes.ts
|
|
27512
|
+
init_runner();
|
|
26524
27513
|
function buildThemeColorsSchema() {
|
|
26525
27514
|
const shape = {};
|
|
26526
27515
|
for (const key of THEME_VARIABLES) shape[key] = external_exports.string();
|
|
@@ -26613,11 +27602,13 @@ async function deleteTheme(req) {
|
|
|
26613
27602
|
// src/api/attachments.ts
|
|
26614
27603
|
init_zod();
|
|
26615
27604
|
init_schemas3();
|
|
27605
|
+
init_runner();
|
|
26616
27606
|
var ListAttachmentsRespSchema = external_exports.array(AttachmentSchema);
|
|
26617
27607
|
|
|
26618
27608
|
// src/api/difftool.ts
|
|
26619
27609
|
init_zod();
|
|
26620
27610
|
init_schemas3();
|
|
27611
|
+
init_runner();
|
|
26621
27612
|
var DifftoolStatusRespSchema = external_exports.object({
|
|
26622
27613
|
tool: external_exports.string().nullable(),
|
|
26623
27614
|
cmd: external_exports.string().nullable(),
|
|
@@ -26652,8 +27643,163 @@ var DifftoolPollRespSchema = external_exports.object({
|
|
|
26652
27643
|
});
|
|
26653
27644
|
var DifftoolEndRespSchema = external_exports.object({ ok: external_exports.literal(true) });
|
|
26654
27645
|
|
|
27646
|
+
// src/api/plugins.ts
|
|
27647
|
+
init_zod();
|
|
27648
|
+
init_runner();
|
|
27649
|
+
var PluginStatusSchema = external_exports.enum(["loaded", "disabled", "error"]);
|
|
27650
|
+
var PluginPreferenceInfoSchema = external_exports.object({
|
|
27651
|
+
key: external_exports.string(),
|
|
27652
|
+
label: external_exports.string(),
|
|
27653
|
+
type: external_exports.enum(["string", "number", "boolean", "select"]),
|
|
27654
|
+
default: external_exports.string().optional(),
|
|
27655
|
+
description: external_exports.string().optional(),
|
|
27656
|
+
options: external_exports.array(external_exports.string()).optional(),
|
|
27657
|
+
scope: external_exports.enum(["global", "project"]).optional(),
|
|
27658
|
+
secret: external_exports.boolean().optional()
|
|
27659
|
+
});
|
|
27660
|
+
var ConfigLabelColorSchema2 = external_exports.enum(["default", "success", "error", "warning", "transient"]);
|
|
27661
|
+
var ConfigLayoutItemSchema2 = external_exports.lazy(
|
|
27662
|
+
() => external_exports.object({
|
|
27663
|
+
type: external_exports.enum(["preference", "divider", "spacer", "label", "button", "group"]),
|
|
27664
|
+
key: external_exports.string().optional(),
|
|
27665
|
+
id: external_exports.string().optional(),
|
|
27666
|
+
text: external_exports.string().optional(),
|
|
27667
|
+
color: ConfigLabelColorSchema2.optional(),
|
|
27668
|
+
label: external_exports.string().optional(),
|
|
27669
|
+
action: external_exports.string().optional(),
|
|
27670
|
+
style: external_exports.string().optional(),
|
|
27671
|
+
title: external_exports.string().optional(),
|
|
27672
|
+
collapsed: external_exports.boolean().optional(),
|
|
27673
|
+
items: external_exports.array(ConfigLayoutItemSchema2).optional()
|
|
27674
|
+
})
|
|
27675
|
+
);
|
|
27676
|
+
var PluginInfoSchema = external_exports.object({
|
|
27677
|
+
id: external_exports.string(),
|
|
27678
|
+
name: external_exports.string(),
|
|
27679
|
+
version: external_exports.string(),
|
|
27680
|
+
/** Extensions the plugin's content types declare (informational). */
|
|
27681
|
+
extensions: external_exports.array(external_exports.string()),
|
|
27682
|
+
status: PluginStatusSchema,
|
|
27683
|
+
error: external_exports.string().optional(),
|
|
27684
|
+
/** True when active (loaded). False when disabled by either scope. */
|
|
27685
|
+
enabled: external_exports.boolean(),
|
|
27686
|
+
/** Which scope disables it (global wins), when disabled. */
|
|
27687
|
+
disabledScope: external_exports.enum(["global", "project"]).optional(),
|
|
27688
|
+
/** The two independent disable flags, so the UI can render both toggles. */
|
|
27689
|
+
globalDisabled: external_exports.boolean(),
|
|
27690
|
+
projectDisabled: external_exports.boolean(),
|
|
27691
|
+
/** Manifest-declared preferences + their current values (doc 29 FR-29.12).
|
|
27692
|
+
* Secret values are never sent; `secretConfigured` names the secret keys that
|
|
27693
|
+
* have a stored value (GB-1054). */
|
|
27694
|
+
preferences: external_exports.array(PluginPreferenceInfoSchema),
|
|
27695
|
+
preferenceValues: external_exports.record(external_exports.string(), external_exports.string()),
|
|
27696
|
+
secretConfigured: external_exports.array(external_exports.string()),
|
|
27697
|
+
/** Optional manifest arrangement of the preferences (doc 29 FR-29.18). */
|
|
27698
|
+
configLayout: external_exports.array(ConfigLayoutItemSchema2).optional(),
|
|
27699
|
+
/** Effective `label`-item text/color, keyed by label id (doc 29 FR-29.18). */
|
|
27700
|
+
configLabels: external_exports.record(external_exports.string(), external_exports.object({ text: external_exports.string(), color: ConfigLabelColorSchema2.optional() }))
|
|
27701
|
+
});
|
|
27702
|
+
var ListPluginsRespSchema = external_exports.object({
|
|
27703
|
+
plugins: external_exports.array(PluginInfoSchema),
|
|
27704
|
+
/** Set when a mutation (e.g. install-from-disk of a non-plugin folder) failed;
|
|
27705
|
+
* the list is still returned so the UI stays in sync + can show the message. */
|
|
27706
|
+
error: external_exports.string().optional()
|
|
27707
|
+
});
|
|
27708
|
+
var SetPluginDisabledReqSchema = external_exports.object({
|
|
27709
|
+
scope: external_exports.enum(["global", "project"]),
|
|
27710
|
+
disabled: external_exports.boolean()
|
|
27711
|
+
});
|
|
27712
|
+
var InstallPluginReqSchema = external_exports.object({ path: external_exports.string().min(1) });
|
|
27713
|
+
var RequirementStatusSchema = external_exports.object({
|
|
27714
|
+
id: external_exports.string(),
|
|
27715
|
+
label: external_exports.string(),
|
|
27716
|
+
met: external_exports.boolean(),
|
|
27717
|
+
hint: external_exports.string(),
|
|
27718
|
+
docUrl: external_exports.string().optional()
|
|
27719
|
+
});
|
|
27720
|
+
var AvailablePluginSchema = external_exports.object({
|
|
27721
|
+
id: external_exports.string(),
|
|
27722
|
+
name: external_exports.string(),
|
|
27723
|
+
version: external_exports.string(),
|
|
27724
|
+
description: external_exports.string().optional(),
|
|
27725
|
+
extensions: external_exports.array(external_exports.string()),
|
|
27726
|
+
requirements: external_exports.array(RequirementStatusSchema),
|
|
27727
|
+
provisionNotes: external_exports.array(external_exports.string()),
|
|
27728
|
+
selfContained: external_exports.boolean(),
|
|
27729
|
+
cliHint: external_exports.string().optional()
|
|
27730
|
+
});
|
|
27731
|
+
var ListAvailablePluginsRespSchema = external_exports.object({ available: external_exports.array(AvailablePluginSchema) });
|
|
27732
|
+
var ProvisionOutcomeSchema = external_exports.object({
|
|
27733
|
+
step: external_exports.string(),
|
|
27734
|
+
ok: external_exports.boolean(),
|
|
27735
|
+
skipped: external_exports.boolean(),
|
|
27736
|
+
detail: external_exports.string()
|
|
27737
|
+
});
|
|
27738
|
+
var InstallResultSchema = external_exports.object({
|
|
27739
|
+
id: external_exports.string(),
|
|
27740
|
+
installed: external_exports.boolean(),
|
|
27741
|
+
status: external_exports.enum(["ready", "needs-setup", "error"]),
|
|
27742
|
+
requirements: external_exports.array(RequirementStatusSchema),
|
|
27743
|
+
provisioned: external_exports.array(ProvisionOutcomeSchema),
|
|
27744
|
+
instructions: external_exports.array(external_exports.string()),
|
|
27745
|
+
error: external_exports.string().optional()
|
|
27746
|
+
});
|
|
27747
|
+
var InstallBundledPluginRespSchema = external_exports.object({
|
|
27748
|
+
result: InstallResultSchema,
|
|
27749
|
+
plugins: external_exports.array(PluginInfoSchema),
|
|
27750
|
+
available: external_exports.array(AvailablePluginSchema)
|
|
27751
|
+
});
|
|
27752
|
+
var UninstallPluginRespSchema = ListPluginsRespSchema.extend({
|
|
27753
|
+
available: external_exports.array(AvailablePluginSchema).optional()
|
|
27754
|
+
});
|
|
27755
|
+
var SetPluginPreferenceReqSchema = external_exports.object({ key: external_exports.string().min(1), value: external_exports.string() });
|
|
27756
|
+
var RunPluginActionReqSchema = external_exports.object({ actionId: external_exports.string().min(1), value: external_exports.string().optional() });
|
|
27757
|
+
var UIElementFaceSchema = external_exports.object({
|
|
27758
|
+
label: external_exports.string().optional(),
|
|
27759
|
+
icon: external_exports.string().optional(),
|
|
27760
|
+
title: external_exports.string().optional(),
|
|
27761
|
+
style: external_exports.string().optional()
|
|
27762
|
+
});
|
|
27763
|
+
var UISegmentSchema = external_exports.object({
|
|
27764
|
+
id: external_exports.string(),
|
|
27765
|
+
label: external_exports.string().optional(),
|
|
27766
|
+
icon: external_exports.string().optional(),
|
|
27767
|
+
title: external_exports.string().optional()
|
|
27768
|
+
});
|
|
27769
|
+
var PluginUIElementSchema = external_exports.object({
|
|
27770
|
+
pluginId: external_exports.string(),
|
|
27771
|
+
id: external_exports.string(),
|
|
27772
|
+
type: external_exports.string(),
|
|
27773
|
+
location: external_exports.string(),
|
|
27774
|
+
label: external_exports.string().optional(),
|
|
27775
|
+
icon: external_exports.string().optional(),
|
|
27776
|
+
title: external_exports.string().optional(),
|
|
27777
|
+
style: external_exports.string().optional(),
|
|
27778
|
+
action: external_exports.string().optional(),
|
|
27779
|
+
url: external_exports.string().optional(),
|
|
27780
|
+
// Stateful controls (doc 30 FR-30.3): toggle/switch (`on`/`off`), segmented
|
|
27781
|
+
// (`segments`/`selectionMode`); `stateKey` persists, `value` is the resolved
|
|
27782
|
+
// current state the host attaches when listing.
|
|
27783
|
+
on: UIElementFaceSchema.optional(),
|
|
27784
|
+
off: UIElementFaceSchema.optional(),
|
|
27785
|
+
onLabel: external_exports.string().optional(),
|
|
27786
|
+
offLabel: external_exports.string().optional(),
|
|
27787
|
+
segments: external_exports.array(UISegmentSchema).optional(),
|
|
27788
|
+
selectionMode: external_exports.string().optional(),
|
|
27789
|
+
stateKey: external_exports.string().optional(),
|
|
27790
|
+
value: external_exports.string().optional()
|
|
27791
|
+
});
|
|
27792
|
+
var ListPluginUiRespSchema = external_exports.object({ elements: external_exports.array(PluginUIElementSchema) });
|
|
27793
|
+
var RunPluginActionRespSchema = ListPluginsRespSchema.extend({
|
|
27794
|
+
result: external_exports.object({ message: external_exports.string().optional() }).optional()
|
|
27795
|
+
});
|
|
27796
|
+
|
|
27797
|
+
// src/api/index.ts
|
|
27798
|
+
init_project_settings();
|
|
27799
|
+
|
|
26655
27800
|
// src/api/review-notes.ts
|
|
26656
27801
|
init_zod();
|
|
27802
|
+
init_runner();
|
|
26657
27803
|
var DiscardReviewNoteReqSchema = external_exports.object({
|
|
26658
27804
|
guid: external_exports.string().min(1),
|
|
26659
27805
|
/** Repo-relative source file the note is on (scopes the shard search). */
|
|
@@ -27100,7 +28246,7 @@ aiApiRoutes.route("/", aiConfigRoutes);
|
|
|
27100
28246
|
aiApiRoutes.route("/", aiAnalysisRoutes);
|
|
27101
28247
|
|
|
27102
28248
|
// src/routes/api.ts
|
|
27103
|
-
import { Hono as
|
|
28249
|
+
import { Hono as Hono16 } from "hono";
|
|
27104
28250
|
|
|
27105
28251
|
// src/routes/api/annotations.ts
|
|
27106
28252
|
import { Hono as Hono4 } from "hono";
|
|
@@ -27110,8 +28256,8 @@ init_queries();
|
|
|
27110
28256
|
init_attachment_queries();
|
|
27111
28257
|
init_queries();
|
|
27112
28258
|
init_schemas3();
|
|
27113
|
-
import { existsSync as
|
|
27114
|
-
import { join as
|
|
28259
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync10, readFileSync as readFileSync12, unlinkSync as unlinkSync2, writeFileSync as writeFileSync11 } from "fs";
|
|
28260
|
+
import { join as join13 } from "path";
|
|
27115
28261
|
init_image_metadata();
|
|
27116
28262
|
|
|
27117
28263
|
// src/utils/formatReviewMode.ts
|
|
@@ -27330,10 +28476,10 @@ function buildRegion(a, gt, dimsFor) {
|
|
|
27330
28476
|
|
|
27331
28477
|
// src/export/generate.ts
|
|
27332
28478
|
function deleteReviewExport(reviewId, repoRoot) {
|
|
27333
|
-
const exportDir =
|
|
28479
|
+
const exportDir = join13(repoRoot, ".glassbox");
|
|
27334
28480
|
for (const ext of ["md", "json"]) {
|
|
27335
|
-
const archivePath =
|
|
27336
|
-
if (
|
|
28481
|
+
const archivePath = join13(exportDir, `review-${reviewId}.${ext}`);
|
|
28482
|
+
if (existsSync13(archivePath)) unlinkSync2(archivePath);
|
|
27337
28483
|
}
|
|
27338
28484
|
}
|
|
27339
28485
|
function annotationAnchorLabel(a) {
|
|
@@ -27360,8 +28506,8 @@ async function generateReviewExport(reviewId, repoRoot, isCurrent) {
|
|
|
27360
28506
|
if (!review) throw new Error("Review not found");
|
|
27361
28507
|
const files = await getReviewFiles(reviewId);
|
|
27362
28508
|
const annotations = await getAnnotationsForReview(reviewId);
|
|
27363
|
-
const exportDir =
|
|
27364
|
-
|
|
28509
|
+
const exportDir = join13(repoRoot, ".glassbox");
|
|
28510
|
+
mkdirSync10(exportDir, { recursive: true });
|
|
27365
28511
|
const byFile = {};
|
|
27366
28512
|
for (const a of annotations) {
|
|
27367
28513
|
if (!(a.file_path in byFile)) byFile[a.file_path] = [];
|
|
@@ -27454,20 +28600,20 @@ async function generateReviewExport(reviewId, repoRoot, isCurrent) {
|
|
|
27454
28600
|
resolveDims: resolveImageDims
|
|
27455
28601
|
});
|
|
27456
28602
|
const json2 = JSON.stringify(exportData, null, 2);
|
|
27457
|
-
const archivePath =
|
|
27458
|
-
|
|
27459
|
-
|
|
28603
|
+
const archivePath = join13(exportDir, `review-${review.id}.md`);
|
|
28604
|
+
writeFileSync11(archivePath, content, "utf-8");
|
|
28605
|
+
writeFileSync11(join13(exportDir, `review-${review.id}.json`), json2, "utf-8");
|
|
27460
28606
|
if (isCurrent) {
|
|
27461
|
-
const latestPath =
|
|
27462
|
-
|
|
27463
|
-
|
|
28607
|
+
const latestPath = join13(exportDir, "latest-review.md");
|
|
28608
|
+
writeFileSync11(latestPath, content, "utf-8");
|
|
28609
|
+
writeFileSync11(join13(exportDir, "latest-review.json"), json2, "utf-8");
|
|
27464
28610
|
return latestPath;
|
|
27465
28611
|
}
|
|
27466
28612
|
return archivePath;
|
|
27467
28613
|
}
|
|
27468
28614
|
function resolveImageDims(absPath) {
|
|
27469
28615
|
try {
|
|
27470
|
-
const meta3 = extractMetadata(
|
|
28616
|
+
const meta3 = extractMetadata(readFileSync12(absPath), absPath);
|
|
27471
28617
|
if (meta3.width !== null && meta3.height !== null) {
|
|
27472
28618
|
return { width: meta3.width, height: meta3.height };
|
|
27473
28619
|
}
|
|
@@ -27572,7 +28718,7 @@ annotationsRoutes.get("/annotations/all", async (c) => {
|
|
|
27572
28718
|
init_store2();
|
|
27573
28719
|
init_attachment_queries();
|
|
27574
28720
|
init_queries();
|
|
27575
|
-
import { readFileSync as
|
|
28721
|
+
import { readFileSync as readFileSync13, statSync as statSync5 } from "fs";
|
|
27576
28722
|
import { Hono as Hono5 } from "hono";
|
|
27577
28723
|
|
|
27578
28724
|
// src/utils/mime.ts
|
|
@@ -27686,9 +28832,9 @@ attachmentsRoutes.get("/attachments/:id/raw", async (c) => {
|
|
|
27686
28832
|
const attachment = await getAttachment(idParam.data);
|
|
27687
28833
|
if (attachment === void 0) return c.text("Not found", 404);
|
|
27688
28834
|
try {
|
|
27689
|
-
const stat =
|
|
28835
|
+
const stat = statSync5(attachment.stored_path);
|
|
27690
28836
|
if (!stat.isFile()) return c.text("Not found", 404);
|
|
27691
|
-
const bytes =
|
|
28837
|
+
const bytes = readFileSync13(attachment.stored_path);
|
|
27692
28838
|
return new Response(new Uint8Array(bytes), {
|
|
27693
28839
|
headers: {
|
|
27694
28840
|
"Content-Type": attachment.mime_type,
|
|
@@ -27706,7 +28852,7 @@ attachmentsRoutes.post("/attachments/:id/quicklook", async (c) => {
|
|
|
27706
28852
|
const attachment = await getAttachment(idParam.data);
|
|
27707
28853
|
if (attachment === void 0) return errorResponse(c, "Attachment not found", 404);
|
|
27708
28854
|
try {
|
|
27709
|
-
|
|
28855
|
+
statSync5(attachment.stored_path);
|
|
27710
28856
|
} catch {
|
|
27711
28857
|
return errorResponse(c, "Attachment file missing", 404);
|
|
27712
28858
|
}
|
|
@@ -27808,6 +28954,19 @@ function groundTruthMetaByFileId(mode, files) {
|
|
|
27808
28954
|
return out;
|
|
27809
28955
|
}
|
|
27810
28956
|
|
|
28957
|
+
// src/plugins/fileView.ts
|
|
28958
|
+
init_plugins();
|
|
28959
|
+
function pluginRendersFile(filePath) {
|
|
28960
|
+
return pluginsEnabled() && mightHandleFile(filePath);
|
|
28961
|
+
}
|
|
28962
|
+
async function renderPluginSvgSide(mode, filePath, oldPath, side, cwd) {
|
|
28963
|
+
if (mode === null || !pluginRendersFile(filePath)) return null;
|
|
28964
|
+
const source = getModeFileContent(mode, side === "old" ? oldPath : filePath, side, cwd);
|
|
28965
|
+
if (source.trim() === "") return null;
|
|
28966
|
+
const view = await renderContent({ bytes: new TextEncoder().encode(source), text: source, path: filePath, side });
|
|
28967
|
+
return view?.svg !== void 0 && view.svg !== "" ? view.svg : null;
|
|
28968
|
+
}
|
|
28969
|
+
|
|
27811
28970
|
// src/routes/api/files.ts
|
|
27812
28971
|
init_openOS();
|
|
27813
28972
|
var filesRoutes = new Hono7();
|
|
@@ -27820,7 +28979,14 @@ filesRoutes.get("/files", async (c) => {
|
|
|
27820
28979
|
getReview(reviewId)
|
|
27821
28980
|
]);
|
|
27822
28981
|
const groundTruth = review ? groundTruthMetaByFileId(parseModeString(review.mode), files) : void 0;
|
|
27823
|
-
|
|
28982
|
+
const pluginRendered = files.filter((f) => pluginRendersFile(f.file_path)).map((f) => f.id);
|
|
28983
|
+
return c.json({
|
|
28984
|
+
files,
|
|
28985
|
+
annotationCounts,
|
|
28986
|
+
staleCounts,
|
|
28987
|
+
...groundTruth ? { groundTruth } : {},
|
|
28988
|
+
...pluginRendered.length > 0 ? { pluginRendered } : {}
|
|
28989
|
+
});
|
|
27824
28990
|
});
|
|
27825
28991
|
filesRoutes.get("/files/:fileId", async (c) => {
|
|
27826
28992
|
const fileId = requirePathParam(c, "fileId");
|
|
@@ -27882,8 +29048,8 @@ import { Hono as Hono8 } from "hono";
|
|
|
27882
29048
|
|
|
27883
29049
|
// src/git/image.ts
|
|
27884
29050
|
import { spawnSync as spawnSync8 } from "child_process";
|
|
27885
|
-
import { readFileSync as
|
|
27886
|
-
import { join as
|
|
29051
|
+
import { readFileSync as readFileSync14 } from "fs";
|
|
29052
|
+
import { join as join14, resolve as resolve8 } from "path";
|
|
27887
29053
|
init_image_metadata();
|
|
27888
29054
|
function getOldRef(mode) {
|
|
27889
29055
|
switch (mode.type) {
|
|
@@ -27947,14 +29113,14 @@ function gitShowFile(ref, filePath, repoRoot) {
|
|
|
27947
29113
|
}
|
|
27948
29114
|
function readWorkingFile(filePath, repoRoot) {
|
|
27949
29115
|
try {
|
|
27950
|
-
return
|
|
29116
|
+
return readFileSync14(resolve8(repoRoot, filePath));
|
|
27951
29117
|
} catch {
|
|
27952
29118
|
return null;
|
|
27953
29119
|
}
|
|
27954
29120
|
}
|
|
27955
29121
|
function readDiskImage(absPath) {
|
|
27956
29122
|
try {
|
|
27957
|
-
const data =
|
|
29123
|
+
const data = readFileSync14(absPath);
|
|
27958
29124
|
return { data, size: data.length };
|
|
27959
29125
|
} catch {
|
|
27960
29126
|
return null;
|
|
@@ -27965,7 +29131,7 @@ function groundTruthEntry(mode, filePath) {
|
|
|
27965
29131
|
}
|
|
27966
29132
|
function getOldImage(mode, filePath, oldPath, repoRoot) {
|
|
27967
29133
|
if (mode.type === "diff") {
|
|
27968
|
-
return readDiskImage(
|
|
29134
|
+
return readDiskImage(join14(directComparisonRoots(mode).rootA, oldPath ?? filePath));
|
|
27969
29135
|
}
|
|
27970
29136
|
if (mode.type === "ground-truth") {
|
|
27971
29137
|
const entry = groundTruthEntry(mode, filePath);
|
|
@@ -27985,7 +29151,7 @@ function getOldImage(mode, filePath, oldPath, repoRoot) {
|
|
|
27985
29151
|
}
|
|
27986
29152
|
function getNewImage(mode, filePath, repoRoot) {
|
|
27987
29153
|
if (mode.type === "diff") {
|
|
27988
|
-
return readDiskImage(
|
|
29154
|
+
return readDiskImage(join14(directComparisonRoots(mode).rootB, filePath));
|
|
27989
29155
|
}
|
|
27990
29156
|
if (mode.type === "ground-truth") {
|
|
27991
29157
|
const entry = groundTruthEntry(mode, filePath);
|
|
@@ -28059,6 +29225,12 @@ imageRoutes.get("/image/:fileId/:side", async (c) => {
|
|
|
28059
29225
|
const diff = parseDiffData(file2.diff_data);
|
|
28060
29226
|
const oldPath = diff?.oldPath ?? null;
|
|
28061
29227
|
const status = diff?.status ?? "modified";
|
|
29228
|
+
if (!isImageFile(file2.file_path) && !(side === "old" && status === "added") && !(side === "new" && status === "deleted")) {
|
|
29229
|
+
const svg = await renderPluginSvgSide(mode, file2.file_path, oldPath ?? file2.file_path, side, repoRoot);
|
|
29230
|
+
if (svg !== null) {
|
|
29231
|
+
return new Response(svg, { headers: { "Content-Type": "image/svg+xml", "Cache-Control": "no-cache" } });
|
|
29232
|
+
}
|
|
29233
|
+
}
|
|
28062
29234
|
const image = resolveImageSide(
|
|
28063
29235
|
fileIdParam.data,
|
|
28064
29236
|
side,
|
|
@@ -28081,7 +29253,7 @@ imageRoutes.get("/image/:fileId/:side", async (c) => {
|
|
|
28081
29253
|
init_queries();
|
|
28082
29254
|
init_debug();
|
|
28083
29255
|
import { spawnSync as spawnSync9 } from "child_process";
|
|
28084
|
-
import { readFileSync as
|
|
29256
|
+
import { readFileSync as readFileSync15 } from "fs";
|
|
28085
29257
|
import { Hono as Hono9 } from "hono";
|
|
28086
29258
|
import { resolve as resolve9 } from "path";
|
|
28087
29259
|
|
|
@@ -28459,7 +29631,7 @@ outlineRoutes.get("/symbol-definition", async (c) => {
|
|
|
28459
29631
|
}
|
|
28460
29632
|
let content = "";
|
|
28461
29633
|
try {
|
|
28462
|
-
content =
|
|
29634
|
+
content = readFileSync15(resolve9(repoRoot, filePath), "utf-8");
|
|
28463
29635
|
} catch {
|
|
28464
29636
|
continue;
|
|
28465
29637
|
}
|
|
@@ -28494,48 +29666,268 @@ function collectDefinitions(symbols, targetName, fileId, filePath, out) {
|
|
|
28494
29666
|
}
|
|
28495
29667
|
}
|
|
28496
29668
|
|
|
28497
|
-
// src/routes/api/
|
|
28498
|
-
import { existsSync as existsSync11, mkdirSync as mkdirSync9, readFileSync as readFileSync13, writeFileSync as writeFileSync10 } from "fs";
|
|
29669
|
+
// src/routes/api/plugins.ts
|
|
28499
29670
|
import { Hono as Hono10 } from "hono";
|
|
28500
|
-
|
|
28501
|
-
|
|
28502
|
-
|
|
28503
|
-
|
|
29671
|
+
|
|
29672
|
+
// src/plugins/available.ts
|
|
29673
|
+
init_install();
|
|
29674
|
+
init_loader();
|
|
29675
|
+
import { existsSync as existsSync14 } from "fs";
|
|
29676
|
+
import { join as join15 } from "path";
|
|
29677
|
+
|
|
29678
|
+
// src/plugins/readiness.ts
|
|
29679
|
+
import { spawnSync as spawnSync10 } from "child_process";
|
|
29680
|
+
var defaultProbe = (command, args) => {
|
|
28504
29681
|
try {
|
|
28505
|
-
|
|
28506
|
-
|
|
28507
|
-
const parsed = ProjectSettingsSchema.safeParse(raw2);
|
|
28508
|
-
if (parsed.success) return parsed.data;
|
|
28509
|
-
}
|
|
29682
|
+
const r = spawnSync10(command, args, { stdio: "ignore", timeout: 1e4 });
|
|
29683
|
+
return r.error === void 0 && r.status === 0;
|
|
28510
29684
|
} catch {
|
|
29685
|
+
return false;
|
|
28511
29686
|
}
|
|
28512
|
-
|
|
29687
|
+
};
|
|
29688
|
+
function checkRequirement(req, runProbe = defaultProbe) {
|
|
29689
|
+
const met = runProbe(req.command, req.checkArgs ?? ["--version"]);
|
|
29690
|
+
return { id: req.id, label: req.label, met, hint: req.hint, docUrl: req.docUrl };
|
|
28513
29691
|
}
|
|
28514
|
-
function
|
|
28515
|
-
|
|
28516
|
-
mkdirSync9(dir, { recursive: true });
|
|
28517
|
-
writeFileSync10(join12(dir, "settings.json"), JSON.stringify(settings, null, 2), "utf-8");
|
|
29692
|
+
function checkRequirements(reqs, runProbe = defaultProbe) {
|
|
29693
|
+
return (reqs ?? []).map((r) => checkRequirement(r, runProbe));
|
|
28518
29694
|
}
|
|
28519
|
-
|
|
29695
|
+
function requirementMet(statuses, id) {
|
|
29696
|
+
return statuses.find((s) => s.id === id)?.met ?? false;
|
|
29697
|
+
}
|
|
29698
|
+
|
|
29699
|
+
// src/plugins/available.ts
|
|
29700
|
+
function manifestExtensions(manifest) {
|
|
29701
|
+
return (manifest.contentTypes ?? []).flatMap((ct) => ct.extensions ?? []);
|
|
29702
|
+
}
|
|
29703
|
+
function provisionNotes(install) {
|
|
29704
|
+
const notes = [];
|
|
29705
|
+
for (const step of install?.provision ?? []) {
|
|
29706
|
+
if (step.kind === "fetch") notes.push(`Downloads ${step.dest}.`);
|
|
29707
|
+
else notes.push(step.note ?? `Installs ${step.packages.join(", ")}.`);
|
|
29708
|
+
}
|
|
29709
|
+
return notes;
|
|
29710
|
+
}
|
|
29711
|
+
function describeAvailablePlugin(manifest, runProbe) {
|
|
29712
|
+
const requirements = checkRequirements(manifest.install?.requirements, runProbe);
|
|
29713
|
+
const notes = provisionNotes(manifest.install);
|
|
29714
|
+
return {
|
|
29715
|
+
id: manifest.id,
|
|
29716
|
+
name: manifest.name,
|
|
29717
|
+
version: manifest.version,
|
|
29718
|
+
description: manifest.description,
|
|
29719
|
+
extensions: manifestExtensions(manifest),
|
|
29720
|
+
requirements,
|
|
29721
|
+
provisionNotes: notes,
|
|
29722
|
+
selfContained: requirements.length === 0 && notes.length === 0,
|
|
29723
|
+
cliHint: manifest.install?.cliHint
|
|
29724
|
+
};
|
|
29725
|
+
}
|
|
29726
|
+
function listAvailablePlugins(opts) {
|
|
29727
|
+
const bundledDir = opts?.bundledDir ?? bundledPluginsDir();
|
|
29728
|
+
const userDir = opts?.userDir ?? pluginsDir();
|
|
29729
|
+
const out = [];
|
|
29730
|
+
for (const { manifest } of discoverBundledPlugins(bundledDir)) {
|
|
29731
|
+
if (manifest.autoInstall !== false) continue;
|
|
29732
|
+
if (existsSync14(join15(userDir, manifest.id))) continue;
|
|
29733
|
+
out.push(describeAvailablePlugin(manifest, opts?.runProbe));
|
|
29734
|
+
}
|
|
29735
|
+
return out.sort((a, b) => a.name.localeCompare(b.name));
|
|
29736
|
+
}
|
|
29737
|
+
|
|
29738
|
+
// src/routes/api/plugins.ts
|
|
29739
|
+
init_enablement();
|
|
29740
|
+
init_plugins();
|
|
29741
|
+
init_install();
|
|
29742
|
+
|
|
29743
|
+
// src/plugins/install-action.ts
|
|
29744
|
+
init_install();
|
|
29745
|
+
init_loader();
|
|
29746
|
+
import { spawnSync as spawnSync11 } from "child_process";
|
|
29747
|
+
import { createHash as createHash4 } from "crypto";
|
|
29748
|
+
import { cpSync as cpSync2, mkdirSync as mkdirSync11, writeFileSync as writeFileSync12 } from "fs";
|
|
29749
|
+
import { join as join16 } from "path";
|
|
29750
|
+
var defaultFetchBytes = async (url2) => {
|
|
29751
|
+
const res = await fetch(url2);
|
|
29752
|
+
if (!res.ok) throw new Error(`HTTP ${String(res.status)}`);
|
|
29753
|
+
return new Uint8Array(await res.arrayBuffer());
|
|
29754
|
+
};
|
|
29755
|
+
var defaultRunNpmInstall = (dir, packages) => {
|
|
29756
|
+
const r = spawnSync11("npm", ["install", "--no-save", "--prefix", dir, ...packages], { stdio: "ignore", timeout: 6e5 });
|
|
29757
|
+
if (r.error !== void 0) return { ok: false, detail: r.error.message };
|
|
29758
|
+
if (r.status !== 0) return { ok: false, detail: `npm exited ${String(r.status)}` };
|
|
29759
|
+
return { ok: true, detail: "installed" };
|
|
29760
|
+
};
|
|
29761
|
+
async function runProvisionStep(step, installDir, requirements, deps) {
|
|
29762
|
+
if (step.kind === "fetch") {
|
|
29763
|
+
const label2 = `Download ${step.dest}`;
|
|
29764
|
+
try {
|
|
29765
|
+
const bytes = await deps.fetchBytes(step.url);
|
|
29766
|
+
if (step.sha256 !== void 0) {
|
|
29767
|
+
const got = createHash4("sha256").update(bytes).digest("hex");
|
|
29768
|
+
if (got.toLowerCase() !== step.sha256.toLowerCase()) {
|
|
29769
|
+
return { step: label2, ok: false, skipped: false, detail: `checksum mismatch (expected ${step.sha256})` };
|
|
29770
|
+
}
|
|
29771
|
+
}
|
|
29772
|
+
writeFileSync12(join16(installDir, step.dest), bytes);
|
|
29773
|
+
return { step: label2, ok: true, skipped: false, detail: `saved ${step.dest}` };
|
|
29774
|
+
} catch (e) {
|
|
29775
|
+
return { step: label2, ok: false, skipped: false, detail: e instanceof Error ? e.message : "download failed" };
|
|
29776
|
+
}
|
|
29777
|
+
}
|
|
29778
|
+
const label = `Install ${step.packages.join(", ")}`;
|
|
29779
|
+
const reqId = step.requires ?? "npm";
|
|
29780
|
+
if (!requirementMet(requirements, reqId)) {
|
|
29781
|
+
return { step: label, ok: false, skipped: true, detail: `${reqId} is not available` };
|
|
29782
|
+
}
|
|
29783
|
+
const r = deps.runNpmInstall(installDir, step.packages);
|
|
29784
|
+
return { step: label, ok: r.ok, skipped: false, detail: r.detail };
|
|
29785
|
+
}
|
|
29786
|
+
function buildInstructions(manifest, requirements, provisioned) {
|
|
29787
|
+
const out = [];
|
|
29788
|
+
for (const r of requirements) if (!r.met) out.push(`${r.label}: ${r.hint}`);
|
|
29789
|
+
for (const p of provisioned) {
|
|
29790
|
+
if (p.ok) continue;
|
|
29791
|
+
out.push(p.skipped ? `${p.step} was skipped \u2014 ${p.detail}.` : `${p.step} failed \u2014 ${p.detail}.`);
|
|
29792
|
+
}
|
|
29793
|
+
const cliHint = manifest.install?.cliHint;
|
|
29794
|
+
if (out.length > 0 && cliHint !== void 0 && cliHint !== "") out.push(`Or finish from a terminal: ${cliHint}`);
|
|
29795
|
+
return out;
|
|
29796
|
+
}
|
|
29797
|
+
async function installAvailablePlugin(id, deps = {}) {
|
|
29798
|
+
const bundledDir = deps.bundledDir ?? bundledPluginsDir();
|
|
29799
|
+
const userDir = deps.userDir ?? pluginsDir();
|
|
29800
|
+
const fetchBytes = deps.fetchBytes ?? defaultFetchBytes;
|
|
29801
|
+
const runNpmInstall = deps.runNpmInstall ?? defaultRunNpmInstall;
|
|
29802
|
+
const bundled = discoverBundledPlugins(bundledDir).find((b) => b.manifest.id === id);
|
|
29803
|
+
if (bundled === void 0) {
|
|
29804
|
+
return { id, installed: false, status: "error", requirements: [], provisioned: [], instructions: [], error: "Not a bundled plugin available to install." };
|
|
29805
|
+
}
|
|
29806
|
+
const manifest = bundled.manifest;
|
|
29807
|
+
const dest = join16(userDir, id);
|
|
29808
|
+
try {
|
|
29809
|
+
mkdirSync11(userDir, { recursive: true });
|
|
29810
|
+
cpSync2(bundled.dir, dest, { recursive: true });
|
|
29811
|
+
undismissPlugin(id, userDir);
|
|
29812
|
+
} catch (e) {
|
|
29813
|
+
return { id, installed: false, status: "error", requirements: [], provisioned: [], instructions: [], error: e instanceof Error ? e.message : "Copy failed." };
|
|
29814
|
+
}
|
|
29815
|
+
const requirements = checkRequirements(manifest.install?.requirements, deps.runProbe);
|
|
29816
|
+
const provisioned = [];
|
|
29817
|
+
for (const step of manifest.install?.provision ?? []) {
|
|
29818
|
+
provisioned.push(await runProvisionStep(step, dest, requirements, { fetchBytes, runNpmInstall }));
|
|
29819
|
+
}
|
|
29820
|
+
const instructions = buildInstructions(manifest, requirements, provisioned);
|
|
29821
|
+
const status = instructions.length === 0 ? "ready" : "needs-setup";
|
|
29822
|
+
return { id, installed: true, status, requirements, provisioned, instructions };
|
|
29823
|
+
}
|
|
29824
|
+
|
|
29825
|
+
// src/routes/api/plugins.ts
|
|
29826
|
+
init_loader();
|
|
29827
|
+
init_settings();
|
|
29828
|
+
var pluginsRoutes = new Hono10();
|
|
29829
|
+
pluginsRoutes.get("/plugins", (c) => {
|
|
29830
|
+
return c.json({ plugins: describeInstalledPlugins(c.get("repoRoot")) });
|
|
29831
|
+
});
|
|
29832
|
+
pluginsRoutes.get("/plugins/ui", (c) => {
|
|
29833
|
+
return c.json({ elements: listPluginUIElements(c.get("repoRoot")) });
|
|
29834
|
+
});
|
|
29835
|
+
pluginsRoutes.get("/plugins/available", (c) => {
|
|
29836
|
+
return c.json({ available: listAvailablePlugins() });
|
|
29837
|
+
});
|
|
29838
|
+
pluginsRoutes.post("/plugins/:id/install-bundled", async (c) => {
|
|
29839
|
+
const id = requirePathParam(c, "id");
|
|
29840
|
+
if (!id.ok) return id.response;
|
|
28520
29841
|
const repoRoot = c.get("repoRoot");
|
|
28521
|
-
|
|
29842
|
+
const result = await installAvailablePlugin(id.data);
|
|
29843
|
+
if (result.installed) await reloadContentPlugins(repoRoot);
|
|
29844
|
+
return c.json({ result, plugins: describeInstalledPlugins(repoRoot), available: listAvailablePlugins() });
|
|
29845
|
+
});
|
|
29846
|
+
pluginsRoutes.post("/plugins/:id/disabled", async (c) => {
|
|
29847
|
+
const id = requirePathParam(c, "id");
|
|
29848
|
+
if (!id.ok) return id.response;
|
|
29849
|
+
const parsed = await parseBody(c, SetPluginDisabledReqSchema);
|
|
29850
|
+
if (!parsed.ok) return parsed.response;
|
|
29851
|
+
const repoRoot = c.get("repoRoot");
|
|
29852
|
+
if (parsed.data.scope === "global") setGlobalDisabled(id.data, parsed.data.disabled);
|
|
29853
|
+
else setProjectDisabled(repoRoot, id.data, parsed.data.disabled);
|
|
29854
|
+
await reloadContentPlugins(repoRoot);
|
|
29855
|
+
return c.json({ plugins: describeInstalledPlugins(repoRoot) });
|
|
29856
|
+
});
|
|
29857
|
+
pluginsRoutes.post("/plugins/install", async (c) => {
|
|
29858
|
+
const parsed = await parseBody(c, InstallPluginReqSchema);
|
|
29859
|
+
if (!parsed.ok) return parsed.response;
|
|
29860
|
+
const repoRoot = c.get("repoRoot");
|
|
29861
|
+
try {
|
|
29862
|
+
installPluginFromDisk(parsed.data.path);
|
|
29863
|
+
} catch (e) {
|
|
29864
|
+
return c.json({ plugins: describeInstalledPlugins(repoRoot), error: e instanceof Error ? e.message : "Install failed" }, 400);
|
|
29865
|
+
}
|
|
29866
|
+
await reloadContentPlugins(repoRoot);
|
|
29867
|
+
return c.json({ plugins: describeInstalledPlugins(repoRoot) });
|
|
29868
|
+
});
|
|
29869
|
+
pluginsRoutes.post("/plugins/:id/preferences", async (c) => {
|
|
29870
|
+
const id = requirePathParam(c, "id");
|
|
29871
|
+
if (!id.ok) return id.response;
|
|
29872
|
+
const parsed = await parseBody(c, SetPluginPreferenceReqSchema);
|
|
29873
|
+
if (!parsed.ok) return parsed.response;
|
|
29874
|
+
const manifest = getPluginManifest(id.data);
|
|
29875
|
+
if (manifest === void 0) return c.json({ error: "Plugin not found" }, 404);
|
|
29876
|
+
const repoRoot = c.get("repoRoot");
|
|
29877
|
+
writePluginSetting(manifest, repoRoot, parsed.data.key, parsed.data.value);
|
|
29878
|
+
await reloadContentPlugins(repoRoot);
|
|
29879
|
+
return c.json({ plugins: describeInstalledPlugins(repoRoot) });
|
|
29880
|
+
});
|
|
29881
|
+
pluginsRoutes.post("/plugins/:id/action", async (c) => {
|
|
29882
|
+
const id = requirePathParam(c, "id");
|
|
29883
|
+
if (!id.ok) return id.response;
|
|
29884
|
+
const parsed = await parseBody(c, RunPluginActionReqSchema);
|
|
29885
|
+
if (!parsed.ok) return parsed.response;
|
|
29886
|
+
const repoRoot = c.get("repoRoot");
|
|
29887
|
+
if (parsed.data.value !== void 0) persistPluginUIState(id.data, parsed.data.actionId, parsed.data.value, repoRoot);
|
|
29888
|
+
let result;
|
|
29889
|
+
try {
|
|
29890
|
+
result = await runPluginAction(id.data, parsed.data.actionId, parsed.data.value);
|
|
29891
|
+
} catch (e) {
|
|
29892
|
+
return c.json({ plugins: describeInstalledPlugins(repoRoot), error: e instanceof Error ? e.message : "Action failed" }, 400);
|
|
29893
|
+
}
|
|
29894
|
+
return c.json({ plugins: describeInstalledPlugins(repoRoot), result: result ?? void 0 });
|
|
29895
|
+
});
|
|
29896
|
+
pluginsRoutes.delete("/plugins/:id", async (c) => {
|
|
29897
|
+
const id = requirePathParam(c, "id");
|
|
29898
|
+
if (!id.ok) return id.response;
|
|
29899
|
+
const repoRoot = c.get("repoRoot");
|
|
29900
|
+
uninstallPlugin(id.data);
|
|
29901
|
+
clearConfigLabelOverrides(id.data);
|
|
29902
|
+
clearPluginUIElements(id.data);
|
|
29903
|
+
await reloadContentPlugins(repoRoot);
|
|
29904
|
+
return c.json({ plugins: describeInstalledPlugins(repoRoot), available: listAvailablePlugins() });
|
|
29905
|
+
});
|
|
29906
|
+
|
|
29907
|
+
// src/routes/api/project-settings.ts
|
|
29908
|
+
init_project_settings();
|
|
29909
|
+
init_project_settings_store();
|
|
29910
|
+
import { Hono as Hono11 } from "hono";
|
|
29911
|
+
var projectSettingsRoutes = new Hono11();
|
|
29912
|
+
projectSettingsRoutes.get("/project-settings", (c) => {
|
|
29913
|
+
return c.json(readProjectSettings(c.get("repoRoot")));
|
|
28522
29914
|
});
|
|
28523
29915
|
projectSettingsRoutes.patch("/project-settings", async (c) => {
|
|
28524
29916
|
const repoRoot = c.get("repoRoot");
|
|
28525
29917
|
const parsed = await parseBody(c, UpdateProjectSettingsReqSchema);
|
|
28526
29918
|
if (!parsed.ok) return parsed.response;
|
|
28527
|
-
|
|
28528
|
-
|
|
28529
|
-
|
|
28530
|
-
return c.json(
|
|
29919
|
+
updateProjectSettings2(repoRoot, (s) => {
|
|
29920
|
+
if (parsed.data.appName !== void 0) s.appName = parsed.data.appName || void 0;
|
|
29921
|
+
});
|
|
29922
|
+
return c.json(readProjectSettings(repoRoot));
|
|
28531
29923
|
});
|
|
28532
29924
|
|
|
28533
29925
|
// src/routes/api/review-notes.ts
|
|
28534
29926
|
init_store();
|
|
28535
|
-
import { readFileSync as
|
|
28536
|
-
import { Hono as
|
|
28537
|
-
import { extname, relative as relative2, resolve as resolve10 } from "path";
|
|
28538
|
-
var reviewNotesRoutes = new
|
|
29927
|
+
import { readFileSync as readFileSync16, statSync as statSync6 } from "fs";
|
|
29928
|
+
import { Hono as Hono12 } from "hono";
|
|
29929
|
+
import { extname as extname2, relative as relative2, resolve as resolve10 } from "path";
|
|
29930
|
+
var reviewNotesRoutes = new Hono12();
|
|
28539
29931
|
var ARTIFACT_SERVE_MAX_BYTES = 1e7;
|
|
28540
29932
|
var IMAGE_CONTENT_TYPES = {
|
|
28541
29933
|
".png": "image/png",
|
|
@@ -28553,13 +29945,13 @@ reviewNotesRoutes.get("/review-notes/artifact", (c) => {
|
|
|
28553
29945
|
const abs = resolve10(repoRoot, file2);
|
|
28554
29946
|
const rel = relative2(repoRoot, abs);
|
|
28555
29947
|
if (rel === "" || rel.startsWith("..") || rel.startsWith("/")) return c.text("Forbidden", 403);
|
|
28556
|
-
const ext =
|
|
29948
|
+
const ext = extname2(abs).toLowerCase();
|
|
28557
29949
|
if (!(ext in IMAGE_CONTENT_TYPES)) return c.text("Unsupported artifact type", 415);
|
|
28558
29950
|
const contentType = IMAGE_CONTENT_TYPES[ext];
|
|
28559
29951
|
try {
|
|
28560
|
-
const stat =
|
|
29952
|
+
const stat = statSync6(abs);
|
|
28561
29953
|
if (!stat.isFile() || stat.size > ARTIFACT_SERVE_MAX_BYTES) return c.text("Not found", 404);
|
|
28562
|
-
const body =
|
|
29954
|
+
const body = readFileSync16(abs);
|
|
28563
29955
|
return c.body(body, 200, { "Content-Type": contentType });
|
|
28564
29956
|
} catch {
|
|
28565
29957
|
return c.text("Not found", 404);
|
|
@@ -28576,17 +29968,17 @@ reviewNotesRoutes.delete("/review-notes/:guid", (c) => {
|
|
|
28576
29968
|
|
|
28577
29969
|
// src/routes/api/reviews.ts
|
|
28578
29970
|
init_queries();
|
|
28579
|
-
import { Hono as
|
|
29971
|
+
import { Hono as Hono13 } from "hono";
|
|
28580
29972
|
|
|
28581
29973
|
// src/export/on-complete-hook.ts
|
|
28582
29974
|
import { spawn as spawn2 } from "child_process";
|
|
28583
29975
|
import { appendFileSync } from "fs";
|
|
28584
|
-
import { join as
|
|
29976
|
+
import { join as join17 } from "path";
|
|
28585
29977
|
function runOnCompleteHook(command, ctx) {
|
|
28586
29978
|
if (command === null || command.trim() === "") {
|
|
28587
29979
|
return Promise.resolve({ ran: false, ok: true, exitCode: 0 });
|
|
28588
29980
|
}
|
|
28589
|
-
const logPath =
|
|
29981
|
+
const logPath = join17(ctx.repoRoot, ".glassbox", "on-complete.log");
|
|
28590
29982
|
const log = (s) => {
|
|
28591
29983
|
try {
|
|
28592
29984
|
appendFileSync(logPath, s);
|
|
@@ -28638,7 +30030,8 @@ $ ${command}
|
|
|
28638
30030
|
}
|
|
28639
30031
|
|
|
28640
30032
|
// src/routes/api/reviews.ts
|
|
28641
|
-
|
|
30033
|
+
init_plugins();
|
|
30034
|
+
var reviewsRoutes = new Hono13();
|
|
28642
30035
|
reviewsRoutes.get("/reviews", async (c) => {
|
|
28643
30036
|
const repoRoot = c.get("repoRoot");
|
|
28644
30037
|
const reviews = await listReviews(repoRoot);
|
|
@@ -28662,6 +30055,13 @@ reviewsRoutes.post("/review/complete", async (c) => {
|
|
|
28662
30055
|
jsonPath: exportPath.replace(/\.md$/, ".json"),
|
|
28663
30056
|
markdownPath: exportPath
|
|
28664
30057
|
});
|
|
30058
|
+
try {
|
|
30059
|
+
const completed = await getReview(reviewId);
|
|
30060
|
+
if (completed !== void 0) {
|
|
30061
|
+
await notifyReviewCompleted(completed, await getAnnotationsForReview(reviewId), exportPath);
|
|
30062
|
+
}
|
|
30063
|
+
} catch {
|
|
30064
|
+
}
|
|
28665
30065
|
return c.json({ status: "completed", exportPath, isCurrent, reviewId, hook });
|
|
28666
30066
|
});
|
|
28667
30067
|
reviewsRoutes.post("/review/reopen", async (c) => {
|
|
@@ -28723,8 +30123,9 @@ reviewsRoutes.post("/reviews/delete-all", async (c) => {
|
|
|
28723
30123
|
|
|
28724
30124
|
// src/routes/api/share-prompt.ts
|
|
28725
30125
|
init_zod();
|
|
28726
|
-
import { Hono as
|
|
28727
|
-
|
|
30126
|
+
import { Hono as Hono14 } from "hono";
|
|
30127
|
+
init_global_config();
|
|
30128
|
+
var sharePromptRoutes = new Hono14();
|
|
28728
30129
|
var SharePromptShapeSchema = external_exports.object({
|
|
28729
30130
|
dismissedAt: external_exports.number().nullable().optional(),
|
|
28730
30131
|
totalOpenMs: external_exports.number().optional()
|
|
@@ -28763,9 +30164,9 @@ sharePromptRoutes.post("/share-prompt/tick", async (c) => {
|
|
|
28763
30164
|
});
|
|
28764
30165
|
|
|
28765
30166
|
// src/routes/api/system.ts
|
|
28766
|
-
import { Hono as
|
|
30167
|
+
import { Hono as Hono15 } from "hono";
|
|
28767
30168
|
init_openOS();
|
|
28768
|
-
var systemRoutes = new
|
|
30169
|
+
var systemRoutes = new Hono15();
|
|
28769
30170
|
systemRoutes.post("/open-external", async (c) => {
|
|
28770
30171
|
const parsed = await parseBody(c, OpenExternalReqSchema);
|
|
28771
30172
|
if (!parsed.ok) return parsed.response;
|
|
@@ -28777,7 +30178,7 @@ systemRoutes.post("/open-external", async (c) => {
|
|
|
28777
30178
|
});
|
|
28778
30179
|
|
|
28779
30180
|
// src/routes/api.ts
|
|
28780
|
-
var apiRoutes = new
|
|
30181
|
+
var apiRoutes = new Hono16();
|
|
28781
30182
|
apiRoutes.route("/", reviewsRoutes);
|
|
28782
30183
|
apiRoutes.route("/", filesRoutes);
|
|
28783
30184
|
apiRoutes.route("/", annotationsRoutes);
|
|
@@ -28785,22 +30186,24 @@ apiRoutes.route("/", attachmentsRoutes);
|
|
|
28785
30186
|
apiRoutes.route("/", outlineRoutes);
|
|
28786
30187
|
apiRoutes.route("/", contextRoutes);
|
|
28787
30188
|
apiRoutes.route("/", projectSettingsRoutes);
|
|
30189
|
+
apiRoutes.route("/", pluginsRoutes);
|
|
28788
30190
|
apiRoutes.route("/", imageRoutes);
|
|
28789
30191
|
apiRoutes.route("/", reviewNotesRoutes);
|
|
28790
30192
|
apiRoutes.route("/", sharePromptRoutes);
|
|
28791
30193
|
apiRoutes.route("/", systemRoutes);
|
|
28792
30194
|
|
|
28793
30195
|
// src/routes/channel-api.ts
|
|
28794
|
-
import { spawnSync as
|
|
28795
|
-
import { mkdirSync as
|
|
28796
|
-
import { Hono as
|
|
28797
|
-
import { join as
|
|
28798
|
-
|
|
30196
|
+
import { spawnSync as spawnSync12 } from "child_process";
|
|
30197
|
+
import { mkdirSync as mkdirSync12 } from "fs";
|
|
30198
|
+
import { Hono as Hono17 } from "hono";
|
|
30199
|
+
import { join as join18 } from "path";
|
|
30200
|
+
init_global_config();
|
|
30201
|
+
var channelApiRoutes = new Hono17();
|
|
28799
30202
|
channelApiRoutes.get("/status", async (c) => {
|
|
28800
30203
|
const config2 = readGlobalConfig();
|
|
28801
30204
|
const enabled = config2.channelEnabled === true;
|
|
28802
30205
|
const repoRoot = c.get("repoRoot");
|
|
28803
|
-
const dataDir =
|
|
30206
|
+
const dataDir = join18(repoRoot, ".glassbox");
|
|
28804
30207
|
const connected = enabled ? await isChannelAlive(dataDir) : false;
|
|
28805
30208
|
return c.json({ enabled, connected });
|
|
28806
30209
|
});
|
|
@@ -28809,8 +30212,8 @@ channelApiRoutes.post("/enable", (c) => {
|
|
|
28809
30212
|
config2.channelEnabled = true;
|
|
28810
30213
|
});
|
|
28811
30214
|
const repoRoot = c.get("repoRoot");
|
|
28812
|
-
const dataDir =
|
|
28813
|
-
|
|
30215
|
+
const dataDir = join18(repoRoot, ".glassbox");
|
|
30216
|
+
mkdirSync12(dataDir, { recursive: true });
|
|
28814
30217
|
registerChannel(dataDir);
|
|
28815
30218
|
return c.json({ ok: true });
|
|
28816
30219
|
});
|
|
@@ -28819,7 +30222,7 @@ channelApiRoutes.post("/disable", (c) => {
|
|
|
28819
30222
|
config2.channelEnabled = false;
|
|
28820
30223
|
});
|
|
28821
30224
|
const repoRoot = c.get("repoRoot");
|
|
28822
|
-
const dataDir =
|
|
30225
|
+
const dataDir = join18(repoRoot, ".glassbox");
|
|
28823
30226
|
unregisterChannel(dataDir);
|
|
28824
30227
|
return c.json({ ok: true });
|
|
28825
30228
|
});
|
|
@@ -28827,7 +30230,7 @@ channelApiRoutes.post("/trigger", async (c) => {
|
|
|
28827
30230
|
const parsed = await parseBody(c, TriggerChannelReqSchema);
|
|
28828
30231
|
if (!parsed.ok) return parsed.response;
|
|
28829
30232
|
const repoRoot = c.get("repoRoot");
|
|
28830
|
-
const dataDir =
|
|
30233
|
+
const dataDir = join18(repoRoot, ".glassbox");
|
|
28831
30234
|
const sent = await triggerChannel(dataDir, parsed.data.message);
|
|
28832
30235
|
if (!sent) {
|
|
28833
30236
|
return c.json({ error: "Channel not connected" }, 503);
|
|
@@ -28836,7 +30239,7 @@ channelApiRoutes.post("/trigger", async (c) => {
|
|
|
28836
30239
|
});
|
|
28837
30240
|
channelApiRoutes.get("/claude-check", (c) => {
|
|
28838
30241
|
try {
|
|
28839
|
-
const result =
|
|
30242
|
+
const result = spawnSync12("claude", ["--version"], { encoding: "utf-8", timeout: 5e3 });
|
|
28840
30243
|
if (result.status !== 0) {
|
|
28841
30244
|
return c.json({ installed: false, version: null, meetsMinimum: false });
|
|
28842
30245
|
}
|
|
@@ -28855,13 +30258,13 @@ channelApiRoutes.get("/claude-check", (c) => {
|
|
|
28855
30258
|
});
|
|
28856
30259
|
|
|
28857
30260
|
// src/routes/difftool-api.ts
|
|
28858
|
-
import { Hono as
|
|
30261
|
+
import { Hono as Hono18 } from "hono";
|
|
28859
30262
|
init_connection();
|
|
28860
30263
|
init_queries();
|
|
28861
30264
|
init_session();
|
|
28862
30265
|
init_difftool();
|
|
28863
30266
|
init_image_blobs();
|
|
28864
|
-
var difftoolApiRoutes = new
|
|
30267
|
+
var difftoolApiRoutes = new Hono18();
|
|
28865
30268
|
difftoolApiRoutes.get("/status", (c) => {
|
|
28866
30269
|
return c.json(getDifftoolStatus("global"));
|
|
28867
30270
|
});
|
|
@@ -28938,8 +30341,8 @@ difftoolApiRoutes.post("/end", (c) => {
|
|
|
28938
30341
|
});
|
|
28939
30342
|
|
|
28940
30343
|
// src/routes/pages.tsx
|
|
28941
|
-
import { readFileSync as
|
|
28942
|
-
import { Hono as
|
|
30344
|
+
import { readFileSync as readFileSync18 } from "fs";
|
|
30345
|
+
import { Hono as Hono19 } from "hono";
|
|
28943
30346
|
import { resolve as resolve11 } from "path";
|
|
28944
30347
|
|
|
28945
30348
|
// src/components/diffView.tsx
|
|
@@ -29730,7 +31133,28 @@ function ReviewNoteRows({ notes, repliesByNote }) {
|
|
|
29730
31133
|
/* @__PURE__ */ jsx4("button", { className: "ai-note-discard-btn", title: "Remove this note from .pr-notes/", children: "Discard" })
|
|
29731
31134
|
] }) : null
|
|
29732
31135
|
] }),
|
|
29733
|
-
n.artifacts !== void 0 && n.artifacts.length > 0 ? /* @__PURE__ */ jsx4("div", { className: "ai-note-artifacts", children: n.artifacts.map((a) => a.
|
|
31136
|
+
n.artifacts !== void 0 && n.artifacts.length > 0 ? /* @__PURE__ */ jsx4("div", { className: "ai-note-artifacts", children: n.artifacts.map((a) => a.renderedSvg !== void 0 ? /* @__PURE__ */ jsxs4("details", { className: "ai-note-artifact", open: true, children: [
|
|
31137
|
+
/* @__PURE__ */ jsxs4("summary", { className: "ai-note-artifact-label", children: [
|
|
31138
|
+
/* @__PURE__ */ jsx4(IconPaperclip, {}),
|
|
31139
|
+
/* @__PURE__ */ jsx4("span", { children: a.uri })
|
|
31140
|
+
] }),
|
|
31141
|
+
/* @__PURE__ */ jsx4("div", { className: "ai-note-artifact-imgwrap", children: /* @__PURE__ */ jsx4(
|
|
31142
|
+
"img",
|
|
31143
|
+
{
|
|
31144
|
+
className: "ai-note-artifact-img",
|
|
31145
|
+
loading: "lazy",
|
|
31146
|
+
alt: a.uri,
|
|
31147
|
+
draggable: false,
|
|
31148
|
+
src: `data:image/svg+xml;utf8,${encodeURIComponent(a.renderedSvg)}`
|
|
31149
|
+
}
|
|
31150
|
+
) })
|
|
31151
|
+
] }) : a.renderedHtml !== void 0 ? /* @__PURE__ */ jsxs4("details", { className: "ai-note-artifact", open: true, children: [
|
|
31152
|
+
/* @__PURE__ */ jsxs4("summary", { className: "ai-note-artifact-label", children: [
|
|
31153
|
+
/* @__PURE__ */ jsx4(IconPaperclip, {}),
|
|
31154
|
+
/* @__PURE__ */ jsx4("span", { children: a.uri })
|
|
31155
|
+
] }),
|
|
31156
|
+
/* @__PURE__ */ jsx4("div", { className: "ai-note-artifact-rendered", children: raw(a.renderedHtml) })
|
|
31157
|
+
] }) : a.content !== void 0 ? /* @__PURE__ */ jsxs4("details", { className: "ai-note-artifact", children: [
|
|
29734
31158
|
/* @__PURE__ */ jsxs4("summary", { className: "ai-note-artifact-label", children: [
|
|
29735
31159
|
/* @__PURE__ */ jsx4(IconPaperclip, {}),
|
|
29736
31160
|
/* @__PURE__ */ jsx4("span", { children: a.uri })
|
|
@@ -29801,9 +31225,10 @@ function AnnotationRows({ annotations }) {
|
|
|
29801
31225
|
}
|
|
29802
31226
|
|
|
29803
31227
|
// src/themes/config.ts
|
|
29804
|
-
import { existsSync as
|
|
29805
|
-
import { join as
|
|
29806
|
-
|
|
31228
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync13, readdirSync as readdirSync4, readFileSync as readFileSync17, unlinkSync as unlinkSync3, writeFileSync as writeFileSync13 } from "fs";
|
|
31229
|
+
import { join as join19 } from "path";
|
|
31230
|
+
init_global_config();
|
|
31231
|
+
var THEMES_DIR = join19(GLOBAL_CONFIG_DIR, "themes");
|
|
29807
31232
|
function getActiveThemeId() {
|
|
29808
31233
|
const config2 = readGlobalConfig();
|
|
29809
31234
|
const theme = config2.theme;
|
|
@@ -29817,13 +31242,13 @@ function setActiveThemeId(id) {
|
|
|
29817
31242
|
});
|
|
29818
31243
|
}
|
|
29819
31244
|
function loadCustomThemes() {
|
|
29820
|
-
if (!
|
|
31245
|
+
if (!existsSync15(THEMES_DIR)) return [];
|
|
29821
31246
|
const themes = [];
|
|
29822
31247
|
try {
|
|
29823
|
-
const files =
|
|
31248
|
+
const files = readdirSync4(THEMES_DIR).filter((f) => f.endsWith(".json"));
|
|
29824
31249
|
for (const file2 of files) {
|
|
29825
31250
|
try {
|
|
29826
|
-
const parsed = StoredCustomThemeSchema.safeParse(JSON.parse(
|
|
31251
|
+
const parsed = StoredCustomThemeSchema.safeParse(JSON.parse(readFileSync17(join19(THEMES_DIR, file2), "utf-8")));
|
|
29827
31252
|
if (!parsed.success) continue;
|
|
29828
31253
|
const d = parsed.data;
|
|
29829
31254
|
themes.push({ id: d.id, name: d.name, colors: d.colors, builtIn: false, baseTheme: d.baseTheme ?? "" });
|
|
@@ -29835,21 +31260,21 @@ function loadCustomThemes() {
|
|
|
29835
31260
|
return themes;
|
|
29836
31261
|
}
|
|
29837
31262
|
function saveCustomTheme(theme) {
|
|
29838
|
-
|
|
29839
|
-
const filePath =
|
|
29840
|
-
|
|
31263
|
+
mkdirSync13(THEMES_DIR, { recursive: true });
|
|
31264
|
+
const filePath = join19(THEMES_DIR, `${theme.id}.json`);
|
|
31265
|
+
writeFileSync13(filePath, JSON.stringify(theme, null, 2), "utf-8");
|
|
29841
31266
|
}
|
|
29842
31267
|
function deleteCustomTheme(id) {
|
|
29843
|
-
const filePath =
|
|
29844
|
-
if (
|
|
31268
|
+
const filePath = join19(THEMES_DIR, `${id}.json`);
|
|
31269
|
+
if (existsSync15(filePath)) {
|
|
29845
31270
|
unlinkSync3(filePath);
|
|
29846
31271
|
}
|
|
29847
31272
|
}
|
|
29848
31273
|
function getCustomTheme(id) {
|
|
29849
|
-
const filePath =
|
|
29850
|
-
if (!
|
|
31274
|
+
const filePath = join19(THEMES_DIR, `${id}.json`);
|
|
31275
|
+
if (!existsSync15(filePath)) return void 0;
|
|
29851
31276
|
try {
|
|
29852
|
-
const parsed = StoredCustomThemeSchema.safeParse(JSON.parse(
|
|
31277
|
+
const parsed = StoredCustomThemeSchema.safeParse(JSON.parse(readFileSync17(filePath, "utf-8")));
|
|
29853
31278
|
if (!parsed.success) return void 0;
|
|
29854
31279
|
const d = parsed.data;
|
|
29855
31280
|
return { id: d.id, name: d.name, colors: d.colors, builtIn: false, baseTheme: d.baseTheme ?? "" };
|
|
@@ -30049,7 +31474,10 @@ function ReviewShell({ reviewId, review, files, annotationCounts, staleCounts, f
|
|
|
30049
31474
|
/* @__PURE__ */ jsx8("div", { className: "file-filter", children: /* @__PURE__ */ jsx8("input", { type: "text", className: "file-filter-input", id: "file-filter", placeholder: "Filter files..." }) }),
|
|
30050
31475
|
/* @__PURE__ */ jsx8(FileList, { files, annotationCounts, staleCounts }),
|
|
30051
31476
|
/* @__PURE__ */ jsx8("div", { className: "sidebar-share", id: "sidebar-share" }),
|
|
30052
|
-
/* @__PURE__ */
|
|
31477
|
+
/* @__PURE__ */ jsxs8("div", { className: "sidebar-footer", children: [
|
|
31478
|
+
footer,
|
|
31479
|
+
/* @__PURE__ */ jsx8("div", { className: "plugin-ui-slot", id: "plugin-ui-sidebar-footer" })
|
|
31480
|
+
] })
|
|
30053
31481
|
] }),
|
|
30054
31482
|
/* @__PURE__ */ jsx8("div", { className: "sidebar-resize", id: "sidebar-resize" }),
|
|
30055
31483
|
/* @__PURE__ */ jsxs8("main", { className: "main-content", children: [
|
|
@@ -30068,7 +31496,8 @@ function ReviewShell({ reviewId, review, files, annotationCounts, staleCounts, f
|
|
|
30068
31496
|
] }) }),
|
|
30069
31497
|
/* @__PURE__ */ jsx8("button", { className: "nav-btn disabled", id: "nav-back-btn", disabled: true, title: "Back", children: /* @__PURE__ */ jsx8("svg", { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx8("path", { d: "m15 18-6-6 6-6" }) }) }),
|
|
30070
31498
|
/* @__PURE__ */ jsx8("button", { className: "nav-btn disabled", id: "nav-forward-btn", disabled: true, title: "Forward", children: /* @__PURE__ */ jsx8("svg", { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx8("path", { d: "m9 18 6-6-6-6" }) }) }),
|
|
30071
|
-
/* @__PURE__ */ jsx8("span", { className: "nav-file-path", id: "nav-file-path" })
|
|
31499
|
+
/* @__PURE__ */ jsx8("span", { className: "nav-file-path", id: "nav-file-path" }),
|
|
31500
|
+
/* @__PURE__ */ jsx8("span", { className: "plugin-ui-slot", id: "plugin-ui-header" })
|
|
30072
31501
|
] }),
|
|
30073
31502
|
/* @__PURE__ */ jsx8("div", { className: "diff-container", id: "diff-container", style: "display:none" }),
|
|
30074
31503
|
/* @__PURE__ */ jsxs8("div", { className: "diff-toolbar", id: "diff-toolbar", style: "display:none", children: [
|
|
@@ -30109,7 +31538,8 @@ function ReviewShell({ reviewId, review, files, annotationCounts, staleCounts, f
|
|
|
30109
31538
|
/* @__PURE__ */ jsx8("button", { className: "image-zoom-btn", "data-zoom-action": "actual", title: "Actual size (1:1)", children: /* @__PURE__ */ jsx8(IconActualSize, {}) }),
|
|
30110
31539
|
/* @__PURE__ */ jsx8("button", { className: "image-zoom-btn", "data-zoom-action": "in", title: "Zoom in", children: /* @__PURE__ */ jsx8(IconZoomIn, {}) })
|
|
30111
31540
|
] })
|
|
30112
|
-
] })
|
|
31541
|
+
] }),
|
|
31542
|
+
/* @__PURE__ */ jsx8("div", { className: "plugin-ui-slot", id: "plugin-ui-diff-toolbar" })
|
|
30113
31543
|
] })
|
|
30114
31544
|
] })
|
|
30115
31545
|
] })
|
|
@@ -30143,6 +31573,25 @@ function svgUsesExternalFonts(svgData) {
|
|
|
30143
31573
|
return /<text[\s>]/i.test(svg) || /font-family/i.test(svg) || /@font-face/i.test(svg);
|
|
30144
31574
|
}
|
|
30145
31575
|
|
|
31576
|
+
// src/plugins/artifacts.ts
|
|
31577
|
+
init_plugins();
|
|
31578
|
+
async function renderNoteArtifacts(views) {
|
|
31579
|
+
for (const view of views) {
|
|
31580
|
+
for (const artifact of view.artifacts ?? []) {
|
|
31581
|
+
if (artifact.isImage === true || artifact.content === void 0) continue;
|
|
31582
|
+
const rendered = await renderContent({
|
|
31583
|
+
bytes: new TextEncoder().encode(artifact.content),
|
|
31584
|
+
text: artifact.content,
|
|
31585
|
+
path: artifact.uri
|
|
31586
|
+
});
|
|
31587
|
+
if (rendered === null) continue;
|
|
31588
|
+
if (rendered.svg !== void 0 && rendered.svg !== "") artifact.renderedSvg = rendered.svg;
|
|
31589
|
+
else if (rendered.html !== void 0 && rendered.html !== "") artifact.renderedHtml = rendered.html;
|
|
31590
|
+
}
|
|
31591
|
+
}
|
|
31592
|
+
return views;
|
|
31593
|
+
}
|
|
31594
|
+
|
|
30146
31595
|
// src/review-notes/reanchor.ts
|
|
30147
31596
|
var MATCH_RADIUS = 50;
|
|
30148
31597
|
function reanchorReviewNotes(notes, diff) {
|
|
@@ -30176,7 +31625,7 @@ function reanchorReviewNotes(notes, diff) {
|
|
|
30176
31625
|
// src/routes/pages.tsx
|
|
30177
31626
|
init_store();
|
|
30178
31627
|
import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs9 } from "kerfjs/jsx-runtime";
|
|
30179
|
-
var pageRoutes = new
|
|
31628
|
+
var pageRoutes = new Hono19();
|
|
30180
31629
|
pageRoutes.get("/", async (c) => {
|
|
30181
31630
|
const reviewId = c.get("reviewId");
|
|
30182
31631
|
const review = await getReview(reviewId);
|
|
@@ -30205,12 +31654,14 @@ pageRoutes.get("/file/:fileId", async (c) => {
|
|
|
30205
31654
|
const reviewMode = review ? parseModeString(review.mode) : null;
|
|
30206
31655
|
const imageSideLabels = reviewMode ? groundTruthSideLabels(reviewMode) : void 0;
|
|
30207
31656
|
const stepNav = reviewMode?.type === "ground-truth" ? groundTruthStepNav(reviewMode, file2.id, await getReviewFiles(file2.review_id)) : void 0;
|
|
30208
|
-
|
|
31657
|
+
const isSvg = isSvgFile(file2.file_path);
|
|
31658
|
+
const isPluginSvg = !isSvg && !diff.isBinary && pluginRendersFile(file2.file_path);
|
|
31659
|
+
if (view === "rendered" && (isSvg || isPluginSvg)) {
|
|
30209
31660
|
const repoRoot = c.get("repoRoot");
|
|
30210
31661
|
let fontWarning = false;
|
|
30211
31662
|
let svgBaseWidth = 300;
|
|
30212
31663
|
let svgBaseHeight = 150;
|
|
30213
|
-
if (reviewMode) {
|
|
31664
|
+
if (isSvg && reviewMode) {
|
|
30214
31665
|
const oldImg = diff.status !== "added" ? getOldImage(reviewMode, file2.file_path, diff.oldPath ?? null, repoRoot) : null;
|
|
30215
31666
|
const newImg = diff.status !== "deleted" ? getNewImage(reviewMode, file2.file_path, repoRoot) : null;
|
|
30216
31667
|
const svgData = newImg ?? oldImg;
|
|
@@ -30222,6 +31673,14 @@ pageRoutes.get("/file/:fileId", async (c) => {
|
|
|
30222
31673
|
if (oldImg && svgUsesExternalFonts(oldImg.data) || newImg && svgUsesExternalFonts(newImg.data)) {
|
|
30223
31674
|
fontWarning = true;
|
|
30224
31675
|
}
|
|
31676
|
+
} else if (isPluginSvg) {
|
|
31677
|
+
const svg = await renderPluginSvgSide(reviewMode, file2.file_path, diff.oldPath ?? file2.file_path, diff.status !== "deleted" ? "new" : "old", repoRoot);
|
|
31678
|
+
if (svg !== null) {
|
|
31679
|
+
const dims = parseSvgDimensions(svg);
|
|
31680
|
+
svgBaseWidth = dims.width;
|
|
31681
|
+
svgBaseHeight = dims.height;
|
|
31682
|
+
if (svgUsesExternalFonts(Buffer.from(svg))) fontWarning = true;
|
|
31683
|
+
}
|
|
30225
31684
|
}
|
|
30226
31685
|
const html2 = /* @__PURE__ */ jsxs9("div", { className: "diff-view", "data-file-id": file2.id, "data-file-path": file2.file_path, "data-is-svg": "true", children: [
|
|
30227
31686
|
/* @__PURE__ */ jsxs9("div", { className: "diff-header", children: [
|
|
@@ -30286,6 +31745,7 @@ pageRoutes.get("/file/:fileId", async (c) => {
|
|
|
30286
31745
|
}
|
|
30287
31746
|
const rawNotes = getDemoMode() !== null ? demoReviewNotes(file2.file_path) : loadReviewNotesForFile(c.get("repoRoot"), file2.file_path);
|
|
30288
31747
|
const reviewNotes = reanchorReviewNotes(rawNotes, finalDiff);
|
|
31748
|
+
await renderNoteArtifacts(reviewNotes);
|
|
30289
31749
|
const html = /* @__PURE__ */ jsx9(DiffView, { file: file2, diff: finalDiff, annotations, mode, reviewNotes, imageSideLabels, stepNav });
|
|
30290
31750
|
return c.html(html.toString());
|
|
30291
31751
|
});
|
|
@@ -30295,7 +31755,7 @@ pageRoutes.get("/file-raw", (c) => {
|
|
|
30295
31755
|
const repoRoot = c.get("repoRoot");
|
|
30296
31756
|
let content;
|
|
30297
31757
|
try {
|
|
30298
|
-
content =
|
|
31758
|
+
content = readFileSync18(resolve11(repoRoot, filePath), "utf-8");
|
|
30299
31759
|
} catch {
|
|
30300
31760
|
return c.text("File not found", 404);
|
|
30301
31761
|
}
|
|
@@ -30351,8 +31811,8 @@ pageRoutes.get("/history", async (c) => {
|
|
|
30351
31811
|
});
|
|
30352
31812
|
|
|
30353
31813
|
// src/routes/theme-api.ts
|
|
30354
|
-
import { Hono as
|
|
30355
|
-
var themeApiRoutes = new
|
|
31814
|
+
import { Hono as Hono20 } from "hono";
|
|
31815
|
+
var themeApiRoutes = new Hono20();
|
|
30356
31816
|
themeApiRoutes.get("/", (c) => {
|
|
30357
31817
|
const themes = getAllThemes();
|
|
30358
31818
|
const activeId = getActiveThemeId();
|
|
@@ -30479,7 +31939,7 @@ function tryServe(appFetch, port) {
|
|
|
30479
31939
|
});
|
|
30480
31940
|
}
|
|
30481
31941
|
async function startServer(port, reviewId, repoRoot, options) {
|
|
30482
|
-
const app = new
|
|
31942
|
+
const app = new Hono21();
|
|
30483
31943
|
const onCompleteCommand = options?.onComplete ?? null;
|
|
30484
31944
|
app.use("*", async (c, next) => {
|
|
30485
31945
|
c.set("reviewId", reviewId);
|
|
@@ -30488,22 +31948,22 @@ async function startServer(port, reviewId, repoRoot, options) {
|
|
|
30488
31948
|
c.set("onCompleteCommand", onCompleteCommand);
|
|
30489
31949
|
await next();
|
|
30490
31950
|
});
|
|
30491
|
-
const selfDir =
|
|
30492
|
-
const distDir =
|
|
31951
|
+
const selfDir = dirname8(fileURLToPath3(import.meta.url));
|
|
31952
|
+
const distDir = existsSync16(join20(selfDir, "client", "styles.css")) ? join20(selfDir, "client") : join20(selfDir, "..", "dist", "client");
|
|
30493
31953
|
app.get("/static/styles.css", (c) => {
|
|
30494
|
-
const css =
|
|
31954
|
+
const css = readFileSync19(join20(distDir, "styles.css"), "utf-8");
|
|
30495
31955
|
return c.text(css, 200, { "Content-Type": "text/css", "Cache-Control": "no-cache" });
|
|
30496
31956
|
});
|
|
30497
31957
|
app.get("/static/app.js", (c) => {
|
|
30498
|
-
const js =
|
|
31958
|
+
const js = readFileSync19(join20(distDir, "app.global.js"), "utf-8");
|
|
30499
31959
|
return c.text(js, 200, { "Content-Type": "application/javascript", "Cache-Control": "no-cache" });
|
|
30500
31960
|
});
|
|
30501
31961
|
app.get("/static/history.js", (c) => {
|
|
30502
|
-
const js =
|
|
31962
|
+
const js = readFileSync19(join20(distDir, "history.global.js"), "utf-8");
|
|
30503
31963
|
return c.text(js, 200, { "Content-Type": "application/javascript", "Cache-Control": "no-cache" });
|
|
30504
31964
|
});
|
|
30505
31965
|
app.get("/favicon.svg", (c) => {
|
|
30506
|
-
const svg =
|
|
31966
|
+
const svg = readFileSync19(join20(distDir, "favicon.svg"), "utf-8");
|
|
30507
31967
|
return c.body(svg, 200, { "Content-Type": "image/svg+xml", "Cache-Control": "no-cache" });
|
|
30508
31968
|
});
|
|
30509
31969
|
app.get("/favicon.ico", (c) => c.body(null, 204));
|
|
@@ -30538,10 +31998,16 @@ async function startServer(port, reviewId, repoRoot, options) {
|
|
|
30538
31998
|
console.log(`
|
|
30539
31999
|
Glassbox running at ${url2}
|
|
30540
32000
|
`);
|
|
32001
|
+
await initContentPlugins(repoRoot);
|
|
32002
|
+
try {
|
|
32003
|
+
const review = await getReview(reviewId);
|
|
32004
|
+
if (review !== void 0) await notifyReviewCreated(review);
|
|
32005
|
+
} catch {
|
|
32006
|
+
}
|
|
30541
32007
|
try {
|
|
30542
32008
|
const globalConfig2 = readGlobalConfig();
|
|
30543
32009
|
if (globalConfig2.channelEnabled === true) {
|
|
30544
|
-
const dataDir =
|
|
32010
|
+
const dataDir = join20(repoRoot, ".glassbox");
|
|
30545
32011
|
registerChannel(dataDir);
|
|
30546
32012
|
}
|
|
30547
32013
|
} catch {
|
|
@@ -30556,8 +32022,8 @@ async function startServer(port, reviewId, repoRoot, options) {
|
|
|
30556
32022
|
}
|
|
30557
32023
|
|
|
30558
32024
|
// src/skills.ts
|
|
30559
|
-
import { existsSync as
|
|
30560
|
-
import { join as
|
|
32025
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync14, readFileSync as readFileSync20, writeFileSync as writeFileSync14 } from "fs";
|
|
32026
|
+
import { join as join21 } from "path";
|
|
30561
32027
|
var SKILL_VERSION = 1;
|
|
30562
32028
|
function versionHeader() {
|
|
30563
32029
|
return `<!-- glassbox-skill-version: ${SKILL_VERSION} -->`;
|
|
@@ -30568,14 +32034,14 @@ function parseVersionHeader(content) {
|
|
|
30568
32034
|
return parseInt(match[1], 10);
|
|
30569
32035
|
}
|
|
30570
32036
|
function updateFile(path, content) {
|
|
30571
|
-
if (
|
|
30572
|
-
const existing =
|
|
32037
|
+
if (existsSync17(path)) {
|
|
32038
|
+
const existing = readFileSync20(path, "utf-8");
|
|
30573
32039
|
const version2 = parseVersionHeader(existing);
|
|
30574
32040
|
if (version2 !== null && version2 >= SKILL_VERSION) {
|
|
30575
32041
|
return false;
|
|
30576
32042
|
}
|
|
30577
32043
|
}
|
|
30578
|
-
|
|
32044
|
+
writeFileSync14(path, content, "utf-8");
|
|
30579
32045
|
return true;
|
|
30580
32046
|
}
|
|
30581
32047
|
function skillBody() {
|
|
@@ -30595,8 +32061,8 @@ function skillBody() {
|
|
|
30595
32061
|
].join("\n");
|
|
30596
32062
|
}
|
|
30597
32063
|
function ensureClaudeSkills(cwd) {
|
|
30598
|
-
const dir =
|
|
30599
|
-
|
|
32064
|
+
const dir = join21(cwd, ".claude", "skills", "glassbox");
|
|
32065
|
+
mkdirSync14(dir, { recursive: true });
|
|
30600
32066
|
const content = [
|
|
30601
32067
|
"---",
|
|
30602
32068
|
"name: glassbox",
|
|
@@ -30608,11 +32074,11 @@ function ensureClaudeSkills(cwd) {
|
|
|
30608
32074
|
skillBody(),
|
|
30609
32075
|
""
|
|
30610
32076
|
].join("\n");
|
|
30611
|
-
return updateFile(
|
|
32077
|
+
return updateFile(join21(dir, "SKILL.md"), content);
|
|
30612
32078
|
}
|
|
30613
32079
|
function ensureCursorRules(cwd) {
|
|
30614
|
-
const rulesDir =
|
|
30615
|
-
|
|
32080
|
+
const rulesDir = join21(cwd, ".cursor", "rules");
|
|
32081
|
+
mkdirSync14(rulesDir, { recursive: true });
|
|
30616
32082
|
const content = [
|
|
30617
32083
|
"---",
|
|
30618
32084
|
"description: Read the latest Glassbox code review and apply all feedback annotations",
|
|
@@ -30623,11 +32089,11 @@ function ensureCursorRules(cwd) {
|
|
|
30623
32089
|
skillBody(),
|
|
30624
32090
|
""
|
|
30625
32091
|
].join("\n");
|
|
30626
|
-
return updateFile(
|
|
32092
|
+
return updateFile(join21(rulesDir, "glassbox.mdc"), content);
|
|
30627
32093
|
}
|
|
30628
32094
|
function ensureCopilotPrompts(cwd) {
|
|
30629
|
-
const promptsDir =
|
|
30630
|
-
|
|
32095
|
+
const promptsDir = join21(cwd, ".github", "prompts");
|
|
32096
|
+
mkdirSync14(promptsDir, { recursive: true });
|
|
30631
32097
|
const content = [
|
|
30632
32098
|
"---",
|
|
30633
32099
|
"description: Read the latest Glassbox code review and apply all feedback annotations",
|
|
@@ -30637,11 +32103,11 @@ function ensureCopilotPrompts(cwd) {
|
|
|
30637
32103
|
skillBody(),
|
|
30638
32104
|
""
|
|
30639
32105
|
].join("\n");
|
|
30640
|
-
return updateFile(
|
|
32106
|
+
return updateFile(join21(promptsDir, "glassbox.prompt.md"), content);
|
|
30641
32107
|
}
|
|
30642
32108
|
function ensureWindsurfRules(cwd) {
|
|
30643
|
-
const rulesDir =
|
|
30644
|
-
|
|
32109
|
+
const rulesDir = join21(cwd, ".windsurf", "rules");
|
|
32110
|
+
mkdirSync14(rulesDir, { recursive: true });
|
|
30645
32111
|
const content = [
|
|
30646
32112
|
"---",
|
|
30647
32113
|
"trigger: manual",
|
|
@@ -30652,21 +32118,21 @@ function ensureWindsurfRules(cwd) {
|
|
|
30652
32118
|
skillBody(),
|
|
30653
32119
|
""
|
|
30654
32120
|
].join("\n");
|
|
30655
|
-
return updateFile(
|
|
32121
|
+
return updateFile(join21(rulesDir, "glassbox.md"), content);
|
|
30656
32122
|
}
|
|
30657
32123
|
function ensureSkills() {
|
|
30658
32124
|
const cwd = process.cwd();
|
|
30659
32125
|
const platforms = [];
|
|
30660
|
-
if (
|
|
32126
|
+
if (existsSync17(join21(cwd, ".claude"))) {
|
|
30661
32127
|
if (ensureClaudeSkills(cwd)) platforms.push("Claude Code");
|
|
30662
32128
|
}
|
|
30663
|
-
if (
|
|
32129
|
+
if (existsSync17(join21(cwd, ".cursor"))) {
|
|
30664
32130
|
if (ensureCursorRules(cwd)) platforms.push("Cursor");
|
|
30665
32131
|
}
|
|
30666
|
-
if (
|
|
32132
|
+
if (existsSync17(join21(cwd, ".github", "prompts")) || existsSync17(join21(cwd, ".github", "copilot-instructions.md"))) {
|
|
30667
32133
|
if (ensureCopilotPrompts(cwd)) platforms.push("GitHub Copilot");
|
|
30668
32134
|
}
|
|
30669
|
-
if (
|
|
32135
|
+
if (existsSync17(join21(cwd, ".windsurf"))) {
|
|
30670
32136
|
if (ensureWindsurfRules(cwd)) platforms.push("Windsurf");
|
|
30671
32137
|
}
|
|
30672
32138
|
return platforms;
|
|
@@ -30674,19 +32140,19 @@ function ensureSkills() {
|
|
|
30674
32140
|
|
|
30675
32141
|
// src/update-check.ts
|
|
30676
32142
|
init_zod();
|
|
30677
|
-
import { existsSync as
|
|
32143
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync15, readFileSync as readFileSync21, writeFileSync as writeFileSync15 } from "fs";
|
|
30678
32144
|
import { get } from "https";
|
|
30679
32145
|
import { homedir as homedir2 } from "os";
|
|
30680
|
-
import { dirname as
|
|
30681
|
-
import { fileURLToPath as
|
|
30682
|
-
var DATA_DIR =
|
|
30683
|
-
var CHECK_FILE =
|
|
32146
|
+
import { dirname as dirname9, join as join22 } from "path";
|
|
32147
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
32148
|
+
var DATA_DIR = join22(homedir2(), ".glassbox");
|
|
32149
|
+
var CHECK_FILE = join22(DATA_DIR, "last-update-check");
|
|
30684
32150
|
var PACKAGE_NAME = "glassbox";
|
|
30685
32151
|
var VersionPayloadSchema = external_exports.object({ version: external_exports.string() });
|
|
30686
32152
|
function getCurrentVersion() {
|
|
30687
32153
|
try {
|
|
30688
|
-
const dir =
|
|
30689
|
-
const raw2 = JSON.parse(
|
|
32154
|
+
const dir = dirname9(fileURLToPath4(import.meta.url));
|
|
32155
|
+
const raw2 = JSON.parse(readFileSync21(join22(dir, "..", "package.json"), "utf-8"));
|
|
30690
32156
|
return VersionPayloadSchema.parse(raw2).version;
|
|
30691
32157
|
} catch {
|
|
30692
32158
|
return "0.0.0";
|
|
@@ -30694,16 +32160,16 @@ function getCurrentVersion() {
|
|
|
30694
32160
|
}
|
|
30695
32161
|
function getLastCheckDate() {
|
|
30696
32162
|
try {
|
|
30697
|
-
if (
|
|
30698
|
-
return
|
|
32163
|
+
if (existsSync18(CHECK_FILE)) {
|
|
32164
|
+
return readFileSync21(CHECK_FILE, "utf-8").trim();
|
|
30699
32165
|
}
|
|
30700
32166
|
} catch {
|
|
30701
32167
|
}
|
|
30702
32168
|
return null;
|
|
30703
32169
|
}
|
|
30704
32170
|
function saveCheckDate() {
|
|
30705
|
-
|
|
30706
|
-
|
|
32171
|
+
mkdirSync15(DATA_DIR, { recursive: true });
|
|
32172
|
+
writeFileSync15(CHECK_FILE, (/* @__PURE__ */ new Date()).toISOString().slice(0, 10), "utf-8");
|
|
30707
32173
|
}
|
|
30708
32174
|
function isFirstUseToday() {
|
|
30709
32175
|
const last = getLastCheckDate();
|
|
@@ -30753,7 +32219,7 @@ function detectUpgradeCommand() {
|
|
|
30753
32219
|
}
|
|
30754
32220
|
return `npm update -g ${PACKAGE_NAME}`;
|
|
30755
32221
|
}
|
|
30756
|
-
function
|
|
32222
|
+
function compareVersions2(current, latest) {
|
|
30757
32223
|
const a = current.split(".").map(Number);
|
|
30758
32224
|
const b = latest.split(".").map(Number);
|
|
30759
32225
|
for (let i = 0; i < 3; i++) {
|
|
@@ -30767,7 +32233,7 @@ async function checkForUpdates(force) {
|
|
|
30767
32233
|
const current = getCurrentVersion();
|
|
30768
32234
|
const latest = await fetchLatestVersion();
|
|
30769
32235
|
saveCheckDate();
|
|
30770
|
-
if (latest === null ||
|
|
32236
|
+
if (latest === null || compareVersions2(current, latest) >= 0) return;
|
|
30771
32237
|
const cmd = detectUpgradeCommand();
|
|
30772
32238
|
const updateLine = `Update available: ${current} \u2192 ${latest}`;
|
|
30773
32239
|
const cmdLine = `Run: ${cmd}`;
|
|
@@ -31008,23 +32474,23 @@ async function main() {
|
|
|
31008
32474
|
console.log("AI service test mode enabled \u2014 using mock AI responses");
|
|
31009
32475
|
}
|
|
31010
32476
|
if (debug) {
|
|
31011
|
-
console.log(`[debug] Build timestamp: ${"2026-07-
|
|
32477
|
+
console.log(`[debug] Build timestamp: ${"2026-07-22T04:50:30.153Z"}`);
|
|
31012
32478
|
}
|
|
31013
32479
|
if (projectDir !== null) {
|
|
31014
|
-
if (!
|
|
32480
|
+
if (!existsSync20(projectDir) || !statSync8(projectDir).isDirectory()) {
|
|
31015
32481
|
console.error(`--project-dir is not a directory: ${projectDir}`);
|
|
31016
32482
|
process.exit(1);
|
|
31017
32483
|
}
|
|
31018
32484
|
process.chdir(projectDir);
|
|
31019
32485
|
}
|
|
31020
32486
|
if (dataDir === null) {
|
|
31021
|
-
dataDir =
|
|
32487
|
+
dataDir = join24(process.cwd(), ".glassbox");
|
|
31022
32488
|
}
|
|
31023
32489
|
if (difftoolServe) {
|
|
31024
32490
|
const { initDifftoolSession: initDifftoolSession2 } = await Promise.resolve().then(() => (init_session(), session_exports));
|
|
31025
32491
|
const { writeDiscovery: writeDiscovery2, clearDiscovery: clearDiscovery2, releaseStartingLock: releaseStartingLock2 } = await Promise.resolve().then(() => (init_difftool_discovery(), difftool_discovery_exports));
|
|
31026
32492
|
const { clearImageBlobs: clearImageBlobs2 } = await Promise.resolve().then(() => (init_image_blobs(), image_blobs_exports));
|
|
31027
|
-
|
|
32493
|
+
mkdirSync17(dataDir, { recursive: true });
|
|
31028
32494
|
setDataDir(dataDir);
|
|
31029
32495
|
ensureGlassboxGitignored(dataDir);
|
|
31030
32496
|
const sessionDataDir = dataDir;
|
|
@@ -31060,13 +32526,13 @@ async function main() {
|
|
|
31060
32526
|
}
|
|
31061
32527
|
process.exit(1);
|
|
31062
32528
|
}
|
|
31063
|
-
dataDir =
|
|
32529
|
+
dataDir = join24(tmpdir2(), `glassbox-demo-${demo}-${Date.now()}`);
|
|
31064
32530
|
setDemoMode(demo);
|
|
31065
32531
|
console.log(`
|
|
31066
32532
|
DEMO MODE: ${scenario.label}
|
|
31067
32533
|
`);
|
|
31068
32534
|
}
|
|
31069
|
-
|
|
32535
|
+
mkdirSync17(dataDir, { recursive: true });
|
|
31070
32536
|
if (demo === null) {
|
|
31071
32537
|
acquireLock(dataDir);
|
|
31072
32538
|
}
|
|
@@ -31091,12 +32557,12 @@ async function main() {
|
|
|
31091
32557
|
if (mode.type === "diff") {
|
|
31092
32558
|
const { pathA, pathB } = mode;
|
|
31093
32559
|
for (const p of [pathA, pathB]) {
|
|
31094
|
-
if (!
|
|
32560
|
+
if (!existsSync20(p)) {
|
|
31095
32561
|
console.error(`Error: path does not exist: ${p}`);
|
|
31096
32562
|
process.exit(1);
|
|
31097
32563
|
}
|
|
31098
32564
|
}
|
|
31099
|
-
if (
|
|
32565
|
+
if (statSync8(pathA).isDirectory() !== statSync8(pathB).isDirectory()) {
|
|
31100
32566
|
console.error("Error: --diff requires two files or two folders, not a mix of both.");
|
|
31101
32567
|
process.exit(1);
|
|
31102
32568
|
}
|
|
@@ -31113,17 +32579,19 @@ async function main() {
|
|
|
31113
32579
|
}
|
|
31114
32580
|
for (const entry of comparisons) {
|
|
31115
32581
|
for (const [role, p] of [["actual", entry.actualPath], ["expected", entry.expectedPath]]) {
|
|
31116
|
-
if (!
|
|
32582
|
+
if (!existsSync20(p)) {
|
|
31117
32583
|
console.error(`Error: ${role} image does not exist: ${p}`);
|
|
31118
32584
|
process.exit(1);
|
|
31119
32585
|
}
|
|
31120
32586
|
}
|
|
31121
32587
|
}
|
|
32588
|
+
const { initContentPlugins: initContentPlugins2 } = await Promise.resolve().then(() => (init_plugins(), plugins_exports));
|
|
32589
|
+
await initContentPlugins2(cwd);
|
|
31122
32590
|
const { comparePerceptual: comparePerceptual2 } = await Promise.resolve().then(() => (init_perceptual_diff(), perceptual_diff_exports));
|
|
31123
32591
|
let identical = 0;
|
|
31124
32592
|
let undecodable = 0;
|
|
31125
32593
|
for (const entry of comparisons) {
|
|
31126
|
-
const result = comparePerceptual2(entry.actualPath, entry.expectedPath);
|
|
32594
|
+
const result = await comparePerceptual2(entry.actualPath, entry.expectedPath);
|
|
31127
32595
|
groundTruthScores.set(entry.key, result.score);
|
|
31128
32596
|
if (result.reason === "undecodable") undecodable++;
|
|
31129
32597
|
else if (result.score === 0) identical++;
|