open-computer-use 0.1.32 → 0.1.35
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 +27 -29
- package/bin/open-codex-computer-use-mcp +178 -80
- package/bin/open-computer-use +178 -80
- package/bin/open-computer-use-mcp +178 -80
- package/dist/Open Computer Use.app/Contents/Info.plist +1 -1
- package/dist/Open Computer Use.app/Contents/MacOS/OpenComputerUse +0 -0
- package/dist/linux/amd64/open-computer-use +0 -0
- package/dist/linux/arm64/open-computer-use +0 -0
- package/dist/windows/amd64/open-computer-use.exe +0 -0
- package/dist/windows/arm64/open-computer-use.exe +0 -0
- package/package.json +9 -10
- package/plugins/open-computer-use/.codex-plugin/plugin.json +5 -5
- package/plugins/open-computer-use/scripts/launch-open-computer-use.sh +12 -1
- package/scripts/install-codex-plugin.sh +123 -18
- package/scripts/install-config-helper.mjs +190 -31
- package/scripts/install-gemini-mcp.sh +60 -0
- package/scripts/install-opencode-mcp.sh +48 -0
- package/scripts/postinstall.mjs +6 -5
|
@@ -12,6 +12,8 @@ function usage() {
|
|
|
12
12
|
process.stdout.write(`Usage:
|
|
13
13
|
node ./scripts/install-config-helper.mjs claude-mcp <config-path> <project-root> <server-name> <command-name>
|
|
14
14
|
node ./scripts/install-config-helper.mjs codex-mcp <config-path> <server-name> <command-name>
|
|
15
|
+
node ./scripts/install-config-helper.mjs gemini-mcp <config-path> <server-name> <command-name>
|
|
16
|
+
node ./scripts/install-config-helper.mjs opencode-mcp <primary-config-path> <secondary-config-path> <server-name> <command-name>
|
|
15
17
|
node ./scripts/install-config-helper.mjs codex-plugin-version <plugin-manifest-path>
|
|
16
18
|
node ./scripts/install-config-helper.mjs codex-plugin-config <config-path> <repo-root> <marketplace-name> <plugin-name>
|
|
17
19
|
node ./scripts/install-config-helper.mjs copy-into-dir <target-dir> <source-path> [<source-path> ...]
|
|
@@ -29,6 +31,53 @@ function ensureParentDir(filePath) {
|
|
|
29
31
|
mkdirSync(path.dirname(filePath), { recursive: true });
|
|
30
32
|
}
|
|
31
33
|
|
|
34
|
+
function readJSONObjectConfig(configPath, label) {
|
|
35
|
+
const raw = readTextIfExists(configPath);
|
|
36
|
+
if (raw.trim().length === 0) {
|
|
37
|
+
return {};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
let data;
|
|
41
|
+
try {
|
|
42
|
+
data = JSON.parse(raw);
|
|
43
|
+
} catch (error) {
|
|
44
|
+
fail(`Existing ${label} is not valid JSON: ${error.message}`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (data === null || Array.isArray(data) || typeof data !== "object") {
|
|
48
|
+
fail(`Existing ${label} root is not a JSON object; refusing to modify it.`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return data;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function ensureObjectField(parent, key, label) {
|
|
55
|
+
const value = parent[key] ?? {};
|
|
56
|
+
if (value === null || Array.isArray(value) || typeof value !== "object") {
|
|
57
|
+
fail(label);
|
|
58
|
+
}
|
|
59
|
+
parent[key] = value;
|
|
60
|
+
return value;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function getOptionalObjectField(parent, key, label) {
|
|
64
|
+
if (!(key in parent) || parent[key] === undefined) {
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const value = parent[key];
|
|
69
|
+
if (value === null || Array.isArray(value) || typeof value !== "object") {
|
|
70
|
+
fail(label);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return value;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function writeJSONConfig(configPath, data) {
|
|
77
|
+
ensureParentDir(configPath);
|
|
78
|
+
writeFileSync(configPath, `${JSON.stringify(data, null, 2)}\n`, "utf8");
|
|
79
|
+
}
|
|
80
|
+
|
|
32
81
|
function normalizeNewlines(text) {
|
|
33
82
|
return text.replace(/\r\n/g, "\n");
|
|
34
83
|
}
|
|
@@ -168,41 +217,55 @@ function installClaudeMcp(configPath, projectRoot, serverName, commandName) {
|
|
|
168
217
|
args: ["mcp"],
|
|
169
218
|
};
|
|
170
219
|
const legacyServerName = "open-codex-computer-use";
|
|
220
|
+
const data = readJSONObjectConfig(configPath, `Claude config ${configPath}`);
|
|
221
|
+
const projects = ensureObjectField(data, "projects", 'Existing Claude config has non-object "projects"; refusing to modify it.');
|
|
222
|
+
const projectEntry = ensureObjectField(
|
|
223
|
+
projects,
|
|
224
|
+
projectRoot,
|
|
225
|
+
`Existing Claude project entry for ${projectRoot} is not an object; refusing to modify it.`,
|
|
226
|
+
);
|
|
227
|
+
const mcpServers = ensureObjectField(
|
|
228
|
+
projectEntry,
|
|
229
|
+
"mcpServers",
|
|
230
|
+
`Existing Claude project MCP config for ${projectRoot} is not an object; refusing to modify it.`,
|
|
231
|
+
);
|
|
171
232
|
|
|
172
|
-
const
|
|
173
|
-
|
|
233
|
+
const target = mcpServers[serverName];
|
|
234
|
+
const legacy = mcpServers[legacyServerName];
|
|
235
|
+
const targetMatches = JSON.stringify(target) === JSON.stringify(desiredEntry);
|
|
236
|
+
const legacyMatches = JSON.stringify(legacy) === JSON.stringify(desiredEntry);
|
|
174
237
|
|
|
175
|
-
if (
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
try {
|
|
179
|
-
data = JSON.parse(raw);
|
|
180
|
-
} catch (error) {
|
|
181
|
-
fail(`Existing Claude config is not valid JSON: ${error.message}`);
|
|
182
|
-
}
|
|
238
|
+
if (targetMatches && !legacyMatches) {
|
|
239
|
+
process.stdout.write(`Claude MCP server "${serverName}" is already installed for ${projectRoot} in ${configPath}.\n`);
|
|
240
|
+
return;
|
|
183
241
|
}
|
|
184
242
|
|
|
185
|
-
|
|
186
|
-
|
|
243
|
+
mcpServers[serverName] = desiredEntry;
|
|
244
|
+
if (legacyMatches) {
|
|
245
|
+
delete mcpServers[legacyServerName];
|
|
187
246
|
}
|
|
188
247
|
|
|
189
|
-
|
|
190
|
-
if (projects === null || Array.isArray(projects) || typeof projects !== "object") {
|
|
191
|
-
fail('Existing Claude config has non-object "projects"; refusing to modify it.');
|
|
192
|
-
}
|
|
193
|
-
data.projects = projects;
|
|
248
|
+
writeJSONConfig(configPath, data);
|
|
194
249
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
250
|
+
if (targetMatches && legacyMatches) {
|
|
251
|
+
process.stdout.write(`Claude MCP server "${serverName}" was already installed for ${projectRoot}; removed legacy alias "${legacyServerName}" from ${configPath}.\n`);
|
|
252
|
+
} else {
|
|
253
|
+
process.stdout.write(`Installed Claude MCP server "${serverName}" for ${projectRoot} into ${configPath}.\n`);
|
|
198
254
|
}
|
|
199
|
-
|
|
255
|
+
}
|
|
200
256
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
257
|
+
function installGeminiMcp(configPath, serverName, commandName) {
|
|
258
|
+
const desiredEntry = {
|
|
259
|
+
command: commandName,
|
|
260
|
+
args: ["mcp"],
|
|
261
|
+
};
|
|
262
|
+
const legacyServerName = "open-codex-computer-use";
|
|
263
|
+
const data = readJSONObjectConfig(configPath, `Gemini config ${configPath}`);
|
|
264
|
+
const mcpServers = ensureObjectField(
|
|
265
|
+
data,
|
|
266
|
+
"mcpServers",
|
|
267
|
+
`Existing Gemini config has non-object "mcpServers"; refusing to modify it.`,
|
|
268
|
+
);
|
|
206
269
|
|
|
207
270
|
const target = mcpServers[serverName];
|
|
208
271
|
const legacy = mcpServers[legacyServerName];
|
|
@@ -210,7 +273,7 @@ function installClaudeMcp(configPath, projectRoot, serverName, commandName) {
|
|
|
210
273
|
const legacyMatches = JSON.stringify(legacy) === JSON.stringify(desiredEntry);
|
|
211
274
|
|
|
212
275
|
if (targetMatches && !legacyMatches) {
|
|
213
|
-
process.stdout.write(`
|
|
276
|
+
process.stdout.write(`Gemini MCP server "${serverName}" is already installed in ${configPath}.\n`);
|
|
214
277
|
return;
|
|
215
278
|
}
|
|
216
279
|
|
|
@@ -219,16 +282,98 @@ function installClaudeMcp(configPath, projectRoot, serverName, commandName) {
|
|
|
219
282
|
delete mcpServers[legacyServerName];
|
|
220
283
|
}
|
|
221
284
|
|
|
222
|
-
|
|
223
|
-
writeFileSync(configPath, `${JSON.stringify(data, null, 2)}\n`, "utf8");
|
|
285
|
+
writeJSONConfig(configPath, data);
|
|
224
286
|
|
|
225
287
|
if (targetMatches && legacyMatches) {
|
|
226
|
-
process.stdout.write(`
|
|
288
|
+
process.stdout.write(`Gemini MCP server "${serverName}" was already installed; removed legacy alias "${legacyServerName}" from ${configPath}.\n`);
|
|
227
289
|
} else {
|
|
228
|
-
process.stdout.write(`Installed
|
|
290
|
+
process.stdout.write(`Installed Gemini MCP server "${serverName}" into ${configPath}.\n`);
|
|
229
291
|
}
|
|
230
292
|
}
|
|
231
293
|
|
|
294
|
+
function installOpencodeMcp(primaryConfigPath, secondaryConfigPath, serverName, commandName) {
|
|
295
|
+
const desiredEntry = {
|
|
296
|
+
type: "local",
|
|
297
|
+
command: [commandName, "mcp"],
|
|
298
|
+
};
|
|
299
|
+
const legacyServerName = "open-codex-computer-use";
|
|
300
|
+
const configEntries = [{ path: primaryConfigPath, role: "primary" }];
|
|
301
|
+
if (secondaryConfigPath && secondaryConfigPath !== primaryConfigPath) {
|
|
302
|
+
configEntries.push({ path: secondaryConfigPath, role: "secondary" });
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const records = configEntries.map((entry) => ({
|
|
306
|
+
...entry,
|
|
307
|
+
data: readJSONObjectConfig(entry.path, `opencode config ${entry.path}`),
|
|
308
|
+
dirty: false,
|
|
309
|
+
}));
|
|
310
|
+
|
|
311
|
+
const targetMatches = [];
|
|
312
|
+
const extraAliases = [];
|
|
313
|
+
for (const record of records) {
|
|
314
|
+
const mcp = getOptionalObjectField(
|
|
315
|
+
record.data,
|
|
316
|
+
"mcp",
|
|
317
|
+
`Existing opencode config has non-object "mcp" in ${record.path}; refusing to modify it.`,
|
|
318
|
+
);
|
|
319
|
+
if (!mcp) {
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (JSON.stringify(mcp[serverName]) === JSON.stringify(desiredEntry)) {
|
|
324
|
+
targetMatches.push(record.path);
|
|
325
|
+
}
|
|
326
|
+
if (serverName in mcp || legacyServerName in mcp) {
|
|
327
|
+
extraAliases.push(record.path);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
if (targetMatches.length === 1 && extraAliases.length === 1 && targetMatches[0] === extraAliases[0]) {
|
|
332
|
+
process.stdout.write(`opencode MCP server "${serverName}" is already installed in ${targetMatches[0]}.\n`);
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
for (const record of records) {
|
|
337
|
+
const mcp = ensureObjectField(
|
|
338
|
+
record.data,
|
|
339
|
+
"mcp",
|
|
340
|
+
`Existing opencode config has non-object "mcp" in ${record.path}; refusing to modify it.`,
|
|
341
|
+
);
|
|
342
|
+
|
|
343
|
+
if (record.role === "primary") {
|
|
344
|
+
if (JSON.stringify(mcp[serverName]) !== JSON.stringify(desiredEntry)) {
|
|
345
|
+
mcp[serverName] = desiredEntry;
|
|
346
|
+
record.dirty = true;
|
|
347
|
+
}
|
|
348
|
+
if (legacyServerName in mcp) {
|
|
349
|
+
delete mcp[legacyServerName];
|
|
350
|
+
record.dirty = true;
|
|
351
|
+
}
|
|
352
|
+
continue;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if (serverName in mcp) {
|
|
356
|
+
delete mcp[serverName];
|
|
357
|
+
record.dirty = true;
|
|
358
|
+
}
|
|
359
|
+
if (legacyServerName in mcp) {
|
|
360
|
+
delete mcp[legacyServerName];
|
|
361
|
+
record.dirty = true;
|
|
362
|
+
}
|
|
363
|
+
if (Object.keys(mcp).length === 0) {
|
|
364
|
+
delete record.data.mcp;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
for (const record of records) {
|
|
369
|
+
if (record.dirty) {
|
|
370
|
+
writeJSONConfig(record.path, record.data);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
process.stdout.write(`Installed opencode MCP server "${serverName}" into ${primaryConfigPath}.\n`);
|
|
375
|
+
}
|
|
376
|
+
|
|
232
377
|
function installCodexMcp(configPath, serverName, commandName) {
|
|
233
378
|
const desiredBody = `command = ${JSON.stringify(commandName)}\nargs = ["mcp"]`;
|
|
234
379
|
const targetHeader = `mcp_servers."${serverName}"`;
|
|
@@ -347,6 +492,20 @@ function main(argv) {
|
|
|
347
492
|
}
|
|
348
493
|
installCodexMcp(...args);
|
|
349
494
|
return;
|
|
495
|
+
case "gemini-mcp":
|
|
496
|
+
if (args.length !== 3) {
|
|
497
|
+
usage();
|
|
498
|
+
process.exit(1);
|
|
499
|
+
}
|
|
500
|
+
installGeminiMcp(...args);
|
|
501
|
+
return;
|
|
502
|
+
case "opencode-mcp":
|
|
503
|
+
if (args.length !== 4) {
|
|
504
|
+
usage();
|
|
505
|
+
process.exit(1);
|
|
506
|
+
}
|
|
507
|
+
installOpencodeMcp(...args);
|
|
508
|
+
return;
|
|
350
509
|
case "codex-plugin-version":
|
|
351
510
|
if (args.length !== 1) {
|
|
352
511
|
usage();
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
6
|
+
config_helper="${script_dir}/install-config-helper.mjs"
|
|
7
|
+
server_name="open-computer-use"
|
|
8
|
+
command_name="open-computer-use"
|
|
9
|
+
scope="project"
|
|
10
|
+
|
|
11
|
+
usage() {
|
|
12
|
+
cat <<'EOF'
|
|
13
|
+
Usage: ./scripts/install-gemini-mcp.sh [--scope project|user]
|
|
14
|
+
|
|
15
|
+
Install the open-computer-use stdio MCP entry into Gemini CLI config.
|
|
16
|
+
Defaults to project scope, which writes ./.gemini/settings.json for the current project.
|
|
17
|
+
Set GEMINI_CONFIG_PATH to override the target file directly.
|
|
18
|
+
EOF
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
while [[ $# -gt 0 ]]; do
|
|
22
|
+
case "$1" in
|
|
23
|
+
--scope)
|
|
24
|
+
if [[ $# -lt 2 ]]; then
|
|
25
|
+
echo "--scope requires a value" >&2
|
|
26
|
+
usage >&2
|
|
27
|
+
exit 1
|
|
28
|
+
fi
|
|
29
|
+
scope="$2"
|
|
30
|
+
shift 2
|
|
31
|
+
;;
|
|
32
|
+
-h|--help)
|
|
33
|
+
usage
|
|
34
|
+
exit 0
|
|
35
|
+
;;
|
|
36
|
+
*)
|
|
37
|
+
echo "Unknown argument: $1" >&2
|
|
38
|
+
usage >&2
|
|
39
|
+
exit 1
|
|
40
|
+
;;
|
|
41
|
+
esac
|
|
42
|
+
done
|
|
43
|
+
|
|
44
|
+
case "${scope}" in
|
|
45
|
+
project)
|
|
46
|
+
default_config_path="$(pwd -P)/.gemini/settings.json"
|
|
47
|
+
;;
|
|
48
|
+
user)
|
|
49
|
+
default_config_path="${HOME}/.gemini/settings.json"
|
|
50
|
+
;;
|
|
51
|
+
*)
|
|
52
|
+
echo "Unsupported Gemini scope: ${scope}" >&2
|
|
53
|
+
usage >&2
|
|
54
|
+
exit 1
|
|
55
|
+
;;
|
|
56
|
+
esac
|
|
57
|
+
|
|
58
|
+
config_path="${GEMINI_CONFIG_PATH:-${default_config_path}}"
|
|
59
|
+
|
|
60
|
+
node "${config_helper}" gemini-mcp "${config_path}" "${server_name}" "${command_name}"
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
6
|
+
config_helper="${script_dir}/install-config-helper.mjs"
|
|
7
|
+
server_name="open-computer-use"
|
|
8
|
+
command_name="open-computer-use"
|
|
9
|
+
opencode_config_dir="${XDG_CONFIG_HOME:-${HOME}/.config}/opencode"
|
|
10
|
+
|
|
11
|
+
usage() {
|
|
12
|
+
cat <<'EOF'
|
|
13
|
+
Usage: ./scripts/install-opencode-mcp.sh
|
|
14
|
+
|
|
15
|
+
Install the open-computer-use stdio MCP entry into opencode config.
|
|
16
|
+
Set OPENCODE_CONFIG_PATH to override the primary config file directly.
|
|
17
|
+
EOF
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
while [[ $# -gt 0 ]]; do
|
|
21
|
+
case "$1" in
|
|
22
|
+
-h|--help)
|
|
23
|
+
usage
|
|
24
|
+
exit 0
|
|
25
|
+
;;
|
|
26
|
+
*)
|
|
27
|
+
echo "Unknown argument: $1" >&2
|
|
28
|
+
usage >&2
|
|
29
|
+
exit 1
|
|
30
|
+
;;
|
|
31
|
+
esac
|
|
32
|
+
done
|
|
33
|
+
|
|
34
|
+
if [[ -n "${OPENCODE_CONFIG_PATH:-}" ]]; then
|
|
35
|
+
primary_config_path="${OPENCODE_CONFIG_PATH}"
|
|
36
|
+
secondary_config_path=""
|
|
37
|
+
elif [[ -f "${opencode_config_dir}/opencode.json" ]]; then
|
|
38
|
+
primary_config_path="${opencode_config_dir}/opencode.json"
|
|
39
|
+
secondary_config_path="${opencode_config_dir}/config.json"
|
|
40
|
+
elif [[ -f "${opencode_config_dir}/config.json" ]]; then
|
|
41
|
+
primary_config_path="${opencode_config_dir}/config.json"
|
|
42
|
+
secondary_config_path="${opencode_config_dir}/opencode.json"
|
|
43
|
+
else
|
|
44
|
+
primary_config_path="${opencode_config_dir}/opencode.json"
|
|
45
|
+
secondary_config_path="${opencode_config_dir}/config.json"
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
node "${config_helper}" opencode-mcp "${primary_config_path}" "${secondary_config_path}" "${server_name}" "${command_name}"
|
package/scripts/postinstall.mjs
CHANGED
|
@@ -11,16 +11,17 @@ const mcpConfig = {
|
|
|
11
11
|
};
|
|
12
12
|
const lines = [
|
|
13
13
|
"",
|
|
14
|
-
"Installed open-computer-use@0.1.
|
|
14
|
+
"Installed open-computer-use@0.1.35.",
|
|
15
15
|
"Package: https://www.npmjs.com/package/open-computer-use",
|
|
16
16
|
"Commands: open-computer-use, open-computer-use-mcp, open-codex-computer-use-mcp",
|
|
17
|
+
"Native runtime will be selected from bundled artifacts for " + process.platform + "-" + process.arch + ".",
|
|
17
18
|
"",
|
|
18
19
|
"Next:",
|
|
19
|
-
"1. Run open-computer-use
|
|
20
|
-
"2.
|
|
21
|
-
"3.
|
|
20
|
+
"1. Run open-computer-use --version",
|
|
21
|
+
"2. Add the MCP config below to your host client",
|
|
22
|
+
"3. On macOS, run open-computer-use doctor and grant Accessibility / Screen Recording if prompted",
|
|
22
23
|
"",
|
|
23
|
-
"
|
|
24
|
+
"MCP config:",
|
|
24
25
|
JSON.stringify(mcpConfig, null, 2),
|
|
25
26
|
"",
|
|
26
27
|
];
|