agentbridge-cli 0.1.12__tar.gz → 0.1.13__tar.gz
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.
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/.codex/skills/build-release/SKILL.md +13 -11
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/.github/workflows/release.yml +48 -52
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/PKG-INFO +18 -11
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/README.md +17 -10
- agentbridge_cli-0.1.13/agentbridge/_build_info.py +1 -0
- agentbridge_cli-0.1.13/design-qa/reference-inactive-menu-icons.png +0 -0
- agentbridge_cli-0.1.13/design-qa/reference-inactive-telegram-badge-normalized.png +0 -0
- agentbridge_cli-0.1.13/design-qa/reference-inactive-telegram-badge.png +0 -0
- agentbridge_cli-0.1.13/design-qa/reference-low-contrast-arrows-normalized.png +0 -0
- agentbridge_cli-0.1.13/design-qa/reference-low-contrast-arrows.png +0 -0
- agentbridge_cli-0.1.13/design-qa/reference-telegram-badge-normalized.png +0 -0
- agentbridge_cli-0.1.13/design-qa/reference-telegram-badge.png +0 -0
- agentbridge_cli-0.1.13/design-qa/status-item-active-workers.png +0 -0
- agentbridge_cli-0.1.13/design-qa/status-item-arrow-contrast-comparison.png +0 -0
- agentbridge_cli-0.1.13/design-qa/status-item-badge-comparison.png +0 -0
- agentbridge_cli-0.1.13/design-qa/status-item-inactive-comparison.png +0 -0
- agentbridge_cli-0.1.13/design-qa/status-item-no-workers.png +0 -0
- agentbridge_cli-0.1.13/design-qa.md +58 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/macos/README.md +4 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/macos/Sources/AgentBridgeMenuBar/AgentBridgeMenuBarApp.swift +19 -14
- agentbridge_cli-0.1.13/macos/Sources/AgentBridgeMenuBar/MenuBarStatusPresenter.swift +135 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/macos/Tests/AgentBridgeMenuBarTests/StatusModelsTests.swift +74 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/pyproject.toml +1 -1
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/scripts/build_macos_app.sh +4 -1
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/uv.lock +1 -1
- agentbridge_cli-0.1.12/agentbridge/_build_info.py +0 -1
- agentbridge_cli-0.1.12/design-qa.md +0 -43
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/.codex/config.toml +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/.codex/skills/build-release/agents/openai.yaml +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/.codex/skills/build-release/scripts/release_build.py +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/.env.example +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/.github/dependabot.yml +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/.github/workflows/ci.yml +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/.github/workflows/dependency-review.yml +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/.gitignore +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/AGENTS.md +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/CLAUDE.md +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/LICENSE +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/Makefile +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/TODO.txt +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/agentbridge/__init__.py +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/agentbridge/config.py +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/agentbridge/dashboard.py +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/agentbridge/models.py +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/agentbridge/pool.py +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/agentbridge/server.py +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/agentbridge/static/brand/android-chrome-192.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/agentbridge/static/brand/android-chrome-512.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/agentbridge/static/brand/apple-touch-icon.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/agentbridge/static/brand/favicon-16.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/agentbridge/static/brand/favicon-32.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/agentbridge/static/brand/favicon-48.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/agentbridge/static/brand/favicon.ico +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/agentbridge/static/brand/favicon.svg +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/agentbridge/static/brand/site.webmanifest +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/agentbridge/templates/dashboard/base.html +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/agentbridge/templates/dashboard/chat.html +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/agentbridge/templates/dashboard/detail.html +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/agentbridge/templates/dashboard/page.html +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/agentbridge/templates/dashboard/pool.html +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/agentbridge/templates/dashboard/requests.html +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/architecture.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/design-qa/agentbridge-popover-final.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/design-qa/agentbridge-popover-normalized.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/design-qa/agentbridge-popover-pass-1.jpeg +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/design-qa/implementation-header-activity.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/design-qa/reference-header-activity.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/design-qa/reference-panel.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/docs/plans/2026-02-14-dashboard-design.md +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/docs/plans/2026-02-14-dashboard-implementation.md +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/hatch_build.py +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/image-assets/apple-store/app-store-icon-1024.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/image-assets/icon/icon-1024.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/image-assets/logo/logo-1024.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/image-assets/manifest.json +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/image-assets/sources/icon-source-keyed.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/image-assets/sources/icon-source.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/image-assets/sources/logo-source-keyed.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/image-assets/sources/logo-source.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/image-assets/sources/social-background-source.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/image-assets/sources/social-source.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/image-assets/web-seo/android-chrome-192.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/image-assets/web-seo/android-chrome-512.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/image-assets/web-seo/apple-touch-icon.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/image-assets/web-seo/favicon/favicon-16.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/image-assets/web-seo/favicon/favicon-32.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/image-assets/web-seo/favicon/favicon-48.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/image-assets/web-seo/favicon/favicon.ico +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/image-assets/web-seo/favicon/favicon.svg +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/image-assets/web-seo/og-image-1200x630.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/image-assets/web-seo/site.webmanifest +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/logo.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/macos/Package.swift +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/macos/Sources/AgentBridgeMenuBar/BrandMark.swift +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/macos/Sources/AgentBridgeMenuBar/HealthClient.swift +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/macos/Sources/AgentBridgeMenuBar/ServerController.swift +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/macos/Sources/AgentBridgeMenuBar/ServerLauncher.swift +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/macos/Sources/AgentBridgeMenuBar/SettingsView.swift +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/macos/Sources/AgentBridgeMenuBar/StatusModels.swift +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/macos/Sources/AgentBridgeMenuBar/StatusPopover.swift +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/scripts/dashboard_load_test.py +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/scripts/verify_macos_app.sh +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/tests/fixtures/ocr_test_document.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/tests/test_config.py +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/tests/test_dashboard.py +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/tests/test_dependency_security.py +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/tests/test_image_utils.py +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/tests/test_model_mapping.py +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/tests/test_models.py +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/tests/test_pool.py +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/tests/test_server.py +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/tests/test_session_logger.py +0 -0
|
@@ -9,8 +9,7 @@ Use the repository-owned GitHub Actions workflow and PyPI Trusted Publishing.
|
|
|
9
9
|
Do not create a local tag or upload with local credentials: pushing a new
|
|
10
10
|
`pyproject.toml` version to `main` triggers `.github/workflows/release.yml`,
|
|
11
11
|
which tests, scans, builds, creates `v<version>`, creates the GitHub Release,
|
|
12
|
-
publishes `agentbridge-cli`, and
|
|
13
|
-
signed and notarized arm64 and x86_64 DMGs.
|
|
12
|
+
publishes `agentbridge-cli`, and attaches ad-hoc-signed arm64 and x86_64 DMGs.
|
|
14
13
|
|
|
15
14
|
Use the next unused patch version unless the user requests another valid,
|
|
16
15
|
unused semantic version. Treat an untagged version absent from PyPI as pending.
|
|
@@ -29,12 +28,8 @@ git log --oneline HEAD..origin/main
|
|
|
29
28
|
Stop on unrelated changes, unpublished commits, divergence, or another branch.
|
|
30
29
|
Do not clean, pull, commit unrelated files, or switch branches.
|
|
31
30
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
the macOS app: `APPLE_CERTIFICATE_BASE64`, `APPLE_CERTIFICATE_PASSWORD`,
|
|
35
|
-
`APPLE_KEYCHAIN_PASSWORD`, `APPLE_NOTARY_APPLE_ID`, `APPLE_NOTARY_PASSWORD`, and
|
|
36
|
-
`APPLE_TEAM_ID`. Secret names may be inspected with `gh secret list`; never read
|
|
37
|
-
or print secret values.
|
|
31
|
+
The macOS artifacts use an ad-hoc signature and are not notarized. They do not
|
|
32
|
+
require Apple Developer credentials or repository secrets.
|
|
38
33
|
|
|
39
34
|
2. Select the release version from the repository root:
|
|
40
35
|
|
|
@@ -59,8 +54,8 @@ The preflight requires macOS. It checks consistent project and lock metadata, an
|
|
|
59
54
|
unused PyPI version and tag, clean patch formatting, a frozen lock, Python 3.12
|
|
60
55
|
and 3.13 tests, Ruff, Swift tests, a self-contained app/DMG build for the current
|
|
61
56
|
Mac architecture, app signature and embedded-runtime smoke tests, wheel and
|
|
62
|
-
sdist audits, and a wheel installation smoke test. CI builds
|
|
63
|
-
|
|
57
|
+
sdist audits, and a wheel installation smoke test. CI builds both macOS
|
|
58
|
+
architectures with ad-hoc signatures. Stop at the exact failed gate.
|
|
64
59
|
|
|
65
60
|
4. Review and publish the release commit:
|
|
66
61
|
|
|
@@ -97,7 +92,7 @@ python3 .codex/skills/build-release/scripts/release_build.py wait-pypi --version
|
|
|
97
92
|
gh release view v<version> --json url,tagName,assets
|
|
98
93
|
```
|
|
99
94
|
|
|
100
|
-
|
|
95
|
+
Require both
|
|
101
96
|
`AgentBridge-<version>-macos-arm64.dmg` and
|
|
102
97
|
`AgentBridge-<version>-macos-x86_64.dmg` plus their `.sha256` files in the
|
|
103
98
|
release assets. Do not report success until PyPI returns exact-version files and
|
|
@@ -105,6 +100,13 @@ all required GitHub assets exist. If the workflow fails, inspect
|
|
|
105
100
|
`gh run view <run-id> --log-failed` and report the failed gate before considering
|
|
106
101
|
recovery.
|
|
107
102
|
|
|
103
|
+
If an existing release is missing only its macOS assets, attach them without
|
|
104
|
+
republishing Python distributions:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
gh workflow run release.yml --ref main -f attach_macos=true
|
|
108
|
+
```
|
|
109
|
+
|
|
108
110
|
## Final Response
|
|
109
111
|
|
|
110
112
|
Lead with the exact PyPI version URL. Report the tag, release workflow URL and
|
|
@@ -8,6 +8,11 @@ on:
|
|
|
8
8
|
required: false
|
|
9
9
|
default: false
|
|
10
10
|
type: boolean
|
|
11
|
+
attach_macos:
|
|
12
|
+
description: Build and attach macOS DMGs to the current version's existing release
|
|
13
|
+
required: false
|
|
14
|
+
default: false
|
|
15
|
+
type: boolean
|
|
11
16
|
push:
|
|
12
17
|
branches: [main]
|
|
13
18
|
paths:
|
|
@@ -200,8 +205,9 @@ jobs:
|
|
|
200
205
|
macos-app:
|
|
201
206
|
needs: [build]
|
|
202
207
|
if: >-
|
|
203
|
-
|
|
204
|
-
|
|
208
|
+
always() &&
|
|
209
|
+
(needs.build.outputs.publish == 'true' ||
|
|
210
|
+
(github.event_name == 'workflow_dispatch' && inputs.attach_macos == true))
|
|
205
211
|
strategy:
|
|
206
212
|
fail-fast: false
|
|
207
213
|
matrix:
|
|
@@ -225,57 +231,10 @@ jobs:
|
|
|
225
231
|
cache-dependency-glob: "uv.lock"
|
|
226
232
|
- name: Install Python 3.12
|
|
227
233
|
run: uv python install 3.12
|
|
228
|
-
- name:
|
|
229
|
-
env:
|
|
230
|
-
APPLE_CERTIFICATE_BASE64: ${{ secrets.APPLE_CERTIFICATE_BASE64 }}
|
|
231
|
-
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
|
232
|
-
KEYCHAIN_PASSWORD: ${{ secrets.APPLE_KEYCHAIN_PASSWORD }}
|
|
233
|
-
shell: bash
|
|
234
|
-
run: |
|
|
235
|
-
certificate_path="$RUNNER_TEMP/developer-id.p12"
|
|
236
|
-
keychain_path="$RUNNER_TEMP/agentbridge-signing.keychain-db"
|
|
237
|
-
echo "$APPLE_CERTIFICATE_BASE64" | base64 -D > "$certificate_path"
|
|
238
|
-
security create-keychain -p "$KEYCHAIN_PASSWORD" "$keychain_path"
|
|
239
|
-
security set-keychain-settings -lut 21600 "$keychain_path"
|
|
240
|
-
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$keychain_path"
|
|
241
|
-
security import "$certificate_path" \
|
|
242
|
-
-P "$APPLE_CERTIFICATE_PASSWORD" \
|
|
243
|
-
-A \
|
|
244
|
-
-t cert \
|
|
245
|
-
-f pkcs12 \
|
|
246
|
-
-k "$keychain_path"
|
|
247
|
-
security list-keychain -d user -s "$keychain_path"
|
|
248
|
-
security set-key-partition-list \
|
|
249
|
-
-S apple-tool:,apple: \
|
|
250
|
-
-s \
|
|
251
|
-
-k "$KEYCHAIN_PASSWORD" \
|
|
252
|
-
"$keychain_path"
|
|
253
|
-
identity="$(security find-identity -v -p codesigning "$keychain_path" \
|
|
254
|
-
| sed -n 's/.*"\(Developer ID Application:.*\)"/\1/p' \
|
|
255
|
-
| head -1)"
|
|
256
|
-
test -n "$identity"
|
|
257
|
-
echo "CODE_SIGN_IDENTITY=$identity" >> "$GITHUB_ENV"
|
|
258
|
-
- name: Build signed application
|
|
234
|
+
- name: Build ad-hoc-signed application
|
|
259
235
|
run: scripts/build_macos_app.sh
|
|
260
236
|
- name: Verify embedded application
|
|
261
237
|
run: scripts/verify_macos_app.sh "build/macos/${{ matrix.architecture }}/AgentBridge.app"
|
|
262
|
-
- name: Notarize and staple disk image
|
|
263
|
-
env:
|
|
264
|
-
APPLE_ID: ${{ secrets.APPLE_NOTARY_APPLE_ID }}
|
|
265
|
-
APPLE_APP_PASSWORD: ${{ secrets.APPLE_NOTARY_PASSWORD }}
|
|
266
|
-
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
|
267
|
-
shell: bash
|
|
268
|
-
run: |
|
|
269
|
-
dmg="$(find "build/macos/${{ matrix.architecture }}" -maxdepth 1 -name '*.dmg' -print -quit)"
|
|
270
|
-
test -n "$dmg"
|
|
271
|
-
xcrun notarytool submit "$dmg" \
|
|
272
|
-
--apple-id "$APPLE_ID" \
|
|
273
|
-
--password "$APPLE_APP_PASSWORD" \
|
|
274
|
-
--team-id "$APPLE_TEAM_ID" \
|
|
275
|
-
--wait
|
|
276
|
-
xcrun stapler staple "$dmg"
|
|
277
|
-
xcrun stapler validate "$dmg"
|
|
278
|
-
shasum -a 256 "$dmg" > "$dmg.sha256"
|
|
279
238
|
- name: Store macOS artifacts
|
|
280
239
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
281
240
|
with:
|
|
@@ -292,7 +251,7 @@ jobs:
|
|
|
292
251
|
always() &&
|
|
293
252
|
needs.build.outputs.publish == 'true' &&
|
|
294
253
|
needs.publish.result == 'success' &&
|
|
295
|
-
|
|
254
|
+
needs.macos-app.result == 'success'
|
|
296
255
|
runs-on: ubuntu-latest
|
|
297
256
|
timeout-minutes: 10
|
|
298
257
|
permissions:
|
|
@@ -304,7 +263,6 @@ jobs:
|
|
|
304
263
|
name: python-package-${{ github.sha }}
|
|
305
264
|
path: dist/
|
|
306
265
|
- name: Download macOS artifacts
|
|
307
|
-
if: needs.macos-app.result == 'success'
|
|
308
266
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
309
267
|
with:
|
|
310
268
|
pattern: macos-app-*-${{ github.sha }}
|
|
@@ -317,6 +275,44 @@ jobs:
|
|
|
317
275
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
318
276
|
TAG: ${{ needs.build.outputs.tag }}
|
|
319
277
|
|
|
278
|
+
attach-macos:
|
|
279
|
+
needs: [macos-app]
|
|
280
|
+
if: >-
|
|
281
|
+
always() &&
|
|
282
|
+
github.event_name == 'workflow_dispatch' &&
|
|
283
|
+
inputs.attach_macos == true &&
|
|
284
|
+
needs.macos-app.result == 'success'
|
|
285
|
+
runs-on: ubuntu-latest
|
|
286
|
+
timeout-minutes: 10
|
|
287
|
+
permissions:
|
|
288
|
+
contents: write
|
|
289
|
+
steps:
|
|
290
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
291
|
+
with:
|
|
292
|
+
persist-credentials: false
|
|
293
|
+
- name: Download macOS artifacts
|
|
294
|
+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
295
|
+
with:
|
|
296
|
+
pattern: macos-app-*-${{ github.sha }}
|
|
297
|
+
path: dist/
|
|
298
|
+
merge-multiple: true
|
|
299
|
+
- name: Attach DMGs to the existing release
|
|
300
|
+
shell: bash
|
|
301
|
+
run: |
|
|
302
|
+
version="$(python - <<'PY'
|
|
303
|
+
import tomllib
|
|
304
|
+
|
|
305
|
+
with open("pyproject.toml", "rb") as pyproject_file:
|
|
306
|
+
print(tomllib.load(pyproject_file)["project"]["version"])
|
|
307
|
+
PY
|
|
308
|
+
)"
|
|
309
|
+
tag="v${version}"
|
|
310
|
+
gh release view "$tag" >/dev/null
|
|
311
|
+
gh release upload "$tag" dist/* --clobber
|
|
312
|
+
env:
|
|
313
|
+
GH_REPO: ${{ github.repository }}
|
|
314
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
315
|
+
|
|
320
316
|
publish:
|
|
321
317
|
needs: [build]
|
|
322
318
|
if: needs.build.outputs.publish == 'true'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: agentbridge-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.13
|
|
4
4
|
Summary: Bridge OpenAI tools to Claude Code SDK, Codex CLI, and OpenRouter
|
|
5
5
|
Project-URL: Homepage, https://github.com/tsilva/agentbridge
|
|
6
6
|
Project-URL: Repository, https://github.com/tsilva/agentbridge
|
|
@@ -46,9 +46,14 @@ It supports streaming and non-streaming responses, image and PDF inputs where th
|
|
|
46
46
|
|
|
47
47
|
### macOS menu-bar app
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
Download the DMG for your Mac architecture from the GitHub Release, drag
|
|
50
|
+
`AgentBridge.app` to Applications, and open it. The app is ad-hoc signed rather
|
|
51
|
+
than Apple-notarized, so on first launch macOS may require you to Control-click
|
|
52
|
+
the app, choose **Open**, and confirm.
|
|
53
|
+
|
|
54
|
+
You can also build the app locally on macOS 13 or later. This requires
|
|
55
|
+
[uv](https://docs.astral.sh/uv/) and the Xcode Command Line Tools, but not an
|
|
56
|
+
Apple Developer account:
|
|
52
57
|
|
|
53
58
|
```bash
|
|
54
59
|
git clone https://github.com/tsilva/agentbridge.git
|
|
@@ -57,10 +62,10 @@ scripts/build_macos_app.sh
|
|
|
57
62
|
open "build/macos/$(uname -m)/AgentBridge-"*.dmg
|
|
58
63
|
```
|
|
59
64
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
65
|
+
The menu-bar window shows live server health and activity, starts and stops the
|
|
66
|
+
local server, opens the dashboard, and provides settings for the port, worker
|
|
67
|
+
count, and launch at login. See [`macos/README.md`](macos/README.md) for
|
|
68
|
+
development instructions.
|
|
64
69
|
|
|
65
70
|
The app contains its own Python 3.12 runtime and AgentBridge dependencies. It
|
|
66
71
|
does not bundle the Claude Code or Codex executables: installed provider tools
|
|
@@ -179,9 +184,11 @@ uv build # build wheel and source d
|
|
|
179
184
|
Releases use the `Release` GitHub Actions workflow and PyPI Trusted Publishing
|
|
180
185
|
for the `agentbridge-cli` project. The publisher is scoped to owner `tsilva`,
|
|
181
186
|
repository `agentbridge`, workflow `release.yml`, and environment `pypi`; no
|
|
182
|
-
PyPI API token is required. GitHub Releases contain the Python distributions
|
|
183
|
-
|
|
184
|
-
|
|
187
|
+
PyPI API token is required. GitHub Releases contain the Python distributions
|
|
188
|
+
plus ad-hoc-signed arm64 and x86_64 macOS DMGs and SHA-256 checksums. These DMGs
|
|
189
|
+
are not Apple-notarized and require no Apple Developer credentials. Releases
|
|
190
|
+
through `0.1.10` remain available under the previous `agentbridge-py`
|
|
191
|
+
distribution name.
|
|
185
192
|
|
|
186
193
|
## Architecture
|
|
187
194
|
|
|
@@ -14,9 +14,14 @@ It supports streaming and non-streaming responses, image and PDF inputs where th
|
|
|
14
14
|
|
|
15
15
|
### macOS menu-bar app
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
Download the DMG for your Mac architecture from the GitHub Release, drag
|
|
18
|
+
`AgentBridge.app` to Applications, and open it. The app is ad-hoc signed rather
|
|
19
|
+
than Apple-notarized, so on first launch macOS may require you to Control-click
|
|
20
|
+
the app, choose **Open**, and confirm.
|
|
21
|
+
|
|
22
|
+
You can also build the app locally on macOS 13 or later. This requires
|
|
23
|
+
[uv](https://docs.astral.sh/uv/) and the Xcode Command Line Tools, but not an
|
|
24
|
+
Apple Developer account:
|
|
20
25
|
|
|
21
26
|
```bash
|
|
22
27
|
git clone https://github.com/tsilva/agentbridge.git
|
|
@@ -25,10 +30,10 @@ scripts/build_macos_app.sh
|
|
|
25
30
|
open "build/macos/$(uname -m)/AgentBridge-"*.dmg
|
|
26
31
|
```
|
|
27
32
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
33
|
+
The menu-bar window shows live server health and activity, starts and stops the
|
|
34
|
+
local server, opens the dashboard, and provides settings for the port, worker
|
|
35
|
+
count, and launch at login. See [`macos/README.md`](macos/README.md) for
|
|
36
|
+
development instructions.
|
|
32
37
|
|
|
33
38
|
The app contains its own Python 3.12 runtime and AgentBridge dependencies. It
|
|
34
39
|
does not bundle the Claude Code or Codex executables: installed provider tools
|
|
@@ -147,9 +152,11 @@ uv build # build wheel and source d
|
|
|
147
152
|
Releases use the `Release` GitHub Actions workflow and PyPI Trusted Publishing
|
|
148
153
|
for the `agentbridge-cli` project. The publisher is scoped to owner `tsilva`,
|
|
149
154
|
repository `agentbridge`, workflow `release.yml`, and environment `pypi`; no
|
|
150
|
-
PyPI API token is required. GitHub Releases contain the Python distributions
|
|
151
|
-
|
|
152
|
-
|
|
155
|
+
PyPI API token is required. GitHub Releases contain the Python distributions
|
|
156
|
+
plus ad-hoc-signed arm64 and x86_64 macOS DMGs and SHA-256 checksums. These DMGs
|
|
157
|
+
are not Apple-notarized and require no Apple Developer credentials. Releases
|
|
158
|
+
through `0.1.10` remain available under the previous `agentbridge-py`
|
|
159
|
+
distribution name.
|
|
153
160
|
|
|
154
161
|
## Architecture
|
|
155
162
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
GIT_HASH = "6918a70"
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Active-worker menu-bar badge and inactive brightness design QA
|
|
2
|
+
|
|
3
|
+
## Evidence
|
|
4
|
+
|
|
5
|
+
- Source visual truths:
|
|
6
|
+
- Badge proportions: `/var/folders/wz/x29jb7_x5rdc_5dcjr4qnhg00000gn/T/codex-clipboard-a88e73a8-8b49-415b-b2ef-9f289e13c5ea.png`
|
|
7
|
+
- Unfocused menu-bar brightness: `/var/folders/wz/x29jb7_x5rdc_5dcjr4qnhg00000gn/T/codex-clipboard-6c362e7b-d087-493b-8d8f-683607e7a420.png`
|
|
8
|
+
- Inner-arrow contrast: `/var/folders/wz/x29jb7_x5rdc_5dcjr4qnhg00000gn/T/codex-clipboard-7fb76339-7c5e-4568-98cf-3c91d8ad0f48.png`
|
|
9
|
+
- Source pixels: 208 × 62 for the badge reference, 3232 × 76 for the unfocused menu-bar reference, and 260 × 58 for the inner-arrow reference, all at Retina density. Focused item crops retain their original 2× scale.
|
|
10
|
+
- Implementation screenshots:
|
|
11
|
+
- Active state: `/Users/tsilva/repos/tsilva/agentbridge/design-qa/status-item-active-workers.png`
|
|
12
|
+
- Zero state: `/Users/tsilva/repos/tsilva/agentbridge/design-qa/status-item-no-workers.png`
|
|
13
|
+
- Implementation pixels: 96 × 62 for a 48 × 31-point native AppKit fixture at 2× density.
|
|
14
|
+
- Viewport: native macOS status-item button, 32 × 22 points inside a 48 × 31-point menu-bar crop.
|
|
15
|
+
- Density normalization: the 64 × 62 source crop was centered on a 96 × 62 canvas without scaling so both sides retain 2× density.
|
|
16
|
+
- State: dark, unfocused-style menu bar with 48 active workers for visual fidelity; running server with zero active workers for empty-state verification.
|
|
17
|
+
- Combined comparisons:
|
|
18
|
+
- Badge proportions: `/Users/tsilva/repos/tsilva/agentbridge/design-qa/status-item-badge-comparison.png`
|
|
19
|
+
- Unfocused brightness: `/Users/tsilva/repos/tsilva/agentbridge/design-qa/status-item-inactive-comparison.png`
|
|
20
|
+
- Inner-arrow contrast before/after: `/Users/tsilva/repos/tsilva/agentbridge/design-qa/status-item-arrow-contrast-comparison.png`
|
|
21
|
+
|
|
22
|
+
## Findings
|
|
23
|
+
|
|
24
|
+
- No actionable P0/P1/P2 differences remain.
|
|
25
|
+
- Fonts and typography: the badge uses a 7.5-point semibold native system font; the two-digit weight and density match the compact Telegram reference without crowding.
|
|
26
|
+
- Spacing and layout rhythm: the 11-point capsule sits at the lower trailing edge of the existing icon, with the source-like overlap and enough of AgentBridge's mark remaining visible.
|
|
27
|
+
- Colors and visual tokens: the badge uses the current semantic label color at 48% opacity and automatically reverses its number color for light and dark appearances. The main mark uses an appearance-aware label-color palette without template dimming, keeping it bright on an unfocused display like Telegram. Filled status symbols now use a contrasting secondary palette layer: translucent black arrows in dark appearance and translucent white detail in light appearance.
|
|
28
|
+
- Image quality and asset fidelity: the existing SF Symbol remains native but is rendered as a non-template, appearance-aware image so AppKit does not mute it with inactive template tinting. The badge is rendered natively at Retina density with no raster replacement or custom icon asset.
|
|
29
|
+
- Copy and content: the visible badge is the exact live active-worker number. The button tooltip and accessibility label include the same count with correct singular/plural wording.
|
|
30
|
+
- Empty state: the zero-state rendering shows only the AgentBridge icon; no empty capsule or numeric badge remains.
|
|
31
|
+
|
|
32
|
+
The full menu-bar reference establishes the inactive-display context and Telegram's brighter mark. The component capture is the complete implementation viewport for this isolated status item. Focused comparisons confirm capsule size, opacity, number weight, overlap, the brighter main-icon treatment, and clear separation between the outer circle and inner arrows.
|
|
33
|
+
|
|
34
|
+
## Comparison History
|
|
35
|
+
|
|
36
|
+
1. The first active-state rendering used a 14-point badge with wider padding. It obscured most of the circular AgentBridge symbol and was visibly larger and lighter than the Telegram badge.
|
|
37
|
+
2. The badge was reduced to 11 points, its type and horizontal padding were tightened, and label opacity was lowered from 68% to 48%. The revised combined comparison shows matching compact proportions and tone, while the zero-state capture confirms complete badge removal.
|
|
38
|
+
3. The inactive-display reference showed AppKit's template tint making AgentBridge materially dimmer than Telegram. The symbol now uses an appearance-aware palette and is explicitly non-template; the revised dark-appearance capture shows the bright main mark while retaining the subdued badge.
|
|
39
|
+
4. The first bright non-template render gave the circle and arrows nearly identical light values, making the arrows hard to perceive. Filled symbols now use a two-color palette with 68%-opaque black inner detail in dark appearance; the before/after comparison shows clear arrow contrast. Outline-only stopped and transitional symbols retain a single semantic label color so they remain visible.
|
|
40
|
+
|
|
41
|
+
## Implementation Checklist
|
|
42
|
+
|
|
43
|
+
- [x] Observe live health-count changes in addition to server-phase changes.
|
|
44
|
+
- [x] Show the exact active-worker count in a compact native badge.
|
|
45
|
+
- [x] Hide the badge at zero.
|
|
46
|
+
- [x] Keep the main icon bright instead of accepting inactive template dimming.
|
|
47
|
+
- [x] Preserve appearance-aware light/dark coloring.
|
|
48
|
+
- [x] Give filled symbols contrasting inner detail without darkening outline-only states.
|
|
49
|
+
- [x] Preserve status-item click behavior.
|
|
50
|
+
- [x] Include the count in tooltip and accessibility text.
|
|
51
|
+
- [x] Verify active and zero states with Retina component captures.
|
|
52
|
+
- [x] Pass Swift unit tests and a release build.
|
|
53
|
+
|
|
54
|
+
## Follow-up Polish
|
|
55
|
+
|
|
56
|
+
- None.
|
|
57
|
+
|
|
58
|
+
final result: passed
|
|
@@ -38,6 +38,10 @@ require an Apple Developer account.
|
|
|
38
38
|
|
|
39
39
|
Open the generated DMG and drag `AgentBridge.app` to Applications.
|
|
40
40
|
|
|
41
|
+
GitHub Releases provide the same ad-hoc-signed DMG for arm64 and x86_64 Macs.
|
|
42
|
+
They are not Apple-notarized, so macOS may require you to Control-click the app,
|
|
43
|
+
choose **Open**, and confirm on first launch.
|
|
44
|
+
|
|
41
45
|
Mutable data stays outside the application bundle:
|
|
42
46
|
|
|
43
47
|
```text
|
|
@@ -26,8 +26,9 @@ struct AgentBridgeMenuBarApp: App {
|
|
|
26
26
|
final class AgentBridgeAppDelegate: NSObject, NSApplicationDelegate {
|
|
27
27
|
private let controller = ServerController()
|
|
28
28
|
private let popover = NSPopover()
|
|
29
|
+
private let statusPresenter = MenuBarStatusPresenter()
|
|
29
30
|
private var statusItem: NSStatusItem?
|
|
30
|
-
private var
|
|
31
|
+
private var statusObservation: AnyCancellable?
|
|
31
32
|
#if DEBUG
|
|
32
33
|
private var debugAnchorWindow: NSWindow?
|
|
33
34
|
#endif
|
|
@@ -39,18 +40,26 @@ final class AgentBridgeAppDelegate: NSObject, NSApplicationDelegate {
|
|
|
39
40
|
if let button = item.button {
|
|
40
41
|
button.target = self
|
|
41
42
|
button.action = #selector(togglePopover(_:))
|
|
43
|
+
statusPresenter.install(on: button)
|
|
44
|
+
statusPresenter.update(
|
|
45
|
+
button: button,
|
|
46
|
+
phase: controller.phase,
|
|
47
|
+
activeWorkers: controller.activeRequests
|
|
48
|
+
)
|
|
42
49
|
}
|
|
43
|
-
updateStatusItem(for: controller.phase)
|
|
44
50
|
|
|
45
51
|
let host = NSHostingController(rootView: StatusPopover(controller: controller))
|
|
46
52
|
popover.contentViewController = host
|
|
47
53
|
popover.behavior = .transient
|
|
48
54
|
popover.animates = true
|
|
49
55
|
|
|
50
|
-
|
|
56
|
+
statusObservation = Publishers.CombineLatest(controller.$phase, controller.$health)
|
|
51
57
|
.receive(on: RunLoop.main)
|
|
52
|
-
.sink { [weak self] phase in
|
|
53
|
-
self?.updateStatusItem(
|
|
58
|
+
.sink { [weak self] phase, health in
|
|
59
|
+
self?.updateStatusItem(
|
|
60
|
+
for: phase,
|
|
61
|
+
activeWorkers: health?.activeRequests ?? 0
|
|
62
|
+
)
|
|
54
63
|
}
|
|
55
64
|
|
|
56
65
|
controller.begin()
|
|
@@ -84,17 +93,13 @@ final class AgentBridgeAppDelegate: NSObject, NSApplicationDelegate {
|
|
|
84
93
|
}
|
|
85
94
|
}
|
|
86
95
|
|
|
87
|
-
private func updateStatusItem(for phase: ServerPhase) {
|
|
96
|
+
private func updateStatusItem(for phase: ServerPhase, activeWorkers: Int) {
|
|
88
97
|
guard let button = statusItem?.button else { return }
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
98
|
+
statusPresenter.update(
|
|
99
|
+
button: button,
|
|
100
|
+
phase: phase,
|
|
101
|
+
activeWorkers: activeWorkers
|
|
93
102
|
)
|
|
94
|
-
image?.isTemplate = true
|
|
95
|
-
button.image = image
|
|
96
|
-
button.toolTip = label
|
|
97
|
-
button.setAccessibilityLabel(label)
|
|
98
103
|
}
|
|
99
104
|
|
|
100
105
|
#if DEBUG
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import AppKit
|
|
2
|
+
|
|
3
|
+
@MainActor
|
|
4
|
+
final class MenuBarStatusPresenter {
|
|
5
|
+
let badgeView = MenuBarActivityBadgeView()
|
|
6
|
+
|
|
7
|
+
func install(on button: NSStatusBarButton) {
|
|
8
|
+
guard badgeView.superview !== button else { return }
|
|
9
|
+
|
|
10
|
+
badgeView.translatesAutoresizingMaskIntoConstraints = false
|
|
11
|
+
button.addSubview(badgeView)
|
|
12
|
+
NSLayoutConstraint.activate([
|
|
13
|
+
badgeView.trailingAnchor.constraint(equalTo: button.trailingAnchor),
|
|
14
|
+
badgeView.bottomAnchor.constraint(equalTo: button.bottomAnchor, constant: 2),
|
|
15
|
+
badgeView.heightAnchor.constraint(equalToConstant: 11),
|
|
16
|
+
])
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
func update(
|
|
20
|
+
button: NSStatusBarButton,
|
|
21
|
+
phase: ServerPhase,
|
|
22
|
+
activeWorkers: Int
|
|
23
|
+
) {
|
|
24
|
+
let count = max(activeWorkers, 0)
|
|
25
|
+
let label = Self.accessibilityLabel(for: phase, activeWorkers: count)
|
|
26
|
+
let baseImage = NSImage(
|
|
27
|
+
systemSymbolName: phase.menuBarSymbol,
|
|
28
|
+
accessibilityDescription: label
|
|
29
|
+
)
|
|
30
|
+
let appearance = button.effectiveAppearance.bestMatch(from: [.darkAqua, .aqua])
|
|
31
|
+
let symbolDetailColor = appearance == .darkAqua
|
|
32
|
+
? NSColor.black.withAlphaComponent(0.68)
|
|
33
|
+
: NSColor.white.withAlphaComponent(0.88)
|
|
34
|
+
let paletteColors: [NSColor]
|
|
35
|
+
switch phase {
|
|
36
|
+
case .runningManaged, .runningExternal, .conflict, .failed:
|
|
37
|
+
paletteColors = [symbolDetailColor, .labelColor]
|
|
38
|
+
case .starting, .stopping, .stopped:
|
|
39
|
+
paletteColors = [.labelColor]
|
|
40
|
+
}
|
|
41
|
+
let colorConfiguration = NSImage.SymbolConfiguration(
|
|
42
|
+
paletteColors: paletteColors
|
|
43
|
+
)
|
|
44
|
+
let image = baseImage?.withSymbolConfiguration(colorConfiguration) ?? baseImage
|
|
45
|
+
image?.isTemplate = false
|
|
46
|
+
button.image = image
|
|
47
|
+
button.toolTip = label
|
|
48
|
+
button.setAccessibilityLabel(label)
|
|
49
|
+
badgeView.update(count: count)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
static func accessibilityLabel(for phase: ServerPhase, activeWorkers: Int) -> String {
|
|
53
|
+
guard activeWorkers > 0 else { return "AgentBridge: \(phase.label)" }
|
|
54
|
+
let noun = activeWorkers == 1 ? "worker" : "workers"
|
|
55
|
+
return "AgentBridge: \(phase.label), \(activeWorkers) active \(noun)"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@MainActor
|
|
60
|
+
final class MenuBarActivityBadgeView: NSView {
|
|
61
|
+
private let countLabel = NSTextField(labelWithString: "")
|
|
62
|
+
|
|
63
|
+
private(set) var displayedCount: Int?
|
|
64
|
+
|
|
65
|
+
override init(frame frameRect: NSRect) {
|
|
66
|
+
super.init(frame: frameRect)
|
|
67
|
+
configureView()
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
required init?(coder: NSCoder) {
|
|
71
|
+
super.init(coder: coder)
|
|
72
|
+
configureView()
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
override var intrinsicContentSize: NSSize {
|
|
76
|
+
let labelWidth = ceil(countLabel.intrinsicContentSize.width)
|
|
77
|
+
return NSSize(width: max(11, labelWidth + 1.5), height: 11)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
override func hitTest(_ point: NSPoint) -> NSView? {
|
|
81
|
+
nil
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
override func viewDidChangeEffectiveAppearance() {
|
|
85
|
+
super.viewDidChangeEffectiveAppearance()
|
|
86
|
+
updateColors()
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
func update(count: Int) {
|
|
90
|
+
guard count > 0 else {
|
|
91
|
+
displayedCount = nil
|
|
92
|
+
countLabel.stringValue = ""
|
|
93
|
+
isHidden = true
|
|
94
|
+
invalidateIntrinsicContentSize()
|
|
95
|
+
return
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
displayedCount = count
|
|
99
|
+
countLabel.stringValue = String(count)
|
|
100
|
+
isHidden = false
|
|
101
|
+
invalidateIntrinsicContentSize()
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
private func configureView() {
|
|
105
|
+
wantsLayer = true
|
|
106
|
+
layer?.cornerRadius = 5.5
|
|
107
|
+
layer?.cornerCurve = .continuous
|
|
108
|
+
|
|
109
|
+
countLabel.translatesAutoresizingMaskIntoConstraints = false
|
|
110
|
+
countLabel.font = .systemFont(ofSize: 7.5, weight: .semibold)
|
|
111
|
+
countLabel.alignment = .center
|
|
112
|
+
countLabel.lineBreakMode = .byClipping
|
|
113
|
+
countLabel.maximumNumberOfLines = 1
|
|
114
|
+
addSubview(countLabel)
|
|
115
|
+
|
|
116
|
+
NSLayoutConstraint.activate([
|
|
117
|
+
countLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0.75),
|
|
118
|
+
countLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -0.75),
|
|
119
|
+
countLabel.centerYAnchor.constraint(equalTo: centerYAnchor),
|
|
120
|
+
])
|
|
121
|
+
|
|
122
|
+
setAccessibilityElement(false)
|
|
123
|
+
isHidden = true
|
|
124
|
+
updateColors()
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
private func updateColors() {
|
|
128
|
+
let match = effectiveAppearance.bestMatch(from: [.darkAqua, .aqua])
|
|
129
|
+
let usesDarkMenuBar = match == .darkAqua
|
|
130
|
+
layer?.backgroundColor = NSColor.labelColor.withAlphaComponent(0.48).cgColor
|
|
131
|
+
countLabel.textColor = usesDarkMenuBar
|
|
132
|
+
? NSColor.black.withAlphaComponent(0.78)
|
|
133
|
+
: NSColor.white.withAlphaComponent(0.94)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import AppKit
|
|
1
2
|
import Foundation
|
|
2
3
|
import XCTest
|
|
3
4
|
@testable import AgentBridgeMenuBar
|
|
@@ -71,4 +72,77 @@ final class StatusModelsTests: XCTestCase {
|
|
|
71
72
|
XCTAssertTrue(paths.contains("/Users/example/.local/bin"))
|
|
72
73
|
XCTAssertTrue(paths.contains("/Users/example/.npm-global/bin"))
|
|
73
74
|
}
|
|
75
|
+
|
|
76
|
+
@MainActor
|
|
77
|
+
func testMenuBarBadgeIsHiddenWithoutActiveWorkers() throws {
|
|
78
|
+
let (canvas, button, presenter) = makeMenuBarFixture(activeWorkers: 0)
|
|
79
|
+
|
|
80
|
+
XCTAssertTrue(presenter.badgeView.isHidden)
|
|
81
|
+
XCTAssertNil(presenter.badgeView.displayedCount)
|
|
82
|
+
XCTAssertEqual(button.toolTip, "AgentBridge: Running")
|
|
83
|
+
|
|
84
|
+
try writeSnapshot(
|
|
85
|
+
of: canvas,
|
|
86
|
+
requestedBy: "AGENTBRIDGE_STATUS_ITEM_ZERO_SNAPSHOT_PATH"
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
@MainActor
|
|
91
|
+
func testMenuBarBadgeShowsActiveWorkerCount() throws {
|
|
92
|
+
let (canvas, button, presenter) = makeMenuBarFixture(activeWorkers: 48)
|
|
93
|
+
|
|
94
|
+
XCTAssertFalse(presenter.badgeView.isHidden)
|
|
95
|
+
XCTAssertEqual(presenter.badgeView.displayedCount, 48)
|
|
96
|
+
XCTAssertEqual(button.toolTip, "AgentBridge: Running, 48 active workers")
|
|
97
|
+
XCTAssertEqual(button.image?.isTemplate, false)
|
|
98
|
+
|
|
99
|
+
try writeSnapshot(
|
|
100
|
+
of: canvas,
|
|
101
|
+
requestedBy: "AGENTBRIDGE_STATUS_ITEM_SNAPSHOT_PATH"
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
@MainActor
|
|
106
|
+
private func makeMenuBarFixture(
|
|
107
|
+
activeWorkers: Int
|
|
108
|
+
) -> (NSView, NSStatusBarButton, MenuBarStatusPresenter) {
|
|
109
|
+
let canvas = NSView(frame: NSRect(x: 0, y: 0, width: 48, height: 31))
|
|
110
|
+
canvas.appearance = NSAppearance(named: .darkAqua)
|
|
111
|
+
canvas.wantsLayer = true
|
|
112
|
+
canvas.layer?.backgroundColor = NSColor(
|
|
113
|
+
calibratedRed: 0.01,
|
|
114
|
+
green: 0.22,
|
|
115
|
+
blue: 0.19,
|
|
116
|
+
alpha: 1
|
|
117
|
+
).cgColor
|
|
118
|
+
|
|
119
|
+
let button = NSStatusBarButton(frame: NSRect(x: 8, y: 4, width: 32, height: 22))
|
|
120
|
+
button.isBordered = false
|
|
121
|
+
canvas.addSubview(button)
|
|
122
|
+
let presenter = MenuBarStatusPresenter()
|
|
123
|
+
presenter.install(on: button)
|
|
124
|
+
presenter.update(
|
|
125
|
+
button: button,
|
|
126
|
+
phase: .runningManaged,
|
|
127
|
+
activeWorkers: activeWorkers
|
|
128
|
+
)
|
|
129
|
+
return (canvas, button, presenter)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
@MainActor
|
|
133
|
+
private func writeSnapshot(of view: NSView, requestedBy environmentKey: String) throws {
|
|
134
|
+
guard let snapshotPath = ProcessInfo.processInfo.environment[environmentKey] else {
|
|
135
|
+
return
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
view.layoutSubtreeIfNeeded()
|
|
139
|
+
let representation = try XCTUnwrap(
|
|
140
|
+
view.bitmapImageRepForCachingDisplay(in: view.bounds)
|
|
141
|
+
)
|
|
142
|
+
view.cacheDisplay(in: view.bounds, to: representation)
|
|
143
|
+
let data = try XCTUnwrap(
|
|
144
|
+
representation.representation(using: .png, properties: [:])
|
|
145
|
+
)
|
|
146
|
+
try data.write(to: URL(fileURLWithPath: snapshotPath), options: .atomic)
|
|
147
|
+
}
|
|
74
148
|
}
|
|
@@ -150,7 +150,10 @@ hdiutil create \
|
|
|
150
150
|
-ov \
|
|
151
151
|
"${dmg_path}" >/dev/null
|
|
152
152
|
|
|
153
|
-
|
|
153
|
+
(
|
|
154
|
+
cd "$(dirname "${dmg_path}")"
|
|
155
|
+
shasum -a 256 "$(basename "${dmg_path}")" > "$(basename "${dmg_path}").sha256"
|
|
156
|
+
)
|
|
154
157
|
|
|
155
158
|
echo "Application: ${app_path}"
|
|
156
159
|
echo "Disk image: ${dmg_path}"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
GIT_HASH = "738d069"
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
# AgentBridge refresh-footer design QA
|
|
2
|
-
|
|
3
|
-
## Evidence
|
|
4
|
-
|
|
5
|
-
- Source visual truth: right-hand footer in `/var/folders/wz/x29jb7_x5rdc_5dcjr4qnhg00000gn/T/codex-clipboard-7372d7c5-9ea8-42d9-9f84-41b99c9c9e24.png`
|
|
6
|
-
- Source pixels: 1540 × 388 at Retina density
|
|
7
|
-
- Implementation: `/Applications/AgentBridge.app`
|
|
8
|
-
- Implementation screenshot: pending user-provided capture
|
|
9
|
-
- Viewport: native macOS status popover, 350-point content width, Retina display
|
|
10
|
-
- State to capture: running server with footer showing `Updated now`
|
|
11
|
-
|
|
12
|
-
## Findings
|
|
13
|
-
|
|
14
|
-
- [Blocked] The revised app is built, installed, and running, but post-fix visual evidence is pending because the user requested that Computer Use not be used and will provide screenshots directly.
|
|
15
|
-
- Functional structure: the icon and update label are now one plain clickable refresh control with an accessible combined label.
|
|
16
|
-
- Fonts and typography: the update label now uses 13-point medium system text to match the reference's optical weight.
|
|
17
|
-
- Spacing and layout rhythm: icon-to-label spacing is reduced from 12 to 9 points and the symbol is constrained to a 16-point slot, matching the compact reference grouping.
|
|
18
|
-
- Colors and visual tokens: icon and label share the same semantic secondary-label color.
|
|
19
|
-
- Image quality and assets: the refresh mark uses the native `arrow.clockwise` SF Symbol at 15-point regular weight; no custom-drawn asset is used.
|
|
20
|
-
- Copy and content: relative update copy is unchanged and continues to show `Updated now` immediately after refresh.
|
|
21
|
-
|
|
22
|
-
## Implementation Checklist
|
|
23
|
-
|
|
24
|
-
- [x] Match the reference refresh symbol family.
|
|
25
|
-
- [x] Match icon size and regular stroke weight.
|
|
26
|
-
- [x] Tighten icon-to-label spacing.
|
|
27
|
-
- [x] Increase label optical weight to medium.
|
|
28
|
-
- [x] Apply one muted semantic color to the icon and copy.
|
|
29
|
-
- [x] Preserve refresh interaction and improve its accessibility label.
|
|
30
|
-
- [x] Run Swift tests and patch-format validation.
|
|
31
|
-
- [x] Build, sign, verify, install, and launch the updated app.
|
|
32
|
-
- [ ] Compare a user-provided post-fix screenshot against the right-hand reference.
|
|
33
|
-
|
|
34
|
-
## Comparison History
|
|
35
|
-
|
|
36
|
-
1. Source evidence showed the implementation label lighter than the target and the icon-to-label gap materially wider; the refresh icon also read smaller and less balanced.
|
|
37
|
-
2. The implementation now uses a 15-point regular symbol in a 16-point slot, 9-point spacing, and 13-point medium text in one aligned control. Automated tests and bundle verification pass; visual comparison awaits the user capture.
|
|
38
|
-
|
|
39
|
-
## Follow-up Polish
|
|
40
|
-
|
|
41
|
-
- None classified until the post-fix screenshot is available.
|
|
42
|
-
|
|
43
|
-
final result: blocked
|
|
File without changes
|
{agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/.codex/skills/build-release/agents/openai.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/agentbridge/static/brand/android-chrome-192.png
RENAMED
|
File without changes
|
{agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/agentbridge/static/brand/android-chrome-512.png
RENAMED
|
File without changes
|
{agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/agentbridge/static/brand/apple-touch-icon.png
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/agentbridge/templates/dashboard/detail.html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/agentbridge/templates/dashboard/requests.html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/design-qa/agentbridge-popover-normalized.png
RENAMED
|
File without changes
|
|
File without changes
|
{agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/design-qa/implementation-header-activity.png
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/docs/plans/2026-02-14-dashboard-implementation.md
RENAMED
|
File without changes
|
|
File without changes
|
{agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/image-assets/apple-store/app-store-icon-1024.png
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/image-assets/sources/icon-source-keyed.png
RENAMED
|
File without changes
|
|
File without changes
|
{agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/image-assets/sources/logo-source-keyed.png
RENAMED
|
File without changes
|
|
File without changes
|
{agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/image-assets/sources/social-background-source.png
RENAMED
|
File without changes
|
|
File without changes
|
{agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/image-assets/web-seo/android-chrome-192.png
RENAMED
|
File without changes
|
{agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/image-assets/web-seo/android-chrome-512.png
RENAMED
|
File without changes
|
|
File without changes
|
{agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/image-assets/web-seo/favicon/favicon-16.png
RENAMED
|
File without changes
|
{agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/image-assets/web-seo/favicon/favicon-32.png
RENAMED
|
File without changes
|
{agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/image-assets/web-seo/favicon/favicon-48.png
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/image-assets/web-seo/og-image-1200x630.png
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/macos/Sources/AgentBridgeMenuBar/BrandMark.swift
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|