Skip to content

Latest commit

 

History

History
66 lines (41 loc) · 5.71 KB

build.md

File metadata and controls

66 lines (41 loc) · 5.71 KB

Install and build

The package isn't published to a registry. Install it from Git at a pinned commit:

npm install github:githubnext/desktop-tools#<commit>

dist/ is checked in, so installing runs no build step. Native builds need macOS with Xcode's SDK and Swift 6.2 or later, Git, and Node 22 or later.

Build the native artifacts with the installed binary, never through a registry lookup:

npm exec --no -- desktop-tools-build --out <directory> --scratch <directory>

From a clone:

git clone https://github.com/githubnext/desktop-tools && cd desktop-tools
npm ci
npm run build:native          # writes build/, scratch in native2/.build
npm run build:js && npm run check   # after editing src/

The build compiles the pure Swift package in native2/ (#143) in release mode. It has no dependencies outside the macOS SDK.

--out receives libDesktopTools.dylib (the embedding runtime, with the C ABI in embed.md), desktop-tools-client, desktop-tools-mcp (the Swift MCP server) and desktop-tools-app (the Desktop Tools app host), and Licenses/<name>/ notices for this package and for Peekaboo and AXorcist, whose code parts of native2/ were ported from. Existing files in <out>/Licenses are kept, except notices a build of the removed Peekaboo stack left there for dependencies this build doesn't contain.

Give each concurrent build its own --scratch directory, or run builds one at a time. The default scratch is native2/.build inside the package, which is not suitable for an installed dependency. --native2, which chose this build while the Peekaboo stack shipped beside it, is accepted and changes nothing.

The build does not sign or stage the Swift runtime. Embedding apps own both. If an app's deployment target needs bundled Swift libraries, stage them with xcrun swift-stdlib-tool --copy into Contents/Frameworks; binaries carry an @loader_path/../Frameworks rpath for that.

CI checks that dist/ matches a fresh compile and that the modules import on Linux. It also builds the native artifacts, unsigned, on macOS with Xcode 26.2.

Development app

Test native changes in the real app with:

npm run dev-app            # build, sign, install and open
npm run dev-app -- --stop  # quit it

The app bundles the Swift MCP server, Desktop Tools Dev.app/Contents/MacOS/desktop-tools-mcp, which finds the app's socket and opens the app in the background when nothing serves it. DESKTOP_TOOLS_STATE=~/.local/state/desktop-tools-dev node dist/mcp.js hands over to it.

Every checkout and worktree installs the same ~/Library/Application Support/desktop-tools/dev/Desktop Tools Dev.app. It has its own bundle identity, dev.githubnext.desktop-tools.dev, nested under the release app's, and the name Desktop Tools Dev. Building quits the running copy, replaces the bundle in place and opens it again through LaunchServices with open. Launching its executable directly can attribute permission checks to the launching terminal or agent instead of the app.

This exists because macOS ties permissions to the app. The Accessibility, Screen Recording and Keyboard input grants belong to a bundle identity and its signing requirement. Screen capture also needs a separate, dated approval to "bypass the system private window picker", which macOS appears to ask for again for each copy of an app that captures, even when the grant is on. Development builds therefore never use the release identity, which would swap the installed app's binaries under its grants, and never run from scattered build folders, each of which would ask again. Grant Desktop Tools Dev once in System Settings, and approve its capture prompt once; rebuilds keep both.

The app serves ~/.local/state/desktop-tools-dev/desktop.sock, the default for the development identity, or DESKTOP_TOOLS_STATE when set at build time, which is passed with open --env. Point the MCP server or npm run live-check at the same folder with DESKTOP_TOOLS_STATE. Its approved agents are kept in that folder too, so the development app asks about each agent once, separately from the installed app.

Every build is signed with one identity, so the designated requirement stays the same across rebuilds: DESKTOP_TOOLS_CODESIGN_IDENTITY when set, otherwise the repository's Git setting desktopTools.codesignIdentity. Set the Git value once per Mac to the SHA-1 of an Apple Development or Developer ID identity from security find-identity -v -p codesigning, since several certificates can share a name:

git config desktopTools.codesignIdentity <SHA-1>

It lives in the repository's .git/config, which all worktrees share, and is never committed. Ad-hoc signing (-) is refused, because it can't keep permissions across builds. Builds use native2/.build as scratch unless you pass --scratch <directory>; give concurrent builds their own. Don't install a development build in /Applications.

Releases

Each change that users would notice adds a bullet under ## [Unreleased] in CHANGELOG.md, under Added, Changed or Fixed, with its PR link. To cut a release:

  1. In one PR, bump version in package.json and package-lock.json, update the #v… tag pinned in README.md and docs/mcp.md, and rename ## [Unreleased] to ## [X.Y.Z] - YYYY-MM-DD above a new, empty ## [Unreleased]. Update the compare links at the bottom of the file. npm test fails if the version has no entries.
  2. After it merges, run the release workflow with publish: gh workflow run release.yml -f publish=true. It publishes v plus the version, using that version's CHANGELOG section (node scripts/changelog.ts) as the release notes, and stops before building if the section is missing.