@minhspark/codex-mcp-bridge 1.22.8 → 1.22.9
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 +17 -0
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/scripts/install-claude-desktop.mjs +9 -9
- package/scripts/install-native-relay.mjs +1 -1
- package/src/claude-bridge.mjs +1 -1
- package/src/index.mjs +1 -1
- package/src/native-relay-companion.mjs +1 -1
- package/src/peer-protocol.mjs +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,23 @@ Follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and [SemVer](ht
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [1.22.9] - 2026-10-20
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- Read the Claude Desktop configuration with exact bigint stats in `codex-mcp-bridge-install`, so a configuration file replaced during the installer's read is detected even when the two inodes differ only beyond Number precision. v1.22.8 converted the runtime stores but missed this installer.
|
|
12
|
+
- Notice a new reply in a Claude Code transcript when the file changes within the same millisecond and keeps its size. The reply poll now compares the file's device, inode, nanosecond modification and change times, and size instead of only the millisecond modification time and size.
|
|
13
|
+
|
|
14
|
+
### Verification
|
|
15
|
+
|
|
16
|
+
- 2 new regression tests: a Desktop configuration whose inode changes only below Number precision (`2^60` and `2^60 + 1`) is rejected and left untouched, and a transcript reply is observed while its Number `mtimeMs` and size stay frozen. Both fail on v1.22.8.
|
|
17
|
+
- Two independent sweeps of `src/`, `scripts/` and `bin/`, one by stat API and one by stored or compared stat data, found no other Number-based identity or change detection.
|
|
18
|
+
- CI passed on macOS, Ubuntu and Windows with Node 22 and 24. CodeQL re-reported 3 `js/path-injection` flows on the edited stat lines; each source is operator environment configuration, matching earlier dismissals, and they were dismissed as false positives.
|
|
19
|
+
|
|
20
|
+
### Limitations
|
|
21
|
+
|
|
22
|
+
- The 64-bit NTFS case runs for real only on Windows CI; local tests simulate it with mocked stats.
|
|
23
|
+
|
|
7
24
|
## [1.22.8] - 2026-10-20
|
|
8
25
|
|
|
9
26
|
### Fixed
|
package/package.json
CHANGED
package/plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
|
|
3
3
|
"name": "codex-mcp-bridge",
|
|
4
|
-
"version": "1.22.
|
|
4
|
+
"version": "1.22.9",
|
|
5
5
|
"description": "Connect ChatGPT to your own Codex Desktop projects and conversations.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Bui Dang Minh",
|
|
@@ -45,23 +45,23 @@ fs.mkdirSync(path.dirname(cfgPath), { recursive: true });
|
|
|
45
45
|
function readConfig() {
|
|
46
46
|
let descriptor;
|
|
47
47
|
try {
|
|
48
|
-
const before = fs.lstatSync(cfgPath);
|
|
49
|
-
if (!before.isFile() || before.isSymbolicLink() || before.nlink !==
|
|
48
|
+
const before = fs.lstatSync(cfgPath, { bigint: true });
|
|
49
|
+
if (!before.isFile() || before.isSymbolicLink() || before.nlink !== 1n || before.size > 4n * 1024n * 1024n) throw new Error("Unsafe Desktop configuration file");
|
|
50
50
|
descriptor = fs.openSync(cfgPath, fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW ?? 0));
|
|
51
|
-
const opened = fs.fstatSync(descriptor);
|
|
52
|
-
const fields = ["dev", "ino", "size", "
|
|
51
|
+
const opened = fs.fstatSync(descriptor, { bigint: true });
|
|
52
|
+
const fields = ["dev", "ino", "size", "mtimeNs", "ctimeNs"];
|
|
53
53
|
const matches = (left, right) => fields.every(field => left[field] === right[field]);
|
|
54
|
-
if (!opened.isFile() || opened.nlink !==
|
|
55
|
-
const data = Buffer.alloc(opened.size);
|
|
54
|
+
if (!opened.isFile() || opened.nlink !== 1n || !matches(before, opened)) throw new Error("Desktop configuration changed during access");
|
|
55
|
+
const data = Buffer.alloc(Number(opened.size));
|
|
56
56
|
let offset = 0;
|
|
57
57
|
while (offset < data.length) {
|
|
58
58
|
const count = fs.readSync(descriptor, data, offset, data.length - offset, offset);
|
|
59
59
|
if (!count) throw new Error("Desktop configuration changed during access");
|
|
60
60
|
offset += count;
|
|
61
61
|
}
|
|
62
|
-
const after = fs.fstatSync(descriptor);
|
|
63
|
-
const current = fs.lstatSync(cfgPath);
|
|
64
|
-
if (!current.isFile() || current.isSymbolicLink() || current.nlink !==
|
|
62
|
+
const after = fs.fstatSync(descriptor, { bigint: true });
|
|
63
|
+
const current = fs.lstatSync(cfgPath, { bigint: true });
|
|
64
|
+
if (!current.isFile() || current.isSymbolicLink() || current.nlink !== 1n || !matches(opened, after) || !matches(after, current)) throw new Error("Desktop configuration changed during access");
|
|
65
65
|
return { data, version: fields.map(field => current[field]) };
|
|
66
66
|
} catch (error) {
|
|
67
67
|
if (error.code === "ENOENT" && descriptor === undefined) return null;
|
|
@@ -24,7 +24,7 @@ import { stableInstallPath } from "../src/install-paths.mjs";
|
|
|
24
24
|
|
|
25
25
|
exitForVersionRequest(import.meta.url);
|
|
26
26
|
|
|
27
|
-
const VERSION = "1.22.
|
|
27
|
+
const VERSION = "1.22.9";
|
|
28
28
|
const root = stableInstallPath(path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."));
|
|
29
29
|
const entry = path.join(root, "src", "mcp-supervisor.mjs");
|
|
30
30
|
const entryArgs = ["native-relay-companion.mjs"];
|
package/src/claude-bridge.mjs
CHANGED
|
@@ -30,7 +30,7 @@ import { discoverClaudeProject } from "./claude-project-discovery.mjs";
|
|
|
30
30
|
|
|
31
31
|
exitForVersionRequest(import.meta.url);
|
|
32
32
|
|
|
33
|
-
const VERSION = "1.22.
|
|
33
|
+
const VERSION = "1.22.9";
|
|
34
34
|
void import("./telemetry.mjs").then(({ startUsageReporting }) => startUsageReporting({ version: VERSION })).catch(() => {});
|
|
35
35
|
const FORWARD_MIN_INTERVAL_MS = 5000;
|
|
36
36
|
const FORWARD_MAX_PER_SESSION = 50;
|
package/src/index.mjs
CHANGED
|
@@ -40,7 +40,7 @@ import { PROJECT_SETUP_TOOLS, registerProjectOnboardingTools } from "./project-o
|
|
|
40
40
|
|
|
41
41
|
exitForVersionRequest(import.meta.url);
|
|
42
42
|
|
|
43
|
-
const VERSION = "1.22.
|
|
43
|
+
const VERSION = "1.22.9";
|
|
44
44
|
void import("./telemetry.mjs").then(({ startUsageReporting }) => startUsageReporting({ version: VERSION })).catch(() => {});
|
|
45
45
|
const log = (msg) => process.stderr.write(`[codex-mcp-bridge] ${msg}\n`);
|
|
46
46
|
|
|
@@ -38,7 +38,7 @@ import { NativeCreationReceipts, creationIdValid } from "./native-creation-recei
|
|
|
38
38
|
|
|
39
39
|
exitForVersionRequest(import.meta.url);
|
|
40
40
|
|
|
41
|
-
const VERSION = "1.22.
|
|
41
|
+
const VERSION = "1.22.9";
|
|
42
42
|
const log = (msg) => process.stderr.write(`[native-relay] ${msg}\n`);
|
|
43
43
|
|
|
44
44
|
function errorResponse(code, message, sent, details) {
|
package/src/peer-protocol.mjs
CHANGED
|
@@ -860,7 +860,7 @@ export class PeerEndpoint {
|
|
|
860
860
|
const { sessionId, cwd } = pending.transcriptSession;
|
|
861
861
|
const file = findTranscriptFile(sessionId, cwd);
|
|
862
862
|
let stamp;
|
|
863
|
-
try { const stat = fs.statSync(file); stamp = `${stat.
|
|
863
|
+
try { const stat = fs.statSync(file, { bigint: true }); stamp = `${stat.dev}:${stat.ino}:${stat.mtimeNs}:${stat.ctimeNs}:${stat.size}`; }
|
|
864
864
|
catch { continue; }
|
|
865
865
|
if (stamp === pending.transcriptStamp) continue;
|
|
866
866
|
pending.transcriptStamp = stamp;
|