burnbar 0.0.0-dev.37.1

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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Lahiru S Liyanage
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,245 @@
1
+ <div align="center">
2
+
3
+ <img src="assets/icon.png" alt="BurnBar icon" width="96">
4
+
5
+ # BurnBar
6
+
7
+ [![Build](https://github.com/nooblk-98/burnbar/actions/workflows/build.yml/badge.svg)](https://github.com/nooblk-98/burnbar/actions/workflows/build.yml)
8
+ [![Release](https://img.shields.io/github/v/release/nooblk-98/burnbar?include_prereleases&label=release)](https://github.com/nooblk-98/burnbar/releases)
9
+ [![Go](https://img.shields.io/github/go-mod/go-version/nooblk-98/burnbar)](go.mod)
10
+ [![Platform](https://img.shields.io/badge/platform-Windows%2010%20%7C%2011-0078D4)](#installation)
11
+ [![License](https://img.shields.io/github/license/nooblk-98/burnbar)](LICENSE)
12
+
13
+ **Claude and Codex usage meter for the Windows taskbar.**<br>
14
+ See how fast you're burning through your plan limits without leaving what you're doing.
15
+
16
+ [Features](#features) • [See it in action](#see-it-in-action) • [Installation](#installation) • [Build from source](#build-from-source)
17
+
18
+ <img src="assets/social-preview.png" alt="BurnBar, Claude and Codex on your Windows taskbar, with quotas, history, and alerts" width="848">
19
+
20
+ </div>
21
+
22
+ Claude and Codex have session and longer-term plan limits. BurnBar keeps both providers on your
23
+ taskbar, warns you before you run out, and shows whether you're using them faster than the time
24
+ window allows. Use both together, or select one from **Usage providers** in the right-click menu.
25
+ Codex window lengths come from your account rather than assuming every plan uses the same limits.
26
+
27
+ > [!NOTE]
28
+ > BurnBar is an unofficial community project, not affiliated with or endorsed by Anthropic or OpenAI.
29
+ > "Claude" and "Codex" belong to their respective owners.
30
+
31
+ ## Features
32
+
33
+ - **Claude + Codex**: one compact meter rotates between providers every 8 seconds with a downward
34
+ flip animation. The provider name stays visible above its two quota bars. Rotation pauses on
35
+ hover and while details are open, and respects Windows' animation setting.
36
+ Select Claude only or Codex only to keep one provider visible. Bars turn red at 90%.
37
+ - **Pace marker**: a tick on each bar shows how much of the window's time has passed. A bar past its
38
+ tick means you'll hit the limit before it resets.
39
+ - **Stays out of the way**: finds free space on the taskbar so it never covers app icons, follows the
40
+ light/dark theme and display scaling, and hides during fullscreen apps and games.
41
+ The meter narrows when taskbar buttons need more room. If no safe gap fits, it hides
42
+ until space opens; the tray icon and details remain available. It never floats over desktop
43
+ content. Vertical or too-short taskbars also use the tray icon. Search, Start and ordinary
44
+ window clicks keep it visible when space is available.
45
+ - **Details panel**: separate provider panels, reported quota windows, reset times, and independent
46
+ 24-hour and 7-day history charts. Claude also shows this week's usage by product when available.
47
+ - **Alerts**: Windows notifications at 80% and 95%, and when a limit you'd been close to resets.
48
+ - **Fresh numbers**: refreshes on a schedule, within a minute of Claude Code or Codex activity, and right after
49
+ a limit resets, backing off automatically if the API rate-limits.
50
+ - **Multi-monitor**: a meter on every taskbar, plus a tray icon with tooltip and menu.
51
+ - **Lightweight**: a single executable with no installer. Codex usage uses your existing Codex CLI.
52
+ - **Start with Windows**: enabled on first launch for new users; turn it off from the
53
+ right-click menu at any time. Existing startup preferences are preserved.
54
+ - **About**: right-click the meter or tray icon for the app version, developer credits,
55
+ and a link to this repository.
56
+
57
+ ## See it in action
58
+
59
+ <div align="center">
60
+ <img src="docs/images/burnbar-demo.gif" alt="BurnBar on Windows, showing the rotating taskbar meter and Claude and Codex usage details" width="848">
61
+ <br>
62
+ <sub>BurnBar in action: Claude and Codex usage on the taskbar, with details a click away.</sub>
63
+ </div>
64
+
65
+ ## Installation
66
+
67
+ You need Windows 10 or 11 and a signed-in provider:
68
+
69
+ - **Claude:** [Claude Code](https://claude.com/claude-code) signed in with a Claude subscription.
70
+ - **Codex:** [Codex CLI](https://developers.openai.com/codex/cli/) signed in with **ChatGPT**
71
+ (`codex login`). API-key-only accounts do not provide these subscription usage limits.
72
+ BurnBar also detects the CLI bundled with the Windows Codex desktop app when available.
73
+
74
+ Both providers rotate through the meter by default. A missing login affects only that provider. Select
75
+ **Usage providers** from the right-click menu to hide a provider you don't use.
76
+
77
+ ### Install or update with npx
78
+
79
+ Requires Windows, [Node.js 22+](https://nodejs.org/) (includes npm/npx), and
80
+ Windows PowerShell 5.1. BurnBar itself does not need Node.js after installation.
81
+
82
+ Install or update using these npm channels (the stable `latest` tag is created
83
+ by the first stable release after this workflow reaches main):
84
+
85
+ ```powershell
86
+ # Latest stable release (same command for first install and updates)
87
+ npx --yes burnbar@latest install
88
+
89
+ # Latest development build (may contain bugs)
90
+ npx --yes burnbar@development install
91
+
92
+ # Switch back to stable
93
+ npx --yes burnbar@latest update --channel stable
94
+ ```
95
+
96
+ `latest` and `development` are **npm installer channels**. The installer selects
97
+ the matching **GitHub binary channel** by default; `--channel` overrides it.
98
+ The installer package version is not a pin to a particular executable version:
99
+ each invocation checks GitHub for the latest published build of that channel.
100
+ Updates are explicit, not an always-running automatic updater.
101
+
102
+ **Before the first stable npm publish**, use the development installer to install
103
+ the stable executable:
104
+
105
+ ```powershell
106
+ npx --yes burnbar@development install --channel stable
107
+ ```
108
+
109
+ Alternatively, use the GitHub-backed package (also
110
+ requires Git). These commands do not require an npm account or npm publication:
111
+
112
+ ```powershell
113
+ npx --yes github:nooblk-98/burnbar#development install --channel stable
114
+ npx --yes github:nooblk-98/burnbar#development install --channel development
115
+ ```
116
+
117
+ Both channels use `%LOCALAPPDATA%\Programs\BurnBar` and preserve existing settings,
118
+ history and startup preferences when switching. The CLI delegates to its bundled
119
+ PowerShell installer, verifies SHA-256 before replacing anything, and creates a
120
+ Start menu shortcut. A development push publishes an immutable prerelease build;
121
+ stable installs never select those prereleases.
122
+
123
+ ```powershell
124
+ # Optional flags work with either package source
125
+ npx --yes burnbar@latest update --install-dir 'D:\Apps\BurnBar' --no-start
126
+ npx --yes burnbar@development install --dry-run
127
+ ```
128
+
129
+ `--dry-run` reads release metadata only: no binary download, file replacement,
130
+ process shutdown or launch. See [release setup](docs/RELEASING.md) for npm publishing.
131
+
132
+ ### Install or update with PowerShell (no Node.js required)
133
+
134
+
135
+ Run this in a regular PowerShell window; administrator access is not needed:
136
+
137
+ ```powershell
138
+ & ([scriptblock]::Create((irm https://raw.githubusercontent.com/nooblk-98/burnbar/main/install.ps1)))
139
+ ```
140
+
141
+ The same command installs or updates the **latest stable release** in
142
+ `%LOCALAPPDATA%\Programs\BurnBar`, verifies its SHA-256 checksum, adds a Start menu
143
+ shortcut, and launches BurnBar. Existing settings and history are kept. A running
144
+ BurnBar instance in your session is closed after the download is verified.
145
+
146
+ You can [review the installer](install.ps1) before running it. To choose a folder
147
+ or install without launching:
148
+
149
+ ```powershell
150
+ $installer = irm https://raw.githubusercontent.com/nooblk-98/burnbar/main/install.ps1
151
+ & ([scriptblock]::Create($installer)) -InstallDir 'D:\Apps\BurnBar' -NoStart
152
+ ```
153
+
154
+ For a manual install, download `burnbar.exe` from the
155
+ [releases page](https://github.com/nooblk-98/burnbar/releases), put it anywhere,
156
+ and run it.
157
+
158
+ On its first normal launch, BurnBar enables **Start with Windows** for a new profile.
159
+ You can disable it from the right-click menu. Demo and diagnostic modes do not enable it.
160
+
161
+ > [!NOTE]
162
+ > BurnBar is distributed **unsigned**. Windows Smart App Control may block it,
163
+ > and SmartScreen may show *Windows protected your PC*.
164
+
165
+ ### If Smart App Control blocks BurnBar (Windows 11)
166
+
167
+ > [!WARNING]
168
+ > Turning off Smart App Control reduces protection for your whole PC. Recent Windows
169
+ > updates support turning it back on, but availability depends on your device and
170
+ > configuration. If Windows warns that restoring it requires a reset or reinstall,
171
+ > do not proceed expecting a temporary change.
172
+
173
+ 1. Install the latest Windows updates before changing this setting.
174
+ 2. Open **Windows Security > App & browser control > Smart App Control settings**.
175
+ 3. If you trust the download and choose to proceed, select **Off** before running
176
+ BurnBar. Read the confirmation carefully.
177
+ 4. Download `burnbar.exe` from this repository's releases, place it in your preferred
178
+ folder, and run it. BurnBar is portable and has no installer.
179
+ 5. Afterwards, return to **Smart App Control settings** and select **On**, if
180
+ available. Restart if Windows asks, and confirm protection is on.
181
+
182
+ Turning protection back on may block BurnBar again. Running it once does not create
183
+ an exception for the app. See [Microsoft's Smart App Control FAQ](https://support.microsoft.com/en-us/windows/security/threat-malware-protection/smart-app-control-frequently-asked-questions)
184
+ for current requirements and limitations.
185
+
186
+ ## Build from source
187
+
188
+ You need [Go 1.26+](https://go.dev/dl/) on Windows.
189
+
190
+ ```powershell
191
+ git clone https://github.com/nooblk-98/burnbar.git
192
+ cd burnbar
193
+ .\build.ps1
194
+ .\bin\burnbar.exe
195
+ ```
196
+
197
+ `build.ps1` runs vet and any locally available tests, then builds `bin\burnbar.exe`.
198
+ Add `-Resources` to regenerate the icon, manifest and version info, or `-SkipTests`
199
+ to skip local tests. Test sources are excluded from Git.
200
+
201
+ The executable also accepts a few flags for development:
202
+
203
+ | Flag | Description |
204
+ |---|---|
205
+ | `-demo` | Show sample numbers and history without calling the API |
206
+ | `-providers both` | Show Claude and Codex; also accepts `claude` or `codex`. Defaults to the saved menu setting. |
207
+ | `-taskbar-layout` | Print measured taskbar spans and the compact meter's available slot, then exit. Useful for diagnosing placement. |
208
+ | `-snapshot <dir>` | Render PNG previews and the provider-rotation GIF in both themes, then exit. Combine with `-demo -providers both`. |
209
+ | `-once` | Print usage summaries for the selected providers, then exit. Needs a console build: `go build -o bin\burnbar-cli.exe ./cmd/burnbar` |
210
+
211
+ > [!WARNING]
212
+ > Local builds and release builds are unsigned, so Windows **Smart App Control** may block them.
213
+ > See [Releasing BurnBar](docs/RELEASING.md) for the release process.
214
+
215
+ <details>
216
+ <summary><strong>Project layout</strong></summary>
217
+
218
+ ```
219
+ cmd/burnbar/ entry point, flags, embedded icon and manifest
220
+ internal/app/ Windows UI: taskbar meters, tray icon, details panel, menu, Win32 and UI Automation
221
+ internal/usage/ Claude usage client and Codex app-server account client
222
+ internal/render/ drawing the meter, details panel, charts and tray icon
223
+ internal/layout/ choosing a free spot on the taskbar
224
+ internal/config/ settings and alert state
225
+ internal/history/ usage history for the charts
226
+ tools/genres/ icon and Windows resource generator
227
+ assets/ app icon
228
+ docs/ screenshots and release guide
229
+ .github/workflows/ CI, GitHub releases and optional winget publishing
230
+ ```
231
+
232
+ </details>
233
+
234
+ ## Contributing
235
+
236
+ Contributors are welcome! Bug fixes, documentation improvements, and new AI tool
237
+ integrations are all appreciated. Feel free to open a pull request.
238
+
239
+ See the [contribution guide](CONTRIBUTING.md) for setup and checks, the
240
+ [roadmap](ROADMAP.md) for upcoming work, and [privacy details](PRIVACY.md) for data
241
+ access and storage.
242
+
243
+ Support for more AI tools is coming soon. To request a specific tool or suggest a
244
+ feature, [open an issue](https://github.com/nooblk-98/burnbar/issues/new/choose) and tell us
245
+ what you would like to see.
package/install.ps1 ADDED
@@ -0,0 +1,206 @@
1
+ #requires -Version 5.1
2
+ <#
3
+ .SYNOPSIS
4
+ Installs or updates a stable or development BurnBar release for the current user.
5
+ .EXAMPLE
6
+ & .\install.ps1
7
+ .EXAMPLE
8
+ & .\install.ps1 -InstallDir 'D:\Apps\BurnBar' -NoStart
9
+ #>
10
+ [CmdletBinding(SupportsShouldProcess = $true)]
11
+ param(
12
+ [string]$InstallDir = (Join-Path $env:LOCALAPPDATA 'Programs\BurnBar'),
13
+ [switch]$NoStart,
14
+ [ValidateSet('stable', 'development')]
15
+ [string]$Channel = 'stable'
16
+ )
17
+
18
+ $ErrorActionPreference = 'Stop'
19
+ if ([Environment]::OSVersion.Platform -ne [PlatformID]::Win32NT) {
20
+ throw 'BurnBar requires Windows.'
21
+ }
22
+ if (-not [Environment]::Is64BitOperatingSystem) {
23
+ throw 'The BurnBar release requires 64-bit Windows.'
24
+ }
25
+
26
+ $installPath = [IO.Path]::GetFullPath([Environment]::ExpandEnvironmentVariables($InstallDir))
27
+ $targetExe = Join-Path $installPath 'burnbar.exe'
28
+ $headers = @{ 'User-Agent' = 'BurnBar-Installer'; Accept = 'application/vnd.github+json' }
29
+ $previousTls = [Net.ServicePointManager]::SecurityProtocol
30
+ $stagePath = $null
31
+ $preserveBackup = $false
32
+ $stoppedPaths = @()
33
+ $installComplete = $false
34
+
35
+ function Get-BurnBarProcesses {
36
+ param([string]$Target, [bool]$TargetOnly)
37
+ $session = [Diagnostics.Process]::GetCurrentProcess().SessionId
38
+ foreach ($candidate in @(Get-Process -Name 'burnbar*' -ErrorAction SilentlyContinue)) {
39
+ try {
40
+ if ($candidate.SessionId -ne $session -or -not $candidate.Path) { continue }
41
+ $candidatePath = [IO.Path]::GetFullPath($candidate.Path)
42
+ if ($candidatePath -eq $Target) {
43
+ $candidate
44
+ } elseif (-not $TargetOnly) {
45
+ $info = [Diagnostics.FileVersionInfo]::GetVersionInfo($candidatePath)
46
+ if ($info.ProductName -eq 'BurnBar' -and $info.OriginalFilename -eq 'burnbar.exe') {
47
+ $candidate
48
+ }
49
+ }
50
+ } catch {
51
+ # Never stop an inaccessible or unverified process based only on its name.
52
+ continue
53
+ }
54
+ }
55
+ }
56
+
57
+ try {
58
+ [Net.ServicePointManager]::SecurityProtocol = $previousTls -bor [Net.SecurityProtocolType]::Tls12
59
+ Write-Host "Checking the latest BurnBar $Channel release..."
60
+ if ($Channel -eq 'stable') {
61
+ $release = Invoke-RestMethod -Uri 'https://api.github.com/repos/nooblk-98/burnbar/releases/latest' -Headers $headers -TimeoutSec 30
62
+ if ($release.draft -or $release.prerelease -or -not $release.tag_name) {
63
+ throw 'GitHub did not return a published stable release.'
64
+ }
65
+ } else {
66
+ # Each development build has immutable assets. Never update two assets on
67
+ # a rolling release, where concurrent clients could fetch a mixed pair.
68
+ $release = $null
69
+ for ($page = 1; $page -le 20; $page++) {
70
+ $releases = @((Invoke-RestMethod -Uri "https://api.github.com/repos/nooblk-98/burnbar/releases?per_page=100&page=$page" -Headers $headers -TimeoutSec 30))
71
+ $release = $releases | Where-Object { -not $_.draft -and $_.prerelease -and $_.tag_name -match '^dev-[0-9]+-[0-9]+$' } | Select-Object -First 1
72
+ if ($release -or $releases.Count -lt 100) { break }
73
+ }
74
+ if (-not $release) { throw 'No published development build was found. Try the stable channel or wait for the development workflow.' }
75
+ Write-Warning 'Installing an unreleased development build. Switch to --channel stable to return to a stable release.'
76
+ }
77
+ $exeAssets = @($release.assets | Where-Object { $_.name -ceq 'burnbar.exe' })
78
+ $hashAssets = @($release.assets | Where-Object { $_.name -ceq 'burnbar.exe.sha256' })
79
+ if ($exeAssets.Count -ne 1 -or $hashAssets.Count -ne 1) {
80
+ throw "Release $($release.tag_name) must contain burnbar.exe and burnbar.exe.sha256."
81
+ }
82
+ foreach ($asset in @($exeAssets[0], $hashAssets[0])) {
83
+ $assetUri = [Uri]$asset.browser_download_url
84
+ $expectedAssetPath = '/nooblk-98/burnbar/releases/download/' + [Uri]::EscapeDataString($release.tag_name) + '/' + $asset.name
85
+ if ($assetUri.Scheme -ne 'https' -or $assetUri.Host -ne 'github.com' -or
86
+ $assetUri.AbsolutePath -cne $expectedAssetPath -or $assetUri.Query -or $assetUri.Fragment) {
87
+ throw 'The release contains an unexpected download URL.'
88
+ }
89
+ }
90
+ if (-not $PSCmdlet.ShouldProcess($targetExe, "Install or update BurnBar $($release.tag_name)")) {
91
+ return
92
+ }
93
+
94
+ [IO.Directory]::CreateDirectory($installPath) | Out-Null
95
+ $stagePath = Join-Path $installPath ('.burnbar-update-' + [Guid]::NewGuid().ToString('N'))
96
+ [IO.Directory]::CreateDirectory($stagePath) | Out-Null
97
+ $downloadExe = Join-Path $stagePath 'burnbar.exe'
98
+ $downloadHash = Join-Path $stagePath 'burnbar.exe.sha256'
99
+ $backupExe = Join-Path $stagePath 'burnbar.previous.exe'
100
+ Write-Host "Downloading BurnBar $($release.tag_name)..."
101
+ Invoke-WebRequest -UseBasicParsing -Uri $hashAssets[0].browser_download_url -Headers $headers -OutFile $downloadHash -TimeoutSec 60 | Out-Null
102
+ Invoke-WebRequest -UseBasicParsing -Uri $exeAssets[0].browser_download_url -Headers $headers -OutFile $downloadExe -TimeoutSec 180 | Out-Null
103
+ $hashText = (Get-Content -LiteralPath $downloadHash -Raw).Trim()
104
+ if ($hashText -notmatch '\A([0-9a-fA-F]{64})[ \t]+\*?burnbar\.exe\z') {
105
+ throw 'The release checksum file has an unexpected format.'
106
+ }
107
+ $expectedHash = $Matches[1].ToLowerInvariant()
108
+ $actualHash = (Get-FileHash -LiteralPath $downloadExe -Algorithm SHA256).Hash.ToLowerInvariant()
109
+ if ($actualHash -ne $expectedHash) {
110
+ throw 'SHA-256 verification failed. The existing installation was not changed.'
111
+ }
112
+ $digest = $exeAssets[0].digest
113
+ if ($digest -and $digest -ne ('sha256:' + $actualHash)) {
114
+ throw 'The download does not match the release asset digest. Installation was stopped.'
115
+ }
116
+
117
+ $sameFile = (Test-Path -LiteralPath $targetExe -PathType Leaf) -and
118
+ ((Get-FileHash -LiteralPath $targetExe -Algorithm SHA256).Hash.ToLowerInvariant() -eq $actualHash)
119
+ $targetRunning = $false
120
+ foreach ($running in @(Get-BurnBarProcesses -Target $targetExe -TargetOnly $NoStart.IsPresent)) {
121
+ if ($running.HasExited) { continue }
122
+ if ($sameFile -and $running.Path -eq $targetExe) {
123
+ $targetRunning = $true
124
+ continue
125
+ }
126
+ $runningPath = $running.Path
127
+ Write-Host 'Closing the running BurnBar instance...'
128
+ $closed = $running.CloseMainWindow() -and $running.WaitForExit(3000)
129
+ if (-not $closed) {
130
+ Stop-Process -Id $running.Id -Force -ErrorAction Stop
131
+ if (-not $running.WaitForExit(5000)) { throw 'BurnBar did not exit; the update was stopped.' }
132
+ }
133
+ $stoppedPaths += $runningPath
134
+ }
135
+ if (-not $sameFile) {
136
+ if (Test-Path -LiteralPath $targetExe -PathType Leaf) {
137
+ [IO.File]::Replace($downloadExe, $targetExe, $backupExe)
138
+ } else {
139
+ [IO.File]::Move($downloadExe, $targetExe)
140
+ }
141
+ }
142
+ $installComplete = $true
143
+
144
+ try {
145
+ $programs = Join-Path $env:APPDATA 'Microsoft\Windows\Start Menu\Programs'
146
+ [IO.Directory]::CreateDirectory($programs) | Out-Null
147
+ $shell = New-Object -ComObject WScript.Shell
148
+ $shortcut = $shell.CreateShortcut((Join-Path $programs 'BurnBar.lnk'))
149
+ $shortcut.TargetPath = $targetExe
150
+ $shortcut.WorkingDirectory = $installPath
151
+ $shortcut.IconLocation = "$targetExe,0"
152
+ $shortcut.Description = 'Claude and Codex usage monitor for the Windows taskbar'
153
+ $shortcut.Save()
154
+ } catch {
155
+ Write-Warning "BurnBar is installed, but its Start menu shortcut could not be created: $($_.Exception.Message)"
156
+ }
157
+ if (-not $NoStart -and -not $targetRunning) {
158
+ try {
159
+ $started = Start-Process -FilePath $targetExe -WorkingDirectory $installPath -WindowStyle Hidden -PassThru
160
+ Start-Sleep -Milliseconds 1000
161
+ if ($started.HasExited) {
162
+ Write-Warning 'BurnBar exited after launch. Another copy may be running; close it and launch the installed executable.'
163
+ }
164
+ } catch {
165
+ Write-Warning "BurnBar is installed, but Windows could not launch it: $($_.Exception.Message)"
166
+ Write-Warning 'See the README for unsigned-app and Smart App Control guidance.'
167
+ }
168
+ }
169
+ if ($sameFile) {
170
+ Write-Host "BurnBar $($release.tag_name) is already up to date."
171
+ } else {
172
+ Write-Host "Installed BurnBar $($release.tag_name)."
173
+ }
174
+ Write-Host "Location: $targetExe"
175
+ } catch {
176
+ $installError = $_
177
+ if ($stagePath -and (Test-Path -LiteralPath (Join-Path $stagePath 'burnbar.previous.exe'))) {
178
+ try {
179
+ [IO.File]::Copy((Join-Path $stagePath 'burnbar.previous.exe'), $targetExe, $true)
180
+ } catch {
181
+ $preserveBackup = $true
182
+ Write-Warning "The previous executable is preserved at $stagePath\burnbar.previous.exe."
183
+ }
184
+ }
185
+ if (-not $NoStart -and -not $installComplete) {
186
+ foreach ($oldPath in @($stoppedPaths | Select-Object -Unique)) {
187
+ try { Start-Process -FilePath $oldPath -WindowStyle Hidden | Out-Null } catch {
188
+ Write-Warning "Could not restart the previous BurnBar instance at $oldPath."
189
+ }
190
+ }
191
+ }
192
+ throw $installError
193
+ } finally {
194
+ [Net.ServicePointManager]::SecurityProtocol = $previousTls
195
+ if ($stagePath) {
196
+ # Remove only our explicitly named files, then the empty staging directory.
197
+ # No recursive deletion or paths outside the unique staging directory.
198
+ foreach ($name in @('burnbar.exe', 'burnbar.exe.sha256')) {
199
+ Remove-Item -LiteralPath (Join-Path $stagePath $name) -Force -ErrorAction SilentlyContinue
200
+ }
201
+ if (-not $preserveBackup) {
202
+ Remove-Item -LiteralPath (Join-Path $stagePath 'burnbar.previous.exe') -Force -ErrorAction SilentlyContinue
203
+ try { [IO.Directory]::Delete($stagePath) } catch { }
204
+ }
205
+ }
206
+ }
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const path = require('node:path');
5
+ const { spawnSync } = require('node:child_process');
6
+ const pkg = require('../../package.json');
7
+
8
+ const help = `BurnBar installer ${pkg.version}
9
+
10
+ Usage: burnbar [install|update] [options]
11
+ --channel stable|development Release channel (default: matches installer package)
12
+ --install-dir <directory> Override %LOCALAPPDATA%\\Programs\\BurnBar
13
+ --no-start Install without launching BurnBar
14
+ --dry-run Check release metadata without modifying the installation
15
+ --help Show this help
16
+ --version Show the installer package version
17
+
18
+ Install and update use the same verified replacement process. Switching channels
19
+ replaces the executable but preserves your settings and history.
20
+ Requires Windows x64 (or x64 emulation), Node.js 22+, and Windows PowerShell 5.1.
21
+ `;
22
+
23
+ function parseArgs(args) {
24
+ const options = { channel: pkg.version.includes('-dev.') ? 'development' : 'stable' };
25
+ const seen = new Set();
26
+ if (args[0] === 'install' || args[0] === 'update') args = args.slice(1);
27
+ for (let i = 0; i < args.length; i++) {
28
+ const arg = args[i];
29
+ if (seen.has(arg)) throw new Error(`Duplicate option: ${arg}`);
30
+ seen.add(arg);
31
+ switch (arg) {
32
+ case '--channel':
33
+ case '--install-dir': {
34
+ const value = args[++i];
35
+ if (!value || value.startsWith('-')) throw new Error(`${arg} needs a value`);
36
+ if (arg === '--channel') {
37
+ if (!['stable', 'development'].includes(value)) throw new Error('Channel must be stable or development');
38
+ options.channel = value;
39
+ } else {
40
+ options.installDir = path.resolve(value);
41
+ }
42
+ break;
43
+ }
44
+ case '--no-start': options.noStart = true; break;
45
+ case '--dry-run': options.dryRun = true; break;
46
+ case '--help': options.help = true; break;
47
+ case '--version': options.version = true; break;
48
+ default: throw new Error(`Unknown argument: ${arg}. Use --help for usage.`);
49
+ }
50
+ }
51
+ return options;
52
+ }
53
+
54
+ function invocation(options, env = process.env) {
55
+ if (!env.SystemRoot || !path.win32.isAbsolute(env.SystemRoot)) {
56
+ throw new Error('Cannot locate Windows PowerShell: SystemRoot is missing or invalid');
57
+ }
58
+ // Use the system executable and a bundled script. Never fetch/execute a remote
59
+ // script or interpolate user input into a PowerShell command string.
60
+ const executable = path.win32.join(env.SystemRoot, 'System32', 'WindowsPowerShell', 'v1.0', 'powershell.exe');
61
+ const args = ['-NoLogo', '-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass',
62
+ '-File', path.resolve(__dirname, '../../install.ps1'), '-Channel', options.channel];
63
+ if (options.installDir) args.push('-InstallDir', options.installDir);
64
+ if (options.noStart) args.push('-NoStart');
65
+ if (options.dryRun) args.push('-WhatIf');
66
+ return { executable, args };
67
+ }
68
+
69
+ function main(args = process.argv.slice(2), runtime = { platform: process.platform, env: process.env, spawnSync }) {
70
+ try {
71
+ const options = parseArgs(args);
72
+ if (options.help) { console.log(help); return 0; }
73
+ if (options.version) { console.log(pkg.version); return 0; }
74
+ if (runtime.platform !== 'win32') throw new Error('BurnBar requires Windows. No files were installed.');
75
+ const { executable, args: psArgs } = invocation(options, runtime.env);
76
+ // PowerShell 7 can pass its incompatible module search path to Windows
77
+ // PowerShell 5.1 (e.g. when npx is launched from pwsh). Let 5.1 construct
78
+ // its own module path so standard cmdlets such as Get-FileHash resolve.
79
+ const childEnv = { ...runtime.env };
80
+ for (const key of Object.keys(childEnv)) {
81
+ if (key.toLowerCase() === 'psmodulepath') delete childEnv[key];
82
+ }
83
+ const result = runtime.spawnSync(executable, psArgs, {
84
+ stdio: 'inherit', shell: false, windowsHide: true, env: childEnv,
85
+ });
86
+ if (result.error) throw new Error(`Could not run Windows PowerShell: ${result.error.message}`);
87
+ return result.status ?? 1;
88
+ } catch (error) {
89
+ console.error(`BurnBar: ${error.message}`);
90
+ return 1;
91
+ }
92
+ }
93
+
94
+ if (require.main === module) process.exitCode = main();
95
+ module.exports = { parseArgs, invocation, main };
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "burnbar",
3
+ "version": "0.0.0-dev.37.1",
4
+ "description": "Install or update the BurnBar Windows taskbar usage meter",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/nooblk-98/burnbar"
9
+ },
10
+ "homepage": "https://github.com/nooblk-98/burnbar#installation",
11
+ "bugs": {
12
+ "url": "https://github.com/nooblk-98/burnbar/issues"
13
+ },
14
+ "bin": {
15
+ "burnbar": "npm/bin/burnbar.cjs"
16
+ },
17
+ "files": [
18
+ "npm/bin/burnbar.cjs",
19
+ "install.ps1"
20
+ ],
21
+ "engines": {
22
+ "node": ">=22"
23
+ },
24
+ "publishConfig": {
25
+ "access": "public"
26
+ }
27
+ }