@m2canvas/connect 0.3.1 → 0.4.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/README.md +19 -0
- package/adapters/claude-agent-acp/package-lock.json +1535 -0
- package/adapters/claude-agent-acp/package.json +8 -0
- package/adapters/codex-acp/package-lock.json +364 -0
- package/adapters/codex-acp/package.json +9 -0
- package/dist/bridge/acp-guard.js +92 -0
- package/dist/bridge/adapters.js +146 -0
- package/dist/bridge/agents.js +73 -0
- package/dist/bridge/env.js +107 -0
- package/dist/bridge/exec.js +90 -0
- package/dist/bridge/lifecycle.js +24 -0
- package/dist/bridge/m2-config.js +38 -0
- package/dist/bridge/main.js +245 -0
- package/dist/bridge/pairing.js +242 -0
- package/dist/bridge/precheck-parse.js +246 -0
- package/dist/bridge/precheck.js +149 -0
- package/dist/bridge/processes.js +212 -0
- package/dist/bridge/protocol.js +62 -0
- package/dist/bridge/security.js +52 -0
- package/dist/bridge/server.js +365 -0
- package/dist/bridge/service.js +109 -0
- package/dist/cli.js +8 -2
- package/package.json +7 -3
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "m2canvas-acp-adapter-claude",
|
|
3
|
+
"private": true,
|
|
4
|
+
"description": "Locked Claude ACP adapter, installed on demand by `npx @m2canvas/connect bridge`. Install with `npm ci` only.",
|
|
5
|
+
"dependencies": {
|
|
6
|
+
"@agentclientprotocol/claude-agent-acp": "0.84.0"
|
|
7
|
+
}
|
|
8
|
+
}
|
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "m2canvas-acp-adapter-codex",
|
|
3
|
+
"lockfileVersion": 3,
|
|
4
|
+
"requires": true,
|
|
5
|
+
"packages": {
|
|
6
|
+
"": {
|
|
7
|
+
"name": "m2canvas-acp-adapter-codex",
|
|
8
|
+
"dependencies": {
|
|
9
|
+
"@agentclientprotocol/codex-acp": "2.1.0",
|
|
10
|
+
"@agentclientprotocol/sdk": "1.5.1"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"node_modules/@agentclientprotocol/codex-acp": {
|
|
14
|
+
"version": "2.1.0",
|
|
15
|
+
"resolved": "https://registry.npmjs.org/@agentclientprotocol/codex-acp/-/codex-acp-2.1.0.tgz",
|
|
16
|
+
"integrity": "sha512-MJv/+hcnutTkUFdJj7ETwshyqOx1mMw+Q7Oaol2V3bnhudhz/wfG3L11IZI47+H2mryCB3FYHzZssc2fxtgXcg==",
|
|
17
|
+
"license": "Apache-2.0",
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@agentclientprotocol/sdk": "^1.5.0",
|
|
20
|
+
"@openai/codex": "^0.159.1",
|
|
21
|
+
"diff": "^9.0.0",
|
|
22
|
+
"open": "^11.0.1",
|
|
23
|
+
"vscode-jsonrpc": "^9.0.1",
|
|
24
|
+
"zod": "^4.0.0"
|
|
25
|
+
},
|
|
26
|
+
"bin": {
|
|
27
|
+
"codex-acp": "dist/index.js"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"node_modules/@agentclientprotocol/sdk": {
|
|
31
|
+
"version": "1.5.1",
|
|
32
|
+
"resolved": "https://registry.npmjs.org/@agentclientprotocol/sdk/-/sdk-1.5.1.tgz",
|
|
33
|
+
"integrity": "sha512-nSUIrC1fOR9+ppKfx9KdwjBBND2sAPzFHo4yVQrGbXZHnayUYpwL8CDVR5LSxbapY/oKMOzNP0wvwfyx3PS2KA==",
|
|
34
|
+
"license": "Apache-2.0",
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"zod": "^3.25.0 || ^4.0.0"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"node_modules/@openai/codex": {
|
|
40
|
+
"version": "0.159.3",
|
|
41
|
+
"resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.159.3.tgz",
|
|
42
|
+
"integrity": "sha512-hg4nlqCwRb87GocxNnHvz4sadOrdJ71mO0a4CUYWXjAozJe2gtuAdRFldKGSQXw9m7SgR4ydis12KDVVPgzDkQ==",
|
|
43
|
+
"license": "Apache-2.0",
|
|
44
|
+
"bin": {
|
|
45
|
+
"codex": "bin/codex.js"
|
|
46
|
+
},
|
|
47
|
+
"engines": {
|
|
48
|
+
"node": ">=16"
|
|
49
|
+
},
|
|
50
|
+
"optionalDependencies": {
|
|
51
|
+
"@openai/codex-darwin-arm64": "npm:@openai/codex@0.159.3-darwin-arm64",
|
|
52
|
+
"@openai/codex-darwin-x64": "npm:@openai/codex@0.159.3-darwin-x64",
|
|
53
|
+
"@openai/codex-linux-arm64": "npm:@openai/codex@0.159.3-linux-arm64",
|
|
54
|
+
"@openai/codex-linux-x64": "npm:@openai/codex@0.159.3-linux-x64",
|
|
55
|
+
"@openai/codex-win32-arm64": "npm:@openai/codex@0.159.3-win32-arm64",
|
|
56
|
+
"@openai/codex-win32-x64": "npm:@openai/codex@0.159.3-win32-x64"
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
"node_modules/@openai/codex-darwin-arm64": {
|
|
60
|
+
"name": "@openai/codex",
|
|
61
|
+
"version": "0.159.3-darwin-arm64",
|
|
62
|
+
"resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.159.3-darwin-arm64.tgz",
|
|
63
|
+
"integrity": "sha512-aI4UY14YURYxJxnRK+AE4QU+aek0mgtyyo7Rw9rNbCQUYETRQ0NYdJzU9ytljERGpPlht3dHHI1u4NhqHoDJDQ==",
|
|
64
|
+
"cpu": [
|
|
65
|
+
"arm64"
|
|
66
|
+
],
|
|
67
|
+
"license": "Apache-2.0",
|
|
68
|
+
"optional": true,
|
|
69
|
+
"os": [
|
|
70
|
+
"darwin"
|
|
71
|
+
],
|
|
72
|
+
"engines": {
|
|
73
|
+
"node": ">=16"
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
"node_modules/@openai/codex-darwin-x64": {
|
|
77
|
+
"name": "@openai/codex",
|
|
78
|
+
"version": "0.159.3-darwin-x64",
|
|
79
|
+
"resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.159.3-darwin-x64.tgz",
|
|
80
|
+
"integrity": "sha512-KTOQOD184DMXpR3TqnDUnLsgad14WJn+x3XmjBaXRHchKYGttjREwqrwzC61xQYFeh6VkogAceybR+SMbbgvYQ==",
|
|
81
|
+
"cpu": [
|
|
82
|
+
"x64"
|
|
83
|
+
],
|
|
84
|
+
"license": "Apache-2.0",
|
|
85
|
+
"optional": true,
|
|
86
|
+
"os": [
|
|
87
|
+
"darwin"
|
|
88
|
+
],
|
|
89
|
+
"engines": {
|
|
90
|
+
"node": ">=16"
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
"node_modules/@openai/codex-linux-arm64": {
|
|
94
|
+
"name": "@openai/codex",
|
|
95
|
+
"version": "0.159.3-linux-arm64",
|
|
96
|
+
"resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.159.3-linux-arm64.tgz",
|
|
97
|
+
"integrity": "sha512-84C1yhAI+i0hqh77Akp/Ma09Z4d5oLKcZKqW1WZpEDh8bKbNvvbNEd6pE+GMUe2Kwg7jCbuvkRNH1zgK+SCM3Q==",
|
|
98
|
+
"cpu": [
|
|
99
|
+
"arm64"
|
|
100
|
+
],
|
|
101
|
+
"license": "Apache-2.0",
|
|
102
|
+
"optional": true,
|
|
103
|
+
"os": [
|
|
104
|
+
"linux"
|
|
105
|
+
],
|
|
106
|
+
"engines": {
|
|
107
|
+
"node": ">=16"
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
"node_modules/@openai/codex-linux-x64": {
|
|
111
|
+
"name": "@openai/codex",
|
|
112
|
+
"version": "0.159.3-linux-x64",
|
|
113
|
+
"resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.159.3-linux-x64.tgz",
|
|
114
|
+
"integrity": "sha512-xlHydfOksnNt/qz4BEVFvjMWunweaVSNtha/DkVUwWZnVZxbqiJwsXSl2iZDk04b6k/g0OI7jU06XqzHB2s5og==",
|
|
115
|
+
"cpu": [
|
|
116
|
+
"x64"
|
|
117
|
+
],
|
|
118
|
+
"license": "Apache-2.0",
|
|
119
|
+
"optional": true,
|
|
120
|
+
"os": [
|
|
121
|
+
"linux"
|
|
122
|
+
],
|
|
123
|
+
"engines": {
|
|
124
|
+
"node": ">=16"
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
"node_modules/@openai/codex-win32-arm64": {
|
|
128
|
+
"name": "@openai/codex",
|
|
129
|
+
"version": "0.159.3-win32-arm64",
|
|
130
|
+
"resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.159.3-win32-arm64.tgz",
|
|
131
|
+
"integrity": "sha512-4C+EuH1zE4rO5w1MsrnS98Tvq6Ff4+zeNr3pVeR/UsQ+VrTY5BCA9nZuXuCXNLKak1tLOw81b+fQM4jKGFCeDA==",
|
|
132
|
+
"cpu": [
|
|
133
|
+
"arm64"
|
|
134
|
+
],
|
|
135
|
+
"license": "Apache-2.0",
|
|
136
|
+
"optional": true,
|
|
137
|
+
"os": [
|
|
138
|
+
"win32"
|
|
139
|
+
],
|
|
140
|
+
"engines": {
|
|
141
|
+
"node": ">=16"
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
"node_modules/@openai/codex-win32-x64": {
|
|
145
|
+
"name": "@openai/codex",
|
|
146
|
+
"version": "0.159.3-win32-x64",
|
|
147
|
+
"resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.159.3-win32-x64.tgz",
|
|
148
|
+
"integrity": "sha512-h8w5nslfQyoYbonZaRlLwvPKFS9Mcxz5vUeCeoHKF5SsZUP9jSBH7hxUdc8RWJffJtfbV2qxdnImVAj9zy2fwA==",
|
|
149
|
+
"cpu": [
|
|
150
|
+
"x64"
|
|
151
|
+
],
|
|
152
|
+
"license": "Apache-2.0",
|
|
153
|
+
"optional": true,
|
|
154
|
+
"os": [
|
|
155
|
+
"win32"
|
|
156
|
+
],
|
|
157
|
+
"engines": {
|
|
158
|
+
"node": ">=16"
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
"node_modules/bundle-name": {
|
|
162
|
+
"version": "4.1.1",
|
|
163
|
+
"resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.1.tgz",
|
|
164
|
+
"integrity": "sha512-DdH81/zPLVS11EUgWq3tEu/xn+EzljlMYooDNdzWEnFha3R3NBMpMV1UqYIpjYHV/SgpFKMIX1Oh7o06SjM/oA==",
|
|
165
|
+
"license": "MIT",
|
|
166
|
+
"dependencies": {
|
|
167
|
+
"run-applescript": "^7.0.0"
|
|
168
|
+
},
|
|
169
|
+
"engines": {
|
|
170
|
+
"node": ">=18"
|
|
171
|
+
},
|
|
172
|
+
"funding": {
|
|
173
|
+
"url": "https://github.com/sponsors/sindresorhus"
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
"node_modules/default-browser": {
|
|
177
|
+
"version": "5.5.1",
|
|
178
|
+
"resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.1.tgz",
|
|
179
|
+
"integrity": "sha512-m1pAzaJgZ/gssEqlOhJkPJp8Xly7QyW6xcrkUa2KKcDeDSEMP7X8xipU3snUcfisTQx0w1AGae+9UtJSfVnXGw==",
|
|
180
|
+
"license": "MIT",
|
|
181
|
+
"dependencies": {
|
|
182
|
+
"bundle-name": "^4.1.0",
|
|
183
|
+
"default-browser-id": "^5.0.0"
|
|
184
|
+
},
|
|
185
|
+
"engines": {
|
|
186
|
+
"node": ">=18"
|
|
187
|
+
},
|
|
188
|
+
"funding": {
|
|
189
|
+
"url": "https://github.com/sponsors/sindresorhus"
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
"node_modules/default-browser-id": {
|
|
193
|
+
"version": "5.0.1",
|
|
194
|
+
"resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz",
|
|
195
|
+
"integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==",
|
|
196
|
+
"license": "MIT",
|
|
197
|
+
"engines": {
|
|
198
|
+
"node": ">=18"
|
|
199
|
+
},
|
|
200
|
+
"funding": {
|
|
201
|
+
"url": "https://github.com/sponsors/sindresorhus"
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
"node_modules/define-lazy-prop": {
|
|
205
|
+
"version": "3.0.0",
|
|
206
|
+
"resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz",
|
|
207
|
+
"integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==",
|
|
208
|
+
"license": "MIT",
|
|
209
|
+
"engines": {
|
|
210
|
+
"node": ">=12"
|
|
211
|
+
},
|
|
212
|
+
"funding": {
|
|
213
|
+
"url": "https://github.com/sponsors/sindresorhus"
|
|
214
|
+
}
|
|
215
|
+
},
|
|
216
|
+
"node_modules/diff": {
|
|
217
|
+
"version": "9.0.0",
|
|
218
|
+
"resolved": "https://registry.npmjs.org/diff/-/diff-9.0.0.tgz",
|
|
219
|
+
"integrity": "sha512-svtcdpS8CgJyqAjEQIXdb3OjhFVVYjzGAPO8WGCmRbrml64SPw/jJD4GoE98aR7r25A0XcgrK3F02yw9R/vhQw==",
|
|
220
|
+
"license": "BSD-3-Clause",
|
|
221
|
+
"engines": {
|
|
222
|
+
"node": ">=0.3.1"
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
"node_modules/is-docker": {
|
|
226
|
+
"version": "3.0.0",
|
|
227
|
+
"resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz",
|
|
228
|
+
"integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==",
|
|
229
|
+
"license": "MIT",
|
|
230
|
+
"bin": {
|
|
231
|
+
"is-docker": "cli.js"
|
|
232
|
+
},
|
|
233
|
+
"engines": {
|
|
234
|
+
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
|
235
|
+
},
|
|
236
|
+
"funding": {
|
|
237
|
+
"url": "https://github.com/sponsors/sindresorhus"
|
|
238
|
+
}
|
|
239
|
+
},
|
|
240
|
+
"node_modules/is-in-ssh": {
|
|
241
|
+
"version": "1.0.0",
|
|
242
|
+
"resolved": "https://registry.npmjs.org/is-in-ssh/-/is-in-ssh-1.0.0.tgz",
|
|
243
|
+
"integrity": "sha512-jYa6Q9rH90kR1vKB6NM7qqd1mge3Fx4Dhw5TVlK1MUBqhEOuCagrEHMevNuCcbECmXZ0ThXkRm+Ymr51HwEPAw==",
|
|
244
|
+
"license": "MIT",
|
|
245
|
+
"engines": {
|
|
246
|
+
"node": ">=20"
|
|
247
|
+
},
|
|
248
|
+
"funding": {
|
|
249
|
+
"url": "https://github.com/sponsors/sindresorhus"
|
|
250
|
+
}
|
|
251
|
+
},
|
|
252
|
+
"node_modules/is-inside-container": {
|
|
253
|
+
"version": "1.0.0",
|
|
254
|
+
"resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz",
|
|
255
|
+
"integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==",
|
|
256
|
+
"license": "MIT",
|
|
257
|
+
"dependencies": {
|
|
258
|
+
"is-docker": "^3.0.0"
|
|
259
|
+
},
|
|
260
|
+
"bin": {
|
|
261
|
+
"is-inside-container": "cli.js"
|
|
262
|
+
},
|
|
263
|
+
"engines": {
|
|
264
|
+
"node": ">=14.16"
|
|
265
|
+
},
|
|
266
|
+
"funding": {
|
|
267
|
+
"url": "https://github.com/sponsors/sindresorhus"
|
|
268
|
+
}
|
|
269
|
+
},
|
|
270
|
+
"node_modules/is-wsl": {
|
|
271
|
+
"version": "3.1.1",
|
|
272
|
+
"resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz",
|
|
273
|
+
"integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==",
|
|
274
|
+
"license": "MIT",
|
|
275
|
+
"dependencies": {
|
|
276
|
+
"is-inside-container": "^1.0.0"
|
|
277
|
+
},
|
|
278
|
+
"engines": {
|
|
279
|
+
"node": ">=16"
|
|
280
|
+
},
|
|
281
|
+
"funding": {
|
|
282
|
+
"url": "https://github.com/sponsors/sindresorhus"
|
|
283
|
+
}
|
|
284
|
+
},
|
|
285
|
+
"node_modules/open": {
|
|
286
|
+
"version": "11.0.4",
|
|
287
|
+
"resolved": "https://registry.npmjs.org/open/-/open-11.0.4.tgz",
|
|
288
|
+
"integrity": "sha512-++Zlftm0kVLPmzC06t6epuWmcRMDbI4z5P3NNX979WA/k23+NtSOynEGzsVfZwguKw2mi5umVgnBlJQMwRz4Pg==",
|
|
289
|
+
"license": "MIT",
|
|
290
|
+
"dependencies": {
|
|
291
|
+
"default-browser": "^5.5.1",
|
|
292
|
+
"define-lazy-prop": "^3.0.0",
|
|
293
|
+
"is-in-ssh": "^1.0.0",
|
|
294
|
+
"is-inside-container": "^1.0.0",
|
|
295
|
+
"powershell-utils": "^0.2.1",
|
|
296
|
+
"wsl-utils": "^1.0.0"
|
|
297
|
+
},
|
|
298
|
+
"engines": {
|
|
299
|
+
"node": ">=20"
|
|
300
|
+
},
|
|
301
|
+
"funding": {
|
|
302
|
+
"url": "https://github.com/sponsors/sindresorhus"
|
|
303
|
+
}
|
|
304
|
+
},
|
|
305
|
+
"node_modules/powershell-utils": {
|
|
306
|
+
"version": "0.2.1",
|
|
307
|
+
"resolved": "https://registry.npmjs.org/powershell-utils/-/powershell-utils-0.2.1.tgz",
|
|
308
|
+
"integrity": "sha512-C+y9x90UElAddDZmV4qOx9W53B61PO7cIqWz2dQsWlwswuq4mr8NEwytdGKboYbQlGZ3awrkTeNvcZiZNHnQ8A==",
|
|
309
|
+
"license": "MIT",
|
|
310
|
+
"engines": {
|
|
311
|
+
"node": ">=20"
|
|
312
|
+
},
|
|
313
|
+
"funding": {
|
|
314
|
+
"url": "https://github.com/sponsors/sindresorhus"
|
|
315
|
+
}
|
|
316
|
+
},
|
|
317
|
+
"node_modules/run-applescript": {
|
|
318
|
+
"version": "7.1.0",
|
|
319
|
+
"resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz",
|
|
320
|
+
"integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==",
|
|
321
|
+
"license": "MIT",
|
|
322
|
+
"engines": {
|
|
323
|
+
"node": ">=18"
|
|
324
|
+
},
|
|
325
|
+
"funding": {
|
|
326
|
+
"url": "https://github.com/sponsors/sindresorhus"
|
|
327
|
+
}
|
|
328
|
+
},
|
|
329
|
+
"node_modules/vscode-jsonrpc": {
|
|
330
|
+
"version": "9.0.3",
|
|
331
|
+
"resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.3.tgz",
|
|
332
|
+
"integrity": "sha512-qETYf9PCl1lVfwR+oBmMfc4vP2nqLNA35ABxorw3j06+xlKRgDVxbLgKm4Y/xPL0YWdeDjLoI4ImICIT3nB0zQ==",
|
|
333
|
+
"license": "MIT",
|
|
334
|
+
"engines": {
|
|
335
|
+
"node": ">=14.0.0"
|
|
336
|
+
}
|
|
337
|
+
},
|
|
338
|
+
"node_modules/wsl-utils": {
|
|
339
|
+
"version": "1.0.1",
|
|
340
|
+
"resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-1.0.1.tgz",
|
|
341
|
+
"integrity": "sha512-c16PKLIRxBYpR3jSWsdS7SFp0COm5SOQdiFjY818dYFHJbMGuLBszpF07LBWmOS1uzrHa9VGdynsEgoeKy0oOQ==",
|
|
342
|
+
"license": "MIT",
|
|
343
|
+
"dependencies": {
|
|
344
|
+
"is-wsl": "^3.1.0",
|
|
345
|
+
"powershell-utils": "^0.2.0"
|
|
346
|
+
},
|
|
347
|
+
"engines": {
|
|
348
|
+
"node": ">=20"
|
|
349
|
+
},
|
|
350
|
+
"funding": {
|
|
351
|
+
"url": "https://github.com/sponsors/sindresorhus"
|
|
352
|
+
}
|
|
353
|
+
},
|
|
354
|
+
"node_modules/zod": {
|
|
355
|
+
"version": "4.6.5",
|
|
356
|
+
"resolved": "https://registry.npmjs.org/zod/-/zod-4.6.5.tgz",
|
|
357
|
+
"integrity": "sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q==",
|
|
358
|
+
"license": "MIT",
|
|
359
|
+
"funding": {
|
|
360
|
+
"url": "https://github.com/sponsors/colinhacks"
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "m2canvas-acp-adapter-codex",
|
|
3
|
+
"private": true,
|
|
4
|
+
"description": "Locked Codex ACP adapter, installed on demand by `npx @m2canvas/connect bridge`. Install with `npm ci` only. @agentclientprotocol/sdk is pinned to the version the ACP PoC ran with (codex-acp itself asks for ^1.5.0).",
|
|
5
|
+
"dependencies": {
|
|
6
|
+
"@agentclientprotocol/codex-acp": "2.1.0",
|
|
7
|
+
"@agentclientprotocol/sdk": "1.5.1"
|
|
8
|
+
}
|
|
9
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 页面 → agent 方向的 ACP 消息检查(纯函数)。bridge 唯一会解析的 ACP 内容就在这里;agent → 页面方向仍然原样转发、不解析。
|
|
3
|
+
*
|
|
4
|
+
* 为什么要有它(review r3 B1):ACP 允许客户端在会话类请求里指定工作目录(`cwd`)、让 agent 额外拉起 MCP 服务器
|
|
5
|
+
* (`mcpServers`,stdio 类型带 command / args / env)、扩大工作区(`additionalDirectories`),Claude 适配器还会从
|
|
6
|
+
* `_meta.claudeCode.options` 读 env / settings。原样转发等于让页面绕过「会话目录由终端用户决定」「不注入 MCP」这两条
|
|
7
|
+
* 已拍板的边界。这里把它们落到实处:
|
|
8
|
+
* - 方法白名单(review r4 R4-1):只放行 `PAGE_ACP_METHODS`,其余一律拒收。名单外有能改 agent 状态的方法 ——
|
|
9
|
+
* `providers/set` 换掉 Claude 的 API 地址与 headers(作用于所有会话)、`authenticate` 的 gateway 方式经 `_meta` 传
|
|
10
|
+
* baseUrl / headers、`logout` 把用户登出、`session/delete` 删本机历史 —— 按字段名扫描挡不住换了字段名的同义参数,
|
|
11
|
+
* 所以按方法放行。页面要用新方法时,在这里加上并发新版 bridge;旧 bridge 会回 `acp_rejected`,页面按 `bridge.version` 引导升级。
|
|
12
|
+
* - 会话类方法(session/new、load、resume、list):`cwd` 必须逐字等于 bridge 的会话目录(welcome 里报给页面的那个值);
|
|
13
|
+
* session/list 必须带 cwd(不带时 Claude / Codex 会列出全机所有项目的会话标题)。
|
|
14
|
+
* - 白名单内的任何请求:params 里出现 `cwd` 必须等于会话目录;`mcpServers` 只能是空数组或不带;不允许 `_meta`、
|
|
15
|
+
* 不允许非空 `additionalDirectories`。
|
|
16
|
+
* - 形状:不是 JSON 对象的行一律拒收(含 JSON-RPC 批量数组 —— ACP SDK 会把数组逐条入队);有 `method` 键但不是字符串的拒收
|
|
17
|
+
* (不能当成响应放行);消息或 params 自带 `__proto__` / `constructor` / `prototype` 键的拒收。
|
|
18
|
+
*
|
|
19
|
+
* 边界(不要把它当成安全的全部):这是纵深防御,不改变「配对 = 授权该页面以你的身份驱动本机 agent」——
|
|
20
|
+
* 页面仍可以用提示词让 agent 执行命令、替用户回答审批、切换到不审批的工作模式。真正的门槛是 security.ts / pairing.ts 的准入。
|
|
21
|
+
* 前提:agent 解析 JSON 的结果与这里一致(例如重复键都取最后一个)。2026-10-03 核实:两个适配器经 ACP SDK 1.5.1 的
|
|
22
|
+
* stream.js 用 JSON.parse;Cursor 是 node bundle;OpenCode、Qoder 是 Bun 单文件可执行(证据见 change 的 verification §13)。
|
|
23
|
+
* 哪家换了解析器,要重新核这一条。
|
|
24
|
+
*/
|
|
25
|
+
import { BRIDGE_PAGE_ACP_METHODS } from './protocol.js';
|
|
26
|
+
/** 页面可以发给 agent 的方法:change 3 实际用到的方法 + JSON-RPC 层的取消请求(定义在 protocol.ts,前端也能 import) */
|
|
27
|
+
export const PAGE_ACP_METHODS = BRIDGE_PAGE_ACP_METHODS;
|
|
28
|
+
/** cwd 由 bridge 决定的会话类方法(必须带 cwd) */
|
|
29
|
+
export const SESSION_SCOPE_METHODS = ['session/new', 'session/load', 'session/resume', 'session/list'];
|
|
30
|
+
/** JSON.parse 会把这些键建成普通自有属性;agent 侧的对象合并 / 校验可能对它们另有处理,一律不放行 */
|
|
31
|
+
const FORBIDDEN_KEYS = ['__proto__', 'constructor', 'prototype'];
|
|
32
|
+
function isObject(value) {
|
|
33
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
34
|
+
}
|
|
35
|
+
function hasOwn(value, key) {
|
|
36
|
+
return Object.prototype.hasOwnProperty.call(value, key);
|
|
37
|
+
}
|
|
38
|
+
function hasForbiddenKey(value) {
|
|
39
|
+
return Object.keys(value).some((key) => FORBIDDEN_KEYS.includes(key));
|
|
40
|
+
}
|
|
41
|
+
function rpcIdOf(message) {
|
|
42
|
+
const id = message.id;
|
|
43
|
+
return typeof id === 'string' || typeof id === 'number' ? id : null;
|
|
44
|
+
}
|
|
45
|
+
/** 返回违规原因;合规返回 null */
|
|
46
|
+
function scopeViolation(params, cwd, sessionScoped) {
|
|
47
|
+
if (hasForbiddenKey(params))
|
|
48
|
+
return 'params must not contain __proto__ / constructor / prototype';
|
|
49
|
+
if (hasOwn(params, 'cwd') || sessionScoped) {
|
|
50
|
+
if (params.cwd !== cwd)
|
|
51
|
+
return `cwd must be the bridge working directory (${cwd})`;
|
|
52
|
+
}
|
|
53
|
+
if (hasOwn(params, 'mcpServers') && !(Array.isArray(params.mcpServers) && params.mcpServers.length === 0)) {
|
|
54
|
+
return 'mcpServers must be empty: agents use their own MCP configuration';
|
|
55
|
+
}
|
|
56
|
+
if (hasOwn(params, 'additionalDirectories') && !(Array.isArray(params.additionalDirectories) && params.additionalDirectories.length === 0)) {
|
|
57
|
+
return 'additionalDirectories is not allowed';
|
|
58
|
+
}
|
|
59
|
+
if (hasOwn(params, '_meta'))
|
|
60
|
+
return '_meta is not allowed';
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
export function checkPageAcpLine(line, cwd) {
|
|
64
|
+
let message;
|
|
65
|
+
try {
|
|
66
|
+
message = JSON.parse(line);
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return { ok: false, reason: 'not valid JSON', method: null, rpcId: null };
|
|
70
|
+
}
|
|
71
|
+
if (!isObject(message))
|
|
72
|
+
return { ok: false, reason: 'must be a single JSON-RPC message object', method: null, rpcId: null };
|
|
73
|
+
const method = typeof message.method === 'string' ? message.method : null;
|
|
74
|
+
const reject = (reason) => ({ ok: false, reason, method, rpcId: rpcIdOf(message) });
|
|
75
|
+
if (hasForbiddenKey(message))
|
|
76
|
+
return reject('message must not contain __proto__ / constructor / prototype');
|
|
77
|
+
// 没有 method 键的是页面对 agent 请求的响应(例如审批结果),不涉及方法与会话范围
|
|
78
|
+
if (!hasOwn(message, 'method'))
|
|
79
|
+
return { ok: true };
|
|
80
|
+
if (method === null)
|
|
81
|
+
return reject('method must be a string');
|
|
82
|
+
if (!PAGE_ACP_METHODS.includes(method))
|
|
83
|
+
return reject(`method ${method} is not allowed from the page`);
|
|
84
|
+
const sessionScoped = SESSION_SCOPE_METHODS.includes(method);
|
|
85
|
+
const params = message.params;
|
|
86
|
+
if (params === undefined && !sessionScoped)
|
|
87
|
+
return { ok: true };
|
|
88
|
+
if (!isObject(params))
|
|
89
|
+
return reject('params must be an object');
|
|
90
|
+
const violation = scopeViolation(params, cwd, sessionScoped);
|
|
91
|
+
return violation ? reject(violation) : { ok: true };
|
|
92
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude / Codex 的 ACP 适配器:按需安装、精确锁定(用户拍板:用适配器自带的 agent 版本,首次使用时再装)。
|
|
3
|
+
*
|
|
4
|
+
* 为什么不放进 connect 的 dependencies:两个适配器各自带一份原生 agent(darwin-arm64 解压后 Claude 226MB、Codex 331MB)。
|
|
5
|
+
* 放进去会让每一次 `npx @m2canvas/connect --client X`(Copy to agent 起手式,在 agent 的沙箱里跑)都下载这些。
|
|
6
|
+
*
|
|
7
|
+
* 为什么必须用随包 lockfile + `npm ci`,而不是 `npx <adapter>@<version>`:codex-acp 对 @openai/codex 是 ^0.159.1,
|
|
8
|
+
* claude-agent-sdk 的 peer 依赖也是范围,只有 lockfile 能把整棵树(含平台二进制)锁死;npm ci 还会校验 integrity。
|
|
9
|
+
*
|
|
10
|
+
* 维护约束:
|
|
11
|
+
* - 升级适配器 = 改 adapters/<dir>/package.json + 用官方 registry 重新生成 package-lock.json + 改下面的 ADAPTERS 常量
|
|
12
|
+
* (单测会核对三者一致),并按 ACP PoC 的场景重新实测。不要只改版本号。
|
|
13
|
+
* - `--ignore-scripts`:两条依赖链上没有安装脚本(实测),加上它能挡掉安装期执行代码。
|
|
14
|
+
* - 安装目录名带 lockfile 哈希:换了 lockfile 就是新目录,旧目录不会被半新半旧地复用。旧目录目前不清理(FU-2026-10-02-120)。
|
|
15
|
+
* - 先装到临时目录、写好就绪标记再原子改名;残留的临时目录或没有标记的目录一律视为未安装。
|
|
16
|
+
*/
|
|
17
|
+
import { createHash, randomBytes } from 'node:crypto';
|
|
18
|
+
import { copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync } from 'node:fs';
|
|
19
|
+
import { join } from 'node:path';
|
|
20
|
+
import { combinedOutput } from './exec.js';
|
|
21
|
+
import { tail } from './precheck-parse.js';
|
|
22
|
+
export const ADAPTERS = {
|
|
23
|
+
claude: {
|
|
24
|
+
dir: 'claude-agent-acp',
|
|
25
|
+
package: '@agentclientprotocol/claude-agent-acp',
|
|
26
|
+
version: '0.84.0',
|
|
27
|
+
entry: 'node_modules/@agentclientprotocol/claude-agent-acp/dist/index.js',
|
|
28
|
+
},
|
|
29
|
+
codex: {
|
|
30
|
+
dir: 'codex-acp',
|
|
31
|
+
package: '@agentclientprotocol/codex-acp',
|
|
32
|
+
version: '2.1.0',
|
|
33
|
+
entry: 'node_modules/@agentclientprotocol/codex-acp/dist/index.js',
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
/** 适配器自带的 codex:预检与授权命令都要用它(本机旧版 codex 存的凭证,新版本刷新不了,PoC 报告 §2.2) */
|
|
37
|
+
export const CODEX_SCRIPT = 'node_modules/@openai/codex/bin/codex.js';
|
|
38
|
+
export const CODEX_BIN_LINK = 'node_modules/.bin/codex';
|
|
39
|
+
const READY_MARKER = '.m2canvas-ready';
|
|
40
|
+
function isProcessAlive(pid) {
|
|
41
|
+
try {
|
|
42
|
+
process.kill(pid, 0);
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
// EPERM = 进程在但属于别的用户,也算活着
|
|
47
|
+
return error.code === 'EPERM';
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
export function isAdapterAgent(agent) {
|
|
51
|
+
return agent === 'claude' || agent === 'codex';
|
|
52
|
+
}
|
|
53
|
+
export class AdapterManager {
|
|
54
|
+
options;
|
|
55
|
+
installing = new Map();
|
|
56
|
+
lastError = new Map();
|
|
57
|
+
constructor(options) {
|
|
58
|
+
this.options = options;
|
|
59
|
+
}
|
|
60
|
+
lockKey(agent) {
|
|
61
|
+
const lock = readFileSync(join(this.options.sourceRoot, ADAPTERS[agent].dir, 'package-lock.json'));
|
|
62
|
+
return createHash('sha256').update(lock).digest('hex').slice(0, 16);
|
|
63
|
+
}
|
|
64
|
+
installDir(agent) {
|
|
65
|
+
return join(this.options.installRoot, `${ADAPTERS[agent].dir}-${this.lockKey(agent)}`);
|
|
66
|
+
}
|
|
67
|
+
entryPath(agent) {
|
|
68
|
+
return join(this.installDir(agent), ADAPTERS[agent].entry);
|
|
69
|
+
}
|
|
70
|
+
isReady(agent) {
|
|
71
|
+
const dir = this.installDir(agent);
|
|
72
|
+
return existsSync(join(dir, READY_MARKER)) && existsSync(join(dir, ADAPTERS[agent].entry));
|
|
73
|
+
}
|
|
74
|
+
info(agent) {
|
|
75
|
+
const spec = ADAPTERS[agent];
|
|
76
|
+
const base = { package: spec.package, version: spec.version };
|
|
77
|
+
if (this.installing.has(agent))
|
|
78
|
+
return { ...base, state: 'installing', error: null };
|
|
79
|
+
if (this.isReady(agent))
|
|
80
|
+
return { ...base, state: 'ready', error: null };
|
|
81
|
+
const error = this.lastError.get(agent);
|
|
82
|
+
return error ? { ...base, state: 'failed', error } : { ...base, state: 'not_installed', error: null };
|
|
83
|
+
}
|
|
84
|
+
/** 已就绪直接返回;正在装就等同一次安装;否则开始安装。失败不抛错,返回 state = failed */
|
|
85
|
+
prepare(agent) {
|
|
86
|
+
if (this.isReady(agent))
|
|
87
|
+
return Promise.resolve(this.info(agent));
|
|
88
|
+
const running = this.installing.get(agent);
|
|
89
|
+
if (running)
|
|
90
|
+
return running;
|
|
91
|
+
this.lastError.delete(agent);
|
|
92
|
+
const job = this.install(agent).finally(() => {
|
|
93
|
+
this.installing.delete(agent);
|
|
94
|
+
this.options.onState?.(agent, this.info(agent));
|
|
95
|
+
});
|
|
96
|
+
this.installing.set(agent, job.then(() => this.info(agent)));
|
|
97
|
+
this.options.onState?.(agent, this.info(agent));
|
|
98
|
+
return this.installing.get(agent);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* 清掉之前被中途杀掉的安装留下的临时目录(可能有几百 MB;那次安装的 finally 没机会执行)。
|
|
102
|
+
* 只删「目录名里的 pid 已不在运行」的;pid 仍在(可能是另一个 bridge 正在装,或 pid 被复用)就不动,宁可多留。
|
|
103
|
+
*/
|
|
104
|
+
removeStaleTemps(spec) {
|
|
105
|
+
if (!existsSync(this.options.installRoot))
|
|
106
|
+
return;
|
|
107
|
+
const pattern = new RegExp(`^\\.tmp-${spec.dir}-(\\d+)-[0-9a-f]+$`);
|
|
108
|
+
for (const name of readdirSync(this.options.installRoot)) {
|
|
109
|
+
const pid = Number(pattern.exec(name)?.[1]);
|
|
110
|
+
if (!pid || isProcessAlive(pid))
|
|
111
|
+
continue;
|
|
112
|
+
rmSync(join(this.options.installRoot, name), { recursive: true, force: true });
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async install(agent) {
|
|
116
|
+
const spec = ADAPTERS[agent];
|
|
117
|
+
const finalDir = this.installDir(agent);
|
|
118
|
+
this.removeStaleTemps(spec);
|
|
119
|
+
const tmpDir = join(this.options.installRoot, `.tmp-${spec.dir}-${process.pid}-${randomBytes(4).toString('hex')}`);
|
|
120
|
+
try {
|
|
121
|
+
mkdirSync(tmpDir, { recursive: true });
|
|
122
|
+
for (const file of ['package.json', 'package-lock.json']) {
|
|
123
|
+
copyFileSync(join(this.options.sourceRoot, spec.dir, file), join(tmpDir, file));
|
|
124
|
+
}
|
|
125
|
+
const result = await this.options.runNpmCi(tmpDir);
|
|
126
|
+
if (result.code !== 0)
|
|
127
|
+
throw new Error(`npm ci failed (${result.timedOut ? 'timed out' : `exit ${String(result.code)}`}): ${tail(combinedOutput(result))}`);
|
|
128
|
+
if (!existsSync(join(tmpDir, spec.entry)))
|
|
129
|
+
throw new Error(`npm ci finished but ${spec.entry} is missing`);
|
|
130
|
+
writeFileSync(join(tmpDir, READY_MARKER), `${JSON.stringify({ package: spec.package, version: spec.version, installedAt: new Date().toISOString() })}\n`);
|
|
131
|
+
if (existsSync(finalDir)) {
|
|
132
|
+
// 另一个 bridge 先装好了(或残留了没有标记的旧目录):前者直接用,后者替换掉
|
|
133
|
+
if (this.isReady(agent))
|
|
134
|
+
return;
|
|
135
|
+
rmSync(finalDir, { recursive: true, force: true });
|
|
136
|
+
}
|
|
137
|
+
renameSync(tmpDir, finalDir);
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
this.lastError.set(agent, error instanceof Error ? error.message : String(error));
|
|
141
|
+
}
|
|
142
|
+
finally {
|
|
143
|
+
rmSync(tmpDir, { recursive: true, force: true });
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|