@minhspark/codex-mcp-bridge 1.12.0 → 1.12.2
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 +34 -0
- package/README.md +47 -31
- package/package.json +2 -2
- package/scripts/check-claude-bridge.mjs +24 -20
- package/scripts/check.mjs +13 -7
- package/scripts/install-claude-desktop.mjs +1 -0
- package/scripts/install-native-relay.mjs +12 -27
- package/scripts/smoke.mjs +38 -35
- package/scripts/sync-version.mjs +1 -0
- package/src/app-server-client.mjs +271 -65
- package/src/claude-bridge.mjs +6 -6
- package/src/index.mjs +115 -81
- package/src/native-relay-companion.mjs +113 -68
- package/src/native-relay.mjs +181 -37
- package/src/peer-protocol.mjs +129 -27
- package/src/platform.mjs +30 -4
- package/src/thread-delivery.mjs +24 -8
- package/src/turn.mjs +28 -10
package/src/thread-delivery.mjs
CHANGED
|
@@ -17,12 +17,17 @@ import { runTurn } from "./turn.mjs";
|
|
|
17
17
|
*/
|
|
18
18
|
export const NATIVE_BACKEND = "codex-desktop-native";
|
|
19
19
|
export const APP_SERVER_BACKEND = "app-server";
|
|
20
|
+
const RELEASE_STATUSES = new Set(["completed", "interrupted", "failed"]);
|
|
20
21
|
|
|
21
22
|
export function createThreadDelivery({
|
|
22
23
|
codex,
|
|
23
24
|
relay = new NativeDesktopRelay(),
|
|
24
25
|
log = () => {},
|
|
25
26
|
timeoutMs = 240000,
|
|
27
|
+
releaseAfterTurn =
|
|
28
|
+
process.env.CODEX_BRIDGE_RELEASE_AFTER_TURN !== undefined
|
|
29
|
+
? process.env.CODEX_BRIDGE_RELEASE_AFTER_TURN === "1"
|
|
30
|
+
: process.platform === "win32",
|
|
26
31
|
} = {}) {
|
|
27
32
|
let reportedUnavailable = null;
|
|
28
33
|
|
|
@@ -42,7 +47,7 @@ export function createThreadDelivery({
|
|
|
42
47
|
reportedUnavailable = null;
|
|
43
48
|
return { backend: NATIVE_BACKEND, threadId, ack };
|
|
44
49
|
} catch (err) {
|
|
45
|
-
if (err.reachedCompanion) throw err;
|
|
50
|
+
if (err.reachedCompanion || err.code !== "RELAY_UNREACHABLE") throw err;
|
|
46
51
|
log(`native relay unreachable (${err.message}); falling back to the app-server path`);
|
|
47
52
|
}
|
|
48
53
|
} else if (status.reason !== reportedUnavailable) {
|
|
@@ -51,13 +56,24 @@ export function createThreadDelivery({
|
|
|
51
56
|
}
|
|
52
57
|
|
|
53
58
|
if (!codex) throw new Error("No Codex app-server client is configured to deliver this message");
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
const send = async () => {
|
|
60
|
+
await codex.ensureThreadAttached(threadId);
|
|
61
|
+
const turn = await runTurn(codex, {
|
|
62
|
+
threadId,
|
|
63
|
+
input: [{ type: "text", text }],
|
|
64
|
+
timeoutMs,
|
|
65
|
+
});
|
|
66
|
+
if (releaseAfterTurn && RELEASE_STATUSES.has(turn.status) && typeof codex.releaseThread === "function") {
|
|
67
|
+
try {
|
|
68
|
+
const released = await codex.releaseThread(threadId);
|
|
69
|
+
if (!released?.released) log("thread release pending: " + (released.reason ?? released.status ?? "awaiting unload"));
|
|
70
|
+
} catch (err) {
|
|
71
|
+
log("thread release failed: " + err.message);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return { backend: APP_SERVER_BACKEND, threadId, turn };
|
|
75
|
+
};
|
|
76
|
+
return codex.withThread ? codex.withThread(threadId, send) : send();
|
|
61
77
|
}
|
|
62
78
|
|
|
63
79
|
function describe() {
|
package/src/turn.mjs
CHANGED
|
@@ -54,10 +54,16 @@ export async function runTurn(client, { threadId, input, timeoutMs = 240000, tur
|
|
|
54
54
|
});
|
|
55
55
|
|
|
56
56
|
const process = (msg) => {
|
|
57
|
+
if (settled) return;
|
|
57
58
|
const params = msg.params ?? {};
|
|
58
59
|
if (turnId && params.turnId && params.turnId !== turnId) return;
|
|
59
60
|
|
|
60
61
|
switch (msg.method) {
|
|
62
|
+
case "thread/closed": {
|
|
63
|
+
settled = true;
|
|
64
|
+
resolveDone({ status: "disconnected", error: { message: "The thread closed before its turn completed" } });
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
61
67
|
case "item/completed": {
|
|
62
68
|
const summary = summarizeItem(params.item);
|
|
63
69
|
if (!summary) return;
|
|
@@ -90,7 +96,7 @@ export async function runTurn(client, { threadId, input, timeoutMs = 240000, tur
|
|
|
90
96
|
};
|
|
91
97
|
|
|
92
98
|
const unsubscribe = client.subscribe(threadId, (msg) => {
|
|
93
|
-
if (!turnId) {
|
|
99
|
+
if (!turnId && msg.method !== "thread/closed") {
|
|
94
100
|
buffered.push(msg);
|
|
95
101
|
return;
|
|
96
102
|
}
|
|
@@ -113,15 +119,27 @@ export async function runTurn(client, { threadId, input, timeoutMs = 240000, tur
|
|
|
113
119
|
});
|
|
114
120
|
|
|
115
121
|
try {
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
122
|
+
const start = await Promise.race([
|
|
123
|
+
client.request(
|
|
124
|
+
"turn/start",
|
|
125
|
+
{ ...turnOverrides, threadId, input },
|
|
126
|
+
{ timeoutMs: Math.min(timeoutMs, 60000) },
|
|
127
|
+
).then((started) => ({ started }), (error) => ({ error })),
|
|
128
|
+
done.then((outcome) => ({ outcome })),
|
|
129
|
+
]);
|
|
130
|
+
if (start.error) throw start.error;
|
|
131
|
+
let outcome = start.outcome;
|
|
132
|
+
if (!outcome) {
|
|
133
|
+
turnId = start.started?.turn?.id ?? null;
|
|
134
|
+
if (typeof turnId !== "string" || !turnId.trim()) {
|
|
135
|
+
throw new Error("Codex app-server did not return a turn id for turn/start");
|
|
136
|
+
}
|
|
137
|
+
for (const msg of buffered.splice(0)) process(msg);
|
|
138
|
+
if (TERMINAL_STATUSES.has(start.started?.turn?.status)) {
|
|
139
|
+
process({ method: "turn/completed", params: { turn: start.started.turn } });
|
|
140
|
+
}
|
|
141
|
+
outcome = await done;
|
|
142
|
+
}
|
|
125
143
|
return {
|
|
126
144
|
threadId,
|
|
127
145
|
turnId,
|