@agent-sh/computer-use-linux 0.7.1 → 0.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -26,7 +26,7 @@ The Rust crate is published as [`computer-use-linux`](https://crates.io/crates/c
|
|
|
26
26
|
|
|
27
27
|
Most computer-use MCP servers are macOS-only (they lean on AppKit, AXUIElement, CGEvent). The few that target Linux either drive `xdotool` against an X11 root window or shell out to OCR over screenshots. Four things set this one apart:
|
|
28
28
|
|
|
29
|
-
- **Wayland actually works.** Pointer actions can use the `org.freedesktop.portal.RemoteDesktop` interface on Wayland, with `ydotool` / `ydotoold` (uinput) as the deterministic fallback. Literal text prefers `wtype` on compatible Wayland compositors when portal keyboard input is unavailable, preserving Unicode and the active layout before falling back to ydotool. Screenshots use the GNOME Shell DBus screenshot method when present, `org.freedesktop.portal.Screenshot` otherwise,
|
|
29
|
+
- **Wayland actually works.** Pointer actions can use the `org.freedesktop.portal.RemoteDesktop` interface on Wayland, with `ydotool` / `ydotoold` (uinput) as the deterministic fallback. Literal text prefers `wtype` on compatible Wayland compositors when portal keyboard input is unavailable, preserving Unicode and the active layout before falling back to ydotool. Screenshots use the GNOME Shell DBus screenshot method when present, `org.freedesktop.portal.Screenshot` otherwise, then on a native X11 session a root-window `GetImage` (device pixels, no toolkit scaling), and finally spawn `gnome-screenshot` for background/systemd contexts where the DBus paths are denied.
|
|
30
30
|
- **Window targeting is compositor-aware.** The window registry tries GNOME Shell extension, GNOME Shell Introspect, COSMIC Wayland helper, KWin DBus scripting, Hyprland `hyprctl`, i3 IPC, and generic X11/EWMH in order, then reports exactly which backend won or why each backend failed.
|
|
31
31
|
- **Semantic selectors, not pixel coordinates.** Tools like `click`, `perform_action`, and `set_value` accept `role` / `name` / `text` / `states` selectors backed by AT-SPI. Pixel coordinates remain available as a fallback for rendering-only surfaces (canvas, games, X clients without ATK).
|
|
32
32
|
- **One JSON readiness report.** `computer-use-linux doctor` returns a structured document covering platform, portals, AT-SPI, windowing, input, and a `readiness` summary with explicit blockers and a recommended next step. MCP hosts can render or surface that to the user without parsing prose.
|
|
@@ -139,7 +139,7 @@ Validated manually on Ubuntu 25.10 (GNOME Shell 50.1, Wayland). Other compositor
|
|
|
139
139
|
| i3 | `i3-msg`; optional `xprop` for PID hydration | Lists and focuses i3 windows over the active i3 IPC socket. |
|
|
140
140
|
| COSMIC Wayland | `computer-use-linux-cosmic` helper | Installed automatically by `./install.sh`, `cargo install`, and npm. For custom/manual layouts, put the helper next to the main binary, on `PATH`, or point `COMPUTER_USE_LINUX_COSMIC_HELPER` at it. |
|
|
141
141
|
| Sway / generic wlroots | no dedicated backend yet | AT-SPI, screenshots, and global `ydotool` input can still work; exact window list/focus is currently unavailable unless another backend applies. |
|
|
142
|
-
| Generic X11 / XFCE / other EWMH WMs | `wmctrl` plus `xprop` | Lists, focuses, moves, and resizes windows; keyboard input prefers `xdotool`/XTEST. |
|
|
142
|
+
| Generic X11 / XFCE / other EWMH WMs | `wmctrl` plus `xprop` | Lists, focuses, moves, and resizes windows; keyboard input prefers `xdotool`/XTEST. Window origins are read from the X server, since `wmctrl -lG` counts the frame offset twice. |
|
|
143
143
|
|
|
144
144
|
If you run on a desktop not covered above, or a covered backend does not come up cleanly, please open an issue with the output of `computer-use-linux doctor` so we can extend the matrix honestly.
|
|
145
145
|
|
|
@@ -351,7 +351,7 @@ Spawn the binary with `["mcp"]` as the argv tail. It speaks JSON-RPC over stdio
|
|
|
351
351
|
computer-use-linux doctor | jq .readiness
|
|
352
352
|
```
|
|
353
353
|
|
|
354
|
-
Aim for `can_register_mcp_tools`, `can_build_accessibility_tree`, `can_send_development_input`, and `
|
|
354
|
+
Aim for `can_register_mcp_tools`, `can_build_accessibility_tree`, `can_send_development_input`, `can_query_windows`, and `can_capture_screenshots` all `true`. The `blockers` array should be empty. `can_capture_screenshots` means a route was detected, not that a test capture succeeded.
|
|
355
355
|
|
|
356
356
|
2. **If `accessibility.at_spi_bus.ok = false`** — run `computer-use-linux setup` (or call the `setup_accessibility` MCP tool). This sets:
|
|
357
357
|
- `org.gnome.desktop.interface toolkit-accessibility true`
|
|
@@ -385,7 +385,7 @@ Most setups need none of these — `doctor` and the installers pick sensible def
|
|
|
385
385
|
| `COMPUTER_USE_LINUX_FORCE_YDOTOOL_POINTER` / `…_KEYBOARD` | Always route pointer / keyboard through `ydotool`, skipping the portal and KDE clipboard paths; pointer forcing also skips native-X11 `xdotool` coordinate clicks. |
|
|
386
386
|
| `COMPUTER_USE_LINUX_FORCE_XDOTOOL_KEYBOARD` | Prefer `xdotool`/XTEST keyboard input when `DISPLAY` is available. `COMPUTER_USE_LINUX_FORCE_YDOTOOL_KEYBOARD=1` takes precedence. |
|
|
387
387
|
| `COMPUTER_USE_LINUX_XDOTOOL_TYPE_DELAY_MS` | Per-character delay for `xdotool type` in milliseconds (default `12`). `0` is faster but can deliver characters out of order on some X servers. |
|
|
388
|
-
| `COMPUTER_USE_LINUX_SCREENSHOT_BACKEND` | Force a single screenshot backend, skipping the fallback chain. Accepts `gnome-shell`, `portal`, or `gnome-screenshot`. Pin `gnome-screenshot` for background/systemd contexts where the GNOME Shell and portal DBus paths are denied. |
|
|
388
|
+
| `COMPUTER_USE_LINUX_SCREENSHOT_BACKEND` | Force a single screenshot backend, skipping the fallback chain. Accepts `gnome-shell`, `portal`, `x11`, or `gnome-screenshot`. `x11` works only on a native X11 session. Pin `gnome-screenshot` for background/systemd contexts where the GNOME Shell and portal DBus paths are denied. |
|
|
389
389
|
| `COMPUTER_USE_LINUX_ENABLE_SHELL` | Set exactly to `1` before starting the MCP server to register the destructive `run_shell` tool. Unset by default. Do not enable for untrusted or unattended MCP hosts. |
|
|
390
390
|
|
|
391
391
|
**Build-time identity overrides** (set while compiling a downstream embedded
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-sh/computer-use-linux",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.3",
|
|
4
4
|
"description": "Linux desktop-control MCP server: AT-SPI accessibility trees, Wayland/X11 input, screenshots, and compositor window targeting.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -7,9 +7,9 @@ export interface GeneratedMcpToolDefinition {
|
|
|
7
7
|
annotations: Record<string, unknown>;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
export const GENERATED_SERVER_VERSION = "0.7.
|
|
11
|
-
export const GENERATED_TOOL_CATALOG_HASH = "
|
|
12
|
-
export const GENERATED_SHELL_TOOL_CATALOG_HASH = "
|
|
10
|
+
export const GENERATED_SERVER_VERSION = "0.7.3";
|
|
11
|
+
export const GENERATED_TOOL_CATALOG_HASH = "a28811e528ada60c60d5e493d3680249428a65037cbd741ab0efa4711dd91837";
|
|
12
|
+
export const GENERATED_SHELL_TOOL_CATALOG_HASH = "3e5700e7be824d965170c202204cb512ee41b7162088050a8d7f479d617d15d3";
|
|
13
13
|
export const GENERATED_MCP_TOOLS =
|
|
14
14
|
[
|
|
15
15
|
{
|
|
@@ -90,7 +90,6 @@ export const GENERATED_MCP_TOOLS =
|
|
|
90
90
|
]
|
|
91
91
|
}
|
|
92
92
|
},
|
|
93
|
-
"title": "ActivateWindowParams",
|
|
94
93
|
"type": "object"
|
|
95
94
|
},
|
|
96
95
|
"name": "activate_window"
|
|
@@ -219,7 +218,6 @@ export const GENERATED_MCP_TOOLS =
|
|
|
219
218
|
]
|
|
220
219
|
}
|
|
221
220
|
},
|
|
222
|
-
"title": "ClickParams",
|
|
223
221
|
"type": "object"
|
|
224
222
|
},
|
|
225
223
|
"name": "click"
|
|
@@ -272,7 +270,6 @@ export const GENERATED_MCP_TOOLS =
|
|
|
272
270
|
"end_x",
|
|
273
271
|
"end_y"
|
|
274
272
|
],
|
|
275
|
-
"title": "DragParams",
|
|
276
273
|
"type": "object"
|
|
277
274
|
},
|
|
278
275
|
"name": "drag"
|
|
@@ -487,7 +484,6 @@ export const GENERATED_MCP_TOOLS =
|
|
|
487
484
|
]
|
|
488
485
|
}
|
|
489
486
|
},
|
|
490
|
-
"title": "GetAppStateParams",
|
|
491
487
|
"type": "object"
|
|
492
488
|
},
|
|
493
489
|
"name": "get_app_state"
|
|
@@ -612,7 +608,6 @@ export const GENERATED_MCP_TOOLS =
|
|
|
612
608
|
"x",
|
|
613
609
|
"y"
|
|
614
610
|
],
|
|
615
|
-
"title": "MoveWindowParams",
|
|
616
611
|
"type": "object"
|
|
617
612
|
},
|
|
618
613
|
"name": "move_window"
|
|
@@ -679,7 +674,6 @@ export const GENERATED_MCP_TOOLS =
|
|
|
679
674
|
]
|
|
680
675
|
}
|
|
681
676
|
},
|
|
682
|
-
"title": "ActionParams",
|
|
683
677
|
"type": "object"
|
|
684
678
|
},
|
|
685
679
|
"name": "perform_action"
|
|
@@ -768,7 +762,6 @@ export const GENERATED_MCP_TOOLS =
|
|
|
768
762
|
"required": [
|
|
769
763
|
"key"
|
|
770
764
|
],
|
|
771
|
-
"title": "PressKeyParams",
|
|
772
765
|
"type": "object"
|
|
773
766
|
},
|
|
774
767
|
"name": "press_key"
|
|
@@ -865,7 +858,6 @@ export const GENERATED_MCP_TOOLS =
|
|
|
865
858
|
"width",
|
|
866
859
|
"height"
|
|
867
860
|
],
|
|
868
|
-
"title": "ResizeWindowParams",
|
|
869
861
|
"type": "object"
|
|
870
862
|
},
|
|
871
863
|
"name": "resize_window"
|
|
@@ -1002,7 +994,6 @@ export const GENERATED_MCP_TOOLS =
|
|
|
1002
994
|
]
|
|
1003
995
|
}
|
|
1004
996
|
},
|
|
1005
|
-
"title": "ScreenshotParams",
|
|
1006
997
|
"type": "object"
|
|
1007
998
|
},
|
|
1008
999
|
"name": "screenshot"
|
|
@@ -1102,7 +1093,6 @@ export const GENERATED_MCP_TOOLS =
|
|
|
1102
1093
|
"required": [
|
|
1103
1094
|
"direction"
|
|
1104
1095
|
],
|
|
1105
|
-
"title": "ScrollParams",
|
|
1106
1096
|
"type": "object"
|
|
1107
1097
|
},
|
|
1108
1098
|
"name": "scroll"
|
|
@@ -1168,7 +1158,6 @@ export const GENERATED_MCP_TOOLS =
|
|
|
1168
1158
|
"required": [
|
|
1169
1159
|
"value"
|
|
1170
1160
|
],
|
|
1171
|
-
"title": "SetValueParams",
|
|
1172
1161
|
"type": "object"
|
|
1173
1162
|
},
|
|
1174
1163
|
"name": "set_value"
|
|
@@ -1285,7 +1274,6 @@ export const GENERATED_MCP_TOOLS =
|
|
|
1285
1274
|
"required": [
|
|
1286
1275
|
"text"
|
|
1287
1276
|
],
|
|
1288
|
-
"title": "TypeTextParams",
|
|
1289
1277
|
"type": "object"
|
|
1290
1278
|
},
|
|
1291
1279
|
"name": "type_text"
|
|
@@ -1337,7 +1325,6 @@ export const GENERATED_OPTIONAL_MCP_TOOLS =
|
|
|
1337
1325
|
"required": [
|
|
1338
1326
|
"command"
|
|
1339
1327
|
],
|
|
1340
|
-
"title": "RunShellParams",
|
|
1341
1328
|
"type": "object"
|
|
1342
1329
|
},
|
|
1343
1330
|
"name": "run_shell"
|
package/pi/extension/index.ts
CHANGED
|
@@ -18,10 +18,11 @@ import {
|
|
|
18
18
|
constants,
|
|
19
19
|
existsSync,
|
|
20
20
|
readFileSync,
|
|
21
|
+
statSync,
|
|
21
22
|
} from "node:fs";
|
|
22
23
|
import { createRequire } from "node:module";
|
|
23
24
|
import { homedir } from "node:os";
|
|
24
|
-
import { join } from "node:path";
|
|
25
|
+
import { delimiter, join } from "node:path";
|
|
25
26
|
import { Type, type TSchema } from "typebox";
|
|
26
27
|
import {
|
|
27
28
|
GENERATED_MCP_TOOLS,
|
|
@@ -154,6 +155,8 @@ function isRecord(value: unknown): value is Record<string, unknown> {
|
|
|
154
155
|
|
|
155
156
|
function executable(path: string): boolean {
|
|
156
157
|
try {
|
|
158
|
+
// A directory on PATH also passes X_OK; only a regular file can be spawned.
|
|
159
|
+
if (!statSync(path).isFile()) return false;
|
|
157
160
|
accessSync(path, constants.X_OK);
|
|
158
161
|
return true;
|
|
159
162
|
} catch {
|
|
@@ -161,6 +164,23 @@ function executable(path: string): boolean {
|
|
|
161
164
|
}
|
|
162
165
|
}
|
|
163
166
|
|
|
167
|
+
/**
|
|
168
|
+
* First executable `name` in a PATH-style list, or `null`. Empty segments are
|
|
169
|
+
* skipped rather than read as the current directory.
|
|
170
|
+
*/
|
|
171
|
+
export function findExecutableOnPath(
|
|
172
|
+
pathValue: string | undefined,
|
|
173
|
+
name: string,
|
|
174
|
+
isExecutable: (path: string) => boolean = executable,
|
|
175
|
+
): string | null {
|
|
176
|
+
for (const dir of (pathValue ?? "").split(delimiter)) {
|
|
177
|
+
if (!dir) continue;
|
|
178
|
+
const candidate = join(dir, name);
|
|
179
|
+
if (isExecutable(candidate)) return candidate;
|
|
180
|
+
}
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
|
|
164
184
|
function runtimeEnvironment(): Record<string, string> {
|
|
165
185
|
const allowed = new Set([
|
|
166
186
|
"PATH",
|
|
@@ -247,6 +267,15 @@ function defaultFindBinary(): BinaryLaunch | null {
|
|
|
247
267
|
return { binaryPath: bundledBinary, env };
|
|
248
268
|
}
|
|
249
269
|
|
|
270
|
+
// A temporary extension (`pi -e npm:@agent-sh/computer-use-linux`) is staged
|
|
271
|
+
// without the postinstall-downloaded platform binary, so fall back to a
|
|
272
|
+
// global npm or cargo install. The MCP client still checks the server
|
|
273
|
+
// version and tool catalog, so a mismatched install fails with that reason.
|
|
274
|
+
const onPath = findExecutableOnPath(process.env.PATH, "computer-use-linux");
|
|
275
|
+
if (onPath) {
|
|
276
|
+
return { binaryPath: onPath, env };
|
|
277
|
+
}
|
|
278
|
+
|
|
250
279
|
return null;
|
|
251
280
|
}
|
|
252
281
|
|
|
@@ -504,7 +533,7 @@ export function createComputerUseLinuxExtension(
|
|
|
504
533
|
if (!resolved) {
|
|
505
534
|
throw new Error(
|
|
506
535
|
"computer-use-linux binary was not found. Reinstall " +
|
|
507
|
-
`${PACKAGE_NAME} or set COMPUTER_USE_LINUX_BIN.`,
|
|
536
|
+
`${PACKAGE_NAME}, install computer-use-linux on PATH, or set COMPUTER_USE_LINUX_BIN.`,
|
|
508
537
|
);
|
|
509
538
|
}
|
|
510
539
|
const { ComputerUseMcpClient } = loadClientModule();
|
|
@@ -622,7 +651,7 @@ export function createComputerUseLinuxExtension(
|
|
|
622
651
|
|
|
623
652
|
if (!resolveLaunch() && ctx.hasUI) {
|
|
624
653
|
ctx.ui.notify(
|
|
625
|
-
`${PACKAGE_NAME}: binary not found; reinstall the package or set COMPUTER_USE_LINUX_BIN.`,
|
|
654
|
+
`${PACKAGE_NAME}: binary not found; reinstall the package, install computer-use-linux on PATH, or set COMPUTER_USE_LINUX_BIN.`,
|
|
626
655
|
"warning",
|
|
627
656
|
);
|
|
628
657
|
}
|
|
@@ -172,6 +172,7 @@ Ready output should have:
|
|
|
172
172
|
- `can_build_accessibility_tree: true`
|
|
173
173
|
- `can_query_windows: true`
|
|
174
174
|
- `can_send_development_input: true`
|
|
175
|
+
- `can_capture_screenshots: true`
|
|
175
176
|
- `blockers: []`
|
|
176
177
|
|
|
177
178
|
Then test with your agent by calling the `doctor` tool or asking the agent to list desktop windows.
|
|
@@ -102,6 +102,12 @@ The extension looks for `computer-use-linux` in this order:
|
|
|
102
102
|
|
|
103
103
|
1. `COMPUTER_USE_LINUX_BIN`
|
|
104
104
|
2. The binary downloaded inside the installed npm package
|
|
105
|
+
3. `computer-use-linux` on `PATH`, from `npm install -g` or `cargo install`
|
|
106
|
+
|
|
107
|
+
A temporary extension (`pi -e npm:@agent-sh/computer-use-linux`) is staged
|
|
108
|
+
without the downloaded binary, so it relies on the `PATH` step. The extension
|
|
109
|
+
still checks that the server version and tool catalog match, so a mismatched
|
|
110
|
+
install fails with that reason instead of starting.
|
|
105
111
|
|
|
106
112
|
Reinstall the package if the bundled binary is missing:
|
|
107
113
|
|
|
@@ -131,4 +137,5 @@ Ready output has:
|
|
|
131
137
|
- `can_build_accessibility_tree: true`
|
|
132
138
|
- `can_query_windows: true`
|
|
133
139
|
- `can_send_development_input: true`
|
|
140
|
+
- `can_capture_screenshots: true`
|
|
134
141
|
- `blockers: []`
|