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.
Files changed (112) hide show
  1. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/.codex/skills/build-release/SKILL.md +13 -11
  2. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/.github/workflows/release.yml +48 -52
  3. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/PKG-INFO +18 -11
  4. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/README.md +17 -10
  5. agentbridge_cli-0.1.13/agentbridge/_build_info.py +1 -0
  6. agentbridge_cli-0.1.13/design-qa/reference-inactive-menu-icons.png +0 -0
  7. agentbridge_cli-0.1.13/design-qa/reference-inactive-telegram-badge-normalized.png +0 -0
  8. agentbridge_cli-0.1.13/design-qa/reference-inactive-telegram-badge.png +0 -0
  9. agentbridge_cli-0.1.13/design-qa/reference-low-contrast-arrows-normalized.png +0 -0
  10. agentbridge_cli-0.1.13/design-qa/reference-low-contrast-arrows.png +0 -0
  11. agentbridge_cli-0.1.13/design-qa/reference-telegram-badge-normalized.png +0 -0
  12. agentbridge_cli-0.1.13/design-qa/reference-telegram-badge.png +0 -0
  13. agentbridge_cli-0.1.13/design-qa/status-item-active-workers.png +0 -0
  14. agentbridge_cli-0.1.13/design-qa/status-item-arrow-contrast-comparison.png +0 -0
  15. agentbridge_cli-0.1.13/design-qa/status-item-badge-comparison.png +0 -0
  16. agentbridge_cli-0.1.13/design-qa/status-item-inactive-comparison.png +0 -0
  17. agentbridge_cli-0.1.13/design-qa/status-item-no-workers.png +0 -0
  18. agentbridge_cli-0.1.13/design-qa.md +58 -0
  19. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/macos/README.md +4 -0
  20. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/macos/Sources/AgentBridgeMenuBar/AgentBridgeMenuBarApp.swift +19 -14
  21. agentbridge_cli-0.1.13/macos/Sources/AgentBridgeMenuBar/MenuBarStatusPresenter.swift +135 -0
  22. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/macos/Tests/AgentBridgeMenuBarTests/StatusModelsTests.swift +74 -0
  23. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/pyproject.toml +1 -1
  24. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/scripts/build_macos_app.sh +4 -1
  25. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/uv.lock +1 -1
  26. agentbridge_cli-0.1.12/agentbridge/_build_info.py +0 -1
  27. agentbridge_cli-0.1.12/design-qa.md +0 -43
  28. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/.codex/config.toml +0 -0
  29. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/.codex/skills/build-release/agents/openai.yaml +0 -0
  30. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/.codex/skills/build-release/scripts/release_build.py +0 -0
  31. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/.env.example +0 -0
  32. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/.github/dependabot.yml +0 -0
  33. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/.github/workflows/ci.yml +0 -0
  34. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/.github/workflows/dependency-review.yml +0 -0
  35. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/.gitignore +0 -0
  36. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/AGENTS.md +0 -0
  37. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/CLAUDE.md +0 -0
  38. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/LICENSE +0 -0
  39. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/Makefile +0 -0
  40. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/TODO.txt +0 -0
  41. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/agentbridge/__init__.py +0 -0
  42. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/agentbridge/config.py +0 -0
  43. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/agentbridge/dashboard.py +0 -0
  44. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/agentbridge/models.py +0 -0
  45. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/agentbridge/pool.py +0 -0
  46. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/agentbridge/server.py +0 -0
  47. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/agentbridge/static/brand/android-chrome-192.png +0 -0
  48. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/agentbridge/static/brand/android-chrome-512.png +0 -0
  49. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/agentbridge/static/brand/apple-touch-icon.png +0 -0
  50. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/agentbridge/static/brand/favicon-16.png +0 -0
  51. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/agentbridge/static/brand/favicon-32.png +0 -0
  52. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/agentbridge/static/brand/favicon-48.png +0 -0
  53. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/agentbridge/static/brand/favicon.ico +0 -0
  54. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/agentbridge/static/brand/favicon.svg +0 -0
  55. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/agentbridge/static/brand/site.webmanifest +0 -0
  56. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/agentbridge/templates/dashboard/base.html +0 -0
  57. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/agentbridge/templates/dashboard/chat.html +0 -0
  58. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/agentbridge/templates/dashboard/detail.html +0 -0
  59. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/agentbridge/templates/dashboard/page.html +0 -0
  60. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/agentbridge/templates/dashboard/pool.html +0 -0
  61. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/agentbridge/templates/dashboard/requests.html +0 -0
  62. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/architecture.png +0 -0
  63. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/design-qa/agentbridge-popover-final.png +0 -0
  64. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/design-qa/agentbridge-popover-normalized.png +0 -0
  65. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/design-qa/agentbridge-popover-pass-1.jpeg +0 -0
  66. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/design-qa/implementation-header-activity.png +0 -0
  67. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/design-qa/reference-header-activity.png +0 -0
  68. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/design-qa/reference-panel.png +0 -0
  69. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/docs/plans/2026-02-14-dashboard-design.md +0 -0
  70. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/docs/plans/2026-02-14-dashboard-implementation.md +0 -0
  71. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/hatch_build.py +0 -0
  72. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/image-assets/apple-store/app-store-icon-1024.png +0 -0
  73. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/image-assets/icon/icon-1024.png +0 -0
  74. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/image-assets/logo/logo-1024.png +0 -0
  75. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/image-assets/manifest.json +0 -0
  76. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/image-assets/sources/icon-source-keyed.png +0 -0
  77. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/image-assets/sources/icon-source.png +0 -0
  78. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/image-assets/sources/logo-source-keyed.png +0 -0
  79. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/image-assets/sources/logo-source.png +0 -0
  80. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/image-assets/sources/social-background-source.png +0 -0
  81. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/image-assets/sources/social-source.png +0 -0
  82. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/image-assets/web-seo/android-chrome-192.png +0 -0
  83. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/image-assets/web-seo/android-chrome-512.png +0 -0
  84. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/image-assets/web-seo/apple-touch-icon.png +0 -0
  85. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/image-assets/web-seo/favicon/favicon-16.png +0 -0
  86. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/image-assets/web-seo/favicon/favicon-32.png +0 -0
  87. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/image-assets/web-seo/favicon/favicon-48.png +0 -0
  88. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/image-assets/web-seo/favicon/favicon.ico +0 -0
  89. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/image-assets/web-seo/favicon/favicon.svg +0 -0
  90. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/image-assets/web-seo/og-image-1200x630.png +0 -0
  91. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/image-assets/web-seo/site.webmanifest +0 -0
  92. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/logo.png +0 -0
  93. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/macos/Package.swift +0 -0
  94. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/macos/Sources/AgentBridgeMenuBar/BrandMark.swift +0 -0
  95. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/macos/Sources/AgentBridgeMenuBar/HealthClient.swift +0 -0
  96. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/macos/Sources/AgentBridgeMenuBar/ServerController.swift +0 -0
  97. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/macos/Sources/AgentBridgeMenuBar/ServerLauncher.swift +0 -0
  98. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/macos/Sources/AgentBridgeMenuBar/SettingsView.swift +0 -0
  99. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/macos/Sources/AgentBridgeMenuBar/StatusModels.swift +0 -0
  100. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/macos/Sources/AgentBridgeMenuBar/StatusPopover.swift +0 -0
  101. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/scripts/dashboard_load_test.py +0 -0
  102. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/scripts/verify_macos_app.sh +0 -0
  103. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/tests/fixtures/ocr_test_document.png +0 -0
  104. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/tests/test_config.py +0 -0
  105. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/tests/test_dashboard.py +0 -0
  106. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/tests/test_dependency_security.py +0 -0
  107. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/tests/test_image_utils.py +0 -0
  108. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/tests/test_model_mapping.py +0 -0
  109. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/tests/test_models.py +0 -0
  110. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/tests/test_pool.py +0 -0
  111. {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.13}/tests/test_server.py +0 -0
  112. {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, when macOS publishing is enabled, attaches
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
- Require the repository variable `MACOS_RELEASE_ENABLED` to be `true` and verify
33
- that these Actions secrets are configured before a release expected to contain
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 and notarizes both
63
- macOS architectures. Stop at the exact failed gate.
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
- When macOS publishing is enabled, require both
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
- needs.build.outputs.publish == 'true' &&
204
- vars.MACOS_RELEASE_ENABLED == 'true'
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: Import Developer ID certificate
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
- (needs.macos-app.result == 'success' || needs.macos-app.result == 'skipped')
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.12
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
- Build the app locally on macOS 13 or later. This requires
50
- [uv](https://docs.astral.sh/uv/) and the Xcode Command Line Tools, but it does
51
- not require an Apple Developer account:
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
- Drag `AgentBridge.app` to Applications and open it. The menu-bar window shows
61
- live server health and activity, starts and stops the local server, opens the
62
- dashboard, and provides settings for the port, worker count, and launch at
63
- login. See [`macos/README.md`](macos/README.md) for development instructions.
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
- the macOS app is tested in CI and built locally from source. Releases through
184
- `0.1.10` remain available under the previous `agentbridge-py` distribution name.
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
- Build the app locally on macOS 13 or later. This requires
18
- [uv](https://docs.astral.sh/uv/) and the Xcode Command Line Tools, but it does
19
- not require an Apple Developer account:
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
- Drag `AgentBridge.app` to Applications and open it. The menu-bar window shows
29
- live server health and activity, starts and stops the local server, opens the
30
- dashboard, and provides settings for the port, worker count, and launch at
31
- login. See [`macos/README.md`](macos/README.md) for development instructions.
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
- the macOS app is tested in CI and built locally from source. Releases through
152
- `0.1.10` remain available under the previous `agentbridge-py` distribution name.
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"
@@ -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 phaseObservation: AnyCancellable?
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
- phaseObservation = controller.$phase
56
+ statusObservation = Publishers.CombineLatest(controller.$phase, controller.$health)
51
57
  .receive(on: RunLoop.main)
52
- .sink { [weak self] phase in
53
- self?.updateStatusItem(for: phase)
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
- let label = "AgentBridge: \(phase.label)"
90
- let image = NSImage(
91
- systemSymbolName: phase.menuBarSymbol,
92
- accessibilityDescription: label
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
  }
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "agentbridge-cli"
3
- version = "0.1.12"
3
+ version = "0.1.13"
4
4
  description = "Bridge OpenAI tools to Claude Code SDK, Codex CLI, and OpenRouter"
5
5
  readme = "README.md"
6
6
  license = {text = "MIT"}
@@ -150,7 +150,10 @@ hdiutil create \
150
150
  -ov \
151
151
  "${dmg_path}" >/dev/null
152
152
 
153
- shasum -a 256 "${dmg_path}" > "${dmg_path}.sha256"
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}"
@@ -37,7 +37,7 @@ constraints = [
37
37
 
38
38
  [[package]]
39
39
  name = "agentbridge-cli"
40
- version = "0.1.12"
40
+ version = "0.1.13"
41
41
  source = { editable = "." }
42
42
  dependencies = [
43
43
  { name = "claude-agent-sdk" },
@@ -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