Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 96 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -119,3 +119,99 @@ jobs:
fi
"$BIN" usage --provider codex --web 2>&1 | tee /tmp/codexbarcli-stderr.txt >/dev/null || true
grep -q "macOS" /tmp/codexbarcli-stderr.txt

build-windows-tray:
name: Windows tray (${{ matrix.rid }})
timeout-minutes: 35
runs-on: ${{ matrix.rid == 'win-arm64' && 'windows-11-arm' || 'windows-latest' }}
env:
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
AZURE_SIGNING_ENDPOINT: ${{ vars.AZURE_SIGNING_ENDPOINT || 'https://wus2.codesigning.azure.net/' }}
AZURE_SIGNING_ACCOUNT_NAME: ${{ vars.AZURE_SIGNING_ACCOUNT_NAME || 'hanselman' }}
AZURE_CERTIFICATE_PROFILE_NAME: ${{ vars.AZURE_CERTIFICATE_PROFILE_NAME || 'WindowsEdgeLight' }}
AZURE_TIMESTAMP_RFC3161: ${{ vars.AZURE_TIMESTAMP_RFC3161 || 'https://timestamp.acs.microsoft.com' }}
strategy:
fail-fast: false
matrix:
rid: [win-x64, win-arm64]
steps:
- uses: actions/checkout@v6

- name: Setup .NET
uses: actions/setup-dotnet@v5
with:
dotnet-version: 8.0.x

- name: Cache NuGet packages
uses: actions/cache@v5
with:
path: ~/.nuget/packages
key: nuget-${{ runner.os }}-${{ matrix.rid }}-${{ hashFiles('Windows/**/*.csproj') }}
restore-keys: nuget-${{ runner.os }}-${{ matrix.rid }}-

- name: Test Windows core
shell: pwsh
run: ./Scripts/build_windows.ps1 test

- name: Publish native tray
shell: pwsh
run: ./Scripts/build_windows.ps1 publish -Runtime ${{ matrix.rid }}

- name: Azure Login for Signing
if: startsWith(github.ref, 'refs/tags/v') && env.AZURE_TENANT_ID != '' && env.AZURE_CLIENT_ID != '' && env.AZURE_CLIENT_SECRET != '' && env.AZURE_SUBSCRIPTION_ID != ''
uses: azure/login@v3
with:
creds: '{"clientId":"${{ env.AZURE_CLIENT_ID }}","clientSecret":"${{ env.AZURE_CLIENT_SECRET }}","subscriptionId":"${{ env.AZURE_SUBSCRIPTION_ID }}","tenantId":"${{ env.AZURE_TENANT_ID }}"}'

- name: Sign tray executable
if: startsWith(github.ref, 'refs/tags/v') && env.AZURE_TENANT_ID != '' && env.AZURE_CLIENT_ID != '' && env.AZURE_CLIENT_SECRET != '' && env.AZURE_SUBSCRIPTION_ID != ''
uses: azure/trusted-signing-action@v2
with:
azure-tenant-id: ${{ env.AZURE_TENANT_ID }}
azure-client-id: ${{ env.AZURE_CLIENT_ID }}
azure-client-secret: ${{ env.AZURE_CLIENT_SECRET }}
endpoint: ${{ env.AZURE_SIGNING_ENDPOINT }}
signing-account-name: ${{ env.AZURE_SIGNING_ACCOUNT_NAME }}
certificate-profile-name: ${{ env.AZURE_CERTIFICATE_PROFILE_NAME }}
files-folder: publish/windows/${{ matrix.rid }}
files-folder-filter: exe
files-folder-recurse: true
file-digest: SHA256
timestamp-rfc3161: ${{ env.AZURE_TIMESTAMP_RFC3161 }}
timestamp-digest: SHA256

- name: Install Inno Setup
shell: pwsh
run: choco install innosetup --no-progress -y

- name: Build installer
shell: pwsh
run: ./Scripts/build_windows.ps1 installer -Runtime ${{ matrix.rid }} -NoPublish

