brew-tui 1.2.3 → 2.0.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 +12 -12
- package/build/brew-tui-bar-installer-4JLEZSJA.js +227 -0
- package/build/brew-tui-bar-installer-4JLEZSJA.js.map +1 -0
- package/build/{brewfile-manager-CPVXIVZC.js → brewfile-manager-PF25DV6U.js} +5 -5
- package/build/{chunk-XI743B6D.js → chunk-43UJQAVI.js} +4 -4
- package/build/{chunk-WX7MPVPH.js → chunk-7EMQAD6F.js} +31 -29
- package/build/chunk-7EMQAD6F.js.map +1 -0
- package/build/{chunk-EDQPT5EF.js → chunk-A37H6V7O.js} +4 -4
- package/build/chunk-A37H6V7O.js.map +1 -0
- package/build/{chunk-CMIC4N74.js → chunk-DKOAK4RV.js} +2 -2
- package/build/{chunk-IGDHDXUH.js → chunk-LFGDNAXH.js} +1 -1
- package/build/chunk-LFGDNAXH.js.map +1 -0
- package/build/{chunk-JNEIP2LJ.js → chunk-MPQVKFZX.js} +2 -2
- package/build/{chunk-VQJBFXZN.js → chunk-YVY3REPD.js} +2 -2
- package/build/compliance-checker-KFXSTQBA.js +12 -0
- package/build/{history-logger-SB5UG5BQ.js → history-logger-VJEVQGCF.js} +3 -3
- package/build/index.js +46 -40
- package/build/index.js.map +1 -1
- package/build/{snapshot-RQ444U5L.js → snapshot-OLD75OWU.js} +3 -3
- package/build/{sync-engine-Q4B2PPQS.js → sync-engine-DPLNZDAK.js} +5 -5
- package/build/{version-check-NS6RPUSU.js → version-check-EDIRWKBT.js} +11 -11
- package/build/version-check-EDIRWKBT.js.map +1 -0
- package/package.json +3 -3
- package/build/brewbar-installer-2OHLRM27.js +0 -161
- package/build/brewbar-installer-2OHLRM27.js.map +0 -1
- package/build/chunk-EDQPT5EF.js.map +0 -1
- package/build/chunk-IGDHDXUH.js.map +0 -1
- package/build/chunk-WX7MPVPH.js.map +0 -1
- package/build/compliance-checker-3FDEX4OI.js +0 -12
- package/build/version-check-NS6RPUSU.js.map +0 -1
- /package/build/{brewfile-manager-CPVXIVZC.js.map → brewfile-manager-PF25DV6U.js.map} +0 -0
- /package/build/{chunk-XI743B6D.js.map → chunk-43UJQAVI.js.map} +0 -0
- /package/build/{chunk-CMIC4N74.js.map → chunk-DKOAK4RV.js.map} +0 -0
- /package/build/{chunk-JNEIP2LJ.js.map → chunk-MPQVKFZX.js.map} +0 -0
- /package/build/{chunk-VQJBFXZN.js.map → chunk-YVY3REPD.js.map} +0 -0
- /package/build/{compliance-checker-3FDEX4OI.js.map → compliance-checker-KFXSTQBA.js.map} +0 -0
- /package/build/{history-logger-SB5UG5BQ.js.map → history-logger-VJEVQGCF.js.map} +0 -0
- /package/build/{snapshot-RQ444U5L.js.map → snapshot-OLD75OWU.js.map} +0 -0
- /package/build/{sync-engine-Q4B2PPQS.js.map → sync-engine-DPLNZDAK.js.map} +0 -0
package/README.md
CHANGED
|
@@ -28,7 +28,7 @@ You don't memorize `brew outdated && brew upgrade && brew services list && brew
|
|
|
28
28
|
| `brew outdated` → wall of text → grep | Press **3** → list with version arrows → `Enter` to upgrade |
|
|
29
29
|
| `brew services list` → restart by hand | Press **4** → toggle services with one key |
|
|
30
30
|
| Vulnerable packages? | Press **9** → cross-checked against [OSV.dev](https://osv.dev) (Pro) |
|
|
31
|
-
| Forgot to update? | **
|
|
31
|
+
| Forgot to update? | **Brew-TUI-Bar** lives in your menu bar and tells you (Pro) |
|
|
32
32
|
|
|
33
33
|
---
|
|
34
34
|
|
|
@@ -68,14 +68,14 @@ npx brew-tui
|
|
|
68
68
|
|---------|----------------|
|
|
69
69
|
| **Smart Rollback** | Auto-snapshots after every install/upgrade/uninstall/pin; revert with bottle/versioned/pin strategies. Press `R` from a flagged CVE to jump straight to the rollback plan |
|
|
70
70
|
| **Cross-machine Sync** | iCloud Drive backend, AES-256-GCM encrypted client-side. Brewfile and snapshots stay aligned across all your Macs with interactive conflict resolution |
|
|
71
|
-
| **CVE Real-time** |
|
|
71
|
+
| **CVE Real-time** | Brew-TUI-Bar polls [OSV.dev](https://osv.dev) hourly. Critical/high CVEs trigger native macOS notifications and a badge count |
|
|
72
72
|
| **Declarative Brewfile** | YAML desired state, drift score 0-100, interactive reconciliation. Closer to a lightweight Nix-flake than `brew bundle` |
|
|
73
73
|
| **Impact Analysis** | Pre-upgrade risk panel (low/medium/high) with dependency tree and reverse-deps that will be affected, surfaced before each upgrade |
|
|
74
74
|
| **Profiles** | Replicate your exact setup on a new Mac in one command |
|
|
75
75
|
| **Smart Cleanup** | Reclaim gigabytes by listing orphans ranked by size |
|
|
76
76
|
| **Action History** | "What did I install last week?" — answered |
|
|
77
77
|
| **Security Audit** | Cross-checks every installed package against [OSV.dev](https://osv.dev) live |
|
|
78
|
-
| **
|
|
78
|
+
| **Brew-TUI-Bar** | A menu bar app that watches your packages while you sleep — auto-installs and auto-launches the moment you go Pro |
|
|
79
79
|
|
|
80
80
|
### Team Tier — €8/seat/mo or €81,60/seat/year (-15%, min 3 seats)
|
|
81
81
|
|
|
@@ -141,9 +141,9 @@ Brew-TUI supports **English** and **Spanish**. Language is detected from your sy
|
|
|
141
141
|
|
|
142
142
|
---
|
|
143
143
|
|
|
144
|
-
##
|
|
144
|
+
## Brew-TUI-Bar (Pro)
|
|
145
145
|
|
|
146
|
-
|
|
146
|
+
Brew-TUI-Bar is a native macOS menu bar companion app (Swift 6 / SwiftUI) that:
|
|
147
147
|
|
|
148
148
|
- Shows a badge with outdated package count
|
|
149
149
|
- Sends push notifications when updates are available
|
|
@@ -152,13 +152,13 @@ BrewBar is a native macOS menu bar companion app (Swift 6 / SwiftUI) that:
|
|
|
152
152
|
- Configurable check interval (1h / 4h / 8h)
|
|
153
153
|
- Supports Launch at Login
|
|
154
154
|
|
|
155
|
-
### Install
|
|
155
|
+
### Install Brew-TUI-Bar
|
|
156
156
|
|
|
157
157
|
```bash
|
|
158
158
|
# Via Brew-TUI CLI (Pro license required)
|
|
159
|
-
brew-tui install-
|
|
160
|
-
brew-tui install-
|
|
161
|
-
brew-tui uninstall-
|
|
159
|
+
brew-tui install-brew-tui-bar
|
|
160
|
+
brew-tui install-brew-tui-bar --force # Reinstall / update
|
|
161
|
+
brew-tui uninstall-brew-tui-bar # Remove
|
|
162
162
|
|
|
163
163
|
# Via Homebrew Cask
|
|
164
164
|
brew install --cask MoLinesDesigns/tap/brewbar
|
|
@@ -169,7 +169,7 @@ brew install --cask MoLinesDesigns/tap/brewbar
|
|
|
169
169
|
```bash
|
|
170
170
|
cd menubar
|
|
171
171
|
tuist generate
|
|
172
|
-
xcodebuild -workspace
|
|
172
|
+
xcodebuild -workspace Brew-TUI-Bar.xcworkspace -scheme Brew-TUI-Bar build
|
|
173
173
|
```
|
|
174
174
|
|
|
175
175
|
Requires [Tuist](https://tuist.io), Xcode, and macOS 14+.
|
|
@@ -200,7 +200,7 @@ Views (React/Ink) --> Stores (Zustand) --> brew-api --> Parsers --> brew CLI (sp
|
|
|
200
200
|
## Security
|
|
201
201
|
|
|
202
202
|
- License data encrypted with AES-256-GCM, machine-bound via UUID
|
|
203
|
-
- SHA-256 verification on
|
|
203
|
+
- SHA-256 verification on Brew-TUI-Bar binary downloads
|
|
204
204
|
- Bundle integrity check at startup (fail-closed)
|
|
205
205
|
- Runtime validation of all external API responses (Polar, OSV)
|
|
206
206
|
- Rate limiting on license activation (5 attempts / 15min lockout)
|
|
@@ -232,7 +232,7 @@ src/
|
|
|
232
232
|
parsers/ # JSON and text parsers for brew output
|
|
233
233
|
i18n/ # English + Spanish translations
|
|
234
234
|
utils/ # Colors, spacing, logger, formatting
|
|
235
|
-
menubar/ #
|
|
235
|
+
menubar/ # Brew-TUI-Bar (Swift 6 / SwiftUI / Tuist)
|
|
236
236
|
```
|
|
237
237
|
|
|
238
238
|
---
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import {
|
|
2
|
+
fetchWithTimeout
|
|
3
|
+
} from "./chunk-NRRQECXA.js";
|
|
4
|
+
import {
|
|
5
|
+
t
|
|
6
|
+
} from "./chunk-7EMQAD6F.js";
|
|
7
|
+
import "./chunk-KDHEUNRI.js";
|
|
8
|
+
|
|
9
|
+
// src/lib/brew-tui-bar-installer.ts
|
|
10
|
+
import { rm, access, readFile } from "fs/promises";
|
|
11
|
+
import { createWriteStream } from "fs";
|
|
12
|
+
import { createHash, randomUUID } from "crypto";
|
|
13
|
+
import { tmpdir } from "os";
|
|
14
|
+
import { join } from "path";
|
|
15
|
+
import { pipeline } from "stream/promises";
|
|
16
|
+
import { execFile } from "child_process";
|
|
17
|
+
import { promisify } from "util";
|
|
18
|
+
var execFileAsync = promisify(execFile);
|
|
19
|
+
var BREWTUIBAR_APP_PATH = "/Applications/Brew-TUI-Bar.app";
|
|
20
|
+
var BREWTUIBAR_BUNDLE_ID = "com.molinesdesigns.brewtuibar";
|
|
21
|
+
var BREWTUIBAR_PROCESS_NAME = "Brew-TUI-Bar";
|
|
22
|
+
var LEGACY_APP_PATH = "/Applications/BrewBar.app";
|
|
23
|
+
var LEGACY_BUNDLE_ID = "com.molinesdesigns.brewbar";
|
|
24
|
+
var LEGACY_PROCESS_NAME = "BrewBar";
|
|
25
|
+
var DOWNLOAD_URL = "https://github.com/MoLinesDesigns/Brew-TUI/releases/latest/download/Brew-TUI-Bar.app.zip";
|
|
26
|
+
var MAX_SIZE = 200 * 1024 * 1024;
|
|
27
|
+
async function isBrewTUIBarInstalled() {
|
|
28
|
+
try {
|
|
29
|
+
await access(BREWTUIBAR_APP_PATH);
|
|
30
|
+
return true;
|
|
31
|
+
} catch {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
async function bundleIdAt(appPath) {
|
|
36
|
+
if (process.platform !== "darwin") return null;
|
|
37
|
+
try {
|
|
38
|
+
const { stdout } = await execFileAsync("defaults", [
|
|
39
|
+
"read",
|
|
40
|
+
`${appPath}/Contents/Info.plist`,
|
|
41
|
+
"CFBundleIdentifier"
|
|
42
|
+
]);
|
|
43
|
+
return stdout.trim();
|
|
44
|
+
} catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
async function installedBundleId() {
|
|
49
|
+
return bundleIdAt(BREWTUIBAR_APP_PATH);
|
|
50
|
+
}
|
|
51
|
+
async function removeLegacyBundleIfOurs() {
|
|
52
|
+
if (process.platform !== "darwin") return;
|
|
53
|
+
try {
|
|
54
|
+
await access(LEGACY_APP_PATH);
|
|
55
|
+
} catch {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const legacyId = await bundleIdAt(LEGACY_APP_PATH);
|
|
59
|
+
if (legacyId !== LEGACY_BUNDLE_ID) return;
|
|
60
|
+
try {
|
|
61
|
+
const { stdout } = await execFileAsync("pgrep", ["-x", LEGACY_PROCESS_NAME]);
|
|
62
|
+
if (stdout.trim().length > 0) {
|
|
63
|
+
try {
|
|
64
|
+
await execFileAsync("osascript", ["-e", `tell application "${LEGACY_PROCESS_NAME}" to quit`]);
|
|
65
|
+
} catch {
|
|
66
|
+
}
|
|
67
|
+
for (let i = 0; i < 15; i++) {
|
|
68
|
+
try {
|
|
69
|
+
const { stdout: s } = await execFileAsync("pgrep", ["-x", LEGACY_PROCESS_NAME]);
|
|
70
|
+
if (s.trim().length === 0) break;
|
|
71
|
+
} catch {
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
75
|
+
}
|
|
76
|
+
try {
|
|
77
|
+
await execFileAsync("pkill", ["-x", LEGACY_PROCESS_NAME]);
|
|
78
|
+
} catch {
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
} catch {
|
|
82
|
+
}
|
|
83
|
+
await rm(LEGACY_APP_PATH, { recursive: true, force: true });
|
|
84
|
+
}
|
|
85
|
+
async function isBrewTUIBarRunning() {
|
|
86
|
+
if (process.platform !== "darwin") return false;
|
|
87
|
+
try {
|
|
88
|
+
const { stdout } = await execFileAsync("pgrep", ["-x", BREWTUIBAR_PROCESS_NAME]);
|
|
89
|
+
return stdout.trim().length > 0;
|
|
90
|
+
} catch {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
async function quitBrewTUIBar() {
|
|
95
|
+
if (process.platform !== "darwin") return;
|
|
96
|
+
try {
|
|
97
|
+
await execFileAsync("osascript", ["-e", `tell application "${BREWTUIBAR_PROCESS_NAME}" to quit`]);
|
|
98
|
+
} catch {
|
|
99
|
+
}
|
|
100
|
+
for (let i = 0; i < 15; i++) {
|
|
101
|
+
if (!await isBrewTUIBarRunning()) return;
|
|
102
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
103
|
+
}
|
|
104
|
+
try {
|
|
105
|
+
await execFileAsync("pkill", ["-x", BREWTUIBAR_PROCESS_NAME]);
|
|
106
|
+
} catch {
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
async function installBrewTUIBar(isPro, force = false) {
|
|
110
|
+
if (process.platform !== "darwin") {
|
|
111
|
+
throw new Error(t("cli_brewtuibarMacOnly"));
|
|
112
|
+
}
|
|
113
|
+
if (!isPro) {
|
|
114
|
+
throw new Error(t("cli_brewtuibarProRequired"));
|
|
115
|
+
}
|
|
116
|
+
if (await isBrewTUIBarInstalled()) {
|
|
117
|
+
const id = await installedBundleId();
|
|
118
|
+
if (id && id !== BREWTUIBAR_BUNDLE_ID) {
|
|
119
|
+
throw new Error(t("cli_brewtuibarForeignBundle", { id }));
|
|
120
|
+
}
|
|
121
|
+
if (!force) {
|
|
122
|
+
throw new Error(t("cli_brewtuibarAlreadyInstalled"));
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
const TMP_ZIP = join(tmpdir(), "Brew-TUI-Bar-" + randomUUID() + ".zip");
|
|
126
|
+
const res = await fetchWithTimeout(DOWNLOAD_URL, {}, 12e4);
|
|
127
|
+
if (!res.ok || !res.body) {
|
|
128
|
+
throw new Error(t("cli_brewtuibarDownloadFailed", { error: `HTTP ${res.status}` }));
|
|
129
|
+
}
|
|
130
|
+
const contentLength = Number(res.headers.get("content-length") ?? "0");
|
|
131
|
+
if (contentLength > MAX_SIZE) {
|
|
132
|
+
throw new Error(t("cli_brewtuibarDownloadFailed", { error: "Download exceeds 200 MB size limit" }));
|
|
133
|
+
}
|
|
134
|
+
let downloadedBytes = 0;
|
|
135
|
+
const fileStream = createWriteStream(TMP_ZIP);
|
|
136
|
+
const transformedBody = new ReadableStream({
|
|
137
|
+
async start(controller) {
|
|
138
|
+
const bodyReader = res.body.getReader();
|
|
139
|
+
try {
|
|
140
|
+
while (true) {
|
|
141
|
+
const { done, value } = await bodyReader.read();
|
|
142
|
+
if (done) break;
|
|
143
|
+
downloadedBytes += value.length;
|
|
144
|
+
if (downloadedBytes > MAX_SIZE) {
|
|
145
|
+
controller.error(new Error("Download exceeds 200 MB limit"));
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
controller.enqueue(value);
|
|
149
|
+
}
|
|
150
|
+
controller.close();
|
|
151
|
+
} catch (err) {
|
|
152
|
+
controller.error(err);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
await pipeline(transformedBody, fileStream);
|
|
157
|
+
let expectedHash = null;
|
|
158
|
+
try {
|
|
159
|
+
const checksumRes = await fetchWithTimeout(`${DOWNLOAD_URL}.sha256`, {}, 15e3);
|
|
160
|
+
if (checksumRes.ok) {
|
|
161
|
+
const text = await checksumRes.text();
|
|
162
|
+
const hash = text.trim().split(/\s+/)[0];
|
|
163
|
+
if (hash && /^[0-9a-f]{64}$/i.test(hash)) {
|
|
164
|
+
expectedHash = hash.toLowerCase();
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
} catch {
|
|
168
|
+
}
|
|
169
|
+
if (expectedHash) {
|
|
170
|
+
const fileBuffer = await readFile(TMP_ZIP);
|
|
171
|
+
const actual = createHash("sha256").update(fileBuffer).digest("hex");
|
|
172
|
+
if (actual !== expectedHash) {
|
|
173
|
+
await rm(TMP_ZIP, { force: true }).catch(() => {
|
|
174
|
+
});
|
|
175
|
+
throw new Error(t("cli_brewtuibarDownloadFailed", { error: "SHA-256 mismatch: binary may have been tampered with" }));
|
|
176
|
+
}
|
|
177
|
+
} else {
|
|
178
|
+
await rm(TMP_ZIP, { force: true }).catch(() => {
|
|
179
|
+
});
|
|
180
|
+
throw new Error(t("cli_brewtuibarDownloadFailed", { error: "SHA-256 checksum unavailable \u2014 cannot verify download integrity" }));
|
|
181
|
+
}
|
|
182
|
+
const wasRunning = await isBrewTUIBarRunning();
|
|
183
|
+
if (wasRunning) {
|
|
184
|
+
await quitBrewTUIBar();
|
|
185
|
+
}
|
|
186
|
+
await removeLegacyBundleIfOurs();
|
|
187
|
+
if (force && await isBrewTUIBarInstalled()) {
|
|
188
|
+
await rm(BREWTUIBAR_APP_PATH, { recursive: true, force: true });
|
|
189
|
+
}
|
|
190
|
+
try {
|
|
191
|
+
await execFileAsync("ditto", ["-xk", TMP_ZIP, "/Applications/"]);
|
|
192
|
+
} catch (err) {
|
|
193
|
+
throw new Error(t("cli_brewtuibarDownloadFailed", { error: err instanceof Error ? err.message : String(err) }), { cause: err });
|
|
194
|
+
} finally {
|
|
195
|
+
await rm(TMP_ZIP, { force: true }).catch(() => {
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
if (wasRunning) {
|
|
199
|
+
await launchBrewTUIBar();
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
async function launchBrewTUIBar() {
|
|
203
|
+
if (process.platform !== "darwin") return;
|
|
204
|
+
if (!await isBrewTUIBarInstalled()) return;
|
|
205
|
+
try {
|
|
206
|
+
await execFileAsync("open", ["-g", "-a", BREWTUIBAR_APP_PATH]);
|
|
207
|
+
} catch {
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
async function uninstallBrewTUIBar() {
|
|
211
|
+
if (!await isBrewTUIBarInstalled()) {
|
|
212
|
+
throw new Error(t("cli_brewtuibarNotInstalled"));
|
|
213
|
+
}
|
|
214
|
+
const id = await installedBundleId();
|
|
215
|
+
if (id && id !== BREWTUIBAR_BUNDLE_ID) {
|
|
216
|
+
throw new Error(t("cli_brewtuibarForeignBundle", { id }));
|
|
217
|
+
}
|
|
218
|
+
await rm(BREWTUIBAR_APP_PATH, { recursive: true, force: true });
|
|
219
|
+
}
|
|
220
|
+
export {
|
|
221
|
+
installBrewTUIBar,
|
|
222
|
+
isBrewTUIBarInstalled,
|
|
223
|
+
isBrewTUIBarRunning,
|
|
224
|
+
launchBrewTUIBar,
|
|
225
|
+
uninstallBrewTUIBar
|
|
226
|
+
};
|
|
227
|
+
//# sourceMappingURL=brew-tui-bar-installer-4JLEZSJA.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/brew-tui-bar-installer.ts"],"sourcesContent":["import { rm, access, readFile } from 'node:fs/promises';\nimport { createWriteStream } from 'node:fs';\nimport { createHash, randomUUID } from 'node:crypto';\nimport { tmpdir } from 'node:os';\nimport { join } from 'node:path';\nimport { pipeline } from 'node:stream/promises';\nimport { execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\nimport { t } from '../i18n/index.js';\nimport { fetchWithTimeout } from './fetch-timeout.js';\n\nconst execFileAsync = promisify(execFile);\nconst BREWTUIBAR_APP_PATH = '/Applications/Brew-TUI-Bar.app';\nconst BREWTUIBAR_BUNDLE_ID = 'com.molinesdesigns.brewtuibar';\nconst BREWTUIBAR_PROCESS_NAME = 'Brew-TUI-Bar';\nconst LEGACY_APP_PATH = '/Applications/BrewBar.app';\nconst LEGACY_BUNDLE_ID = 'com.molinesdesigns.brewbar';\nconst LEGACY_PROCESS_NAME = 'BrewBar';\nconst DOWNLOAD_URL = 'https://github.com/MoLinesDesigns/Brew-TUI/releases/latest/download/Brew-TUI-Bar.app.zip';\nconst MAX_SIZE = 200 * 1024 * 1024; // 200 MB\n\nexport async function isBrewTUIBarInstalled(): Promise<boolean> {\n try {\n await access(BREWTUIBAR_APP_PATH);\n return true;\n } catch {\n return false;\n }\n}\n\n/// Reads CFBundleIdentifier of an installed .app bundle. Used to detect when\n/// another app has claimed a path we care about (e.g. a third-party clone at\n/// /Applications/Brew-TUI-Bar.app, or a foreign app sitting at the legacy\n/// /Applications/BrewBar.app path).\nasync function bundleIdAt(appPath: string): Promise<string | null> {\n if (process.platform !== 'darwin') return null;\n try {\n const { stdout } = await execFileAsync('defaults', [\n 'read',\n `${appPath}/Contents/Info.plist`,\n 'CFBundleIdentifier',\n ]);\n return stdout.trim();\n } catch {\n return null;\n }\n}\n\nasync function installedBundleId(): Promise<string | null> {\n return bundleIdAt(BREWTUIBAR_APP_PATH);\n}\n\n/// Cleans up the legacy BrewBar.app bundle if present and owned by us. The\n/// cask transitional path handles this for `brew upgrade` users; this covers\n/// `brew-tui install-brew-tui-bar` and the npm cold-start auto-install. We\n/// only touch the bundle when its CFBundleIdentifier matches the legacy ID,\n/// so a foreign app at the same path is left alone.\nasync function removeLegacyBundleIfOurs(): Promise<void> {\n if (process.platform !== 'darwin') return;\n try {\n await access(LEGACY_APP_PATH);\n } catch {\n return;\n }\n\n const legacyId = await bundleIdAt(LEGACY_APP_PATH);\n if (legacyId !== LEGACY_BUNDLE_ID) return; // not ours, leave it\n\n // Quit the legacy process if it's running, then remove the bundle.\n try {\n const { stdout } = await execFileAsync('pgrep', ['-x', LEGACY_PROCESS_NAME]);\n if (stdout.trim().length > 0) {\n try {\n await execFileAsync('osascript', ['-e', `tell application \"${LEGACY_PROCESS_NAME}\" to quit`]);\n } catch { /* fall through to pkill */ }\n for (let i = 0; i < 15; i++) {\n try {\n const { stdout: s } = await execFileAsync('pgrep', ['-x', LEGACY_PROCESS_NAME]);\n if (s.trim().length === 0) break;\n } catch {\n break;\n }\n await new Promise((r) => setTimeout(r, 200));\n }\n try {\n await execFileAsync('pkill', ['-x', LEGACY_PROCESS_NAME]);\n } catch { /* nothing to kill */ }\n }\n } catch {\n /* pgrep exits 1 when no match — legacy app not running */\n }\n\n await rm(LEGACY_APP_PATH, { recursive: true, force: true });\n}\n\n/// Returns true if the Brew-TUI-Bar process is currently running.\n/// Used by the installer to decide whether to quit + relaunch after an update.\nexport async function isBrewTUIBarRunning(): Promise<boolean> {\n if (process.platform !== 'darwin') return false;\n try {\n const { stdout } = await execFileAsync('pgrep', ['-x', BREWTUIBAR_PROCESS_NAME]);\n return stdout.trim().length > 0;\n } catch {\n // pgrep exits 1 when no match; that means \"not running\", not a failure.\n return false;\n }\n}\n\n/// Asks Brew-TUI-Bar to quit gracefully (LSUIElement → no dialogs), then falls back\n/// to pkill if it hasn't exited within 3 s. Required before reemplazar el bundle:\n/// `ditto -xk` sobre una app en ejecución deja un bundle viejo con un Info.plist\n/// nuevo, lo cual confunde el monitor de last-action y los watchers FSEvents.\nasync function quitBrewTUIBar(): Promise<void> {\n if (process.platform !== 'darwin') return;\n try {\n await execFileAsync('osascript', ['-e', `tell application \"${BREWTUIBAR_PROCESS_NAME}\" to quit`]);\n } catch {\n /* osascript falla si la app no está registrada; pasamos a pkill */\n }\n for (let i = 0; i < 15; i++) {\n if (!(await isBrewTUIBarRunning())) return;\n await new Promise((r) => setTimeout(r, 200));\n }\n try {\n await execFileAsync('pkill', ['-x', BREWTUIBAR_PROCESS_NAME]);\n } catch {\n /* nada que matar */\n }\n}\n\nexport async function installBrewTUIBar(isPro: boolean, force = false): Promise<void> {\n // macOS only\n if (process.platform !== 'darwin') {\n throw new Error(t('cli_brewtuibarMacOnly'));\n }\n\n // Pro check\n if (!isPro) {\n throw new Error(t('cli_brewtuibarProRequired'));\n }\n\n // If an app already exists at our install path, verify it's ours before\n // we touch it. Defends against name collisions with third-party clones.\n if (await isBrewTUIBarInstalled()) {\n const id = await installedBundleId();\n if (id && id !== BREWTUIBAR_BUNDLE_ID) {\n throw new Error(t('cli_brewtuibarForeignBundle', { id }));\n }\n if (!force) {\n throw new Error(t('cli_brewtuibarAlreadyInstalled'));\n }\n }\n\n // EP-013: Use unique temp path\n const TMP_ZIP = join(tmpdir(), 'Brew-TUI-Bar-' + randomUUID() + '.zip');\n\n // Download zip (120s timeout for large binary)\n const res = await fetchWithTimeout(DOWNLOAD_URL, {}, 120_000);\n if (!res.ok || !res.body) {\n throw new Error(t('cli_brewtuibarDownloadFailed', { error: `HTTP ${res.status}` }));\n }\n\n // Reject downloads larger than 200 MB (from Content-Length header)\n const contentLength = Number(res.headers.get('content-length') ?? '0');\n if (contentLength > MAX_SIZE) {\n throw new Error(t('cli_brewtuibarDownloadFailed', { error: 'Download exceeds 200 MB size limit' }));\n }\n\n // EP-005: Track downloaded bytes during the stream\n let downloadedBytes = 0;\n\n // Write to tmp file with byte counting\n const fileStream = createWriteStream(TMP_ZIP);\n const transformedBody = new ReadableStream({\n async start(controller) {\n const bodyReader = (res.body as ReadableStream<Uint8Array>).getReader();\n try {\n while (true) {\n const { done, value } = await bodyReader.read();\n if (done) break;\n downloadedBytes += value.length;\n if (downloadedBytes > MAX_SIZE) {\n controller.error(new Error('Download exceeds 200 MB limit'));\n return;\n }\n controller.enqueue(value);\n }\n controller.close();\n } catch (err) {\n controller.error(err);\n }\n },\n });\n await pipeline(transformedBody as unknown as NodeJS.ReadableStream, fileStream);\n\n // SEG-001: SHA-256 integrity check with proper error handling\n let expectedHash: string | null = null;\n try {\n const checksumRes = await fetchWithTimeout(`${DOWNLOAD_URL}.sha256`, {}, 15_000);\n if (checksumRes.ok) {\n const text = await checksumRes.text();\n // EP-009: Validate split result is defined\n const hash = text.trim().split(/\\s+/)[0];\n // EP-010: Validate hash format\n if (hash && /^[0-9a-f]{64}$/i.test(hash)) {\n expectedHash = hash.toLowerCase();\n }\n }\n } catch {\n /* checksum file not available */\n }\n\n if (expectedHash) {\n const fileBuffer = await readFile(TMP_ZIP);\n const actual = createHash('sha256').update(fileBuffer).digest('hex');\n if (actual !== expectedHash) {\n await rm(TMP_ZIP, { force: true }).catch(() => {});\n throw new Error(t('cli_brewtuibarDownloadFailed', { error: 'SHA-256 mismatch: binary may have been tampered with' }));\n }\n } else {\n // NUEVO-003: Treat missing checksum as fatal — don't install unverified binaries\n await rm(TMP_ZIP, { force: true }).catch(() => {});\n throw new Error(t('cli_brewtuibarDownloadFailed', { error: 'SHA-256 checksum unavailable — cannot verify download integrity' }));\n }\n\n // Si Brew-TUI-Bar está corriendo, cerrarla antes de tocar el bundle. Sin esto\n // `ditto -xk` sobreescribe los recursos de un proceso vivo y la app queda\n // en estado degradado hasta el próximo lanzamiento.\n const wasRunning = await isBrewTUIBarRunning();\n if (wasRunning) {\n await quitBrewTUIBar();\n }\n\n // Clean up the legacy BrewBar.app bundle if it's ours. The cask transitional\n // path handles this on the brew upgrade side; this covers npm and cold-start.\n await removeLegacyBundleIfOurs();\n\n // Remove old app if force reinstall\n if (force && await isBrewTUIBarInstalled()) {\n await rm(BREWTUIBAR_APP_PATH, { recursive: true, force: true });\n }\n\n // Unzip to /Applications\n try {\n await execFileAsync('ditto', ['-xk', TMP_ZIP, '/Applications/']);\n } catch (err) {\n throw new Error(t('cli_brewtuibarDownloadFailed', { error: err instanceof Error ? err.message : String(err) }), { cause: err });\n } finally {\n // Clean up tmp zip\n await rm(TMP_ZIP, { force: true }).catch(() => {});\n }\n\n // Si estaba corriendo antes de la actualización, relanzarla para que el\n // usuario vuelva a ver el ícono en la menubar sin pasos manuales.\n if (wasRunning) {\n await launchBrewTUIBar();\n }\n}\n\n/// Launches Brew-TUI-Bar detached from the parent process so it survives terminal close.\n/// `open -g -a` runs the app in the background without bringing it to foreground.\nexport async function launchBrewTUIBar(): Promise<void> {\n if (process.platform !== 'darwin') return;\n if (!await isBrewTUIBarInstalled()) return;\n try {\n await execFileAsync('open', ['-g', '-a', BREWTUIBAR_APP_PATH]);\n } catch {\n // Non-fatal: may already be running, or LaunchServices may need a moment.\n }\n}\n\nexport async function uninstallBrewTUIBar(): Promise<void> {\n if (!await isBrewTUIBarInstalled()) {\n throw new Error(t('cli_brewtuibarNotInstalled'));\n }\n // Refuse to delete a foreign app that happens to live at the same path.\n const id = await installedBundleId();\n if (id && id !== BREWTUIBAR_BUNDLE_ID) {\n throw new Error(t('cli_brewtuibarForeignBundle', { id }));\n }\n\n await rm(BREWTUIBAR_APP_PATH, { recursive: true, force: true });\n}\n"],"mappings":"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","names":[]}
|
|
@@ -5,10 +5,10 @@ import {
|
|
|
5
5
|
loadBrewfile,
|
|
6
6
|
reconcile,
|
|
7
7
|
saveBrewfile
|
|
8
|
-
} from "./chunk-
|
|
9
|
-
import "./chunk-
|
|
10
|
-
import "./chunk-
|
|
11
|
-
import "./chunk-
|
|
8
|
+
} from "./chunk-43UJQAVI.js";
|
|
9
|
+
import "./chunk-DKOAK4RV.js";
|
|
10
|
+
import "./chunk-LFGDNAXH.js";
|
|
11
|
+
import "./chunk-7EMQAD6F.js";
|
|
12
12
|
import "./chunk-KDHEUNRI.js";
|
|
13
13
|
export {
|
|
14
14
|
BREWFILE_PATH,
|
|
@@ -18,4 +18,4 @@ export {
|
|
|
18
18
|
reconcile,
|
|
19
19
|
saveBrewfile
|
|
20
20
|
};
|
|
21
|
-
//# sourceMappingURL=brewfile-manager-
|
|
21
|
+
//# sourceMappingURL=brewfile-manager-PF25DV6U.js.map
|
|
@@ -4,14 +4,14 @@ import {
|
|
|
4
4
|
execBrew,
|
|
5
5
|
saveSnapshot,
|
|
6
6
|
streamBrew
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-DKOAK4RV.js";
|
|
8
8
|
import {
|
|
9
9
|
DATA_DIR,
|
|
10
10
|
ensureDataDirs
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-LFGDNAXH.js";
|
|
12
12
|
import {
|
|
13
13
|
t
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-7EMQAD6F.js";
|
|
15
15
|
import {
|
|
16
16
|
logger
|
|
17
17
|
} from "./chunk-KDHEUNRI.js";
|
|
@@ -870,4 +870,4 @@ export {
|
|
|
870
870
|
computeDrift,
|
|
871
871
|
reconcile
|
|
872
872
|
};
|
|
873
|
-
//# sourceMappingURL=chunk-
|
|
873
|
+
//# sourceMappingURL=chunk-43UJQAVI.js.map
|
|
@@ -297,7 +297,7 @@ var en = {
|
|
|
297
297
|
account_expiresLabel: "Expires:",
|
|
298
298
|
account_activatedLabel: "Activated:",
|
|
299
299
|
account_upgradeTitle: "Upgrade to Brew-TUI Pro",
|
|
300
|
-
account_unlockDesc: "Unlock Profiles, Smart Cleanup, History, Security Audit, and
|
|
300
|
+
account_unlockDesc: "Unlock Profiles, Smart Cleanup, History, Security Audit, and Brew-TUI-Bar (macOS menu bar companion).",
|
|
301
301
|
account_pricing: "9.95\u20AC/month or 82\u20AC/year (save 31%)",
|
|
302
302
|
account_runActivate: "Run:",
|
|
303
303
|
account_activateCmd: "brew-tui activate <key>",
|
|
@@ -331,7 +331,7 @@ var en = {
|
|
|
331
331
|
upgrade_buyUrlTeam: "https://buy.polar.sh/polar_cl_CO6xqSzKgFiQJwXnhZYGqisOP04Wspi0KKZSn38NjFZ?quantity=3",
|
|
332
332
|
upgrade_activateWith: "Then activate with:",
|
|
333
333
|
upgrade_activateCmd: "brew-tui activate <your-license-key>",
|
|
334
|
-
upgrade_proLabel: "Brew-TUI Pro \u2014 9.95\u20AC/month or 82\u20AC/year \u2014 Includes
|
|
334
|
+
upgrade_proLabel: "Brew-TUI Pro \u2014 9.95\u20AC/month or 82\u20AC/year \u2014 Includes Brew-TUI-Bar for macOS",
|
|
335
335
|
upgrade_teamLabel: "Brew-TUI Team \u2014 8\u20AC/seat/month \u2014 Includes everything in Pro plus Compliance",
|
|
336
336
|
// ── Progress Log ──
|
|
337
337
|
progress_noOutput: "No output yet",
|
|
@@ -364,18 +364,19 @@ var en = {
|
|
|
364
364
|
cli_revalidateFailed: "\u2718 License revalidation failed. Renew your subscription or activate a valid key.",
|
|
365
365
|
cli_rateLimited: "Too many activation attempts. Try again in {{minutes}} minutes.",
|
|
366
366
|
cli_cooldown: "Please wait before trying again.",
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
367
|
+
cli_brewtuibarInstalling: "Downloading Brew-TUI-Bar...",
|
|
368
|
+
cli_brewtuibarInstalled: "\u2714 Brew-TUI-Bar installed to /Applications/Brew-TUI-Bar.app",
|
|
369
|
+
cli_brewtuibarAlreadyInstalled: "Brew-TUI-Bar is already installed. Use --force to reinstall.",
|
|
370
|
+
cli_brewtuibarUninstalled: "\u2714 Brew-TUI-Bar removed from /Applications.",
|
|
371
|
+
cli_brewtuibarNotInstalled: "Brew-TUI-Bar is not installed.",
|
|
372
|
+
cli_brewtuibarProRequired: "\u2718 Brew-TUI-Bar requires a Pro license.\n Run: brew-tui activate <key>",
|
|
373
|
+
cli_brewtuibarMacOnly: "\u2718 Brew-TUI-Bar is only available on macOS.",
|
|
374
|
+
cli_brewtuibarDownloadFailed: "\u2718 Failed to download Brew-TUI-Bar: {{error}}",
|
|
375
|
+
cli_brewtuibarAutoFailed: "\u26A0 Brew-TUI-Bar auto-launch failed: {{error}}",
|
|
376
|
+
cli_brewtuibarUpdating: "Updating Brew-TUI-Bar from {{installed}} to {{expected}} to match Brew-TUI...",
|
|
377
|
+
cli_brewtuibarVersionMismatch: "\u26A0 Brew-TUI-Bar {{installed}} is out of sync with Brew-TUI {{expected}}. Run: brew-tui install-brew-tui-bar --force",
|
|
378
|
+
cli_brewtuibarLegacyAlias: "\u26A0 `{{legacy}}` is deprecated and will be removed in 2.1.0. Use `brew-tui {{current}}` instead.",
|
|
379
|
+
cli_brewtuibarForeignBundle: "\u2718 /Applications/Brew-TUI-Bar.app exists but its bundle ID is `{{id}}`, not com.molinesdesigns.brewtuibar. Refusing to touch a foreign app. Remove or rename it first.",
|
|
379
380
|
cli_deactivateRemoteFailed: "\u26A0 Warning: Could not reach the server to deactivate remotely. The license was removed locally but may still count as active.",
|
|
380
381
|
// ── License degradation (Layer 15) ──
|
|
381
382
|
license_offlineWarning: "Your license has not been validated for {{days}} days. Please connect to the internet.",
|
|
@@ -818,7 +819,7 @@ var es = {
|
|
|
818
819
|
account_expiresLabel: "Expira:",
|
|
819
820
|
account_activatedLabel: "Activado:",
|
|
820
821
|
account_upgradeTitle: "Actualiza a Brew-TUI Pro",
|
|
821
|
-
account_unlockDesc: "Desbloquea Perfiles, Limpieza Inteligente, Historial, Auditor\xEDa de Seguridad y
|
|
822
|
+
account_unlockDesc: "Desbloquea Perfiles, Limpieza Inteligente, Historial, Auditor\xEDa de Seguridad y Brew-TUI-Bar (barra de men\xFA macOS).",
|
|
822
823
|
account_pricing: "9,95\u20AC/mes o 82\u20AC/a\xF1o (ahorra 31%)",
|
|
823
824
|
account_runActivate: "Ejecuta:",
|
|
824
825
|
account_activateCmd: "brew-tui activate <clave>",
|
|
@@ -852,7 +853,7 @@ var es = {
|
|
|
852
853
|
upgrade_buyUrlTeam: "https://buy.polar.sh/polar_cl_CO6xqSzKgFiQJwXnhZYGqisOP04Wspi0KKZSn38NjFZ?quantity=3",
|
|
853
854
|
upgrade_activateWith: "Luego activa con:",
|
|
854
855
|
upgrade_activateCmd: "brew-tui activate <tu-clave-de-licencia>",
|
|
855
|
-
upgrade_proLabel: "Brew-TUI Pro \u2014 9,95\u20AC/mes o 82\u20AC/a\xF1o \u2014 Incluye
|
|
856
|
+
upgrade_proLabel: "Brew-TUI Pro \u2014 9,95\u20AC/mes o 82\u20AC/a\xF1o \u2014 Incluye Brew-TUI-Bar para macOS",
|
|
856
857
|
upgrade_teamLabel: "Brew-TUI Team \u2014 8\u20AC/seat/mes \u2014 Incluye todo Pro m\xE1s Compliance",
|
|
857
858
|
// ── Progress Log ──
|
|
858
859
|
progress_noOutput: "Sin salida a\xFAn",
|
|
@@ -885,18 +886,19 @@ var es = {
|
|
|
885
886
|
cli_revalidateFailed: "\u2718 La revalidaci\xF3n de la licencia fall\xF3. Renueva tu suscripci\xF3n o activa una clave v\xE1lida.",
|
|
886
887
|
cli_rateLimited: "Demasiados intentos de activaci\xF3n. Int\xE9ntalo en {{minutes}} minutos.",
|
|
887
888
|
cli_cooldown: "Por favor espera antes de intentar de nuevo.",
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
889
|
+
cli_brewtuibarInstalling: "Descargando Brew-TUI-Bar...",
|
|
890
|
+
cli_brewtuibarInstalled: "\u2714 Brew-TUI-Bar instalado en /Applications/Brew-TUI-Bar.app",
|
|
891
|
+
cli_brewtuibarAlreadyInstalled: "Brew-TUI-Bar ya est\xE1 instalado. Usa --force para reinstalar.",
|
|
892
|
+
cli_brewtuibarUninstalled: "\u2714 Brew-TUI-Bar eliminado de /Applications.",
|
|
893
|
+
cli_brewtuibarNotInstalled: "Brew-TUI-Bar no est\xE1 instalado.",
|
|
894
|
+
cli_brewtuibarProRequired: "\u2718 Brew-TUI-Bar requiere una licencia Pro.\n Ejecuta: brew-tui activate <clave>",
|
|
895
|
+
cli_brewtuibarMacOnly: "\u2718 Brew-TUI-Bar solo est\xE1 disponible en macOS.",
|
|
896
|
+
cli_brewtuibarDownloadFailed: "\u2718 Error al descargar Brew-TUI-Bar: {{error}}",
|
|
897
|
+
cli_brewtuibarAutoFailed: "\u26A0 No se pudo lanzar Brew-TUI-Bar autom\xE1ticamente: {{error}}",
|
|
898
|
+
cli_brewtuibarUpdating: "Actualizando Brew-TUI-Bar de {{installed}} a {{expected}} para igualar Brew-TUI...",
|
|
899
|
+
cli_brewtuibarVersionMismatch: "\u26A0 Brew-TUI-Bar {{installed}} no coincide con Brew-TUI {{expected}}. Ejecuta: brew-tui install-brew-tui-bar --force",
|
|
900
|
+
cli_brewtuibarLegacyAlias: "\u26A0 `{{legacy}}` est\xE1 obsoleto y se eliminar\xE1 en 2.1.0. Usa `brew-tui {{current}}` en su lugar.",
|
|
901
|
+
cli_brewtuibarForeignBundle: "\u2718 /Applications/Brew-TUI-Bar.app existe pero su bundle ID es `{{id}}`, no com.molinesdesigns.brewtuibar. No se tocar\xE1 una app ajena. Elim\xEDnala o ren\xF3mbrala primero.",
|
|
900
902
|
cli_deactivateRemoteFailed: "\u26A0 Advertencia: No se pudo contactar al servidor para desactivar remotamente. La licencia se elimin\xF3 localmente pero puede seguir contando como activa.",
|
|
901
903
|
// ── License degradation (Layer 15) ──
|
|
902
904
|
license_offlineWarning: "Tu licencia no se ha validado en {{days}} d\xEDas. Por favor con\xE9ctate a internet.",
|
|
@@ -1091,4 +1093,4 @@ export {
|
|
|
1091
1093
|
t,
|
|
1092
1094
|
tp
|
|
1093
1095
|
};
|
|
1094
|
-
//# sourceMappingURL=chunk-
|
|
1096
|
+
//# sourceMappingURL=chunk-7EMQAD6F.js.map
|