From 9defcaed993ec9c4c982161b34e7bf20db7989fb Mon Sep 17 00:00:00 2001 From: "leonardo.piel@outlook.com" Date: Thu, 18 Jun 2026 12:55:27 -0300 Subject: [PATCH 1/2] chore: regenerate package-lock.json with missing transitive deps npm 11 detects missing entries (encoding@0.1.13) that older npm didn't catch, blocking npm ci. Regenerated with npm install. Co-Authored-By: Claude Opus 4.8 --- package-lock.json | 65 +++++++++++++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 27 deletions(-) diff --git a/package-lock.json b/package-lock.json index 044b595f..a6a1c74d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -865,7 +865,6 @@ "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -1479,7 +1478,6 @@ "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -1877,6 +1875,7 @@ "dev": true, "license": "BSD-2-Clause", "optional": true, + "peer": true, "dependencies": { "cross-dirname": "^0.1.0", "debug": "^4.3.4", @@ -1898,6 +1897,7 @@ "dev": true, "license": "MIT", "optional": true, + "peer": true, "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -1914,6 +1914,7 @@ "dev": true, "license": "MIT", "optional": true, + "peer": true, "dependencies": { "universalify": "^2.0.0" }, @@ -1928,6 +1929,7 @@ "dev": true, "license": "MIT", "optional": true, + "peer": true, "engines": { "node": ">= 10.0.0" } @@ -3276,7 +3278,6 @@ "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.27.1.tgz", "integrity": "sha512-sr6GbP+4edBwFndLbM60gf07z0FQ79gaExpnsjMGePXqFcSSb7t6iscpjk9DhFhwd+mTEQrzNafGP8/iGGFYaA==", "license": "MIT", - "peer": true, "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", @@ -4776,6 +4777,7 @@ "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", "license": "MIT", + "peer": true, "dependencies": { "@types/connect": "*", "@types/node": "*" @@ -4809,6 +4811,7 @@ "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", "license": "MIT", + "peer": true, "dependencies": { "@types/node": "*" } @@ -4861,6 +4864,7 @@ "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz", "integrity": "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==", "license": "MIT", + "peer": true, "dependencies": { "@types/node": "*", "@types/qs": "*", @@ -4897,7 +4901,8 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@types/jsonwebtoken": { "version": "9.0.10", @@ -4950,7 +4955,6 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-25.6.0.tgz", "integrity": "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==", "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~7.19.0" } @@ -4993,20 +4997,21 @@ "version": "6.15.0", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.0.tgz", "integrity": "sha512-JawvT8iBVWpzTrz3EGw9BTQFg3BQNmwERdKE22vlTxawwtbyUSlMppvZYKLZzB5zgACXdXxbD3m1bXaMqP/9ow==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@types/range-parser": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@types/react": { "version": "18.3.27", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.27.tgz", "integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==", "license": "MIT", - "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.2.2" @@ -5052,6 +5057,7 @@ "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", "license": "MIT", + "peer": true, "dependencies": { "@types/node": "*" } @@ -5061,6 +5067,7 @@ "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz", "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", "license": "MIT", + "peer": true, "dependencies": { "@types/http-errors": "*", "@types/node": "*" @@ -5139,7 +5146,6 @@ "integrity": "sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==", "dev": true, "license": "BSD-2-Clause", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "7.18.0", "@typescript-eslint/types": "7.18.0", @@ -5535,7 +5541,6 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -6405,7 +6410,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", @@ -7333,7 +7337,6 @@ "integrity": "sha512-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "env-paths": "^2.2.1", "import-fresh": "^3.3.0", @@ -7415,7 +7418,8 @@ "integrity": "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==", "dev": true, "license": "MIT", - "optional": true + "optional": true, + "peer": true }, "node_modules/cross-spawn": { "version": "7.0.6", @@ -7753,7 +7757,6 @@ "integrity": "sha512-glMJgnTreo8CFINujtAhCgN96QAqApDMZ8Vl1r8f0QT8QprvC1UCltV4CcWj20YoIyLZx6IUskaJZ0NV8fokcg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "app-builder-lib": "26.8.1", "builder-util": "26.8.1", @@ -8208,6 +8211,7 @@ "dev": true, "hasInstallScript": true, "license": "MIT", + "peer": true, "dependencies": { "@electron/asar": "^3.2.1", "debug": "^4.1.1", @@ -8228,6 +8232,7 @@ "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "graceful-fs": "^4.1.2", "jsonfile": "^4.0.0", @@ -8262,6 +8267,16 @@ "node": ">= 0.8" } }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, "node_modules/end-of-stream": { "version": "1.4.5", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", @@ -8492,7 +8507,6 @@ "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -8823,7 +8837,6 @@ "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", "license": "MIT", - "peer": true, "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", @@ -10163,7 +10176,6 @@ "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.8.tgz", "integrity": "sha512-VJCEvtrezO1IAR+kqEYnxUOoStaQPGrCmX3j4wDTNOcD1uRPFpGlwQUIW8niPuvHXaTUxeOUl5MMDGrl+tmO9A==", "license": "MIT", - "peer": true, "engines": { "node": ">=16.9.0" } @@ -10343,7 +10355,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.29.2" }, @@ -10387,7 +10398,7 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" @@ -14027,7 +14038,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -14178,6 +14188,7 @@ "dev": true, "license": "MIT", "optional": true, + "peer": true, "dependencies": { "commander": "^9.4.0" }, @@ -14195,6 +14206,7 @@ "dev": true, "license": "MIT", "optional": true, + "peer": true, "engines": { "node": "^12.20.0 || >=14" } @@ -14547,7 +14559,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -16105,6 +16116,7 @@ "integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "mkdirp": "^0.5.1", "rimraf": "~2.6.2" @@ -16168,6 +16180,7 @@ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -16180,6 +16193,7 @@ "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, "license": "ISC", + "peer": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -16201,6 +16215,7 @@ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", + "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -16214,6 +16229,7 @@ "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "minimist": "^1.2.6" }, @@ -16228,6 +16244,7 @@ "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, "license": "ISC", + "peer": true, "dependencies": { "glob": "^7.1.3" }, @@ -16370,7 +16387,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -16583,7 +16599,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "devOptional": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -16929,7 +16944,6 @@ "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", @@ -17038,7 +17052,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -17052,7 +17065,6 @@ "integrity": "sha512-9Xx1v3/ih3m9hN+SbfkUyy0JAs72ap3r7joc87XL6jwF0jGg6mFBvQ1SrwaX+h8BlkX6Hz9shdd1uo6AF+ZGpg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@vitest/expect": "4.1.5", "@vitest/mocker": "4.1.5", @@ -17516,7 +17528,6 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } From c16119ed8e63924eb3e8d0d9cee3ffcfc9fdd088 Mon Sep 17 00:00:00 2001 From: "leonardo.piel@outlook.com" Date: Thu, 18 Jun 2026 13:16:02 -0300 Subject: [PATCH 2/2] feat: add Linux AppImage build support and desktop launcher integration - Fix prepare-python.js to skip macOS-only pyobjc packages on Linux - Add install-linux-desktop.sh script for XDG desktop entry setup - Update readme.md with Linux installation instructions and platform badge --- readme.md | 55 +++++++++++++-- scripts/install-linux-desktop.sh | 111 +++++++++++++++++++++++++++++++ scripts/prepare-python.js | 10 ++- 3 files changed, 166 insertions(+), 10 deletions(-) create mode 100755 scripts/install-linux-desktop.sh diff --git a/readme.md b/readme.md index 0aa4a679..370c099b 100644 --- a/readme.md +++ b/readme.md @@ -18,7 +18,7 @@

