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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/computer-use-ref
Original file line number Diff line number Diff line change
@@ -1 +1 @@
fc92b70417cdae941ba8b104a6e7b3b5b52a07e2
31a5d26a1e696b86cc130efc50d6f3459131eba9
48 changes: 26 additions & 22 deletions .github/workflows/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,18 @@ resolutions. Weekly Dependabot updates keep the GitHub Actions versions current.

5. **Signs macOS binaries and Autohand Computer Use**. When all Apple credentials
are configured, it uses Developer ID, notarizes the artifacts, and staples the app
ticket. While credentials are unavailable, alpha and stable releases use ad-hoc
signatures. Both modes verify each transported Actions artifact on a native Apple
Silicon or Intel runner before publication.
ticket. Every alpha and stable release requires Apple credentials. Missing
credentials stop publication. Transported artifacts are verified on a native
Apple Silicon or Intel runner before publication.
Developer ID signing applies `native/macos/cli.entitlements.plist` to the Bun
executables for JavaScript execution and native addons. The Swift Computer Use
host is signed separately without those runtime exceptions.

macOS build, verification, and CI jobs use `macos-26` (Apple Silicon) and
`macos-26-intel` (Intel). GitHub retired the macOS 14 runner image on
[November 2, 2026](https://github.blog/changelog/2026-10-01-github-actions-macos-14-runner-image-retirement/).
The Computer Use host is compiled explicitly for macOS 13, matching its bundle
deployment target, so the runner upgrade does not raise its minimum OS.

6. **Generates release notes** from the correct previous release tag

Expand Down Expand Up @@ -132,25 +141,20 @@ Add these secrets in GitHub Settings → Secrets → Actions:
2. **`AHTRACES_REPO_TOKEN`** (required for release and CI builds)
- Fine-grained read-only token for the private `autohandai/ahtraces` repository

3. **macOS signing and notarization secrets** (optional while Apple enrollment is pending)
- `APPLICATION_CERT_BASE64`: base64-encoded Developer ID Application `.p12`
- `CERT_PASSWORD`: password for the signing certificate archive
- `DEVELOPER_NAME`: organization name in the Developer ID certificate
- `TEAM_ID`: Apple Developer team identifier
- `APPLE_ID`: Apple account used by `notarytool`
- `APP_SPECIFIC_PASSWORD`: app-specific password for that Apple account
- Configure all six secrets to sign with Developer ID and notarize macOS artifacts.
Partially configured signing fails in the prepare job rather than silently
downgrading a configured Developer ID release.
- Alpha and stable releases with none of these secrets use temporary ad-hoc signing. Signatures are
verified before upload and again after downloading the artifacts, but Apple
does not notarize these builds. Downloaded apps may require **Open Anyway** in
System Settings → Privacy & Security. See [Apple's instructions](https://support.apple.com/en-us/102445).
- Ad-hoc signing does not preserve the Developer ID permission identity across
updates. Users may need to grant Accessibility and Screen Recording access
to Autohand Computer Use again after an update. The CLI asks for explicit trust
before first-use permission setup, even with `--yes`, and checks the effective
OS grants before allowing desktop requests to proceed.
3. **macOS signing and notarization secrets** (required for every release)
- `CSC_LINK`: base64-encoded Developer ID Application `.p12`
- `CSC_KEY_PASSWORD`: password for the signing certificate archive
- `APPLE_TEAM_ID`: Autohand's Apple Developer team identifier
- `APPLE_API_KEY`: raw App Store Connect API private key (`.p8`)
- `APPLE_API_KEY_ID`: API key identifier
- `APPLE_API_ISSUER`: App Store Connect issuer identifier
- The same credential names are used by Autohand Computer Use and Desktop.
The component importer derives the Developer ID identity from the P12 and
checks its team. No Apple ID password or upstream Cua provisioning profile
is required. Credentials are written only to the runner's temporary folder.
- Signing and notarization are mandatory for alpha and stable releases; there
is no ad-hoc publication fallback. The host's stapled ticket and Gatekeeper
acceptance are validated again after artifact transport.

4. **`MODEL_CATALOG_PR_TOKEN`** (optional for model catalog pull requests)
- Fine-grained token with repository Contents, Issues, and Pull requests read/write access
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [macos-latest, ubuntu-latest, windows-latest]
os: [macos-26, ubuntu-latest, windows-latest]
steps:
- uses: actions/checkout@v7

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/installer-release-compatibility.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
os: [ubuntu-latest, macos-26]
channel: [stable, alpha]
runs-on: ${{ matrix.os }}
steps:
Expand Down
166 changes: 54 additions & 112 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,31 +67,24 @@ jobs:
if: steps.check.outputs.should_release == 'true'
env:
RELEASE_CHANNEL: ${{ steps.determine.outputs.channel }}
APPLICATION_CERT_BASE64: ${{ secrets.APPLICATION_CERT_BASE64 }}
CERT_PASSWORD: ${{ secrets.CERT_PASSWORD }}
DEVELOPER_NAME: ${{ secrets.DEVELOPER_NAME }}
TEAM_ID: ${{ secrets.TEAM_ID }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APP_SPECIFIC_PASSWORD: ${{ secrets.APP_SPECIFIC_PASSWORD }}
CSC_LINK: ${{ secrets.CSC_LINK }}
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }}
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
run: |
set -euo pipefail
required=(APPLICATION_CERT_BASE64 CERT_PASSWORD DEVELOPER_NAME TEAM_ID APPLE_ID APP_SPECIFIC_PASSWORD)
required=(CSC_LINK CSC_KEY_PASSWORD APPLE_TEAM_ID APPLE_API_KEY APPLE_API_KEY_ID APPLE_API_ISSUER)
missing=()
for name in "${required[@]}"; do
if [ -z "${!name}" ]; then
missing+=("$name")
fi
if [ -z "${!name:-}" ]; then missing+=("$name"); fi
done

if [ "${#missing[@]}" -eq 0 ]; then
echo "mode=developer-id" >> "$GITHUB_OUTPUT"
elif [ "${#missing[@]}" -eq "${#required[@]}" ]; then
echo "mode=adhoc" >> "$GITHUB_OUTPUT"
echo "::warning::macOS artifacts will be ad-hoc signed, not notarized, while Apple credentials are unavailable. Users must trust the app on first use and may need Open Anyway and renewed Computer Use permissions after updates."
else
echo "::error::Incomplete macOS signing configuration. Missing: ${missing[*]}. Configure all Apple credentials for notarization, or omit all of them for the temporary ad-hoc signing path."
if [ "${#missing[@]}" -ne 0 ]; then
echo "::error::Apple signing is required for every release. Missing: ${missing[*]}."
exit 1
fi
echo "mode=developer-id" >> "$GITHUB_OUTPUT"

- name: Setup Node.js
uses: actions/setup-node@v7
Expand Down Expand Up @@ -249,12 +242,12 @@ jobs:
strategy:
matrix:
include:
- os: macos-latest
- os: macos-26
target: darwin-arm64
artifact: autohand-macos-arm64
tracesArtifact: ahtraces-macos-arm64
computerUseArtifact: autohand-computer-use-darwin-arm64.tar.gz
- os: macos-15-intel
- os: macos-26-intel
target: darwin-x64
artifact: autohand-macos-x64
tracesArtifact: ahtraces-macos-x64
Expand Down Expand Up @@ -373,39 +366,13 @@ jobs:
- name: Import macOS signing certificate
if: runner.os == 'macOS' && needs.prepare.outputs.macos_signing_mode == 'developer-id'
env:
APPLICATION_CERT_BASE64: ${{ secrets.APPLICATION_CERT_BASE64 }}
CERT_PASSWORD: ${{ secrets.CERT_PASSWORD }}
run: |
set -euo pipefail
test -n "$APPLICATION_CERT_BASE64" || {
echo "::error::APPLICATION_CERT_BASE64 is required for macOS releases"
exit 1
}
test -n "$CERT_PASSWORD" || {
echo "::error::CERT_PASSWORD is required for macOS releases"
exit 1
}

KEYCHAIN_PASSWORD=$(uuidgen)
echo "KEYCHAIN_PASSWORD=$KEYCHAIN_PASSWORD" >> "$GITHUB_ENV"
security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
security default-keychain -s build.keychain
security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
security set-keychain-settings -t 3600 -l build.keychain

printf '%s' "$APPLICATION_CERT_BASE64" | base64 --decode > application.p12
security import application.p12 -k build.keychain -P "$CERT_PASSWORD" \
-T /usr/bin/codesign > /dev/null 2>&1
rm application.p12
security set-key-partition-list -S apple-tool:,apple:,codesign: \
-s -k "$KEYCHAIN_PASSWORD" build.keychain > /dev/null 2>&1

CERT_COUNT=$(security find-identity -v -p codesigning build.keychain \
| grep -c "Developer ID Application" || true)
if [ "$CERT_COUNT" -eq 0 ]; then
echo "::error::No Developer ID Application certificate was imported"
exit 1
fi
CSC_LINK: ${{ secrets.CSC_LINK }}
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }}
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
run: bash computer-use-component/autohand/scripts/import-apple-signing.sh

