@minhspark/codex-mcp-bridge 1.25.2 → 1.25.3
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/.codex-plugin/plugin.json +1 -1
- package/CHANGELOG.md +27 -0
- package/README.md +38 -14
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/scripts/install-antigravity-bridge.mjs +40 -10
- package/scripts/install-claude-code.mjs +2 -2
- package/scripts/install-claude-desktop.mjs +12 -7
- package/scripts/install-native-relay.mjs +2 -2
- package/scripts/setup.mjs +3 -3
- package/src/antigravity-bridge.mjs +79 -25
- package/src/antigravity-deliveries.mjs +32 -8
- package/src/antigravity-ipc.mjs +18 -9
- package/src/antigravity-outbound-bridge.mjs +46 -19
- package/src/antigravity-outbound-targets.mjs +41 -6
- package/src/bridge-readiness.mjs +2 -2
- package/src/claude-bridge.mjs +38 -23
- package/src/claude-code-registration.mjs +2 -1
- package/src/claude-session-router.mjs +8 -15
- package/src/index.mjs +33 -21
- package/src/mcp-supervisor.mjs +10 -6
- package/src/native-relay-companion.mjs +4 -1
- package/src/native-relay.mjs +5 -1
- package/src/peer-protocol.mjs +9 -6
- package/src/recipient-preflight.mjs +81 -0
- package/src/setup.mjs +38 -18
- package/src/thread-delivery.mjs +10 -5
|
@@ -1,5 +1,86 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
|
|
4
|
+
const { version } = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
|
|
5
|
+
const diagnosticCode = (value) => typeof value === "string" && /^[A-Z][A-Z0-9_]{0,79}$/.test(value) ? value : null;
|
|
6
|
+
const diagnosticId = (value) => typeof value === "string" && /^(?:[a-f0-9]{64}|(?:local_)?[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12})$/i.test(value) ? value : null;
|
|
7
|
+
|
|
8
|
+
export function diagnosticText(value) {
|
|
9
|
+
return String(value ?? "Unknown bridge failure")
|
|
10
|
+
.replace(/\{[\s\S]*\}|\[\s*(?:\{|"|true|false|null|\d)[\s\S]*\]/g, "[structured details omitted]")
|
|
11
|
+
.replace(/\b(?:Bearer|Basic)\s+[A-Za-z0-9._~+/=-]+/gi, "[credential]")
|
|
12
|
+
.replace(/((?:access[_-]?token|refresh[_-]?token|api[_-]?key|token|secret|password|authorization)\s*["']?\s*[:=]\s*)["']?[^\s,"';}]+["']?/gi, "$1[redacted]")
|
|
13
|
+
.replace(/https?:\/\/[^\s<>"']+/gi, "[url]")
|
|
14
|
+
.replace(/(?:[a-z]:[\\/]|\\\\)[^\r\n"'<>|,;]*/gi, "[path]")
|
|
15
|
+
.replace(/(^|[\s"'(])\/(?:Users|home|tmp|var|private|Volumes|mnt)\/[^\r\n"'<>|,;]*/g, "$1[path]")
|
|
16
|
+
.replace(/[\w.+-]+@[\w.-]+\.[a-z]{2,}/gi, "[email]")
|
|
17
|
+
.replace(/\b(?:sk-[a-z0-9_-]+|gh[pousr]_[a-z0-9_]+)\b/gi, "[credential]")
|
|
18
|
+
.replace(/[\u0000-\u001f\u007f]/g, " ").slice(0, 1400);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function bridgeDiagnostic(error, options = {}) {
|
|
22
|
+
const previous = error?.diagnostic?.schemaVersion === 1 ? error.diagnostic : null;
|
|
23
|
+
const chain = [];
|
|
24
|
+
const seen = new Set();
|
|
25
|
+
for (let cause = error; cause && !seen.has(cause) && chain.length < 8; cause = cause.cause) {
|
|
26
|
+
seen.add(cause);
|
|
27
|
+
chain.push(cause);
|
|
28
|
+
}
|
|
29
|
+
const codes = [...new Set(chain.flatMap((cause) => [cause?.preflight?.code, cause?.code, cause?.reasonCode, cause?.diagnostic?.code]).map(diagnosticCode).filter(Boolean))];
|
|
30
|
+
const code = diagnosticCode(previous?.code) ?? diagnosticCode(options.code) ?? codes[0] ?? "BRIDGE_OPERATION_FAILED";
|
|
31
|
+
const state = options.deliveryState ?? previous?.deliveryState ?? error?.deliveryState;
|
|
32
|
+
const deliveryState = state === "not_applicable" || options.readOnly && !previous ? "not_applicable"
|
|
33
|
+
: ["not_sent", "not-sent"].includes(state) ? "not_sent"
|
|
34
|
+
: state === "sent" ? "sent"
|
|
35
|
+
: state === "unknown" || error?.deliveryUncertain ? "unknown"
|
|
36
|
+
: chain.some((cause) => cause?.sent === false || cause?.preflight?.sent === false) ? "not_sent" : "unknown";
|
|
37
|
+
const name = (value, fallback) => typeof value === "string" && /^[a-zA-Z][a-zA-Z0-9_./-]{0,99}$/.test(value) ? value : fallback;
|
|
38
|
+
const ids = {};
|
|
39
|
+
for (const key of ["requestId", "msgId", "deliveryId", "threadId", "taskId", "conversationId"]) {
|
|
40
|
+
const id = diagnosticId(previous?.ids?.[key] ?? options[key] ?? error?.[key]);
|
|
41
|
+
if (id) ids[key] = id;
|
|
42
|
+
}
|
|
43
|
+
const timestamp = previous?.timestamp;
|
|
44
|
+
return {
|
|
45
|
+
schemaVersion: 1,
|
|
46
|
+
reportId: diagnosticId(previous?.reportId) ?? randomUUID(),
|
|
47
|
+
timestamp: typeof timestamp === "string" && /^\d{4}-\d{2}-\d{2}T[\d:.]+Z$/.test(timestamp) ? timestamp : new Date().toISOString(),
|
|
48
|
+
component: name(previous?.component ?? options.component, "bridge"),
|
|
49
|
+
operation: name(previous?.operation === "unknown" ? options.operation : previous?.operation ?? options.operation, "unknown"),
|
|
50
|
+
version: typeof previous?.version === "string" && /^\d+\.\d+\.\d+(?:-[a-z0-9.-]+)?$/i.test(previous.version) ? previous.version : version,
|
|
51
|
+
platform: process.platform,
|
|
52
|
+
nodeVersion: process.versions.node,
|
|
53
|
+
code,
|
|
54
|
+
causeCodes: [...new Set([...(Array.isArray(previous?.causeCodes) ? previous.causeCodes : []).map(diagnosticCode).filter(Boolean), ...codes])].filter((value) => value !== code).slice(0, 8),
|
|
55
|
+
phase: name(previous?.phase ?? options.phase ?? error?.phase ?? error?.diagnostic?.stage, deliveryState === "not_sent" ? "preflight" : options.readOnly ? "inspection" : "delivery"),
|
|
56
|
+
deliveryState,
|
|
57
|
+
sent: deliveryState === "not_sent" ? false : deliveryState === "sent" ? true : null,
|
|
58
|
+
retrySafe: deliveryState === "not_sent",
|
|
59
|
+
nextAction: deliveryState === "not_sent" ? "Inspect the reported cause and bridge status before retrying. Do not open or create chats to load a relay."
|
|
60
|
+
: deliveryState === "not_applicable" ? "Inspect bridge status and include this diagnostic in the report."
|
|
61
|
+
: "Inspect the original delivery receipt. Do not resend automatically or create a replacement chat.",
|
|
62
|
+
reason: diagnosticText(previous?.reason ?? error?.preflight?.reason ?? error?.message ?? error),
|
|
63
|
+
ids,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function withBridgeDiagnostic(result, error, options = {}) {
|
|
68
|
+
const diagnostic = bridgeDiagnostic({ ...error, cause: error?.cause, message: error?.message ?? String(error ?? "Bridge operation failed"), diagnostic: result?.structuredContent?.diagnostic ?? result?.diagnostic ?? error?.diagnostic }, options);
|
|
69
|
+
const report = ["Bridge diagnostic (include this block in a bug report)", `${diagnostic.code} | ${diagnostic.component} / ${diagnostic.operation}`,
|
|
70
|
+
`Bridge ${diagnostic.version} | ${diagnostic.platform} | Node ${diagnostic.nodeVersion}`,
|
|
71
|
+
`Phase: ${diagnostic.phase} | Delivery: ${diagnostic.deliveryState} | Sent: ${diagnostic.sent ?? "unconfirmed"}`,
|
|
72
|
+
`Reason: ${diagnostic.reason}`, ...(diagnostic.causeCodes.length ? [`Cause codes: ${diagnostic.causeCodes.join(", ")}`] : []),
|
|
73
|
+
...Object.entries(diagnostic.ids).map(([key, value]) => `${key}: ${value}`),
|
|
74
|
+
`Report: ${diagnostic.reportId} | ${diagnostic.timestamp}`, diagnostic.nextAction].join("\n");
|
|
75
|
+
return { ...result, content: [...(result?.content ?? []).filter((item) => item.type !== "text" || !item.text.startsWith("Bridge diagnostic (")), { type: "text", text: report }],
|
|
76
|
+
structuredContent: { ...result?.structuredContent, diagnostic } };
|
|
77
|
+
}
|
|
78
|
+
|
|
1
79
|
export function preflightFailure(code, reason) {
|
|
2
80
|
const error = new Error(`${reason} No message was sent.`);
|
|
81
|
+
error.code = code;
|
|
82
|
+
error.deliveryState = "not_sent";
|
|
83
|
+
error.sent = false;
|
|
3
84
|
error.preflight = { status: "blocked", code, reason, sent: false };
|
|
4
85
|
return error;
|
|
5
86
|
}
|
package/src/setup.mjs
CHANGED
|
@@ -89,7 +89,7 @@ export function setupEnvironment(env, paths, codex) {
|
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
function configurationCheck(label, check, components) {
|
|
92
|
-
try { check(); components.push({ name: label, status: "configured" }); }
|
|
92
|
+
try { const details = check(); components.push({ name: label, status: "configured", ...details }); }
|
|
93
93
|
catch (error) { components.push({ name: label, status: "incomplete", detail: error.message }); }
|
|
94
94
|
}
|
|
95
95
|
|
|
@@ -99,8 +99,9 @@ function assertEntry(entry, worker, root, { caller } = {}) {
|
|
|
99
99
|
const expected = worker === "index.mjs" || worker === "claude-bridge.mjs" || worker === "native-relay-companion.mjs"
|
|
100
100
|
? [path.join(root, "src", "mcp-supervisor.mjs"), worker] : [path.join(root, "src", worker)];
|
|
101
101
|
if (JSON.stringify(args) !== JSON.stringify(expected)) throw new Error("MCP entry points to a different installation or unsupported launcher");
|
|
102
|
-
if (entry.enabled === false || entry.disabled === true) throw new Error("MCP entry is disabled; the saved choice was preserved");
|
|
103
102
|
if (caller && entry.env?.ANTIGRAVITY_BRIDGE_CALLER !== caller) throw new Error("Antigravity caller registration is missing or conflicting");
|
|
103
|
+
if (entry.enabled === false || entry.disabled === true) return { deliveryEnabled: false,
|
|
104
|
+
notices: ["This MCP registration is intentionally disabled. Its saved choice was preserved; delivery through this connector is disabled."] };
|
|
104
105
|
}
|
|
105
106
|
|
|
106
107
|
export function checkSetup({ env = process.env, platform = process.platform, root = ROOT, run = execute, resolve = resolveCodexBin } = {}) {
|
|
@@ -119,8 +120,9 @@ export function checkSetup({ env = process.env, platform = process.platform, roo
|
|
|
119
120
|
const item = inventory?.find((server) => server.name === name);
|
|
120
121
|
if (!item) throw new Error("MCP registration is missing");
|
|
121
122
|
const entry = JSON.parse(run(codex, ["mcp", "get", name, "--json"], setupEnvironment(env, paths, codex), 30000));
|
|
122
|
-
assertEntry({ ...entry.transport, enabled: entry.enabled }, worker, root, name === "antigravity-bridge" ? { caller: "codex" } : {});
|
|
123
|
+
const details = assertEntry({ ...entry.transport, enabled: entry.enabled }, worker, root, name === "antigravity-bridge" ? { caller: "codex" } : {});
|
|
123
124
|
if (name === "codex-native-relay" && !entry.transport.env_vars?.includes("CODEX_APP_TOOLS_PIPE_PATH")) throw new Error("Native pipe forwarding is missing");
|
|
125
|
+
return details;
|
|
124
126
|
}, components);
|
|
125
127
|
}
|
|
126
128
|
for (const [label, file, name, worker, caller] of [
|
|
@@ -130,14 +132,26 @@ export function checkSetup({ env = process.env, platform = process.platform, roo
|
|
|
130
132
|
["Antigravity / agent-bridge", path.join(paths.gemini, "mcp_config.json"), "agent-bridge", "antigravity-outbound-bridge.mjs"],
|
|
131
133
|
]) configurationCheck(label, () => {
|
|
132
134
|
const entry = readJson(file)?.mcpServers?.[name];
|
|
133
|
-
assertEntry(entry, worker, root, { caller });
|
|
135
|
+
const details = assertEntry(entry, worker, root, { caller });
|
|
136
|
+
if (worker === "index.mjs" && entry.env?.CODEX_BRIDGE_OPEN_IN_APP === "1") {
|
|
137
|
+
throw new Error("Managed bridge settings still allow opening Codex Desktop. Run codex-bridge-setup to apply the current silent configuration.");
|
|
138
|
+
}
|
|
134
139
|
if (name === "codex-bridge-desktop" && entry.env?.CODEX_BRIDGE_DESKTOP_TASKS === "1") {
|
|
135
|
-
|
|
140
|
+
return { ...details, nativeCodeOnly: true, sharedChatDelivery: "unsupported",
|
|
141
|
+
notices: [...(details?.notices ?? []), "The saved native Code-only policy was preserved. Ordinary Claude Desktop Chat cannot deliver through this connector; native delivery uses the registered Claude Desktop Code connector and remains unverified by setup."] };
|
|
136
142
|
}
|
|
143
|
+
return details;
|
|
144
|
+
}, components);
|
|
145
|
+
configurationCheck("Claude Desktop / background preference", () => {
|
|
146
|
+
if (readJson(paths.desktop)?.preferences?.menuBarEnabled !== true) throw new Error("The managed Claude background preference is not enabled. Run codex-bridge-setup to apply it; live activation is verified separately.");
|
|
137
147
|
}, components);
|
|
138
148
|
configurationCheck("Antigravity / codex-bridge sidecar", () => {
|
|
139
|
-
assertEntry(readJson(path.join(paths.gemini, "sidecars", "codex-bridge", "sidecar.json")), "antigravity-sidecar.mjs", root);
|
|
140
|
-
|
|
149
|
+
const details = assertEntry(readJson(path.join(paths.gemini, "sidecars", "codex-bridge", "sidecar.json")), "antigravity-sidecar.mjs", root);
|
|
150
|
+
const enabled = readJson(path.join(paths.gemini, "config.json"))?.sidecars?.["codex-bridge"]?.enabled;
|
|
151
|
+
if (typeof enabled !== "boolean") throw new Error("Antigravity sidecar enabled setting is missing or invalid");
|
|
152
|
+
if (!enabled) return { deliveryEnabled: false,
|
|
153
|
+
notices: [...(details?.notices ?? []), "The Antigravity bridge sidecar is intentionally disabled. Its saved choice was preserved; native Desktop delivery through this sidecar is disabled."] };
|
|
154
|
+
return details;
|
|
141
155
|
}, components);
|
|
142
156
|
if (native) configurationCheck("Native relay executor", () => {
|
|
143
157
|
const relay = readJson(path.join(paths.codex, "native-relay.json"));
|
|
@@ -151,7 +165,7 @@ export function checkSetup({ env = process.env, platform = process.platform, roo
|
|
|
151
165
|
|
|
152
166
|
export function runSetup({ args = [], env = process.env, platform = process.platform, root = ROOT, run = execute, resolve = resolveCodexBin } = {}) {
|
|
153
167
|
const options = setupArguments(args);
|
|
154
|
-
if (options.help) return { help: "Usage: codex-bridge-setup [--check|--dry-run] [--json] [--no-download]\nGlobal npm installation configures Claude Desktop, Claude Code, Codex and Antigravity automatically. CODEX_BRIDGE_SKIP_SETUP=1 opts out." };
|
|
168
|
+
if (options.help) return { help: "Usage: codex-bridge-setup [--check|--dry-run] [--json] [--no-download]\nUpdate the package and apply managed settings with: npm install -g @minhspark/codex-mcp-bridge@latest --allow-scripts=@minhspark/codex-mcp-bridge\nGlobal npm installation configures Claude Desktop, Claude Code, Codex and Antigravity automatically. CODEX_BRIDGE_SKIP_SETUP=1 opts out. Saved configuration and loaded app connections are verified separately." };
|
|
155
169
|
if (options.postinstall && !automaticSetupEnabled(env)) return { skipped: true, reason: "Automatic configuration runs only for a global npm install; use codex-bridge-setup for manual setup." };
|
|
156
170
|
if (options.check) return checkSetup({ env, platform, root, run, resolve });
|
|
157
171
|
const native = platform === "win32" || platform === "darwin";
|
|
@@ -166,16 +180,18 @@ export function runSetup({ args = [], env = process.env, platform = process.plat
|
|
|
166
180
|
const install = (name, script, arguments_, environment = base) => {
|
|
167
181
|
try {
|
|
168
182
|
const output = run(process.execPath, [path.join(root, "scripts", script), ...arguments_], environment, 180000);
|
|
169
|
-
let
|
|
170
|
-
try {
|
|
171
|
-
|
|
183
|
+
let details = {};
|
|
184
|
+
try { details = JSON.parse(output); } catch {}
|
|
185
|
+
const notices = details.notices ?? [];
|
|
186
|
+
components.push({ name, status: "configured", ...(notices.length ? { notices } : {}),
|
|
187
|
+
...(details.backgroundPreference ? { backgroundPreference: details.backgroundPreference } : {}) });
|
|
172
188
|
return true;
|
|
173
189
|
} catch (error) { components.push({ name, status: "incomplete", detail: error.message }); return false; }
|
|
174
190
|
};
|
|
175
|
-
const code = install(planned[0], "install-claude-code.mjs", ["--install", "--desktop-config", paths.desktop], {
|
|
191
|
+
const code = install(planned[0], "install-claude-code.mjs", ["--install", "--setup", "--desktop-config", paths.desktop], {
|
|
176
192
|
...base, CODEX_BRIDGE_DESKTOP_TASKS: native ? "1" : "0", CODEX_BRIDGE_AUTOSTART: native ? "0" : "1", CODEX_BRIDGE_THREAD_POLICY: "roots",
|
|
177
193
|
});
|
|
178
|
-
if (code) install(planned[1], "install-claude-desktop.mjs", ["--entry-name", "codex-bridge-desktop", "--quiet"]);
|
|
194
|
+
if (code) install(planned[1], "install-claude-desktop.mjs", ["--entry-name", "codex-bridge-desktop", "--quiet", "--setup"]);
|
|
179
195
|
if (native) {
|
|
180
196
|
const relay = readJson(path.join(paths.codex, "native-relay.json"));
|
|
181
197
|
install("Codex / codex-native-relay and executor", "install-native-relay.mjs", ["--setup", ...(relay?.desktopTasks === undefined ? ["--desktop-tasks"] : [])]);
|
|
@@ -185,20 +201,24 @@ export function runSetup({ args = [], env = process.env, platform = process.plat
|
|
|
185
201
|
const inspection = checkSetup({ env: base, platform, root, run, resolve: () => runtime.binary });
|
|
186
202
|
return { configured: components.every((item) => item.status === "configured") && inspection.configured, liveVerified: false,
|
|
187
203
|
cliInstalled: runtime.installed, components, verification: inspection.components,
|
|
188
|
-
nextActions: ["
|
|
189
|
-
"
|
|
190
|
-
"
|
|
204
|
+
nextActions: ["Running MCP processes may still hold earlier settings. Setup does not start apps or open conversations.",
|
|
205
|
+
"Existing compatible supervisors reload installed source when idle; configuration changes are not proof of loaded-process readiness.",
|
|
206
|
+
"Native delivery uses the same saved project and signed-in accounts in Claude Desktop Code and Codex Desktop.",
|
|
207
|
+
"codex_bridge_status, native_relay_status and agent_bridge_status report live readiness separately from this configuration check."] };
|
|
191
208
|
}
|
|
192
209
|
|
|
193
210
|
export function printSetup(result, { json = false, output = console.log } = {}) {
|
|
194
211
|
if (json) { output(JSON.stringify(result, null, 2)); return; }
|
|
195
212
|
if (result.help) { output(result.help); return; }
|
|
196
213
|
if (result.skipped) { output(result.reason); return; }
|
|
197
|
-
output(result.dryRun ? "Bridge setup preview (no changes)" : result.configured ? "Bridge configuration verified.
|
|
214
|
+
output(result.dryRun ? "Bridge setup preview (no changes)" : result.configured ? "Bridge configuration verified. Running app connections have not been verified." : "Bridge setup is incomplete. Fix the items below, then run codex-bridge-setup again.");
|
|
198
215
|
for (const item of result.components ?? []) {
|
|
199
216
|
output(`[${item.status}] ${item.name}${item.detail ? `: ${item.detail}` : ""}`);
|
|
200
217
|
for (const notice of item.notices ?? []) output(` ${notice}`);
|
|
201
218
|
}
|
|
202
|
-
for (const item of result.verification ?? [])
|
|
219
|
+
for (const item of result.verification ?? []) {
|
|
220
|
+
if (item.status !== "configured") output(`[${item.status}] ${item.name}: ${item.detail}`);
|
|
221
|
+
for (const notice of item.notices ?? []) output(`${item.name}: ${notice}`);
|
|
222
|
+
}
|
|
203
223
|
for (const action of result.nextActions ?? []) output(action);
|
|
204
224
|
}
|
package/src/thread-delivery.mjs
CHANGED
|
@@ -9,6 +9,7 @@ import { captureCodexRolloutWatermark, inspectCodexNativeTurn, readCodexNativeTu
|
|
|
9
9
|
import { captureProjectScope, recheckProjectScope } from "./project-scope.mjs";
|
|
10
10
|
import { isRolloutChangedDuringRead } from "./rollout-segments.mjs";
|
|
11
11
|
import { requireClaudeSenderContext } from "./claude-sender-context.mjs";
|
|
12
|
+
import { bridgeDiagnostic } from "./recipient-preflight.mjs";
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Which backend puts a message into a Codex thread.
|
|
@@ -74,7 +75,7 @@ export function matchDesktopProject(projects, cwd, { canonicalize = realpathSync
|
|
|
74
75
|
return matches[0];
|
|
75
76
|
}
|
|
76
77
|
|
|
77
|
-
export const RELAY_DOWN_GUIDANCE = "The Codex Desktop native relay is not listening after waiting for silent recovery. Its Desktop-owned supervisor restarts crashed relay workers without replaying requests.
|
|
78
|
+
export const RELAY_DOWN_GUIDANCE = "The Codex Desktop native relay is not listening after waiting for silent recovery. Its Desktop-owned supervisor restarts crashed relay workers without replaying requests. An unloaded native host cannot be cold-started by this bridge. Inspect codex_bridge_status and include its diagnostic in a report. Never open an app or an old, new, empty or failed chat to load a relay.";
|
|
78
79
|
|
|
79
80
|
function relayUnreachable(error) {
|
|
80
81
|
return error?.code === "RELAY_UNREACHABLE" && error.sent === false;
|
|
@@ -196,13 +197,17 @@ export class DesktopTaskDelivery {
|
|
|
196
197
|
async status() {
|
|
197
198
|
const accountContext = this.accountContext?.();
|
|
198
199
|
const socketPath = this.relay.status?.({ accountContext })?.socketPath ?? this.relay.socketPath;
|
|
199
|
-
if (this.accountContext && !accountContext)
|
|
200
|
+
if (this.accountContext && !accountContext) {
|
|
201
|
+
const reason = "The current Claude and Codex accounts could not both be verified. Finish signing in and inspect the account diagnostics before sending work.";
|
|
202
|
+
return { available: false, socketPath, code: "BRIDGE_ACCOUNT_UNVERIFIED", reason, diagnostic: bridgeDiagnostic(new Error(reason), { component: "codex-bridge", operation: "codex_bridge_status", code: "BRIDGE_ACCOUNT_UNVERIFIED", readOnly: true }) };
|
|
203
|
+
}
|
|
200
204
|
try {
|
|
201
205
|
const response = await this.request("list_projects", {});
|
|
202
206
|
if (!Array.isArray(response?.projects)) throw new Error("Desktop returned no project list");
|
|
203
207
|
return { available: true, socketPath, localProjects: response.projects.filter((project) => project.projectKind === "local" && project.hostId === "local").length };
|
|
204
208
|
} catch (err) {
|
|
205
|
-
|
|
209
|
+
const diagnostic = bridgeDiagnostic(err, { component: "codex-bridge", operation: "codex_bridge_status", readOnly: true });
|
|
210
|
+
return { available: false, socketPath, code: diagnostic.code, reason: err.message, diagnostic };
|
|
206
211
|
}
|
|
207
212
|
}
|
|
208
213
|
|
|
@@ -336,11 +341,11 @@ export class DesktopTaskDelivery {
|
|
|
336
341
|
const confirmedId = response?.threadId ?? response?.conversationId;
|
|
337
342
|
threadId = confirmedId;
|
|
338
343
|
if (!threadId && (response?.status === "outcome-unknown" || response?.firstTurn?.status === "outcome-unknown")) threadId = response?.clientThreadId;
|
|
339
|
-
if (typeof confirmedId !== "string" || !confirmedId.trim() || response?.status === "outcome-unknown" || response.hostId !== "local") throw new Error(
|
|
344
|
+
if (typeof confirmedId !== "string" || !confirmedId.trim() || response?.status === "outcome-unknown" || response.hostId !== "local") throw Object.assign(new Error("Desktop creation is not confirmed; inspect the original native receipt."), { code: "CODEX_CREATION_UNCONFIRMED", deliveryState: "unknown", phase: "creation" });
|
|
340
345
|
await this.receipts.write(identity.key, { ...receipt, state: "known", threadId });
|
|
341
346
|
creationConfirmed = true;
|
|
342
347
|
this.security.registerThread(threadId);
|
|
343
|
-
if (response.firstTurn && response.firstTurn.status !== "accepted") throw new Error(`Desktop created task ${threadId}; its first turn reports ${response.firstTurn.status
|
|
348
|
+
if (response.firstTurn && response.firstTurn.status !== "accepted") throw Object.assign(new Error(`Desktop created task ${threadId}; its first turn reports ${["failed", "waitingOnApproval", "waitingOnUserInput", "outcome-unknown"].includes(response.firstTurn.status) ? response.firstTurn.status : "an unconfirmed state"}. Inspect the original native receipt.`), { code: "CODEX_FIRST_TURN_NOT_ACCEPTED", deliveryState: "sent", phase: "first_turn" });
|
|
344
349
|
const created = { threadId, name, cwd, projectId: project.projectId, projectName: project.label, backend: NATIVE_BACKEND };
|
|
345
350
|
const delivered = { ...created, previousTurnId: null, deliveryStatus: "accepted", responseObservation: {
|
|
346
351
|
operation: "create_thread", threadId, previousTurnId: null, expectedCwd: cwd,
|