- name: Sign installer
if: startsWith(github.ref, 'refs/tags/v') && env.AZURE_TENANT_ID != '' && env.AZURE_CLIENT_ID != '' && env.AZURE_CLIENT_SECRET != '' && env.AZURE_SUBSCRIPTION_ID != ''
uses: azure/trusted-signing-action@v2
with:
azure-tenant-id: ${{ env.AZURE_TENANT_ID }}
azure-client-id: ${{ env.AZURE_CLIENT_ID }}
azure-client-secret: ${{ env.AZURE_CLIENT_SECRET }}
endpoint: ${{ env.AZURE_SIGNING_ENDPOINT }}
signing-account-name: ${{ env.AZURE_SIGNING_ACCOUNT_NAME }}
certificate-profile-name: ${{ env.AZURE_CERTIFICATE_PROFILE_NAME }}
files-folder: Output
files-folder-filter: exe
files-folder-recurse: false
file-digest: SHA256
timestamp-rfc3161: ${{ env.AZURE_TIMESTAMP_RFC3161 }}
timestamp-digest: SHA256

- name: Upload Windows tray artifacts
uses: actions/upload-artifact@v7
with:
name: codexbar-windows-${{ matrix.rid }}
path: |
publish/windows/${{ matrix.rid }}/
Output/CodexBar-Setup-*.exe
160 changes: 160 additions & 0 deletions .github/workflows/release-cli.yml
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,166 @@ jobs:
${{ steps.pkg.outputs.out_dir }}/${{ steps.pkg.outputs.asset }}
${{ steps.pkg.outputs.out_dir }}/${{ steps.pkg.outputs.asset }}.sha256

build-windows:
strategy:
fail-fast: false
matrix:
include:
- rid: win-x64
arch: x64
runs-on: windows-latest
- rid: win-arm64
arch: arm64
runs-on: windows-11-arm
runs-on: ${{ matrix.runs-on }}
env:
RELEASE_TAG: ${{ inputs.tag || github.ref_name }}
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
AZURE_SIGNING_ENDPOINT: ${{ vars.AZURE_SIGNING_ENDPOINT || 'https://wus2.codesigning.azure.net/' }}
AZURE_SIGNING_ACCOUNT_NAME: ${{ vars.AZURE_SIGNING_ACCOUNT_NAME || 'hanselman' }}
AZURE_CERTIFICATE_PROFILE_NAME: ${{ vars.AZURE_CERTIFICATE_PROFILE_NAME || 'WindowsEdgeLight' }}
AZURE_TIMESTAMP_RFC3161: ${{ vars.AZURE_TIMESTAMP_RFC3161 || 'https://timestamp.acs.microsoft.com' }}
steps:
- uses: actions/checkout@v6

- name: Validate release tag
if: github.event_name == 'release' || inputs.tag != ''
shell: pwsh
run: |
if ([string]::IsNullOrWhiteSpace($env:RELEASE_TAG)) {
throw "Missing release tag."
}
if ($env:RELEASE_TAG -notmatch '^v[0-9A-Za-z._-]+$') {
throw "Invalid release tag: $env:RELEASE_TAG"
}

- name: Resolve Windows build version
id: winver
shell: pwsh
run: |
if ($env:RELEASE_TAG -match '^v[0-9A-Za-z._-]+$') {
$version = $env:RELEASE_TAG.TrimStart('v')
$assetSuffix = $env:RELEASE_TAG
} else {
$version = "0.0.0-dev"
$assetSuffix = ($env:RELEASE_TAG -replace '[^0-9A-Za-z._-]', '-')
}
"version=$version" >> $env:GITHUB_OUTPUT
"asset_suffix=$assetSuffix" >> $env:GITHUB_OUTPUT

- name: Require signing secrets for release assets
if: github.event_name == 'release' && (env.AZURE_TENANT_ID == '' || env.AZURE_CLIENT_ID == '' || env.AZURE_CLIENT_SECRET == '' || env.AZURE_SUBSCRIPTION_ID == '')
shell: pwsh
run: throw "Windows release assets require Azure Trusted Signing secrets."

- name: Setup .NET
uses: actions/setup-dotnet@v5
with:
dotnet-version: 8.0.x

