Skip to content

[Feature]: Publish codexbar-cli.exe as a release asset #392

Description

@JnManso

Existing issues

  • I searched existing issues and did not find a duplicate.

Problem or use case

I'm adding Windows support to a Claude/Codex usage plugin for kandev (kdlbs/kandev-plugin-provider-usage), which reads provider usage by shelling out to the CodexBar CLI.

On macOS and Linux the plugin needs no user action: upstream steipete/CodexBar publishes CLI-only tarballs per platform (CodexBarCLI-v0.45.2-macos-arm64.tar.gz, -linux-musl-x86_64.tar.gz, ...), so the plugin downloads the pinned build, verifies its SHA-256, and caches it. Nothing is installed and the user does nothing.

On Windows there is no equivalent asset. The releases here ship CodexBar-<version>-Setup.exe and CodexBar-<version>-portable.exe, and codexbar-cli.exe only exists inside the installer. So the only way to obtain the CLI is to run a full GUI application install — for a background integration that never uses the tray app.

The CLI itself is already a perfect fit. On 0.55.0 I verified it accepts exactly the same invocation the plugin already uses on macOS/Linux:

codexbar-cli usage --provider claude --format json --no-color [--source oauth]
codexbar-cli --version        # -> "codexbar 0.55.0"

Proposed solution

Publish the CLI binary you already build as a release asset.

scripts/windows-release-build.ps1 (as of v0.55.0) already produces and validates it:

$releaseExe = Join-Path $releaseBinDir "codexbar-cli.exe"          # ~line 378
...
foreach ($path in @($desktopExe, $releaseExe, $installer)) {       # ~line 468 - validated here
    if (-not (Test-Path $path)) { throw "Missing expected asset: $path" }
}
...
foreach ($asset in @($installerAsset, $portableExe)) {             # ~line 477 - but not published
    $hash = (Get-FileHash -Algorithm SHA256 $asset).Hash.ToLower()
    "$hash  $fileName" | Set-Content -Encoding ascii "$asset.sha256"
}

$releaseExe is verified to exist but never copied into $AssetsDir, so it never reaches the release. Adding it to the second loop would publish it with the same .sha256 sidecar convention as the other assets.

On the archive format: a .tar.gz would match the upstream CodexBarCLI-v<version>-<platform>.tar.gz naming, which lets existing consumers reuse the extraction path they already have for macOS/Linux. A .zip is more idiomatic on Windows and is equally fine — consumers can adapt. The important part is that a CLI-only archive exists with a checksum sidecar.

I'm happy to open the PR for this if you'd like — it looks like a small change to the asset loop.

Affected area

  • Installer / release packaging
  • CLI
  • Documentation

Alternatives considered

  • Running the installer silently (/VERYSILENT) from the plugin. Rejected: a background integration should not install a GUI application on the user's machine without them asking.
  • Extracting codexbar-cli.exe from the Inno installer with a third-party extractor. Fragile and breaks whenever the installer changes.
  • Detecting an already-installed CLI at %LOCALAPPDATA%\Programs\CodexBar\codexbar-cli.exe. I'm implementing this anyway as a fallback for users who already run Win-CodexBar, but it still requires every other user to install the full application first.
  • Building or mirroring the CLI myself (the MIT license allows it). I'd rather consume an official artifact than have the plugin become a second distribution point for your binaries.

Additional context

Consumer side, for reference: kdlbs/kandev-plugin-provider-usage — see server/download.go, where codexbarAssets maps a platform to its pinned tarball and checksum. A published Windows CLI archive would become one entry in that map.

Separately, while testing 0.55.0 I noticed the usage --format json payload differs from upstream's: the usage object uses snake_case keys (used_percent, resets_at, extra_rate_windows, login_method) where upstream emits camelCase with an identity block, and a failing provider's error is a plain string rather than an object. Happy to file that separately if it's useful — it's not a blocker for me, I'm handling both shapes on the plugin side, and it's unrelated to this packaging request.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions