brew-tui 0.6.0 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -5
- package/build/{brewbar-installer-BAHS6EEZ.js → brewbar-installer-WJZKSELD.js} +3 -3
- package/build/brewbar-installer-WJZKSELD.js.map +1 -0
- package/build/{brewfile-manager-3SERRYNC.js → brewfile-manager-OITSKEHY.js} +4 -4
- package/build/{chunk-U2DRWB7A.js → chunk-5MYWF5D7.js} +2 -2
- package/build/{chunk-MSXH66I2.js → chunk-6NA4INJS.js} +31 -3
- package/build/chunk-6NA4INJS.js.map +1 -0
- package/build/chunk-EHIBIFCB.js +54 -0
- package/build/chunk-EHIBIFCB.js.map +1 -0
- package/build/{chunk-LXF72RCD.js → chunk-KSIAKLE2.js} +3 -3
- package/build/{chunk-4I344KQX.js → chunk-PVSE6XO7.js} +42 -2
- package/build/chunk-PVSE6XO7.js.map +1 -0
- package/build/chunk-QPXROTAP.js +679 -0
- package/build/chunk-QPXROTAP.js.map +1 -0
- package/build/{chunk-KVCVIRWI.js → chunk-Z2VN4VYQ.js} +2 -2
- package/build/compliance-checker-7NMFKWTI.js +12 -0
- package/build/{history-logger-PBDOLKNJ.js → history-logger-ZGYRAFON.js} +3 -3
- package/build/index.js +449 -678
- package/build/index.js.map +1 -1
- package/build/{snapshot-RAPGMAJF.js → snapshot-YWIOFQ5H.js} +7 -3
- package/build/{sync-engine-CAFK4LHA.js → sync-engine-DIYXV66P.js} +7 -5
- package/package.json +4 -4
- package/build/brewbar-installer-BAHS6EEZ.js.map +0 -1
- package/build/chunk-4I344KQX.js.map +0 -1
- package/build/chunk-AIAZQJKL.js +0 -299
- package/build/chunk-AIAZQJKL.js.map +0 -1
- package/build/chunk-MSXH66I2.js.map +0 -1
- package/build/chunk-UWS4A4F5.js +0 -25
- package/build/chunk-UWS4A4F5.js.map +0 -1
- package/build/compliance-checker-X7P623UF.js +0 -12
- /package/build/{brewfile-manager-3SERRYNC.js.map → brewfile-manager-OITSKEHY.js.map} +0 -0
- /package/build/{chunk-U2DRWB7A.js.map → chunk-5MYWF5D7.js.map} +0 -0
- /package/build/{chunk-LXF72RCD.js.map → chunk-KSIAKLE2.js.map} +0 -0
- /package/build/{chunk-KVCVIRWI.js.map → chunk-Z2VN4VYQ.js.map} +0 -0
- /package/build/{compliance-checker-X7P623UF.js.map → compliance-checker-7NMFKWTI.js.map} +0 -0
- /package/build/{history-logger-PBDOLKNJ.js.map → history-logger-ZGYRAFON.js.map} +0 -0
- /package/build/{snapshot-RAPGMAJF.js.map → snapshot-YWIOFQ5H.js.map} +0 -0
- /package/build/{sync-engine-CAFK4LHA.js.map → sync-engine-DIYXV66P.js.map} +0 -0
package/build/index.js
CHANGED
|
@@ -5,24 +5,31 @@ import {
|
|
|
5
5
|
loadBrewfile,
|
|
6
6
|
reconcile,
|
|
7
7
|
saveBrewfile
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-KSIAKLE2.js";
|
|
9
9
|
import {
|
|
10
|
+
activate,
|
|
10
11
|
applyConflictResolutions,
|
|
12
|
+
deactivate,
|
|
11
13
|
decryptPayload,
|
|
14
|
+
getDegradationLevel,
|
|
15
|
+
isExpired,
|
|
16
|
+
loadLicense,
|
|
12
17
|
loadSyncConfig,
|
|
18
|
+
needsRevalidation,
|
|
13
19
|
readSyncEnvelope,
|
|
20
|
+
revalidate,
|
|
14
21
|
sync
|
|
15
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-QPXROTAP.js";
|
|
16
23
|
import {
|
|
17
24
|
checkCompliance
|
|
18
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-5MYWF5D7.js";
|
|
19
26
|
import {
|
|
20
27
|
captureSnapshot,
|
|
21
28
|
execBrew,
|
|
22
29
|
loadSnapshots,
|
|
23
30
|
saveSnapshot,
|
|
24
31
|
streamBrew
|
|
25
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-PVSE6XO7.js";
|
|
26
33
|
import {
|
|
27
34
|
exportReport,
|
|
28
35
|
loadPolicy
|
|
@@ -32,13 +39,13 @@ import {
|
|
|
32
39
|
clearHistory,
|
|
33
40
|
detectAction,
|
|
34
41
|
loadHistory
|
|
35
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-Z2VN4VYQ.js";
|
|
36
43
|
import {
|
|
37
44
|
DATA_DIR,
|
|
38
|
-
LICENSE_PATH,
|
|
39
45
|
PROFILES_DIR,
|
|
40
|
-
ensureDataDirs
|
|
41
|
-
|
|
46
|
+
ensureDataDirs,
|
|
47
|
+
getMachineId
|
|
48
|
+
} from "./chunk-EHIBIFCB.js";
|
|
42
49
|
import {
|
|
43
50
|
fetchWithRetry,
|
|
44
51
|
fetchWithTimeout,
|
|
@@ -46,14 +53,14 @@ import {
|
|
|
46
53
|
t,
|
|
47
54
|
tp,
|
|
48
55
|
useLocaleStore
|
|
49
|
-
} from "./chunk-
|
|
56
|
+
} from "./chunk-6NA4INJS.js";
|
|
50
57
|
import {
|
|
51
58
|
logger
|
|
52
59
|
} from "./chunk-KDHEUNRI.js";
|
|
53
60
|
|
|
54
61
|
// src/index.tsx
|
|
55
62
|
import { createInterface } from "readline/promises";
|
|
56
|
-
import { rm as
|
|
63
|
+
import { rm as rm2 } from "fs/promises";
|
|
57
64
|
import { render } from "ink";
|
|
58
65
|
|
|
59
66
|
// src/app.tsx
|
|
@@ -137,7 +144,7 @@ function isTeamView(viewId) {
|
|
|
137
144
|
}
|
|
138
145
|
|
|
139
146
|
// src/utils/colors.ts
|
|
140
|
-
var
|
|
147
|
+
var PALETTE = {
|
|
141
148
|
success: "#22C55E",
|
|
142
149
|
error: "#EF4444",
|
|
143
150
|
warning: "#F59E0B",
|
|
@@ -155,6 +162,14 @@ var COLORS = {
|
|
|
155
162
|
border: "#4B5563",
|
|
156
163
|
white: "#FFFFFF"
|
|
157
164
|
};
|
|
165
|
+
function isNoColorRequested() {
|
|
166
|
+
const v = process.env["NO_COLOR"];
|
|
167
|
+
return typeof v === "string" && v.length > 0;
|
|
168
|
+
}
|
|
169
|
+
var NO_COLOR = isNoColorRequested();
|
|
170
|
+
var COLORS = NO_COLOR ? Object.fromEntries(
|
|
171
|
+
Object.keys(PALETTE).map((k) => [k, ""])
|
|
172
|
+
) : PALETTE;
|
|
158
173
|
|
|
159
174
|
// src/utils/gradient.tsx
|
|
160
175
|
import React, { useMemo } from "react";
|
|
@@ -176,6 +191,9 @@ function interpolateColor(c1, c2, t2) {
|
|
|
176
191
|
return rgbToHex(lerp(r1, r2, t2), lerp(g1, g2, t2), lerp(b1, b2, t2));
|
|
177
192
|
}
|
|
178
193
|
var GradientText = React.memo(function GradientText2({ children, colors, bold }) {
|
|
194
|
+
if (NO_COLOR) {
|
|
195
|
+
return /* @__PURE__ */ jsx(Text, { bold, children });
|
|
196
|
+
}
|
|
179
197
|
if (colors.length < 2) {
|
|
180
198
|
return /* @__PURE__ */ jsx(Text, { color: colors[0], bold, children });
|
|
181
199
|
}
|
|
@@ -205,6 +223,17 @@ var GRADIENTS = {
|
|
|
205
223
|
darkGold: ["#B8860B", "#8B6914", "#6B4F10"]
|
|
206
224
|
};
|
|
207
225
|
|
|
226
|
+
// src/utils/spacing.ts
|
|
227
|
+
var SPACING = {
|
|
228
|
+
none: 0,
|
|
229
|
+
xs: 1,
|
|
230
|
+
sm: 2,
|
|
231
|
+
md: 3,
|
|
232
|
+
lg: 4,
|
|
233
|
+
xl: 6,
|
|
234
|
+
xxl: 8
|
|
235
|
+
};
|
|
236
|
+
|
|
208
237
|
// src/components/layout/header.tsx
|
|
209
238
|
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
210
239
|
var LOGO_BREW = [
|
|
@@ -311,12 +340,12 @@ function Header() {
|
|
|
311
340
|
/* @__PURE__ */ jsx2(GradientText, { colors: GRADIENTS.gold, children: brew }),
|
|
312
341
|
/* @__PURE__ */ jsx2(GradientText, { colors: GRADIENTS.darkGold, children: LOGO_TUI[i] })
|
|
313
342
|
] }, i)) });
|
|
314
|
-
const menuBlock = /* @__PURE__ */ jsxs(Box, { borderStyle: "round", borderColor: COLORS.lavender, paddingX:
|
|
343
|
+
const menuBlock = /* @__PURE__ */ jsxs(Box, { borderStyle: "round", borderColor: COLORS.lavender, paddingX: SPACING.xs, flexDirection: "column", alignSelf: isNarrow ? "flex-start" : "center", children: [
|
|
315
344
|
/* @__PURE__ */ jsxs(Box, { flexDirection: "row", children: [
|
|
316
345
|
/* @__PURE__ */ jsx2(Box, { flexDirection: "column", children: COL1_VIEWS.map((view) => /* @__PURE__ */ jsx2(MenuItem, { view, currentView }, view)) }),
|
|
317
|
-
/* @__PURE__ */ jsx2(Box, { flexDirection: "column", marginLeft:
|
|
346
|
+
/* @__PURE__ */ jsx2(Box, { flexDirection: "column", marginLeft: SPACING.sm, children: COL2_VIEWS.map((view) => /* @__PURE__ */ jsx2(MenuItem, { view, currentView }, view)) })
|
|
318
347
|
] }),
|
|
319
|
-
/* @__PURE__ */ jsxs(Box, { borderStyle: "single", borderTop: true, borderBottom: false, borderLeft: false, borderRight: false, borderColor: COLORS.lavender, marginTop:
|
|
348
|
+
/* @__PURE__ */ jsxs(Box, { borderStyle: "single", borderTop: true, borderBottom: false, borderLeft: false, borderRight: false, borderColor: COLORS.lavender, marginTop: SPACING.none, children: [
|
|
320
349
|
/* @__PURE__ */ jsx2(Text2, { bold: true, color: COLORS.white, children: "S" }),
|
|
321
350
|
/* @__PURE__ */ jsxs(Text2, { color: COLORS.textSecondary, children: [
|
|
322
351
|
" ",
|
|
@@ -335,14 +364,14 @@ function Header() {
|
|
|
335
364
|
] })
|
|
336
365
|
] });
|
|
337
366
|
if (isNarrow) {
|
|
338
|
-
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX:
|
|
367
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: SPACING.xs, children: [
|
|
339
368
|
logoBlock,
|
|
340
|
-
/* @__PURE__ */ jsx2(Box, { marginTop:
|
|
369
|
+
/* @__PURE__ */ jsx2(Box, { marginTop: SPACING.xs, children: menuBlock })
|
|
341
370
|
] });
|
|
342
371
|
}
|
|
343
|
-
return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", paddingX:
|
|
372
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", paddingX: SPACING.xs, alignItems: "center", children: [
|
|
344
373
|
logoBlock,
|
|
345
|
-
/* @__PURE__ */ jsx2(Box, { marginLeft:
|
|
374
|
+
/* @__PURE__ */ jsx2(Box, { marginLeft: SPACING.sm, children: menuBlock })
|
|
346
375
|
] });
|
|
347
376
|
}
|
|
348
377
|
|
|
@@ -380,7 +409,7 @@ function Footer() {
|
|
|
380
409
|
const currentView = useNavigationStore((s) => s.currentView);
|
|
381
410
|
const locale = useLocaleStore((s) => s.locale);
|
|
382
411
|
const defs = VIEW_HINT_DEFS[currentView] ?? [];
|
|
383
|
-
return /* @__PURE__ */ jsxs2(Box2, { borderStyle: "single", borderTop: true, borderBottom: false, borderLeft: false, borderRight: false, borderColor: COLORS.gold, paddingX:
|
|
412
|
+
return /* @__PURE__ */ jsxs2(Box2, { borderStyle: "single", borderTop: true, borderBottom: false, borderLeft: false, borderRight: false, borderColor: COLORS.gold, paddingX: SPACING.xs, flexWrap: "wrap", children: [
|
|
384
413
|
defs.map((def, i) => {
|
|
385
414
|
const key = def.length === 1 ? def[0] : `${def[0]}:${def[1]}`;
|
|
386
415
|
return /* @__PURE__ */ jsxs2(React2.Fragment, { children: [
|
|
@@ -413,7 +442,7 @@ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
|
413
442
|
function AppLayout({ children }) {
|
|
414
443
|
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", width: "100%", children: [
|
|
415
444
|
/* @__PURE__ */ jsx4(Header, {}),
|
|
416
|
-
/* @__PURE__ */ jsx4(Box3, { flexDirection: "column", flexGrow: 1, paddingX:
|
|
445
|
+
/* @__PURE__ */ jsx4(Box3, { flexDirection: "column", flexGrow: 1, paddingX: SPACING.sm, paddingY: SPACING.xs, children }),
|
|
417
446
|
/* @__PURE__ */ jsx4(Footer, {})
|
|
418
447
|
] });
|
|
419
448
|
}
|
|
@@ -421,371 +450,6 @@ function AppLayout({ children }) {
|
|
|
421
450
|
// src/stores/license-store.ts
|
|
422
451
|
import { create as create2 } from "zustand";
|
|
423
452
|
|
|
424
|
-
// src/lib/license/license-manager.ts
|
|
425
|
-
import { readFile as readFile2, writeFile as writeFile2, rename, rm } from "fs/promises";
|
|
426
|
-
import { createCipheriv, createDecipheriv, randomBytes, scryptSync } from "crypto";
|
|
427
|
-
|
|
428
|
-
// src/lib/license/polar-api.ts
|
|
429
|
-
import { randomUUID } from "crypto";
|
|
430
|
-
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
431
|
-
import { join } from "path";
|
|
432
|
-
import { homedir } from "os";
|
|
433
|
-
var BASE_URL = "https://api.polar.sh/v1/customer-portal/license-keys";
|
|
434
|
-
var POLAR_ORGANIZATION_ID = "b8f245c0-d116-4457-92fb-1bda47139f82";
|
|
435
|
-
var DATA_DIR2 = join(homedir(), ".brew-tui");
|
|
436
|
-
var MACHINE_ID_PATH = join(DATA_DIR2, "machine-id");
|
|
437
|
-
async function getMachineId() {
|
|
438
|
-
try {
|
|
439
|
-
const id2 = (await readFile(MACHINE_ID_PATH, "utf-8")).trim();
|
|
440
|
-
if (id2) return id2;
|
|
441
|
-
} catch {
|
|
442
|
-
}
|
|
443
|
-
const id = randomUUID();
|
|
444
|
-
await mkdir(DATA_DIR2, { recursive: true, mode: 448 });
|
|
445
|
-
await writeFile(MACHINE_ID_PATH, id, { encoding: "utf-8", mode: 384 });
|
|
446
|
-
return id;
|
|
447
|
-
}
|
|
448
|
-
function validateApiUrl(url) {
|
|
449
|
-
const parsed = new URL(url);
|
|
450
|
-
if (parsed.protocol !== "https:") {
|
|
451
|
-
throw new Error("HTTPS required for license API");
|
|
452
|
-
}
|
|
453
|
-
if (!parsed.hostname.endsWith("polar.sh")) {
|
|
454
|
-
throw new Error("Invalid API host");
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
async function post(endpoint, body, expectEmpty = false) {
|
|
458
|
-
const url = `${BASE_URL}/${endpoint}`;
|
|
459
|
-
validateApiUrl(url);
|
|
460
|
-
const res = await fetchWithRetry(url, {
|
|
461
|
-
method: "POST",
|
|
462
|
-
headers: { "Content-Type": "application/json" },
|
|
463
|
-
body: JSON.stringify(body)
|
|
464
|
-
}, 15e3);
|
|
465
|
-
if (!res.ok) {
|
|
466
|
-
let message = `Request failed with status ${res.status}`;
|
|
467
|
-
try {
|
|
468
|
-
const errBody = await res.json();
|
|
469
|
-
if (typeof errBody.detail === "string") message = errBody.detail;
|
|
470
|
-
else if (typeof errBody.error === "string") message = errBody.error;
|
|
471
|
-
else if (typeof errBody.message === "string") message = errBody.message;
|
|
472
|
-
} catch {
|
|
473
|
-
}
|
|
474
|
-
throw new Error(message);
|
|
475
|
-
}
|
|
476
|
-
if (expectEmpty || res.status === 204) return void 0;
|
|
477
|
-
return res.json();
|
|
478
|
-
}
|
|
479
|
-
async function activateLicense(key) {
|
|
480
|
-
const machineId = await getMachineId();
|
|
481
|
-
const activation = await post("activate", {
|
|
482
|
-
key,
|
|
483
|
-
organization_id: POLAR_ORGANIZATION_ID,
|
|
484
|
-
label: machineId
|
|
485
|
-
// SEG-004: Use machine UUID instead of hostname
|
|
486
|
-
});
|
|
487
|
-
if (!activation || typeof activation.id !== "string" || !activation.license_key) {
|
|
488
|
-
throw new Error("Invalid activation response: missing required fields");
|
|
489
|
-
}
|
|
490
|
-
let customerEmail = "";
|
|
491
|
-
let customerName = "";
|
|
492
|
-
try {
|
|
493
|
-
const validated = await post("validate", {
|
|
494
|
-
key,
|
|
495
|
-
organization_id: POLAR_ORGANIZATION_ID,
|
|
496
|
-
activation_id: activation.id
|
|
497
|
-
});
|
|
498
|
-
customerEmail = validated.customer?.email ?? "";
|
|
499
|
-
customerName = validated.customer?.name ?? "";
|
|
500
|
-
} catch {
|
|
501
|
-
}
|
|
502
|
-
return {
|
|
503
|
-
activated: true,
|
|
504
|
-
error: null,
|
|
505
|
-
instance: { id: activation.id },
|
|
506
|
-
license_key: {
|
|
507
|
-
id: 0,
|
|
508
|
-
status: activation.license_key.status,
|
|
509
|
-
key,
|
|
510
|
-
activation_limit: 0,
|
|
511
|
-
activations_count: 0,
|
|
512
|
-
expires_at: activation.license_key.expires_at
|
|
513
|
-
},
|
|
514
|
-
meta: { customer_email: customerEmail, customer_name: customerName }
|
|
515
|
-
};
|
|
516
|
-
}
|
|
517
|
-
async function validateLicense(key, instanceId) {
|
|
518
|
-
const res = await post("validate", {
|
|
519
|
-
key,
|
|
520
|
-
organization_id: POLAR_ORGANIZATION_ID,
|
|
521
|
-
activation_id: instanceId
|
|
522
|
-
});
|
|
523
|
-
if (!res || typeof res.id !== "string" || typeof res.status !== "string" || !res.customer) {
|
|
524
|
-
throw new Error("Invalid validation response: missing required fields");
|
|
525
|
-
}
|
|
526
|
-
const notExpired = res.expires_at === null || new Date(res.expires_at) > /* @__PURE__ */ new Date();
|
|
527
|
-
const valid = res.status === "granted" && notExpired;
|
|
528
|
-
return {
|
|
529
|
-
valid,
|
|
530
|
-
error: valid ? null : `License ${res.status}`,
|
|
531
|
-
license_key: {
|
|
532
|
-
id: 0,
|
|
533
|
-
status: res.status,
|
|
534
|
-
key,
|
|
535
|
-
expires_at: res.expires_at
|
|
536
|
-
},
|
|
537
|
-
instance: { id: instanceId }
|
|
538
|
-
};
|
|
539
|
-
}
|
|
540
|
-
async function deactivateLicense(key, instanceId) {
|
|
541
|
-
await post(
|
|
542
|
-
"deactivate",
|
|
543
|
-
{ key, organization_id: POLAR_ORGANIZATION_ID, activation_id: instanceId },
|
|
544
|
-
true
|
|
545
|
-
);
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
// src/lib/license/license-manager.ts
|
|
549
|
-
var REVALIDATION_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
550
|
-
var GRACE_PERIOD_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
551
|
-
var ACTIVATION_COOLDOWN_MS = 3e4;
|
|
552
|
-
var MAX_ATTEMPTS = 5;
|
|
553
|
-
var LOCKOUT_MS = 15 * 60 * 1e3;
|
|
554
|
-
var tracker = {
|
|
555
|
-
attempts: 0,
|
|
556
|
-
lastAttempt: 0,
|
|
557
|
-
lockedUntil: 0
|
|
558
|
-
};
|
|
559
|
-
function checkRateLimit() {
|
|
560
|
-
const now = Date.now();
|
|
561
|
-
if (now < tracker.lockedUntil) {
|
|
562
|
-
const remaining = Math.ceil((tracker.lockedUntil - now) / 6e4);
|
|
563
|
-
throw new Error(t("cli_rateLimited", { minutes: remaining }));
|
|
564
|
-
}
|
|
565
|
-
if (now - tracker.lastAttempt < ACTIVATION_COOLDOWN_MS) {
|
|
566
|
-
throw new Error(t("cli_cooldown"));
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
function recordAttempt(success) {
|
|
570
|
-
const now = Date.now();
|
|
571
|
-
tracker.lastAttempt = now;
|
|
572
|
-
if (success) {
|
|
573
|
-
tracker.attempts = 0;
|
|
574
|
-
return;
|
|
575
|
-
}
|
|
576
|
-
tracker.attempts++;
|
|
577
|
-
if (tracker.attempts >= MAX_ATTEMPTS) {
|
|
578
|
-
tracker.lockedUntil = now + LOCKOUT_MS;
|
|
579
|
-
tracker.attempts = 0;
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
var ENCRYPTION_SECRET = "brew-tui-license-aes256gcm-v1";
|
|
583
|
-
var SCRYPT_SALT = "brew-tui-salt-v1";
|
|
584
|
-
var _derivedKey = null;
|
|
585
|
-
function deriveEncryptionKey() {
|
|
586
|
-
if (!_derivedKey) _derivedKey = scryptSync(ENCRYPTION_SECRET, SCRYPT_SALT, 32);
|
|
587
|
-
return _derivedKey;
|
|
588
|
-
}
|
|
589
|
-
function encryptLicenseData(data) {
|
|
590
|
-
const key = deriveEncryptionKey();
|
|
591
|
-
const iv = randomBytes(12);
|
|
592
|
-
const cipher = createCipheriv("aes-256-gcm", key, iv);
|
|
593
|
-
const plaintext = JSON.stringify(data);
|
|
594
|
-
const ciphertext = Buffer.concat([cipher.update(plaintext, "utf-8"), cipher.final()]);
|
|
595
|
-
const tag = cipher.getAuthTag();
|
|
596
|
-
return {
|
|
597
|
-
encrypted: ciphertext.toString("base64"),
|
|
598
|
-
iv: iv.toString("base64"),
|
|
599
|
-
tag: tag.toString("base64")
|
|
600
|
-
};
|
|
601
|
-
}
|
|
602
|
-
function decryptLicenseData(encrypted, iv, tag) {
|
|
603
|
-
const key = deriveEncryptionKey();
|
|
604
|
-
const decipher = createDecipheriv(
|
|
605
|
-
"aes-256-gcm",
|
|
606
|
-
key,
|
|
607
|
-
Buffer.from(iv, "base64")
|
|
608
|
-
);
|
|
609
|
-
decipher.setAuthTag(Buffer.from(tag, "base64"));
|
|
610
|
-
const plaintext = Buffer.concat([
|
|
611
|
-
decipher.update(Buffer.from(encrypted, "base64")),
|
|
612
|
-
decipher.final()
|
|
613
|
-
]);
|
|
614
|
-
return JSON.parse(plaintext.toString("utf-8"));
|
|
615
|
-
}
|
|
616
|
-
function isLicenseFile(obj) {
|
|
617
|
-
return typeof obj === "object" && obj !== null && obj.version === 1;
|
|
618
|
-
}
|
|
619
|
-
function isEncryptedLicenseFile(obj) {
|
|
620
|
-
if (!isLicenseFile(obj)) return false;
|
|
621
|
-
const record = obj;
|
|
622
|
-
return typeof record.encrypted === "string" && typeof record.iv === "string" && typeof record.tag === "string";
|
|
623
|
-
}
|
|
624
|
-
async function getMachineId2() {
|
|
625
|
-
try {
|
|
626
|
-
const { readFile: readMachineId } = await import("fs/promises");
|
|
627
|
-
const { join: join7 } = await import("path");
|
|
628
|
-
const { homedir: homedir4 } = await import("os");
|
|
629
|
-
const machineIdPath = join7(homedir4(), ".brew-tui", "machine-id");
|
|
630
|
-
return (await readMachineId(machineIdPath, "utf-8")).trim() || null;
|
|
631
|
-
} catch {
|
|
632
|
-
return null;
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
|
-
async function loadLicense() {
|
|
636
|
-
try {
|
|
637
|
-
const raw = await readFile2(LICENSE_PATH, "utf-8");
|
|
638
|
-
const parsed = JSON.parse(raw);
|
|
639
|
-
if (!isLicenseFile(parsed)) {
|
|
640
|
-
throw new Error("Invalid license data format");
|
|
641
|
-
}
|
|
642
|
-
const file = parsed;
|
|
643
|
-
if (file.version !== 1) {
|
|
644
|
-
throw new Error("Unsupported data version");
|
|
645
|
-
}
|
|
646
|
-
if (isEncryptedLicenseFile(file)) {
|
|
647
|
-
const data = decryptLicenseData(file.encrypted, file.iv, file.tag);
|
|
648
|
-
const fileRecord = file;
|
|
649
|
-
if (fileRecord.machineId) {
|
|
650
|
-
const currentMachineId = await getMachineId2();
|
|
651
|
-
if (currentMachineId && fileRecord.machineId !== currentMachineId) {
|
|
652
|
-
throw new Error("License was activated on a different machine");
|
|
653
|
-
}
|
|
654
|
-
}
|
|
655
|
-
return data;
|
|
656
|
-
}
|
|
657
|
-
if (file.license) {
|
|
658
|
-
const data = file.license;
|
|
659
|
-
await saveLicense(data);
|
|
660
|
-
return data;
|
|
661
|
-
}
|
|
662
|
-
return null;
|
|
663
|
-
} catch {
|
|
664
|
-
return null;
|
|
665
|
-
}
|
|
666
|
-
}
|
|
667
|
-
async function saveLicense(data) {
|
|
668
|
-
await ensureDataDirs();
|
|
669
|
-
const { encrypted, iv, tag } = encryptLicenseData(data);
|
|
670
|
-
const machineId = await getMachineId2();
|
|
671
|
-
const file = { version: 1, encrypted, iv, tag };
|
|
672
|
-
if (machineId) file.machineId = machineId;
|
|
673
|
-
const tmpPath = LICENSE_PATH + ".tmp";
|
|
674
|
-
await writeFile2(tmpPath, JSON.stringify(file, null, 2), { encoding: "utf-8", mode: 384 });
|
|
675
|
-
await rename(tmpPath, LICENSE_PATH);
|
|
676
|
-
}
|
|
677
|
-
async function clearLicense() {
|
|
678
|
-
try {
|
|
679
|
-
await rm(LICENSE_PATH);
|
|
680
|
-
} catch {
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
function isExpired(license) {
|
|
684
|
-
if (!license.expiresAt) return false;
|
|
685
|
-
return new Date(license.expiresAt).getTime() < Date.now();
|
|
686
|
-
}
|
|
687
|
-
function needsRevalidation(license) {
|
|
688
|
-
const lastValidated = new Date(license.lastValidatedAt).getTime();
|
|
689
|
-
if (isNaN(lastValidated)) return true;
|
|
690
|
-
return Date.now() - lastValidated > REVALIDATION_INTERVAL_MS;
|
|
691
|
-
}
|
|
692
|
-
function isWithinGracePeriod(license) {
|
|
693
|
-
const lastValidated = new Date(license.lastValidatedAt).getTime();
|
|
694
|
-
if (isNaN(lastValidated)) return false;
|
|
695
|
-
return Date.now() - lastValidated < GRACE_PERIOD_MS;
|
|
696
|
-
}
|
|
697
|
-
function getDegradationLevel(license) {
|
|
698
|
-
const lastValidated = new Date(license.lastValidatedAt).getTime();
|
|
699
|
-
if (isNaN(lastValidated)) return "expired";
|
|
700
|
-
const elapsed = Date.now() - lastValidated;
|
|
701
|
-
if (elapsed < 0) return "none";
|
|
702
|
-
const days = elapsed / (24 * 60 * 60 * 1e3);
|
|
703
|
-
if (days <= 7) return "none";
|
|
704
|
-
if (days <= 14) return "warning";
|
|
705
|
-
if (days <= 30) return "limited";
|
|
706
|
-
return "expired";
|
|
707
|
-
}
|
|
708
|
-
function validateLicenseKey(key) {
|
|
709
|
-
if (key.length < 10 || key.length > 100) {
|
|
710
|
-
throw new Error("Invalid license key format");
|
|
711
|
-
}
|
|
712
|
-
if (!/^[\w-]+$/.test(key)) {
|
|
713
|
-
throw new Error("Invalid license key format");
|
|
714
|
-
}
|
|
715
|
-
}
|
|
716
|
-
function detectPlan(key) {
|
|
717
|
-
const upper = key.toUpperCase();
|
|
718
|
-
return upper.startsWith("BTUI-T-") || upper.startsWith("BTUI-T_") ? "team" : "pro";
|
|
719
|
-
}
|
|
720
|
-
async function activate(key) {
|
|
721
|
-
validateLicenseKey(key);
|
|
722
|
-
checkRateLimit();
|
|
723
|
-
let success = false;
|
|
724
|
-
try {
|
|
725
|
-
const res = await activateLicense(key);
|
|
726
|
-
if (!res.activated) {
|
|
727
|
-
throw new Error(res.error ?? "Activation failed");
|
|
728
|
-
}
|
|
729
|
-
const license = {
|
|
730
|
-
key,
|
|
731
|
-
instanceId: res.instance.id,
|
|
732
|
-
status: "active",
|
|
733
|
-
customerEmail: res.meta.customer_email,
|
|
734
|
-
customerName: res.meta.customer_name,
|
|
735
|
-
plan: detectPlan(key),
|
|
736
|
-
activatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
737
|
-
expiresAt: res.license_key.expires_at,
|
|
738
|
-
lastValidatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
739
|
-
};
|
|
740
|
-
await saveLicense(license);
|
|
741
|
-
success = true;
|
|
742
|
-
return license;
|
|
743
|
-
} finally {
|
|
744
|
-
recordAttempt(success);
|
|
745
|
-
}
|
|
746
|
-
}
|
|
747
|
-
function isNetworkError(err) {
|
|
748
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
749
|
-
return /fetch failed|ECONNREFUSED|ENOTFOUND|ETIMEDOUT|network|timeout|abort/i.test(msg);
|
|
750
|
-
}
|
|
751
|
-
async function revalidate(license) {
|
|
752
|
-
try {
|
|
753
|
-
const res = await validateLicense(license.key, license.instanceId);
|
|
754
|
-
if (res.valid) {
|
|
755
|
-
const updated = {
|
|
756
|
-
...license,
|
|
757
|
-
lastValidatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
758
|
-
status: "active",
|
|
759
|
-
expiresAt: res.license_key.expires_at
|
|
760
|
-
};
|
|
761
|
-
await saveLicense(updated);
|
|
762
|
-
return "valid";
|
|
763
|
-
}
|
|
764
|
-
await saveLicense({ ...license, status: "expired" });
|
|
765
|
-
return "expired";
|
|
766
|
-
} catch (err) {
|
|
767
|
-
if (isNetworkError(err)) {
|
|
768
|
-
return isWithinGracePeriod(license) ? "grace" : "expired";
|
|
769
|
-
}
|
|
770
|
-
await saveLicense({ ...license, status: "expired" });
|
|
771
|
-
return "expired";
|
|
772
|
-
}
|
|
773
|
-
}
|
|
774
|
-
async function deactivate(license) {
|
|
775
|
-
let remoteSuccess = false;
|
|
776
|
-
for (let attempt = 0; attempt < 3; attempt++) {
|
|
777
|
-
try {
|
|
778
|
-
await deactivateLicense(license.key, license.instanceId);
|
|
779
|
-
remoteSuccess = true;
|
|
780
|
-
break;
|
|
781
|
-
} catch {
|
|
782
|
-
if (attempt < 2) await new Promise((r) => setTimeout(r, 1e3));
|
|
783
|
-
}
|
|
784
|
-
}
|
|
785
|
-
await clearLicense();
|
|
786
|
-
return { remoteSuccess };
|
|
787
|
-
}
|
|
788
|
-
|
|
789
453
|
// src/lib/license/anti-tamper.ts
|
|
790
454
|
var _originalIsPro = null;
|
|
791
455
|
var _originalGetState = null;
|
|
@@ -885,6 +549,15 @@ var useLicenseStore = create2((set, get) => ({
|
|
|
885
549
|
}
|
|
886
550
|
set({ status: "free", license: null, degradation: "none", error: null });
|
|
887
551
|
},
|
|
552
|
+
revalidate: async () => {
|
|
553
|
+
const { license } = get();
|
|
554
|
+
if (!license) return;
|
|
555
|
+
try {
|
|
556
|
+
await doRevalidation(license, set);
|
|
557
|
+
} catch (err) {
|
|
558
|
+
set({ error: err instanceof Error ? err.message : String(err) });
|
|
559
|
+
}
|
|
560
|
+
},
|
|
888
561
|
// Team is a superset of Pro — team users have full Pro access plus team features.
|
|
889
562
|
// Pro users do NOT get Team features (Compliance) without paying for the Team tier.
|
|
890
563
|
isPro: () => {
|
|
@@ -973,7 +646,9 @@ var FEATURE_KEYS = {
|
|
|
973
646
|
history: { title: "upgrade_history", desc: "upgrade_historyDesc" },
|
|
974
647
|
"security-audit": { title: "upgrade_security", desc: "upgrade_securityDesc" },
|
|
975
648
|
sync: { title: "upgrade_sync", desc: "upgrade_syncDesc" },
|
|
976
|
-
compliance: { title: "upgrade_compliance", desc: "upgrade_complianceDesc" }
|
|
649
|
+
compliance: { title: "upgrade_compliance", desc: "upgrade_complianceDesc" },
|
|
650
|
+
rollback: { title: "upgrade_rollback", desc: "upgrade_rollbackDesc" },
|
|
651
|
+
brewfile: { title: "upgrade_brewfile", desc: "upgrade_brewfileDesc" }
|
|
977
652
|
};
|
|
978
653
|
function UpgradePrompt({ viewId }) {
|
|
979
654
|
const keys = FEATURE_KEYS[viewId];
|
|
@@ -984,13 +659,13 @@ function UpgradePrompt({ viewId }) {
|
|
|
984
659
|
const pricingKey = team ? "upgrade_teamPricing" : "upgrade_pricing";
|
|
985
660
|
const buyUrlKey = team ? "upgrade_buyUrlTeam" : "upgrade_buyUrl";
|
|
986
661
|
const labelKey = team ? "upgrade_teamLabel" : "upgrade_proLabel";
|
|
987
|
-
return /* @__PURE__ */ jsx5(Box4, { flexDirection: "column", alignItems: "center", paddingY:
|
|
662
|
+
return /* @__PURE__ */ jsx5(Box4, { flexDirection: "column", alignItems: "center", paddingY: SPACING.sm, children: /* @__PURE__ */ jsxs4(
|
|
988
663
|
Box4,
|
|
989
664
|
{
|
|
990
665
|
borderStyle: "double",
|
|
991
666
|
borderColor: COLORS.brand,
|
|
992
|
-
paddingX:
|
|
993
|
-
paddingY:
|
|
667
|
+
paddingX: SPACING.md,
|
|
668
|
+
paddingY: SPACING.sm,
|
|
994
669
|
flexDirection: "column",
|
|
995
670
|
alignItems: "center",
|
|
996
671
|
width: "80%",
|
|
@@ -1027,7 +702,7 @@ function UpgradePrompt({ viewId }) {
|
|
|
1027
702
|
|
|
1028
703
|
// src/views/dashboard.tsx
|
|
1029
704
|
import { useEffect, useMemo as useMemo2 } from "react";
|
|
1030
|
-
import { Box as Box8, Text as Text10, useStdout as useStdout3 } from "ink";
|
|
705
|
+
import { Box as Box8, Text as Text10, useInput as useInput2, useStdout as useStdout3 } from "ink";
|
|
1031
706
|
|
|
1032
707
|
// src/stores/brew-store.ts
|
|
1033
708
|
import { create as create4 } from "zustand";
|
|
@@ -1231,11 +906,26 @@ function validatePackageName(name) {
|
|
|
1231
906
|
async function brewUpdate() {
|
|
1232
907
|
return new Promise((resolve, reject) => {
|
|
1233
908
|
const proc = spawn("brew", ["update"], { stdio: "ignore" });
|
|
909
|
+
let settled = false;
|
|
910
|
+
const timeout = setTimeout(() => {
|
|
911
|
+
if (settled) return;
|
|
912
|
+
settled = true;
|
|
913
|
+
proc.kill("SIGTERM");
|
|
914
|
+
reject(new Error("brew update timed out after 120s"));
|
|
915
|
+
}, 12e4);
|
|
1234
916
|
proc.on("close", (code) => {
|
|
917
|
+
if (settled) return;
|
|
918
|
+
settled = true;
|
|
919
|
+
clearTimeout(timeout);
|
|
1235
920
|
if (code === 0) resolve();
|
|
1236
921
|
else reject(new Error(`brew update exited with code ${code}`));
|
|
1237
922
|
});
|
|
1238
|
-
proc.on("error",
|
|
923
|
+
proc.on("error", (err) => {
|
|
924
|
+
if (settled) return;
|
|
925
|
+
settled = true;
|
|
926
|
+
clearTimeout(timeout);
|
|
927
|
+
reject(err);
|
|
928
|
+
});
|
|
1239
929
|
});
|
|
1240
930
|
}
|
|
1241
931
|
async function getInstalled() {
|
|
@@ -1568,7 +1258,7 @@ async function queryOneByOne(packages) {
|
|
|
1568
1258
|
try {
|
|
1569
1259
|
const partial = await queryBatch(
|
|
1570
1260
|
[pkg],
|
|
1571
|
-
[{ package: { name: pkg.name, ecosystem: "
|
|
1261
|
+
[{ package: { name: pkg.name, ecosystem: "Bitnami" }, version: pkg.version }]
|
|
1572
1262
|
);
|
|
1573
1263
|
for (const [k, v] of partial) result.set(k, v);
|
|
1574
1264
|
} catch (err) {
|
|
@@ -1583,7 +1273,7 @@ async function queryOneByOne(packages) {
|
|
|
1583
1273
|
try {
|
|
1584
1274
|
const retryResult = await queryBatch(
|
|
1585
1275
|
[pkg],
|
|
1586
|
-
[{ package: { name: pkg.name, ecosystem: "
|
|
1276
|
+
[{ package: { name: pkg.name, ecosystem: "Bitnami" }, version: pkg.version }]
|
|
1587
1277
|
);
|
|
1588
1278
|
for (const [k, v] of retryResult) result.set(k, v);
|
|
1589
1279
|
} catch {
|
|
@@ -1609,7 +1299,7 @@ async function queryVulnerabilities(packages) {
|
|
|
1609
1299
|
for (let i = 0; i < validPackages.length; i += BATCH_SIZE) {
|
|
1610
1300
|
const batch = validPackages.slice(i, i + BATCH_SIZE);
|
|
1611
1301
|
const queries = batch.map((p) => ({
|
|
1612
|
-
package: { name: p.name, ecosystem: "
|
|
1302
|
+
package: { name: p.name, ecosystem: "Bitnami" },
|
|
1613
1303
|
version: p.version
|
|
1614
1304
|
}));
|
|
1615
1305
|
const partial = await queryBatch(batch, queries);
|
|
@@ -1885,7 +1575,9 @@ var useSyncStore = create7((set, get) => ({
|
|
|
1885
1575
|
if (!config) throw new Error("Sync not initialized");
|
|
1886
1576
|
const envelope = await readSyncEnvelope();
|
|
1887
1577
|
if (!envelope) throw new Error("No sync data found");
|
|
1888
|
-
const
|
|
1578
|
+
const license = await loadLicense();
|
|
1579
|
+
if (!license) throw new Error("License missing");
|
|
1580
|
+
const payload = decryptPayload(envelope.encrypted, envelope.iv, envelope.tag, license.key);
|
|
1889
1581
|
await applyConflictResolutions(payload, resolutions, config.machineId);
|
|
1890
1582
|
const updatedConfig = await loadSyncConfig();
|
|
1891
1583
|
set({ config: updatedConfig, conflicts: [], loading: false });
|
|
@@ -1944,8 +1636,8 @@ function StatCard({ label, value, color = "white" }) {
|
|
|
1944
1636
|
{
|
|
1945
1637
|
borderStyle: "round",
|
|
1946
1638
|
borderColor: color,
|
|
1947
|
-
paddingX:
|
|
1948
|
-
paddingY:
|
|
1639
|
+
paddingX: SPACING.sm,
|
|
1640
|
+
paddingY: SPACING.none,
|
|
1949
1641
|
flexDirection: "column",
|
|
1950
1642
|
alignItems: "center",
|
|
1951
1643
|
minWidth: minW,
|
|
@@ -1963,11 +1655,11 @@ import { Spinner } from "@inkjs/ui";
|
|
|
1963
1655
|
import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1964
1656
|
function Loading({ message }) {
|
|
1965
1657
|
useLocaleStore((s) => s.locale);
|
|
1966
|
-
return /* @__PURE__ */ jsx7(Box6, { paddingY:
|
|
1658
|
+
return /* @__PURE__ */ jsx7(Box6, { paddingY: SPACING.xs, children: /* @__PURE__ */ jsx7(Spinner, { label: message ?? t("loading_default") }) });
|
|
1967
1659
|
}
|
|
1968
1660
|
function ErrorMessage({ message }) {
|
|
1969
1661
|
useLocaleStore((s) => s.locale);
|
|
1970
|
-
return /* @__PURE__ */ jsxs6(Box6, { paddingY:
|
|
1662
|
+
return /* @__PURE__ */ jsxs6(Box6, { paddingY: SPACING.xs, children: [
|
|
1971
1663
|
/* @__PURE__ */ jsxs6(Text6, { color: COLORS.error, bold: true, children: [
|
|
1972
1664
|
"\u2718",
|
|
1973
1665
|
" ",
|
|
@@ -2000,7 +1692,7 @@ function StatusBadge({ label, variant }) {
|
|
|
2000
1692
|
import { Box as Box7, Text as Text8 } from "ink";
|
|
2001
1693
|
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
2002
1694
|
function SectionHeader({ emoji, title, color = COLORS.gold, gradient, count }) {
|
|
2003
|
-
return /* @__PURE__ */ jsxs8(Box7, { gap:
|
|
1695
|
+
return /* @__PURE__ */ jsxs8(Box7, { gap: SPACING.xs, children: [
|
|
2004
1696
|
/* @__PURE__ */ jsxs8(Text8, { children: [
|
|
2005
1697
|
emoji,
|
|
2006
1698
|
" "
|
|
@@ -2075,27 +1767,27 @@ function ProStatusPanel() {
|
|
|
2075
1767
|
const lastSync = syncConfig?.lastSync ?? null;
|
|
2076
1768
|
const syncAgo = lastSync ? formatRelativeTime(new Date(lastSync).getTime() / 1e3) : null;
|
|
2077
1769
|
const violationCount = complianceReport ? complianceReport.violations.length : null;
|
|
2078
|
-
return /* @__PURE__ */ jsxs10(Box8, { flexDirection: "column", borderStyle: "round", borderColor: COLORS.purple, paddingX:
|
|
1770
|
+
return /* @__PURE__ */ jsxs10(Box8, { flexDirection: "column", borderStyle: "round", borderColor: COLORS.purple, paddingX: SPACING.sm, paddingY: SPACING.none, marginTop: SPACING.xs, children: [
|
|
2079
1771
|
/* @__PURE__ */ jsx10(Text10, { bold: true, color: COLORS.purple, children: t("dashboard_pro_status") }),
|
|
2080
|
-
/* @__PURE__ */ jsxs10(Box8, { gap:
|
|
1772
|
+
/* @__PURE__ */ jsxs10(Box8, { gap: SPACING.xs, children: [
|
|
2081
1773
|
/* @__PURE__ */ jsx10(Text10, { color: COLORS.muted, children: t("dashboard_security") }),
|
|
2082
1774
|
cveCount === null ? /* @__PURE__ */ jsx10(Text10, { color: COLORS.muted, children: "\u2014" }) : cveCount === 0 ? /* @__PURE__ */ jsx10(Text10, { color: COLORS.success, children: t("dashboard_no_cves") }) : /* @__PURE__ */ jsxs10(Text10, { color: COLORS.error, children: [
|
|
2083
1775
|
t("dashboard_cves", { count: String(cveCount) }),
|
|
2084
1776
|
criticalCount && criticalCount > 0 ? ` (${criticalCount} critical)` : ""
|
|
2085
1777
|
] })
|
|
2086
1778
|
] }),
|
|
2087
|
-
/* @__PURE__ */ jsxs10(Box8, { gap:
|
|
1779
|
+
/* @__PURE__ */ jsxs10(Box8, { gap: SPACING.xs, children: [
|
|
2088
1780
|
/* @__PURE__ */ jsx10(Text10, { color: COLORS.muted, children: t("dashboard_brewfile") }),
|
|
2089
1781
|
driftScore === null ? /* @__PURE__ */ jsx10(Text10, { color: COLORS.muted, children: "\u2014" }) : /* @__PURE__ */ jsxs10(Text10, { color: driftScore >= 80 ? COLORS.success : COLORS.warning, children: [
|
|
2090
1782
|
driftScore,
|
|
2091
1783
|
"%"
|
|
2092
1784
|
] })
|
|
2093
1785
|
] }),
|
|
2094
|
-
/* @__PURE__ */ jsxs10(Box8, { gap:
|
|
1786
|
+
/* @__PURE__ */ jsxs10(Box8, { gap: SPACING.xs, children: [
|
|
2095
1787
|
/* @__PURE__ */ jsx10(Text10, { color: COLORS.muted, children: t("dashboard_sync") }),
|
|
2096
1788
|
syncAgo === null ? /* @__PURE__ */ jsx10(Text10, { color: COLORS.muted, children: t("dashboard_sync_never") }) : /* @__PURE__ */ jsx10(Text10, { color: COLORS.info, children: t("dashboard_sync_ago", { time: syncAgo }) })
|
|
2097
1789
|
] }),
|
|
2098
|
-
/* @__PURE__ */ jsxs10(Box8, { gap:
|
|
1790
|
+
/* @__PURE__ */ jsxs10(Box8, { gap: SPACING.xs, children: [
|
|
2099
1791
|
/* @__PURE__ */ jsx10(Text10, { color: COLORS.muted, children: t("dashboard_compliance") }),
|
|
2100
1792
|
violationCount === null ? /* @__PURE__ */ jsx10(Text10, { color: COLORS.muted, children: "\u2014" }) : violationCount === 0 ? /* @__PURE__ */ jsx10(Text10, { color: COLORS.success, children: t("dashboard_compliance_ok") }) : /* @__PURE__ */ jsx10(Text10, { color: COLORS.warning, children: t("dashboard_compliance_violations", { count: String(violationCount) }) })
|
|
2101
1793
|
] })
|
|
@@ -2109,6 +1801,11 @@ function DashboardView() {
|
|
|
2109
1801
|
useEffect(() => {
|
|
2110
1802
|
fetchAll();
|
|
2111
1803
|
}, []);
|
|
1804
|
+
useInput2((input) => {
|
|
1805
|
+
if (errors.installed && (input === "r" || input === "R")) {
|
|
1806
|
+
void fetchAll();
|
|
1807
|
+
}
|
|
1808
|
+
});
|
|
2112
1809
|
const errorServiceList = useMemo2(
|
|
2113
1810
|
() => services.filter((s) => s.status === "error"),
|
|
2114
1811
|
[services]
|
|
@@ -2127,11 +1824,19 @@ function DashboardView() {
|
|
|
2127
1824
|
const servicesValue = loading.services ? "..." : errors.services ? t("dashboard_statError") : `${runningServices}/${services.length}`;
|
|
2128
1825
|
const lastUpdated = lastFetchedAt.installed ? formatRelativeTime(lastFetchedAt.installed / 1e3) : null;
|
|
2129
1826
|
if (loading.installed) return /* @__PURE__ */ jsx10(Loading, { message: t("loading_fetchingBrew") });
|
|
2130
|
-
if (errors.installed)
|
|
1827
|
+
if (errors.installed) {
|
|
1828
|
+
return /* @__PURE__ */ jsxs10(Box8, { flexDirection: "column", children: [
|
|
1829
|
+
/* @__PURE__ */ jsx10(ErrorMessage, { message: errors.installed }),
|
|
1830
|
+
/* @__PURE__ */ jsx10(Box8, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsxs10(Text10, { color: COLORS.textSecondary, children: [
|
|
1831
|
+
"r:",
|
|
1832
|
+
t("hint_refresh")
|
|
1833
|
+
] }) })
|
|
1834
|
+
] });
|
|
1835
|
+
}
|
|
2131
1836
|
const isNarrow = columns < 60;
|
|
2132
|
-
return /* @__PURE__ */ jsxs10(Box8, { flexDirection: "column", gap:
|
|
1837
|
+
return /* @__PURE__ */ jsxs10(Box8, { flexDirection: "column", gap: SPACING.sm, children: [
|
|
2133
1838
|
/* @__PURE__ */ jsx10(SectionHeader, { emoji: "\u{1F4CA}", title: t("dashboard_overview"), gradient: GRADIENTS.gold }),
|
|
2134
|
-
/* @__PURE__ */ jsxs10(Box8, { gap:
|
|
1839
|
+
/* @__PURE__ */ jsxs10(Box8, { gap: SPACING.xs, flexWrap: "wrap", flexDirection: isNarrow ? "column" : "row", children: [
|
|
2135
1840
|
/* @__PURE__ */ jsx10(StatCard, { label: t("dashboard_formulae"), value: formulae.length, color: COLORS.info }),
|
|
2136
1841
|
/* @__PURE__ */ jsx10(StatCard, { label: t("dashboard_casks"), value: casks.length, color: COLORS.purple }),
|
|
2137
1842
|
/* @__PURE__ */ jsx10(
|
|
@@ -2152,7 +1857,7 @@ function DashboardView() {
|
|
|
2152
1857
|
)
|
|
2153
1858
|
] }),
|
|
2154
1859
|
lastUpdated && /* @__PURE__ */ jsx10(Text10, { color: COLORS.muted, children: t("dashboard_lastUpdated", { time: lastUpdated }) }),
|
|
2155
|
-
partialErrors.length > 0 && /* @__PURE__ */ jsxs10(Box8, { flexDirection: "column", borderStyle: "round", borderColor: COLORS.warning, paddingX:
|
|
1860
|
+
partialErrors.length > 0 && /* @__PURE__ */ jsxs10(Box8, { flexDirection: "column", borderStyle: "round", borderColor: COLORS.warning, paddingX: SPACING.sm, paddingY: SPACING.none, children: [
|
|
2156
1861
|
/* @__PURE__ */ jsx10(Text10, { color: COLORS.warning, bold: true, children: t("dashboard_partialData") }),
|
|
2157
1862
|
partialErrors.map((item) => /* @__PURE__ */ jsxs10(Text10, { color: COLORS.muted, children: [
|
|
2158
1863
|
item.label,
|
|
@@ -2162,7 +1867,7 @@ function DashboardView() {
|
|
|
2162
1867
|
] }),
|
|
2163
1868
|
config && !errors.config && /* @__PURE__ */ jsxs10(Box8, { flexDirection: "column", children: [
|
|
2164
1869
|
/* @__PURE__ */ jsx10(SectionHeader, { emoji: "\u2139\uFE0F", title: t("dashboard_systemInfo"), gradient: [COLORS.text, COLORS.muted] }),
|
|
2165
|
-
/* @__PURE__ */ jsxs10(Box8, { borderStyle: "round", borderColor: COLORS.blue, paddingX:
|
|
1870
|
+
/* @__PURE__ */ jsxs10(Box8, { borderStyle: "round", borderColor: COLORS.blue, paddingX: SPACING.sm, paddingY: SPACING.none, flexDirection: "column", marginTop: SPACING.xs, children: [
|
|
2166
1871
|
/* @__PURE__ */ jsxs10(Text10, { children: [
|
|
2167
1872
|
/* @__PURE__ */ jsx10(Text10, { color: COLORS.muted, children: t("dashboard_homebrew") }),
|
|
2168
1873
|
" ",
|
|
@@ -2180,19 +1885,19 @@ function DashboardView() {
|
|
|
2180
1885
|
] })
|
|
2181
1886
|
] })
|
|
2182
1887
|
] }),
|
|
2183
|
-
!errors.outdated && outdated.formulae.length > 0 && /* @__PURE__ */ jsxs10(Box8, { flexDirection: "column", marginTop:
|
|
1888
|
+
!errors.outdated && outdated.formulae.length > 0 && /* @__PURE__ */ jsxs10(Box8, { flexDirection: "column", marginTop: SPACING.xs, children: [
|
|
2184
1889
|
/* @__PURE__ */ jsx10(SectionHeader, { emoji: "\u{1F4E6}", title: t("dashboard_outdatedPackages"), gradient: GRADIENTS.fire }),
|
|
2185
|
-
/* @__PURE__ */ jsxs10(Box8, { paddingLeft:
|
|
2186
|
-
outdated.formulae.slice(0, 10).map((pkg) => /* @__PURE__ */ jsxs10(Box8, { gap:
|
|
1890
|
+
/* @__PURE__ */ jsxs10(Box8, { paddingLeft: SPACING.sm, flexDirection: "column", children: [
|
|
1891
|
+
outdated.formulae.slice(0, 10).map((pkg) => /* @__PURE__ */ jsxs10(Box8, { gap: SPACING.xs, children: [
|
|
2187
1892
|
/* @__PURE__ */ jsx10(Text10, { color: COLORS.text, children: pkg.name }),
|
|
2188
1893
|
/* @__PURE__ */ jsx10(VersionArrow, { current: pkg.installed_versions[0] ?? "", latest: pkg.current_version })
|
|
2189
1894
|
] }, pkg.name)),
|
|
2190
1895
|
outdated.formulae.length > 10 && /* @__PURE__ */ jsx10(Text10, { color: COLORS.textSecondary, italic: true, children: t("common_andMore", { count: outdated.formulae.length - 10 }) })
|
|
2191
1896
|
] })
|
|
2192
1897
|
] }),
|
|
2193
|
-
!errors.services && errorServices > 0 && /* @__PURE__ */ jsxs10(Box8, { flexDirection: "column", marginTop:
|
|
1898
|
+
!errors.services && errorServices > 0 && /* @__PURE__ */ jsxs10(Box8, { flexDirection: "column", marginTop: SPACING.xs, children: [
|
|
2194
1899
|
/* @__PURE__ */ jsx10(SectionHeader, { emoji: "\u26A0\uFE0F", title: t("dashboard_serviceErrors"), color: COLORS.error }),
|
|
2195
|
-
/* @__PURE__ */ jsx10(Box8, { paddingLeft:
|
|
1900
|
+
/* @__PURE__ */ jsx10(Box8, { paddingLeft: SPACING.sm, flexDirection: "column", children: errorServiceList.map((s) => /* @__PURE__ */ jsxs10(Box8, { gap: SPACING.xs, children: [
|
|
2196
1901
|
/* @__PURE__ */ jsx10(StatusBadge, { label: t("badge_error"), variant: "error" }),
|
|
2197
1902
|
/* @__PURE__ */ jsx10(Text10, { children: s.name }),
|
|
2198
1903
|
s.exit_code != null && /* @__PURE__ */ jsx10(Text10, { color: COLORS.muted, children: t("common_exit", { code: s.exit_code }) })
|
|
@@ -2204,7 +1909,7 @@ function DashboardView() {
|
|
|
2204
1909
|
|
|
2205
1910
|
// src/views/installed.tsx
|
|
2206
1911
|
import { useState as useState3, useMemo as useMemo3, useEffect as useEffect5 } from "react";
|
|
2207
|
-
import { Box as Box14, Text as Text16, useInput as
|
|
1912
|
+
import { Box as Box14, Text as Text16, useInput as useInput4, useStdout as useStdout4 } from "ink";
|
|
2208
1913
|
|
|
2209
1914
|
// src/hooks/use-debounce.ts
|
|
2210
1915
|
import { useState, useEffect as useEffect2 } from "react";
|
|
@@ -2225,7 +1930,7 @@ async function logToHistory(args, success, error) {
|
|
|
2225
1930
|
if (!detected) return;
|
|
2226
1931
|
try {
|
|
2227
1932
|
const isPro = useLicenseStore.getState().isPro();
|
|
2228
|
-
const { appendEntry: appendEntry2 } = await import("./history-logger-
|
|
1933
|
+
const { appendEntry: appendEntry2 } = await import("./history-logger-ZGYRAFON.js");
|
|
2229
1934
|
await appendEntry2(isPro, detected.action, detected.packageName, success, error);
|
|
2230
1935
|
} catch {
|
|
2231
1936
|
}
|
|
@@ -2277,7 +1982,7 @@ function useBrewStream() {
|
|
|
2277
1982
|
}
|
|
2278
1983
|
const MUTATING_COMMANDS = /* @__PURE__ */ new Set(["install", "uninstall", "upgrade", "pin", "unpin", "tap", "untap"]);
|
|
2279
1984
|
if (!cancelRef.current && MUTATING_COMMANDS.has(args[0] ?? "")) {
|
|
2280
|
-
void import("./snapshot-
|
|
1985
|
+
void import("./snapshot-YWIOFQ5H.js").then(({ captureSnapshot: captureSnapshot2, saveSnapshot: saveSnapshot2 }) => {
|
|
2281
1986
|
captureSnapshot2().then((s) => saveSnapshot2(s)).catch((err) => logger.warn("snapshot: capture/save failed", { error: String(err) }));
|
|
2282
1987
|
});
|
|
2283
1988
|
}
|
|
@@ -2318,7 +2023,7 @@ function SearchInput({ defaultValue, onChange, placeholder, isActive = true }) {
|
|
|
2318
2023
|
|
|
2319
2024
|
// src/components/common/confirm-dialog.tsx
|
|
2320
2025
|
import { useEffect as useEffect4 } from "react";
|
|
2321
|
-
import { Box as Box10, Text as Text12, useInput as
|
|
2026
|
+
import { Box as Box10, Text as Text12, useInput as useInput3 } from "ink";
|
|
2322
2027
|
import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
2323
2028
|
function ConfirmDialog({ message, onConfirm, onCancel }) {
|
|
2324
2029
|
const locale = useLocaleStore((s) => s.locale);
|
|
@@ -2329,15 +2034,15 @@ function ConfirmDialog({ message, onConfirm, onCancel }) {
|
|
|
2329
2034
|
closeModal();
|
|
2330
2035
|
};
|
|
2331
2036
|
}, []);
|
|
2332
|
-
|
|
2037
|
+
useInput3((input, key) => {
|
|
2333
2038
|
if (input === "y" || input === "Y") onConfirm();
|
|
2334
2039
|
else if (locale === "es" && (input === "s" || input === "S")) onConfirm();
|
|
2335
2040
|
else if (input === "n" || input === "N") onCancel();
|
|
2336
2041
|
else if (key.escape) onCancel();
|
|
2337
2042
|
});
|
|
2338
|
-
return /* @__PURE__ */ jsxs12(Box10, { borderStyle: "double", borderColor: COLORS.purple, paddingX:
|
|
2043
|
+
return /* @__PURE__ */ jsxs12(Box10, { borderStyle: "double", borderColor: COLORS.purple, paddingX: SPACING.sm, paddingY: SPACING.xs, flexDirection: "column", children: [
|
|
2339
2044
|
/* @__PURE__ */ jsx12(Text12, { bold: true, color: COLORS.text, children: message }),
|
|
2340
|
-
/* @__PURE__ */ jsxs12(Box10, { marginTop:
|
|
2045
|
+
/* @__PURE__ */ jsxs12(Box10, { marginTop: SPACING.xs, children: [
|
|
2341
2046
|
/* @__PURE__ */ jsx12(Text12, { color: COLORS.success, children: t("confirm_yes") }),
|
|
2342
2047
|
/* @__PURE__ */ jsx12(Text12, { children: " / " }),
|
|
2343
2048
|
/* @__PURE__ */ jsx12(Text12, { color: COLORS.error, children: t("confirm_no") })
|
|
@@ -2351,8 +2056,8 @@ import { Spinner as Spinner2 } from "@inkjs/ui";
|
|
|
2351
2056
|
import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
2352
2057
|
function ProgressLog({ lines, isRunning, title, maxVisible = 15 }) {
|
|
2353
2058
|
const visible = lines.slice(-maxVisible);
|
|
2354
|
-
return /* @__PURE__ */ jsxs13(Box11, { flexDirection: "column", borderStyle: "round", borderColor: COLORS.sky, paddingX:
|
|
2355
|
-
title && /* @__PURE__ */ jsxs13(Box11, { marginBottom:
|
|
2059
|
+
return /* @__PURE__ */ jsxs13(Box11, { flexDirection: "column", borderStyle: "round", borderColor: COLORS.sky, paddingX: SPACING.xs, children: [
|
|
2060
|
+
title && /* @__PURE__ */ jsxs13(Box11, { marginBottom: SPACING.xs, children: [
|
|
2356
2061
|
isRunning && /* @__PURE__ */ jsx13(Spinner2, { label: "" }),
|
|
2357
2062
|
/* @__PURE__ */ jsxs13(Text13, { bold: true, color: COLORS.sky, children: [
|
|
2358
2063
|
" ",
|
|
@@ -2374,7 +2079,7 @@ var STATUS_COLORS = {
|
|
|
2374
2079
|
info: COLORS.info
|
|
2375
2080
|
};
|
|
2376
2081
|
function ResultBanner({ status, message }) {
|
|
2377
|
-
return /* @__PURE__ */ jsx14(Box12, { borderStyle: "round", borderColor: STATUS_COLORS[status], paddingX:
|
|
2082
|
+
return /* @__PURE__ */ jsx14(Box12, { borderStyle: "round", borderColor: STATUS_COLORS[status], paddingX: SPACING.sm, paddingY: SPACING.none, children: /* @__PURE__ */ jsx14(Text14, { color: STATUS_COLORS[status], bold: true, children: message }) });
|
|
2378
2083
|
}
|
|
2379
2084
|
|
|
2380
2085
|
// src/components/common/selectable-row.tsx
|
|
@@ -2429,7 +2134,7 @@ function InstalledView() {
|
|
|
2429
2134
|
(p) => p.name.toLowerCase().includes(lower) || p.desc.toLowerCase().includes(lower)
|
|
2430
2135
|
);
|
|
2431
2136
|
}, [formulae, casks, tab, debouncedFilter]);
|
|
2432
|
-
|
|
2137
|
+
useInput4((input, key) => {
|
|
2433
2138
|
if (confirmUninstall || stream.isRunning) return;
|
|
2434
2139
|
if (!stream.isRunning && stream.lines.length > 0) {
|
|
2435
2140
|
if (key.escape) {
|
|
@@ -2485,7 +2190,7 @@ function InstalledView() {
|
|
|
2485
2190
|
"esc:",
|
|
2486
2191
|
t("hint_cancel")
|
|
2487
2192
|
] }),
|
|
2488
|
-
!stream.isRunning && /* @__PURE__ */ jsxs15(Box14, { flexDirection: "column", marginTop:
|
|
2193
|
+
!stream.isRunning && /* @__PURE__ */ jsxs15(Box14, { flexDirection: "column", marginTop: SPACING.xs, children: [
|
|
2489
2194
|
/* @__PURE__ */ jsx16(
|
|
2490
2195
|
ResultBanner,
|
|
2491
2196
|
{
|
|
@@ -2504,13 +2209,13 @@ function InstalledView() {
|
|
|
2504
2209
|
const start = Math.max(0, cursor - Math.floor(MAX_VISIBLE_ROWS / 2));
|
|
2505
2210
|
const visible = allItems.slice(start, start + MAX_VISIBLE_ROWS);
|
|
2506
2211
|
return /* @__PURE__ */ jsxs15(Box14, { flexDirection: "column", children: [
|
|
2507
|
-
/* @__PURE__ */ jsxs15(Box14, { marginBottom:
|
|
2212
|
+
/* @__PURE__ */ jsxs15(Box14, { marginBottom: SPACING.xs, gap: SPACING.xs, children: [
|
|
2508
2213
|
/* @__PURE__ */ jsx16(
|
|
2509
2214
|
Box14,
|
|
2510
2215
|
{
|
|
2511
2216
|
borderStyle: "round",
|
|
2512
2217
|
borderColor: tab === "formulae" ? COLORS.info : COLORS.textSecondary,
|
|
2513
|
-
paddingX:
|
|
2218
|
+
paddingX: SPACING.xs,
|
|
2514
2219
|
children: /* @__PURE__ */ jsxs15(Text16, { bold: tab === "formulae", color: tab === "formulae" ? COLORS.info : COLORS.textSecondary, children: [
|
|
2515
2220
|
"\u{1F4E6}",
|
|
2516
2221
|
" ",
|
|
@@ -2523,7 +2228,7 @@ function InstalledView() {
|
|
|
2523
2228
|
{
|
|
2524
2229
|
borderStyle: "round",
|
|
2525
2230
|
borderColor: tab === "casks" ? COLORS.purple : COLORS.textSecondary,
|
|
2526
|
-
paddingX:
|
|
2231
|
+
paddingX: SPACING.xs,
|
|
2527
2232
|
children: /* @__PURE__ */ jsxs15(Text16, { bold: tab === "casks", color: tab === "casks" ? COLORS.purple : COLORS.textSecondary, children: [
|
|
2528
2233
|
"\u{1F37A}",
|
|
2529
2234
|
" ",
|
|
@@ -2546,8 +2251,8 @@ function InstalledView() {
|
|
|
2546
2251
|
onCancel: () => setConfirmUninstall(null)
|
|
2547
2252
|
}
|
|
2548
2253
|
),
|
|
2549
|
-
isSearching && /* @__PURE__ */ jsx16(Box14, { marginBottom:
|
|
2550
|
-
/* @__PURE__ */ jsxs15(Box14, { gap:
|
|
2254
|
+
isSearching && /* @__PURE__ */ jsx16(Box14, { marginBottom: SPACING.xs, borderStyle: "round", borderColor: COLORS.gold, paddingX: SPACING.xs, children: /* @__PURE__ */ jsx16(SearchInput, { defaultValue: filter, onChange: setFilter, isActive: isSearching }) }),
|
|
2255
|
+
/* @__PURE__ */ jsxs15(Box14, { gap: SPACING.xs, borderStyle: "single", borderBottom: true, borderTop: false, borderLeft: false, borderRight: false, borderColor: COLORS.border, children: [
|
|
2551
2256
|
/* @__PURE__ */ jsxs15(Text16, { color: COLORS.text, bold: true, children: [
|
|
2552
2257
|
" ",
|
|
2553
2258
|
t("installed_col_package").padEnd(nameWidth)
|
|
@@ -2556,7 +2261,7 @@ function InstalledView() {
|
|
|
2556
2261
|
/* @__PURE__ */ jsx16(Text16, { color: COLORS.text, bold: true, children: t("installed_col_status") })
|
|
2557
2262
|
] }),
|
|
2558
2263
|
/* @__PURE__ */ jsxs15(Box14, { flexDirection: "column", children: [
|
|
2559
|
-
visible.length === 0 && /* @__PURE__ */ jsx16(Box14, { paddingY:
|
|
2264
|
+
visible.length === 0 && /* @__PURE__ */ jsx16(Box14, { paddingY: SPACING.xs, justifyContent: "center", children: /* @__PURE__ */ jsx16(Text16, { color: COLORS.textSecondary, italic: true, children: t("installed_noPackages") }) }),
|
|
2560
2265
|
start > 0 && /* @__PURE__ */ jsxs15(Text16, { color: COLORS.textSecondary, dimColor: true, children: [
|
|
2561
2266
|
" ",
|
|
2562
2267
|
t("scroll_moreAbove", { count: start })
|
|
@@ -2579,13 +2284,13 @@ function InstalledView() {
|
|
|
2579
2284
|
t("scroll_moreBelow", { count: allItems.length - start - MAX_VISIBLE_ROWS })
|
|
2580
2285
|
] })
|
|
2581
2286
|
] }),
|
|
2582
|
-
/* @__PURE__ */ jsx16(Box14, { marginTop:
|
|
2287
|
+
/* @__PURE__ */ jsx16(Box14, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsx16(Text16, { color: COLORS.text, bold: true, children: allItems.length > 0 ? `${cursor + 1}/${allItems.length}` : "0/0" }) })
|
|
2583
2288
|
] });
|
|
2584
2289
|
}
|
|
2585
2290
|
|
|
2586
2291
|
// src/views/search.tsx
|
|
2587
2292
|
import { useState as useState4, useCallback as useCallback2, useEffect as useEffect6, useRef as useRef2 } from "react";
|
|
2588
|
-
import { Box as Box15, Text as Text17, useInput as
|
|
2293
|
+
import { Box as Box15, Text as Text17, useInput as useInput5 } from "ink";
|
|
2589
2294
|
import { TextInput as TextInput2 } from "@inkjs/ui";
|
|
2590
2295
|
import { jsx as jsx17, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
2591
2296
|
function SearchView() {
|
|
@@ -2639,7 +2344,7 @@ function SearchView() {
|
|
|
2639
2344
|
const visibleFormulae = results ? results.formulae.slice(0, MAX_VISIBLE) : [];
|
|
2640
2345
|
const visibleCasks = results ? results.casks.slice(0, MAX_VISIBLE) : [];
|
|
2641
2346
|
const allVisible = [...visibleFormulae, ...visibleCasks];
|
|
2642
|
-
|
|
2347
|
+
useInput5((input, key) => {
|
|
2643
2348
|
if (stream.isRunning) {
|
|
2644
2349
|
if (key.escape) stream.cancel();
|
|
2645
2350
|
return;
|
|
@@ -2687,7 +2392,7 @@ function SearchView() {
|
|
|
2687
2392
|
"esc:",
|
|
2688
2393
|
t("hint_cancel")
|
|
2689
2394
|
] }),
|
|
2690
|
-
!stream.isRunning && /* @__PURE__ */ jsxs16(Box15, { flexDirection: "column", marginTop:
|
|
2395
|
+
!stream.isRunning && /* @__PURE__ */ jsxs16(Box15, { flexDirection: "column", marginTop: SPACING.xs, children: [
|
|
2691
2396
|
/* @__PURE__ */ jsx17(
|
|
2692
2397
|
ResultBanner,
|
|
2693
2398
|
{
|
|
@@ -2703,7 +2408,7 @@ function SearchView() {
|
|
|
2703
2408
|
] });
|
|
2704
2409
|
}
|
|
2705
2410
|
return /* @__PURE__ */ jsxs16(Box15, { flexDirection: "column", children: [
|
|
2706
|
-
/* @__PURE__ */ jsxs16(Box15, { marginBottom:
|
|
2411
|
+
/* @__PURE__ */ jsxs16(Box15, { marginBottom: SPACING.xs, children: [
|
|
2707
2412
|
/* @__PURE__ */ jsxs16(Text17, { color: COLORS.gold, children: [
|
|
2708
2413
|
"\u{1F50D}",
|
|
2709
2414
|
" "
|
|
@@ -2725,7 +2430,7 @@ function SearchView() {
|
|
|
2725
2430
|
] })
|
|
2726
2431
|
] }),
|
|
2727
2432
|
searching && /* @__PURE__ */ jsx17(Loading, { message: t("loading_searching") }),
|
|
2728
|
-
searchError && /* @__PURE__ */ jsx17(Box15, { marginBottom:
|
|
2433
|
+
searchError && /* @__PURE__ */ jsx17(Box15, { marginBottom: SPACING.xs, children: /* @__PURE__ */ jsx17(Text17, { color: COLORS.error, children: searchError }) }),
|
|
2729
2434
|
confirmInstall && /* @__PURE__ */ jsx17(
|
|
2730
2435
|
ConfirmDialog,
|
|
2731
2436
|
{
|
|
@@ -2740,7 +2445,7 @@ function SearchView() {
|
|
|
2740
2445
|
}
|
|
2741
2446
|
),
|
|
2742
2447
|
results && !searching && !confirmInstall && /* @__PURE__ */ jsxs16(Box15, { flexDirection: "column", children: [
|
|
2743
|
-
visibleFormulae.length > 0 && /* @__PURE__ */ jsxs16(Box15, { flexDirection: "column", marginBottom:
|
|
2448
|
+
visibleFormulae.length > 0 && /* @__PURE__ */ jsxs16(Box15, { flexDirection: "column", marginBottom: SPACING.xs, children: [
|
|
2744
2449
|
/* @__PURE__ */ jsx17(Text17, { bold: true, color: COLORS.info, children: t("search_formulaeHeader", { count: results.formulae.length }) }),
|
|
2745
2450
|
visibleFormulae.map((name, i) => {
|
|
2746
2451
|
const isCurrent = i === cursor;
|
|
@@ -2763,21 +2468,21 @@ function SearchView() {
|
|
|
2763
2468
|
t("scroll_moreBelow", { count: results.casks.length - MAX_VISIBLE })
|
|
2764
2469
|
] })
|
|
2765
2470
|
] }),
|
|
2766
|
-
allVisible.length === 0 && /* @__PURE__ */ jsx17(Box15, { borderStyle: "round", borderColor: COLORS.textSecondary, paddingX:
|
|
2767
|
-
/* @__PURE__ */ jsx17(Box15, { marginTop:
|
|
2471
|
+
allVisible.length === 0 && /* @__PURE__ */ jsx17(Box15, { borderStyle: "round", borderColor: COLORS.textSecondary, paddingX: SPACING.sm, children: /* @__PURE__ */ jsx17(Text17, { color: COLORS.textSecondary, italic: true, children: t("search_noResults") }) }),
|
|
2472
|
+
/* @__PURE__ */ jsx17(Box15, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsx17(Text17, { color: COLORS.text, bold: true, children: allVisible.length > 0 ? `${cursor + 1}/${allVisible.length}` : "" }) })
|
|
2768
2473
|
] })
|
|
2769
2474
|
] });
|
|
2770
2475
|
}
|
|
2771
2476
|
|
|
2772
2477
|
// src/views/outdated.tsx
|
|
2773
|
-
import { useEffect as useEffect7, useRef as useRef3, useState as useState5 } from "react";
|
|
2774
|
-
import { Box as Box16, Text as Text18, useInput as
|
|
2478
|
+
import { useEffect as useEffect7, useMemo as useMemo4, useRef as useRef3, useState as useState5 } from "react";
|
|
2479
|
+
import { Box as Box16, Text as Text18, useInput as useInput6, useStdout as useStdout5 } from "ink";
|
|
2775
2480
|
import { jsx as jsx18, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
2776
2481
|
function ImpactPanel({ impact }) {
|
|
2777
2482
|
const riskColor = impact.risk === "high" ? COLORS.error : impact.risk === "medium" ? COLORS.warning : COLORS.success;
|
|
2778
2483
|
const riskLabel = impact.risk === "high" ? t("impact_high") : impact.risk === "medium" ? t("impact_medium") : t("impact_low");
|
|
2779
2484
|
const riskIcon = impact.risk === "high" ? "\u26A0" : impact.risk === "medium" ? "~" : "\u2713";
|
|
2780
|
-
return /* @__PURE__ */ jsxs17(Box16, { flexDirection: "column", marginTop:
|
|
2485
|
+
return /* @__PURE__ */ jsxs17(Box16, { flexDirection: "column", marginTop: SPACING.xs, borderStyle: "round", borderColor: riskColor, paddingX: SPACING.sm, paddingY: SPACING.none, children: [
|
|
2781
2486
|
/* @__PURE__ */ jsxs17(Box16, { children: [
|
|
2782
2487
|
/* @__PURE__ */ jsxs17(Text18, { bold: true, color: riskColor, children: [
|
|
2783
2488
|
riskIcon,
|
|
@@ -2811,10 +2516,13 @@ function OutdatedView() {
|
|
|
2811
2516
|
void fetchOutdated();
|
|
2812
2517
|
}
|
|
2813
2518
|
}, [stream.isRunning, stream.error]);
|
|
2814
|
-
const allOutdated =
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2519
|
+
const allOutdated = useMemo4(
|
|
2520
|
+
() => [
|
|
2521
|
+
...outdated.formulae.map((p) => ({ ...p, type: "formula" })),
|
|
2522
|
+
...outdated.casks.map((p) => ({ ...p, type: "cask" }))
|
|
2523
|
+
],
|
|
2524
|
+
[outdated.formulae, outdated.casks]
|
|
2525
|
+
);
|
|
2818
2526
|
const debouncedCursor = useDebounce(cursor, 150);
|
|
2819
2527
|
useEffect7(() => {
|
|
2820
2528
|
const pkg = allOutdated[debouncedCursor];
|
|
@@ -2830,7 +2538,7 @@ function OutdatedView() {
|
|
|
2830
2538
|
pkg.type
|
|
2831
2539
|
).then(setImpact).catch(() => setImpact(null)).finally(() => setImpactLoading(false));
|
|
2832
2540
|
}, [debouncedCursor, stream.isRunning]);
|
|
2833
|
-
|
|
2541
|
+
useInput6((input, key) => {
|
|
2834
2542
|
if (stream.isRunning) {
|
|
2835
2543
|
if (key.escape) stream.cancel();
|
|
2836
2544
|
return;
|
|
@@ -2883,8 +2591,8 @@ function OutdatedView() {
|
|
|
2883
2591
|
"esc:",
|
|
2884
2592
|
t("hint_cancel")
|
|
2885
2593
|
] }),
|
|
2886
|
-
!stream.isRunning && /* @__PURE__ */ jsxs17(Box16, { flexDirection: "column", marginTop:
|
|
2887
|
-
/* @__PURE__ */ jsxs17(Box16, { borderStyle: "round", borderColor: stream.error ? COLORS.error : COLORS.success, paddingX:
|
|
2594
|
+
!stream.isRunning && /* @__PURE__ */ jsxs17(Box16, { flexDirection: "column", marginTop: SPACING.xs, children: [
|
|
2595
|
+
/* @__PURE__ */ jsxs17(Box16, { borderStyle: "round", borderColor: stream.error ? COLORS.error : COLORS.success, paddingX: SPACING.sm, paddingY: SPACING.none, children: [
|
|
2888
2596
|
/* @__PURE__ */ jsx18(Text18, { color: stream.error ? COLORS.error : COLORS.success, bold: true, children: stream.error ? `\u2718 ${stream.error}` : `\u2714 ${t("outdated_upgradeComplete")}` }),
|
|
2889
2597
|
/* @__PURE__ */ jsxs17(Text18, { color: COLORS.muted, children: [
|
|
2890
2598
|
" ",
|
|
@@ -2904,7 +2612,7 @@ function OutdatedView() {
|
|
|
2904
2612
|
${t("outdated_upgradeAllList", { list: allOutdated.map((p) => p.name).join(", ") })}` : "";
|
|
2905
2613
|
return /* @__PURE__ */ jsxs17(Box16, { flexDirection: "column", children: [
|
|
2906
2614
|
/* @__PURE__ */ jsx18(SectionHeader, { emoji: "\u{1F4E6}", title: t("outdated_title", { count: allOutdated.length }), gradient: GRADIENTS.fire }),
|
|
2907
|
-
confirmAction && /* @__PURE__ */ jsx18(Box16, { marginY:
|
|
2615
|
+
confirmAction && /* @__PURE__ */ jsx18(Box16, { marginY: SPACING.xs, children: /* @__PURE__ */ jsx18(
|
|
2908
2616
|
ConfirmDialog,
|
|
2909
2617
|
{
|
|
2910
2618
|
message: confirmAction.type === "all" ? upgradeAllMessage : t("outdated_confirmSingle", { name: confirmAction.type === "single" ? confirmAction.name : "" }),
|
|
@@ -2920,8 +2628,8 @@ ${t("outdated_upgradeAllList", { list: allOutdated.map((p) => p.name).join(", ")
|
|
|
2920
2628
|
onCancel: () => setConfirmAction(null)
|
|
2921
2629
|
}
|
|
2922
2630
|
) }),
|
|
2923
|
-
allOutdated.length === 0 && !confirmAction && /* @__PURE__ */ jsx18(Box16, { marginTop:
|
|
2924
|
-
allOutdated.length > 0 && !confirmAction && /* @__PURE__ */ jsxs17(Box16, { flexDirection: "column", marginTop:
|
|
2631
|
+
allOutdated.length === 0 && !confirmAction && /* @__PURE__ */ jsx18(Box16, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsx18(ResultBanner, { status: "success", message: `\u2714 ${t("outdated_upToDate")}` }) }),
|
|
2632
|
+
allOutdated.length > 0 && !confirmAction && /* @__PURE__ */ jsxs17(Box16, { flexDirection: "column", marginTop: SPACING.xs, children: [
|
|
2925
2633
|
start > 0 && /* @__PURE__ */ jsxs17(Text18, { color: COLORS.textSecondary, dimColor: true, children: [
|
|
2926
2634
|
" ",
|
|
2927
2635
|
t("scroll_moreAbove", { count: start })
|
|
@@ -2939,21 +2647,21 @@ ${t("outdated_upgradeAllList", { list: allOutdated.map((p) => p.name).join(", ")
|
|
|
2939
2647
|
" ",
|
|
2940
2648
|
t("scroll_moreBelow", { count: allOutdated.length - start - MAX_VISIBLE_ROWS })
|
|
2941
2649
|
] }),
|
|
2942
|
-
/* @__PURE__ */ jsx18(Box16, { marginTop:
|
|
2650
|
+
/* @__PURE__ */ jsx18(Box16, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsxs17(Text18, { color: COLORS.text, bold: true, children: [
|
|
2943
2651
|
cursor + 1,
|
|
2944
2652
|
"/",
|
|
2945
2653
|
allOutdated.length
|
|
2946
2654
|
] }) }),
|
|
2947
2655
|
impact && !stream.isRunning && !confirmAction && /* @__PURE__ */ jsx18(ImpactPanel, { impact }),
|
|
2948
|
-
impactLoading && !stream.isRunning && !confirmAction && /* @__PURE__ */ jsx18(Box16, { marginTop:
|
|
2949
|
-
/* @__PURE__ */ jsx18(Box16, { marginTop:
|
|
2656
|
+
impactLoading && !stream.isRunning && !confirmAction && /* @__PURE__ */ jsx18(Box16, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsx18(Text18, { color: COLORS.textSecondary, children: t("impact_analyzing") }) }),
|
|
2657
|
+
/* @__PURE__ */ jsx18(Box16, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsx18(Text18, { color: COLORS.textSecondary, children: t("impact_hint") }) })
|
|
2950
2658
|
] })
|
|
2951
2659
|
] });
|
|
2952
2660
|
}
|
|
2953
2661
|
|
|
2954
2662
|
// src/views/package-info.tsx
|
|
2955
2663
|
import { useEffect as useEffect8, useRef as useRef4, useState as useState6 } from "react";
|
|
2956
|
-
import { Box as Box17, Text as Text19, useInput as
|
|
2664
|
+
import { Box as Box17, Text as Text19, useInput as useInput7 } from "ink";
|
|
2957
2665
|
import { Fragment as Fragment4, jsx as jsx19, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
2958
2666
|
var ACTION_PROGRESS_KEYS = {
|
|
2959
2667
|
install: "pkgInfo_installing",
|
|
@@ -3046,7 +2754,7 @@ function PackageInfoView() {
|
|
|
3046
2754
|
});
|
|
3047
2755
|
}
|
|
3048
2756
|
}, [stream.isRunning, stream.error]);
|
|
3049
|
-
|
|
2757
|
+
useInput7((input, key) => {
|
|
3050
2758
|
if (stream.isRunning) {
|
|
3051
2759
|
if (key.escape) stream.cancel();
|
|
3052
2760
|
return;
|
|
@@ -3103,7 +2811,7 @@ function PackageInfoView() {
|
|
|
3103
2811
|
onCancel: () => setConfirmAction(null)
|
|
3104
2812
|
}
|
|
3105
2813
|
),
|
|
3106
|
-
/* @__PURE__ */ jsxs18(Box17, { gap:
|
|
2814
|
+
/* @__PURE__ */ jsxs18(Box17, { gap: SPACING.sm, marginBottom: SPACING.xs, children: [
|
|
3107
2815
|
/* @__PURE__ */ jsx19(GradientText, { colors: GRADIENTS.gold, bold: true, children: formula.name }),
|
|
3108
2816
|
/* @__PURE__ */ jsx19(Text19, { color: COLORS.teal, children: installed?.version ?? formula.versions.stable }),
|
|
3109
2817
|
isInstalled && /* @__PURE__ */ jsx19(StatusBadge, { label: t("badge_installed"), variant: "success" }),
|
|
@@ -3112,11 +2820,11 @@ function PackageInfoView() {
|
|
|
3112
2820
|
formula.keg_only && /* @__PURE__ */ jsx19(StatusBadge, { label: t("badge_kegOnly"), variant: "muted" }),
|
|
3113
2821
|
formula.deprecated && /* @__PURE__ */ jsx19(StatusBadge, { label: t("badge_deprecated"), variant: "error" })
|
|
3114
2822
|
] }),
|
|
3115
|
-
/* @__PURE__ */ jsxs18(Box17, { flexDirection: "column", gap:
|
|
2823
|
+
/* @__PURE__ */ jsxs18(Box17, { flexDirection: "column", gap: SPACING.xs, children: [
|
|
3116
2824
|
/* @__PURE__ */ jsx19(Text19, { children: formula.desc }),
|
|
3117
2825
|
/* @__PURE__ */ jsxs18(Box17, { flexDirection: "column", children: [
|
|
3118
2826
|
/* @__PURE__ */ jsx19(SectionHeader, { emoji: "\u{1F4CB}", title: t("pkgInfo_details"), gradient: [COLORS.text, COLORS.muted] }),
|
|
3119
|
-
/* @__PURE__ */ jsxs18(Box17, { borderStyle: "round", borderColor: COLORS.border, paddingX:
|
|
2827
|
+
/* @__PURE__ */ jsxs18(Box17, { borderStyle: "round", borderColor: COLORS.border, paddingX: SPACING.sm, flexDirection: "column", children: [
|
|
3120
2828
|
/* @__PURE__ */ jsxs18(Text19, { children: [
|
|
3121
2829
|
/* @__PURE__ */ jsx19(Text19, { color: COLORS.muted, children: t("pkgInfo_homepage") }),
|
|
3122
2830
|
" ",
|
|
@@ -3161,14 +2869,14 @@ function PackageInfoView() {
|
|
|
3161
2869
|
] }),
|
|
3162
2870
|
formula.dependencies.length > 0 && /* @__PURE__ */ jsxs18(Box17, { flexDirection: "column", children: [
|
|
3163
2871
|
/* @__PURE__ */ jsx19(SectionHeader, { emoji: "\u{1F517}", title: t("pkgInfo_dependencies", { count: formula.dependencies.length }), gradient: GRADIENTS.ocean }),
|
|
3164
|
-
/* @__PURE__ */ jsx19(Box17, { paddingLeft:
|
|
2872
|
+
/* @__PURE__ */ jsx19(Box17, { paddingLeft: SPACING.sm, flexWrap: "wrap", columnGap: 2, children: formula.dependencies.map((dep) => /* @__PURE__ */ jsx19(Text19, { color: COLORS.muted, children: dep }, dep)) })
|
|
3165
2873
|
] }),
|
|
3166
2874
|
formula.caveats && /* @__PURE__ */ jsxs18(Box17, { flexDirection: "column", children: [
|
|
3167
2875
|
/* @__PURE__ */ jsx19(SectionHeader, { emoji: "\u26A0\uFE0F", title: t("pkgInfo_caveats"), color: COLORS.warning }),
|
|
3168
|
-
/* @__PURE__ */ jsx19(Box17, { borderStyle: "round", borderColor: COLORS.warning, paddingX:
|
|
2876
|
+
/* @__PURE__ */ jsx19(Box17, { borderStyle: "round", borderColor: COLORS.warning, paddingX: SPACING.sm, children: /* @__PURE__ */ jsx19(Text19, { color: COLORS.warning, children: formula.caveats }) })
|
|
3169
2877
|
] })
|
|
3170
2878
|
] }),
|
|
3171
|
-
/* @__PURE__ */ jsx19(Box17, { marginTop:
|
|
2879
|
+
/* @__PURE__ */ jsx19(Box17, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsxs18(Text19, { color: COLORS.textSecondary, children: [
|
|
3172
2880
|
isInstalled ? `u:${t("hint_uninstall")}` : `i:${t("hint_install")}`,
|
|
3173
2881
|
isInstalled && formula.outdated ? ` U:${t("hint_upgrade")}` : "",
|
|
3174
2882
|
` esc:${t("hint_back")}`
|
|
@@ -3178,7 +2886,7 @@ function PackageInfoView() {
|
|
|
3178
2886
|
|
|
3179
2887
|
// src/views/services.tsx
|
|
3180
2888
|
import { useEffect as useEffect9, useState as useState7 } from "react";
|
|
3181
|
-
import { Box as Box18, Text as Text20, useInput as
|
|
2889
|
+
import { Box as Box18, Text as Text20, useInput as useInput8, useStdout as useStdout6 } from "ink";
|
|
3182
2890
|
import { jsx as jsx20, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
3183
2891
|
var STATUS_VARIANTS = {
|
|
3184
2892
|
started: "success",
|
|
@@ -3200,7 +2908,7 @@ function ServicesView() {
|
|
|
3200
2908
|
useEffect9(() => {
|
|
3201
2909
|
fetchServices();
|
|
3202
2910
|
}, []);
|
|
3203
|
-
|
|
2911
|
+
useInput8((input, key) => {
|
|
3204
2912
|
if (actionInProgress) return;
|
|
3205
2913
|
if (confirmAction) return;
|
|
3206
2914
|
if (lastError) {
|
|
@@ -3241,7 +2949,7 @@ function ServicesView() {
|
|
|
3241
2949
|
const visible = services.slice(start, start + MAX_VISIBLE_ROWS);
|
|
3242
2950
|
return /* @__PURE__ */ jsxs19(Box18, { flexDirection: "column", children: [
|
|
3243
2951
|
/* @__PURE__ */ jsx20(SectionHeader, { emoji: "\u2699\uFE0F", title: t("services_titleCount", { count: services.length }), gradient: GRADIENTS.ocean }),
|
|
3244
|
-
confirmAction && /* @__PURE__ */ jsx20(Box18, { marginY:
|
|
2952
|
+
confirmAction && /* @__PURE__ */ jsx20(Box18, { marginY: SPACING.xs, children: /* @__PURE__ */ jsx20(
|
|
3245
2953
|
ConfirmDialog,
|
|
3246
2954
|
{
|
|
3247
2955
|
message: confirmAction.type === "stop" ? t("services_confirmStop", { name: confirmAction.name }) : t("services_confirmRestart", { name: confirmAction.name }),
|
|
@@ -3260,8 +2968,8 @@ function ServicesView() {
|
|
|
3260
2968
|
onCancel: () => setConfirmAction(null)
|
|
3261
2969
|
}
|
|
3262
2970
|
) }),
|
|
3263
|
-
/* @__PURE__ */ jsxs19(Box18, { flexDirection: "column", marginTop:
|
|
3264
|
-
/* @__PURE__ */ jsxs19(Box18, { gap:
|
|
2971
|
+
/* @__PURE__ */ jsxs19(Box18, { flexDirection: "column", marginTop: SPACING.xs, children: [
|
|
2972
|
+
/* @__PURE__ */ jsxs19(Box18, { gap: SPACING.xs, borderStyle: "single", borderBottom: true, borderTop: false, borderLeft: false, borderRight: false, borderColor: COLORS.border, paddingBottom: SPACING.none, children: [
|
|
3265
2973
|
/* @__PURE__ */ jsxs19(Text20, { bold: true, color: COLORS.text, children: [
|
|
3266
2974
|
" ",
|
|
3267
2975
|
t("services_name").padEnd(svcNameWidth)
|
|
@@ -3289,8 +2997,8 @@ function ServicesView() {
|
|
|
3289
2997
|
] })
|
|
3290
2998
|
] }),
|
|
3291
2999
|
actionInProgress && /* @__PURE__ */ jsx20(Text20, { color: COLORS.sky, children: t("services_processing") }),
|
|
3292
|
-
lastError && /* @__PURE__ */ jsx20(Box18, { marginTop:
|
|
3293
|
-
/* @__PURE__ */ jsx20(Box18, { marginTop:
|
|
3000
|
+
lastError && /* @__PURE__ */ jsx20(Box18, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsx20(Text20, { color: COLORS.error, children: lastError }) }),
|
|
3001
|
+
/* @__PURE__ */ jsx20(Box18, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsxs19(Text20, { color: COLORS.text, bold: true, children: [
|
|
3294
3002
|
cursor + 1,
|
|
3295
3003
|
"/",
|
|
3296
3004
|
services.length
|
|
@@ -3300,7 +3008,7 @@ function ServicesView() {
|
|
|
3300
3008
|
|
|
3301
3009
|
// src/views/doctor.tsx
|
|
3302
3010
|
import { useEffect as useEffect10, useRef as useRef5 } from "react";
|
|
3303
|
-
import { Box as Box19, Text as Text21, useInput as
|
|
3011
|
+
import { Box as Box19, Text as Text21, useInput as useInput9 } from "ink";
|
|
3304
3012
|
import { jsx as jsx21, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
3305
3013
|
function DoctorView() {
|
|
3306
3014
|
const { doctorWarnings, doctorClean, loading, errors, fetchDoctor } = useBrewStore();
|
|
@@ -3314,38 +3022,38 @@ function DoctorView() {
|
|
|
3314
3022
|
useEffect10(() => {
|
|
3315
3023
|
fetchDoctor();
|
|
3316
3024
|
}, []);
|
|
3317
|
-
|
|
3025
|
+
useInput9((input) => {
|
|
3318
3026
|
if (input === "r") void fetchDoctor();
|
|
3319
3027
|
});
|
|
3320
3028
|
if (loading.doctor) return /* @__PURE__ */ jsx21(Loading, { message: t("loading_doctor") });
|
|
3321
3029
|
if (errors.doctor) return /* @__PURE__ */ jsx21(ErrorMessage, { message: errors.doctor });
|
|
3322
3030
|
return /* @__PURE__ */ jsxs20(Box19, { flexDirection: "column", children: [
|
|
3323
3031
|
/* @__PURE__ */ jsx21(SectionHeader, { emoji: "\u{1FA7A}", title: t("doctor_title"), gradient: GRADIENTS.emerald }),
|
|
3324
|
-
/* @__PURE__ */ jsxs20(Box19, { flexDirection: "column", marginTop:
|
|
3032
|
+
/* @__PURE__ */ jsxs20(Box19, { flexDirection: "column", marginTop: SPACING.xs, children: [
|
|
3325
3033
|
doctorClean && /* @__PURE__ */ jsx21(ResultBanner, { status: "success", message: `\u2714 ${t("doctor_clean")}` }),
|
|
3326
3034
|
doctorClean === false && doctorWarnings.length === 0 && /* @__PURE__ */ jsx21(Text21, { color: COLORS.warning, children: t("doctor_warningsNotCaptured") }),
|
|
3327
3035
|
doctorWarnings.map((warning, i) => (
|
|
3328
3036
|
// FE-004: Improved React key
|
|
3329
|
-
/* @__PURE__ */ jsx21(Box19, { flexDirection: "column", marginBottom:
|
|
3037
|
+
/* @__PURE__ */ jsx21(Box19, { flexDirection: "column", marginBottom: SPACING.xs, borderStyle: "single", borderColor: COLORS.warning, paddingX: SPACING.xs, children: warning.split("\n").map((line, j) => /* @__PURE__ */ jsx21(Text21, { color: j === 0 ? COLORS.warning : COLORS.muted, children: line }, `warning-${i}-${j}-${line.slice(0, 20)}`)) }, `warning-${i}-${warning.slice(0, 20)}`)
|
|
3330
3038
|
))
|
|
3331
3039
|
] }),
|
|
3332
|
-
/* @__PURE__ */ jsx21(Box19, { marginTop:
|
|
3040
|
+
/* @__PURE__ */ jsx21(Box19, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsx21(Text21, { color: COLORS.text, bold: true, children: doctorWarnings.length > 0 ? tp("plural_warnings", doctorWarnings.length) : "" }) })
|
|
3333
3041
|
] });
|
|
3334
3042
|
}
|
|
3335
3043
|
|
|
3336
3044
|
// src/views/profiles.tsx
|
|
3337
3045
|
import { useEffect as useEffect11, useRef as useRef6, useState as useState8 } from "react";
|
|
3338
|
-
import { Box as Box24, useInput as
|
|
3046
|
+
import { Box as Box24, useInput as useInput10 } from "ink";
|
|
3339
3047
|
|
|
3340
3048
|
// src/stores/profile-store.ts
|
|
3341
3049
|
import { create as create9 } from "zustand";
|
|
3342
3050
|
|
|
3343
3051
|
// src/lib/profiles/profile-manager.ts
|
|
3344
|
-
import { readFile
|
|
3345
|
-
import { join
|
|
3052
|
+
import { readFile, writeFile, readdir, rm, rename } from "fs/promises";
|
|
3053
|
+
import { join, basename } from "path";
|
|
3346
3054
|
|
|
3347
3055
|
// src/lib/license/watermark.ts
|
|
3348
|
-
function getWatermark(license, consent =
|
|
3056
|
+
function getWatermark(license, consent = false) {
|
|
3349
3057
|
if (!consent) return "";
|
|
3350
3058
|
if (!license?.customerEmail) return "";
|
|
3351
3059
|
return `Licensed to: ${license.customerEmail}`;
|
|
@@ -3369,7 +3077,7 @@ function validateProfileName(name) {
|
|
|
3369
3077
|
}
|
|
3370
3078
|
function profilePath(name) {
|
|
3371
3079
|
validateProfileName(name);
|
|
3372
|
-
return
|
|
3080
|
+
return join(PROFILES_DIR, `${basename(name)}.json`);
|
|
3373
3081
|
}
|
|
3374
3082
|
async function listProfiles(isPro) {
|
|
3375
3083
|
proCheck(isPro);
|
|
@@ -3383,7 +3091,7 @@ async function listProfiles(isPro) {
|
|
|
3383
3091
|
}
|
|
3384
3092
|
async function loadProfile(isPro, name) {
|
|
3385
3093
|
proCheck(isPro);
|
|
3386
|
-
const raw = await
|
|
3094
|
+
const raw = await readFile(profilePath(name), "utf-8");
|
|
3387
3095
|
let file;
|
|
3388
3096
|
try {
|
|
3389
3097
|
file = JSON.parse(raw);
|
|
@@ -3404,13 +3112,13 @@ async function saveProfile(isPro, profile) {
|
|
|
3404
3112
|
const filePath = profilePath(profile.name);
|
|
3405
3113
|
const tmpPath = filePath + ".tmp";
|
|
3406
3114
|
const file = { version: 1, profile };
|
|
3407
|
-
await
|
|
3408
|
-
await
|
|
3115
|
+
await writeFile(tmpPath, JSON.stringify(file, null, 2), { encoding: "utf-8", mode: 384 });
|
|
3116
|
+
await rename(tmpPath, filePath);
|
|
3409
3117
|
}
|
|
3410
3118
|
async function deleteProfile(isPro, name) {
|
|
3411
3119
|
proCheck(isPro);
|
|
3412
3120
|
try {
|
|
3413
|
-
await
|
|
3121
|
+
await rm(profilePath(name));
|
|
3414
3122
|
} catch {
|
|
3415
3123
|
}
|
|
3416
3124
|
}
|
|
@@ -3436,8 +3144,8 @@ async function exportCurrentSetup(isPro, name, description, license = null, cons
|
|
|
3436
3144
|
formulae: leaves,
|
|
3437
3145
|
casks,
|
|
3438
3146
|
taps,
|
|
3439
|
-
exportedBy:
|
|
3440
|
-
//
|
|
3147
|
+
exportedBy: getWatermark(license, consent)
|
|
3148
|
+
// BK-014: explicit consent required (default false)
|
|
3441
3149
|
};
|
|
3442
3150
|
await saveProfile(isPro, profile);
|
|
3443
3151
|
return profile;
|
|
@@ -3560,8 +3268,8 @@ import { jsx as jsx22, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
|
3560
3268
|
function ProfileListMode({ profileNames, cursor, confirmDelete, loadError, onConfirmDelete, onCancelDelete }) {
|
|
3561
3269
|
return /* @__PURE__ */ jsxs21(Box20, { flexDirection: "column", children: [
|
|
3562
3270
|
/* @__PURE__ */ jsx22(SectionHeader, { emoji: "\u{1F4C1}", title: t("profiles_title", { count: profileNames.length }), gradient: GRADIENTS.gold }),
|
|
3563
|
-
loadError && /* @__PURE__ */ jsx22(Box20, { marginY:
|
|
3564
|
-
confirmDelete && profileNames[cursor] && /* @__PURE__ */ jsx22(Box20, { marginY:
|
|
3271
|
+
loadError && /* @__PURE__ */ jsx22(Box20, { marginY: SPACING.xs, children: /* @__PURE__ */ jsx22(Text22, { color: COLORS.error, children: loadError }) }),
|
|
3272
|
+
confirmDelete && profileNames[cursor] && /* @__PURE__ */ jsx22(Box20, { marginY: SPACING.xs, children: /* @__PURE__ */ jsx22(
|
|
3565
3273
|
ConfirmDialog,
|
|
3566
3274
|
{
|
|
3567
3275
|
message: t("profiles_confirmDelete", { name: profileNames[cursor] }),
|
|
@@ -3569,7 +3277,7 @@ function ProfileListMode({ profileNames, cursor, confirmDelete, loadError, onCon
|
|
|
3569
3277
|
onCancel: onCancelDelete
|
|
3570
3278
|
}
|
|
3571
3279
|
) }),
|
|
3572
|
-
profileNames.length === 0 && !confirmDelete && /* @__PURE__ */ jsx22(Box20, { marginTop:
|
|
3280
|
+
profileNames.length === 0 && !confirmDelete && /* @__PURE__ */ jsx22(Box20, { marginTop: SPACING.xs, borderStyle: "round", borderColor: COLORS.textSecondary, paddingX: SPACING.sm, paddingY: SPACING.none, children: /* @__PURE__ */ jsxs21(Box20, { flexDirection: "column", children: [
|
|
3573
3281
|
/* @__PURE__ */ jsx22(Text22, { color: COLORS.textSecondary, italic: true, children: t("profiles_noProfiles") }),
|
|
3574
3282
|
/* @__PURE__ */ jsxs21(Text22, { color: COLORS.muted, children: [
|
|
3575
3283
|
t("profiles_press"),
|
|
@@ -3579,12 +3287,12 @@ function ProfileListMode({ profileNames, cursor, confirmDelete, loadError, onCon
|
|
|
3579
3287
|
t("profiles_exportHint")
|
|
3580
3288
|
] })
|
|
3581
3289
|
] }) }),
|
|
3582
|
-
profileNames.length > 0 && !confirmDelete && /* @__PURE__ */ jsxs21(Box20, { flexDirection: "column", marginTop:
|
|
3290
|
+
profileNames.length > 0 && !confirmDelete && /* @__PURE__ */ jsxs21(Box20, { flexDirection: "column", marginTop: SPACING.xs, children: [
|
|
3583
3291
|
profileNames.map((name, i) => {
|
|
3584
3292
|
const isCurrent = i === cursor;
|
|
3585
3293
|
return /* @__PURE__ */ jsx22(SelectableRow, { isCurrent, children: /* @__PURE__ */ jsx22(Text22, { bold: isCurrent, inverse: isCurrent, children: name }) }, name);
|
|
3586
3294
|
}),
|
|
3587
|
-
/* @__PURE__ */ jsx22(Box20, { marginTop:
|
|
3295
|
+
/* @__PURE__ */ jsx22(Box20, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsxs21(Text22, { color: COLORS.text, bold: true, children: [
|
|
3588
3296
|
cursor + 1,
|
|
3589
3297
|
"/",
|
|
3590
3298
|
profileNames.length
|
|
@@ -3601,16 +3309,16 @@ function ProfileDetailMode({ profile }) {
|
|
|
3601
3309
|
/* @__PURE__ */ jsx23(Text23, { bold: true, color: COLORS.gold, children: profile.name }),
|
|
3602
3310
|
/* @__PURE__ */ jsx23(Text23, { color: COLORS.muted, children: profile.description }),
|
|
3603
3311
|
/* @__PURE__ */ jsx23(Text23, { color: COLORS.muted, children: t("profiles_created", { date: formatDate(profile.createdAt) }) }),
|
|
3604
|
-
/* @__PURE__ */ jsxs22(Box21, { marginTop:
|
|
3312
|
+
/* @__PURE__ */ jsxs22(Box21, { marginTop: SPACING.xs, flexDirection: "column", children: [
|
|
3605
3313
|
/* @__PURE__ */ jsx23(Text23, { bold: true, children: t("profiles_formulaeCount", { count: profile.formulae.length }) }),
|
|
3606
|
-
/* @__PURE__ */ jsxs22(Box21, { paddingLeft:
|
|
3314
|
+
/* @__PURE__ */ jsxs22(Box21, { paddingLeft: SPACING.sm, flexDirection: "column", children: [
|
|
3607
3315
|
profile.formulae.slice(0, 30).map((f) => /* @__PURE__ */ jsx23(Text23, { color: COLORS.muted, children: f }, f)),
|
|
3608
3316
|
profile.formulae.length > 30 && /* @__PURE__ */ jsx23(Text23, { color: COLORS.textSecondary, italic: true, children: t("common_andMore", { count: profile.formulae.length - 30 }) })
|
|
3609
3317
|
] }),
|
|
3610
3318
|
/* @__PURE__ */ jsx23(Text23, { bold: true, children: t("profiles_casksCount", { count: profile.casks.length }) }),
|
|
3611
|
-
/* @__PURE__ */ jsx23(Box21, { paddingLeft:
|
|
3319
|
+
/* @__PURE__ */ jsx23(Box21, { paddingLeft: SPACING.sm, flexDirection: "column", children: profile.casks.map((c) => /* @__PURE__ */ jsx23(Text23, { color: COLORS.muted, children: c }, c)) })
|
|
3612
3320
|
] }),
|
|
3613
|
-
/* @__PURE__ */ jsx23(Box21, { marginTop:
|
|
3321
|
+
/* @__PURE__ */ jsx23(Box21, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsxs22(Text23, { color: COLORS.textSecondary, children: [
|
|
3614
3322
|
"esc:",
|
|
3615
3323
|
t("hint_back"),
|
|
3616
3324
|
" e:",
|
|
@@ -3724,7 +3432,7 @@ function ProfilesView() {
|
|
|
3724
3432
|
}
|
|
3725
3433
|
return void 0;
|
|
3726
3434
|
}, [mode]);
|
|
3727
|
-
|
|
3435
|
+
useInput10((input, key) => {
|
|
3728
3436
|
if (mode !== "list" || confirmDelete) return;
|
|
3729
3437
|
if (input === "n") {
|
|
3730
3438
|
setMode("create-name");
|
|
@@ -3746,7 +3454,7 @@ function ProfilesView() {
|
|
|
3746
3454
|
if (input === "j" || key.downArrow) setCursor((c) => Math.min(c + 1, Math.max(0, profileNames.length - 1)));
|
|
3747
3455
|
else if (input === "k" || key.upArrow) setCursor((c) => Math.max(c - 1, 0));
|
|
3748
3456
|
});
|
|
3749
|
-
|
|
3457
|
+
useInput10((input, key) => {
|
|
3750
3458
|
if (key.escape || input === "q") {
|
|
3751
3459
|
setMode("list");
|
|
3752
3460
|
return;
|
|
@@ -3757,7 +3465,7 @@ function ProfilesView() {
|
|
|
3757
3465
|
setMode("edit-name");
|
|
3758
3466
|
}
|
|
3759
3467
|
}, { isActive: mode === "detail" });
|
|
3760
|
-
|
|
3468
|
+
useInput10(() => {
|
|
3761
3469
|
setMode("list");
|
|
3762
3470
|
}, { isActive: mode === "importing" && !importRunning });
|
|
3763
3471
|
const prepareImport = async (name) => {
|
|
@@ -3821,7 +3529,7 @@ function ProfilesView() {
|
|
|
3821
3529
|
if (mode === "importing") {
|
|
3822
3530
|
return /* @__PURE__ */ jsxs25(Box24, { flexDirection: "column", children: [
|
|
3823
3531
|
/* @__PURE__ */ jsx26(ProgressLog, { lines: importLines, isRunning: importRunning, title: t("profiles_importTitle") }),
|
|
3824
|
-
!importRunning && /* @__PURE__ */ jsx26(Box24, { marginTop:
|
|
3532
|
+
!importRunning && /* @__PURE__ */ jsx26(Box24, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsx26(ResultBanner, { status: importHadError ? "error" : "success", message: importHadError ? t("profiles_importPartial") : `\u2714 ${t("profiles_importComplete")}` }) })
|
|
3825
3533
|
] });
|
|
3826
3534
|
}
|
|
3827
3535
|
if (mode === "create-name") {
|
|
@@ -3892,7 +3600,7 @@ function ProfilesView() {
|
|
|
3892
3600
|
|
|
3893
3601
|
// src/views/smart-cleanup.tsx
|
|
3894
3602
|
import { useEffect as useEffect12, useRef as useRef7, useState as useState9 } from "react";
|
|
3895
|
-
import { Box as Box25, Text as Text26, useInput as
|
|
3603
|
+
import { Box as Box25, Text as Text26, useInput as useInput11 } from "ink";
|
|
3896
3604
|
|
|
3897
3605
|
// src/stores/cleanup-store.ts
|
|
3898
3606
|
import { create as create10 } from "zustand";
|
|
@@ -4032,7 +3740,7 @@ function SmartCleanupView() {
|
|
|
4032
3740
|
}, [stream.isRunning, stream.error]);
|
|
4033
3741
|
const candidates = summary?.candidates ?? [];
|
|
4034
3742
|
const isDependencyError = stream.error != null && stream.lines.some((l) => l.includes("Refusing to uninstall") || l.includes("required by"));
|
|
4035
|
-
|
|
3743
|
+
useInput11((input, key) => {
|
|
4036
3744
|
if (stream.isRunning) {
|
|
4037
3745
|
if (key.escape) stream.cancel();
|
|
4038
3746
|
return;
|
|
@@ -4073,7 +3781,7 @@ function SmartCleanupView() {
|
|
|
4073
3781
|
t("hint_cancel")
|
|
4074
3782
|
] }),
|
|
4075
3783
|
!stream.isRunning && !stream.error && /* @__PURE__ */ jsx27(ResultBanner, { status: "success", message: `\u2714 ${t("cleanup_complete")}` }),
|
|
4076
|
-
!stream.isRunning && stream.error && /* @__PURE__ */ jsxs26(Box25, { flexDirection: "column", gap:
|
|
3784
|
+
!stream.isRunning && stream.error && /* @__PURE__ */ jsxs26(Box25, { flexDirection: "column", gap: SPACING.xs, children: [
|
|
4077
3785
|
/* @__PURE__ */ jsx27(ResultBanner, { status: "error", message: `\u2718 ${t("cleanup_depError")}` }),
|
|
4078
3786
|
isDependencyError && failedNames.length > 0 && /* @__PURE__ */ jsxs26(Text26, { color: COLORS.warning, children: [
|
|
4079
3787
|
"F:",
|
|
@@ -4082,7 +3790,7 @@ function SmartCleanupView() {
|
|
|
4082
3790
|
t("hint_refresh")
|
|
4083
3791
|
] })
|
|
4084
3792
|
] }),
|
|
4085
|
-
confirmForce && /* @__PURE__ */ jsx27(Box25, { marginY:
|
|
3793
|
+
confirmForce && /* @__PURE__ */ jsx27(Box25, { marginY: SPACING.xs, children: /* @__PURE__ */ jsx27(
|
|
4086
3794
|
ConfirmDialog,
|
|
4087
3795
|
{
|
|
4088
3796
|
message: t("cleanup_confirmForce", { count: failedNames.length }),
|
|
@@ -4099,13 +3807,13 @@ function SmartCleanupView() {
|
|
|
4099
3807
|
}
|
|
4100
3808
|
return /* @__PURE__ */ jsxs26(Box25, { flexDirection: "column", children: [
|
|
4101
3809
|
/* @__PURE__ */ jsx27(SectionHeader, { emoji: "\u{1F9F9}", title: t("cleanup_title"), gradient: GRADIENTS.emerald }),
|
|
4102
|
-
summary && /* @__PURE__ */ jsxs26(Box25, { gap:
|
|
3810
|
+
summary && /* @__PURE__ */ jsxs26(Box25, { gap: SPACING.xs, marginY: SPACING.xs, children: [
|
|
4103
3811
|
/* @__PURE__ */ jsx27(StatCard, { label: t("cleanup_orphans"), value: candidates.length, color: candidates.length > 0 ? COLORS.warning : COLORS.success }),
|
|
4104
3812
|
/* @__PURE__ */ jsx27(StatCard, { label: t("cleanup_reclaimable"), value: summary.totalReclaimableFormatted, color: COLORS.sky }),
|
|
4105
3813
|
/* @__PURE__ */ jsx27(StatCard, { label: t("cleanup_selected"), value: selected.size, color: selected.size > 0 ? COLORS.success : COLORS.muted })
|
|
4106
3814
|
] }),
|
|
4107
|
-
confirmClean && /* @__PURE__ */ jsxs26(Box25, { flexDirection: "column", marginY:
|
|
4108
|
-
/* @__PURE__ */ jsx27(Box25, { borderStyle: "round", borderColor: COLORS.warning, paddingX:
|
|
3815
|
+
confirmClean && /* @__PURE__ */ jsxs26(Box25, { flexDirection: "column", marginY: SPACING.xs, gap: SPACING.xs, children: [
|
|
3816
|
+
/* @__PURE__ */ jsx27(Box25, { borderStyle: "round", borderColor: COLORS.warning, paddingX: SPACING.sm, paddingY: SPACING.none, children: /* @__PURE__ */ jsx27(Text26, { color: COLORS.warning, children: t("cleanup_warning_system_tools") }) }),
|
|
4109
3817
|
/* @__PURE__ */ jsx27(
|
|
4110
3818
|
ConfirmDialog,
|
|
4111
3819
|
{
|
|
@@ -4137,7 +3845,7 @@ function SmartCleanupView() {
|
|
|
4137
3845
|
] })
|
|
4138
3846
|
] }, c.name);
|
|
4139
3847
|
}),
|
|
4140
|
-
/* @__PURE__ */ jsx27(Box25, { marginTop:
|
|
3848
|
+
/* @__PURE__ */ jsx27(Box25, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsxs26(Text26, { color: COLORS.text, bold: true, children: [
|
|
4141
3849
|
cursor + 1,
|
|
4142
3850
|
"/",
|
|
4143
3851
|
candidates.length
|
|
@@ -4147,8 +3855,8 @@ function SmartCleanupView() {
|
|
|
4147
3855
|
}
|
|
4148
3856
|
|
|
4149
3857
|
// src/views/history.tsx
|
|
4150
|
-
import { useEffect as useEffect13, useState as useState10, useMemo as
|
|
4151
|
-
import { Box as Box26, Text as Text27, useInput as
|
|
3858
|
+
import { useEffect as useEffect13, useState as useState10, useMemo as useMemo5 } from "react";
|
|
3859
|
+
import { Box as Box26, Text as Text27, useInput as useInput12, useStdout as useStdout7 } from "ink";
|
|
4152
3860
|
|
|
4153
3861
|
// src/stores/history-store.ts
|
|
4154
3862
|
import { create as create11 } from "zustand";
|
|
@@ -4221,7 +3929,7 @@ function HistoryView() {
|
|
|
4221
3929
|
}
|
|
4222
3930
|
return void 0;
|
|
4223
3931
|
}, [isSearching]);
|
|
4224
|
-
const filtered =
|
|
3932
|
+
const filtered = useMemo5(() => {
|
|
4225
3933
|
let result = entries;
|
|
4226
3934
|
if (filter !== "all") {
|
|
4227
3935
|
result = result.filter((e) => e.action === filter);
|
|
@@ -4232,7 +3940,7 @@ function HistoryView() {
|
|
|
4232
3940
|
}
|
|
4233
3941
|
return result;
|
|
4234
3942
|
}, [entries, filter, debouncedQuery]);
|
|
4235
|
-
|
|
3943
|
+
useInput12((input, key) => {
|
|
4236
3944
|
if (confirmClear || confirmReplay || stream.isRunning) return;
|
|
4237
3945
|
if (isSearching) {
|
|
4238
3946
|
if (key.escape) {
|
|
@@ -4269,11 +3977,11 @@ function HistoryView() {
|
|
|
4269
3977
|
const start = Math.max(0, cursor - Math.floor(MAX_VISIBLE_ROWS / 2));
|
|
4270
3978
|
const visible = filtered.slice(start, start + MAX_VISIBLE_ROWS);
|
|
4271
3979
|
return /* @__PURE__ */ jsxs27(Box26, { flexDirection: "column", children: [
|
|
4272
|
-
/* @__PURE__ */ jsxs27(Box26, { gap:
|
|
3980
|
+
/* @__PURE__ */ jsxs27(Box26, { gap: SPACING.sm, marginBottom: SPACING.xs, children: [
|
|
4273
3981
|
/* @__PURE__ */ jsx28(SectionHeader, { emoji: "\u{1F4DC}", title: t("history_title", { count: filtered.length }), gradient: GRADIENTS.gold }),
|
|
4274
3982
|
/* @__PURE__ */ jsx28(Text27, { color: filter === "all" ? COLORS.text : COLORS.gold, children: t("history_filterLabel", { filter }) })
|
|
4275
3983
|
] }),
|
|
4276
|
-
isSearching && /* @__PURE__ */ jsx28(Box26, { marginBottom:
|
|
3984
|
+
isSearching && /* @__PURE__ */ jsx28(Box26, { marginBottom: SPACING.xs, children: /* @__PURE__ */ jsx28(SearchInput, { defaultValue: searchQuery, onChange: setSearchQuery, placeholder: t("history_searchPlaceholder"), isActive: true }) }),
|
|
4277
3985
|
confirmClear && /* @__PURE__ */ jsx28(
|
|
4278
3986
|
ConfirmDialog,
|
|
4279
3987
|
{
|
|
@@ -4313,7 +4021,7 @@ function HistoryView() {
|
|
|
4313
4021
|
onCancel: () => setConfirmReplay(null)
|
|
4314
4022
|
}
|
|
4315
4023
|
),
|
|
4316
|
-
(stream.isRunning || stream.lines.length > 0) && /* @__PURE__ */ jsx28(Box26, { marginY:
|
|
4024
|
+
(stream.isRunning || stream.lines.length > 0) && /* @__PURE__ */ jsx28(Box26, { marginY: SPACING.xs, children: /* @__PURE__ */ jsx28(ProgressLog, { lines: stream.lines, isRunning: stream.isRunning, title: t("hint_replay") }) }),
|
|
4317
4025
|
filtered.length === 0 && !confirmClear && /* @__PURE__ */ jsx28(Text27, { color: COLORS.textSecondary, italic: true, children: filter !== "all" ? t("history_noEntriesFor", { filter }) : t("history_noEntries") }),
|
|
4318
4026
|
filtered.length > 0 && !confirmClear && /* @__PURE__ */ jsxs27(Box26, { flexDirection: "column", children: [
|
|
4319
4027
|
start > 0 && /* @__PURE__ */ jsxs27(Text27, { color: COLORS.textSecondary, dimColor: true, children: [
|
|
@@ -4337,7 +4045,7 @@ function HistoryView() {
|
|
|
4337
4045
|
" ",
|
|
4338
4046
|
t("scroll_moreBelow", { count: filtered.length - start - MAX_VISIBLE_ROWS })
|
|
4339
4047
|
] }),
|
|
4340
|
-
/* @__PURE__ */ jsx28(Box26, { marginTop:
|
|
4048
|
+
/* @__PURE__ */ jsx28(Box26, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsxs27(Text27, { color: COLORS.text, bold: true, children: [
|
|
4341
4049
|
cursor + 1,
|
|
4342
4050
|
"/",
|
|
4343
4051
|
filtered.length
|
|
@@ -4348,7 +4056,7 @@ function HistoryView() {
|
|
|
4348
4056
|
|
|
4349
4057
|
// src/views/security-audit.tsx
|
|
4350
4058
|
import { useEffect as useEffect14, useState as useState11 } from "react";
|
|
4351
|
-
import { Box as Box27, Text as Text28, useInput as
|
|
4059
|
+
import { Box as Box27, Text as Text28, useInput as useInput13 } from "ink";
|
|
4352
4060
|
import { jsx as jsx29, jsxs as jsxs28 } from "react/jsx-runtime";
|
|
4353
4061
|
var SEVERITY_COLORS = {
|
|
4354
4062
|
CRITICAL: COLORS.error,
|
|
@@ -4364,7 +4072,7 @@ var SEVERITY_BADGE = {
|
|
|
4364
4072
|
LOW: "muted",
|
|
4365
4073
|
UNKNOWN: "muted"
|
|
4366
4074
|
};
|
|
4367
|
-
function
|
|
4075
|
+
function isNetworkError(msg) {
|
|
4368
4076
|
return /fetch failed|ECONNREFUSED|ENOTFOUND|ETIMEDOUT|network|abort/i.test(msg);
|
|
4369
4077
|
}
|
|
4370
4078
|
function SecurityAuditView() {
|
|
@@ -4378,7 +4086,7 @@ function SecurityAuditView() {
|
|
|
4378
4086
|
scan();
|
|
4379
4087
|
}, []);
|
|
4380
4088
|
const results = summary?.results ?? [];
|
|
4381
|
-
|
|
4089
|
+
useInput13((input, key) => {
|
|
4382
4090
|
if (confirmUpgrade || stream.isRunning) return;
|
|
4383
4091
|
if (input === "r") {
|
|
4384
4092
|
void scan(true);
|
|
@@ -4400,13 +4108,13 @@ function SecurityAuditView() {
|
|
|
4400
4108
|
});
|
|
4401
4109
|
if (loading) return /* @__PURE__ */ jsx29(Loading, { message: t("loading_security") });
|
|
4402
4110
|
if (error) {
|
|
4403
|
-
const displayError =
|
|
4111
|
+
const displayError = isNetworkError(error) ? t("security_networkError") : error;
|
|
4404
4112
|
return /* @__PURE__ */ jsx29(ErrorMessage, { message: displayError });
|
|
4405
4113
|
}
|
|
4406
4114
|
const cacheAge = cachedAt ? formatRelativeTime(cachedAt / 1e3) : null;
|
|
4407
4115
|
return /* @__PURE__ */ jsxs28(Box27, { flexDirection: "column", children: [
|
|
4408
4116
|
/* @__PURE__ */ jsx29(SectionHeader, { emoji: "\u{1F6E1}\uFE0F", title: t("security_title"), gradient: GRADIENTS.ocean }),
|
|
4409
|
-
summary && /* @__PURE__ */ jsxs28(Box27, { gap:
|
|
4117
|
+
summary && /* @__PURE__ */ jsxs28(Box27, { gap: SPACING.xs, marginY: SPACING.xs, children: [
|
|
4410
4118
|
/* @__PURE__ */ jsx29(StatCard, { label: t("security_scanned"), value: summary.totalPackages, color: COLORS.info }),
|
|
4411
4119
|
/* @__PURE__ */ jsx29(
|
|
4412
4120
|
StatCard,
|
|
@@ -4421,8 +4129,8 @@ function SecurityAuditView() {
|
|
|
4421
4129
|
summary.mediumCount > 0 && /* @__PURE__ */ jsx29(StatCard, { label: t("security_medium"), value: summary.mediumCount, color: COLORS.warning })
|
|
4422
4130
|
] }),
|
|
4423
4131
|
cacheAge && /* @__PURE__ */ jsx29(Text28, { color: COLORS.muted, children: t("security_cachedResults", { time: cacheAge }) }),
|
|
4424
|
-
results.length === 0 && summary && /* @__PURE__ */ jsx29(Box27, { marginTop:
|
|
4425
|
-
confirmUpgrade && /* @__PURE__ */ jsx29(Box27, { marginY:
|
|
4132
|
+
results.length === 0 && summary && /* @__PURE__ */ jsx29(Box27, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsx29(ResultBanner, { status: "success", message: `\u2714 ${t("security_noVulns")}` }) }),
|
|
4133
|
+
confirmUpgrade && /* @__PURE__ */ jsx29(Box27, { marginY: SPACING.xs, children: /* @__PURE__ */ jsx29(
|
|
4426
4134
|
ConfirmDialog,
|
|
4427
4135
|
{
|
|
4428
4136
|
message: t("security_confirmUpgrade", { name: confirmUpgrade }),
|
|
@@ -4435,8 +4143,8 @@ function SecurityAuditView() {
|
|
|
4435
4143
|
onCancel: () => setConfirmUpgrade(null)
|
|
4436
4144
|
}
|
|
4437
4145
|
) }),
|
|
4438
|
-
(stream.isRunning || stream.lines.length > 0) && /* @__PURE__ */ jsx29(Box27, { marginY:
|
|
4439
|
-
results.length > 0 && /* @__PURE__ */ jsxs28(Box27, { flexDirection: "column", marginTop:
|
|
4146
|
+
(stream.isRunning || stream.lines.length > 0) && /* @__PURE__ */ jsx29(Box27, { marginY: SPACING.xs, children: /* @__PURE__ */ jsx29(ProgressLog, { lines: stream.lines, isRunning: stream.isRunning, title: t("hint_upgrade") }) }),
|
|
4147
|
+
results.length > 0 && /* @__PURE__ */ jsxs28(Box27, { flexDirection: "column", marginTop: SPACING.xs, children: [
|
|
4440
4148
|
results.map((pkg, i) => {
|
|
4441
4149
|
const isCurrent = i === cursor;
|
|
4442
4150
|
const isExpanded = expandedPkg === pkg.packageName;
|
|
@@ -4453,8 +4161,8 @@ function SecurityAuditView() {
|
|
|
4453
4161
|
] }),
|
|
4454
4162
|
/* @__PURE__ */ jsx29(Text28, { color: COLORS.muted, children: isExpanded ? "\u25BC" : "\u25B6" })
|
|
4455
4163
|
] }),
|
|
4456
|
-
isExpanded && /* @__PURE__ */ jsx29(Box27, { flexDirection: "column", paddingLeft:
|
|
4457
|
-
/* @__PURE__ */ jsxs28(Box27, { gap:
|
|
4164
|
+
isExpanded && /* @__PURE__ */ jsx29(Box27, { flexDirection: "column", paddingLeft: SPACING.lg, marginBottom: SPACING.xs, children: pkg.vulnerabilities.map((vuln) => /* @__PURE__ */ jsxs28(Box27, { flexDirection: "column", marginBottom: SPACING.xs, children: [
|
|
4165
|
+
/* @__PURE__ */ jsxs28(Box27, { gap: SPACING.xs, children: [
|
|
4458
4166
|
/* @__PURE__ */ jsx29(Text28, { color: SEVERITY_COLORS[vuln.severity], bold: true, children: vuln.id }),
|
|
4459
4167
|
/* @__PURE__ */ jsxs28(Text28, { color: COLORS.muted, children: [
|
|
4460
4168
|
"[",
|
|
@@ -4467,50 +4175,51 @@ function SecurityAuditView() {
|
|
|
4467
4175
|
] }, vuln.id)) })
|
|
4468
4176
|
] }, pkg.packageName);
|
|
4469
4177
|
}),
|
|
4470
|
-
/* @__PURE__ */ jsx29(Box27, { marginTop:
|
|
4178
|
+
/* @__PURE__ */ jsx29(Box27, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsxs28(Text28, { color: COLORS.text, bold: true, children: [
|
|
4471
4179
|
cursor + 1,
|
|
4472
4180
|
"/",
|
|
4473
4181
|
results.length
|
|
4474
4182
|
] }) }),
|
|
4475
|
-
/* @__PURE__ */ jsx29(Box27, { marginTop:
|
|
4183
|
+
/* @__PURE__ */ jsx29(Box27, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsx29(Text28, { color: COLORS.textSecondary, children: t("security_rollback_hint") }) })
|
|
4476
4184
|
] })
|
|
4477
4185
|
] });
|
|
4478
4186
|
}
|
|
4479
4187
|
|
|
4480
4188
|
// src/views/account.tsx
|
|
4481
4189
|
import { useState as useState12 } from "react";
|
|
4482
|
-
import { Box as Box28, Text as Text29, useInput as
|
|
4190
|
+
import { Box as Box28, Text as Text29, useInput as useInput14 } from "ink";
|
|
4483
4191
|
import { TextInput as TextInput5 } from "@inkjs/ui";
|
|
4484
4192
|
|
|
4485
4193
|
// src/lib/license/promo.ts
|
|
4486
|
-
import { readFile as
|
|
4487
|
-
import { randomBytes
|
|
4488
|
-
import { join as
|
|
4489
|
-
|
|
4490
|
-
var
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
}
|
|
4194
|
+
import { readFile as readFile2, writeFile as writeFile2, rename as rename2 } from "fs/promises";
|
|
4195
|
+
import { randomBytes, randomUUID } from "crypto";
|
|
4196
|
+
import { join as join2 } from "path";
|
|
4197
|
+
var PROMO_PATH = join2(DATA_DIR, "promo.json");
|
|
4198
|
+
var PROMO_API_URL = "https://api.molinesdesigns.com/api/promo";
|
|
4199
|
+
function validatePromoApiUrl(url) {
|
|
4200
|
+
const parsed = new URL(url);
|
|
4201
|
+
if (parsed.protocol !== "https:") {
|
|
4202
|
+
throw new Error("HTTPS required for promo API");
|
|
4203
|
+
}
|
|
4204
|
+
if (!parsed.hostname.endsWith("molinesdesigns.com")) {
|
|
4205
|
+
throw new Error("Invalid promo API host");
|
|
4496
4206
|
}
|
|
4497
|
-
const id = randomUUID2();
|
|
4498
|
-
await mkdir2(join3(homedir2(), ".brew-tui"), { recursive: true, mode: 448 });
|
|
4499
|
-
await writeFile4(MACHINE_ID_PATH2, id, { encoding: "utf-8", mode: 384 });
|
|
4500
|
-
return id;
|
|
4501
4207
|
}
|
|
4502
|
-
var PROMO_PATH = join3(DATA_DIR, "promo.json");
|
|
4503
|
-
var PROMO_API_URL = "https://api.molinesdesigns.com/api/promo";
|
|
4504
4208
|
async function redeemPromoCode(code) {
|
|
4505
4209
|
const normalized = code.trim().toUpperCase();
|
|
4506
|
-
const machineId = await
|
|
4210
|
+
const machineId = await getMachineId();
|
|
4211
|
+
validatePromoApiUrl(`${PROMO_API_URL}/redeem`);
|
|
4507
4212
|
let serverExpiresAt;
|
|
4508
4213
|
let serverType;
|
|
4214
|
+
const idempotencyKey = randomUUID();
|
|
4509
4215
|
try {
|
|
4510
4216
|
const res = await fetchWithTimeout(`${PROMO_API_URL}/redeem`, {
|
|
4511
4217
|
method: "POST",
|
|
4512
|
-
headers: {
|
|
4513
|
-
|
|
4218
|
+
headers: {
|
|
4219
|
+
"Content-Type": "application/json",
|
|
4220
|
+
"Idempotency-Key": idempotencyKey
|
|
4221
|
+
},
|
|
4222
|
+
body: JSON.stringify({ code: normalized, machineId, idempotencyKey })
|
|
4514
4223
|
}, 1e4);
|
|
4515
4224
|
if (!res.ok) {
|
|
4516
4225
|
const body = await res.json().catch(() => ({}));
|
|
@@ -4541,7 +4250,7 @@ async function redeemPromoCode(code) {
|
|
|
4541
4250
|
};
|
|
4542
4251
|
let file = { version: 1, redemptions: [] };
|
|
4543
4252
|
try {
|
|
4544
|
-
const raw = await
|
|
4253
|
+
const raw = await readFile2(PROMO_PATH, "utf-8");
|
|
4545
4254
|
file = JSON.parse(raw);
|
|
4546
4255
|
} catch {
|
|
4547
4256
|
}
|
|
@@ -4550,23 +4259,24 @@ async function redeemPromoCode(code) {
|
|
|
4550
4259
|
}
|
|
4551
4260
|
file.redemptions.push(redemption);
|
|
4552
4261
|
const tmpPath = PROMO_PATH + ".tmp";
|
|
4553
|
-
await
|
|
4554
|
-
await
|
|
4262
|
+
await writeFile2(tmpPath, JSON.stringify(file, null, 2), { encoding: "utf-8", mode: 384 });
|
|
4263
|
+
await rename2(tmpPath, PROMO_PATH);
|
|
4555
4264
|
return { success: true, expiresAt: redemption.expiresAt };
|
|
4556
4265
|
}
|
|
4557
4266
|
|
|
4558
4267
|
// src/views/account.tsx
|
|
4559
4268
|
import { Fragment as Fragment5, jsx as jsx30, jsxs as jsxs29 } from "react/jsx-runtime";
|
|
4560
4269
|
function AccountView() {
|
|
4561
|
-
const { status, license, deactivate: deactivate2, degradation } = useLicenseStore();
|
|
4270
|
+
const { status, license, deactivate: deactivate2, revalidate: revalidate2, degradation } = useLicenseStore();
|
|
4562
4271
|
const [confirmDeactivate, setConfirmDeactivate] = useState12(false);
|
|
4563
4272
|
const [deactivating, setDeactivating] = useState12(false);
|
|
4564
4273
|
const [deactivateError, setDeactivateError] = useState12(null);
|
|
4565
4274
|
const [promoMode, setPromoMode] = useState12(false);
|
|
4566
4275
|
const [promoLoading, setPromoLoading] = useState12(false);
|
|
4567
4276
|
const [promoResult, setPromoResult] = useState12(null);
|
|
4568
|
-
|
|
4569
|
-
|
|
4277
|
+
const [revalidating, setRevalidating] = useState12(false);
|
|
4278
|
+
useInput14((input, key) => {
|
|
4279
|
+
if (confirmDeactivate || deactivating || promoMode || revalidating) {
|
|
4570
4280
|
if (key.escape && promoMode) {
|
|
4571
4281
|
setPromoMode(false);
|
|
4572
4282
|
setPromoResult(null);
|
|
@@ -4580,6 +4290,10 @@ function AccountView() {
|
|
|
4580
4290
|
setPromoMode(true);
|
|
4581
4291
|
setPromoResult(null);
|
|
4582
4292
|
}
|
|
4293
|
+
if ((input === "v" || input === "V") && (status === "pro" || status === "team" || status === "expired")) {
|
|
4294
|
+
setRevalidating(true);
|
|
4295
|
+
void revalidate2().finally(() => setRevalidating(false));
|
|
4296
|
+
}
|
|
4583
4297
|
});
|
|
4584
4298
|
const maskKey = (key) => {
|
|
4585
4299
|
if (key.length <= 8) return key;
|
|
@@ -4590,7 +4304,7 @@ function AccountView() {
|
|
|
4590
4304
|
}
|
|
4591
4305
|
return /* @__PURE__ */ jsxs29(Box28, { flexDirection: "column", children: [
|
|
4592
4306
|
/* @__PURE__ */ jsx30(SectionHeader, { emoji: "\u{1F464}", title: t("account_title"), gradient: GRADIENTS.gold }),
|
|
4593
|
-
confirmDeactivate && /* @__PURE__ */ jsx30(Box28, { marginY:
|
|
4307
|
+
confirmDeactivate && /* @__PURE__ */ jsx30(Box28, { marginY: SPACING.xs, children: /* @__PURE__ */ jsx30(
|
|
4594
4308
|
ConfirmDialog,
|
|
4595
4309
|
{
|
|
4596
4310
|
message: t("account_confirmDeactivate"),
|
|
@@ -4609,43 +4323,43 @@ function AccountView() {
|
|
|
4609
4323
|
onCancel: () => setConfirmDeactivate(false)
|
|
4610
4324
|
}
|
|
4611
4325
|
) }),
|
|
4612
|
-
/* @__PURE__ */ jsxs29(Box28, { flexDirection: "column", marginTop:
|
|
4613
|
-
/* @__PURE__ */ jsxs29(Box28, { gap:
|
|
4326
|
+
/* @__PURE__ */ jsxs29(Box28, { flexDirection: "column", marginTop: SPACING.xs, paddingLeft: SPACING.sm, children: [
|
|
4327
|
+
/* @__PURE__ */ jsxs29(Box28, { gap: SPACING.xs, children: [
|
|
4614
4328
|
/* @__PURE__ */ jsx30(Text29, { color: COLORS.muted, children: t("account_statusLabel") }),
|
|
4615
4329
|
status === "pro" && /* @__PURE__ */ jsx30(Text29, { color: COLORS.success, bold: true, children: t("account_pro") }),
|
|
4616
4330
|
status === "free" && /* @__PURE__ */ jsx30(Text29, { color: COLORS.muted, children: t("account_free") }),
|
|
4617
4331
|
status === "expired" && /* @__PURE__ */ jsx30(Text29, { color: COLORS.error, children: t("account_expired") })
|
|
4618
4332
|
] }),
|
|
4619
|
-
(degradation === "warning" || degradation === "limited") && license && /* @__PURE__ */ jsx30(Box28, { marginTop:
|
|
4333
|
+
(degradation === "warning" || degradation === "limited") && license && /* @__PURE__ */ jsx30(Box28, { marginTop: SPACING.xs, borderStyle: "round", borderColor: COLORS.warning, paddingX: SPACING.sm, paddingY: SPACING.none, children: /* @__PURE__ */ jsx30(Text29, { color: COLORS.warning, children: t("license_offlineWarning", {
|
|
4620
4334
|
days: Math.floor((Date.now() - new Date(license.lastValidatedAt).getTime()) / (24 * 60 * 60 * 1e3))
|
|
4621
4335
|
}) }) }),
|
|
4622
4336
|
license && /* @__PURE__ */ jsxs29(Fragment5, { children: [
|
|
4623
|
-
/* @__PURE__ */ jsxs29(Box28, { gap:
|
|
4337
|
+
/* @__PURE__ */ jsxs29(Box28, { gap: SPACING.xs, children: [
|
|
4624
4338
|
/* @__PURE__ */ jsx30(Text29, { color: COLORS.muted, children: t("account_emailLabel") }),
|
|
4625
4339
|
/* @__PURE__ */ jsx30(Text29, { children: license.customerEmail })
|
|
4626
4340
|
] }),
|
|
4627
|
-
/* @__PURE__ */ jsxs29(Box28, { gap:
|
|
4341
|
+
/* @__PURE__ */ jsxs29(Box28, { gap: SPACING.xs, children: [
|
|
4628
4342
|
/* @__PURE__ */ jsx30(Text29, { color: COLORS.muted, children: t("account_nameLabel") }),
|
|
4629
4343
|
/* @__PURE__ */ jsx30(Text29, { children: license.customerName })
|
|
4630
4344
|
] }),
|
|
4631
|
-
/* @__PURE__ */ jsxs29(Box28, { gap:
|
|
4345
|
+
/* @__PURE__ */ jsxs29(Box28, { gap: SPACING.xs, children: [
|
|
4632
4346
|
/* @__PURE__ */ jsx30(Text29, { color: COLORS.muted, children: t("account_planLabel") }),
|
|
4633
4347
|
/* @__PURE__ */ jsx30(Text29, { color: COLORS.success, bold: true, children: "Pro" })
|
|
4634
4348
|
] }),
|
|
4635
|
-
/* @__PURE__ */ jsxs29(Box28, { gap:
|
|
4349
|
+
/* @__PURE__ */ jsxs29(Box28, { gap: SPACING.xs, children: [
|
|
4636
4350
|
/* @__PURE__ */ jsx30(Text29, { color: COLORS.muted, children: t("account_keyLabel") }),
|
|
4637
4351
|
/* @__PURE__ */ jsx30(Text29, { children: maskKey(license.key) })
|
|
4638
4352
|
] }),
|
|
4639
|
-
license.expiresAt && /* @__PURE__ */ jsxs29(Box28, { gap:
|
|
4353
|
+
license.expiresAt && /* @__PURE__ */ jsxs29(Box28, { gap: SPACING.xs, children: [
|
|
4640
4354
|
/* @__PURE__ */ jsx30(Text29, { color: COLORS.muted, children: t("account_expiresLabel") }),
|
|
4641
4355
|
/* @__PURE__ */ jsx30(Text29, { children: formatDate(license.expiresAt) })
|
|
4642
4356
|
] }),
|
|
4643
|
-
/* @__PURE__ */ jsxs29(Box28, { gap:
|
|
4357
|
+
/* @__PURE__ */ jsxs29(Box28, { gap: SPACING.xs, children: [
|
|
4644
4358
|
/* @__PURE__ */ jsx30(Text29, { color: COLORS.muted, children: t("account_activatedLabel") }),
|
|
4645
4359
|
/* @__PURE__ */ jsx30(Text29, { children: formatDate(license.activatedAt) })
|
|
4646
4360
|
] })
|
|
4647
4361
|
] }),
|
|
4648
|
-
status === "free" && /* @__PURE__ */ jsxs29(Box28, { flexDirection: "column", marginTop:
|
|
4362
|
+
status === "free" && /* @__PURE__ */ jsxs29(Box28, { flexDirection: "column", marginTop: SPACING.sm, borderStyle: "round", borderColor: COLORS.brand, paddingX: SPACING.sm, paddingY: SPACING.xs, children: [
|
|
4649
4363
|
/* @__PURE__ */ jsxs29(Text29, { bold: true, color: COLORS.brand, children: [
|
|
4650
4364
|
"\u2B50",
|
|
4651
4365
|
" ",
|
|
@@ -4666,13 +4380,13 @@ function AccountView() {
|
|
|
4666
4380
|
/* @__PURE__ */ jsx30(Text29, { color: COLORS.success, bold: true, children: t("account_activateCmd") })
|
|
4667
4381
|
] })
|
|
4668
4382
|
] }),
|
|
4669
|
-
status === "expired" && /* @__PURE__ */ jsx30(Box28, { marginTop:
|
|
4383
|
+
status === "expired" && /* @__PURE__ */ jsx30(Box28, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsx30(Box28, { borderStyle: "round", borderColor: COLORS.error, paddingX: SPACING.sm, paddingY: SPACING.none, children: /* @__PURE__ */ jsx30(Text29, { color: COLORS.error, children: t("account_licenseExpired") }) }) }),
|
|
4670
4384
|
deactivating && /* @__PURE__ */ jsx30(Text29, { color: COLORS.sky, children: t("account_deactivating") }),
|
|
4671
4385
|
deactivateError && /* @__PURE__ */ jsx30(Text29, { color: COLORS.error, children: deactivateError })
|
|
4672
4386
|
] }),
|
|
4673
|
-
/* @__PURE__ */ jsx30(Box28, { flexDirection: "column", marginTop:
|
|
4387
|
+
/* @__PURE__ */ jsx30(Box28, { flexDirection: "column", marginTop: SPACING.xs, paddingLeft: SPACING.sm, children: promoMode ? /* @__PURE__ */ jsxs29(Box28, { flexDirection: "column", gap: SPACING.xs, children: [
|
|
4674
4388
|
/* @__PURE__ */ jsx30(Text29, { bold: true, color: COLORS.gold, children: t("account_promoTitle") }),
|
|
4675
|
-
promoLoading ? /* @__PURE__ */ jsx30(Text29, { color: COLORS.sky, children: t("account_promoValidating") }) : /* @__PURE__ */ jsxs29(Box28, { gap:
|
|
4389
|
+
promoLoading ? /* @__PURE__ */ jsx30(Text29, { color: COLORS.sky, children: t("account_promoValidating") }) : /* @__PURE__ */ jsxs29(Box28, { gap: SPACING.xs, children: [
|
|
4676
4390
|
/* @__PURE__ */ jsx30(Text29, { color: COLORS.muted, children: t("account_promoLabel") }),
|
|
4677
4391
|
/* @__PURE__ */ jsx30(
|
|
4678
4392
|
TextInput5,
|
|
@@ -4701,24 +4415,26 @@ function AccountView() {
|
|
|
4701
4415
|
promoResult && /* @__PURE__ */ jsx30(ResultBanner, { status: promoResult.success ? "success" : "error", message: promoResult.message }),
|
|
4702
4416
|
/* @__PURE__ */ jsx30(Text29, { color: COLORS.textSecondary, dimColor: true, children: t("account_promoEsc") })
|
|
4703
4417
|
] }) : /* @__PURE__ */ jsx30(Text29, { color: COLORS.textSecondary, children: t("account_promoHint") }) }),
|
|
4704
|
-
/* @__PURE__ */ jsx30(Box28, { marginTop:
|
|
4705
|
-
status === "pro" ? `d ${t("hint_deactivate")}` : "",
|
|
4418
|
+
/* @__PURE__ */ jsx30(Box28, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsxs29(Text29, { color: COLORS.textSecondary, children: [
|
|
4419
|
+
status === "pro" || status === "team" ? `d ${t("hint_deactivate")} ` : "",
|
|
4420
|
+
status === "pro" || status === "team" || status === "expired" ? `v ${t("hint_revalidate")} ` : "",
|
|
4421
|
+
revalidating ? t("account_revalidating") : "",
|
|
4706
4422
|
" ",
|
|
4707
|
-
t("app_version", { version: "0.6.
|
|
4423
|
+
t("app_version", { version: "0.6.2" })
|
|
4708
4424
|
] }) })
|
|
4709
4425
|
] });
|
|
4710
4426
|
}
|
|
4711
4427
|
|
|
4712
4428
|
// src/views/rollback.tsx
|
|
4713
4429
|
import { useCallback as useCallback3, useEffect as useEffect15, useRef as useRef8, useState as useState13 } from "react";
|
|
4714
|
-
import { Box as Box29, Text as Text30, useInput as
|
|
4430
|
+
import { Box as Box29, Text as Text30, useInput as useInput15 } from "ink";
|
|
4715
4431
|
|
|
4716
4432
|
// src/stores/rollback-store.ts
|
|
4717
4433
|
import { create as create12 } from "zustand";
|
|
4718
4434
|
|
|
4719
4435
|
// src/lib/rollback/rollback-engine.ts
|
|
4720
4436
|
import { readdir as readdir2 } from "fs/promises";
|
|
4721
|
-
import { join as
|
|
4437
|
+
import { join as join3 } from "path";
|
|
4722
4438
|
async function detectStrategy(name, targetVersion, packageType) {
|
|
4723
4439
|
if (packageType === "cask") {
|
|
4724
4440
|
return { strategy: "pin-only" };
|
|
@@ -4734,7 +4450,7 @@ async function detectStrategy(name, targetVersion, packageType) {
|
|
|
4734
4450
|
}
|
|
4735
4451
|
try {
|
|
4736
4452
|
const brewCache = (await execBrew(["--cache"])).trim();
|
|
4737
|
-
const downloadsDir =
|
|
4453
|
+
const downloadsDir = join3(brewCache, "downloads");
|
|
4738
4454
|
const entries = await readdir2(downloadsDir);
|
|
4739
4455
|
const found = entries.some(
|
|
4740
4456
|
(entry) => entry.includes(name) && entry.includes(targetVersion)
|
|
@@ -4949,8 +4665,8 @@ function actionPrefix(action) {
|
|
|
4949
4665
|
}
|
|
4950
4666
|
function PlanView({ plan }) {
|
|
4951
4667
|
const executableCount = plan.actions.filter((a) => a.strategy !== "unavailable" && a.action !== "remove").length;
|
|
4952
|
-
return /* @__PURE__ */ jsxs30(Box29, { flexDirection: "column", marginTop:
|
|
4953
|
-
/* @__PURE__ */ jsxs30(Box29, { marginBottom:
|
|
4668
|
+
return /* @__PURE__ */ jsxs30(Box29, { flexDirection: "column", marginTop: SPACING.xs, children: [
|
|
4669
|
+
/* @__PURE__ */ jsxs30(Box29, { marginBottom: SPACING.xs, children: [
|
|
4954
4670
|
/* @__PURE__ */ jsxs30(Text30, { color: COLORS.text, bold: true, children: [
|
|
4955
4671
|
plan.snapshotLabel,
|
|
4956
4672
|
" "
|
|
@@ -4981,11 +4697,11 @@ function PlanView({ plan }) {
|
|
|
4981
4697
|
"]"
|
|
4982
4698
|
] })
|
|
4983
4699
|
] }, a.packageName + a.action)),
|
|
4984
|
-
plan.warnings.map((w) => /* @__PURE__ */ jsx31(Box29, { marginTop:
|
|
4700
|
+
plan.warnings.map((w) => /* @__PURE__ */ jsx31(Box29, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsxs30(Text30, { color: COLORS.warning, children: [
|
|
4985
4701
|
"\u26A0 ",
|
|
4986
4702
|
w
|
|
4987
4703
|
] }) }, w)),
|
|
4988
|
-
/* @__PURE__ */ jsx31(Box29, { marginTop:
|
|
4704
|
+
/* @__PURE__ */ jsx31(Box29, { marginTop: SPACING.xs, children: plan.canExecute ? /* @__PURE__ */ jsxs30(Text30, { color: COLORS.textSecondary, children: [
|
|
4989
4705
|
"enter:",
|
|
4990
4706
|
t("rollback_confirm", { count: String(executableCount) }),
|
|
4991
4707
|
" esc:",
|
|
@@ -5041,7 +4757,7 @@ function RollbackView() {
|
|
|
5041
4757
|
}
|
|
5042
4758
|
}
|
|
5043
4759
|
}, [isPro]);
|
|
5044
|
-
|
|
4760
|
+
useInput15((input, key) => {
|
|
5045
4761
|
if (phase === "executing") return;
|
|
5046
4762
|
if (phase === "result") {
|
|
5047
4763
|
if (key.escape || input === "r") {
|
|
@@ -5080,7 +4796,7 @@ function RollbackView() {
|
|
|
5080
4796
|
return /* @__PURE__ */ jsx31(Box29, { flexDirection: "column", children: /* @__PURE__ */ jsx31(ProgressLog, { lines: streamLines, isRunning: streamRunning, title: t("rollback_executing") }) });
|
|
5081
4797
|
}
|
|
5082
4798
|
if (phase === "result") {
|
|
5083
|
-
return /* @__PURE__ */ jsxs30(Box29, { flexDirection: "column", marginTop:
|
|
4799
|
+
return /* @__PURE__ */ jsxs30(Box29, { flexDirection: "column", marginTop: SPACING.xs, children: [
|
|
5084
4800
|
/* @__PURE__ */ jsx31(
|
|
5085
4801
|
ResultBanner,
|
|
5086
4802
|
{
|
|
@@ -5088,7 +4804,7 @@ function RollbackView() {
|
|
|
5088
4804
|
message: streamError ? t("rollback_error", { error: streamError }) : t("rollback_success")
|
|
5089
4805
|
}
|
|
5090
4806
|
),
|
|
5091
|
-
/* @__PURE__ */ jsx31(Box29, { marginTop:
|
|
4807
|
+
/* @__PURE__ */ jsx31(Box29, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsxs30(Text30, { color: COLORS.textSecondary, children: [
|
|
5092
4808
|
"r:",
|
|
5093
4809
|
t("hint_refresh"),
|
|
5094
4810
|
" esc:",
|
|
@@ -5098,10 +4814,10 @@ function RollbackView() {
|
|
|
5098
4814
|
}
|
|
5099
4815
|
return /* @__PURE__ */ jsxs30(Box29, { flexDirection: "column", children: [
|
|
5100
4816
|
/* @__PURE__ */ jsx31(SectionHeader, { emoji: "\u23EA", title: t("rollback_title"), gradient: GRADIENTS.gold }),
|
|
5101
|
-
snapshots.length === 0 && /* @__PURE__ */ jsx31(Box29, { marginTop:
|
|
5102
|
-
phase === "list" && snapshots.length > 0 && /* @__PURE__ */ jsxs30(Box29, { flexDirection: "column", marginTop:
|
|
4817
|
+
snapshots.length === 0 && /* @__PURE__ */ jsx31(Box29, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsx31(ResultBanner, { status: "info", message: t("rollback_no_snapshots") }) }),
|
|
4818
|
+
phase === "list" && snapshots.length > 0 && /* @__PURE__ */ jsxs30(Box29, { flexDirection: "column", marginTop: SPACING.xs, children: [
|
|
5103
4819
|
/* @__PURE__ */ jsx31(Text30, { color: COLORS.textSecondary, dimColor: true, children: t("rollback_select_snapshot") }),
|
|
5104
|
-
/* @__PURE__ */ jsx31(Box29, { flexDirection: "column", marginTop:
|
|
4820
|
+
/* @__PURE__ */ jsx31(Box29, { flexDirection: "column", marginTop: SPACING.xs, children: snapshots.map((s, i) => /* @__PURE__ */ jsxs30(SelectableRow, { isCurrent: i === cursor, children: [
|
|
5105
4821
|
/* @__PURE__ */ jsx31(Text30, { bold: i === cursor, color: i === cursor ? COLORS.text : COLORS.muted, children: s.label ?? t("rollback_snapshot_auto") }),
|
|
5106
4822
|
/* @__PURE__ */ jsxs30(Text30, { color: COLORS.textSecondary, children: [
|
|
5107
4823
|
" \u2014 ",
|
|
@@ -5120,7 +4836,7 @@ function RollbackView() {
|
|
|
5120
4836
|
planError && /* @__PURE__ */ jsx31(ErrorMessage, { message: planError }),
|
|
5121
4837
|
plan && !planLoading && /* @__PURE__ */ jsx31(PlanView, { plan })
|
|
5122
4838
|
] }),
|
|
5123
|
-
phase === "confirm" && plan && /* @__PURE__ */ jsx31(Box29, { marginTop:
|
|
4839
|
+
phase === "confirm" && plan && /* @__PURE__ */ jsx31(Box29, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsx31(
|
|
5124
4840
|
ConfirmDialog,
|
|
5125
4841
|
{
|
|
5126
4842
|
message: t("rollback_confirm", {
|
|
@@ -5135,7 +4851,7 @@ function RollbackView() {
|
|
|
5135
4851
|
|
|
5136
4852
|
// src/views/brewfile.tsx
|
|
5137
4853
|
import { useCallback as useCallback4, useEffect as useEffect16, useRef as useRef9, useState as useState14 } from "react";
|
|
5138
|
-
import { Box as Box30, Text as Text31, useInput as
|
|
4854
|
+
import { Box as Box30, Text as Text31, useInput as useInput16 } from "ink";
|
|
5139
4855
|
import { TextInput as TextInput6 } from "@inkjs/ui";
|
|
5140
4856
|
import { jsx as jsx32, jsxs as jsxs31 } from "react/jsx-runtime";
|
|
5141
4857
|
function DriftScore({ score }) {
|
|
@@ -5157,7 +4873,7 @@ function DriftScore({ score }) {
|
|
|
5157
4873
|
] });
|
|
5158
4874
|
}
|
|
5159
4875
|
function DriftSummary({ drift }) {
|
|
5160
|
-
return /* @__PURE__ */ jsxs31(Box30, { flexDirection: "column", marginTop:
|
|
4876
|
+
return /* @__PURE__ */ jsxs31(Box30, { flexDirection: "column", marginTop: SPACING.xs, children: [
|
|
5161
4877
|
drift.missingPackages.length > 0 && /* @__PURE__ */ jsxs31(Box30, { children: [
|
|
5162
4878
|
/* @__PURE__ */ jsx32(Text31, { color: COLORS.error, children: "\u25CF " }),
|
|
5163
4879
|
/* @__PURE__ */ jsx32(Text31, { color: COLORS.error, children: t("brewfile_drift_missing", { count: drift.missingPackages.length }) }),
|
|
@@ -5174,7 +4890,7 @@ function DriftSummary({ drift }) {
|
|
|
5174
4890
|
/* @__PURE__ */ jsx32(Text31, { color: COLORS.info, children: "\u25CF " }),
|
|
5175
4891
|
/* @__PURE__ */ jsx32(Text31, { color: COLORS.info, children: t("brewfile_drift_wrong", { count: drift.wrongVersions.length }) })
|
|
5176
4892
|
] }),
|
|
5177
|
-
drift.missingPackages.length === 0 && drift.extraPackages.length === 0 && drift.wrongVersions.length === 0 && /* @__PURE__ */ jsx32(ResultBanner, { status: "success", message: "
|
|
4893
|
+
drift.missingPackages.length === 0 && drift.extraPackages.length === 0 && drift.wrongVersions.length === 0 && /* @__PURE__ */ jsx32(ResultBanner, { status: "success", message: t("brewfile_in_sync") })
|
|
5178
4894
|
] });
|
|
5179
4895
|
}
|
|
5180
4896
|
function BrewfileView() {
|
|
@@ -5225,8 +4941,9 @@ function BrewfileView() {
|
|
|
5225
4941
|
}
|
|
5226
4942
|
}
|
|
5227
4943
|
}, [schema, isPro]);
|
|
5228
|
-
|
|
4944
|
+
useInput16((input, key) => {
|
|
5229
4945
|
if (phase === "reconciling") return;
|
|
4946
|
+
if (phase === "confirming-reconcile") return;
|
|
5230
4947
|
if (phase === "result") {
|
|
5231
4948
|
if (key.escape || input === "r") {
|
|
5232
4949
|
setPhase("overview");
|
|
@@ -5246,7 +4963,7 @@ function BrewfileView() {
|
|
|
5246
4963
|
if (input === "c") {
|
|
5247
4964
|
const needsReconcile = drift && (drift.missingPackages.length > 0 || drift.wrongVersions.length > 0);
|
|
5248
4965
|
if (needsReconcile) {
|
|
5249
|
-
|
|
4966
|
+
setPhase("confirming-reconcile");
|
|
5250
4967
|
}
|
|
5251
4968
|
return;
|
|
5252
4969
|
}
|
|
@@ -5255,10 +4972,27 @@ function BrewfileView() {
|
|
|
5255
4972
|
});
|
|
5256
4973
|
if (loading) return /* @__PURE__ */ jsx32(Loading, { message: t("loading_default") });
|
|
5257
4974
|
if (error) return /* @__PURE__ */ jsx32(ErrorMessage, { message: error });
|
|
4975
|
+
if (phase === "confirming-reconcile" && drift) {
|
|
4976
|
+
return /* @__PURE__ */ jsx32(
|
|
4977
|
+
ConfirmDialog,
|
|
4978
|
+
{
|
|
4979
|
+
message: t("confirm_brewfile_reconcile", {
|
|
4980
|
+
missing: String(drift.missingPackages.length),
|
|
4981
|
+
wrongVer: String(drift.wrongVersions.length)
|
|
4982
|
+
}),
|
|
4983
|
+
onConfirm: () => {
|
|
4984
|
+
void startReconcile();
|
|
4985
|
+
},
|
|
4986
|
+
onCancel: () => {
|
|
4987
|
+
setPhase("overview");
|
|
4988
|
+
}
|
|
4989
|
+
}
|
|
4990
|
+
);
|
|
4991
|
+
}
|
|
5258
4992
|
if (phase === "creating") {
|
|
5259
|
-
return /* @__PURE__ */ jsxs31(Box30, { flexDirection: "column", marginTop:
|
|
4993
|
+
return /* @__PURE__ */ jsxs31(Box30, { flexDirection: "column", marginTop: SPACING.xs, children: [
|
|
5260
4994
|
/* @__PURE__ */ jsx32(SectionHeader, { emoji: "\u{1F4E6}", title: t("brewfile_title"), gradient: GRADIENTS.ocean }),
|
|
5261
|
-
/* @__PURE__ */ jsxs31(Box30, { marginTop:
|
|
4995
|
+
/* @__PURE__ */ jsxs31(Box30, { marginTop: SPACING.xs, children: [
|
|
5262
4996
|
/* @__PURE__ */ jsxs31(Text31, { color: COLORS.textSecondary, children: [
|
|
5263
4997
|
t("brewfile_create_name"),
|
|
5264
4998
|
" "
|
|
@@ -5289,7 +5023,7 @@ function BrewfileView() {
|
|
|
5289
5023
|
) });
|
|
5290
5024
|
}
|
|
5291
5025
|
if (phase === "result") {
|
|
5292
|
-
return /* @__PURE__ */ jsxs31(Box30, { flexDirection: "column", marginTop:
|
|
5026
|
+
return /* @__PURE__ */ jsxs31(Box30, { flexDirection: "column", marginTop: SPACING.xs, children: [
|
|
5293
5027
|
/* @__PURE__ */ jsx32(
|
|
5294
5028
|
ResultBanner,
|
|
5295
5029
|
{
|
|
@@ -5297,7 +5031,7 @@ function BrewfileView() {
|
|
|
5297
5031
|
message: resultMessage
|
|
5298
5032
|
}
|
|
5299
5033
|
),
|
|
5300
|
-
/* @__PURE__ */ jsx32(Box30, { marginTop:
|
|
5034
|
+
/* @__PURE__ */ jsx32(Box30, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsxs31(Text31, { color: COLORS.textSecondary, children: [
|
|
5301
5035
|
"r:",
|
|
5302
5036
|
t("hint_refresh"),
|
|
5303
5037
|
" esc:",
|
|
@@ -5307,14 +5041,14 @@ function BrewfileView() {
|
|
|
5307
5041
|
}
|
|
5308
5042
|
return /* @__PURE__ */ jsxs31(Box30, { flexDirection: "column", children: [
|
|
5309
5043
|
/* @__PURE__ */ jsx32(SectionHeader, { emoji: "\u{1F4E6}", title: t("brewfile_title"), gradient: GRADIENTS.ocean }),
|
|
5310
|
-
schema === null ? /* @__PURE__ */ jsxs31(Box30, { marginTop:
|
|
5044
|
+
schema === null ? /* @__PURE__ */ jsxs31(Box30, { marginTop: SPACING.xs, flexDirection: "column", children: [
|
|
5311
5045
|
/* @__PURE__ */ jsx32(ResultBanner, { status: "info", message: t("brewfile_no_brewfile") }),
|
|
5312
|
-
/* @__PURE__ */ jsx32(Box30, { marginTop:
|
|
5046
|
+
/* @__PURE__ */ jsx32(Box30, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsxs31(Text31, { color: COLORS.textSecondary, children: [
|
|
5313
5047
|
"n:",
|
|
5314
5048
|
t("hint_new")
|
|
5315
5049
|
] }) })
|
|
5316
|
-
] }) : /* @__PURE__ */ jsxs31(Box30, { flexDirection: "column", marginTop:
|
|
5317
|
-
/* @__PURE__ */ jsxs31(Box30, { gap:
|
|
5050
|
+
] }) : /* @__PURE__ */ jsxs31(Box30, { flexDirection: "column", marginTop: SPACING.xs, children: [
|
|
5051
|
+
/* @__PURE__ */ jsxs31(Box30, { gap: SPACING.sm, children: [
|
|
5318
5052
|
/* @__PURE__ */ jsx32(Text31, { color: COLORS.text, bold: true, children: schema.meta.name }),
|
|
5319
5053
|
schema.meta.description && /* @__PURE__ */ jsx32(Text31, { color: COLORS.textSecondary, children: schema.meta.description }),
|
|
5320
5054
|
schema.strictMode && /* @__PURE__ */ jsxs31(Text31, { color: COLORS.warning, children: [
|
|
@@ -5323,16 +5057,16 @@ function BrewfileView() {
|
|
|
5323
5057
|
"]"
|
|
5324
5058
|
] })
|
|
5325
5059
|
] }),
|
|
5326
|
-
/* @__PURE__ */ jsxs31(Box30, { gap:
|
|
5060
|
+
/* @__PURE__ */ jsxs31(Box30, { gap: SPACING.md, marginTop: SPACING.xs, children: [
|
|
5327
5061
|
/* @__PURE__ */ jsx32(Text31, { color: COLORS.sky, children: t("brewfile_formulae_count", { count: schema.formulae.length }) }),
|
|
5328
5062
|
/* @__PURE__ */ jsx32(Text31, { color: COLORS.teal, children: t("brewfile_casks_count", { count: schema.casks.length }) })
|
|
5329
5063
|
] }),
|
|
5330
|
-
driftLoading && /* @__PURE__ */ jsx32(Box30, { marginTop:
|
|
5331
|
-
drift && !driftLoading && /* @__PURE__ */ jsxs31(Box30, { flexDirection: "column", marginTop:
|
|
5064
|
+
driftLoading && /* @__PURE__ */ jsx32(Box30, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsx32(Text31, { color: COLORS.muted, children: t("brewfile_computing_drift") }) }),
|
|
5065
|
+
drift && !driftLoading && /* @__PURE__ */ jsxs31(Box30, { flexDirection: "column", marginTop: SPACING.xs, children: [
|
|
5332
5066
|
/* @__PURE__ */ jsx32(DriftScore, { score: drift.score }),
|
|
5333
5067
|
/* @__PURE__ */ jsx32(DriftSummary, { drift })
|
|
5334
5068
|
] }),
|
|
5335
|
-
/* @__PURE__ */ jsx32(Box30, { marginTop:
|
|
5069
|
+
/* @__PURE__ */ jsx32(Box30, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsxs31(Text31, { color: COLORS.textSecondary, children: [
|
|
5336
5070
|
"r:",
|
|
5337
5071
|
t("hint_refresh"),
|
|
5338
5072
|
drift && (drift.missingPackages.length > 0 || drift.wrongVersions.length > 0) ? ` c:${t("hint_reconcile")}` : "",
|
|
@@ -5346,7 +5080,7 @@ function BrewfileView() {
|
|
|
5346
5080
|
|
|
5347
5081
|
// src/views/sync.tsx
|
|
5348
5082
|
import { useCallback as useCallback5, useEffect as useEffect17, useState as useState15 } from "react";
|
|
5349
|
-
import { Box as Box31, Text as Text32, useInput as
|
|
5083
|
+
import { Box as Box31, Text as Text32, useInput as useInput17 } from "ink";
|
|
5350
5084
|
import { Fragment as Fragment6, jsx as jsx33, jsxs as jsxs32 } from "react/jsx-runtime";
|
|
5351
5085
|
function OverviewSection({
|
|
5352
5086
|
config,
|
|
@@ -5357,10 +5091,10 @@ function OverviewSection({
|
|
|
5357
5091
|
}) {
|
|
5358
5092
|
const hasConflicts = conflicts.length > 0;
|
|
5359
5093
|
const showComplianceHint = !hasConflicts && !!lastResult?.success;
|
|
5360
|
-
return /* @__PURE__ */ jsxs32(Box31, { flexDirection: "column", marginTop:
|
|
5094
|
+
return /* @__PURE__ */ jsxs32(Box31, { flexDirection: "column", marginTop: SPACING.xs, children: [
|
|
5361
5095
|
config ? /* @__PURE__ */ jsxs32(Fragment6, { children: [
|
|
5362
|
-
/* @__PURE__ */ jsx33(Box31, { marginBottom:
|
|
5363
|
-
config.lastSync && /* @__PURE__ */ jsx33(Box31, { marginBottom:
|
|
5096
|
+
/* @__PURE__ */ jsx33(Box31, { marginBottom: SPACING.xs, children: /* @__PURE__ */ jsx33(Text32, { color: COLORS.textSecondary, children: t("sync_machine", { name: config.machineName }) }) }),
|
|
5097
|
+
config.lastSync && /* @__PURE__ */ jsx33(Box31, { marginBottom: SPACING.xs, children: /* @__PURE__ */ jsx33(Text32, { color: COLORS.textSecondary, children: t("sync_last_sync", { date: new Date(config.lastSync).toLocaleString() }) }) }),
|
|
5364
5098
|
hasConflicts ? /* @__PURE__ */ jsx33(
|
|
5365
5099
|
ResultBanner,
|
|
5366
5100
|
{
|
|
@@ -5368,8 +5102,8 @@ function OverviewSection({
|
|
|
5368
5102
|
message: t("sync_status_conflict", { count: String(conflicts.length) })
|
|
5369
5103
|
}
|
|
5370
5104
|
) : lastResult?.success ? /* @__PURE__ */ jsx33(ResultBanner, { status: "success", message: t("sync_status_ok") }) : null
|
|
5371
|
-
] }) : /* @__PURE__ */ jsx33(Box31, { marginBottom:
|
|
5372
|
-
/* @__PURE__ */ jsx33(Box31, { marginTop:
|
|
5105
|
+
] }) : /* @__PURE__ */ jsx33(Box31, { marginBottom: SPACING.xs, children: /* @__PURE__ */ jsx33(Text32, { color: COLORS.textSecondary, children: t("sync_disabled") }) }),
|
|
5106
|
+
/* @__PURE__ */ jsx33(Box31, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsxs32(Text32, { color: COLORS.textSecondary, children: [
|
|
5373
5107
|
"s",
|
|
5374
5108
|
/* @__PURE__ */ jsxs32(Text32, { color: COLORS.gold, children: [
|
|
5375
5109
|
":",
|
|
@@ -5396,11 +5130,11 @@ function ConflictsList({
|
|
|
5396
5130
|
entries,
|
|
5397
5131
|
cursor
|
|
5398
5132
|
}) {
|
|
5399
|
-
return /* @__PURE__ */ jsxs32(Box31, { flexDirection: "column", marginTop:
|
|
5133
|
+
return /* @__PURE__ */ jsxs32(Box31, { flexDirection: "column", marginTop: SPACING.xs, children: [
|
|
5400
5134
|
entries.map((entry, i) => {
|
|
5401
5135
|
const { conflict, resolution } = entry;
|
|
5402
5136
|
const isActive = i === cursor;
|
|
5403
|
-
return /* @__PURE__ */ jsxs32(Box31, { flexDirection: "column", marginBottom:
|
|
5137
|
+
return /* @__PURE__ */ jsxs32(Box31, { flexDirection: "column", marginBottom: SPACING.xs, children: [
|
|
5404
5138
|
/* @__PURE__ */ jsxs32(SelectableRow, { isCurrent: isActive, children: [
|
|
5405
5139
|
/* @__PURE__ */ jsx33(Text32, { bold: true, color: isActive ? COLORS.text : COLORS.textSecondary, children: t("sync_conflict_title", { package: conflict.packageName }) }),
|
|
5406
5140
|
/* @__PURE__ */ jsxs32(Text32, { color: COLORS.muted, children: [
|
|
@@ -5409,7 +5143,7 @@ function ConflictsList({
|
|
|
5409
5143
|
")"
|
|
5410
5144
|
] })
|
|
5411
5145
|
] }),
|
|
5412
|
-
/* @__PURE__ */ jsxs32(Box31, { marginLeft:
|
|
5146
|
+
/* @__PURE__ */ jsxs32(Box31, { marginLeft: SPACING.sm, flexDirection: "column", children: [
|
|
5413
5147
|
/* @__PURE__ */ jsxs32(
|
|
5414
5148
|
Text32,
|
|
5415
5149
|
{
|
|
@@ -5435,12 +5169,17 @@ function ConflictsList({
|
|
|
5435
5169
|
] })
|
|
5436
5170
|
] }, `${conflict.packageName}-${conflict.remoteMachine}`);
|
|
5437
5171
|
}),
|
|
5438
|
-
/* @__PURE__ */ jsx33(Box31, { marginTop:
|
|
5439
|
-
"j/k:
|
|
5172
|
+
/* @__PURE__ */ jsx33(Box31, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsxs32(Text32, { color: COLORS.textSecondary, children: [
|
|
5173
|
+
"j/k:",
|
|
5174
|
+
t("hint_navigate"),
|
|
5175
|
+
" l:",
|
|
5440
5176
|
t("sync_conflict_use_local"),
|
|
5441
5177
|
" r:",
|
|
5442
5178
|
t("sync_conflict_use_remote"),
|
|
5443
|
-
" enter:
|
|
5179
|
+
" enter:",
|
|
5180
|
+
t("hint_apply"),
|
|
5181
|
+
" esc:",
|
|
5182
|
+
t("hint_back")
|
|
5444
5183
|
] }) })
|
|
5445
5184
|
] });
|
|
5446
5185
|
}
|
|
@@ -5486,8 +5225,9 @@ function SyncView() {
|
|
|
5486
5225
|
await resolveConflicts(resolutions);
|
|
5487
5226
|
setPhase("result");
|
|
5488
5227
|
}, [conflictEntries, resolveConflicts]);
|
|
5489
|
-
|
|
5228
|
+
useInput17((input, key) => {
|
|
5490
5229
|
if (phase === "syncing") return;
|
|
5230
|
+
if (phase === "confirming-sync" || phase === "confirming-apply") return;
|
|
5491
5231
|
if (phase === "result") {
|
|
5492
5232
|
if (key.escape || input === "r") {
|
|
5493
5233
|
setPhase("overview");
|
|
@@ -5522,12 +5262,13 @@ function SyncView() {
|
|
|
5522
5262
|
return;
|
|
5523
5263
|
}
|
|
5524
5264
|
if (key.return) {
|
|
5525
|
-
|
|
5265
|
+
const pending = conflictEntries.filter((e) => e.resolution === "pending");
|
|
5266
|
+
if (pending.length === 0) setPhase("confirming-apply");
|
|
5526
5267
|
return;
|
|
5527
5268
|
}
|
|
5528
5269
|
}
|
|
5529
5270
|
if (input === "s") {
|
|
5530
|
-
|
|
5271
|
+
setPhase("confirming-sync");
|
|
5531
5272
|
return;
|
|
5532
5273
|
}
|
|
5533
5274
|
if (input === "c" && conflicts.length > 0) {
|
|
@@ -5544,21 +5285,49 @@ function SyncView() {
|
|
|
5544
5285
|
return;
|
|
5545
5286
|
}
|
|
5546
5287
|
});
|
|
5288
|
+
if (phase === "confirming-sync") {
|
|
5289
|
+
return /* @__PURE__ */ jsx33(
|
|
5290
|
+
ConfirmDialog,
|
|
5291
|
+
{
|
|
5292
|
+
message: t("confirm_sync_now"),
|
|
5293
|
+
onConfirm: () => {
|
|
5294
|
+
void handleSyncNow();
|
|
5295
|
+
},
|
|
5296
|
+
onCancel: () => {
|
|
5297
|
+
setPhase("overview");
|
|
5298
|
+
}
|
|
5299
|
+
}
|
|
5300
|
+
);
|
|
5301
|
+
}
|
|
5302
|
+
if (phase === "confirming-apply") {
|
|
5303
|
+
return /* @__PURE__ */ jsx33(
|
|
5304
|
+
ConfirmDialog,
|
|
5305
|
+
{
|
|
5306
|
+
message: t("confirm_sync_apply", { count: String(conflictEntries.length) }),
|
|
5307
|
+
onConfirm: () => {
|
|
5308
|
+
void handleApplyResolutions();
|
|
5309
|
+
},
|
|
5310
|
+
onCancel: () => {
|
|
5311
|
+
setPhase("conflicts");
|
|
5312
|
+
}
|
|
5313
|
+
}
|
|
5314
|
+
);
|
|
5315
|
+
}
|
|
5547
5316
|
if (phase === "syncing" || loading) {
|
|
5548
5317
|
return /* @__PURE__ */ jsx33(Loading, { message: t("sync_syncing") });
|
|
5549
5318
|
}
|
|
5550
5319
|
if (phase === "result") {
|
|
5551
5320
|
const isError = !!(syncError ?? error);
|
|
5552
|
-
return /* @__PURE__ */ jsxs32(Box31, { flexDirection: "column", marginTop:
|
|
5321
|
+
return /* @__PURE__ */ jsxs32(Box31, { flexDirection: "column", marginTop: SPACING.xs, children: [
|
|
5553
5322
|
/* @__PURE__ */ jsx33(SectionHeader, { emoji: "\u{1F504}", title: t("sync_title"), gradient: GRADIENTS.gold }),
|
|
5554
|
-
/* @__PURE__ */ jsx33(Box31, { marginTop:
|
|
5323
|
+
/* @__PURE__ */ jsx33(Box31, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsx33(
|
|
5555
5324
|
ResultBanner,
|
|
5556
5325
|
{
|
|
5557
5326
|
status: isError ? "error" : "success",
|
|
5558
5327
|
message: isError ? t("sync_error", { error: syncError ?? error ?? "" }) : t("sync_success")
|
|
5559
5328
|
}
|
|
5560
5329
|
) }),
|
|
5561
|
-
/* @__PURE__ */ jsx33(Box31, { marginTop:
|
|
5330
|
+
/* @__PURE__ */ jsx33(Box31, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsxs32(Text32, { color: COLORS.textSecondary, children: [
|
|
5562
5331
|
"r:",
|
|
5563
5332
|
t("hint_refresh"),
|
|
5564
5333
|
" esc:",
|
|
@@ -5568,7 +5337,7 @@ function SyncView() {
|
|
|
5568
5337
|
}
|
|
5569
5338
|
return /* @__PURE__ */ jsxs32(Box31, { flexDirection: "column", children: [
|
|
5570
5339
|
/* @__PURE__ */ jsx33(SectionHeader, { emoji: "\u{1F504}", title: t("sync_title"), gradient: GRADIENTS.gold }),
|
|
5571
|
-
error && phase === "overview" && /* @__PURE__ */ jsx33(Box31, { marginTop:
|
|
5340
|
+
error && phase === "overview" && /* @__PURE__ */ jsx33(Box31, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsx33(ResultBanner, { status: "error", message: t("sync_error", { error }) }) }),
|
|
5572
5341
|
phase === "overview" && /* @__PURE__ */ jsx33(
|
|
5573
5342
|
OverviewSection,
|
|
5574
5343
|
{
|
|
@@ -5591,7 +5360,7 @@ function SyncView() {
|
|
|
5591
5360
|
|
|
5592
5361
|
// src/views/compliance.tsx
|
|
5593
5362
|
import { useCallback as useCallback6, useEffect as useEffect18, useRef as useRef10, useState as useState16 } from "react";
|
|
5594
|
-
import { Box as Box32, Text as Text33, useInput as
|
|
5363
|
+
import { Box as Box32, Text as Text33, useInput as useInput18 } from "ink";
|
|
5595
5364
|
import { TextInput as TextInput7 } from "@inkjs/ui";
|
|
5596
5365
|
|
|
5597
5366
|
// src/lib/compliance/compliance-remediator.ts
|
|
@@ -5635,12 +5404,12 @@ async function* remediateViolations(violations, isPro) {
|
|
|
5635
5404
|
}
|
|
5636
5405
|
|
|
5637
5406
|
// src/views/compliance.tsx
|
|
5638
|
-
import { join as
|
|
5407
|
+
import { join as join4 } from "path";
|
|
5639
5408
|
import { jsx as jsx34, jsxs as jsxs33 } from "react/jsx-runtime";
|
|
5640
5409
|
function ComplianceScore({ report }) {
|
|
5641
5410
|
const color = report.score >= 80 ? COLORS.success : report.score >= 50 ? COLORS.warning : COLORS.error;
|
|
5642
5411
|
const bars = Math.round(report.score / 10);
|
|
5643
|
-
return /* @__PURE__ */ jsxs33(Box32, { flexDirection: "column", marginBottom:
|
|
5412
|
+
return /* @__PURE__ */ jsxs33(Box32, { flexDirection: "column", marginBottom: SPACING.xs, children: [
|
|
5644
5413
|
/* @__PURE__ */ jsxs33(Box32, { children: [
|
|
5645
5414
|
/* @__PURE__ */ jsxs33(Text33, { color, children: [
|
|
5646
5415
|
"\u2593".repeat(bars),
|
|
@@ -5666,7 +5435,7 @@ function ComplianceScore({ report }) {
|
|
|
5666
5435
|
function ViolationItem({ violation }) {
|
|
5667
5436
|
const color = violation.severity === "error" ? COLORS.error : COLORS.warning;
|
|
5668
5437
|
const prefix = violation.severity === "error" ? "\u2717" : "\u26A0";
|
|
5669
|
-
return /* @__PURE__ */ jsxs33(Box32, { marginBottom:
|
|
5438
|
+
return /* @__PURE__ */ jsxs33(Box32, { marginBottom: SPACING.none, children: [
|
|
5670
5439
|
/* @__PURE__ */ jsxs33(Text33, { color, children: [
|
|
5671
5440
|
prefix,
|
|
5672
5441
|
" "
|
|
@@ -5677,8 +5446,8 @@ function ViolationItem({ violation }) {
|
|
|
5677
5446
|
function ViolationList({ violations }) {
|
|
5678
5447
|
const errors = violations.filter((v) => v.severity === "error");
|
|
5679
5448
|
const warnings = violations.filter((v) => v.severity === "warning");
|
|
5680
|
-
return /* @__PURE__ */ jsxs33(Box32, { flexDirection: "column", marginTop:
|
|
5681
|
-
errors.length > 0 && /* @__PURE__ */ jsxs33(Box32, { flexDirection: "column", marginBottom:
|
|
5449
|
+
return /* @__PURE__ */ jsxs33(Box32, { flexDirection: "column", marginTop: SPACING.xs, children: [
|
|
5450
|
+
errors.length > 0 && /* @__PURE__ */ jsxs33(Box32, { flexDirection: "column", marginBottom: SPACING.xs, children: [
|
|
5682
5451
|
/* @__PURE__ */ jsxs33(Text33, { color: COLORS.error, bold: true, children: [
|
|
5683
5452
|
t("compliance_violations", { count: String(errors.length) }),
|
|
5684
5453
|
" (errors)"
|
|
@@ -5731,7 +5500,7 @@ function ComplianceView() {
|
|
|
5731
5500
|
const handleExport = useCallback6(async () => {
|
|
5732
5501
|
if (!report) return;
|
|
5733
5502
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
5734
|
-
const outputPath =
|
|
5503
|
+
const outputPath = join4(DATA_DIR, `compliance-report-${timestamp}.json`);
|
|
5735
5504
|
try {
|
|
5736
5505
|
await exportReport(report, outputPath);
|
|
5737
5506
|
setResultMessage({ ok: true, text: t("compliance_export_done", { path: outputPath }) });
|
|
@@ -5779,8 +5548,9 @@ function ComplianceView() {
|
|
|
5779
5548
|
}
|
|
5780
5549
|
}
|
|
5781
5550
|
}, [report, isPro, runCheck]);
|
|
5782
|
-
|
|
5551
|
+
useInput18((input, key) => {
|
|
5783
5552
|
if (phase === "remediating" || phase === "importing") return;
|
|
5553
|
+
if (phase === "confirming-remediate") return;
|
|
5784
5554
|
if (phase === "result") {
|
|
5785
5555
|
if (key.escape || input === "r") {
|
|
5786
5556
|
setPhase("overview");
|
|
@@ -5805,31 +5575,48 @@ function ComplianceView() {
|
|
|
5805
5575
|
(v) => v.type === "missing" || v.type === "wrong-version"
|
|
5806
5576
|
);
|
|
5807
5577
|
if (actionable.length > 0) {
|
|
5808
|
-
|
|
5578
|
+
setPhase("confirming-remediate");
|
|
5809
5579
|
}
|
|
5810
5580
|
return;
|
|
5811
5581
|
}
|
|
5812
5582
|
});
|
|
5583
|
+
if (phase === "confirming-remediate" && report) {
|
|
5584
|
+
const actionable = report.violations.filter(
|
|
5585
|
+
(v) => v.type === "missing" || v.type === "wrong-version"
|
|
5586
|
+
);
|
|
5587
|
+
return /* @__PURE__ */ jsx34(
|
|
5588
|
+
ConfirmDialog,
|
|
5589
|
+
{
|
|
5590
|
+
message: t("confirm_compliance_remediate", { count: String(actionable.length) }),
|
|
5591
|
+
onConfirm: () => {
|
|
5592
|
+
void handleRemediate();
|
|
5593
|
+
},
|
|
5594
|
+
onCancel: () => {
|
|
5595
|
+
setPhase("overview");
|
|
5596
|
+
}
|
|
5597
|
+
}
|
|
5598
|
+
);
|
|
5599
|
+
}
|
|
5813
5600
|
if (phase === "remediating" || loading && phase !== "importing") {
|
|
5814
5601
|
if (phase === "remediating") {
|
|
5815
5602
|
return /* @__PURE__ */ jsxs33(Box32, { flexDirection: "column", children: [
|
|
5816
5603
|
/* @__PURE__ */ jsx34(SectionHeader, { emoji: "\u{1F50D}", title: t("compliance_title"), gradient: GRADIENTS.gold }),
|
|
5817
|
-
/* @__PURE__ */ jsx34(Box32, { marginTop:
|
|
5604
|
+
/* @__PURE__ */ jsx34(Box32, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsx34(ProgressLog, { lines: streamLines, isRunning: streamRunning, title: t("compliance_remediating") }) })
|
|
5818
5605
|
] });
|
|
5819
5606
|
}
|
|
5820
5607
|
return /* @__PURE__ */ jsx34(Loading, { message: t("compliance_title") });
|
|
5821
5608
|
}
|
|
5822
5609
|
if (phase === "result" && resultMessage) {
|
|
5823
|
-
return /* @__PURE__ */ jsxs33(Box32, { flexDirection: "column", marginTop:
|
|
5610
|
+
return /* @__PURE__ */ jsxs33(Box32, { flexDirection: "column", marginTop: SPACING.xs, children: [
|
|
5824
5611
|
/* @__PURE__ */ jsx34(SectionHeader, { emoji: "\u{1F50D}", title: t("compliance_title"), gradient: GRADIENTS.gold }),
|
|
5825
|
-
/* @__PURE__ */ jsx34(Box32, { marginTop:
|
|
5612
|
+
/* @__PURE__ */ jsx34(Box32, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsx34(
|
|
5826
5613
|
ResultBanner,
|
|
5827
5614
|
{
|
|
5828
5615
|
status: resultMessage.ok ? "success" : "error",
|
|
5829
5616
|
message: resultMessage.text
|
|
5830
5617
|
}
|
|
5831
5618
|
) }),
|
|
5832
|
-
/* @__PURE__ */ jsx34(Box32, { marginTop:
|
|
5619
|
+
/* @__PURE__ */ jsx34(Box32, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsxs33(Text33, { color: COLORS.textSecondary, children: [
|
|
5833
5620
|
"r:",
|
|
5834
5621
|
t("hint_refresh"),
|
|
5835
5622
|
" esc:",
|
|
@@ -5839,10 +5626,10 @@ function ComplianceView() {
|
|
|
5839
5626
|
}
|
|
5840
5627
|
return /* @__PURE__ */ jsxs33(Box32, { flexDirection: "column", children: [
|
|
5841
5628
|
/* @__PURE__ */ jsx34(SectionHeader, { emoji: "\u{1F50D}", title: t("compliance_title"), gradient: GRADIENTS.gold }),
|
|
5842
|
-
error && /* @__PURE__ */ jsx34(Box32, { marginTop:
|
|
5843
|
-
phase === "importing" && /* @__PURE__ */ jsxs33(Box32, { marginTop:
|
|
5629
|
+
error && /* @__PURE__ */ jsx34(Box32, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsx34(ResultBanner, { status: "error", message: t("compliance_import_error", { error }) }) }),
|
|
5630
|
+
phase === "importing" && /* @__PURE__ */ jsxs33(Box32, { marginTop: SPACING.xs, flexDirection: "column", children: [
|
|
5844
5631
|
/* @__PURE__ */ jsx34(Text33, { color: COLORS.textSecondary, children: t("compliance_import_prompt") }),
|
|
5845
|
-
/* @__PURE__ */ jsx34(Box32, { marginTop:
|
|
5632
|
+
/* @__PURE__ */ jsx34(Box32, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsx34(
|
|
5846
5633
|
TextInput7,
|
|
5847
5634
|
{
|
|
5848
5635
|
defaultValue: "",
|
|
@@ -5851,20 +5638,20 @@ function ComplianceView() {
|
|
|
5851
5638
|
}
|
|
5852
5639
|
}
|
|
5853
5640
|
) }),
|
|
5854
|
-
/* @__PURE__ */ jsx34(Box32, { marginTop:
|
|
5641
|
+
/* @__PURE__ */ jsx34(Box32, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsxs33(Text33, { color: COLORS.muted, dimColor: true, children: [
|
|
5855
5642
|
"esc:",
|
|
5856
5643
|
t("hint_back")
|
|
5857
5644
|
] }) })
|
|
5858
5645
|
] }),
|
|
5859
|
-
phase === "overview" && /* @__PURE__ */ jsxs33(Box32, { flexDirection: "column", marginTop:
|
|
5646
|
+
phase === "overview" && /* @__PURE__ */ jsxs33(Box32, { flexDirection: "column", marginTop: SPACING.xs, children: [
|
|
5860
5647
|
!policy ? /* @__PURE__ */ jsx34(Box32, { flexDirection: "column", children: /* @__PURE__ */ jsx34(Text33, { color: COLORS.textSecondary, children: t("compliance_no_policy") }) }) : /* @__PURE__ */ jsxs33(Box32, { flexDirection: "column", children: [
|
|
5861
5648
|
/* @__PURE__ */ jsx34(Text33, { color: COLORS.textSecondary, bold: true, children: t("compliance_policy_by", { maintainer: policy.meta.maintainer }) }),
|
|
5862
|
-
report ? /* @__PURE__ */ jsxs33(Box32, { flexDirection: "column", marginTop:
|
|
5649
|
+
report ? /* @__PURE__ */ jsxs33(Box32, { flexDirection: "column", marginTop: SPACING.xs, children: [
|
|
5863
5650
|
/* @__PURE__ */ jsx34(ComplianceScore, { report }),
|
|
5864
5651
|
report.compliant ? /* @__PURE__ */ jsx34(ResultBanner, { status: "success", message: t("compliance_ok") }) : /* @__PURE__ */ jsx34(ViolationList, { violations: report.violations })
|
|
5865
|
-
] }) : /* @__PURE__ */ jsx34(Box32, { marginTop:
|
|
5652
|
+
] }) : /* @__PURE__ */ jsx34(Box32, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsx34(Text33, { color: COLORS.muted, dimColor: true, children: t("compliance_press_r_hint") }) })
|
|
5866
5653
|
] }),
|
|
5867
|
-
/* @__PURE__ */ jsx34(Box32, { marginTop:
|
|
5654
|
+
/* @__PURE__ */ jsx34(Box32, { marginTop: SPACING.sm, flexWrap: "wrap", children: /* @__PURE__ */ jsxs33(Text33, { color: COLORS.textSecondary, children: [
|
|
5868
5655
|
"i:",
|
|
5869
5656
|
t("hint_import"),
|
|
5870
5657
|
policy && /* @__PURE__ */ jsxs33(Text33, { children: [
|
|
@@ -5950,19 +5737,17 @@ function App() {
|
|
|
5950
5737
|
}
|
|
5951
5738
|
|
|
5952
5739
|
// src/lib/crash-reporter.ts
|
|
5953
|
-
import { readFile as
|
|
5954
|
-
import {
|
|
5955
|
-
import {
|
|
5956
|
-
import { join as join6 } from "path";
|
|
5740
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
5741
|
+
import { homedir, platform, release, arch } from "os";
|
|
5742
|
+
import { join as join5 } from "path";
|
|
5957
5743
|
var ENDPOINT_ENV = "BREW_TUI_CRASH_ENDPOINT";
|
|
5958
5744
|
var TOKEN_ENV = "BREW_TUI_CRASH_TOKEN";
|
|
5959
|
-
var CONFIG_PATH =
|
|
5960
|
-
var MACHINE_ID_PATH3 = join6(homedir3(), ".brew-tui", "machine-id");
|
|
5745
|
+
var CONFIG_PATH = join5(homedir(), ".brew-tui", "crash-reporter.json");
|
|
5961
5746
|
var POST_TIMEOUT_MS = 5e3;
|
|
5962
5747
|
var _installed = false;
|
|
5963
5748
|
async function loadConfigFromDisk() {
|
|
5964
5749
|
try {
|
|
5965
|
-
const raw = await
|
|
5750
|
+
const raw = await readFile3(CONFIG_PATH, "utf-8");
|
|
5966
5751
|
const parsed = JSON.parse(raw);
|
|
5967
5752
|
return {
|
|
5968
5753
|
endpoint: typeof parsed.endpoint === "string" ? parsed.endpoint : null,
|
|
@@ -5973,20 +5758,6 @@ async function loadConfigFromDisk() {
|
|
|
5973
5758
|
return { endpoint: null, token: null, enabled: false };
|
|
5974
5759
|
}
|
|
5975
5760
|
}
|
|
5976
|
-
async function getMachineId4() {
|
|
5977
|
-
try {
|
|
5978
|
-
const id2 = (await readFile5(MACHINE_ID_PATH3, "utf-8")).trim();
|
|
5979
|
-
if (id2) return id2;
|
|
5980
|
-
} catch {
|
|
5981
|
-
}
|
|
5982
|
-
const id = randomUUID3();
|
|
5983
|
-
try {
|
|
5984
|
-
await mkdir3(join6(homedir3(), ".brew-tui"), { recursive: true, mode: 448 });
|
|
5985
|
-
await writeFile5(MACHINE_ID_PATH3, id, { encoding: "utf-8", mode: 384 });
|
|
5986
|
-
} catch {
|
|
5987
|
-
}
|
|
5988
|
-
return id;
|
|
5989
|
-
}
|
|
5990
5761
|
async function resolveConfig() {
|
|
5991
5762
|
const envEndpoint = process.env[ENDPOINT_ENV]?.trim();
|
|
5992
5763
|
const envToken = process.env[TOKEN_ENV]?.trim();
|
|
@@ -6044,8 +5815,8 @@ function buildReport(level, err, context, machineId, version) {
|
|
|
6044
5815
|
async function reportError(err, context = {}) {
|
|
6045
5816
|
const config = await resolveConfig();
|
|
6046
5817
|
if (!config.enabled || !config.endpoint) return;
|
|
6047
|
-
const machineId = await
|
|
6048
|
-
const version = true ? "0.6.
|
|
5818
|
+
const machineId = await getMachineId();
|
|
5819
|
+
const version = true ? "0.6.2" : "unknown";
|
|
6049
5820
|
await postReport(buildReport("error", err, context, machineId, version), config);
|
|
6050
5821
|
}
|
|
6051
5822
|
async function installCrashReporter() {
|
|
@@ -6053,8 +5824,8 @@ async function installCrashReporter() {
|
|
|
6053
5824
|
const config = await resolveConfig();
|
|
6054
5825
|
if (!config.enabled || !config.endpoint) return;
|
|
6055
5826
|
_installed = true;
|
|
6056
|
-
const machineId = await
|
|
6057
|
-
const version = true ? "0.6.
|
|
5827
|
+
const machineId = await getMachineId();
|
|
5828
|
+
const version = true ? "0.6.2" : "unknown";
|
|
6058
5829
|
process.on("uncaughtException", (err) => {
|
|
6059
5830
|
void postReport(buildReport("fatal", err, { kind: "uncaughtException" }, machineId, version), config);
|
|
6060
5831
|
});
|
|
@@ -6160,7 +5931,7 @@ async function runCli() {
|
|
|
6160
5931
|
}
|
|
6161
5932
|
if (isPro) {
|
|
6162
5933
|
try {
|
|
6163
|
-
const { loadSnapshots: loadSnapshots2 } = await import("./snapshot-
|
|
5934
|
+
const { loadSnapshots: loadSnapshots2 } = await import("./snapshot-YWIOFQ5H.js");
|
|
6164
5935
|
const snapshots = await loadSnapshots2();
|
|
6165
5936
|
if (snapshots.length > 0) {
|
|
6166
5937
|
const latest = snapshots[0];
|
|
@@ -6172,7 +5943,7 @@ Snapshots: ${snapshots.length} (latest: ${latest ? formatDate(latest.capturedAt)
|
|
|
6172
5943
|
} catch {
|
|
6173
5944
|
}
|
|
6174
5945
|
try {
|
|
6175
|
-
const { loadBrewfile: loadBrewfile2, computeDrift: computeDrift2 } = await import("./brewfile-manager-
|
|
5946
|
+
const { loadBrewfile: loadBrewfile2, computeDrift: computeDrift2 } = await import("./brewfile-manager-OITSKEHY.js");
|
|
6176
5947
|
const schema = await loadBrewfile2();
|
|
6177
5948
|
if (schema) {
|
|
6178
5949
|
const drift = await computeDrift2(schema);
|
|
@@ -6181,7 +5952,7 @@ Snapshots: ${snapshots.length} (latest: ${latest ? formatDate(latest.capturedAt)
|
|
|
6181
5952
|
} catch {
|
|
6182
5953
|
}
|
|
6183
5954
|
try {
|
|
6184
|
-
const { loadSyncConfig: loadSyncConfig2 } = await import("./sync-engine-
|
|
5955
|
+
const { loadSyncConfig: loadSyncConfig2 } = await import("./sync-engine-DIYXV66P.js");
|
|
6185
5956
|
const syncConfig = await loadSyncConfig2();
|
|
6186
5957
|
if (syncConfig?.lastSync) {
|
|
6187
5958
|
console.log(`Sync: last sync ${formatDate(syncConfig.lastSync)}`);
|
|
@@ -6190,7 +5961,7 @@ Snapshots: ${snapshots.length} (latest: ${latest ? formatDate(latest.capturedAt)
|
|
|
6190
5961
|
}
|
|
6191
5962
|
try {
|
|
6192
5963
|
const { loadPolicy: loadPolicy2 } = await import("./policy-io-EECGRKNA.js");
|
|
6193
|
-
const { checkCompliance: checkCompliance2 } = await import("./compliance-checker-
|
|
5964
|
+
const { checkCompliance: checkCompliance2 } = await import("./compliance-checker-7NMFKWTI.js");
|
|
6194
5965
|
const policy = await loadPolicy2(`${process.env["HOME"] ?? "~"}/.brew-tui/policy.yaml`).catch(() => null);
|
|
6195
5966
|
if (policy) {
|
|
6196
5967
|
const report = await checkCompliance2(policy, true);
|
|
@@ -6204,7 +5975,7 @@ Snapshots: ${snapshots.length} (latest: ${latest ? formatDate(latest.capturedAt)
|
|
|
6204
5975
|
if (command === "install-brewbar") {
|
|
6205
5976
|
await useLicenseStore.getState().initialize();
|
|
6206
5977
|
const isPro = useLicenseStore.getState().isPro();
|
|
6207
|
-
const { installBrewBar } = await import("./brewbar-installer-
|
|
5978
|
+
const { installBrewBar } = await import("./brewbar-installer-WJZKSELD.js");
|
|
6208
5979
|
try {
|
|
6209
5980
|
await installBrewBar(isPro, arg === "--force");
|
|
6210
5981
|
console.log(t("cli_brewbarInstalled"));
|
|
@@ -6215,7 +5986,7 @@ Snapshots: ${snapshots.length} (latest: ${latest ? formatDate(latest.capturedAt)
|
|
|
6215
5986
|
return;
|
|
6216
5987
|
}
|
|
6217
5988
|
if (command === "uninstall-brewbar") {
|
|
6218
|
-
const { uninstallBrewBar } = await import("./brewbar-installer-
|
|
5989
|
+
const { uninstallBrewBar } = await import("./brewbar-installer-WJZKSELD.js");
|
|
6219
5990
|
try {
|
|
6220
5991
|
await uninstallBrewBar();
|
|
6221
5992
|
console.log(t("cli_brewbarUninstalled"));
|
|
@@ -6233,7 +6004,7 @@ Snapshots: ${snapshots.length} (latest: ${latest ? formatDate(latest.capturedAt)
|
|
|
6233
6004
|
console.log(t("cli_deactivateCancelled"));
|
|
6234
6005
|
return;
|
|
6235
6006
|
}
|
|
6236
|
-
await
|
|
6007
|
+
await rm2(DATA_DIR, { recursive: true, force: true });
|
|
6237
6008
|
console.log(t("delete_account_success"));
|
|
6238
6009
|
return;
|
|
6239
6010
|
}
|
|
@@ -6246,7 +6017,7 @@ async function ensureBrewBarRunning() {
|
|
|
6246
6017
|
if (process.platform !== "darwin") return;
|
|
6247
6018
|
await useLicenseStore.getState().initialize();
|
|
6248
6019
|
if (!useLicenseStore.getState().isPro()) return;
|
|
6249
|
-
const { isBrewBarInstalled, installBrewBar, launchBrewBar } = await import("./brewbar-installer-
|
|
6020
|
+
const { isBrewBarInstalled, installBrewBar, launchBrewBar } = await import("./brewbar-installer-WJZKSELD.js");
|
|
6250
6021
|
try {
|
|
6251
6022
|
if (!await isBrewBarInstalled()) {
|
|
6252
6023
|
console.log(t("cli_brewbarInstalling"));
|