Skip to content

Commit b8764bf

Browse files
committed
feat(desktop): add Windows support
Source: per-simmons/damon-ade#9
1 parent db2aaa3 commit b8764bf

43 files changed

Lines changed: 1049 additions & 201 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/build-desktop.yml

Lines changed: 103 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ jobs:
4646
- name: Setup Bun
4747
uses: oven-sh/setup-bun@v1
4848
with:
49-
bun-version: "1.3.2"
49+
bun-version: "1.3.6"
5050

5151
- name: Cache dependencies
5252
uses: actions/cache@v4
@@ -152,7 +152,7 @@ jobs:
152152
- name: Setup Bun
153153
uses: oven-sh/setup-bun@v1
154154
with:
155-
bun-version: "1.3.2"
155+
bun-version: "1.3.6"
156156

157157
- name: Cache dependencies
158158
uses: actions/cache@v4
@@ -234,3 +234,104 @@ jobs:
234234
path: apps/desktop/release/*-linux.yml
235235
retention-days: ${{ inputs.artifact_retention_days }}
236236
if-no-files-found: error
237+
238+
build-windows:
239+
name: Build - Windows (x64)
240+
runs-on: windows-latest
241+
environment: production
242+
243+
steps:
244+
- name: Checkout code
245+
uses: actions/checkout@v4
246+
247+
- name: Setup Bun
248+
uses: oven-sh/setup-bun@v1
249+
with:
250+
bun-version: "1.3.6"
251+
252+
- name: Cache dependencies
253+
uses: actions/cache@v4
254+
with:
255+
path: |
256+
~/.bun/install/cache
257+
key: ${{ runner.os }}-bun-${{ github.sha }}
258+
restore-keys: |
259+
${{ runner.os }}-bun-
260+
261+
- name: Install dependencies
262+
run: bun install --frozen
263+
264+
- name: Set version suffix
265+
if: inputs.version_suffix != ''
266+
working-directory: apps/desktop
267+
shell: bash
268+
run: |
269+
CURRENT_VERSION=$(node -p "require('./package.json').version")
270+
NEW_VERSION="${CURRENT_VERSION}${{ inputs.version_suffix }}"
271+
echo "Setting version to: $NEW_VERSION"
272+
node -e "
273+
const fs = require('fs');
274+
const pkg = require('./package.json');
275+
pkg.version = '$NEW_VERSION';
276+
fs.writeFileSync('./package.json', JSON.stringify(pkg, null, '\t') + '\n');
277+
"
278+
echo "Updated package.json version to $NEW_VERSION"
279+
280+
- name: Clean dev folder
281+
working-directory: apps/desktop
282+
run: bun run clean:dev
283+
284+
- name: Compile app with electron-vite
285+
working-directory: apps/desktop
286+
env:
287+
NEXT_PUBLIC_POSTHOG_KEY: ${{ secrets.NEXT_PUBLIC_POSTHOG_KEY }}
288+
NEXT_PUBLIC_POSTHOG_HOST: ${{ secrets.NEXT_PUBLIC_POSTHOG_HOST }}
289+
GOOGLE_CLIENT_ID: ${{ secrets.GOOGLE_CLIENT_ID }}
290+
GH_CLIENT_ID: ${{ secrets.GH_CLIENT_ID }}
291+
NEXT_PUBLIC_WEB_URL: ${{ secrets.NEXT_PUBLIC_WEB_URL }}
292+
NEXT_PUBLIC_API_URL: ${{ secrets.NEXT_PUBLIC_API_URL }}
293+
NEXT_PUBLIC_DOCS_URL: ${{ secrets.NEXT_PUBLIC_DOCS_URL }}
294+
NEXT_PUBLIC_STREAMS_URL: ${{ secrets.NEXT_PUBLIC_STREAMS_URL }}
295+
NEXT_PUBLIC_ELECTRIC_URL: ${{ secrets.NEXT_PUBLIC_ELECTRIC_URL }}
296+
SENTRY_DSN_DESKTOP: ${{ secrets.SENTRY_DSN_DESKTOP }}
297+
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
298+
SUPERSET_WORKSPACE_NAME: superset
299+
run: bun run compile:app
300+
301+
- name: Build Electron app
302+
working-directory: apps/desktop
303+
env:
304+
CSC_IDENTITY_AUTO_DISCOVERY: false
305+
WIN_CSC_LINK: ${{ secrets.WIN_CSC_LINK }}
306+
WIN_CSC_KEY_PASSWORD: ${{ secrets.WIN_CSC_KEY_PASSWORD }}
307+
CSC_LINK: ${{ secrets.WIN_CSC_LINK }}
308+
CSC_KEY_PASSWORD: ${{ secrets.WIN_CSC_KEY_PASSWORD }}
309+
run: bun run package -- --publish never --config ${{ inputs.electron_builder_config }}
310+
311+
- name: Verify Windows installer + update manifest exist
312+
working-directory: apps/desktop
313+
shell: pwsh
314+
run: |
315+
Get-ChildItem release
316+
if (-not (Get-ChildItem release -Filter "*.exe" -File)) {
317+
Write-Error "No Windows installer generated in apps/desktop/release"
318+
}
319+
if (-not (Test-Path release/latest.yml)) {
320+
Write-Error "No Windows auto-update manifest generated in apps/desktop/release"
321+
}
322+
323+
- name: Upload Windows installer artifact
324+
uses: actions/upload-artifact@v4
325+
with:
326+
name: ${{ inputs.artifact_prefix }}-windows-installer
327+
path: apps/desktop/release/*.exe
328+
retention-days: ${{ inputs.artifact_retention_days }}
329+
if-no-files-found: error
330+
331+
- name: Upload Windows auto-update manifest
332+
uses: actions/upload-artifact@v4
333+
with:
334+
name: ${{ inputs.artifact_prefix }}-windows-update-manifest
335+
path: apps/desktop/release/latest.yml
336+
retention-days: ${{ inputs.artifact_retention_days }}
337+
if-no-files-found: error

.github/workflows/release-desktop-canary.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,13 @@ jobs:
106106
echo "Created canary copy: ADE-Canary-${arch}.AppImage"
107107
fi
108108
done
109+
for file in *.exe; do
110+
if [[ -f "$file" && "$file" != *.__uninstaller.exe ]]; then
111+
arch=$(echo "$file" | sed -E 's/.*-([^-]+)\.exe$/\1/')
112+
cp "$file" "ADE-Canary-${arch}.exe"
113+
echo "Created canary copy: ADE-Canary-${arch}.exe"
114+
fi
115+
done
109116
# Prerelease builds may request canary-linux.yml; keep latest-linux.yml as fallback.
110117
for file in *-linux.yml; do
111118
if [[ -f "$file" && "$file" != "canary-linux.yml" && "$file" != "latest-linux.yml" ]]; then
@@ -115,6 +122,11 @@ jobs:
115122
break
116123
fi
117124
done
125+
# Windows generic provider uses latest.yml by default and may request canary.yml for prereleases.
126+
if [[ -f latest.yml && ! -f canary.yml ]]; then
127+
cp latest.yml canary.yml
128+
echo "Created Windows canary manifest: canary.yml"
129+
fi
118130
119131
- name: List artifacts
120132
run: |

.github/workflows/release-desktop.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,14 @@ jobs:
8686
echo "Created stable copy: ADE-${arch}.AppImage"
8787
fi
8888
done
89+
for file in *.exe; do
90+
if [[ -f "$file" && "$file" != *.__uninstaller.exe ]]; then
91+
# Extract architecture from filename (e.g., ADE-1.0.3-x64.exe -> x64)
92+
arch=$(echo "$file" | sed -E 's/.*-([^-]+)\.exe$/\1/')
93+
cp "$file" "ADE-${arch}.exe"
94+
echo "Created stable copy: ADE-${arch}.exe"
95+
fi
96+
done
8997
# Keep Linux updater manifest at a stable filename for generic provider lookups.
9098
for file in *-linux.yml; do
9199
if [[ -f "$file" && "$file" != "latest-linux.yml" ]]; then

README.md

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# ADE
22

3-
An agentic development environment for macOS. ADE is a local-first, single-user desktop app where you build a roster of persistent coding agents and work alongside them in the terminal. Every agent is a durable identity — its own name, photo, git repository, runtime CLI, and long-lived memory — not a throwaway chat session. You come back to the same agent tomorrow and it remembers what it learned today.
3+
An agentic development environment for macOS, Windows, and Linux. ADE is a local-first, single-user desktop app where you build a roster of persistent coding agents and work alongside them in the terminal. Every agent is a durable identity — its own name, photo, git repository, runtime CLI, and long-lived memory — not a throwaway chat session. You come back to the same agent tomorrow and it remembers what it learned today.
44

55
The interface is a two-level left rail. **Teams** group your work (a name and a square photo); inside each team live **Agents** (a name and a circular photo). Selecting an agent opens its workspace: a strip of **session** tabs, each a real terminal running the agent's coding CLI inside that agent's own git worktree. A **model bar** under the tabs lets you spawn a session on a different model without leaving the agent. On the right, the **Agent Files** panel shows the agent's memory growing as it works.
66

@@ -16,16 +16,19 @@ ADE runs whatever CLI coding agents you already have installed. Claude Code, Ope
1616

1717
### Download (recommended)
1818

19-
Download the signed DMG from the [latest release](https://github.com/per-simmons/damon-ade/releases/latest), open it, and drag ADE to your Applications folder. macOS only.
19+
Download the installer for your platform from the [latest release](https://github.com/per-simmons/damon-ade/releases/latest):
2020

21+
- macOS: signed `.dmg`
22+
- Windows: `ADE-<version>-x64.exe`
23+
- Linux: `.AppImage`
2124

2225
### Build from source
2326

24-
Requires [Bun](https://bun.sh) 1.0+.
27+
Requires [Bun](https://bun.sh) 1.3.6+.
2528

2629
```bash
2730
git clone https://github.com/per-simmons/damon-ade.git
28-
cd REPO
31+
cd damon-ade
2932
bun install
3033
cd apps/desktop
3134
bun run compile:app # builds main + preload + renderer into dist/
@@ -34,11 +37,19 @@ bunx electron . # launches the built app
3437

3538
`compile:app` runs the full production build; `bunx electron .` then launches it directly. (Avoid `electron-vite preview` for a full run — it can exhaust memory.)
3639

40+
To package a desktop installer from `apps/desktop`, run:
41+
42+
```bash
43+
bun run build
44+
```
45+
46+
On Windows, the installer is written to `apps/desktop/release/ADE-<version>-x64.exe`.
47+
3748
## Prerequisites
3849

3950
ADE orchestrates coding CLIs; it does not bundle them. You need:
4051

41-
- **Git** — required. Each agent gets its own repository or worktree. Install Apple's command line tools with `xcode-select --install`.
52+
- **Git** — required. Each agent gets its own repository or worktree. On macOS, install Apple's command line tools with `xcode-select --install`; on Windows, install [Git for Windows](https://git-scm.com/download/win).
4253
- **At least one agent CLI.** Claude Code is recommended, because it also powers the Kimi, MiniMax, and GLM sessions from the model bar (they run Claude Code pointed at OpenRouter):
4354

4455
```bash
@@ -75,7 +86,7 @@ ADE orchestrates coding CLIs; it does not bundle them. You need:
7586

7687
**6. Switch models from the model bar.** Below the session tabs is a quiet row of model logos: **Claude** (the default), **OpenAI** (Codex on GPT-5.5), **Kimi K2.7**, **MiniMax M3**, and **GLM 5.2**. Click any logo to open a new session in the current agent's worktree running that model — the same code, a different model, no context switch.
7788

78-
**7. Connect OpenRouter (first open model only).** The first time you click Kimi, MiniMax, or GLM, ADE asks for your OpenRouter API key (get one at [openrouter.ai/keys](https://openrouter.ai/keys)). Paste it and choose **Save & Launch**. The key is encrypted with the macOS keychain, stored locally, and injected only into the agent's terminal — it never leaves your machine and is never shown back to the app's UI. You enter it once; later open-model sessions launch straight away.
89+
**7. Connect OpenRouter (first open model only).** The first time you click Kimi, MiniMax, or GLM, ADE asks for your OpenRouter API key (get one at [openrouter.ai/keys](https://openrouter.ai/keys)). Paste it and choose **Save & Launch**. The key is encrypted with the OS credential store, stored locally, and injected only into the agent's terminal — it never leaves your machine and is never shown back to the app's UI. You enter it once; later open-model sessions launch straight away.
7990

8091
**8. Watch the memory grow.** The **Agent Files** panel on the right lists the agent's memory surface, grouped into **Memory**, **Skills**, and **Worktree**. It starts nearly empty and fills in as the agent learns — its identity, your profile, its notes, and any skills it writes for itself. Click a file to open it in a viewer tab.
8192

apps/admin/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"scripts": {
77
"build": "next build",
88
"clean": "git clean -xdf .cache .next .turbo node_modules",
9-
"dev": "dotenv -e ../../.env -- sh -c 'next dev --port ${ADMIN_PORT:-3003}'",
9+
"dev": "dotenv -e ../../.env -- bun run ../../scripts/next-dev.ts ADMIN_PORT 3003",
1010
"start": "next start",
1111
"typecheck": "tsc --noEmit"
1212
},

apps/api/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"scripts": {
77
"build": "next build",
88
"clean": "git clean -xdf .cache .next .turbo node_modules",
9-
"dev": "dotenv -e ../../.env -- sh -c 'next dev --port ${API_PORT:-3001}'",
9+
"dev": "dotenv -e ../../.env -- bun run ../../scripts/next-dev.ts API_PORT 3001",
1010
"start": "next start --port 3001",
1111
"typecheck": "tsc --noEmit"
1212
},

apps/desktop/BUILDING.md

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,24 @@ This skips environment variable validation and the sign-in screen, useful for lo
1010

1111
# Release
1212

13-
When building for release, make sure `node-pty` is built for the correct architecture with `bun run install:deps`, then run `bun run release`.
13+
When building for release, run `bun run prebuild` first so native modules are copied into the desktop package and validated, then run `bun run release`.
14+
15+
# Windows (NSIS) local build
16+
17+
From `apps/desktop` in PowerShell, cmd, or Git Bash:
18+
19+
```bash
20+
bun run clean:dev
21+
bun run compile:app
22+
bun run package -- --publish never --config electron-builder.ts
23+
```
24+
25+
Expected outputs in `apps/desktop/release/`:
26+
27+
- `ADE-<version>-x64.exe`
28+
- `latest.yml` (Windows auto-update manifest)
29+
30+
Unsigned local Windows builds leave executable signing/resource editing off by default unless Windows signing credentials are configured. Set `ADE_WIN_EDIT_EXECUTABLE=true` to force executable metadata/icon editing in a Developer Mode or elevated shell.
1431

1532
# Linux (AppImage) local build
1633

@@ -36,4 +53,4 @@ ls -la release/*.AppImage
3653
ls -la release/*-linux.yml
3754
```
3855

39-
If both files exist, packaging produced the Linux artifact + updater metadata that `electron-updater` expects.
56+
If both files exist, packaging produced the Linux artifact + updater metadata that `electron-updater` expects.

apps/desktop/RELEASE.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,10 @@ Releases. Replace `ade` (and confirm the owner) below:
6060

6161
- **macOS manifest**: `https://github.com/per-simmons/damon-ade/releases/latest/download/latest-mac.yml`
6262
- **Linux manifest**: `https://github.com/per-simmons/damon-ade/releases/latest/download/latest-linux.yml`
63+
- **Windows manifest**: `https://github.com/per-simmons/damon-ade/releases/latest/download/latest.yml`
6364
- **macOS installer**: `https://github.com/per-simmons/damon-ade/releases/latest/download/ADE-arm64.dmg`
6465
- **Linux installer**: `https://github.com/per-simmons/damon-ade/releases/latest/download/ADE-x64.AppImage`
66+
- **Windows installer**: `https://github.com/per-simmons/damon-ade/releases/latest/download/ADE-x64.exe`
6567

6668
The workflow creates stable-named copies (without version) so these URLs always point to the latest build.
6769

@@ -75,6 +77,12 @@ macOS code signing uses these repository secrets:
7577
- `MAC_CERTIFICATE` / `MAC_CERTIFICATE_PASSWORD`
7678
- `APPLE_ID` / `APPLE_ID_PASSWORD` / `APPLE_TEAM_ID`
7779

80+
Windows code signing uses the standard electron-builder variables when present:
81+
82+
- `WIN_CSC_LINK` / `WIN_CSC_KEY_PASSWORD`
83+
- or `CSC_LINK` / `CSC_KEY_PASSWORD`
84+
- or Azure Trusted Signing variables
85+
7886
## Local Testing
7987

8088
```bash
@@ -91,8 +99,15 @@ Linux output should include:
9199
- `*.AppImage`
92100
- `*-linux.yml` (auto-update manifest)
93101

102+
Windows output should include:
103+
104+
- `ADE-<version>-x64.exe`
105+
- `latest.yml` (auto-update manifest)
106+
94107
## Troubleshooting
95108

96109
- **Linux auto-update not working**: Verify `release/*-linux.yml` is uploaded to the GitHub release
110+
- **Windows auto-update not working**: Verify `release/latest.yml` and the matching `.exe` are uploaded to the GitHub release
97111
- **Build icon warnings/failures**: Add icons under `src/resources/build/icons/` (`icon.icns`, `icon.ico`, optional Linux `.png`)
98112
- **Native module errors**: Ensure `node-pty` is in externals in both `electron.vite.config.ts` and `electron-builder.ts`
113+
- **Windows symlink errors while packaging**: Run from Developer Mode/elevated shell, or leave executable editing disabled for unsigned local builds

apps/desktop/create-release.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,7 @@ else
441441
echo -e "${BLUE}Direct download:${NC}"
442442
echo "${LATEST_URL}/download/ADE-arm64.dmg"
443443
echo "${LATEST_URL}/download/ADE-x64.AppImage"
444+
echo "${LATEST_URL}/download/ADE-x64.exe"
444445
echo ""
445446
else
446447
success "Draft release created!"

apps/desktop/electron-builder.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,15 @@ const RELEASE_REPO_NAME = "damon-ade"; // TODO(release): set public repo name
2525
// notarytool. Unsigned local smoke-test builds leave APPLE_TEAM_ID unset and
2626
// skip notarization automatically.
2727
const notarize = Boolean(process.env.APPLE_TEAM_ID);
28+
const hasWindowsSigningConfig = Boolean(
29+
process.env.WIN_CSC_LINK ||
30+
process.env.CSC_LINK ||
31+
process.env.WIN_CSC_NAME ||
32+
process.env.CSC_NAME ||
33+
process.env.AZURE_TENANT_ID,
34+
);
35+
const shouldSignAndEditWindowsExecutable =
36+
hasWindowsSigningConfig || process.env.ADE_WIN_EDIT_EXECUTABLE === "true";
2837
const macIconPath = join(pkg.resources, "build/icons/icon.icns");
2938
const linuxIconPath = join(pkg.resources, "build/icons");
3039
const winIconPath = join(pkg.resources, "build/icons/icon.ico");
@@ -151,8 +160,10 @@ const config: Configuration = {
151160
"!**/.DS_Store",
152161
],
153162

154-
// Rebuild native modules for Electron's Node.js version
155-
npmRebuild: true,
163+
// Native modules are prepared by scripts/copy-native-modules.ts and
164+
// validated before packaging. Rebuilding here forces node-gyp for node-pty
165+
// on Windows even though node-pty ships prebuilds for win32-x64/win32-arm64.
166+
npmRebuild: false,
156167

157168
// macOS
158169
mac: {
@@ -211,6 +222,7 @@ const config: Configuration = {
211222
// Windows
212223
win: {
213224
...(existsSync(winIconPath) ? { icon: winIconPath } : {}),
225+
signAndEditExecutable: shouldSignAndEditWindowsExecutable,
214226
target: [
215227
{
216228
target: "nsis",

0 commit comments

Comments
 (0)