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.
Files changed (38) hide show
  1. package/README.md +5 -5
  2. package/build/{brewbar-installer-BAHS6EEZ.js → brewbar-installer-WJZKSELD.js} +3 -3
  3. package/build/brewbar-installer-WJZKSELD.js.map +1 -0
  4. package/build/{brewfile-manager-3SERRYNC.js → brewfile-manager-OITSKEHY.js} +4 -4
  5. package/build/{chunk-U2DRWB7A.js → chunk-5MYWF5D7.js} +2 -2
  6. package/build/{chunk-MSXH66I2.js → chunk-6NA4INJS.js} +31 -3
  7. package/build/chunk-6NA4INJS.js.map +1 -0
  8. package/build/chunk-EHIBIFCB.js +54 -0
  9. package/build/chunk-EHIBIFCB.js.map +1 -0
  10. package/build/{chunk-LXF72RCD.js → chunk-KSIAKLE2.js} +3 -3
  11. package/build/{chunk-4I344KQX.js → chunk-PVSE6XO7.js} +42 -2
  12. package/build/chunk-PVSE6XO7.js.map +1 -0
  13. package/build/chunk-QPXROTAP.js +679 -0
  14. package/build/chunk-QPXROTAP.js.map +1 -0
  15. package/build/{chunk-KVCVIRWI.js → chunk-Z2VN4VYQ.js} +2 -2
  16. package/build/compliance-checker-7NMFKWTI.js +12 -0
  17. package/build/{history-logger-PBDOLKNJ.js → history-logger-ZGYRAFON.js} +3 -3
  18. package/build/index.js +449 -678
  19. package/build/index.js.map +1 -1
  20. package/build/{snapshot-RAPGMAJF.js → snapshot-YWIOFQ5H.js} +7 -3
  21. package/build/{sync-engine-CAFK4LHA.js → sync-engine-DIYXV66P.js} +7 -5
  22. package/package.json +4 -4
  23. package/build/brewbar-installer-BAHS6EEZ.js.map +0 -1
  24. package/build/chunk-4I344KQX.js.map +0 -1
  25. package/build/chunk-AIAZQJKL.js +0 -299
  26. package/build/chunk-AIAZQJKL.js.map +0 -1
  27. package/build/chunk-MSXH66I2.js.map +0 -1
  28. package/build/chunk-UWS4A4F5.js +0 -25
  29. package/build/chunk-UWS4A4F5.js.map +0 -1
  30. package/build/compliance-checker-X7P623UF.js +0 -12
  31. /package/build/{brewfile-manager-3SERRYNC.js.map → brewfile-manager-OITSKEHY.js.map} +0 -0
  32. /package/build/{chunk-U2DRWB7A.js.map → chunk-5MYWF5D7.js.map} +0 -0
  33. /package/build/{chunk-LXF72RCD.js.map → chunk-KSIAKLE2.js.map} +0 -0
  34. /package/build/{chunk-KVCVIRWI.js.map → chunk-Z2VN4VYQ.js.map} +0 -0
  35. /package/build/{compliance-checker-X7P623UF.js.map → compliance-checker-7NMFKWTI.js.map} +0 -0
  36. /package/build/{history-logger-PBDOLKNJ.js.map → history-logger-ZGYRAFON.js.map} +0 -0
  37. /package/build/{snapshot-RAPGMAJF.js.map → snapshot-YWIOFQ5H.js.map} +0 -0
  38. /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-LXF72RCD.js";
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-AIAZQJKL.js";
22
+ } from "./chunk-QPXROTAP.js";
16
23
  import {
17
24
  checkCompliance
18
- } from "./chunk-U2DRWB7A.js";
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-4I344KQX.js";
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-KVCVIRWI.js";
42
+ } from "./chunk-Z2VN4VYQ.js";
36
43
  import {
37
44
  DATA_DIR,
38
- LICENSE_PATH,
39
45
  PROFILES_DIR,
40
- ensureDataDirs
41
- } from "./chunk-UWS4A4F5.js";
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-MSXH66I2.js";
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 rm3 } from "fs/promises";
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 COLORS = {
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: 1, flexDirection: "column", alignSelf: isNarrow ? "flex-start" : "center", children: [
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: 2, children: COL2_VIEWS.map((view) => /* @__PURE__ */ jsx2(MenuItem, { view, currentView }, view)) })
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: 0, children: [
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: 1, children: [
367
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: SPACING.xs, children: [
339
368
  logoBlock,
340
- /* @__PURE__ */ jsx2(Box, { marginTop: 1, children: menuBlock })
369
+ /* @__PURE__ */ jsx2(Box, { marginTop: SPACING.xs, children: menuBlock })
341
370
  ] });
