brew-tui 0.9.1 → 0.9.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.
@@ -6,7 +6,7 @@ var execFileAsync = promisify(execFile);
6
6
  var BREWBAR_INFO_PLIST = "/Applications/BrewBar.app/Contents/Info.plist";
7
7
  var CONTRACT_VERSION = 1;
8
8
  function expectedVersion() {
9
- return "0.9.1";
9
+ return "0.9.2";
10
10
  }
11
11
  async function readBrewBarVersion() {
12
12
  try {
@@ -61,4 +61,4 @@ export {
61
61
  expectedVersion,
62
62
  readBrewBarVersion
63
63
  };
64
- //# sourceMappingURL=version-check-X3HTR3HM.js.map
64
+ //# sourceMappingURL=version-check-LHQYDFDA.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brew-tui",
3
- "version": "0.9.1",
3
+ "version": "0.9.2",
4
4
  "description": "Brew-TUI — Visual TUI for Homebrew package management",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/lib/brewbar-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 BREWBAR_APP_PATH = '/Applications/BrewBar.app';\nconst DOWNLOAD_URL = 'https://github.com/MoLinesDesigns/Brew-TUI/releases/latest/download/BrewBar.app.zip';\nconst MAX_SIZE = 200 * 1024 * 1024; // 200 MB\n\nexport async function isBrewBarInstalled(): Promise<boolean> {\n try {\n await access(BREWBAR_APP_PATH);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function installBrewBar(isPro: boolean, force = false): Promise<void> {\n // macOS only\n if (process.platform !== 'darwin') {\n throw new Error(t('cli_brewbarMacOnly'));\n }\n\n // Pro check\n if (!isPro) {\n throw new Error(t('cli_brewbarProRequired'));\n }\n\n // Already installed check\n if (!force && await isBrewBarInstalled()) {\n throw new Error(t('cli_brewbarAlreadyInstalled'));\n }\n\n console.log(t('cli_brewbarInstalling'));\n\n // EP-013: Use unique temp path\n const TMP_ZIP = join(tmpdir(), 'BrewBar-' + 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_brewbarDownloadFailed', { 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_brewbarDownloadFailed', { 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_brewbarDownloadFailed', { 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_brewbarDownloadFailed', { error: 'SHA-256 checksum unavailable — cannot verify download integrity' }));\n }\n\n // Remove old app if force reinstall\n if (force && await isBrewBarInstalled()) {\n await rm(BREWBAR_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_brewbarDownloadFailed', { 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\n/// Launches BrewBar 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 launchBrewBar(): Promise<void> {\n if (process.platform !== 'darwin') return;\n if (!await isBrewBarInstalled()) return;\n try {\n await execFileAsync('open', ['-g', '-a', BREWBAR_APP_PATH]);\n } catch {\n // Non-fatal: BrewBar may already be running, or LaunchServices may need a moment.\n }\n}\n\nexport async function uninstallBrewBar(): Promise<void> {\n if (!await isBrewBarInstalled()) {\n throw new Error(t('cli_brewbarNotInstalled'));\n }\n\n await rm(BREWBAR_APP_PATH, { recursive: true, force: true });\n}\n"],"mappings":"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","names":[]}