@minhspark/codex-mcp-bridge 1.13.9 → 1.15.1
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 +47 -0
- package/README.md +53 -24
- package/package.json +6 -6
- package/scripts/check.mjs +6 -1
- package/scripts/install-claude-desktop.mjs +22 -7
- package/scripts/install-codex-mcp.mjs +8 -5
- package/scripts/install-native-relay.mjs +15 -9
- package/src/bridge-account-context.mjs +77 -0
- package/src/claude-bridge-launcher.mjs +3 -0
- package/src/claude-bridge.mjs +91 -28
- package/src/claude-desktop-context.mjs +11 -6
- package/src/claude-sender-context.mjs +206 -0
- package/src/claude-session-router.mjs +92 -0
- package/src/codex-account-context.mjs +116 -0
- package/src/codex-bridge-launcher.mjs +3 -0
- package/src/codex-mcp-registration.mjs +14 -9
- package/src/desktop-account-context.mjs +140 -0
- package/src/desktop-task-receipts.mjs +3 -1
- package/src/index.mjs +91 -9
- package/src/mcp-supervisor.mjs +326 -0
- package/src/native-relay-companion.mjs +173 -34
- package/src/native-relay-launcher.mjs +3 -0
- package/src/native-relay.mjs +234 -50
- package/src/peer-protocol.mjs +120 -17
- package/src/platform.mjs +4 -1
- package/src/release-snapshot.mjs +198 -0
- package/src/reload-control.mjs +102 -0
- package/src/reply-forwarder.mjs +43 -0
- package/src/thread-delivery.mjs +27 -11
package/src/thread-delivery.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { NativeDesktopRelay, desktopTaskSocketPath, desktopTasksConfigured } from "./native-relay.mjs";
|
|
1
|
+
import { NativeDesktopRelay, accountRelaySocketPath, desktopTaskSocketPath, desktopTasksConfigured } from "./native-relay.mjs";
|
|
2
2
|
import { runTurn } from "./turn.mjs";
|
|
3
3
|
import { realpathSync } from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
@@ -42,20 +42,28 @@ export function matchDesktopProject(projects, cwd, { canonicalize = realpathSync
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
export class DesktopTaskDelivery {
|
|
45
|
-
constructor({ relay = new NativeDesktopRelay({ socketPath: desktopTaskSocketPath() }), security, sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)), now = Date.now, receipts = new DesktopTaskReceipts() } = {}) {
|
|
45
|
+
constructor({ relay = new NativeDesktopRelay({ socketPath: desktopTaskSocketPath(), accountSocketPath: accountRelaySocketPath() }), security, sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)), now = Date.now, receipts = new DesktopTaskReceipts(), beforeRequest, accountContext } = {}) {
|
|
46
46
|
this.relay = relay;
|
|
47
47
|
this.security = security;
|
|
48
48
|
this.sleep = sleep;
|
|
49
49
|
this.now = now;
|
|
50
50
|
this.receipts = receipts;
|
|
51
|
+
this.beforeRequest = beforeRequest;
|
|
52
|
+
this.accountContext = accountContext;
|
|
51
53
|
this.threadOperations = new Map();
|
|
52
54
|
}
|
|
53
55
|
|
|
54
56
|
async request(operation, args, { deadline } = {}) {
|
|
55
57
|
try {
|
|
58
|
+
await this.beforeRequest?.({ operation, args });
|
|
56
59
|
const remaining = deadline === undefined ? undefined : deadline - this.now();
|
|
57
60
|
if (remaining !== undefined && remaining <= 0) throw new Error("The bridge's response deadline has elapsed; this operation was not sent");
|
|
58
|
-
const
|
|
61
|
+
const accountContext = this.accountContext?.();
|
|
62
|
+
const response = await this.relay.requestDesktop(operation, args, {
|
|
63
|
+
...(remaining === undefined ? {} : { timeoutMs: remaining }),
|
|
64
|
+
...(this.beforeRequest ? { beforeSend: () => this.beforeRequest({ operation, args, phase: "write" }) } : {}),
|
|
65
|
+
...(accountContext ? { accountContext } : {}),
|
|
66
|
+
});
|
|
59
67
|
return response.result;
|
|
60
68
|
} catch (err) {
|
|
61
69
|
throw new Error(`Codex Desktop operation ${operation} failed: ${err.message}. Desktop-only mode will not start or use an external app-server. Open Codex Desktop and reconnect its native relay, then inspect the existing task before retrying a send.`, { cause: err });
|
|
@@ -87,12 +95,15 @@ export class DesktopTaskDelivery {
|
|
|
87
95
|
}
|
|
88
96
|
|
|
89
97
|
async status() {
|
|
98
|
+
const accountContext = this.accountContext?.();
|
|
99
|
+
const socketPath = this.relay.status?.({ accountContext })?.socketPath ?? this.relay.socketPath;
|
|
100
|
+
if (this.accountContext && !accountContext) return { available: false, socketPath, reason: "The current Claude and Codex accounts could not both be verified. Finish signing in and inspect the account diagnostics before sending work." };
|
|
90
101
|
try {
|
|
91
102
|
const response = await this.request("list_projects", {});
|
|
92
103
|
if (!Array.isArray(response?.projects)) throw new Error("Desktop returned no project list");
|
|
93
|
-
return { available: true, socketPath
|
|
104
|
+
return { available: true, socketPath, localProjects: response.projects.filter((project) => project.projectKind === "local" && project.hostId === "local").length };
|
|
94
105
|
} catch (err) {
|
|
95
|
-
return { available: false, socketPath
|
|
106
|
+
return { available: false, socketPath, reason: err.message };
|
|
96
107
|
}
|
|
97
108
|
}
|
|
98
109
|
|
|
@@ -118,8 +129,11 @@ export class DesktopTaskDelivery {
|
|
|
118
129
|
return { rows, coverage: "Codex Desktop's recent/pinned snapshot; local Codex workspaces only. Agent-created tasks visible in the sidebar can be omitted; read a known task ID directly before assuming creation failed." };
|
|
119
130
|
}
|
|
120
131
|
|
|
121
|
-
async reuseReceipt(receipt, { cwd, promptHash, deadline }) {
|
|
132
|
+
async reuseReceipt(receipt, { cwd, promptHash, deadline, accountContext }) {
|
|
122
133
|
if (!receipt) return null;
|
|
134
|
+
if (accountContext && (!receipt.accountContext || ["claude", "codex"].some((provider) => receipt.accountContext[provider] !== accountContext[provider]))) {
|
|
135
|
+
throw new Error(`The existing Desktop creation receipt ${receipt.threadId ? `for task ${receipt.threadId} ` : ""}${receipt.accountContext ? "belongs to different accounts" : "has no verified original account binding"}. Its receipt and task ID were retained. No creation or prompt resend was attempted; inspect the explicit existing task before continuing.`);
|
|
136
|
+
}
|
|
123
137
|
if (path.relative(cwd, realpathSync.native(receipt.cwd))) throw new Error("The stored Desktop creation receipt belongs to another workspace. No prompt was sent.");
|
|
124
138
|
if (receipt.state !== "known") throw new Error(`An earlier Desktop creation is ${receipt.state} and may already have started. ${receipt.threadId ? `threadId: ${receipt.threadId}. ` : ""}Do not resend or create another task; inspect the existing Desktop task and resolve its creation receipt first.`);
|
|
125
139
|
let response;
|
|
@@ -180,7 +194,9 @@ export class DesktopTaskDelivery {
|
|
|
180
194
|
this.security.assertCwd(cwd);
|
|
181
195
|
dedupeName = dedupeName?.normalize("NFC").trim().replace(/\s+/g, " ") || undefined;
|
|
182
196
|
const identity = this.receipts.key({ cwd, prompt, name: dedupeName });
|
|
183
|
-
const
|
|
197
|
+
const accountContext = this.accountContext?.();
|
|
198
|
+
if (this.accountContext && !accountContext) throw new Error("Desktop creation requires the original verified account context.");
|
|
199
|
+
const options = { cwd, promptHash: identity.promptHash, deadline, accountContext };
|
|
184
200
|
const reused = await this.reuseReceipt(await this.receipts.read(identity.key), options);
|
|
185
201
|
if (reused) return reused;
|
|
186
202
|
const listed = await this.request("list_projects", {}, { deadline });
|
|
@@ -190,7 +206,7 @@ export class DesktopTaskDelivery {
|
|
|
190
206
|
const existing = await this.reuseReceipt(await this.receipts.read(identity.key), options);
|
|
191
207
|
if (existing) return existing;
|
|
192
208
|
if (this.now() >= deadline) throw new Error("The response deadline elapsed before Desktop creation. No task was created.");
|
|
193
|
-
const receipt = { version: 1, ...identity, cwd, state: "pending", startedAt: this.now(), ...(dedupeName ? { name: dedupeName } : {}), projectId: project.projectId, ...(project.label ? { projectName: project.label } : {}) };
|
|
209
|
+
const receipt = { version: 1, ...identity, cwd, state: "pending", startedAt: this.now(), ...(accountContext ? { accountContext: { ...accountContext } } : {}), ...(dedupeName ? { name: dedupeName } : {}), projectId: project.projectId, ...(project.label ? { projectName: project.label } : {}) };
|
|
194
210
|
await this.receipts.write(identity.key, receipt);
|
|
195
211
|
let threadId;
|
|
196
212
|
let creationConfirmed = false;
|
|
@@ -309,11 +325,11 @@ export function createThreadDelivery({
|
|
|
309
325
|
* app-server only spawns a process that contends for the ~/.codex state and
|
|
310
326
|
* then fails on the writer lock the native path exists to avoid.
|
|
311
327
|
*/
|
|
312
|
-
async function deliver(threadId, text) {
|
|
313
|
-
const status = relay.status();
|
|
328
|
+
async function deliver(threadId, text, options = {}) {
|
|
329
|
+
const status = relay.status(options);
|
|
314
330
|
if (status.enabled) {
|
|
315
331
|
try {
|
|
316
|
-
const ack = await relay.sendMessage(threadId, text);
|
|
332
|
+
const ack = await relay.sendMessage(threadId, text, options);
|
|
317
333
|
reportedUnavailable = null;
|
|
318
334
|
return { backend: NATIVE_BACKEND, threadId, ack };
|
|
319
335
|
} catch (err) {
|