brew-tui 2.2.0 → 2.2.2
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/{brew-tui-bar-installer-W5ERTVV4.js → brew-tui-bar-installer-GTV4OEZW.js} +4 -2
- package/build/{chunk-NTJNEZ52.js → chunk-YUE5NRTE.js} +23 -1
- package/build/{chunk-NTJNEZ52.js.map → chunk-YUE5NRTE.js.map} +1 -1
- package/build/index.js +8 -25
- package/build/index.js.map +1 -1
- package/build/postinstall.js +3 -10
- package/build/postinstall.js.map +1 -1
- package/build/{version-check-B2GS6HLU.js → version-check-QY3SQ6XI.js} +2 -2
- package/package.json +1 -1
- /package/build/{brew-tui-bar-installer-W5ERTVV4.js.map → brew-tui-bar-installer-GTV4OEZW.js.map} +0 -0
- /package/build/{version-check-B2GS6HLU.js.map → version-check-QY3SQ6XI.js.map} +0 -0
|
@@ -3,8 +3,9 @@ import {
|
|
|
3
3
|
isBrewTUIBarInstalled,
|
|
4
4
|
isBrewTUIBarRunning,
|
|
5
5
|
launchBrewTUIBar,
|
|
6
|
+
syncAndLaunchBrewTUIBar,
|
|
6
7
|
uninstallBrewTUIBar
|
|
7
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-YUE5NRTE.js";
|
|
8
9
|
import "./chunk-NRRQECXA.js";
|
|
9
10
|
import "./chunk-F2S7TGCS.js";
|
|
10
11
|
import "./chunk-KDHEUNRI.js";
|
|
@@ -13,6 +14,7 @@ export {
|
|
|
13
14
|
isBrewTUIBarInstalled,
|
|
14
15
|
isBrewTUIBarRunning,
|
|
15
16
|
launchBrewTUIBar,
|
|
17
|
+
syncAndLaunchBrewTUIBar,
|
|
16
18
|
uninstallBrewTUIBar
|
|
17
19
|
};
|
|
18
|
-
//# sourceMappingURL=brew-tui-bar-installer-
|
|
20
|
+
//# sourceMappingURL=brew-tui-bar-installer-GTV4OEZW.js.map
|
|
@@ -203,6 +203,27 @@ async function launchBrewTUIBar() {
|
|
|
203
203
|
} catch {
|
|
204
204
|
}
|
|
205
205
|
}
|
|
206
|
+
async function syncAndLaunchBrewTUIBar() {
|
|
207
|
+
if (process.platform !== "darwin") return;
|
|
208
|
+
const { checkBrewTUIBarVersion } = await import("./version-check-QY3SQ6XI.js");
|
|
209
|
+
try {
|
|
210
|
+
if (!await isBrewTUIBarInstalled()) {
|
|
211
|
+
console.log(t("cli_brewtuibarInstalling"));
|
|
212
|
+
await installBrewTUIBar(false, false);
|
|
213
|
+
console.log(t("cli_brewtuibarInstalled"));
|
|
214
|
+
} else {
|
|
215
|
+
const status = await checkBrewTUIBarVersion();
|
|
216
|
+
if (status.kind === "outdated") {
|
|
217
|
+
console.log(t("cli_brewtuibarUpdating", { installed: status.installed, expected: status.expected }));
|
|
218
|
+
await installBrewTUIBar(false, true);
|
|
219
|
+
console.log(t("cli_brewtuibarInstalled"));
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
await launchBrewTUIBar();
|
|
223
|
+
} catch (err) {
|
|
224
|
+
console.warn(t("cli_brewtuibarAutoFailed", { error: err instanceof Error ? err.message : String(err) }));
|
|
225
|
+
}
|
|
226
|
+
}
|
|
206
227
|
async function uninstallBrewTUIBar() {
|
|
207
228
|
if (!await isBrewTUIBarInstalled()) {
|
|
208
229
|
throw new Error(t("cli_brewtuibarNotInstalled"));
|
|
@@ -219,6 +240,7 @@ export {
|
|
|
219
240
|
isBrewTUIBarRunning,
|
|
220
241
|
installBrewTUIBar,
|
|
221
242
|
launchBrewTUIBar,
|
|
243
|
+
syncAndLaunchBrewTUIBar,
|
|
222
244
|
uninstallBrewTUIBar
|
|
223
245
|
};
|
|
224
|
-
//# sourceMappingURL=chunk-
|
|
246
|
+
//# sourceMappingURL=chunk-YUE5NRTE.js.map
|
|
@@ -1 +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\n/// Install Brew-TUI-Bar. As of 2.1.0 we no longer gate on Pro: Free users get\n/// the bundle too and see the in-app upgrade prompt when they click the menu\n/// bar icon. The `_isPro` parameter is kept for backwards compatibility with\n/// existing call sites but is ignored.\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 // 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":[]}
|
|
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\n/// Install Brew-TUI-Bar. As of 2.1.0 we no longer gate on Pro: Free users get\n/// the bundle too and see the in-app upgrade prompt when they click the menu\n/// bar icon. The `_isPro` parameter is kept for backwards compatibility with\n/// existing call sites but is ignored.\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 // 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\n/// One-shot \"install if missing, update if outdated, launch\" flow shared by\n/// the CLI cold-start (`ensureBrewTUIBarRunning`) and the npm postinstall.\n/// All errors are swallowed and logged as warnings — callers should never\n/// have their install/launch fail just because the menu bar app is unhappy.\nexport async function syncAndLaunchBrewTUIBar(): Promise<void> {\n if (process.platform !== 'darwin') return;\n\n const { checkBrewTUIBarVersion } = await import('./version-check.js');\n\n try {\n if (!(await isBrewTUIBarInstalled())) {\n console.log(t('cli_brewtuibarInstalling'));\n await installBrewTUIBar(false, false);\n console.log(t('cli_brewtuibarInstalled'));\n } else {\n // Reinstall in place when the installed bundle is older than the CLI.\n // Same contract enforced by `checkBrewTUIBarVersion`, so the menubar\n // app and CLI always agree on the license/IPC schema.\n const status = await checkBrewTUIBarVersion();\n if (status.kind === 'outdated') {\n console.log(t('cli_brewtuibarUpdating', { installed: status.installed, expected: status.expected }));\n await installBrewTUIBar(false, true);\n console.log(t('cli_brewtuibarInstalled'));\n }\n }\n await launchBrewTUIBar();\n } catch (err) {\n console.warn(t('cli_brewtuibarAutoFailed', { error: err instanceof Error ? err.message : String(err) }));\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":[]}
|
package/build/index.js
CHANGED
|
@@ -4668,7 +4668,7 @@ function AccountView() {
|
|
|
4668
4668
|
status === "pro" || status === "team" || status === "expired" ? `v ${t("hint_revalidate")} ` : "",
|
|
4669
4669
|
revalidating ? t("account_revalidating") : "",
|
|
4670
4670
|
" ",
|
|
4671
|
-
t("app_version", { version: "2.2.
|
|
4671
|
+
t("app_version", { version: "2.2.2" })
|
|
4672
4672
|
] }) })
|
|
4673
4673
|
] });
|
|
4674
4674
|
}
|
|
@@ -6176,7 +6176,7 @@ async function reportError(err, context = {}) {
|
|
|
6176
6176
|
const config = await resolveConfig();
|
|
6177
6177
|
if (!config.enabled || !config.endpoint) return;
|
|
6178
6178
|
const machineId = await getMachineId();
|
|
6179
|
-
const version = true ? "2.2.
|
|
6179
|
+
const version = true ? "2.2.2" : "unknown";
|
|
6180
6180
|
await postReport(buildReport("error", err, context, machineId, version), config);
|
|
6181
6181
|
}
|
|
6182
6182
|
async function installCrashReporter() {
|
|
@@ -6185,7 +6185,7 @@ async function installCrashReporter() {
|
|
|
6185
6185
|
if (!config.enabled || !config.endpoint) return;
|
|
6186
6186
|
_installed = true;
|
|
6187
6187
|
const machineId = await getMachineId();
|
|
6188
|
-
const version = true ? "2.2.
|
|
6188
|
+
const version = true ? "2.2.2" : "unknown";
|
|
6189
6189
|
process.on("uncaughtException", (err) => {
|
|
6190
6190
|
void postReport(buildReport("fatal", err, { kind: "uncaughtException" }, machineId, version), config);
|
|
6191
6191
|
});
|
|
@@ -6200,7 +6200,7 @@ import { jsx as jsx39 } from "react/jsx-runtime";
|
|
|
6200
6200
|
var [, , command, arg] = process.argv;
|
|
6201
6201
|
async function runCli() {
|
|
6202
6202
|
if (command === "--version" || command === "-v" || command === "version") {
|
|
6203
|
-
process.stdout.write("2.2.
|
|
6203
|
+
process.stdout.write("2.2.2\n");
|
|
6204
6204
|
return;
|
|
6205
6205
|
}
|
|
6206
6206
|
await ensureDataDirs();
|
|
@@ -6341,7 +6341,7 @@ Snapshots: ${snapshots.length} (latest: ${latest ? formatDate(latest.capturedAt)
|
|
|
6341
6341
|
if (command === "install-brewbar") {
|
|
6342
6342
|
console.warn(t("cli_brewtuibarLegacyAlias", { legacy: command, current: "install-brew-tui-bar" }));
|
|
6343
6343
|
}
|
|
6344
|
-
const { installBrewTUIBar } = await import("./brew-tui-bar-installer-
|
|
6344
|
+
const { installBrewTUIBar } = await import("./brew-tui-bar-installer-GTV4OEZW.js");
|
|
6345
6345
|
try {
|
|
6346
6346
|
console.log(t("cli_brewtuibarInstalling"));
|
|
6347
6347
|
await installBrewTUIBar(false, arg === "--force");
|
|
@@ -6356,7 +6356,7 @@ Snapshots: ${snapshots.length} (latest: ${latest ? formatDate(latest.capturedAt)
|
|
|
6356
6356
|
if (command === "uninstall-brewbar") {
|
|
6357
6357
|
console.warn(t("cli_brewtuibarLegacyAlias", { legacy: command, current: "uninstall-brew-tui-bar" }));
|
|
6358
6358
|
}
|
|
6359
|
-
const { uninstallBrewTUIBar } = await import("./brew-tui-bar-installer-
|
|
6359
|
+
const { uninstallBrewTUIBar } = await import("./brew-tui-bar-installer-GTV4OEZW.js");
|
|
6360
6360
|
try {
|
|
6361
6361
|
await uninstallBrewTUIBar();
|
|
6362
6362
|
console.log(t("cli_brewtuibarUninstalled"));
|
|
@@ -6386,25 +6386,8 @@ Snapshots: ${snapshots.length} (latest: ${latest ? formatDate(latest.capturedAt)
|
|
|
6386
6386
|
async function ensureBrewTUIBarRunning() {
|
|
6387
6387
|
if (process.platform !== "darwin") return;
|
|
6388
6388
|
await useLicenseStore.getState().initialize();
|
|
6389
|
-
const {
|
|
6390
|
-
|
|
6391
|
-
try {
|
|
6392
|
-
if (!await isBrewTUIBarInstalled()) {
|
|
6393
|
-
console.log(t("cli_brewtuibarInstalling"));
|
|
6394
|
-
await installBrewTUIBar(false, false);
|
|
6395
|
-
console.log(t("cli_brewtuibarInstalled"));
|
|
6396
|
-
} else {
|
|
6397
|
-
const status = await checkBrewTUIBarVersion();
|
|
6398
|
-
if (status.kind === "outdated") {
|
|
6399
|
-
console.log(t("cli_brewtuibarUpdating", { installed: status.installed, expected: status.expected }));
|
|
6400
|
-
await installBrewTUIBar(false, true);
|
|
6401
|
-
console.log(t("cli_brewtuibarInstalled"));
|
|
6402
|
-
}
|
|
6403
|
-
}
|
|
6404
|
-
await launchBrewTUIBar();
|
|
6405
|
-
} catch (err) {
|
|
6406
|
-
console.warn(t("cli_brewtuibarAutoFailed", { error: err instanceof Error ? err.message : String(err) }));
|
|
6407
|
-
}
|
|
6389
|
+
const { syncAndLaunchBrewTUIBar } = await import("./brew-tui-bar-installer-GTV4OEZW.js");
|
|
6390
|
+
await syncAndLaunchBrewTUIBar();
|
|
6408
6391
|
}
|
|
6409
6392
|
runCli().catch((err) => {
|
|
6410
6393
|
console.error(err instanceof Error ? err.message : String(err));
|