brew-tui 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/index.js CHANGED
@@ -1,25 +1,25 @@
1
1
  import {
2
+ DATA_DIR,
3
+ LICENSE_PATH,
4
+ PROFILES_DIR,
2
5
  appendEntry,
3
6
  clearHistory,
4
7
  detectAction,
8
+ ensureDataDirs,
5
9
  loadHistory
6
- } from "./chunk-UBHTQL7T.js";
10
+ } from "./chunk-65YZJX2E.js";
7
11
  import {
8
- PROFILES_DIR,
9
- activate,
10
- deactivate,
11
- ensureDataDirs,
12
12
  fetchWithTimeout,
13
- loadLicense,
14
- requirePro,
13
+ getLocale,
14
+ logger,
15
15
  t,
16
16
  tp,
17
- useLicenseStore,
18
17
  useLocaleStore
19
- } from "./chunk-KXDTKY3E.js";
18
+ } from "./chunk-PTLSNG2N.js";
20
19
 
21
20
  // src/index.tsx
22
21
  import { createInterface } from "readline/promises";
22
+ import { rm as rm3 } from "fs/promises";
23
23
  import { render } from "ink";
24
24
 
25
25
  // src/app.tsx
@@ -51,15 +51,14 @@ var VIEWS = [
51
51
  ];
52
52
  var useNavigationStore = create((set, get) => ({
53
53
  currentView: "dashboard",
54
- previousView: null,
55
54
  selectedPackage: null,
55
+ selectedPackageType: null,
56
56
  viewHistory: [],
57
57
  navigate: (view) => {
58
58
  const { currentView, viewHistory } = get();
59
59
  if (view === currentView) return;
60
60
  set({
61
61
  currentView: view,
62
- previousView: currentView,
63
62
  viewHistory: [...viewHistory.slice(-19), currentView]
64
63
  });
65
64
  },
@@ -69,11 +68,10 @@ var useNavigationStore = create((set, get) => ({
69
68
  const prev = viewHistory[viewHistory.length - 1];
70
69
  set({
71
70
  currentView: prev,
72
- previousView: get().currentView,
73
71
  viewHistory: viewHistory.slice(0, -1)
74
72
  });
75
73
  },
76
- selectPackage: (name) => set({ selectedPackage: name })
74
+ selectPackage: (name, type = null) => set({ selectedPackage: name, selectedPackageType: type })
77
75
  }));
78
76
  function getNextView(current) {
79
77
  const idx = VIEWS.indexOf(current);
@@ -95,6 +93,24 @@ function isProView(viewId) {
95
93
  return PRO_VIEWS.has(viewId);
96
94
  }
97
95
 
96
+ // src/utils/colors.ts
97
+ var COLORS = {
98
+ success: "#22C55E",
99
+ error: "#EF4444",
100
+ warning: "#F59E0B",
101
+ info: "#06B6D4",
102
+ brand: "#FF6B2B",
103
+ muted: "#9CA3AF",
104
+ text: "#F9FAFB",
105
+ textSecondary: "#6B7280",
106
+ teal: "#2DD4BF",
107
+ sky: "#38BDF8",
108
+ gold: "#FFD700",
109
+ purple: "#A855F7",
110
+ blue: "#3B82F6",
111
+ border: "#4B5563"
112
+ };
113
+
98
114
  // src/utils/gradient.tsx
99
115
  import React, { useMemo } from "react";
100
116
  import { Text } from "ink";
@@ -194,6 +210,7 @@ var TAB_VIEWS = [
194
210
  "installed",
195
211
  "search",
196
212
  "outdated",
213
+ "package-info",
197
214
  "services",
198
215
  "doctor",
199
216
  "profiles",
@@ -210,13 +227,13 @@ function Header() {
210
227
  /* @__PURE__ */ jsx2(GradientText, { colors: GRADIENTS.gold, children: brew }),
211
228
  /* @__PURE__ */ jsx2(GradientText, { colors: ["#B8860B", "#8B6914", "#6B4F10"], children: LOGO_TUI[i] })
212
229
  ] }, i)) }),