- name: Cache NuGet packages
uses: actions/cache@v5
with:
path: ~/.nuget/packages
key: nuget-${{ runner.os }}-${{ matrix.rid }}-${{ hashFiles('Windows/**/*.csproj') }}
restore-keys: nuget-${{ runner.os }}-${{ matrix.rid }}-

- name: Test Windows core
shell: pwsh
run: ./Scripts/build_windows.ps1 test

- name: Publish native tray
shell: pwsh
run: ./Scripts/build_windows.ps1 publish -Runtime ${{ matrix.rid }} -Version ${{ steps.winver.outputs.version }}

- name: Azure Login for Signing
if: env.AZURE_TENANT_ID != '' && env.AZURE_CLIENT_ID != '' && env.AZURE_CLIENT_SECRET != '' && env.AZURE_SUBSCRIPTION_ID != ''
uses: azure/login@v3
with:
creds: '{"clientId":"${{ env.AZURE_CLIENT_ID }}","clientSecret":"${{ env.AZURE_CLIENT_SECRET }}","subscriptionId":"${{ env.AZURE_SUBSCRIPTION_ID }}","tenantId":"${{ env.AZURE_TENANT_ID }}"}'

- name: Sign tray executable
if: env.AZURE_TENANT_ID != '' && env.AZURE_CLIENT_ID != '' && env.AZURE_CLIENT_SECRET != '' && env.AZURE_SUBSCRIPTION_ID != ''
uses: azure/trusted-signing-action@v2
with:
azure-tenant-id: ${{ env.AZURE_TENANT_ID }}
azure-client-id: ${{ env.AZURE_CLIENT_ID }}
azure-client-secret: ${{ env.AZURE_CLIENT_SECRET }}
endpoint: ${{ env.AZURE_SIGNING_ENDPOINT }}
signing-account-name: ${{ env.AZURE_SIGNING_ACCOUNT_NAME }}
certificate-profile-name: ${{ env.AZURE_CERTIFICATE_PROFILE_NAME }}
files-folder: publish/windows/${{ matrix.rid }}
files-folder-filter: exe
files-folder-recurse: true
file-digest: SHA256
timestamp-rfc3161: ${{ env.AZURE_TIMESTAMP_RFC3161 }}
timestamp-digest: SHA256

- name: Install Inno Setup
shell: pwsh
run: choco install innosetup --no-progress -y

- name: Build installer
shell: pwsh
run: ./Scripts/build_windows.ps1 installer -Runtime ${{ matrix.rid }} -Version ${{ steps.winver.outputs.version }} -NoPublish

- name: Sign installer
if: env.AZURE_TENANT_ID != '' && env.AZURE_CLIENT_ID != '' && env.AZURE_CLIENT_SECRET != '' && env.AZURE_SUBSCRIPTION_ID != ''
uses: azure/trusted-signing-action@v2
with:
azure-tenant-id: ${{ env.AZURE_TENANT_ID }}
azure-client-id: ${{ env.AZURE_CLIENT_ID }}
azure-client-secret: ${{ env.AZURE_CLIENT_SECRET }}
endpoint: ${{ env.AZURE_SIGNING_ENDPOINT }}
signing-account-name: ${{ env.AZURE_SIGNING_ACCOUNT_NAME }}
certificate-profile-name: ${{ env.AZURE_CERTIFICATE_PROFILE_NAME }}
files-folder: Output
files-folder-filter: exe
files-folder-recurse: false
file-digest: SHA256
timestamp-rfc3161: ${{ env.AZURE_TIMESTAMP_RFC3161 }}
timestamp-digest: SHA256

