@frontendxlab/opencode-computer-use 0.1.3 → 0.1.5
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/CHANGELOG.md +52 -0
- package/README.md +168 -25
- package/SECURITY.md +6 -0
- package/THIRD_PARTY_NOTICES.md +4 -0
- package/bin/opencode-computer-use-permissions.js +221 -0
- package/bin/opencode-computer-use-repl.js +25 -3
- package/bin/opencode-computer-use-setup.js +113 -95
- package/docs/NATIVE_VALIDATION.md +102 -0
- package/docs/index.html +577 -40
- package/docs/llms-full.txt +146 -31
- package/docs/llms.txt +8 -2
- package/docs/sitemap.xml +1 -1
- package/docs/webmcp.js +2 -2
- package/lib/backend.js +50 -14
- package/lib/json-line-peer.mjs +3 -0
- package/lib/sandbox.mjs +51 -4
- package/package.json +11 -8
package/docs/llms-full.txt
CHANGED
|
@@ -2,6 +2,7 @@ OpenCode Computer Use - full documentation summary
|
|
|
2
2
|
Canonical site: https://opencode-computer-use.pages.dev/
|
|
3
3
|
Repository: https://github.com/rashidtvmr/opencode-computer-use
|
|
4
4
|
License: MIT
|
|
5
|
+
Documentation updated: 2026-10-30
|
|
5
6
|
|
|
6
7
|
1. What it is
|
|
7
8
|
OpenCode Computer Use is a dual-generation OpenCode plugin that provides a
|
|
@@ -26,6 +27,17 @@ THIRD_PARTY_NOTICES.md in the repository.
|
|
|
26
27
|
accessibility, screen capture, and input permissions are enforced there.
|
|
27
28
|
The JavaScript VM is a capability guard, not a complete security boundary.
|
|
28
29
|
|
|
30
|
+
Device scope: the controlled desktop is the signed-in desktop on the computer
|
|
31
|
+
running the OpenCode server and its local native MCP child. A browser or
|
|
32
|
+
attached terminal on a second laptop or phone does not switch that target.
|
|
33
|
+
There is no built-in remote-device discovery, pairing service, or network
|
|
34
|
+
transport. Install dependencies, configure the plugin, and grant desktop
|
|
35
|
+
permissions separately on each target computer. Regenerate checkout paths
|
|
36
|
+
with --source after moving them; do not copy node_modules between OS targets.
|
|
37
|
+
Services, containers, WSL, and detached SSH sessions do not automatically
|
|
38
|
+
inherit access to the host user's graphical desktop. A custom backend is an
|
|
39
|
+
explicit compatible executable plus argv, not automatic remote-device support.
|
|
40
|
+
|
|
29
41
|
3. Model workflow
|
|
30
42
|
The js tool accepts code (required JavaScript source), timeout_ms (optional,
|
|
31
43
|
capped at 300 seconds), and title (optional short action title).
|
|
@@ -34,7 +46,7 @@ Typical first call:
|
|
|
34
46
|
var app = await cua.getApp(apps[0].id);
|
|
35
47
|
await app.getAXState();
|
|
36
48
|
Typical action and verification:
|
|
37
|
-
|
|
49
|
+
await app.click(12);
|
|
38
50
|
await app.getAXState();
|
|
39
51
|
State and action rules: read getAXState() or getAXStateAndScreenshot()
|
|
40
52
|
before acting on a new layout; element indexes belong to the most recent
|
|
@@ -47,10 +59,12 @@ decide anew; js_reset discards JavaScript bindings only, not native app
|
|
|
47
59
|
state.
|
|
48
60
|
|
|
49
61
|
4. API
|
|
50
|
-
- cua.getState(options?):
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
62
|
+
- cua.getState(options?): return permitted app inventory as { apps }; emits
|
|
63
|
+
inventory by default unless options.emit is false.
|
|
64
|
+
- cua.listApps(options?): return permitted app records as an array; emits
|
|
65
|
+
inventory by default unless options.emit is false. Both inventory methods
|
|
66
|
+
apply appAccess rules before returning or emitting app records.
|
|
67
|
+
- cua.getApp(nameOrID): bind an app and emit initial accessibility state.
|
|
54
68
|
- app.getAXState(options?): fresh semantic accessibility tree.
|
|
55
69
|
- app.getScreenshot(options?): local image bytes, optionally emitted.
|
|
56
70
|
- app.getAXStateAndScreenshot(options?): combined state and image read.
|
|
@@ -81,16 +95,45 @@ the native MCP operations used by cua: initialize, tools/list, tools/call,
|
|
|
81
95
|
and notifications.
|
|
82
96
|
|
|
83
97
|
6. Install
|
|
98
|
+
Documentation version: 0.1.5. Run npm view @frontendxlab/opencode-computer-use
|
|
99
|
+
version to check the registry release. Use the checkout instructions
|
|
100
|
+
for the changes documented here. Published-package commands use the
|
|
101
|
+
registry release until a new version is published.
|
|
102
|
+
|
|
103
|
+
Cloned repository (development dependency installation does not register it):
|
|
104
|
+
npm ci --ignore-scripts
|
|
105
|
+
node bin/opencode-computer-use-setup.js --local --v1 --source .
|
|
106
|
+
node bin/opencode-computer-use-permissions.js --strict
|
|
107
|
+
opencode mcp list
|
|
108
|
+
Run these commands from the checkout. Use --v2 for OpenCode2 or --global to
|
|
109
|
+
use the checkout from other projects. Setup resolves the source to an absolute
|
|
110
|
+
entrypoint; moving the checkout requires rerunning setup.
|
|
111
|
+
|
|
84
112
|
Published package:
|
|
85
113
|
npm install @frontendxlab/opencode-computer-use
|
|
86
114
|
The package postinstall step detects OpenCode 1 versus OpenCode2 and local
|
|
87
115
|
versus global installation, edits JSONC without dropping comments, and is
|
|
88
|
-
idempotent.
|
|
89
|
-
|
|
90
|
-
|
|
116
|
+
idempotent. Global setup also edits the current user's selected JSONC directly;
|
|
117
|
+
it does not depend on an OpenCode plugin-add subcommand. If generation detection
|
|
118
|
+
is ambiguous, automatic installation makes no change and prints manual
|
|
119
|
+
instructions. Explicit manual failures return a nonzero exit status. From
|
|
120
|
+
any directory, select the scoped package and generation explicitly:
|
|
121
|
+
npm exec --package=@frontendxlab/opencode-computer-use -- opencode-computer-use-setup --local --v1
|
|
122
|
+
npm exec --package=@frontendxlab/opencode-computer-use -- opencode-computer-use-setup --global --v1
|
|
123
|
+
Use --v2 for OpenCode2 and --dry-run to preview. The shorthand npx
|
|
124
|
+
opencode-computer-use-setup is only suitable when its binary is already
|
|
125
|
+
available in the installed project; it does not name the scoped package for
|
|
126
|
+
download. In a checkout use node bin/opencode-computer-use-setup.js instead.
|
|
127
|
+
If pnpm reports
|
|
91
128
|
ERR_PNPM_IGNORED_BUILDS, approve the package once with pnpm approve-builds
|
|
92
129
|
@frontendxlab/opencode-computer-use, or run pnpm exec
|
|
93
|
-
opencode-computer-use-setup manually. CI environments skip setup.
|
|
130
|
+
opencode-computer-use-setup manually. CI environments skip setup. From v0.1.5, desktop onboarding runs during postinstall when lifecycle scripts are allowed.
|
|
131
|
+
The installer invokes opencode-computer-use-permissions --install. macOS runs
|
|
132
|
+
doctor and opens native Accessibility/Screen Recording onboarding when needed;
|
|
133
|
+
the user must still approve protected TCC toggles. GNOME Linux best-effort
|
|
134
|
+
enables toolkit accessibility before AT-SPI/session diagnostics. Windows checks
|
|
135
|
+
UI Automation and the signed-in interactive desktop. Rerun manually with
|
|
136
|
+
npm exec --package=@frontendxlab/opencode-computer-use -- opencode-computer-use-permissions --strict.
|
|
94
137
|
OpenCode 1 (opencode.json or opencode.jsonc, plugin list):
|
|
95
138
|
{ "$schema": "https://opencode.ai/config.json",
|
|
96
139
|
"plugin": [["@frontendxlab/opencode-computer-use", { "backend": "auto" }]] }
|
|
@@ -98,10 +141,11 @@ OpenCode2 V2 (plugins shape):
|
|
|
98
141
|
{ "$schema": "https://opencode.ai/config.json",
|
|
99
142
|
"plugins": [{ "package": "@frontendxlab/opencode-computer-use",
|
|
100
143
|
"options": { "backend": "auto" } }] }
|
|
101
|
-
|
|
102
|
-
|
|
144
|
+
For a checkout, prefer --source . so setup selects its actual entrypoint.
|
|
145
|
+
Do not configure the same package both as an explicit plugin and through
|
|
103
146
|
automatic plugin discovery. If Node is not on PATH, set OPENCODE_NODE to an
|
|
104
|
-
absolute Node executable.
|
|
147
|
+
absolute Node executable. Restart the relevant OpenCode server/TUI after
|
|
148
|
+
changing its configuration, then verify from the same project with opencode mcp list.
|
|
105
149
|
|
|
106
150
|
7. Requirements and OS support
|
|
107
151
|
Requirements: Node.js 18 or newer; OpenCode 1.18.29 or newer (V1) or
|
|
@@ -110,14 +154,40 @@ and Screen Recording permissions; Linux AT-SPI plus required input and
|
|
|
110
154
|
capture services; Windows UI Automation in the interactive desktop session.
|
|
111
155
|
The plugin cannot control lock screens, login screens, UAC secure desktops,
|
|
112
156
|
or other OS-owned consent surfaces, and does not bypass permissions.
|
|
157
|
+
Install-time desktop onboarding:
|
|
158
|
+
- macOS 14+: postinstall runs doctor. If Accessibility or Screen Recording is
|
|
159
|
+
missing, Open Computer Use.app opens its onboarding and System Settings.
|
|
160
|
+
Manual panes: open x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility
|
|
161
|
+
and open x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture.
|
|
162
|
+
Apple still requires the user to approve the protected toggles.
|
|
163
|
+
- Linux: postinstall checks the signed-in AT-SPI2/D-Bus desktop session. On
|
|
164
|
+
GNOME it best-effort runs: gsettings set org.gnome.desktop.interface
|
|
165
|
+
toolkit-accessibility true. Wayland capture/coordinate input is compositor-dependent.
|
|
166
|
+
- Windows: there is no separate UI Automation permission toggle. Run in the
|
|
167
|
+
signed-in interactive desktop session, then verify with open-computer-use call list_apps.
|
|
168
|
+
Rerun the helper with node bin/opencode-computer-use-permissions.js --strict
|
|
169
|
+
from a checkout, or the scoped npm exec helper command above from elsewhere.
|
|
170
|
+
Native open-computer-use commands below assume that CLI is on PATH; otherwise
|
|
171
|
+
prefix them with npm exec --package=open-computer-use@0.3.5 --.
|
|
113
172
|
Packaged native targets: darwin-arm64, darwin-x64, linux-arm64, linux-x64,
|
|
114
|
-
win32-arm64, win32-x64.
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
173
|
+
win32-arm64, win32-x64. Android and iOS are not native targets. CI exercises
|
|
174
|
+
the adapter with a fake native backend on Linux, macOS, and Windows without
|
|
175
|
+
a graphical desktop. Packaged binaries and passing headless tests do not
|
|
176
|
+
certify live accessibility, screenshots, focus, or input. Native smoke evidence
|
|
177
|
+
on 2026-10-30: Ubuntu GNOME Wayland Linux x64, Node 26.13.0, OpenCode 1.19.3,
|
|
178
|
+
and native 0.3.5 passed MCP registration, app inventory, persistent bindings,
|
|
179
|
+
and reset checks. In a disposable GTK3 app, semantic app.click changed a
|
|
180
|
+
counter from 0 to 1 and app.setValue changed an entry; fresh accessibility
|
|
181
|
+
snapshots verified both changes. app.getScreenshot returned Screenshot
|
|
182
|
+
unavailable. Keyboard and coordinate input were not validated. Other
|
|
183
|
+
app/session combinations may expose incomplete trees; the GTK result does
|
|
184
|
+
not certify every Linux app or capability. Observe fresh state after actions.
|
|
185
|
+
macOS, Windows, and Linux arm64 native behavior still needs validation on
|
|
186
|
+
real hosts using docs/NATIVE_VALIDATION.md. Permission checks alone are
|
|
187
|
+
insufficient to certify every native capability.
|
|
188
|
+
The --strict helper invokes the native doctor and reports command failures.
|
|
189
|
+
The current Linux doctor prints session guidance; exit status zero does not
|
|
190
|
+
prove that app discovery, accessibility, screenshots, or input will work.
|
|
121
191
|
|
|
122
192
|
8. Safety
|
|
123
193
|
High-privilege desktop-control surface. The js tool is marked conservatively
|
|
@@ -129,23 +199,67 @@ uploading, changing permissions). Do not expose the session to an untrusted
|
|
|
129
199
|
model or run it unattended against password managers, banking, administrator,
|
|
130
200
|
or other high-value applications. Screenshots, accessibility trees, and typed
|
|
131
201
|
values can contain sensitive data and should stay local. Read SECURITY.md
|
|
132
|
-
before high-risk use. Manual
|
|
202
|
+
before high-risk use. Manual permission and visibility checks before acting:
|
|
133
203
|
open-computer-use doctor
|
|
134
204
|
open-computer-use list-apps
|
|
135
205
|
|
|
136
206
|
9. Troubleshooting (from the docs page)
|
|
137
|
-
- cua_repl not listed:
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
207
|
+
- cua_repl not listed: cloning or installing development dependencies does
|
|
208
|
+
not register the package. Run the checkout setup sequence with --source .
|
|
209
|
+
above, or the scoped npm exec setup command for the published package.
|
|
210
|
+
Choose --local/--global and --v1/--v2 explicitly if needed. Check the
|
|
211
|
+
configuration path printed by setup, including OPENCODE_CONFIG overrides.
|
|
212
|
+
Restart the OpenCode server/TUI that uses that configuration and run
|
|
213
|
+
opencode mcp list from the same project. The plugin registers MCP tools,
|
|
214
|
+
not a slash command. An existing disabled or stale cua_repl entry is
|
|
215
|
+
preserved by default; inspect it. Avoid double registration.
|
|
216
|
+
- Works on one device only: install and configure the package on each target
|
|
217
|
+
computer and grant that desktop's permissions. Attached clients still
|
|
218
|
+
control the server host. Local paths and graphical sessions are not
|
|
219
|
+
portable device connections; remote pairing/transport is not built in.
|
|
220
|
+
- Windows PATH launcher error: standard npm global .cmd/.bat launchers are
|
|
221
|
+
resolved to the adjacent package's native executable without a shell.
|
|
222
|
+
Other launchers require the runtime installed locally or
|
|
223
|
+
OPEN_COMPUTER_USE_NATIVE_COMMAND set to the absolute open-computer-use.exe.
|
|
142
224
|
- Node not found: set OPENCODE_NODE or fix server PATH.
|
|
143
|
-
-
|
|
144
|
-
|
|
145
|
-
|
|
225
|
+
- macOS Accessibility missing: grant Accessibility to Open Computer Use.app,
|
|
226
|
+
not Terminal/OpenCode, restart the app-agent, then rerun doctor. Open the pane
|
|
227
|
+
with: open x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility.
|
|
228
|
+
- macOS Screen Recording missing: grant Open Computer Use.app in Screen &
|
|
229
|
+
System Audio Recording, restart the app-agent, then rerun doctor. Open the
|
|
230
|
+
pane with: open x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture.
|
|
231
|
+
- macOS stale permission state: first restart Open Computer Use and verify the
|
|
232
|
+
correct release app is approved. Last-resort resets are:
|
|
233
|
+
tccutil reset Accessibility com.ifuryst.opencomputeruse
|
|
234
|
+
tccutil reset ScreenCapture com.ifuryst.opencomputeruse
|
|
235
|
+
These reset consent and require the user to approve it again; they do not grant it.
|
|
236
|
+
- macOS native binary will not start: verify sw_vers -productVersion; current
|
|
237
|
+
native macOS runtime requires macOS 14+ and permissions cannot fix an older OS.
|
|
238
|
+
- Linux empty app list / AT-SPI unavailable: run from the logged-in graphical
|
|
239
|
+
desktop session, enable GNOME toolkit accessibility with
|
|
240
|
+
gsettings set org.gnome.desktop.interface toolkit-accessibility true, verify
|
|
241
|
+
D-Bus/session variables, then rerun open-computer-use call list_apps.
|
|
242
|
+
- Linux Wayland screenshot black/omitted: compositor capture restriction; use
|
|
243
|
+
semantic accessibility state/actions or test the workflow in an X11 session
|
|
244
|
+
when reliable coordinate/screenshot automation is required.
|
|
245
|
+
- Linux action reports success but nothing changes: fetch a fresh state and
|
|
246
|
+
prefer element-index AT-SPI actions/set-value/secondary actions. Coordinate
|
|
247
|
+
and key synthesis are best-effort under Wayland.
|
|
248
|
+
- Linux Accessible.is_text/is_editable_text error: upgrade/reinstall the current
|
|
249
|
+
open-computer-use native runtime, which uses standard AT-SPI interfaces.
|
|
250
|
+
- Windows empty app list: run inside the signed-in interactive desktop, not a
|
|
251
|
+
Windows service, session 0, detached SSH context, or unrelated user session.
|
|
252
|
+
- Windows elevated target: UI Automation/input can be blocked by integrity
|
|
253
|
+
boundaries. Prefer a normal-user target; match integrity level only when the
|
|
254
|
+
explicit task truly requires elevation.
|
|
255
|
+
- Windows UAC/lock/sign-in screen: intentionally unsupported secure-desktop/OS
|
|
256
|
+
surfaces; approve/dismiss manually and return to the normal desktop.
|
|
257
|
+
- Windows app found but action does nothing: keep a visible non-minimized window,
|
|
258
|
+
refresh state, prefer semantic UIA actions, and verify the result after input.
|
|
259
|
+
- Empty app list generally: run open-computer-use call list_apps in the same
|
|
260
|
+
graphical session; confirm the app is visible and desktop accessibility is available.
|
|
261
|
+
- Screenshots unavailable generally: OS compositor or permission gap; bounded
|
|
146
262
|
accessibility text may still work.
|
|
147
|
-
- Linux input returns success but nothing changes: session type or missing
|
|
148
|
-
runtime prerequisite; observe again before retrying mutating actions.
|
|
149
263
|
- Global pointer fallback rejected: expected safety gate.
|
|
150
264
|
|
|
151
265
|
10. WebMCP preview
|
|
@@ -157,6 +271,7 @@ control, shell, network mutation, or credentials. WebMCP is experimental and
|
|
|
157
271
|
should not be treated as stable browser support.
|
|
158
272
|
|
|
159
273
|
11. Development
|
|
160
|
-
npm
|
|
274
|
+
npm ci --ignore-scripts; npm test; npm run lint. Register a development
|
|
275
|
+
checkout explicitly with the setup --source . command above. Adapter tests use a fake native MCP
|
|
161
276
|
server and need no real desktop. Live desktop validation must run
|
|
162
277
|
separately per OS.
|
package/docs/llms.txt
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
- [OS support](https://opencode-computer-use.pages.dev/#os)
|
|
12
12
|
- [WebMCP preview](https://opencode-computer-use.pages.dev/#webmcp)
|
|
13
13
|
- [Security](https://opencode-computer-use.pages.dev/#security)
|
|
14
|
+
- [Troubleshooting](https://opencode-computer-use.pages.dev/#troubleshooting)
|
|
14
15
|
- [FAQ](https://opencode-computer-use.pages.dev/#faq)
|
|
15
16
|
|
|
16
17
|
## Repo
|
|
@@ -21,12 +22,17 @@
|
|
|
21
22
|
|
|
22
23
|
## Key facts
|
|
23
24
|
|
|
25
|
+
- Documentation version: 0.1.5. Check npm view @frontendxlab/opencode-computer-use version for the registry release. Checkout setup can load source changes before publication; scoped npm exec commands use the registry release.
|
|
24
26
|
- MCP server name: cua_repl. Model-facing tools: js (persistent evaluation) and js_reset (clean session).
|
|
25
27
|
- Async cua API: listApps, getApp, getState, getAXState, getScreenshot, getAXStateAndScreenshot, click, scroll, drag, typeText, pressKey, setValue, performSecondaryAction. Output helpers: nodeRepl.write, nodeRepl.emitImage.
|
|
26
28
|
- WebMCP preview: the docs page registers only read-only search_docs, get_install_config, and get_api_reference tools when document.modelContext is available. It is progressive enhancement, not stable browser support.
|
|
27
29
|
- Safety profiles: safetyMode=read-only blocks mutating CUA methods and changes js annotations; appAccess supports exact-name and ID allow/deny rules.
|
|
28
30
|
- Reliability: app bindings and output are bounded; native timeouts send cancellation; shutdown sends a best-effort turn-ended notification.
|
|
29
31
|
- No arbitrary shell escape. No run_shell in the model-facing API. Restricted VM blocks require, process, fetch, filesystem, dynamic import, and code generation.
|
|
30
|
-
- Published package: @frontendxlab/opencode-computer-use. Its postinstall setup detects OpenCode generation and local/global scope
|
|
32
|
+
- Published package: @frontendxlab/opencode-computer-use. Its postinstall setup detects OpenCode generation and local/global scope and preserves JSONC; global setup edits JSONC directly. Ambiguous automatic setup leaves configuration unchanged; explicit manual failures return nonzero. From any directory: npm exec --package=@frontendxlab/opencode-computer-use -- opencode-computer-use-setup --local --v1. Select --global or --v2 as appropriate. A bare npx helper name only works when the binary is already available; it does not identify this scoped package for download.
|
|
33
|
+
- Cloned repository: npm ci --ignore-scripts, then node bin/opencode-computer-use-setup.js --local --v1 --source . and node bin/opencode-computer-use-permissions.js --strict. Use --v2 for OpenCode2 or --global for other projects. Dependency installation alone does not register a development checkout. Restart OpenCode after setup and check opencode mcp list.
|
|
34
|
+
- Desktop onboarding runs in postinstall when lifecycle scripts are allowed: macOS doctor/onboarding, best-effort GNOME accessibility enablement, and Windows interactive-session diagnostics. Outside a checkout: npm exec --package=@frontendxlab/opencode-computer-use -- opencode-computer-use-permissions --strict.
|
|
35
|
+
- Device scope: the controlled desktop belongs to the OpenCode/MCP server's computer. Attaching a browser or terminal from another device does not change that target. Each computer needs its own dependencies, configuration, permissions, and interactive desktop session; automatic remote-device pairing and network transport are not built in.
|
|
31
36
|
- Requirements: Node.js 18 or newer, OpenCode 1.18.29 or newer (V1) or OpenCode2 V2, plus a signed-in graphical desktop session with OS accessibility and capture permissions.
|
|
32
|
-
- Native runtimes packaged for darwin-arm64, darwin-x64, linux-arm64, linux-x64, win32-arm64, win32-x64.
|
|
37
|
+
- Native runtimes packaged for darwin-arm64, darwin-x64, linux-arm64, linux-x64, win32-arm64, win32-x64. Android/iOS are not native targets. CI tests the adapter against a fake native backend; it does not certify desktop input or capture. macOS, Windows, and Linux arm64 require native validation on real hosts.
|
|
38
|
+
- Native smoke evidence on 2026-10-30: Ubuntu GNOME Wayland Linux x64, Node 26.13.0, OpenCode 1.19.3, native 0.3.5. MCP registration, app inventory, persistent bindings/reset, and semantic click/setValue in a disposable GTK3 app passed with fresh accessibility-state verification. Screenshot capture returned unavailable. Keyboard/coordinate input was not validated; other apps and Wayland capabilities need separate checks.
|
package/docs/sitemap.xml
CHANGED
package/docs/webmcp.js
CHANGED
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
for (var index = 0; index < entries.length; index += 1) {
|
|
65
65
|
var label = entries[index].querySelector("strong");
|
|
66
66
|
if (!label) continue;
|
|
67
|
-
var documented = String(label.textContent || "").trim().toLowerCase();
|
|
67
|
+
var documented = String(label.textContent || "").split("(")[0].trim().toLowerCase();
|
|
68
68
|
var method = documented.split(".").pop();
|
|
69
69
|
if (documented === requested || documented.endsWith("." + requested) || (requested.indexOf(".") === -1 && method === requested)) {
|
|
70
70
|
return result(plainText(entries[index], 2000));
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
{
|
|
93
93
|
name: "get_install_config",
|
|
94
94
|
title: "Get installation configuration",
|
|
95
|
-
description: "Return the
|
|
95
|
+
description: "Return the published-package OpenCode configuration for one generation. For a cloned repository, use the documented setup --source command instead. The local MCP server controls the OpenCode server host's desktop; this configuration does not pair remote devices.",
|
|
96
96
|
inputSchema: {
|
|
97
97
|
type: "object",
|
|
98
98
|
properties: {
|
package/lib/backend.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { constants, accessSync, existsSync, statSync } from "node:fs";
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
|
-
import {
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
4
|
import process from "node:process";
|
|
5
5
|
import { normalizeOptions, optionsFromEnvironment } from "./options.js";
|
|
6
6
|
|
|
@@ -15,6 +15,8 @@ const PLATFORM_PATHS = {
|
|
|
15
15
|
"win32-x64": ["dist", "windows", "amd64", "open-computer-use.exe"],
|
|
16
16
|
};
|
|
17
17
|
|
|
18
|
+
const SUPPORTED_TARGETS = Object.freeze(Object.keys(PLATFORM_PATHS));
|
|
19
|
+
|
|
18
20
|
function isExecutable(path) {
|
|
19
21
|
try {
|
|
20
22
|
accessSync(path, constants.X_OK);
|
|
@@ -24,14 +26,22 @@ function isExecutable(path) {
|
|
|
24
26
|
}
|
|
25
27
|
}
|
|
26
28
|
|
|
27
|
-
function
|
|
28
|
-
|
|
29
|
+
function environmentValue(environment, name, platform) {
|
|
30
|
+
if (platform !== "win32") return environment[name];
|
|
31
|
+
// Windows environment names are case-insensitive. Match Node's sorted-key
|
|
32
|
+
// choice when a supplied object contains more than one spelling of a name.
|
|
33
|
+
const key = Object.keys(environment).sort().find((key) => key.toUpperCase() === name.toUpperCase());
|
|
34
|
+
return key === undefined ? undefined : environment[key];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function commandOnPath(name, environment = process.env, platform = process.platform) {
|
|
38
|
+
const pathValue = environmentValue(environment, "PATH", platform);
|
|
29
39
|
if (!pathValue) return undefined;
|
|
30
40
|
const extensions =
|
|
31
|
-
|
|
32
|
-
? (environment
|
|
41
|
+
platform === "win32"
|
|
42
|
+
? (environmentValue(environment, "PATHEXT", platform) || ".EXE;.CMD;.BAT;.COM").split(";")
|
|
33
43
|
: [""];
|
|
34
|
-
for (const directory of pathValue.split(
|
|
44
|
+
for (const directory of pathValue.split(platform === "win32" ? ";" : ":")) {
|
|
35
45
|
if (!directory) continue;
|
|
36
46
|
for (const extension of extensions) {
|
|
37
47
|
const candidate = join(directory, `${name}${extension}`);
|
|
@@ -41,6 +51,21 @@ function commandOnPath(name, environment = process.env) {
|
|
|
41
51
|
return undefined;
|
|
42
52
|
}
|
|
43
53
|
|
|
54
|
+
export function resolvePathExecutable(command, platform = process.platform, arch = process.arch) {
|
|
55
|
+
if (platform !== "win32" || !/\.(?:cmd|bat)$/i.test(command)) return command;
|
|
56
|
+
// npm's global Windows shim cannot be spawned with shell:false. Resolve the
|
|
57
|
+
// known package's native executable beside that shim instead of invoking a
|
|
58
|
+
// command shell or interpreting arbitrary shim contents.
|
|
59
|
+
const relative = PLATFORM_PATHS[`${platform}-${arch}`];
|
|
60
|
+
if (relative) {
|
|
61
|
+
const executable = join(dirname(command), "node_modules", "open-computer-use", ...relative);
|
|
62
|
+
if (isExecutable(executable)) return executable;
|
|
63
|
+
}
|
|
64
|
+
throw new Error(
|
|
65
|
+
"The Windows open-computer-use PATH entry is a .cmd/.bat launcher without an adjacent native runtime. Install open-computer-use@0.3.5 in this package or set OPEN_COMPUTER_USE_NATIVE_COMMAND to the absolute open-computer-use.exe path. Shell launchers are not executed.",
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
44
69
|
function packageRoot() {
|
|
45
70
|
try {
|
|
46
71
|
return dirname(require.resolve("open-computer-use/package.json"));
|
|
@@ -79,6 +104,11 @@ function nativeCommand(environment) {
|
|
|
79
104
|
|
|
80
105
|
const root = packageRoot();
|
|
81
106
|
const relative = PLATFORM_PATHS[`${process.platform}-${process.arch}`];
|
|
107
|
+
if (!relative) {
|
|
108
|
+
throw new Error(
|
|
109
|
+
`Open Computer Use does not provide a native runtime for ${process.platform}-${process.arch}. Supported targets: ${SUPPORTED_TARGETS.join(", ")}. Configure backend 'custom' or OPEN_COMPUTER_USE_NATIVE_COMMAND if you provide a compatible runtime yourself.`,
|
|
110
|
+
);
|
|
111
|
+
}
|
|
82
112
|
if (root && relative) {
|
|
83
113
|
const executable = join(root, ...relative);
|
|
84
114
|
if (isExecutable(executable)) {
|
|
@@ -98,7 +128,7 @@ function nativeCommand(environment) {
|
|
|
98
128
|
return {
|
|
99
129
|
name: "open-computer-use",
|
|
100
130
|
source: "PATH:open-computer-use",
|
|
101
|
-
command: fromPath,
|
|
131
|
+
command: resolvePathExecutable(fromPath),
|
|
102
132
|
args: ["mcp"],
|
|
103
133
|
platform: process.platform,
|
|
104
134
|
arch: process.arch,
|
|
@@ -106,7 +136,7 @@ function nativeCommand(environment) {
|
|
|
106
136
|
}
|
|
107
137
|
|
|
108
138
|
throw new Error(
|
|
109
|
-
|
|
139
|
+
`Open Computer Use native runtime for ${process.platform}-${process.arch} is not installed or executable. Install open-computer-use@0.3.5, ensure its install scripts were allowed, or set OPEN_COMPUTER_USE_NATIVE_COMMAND.`,
|
|
110
140
|
);
|
|
111
141
|
}
|
|
112
142
|
|
|
@@ -132,7 +162,7 @@ export function resolveBackend(input = {}, environment = process.env) {
|
|
|
132
162
|
return nativeCommand(environment);
|
|
133
163
|
}
|
|
134
164
|
|
|
135
|
-
export function backendEnvironment(environment = process.env) {
|
|
165
|
+
export function backendEnvironment(environment = process.env, platform = process.platform) {
|
|
136
166
|
const allowed = new Set([
|
|
137
167
|
"PATH",
|
|
138
168
|
"HOME",
|
|
@@ -182,9 +212,15 @@ export function backendEnvironment(environment = process.env) {
|
|
|
182
212
|
"COMPUTER_USE_LINUX_SCREENSHOT_BACKEND",
|
|
183
213
|
]);
|
|
184
214
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
);
|
|
215
|
+
const normalizeName = platform === "win32" ? (name) => name.toUpperCase() : (name) => name;
|
|
216
|
+
const allowedNames = new Set([...allowed].map(normalizeName));
|
|
217
|
+
const seen = new Set();
|
|
218
|
+
const keys = Object.keys(environment);
|
|
219
|
+
if (platform === "win32") keys.sort();
|
|
220
|
+
return Object.fromEntries(keys.flatMap((key) => {
|
|
221
|
+
const name = normalizeName(key);
|
|
222
|
+
if (!allowedNames.has(name) || seen.has(name) || typeof environment[key] !== "string") return [];
|
|
223
|
+
seen.add(name);
|
|
224
|
+
return [[key, environment[key]]];
|
|
225
|
+
}));
|
|
190
226
|
}
|
package/lib/json-line-peer.mjs
CHANGED
|
@@ -37,6 +37,9 @@ export class JsonLinePeer extends EventEmitter {
|
|
|
37
37
|
this.child.stderr.setEncoding("utf8");
|
|
38
38
|
this.child.stdout.on("data", (chunk) => this.#onData(chunk));
|
|
39
39
|
this.child.stderr.on("data", (chunk) => { this.stderr = (this.stderr + chunk).slice(-8192); });
|
|
40
|
+
for (const stream of [this.child.stdin, this.child.stdout, this.child.stderr]) {
|
|
41
|
+
stream.on("error", (error) => this.#close(error, true));
|
|
42
|
+
}
|
|
40
43
|
this.child.once("error", (error) => this.#close(error, true));
|
|
41
44
|
this.child.once("exit", (code, signal) => {
|
|
42
45
|
this.exited = true;
|
package/lib/sandbox.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { runInContext } from "node:vm";
|
|
2
|
+
import { parse } from "acorn";
|
|
2
3
|
|
|
3
4
|
const MAX_FRAME_BYTES = 32 * 1024 * 1024;
|
|
4
5
|
const MAX_APP_BINDINGS = 128;
|
|
@@ -219,8 +220,45 @@ export function hardenSandboxContext(context) {
|
|
|
219
220
|
}
|
|
220
221
|
|
|
221
222
|
export function assertSandboxCode(code) {
|
|
222
|
-
|
|
223
|
-
|
|
223
|
+
const options = {
|
|
224
|
+
ecmaVersion: "latest",
|
|
225
|
+
allowAwaitOutsideFunction: true,
|
|
226
|
+
allowReturnOutsideFunction: true,
|
|
227
|
+
allowImportExportEverywhere: true,
|
|
228
|
+
};
|
|
229
|
+
let ast;
|
|
230
|
+
try {
|
|
231
|
+
ast = parse(code, { ...options, sourceType: "script" });
|
|
232
|
+
} catch (scriptError) {
|
|
233
|
+
try {
|
|
234
|
+
// Module grammar also covers top-level resource declarations. The REPL
|
|
235
|
+
// remains responsible for whether otherwise-safe syntax can execute.
|
|
236
|
+
ast = parse(code, { ...options, sourceType: "module" });
|
|
237
|
+
} catch {
|
|
238
|
+
try {
|
|
239
|
+
// Like Node's REPL, accept an object literal entered as one expression.
|
|
240
|
+
ast = parse(`(${code}\n)`, { ...options, sourceType: "script" });
|
|
241
|
+
} catch {
|
|
242
|
+
throw scriptError;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
const nodes = [ast];
|
|
247
|
+
while (nodes.length > 0) {
|
|
248
|
+
const node = nodes.pop();
|
|
249
|
+
if (node.type === "ImportExpression" || node.type === "ImportDeclaration"
|
|
250
|
+
|| (node.type === "MetaProperty" && node.meta?.name === "import")) {
|
|
251
|
+
throw new Error("dynamic imports are disabled in the CUA sandbox");
|
|
252
|
+
}
|
|
253
|
+
for (const value of Object.values(node)) {
|
|
254
|
+
if (Array.isArray(value)) {
|
|
255
|
+
for (const child of value) {
|
|
256
|
+
if (child && typeof child.type === "string") nodes.push(child);
|
|
257
|
+
}
|
|
258
|
+
} else if (value && typeof value.type === "string") {
|
|
259
|
+
nodes.push(value);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
224
262
|
}
|
|
225
263
|
}
|
|
226
264
|
|
|
@@ -294,6 +332,9 @@ export function createSandboxBridge(
|
|
|
294
332
|
return accessPolicy.default === "allow";
|
|
295
333
|
};
|
|
296
334
|
const assertNamedAppAllowed = async (name) => {
|
|
335
|
+
if (accessPolicy.default === "deny" && accessPolicy.allow.size === 0) {
|
|
336
|
+
throw new Error(`app access denied by policy: ${name}`);
|
|
337
|
+
}
|
|
297
338
|
if (accessPolicy.allow.size === 0 && accessPolicy.deny.size === 0) return;
|
|
298
339
|
const apps = await api.listApps({ emit: false });
|
|
299
340
|
const matches = Array.isArray(apps) ? apps.filter((app) => appNameMatches(app, name)) : [];
|
|
@@ -313,9 +354,15 @@ export function createSandboxBridge(
|
|
|
313
354
|
if (!isActive()) throw new Error("CUA calls are only available while js is executing");
|
|
314
355
|
if (method === "getState") {
|
|
315
356
|
const state = await api.getState({ emit: false });
|
|
316
|
-
|
|
357
|
+
const apps = filterApps(state.apps);
|
|
358
|
+
if (args[0]?.emit !== false) activeOutput().write(apps, "cua.state");
|
|
359
|
+
return encodeSandboxResult({ apps });
|
|
360
|
+
}
|
|
361
|
+
if (method === "listApps") {
|
|
362
|
+
const apps = filterApps(await api.listApps({ emit: false }));
|
|
363
|
+
if (args[0]?.emit !== false) activeOutput().write(apps, "cua.state");
|
|
364
|
+
return encodeSandboxResult(apps);
|
|
317
365
|
}
|
|
318
|
-
if (method === "listApps") return encodeSandboxResult(filterApps(await api.listApps({ emit: false })));
|
|
319
366
|
if (method === "getApp") {
|
|
320
367
|
const app = args[0];
|
|
321
368
|
if (typeof app !== "string" || !app.trim()) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frontendxlab/opencode-computer-use",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "A dual OpenCode and OpenCode2 plugin for local cross-platform desktop control.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -29,27 +29,29 @@
|
|
|
29
29
|
},
|
|
30
30
|
"bin": {
|
|
31
31
|
"opencode-computer-use-repl": "bin/opencode-computer-use-repl.js",
|
|
32
|
-
"opencode-computer-use-setup": "bin/opencode-computer-use-setup.js"
|
|
32
|
+
"opencode-computer-use-setup": "bin/opencode-computer-use-setup.js",
|
|
33
|
+
"opencode-computer-use-permissions": "bin/opencode-computer-use-permissions.js"
|
|
33
34
|
},
|
|
34
35
|
"files": [
|
|
35
36
|
"bin/",
|
|
36
37
|
"lib/",
|
|
37
38
|
"docs/",
|
|
38
39
|
"index.js",
|
|
40
|
+
"CHANGELOG.md",
|
|
39
41
|
"README.md",
|
|
40
42
|
"SECURITY.md",
|
|
41
43
|
"LICENSE",
|
|
42
44
|
"THIRD_PARTY_NOTICES.md"
|
|
43
45
|
],
|
|
44
46
|
"engines": {
|
|
45
|
-
"node": ">=18"
|
|
46
|
-
"opencode": ">=0.0.0-beta-18684"
|
|
47
|
+
"node": ">=18"
|
|
47
48
|
},
|
|
48
49
|
"scripts": {
|
|
49
|
-
"postinstall": "node bin/opencode-computer-use-setup.js --auto",
|
|
50
|
-
"lint": "node --check index.js && node --check bin/opencode-computer-use-repl.js && node --check bin/opencode-computer-use-setup.js && node --check lib/backend.js && node --check lib/json-line-peer.mjs && node --check lib/options.js && node --check lib/output.mjs && node --check lib/repl.mjs && node --check lib/repl-kernel.mjs && node --check lib/sandbox.mjs",
|
|
51
|
-
"test": "node --test",
|
|
52
|
-
"
|
|
50
|
+
"postinstall": "node bin/opencode-computer-use-setup.js --auto && node bin/opencode-computer-use-permissions.js --install",
|
|
51
|
+
"lint": "node --check index.js && node --check bin/opencode-computer-use-repl.js && node --check bin/opencode-computer-use-setup.js && node --check bin/opencode-computer-use-permissions.js && node --check lib/backend.js && node --check lib/json-line-peer.mjs && node --check lib/options.js && node --check lib/output.mjs && node --check lib/repl.mjs && node --check lib/repl-kernel.mjs && node --check lib/sandbox.mjs",
|
|
52
|
+
"test": "node --test test/run.mjs",
|
|
53
|
+
"test:opencode": "node test/opencode-smoke.mjs",
|
|
54
|
+
"prepack": "node --check index.js && node --check bin/opencode-computer-use-repl.js && node --check bin/opencode-computer-use-setup.js && node --check bin/opencode-computer-use-permissions.js && node -e \"JSON.parse(require('fs').readFileSync('package.json','utf8'))\"",
|
|
53
55
|
"prepublishOnly": "npm run lint && npm test"
|
|
54
56
|
},
|
|
55
57
|
"optionalDependencies": {
|
|
@@ -57,6 +59,7 @@
|
|
|
57
59
|
},
|
|
58
60
|
"sideEffects": false,
|
|
59
61
|
"dependencies": {
|
|
62
|
+
"acorn": "^8.19.0",
|
|
60
63
|
"jsonc-parser": "^3.3.1"
|
|
61
64
|
}
|
|
62
65
|
}
|