@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.
@@ -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 response = await this.relay.requestDesktop(operation, args, remaining === undefined ? undefined : { timeoutMs: remaining });
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: this.relay.socketPath, localProjects: response.projects.filter((project) => project.projectKind === "local" && project.hostId === "local").length };
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: this.relay.socketPath, reason: err.message };
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 options = { cwd, promptHash: identity.promptHash, deadline };
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) {