- name: Package release assets
id: pkg
shell: pwsh
run: |
$zip = "CodexBarWindows-${{ steps.winver.outputs.asset_suffix }}-${{ matrix.rid }}.zip"
$installer = "CodexBar-Setup-${{ matrix.arch }}.exe"
Compress-Archive -Path "publish/windows/${{ matrix.rid }}/*" -DestinationPath $zip -Force
Get-FileHash -Algorithm SHA256 $zip | ForEach-Object { "$($_.Hash.ToLowerInvariant()) $zip" } | Set-Content "$zip.sha256"
Get-FileHash -Algorithm SHA256 "Output/$installer" | ForEach-Object { "$($_.Hash.ToLowerInvariant()) $installer" } | Set-Content "$installer.sha256"
"zip=$zip" >> $env:GITHUB_OUTPUT
"installer=Output/$installer" >> $env:GITHUB_OUTPUT
"installer_sha=$installer.sha256" >> $env:GITHUB_OUTPUT

- name: Upload release assets
if: github.event_name == 'release'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
shell: pwsh
run: |
gh release upload $env:RELEASE_TAG `
"${{ steps.pkg.outputs.zip }}" `
"${{ steps.pkg.outputs.zip }}.sha256" `
"${{ steps.pkg.outputs.installer }}" `
"${{ steps.pkg.outputs.installer_sha }}" `
--clobber

- name: Upload workflow artifact (manual runs)
if: github.event_name == 'workflow_dispatch'
uses: actions/upload-artifact@v6
with:
name: codexbar-windows-${{ matrix.rid }}
path: |
${{ steps.pkg.outputs.zip }}
${{ steps.pkg.outputs.zip }}.sha256
${{ steps.pkg.outputs.installer }}
${{ steps.pkg.outputs.installer_sha }}

update-homebrew-tap:
runs-on: ubuntu-latest
needs: build-cli
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ xcuserdata/
# Build products
.build/
DerivedData
Windows/**/bin/
Windows/**/obj/
publish/
Output/

# Bundles / artifacts
# Main app bundle (any variation)
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## 0.32.5 — Unreleased

### Added
- Windows: add a native notification-area companion with provider snapshot/command probes, self-contained publish targets, installer packaging, CI artifacts, and Azure Trusted Signing hooks.

### Fixed
- Menu bar: defer merged-menu close rebuilds and cache repeated menu-card height measurements so dismissing or rapidly switching the merged dropdown avoids rebuilding SwiftUI-backed cards on the main thread (#1274, #1286, #1314). Thanks @hhh2210!
- Menu bar: observe a compact icon-state signature so merged status icons no longer redraw for provider snapshot changes that cannot affect the visible icon (#1297). Thanks @hhh2210!
Expand Down
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ Or download release tarballs from GitHub Releases:
- macOS: `CodexBarCLI-v<tag>-macos-arm64.tar.gz`, `CodexBarCLI-v<tag>-macos-x86_64.tar.gz`
- Linux: `CodexBarCLI-v<tag>-linux-aarch64.tar.gz`, `CodexBarCLI-v<tag>-linux-x86_64.tar.gz`

### Windows native tray
CodexBar now has a native Windows notification-area companion with self-contained `win-x64` and `win-arm64` builds. It reads provider quota snapshots or command probes from `%APPDATA%\CodexBar\windows-settings.json`.

See [CodexBar for Windows](docs/windows.md) for build, installer, and probe configuration.

### First run
- Open Settings → Providers and enable what you use.
- Install/sign in to the provider sources you rely on: CLIs, browser sessions, OAuth/device flow, API keys, local app files, or provider apps depending on the provider.
Expand Down Expand Up @@ -214,8 +219,9 @@ CLI install:
- 🧳 [MCPorter](https://mcporter.dev) — TypeScript toolkit + CLI for Model Context Protocol servers.
- 🧿 [oracle](https://askoracle.dev) — Ask the oracle when you're stuck. Invoke GPT-5 Pro with a custom context and files.

## Looking for a Windows version?
- [Win-CodexBar](https://github.com/Finesssee/Win-CodexBar)
## Windows desktop integration
- Native CodexBar Windows tray companion: [docs/windows.md](docs/windows.md).
- Community app: [Win-CodexBar](https://github.com/Finesssee/Win-CodexBar).

## Linux desktop integration?
- [codexbar-waybar](https://github.com/Marouan-chak/codexbar-waybar) — Waybar custom module + GTK4 popover for Hyprland / Sway / other Wayland compositors, built on top of the bundled Linux CLI.
Expand Down
Loading