- Node
^20.19.0 || >=22.12.0 - pnpm
11.7.0(pinned viapackageManager;corepack enablewill provision it)
pnpm run env:setup # install deps + download the Electron runtime (uses the .npmrc mirror)pnpm dev # run the Electron app in development
pnpm -r typecheck # typecheck every package (also aliased as `pnpm lint`)
pnpm -r test # run vitest across packages (api + client)
pnpm build # build all packages
pnpm package # build installers for the current OS (electron-builder)
pnpm package:mac # .dmg / .zip (arm64 + x64)
pnpm package:win # Windows NSIS .exe (x64)
pnpm package:linux # Linux AppImage (x64 + arm64)Packaging runs through packages/client/scripts/with-env.mjs, which loads the monorepo-root
.env into the environment before invoking electron-builder. Values already present in the
environment (e.g. CI-provided ones) always win over .env.
Copy .env.example to .env for local development. Only OAUTH_CLIENT_ID is required; the
rest are optional and used only for signed/published release builds.
| Variable | Required | Purpose | Local | GitHub Actions |
|---|---|---|---|---|
OAUTH_CLIENT_ID |
yes | GitHub OAuth App client id, baked into the app at build time | .env |
Variable or Secret |
APP_ID |
no | Override the app/bundle id (defaults to electron-builder.yml's appId) |
.env |
Variable or Secret |
CSC_LINK |
no | Code-signing cert — .p12 path or base64 string |
.env |
Secret or Variable |
CSC_KEY_PASSWORD |
no | Code-signing cert password | .env |
Secret or Variable |
APPLE_CERTIFICATE |
no | Alias for CSC_LINK, useful with Apple-named CI templates |
.env |
Secret or Variable |
APPLE_CERTIFICATE_PASSWORD |
no | Alias for CSC_KEY_PASSWORD, useful with Apple-named CI templates |
.env |
Secret or Variable |
APPLE_API_KEY |
no | App Store Connect API key — AuthKey_*.p8 path, pasted PEM, or base64 string (notarization) |
.env |
Secret or Variable |
APPLE_API_KEY_ID |
no | App Store Connect API key id (notarization) | .env |
Variable or Secret |
APPLE_API_ISSUER |
no | App Store Connect issuer id (notarization) | .env |
Variable or Secret |
APPLE_TEAM_ID |
no | Apple Developer Team ID (notarization) | .env |
Variable or Secret |
R2_ACCOUNT_ID |
no | Cloudflare account id (R2 upload) | .env |
Variable or Secret |
R2_BUCKET |
no | R2 bucket name (publish uploads here; also gates the R2 step) | .env |
Variable or Secret |
R2_PUBLIC_BASE_URL |
no | Public URL the R2 files are served from; also triggers latest*.yml (update manifest) generation |
.env |
Variable or Secret |
R2_ACCESS_KEY_ID |
no | R2 S3 access key id | .env |
Secret or Variable |
R2_SECRET_ACCESS_KEY |
no | R2 S3 secret access key | .env |
Secret or Variable |
OH_MY_GITHUB_CDP_PORT |
no | Dev-only Chrome DevTools Protocol port (see README) | env | — |
HTTPS_PROXY / HTTP_PROXY / ALL_PROXY |
no | Dev network proxy (see README) | env | — |
Variable vs Secret: non-sensitive values usually go in repo/Org Variables (
vars.*), credentials should go in Secrets (secrets.*). The build and publish workflows accept custom CI values from either place, preferring Secrets when both are set.GITHUB_TOKENis provided automatically.
Three workflows under .github/workflows/:
| Workflow | Trigger | What it does |
|---|---|---|
ci.yml |
push to main, every PR |
pnpm -r typecheck + pnpm -r test |
build.yml |
every PR to main, manual dispatch |
Test-packages macOS / Windows / Linux and uploads the installers as artifacts (unsigned) |
publish.yml |
push of a v* tag |
Packages all platforms → changelogithub generates the changelog and creates the GitHub Release → installers are attached to the release and uploaded to Cloudflare R2 |
# bump the version (root + packages/client package.json), commit, then:
git tag v0.2.0
git push origin v0.2.0The tag push triggers publish.yml. Installers land on the GitHub Release and, when the R2
variables/secrets are configured, in the R2 bucket for public download.
The repository is private, so GitHub Release assets are not publicly downloadable. The publish
workflow therefore also uploads installers to a Cloudflare R2 bucket (S3-compatible, zero
egress fees). Make the bucket public via its *.r2.dev URL or a custom domain, set the R2_*
variables/secrets, and share R2_PUBLIC_BASE_URL as the download link. If the R2 variables are
absent the workflow simply skips the upload.
When R2_PUBLIC_BASE_URL is set, the build also emits the update manifest
(latest.yml / latest-mac.yml / latest-linux.yml) and bundles an app-update.yml pointing
at that URL. These are uploaded alongside the installers (manifests with Cache-Control: no-cache).
There is no in-app auto-updater yet — the manifest is published so a future manual "check for
updates" can read the latest version from R2.
Builds are unsigned by default. To sign:
- macOS / Windows: set
CSC_LINK+CSC_KEY_PASSWORD(orAPPLE_CERTIFICATE+APPLE_CERTIFICATE_PASSWORD). The build script auto-signs when a cert is present (and stays unsigned otherwise). - macOS notarization: additionally set
APPLE_API_KEY,APPLE_API_KEY_ID,APPLE_API_ISSUER, andAPPLE_TEAM_ID. The build script enablesmac.notarizeonly when signing credentials and these App Store Connect API credentials are present.
- Commit messages and PR titles follow Conventional Commits, in English
(
changelogithubderives the release notes from them). - See
AGENTS.mdfor repository conventions and the frontend design language.