342
371
  }
343
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", paddingX: 1, alignItems: "center", children: [
372
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", paddingX: SPACING.xs, alignItems: "center", children: [
344
373
  logoBlock,
345
- /* @__PURE__ */ jsx2(Box, { marginLeft: 2, children: menuBlock })
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: 1, flexWrap: "wrap", children: [
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: 2, paddingY: 1, children }),
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: 2, children: /* @__PURE__ */ jsxs4(
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: 3,
993
- paddingY: 2,
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", reject);
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: "Homebrew" }, version: pkg.version }]
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: "Homebrew" }, version: pkg.version }]
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: "Homebrew" },
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 payload = decryptPayload(envelope.encrypted, envelope.iv, envelope.tag);
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: 2,
1948
- paddingY: 0,
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: 1, children: /* @__PURE__ */ jsx7(Spinner, { label: message ?? t("loading_default") }) });
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: 1, children: [
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: 1, children: [
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: 2, paddingY: 0, marginTop: 1, children: [
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: 1, children: [
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: 1, children: [
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: 1, children: [
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: 1, children: [
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) return /* @__PURE__ */ jsx10(ErrorMessage, { message: 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: 2, children: [
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: 1, flexWrap: "wrap", flexDirection: isNarrow ? "column" : "row", children: [
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: 2, paddingY: 0, children: [
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: 2, paddingY: 0, flexDirection: "column", marginTop: 1, children: [
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: 1, children: [
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: 2, flexDirection: "column", children: [
2186
- outdated.formulae.slice(0, 10).map((pkg) => /* @__PURE__ */ jsxs10(Box8, { gap: 1, children: [
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: 1, children: [
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: 2, flexDirection: "column", children: errorServiceList.map((s) => /* @__PURE__ */ jsxs10(Box8, { gap: 1, children: [
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 useInput3, useStdout as useStdout4 } from "ink";
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-PBDOLKNJ.js");
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-RAPGMAJF.js").then(({ captureSnapshot: captureSnapshot2, saveSnapshot: saveSnapshot2 }) => {
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 useInput2 } from "ink";
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
- useInput2((input, key) => {
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: 2, paddingY: 1, flexDirection: "column", children: [
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: 1, children: [
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: 1, children: [
2355
- title && /* @__PURE__ */ jsxs13(Box11, { marginBottom: 1, children: [
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: 2, paddingY: 0, children: /* @__PURE__ */ jsx14(Text14, { color: STATUS_COLORS[status], bold: true, children: message }) });
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
- useInput3((input, key) => {
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: 1, children: [
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: 1, gap: 1, children: [
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: 1,
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: 1,
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: 1, borderStyle: "round", borderColor: COLORS.gold, paddingX: 1, children: /* @__PURE__ */ jsx16(SearchInput, { defaultValue: filter, onChange: setFilter, isActive: isSearching }) }),
2550
- /* @__PURE__ */ jsxs15(Box14, { gap: 1, borderStyle: "single", borderBottom: true, borderTop: false, borderLeft: false, borderRight: false, borderColor: COLORS.border, children: [
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: 1, justifyContent: "center", children: /* @__PURE__ */ jsx16(Text16, { color: COLORS.textSecondary, italic: true, children: t("installed_noPackages") }) }),
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: 1, children: /* @__PURE__ */ jsx16(Text16, { color: COLORS.text, bold: true, children: allItems.length > 0 ? `${cursor + 1}/${allItems.length}` : "0/0" }) })
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 useInput4 } from "ink";
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
- useInput4((input, key) => {
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: 1, children: [
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: 1, children: [
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: 1, children: /* @__PURE__ */ jsx17(Text17, { color: COLORS.error, children: searchError }) }),
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: 1, children: [
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: 2, children: /* @__PURE__ */ jsx17(Text17, { color: COLORS.textSecondary, italic: true, children: t("search_noResults") }) }),
2767
- /* @__PURE__ */ jsx17(Box15, { marginTop: 1, children: /* @__PURE__ */ jsx17(Text17, { color: COLORS.text, bold: true, children: allVisible.length > 0 ? `${cursor + 1}/${allVisible.length}` : "" }) })
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 useInput5, useStdout as useStdout5 } from "ink";
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: 1, borderStyle: "round", borderColor: riskColor, paddingX: 2, paddingY: 0, children: [
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
- ...outdated.formulae.map((p) => ({ ...p, type: "formula" })),
2816
- ...outdated.casks.map((p) => ({ ...p, type: "cask" }))
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
- useInput5((input, key) => {
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: 1, children: [
2887
- /* @__PURE__ */ jsxs17(Box16, { borderStyle: "round", borderColor: stream.error ? COLORS.error : COLORS.success, paddingX: 2, paddingY: 0, children: [
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: 1, children: /* @__PURE__ */ jsx18(
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: 1, children: /* @__PURE__ */ jsx18(ResultBanner, { status: "success", message: `\u2714 ${t("outdated_upToDate")}` }) }),
2924
- allOutdated.length > 0 && !confirmAction && /* @__PURE__ */ jsxs17(Box16, { flexDirection: "column", marginTop: 1, children: [
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: 1, children: /* @__PURE__ */ jsxs17(Text18, { color: COLORS.text, bold: true, children: [
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: 1, children: /* @__PURE__ */ jsx18(Text18, { color: COLORS.textSecondary, children: t("impact_analyzing") }) }),
2949
- /* @__PURE__ */ jsx18(Box16, { marginTop: 1, children: /* @__PURE__ */ jsx18(Text18, { color: COLORS.textSecondary, children: t("impact_hint") }) })
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 useInput6 } from "ink";
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
- useInput6((input, key) => {
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: 2, marginBottom: 1, children: [
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: 1, children: [
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: 2, flexDirection: "column", children: [
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: 2, flexWrap: "wrap", columnGap: 2, children: formula.dependencies.map((dep) => /* @__PURE__ */ jsx19(Text19, { color: COLORS.muted, children: dep }, dep)) })
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: 2, children: /* @__PURE__ */ jsx19(Text19, { color: COLORS.warning, children: formula.caveats }) })
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: 1, children: /* @__PURE__ */ jsxs18(Text19, { color: COLORS.textSecondary, children: [
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 useInput7, useStdout as useStdout6 } from "ink";
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
- useInput7((input, key) => {
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: 1, children: /* @__PURE__ */ jsx20(
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: 1, children: [
3264
- /* @__PURE__ */ jsxs19(Box18, { gap: 1, borderStyle: "single", borderBottom: true, borderTop: false, borderLeft: false, borderRight: false, borderColor: COLORS.border, paddingBottom: 0, children: [
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: 1, children: /* @__PURE__ */ jsx20(Text20, { color: COLORS.error, children: lastError }) }),
3293
- /* @__PURE__ */ jsx20(Box18, { marginTop: 1, children: /* @__PURE__ */ jsxs19(Text20, { color: COLORS.text, bold: true, children: [
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 useInput8 } from "ink";
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
- useInput8((input) => {
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: 1, children: [
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: 1, borderStyle: "single", borderColor: COLORS.warning, paddingX: 1, 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)}`)
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: 1, children: /* @__PURE__ */ jsx21(Text21, { color: COLORS.text, bold: true, children: doctorWarnings.length > 0 ? tp("plural_warnings", doctorWarnings.length) : "" }) })
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 useInput9 } from "ink";
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 as readFile3, writeFile as writeFile3, readdir, rm as rm2, rename as rename2 } from "fs/promises";
3345
- import { join as join2, basename } from "path";
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 = true) {
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 join2(PROFILES_DIR, `${basename(name)}.json`);
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 readFile3(profilePath(name), "utf-8");
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 writeFile3(tmpPath, JSON.stringify(file, null, 2), { encoding: "utf-8", mode: 384 });
3408
- await rename2(tmpPath, filePath);
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 rm2(profilePath(name));
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: consent ? getWatermark(license) : ""
3440
- // SEG-003: Only embed watermark with consent
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: 1, children: /* @__PURE__ */ jsx22(Text22, { color: COLORS.error, children: loadError }) }),
3564
- confirmDelete && profileNames[cursor] && /* @__PURE__ */ jsx22(Box20, { marginY: 1, children: /* @__PURE__ */ jsx22(
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: 1, borderStyle: "round", borderColor: COLORS.textSecondary, paddingX: 2, paddingY: 0, children: /* @__PURE__ */ jsxs21(Box20, { flexDirection: "column", children: [
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: 1, children: [
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: 1, children: /* @__PURE__ */ jsxs21(Text22, { color: COLORS.text, bold: true, children: [
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: 1, flexDirection: "column", children: [
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: 2, flexDirection: "column", children: [
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: 2, flexDirection: "column", children: profile.casks.map((c) => /* @__PURE__ */ jsx23(Text23, { color: COLORS.muted, children: c }, c)) })
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: 1, children: /* @__PURE__ */ jsxs22(Text23, { color: COLORS.textSecondary, children: [
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
- useInput9((input, key) => {
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
- useInput9((input, key) => {
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
- useInput9(() => {
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: 1, children: /* @__PURE__ */ jsx26(ResultBanner, { status: importHadError ? "error" : "success", message: importHadError ? t("profiles_importPartial") : `\u2714 ${t("profiles_importComplete")}` }) })
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 useInput10 } from "ink";
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
- useInput10((input, key) => {
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: 1, children: [
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: 1, children: /* @__PURE__ */ jsx27(
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: 1, marginY: 1, children: [
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: 1, gap: 1, children: [
4108
- /* @__PURE__ */ jsx27(Box25, { borderStyle: "round", borderColor: COLORS.warning, paddingX: 2, paddingY: 0, children: /* @__PURE__ */ jsx27(Text26, { color: COLORS.warning, children: t("cleanup_warning_system_tools") }) }),
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: 1, children: /* @__PURE__ */ jsxs26(Text26, { color: COLORS.text, bold: true, children: [
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 useMemo4 } from "react";
4151
- import { Box as Box26, Text as Text27, useInput as useInput11, useStdout as useStdout7 } from "ink";
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 = useMemo4(() => {
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
- useInput11((input, key) => {
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: 2, marginBottom: 1, children: [
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: 1, children: /* @__PURE__ */ jsx28(SearchInput, { defaultValue: searchQuery, onChange: setSearchQuery, placeholder: t("history_searchPlaceholder"), isActive: true }) }),
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: 1, children: /* @__PURE__ */ jsx28(ProgressLog, { lines: stream.lines, isRunning: stream.isRunning, title: t("hint_replay") }) }),
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: 1, children: /* @__PURE__ */ jsxs27(Text27, { color: COLORS.text, bold: true, children: [
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 useInput12 } from "ink";
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 isNetworkError2(msg) {
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
- useInput12((input, key) => {
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 = isNetworkError2(error) ? t("security_networkError") : error;
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: 1, marginY: 1, children: [
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: 1, children: /* @__PURE__ */ jsx29(ResultBanner, { status: "success", message: `\u2714 ${t("security_noVulns")}` }) }),
4425
- confirmUpgrade && /* @__PURE__ */ jsx29(Box27, { marginY: 1, children: /* @__PURE__ */ jsx29(
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: 1, children: /* @__PURE__ */ jsx29(ProgressLog, { lines: stream.lines, isRunning: stream.isRunning, title: t("hint_upgrade") }) }),
4439
- results.length > 0 && /* @__PURE__ */ jsxs28(Box27, { flexDirection: "column", marginTop: 1, children: [
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: 4, marginBottom: 1, children: pkg.vulnerabilities.map((vuln) => /* @__PURE__ */ jsxs28(Box27, { flexDirection: "column", marginBottom: 1, children: [
4457
- /* @__PURE__ */ jsxs28(Box27, { gap: 1, children: [
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: 1, children: /* @__PURE__ */ jsxs28(Text28, { color: COLORS.text, bold: true, children: [
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: 1, children: /* @__PURE__ */ jsx29(Text28, { color: COLORS.textSecondary, children: t("security_rollback_hint") }) })
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 useInput13 } from "ink";
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 readFile4, writeFile as writeFile4, rename as rename3, mkdir as mkdir2 } from "fs/promises";
4487
- import { randomBytes as randomBytes2, randomUUID as randomUUID2 } from "crypto";
4488
- import { join as join3 } from "path";
4489
- import { homedir as homedir2 } from "os";
4490
- var MACHINE_ID_PATH2 = join3(homedir2(), ".brew-tui", "machine-id");
4491
- async function getMachineId3() {
4492
- try {
4493
- const id2 = (await readFile4(MACHINE_ID_PATH2, "utf-8")).trim();
4494
- if (id2) return id2;
4495
- } catch {
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 getMachineId3();
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: { "Content-Type": "application/json" },
4513
- body: JSON.stringify({ code: normalized, machineId })
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 readFile4(PROMO_PATH, "utf-8");
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 writeFile4(tmpPath, JSON.stringify(file, null, 2), { encoding: "utf-8", mode: 384 });
4554
- await rename3(tmpPath, PROMO_PATH);
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
- useInput13((input, key) => {
4569
- if (confirmDeactivate || deactivating || promoMode) {
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: 1, children: /* @__PURE__ */ jsx30(
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: 1, paddingLeft: 2, children: [
4613
- /* @__PURE__ */ jsxs29(Box28, { gap: 1, children: [
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: 1, borderStyle: "round", borderColor: COLORS.warning, paddingX: 2, paddingY: 0, children: /* @__PURE__ */ jsx30(Text29, { color: COLORS.warning, children: t("license_offlineWarning", {
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: 1, children: [
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: 1, children: [
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: 1, children: [
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: 1, children: [
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: 1, children: [
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: 1, children: [
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: 2, borderStyle: "round", borderColor: COLORS.brand, paddingX: 2, paddingY: 1, children: [
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: 1, children: /* @__PURE__ */ jsx30(Box28, { borderStyle: "round", borderColor: COLORS.error, paddingX: 2, paddingY: 0, children: /* @__PURE__ */ jsx30(Text29, { color: COLORS.error, children: t("account_licenseExpired") }) }) }),
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: 1, paddingLeft: 2, children: promoMode ? /* @__PURE__ */ jsxs29(Box28, { flexDirection: "column", gap: 1, children: [
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: 1, children: [
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: 1, children: /* @__PURE__ */ jsxs29(Text29, { color: COLORS.textSecondary, children: [
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.0" })
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 useInput14 } from "ink";
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 join4 } from "path";
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 = join4(brewCache, "downloads");
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: 1, children: [
4953
- /* @__PURE__ */ jsxs30(Box29, { marginBottom: 1, children: [
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: 1, children: /* @__PURE__ */ jsxs30(Text30, { color: COLORS.warning, children: [
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: 1, children: plan.canExecute ? /* @__PURE__ */ jsxs30(Text30, { color: COLORS.textSecondary, children: [
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
- useInput14((input, key) => {
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: 1, children: [
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: 1, children: /* @__PURE__ */ jsxs30(Text30, { color: COLORS.textSecondary, children: [
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: 1, children: /* @__PURE__ */ jsx31(ResultBanner, { status: "info", message: t("rollback_no_snapshots") }) }),
5102
- phase === "list" && snapshots.length > 0 && /* @__PURE__ */ jsxs30(Box29, { flexDirection: "column", marginTop: 1, children: [
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: 1, children: snapshots.map((s, i) => /* @__PURE__ */ jsxs30(SelectableRow, { isCurrent: i === cursor, children: [
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: 1, children: /* @__PURE__ */ jsx31(
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 useInput15 } from "ink";
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: 1, children: [
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: "\u2713 System is in sync with Brewfile" })
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
- useInput15((input, key) => {
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
- void startReconcile();
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: 1, children: [
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: 1, children: [
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: 1, children: [
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: 1, children: /* @__PURE__ */ jsxs31(Text31, { color: COLORS.textSecondary, children: [
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: 1, flexDirection: "column", children: [
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: 1, children: /* @__PURE__ */ jsxs31(Text31, { color: COLORS.textSecondary, children: [
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: 1, children: [
5317
- /* @__PURE__ */ jsxs31(Box30, { gap: 2, children: [
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: 3, marginTop: 1, children: [
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: 1, children: /* @__PURE__ */ jsx32(Text31, { color: COLORS.muted, children: "Computing drift..." }) }),
5331
- drift && !driftLoading && /* @__PURE__ */ jsxs31(Box30, { flexDirection: "column", marginTop: 1, children: [
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: 1, children: /* @__PURE__ */ jsxs31(Text31, { color: COLORS.textSecondary, children: [
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 useInput16 } from "ink";
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: 1, children: [
5094
+ return /* @__PURE__ */ jsxs32(Box31, { flexDirection: "column", marginTop: SPACING.xs, children: [
5361
5095
  config ? /* @__PURE__ */ jsxs32(Fragment6, { children: [
5362
- /* @__PURE__ */ jsx33(Box31, { marginBottom: 1, children: /* @__PURE__ */ jsx33(Text32, { color: COLORS.textSecondary, children: t("sync_machine", { name: config.machineName }) }) }),
5363
- config.lastSync && /* @__PURE__ */ jsx33(Box31, { marginBottom: 1, children: /* @__PURE__ */ jsx33(Text32, { color: COLORS.textSecondary, children: t("sync_last_sync", { date: new Date(config.lastSync).toLocaleString() }) }) }),
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: 1, children: /* @__PURE__ */ jsx33(Text32, { color: COLORS.textSecondary, children: t("sync_disabled") }) }),
5372
- /* @__PURE__ */ jsx33(Box31, { marginTop: 1, children: /* @__PURE__ */ jsxs32(Text32, { color: COLORS.textSecondary, children: [
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: 1, children: [
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: 1, children: [
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: 2, flexDirection: "column", children: [
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: 1, children: /* @__PURE__ */ jsxs32(Text32, { color: COLORS.textSecondary, children: [
5439
- "j/k:navegar l:",
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:aplicar"
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
- useInput16((input, key) => {
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
- void handleApplyResolutions();
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
- void handleSyncNow();
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: 1, children: [
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: 1, children: /* @__PURE__ */ jsx33(
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: 1, children: /* @__PURE__ */ jsxs32(Text32, { color: COLORS.textSecondary, children: [
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: 1, children: /* @__PURE__ */ jsx33(ResultBanner, { status: "error", message: t("sync_error", { error }) }) }),
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 useInput17 } from "ink";
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 join5 } from "path";
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: 1, children: [
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: 0, children: [
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: 1, children: [
5681
- errors.length > 0 && /* @__PURE__ */ jsxs33(Box32, { flexDirection: "column", marginBottom: 1, children: [
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 = join5(DATA_DIR, `compliance-report-${timestamp}.json`);
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
- useInput17((input, key) => {
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
- void handleRemediate();
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: 1, children: /* @__PURE__ */ jsx34(ProgressLog, { lines: streamLines, isRunning: streamRunning, title: t("compliance_remediating") }) })
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: 1, children: [
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: 1, children: /* @__PURE__ */ jsx34(
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: 1, children: /* @__PURE__ */ jsxs33(Text33, { color: COLORS.textSecondary, children: [
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: 1, children: /* @__PURE__ */ jsx34(ResultBanner, { status: "error", message: t("compliance_import_error", { error }) }) }),
5843
- phase === "importing" && /* @__PURE__ */ jsxs33(Box32, { marginTop: 1, flexDirection: "column", children: [
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: 1, children: /* @__PURE__ */ jsx34(
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: 1, children: /* @__PURE__ */ jsxs33(Text33, { color: COLORS.muted, dimColor: true, children: [
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: 1, children: [
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: 1, children: [
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: 1, children: /* @__PURE__ */ jsx34(Text33, { color: COLORS.muted, dimColor: true, children: t("compliance_press_r_hint") }) })
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: 2, flexWrap: "wrap", children: /* @__PURE__ */ jsxs33(Text33, { color: COLORS.textSecondary, children: [
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 readFile5, writeFile as writeFile5, mkdir as mkdir3 } from "fs/promises";
5954
- import { randomUUID as randomUUID3 } from "crypto";
5955
- import { homedir as homedir3, platform, release, arch } from "os";
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 = join6(homedir3(), ".brew-tui", "crash-reporter.json");
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 readFile5(CONFIG_PATH, "utf-8");
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 getMachineId4();
6048
- const version = true ? "0.6.0" : "unknown";
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 getMachineId4();
6057
- const version = true ? "0.6.0" : "unknown";
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-RAPGMAJF.js");
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-3SERRYNC.js");
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-CAFK4LHA.js");
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-X7P623UF.js");
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-BAHS6EEZ.js");
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-BAHS6EEZ.js");
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 rm3(DATA_DIR, { recursive: true, force: true });
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-BAHS6EEZ.js");
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"));