@minhspark/codex-mcp-bridge 1.20.1 → 1.21.0
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 +26 -0
- package/README.md +57 -15
- package/assets/README.md +30 -0
- package/assets/agent-pets.svg +1 -0
- package/docs/daily-handoffs.md +106 -0
- package/docs/desktop-handoff-design.md +180 -0
- package/docs/new-machine-setup.md +112 -0
- package/docs/project-onboarding.md +122 -0
- package/package.json +8 -2
- package/plugin.json +1 -1
- package/scripts/bridge-projects.mjs +44 -0
- package/scripts/check.mjs +2 -1
- package/scripts/collect-repo-analytics.mjs +5 -2
- package/scripts/configure-message-automation.mjs +18 -0
- package/scripts/install-claude-code.mjs +64 -0
- package/scripts/install-claude-desktop.mjs +48 -5
- package/scripts/install-native-relay.mjs +6 -1
- package/scripts/install-npm-footer.mjs +13 -1
- package/scripts/publish-repo-analytics.mjs +24 -1
- package/src/app-server-client.mjs +5 -2
- package/src/bridge-readiness.mjs +23 -0
- package/src/card-settings-sync.mjs +125 -0
- package/src/claude-bridge.mjs +54 -20
- package/src/claude-code-registration.mjs +54 -0
- package/src/claude-sender-context.mjs +1 -1
- package/src/claude-session-creation.mjs +10 -2
- package/src/codex-mcp-registration.mjs +1 -1
- package/src/codex-native-response.mjs +139 -107
- package/src/codex-sender-context.mjs +62 -61
- package/src/desktop-reply-receipts.mjs +87 -0
- package/src/desktop-task-receipts.mjs +30 -7
- package/src/hardened-root-policy.mjs +5 -0
- package/src/index.mjs +97 -19
- package/src/mcp-supervisor.mjs +6 -2
- package/src/message-automation.mjs +89 -0
- package/src/native-creation-receipts.mjs +155 -0
- package/src/native-relay-companion.mjs +37 -6
- package/src/native-relay.mjs +12 -3
- package/src/peer-protocol.mjs +192 -18
- package/src/project-manager-registration.mjs +34 -0
- package/src/project-onboarding-tools.mjs +30 -0
- package/src/project-onboarding.mjs +178 -0
- package/src/project-policy.mjs +221 -0
- package/src/project-scope.mjs +33 -5
- package/src/prompt-guidance.mjs +2 -0
- package/src/release-snapshot.mjs +30 -5
- package/src/rollout-reader.mjs +58 -0
- package/src/rollout-segments.mjs +65 -0
- package/src/security-policy.mjs +4 -2
- package/src/sites-connector.mjs +17 -2
- package/src/telemetry.mjs +10 -8
- package/src/thread-delivery.mjs +134 -35
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# Desktop handoff recovery and project onboarding
|
|
2
|
+
|
|
3
|
+
This follow-up is based on upstream `00730b6` (1.19.6). PR #84 is already
|
|
4
|
+
merged and is not being resubmitted. Its historical validation record remains
|
|
5
|
+
dated; the maintainer's managed-permission implementation takes precedence.
|
|
6
|
+
|
|
7
|
+
## Problems addressed
|
|
8
|
+
|
|
9
|
+
| Trigger | Result before this change | Recovery/behavior in this branch |
|
|
10
|
+
| --- | --- | --- |
|
|
11
|
+
| A long Desktop conversation exceeds a whole-file read limit or rotates into continuation files | Sender verification or final-response collection becomes unavailable | Bounded per-record streaming, exact-ID segment discovery and active-turn/native identity checks |
|
|
12
|
+
| A native Desktop send is accepted but its first wait expires | The caller cannot continue the same reply observation after reconnecting | An owner/account-bound `deliveryId` and `wait_codex_reply`; no second send |
|
|
13
|
+
| A confirmed creation uses up the first-call budget, or a follow-up acknowledgement arrives late | The reply exists but no continuation ID reaches Claude | Preserve the original creation or pre-send binding, distinguish unconfirmed delivery, and continue observing without another send |
|
|
14
|
+
| Claude finishes a background command after an interim answer | The interim answer may be mistaken for the final report | Correlate the originating tool, background task and native completion notification |
|
|
15
|
+
| A new conversation command interrupts the reply chain | An old pending observation blocks later handoffs indefinitely | Return `interrupted`, `pending=false`, with task outcome explicitly unknown; inspect existing work before a follow-up |
|
|
16
|
+
| Read(image) adds a native `turnCompanion` record | A user-role image-size annotation breaks reply ancestry even though Claude already replied | Follow only companions with matching session, prompt and Read/image-result ancestry; preserve real user boundaries |
|
|
17
|
+
| Native `create_thread` creates a dedicated dispatcher | `agent_created_thread` is rejected as though it were a transient subagent | Require independent, account-bound Desktop ownership and exact active-turn proof, then re-read that task's own permissions |
|
|
18
|
+
| New projects need multiple copied connection settings | Generic Desktop processes cannot attest their actual Code caller, and grants drift between entries | Explicit user-level Code registration, a shared live project policy and diagnostic onboarding tools |
|
|
19
|
+
|
|
20
|
+
## Integration with current upstream
|
|
21
|
+
|
|
22
|
+
The existing upstream permission classifier, managed-policy diagnostics and
|
|
23
|
+
opt-in Full access repair implementation are retained. This contribution does
|
|
24
|
+
not enable Full access repair or change a user's installed permission mode.
|
|
25
|
+
|
|
26
|
+
Two modules serve different purposes:
|
|
27
|
+
|
|
28
|
+
- Upstream `project-scope.mjs` is unchanged: it binds the verified Claude sender
|
|
29
|
+
and Codex destination to the same canonical directory or registered Git
|
|
30
|
+
repository, and detects replacement before a native mutation.
|
|
31
|
+
- New `project-policy.mjs` stores explicit project/parent grants and denials.
|
|
32
|
+
It is optional; when configured, both that policy and the upstream binding
|
|
33
|
+
must pass. A grant for two unrelated repositories does not permit a
|
|
34
|
+
cross-project Claude-to-Codex handoff.
|
|
35
|
+
|
|
36
|
+
The authorization module was renamed during integration because both branches
|
|
37
|
+
had independently introduced `project-scope.mjs`. Both sets of tests are kept.
|
|
38
|
+
Native creation, sends and renames retain the upstream before-write project
|
|
39
|
+
checks and additionally recheck a captured live policy grant. Diagnostic
|
|
40
|
+
readiness also reports a sender/destination mismatch even when both paths are
|
|
41
|
+
authorized. MCP tool approval is still evaluated separately by the host.
|
|
42
|
+
|
|
43
|
+
## Deliberate boundaries
|
|
44
|
+
|
|
45
|
+
Identity comes from host metadata, local records and native account-bound
|
|
46
|
+
inspection, not from claims in a message. `agent_created_thread` support is
|
|
47
|
+
limited to the measured Desktop origin; unknown sources and transient
|
|
48
|
+
subagents remain unsupported. The permission class is always the sending
|
|
49
|
+
task's current class, never a parent's class.
|
|
50
|
+
|
|
51
|
+
An interrupted wait is not a cancellation or a successful task result. Timeout,
|
|
52
|
+
unreadable records and unknown transcript formats do not silently discard
|
|
53
|
+
pending ownership. Image companions are recognized through bounded ancestry,
|
|
54
|
+
not by matching an image-size sentence. Forged companions, unrelated tools,
|
|
55
|
+
different sessions/prompts, failed image results and real human interruptions
|
|
56
|
+
have negative regression cases.
|
|
57
|
+
|
|
58
|
+
Project setup is explicit and backed up. The messaging-automation installer is
|
|
59
|
+
preview-only unless the user opts in, permits only the exact send/wait tools,
|
|
60
|
+
and refuses conflicting ask/deny rules. The optional card-settings adapter
|
|
61
|
+
does not require the card extension for communication or merge card and
|
|
62
|
+
communication permissions. The separate card extension binary is not shipped
|
|
63
|
+
by this PR.
|
|
64
|
+
|
|
65
|
+
## Validation and limits
|
|
66
|
+
|
|
67
|
+
Run the full isolated regression suite with:
|
|
68
|
+
|
|
69
|
+
```sh
|
|
70
|
+
node --test --test-concurrency=2
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
The merge retains upstream mismatch, moved-target, replaced-directory and
|
|
74
|
+
managed-policy negative tests. Additional integration cases combine live
|
|
75
|
+
revocation with same-project enforcement and confirm that authorized unrelated
|
|
76
|
+
repositories fail readiness while registered worktrees pass.
|
|
77
|
+
|
|
78
|
+
Earlier installed Windows builds completed two-way Desktop handoffs and
|
|
79
|
+
accepted-timeout continuation without a resend. The latest image-companion fix
|
|
80
|
+
passed 62 peer tests and recovered the exact historical reply in an offline
|
|
81
|
+
replay of the affected transcript, including pending-release and reload checks.
|
|
82
|
+
Raw transcripts, identifiers, hashes and machine paths are not published.
|
|
83
|
+
|
|
84
|
+
That offline replay is not a claim that the old live worker recovered. A worker
|
|
85
|
+
already stuck on an unrecognized pending record can defer hot reload until it
|
|
86
|
+
is reconnected. Preserve the original receipt and inspect the existing task
|
|
87
|
+
first; an unknown ID after reconnecting never proves non-delivery. The merged
|
|
88
|
+
1.19.6 branch has not yet replaced the installed runtime or completed a new
|
|
89
|
+
live Desktop round trip. No second-machine or cross-platform live acceptance
|
|
90
|
+
is claimed; upstream CI should validate the supported platforms.
|
|
91
|
+
|
|
92
|
+
### Merged-branch Windows check (2026-10-02)
|
|
93
|
+
|
|
94
|
+
Node 24.13.0, `node --test --test-concurrency=2`: **1,000 tests,
|
|
95
|
+
986 passed, 2 failed, 12 skipped**, approximately 903 seconds. The Windows
|
|
96
|
+
pipe ACL checks ran successfully with `RemoteSigned` scoped only to the test
|
|
97
|
+
process; machine/account policy and assertions were unchanged. All messaging,
|
|
98
|
+
sender, project-binding and revocation checks passed.
|
|
99
|
+
|
|
100
|
+
Both failures were existing installer cases reaching their unchanged 60-second
|
|
101
|
+
deadline: explicit Full access setup and cross-machine defaults. A focused
|
|
102
|
+
repeat of the installer, messaging-automation and repository-hygiene suites
|
|
103
|
+
gave **28 passed, the same 2 timeouts**, after the final documentation/package
|
|
104
|
+
and language-guidance edits. This is not a clean full-suite pass.
|
|
105
|
+
|
|
106
|
+
A control run on untouched upstream `00730b6`, with freshly installed locked
|
|
107
|
+
dependencies and only those two test names selected, reproduced **both**
|
|
108
|
+
60-second timeouts (~125 seconds total). Thus the failures reproduce without
|
|
109
|
+
this contribution on this machine; their underlying cause remains undiagnosed.
|
|
110
|
+
No test deadline was increased and no failing case was skipped.
|
|
111
|
+
|
|
112
|
+
`npm pack --dry-run --json --ignore-scripts` includes the English guides and
|
|
113
|
+
excludes local acceptance artifacts. `git diff --check` passes. The standalone
|
|
114
|
+
Desktop health check discovers tools and the existing native relay, then
|
|
115
|
+
returns `SENDER_UNVERIFIED` as expected for a terminal process with no Claude
|
|
116
|
+
Code ancestry. It does not substitute a sender or start an external app-server.
|
|
117
|
+
|
|
118
|
+
### Path-alias CI fixes (2026-10-03)
|
|
119
|
+
|
|
120
|
+
The first CI run exposed path aliases on macOS (`/var` versus `/private/var`)
|
|
121
|
+
and Windows (short versus long temporary-directory names). The card-settings
|
|
122
|
+
adapter compared page paths literally against canonical policy paths, so
|
|
123
|
+
removing a displayed project or parent could leave its grant in place. It now
|
|
124
|
+
canonicalizes page paths, including missing projects via their nearest existing
|
|
125
|
+
ancestor, before reconciliation. Exclusions and concurrent-change checks remain
|
|
126
|
+
in force. Onboarding also canonicalizes the configured home before rejecting
|
|
127
|
+
whole-home trust requests.
|
|
128
|
+
|
|
129
|
+
Continuation/sender tests now expect the canonical paths returned by rollout
|
|
130
|
+
discovery. They still check exact file and turn identity; no production sender
|
|
131
|
+
check was relaxed. New directory-alias regressions exercise project revocation,
|
|
132
|
+
parent removal, deleted-project exclusions and home-root rejection on Windows
|
|
133
|
+
junctions and POSIX symlinks. The focused Windows run passed 102 checks with one
|
|
134
|
+
existing platform skip. Fresh installation and bidirectional live acceptance
|
|
135
|
+
remain separate from these isolated tests.
|
|
136
|
+
|
|
137
|
+
The full Windows run at `989ed6c` completed with **989 passed, 2 failed and
|
|
138
|
+
12 skipped** (1,003 tests). Both failures remain the same installer cases
|
|
139
|
+
reaching the unchanged 60-second deadline described above. The standalone
|
|
140
|
+
health check listed tools but could not reach the default native relay and
|
|
141
|
+
reported an unverified sender from the terminal; it did not send a message,
|
|
142
|
+
change permissions or launch an external app-server.
|
|
143
|
+
|
|
144
|
+
Upstream subsequently advanced to `e0f40b5` (1.20.1), making the PR conflicted
|
|
145
|
+
and preventing a fresh pull-request CI run. The merge preserves upstream's
|
|
146
|
+
composer submission, dormant-task recovery, Sites connector and dependency
|
|
147
|
+
updates. Creation and recovery callbacks still await this branch's asynchronous
|
|
148
|
+
sender verification before any UI action. A regression checks rejection both
|
|
149
|
+
before opening and between opening and automatic trust/submission. No installed
|
|
150
|
+
runtime or client configuration was replaced for this merge.
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
### Native creation acknowledgement continuation
|
|
154
|
+
|
|
155
|
+
Local Desktop creation probes the companion's `get_creation_receipt` capability
|
|
156
|
+
before mutation. An older companion refuses the probe before a task is created;
|
|
157
|
+
reload both installed workers after upgrading. Hosted callers retain their
|
|
158
|
+
existing creation contract and receive no local observation binding.
|
|
159
|
+
|
|
160
|
+
The caller persists an owner/account-bound delivery ID before dispatch. The
|
|
161
|
+
companion reserves a separate unpredictable native receipt ID with the exact
|
|
162
|
+
creation arguments, their canonical hash, accounts and executor. It keeps the
|
|
163
|
+
result after the caller socket times out. `wait_codex_reply` queries that receipt
|
|
164
|
+
without another creation and verifies current accounts, project scope, the native
|
|
165
|
+
task directory and the exact initial creation response before releasing text.
|
|
166
|
+
The companion authenticates every persisted receipt field with HMAC-SHA256.
|
|
167
|
+
Its 32-byte signing key lives in the separate `bridge-native-creation-authority`
|
|
168
|
+
directory beside the receipt store, with owner-only permissions or a protected
|
|
169
|
+
Windows ACL. Altered, unsigned legacy receipts and missing, replaced or unsafe
|
|
170
|
+
keys fail closed before any ownership is restored. The key is never returned
|
|
171
|
+
to callers. Existing live account, executor and project checks still apply.
|
|
172
|
+
|
|
173
|
+
Duplicate native receipt IDs never dispatch again. A prompt edited on retry
|
|
174
|
+
cannot replace the original observation binding.
|
|
175
|
+
|
|
176
|
+
This is recovery of a late acknowledgement, not reconstruction by title or by
|
|
177
|
+
scanning unrelated conversations. If the native operation itself never confirms,
|
|
178
|
+
or the companion crashes before persisting its result, the outcome remains
|
|
179
|
+
unknown and automatic re-creation remains blocked. Receipts made by earlier
|
|
180
|
+
versions without this binding cannot be retroactively recovered by guessing.
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# New-machine setup and initial permissions
|
|
2
|
+
|
|
3
|
+
This is a Windows local Desktop source-install guide, not evidence of testing
|
|
4
|
+
on a second machine. Use a pinned source revision containing these scripts;
|
|
5
|
+
published npm versions may not yet include this branch. The optional card
|
|
6
|
+
extension is distributed separately. Keep the source in a stable directory.
|
|
7
|
+
|
|
8
|
+
Install/sign in to Codex Desktop and Claude Desktop; install Node 22+ and Git,
|
|
9
|
+
and make the Codex CLI available. Record the source revision and optional MCPB
|
|
10
|
+
version/hash. Do not copy login caches, old session identities, unfinished
|
|
11
|
+
receipts or directory-identity grants from another machine.
|
|
12
|
+
|
|
13
|
+
## Three messaging entries
|
|
14
|
+
|
|
15
|
+
| Client | Entry | Purpose |
|
|
16
|
+
| --- | --- | --- |
|
|
17
|
+
| Codex | `claude-bridge` | Send to Claude and observe replies |
|
|
18
|
+
| Codex | `codex-native-relay` | Deliver Claude requests through the Desktop host |
|
|
19
|
+
| Claude Code, user scope | `codex-bridge` | Send to Codex and continue reply observation |
|
|
20
|
+
|
|
21
|
+
Begin with one dedicated test directory and explicitly grant it:
|
|
22
|
+
|
|
23
|
+
```powershell
|
|
24
|
+
node scripts/bridge-projects.mjs allow-project 'C:\Projects\BridgeTest'
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Merge a user-level Claude Code entry, replacing placeholder paths with actual
|
|
28
|
+
absolute paths and preserving unrelated settings:
|
|
29
|
+
|
|
30
|
+
```json
|
|
31
|
+
{
|
|
32
|
+
"mcpServers": {
|
|
33
|
+
"codex-bridge": {
|
|
34
|
+
"command": "C:/Tools/nodejs/node.exe",
|
|
35
|
+
"args": ["C:/Tools/GptClaudeBridge/src/mcp-supervisor.mjs", "index.mjs"],
|
|
36
|
+
"env": {
|
|
37
|
+
"CODEX_BIN": "C:/Tools/Codex/codex.exe",
|
|
38
|
+
"CODEX_BRIDGE_DESKTOP_TASKS": "1",
|
|
39
|
+
"CODEX_BRIDGE_AUTOSTART": "0",
|
|
40
|
+
"CODEX_BRIDGE_PROJECT_POLICY": "C:/Users/<user>/.config/GptClaudeBridge/projects.json",
|
|
41
|
+
"CODEX_BRIDGE_ALLOWED_ROOTS": "C:/Projects/BridgeTest",
|
|
42
|
+
"CODEX_BRIDGE_THREAD_POLICY": "roots",
|
|
43
|
+
"CODEX_BRIDGE_APPROVAL_POLICY": "on-request",
|
|
44
|
+
"CODEX_BRIDGE_SANDBOX": "workspace-write"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Then run `node scripts/install-claude-code.mjs --check`. That helper migrates or
|
|
52
|
+
checks an existing supervised entry; it does not create a first entry from
|
|
53
|
+
nothing. See [project onboarding](project-onboarding.md) for migration.
|
|
54
|
+
|
|
55
|
+
Configure the same policy for Codex using the repository installers:
|
|
56
|
+
|
|
57
|
+
```powershell
|
|
58
|
+
$env:CODEX_BRIDGE_PROJECT_POLICY = Join-Path $env:USERPROFILE '.config\GptClaudeBridge\projects.json'
|
|
59
|
+
$env:CODEX_BRIDGE_DESKTOP_TASKS = '1'
|
|
60
|
+
node scripts/install-native-relay.mjs
|
|
61
|
+
node scripts/install-codex-mcp.mjs
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
If discovery fails, set `CODEX_EXE` to the real CLI executable. Relay setup may
|
|
65
|
+
create an initial relay task. Do not add a permission-bypass mode. If CC Switch
|
|
66
|
+
manages the entries, save the same configuration there. Reload affected clients
|
|
67
|
+
and inspect the tools inside the actual Code/dispatch conversations.
|
|
68
|
+
|
|
69
|
+
## Permissions are separate
|
|
70
|
+
|
|
71
|
+
- Prepare workspace trust for the explicitly selected project using
|
|
72
|
+
`prepare_bridge_project`; verify with `inspect_bridge_project`.
|
|
73
|
+
- Grant specific communication projects or parents, not all local projects by
|
|
74
|
+
default. A card extension's grants are separate. Denials take precedence.
|
|
75
|
+
- If the user opts into automatic messaging, preview and apply
|
|
76
|
+
`configure-message-automation.mjs` as described in the onboarding guide. It
|
|
77
|
+
permits only the exact send/wait tools and preserves other host checks.
|
|
78
|
+
|
|
79
|
+
If card management is wanted, select its precise tools independently in the
|
|
80
|
+
actual Code permission configuration. Do not assume ordinary-chat connector
|
|
81
|
+
settings apply to Code, or that a query/authorization allow rule includes card
|
|
82
|
+
startup, cleanup or permanent deletion. `ready=true` is not host approval.
|
|
83
|
+
|
|
84
|
+
For a compatible optional card extension, install its MCPB and run
|
|
85
|
+
`node scripts/bridge-projects.mjs register-card-settings`. Verify separate card
|
|
86
|
+
and communication lists in its original settings page. Do not edit Claude's
|
|
87
|
+
private settings file to force a UI refresh. A local launcher using
|
|
88
|
+
`--disable-backgrounding-occluded-windows --disable-renderer-backgrounding`
|
|
89
|
+
helped background card redraw on the measured machine, but must be retested on
|
|
90
|
+
each client build. Remote Control is not required.
|
|
91
|
+
|
|
92
|
+
## One small end-to-end acceptance
|
|
93
|
+
|
|
94
|
+
Create one Claude collaboration parent from the intended Code project sidebar
|
|
95
|
+
and verify its real cwd first; see [daily handoffs](daily-handoffs.md).
|
|
96
|
+
|
|
97
|
+
1. Check both identities, target projects and normal tool permissions.
|
|
98
|
+
2. Send Codex-to-Claude and automatically receive the exact complete answer.
|
|
99
|
+
3. Send Claude-to-Codex; also test one accepted timeout followed by automatic
|
|
100
|
+
continuation with the same receipt and no second send.
|
|
101
|
+
4. In the authorized test directory, create, read back and delete one clearly
|
|
102
|
+
named test file. Respect normal file-operation approval.
|
|
103
|
+
5. If using cards, verify local and worktree startup, parent/child identity,
|
|
104
|
+
directory, branch, Remote Control off and no duplicate on repeated startup.
|
|
105
|
+
6. Revoke and restore test-only card authorization, checking UI and effective
|
|
106
|
+
state while communication grants remain unchanged. Run another normal task.
|
|
107
|
+
|
|
108
|
+
Record incomplete steps instead of calling tool discovery a full pass. Before
|
|
109
|
+
upgrading, back up configuration and receipts without publishing personal data.
|
|
110
|
+
Archive historical files with an inventory; verify dependencies and unique
|
|
111
|
+
worktree changes before deletion. Conversation archival does not prove that
|
|
112
|
+
its checkout or branch was removed.
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# Project onboarding and shared authorization
|
|
2
|
+
|
|
3
|
+
Both bridge directions can read the same optional policy file via
|
|
4
|
+
`CODEX_BRIDGE_PROJECT_POLICY`. `bridge-projects` defaults to
|
|
5
|
+
`~/.config/GptClaudeBridge/projects.json`. A shared home-directory path avoids
|
|
6
|
+
different AppData views in packaged Windows applications. The policy stores
|
|
7
|
+
canonical directory identities; do not copy it to a new machine as proof of
|
|
8
|
+
authorization there.
|
|
9
|
+
|
|
10
|
+
```sh
|
|
11
|
+
node scripts/bridge-projects.mjs allow-project /absolute/projects/example
|
|
12
|
+
node scripts/bridge-projects.mjs allow-parent /absolute/projects
|
|
13
|
+
node scripts/bridge-projects.mjs revoke /absolute/projects/example
|
|
14
|
+
node scripts/bridge-projects.mjs list
|
|
15
|
+
node scripts/bridge-projects.mjs check /absolute/projects/example
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
A registered worktree inherits a grant for its repository, including when its
|
|
19
|
+
directory is outside the granted parent. Denials beat parent grants and require
|
|
20
|
+
explicit reauthorization. Revocation affects subsequent operations, not work
|
|
21
|
+
already dispatched. Invalid/missing configured policies fail closed.
|
|
22
|
+
|
|
23
|
+
Project grants bind both the project directory and its verified common Git
|
|
24
|
+
directory. Revocation preserves repository and registered worktree path
|
|
25
|
+
denials even when the original directory or Git metadata disappears or is
|
|
26
|
+
replaced. Discovery failures retain saved denials and appear in policy status
|
|
27
|
+
as warnings; they cannot leave the original grant enabled. Malformed Git
|
|
28
|
+
markers fail closed instead of becoming ordinary folders under parent grants.
|
|
29
|
+
|
|
30
|
+
This optional `project-policy.mjs` store does not replace upstream
|
|
31
|
+
`project-scope.mjs`: Claude-to-Codex delivery still requires the verified sender
|
|
32
|
+
and target to share a directory or registered Git repository. Two granted
|
|
33
|
+
repositories are not permission to send between them. Diagnostic readiness
|
|
34
|
+
reports this mismatch separately from a missing grant.
|
|
35
|
+
|
|
36
|
+
## Explicit workspace preparation
|
|
37
|
+
|
|
38
|
+
After the user selects a project, `prepare_bridge_project({cwd})` saves its
|
|
39
|
+
Claude workspace trust and shared communication grant. Registered worktrees
|
|
40
|
+
resolve to the verified primary repository. Existing parent grants are reused.
|
|
41
|
+
Revoked projects require `reauthorize: true`; a denied parent still blocks them.
|
|
42
|
+
`inspect_bridge_project({cwd})` is read-only.
|
|
43
|
+
|
|
44
|
+
```sh
|
|
45
|
+
node scripts/bridge-projects.mjs inspect /absolute/projects/example
|
|
46
|
+
node scripts/bridge-projects.mjs prepare /absolute/projects/example
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Changes are backed up and verified. Concurrent changes abort; failed rollback
|
|
50
|
+
returns `partial_needs_review` and remaining files. `configured` does not mean
|
|
51
|
+
live communication passed. Setup does not approve tools, change session modes,
|
|
52
|
+
create conversations or restart clients. Workspace trust follows Claude's
|
|
53
|
+
[documented project settings](https://code.claude.com/docs/en/permissions#project-allow-rules-and-workspace-trust).
|
|
54
|
+
|
|
55
|
+
## Correct Code registration
|
|
56
|
+
|
|
57
|
+
Claude's user-level `codex-bridge` must run under the actual Code session. A
|
|
58
|
+
generic shared Desktop process cannot establish that caller's ancestry. The
|
|
59
|
+
Code migration helper previews changes by default; it is not a first-install
|
|
60
|
+
wizard. With both clients stopped, `--apply --clients-stopped` backs up and
|
|
61
|
+
migrates an existing supervised entry. `--check` diagnoses shared/duplicate
|
|
62
|
+
entries and policy errors. Customized entries are left for review. Keep an
|
|
63
|
+
external configuration manager, such as CC Switch, consistent with the selected
|
|
64
|
+
entry so it does not restore an obsolete copy later.
|
|
65
|
+
|
|
66
|
+
Reload the affected client after changing MCP registration. Merely refreshing
|
|
67
|
+
the conversation did not reload the Code process in the measured Windows
|
|
68
|
+
setup. Normal live policy changes do not need a restart. Verify in the actual
|
|
69
|
+
sending session using `codex_bridge_status({cwd})` or `claude_bridge_status`.
|
|
70
|
+
|
|
71
|
+
## Opt-in messaging automation
|
|
72
|
+
|
|
73
|
+
```sh
|
|
74
|
+
node scripts/configure-message-automation.mjs
|
|
75
|
+
node scripts/configure-message-automation.mjs --apply --approve-message-automation
|
|
76
|
+
node scripts/configure-message-automation.mjs --check
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
The explicit apply flag installs only
|
|
80
|
+
`mcp__codex-bridge__send_to_codex_thread` and
|
|
81
|
+
`mcp__codex-bridge__wait_codex_reply` allow rules, plus scoped collaboration
|
|
82
|
+
guidance. Existing ask/deny conflicts are not removed. Project grants, file and
|
|
83
|
+
shell permissions, deletion tools, session mode and managed policy are not
|
|
84
|
+
expanded. Host approval is separate from readiness; existing conversations may
|
|
85
|
+
retain earlier denial or authorization context and require a real user action.
|
|
86
|
+
Never disguise peer messages as direct user consent.
|
|
87
|
+
|
|
88
|
+
The user's standing collaboration scope permits its ordinary handoffs and
|
|
89
|
+
reports, not arbitrary instructions received from another agent. An accepted
|
|
90
|
+
timeout should be continued automatically using the same deliveryId. A receipt
|
|
91
|
+
survives reconnecting but remains bound to the original Claude sender and both
|
|
92
|
+
accounts; observation still rechecks current permissions and workspace.
|
|
93
|
+
Bounded waits require the agent to keep executing: this is not an unattended
|
|
94
|
+
push service that runs after Claude closes.
|
|
95
|
+
|
|
96
|
+
## Optional native card-settings integration
|
|
97
|
+
|
|
98
|
+
```sh
|
|
99
|
+
node scripts/bridge-projects.mjs register-card-settings
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
This registers local management paths in
|
|
103
|
+
`~/.config/GptClaudeBridge/management.json`; it does not install an extension or
|
|
104
|
+
grant a project. A compatible separately installed Local Card Bridge Desktop
|
|
105
|
+
can expose communication projects, parents and denials in its original settings
|
|
106
|
+
page, alongside independent card authorization. The bridge operates without
|
|
107
|
+
the extension, and the extension's card functions operate without the bridge.
|
|
108
|
+
|
|
109
|
+
Synchronization processes only changed entries, preserves newer denials and
|
|
110
|
+
reports concurrent conflicts. Removing a communication project adds a denial;
|
|
111
|
+
removing a parent cancels only that bulk grant. It does not change workspace
|
|
112
|
+
trust or tool permissions. A UI save can reload the extension, so inspect its
|
|
113
|
+
connection afterward; a saved file or matching screen is not a live round trip.
|
|
114
|
+
|
|
115
|
+
## Acceptance
|
|
116
|
+
|
|
117
|
+
Test both directions, a timeout followed by same-receipt continuation, and a
|
|
118
|
+
second ordinary handoff without reinitialization. Keep observed IDs and hashes
|
|
119
|
+
locally; do not publish transcripts or account data. The installed-source
|
|
120
|
+
Windows checks and the newly merged branch's automated tests are distinct; see
|
|
121
|
+
[design and validation limits](desktop-handoff-design.md). A status check cannot
|
|
122
|
+
guarantee compatibility with every future client version.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@minhspark/codex-mcp-bridge",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.21.0",
|
|
4
4
|
"description": "Two-way MCP bridge between Claude and Codex: prompts into a live Codex thread, messages into a running Claude Code session.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mcp",
|
|
@@ -25,6 +25,8 @@
|
|
|
25
25
|
"codex-mcp-bridge": "src/codex-bridge-launcher.mjs",
|
|
26
26
|
"claude-mcp-bridge": "src/claude-bridge-launcher.mjs",
|
|
27
27
|
"codex-mcp-bridge-install": "scripts/install-claude-desktop.mjs",
|
|
28
|
+
"codex-bridge-code-install": "scripts/install-claude-code.mjs",
|
|
29
|
+
"bridge-projects": "scripts/bridge-projects.mjs",
|
|
28
30
|
"claude-mcp-bridge-install": "scripts/install-codex-mcp.mjs",
|
|
29
31
|
"codex-native-relay": "src/native-relay-launcher.mjs",
|
|
30
32
|
"codex-native-relay-install": "scripts/install-native-relay.mjs",
|
|
@@ -43,7 +45,11 @@
|
|
|
43
45
|
".mcp.json",
|
|
44
46
|
"README.md",
|
|
45
47
|
"LICENSE",
|
|
46
|
-
"CHANGELOG.md"
|
|
48
|
+
"CHANGELOG.md",
|
|
49
|
+
"docs/daily-handoffs.md",
|
|
50
|
+
"docs/new-machine-setup.md",
|
|
51
|
+
"docs/project-onboarding.md",
|
|
52
|
+
"docs/desktop-handoff-design.md"
|
|
47
53
|
],
|
|
48
54
|
"publishConfig": {
|
|
49
55
|
"access": "public"
|
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.
|
|
4
|
+
"version": "1.21.0",
|
|
5
5
|
"description": "Connect ChatGPT to your own Codex Desktop projects and conversations.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Bui Dang Minh",
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import { createProjectScope, editProjectGrant, readProjectPolicy, updateProjectPolicy } from "../src/project-policy.mjs";
|
|
6
|
+
import { exitForVersionRequest } from "../src/cli-version.mjs";
|
|
7
|
+
import { inspectBridgeProject, prepareBridgeProject } from "../src/project-onboarding.mjs";
|
|
8
|
+
import { registerProjectManager } from "../src/project-manager-registration.mjs";
|
|
9
|
+
import { syncCardSettings } from "../src/card-settings-sync.mjs";
|
|
10
|
+
|
|
11
|
+
exitForVersionRequest(import.meta.url);
|
|
12
|
+
|
|
13
|
+
const args = process.argv.slice(2);
|
|
14
|
+
const index = args.indexOf("--file");
|
|
15
|
+
// A shared home path avoids packaged Windows clients' private AppData views.
|
|
16
|
+
const file = index < 0 ? process.env.CODEX_BRIDGE_PROJECT_POLICY ?? path.join(os.homedir(), ".config", "GptClaudeBridge", "projects.json") : args.splice(index, 2)[1];
|
|
17
|
+
const [action = "list", input] = args;
|
|
18
|
+
try {
|
|
19
|
+
if (action === "register-manager" || action === "register-card-settings") console.log(JSON.stringify(registerProjectManager({ policyFile: file }), null, 2));
|
|
20
|
+
else if (action === "sync-card-settings") {
|
|
21
|
+
if (!input || !path.isAbsolute(input)) throw new Error('An absolute settings snapshot file is required');
|
|
22
|
+
const stat = fs.lstatSync(input);
|
|
23
|
+
if (!stat.isFile() || stat.isSymbolicLink() || stat.size > 256 * 1024) throw new Error('Invalid settings snapshot');
|
|
24
|
+
const snapshot = JSON.parse(fs.readFileSync(input, 'utf8'));
|
|
25
|
+
console.log(JSON.stringify(syncCardSettings(file, snapshot, { acknowledgeRender: snapshot.acknowledgeRender === true }), null, 2));
|
|
26
|
+
}
|
|
27
|
+
else if (["prepare", "inspect"].includes(action)) {
|
|
28
|
+
if (!input) throw new Error("Usage: bridge-projects prepare|inspect <absolute project path> [--reauthorize] [--task-cards] [--file <policy.json>]");
|
|
29
|
+
const operation = action === "prepare" ? prepareBridgeProject : inspectBridgeProject;
|
|
30
|
+
console.log(JSON.stringify(operation({ cwd: input, reauthorize: args.includes("--reauthorize"), taskCards: args.includes("--task-cards") }, {
|
|
31
|
+
env: { ...process.env, CODEX_BRIDGE_PROJECT_POLICY: file },
|
|
32
|
+
}), null, 2));
|
|
33
|
+
}
|
|
34
|
+
else if (action === "list") console.log(JSON.stringify({ file, ...readProjectPolicy(file) }, null, 2));
|
|
35
|
+
else if (action === "check") {
|
|
36
|
+
const result = createProjectScope(file).status(input);
|
|
37
|
+
console.log(JSON.stringify(result, null, 2));
|
|
38
|
+
if (result.error) process.exitCode = 1;
|
|
39
|
+
} else {
|
|
40
|
+
if (!input) throw new Error("Usage: bridge-projects allow-project|allow-parent|revoke <absolute path> [--file <policy.json>]");
|
|
41
|
+
updateProjectPolicy(file, (policy) => editProjectGrant(policy, action, input));
|
|
42
|
+
console.log(JSON.stringify({ file, action, path: input, effective: "next bridge operation; no client restart needed" }, null, 2));
|
|
43
|
+
}
|
|
44
|
+
} catch (error) { console.error(JSON.stringify({ error: error.message, ...(error.onboarding ? { onboarding: error.onboarding } : {}) })); process.exitCode = 1; }
|
package/scripts/check.mjs
CHANGED
|
@@ -13,6 +13,7 @@ const bridgeEnvNames = [
|
|
|
13
13
|
"CODEX_APP_SERVER_URL",
|
|
14
14
|
"CODEX_BIN",
|
|
15
15
|
"CODEX_BRIDGE_ALLOWED_ROOTS",
|
|
16
|
+
"CODEX_BRIDGE_PROJECT_POLICY",
|
|
16
17
|
"CODEX_BRIDGE_ALLOWED_THREADS",
|
|
17
18
|
"CODEX_BRIDGE_APPROVAL",
|
|
18
19
|
"CODEX_BRIDGE_AUTO_APPROVE_ACK",
|
|
@@ -52,7 +53,7 @@ try {
|
|
|
52
53
|
const listed = await client.callTool(desktopTasksConfigured()
|
|
53
54
|
? { name: "codex_bridge_status", arguments: {} }
|
|
54
55
|
: { name: "list_codex_threads", arguments: { limit: 3 } });
|
|
55
|
-
console.log(listed.content[0].text
|
|
56
|
+
console.log(listed.content[0].text);
|
|
56
57
|
if (listed.isError) process.exitCode = 1;
|
|
57
58
|
} catch (err) {
|
|
58
59
|
console.error(err.message);
|
|
@@ -20,9 +20,12 @@ export function parseArgs(args) {
|
|
|
20
20
|
const options = { repo: defaultRepo, package: defaultPackage, output: defaultOutput() };
|
|
21
21
|
for (let i = 0; i < args.length; i++) {
|
|
22
22
|
if (args[i] === '--help' || args[i] === '-h') return { help: true };
|
|
23
|
-
const name = args[i].slice(2);
|
|
24
23
|
if (!['--repo', '--package', '--output'].includes(args[i]) || !args[i + 1] || args[i + 1].startsWith('--')) throw new Error('Expected --repo owner/name, --package name, or --output directory.');
|
|
25
|
-
|
|
24
|
+
const flag = args[i];
|
|
25
|
+
const value = args[++i];
|
|
26
|
+
if (flag === '--repo') options.repo = value;
|
|
27
|
+
else if (flag === '--package') options.package = value;
|
|
28
|
+
else options.output = value;
|
|
26
29
|
}
|
|
27
30
|
if (!/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(options.repo)) throw new Error('Invalid repository: expected owner/name.');
|
|
28
31
|
if (!/^(?:@[a-z0-9._-]+\/)?[a-z0-9._-]+$/.test(options.package)) throw new Error('Invalid npm package name.');
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { planMessageAutomation, applyMessageAutomation } from "../src/message-automation.mjs";
|
|
5
|
+
import { exitForVersionRequest } from "../src/cli-version.mjs";
|
|
6
|
+
exitForVersionRequest(import.meta.url);
|
|
7
|
+
const args = process.argv.slice(2);
|
|
8
|
+
const option = (key, fallback) => { const i = args.indexOf(key); if (i < 0) return fallback; if (!args[i + 1] || args[i + 1].startsWith("--")) throw new Error(`Missing value: ${key}`); return args[i + 1]; };
|
|
9
|
+
try {
|
|
10
|
+
const plan = planMessageAutomation({ claudeHome: option("--claude-home", path.join(os.homedir(), ".claude")), codeFile: option("--code-config", path.join(os.homedir(), ".claude.json")) });
|
|
11
|
+
const installed = plan.files.every(item => item.before === item.after);
|
|
12
|
+
console.log(JSON.stringify({ installed, policyFile: plan.policyFile, tools: plan.tools, changes: plan.files.filter(item => item.before !== item.after).map(item => item.file), limits: plan.limits }, null, 2));
|
|
13
|
+
if (args.includes("--apply")) {
|
|
14
|
+
if (!args.includes("--approve-message-automation")) throw new Error("Applying requires the user's opt-in: --approve-message-automation. Never invoke this to evade a peer tool refusal.");
|
|
15
|
+
console.log(JSON.stringify(applyMessageAutomation(plan), null, 2));
|
|
16
|
+
console.log("Verify in the actual Claude Code session that the settings and collaboration rule loaded; reload the existing client if needed. Test both directions without manual message relay.");
|
|
17
|
+
} else if (args.includes("--check") && !installed) process.exitCode = 1;
|
|
18
|
+
} catch (error) { console.error(error.message); process.exitCode = 1; }
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import { randomUUID } from "node:crypto";
|
|
6
|
+
import { claudeDesktopConfigPath } from "../src/platform.mjs";
|
|
7
|
+
import { checkCodeRegistration, planCodeRegistration } from "../src/claude-code-registration.mjs";
|
|
8
|
+
import { readProjectPolicy } from "../src/project-policy.mjs";
|
|
9
|
+
import { exitForVersionRequest } from "../src/cli-version.mjs";
|
|
10
|
+
|
|
11
|
+
exitForVersionRequest(import.meta.url);
|
|
12
|
+
|
|
13
|
+
const args = process.argv.slice(2);
|
|
14
|
+
const option = (key, fallback) => { const index = args.indexOf(key); return index < 0 ? fallback : args[index + 1]; };
|
|
15
|
+
const codeFile = option("--code-config", path.join(os.homedir(), ".claude.json"));
|
|
16
|
+
const desktopFile = option("--desktop-config", claudeDesktopConfigPath());
|
|
17
|
+
const policyFile = option("--policy", process.env.CODEX_BRIDGE_PROJECT_POLICY);
|
|
18
|
+
try {
|
|
19
|
+
const files = [codeFile, desktopFile];
|
|
20
|
+
const originals = files.map((file) => fs.existsSync(file) ? fs.readFileSync(file, "utf8") : null);
|
|
21
|
+
const [code, desktop] = originals.map((raw) => raw === null ? {} : JSON.parse(raw.replace(/^\uFEFF/, "")));
|
|
22
|
+
if (args.includes("--check")) {
|
|
23
|
+
const result = checkCodeRegistration({ code, desktop });
|
|
24
|
+
if (result.policyFile) { try { readProjectPolicy(result.policyFile); } catch (error) { result.issues.push(error.message); result.ready = false; } }
|
|
25
|
+
console.log(JSON.stringify(result, null, 2));
|
|
26
|
+
if (!result.ready) process.exitCode = 1;
|
|
27
|
+
} else {
|
|
28
|
+
if (!policyFile) throw new Error("Specify --policy <absolute shared projects.json>; initialize its authorized projects first");
|
|
29
|
+
readProjectPolicy(policyFile);
|
|
30
|
+
const plan = planCodeRegistration({ code, desktop, policyFile });
|
|
31
|
+
console.log(JSON.stringify({ action: args.includes("--apply") ? "apply" : "plan", changes: plan.changes, policyFile }, null, 2));
|
|
32
|
+
if (args.includes("--apply")) {
|
|
33
|
+
if (!args.includes("--clients-stopped")) throw new Error("Exit Claude and the configuration manager first, then pass --clients-stopped to avoid their overwriting the migration");
|
|
34
|
+
const contents = [plan.code, plan.desktop].map((value) => JSON.stringify(value, null, 2) + "\n");
|
|
35
|
+
const suffix = randomUUID();
|
|
36
|
+
const written = [];
|
|
37
|
+
try {
|
|
38
|
+
for (let index = 0; index < files.length; index++) {
|
|
39
|
+
const file = files[index];
|
|
40
|
+
const now = fs.existsSync(file) ? fs.readFileSync(file, "utf8") : null;
|
|
41
|
+
if (now !== originals[index]) throw new Error(`Configuration changed concurrently: ${file}`);
|
|
42
|
+
if (now === null && index === 1 && !Object.keys(plan.desktop.mcpServers ?? {}).length) continue;
|
|
43
|
+
if (now !== null) fs.copyFileSync(file, `${file}.backup-${suffix}`, fs.constants.COPYFILE_EXCL);
|
|
44
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
45
|
+
const temporary = `${file}.pending-${suffix}`;
|
|
46
|
+
try {
|
|
47
|
+
fs.writeFileSync(temporary, contents[index], { flag: "wx", mode: 0o600 });
|
|
48
|
+
fs.renameSync(temporary, file);
|
|
49
|
+
} finally { fs.rmSync(temporary, { force: true }); }
|
|
50
|
+
written.push(index);
|
|
51
|
+
}
|
|
52
|
+
} catch (error) {
|
|
53
|
+
for (const index of written) {
|
|
54
|
+
if (fs.readFileSync(files[index], "utf8") === contents[index]) {
|
|
55
|
+
if (originals[index] === null) fs.rmSync(files[index]);
|
|
56
|
+
else fs.writeFileSync(files[index], originals[index]);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
console.log("Reopen the original Code task and call codex_bridge_status with the intended project cwd. Update the configuration manager's matching entry to the same policy file. No new per-project MCP entry is needed.");
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
} catch (error) { console.error(error.message); process.exitCode = 1; }
|