Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
*.sh text eol=lf
*.service text eol=lf
*.service.template text eol=lf
69 changes: 69 additions & 0 deletions .github/workflows/linux-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
name: Linux CI

on:
push:
branches: [main, "feat/**"]
pull_request:

permissions:
contents: read

env:
DESKTOP_COMMANDER_DISABLE_TELEMETRY: "true"
DESKTOP_COMMANDER_HEADLESS: "true"

jobs:
ubuntu-matrix:
name: Ubuntu ${{ matrix.os }} / Node ${{ matrix.node }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-22.04, ubuntu-24.04]
node: [20, 22]

steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
cache: npm
- name: Install system dependencies
run: sudo apt-get update && sudo apt-get install -y procps ripgrep
- run: npm ci --ignore-scripts --no-audit --no-fund
- run: npm run build
- run: node dist/npm-scripts/verify-ripgrep.js
- run: node test/test-linux-platform.js
- run: node test/test-linux-service-installer.js

debian-12:
name: Debian 12 / Node 20
runs-on: ubuntu-24.04
container: node:20-bookworm

steps:
- name: Install system dependencies
run: apt-get update && apt-get install -y --no-install-recommends git procps ripgrep bash ca-certificates systemd
- uses: actions/checkout@v4
- run: npm ci --ignore-scripts --no-audit --no-fund
- run: npm run build
- run: node dist/npm-scripts/verify-ripgrep.js
- run: node test/test-linux-platform.js
- run: node test/test-linux-service-installer.js
- name: Validate systemd installer syntax
run: bash -n scripts/install-linux-service.sh

full-linux-suite:
name: Full Linux test suite
runs-on: ubuntu-24.04

steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- name: Install system dependencies
run: sudo apt-get update && sudo apt-get install -y python3 procps ripgrep
- run: npm ci --ignore-scripts --no-audit --no-fund
- run: npm test
109 changes: 109 additions & 0 deletions LINUX.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# Desktop Commander on Linux

Desktop Commander supports desktop Linux and headless Ubuntu/Debian servers.
The Linux runtime uses native shells, `ps`, filesystem permissions, signals,
and systemd rather than Windows-specific process APIs.

## Supported baseline

- Ubuntu 22.04 and 24.04
- Debian 12
- Node.js 20 and 22
- Bash, POSIX `sh`, Zsh, and Fish
- Desktop sessions using X11 or Wayland
- Headless VPS and server environments

## Local development

```bash
git clone https://github.com/wonderwhy-er/DesktopCommanderMCP.git
cd DesktopCommanderMCP
npm ci
npm run build
node test/test-linux-platform.js
```

Run the MCP server directly:

```bash
node dist/index.js
```

## Headless remote device

A Linux host without `DISPLAY`, `WAYLAND_DISPLAY`, or `MIR_SOCKET` is detected
as headless. Browser launch is skipped and the device authorization URL and code
remain visible in the terminal or systemd journal.

You can force the runtime mode:

```bash
export DESKTOP_COMMANDER_HEADLESS=true
```

Start a remote device manually:

```bash
desktop-commander remote --persist-session --disable-no-sleep
```

The persisted device session is stored under:

```text
~/.desktop-commander-device/device.json
```

The file is created with mode `0600`.

## systemd installation

Install the package globally first, then run:

```bash
sudo desktop-commander linux-service --user "$USER"
```

The installer refuses to create a root-owned remote agent by default. A root
service requires the explicit `--allow-root` flag and grants the connected AI
full root-level host access, so it is not recommended.

For a non-standard executable path:

```bash
sudo desktop-commander linux-service \
--user "$USER" \
--bin "$HOME/.npm-global/bin/desktop-commander"
```

From a source checkout, the equivalent installer is
`sudo ./scripts/install-linux-service.sh --user "$USER"`.

The installer enables the service but does not start it by default. Start it and
watch the first authorization flow with:

```bash
sudo systemctl start desktop-commander-device
sudo journalctl -u desktop-commander-device -f
```

Use `--start` to start it immediately during installation.

## Service management