- name: Build Autohand Computer Use host
if: runner.os == 'macOS'
Expand All @@ -417,37 +384,20 @@ jobs:
- name: Sign and notarize macOS artifacts
if: runner.os == 'macOS' && needs.prepare.outputs.macos_signing_mode == 'developer-id'
env:
DEVELOPER_NAME: ${{ secrets.DEVELOPER_NAME }}
TEAM_ID: ${{ secrets.TEAM_ID }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APP_SPECIFIC_PASSWORD: ${{ secrets.APP_SPECIFIC_PASSWORD }}
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
run: |
set -euo pipefail
test -n "$DEVELOPER_NAME" || {
echo "::error::DEVELOPER_NAME is required for macOS releases"
exit 1
}
test -n "$TEAM_ID" || {
echo "::error::TEAM_ID is required for macOS releases"
exit 1
}
test -n "$APPLE_ID" || {
echo "::error::APPLE_ID is required for macOS notarization"
exit 1
}
test -n "$APP_SPECIFIC_PASSWORD" || {
echo "::error::APP_SPECIFIC_PASSWORD is required for macOS notarization"
exit 1
}

IDENTITY="Developer ID Application: ${DEVELOPER_NAME} (${TEAM_ID})"
codesign --force --timestamp --options runtime --sign "$IDENTITY" \
IDENTITY="$AUTOHAND_CODESIGN_IDENTITY"
codesign --force --timestamp --options runtime --sign "$IDENTITY" --keychain "$AUTOHAND_CODESIGN_KEYCHAIN" \
--entitlements "./native/macos/cli.entitlements.plist" \
./binaries/${{ matrix.artifact }}
codesign --verify --strict --verbose=4 ./binaries/${{ matrix.artifact }}
codesign --force --timestamp --options runtime --sign "$IDENTITY" \
codesign --force --timestamp --options runtime --sign "$IDENTITY" --keychain "$AUTOHAND_CODESIGN_KEYCHAIN" \
--entitlements "./native/macos/cli.entitlements.plist" \
./binaries/${{ matrix.tracesArtifact }}
codesign --verify --strict --verbose=4 ./binaries/${{ matrix.tracesArtifact }}
codesign --force --timestamp --options runtime --sign "$IDENTITY" \
codesign --force --timestamp --options runtime --sign "$IDENTITY" --keychain "$AUTOHAND_CODESIGN_KEYCHAIN" \
"./binaries/Autohand Computer Use.app"
codesign --verify --deep --strict --verbose=4 "./binaries/Autohand Computer Use.app"

Expand All @@ -459,9 +409,9 @@ jobs:
ditto -c -k --keepParent ./binaries/notarization \
./binaries/autohand-macos-notarization.zip
xcrun notarytool submit ./binaries/autohand-macos-notarization.zip \
--apple-id "$APPLE_ID" \
--team-id "$TEAM_ID" \
--password "$APP_SPECIFIC_PASSWORD" \
--key "$APPLE_API_KEY_PATH" \
--key-id "$APPLE_API_KEY_ID" \
--issuer "$APPLE_API_ISSUER" \
--wait --timeout 20m
xcrun stapler staple "./binaries/Autohand Computer Use.app"
spctl -a -vv -t exec "./binaries/Autohand Computer Use.app"
Expand All @@ -470,29 +420,16 @@ jobs:
tar -czf "./binaries/autohand-computer-use-${{ matrix.target }}.tar.gz" \
-C ./binaries "Autohand Computer Use.app"

- name: Ad-hoc sign macOS artifacts
if: runner.os == 'macOS' && needs.prepare.outputs.macos_signing_mode == 'adhoc'
run: |
set -euo pipefail
codesign --force --sign - --timestamp=none ./binaries/${{ matrix.artifact }}
codesign --verify --strict --verbose=4 ./binaries/${{ matrix.artifact }}
codesign --force --sign - --timestamp=none ./binaries/${{ matrix.tracesArtifact }}
codesign --verify --strict --verbose=4 ./binaries/${{ matrix.tracesArtifact }}
codesign --force --sign - --timestamp=none "./binaries/Autohand Computer Use.app"
codesign --verify --deep --strict --verbose=4 "./binaries/Autohand Computer Use.app"
tar -czf "./binaries/autohand-computer-use-${{ matrix.target }}.tar.gz" \
-C ./binaries "Autohand Computer Use.app"

- name: Verify binary
if: runner.os != 'Windows' && !contains(matrix.target, 'arm64') || matrix.os == 'macos-latest' && contains(matrix.target, 'arm64')
if: runner.os != 'Windows' && !contains(matrix.target, 'arm64') || matrix.os == 'macos-26' && contains(matrix.target, 'arm64')
run: |
chmod +x ./binaries/${{ matrix.artifact }}
file ./binaries/${{ matrix.artifact }}
chmod +x ./binaries/${{ matrix.tracesArtifact }}
file ./binaries/${{ matrix.tracesArtifact }}

- name: Smoke test binary
if: runner.os != 'Windows' && !contains(matrix.target, 'arm64') && !contains(matrix.target, 'musl') || matrix.os == 'macos-latest' && contains(matrix.target, 'arm64')
if: runner.os != 'Windows' && !contains(matrix.target, 'arm64') && !contains(matrix.target, 'musl') || matrix.os == 'macos-26' && contains(matrix.target, 'arm64')
shell: bash
run: |
# Helper: run a command with a timeout (works on both Linux and macOS)
Expand Down Expand Up @@ -553,7 +490,7 @@ jobs:
}

