brew-tui 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/index.js ADDED
@@ -0,0 +1,3338 @@
1
+ import {
2
+ appendEntry,
3
+ clearHistory,
4
+ loadHistory
5
+ } from "./chunk-3BK3B53S.js";
6
+ import {
7
+ PROFILES_DIR,
8
+ activate,
9
+ deactivate,
10
+ ensureDataDirs,
11
+ loadLicense,
12
+ requirePro,
13
+ t,
14
+ tp,
15
+ useLicenseStore,
16
+ useLocaleStore
17
+ } from "./chunk-P6PTN4HR.js";
18
+
19
+ // src/index.tsx
20
+ import { createInterface } from "readline/promises";
21
+ import { render } from "ink";
22
+
23
+ // src/app.tsx
24
+ import { useEffect as useEffect15 } from "react";
25
+ import { useApp } from "ink";
26
+
27
+ // src/components/layout/app-layout.tsx
28
+ import { Box as Box3 } from "ink";
29
+
30
+ // src/components/layout/header.tsx
31
+ import React from "react";
32
+ import { Box, Text as Text2 } from "ink";
33
+
34
+ // src/stores/navigation-store.ts
35
+ import { create } from "zustand";
36
+ var VIEWS = [
37
+ "dashboard",
38
+ "installed",
39
+ "search",
40
+ "outdated",
41
+ "package-info",
42
+ "services",
43
+ "doctor",
44
+ "profiles",
45
+ "smart-cleanup",
46
+ "history",
47
+ "security-audit",
48
+ "account"
49
+ ];
50
+ var useNavigationStore = create((set, get) => ({
51
+ currentView: "dashboard",
52
+ previousView: null,
53
+ selectedPackage: null,
54
+ viewHistory: ["dashboard"],
55
+ navigate: (view) => {
56
+ const { currentView, viewHistory } = get();
57
+ if (view === currentView) return;
58
+ set({
59
+ currentView: view,
60
+ previousView: currentView,
61
+ viewHistory: [...viewHistory.slice(-19), view]
62
+ });
63
+ },
64
+ goBack: () => {
65
+ const { previousView } = get();
66
+ if (previousView) {
67
+ set((state) => ({
68
+ currentView: previousView,
69
+ previousView: state.currentView
70
+ }));
71
+ }
72
+ },
73
+ selectPackage: (name) => set({ selectedPackage: name })
74
+ }));
75
+ function getNextView(current) {
76
+ const idx = VIEWS.indexOf(current);
77
+ return VIEWS[(idx + 1) % VIEWS.length];
78
+ }
79
+ function getPrevView(current) {
80
+ const idx = VIEWS.indexOf(current);
81
+ return VIEWS[(idx - 1 + VIEWS.length) % VIEWS.length];
82
+ }
83
+
84
+ // src/lib/license/feature-gate.ts
85
+ var PRO_VIEWS = /* @__PURE__ */ new Set([
86
+ "profiles",
87
+ "smart-cleanup",
88
+ "history",
89
+ "security-audit"
90
+ ]);
91
+ function isProView(viewId) {
92
+ return PRO_VIEWS.has(viewId);
93
+ }
94
+
95
+ // src/utils/gradient.tsx
96
+ import { Text } from "ink";
97
+ import { Fragment, jsx } from "react/jsx-runtime";
98
+ function hexToRgb(hex) {
99
+ const n = parseInt(hex.slice(1), 16);
100
+ return [n >> 16 & 255, n >> 8 & 255, n & 255];
101
+ }
102
+ function rgbToHex(r, g, b) {
103
+ return "#" + (1 << 24 | r << 16 | g << 8 | b).toString(16).slice(1).toUpperCase();
104
+ }
105
+ function lerp(a, b, t2) {
106
+ return Math.round(a + (b - a) * t2);
107
+ }
108
+ function interpolateColor(c1, c2, t2) {
109
+ const [r1, g1, b1] = hexToRgb(c1);
110
+ const [r2, g2, b2] = hexToRgb(c2);
111
+ return rgbToHex(lerp(r1, r2, t2), lerp(g1, g2, t2), lerp(b1, b2, t2));
112
+ }
113
+ function GradientText({ children, colors, bold }) {
114
+ if (colors.length < 2) {
115
+ return /* @__PURE__ */ jsx(Text, { color: colors[0], bold, children });
116
+ }
117
+ const chars = [...children];
118
+ const maxIdx = Math.max(chars.length - 1, 1);
119
+ return /* @__PURE__ */ jsx(Fragment, { children: chars.map((char, i) => {
120
+ const t2 = i / maxIdx;
121
+ const segment = t2 * (colors.length - 1);
122
+ const lower = Math.floor(segment);
123
+ const upper = Math.min(lower + 1, colors.length - 1);
124
+ const frac = segment - lower;
125
+ const color = interpolateColor(colors[lower], colors[upper], frac);
126
+ return /* @__PURE__ */ jsx(Text, { color, bold, children: char }, i);
127
+ }) });
128
+ }
129
+ var GRADIENTS = {
130
+ gold: ["#FFD700", "#FFA500", "#B8860B"],
131
+ sunset: ["#FF6B2B", "#FFD700", "#FF6B2B"],
132
+ ocean: ["#06B6D4", "#3B82F6", "#A855F7"],
133
+ emerald: ["#22C55E", "#2DD4BF", "#06B6D4"],
134
+ fire: ["#EF4444", "#F59E0B", "#FFD700"],
135
+ version: ["#EF4444", "#9CA3AF", "#2DD4BF"],
136
+ pro: ["#FF6B2B", "#FFD700", "#FF6B2B"]
137
+ };
138
+
139
+ // src/components/layout/header.tsx
140
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
141
+ var LOGO_BREW = [
142
+ "\u256D\u2501\u2501\u256E\u2571\u256D\u2501\u2501\u2501\u256E\u256D\u2501\u2501\u2501\u256E\u256D\u256E\u256D\u256E\u256D\u256E\u2571\u2571\u2571\u2571\u2571\u2571\u2571",
143
+ "\u2503\u256D\u256E\u2503\u2571\u2503\u256D\u2501\u256E\u2503\u2503\u256D\u2501\u2501\u256F\u2503\u2503\u2503\u2503\u2503\u2503\u2571\u2571\u2571\u2571\u2571\u2571\u2571",
144
+ "\u2503\u2570\u256F\u2570\u256E\u2503\u2570\u2501\u256F\u2503\u2503\u2570\u2501\u2501\u256E\u2503\u2503\u2503\u2503\u2503\u2503\u2571\u2571\u2571\u2571\u2571\u2571\u2571",
145
+ "\u2503\u256D\u2501\u256E\u2503\u2503\u256D\u256E\u256D\u256F\u2503\u256D\u2501\u2501\u256F\u2503\u2570\u256F\u2570\u256F\u2503\u256D\u2501\u2501\u2533\u2501\u2501\u256E",
146
+ "\u2503\u2570\u2501\u256F\u2503\u2503\u2503\u2503\u2570\u256E\u2503\u2570\u2501\u2501\u256E\u2570\u256E\u256D\u256E\u256D\u256F\u2570\u2501\u2501\u253B\u2501\u2501\u256F",
147
+ "\u2570\u2501\u2501\u2501\u256F\u2570\u256F\u2570\u2501\u256F\u2570\u2501\u2501\u2501\u256F\u2571\u2570\u256F\u2570\u256F\u2571\u2571\u2571\u2571\u2571\u2571\u2571\u2571"
148
+ ];
149
+ var LOGO_TUI = [
150
+ "\u256D\u2501\u2501\u2501\u2501\u256E\u256D\u256E\u2571\u256D\u256E\u256D\u2501\u2501\u256E",
151
+ "\u2503\u256D\u256E\u256D\u256E\u2503\u2503\u2503\u2571\u2503\u2503\u2570\u252B\u2523\u256F",
152
+ "\u2570\u256F\u2503\u2503\u2570\u256F\u2503\u2503\u2571\u2503\u2503\u2571\u2503\u2503",
153
+ "\u2571\u2571\u2503\u2503\u2571\u2571\u2503\u2503\u2571\u2503\u2503\u2571\u2503\u2503",
154
+ "\u2571\u2571\u2503\u2503\u2571\u2571\u2503\u2570\u2501\u256F\u2503\u256D\u252B\u2523\u256E",
155
+ "\u2571\u2571\u2570\u256F\u2571\u2571\u2570\u2501\u2501\u2501\u256F\u2570\u2501\u2501\u256F"
156
+ ];
157
+ var VIEW_LABEL_KEYS = {
158
+ dashboard: "view_dashboard",
159
+ installed: "view_installed",
160
+ search: "view_search",
161
+ outdated: "view_outdated",
162
+ "package-info": "view_packageInfo",
163
+ services: "view_services",
164
+ doctor: "view_doctor",
165
+ profiles: "view_profiles",
166
+ "smart-cleanup": "view_smartCleanup",
167
+ history: "view_history",
168
+ "security-audit": "view_securityAudit",
169
+ account: "view_account"
170
+ };
171
+ var VIEW_KEYS = {
172
+ dashboard: "1",
173
+ installed: "2",
174
+ search: "3",
175
+ outdated: "4",
176
+ "package-info": "",
177
+ services: "5",
178
+ doctor: "6",
179
+ profiles: "7",
180
+ "smart-cleanup": "8",
181
+ history: "9",
182
+ "security-audit": "0",
183
+ account: ""
184
+ };
185
+ var TAB_VIEWS = [
186
+ "dashboard",
187
+ "installed",
188
+ "search",
189
+ "outdated",
190
+ "services",
191
+ "doctor",
192
+ "profiles",
193
+ "smart-cleanup",
194
+ "history",
195
+ "security-audit",
196
+ "account"
197
+ ];
198
+ function Header() {
199
+ const currentView = useNavigationStore((s) => s.currentView);
200
+ useLocaleStore((s) => s.locale);
201
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
202
+ /* @__PURE__ */ jsx2(Box, { flexDirection: "column", paddingX: 1, children: LOGO_BREW.map((brew, i) => /* @__PURE__ */ jsxs(Box, { children: [
203
+ /* @__PURE__ */ jsx2(GradientText, { colors: GRADIENTS.gold, children: brew }),
204
+ /* @__PURE__ */ jsx2(GradientText, { colors: ["#B8860B", "#8B6914", "#6B4F10"], children: LOGO_TUI[i] })
205
+ ] }, i)) }),
206
+ /* @__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) => {
207
+ const key = VIEW_KEYS[view];
208
+ const viewLabel = t(VIEW_LABEL_KEYS[view]);
209
+ const label = key ? `${key}:${viewLabel}` : viewLabel;
210
+ const isPro = isProView(view);
211
+ return /* @__PURE__ */ jsxs(React.Fragment, { children: [
212
+ i > 0 && /* @__PURE__ */ jsxs(Text2, { color: "#4B5563", children: [
213
+ " ",
214
+ "\u2502",
215
+ " "
216
+ ] }),
217
+ /* @__PURE__ */ jsx2(
218
+ Text2,
219
+ {
220
+ bold: view === currentView,
221
+ color: view === currentView ? "#22C55E" : "#6B7280",
222
+ underline: view === currentView,
223
+ children: view === currentView ? `\u25CF ${label}` : label
224
+ }
225
+ ),
226
+ isPro && /* @__PURE__ */ jsxs(Text2, { color: "#FF6B2B", bold: true, children: [
227
+ " ",
228
+ t("pro_badge")
229
+ ] })
230
+ ] }, view);
231
+ }) })
232
+ ] });
233
+ }
234
+
235
+ // src/components/layout/footer.tsx
236
+ import React2 from "react";
237
+ import { Box as Box2, Text as Text3 } from "ink";
238
+ import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
239
+ var VIEW_HINT_DEFS = {
240
+ dashboard: [["1-0", "hint_navigate"], ["tab", "hint_next"], ["q", "hint_quit"]],
241
+ installed: [["/", "hint_filter"], ["enter", "hint_info"], ["u", "hint_uninstall"], ["f", "hint_toggle"], ["tab", "hint_next"], ["q", "hint_quit"]],
242
+ search: [["hint_typeToSearch"], ["enter", "hint_details"], ["i", "hint_install"], ["esc", "hint_back"], ["q", "hint_quit"]],
243
+ outdated: [["enter", "hint_upgrade"], ["A", "hint_upgradeAll"], ["p", "hint_pin"], ["tab", "hint_next"], ["q", "hint_quit"]],
244
+ "package-info": [["i", "hint_install"], ["u", "hint_uninstall"], ["U", "hint_upgrade"], ["esc", "hint_back"], ["q", "hint_quit"]],
245
+ services: [["s", "hint_start"], ["S", "hint_stop"], ["R", "hint_restart"], ["r", "hint_refresh"], ["tab", "hint_next"], ["q", "hint_quit"]],
246
+ doctor: [["r", "hint_refresh"], ["tab", "hint_next"], ["q", "hint_quit"]],
247
+ profiles: [["n", "hint_new"], ["enter", "hint_details"], ["e", "hint_edit"], ["i", "hint_import"], ["d", "hint_delete"], ["q", "hint_quit"]],
248
+ "smart-cleanup": [["enter", "hint_toggle"], ["c", "hint_clean"], ["r", "hint_refresh"], ["q", "hint_quit"]],
249
+ history: [["/", "hint_search"], ["enter", "hint_replay"], ["f", "hint_filter"], ["c", "hint_clear"], ["q", "hint_quit"]],
250
+ "security-audit": [["r", "hint_scan"], ["enter", "hint_details"], ["u", "hint_upgrade"], ["q", "hint_quit"]],
251
+ account: [["d", "hint_deactivate"], ["q", "hint_quit"]]
252
+ };
253
+ function HintItem({ def }) {
254
+ if (def.length === 1) return /* @__PURE__ */ jsx3(Text3, { color: "#FFD700", dimColor: true, children: t(def[0]) });
255
+ return /* @__PURE__ */ jsxs2(Fragment2, { children: [
256
+ /* @__PURE__ */ jsx3(Text3, { color: "#F9FAFB", bold: true, children: def[0] }),
257
+ /* @__PURE__ */ jsx3(Text3, { color: "#6B7280", children: ":" }),
258
+ /* @__PURE__ */ jsx3(Text3, { color: "#FFD700", dimColor: true, children: t(def[1]) })
259
+ ] });
260
+ }
261
+ function Footer() {
262
+ const currentView = useNavigationStore((s) => s.currentView);
263
+ const locale = useLocaleStore((s) => s.locale);
264
+ const defs = VIEW_HINT_DEFS[currentView] ?? [];
265
+ return /* @__PURE__ */ jsxs2(Box2, { borderStyle: "single", borderTop: true, borderBottom: false, borderLeft: false, borderRight: false, borderColor: "#FFD700", paddingX: 1, flexWrap: "wrap", children: [
266
+ defs.map((def, i) => {
267
+ const key = def.length === 1 ? def[0] : `${def[0]}:${def[1]}`;
268
+ return /* @__PURE__ */ jsxs2(React2.Fragment, { children: [
269
+ i > 0 && /* @__PURE__ */ jsxs2(Text3, { color: "#4B5563", children: [
270
+ " ",
271
+ "\u2502",
272
+ " "
273
+ ] }),
274
+ /* @__PURE__ */ jsx3(HintItem, { def })
275
+ ] }, key);
276
+ }),
277
+ /* @__PURE__ */ jsxs2(Text3, { color: "#4B5563", children: [
278
+ " ",
279
+ "\u2502",
280
+ " "
281
+ ] }),
282
+ /* @__PURE__ */ jsx3(Text3, { color: "#F9FAFB", bold: true, children: "L" }),
283
+ /* @__PURE__ */ jsx3(Text3, { color: "#6B7280", children: ":" }),
284
+ /* @__PURE__ */ jsxs2(Text3, { color: "#FFD700", dimColor: true, children: [
285
+ t("hint_lang"),
286
+ "(",
287
+ locale,
288
+ ")"
289
+ ] })
290
+ ] });
291
+ }
292
+
293
+ // src/components/layout/app-layout.tsx
294
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
295
+ function AppLayout({ children }) {
296
+ return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", width: "100%", children: [
297
+ /* @__PURE__ */ jsx4(Header, {}),
298
+ /* @__PURE__ */ jsx4(Box3, { flexDirection: "column", flexGrow: 1, paddingX: 2, paddingY: 1, children }),
299
+ /* @__PURE__ */ jsx4(Footer, {})
300
+ ] });
301
+ }
302
+
303
+ // src/hooks/use-keyboard.ts
304
+ import { useInput } from "ink";
305
+
306
+ // src/stores/modal-store.ts
307
+ import { create as create2 } from "zustand";
308
+ var useModalStore = create2((set) => ({
309
+ _count: 0,
310
+ isOpen: false,
311
+ openModal: () => set((s) => {
312
+ const next = s._count + 1;
313
+ return { _count: next, isOpen: next > 0 };
314
+ }),
315
+ closeModal: () => set((s) => {
316
+ const next = Math.max(0, s._count - 1);
317
+ return { _count: next, isOpen: next > 0 };
318
+ })
319
+ }));
320
+
321
+ // src/hooks/use-keyboard.ts
322
+ var VIEW_KEYS2 = {
323
+ "1": "dashboard",
324
+ "2": "installed",
325
+ "3": "search",
326
+ "4": "outdated",
327
+ "5": "services",
328
+ "6": "doctor",
329
+ "7": "profiles",
330
+ "8": "smart-cleanup",
331
+ "9": "history",
332
+ "0": "security-audit"
333
+ };
334
+ function useGlobalKeyboard(opts) {
335
+ const navigate = useNavigationStore((s) => s.navigate);
336
+ const currentView = useNavigationStore((s) => s.currentView);
337
+ const goBack = useNavigationStore((s) => s.goBack);
338
+ const { locale, setLocale } = useLocaleStore();
339
+ const modalOpen = useModalStore((s) => s.isOpen);
340
+ useInput((input, key) => {
341
+ if (opts?.disabled) return;
342
+ if (modalOpen) return;
343
+ if (input === "q" || key.ctrl && input === "c") {
344
+ opts?.onQuit?.();
345
+ return;
346
+ }
347
+ if (key.escape) {
348
+ goBack();
349
+ return;
350
+ }
351
+ if (key.tab && key.shift) {
352
+ navigate(getPrevView(currentView));
353
+ return;
354
+ }
355
+ if (key.tab) {
356
+ navigate(getNextView(currentView));
357
+ return;
358
+ }
359
+ if (input === "L") {
360
+ setLocale(locale === "en" ? "es" : "en");
361
+ return;
362
+ }
363
+ if (input in VIEW_KEYS2) {
364
+ navigate(VIEW_KEYS2[input]);
365
+ }
366
+ }, { isActive: !opts?.disabled });
367
+ }
368
+
369
+ // src/components/common/upgrade-prompt.tsx
370
+ import { Box as Box4, Text as Text4 } from "ink";
371
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
372
+ var FEATURE_KEYS = {
373
+ profiles: { title: "upgrade_profiles", desc: "upgrade_profilesDesc" },
374
+ "smart-cleanup": { title: "upgrade_cleanup", desc: "upgrade_cleanupDesc" },
375
+ history: { title: "upgrade_history", desc: "upgrade_historyDesc" },
376
+ "security-audit": { title: "upgrade_security", desc: "upgrade_securityDesc" }
377
+ };
378
+ function UpgradePrompt({ viewId }) {
379
+ const keys = FEATURE_KEYS[viewId];
380
+ if (!keys) return null;
381
+ const title = t(keys.title);
382
+ return /* @__PURE__ */ jsx5(Box4, { flexDirection: "column", alignItems: "center", paddingY: 2, children: /* @__PURE__ */ jsxs4(
383
+ Box4,
384
+ {
385
+ borderStyle: "double",
386
+ borderColor: "#FF6B2B",
387
+ paddingX: 3,
388
+ paddingY: 2,
389
+ flexDirection: "column",
390
+ alignItems: "center",
391
+ width: "80%",
392
+ children: [
393
+ /* @__PURE__ */ jsxs4(Text4, { bold: true, color: "#FF6B2B", children: [
394
+ "\u2B50",
395
+ " ",
396
+ t("upgrade_proFeature", { title })
397
+ ] }),
398
+ /* @__PURE__ */ jsx5(Text4, { children: " " }),
399
+ /* @__PURE__ */ jsx5(Text4, { color: "#F9FAFB", wrap: "wrap", children: t(keys.desc) }),
400
+ /* @__PURE__ */ jsx5(Text4, { children: " " }),
401
+ /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", alignItems: "center", children: [
402
+ /* @__PURE__ */ jsx5(Text4, { color: "#06B6D4", bold: true, children: t("upgrade_pricing") }),
403
+ /* @__PURE__ */ jsx5(Text4, { children: " " }),
404
+ /* @__PURE__ */ jsx5(Text4, { color: "#9CA3AF", children: t("upgrade_activateWith") }),
405
+ /* @__PURE__ */ jsxs4(Text4, { color: "#22C55E", bold: true, children: [
406
+ " ",
407
+ t("upgrade_activateCmd")
408
+ ] }),
409
+ /* @__PURE__ */ jsx5(Text4, { children: " " }),
410
+ /* @__PURE__ */ jsx5(Text4, { color: "#FF6B2B", children: t("upgrade_proLabel") })
411
+ ] })
412
+ ]
413
+ }
414
+ ) });
415
+ }
416
+
417
+ // src/views/dashboard.tsx
418
+ import { useEffect, useMemo } from "react";
419
+ import { Box as Box8, Text as Text10 } from "ink";
420
+
421
+ // src/stores/brew-store.ts
422
+ import { create as create3 } from "zustand";
423
+
424
+ // src/lib/brew-api.ts
425
+ import { spawn as spawn2 } from "child_process";
426
+
427
+ // src/lib/brew-cli.ts
428
+ import { spawn } from "child_process";
429
+ async function execBrew(args) {
430
+ return new Promise((resolve, reject) => {
431
+ const proc = spawn("brew", args, { env: { ...process.env, HOMEBREW_NO_AUTO_UPDATE: "1" } });
432
+ let stdout = "";
433
+ let stderr = "";
434
+ proc.stdout.on("data", (d) => {
435
+ stdout += d.toString();
436
+ });
437
+ proc.stderr.on("data", (d) => {
438
+ stderr += d.toString();
439
+ });
440
+ proc.on("close", (code) => {
441
+ if (code === 0) {
442
+ resolve(stdout);
443
+ } else {
444
+ reject(new Error(stderr.trim() || `brew ${args.join(" ")} exited with code ${code}`));
445
+ }
446
+ });
447
+ proc.on("error", (err) => {
448
+ reject(new Error(`Failed to run brew: ${err.message}`));
449
+ });
450
+ });
451
+ }
452
+ async function* streamBrew(args) {
453
+ const proc = spawn("brew", args, {
454
+ env: { ...process.env, HOMEBREW_NO_AUTO_UPDATE: "1" },
455
+ stdio: ["ignore", "pipe", "pipe"]
456
+ });
457
+ let buffer = "";
458
+ const lines = [];
459
+ let done = false;
460
+ let exitError = null;
461
+ const push = (chunk) => {
462
+ buffer += chunk.toString();
463
+ const parts = buffer.split("\n");
464
+ buffer = parts.pop() ?? "";
465
+ for (const line of parts) {
466
+ if (line.trim()) lines.push(line);
467
+ }
468
+ };
469
+ proc.stdout.on("data", push);
470
+ proc.stderr.on("data", push);
471
+ proc.on("close", (code) => {
472
+ if (buffer.trim()) lines.push(buffer.trim());
473
+ done = true;
474
+ if (code !== 0) {
475
+ exitError = `brew ${args.join(" ")} exited with code ${code}`;
476
+ }
477
+ });
478
+ proc.on("error", (err) => {
479
+ done = true;
480
+ exitError = err.message;
481
+ });
482
+ try {
483
+ while (!done || lines.length > 0) {
484
+ if (lines.length > 0) {
485
+ yield lines.shift();
486
+ } else if (!done) {
487
+ await new Promise((r) => setTimeout(r, 50));
488
+ }
489
+ }
490
+ } finally {
491
+ if (!done) {
492
+ proc.kill();
493
+ }
494
+ }
495
+ if (exitError) {
496
+ throw new Error(exitError);
497
+ }
498
+ }
499
+
500
+ // src/lib/parsers/json-parser.ts
501
+ function safeParse(raw, context) {
502
+ try {
503
+ const result = JSON.parse(raw);
504
+ if (result === null || result === void 0) {
505
+ throw new Error(`${context} returned null or empty response`);
506
+ }
507
+ return result;
508
+ } catch (err) {
509
+ throw new Error(`Failed to parse ${context} JSON: ${err instanceof Error ? err.message : String(err)}`);
510
+ }
511
+ }
512
+ function parseInstalledJson(raw) {
513
+ const data = safeParse(raw, "brew info --installed");
514
+ return {
515
+ formulae: Array.isArray(data.formulae) ? data.formulae : [],
516
+ casks: Array.isArray(data.casks) ? data.casks : []
517
+ };
518
+ }
519
+ function parseOutdatedJson(raw) {
520
+ const data = safeParse(raw, "brew outdated");
521
+ return {
522
+ formulae: Array.isArray(data.formulae) ? data.formulae : [],
523
+ casks: Array.isArray(data.casks) ? data.casks : []
524
+ };
525
+ }
526
+ function parseServicesJson(raw) {
527
+ const data = safeParse(raw, "brew services list");
528
+ if (!Array.isArray(data)) return [];
529
+ return data.map((s) => ({
530
+ name: s.name,
531
+ status: s.status ?? "none",
532
+ user: s.user ?? null,
533
+ file: s.file ?? null,
534
+ exit_code: s.exit_code ?? null
535
+ }));
536
+ }
537
+ function parseFormulaInfoJson(raw) {
538
+ const data = safeParse(raw, "brew info");
539
+ return data.formulae?.[0] ?? null;
540
+ }
541
+
542
+ // src/lib/parsers/text-parser.ts
543
+ function parseSearchResults(raw) {
544
+ const lines = raw.split("\n").map((l) => l.trim()).filter(Boolean);
545
+ const formulae = [];
546
+ const casks = [];
547
+ let section = "formulae";
548
+ for (const line of lines) {
549
+ if (line.startsWith("==> Formulae")) {
550
+ section = "formulae";
551
+ continue;
552
+ }
553
+ if (line.startsWith("==> Casks")) {
554
+ section = "casks";
555
+ continue;
556
+ }
557
+ if (line.startsWith("==>")) continue;
558
+ if (section === "formulae") formulae.push(line);
559
+ else casks.push(line);
560
+ }
561
+ return { formulae, casks };
562
+ }
563
+ function parseDoctorOutput(raw) {
564
+ const cleaned = raw.trim();
565
+ if (cleaned.includes("Your system is ready to brew")) {
566
+ return { warnings: [], isClean: true };
567
+ }
568
+ const warnings = [];
569
+ let current = "";
570
+ for (const line of cleaned.split("\n")) {
571
+ if (line.startsWith("Warning:")) {
572
+ if (current) warnings.push(current.trim());
573
+ current = line;
574
+ } else if (current) {
575
+ current += "\n" + line;
576
+ }
577
+ }
578
+ if (current) warnings.push(current.trim());
579
+ return { warnings, isClean: false };
580
+ }
581
+ function parseBrewConfig(raw) {
582
+ const lines = raw.split("\n");
583
+ const get = (key) => {
584
+ const line = lines.find((l) => l.startsWith(key));
585
+ return line?.split(":").slice(1).join(":").trim() ?? "";
586
+ };
587
+ return {
588
+ HOMEBREW_VERSION: get("HOMEBREW_VERSION"),
589
+ HOMEBREW_PREFIX: get("HOMEBREW_PREFIX"),
590
+ coreUpdated: get("Core tap last commit") || get("Core tap JSON")
591
+ };
592
+ }
593
+ function parseLeavesOutput(raw) {
594
+ return raw.split("\n").map((l) => l.trim()).filter(Boolean);
595
+ }
596
+
597
+ // src/lib/brew-api.ts
598
+ async function brewUpdate() {
599
+ return new Promise((resolve, reject) => {
600
+ const proc = spawn2("brew", ["update"], { stdio: "ignore" });
601
+ proc.on("close", (code) => {
602
+ if (code === 0) resolve();
603
+ else reject(new Error(`brew update exited with code ${code}`));
604
+ });
605
+ proc.on("error", reject);
606
+ });
607
+ }
608
+ async function getInstalled() {
609
+ const raw = await execBrew(["info", "--json=v2", "--installed"]);
610
+ return parseInstalledJson(raw);
611
+ }
612
+ async function getOutdated() {
613
+ const raw = await execBrew(["outdated", "--json=v2"]);
614
+ return parseOutdatedJson(raw);
615
+ }
616
+ async function getServices() {
617
+ const raw = await execBrew(["services", "list", "--json"]);
618
+ return parseServicesJson(raw);
619
+ }
620
+ async function getFormulaInfo(name) {
621
+ const raw = await execBrew(["info", "--json=v2", name]);
622
+ return parseFormulaInfoJson(raw);
623
+ }
624
+ async function search(term) {
625
+ const safeTerm = term.replace(/^-+/, "");
626
+ if (!safeTerm) return { formulae: [], casks: [] };
627
+ const raw = await execBrew(["search", safeTerm]);
628
+ return parseSearchResults(raw);
629
+ }
630
+ async function getDoctor() {
631
+ try {
632
+ const raw = await execBrew(["doctor"]);
633
+ return parseDoctorOutput(raw);
634
+ } catch (err) {
635
+ const msg = err instanceof Error ? err.message : String(err);
636
+ return parseDoctorOutput(msg);
637
+ }
638
+ }
639
+ async function getConfig() {
640
+ const raw = await execBrew(["config"]);
641
+ return parseBrewConfig(raw);
642
+ }
643
+ async function getLeaves() {
644
+ const raw = await execBrew(["leaves"]);
645
+ return parseLeavesOutput(raw);
646
+ }
647
+ async function uninstallPackage(name) {
648
+ return execBrew(["uninstall", name]);
649
+ }
650
+ async function serviceAction(name, action) {
651
+ return execBrew(["services", action, name]);
652
+ }
653
+ function formulaeToListItems(formulae) {
654
+ return formulae.map((f) => {
655
+ const installed = f.installed[0];
656
+ return {
657
+ name: f.name,
658
+ version: installed?.version ?? f.versions.stable,
659
+ desc: f.desc,
660
+ type: "formula",
661
+ outdated: f.outdated,
662
+ pinned: f.pinned,
663
+ kegOnly: f.keg_only,
664
+ installedAsDependency: installed?.installed_as_dependency ?? false,
665
+ installedTime: installed?.time ?? null
666
+ };
667
+ });
668
+ }
669
+ function casksToListItems(casks) {
670
+ return casks.map((c) => ({
671
+ name: c.token,
672
+ version: c.installed ?? c.version,
673
+ desc: c.desc,
674
+ type: "cask",
675
+ outdated: c.outdated,
676
+ pinned: false,
677
+ kegOnly: false,
678
+ installedAsDependency: false,
679
+ installedTime: c.installed_time ?? null
680
+ }));
681
+ }
682
+
683
+ // src/stores/brew-store.ts
684
+ function setLoading(set, key, value) {
685
+ set((s) => ({ loading: { ...s.loading, [key]: value } }));
686
+ }
687
+ function setError(set, key, error) {
688
+ set((s) => ({ errors: { ...s.errors, [key]: error } }));
689
+ }
690
+ var useBrewStore = create3((set) => ({
691
+ formulae: [],
692
+ casks: [],
693
+ outdated: { formulae: [], casks: [] },
694
+ services: [],
695
+ config: null,
696
+ leaves: [],
697
+ doctorWarnings: [],
698
+ doctorClean: null,
699
+ // Pre-initialize loading flags for keys that fetchAll always triggers so
700
+ // views that check loading.X get a spinner on first render rather than
701
+ // flashing empty/zeroed content for one frame before the fetch starts.
702
+ loading: { installed: true, outdated: true, services: true, config: true, doctor: false },
703
+ errors: {},
704
+ fetchInstalled: async () => {
705
+ setLoading(set, "installed", true);
706
+ setError(set, "installed", null);
707
+ try {
708
+ const result = await getInstalled();
709
+ set({ formulae: result.formulae, casks: result.casks });
710
+ } catch (err) {
711
+ setError(set, "installed", err instanceof Error ? err.message : String(err));
712
+ } finally {
713
+ setLoading(set, "installed", false);
714
+ }
715
+ },
716
+ fetchOutdated: async () => {
717
+ setLoading(set, "outdated", true);
718
+ setError(set, "outdated", null);
719
+ try {
720
+ const result = await getOutdated();
721
+ set({ outdated: result });
722
+ } catch (err) {
723
+ setError(set, "outdated", err instanceof Error ? err.message : String(err));
724
+ } finally {
725
+ setLoading(set, "outdated", false);
726
+ }
727
+ },
728
+ fetchServices: async () => {
729
+ setLoading(set, "services", true);
730
+ setError(set, "services", null);
731
+ try {
732
+ const result = await getServices();
733
+ set({ services: result });
734
+ } catch (err) {
735
+ setError(set, "services", err instanceof Error ? err.message : String(err));
736
+ } finally {
737
+ setLoading(set, "services", false);
738
+ }
739
+ },
740
+ fetchConfig: async () => {
741
+ setLoading(set, "config", true);
742
+ try {
743
+ const result = await getConfig();
744
+ set({ config: result });
745
+ } catch (err) {
746
+ setError(set, "config", err instanceof Error ? err.message : String(err));
747
+ } finally {
748
+ setLoading(set, "config", false);
749
+ }
750
+ },
751
+ fetchLeaves: async () => {
752
+ try {
753
+ const result = await getLeaves();
754
+ set({ leaves: result });
755
+ } catch (err) {
756
+ if (process.env.NODE_ENV !== "production") {
757
+ console.error("[brew-store] fetchLeaves failed:", err instanceof Error ? err.message : String(err));
758
+ }
759
+ }
760
+ },
761
+ fetchDoctor: async () => {
762
+ setLoading(set, "doctor", true);
763
+ setError(set, "doctor", null);
764
+ try {
765
+ const result = await getDoctor();
766
+ set({ doctorWarnings: result.warnings, doctorClean: result.isClean });
767
+ } catch (err) {
768
+ setError(set, "doctor", err instanceof Error ? err.message : String(err));
769
+ } finally {
770
+ setLoading(set, "doctor", false);
771
+ }
772
+ },
773
+ fetchAll: async () => {
774
+ try {
775
+ await brewUpdate();
776
+ } catch {
777
+ }
778
+ const store = useBrewStore.getState();
779
+ await Promise.all([
780
+ store.fetchInstalled(),
781
+ store.fetchOutdated(),
782
+ store.fetchServices(),
783
+ store.fetchConfig(),
784
+ store.fetchLeaves()
785
+ ]);
786
+ },
787
+ uninstallPackage: async (name) => {
788
+ setLoading(set, "action", true);
789
+ try {
790
+ await uninstallPackage(name);
791
+ await useBrewStore.getState().fetchInstalled();
792
+ } catch (err) {
793
+ setError(set, "action", err instanceof Error ? err.message : String(err));
794
+ } finally {
795
+ setLoading(set, "action", false);
796
+ }
797
+ },
798
+ serviceAction: async (name, action) => {
799
+ setLoading(set, "service-action", true);
800
+ try {
801
+ await serviceAction(name, action);
802
+ await useBrewStore.getState().fetchServices();
803
+ } catch (err) {
804
+ setError(set, "service-action", err instanceof Error ? err.message : String(err));
805
+ } finally {
806
+ setLoading(set, "service-action", false);
807
+ }
808
+ }
809
+ }));
810
+
811
+ // src/components/common/stat-card.tsx
812
+ import { Box as Box5, Text as Text5 } from "ink";
813
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
814
+ function StatCard({ label, value, color = "white" }) {
815
+ return /* @__PURE__ */ jsxs5(
816
+ Box5,
817
+ {
818
+ borderStyle: "round",
819
+ borderColor: color,
820
+ paddingX: 2,
821
+ paddingY: 1,
822
+ flexDirection: "column",
823
+ alignItems: "center",
824
+ minWidth: 16,
825
+ children: [
826
+ /* @__PURE__ */ jsx6(Text5, { bold: true, color, children: value }),
827
+ /* @__PURE__ */ jsx6(Text5, { color: "#9CA3AF", children: label })
828
+ ]
829
+ }
830
+ );
831
+ }
832
+
833
+ // src/components/common/loading.tsx
834
+ import { Box as Box6, Text as Text6 } from "ink";
835
+ import { Spinner } from "@inkjs/ui";
836
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
837
+ function Loading({ message }) {
838
+ useLocaleStore((s) => s.locale);
839
+ return /* @__PURE__ */ jsx7(Box6, { paddingY: 1, children: /* @__PURE__ */ jsx7(Spinner, { label: message ?? t("loading_default") }) });
840
+ }
841
+ function ErrorMessage({ message }) {
842
+ useLocaleStore((s) => s.locale);
843
+ return /* @__PURE__ */ jsxs6(Box6, { paddingY: 1, children: [
844
+ /* @__PURE__ */ jsxs6(Text6, { color: "#EF4444", bold: true, children: [
845
+ "\u2718",
846
+ " ",
847
+ t("error_prefix")
848
+ ] }),
849
+ /* @__PURE__ */ jsx7(Text6, { color: "#EF4444", children: message })
850
+ ] });
851
+ }
852
+
853
+ // src/components/common/status-badge.tsx
854
+ import { Text as Text7 } from "ink";
855
+ import { jsxs as jsxs7 } from "react/jsx-runtime";
856
+ var BADGE_STYLES = {
857
+ success: { icon: "\u2714", color: "#22C55E" },
858
+ warning: { icon: "\u25CF", color: "#F59E0B" },
859
+ error: { icon: "\u2718", color: "#EF4444" },
860
+ info: { icon: "\u25C6", color: "#3B82F6" },
861
+ muted: { icon: "\u25CB", color: "#6B7280" }
862
+ };
863
+ function StatusBadge({ label, variant }) {
864
+ const { icon, color } = BADGE_STYLES[variant];
865
+ return /* @__PURE__ */ jsxs7(Text7, { color, children: [
866
+ icon,
867
+ " ",
868
+ label
869
+ ] });
870
+ }
871
+
872
+ // src/components/common/section-header.tsx
873
+ import { Box as Box7, Text as Text8 } from "ink";
874
+ import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
875
+ function SectionHeader({ emoji, title, color = "#FFD700", gradient, count }) {
876
+ return /* @__PURE__ */ jsxs8(Box7, { gap: 1, children: [
877
+ /* @__PURE__ */ jsx8(Text8, { children: emoji }),
878
+ gradient ? /* @__PURE__ */ jsx8(GradientText, { colors: gradient, bold: true, children: title }) : /* @__PURE__ */ jsx8(Text8, { bold: true, color, children: title }),
879
+ count !== void 0 && /* @__PURE__ */ jsxs8(Text8, { color: "#6B7280", children: [
880
+ "(",
881
+ count,
882
+ ")"
883
+ ] })
884
+ ] });
885
+ }
886
+
887
+ // src/components/common/version-arrow.tsx
888
+ import { Text as Text9 } from "ink";
889
+ import { Fragment as Fragment3, jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
890
+ function VersionArrow({ current, latest }) {
891
+ return /* @__PURE__ */ jsxs9(Fragment3, { children: [
892
+ /* @__PURE__ */ jsx9(Text9, { color: "#EF4444", children: current }),
893
+ /* @__PURE__ */ jsx9(Text9, { color: "#F59E0B", children: " \u2500\u2500 " }),
894
+ /* @__PURE__ */ jsx9(Text9, { color: "#FFD700", children: "\u25B6" }),
895
+ /* @__PURE__ */ jsxs9(Text9, { color: "#2DD4BF", children: [
896
+ " ",
897
+ latest
898
+ ] })
899
+ ] });
900
+ }
901
+
902
+ // src/views/dashboard.tsx
903
+ import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
904
+ function DashboardView() {
905
+ const { formulae, casks, outdated, services, config, loading, errors, fetchAll } = useBrewStore();
906
+ useEffect(() => {
907
+ fetchAll();
908
+ }, []);
909
+ const errorServiceList = useMemo(
910
+ () => services.filter((s) => s.status === "error"),
911
+ [services]
912
+ );
913
+ const runningServices = useMemo(
914
+ () => services.filter((s) => s.status === "started").length,
915
+ [services]
916
+ );
917
+ const errorServices = errorServiceList.length;
918
+ if (loading.installed) return /* @__PURE__ */ jsx10(Loading, { message: t("loading_fetchingBrew") });
919
+ if (errors.installed) return /* @__PURE__ */ jsx10(ErrorMessage, { message: errors.installed });
920
+ return /* @__PURE__ */ jsxs10(Box8, { flexDirection: "column", gap: 2, children: [
921
+ /* @__PURE__ */ jsx10(SectionHeader, { emoji: "\u{1F4CA}", title: t("dashboard_overview"), gradient: GRADIENTS.gold }),
922
+ /* @__PURE__ */ jsxs10(Box8, { gap: 1, flexWrap: "wrap", children: [
923
+ /* @__PURE__ */ jsx10(StatCard, { label: t("dashboard_formulae"), value: formulae.length, color: "#06B6D4" }),
924
+ /* @__PURE__ */ jsx10(StatCard, { label: t("dashboard_casks"), value: casks.length, color: "#A855F7" }),
925
+ /* @__PURE__ */ jsx10(
926
+ StatCard,
927
+ {
928
+ label: t("dashboard_outdated"),
929
+ value: outdated.formulae.length + outdated.casks.length,
930
+ color: outdated.formulae.length + outdated.casks.length > 0 ? "#F59E0B" : "#22C55E"
931
+ }
932
+ ),
933
+ /* @__PURE__ */ jsx10(
934
+ StatCard,
935
+ {
936
+ label: t("dashboard_services"),
937
+ value: `${runningServices}/${services.length}`,
938
+ color: errorServices > 0 ? "#EF4444" : "#22C55E"
939
+ }
940
+ )
941
+ ] }),
942
+ config && /* @__PURE__ */ jsxs10(Box8, { flexDirection: "column", children: [
943
+ /* @__PURE__ */ jsx10(SectionHeader, { emoji: "\u2139\uFE0F", title: t("dashboard_systemInfo"), gradient: ["#F9FAFB", "#9CA3AF"] }),
944
+ /* @__PURE__ */ jsxs10(Box8, { borderStyle: "round", borderColor: "#3B82F6", paddingX: 2, paddingY: 0, flexDirection: "column", marginTop: 1, children: [
945
+ /* @__PURE__ */ jsxs10(Text10, { children: [
946
+ /* @__PURE__ */ jsx10(Text10, { color: "#9CA3AF", children: t("dashboard_homebrew") }),
947
+ " ",
948
+ config.HOMEBREW_VERSION
949
+ ] }),
950
+ /* @__PURE__ */ jsxs10(Text10, { children: [
951
+ /* @__PURE__ */ jsx10(Text10, { color: "#9CA3AF", children: t("dashboard_prefix") }),
952
+ " ",
953
+ config.HOMEBREW_PREFIX
954
+ ] }),
955
+ /* @__PURE__ */ jsxs10(Text10, { children: [
956
+ /* @__PURE__ */ jsx10(Text10, { color: "#9CA3AF", children: t("dashboard_updated") }),
957
+ " ",
958
+ config.coreUpdated
959
+ ] })
960
+ ] })
961
+ ] }),
962
+ outdated.formulae.length > 0 && /* @__PURE__ */ jsxs10(Box8, { flexDirection: "column", marginTop: 1, children: [
963
+ /* @__PURE__ */ jsx10(SectionHeader, { emoji: "\u{1F4E6}", title: t("dashboard_outdatedPackages"), gradient: GRADIENTS.fire }),
964
+ /* @__PURE__ */ jsxs10(Box8, { paddingLeft: 2, flexDirection: "column", children: [
965
+ outdated.formulae.slice(0, 10).map((pkg) => /* @__PURE__ */ jsxs10(Box8, { gap: 1, children: [
966
+ /* @__PURE__ */ jsx10(Text10, { color: "#F9FAFB", children: pkg.name }),
967
+ /* @__PURE__ */ jsx10(VersionArrow, { current: pkg.installed_versions[0] ?? "", latest: pkg.current_version })
968
+ ] }, pkg.name)),
969
+ outdated.formulae.length > 10 && /* @__PURE__ */ jsx10(Text10, { color: "#6B7280", italic: true, children: t("common_andMore", { count: outdated.formulae.length - 10 }) })
970
+ ] })
971
+ ] }),
972
+ errorServices > 0 && /* @__PURE__ */ jsxs10(Box8, { flexDirection: "column", marginTop: 1, children: [
973
+ /* @__PURE__ */ jsx10(SectionHeader, { emoji: "\u26A0\uFE0F", title: t("dashboard_serviceErrors"), color: "#EF4444" }),
974
+ /* @__PURE__ */ jsx10(Box8, { paddingLeft: 2, flexDirection: "column", children: errorServiceList.map((s) => /* @__PURE__ */ jsxs10(Box8, { gap: 1, children: [
975
+ /* @__PURE__ */ jsx10(StatusBadge, { label: t("badge_error"), variant: "error" }),
976
+ /* @__PURE__ */ jsx10(Text10, { children: s.name }),
977
+ s.exit_code != null && /* @__PURE__ */ jsx10(Text10, { color: "#9CA3AF", children: t("common_exit", { code: s.exit_code }) })
978
+ ] }, s.name)) })
979
+ ] })
980
+ ] });
981
+ }
982
+
983
+ // src/views/installed.tsx
984
+ import { useState as useState3, useMemo as useMemo2, useEffect as useEffect5 } from "react";
985
+ import { Box as Box12, Text as Text14, useInput as useInput3 } from "ink";
986
+
987
+ // src/hooks/use-debounce.ts
988
+ import { useState, useEffect as useEffect2 } from "react";
989
+ function useDebounce(value, delayMs) {
990
+ const [debounced, setDebounced] = useState(value);
991
+ useEffect2(() => {
992
+ const timer = setTimeout(() => setDebounced(value), delayMs);
993
+ return () => clearTimeout(timer);
994
+ }, [value, delayMs]);
995
+ return debounced;
996
+ }
997
+
998
+ // src/hooks/use-brew-stream.ts
999
+ import { useState as useState2, useCallback, useRef, useEffect as useEffect3 } from "react";
1000
+ var MAX_LINES = 100;
1001
+ function detectAction(args) {
1002
+ const cmd = args[0];
1003
+ if (cmd === "install") return { action: "install", packageName: args[1] ?? null };
1004
+ if (cmd === "uninstall") {
1005
+ const name = args.find((a) => !a.startsWith("-")) === "uninstall" ? args.find((a, i) => i > 0 && !a.startsWith("-")) ?? null : args[1] ?? null;
1006
+ return { action: "uninstall", packageName: name };
1007
+ }
1008
+ if (cmd === "upgrade") {
1009
+ if (args.length === 1) return { action: "upgrade-all", packageName: null };
1010
+ return { action: "upgrade", packageName: args[1] ?? null };
1011
+ }
1012
+ return null;
1013
+ }
1014
+ async function logToHistory(args, success, error) {
1015
+ const detected = detectAction(args);
1016
+ if (!detected) return;
1017
+ try {
1018
+ const { appendEntry: appendEntry2 } = await import("./history-logger-LQT622M2.js");
1019
+ await appendEntry2(detected.action, detected.packageName, success, error);
1020
+ } catch {
1021
+ }
1022
+ }
1023
+ function useBrewStream() {
1024
+ const [lines, setLines] = useState2([]);
1025
+ const [isRunning, setIsRunning] = useState2(false);
1026
+ const [error, setError2] = useState2(null);
1027
+ const cancelRef = useRef(false);
1028
+ const generatorRef = useRef(null);
1029
+ const mountedRef = useRef(true);
1030
+ useEffect3(() => {
1031
+ mountedRef.current = true;
1032
+ return () => {
1033
+ mountedRef.current = false;
1034
+ generatorRef.current?.return(void 0);
1035
+ generatorRef.current = null;
1036
+ };
1037
+ }, []);
1038
+ const run = useCallback(async (args) => {
1039
+ if (!mountedRef.current) return;
1040
+ setIsRunning(true);
1041
+ setError2(null);
1042
+ setLines([]);
1043
+ cancelRef.current = false;
1044
+ const gen = streamBrew(args);
1045
+ generatorRef.current = gen;
1046
+ let streamError = null;
1047
+ try {
1048
+ for await (const line of gen) {
1049
+ if (cancelRef.current) break;
1050
+ if (mountedRef.current) {
1051
+ setLines((prev) => [...prev.slice(-(MAX_LINES - 1)), line]);
1052
+ }
1053
+ }
1054
+ } catch (err) {
1055
+ streamError = err instanceof Error ? err.message : String(err);
1056
+ if (!cancelRef.current && mountedRef.current) {
1057
+ setError2(streamError);
1058
+ }
1059
+ } finally {
1060
+ generatorRef.current = null;
1061
+ if (mountedRef.current) {
1062
+ setIsRunning(false);
1063
+ }
1064
+ }
1065
+ if (!cancelRef.current) {
1066
+ void logToHistory(args, streamError === null, streamError);
1067
+ }
1068
+ }, []);
1069
+ const cancel = useCallback(() => {
1070
+ cancelRef.current = true;
1071
+ generatorRef.current?.return(void 0);
1072
+ generatorRef.current = null;
1073
+ }, []);
1074
+ const clear = useCallback(() => {
1075
+ setLines([]);
1076
+ setError2(null);
1077
+ }, []);
1078
+ return { lines, isRunning, error, run, cancel, clear };
1079
+ }
1080
+
1081
+ // src/components/common/search-input.tsx
1082
+ import { Box as Box9, Text as Text11 } from "ink";
1083
+ import { TextInput } from "@inkjs/ui";
1084
+ import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
1085
+ function SearchInput({ defaultValue, onChange, placeholder, isActive = true }) {
1086
+ const resolvedPlaceholder = placeholder ?? t("searchInput_placeholder");
1087
+ return /* @__PURE__ */ jsxs11(Box9, { children: [
1088
+ /* @__PURE__ */ jsxs11(Text11, { color: "#FFD700", children: [
1089
+ "\u{1F50D}",
1090
+ " "
1091
+ ] }),
1092
+ isActive ? /* @__PURE__ */ jsx11(
1093
+ TextInput,
1094
+ {
1095
+ placeholder: resolvedPlaceholder,
1096
+ defaultValue,
1097
+ onChange
1098
+ }
1099
+ ) : /* @__PURE__ */ jsx11(Text11, { color: "#6B7280", children: defaultValue || placeholder })
1100
+ ] });
1101
+ }
1102
+
1103
+ // src/components/common/confirm-dialog.tsx
1104
+ import { useEffect as useEffect4 } from "react";
1105
+ import { Box as Box10, Text as Text12, useInput as useInput2 } from "ink";
1106
+ import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
1107
+ function ConfirmDialog({ message, onConfirm, onCancel }) {
1108
+ const locale = useLocaleStore((s) => s.locale);
1109
+ const { openModal, closeModal } = useModalStore();
1110
+ useEffect4(() => {
1111
+ openModal();
1112
+ return () => {
1113
+ closeModal();
1114
+ };
1115
+ }, []);
1116
+ useInput2((input, key) => {
1117
+ if (input === "y" || input === "Y") onConfirm();
1118
+ else if (locale === "es" && (input === "s" || input === "S")) onConfirm();
1119
+ else if (input === "n" || input === "N") onCancel();
1120
+ else if (key.escape) onCancel();
1121
+ });
1122
+ return /* @__PURE__ */ jsxs12(Box10, { borderStyle: "double", borderColor: "#A855F7", paddingX: 2, paddingY: 1, flexDirection: "column", children: [
1123
+ /* @__PURE__ */ jsx12(Text12, { bold: true, color: "#F9FAFB", children: message }),
1124
+ /* @__PURE__ */ jsxs12(Box10, { marginTop: 1, children: [
1125
+ /* @__PURE__ */ jsx12(Text12, { color: "#22C55E", children: t("confirm_yes") }),
1126
+ /* @__PURE__ */ jsx12(Text12, { children: " / " }),
1127
+ /* @__PURE__ */ jsx12(Text12, { color: "#EF4444", children: t("confirm_no") })
1128
+ ] })
1129
+ ] });
1130
+ }
1131
+
1132
+ // src/components/common/progress-log.tsx
1133
+ import { Box as Box11, Text as Text13 } from "ink";
1134
+ import { Spinner as Spinner2 } from "@inkjs/ui";
1135
+ import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
1136
+ function ProgressLog({ lines, isRunning, title, maxVisible = 15 }) {
1137
+ const visible = lines.slice(-maxVisible);
1138
+ return /* @__PURE__ */ jsxs13(Box11, { flexDirection: "column", borderStyle: "round", borderColor: "#38BDF8", paddingX: 1, children: [
1139
+ title && /* @__PURE__ */ jsxs13(Box11, { marginBottom: 1, children: [
1140
+ isRunning && /* @__PURE__ */ jsx13(Spinner2, { label: "" }),
1141
+ /* @__PURE__ */ jsxs13(Text13, { bold: true, color: "#38BDF8", children: [
1142
+ " ",
1143
+ title
1144
+ ] })
1145
+ ] }),
1146
+ visible.map((line, i) => /* @__PURE__ */ jsx13(Text13, { color: "#9CA3AF", wrap: "wrap", children: line }, i)),
1147
+ lines.length === 0 && !isRunning && /* @__PURE__ */ jsx13(Text13, { color: "#6B7280", italic: true, children: t("progress_noOutput") })
1148
+ ] });
1149
+ }
1150
+
1151
+ // src/utils/format.ts
1152
+ function formatBytes(bytes) {
1153
+ if (!isFinite(bytes) || bytes <= 0) return "0 B";
1154
+ const units = ["B", "KB", "MB", "GB", "TB"];
1155
+ const i = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1);
1156
+ return `${(bytes / Math.pow(1024, i)).toFixed(1)} ${units[i]}`;
1157
+ }
1158
+ function formatRelativeTime(timestamp) {
1159
+ if (!timestamp || !isFinite(timestamp)) return t("time_justNow");
1160
+ const diff = Date.now() / 1e3 - timestamp;
1161
+ if (diff < 0) return t("time_justNow");
1162
+ if (diff < 60) return t("time_justNow");
1163
+ if (diff < 3600) return t("time_minutesAgo", { n: Math.floor(diff / 60) });
1164
+ if (diff < 86400) return t("time_hoursAgo", { n: Math.floor(diff / 3600) });
1165
+ if (diff < 2592e3) return t("time_daysAgo", { n: Math.floor(diff / 86400) });
1166
+ return t("time_monthsAgo", { n: Math.floor(diff / 2592e3) });
1167
+ }
1168
+ function truncate(str, maxLen) {
1169
+ if (str.length <= maxLen) return str;
1170
+ return str.slice(0, maxLen - 1) + "\u2026";
1171
+ }
1172
+
1173
+ // src/views/installed.tsx
1174
+ import { jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
1175
+ function InstalledView() {
1176
+ const { formulae, casks, loading, errors, fetchInstalled } = useBrewStore();
1177
+ const navigate = useNavigationStore((s) => s.navigate);
1178
+ const selectPackage = useNavigationStore((s) => s.selectPackage);
1179
+ const [filter, setFilter] = useState3("");
1180
+ const [cursor, setCursor] = useState3(0);
1181
+ const [tab, setTab] = useState3("formulae");
1182
+ const [isSearching, setIsSearching] = useState3(false);
1183
+ const [confirmUninstall, setConfirmUninstall] = useState3(null);
1184
+ const debouncedFilter = useDebounce(filter, 200);
1185
+ const stream = useBrewStream();
1186
+ const { openModal, closeModal } = useModalStore();
1187
+ useEffect5(() => {
1188
+ fetchInstalled();
1189
+ }, []);
1190
+ useEffect5(() => {
1191
+ if (isSearching) {
1192
+ openModal();
1193
+ return () => {
1194
+ closeModal();
1195
+ };
1196
+ }
1197
+ return void 0;
1198
+ }, [isSearching]);
1199
+ const allItems = useMemo2(() => {
1200
+ const items = tab === "formulae" ? formulaeToListItems(formulae) : casksToListItems(casks);
1201
+ if (!debouncedFilter) return items;
1202
+ const lower = debouncedFilter.toLowerCase();
1203
+ return items.filter(
1204
+ (p) => p.name.toLowerCase().includes(lower) || p.desc.toLowerCase().includes(lower)
1205
+ );
1206
+ }, [formulae, casks, tab, debouncedFilter]);
1207
+ useInput3((input, key) => {
1208
+ if (confirmUninstall || stream.isRunning) return;
1209
+ if (isSearching) {
1210
+ if (key.escape) {
1211
+ setIsSearching(false);
1212
+ setFilter("");
1213
+ }
1214
+ return;
1215
+ }
1216
+ if (input === "/") {
1217
+ setIsSearching(true);
1218
+ return;
1219
+ }
1220
+ if (input === "u" && allItems[cursor]) {
1221
+ setConfirmUninstall(allItems[cursor].name);
1222
+ return;
1223
+ }
1224
+ if (input === "j" || key.downArrow) {
1225
+ setCursor((c) => Math.min(c + 1, Math.max(0, allItems.length - 1)));
1226
+ } else if (input === "k" || key.upArrow) {
1227
+ setCursor((c) => Math.max(c - 1, 0));
1228
+ } else if (input === "g") {
1229
+ setCursor(0);
1230
+ } else if (input === "G") {
1231
+ setCursor(Math.max(allItems.length - 1, 0));
1232
+ } else if (key.return && allItems[cursor]) {
1233
+ selectPackage(allItems[cursor].name);
1234
+ navigate("package-info");
1235
+ } else if (input === "f") {
1236
+ setTab((t2) => t2 === "formulae" ? "casks" : "formulae");
1237
+ setCursor(0);
1238
+ }
1239
+ }, { isActive: true });
1240
+ if (loading.installed) return /* @__PURE__ */ jsx14(Loading, { message: t("loading_installed") });
1241
+ if (errors.installed) return /* @__PURE__ */ jsx14(ErrorMessage, { message: errors.installed });
1242
+ if (stream.isRunning || stream.lines.length > 0) {
1243
+ return /* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", children: [
1244
+ /* @__PURE__ */ jsx14(
1245
+ ProgressLog,
1246
+ {
1247
+ lines: stream.lines,
1248
+ isRunning: stream.isRunning,
1249
+ title: t("pkgInfo_uninstalling", { name: "..." })
1250
+ }
1251
+ ),
1252
+ !stream.isRunning && /* @__PURE__ */ jsx14(Box12, { marginTop: 1, children: /* @__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")}` }) }) })
1253
+ ] });
1254
+ }
1255
+ const MAX_VISIBLE_ROWS = 20;
1256
+ const start = Math.max(0, cursor - Math.floor(MAX_VISIBLE_ROWS / 2));
1257
+ const visible = allItems.slice(start, start + MAX_VISIBLE_ROWS);
1258
+ return /* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", children: [
1259
+ /* @__PURE__ */ jsxs14(Box12, { marginBottom: 1, gap: 1, children: [
1260
+ /* @__PURE__ */ jsx14(
1261
+ Box12,
1262
+ {
1263
+ borderStyle: "round",
1264
+ borderColor: tab === "formulae" ? "#06B6D4" : "#6B7280",
1265
+ paddingX: 1,
1266
+ children: /* @__PURE__ */ jsxs14(Text14, { bold: tab === "formulae", color: tab === "formulae" ? "#06B6D4" : "#6B7280", children: [
1267
+ "\u{1F4E6}",
1268
+ " ",
1269
+ t("installed_formulaeCount", { count: formulae.length })
1270
+ ] })
1271
+ }
1272
+ ),
1273
+ /* @__PURE__ */ jsx14(
1274
+ Box12,
1275
+ {
1276
+ borderStyle: "round",
1277
+ borderColor: tab === "casks" ? "#A855F7" : "#6B7280",
1278
+ paddingX: 1,
1279
+ children: /* @__PURE__ */ jsxs14(Text14, { bold: tab === "casks", color: tab === "casks" ? "#A855F7" : "#6B7280", children: [
1280
+ "\u{1F37A}",
1281
+ " ",
1282
+ t("installed_casksCount", { count: casks.length })
1283
+ ] })
1284
+ }
1285
+ )
1286
+ ] }),
1287
+ confirmUninstall && /* @__PURE__ */ jsx14(
1288
+ ConfirmDialog,
1289
+ {
1290
+ message: t("installed_confirmUninstall", { name: confirmUninstall }),
1291
+ onConfirm: () => {
1292
+ const name = confirmUninstall;
1293
+ setConfirmUninstall(null);
1294
+ void stream.run(["uninstall", name]).then(() => {
1295
+ fetchInstalled();
1296
+ });
1297
+ },
1298
+ onCancel: () => setConfirmUninstall(null)
1299
+ }
1300
+ ),
1301
+ isSearching && /* @__PURE__ */ jsx14(Box12, { marginBottom: 1, borderStyle: "round", borderColor: "#FFD700", paddingX: 1, children: /* @__PURE__ */ jsx14(SearchInput, { defaultValue: filter, onChange: setFilter, isActive: isSearching }) }),
1302
+ /* @__PURE__ */ jsxs14(Box12, { gap: 1, borderStyle: "single", borderBottom: true, borderTop: false, borderLeft: false, borderRight: false, borderColor: "#4B5563", children: [
1303
+ /* @__PURE__ */ jsxs14(Text14, { color: "#F9FAFB", bold: true, children: [
1304
+ " ",
1305
+ "Package".padEnd(27)
1306
+ ] }),
1307
+ /* @__PURE__ */ jsx14(Text14, { color: "#F9FAFB", bold: true, children: "Version".padEnd(12) }),
1308
+ /* @__PURE__ */ jsx14(Text14, { color: "#F9FAFB", bold: true, children: "Status" })
1309
+ ] }),
1310
+ /* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", children: [
1311
+ visible.length === 0 && /* @__PURE__ */ jsx14(Box12, { paddingY: 1, justifyContent: "center", children: /* @__PURE__ */ jsx14(Text14, { color: "#6B7280", italic: true, children: t("installed_noPackages") }) }),
1312
+ start > 0 && /* @__PURE__ */ jsxs14(Text14, { color: "#6B7280", dimColor: true, children: [
1313
+ " ",
1314
+ t("scroll_moreAbove", { count: start })
1315
+ ] }),
1316
+ visible.map((item, i) => {
1317
+ const idx = start + i;
1318
+ const isCurrent = idx === cursor;
1319
+ return /* @__PURE__ */ jsxs14(Box12, { gap: 1, children: [
1320
+ /* @__PURE__ */ jsx14(Text14, { color: isCurrent ? "#22C55E" : "#9CA3AF", children: isCurrent ? "\u25B6" : " " }),
1321
+ /* @__PURE__ */ jsx14(Text14, { bold: isCurrent, inverse: isCurrent, color: isCurrent ? "#F9FAFB" : "#9CA3AF", children: truncate(item.name, 27).padEnd(27) }),
1322
+ /* @__PURE__ */ jsx14(Text14, { color: "#2DD4BF", children: item.version.padEnd(12) }),
1323
+ item.outdated && /* @__PURE__ */ jsx14(StatusBadge, { label: t("badge_outdated"), variant: "warning" }),
1324
+ item.pinned && /* @__PURE__ */ jsx14(StatusBadge, { label: t("badge_pinned"), variant: "info" }),
1325
+ item.kegOnly && /* @__PURE__ */ jsx14(StatusBadge, { label: t("badge_kegOnly"), variant: "muted" }),
1326
+ item.installedAsDependency && /* @__PURE__ */ jsx14(StatusBadge, { label: t("badge_dep"), variant: "muted" }),
1327
+ !item.outdated && !item.pinned && !item.kegOnly && !item.installedAsDependency && /* @__PURE__ */ jsx14(Text14, { color: "#6B7280", dimColor: true, children: truncate(item.desc, 30) })
1328
+ ] }, item.name);
1329
+ }),
1330
+ start + MAX_VISIBLE_ROWS < allItems.length && /* @__PURE__ */ jsxs14(Text14, { color: "#6B7280", dimColor: true, children: [
1331
+ " ",
1332
+ t("scroll_moreBelow", { count: allItems.length - start - MAX_VISIBLE_ROWS })
1333
+ ] })
1334
+ ] }),
1335
+ /* @__PURE__ */ jsx14(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx14(Text14, { color: "#F9FAFB", bold: true, children: allItems.length > 0 ? `${cursor + 1}/${allItems.length}` : "0/0" }) })
1336
+ ] });
1337
+ }
1338
+
1339
+ // src/views/search.tsx
1340
+ import { useState as useState4, useCallback as useCallback2, useEffect as useEffect6, useRef as useRef2 } from "react";
1341
+ import { Box as Box13, Text as Text15, useInput as useInput4 } from "ink";
1342
+ import { TextInput as TextInput2 } from "@inkjs/ui";
1343
+ import { jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
1344
+ function SearchView() {
1345
+ const [query, setQuery] = useState4("");
1346
+ const [results, setResults] = useState4(null);
1347
+ const [searching, setSearching] = useState4(false);
1348
+ const [cursor, setCursor] = useState4(0);
1349
+ const [confirmInstall, setConfirmInstall] = useState4(null);
1350
+ const stream = useBrewStream();
1351
+ const { openModal, closeModal } = useModalStore();
1352
+ const navigate = useNavigationStore((s) => s.navigate);
1353
+ const selectPackage = useNavigationStore((s) => s.selectPackage);
1354
+ const fetchInstalled = useBrewStore((s) => s.fetchInstalled);
1355
+ const hasRefreshed = useRef2(false);
1356
+ useEffect6(() => {
1357
+ if (results !== null) {
1358
+ openModal();
1359
+ return () => {
1360
+ closeModal();
1361
+ };
1362
+ }
1363
+ return void 0;
1364
+ }, [results]);
1365
+ const doSearch = useCallback2(async (term) => {
1366
+ if (term.length < 2) return;
1367
+ setSearching(true);
1368
+ try {
1369
+ const r = await search(term);
1370
+ setResults(r);
1371
+ setCursor(0);
1372
+ } catch (err) {
1373
+ setResults({ formulae: [], casks: [] });
1374
+ void err;
1375
+ } finally {
1376
+ setSearching(false);
1377
+ }
1378
+ }, []);
1379
+ useEffect6(() => {
1380
+ if (!stream.isRunning && !stream.error && stream.lines.length > 0 && !hasRefreshed.current) {
1381
+ hasRefreshed.current = true;
1382
+ void fetchInstalled();
1383
+ }
1384
+ }, [stream.isRunning, stream.error]);
1385
+ const allResults = results ? [...results.formulae, ...results.casks] : [];
1386
+ useInput4((input, key) => {
1387
+ if (stream.isRunning) {
1388
+ if (key.escape) stream.cancel();
1389
+ return;
1390
+ }
1391
+ if (confirmInstall) return;
1392
+ if (key.return && !results) {
1393
+ void doSearch(query);
1394
+ return;
1395
+ }
1396
+ if (key.return && allResults[cursor]) {
1397
+ selectPackage(allResults[cursor]);
1398
+ navigate("package-info");
1399
+ return;
1400
+ }
1401
+ if (input === "i" && allResults[cursor]) {
1402
+ setConfirmInstall(allResults[cursor]);
1403
+ return;
1404
+ }
1405
+ if (input === "j" || key.downArrow) {
1406
+ setCursor((c) => Math.min(c + 1, Math.max(0, allResults.length - 1)));
1407
+ } else if (input === "k" || key.upArrow) {
1408
+ setCursor((c) => Math.max(c - 1, 0));
1409
+ } else if (key.escape) {
1410
+ setResults(null);
1411
+ setQuery("");
1412
+ }
1413
+ });
1414
+ if (stream.isRunning || stream.lines.length > 0) {
1415
+ return /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", children: [
1416
+ /* @__PURE__ */ jsx15(
1417
+ ProgressLog,
1418
+ {
1419
+ lines: stream.lines,
1420
+ isRunning: stream.isRunning,
1421
+ title: t("search_installing")
1422
+ }
1423
+ ),
1424
+ stream.isRunning && /* @__PURE__ */ jsxs15(Text15, { color: "#6B7280", children: [
1425
+ "esc:",
1426
+ t("hint_cancel")
1427
+ ] }),
1428
+ !stream.isRunning && /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", marginTop: 1, children: [
1429
+ /* @__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")}` }) }),
1430
+ /* @__PURE__ */ jsxs15(Text15, { color: "#6B7280", children: [
1431
+ "esc:",
1432
+ t("hint_back")
1433
+ ] })
1434
+ ] })
1435
+ ] });
1436
+ }
1437
+ return /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", children: [
1438
+ /* @__PURE__ */ jsxs15(Box13, { marginBottom: 1, children: [
1439
+ /* @__PURE__ */ jsxs15(Text15, { color: "#FFD700", children: [
1440
+ "\u{1F50D}",
1441
+ " "
1442
+ ] }),
1443
+ !results ? /* @__PURE__ */ jsx15(
1444
+ TextInput2,
1445
+ {
1446
+ placeholder: t("search_placeholder"),
1447
+ defaultValue: query,
1448
+ onChange: setQuery,
1449
+ onSubmit: () => void doSearch(query)
1450
+ }
1451
+ ) : /* @__PURE__ */ jsxs15(Text15, { children: [
1452
+ t("search_resultsFor"),
1453
+ ' "',
1454
+ /* @__PURE__ */ jsx15(Text15, { bold: true, color: "#F9FAFB", children: query }),
1455
+ '" ',
1456
+ /* @__PURE__ */ jsx15(Text15, { color: "#6B7280", children: t("search_escToClear") })
1457
+ ] })
1458
+ ] }),
1459
+ searching && /* @__PURE__ */ jsx15(Loading, { message: t("loading_searching") }),
1460
+ confirmInstall && /* @__PURE__ */ jsx15(
1461
+ ConfirmDialog,
1462
+ {
1463
+ message: t("search_confirmInstall", { name: confirmInstall }),
1464
+ onConfirm: () => {
1465
+ const name = confirmInstall;
1466
+ hasRefreshed.current = false;
1467
+ setConfirmInstall(null);
1468
+ void stream.run(["install", name]);
1469
+ },
1470
+ onCancel: () => setConfirmInstall(null)
1471
+ }
1472
+ ),
1473
+ results && !searching && !confirmInstall && /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", children: [
1474
+ results.formulae.length > 0 && /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", marginBottom: 1, children: [
1475
+ /* @__PURE__ */ jsx15(Text15, { bold: true, color: "#06B6D4", children: t("search_formulaeHeader", { count: results.formulae.length }) }),
1476
+ results.formulae.slice(0, 20).map((name, i) => {
1477
+ const isCurrent = i === cursor;
1478
+ return /* @__PURE__ */ jsxs15(Box13, { gap: 1, children: [
1479
+ /* @__PURE__ */ jsx15(Text15, { color: isCurrent ? "#22C55E" : "#9CA3AF", children: isCurrent ? "\u25B6" : " " }),
1480
+ /* @__PURE__ */ jsx15(Text15, { bold: isCurrent, inverse: isCurrent, children: name })
1481
+ ] }, name);
1482
+ }),
1483
+ results.formulae.length > 20 && /* @__PURE__ */ jsxs15(Text15, { color: "#6B7280", dimColor: true, children: [
1484
+ " ",
1485
+ t("scroll_moreBelow", { count: results.formulae.length - 20 })
1486
+ ] })
1487
+ ] }),
1488
+ results.casks.length > 0 && /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", children: [
1489
+ /* @__PURE__ */ jsx15(Text15, { bold: true, color: "#A855F7", children: t("search_casksHeader", { count: results.casks.length }) }),
1490
+ results.casks.slice(0, 20).map((name, i) => {
1491
+ const idx = results.formulae.length + i;
1492
+ const isCurrent = idx === cursor;
1493
+ return /* @__PURE__ */ jsxs15(Box13, { gap: 1, children: [
1494
+ /* @__PURE__ */ jsx15(Text15, { color: isCurrent ? "#22C55E" : "#9CA3AF", children: isCurrent ? "\u25B6" : " " }),
1495
+ /* @__PURE__ */ jsx15(Text15, { bold: isCurrent, inverse: isCurrent, children: name })
1496
+ ] }, name);
1497
+ }),
1498
+ results.casks.length > 20 && /* @__PURE__ */ jsxs15(Text15, { color: "#6B7280", dimColor: true, children: [
1499
+ " ",
1500
+ t("scroll_moreBelow", { count: results.casks.length - 20 })
1501
+ ] })
1502
+ ] }),
1503
+ allResults.length === 0 && /* @__PURE__ */ jsx15(Box13, { borderStyle: "round", borderColor: "#6B7280", paddingX: 2, children: /* @__PURE__ */ jsx15(Text15, { color: "#6B7280", italic: true, children: t("search_noResults") }) }),
1504
+ /* @__PURE__ */ jsx15(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx15(Text15, { color: "#F9FAFB", bold: true, children: allResults.length > 0 ? `${cursor + 1}/${allResults.length}` : "" }) })
1505
+ ] })
1506
+ ] });
1507
+ }
1508
+
1509
+ // src/views/outdated.tsx
1510
+ import { useEffect as useEffect7, useRef as useRef3, useState as useState5 } from "react";
1511
+ import { Box as Box14, Text as Text16, useInput as useInput5 } from "ink";
1512
+ import { jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
1513
+ function OutdatedView() {
1514
+ const { outdated, loading, errors, fetchOutdated } = useBrewStore();
1515
+ const stream = useBrewStream();
1516
+ const [cursor, setCursor] = useState5(0);
1517
+ const [confirmAction, setConfirmAction] = useState5(null);
1518
+ const hasRefreshed = useRef3(false);
1519
+ useEffect7(() => {
1520
+ fetchOutdated();
1521
+ }, []);
1522
+ useEffect7(() => {
1523
+ if (!stream.isRunning && !stream.error && stream.lines.length > 0 && !hasRefreshed.current) {
1524
+ hasRefreshed.current = true;
1525
+ void fetchOutdated();
1526
+ }
1527
+ }, [stream.isRunning, stream.error]);
1528
+ const allOutdated = [...outdated.formulae, ...outdated.casks];
1529
+ useInput5((input, key) => {
1530
+ if (stream.isRunning) {
1531
+ if (key.escape) stream.cancel();
1532
+ return;
1533
+ }
1534
+ if (confirmAction) return;
1535
+ if (input === "j" || key.downArrow) {
1536
+ setCursor((c) => Math.min(c + 1, Math.max(0, allOutdated.length - 1)));
1537
+ } else if (input === "k" || key.upArrow) {
1538
+ setCursor((c) => Math.max(c - 1, 0));
1539
+ } else if (key.return && allOutdated[cursor]) {
1540
+ setConfirmAction({ type: "single", name: allOutdated[cursor].name });
1541
+ } else if (input === "A" && allOutdated.length > 0) {
1542
+ setConfirmAction({ type: "all" });
1543
+ } else if (input === "p" && allOutdated[cursor]) {
1544
+ const pkg = allOutdated[cursor];
1545
+ void execBrew([pkg.pinned ? "unpin" : "pin", pkg.name]).then(() => void fetchOutdated());
1546
+ return;
1547
+ } else if (input === "r") {
1548
+ void fetchOutdated();
1549
+ }
1550
+ });
1551
+ if (loading.outdated) return /* @__PURE__ */ jsx16(Loading, { message: t("loading_outdated") });
1552
+ if (errors.outdated) return /* @__PURE__ */ jsx16(ErrorMessage, { message: errors.outdated });
1553
+ if (stream.isRunning || stream.lines.length > 0) {
1554
+ return /* @__PURE__ */ jsxs16(Box14, { flexDirection: "column", children: [
1555
+ /* @__PURE__ */ jsx16(
1556
+ ProgressLog,
1557
+ {
1558
+ lines: stream.lines,
1559
+ isRunning: stream.isRunning,
1560
+ title: t("outdated_upgrading")
1561
+ }
1562
+ ),
1563
+ stream.isRunning && /* @__PURE__ */ jsxs16(Text16, { color: "#6B7280", children: [
1564
+ "esc:",
1565
+ t("hint_cancel")
1566
+ ] }),
1567
+ !stream.isRunning && /* @__PURE__ */ jsxs16(Box14, { flexDirection: "column", marginTop: 1, children: [
1568
+ /* @__PURE__ */ jsxs16(Box14, { borderStyle: "round", borderColor: stream.error ? "#EF4444" : "#22C55E", paddingX: 2, paddingY: 0, children: [
1569
+ /* @__PURE__ */ jsx16(Text16, { color: stream.error ? "#EF4444" : "#22C55E", bold: true, children: stream.error ? `\u2718 ${stream.error}` : `\u2714 ${t("outdated_upgradeComplete")}` }),
1570
+ /* @__PURE__ */ jsxs16(Text16, { color: "#9CA3AF", children: [
1571
+ " ",
1572
+ t("outdated_pressRefresh")
1573
+ ] })
1574
+ ] }),
1575
+ /* @__PURE__ */ jsxs16(Text16, { color: "#6B7280", children: [
1576
+ "r:",
1577
+ t("hint_refresh"),
1578
+ " esc:",
1579
+ t("hint_back")
1580
+ ] })
1581
+ ] })
1582
+ ] });
1583
+ }
1584
+ return /* @__PURE__ */ jsxs16(Box14, { flexDirection: "column", children: [
1585
+ /* @__PURE__ */ jsx16(SectionHeader, { emoji: "\u{1F4E6}", title: t("outdated_title", { count: allOutdated.length }), gradient: GRADIENTS.fire }),
1586
+ confirmAction && /* @__PURE__ */ jsx16(Box14, { marginY: 1, children: /* @__PURE__ */ jsx16(
1587
+ ConfirmDialog,
1588
+ {
1589
+ message: confirmAction.type === "all" ? t("outdated_confirmAll", { count: allOutdated.length }) : t("outdated_confirmSingle", { name: confirmAction.type === "single" ? confirmAction.name : "" }),
1590
+ onConfirm: () => {
1591
+ hasRefreshed.current = false;
1592
+ if (confirmAction.type === "all") {
1593
+ void stream.run(["upgrade"]);
1594
+ } else if (confirmAction.name) {
1595
+ void stream.run(["upgrade", confirmAction.name]);
1596
+ }
1597
+ setConfirmAction(null);
1598
+ },
1599
+ onCancel: () => setConfirmAction(null)
1600
+ }
1601
+ ) }),
1602
+ 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: [
1603
+ "\u2714",
1604
+ " ",
1605
+ t("outdated_upToDate")
1606
+ ] }) }) }),
1607
+ allOutdated.length > 0 && !confirmAction && /* @__PURE__ */ jsxs16(Box14, { flexDirection: "column", marginTop: 1, children: [
1608
+ allOutdated.map((pkg, i) => {
1609
+ const isCurrent = i === cursor;
1610
+ return /* @__PURE__ */ jsxs16(Box14, { gap: 1, children: [
1611
+ /* @__PURE__ */ jsx16(Text16, { color: isCurrent ? "#22C55E" : "#9CA3AF", children: isCurrent ? "\u25B6" : " " }),
1612
+ /* @__PURE__ */ jsx16(Text16, { bold: isCurrent, inverse: isCurrent, color: isCurrent ? "#F9FAFB" : "#9CA3AF", children: pkg.name }),
1613
+ /* @__PURE__ */ jsx16(VersionArrow, { current: pkg.installed_versions[0] ?? "", latest: pkg.current_version }),
1614
+ pkg.pinned && /* @__PURE__ */ jsx16(StatusBadge, { label: t("outdated_pinned"), variant: "info" })
1615
+ ] }, pkg.name);
1616
+ }),
1617
+ /* @__PURE__ */ jsx16(Box14, { marginTop: 1, children: /* @__PURE__ */ jsxs16(Text16, { color: "#F9FAFB", bold: true, children: [
1618
+ cursor + 1,
1619
+ "/",
1620
+ allOutdated.length
1621
+ ] }) })
1622
+ ] })
1623
+ ] });
1624
+ }
1625
+
1626
+ // src/views/package-info.tsx
1627
+ import { useEffect as useEffect8, useRef as useRef4, useState as useState6 } from "react";
1628
+ import { Box as Box15, Text as Text17, useInput as useInput6 } from "ink";
1629
+ import { Fragment as Fragment4, jsx as jsx17, jsxs as jsxs17 } from "react/jsx-runtime";
1630
+ var ACTION_PROGRESS_KEYS = {
1631
+ install: "pkgInfo_installing",
1632
+ uninstall: "pkgInfo_uninstalling",
1633
+ upgrade: "pkgInfo_upgrading"
1634
+ };
1635
+ var ACTION_CONFIRM_KEYS = {
1636
+ install: "pkgInfo_confirmInstall",
1637
+ uninstall: "pkgInfo_confirmUninstall",
1638
+ upgrade: "pkgInfo_confirmUpgrade"
1639
+ };
1640
+ function PackageInfoView() {
1641
+ const packageName = useNavigationStore((s) => s.selectedPackage);
1642
+ const [formula, setFormula] = useState6(null);
1643
+ const [loading, setLoading2] = useState6(true);
1644
+ const [error, setError2] = useState6(null);
1645
+ const [confirmAction, setConfirmAction] = useState6(null);
1646
+ const activeActionRef = useRef4("install");
1647
+ const mountedRef = useRef4(true);
1648
+ const hasRefreshed = useRef4(false);
1649
+ const stream = useBrewStream();
1650
+ useEffect8(() => {
1651
+ mountedRef.current = true;
1652
+ return () => {
1653
+ mountedRef.current = false;
1654
+ };
1655
+ }, []);
1656
+ useEffect8(() => {
1657
+ if (!packageName) return;
1658
+ setLoading2(true);
1659
+ getFormulaInfo(packageName).then((f) => {
1660
+ if (mountedRef.current) {
1661
+ setFormula(f);
1662
+ setLoading2(false);
1663
+ }
1664
+ }).catch((err) => {
1665
+ if (mountedRef.current) {
1666
+ setError2(err.message);
1667
+ setLoading2(false);
1668
+ }
1669
+ });
1670
+ }, [packageName]);
1671
+ useEffect8(() => {
1672
+ if (!stream.isRunning && !stream.error && stream.lines.length > 0 && !hasRefreshed.current && packageName) {
1673
+ hasRefreshed.current = true;
1674
+ getFormulaInfo(packageName).then((f) => {
1675
+ if (mountedRef.current) {
1676
+ setFormula(f);
1677
+ }
1678
+ }).catch(() => {
1679
+ });
1680
+ }
1681
+ }, [stream.isRunning, stream.error]);
1682
+ useInput6((input, key) => {
1683
+ if (stream.isRunning) {
1684
+ if (key.escape) stream.cancel();
1685
+ return;
1686
+ }
1687
+ if (confirmAction) return;
1688
+ if (!formula) return;
1689
+ const isInstalled2 = formula.installed.length > 0;
1690
+ if (input === "i" && !isInstalled2) {
1691
+ setConfirmAction("install");
1692
+ } else if (input === "u" && isInstalled2) {
1693
+ setConfirmAction("uninstall");
1694
+ } else if (input === "U" && isInstalled2 && formula.outdated) {
1695
+ setConfirmAction("upgrade");
1696
+ }
1697
+ });
1698
+ if (!packageName) {
1699
+ return /* @__PURE__ */ jsx17(Text17, { color: "#6B7280", italic: true, children: t("pkgInfo_noPackage") });
1700
+ }
1701
+ if (loading) return /* @__PURE__ */ jsx17(Loading, { message: t("loading_package", { name: packageName }) });
1702
+ if (error) return /* @__PURE__ */ jsx17(ErrorMessage, { message: error });
1703
+ if (!formula) return /* @__PURE__ */ jsx17(ErrorMessage, { message: t("pkgInfo_notFound") });
1704
+ if (stream.isRunning || stream.lines.length > 0) {
1705
+ return /* @__PURE__ */ jsxs17(Box15, { flexDirection: "column", children: [
1706
+ /* @__PURE__ */ jsx17(ProgressLog, { lines: stream.lines, isRunning: stream.isRunning, title: t(ACTION_PROGRESS_KEYS[activeActionRef.current] ?? ACTION_PROGRESS_KEYS["install"], { name: formula.name }) }),
1707
+ stream.isRunning && /* @__PURE__ */ jsxs17(Text17, { color: "#6B7280", children: [
1708
+ "esc:",
1709
+ t("hint_cancel")
1710
+ ] }),
1711
+ !stream.isRunning && /* @__PURE__ */ jsxs17(Fragment4, { children: [
1712
+ /* @__PURE__ */ jsx17(Text17, { color: stream.error ? "#EF4444" : "#22C55E", bold: true, children: stream.error ? `\u2718 ${stream.error}` : `\u2714 ${t("pkgInfo_done")}` }),
1713
+ /* @__PURE__ */ jsxs17(Text17, { color: "#6B7280", children: [
1714
+ "esc:",
1715
+ t("hint_back")
1716
+ ] })
1717
+ ] })
1718
+ ] });
1719
+ }
1720
+ const installed = formula.installed[0];
1721
+ const isInstalled = formula.installed.length > 0;
1722
+ return /* @__PURE__ */ jsxs17(Box15, { flexDirection: "column", children: [
1723
+ confirmAction && /* @__PURE__ */ jsx17(
1724
+ ConfirmDialog,
1725
+ {
1726
+ message: t(ACTION_CONFIRM_KEYS[confirmAction], { name: formula.name }),
1727
+ onConfirm: () => {
1728
+ const action = confirmAction;
1729
+ activeActionRef.current = action ?? "install";
1730
+ hasRefreshed.current = false;
1731
+ setConfirmAction(null);
1732
+ if (action === "install") void stream.run(["install", formula.name]);
1733
+ else if (action === "uninstall") void stream.run(["uninstall", formula.name]);
1734
+ else if (action === "upgrade") void stream.run(["upgrade", formula.name]);
1735
+ },
1736
+ onCancel: () => setConfirmAction(null)
1737
+ }
1738
+ ),
1739
+ /* @__PURE__ */ jsxs17(Box15, { gap: 2, marginBottom: 1, children: [
1740
+ /* @__PURE__ */ jsx17(GradientText, { colors: GRADIENTS.gold, bold: true, children: formula.name }),
1741
+ /* @__PURE__ */ jsx17(Text17, { color: "#2DD4BF", children: installed?.version ?? formula.versions.stable }),
1742
+ isInstalled && /* @__PURE__ */ jsx17(StatusBadge, { label: t("badge_installed"), variant: "success" }),
1743
+ formula.outdated && /* @__PURE__ */ jsx17(StatusBadge, { label: t("badge_outdated"), variant: "warning" }),
1744
+ formula.pinned && /* @__PURE__ */ jsx17(StatusBadge, { label: t("badge_pinned"), variant: "info" }),
1745
+ formula.keg_only && /* @__PURE__ */ jsx17(StatusBadge, { label: t("badge_kegOnly"), variant: "muted" }),
1746
+ formula.deprecated && /* @__PURE__ */ jsx17(StatusBadge, { label: t("badge_deprecated"), variant: "error" })
1747
+ ] }),
1748
+ /* @__PURE__ */ jsxs17(Box15, { flexDirection: "column", gap: 1, children: [
1749
+ /* @__PURE__ */ jsx17(Text17, { children: formula.desc }),
1750
+ /* @__PURE__ */ jsxs17(Box15, { flexDirection: "column", children: [
1751
+ /* @__PURE__ */ jsx17(SectionHeader, { emoji: "\u{1F4CB}", title: t("pkgInfo_details"), gradient: ["#F9FAFB", "#9CA3AF"] }),
1752
+ /* @__PURE__ */ jsxs17(Box15, { borderStyle: "round", borderColor: "#4B5563", paddingX: 2, flexDirection: "column", children: [
1753
+ /* @__PURE__ */ jsxs17(Text17, { children: [
1754
+ /* @__PURE__ */ jsx17(Text17, { color: "#9CA3AF", children: t("pkgInfo_homepage") }),
1755
+ " ",
1756
+ formula.homepage
1757
+ ] }),
1758
+ /* @__PURE__ */ jsxs17(Text17, { children: [
1759
+ /* @__PURE__ */ jsx17(Text17, { color: "#9CA3AF", children: t("pkgInfo_license") }),
1760
+ " ",
1761
+ formula.license
1762
+ ] }),
1763
+ /* @__PURE__ */ jsxs17(Text17, { children: [
1764
+ /* @__PURE__ */ jsx17(Text17, { color: "#9CA3AF", children: t("pkgInfo_tap") }),
1765
+ " ",
1766
+ formula.tap
1767
+ ] }),
1768
+ /* @__PURE__ */ jsxs17(Text17, { children: [
1769
+ /* @__PURE__ */ jsx17(Text17, { color: "#9CA3AF", children: t("pkgInfo_stable") }),
1770
+ " ",
1771
+ formula.versions.stable
1772
+ ] }),
1773
+ installed && /* @__PURE__ */ jsxs17(Fragment4, { children: [
1774
+ /* @__PURE__ */ jsxs17(Text17, { children: [
1775
+ /* @__PURE__ */ jsx17(Text17, { color: "#9CA3AF", children: t("pkgInfo_installed") }),
1776
+ " ",
1777
+ installed.version,
1778
+ " (",
1779
+ formatRelativeTime(installed.time),
1780
+ ")"
1781
+ ] }),
1782
+ /* @__PURE__ */ jsxs17(Text17, { children: [
1783
+ /* @__PURE__ */ jsx17(Text17, { color: "#9CA3AF", children: t("pkgInfo_bottle") }),
1784
+ " ",
1785
+ installed.poured_from_bottle ? t("common_yes") : t("common_no")
1786
+ ] }),
1787
+ /* @__PURE__ */ jsxs17(Text17, { children: [
1788
+ /* @__PURE__ */ jsx17(Text17, { color: "#9CA3AF", children: t("pkgInfo_onRequest") }),
1789
+ " ",
1790
+ installed.installed_on_request ? t("common_yes") : t("pkgInfo_noDependency")
1791
+ ] })
1792
+ ] })
1793
+ ] })
1794
+ ] }),
1795
+ formula.dependencies.length > 0 && /* @__PURE__ */ jsxs17(Box15, { flexDirection: "column", children: [
1796
+ /* @__PURE__ */ jsx17(SectionHeader, { emoji: "\u{1F517}", title: t("pkgInfo_dependencies", { count: formula.dependencies.length }), gradient: GRADIENTS.ocean }),
1797
+ /* @__PURE__ */ jsx17(Box15, { paddingLeft: 2, flexWrap: "wrap", columnGap: 2, children: formula.dependencies.map((dep) => /* @__PURE__ */ jsx17(Text17, { color: "#9CA3AF", children: dep }, dep)) })
1798
+ ] }),
1799
+ formula.caveats && /* @__PURE__ */ jsxs17(Box15, { flexDirection: "column", children: [
1800
+ /* @__PURE__ */ jsx17(SectionHeader, { emoji: "\u26A0\uFE0F", title: t("pkgInfo_caveats"), color: "#F59E0B" }),
1801
+ /* @__PURE__ */ jsx17(Box15, { borderStyle: "round", borderColor: "#F59E0B", paddingX: 2, children: /* @__PURE__ */ jsx17(Text17, { color: "#F59E0B", children: formula.caveats }) })
1802
+ ] })
1803
+ ] }),
1804
+ /* @__PURE__ */ jsx17(Box15, { marginTop: 1, children: /* @__PURE__ */ jsxs17(Text17, { color: "#6B7280", children: [
1805
+ isInstalled ? `u:${t("hint_uninstall")}` : `i:${t("hint_install")}`,
1806
+ isInstalled && formula.outdated ? ` U:${t("hint_upgrade")}` : "",
1807
+ ` esc:${t("hint_back")}`
1808
+ ] }) })
1809
+ ] });
1810
+ }
1811
+
1812
+ // src/views/services.tsx
1813
+ import { useEffect as useEffect9, useState as useState7 } from "react";
1814
+ import { Box as Box16, Text as Text18, useInput as useInput7 } from "ink";
1815
+ import { jsx as jsx18, jsxs as jsxs18 } from "react/jsx-runtime";
1816
+ var STATUS_VARIANTS = {
1817
+ started: "success",
1818
+ stopped: "muted",
1819
+ error: "error",
1820
+ none: "muted"
1821
+ };
1822
+ function ServicesView() {
1823
+ const { services, loading, errors, fetchServices, serviceAction: serviceAction2 } = useBrewStore();
1824
+ const [cursor, setCursor] = useState7(0);
1825
+ const [actionInProgress, setActionInProgress] = useState7(false);
1826
+ const [confirmAction, setConfirmAction] = useState7(null);
1827
+ useEffect9(() => {
1828
+ fetchServices();
1829
+ }, []);
1830
+ useInput7((input, key) => {
1831
+ if (actionInProgress) return;
1832
+ if (confirmAction) return;
1833
+ if (input === "j" || key.downArrow) {
1834
+ setCursor((c) => Math.min(c + 1, Math.max(0, services.length - 1)));
1835
+ } else if (input === "k" || key.upArrow) {
1836
+ setCursor((c) => Math.max(c - 1, 0));
1837
+ } else if (input === "r") {
1838
+ void fetchServices();
1839
+ }
1840
+ const svc = services[cursor];
1841
+ if (!svc) return;
1842
+ const doAction = (action) => {
1843
+ setActionInProgress(true);
1844
+ void serviceAction2(svc.name, action).finally(() => {
1845
+ setActionInProgress(false);
1846
+ });
1847
+ };
1848
+ if (input === "s") doAction("start");
1849
+ else if (input === "S") setConfirmAction({ type: "stop", name: svc.name });
1850
+ else if (input === "R") setConfirmAction({ type: "restart", name: svc.name });
1851
+ });
1852
+ if (loading.services) return /* @__PURE__ */ jsx18(Loading, { message: t("loading_services") });
1853
+ if (errors.services) return /* @__PURE__ */ jsx18(ErrorMessage, { message: errors.services });
1854
+ if (services.length === 0) {
1855
+ return /* @__PURE__ */ jsxs18(Box16, { flexDirection: "column", children: [
1856
+ /* @__PURE__ */ jsx18(SectionHeader, { emoji: "\u2699\uFE0F", title: t("services_title"), gradient: GRADIENTS.ocean }),
1857
+ /* @__PURE__ */ jsx18(Text18, { color: "#6B7280", italic: true, children: t("services_noServices") })
1858
+ ] });
1859
+ }
1860
+ return /* @__PURE__ */ jsxs18(Box16, { flexDirection: "column", children: [
1861
+ /* @__PURE__ */ jsx18(SectionHeader, { emoji: "\u2699\uFE0F", title: t("services_titleCount", { count: services.length }), gradient: GRADIENTS.ocean }),
1862
+ confirmAction && /* @__PURE__ */ jsx18(Box16, { marginY: 1, children: /* @__PURE__ */ jsx18(
1863
+ ConfirmDialog,
1864
+ {
1865
+ message: confirmAction.type === "stop" ? t("services_confirmStop", { name: confirmAction.name }) : t("services_confirmRestart", { name: confirmAction.name }),
1866
+ onConfirm: () => {
1867
+ const { type, name } = confirmAction;
1868
+ setConfirmAction(null);
1869
+ setActionInProgress(true);
1870
+ void serviceAction2(name, type).finally(() => {
1871
+ setActionInProgress(false);
1872
+ });
1873
+ },
1874
+ onCancel: () => setConfirmAction(null)
1875
+ }
1876
+ ) }),
1877
+ /* @__PURE__ */ jsxs18(Box16, { flexDirection: "column", marginTop: 1, children: [
1878
+ /* @__PURE__ */ jsxs18(Box16, { gap: 1, borderStyle: "single", borderBottom: true, borderTop: false, borderLeft: false, borderRight: false, borderColor: "#4B5563", paddingBottom: 0, children: [
1879
+ /* @__PURE__ */ jsxs18(Text18, { bold: true, color: "#F9FAFB", children: [
1880
+ " ",
1881
+ t("services_name").padEnd(22)
1882
+ ] }),
1883
+ /* @__PURE__ */ jsx18(Text18, { bold: true, color: "#F9FAFB", children: t("services_status").padEnd(12) }),
1884
+ /* @__PURE__ */ jsx18(Text18, { bold: true, color: "#F9FAFB", children: t("services_user") })
1885
+ ] }),
1886
+ services.map((svc, i) => {
1887
+ const isCurrent = i === cursor;
1888
+ return /* @__PURE__ */ jsxs18(Box16, { gap: 1, children: [
1889
+ /* @__PURE__ */ jsx18(Text18, { color: isCurrent ? "#22C55E" : "#9CA3AF", children: isCurrent ? "\u25B6" : " " }),
1890
+ /* @__PURE__ */ jsx18(Text18, { bold: isCurrent, inverse: isCurrent, color: isCurrent ? "#F9FAFB" : "#9CA3AF", children: svc.name.padEnd(20) }),
1891
+ /* @__PURE__ */ jsx18(StatusBadge, { label: svc.status, variant: STATUS_VARIANTS[svc.status] }),
1892
+ /* @__PURE__ */ jsx18(Text18, { color: "#9CA3AF", children: svc.user ?? "-" }),
1893
+ svc.exit_code != null && svc.exit_code !== 0 && /* @__PURE__ */ jsx18(Text18, { color: "#EF4444", children: t("common_exit", { code: svc.exit_code }) })
1894
+ ] }, svc.name);
1895
+ })
1896
+ ] }),
1897
+ actionInProgress && /* @__PURE__ */ jsx18(Text18, { color: "#38BDF8", children: t("services_processing") }),
1898
+ /* @__PURE__ */ jsx18(Box16, { marginTop: 1, children: /* @__PURE__ */ jsxs18(Text18, { color: "#F9FAFB", bold: true, children: [
1899
+ cursor + 1,
1900
+ "/",
1901
+ services.length
1902
+ ] }) })
1903
+ ] });
1904
+ }
1905
+
1906
+ // src/views/doctor.tsx
1907
+ import { useEffect as useEffect10 } from "react";
1908
+ import { Box as Box17, Text as Text19, useInput as useInput8 } from "ink";
1909
+ import { jsx as jsx19, jsxs as jsxs19 } from "react/jsx-runtime";
1910
+ function DoctorView() {
1911
+ const { doctorWarnings, doctorClean, loading, errors, fetchDoctor } = useBrewStore();
1912
+ useEffect10(() => {
1913
+ fetchDoctor();
1914
+ }, []);
1915
+ useInput8((input) => {
1916
+ if (input === "r") void fetchDoctor();
1917
+ });
1918
+ if (loading.doctor) return /* @__PURE__ */ jsx19(Loading, { message: t("loading_doctor") });
1919
+ if (errors.doctor) return /* @__PURE__ */ jsx19(ErrorMessage, { message: errors.doctor });
1920
+ return /* @__PURE__ */ jsxs19(Box17, { flexDirection: "column", children: [
1921
+ /* @__PURE__ */ jsx19(SectionHeader, { emoji: "\u{1FA7A}", title: t("doctor_title"), gradient: GRADIENTS.emerald }),
1922
+ /* @__PURE__ */ jsxs19(Box17, { flexDirection: "column", marginTop: 1, children: [
1923
+ doctorClean && /* @__PURE__ */ jsx19(Box17, { borderStyle: "round", borderColor: "#22C55E", paddingX: 2, paddingY: 0, children: /* @__PURE__ */ jsxs19(Text19, { color: "#22C55E", bold: true, children: [
1924
+ "\u2714",
1925
+ " ",
1926
+ t("doctor_clean")
1927
+ ] }) }),
1928
+ doctorClean === false && doctorWarnings.length === 0 && /* @__PURE__ */ jsx19(Text19, { color: "#F59E0B", children: t("doctor_warningsNotCaptured") }),
1929
+ 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)) }, i))
1930
+ ] }),
1931
+ /* @__PURE__ */ jsx19(Box17, { marginTop: 1, children: /* @__PURE__ */ jsx19(Text19, { color: "#F9FAFB", bold: true, children: doctorWarnings.length > 0 ? tp("plural_warnings", doctorWarnings.length) : "" }) })
1932
+ ] });
1933
+ }
1934
+
1935
+ // src/views/profiles.tsx
1936
+ import { useEffect as useEffect11, useRef as useRef5, useState as useState8 } from "react";
1937
+ import { Box as Box18, Text as Text20, useInput as useInput9 } from "ink";
1938
+ import { TextInput as TextInput3 } from "@inkjs/ui";
1939
+
1940
+ // src/stores/profile-store.ts
1941
+ import { create as create4 } from "zustand";
1942
+
1943
+ // src/lib/profiles/profile-manager.ts
1944
+ import { readFile, writeFile, readdir, rm } from "fs/promises";
1945
+ import { join, basename } from "path";
1946
+
1947
+ // src/lib/license/watermark.ts
1948
+ function getWatermark() {
1949
+ const license = useLicenseStore.getState().license;
1950
+ if (!license?.customerEmail) return "";
1951
+ return `Licensed to: ${license.customerEmail}`;
1952
+ }
1953
+
1954
+ // src/lib/profiles/profile-manager.ts
1955
+ var MAX_PROFILE_NAME_LENGTH = 100;
1956
+ function validateProfileName(name) {
1957
+ if (!name || name.trim().length === 0) {
1958
+ throw new Error("Profile name cannot be empty");
1959
+ }
1960
+ if (name.length > MAX_PROFILE_NAME_LENGTH) {
1961
+ throw new Error(`Profile name is too long (max ${MAX_PROFILE_NAME_LENGTH} characters)`);
1962
+ }
1963
+ if (!/^[\w\s-]+$/.test(name)) {
1964
+ throw new Error(`Invalid profile name: "${name}". Only letters, numbers, spaces, hyphens, and underscores are allowed.`);
1965
+ }
1966
+ }
1967
+ function profilePath(name) {
1968
+ validateProfileName(name);
1969
+ return join(PROFILES_DIR, `${basename(name)}.json`);
1970
+ }
1971
+ async function listProfiles() {
1972
+ requirePro();
1973
+ await ensureDataDirs();
1974
+ try {
1975
+ const files = await readdir(PROFILES_DIR);
1976
+ return files.filter((f) => f.endsWith(".json")).map((f) => f.replace(".json", ""));
1977
+ } catch {
1978
+ return [];
1979
+ }
1980
+ }
1981
+ async function loadProfile(name) {
1982
+ requirePro();
1983
+ const raw = await readFile(profilePath(name), "utf-8");
1984
+ let file;
1985
+ try {
1986
+ file = JSON.parse(raw);
1987
+ } catch (err) {
1988
+ throw new Error(`Profile "${name}" is corrupted: ${err instanceof Error ? err.message : String(err)}`);
1989
+ }
1990
+ if (!file.profile) {
1991
+ throw new Error(`Profile "${name}" is missing required data`);
1992
+ }
1993
+ return file.profile;
1994
+ }
1995
+ async function saveProfile(profile) {
1996
+ requirePro();
1997
+ await ensureDataDirs();
1998
+ const file = { version: 1, profile };
1999
+ await writeFile(profilePath(profile.name), JSON.stringify(file, null, 2), "utf-8");
2000
+ }
2001
+ async function deleteProfile(name) {
2002
+ requirePro();
2003
+ try {
2004
+ await rm(profilePath(name));
2005
+ } catch {
2006
+ }
2007
+ }
2008
+ async function exportCurrentSetup(name, description) {
2009
+ requirePro();
2010
+ const [installed, leaves, tapsRaw] = await Promise.all([
2011
+ getInstalled(),
2012
+ getLeaves(),
2013
+ execBrew(["tap"])
2014
+ ]);
2015
+ const taps = tapsRaw.split("\n").map((l) => l.trim()).filter(Boolean);
2016
+ const casks = installed.casks.filter((c) => c.installed).map((c) => c.token);
2017
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2018
+ const profile = {
2019
+ name,
2020
+ description,
2021
+ createdAt: now,
2022
+ updatedAt: now,
2023
+ formulae: leaves,
2024
+ casks,
2025
+ taps,
2026
+ exportedBy: getWatermark()
2027
+ // Layer 16: Watermark — who exported this profile
2028
+ };
2029
+ await saveProfile(profile);
2030
+ return profile;
2031
+ }
2032
+ async function updateProfile(oldName, newName, newDescription) {
2033
+ requirePro();
2034
+ const profile = await loadProfile(oldName);
2035
+ if (oldName !== newName) {
2036
+ await deleteProfile(oldName);
2037
+ }
2038
+ const updated = {
2039
+ ...profile,
2040
+ name: newName,
2041
+ description: newDescription,
2042
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
2043
+ };
2044
+ await saveProfile(updated);
2045
+ }
2046
+ async function* importProfile(profile) {
2047
+ requirePro();
2048
+ const installed = await getInstalled();
2049
+ const installedFormulae = new Set(installed.formulae.map((f) => f.name));
2050
+ const installedCasks = new Set(installed.casks.filter((c) => c.installed).map((c) => c.token));
2051
+ for (const tap of profile.taps) {
2052
+ yield t("profileMgr_tapping", { name: tap });
2053
+ try {
2054
+ await execBrew(["tap", tap]);
2055
+ } catch {
2056
+ }
2057
+ }
2058
+ const missingFormulae = profile.formulae.filter((f) => !installedFormulae.has(f));
2059
+ for (const name of missingFormulae) {
2060
+ yield t("profileMgr_installing", { name });
2061
+ for await (const line of streamBrew(["install", name])) {
2062
+ yield line;
2063
+ }
2064
+ }
2065
+ const missingCasks = profile.casks.filter((c) => !installedCasks.has(c));
2066
+ for (const name of missingCasks) {
2067
+ yield t("profileMgr_installingCask", { name });
2068
+ for await (const line of streamBrew(["install", "--cask", name])) {
2069
+ yield line;
2070
+ }
2071
+ }
2072
+ const totalInstalled = missingFormulae.length + missingCasks.length;
2073
+ yield t("profileMgr_importDone", { count: totalInstalled });
2074
+ }
2075
+
2076
+ // src/stores/profile-store.ts
2077
+ var useProfileStore = create4((set) => ({
2078
+ profileNames: [],
2079
+ selectedProfile: null,
2080
+ loading: false,
2081
+ loadError: null,
2082
+ fetchProfiles: async () => {
2083
+ set({ loading: true });
2084
+ const names = await listProfiles();
2085
+ set({ profileNames: names, loading: false });
2086
+ },
2087
+ loadProfile: async (name) => {
2088
+ set({ loadError: null });
2089
+ try {
2090
+ const profile = await loadProfile(name);
2091
+ set({ selectedProfile: profile });
2092
+ } catch (err) {
2093
+ set({ loadError: err instanceof Error ? err.message : String(err) });
2094
+ }
2095
+ },
2096
+ exportCurrent: async (name, description) => {
2097
+ set({ loading: true, loadError: null });
2098
+ try {
2099
+ await exportCurrentSetup(name, description);
2100
+ const names = await listProfiles();
2101
+ set({ profileNames: names, loading: false });
2102
+ } catch (err) {
2103
+ set({ loading: false, loadError: err instanceof Error ? err.message : String(err) });
2104
+ throw err;
2105
+ }
2106
+ },
2107
+ deleteProfile: async (name) => {
2108
+ await deleteProfile(name);
2109
+ const names = await listProfiles();
2110
+ set({ profileNames: names, selectedProfile: null });
2111
+ },
2112
+ updateProfile: async (oldName, newName, newDescription) => {
2113
+ set({ loadError: null });
2114
+ try {
2115
+ await updateProfile(oldName, newName, newDescription);
2116
+ const names = await listProfiles();
2117
+ const updated = await loadProfile(newName);
2118
+ set({ profileNames: names, selectedProfile: updated });
2119
+ } catch (err) {
2120
+ set({ loadError: err instanceof Error ? err.message : String(err) });
2121
+ }
2122
+ }
2123
+ }));
2124
+
2125
+ // src/views/profiles.tsx
2126
+ import { jsx as jsx20, jsxs as jsxs20 } from "react/jsx-runtime";
2127
+ function ProfilesView() {
2128
+ const { profileNames, selectedProfile, loading, loadError, fetchProfiles, loadProfile: loadProfile2, exportCurrent, deleteProfile: deleteProfile2, updateProfile: updateProfile2 } = useProfileStore();
2129
+ const [cursor, setCursor] = useState8(0);
2130
+ const [mode, setMode] = useState8("list");
2131
+ const [newName, setNewName] = useState8("");
2132
+ const [confirmDelete, setConfirmDelete] = useState8(false);
2133
+ const [editName, setEditName] = useState8("");
2134
+ const [editDesc, setEditDesc] = useState8("");
2135
+ const [importLines, setImportLines] = useState8([]);
2136
+ const [importRunning, setImportRunning] = useState8(false);
2137
+ const { openModal, closeModal } = useModalStore();
2138
+ const importGenRef = useRef5(null);
2139
+ const mountedRef = useRef5(true);
2140
+ useEffect11(() => {
2141
+ fetchProfiles();
2142
+ }, []);
2143
+ useEffect11(() => {
2144
+ mountedRef.current = true;
2145
+ return () => {
2146
+ mountedRef.current = false;
2147
+ importGenRef.current?.return(void 0);
2148
+ importGenRef.current = null;
2149
+ };
2150
+ }, []);
2151
+ useEffect11(() => {
2152
+ if (mode === "detail" || mode === "create-name" || mode === "create-desc" || mode === "importing" || mode === "edit-name" || mode === "edit-desc") {
2153
+ openModal();
2154
+ return () => {
2155
+ closeModal();
2156
+ };
2157
+ }
2158
+ return void 0;
2159
+ }, [mode]);
2160
+ useInput9((input, key) => {
2161
+ if (mode !== "list" || confirmDelete) return;
2162
+ if (input === "n") {
2163
+ setMode("create-name");
2164
+ return;
2165
+ }
2166
+ if (input === "d" && profileNames[cursor]) {
2167
+ setConfirmDelete(true);
2168
+ return;
2169
+ }
2170
+ if (key.return && profileNames[cursor]) {
2171
+ void loadProfile2(profileNames[cursor]);
2172
+ setMode("detail");
2173
+ return;
2174
+ }
2175
+ if (input === "i" && profileNames[cursor]) {
2176
+ void startImport(profileNames[cursor]);
2177
+ return;
2178
+ }
2179
+ if (input === "j" || key.downArrow) setCursor((c) => Math.min(c + 1, Math.max(0, profileNames.length - 1)));
2180
+ else if (input === "k" || key.upArrow) setCursor((c) => Math.max(c - 1, 0));
2181
+ });
2182
+ useInput9((input, key) => {
2183
+ if (key.escape || input === "q") {
2184
+ setMode("list");
2185
+ return;
2186
+ }
2187
+ if (input === "e" && selectedProfile) {
2188
+ setEditName(selectedProfile.name);
2189
+ setEditDesc(selectedProfile.description);
2190
+ setMode("edit-name");
2191
+ }
2192
+ }, { isActive: mode === "detail" });
2193
+ const startImport = async (name) => {
2194
+ setMode("importing");
2195
+ setImportLines([]);
2196
+ setImportRunning(true);
2197
+ try {
2198
+ const profile = await loadProfile(name);
2199
+ const gen = importProfile(profile);
2200
+ importGenRef.current = gen;
2201
+ for await (const line of gen) {
2202
+ if (!mountedRef.current) break;
2203
+ setImportLines((prev) => [...prev.slice(-99), line]);
2204
+ }
2205
+ } catch (err) {
2206
+ if (mountedRef.current) {
2207
+ setImportLines((prev) => [...prev, `${t("error_prefix")}${err instanceof Error ? err.message : err}`]);
2208
+ }
2209
+ } finally {
2210
+ importGenRef.current = null;
2211
+ if (mountedRef.current) {
2212
+ setImportRunning(false);
2213
+ }
2214
+ }
2215
+ };
2216
+ if (loading) return /* @__PURE__ */ jsx20(Loading, { message: t("loading_profiles") });
2217
+ if (mode === "importing") {
2218
+ return /* @__PURE__ */ jsxs20(Box18, { flexDirection: "column", children: [
2219
+ /* @__PURE__ */ jsx20(ProgressLog, { lines: importLines, isRunning: importRunning, title: t("profiles_importTitle") }),
2220
+ !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: [
2221
+ "\u2714",
2222
+ " ",
2223
+ t("profiles_importComplete")
2224
+ ] }) }) })
2225
+ ] });
2226
+ }
2227
+ if (mode === "create-name") {
2228
+ return /* @__PURE__ */ jsxs20(Box18, { flexDirection: "column", children: [
2229
+ /* @__PURE__ */ jsx20(Text20, { bold: true, children: t("profiles_createName") }),
2230
+ /* @__PURE__ */ jsx20(
2231
+ TextInput3,
2232
+ {
2233
+ placeholder: t("profiles_namePlaceholder"),
2234
+ onSubmit: (val) => {
2235
+ setNewName(val);
2236
+ setMode("create-desc");
2237
+ }
2238
+ }
2239
+ )
2240
+ ] });
2241
+ }
2242
+ if (mode === "create-desc") {
2243
+ return /* @__PURE__ */ jsxs20(Box18, { flexDirection: "column", children: [
2244
+ /* @__PURE__ */ jsx20(Text20, { bold: true, children: t("profiles_createDesc", { name: newName }) }),
2245
+ loadError && /* @__PURE__ */ jsxs20(Text20, { color: "#EF4444", children: [
2246
+ t("error_prefix"),
2247
+ loadError
2248
+ ] }),
2249
+ /* @__PURE__ */ jsx20(
2250
+ TextInput3,
2251
+ {
2252
+ placeholder: t("profiles_descPlaceholder"),
2253
+ onSubmit: async (val) => {
2254
+ try {
2255
+ await exportCurrent(newName, val);
2256
+ } finally {
2257
+ setMode("list");
2258
+ setNewName("");
2259
+ }
2260
+ }
2261
+ }
2262
+ )
2263
+ ] });
2264
+ }
2265
+ if (mode === "edit-name") {
2266
+ return /* @__PURE__ */ jsxs20(Box18, { flexDirection: "column", children: [
2267
+ /* @__PURE__ */ jsx20(Text20, { bold: true, children: t("profiles_editName") }),
2268
+ /* @__PURE__ */ jsx20(
2269
+ TextInput3,
2270
+ {
2271
+ defaultValue: editName,
2272
+ onSubmit: (val) => {
2273
+ setEditName(val);
2274
+ setMode("edit-desc");
2275
+ }
2276
+ }
2277
+ )
2278
+ ] });
2279
+ }
2280
+ if (mode === "edit-desc") {
2281
+ return /* @__PURE__ */ jsxs20(Box18, { flexDirection: "column", children: [
2282
+ /* @__PURE__ */ jsx20(Text20, { bold: true, children: t("profiles_editDesc", { name: editName }) }),
2283
+ loadError && /* @__PURE__ */ jsxs20(Text20, { color: "#EF4444", children: [
2284
+ t("error_prefix"),
2285
+ loadError
2286
+ ] }),
2287
+ /* @__PURE__ */ jsx20(
2288
+ TextInput3,
2289
+ {
2290
+ defaultValue: editDesc,
2291
+ onSubmit: async (val) => {
2292
+ if (selectedProfile) {
2293
+ await updateProfile2(selectedProfile.name, editName, val);
2294
+ }
2295
+ setMode("detail");
2296
+ setEditName("");
2297
+ setEditDesc("");
2298
+ }
2299
+ }
2300
+ )
2301
+ ] });
2302
+ }
2303
+ if (mode === "detail" && selectedProfile) {
2304
+ return /* @__PURE__ */ jsxs20(Box18, { flexDirection: "column", children: [
2305
+ /* @__PURE__ */ jsx20(Text20, { bold: true, color: "#FFD700", children: selectedProfile.name }),
2306
+ /* @__PURE__ */ jsx20(Text20, { color: "#9CA3AF", children: selectedProfile.description }),
2307
+ /* @__PURE__ */ jsx20(Text20, { color: "#9CA3AF", children: t("profiles_created", { date: new Date(selectedProfile.createdAt).toLocaleDateString() }) }),
2308
+ /* @__PURE__ */ jsxs20(Box18, { marginTop: 1, flexDirection: "column", children: [
2309
+ /* @__PURE__ */ jsx20(Text20, { bold: true, children: t("profiles_formulaeCount", { count: selectedProfile.formulae.length }) }),
2310
+ /* @__PURE__ */ jsxs20(Box18, { paddingLeft: 2, flexDirection: "column", children: [
2311
+ selectedProfile.formulae.slice(0, 30).map((f) => /* @__PURE__ */ jsx20(Text20, { color: "#9CA3AF", children: f }, f)),
2312
+ selectedProfile.formulae.length > 30 && /* @__PURE__ */ jsx20(Text20, { color: "#6B7280", italic: true, children: t("common_andMore", { count: selectedProfile.formulae.length - 30 }) })
2313
+ ] }),
2314
+ /* @__PURE__ */ jsx20(Text20, { bold: true, children: t("profiles_casksCount", { count: selectedProfile.casks.length }) }),
2315
+ /* @__PURE__ */ jsx20(Box18, { paddingLeft: 2, flexDirection: "column", children: selectedProfile.casks.map((c) => /* @__PURE__ */ jsx20(Text20, { color: "#9CA3AF", children: c }, c)) })
2316
+ ] }),
2317
+ /* @__PURE__ */ jsx20(Box18, { marginTop: 1, children: /* @__PURE__ */ jsxs20(Text20, { color: "#6B7280", children: [
2318
+ "esc:",
2319
+ t("hint_back"),
2320
+ " e:",
2321
+ t("hint_edit"),
2322
+ " i:",
2323
+ t("hint_importProfile")
2324
+ ] }) })
2325
+ ] });
2326
+ }
2327
+ return /* @__PURE__ */ jsxs20(Box18, { flexDirection: "column", children: [
2328
+ /* @__PURE__ */ jsx20(SectionHeader, { emoji: "\u{1F4C1}", title: t("profiles_title", { count: profileNames.length }), gradient: GRADIENTS.gold }),
2329
+ confirmDelete && profileNames[cursor] && /* @__PURE__ */ jsx20(Box18, { marginY: 1, children: /* @__PURE__ */ jsx20(
2330
+ ConfirmDialog,
2331
+ {
2332
+ message: t("profiles_confirmDelete", { name: profileNames[cursor] }),
2333
+ onConfirm: () => {
2334
+ void deleteProfile2(profileNames[cursor]);
2335
+ setConfirmDelete(false);
2336
+ },
2337
+ onCancel: () => setConfirmDelete(false)
2338
+ }
2339
+ ) }),
2340
+ profileNames.length === 0 && !confirmDelete && /* @__PURE__ */ jsx20(Box18, { marginTop: 1, borderStyle: "round", borderColor: "#6B7280", paddingX: 2, paddingY: 0, children: /* @__PURE__ */ jsxs20(Box18, { flexDirection: "column", children: [
2341
+ /* @__PURE__ */ jsx20(Text20, { color: "#6B7280", italic: true, children: t("profiles_noProfiles") }),
2342
+ /* @__PURE__ */ jsxs20(Text20, { color: "#9CA3AF", children: [
2343
+ t("profiles_press"),
2344
+ " ",
2345
+ /* @__PURE__ */ jsx20(Text20, { color: "#FFD700", bold: true, children: "n" }),
2346
+ " ",
2347
+ t("profiles_exportHint")
2348
+ ] })
2349
+ ] }) }),
2350
+ profileNames.length > 0 && !confirmDelete && /* @__PURE__ */ jsxs20(Box18, { flexDirection: "column", marginTop: 1, children: [
2351
+ profileNames.map((name, i) => {
2352
+ const isCurrent = i === cursor;
2353
+ return /* @__PURE__ */ jsxs20(Box18, { gap: 1, children: [
2354
+ /* @__PURE__ */ jsx20(Text20, { color: isCurrent ? "#22C55E" : "#9CA3AF", children: isCurrent ? "\u25B6" : " " }),
2355
+ /* @__PURE__ */ jsx20(Text20, { bold: isCurrent, inverse: isCurrent, children: name })
2356
+ ] }, name);
2357
+ }),
2358
+ /* @__PURE__ */ jsx20(Box18, { marginTop: 1, children: /* @__PURE__ */ jsxs20(Text20, { color: "#F9FAFB", bold: true, children: [
2359
+ cursor + 1,
2360
+ "/",
2361
+ profileNames.length
2362
+ ] }) })
2363
+ ] })
2364
+ ] });
2365
+ }
2366
+
2367
+ // src/views/smart-cleanup.tsx
2368
+ import { useEffect as useEffect12, useRef as useRef6, useState as useState9 } from "react";
2369
+ import { Box as Box19, Text as Text21, useInput as useInput10 } from "ink";
2370
+
2371
+ // src/stores/cleanup-store.ts
2372
+ import { create as create5 } from "zustand";
2373
+
2374
+ // src/lib/cleanup/cleanup-analyzer.ts
2375
+ import { execFile } from "child_process";
2376
+ import { promisify } from "util";
2377
+ var execFileAsync = promisify(execFile);
2378
+ async function getDiskUsage(cellarPath) {
2379
+ try {
2380
+ const { stdout } = await execFileAsync("du", ["-sk", cellarPath]);
2381
+ const kb = parseInt(stdout.split(" ")[0] ?? "0", 10);
2382
+ return kb * 1024;
2383
+ } catch {
2384
+ return 0;
2385
+ }
2386
+ }
2387
+ async function getCellarPath(name) {
2388
+ try {
2389
+ const raw = await execBrew(["--cellar", name]);
2390
+ return raw.trim() || null;
2391
+ } catch {
2392
+ return null;
2393
+ }
2394
+ }
2395
+ async function analyzeCleanup(formulae, leaves) {
2396
+ requirePro();
2397
+ const leavesSet = new Set(leaves);
2398
+ const reverseDeps = /* @__PURE__ */ new Map();
2399
+ for (const f of formulae) {
2400
+ for (const dep of f.dependencies) {
2401
+ reverseDeps.set(dep, (reverseDeps.get(dep) ?? 0) + 1);
2402
+ }
2403
+ }
2404
+ const orphans = [];
2405
+ for (const f of formulae) {
2406
+ const installed = f.installed[0];
2407
+ if (!installed) continue;
2408
+ if (installed.installed_as_dependency && !installed.installed_on_request && !leavesSet.has(f.name)) {
2409
+ let isNeeded = false;
2410
+ for (const other of formulae) {
2411
+ if (other.name === f.name) continue;
2412
+ if (other.dependencies.includes(f.name)) {
2413
+ isNeeded = true;
2414
+ break;
2415
+ }
2416
+ }
2417
+ if (!isNeeded) {
2418
+ orphans.push(f.name);
2419
+ }
2420
+ }
2421
+ }
2422
+ const candidates = [];
2423
+ const concurrency = 5;
2424
+ for (let i = 0; i < orphans.length; i += concurrency) {
2425
+ const batch = orphans.slice(i, i + concurrency);
2426
+ const results = await Promise.all(
2427
+ batch.map(async (name) => {
2428
+ const cellarPath = await getCellarPath(name);
2429
+ const bytes = cellarPath ? await getDiskUsage(cellarPath) : 0;
2430
+ return {
2431
+ name,
2432
+ reason: "orphan",
2433
+ diskUsageBytes: bytes,
2434
+ diskUsageFormatted: formatBytes(bytes),
2435
+ installedAsDependency: true,
2436
+ dependentsCount: 0
2437
+ };
2438
+ })
2439
+ );
2440
+ candidates.push(...results);
2441
+ }
2442
+ candidates.sort((a, b) => b.diskUsageBytes - a.diskUsageBytes);
2443
+ const totalBytes = candidates.reduce((sum, c) => sum + c.diskUsageBytes, 0);
2444
+ return {
2445
+ totalReclaimableBytes: totalBytes,
2446
+ totalReclaimableFormatted: formatBytes(totalBytes),
2447
+ candidates,
2448
+ analyzedAt: (/* @__PURE__ */ new Date()).toISOString()
2449
+ };
2450
+ }
2451
+
2452
+ // src/stores/cleanup-store.ts
2453
+ var useCleanupStore = create5((set, get) => ({
2454
+ summary: null,
2455
+ selected: /* @__PURE__ */ new Set(),
2456
+ loading: false,
2457
+ error: null,
2458
+ analyze: async () => {
2459
+ set({ loading: true, error: null });
2460
+ try {
2461
+ const brewState = useBrewStore.getState();
2462
+ if (brewState.formulae.length === 0) {
2463
+ await brewState.fetchInstalled();
2464
+ await brewState.fetchLeaves();
2465
+ }
2466
+ const { formulae, leaves } = useBrewStore.getState();
2467
+ const summary = await analyzeCleanup(formulae, leaves);
2468
+ set({ summary, selected: /* @__PURE__ */ new Set(), loading: false });
2469
+ } catch (err) {
2470
+ set({ error: err instanceof Error ? err.message : String(err), loading: false });
2471
+ }
2472
+ },
2473
+ toggleSelect: (name) => {
2474
+ const selected = new Set(get().selected);
2475
+ if (selected.has(name)) selected.delete(name);
2476
+ else selected.add(name);
2477
+ set({ selected });
2478
+ },
2479
+ selectAll: () => {
2480
+ const names = get().summary?.candidates.map((c) => c.name) ?? [];
2481
+ set({ selected: new Set(names) });
2482
+ },
2483
+ deselectAll: () => set({ selected: /* @__PURE__ */ new Set() })
2484
+ }));
2485
+
2486
+ // src/views/smart-cleanup.tsx
2487
+ import { jsx as jsx21, jsxs as jsxs21 } from "react/jsx-runtime";
2488
+ function SmartCleanupView() {
2489
+ const { summary, selected, loading, error, analyze, toggleSelect, selectAll } = useCleanupStore();
2490
+ const [cursor, setCursor] = useState9(0);
2491
+ const [confirmClean, setConfirmClean] = useState9(false);
2492
+ const [confirmForce, setConfirmForce] = useState9(false);
2493
+ const [failedNames, setFailedNames] = useState9([]);
2494
+ const stream = useBrewStream();
2495
+ const hasRefreshed = useRef6(false);
2496
+ useEffect12(() => {
2497
+ analyze();
2498
+ }, []);
2499
+ useEffect12(() => {
2500
+ if (!stream.isRunning && !stream.error && stream.lines.length > 0 && !hasRefreshed.current) {
2501
+ hasRefreshed.current = true;
2502
+ void analyze();
2503
+ }
2504
+ }, [stream.isRunning, stream.error]);
2505
+ const candidates = summary?.candidates ?? [];
2506
+ const isDependencyError = stream.error != null && stream.lines.some((l) => l.includes("Refusing to uninstall") || l.includes("required by"));
2507
+ useInput10((input, key) => {
2508
+ if (stream.isRunning) {
2509
+ if (key.escape) stream.cancel();
2510
+ return;
2511
+ }
2512
+ if (confirmClean || confirmForce) return;
2513
+ if (input === "F" && isDependencyError && failedNames.length > 0) {
2514
+ setConfirmForce(true);
2515
+ return;
2516
+ }
2517
+ if (input === "r") {
2518
+ stream.clear();
2519
+ setFailedNames([]);
2520
+ void analyze();
2521
+ return;
2522
+ }
2523
+ if (key.return && candidates[cursor]) {
2524
+ toggleSelect(candidates[cursor].name);
2525
+ return;
2526
+ }
2527
+ if (input === "a") {
2528
+ selectAll();
2529
+ return;
2530
+ }
2531
+ if (input === "c" && selected.size > 0) {
2532
+ setConfirmClean(true);
2533
+ return;
2534
+ }
2535
+ if (input === "j" || key.downArrow) setCursor((c) => Math.min(c + 1, Math.max(0, candidates.length - 1)));
2536
+ else if (input === "k" || key.upArrow) setCursor((c) => Math.max(c - 1, 0));
2537
+ });
2538
+ if (loading) return /* @__PURE__ */ jsx21(Loading, { message: t("loading_cleanup") });
2539
+ if (error) return /* @__PURE__ */ jsx21(ErrorMessage, { message: error });
2540
+ if (stream.isRunning || stream.lines.length > 0) {
2541
+ return /* @__PURE__ */ jsxs21(Box19, { flexDirection: "column", children: [
2542
+ /* @__PURE__ */ jsx21(ProgressLog, { lines: stream.lines, isRunning: stream.isRunning, title: t("cleanup_cleaning") }),
2543
+ stream.isRunning && /* @__PURE__ */ jsxs21(Text21, { color: "#6B7280", children: [
2544
+ "esc:",
2545
+ t("hint_cancel")
2546
+ ] }),
2547
+ !stream.isRunning && !stream.error && /* @__PURE__ */ jsx21(Box19, { borderStyle: "round", borderColor: "#22C55E", paddingX: 2, paddingY: 0, children: /* @__PURE__ */ jsxs21(Text21, { color: "#22C55E", bold: true, children: [
2548
+ "\u2714",
2549
+ " ",
2550
+ t("cleanup_complete")
2551
+ ] }) }),
2552
+ !stream.isRunning && stream.error && /* @__PURE__ */ jsxs21(Box19, { flexDirection: "column", gap: 1, children: [
2553
+ /* @__PURE__ */ jsx21(Box19, { borderStyle: "round", borderColor: "#EF4444", paddingX: 2, paddingY: 0, children: /* @__PURE__ */ jsxs21(Text21, { color: "#EF4444", bold: true, children: [
2554
+ "\u2718",
2555
+ " ",
2556
+ t("cleanup_depError")
2557
+ ] }) }),
2558
+ isDependencyError && failedNames.length > 0 && /* @__PURE__ */ jsxs21(Text21, { color: "#F59E0B", children: [
2559
+ "F:",
2560
+ t("hint_force"),
2561
+ " r:",
2562
+ t("hint_refresh")
2563
+ ] })
2564
+ ] }),
2565
+ confirmForce && /* @__PURE__ */ jsx21(Box19, { marginY: 1, children: /* @__PURE__ */ jsx21(
2566
+ ConfirmDialog,
2567
+ {
2568
+ message: t("cleanup_confirmForce", { count: failedNames.length }),
2569
+ onConfirm: () => {
2570
+ setConfirmForce(false);
2571
+ hasRefreshed.current = false;
2572
+ stream.clear();
2573
+ void stream.run(["uninstall", "--ignore-dependencies", ...failedNames]);
2574
+ },
2575
+ onCancel: () => setConfirmForce(false)
2576
+ }
2577
+ ) })
2578
+ ] });
2579
+ }
2580
+ return /* @__PURE__ */ jsxs21(Box19, { flexDirection: "column", children: [
2581
+ /* @__PURE__ */ jsx21(SectionHeader, { emoji: "\u{1F9F9}", title: t("cleanup_title"), gradient: GRADIENTS.emerald }),
2582
+ summary && /* @__PURE__ */ jsxs21(Box19, { gap: 1, marginY: 1, children: [
2583
+ /* @__PURE__ */ jsx21(StatCard, { label: t("cleanup_orphans"), value: candidates.length, color: candidates.length > 0 ? "#F59E0B" : "#22C55E" }),
2584
+ /* @__PURE__ */ jsx21(StatCard, { label: t("cleanup_reclaimable"), value: summary.totalReclaimableFormatted, color: "#38BDF8" }),
2585
+ /* @__PURE__ */ jsx21(StatCard, { label: t("cleanup_selected"), value: selected.size, color: selected.size > 0 ? "#22C55E" : "#6B7280" })
2586
+ ] }),
2587
+ confirmClean && /* @__PURE__ */ jsx21(Box19, { marginY: 1, children: /* @__PURE__ */ jsx21(
2588
+ ConfirmDialog,
2589
+ {
2590
+ message: t("cleanup_confirmUninstall", { count: selected.size }),
2591
+ onConfirm: () => {
2592
+ setConfirmClean(false);
2593
+ hasRefreshed.current = false;
2594
+ const names = Array.from(selected);
2595
+ setFailedNames(names);
2596
+ void stream.run(["uninstall", ...names]);
2597
+ },
2598
+ onCancel: () => setConfirmClean(false)
2599
+ }
2600
+ ) }),
2601
+ candidates.length === 0 && !confirmClean && /* @__PURE__ */ jsx21(Box19, { borderStyle: "round", borderColor: "#22C55E", paddingX: 2, paddingY: 0, children: /* @__PURE__ */ jsxs21(Text21, { color: "#22C55E", bold: true, children: [
2602
+ "\u2714",
2603
+ " ",
2604
+ t("cleanup_systemClean")
2605
+ ] }) }),
2606
+ candidates.length > 0 && !confirmClean && /* @__PURE__ */ jsxs21(Box19, { flexDirection: "column", children: [
2607
+ candidates.map((c, i) => {
2608
+ const isCurrent = i === cursor;
2609
+ const isSelected = selected.has(c.name);
2610
+ return /* @__PURE__ */ jsxs21(Box19, { gap: 1, children: [
2611
+ /* @__PURE__ */ jsx21(Text21, { color: isCurrent ? "#22C55E" : "#9CA3AF", children: isCurrent ? "\u25B6" : " " }),
2612
+ /* @__PURE__ */ jsx21(Text21, { color: isSelected ? "#22C55E" : "#9CA3AF", children: isSelected ? "\u2611" : "\u2610" }),
2613
+ /* @__PURE__ */ jsx21(Text21, { bold: isCurrent, inverse: isCurrent, color: isCurrent ? "#F9FAFB" : "#9CA3AF", children: c.name }),
2614
+ /* @__PURE__ */ jsx21(Text21, { color: "#F59E0B", children: c.diskUsageFormatted }),
2615
+ /* @__PURE__ */ jsxs21(Text21, { color: "#6B7280", children: [
2616
+ "[",
2617
+ c.reason,
2618
+ "]"
2619
+ ] })
2620
+ ] }, c.name);
2621
+ }),
2622
+ /* @__PURE__ */ jsx21(Box19, { marginTop: 1, children: /* @__PURE__ */ jsxs21(Text21, { color: "#F9FAFB", bold: true, children: [
2623
+ cursor + 1,
2624
+ "/",
2625
+ candidates.length
2626
+ ] }) })
2627
+ ] })
2628
+ ] });
2629
+ }
2630
+
2631
+ // src/views/history.tsx
2632
+ import { useEffect as useEffect13, useState as useState10, useMemo as useMemo3 } from "react";
2633
+ import { Box as Box20, Text as Text22, useInput as useInput11 } from "ink";
2634
+
2635
+ // src/stores/history-store.ts
2636
+ import { create as create6 } from "zustand";
2637
+ var useHistoryStore = create6((set) => ({
2638
+ entries: [],
2639
+ loading: false,
2640
+ error: null,
2641
+ fetchHistory: async () => {
2642
+ set({ loading: true, error: null });
2643
+ try {
2644
+ const entries = await loadHistory();
2645
+ set({ entries, loading: false });
2646
+ } catch (err) {
2647
+ set({ loading: false, error: err instanceof Error ? err.message : String(err) });
2648
+ }
2649
+ },
2650
+ logAction: async (action, packageName, success, error = null) => {
2651
+ await appendEntry(action, packageName, success, error);
2652
+ const entries = await loadHistory();
2653
+ set({ entries });
2654
+ },
2655
+ clearHistory: async () => {
2656
+ await clearHistory();
2657
+ set({ entries: [] });
2658
+ }
2659
+ }));
2660
+
2661
+ // src/views/history.tsx
2662
+ import { jsx as jsx22, jsxs as jsxs22 } from "react/jsx-runtime";
2663
+ var ACTION_ICONS = {
2664
+ install: { icon: "+", color: "#22C55E" },
2665
+ uninstall: { icon: "-", color: "#EF4444" },
2666
+ upgrade: { icon: "\u2191", color: "#06B6D4" },
2667
+ "upgrade-all": { icon: "\u21C8", color: "#06B6D4" }
2668
+ };
2669
+ var ACTION_LABEL_KEYS = {
2670
+ install: "history_actionInstall",
2671
+ uninstall: "history_actionUninstall",
2672
+ upgrade: "history_actionUpgrade",
2673
+ "upgrade-all": "history_actionUpgradeAll"
2674
+ };
2675
+ var FILTERS = ["all", "install", "uninstall", "upgrade", "upgrade-all"];
2676
+ function HistoryView() {
2677
+ const { entries, loading, error, fetchHistory, clearHistory: clearHistory2 } = useHistoryStore();
2678
+ const [cursor, setCursor] = useState10(0);
2679
+ const [filter, setFilter] = useState10("all");
2680
+ const [searchQuery, setSearchQuery] = useState10("");
2681
+ const [isSearching, setIsSearching] = useState10(false);
2682
+ const [confirmClear, setConfirmClear] = useState10(false);
2683
+ const [confirmReplay, setConfirmReplay] = useState10(null);
2684
+ const stream = useBrewStream();
2685
+ const debouncedQuery = useDebounce(searchQuery, 200);
2686
+ const { openModal, closeModal } = useModalStore();
2687
+ useEffect13(() => {
2688
+ fetchHistory();
2689
+ }, []);
2690
+ useEffect13(() => {
2691
+ if (isSearching) {
2692
+ openModal();
2693
+ return () => {
2694
+ closeModal();
2695
+ };
2696
+ }
2697
+ return void 0;
2698
+ }, [isSearching]);
2699
+ const filtered = useMemo3(() => {
2700
+ let result = entries;
2701
+ if (filter !== "all") {
2702
+ result = result.filter((e) => e.action === filter);
2703
+ }
2704
+ if (debouncedQuery) {
2705
+ const q = debouncedQuery.toLowerCase();
2706
+ result = result.filter((e) => e.packageName?.toLowerCase().includes(q));
2707
+ }
2708
+ return result;
2709
+ }, [entries, filter, debouncedQuery]);
2710
+ useInput11((input, key) => {
2711
+ if (confirmClear || confirmReplay || stream.isRunning) return;
2712
+ if (isSearching) {
2713
+ if (key.escape) {
2714
+ setIsSearching(false);
2715
+ setSearchQuery("");
2716
+ }
2717
+ return;
2718
+ }
2719
+ if (input === "/") {
2720
+ setIsSearching(true);
2721
+ return;
2722
+ }
2723
+ if (input === "f") {
2724
+ const idx = FILTERS.indexOf(filter);
2725
+ setFilter(FILTERS[(idx + 1) % FILTERS.length]);
2726
+ setCursor(0);
2727
+ return;
2728
+ }
2729
+ if (input === "c" && entries.length > 0) {
2730
+ setConfirmClear(true);
2731
+ return;
2732
+ }
2733
+ if (key.return && filtered[cursor]) {
2734
+ setConfirmReplay(filtered[cursor]);
2735
+ return;
2736
+ }
2737
+ if (input === "j" || key.downArrow) setCursor((c) => Math.min(c + 1, Math.max(0, filtered.length - 1)));
2738
+ else if (input === "k" || key.upArrow) setCursor((c) => Math.max(c - 1, 0));
2739
+ });
2740
+ if (loading) return /* @__PURE__ */ jsx22(Loading, { message: t("loading_history") });
2741
+ if (error) return /* @__PURE__ */ jsx22(ErrorMessage, { message: error });
2742
+ const MAX_VISIBLE_ROWS = 20;
2743
+ const start = Math.max(0, cursor - Math.floor(MAX_VISIBLE_ROWS / 2));
2744
+ const visible = filtered.slice(start, start + MAX_VISIBLE_ROWS);
2745
+ return /* @__PURE__ */ jsxs22(Box20, { flexDirection: "column", children: [
2746
+ /* @__PURE__ */ jsxs22(Box20, { gap: 2, marginBottom: 1, children: [
2747
+ /* @__PURE__ */ jsx22(SectionHeader, { emoji: "\u{1F4DC}", title: t("history_title", { count: filtered.length }), gradient: GRADIENTS.gold }),
2748
+ /* @__PURE__ */ jsx22(Text22, { color: filter === "all" ? "#F9FAFB" : "#FFD700", children: t("history_filterLabel", { filter }) })
2749
+ ] }),
2750
+ isSearching && /* @__PURE__ */ jsx22(Box20, { marginBottom: 1, children: /* @__PURE__ */ jsx22(SearchInput, { defaultValue: searchQuery, onChange: setSearchQuery, placeholder: t("history_searchPlaceholder"), isActive: true }) }),
2751
+ confirmClear && /* @__PURE__ */ jsx22(
2752
+ ConfirmDialog,
2753
+ {
2754
+ message: t("history_confirmClear", { count: entries.length }),
2755
+ onConfirm: () => {
2756
+ void clearHistory2();
2757
+ setConfirmClear(false);
2758
+ },
2759
+ onCancel: () => setConfirmClear(false)
2760
+ }
2761
+ ),
2762
+ confirmReplay && /* @__PURE__ */ jsx22(
2763
+ ConfirmDialog,
2764
+ {
2765
+ message: confirmReplay.action === "upgrade-all" ? t("history_replayAll") : t("history_confirmReplay", { action: t(ACTION_LABEL_KEYS[confirmReplay.action]), name: confirmReplay.packageName ?? "" }),
2766
+ onConfirm: async () => {
2767
+ const entry = confirmReplay;
2768
+ setConfirmReplay(null);
2769
+ let args;
2770
+ switch (entry.action) {
2771
+ case "install":
2772
+ args = ["install", entry.packageName];
2773
+ break;
2774
+ case "uninstall":
2775
+ args = ["uninstall", entry.packageName];
2776
+ break;
2777
+ case "upgrade":
2778
+ args = ["upgrade", entry.packageName];
2779
+ break;
2780
+ case "upgrade-all":
2781
+ args = ["upgrade"];
2782
+ break;
2783
+ }
2784
+ await stream.run(args);
2785
+ void fetchHistory();
2786
+ },
2787
+ onCancel: () => setConfirmReplay(null)
2788
+ }
2789
+ ),
2790
+ (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") }) }),
2791
+ filtered.length === 0 && !confirmClear && /* @__PURE__ */ jsx22(Text22, { color: "#6B7280", italic: true, children: filter !== "all" ? t("history_noEntriesFor", { filter }) : t("history_noEntries") }),
2792
+ filtered.length > 0 && !confirmClear && /* @__PURE__ */ jsxs22(Box20, { flexDirection: "column", children: [
2793
+ start > 0 && /* @__PURE__ */ jsxs22(Text22, { color: "#6B7280", dimColor: true, children: [
2794
+ " ",
2795
+ t("scroll_moreAbove", { count: start })
2796
+ ] }),
2797
+ visible.map((entry, i) => {
2798
+ const idx = start + i;
2799
+ const isCurrent = idx === cursor;
2800
+ const { icon, color } = ACTION_ICONS[entry.action];
2801
+ const ts = new Date(entry.timestamp).getTime() / 1e3;
2802
+ return /* @__PURE__ */ jsxs22(Box20, { gap: 1, children: [
2803
+ /* @__PURE__ */ jsx22(Text22, { color: isCurrent ? "#22C55E" : "#9CA3AF", children: isCurrent ? "\u25B6" : " " }),
2804
+ /* @__PURE__ */ jsx22(Text22, { color, bold: true, children: icon }),
2805
+ /* @__PURE__ */ jsx22(Text22, { bold: isCurrent, inverse: isCurrent, color: isCurrent ? "#F9FAFB" : "#9CA3AF", children: t(ACTION_LABEL_KEYS[entry.action]).padEnd(12) }),
2806
+ /* @__PURE__ */ jsx22(Text22, { color: "#F9FAFB", children: entry.packageName ?? t("history_all") }),
2807
+ entry.success ? /* @__PURE__ */ jsx22(StatusBadge, { label: t("badge_ok"), variant: "success" }) : /* @__PURE__ */ jsx22(StatusBadge, { label: t("badge_fail"), variant: "error" }),
2808
+ /* @__PURE__ */ jsx22(Text22, { color: "#9CA3AF", children: formatRelativeTime(ts) })
2809
+ ] }, entry.id);
2810
+ }),
2811
+ start + MAX_VISIBLE_ROWS < filtered.length && /* @__PURE__ */ jsxs22(Text22, { color: "#6B7280", dimColor: true, children: [
2812
+ " ",
2813
+ t("scroll_moreBelow", { count: filtered.length - start - MAX_VISIBLE_ROWS })
2814
+ ] }),
2815
+ /* @__PURE__ */ jsx22(Box20, { marginTop: 1, children: /* @__PURE__ */ jsxs22(Text22, { color: "#F9FAFB", bold: true, children: [
2816
+ cursor + 1,
2817
+ "/",
2818
+ filtered.length
2819
+ ] }) })
2820
+ ] })
2821
+ ] });
2822
+ }
2823
+
2824
+ // src/views/security-audit.tsx
2825
+ import { useEffect as useEffect14, useState as useState11 } from "react";
2826
+ import { Box as Box21, Text as Text23, useInput as useInput12 } from "ink";
2827
+
2828
+ // src/stores/security-store.ts
2829
+ import { create as create7 } from "zustand";
2830
+
2831
+ // src/lib/security/osv-api.ts
2832
+ var OSV_BATCH_URL = "https://api.osv.dev/v1/querybatch";
2833
+ function mapSeverity(vuln) {
2834
+ const dbSev = vuln.database_specific?.severity?.toUpperCase();
2835
+ if (dbSev && ["CRITICAL", "HIGH", "MEDIUM", "LOW"].includes(dbSev)) {
2836
+ return dbSev;
2837
+ }
2838
+ const cvss = vuln.severity?.find((s) => s.type === "CVSS_V3");
2839
+ if (cvss) {
2840
+ const score = parseFloat(cvss.score);
2841
+ if (score >= 9) return "CRITICAL";
2842
+ if (score >= 7) return "HIGH";
2843
+ if (score >= 4) return "MEDIUM";
2844
+ return "LOW";
2845
+ }
2846
+ return "UNKNOWN";
2847
+ }
2848
+ function getFixedVersion(vuln) {
2849
+ for (const affected of vuln.affected ?? []) {
2850
+ for (const range of affected.ranges ?? []) {
2851
+ for (const event of range.events ?? []) {
2852
+ if (event.fixed) return event.fixed;
2853
+ }
2854
+ }
2855
+ }
2856
+ return null;
2857
+ }
2858
+ var BATCH_SIZE = 100;
2859
+ async function queryBatch(packages, queries) {
2860
+ const res = await fetch(OSV_BATCH_URL, {
2861
+ method: "POST",
2862
+ headers: { "Content-Type": "application/json" },
2863
+ body: JSON.stringify({ queries })
2864
+ });
2865
+ if (!res.ok) {
2866
+ if (res.status === 400 && queries.length > 1) {
2867
+ return queryOneByOne(packages);
2868
+ }
2869
+ throw new Error(`OSV API error: ${res.status} ${res.statusText}`);
2870
+ }
2871
+ const data = await res.json();
2872
+ const result = /* @__PURE__ */ new Map();
2873
+ for (let i = 0; i < packages.length; i++) {
2874
+ const vulns = data.results[i]?.vulns;
2875
+ if (!vulns || vulns.length === 0) continue;
2876
+ result.set(
2877
+ packages[i].name,
2878
+ vulns.map((v) => ({
2879
+ id: v.id,
2880
+ summary: v.summary ?? "No description available",
2881
+ severity: mapSeverity(v),
2882
+ fixedVersion: getFixedVersion(v),
2883
+ references: v.references?.map((r) => r.url) ?? []
2884
+ }))
2885
+ );
2886
+ }
2887
+ return result;
2888
+ }
2889
+ async function queryOneByOne(packages) {
2890
+ const result = /* @__PURE__ */ new Map();
2891
+ for (const pkg of packages) {
2892
+ try {
2893
+ const partial = await queryBatch(
2894
+ [pkg],
2895
+ [{ package: { name: pkg.name, ecosystem: "Homebrew" }, version: pkg.version }]
2896
+ );
2897
+ for (const [k, v] of partial) result.set(k, v);
2898
+ } catch {
2899
+ }
2900
+ }
2901
+ return result;
2902
+ }
2903
+ async function queryVulnerabilities(packages) {
2904
+ const result = /* @__PURE__ */ new Map();
2905
+ for (let i = 0; i < packages.length; i += BATCH_SIZE) {
2906
+ const batch = packages.slice(i, i + BATCH_SIZE);
2907
+ const queries = batch.map((p) => ({
2908
+ package: { name: p.name, ecosystem: "Homebrew" },
2909
+ version: p.version
2910
+ }));
2911
+ const partial = await queryBatch(batch, queries);
2912
+ for (const [k, v] of partial) result.set(k, v);
2913
+ }
2914
+ return result;
2915
+ }
2916
+
2917
+ // src/lib/security/audit-runner.ts
2918
+ var SEVERITY_ORDER = {
2919
+ CRITICAL: 4,
2920
+ HIGH: 3,
2921
+ MEDIUM: 2,
2922
+ LOW: 1,
2923
+ UNKNOWN: 0
2924
+ };
2925
+ async function runSecurityAudit(formulae, casks) {
2926
+ requirePro();
2927
+ const packages = [];
2928
+ for (const f of formulae) {
2929
+ const version = f.installed[0]?.version ?? f.versions.stable;
2930
+ packages.push({ name: f.name, version });
2931
+ }
2932
+ for (const c of casks) {
2933
+ if (c.installed) {
2934
+ packages.push({ name: c.token, version: c.installed });
2935
+ }
2936
+ }
2937
+ const vulnMap = await queryVulnerabilities(packages);
2938
+ const results = [];
2939
+ let criticalCount = 0;
2940
+ let highCount = 0;
2941
+ let mediumCount = 0;
2942
+ let lowCount = 0;
2943
+ for (const [name, vulns] of vulnMap) {
2944
+ const pkg = packages.find((p) => p.name === name);
2945
+ if (!pkg) continue;
2946
+ const maxSeverity = vulns.reduce(
2947
+ (max, v) => SEVERITY_ORDER[v.severity] > SEVERITY_ORDER[max] ? v.severity : max,
2948
+ "UNKNOWN"
2949
+ );
2950
+ results.push({
2951
+ packageName: name,
2952
+ installedVersion: pkg.version,
2953
+ vulnerabilities: vulns,
2954
+ maxSeverity
2955
+ });
2956
+ for (const v of vulns) {
2957
+ if (v.severity === "CRITICAL") criticalCount++;
2958
+ else if (v.severity === "HIGH") highCount++;
2959
+ else if (v.severity === "MEDIUM") mediumCount++;
2960
+ else if (v.severity === "LOW") lowCount++;
2961
+ }
2962
+ }
2963
+ results.sort((a, b) => SEVERITY_ORDER[b.maxSeverity] - SEVERITY_ORDER[a.maxSeverity]);
2964
+ return {
2965
+ scannedAt: (/* @__PURE__ */ new Date()).toISOString(),
2966
+ totalPackages: packages.length,
2967
+ vulnerablePackages: results.length,
2968
+ criticalCount,
2969
+ highCount,
2970
+ mediumCount,
2971
+ lowCount,
2972
+ results
2973
+ };
2974
+ }
2975
+
2976
+ // src/stores/security-store.ts
2977
+ var useSecurityStore = create7((set) => ({
2978
+ summary: null,
2979
+ loading: false,
2980
+ error: null,
2981
+ scan: async () => {
2982
+ set({ loading: true, error: null });
2983
+ try {
2984
+ const brewState = useBrewStore.getState();
2985
+ if (brewState.formulae.length === 0) {
2986
+ await brewState.fetchInstalled();
2987
+ }
2988
+ const { formulae, casks } = useBrewStore.getState();
2989
+ const summary = await runSecurityAudit(formulae, casks);
2990
+ set({ summary, loading: false });
2991
+ } catch (err) {
2992
+ set({ error: err instanceof Error ? err.message : String(err), loading: false });
2993
+ }
2994
+ }
2995
+ }));
2996
+
2997
+ // src/views/security-audit.tsx
2998
+ import { jsx as jsx23, jsxs as jsxs23 } from "react/jsx-runtime";
2999
+ var SEVERITY_COLORS = {
3000
+ CRITICAL: "#EF4444",
3001
+ HIGH: "#EF4444",
3002
+ MEDIUM: "#F59E0B",
3003
+ LOW: "#6B7280",
3004
+ UNKNOWN: "#6B7280"
3005
+ };
3006
+ var SEVERITY_BADGE = {
3007
+ CRITICAL: "error",
3008
+ HIGH: "error",
3009
+ MEDIUM: "warning",
3010
+ LOW: "muted",
3011
+ UNKNOWN: "muted"
3012
+ };
3013
+ function SecurityAuditView() {
3014
+ const { summary, loading, error, scan } = useSecurityStore();
3015
+ const [cursor, setCursor] = useState11(0);
3016
+ const [expandedPkg, setExpandedPkg] = useState11(null);
3017
+ const [confirmUpgrade, setConfirmUpgrade] = useState11(null);
3018
+ const stream = useBrewStream();
3019
+ useEffect14(() => {
3020
+ scan();
3021
+ }, []);
3022
+ const results = summary?.results ?? [];
3023
+ useInput12((input, key) => {
3024
+ if (confirmUpgrade || stream.isRunning) return;
3025
+ if (input === "r") {
3026
+ void scan();
3027
+ return;
3028
+ }
3029
+ if (input === "u" && results[cursor]) {
3030
+ setConfirmUpgrade(results[cursor].packageName);
3031
+ return;
3032
+ }
3033
+ if (input === "j" || key.downArrow) setCursor((c) => Math.min(c + 1, Math.max(0, results.length - 1)));
3034
+ else if (input === "k" || key.upArrow) setCursor((c) => Math.max(c - 1, 0));
3035
+ else if (key.return && results[cursor]) {
3036
+ setExpandedPkg(expandedPkg === results[cursor].packageName ? null : results[cursor].packageName);
3037
+ }
3038
+ });
3039
+ if (loading) return /* @__PURE__ */ jsx23(Loading, { message: t("loading_security") });
3040
+ if (error) return /* @__PURE__ */ jsx23(ErrorMessage, { message: error });
3041
+ return /* @__PURE__ */ jsxs23(Box21, { flexDirection: "column", children: [
3042
+ /* @__PURE__ */ jsx23(SectionHeader, { emoji: "\u{1F6E1}\uFE0F", title: t("security_title"), gradient: GRADIENTS.ocean }),
3043
+ summary && /* @__PURE__ */ jsxs23(Box21, { gap: 1, marginY: 1, children: [
3044
+ /* @__PURE__ */ jsx23(StatCard, { label: t("security_scanned"), value: summary.totalPackages, color: "#06B6D4" }),
3045
+ /* @__PURE__ */ jsx23(
3046
+ StatCard,
3047
+ {
3048
+ label: t("security_vulnerable"),
3049
+ value: summary.vulnerablePackages,
3050
+ color: summary.vulnerablePackages > 0 ? "#EF4444" : "#22C55E"
3051
+ }
3052
+ ),
3053
+ summary.criticalCount > 0 && /* @__PURE__ */ jsx23(StatCard, { label: t("security_critical"), value: summary.criticalCount, color: "#EF4444" }),
3054
+ summary.highCount > 0 && /* @__PURE__ */ jsx23(StatCard, { label: t("security_high"), value: summary.highCount, color: "#EF4444" }),
3055
+ summary.mediumCount > 0 && /* @__PURE__ */ jsx23(StatCard, { label: t("security_medium"), value: summary.mediumCount, color: "#F59E0B" })
3056
+ ] }),
3057
+ 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: [
3058
+ "\u2714",
3059
+ " ",
3060
+ t("security_noVulns")
3061
+ ] }) }) }),
3062
+ confirmUpgrade && /* @__PURE__ */ jsx23(Box21, { marginY: 1, children: /* @__PURE__ */ jsx23(
3063
+ ConfirmDialog,
3064
+ {
3065
+ message: t("security_confirmUpgrade", { name: confirmUpgrade }),
3066
+ onConfirm: async () => {
3067
+ const name = confirmUpgrade;
3068
+ setConfirmUpgrade(null);
3069
+ await stream.run(["upgrade", name]);
3070
+ void scan();
3071
+ },
3072
+ onCancel: () => setConfirmUpgrade(null)
3073
+ }
3074
+ ) }),
3075
+ (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") }) }),
3076
+ results.length > 0 && /* @__PURE__ */ jsxs23(Box21, { flexDirection: "column", marginTop: 1, children: [
3077
+ results.map((pkg, i) => {
3078
+ const isCurrent = i === cursor;
3079
+ const isExpanded = expandedPkg === pkg.packageName;
3080
+ return /* @__PURE__ */ jsxs23(Box21, { flexDirection: "column", children: [
3081
+ /* @__PURE__ */ jsxs23(Box21, { gap: 1, children: [
3082
+ /* @__PURE__ */ jsx23(Text23, { color: isCurrent ? "#22C55E" : "#9CA3AF", children: isCurrent ? "\u25B6" : " " }),
3083
+ /* @__PURE__ */ jsx23(StatusBadge, { label: pkg.maxSeverity, variant: SEVERITY_BADGE[pkg.maxSeverity] }),
3084
+ /* @__PURE__ */ jsx23(Text23, { bold: isCurrent, inverse: isCurrent, color: isCurrent ? "#F9FAFB" : "#9CA3AF", children: pkg.packageName }),
3085
+ /* @__PURE__ */ jsx23(Text23, { color: "#9CA3AF", children: pkg.installedVersion }),
3086
+ /* @__PURE__ */ jsx23(Text23, { color: "#9CA3AF", children: tp("plural_vulns", pkg.vulnerabilities.length) }),
3087
+ /* @__PURE__ */ jsx23(Text23, { color: "#9CA3AF", children: isExpanded ? "\u25BC" : "\u25B6" })
3088
+ ] }),
3089
+ isExpanded && /* @__PURE__ */ jsx23(Box21, { flexDirection: "column", paddingLeft: 4, marginBottom: 1, children: pkg.vulnerabilities.map((vuln) => /* @__PURE__ */ jsxs23(Box21, { flexDirection: "column", marginBottom: 1, children: [
3090
+ /* @__PURE__ */ jsxs23(Box21, { gap: 1, children: [
3091
+ /* @__PURE__ */ jsx23(Text23, { color: SEVERITY_COLORS[vuln.severity], bold: true, children: vuln.id }),
3092
+ /* @__PURE__ */ jsxs23(Text23, { color: "#9CA3AF", children: [
3093
+ "[",
3094
+ vuln.severity,
3095
+ "]"
3096
+ ] })
3097
+ ] }),
3098
+ /* @__PURE__ */ jsx23(Text23, { color: "#9CA3AF", wrap: "wrap", children: vuln.summary }),
3099
+ vuln.fixedVersion && /* @__PURE__ */ jsx23(Text23, { color: "#22C55E", children: t("security_fixedIn", { version: vuln.fixedVersion }) })
3100
+ ] }, vuln.id)) })
3101
+ ] }, pkg.packageName);
3102
+ }),
3103
+ /* @__PURE__ */ jsx23(Box21, { marginTop: 1, children: /* @__PURE__ */ jsxs23(Text23, { color: "#F9FAFB", bold: true, children: [
3104
+ cursor + 1,
3105
+ "/",
3106
+ results.length
3107
+ ] }) })
3108
+ ] })
3109
+ ] });
3110
+ }
3111
+
3112
+ // src/views/account.tsx
3113
+ import { useState as useState12 } from "react";
3114
+ import { Box as Box22, Text as Text24, useInput as useInput13 } from "ink";
3115
+ import { Fragment as Fragment5, jsx as jsx24, jsxs as jsxs24 } from "react/jsx-runtime";
3116
+ function AccountView() {
3117
+ const { status, license, deactivate: deactivate2, degradation } = useLicenseStore();
3118
+ const [confirmDeactivate, setConfirmDeactivate] = useState12(false);
3119
+ const [deactivating, setDeactivating] = useState12(false);
3120
+ useInput13((input) => {
3121
+ if (confirmDeactivate || deactivating) return;
3122
+ if (input === "d" && status === "pro") {
3123
+ setConfirmDeactivate(true);
3124
+ }
3125
+ });
3126
+ const maskKey = (key) => {
3127
+ if (key.length <= 8) return key;
3128
+ return key.slice(0, 4) + "-****-****-" + key.slice(-4);
3129
+ };
3130
+ return /* @__PURE__ */ jsxs24(Box22, { flexDirection: "column", children: [
3131
+ /* @__PURE__ */ jsx24(SectionHeader, { emoji: "\u{1F464}", title: t("account_title"), gradient: GRADIENTS.gold }),
3132
+ confirmDeactivate && /* @__PURE__ */ jsx24(Box22, { marginY: 1, children: /* @__PURE__ */ jsx24(
3133
+ ConfirmDialog,
3134
+ {
3135
+ message: t("account_confirmDeactivate"),
3136
+ onConfirm: async () => {
3137
+ setConfirmDeactivate(false);
3138
+ setDeactivating(true);
3139
+ await deactivate2();
3140
+ setDeactivating(false);
3141
+ },
3142
+ onCancel: () => setConfirmDeactivate(false)
3143
+ }
3144
+ ) }),
3145
+ /* @__PURE__ */ jsxs24(Box22, { flexDirection: "column", marginTop: 1, paddingLeft: 2, children: [
3146
+ /* @__PURE__ */ jsxs24(Box22, { gap: 1, children: [
3147
+ /* @__PURE__ */ jsx24(Text24, { color: "#9CA3AF", children: t("account_statusLabel") }),
3148
+ status === "pro" && /* @__PURE__ */ jsx24(Text24, { color: "#22C55E", bold: true, children: t("account_pro") }),
3149
+ status === "free" && /* @__PURE__ */ jsx24(Text24, { color: "#9CA3AF", children: t("account_free") }),
3150
+ status === "expired" && /* @__PURE__ */ jsx24(Text24, { color: "#EF4444", children: t("account_expired") }),
3151
+ status === "validating" && /* @__PURE__ */ jsx24(Text24, { color: "#38BDF8", children: t("account_validating") })
3152
+ ] }),
3153
+ (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", {
3154
+ days: Math.floor((Date.now() - new Date(license.lastValidatedAt).getTime()) / (24 * 60 * 60 * 1e3))
3155
+ }) }) }),
3156
+ license && /* @__PURE__ */ jsxs24(Fragment5, { children: [
3157
+ /* @__PURE__ */ jsxs24(Box22, { gap: 1, children: [
3158
+ /* @__PURE__ */ jsx24(Text24, { color: "#9CA3AF", children: t("account_emailLabel") }),
3159
+ /* @__PURE__ */ jsx24(Text24, { children: license.customerEmail })
3160
+ ] }),
3161
+ /* @__PURE__ */ jsxs24(Box22, { gap: 1, children: [
3162
+ /* @__PURE__ */ jsx24(Text24, { color: "#9CA3AF", children: t("account_nameLabel") }),
3163
+ /* @__PURE__ */ jsx24(Text24, { children: license.customerName })
3164
+ ] }),
3165
+ /* @__PURE__ */ jsxs24(Box22, { gap: 1, children: [
3166
+ /* @__PURE__ */ jsx24(Text24, { color: "#9CA3AF", children: t("account_planLabel") }),
3167
+ /* @__PURE__ */ jsx24(Text24, { color: "#22C55E", bold: true, children: "Pro" })
3168
+ ] }),
3169
+ /* @__PURE__ */ jsxs24(Box22, { gap: 1, children: [
3170
+ /* @__PURE__ */ jsx24(Text24, { color: "#9CA3AF", children: t("account_keyLabel") }),
3171
+ /* @__PURE__ */ jsx24(Text24, { children: maskKey(license.key) })
3172
+ ] }),
3173
+ license.expiresAt && /* @__PURE__ */ jsxs24(Box22, { gap: 1, children: [
3174
+ /* @__PURE__ */ jsx24(Text24, { color: "#9CA3AF", children: t("account_expiresLabel") }),
3175
+ /* @__PURE__ */ jsx24(Text24, { children: new Date(license.expiresAt).toLocaleDateString() })
3176
+ ] }),
3177
+ /* @__PURE__ */ jsxs24(Box22, { gap: 1, children: [
3178
+ /* @__PURE__ */ jsx24(Text24, { color: "#9CA3AF", children: t("account_activatedLabel") }),
3179
+ /* @__PURE__ */ jsx24(Text24, { children: new Date(license.activatedAt).toLocaleDateString() })
3180
+ ] })
3181
+ ] }),
3182
+ status === "free" && /* @__PURE__ */ jsxs24(Box22, { flexDirection: "column", marginTop: 2, borderStyle: "round", borderColor: "#FF6B2B", paddingX: 2, paddingY: 1, children: [
3183
+ /* @__PURE__ */ jsxs24(Text24, { bold: true, color: "#FF6B2B", children: [
3184
+ "\u2B50",
3185
+ " ",
3186
+ t("account_upgradeTitle")
3187
+ ] }),
3188
+ /* @__PURE__ */ jsx24(Text24, { children: " " }),
3189
+ /* @__PURE__ */ jsx24(Text24, { children: t("account_unlockDesc") }),
3190
+ /* @__PURE__ */ jsx24(Text24, { color: "#06B6D4", bold: true, children: t("account_pricing") }),
3191
+ /* @__PURE__ */ jsx24(Text24, { children: " " }),
3192
+ /* @__PURE__ */ jsxs24(Text24, { color: "#9CA3AF", children: [
3193
+ t("account_runActivate"),
3194
+ " ",
3195
+ /* @__PURE__ */ jsx24(Text24, { color: "#22C55E", bold: true, children: t("account_activateCmd") })
3196
+ ] })
3197
+ ] }),
3198
+ 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") }) }) }),
3199
+ deactivating && /* @__PURE__ */ jsx24(Text24, { color: "#38BDF8", children: t("account_deactivating") })
3200
+ ] }),
3201
+ /* @__PURE__ */ jsx24(Box22, { marginTop: 2, children: /* @__PURE__ */ jsxs24(Text24, { color: "#6B7280", children: [
3202
+ status === "pro" ? `d:${t("hint_deactivate")}` : "",
3203
+ " ",
3204
+ t("app_version", { version: "0.1.0" })
3205
+ ] }) })
3206
+ ] });
3207
+ }
3208
+
3209
+ // src/app.tsx
3210
+ import { jsx as jsx25 } from "react/jsx-runtime";
3211
+ function App() {
3212
+ const { exit } = useApp();
3213
+ const currentView = useNavigationStore((s) => s.currentView);
3214
+ const isPro = useLicenseStore((s) => s.isPro);
3215
+ const initLicense = useLicenseStore((s) => s.initialize);
3216
+ useEffect15(() => {
3217
+ initLicense();
3218
+ }, []);
3219
+ useGlobalKeyboard({ onQuit: exit });
3220
+ const renderView = () => {
3221
+ if (isProView(currentView) && !isPro()) {
3222
+ return /* @__PURE__ */ jsx25(UpgradePrompt, { viewId: currentView });
3223
+ }
3224
+ switch (currentView) {
3225
+ case "dashboard":
3226
+ return /* @__PURE__ */ jsx25(DashboardView, {});
3227
+ case "installed":
3228
+ return /* @__PURE__ */ jsx25(InstalledView, {});
3229
+ case "search":
3230
+ return /* @__PURE__ */ jsx25(SearchView, {});
3231
+ case "outdated":
3232
+ return /* @__PURE__ */ jsx25(OutdatedView, {});
3233
+ case "package-info":
3234
+ return /* @__PURE__ */ jsx25(PackageInfoView, {});
3235
+ case "services":
3236
+ return /* @__PURE__ */ jsx25(ServicesView, {});
3237
+ case "doctor":
3238
+ return /* @__PURE__ */ jsx25(DoctorView, {});
3239
+ case "profiles":
3240
+ return /* @__PURE__ */ jsx25(ProfilesView, {});
3241
+ case "smart-cleanup":
3242
+ return /* @__PURE__ */ jsx25(SmartCleanupView, {});
3243
+ case "history":
3244
+ return /* @__PURE__ */ jsx25(HistoryView, {});
3245
+ case "security-audit":
3246
+ return /* @__PURE__ */ jsx25(SecurityAuditView, {});
3247
+ case "account":
3248
+ return /* @__PURE__ */ jsx25(AccountView, {});
3249
+ }
3250
+ };
3251
+ return /* @__PURE__ */ jsx25(AppLayout, { children: renderView() });
3252
+ }
3253
+
3254
+ // src/index.tsx
3255
+ import { jsx as jsx26 } from "react/jsx-runtime";
3256
+ var [, , command, arg] = process.argv;
3257
+ async function runCli() {
3258
+ await ensureDataDirs();
3259
+ if (command === "activate") {
3260
+ const key = arg?.trim() ?? "";
3261
+ if (!key) {
3262
+ console.error(t("cli_usageActivate"));
3263
+ process.exit(1);
3264
+ }
3265
+ try {
3266
+ const license = await activate(key);
3267
+ console.log(t("cli_activated", { email: license.customerEmail }));
3268
+ console.log(t("cli_planPro"));
3269
+ if (license.expiresAt) {
3270
+ console.log(t("cli_expires", { date: new Date(license.expiresAt).toLocaleDateString() }));
3271
+ }
3272
+ } catch (err) {
3273
+ console.error(t("cli_activationFailed", { error: err instanceof Error ? err.message : String(err) }));
3274
+ process.exit(1);
3275
+ }
3276
+ return;
3277
+ }
3278
+ if (command === "deactivate") {
3279
+ const license = await loadLicense();
3280
+ if (!license) {
3281
+ console.log(t("cli_noLicense"));
3282
+ return;
3283
+ }
3284
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
3285
+ const answer = await rl.question(t("cli_confirmDeactivate"));
3286
+ rl.close();
3287
+ if (answer.toLowerCase() !== "y" && answer.toLowerCase() !== "s") {
3288
+ console.log(t("cli_deactivateCancelled"));
3289
+ return;
3290
+ }
3291
+ await deactivate(license);
3292
+ console.log(t("cli_deactivated"));
3293
+ return;
3294
+ }
3295
+ if (command === "status") {
3296
+ const license = await loadLicense();
3297
+ if (!license) {
3298
+ console.log(t("cli_planFree"));
3299
+ console.log(t("cli_upgradeHint"));
3300
+ } else {
3301
+ console.log(t("cli_planPro"));
3302
+ console.log(t("cli_email", { email: license.customerEmail }));
3303
+ console.log(t("cli_status", { status: license.status }));
3304
+ if (license.expiresAt) {
3305
+ console.log(t("cli_expires", { date: new Date(license.expiresAt).toLocaleDateString() }));
3306
+ }
3307
+ }
3308
+ return;
3309
+ }
3310
+ if (command === "install-brewbar") {
3311
+ const { installBrewBar } = await import("./brewbar-installer-CPCOE3MI.js");
3312
+ try {
3313
+ await installBrewBar(arg === "--force");
3314
+ console.log(t("cli_brewbarInstalled"));
3315
+ } catch (err) {
3316
+ console.error(err instanceof Error ? err.message : String(err));
3317
+ process.exit(1);
3318
+ }
3319
+ return;
3320
+ }
3321
+ if (command === "uninstall-brewbar") {
3322
+ const { uninstallBrewBar } = await import("./brewbar-installer-CPCOE3MI.js");
3323
+ try {
3324
+ await uninstallBrewBar();
3325
+ console.log(t("cli_brewbarUninstalled"));
3326
+ } catch (err) {
3327
+ console.error(err instanceof Error ? err.message : String(err));
3328
+ process.exit(1);
3329
+ }
3330
+ return;
3331
+ }
3332
+ render(/* @__PURE__ */ jsx26(App, {}));
3333
+ }
3334
+ runCli().catch((err) => {
3335
+ console.error(err instanceof Error ? err.message : String(err));
3336
+ process.exit(1);
3337
+ });
3338
+ //# sourceMappingURL=index.js.map