- Platform + Platform License Node.js Discord @@ -27,7 +27,7 @@ --- -Open Cowork is a free, open-source AI agent desktop application for Windows and macOS. It wraps Claude Code, OpenAI, Gemini, DeepSeek, and other AI models into a user-friendly GUI with one-click installation — no coding required. Key capabilities include VM-level sandbox isolation (WSL2 on Windows, Lima on macOS), a built-in Skills system for generating PPTX, DOCX, XLSX, and PDF documents, MCP (Model Context Protocol) integration for connecting to browsers, Notion, and other desktop apps, GUI automation via computer use, and remote control through Feishu (Lark) and Slack. Open Cowork is the open-source implementation of Claude Cowork, designed to make AI-powered desktop automation accessible to everyone. +Open Cowork is a free, open-source AI agent desktop application for Windows, macOS, and Linux. It wraps Claude Code, OpenAI, Gemini, DeepSeek, and other AI models into a user-friendly GUI with one-click installation — no coding required. Key capabilities include VM-level sandbox isolation (WSL2 on Windows, Lima on macOS), a built-in Skills system for generating PPTX, DOCX, XLSX, and PDF documents, MCP (Model Context Protocol) integration for connecting to browsers, Notion, and other desktop apps, GUI automation via computer use, and remote control through Feishu (Lark) and Slack. Open Cowork is the open-source implementation of Claude Cowork, designed to make AI-powered desktop automation accessible to everyone. --- @@ -110,10 +110,51 @@ brew install --cask --no-quarantine open-cowork Get the latest version from our [Releases Page](https://github.com/OpenCoworkAI/open-cowork/releases). -| Platform | File Type | -| ------------------------- | --------- | -| **Windows** | `.exe` | -| **macOS** (Apple Silicon) | `.dmg` | +| Platform | File Type | +| ------------------------- | ----------- | +| **Windows** | `.exe` | +| **macOS** (Apple Silicon) | `.dmg` | +| **Linux** (x64) | `.AppImage` | + +#### Linux Desktop Integration + +After downloading the AppImage, run the install script to add it to your application launcher: + +```bash +# Make executable and run +chmod +x "Open Cowork-*.AppImage" +./scripts/install-linux-desktop.sh ./release +``` + +Or manually: + +```bash +# Copy to a permanent location +cp "Open Cowork-*.AppImage" ~/.local/bin/open-cowork +chmod +x ~/.local/bin/open-cowork + +# Install icon +mkdir -p ~/.local/share/icons/hicolor/256x256/apps +cp resources/icon.png ~/.local/share/icons/hicolor/256x256/apps/open-cowork.png + +# Create desktop entry +mkdir -p ~/.local/share/applications +cat > ~/.local/share/applications/open-cowork.desktop << 'EOF' +[Desktop Entry] +Name=Open Cowork +Comment=Open-source AI agent desktop app +Exec=/home/$USER/.local/bin/open-cowork %U +Icon=open-cowork +Type=Application +Categories=Development;Utility; +Terminal=false +EOF + +# Refresh launcher +update-desktop-database ~/.local/share/applications/ +``` + +Press **Super** and search for "Open Cowork" — the app appears in your launcher. ### Option 3: Build from Source @@ -299,7 +340,7 @@ Claude (via Anthropic or OpenRouter), OpenAI-compatible APIs, and Chinese models Yes. Open Cowork itself is completely free and open-source under the MIT license. You only need to pay for the AI model API usage from your chosen provider. **Does Open Cowork work on Linux?** -Currently, Open Cowork provides pre-built installers for Windows and macOS only. Linux users can build from source — see the [Build from Source](#installation) section. +Yes. Linux (x64) AppImages are available on the [Releases page](https://github.com/OpenCoworkAI/open-cowork/releases). For desktop launcher integration, run `scripts/install-linux-desktop.sh` after downloading. You can also build from source — see the [Build from Source](#installation) section. **How does sandbox isolation work?** Open Cowork offers multi-level protection: basic path-based restrictions on all platforms, and enhanced VM-level isolation using WSL2 (Windows) or Lima (macOS). When a VM is available, all commands execute inside an isolated Linux environment, protecting your host system. diff --git a/scripts/install-linux-desktop.sh b/scripts/install-linux-desktop.sh new file mode 100755 index 00000000..ece1633e --- /dev/null +++ b/scripts/install-linux-desktop.sh @@ -0,0 +1,111 @@ +#!/usr/bin/env bash +# +# install-linux-desktop.sh — Integrate Open Cowork AppImage into the Linux +# application launcher (XDG desktop entry). +# +# Usage: +# ./scripts/install-linux-desktop.sh [APPDIR] +# +# APPDIR defaults to ./release. The script looks for the first +# Open Cowork-*-linux-*.AppImage in that directory. +# +# What it does: +# 1. Copies the AppImage to ~/.local/bin/open-cowork +# 2. Installs the application icon to ~/.local/share/icons/hicolor/ +# 3. Creates a .desktop entry in ~/.local/share/applications/ +# 4. Updates the desktop database so the launcher picks it up immediately +# +# After running, press Super and search for "Open Cowork". + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" +APPDIR="${1:-$PROJECT_ROOT/release}" + +# ── Locate AppImage ────────────────────────────────────────────────────────── +APPIMAGE="" +if [ -d "$APPDIR" ]; then + APPIMAGE=$(find "$APPDIR" -maxdepth 1 -name 'Open Cowork-*-linux-*.AppImage' -print -quit 2>/dev/null || true) +fi + +if [ -z "$APPIMAGE" ] || [ ! -f "$APPIMAGE" ]; then + echo "ERROR: No AppImage found in $APPDIR" + echo "Build it first: npm run build" + echo "Or pass a custom path: $0 /path/to/release" + exit 1 +fi + +echo "Found: $(basename "$APPIMAGE")" + +# ── Directories ────────────────────────────────────────────────────────────── +BIN_DIR="$HOME/.local/bin" +ICON_DIR="$HOME/.local/share/icons/hicolor/256x256/apps" +DESKTOP_DIR="$HOME/.local/share/applications" + +mkdir -p "$BIN_DIR" "$ICON_DIR" "$DESKTOP_DIR" + +# ── 1. Install AppImage ───────────────────────────────────────────────────── +APPIMAGE_DEST="$BIN_DIR/open-cowork" +echo "→ Installing AppImage to $APPIMAGE_DEST" +cp "$APPIMAGE" "$APPIMAGE_DEST" +chmod +x "$APPIMAGE_DEST" + +# ── 2. Install icon ───────────────────────────────────────────────────────── +# Try the source icon first (packaged in repo), fall back to extracting from +# the AppImage itself. +ICON_SRC="$PROJECT_ROOT/resources/icon.png" +ICON_DEST="$ICON_DIR/open-cowork.png" + +if [ -f "$ICON_SRC" ]; then + echo "→ Installing icon from resources/icon.png" + cp "$ICON_SRC" "$ICON_DEST" +else + echo "→ Extracting icon from AppImage" + TMPDIR=$(mktemp -d) + trap 'rm -rf "$TMPDIR"' EXIT + cd "$TMPDIR" + "$APPIMAGE" --appimage-extract open-cowork.png 2>/dev/null || true + # electron-builder AppImages use this path + if [ -f squashfs-root/usr/share/icons/hicolor/1024x1024/apps/open-cowork.png ]; then + cp squashfs-root/usr/share/icons/hicolor/1024x1024/apps/open-cowork.png "$ICON_DEST" + elif [ -f squashfs-root/open-cowork.png ]; then + cp squashfs-root/open-cowork.png "$ICON_DEST" + else + echo "WARNING: Could not extract icon from AppImage — desktop entry will have no icon." + fi + cd "$PROJECT_ROOT" +fi + +# ── 3. Create .desktop entry ──────────────────────────────────────────────── +DESKTOP_FILE="$DESKTOP_DIR/open-cowork.desktop" +echo "→ Creating desktop entry: $DESKTOP_FILE" + +cat > "$DESKTOP_FILE" << EOF +[Desktop Entry] +Name=Open Cowork +Comment=Open-source AI agent desktop app — Claude Code, MCP tools, and Skills +Exec=$APPIMAGE_DEST %U +Icon=open-cowork +Type=Application +Categories=Development;Utility; +Terminal=false +StartupWMClass=Open Cowork +EOF + +# ── 4. Update desktop database ────────────────────────────────────────────── +if command -v update-desktop-database &>/dev/null; then + echo "→ Updating desktop database" + update-desktop-database "$DESKTOP_DIR" +fi + +# ── Done ───────────────────────────────────────────────────────────────────── +echo "" +echo "✅ Open Cowork installed to application launcher." +echo " Press Super and search for \"Open Cowork\"." +echo "" +echo " AppImage: $APPIMAGE_DEST" +echo " Desktop: $DESKTOP_FILE" +echo " Icon: $ICON_DEST" +echo "" +echo " To uninstall, delete those three files." diff --git a/scripts/prepare-python.js b/scripts/prepare-python.js index ac94c10b..6a7b1619 100644 --- a/scripts/prepare-python.js +++ b/scripts/prepare-python.js @@ -323,11 +323,15 @@ function ensurePipAvailable(pythonBin) { } } -function installPackages(siteDir, platformTag, pythonBin) { +function installPackages(siteDir, platformTag, pythonBin, platform) { ensureDir(siteDir); const pipPython = process.env.OPEN_COWORK_PIP_PYTHON || pythonBin; - const packageSpecs = [...BUNDLED_GUI_PACKAGES]; + // pyobjc is macOS-only — skip on Linux to avoid pip resolution errors + const packageSpecs = BUNDLED_GUI_PACKAGES.filter(pkg => { + if (pkg.startsWith('pyobjc') && platform !== 'darwin') return false; + return true; + }); const pythonRoot = path.resolve(siteDir, '..'); const runtimeMarkerFile = resolveRuntimeVersionFile(pythonRoot); const runtimeMarker = exists(runtimeMarkerFile) @@ -513,7 +517,7 @@ async function preparePlatformArch(platform, arch) { } // Install packages for GUI automation - installPackages(siteDir, target.platformTag, pythonBin); + installPackages(siteDir, target.platformTag, pythonBin, platform); // Clean site-packages of non-whitelisted packages (also runs after pip install) cleanPythonRuntime(destDir, siteDir);