- name: Verify fresh alpha installation
if: runner.os != 'Windows' && !contains(matrix.target, 'arm64') && !contains(matrix.target, 'musl') || matrix.os == 'macos-latest' && contains(matrix.target, 'arm64')
if: runner.os != 'Windows' && !contains(matrix.target, 'arm64') && !contains(matrix.target, 'musl') || matrix.os == 'macos-26' && contains(matrix.target, 'arm64')
shell: bash
timeout-minutes: 10
run: |
Expand All @@ -575,18 +512,27 @@ jobs:
./binaries/autohand-computer-use-*.tar.gz
retention-days: 1

- name: Remove temporary Apple signing credentials
if: always() && runner.os == 'macOS'
shell: bash
run: |
if [ -f "$RUNNER_TEMP/autohand-signing.keychain-db" ]; then
security delete-keychain "$RUNNER_TEMP/autohand-signing.keychain-db"
fi
rm -f "$RUNNER_TEMP/AutohandNotary.p8" "$RUNNER_TEMP/autohand-signing.p12"

verify-macos-artifacts:
needs: [prepare, build]
if: needs.prepare.outputs.should_release == 'true'
runs-on: ${{ matrix.os }}
strategy:
matrix:
include:
- os: macos-latest
- os: macos-26
artifact: autohand-macos-arm64
tracesArtifact: ahtraces-macos-arm64
computerUseArtifact: autohand-computer-use-darwin-arm64.tar.gz
- os: macos-15-intel
- os: macos-26-intel
artifact: autohand-macos-x64
tracesArtifact: ahtraces-macos-x64
computerUseArtifact: autohand-computer-use-darwin-x64.tar.gz
Expand All @@ -600,21 +546,30 @@ jobs:

- name: Verify transported macOS binary
shell: bash
env:
MACOS_SIGNING_MODE: ${{ needs.prepare.outputs.macos_signing_mode }}
run: |
set -euo pipefail
binary="./binaries/${{ matrix.artifact }}"
ahtraces="./binaries/${{ matrix.tracesArtifact }}"
computer_use_archive="./binaries/${{ matrix.computerUseArtifact }}"
chmod +x "$binary"
file "$binary"
codesign --verify --strict --verbose=4 "$binary"
codesign --verify --strict -R=notarized --check-notarization "$binary"
"$binary" --version < /dev/null
chmod +x "$ahtraces"
file "$ahtraces"
codesign --verify --strict --verbose=4 "$ahtraces"
codesign --verify --strict -R=notarized --check-notarization "$ahtraces"
"$ahtraces" --version < /dev/null
mkdir -p computer-use-verification
tar -xzf "$computer_use_archive" -C computer-use-verification
codesign --verify --deep --strict --verbose=4 "computer-use-verification/Autohand Computer Use.app"
if [ "$MACOS_SIGNING_MODE" = "developer-id" ]; then
xcrun stapler validate "computer-use-verification/Autohand Computer Use.app"
spctl -a -vv -t exec "computer-use-verification/Autohand Computer Use.app"
fi

release:
needs: [prepare, build, verify-macos-artifacts]
Expand Down Expand Up @@ -808,19 +763,6 @@ jobs:
--output release-notes.md
cat release-notes.md

- name: Document ad-hoc macOS signing
if: needs.prepare.outputs.macos_signing_mode == 'adhoc'
run: |
cat >> release-notes.md <<'EOF'

### macOS signing and first use

These macOS binaries and the Computer Use app are ad-hoc signed, not notarized by Apple while our Developer ID setup is pending.
On first use, trust Autohand Computer Use only if you trust the download. If macOS blocks it, try opening the app, then choose **Open Anyway** in System Settings → Privacy & Security and confirm **Open**.
See [Apple's instructions](https://support.apple.com/en-us/102445).
Computer Use may require Accessibility and Screen Recording permissions again after updates.
EOF

- name: Create Release
uses: softprops/action-gh-release@v3
with:
Expand Down
Loading
Loading