```bash
systemctl status desktop-commander-device
sudo systemctl restart desktop-commander-device
sudo systemctl stop desktop-commander-device
journalctl -u desktop-commander-device --since today
```
Comment thread
coderabbitai[bot] marked this conversation as resolved.

The unit uses `KillMode=control-group`, so child shells and long-running commands
are stopped with the service. It also enables `NoNewPrivileges`, `PrivateTmp`,
and read-only protection for system directories.

## Security boundary

Run the service as a dedicated non-root user. Desktop Commander can execute
arbitrary commands with that user's permissions. Directory allowlists and the
command blocklist reduce mistakes but do not provide sandbox isolation. Use a
container or VM when the connected AI must not reach the wider host.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ Execute long-running terminal commands on your computer and manage processes thr
- Command blocklist for accidental execution
- [Docker isolation](#option-6-docker-installation--auto-updates-no-nodejs-required) for complete isolation

## Linux support

Linux desktop and headless Ubuntu/Debian deployments are documented in [LINUX.md](LINUX.md), including systemd installation and runtime behavior.

## How to install

### Install in Claude Desktop
Expand Down
27 changes: 27 additions & 0 deletions deploy/linux/desktop-commander-device.service.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
[Unit]
Description=Desktop Commander Remote Device
Documentation=https://github.com/wonderwhy-er/DesktopCommanderMCP
Wants=network-online.target
After=network-online.target

[Service]
Type=simple
User=__USER__
Group=__GROUP__
WorkingDirectory=__HOME__
Environment="HOME=__HOME__"
Environment=NODE_ENV=production
Environment=DESKTOP_COMMANDER_HEADLESS=true
Environment="PATH=__PATH__"
ExecStart="__BINARY__" remote --persist-session --disable-no-sleep
Restart=on-failure
RestartSec=5
TimeoutStopSec=15
KillMode=control-group
UMask=0077
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=full

[Install]
WantedBy=multi-user.target
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"bugs": "https://github.com/wonderwhy-er/DesktopCommanderMCP/issues",
"type": "module",
"engines": {
"node": ">=18.0.0"
"node": ">=20.0.0"
},
"bin": {
"desktop-commander": "dist/index.js",
Expand All @@ -35,7 +35,7 @@
"bump": "node scripts/sync-version.js --bump",
"bump:minor": "node scripts/sync-version.js --bump --minor",
"bump:major": "node scripts/sync-version.js --bump --major",
"build": "tsc && shx cp setup-claude-server.js uninstall-claude-server.js track-installation.js dist/ && shx chmod +x dist/*.js && shx mkdir -p dist/data && shx cp src/data/onboarding-prompts.json dist/data/ && shx mkdir -p dist/remote-device/scripts && shx cp src/remote-device/scripts/blocking-offline-update.js dist/remote-device/scripts/ && node scripts/build-ui-runtime.cjs",
"build": "tsc && shx cp setup-claude-server.js uninstall-claude-server.js track-installation.js dist/ && shx chmod +x dist/*.js && shx mkdir -p dist/data && shx cp src/data/onboarding-prompts.json dist/data/ && shx mkdir -p dist/remote-device/scripts && shx cp src/remote-device/scripts/blocking-offline-update.js dist/remote-device/scripts/ && node scripts/build-ui-runtime.cjs && shx mkdir -p dist/linux && shx cp scripts/install-linux-service.sh deploy/linux/desktop-commander-device.service.template dist/linux/ && shx chmod +x dist/linux/install-linux-service.sh",
"watch": "tsc --watch",
"start": "node dist/index.js",
"start:debug": "node --inspect-brk=9229 dist/index.js",
Expand All @@ -46,6 +46,7 @@
"clean": "shx rm -rf dist",
"test": "npm run build && node test/run-all-tests.js",
"test:integration": "npm run build && node test/integration/run-all-integration-tests.js",
"test:linux": "npm run build && node test/test-linux-platform.js && node test/test-linux-service-installer.js",
"test:debug": "node --inspect test/run-all-tests.js",
"validate:tools": "npm run build && node scripts/validate-tools-sync.js",
"link:local": "npm run build && npm link",
Expand Down
Loading