213
- /* @__PURE__ */ jsx2(Box, { borderStyle: "single", borderBottom: true, borderLeft: false, borderRight: false, borderTop: false, borderColor: "#FFD700", paddingX: 1, flexWrap: "wrap", children: TAB_VIEWS.map((view, i) => {
230
+ /* @__PURE__ */ jsx2(Box, { borderStyle: "single", borderBottom: true, borderLeft: false, borderRight: false, borderTop: false, borderColor: COLORS.gold, paddingX: 1, flexWrap: "wrap", children: TAB_VIEWS.map((view, i) => {
214
231
  const key = VIEW_KEYS[view];
215
232
  const viewLabel = t(VIEW_LABEL_KEYS[view]);
216
233
  const label = key ? `${key}:${viewLabel}` : viewLabel;
217
234
  const isPro = isProView(view);
218
235
  return /* @__PURE__ */ jsxs(React2.Fragment, { children: [
219
- i > 0 && /* @__PURE__ */ jsxs(Text2, { color: "#4B5563", children: [
236
+ i > 0 && /* @__PURE__ */ jsxs(Text2, { color: COLORS.border, children: [
220
237
  " ",
221
238
  "\u2502",
222
239
  " "
@@ -225,12 +242,12 @@ function Header() {
225
242
  Text2,
226
243
  {
227
244
  bold: view === currentView,
228
- color: view === currentView ? "#22C55E" : "#6B7280",
245
+ color: view === currentView ? COLORS.success : COLORS.textSecondary,
229
246
  underline: view === currentView,
230
247
  children: view === currentView ? `\u25CF ${label}` : label
231
248
  }
232
249
  ),
233
- isPro && /* @__PURE__ */ jsxs(Text2, { color: "#FF6B2B", bold: true, children: [
250
+ isPro && /* @__PURE__ */ jsxs(Text2, { color: COLORS.brand, bold: true, children: [
234
251
  " ",
235
252
  t("pro_badge")
236
253
  ] })
@@ -258,22 +275,22 @@ var VIEW_HINT_DEFS = {
258
275
  account: [["d", "hint_deactivate"], ["q", "hint_quit"]]
259
276
  };
260
277
  function HintItem({ def }) {
261
- if (def.length === 1) return /* @__PURE__ */ jsx3(Text3, { color: "#FFD700", dimColor: true, children: t(def[0]) });
278
+ if (def.length === 1) return /* @__PURE__ */ jsx3(Text3, { color: COLORS.gold, dimColor: true, children: t(def[0]) });
262
279
  return /* @__PURE__ */ jsxs2(Fragment2, { children: [
263
- /* @__PURE__ */ jsx3(Text3, { color: "#F9FAFB", bold: true, children: def[0] }),
264
- /* @__PURE__ */ jsx3(Text3, { color: "#6B7280", children: ":" }),
265
- /* @__PURE__ */ jsx3(Text3, { color: "#FFD700", dimColor: true, children: t(def[1]) })
280
+ /* @__PURE__ */ jsx3(Text3, { color: COLORS.text, bold: true, children: def[0] }),
281
+ /* @__PURE__ */ jsx3(Text3, { color: COLORS.textSecondary, children: ":" }),
282
+ /* @__PURE__ */ jsx3(Text3, { color: COLORS.gold, dimColor: true, children: t(def[1]) })
266
283
  ] });
267
284
  }
268
285
  function Footer() {
269
286
  const currentView = useNavigationStore((s) => s.currentView);
270
287
  const locale = useLocaleStore((s) => s.locale);
271
288
  const defs = VIEW_HINT_DEFS[currentView] ?? [];
272
- return /* @__PURE__ */ jsxs2(Box2, { borderStyle: "single", borderTop: true, borderBottom: false, borderLeft: false, borderRight: false, borderColor: "#FFD700", paddingX: 1, flexWrap: "wrap", children: [
289
+ return /* @__PURE__ */ jsxs2(Box2, { borderStyle: "single", borderTop: true, borderBottom: false, borderLeft: false, borderRight: false, borderColor: COLORS.gold, paddingX: 1, flexWrap: "wrap", children: [
273
290
  defs.map((def, i) => {
274
291
  const key = def.length === 1 ? def[0] : `${def[0]}:${def[1]}`;
275
292
  return /* @__PURE__ */ jsxs2(React3.Fragment, { children: [
276
- i > 0 && /* @__PURE__ */ jsxs2(Text3, { color: "#4B5563", children: [
293
+ i > 0 && /* @__PURE__ */ jsxs2(Text3, { color: COLORS.border, children: [
277
294
  " ",
278
295
  "\u2502",
279
296
  " "
@@ -281,14 +298,14 @@ function Footer() {
281
298
  /* @__PURE__ */ jsx3(HintItem, { def })
282
299
  ] }, key);
283
300
  }),
284
- /* @__PURE__ */ jsxs2(Text3, { color: "#4B5563", children: [
301
+ /* @__PURE__ */ jsxs2(Text3, { color: COLORS.border, children: [
285
302
  " ",
286
303
  "\u2502",
287
304
  " "
288
305
  ] }),
289
- /* @__PURE__ */ jsx3(Text3, { color: "#F9FAFB", bold: true, children: "L" }),
290
- /* @__PURE__ */ jsx3(Text3, { color: "#6B7280", children: ":" }),
291
- /* @__PURE__ */ jsxs2(Text3, { color: "#FFD700", dimColor: true, children: [
306
+ /* @__PURE__ */ jsx3(Text3, { color: COLORS.text, bold: true, children: "L" }),
307
+ /* @__PURE__ */ jsx3(Text3, { color: COLORS.textSecondary, children: ":" }),
308
+ /* @__PURE__ */ jsxs2(Text3, { color: COLORS.gold, dimColor: true, children: [
292
309
  t("hint_lang"),
293
310
  "(",
294
311
  locale,
@@ -307,12 +324,469 @@ function AppLayout({ children }) {
307
324
  ] });
308
325
  }
309
326
 
327
+ // src/stores/license-store.ts
328
+ import { create as create2 } from "zustand";
329
+
330
+ // src/lib/license/license-manager.ts
331
+ import { readFile as readFile2, writeFile as writeFile2, rename, rm } from "fs/promises";
332
+ import { createCipheriv, createDecipheriv, randomBytes, scryptSync } from "crypto";
333
+
334
+ // src/lib/license/polar-api.ts
335
+ import { randomUUID } from "crypto";
336
+ import { readFile, writeFile, mkdir } from "fs/promises";
337
+ import { join } from "path";
338
+ import { homedir } from "os";
339
+ var BASE_URL = "https://api.polar.sh/v1/customer-portal/license-keys";
340
+ var POLAR_ORGANIZATION_ID = "b8f245c0-d116-4457-92fb-1bda47139f82";
341
+ var DATA_DIR2 = join(homedir(), ".brew-tui");
342
+ var MACHINE_ID_PATH = join(DATA_DIR2, "machine-id");
343
+ async function getMachineId() {
344
+ try {
345
+ const id2 = (await readFile(MACHINE_ID_PATH, "utf-8")).trim();
346
+ if (id2) return id2;
347
+ } catch {
348
+ }
349
+ const id = randomUUID();
350
+ await mkdir(DATA_DIR2, { recursive: true, mode: 448 });
351
+ await writeFile(MACHINE_ID_PATH, id, { encoding: "utf-8", mode: 384 });
352
+ return id;
353
+ }
354
+ function validateApiUrl(url) {
355
+ const parsed = new URL(url);
356
+ if (parsed.protocol !== "https:") {
357
+ throw new Error("HTTPS required for license API");
358
+ }
359
+ if (!parsed.hostname.endsWith("polar.sh")) {
360
+ throw new Error("Invalid API host");
361
+ }
362
+ }
363
+ async function post(endpoint, body, expectEmpty = false) {
364
+ const url = `${BASE_URL}/${endpoint}`;
365
+ validateApiUrl(url);
366
+ const res = await fetchWithTimeout(url, {
367
+ method: "POST",
368
+ headers: { "Content-Type": "application/json" },
369
+ body: JSON.stringify(body)
370
+ }, 15e3);
371
+ if (!res.ok) {
372
+ let message = `Request failed with status ${res.status}`;
373
+ try {
374
+ const errBody = await res.json();
375
+ if (typeof errBody.detail === "string") message = errBody.detail;
376
+ else if (typeof errBody.error === "string") message = errBody.error;
377
+ else if (typeof errBody.message === "string") message = errBody.message;
378
+ } catch {
379
+ }
380
+ throw new Error(message);
381
+ }
382
+ if (expectEmpty || res.status === 204) return void 0;
383
+ return res.json();
384
+ }
385
+ async function activateLicense(key) {
386
+ const machineId = await getMachineId();
387
+ const activation = await post("activate", {
388
+ key,
389
+ organization_id: POLAR_ORGANIZATION_ID,
390
+ label: machineId
391
+ // SEG-004: Use machine UUID instead of hostname
392
+ });
393
+ if (!activation || typeof activation.id !== "string" || !activation.license_key) {
394
+ throw new Error("Invalid activation response: missing required fields");
395
+ }
396
+ let customerEmail = "";
397
+ let customerName = "";
398
+ try {
399
+ const validated = await post("validate", {
400
+ key,
401
+ organization_id: POLAR_ORGANIZATION_ID,
402
+ activation_id: activation.id
403
+ });
404
+ customerEmail = validated.customer?.email ?? "";
405
+ customerName = validated.customer?.name ?? "";
406
+ } catch {
407
+ }
408
+ return {
409
+ activated: true,
410
+ error: null,
411
+ instance: { id: activation.id },
412
+ license_key: {
413
+ id: 0,
414
+ status: activation.license_key.status,
415
+ key,
416
+ activation_limit: 0,
417
+ activations_count: 0,
418
+ expires_at: activation.license_key.expires_at
419
+ },
420
+ meta: { customer_email: customerEmail, customer_name: customerName }
421
+ };
422
+ }
423
+ async function validateLicense(key, instanceId) {
424
+ const res = await post("validate", {
425
+ key,
426
+ organization_id: POLAR_ORGANIZATION_ID,
427
+ activation_id: instanceId
428
+ });
429
+ if (!res || typeof res.id !== "string" || typeof res.status !== "string" || !res.customer) {
430
+ throw new Error("Invalid validation response: missing required fields");
431
+ }
432
+ const notExpired = res.expires_at === null || new Date(res.expires_at) > /* @__PURE__ */ new Date();
433
+ const valid = res.status === "granted" && notExpired;
434
+ return {
435
+ valid,
436
+ error: valid ? null : `License ${res.status}`,
437
+ license_key: {
438
+ id: 0,
439
+ status: res.status,
440
+ key,
441
+ expires_at: res.expires_at
442
+ },
443
+ instance: { id: instanceId }
444
+ };
445
+ }
446
+ async function deactivateLicense(key, instanceId) {
447
+ await post(
448
+ "deactivate",
449
+ { key, organization_id: POLAR_ORGANIZATION_ID, activation_id: instanceId },
450
+ true
451
+ );
452
+ }
453
+
454
+ // src/lib/license/license-manager.ts
455
+ var REVALIDATION_INTERVAL_MS = 24 * 60 * 60 * 1e3;
456
+ var GRACE_PERIOD_MS = 7 * 24 * 60 * 60 * 1e3;
457
+ var ACTIVATION_COOLDOWN_MS = 3e4;
458
+ var MAX_ATTEMPTS = 5;
459
+ var LOCKOUT_MS = 15 * 60 * 1e3;
460
+ var tracker = {
461
+ attempts: 0,
462
+ lastAttempt: 0,
463
+ lockedUntil: 0
464
+ };
465
+ function checkRateLimit() {
466
+ const now = Date.now();
467
+ if (now < tracker.lockedUntil) {
468
+ const remaining = Math.ceil((tracker.lockedUntil - now) / 6e4);
469
+ throw new Error(t("cli_rateLimited", { minutes: remaining }));
470
+ }
471
+ if (now - tracker.lastAttempt < ACTIVATION_COOLDOWN_MS) {
472
+ throw new Error(t("cli_cooldown"));
473
+ }
474
+ }
475
+ function recordAttempt(success) {
476
+ const now = Date.now();
477
+ tracker.lastAttempt = now;
478
+ if (success) {
479
+ tracker.attempts = 0;
480
+ return;
481
+ }
482
+ tracker.attempts++;
483
+ if (tracker.attempts >= MAX_ATTEMPTS) {
484
+ tracker.lockedUntil = now + LOCKOUT_MS;
485
+ tracker.attempts = 0;
486
+ }
487
+ }
488
+ var ENCRYPTION_SECRET = "brew-tui-license-aes256gcm-v1";
489
+ var SCRYPT_SALT = "brew-tui-salt-v1";
490
+ var _derivedKey = null;
491
+ function deriveEncryptionKey() {
492
+ if (!_derivedKey) _derivedKey = scryptSync(ENCRYPTION_SECRET, SCRYPT_SALT, 32);
493
+ return _derivedKey;
494
+ }
495
+ function encryptLicenseData(data) {
496
+ const key = deriveEncryptionKey();
497
+ const iv = randomBytes(12);
498
+ const cipher = createCipheriv("aes-256-gcm", key, iv);
499
+ const plaintext = JSON.stringify(data);
500
+ const ciphertext = Buffer.concat([cipher.update(plaintext, "utf-8"), cipher.final()]);
501
+ const tag = cipher.getAuthTag();
502
+ return {
503
+ encrypted: ciphertext.toString("base64"),
504
+ iv: iv.toString("base64"),
505
+ tag: tag.toString("base64")
506
+ };
507
+ }
508
+ function decryptLicenseData(encrypted, iv, tag) {
509
+ const key = deriveEncryptionKey();
510
+ const decipher = createDecipheriv(
511
+ "aes-256-gcm",
512
+ key,
513
+ Buffer.from(iv, "base64")
514
+ );
515
+ decipher.setAuthTag(Buffer.from(tag, "base64"));
516
+ const plaintext = Buffer.concat([
517
+ decipher.update(Buffer.from(encrypted, "base64")),
518
+ decipher.final()
519
+ ]);
520
+ return JSON.parse(plaintext.toString("utf-8"));
521
+ }
522
+ function isLicenseFile(obj) {
523
+ return typeof obj === "object" && obj !== null && obj.version === 1;
524
+ }
525
+ function isEncryptedLicenseFile(obj) {
526
+ if (!isLicenseFile(obj)) return false;
527
+ const record = obj;
528
+ return typeof record.encrypted === "string" && typeof record.iv === "string" && typeof record.tag === "string";
529
+ }
530
+ async function getMachineId2() {
531
+ try {
532
+ const { readFile: readMachineId } = await import("fs/promises");
533
+ const { join: join3 } = await import("path");
534
+ const { homedir: homedir2 } = await import("os");
535
+ const machineIdPath = join3(homedir2(), ".brew-tui", "machine-id");
536
+ return (await readMachineId(machineIdPath, "utf-8")).trim() || null;
537
+ } catch {
538
+ return null;
539
+ }
540
+ }
541
+ async function loadLicense() {
542
+ try {
543
+ const raw = await readFile2(LICENSE_PATH, "utf-8");
544
+ const parsed = JSON.parse(raw);
545
+ if (!isLicenseFile(parsed)) {
546
+ throw new Error("Invalid license data format");
547
+ }
548
+ const file = parsed;
549
+ if (file.version !== 1) {
550
+ throw new Error("Unsupported data version");
551
+ }
552
+ if (isEncryptedLicenseFile(file)) {
553
+ const data = decryptLicenseData(file.encrypted, file.iv, file.tag);
554
+ const fileRecord = file;
555
+ if (fileRecord.machineId) {
556
+ const currentMachineId = await getMachineId2();
557
+ if (currentMachineId && fileRecord.machineId !== currentMachineId) {
558
+ throw new Error("License was activated on a different machine");
559
+ }
560
+ }
561
+ return data;
562
+ }
563
+ if (file.license) {
564
+ const data = file.license;
565
+ await saveLicense(data);
566
+ return data;
567
+ }
568
+ return null;
569
+ } catch {
570
+ return null;
571
+ }
572
+ }
573
+ async function saveLicense(data) {
574
+ await ensureDataDirs();
575
+ const { encrypted, iv, tag } = encryptLicenseData(data);
576
+ const machineId = await getMachineId2();
577
+ const file = { version: 1, encrypted, iv, tag };
578
+ if (machineId) file.machineId = machineId;
579
+ const tmpPath = LICENSE_PATH + ".tmp";
580
+ await writeFile2(tmpPath, JSON.stringify(file, null, 2), { encoding: "utf-8", mode: 384 });
581
+ await rename(tmpPath, LICENSE_PATH);
582
+ }
583
+ async function clearLicense() {
584
+ try {
585
+ await rm(LICENSE_PATH);
586
+ } catch {
587
+ }
588
+ }
589
+ function isExpired(license) {
590
+ if (!license.expiresAt) return false;
591
+ return new Date(license.expiresAt).getTime() < Date.now();
592
+ }
593
+ function needsRevalidation(license) {
594
+ const lastValidated = new Date(license.lastValidatedAt).getTime();
595
+ if (isNaN(lastValidated)) return true;
596
+ return Date.now() - lastValidated > REVALIDATION_INTERVAL_MS;
597
+ }
598
+ function isWithinGracePeriod(license) {
599
+ const lastValidated = new Date(license.lastValidatedAt).getTime();
600
+ if (isNaN(lastValidated)) return false;
601
+ return Date.now() - lastValidated < GRACE_PERIOD_MS;
602
+ }
603
+ function getDegradationLevel(license) {
604
+ const lastValidated = new Date(license.lastValidatedAt).getTime();
605
+ if (isNaN(lastValidated)) return "expired";
606
+ const elapsed = Date.now() - lastValidated;
607
+ if (elapsed < 0) return "none";
608
+ const days = elapsed / (24 * 60 * 60 * 1e3);
609
+ if (days <= 7) return "none";
610
+ if (days <= 14) return "warning";
611
+ if (days <= 30) return "limited";
612
+ return "expired";
613
+ }
614
+ function validateLicenseKey(key) {
615
+ if (key.length < 10 || key.length > 100) {
616
+ throw new Error("Invalid license key format");
617
+ }
618
+ if (!/^[\w-]+$/.test(key)) {
619
+ throw new Error("Invalid license key format");
620
+ }
621
+ }
622
+ async function activate(key) {
623
+ validateLicenseKey(key);
624
+ checkRateLimit();
625
+ let success = false;
626
+ try {
627
+ const res = await activateLicense(key);
628
+ if (!res.activated) {
629
+ throw new Error(res.error ?? "Activation failed");
630
+ }
631
+ const license = {
632
+ key,
633
+ instanceId: res.instance.id,
634
+ status: "active",
635
+ customerEmail: res.meta.customer_email,
636
+ customerName: res.meta.customer_name,
637
+ plan: "pro",
638
+ activatedAt: (/* @__PURE__ */ new Date()).toISOString(),
639
+ expiresAt: res.license_key.expires_at,
640
+ lastValidatedAt: (/* @__PURE__ */ new Date()).toISOString()
641
+ };
642
+ await saveLicense(license);
643
+ success = true;
644
+ return license;
645
+ } finally {
646
+ recordAttempt(success);
647
+ }
648
+ }
649
+ function isNetworkError(err) {
650
+ const msg = err instanceof Error ? err.message : String(err);
651
+ return /fetch failed|ECONNREFUSED|ENOTFOUND|ETIMEDOUT|network|timeout|abort/i.test(msg);
652
+ }
653
+ async function revalidate(license) {
654
+ try {
655
+ const res = await validateLicense(license.key, license.instanceId);
656
+ if (res.valid) {
657
+ const updated = {
658
+ ...license,
659
+ lastValidatedAt: (/* @__PURE__ */ new Date()).toISOString(),
660
+ status: "active",
661
+ expiresAt: res.license_key.expires_at
662
+ };
663
+ await saveLicense(updated);
664
+ return "valid";
665
+ }
666
+ await saveLicense({ ...license, status: "expired" });
667
+ return "expired";
668
+ } catch (err) {
669
+ if (isNetworkError(err)) {
670
+ return isWithinGracePeriod(license) ? "grace" : "expired";
671
+ }
672
+ await saveLicense({ ...license, status: "expired" });
673
+ return "expired";
674
+ }
675
+ }
676
+ async function deactivate(license) {
677
+ let remoteSuccess = false;
678
+ for (let attempt = 0; attempt < 3; attempt++) {
679
+ try {
680
+ await deactivateLicense(license.key, license.instanceId);
681
+ remoteSuccess = true;
682
+ break;
683
+ } catch {
684
+ if (attempt < 2) await new Promise((r) => setTimeout(r, 1e3));
685
+ }
686
+ }
687
+ await clearLicense();
688
+ return { remoteSuccess };
689
+ }
690
+
691
+ // src/lib/license/anti-tamper.ts
692
+ var _originalIsPro = null;
693
+ var _originalGetState = null;
694
+ var _storeApi = null;
695
+ function initStoreIntegrity(store) {
696
+ _storeApi = store;
697
+ _originalIsPro = store.getState().isPro;
698
+ _originalGetState = store.getState;
699
+ }
700
+
701
+ // src/stores/license-store.ts
702
+ var REVALIDATION_CHECK_MS = 60 * 60 * 1e3;
703
+ var _revalidatingPromise = null;
704
+ var _revalidationInterval = null;
705
+ async function doRevalidation(license, set) {
706
+ const result = await revalidate(license);
707
+ if (result === "expired") {
708
+ set({ status: "expired", license: { ...license, status: "expired" }, degradation: "expired" });
709
+ } else {
710
+ const updated = await loadLicense();
711
+ const effective = updated ?? license;
712
+ set({ license: effective, degradation: getDegradationLevel(effective) });
713
+ }
714
+ }
715
+ var useLicenseStore = create2((set, get) => ({
716
+ status: "validating",
717
+ license: null,
718
+ error: null,
719
+ degradation: "none",
720
+ initialize: async () => {
721
+ initStoreIntegrity(useLicenseStore);
722
+ await ensureDataDirs();
723
+ const license = await loadLicense();
724
+ if (!license) {
725
+ set({ status: "free", license: null, degradation: "none" });
726
+ return;
727
+ }
728
+ if (isExpired(license)) {
729
+ set({ status: "expired", license, degradation: "expired" });
730
+ return;
731
+ }
732
+ const level = getDegradationLevel(license);
733
+ if (level === "expired") {
734
+ set({ status: "expired", license, degradation: "expired" });
735
+ return;
736
+ }
737
+ set({ status: "pro", license, degradation: level });
738
+ if (needsRevalidation(license)) {
739
+ if (!_revalidatingPromise) {
740
+ _revalidatingPromise = doRevalidation(license, set).finally(() => {
741
+ _revalidatingPromise = null;
742
+ });
743
+ }
744
+ await _revalidatingPromise;
745
+ }
746
+ if (_revalidationInterval) clearInterval(_revalidationInterval);
747
+ _revalidationInterval = setInterval(() => {
748
+ const current = get().license;
749
+ if (!current || get().status !== "pro") return;
750
+ if (!needsRevalidation(current)) return;
751
+ if (_revalidatingPromise) return;
752
+ _revalidatingPromise = doRevalidation(current, set).finally(() => {
753
+ _revalidatingPromise = null;
754
+ });
755
+ }, REVALIDATION_CHECK_MS);
756
+ _revalidationInterval.unref();
757
+ },
758
+ activate: async (key) => {
759
+ set({ error: null });
760
+ try {
761
+ const license = await activate(key);
762
+ set({ status: "pro", license, degradation: "none" });
763
+ return true;
764
+ } catch (err) {
765
+ const msg = err instanceof Error ? err.message : String(err);
766
+ set({ error: msg });
767
+ return false;
768
+ }
769
+ },
770
+ deactivate: async () => {
771
+ const { license } = get();
772
+ if (license) {
773
+ const { remoteSuccess } = await deactivate(license);
774
+ if (!remoteSuccess) {
775
+ set({ status: "free", license: null, degradation: "none", error: "License removed locally but server deactivation failed. It may remain active remotely." });
776
+ return;
777
+ }
778
+ }
779
+ set({ status: "free", license: null, degradation: "none", error: null });
780
+ },
781
+ isPro: () => get().status === "pro"
782
+ }));
783
+
310
784
  // src/hooks/use-keyboard.ts
311
785
  import { useInput } from "ink";
312
786
 
313
787
  // src/stores/modal-store.ts
314
- import { create as create2 } from "zustand";
315
- var useModalStore = create2((set) => ({
788
+ import { create as create3 } from "zustand";
789
+ var useModalStore = create3((set) => ({
316
790
  _count: 0,
317
791
  isOpen: false,
318
792
  openModal: () => set((s) => {
@@ -390,37 +864,37 @@ function UpgradePrompt({ viewId }) {
390
864
  Box4,
391
865
  {
392
866
  borderStyle: "double",
393
- borderColor: "#FF6B2B",
867
+ borderColor: COLORS.brand,
394
868
  paddingX: 3,
395
869
  paddingY: 2,
396
870
  flexDirection: "column",
397
871
  alignItems: "center",
398
872
  width: "80%",
399
873
  children: [
400
- /* @__PURE__ */ jsxs4(Text4, { bold: true, color: "#FF6B2B", children: [
874
+ /* @__PURE__ */ jsxs4(Text4, { bold: true, color: COLORS.brand, children: [
401
875
  "\u2B50",
402
876
  " ",
403
877
  t("upgrade_proFeature", { title })
404
878
  ] }),
405
879
  /* @__PURE__ */ jsx5(Text4, { children: " " }),
406
- /* @__PURE__ */ jsx5(Text4, { color: "#F9FAFB", wrap: "wrap", children: t(keys.desc) }),
880
+ /* @__PURE__ */ jsx5(Text4, { color: COLORS.text, wrap: "wrap", children: t(keys.desc) }),
407
881
  /* @__PURE__ */ jsx5(Text4, { children: " " }),
408
882
  /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", alignItems: "center", children: [
409
- /* @__PURE__ */ jsx5(Text4, { color: "#06B6D4", bold: true, children: t("upgrade_pricing") }),
883
+ /* @__PURE__ */ jsx5(Text4, { color: COLORS.info, bold: true, children: t("upgrade_pricing") }),
410
884
  /* @__PURE__ */ jsx5(Text4, { children: " " }),
411
- /* @__PURE__ */ jsx5(Text4, { color: "#9CA3AF", children: t("upgrade_buyAt") }),
412
- /* @__PURE__ */ jsxs4(Text4, { color: "#38BDF8", bold: true, children: [
885
+ /* @__PURE__ */ jsx5(Text4, { color: COLORS.muted, children: t("upgrade_buyAt") }),
886
+ /* @__PURE__ */ jsxs4(Text4, { color: COLORS.sky, bold: true, children: [
413
887
  " ",
414
888
  t("upgrade_buyUrl")
415
889
  ] }),
416
890
  /* @__PURE__ */ jsx5(Text4, { children: " " }),
417
- /* @__PURE__ */ jsx5(Text4, { color: "#9CA3AF", children: t("upgrade_activateWith") }),
418
- /* @__PURE__ */ jsxs4(Text4, { color: "#22C55E", bold: true, children: [
891
+ /* @__PURE__ */ jsx5(Text4, { color: COLORS.muted, children: t("upgrade_activateWith") }),
892
+ /* @__PURE__ */ jsxs4(Text4, { color: COLORS.success, bold: true, children: [
419
893
  " ",
420
894
  t("upgrade_activateCmd")
421
895
  ] }),
422
896
  /* @__PURE__ */ jsx5(Text4, { children: " " }),
423
- /* @__PURE__ */ jsx5(Text4, { color: "#FF6B2B", children: t("upgrade_proLabel") })
897
+ /* @__PURE__ */ jsx5(Text4, { color: COLORS.brand, children: t("upgrade_proLabel") })
424
898
  ] })
425
899
  ]
426
900
  }
@@ -429,21 +903,29 @@ function UpgradePrompt({ viewId }) {
429
903
 
430
904
  // src/views/dashboard.tsx
431
905
  import { useEffect, useMemo as useMemo2 } from "react";
432
- import { Box as Box8, Text as Text10 } from "ink";
906
+ import { Box as Box8, Text as Text10, useStdout } from "ink";
433
907
 
434
908
  // src/stores/brew-store.ts
435
- import { create as create3 } from "zustand";
909
+ import { create as create4 } from "zustand";
436
910
 
437
911
  // src/lib/brew-api.ts
438
912
  import { spawn as spawn2 } from "child_process";
439
913
 
440
914
  // src/lib/brew-cli.ts
441
915
  import { spawn } from "child_process";
442
- async function execBrew(args) {
916
+ var DEFAULT_TIMEOUT_MS = 3e4;
917
+ var STREAM_IDLE_TIMEOUT_MS = 5 * 60 * 1e3;
918
+ async function execBrew(args, timeoutMs = DEFAULT_TIMEOUT_MS) {
443
919
  return new Promise((resolve, reject) => {
444
920
  const proc = spawn("brew", args, { env: { ...process.env, HOMEBREW_NO_AUTO_UPDATE: "1" } });
445
921
  let stdout = "";
446
922
  let stderr = "";
923
+ let killed = false;
924
+ const timer = setTimeout(() => {
925
+ killed = true;
926
+ proc.kill();
927
+ reject(new Error(`brew ${args.join(" ")} timed out after ${timeoutMs}ms`));
928
+ }, timeoutMs);
447
929
  proc.stdout.on("data", (d) => {
448
930
  stdout += d.toString();
449
931
  });
@@ -451,6 +933,8 @@ async function execBrew(args) {
451
933
  stderr += d.toString();
452
934
  });
453
935
  proc.on("close", (code) => {
936
+ clearTimeout(timer);
937
+ if (killed) return;
454
938
  if (code === 0) {
455
939
  resolve(stdout);
456
940
  } else {
@@ -458,6 +942,8 @@ async function execBrew(args) {
458
942
  }
459
943
  });
460
944
  proc.on("error", (err) => {
945
+ clearTimeout(timer);
946
+ if (killed) return;
461
947
  reject(new Error(`Failed to run brew: ${err.message}`));
462
948
  });
463
949
  });
@@ -471,7 +957,9 @@ async function* streamBrew(args) {
471
957
  const lines = [];
472
958
  let done = false;
473
959
  let exitError = null;
960
+ let lastOutputAt = Date.now();
474
961
  const push = (chunk) => {
962
+ lastOutputAt = Date.now();
475
963
  buffer += chunk.toString();
476
964
  const parts = buffer.split("\n");
477
965
  buffer = parts.pop() ?? "";
@@ -497,6 +985,10 @@ async function* streamBrew(args) {
497
985
  if (lines.length > 0) {
498
986
  yield lines.shift();
499
987
  } else if (!done) {
988
+ if (Date.now() - lastOutputAt > STREAM_IDLE_TIMEOUT_MS) {
989
+ proc.kill();
990
+ throw new Error(`brew ${args.join(" ")} timed out: no output for ${STREAM_IDLE_TIMEOUT_MS / 1e3}s`);
991
+ }
500
992
  await new Promise((r) => setTimeout(r, 100));
501
993
  }
502
994
  }
@@ -608,6 +1100,10 @@ function parseLeavesOutput(raw) {
608
1100
  }
609
1101
 
610
1102
  // src/lib/brew-api.ts
1103
+ var PKG_PATTERN = /^[\w@./+-]+$/;
1104
+ function validatePackageName(name) {
1105
+ if (!PKG_PATTERN.test(name)) throw new Error("Invalid package name: " + name);
1106
+ }
611
1107
  async function brewUpdate() {
612
1108
  return new Promise((resolve, reject) => {
613
1109
  const proc = spawn2("brew", ["update"], { stdio: "ignore" });
@@ -631,9 +1127,20 @@ async function getServices() {
631
1127
  return parseServicesJson(raw);
632
1128
  }
633
1129
  async function getFormulaInfo(name) {
1130
+ validatePackageName(name);
634
1131
  const raw = await execBrew(["info", "--json=v2", name]);
635
1132
  return parseFormulaInfoJson(raw);
636
1133
  }
1134
+ async function getCaskInfo(name) {
1135
+ validatePackageName(name);
1136
+ try {
1137
+ const raw = await execBrew(["info", "--json=v2", "--cask", name]);
1138
+ const data = JSON.parse(raw);
1139
+ return data.casks?.[0] ?? null;
1140
+ } catch {
1141
+ return null;
1142
+ }
1143
+ }
637
1144
  async function search(term) {
638
1145
  const safeTerm = term.replace(/^-+/, "");
639
1146
  if (!safeTerm) return { formulae: [], casks: [] };
@@ -658,11 +1165,21 @@ async function getLeaves() {
658
1165
  return parseLeavesOutput(raw);
659
1166
  }
660
1167
  async function uninstallPackage(name) {
1168
+ validatePackageName(name);
661
1169
  return execBrew(["uninstall", name]);
662
1170
  }
663
1171
  async function serviceAction(name, action) {
1172
+ validatePackageName(name);
664
1173
  return execBrew(["services", action, name]);
665
1174
  }
1175
+ async function pinPackage(name) {
1176
+ validatePackageName(name);
1177
+ return execBrew(["pin", name]);
1178
+ }
1179
+ async function unpinPackage(name) {
1180
+ validatePackageName(name);
1181
+ return execBrew(["unpin", name]);
1182
+ }
666
1183
  function formulaeToListItems(formulae) {
667
1184
  return formulae.map((f) => {
668
1185
  const installed = f.installed[0];
@@ -694,6 +1211,10 @@ function casksToListItems(casks) {
694
1211
  }
695
1212
 
696
1213
  // src/stores/brew-store.ts
1214
+ var BREW_UPDATE_COOLDOWN_MS = 5 * 60 * 1e3;
1215
+ var fetchAllInFlight = null;
1216
+ var brewUpdateInFlight = null;
1217
+ var lastBrewUpdateStartedAt = 0;
697
1218
  function setLoading(set, key, value) {
698
1219
  set((s) => ({ loading: { ...s.loading, [key]: value } }));
699
1220
  }
@@ -703,7 +1224,7 @@ function setError(set, key, error) {
703
1224
  function recordFetchTime(set, key) {
704
1225
  set((s) => ({ lastFetchedAt: { ...s.lastFetchedAt, [key]: Date.now() } }));
705
1226
  }
706
- var useBrewStore = create3((set) => ({
1227
+ var useBrewStore = create4((set, get) => ({
707
1228
  formulae: [],
708
1229
  casks: [],
709
1230
  outdated: { formulae: [], casks: [] },
@@ -715,7 +1236,8 @@ var useBrewStore = create3((set) => ({
715
1236
  // Pre-initialize loading flags for keys that fetchAll always triggers so
716
1237
  // views that check loading.X get a spinner on first render rather than
717
1238
  // flashing empty/zeroed content for one frame before the fetch starts.
718
- loading: { installed: true, outdated: true, services: true, config: true, doctor: false },
1239
+ // SCR-013: Pre-initialize doctor loading to true
1240
+ loading: { installed: true, outdated: true, services: true, config: true, doctor: true },
719
1241
  errors: {},
720
1242
  lastFetchedAt: {},
721
1243
  fetchInstalled: async () => {
@@ -774,9 +1296,7 @@ var useBrewStore = create3((set) => ({
774
1296
  const result = await getLeaves();
775
1297
  set({ leaves: result });
776
1298
  } catch (err) {
777
- if (false) {
778
- console.error("[brew-store] fetchLeaves failed:", err instanceof Error ? err.message : String(err));
779
- }
1299
+ logger.error("fetchLeaves failed", { error: err instanceof Error ? err.message : String(err) });
780
1300
  } finally {
781
1301
  recordFetchTime(set, "leaves");
782
1302
  }
@@ -795,22 +1315,36 @@ var useBrewStore = create3((set) => ({
795
1315
  }
796
1316
  },
797
1317
  fetchAll: async () => {
798
- brewUpdate().catch(() => {
799
- });
800
- const store = useBrewStore.getState();
801
- await Promise.all([
1318
+ if (fetchAllInFlight) {
1319
+ return fetchAllInFlight;
1320
+ }
1321
+ const now = Date.now();
1322
+ if (!brewUpdateInFlight && now - lastBrewUpdateStartedAt > BREW_UPDATE_COOLDOWN_MS) {
1323
+ lastBrewUpdateStartedAt = now;
1324
+ brewUpdateInFlight = brewUpdate().catch((err) => {
1325
+ set((s) => ({ errors: { ...s.errors, update: String(err) } }));
1326
+ }).finally(() => {
1327
+ brewUpdateInFlight = null;
1328
+ });
1329
+ }
1330
+ const store = get();
1331
+ fetchAllInFlight = Promise.all([
802
1332
  store.fetchInstalled(),
803
1333
  store.fetchOutdated(),
804
1334
  store.fetchServices(),
805
1335
  store.fetchConfig(),
806
- store.fetchLeaves()
807
- ]);
1336
+ store.fetchLeaves(),
1337
+ store.fetchDoctor()
1338
+ ]).then(() => void 0).finally(() => {
1339
+ fetchAllInFlight = null;
1340
+ });
1341
+ return fetchAllInFlight;
808
1342
  },
809
1343
  uninstallPackage: async (name) => {
810
1344
  setLoading(set, "action", true);
811
1345
  try {
812
1346
  await uninstallPackage(name);
813
- await useBrewStore.getState().fetchInstalled();
1347
+ await get().fetchInstalled();
814
1348
  } catch (err) {
815
1349
  setError(set, "action", err instanceof Error ? err.message : String(err));
816
1350
  } finally {
@@ -821,7 +1355,7 @@ var useBrewStore = create3((set) => ({
821
1355
  setLoading(set, "service-action", true);
822
1356
  try {
823
1357
  await serviceAction(name, action);
824
- await useBrewStore.getState().fetchServices();
1358
+ await get().fetchServices();
825
1359
  } catch (err) {
826
1360
  setError(set, "service-action", err instanceof Error ? err.message : String(err));
827
1361
  } finally {
@@ -846,7 +1380,7 @@ function StatCard({ label, value, color = "white" }) {
846
1380
  minWidth: 16,
847
1381
  children: [
848
1382
  /* @__PURE__ */ jsx6(Text5, { bold: true, color, children: value }),
849
- /* @__PURE__ */ jsx6(Text5, { color: "#9CA3AF", children: label })
1383
+ /* @__PURE__ */ jsx6(Text5, { color: COLORS.muted, children: label })
850
1384
  ]
851
1385
  }
852
1386
  );
@@ -863,12 +1397,12 @@ function Loading({ message }) {
863
1397
  function ErrorMessage({ message }) {
864
1398
  useLocaleStore((s) => s.locale);
865
1399
  return /* @__PURE__ */ jsxs6(Box6, { paddingY: 1, children: [
866
- /* @__PURE__ */ jsxs6(Text6, { color: "#EF4444", bold: true, children: [
1400
+ /* @__PURE__ */ jsxs6(Text6, { color: COLORS.error, bold: true, children: [
867
1401
  "\u2718",
868
1402
  " ",
869
1403
  t("error_prefix")
870
1404
  ] }),
871
- /* @__PURE__ */ jsx7(Text6, { color: "#EF4444", children: message })
1405
+ /* @__PURE__ */ jsx7(Text6, { color: COLORS.error, children: message })
872
1406
  ] });
873
1407
  }
874
1408
 
@@ -876,11 +1410,11 @@ function ErrorMessage({ message }) {
876
1410
  import { Text as Text7 } from "ink";
877
1411
  import { jsxs as jsxs7 } from "react/jsx-runtime";
878
1412
  var BADGE_STYLES = {
879
- success: { icon: "\u2714", color: "#22C55E" },
880
- warning: { icon: "\u25CF", color: "#F59E0B" },
881
- error: { icon: "\u2718", color: "#EF4444" },
882
- info: { icon: "\u25C6", color: "#3B82F6" },
883
- muted: { icon: "\u25CB", color: "#6B7280" }
1413
+ success: { icon: "\u2714", color: COLORS.success },
1414
+ warning: { icon: "\u25CF", color: COLORS.warning },
1415
+ error: { icon: "\u2718", color: COLORS.error },
1416
+ info: { icon: "\u25C6", color: COLORS.blue },
1417
+ muted: { icon: "\u25CB", color: COLORS.textSecondary }
884
1418
  };
885
1419
  function StatusBadge({ label, variant }) {
886
1420
  const { icon, color } = BADGE_STYLES[variant];
@@ -894,11 +1428,11 @@ function StatusBadge({ label, variant }) {
894
1428
  // src/components/common/section-header.tsx
895
1429
  import { Box as Box7, Text as Text8 } from "ink";
896
1430
  import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
897
- function SectionHeader({ emoji, title, color = "#FFD700", gradient, count }) {
1431
+ function SectionHeader({ emoji, title, color = COLORS.gold, gradient, count }) {
898
1432
  return /* @__PURE__ */ jsxs8(Box7, { gap: 1, children: [
899
1433
  /* @__PURE__ */ jsx8(Text8, { children: emoji }),
900
1434
  gradient ? /* @__PURE__ */ jsx8(GradientText, { colors: gradient, bold: true, children: title }) : /* @__PURE__ */ jsx8(Text8, { bold: true, color, children: title }),
901
- count !== void 0 && /* @__PURE__ */ jsxs8(Text8, { color: "#6B7280", children: [
1435
+ count !== void 0 && /* @__PURE__ */ jsxs8(Text8, { color: COLORS.textSecondary, children: [
902
1436
  "(",
903
1437
  count,
904
1438
  ")"
@@ -911,20 +1445,55 @@ import { Text as Text9 } from "ink";
911
1445
  import { Fragment as Fragment3, jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
912
1446
  function VersionArrow({ current, latest }) {
913
1447
  return /* @__PURE__ */ jsxs9(Fragment3, { children: [
914
- /* @__PURE__ */ jsx9(Text9, { color: "#EF4444", children: current }),
915
- /* @__PURE__ */ jsx9(Text9, { color: "#F59E0B", children: " \u2500\u2500 " }),
916
- /* @__PURE__ */ jsx9(Text9, { color: "#FFD700", children: "\u25B6" }),
917
- /* @__PURE__ */ jsxs9(Text9, { color: "#2DD4BF", children: [
1448
+ /* @__PURE__ */ jsxs9(Text9, { color: COLORS.muted, children: [
1449
+ t("version_installed"),
1450
+ " "
1451
+ ] }),
1452
+ /* @__PURE__ */ jsx9(Text9, { color: COLORS.error, children: current }),
1453
+ /* @__PURE__ */ jsx9(Text9, { color: COLORS.warning, children: " \u2500\u2500 " }),
1454
+ /* @__PURE__ */ jsx9(Text9, { color: COLORS.gold, children: "\u25B6" }),
1455
+ /* @__PURE__ */ jsxs9(Text9, { color: COLORS.muted, children: [
918
1456
  " ",
919
- latest
920
- ] })
1457
+ t("version_available"),
1458
+ " "
1459
+ ] }),
1460
+ /* @__PURE__ */ jsx9(Text9, { color: COLORS.teal, children: latest })
921
1461
  ] });
922
1462
  }
923
1463
 
1464
+ // src/utils/format.ts
1465
+ function formatBytes(bytes) {
1466
+ if (!isFinite(bytes) || bytes <= 0) return "0 B";
1467
+ const units = ["B", "KB", "MB", "GB", "TB"];
1468
+ const i = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1);
1469
+ return `${(bytes / Math.pow(1024, i)).toFixed(1)} ${units[i]}`;
1470
+ }
1471
+ function formatRelativeTime(timestamp) {
1472
+ if (!timestamp || !isFinite(timestamp)) return t("time_justNow");
1473
+ const diff = Date.now() / 1e3 - timestamp;
1474
+ if (diff < 0) return t("time_justNow");
1475
+ if (diff < 60) return t("time_justNow");
1476
+ if (diff < 3600) return t("time_minutesAgo", { n: Math.floor(diff / 60) });
1477
+ if (diff < 86400) return t("time_hoursAgo", { n: Math.floor(diff / 3600) });
1478
+ if (diff < 2592e3) return t("time_daysAgo", { n: Math.floor(diff / 86400) });
1479
+ return t("time_monthsAgo", { n: Math.floor(diff / 2592e3) });
1480
+ }
1481
+ function formatDate(value) {
1482
+ const date = value instanceof Date ? value : new Date(value);
1483
+ const locale = getLocale();
1484
+ return date.toLocaleDateString(locale === "es" ? "es-ES" : "en-US");
1485
+ }
1486
+ function truncate(str, maxLen) {
1487
+ if (str.length <= maxLen) return str;
1488
+ return str.slice(0, maxLen - 1) + "\u2026";
1489
+ }
1490
+
924
1491
  // src/views/dashboard.tsx
925
1492
  import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
926
1493
  function DashboardView() {
927
- const { formulae, casks, outdated, services, config, loading, errors, fetchAll } = useBrewStore();
1494
+ const { formulae, casks, outdated, services, config, loading, errors, lastFetchedAt, fetchAll } = useBrewStore();
1495
+ const { stdout } = useStdout();
1496
+ const columns = stdout?.columns ?? 80;
928
1497
  useEffect(() => {
929
1498
  fetchAll();
930
1499
  }, []);
@@ -937,66 +1506,84 @@ function DashboardView() {
937
1506
  [services]
938
1507
  );
939
1508
  const errorServices = errorServiceList.length;
1509
+ const partialErrors = [
1510
+ errors.outdated ? { label: t("dashboard_outdated"), message: errors.outdated } : null,
1511
+ errors.services ? { label: t("dashboard_services"), message: errors.services } : null,
1512
+ errors.config ? { label: t("dashboard_systemInfo"), message: errors.config } : null
1513
+ ].filter((item) => item !== null);
1514
+ const outdatedValue = loading.outdated ? "..." : errors.outdated ? t("dashboard_statError") : outdated.formulae.length + outdated.casks.length;
1515
+ const servicesValue = loading.services ? "..." : errors.services ? t("dashboard_statError") : `${runningServices}/${services.length}`;
1516
+ const lastUpdated = lastFetchedAt.installed ? formatRelativeTime(lastFetchedAt.installed / 1e3) : null;
940
1517
  if (loading.installed) return /* @__PURE__ */ jsx10(Loading, { message: t("loading_fetchingBrew") });
941
1518
  if (errors.installed) return /* @__PURE__ */ jsx10(ErrorMessage, { message: errors.installed });
1519
+ const isNarrow = columns < 60;
942
1520
  return /* @__PURE__ */ jsxs10(Box8, { flexDirection: "column", gap: 2, children: [
943
1521
  /* @__PURE__ */ jsx10(SectionHeader, { emoji: "\u{1F4CA}", title: t("dashboard_overview"), gradient: GRADIENTS.gold }),
944
- /* @__PURE__ */ jsxs10(Box8, { gap: 1, flexWrap: "wrap", children: [
945
- /* @__PURE__ */ jsx10(StatCard, { label: t("dashboard_formulae"), value: formulae.length, color: "#06B6D4" }),
946
- /* @__PURE__ */ jsx10(StatCard, { label: t("dashboard_casks"), value: casks.length, color: "#A855F7" }),
1522
+ /* @__PURE__ */ jsxs10(Box8, { gap: 1, flexWrap: "wrap", flexDirection: isNarrow ? "column" : "row", children: [
1523
+ /* @__PURE__ */ jsx10(StatCard, { label: t("dashboard_formulae"), value: formulae.length, color: COLORS.info }),
1524
+ /* @__PURE__ */ jsx10(StatCard, { label: t("dashboard_casks"), value: casks.length, color: COLORS.purple }),
947
1525
  /* @__PURE__ */ jsx10(
948
1526
  StatCard,
949
1527
  {
950
1528
  label: t("dashboard_outdated"),
951
- value: outdated.formulae.length + outdated.casks.length,
952
- color: outdated.formulae.length + outdated.casks.length > 0 ? "#F59E0B" : "#22C55E"
1529
+ value: outdatedValue,
1530
+ color: typeof outdatedValue === "number" && outdatedValue > 0 ? COLORS.warning : errors.outdated ? COLORS.error : COLORS.success
953
1531
  }
954
1532
  ),
955
1533
  /* @__PURE__ */ jsx10(
956
1534
  StatCard,
957
1535
  {
958
1536
  label: t("dashboard_services"),
959
- value: `${runningServices}/${services.length}`,
960
- color: errorServices > 0 ? "#EF4444" : "#22C55E"
1537
+ value: servicesValue,
1538
+ color: errors.services || errorServices > 0 ? COLORS.error : COLORS.success
961
1539
  }
962
1540
  )
963
1541
  ] }),
964
- config && /* @__PURE__ */ jsxs10(Box8, { flexDirection: "column", children: [
965
- /* @__PURE__ */ jsx10(SectionHeader, { emoji: "\u2139\uFE0F", title: t("dashboard_systemInfo"), gradient: ["#F9FAFB", "#9CA3AF"] }),
966
- /* @__PURE__ */ jsxs10(Box8, { borderStyle: "round", borderColor: "#3B82F6", paddingX: 2, paddingY: 0, flexDirection: "column", marginTop: 1, children: [
1542
+ lastUpdated && /* @__PURE__ */ jsx10(Text10, { color: COLORS.muted, children: t("dashboard_lastUpdated", { time: lastUpdated }) }),
1543
+ partialErrors.length > 0 && /* @__PURE__ */ jsxs10(Box8, { flexDirection: "column", borderStyle: "round", borderColor: COLORS.warning, paddingX: 2, paddingY: 0, children: [
1544
+ /* @__PURE__ */ jsx10(Text10, { color: COLORS.warning, bold: true, children: t("dashboard_partialData") }),
1545
+ partialErrors.map((item) => /* @__PURE__ */ jsxs10(Text10, { color: COLORS.muted, children: [
1546
+ item.label,
1547
+ ": ",
1548
+ item.message
1549
+ ] }, item.label))
1550
+ ] }),
1551
+ config && !errors.config && /* @__PURE__ */ jsxs10(Box8, { flexDirection: "column", children: [
1552
+ /* @__PURE__ */ jsx10(SectionHeader, { emoji: "\u2139\uFE0F", title: t("dashboard_systemInfo"), gradient: [COLORS.text, COLORS.muted] }),
1553
+ /* @__PURE__ */ jsxs10(Box8, { borderStyle: "round", borderColor: COLORS.blue, paddingX: 2, paddingY: 0, flexDirection: "column", marginTop: 1, children: [
967
1554
  /* @__PURE__ */ jsxs10(Text10, { children: [
968
- /* @__PURE__ */ jsx10(Text10, { color: "#9CA3AF", children: t("dashboard_homebrew") }),
1555
+ /* @__PURE__ */ jsx10(Text10, { color: COLORS.muted, children: t("dashboard_homebrew") }),
969
1556
  " ",
970
1557
  config.HOMEBREW_VERSION
971
1558
  ] }),
972
1559
  /* @__PURE__ */ jsxs10(Text10, { children: [
973
- /* @__PURE__ */ jsx10(Text10, { color: "#9CA3AF", children: t("dashboard_prefix") }),
1560
+ /* @__PURE__ */ jsx10(Text10, { color: COLORS.muted, children: t("dashboard_prefix") }),
974
1561
  " ",
975
1562
  config.HOMEBREW_PREFIX
976
1563
  ] }),
977
1564
  /* @__PURE__ */ jsxs10(Text10, { children: [
978
- /* @__PURE__ */ jsx10(Text10, { color: "#9CA3AF", children: t("dashboard_updated") }),
1565
+ /* @__PURE__ */ jsx10(Text10, { color: COLORS.muted, children: t("dashboard_updated") }),
979
1566
  " ",
980
1567
  config.coreUpdated
981
1568
  ] })
982
1569
  ] })
983
1570
  ] }),
984
- outdated.formulae.length > 0 && /* @__PURE__ */ jsxs10(Box8, { flexDirection: "column", marginTop: 1, children: [
1571
+ !errors.outdated && outdated.formulae.length > 0 && /* @__PURE__ */ jsxs10(Box8, { flexDirection: "column", marginTop: 1, children: [
985
1572
  /* @__PURE__ */ jsx10(SectionHeader, { emoji: "\u{1F4E6}", title: t("dashboard_outdatedPackages"), gradient: GRADIENTS.fire }),
986
1573
  /* @__PURE__ */ jsxs10(Box8, { paddingLeft: 2, flexDirection: "column", children: [
987
1574
  outdated.formulae.slice(0, 10).map((pkg) => /* @__PURE__ */ jsxs10(Box8, { gap: 1, children: [
988
- /* @__PURE__ */ jsx10(Text10, { color: "#F9FAFB", children: pkg.name }),
1575
+ /* @__PURE__ */ jsx10(Text10, { color: COLORS.text, children: pkg.name }),
989
1576
  /* @__PURE__ */ jsx10(VersionArrow, { current: pkg.installed_versions[0] ?? "", latest: pkg.current_version })
990
1577
  ] }, pkg.name)),
991
- outdated.formulae.length > 10 && /* @__PURE__ */ jsx10(Text10, { color: "#6B7280", italic: true, children: t("common_andMore", { count: outdated.formulae.length - 10 }) })
1578
+ outdated.formulae.length > 10 && /* @__PURE__ */ jsx10(Text10, { color: COLORS.textSecondary, italic: true, children: t("common_andMore", { count: outdated.formulae.length - 10 }) })
992
1579
  ] })
993
1580
  ] }),
994
- errorServices > 0 && /* @__PURE__ */ jsxs10(Box8, { flexDirection: "column", marginTop: 1, children: [
995
- /* @__PURE__ */ jsx10(SectionHeader, { emoji: "\u26A0\uFE0F", title: t("dashboard_serviceErrors"), color: "#EF4444" }),
1581
+ !errors.services && errorServices > 0 && /* @__PURE__ */ jsxs10(Box8, { flexDirection: "column", marginTop: 1, children: [
1582
+ /* @__PURE__ */ jsx10(SectionHeader, { emoji: "\u26A0\uFE0F", title: t("dashboard_serviceErrors"), color: COLORS.error }),
996
1583
  /* @__PURE__ */ jsx10(Box8, { paddingLeft: 2, flexDirection: "column", children: errorServiceList.map((s) => /* @__PURE__ */ jsxs10(Box8, { gap: 1, children: [
997
1584
  /* @__PURE__ */ jsx10(StatusBadge, { label: t("badge_error"), variant: "error" }),
998
1585
  /* @__PURE__ */ jsx10(Text10, { children: s.name }),
999
- s.exit_code != null && /* @__PURE__ */ jsx10(Text10, { color: "#9CA3AF", children: t("common_exit", { code: s.exit_code }) })
1586
+ s.exit_code != null && /* @__PURE__ */ jsx10(Text10, { color: COLORS.muted, children: t("common_exit", { code: s.exit_code }) })
1000
1587
  ] }, s.name)) })
1001
1588
  ] })
1002
1589
  ] });
@@ -1004,7 +1591,7 @@ function DashboardView() {
1004
1591
 
1005
1592
  // src/views/installed.tsx
1006
1593
  import { useState as useState3, useMemo as useMemo3, useEffect as useEffect5 } from "react";
1007
- import { Box as Box12, Text as Text14, useInput as useInput3, useStdout } from "ink";
1594
+ import { Box as Box14, Text as Text16, useInput as useInput3, useStdout as useStdout2 } from "ink";
1008
1595
 
1009
1596
  // src/hooks/use-debounce.ts
1010
1597
  import { useState, useEffect as useEffect2 } from "react";
@@ -1024,8 +1611,9 @@ async function logToHistory(args, success, error) {
1024
1611
  const detected = detectAction(args);
1025
1612
  if (!detected) return;
1026
1613
  try {
1027
- const { appendEntry: appendEntry2 } = await import("./history-logger-65UF2R6F.js");
1028
- await appendEntry2(detected.action, detected.packageName, success, error);
1614
+ const isPro = useLicenseStore.getState().isPro();
1615
+ const { appendEntry: appendEntry2 } = await import("./history-logger-2PGYSPFL.js");
1616
+ await appendEntry2(isPro, detected.action, detected.packageName, success, error);
1029
1617
  } catch {
1030
1618
  }
1031
1619
  }
@@ -1094,7 +1682,7 @@ import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
1094
1682
  function SearchInput({ defaultValue, onChange, placeholder, isActive = true }) {
1095
1683
  const resolvedPlaceholder = placeholder ?? t("searchInput_placeholder");
1096
1684
  return /* @__PURE__ */ jsxs11(Box9, { children: [
1097
- /* @__PURE__ */ jsxs11(Text11, { color: "#FFD700", children: [
1685
+ /* @__PURE__ */ jsxs11(Text11, { color: COLORS.gold, children: [
1098
1686
  "\u{1F50D}",
1099
1687
  " "
1100
1688
  ] }),
@@ -1105,7 +1693,7 @@ function SearchInput({ defaultValue, onChange, placeholder, isActive = true }) {
1105
1693
  defaultValue,
1106
1694
  onChange
1107
1695
  }
1108
- ) : /* @__PURE__ */ jsx11(Text11, { color: "#6B7280", children: defaultValue || placeholder })
1696
+ ) : /* @__PURE__ */ jsx11(Text11, { color: COLORS.textSecondary, children: defaultValue || placeholder })
1109
1697
  ] });
1110
1698
  }
1111
1699
 
@@ -1128,12 +1716,12 @@ function ConfirmDialog({ message, onConfirm, onCancel }) {
1128
1716
  else if (input === "n" || input === "N") onCancel();
1129
1717
  else if (key.escape) onCancel();
1130
1718
  });
1131
- return /* @__PURE__ */ jsxs12(Box10, { borderStyle: "double", borderColor: "#A855F7", paddingX: 2, paddingY: 1, flexDirection: "column", children: [
1132
- /* @__PURE__ */ jsx12(Text12, { bold: true, color: "#F9FAFB", children: message }),
1719
+ return /* @__PURE__ */ jsxs12(Box10, { borderStyle: "double", borderColor: COLORS.purple, paddingX: 2, paddingY: 1, flexDirection: "column", children: [
1720
+ /* @__PURE__ */ jsx12(Text12, { bold: true, color: COLORS.text, children: message }),
1133
1721
  /* @__PURE__ */ jsxs12(Box10, { marginTop: 1, children: [
1134
- /* @__PURE__ */ jsx12(Text12, { color: "#22C55E", children: t("confirm_yes") }),
1722
+ /* @__PURE__ */ jsx12(Text12, { color: COLORS.success, children: t("confirm_yes") }),
1135
1723
  /* @__PURE__ */ jsx12(Text12, { children: " / " }),
1136
- /* @__PURE__ */ jsx12(Text12, { color: "#EF4444", children: t("confirm_no") })
1724
+ /* @__PURE__ */ jsx12(Text12, { color: COLORS.error, children: t("confirm_no") })
1137
1725
  ] })
1138
1726
  ] });
1139
1727
  }
@@ -1144,43 +1732,44 @@ import { Spinner as Spinner2 } from "@inkjs/ui";
1144
1732
  import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
1145
1733
  function ProgressLog({ lines, isRunning, title, maxVisible = 15 }) {
1146
1734
  const visible = lines.slice(-maxVisible);
1147
- return /* @__PURE__ */ jsxs13(Box11, { flexDirection: "column", borderStyle: "round", borderColor: "#38BDF8", paddingX: 1, children: [
1735
+ return /* @__PURE__ */ jsxs13(Box11, { flexDirection: "column", borderStyle: "round", borderColor: COLORS.sky, paddingX: 1, children: [
1148
1736
  title && /* @__PURE__ */ jsxs13(Box11, { marginBottom: 1, children: [
1149
1737
  isRunning && /* @__PURE__ */ jsx13(Spinner2, { label: "" }),
1150
- /* @__PURE__ */ jsxs13(Text13, { bold: true, color: "#38BDF8", children: [
1738
+ /* @__PURE__ */ jsxs13(Text13, { bold: true, color: COLORS.sky, children: [
1151
1739
  " ",
1152
1740
  title
1153
1741
  ] })
1154
1742
  ] }),
1155
- visible.map((line, i) => /* @__PURE__ */ jsx13(Text13, { color: "#9CA3AF", wrap: "wrap", children: line }, line.slice(0, 30) + (lines.length - visible.length + i))),
1156
- lines.length === 0 && !isRunning && /* @__PURE__ */ jsx13(Text13, { color: "#6B7280", italic: true, children: t("progress_noOutput") })
1743
+ visible.map((line, i) => /* @__PURE__ */ jsx13(Text13, { color: COLORS.muted, wrap: "wrap", children: line }, lines.length - visible.length + i)),
1744
+ lines.length === 0 && !isRunning && /* @__PURE__ */ jsx13(Text13, { color: COLORS.textSecondary, italic: true, children: t("progress_noOutput") })
1157
1745
  ] });
1158
1746
  }
1159
1747
 
1160
- // src/utils/format.ts
1161
- function formatBytes(bytes) {
1162
- if (!isFinite(bytes) || bytes <= 0) return "0 B";
1163
- const units = ["B", "KB", "MB", "GB", "TB"];
1164
- const i = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1);
1165
- return `${(bytes / Math.pow(1024, i)).toFixed(1)} ${units[i]}`;
1166
- }
1167
- function formatRelativeTime(timestamp) {
1168
- if (!timestamp || !isFinite(timestamp)) return t("time_justNow");
1169
- const diff = Date.now() / 1e3 - timestamp;
1170
- if (diff < 0) return t("time_justNow");
1171
- if (diff < 60) return t("time_justNow");
1172
- if (diff < 3600) return t("time_minutesAgo", { n: Math.floor(diff / 60) });
1173
- if (diff < 86400) return t("time_hoursAgo", { n: Math.floor(diff / 3600) });
1174
- if (diff < 2592e3) return t("time_daysAgo", { n: Math.floor(diff / 86400) });
1175
- return t("time_monthsAgo", { n: Math.floor(diff / 2592e3) });
1748
+ // src/components/common/result-banner.tsx
1749
+ import { Box as Box12, Text as Text14 } from "ink";
1750
+ import { jsx as jsx14 } from "react/jsx-runtime";
1751
+ var STATUS_COLORS = {
1752
+ success: COLORS.success,
1753
+ error: COLORS.error,
1754
+ warning: COLORS.warning,
1755
+ info: COLORS.info
1756
+ };
1757
+ function ResultBanner({ status, message }) {
1758
+ 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 }) });
1176
1759
  }
1177
- function truncate(str, maxLen) {
1178
- if (str.length <= maxLen) return str;
1179
- return str.slice(0, maxLen - 1) + "\u2026";
1760
+
1761
+ // src/components/common/selectable-row.tsx
1762
+ import { Box as Box13, Text as Text15 } from "ink";
1763
+ import { jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
1764
+ function SelectableRow({ isCurrent, children, gap = 1 }) {
1765
+ return /* @__PURE__ */ jsxs14(Box13, { gap, children: [
1766
+ /* @__PURE__ */ jsx15(Text15, { color: isCurrent ? COLORS.success : COLORS.muted, children: isCurrent ? "\u25B6" : " " }),
1767
+ children
1768
+ ] });
1180
1769
  }
1181
1770
 
1182
1771
  // src/views/installed.tsx
1183
- import { jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
1772
+ import { jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
1184
1773
  function InstalledView() {
1185
1774
  const { formulae, casks, loading, errors, fetchInstalled } = useBrewStore();
1186
1775
  const navigate = useNavigationStore((s) => s.navigate);
@@ -1193,7 +1782,7 @@ function InstalledView() {
1193
1782
  const debouncedFilter = useDebounce(filter, 200);
1194
1783
  const stream = useBrewStream();
1195
1784
  const { openModal, closeModal } = useModalStore();
1196
- const { stdout } = useStdout();
1785
+ const { stdout } = useStdout2();
1197
1786
  const cols = stdout?.columns ?? 80;
1198
1787
  const nameWidth = Math.floor(cols * 0.35);
1199
1788
  const versionWidth = Math.floor(cols * 0.15);
@@ -1250,18 +1839,18 @@ function InstalledView() {
1250
1839
  } else if (input === "G") {
1251
1840
  setCursor(Math.max(allItems.length - 1, 0));
1252
1841
  } else if (key.return && allItems[cursor]) {
1253
- selectPackage(allItems[cursor].name);
1842
+ selectPackage(allItems[cursor].name, tab === "formulae" ? "formula" : "cask");
1254
1843
  navigate("package-info");
1255
1844
  } else if (input === "f") {
1256
1845
  setTab((t2) => t2 === "formulae" ? "casks" : "formulae");
1257
1846
  setCursor(0);
1258
1847
  }
1259
1848
  }, { isActive: true });
1260
- if (loading.installed) return /* @__PURE__ */ jsx14(Loading, { message: t("loading_installed") });
1261
- if (errors.installed) return /* @__PURE__ */ jsx14(ErrorMessage, { message: errors.installed });
1849
+ if (loading.installed) return /* @__PURE__ */ jsx16(Loading, { message: t("loading_installed") });
1850
+ if (errors.installed) return /* @__PURE__ */ jsx16(ErrorMessage, { message: errors.installed });
1262
1851
  if (stream.isRunning || stream.lines.length > 0) {
1263
- return /* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", children: [
1264
- /* @__PURE__ */ jsx14(
1852
+ return /* @__PURE__ */ jsxs15(Box14, { flexDirection: "column", children: [
1853
+ /* @__PURE__ */ jsx16(
1265
1854
  ProgressLog,
1266
1855
  {
1267
1856
  lines: stream.lines,
@@ -1269,13 +1858,19 @@ function InstalledView() {
1269
1858
  title: t("pkgInfo_uninstalling", { name: "..." })
1270
1859
  }
1271
1860
  ),
1272
- stream.isRunning && /* @__PURE__ */ jsxs14(Text14, { color: "#6B7280", children: [
1861
+ stream.isRunning && /* @__PURE__ */ jsxs15(Text16, { color: COLORS.textSecondary, children: [
1273
1862
  "esc:",
1274
1863
  t("hint_cancel")
1275
1864
  ] }),
1276
- !stream.isRunning && /* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", marginTop: 1, children: [
1277
- /* @__PURE__ */ jsx14(Box12, { borderStyle: "round", borderColor: stream.error ? "#EF4444" : "#22C55E", paddingX: 2, paddingY: 0, children: /* @__PURE__ */ jsx14(Text14, { color: stream.error ? "#EF4444" : "#22C55E", bold: true, children: stream.error ? `\u2718 ${stream.error}` : `\u2714 ${t("pkgInfo_done")}` }) }),
1278
- /* @__PURE__ */ jsxs14(Text14, { color: "#6B7280", children: [
1865
+ !stream.isRunning && /* @__PURE__ */ jsxs15(Box14, { flexDirection: "column", marginTop: 1, children: [
1866
+ /* @__PURE__ */ jsx16(
1867
+ ResultBanner,
1868
+ {
1869
+ status: stream.error ? "error" : "success",
1870
+ message: stream.error ? `\u2718 ${stream.error}` : `\u2714 ${t("pkgInfo_done")}`
1871
+ }
1872
+ ),
1873
+ /* @__PURE__ */ jsxs15(Text16, { color: COLORS.textSecondary, children: [
1279
1874
  "esc:",
1280
1875
  t("hint_back")
1281
1876
  ] })
@@ -1285,28 +1880,28 @@ function InstalledView() {
1285
1880
  const MAX_VISIBLE_ROWS = Math.max(5, (stdout?.rows ?? 24) - 8);
1286
1881
  const start = Math.max(0, cursor - Math.floor(MAX_VISIBLE_ROWS / 2));
1287
1882
  const visible = allItems.slice(start, start + MAX_VISIBLE_ROWS);
1288
- return /* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", children: [
1289
- /* @__PURE__ */ jsxs14(Box12, { marginBottom: 1, gap: 1, children: [
1290
- /* @__PURE__ */ jsx14(
1291
- Box12,
1883
+ return /* @__PURE__ */ jsxs15(Box14, { flexDirection: "column", children: [
1884
+ /* @__PURE__ */ jsxs15(Box14, { marginBottom: 1, gap: 1, children: [
1885
+ /* @__PURE__ */ jsx16(
1886
+ Box14,
1292
1887
  {
1293
1888
  borderStyle: "round",
1294
- borderColor: tab === "formulae" ? "#06B6D4" : "#6B7280",
1889
+ borderColor: tab === "formulae" ? COLORS.info : COLORS.textSecondary,
1295
1890
  paddingX: 1,
1296
- children: /* @__PURE__ */ jsxs14(Text14, { bold: tab === "formulae", color: tab === "formulae" ? "#06B6D4" : "#6B7280", children: [
1891
+ children: /* @__PURE__ */ jsxs15(Text16, { bold: tab === "formulae", color: tab === "formulae" ? COLORS.info : COLORS.textSecondary, children: [
1297
1892
  "\u{1F4E6}",
1298
1893
  " ",
1299
1894
  t("installed_formulaeCount", { count: formulae.length })
1300
1895
  ] })
1301
1896
  }
1302
1897
  ),
1303
- /* @__PURE__ */ jsx14(
1304
- Box12,
1898
+ /* @__PURE__ */ jsx16(
1899
+ Box14,
1305
1900
  {
1306
1901
  borderStyle: "round",
1307
- borderColor: tab === "casks" ? "#A855F7" : "#6B7280",
1902
+ borderColor: tab === "casks" ? COLORS.purple : COLORS.textSecondary,
1308
1903
  paddingX: 1,
1309
- children: /* @__PURE__ */ jsxs14(Text14, { bold: tab === "casks", color: tab === "casks" ? "#A855F7" : "#6B7280", children: [
1904
+ children: /* @__PURE__ */ jsxs15(Text16, { bold: tab === "casks", color: tab === "casks" ? COLORS.purple : COLORS.textSecondary, children: [
1310
1905
  "\u{1F37A}",
1311
1906
  " ",
1312
1907
  t("installed_casksCount", { count: casks.length })
@@ -1314,7 +1909,7 @@ function InstalledView() {
1314
1909
  }
1315
1910
  )
1316
1911
  ] }),
1317
- confirmUninstall && /* @__PURE__ */ jsx14(
1912
+ confirmUninstall && /* @__PURE__ */ jsx16(
1318
1913
  ConfirmDialog,
1319
1914
  {
1320
1915
  message: t("installed_confirmUninstall", { name: confirmUninstall }),
@@ -1328,49 +1923,48 @@ function InstalledView() {
1328
1923
  onCancel: () => setConfirmUninstall(null)
1329
1924
  }
1330
1925
  ),
1331
- isSearching && /* @__PURE__ */ jsx14(Box12, { marginBottom: 1, borderStyle: "round", borderColor: "#FFD700", paddingX: 1, children: /* @__PURE__ */ jsx14(SearchInput, { defaultValue: filter, onChange: setFilter, isActive: isSearching }) }),
1332
- /* @__PURE__ */ jsxs14(Box12, { gap: 1, borderStyle: "single", borderBottom: true, borderTop: false, borderLeft: false, borderRight: false, borderColor: "#4B5563", children: [
1333
- /* @__PURE__ */ jsxs14(Text14, { color: "#F9FAFB", bold: true, children: [
1926
+ isSearching && /* @__PURE__ */ jsx16(Box14, { marginBottom: 1, borderStyle: "round", borderColor: COLORS.gold, paddingX: 1, children: /* @__PURE__ */ jsx16(SearchInput, { defaultValue: filter, onChange: setFilter, isActive: isSearching }) }),
1927
+ /* @__PURE__ */ jsxs15(Box14, { gap: 1, borderStyle: "single", borderBottom: true, borderTop: false, borderLeft: false, borderRight: false, borderColor: COLORS.border, children: [
1928
+ /* @__PURE__ */ jsxs15(Text16, { color: COLORS.text, bold: true, children: [
1334
1929
  " ",
1335
- "Package".padEnd(nameWidth)
1930
+ t("installed_col_package").padEnd(nameWidth)
1336
1931
  ] }),
1337
- /* @__PURE__ */ jsx14(Text14, { color: "#F9FAFB", bold: true, children: "Version".padEnd(versionWidth) }),
1338
- /* @__PURE__ */ jsx14(Text14, { color: "#F9FAFB", bold: true, children: "Status" })
1932
+ /* @__PURE__ */ jsx16(Text16, { color: COLORS.text, bold: true, children: t("installed_col_version").padEnd(versionWidth) }),
1933
+ /* @__PURE__ */ jsx16(Text16, { color: COLORS.text, bold: true, children: t("installed_col_status") })
1339
1934
  ] }),
1340
- /* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", children: [
1341
- visible.length === 0 && /* @__PURE__ */ jsx14(Box12, { paddingY: 1, justifyContent: "center", children: /* @__PURE__ */ jsx14(Text14, { color: "#6B7280", italic: true, children: t("installed_noPackages") }) }),
1342
- start > 0 && /* @__PURE__ */ jsxs14(Text14, { color: "#6B7280", dimColor: true, children: [
1935
+ /* @__PURE__ */ jsxs15(Box14, { flexDirection: "column", children: [
1936
+ visible.length === 0 && /* @__PURE__ */ jsx16(Box14, { paddingY: 1, justifyContent: "center", children: /* @__PURE__ */ jsx16(Text16, { color: COLORS.textSecondary, italic: true, children: t("installed_noPackages") }) }),
1937
+ start > 0 && /* @__PURE__ */ jsxs15(Text16, { color: COLORS.textSecondary, dimColor: true, children: [
1343
1938
  " ",
1344
1939
  t("scroll_moreAbove", { count: start })
1345
1940
  ] }),
1346
1941
  visible.map((item, i) => {
1347
1942
  const idx = start + i;
1348
1943
  const isCurrent = idx === cursor;
1349
- return /* @__PURE__ */ jsxs14(Box12, { gap: 1, children: [
1350
- /* @__PURE__ */ jsx14(Text14, { color: isCurrent ? "#22C55E" : "#9CA3AF", children: isCurrent ? "\u25B6" : " " }),
1351
- /* @__PURE__ */ jsx14(Text14, { bold: isCurrent, inverse: isCurrent, color: isCurrent ? "#F9FAFB" : "#9CA3AF", children: truncate(item.name, nameWidth).padEnd(nameWidth) }),
1352
- /* @__PURE__ */ jsx14(Text14, { color: "#2DD4BF", children: item.version.padEnd(versionWidth) }),
1353
- item.outdated && /* @__PURE__ */ jsx14(StatusBadge, { label: t("badge_outdated"), variant: "warning" }),
1354
- item.pinned && /* @__PURE__ */ jsx14(StatusBadge, { label: t("badge_pinned"), variant: "info" }),
1355
- item.kegOnly && /* @__PURE__ */ jsx14(StatusBadge, { label: t("badge_kegOnly"), variant: "muted" }),
1356
- item.installedAsDependency && /* @__PURE__ */ jsx14(StatusBadge, { label: t("badge_dep"), variant: "muted" }),
1357
- !item.outdated && !item.pinned && !item.kegOnly && !item.installedAsDependency && /* @__PURE__ */ jsx14(Text14, { color: "#6B7280", dimColor: true, children: truncate(item.desc, 30) })
1944
+ return /* @__PURE__ */ jsxs15(SelectableRow, { isCurrent, children: [
1945
+ /* @__PURE__ */ jsx16(Text16, { bold: isCurrent, inverse: isCurrent, color: isCurrent ? COLORS.text : COLORS.muted, children: truncate(item.name, nameWidth).padEnd(nameWidth) }),
1946
+ /* @__PURE__ */ jsx16(Text16, { color: COLORS.teal, children: item.version.padEnd(versionWidth) }),
1947
+ item.outdated && /* @__PURE__ */ jsx16(StatusBadge, { label: t("badge_outdated"), variant: "warning" }),
1948
+ item.pinned && /* @__PURE__ */ jsx16(StatusBadge, { label: t("badge_pinned"), variant: "info" }),
1949
+ item.kegOnly && /* @__PURE__ */ jsx16(StatusBadge, { label: t("badge_kegOnly"), variant: "muted" }),
1950
+ item.installedAsDependency && /* @__PURE__ */ jsx16(StatusBadge, { label: t("badge_dep"), variant: "muted" }),
1951
+ !item.outdated && !item.pinned && !item.kegOnly && !item.installedAsDependency && /* @__PURE__ */ jsx16(Text16, { color: COLORS.textSecondary, dimColor: true, children: truncate(item.desc, 30) })
1358
1952
  ] }, item.name);
1359
1953
  }),
1360
- start + MAX_VISIBLE_ROWS < allItems.length && /* @__PURE__ */ jsxs14(Text14, { color: "#6B7280", dimColor: true, children: [
1954
+ start + MAX_VISIBLE_ROWS < allItems.length && /* @__PURE__ */ jsxs15(Text16, { color: COLORS.textSecondary, dimColor: true, children: [
1361
1955
  " ",
1362
1956
  t("scroll_moreBelow", { count: allItems.length - start - MAX_VISIBLE_ROWS })
1363
1957
  ] })
1364
1958
  ] }),
1365
- /* @__PURE__ */ jsx14(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx14(Text14, { color: "#F9FAFB", bold: true, children: allItems.length > 0 ? `${cursor + 1}/${allItems.length}` : "0/0" }) })
1959
+ /* @__PURE__ */ jsx16(Box14, { marginTop: 1, children: /* @__PURE__ */ jsx16(Text16, { color: COLORS.text, bold: true, children: allItems.length > 0 ? `${cursor + 1}/${allItems.length}` : "0/0" }) })
1366
1960
  ] });
1367
1961
  }
1368
1962
 
1369
1963
  // src/views/search.tsx
1370
1964
  import { useState as useState4, useCallback as useCallback2, useEffect as useEffect6, useRef as useRef2 } from "react";
1371
- import { Box as Box13, Text as Text15, useInput as useInput4 } from "ink";
1965
+ import { Box as Box15, Text as Text17, useInput as useInput4 } from "ink";
1372
1966
  import { TextInput as TextInput2 } from "@inkjs/ui";
1373
- import { jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
1967
+ import { jsx as jsx17, jsxs as jsxs16 } from "react/jsx-runtime";
1374
1968
  function SearchView() {
1375
1969
  const [query, setQuery] = useState4("");
1376
1970
  const [results, setResults] = useState4(null);
@@ -1394,7 +1988,11 @@ function SearchView() {
1394
1988
  return void 0;
1395
1989
  }, [results]);
1396
1990
  const doSearch = useCallback2(async (term) => {
1397
- if (term.length < 2) return;
1991
+ if (term.length < 2) {
1992
+ setResults(null);
1993
+ setSearchError(t("search_minChars"));
1994
+ return;
1995
+ }
1398
1996
  setSearching(true);
1399
1997
  setSearchError(null);
1400
1998
  try {
@@ -1403,7 +2001,7 @@ function SearchView() {
1403
2001
  setCursor(0);
1404
2002
  } catch (err) {
1405
2003
  setResults({ formulae: [], casks: [] });
1406
- setSearchError(err instanceof Error ? err.message : "Search failed");
2004
+ setSearchError(err instanceof Error ? err.message : t("search_failed"));
1407
2005
  } finally {
1408
2006
  setSearching(false);
1409
2007
  }
@@ -1423,6 +2021,12 @@ function SearchView() {
1423
2021
  if (key.escape) stream.cancel();
1424
2022
  return;
1425
2023
  }
2024
+ if (stream.lines.length > 0) {
2025
+ if (key.escape) {
2026
+ stream.clear();
2027
+ }
2028
+ return;
2029
+ }
1426
2030
  if (confirmInstall) return;
1427
2031
  if (key.return && !results) {
1428
2032
  void doSearch(query);
@@ -1447,8 +2051,8 @@ function SearchView() {
1447
2051
  }
1448
2052
  });
1449
2053
  if (stream.isRunning || stream.lines.length > 0) {
1450
- return /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", children: [
1451
- /* @__PURE__ */ jsx15(
2054
+ return /* @__PURE__ */ jsxs16(Box15, { flexDirection: "column", children: [
2055
+ /* @__PURE__ */ jsx17(
1452
2056
  ProgressLog,
1453
2057
  {
1454
2058
  lines: stream.lines,
@@ -1456,26 +2060,32 @@ function SearchView() {
1456
2060
  title: t("search_installing")
1457
2061
  }
1458
2062
  ),
1459
- stream.isRunning && /* @__PURE__ */ jsxs15(Text15, { color: "#6B7280", children: [
2063
+ stream.isRunning && /* @__PURE__ */ jsxs16(Text17, { color: COLORS.textSecondary, children: [
1460
2064
  "esc:",
1461
2065
  t("hint_cancel")
1462
2066
  ] }),
1463
- !stream.isRunning && /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", marginTop: 1, children: [
1464
- /* @__PURE__ */ jsx15(Box13, { borderStyle: "round", borderColor: stream.error ? "#EF4444" : "#22C55E", paddingX: 2, paddingY: 0, children: /* @__PURE__ */ jsx15(Text15, { color: stream.error ? "#EF4444" : "#22C55E", bold: true, children: stream.error ? `\u2718 ${stream.error}` : `\u2714 ${t("search_installComplete")}` }) }),
1465
- /* @__PURE__ */ jsxs15(Text15, { color: "#6B7280", children: [
2067
+ !stream.isRunning && /* @__PURE__ */ jsxs16(Box15, { flexDirection: "column", marginTop: 1, children: [
2068
+ /* @__PURE__ */ jsx17(
2069
+ ResultBanner,
2070
+ {
2071
+ status: stream.error ? "error" : "success",
2072
+ message: stream.error ? `\u2718 ${stream.error}` : `\u2714 ${t("search_installComplete")}`
2073
+ }
2074
+ ),
2075
+ /* @__PURE__ */ jsxs16(Text17, { color: COLORS.textSecondary, children: [
1466
2076
  "esc:",
1467
- t("hint_back")
2077
+ t("hint_clear")
1468
2078
  ] })
1469
2079
  ] })
1470
2080
  ] });
1471
2081
  }
1472
- return /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", children: [
1473
- /* @__PURE__ */ jsxs15(Box13, { marginBottom: 1, children: [
1474
- /* @__PURE__ */ jsxs15(Text15, { color: "#FFD700", children: [
2082
+ return /* @__PURE__ */ jsxs16(Box15, { flexDirection: "column", children: [
2083
+ /* @__PURE__ */ jsxs16(Box15, { marginBottom: 1, children: [
2084
+ /* @__PURE__ */ jsxs16(Text17, { color: COLORS.gold, children: [
1475
2085
  "\u{1F50D}",
1476
2086
  " "
1477
2087
  ] }),
1478
- !results ? /* @__PURE__ */ jsx15(
2088
+ !results ? /* @__PURE__ */ jsx17(
1479
2089
  TextInput2,
1480
2090
  {
1481
2091
  placeholder: t("search_placeholder"),
@@ -1483,17 +2093,17 @@ function SearchView() {
1483
2093
  onChange: setQuery,
1484
2094
  onSubmit: () => void doSearch(query)
1485
2095
  }
1486
- ) : /* @__PURE__ */ jsxs15(Text15, { children: [
2096
+ ) : /* @__PURE__ */ jsxs16(Text17, { children: [
1487
2097
  t("search_resultsFor"),
1488
2098
  ' "',
1489
- /* @__PURE__ */ jsx15(Text15, { bold: true, color: "#F9FAFB", children: query }),
2099
+ /* @__PURE__ */ jsx17(Text17, { bold: true, color: COLORS.text, children: query }),
1490
2100
  '" ',
1491
- /* @__PURE__ */ jsx15(Text15, { color: "#6B7280", children: t("search_escToClear") })
2101
+ /* @__PURE__ */ jsx17(Text17, { color: COLORS.textSecondary, children: t("search_escToClear") })
1492
2102
  ] })
1493
2103
  ] }),
1494
- searching && /* @__PURE__ */ jsx15(Loading, { message: t("loading_searching") }),
1495
- searchError && /* @__PURE__ */ jsx15(Box13, { marginBottom: 1, children: /* @__PURE__ */ jsx15(Text15, { color: "#EF4444", children: searchError }) }),
1496
- confirmInstall && /* @__PURE__ */ jsx15(
2104
+ searching && /* @__PURE__ */ jsx17(Loading, { message: t("loading_searching") }),
2105
+ searchError && /* @__PURE__ */ jsx17(Box15, { marginBottom: 1, children: /* @__PURE__ */ jsx17(Text17, { color: COLORS.error, children: searchError }) }),
2106
+ confirmInstall && /* @__PURE__ */ jsx17(
1497
2107
  ConfirmDialog,
1498
2108
  {
1499
2109
  message: t("search_confirmInstall", { name: confirmInstall }),
@@ -1506,46 +2116,40 @@ function SearchView() {
1506
2116
  onCancel: () => setConfirmInstall(null)
1507
2117
  }
1508
2118
  ),
1509
- results && !searching && !confirmInstall && /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", children: [
1510
- visibleFormulae.length > 0 && /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", marginBottom: 1, children: [
1511
- /* @__PURE__ */ jsx15(Text15, { bold: true, color: "#06B6D4", children: t("search_formulaeHeader", { count: results.formulae.length }) }),
2119
+ results && !searching && !confirmInstall && /* @__PURE__ */ jsxs16(Box15, { flexDirection: "column", children: [
2120
+ visibleFormulae.length > 0 && /* @__PURE__ */ jsxs16(Box15, { flexDirection: "column", marginBottom: 1, children: [
2121
+ /* @__PURE__ */ jsx17(Text17, { bold: true, color: COLORS.info, children: t("search_formulaeHeader", { count: results.formulae.length }) }),
1512
2122
  visibleFormulae.map((name, i) => {
1513
2123
  const isCurrent = i === cursor;
1514
- return /* @__PURE__ */ jsxs15(Box13, { gap: 1, children: [
1515
- /* @__PURE__ */ jsx15(Text15, { color: isCurrent ? "#22C55E" : "#9CA3AF", children: isCurrent ? "\u25B6" : " " }),
1516
- /* @__PURE__ */ jsx15(Text15, { bold: isCurrent, inverse: isCurrent, children: name })
1517
- ] }, name);
2124
+ return /* @__PURE__ */ jsx17(SelectableRow, { isCurrent, children: /* @__PURE__ */ jsx17(Text17, { bold: isCurrent, inverse: isCurrent, children: name }) }, name);
1518
2125
  }),
1519
- results.formulae.length > MAX_VISIBLE && /* @__PURE__ */ jsxs15(Text15, { color: "#6B7280", dimColor: true, children: [
2126
+ results.formulae.length > MAX_VISIBLE && /* @__PURE__ */ jsxs16(Text17, { color: COLORS.textSecondary, dimColor: true, children: [
1520
2127
  " ",
1521
2128
  t("scroll_moreBelow", { count: results.formulae.length - MAX_VISIBLE })
1522
2129
  ] })
1523
2130
  ] }),
1524
- visibleCasks.length > 0 && /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", children: [
1525
- /* @__PURE__ */ jsx15(Text15, { bold: true, color: "#A855F7", children: t("search_casksHeader", { count: results.casks.length }) }),
2131
+ visibleCasks.length > 0 && /* @__PURE__ */ jsxs16(Box15, { flexDirection: "column", children: [
2132
+ /* @__PURE__ */ jsx17(Text17, { bold: true, color: COLORS.purple, children: t("search_casksHeader", { count: results.casks.length }) }),
1526
2133
  visibleCasks.map((name, i) => {
1527
2134
  const idx = visibleFormulae.length + i;
1528
2135
  const isCurrent = idx === cursor;
1529
- return /* @__PURE__ */ jsxs15(Box13, { gap: 1, children: [
1530
- /* @__PURE__ */ jsx15(Text15, { color: isCurrent ? "#22C55E" : "#9CA3AF", children: isCurrent ? "\u25B6" : " " }),
1531
- /* @__PURE__ */ jsx15(Text15, { bold: isCurrent, inverse: isCurrent, children: name })
1532
- ] }, name);
2136
+ return /* @__PURE__ */ jsx17(SelectableRow, { isCurrent, children: /* @__PURE__ */ jsx17(Text17, { bold: isCurrent, inverse: isCurrent, children: name }) }, name);
1533
2137
  }),
1534
- results.casks.length > MAX_VISIBLE && /* @__PURE__ */ jsxs15(Text15, { color: "#6B7280", dimColor: true, children: [
2138
+ results.casks.length > MAX_VISIBLE && /* @__PURE__ */ jsxs16(Text17, { color: COLORS.textSecondary, dimColor: true, children: [
1535
2139
  " ",
1536
2140
  t("scroll_moreBelow", { count: results.casks.length - MAX_VISIBLE })
1537
2141
  ] })
1538
2142
  ] }),
1539
- allVisible.length === 0 && /* @__PURE__ */ jsx15(Box13, { borderStyle: "round", borderColor: "#6B7280", paddingX: 2, children: /* @__PURE__ */ jsx15(Text15, { color: "#6B7280", italic: true, children: t("search_noResults") }) }),
1540
- /* @__PURE__ */ jsx15(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx15(Text15, { color: "#F9FAFB", bold: true, children: allVisible.length > 0 ? `${cursor + 1}/${allVisible.length}` : "" }) })
2143
+ 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") }) }),
2144
+ /* @__PURE__ */ jsx17(Box15, { marginTop: 1, children: /* @__PURE__ */ jsx17(Text17, { color: COLORS.text, bold: true, children: allVisible.length > 0 ? `${cursor + 1}/${allVisible.length}` : "" }) })
1541
2145
  ] })
1542
2146
  ] });
1543
2147
  }
1544
2148
 
1545
2149
  // src/views/outdated.tsx
1546
2150
  import { useEffect as useEffect7, useRef as useRef3, useState as useState5 } from "react";
1547
- import { Box as Box14, Text as Text16, useInput as useInput5, useStdout as useStdout2 } from "ink";
1548
- import { jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
2151
+ import { Box as Box16, Text as Text18, useInput as useInput5, useStdout as useStdout3 } from "ink";
2152
+ import { jsx as jsx18, jsxs as jsxs17 } from "react/jsx-runtime";
1549
2153
  function OutdatedView() {
1550
2154
  const { outdated, loading, errors, fetchOutdated } = useBrewStore();
1551
2155
  const stream = useBrewStream();
@@ -1567,6 +2171,17 @@ function OutdatedView() {
1567
2171
  if (key.escape) stream.cancel();
1568
2172
  return;
1569
2173
  }
2174
+ if (stream.lines.length > 0) {
2175
+ if (key.escape) {
2176
+ stream.clear();
2177
+ return;
2178
+ }
2179
+ if (input === "r") {
2180
+ stream.clear();
2181
+ void fetchOutdated();
2182
+ }
2183
+ return;
2184
+ }
1570
2185
  if (confirmAction) return;
1571
2186
  if (input === "j" || key.downArrow) {
1572
2187
  setCursor((c) => Math.min(c + 1, Math.max(0, allOutdated.length - 1)));
@@ -1578,21 +2193,21 @@ function OutdatedView() {
1578
2193
  setConfirmAction({ type: "all" });
1579
2194
  } else if (input === "p" && allOutdated[cursor]) {
1580
2195
  const pkg = allOutdated[cursor];
1581
- void execBrew([pkg.pinned ? "unpin" : "pin", pkg.name]).then(() => void fetchOutdated());
2196
+ void (pkg.pinned ? unpinPackage(pkg.name) : pinPackage(pkg.name)).then(() => void fetchOutdated());
1582
2197
  return;
1583
2198
  } else if (input === "r") {
1584
2199
  void fetchOutdated();
1585
2200
  }
1586
2201
  });
1587
- const { stdout } = useStdout2();
2202
+ const { stdout } = useStdout3();
1588
2203
  const MAX_VISIBLE_ROWS = Math.max(5, (stdout?.rows ?? 24) - 8);
1589
2204
  const start = Math.max(0, cursor - Math.floor(MAX_VISIBLE_ROWS / 2));
1590
2205
  const visible = allOutdated.slice(start, start + MAX_VISIBLE_ROWS);
1591
- if (loading.outdated) return /* @__PURE__ */ jsx16(Loading, { message: t("loading_outdated") });
1592
- if (errors.outdated) return /* @__PURE__ */ jsx16(ErrorMessage, { message: errors.outdated });
2206
+ if (loading.outdated) return /* @__PURE__ */ jsx18(Loading, { message: t("loading_outdated") });
2207
+ if (errors.outdated) return /* @__PURE__ */ jsx18(ErrorMessage, { message: errors.outdated });
1593
2208
  if (stream.isRunning || stream.lines.length > 0) {
1594
- return /* @__PURE__ */ jsxs16(Box14, { flexDirection: "column", children: [
1595
- /* @__PURE__ */ jsx16(
2209
+ return /* @__PURE__ */ jsxs17(Box16, { flexDirection: "column", children: [
2210
+ /* @__PURE__ */ jsx18(
1596
2211
  ProgressLog,
1597
2212
  {
1598
2213
  lines: stream.lines,
@@ -1600,33 +2215,35 @@ function OutdatedView() {
1600
2215
  title: t("outdated_upgrading")
1601
2216
  }
1602
2217
  ),
1603
- stream.isRunning && /* @__PURE__ */ jsxs16(Text16, { color: "#6B7280", children: [
2218
+ stream.isRunning && /* @__PURE__ */ jsxs17(Text18, { color: COLORS.textSecondary, children: [
1604
2219
  "esc:",
1605
2220
  t("hint_cancel")
1606
2221
  ] }),
1607
- !stream.isRunning && /* @__PURE__ */ jsxs16(Box14, { flexDirection: "column", marginTop: 1, children: [
1608
- /* @__PURE__ */ jsxs16(Box14, { borderStyle: "round", borderColor: stream.error ? "#EF4444" : "#22C55E", paddingX: 2, paddingY: 0, children: [
1609
- /* @__PURE__ */ jsx16(Text16, { color: stream.error ? "#EF4444" : "#22C55E", bold: true, children: stream.error ? `\u2718 ${stream.error}` : `\u2714 ${t("outdated_upgradeComplete")}` }),
1610
- /* @__PURE__ */ jsxs16(Text16, { color: "#9CA3AF", children: [
2222
+ !stream.isRunning && /* @__PURE__ */ jsxs17(Box16, { flexDirection: "column", marginTop: 1, children: [
2223
+ /* @__PURE__ */ jsxs17(Box16, { borderStyle: "round", borderColor: stream.error ? COLORS.error : COLORS.success, paddingX: 2, paddingY: 0, children: [
2224
+ /* @__PURE__ */ jsx18(Text18, { color: stream.error ? COLORS.error : COLORS.success, bold: true, children: stream.error ? `\u2718 ${stream.error}` : `\u2714 ${t("outdated_upgradeComplete")}` }),
2225
+ /* @__PURE__ */ jsxs17(Text18, { color: COLORS.muted, children: [
1611
2226
  " ",
1612
2227
  t("outdated_pressRefresh")
1613
2228
  ] })
1614
2229
  ] }),
1615
- /* @__PURE__ */ jsxs16(Text16, { color: "#6B7280", children: [
2230
+ /* @__PURE__ */ jsxs17(Text18, { color: COLORS.textSecondary, children: [
1616
2231
  "r:",
1617
2232
  t("hint_refresh"),
1618
2233
  " esc:",
1619
- t("hint_back")
2234
+ t("hint_clear")
1620
2235
  ] })
1621
2236
  ] })
1622
2237
  ] });
1623
2238
  }
1624
- return /* @__PURE__ */ jsxs16(Box14, { flexDirection: "column", children: [
1625
- /* @__PURE__ */ jsx16(SectionHeader, { emoji: "\u{1F4E6}", title: t("outdated_title", { count: allOutdated.length }), gradient: GRADIENTS.fire }),
1626
- confirmAction && /* @__PURE__ */ jsx16(Box14, { marginY: 1, children: /* @__PURE__ */ jsx16(
2239
+ const upgradeAllMessage = confirmAction?.type === "all" ? `${t("outdated_confirmAll", { count: allOutdated.length })}
2240
+ ${t("outdated_upgradeAllList", { list: allOutdated.map((p) => p.name).join(", ") })}` : "";
2241
+ return /* @__PURE__ */ jsxs17(Box16, { flexDirection: "column", children: [
2242
+ /* @__PURE__ */ jsx18(SectionHeader, { emoji: "\u{1F4E6}", title: t("outdated_title", { count: allOutdated.length }), gradient: GRADIENTS.fire }),
2243
+ confirmAction && /* @__PURE__ */ jsx18(Box16, { marginY: 1, children: /* @__PURE__ */ jsx18(
1627
2244
  ConfirmDialog,
1628
2245
  {
1629
- message: confirmAction.type === "all" ? t("outdated_confirmAll", { count: allOutdated.length }) : t("outdated_confirmSingle", { name: confirmAction.type === "single" ? confirmAction.name : "" }),
2246
+ message: confirmAction.type === "all" ? upgradeAllMessage : t("outdated_confirmSingle", { name: confirmAction.type === "single" ? confirmAction.name : "" }),
1630
2247
  onConfirm: () => {
1631
2248
  hasRefreshed.current = false;
1632
2249
  if (confirmAction.type === "all") {
@@ -1639,31 +2256,26 @@ function OutdatedView() {
1639
2256
  onCancel: () => setConfirmAction(null)
1640
2257
  }
1641
2258
  ) }),
1642
- allOutdated.length === 0 && !confirmAction && /* @__PURE__ */ jsx16(Box14, { marginTop: 1, children: /* @__PURE__ */ jsx16(Box14, { borderStyle: "round", borderColor: "#22C55E", paddingX: 2, paddingY: 0, children: /* @__PURE__ */ jsxs16(Text16, { color: "#22C55E", bold: true, children: [
1643
- "\u2714",
1644
- " ",
1645
- t("outdated_upToDate")
1646
- ] }) }) }),
1647
- allOutdated.length > 0 && !confirmAction && /* @__PURE__ */ jsxs16(Box14, { flexDirection: "column", marginTop: 1, children: [
1648
- start > 0 && /* @__PURE__ */ jsxs16(Text16, { color: "#6B7280", dimColor: true, children: [
2259
+ allOutdated.length === 0 && !confirmAction && /* @__PURE__ */ jsx18(Box16, { marginTop: 1, children: /* @__PURE__ */ jsx18(ResultBanner, { status: "success", message: `\u2714 ${t("outdated_upToDate")}` }) }),
2260
+ allOutdated.length > 0 && !confirmAction && /* @__PURE__ */ jsxs17(Box16, { flexDirection: "column", marginTop: 1, children: [
2261
+ start > 0 && /* @__PURE__ */ jsxs17(Text18, { color: COLORS.textSecondary, dimColor: true, children: [
1649
2262
  " ",
1650
2263
  t("scroll_moreAbove", { count: start })
1651
2264
  ] }),
1652
2265
  visible.map((pkg, i) => {
1653
2266
  const idx = start + i;
1654
2267
  const isCurrent = idx === cursor;
1655
- return /* @__PURE__ */ jsxs16(Box14, { gap: 1, children: [
1656
- /* @__PURE__ */ jsx16(Text16, { color: isCurrent ? "#22C55E" : "#9CA3AF", children: isCurrent ? "\u25B6" : " " }),
1657
- /* @__PURE__ */ jsx16(Text16, { bold: isCurrent, inverse: isCurrent, color: isCurrent ? "#F9FAFB" : "#9CA3AF", children: pkg.name }),
1658
- /* @__PURE__ */ jsx16(VersionArrow, { current: pkg.installed_versions[0] ?? "", latest: pkg.current_version }),
1659
- pkg.pinned && /* @__PURE__ */ jsx16(StatusBadge, { label: t("outdated_pinned"), variant: "info" })
2268
+ return /* @__PURE__ */ jsxs17(SelectableRow, { isCurrent, children: [
2269
+ /* @__PURE__ */ jsx18(Text18, { bold: isCurrent, inverse: isCurrent, color: isCurrent ? COLORS.text : COLORS.muted, children: pkg.name }),
2270
+ /* @__PURE__ */ jsx18(VersionArrow, { current: pkg.installed_versions[0] ?? "", latest: pkg.current_version }),
2271
+ pkg.pinned && /* @__PURE__ */ jsx18(StatusBadge, { label: t("outdated_pinned"), variant: "info" })
1660
2272
  ] }, pkg.name);
1661
2273
  }),
1662
- start + MAX_VISIBLE_ROWS < allOutdated.length && /* @__PURE__ */ jsxs16(Text16, { color: "#6B7280", dimColor: true, children: [
2274
+ start + MAX_VISIBLE_ROWS < allOutdated.length && /* @__PURE__ */ jsxs17(Text18, { color: COLORS.textSecondary, dimColor: true, children: [
1663
2275
  " ",
1664
2276
  t("scroll_moreBelow", { count: allOutdated.length - start - MAX_VISIBLE_ROWS })
1665
2277
  ] }),
1666
- /* @__PURE__ */ jsx16(Box14, { marginTop: 1, children: /* @__PURE__ */ jsxs16(Text16, { color: "#F9FAFB", bold: true, children: [
2278
+ /* @__PURE__ */ jsx18(Box16, { marginTop: 1, children: /* @__PURE__ */ jsxs17(Text18, { color: COLORS.text, bold: true, children: [
1667
2279
  cursor + 1,
1668
2280
  "/",
1669
2281
  allOutdated.length
@@ -1674,8 +2286,8 @@ function OutdatedView() {
1674
2286
 
1675
2287
  // src/views/package-info.tsx
1676
2288
  import { useEffect as useEffect8, useRef as useRef4, useState as useState6 } from "react";
1677
- import { Box as Box15, Text as Text17, useInput as useInput6 } from "ink";
1678
- import { Fragment as Fragment4, jsx as jsx17, jsxs as jsxs17 } from "react/jsx-runtime";
2289
+ import { Box as Box17, Text as Text19, useInput as useInput6 } from "ink";
2290
+ import { Fragment as Fragment4, jsx as jsx19, jsxs as jsxs18 } from "react/jsx-runtime";
1679
2291
  var ACTION_PROGRESS_KEYS = {
1680
2292
  install: "pkgInfo_installing",
1681
2293
  uninstall: "pkgInfo_uninstalling",
@@ -1688,6 +2300,7 @@ var ACTION_CONFIRM_KEYS = {
1688
2300
  };
1689
2301
  function PackageInfoView() {
1690
2302
  const packageName = useNavigationStore((s) => s.selectedPackage);
2303
+ const packageType = useNavigationStore((s) => s.selectedPackageType);
1691
2304
  const [formula, setFormula] = useState6(null);
1692
2305
  const [loading, setLoading2] = useState6(true);
1693
2306
  const [error, setError2] = useState6(null);
@@ -1705,22 +2318,60 @@ function PackageInfoView() {
1705
2318
  useEffect8(() => {
1706
2319
  if (!packageName) return;
1707
2320
  setLoading2(true);
1708
- getFormulaInfo(packageName).then((f) => {
2321
+ const fetchInfo = async () => {
2322
+ if (packageType === "cask") {
2323
+ const caskInfo = await getCaskInfo(packageName);
2324
+ if (caskInfo && mountedRef.current) {
2325
+ const formulaLike = {
2326
+ name: caskInfo.token,
2327
+ full_name: caskInfo.full_token,
2328
+ tap: "",
2329
+ desc: caskInfo.desc,
2330
+ license: "",
2331
+ homepage: caskInfo.homepage,
2332
+ versions: { stable: caskInfo.version, head: null, bottle: false },
2333
+ dependencies: [],
2334
+ build_dependencies: [],
2335
+ installed: caskInfo.installed ? [{
2336
+ version: caskInfo.installed,
2337
+ used_options: [],
2338
+ built_as_bottle: false,
2339
+ poured_from_bottle: false,
2340
+ time: caskInfo.installed_time ?? 0,
2341
+ runtime_dependencies: [],
2342
+ installed_as_dependency: false,
2343
+ installed_on_request: true
2344
+ }] : [],
2345
+ linked_keg: null,
2346
+ pinned: false,
2347
+ outdated: caskInfo.outdated,
2348
+ deprecated: false,
2349
+ keg_only: false,
2350
+ caveats: null
2351
+ };
2352
+ setFormula(formulaLike);
2353
+ setLoading2(false);
2354
+ return;
2355
+ }
2356
+ }
2357
+ const f = await getFormulaInfo(packageName);
1709
2358
  if (mountedRef.current) {
1710
2359
  setFormula(f);
1711
2360
  setLoading2(false);
1712
2361
  }
1713
- }).catch((err) => {
2362
+ };
2363
+ fetchInfo().catch((err) => {
1714
2364
  if (mountedRef.current) {
1715
2365
  setError2(err.message);
1716
2366
  setLoading2(false);
1717
2367
  }
1718
2368
  });
1719
- }, [packageName]);
2369
+ }, [packageName, packageType]);
1720
2370
  useEffect8(() => {
1721
2371
  if (!stream.isRunning && !stream.error && stream.lines.length > 0 && !hasRefreshed.current && packageName) {
1722
2372
  hasRefreshed.current = true;
1723
- getFormulaInfo(packageName).then((f) => {
2373
+ const refreshFn = packageType === "cask" ? getCaskInfo(packageName).then((c) => c ? { ...c, installed: c.installed ? [{ version: c.installed }] : [] } : null) : getFormulaInfo(packageName);
2374
+ refreshFn.then((f) => {
1724
2375
  if (mountedRef.current) {
1725
2376
  setFormula(f);
1726
2377
  }
@@ -1745,21 +2396,21 @@ function PackageInfoView() {
1745
2396
  }
1746
2397
  });
1747
2398
  if (!packageName) {
1748
- return /* @__PURE__ */ jsx17(Text17, { color: "#6B7280", italic: true, children: t("pkgInfo_noPackage") });
2399
+ return /* @__PURE__ */ jsx19(Text19, { color: COLORS.textSecondary, italic: true, children: t("pkgInfo_noPackage") });
1749
2400
  }
1750
- if (loading) return /* @__PURE__ */ jsx17(Loading, { message: t("loading_package", { name: packageName }) });
1751
- if (error) return /* @__PURE__ */ jsx17(ErrorMessage, { message: error });
1752
- if (!formula) return /* @__PURE__ */ jsx17(ErrorMessage, { message: t("pkgInfo_notFound") });
2401
+ if (loading) return /* @__PURE__ */ jsx19(Loading, { message: t("loading_package", { name: packageName }) });
2402
+ if (error) return /* @__PURE__ */ jsx19(ErrorMessage, { message: error });
2403
+ if (!formula) return /* @__PURE__ */ jsx19(ErrorMessage, { message: t("pkgInfo_notFound") });
1753
2404
  if (stream.isRunning || stream.lines.length > 0) {
1754
- return /* @__PURE__ */ jsxs17(Box15, { flexDirection: "column", children: [
1755
- /* @__PURE__ */ jsx17(ProgressLog, { lines: stream.lines, isRunning: stream.isRunning, title: t(ACTION_PROGRESS_KEYS[activeActionRef.current] ?? ACTION_PROGRESS_KEYS["install"], { name: formula.name }) }),
1756
- stream.isRunning && /* @__PURE__ */ jsxs17(Text17, { color: "#6B7280", children: [
2405
+ return /* @__PURE__ */ jsxs18(Box17, { flexDirection: "column", children: [
2406
+ /* @__PURE__ */ jsx19(ProgressLog, { lines: stream.lines, isRunning: stream.isRunning, title: t(ACTION_PROGRESS_KEYS[activeActionRef.current] ?? ACTION_PROGRESS_KEYS["install"], { name: formula.name }) }),
2407
+ stream.isRunning && /* @__PURE__ */ jsxs18(Text19, { color: COLORS.textSecondary, children: [
1757
2408
  "esc:",
1758
2409
  t("hint_cancel")
1759
2410
  ] }),
1760
- !stream.isRunning && /* @__PURE__ */ jsxs17(Fragment4, { children: [
1761
- /* @__PURE__ */ jsx17(Text17, { color: stream.error ? "#EF4444" : "#22C55E", bold: true, children: stream.error ? `\u2718 ${stream.error}` : `\u2714 ${t("pkgInfo_done")}` }),
1762
- /* @__PURE__ */ jsxs17(Text17, { color: "#6B7280", children: [
2411
+ !stream.isRunning && /* @__PURE__ */ jsxs18(Fragment4, { children: [
2412
+ /* @__PURE__ */ jsx19(Text19, { color: stream.error ? COLORS.error : COLORS.success, bold: true, children: stream.error ? `\u2718 ${stream.error}` : `\u2714 ${t("pkgInfo_done")}` }),
2413
+ /* @__PURE__ */ jsxs18(Text19, { color: COLORS.textSecondary, children: [
1763
2414
  "esc:",
1764
2415
  t("hint_back")
1765
2416
  ] })
@@ -1768,8 +2419,8 @@ function PackageInfoView() {
1768
2419
  }
1769
2420
  const installed = formula.installed[0];
1770
2421
  const isInstalled = formula.installed.length > 0;
1771
- return /* @__PURE__ */ jsxs17(Box15, { flexDirection: "column", children: [
1772
- confirmAction && /* @__PURE__ */ jsx17(
2422
+ return /* @__PURE__ */ jsxs18(Box17, { flexDirection: "column", children: [
2423
+ confirmAction && /* @__PURE__ */ jsx19(
1773
2424
  ConfirmDialog,
1774
2425
  {
1775
2426
  message: t(ACTION_CONFIRM_KEYS[confirmAction], { name: formula.name }),
@@ -1785,72 +2436,72 @@ function PackageInfoView() {
1785
2436
  onCancel: () => setConfirmAction(null)
1786
2437
  }
1787
2438
  ),
1788
- /* @__PURE__ */ jsxs17(Box15, { gap: 2, marginBottom: 1, children: [
1789
- /* @__PURE__ */ jsx17(GradientText, { colors: GRADIENTS.gold, bold: true, children: formula.name }),
1790
- /* @__PURE__ */ jsx17(Text17, { color: "#2DD4BF", children: installed?.version ?? formula.versions.stable }),
1791
- isInstalled && /* @__PURE__ */ jsx17(StatusBadge, { label: t("badge_installed"), variant: "success" }),
1792
- formula.outdated && /* @__PURE__ */ jsx17(StatusBadge, { label: t("badge_outdated"), variant: "warning" }),
1793
- formula.pinned && /* @__PURE__ */ jsx17(StatusBadge, { label: t("badge_pinned"), variant: "info" }),
1794
- formula.keg_only && /* @__PURE__ */ jsx17(StatusBadge, { label: t("badge_kegOnly"), variant: "muted" }),
1795
- formula.deprecated && /* @__PURE__ */ jsx17(StatusBadge, { label: t("badge_deprecated"), variant: "error" })
2439
+ /* @__PURE__ */ jsxs18(Box17, { gap: 2, marginBottom: 1, children: [
2440
+ /* @__PURE__ */ jsx19(GradientText, { colors: GRADIENTS.gold, bold: true, children: formula.name }),
2441
+ /* @__PURE__ */ jsx19(Text19, { color: COLORS.teal, children: installed?.version ?? formula.versions.stable }),
2442
+ isInstalled && /* @__PURE__ */ jsx19(StatusBadge, { label: t("badge_installed"), variant: "success" }),
2443
+ formula.outdated && /* @__PURE__ */ jsx19(StatusBadge, { label: t("badge_outdated"), variant: "warning" }),
2444
+ formula.pinned && /* @__PURE__ */ jsx19(StatusBadge, { label: t("badge_pinned"), variant: "info" }),
2445
+ formula.keg_only && /* @__PURE__ */ jsx19(StatusBadge, { label: t("badge_kegOnly"), variant: "muted" }),
2446
+ formula.deprecated && /* @__PURE__ */ jsx19(StatusBadge, { label: t("badge_deprecated"), variant: "error" })
1796
2447
  ] }),
1797
- /* @__PURE__ */ jsxs17(Box15, { flexDirection: "column", gap: 1, children: [
1798
- /* @__PURE__ */ jsx17(Text17, { children: formula.desc }),
1799
- /* @__PURE__ */ jsxs17(Box15, { flexDirection: "column", children: [
1800
- /* @__PURE__ */ jsx17(SectionHeader, { emoji: "\u{1F4CB}", title: t("pkgInfo_details"), gradient: ["#F9FAFB", "#9CA3AF"] }),
1801
- /* @__PURE__ */ jsxs17(Box15, { borderStyle: "round", borderColor: "#4B5563", paddingX: 2, flexDirection: "column", children: [
1802
- /* @__PURE__ */ jsxs17(Text17, { children: [
1803
- /* @__PURE__ */ jsx17(Text17, { color: "#9CA3AF", children: t("pkgInfo_homepage") }),
2448
+ /* @__PURE__ */ jsxs18(Box17, { flexDirection: "column", gap: 1, children: [
2449
+ /* @__PURE__ */ jsx19(Text19, { children: formula.desc }),
2450
+ /* @__PURE__ */ jsxs18(Box17, { flexDirection: "column", children: [
2451
+ /* @__PURE__ */ jsx19(SectionHeader, { emoji: "\u{1F4CB}", title: t("pkgInfo_details"), gradient: [COLORS.text, COLORS.muted] }),
2452
+ /* @__PURE__ */ jsxs18(Box17, { borderStyle: "round", borderColor: COLORS.border, paddingX: 2, flexDirection: "column", children: [
2453
+ /* @__PURE__ */ jsxs18(Text19, { children: [
2454
+ /* @__PURE__ */ jsx19(Text19, { color: COLORS.muted, children: t("pkgInfo_homepage") }),
1804
2455
  " ",
1805
2456
  formula.homepage
1806
2457
  ] }),
1807
- /* @__PURE__ */ jsxs17(Text17, { children: [
1808
- /* @__PURE__ */ jsx17(Text17, { color: "#9CA3AF", children: t("pkgInfo_license") }),
2458
+ /* @__PURE__ */ jsxs18(Text19, { children: [
2459
+ /* @__PURE__ */ jsx19(Text19, { color: COLORS.muted, children: t("pkgInfo_license") }),
1809
2460
  " ",
1810
2461
  formula.license
1811
2462
  ] }),
1812
- /* @__PURE__ */ jsxs17(Text17, { children: [
1813
- /* @__PURE__ */ jsx17(Text17, { color: "#9CA3AF", children: t("pkgInfo_tap") }),
2463
+ /* @__PURE__ */ jsxs18(Text19, { children: [
2464
+ /* @__PURE__ */ jsx19(Text19, { color: COLORS.muted, children: t("pkgInfo_tap") }),
1814
2465
  " ",
1815
2466
  formula.tap
1816
2467
  ] }),
1817
- /* @__PURE__ */ jsxs17(Text17, { children: [
1818
- /* @__PURE__ */ jsx17(Text17, { color: "#9CA3AF", children: t("pkgInfo_stable") }),
2468
+ /* @__PURE__ */ jsxs18(Text19, { children: [
2469
+ /* @__PURE__ */ jsx19(Text19, { color: COLORS.muted, children: t("pkgInfo_stable") }),
1819
2470
  " ",
1820
2471
  formula.versions.stable
1821
2472
  ] }),
1822
- installed && /* @__PURE__ */ jsxs17(Fragment4, { children: [
1823
- /* @__PURE__ */ jsxs17(Text17, { children: [
1824
- /* @__PURE__ */ jsx17(Text17, { color: "#9CA3AF", children: t("pkgInfo_installed") }),
2473
+ installed && /* @__PURE__ */ jsxs18(Fragment4, { children: [
2474
+ /* @__PURE__ */ jsxs18(Text19, { children: [
2475
+ /* @__PURE__ */ jsx19(Text19, { color: COLORS.muted, children: t("pkgInfo_installed") }),
1825
2476
  " ",
1826
2477
  installed.version,
1827
2478
  " (",
1828
2479
  formatRelativeTime(installed.time),
1829
2480
  ")"
1830
2481
  ] }),
1831
- /* @__PURE__ */ jsxs17(Text17, { children: [
1832
- /* @__PURE__ */ jsx17(Text17, { color: "#9CA3AF", children: t("pkgInfo_bottle") }),
2482
+ /* @__PURE__ */ jsxs18(Text19, { children: [
2483
+ /* @__PURE__ */ jsx19(Text19, { color: COLORS.muted, children: t("pkgInfo_bottle") }),
1833
2484
  " ",
1834
2485
  installed.poured_from_bottle ? t("common_yes") : t("common_no")
1835
2486
  ] }),
1836
- /* @__PURE__ */ jsxs17(Text17, { children: [
1837
- /* @__PURE__ */ jsx17(Text17, { color: "#9CA3AF", children: t("pkgInfo_onRequest") }),
2487
+ /* @__PURE__ */ jsxs18(Text19, { children: [
2488
+ /* @__PURE__ */ jsx19(Text19, { color: COLORS.muted, children: t("pkgInfo_onRequest") }),
1838
2489
  " ",
1839
2490
  installed.installed_on_request ? t("common_yes") : t("pkgInfo_noDependency")
1840
2491
  ] })
1841
2492
  ] })
1842
2493
  ] })
1843
2494
  ] }),
1844
- formula.dependencies.length > 0 && /* @__PURE__ */ jsxs17(Box15, { flexDirection: "column", children: [
1845
- /* @__PURE__ */ jsx17(SectionHeader, { emoji: "\u{1F517}", title: t("pkgInfo_dependencies", { count: formula.dependencies.length }), gradient: GRADIENTS.ocean }),
1846
- /* @__PURE__ */ jsx17(Box15, { paddingLeft: 2, flexWrap: "wrap", columnGap: 2, children: formula.dependencies.map((dep) => /* @__PURE__ */ jsx17(Text17, { color: "#9CA3AF", children: dep }, dep)) })
2495
+ formula.dependencies.length > 0 && /* @__PURE__ */ jsxs18(Box17, { flexDirection: "column", children: [
2496
+ /* @__PURE__ */ jsx19(SectionHeader, { emoji: "\u{1F517}", title: t("pkgInfo_dependencies", { count: formula.dependencies.length }), gradient: GRADIENTS.ocean }),
2497
+ /* @__PURE__ */ jsx19(Box17, { paddingLeft: 2, flexWrap: "wrap", columnGap: 2, children: formula.dependencies.map((dep) => /* @__PURE__ */ jsx19(Text19, { color: COLORS.muted, children: dep }, dep)) })
1847
2498
  ] }),
1848
- formula.caveats && /* @__PURE__ */ jsxs17(Box15, { flexDirection: "column", children: [
1849
- /* @__PURE__ */ jsx17(SectionHeader, { emoji: "\u26A0\uFE0F", title: t("pkgInfo_caveats"), color: "#F59E0B" }),
1850
- /* @__PURE__ */ jsx17(Box15, { borderStyle: "round", borderColor: "#F59E0B", paddingX: 2, children: /* @__PURE__ */ jsx17(Text17, { color: "#F59E0B", children: formula.caveats }) })
2499
+ formula.caveats && /* @__PURE__ */ jsxs18(Box17, { flexDirection: "column", children: [
2500
+ /* @__PURE__ */ jsx19(SectionHeader, { emoji: "\u26A0\uFE0F", title: t("pkgInfo_caveats"), color: COLORS.warning }),
2501
+ /* @__PURE__ */ jsx19(Box17, { borderStyle: "round", borderColor: COLORS.warning, paddingX: 2, children: /* @__PURE__ */ jsx19(Text19, { color: COLORS.warning, children: formula.caveats }) })
1851
2502
  ] })
1852
2503
  ] }),
1853
- /* @__PURE__ */ jsx17(Box15, { marginTop: 1, children: /* @__PURE__ */ jsxs17(Text17, { color: "#6B7280", children: [
2504
+ /* @__PURE__ */ jsx19(Box17, { marginTop: 1, children: /* @__PURE__ */ jsxs18(Text19, { color: COLORS.textSecondary, children: [
1854
2505
  isInstalled ? `u:${t("hint_uninstall")}` : `i:${t("hint_install")}`,
1855
2506
  isInstalled && formula.outdated ? ` U:${t("hint_upgrade")}` : "",
1856
2507
  ` esc:${t("hint_back")}`
@@ -1860,8 +2511,8 @@ function PackageInfoView() {
1860
2511
 
1861
2512
  // src/views/services.tsx
1862
2513
  import { useEffect as useEffect9, useState as useState7 } from "react";
1863
- import { Box as Box16, Text as Text18, useInput as useInput7, useStdout as useStdout3 } from "ink";
1864
- import { jsx as jsx18, jsxs as jsxs18 } from "react/jsx-runtime";
2514
+ import { Box as Box18, Text as Text20, useInput as useInput7, useStdout as useStdout4 } from "ink";
2515
+ import { jsx as jsx20, jsxs as jsxs19 } from "react/jsx-runtime";
1865
2516
  var STATUS_VARIANTS = {
1866
2517
  started: "success",
1867
2518
  stopped: "muted",
@@ -1873,16 +2524,21 @@ function ServicesView() {
1873
2524
  const [cursor, setCursor] = useState7(0);
1874
2525
  const [actionInProgress, setActionInProgress] = useState7(false);
1875
2526
  const [confirmAction, setConfirmAction] = useState7(null);
1876
- const { stdout } = useStdout3();
2527
+ const [lastError, setLastError] = useState7(null);
2528
+ const { stdout } = useStdout4();
1877
2529
  const cols = stdout?.columns ?? 80;
1878
2530
  const svcNameWidth = Math.floor(cols * 0.35);
1879
2531
  const svcStatusWidth = Math.floor(cols * 0.15);
2532
+ const MAX_VISIBLE_ROWS = Math.max(5, (stdout?.rows ?? 24) - 10);
1880
2533
  useEffect9(() => {
1881
2534
  fetchServices();
1882
2535
  }, []);
1883
2536
  useInput7((input, key) => {
1884
2537
  if (actionInProgress) return;
1885
2538
  if (confirmAction) return;
2539
+ if (lastError) {
2540
+ setLastError(null);
2541
+ }
1886
2542
  if (input === "j" || key.downArrow) {
1887
2543
  setCursor((c) => Math.min(c + 1, Math.max(0, services.length - 1)));
1888
2544
  } else if (input === "k" || key.upArrow) {
@@ -1894,26 +2550,31 @@ function ServicesView() {
1894
2550
  if (!svc) return;
1895
2551
  const doAction = (action) => {
1896
2552
  setActionInProgress(true);
1897
- void serviceAction2(svc.name, action).finally(() => {
2553
+ void serviceAction2(svc.name, action).catch((err) => {
2554
+ setLastError(err instanceof Error ? err.message : String(err));
2555
+ }).finally(() => {
1898
2556
  setActionInProgress(false);
2557
+ const storeError = useBrewStore.getState().errors["service-action"];
2558
+ if (storeError) setLastError(storeError);
1899
2559
  });
1900
2560
  };
1901
2561
  if (input === "s") doAction("start");
1902
2562
  else if (input === "S") setConfirmAction({ type: "stop", name: svc.name });
1903
2563
  else if (input === "R") setConfirmAction({ type: "restart", name: svc.name });
1904
2564
  });
1905
- const serviceActionError = useBrewStore((s) => s.errors["service-action"]);
1906
- if (loading.services) return /* @__PURE__ */ jsx18(Loading, { message: t("loading_services") });
1907
- if (errors.services) return /* @__PURE__ */ jsx18(ErrorMessage, { message: errors.services });
2565
+ if (loading.services) return /* @__PURE__ */ jsx20(Loading, { message: t("loading_services") });
2566
+ if (errors.services) return /* @__PURE__ */ jsx20(ErrorMessage, { message: errors.services });
1908
2567
  if (services.length === 0) {
1909
- return /* @__PURE__ */ jsxs18(Box16, { flexDirection: "column", children: [
1910
- /* @__PURE__ */ jsx18(SectionHeader, { emoji: "\u2699\uFE0F", title: t("services_title"), gradient: GRADIENTS.ocean }),
1911
- /* @__PURE__ */ jsx18(Text18, { color: "#6B7280", italic: true, children: t("services_noServices") })
2568
+ return /* @__PURE__ */ jsxs19(Box18, { flexDirection: "column", children: [
2569
+ /* @__PURE__ */ jsx20(SectionHeader, { emoji: "\u2699\uFE0F", title: t("services_title"), gradient: GRADIENTS.ocean }),
2570
+ /* @__PURE__ */ jsx20(Text20, { color: COLORS.textSecondary, italic: true, children: t("services_noServices") })
1912
2571
  ] });
1913
2572
  }
1914
- return /* @__PURE__ */ jsxs18(Box16, { flexDirection: "column", children: [
1915
- /* @__PURE__ */ jsx18(SectionHeader, { emoji: "\u2699\uFE0F", title: t("services_titleCount", { count: services.length }), gradient: GRADIENTS.ocean }),
1916
- confirmAction && /* @__PURE__ */ jsx18(Box16, { marginY: 1, children: /* @__PURE__ */ jsx18(
2573
+ const start = Math.max(0, cursor - Math.floor(MAX_VISIBLE_ROWS / 2));
2574
+ const visible = services.slice(start, start + MAX_VISIBLE_ROWS);
2575
+ return /* @__PURE__ */ jsxs19(Box18, { flexDirection: "column", children: [
2576
+ /* @__PURE__ */ jsx20(SectionHeader, { emoji: "\u2699\uFE0F", title: t("services_titleCount", { count: services.length }), gradient: GRADIENTS.ocean }),
2577
+ confirmAction && /* @__PURE__ */ jsx20(Box18, { marginY: 1, children: /* @__PURE__ */ jsx20(
1917
2578
  ConfirmDialog,
1918
2579
  {
1919
2580
  message: confirmAction.type === "stop" ? t("services_confirmStop", { name: confirmAction.name }) : t("services_confirmRestart", { name: confirmAction.name }),
@@ -1921,36 +2582,48 @@ function ServicesView() {
1921
2582
  const { type, name } = confirmAction;
1922
2583
  setConfirmAction(null);
1923
2584
  setActionInProgress(true);
1924
- void serviceAction2(name, type).finally(() => {
2585
+ void serviceAction2(name, type).catch((err) => {
2586
+ setLastError(err instanceof Error ? err.message : String(err));
2587
+ }).finally(() => {
1925
2588
  setActionInProgress(false);
2589
+ const storeError = useBrewStore.getState().errors["service-action"];
2590
+ if (storeError) setLastError(storeError);
1926
2591
  });
1927
2592
  },
1928
2593
  onCancel: () => setConfirmAction(null)
1929
2594
  }
1930
2595
  ) }),
1931
- /* @__PURE__ */ jsxs18(Box16, { flexDirection: "column", marginTop: 1, children: [
1932
- /* @__PURE__ */ jsxs18(Box16, { gap: 1, borderStyle: "single", borderBottom: true, borderTop: false, borderLeft: false, borderRight: false, borderColor: "#4B5563", paddingBottom: 0, children: [
1933
- /* @__PURE__ */ jsxs18(Text18, { bold: true, color: "#F9FAFB", children: [
2596
+ /* @__PURE__ */ jsxs19(Box18, { flexDirection: "column", marginTop: 1, children: [
2597
+ /* @__PURE__ */ jsxs19(Box18, { gap: 1, borderStyle: "single", borderBottom: true, borderTop: false, borderLeft: false, borderRight: false, borderColor: COLORS.border, paddingBottom: 0, children: [
2598
+ /* @__PURE__ */ jsxs19(Text20, { bold: true, color: COLORS.text, children: [
1934
2599
  " ",
1935
2600
  t("services_name").padEnd(svcNameWidth)
1936
2601
  ] }),
1937
- /* @__PURE__ */ jsx18(Text18, { bold: true, color: "#F9FAFB", children: t("services_status").padEnd(svcStatusWidth) }),
1938
- /* @__PURE__ */ jsx18(Text18, { bold: true, color: "#F9FAFB", children: t("services_user") })
2602
+ /* @__PURE__ */ jsx20(Text20, { bold: true, color: COLORS.text, children: t("services_status").padEnd(svcStatusWidth) }),
2603
+ /* @__PURE__ */ jsx20(Text20, { bold: true, color: COLORS.text, children: t("services_user") })
1939
2604
  ] }),
1940
- services.map((svc, i) => {
1941
- const isCurrent = i === cursor;
1942
- return /* @__PURE__ */ jsxs18(Box16, { gap: 1, children: [
1943
- /* @__PURE__ */ jsx18(Text18, { color: isCurrent ? "#22C55E" : "#9CA3AF", children: isCurrent ? "\u25B6" : " " }),
1944
- /* @__PURE__ */ jsx18(Text18, { bold: isCurrent, inverse: isCurrent, color: isCurrent ? "#F9FAFB" : "#9CA3AF", children: svc.name.padEnd(svcNameWidth - 2) }),
1945
- /* @__PURE__ */ jsx18(StatusBadge, { label: svc.status, variant: STATUS_VARIANTS[svc.status] }),
1946
- /* @__PURE__ */ jsx18(Text18, { color: "#9CA3AF", children: svc.user ?? "-" }),
1947
- svc.exit_code != null && svc.exit_code !== 0 && /* @__PURE__ */ jsx18(Text18, { color: "#EF4444", children: t("common_exit", { code: svc.exit_code }) })
2605
+ start > 0 && /* @__PURE__ */ jsxs19(Text20, { color: COLORS.textSecondary, dimColor: true, children: [
2606
+ " ",
2607
+ t("scroll_moreAbove", { count: start })
2608
+ ] }),
2609
+ visible.map((svc, i) => {
2610
+ const idx = start + i;
2611
+ const isCurrent = idx === cursor;
2612
+ return /* @__PURE__ */ jsxs19(SelectableRow, { isCurrent, children: [
2613
+ /* @__PURE__ */ jsx20(Text20, { bold: isCurrent, inverse: isCurrent, color: isCurrent ? COLORS.text : COLORS.muted, children: svc.name.padEnd(svcNameWidth - 2) }),
2614
+ /* @__PURE__ */ jsx20(StatusBadge, { label: svc.status, variant: STATUS_VARIANTS[svc.status] }),
2615
+ /* @__PURE__ */ jsx20(Text20, { color: COLORS.muted, children: svc.user ?? "-" }),
2616
+ svc.exit_code != null && svc.exit_code !== 0 && /* @__PURE__ */ jsx20(Text20, { color: COLORS.error, children: t("common_exit", { code: svc.exit_code }) })
1948
2617
  ] }, svc.name);
1949
- })
2618
+ }),
2619
+ start + MAX_VISIBLE_ROWS < services.length && /* @__PURE__ */ jsxs19(Text20, { color: COLORS.textSecondary, dimColor: true, children: [
2620
+ " ",
2621
+ t("scroll_moreBelow", { count: services.length - start - MAX_VISIBLE_ROWS })
2622
+ ] })
1950
2623
  ] }),
1951
- actionInProgress && /* @__PURE__ */ jsx18(Text18, { color: "#38BDF8", children: t("services_processing") }),
1952
- serviceActionError && /* @__PURE__ */ jsx18(Box16, { marginTop: 1, children: /* @__PURE__ */ jsx18(Text18, { color: "#EF4444", children: serviceActionError }) }),
1953
- /* @__PURE__ */ jsx18(Box16, { marginTop: 1, children: /* @__PURE__ */ jsxs18(Text18, { color: "#F9FAFB", bold: true, children: [
2624
+ actionInProgress && /* @__PURE__ */ jsx20(Text20, { color: COLORS.sky, children: t("services_processing") }),
2625
+ lastError && /* @__PURE__ */ jsx20(Box18, { marginTop: 1, children: /* @__PURE__ */ jsx20(Text20, { color: COLORS.error, children: lastError }) }),
2626
+ /* @__PURE__ */ jsx20(Box18, { marginTop: 1, children: /* @__PURE__ */ jsxs19(Text20, { color: COLORS.text, bold: true, children: [
1954
2627
  cursor + 1,
1955
2628
  "/",
1956
2629
  services.length
@@ -1959,56 +2632,61 @@ function ServicesView() {
1959
2632
  }
1960
2633
 
1961
2634
  // src/views/doctor.tsx
1962
- import { useEffect as useEffect10 } from "react";
1963
- import { Box as Box17, Text as Text19, useInput as useInput8 } from "ink";
1964
- import { jsx as jsx19, jsxs as jsxs19 } from "react/jsx-runtime";
2635
+ import { useEffect as useEffect10, useRef as useRef5 } from "react";
2636
+ import { Box as Box19, Text as Text21, useInput as useInput8 } from "ink";
2637
+ import { jsx as jsx21, jsxs as jsxs20 } from "react/jsx-runtime";
1965
2638
  function DoctorView() {
1966
2639
  const { doctorWarnings, doctorClean, loading, errors, fetchDoctor } = useBrewStore();
2640
+ const mountedRef = useRef5(true);
2641
+ useEffect10(() => {
2642
+ mountedRef.current = true;
2643
+ return () => {
2644
+ mountedRef.current = false;
2645
+ };
2646
+ }, []);
1967
2647
  useEffect10(() => {
1968
2648
  fetchDoctor();
1969
2649
  }, []);
1970
2650
  useInput8((input) => {
1971
2651
  if (input === "r") void fetchDoctor();
1972
2652
  });
1973
- if (loading.doctor) return /* @__PURE__ */ jsx19(Loading, { message: t("loading_doctor") });
1974
- if (errors.doctor) return /* @__PURE__ */ jsx19(ErrorMessage, { message: errors.doctor });
1975
- return /* @__PURE__ */ jsxs19(Box17, { flexDirection: "column", children: [
1976
- /* @__PURE__ */ jsx19(SectionHeader, { emoji: "\u{1FA7A}", title: t("doctor_title"), gradient: GRADIENTS.emerald }),
1977
- /* @__PURE__ */ jsxs19(Box17, { flexDirection: "column", marginTop: 1, children: [
1978
- doctorClean && /* @__PURE__ */ jsx19(Box17, { borderStyle: "round", borderColor: "#22C55E", paddingX: 2, paddingY: 0, children: /* @__PURE__ */ jsxs19(Text19, { color: "#22C55E", bold: true, children: [
1979
- "\u2714",
1980
- " ",
1981
- t("doctor_clean")
1982
- ] }) }),
1983
- doctorClean === false && doctorWarnings.length === 0 && /* @__PURE__ */ jsx19(Text19, { color: "#F59E0B", children: t("doctor_warningsNotCaptured") }),
1984
- doctorWarnings.map((warning, i) => /* @__PURE__ */ jsx19(Box17, { flexDirection: "column", marginBottom: 1, borderStyle: "single", borderColor: "#F59E0B", paddingX: 1, children: warning.split("\n").map((line, j) => /* @__PURE__ */ jsx19(Text19, { color: j === 0 ? "#F59E0B" : "#9CA3AF", children: line }, j)) }, warning.slice(0, 50) + i))
2653
+ if (loading.doctor) return /* @__PURE__ */ jsx21(Loading, { message: t("loading_doctor") });
2654
+ if (errors.doctor) return /* @__PURE__ */ jsx21(ErrorMessage, { message: errors.doctor });
2655
+ return /* @__PURE__ */ jsxs20(Box19, { flexDirection: "column", children: [
2656
+ /* @__PURE__ */ jsx21(SectionHeader, { emoji: "\u{1FA7A}", title: t("doctor_title"), gradient: GRADIENTS.emerald }),
2657
+ /* @__PURE__ */ jsxs20(Box19, { flexDirection: "column", marginTop: 1, children: [
2658
+ doctorClean && /* @__PURE__ */ jsx21(ResultBanner, { status: "success", message: `\u2714 ${t("doctor_clean")}` }),
2659
+ doctorClean === false && doctorWarnings.length === 0 && /* @__PURE__ */ jsx21(Text21, { color: COLORS.warning, children: t("doctor_warningsNotCaptured") }),
2660
+ doctorWarnings.map((warning, i) => (
2661
+ // FE-004: Improved React key
2662
+ /* @__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)}`)
2663
+ ))
1985
2664
  ] }),
1986
- /* @__PURE__ */ jsx19(Box17, { marginTop: 1, children: /* @__PURE__ */ jsx19(Text19, { color: "#F9FAFB", bold: true, children: doctorWarnings.length > 0 ? tp("plural_warnings", doctorWarnings.length) : "" }) })
2665
+ /* @__PURE__ */ jsx21(Box19, { marginTop: 1, children: /* @__PURE__ */ jsx21(Text21, { color: COLORS.text, bold: true, children: doctorWarnings.length > 0 ? tp("plural_warnings", doctorWarnings.length) : "" }) })
1987
2666
  ] });
1988
2667
  }
1989
2668
 
1990
2669
  // src/views/profiles.tsx
1991
- import { useEffect as useEffect11, useRef as useRef5, useState as useState8 } from "react";
1992
- import { Box as Box18, Text as Text20, useInput as useInput9 } from "ink";
1993
- import { TextInput as TextInput3 } from "@inkjs/ui";
2670
+ import { useEffect as useEffect11, useRef as useRef6, useState as useState8 } from "react";
2671
+ import { Box as Box24, useInput as useInput9 } from "ink";
1994
2672
 
1995
2673
  // src/stores/profile-store.ts
1996
- import { create as create4 } from "zustand";
2674
+ import { create as create5 } from "zustand";
1997
2675
 
1998
2676
  // src/lib/profiles/profile-manager.ts
1999
- import { readFile, writeFile, readdir, rm } from "fs/promises";
2000
- import { join, basename } from "path";
2677
+ import { readFile as readFile3, writeFile as writeFile3, readdir, rm as rm2, rename as rename2 } from "fs/promises";
2678
+ import { join as join2, basename } from "path";
2001
2679
 
2002
2680
  // src/lib/license/watermark.ts
2003
- function getWatermark(license) {
2681
+ function getWatermark(license, consent = true) {
2682
+ if (!consent) return "";
2004
2683
  if (!license?.customerEmail) return "";
2005
2684
  return `Licensed to: ${license.customerEmail}`;
2006
2685
  }
2007
2686
 
2008
2687
  // src/lib/profiles/profile-manager.ts
2009
- function proCheck() {
2010
- const { license, status } = useLicenseStore.getState();
2011
- requirePro(license, status);
2688
+ function proCheck(isPro) {
2689
+ if (!isPro) throw new Error("Pro license required");
2012
2690
  }
2013
2691
  var MAX_PROFILE_NAME_LENGTH = 100;
2014
2692
  function validateProfileName(name) {
@@ -2024,10 +2702,10 @@ function validateProfileName(name) {
2024
2702
  }
2025
2703
  function profilePath(name) {
2026
2704
  validateProfileName(name);
2027
- return join(PROFILES_DIR, `${basename(name)}.json`);
2705
+ return join2(PROFILES_DIR, `${basename(name)}.json`);
2028
2706
  }
2029
- async function listProfiles() {
2030
- proCheck();
2707
+ async function listProfiles(isPro) {
2708
+ proCheck(isPro);
2031
2709
  await ensureDataDirs();
2032
2710
  try {
2033
2711
  const files = await readdir(PROFILES_DIR);
@@ -2036,9 +2714,9 @@ async function listProfiles() {
2036
2714
  return [];
2037
2715
  }
2038
2716
  }
2039
- async function loadProfile(name) {
2040
- proCheck();
2041
- const raw = await readFile(profilePath(name), "utf-8");
2717
+ async function loadProfile(isPro, name) {
2718
+ proCheck(isPro);
2719
+ const raw = await readFile3(profilePath(name), "utf-8");
2042
2720
  let file;
2043
2721
  try {
2044
2722
  file = JSON.parse(raw);
@@ -2053,21 +2731,28 @@ async function loadProfile(name) {
2053
2731
  }
2054
2732
  return file.profile;
2055
2733
  }
2056
- async function saveProfile(profile) {
2057
- proCheck();
2734
+ async function saveProfile(isPro, profile) {
2735
+ proCheck(isPro);
2058
2736
  await ensureDataDirs();
2737
+ const filePath = profilePath(profile.name);
2738
+ const tmpPath = filePath + ".tmp";
2059
2739
  const file = { version: 1, profile };
2060
- await writeFile(profilePath(profile.name), JSON.stringify(file, null, 2), { encoding: "utf-8", mode: 384 });
2740
+ await writeFile3(tmpPath, JSON.stringify(file, null, 2), { encoding: "utf-8", mode: 384 });
2741
+ await rename2(tmpPath, filePath);
2061
2742
  }
2062
- async function deleteProfile(name) {
2063
- proCheck();
2743
+ async function deleteProfile(isPro, name) {
2744
+ proCheck(isPro);
2064
2745
  try {
2065
- await rm(profilePath(name));
2746
+ await rm2(profilePath(name));
2066
2747
  } catch {
2067
2748
  }
2068
2749
  }
2069
- async function exportCurrentSetup(name, description, license = null) {
2070
- proCheck();
2750
+ async function exportCurrentSetup(isPro, name, description, license = null, consent = true) {
2751
+ proCheck(isPro);
2752
+ const existingNames = await listProfiles(isPro);
2753
+ if (existingNames.includes(name)) {
2754
+ throw new Error("Profile already exists: " + name);
2755
+ }
2071
2756
  const [installed, leaves, tapsRaw] = await Promise.all([
2072
2757
  getInstalled(),
2073
2758
  getLeaves(),
@@ -2084,17 +2769,17 @@ async function exportCurrentSetup(name, description, license = null) {
2084
2769
  formulae: leaves,
2085
2770
  casks,
2086
2771
  taps,
2087
- exportedBy: getWatermark(license)
2088
- // Layer 16: Watermark who exported this profile
2772
+ exportedBy: consent ? getWatermark(license) : ""
2773
+ // SEG-003: Only embed watermark with consent
2089
2774
  };
2090
- await saveProfile(profile);
2775
+ await saveProfile(isPro, profile);
2091
2776
  return profile;
2092
2777
  }
2093
- async function updateProfile(oldName, newName, newDescription) {
2094
- proCheck();
2095
- const profile = await loadProfile(oldName);
2778
+ async function updateProfile(isPro, oldName, newName, newDescription) {
2779
+ proCheck(isPro);
2780
+ const profile = await loadProfile(isPro, oldName);
2096
2781
  if (oldName !== newName) {
2097
- await deleteProfile(oldName);
2782
+ await deleteProfile(isPro, oldName);
2098
2783
  }
2099
2784
  const updated = {
2100
2785
  ...profile,
@@ -2102,12 +2787,12 @@ async function updateProfile(oldName, newName, newDescription) {
2102
2787
  description: newDescription,
2103
2788
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
2104
2789
  };
2105
- await saveProfile(updated);
2790
+ await saveProfile(isPro, updated);
2106
2791
  }
2107
2792
  var TAP_PATTERN = /^[a-z0-9][-a-z0-9]*\/[a-z0-9][-a-z0-9]*$/;
2108
- var PKG_PATTERN = /^[a-z0-9][-a-z0-9_.@+]*$/;
2109
- async function* importProfile(profile) {
2110
- proCheck();
2793
+ var PKG_PATTERN2 = /^[a-z0-9][-a-z0-9_.@+]*$/;
2794
+ async function* importProfile(isPro, profile) {
2795
+ proCheck(isPro);
2111
2796
  const installed = await getInstalled();
2112
2797
  const installedFormulae = new Set(installed.formulae.map((f) => f.name));
2113
2798
  const installedCasks = new Set(installed.casks.filter((c) => c.installed).map((c) => c.token));
@@ -2124,7 +2809,7 @@ async function* importProfile(profile) {
2124
2809
  }
2125
2810
  const missingFormulae = profile.formulae.filter((f) => !installedFormulae.has(f));
2126
2811
  for (const name of missingFormulae) {
2127
- if (!PKG_PATTERN.test(name)) {
2812
+ if (!PKG_PATTERN2.test(name)) {
2128
2813
  yield `Skipping invalid formula name: ${name}`;
2129
2814
  continue;
2130
2815
  }
@@ -2135,7 +2820,7 @@ async function* importProfile(profile) {
2135
2820
  }
2136
2821
  const missingCasks = profile.casks.filter((c) => !installedCasks.has(c));
2137
2822
  for (const name of missingCasks) {
2138
- if (!PKG_PATTERN.test(name)) {
2823
+ if (!PKG_PATTERN2.test(name)) {
2139
2824
  yield `Skipping invalid cask name: ${name}`;
2140
2825
  continue;
2141
2826
  }
@@ -2149,20 +2834,23 @@ async function* importProfile(profile) {
2149
2834
  }
2150
2835
 
2151
2836
  // src/stores/profile-store.ts
2152
- var useProfileStore = create4((set) => ({
2837
+ function getIsPro() {
2838
+ return useLicenseStore.getState().isPro();
2839
+ }
2840
+ var useProfileStore = create5((set) => ({
2153
2841
  profileNames: [],
2154
2842
  selectedProfile: null,
2155
2843
  loading: false,
2156
2844
  loadError: null,
2157
2845
  fetchProfiles: async () => {
2158
2846
  set({ loading: true });
2159
- const names = await listProfiles();
2847
+ const names = await listProfiles(getIsPro());
2160
2848
  set({ profileNames: names, loading: false });
2161
2849
  },
2162
2850
  loadProfile: async (name) => {
2163
2851
  set({ loadError: null });
2164
2852
  try {
2165
- const profile = await loadProfile(name);
2853
+ const profile = await loadProfile(getIsPro(), name);
2166
2854
  set({ selectedProfile: profile });
2167
2855
  } catch (err) {
2168
2856
  set({ loadError: err instanceof Error ? err.message : String(err) });
@@ -2172,8 +2860,8 @@ var useProfileStore = create4((set) => ({
2172
2860
  set({ loading: true, loadError: null });
2173
2861
  try {
2174
2862
  const license = useLicenseStore.getState().license;
2175
- await exportCurrentSetup(name, description, license);
2176
- const names = await listProfiles();
2863
+ await exportCurrentSetup(getIsPro(), name, description, license);
2864
+ const names = await listProfiles(getIsPro());
2177
2865
  set({ profileNames: names, loading: false });
2178
2866
  } catch (err) {
2179
2867
  set({ loading: false, loadError: err instanceof Error ? err.message : String(err) });
@@ -2181,16 +2869,16 @@ var useProfileStore = create4((set) => ({
2181
2869
  }
2182
2870
  },
2183
2871
  deleteProfile: async (name) => {
2184
- await deleteProfile(name);
2185
- const names = await listProfiles();
2872
+ await deleteProfile(getIsPro(), name);
2873
+ const names = await listProfiles(getIsPro());
2186
2874
  set({ profileNames: names, selectedProfile: null });
2187
2875
  },
2188
2876
  updateProfile: async (oldName, newName, newDescription) => {
2189
2877
  set({ loadError: null });
2190
2878
  try {
2191
- await updateProfile(oldName, newName, newDescription);
2192
- const names = await listProfiles();
2193
- const updated = await loadProfile(newName);
2879
+ await updateProfile(getIsPro(), oldName, newName, newDescription);
2880
+ const names = await listProfiles(getIsPro());
2881
+ const updated = await loadProfile(getIsPro(), newName);
2194
2882
  set({ profileNames: names, selectedProfile: updated });
2195
2883
  } catch (err) {
2196
2884
  set({ loadError: err instanceof Error ? err.message : String(err) });
@@ -2198,8 +2886,141 @@ var useProfileStore = create4((set) => ({
2198
2886
  }
2199
2887
  }));
2200
2888
 
2889
+ // src/views/profiles/profile-list-mode.tsx
2890
+ import { Box as Box20, Text as Text22 } from "ink";
2891
+ import { jsx as jsx22, jsxs as jsxs21 } from "react/jsx-runtime";
2892
+ function ProfileListMode({ profileNames, cursor, confirmDelete, loadError, onConfirmDelete, onCancelDelete }) {
2893
+ return /* @__PURE__ */ jsxs21(Box20, { flexDirection: "column", children: [
2894
+ /* @__PURE__ */ jsx22(SectionHeader, { emoji: "\u{1F4C1}", title: t("profiles_title", { count: profileNames.length }), gradient: GRADIENTS.gold }),
2895
+ loadError && /* @__PURE__ */ jsx22(Box20, { marginY: 1, children: /* @__PURE__ */ jsx22(Text22, { color: COLORS.error, children: loadError }) }),
2896
+ confirmDelete && profileNames[cursor] && /* @__PURE__ */ jsx22(Box20, { marginY: 1, children: /* @__PURE__ */ jsx22(
2897
+ ConfirmDialog,
2898
+ {
2899
+ message: t("profiles_confirmDelete", { name: profileNames[cursor] }),
2900
+ onConfirm: onConfirmDelete,
2901
+ onCancel: onCancelDelete
2902
+ }
2903
+ ) }),
2904
+ 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: [
2905
+ /* @__PURE__ */ jsx22(Text22, { color: COLORS.textSecondary, italic: true, children: t("profiles_noProfiles") }),
2906
+ /* @__PURE__ */ jsxs21(Text22, { color: COLORS.muted, children: [
2907
+ t("profiles_press"),
2908
+ " ",
2909
+ /* @__PURE__ */ jsx22(Text22, { color: COLORS.gold, bold: true, children: "n" }),
2910
+ " ",
2911
+ t("profiles_exportHint")
2912
+ ] })
2913
+ ] }) }),
2914
+ profileNames.length > 0 && !confirmDelete && /* @__PURE__ */ jsxs21(Box20, { flexDirection: "column", marginTop: 1, children: [
2915
+ profileNames.map((name, i) => {
2916
+ const isCurrent = i === cursor;
2917
+ return /* @__PURE__ */ jsx22(SelectableRow, { isCurrent, children: /* @__PURE__ */ jsx22(Text22, { bold: isCurrent, inverse: isCurrent, children: name }) }, name);
2918
+ }),
2919
+ /* @__PURE__ */ jsx22(Box20, { marginTop: 1, children: /* @__PURE__ */ jsxs21(Text22, { color: COLORS.text, bold: true, children: [
2920
+ cursor + 1,
2921
+ "/",
2922
+ profileNames.length
2923
+ ] }) })
2924
+ ] })
2925
+ ] });
2926
+ }
2927
+
2928
+ // src/views/profiles/profile-detail-mode.tsx
2929
+ import { Box as Box21, Text as Text23 } from "ink";
2930
+ import { jsx as jsx23, jsxs as jsxs22 } from "react/jsx-runtime";
2931
+ function ProfileDetailMode({ profile }) {
2932
+ return /* @__PURE__ */ jsxs22(Box21, { flexDirection: "column", children: [
2933
+ /* @__PURE__ */ jsx23(Text23, { bold: true, color: COLORS.gold, children: profile.name }),
2934
+ /* @__PURE__ */ jsx23(Text23, { color: COLORS.muted, children: profile.description }),
2935
+ /* @__PURE__ */ jsx23(Text23, { color: COLORS.muted, children: t("profiles_created", { date: formatDate(profile.createdAt) }) }),
2936
+ /* @__PURE__ */ jsxs22(Box21, { marginTop: 1, flexDirection: "column", children: [
2937
+ /* @__PURE__ */ jsx23(Text23, { bold: true, children: t("profiles_formulaeCount", { count: profile.formulae.length }) }),
2938
+ /* @__PURE__ */ jsxs22(Box21, { paddingLeft: 2, flexDirection: "column", children: [
2939
+ profile.formulae.slice(0, 30).map((f) => /* @__PURE__ */ jsx23(Text23, { color: COLORS.muted, children: f }, f)),
2940
+ profile.formulae.length > 30 && /* @__PURE__ */ jsx23(Text23, { color: COLORS.textSecondary, italic: true, children: t("common_andMore", { count: profile.formulae.length - 30 }) })
2941
+ ] }),
2942
+ /* @__PURE__ */ jsx23(Text23, { bold: true, children: t("profiles_casksCount", { count: profile.casks.length }) }),
2943
+ /* @__PURE__ */ jsx23(Box21, { paddingLeft: 2, flexDirection: "column", children: profile.casks.map((c) => /* @__PURE__ */ jsx23(Text23, { color: COLORS.muted, children: c }, c)) })
2944
+ ] }),
2945
+ /* @__PURE__ */ jsx23(Box21, { marginTop: 1, children: /* @__PURE__ */ jsxs22(Text23, { color: COLORS.textSecondary, children: [
2946
+ "esc:",
2947
+ t("hint_back"),
2948
+ " e:",
2949
+ t("hint_edit"),
2950
+ " i:",
2951
+ t("hint_importProfile")
2952
+ ] }) })
2953
+ ] });
2954
+ }
2955
+
2956
+ // src/views/profiles/profile-create-flow.tsx
2957
+ import { Box as Box22, Text as Text24 } from "ink";
2958
+ import { TextInput as TextInput3 } from "@inkjs/ui";
2959
+ import { jsx as jsx24, jsxs as jsxs23 } from "react/jsx-runtime";
2960
+ function ProfileCreateName({ onSubmit }) {
2961
+ return /* @__PURE__ */ jsxs23(Box22, { flexDirection: "column", children: [
2962
+ /* @__PURE__ */ jsx24(Text24, { bold: true, children: t("profiles_createName") }),
2963
+ /* @__PURE__ */ jsx24(
2964
+ TextInput3,
2965
+ {
2966
+ placeholder: t("profiles_namePlaceholder"),
2967
+ onSubmit
2968
+ }
2969
+ )
2970
+ ] });
2971
+ }
2972
+ function ProfileCreateDesc({ name, loadError, onSubmit }) {
2973
+ return /* @__PURE__ */ jsxs23(Box22, { flexDirection: "column", children: [
2974
+ /* @__PURE__ */ jsx24(Text24, { bold: true, children: t("profiles_createDesc", { name }) }),
2975
+ loadError && /* @__PURE__ */ jsxs23(Text24, { color: COLORS.error, children: [
2976
+ t("error_prefix"),
2977
+ loadError
2978
+ ] }),
2979
+ /* @__PURE__ */ jsx24(
2980
+ TextInput3,
2981
+ {
2982
+ placeholder: t("profiles_descPlaceholder"),
2983
+ onSubmit
2984
+ }
2985
+ )
2986
+ ] });
2987
+ }
2988
+
2989
+ // src/views/profiles/profile-edit-flow.tsx
2990
+ import { Box as Box23, Text as Text25 } from "ink";
2991
+ import { TextInput as TextInput4 } from "@inkjs/ui";
2992
+ import { jsx as jsx25, jsxs as jsxs24 } from "react/jsx-runtime";
2993
+ function ProfileEditName({ defaultName, onSubmit }) {
2994
+ return /* @__PURE__ */ jsxs24(Box23, { flexDirection: "column", children: [
2995
+ /* @__PURE__ */ jsx25(Text25, { bold: true, children: t("profiles_editName") }),
2996
+ /* @__PURE__ */ jsx25(
2997
+ TextInput4,
2998
+ {
2999
+ defaultValue: defaultName,
3000
+ onSubmit
3001
+ }
3002
+ )
3003
+ ] });
3004
+ }
3005
+ function ProfileEditDesc({ name, defaultDesc, loadError, onSubmit }) {
3006
+ return /* @__PURE__ */ jsxs24(Box23, { flexDirection: "column", children: [
3007
+ /* @__PURE__ */ jsx25(Text25, { bold: true, children: t("profiles_editDesc", { name }) }),
3008
+ loadError && /* @__PURE__ */ jsxs24(Text25, { color: COLORS.error, children: [
3009
+ t("error_prefix"),
3010
+ loadError
3011
+ ] }),
3012
+ /* @__PURE__ */ jsx25(
3013
+ TextInput4,
3014
+ {
3015
+ defaultValue: defaultDesc,
3016
+ onSubmit
3017
+ }
3018
+ )
3019
+ ] });
3020
+ }
3021
+
2201
3022
  // src/views/profiles.tsx
2202
- import { jsx as jsx20, jsxs as jsxs20 } from "react/jsx-runtime";
3023
+ import { jsx as jsx26, jsxs as jsxs25 } from "react/jsx-runtime";
2203
3024
  function ProfilesView() {
2204
3025
  const { profileNames, selectedProfile, loading, loadError, fetchProfiles, loadProfile: loadProfile2, exportCurrent, deleteProfile: deleteProfile2, updateProfile: updateProfile2 } = useProfileStore();
2205
3026
  const [cursor, setCursor] = useState8(0);
@@ -2210,9 +3031,11 @@ function ProfilesView() {
2210
3031
  const [editDesc, setEditDesc] = useState8("");
2211
3032
  const [importLines, setImportLines] = useState8([]);
2212
3033
  const [importRunning, setImportRunning] = useState8(false);
3034
+ const [importHadError, setImportHadError] = useState8(false);
3035
+ const [importProfile2, setImportProfile] = useState8(null);
2213
3036
  const { openModal, closeModal } = useModalStore();
2214
- const importGenRef = useRef5(null);
2215
- const mountedRef = useRef5(true);
3037
+ const importGenRef = useRef6(null);
3038
+ const mountedRef = useRef6(true);
2216
3039
  useEffect11(() => {
2217
3040
  fetchProfiles();
2218
3041
  }, []);
@@ -2225,7 +3048,7 @@ function ProfilesView() {
2225
3048
  };
2226
3049
  }, []);
2227
3050
  useEffect11(() => {
2228
- if (mode === "detail" || mode === "create-name" || mode === "create-desc" || mode === "importing" || mode === "edit-name" || mode === "edit-desc") {
3051
+ if (mode !== "list") {
2229
3052
  openModal();
2230
3053
  return () => {
2231
3054
  closeModal();
@@ -2249,7 +3072,7 @@ function ProfilesView() {
2249
3072
  return;
2250
3073
  }
2251
3074
  if (input === "i" && profileNames[cursor]) {
2252
- void startImport(profileNames[cursor]);
3075
+ void prepareImport(profileNames[cursor]);
2253
3076
  return;
2254
3077
  }
2255
3078
  if (input === "j" || key.downArrow) setCursor((c) => Math.min(c + 1, Math.max(0, profileNames.length - 1)));
@@ -2269,13 +3092,26 @@ function ProfilesView() {
2269
3092
  useInput9(() => {
2270
3093
  setMode("list");
2271
3094
  }, { isActive: mode === "importing" && !importRunning });
2272
- const startImport = async (name) => {
3095
+ const prepareImport = async (name) => {
3096
+ try {
3097
+ const isPro = useLicenseStore.getState().isPro();
3098
+ const profile = await loadProfile(isPro, name);
3099
+ setImportProfile(profile);
3100
+ setMode("confirm-import");
3101
+ } catch (err) {
3102
+ setImportLines([`${t("error_prefix")}${err instanceof Error ? err.message : err}`]);
3103
+ setMode("importing");
3104
+ setImportRunning(false);
3105
+ }
3106
+ };
3107
+ const startImport = async (profile) => {
2273
3108
  setMode("importing");
2274
3109
  setImportLines([]);
2275
3110
  setImportRunning(true);
3111
+ setImportHadError(false);
2276
3112
  try {
2277
- const profile = await loadProfile(name);
2278
- const gen = importProfile(profile);
3113
+ const isPro = useLicenseStore.getState().isPro();
3114
+ const gen = importProfile(isPro, profile);
2279
3115
  importGenRef.current = gen;
2280
3116
  for await (const line of gen) {
2281
3117
  if (!mountedRef.current) break;
@@ -2283,6 +3119,7 @@ function ProfilesView() {
2283
3119
  }
2284
3120
  } catch (err) {
2285
3121
  if (mountedRef.current) {
3122
+ setImportHadError(true);
2286
3123
  setImportLines((prev) => [...prev, `${t("error_prefix")}${err instanceof Error ? err.message : err}`]);
2287
3124
  }
2288
3125
  } finally {
@@ -2292,163 +3129,105 @@ function ProfilesView() {
2292
3129
  }
2293
3130
  }
2294
3131
  };
2295
- if (loading) return /* @__PURE__ */ jsx20(Loading, { message: t("loading_profiles") });
3132
+ if (loading) return /* @__PURE__ */ jsx26(Loading, { message: t("loading_profiles") });
3133
+ if (mode === "confirm-import" && importProfile2) {
3134
+ return /* @__PURE__ */ jsx26(Box24, { flexDirection: "column", children: /* @__PURE__ */ jsx26(
3135
+ ConfirmDialog,
3136
+ {
3137
+ message: t("profiles_importSummary", {
3138
+ formulae: String(importProfile2.formulae.length),
3139
+ casks: String(importProfile2.casks.length)
3140
+ }),
3141
+ onConfirm: () => {
3142
+ const profile = importProfile2;
3143
+ setImportProfile(null);
3144
+ void startImport(profile);
3145
+ },
3146
+ onCancel: () => {
3147
+ setImportProfile(null);
3148
+ setMode("list");
3149
+ }
3150
+ }
3151
+ ) });
3152
+ }
2296
3153
  if (mode === "importing") {
2297
- return /* @__PURE__ */ jsxs20(Box18, { flexDirection: "column", children: [
2298
- /* @__PURE__ */ jsx20(ProgressLog, { lines: importLines, isRunning: importRunning, title: t("profiles_importTitle") }),
2299
- !importRunning && /* @__PURE__ */ jsx20(Box18, { marginTop: 1, children: /* @__PURE__ */ jsx20(Box18, { borderStyle: "round", borderColor: "#22C55E", paddingX: 2, paddingY: 0, children: /* @__PURE__ */ jsxs20(Text20, { color: "#22C55E", bold: true, children: [
2300
- "\u2714",
2301
- " ",
2302
- t("profiles_importComplete")
2303
- ] }) }) })
3154
+ return /* @__PURE__ */ jsxs25(Box24, { flexDirection: "column", children: [
3155
+ /* @__PURE__ */ jsx26(ProgressLog, { lines: importLines, isRunning: importRunning, title: t("profiles_importTitle") }),
3156
+ !importRunning && /* @__PURE__ */ jsx26(Box24, { marginTop: 1, children: /* @__PURE__ */ jsx26(ResultBanner, { status: importHadError ? "error" : "success", message: importHadError ? t("profiles_importPartial") : `\u2714 ${t("profiles_importComplete")}` }) })
2304
3157
  ] });
2305
3158
  }
2306
3159
  if (mode === "create-name") {
2307
- return /* @__PURE__ */ jsxs20(Box18, { flexDirection: "column", children: [
2308
- /* @__PURE__ */ jsx20(Text20, { bold: true, children: t("profiles_createName") }),
2309
- /* @__PURE__ */ jsx20(
2310
- TextInput3,
2311
- {
2312
- placeholder: t("profiles_namePlaceholder"),
2313
- onSubmit: (val) => {
2314
- setNewName(val);
2315
- setMode("create-desc");
2316
- }
2317
- }
2318
- )
2319
- ] });
3160
+ return /* @__PURE__ */ jsx26(ProfileCreateName, { onSubmit: (val) => {
3161
+ setNewName(val);
3162
+ setMode("create-desc");
3163
+ } });
2320
3164
  }
2321
3165
  if (mode === "create-desc") {
2322
- return /* @__PURE__ */ jsxs20(Box18, { flexDirection: "column", children: [
2323
- /* @__PURE__ */ jsx20(Text20, { bold: true, children: t("profiles_createDesc", { name: newName }) }),
2324
- loadError && /* @__PURE__ */ jsxs20(Text20, { color: "#EF4444", children: [
2325
- t("error_prefix"),
2326
- loadError
2327
- ] }),
2328
- /* @__PURE__ */ jsx20(
2329
- TextInput3,
2330
- {
2331
- placeholder: t("profiles_descPlaceholder"),
2332
- onSubmit: async (val) => {
2333
- try {
2334
- await exportCurrent(newName, val);
2335
- } finally {
2336
- setMode("list");
2337
- setNewName("");
2338
- }
3166
+ return /* @__PURE__ */ jsx26(
3167
+ ProfileCreateDesc,
3168
+ {
3169
+ name: newName,
3170
+ loadError,
3171
+ onSubmit: async (val) => {
3172
+ try {
3173
+ await exportCurrent(newName, val);
3174
+ } finally {
3175
+ setMode("list");
3176
+ setNewName("");
2339
3177
  }
2340
3178
  }
2341
- )
2342
- ] });
3179
+ }
3180
+ );
2343
3181
  }
2344
3182
  if (mode === "edit-name") {
2345
- return /* @__PURE__ */ jsxs20(Box18, { flexDirection: "column", children: [
2346
- /* @__PURE__ */ jsx20(Text20, { bold: true, children: t("profiles_editName") }),
2347
- /* @__PURE__ */ jsx20(
2348
- TextInput3,
2349
- {
2350
- defaultValue: editName,
2351
- onSubmit: (val) => {
2352
- setEditName(val);
2353
- setMode("edit-desc");
2354
- }
2355
- }
2356
- )
2357
- ] });
3183
+ return /* @__PURE__ */ jsx26(ProfileEditName, { defaultName: editName, onSubmit: (val) => {
3184
+ setEditName(val);
3185
+ setMode("edit-desc");
3186
+ } });
2358
3187
  }
2359
3188
  if (mode === "edit-desc") {
2360
- return /* @__PURE__ */ jsxs20(Box18, { flexDirection: "column", children: [
2361
- /* @__PURE__ */ jsx20(Text20, { bold: true, children: t("profiles_editDesc", { name: editName }) }),
2362
- loadError && /* @__PURE__ */ jsxs20(Text20, { color: "#EF4444", children: [
2363
- t("error_prefix"),
2364
- loadError
2365
- ] }),
2366
- /* @__PURE__ */ jsx20(
2367
- TextInput3,
2368
- {
2369
- defaultValue: editDesc,
2370
- onSubmit: async (val) => {
2371
- if (selectedProfile) {
2372
- await updateProfile2(selectedProfile.name, editName, val);
2373
- }
2374
- setMode("detail");
2375
- setEditName("");
2376
- setEditDesc("");
3189
+ return /* @__PURE__ */ jsx26(
3190
+ ProfileEditDesc,
3191
+ {
3192
+ name: editName,
3193
+ defaultDesc: editDesc,
3194
+ loadError,
3195
+ onSubmit: async (val) => {
3196
+ if (selectedProfile) {
3197
+ await updateProfile2(selectedProfile.name, editName, val);
2377
3198
  }
3199
+ setMode("detail");
3200
+ setEditName("");
3201
+ setEditDesc("");
2378
3202
  }
2379
- )
2380
- ] });
3203
+ }
3204
+ );
2381
3205
  }
2382
3206
  if (mode === "detail" && selectedProfile) {
2383
- return /* @__PURE__ */ jsxs20(Box18, { flexDirection: "column", children: [
2384
- /* @__PURE__ */ jsx20(Text20, { bold: true, color: "#FFD700", children: selectedProfile.name }),
2385
- /* @__PURE__ */ jsx20(Text20, { color: "#9CA3AF", children: selectedProfile.description }),
2386
- /* @__PURE__ */ jsx20(Text20, { color: "#9CA3AF", children: t("profiles_created", { date: new Date(selectedProfile.createdAt).toLocaleDateString() }) }),
2387
- /* @__PURE__ */ jsxs20(Box18, { marginTop: 1, flexDirection: "column", children: [
2388
- /* @__PURE__ */ jsx20(Text20, { bold: true, children: t("profiles_formulaeCount", { count: selectedProfile.formulae.length }) }),
2389
- /* @__PURE__ */ jsxs20(Box18, { paddingLeft: 2, flexDirection: "column", children: [
2390
- selectedProfile.formulae.slice(0, 30).map((f) => /* @__PURE__ */ jsx20(Text20, { color: "#9CA3AF", children: f }, f)),
2391
- selectedProfile.formulae.length > 30 && /* @__PURE__ */ jsx20(Text20, { color: "#6B7280", italic: true, children: t("common_andMore", { count: selectedProfile.formulae.length - 30 }) })
2392
- ] }),
2393
- /* @__PURE__ */ jsx20(Text20, { bold: true, children: t("profiles_casksCount", { count: selectedProfile.casks.length }) }),
2394
- /* @__PURE__ */ jsx20(Box18, { paddingLeft: 2, flexDirection: "column", children: selectedProfile.casks.map((c) => /* @__PURE__ */ jsx20(Text20, { color: "#9CA3AF", children: c }, c)) })
2395
- ] }),
2396
- /* @__PURE__ */ jsx20(Box18, { marginTop: 1, children: /* @__PURE__ */ jsxs20(Text20, { color: "#6B7280", children: [
2397
- "esc:",
2398
- t("hint_back"),
2399
- " e:",
2400
- t("hint_edit"),
2401
- " i:",
2402
- t("hint_importProfile")
2403
- ] }) })
2404
- ] });
3207
+ return /* @__PURE__ */ jsx26(ProfileDetailMode, { profile: selectedProfile });
2405
3208
  }
2406
- return /* @__PURE__ */ jsxs20(Box18, { flexDirection: "column", children: [
2407
- /* @__PURE__ */ jsx20(SectionHeader, { emoji: "\u{1F4C1}", title: t("profiles_title", { count: profileNames.length }), gradient: GRADIENTS.gold }),
2408
- confirmDelete && profileNames[cursor] && /* @__PURE__ */ jsx20(Box18, { marginY: 1, children: /* @__PURE__ */ jsx20(
2409
- ConfirmDialog,
2410
- {
2411
- message: t("profiles_confirmDelete", { name: profileNames[cursor] }),
2412
- onConfirm: () => {
2413
- void deleteProfile2(profileNames[cursor]);
2414
- setConfirmDelete(false);
2415
- },
2416
- onCancel: () => setConfirmDelete(false)
2417
- }
2418
- ) }),
2419
- profileNames.length === 0 && !confirmDelete && /* @__PURE__ */ jsx20(Box18, { marginTop: 1, borderStyle: "round", borderColor: "#6B7280", paddingX: 2, paddingY: 0, children: /* @__PURE__ */ jsxs20(Box18, { flexDirection: "column", children: [
2420
- /* @__PURE__ */ jsx20(Text20, { color: "#6B7280", italic: true, children: t("profiles_noProfiles") }),
2421
- /* @__PURE__ */ jsxs20(Text20, { color: "#9CA3AF", children: [
2422
- t("profiles_press"),
2423
- " ",
2424
- /* @__PURE__ */ jsx20(Text20, { color: "#FFD700", bold: true, children: "n" }),
2425
- " ",
2426
- t("profiles_exportHint")
2427
- ] })
2428
- ] }) }),
2429
- profileNames.length > 0 && !confirmDelete && /* @__PURE__ */ jsxs20(Box18, { flexDirection: "column", marginTop: 1, children: [
2430
- profileNames.map((name, i) => {
2431
- const isCurrent = i === cursor;
2432
- return /* @__PURE__ */ jsxs20(Box18, { gap: 1, children: [
2433
- /* @__PURE__ */ jsx20(Text20, { color: isCurrent ? "#22C55E" : "#9CA3AF", children: isCurrent ? "\u25B6" : " " }),
2434
- /* @__PURE__ */ jsx20(Text20, { bold: isCurrent, inverse: isCurrent, children: name })
2435
- ] }, name);
2436
- }),
2437
- /* @__PURE__ */ jsx20(Box18, { marginTop: 1, children: /* @__PURE__ */ jsxs20(Text20, { color: "#F9FAFB", bold: true, children: [
2438
- cursor + 1,
2439
- "/",
2440
- profileNames.length
2441
- ] }) })
2442
- ] })
2443
- ] });
3209
+ return /* @__PURE__ */ jsx26(
3210
+ ProfileListMode,
3211
+ {
3212
+ profileNames,
3213
+ cursor,
3214
+ confirmDelete,
3215
+ loadError,
3216
+ onConfirmDelete: () => {
3217
+ void deleteProfile2(profileNames[cursor]);
3218
+ setConfirmDelete(false);
3219
+ },
3220
+ onCancelDelete: () => setConfirmDelete(false)
3221
+ }
3222
+ );
2444
3223
  }
2445
3224
 
2446
3225
  // src/views/smart-cleanup.tsx
2447
- import { useEffect as useEffect12, useRef as useRef6, useState as useState9 } from "react";
2448
- import { Box as Box19, Text as Text21, useInput as useInput10 } from "ink";
3226
+ import { useEffect as useEffect12, useRef as useRef7, useState as useState9 } from "react";
3227
+ import { Box as Box25, Text as Text26, useInput as useInput10 } from "ink";
2449
3228
 
2450
3229
  // src/stores/cleanup-store.ts
2451
- import { create as create5 } from "zustand";
3230
+ import { create as create6 } from "zustand";
2452
3231
 
2453
3232
  // src/lib/cleanup/cleanup-analyzer.ts
2454
3233
  import { execFile } from "child_process";
@@ -2471,9 +3250,8 @@ async function getCellarPath(name) {
2471
3250
  return null;
2472
3251
  }
2473
3252
  }
2474
- async function analyzeCleanup(formulae, leaves) {
2475
- const { license, status } = useLicenseStore.getState();
2476
- requirePro(license, status);
3253
+ async function analyzeCleanup(isPro, formulae, leaves) {
3254
+ if (!isPro) throw new Error("Pro license required");
2477
3255
  const leavesSet = new Set(leaves);
2478
3256
  const reverseDeps = /* @__PURE__ */ new Map();
2479
3257
  for (const f of formulae) {
@@ -2530,7 +3308,7 @@ async function analyzeCleanup(formulae, leaves) {
2530
3308
  }
2531
3309
 
2532
3310
  // src/stores/cleanup-store.ts
2533
- var useCleanupStore = create5((set, get) => ({
3311
+ var useCleanupStore = create6((set, get) => ({
2534
3312
  summary: null,
2535
3313
  selected: /* @__PURE__ */ new Set(),
2536
3314
  loading: false,
@@ -2544,7 +3322,8 @@ var useCleanupStore = create5((set, get) => ({
2544
3322
  await brewState.fetchLeaves();
2545
3323
  }
2546
3324
  const { formulae, leaves } = useBrewStore.getState();
2547
- const summary = await analyzeCleanup(formulae, leaves);
3325
+ const isPro = useLicenseStore.getState().isPro();
3326
+ const summary = await analyzeCleanup(isPro, formulae, leaves);
2548
3327
  set({ summary, selected: /* @__PURE__ */ new Set(), loading: false });
2549
3328
  } catch (err) {
2550
3329
  set({ error: err instanceof Error ? err.message : String(err), loading: false });
@@ -2564,7 +3343,7 @@ var useCleanupStore = create5((set, get) => ({
2564
3343
  }));
2565
3344
 
2566
3345
  // src/views/smart-cleanup.tsx
2567
- import { jsx as jsx21, jsxs as jsxs21 } from "react/jsx-runtime";
3346
+ import { jsx as jsx27, jsxs as jsxs26 } from "react/jsx-runtime";
2568
3347
  function SmartCleanupView() {
2569
3348
  const { summary, selected, loading, error, analyze, toggleSelect, selectAll } = useCleanupStore();
2570
3349
  const [cursor, setCursor] = useState9(0);
@@ -2572,7 +3351,7 @@ function SmartCleanupView() {
2572
3351
  const [confirmForce, setConfirmForce] = useState9(false);
2573
3352
  const [failedNames, setFailedNames] = useState9([]);
2574
3353
  const stream = useBrewStream();
2575
- const hasRefreshed = useRef6(false);
3354
+ const hasRefreshed = useRef7(false);
2576
3355
  useEffect12(() => {
2577
3356
  analyze();
2578
3357
  }, []);
@@ -2615,34 +3394,26 @@ function SmartCleanupView() {
2615
3394
  if (input === "j" || key.downArrow) setCursor((c) => Math.min(c + 1, Math.max(0, candidates.length - 1)));
2616
3395
  else if (input === "k" || key.upArrow) setCursor((c) => Math.max(c - 1, 0));
2617
3396
  });
2618
- if (loading) return /* @__PURE__ */ jsx21(Loading, { message: t("loading_cleanup") });
2619
- if (error) return /* @__PURE__ */ jsx21(ErrorMessage, { message: error });
3397
+ if (loading) return /* @__PURE__ */ jsx27(Loading, { message: t("loading_cleanup") });
3398
+ if (error) return /* @__PURE__ */ jsx27(ErrorMessage, { message: error });
2620
3399
  if (stream.isRunning || stream.lines.length > 0) {
2621
- return /* @__PURE__ */ jsxs21(Box19, { flexDirection: "column", children: [
2622
- /* @__PURE__ */ jsx21(ProgressLog, { lines: stream.lines, isRunning: stream.isRunning, title: t("cleanup_cleaning") }),
2623
- stream.isRunning && /* @__PURE__ */ jsxs21(Text21, { color: "#6B7280", children: [
3400
+ return /* @__PURE__ */ jsxs26(Box25, { flexDirection: "column", children: [
3401
+ /* @__PURE__ */ jsx27(ProgressLog, { lines: stream.lines, isRunning: stream.isRunning, title: t("cleanup_cleaning") }),
3402
+ stream.isRunning && /* @__PURE__ */ jsxs26(Text26, { color: COLORS.muted, children: [
2624
3403
  "esc:",
2625
3404
  t("hint_cancel")
2626
3405
  ] }),
2627
- !stream.isRunning && !stream.error && /* @__PURE__ */ jsx21(Box19, { borderStyle: "round", borderColor: "#22C55E", paddingX: 2, paddingY: 0, children: /* @__PURE__ */ jsxs21(Text21, { color: "#22C55E", bold: true, children: [
2628
- "\u2714",
2629
- " ",
2630
- t("cleanup_complete")
2631
- ] }) }),
2632
- !stream.isRunning && stream.error && /* @__PURE__ */ jsxs21(Box19, { flexDirection: "column", gap: 1, children: [
2633
- /* @__PURE__ */ jsx21(Box19, { borderStyle: "round", borderColor: "#EF4444", paddingX: 2, paddingY: 0, children: /* @__PURE__ */ jsxs21(Text21, { color: "#EF4444", bold: true, children: [
2634
- "\u2718",
2635
- " ",
2636
- t("cleanup_depError")
2637
- ] }) }),
2638
- isDependencyError && failedNames.length > 0 && /* @__PURE__ */ jsxs21(Text21, { color: "#F59E0B", children: [
3406
+ !stream.isRunning && !stream.error && /* @__PURE__ */ jsx27(ResultBanner, { status: "success", message: `\u2714 ${t("cleanup_complete")}` }),
3407
+ !stream.isRunning && stream.error && /* @__PURE__ */ jsxs26(Box25, { flexDirection: "column", gap: 1, children: [
3408
+ /* @__PURE__ */ jsx27(ResultBanner, { status: "error", message: `\u2718 ${t("cleanup_depError")}` }),
3409
+ isDependencyError && failedNames.length > 0 && /* @__PURE__ */ jsxs26(Text26, { color: COLORS.warning, children: [
2639
3410
  "F:",
2640
3411
  t("hint_force"),
2641
3412
  " r:",
2642
3413
  t("hint_refresh")
2643
3414
  ] })
2644
3415
  ] }),
2645
- confirmForce && /* @__PURE__ */ jsx21(Box19, { marginY: 1, children: /* @__PURE__ */ jsx21(
3416
+ confirmForce && /* @__PURE__ */ jsx27(Box25, { marginY: 1, children: /* @__PURE__ */ jsx27(
2646
3417
  ConfirmDialog,
2647
3418
  {
2648
3419
  message: t("cleanup_confirmForce", { count: failedNames.length }),
@@ -2657,49 +3428,47 @@ function SmartCleanupView() {
2657
3428
  ) })
2658
3429
  ] });
2659
3430
  }
2660
- return /* @__PURE__ */ jsxs21(Box19, { flexDirection: "column", children: [
2661
- /* @__PURE__ */ jsx21(SectionHeader, { emoji: "\u{1F9F9}", title: t("cleanup_title"), gradient: GRADIENTS.emerald }),
2662
- summary && /* @__PURE__ */ jsxs21(Box19, { gap: 1, marginY: 1, children: [
2663
- /* @__PURE__ */ jsx21(StatCard, { label: t("cleanup_orphans"), value: candidates.length, color: candidates.length > 0 ? "#F59E0B" : "#22C55E" }),
2664
- /* @__PURE__ */ jsx21(StatCard, { label: t("cleanup_reclaimable"), value: summary.totalReclaimableFormatted, color: "#38BDF8" }),
2665
- /* @__PURE__ */ jsx21(StatCard, { label: t("cleanup_selected"), value: selected.size, color: selected.size > 0 ? "#22C55E" : "#6B7280" })
3431
+ return /* @__PURE__ */ jsxs26(Box25, { flexDirection: "column", children: [
3432
+ /* @__PURE__ */ jsx27(SectionHeader, { emoji: "\u{1F9F9}", title: t("cleanup_title"), gradient: GRADIENTS.emerald }),
3433
+ summary && /* @__PURE__ */ jsxs26(Box25, { gap: 1, marginY: 1, children: [
3434
+ /* @__PURE__ */ jsx27(StatCard, { label: t("cleanup_orphans"), value: candidates.length, color: candidates.length > 0 ? COLORS.warning : COLORS.success }),
3435
+ /* @__PURE__ */ jsx27(StatCard, { label: t("cleanup_reclaimable"), value: summary.totalReclaimableFormatted, color: COLORS.sky }),
3436
+ /* @__PURE__ */ jsx27(StatCard, { label: t("cleanup_selected"), value: selected.size, color: selected.size > 0 ? COLORS.success : COLORS.muted })
2666
3437
  ] }),
2667
- confirmClean && /* @__PURE__ */ jsx21(Box19, { marginY: 1, children: /* @__PURE__ */ jsx21(
2668
- ConfirmDialog,
2669
- {
2670
- message: t("cleanup_confirmUninstall", { count: selected.size }),
2671
- onConfirm: () => {
2672
- setConfirmClean(false);
2673
- hasRefreshed.current = false;
2674
- const names = Array.from(selected);
2675
- setFailedNames(names);
2676
- void stream.run(["uninstall", ...names]);
2677
- },
2678
- onCancel: () => setConfirmClean(false)
2679
- }
2680
- ) }),
2681
- candidates.length === 0 && !confirmClean && /* @__PURE__ */ jsx21(Box19, { borderStyle: "round", borderColor: "#22C55E", paddingX: 2, paddingY: 0, children: /* @__PURE__ */ jsxs21(Text21, { color: "#22C55E", bold: true, children: [
2682
- "\u2714",
2683
- " ",
2684
- t("cleanup_systemClean")
2685
- ] }) }),
2686
- candidates.length > 0 && !confirmClean && /* @__PURE__ */ jsxs21(Box19, { flexDirection: "column", children: [
3438
+ confirmClean && /* @__PURE__ */ jsxs26(Box25, { flexDirection: "column", marginY: 1, gap: 1, children: [
3439
+ /* @__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") }) }),
3440
+ /* @__PURE__ */ jsx27(
3441
+ ConfirmDialog,
3442
+ {
3443
+ message: t("cleanup_confirmUninstall", { count: selected.size }),
3444
+ onConfirm: () => {
3445
+ setConfirmClean(false);
3446
+ hasRefreshed.current = false;
3447
+ const names = Array.from(selected);
3448
+ setFailedNames(names);
3449
+ void stream.run(["uninstall", ...names]);
3450
+ },
3451
+ onCancel: () => setConfirmClean(false)
3452
+ }
3453
+ )
3454
+ ] }),
3455
+ candidates.length === 0 && !confirmClean && /* @__PURE__ */ jsx27(ResultBanner, { status: "success", message: `\u2714 ${t("cleanup_systemClean")}` }),
3456
+ candidates.length > 0 && !confirmClean && /* @__PURE__ */ jsxs26(Box25, { flexDirection: "column", children: [
2687
3457
  candidates.map((c, i) => {
2688
3458
  const isCurrent = i === cursor;
2689
3459
  const isSelected = selected.has(c.name);
2690
- return /* @__PURE__ */ jsxs21(Box19, { gap: 1, children: [
2691
- /* @__PURE__ */ jsx21(Text21, { color: isCurrent ? "#22C55E" : "#9CA3AF", children: isCurrent ? "\u25B6" : " " }),
2692
- /* @__PURE__ */ jsx21(Text21, { color: isSelected ? "#22C55E" : "#9CA3AF", children: isSelected ? "\u2611" : "\u2610" }),
2693
- /* @__PURE__ */ jsx21(Text21, { bold: isCurrent, inverse: isCurrent, color: isCurrent ? "#F9FAFB" : "#9CA3AF", children: c.name }),
2694
- /* @__PURE__ */ jsx21(Text21, { color: "#F59E0B", children: c.diskUsageFormatted }),
2695
- /* @__PURE__ */ jsxs21(Text21, { color: "#6B7280", children: [
3460
+ return /* @__PURE__ */ jsxs26(SelectableRow, { isCurrent, children: [
3461
+ /* @__PURE__ */ jsx27(Text26, { color: isSelected ? COLORS.success : COLORS.muted, children: isSelected ? "\u2611" : "\u2610" }),
3462
+ /* @__PURE__ */ jsx27(Text26, { bold: isCurrent, inverse: isCurrent, color: isCurrent ? COLORS.text : COLORS.muted, children: c.name }),
3463
+ /* @__PURE__ */ jsx27(Text26, { color: COLORS.warning, children: c.diskUsageFormatted }),
3464
+ /* @__PURE__ */ jsxs26(Text26, { color: COLORS.textSecondary, children: [
2696
3465
  "[",
2697
3466
  c.reason,
2698
3467
  "]"
2699
3468
  ] })
2700
3469
  ] }, c.name);
2701
3470
  }),
2702
- /* @__PURE__ */ jsx21(Box19, { marginTop: 1, children: /* @__PURE__ */ jsxs21(Text21, { color: "#F9FAFB", bold: true, children: [
3471
+ /* @__PURE__ */ jsx27(Box25, { marginTop: 1, children: /* @__PURE__ */ jsxs26(Text26, { color: COLORS.text, bold: true, children: [
2703
3472
  cursor + 1,
2704
3473
  "/",
2705
3474
  candidates.length
@@ -2710,41 +3479,44 @@ function SmartCleanupView() {
2710
3479
 
2711
3480
  // src/views/history.tsx
2712
3481
  import { useEffect as useEffect13, useState as useState10, useMemo as useMemo4 } from "react";
2713
- import { Box as Box20, Text as Text22, useInput as useInput11, useStdout as useStdout4 } from "ink";
3482
+ import { Box as Box26, Text as Text27, useInput as useInput11, useStdout as useStdout5 } from "ink";
2714
3483
 
2715
3484
  // src/stores/history-store.ts
2716
- import { create as create6 } from "zustand";
2717
- var useHistoryStore = create6((set) => ({
3485
+ import { create as create7 } from "zustand";
3486
+ var useHistoryStore = create7((set) => ({
2718
3487
  entries: [],
2719
3488
  loading: false,
2720
3489
  error: null,
2721
3490
  fetchHistory: async () => {
2722
3491
  set({ loading: true, error: null });
2723
3492
  try {
2724
- const entries = await loadHistory();
3493
+ const isPro = useLicenseStore.getState().isPro();
3494
+ const entries = await loadHistory(isPro);
2725
3495
  set({ entries, loading: false });
2726
3496
  } catch (err) {
2727
3497
  set({ loading: false, error: err instanceof Error ? err.message : String(err) });
2728
3498
  }
2729
3499
  },
2730
3500
  logAction: async (action, packageName, success, error = null) => {
2731
- await appendEntry(action, packageName, success, error);
2732
- const entries = await loadHistory();
3501
+ const isPro = useLicenseStore.getState().isPro();
3502
+ await appendEntry(isPro, action, packageName, success, error);
3503
+ const entries = await loadHistory(isPro);
2733
3504
  set({ entries });
2734
3505
  },
2735
3506
  clearHistory: async () => {
2736
- await clearHistory();
3507
+ const isPro = useLicenseStore.getState().isPro();
3508
+ await clearHistory(isPro);
2737
3509
  set({ entries: [] });
2738
3510
  }
2739
3511
  }));
2740
3512
 
2741
3513
  // src/views/history.tsx
2742
- import { jsx as jsx22, jsxs as jsxs22 } from "react/jsx-runtime";
3514
+ import { jsx as jsx28, jsxs as jsxs27 } from "react/jsx-runtime";
2743
3515
  var ACTION_ICONS = {
2744
- install: { icon: "+", color: "#22C55E" },
2745
- uninstall: { icon: "-", color: "#EF4444" },
2746
- upgrade: { icon: "\u2191", color: "#06B6D4" },
2747
- "upgrade-all": { icon: "\u21C8", color: "#06B6D4" }
3516
+ install: { icon: "+", color: COLORS.success },
3517
+ uninstall: { icon: "-", color: COLORS.error },
3518
+ upgrade: { icon: "\u2191", color: COLORS.info },
3519
+ "upgrade-all": { icon: "\u21C8", color: COLORS.info }
2748
3520
  };
2749
3521
  var ACTION_LABEL_KEYS = {
2750
3522
  install: "history_actionInstall",
@@ -2817,19 +3589,19 @@ function HistoryView() {
2817
3589
  if (input === "j" || key.downArrow) setCursor((c) => Math.min(c + 1, Math.max(0, filtered.length - 1)));
2818
3590
  else if (input === "k" || key.upArrow) setCursor((c) => Math.max(c - 1, 0));
2819
3591
  });
2820
- if (loading) return /* @__PURE__ */ jsx22(Loading, { message: t("loading_history") });
2821
- if (error) return /* @__PURE__ */ jsx22(ErrorMessage, { message: error });
2822
- const { stdout } = useStdout4();
3592
+ if (loading) return /* @__PURE__ */ jsx28(Loading, { message: t("loading_history") });
3593
+ if (error) return /* @__PURE__ */ jsx28(ErrorMessage, { message: error });
3594
+ const { stdout } = useStdout5();
2823
3595
  const MAX_VISIBLE_ROWS = Math.max(5, (stdout?.rows ?? 24) - 8);
2824
3596
  const start = Math.max(0, cursor - Math.floor(MAX_VISIBLE_ROWS / 2));
2825
3597
  const visible = filtered.slice(start, start + MAX_VISIBLE_ROWS);
2826
- return /* @__PURE__ */ jsxs22(Box20, { flexDirection: "column", children: [
2827
- /* @__PURE__ */ jsxs22(Box20, { gap: 2, marginBottom: 1, children: [
2828
- /* @__PURE__ */ jsx22(SectionHeader, { emoji: "\u{1F4DC}", title: t("history_title", { count: filtered.length }), gradient: GRADIENTS.gold }),
2829
- /* @__PURE__ */ jsx22(Text22, { color: filter === "all" ? "#F9FAFB" : "#FFD700", children: t("history_filterLabel", { filter }) })
3598
+ return /* @__PURE__ */ jsxs27(Box26, { flexDirection: "column", children: [
3599
+ /* @__PURE__ */ jsxs27(Box26, { gap: 2, marginBottom: 1, children: [
3600
+ /* @__PURE__ */ jsx28(SectionHeader, { emoji: "\u{1F4DC}", title: t("history_title", { count: filtered.length }), gradient: GRADIENTS.gold }),
3601
+ /* @__PURE__ */ jsx28(Text27, { color: filter === "all" ? COLORS.text : COLORS.gold, children: t("history_filterLabel", { filter }) })
2830
3602
  ] }),
2831
- isSearching && /* @__PURE__ */ jsx22(Box20, { marginBottom: 1, children: /* @__PURE__ */ jsx22(SearchInput, { defaultValue: searchQuery, onChange: setSearchQuery, placeholder: t("history_searchPlaceholder"), isActive: true }) }),
2832
- confirmClear && /* @__PURE__ */ jsx22(
3603
+ isSearching && /* @__PURE__ */ jsx28(Box26, { marginBottom: 1, children: /* @__PURE__ */ jsx28(SearchInput, { defaultValue: searchQuery, onChange: setSearchQuery, placeholder: t("history_searchPlaceholder"), isActive: true }) }),
3604
+ confirmClear && /* @__PURE__ */ jsx28(
2833
3605
  ConfirmDialog,
2834
3606
  {
2835
3607
  message: t("history_confirmClear", { count: entries.length }),
@@ -2840,10 +3612,10 @@ function HistoryView() {
2840
3612
  onCancel: () => setConfirmClear(false)
2841
3613
  }
2842
3614
  ),
2843
- confirmReplay && /* @__PURE__ */ jsx22(
3615
+ confirmReplay && /* @__PURE__ */ jsx28(
2844
3616
  ConfirmDialog,
2845
3617
  {
2846
- message: confirmReplay.action === "upgrade-all" ? t("history_replayAll") : t("history_confirmReplay", { action: t(ACTION_LABEL_KEYS[confirmReplay.action]), name: confirmReplay.packageName ?? "" }),
3618
+ message: confirmReplay.action === "upgrade-all" ? t("history_replayAll") + "\n" + t("upgrade_all_warning") : t("history_confirmReplay", { action: t(ACTION_LABEL_KEYS[confirmReplay.action]), name: confirmReplay.packageName ?? "" }),
2847
3619
  onConfirm: async () => {
2848
3620
  const entry = confirmReplay;
2849
3621
  setConfirmReplay(null);
@@ -2868,10 +3640,10 @@ function HistoryView() {
2868
3640
  onCancel: () => setConfirmReplay(null)
2869
3641
  }
2870
3642
  ),
2871
- (stream.isRunning || stream.lines.length > 0) && /* @__PURE__ */ jsx22(Box20, { marginY: 1, children: /* @__PURE__ */ jsx22(ProgressLog, { lines: stream.lines, isRunning: stream.isRunning, title: t("hint_replay") }) }),
2872
- filtered.length === 0 && !confirmClear && /* @__PURE__ */ jsx22(Text22, { color: "#6B7280", italic: true, children: filter !== "all" ? t("history_noEntriesFor", { filter }) : t("history_noEntries") }),
2873
- filtered.length > 0 && !confirmClear && /* @__PURE__ */ jsxs22(Box20, { flexDirection: "column", children: [
2874
- start > 0 && /* @__PURE__ */ jsxs22(Text22, { color: "#6B7280", dimColor: true, children: [
3643
+ (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") }) }),
3644
+ filtered.length === 0 && !confirmClear && /* @__PURE__ */ jsx28(Text27, { color: COLORS.textSecondary, italic: true, children: filter !== "all" ? t("history_noEntriesFor", { filter }) : t("history_noEntries") }),
3645
+ filtered.length > 0 && !confirmClear && /* @__PURE__ */ jsxs27(Box26, { flexDirection: "column", children: [
3646
+ start > 0 && /* @__PURE__ */ jsxs27(Text27, { color: COLORS.textSecondary, dimColor: true, children: [
2875
3647
  " ",
2876
3648
  t("scroll_moreAbove", { count: start })
2877
3649
  ] }),
@@ -2880,20 +3652,19 @@ function HistoryView() {
2880
3652
  const isCurrent = idx === cursor;
2881
3653
  const { icon, color } = ACTION_ICONS[entry.action];
2882
3654
  const ts = new Date(entry.timestamp).getTime() / 1e3;
2883
- return /* @__PURE__ */ jsxs22(Box20, { gap: 1, children: [
2884
- /* @__PURE__ */ jsx22(Text22, { color: isCurrent ? "#22C55E" : "#9CA3AF", children: isCurrent ? "\u25B6" : " " }),
2885
- /* @__PURE__ */ jsx22(Text22, { color, bold: true, children: icon }),
2886
- /* @__PURE__ */ jsx22(Text22, { bold: isCurrent, inverse: isCurrent, color: isCurrent ? "#F9FAFB" : "#9CA3AF", children: t(ACTION_LABEL_KEYS[entry.action]).padEnd(12) }),
2887
- /* @__PURE__ */ jsx22(Text22, { color: "#F9FAFB", children: entry.packageName ?? t("history_all") }),
2888
- entry.success ? /* @__PURE__ */ jsx22(StatusBadge, { label: t("badge_ok"), variant: "success" }) : /* @__PURE__ */ jsx22(StatusBadge, { label: t("badge_fail"), variant: "error" }),
2889
- /* @__PURE__ */ jsx22(Text22, { color: "#9CA3AF", children: formatRelativeTime(ts) })
3655
+ return /* @__PURE__ */ jsxs27(SelectableRow, { isCurrent, children: [
3656
+ /* @__PURE__ */ jsx28(Text27, { color, bold: true, children: icon }),
3657
+ /* @__PURE__ */ jsx28(Text27, { bold: isCurrent, inverse: isCurrent, color: isCurrent ? COLORS.text : COLORS.muted, children: t(ACTION_LABEL_KEYS[entry.action]).padEnd(12) }),
3658
+ /* @__PURE__ */ jsx28(Text27, { color: COLORS.text, children: entry.packageName ?? t("history_all") }),
3659
+ entry.success ? /* @__PURE__ */ jsx28(StatusBadge, { label: t("badge_ok"), variant: "success" }) : /* @__PURE__ */ jsx28(StatusBadge, { label: t("badge_fail"), variant: "error" }),
3660
+ /* @__PURE__ */ jsx28(Text27, { color: COLORS.muted, children: formatRelativeTime(ts) })
2890
3661
  ] }, entry.id);
2891
3662
  }),
2892
- start + MAX_VISIBLE_ROWS < filtered.length && /* @__PURE__ */ jsxs22(Text22, { color: "#6B7280", dimColor: true, children: [
3663
+ start + MAX_VISIBLE_ROWS < filtered.length && /* @__PURE__ */ jsxs27(Text27, { color: COLORS.textSecondary, dimColor: true, children: [
2893
3664
  " ",
2894
3665
  t("scroll_moreBelow", { count: filtered.length - start - MAX_VISIBLE_ROWS })
2895
3666
  ] }),
2896
- /* @__PURE__ */ jsx22(Box20, { marginTop: 1, children: /* @__PURE__ */ jsxs22(Text22, { color: "#F9FAFB", bold: true, children: [
3667
+ /* @__PURE__ */ jsx28(Box26, { marginTop: 1, children: /* @__PURE__ */ jsxs27(Text27, { color: COLORS.text, bold: true, children: [
2897
3668
  cursor + 1,
2898
3669
  "/",
2899
3670
  filtered.length
@@ -2904,10 +3675,10 @@ function HistoryView() {
2904
3675
 
2905
3676
  // src/views/security-audit.tsx
2906
3677
  import { useEffect as useEffect14, useState as useState11 } from "react";
2907
- import { Box as Box21, Text as Text23, useInput as useInput12 } from "ink";
3678
+ import { Box as Box27, Text as Text28, useInput as useInput12 } from "ink";
2908
3679
 
2909
3680
  // src/stores/security-store.ts
2910
- import { create as create7 } from "zustand";
3681
+ import { create as create8 } from "zustand";
2911
3682
 
2912
3683
  // src/lib/security/osv-api.ts
2913
3684
  var OSV_BATCH_URL = "https://api.osv.dev/v1/querybatch";
@@ -2950,6 +3721,12 @@ async function queryBatch(packages, queries) {
2950
3721
  throw new Error(`OSV API error: ${res.status} ${res.statusText}`);
2951
3722
  }
2952
3723
  const data = await res.json();
3724
+ if (!data || !Array.isArray(data.results)) {
3725
+ throw new Error("Invalid OSV API response: missing results array");
3726
+ }
3727
+ if (data.results.length !== packages.length) {
3728
+ throw new Error(`OSV API response mismatch: expected ${packages.length} results, got ${data.results.length}`);
3729
+ }
2953
3730
  const result = /* @__PURE__ */ new Map();
2954
3731
  for (let i = 0; i < packages.length; i++) {
2955
3732
  const vulns = data.results[i]?.vulns;
@@ -2969,6 +3746,7 @@ async function queryBatch(packages, queries) {
2969
3746
  }
2970
3747
  async function queryOneByOne(packages) {
2971
3748
  const result = /* @__PURE__ */ new Map();
3749
+ const errors = [];
2972
3750
  for (const pkg of packages) {
2973
3751
  try {
2974
3752
  const partial = await queryBatch(
@@ -2976,15 +3754,43 @@ async function queryOneByOne(packages) {
2976
3754
  [{ package: { name: pkg.name, ecosystem: "Homebrew" }, version: pkg.version }]
2977
3755
  );
2978
3756
  for (const [k, v] of partial) result.set(k, v);
2979
- } catch {
3757
+ } catch (err) {
3758
+ const msg = err instanceof Error ? err.message : String(err);
3759
+ if (msg.includes("400")) {
3760
+ errors.push(`Skipped ${pkg.name}: ${msg}`);
3761
+ continue;
3762
+ }
3763
+ if (msg.includes("429")) {
3764
+ logger.warn(`Rate limited by OSV API, backing off`, { package: pkg.name });
3765
+ await new Promise((r) => setTimeout(r, 2e3));
3766
+ try {
3767
+ const retryResult = await queryBatch(
3768
+ [pkg],
3769
+ [{ package: { name: pkg.name, ecosystem: "Homebrew" }, version: pkg.version }]
3770
+ );
3771
+ for (const [k, v] of retryResult) result.set(k, v);
3772
+ } catch {
3773
+ errors.push(`Failed after retry ${pkg.name}: ${msg}`);
3774
+ }
3775
+ continue;
3776
+ }
3777
+ logger.error(`OSV query failed for ${pkg.name}: ${msg}`);
3778
+ errors.push(`${pkg.name}: ${msg}`);
2980
3779
  }
3780
+ await new Promise((r) => setTimeout(r, 75));
3781
+ }
3782
+ if (errors.length > 0) {
3783
+ logger.warn(`OSV query errors for ${errors.length} packages`, { errors: errors.slice(0, 5) });
2981
3784
  }
2982
3785
  return result;
2983
3786
  }
2984
3787
  async function queryVulnerabilities(packages) {
3788
+ const validPackages = packages.filter(
3789
+ (p) => p.version && typeof p.version === "string" && p.version.trim().length > 0
3790
+ );
2985
3791
  const result = /* @__PURE__ */ new Map();
2986
- for (let i = 0; i < packages.length; i += BATCH_SIZE) {
2987
- const batch = packages.slice(i, i + BATCH_SIZE);
3792
+ for (let i = 0; i < validPackages.length; i += BATCH_SIZE) {
3793
+ const batch = validPackages.slice(i, i + BATCH_SIZE);
2988
3794
  const queries = batch.map((p) => ({
2989
3795
  package: { name: p.name, ecosystem: "Homebrew" },
2990
3796
  version: p.version
@@ -3003,9 +3809,8 @@ var SEVERITY_ORDER = {
3003
3809
  LOW: 1,
3004
3810
  UNKNOWN: 0
3005
3811
  };
3006
- async function runSecurityAudit(formulae, casks) {
3007
- const { license, status } = useLicenseStore.getState();
3008
- requirePro(license, status);
3812
+ async function runSecurityAudit(isPro, formulae, casks) {
3813
+ if (!isPro) throw new Error("Pro license required");
3009
3814
  const packages = [];
3010
3815
  for (const f of formulae) {
3011
3816
  const version = f.installed[0]?.version ?? f.versions.stable;
@@ -3056,11 +3861,17 @@ async function runSecurityAudit(formulae, casks) {
3056
3861
  }
3057
3862
 
3058
3863
  // src/stores/security-store.ts
3059
- var useSecurityStore = create7((set) => ({
3864
+ var CACHE_TTL_MS = 30 * 60 * 1e3;
3865
+ var useSecurityStore = create8((set, get) => ({
3060
3866
  summary: null,
3061
3867
  loading: false,
3062
3868
  error: null,
3063
- scan: async () => {
3869
+ cachedAt: null,
3870
+ scan: async (forceRefresh = false) => {
3871
+ const { summary, cachedAt } = get();
3872
+ if (!forceRefresh && summary && cachedAt && Date.now() - cachedAt < CACHE_TTL_MS) {
3873
+ return;
3874
+ }
3064
3875
  set({ loading: true, error: null });
3065
3876
  try {
3066
3877
  const brewState = useBrewStore.getState();
@@ -3068,8 +3879,9 @@ var useSecurityStore = create7((set) => ({
3068
3879
  await brewState.fetchInstalled();
3069
3880
  }
3070
3881
  const { formulae, casks } = useBrewStore.getState();
3071
- const summary = await runSecurityAudit(formulae, casks);
3072
- set({ summary, loading: false });
3882
+ const isPro = useLicenseStore.getState().isPro();
3883
+ const result = await runSecurityAudit(isPro, formulae, casks);
3884
+ set({ summary: result, loading: false, cachedAt: Date.now() });
3073
3885
  } catch (err) {
3074
3886
  set({ error: err instanceof Error ? err.message : String(err), loading: false });
3075
3887
  }
@@ -3077,13 +3889,13 @@ var useSecurityStore = create7((set) => ({
3077
3889
  }));
3078
3890
 
3079
3891
  // src/views/security-audit.tsx
3080
- import { jsx as jsx23, jsxs as jsxs23 } from "react/jsx-runtime";
3892
+ import { jsx as jsx29, jsxs as jsxs28 } from "react/jsx-runtime";
3081
3893
  var SEVERITY_COLORS = {
3082
- CRITICAL: "#EF4444",
3083
- HIGH: "#EF4444",
3084
- MEDIUM: "#F59E0B",
3085
- LOW: "#6B7280",
3086
- UNKNOWN: "#6B7280"
3894
+ CRITICAL: COLORS.error,
3895
+ HIGH: COLORS.error,
3896
+ MEDIUM: COLORS.warning,
3897
+ LOW: COLORS.textSecondary,
3898
+ UNKNOWN: COLORS.textSecondary
3087
3899
  };
3088
3900
  var SEVERITY_BADGE = {
3089
3901
  CRITICAL: "error",
@@ -3092,8 +3904,11 @@ var SEVERITY_BADGE = {
3092
3904
  LOW: "muted",
3093
3905
  UNKNOWN: "muted"
3094
3906
  };
3907
+ function isNetworkError2(msg) {
3908
+ return /fetch failed|ECONNREFUSED|ENOTFOUND|ETIMEDOUT|network|abort/i.test(msg);
3909
+ }
3095
3910
  function SecurityAuditView() {
3096
- const { summary, loading, error, scan } = useSecurityStore();
3911
+ const { summary, loading, error, scan, cachedAt } = useSecurityStore();
3097
3912
  const [cursor, setCursor] = useState11(0);
3098
3913
  const [expandedPkg, setExpandedPkg] = useState11(null);
3099
3914
  const [confirmUpgrade, setConfirmUpgrade] = useState11(null);
@@ -3105,7 +3920,7 @@ function SecurityAuditView() {
3105
3920
  useInput12((input, key) => {
3106
3921
  if (confirmUpgrade || stream.isRunning) return;
3107
3922
  if (input === "r") {
3108
- void scan();
3923
+ void scan(true);
3109
3924
  return;
3110
3925
  }
3111
3926
  if (input === "u" && results[cursor]) {
@@ -3118,30 +3933,31 @@ function SecurityAuditView() {
3118
3933
  setExpandedPkg(expandedPkg === results[cursor].packageName ? null : results[cursor].packageName);
3119
3934
  }
3120
3935
  });
3121
- if (loading) return /* @__PURE__ */ jsx23(Loading, { message: t("loading_security") });
3122
- if (error) return /* @__PURE__ */ jsx23(ErrorMessage, { message: error });
3123
- return /* @__PURE__ */ jsxs23(Box21, { flexDirection: "column", children: [
3124
- /* @__PURE__ */ jsx23(SectionHeader, { emoji: "\u{1F6E1}\uFE0F", title: t("security_title"), gradient: GRADIENTS.ocean }),
3125
- summary && /* @__PURE__ */ jsxs23(Box21, { gap: 1, marginY: 1, children: [
3126
- /* @__PURE__ */ jsx23(StatCard, { label: t("security_scanned"), value: summary.totalPackages, color: "#06B6D4" }),
3127
- /* @__PURE__ */ jsx23(
3936
+ if (loading) return /* @__PURE__ */ jsx29(Loading, { message: t("loading_security") });
3937
+ if (error) {
3938
+ const displayError = isNetworkError2(error) ? t("security_networkError") : error;
3939
+ return /* @__PURE__ */ jsx29(ErrorMessage, { message: displayError });
3940
+ }
3941
+ const cacheAge = cachedAt ? formatRelativeTime(cachedAt / 1e3) : null;
3942
+ return /* @__PURE__ */ jsxs28(Box27, { flexDirection: "column", children: [
3943
+ /* @__PURE__ */ jsx29(SectionHeader, { emoji: "\u{1F6E1}\uFE0F", title: t("security_title"), gradient: GRADIENTS.ocean }),
3944
+ summary && /* @__PURE__ */ jsxs28(Box27, { gap: 1, marginY: 1, children: [
3945
+ /* @__PURE__ */ jsx29(StatCard, { label: t("security_scanned"), value: summary.totalPackages, color: COLORS.info }),
3946
+ /* @__PURE__ */ jsx29(
3128
3947
  StatCard,
3129
3948
  {
3130
3949
  label: t("security_vulnerable"),
3131
3950
  value: summary.vulnerablePackages,
3132
- color: summary.vulnerablePackages > 0 ? "#EF4444" : "#22C55E"
3951
+ color: summary.vulnerablePackages > 0 ? COLORS.error : COLORS.success
3133
3952
  }
3134
3953
  ),
3135
- summary.criticalCount > 0 && /* @__PURE__ */ jsx23(StatCard, { label: t("security_critical"), value: summary.criticalCount, color: "#EF4444" }),
3136
- summary.highCount > 0 && /* @__PURE__ */ jsx23(StatCard, { label: t("security_high"), value: summary.highCount, color: "#EF4444" }),
3137
- summary.mediumCount > 0 && /* @__PURE__ */ jsx23(StatCard, { label: t("security_medium"), value: summary.mediumCount, color: "#F59E0B" })
3954
+ summary.criticalCount > 0 && /* @__PURE__ */ jsx29(StatCard, { label: t("security_critical"), value: summary.criticalCount, color: COLORS.error }),
3955
+ summary.highCount > 0 && /* @__PURE__ */ jsx29(StatCard, { label: t("security_high"), value: summary.highCount, color: COLORS.error }),
3956
+ summary.mediumCount > 0 && /* @__PURE__ */ jsx29(StatCard, { label: t("security_medium"), value: summary.mediumCount, color: COLORS.warning })
3138
3957
  ] }),
3139
- results.length === 0 && summary && /* @__PURE__ */ jsx23(Box21, { marginTop: 1, children: /* @__PURE__ */ jsx23(Box21, { borderStyle: "round", borderColor: "#22C55E", paddingX: 2, paddingY: 0, children: /* @__PURE__ */ jsxs23(Text23, { color: "#22C55E", bold: true, children: [
3140
- "\u2714",
3141
- " ",
3142
- t("security_noVulns")
3143
- ] }) }) }),
3144
- confirmUpgrade && /* @__PURE__ */ jsx23(Box21, { marginY: 1, children: /* @__PURE__ */ jsx23(
3958
+ cacheAge && /* @__PURE__ */ jsx29(Text28, { color: COLORS.muted, children: t("security_cachedResults", { time: cacheAge }) }),
3959
+ results.length === 0 && summary && /* @__PURE__ */ jsx29(Box27, { marginTop: 1, children: /* @__PURE__ */ jsx29(ResultBanner, { status: "success", message: `\u2714 ${t("security_noVulns")}` }) }),
3960
+ confirmUpgrade && /* @__PURE__ */ jsx29(Box27, { marginY: 1, children: /* @__PURE__ */ jsx29(
3145
3961
  ConfirmDialog,
3146
3962
  {
3147
3963
  message: t("security_confirmUpgrade", { name: confirmUpgrade }),
@@ -3149,40 +3965,39 @@ function SecurityAuditView() {
3149
3965
  const name = confirmUpgrade;
3150
3966
  setConfirmUpgrade(null);
3151
3967
  await stream.run(["upgrade", name]);
3152
- void scan();
3968
+ void scan(true);
3153
3969
  },
3154
3970
  onCancel: () => setConfirmUpgrade(null)
3155
3971
  }
3156
3972
  ) }),
3157
- (stream.isRunning || stream.lines.length > 0) && /* @__PURE__ */ jsx23(Box21, { marginY: 1, children: /* @__PURE__ */ jsx23(ProgressLog, { lines: stream.lines, isRunning: stream.isRunning, title: t("hint_upgrade") }) }),
3158
- results.length > 0 && /* @__PURE__ */ jsxs23(Box21, { flexDirection: "column", marginTop: 1, children: [
3973
+ (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") }) }),
3974
+ results.length > 0 && /* @__PURE__ */ jsxs28(Box27, { flexDirection: "column", marginTop: 1, children: [
3159
3975
  results.map((pkg, i) => {
3160
3976
  const isCurrent = i === cursor;
3161
3977
  const isExpanded = expandedPkg === pkg.packageName;
3162
- return /* @__PURE__ */ jsxs23(Box21, { flexDirection: "column", children: [
3163
- /* @__PURE__ */ jsxs23(Box21, { gap: 1, children: [
3164
- /* @__PURE__ */ jsx23(Text23, { color: isCurrent ? "#22C55E" : "#9CA3AF", children: isCurrent ? "\u25B6" : " " }),
3165
- /* @__PURE__ */ jsx23(StatusBadge, { label: pkg.maxSeverity, variant: SEVERITY_BADGE[pkg.maxSeverity] }),
3166
- /* @__PURE__ */ jsx23(Text23, { bold: isCurrent, inverse: isCurrent, color: isCurrent ? "#F9FAFB" : "#9CA3AF", children: pkg.packageName }),
3167
- /* @__PURE__ */ jsx23(Text23, { color: "#9CA3AF", children: pkg.installedVersion }),
3168
- /* @__PURE__ */ jsx23(Text23, { color: "#9CA3AF", children: tp("plural_vulns", pkg.vulnerabilities.length) }),
3169
- /* @__PURE__ */ jsx23(Text23, { color: "#9CA3AF", children: isExpanded ? "\u25BC" : "\u25B6" })
3978
+ return /* @__PURE__ */ jsxs28(Box27, { flexDirection: "column", children: [
3979
+ /* @__PURE__ */ jsxs28(SelectableRow, { isCurrent, children: [
3980
+ /* @__PURE__ */ jsx29(StatusBadge, { label: pkg.maxSeverity, variant: SEVERITY_BADGE[pkg.maxSeverity] }),
3981
+ /* @__PURE__ */ jsx29(Text28, { bold: isCurrent, inverse: isCurrent, color: isCurrent ? COLORS.text : COLORS.muted, children: pkg.packageName }),
3982
+ /* @__PURE__ */ jsx29(Text28, { color: COLORS.muted, children: pkg.installedVersion }),
3983
+ /* @__PURE__ */ jsx29(Text28, { color: COLORS.muted, children: tp("plural_vulns", pkg.vulnerabilities.length) }),
3984
+ /* @__PURE__ */ jsx29(Text28, { color: COLORS.muted, children: isExpanded ? "\u25BC" : "\u25B6" })
3170
3985
  ] }),
3171
- isExpanded && /* @__PURE__ */ jsx23(Box21, { flexDirection: "column", paddingLeft: 4, marginBottom: 1, children: pkg.vulnerabilities.map((vuln) => /* @__PURE__ */ jsxs23(Box21, { flexDirection: "column", marginBottom: 1, children: [
3172
- /* @__PURE__ */ jsxs23(Box21, { gap: 1, children: [
3173
- /* @__PURE__ */ jsx23(Text23, { color: SEVERITY_COLORS[vuln.severity], bold: true, children: vuln.id }),
3174
- /* @__PURE__ */ jsxs23(Text23, { color: "#9CA3AF", children: [
3986
+ isExpanded && /* @__PURE__ */ jsx29(Box27, { flexDirection: "column", paddingLeft: 4, marginBottom: 1, children: pkg.vulnerabilities.map((vuln) => /* @__PURE__ */ jsxs28(Box27, { flexDirection: "column", marginBottom: 1, children: [
3987
+ /* @__PURE__ */ jsxs28(Box27, { gap: 1, children: [
3988
+ /* @__PURE__ */ jsx29(Text28, { color: SEVERITY_COLORS[vuln.severity], bold: true, children: vuln.id }),
3989
+ /* @__PURE__ */ jsxs28(Text28, { color: COLORS.muted, children: [
3175
3990
  "[",
3176
3991
  vuln.severity,
3177
3992
  "]"
3178
3993
  ] })
3179
3994
  ] }),
3180
- /* @__PURE__ */ jsx23(Text23, { color: "#9CA3AF", wrap: "wrap", children: vuln.summary }),
3181
- vuln.fixedVersion && /* @__PURE__ */ jsx23(Text23, { color: "#22C55E", children: t("security_fixedIn", { version: vuln.fixedVersion }) })
3995
+ /* @__PURE__ */ jsx29(Text28, { color: COLORS.muted, wrap: "wrap", children: vuln.summary }),
3996
+ vuln.fixedVersion && /* @__PURE__ */ jsx29(Text28, { color: COLORS.success, children: t("security_fixedIn", { version: vuln.fixedVersion }) })
3182
3997
  ] }, vuln.id)) })
3183
3998
  ] }, pkg.packageName);
3184
3999
  }),
3185
- /* @__PURE__ */ jsx23(Box21, { marginTop: 1, children: /* @__PURE__ */ jsxs23(Text23, { color: "#F9FAFB", bold: true, children: [
4000
+ /* @__PURE__ */ jsx29(Box27, { marginTop: 1, children: /* @__PURE__ */ jsxs28(Text28, { color: COLORS.text, bold: true, children: [
3186
4001
  cursor + 1,
3187
4002
  "/",
3188
4003
  results.length
@@ -3193,12 +4008,13 @@ function SecurityAuditView() {
3193
4008
 
3194
4009
  // src/views/account.tsx
3195
4010
  import { useState as useState12 } from "react";
3196
- import { Box as Box22, Text as Text24, useInput as useInput13 } from "ink";
3197
- import { Fragment as Fragment5, jsx as jsx24, jsxs as jsxs24 } from "react/jsx-runtime";
4011
+ import { Box as Box28, Text as Text29, useInput as useInput13 } from "ink";
4012
+ import { Fragment as Fragment5, jsx as jsx30, jsxs as jsxs29 } from "react/jsx-runtime";
3198
4013
  function AccountView() {
3199
4014
  const { status, license, deactivate: deactivate2, degradation } = useLicenseStore();
3200
4015
  const [confirmDeactivate, setConfirmDeactivate] = useState12(false);
3201
4016
  const [deactivating, setDeactivating] = useState12(false);
4017
+ const [deactivateError, setDeactivateError] = useState12(null);
3202
4018
  useInput13((input) => {
3203
4019
  if (confirmDeactivate || deactivating) return;
3204
4020
  if (input === "d" && status === "pro") {
@@ -3209,17 +4025,23 @@ function AccountView() {
3209
4025
  if (key.length <= 8) return key;
3210
4026
  return key.slice(0, 4) + "-****-****-" + key.slice(-4);
3211
4027
  };
3212
- return /* @__PURE__ */ jsxs24(Box22, { flexDirection: "column", children: [
3213
- /* @__PURE__ */ jsx24(SectionHeader, { emoji: "\u{1F464}", title: t("account_title"), gradient: GRADIENTS.gold }),
3214
- confirmDeactivate && /* @__PURE__ */ jsx24(Box22, { marginY: 1, children: /* @__PURE__ */ jsx24(
4028
+ if (status === "validating") {
4029
+ return /* @__PURE__ */ jsx30(Loading, { message: t("account_loading") });
4030
+ }
4031
+ return /* @__PURE__ */ jsxs29(Box28, { flexDirection: "column", children: [
4032
+ /* @__PURE__ */ jsx30(SectionHeader, { emoji: "\u{1F464}", title: t("account_title"), gradient: GRADIENTS.gold }),
4033
+ confirmDeactivate && /* @__PURE__ */ jsx30(Box28, { marginY: 1, children: /* @__PURE__ */ jsx30(
3215
4034
  ConfirmDialog,
3216
4035
  {
3217
4036
  message: t("account_confirmDeactivate"),
3218
4037
  onConfirm: async () => {
3219
4038
  setConfirmDeactivate(false);
3220
4039
  setDeactivating(true);
4040
+ setDeactivateError(null);
3221
4041
  try {
3222
4042
  await deactivate2();
4043
+ } catch (err) {
4044
+ setDeactivateError(t("deactivate_failed") + ": " + String(err));
3223
4045
  } finally {
3224
4046
  setDeactivating(false);
3225
4047
  }
@@ -3227,122 +4049,128 @@ function AccountView() {
3227
4049
  onCancel: () => setConfirmDeactivate(false)
3228
4050
  }
3229
4051
  ) }),
3230
- /* @__PURE__ */ jsxs24(Box22, { flexDirection: "column", marginTop: 1, paddingLeft: 2, children: [
3231
- /* @__PURE__ */ jsxs24(Box22, { gap: 1, children: [
3232
- /* @__PURE__ */ jsx24(Text24, { color: "#9CA3AF", children: t("account_statusLabel") }),
3233
- status === "pro" && /* @__PURE__ */ jsx24(Text24, { color: "#22C55E", bold: true, children: t("account_pro") }),
3234
- status === "free" && /* @__PURE__ */ jsx24(Text24, { color: "#9CA3AF", children: t("account_free") }),
3235
- status === "expired" && /* @__PURE__ */ jsx24(Text24, { color: "#EF4444", children: t("account_expired") }),
3236
- status === "validating" && /* @__PURE__ */ jsx24(Text24, { color: "#38BDF8", children: t("account_validating") })
4052
+ /* @__PURE__ */ jsxs29(Box28, { flexDirection: "column", marginTop: 1, paddingLeft: 2, children: [
4053
+ /* @__PURE__ */ jsxs29(Box28, { gap: 1, children: [
4054
+ /* @__PURE__ */ jsx30(Text29, { color: COLORS.muted, children: t("account_statusLabel") }),
4055
+ status === "pro" && /* @__PURE__ */ jsx30(Text29, { color: COLORS.success, bold: true, children: t("account_pro") }),
4056
+ status === "free" && /* @__PURE__ */ jsx30(Text29, { color: COLORS.muted, children: t("account_free") }),
4057
+ status === "expired" && /* @__PURE__ */ jsx30(Text29, { color: COLORS.error, children: t("account_expired") })
3237
4058
  ] }),
3238
- (degradation === "warning" || degradation === "limited") && license && /* @__PURE__ */ jsx24(Box22, { marginTop: 1, borderStyle: "round", borderColor: "#F59E0B", paddingX: 2, paddingY: 0, children: /* @__PURE__ */ jsx24(Text24, { color: "#F59E0B", children: t("license_offlineWarning", {
4059
+ (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", {
3239
4060
  days: Math.floor((Date.now() - new Date(license.lastValidatedAt).getTime()) / (24 * 60 * 60 * 1e3))
3240
4061
  }) }) }),
3241
- license && /* @__PURE__ */ jsxs24(Fragment5, { children: [
3242
- /* @__PURE__ */ jsxs24(Box22, { gap: 1, children: [
3243
- /* @__PURE__ */ jsx24(Text24, { color: "#9CA3AF", children: t("account_emailLabel") }),
3244
- /* @__PURE__ */ jsx24(Text24, { children: license.customerEmail })
4062
+ license && /* @__PURE__ */ jsxs29(Fragment5, { children: [
4063
+ /* @__PURE__ */ jsxs29(Box28, { gap: 1, children: [
4064
+ /* @__PURE__ */ jsx30(Text29, { color: COLORS.muted, children: t("account_emailLabel") }),
4065
+ /* @__PURE__ */ jsx30(Text29, { children: license.customerEmail })
3245
4066
  ] }),
3246
- /* @__PURE__ */ jsxs24(Box22, { gap: 1, children: [
3247
- /* @__PURE__ */ jsx24(Text24, { color: "#9CA3AF", children: t("account_nameLabel") }),
3248
- /* @__PURE__ */ jsx24(Text24, { children: license.customerName })
4067
+ /* @__PURE__ */ jsxs29(Box28, { gap: 1, children: [
4068
+ /* @__PURE__ */ jsx30(Text29, { color: COLORS.muted, children: t("account_nameLabel") }),
4069
+ /* @__PURE__ */ jsx30(Text29, { children: license.customerName })
3249
4070
  ] }),
3250
- /* @__PURE__ */ jsxs24(Box22, { gap: 1, children: [
3251
- /* @__PURE__ */ jsx24(Text24, { color: "#9CA3AF", children: t("account_planLabel") }),
3252
- /* @__PURE__ */ jsx24(Text24, { color: "#22C55E", bold: true, children: "Pro" })
4071
+ /* @__PURE__ */ jsxs29(Box28, { gap: 1, children: [
4072
+ /* @__PURE__ */ jsx30(Text29, { color: COLORS.muted, children: t("account_planLabel") }),
4073
+ /* @__PURE__ */ jsx30(Text29, { color: COLORS.success, bold: true, children: "Pro" })
3253
4074
  ] }),
3254
- /* @__PURE__ */ jsxs24(Box22, { gap: 1, children: [
3255
- /* @__PURE__ */ jsx24(Text24, { color: "#9CA3AF", children: t("account_keyLabel") }),
3256
- /* @__PURE__ */ jsx24(Text24, { children: maskKey(license.key) })
4075
+ /* @__PURE__ */ jsxs29(Box28, { gap: 1, children: [
4076
+ /* @__PURE__ */ jsx30(Text29, { color: COLORS.muted, children: t("account_keyLabel") }),
4077
+ /* @__PURE__ */ jsx30(Text29, { children: maskKey(license.key) })
3257
4078
  ] }),
3258
- license.expiresAt && /* @__PURE__ */ jsxs24(Box22, { gap: 1, children: [
3259
- /* @__PURE__ */ jsx24(Text24, { color: "#9CA3AF", children: t("account_expiresLabel") }),
3260
- /* @__PURE__ */ jsx24(Text24, { children: new Date(license.expiresAt).toLocaleDateString() })
4079
+ license.expiresAt && /* @__PURE__ */ jsxs29(Box28, { gap: 1, children: [
4080
+ /* @__PURE__ */ jsx30(Text29, { color: COLORS.muted, children: t("account_expiresLabel") }),
4081
+ /* @__PURE__ */ jsx30(Text29, { children: formatDate(license.expiresAt) })
3261
4082
  ] }),
3262
- /* @__PURE__ */ jsxs24(Box22, { gap: 1, children: [
3263
- /* @__PURE__ */ jsx24(Text24, { color: "#9CA3AF", children: t("account_activatedLabel") }),
3264
- /* @__PURE__ */ jsx24(Text24, { children: new Date(license.activatedAt).toLocaleDateString() })
4083
+ /* @__PURE__ */ jsxs29(Box28, { gap: 1, children: [
4084
+ /* @__PURE__ */ jsx30(Text29, { color: COLORS.muted, children: t("account_activatedLabel") }),
4085
+ /* @__PURE__ */ jsx30(Text29, { children: formatDate(license.activatedAt) })
3265
4086
  ] })
3266
4087
  ] }),
3267
- status === "free" && /* @__PURE__ */ jsxs24(Box22, { flexDirection: "column", marginTop: 2, borderStyle: "round", borderColor: "#FF6B2B", paddingX: 2, paddingY: 1, children: [
3268
- /* @__PURE__ */ jsxs24(Text24, { bold: true, color: "#FF6B2B", children: [
4088
+ status === "free" && /* @__PURE__ */ jsxs29(Box28, { flexDirection: "column", marginTop: 2, borderStyle: "round", borderColor: COLORS.brand, paddingX: 2, paddingY: 1, children: [
4089
+ /* @__PURE__ */ jsxs29(Text29, { bold: true, color: COLORS.brand, children: [
3269
4090
  "\u2B50",
3270
4091
  " ",
3271
4092
  t("account_upgradeTitle")
3272
4093
  ] }),
3273
- /* @__PURE__ */ jsx24(Text24, { children: " " }),
3274
- /* @__PURE__ */ jsx24(Text24, { children: t("account_unlockDesc") }),
3275
- /* @__PURE__ */ jsx24(Text24, { color: "#06B6D4", bold: true, children: t("account_pricing") }),
3276
- /* @__PURE__ */ jsx24(Text24, { children: " " }),
3277
- /* @__PURE__ */ jsxs24(Text24, { color: "#9CA3AF", children: [
4094
+ /* @__PURE__ */ jsx30(Text29, { children: " " }),
4095
+ /* @__PURE__ */ jsx30(Text29, { children: t("account_unlockDesc") }),
4096
+ /* @__PURE__ */ jsx30(Text29, { color: COLORS.info, bold: true, children: t("account_pricing") }),
4097
+ /* @__PURE__ */ jsx30(Text29, { children: " " }),
4098
+ /* @__PURE__ */ jsxs29(Text29, { color: COLORS.muted, children: [
3278
4099
  t("upgrade_buyAt"),
3279
4100
  " ",
3280
- /* @__PURE__ */ jsx24(Text24, { color: "#38BDF8", bold: true, children: t("upgrade_buyUrl") })
4101
+ /* @__PURE__ */ jsx30(Text29, { color: COLORS.sky, bold: true, children: t("upgrade_buyUrl") })
3281
4102
  ] }),
3282
- /* @__PURE__ */ jsxs24(Text24, { color: "#9CA3AF", children: [
4103
+ /* @__PURE__ */ jsxs29(Text29, { color: COLORS.muted, children: [
3283
4104
  t("account_runActivate"),
3284
4105
  " ",
3285
- /* @__PURE__ */ jsx24(Text24, { color: "#22C55E", bold: true, children: t("account_activateCmd") })
4106
+ /* @__PURE__ */ jsx30(Text29, { color: COLORS.success, bold: true, children: t("account_activateCmd") })
3286
4107
  ] })
3287
4108
  ] }),
3288
- status === "expired" && /* @__PURE__ */ jsx24(Box22, { marginTop: 1, children: /* @__PURE__ */ jsx24(Box22, { borderStyle: "round", borderColor: "#EF4444", paddingX: 2, paddingY: 0, children: /* @__PURE__ */ jsx24(Text24, { color: "#EF4444", children: t("account_licenseExpired") }) }) }),
3289
- deactivating && /* @__PURE__ */ jsx24(Text24, { color: "#38BDF8", children: t("account_deactivating") })
4109
+ 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") }) }) }),
4110
+ deactivating && /* @__PURE__ */ jsx30(Text29, { color: COLORS.sky, children: t("account_deactivating") }),
4111
+ deactivateError && /* @__PURE__ */ jsx30(Text29, { color: COLORS.error, children: deactivateError })
3290
4112
  ] }),
3291
- /* @__PURE__ */ jsx24(Box22, { marginTop: 2, children: /* @__PURE__ */ jsxs24(Text24, { color: "#6B7280", children: [
4113
+ /* @__PURE__ */ jsx30(Box28, { marginTop: 2, children: /* @__PURE__ */ jsxs29(Text29, { color: COLORS.textSecondary, children: [
3292
4114
  status === "pro" ? `d:${t("hint_deactivate")}` : "",
3293
4115
  " ",
3294
- t("app_version", { version: "0.2.0" })
4116
+ t("app_version", { version: "0.3.1" })
3295
4117
  ] }) })
3296
4118
  ] });
3297
4119
  }
3298
4120
 
3299
4121
  // src/app.tsx
3300
- import { jsx as jsx25 } from "react/jsx-runtime";
3301
- function App() {
3302
- const { exit } = useApp();
3303
- const currentView = useNavigationStore((s) => s.currentView);
3304
- const isPro = useLicenseStore((s) => s.isPro);
4122
+ import { jsx as jsx31, jsxs as jsxs30 } from "react/jsx-runtime";
4123
+ function LicenseInitializer() {
3305
4124
  const initLicense = useLicenseStore((s) => s.initialize);
3306
4125
  useEffect15(() => {
3307
4126
  initLicense();
3308
4127
  }, []);
4128
+ return null;
4129
+ }
4130
+ function ViewRouter({ currentView }) {
4131
+ const isPro = useLicenseStore((s) => s.isPro);
4132
+ if (isProView(currentView) && !isPro()) {
4133
+ return /* @__PURE__ */ jsx31(UpgradePrompt, { viewId: currentView });
4134
+ }
4135
+ switch (currentView) {
4136
+ case "dashboard":
4137
+ return /* @__PURE__ */ jsx31(DashboardView, {});
4138
+ case "installed":
4139
+ return /* @__PURE__ */ jsx31(InstalledView, {});
4140
+ case "search":
4141
+ return /* @__PURE__ */ jsx31(SearchView, {});
4142
+ case "outdated":
4143
+ return /* @__PURE__ */ jsx31(OutdatedView, {});
4144
+ case "package-info":
4145
+ return /* @__PURE__ */ jsx31(PackageInfoView, {});
4146
+ case "services":
4147
+ return /* @__PURE__ */ jsx31(ServicesView, {});
4148
+ case "doctor":
4149
+ return /* @__PURE__ */ jsx31(DoctorView, {});
4150
+ case "profiles":
4151
+ return /* @__PURE__ */ jsx31(ProfilesView, {});
4152
+ case "smart-cleanup":
4153
+ return /* @__PURE__ */ jsx31(SmartCleanupView, {});
4154
+ case "history":
4155
+ return /* @__PURE__ */ jsx31(HistoryView, {});
4156
+ case "security-audit":
4157
+ return /* @__PURE__ */ jsx31(SecurityAuditView, {});
4158
+ case "account":
4159
+ return /* @__PURE__ */ jsx31(AccountView, {});
4160
+ }
4161
+ }
4162
+ function App() {
4163
+ const { exit } = useApp();
4164
+ const currentView = useNavigationStore((s) => s.currentView);
3309
4165
  useGlobalKeyboard({ onQuit: exit });
3310
- const renderView = () => {
3311
- if (isProView(currentView) && !isPro()) {
3312
- return /* @__PURE__ */ jsx25(UpgradePrompt, { viewId: currentView });
3313
- }
3314
- switch (currentView) {
3315
- case "dashboard":
3316
- return /* @__PURE__ */ jsx25(DashboardView, {});
3317
- case "installed":
3318
- return /* @__PURE__ */ jsx25(InstalledView, {});
3319
- case "search":
3320
- return /* @__PURE__ */ jsx25(SearchView, {});
3321
- case "outdated":
3322
- return /* @__PURE__ */ jsx25(OutdatedView, {});
3323
- case "package-info":
3324
- return /* @__PURE__ */ jsx25(PackageInfoView, {});
3325
- case "services":
3326
- return /* @__PURE__ */ jsx25(ServicesView, {});
3327
- case "doctor":
3328
- return /* @__PURE__ */ jsx25(DoctorView, {});
3329
- case "profiles":
3330
- return /* @__PURE__ */ jsx25(ProfilesView, {});
3331
- case "smart-cleanup":
3332
- return /* @__PURE__ */ jsx25(SmartCleanupView, {});
3333
- case "history":
3334
- return /* @__PURE__ */ jsx25(HistoryView, {});
3335
- case "security-audit":
3336
- return /* @__PURE__ */ jsx25(SecurityAuditView, {});
3337
- case "account":
3338
- return /* @__PURE__ */ jsx25(AccountView, {});
3339
- }
3340
- };
3341
- return /* @__PURE__ */ jsx25(AppLayout, { children: renderView() });
4166
+ return /* @__PURE__ */ jsxs30(AppLayout, { children: [
4167
+ /* @__PURE__ */ jsx31(LicenseInitializer, {}),
4168
+ /* @__PURE__ */ jsx31(ViewRouter, { currentView })
4169
+ ] });
3342
4170
  }
3343
4171
 
3344
4172
  // src/index.tsx
3345
- import { jsx as jsx26 } from "react/jsx-runtime";
4173
+ import { jsx as jsx32 } from "react/jsx-runtime";
3346
4174
  var [, , command, arg] = process.argv;
3347
4175
  async function runCli() {
3348
4176
  await ensureDataDirs();
@@ -3357,7 +4185,7 @@ async function runCli() {
3357
4185
  console.log(t("cli_activated", { email: license.customerEmail }));
3358
4186
  console.log(t("cli_planPro"));
3359
4187
  if (license.expiresAt) {
3360
- console.log(t("cli_expires", { date: new Date(license.expiresAt).toLocaleDateString() }));
4188
+ console.log(t("cli_expires", { date: formatDate(license.expiresAt) }));
3361
4189
  }
3362
4190
  } catch (err) {
3363
4191
  console.error(t("cli_activationFailed", { error: err instanceof Error ? err.message : String(err) }));
@@ -3385,25 +4213,62 @@ async function runCli() {
3385
4213
  }
3386
4214
  return;
3387
4215
  }
3388
- if (command === "status") {
4216
+ if (command === "revalidate") {
3389
4217
  const license = await loadLicense();
3390
4218
  if (!license) {
4219
+ console.log(t("cli_noLicense"));
4220
+ process.exit(1);
4221
+ }
4222
+ const result = await revalidate(license);
4223
+ if (result === "expired") {
4224
+ console.error(t("cli_revalidateFailed"));
4225
+ process.exit(1);
4226
+ }
4227
+ const updated = await loadLicense();
4228
+ if (result === "grace") {
4229
+ console.warn(t("cli_revalidateGrace"));
4230
+ } else {
4231
+ console.log(t("cli_revalidated"));
4232
+ }
4233
+ if (updated?.expiresAt) {
4234
+ console.log(t("cli_expires", { date: formatDate(updated.expiresAt) }));
4235
+ }
4236
+ return;
4237
+ }
4238
+ if (command === "status") {
4239
+ await useLicenseStore.getState().initialize();
4240
+ const { status, license, degradation } = useLicenseStore.getState();
4241
+ if (status === "free") {
3391
4242
  console.log(t("cli_planFree"));
3392
4243
  console.log(t("cli_upgradeHint"));
3393
4244
  } else {
3394
- console.log(t("cli_planPro"));
3395
- console.log(t("cli_email", { email: license.customerEmail }));
3396
- console.log(t("cli_status", { status: license.status }));
3397
- if (license.expiresAt) {
3398
- console.log(t("cli_expires", { date: new Date(license.expiresAt).toLocaleDateString() }));
4245
+ const planLabel = status === "expired" ? t("cli_planExpired") : t("cli_planPro");
4246
+ console.log(planLabel);
4247
+ if (license) {
4248
+ console.log(t("cli_email", { email: license.customerEmail }));
4249
+ }
4250
+ const statusText = status === "pro" ? degradation === "none" ? license?.status ?? "active" : degradation : status;
4251
+ console.log(t("cli_status", { status: statusText }));
4252
+ if (license?.expiresAt) {
4253
+ console.log(t("cli_expires", { date: formatDate(license.expiresAt) }));
4254
+ }
4255
+ if (status === "expired") {
4256
+ console.log(t("cli_revalidateHint"));
4257
+ }
4258
+ if (status === "pro" && degradation !== "none" && license) {
4259
+ const days = Math.floor((Date.now() - new Date(license.lastValidatedAt).getTime()) / (24 * 60 * 60 * 1e3));
4260
+ console.log(t("license_offlineWarning", { days }));
4261
+ console.log(t("cli_revalidateHint"));
3399
4262
  }
3400
4263
  }
3401
4264
  return;
3402
4265
  }
3403
4266
  if (command === "install-brewbar") {
3404
- const { installBrewBar } = await import("./brewbar-installer-4Z2WE57I.js");
4267
+ await useLicenseStore.getState().initialize();
4268
+ const isPro = useLicenseStore.getState().isPro();
4269
+ const { installBrewBar } = await import("./brewbar-installer-H5MLNNTD.js");
3405
4270
  try {
3406
- await installBrewBar(arg === "--force");
4271
+ await installBrewBar(isPro, arg === "--force");
3407
4272
  console.log(t("cli_brewbarInstalled"));
3408
4273
  } catch (err) {
3409
4274
  console.error(err instanceof Error ? err.message : String(err));
@@ -3412,7 +4277,7 @@ async function runCli() {
3412
4277
  return;
3413
4278
  }
3414
4279
  if (command === "uninstall-brewbar") {
3415
- const { uninstallBrewBar } = await import("./brewbar-installer-4Z2WE57I.js");
4280
+ const { uninstallBrewBar } = await import("./brewbar-installer-H5MLNNTD.js");
3416
4281
  try {
3417
4282
  await uninstallBrewBar();
3418
4283
  console.log(t("cli_brewbarUninstalled"));
@@ -3422,9 +4287,22 @@ async function runCli() {
3422
4287
  }
3423
4288
  return;
3424
4289
  }
3425
- render(/* @__PURE__ */ jsx26(App, {}));
4290
+ if (command === "delete-account") {
4291
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
4292
+ const answer = await rl.question(t("delete_account_confirm") + " (y/N): ");
4293
+ rl.close();
4294
+ if (answer.toLowerCase() !== "y" && answer.toLowerCase() !== "s") {
4295
+ console.log(t("cli_deactivateCancelled"));
4296
+ return;
4297
+ }
4298
+ await rm3(DATA_DIR, { recursive: true, force: true });
4299
+ console.log(t("delete_account_success"));
4300
+ return;
4301
+ }
4302
+ render(/* @__PURE__ */ jsx32(App, {}));
3426
4303
  }
3427
4304
  runCli().catch((err) => {
3428
4305
  console.error(err instanceof Error ? err.message : String(err));
3429
4306
  process.exit(1);
3430
4307
  });
4308
+ //# sourceMappingURL=index.js.map