codex-computer-use-mcp 0.3.3 → 0.4.0
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 +38 -121
- package/SECURITY.md +5 -63
- package/dist/audit.d.ts +1 -7
- package/dist/audit.js +4 -12
- package/dist/audit.js.map +1 -1
- package/dist/direct-broker.d.ts +18 -30
- package/dist/direct-broker.js +87 -161
- package/dist/direct-broker.js.map +1 -1
- package/dist/direct-service.d.ts +13 -21
- package/dist/direct-service.js +82 -301
- package/dist/direct-service.js.map +1 -1
- package/dist/mcp-elicitation.d.ts +1 -2
- package/dist/mcp-elicitation.js +18 -23
- package/dist/mcp-elicitation.js.map +1 -1
- package/dist/mcp-server.js +34 -38
- package/dist/mcp-server.js.map +1 -1
- package/dist/session-executor.d.ts +22 -0
- package/dist/session-executor.js +89 -0
- package/dist/session-executor.js.map +1 -0
- package/dist/tools.d.ts +743 -81
- package/dist/tools.js +55 -178
- package/dist/tools.js.map +1 -1
- package/dist/version.d.ts +1 -0
- package/dist/version.js +3 -0
- package/dist/version.js.map +1 -0
- package/integrations/pi/index.ts +99 -244
- package/package.json +13 -26
- package/ARCHITECTURE.md +0 -162
- package/CHANGELOG.md +0 -13
- package/CONTRIBUTING.md +0 -33
- package/MIGRATION.md +0 -125
- package/PROOF.md +0 -160
- package/dist/config.d.ts +0 -12
- package/dist/config.js +0 -10
- package/dist/config.js.map +0 -1
- package/dist/lock.d.ts +0 -17
- package/dist/lock.js +0 -137
- package/dist/lock.js.map +0 -1
- package/dist/system.d.ts +0 -27
- package/dist/system.js +0 -215
- package/dist/system.js.map +0 -1
- package/docs/adr/0001-standalone-code-mode-mcp.md +0 -45
- package/integrations/pi/README.md +0 -73
- package/integrations/pi/mcp.json.example +0 -15
- package/npm-shrinkwrap.json +0 -4827
package/dist/lock.js
DELETED
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
import crypto from "node:crypto";
|
|
2
|
-
import { spawn } from "node:child_process";
|
|
3
|
-
import { chmod, lstat, mkdir, open, readFile, rename, rm } from "node:fs/promises";
|
|
4
|
-
import path from "node:path";
|
|
5
|
-
export class AppBusyError extends Error {
|
|
6
|
-
owner;
|
|
7
|
-
constructor(app, owner) {
|
|
8
|
-
super(`${app} is already leased by another direct Computer Use task; concurrent same-app access is blocked`);
|
|
9
|
-
this.name = "AppBusyError";
|
|
10
|
-
this.owner = owner;
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
export function globalAppLockRoot() {
|
|
14
|
-
const uid = process.getuid?.();
|
|
15
|
-
if (!Number.isSafeInteger(uid))
|
|
16
|
-
throw new Error("Could not determine the current user for global app locking");
|
|
17
|
-
return path.join("/tmp", `codex-computer-use-mcp-${uid}`);
|
|
18
|
-
}
|
|
19
|
-
async function ensurePrivateDirectory(directory) {
|
|
20
|
-
try {
|
|
21
|
-
const info = await lstat(directory);
|
|
22
|
-
if (info.isSymbolicLink() || !info.isDirectory())
|
|
23
|
-
throw new Error("App-lock path must be a non-symlink directory");
|
|
24
|
-
if (process.getuid && info.uid !== process.getuid())
|
|
25
|
-
throw new Error("App-lock path must be owned by the current user");
|
|
26
|
-
if ((info.mode & 0o077) !== 0)
|
|
27
|
-
throw new Error("App-lock path permissions must be private");
|
|
28
|
-
}
|
|
29
|
-
catch (error) {
|
|
30
|
-
if (error?.code !== "ENOENT")
|
|
31
|
-
throw error;
|
|
32
|
-
await mkdir(directory, { recursive: true, mode: 0o700 });
|
|
33
|
-
const info = await lstat(directory);
|
|
34
|
-
if (info.isSymbolicLink() || !info.isDirectory())
|
|
35
|
-
throw new Error("App-lock path must be a non-symlink directory");
|
|
36
|
-
if (process.getuid && info.uid !== process.getuid())
|
|
37
|
-
throw new Error("App-lock path must be owned by the current user");
|
|
38
|
-
}
|
|
39
|
-
await chmod(directory, 0o700);
|
|
40
|
-
}
|
|
41
|
-
function lockKey(value) {
|
|
42
|
-
return crypto.createHash("sha256").update(value).digest("hex");
|
|
43
|
-
}
|
|
44
|
-
async function readOwner(ownerPath) {
|
|
45
|
-
try {
|
|
46
|
-
return JSON.parse(await readFile(ownerPath, "utf8"));
|
|
47
|
-
}
|
|
48
|
-
catch {
|
|
49
|
-
return undefined;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
async function publishOwner(ownerPath, owner) {
|
|
53
|
-
const temporary = `${ownerPath}.${process.pid}.${owner.runId}.tmp`;
|
|
54
|
-
const handle = await open(temporary, "wx", 0o600);
|
|
55
|
-
try {
|
|
56
|
-
await handle.writeFile(`${JSON.stringify(owner)}\n`, "utf8");
|
|
57
|
-
await handle.sync();
|
|
58
|
-
}
|
|
59
|
-
finally {
|
|
60
|
-
await handle.close();
|
|
61
|
-
}
|
|
62
|
-
try {
|
|
63
|
-
await rename(temporary, ownerPath);
|
|
64
|
-
}
|
|
65
|
-
finally {
|
|
66
|
-
await rm(temporary, { force: true });
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
export async function acquireAppLock(stateDir, app, runId, _staleAfterMs = 10 * 60_000) {
|
|
70
|
-
await ensurePrivateDirectory(stateDir);
|
|
71
|
-
const locksDir = path.join(stateDir, "locks");
|
|
72
|
-
await ensurePrivateDirectory(locksDir);
|
|
73
|
-
const key = lockKey(app);
|
|
74
|
-
const lockPath = path.join(locksDir, `${key}.lock`);
|
|
75
|
-
const ownerPath = path.join(locksDir, `${key}.owner.json`);
|
|
76
|
-
const owner = {
|
|
77
|
-
runId,
|
|
78
|
-
pid: process.pid,
|
|
79
|
-
app: `selector-sha256:${key.slice(0, 16)}`,
|
|
80
|
-
startedAt: new Date().toISOString(),
|
|
81
|
-
};
|
|
82
|
-
// lockf is a kernel advisory lock: acquisition is atomic and a crashed owner releases it automatically.
|
|
83
|
-
// The static shell command emits readiness only after lockf owns the file, then blocks on stdin.
|
|
84
|
-
const proc = spawn("/usr/bin/lockf", ["-t", "0", lockPath, "/bin/sh", "-c", 'printf "LOCKED\\n"; /bin/cat >/dev/null'], { stdio: ["pipe", "pipe", "ignore"], shell: false });
|
|
85
|
-
const acquired = await new Promise((resolve, reject) => {
|
|
86
|
-
let output = "";
|
|
87
|
-
let settled = false;
|
|
88
|
-
const finish = (value) => {
|
|
89
|
-
if (settled)
|
|
90
|
-
return;
|
|
91
|
-
settled = true;
|
|
92
|
-
resolve(value);
|
|
93
|
-
};
|
|
94
|
-
proc.once("error", (error) => {
|
|
95
|
-
if (settled)
|
|
96
|
-
return;
|
|
97
|
-
settled = true;
|
|
98
|
-
reject(error);
|
|
99
|
-
});
|
|
100
|
-
proc.once("close", () => finish(false));
|
|
101
|
-
proc.stdout.setEncoding("utf8");
|
|
102
|
-
proc.stdout.on("data", (chunk) => {
|
|
103
|
-
output += chunk;
|
|
104
|
-
if (output.includes("LOCKED\n"))
|
|
105
|
-
finish(true);
|
|
106
|
-
});
|
|
107
|
-
});
|
|
108
|
-
if (!acquired) {
|
|
109
|
-
proc.stdin.destroy();
|
|
110
|
-
throw new AppBusyError(app, await readOwner(ownerPath));
|
|
111
|
-
}
|
|
112
|
-
try {
|
|
113
|
-
await publishOwner(ownerPath, owner);
|
|
114
|
-
}
|
|
115
|
-
catch (error) {
|
|
116
|
-
proc.stdin.end();
|
|
117
|
-
await new Promise((resolve) => proc.once("close", () => resolve()));
|
|
118
|
-
throw error;
|
|
119
|
-
}
|
|
120
|
-
let released = false;
|
|
121
|
-
return {
|
|
122
|
-
path: lockPath,
|
|
123
|
-
owner,
|
|
124
|
-
async release() {
|
|
125
|
-
if (released)
|
|
126
|
-
return;
|
|
127
|
-
released = true;
|
|
128
|
-
proc.stdin.end();
|
|
129
|
-
if (proc.exitCode === null)
|
|
130
|
-
await new Promise((resolve) => proc.once("close", () => resolve()));
|
|
131
|
-
const current = await readOwner(ownerPath);
|
|
132
|
-
if (current?.runId === runId)
|
|
133
|
-
await rm(ownerPath, { force: true });
|
|
134
|
-
},
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
//# sourceMappingURL=lock.js.map
|
package/dist/lock.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"lock.js","sourceRoot":"","sources":["../src/lock.ts"],"names":[],"mappings":"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"}
|
package/dist/system.d.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Parse the bundle identifier from `lsappinfo info -only bundleID <asn>` output.
|
|
3
|
-
*
|
|
4
|
-
* Upstream API assumption: LaunchServices changed the emitted key across macOS
|
|
5
|
-
* releases. macOS <= 26 prints `"CFBundleIdentifier"="com.example"`; macOS 27+
|
|
6
|
-
* (Darwin 27) prints `bundleID="com.example"` and drops `CFBundleIdentifier`.
|
|
7
|
-
* Accept both spellings so frontmost detection survives the rename; otherwise
|
|
8
|
-
* `frontmostBundleId()` returns `undefined` and every direct dispatch throws
|
|
9
|
-
* "Could not observe the frontmost app" before reaching the broker.
|
|
10
|
-
*/
|
|
11
|
-
export declare function parseLsappinfoBundleId(stdout: string): string | undefined;
|
|
12
|
-
export declare function frontmostApplicationToken(): string | undefined;
|
|
13
|
-
export declare function frontmostBundleId(): string | undefined;
|
|
14
|
-
export declare function frontmostBundleIdAsync(): Promise<string | undefined>;
|
|
15
|
-
export interface ResolvedAppIdentity {
|
|
16
|
-
bundleId?: string;
|
|
17
|
-
leaseId: string;
|
|
18
|
-
verifiedSystemDictionary: boolean;
|
|
19
|
-
}
|
|
20
|
-
export declare function resolveAppIdentity(app: string): ResolvedAppIdentity;
|
|
21
|
-
export declare function resolveAppLeaseId(app: string): string;
|
|
22
|
-
export interface TargetFrontmostWatcher {
|
|
23
|
-
becameFrontmost(bundleId: string): boolean;
|
|
24
|
-
healthy(): boolean;
|
|
25
|
-
stop(): Promise<void>;
|
|
26
|
-
}
|
|
27
|
-
export declare function watchTargetFrontmost(listenerPath?: string, infoPath?: string): Promise<TargetFrontmostWatcher>;
|
package/dist/system.js
DELETED
|
@@ -1,215 +0,0 @@
|
|
|
1
|
-
import { execFile, spawn, spawnSync } from "node:child_process";
|
|
2
|
-
import { realpathSync } from "node:fs";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import { promisify } from "node:util";
|
|
5
|
-
const execFileAsync = promisify(execFile);
|
|
6
|
-
/**
|
|
7
|
-
* Parse the bundle identifier from `lsappinfo info -only bundleID <asn>` output.
|
|
8
|
-
*
|
|
9
|
-
* Upstream API assumption: LaunchServices changed the emitted key across macOS
|
|
10
|
-
* releases. macOS <= 26 prints `"CFBundleIdentifier"="com.example"`; macOS 27+
|
|
11
|
-
* (Darwin 27) prints `bundleID="com.example"` and drops `CFBundleIdentifier`.
|
|
12
|
-
* Accept both spellings so frontmost detection survives the rename; otherwise
|
|
13
|
-
* `frontmostBundleId()` returns `undefined` and every direct dispatch throws
|
|
14
|
-
* "Could not observe the frontmost app" before reaching the broker.
|
|
15
|
-
*/
|
|
16
|
-
export function parseLsappinfoBundleId(stdout) {
|
|
17
|
-
return stdout.match(/(?:"CFBundleIdentifier"|bundleID)="([^"]+)"/)?.[1];
|
|
18
|
-
}
|
|
19
|
-
export function frontmostApplicationToken() {
|
|
20
|
-
const front = spawnSync("/usr/bin/lsappinfo", ["front"], { encoding: "utf8", timeout: 3000 });
|
|
21
|
-
const asn = (front.stdout ?? "").trim();
|
|
22
|
-
return front.status === 0 && asn.startsWith("ASN:") ? asn : undefined;
|
|
23
|
-
}
|
|
24
|
-
export function frontmostBundleId() {
|
|
25
|
-
const asn = frontmostApplicationToken();
|
|
26
|
-
if (!asn)
|
|
27
|
-
return undefined;
|
|
28
|
-
const info = spawnSync("/usr/bin/lsappinfo", ["info", "-only", "bundleID", asn], {
|
|
29
|
-
encoding: "utf8",
|
|
30
|
-
timeout: 3000,
|
|
31
|
-
});
|
|
32
|
-
if (info.status !== 0)
|
|
33
|
-
return undefined;
|
|
34
|
-
return parseLsappinfoBundleId(info.stdout ?? "");
|
|
35
|
-
}
|
|
36
|
-
export async function frontmostBundleIdAsync() {
|
|
37
|
-
try {
|
|
38
|
-
const front = await execFileAsync("/usr/bin/lsappinfo", ["front"], { encoding: "utf8", timeout: 3000 });
|
|
39
|
-
const asn = front.stdout.trim();
|
|
40
|
-
if (!asn.startsWith("ASN:"))
|
|
41
|
-
return undefined;
|
|
42
|
-
const info = await execFileAsync("/usr/bin/lsappinfo", ["info", "-only", "bundleID", asn], {
|
|
43
|
-
encoding: "utf8",
|
|
44
|
-
timeout: 3000,
|
|
45
|
-
});
|
|
46
|
-
return parseLsappinfoBundleId(info.stdout);
|
|
47
|
-
}
|
|
48
|
-
catch {
|
|
49
|
-
return undefined;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
const DICTIONARY_BUNDLE_ID = "com.apple.Dictionary";
|
|
53
|
-
const DICTIONARY_PATH = "/System/Applications/Dictionary.app";
|
|
54
|
-
function verifySystemDictionaryBundle(bundleId) {
|
|
55
|
-
if (bundleId !== DICTIONARY_BUNDLE_ID)
|
|
56
|
-
return false;
|
|
57
|
-
const verify = spawnSync("/usr/bin/codesign", ["--verify", "--strict", DICTIONARY_PATH], {
|
|
58
|
-
encoding: "utf8",
|
|
59
|
-
timeout: 10_000,
|
|
60
|
-
});
|
|
61
|
-
if (verify.status !== 0)
|
|
62
|
-
return false;
|
|
63
|
-
const requirement = spawnSync("/usr/bin/codesign", ["-dr", "-", DICTIONARY_PATH], {
|
|
64
|
-
encoding: "utf8",
|
|
65
|
-
timeout: 10_000,
|
|
66
|
-
});
|
|
67
|
-
const output = `${requirement.stdout ?? ""}\n${requirement.stderr ?? ""}`;
|
|
68
|
-
return requirement.status === 0 && output.includes(`identifier "${DICTIONARY_BUNDLE_ID}"`) && output.includes("anchor apple");
|
|
69
|
-
}
|
|
70
|
-
export function resolveAppIdentity(app) {
|
|
71
|
-
const trimmed = app.trim();
|
|
72
|
-
if (path.isAbsolute(trimmed)) {
|
|
73
|
-
try {
|
|
74
|
-
const resolvedPath = realpathSync(trimmed);
|
|
75
|
-
const plist = path.join(resolvedPath, "Contents", "Info.plist");
|
|
76
|
-
const metadata = spawnSync("/usr/bin/plutil", ["-extract", "CFBundleIdentifier", "raw", plist], {
|
|
77
|
-
encoding: "utf8",
|
|
78
|
-
timeout: 3000,
|
|
79
|
-
});
|
|
80
|
-
const bundleId = (metadata.stdout ?? "").trim();
|
|
81
|
-
if (metadata.status === 0 && /^[A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)+$/.test(bundleId)) {
|
|
82
|
-
return {
|
|
83
|
-
bundleId,
|
|
84
|
-
leaseId: bundleId.toLowerCase(),
|
|
85
|
-
verifiedSystemDictionary: verifySystemDictionaryBundle(bundleId),
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
return { leaseId: `path:${resolvedPath.toLowerCase()}`, verifiedSystemDictionary: false };
|
|
89
|
-
}
|
|
90
|
-
catch {
|
|
91
|
-
return { leaseId: `path:${trimmed.toLowerCase()}`, verifiedSystemDictionary: false };
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
const isBundleId = /^[A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)+$/.test(trimmed);
|
|
95
|
-
const script = [
|
|
96
|
-
"on run argv",
|
|
97
|
-
isBundleId ? "id of application id (item 1 of argv)" : "id of application (item 1 of argv)",
|
|
98
|
-
"end run",
|
|
99
|
-
];
|
|
100
|
-
const result = spawnSync("/usr/bin/osascript", ["-e", script[0], "-e", script[1], "-e", script[2], "--", trimmed], {
|
|
101
|
-
encoding: "utf8",
|
|
102
|
-
timeout: 3000,
|
|
103
|
-
});
|
|
104
|
-
const resolvedBundleId = (result.stdout ?? "").trim();
|
|
105
|
-
if (result.status === 0 && /^[A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)+$/.test(resolvedBundleId)) {
|
|
106
|
-
return {
|
|
107
|
-
bundleId: resolvedBundleId,
|
|
108
|
-
leaseId: resolvedBundleId.toLowerCase(),
|
|
109
|
-
verifiedSystemDictionary: verifySystemDictionaryBundle(resolvedBundleId),
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
if (isBundleId)
|
|
113
|
-
return { leaseId: trimmed.toLowerCase(), verifiedSystemDictionary: false };
|
|
114
|
-
return { leaseId: `name:${trimmed.toLowerCase()}`, verifiedSystemDictionary: false };
|
|
115
|
-
}
|
|
116
|
-
export function resolveAppLeaseId(app) {
|
|
117
|
-
return resolveAppIdentity(app).leaseId;
|
|
118
|
-
}
|
|
119
|
-
function bundleIdForAsn(asn, infoPath = "/usr/bin/lsappinfo") {
|
|
120
|
-
const info = spawnSync(infoPath, ["info", "-only", "bundleID", asn], {
|
|
121
|
-
encoding: "utf8",
|
|
122
|
-
timeout: 3000,
|
|
123
|
-
});
|
|
124
|
-
if (info.status !== 0)
|
|
125
|
-
return undefined;
|
|
126
|
-
return parseLsappinfoBundleId(info.stdout ?? "");
|
|
127
|
-
}
|
|
128
|
-
export async function watchTargetFrontmost(listenerPath = "/usr/bin/lsappinfo", infoPath = "/usr/bin/lsappinfo") {
|
|
129
|
-
// Listen globally so a stopped/display-name target is covered before background launch and canonical resolution.
|
|
130
|
-
const proc = spawn(listenerPath, ["listen", "+becameFrontmost", "forever"], {
|
|
131
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
132
|
-
});
|
|
133
|
-
const observedBundles = new Set();
|
|
134
|
-
const observedAsns = new Set();
|
|
135
|
-
const unresolvedAsns = new Set();
|
|
136
|
-
let running = true;
|
|
137
|
-
let buffer = "";
|
|
138
|
-
const resolvePending = () => {
|
|
139
|
-
for (const asn of unresolvedAsns) {
|
|
140
|
-
const bundleId = bundleIdForAsn(asn, infoPath);
|
|
141
|
-
if (!bundleId)
|
|
142
|
-
continue;
|
|
143
|
-
observedBundles.add(bundleId.toLowerCase());
|
|
144
|
-
observedAsns.add(asn);
|
|
145
|
-
unresolvedAsns.delete(asn);
|
|
146
|
-
}
|
|
147
|
-
};
|
|
148
|
-
const retryTimer = setInterval(resolvePending, 50);
|
|
149
|
-
retryTimer.unref();
|
|
150
|
-
const childRunning = () => proc.exitCode === null && proc.signalCode === null;
|
|
151
|
-
const markStopped = () => {
|
|
152
|
-
running = false;
|
|
153
|
-
clearInterval(retryTimer);
|
|
154
|
-
};
|
|
155
|
-
proc.stdout.setEncoding("utf8");
|
|
156
|
-
proc.stdout.on("data", (chunk) => {
|
|
157
|
-
buffer += chunk;
|
|
158
|
-
for (const match of buffer.matchAll(/ASN:0x[0-9a-f]+-0x[0-9a-f]+/gi)) {
|
|
159
|
-
const asn = match[0];
|
|
160
|
-
if (!observedAsns.has(asn))
|
|
161
|
-
unresolvedAsns.add(asn);
|
|
162
|
-
}
|
|
163
|
-
// Every complete ASN above has been consumed; retain only enough overlap for a token split across chunks.
|
|
164
|
-
buffer = buffer.slice(-128);
|
|
165
|
-
resolvePending();
|
|
166
|
-
});
|
|
167
|
-
proc.once("error", markStopped);
|
|
168
|
-
proc.once("close", markStopped);
|
|
169
|
-
await new Promise((resolve) => setTimeout(resolve, 75));
|
|
170
|
-
if (!running || !childRunning())
|
|
171
|
-
throw new Error("Target focus-event monitor failed to start");
|
|
172
|
-
return {
|
|
173
|
-
becameFrontmost: (bundleId) => {
|
|
174
|
-
resolvePending();
|
|
175
|
-
return observedBundles.has(bundleId.toLowerCase());
|
|
176
|
-
},
|
|
177
|
-
healthy: () => running && childRunning(),
|
|
178
|
-
stop: async () => {
|
|
179
|
-
if (childRunning()) {
|
|
180
|
-
proc.kill("SIGTERM");
|
|
181
|
-
await new Promise((resolve) => {
|
|
182
|
-
const timeout = setTimeout(resolve, 1000);
|
|
183
|
-
proc.once("close", () => {
|
|
184
|
-
clearTimeout(timeout);
|
|
185
|
-
resolve();
|
|
186
|
-
});
|
|
187
|
-
});
|
|
188
|
-
if (childRunning()) {
|
|
189
|
-
proc.kill("SIGKILL");
|
|
190
|
-
await new Promise((resolve) => {
|
|
191
|
-
const timeout = setTimeout(resolve, 500);
|
|
192
|
-
proc.once("close", () => {
|
|
193
|
-
clearTimeout(timeout);
|
|
194
|
-
resolve();
|
|
195
|
-
});
|
|
196
|
-
});
|
|
197
|
-
}
|
|
198
|
-
if (childRunning()) {
|
|
199
|
-
clearInterval(retryTimer);
|
|
200
|
-
throw new Error("Target focus-event monitor did not terminate");
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
clearInterval(retryTimer);
|
|
204
|
-
// Events may arrive only while SIGTERM is draining stdout. Resolve every queued ASN before returning.
|
|
205
|
-
for (let attempt = 0; unresolvedAsns.size > 0 && attempt < 10; attempt += 1) {
|
|
206
|
-
resolvePending();
|
|
207
|
-
if (unresolvedAsns.size > 0)
|
|
208
|
-
await new Promise((resolve) => setTimeout(resolve, 25));
|
|
209
|
-
}
|
|
210
|
-
if (unresolvedAsns.size > 0)
|
|
211
|
-
throw new Error("Could not resolve all queued focus-event identities");
|
|
212
|
-
},
|
|
213
|
-
};
|
|
214
|
-
}
|
|
215
|
-
//# sourceMappingURL=system.js.map
|
package/dist/system.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"system.js","sourceRoot":"","sources":["../src/system.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
# ADR 0001: Keep Code Mode in a standalone MCP process
|
|
2
|
-
|
|
3
|
-
- **Status:** Accepted
|
|
4
|
-
- **Date:** 2026-07-15
|
|
5
|
-
|
|
6
|
-
## Context
|
|
7
|
-
|
|
8
|
-
Codex can compose MCP tools through JavaScript Code Mode. Pi's progressive tool disclosure reduces schema context but does not let generated code invoke arbitrary Pi tools. Adding an executor, loader, or batch method here would also break this repository's exact ten-tool Computer Use contract.
|
|
9
|
-
|
|
10
|
-
We considered existing Pi and MCP implementations:
|
|
11
|
-
|
|
12
|
-
- `pi-mcp-adapter` has the strongest Pi-facing MCP configuration, OAuth, elicitation, lifecycle, metadata, and rich-result handling, but no Code Mode executor.
|
|
13
|
-
- `dmmulroy/pi-mcp` has a clean MCP manager but no Code Mode, no published package, and no detected licence.
|
|
14
|
-
- `Hor1zonZzz/pi-codeMode` demonstrates Pi-side JavaScript orchestration but supports only reconstructed built-ins and has no MCP support or tests.
|
|
15
|
-
- `pi-code-tool` is a tested Pi-native pattern, but uses Python/Monty and does not aggregate MCP servers.
|
|
16
|
-
- Cloudflare's Code Mode packages are strong design references, but their secure executor depends on Cloudflare Dynamic Workers; `cloudflare/mcp` is specific to the Cloudflare API.
|
|
17
|
-
- `tool-sandbox` and `tool-sandbox-mcp` provide QuickJS-based execution, but impose a separate capability boundary and the server primarily targets one upstream HTTP gateway.
|
|
18
|
-
- `cmcp` is the closest standalone multi-server implementation, but lacks the OAuth, elicitation, sampling, roots, and notification fidelity required here.
|
|
19
|
-
|
|
20
|
-
## Decision
|
|
21
|
-
|
|
22
|
-
If we add Code Mode for Pi, implement it as a **separate stdio MCP server** loaded through Pi's existing MCP adapter.
|
|
23
|
-
|
|
24
|
-
- Do not modify Pi core.
|
|
25
|
-
- Do not add Code Mode methods to `codex-computer-use-mcp`; its public Computer Use surface remains the exact ten official tools.
|
|
26
|
-
- Let the Code Mode process connect to upstream MCP servers and expose a compact code-execution surface for discovery and composition.
|
|
27
|
-
- Give generated code the same filesystem, network, environment, and process authority as the Code Mode server's surrounding Pi environment. Do not add a stricter capability/security sandbox solely around Code Mode.
|
|
28
|
-
- Use the standalone MCP process as the fault-containment boundary. Pi can time out, terminate, and restart it if generated code wedges the process. Do not add a child process or isolate per execution without operational evidence that the server boundary is insufficient.
|
|
29
|
-
- Preserve upstream MCP behavior across the nested boundary, especially OAuth, cancellation, rich content, and elicitation responses. Never fabricate `accept` or `decline`; unsupported interaction returns `cancel`.
|
|
30
|
-
|
|
31
|
-
Normal Pi tools remain available directly. This decision adds MCP composition; it does not attempt to turn every Pi tool into a nested Code Mode function.
|
|
32
|
-
|
|
33
|
-
## Rationale
|
|
34
|
-
|
|
35
|
-
The process boundary provides the practical reliability benefit—one malformed snippet cannot freeze Pi—without creating an artificial authority mismatch between Pi and Code Mode. A separate security sandbox would constrain only one route while the same agent can already use Pi's deliberately unrestricted tools.
|
|
36
|
-
|
|
37
|
-
Keeping Code Mode separate also preserves this package's upstream contract, works with agent-agnostic MCP clients, and avoids coupling the implementation to Pi internals.
|
|
38
|
-
|
|
39
|
-
## Consequences
|
|
40
|
-
|
|
41
|
-
- Code Mode is a separate package and lifecycle from Computer Use.
|
|
42
|
-
- The first implementation must close the protocol-fidelity gaps found in existing standalone proxies rather than adopting one unchanged.
|
|
43
|
-
- Pi's MCP adapter remains responsible for the outer client connection and UI; the Code Mode server remains responsible for its upstream MCP connections and execution lifecycle.
|
|
44
|
-
- Process timeout, termination, restart, cleanup, and end-to-end elicitation tests are release requirements.
|
|
45
|
-
- Additional execution isolation remains an evidence-driven future option, not a default architectural requirement.
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
# Pi integration
|
|
2
|
-
|
|
3
|
-
The native Pi adapter is the primary 0.3 path. It registers and activates ten namespaced typed tools together so Pi itself chooses each official Computer Use method and argument.
|
|
4
|
-
|
|
5
|
-
## Source checkout acceptance
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
npm ci
|
|
9
|
-
npm run build
|
|
10
|
-
CODEX_COMPUTER_USE_HOME="$(mktemp -d)" \
|
|
11
|
-
pi -ne -e /absolute/path/to/codex-computer-use-mcp/integrations/pi/index.ts
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
`-ne` prevents an installed 0.1 adapter from loading at the same time. This source workflow does not install or switch live Pi configuration.
|
|
15
|
-
|
|
16
|
-
For normal installation, use the exact version 0.3.3 npm package:
|
|
17
|
-
|
|
18
|
-
```bash
|
|
19
|
-
pi install npm:codex-computer-use-mcp@0.3.3
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
Use the source workflow only when you need to test an exact reviewed commit. Follow the rollback procedure in `MIGRATION.md` when replacing version 0.1.
|
|
23
|
-
|
|
24
|
-
## Registered surface
|
|
25
|
-
|
|
26
|
-
All ten tools are registered and active from session start:
|
|
27
|
-
|
|
28
|
-
- `computer_use_list_apps`
|
|
29
|
-
- `computer_use_get_app_state`
|
|
30
|
-
- `computer_use_click`
|
|
31
|
-
- `computer_use_perform_secondary_action`
|
|
32
|
-
- `computer_use_set_value`
|
|
33
|
-
- `computer_use_select_text`
|
|
34
|
-
- `computer_use_scroll`
|
|
35
|
-
- `computer_use_drag`
|
|
36
|
-
- `computer_use_press_key`
|
|
37
|
-
- `computer_use_type_text`
|
|
38
|
-
|
|
39
|
-
Command:
|
|
40
|
-
|
|
41
|
-
- `/computer-use-status`
|
|
42
|
-
|
|
43
|
-
Session-start activation is purely additive and preserves every active tool owned by Pi or another extension. The interaction definitions rely on their official descriptions and omit extra prompt metadata.
|
|
44
|
-
|
|
45
|
-
A successful `get_app_state` retains its verified app-server runtime, thread, and signed Computer Use client. The following direct interaction uses that same client and official active-app lease, then closes the retained process tree with strict cleanup verification. A new inspection replaces and closes any earlier retained session; Pi `session_shutdown` also closes it.
|
|
46
|
-
|
|
47
|
-
No-permissions is the only policy: all ten tools are registered and available through this lifecycle with no wrapper permission prompts, mode selector, or app/intent/action gate. The signed host runs with Codex Full access (`approvalPolicy: "never"`, `sandbox: "danger-full-access"`), so normal empty-schema Computer Use app approvals are accepted by Codex before they reach Pi. Pi renders any form, OpenAI-form, or URL elicitation app-server does emit. The user's `accept`, `decline`, or `cancel` response is returned unchanged; the adapter never fabricates one.
|
|
48
|
-
|
|
49
|
-
## Generic MCP gateway
|
|
50
|
-
|
|
51
|
-
Merge `mcp.json.example` only for the exact 0.3.3 package or after building an exact reviewed source commit. `directTools: false` is intentional; it keeps this powerful generic MCP surface behind Pi's gateway.
|
|
52
|
-
|
|
53
|
-
For a source checkout:
|
|
54
|
-
|
|
55
|
-
```json
|
|
56
|
-
{
|
|
57
|
-
"mcpServers": {
|
|
58
|
-
"computer-use-direct-source": {
|
|
59
|
-
"command": "node",
|
|
60
|
-
"args": ["/absolute/path/to/codex-computer-use-mcp/dist/mcp-server.js"],
|
|
61
|
-
"lifecycle": "lazy",
|
|
62
|
-
"requestTimeoutMs": 180000,
|
|
63
|
-
"directTools": false
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
Do not load the native adapter and generic MCP adapter into the same acceptance process unless tool names are intentionally isolated.
|
|
70
|
-
|
|
71
|
-
The generic MCP path uses the same durable no-permissions and official Full access policy: all ten methods and no wrapper permission gate. App-access approvals resolve inside Codex. Any standard form or URL elicitation app-server emits is forwarded to the invoking MCP client; an unsupported or headless client returns `cancel` rather than a fabricated decision.
|
|
72
|
-
|
|
73
|
-
See the root `MIGRATION.md` before replacing an installed 0.1 adapter.
|