From 0bf49edc5aa64a83afe627b5540f70893b90b1a3 Mon Sep 17 00:00:00 2001 From: davebulaval Date: Fri, 27 Mar 2026 11:59:23 -0400 Subject: [PATCH 1/8] feat: add Ubuntu/Linux build support (AppImage + deb) Add electron-builder Linux configuration with AppImage and deb targets, npm scripts for Linux builds, and a GitHub Actions workflow that builds on ubuntu-latest and uploads artifacts to releases. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/build-linux.yml | 63 +++++++++++++++++++++++++++++++ package.json | 33 +++++++++++++++- 2 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/build-linux.yml diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml new file mode 100644 index 00000000..1a8f383d --- /dev/null +++ b/.github/workflows/build-linux.yml @@ -0,0 +1,63 @@ +name: Build - Linux + +on: + push: + tags: + - 'v*' + workflow_dispatch: + +jobs: + build-linux: + name: Build Linux (Ubuntu) + runs-on: ubuntu-latest + + strategy: + matrix: + arch: [x64] + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Install Linux build dependencies + run: | + sudo apt-get update + sudo apt-get install -y libarchive-tools + + - name: Build Electron app for Linux + run: npm run electron:build:linux + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Upload AppImage artifact + uses: actions/upload-artifact@v4 + with: + name: Dorothy-Linux-AppImage-${{ matrix.arch }} + path: release/*.AppImage + if-no-files-found: warn + + - name: Upload deb artifact + uses: actions/upload-artifact@v4 + with: + name: Dorothy-Linux-deb-${{ matrix.arch }} + path: release/*.deb + if-no-files-found: warn + + - name: Upload to GitHub Release + if: startsWith(github.ref, 'refs/tags/v') + uses: softprops/action-gh-release@v2 + with: + files: | + release/*.AppImage + release/*.deb + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/package.json b/package.json index 14414a2d..4f876981 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,9 @@ "electron:dev": "concurrently \"npm run dev\" \"npm run electron:start\"", "electron:start": "wait-on http://localhost:3000 && tsc -p electron/tsconfig.json && NODE_ENV=development electron .", "electron:build": "bash -c 'set -e; mv src/app/api src/app/_api_backup; mv src/app/icon.tsx src/app/_icon_backup.tsx 2>/dev/null || true; trap \"mv src/app/_api_backup src/app/api 2>/dev/null; mv src/app/_icon_backup.tsx src/app/icon.tsx 2>/dev/null\" EXIT; ELECTRON_BUILD=1 next build; tsc -p electron/tsconfig.json; cd mcp-orchestrator && npm install && npm run build && cd ..; cd mcp-telegram && npm install && npm run build && cd ..; cd mcp-kanban && npm install && npm run build && cd ..; cd mcp-vault && npm install && npm run build && cd ..; cd mcp-socialdata && npm install && npm run build && cd ..; cd mcp-x && npm install && npm run build && cd ..; cd mcp-world && npm install && npm run build && cd ..; electron-builder --mac'", - "electron:pack": "tsc -p electron/tsconfig.json && cd mcp-orchestrator && npm install && npm run build && cd .. && cd mcp-telegram && npm install && npm run build && cd .. && cd mcp-kanban && npm install && npm run build && cd .. && cd mcp-vault && npm install && npm run build && cd .. && cd mcp-socialdata && npm install && npm run build && cd .. && cd mcp-x && npm install && npm run build && cd .. && cd mcp-world && npm install && npm run build && cd .. && electron-builder --dir --mac" + "electron:build:linux": "bash -c 'set -e; mv src/app/api src/app/_api_backup; mv src/app/icon.tsx src/app/_icon_backup.tsx 2>/dev/null || true; trap \"mv src/app/_api_backup src/app/api 2>/dev/null; mv src/app/_icon_backup.tsx src/app/icon.tsx 2>/dev/null\" EXIT; ELECTRON_BUILD=1 next build; tsc -p electron/tsconfig.json; cd mcp-orchestrator && npm install && npm run build && cd ..; cd mcp-telegram && npm install && npm run build && cd ..; cd mcp-kanban && npm install && npm run build && cd ..; cd mcp-vault && npm install && npm run build && cd ..; cd mcp-socialdata && npm install && npm run build && cd ..; cd mcp-x && npm install && npm run build && cd ..; cd mcp-world && npm install && npm run build && cd ..; electron-builder --linux'", + "electron:pack": "tsc -p electron/tsconfig.json && cd mcp-orchestrator && npm install && npm run build && cd .. && cd mcp-telegram && npm install && npm run build && cd .. && cd mcp-kanban && npm install && npm run build && cd .. && cd mcp-vault && npm install && npm run build && cd .. && cd mcp-socialdata && npm install && npm run build && cd .. && cd mcp-x && npm install && npm run build && cd .. && cd mcp-world && npm install && npm run build && cd .. && electron-builder --dir --mac", + "electron:pack:linux": "tsc -p electron/tsconfig.json && cd mcp-orchestrator && npm install && npm run build && cd .. && cd mcp-telegram && npm install && npm run build && cd .. && cd mcp-kanban && npm install && npm run build && cd .. && cd mcp-vault && npm install && npm run build && cd .. && cd mcp-socialdata && npm install && npm run build && cd .. && cd mcp-x && npm install && npm run build && cd .. && cd mcp-world && npm install && npm run build && cd .. && electron-builder --dir --linux" }, "build": { "appId": "io.dorothy.app", @@ -117,6 +119,35 @@ "zip" ] }, + "linux": { + "category": "Development", + "icon": "public/icon-512.png", + "target": [ + "AppImage", + "deb" + ], + "desktop": { + "StartupNotify": "true", + "Categories": "Development;IDE;" + } + }, + "appImage": { + "artifactName": "Dorothy-${version}-${arch}.AppImage" + }, + "deb": { + "depends": [ + "libgtk-3-0", + "libnotify4", + "libnss3", + "libxss1", + "libxtst6", + "xdg-utils", + "libatspi2.0-0", + "libuuid1", + "libsecret-1-0" + ], + "artifactName": "Dorothy-${version}-${arch}.deb" + }, "dmg": { "title": "Dorothy", "contents": [ From b2731e6de667ff1377cd400666b7707a4f13940e Mon Sep 17 00:00:00 2001 From: davebulaval Date: Fri, 27 Mar 2026 12:08:29 -0400 Subject: [PATCH 2/8] fix: add deb maintainer field for Linux build Co-Authored-By: Claude Opus 4.6 (1M context) --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 4f876981..f84e18e3 100644 --- a/package.json +++ b/package.json @@ -135,6 +135,7 @@ "artifactName": "Dorothy-${version}-${arch}.AppImage" }, "deb": { + "maintainer": "Charlie85270", "depends": [ "libgtk-3-0", "libnotify4", From 319cd0a9f808effafbdd81d529f27eef2910b4a5 Mon Sep 17 00:00:00 2001 From: davebulaval Date: Sat, 28 Mar 2026 09:05:07 -0400 Subject: [PATCH 3/8] feat: finalize Linux deb build (drop AppImage, add smoke tests, fix EPIPE) - Remove AppImage target, keep .deb only for Ubuntu/Linux - Add CI smoke tests: size check, dpkg-deb metadata, binary presence - Fix EPIPE errors on stdout/stderr for desktop entry launches - Add --no-sandbox flag to .desktop file executable args - Add description and author fields to package.json - Wire after-install script via fpm for chrome-sandbox SUID Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/build-linux.yml | 25 ++++++++++++++++++------- electron/main.ts | 12 ++++++++++++ package.json | 10 +++++----- 3 files changed, 35 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml index 1a8f383d..0560bcd0 100644 --- a/.github/workflows/build-linux.yml +++ b/.github/workflows/build-linux.yml @@ -38,12 +38,24 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Upload AppImage artifact - uses: actions/upload-artifact@v4 - with: - name: Dorothy-Linux-AppImage-${{ matrix.arch }} - path: release/*.AppImage - if-no-files-found: warn + - name: Verify build artifacts + run: | + echo "=== Checking deb ===" + DEB=$(ls release/*.deb 2>/dev/null) + if [ -z "$DEB" ]; then echo "ERROR: No .deb found"; exit 1; fi + DEB_SIZE=$(stat --format=%s "$DEB") + echo "deb: $DEB ($DEB_SIZE bytes)" + if [ "$DEB_SIZE" -lt 52428800 ]; then echo "ERROR: deb too small (<50MB)"; exit 1; fi + + echo "=== Checking deb metadata ===" + dpkg-deb --info "$DEB" + + echo "=== Checking deb is a valid ELF package ===" + MAIN_BIN=$(dpkg-deb --contents "$DEB" | grep -m1 "opt/Dorothy/dorothy$" | awk '{print $NF}') + if [ -z "$MAIN_BIN" ]; then echo "ERROR: dorothy binary not found in deb"; exit 1; fi + echo "Main binary found: $MAIN_BIN" + + echo "=== All checks passed ===" - name: Upload deb artifact uses: actions/upload-artifact@v4 @@ -57,7 +69,6 @@ jobs: uses: softprops/action-gh-release@v2 with: files: | - release/*.AppImage release/*.deb env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/electron/main.ts b/electron/main.ts index 5d692d50..600bd70b 100644 --- a/electron/main.ts +++ b/electron/main.ts @@ -15,6 +15,18 @@ import * as fs from 'fs'; import * as path from 'path'; import * as os from 'os'; +if (process.platform === 'linux') { + // Ignore EPIPE errors on stdout/stderr (happens when launched from desktop entries) + process.stdout?.on?.('error', (err: NodeJS.ErrnoException) => { + if (err.code === 'EPIPE') return; + throw err; + }); + process.stderr?.on?.('error', (err: NodeJS.ErrnoException) => { + if (err.code === 'EPIPE') return; + throw err; + }); +} + // Types import type { AppSettings, AgentStatus } from './types'; diff --git a/package.json b/package.json index f84e18e3..1cefdd59 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,8 @@ { "name": "dorothy", "version": "1.2.5", + "description": "Agent Control Center - manage AI coding agents from a single desktop app", + "author": "Charlie85270", "private": true, "main": "electron/dist/main.js", "scripts": { @@ -123,19 +125,17 @@ "category": "Development", "icon": "public/icon-512.png", "target": [ - "AppImage", "deb" ], "desktop": { "StartupNotify": "true", "Categories": "Development;IDE;" - } - }, - "appImage": { - "artifactName": "Dorothy-${version}-${arch}.AppImage" + }, + "executableArgs": ["--no-sandbox"] }, "deb": { "maintainer": "Charlie85270", + "fpm": ["--after-install", "build/linux/after-install.sh"], "depends": [ "libgtk-3-0", "libnotify4", From 0ad0824105c2984a9ab6a198e4c593fbf3187459 Mon Sep 17 00:00:00 2001 From: davebulaval Date: Sat, 28 Mar 2026 10:16:27 -0400 Subject: [PATCH 4/8] =?UTF-8?q?fix:=20address=20PR=20review=20=E2=80=94=20?= =?UTF-8?q?permissions,=20robust=20deb=20check,=20remove=20--no-sandbox?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add explicit `permissions: contents: write` to workflow for hardened orgs - Use bash array loop for .deb validation (handles multiple artifacts) - Remove `--no-sandbox` from desktop entry; rely on SUID chrome-sandbox set by after-install.sh (chown root + chmod 4755) per Electron guidance - Make after-install.sh idempotent with chown root:root Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/build-linux.yml | 28 +++++++++++++++++----------- build/linux/after-install.sh | 6 ++++++ package.json | 3 +-- 3 files changed, 24 insertions(+), 13 deletions(-) create mode 100644 build/linux/after-install.sh diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml index 0560bcd0..4f860822 100644 --- a/.github/workflows/build-linux.yml +++ b/.github/workflows/build-linux.yml @@ -6,6 +6,9 @@ on: - 'v*' workflow_dispatch: +permissions: + contents: write + jobs: build-linux: name: Build Linux (Ubuntu) @@ -41,19 +44,22 @@ jobs: - name: Verify build artifacts run: | echo "=== Checking deb ===" - DEB=$(ls release/*.deb 2>/dev/null) - if [ -z "$DEB" ]; then echo "ERROR: No .deb found"; exit 1; fi - DEB_SIZE=$(stat --format=%s "$DEB") - echo "deb: $DEB ($DEB_SIZE bytes)" - if [ "$DEB_SIZE" -lt 52428800 ]; then echo "ERROR: deb too small (<50MB)"; exit 1; fi + shopt -s nullglob + DEBS=(release/*.deb) + if [ ${#DEBS[@]} -eq 0 ]; then echo "ERROR: No .deb found"; exit 1; fi + for DEB in "${DEBS[@]}"; do + DEB_SIZE=$(stat --format=%s "$DEB") + echo "deb: $DEB ($DEB_SIZE bytes)" + if [ "$DEB_SIZE" -lt 52428800 ]; then echo "ERROR: deb too small (<50MB): $DEB"; exit 1; fi - echo "=== Checking deb metadata ===" - dpkg-deb --info "$DEB" + echo "=== Checking deb metadata ===" + dpkg-deb --info "$DEB" - echo "=== Checking deb is a valid ELF package ===" - MAIN_BIN=$(dpkg-deb --contents "$DEB" | grep -m1 "opt/Dorothy/dorothy$" | awk '{print $NF}') - if [ -z "$MAIN_BIN" ]; then echo "ERROR: dorothy binary not found in deb"; exit 1; fi - echo "Main binary found: $MAIN_BIN" + echo "=== Checking deb contains dorothy binary ===" + MAIN_BIN=$(dpkg-deb --contents "$DEB" | grep -m1 "opt/Dorothy/dorothy$" | awk '{print $NF}') + if [ -z "$MAIN_BIN" ]; then echo "ERROR: dorothy binary not found in deb"; exit 1; fi + echo "Main binary found: $MAIN_BIN" + done echo "=== All checks passed ===" diff --git a/build/linux/after-install.sh b/build/linux/after-install.sh new file mode 100644 index 00000000..db9d3d88 --- /dev/null +++ b/build/linux/after-install.sh @@ -0,0 +1,6 @@ +#!/bin/bash +# Set SUID bit on chrome-sandbox for Electron's sandbox to work. +# Required for Chromium's SUID sandbox on Linux (avoids --no-sandbox). +# Idempotent: safe to run multiple times. +chown root:root /opt/Dorothy/chrome-sandbox +chmod 4755 /opt/Dorothy/chrome-sandbox diff --git a/package.json b/package.json index 1cefdd59..bfd50f16 100644 --- a/package.json +++ b/package.json @@ -130,8 +130,7 @@ "desktop": { "StartupNotify": "true", "Categories": "Development;IDE;" - }, - "executableArgs": ["--no-sandbox"] + } }, "deb": { "maintainer": "Charlie85270", From 81697a92e2b5cb2019712e93647923e1205a6c2f Mon Sep 17 00:00:00 2001 From: davebulaval Date: Tue, 31 Mar 2026 18:43:11 -0400 Subject: [PATCH 5/8] =?UTF-8?q?fix:=20address=20review=20=E2=80=94=20drop?= =?UTF-8?q?=20optional=20chaining,=20robust=20CI=20check,=20cleanup=20on?= =?UTF-8?q?=20uninstall?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove unnecessary optional chaining on process.stdout/stderr (always defined in Node.js) - Replace magic 50MB size threshold with non-zero binary size check in CI smoke tests - Add before-remove.sh to strip SUID bit from chrome-sandbox on package uninstall (security hygiene) - Wire before-remove.sh into deb fpm config Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/build-linux.yml | 10 +++++----- build/linux/before-remove.sh | 6 ++++++ electron/main.ts | 4 ++-- package.json | 2 +- 4 files changed, 14 insertions(+), 8 deletions(-) create mode 100644 build/linux/before-remove.sh diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml index 4f860822..815af464 100644 --- a/.github/workflows/build-linux.yml +++ b/.github/workflows/build-linux.yml @@ -48,17 +48,17 @@ jobs: DEBS=(release/*.deb) if [ ${#DEBS[@]} -eq 0 ]; then echo "ERROR: No .deb found"; exit 1; fi for DEB in "${DEBS[@]}"; do - DEB_SIZE=$(stat --format=%s "$DEB") - echo "deb: $DEB ($DEB_SIZE bytes)" - if [ "$DEB_SIZE" -lt 52428800 ]; then echo "ERROR: deb too small (<50MB): $DEB"; exit 1; fi + echo "deb: $DEB ($(stat --format=%s "$DEB") bytes)" echo "=== Checking deb metadata ===" dpkg-deb --info "$DEB" echo "=== Checking deb contains dorothy binary ===" - MAIN_BIN=$(dpkg-deb --contents "$DEB" | grep -m1 "opt/Dorothy/dorothy$" | awk '{print $NF}') + MAIN_BIN=$(dpkg-deb --contents "$DEB" | grep -m1 "opt/Dorothy/dorothy$") if [ -z "$MAIN_BIN" ]; then echo "ERROR: dorothy binary not found in deb"; exit 1; fi - echo "Main binary found: $MAIN_BIN" + BIN_SIZE=$(echo "$MAIN_BIN" | awk '{print $3}') + echo "Main binary: $(echo "$MAIN_BIN" | awk '{print $NF}') ($BIN_SIZE bytes)" + if [ "$BIN_SIZE" -eq 0 ]; then echo "ERROR: dorothy binary is zero-size"; exit 1; fi done echo "=== All checks passed ===" diff --git a/build/linux/before-remove.sh b/build/linux/before-remove.sh new file mode 100644 index 00000000..5f886997 --- /dev/null +++ b/build/linux/before-remove.sh @@ -0,0 +1,6 @@ +#!/bin/bash +# Remove SUID bit from chrome-sandbox before package removal. +# Ensures no world-executable SUID root binary is left behind. +if [ -f /opt/Dorothy/chrome-sandbox ]; then + chmod 0755 /opt/Dorothy/chrome-sandbox +fi diff --git a/electron/main.ts b/electron/main.ts index 600bd70b..ff93315a 100644 --- a/electron/main.ts +++ b/electron/main.ts @@ -17,11 +17,11 @@ import * as os from 'os'; if (process.platform === 'linux') { // Ignore EPIPE errors on stdout/stderr (happens when launched from desktop entries) - process.stdout?.on?.('error', (err: NodeJS.ErrnoException) => { + process.stdout.on('error', (err: NodeJS.ErrnoException) => { if (err.code === 'EPIPE') return; throw err; }); - process.stderr?.on?.('error', (err: NodeJS.ErrnoException) => { + process.stderr.on('error', (err: NodeJS.ErrnoException) => { if (err.code === 'EPIPE') return; throw err; }); diff --git a/package.json b/package.json index bfd50f16..24c861e3 100644 --- a/package.json +++ b/package.json @@ -134,7 +134,7 @@ }, "deb": { "maintainer": "Charlie85270", - "fpm": ["--after-install", "build/linux/after-install.sh"], + "fpm": ["--after-install", "build/linux/after-install.sh", "--before-remove", "build/linux/before-remove.sh"], "depends": [ "libgtk-3-0", "libnotify4", From c257ae2f99ecc35e64e4437654684b22b2421941 Mon Sep 17 00:00:00 2001 From: davebulaval Date: Tue, 31 Mar 2026 19:17:25 -0400 Subject: [PATCH 6/8] refactor: extract shared electron build steps into scripts/electron-prepare.sh The 4 electron build/pack scripts duplicated ~200 chars of identical logic (API backup, next build, tsc, MCP sub-package builds). Extract into a shared script and simplify each npm script to a one-liner. Co-Authored-By: Claude Opus 4.6 (1M context) --- package.json | 9 +++++---- scripts/electron-prepare.sh | 20 ++++++++++++++++++++ 2 files changed, 25 insertions(+), 4 deletions(-) create mode 100755 scripts/electron-prepare.sh diff --git a/package.json b/package.json index 1fe5eb8d..c73213a6 100644 --- a/package.json +++ b/package.json @@ -16,10 +16,11 @@ "test:coverage": "vitest run --coverage", "electron:dev": "concurrently \"npm run dev\" \"npm run electron:start\"", "electron:start": "wait-on http://localhost:3000 && tsc -p electron/tsconfig.json && NODE_ENV=development electron .", - "electron:build": "bash -c 'set -e; mv src/app/api src/app/_api_backup; mv src/app/icon.tsx src/app/_icon_backup.tsx 2>/dev/null || true; trap \"mv src/app/_api_backup src/app/api 2>/dev/null; mv src/app/_icon_backup.tsx src/app/icon.tsx 2>/dev/null\" EXIT; ELECTRON_BUILD=1 next build; tsc -p electron/tsconfig.json; cd mcp-orchestrator && npm install && npm run build && cd ..; cd mcp-telegram && npm install && npm run build && cd ..; cd mcp-kanban && npm install && npm run build && cd ..; cd mcp-vault && npm install && npm run build && cd ..; cd mcp-socialdata && npm install && npm run build && cd ..; cd mcp-x && npm install && npm run build && cd ..; cd mcp-world && npm install && npm run build && cd ..; electron-builder --mac'", - "electron:build:linux": "bash -c 'set -e; mv src/app/api src/app/_api_backup; mv src/app/icon.tsx src/app/_icon_backup.tsx 2>/dev/null || true; trap \"mv src/app/_api_backup src/app/api 2>/dev/null; mv src/app/_icon_backup.tsx src/app/icon.tsx 2>/dev/null\" EXIT; ELECTRON_BUILD=1 next build; tsc -p electron/tsconfig.json; cd mcp-orchestrator && npm install && npm run build && cd ..; cd mcp-telegram && npm install && npm run build && cd ..; cd mcp-kanban && npm install && npm run build && cd ..; cd mcp-vault && npm install && npm run build && cd ..; cd mcp-socialdata && npm install && npm run build && cd ..; cd mcp-x && npm install && npm run build && cd ..; cd mcp-world && npm install && npm run build && cd ..; electron-builder --linux'", - "electron:pack": "tsc -p electron/tsconfig.json && cd mcp-orchestrator && npm install && npm run build && cd .. && cd mcp-telegram && npm install && npm run build && cd .. && cd mcp-kanban && npm install && npm run build && cd .. && cd mcp-vault && npm install && npm run build && cd .. && cd mcp-socialdata && npm install && npm run build && cd .. && cd mcp-x && npm install && npm run build && cd .. && cd mcp-world && npm install && npm run build && cd .. && electron-builder --dir --mac", - "electron:pack:linux": "tsc -p electron/tsconfig.json && cd mcp-orchestrator && npm install && npm run build && cd .. && cd mcp-telegram && npm install && npm run build && cd .. && cd mcp-kanban && npm install && npm run build && cd .. && cd mcp-vault && npm install && npm run build && cd .. && cd mcp-socialdata && npm install && npm run build && cd .. && cd mcp-x && npm install && npm run build && cd .. && cd mcp-world && npm install && npm run build && cd .. && electron-builder --dir --linux" + "electron:prepare": "bash scripts/electron-prepare.sh", + "electron:build": "npm run electron:prepare && electron-builder --mac", + "electron:build:linux": "npm run electron:prepare && electron-builder --linux", + "electron:pack": "npm run electron:prepare && electron-builder --dir --mac", + "electron:pack:linux": "npm run electron:prepare && electron-builder --dir --linux" }, "build": { "appId": "io.dorothy.app", diff --git a/scripts/electron-prepare.sh b/scripts/electron-prepare.sh new file mode 100755 index 00000000..ff490797 --- /dev/null +++ b/scripts/electron-prepare.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# Shared preparation steps for all Electron build/pack scripts. +# Builds Next.js (with API route backup), compiles TypeScript, and builds all MCP sub-packages. +set -e + +# Backup API routes and icon that are incompatible with static export +mv src/app/api src/app/_api_backup +mv src/app/icon.tsx src/app/_icon_backup.tsx 2>/dev/null || true +trap "mv src/app/_api_backup src/app/api 2>/dev/null; mv src/app/_icon_backup.tsx src/app/icon.tsx 2>/dev/null" EXIT + +# Build Next.js static export +ELECTRON_BUILD=1 next build + +# Compile Electron TypeScript +tsc -p electron/tsconfig.json + +# Build all MCP sub-packages +for pkg in mcp-orchestrator mcp-telegram mcp-kanban mcp-vault mcp-socialdata mcp-x mcp-world; do + cd "$pkg" && npm install && npm run build && cd .. +done From 06031f3939c43afb4ff8f4c7169cdf1a49066c4a Mon Sep 17 00:00:00 2001 From: davebulaval Date: Tue, 31 Mar 2026 19:36:06 -0400 Subject: [PATCH 7/8] fix: nosuid warning in after-install.sh, whitelist build/linux in .gitignore - Add post-install check: warn if SUID bit didn't stick (nosuid mount) - Change .gitignore from `/build` to `/build/*` + `!/build/linux/` so Linux packaging scripts are tracked without git add -f Co-Authored-By: Claude Opus 4.6 (1M context) --- .gitignore | 5 +++-- build/linux/after-install.sh | 7 +++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 14634310..1ab2a2d9 100644 --- a/.gitignore +++ b/.gitignore @@ -17,8 +17,9 @@ /.next/ /out/ -# production -/build +# production (Next.js output) +/build/* +!/build/linux/ # misc .DS_Store diff --git a/build/linux/after-install.sh b/build/linux/after-install.sh index db9d3d88..7619bbcf 100644 --- a/build/linux/after-install.sh +++ b/build/linux/after-install.sh @@ -4,3 +4,10 @@ # Idempotent: safe to run multiple times. chown root:root /opt/Dorothy/chrome-sandbox chmod 4755 /opt/Dorothy/chrome-sandbox + +# Warn if SUID bit didn't stick (e.g. /opt mounted with nosuid) +if [ ! -u /opt/Dorothy/chrome-sandbox ]; then + echo "WARNING: Failed to set SUID bit on /opt/Dorothy/chrome-sandbox." >&2 + echo "If /opt is mounted with 'nosuid', the Electron sandbox will not work." >&2 + echo "You may need to run Dorothy with --no-sandbox as a workaround." >&2 +fi From 34ebb1bb9e26b7ad9320e2f52a9aec9e43807f9a Mon Sep 17 00:00:00 2001 From: davebulaval Date: Tue, 31 Mar 2026 19:49:24 -0400 Subject: [PATCH 8/8] fix: absolute paths in electron-prepare.sh, t64 deb deps for Ubuntu 24.04 - Use REPO_ROOT absolute paths in trap/cleanup so API route restoration works even if script exits from a subdirectory (cd into mcp-* packages) - Use pushd/popd instead of cd for MCP sub-package builds - Use npx for next build to ensure it's found outside npm script context - Add t64 alternatives for libgtk-3-0 and libatspi2.0-0 in deb depends (Ubuntu 24.04 Y2038 transition, electron-builder#9539) Co-Authored-By: Claude Opus 4.6 (1M context) --- package.json | 4 ++-- scripts/electron-prepare.sh | 19 ++++++++++++++----- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index c73213a6..2f06caef 100644 --- a/package.json +++ b/package.json @@ -136,13 +136,13 @@ "maintainer": "Charlie85270", "fpm": ["--after-install", "build/linux/after-install.sh", "--before-remove", "build/linux/before-remove.sh"], "depends": [ - "libgtk-3-0", + "libgtk-3-0 | libgtk-3-0t64", "libnotify4", "libnss3", "libxss1", "libxtst6", "xdg-utils", - "libatspi2.0-0", + "libatspi2.0-0 | libatspi2.0-0t64", "libuuid1", "libsecret-1-0" ], diff --git a/scripts/electron-prepare.sh b/scripts/electron-prepare.sh index ff490797..1aa4dc70 100755 --- a/scripts/electron-prepare.sh +++ b/scripts/electron-prepare.sh @@ -3,18 +3,27 @@ # Builds Next.js (with API route backup), compiles TypeScript, and builds all MCP sub-packages. set -e +REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" + # Backup API routes and icon that are incompatible with static export -mv src/app/api src/app/_api_backup -mv src/app/icon.tsx src/app/_icon_backup.tsx 2>/dev/null || true -trap "mv src/app/_api_backup src/app/api 2>/dev/null; mv src/app/_icon_backup.tsx src/app/icon.tsx 2>/dev/null" EXIT +mv "$REPO_ROOT/src/app/api" "$REPO_ROOT/src/app/_api_backup" +mv "$REPO_ROOT/src/app/icon.tsx" "$REPO_ROOT/src/app/_icon_backup.tsx" 2>/dev/null || true + +cleanup() { + mv "$REPO_ROOT/src/app/_api_backup" "$REPO_ROOT/src/app/api" 2>/dev/null || true + mv "$REPO_ROOT/src/app/_icon_backup.tsx" "$REPO_ROOT/src/app/icon.tsx" 2>/dev/null || true +} +trap cleanup EXIT # Build Next.js static export -ELECTRON_BUILD=1 next build +ELECTRON_BUILD=1 npx next build # Compile Electron TypeScript tsc -p electron/tsconfig.json # Build all MCP sub-packages for pkg in mcp-orchestrator mcp-telegram mcp-kanban mcp-vault mcp-socialdata mcp-x mcp-world; do - cd "$pkg" && npm install && npm run build && cd .. + pushd "$pkg" > /dev/null + npm install && npm run build + popd > /dev/null done