Skip to content
Merged
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
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ jobs:
- uses: oven-sh/setup-bun@v2
with:
bun-version: latest
# The desktop app's checks need no Electron binary, which its install would download.
- run: bun install --frozen-lockfile
env:
ELECTRON_SKIP_BINARY_DOWNLOAD: 1
- run: bun run typecheck
- run: bun run test
- run: bun run build
Expand Down
110 changes: 110 additions & 0 deletions .github/workflows/desktop.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
name: desktop

# The macOS desktop app (packages/desktop), on a `desktop-v1.2.3` tag: built for Apple silicon and
# Intel and published as the release for the tag, with latest-mac.yml, the feed the app checks the
# repository's latest release for. Pull requests touching the app only build it.
#
# Pull requests build an unsigned artifact. Tagged releases require a Developer ID Application
# certificate and App Store Connect API key, then verify the signature and stapled notarization
# ticket before anything is published. See packages/desktop/README.md for the one-time setup.

on:
push:
tags: ['desktop-v*']
pull_request:
paths:
- packages/desktop/**
- .github/workflows/desktop.yml

permissions:
contents: write

jobs:
build:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- run: bun install --frozen-lockfile
- name: Check the tag names the version in package.json
if: startsWith(github.ref, 'refs/tags/desktop-v')
run: |
version=$(bun -e "console.log(require('./packages/desktop/package.json').version)")
if [[ "${GITHUB_REF_NAME}" != "desktop-v${version}" ]]; then
echo "::error::The tag is ${GITHUB_REF_NAME} but packages/desktop/package.json says ${version}."
exit 1
fi
- if: github.event_name == 'pull_request'
name: Build the unsigned pull-request artifact
run: bun run --cwd packages/desktop package
- if: startsWith(github.ref, 'refs/tags/desktop-v')
name: Require Apple release credentials
env:
CSC_LINK: ${{ secrets.CSC_LINK }}
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
APPLE_API_KEY_BASE64: ${{ secrets.APPLE_API_KEY_BASE64 }}
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
run: |
missing=()
for name in CSC_LINK CSC_KEY_PASSWORD APPLE_API_KEY_BASE64 APPLE_API_KEY_ID APPLE_API_ISSUER; do
if [[ -z "${!name}" ]]; then
missing+=("${name}")
fi
done
if (( ${#missing[@]} > 0 )); then
echo "::error::Missing required release secrets: ${missing[*]}"
exit 1
fi
- if: startsWith(github.ref, 'refs/tags/desktop-v')
name: Prepare the notarization key
env:
APPLE_API_KEY_BASE64: ${{ secrets.APPLE_API_KEY_BASE64 }}
run: |
umask 077
printf '%s' "${APPLE_API_KEY_BASE64}" | base64 --decode > "${RUNNER_TEMP}/AuthKey.p8"
- if: startsWith(github.ref, 'refs/tags/desktop-v')
name: Build, sign and notarize the release
env:
CSC_LINK: ${{ secrets.CSC_LINK }}
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
APPLE_API_KEY: ${{ runner.temp }}/AuthKey.p8
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
run: bun run --cwd packages/desktop package:release
- if: startsWith(github.ref, 'refs/tags/desktop-v')
name: Verify the signatures and notarization tickets
run: |
app_count=0
while IFS= read -r -d '' app; do
codesign --verify --deep --strict --verbose=2 "${app}"
xcrun stapler validate "${app}"
spctl --assess --type exec --verbose=2 "${app}"
app_count=$((app_count + 1))
done < <(find packages/desktop/release -type d -name '*.app' -prune -print0)
if [[ "${app_count}" -ne 2 ]]; then
echo "::error::Expected two signed apps, found ${app_count}."
exit 1
fi
- if: startsWith(github.ref, 'refs/tags/desktop-v')
name: Publish the release
env:
GH_TOKEN: ${{ github.token }}
run: |
gh release create "${GITHUB_REF_NAME}" \
--title "Personal Agent Proxy for macOS ${GITHUB_REF_NAME#desktop-v}" \
--generate-notes \
packages/desktop/release/*.dmg \
packages/desktop/release/*.zip \
packages/desktop/release/*.blockmap \
packages/desktop/release/latest-mac.yml
- if: github.event_name == 'pull_request'
uses: actions/upload-artifact@v4
with:
name: desktop-mac
path: |
packages/desktop/release/*.dmg
packages/desktop/release/latest-mac.yml
if-no-files-found: error
3 changes: 2 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@
- `bun run docs` - Start only the docs (http://localhost:5174), without the database, the api or the other apps
- `bun run website` - Start only the website (http://localhost:5175), without the database, the api or the other apps
- `bun run map` - Start the screen map (http://localhost:5176), the development-only canvas of every screen of the web app and the ways between them. It shows what `bun run crawl` in `packages/map` last wrote: with `bun run dev` up, the crawl seeds the demo workspace, signs in as a person, as a new organization and as an agent, visits every route in the web app's router, screenshots each and notes its links. Everything it writes is in `packages/map/generated`, which is gitignored
- `bun run desktop` - Start the desktop app (`packages/desktop`, an Electron window around the web app) on this checkout. A `bun run dev` already running is used as it is; without one the stack is started first and stopped when the window closes. The app loads `.env`'s `APP_URL`; `DESKTOP_APP_URL=<url>` points it at another origin and skips the stack
- `bun run seed` - With `bun run dev` up, fill the local database with a demo workspace (connections, agents with settings of their own, activity) and print a sign-in link for `demo@proxy.local`. Re-running replaces it
- `bun run --cwd packages/db db:migrate:diff` - With the database running, print the SQL from the database to `schema.prisma`; save it as `packages/db/prisma/migrations/<name>/migration.sql`, the name `v` and the next three-digit number, such as `v011_add_x`: Prisma applies migrations in the order their names sort as text, so `10_x` would run before `9_mcp_oauth` (a test refuses any other name). `bun run dev` applies pending migrations on start
- `bun run typecheck` - Typecheck all packages
- `bun run test` - Run every package's tests (`bun test`; the api's with `--isolate`, since its tests mock modules per file)
- `bun run lint` - Lint the web app, the docs and the website with oxlint
- `bun run lint` - Lint the web app, the docs, the website, the screen map and the desktop app with oxlint
- `bun run build` - Production builds of the web app, the docs and the website
- `docker build -t proxy .` - The self-hosting image (`Dockerfile`): the api serving the web app's build on one origin, applying the migrations on start (`docker-entrypoint.sh`). `docker-compose.yml` runs it with Postgres and Caddy; `.github/workflows/image.yml` publishes it to `ghcr.io/personalagentproxy/proxy`. Hosted Personal Agent Proxy runs on Render from `render.yaml`: the image as one service (api and web app on one origin, two instances), Render Postgres, and the docs and the website as static sites

Expand Down
Loading
Loading