brew-tui 0.1.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/LICENSE +21 -0
- package/README.md +105 -0
- package/bin/brew-tui.js +2 -0
- package/build/brewbar-installer-CPCOE3MI.js +64 -0
- package/build/brewbar-installer-CPCOE3MI.js.map +1 -0
- package/build/chunk-3BK3B53S.js +55 -0
- package/build/chunk-3BK3B53S.js.map +1 -0
- package/build/chunk-P6PTN4HR.js +1140 -0
- package/build/chunk-P6PTN4HR.js.map +1 -0
- package/build/history-logger-LQT622M2.js +12 -0
- package/build/history-logger-LQT622M2.js.map +1 -0
- package/build/index.js +3338 -0
- package/build/index.js.map +1 -0
- package/package.json +66 -0
|
@@ -0,0 +1,1140 @@
|
|
|
1
|
+
// src/i18n/index.ts
|
|
2
|
+
import { create } from "zustand";
|
|
3
|
+
|
|
4
|
+
// src/i18n/en.ts
|
|
5
|
+
var en = {
|
|
6
|
+
// ── App chrome ──
|
|
7
|
+
app_title: "Brew-TUI",
|
|
8
|
+
pro_badge: "PRO",
|
|
9
|
+
app_version: "Brew-TUI v{{version}}",
|
|
10
|
+
// ── View labels (header tab bar) ──
|
|
11
|
+
view_dashboard: "Dashboard",
|
|
12
|
+
view_installed: "Installed",
|
|
13
|
+
view_search: "Search",
|
|
14
|
+
view_outdated: "Outdated",
|
|
15
|
+
view_packageInfo: "Pkg Info",
|
|
16
|
+
view_services: "Services",
|
|
17
|
+
view_doctor: "Doctor",
|
|
18
|
+
view_profiles: "Profiles",
|
|
19
|
+
view_smartCleanup: "Cleanup",
|
|
20
|
+
view_history: "History",
|
|
21
|
+
view_securityAudit: "Security",
|
|
22
|
+
view_account: "Account",
|
|
23
|
+
// ── Keyboard hint actions ──
|
|
24
|
+
hint_navigate: "navigate",
|
|
25
|
+
hint_next: "next",
|
|
26
|
+
hint_quit: "quit",
|
|
27
|
+
hint_filter: "filter",
|
|
28
|
+
hint_info: "info",
|
|
29
|
+
hint_toggle: "toggle",
|
|
30
|
+
hint_typeToSearch: "type to search",
|
|
31
|
+
hint_install: "install",
|
|
32
|
+
hint_uninstall: "uninstall",
|
|
33
|
+
hint_upgrade: "upgrade",
|
|
34
|
+
hint_upgradeAll: "upgrade all",
|
|
35
|
+
hint_back: "back",
|
|
36
|
+
hint_start: "start",
|
|
37
|
+
hint_stop: "stop",
|
|
38
|
+
hint_restart: "restart",
|
|
39
|
+
hint_refresh: "refresh",
|
|
40
|
+
hint_new: "new",
|
|
41
|
+
hint_details: "details",
|
|
42
|
+
hint_import: "import",
|
|
43
|
+
hint_delete: "delete",
|
|
44
|
+
hint_clean: "clean",
|
|
45
|
+
hint_all: "all",
|
|
46
|
+
hint_search: "search",
|
|
47
|
+
hint_clear: "clear",
|
|
48
|
+
hint_scan: "scan",
|
|
49
|
+
hint_expand: "expand",
|
|
50
|
+
hint_cancel: "cancel",
|
|
51
|
+
hint_force: "force uninstall (ignore deps)",
|
|
52
|
+
hint_rescan: "rescan",
|
|
53
|
+
hint_deactivate: "deactivate",
|
|
54
|
+
hint_importProfile: "import this profile",
|
|
55
|
+
hint_lang: "lang",
|
|
56
|
+
hint_replay: "replay",
|
|
57
|
+
hint_edit: "edit",
|
|
58
|
+
hint_pin: "pin/unpin",
|
|
59
|
+
// ── Loading / progress ──
|
|
60
|
+
loading_default: "Loading...",
|
|
61
|
+
loading_fetchingBrew: "Fetching Homebrew data...",
|
|
62
|
+
loading_installed: "Loading installed packages...",
|
|
63
|
+
loading_outdated: "Checking for outdated packages...",
|
|
64
|
+
loading_services: "Loading services...",
|
|
65
|
+
loading_doctor: "Running brew doctor... (this may take a moment)",
|
|
66
|
+
loading_profiles: "Loading profiles...",
|
|
67
|
+
loading_cleanup: "Analyzing packages... (checking disk usage)",
|
|
68
|
+
loading_history: "Loading history...",
|
|
69
|
+
loading_security: "Scanning packages against OSV vulnerability database...",
|
|
70
|
+
loading_searching: "Searching...",
|
|
71
|
+
loading_package: "Loading {{name}}...",
|
|
72
|
+
// ── Confirm dialog ──
|
|
73
|
+
confirm_yes: "[Y]es",
|
|
74
|
+
confirm_no: "[N]o",
|
|
75
|
+
// ── Error ──
|
|
76
|
+
error_prefix: "Error: ",
|
|
77
|
+
// ── Common ──
|
|
78
|
+
common_andMore: "...and {{count}} more",
|
|
79
|
+
common_exit: "(exit {{code}})",
|
|
80
|
+
common_yes: "yes",
|
|
81
|
+
common_no: "no",
|
|
82
|
+
// ── Relative time ──
|
|
83
|
+
time_justNow: "just now",
|
|
84
|
+
time_minutesAgo: "{{n}}m ago",
|
|
85
|
+
time_hoursAgo: "{{n}}h ago",
|
|
86
|
+
time_daysAgo: "{{n}}d ago",
|
|
87
|
+
time_monthsAgo: "{{n}}mo ago",
|
|
88
|
+
// ── Badges ──
|
|
89
|
+
badge_outdated: "outdated",
|
|
90
|
+
badge_pinned: "pinned",
|
|
91
|
+
badge_kegOnly: "keg-only",
|
|
92
|
+
badge_dep: "dep",
|
|
93
|
+
badge_installed: "installed",
|
|
94
|
+
badge_deprecated: "deprecated",
|
|
95
|
+
badge_ok: "ok",
|
|
96
|
+
badge_fail: "fail",
|
|
97
|
+
badge_error: "error",
|
|
98
|
+
// ── Dashboard ──
|
|
99
|
+
dashboard_overview: "Overview",
|
|
100
|
+
dashboard_formulae: "Formulae",
|
|
101
|
+
dashboard_casks: "Casks",
|
|
102
|
+
dashboard_outdated: "Outdated",
|
|
103
|
+
dashboard_services: "Services",
|
|
104
|
+
dashboard_systemInfo: "System Info",
|
|
105
|
+
dashboard_homebrew: "Homebrew:",
|
|
106
|
+
dashboard_prefix: "Prefix:",
|
|
107
|
+
dashboard_updated: "Updated:",
|
|
108
|
+
dashboard_outdatedPackages: "Outdated Packages",
|
|
109
|
+
dashboard_serviceErrors: "Service Errors",
|
|
110
|
+
// ── Installed ──
|
|
111
|
+
installed_formulaeCount: "Formulae ({{count}})",
|
|
112
|
+
installed_casksCount: "Casks ({{count}})",
|
|
113
|
+
installed_filterDisplay: 'Filter: "{{query}}" ({{count}} matches)',
|
|
114
|
+
installed_noPackages: "No packages found",
|
|
115
|
+
installed_confirmUninstall: "Uninstall {{name}}?",
|
|
116
|
+
// ── Search ──
|
|
117
|
+
search_placeholder: "Search Homebrew packages... (enter to search)",
|
|
118
|
+
search_resultsFor: "Results for",
|
|
119
|
+
search_escToClear: "(esc to clear)",
|
|
120
|
+
search_installing: "Installing package...",
|
|
121
|
+
search_installComplete: "Installation complete!",
|
|
122
|
+
search_confirmInstall: "Install {{name}}?",
|
|
123
|
+
search_formulaeHeader: "=== Formulae ({{count}})",
|
|
124
|
+
search_casksHeader: "=== Casks ({{count}})",
|
|
125
|
+
search_noResults: "No results found",
|
|
126
|
+
// ── Outdated ──
|
|
127
|
+
outdated_title: "Outdated Packages ({{count}})",
|
|
128
|
+
outdated_upgrading: "Upgrading...",
|
|
129
|
+
outdated_upgradeComplete: "Upgrade complete!",
|
|
130
|
+
outdated_pressRefresh: "(press r to refresh)",
|
|
131
|
+
outdated_upToDate: "Everything is up to date!",
|
|
132
|
+
outdated_confirmAll: "Upgrade all {{count}} packages?",
|
|
133
|
+
outdated_confirmSingle: "Upgrade {{name}}?",
|
|
134
|
+
outdated_pinned: "[pinned]",
|
|
135
|
+
// ── Package Info ──
|
|
136
|
+
pkgInfo_noPackage: "No package selected. Go to Installed and press Enter on a package.",
|
|
137
|
+
pkgInfo_notFound: "Package not found",
|
|
138
|
+
pkgInfo_installing: "Installing {{name}}...",
|
|
139
|
+
pkgInfo_uninstalling: "Uninstalling {{name}}...",
|
|
140
|
+
pkgInfo_upgrading: "Upgrading {{name}}...",
|
|
141
|
+
pkgInfo_done: "Done!",
|
|
142
|
+
pkgInfo_confirmInstall: "install {{name}}?",
|
|
143
|
+
pkgInfo_confirmUninstall: "uninstall {{name}}?",
|
|
144
|
+
pkgInfo_confirmUpgrade: "upgrade {{name}}?",
|
|
145
|
+
pkgInfo_details: "Details",
|
|
146
|
+
pkgInfo_homepage: "Homepage:",
|
|
147
|
+
pkgInfo_license: "License:",
|
|
148
|
+
pkgInfo_tap: "Tap:",
|
|
149
|
+
pkgInfo_stable: "Stable:",
|
|
150
|
+
pkgInfo_installed: "Installed:",
|
|
151
|
+
pkgInfo_bottle: "Bottle:",
|
|
152
|
+
pkgInfo_onRequest: "On request:",
|
|
153
|
+
pkgInfo_noDependency: "no (dependency)",
|
|
154
|
+
pkgInfo_dependencies: "Dependencies ({{count}})",
|
|
155
|
+
pkgInfo_caveats: "Caveats",
|
|
156
|
+
// ── Services ──
|
|
157
|
+
services_title: "Homebrew Services",
|
|
158
|
+
services_titleCount: "Homebrew Services ({{count}})",
|
|
159
|
+
services_noServices: "No services found",
|
|
160
|
+
services_name: "Name",
|
|
161
|
+
services_status: "Status",
|
|
162
|
+
services_user: "User",
|
|
163
|
+
services_processing: "Processing...",
|
|
164
|
+
services_confirmStop: "Stop service {{name}}?",
|
|
165
|
+
services_confirmRestart: "Restart service {{name}}?",
|
|
166
|
+
// ── Doctor ──
|
|
167
|
+
doctor_title: "Homebrew Doctor",
|
|
168
|
+
doctor_clean: "Your system is ready to brew.",
|
|
169
|
+
doctor_warningsNotCaptured: "Doctor finished with warnings but none were captured.",
|
|
170
|
+
// ── Profiles ──
|
|
171
|
+
profiles_title: "Package Profiles ({{count}})",
|
|
172
|
+
profiles_importTitle: "Importing profile...",
|
|
173
|
+
profiles_importComplete: "Import complete. Press any key.",
|
|
174
|
+
profiles_createName: "Create Profile \u2014 Name:",
|
|
175
|
+
profiles_namePlaceholder: "e.g. work, personal, project-x",
|
|
176
|
+
profiles_createDesc: 'Create Profile "{{name}}" \u2014 Description:',
|
|
177
|
+
profiles_descPlaceholder: "Brief description of this setup",
|
|
178
|
+
profiles_created: "Created: {{date}}",
|
|
179
|
+
profiles_formulaeCount: "Formulae ({{count}})",
|
|
180
|
+
profiles_casksCount: "Casks ({{count}})",
|
|
181
|
+
profiles_confirmDelete: 'Delete profile "{{name}}"?',
|
|
182
|
+
profiles_noProfiles: "No profiles saved yet.",
|
|
183
|
+
profiles_press: "Press",
|
|
184
|
+
profiles_exportHint: "to export your current setup as a profile.",
|
|
185
|
+
profiles_editName: "Edit Profile \u2014 Name:",
|
|
186
|
+
profiles_editDesc: 'Edit Profile "{{name}}" \u2014 Description:',
|
|
187
|
+
// ── Smart Cleanup ──
|
|
188
|
+
cleanup_title: "Smart Cleanup",
|
|
189
|
+
cleanup_cleaning: "Cleaning up...",
|
|
190
|
+
cleanup_complete: "Cleanup complete! Press r to re-analyze.",
|
|
191
|
+
cleanup_orphans: "Orphans",
|
|
192
|
+
cleanup_reclaimable: "Reclaimable",
|
|
193
|
+
cleanup_selected: "Selected",
|
|
194
|
+
cleanup_confirmUninstall: "Uninstall {{count}} packages?",
|
|
195
|
+
cleanup_confirmForce: "Some packages have dependencies. Force uninstall {{count}} packages? (ignores dependencies)",
|
|
196
|
+
cleanup_depError: "Some packages could not be removed due to dependencies.",
|
|
197
|
+
cleanup_systemClean: "No orphaned packages found. Your system is clean!",
|
|
198
|
+
// ── History ──
|
|
199
|
+
history_title: "Action History ({{count}})",
|
|
200
|
+
history_filterLabel: "filter: {{filter}}",
|
|
201
|
+
history_searchPlaceholder: "Search packages...",
|
|
202
|
+
history_confirmClear: "Clear all {{count}} history entries?",
|
|
203
|
+
history_noEntries: "No history entries",
|
|
204
|
+
history_noEntriesFor: 'No history entries for "{{filter}}"',
|
|
205
|
+
history_all: "(all)",
|
|
206
|
+
history_actionInstall: "install",
|
|
207
|
+
history_actionUninstall: "uninstall",
|
|
208
|
+
history_actionUpgrade: "upgrade",
|
|
209
|
+
history_actionUpgradeAll: "upgrade-all",
|
|
210
|
+
history_confirmReplay: "Re-run: {{action}} {{name}}?",
|
|
211
|
+
history_replayAll: "Re-run: upgrade all packages?",
|
|
212
|
+
// ── Security Audit ──
|
|
213
|
+
security_title: "Security Audit",
|
|
214
|
+
security_scanned: "Scanned",
|
|
215
|
+
security_vulnerable: "Vulnerable",
|
|
216
|
+
security_critical: "Critical",
|
|
217
|
+
security_high: "High",
|
|
218
|
+
security_medium: "Medium",
|
|
219
|
+
security_noVulns: "No known vulnerabilities found in your installed packages!",
|
|
220
|
+
security_fixedIn: "Fixed in: {{version}}",
|
|
221
|
+
security_confirmUpgrade: "Upgrade {{name}} to fix vulnerabilities?",
|
|
222
|
+
// ── Account ──
|
|
223
|
+
account_title: "Account & License",
|
|
224
|
+
account_confirmDeactivate: "Deactivate your Pro license on this machine?",
|
|
225
|
+
account_statusLabel: "Status:",
|
|
226
|
+
account_pro: "[Pro]",
|
|
227
|
+
account_free: "[Free]",
|
|
228
|
+
account_expired: "[Expired]",
|
|
229
|
+
account_validating: "[Validating...]",
|
|
230
|
+
account_emailLabel: "Email:",
|
|
231
|
+
account_nameLabel: "Name:",
|
|
232
|
+
account_planLabel: "Plan:",
|
|
233
|
+
account_monthlyPrice: "9\u20AC/month",
|
|
234
|
+
account_yearlyPrice: "49\u20AC/year",
|
|
235
|
+
account_keyLabel: "Key:",
|
|
236
|
+
account_expiresLabel: "Expires:",
|
|
237
|
+
account_activatedLabel: "Activated:",
|
|
238
|
+
account_upgradeTitle: "Upgrade to Brew-TUI Pro",
|
|
239
|
+
account_unlockDesc: "Unlock Profiles, Smart Cleanup, History, Security Audit, and BrewBar (macOS menu bar companion).",
|
|
240
|
+
account_pricing: "9\u20AC/month or 49\u20AC/year",
|
|
241
|
+
account_runActivate: "Run:",
|
|
242
|
+
account_activateCmd: "brew-tui activate <key>",
|
|
243
|
+
account_licenseExpired: "Your license has expired. Renew to continue using Pro features.",
|
|
244
|
+
account_deactivating: "Deactivating...",
|
|
245
|
+
// ── Upgrade Prompt ──
|
|
246
|
+
upgrade_proFeature: "{{title}} \u2014 Pro Feature",
|
|
247
|
+
upgrade_profiles: "Package Profiles",
|
|
248
|
+
upgrade_profilesDesc: "Export and import your Homebrew setup across machines. Save named profiles for work, personal, or project-specific configurations.",
|
|
249
|
+
upgrade_cleanup: "Smart Cleanup",
|
|
250
|
+
upgrade_cleanupDesc: "Find orphaned packages, analyze disk usage per package, and reclaim disk space with one-click intelligent cleanup.",
|
|
251
|
+
upgrade_history: "Action History",
|
|
252
|
+
upgrade_historyDesc: "Track every install, uninstall, and upgrade with timestamps. Search and filter your package management history.",
|
|
253
|
+
upgrade_security: "Security Audit",
|
|
254
|
+
upgrade_securityDesc: "Scan installed packages against known vulnerabilities (CVEs). See severity levels, affected versions, and available fixes.",
|
|
255
|
+
upgrade_pricing: "9\u20AC/month or 49\u20AC/year",
|
|
256
|
+
upgrade_activateWith: "Activate with:",
|
|
257
|
+
upgrade_activateCmd: "brew-tui activate <your-license-key>",
|
|
258
|
+
upgrade_proLabel: "Brew-TUI Pro \u2014 9\u20AC/month or 49\u20AC/year \u2014 Includes BrewBar for macOS",
|
|
259
|
+
// ── Progress Log ──
|
|
260
|
+
progress_noOutput: "No output yet",
|
|
261
|
+
// ── Search Input ──
|
|
262
|
+
searchInput_placeholder: "Type to filter...",
|
|
263
|
+
// ── Profile Manager ──
|
|
264
|
+
profileMgr_tapping: "Tapping {{name}}...",
|
|
265
|
+
profileMgr_installing: "Installing {{name}}...",
|
|
266
|
+
profileMgr_installingCask: "Installing cask {{name}}...",
|
|
267
|
+
profileMgr_importDone: "Done! Installed {{count}} packages.",
|
|
268
|
+
// ── CLI ──
|
|
269
|
+
cli_usageActivate: "Usage: brew-tui activate <license-key>",
|
|
270
|
+
cli_activated: "\u2714 Pro activated for {{email}}",
|
|
271
|
+
cli_plan: " Plan: {{plan}}",
|
|
272
|
+
cli_expires: " Expires: {{date}}",
|
|
273
|
+
cli_activationFailed: "\u2718 Activation failed: {{error}}",
|
|
274
|
+
cli_noLicense: "No active license found.",
|
|
275
|
+
cli_deactivated: "\u2714 License deactivated.",
|
|
276
|
+
cli_planFree: "Plan: Free",
|
|
277
|
+
cli_planPro: "Plan: Pro",
|
|
278
|
+
cli_confirmDeactivate: "Deactivate your Pro license on this machine? (y/N): ",
|
|
279
|
+
cli_deactivateCancelled: "Deactivation cancelled.",
|
|
280
|
+
cli_upgradeHint: "Run `brew-tui activate <key>` to upgrade to Pro.",
|
|
281
|
+
cli_email: "Email: {{email}}",
|
|
282
|
+
cli_status: "Status: {{status}}",
|
|
283
|
+
cli_rateLimited: "Too many activation attempts. Try again in {{minutes}} minutes.",
|
|
284
|
+
cli_cooldown: "Please wait before trying again.",
|
|
285
|
+
cli_brewbarInstalling: "Downloading BrewBar...",
|
|
286
|
+
cli_brewbarInstalled: "\u2714 BrewBar installed to /Applications/BrewBar.app",
|
|
287
|
+
cli_brewbarAlreadyInstalled: "BrewBar is already installed. Use --force to reinstall.",
|
|
288
|
+
cli_brewbarUninstalled: "\u2714 BrewBar removed from /Applications.",
|
|
289
|
+
cli_brewbarNotInstalled: "BrewBar is not installed.",
|
|
290
|
+
cli_brewbarProRequired: "\u2718 BrewBar requires a Pro license.\n Run: brew-tui activate <key>",
|
|
291
|
+
cli_brewbarMacOnly: "\u2718 BrewBar is only available on macOS.",
|
|
292
|
+
cli_brewbarDownloadFailed: "\u2718 Failed to download BrewBar: {{error}}",
|
|
293
|
+
// ── License degradation (Layer 15) ──
|
|
294
|
+
license_offlineWarning: "Your license has not been validated for {{days}} days. Please connect to the internet.",
|
|
295
|
+
// ── Plurals ──
|
|
296
|
+
plural_vulns_one: "({{count}} vuln)",
|
|
297
|
+
plural_vulns_other: "({{count}} vulns)",
|
|
298
|
+
plural_warnings_one: "{{count}} warning",
|
|
299
|
+
plural_warnings_other: "{{count}} warnings",
|
|
300
|
+
// ── Scroll indicators ──
|
|
301
|
+
scroll_moreAbove: "\u2191 {{count}} more",
|
|
302
|
+
scroll_moreBelow: "\u2193 {{count}} more"
|
|
303
|
+
};
|
|
304
|
+
var en_default = en;
|
|
305
|
+
|
|
306
|
+
// src/i18n/es.ts
|
|
307
|
+
var es = {
|
|
308
|
+
// ── App chrome ──
|
|
309
|
+
app_title: "Brew-TUI",
|
|
310
|
+
pro_badge: "PRO",
|
|
311
|
+
app_version: "Brew-TUI v{{version}}",
|
|
312
|
+
// ── View labels (header tab bar) ──
|
|
313
|
+
view_dashboard: "Inicio",
|
|
314
|
+
view_installed: "Instalados",
|
|
315
|
+
view_search: "Buscar",
|
|
316
|
+
view_outdated: "Desactual.",
|
|
317
|
+
view_packageInfo: "Info Paq.",
|
|
318
|
+
view_services: "Servicios",
|
|
319
|
+
view_doctor: "Doctor",
|
|
320
|
+
view_profiles: "Perfiles",
|
|
321
|
+
view_smartCleanup: "Limpieza",
|
|
322
|
+
view_history: "Historial",
|
|
323
|
+
view_securityAudit: "Seguridad",
|
|
324
|
+
view_account: "Cuenta",
|
|
325
|
+
// ── Keyboard hint actions ──
|
|
326
|
+
hint_navigate: "navegar",
|
|
327
|
+
hint_next: "siguiente",
|
|
328
|
+
hint_quit: "salir",
|
|
329
|
+
hint_filter: "filtrar",
|
|
330
|
+
hint_info: "info",
|
|
331
|
+
hint_toggle: "cambiar",
|
|
332
|
+
hint_typeToSearch: "escriba para buscar",
|
|
333
|
+
hint_install: "instalar",
|
|
334
|
+
hint_uninstall: "desinstalar",
|
|
335
|
+
hint_upgrade: "actualizar",
|
|
336
|
+
hint_upgradeAll: "actualizar todo",
|
|
337
|
+
hint_back: "volver",
|
|
338
|
+
hint_start: "iniciar",
|
|
339
|
+
hint_stop: "detener",
|
|
340
|
+
hint_restart: "reiniciar",
|
|
341
|
+
hint_refresh: "refrescar",
|
|
342
|
+
hint_new: "nuevo",
|
|
343
|
+
hint_details: "detalles",
|
|
344
|
+
hint_import: "importar",
|
|
345
|
+
hint_delete: "eliminar",
|
|
346
|
+
hint_clean: "limpiar",
|
|
347
|
+
hint_all: "todo",
|
|
348
|
+
hint_search: "buscar",
|
|
349
|
+
hint_clear: "borrar",
|
|
350
|
+
hint_scan: "escanear",
|
|
351
|
+
hint_expand: "expandir",
|
|
352
|
+
hint_cancel: "cancelar",
|
|
353
|
+
hint_force: "forzar (ignorar deps)",
|
|
354
|
+
hint_rescan: "re-escanear",
|
|
355
|
+
hint_deactivate: "desactivar",
|
|
356
|
+
hint_importProfile: "importar este perfil",
|
|
357
|
+
hint_lang: "idioma",
|
|
358
|
+
hint_replay: "repetir",
|
|
359
|
+
hint_edit: "editar",
|
|
360
|
+
hint_pin: "fijar/desfijar",
|
|
361
|
+
// ── Loading / progress ──
|
|
362
|
+
loading_default: "Cargando...",
|
|
363
|
+
loading_fetchingBrew: "Obteniendo datos de Homebrew...",
|
|
364
|
+
loading_installed: "Cargando paquetes instalados...",
|
|
365
|
+
loading_outdated: "Buscando paquetes desactualizados...",
|
|
366
|
+
loading_services: "Cargando servicios...",
|
|
367
|
+
loading_doctor: "Ejecutando brew doctor... (puede tardar un momento)",
|
|
368
|
+
loading_profiles: "Cargando perfiles...",
|
|
369
|
+
loading_cleanup: "Analizando paquetes... (verificando uso de disco)",
|
|
370
|
+
loading_history: "Cargando historial...",
|
|
371
|
+
loading_security: "Escaneando paquetes en la base de datos de vulnerabilidades OSV...",
|
|
372
|
+
loading_searching: "Buscando...",
|
|
373
|
+
loading_package: "Cargando {{name}}...",
|
|
374
|
+
// ── Confirm dialog ──
|
|
375
|
+
confirm_yes: "[S]\xED",
|
|
376
|
+
confirm_no: "[N]o",
|
|
377
|
+
// ── Error ──
|
|
378
|
+
error_prefix: "Error: ",
|
|
379
|
+
// ── Common ──
|
|
380
|
+
common_andMore: "...y {{count}} m\xE1s",
|
|
381
|
+
common_exit: "(salida {{code}})",
|
|
382
|
+
common_yes: "s\xED",
|
|
383
|
+
common_no: "no",
|
|
384
|
+
// ── Relative time ──
|
|
385
|
+
time_justNow: "ahora",
|
|
386
|
+
time_minutesAgo: "hace {{n}}m",
|
|
387
|
+
time_hoursAgo: "hace {{n}}h",
|
|
388
|
+
time_daysAgo: "hace {{n}}d",
|
|
389
|
+
time_monthsAgo: "hace {{n}}me",
|
|
390
|
+
// ── Badges ──
|
|
391
|
+
badge_outdated: "desactualizado",
|
|
392
|
+
badge_pinned: "fijado",
|
|
393
|
+
badge_kegOnly: "keg-only",
|
|
394
|
+
badge_dep: "dep",
|
|
395
|
+
badge_installed: "instalado",
|
|
396
|
+
badge_deprecated: "obsoleto",
|
|
397
|
+
badge_ok: "ok",
|
|
398
|
+
badge_fail: "fallo",
|
|
399
|
+
badge_error: "error",
|
|
400
|
+
// ── Dashboard ──
|
|
401
|
+
dashboard_overview: "Resumen",
|
|
402
|
+
dashboard_formulae: "Formulae",
|
|
403
|
+
dashboard_casks: "Casks",
|
|
404
|
+
dashboard_outdated: "Desactualizados",
|
|
405
|
+
dashboard_services: "Servicios",
|
|
406
|
+
dashboard_systemInfo: "Info del Sistema",
|
|
407
|
+
dashboard_homebrew: "Homebrew:",
|
|
408
|
+
dashboard_prefix: "Prefijo:",
|
|
409
|
+
dashboard_updated: "Actualizado:",
|
|
410
|
+
dashboard_outdatedPackages: "Paquetes Desactualizados",
|
|
411
|
+
dashboard_serviceErrors: "Errores de Servicios",
|
|
412
|
+
// ── Installed ──
|
|
413
|
+
installed_formulaeCount: "Formulae ({{count}})",
|
|
414
|
+
installed_casksCount: "Casks ({{count}})",
|
|
415
|
+
installed_filterDisplay: 'Filtro: "{{query}}" ({{count}} coincidencias)',
|
|
416
|
+
installed_noPackages: "No se encontraron paquetes",
|
|
417
|
+
installed_confirmUninstall: "\xBFDesinstalar {{name}}?",
|
|
418
|
+
// ── Search ──
|
|
419
|
+
search_placeholder: "Buscar paquetes Homebrew... (enter para buscar)",
|
|
420
|
+
search_resultsFor: "Resultados para",
|
|
421
|
+
search_escToClear: "(esc para limpiar)",
|
|
422
|
+
search_installing: "Instalando paquete...",
|
|
423
|
+
search_installComplete: "\xA1Instalaci\xF3n completa!",
|
|
424
|
+
search_confirmInstall: "\xBFInstalar {{name}}?",
|
|
425
|
+
search_formulaeHeader: "=== Formulae ({{count}})",
|
|
426
|
+
search_casksHeader: "=== Casks ({{count}})",
|
|
427
|
+
search_noResults: "Sin resultados",
|
|
428
|
+
// ── Outdated ──
|
|
429
|
+
outdated_title: "Paquetes Desactualizados ({{count}})",
|
|
430
|
+
outdated_upgrading: "Actualizando...",
|
|
431
|
+
outdated_upgradeComplete: "\xA1Actualizaci\xF3n completa!",
|
|
432
|
+
outdated_pressRefresh: "(presiona r para refrescar)",
|
|
433
|
+
outdated_upToDate: "\xA1Todo est\xE1 al d\xEDa!",
|
|
434
|
+
outdated_confirmAll: "\xBFActualizar los {{count}} paquetes?",
|
|
435
|
+
outdated_confirmSingle: "\xBFActualizar {{name}}?",
|
|
436
|
+
outdated_pinned: "[fijado]",
|
|
437
|
+
// ── Package Info ──
|
|
438
|
+
pkgInfo_noPackage: "Ning\xFAn paquete seleccionado. Ve a Instalados y presiona Enter en un paquete.",
|
|
439
|
+
pkgInfo_notFound: "Paquete no encontrado",
|
|
440
|
+
pkgInfo_installing: "Instalando {{name}}...",
|
|
441
|
+
pkgInfo_uninstalling: "Desinstalando {{name}}...",
|
|
442
|
+
pkgInfo_upgrading: "Actualizando {{name}}...",
|
|
443
|
+
pkgInfo_done: "\xA1Listo!",
|
|
444
|
+
pkgInfo_confirmInstall: "\xBFinstalar {{name}}?",
|
|
445
|
+
pkgInfo_confirmUninstall: "\xBFdesinstalar {{name}}?",
|
|
446
|
+
pkgInfo_confirmUpgrade: "\xBFactualizar {{name}}?",
|
|
447
|
+
pkgInfo_details: "Detalles",
|
|
448
|
+
pkgInfo_homepage: "Web:",
|
|
449
|
+
pkgInfo_license: "Licencia:",
|
|
450
|
+
pkgInfo_tap: "Tap:",
|
|
451
|
+
pkgInfo_stable: "Estable:",
|
|
452
|
+
pkgInfo_installed: "Instalado:",
|
|
453
|
+
pkgInfo_bottle: "Bottle:",
|
|
454
|
+
pkgInfo_onRequest: "Por solicitud:",
|
|
455
|
+
pkgInfo_noDependency: "no (dependencia)",
|
|
456
|
+
pkgInfo_dependencies: "Dependencias ({{count}})",
|
|
457
|
+
pkgInfo_caveats: "Advertencias",
|
|
458
|
+
// ── Services ──
|
|
459
|
+
services_title: "Servicios Homebrew",
|
|
460
|
+
services_titleCount: "Servicios Homebrew ({{count}})",
|
|
461
|
+
services_noServices: "No se encontraron servicios",
|
|
462
|
+
services_name: "Nombre",
|
|
463
|
+
services_status: "Estado",
|
|
464
|
+
services_user: "Usuario",
|
|
465
|
+
services_processing: "Procesando...",
|
|
466
|
+
services_confirmStop: "\xBFDetener servicio {{name}}?",
|
|
467
|
+
services_confirmRestart: "\xBFReiniciar servicio {{name}}?",
|
|
468
|
+
// ── Doctor ──
|
|
469
|
+
doctor_title: "Homebrew Doctor",
|
|
470
|
+
doctor_clean: "Tu sistema est\xE1 listo para brew.",
|
|
471
|
+
doctor_warningsNotCaptured: "Doctor termin\xF3 con advertencias pero no se capturaron.",
|
|
472
|
+
// ── Profiles ──
|
|
473
|
+
profiles_title: "Perfiles de Paquetes ({{count}})",
|
|
474
|
+
profiles_importTitle: "Importando perfil...",
|
|
475
|
+
profiles_importComplete: "Importaci\xF3n completa. Presiona cualquier tecla.",
|
|
476
|
+
profiles_createName: "Crear Perfil \u2014 Nombre:",
|
|
477
|
+
profiles_namePlaceholder: "ej. trabajo, personal, proyecto-x",
|
|
478
|
+
profiles_createDesc: 'Crear Perfil "{{name}}" \u2014 Descripci\xF3n:',
|
|
479
|
+
profiles_descPlaceholder: "Breve descripci\xF3n de esta configuraci\xF3n",
|
|
480
|
+
profiles_created: "Creado: {{date}}",
|
|
481
|
+
profiles_formulaeCount: "Formulae ({{count}})",
|
|
482
|
+
profiles_casksCount: "Casks ({{count}})",
|
|
483
|
+
profiles_confirmDelete: '\xBFEliminar perfil "{{name}}"?',
|
|
484
|
+
profiles_noProfiles: "A\xFAn no hay perfiles guardados.",
|
|
485
|
+
profiles_press: "Presiona",
|
|
486
|
+
profiles_exportHint: "para exportar tu configuraci\xF3n actual como perfil.",
|
|
487
|
+
profiles_editName: "Editar Perfil \u2014 Nombre:",
|
|
488
|
+
profiles_editDesc: 'Editar Perfil "{{name}}" \u2014 Descripci\xF3n:',
|
|
489
|
+
// ── Smart Cleanup ──
|
|
490
|
+
cleanup_title: "Limpieza Inteligente",
|
|
491
|
+
cleanup_cleaning: "Limpiando...",
|
|
492
|
+
cleanup_complete: "\xA1Limpieza completa! Presiona r para re-analizar.",
|
|
493
|
+
cleanup_orphans: "Hu\xE9rfanos",
|
|
494
|
+
cleanup_reclaimable: "Recuperable",
|
|
495
|
+
cleanup_selected: "Seleccionados",
|
|
496
|
+
cleanup_confirmUninstall: "\xBFDesinstalar {{count}} paquetes?",
|
|
497
|
+
cleanup_confirmForce: "Algunos paquetes tienen dependencias. \xBFForzar desinstalaci\xF3n de {{count}} paquetes? (ignora dependencias)",
|
|
498
|
+
cleanup_depError: "Algunos paquetes no pudieron eliminarse por dependencias.",
|
|
499
|
+
cleanup_systemClean: "\xA1No se encontraron paquetes hu\xE9rfanos. Tu sistema est\xE1 limpio!",
|
|
500
|
+
// ── History ──
|
|
501
|
+
history_title: "Historial de Acciones ({{count}})",
|
|
502
|
+
history_filterLabel: "filtro: {{filter}}",
|
|
503
|
+
history_searchPlaceholder: "Buscar paquetes...",
|
|
504
|
+
history_confirmClear: "\xBFBorrar las {{count}} entradas del historial?",
|
|
505
|
+
history_noEntries: "Sin entradas en el historial",
|
|
506
|
+
history_noEntriesFor: 'Sin entradas en el historial para "{{filter}}"',
|
|
507
|
+
history_all: "(todos)",
|
|
508
|
+
history_actionInstall: "instalar",
|
|
509
|
+
history_actionUninstall: "desinstalar",
|
|
510
|
+
history_actionUpgrade: "actualizar",
|
|
511
|
+
history_actionUpgradeAll: "actualizar-todo",
|
|
512
|
+
history_confirmReplay: "\xBFRe-ejecutar: {{action}} {{name}}?",
|
|
513
|
+
history_replayAll: "\xBFRe-ejecutar: actualizar todos los paquetes?",
|
|
514
|
+
// ── Security Audit ──
|
|
515
|
+
security_title: "Auditor\xEDa de Seguridad",
|
|
516
|
+
security_scanned: "Escaneados",
|
|
517
|
+
security_vulnerable: "Vulnerables",
|
|
518
|
+
security_critical: "Cr\xEDticos",
|
|
519
|
+
security_high: "Altos",
|
|
520
|
+
security_medium: "Medios",
|
|
521
|
+
security_noVulns: "\xA1No se encontraron vulnerabilidades conocidas en tus paquetes instalados!",
|
|
522
|
+
security_fixedIn: "Corregido en: {{version}}",
|
|
523
|
+
security_confirmUpgrade: "\xBFActualizar {{name}} para corregir vulnerabilidades?",
|
|
524
|
+
// ── Account ──
|
|
525
|
+
account_title: "Cuenta y Licencia",
|
|
526
|
+
account_confirmDeactivate: "\xBFDesactivar tu licencia Pro en esta m\xE1quina?",
|
|
527
|
+
account_statusLabel: "Estado:",
|
|
528
|
+
account_pro: "[Pro]",
|
|
529
|
+
account_free: "[Gratis]",
|
|
530
|
+
account_expired: "[Expirada]",
|
|
531
|
+
account_validating: "[Validando...]",
|
|
532
|
+
account_emailLabel: "Email:",
|
|
533
|
+
account_nameLabel: "Nombre:",
|
|
534
|
+
account_planLabel: "Plan:",
|
|
535
|
+
account_monthlyPrice: "9\u20AC/mes",
|
|
536
|
+
account_yearlyPrice: "49\u20AC/a\xF1o",
|
|
537
|
+
account_keyLabel: "Clave:",
|
|
538
|
+
account_expiresLabel: "Expira:",
|
|
539
|
+
account_activatedLabel: "Activado:",
|
|
540
|
+
account_upgradeTitle: "Actualiza a Brew-TUI Pro",
|
|
541
|
+
account_unlockDesc: "Desbloquea Perfiles, Limpieza Inteligente, Historial, Auditor\xEDa de Seguridad y BrewBar (barra de men\xFA macOS).",
|
|
542
|
+
account_pricing: "9\u20AC/mes o 49\u20AC/a\xF1o",
|
|
543
|
+
account_runActivate: "Ejecuta:",
|
|
544
|
+
account_activateCmd: "brew-tui activate <clave>",
|
|
545
|
+
account_licenseExpired: "Tu licencia ha expirado. Renueva para seguir usando las funciones Pro.",
|
|
546
|
+
account_deactivating: "Desactivando...",
|
|
547
|
+
// ── Upgrade Prompt ──
|
|
548
|
+
upgrade_proFeature: "{{title}} \u2014 Funci\xF3n Pro",
|
|
549
|
+
upgrade_profiles: "Perfiles de Paquetes",
|
|
550
|
+
upgrade_profilesDesc: "Exporta e importa tu configuraci\xF3n de Homebrew entre m\xE1quinas. Guarda perfiles con nombre para trabajo, personal o proyectos espec\xEDficos.",
|
|
551
|
+
upgrade_cleanup: "Limpieza Inteligente",
|
|
552
|
+
upgrade_cleanupDesc: "Encuentra paquetes hu\xE9rfanos, analiza uso de disco por paquete y recupera espacio con limpieza inteligente de un clic.",
|
|
553
|
+
upgrade_history: "Historial de Acciones",
|
|
554
|
+
upgrade_historyDesc: "Rastrea cada instalaci\xF3n, desinstalaci\xF3n y actualizaci\xF3n con marcas de tiempo. Busca y filtra tu historial de gesti\xF3n de paquetes.",
|
|
555
|
+
upgrade_security: "Auditor\xEDa de Seguridad",
|
|
556
|
+
upgrade_securityDesc: "Escanea paquetes instalados contra vulnerabilidades conocidas (CVEs). Ve niveles de severidad, versiones afectadas y correcciones disponibles.",
|
|
557
|
+
upgrade_pricing: "9\u20AC/mes o 49\u20AC/a\xF1o",
|
|
558
|
+
upgrade_activateWith: "Activa con:",
|
|
559
|
+
upgrade_activateCmd: "brew-tui activate <tu-clave-de-licencia>",
|
|
560
|
+
upgrade_proLabel: "Brew-TUI Pro \u2014 9\u20AC/mes o 49\u20AC/a\xF1o \u2014 Incluye BrewBar para macOS",
|
|
561
|
+
// ── Progress Log ──
|
|
562
|
+
progress_noOutput: "Sin salida a\xFAn",
|
|
563
|
+
// ── Search Input ──
|
|
564
|
+
searchInput_placeholder: "Escriba para filtrar...",
|
|
565
|
+
// ── Profile Manager ──
|
|
566
|
+
profileMgr_tapping: "A\xF1adiendo tap {{name}}...",
|
|
567
|
+
profileMgr_installing: "Instalando {{name}}...",
|
|
568
|
+
profileMgr_installingCask: "Instalando cask {{name}}...",
|
|
569
|
+
profileMgr_importDone: "\xA1Listo! {{count}} paquetes instalados.",
|
|
570
|
+
// ── CLI ──
|
|
571
|
+
cli_usageActivate: "Uso: brew-tui activate <clave-de-licencia>",
|
|
572
|
+
cli_activated: "\u2714 Pro activado para {{email}}",
|
|
573
|
+
cli_plan: " Plan: {{plan}}",
|
|
574
|
+
cli_expires: " Expira: {{date}}",
|
|
575
|
+
cli_activationFailed: "\u2718 Activaci\xF3n fallida: {{error}}",
|
|
576
|
+
cli_noLicense: "No se encontr\xF3 licencia activa.",
|
|
577
|
+
cli_deactivated: "\u2714 Licencia desactivada.",
|
|
578
|
+
cli_planFree: "Plan: Gratis",
|
|
579
|
+
cli_planPro: "Plan: Pro",
|
|
580
|
+
cli_confirmDeactivate: "\xBFDesactivar tu licencia Pro en esta m\xE1quina? (s/N): ",
|
|
581
|
+
cli_deactivateCancelled: "Desactivaci\xF3n cancelada.",
|
|
582
|
+
cli_upgradeHint: "Ejecuta `brew-tui activate <clave>` para actualizar a Pro.",
|
|
583
|
+
cli_email: "Email: {{email}}",
|
|
584
|
+
cli_status: "Estado: {{status}}",
|
|
585
|
+
cli_rateLimited: "Demasiados intentos de activaci\xF3n. Int\xE9ntalo en {{minutes}} minutos.",
|
|
586
|
+
cli_cooldown: "Por favor espera antes de intentar de nuevo.",
|
|
587
|
+
cli_brewbarInstalling: "Descargando BrewBar...",
|
|
588
|
+
cli_brewbarInstalled: "\u2714 BrewBar instalado en /Applications/BrewBar.app",
|
|
589
|
+
cli_brewbarAlreadyInstalled: "BrewBar ya est\xE1 instalado. Usa --force para reinstalar.",
|
|
590
|
+
cli_brewbarUninstalled: "\u2714 BrewBar eliminado de /Applications.",
|
|
591
|
+
cli_brewbarNotInstalled: "BrewBar no est\xE1 instalado.",
|
|
592
|
+
cli_brewbarProRequired: "\u2718 BrewBar requiere una licencia Pro.\n Ejecuta: brew-tui activate <clave>",
|
|
593
|
+
cli_brewbarMacOnly: "\u2718 BrewBar solo est\xE1 disponible en macOS.",
|
|
594
|
+
cli_brewbarDownloadFailed: "\u2718 Error al descargar BrewBar: {{error}}",
|
|
595
|
+
// ── License degradation (Layer 15) ──
|
|
596
|
+
license_offlineWarning: "Tu licencia no se ha validado en {{days}} d\xEDas. Por favor con\xE9ctate a internet.",
|
|
597
|
+
// ── Plurals ──
|
|
598
|
+
plural_vulns_one: "({{count}} vuln)",
|
|
599
|
+
plural_vulns_other: "({{count}} vulns)",
|
|
600
|
+
plural_warnings_one: "{{count}} advertencia",
|
|
601
|
+
plural_warnings_other: "{{count}} advertencias",
|
|
602
|
+
// ── Scroll indicators ──
|
|
603
|
+
scroll_moreAbove: "\u2191 {{count}} m\xE1s",
|
|
604
|
+
scroll_moreBelow: "\u2193 {{count}} m\xE1s"
|
|
605
|
+
};
|
|
606
|
+
var es_default = es;
|
|
607
|
+
|
|
608
|
+
// src/i18n/index.ts
|
|
609
|
+
var locales = { en: en_default, es: es_default };
|
|
610
|
+
function isLocale(s) {
|
|
611
|
+
return s === "en" || s === "es";
|
|
612
|
+
}
|
|
613
|
+
function detectLocale() {
|
|
614
|
+
const flag = process.argv.find((a) => a.startsWith("--lang="));
|
|
615
|
+
if (flag) {
|
|
616
|
+
const code = flag.split("=")[1];
|
|
617
|
+
if (code && isLocale(code)) return code;
|
|
618
|
+
}
|
|
619
|
+
const env = process.env.LANG ?? process.env.LC_ALL ?? process.env.LC_MESSAGES ?? "";
|
|
620
|
+
const prefix = env.split(/[_.]/)[0] ?? "";
|
|
621
|
+
if (isLocale(prefix)) return prefix;
|
|
622
|
+
return "en";
|
|
623
|
+
}
|
|
624
|
+
var useLocaleStore = create((set) => ({
|
|
625
|
+
locale: detectLocale(),
|
|
626
|
+
setLocale: (locale) => set({ locale })
|
|
627
|
+
}));
|
|
628
|
+
function t(key, values) {
|
|
629
|
+
const locale = useLocaleStore.getState().locale;
|
|
630
|
+
let text = locales[locale][key] ?? locales["en"][key] ?? key;
|
|
631
|
+
if (values) {
|
|
632
|
+
for (const [k, v] of Object.entries(values)) {
|
|
633
|
+
text = text.replaceAll(`{{${k}}}`, String(v));
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
return text;
|
|
637
|
+
}
|
|
638
|
+
function tp(baseKey, count, values) {
|
|
639
|
+
const suffix = count === 1 ? "_one" : "_other";
|
|
640
|
+
return t(`${baseKey}${suffix}`, { count, ...values });
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
// src/lib/data-dir.ts
|
|
644
|
+
import { homedir } from "os";
|
|
645
|
+
import { join } from "path";
|
|
646
|
+
import { mkdir } from "fs/promises";
|
|
647
|
+
var DATA_DIR = join(homedir(), ".brew-tui");
|
|
648
|
+
var PROFILES_DIR = join(DATA_DIR, "profiles");
|
|
649
|
+
var LICENSE_PATH = join(DATA_DIR, "license.json");
|
|
650
|
+
var HISTORY_PATH = join(DATA_DIR, "history.json");
|
|
651
|
+
async function ensureDataDirs() {
|
|
652
|
+
await mkdir(DATA_DIR, { recursive: true });
|
|
653
|
+
await mkdir(PROFILES_DIR, { recursive: true });
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
// src/lib/license/license-manager.ts
|
|
657
|
+
import { readFile, writeFile, rm } from "fs/promises";
|
|
658
|
+
import { createCipheriv, createDecipheriv, randomBytes, scryptSync } from "crypto";
|
|
659
|
+
|
|
660
|
+
// src/lib/license/polar-api.ts
|
|
661
|
+
import { hostname } from "os";
|
|
662
|
+
var BASE_URL = "https://api.polar.sh/v1/customer-portal/license-keys";
|
|
663
|
+
var POLAR_ORGANIZATION_ID = "b8f245c0-d116-4457-92fb-1bda47139f82";
|
|
664
|
+
function validateApiUrl(url) {
|
|
665
|
+
const parsed = new URL(url);
|
|
666
|
+
if (parsed.protocol !== "https:") {
|
|
667
|
+
throw new Error("HTTPS required for license API");
|
|
668
|
+
}
|
|
669
|
+
if (!parsed.hostname.endsWith("polar.sh")) {
|
|
670
|
+
throw new Error("Invalid API host");
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
async function post(endpoint, body, expectEmpty = false) {
|
|
674
|
+
const url = `${BASE_URL}/${endpoint}`;
|
|
675
|
+
validateApiUrl(url);
|
|
676
|
+
const res = await fetch(url, {
|
|
677
|
+
method: "POST",
|
|
678
|
+
headers: { "Content-Type": "application/json" },
|
|
679
|
+
body: JSON.stringify(body)
|
|
680
|
+
});
|
|
681
|
+
if (!res.ok) {
|
|
682
|
+
let message = `Request failed with status ${res.status}`;
|
|
683
|
+
try {
|
|
684
|
+
const errBody = await res.json();
|
|
685
|
+
if (typeof errBody.detail === "string") message = errBody.detail;
|
|
686
|
+
else if (typeof errBody.error === "string") message = errBody.error;
|
|
687
|
+
else if (typeof errBody.message === "string") message = errBody.message;
|
|
688
|
+
} catch {
|
|
689
|
+
}
|
|
690
|
+
throw new Error(message);
|
|
691
|
+
}
|
|
692
|
+
if (expectEmpty || res.status === 204) return void 0;
|
|
693
|
+
return res.json();
|
|
694
|
+
}
|
|
695
|
+
async function activateLicense(key) {
|
|
696
|
+
const activation = await post("activate", {
|
|
697
|
+
key,
|
|
698
|
+
organization_id: POLAR_ORGANIZATION_ID,
|
|
699
|
+
label: hostname()
|
|
700
|
+
});
|
|
701
|
+
let customerEmail = "";
|
|
702
|
+
let customerName = "";
|
|
703
|
+
try {
|
|
704
|
+
const validated = await post("validate", {
|
|
705
|
+
key,
|
|
706
|
+
organization_id: POLAR_ORGANIZATION_ID,
|
|
707
|
+
activation_id: activation.id
|
|
708
|
+
});
|
|
709
|
+
customerEmail = validated.customer?.email ?? "";
|
|
710
|
+
customerName = validated.customer?.name ?? "";
|
|
711
|
+
} catch {
|
|
712
|
+
}
|
|
713
|
+
return {
|
|
714
|
+
activated: true,
|
|
715
|
+
error: null,
|
|
716
|
+
instance: { id: activation.id },
|
|
717
|
+
license_key: {
|
|
718
|
+
id: 0,
|
|
719
|
+
status: activation.license_key.status,
|
|
720
|
+
key,
|
|
721
|
+
activation_limit: 0,
|
|
722
|
+
activations_count: 0,
|
|
723
|
+
expires_at: activation.license_key.expires_at
|
|
724
|
+
},
|
|
725
|
+
meta: { customer_email: customerEmail, customer_name: customerName }
|
|
726
|
+
};
|
|
727
|
+
}
|
|
728
|
+
async function validateLicense(key, instanceId) {
|
|
729
|
+
const res = await post("validate", {
|
|
730
|
+
key,
|
|
731
|
+
organization_id: POLAR_ORGANIZATION_ID,
|
|
732
|
+
activation_id: instanceId
|
|
733
|
+
});
|
|
734
|
+
const notExpired = res.expires_at === null || new Date(res.expires_at) > /* @__PURE__ */ new Date();
|
|
735
|
+
const valid = res.status === "granted" && notExpired;
|
|
736
|
+
return {
|
|
737
|
+
valid,
|
|
738
|
+
error: valid ? null : `License ${res.status}`,
|
|
739
|
+
license_key: {
|
|
740
|
+
id: 0,
|
|
741
|
+
status: res.status,
|
|
742
|
+
key,
|
|
743
|
+
expires_at: res.expires_at
|
|
744
|
+
},
|
|
745
|
+
instance: { id: instanceId }
|
|
746
|
+
};
|
|
747
|
+
}
|
|
748
|
+
async function deactivateLicense(key, instanceId) {
|
|
749
|
+
await post(
|
|
750
|
+
"deactivate",
|
|
751
|
+
{ key, organization_id: POLAR_ORGANIZATION_ID, activation_id: instanceId },
|
|
752
|
+
true
|
|
753
|
+
);
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
// src/lib/license/license-manager.ts
|
|
757
|
+
var REVALIDATION_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
758
|
+
var GRACE_PERIOD_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
759
|
+
var ACTIVATION_COOLDOWN_MS = 3e4;
|
|
760
|
+
var MAX_ATTEMPTS = 5;
|
|
761
|
+
var LOCKOUT_MS = 15 * 60 * 1e3;
|
|
762
|
+
var tracker = {
|
|
763
|
+
attempts: 0,
|
|
764
|
+
lastAttempt: 0,
|
|
765
|
+
lockedUntil: 0
|
|
766
|
+
};
|
|
767
|
+
function checkRateLimit() {
|
|
768
|
+
const now = Date.now();
|
|
769
|
+
if (now < tracker.lockedUntil) {
|
|
770
|
+
const remaining = Math.ceil((tracker.lockedUntil - now) / 6e4);
|
|
771
|
+
throw new Error(t("cli_rateLimited", { minutes: remaining }));
|
|
772
|
+
}
|
|
773
|
+
if (now - tracker.lastAttempt < ACTIVATION_COOLDOWN_MS) {
|
|
774
|
+
throw new Error(t("cli_cooldown"));
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
function recordAttempt(success) {
|
|
778
|
+
const now = Date.now();
|
|
779
|
+
tracker.lastAttempt = now;
|
|
780
|
+
if (success) {
|
|
781
|
+
tracker.attempts = 0;
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
784
|
+
tracker.attempts++;
|
|
785
|
+
if (tracker.attempts >= MAX_ATTEMPTS) {
|
|
786
|
+
tracker.lockedUntil = now + LOCKOUT_MS;
|
|
787
|
+
tracker.attempts = 0;
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
var ENCRYPTION_SECRET = "brew-tui-license-aes256gcm-v1";
|
|
791
|
+
var SCRYPT_SALT = "brew-tui-salt-v1";
|
|
792
|
+
var _derivedKey = scryptSync(ENCRYPTION_SECRET, SCRYPT_SALT, 32);
|
|
793
|
+
function deriveEncryptionKey() {
|
|
794
|
+
return _derivedKey;
|
|
795
|
+
}
|
|
796
|
+
function encryptLicenseData(data) {
|
|
797
|
+
const key = deriveEncryptionKey();
|
|
798
|
+
const iv = randomBytes(12);
|
|
799
|
+
const cipher = createCipheriv("aes-256-gcm", key, iv);
|
|
800
|
+
const plaintext = JSON.stringify(data);
|
|
801
|
+
const ciphertext = Buffer.concat([cipher.update(plaintext, "utf-8"), cipher.final()]);
|
|
802
|
+
const tag = cipher.getAuthTag();
|
|
803
|
+
return {
|
|
804
|
+
encrypted: ciphertext.toString("base64"),
|
|
805
|
+
iv: iv.toString("base64"),
|
|
806
|
+
tag: tag.toString("base64")
|
|
807
|
+
};
|
|
808
|
+
}
|
|
809
|
+
function decryptLicenseData(encrypted, iv, tag) {
|
|
810
|
+
const key = deriveEncryptionKey();
|
|
811
|
+
const decipher = createDecipheriv(
|
|
812
|
+
"aes-256-gcm",
|
|
813
|
+
key,
|
|
814
|
+
Buffer.from(iv, "base64")
|
|
815
|
+
);
|
|
816
|
+
decipher.setAuthTag(Buffer.from(tag, "base64"));
|
|
817
|
+
const plaintext = Buffer.concat([
|
|
818
|
+
decipher.update(Buffer.from(encrypted, "base64")),
|
|
819
|
+
decipher.final()
|
|
820
|
+
]);
|
|
821
|
+
return JSON.parse(plaintext.toString("utf-8"));
|
|
822
|
+
}
|
|
823
|
+
async function loadLicense() {
|
|
824
|
+
try {
|
|
825
|
+
const raw = await readFile(LICENSE_PATH, "utf-8");
|
|
826
|
+
const file = JSON.parse(raw);
|
|
827
|
+
if (file.encrypted && file.iv && file.tag) {
|
|
828
|
+
const data = decryptLicenseData(file.encrypted, file.iv, file.tag);
|
|
829
|
+
return data;
|
|
830
|
+
}
|
|
831
|
+
if (file.license) {
|
|
832
|
+
const data = file.license;
|
|
833
|
+
await saveLicense(data);
|
|
834
|
+
return data;
|
|
835
|
+
}
|
|
836
|
+
return null;
|
|
837
|
+
} catch {
|
|
838
|
+
return null;
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
async function saveLicense(data) {
|
|
842
|
+
await ensureDataDirs();
|
|
843
|
+
const { encrypted, iv, tag } = encryptLicenseData(data);
|
|
844
|
+
const file = { version: 1, encrypted, iv, tag };
|
|
845
|
+
await writeFile(LICENSE_PATH, JSON.stringify(file, null, 2), { encoding: "utf-8", mode: 384 });
|
|
846
|
+
}
|
|
847
|
+
async function clearLicense() {
|
|
848
|
+
try {
|
|
849
|
+
await rm(LICENSE_PATH);
|
|
850
|
+
} catch {
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
function isExpired(license) {
|
|
854
|
+
if (!license.expiresAt) return false;
|
|
855
|
+
return new Date(license.expiresAt).getTime() < Date.now();
|
|
856
|
+
}
|
|
857
|
+
function needsRevalidation(license) {
|
|
858
|
+
const lastValidated = new Date(license.lastValidatedAt).getTime();
|
|
859
|
+
if (isNaN(lastValidated)) return true;
|
|
860
|
+
return Date.now() - lastValidated > REVALIDATION_INTERVAL_MS;
|
|
861
|
+
}
|
|
862
|
+
function isWithinGracePeriod(license) {
|
|
863
|
+
const lastValidated = new Date(license.lastValidatedAt).getTime();
|
|
864
|
+
if (isNaN(lastValidated)) return false;
|
|
865
|
+
return Date.now() - lastValidated < GRACE_PERIOD_MS;
|
|
866
|
+
}
|
|
867
|
+
function getDegradationLevel(license) {
|
|
868
|
+
const lastValidated = new Date(license.lastValidatedAt).getTime();
|
|
869
|
+
if (isNaN(lastValidated)) return "expired";
|
|
870
|
+
const elapsed = Date.now() - lastValidated;
|
|
871
|
+
if (elapsed < 0) return "none";
|
|
872
|
+
const days = elapsed / (24 * 60 * 60 * 1e3);
|
|
873
|
+
if (days <= 7) return "none";
|
|
874
|
+
if (days <= 14) return "warning";
|
|
875
|
+
if (days <= 30) return "limited";
|
|
876
|
+
return "expired";
|
|
877
|
+
}
|
|
878
|
+
function validateLicenseKey(key) {
|
|
879
|
+
if (key.length < 10 || key.length > 100) {
|
|
880
|
+
throw new Error("Invalid license key format");
|
|
881
|
+
}
|
|
882
|
+
if (!/^[\w-]+$/.test(key)) {
|
|
883
|
+
throw new Error("Invalid license key format");
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
async function activate(key) {
|
|
887
|
+
validateLicenseKey(key);
|
|
888
|
+
checkRateLimit();
|
|
889
|
+
let success = false;
|
|
890
|
+
try {
|
|
891
|
+
const res = await activateLicense(key);
|
|
892
|
+
if (!res.activated) {
|
|
893
|
+
throw new Error(res.error ?? "Activation failed");
|
|
894
|
+
}
|
|
895
|
+
const license = {
|
|
896
|
+
key,
|
|
897
|
+
instanceId: res.instance.id,
|
|
898
|
+
status: "active",
|
|
899
|
+
customerEmail: res.meta.customer_email,
|
|
900
|
+
customerName: res.meta.customer_name,
|
|
901
|
+
plan: "pro",
|
|
902
|
+
activatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
903
|
+
expiresAt: res.license_key.expires_at,
|
|
904
|
+
lastValidatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
905
|
+
};
|
|
906
|
+
await saveLicense(license);
|
|
907
|
+
success = true;
|
|
908
|
+
return license;
|
|
909
|
+
} finally {
|
|
910
|
+
recordAttempt(success);
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
async function revalidate(license) {
|
|
914
|
+
try {
|
|
915
|
+
const res = await validateLicense(license.key, license.instanceId);
|
|
916
|
+
if (res.valid) {
|
|
917
|
+
const updated = {
|
|
918
|
+
...license,
|
|
919
|
+
lastValidatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
920
|
+
status: "active",
|
|
921
|
+
expiresAt: res.license_key.expires_at
|
|
922
|
+
};
|
|
923
|
+
await saveLicense(updated);
|
|
924
|
+
return true;
|
|
925
|
+
}
|
|
926
|
+
await saveLicense({ ...license, status: "expired" });
|
|
927
|
+
return false;
|
|
928
|
+
} catch {
|
|
929
|
+
return isWithinGracePeriod(license);
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
async function deactivate(license) {
|
|
933
|
+
try {
|
|
934
|
+
await deactivateLicense(license.key, license.instanceId);
|
|
935
|
+
} catch {
|
|
936
|
+
}
|
|
937
|
+
await clearLicense();
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
// src/stores/license-store.ts
|
|
941
|
+
import { create as create2 } from "zustand";
|
|
942
|
+
var REVALIDATION_CHECK_MS = 60 * 60 * 1e3;
|
|
943
|
+
var _revalidating = false;
|
|
944
|
+
var useLicenseStore = create2((set, get) => ({
|
|
945
|
+
status: "free",
|
|
946
|
+
license: null,
|
|
947
|
+
error: null,
|
|
948
|
+
degradation: "none",
|
|
949
|
+
initialize: async () => {
|
|
950
|
+
await ensureDataDirs();
|
|
951
|
+
const license = await loadLicense();
|
|
952
|
+
if (!license) {
|
|
953
|
+
set({ status: "free", license: null, degradation: "none" });
|
|
954
|
+
return;
|
|
955
|
+
}
|
|
956
|
+
if (isExpired(license)) {
|
|
957
|
+
set({ status: "expired", license, degradation: "expired" });
|
|
958
|
+
return;
|
|
959
|
+
}
|
|
960
|
+
const level = getDegradationLevel(license);
|
|
961
|
+
if (level === "expired") {
|
|
962
|
+
set({ status: "expired", license, degradation: "expired" });
|
|
963
|
+
return;
|
|
964
|
+
}
|
|
965
|
+
set({ status: "pro", license, degradation: level });
|
|
966
|
+
if (needsRevalidation(license) && !_revalidating) {
|
|
967
|
+
_revalidating = true;
|
|
968
|
+
try {
|
|
969
|
+
const valid = await revalidate(license);
|
|
970
|
+
if (!valid) {
|
|
971
|
+
set({ status: "expired", license: { ...license, status: "expired" } });
|
|
972
|
+
} else {
|
|
973
|
+
const updated = await loadLicense();
|
|
974
|
+
if (updated) set({ license: updated });
|
|
975
|
+
}
|
|
976
|
+
} finally {
|
|
977
|
+
_revalidating = false;
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
setInterval(async () => {
|
|
981
|
+
const current = get().license;
|
|
982
|
+
if (!current || get().status !== "pro") return;
|
|
983
|
+
if (!needsRevalidation(current)) return;
|
|
984
|
+
if (_revalidating) return;
|
|
985
|
+
_revalidating = true;
|
|
986
|
+
try {
|
|
987
|
+
const valid = await revalidate(current);
|
|
988
|
+
if (!valid) {
|
|
989
|
+
set({ status: "expired", license: { ...current, status: "expired" } });
|
|
990
|
+
} else {
|
|
991
|
+
const updated = await loadLicense();
|
|
992
|
+
if (updated) set({ license: updated });
|
|
993
|
+
}
|
|
994
|
+
} finally {
|
|
995
|
+
_revalidating = false;
|
|
996
|
+
}
|
|
997
|
+
}, REVALIDATION_CHECK_MS).unref();
|
|
998
|
+
},
|
|
999
|
+
activate: async (key) => {
|
|
1000
|
+
set({ error: null });
|
|
1001
|
+
try {
|
|
1002
|
+
const license = await activate(key);
|
|
1003
|
+
set({ status: "pro", license });
|
|
1004
|
+
return true;
|
|
1005
|
+
} catch (err) {
|
|
1006
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1007
|
+
set({ error: msg });
|
|
1008
|
+
return false;
|
|
1009
|
+
}
|
|
1010
|
+
},
|
|
1011
|
+
deactivate: async () => {
|
|
1012
|
+
const { license } = get();
|
|
1013
|
+
if (license) {
|
|
1014
|
+
await deactivate(license);
|
|
1015
|
+
}
|
|
1016
|
+
set({ status: "free", license: null, error: null });
|
|
1017
|
+
},
|
|
1018
|
+
isPro: () => get().status === "pro"
|
|
1019
|
+
}));
|
|
1020
|
+
|
|
1021
|
+
// src/lib/license/anti-debug.ts
|
|
1022
|
+
import inspector from "inspector";
|
|
1023
|
+
function isDebuggerAttached() {
|
|
1024
|
+
if (process.env.VITEST || process.env.CI) return false;
|
|
1025
|
+
if (inspector.url()) return true;
|
|
1026
|
+
if (process.execArgv.some((a) => a.includes("--inspect") || a.includes("--debug"))) return true;
|
|
1027
|
+
if (process.env.NODE_OPTIONS?.includes("--inspect")) return true;
|
|
1028
|
+
return false;
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
// src/lib/license/anti-tamper.ts
|
|
1032
|
+
var _originalIsPro = useLicenseStore.getState().isPro;
|
|
1033
|
+
var _originalGetState = useLicenseStore.getState;
|
|
1034
|
+
function verifyStoreIntegrity() {
|
|
1035
|
+
const state = useLicenseStore.getState();
|
|
1036
|
+
if (state.isPro !== _originalIsPro) return false;
|
|
1037
|
+
if (useLicenseStore.getState !== _originalGetState) return false;
|
|
1038
|
+
if (state.status === "free" && state.isPro()) return false;
|
|
1039
|
+
return true;
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
// src/lib/license/canary.ts
|
|
1043
|
+
var _canaryTripped = false;
|
|
1044
|
+
function isProUnlocked() {
|
|
1045
|
+
return false;
|
|
1046
|
+
}
|
|
1047
|
+
function hasProAccess() {
|
|
1048
|
+
return false;
|
|
1049
|
+
}
|
|
1050
|
+
function isLicenseValid() {
|
|
1051
|
+
return false;
|
|
1052
|
+
}
|
|
1053
|
+
function checkCanaries() {
|
|
1054
|
+
if (isProUnlocked()) {
|
|
1055
|
+
_canaryTripped = true;
|
|
1056
|
+
return false;
|
|
1057
|
+
}
|
|
1058
|
+
if (hasProAccess()) {
|
|
1059
|
+
_canaryTripped = true;
|
|
1060
|
+
return false;
|
|
1061
|
+
}
|
|
1062
|
+
if (isLicenseValid()) {
|
|
1063
|
+
_canaryTripped = true;
|
|
1064
|
+
return false;
|
|
1065
|
+
}
|
|
1066
|
+
return !_canaryTripped;
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
// src/lib/license/integrity.ts
|
|
1070
|
+
import { readFileSync } from "fs";
|
|
1071
|
+
import { createHash } from "crypto";
|
|
1072
|
+
import { fileURLToPath } from "url";
|
|
1073
|
+
var _baselineHash = null;
|
|
1074
|
+
function _captureBaseline() {
|
|
1075
|
+
try {
|
|
1076
|
+
const bundlePath = fileURLToPath(import.meta.url);
|
|
1077
|
+
const content = readFileSync(bundlePath, "utf-8");
|
|
1078
|
+
return createHash("sha256").update(content).digest("hex");
|
|
1079
|
+
} catch {
|
|
1080
|
+
return null;
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
_baselineHash = _captureBaseline();
|
|
1084
|
+
function checkBundleIntegrity() {
|
|
1085
|
+
if (_baselineHash === null) {
|
|
1086
|
+
return true;
|
|
1087
|
+
}
|
|
1088
|
+
try {
|
|
1089
|
+
const bundlePath = fileURLToPath(import.meta.url);
|
|
1090
|
+
const content = readFileSync(bundlePath, "utf-8");
|
|
1091
|
+
const current = createHash("sha256").update(content).digest("hex");
|
|
1092
|
+
return current === _baselineHash;
|
|
1093
|
+
} catch {
|
|
1094
|
+
return true;
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
// src/lib/license/pro-guard.ts
|
|
1099
|
+
var _S = String.fromCharCode(115, 116, 97, 116, 117, 115);
|
|
1100
|
+
var _P = String.fromCharCode(112, 114, 111);
|
|
1101
|
+
function _verify() {
|
|
1102
|
+
const state = useLicenseStore.getState();
|
|
1103
|
+
return state[_S] === _P;
|
|
1104
|
+
}
|
|
1105
|
+
function verifyPro() {
|
|
1106
|
+
if (isDebuggerAttached()) return false;
|
|
1107
|
+
if (!checkBundleIntegrity()) return false;
|
|
1108
|
+
if (!verifyStoreIntegrity()) return false;
|
|
1109
|
+
if (!checkCanaries()) return false;
|
|
1110
|
+
const directCheck = useLicenseStore.getState().status === "pro";
|
|
1111
|
+
const indirectCheck = _verify();
|
|
1112
|
+
if (!directCheck || !indirectCheck) return false;
|
|
1113
|
+
const license = useLicenseStore.getState().license;
|
|
1114
|
+
if (license) {
|
|
1115
|
+
const level = getDegradationLevel(license);
|
|
1116
|
+
if (level === "expired" || level === "limited") return false;
|
|
1117
|
+
}
|
|
1118
|
+
return true;
|
|
1119
|
+
}
|
|
1120
|
+
function requirePro() {
|
|
1121
|
+
if (!verifyPro()) {
|
|
1122
|
+
throw new Error("Pro license required");
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
export {
|
|
1127
|
+
useLocaleStore,
|
|
1128
|
+
t,
|
|
1129
|
+
tp,
|
|
1130
|
+
PROFILES_DIR,
|
|
1131
|
+
HISTORY_PATH,
|
|
1132
|
+
ensureDataDirs,
|
|
1133
|
+
loadLicense,
|
|
1134
|
+
activate,
|
|
1135
|
+
deactivate,
|
|
1136
|
+
useLicenseStore,
|
|
1137
|
+
verifyPro,
|
|
1138
|
+
requirePro
|
|
1139
|
+
};
|
|
1140
|
+
//# sourceMappingURL=chunk-P6PTN4HR.js.map
|