From 32a31e0b4eeda1bffaeb309ddd3e5377858a9aca Mon Sep 17 00:00:00 2001 From: Joshua Cold Date: Thu, 14 May 2026 09:14:57 -0600 Subject: [PATCH 1/2] fix(build): Install as Agent-Teams-UI, display with spaces. This changes how the app's install directory functions so there are not spaces in the destination path, but adds spaces to the Apps display name so it should look the same in installed environments when launching the application. fixes: #111 --- .github/workflows/release.yml | 2 +- package.json | 7 +++++-- src/main/index.ts | 4 ++++ test/main/services/team/ClaudeBinaryResolver.test.ts | 4 ++-- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1ab97a28..41f5883a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -254,7 +254,7 @@ jobs: run: ${{ matrix.dist_command }} --publish never - name: Validate packaged bundle (macOS ${{ matrix.arch }}) - run: node ./scripts/electron-builder/verifyBundle.cjs "release/mac-${{ matrix.arch }}/Agent Teams UI.app" darwin ${{ matrix.arch }} + run: node ./scripts/electron-builder/verifyBundle.cjs "release/mac-${{ matrix.arch }}/Agent-Teams-UI.app" darwin ${{ matrix.arch }} - name: Upload assets to release if: startsWith(github.ref, 'refs/tags/v') diff --git a/package.json b/package.json index b6ee7177..d3f74fd9 100644 --- a/package.json +++ b/package.json @@ -230,7 +230,7 @@ }, "build": { "appId": "com.agent-teams.app", - "productName": "Agent Teams UI", + "productName": "Agent-Teams-UI", "directories": { "output": "release" }, @@ -305,7 +305,10 @@ "pacman" ], "icon": "resources/icons/png", - "category": "Development" + "category": "Development", + "desktop": { + "Name": "Agent Teams UI" + } }, "appImage": { "artifactName": "Agent.Teams.AI-${version}.${ext}" diff --git a/src/main/index.ts b/src/main/index.ts index 4c914fc7..ba782746 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -107,6 +107,10 @@ import { app, BrowserWindow, ipcMain } from 'electron'; import { existsSync } from 'fs'; import { join } from 'path'; +// productName uses hyphens to avoid spaces in the Linux install path (/opt/Agent-Teams-UI/). +// Restore the human-readable display name for macOS menus and Windows system dialogs. +app.setName('Agent Teams UI'); + import { cleanupEditorState, setEditorMainWindow } from './ipc/editor'; import { initializeIpcHandlers, removeIpcHandlers } from './ipc/handlers'; import { registerRendererLogHandlers } from './ipc/rendererLogs'; diff --git a/test/main/services/team/ClaudeBinaryResolver.test.ts b/test/main/services/team/ClaudeBinaryResolver.test.ts index 2378db8e..810a90eb 100644 --- a/test/main/services/team/ClaudeBinaryResolver.test.ts +++ b/test/main/services/team/ClaudeBinaryResolver.test.ts @@ -72,7 +72,7 @@ describe('ClaudeBinaryResolver', () => { }); process.cwd = vi.fn(() => workspaceRoot); Object.defineProperty(process, 'resourcesPath', { - value: '/Applications/Agent Teams UI.app/Contents/Resources', + value: '/Applications/Agent-Teams-UI.app/Contents/Resources', configurable: true, writable: true, }); @@ -200,7 +200,7 @@ describe('ClaudeBinaryResolver', () => { it('prefers the bundled runtime binary for packaged agent_teams_orchestrator builds', async () => { const expectedBinary = path.join( - '/Applications/Agent Teams UI.app/Contents/Resources', + '/Applications/Agent-Teams-UI.app/Contents/Resources', 'runtime', 'claude-multimodel' ); From b3a10ff2cadc9cc2765c5b9191b5529eedd78d66 Mon Sep 17 00:00:00 2001 From: 777genius Date: Thu, 14 May 2026 20:05:48 +0300 Subject: [PATCH 2/2] fix(build): isolate Linux package name override --- .github/workflows/release.yml | 2 +- package.json | 19 ++-- scripts/electron-builder/dist.mjs | 97 +++++++++++++++++++ src/main/index.ts | 4 - .../build/electronBuilderDistScript.test.ts | 52 ++++++++++ .../team/ClaudeBinaryResolver.test.ts | 4 +- 6 files changed, 160 insertions(+), 18 deletions(-) create mode 100644 scripts/electron-builder/dist.mjs create mode 100644 test/main/build/electronBuilderDistScript.test.ts diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 41f5883a..1ab97a28 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -254,7 +254,7 @@ jobs: run: ${{ matrix.dist_command }} --publish never - name: Validate packaged bundle (macOS ${{ matrix.arch }}) - run: node ./scripts/electron-builder/verifyBundle.cjs "release/mac-${{ matrix.arch }}/Agent-Teams-UI.app" darwin ${{ matrix.arch }} + run: node ./scripts/electron-builder/verifyBundle.cjs "release/mac-${{ matrix.arch }}/Agent Teams UI.app" darwin ${{ matrix.arch }} - name: Upload assets to release if: startsWith(github.ref, 'refs/tags/v') diff --git a/package.json b/package.json index d3f74fd9..b61c4d8d 100644 --- a/package.json +++ b/package.json @@ -32,12 +32,12 @@ "team:smoke-changes-real-data": "tsx scripts/team-changes-real-data-smoke.ts", "prebuild": "tsx scripts/fetch-pricing-data.ts && pnpm --filter agent-teams-controller build && pnpm --filter agent-teams-mcp build", "build": "node --max-old-space-size=8192 ./node_modules/electron-vite/bin/electron-vite.js build", - "dist": "electron-builder --mac --win --linux", - "dist:mac": "electron-builder --mac", - "dist:mac:arm64": "electron-builder --mac --arm64", - "dist:mac:x64": "electron-builder --mac --x64", - "dist:win": "electron-builder --win", - "dist:linux": "electron-builder --linux", + "dist": "node ./scripts/electron-builder/dist.mjs --mac --win --linux", + "dist:mac": "node ./scripts/electron-builder/dist.mjs --mac", + "dist:mac:arm64": "node ./scripts/electron-builder/dist.mjs --mac --arm64", + "dist:mac:x64": "node ./scripts/electron-builder/dist.mjs --mac --x64", + "dist:win": "node ./scripts/electron-builder/dist.mjs --win", + "dist:linux": "node ./scripts/electron-builder/dist.mjs --linux", "preview": "electron-vite preview", "typecheck": "tsc --noEmit", "typecheck:workspace": "pnpm typecheck && pnpm --filter agent-teams-mcp typecheck && pnpm --filter agent-teams-mcp typecheck:test", @@ -230,7 +230,7 @@ }, "build": { "appId": "com.agent-teams.app", - "productName": "Agent-Teams-UI", + "productName": "Agent Teams UI", "directories": { "output": "release" }, @@ -305,10 +305,7 @@ "pacman" ], "icon": "resources/icons/png", - "category": "Development", - "desktop": { - "Name": "Agent Teams UI" - } + "category": "Development" }, "appImage": { "artifactName": "Agent.Teams.AI-${version}.${ext}" diff --git a/scripts/electron-builder/dist.mjs b/scripts/electron-builder/dist.mjs new file mode 100644 index 00000000..e3c72615 --- /dev/null +++ b/scripts/electron-builder/dist.mjs @@ -0,0 +1,97 @@ +#!/usr/bin/env node +import { spawn } from 'node:child_process'; +import { createRequire } from 'node:module'; +import { pathToFileURL } from 'node:url'; + +const require = createRequire(import.meta.url); + +const PLATFORM_FLAGS = new Map([ + ['--mac', 'mac'], + ['-m', 'mac'], + ['--win', 'win'], + ['-w', 'win'], + ['--linux', 'linux'], + ['-l', 'linux'], +]); + +const PLATFORM_ARGS = { + mac: '--mac', + win: '--win', + linux: '--linux', +}; + +const LINUX_PACKAGE_NAME_OVERRIDES = [ + '--config.productName=Agent-Teams-UI', + '--config.linux.desktop.entry.Name=Agent Teams UI', +]; + +export function buildElectronBuilderInvocations(argv) { + const targets = []; + const sharedArgs = []; + + for (const arg of argv) { + const target = PLATFORM_FLAGS.get(arg); + if (target) { + if (!targets.includes(target)) { + targets.push(target); + } + continue; + } + sharedArgs.push(arg); + } + + if (targets.length === 0) { + return [{ args: sharedArgs }]; + } + + return targets.map((target) => ({ + args: [ + PLATFORM_ARGS[target], + ...sharedArgs, + ...(target === 'linux' ? LINUX_PACKAGE_NAME_OVERRIDES : []), + ], + })); +} + +async function runElectronBuilder(args) { + const cliPath = require.resolve('electron-builder/cli.js'); + await new Promise((resolve, reject) => { + const child = spawn(process.execPath, [cliPath, ...args], { + stdio: 'inherit', + env: process.env, + }); + + child.on('error', reject); + child.on('exit', (code, signal) => { + if (code === 0) { + resolve(); + return; + } + reject(new Error(`electron-builder failed with ${signal ?? `exit code ${code}`}`)); + }); + }); +} + +async function main(argv) { + const invocations = buildElectronBuilderInvocations(argv); + + if (process.env.ELECTRON_BUILDER_DIST_DRY_RUN === '1') { + console.log( + JSON.stringify( + invocations.map((invocation) => invocation.args), + null, + 2 + ) + ); + return; + } + + for (const invocation of invocations) { + await runElectronBuilder(invocation.args); + } +} + +const entryPointUrl = process.argv[1] ? pathToFileURL(process.argv[1]).href : null; +if (entryPointUrl === import.meta.url) { + await main(process.argv.slice(2)); +} diff --git a/src/main/index.ts b/src/main/index.ts index ba782746..4c914fc7 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -107,10 +107,6 @@ import { app, BrowserWindow, ipcMain } from 'electron'; import { existsSync } from 'fs'; import { join } from 'path'; -// productName uses hyphens to avoid spaces in the Linux install path (/opt/Agent-Teams-UI/). -// Restore the human-readable display name for macOS menus and Windows system dialogs. -app.setName('Agent Teams UI'); - import { cleanupEditorState, setEditorMainWindow } from './ipc/editor'; import { initializeIpcHandlers, removeIpcHandlers } from './ipc/handlers'; import { registerRendererLogHandlers } from './ipc/rendererLogs'; diff --git a/test/main/build/electronBuilderDistScript.test.ts b/test/main/build/electronBuilderDistScript.test.ts new file mode 100644 index 00000000..dcb80d48 --- /dev/null +++ b/test/main/build/electronBuilderDistScript.test.ts @@ -0,0 +1,52 @@ +// @vitest-environment node +import { pathToFileURL } from 'node:url'; + +import { describe, expect, it } from 'vitest'; + +const scriptUrl = pathToFileURL(`${process.cwd()}/scripts/electron-builder/dist.mjs`).href; + +describe('electron-builder dist wrapper', () => { + it('splits multi-platform builds so Linux-only package name overrides do not affect macOS or Windows', async () => { + const { buildElectronBuilderInvocations } = await import(scriptUrl); + + expect( + buildElectronBuilderInvocations(['--mac', '--win', '--linux', '--publish', 'never']) + ).toEqual([ + { args: ['--mac', '--publish', 'never'] }, + { args: ['--win', '--publish', 'never'] }, + { + args: [ + '--linux', + '--publish', + 'never', + '--config.productName=Agent-Teams-UI', + '--config.linux.desktop.entry.Name=Agent Teams UI', + ], + }, + ]); + }); + + it('adds the filesystem-safe package name override to Linux-only builds', async () => { + const { buildElectronBuilderInvocations } = await import(scriptUrl); + + expect(buildElectronBuilderInvocations(['--linux', '--publish', 'never'])).toEqual([ + { + args: [ + '--linux', + '--publish', + 'never', + '--config.productName=Agent-Teams-UI', + '--config.linux.desktop.entry.Name=Agent Teams UI', + ], + }, + ]); + }); + + it('leaves macOS arch-specific builds unchanged', async () => { + const { buildElectronBuilderInvocations } = await import(scriptUrl); + + expect(buildElectronBuilderInvocations(['--mac', '--arm64', '--publish', 'never'])).toEqual([ + { args: ['--mac', '--arm64', '--publish', 'never'] }, + ]); + }); +}); diff --git a/test/main/services/team/ClaudeBinaryResolver.test.ts b/test/main/services/team/ClaudeBinaryResolver.test.ts index 810a90eb..2378db8e 100644 --- a/test/main/services/team/ClaudeBinaryResolver.test.ts +++ b/test/main/services/team/ClaudeBinaryResolver.test.ts @@ -72,7 +72,7 @@ describe('ClaudeBinaryResolver', () => { }); process.cwd = vi.fn(() => workspaceRoot); Object.defineProperty(process, 'resourcesPath', { - value: '/Applications/Agent-Teams-UI.app/Contents/Resources', + value: '/Applications/Agent Teams UI.app/Contents/Resources', configurable: true, writable: true, }); @@ -200,7 +200,7 @@ describe('ClaudeBinaryResolver', () => { it('prefers the bundled runtime binary for packaged agent_teams_orchestrator builds', async () => { const expectedBinary = path.join( - '/Applications/Agent-Teams-UI.app/Contents/Resources', + '/Applications/Agent Teams UI.app/Contents/Resources', 'runtime', 'claude-multimodel' );