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.
@@ -3,8 +3,9 @@ import {
3
3
  isBrewTUIBarInstalled,
4
4
  isBrewTUIBarRunning,
5
5
  launchBrewTUIBar,
6
+ syncAndLaunchBrewTUIBar,
6
7
  uninstallBrewTUIBar
7
- } from "./chunk-NTJNEZ52.js";
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-W5ERTVV4.js.map
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-NTJNEZ52.js.map
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.0" })
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.0" : "unknown";
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.0" : "unknown";
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.0\n");
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-W5ERTVV4.js");
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-W5ERTVV4.js");
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 { isBrewTUIBarInstalled, installBrewTUIBar, launchBrewTUIBar } = await import("./brew-tui-bar-installer-W5ERTVV4.js");
6390
- const { checkBrewTUIBarVersion } = await import("./version-check-B2GS6HLU.js");
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));