agentbridge-cli 0.1.12__tar.gz → 0.1.14__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.14}/.codex/skills/build-release/SKILL.md +13 -13
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/.codex/skills/build-release/scripts/release_build.py +4 -2
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/.github/workflows/release.yml +48 -54
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/PKG-INFO +18 -11
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/README.md +17 -10
- agentbridge_cli-0.1.14/agentbridge/_build_info.py +1 -0
- agentbridge_cli-0.1.14/design-qa/reference-inactive-menu-icons.png +0 -0
- agentbridge_cli-0.1.14/design-qa/reference-inactive-telegram-badge-normalized.png +0 -0
- agentbridge_cli-0.1.14/design-qa/reference-inactive-telegram-badge.png +0 -0
- agentbridge_cli-0.1.14/design-qa/reference-low-contrast-arrows-normalized.png +0 -0
- agentbridge_cli-0.1.14/design-qa/reference-low-contrast-arrows.png +0 -0
- agentbridge_cli-0.1.14/design-qa/reference-telegram-badge-normalized.png +0 -0
- agentbridge_cli-0.1.14/design-qa/reference-telegram-badge.png +0 -0
- agentbridge_cli-0.1.14/design-qa/status-item-active-workers.png +0 -0
- agentbridge_cli-0.1.14/design-qa/status-item-arrow-contrast-comparison.png +0 -0
- agentbridge_cli-0.1.14/design-qa/status-item-badge-comparison.png +0 -0
- agentbridge_cli-0.1.14/design-qa/status-item-inactive-comparison.png +0 -0
- agentbridge_cli-0.1.14/design-qa/status-item-no-workers.png +0 -0
- agentbridge_cli-0.1.14/design-qa.md +55 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/macos/README.md +7 -3
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/macos/Sources/AgentBridgeMenuBar/AgentBridgeMenuBarApp.swift +19 -14
- agentbridge_cli-0.1.14/macos/Sources/AgentBridgeMenuBar/MenuBarStatusPresenter.swift +135 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/macos/Sources/AgentBridgeMenuBar/StatusPopover.swift +1 -2
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/macos/Tests/AgentBridgeMenuBarTests/StatusModelsTests.swift +74 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/pyproject.toml +1 -1
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/scripts/build_macos_app.sh +9 -6
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/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.14}/.codex/config.toml +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/.codex/skills/build-release/agents/openai.yaml +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/.env.example +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/.github/dependabot.yml +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/.github/workflows/ci.yml +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/.github/workflows/dependency-review.yml +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/.gitignore +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/AGENTS.md +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/CLAUDE.md +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/LICENSE +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/Makefile +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/TODO.txt +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/agentbridge/__init__.py +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/agentbridge/config.py +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/agentbridge/dashboard.py +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/agentbridge/models.py +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/agentbridge/pool.py +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/agentbridge/server.py +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/agentbridge/static/brand/android-chrome-192.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/agentbridge/static/brand/android-chrome-512.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/agentbridge/static/brand/apple-touch-icon.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/agentbridge/static/brand/favicon-16.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/agentbridge/static/brand/favicon-32.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/agentbridge/static/brand/favicon-48.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/agentbridge/static/brand/favicon.ico +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/agentbridge/static/brand/favicon.svg +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/agentbridge/static/brand/site.webmanifest +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/agentbridge/templates/dashboard/base.html +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/agentbridge/templates/dashboard/chat.html +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/agentbridge/templates/dashboard/detail.html +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/agentbridge/templates/dashboard/page.html +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/agentbridge/templates/dashboard/pool.html +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/agentbridge/templates/dashboard/requests.html +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/architecture.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/design-qa/agentbridge-popover-final.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/design-qa/agentbridge-popover-normalized.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/design-qa/agentbridge-popover-pass-1.jpeg +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/design-qa/implementation-header-activity.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/design-qa/reference-header-activity.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/design-qa/reference-panel.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/docs/plans/2026-02-14-dashboard-design.md +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/docs/plans/2026-02-14-dashboard-implementation.md +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/hatch_build.py +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/image-assets/apple-store/app-store-icon-1024.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/image-assets/icon/icon-1024.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/image-assets/logo/logo-1024.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/image-assets/manifest.json +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/image-assets/sources/icon-source-keyed.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/image-assets/sources/icon-source.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/image-assets/sources/logo-source-keyed.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/image-assets/sources/logo-source.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/image-assets/sources/social-background-source.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/image-assets/sources/social-source.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/image-assets/web-seo/android-chrome-192.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/image-assets/web-seo/android-chrome-512.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/image-assets/web-seo/apple-touch-icon.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/image-assets/web-seo/favicon/favicon-16.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/image-assets/web-seo/favicon/favicon-32.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/image-assets/web-seo/favicon/favicon-48.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/image-assets/web-seo/favicon/favicon.ico +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/image-assets/web-seo/favicon/favicon.svg +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/image-assets/web-seo/og-image-1200x630.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/image-assets/web-seo/site.webmanifest +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/logo.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/macos/Package.swift +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/macos/Sources/AgentBridgeMenuBar/BrandMark.swift +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/macos/Sources/AgentBridgeMenuBar/HealthClient.swift +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/macos/Sources/AgentBridgeMenuBar/ServerController.swift +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/macos/Sources/AgentBridgeMenuBar/ServerLauncher.swift +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/macos/Sources/AgentBridgeMenuBar/SettingsView.swift +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/macos/Sources/AgentBridgeMenuBar/StatusModels.swift +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/scripts/dashboard_load_test.py +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/scripts/verify_macos_app.sh +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/tests/fixtures/ocr_test_document.png +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/tests/test_config.py +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/tests/test_dashboard.py +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/tests/test_dependency_security.py +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/tests/test_image_utils.py +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/tests/test_model_mapping.py +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/tests/test_models.py +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/tests/test_pool.py +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/tests/test_server.py +0 -0
- {agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/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 an ad-hoc-signed arm64 DMG.
|
|
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 the arm64 macOS
|
|
58
|
+
artifact with an ad-hoc signature. Stop at the exact failed gate.
|
|
64
59
|
|
|
65
60
|
4. Review and publish the release commit:
|
|
66
61
|
|
|
@@ -97,14 +92,19 @@ 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
|
-
|
|
101
|
-
`AgentBridge-<version>-macos-arm64.dmg` and
|
|
102
|
-
`AgentBridge-<version>-macos-x86_64.dmg` plus their `.sha256` files in the
|
|
95
|
+
Require `AgentBridge-<version>-macos-arm64.dmg` and its `.sha256` file in the
|
|
103
96
|
release assets. Do not report success until PyPI returns exact-version files and
|
|
104
97
|
all required GitHub assets exist. If the workflow fails, inspect
|
|
105
98
|
`gh run view <run-id> --log-failed` and report the failed gate before considering
|
|
106
99
|
recovery.
|
|
107
100
|
|
|
101
|
+
If an existing release is missing only its macOS assets, attach them without
|
|
102
|
+
republishing Python distributions:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
gh workflow run release.yml --ref main -f attach_macos=true
|
|
106
|
+
```
|
|
107
|
+
|
|
108
108
|
## Final Response
|
|
109
109
|
|
|
110
110
|
Lead with the exact PyPI version URL. Report the tag, release workflow URL and
|
|
@@ -313,8 +313,10 @@ def preflight_macos_app(version: str) -> dict[str, object]:
|
|
|
313
313
|
raise SystemExit("the AgentBridge release preflight requires macOS for app validation")
|
|
314
314
|
|
|
315
315
|
architecture = platform.machine()
|
|
316
|
-
if architecture
|
|
317
|
-
raise SystemExit(
|
|
316
|
+
if architecture != "arm64":
|
|
317
|
+
raise SystemExit(
|
|
318
|
+
f"unsupported macOS release architecture: {architecture}; AgentBridge requires arm64"
|
|
319
|
+
)
|
|
318
320
|
|
|
319
321
|
run(["swift", "test", "--package-path", "macos"])
|
|
320
322
|
run(["scripts/build_macos_app.sh"])
|
|
@@ -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,16 +205,15 @@ 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:
|
|
208
214
|
include:
|
|
209
215
|
- runner: macos-15
|
|
210
216
|
architecture: arm64
|
|
211
|
-
- runner: macos-15-intel
|
|
212
|
-
architecture: x86_64
|
|
213
217
|
runs-on: ${{ matrix.runner }}
|
|
214
218
|
timeout-minutes: 30
|
|
215
219
|
permissions:
|
|
@@ -225,57 +229,10 @@ jobs:
|
|
|
225
229
|
cache-dependency-glob: "uv.lock"
|
|
226
230
|
- name: Install Python 3.12
|
|
227
231
|
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
|
|
232
|
+
- name: Build ad-hoc-signed application
|
|
259
233
|
run: scripts/build_macos_app.sh
|
|
260
234
|
- name: Verify embedded application
|
|
261
235
|
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
236
|
- name: Store macOS artifacts
|
|
280
237
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
281
238
|
with:
|
|
@@ -292,7 +249,7 @@ jobs:
|
|
|
292
249
|
always() &&
|
|
293
250
|
needs.build.outputs.publish == 'true' &&
|
|
294
251
|
needs.publish.result == 'success' &&
|
|
295
|
-
|
|
252
|
+
needs.macos-app.result == 'success'
|
|
296
253
|
runs-on: ubuntu-latest
|
|
297
254
|
timeout-minutes: 10
|
|
298
255
|
permissions:
|
|
@@ -304,7 +261,6 @@ jobs:
|
|
|
304
261
|
name: python-package-${{ github.sha }}
|
|
305
262
|
path: dist/
|
|
306
263
|
- name: Download macOS artifacts
|
|
307
|
-
if: needs.macos-app.result == 'success'
|
|
308
264
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
309
265
|
with:
|
|
310
266
|
pattern: macos-app-*-${{ github.sha }}
|
|
@@ -317,6 +273,44 @@ jobs:
|
|
|
317
273
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
318
274
|
TAG: ${{ needs.build.outputs.tag }}
|
|
319
275
|
|
|
276
|
+
attach-macos:
|
|
277
|
+
needs: [macos-app]
|
|
278
|
+
if: >-
|
|
279
|
+
always() &&
|
|
280
|
+
github.event_name == 'workflow_dispatch' &&
|
|
281
|
+
inputs.attach_macos == true &&
|
|
282
|
+
needs.macos-app.result == 'success'
|
|
283
|
+
runs-on: ubuntu-latest
|
|
284
|
+
timeout-minutes: 10
|
|
285
|
+
permissions:
|
|
286
|
+
contents: write
|
|
287
|
+
steps:
|
|
288
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
289
|
+
with:
|
|
290
|
+
persist-credentials: false
|
|
291
|
+
- name: Download macOS artifacts
|
|
292
|
+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
293
|
+
with:
|
|
294
|
+
pattern: macos-app-*-${{ github.sha }}
|
|
295
|
+
path: dist/
|
|
296
|
+
merge-multiple: true
|
|
297
|
+
- name: Attach DMGs to the existing release
|
|
298
|
+
shell: bash
|
|
299
|
+
run: |
|
|
300
|
+
version="$(python - <<'PY'
|
|
301
|
+
import tomllib
|
|
302
|
+
|
|
303
|
+
with open("pyproject.toml", "rb") as pyproject_file:
|
|
304
|
+
print(tomllib.load(pyproject_file)["project"]["version"])
|
|
305
|
+
PY
|
|
306
|
+
)"
|
|
307
|
+
tag="v${version}"
|
|
308
|
+
gh release view "$tag" >/dev/null
|
|
309
|
+
gh release upload "$tag" dist/* --clobber
|
|
310
|
+
env:
|
|
311
|
+
GH_REPO: ${{ github.repository }}
|
|
312
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
313
|
+
|
|
320
314
|
publish:
|
|
321
315
|
needs: [build]
|
|
322
316
|
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.14
|
|
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
|
+
On an Apple silicon Mac, download the arm64 DMG 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. Intel Macs are not supported.
|
|
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 an ad-hoc-signed arm64 macOS DMG and SHA-256 checksum. The DMG is not
|
|
189
|
+
Apple-notarized and requires 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
|
+
On an Apple silicon Mac, download the arm64 DMG 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. Intel Macs are not supported.
|
|
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 an ad-hoc-signed arm64 macOS DMG and SHA-256 checksum. The DMG is not
|
|
157
|
+
Apple-notarized and requires 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 = "0980cbb"
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Design QA
|
|
2
|
+
|
|
3
|
+
**Comparison target**
|
|
4
|
+
|
|
5
|
+
- Source visual truth: `/var/folders/wz/x29jb7_x5rdc_5dcjr4qnhg00000gn/T/codex-clipboard-5d0ece7b-8e7b-489f-8d94-28b6877699ba.png`
|
|
6
|
+
- Implementation screenshot: `/tmp/agentbridge-popover-neutral-20260829.png`
|
|
7
|
+
- Combined comparison: `/tmp/agentbridge-popover-comparison-20260829.png`
|
|
8
|
+
- State: connected, zero active requests, one worker, dark appearance.
|
|
9
|
+
- Viewport: native macOS status popover at its fixed 350-point content width.
|
|
10
|
+
- Source pixels: 818 × 404. Implementation pixels: 752 × 444. Both are native high-density captures; no density normalization was applied because this change is limited to the visible control colors rather than geometry.
|
|
11
|
+
|
|
12
|
+
**Full-view comparison evidence**
|
|
13
|
+
|
|
14
|
+
- The source shows the gear and Open Dashboard control inheriting the green brand tint.
|
|
15
|
+
- The implementation shows both controls in the same neutral secondary-label palette used by the refresh row and supporting text.
|
|
16
|
+
- The native screenshot API renders the vibrancy material with a pink capture artifact; this does not affect the visible verification that the two controls no longer use the green tint.
|
|
17
|
+
|
|
18
|
+
**Focused region comparison evidence**
|
|
19
|
+
|
|
20
|
+
- A separate crop was not needed: the settings icon and dashboard button are large and clearly readable in the combined full-view comparison.
|
|
21
|
+
|
|
22
|
+
**Required fidelity surfaces**
|
|
23
|
+
|
|
24
|
+
- Fonts and typography: unchanged.
|
|
25
|
+
- Spacing and layout rhythm: unchanged.
|
|
26
|
+
- Colors and visual tokens: the popover-wide control tint now uses the native secondary-label color. The logo, connected indicator, and activity fill retain their intentional brand/status colors.
|
|
27
|
+
- Image quality and asset fidelity: unchanged; the existing native brand mark and SF Symbols remain in use.
|
|
28
|
+
- Copy and content: unchanged.
|
|
29
|
+
|
|
30
|
+
**Findings**
|
|
31
|
+
|
|
32
|
+
- No actionable P0, P1, or P2 mismatches remain for the requested control-color change.
|
|
33
|
+
|
|
34
|
+
**Open Questions**
|
|
35
|
+
|
|
36
|
+
- None.
|
|
37
|
+
|
|
38
|
+
**Implementation Checklist**
|
|
39
|
+
|
|
40
|
+
- [x] Remove the green tint from popover controls.
|
|
41
|
+
- [x] Preserve brand and semantic status colors.
|
|
42
|
+
- [x] Build and run the native test suite.
|
|
43
|
+
- [x] Capture and compare the rendered popover.
|
|
44
|
+
|
|
45
|
+
**Comparison History**
|
|
46
|
+
|
|
47
|
+
- Initial source finding: the settings icon and dashboard button inherited the green popover-wide tint.
|
|
48
|
+
- Fix: changed the popover tint to the native secondary-label color.
|
|
49
|
+
- Post-fix evidence: the combined comparison shows both controls rendered in neutral gray, with the brand and status colors preserved.
|
|
50
|
+
|
|
51
|
+
**Follow-up Polish**
|
|
52
|
+
|
|
53
|
+
- None.
|
|
54
|
+
|
|
55
|
+
final result: passed
|
|
@@ -32,12 +32,16 @@ scripts/build_macos_app.sh
|
|
|
32
32
|
|
|
33
33
|
The script builds the Swift executable, embeds a uv-managed Python 3.12 runtime
|
|
34
34
|
and the locked AgentBridge dependencies, applies an ad-hoc signature for local
|
|
35
|
-
use, and creates
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
use, and creates an arm64 DMG under `build/macos/arm64/`. Building and running
|
|
36
|
+
this local app requires an Apple silicon Mac but does not require an Apple
|
|
37
|
+
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 Apple silicon Macs. It is
|
|
42
|
+
not Apple-notarized, so macOS may require you to Control-click the app, choose
|
|
43
|
+
**Open**, and confirm on first launch. Intel Macs are not supported.
|
|
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
|
+
}
|
|
@@ -32,12 +32,11 @@ struct StatusPopover: View {
|
|
|
32
32
|
.padding(.bottom, 16)
|
|
33
33
|
.frame(width: 350)
|
|
34
34
|
.fixedSize(horizontal: false, vertical: true)
|
|
35
|
-
.tint(
|
|
35
|
+
.tint(secondaryTextColor)
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
private var header: some View {
|
|
39
39
|
HStack(spacing: 8) {
|
|
40
|
-
AgentBridgeBrandMark(size: 24)
|
|
41
40
|
Text("AgentBridge")
|
|
42
41
|
.font(.system(size: 17, weight: .bold))
|
|
43
42
|
Spacer()
|
|
@@ -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
|
}
|
|
@@ -23,11 +23,11 @@ with Path("pyproject.toml").open("rb") as file:
|
|
|
23
23
|
PY
|
|
24
24
|
)"
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
if [[ "${architecture}" != "arm64" ]]; then
|
|
27
|
+
echo "Unsupported macOS architecture: ${architecture}; AgentBridge requires arm64" >&2
|
|
28
|
+
exit 1
|
|
29
|
+
fi
|
|
30
|
+
artifact_architecture="arm64"
|
|
31
31
|
|
|
32
32
|
if [[ "${output_root}" != "${repository_root}"/build/macos/* ]]; then
|
|
33
33
|
echo "Refusing to replace unexpected build directory: ${output_root}" >&2
|
|
@@ -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.14}/.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
|
{agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/agentbridge/static/brand/android-chrome-192.png
RENAMED
|
File without changes
|
{agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/agentbridge/static/brand/android-chrome-512.png
RENAMED
|
File without changes
|
{agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/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.14}/agentbridge/templates/dashboard/detail.html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/agentbridge/templates/dashboard/requests.html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/design-qa/agentbridge-popover-normalized.png
RENAMED
|
File without changes
|
|
File without changes
|
{agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/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.14}/docs/plans/2026-02-14-dashboard-implementation.md
RENAMED
|
File without changes
|
|
File without changes
|
{agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/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.14}/image-assets/sources/icon-source-keyed.png
RENAMED
|
File without changes
|
|
File without changes
|
{agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/image-assets/sources/logo-source-keyed.png
RENAMED
|
File without changes
|
|
File without changes
|
{agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/image-assets/sources/social-background-source.png
RENAMED
|
File without changes
|
|
File without changes
|
{agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/image-assets/web-seo/android-chrome-192.png
RENAMED
|
File without changes
|
{agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/image-assets/web-seo/android-chrome-512.png
RENAMED
|
File without changes
|
|
File without changes
|
{agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/image-assets/web-seo/favicon/favicon-16.png
RENAMED
|
File without changes
|
{agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/image-assets/web-seo/favicon/favicon-32.png
RENAMED
|
File without changes
|
{agentbridge_cli-0.1.12 → agentbridge_cli-0.1.14}/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.14}/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.14}/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
|