brew-tui 0.1.0 → 0.2.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/README.md +2 -0
- package/build/{brewbar-installer-CPCOE3MI.js → brewbar-installer-4Z2WE57I.js} +27 -5
- package/build/{chunk-P6PTN4HR.js → chunk-KXDTKY3E.js} +85 -47
- package/build/chunk-UBHTQL7T.js +76 -0
- package/build/{history-logger-LQT622M2.js → history-logger-65UF2R6F.js} +4 -3
- package/build/index.js +207 -115
- package/package.json +2 -2
- package/build/brewbar-installer-CPCOE3MI.js.map +0 -1
- package/build/chunk-3BK3B53S.js +0 -55
- package/build/chunk-3BK3B53S.js.map +0 -1
- package/build/chunk-P6PTN4HR.js.map +0 -1
- package/build/history-logger-LQT622M2.js.map +0 -1
- package/build/index.js.map +0 -1
package/build/index.js
CHANGED
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
import {
|
|
2
2
|
appendEntry,
|
|
3
3
|
clearHistory,
|
|
4
|
+
detectAction,
|
|
4
5
|
loadHistory
|
|
5
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-UBHTQL7T.js";
|
|
6
7
|
import {
|
|
7
8
|
PROFILES_DIR,
|
|
8
9
|
activate,
|
|
9
10
|
deactivate,
|
|
10
11
|
ensureDataDirs,
|
|
12
|
+
fetchWithTimeout,
|
|
11
13
|
loadLicense,
|
|
12
14
|
requirePro,
|
|
13
15
|
t,
|
|
14
16
|
tp,
|
|
15
17
|
useLicenseStore,
|
|
16
18
|
useLocaleStore
|
|
17
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-KXDTKY3E.js";
|
|
18
20
|
|
|
19
21
|
// src/index.tsx
|
|
20
22
|
import { createInterface } from "readline/promises";
|
|
@@ -28,7 +30,7 @@ import { useApp } from "ink";
|
|
|
28
30
|
import { Box as Box3 } from "ink";
|
|
29
31
|
|
|
30
32
|
// src/components/layout/header.tsx
|
|
31
|
-
import
|
|
33
|
+
import React2 from "react";
|
|
32
34
|
import { Box, Text as Text2 } from "ink";
|
|
33
35
|
|
|
34
36
|
// src/stores/navigation-store.ts
|
|
@@ -51,24 +53,25 @@ var useNavigationStore = create((set, get) => ({
|
|
|
51
53
|
currentView: "dashboard",
|
|
52
54
|
previousView: null,
|
|
53
55
|
selectedPackage: null,
|
|
54
|
-
viewHistory: [
|
|
56
|
+
viewHistory: [],
|
|
55
57
|
navigate: (view) => {
|
|
56
58
|
const { currentView, viewHistory } = get();
|
|
57
59
|
if (view === currentView) return;
|
|
58
60
|
set({
|
|
59
61
|
currentView: view,
|
|
60
62
|
previousView: currentView,
|
|
61
|
-
viewHistory: [...viewHistory.slice(-19),
|
|
63
|
+
viewHistory: [...viewHistory.slice(-19), currentView]
|
|
62
64
|
});
|
|
63
65
|
},
|
|
64
66
|
goBack: () => {
|
|
65
|
-
const {
|
|
66
|
-
if (
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
67
|
+
const { viewHistory } = get();
|
|
68
|
+
if (viewHistory.length === 0) return;
|
|
69
|
+
const prev = viewHistory[viewHistory.length - 1];
|
|
70
|
+
set({
|
|
71
|
+
currentView: prev,
|
|
72
|
+
previousView: get().currentView,
|
|
73
|
+
viewHistory: viewHistory.slice(0, -1)
|
|
74
|
+
});
|
|
72
75
|
},
|
|
73
76
|
selectPackage: (name) => set({ selectedPackage: name })
|
|
74
77
|
}));
|
|
@@ -93,6 +96,7 @@ function isProView(viewId) {
|
|
|
93
96
|
}
|
|
94
97
|
|
|
95
98
|
// src/utils/gradient.tsx
|
|
99
|
+
import React, { useMemo } from "react";
|
|
96
100
|
import { Text } from "ink";
|
|
97
101
|
import { Fragment, jsx } from "react/jsx-runtime";
|
|
98
102
|
function hexToRgb(hex) {
|
|
@@ -110,22 +114,25 @@ function interpolateColor(c1, c2, t2) {
|
|
|
110
114
|
const [r2, g2, b2] = hexToRgb(c2);
|
|
111
115
|
return rgbToHex(lerp(r1, r2, t2), lerp(g1, g2, t2), lerp(b1, b2, t2));
|
|
112
116
|
}
|
|
113
|
-
|
|
117
|
+
var GradientText = React.memo(function GradientText2({ children, colors, bold }) {
|
|
114
118
|
if (colors.length < 2) {
|
|
115
119
|
return /* @__PURE__ */ jsx(Text, { color: colors[0], bold, children });
|
|
116
120
|
}
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
}
|
|
121
|
+
const coloredChars = useMemo(() => {
|
|
122
|
+
const chars = [...children];
|
|
123
|
+
const maxIdx = Math.max(chars.length - 1, 1);
|
|
124
|
+
return chars.map((char, i) => {
|
|
125
|
+
const t2 = i / maxIdx;
|
|
126
|
+
const segment = t2 * (colors.length - 1);
|
|
127
|
+
const lower = Math.floor(segment);
|
|
128
|
+
const upper = Math.min(lower + 1, colors.length - 1);
|
|
129
|
+
const frac = segment - lower;
|
|
130
|
+
const color = interpolateColor(colors[lower], colors[upper], frac);
|
|
131
|
+
return { char, color, key: `${i}-${color}` };
|
|
132
|
+
});
|
|
133
|
+
}, [children, colors]);
|
|
134
|
+
return /* @__PURE__ */ jsx(Fragment, { children: coloredChars.map(({ char, color, key }) => /* @__PURE__ */ jsx(Text, { color, bold, children: char }, key)) });
|
|
135
|
+
});
|
|
129
136
|
var GRADIENTS = {
|
|
130
137
|
gold: ["#FFD700", "#FFA500", "#B8860B"],
|
|
131
138
|
sunset: ["#FF6B2B", "#FFD700", "#FF6B2B"],
|
|
@@ -208,7 +215,7 @@ function Header() {
|
|
|
208
215
|
const viewLabel = t(VIEW_LABEL_KEYS[view]);
|
|
209
216
|
const label = key ? `${key}:${viewLabel}` : viewLabel;
|
|
210
217
|
const isPro = isProView(view);
|
|
211
|
-
return /* @__PURE__ */ jsxs(
|
|
218
|
+
return /* @__PURE__ */ jsxs(React2.Fragment, { children: [
|
|
212
219
|
i > 0 && /* @__PURE__ */ jsxs(Text2, { color: "#4B5563", children: [
|
|
213
220
|
" ",
|
|
214
221
|
"\u2502",
|
|
@@ -233,7 +240,7 @@ function Header() {
|
|
|
233
240
|
}
|
|
234
241
|
|
|
235
242
|
// src/components/layout/footer.tsx
|
|
236
|
-
import
|
|
243
|
+
import React3 from "react";
|
|
237
244
|
import { Box as Box2, Text as Text3 } from "ink";
|
|
238
245
|
import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
239
246
|
var VIEW_HINT_DEFS = {
|
|
@@ -265,7 +272,7 @@ function Footer() {
|
|
|
265
272
|
return /* @__PURE__ */ jsxs2(Box2, { borderStyle: "single", borderTop: true, borderBottom: false, borderLeft: false, borderRight: false, borderColor: "#FFD700", paddingX: 1, flexWrap: "wrap", children: [
|
|
266
273
|
defs.map((def, i) => {
|
|
267
274
|
const key = def.length === 1 ? def[0] : `${def[0]}:${def[1]}`;
|
|
268
|
-
return /* @__PURE__ */ jsxs2(
|
|
275
|
+
return /* @__PURE__ */ jsxs2(React3.Fragment, { children: [
|
|
269
276
|
i > 0 && /* @__PURE__ */ jsxs2(Text3, { color: "#4B5563", children: [
|
|
270
277
|
" ",
|
|
271
278
|
"\u2502",
|
|
@@ -401,6 +408,12 @@ function UpgradePrompt({ viewId }) {
|
|
|
401
408
|
/* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", alignItems: "center", children: [
|
|
402
409
|
/* @__PURE__ */ jsx5(Text4, { color: "#06B6D4", bold: true, children: t("upgrade_pricing") }),
|
|
403
410
|
/* @__PURE__ */ jsx5(Text4, { children: " " }),
|
|
411
|
+
/* @__PURE__ */ jsx5(Text4, { color: "#9CA3AF", children: t("upgrade_buyAt") }),
|
|
412
|
+
/* @__PURE__ */ jsxs4(Text4, { color: "#38BDF8", bold: true, children: [
|
|
413
|
+
" ",
|
|
414
|
+
t("upgrade_buyUrl")
|
|
415
|
+
] }),
|
|
416
|
+
/* @__PURE__ */ jsx5(Text4, { children: " " }),
|
|
404
417
|
/* @__PURE__ */ jsx5(Text4, { color: "#9CA3AF", children: t("upgrade_activateWith") }),
|
|
405
418
|
/* @__PURE__ */ jsxs4(Text4, { color: "#22C55E", bold: true, children: [
|
|
406
419
|
" ",
|
|
@@ -415,7 +428,7 @@ function UpgradePrompt({ viewId }) {
|
|
|
415
428
|
}
|
|
416
429
|
|
|
417
430
|
// src/views/dashboard.tsx
|
|
418
|
-
import { useEffect, useMemo } from "react";
|
|
431
|
+
import { useEffect, useMemo as useMemo2 } from "react";
|
|
419
432
|
import { Box as Box8, Text as Text10 } from "ink";
|
|
420
433
|
|
|
421
434
|
// src/stores/brew-store.ts
|
|
@@ -484,7 +497,7 @@ async function* streamBrew(args) {
|
|
|
484
497
|
if (lines.length > 0) {
|
|
485
498
|
yield lines.shift();
|
|
486
499
|
} else if (!done) {
|
|
487
|
-
await new Promise((r) => setTimeout(r,
|
|
500
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
488
501
|
}
|
|
489
502
|
}
|
|
490
503
|
} finally {
|
|
@@ -687,6 +700,9 @@ function setLoading(set, key, value) {
|
|
|
687
700
|
function setError(set, key, error) {
|
|
688
701
|
set((s) => ({ errors: { ...s.errors, [key]: error } }));
|
|
689
702
|
}
|
|
703
|
+
function recordFetchTime(set, key) {
|
|
704
|
+
set((s) => ({ lastFetchedAt: { ...s.lastFetchedAt, [key]: Date.now() } }));
|
|
705
|
+
}
|
|
690
706
|
var useBrewStore = create3((set) => ({
|
|
691
707
|
formulae: [],
|
|
692
708
|
casks: [],
|
|
@@ -701,6 +717,7 @@ var useBrewStore = create3((set) => ({
|
|
|
701
717
|
// flashing empty/zeroed content for one frame before the fetch starts.
|
|
702
718
|
loading: { installed: true, outdated: true, services: true, config: true, doctor: false },
|
|
703
719
|
errors: {},
|
|
720
|
+
lastFetchedAt: {},
|
|
704
721
|
fetchInstalled: async () => {
|
|
705
722
|
setLoading(set, "installed", true);
|
|
706
723
|
setError(set, "installed", null);
|
|
@@ -711,6 +728,7 @@ var useBrewStore = create3((set) => ({
|
|
|
711
728
|
setError(set, "installed", err instanceof Error ? err.message : String(err));
|
|
712
729
|
} finally {
|
|
713
730
|
setLoading(set, "installed", false);
|
|
731
|
+
recordFetchTime(set, "installed");
|
|
714
732
|
}
|
|
715
733
|
},
|
|
716
734
|
fetchOutdated: async () => {
|
|
@@ -723,6 +741,7 @@ var useBrewStore = create3((set) => ({
|
|
|
723
741
|
setError(set, "outdated", err instanceof Error ? err.message : String(err));
|
|
724
742
|
} finally {
|
|
725
743
|
setLoading(set, "outdated", false);
|
|
744
|
+
recordFetchTime(set, "outdated");
|
|
726
745
|
}
|
|
727
746
|
},
|
|
728
747
|
fetchServices: async () => {
|
|
@@ -735,6 +754,7 @@ var useBrewStore = create3((set) => ({
|
|
|
735
754
|
setError(set, "services", err instanceof Error ? err.message : String(err));
|
|
736
755
|
} finally {
|
|
737
756
|
setLoading(set, "services", false);
|
|
757
|
+
recordFetchTime(set, "services");
|
|
738
758
|
}
|
|
739
759
|
},
|
|
740
760
|
fetchConfig: async () => {
|
|
@@ -746,6 +766,7 @@ var useBrewStore = create3((set) => ({
|
|
|
746
766
|
setError(set, "config", err instanceof Error ? err.message : String(err));
|
|
747
767
|
} finally {
|
|
748
768
|
setLoading(set, "config", false);
|
|
769
|
+
recordFetchTime(set, "config");
|
|
749
770
|
}
|
|
750
771
|
},
|
|
751
772
|
fetchLeaves: async () => {
|
|
@@ -753,9 +774,11 @@ var useBrewStore = create3((set) => ({
|
|
|
753
774
|
const result = await getLeaves();
|
|
754
775
|
set({ leaves: result });
|
|
755
776
|
} catch (err) {
|
|
756
|
-
if (
|
|
777
|
+
if (false) {
|
|
757
778
|
console.error("[brew-store] fetchLeaves failed:", err instanceof Error ? err.message : String(err));
|
|
758
779
|
}
|
|
780
|
+
} finally {
|
|
781
|
+
recordFetchTime(set, "leaves");
|
|
759
782
|
}
|
|
760
783
|
},
|
|
761
784
|
fetchDoctor: async () => {
|
|
@@ -768,13 +791,12 @@ var useBrewStore = create3((set) => ({
|
|
|
768
791
|
setError(set, "doctor", err instanceof Error ? err.message : String(err));
|
|
769
792
|
} finally {
|
|
770
793
|
setLoading(set, "doctor", false);
|
|
794
|
+
recordFetchTime(set, "doctor");
|
|
771
795
|
}
|
|
772
796
|
},
|
|
773
797
|
fetchAll: async () => {
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
} catch {
|
|
777
|
-
}
|
|
798
|
+
brewUpdate().catch(() => {
|
|
799
|
+
});
|
|
778
800
|
const store = useBrewStore.getState();
|
|
779
801
|
await Promise.all([
|
|
780
802
|
store.fetchInstalled(),
|
|
@@ -906,11 +928,11 @@ function DashboardView() {
|
|
|
906
928
|
useEffect(() => {
|
|
907
929
|
fetchAll();
|
|
908
930
|
}, []);
|
|
909
|
-
const errorServiceList =
|
|
931
|
+
const errorServiceList = useMemo2(
|
|
910
932
|
() => services.filter((s) => s.status === "error"),
|
|
911
933
|
[services]
|
|
912
934
|
);
|
|
913
|
-
const runningServices =
|
|
935
|
+
const runningServices = useMemo2(
|
|
914
936
|
() => services.filter((s) => s.status === "started").length,
|
|
915
937
|
[services]
|
|
916
938
|
);
|
|
@@ -981,8 +1003,8 @@ function DashboardView() {
|
|
|
981
1003
|
}
|
|
982
1004
|
|
|
983
1005
|
// src/views/installed.tsx
|
|
984
|
-
import { useState as useState3, useMemo as
|
|
985
|
-
import { Box as Box12, Text as Text14, useInput as useInput3 } from "ink";
|
|
1006
|
+
import { useState as useState3, useMemo as useMemo3, useEffect as useEffect5 } from "react";
|
|
1007
|
+
import { Box as Box12, Text as Text14, useInput as useInput3, useStdout } from "ink";
|
|
986
1008
|
|
|
987
1009
|
// src/hooks/use-debounce.ts
|
|
988
1010
|
import { useState, useEffect as useEffect2 } from "react";
|
|
@@ -998,24 +1020,11 @@ function useDebounce(value, delayMs) {
|
|
|
998
1020
|
// src/hooks/use-brew-stream.ts
|
|
999
1021
|
import { useState as useState2, useCallback, useRef, useEffect as useEffect3 } from "react";
|
|
1000
1022
|
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
1023
|
async function logToHistory(args, success, error) {
|
|
1015
1024
|
const detected = detectAction(args);
|
|
1016
1025
|
if (!detected) return;
|
|
1017
1026
|
try {
|
|
1018
|
-
const { appendEntry: appendEntry2 } = await import("./history-logger-
|
|
1027
|
+
const { appendEntry: appendEntry2 } = await import("./history-logger-65UF2R6F.js");
|
|
1019
1028
|
await appendEntry2(detected.action, detected.packageName, success, error);
|
|
1020
1029
|
} catch {
|
|
1021
1030
|
}
|
|
@@ -1143,7 +1152,7 @@ function ProgressLog({ lines, isRunning, title, maxVisible = 15 }) {
|
|
|
1143
1152
|
title
|
|
1144
1153
|
] })
|
|
1145
1154
|
] }),
|
|
1146
|
-
visible.map((line, i) => /* @__PURE__ */ jsx13(Text13, { color: "#9CA3AF", wrap: "wrap", children: line }, i)),
|
|
1155
|
+
visible.map((line, i) => /* @__PURE__ */ jsx13(Text13, { color: "#9CA3AF", wrap: "wrap", children: line }, line.slice(0, 30) + (lines.length - visible.length + i))),
|
|
1147
1156
|
lines.length === 0 && !isRunning && /* @__PURE__ */ jsx13(Text13, { color: "#6B7280", italic: true, children: t("progress_noOutput") })
|
|
1148
1157
|
] });
|
|
1149
1158
|
}
|
|
@@ -1184,6 +1193,10 @@ function InstalledView() {
|
|
|
1184
1193
|
const debouncedFilter = useDebounce(filter, 200);
|
|
1185
1194
|
const stream = useBrewStream();
|
|
1186
1195
|
const { openModal, closeModal } = useModalStore();
|
|
1196
|
+
const { stdout } = useStdout();
|
|
1197
|
+
const cols = stdout?.columns ?? 80;
|
|
1198
|
+
const nameWidth = Math.floor(cols * 0.35);
|
|
1199
|
+
const versionWidth = Math.floor(cols * 0.15);
|
|
1187
1200
|
useEffect5(() => {
|
|
1188
1201
|
fetchInstalled();
|
|
1189
1202
|
}, []);
|
|
@@ -1196,7 +1209,7 @@ function InstalledView() {
|
|
|
1196
1209
|
}
|
|
1197
1210
|
return void 0;
|
|
1198
1211
|
}, [isSearching]);
|
|
1199
|
-
const allItems =
|
|
1212
|
+
const allItems = useMemo3(() => {
|
|
1200
1213
|
const items = tab === "formulae" ? formulaeToListItems(formulae) : casksToListItems(casks);
|
|
1201
1214
|
if (!debouncedFilter) return items;
|
|
1202
1215
|
const lower = debouncedFilter.toLowerCase();
|
|
@@ -1206,6 +1219,13 @@ function InstalledView() {
|
|
|
1206
1219
|
}, [formulae, casks, tab, debouncedFilter]);
|
|
1207
1220
|
useInput3((input, key) => {
|
|
1208
1221
|
if (confirmUninstall || stream.isRunning) return;
|
|
1222
|
+
if (!stream.isRunning && stream.lines.length > 0) {
|
|
1223
|
+
if (key.escape) {
|
|
1224
|
+
stream.clear();
|
|
1225
|
+
void fetchInstalled();
|
|
1226
|
+
}
|
|
1227
|
+
return;
|
|
1228
|
+
}
|
|
1209
1229
|
if (isSearching) {
|
|
1210
1230
|
if (key.escape) {
|
|
1211
1231
|
setIsSearching(false);
|
|
@@ -1249,10 +1269,20 @@ function InstalledView() {
|
|
|
1249
1269
|
title: t("pkgInfo_uninstalling", { name: "..." })
|
|
1250
1270
|
}
|
|
1251
1271
|
),
|
|
1252
|
-
|
|
1272
|
+
stream.isRunning && /* @__PURE__ */ jsxs14(Text14, { color: "#6B7280", children: [
|
|
1273
|
+
"esc:",
|
|
1274
|
+
t("hint_cancel")
|
|
1275
|
+
] }),
|
|
1276
|
+
!stream.isRunning && /* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", marginTop: 1, children: [
|
|
1277
|
+
/* @__PURE__ */ jsx14(Box12, { borderStyle: "round", borderColor: stream.error ? "#EF4444" : "#22C55E", paddingX: 2, paddingY: 0, children: /* @__PURE__ */ jsx14(Text14, { color: stream.error ? "#EF4444" : "#22C55E", bold: true, children: stream.error ? `\u2718 ${stream.error}` : `\u2714 ${t("pkgInfo_done")}` }) }),
|
|
1278
|
+
/* @__PURE__ */ jsxs14(Text14, { color: "#6B7280", children: [
|
|
1279
|
+
"esc:",
|
|
1280
|
+
t("hint_back")
|
|
1281
|
+
] })
|
|
1282
|
+
] })
|
|
1253
1283
|
] });
|
|
1254
1284
|
}
|
|
1255
|
-
const MAX_VISIBLE_ROWS =
|
|
1285
|
+
const MAX_VISIBLE_ROWS = Math.max(5, (stdout?.rows ?? 24) - 8);
|
|
1256
1286
|
const start = Math.max(0, cursor - Math.floor(MAX_VISIBLE_ROWS / 2));
|
|
1257
1287
|
const visible = allItems.slice(start, start + MAX_VISIBLE_ROWS);
|
|
1258
1288
|
return /* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", children: [
|
|
@@ -1302,9 +1332,9 @@ function InstalledView() {
|
|
|
1302
1332
|
/* @__PURE__ */ jsxs14(Box12, { gap: 1, borderStyle: "single", borderBottom: true, borderTop: false, borderLeft: false, borderRight: false, borderColor: "#4B5563", children: [
|
|
1303
1333
|
/* @__PURE__ */ jsxs14(Text14, { color: "#F9FAFB", bold: true, children: [
|
|
1304
1334
|
" ",
|
|
1305
|
-
"Package".padEnd(
|
|
1335
|
+
"Package".padEnd(nameWidth)
|
|
1306
1336
|
] }),
|
|
1307
|
-
/* @__PURE__ */ jsx14(Text14, { color: "#F9FAFB", bold: true, children: "Version".padEnd(
|
|
1337
|
+
/* @__PURE__ */ jsx14(Text14, { color: "#F9FAFB", bold: true, children: "Version".padEnd(versionWidth) }),
|
|
1308
1338
|
/* @__PURE__ */ jsx14(Text14, { color: "#F9FAFB", bold: true, children: "Status" })
|
|
1309
1339
|
] }),
|
|
1310
1340
|
/* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", children: [
|
|
@@ -1318,8 +1348,8 @@ function InstalledView() {
|
|
|
1318
1348
|
const isCurrent = idx === cursor;
|
|
1319
1349
|
return /* @__PURE__ */ jsxs14(Box12, { gap: 1, children: [
|
|
1320
1350
|
/* @__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,
|
|
1322
|
-
/* @__PURE__ */ jsx14(Text14, { color: "#2DD4BF", children: item.version.padEnd(
|
|
1351
|
+
/* @__PURE__ */ jsx14(Text14, { bold: isCurrent, inverse: isCurrent, color: isCurrent ? "#F9FAFB" : "#9CA3AF", children: truncate(item.name, nameWidth).padEnd(nameWidth) }),
|
|
1352
|
+
/* @__PURE__ */ jsx14(Text14, { color: "#2DD4BF", children: item.version.padEnd(versionWidth) }),
|
|
1323
1353
|
item.outdated && /* @__PURE__ */ jsx14(StatusBadge, { label: t("badge_outdated"), variant: "warning" }),
|
|
1324
1354
|
item.pinned && /* @__PURE__ */ jsx14(StatusBadge, { label: t("badge_pinned"), variant: "info" }),
|
|
1325
1355
|
item.kegOnly && /* @__PURE__ */ jsx14(StatusBadge, { label: t("badge_kegOnly"), variant: "muted" }),
|
|
@@ -1345,6 +1375,7 @@ function SearchView() {
|
|
|
1345
1375
|
const [query, setQuery] = useState4("");
|
|
1346
1376
|
const [results, setResults] = useState4(null);
|
|
1347
1377
|
const [searching, setSearching] = useState4(false);
|
|
1378
|
+
const [searchError, setSearchError] = useState4(null);
|
|
1348
1379
|
const [cursor, setCursor] = useState4(0);
|
|
1349
1380
|
const [confirmInstall, setConfirmInstall] = useState4(null);
|
|
1350
1381
|
const stream = useBrewStream();
|
|
@@ -1365,13 +1396,14 @@ function SearchView() {
|
|
|
1365
1396
|
const doSearch = useCallback2(async (term) => {
|
|
1366
1397
|
if (term.length < 2) return;
|
|
1367
1398
|
setSearching(true);
|
|
1399
|
+
setSearchError(null);
|
|
1368
1400
|
try {
|
|
1369
1401
|
const r = await search(term);
|
|
1370
1402
|
setResults(r);
|
|
1371
1403
|
setCursor(0);
|
|
1372
1404
|
} catch (err) {
|
|
1373
1405
|
setResults({ formulae: [], casks: [] });
|
|
1374
|
-
|
|
1406
|
+
setSearchError(err instanceof Error ? err.message : "Search failed");
|
|
1375
1407
|
} finally {
|
|
1376
1408
|
setSearching(false);
|
|
1377
1409
|
}
|
|
@@ -1382,7 +1414,10 @@ function SearchView() {
|
|
|
1382
1414
|
void fetchInstalled();
|
|
1383
1415
|
}
|
|
1384
1416
|
}, [stream.isRunning, stream.error]);
|
|
1385
|
-
const
|
|
1417
|
+
const MAX_VISIBLE = 20;
|
|
1418
|
+
const visibleFormulae = results ? results.formulae.slice(0, MAX_VISIBLE) : [];
|
|
1419
|
+
const visibleCasks = results ? results.casks.slice(0, MAX_VISIBLE) : [];
|
|
1420
|
+
const allVisible = [...visibleFormulae, ...visibleCasks];
|
|
1386
1421
|
useInput4((input, key) => {
|
|
1387
1422
|
if (stream.isRunning) {
|
|
1388
1423
|
if (key.escape) stream.cancel();
|
|
@@ -1393,17 +1428,17 @@ function SearchView() {
|
|
|
1393
1428
|
void doSearch(query);
|
|
1394
1429
|
return;
|
|
1395
1430
|
}
|
|
1396
|
-
if (key.return &&
|
|
1397
|
-
selectPackage(
|
|
1431
|
+
if (key.return && allVisible[cursor]) {
|
|
1432
|
+
selectPackage(allVisible[cursor]);
|
|
1398
1433
|
navigate("package-info");
|
|
1399
1434
|
return;
|
|
1400
1435
|
}
|
|
1401
|
-
if (input === "i" &&
|
|
1402
|
-
setConfirmInstall(
|
|
1436
|
+
if (input === "i" && allVisible[cursor]) {
|
|
1437
|
+
setConfirmInstall(allVisible[cursor]);
|
|
1403
1438
|
return;
|
|
1404
1439
|
}
|
|
1405
1440
|
if (input === "j" || key.downArrow) {
|
|
1406
|
-
setCursor((c) => Math.min(c + 1, Math.max(0,
|
|
1441
|
+
setCursor((c) => Math.min(c + 1, Math.max(0, allVisible.length - 1)));
|
|
1407
1442
|
} else if (input === "k" || key.upArrow) {
|
|
1408
1443
|
setCursor((c) => Math.max(c - 1, 0));
|
|
1409
1444
|
} else if (key.escape) {
|
|
@@ -1457,6 +1492,7 @@ function SearchView() {
|
|
|
1457
1492
|
] })
|
|
1458
1493
|
] }),
|
|
1459
1494
|
searching && /* @__PURE__ */ jsx15(Loading, { message: t("loading_searching") }),
|
|
1495
|
+
searchError && /* @__PURE__ */ jsx15(Box13, { marginBottom: 1, children: /* @__PURE__ */ jsx15(Text15, { color: "#EF4444", children: searchError }) }),
|
|
1460
1496
|
confirmInstall && /* @__PURE__ */ jsx15(
|
|
1461
1497
|
ConfirmDialog,
|
|
1462
1498
|
{
|
|
@@ -1471,44 +1507,44 @@ function SearchView() {
|
|
|
1471
1507
|
}
|
|
1472
1508
|
),
|
|
1473
1509
|
results && !searching && !confirmInstall && /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", children: [
|
|
1474
|
-
|
|
1510
|
+
visibleFormulae.length > 0 && /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", marginBottom: 1, children: [
|
|
1475
1511
|
/* @__PURE__ */ jsx15(Text15, { bold: true, color: "#06B6D4", children: t("search_formulaeHeader", { count: results.formulae.length }) }),
|
|
1476
|
-
|
|
1512
|
+
visibleFormulae.map((name, i) => {
|
|
1477
1513
|
const isCurrent = i === cursor;
|
|
1478
1514
|
return /* @__PURE__ */ jsxs15(Box13, { gap: 1, children: [
|
|
1479
1515
|
/* @__PURE__ */ jsx15(Text15, { color: isCurrent ? "#22C55E" : "#9CA3AF", children: isCurrent ? "\u25B6" : " " }),
|
|
1480
1516
|
/* @__PURE__ */ jsx15(Text15, { bold: isCurrent, inverse: isCurrent, children: name })
|
|
1481
1517
|
] }, name);
|
|
1482
1518
|
}),
|
|
1483
|
-
results.formulae.length >
|
|
1519
|
+
results.formulae.length > MAX_VISIBLE && /* @__PURE__ */ jsxs15(Text15, { color: "#6B7280", dimColor: true, children: [
|
|
1484
1520
|
" ",
|
|
1485
|
-
t("scroll_moreBelow", { count: results.formulae.length -
|
|
1521
|
+
t("scroll_moreBelow", { count: results.formulae.length - MAX_VISIBLE })
|
|
1486
1522
|
] })
|
|
1487
1523
|
] }),
|
|
1488
|
-
|
|
1524
|
+
visibleCasks.length > 0 && /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", children: [
|
|
1489
1525
|
/* @__PURE__ */ jsx15(Text15, { bold: true, color: "#A855F7", children: t("search_casksHeader", { count: results.casks.length }) }),
|
|
1490
|
-
|
|
1491
|
-
const idx =
|
|
1526
|
+
visibleCasks.map((name, i) => {
|
|
1527
|
+
const idx = visibleFormulae.length + i;
|
|
1492
1528
|
const isCurrent = idx === cursor;
|
|
1493
1529
|
return /* @__PURE__ */ jsxs15(Box13, { gap: 1, children: [
|
|
1494
1530
|
/* @__PURE__ */ jsx15(Text15, { color: isCurrent ? "#22C55E" : "#9CA3AF", children: isCurrent ? "\u25B6" : " " }),
|
|
1495
1531
|
/* @__PURE__ */ jsx15(Text15, { bold: isCurrent, inverse: isCurrent, children: name })
|
|
1496
1532
|
] }, name);
|
|
1497
1533
|
}),
|
|
1498
|
-
results.casks.length >
|
|
1534
|
+
results.casks.length > MAX_VISIBLE && /* @__PURE__ */ jsxs15(Text15, { color: "#6B7280", dimColor: true, children: [
|
|
1499
1535
|
" ",
|
|
1500
|
-
t("scroll_moreBelow", { count: results.casks.length -
|
|
1536
|
+
t("scroll_moreBelow", { count: results.casks.length - MAX_VISIBLE })
|
|
1501
1537
|
] })
|
|
1502
1538
|
] }),
|
|
1503
|
-
|
|
1504
|
-
/* @__PURE__ */ jsx15(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx15(Text15, { color: "#F9FAFB", bold: true, children:
|
|
1539
|
+
allVisible.length === 0 && /* @__PURE__ */ jsx15(Box13, { borderStyle: "round", borderColor: "#6B7280", paddingX: 2, children: /* @__PURE__ */ jsx15(Text15, { color: "#6B7280", italic: true, children: t("search_noResults") }) }),
|
|
1540
|
+
/* @__PURE__ */ jsx15(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx15(Text15, { color: "#F9FAFB", bold: true, children: allVisible.length > 0 ? `${cursor + 1}/${allVisible.length}` : "" }) })
|
|
1505
1541
|
] })
|
|
1506
1542
|
] });
|
|
1507
1543
|
}
|
|
1508
1544
|
|
|
1509
1545
|
// src/views/outdated.tsx
|
|
1510
1546
|
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";
|
|
1547
|
+
import { Box as Box14, Text as Text16, useInput as useInput5, useStdout as useStdout2 } from "ink";
|
|
1512
1548
|
import { jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
1513
1549
|
function OutdatedView() {
|
|
1514
1550
|
const { outdated, loading, errors, fetchOutdated } = useBrewStore();
|
|
@@ -1548,6 +1584,10 @@ function OutdatedView() {
|
|
|
1548
1584
|
void fetchOutdated();
|
|
1549
1585
|
}
|
|
1550
1586
|
});
|
|
1587
|
+
const { stdout } = useStdout2();
|
|
1588
|
+
const MAX_VISIBLE_ROWS = Math.max(5, (stdout?.rows ?? 24) - 8);
|
|
1589
|
+
const start = Math.max(0, cursor - Math.floor(MAX_VISIBLE_ROWS / 2));
|
|
1590
|
+
const visible = allOutdated.slice(start, start + MAX_VISIBLE_ROWS);
|
|
1551
1591
|
if (loading.outdated) return /* @__PURE__ */ jsx16(Loading, { message: t("loading_outdated") });
|
|
1552
1592
|
if (errors.outdated) return /* @__PURE__ */ jsx16(ErrorMessage, { message: errors.outdated });
|
|
1553
1593
|
if (stream.isRunning || stream.lines.length > 0) {
|
|
@@ -1605,8 +1645,13 @@ function OutdatedView() {
|
|
|
1605
1645
|
t("outdated_upToDate")
|
|
1606
1646
|
] }) }) }),
|
|
1607
1647
|
allOutdated.length > 0 && !confirmAction && /* @__PURE__ */ jsxs16(Box14, { flexDirection: "column", marginTop: 1, children: [
|
|
1608
|
-
|
|
1609
|
-
|
|
1648
|
+
start > 0 && /* @__PURE__ */ jsxs16(Text16, { color: "#6B7280", dimColor: true, children: [
|
|
1649
|
+
" ",
|
|
1650
|
+
t("scroll_moreAbove", { count: start })
|
|
1651
|
+
] }),
|
|
1652
|
+
visible.map((pkg, i) => {
|
|
1653
|
+
const idx = start + i;
|
|
1654
|
+
const isCurrent = idx === cursor;
|
|
1610
1655
|
return /* @__PURE__ */ jsxs16(Box14, { gap: 1, children: [
|
|
1611
1656
|
/* @__PURE__ */ jsx16(Text16, { color: isCurrent ? "#22C55E" : "#9CA3AF", children: isCurrent ? "\u25B6" : " " }),
|
|
1612
1657
|
/* @__PURE__ */ jsx16(Text16, { bold: isCurrent, inverse: isCurrent, color: isCurrent ? "#F9FAFB" : "#9CA3AF", children: pkg.name }),
|
|
@@ -1614,6 +1659,10 @@ function OutdatedView() {
|
|
|
1614
1659
|
pkg.pinned && /* @__PURE__ */ jsx16(StatusBadge, { label: t("outdated_pinned"), variant: "info" })
|
|
1615
1660
|
] }, pkg.name);
|
|
1616
1661
|
}),
|
|
1662
|
+
start + MAX_VISIBLE_ROWS < allOutdated.length && /* @__PURE__ */ jsxs16(Text16, { color: "#6B7280", dimColor: true, children: [
|
|
1663
|
+
" ",
|
|
1664
|
+
t("scroll_moreBelow", { count: allOutdated.length - start - MAX_VISIBLE_ROWS })
|
|
1665
|
+
] }),
|
|
1617
1666
|
/* @__PURE__ */ jsx16(Box14, { marginTop: 1, children: /* @__PURE__ */ jsxs16(Text16, { color: "#F9FAFB", bold: true, children: [
|
|
1618
1667
|
cursor + 1,
|
|
1619
1668
|
"/",
|
|
@@ -1811,7 +1860,7 @@ function PackageInfoView() {
|
|
|
1811
1860
|
|
|
1812
1861
|
// src/views/services.tsx
|
|
1813
1862
|
import { useEffect as useEffect9, useState as useState7 } from "react";
|
|
1814
|
-
import { Box as Box16, Text as Text18, useInput as useInput7 } from "ink";
|
|
1863
|
+
import { Box as Box16, Text as Text18, useInput as useInput7, useStdout as useStdout3 } from "ink";
|
|
1815
1864
|
import { jsx as jsx18, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
1816
1865
|
var STATUS_VARIANTS = {
|
|
1817
1866
|
started: "success",
|
|
@@ -1824,6 +1873,10 @@ function ServicesView() {
|
|
|
1824
1873
|
const [cursor, setCursor] = useState7(0);
|
|
1825
1874
|
const [actionInProgress, setActionInProgress] = useState7(false);
|
|
1826
1875
|
const [confirmAction, setConfirmAction] = useState7(null);
|
|
1876
|
+
const { stdout } = useStdout3();
|
|
1877
|
+
const cols = stdout?.columns ?? 80;
|
|
1878
|
+
const svcNameWidth = Math.floor(cols * 0.35);
|
|
1879
|
+
const svcStatusWidth = Math.floor(cols * 0.15);
|
|
1827
1880
|
useEffect9(() => {
|
|
1828
1881
|
fetchServices();
|
|
1829
1882
|
}, []);
|
|
@@ -1849,6 +1902,7 @@ function ServicesView() {
|
|
|
1849
1902
|
else if (input === "S") setConfirmAction({ type: "stop", name: svc.name });
|
|
1850
1903
|
else if (input === "R") setConfirmAction({ type: "restart", name: svc.name });
|
|
1851
1904
|
});
|
|
1905
|
+
const serviceActionError = useBrewStore((s) => s.errors["service-action"]);
|
|
1852
1906
|
if (loading.services) return /* @__PURE__ */ jsx18(Loading, { message: t("loading_services") });
|
|
1853
1907
|
if (errors.services) return /* @__PURE__ */ jsx18(ErrorMessage, { message: errors.services });
|
|
1854
1908
|
if (services.length === 0) {
|
|
@@ -1878,16 +1932,16 @@ function ServicesView() {
|
|
|
1878
1932
|
/* @__PURE__ */ jsxs18(Box16, { gap: 1, borderStyle: "single", borderBottom: true, borderTop: false, borderLeft: false, borderRight: false, borderColor: "#4B5563", paddingBottom: 0, children: [
|
|
1879
1933
|
/* @__PURE__ */ jsxs18(Text18, { bold: true, color: "#F9FAFB", children: [
|
|
1880
1934
|
" ",
|
|
1881
|
-
t("services_name").padEnd(
|
|
1935
|
+
t("services_name").padEnd(svcNameWidth)
|
|
1882
1936
|
] }),
|
|
1883
|
-
/* @__PURE__ */ jsx18(Text18, { bold: true, color: "#F9FAFB", children: t("services_status").padEnd(
|
|
1937
|
+
/* @__PURE__ */ jsx18(Text18, { bold: true, color: "#F9FAFB", children: t("services_status").padEnd(svcStatusWidth) }),
|
|
1884
1938
|
/* @__PURE__ */ jsx18(Text18, { bold: true, color: "#F9FAFB", children: t("services_user") })
|
|
1885
1939
|
] }),
|
|
1886
1940
|
services.map((svc, i) => {
|
|
1887
1941
|
const isCurrent = i === cursor;
|
|
1888
1942
|
return /* @__PURE__ */ jsxs18(Box16, { gap: 1, children: [
|
|
1889
1943
|
/* @__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(
|
|
1944
|
+
/* @__PURE__ */ jsx18(Text18, { bold: isCurrent, inverse: isCurrent, color: isCurrent ? "#F9FAFB" : "#9CA3AF", children: svc.name.padEnd(svcNameWidth - 2) }),
|
|
1891
1945
|
/* @__PURE__ */ jsx18(StatusBadge, { label: svc.status, variant: STATUS_VARIANTS[svc.status] }),
|
|
1892
1946
|
/* @__PURE__ */ jsx18(Text18, { color: "#9CA3AF", children: svc.user ?? "-" }),
|
|
1893
1947
|
svc.exit_code != null && svc.exit_code !== 0 && /* @__PURE__ */ jsx18(Text18, { color: "#EF4444", children: t("common_exit", { code: svc.exit_code }) })
|
|
@@ -1895,6 +1949,7 @@ function ServicesView() {
|
|
|
1895
1949
|
})
|
|
1896
1950
|
] }),
|
|
1897
1951
|
actionInProgress && /* @__PURE__ */ jsx18(Text18, { color: "#38BDF8", children: t("services_processing") }),
|
|
1952
|
+
serviceActionError && /* @__PURE__ */ jsx18(Box16, { marginTop: 1, children: /* @__PURE__ */ jsx18(Text18, { color: "#EF4444", children: serviceActionError }) }),
|
|
1898
1953
|
/* @__PURE__ */ jsx18(Box16, { marginTop: 1, children: /* @__PURE__ */ jsxs18(Text18, { color: "#F9FAFB", bold: true, children: [
|
|
1899
1954
|
cursor + 1,
|
|
1900
1955
|
"/",
|
|
@@ -1926,7 +1981,7 @@ function DoctorView() {
|
|
|
1926
1981
|
t("doctor_clean")
|
|
1927
1982
|
] }) }),
|
|
1928
1983
|
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))
|
|
1984
|
+
doctorWarnings.map((warning, i) => /* @__PURE__ */ jsx19(Box17, { flexDirection: "column", marginBottom: 1, borderStyle: "single", borderColor: "#F59E0B", paddingX: 1, children: warning.split("\n").map((line, j) => /* @__PURE__ */ jsx19(Text19, { color: j === 0 ? "#F59E0B" : "#9CA3AF", children: line }, j)) }, warning.slice(0, 50) + i))
|
|
1930
1985
|
] }),
|
|
1931
1986
|
/* @__PURE__ */ jsx19(Box17, { marginTop: 1, children: /* @__PURE__ */ jsx19(Text19, { color: "#F9FAFB", bold: true, children: doctorWarnings.length > 0 ? tp("plural_warnings", doctorWarnings.length) : "" }) })
|
|
1932
1987
|
] });
|
|
@@ -1945,13 +2000,16 @@ import { readFile, writeFile, readdir, rm } from "fs/promises";
|
|
|
1945
2000
|
import { join, basename } from "path";
|
|
1946
2001
|
|
|
1947
2002
|
// src/lib/license/watermark.ts
|
|
1948
|
-
function getWatermark() {
|
|
1949
|
-
const license = useLicenseStore.getState().license;
|
|
2003
|
+
function getWatermark(license) {
|
|
1950
2004
|
if (!license?.customerEmail) return "";
|
|
1951
2005
|
return `Licensed to: ${license.customerEmail}`;
|
|
1952
2006
|
}
|
|
1953
2007
|
|
|
1954
2008
|
// src/lib/profiles/profile-manager.ts
|
|
2009
|
+
function proCheck() {
|
|
2010
|
+
const { license, status } = useLicenseStore.getState();
|
|
2011
|
+
requirePro(license, status);
|
|
2012
|
+
}
|
|
1955
2013
|
var MAX_PROFILE_NAME_LENGTH = 100;
|
|
1956
2014
|
function validateProfileName(name) {
|
|
1957
2015
|
if (!name || name.trim().length === 0) {
|
|
@@ -1969,7 +2027,7 @@ function profilePath(name) {
|
|
|
1969
2027
|
return join(PROFILES_DIR, `${basename(name)}.json`);
|
|
1970
2028
|
}
|
|
1971
2029
|
async function listProfiles() {
|
|
1972
|
-
|
|
2030
|
+
proCheck();
|
|
1973
2031
|
await ensureDataDirs();
|
|
1974
2032
|
try {
|
|
1975
2033
|
const files = await readdir(PROFILES_DIR);
|
|
@@ -1979,7 +2037,7 @@ async function listProfiles() {
|
|
|
1979
2037
|
}
|
|
1980
2038
|
}
|
|
1981
2039
|
async function loadProfile(name) {
|
|
1982
|
-
|
|
2040
|
+
proCheck();
|
|
1983
2041
|
const raw = await readFile(profilePath(name), "utf-8");
|
|
1984
2042
|
let file;
|
|
1985
2043
|
try {
|
|
@@ -1987,26 +2045,29 @@ async function loadProfile(name) {
|
|
|
1987
2045
|
} catch (err) {
|
|
1988
2046
|
throw new Error(`Profile "${name}" is corrupted: ${err instanceof Error ? err.message : String(err)}`);
|
|
1989
2047
|
}
|
|
2048
|
+
if (file.version !== 1) {
|
|
2049
|
+
throw new Error("Unsupported data version");
|
|
2050
|
+
}
|
|
1990
2051
|
if (!file.profile) {
|
|
1991
2052
|
throw new Error(`Profile "${name}" is missing required data`);
|
|
1992
2053
|
}
|
|
1993
2054
|
return file.profile;
|
|
1994
2055
|
}
|
|
1995
2056
|
async function saveProfile(profile) {
|
|
1996
|
-
|
|
2057
|
+
proCheck();
|
|
1997
2058
|
await ensureDataDirs();
|
|
1998
2059
|
const file = { version: 1, profile };
|
|
1999
|
-
await writeFile(profilePath(profile.name), JSON.stringify(file, null, 2), "utf-8");
|
|
2060
|
+
await writeFile(profilePath(profile.name), JSON.stringify(file, null, 2), { encoding: "utf-8", mode: 384 });
|
|
2000
2061
|
}
|
|
2001
2062
|
async function deleteProfile(name) {
|
|
2002
|
-
|
|
2063
|
+
proCheck();
|
|
2003
2064
|
try {
|
|
2004
2065
|
await rm(profilePath(name));
|
|
2005
2066
|
} catch {
|
|
2006
2067
|
}
|
|
2007
2068
|
}
|
|
2008
|
-
async function exportCurrentSetup(name, description) {
|
|
2009
|
-
|
|
2069
|
+
async function exportCurrentSetup(name, description, license = null) {
|
|
2070
|
+
proCheck();
|
|
2010
2071
|
const [installed, leaves, tapsRaw] = await Promise.all([
|
|
2011
2072
|
getInstalled(),
|
|
2012
2073
|
getLeaves(),
|
|
@@ -2023,14 +2084,14 @@ async function exportCurrentSetup(name, description) {
|
|
|
2023
2084
|
formulae: leaves,
|
|
2024
2085
|
casks,
|
|
2025
2086
|
taps,
|
|
2026
|
-
exportedBy: getWatermark()
|
|
2087
|
+
exportedBy: getWatermark(license)
|
|
2027
2088
|
// Layer 16: Watermark — who exported this profile
|
|
2028
2089
|
};
|
|
2029
2090
|
await saveProfile(profile);
|
|
2030
2091
|
return profile;
|
|
2031
2092
|
}
|
|
2032
2093
|
async function updateProfile(oldName, newName, newDescription) {
|
|
2033
|
-
|
|
2094
|
+
proCheck();
|
|
2034
2095
|
const profile = await loadProfile(oldName);
|
|
2035
2096
|
if (oldName !== newName) {
|
|
2036
2097
|
await deleteProfile(oldName);
|
|
@@ -2043,12 +2104,18 @@ async function updateProfile(oldName, newName, newDescription) {
|
|
|
2043
2104
|
};
|
|
2044
2105
|
await saveProfile(updated);
|
|
2045
2106
|
}
|
|
2107
|
+
var TAP_PATTERN = /^[a-z0-9][-a-z0-9]*\/[a-z0-9][-a-z0-9]*$/;
|
|
2108
|
+
var PKG_PATTERN = /^[a-z0-9][-a-z0-9_.@+]*$/;
|
|
2046
2109
|
async function* importProfile(profile) {
|
|
2047
|
-
|
|
2110
|
+
proCheck();
|
|
2048
2111
|
const installed = await getInstalled();
|
|
2049
2112
|
const installedFormulae = new Set(installed.formulae.map((f) => f.name));
|
|
2050
2113
|
const installedCasks = new Set(installed.casks.filter((c) => c.installed).map((c) => c.token));
|
|
2051
2114
|
for (const tap of profile.taps) {
|
|
2115
|
+
if (!TAP_PATTERN.test(tap)) {
|
|
2116
|
+
yield `Skipping invalid tap name: ${tap}`;
|
|
2117
|
+
continue;
|
|
2118
|
+
}
|
|
2052
2119
|
yield t("profileMgr_tapping", { name: tap });
|
|
2053
2120
|
try {
|
|
2054
2121
|
await execBrew(["tap", tap]);
|
|
@@ -2057,6 +2124,10 @@ async function* importProfile(profile) {
|
|
|
2057
2124
|
}
|
|
2058
2125
|
const missingFormulae = profile.formulae.filter((f) => !installedFormulae.has(f));
|
|
2059
2126
|
for (const name of missingFormulae) {
|
|
2127
|
+
if (!PKG_PATTERN.test(name)) {
|
|
2128
|
+
yield `Skipping invalid formula name: ${name}`;
|
|
2129
|
+
continue;
|
|
2130
|
+
}
|
|
2060
2131
|
yield t("profileMgr_installing", { name });
|
|
2061
2132
|
for await (const line of streamBrew(["install", name])) {
|
|
2062
2133
|
yield line;
|
|
@@ -2064,6 +2135,10 @@ async function* importProfile(profile) {
|
|
|
2064
2135
|
}
|
|
2065
2136
|
const missingCasks = profile.casks.filter((c) => !installedCasks.has(c));
|
|
2066
2137
|
for (const name of missingCasks) {
|
|
2138
|
+
if (!PKG_PATTERN.test(name)) {
|
|
2139
|
+
yield `Skipping invalid cask name: ${name}`;
|
|
2140
|
+
continue;
|
|
2141
|
+
}
|
|
2067
2142
|
yield t("profileMgr_installingCask", { name });
|
|
2068
2143
|
for await (const line of streamBrew(["install", "--cask", name])) {
|
|
2069
2144
|
yield line;
|
|
@@ -2096,7 +2171,8 @@ var useProfileStore = create4((set) => ({
|
|
|
2096
2171
|
exportCurrent: async (name, description) => {
|
|
2097
2172
|
set({ loading: true, loadError: null });
|
|
2098
2173
|
try {
|
|
2099
|
-
|
|
2174
|
+
const license = useLicenseStore.getState().license;
|
|
2175
|
+
await exportCurrentSetup(name, description, license);
|
|
2100
2176
|
const names = await listProfiles();
|
|
2101
2177
|
set({ profileNames: names, loading: false });
|
|
2102
2178
|
} catch (err) {
|
|
@@ -2190,6 +2266,9 @@ function ProfilesView() {
|
|
|
2190
2266
|
setMode("edit-name");
|
|
2191
2267
|
}
|
|
2192
2268
|
}, { isActive: mode === "detail" });
|
|
2269
|
+
useInput9(() => {
|
|
2270
|
+
setMode("list");
|
|
2271
|
+
}, { isActive: mode === "importing" && !importRunning });
|
|
2193
2272
|
const startImport = async (name) => {
|
|
2194
2273
|
setMode("importing");
|
|
2195
2274
|
setImportLines([]);
|
|
@@ -2393,7 +2472,8 @@ async function getCellarPath(name) {
|
|
|
2393
2472
|
}
|
|
2394
2473
|
}
|
|
2395
2474
|
async function analyzeCleanup(formulae, leaves) {
|
|
2396
|
-
|
|
2475
|
+
const { license, status } = useLicenseStore.getState();
|
|
2476
|
+
requirePro(license, status);
|
|
2397
2477
|
const leavesSet = new Set(leaves);
|
|
2398
2478
|
const reverseDeps = /* @__PURE__ */ new Map();
|
|
2399
2479
|
for (const f of formulae) {
|
|
@@ -2629,8 +2709,8 @@ function SmartCleanupView() {
|
|
|
2629
2709
|
}
|
|
2630
2710
|
|
|
2631
2711
|
// src/views/history.tsx
|
|
2632
|
-
import { useEffect as useEffect13, useState as useState10, useMemo as
|
|
2633
|
-
import { Box as Box20, Text as Text22, useInput as useInput11 } from "ink";
|
|
2712
|
+
import { useEffect as useEffect13, useState as useState10, useMemo as useMemo4 } from "react";
|
|
2713
|
+
import { Box as Box20, Text as Text22, useInput as useInput11, useStdout as useStdout4 } from "ink";
|
|
2634
2714
|
|
|
2635
2715
|
// src/stores/history-store.ts
|
|
2636
2716
|
import { create as create6 } from "zustand";
|
|
@@ -2696,7 +2776,7 @@ function HistoryView() {
|
|
|
2696
2776
|
}
|
|
2697
2777
|
return void 0;
|
|
2698
2778
|
}, [isSearching]);
|
|
2699
|
-
const filtered =
|
|
2779
|
+
const filtered = useMemo4(() => {
|
|
2700
2780
|
let result = entries;
|
|
2701
2781
|
if (filter !== "all") {
|
|
2702
2782
|
result = result.filter((e) => e.action === filter);
|
|
@@ -2739,7 +2819,8 @@ function HistoryView() {
|
|
|
2739
2819
|
});
|
|
2740
2820
|
if (loading) return /* @__PURE__ */ jsx22(Loading, { message: t("loading_history") });
|
|
2741
2821
|
if (error) return /* @__PURE__ */ jsx22(ErrorMessage, { message: error });
|
|
2742
|
-
const
|
|
2822
|
+
const { stdout } = useStdout4();
|
|
2823
|
+
const MAX_VISIBLE_ROWS = Math.max(5, (stdout?.rows ?? 24) - 8);
|
|
2743
2824
|
const start = Math.max(0, cursor - Math.floor(MAX_VISIBLE_ROWS / 2));
|
|
2744
2825
|
const visible = filtered.slice(start, start + MAX_VISIBLE_ROWS);
|
|
2745
2826
|
return /* @__PURE__ */ jsxs22(Box20, { flexDirection: "column", children: [
|
|
@@ -2857,11 +2938,11 @@ function getFixedVersion(vuln) {
|
|
|
2857
2938
|
}
|
|
2858
2939
|
var BATCH_SIZE = 100;
|
|
2859
2940
|
async function queryBatch(packages, queries) {
|
|
2860
|
-
const res = await
|
|
2941
|
+
const res = await fetchWithTimeout(OSV_BATCH_URL, {
|
|
2861
2942
|
method: "POST",
|
|
2862
2943
|
headers: { "Content-Type": "application/json" },
|
|
2863
2944
|
body: JSON.stringify({ queries })
|
|
2864
|
-
});
|
|
2945
|
+
}, 15e3);
|
|
2865
2946
|
if (!res.ok) {
|
|
2866
2947
|
if (res.status === 400 && queries.length > 1) {
|
|
2867
2948
|
return queryOneByOne(packages);
|
|
@@ -2923,7 +3004,8 @@ var SEVERITY_ORDER = {
|
|
|
2923
3004
|
UNKNOWN: 0
|
|
2924
3005
|
};
|
|
2925
3006
|
async function runSecurityAudit(formulae, casks) {
|
|
2926
|
-
|
|
3007
|
+
const { license, status } = useLicenseStore.getState();
|
|
3008
|
+
requirePro(license, status);
|
|
2927
3009
|
const packages = [];
|
|
2928
3010
|
for (const f of formulae) {
|
|
2929
3011
|
const version = f.installed[0]?.version ?? f.versions.stable;
|
|
@@ -3136,8 +3218,11 @@ function AccountView() {
|
|
|
3136
3218
|
onConfirm: async () => {
|
|
3137
3219
|
setConfirmDeactivate(false);
|
|
3138
3220
|
setDeactivating(true);
|
|
3139
|
-
|
|
3140
|
-
|
|
3221
|
+
try {
|
|
3222
|
+
await deactivate2();
|
|
3223
|
+
} finally {
|
|
3224
|
+
setDeactivating(false);
|
|
3225
|
+
}
|
|
3141
3226
|
},
|
|
3142
3227
|
onCancel: () => setConfirmDeactivate(false)
|
|
3143
3228
|
}
|
|
@@ -3189,6 +3274,11 @@ function AccountView() {
|
|
|
3189
3274
|
/* @__PURE__ */ jsx24(Text24, { children: t("account_unlockDesc") }),
|
|
3190
3275
|
/* @__PURE__ */ jsx24(Text24, { color: "#06B6D4", bold: true, children: t("account_pricing") }),
|
|
3191
3276
|
/* @__PURE__ */ jsx24(Text24, { children: " " }),
|
|
3277
|
+
/* @__PURE__ */ jsxs24(Text24, { color: "#9CA3AF", children: [
|
|
3278
|
+
t("upgrade_buyAt"),
|
|
3279
|
+
" ",
|
|
3280
|
+
/* @__PURE__ */ jsx24(Text24, { color: "#38BDF8", bold: true, children: t("upgrade_buyUrl") })
|
|
3281
|
+
] }),
|
|
3192
3282
|
/* @__PURE__ */ jsxs24(Text24, { color: "#9CA3AF", children: [
|
|
3193
3283
|
t("account_runActivate"),
|
|
3194
3284
|
" ",
|
|
@@ -3201,7 +3291,7 @@ function AccountView() {
|
|
|
3201
3291
|
/* @__PURE__ */ jsx24(Box22, { marginTop: 2, children: /* @__PURE__ */ jsxs24(Text24, { color: "#6B7280", children: [
|
|
3202
3292
|
status === "pro" ? `d:${t("hint_deactivate")}` : "",
|
|
3203
3293
|
" ",
|
|
3204
|
-
t("app_version", { version: "0.
|
|
3294
|
+
t("app_version", { version: "0.2.0" })
|
|
3205
3295
|
] }) })
|
|
3206
3296
|
] });
|
|
3207
3297
|
}
|
|
@@ -3288,8 +3378,11 @@ async function runCli() {
|
|
|
3288
3378
|
console.log(t("cli_deactivateCancelled"));
|
|
3289
3379
|
return;
|
|
3290
3380
|
}
|
|
3291
|
-
await deactivate(license);
|
|
3381
|
+
const { remoteSuccess } = await deactivate(license);
|
|
3292
3382
|
console.log(t("cli_deactivated"));
|
|
3383
|
+
if (!remoteSuccess) {
|
|
3384
|
+
console.warn(t("cli_deactivateRemoteFailed"));
|
|
3385
|
+
}
|
|
3293
3386
|
return;
|
|
3294
3387
|
}
|
|
3295
3388
|
if (command === "status") {
|
|
@@ -3308,7 +3401,7 @@ async function runCli() {
|
|
|
3308
3401
|
return;
|
|
3309
3402
|
}
|
|
3310
3403
|
if (command === "install-brewbar") {
|
|
3311
|
-
const { installBrewBar } = await import("./brewbar-installer-
|
|
3404
|
+
const { installBrewBar } = await import("./brewbar-installer-4Z2WE57I.js");
|
|
3312
3405
|
try {
|
|
3313
3406
|
await installBrewBar(arg === "--force");
|
|
3314
3407
|
console.log(t("cli_brewbarInstalled"));
|
|
@@ -3319,7 +3412,7 @@ async function runCli() {
|
|
|
3319
3412
|
return;
|
|
3320
3413
|
}
|
|
3321
3414
|
if (command === "uninstall-brewbar") {
|
|
3322
|
-
const { uninstallBrewBar } = await import("./brewbar-installer-
|
|
3415
|
+
const { uninstallBrewBar } = await import("./brewbar-installer-4Z2WE57I.js");
|
|
3323
3416
|
try {
|
|
3324
3417
|
await uninstallBrewBar();
|
|
3325
3418
|
console.log(t("cli_brewbarUninstalled"));
|
|
@@ -3335,4 +3428,3 @@ runCli().catch((err) => {
|
|
|
3335
3428
|
console.error(err instanceof Error ? err.message : String(err));
|
|
3336
3429
|
process.exit(1);
|
|
3337
3430
|
});
|
|
3338
|
-
//# sourceMappingURL=index.js.map
|