brew-tui 0.5.2 → 0.6.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/{brewbar-installer-VVZRCP5K.js → brewbar-installer-BAHS6EEZ.js} +2 -2
- package/build/{chunk-42URLVAJ.js → chunk-AIAZQJKL.js} +2 -2
- package/build/{chunk-42URLVAJ.js.map → chunk-AIAZQJKL.js.map} +1 -1
- package/build/{chunk-3BPHXV35.js → chunk-MSXH66I2.js} +48 -4
- package/build/chunk-MSXH66I2.js.map +1 -0
- package/build/index.js +274 -59
- package/build/index.js.map +1 -1
- package/build/{sync-engine-4ERSW4EQ.js → sync-engine-CAFK4LHA.js} +2 -2
- package/package.json +4 -2
- package/build/chunk-3BPHXV35.js.map +0 -1
- /package/build/{brewbar-installer-VVZRCP5K.js.map → brewbar-installer-BAHS6EEZ.js.map} +0 -0
- /package/build/{sync-engine-4ERSW4EQ.js.map → sync-engine-CAFK4LHA.js.map} +0 -0
package/build/index.js
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
loadSyncConfig,
|
|
13
13
|
readSyncEnvelope,
|
|
14
14
|
sync
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-AIAZQJKL.js";
|
|
16
16
|
import {
|
|
17
17
|
checkCompliance
|
|
18
18
|
} from "./chunk-U2DRWB7A.js";
|
|
@@ -40,12 +40,13 @@ import {
|
|
|
40
40
|
ensureDataDirs
|
|
41
41
|
} from "./chunk-UWS4A4F5.js";
|
|
42
42
|
import {
|
|
43
|
+
fetchWithRetry,
|
|
43
44
|
fetchWithTimeout,
|
|
44
45
|
getLocale,
|
|
45
46
|
t,
|
|
46
47
|
tp,
|
|
47
48
|
useLocaleStore
|
|
48
|
-
} from "./chunk-
|
|
49
|
+
} from "./chunk-MSXH66I2.js";
|
|
49
50
|
import {
|
|
50
51
|
logger
|
|
51
52
|
} from "./chunk-KDHEUNRI.js";
|
|
@@ -151,7 +152,8 @@ var COLORS = {
|
|
|
151
152
|
purple: "#A855F7",
|
|
152
153
|
blue: "#3B82F6",
|
|
153
154
|
lavender: "#C4B5FD",
|
|
154
|
-
border: "#4B5563"
|
|
155
|
+
border: "#4B5563",
|
|
156
|
+
white: "#FFFFFF"
|
|
155
157
|
};
|
|
156
158
|
|
|
157
159
|
// src/utils/gradient.tsx
|
|
@@ -199,7 +201,8 @@ var GRADIENTS = {
|
|
|
199
201
|
emerald: ["#22C55E", "#2DD4BF", "#06B6D4"],
|
|
200
202
|
fire: ["#EF4444", "#F59E0B", "#FFD700"],
|
|
201
203
|
version: ["#EF4444", "#9CA3AF", "#2DD4BF"],
|
|
202
|
-
pro: ["#FF6B2B", "#FFD700", "#FF6B2B"]
|
|
204
|
+
pro: ["#FF6B2B", "#FFD700", "#FF6B2B"],
|
|
205
|
+
darkGold: ["#B8860B", "#8B6914", "#6B4F10"]
|
|
203
206
|
};
|
|
204
207
|
|
|
205
208
|
// src/components/layout/header.tsx
|
|
@@ -285,7 +288,7 @@ function MenuItem({ view, currentView }) {
|
|
|
285
288
|
"\u25B6",
|
|
286
289
|
" "
|
|
287
290
|
] }) : /* @__PURE__ */ jsx2(Text2, { children: " " }),
|
|
288
|
-
/* @__PURE__ */ jsx2(Text2, { bold: true, color: key ?
|
|
291
|
+
/* @__PURE__ */ jsx2(Text2, { bold: true, color: key ? COLORS.white : COLORS.textSecondary, children: indicator }),
|
|
289
292
|
/* @__PURE__ */ jsxs(Text2, { bold: isActive, underline: isActive, color: isActive ? COLORS.success : isAccount ? COLORS.gold : COLORS.textSecondary, children: [
|
|
290
293
|
" ",
|
|
291
294
|
viewLabel
|
|
@@ -306,7 +309,7 @@ function Header() {
|
|
|
306
309
|
const isNarrow = cols < 95;
|
|
307
310
|
const logoBlock = /* @__PURE__ */ jsx2(Box, { flexDirection: "column", flexShrink: 0, children: LOGO_BREW.map((brew, i) => /* @__PURE__ */ jsxs(Box, { children: [
|
|
308
311
|
/* @__PURE__ */ jsx2(GradientText, { colors: GRADIENTS.gold, children: brew }),
|
|
309
|
-
/* @__PURE__ */ jsx2(GradientText, { colors:
|
|
312
|
+
/* @__PURE__ */ jsx2(GradientText, { colors: GRADIENTS.darkGold, children: LOGO_TUI[i] })
|
|
310
313
|
] }, i)) });
|
|
311
314
|
const menuBlock = /* @__PURE__ */ jsxs(Box, { borderStyle: "round", borderColor: COLORS.lavender, paddingX: 1, flexDirection: "column", alignSelf: isNarrow ? "flex-start" : "center", children: [
|
|
312
315
|
/* @__PURE__ */ jsxs(Box, { flexDirection: "row", children: [
|
|
@@ -314,7 +317,7 @@ function Header() {
|
|
|
314
317
|
/* @__PURE__ */ jsx2(Box, { flexDirection: "column", marginLeft: 2, children: COL2_VIEWS.map((view) => /* @__PURE__ */ jsx2(MenuItem, { view, currentView }, view)) })
|
|
315
318
|
] }),
|
|
316
319
|
/* @__PURE__ */ jsxs(Box, { borderStyle: "single", borderTop: true, borderBottom: false, borderLeft: false, borderRight: false, borderColor: COLORS.lavender, marginTop: 0, children: [
|
|
317
|
-
/* @__PURE__ */ jsx2(Text2, { bold: true, color:
|
|
320
|
+
/* @__PURE__ */ jsx2(Text2, { bold: true, color: COLORS.white, children: "S" }),
|
|
318
321
|
/* @__PURE__ */ jsxs(Text2, { color: COLORS.textSecondary, children: [
|
|
319
322
|
" ",
|
|
320
323
|
t("hint_search")
|
|
@@ -324,7 +327,7 @@ function Header() {
|
|
|
324
327
|
"\u2503",
|
|
325
328
|
" "
|
|
326
329
|
] }),
|
|
327
|
-
/* @__PURE__ */ jsx2(Text2, { bold: true, color:
|
|
330
|
+
/* @__PURE__ */ jsx2(Text2, { bold: true, color: COLORS.white, children: "L" }),
|
|
328
331
|
/* @__PURE__ */ jsxs(Text2, { color: COLORS.textSecondary, children: [
|
|
329
332
|
" ",
|
|
330
333
|
t("hint_lang")
|
|
@@ -349,19 +352,19 @@ import { Box as Box2, Text as Text3 } from "ink";
|
|
|
349
352
|
import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
350
353
|
var VIEW_HINT_DEFS = {
|
|
351
354
|
dashboard: [["1-9,0", "hint_navigate"], ["S", "hint_search"], ["tab", "hint_next"], ["q", "hint_quit"]],
|
|
352
|
-
installed: [["/", "hint_filter"], ["enter", "hint_info"], ["u", "hint_uninstall"], ["S", "hint_search"], ["q", "hint_quit"]],
|
|
355
|
+
installed: [["/", "hint_filter"], ["enter", "hint_info"], ["u", "hint_uninstall"], ["f", "hint_switchTab"], ["S", "hint_search"], ["q", "hint_quit"]],
|
|
353
356
|
search: [["hint_typeToSearch"], ["enter", "hint_details"], ["i", "hint_install"], ["esc", "hint_back"], ["q", "hint_quit"]],
|
|
354
|
-
outdated: [["enter", "hint_upgrade"], ["A", "hint_upgradeAll"], ["p", "hint_pin"], ["S", "hint_search"], ["q", "hint_quit"]],
|
|
357
|
+
outdated: [["enter", "hint_upgrade"], ["A", "hint_upgradeAll"], ["p", "hint_pin"], ["r", "hint_refresh"], ["S", "hint_search"], ["q", "hint_quit"]],
|
|
355
358
|
"package-info": [["i", "hint_install"], ["u", "hint_uninstall"], ["U", "hint_upgrade"], ["esc", "hint_back"], ["q", "hint_quit"]],
|
|
356
359
|
services: [["s", "hint_start"], ["x", "hint_stop"], ["R", "hint_restart"], ["r", "hint_refresh"], ["S", "hint_search"], ["q", "hint_quit"]],
|
|
357
360
|
doctor: [["r", "hint_refresh"], ["S", "hint_search"], ["tab", "hint_next"], ["q", "hint_quit"]],
|
|
358
361
|
profiles: [["n", "hint_new"], ["enter", "hint_details"], ["e", "hint_edit"], ["i", "hint_import"], ["d", "hint_delete"], ["q", "hint_quit"]],
|
|
359
|
-
"smart-cleanup": [["enter", "hint_toggle"], ["c", "hint_clean"], ["r", "hint_refresh"], ["S", "hint_search"], ["q", "hint_quit"]],
|
|
362
|
+
"smart-cleanup": [["enter", "hint_toggle"], ["a", "hint_all"], ["c", "hint_clean"], ["F", "hint_force"], ["r", "hint_refresh"], ["S", "hint_search"], ["q", "hint_quit"]],
|
|
360
363
|
history: [["/", "hint_search"], ["enter", "hint_replay"], ["f", "hint_filter"], ["c", "hint_clear"], ["q", "hint_quit"]],
|
|
361
364
|
"security-audit": [["r", "hint_scan"], ["enter", "hint_details"], ["u", "hint_upgrade"], ["S", "hint_search"], ["q", "hint_quit"]],
|
|
362
365
|
rollback: [["j/k", "hint_navigate"], ["enter", "hint_select"], ["r", "hint_rollback_confirm"], ["esc", "hint_back"], ["q", "hint_quit"]],
|
|
363
366
|
brewfile: [["j/k", "hint_navigate"], ["a", "hint_add"], ["d", "hint_delete"], ["r", "hint_reconcile"], ["e", "hint_export"], ["q", "hint_quit"]],
|
|
364
|
-
sync: [["s", "hint_sync"], ["r", "hint_refresh"], ["c", "hint_conflict"], ["esc", "hint_back"], ["q", "hint_quit"]],
|
|
367
|
+
sync: [["s", "hint_sync"], ["r", "hint_refresh"], ["c", "hint_conflict"], ["l", "hint_useLocal"], ["esc", "hint_back"], ["q", "hint_quit"]],
|
|
365
368
|
compliance: [["r", "hint_scan"], ["i", "hint_import"], ["e", "hint_export"], ["c", "hint_clean"], ["q", "hint_quit"]],
|
|
366
369
|
account: [["p", "hint_promo"], ["d", "hint_deactivate"], ["S", "hint_search"], ["q", "hint_quit"]]
|
|
367
370
|
};
|
|
@@ -454,7 +457,7 @@ function validateApiUrl(url) {
|
|
|
454
457
|
async function post(endpoint, body, expectEmpty = false) {
|
|
455
458
|
const url = `${BASE_URL}/${endpoint}`;
|
|
456
459
|
validateApiUrl(url);
|
|
457
|
-
const res = await
|
|
460
|
+
const res = await fetchWithRetry(url, {
|
|
458
461
|
method: "POST",
|
|
459
462
|
headers: { "Content-Type": "application/json" },
|
|
460
463
|
body: JSON.stringify(body)
|
|
@@ -543,15 +546,6 @@ async function deactivateLicense(key, instanceId) {
|
|
|
543
546
|
}
|
|
544
547
|
|
|
545
548
|
// src/lib/license/license-manager.ts
|
|
546
|
-
var BUILTIN_ACCOUNTS = {
|
|
547
|
-
"admin@molinesdesigns.com": "pro",
|
|
548
|
-
"team@molinesdesigns.com": "team",
|
|
549
|
-
// Team tier test/admin account
|
|
550
|
-
"artax1983@icloud.com": "free"
|
|
551
|
-
};
|
|
552
|
-
function getBuiltinAccountType(email) {
|
|
553
|
-
return BUILTIN_ACCOUNTS[email] ?? null;
|
|
554
|
-
}
|
|
555
549
|
var REVALIDATION_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
556
550
|
var GRACE_PERIOD_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
557
551
|
var ACTIVATION_COOLDOWN_MS = 3e4;
|
|
@@ -630,9 +624,9 @@ function isEncryptedLicenseFile(obj) {
|
|
|
630
624
|
async function getMachineId2() {
|
|
631
625
|
try {
|
|
632
626
|
const { readFile: readMachineId } = await import("fs/promises");
|
|
633
|
-
const { join:
|
|
634
|
-
const { homedir:
|
|
635
|
-
const machineIdPath =
|
|
627
|
+
const { join: join7 } = await import("path");
|
|
628
|
+
const { homedir: homedir4 } = await import("os");
|
|
629
|
+
const machineIdPath = join7(homedir4(), ".brew-tui", "machine-id");
|
|
636
630
|
return (await readMachineId(machineIdPath, "utf-8")).trim() || null;
|
|
637
631
|
} catch {
|
|
638
632
|
return null;
|
|
@@ -801,6 +795,14 @@ function initStoreIntegrity(store) {
|
|
|
801
795
|
_originalIsPro = store.getState().isPro;
|
|
802
796
|
_originalGetState = store.getState;
|
|
803
797
|
}
|
|
798
|
+
function verifyStoreIntegrity() {
|
|
799
|
+
if (!_storeApi || !_originalIsPro || !_originalGetState) return false;
|
|
800
|
+
const state = _storeApi.getState();
|
|
801
|
+
if (state.isPro !== _originalIsPro) return false;
|
|
802
|
+
if (_storeApi.getState !== _originalGetState) return false;
|
|
803
|
+
if (state.status === "free" && state.isPro()) return false;
|
|
804
|
+
return true;
|
|
805
|
+
}
|
|
804
806
|
|
|
805
807
|
// src/stores/license-store.ts
|
|
806
808
|
var REVALIDATION_CHECK_MS = 60 * 60 * 1e3;
|
|
@@ -829,19 +831,6 @@ var useLicenseStore = create2((set, get) => ({
|
|
|
829
831
|
set({ status: "free", license: null, degradation: "none" });
|
|
830
832
|
return;
|
|
831
833
|
}
|
|
832
|
-
const builtinType = getBuiltinAccountType(license.customerEmail);
|
|
833
|
-
if (builtinType === "pro") {
|
|
834
|
-
set({ status: "pro", license: { ...license, status: "active" }, degradation: "none" });
|
|
835
|
-
return;
|
|
836
|
-
}
|
|
837
|
-
if (builtinType === "team") {
|
|
838
|
-
set({ status: "team", license: { ...license, status: "active", plan: "team" }, degradation: "none" });
|
|
839
|
-
return;
|
|
840
|
-
}
|
|
841
|
-
if (builtinType === "free") {
|
|
842
|
-
set({ status: "free", license: null, degradation: "none" });
|
|
843
|
-
return;
|
|
844
|
-
}
|
|
845
834
|
if (isExpired(license)) {
|
|
846
835
|
set({ status: "expired", license, degradation: "expired" });
|
|
847
836
|
return;
|
|
@@ -1254,7 +1243,7 @@ async function getInstalled() {
|
|
|
1254
1243
|
return parseInstalledJson(raw);
|
|
1255
1244
|
}
|
|
1256
1245
|
async function getOutdated() {
|
|
1257
|
-
const raw = await execBrew(["outdated", "--json=v2"]);
|
|
1246
|
+
const raw = await execBrew(["outdated", "--json=v2", "--greedy"]);
|
|
1258
1247
|
return parseOutdatedJson(raw);
|
|
1259
1248
|
}
|
|
1260
1249
|
async function getServices() {
|
|
@@ -1535,11 +1524,13 @@ function getFixedVersion(vuln) {
|
|
|
1535
1524
|
}
|
|
1536
1525
|
var BATCH_SIZE = 100;
|
|
1537
1526
|
async function queryBatch(packages, queries) {
|
|
1538
|
-
const res = await
|
|
1527
|
+
const res = await fetchWithRetry(OSV_BATCH_URL, {
|
|
1539
1528
|
method: "POST",
|
|
1540
1529
|
headers: { "Content-Type": "application/json" },
|
|
1541
1530
|
body: JSON.stringify({ queries })
|
|
1542
|
-
}, 15e3
|
|
1531
|
+
}, 15e3, {
|
|
1532
|
+
retryOn: (r) => r.status >= 500 && r.status < 600
|
|
1533
|
+
});
|
|
1543
1534
|
if (!res.ok) {
|
|
1544
1535
|
if (res.status === 400 && queries.length > 1) {
|
|
1545
1536
|
return queryOneByOne(packages);
|
|
@@ -1686,6 +1677,93 @@ async function runSecurityAudit(isPro, formulae, casks) {
|
|
|
1686
1677
|
};
|
|
1687
1678
|
}
|
|
1688
1679
|
|
|
1680
|
+
// src/lib/license/anti-debug.ts
|
|
1681
|
+
import inspector from "inspector";
|
|
1682
|
+
function isDebuggerAttached() {
|
|
1683
|
+
if (false) return false;
|
|
1684
|
+
if (inspector.url()) return true;
|
|
1685
|
+
if (process.execArgv.some((a) => a.includes("--inspect") || a.includes("--debug"))) return true;
|
|
1686
|
+
if (process.env.NODE_OPTIONS?.includes("--inspect")) return true;
|
|
1687
|
+
return false;
|
|
1688
|
+
}
|
|
1689
|
+
|
|
1690
|
+
// src/lib/license/canary.ts
|
|
1691
|
+
var _canaryTripped = false;
|
|
1692
|
+
function isProUnlocked() {
|
|
1693
|
+
return false;
|
|
1694
|
+
}
|
|
1695
|
+
function hasProAccess() {
|
|
1696
|
+
return false;
|
|
1697
|
+
}
|
|
1698
|
+
function isLicenseValid() {
|
|
1699
|
+
return false;
|
|
1700
|
+
}
|
|
1701
|
+
function checkCanaries() {
|
|
1702
|
+
if (isProUnlocked()) {
|
|
1703
|
+
_canaryTripped = true;
|
|
1704
|
+
return false;
|
|
1705
|
+
}
|
|
1706
|
+
if (hasProAccess()) {
|
|
1707
|
+
_canaryTripped = true;
|
|
1708
|
+
return false;
|
|
1709
|
+
}
|
|
1710
|
+
if (isLicenseValid()) {
|
|
1711
|
+
_canaryTripped = true;
|
|
1712
|
+
return false;
|
|
1713
|
+
}
|
|
1714
|
+
return !_canaryTripped;
|
|
1715
|
+
}
|
|
1716
|
+
|
|
1717
|
+
// src/lib/license/integrity.ts
|
|
1718
|
+
import { readFileSync } from "fs";
|
|
1719
|
+
import { createHash } from "crypto";
|
|
1720
|
+
import { fileURLToPath } from "url";
|
|
1721
|
+
var _baselineHash = null;
|
|
1722
|
+
var _isProduction = true;
|
|
1723
|
+
function _captureBaseline() {
|
|
1724
|
+
try {
|
|
1725
|
+
const bundlePath = fileURLToPath(import.meta.url);
|
|
1726
|
+
const content = readFileSync(bundlePath, "utf-8");
|
|
1727
|
+
return createHash("sha256").update(content).digest("hex");
|
|
1728
|
+
} catch {
|
|
1729
|
+
return null;
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1732
|
+
_baselineHash = _captureBaseline();
|
|
1733
|
+
function checkBundleIntegrity() {
|
|
1734
|
+
if (_baselineHash === null) {
|
|
1735
|
+
return !_isProduction;
|
|
1736
|
+
}
|
|
1737
|
+
try {
|
|
1738
|
+
const bundlePath = fileURLToPath(import.meta.url);
|
|
1739
|
+
const content = readFileSync(bundlePath, "utf-8");
|
|
1740
|
+
const current = createHash("sha256").update(content).digest("hex");
|
|
1741
|
+
return current === _baselineHash;
|
|
1742
|
+
} catch {
|
|
1743
|
+
return !_isProduction;
|
|
1744
|
+
}
|
|
1745
|
+
}
|
|
1746
|
+
|
|
1747
|
+
// src/lib/license/pro-guard.ts
|
|
1748
|
+
var _P = String.fromCharCode(112, 114, 111);
|
|
1749
|
+
function _verify(status) {
|
|
1750
|
+
return status === _P;
|
|
1751
|
+
}
|
|
1752
|
+
function verifyPro(license, status) {
|
|
1753
|
+
if (isDebuggerAttached()) return false;
|
|
1754
|
+
if (!checkBundleIntegrity()) return false;
|
|
1755
|
+
if (!verifyStoreIntegrity()) return false;
|
|
1756
|
+
if (!checkCanaries()) return false;
|
|
1757
|
+
const directCheck = status === "pro";
|
|
1758
|
+
const indirectCheck = _verify(status);
|
|
1759
|
+
if (!directCheck || !indirectCheck) return false;
|
|
1760
|
+
if (license) {
|
|
1761
|
+
const level = getDegradationLevel(license);
|
|
1762
|
+
if (level === "expired" || level === "limited") return false;
|
|
1763
|
+
}
|
|
1764
|
+
return true;
|
|
1765
|
+
}
|
|
1766
|
+
|
|
1689
1767
|
// src/stores/security-store.ts
|
|
1690
1768
|
var CACHE_TTL_MS = 30 * 60 * 1e3;
|
|
1691
1769
|
var useSecurityStore = create5((set, get) => ({
|
|
@@ -1705,7 +1783,8 @@ var useSecurityStore = create5((set, get) => ({
|
|
|
1705
1783
|
await brewState.fetchInstalled();
|
|
1706
1784
|
}
|
|
1707
1785
|
const { formulae, casks } = useBrewStore.getState();
|
|
1708
|
-
const
|
|
1786
|
+
const { license, status } = useLicenseStore.getState();
|
|
1787
|
+
const isPro = verifyPro(license, status);
|
|
1709
1788
|
const result = await runSecurityAudit(isPro, formulae, casks);
|
|
1710
1789
|
set({ summary: result, loading: false, cachedAt: Date.now() });
|
|
1711
1790
|
} catch (err) {
|
|
@@ -2199,8 +2278,7 @@ function useBrewStream() {
|
|
|
2199
2278
|
const MUTATING_COMMANDS = /* @__PURE__ */ new Set(["install", "uninstall", "upgrade", "pin", "unpin", "tap", "untap"]);
|
|
2200
2279
|
if (!cancelRef.current && MUTATING_COMMANDS.has(args[0] ?? "")) {
|
|
2201
2280
|
void import("./snapshot-RAPGMAJF.js").then(({ captureSnapshot: captureSnapshot2, saveSnapshot: saveSnapshot2 }) => {
|
|
2202
|
-
captureSnapshot2().then((s) => saveSnapshot2(s)).catch(() => {
|
|
2203
|
-
});
|
|
2281
|
+
captureSnapshot2().then((s) => saveSnapshot2(s)).catch((err) => logger.warn("snapshot: capture/save failed", { error: String(err) }));
|
|
2204
2282
|
});
|
|
2205
2283
|
}
|
|
2206
2284
|
}, []);
|
|
@@ -2312,7 +2390,11 @@ function SelectableRow({ isCurrent, children, gap = 1 }) {
|
|
|
2312
2390
|
// src/views/installed.tsx
|
|
2313
2391
|
import { jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
2314
2392
|
function InstalledView() {
|
|
2315
|
-
const
|
|
2393
|
+
const formulae = useBrewStore((s) => s.formulae);
|
|
2394
|
+
const casks = useBrewStore((s) => s.casks);
|
|
2395
|
+
const loading = useBrewStore((s) => s.loading);
|
|
2396
|
+
const errors = useBrewStore((s) => s.errors);
|
|
2397
|
+
const fetchInstalled = useBrewStore((s) => s.fetchInstalled);
|
|
2316
2398
|
const navigate = useNavigationStore((s) => s.navigate);
|
|
2317
2399
|
const selectPackage = useNavigationStore((s) => s.selectPackage);
|
|
2318
2400
|
const [filter, setFilter] = useState3("");
|
|
@@ -3420,7 +3502,8 @@ async function* importProfile(isPro, profile) {
|
|
|
3420
3502
|
|
|
3421
3503
|
// src/stores/profile-store.ts
|
|
3422
3504
|
function getIsPro() {
|
|
3423
|
-
|
|
3505
|
+
const { license, status } = useLicenseStore.getState();
|
|
3506
|
+
return verifyPro(license, status);
|
|
3424
3507
|
}
|
|
3425
3508
|
var useProfileStore = create9((set) => ({
|
|
3426
3509
|
profileNames: [],
|
|
@@ -3907,7 +3990,8 @@ var useCleanupStore = create10((set, get) => ({
|
|
|
3907
3990
|
await brewState.fetchLeaves();
|
|
3908
3991
|
}
|
|
3909
3992
|
const { formulae, leaves } = useBrewStore.getState();
|
|
3910
|
-
const
|
|
3993
|
+
const { license, status } = useLicenseStore.getState();
|
|
3994
|
+
const isPro = verifyPro(license, status);
|
|
3911
3995
|
const summary = await analyzeCleanup(isPro, formulae, leaves);
|
|
3912
3996
|
set({ summary, selected: /* @__PURE__ */ new Set(), loading: false });
|
|
3913
3997
|
} catch (err) {
|
|
@@ -4068,6 +4152,10 @@ import { Box as Box26, Text as Text27, useInput as useInput11, useStdout as useS
|
|
|
4068
4152
|
|
|
4069
4153
|
// src/stores/history-store.ts
|
|
4070
4154
|
import { create as create11 } from "zustand";
|
|
4155
|
+
function getStrongIsPro() {
|
|
4156
|
+
const { license, status } = useLicenseStore.getState();
|
|
4157
|
+
return verifyPro(license, status);
|
|
4158
|
+
}
|
|
4071
4159
|
var useHistoryStore = create11((set) => ({
|
|
4072
4160
|
entries: [],
|
|
4073
4161
|
loading: false,
|
|
@@ -4075,7 +4163,7 @@ var useHistoryStore = create11((set) => ({
|
|
|
4075
4163
|
fetchHistory: async () => {
|
|
4076
4164
|
set({ loading: true, error: null });
|
|
4077
4165
|
try {
|
|
4078
|
-
const isPro =
|
|
4166
|
+
const isPro = getStrongIsPro();
|
|
4079
4167
|
const entries = await loadHistory(isPro);
|
|
4080
4168
|
set({ entries, loading: false });
|
|
4081
4169
|
} catch (err) {
|
|
@@ -4083,13 +4171,13 @@ var useHistoryStore = create11((set) => ({
|
|
|
4083
4171
|
}
|
|
4084
4172
|
},
|
|
4085
4173
|
logAction: async (action, packageName, success, error = null) => {
|
|
4086
|
-
const isPro =
|
|
4174
|
+
const isPro = getStrongIsPro();
|
|
4087
4175
|
await appendEntry(isPro, action, packageName, success, error);
|
|
4088
4176
|
const entries = await loadHistory(isPro);
|
|
4089
4177
|
set({ entries });
|
|
4090
4178
|
},
|
|
4091
4179
|
clearHistory: async () => {
|
|
4092
|
-
const isPro =
|
|
4180
|
+
const isPro = getStrongIsPro();
|
|
4093
4181
|
await clearHistory(isPro);
|
|
4094
4182
|
set({ entries: [] });
|
|
4095
4183
|
}
|
|
@@ -4416,6 +4504,8 @@ var PROMO_API_URL = "https://api.molinesdesigns.com/api/promo";
|
|
|
4416
4504
|
async function redeemPromoCode(code) {
|
|
4417
4505
|
const normalized = code.trim().toUpperCase();
|
|
4418
4506
|
const machineId = await getMachineId3();
|
|
4507
|
+
let serverExpiresAt;
|
|
4508
|
+
let serverType;
|
|
4419
4509
|
try {
|
|
4420
4510
|
const res = await fetchWithTimeout(`${PROMO_API_URL}/redeem`, {
|
|
4421
4511
|
method: "POST",
|
|
@@ -4427,8 +4517,17 @@ async function redeemPromoCode(code) {
|
|
|
4427
4517
|
return { success: false, error: body.error ?? "Invalid or expired promo code" };
|
|
4428
4518
|
}
|
|
4429
4519
|
const data = await res.json();
|
|
4430
|
-
|
|
4431
|
-
|
|
4520
|
+
const inner = data?.data;
|
|
4521
|
+
if (!inner || typeof inner !== "object" || typeof inner.expiresAt !== "string" || typeof inner.type !== "string") {
|
|
4522
|
+
return { success: false, error: "Unexpected promo redeem response" };
|
|
4523
|
+
}
|
|
4524
|
+
const expiresAt = inner.expiresAt;
|
|
4525
|
+
const type = inner.type;
|
|
4526
|
+
if (type !== "trial" && type !== "discount" && type !== "full") {
|
|
4527
|
+
return { success: false, error: "Unsupported promo type" };
|
|
4528
|
+
}
|
|
4529
|
+
serverExpiresAt = expiresAt;
|
|
4530
|
+
serverType = type;
|
|
4432
4531
|
} catch (err) {
|
|
4433
4532
|
logger.error("Promo redeem failed", { error: String(err) });
|
|
4434
4533
|
return { success: false, error: "Could not reach promo server. Check your connection." };
|
|
@@ -4605,7 +4704,7 @@ function AccountView() {
|
|
|
4605
4704
|
/* @__PURE__ */ jsx30(Box28, { marginTop: 1, children: /* @__PURE__ */ jsxs29(Text29, { color: COLORS.textSecondary, children: [
|
|
4606
4705
|
status === "pro" ? `d ${t("hint_deactivate")}` : "",
|
|
4607
4706
|
" ",
|
|
4608
|
-
t("app_version", { version: "0.
|
|
4707
|
+
t("app_version", { version: "0.6.0" })
|
|
4609
4708
|
] }) })
|
|
4610
4709
|
] });
|
|
4611
4710
|
}
|
|
@@ -5763,7 +5862,7 @@ function ComplianceView() {
|
|
|
5763
5862
|
report ? /* @__PURE__ */ jsxs33(Box32, { flexDirection: "column", marginTop: 1, children: [
|
|
5764
5863
|
/* @__PURE__ */ jsx34(ComplianceScore, { report }),
|
|
5765
5864
|
report.compliant ? /* @__PURE__ */ jsx34(ResultBanner, { status: "success", message: t("compliance_ok") }) : /* @__PURE__ */ jsx34(ViolationList, { violations: report.violations })
|
|
5766
|
-
] }) : /* @__PURE__ */ jsx34(Box32, { marginTop: 1, children: /* @__PURE__ */ jsx34(Text33, { color: COLORS.muted, dimColor: true, children: "
|
|
5865
|
+
] }) : /* @__PURE__ */ jsx34(Box32, { marginTop: 1, children: /* @__PURE__ */ jsx34(Text33, { color: COLORS.muted, dimColor: true, children: t("compliance_press_r_hint") }) })
|
|
5767
5866
|
] }),
|
|
5768
5867
|
/* @__PURE__ */ jsx34(Box32, { marginTop: 2, flexWrap: "wrap", children: /* @__PURE__ */ jsxs33(Text33, { color: COLORS.textSecondary, children: [
|
|
5769
5868
|
"i:",
|
|
@@ -5850,11 +5949,127 @@ function App() {
|
|
|
5850
5949
|
] });
|
|
5851
5950
|
}
|
|
5852
5951
|
|
|
5952
|
+
// src/lib/crash-reporter.ts
|
|
5953
|
+
import { readFile as readFile5, writeFile as writeFile5, mkdir as mkdir3 } from "fs/promises";
|
|
5954
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
5955
|
+
import { homedir as homedir3, platform, release, arch } from "os";
|
|
5956
|
+
import { join as join6 } from "path";
|
|
5957
|
+
var ENDPOINT_ENV = "BREW_TUI_CRASH_ENDPOINT";
|
|
5958
|
+
var TOKEN_ENV = "BREW_TUI_CRASH_TOKEN";
|
|
5959
|
+
var CONFIG_PATH = join6(homedir3(), ".brew-tui", "crash-reporter.json");
|
|
5960
|
+
var MACHINE_ID_PATH3 = join6(homedir3(), ".brew-tui", "machine-id");
|
|
5961
|
+
var POST_TIMEOUT_MS = 5e3;
|
|
5962
|
+
var _installed = false;
|
|
5963
|
+
async function loadConfigFromDisk() {
|
|
5964
|
+
try {
|
|
5965
|
+
const raw = await readFile5(CONFIG_PATH, "utf-8");
|
|
5966
|
+
const parsed = JSON.parse(raw);
|
|
5967
|
+
return {
|
|
5968
|
+
endpoint: typeof parsed.endpoint === "string" ? parsed.endpoint : null,
|
|
5969
|
+
token: typeof parsed.token === "string" ? parsed.token : null,
|
|
5970
|
+
enabled: parsed.enabled === true
|
|
5971
|
+
};
|
|
5972
|
+
} catch {
|
|
5973
|
+
return { endpoint: null, token: null, enabled: false };
|
|
5974
|
+
}
|
|
5975
|
+
}
|
|
5976
|
+
async function getMachineId4() {
|
|
5977
|
+
try {
|
|
5978
|
+
const id2 = (await readFile5(MACHINE_ID_PATH3, "utf-8")).trim();
|
|
5979
|
+
if (id2) return id2;
|
|
5980
|
+
} catch {
|
|
5981
|
+
}
|
|
5982
|
+
const id = randomUUID3();
|
|
5983
|
+
try {
|
|
5984
|
+
await mkdir3(join6(homedir3(), ".brew-tui"), { recursive: true, mode: 448 });
|
|
5985
|
+
await writeFile5(MACHINE_ID_PATH3, id, { encoding: "utf-8", mode: 384 });
|
|
5986
|
+
} catch {
|
|
5987
|
+
}
|
|
5988
|
+
return id;
|
|
5989
|
+
}
|
|
5990
|
+
async function resolveConfig() {
|
|
5991
|
+
const envEndpoint = process.env[ENDPOINT_ENV]?.trim();
|
|
5992
|
+
const envToken = process.env[TOKEN_ENV]?.trim();
|
|
5993
|
+
if (envEndpoint) {
|
|
5994
|
+
return { endpoint: envEndpoint, token: envToken ?? null, enabled: true };
|
|
5995
|
+
}
|
|
5996
|
+
return loadConfigFromDisk();
|
|
5997
|
+
}
|
|
5998
|
+
function isHttpsOrLocal(url) {
|
|
5999
|
+
try {
|
|
6000
|
+
const parsed = new URL(url);
|
|
6001
|
+
if (parsed.protocol === "https:") return true;
|
|
6002
|
+
if (parsed.protocol === "http:") {
|
|
6003
|
+
const host = parsed.hostname;
|
|
6004
|
+
return /^(localhost|127\.\d+\.\d+\.\d+|10\.|192\.168\.|172\.(1[6-9]|2\d|3[0-1])\.|::1$)/i.test(host);
|
|
6005
|
+
}
|
|
6006
|
+
return false;
|
|
6007
|
+
} catch {
|
|
6008
|
+
return false;
|
|
6009
|
+
}
|
|
6010
|
+
}
|
|
6011
|
+
async function postReport(report, config) {
|
|
6012
|
+
if (!config.endpoint || !isHttpsOrLocal(config.endpoint)) return;
|
|
6013
|
+
try {
|
|
6014
|
+
await fetch(config.endpoint, {
|
|
6015
|
+
method: "POST",
|
|
6016
|
+
headers: {
|
|
6017
|
+
"Content-Type": "application/json",
|
|
6018
|
+
...config.token ? { "Authorization": `Bearer ${config.token}` } : {}
|
|
6019
|
+
},
|
|
6020
|
+
body: JSON.stringify(report),
|
|
6021
|
+
signal: AbortSignal.timeout(POST_TIMEOUT_MS)
|
|
6022
|
+
});
|
|
6023
|
+
} catch (err) {
|
|
6024
|
+
logger.warn("crash-reporter: POST failed", { error: String(err) });
|
|
6025
|
+
}
|
|
6026
|
+
}
|
|
6027
|
+
function buildReport(level, err, context, machineId, version) {
|
|
6028
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
6029
|
+
const stack = err instanceof Error && typeof err.stack === "string" ? err.stack : null;
|
|
6030
|
+
return {
|
|
6031
|
+
app: "brew-tui",
|
|
6032
|
+
version,
|
|
6033
|
+
platform: platform(),
|
|
6034
|
+
os: release(),
|
|
6035
|
+
arch: arch(),
|
|
6036
|
+
machineId,
|
|
6037
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6038
|
+
level,
|
|
6039
|
+
message,
|
|
6040
|
+
stack,
|
|
6041
|
+
context
|
|
6042
|
+
};
|
|
6043
|
+
}
|
|
6044
|
+
async function reportError(err, context = {}) {
|
|
6045
|
+
const config = await resolveConfig();
|
|
6046
|
+
if (!config.enabled || !config.endpoint) return;
|
|
6047
|
+
const machineId = await getMachineId4();
|
|
6048
|
+
const version = true ? "0.6.0" : "unknown";
|
|
6049
|
+
await postReport(buildReport("error", err, context, machineId, version), config);
|
|
6050
|
+
}
|
|
6051
|
+
async function installCrashReporter() {
|
|
6052
|
+
if (_installed) return;
|
|
6053
|
+
const config = await resolveConfig();
|
|
6054
|
+
if (!config.enabled || !config.endpoint) return;
|
|
6055
|
+
_installed = true;
|
|
6056
|
+
const machineId = await getMachineId4();
|
|
6057
|
+
const version = true ? "0.6.0" : "unknown";
|
|
6058
|
+
process.on("uncaughtException", (err) => {
|
|
6059
|
+
void postReport(buildReport("fatal", err, { kind: "uncaughtException" }, machineId, version), config);
|
|
6060
|
+
});
|
|
6061
|
+
process.on("unhandledRejection", (reason) => {
|
|
6062
|
+
void postReport(buildReport("error", reason, { kind: "unhandledRejection" }, machineId, version), config);
|
|
6063
|
+
});
|
|
6064
|
+
logger.info("crash-reporter: enabled", { endpoint: config.endpoint });
|
|
6065
|
+
}
|
|
6066
|
+
|
|
5853
6067
|
// src/index.tsx
|
|
5854
6068
|
import { jsx as jsx36 } from "react/jsx-runtime";
|
|
5855
6069
|
var [, , command, arg] = process.argv;
|
|
5856
6070
|
async function runCli() {
|
|
5857
6071
|
await ensureDataDirs();
|
|
6072
|
+
await installCrashReporter();
|
|
5858
6073
|
if (command === "activate") {
|
|
5859
6074
|
const key = arg?.trim() ?? "";
|
|
5860
6075
|
if (!key) {
|
|
@@ -5966,7 +6181,7 @@ Snapshots: ${snapshots.length} (latest: ${latest ? formatDate(latest.capturedAt)
|
|
|
5966
6181
|
} catch {
|
|
5967
6182
|
}
|
|
5968
6183
|
try {
|
|
5969
|
-
const { loadSyncConfig: loadSyncConfig2 } = await import("./sync-engine-
|
|
6184
|
+
const { loadSyncConfig: loadSyncConfig2 } = await import("./sync-engine-CAFK4LHA.js");
|
|
5970
6185
|
const syncConfig = await loadSyncConfig2();
|
|
5971
6186
|
if (syncConfig?.lastSync) {
|
|
5972
6187
|
console.log(`Sync: last sync ${formatDate(syncConfig.lastSync)}`);
|
|
@@ -5989,7 +6204,7 @@ Snapshots: ${snapshots.length} (latest: ${latest ? formatDate(latest.capturedAt)
|
|
|
5989
6204
|
if (command === "install-brewbar") {
|
|
5990
6205
|
await useLicenseStore.getState().initialize();
|
|
5991
6206
|
const isPro = useLicenseStore.getState().isPro();
|
|
5992
|
-
const { installBrewBar } = await import("./brewbar-installer-
|
|
6207
|
+
const { installBrewBar } = await import("./brewbar-installer-BAHS6EEZ.js");
|
|
5993
6208
|
try {
|
|
5994
6209
|
await installBrewBar(isPro, arg === "--force");
|
|
5995
6210
|
console.log(t("cli_brewbarInstalled"));
|
|
@@ -6000,7 +6215,7 @@ Snapshots: ${snapshots.length} (latest: ${latest ? formatDate(latest.capturedAt)
|
|
|
6000
6215
|
return;
|
|
6001
6216
|
}
|
|
6002
6217
|
if (command === "uninstall-brewbar") {
|
|
6003
|
-
const { uninstallBrewBar } = await import("./brewbar-installer-
|
|
6218
|
+
const { uninstallBrewBar } = await import("./brewbar-installer-BAHS6EEZ.js");
|
|
6004
6219
|
try {
|
|
6005
6220
|
await uninstallBrewBar();
|
|
6006
6221
|
console.log(t("cli_brewbarUninstalled"));
|
|
@@ -6031,7 +6246,7 @@ async function ensureBrewBarRunning() {
|
|
|
6031
6246
|
if (process.platform !== "darwin") return;
|
|
6032
6247
|
await useLicenseStore.getState().initialize();
|
|
6033
6248
|
if (!useLicenseStore.getState().isPro()) return;
|
|
6034
|
-
const { isBrewBarInstalled, installBrewBar, launchBrewBar } = await import("./brewbar-installer-
|
|
6249
|
+
const { isBrewBarInstalled, installBrewBar, launchBrewBar } = await import("./brewbar-installer-BAHS6EEZ.js");
|
|
6035
6250
|
try {
|
|
6036
6251
|
if (!await isBrewBarInstalled()) {
|
|
6037
6252
|
console.log(t("cli_brewbarInstalling"));
|
|
@@ -6045,6 +6260,6 @@ async function ensureBrewBarRunning() {
|
|
|
6045
6260
|
}
|
|
6046
6261
|
runCli().catch((err) => {
|
|
6047
6262
|
console.error(err instanceof Error ? err.message : String(err));
|
|
6048
|
-
process.exit(1);
|
|
6263
|
+
void reportError(err, { kind: "runCli-rejection" }).finally(() => process.exit(1));
|
|
6049
6264
|
});
|
|
6050
6265
|
//# sourceMappingURL=index.js.map
|