@introspection-ai/recipes 0.25.0 → 0.26.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/dist/mcp-chunks/{auth-command-4WFRNU2I.js → auth-command-JBYNHHMV.js} +23 -27
- package/dist/mcp-chunks/call-arguments-S4EGFKUQ.js +15 -0
- package/dist/mcp-chunks/{call-command-JBIKXOGC.js → call-command-467BVXTE.js} +32 -33
- package/dist/mcp-chunks/{chunk-IPNSPKQK.js → chunk-3Q2Q7SBU.js} +12 -14
- package/dist/mcp-chunks/chunk-66P5FXP3.js +31 -0
- package/dist/mcp-chunks/{chunk-TF2YTYE4.js → chunk-6DVS7RBO.js} +1 -1
- package/dist/mcp-chunks/{chunk-IME7DGLJ.js → chunk-AK5ZEDEF.js} +2 -2
- package/dist/mcp-chunks/{chunk-OP3IW2V6.js → chunk-AP5VHS73.js} +2 -2
- package/dist/mcp-chunks/{chunk-B2LNXGAP.js → chunk-AS27BTCS.js} +1 -1
- package/dist/mcp-chunks/{chunk-MOEZ43ZL.js → chunk-BC2HDCYV.js} +1 -1
- package/dist/mcp-chunks/{chunk-RSTKSBVF.js → chunk-BSCNF4NY.js} +2 -2
- package/dist/mcp-chunks/chunk-BTU34BUG.js +1182 -0
- package/dist/mcp-chunks/{chunk-HN7WPMCA.js → chunk-CN5YF3IQ.js} +21042 -3688
- package/dist/mcp-chunks/{chunk-G6OMDUNG.js → chunk-CZ43ZITK.js} +1 -1
- package/dist/mcp-chunks/{chunk-JG4TRUPF.js → chunk-D2P2IV4H.js} +1 -1
- package/dist/mcp-chunks/{chunk-3R2WK3NB.js → chunk-DMBE47LM.js} +6 -6
- package/dist/mcp-chunks/{chunk-WTMJMM3B.js → chunk-EP22XMWK.js} +3 -30
- package/dist/mcp-chunks/{chunk-ITCCROID.js → chunk-EUDC3ICZ.js} +5 -5
- package/dist/mcp-chunks/{chunk-FX5NYWBH.js → chunk-G4H535MI.js} +3 -21
- package/dist/mcp-chunks/{chunk-HCOKLSOB.js → chunk-GA4ZYSRX.js} +4 -4
- package/dist/mcp-chunks/{chunk-C2KNLFZR.js → chunk-GEVQ2GJW.js} +2 -2
- package/dist/mcp-chunks/{chunk-WYLHHXI6.js → chunk-GK4LA4E6.js} +26 -11
- package/dist/mcp-chunks/{chunk-QZUC2T72.js → chunk-GL4AA5QV.js} +2 -2
- package/dist/mcp-chunks/{chunk-KW3ANJ36.js → chunk-HBN3ESMH.js} +4 -4
- package/dist/mcp-chunks/{chunk-ELA77TDC.js → chunk-HKZZGSDV.js} +4 -4
- package/dist/mcp-chunks/{chunk-36VIJRYY.js → chunk-HTXQPQPB.js} +4 -4
- package/dist/mcp-chunks/{chunk-3JGS7LQB.js → chunk-I7JR2GIC.js} +2 -2
- package/dist/mcp-chunks/{chunk-AIKLVBYU.js → chunk-IO2IMWYS.js} +2 -2
- package/dist/mcp-chunks/{chunk-RD7NHCRK.js → chunk-J2WP6ERZ.js} +39 -42
- package/dist/mcp-chunks/{chunk-HLRCUHNO.js → chunk-KJNNO5Z6.js} +1 -1
- package/dist/mcp-chunks/{chunk-4ZNBWL65.js → chunk-L2LGMX5P.js} +2 -2
- package/dist/mcp-chunks/chunk-LG3GLOSA.js +513 -0
- package/dist/mcp-chunks/{chunk-CZKDJZER.js → chunk-LTR626QF.js} +1 -1
- package/dist/mcp-chunks/{chunk-FS5JIKT6.js → chunk-LYWEATXZ.js} +6 -6
- package/dist/mcp-chunks/{chunk-YIRGLKDA.js → chunk-M62ESEOY.js} +45 -14
- package/dist/mcp-chunks/{chunk-V5AYEKSL.js → chunk-MJHMXYMO.js} +52 -81
- package/dist/mcp-chunks/{chunk-7WOKCV6Q.js → chunk-N7ZWX3WW.js} +1 -1
- package/dist/mcp-chunks/chunk-NTABJCUL.js +123 -0
- package/dist/mcp-chunks/{chunk-EPT4MYOF.js → chunk-OABFKFSL.js} +11 -11
- package/dist/mcp-chunks/{chunk-SCOMDV54.js → chunk-OB256NMD.js} +1 -1
- package/dist/mcp-chunks/{chunk-IYWYVHHD.js → chunk-OZQTZWW4.js} +1 -1
- package/dist/mcp-chunks/{chunk-ITR7FQM3.js → chunk-QIBGY4IX.js} +2 -2
- package/dist/mcp-chunks/{chunk-ZOAQDFAO.js → chunk-REEFOL44.js} +1 -1
- package/dist/mcp-chunks/{chunk-ED2WN5BI.js → chunk-SUNOYTQJ.js} +3 -3
- package/dist/mcp-chunks/{chunk-Y3EDWN2P.js → chunk-TFU4NKFJ.js} +1 -1
- package/dist/mcp-chunks/{chunk-EMSNQHZZ.js → chunk-TGPIW6F6.js} +1 -1
- package/dist/mcp-chunks/{chunk-SNKXMWDB.js → chunk-UCJ7YDFN.js} +1 -1
- package/dist/mcp-chunks/{chunk-TPHPAEDH.js → chunk-WUEDIOR5.js} +1 -1
- package/dist/mcp-chunks/{chunk-FAJPJTOC.js → chunk-XCNWICLD.js} +1 -1
- package/dist/mcp-chunks/{chunk-6YZW3XAE.js → chunk-XFQSUC7T.js} +5 -5
- package/dist/mcp-chunks/{chunk-GQ6NSIDS.js → chunk-Y3L5LWBY.js} +3037 -747
- package/dist/mcp-chunks/{chunk-53NRFMB3.js → chunk-YI5SMRFH.js} +46 -25
- package/dist/mcp-chunks/{chunk-GUXMP7QB.js → chunk-YPTN46JQ.js} +2 -2
- package/dist/mcp-chunks/{chunk-I5GI2A2D.js → chunk-YVH36MGQ.js} +2 -2
- package/dist/mcp-chunks/{chunk-ASG4OY3M.js → chunk-Z6QEOUMK.js} +22 -23
- package/dist/mcp-chunks/{cli-XU26MUCN.js → cli-TN6JTLTS.js} +108 -77
- package/dist/mcp-chunks/client-G33HPQAC.js +29 -0
- package/dist/mcp-chunks/{config-command-UQMMIK2U.js → config-command-LICU56ZH.js} +27 -28
- package/dist/mcp-chunks/daemon-command-JLVHGWHB.js +29 -0
- package/dist/mcp-chunks/{dist-DM2RXZMU.js → dist-MFAWSKGH.js} +80 -46
- package/dist/mcp-chunks/{emit-ts-command-KAOL6DAN.js → emit-ts-command-FVX3JMSN.js} +18 -18
- package/dist/mcp-chunks/{generate-cli-runner-AK6XNRTK.js → generate-cli-runner-L2L7VMCA.js} +53 -51
- package/dist/mcp-chunks/generated-daemon-runtime-DC6IL7E7.js +29 -0
- package/dist/mcp-chunks/{inspect-cli-command-6FVJO7H2.js → inspect-cli-command-Y5D3GRE2.js} +9 -10
- package/dist/mcp-chunks/{launch-EIT73HX4.js → launch-ZFY6CM2A.js} +1 -1
- package/dist/mcp-chunks/{lifecycle-QH4TUI3K.js → lifecycle-H7DIW5DM.js} +1 -1
- package/dist/mcp-chunks/{list-command-UOB2XZK7.js → list-command-55LCLW4S.js} +34 -35
- package/dist/mcp-chunks/{output-utils-6YXH23T7.js → output-utils-YYHI637S.js} +3 -3
- package/dist/mcp-chunks/{prompt-CH6TK0bC-ZB6RFAXR.js → prompt-CH6TK0bC-DZLFAAPK.js} +1 -1
- package/dist/mcp-chunks/{record-command-FO5TTERC.js → record-command-PWIBYC5J.js} +5 -5
- package/dist/mcp-chunks/{replay-command-SCBPCGQ7.js → replay-command-QHZZZMXF.js} +6 -6
- package/dist/mcp-chunks/{resource-command-S2J7XEMV.js → resource-command-HMR2KXWK.js} +9 -10
- package/dist/mcp-chunks/{result-utils-OME6HGS6.js → result-utils-UGCQSRJ4.js} +3 -4
- package/dist/mcp-chunks/runtime-ZNFPZXKE.js +36 -0
- package/dist/mcp-chunks/runtime-wrapper-I4B3OROR.js +10 -0
- package/dist/mcp-chunks/{serve-command-DDBZZSG6.js → serve-command-NTDI3YPN.js} +29 -34
- package/dist/mcp-chunks/{timeouts-HNGNKDQN.js → timeouts-35UDWFA7.js} +3 -3
- package/dist/mcp-chunks/{vault-command-TJZYS3YI.js → vault-command-D5QZL4FI.js} +4 -6
- package/dist/mcp-daemon.js +26 -31
- package/dist/mcp-run-worker.js +26 -31
- package/dist/pi-extension.d.ts.map +1 -1
- package/dist/pi-extension.js +27 -0
- package/dist/pi-extension.js.map +1 -1
- package/docs/pi-extension.md +1 -1
- package/docs/recipe-format.md +7 -3
- package/docs/recipe-templates.md +46 -0
- package/package.json +14 -14
- package/dist/mcp-chunks/call-arguments-4DWPBLQP.js +0 -15
- package/dist/mcp-chunks/chunk-3Z7MFS6K.js +0 -86
- package/dist/mcp-chunks/chunk-AD3FQ73K.js +0 -39
- package/dist/mcp-chunks/chunk-BWW4XRUC.js +0 -1931
- package/dist/mcp-chunks/chunk-DTZHEVKC.js +0 -12
- package/dist/mcp-chunks/chunk-H3JCYHGS.js +0 -1018
- package/dist/mcp-chunks/chunk-IRTJXFWA.js +0 -28
- package/dist/mcp-chunks/chunk-NTHUNCXJ.js +0 -526
- package/dist/mcp-chunks/chunk-OD3MSMXY.js +0 -15
- package/dist/mcp-chunks/chunk-PZ323CXI.js +0 -17394
- package/dist/mcp-chunks/client-OT34CKRH.js +0 -21
- package/dist/mcp-chunks/daemon-command-YCIOZGPZ.js +0 -35
- package/dist/mcp-chunks/runtime-QIPMLIJI.js +0 -39
- package/dist/mcp-chunks/runtime-wrapper-X4YFKMJI.js +0 -16
|
@@ -1,43 +1,69 @@
|
|
|
1
1
|
import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
stableJsonStringify
|
|
4
|
+
} from "./chunk-AS27BTCS.js";
|
|
2
5
|
import {
|
|
3
6
|
NON_INTERACTIVE_ELICITATION_HINT,
|
|
4
7
|
createNonInteractiveElicitationResponder,
|
|
5
8
|
registerElicitationHandler
|
|
6
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-REEFOL44.js";
|
|
7
10
|
import {
|
|
8
11
|
RecordTransport,
|
|
9
12
|
resolveRecordingPath
|
|
10
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-AP5VHS73.js";
|
|
11
14
|
import {
|
|
12
15
|
OAuthTimeoutError,
|
|
13
16
|
connectWithAuth,
|
|
14
17
|
createOAuthSession,
|
|
15
18
|
isOAuthFlowError,
|
|
16
19
|
isPostAuthConnectError,
|
|
20
|
+
isUnauthorizedError,
|
|
21
|
+
maybeEnableOAuth,
|
|
17
22
|
resolveOAuthTimeoutFromEnv
|
|
18
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-M62ESEOY.js";
|
|
19
24
|
import {
|
|
20
25
|
readCachedAccessToken
|
|
21
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-Z6QEOUMK.js";
|
|
22
27
|
import {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
28
|
+
withRuntimeEnvironment
|
|
29
|
+
} from "./chunk-YI5SMRFH.js";
|
|
30
|
+
import {
|
|
31
|
+
createPrefixedConsoleLogger,
|
|
32
|
+
resolveLogLevelFromEnv
|
|
33
|
+
} from "./chunk-N7ZWX3WW.js";
|
|
26
34
|
import {
|
|
27
35
|
STDIO_TRACE_ENABLED,
|
|
28
36
|
flushStdioLogs,
|
|
29
37
|
ignoreStdioEmitterError
|
|
30
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-TGPIW6F6.js";
|
|
31
39
|
import {
|
|
32
40
|
MCPORTER_VERSION
|
|
33
|
-
} from "./chunk-
|
|
41
|
+
} from "./chunk-NTABJCUL.js";
|
|
34
42
|
import {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
43
|
+
normalizeTimeout,
|
|
44
|
+
raceWithTimeout,
|
|
45
|
+
resolveCommandArgument,
|
|
46
|
+
resolveCommandArguments
|
|
47
|
+
} from "./chunk-YVH36MGQ.js";
|
|
48
|
+
import {
|
|
49
|
+
loadServerDefinitions,
|
|
50
|
+
normalizeServerEntry,
|
|
51
|
+
parseTree,
|
|
52
|
+
readConfigFile
|
|
53
|
+
} from "./chunk-J2WP6ERZ.js";
|
|
54
|
+
import {
|
|
55
|
+
pathExists
|
|
56
|
+
} from "./chunk-G4H535MI.js";
|
|
57
|
+
import {
|
|
58
|
+
mcporterConfigCandidates
|
|
59
|
+
} from "./chunk-LYWEATXZ.js";
|
|
60
|
+
import {
|
|
61
|
+
resolveEnvPlaceholders,
|
|
62
|
+
resolveEnvValue
|
|
63
|
+
} from "./chunk-EP22XMWK.js";
|
|
38
64
|
import {
|
|
39
65
|
analyzeConnectionError
|
|
40
|
-
} from "./chunk-
|
|
66
|
+
} from "./chunk-GL4AA5QV.js";
|
|
41
67
|
import {
|
|
42
68
|
Client,
|
|
43
69
|
DEFAULT_REQUEST_TIMEOUT_MSEC,
|
|
@@ -50,28 +76,7 @@ import {
|
|
|
50
76
|
StreamableHTTPClientTransport,
|
|
51
77
|
serializeMessage,
|
|
52
78
|
specTypeSchemas
|
|
53
|
-
} from "./chunk-
|
|
54
|
-
import {
|
|
55
|
-
ChromeDevtoolsRelayRequiredError,
|
|
56
|
-
formatChromeDevtoolsRelayDecision,
|
|
57
|
-
recordChromeDevtoolsRelayDecision,
|
|
58
|
-
resolveChromeDevtoolsAutoConnectCommand,
|
|
59
|
-
resolveChromeDevtoolsRelayEnvironment,
|
|
60
|
-
rewriteChromeDevtoolsArgsForRelay
|
|
61
|
-
} from "./chunk-BWW4XRUC.js";
|
|
62
|
-
import {
|
|
63
|
-
normalizeTimeout,
|
|
64
|
-
raceWithTimeout,
|
|
65
|
-
resolveCommandArgument,
|
|
66
|
-
resolveCommandArguments
|
|
67
|
-
} from "./chunk-I5GI2A2D.js";
|
|
68
|
-
import {
|
|
69
|
-
loadServerDefinitions
|
|
70
|
-
} from "./chunk-RD7NHCRK.js";
|
|
71
|
-
import {
|
|
72
|
-
resolveEnvPlaceholders,
|
|
73
|
-
withEnvOverrides
|
|
74
|
-
} from "./chunk-WTMJMM3B.js";
|
|
79
|
+
} from "./chunk-CN5YF3IQ.js";
|
|
75
80
|
import {
|
|
76
81
|
__commonJS,
|
|
77
82
|
__require,
|
|
@@ -83,8 +88,8 @@ var require_windows = __commonJS({
|
|
|
83
88
|
"node_modules/.pnpm/isexe@2.0.0/node_modules/isexe/windows.js"(exports, module) {
|
|
84
89
|
module.exports = isexe;
|
|
85
90
|
isexe.sync = sync;
|
|
86
|
-
var
|
|
87
|
-
function checkPathExt(
|
|
91
|
+
var fs8 = __require("fs");
|
|
92
|
+
function checkPathExt(path9, options) {
|
|
88
93
|
var pathext = options.pathExt !== void 0 ? options.pathExt : process.env.PATHEXT;
|
|
89
94
|
if (!pathext) {
|
|
90
95
|
return true;
|
|
@@ -95,25 +100,25 @@ var require_windows = __commonJS({
|
|
|
95
100
|
}
|
|
96
101
|
for (var i = 0; i < pathext.length; i++) {
|
|
97
102
|
var p = pathext[i].toLowerCase();
|
|
98
|
-
if (p &&
|
|
103
|
+
if (p && path9.substr(-p.length).toLowerCase() === p) {
|
|
99
104
|
return true;
|
|
100
105
|
}
|
|
101
106
|
}
|
|
102
107
|
return false;
|
|
103
108
|
}
|
|
104
|
-
function checkStat(stat,
|
|
109
|
+
function checkStat(stat, path9, options) {
|
|
105
110
|
if (!stat.isSymbolicLink() && !stat.isFile()) {
|
|
106
111
|
return false;
|
|
107
112
|
}
|
|
108
|
-
return checkPathExt(
|
|
113
|
+
return checkPathExt(path9, options);
|
|
109
114
|
}
|
|
110
|
-
function isexe(
|
|
111
|
-
|
|
112
|
-
cb(er, er ? false : checkStat(stat,
|
|
115
|
+
function isexe(path9, options, cb) {
|
|
116
|
+
fs8.stat(path9, function(er, stat) {
|
|
117
|
+
cb(er, er ? false : checkStat(stat, path9, options));
|
|
113
118
|
});
|
|
114
119
|
}
|
|
115
|
-
function sync(
|
|
116
|
-
return checkStat(
|
|
120
|
+
function sync(path9, options) {
|
|
121
|
+
return checkStat(fs8.statSync(path9), path9, options);
|
|
117
122
|
}
|
|
118
123
|
}
|
|
119
124
|
});
|
|
@@ -123,14 +128,14 @@ var require_mode = __commonJS({
|
|
|
123
128
|
"node_modules/.pnpm/isexe@2.0.0/node_modules/isexe/mode.js"(exports, module) {
|
|
124
129
|
module.exports = isexe;
|
|
125
130
|
isexe.sync = sync;
|
|
126
|
-
var
|
|
127
|
-
function isexe(
|
|
128
|
-
|
|
131
|
+
var fs8 = __require("fs");
|
|
132
|
+
function isexe(path9, options, cb) {
|
|
133
|
+
fs8.stat(path9, function(er, stat) {
|
|
129
134
|
cb(er, er ? false : checkStat(stat, options));
|
|
130
135
|
});
|
|
131
136
|
}
|
|
132
|
-
function sync(
|
|
133
|
-
return checkStat(
|
|
137
|
+
function sync(path9, options) {
|
|
138
|
+
return checkStat(fs8.statSync(path9), options);
|
|
134
139
|
}
|
|
135
140
|
function checkStat(stat, options) {
|
|
136
141
|
return stat.isFile() && checkMode(stat, options);
|
|
@@ -154,7 +159,7 @@ var require_mode = __commonJS({
|
|
|
154
159
|
// node_modules/.pnpm/isexe@2.0.0/node_modules/isexe/index.js
|
|
155
160
|
var require_isexe = __commonJS({
|
|
156
161
|
"node_modules/.pnpm/isexe@2.0.0/node_modules/isexe/index.js"(exports, module) {
|
|
157
|
-
var
|
|
162
|
+
var fs8 = __require("fs");
|
|
158
163
|
var core;
|
|
159
164
|
if (process.platform === "win32" || global.TESTING_WINDOWS) {
|
|
160
165
|
core = require_windows();
|
|
@@ -163,7 +168,7 @@ var require_isexe = __commonJS({
|
|
|
163
168
|
}
|
|
164
169
|
module.exports = isexe;
|
|
165
170
|
isexe.sync = sync;
|
|
166
|
-
function isexe(
|
|
171
|
+
function isexe(path9, options, cb) {
|
|
167
172
|
if (typeof options === "function") {
|
|
168
173
|
cb = options;
|
|
169
174
|
options = {};
|
|
@@ -173,7 +178,7 @@ var require_isexe = __commonJS({
|
|
|
173
178
|
throw new TypeError("callback not provided");
|
|
174
179
|
}
|
|
175
180
|
return new Promise(function(resolve, reject) {
|
|
176
|
-
isexe(
|
|
181
|
+
isexe(path9, options || {}, function(er, is) {
|
|
177
182
|
if (er) {
|
|
178
183
|
reject(er);
|
|
179
184
|
} else {
|
|
@@ -182,7 +187,7 @@ var require_isexe = __commonJS({
|
|
|
182
187
|
});
|
|
183
188
|
});
|
|
184
189
|
}
|
|
185
|
-
core(
|
|
190
|
+
core(path9, options || {}, function(er, is) {
|
|
186
191
|
if (er) {
|
|
187
192
|
if (er.code === "EACCES" || options && options.ignoreErrors) {
|
|
188
193
|
er = null;
|
|
@@ -192,9 +197,9 @@ var require_isexe = __commonJS({
|
|
|
192
197
|
cb(er, is);
|
|
193
198
|
});
|
|
194
199
|
}
|
|
195
|
-
function sync(
|
|
200
|
+
function sync(path9, options) {
|
|
196
201
|
try {
|
|
197
|
-
return core.sync(
|
|
202
|
+
return core.sync(path9, options || {});
|
|
198
203
|
} catch (er) {
|
|
199
204
|
if (options && options.ignoreErrors || er.code === "EACCES") {
|
|
200
205
|
return false;
|
|
@@ -210,7 +215,7 @@ var require_isexe = __commonJS({
|
|
|
210
215
|
var require_which = __commonJS({
|
|
211
216
|
"node_modules/.pnpm/which@2.0.2/node_modules/which/which.js"(exports, module) {
|
|
212
217
|
var isWindows = process.platform === "win32" || process.env.OSTYPE === "cygwin" || process.env.OSTYPE === "msys";
|
|
213
|
-
var
|
|
218
|
+
var path9 = __require("path");
|
|
214
219
|
var COLON = isWindows ? ";" : ":";
|
|
215
220
|
var isexe = require_isexe();
|
|
216
221
|
var getNotFoundError = (cmd) => Object.assign(new Error(`not found: ${cmd}`), { code: "ENOENT" });
|
|
@@ -248,7 +253,7 @@ var require_which = __commonJS({
|
|
|
248
253
|
return opt.all && found.length ? resolve(found) : reject(getNotFoundError(cmd));
|
|
249
254
|
const ppRaw = pathEnv[i];
|
|
250
255
|
const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
|
|
251
|
-
const pCmd =
|
|
256
|
+
const pCmd = path9.join(pathPart, cmd);
|
|
252
257
|
const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
|
|
253
258
|
resolve(subStep(p, i, 0));
|
|
254
259
|
});
|
|
@@ -275,7 +280,7 @@ var require_which = __commonJS({
|
|
|
275
280
|
for (let i = 0; i < pathEnv.length; i++) {
|
|
276
281
|
const ppRaw = pathEnv[i];
|
|
277
282
|
const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
|
|
278
|
-
const pCmd =
|
|
283
|
+
const pCmd = path9.join(pathPart, cmd);
|
|
279
284
|
const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
|
|
280
285
|
for (let j = 0; j < pathExt.length; j++) {
|
|
281
286
|
const cur = p + pathExt[j];
|
|
@@ -323,7 +328,7 @@ var require_path_key = __commonJS({
|
|
|
323
328
|
var require_resolveCommand = __commonJS({
|
|
324
329
|
"node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/lib/util/resolveCommand.js"(exports, module) {
|
|
325
330
|
"use strict";
|
|
326
|
-
var
|
|
331
|
+
var path9 = __require("path");
|
|
327
332
|
var which = require_which();
|
|
328
333
|
var getPathKey = require_path_key();
|
|
329
334
|
function resolveCommandAttempt(parsed, withoutPathExt) {
|
|
@@ -341,7 +346,7 @@ var require_resolveCommand = __commonJS({
|
|
|
341
346
|
try {
|
|
342
347
|
resolved = which.sync(parsed.command, {
|
|
343
348
|
path: env[getPathKey({ env })],
|
|
344
|
-
pathExt: withoutPathExt ?
|
|
349
|
+
pathExt: withoutPathExt ? path9.delimiter : void 0
|
|
345
350
|
});
|
|
346
351
|
} catch (e) {
|
|
347
352
|
} finally {
|
|
@@ -350,7 +355,7 @@ var require_resolveCommand = __commonJS({
|
|
|
350
355
|
}
|
|
351
356
|
}
|
|
352
357
|
if (resolved) {
|
|
353
|
-
resolved =
|
|
358
|
+
resolved = path9.resolve(hasCustomCwd ? parsed.options.cwd : "", resolved);
|
|
354
359
|
}
|
|
355
360
|
return resolved;
|
|
356
361
|
}
|
|
@@ -404,8 +409,8 @@ var require_shebang_command = __commonJS({
|
|
|
404
409
|
if (!match) {
|
|
405
410
|
return null;
|
|
406
411
|
}
|
|
407
|
-
const [
|
|
408
|
-
const binary =
|
|
412
|
+
const [path9, argument] = match[0].replace(/#! ?/, "").split(" ");
|
|
413
|
+
const binary = path9.split("/").pop();
|
|
409
414
|
if (binary === "env") {
|
|
410
415
|
return argument;
|
|
411
416
|
}
|
|
@@ -418,16 +423,16 @@ var require_shebang_command = __commonJS({
|
|
|
418
423
|
var require_readShebang = __commonJS({
|
|
419
424
|
"node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/lib/util/readShebang.js"(exports, module) {
|
|
420
425
|
"use strict";
|
|
421
|
-
var
|
|
426
|
+
var fs8 = __require("fs");
|
|
422
427
|
var shebangCommand = require_shebang_command();
|
|
423
428
|
function readShebang(command) {
|
|
424
429
|
const size = 150;
|
|
425
430
|
const buffer = Buffer.alloc(size);
|
|
426
431
|
let fd;
|
|
427
432
|
try {
|
|
428
|
-
fd =
|
|
429
|
-
|
|
430
|
-
|
|
433
|
+
fd = fs8.openSync(command, "r");
|
|
434
|
+
fs8.readSync(fd, buffer, 0, size, 0);
|
|
435
|
+
fs8.closeSync(fd);
|
|
431
436
|
} catch (e) {
|
|
432
437
|
}
|
|
433
438
|
return shebangCommand(buffer.toString());
|
|
@@ -440,7 +445,7 @@ var require_readShebang = __commonJS({
|
|
|
440
445
|
var require_parse = __commonJS({
|
|
441
446
|
"node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/lib/parse.js"(exports, module) {
|
|
442
447
|
"use strict";
|
|
443
|
-
var
|
|
448
|
+
var path9 = __require("path");
|
|
444
449
|
var resolveCommand = require_resolveCommand();
|
|
445
450
|
var escape = require_escape();
|
|
446
451
|
var readShebang = require_readShebang();
|
|
@@ -465,7 +470,7 @@ var require_parse = __commonJS({
|
|
|
465
470
|
const needsShell = !isExecutableRegExp.test(commandFile);
|
|
466
471
|
if (parsed.options.forceShell || needsShell) {
|
|
467
472
|
const needsDoubleEscapeMetaChars = isCmdShimRegExp.test(commandFile);
|
|
468
|
-
parsed.command =
|
|
473
|
+
parsed.command = path9.normalize(parsed.command);
|
|
469
474
|
parsed.command = escape.command(parsed.command);
|
|
470
475
|
parsed.args = parsed.args.map((arg) => escape.argument(arg, needsDoubleEscapeMetaChars));
|
|
471
476
|
const shellCommand = [parsed.command].concat(parsed.args).join(" ");
|
|
@@ -555,7 +560,7 @@ var require_cross_spawn = __commonJS({
|
|
|
555
560
|
var cp = __require("child_process");
|
|
556
561
|
var parse = require_parse();
|
|
557
562
|
var enoent = require_enoent();
|
|
558
|
-
function
|
|
563
|
+
function spawn3(command, args, options) {
|
|
559
564
|
const parsed = parse(command, args, options);
|
|
560
565
|
const spawned = cp.spawn(parsed.command, parsed.args, parsed.options);
|
|
561
566
|
enoent.hookChildProcess(spawned, parsed);
|
|
@@ -567,178 +572,2651 @@ var require_cross_spawn = __commonJS({
|
|
|
567
572
|
result.error = result.error || enoent.verifyENOENTSync(result.status, parsed);
|
|
568
573
|
return result;
|
|
569
574
|
}
|
|
570
|
-
module.exports =
|
|
571
|
-
module.exports.spawn =
|
|
575
|
+
module.exports = spawn3;
|
|
576
|
+
module.exports.spawn = spawn3;
|
|
572
577
|
module.exports.sync = spawnSync2;
|
|
573
578
|
module.exports._parse = parse;
|
|
574
579
|
module.exports._enoent = enoent;
|
|
575
580
|
}
|
|
576
581
|
});
|
|
577
582
|
|
|
578
|
-
// node_modules/.pnpm/mcporter@0.13.
|
|
579
|
-
import {
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
583
|
+
// node_modules/.pnpm/mcporter@0.13.10/node_modules/mcporter/dist/daemon/connection-identity.js
|
|
584
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
585
|
+
import fs2 from "node:fs/promises";
|
|
586
|
+
import path3 from "node:path";
|
|
587
|
+
|
|
588
|
+
// node_modules/.pnpm/mcporter@0.13.10/node_modules/mcporter/dist/chrome-devtools-relay.js
|
|
589
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
590
|
+
import fs from "node:fs";
|
|
591
|
+
import os from "node:os";
|
|
592
|
+
import path2 from "node:path";
|
|
593
|
+
|
|
594
|
+
// node_modules/.pnpm/mcporter@0.13.10/node_modules/mcporter/dist/chrome-devtools-command.js
|
|
595
|
+
var AUTO_CONNECT_FLAGS = /* @__PURE__ */ new Set(["--autoConnect", "--auto-connect"]);
|
|
596
|
+
var NEGATED_AUTO_CONNECT_FLAGS = /* @__PURE__ */ new Set(["--no-autoConnect", "--no-auto-connect"]);
|
|
597
|
+
var CONNECTION_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
598
|
+
"--browserUrl",
|
|
599
|
+
"--browser-url",
|
|
600
|
+
"--channel",
|
|
601
|
+
"--executablePath",
|
|
602
|
+
"--executable-path",
|
|
603
|
+
"--userDataDir",
|
|
604
|
+
"--user-data-dir",
|
|
605
|
+
"--wsEndpoint",
|
|
606
|
+
"--ws-endpoint",
|
|
607
|
+
"--wsHeaders",
|
|
608
|
+
"--ws-headers",
|
|
609
|
+
"-e",
|
|
610
|
+
"-u",
|
|
611
|
+
"-w"
|
|
612
|
+
]);
|
|
613
|
+
var NPX_VALUE_OPTIONS = /* @__PURE__ */ new Set([
|
|
614
|
+
"--cache",
|
|
615
|
+
"--package",
|
|
616
|
+
"--prefix",
|
|
617
|
+
"--registry",
|
|
618
|
+
"--userconfig",
|
|
619
|
+
"--workspace",
|
|
620
|
+
"-p",
|
|
621
|
+
"-w"
|
|
622
|
+
]);
|
|
623
|
+
var NPX_COMMAND_STRING_OPTIONS = /* @__PURE__ */ new Set(["--call", "-c"]);
|
|
624
|
+
var NPX_NO_VALUE_OPTIONS = /* @__PURE__ */ new Set(["--ignore-existing", "--no-install", "--quiet", "--silent", "--yes", "-y"]);
|
|
625
|
+
var COMMON_WRAPPERS = /* @__PURE__ */ new Set([
|
|
626
|
+
"ash",
|
|
627
|
+
"bash",
|
|
628
|
+
"csh",
|
|
629
|
+
"dash",
|
|
630
|
+
"cmd",
|
|
631
|
+
"doas",
|
|
632
|
+
"env",
|
|
633
|
+
"fish",
|
|
634
|
+
"ksh",
|
|
635
|
+
"mksh",
|
|
636
|
+
"node",
|
|
637
|
+
"npm",
|
|
638
|
+
"npx",
|
|
639
|
+
"bunx",
|
|
640
|
+
"pnpm",
|
|
641
|
+
"powershell",
|
|
642
|
+
"pwsh",
|
|
643
|
+
"sh",
|
|
644
|
+
"sudo",
|
|
645
|
+
"tcsh",
|
|
646
|
+
"yarn",
|
|
647
|
+
"zsh"
|
|
648
|
+
]);
|
|
649
|
+
function isChromeDevtoolsToken(token) {
|
|
650
|
+
const normalized = token.replaceAll("\\", "/").toLowerCase();
|
|
651
|
+
const basename = normalized.slice(normalized.lastIndexOf("/") + 1);
|
|
652
|
+
return /^chrome-devtools-mcp(?:@[^/]+)?(?:\.(?:bat|cjs|cmd|exe|js|mjs))?$/u.test(basename) || normalized.includes("/chrome-devtools-mcp/") && /\.(?:bat|cjs|cmd|exe|js|mjs)$/u.test(basename);
|
|
653
|
+
}
|
|
654
|
+
function isAmbiguousChromeDevtoolsAutoConnectCommand(command, args) {
|
|
655
|
+
if (isChromeDevtoolsToken(command))
|
|
656
|
+
return false;
|
|
657
|
+
if (isSupportedPackageLauncher(command)) {
|
|
658
|
+
const target = findPackageLauncherTarget(args);
|
|
659
|
+
if (!target)
|
|
660
|
+
return false;
|
|
661
|
+
if (target.ambiguous)
|
|
662
|
+
return true;
|
|
663
|
+
const nestedTarget = commandBasename(args[target.index] ?? "");
|
|
664
|
+
return COMMON_WRAPPERS.has(nestedTarget) && containsChromeAutoConnectInvocation(args.slice(target.index + 1));
|
|
665
|
+
}
|
|
666
|
+
const launcher = commandBasename(command);
|
|
667
|
+
if (COMMON_WRAPPERS.has(launcher))
|
|
668
|
+
return containsChromeAutoConnectInvocation(args);
|
|
669
|
+
const scopedArgs = args.slice(0, optionTerminator(args));
|
|
670
|
+
const tokenIndex = scopedArgs.findIndex(isChromeDevtoolsToken);
|
|
671
|
+
return tokenIndex >= 0 && parseChromeDevtoolsAutoConnectArgs(scopedArgs.slice(tokenIndex + 1)).enabled;
|
|
672
|
+
}
|
|
673
|
+
function resolveChromeDevtoolsAutoConnectCommand(command, args) {
|
|
674
|
+
const target = resolveChromeDevtoolsTarget(command, args);
|
|
675
|
+
if (!target)
|
|
676
|
+
return { enabled: false, withoutAutoConnect: args };
|
|
677
|
+
const parsed = parseChromeDevtoolsAutoConnectArgs(target.args);
|
|
678
|
+
return {
|
|
679
|
+
enabled: parsed.enabled,
|
|
680
|
+
withoutAutoConnect: [...target.prefix, ...parsed.withoutAutoConnect]
|
|
681
|
+
};
|
|
682
|
+
}
|
|
683
|
+
function replaceChromeDevtoolsAutoConnectArgs(command, args, replacement) {
|
|
684
|
+
const target = resolveChromeDevtoolsTarget(command, args);
|
|
685
|
+
if (!target)
|
|
686
|
+
return args;
|
|
687
|
+
const parsed = parseChromeDevtoolsAutoConnectArgs(target.args);
|
|
688
|
+
const stripped = stripConnectionSelectionArgs(parsed.withoutAutoConnect);
|
|
689
|
+
const targetArgs = [...stripped.args];
|
|
690
|
+
targetArgs.splice(stripped.insertionIndex, 0, ...replacement);
|
|
691
|
+
return [...target.prefix, ...targetArgs];
|
|
692
|
+
}
|
|
693
|
+
function resolveChromeDevtoolsTarget(command, args) {
|
|
694
|
+
if (isChromeDevtoolsToken(command))
|
|
695
|
+
return { prefix: [], args };
|
|
696
|
+
if (!isSupportedPackageLauncher(command))
|
|
697
|
+
return void 0;
|
|
698
|
+
const target = findPackageLauncherTarget(args);
|
|
699
|
+
if (!target || target.ambiguous || !isChromeDevtoolsToken(args[target.index] ?? ""))
|
|
700
|
+
return void 0;
|
|
701
|
+
const separatorIndex = target.index + 1;
|
|
702
|
+
const consumesSeparator = target.consumePostTargetSeparator && args[separatorIndex] === "--";
|
|
703
|
+
const targetStart = consumesSeparator ? separatorIndex + 1 : separatorIndex;
|
|
704
|
+
return { prefix: args.slice(0, targetStart), args: args.slice(targetStart) };
|
|
705
|
+
}
|
|
706
|
+
function findPackageLauncherTarget(args) {
|
|
707
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
708
|
+
const arg = args[index];
|
|
709
|
+
if (arg === void 0)
|
|
710
|
+
continue;
|
|
711
|
+
if (arg === "--") {
|
|
712
|
+
return index + 1 < args.length ? { index: index + 1, ambiguous: false, consumePostTargetSeparator: false } : void 0;
|
|
589
713
|
}
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
if (pidBeforeClose) {
|
|
594
|
-
destroyTransportStreams(transport);
|
|
595
|
-
await ensureProcessTreeTerminated(logger, pidBeforeClose, targetsBeforeClose);
|
|
596
|
-
if (!closeSettled) {
|
|
597
|
-
await Promise.race([closePromise, delay(100)]);
|
|
714
|
+
if (arg.startsWith("--call=")) {
|
|
715
|
+
const value = arg.slice("--call=".length);
|
|
716
|
+
return containsInlineChromeAutoConnect([value]) ? { index, ambiguous: true, consumePostTargetSeparator: false } : void 0;
|
|
598
717
|
}
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
if (
|
|
610
|
-
return
|
|
718
|
+
if (NPX_COMMAND_STRING_OPTIONS.has(arg)) {
|
|
719
|
+
const value = args[index + 1] ?? "";
|
|
720
|
+
return containsInlineChromeAutoConnect([value]) ? { index: index + 1, ambiguous: true, consumePostTargetSeparator: false } : void 0;
|
|
721
|
+
}
|
|
722
|
+
if (NPX_VALUE_OPTIONS.has(arg)) {
|
|
723
|
+
index += 1;
|
|
724
|
+
continue;
|
|
725
|
+
}
|
|
726
|
+
if (NPX_NO_VALUE_OPTIONS.has(arg) || /^--[^=]+=.+/u.test(arg))
|
|
727
|
+
continue;
|
|
728
|
+
if (arg.startsWith("-")) {
|
|
729
|
+
return {
|
|
730
|
+
index,
|
|
731
|
+
ambiguous: containsChromeAutoConnectInvocation(args.slice(index)),
|
|
732
|
+
consumePostTargetSeparator: false
|
|
733
|
+
};
|
|
611
734
|
}
|
|
735
|
+
return { index, ambiguous: false, consumePostTargetSeparator: true };
|
|
612
736
|
}
|
|
613
|
-
return
|
|
737
|
+
return void 0;
|
|
614
738
|
}
|
|
615
|
-
function
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
return true;
|
|
619
|
-
} catch {
|
|
620
|
-
return false;
|
|
621
|
-
}
|
|
739
|
+
function isSupportedPackageLauncher(command) {
|
|
740
|
+
const launcher = commandBasename(command);
|
|
741
|
+
return launcher === "npx" || launcher === "bunx";
|
|
622
742
|
}
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
if (
|
|
626
|
-
return;
|
|
627
|
-
|
|
628
|
-
await sendSignalToTargets(targets, "SIGTERM");
|
|
629
|
-
targets = uniquePids([...targets, ...await collectProcessTreePids(rootPid)]);
|
|
630
|
-
if (await waitForTreeExit(targets, 700)) {
|
|
631
|
-
return;
|
|
632
|
-
}
|
|
633
|
-
targets = uniquePids([...targets, ...await collectProcessTreePids(rootPid)]);
|
|
634
|
-
await sendSignalToTargets(targets, "SIGKILL");
|
|
635
|
-
if (await waitForTreeExit(targets, 500)) {
|
|
636
|
-
return;
|
|
637
|
-
}
|
|
638
|
-
logger.warn(`Process tree rooted at pid=${rootPid} did not exit after SIGKILL.`);
|
|
743
|
+
function containsChromeAutoConnectInvocation(args) {
|
|
744
|
+
const tokenIndex = args.findIndex(isChromeDevtoolsToken);
|
|
745
|
+
if (tokenIndex >= 0)
|
|
746
|
+
return parseChromeDevtoolsAutoConnectArgs(args.slice(tokenIndex + 1)).enabled;
|
|
747
|
+
return containsInlineChromeAutoConnect(args);
|
|
639
748
|
}
|
|
640
|
-
function
|
|
641
|
-
|
|
749
|
+
function containsInlineChromeAutoConnect(args) {
|
|
750
|
+
const rendered = args.join(" ").toLowerCase();
|
|
751
|
+
return rendered.includes("chrome-devtools-mcp") && /--(?:auto-connect|autoconnect)(?:=true)?(?:[;'"&|)\]}]|\s|$)/u.test(rendered);
|
|
642
752
|
}
|
|
643
|
-
function
|
|
644
|
-
const
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
753
|
+
function commandBasename(command) {
|
|
754
|
+
const normalized = command.replaceAll("\\", "/").toLowerCase();
|
|
755
|
+
return normalized.slice(normalized.lastIndexOf("/") + 1).replace(/\.(?:cmd|exe)$/u, "");
|
|
756
|
+
}
|
|
757
|
+
function optionTerminator(args) {
|
|
758
|
+
const index = args.indexOf("--");
|
|
759
|
+
return index >= 0 ? index : args.length;
|
|
760
|
+
}
|
|
761
|
+
function stripConnectionSelectionArgs(args) {
|
|
762
|
+
const result = [];
|
|
763
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
764
|
+
const arg = args[index];
|
|
765
|
+
if (arg === void 0)
|
|
766
|
+
continue;
|
|
767
|
+
if (arg === "--") {
|
|
768
|
+
result.push(...args.slice(index));
|
|
769
|
+
break;
|
|
650
770
|
}
|
|
771
|
+
if (CONNECTION_VALUE_FLAGS.has(arg)) {
|
|
772
|
+
index += 1;
|
|
773
|
+
continue;
|
|
774
|
+
}
|
|
775
|
+
if ([...CONNECTION_VALUE_FLAGS].some((flag) => arg.startsWith(`${flag}=`)))
|
|
776
|
+
continue;
|
|
777
|
+
result.push(arg);
|
|
651
778
|
}
|
|
779
|
+
const terminatorIndex = result.indexOf("--");
|
|
780
|
+
return { args: result, insertionIndex: terminatorIndex >= 0 ? terminatorIndex : result.length };
|
|
652
781
|
}
|
|
653
|
-
|
|
654
|
-
const
|
|
655
|
-
|
|
656
|
-
|
|
782
|
+
function parseChromeDevtoolsAutoConnectArgs(args) {
|
|
783
|
+
const consumed = /* @__PURE__ */ new Set();
|
|
784
|
+
let enabled = false;
|
|
785
|
+
let sawOption = false;
|
|
786
|
+
let terminatorIndex = args.length;
|
|
787
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
788
|
+
const arg = args[index];
|
|
789
|
+
if (arg === void 0)
|
|
790
|
+
continue;
|
|
791
|
+
if (arg === "--") {
|
|
792
|
+
terminatorIndex = index;
|
|
793
|
+
break;
|
|
794
|
+
}
|
|
795
|
+
if (NEGATED_AUTO_CONNECT_FLAGS.has(arg)) {
|
|
796
|
+
sawOption = true;
|
|
797
|
+
enabled = false;
|
|
798
|
+
consumed.add(index);
|
|
657
799
|
continue;
|
|
658
800
|
}
|
|
659
|
-
|
|
660
|
-
|
|
801
|
+
let matchedNegatedInline = false;
|
|
802
|
+
for (const flag of NEGATED_AUTO_CONNECT_FLAGS) {
|
|
803
|
+
if (!arg.startsWith(`${flag}=`))
|
|
804
|
+
continue;
|
|
805
|
+
sawOption = true;
|
|
806
|
+
enabled = false;
|
|
807
|
+
consumed.add(index);
|
|
808
|
+
matchedNegatedInline = true;
|
|
809
|
+
break;
|
|
810
|
+
}
|
|
811
|
+
if (matchedNegatedInline)
|
|
812
|
+
continue;
|
|
813
|
+
if (AUTO_CONNECT_FLAGS.has(arg)) {
|
|
814
|
+
sawOption = true;
|
|
815
|
+
consumed.add(index);
|
|
816
|
+
const pairedValue = booleanLiteral(args[index + 1]);
|
|
817
|
+
if (pairedValue !== void 0) {
|
|
818
|
+
enabled = pairedValue;
|
|
819
|
+
consumed.add(index + 1);
|
|
820
|
+
index += 1;
|
|
821
|
+
} else {
|
|
822
|
+
enabled = true;
|
|
823
|
+
}
|
|
824
|
+
continue;
|
|
825
|
+
}
|
|
826
|
+
for (const flag of AUTO_CONNECT_FLAGS) {
|
|
827
|
+
if (!arg.startsWith(`${flag}=`))
|
|
828
|
+
continue;
|
|
829
|
+
sawOption = true;
|
|
830
|
+
enabled = arg.slice(flag.length + 1) === "true";
|
|
831
|
+
consumed.add(index);
|
|
832
|
+
break;
|
|
833
|
+
}
|
|
661
834
|
}
|
|
835
|
+
const withoutAutoConnect = args.filter((_arg, index) => !consumed.has(index));
|
|
836
|
+
let insertionIndex = 0;
|
|
837
|
+
for (let index = 0; index < terminatorIndex; index += 1) {
|
|
838
|
+
if (!consumed.has(index))
|
|
839
|
+
insertionIndex += 1;
|
|
840
|
+
}
|
|
841
|
+
return { enabled: sawOption && enabled, withoutAutoConnect, insertionIndex };
|
|
662
842
|
}
|
|
663
|
-
function
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
843
|
+
function booleanLiteral(value) {
|
|
844
|
+
if (value === "true")
|
|
845
|
+
return true;
|
|
846
|
+
if (value === "false")
|
|
847
|
+
return false;
|
|
848
|
+
return void 0;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
// node_modules/.pnpm/mcporter@0.13.10/node_modules/mcporter/dist/chrome-devtools-relay-proxy.js
|
|
852
|
+
import { createHash, randomBytes, timingSafeEqual } from "node:crypto";
|
|
853
|
+
import http, {} from "node:http";
|
|
854
|
+
var WEBSOCKET_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
|
855
|
+
async function startChromeDevtoolsRelayProxy(options) {
|
|
856
|
+
const sockets = /* @__PURE__ */ new Set();
|
|
857
|
+
const clientAuthorization = `Bearer ${randomBytes(32).toString("base64url")}`;
|
|
858
|
+
let downstreamAccepted = false;
|
|
859
|
+
let closed = false;
|
|
860
|
+
const server = http.createServer((_request, response) => {
|
|
861
|
+
response.writeHead(404, { Connection: "close", "Content-Length": "0" }).end();
|
|
862
|
+
});
|
|
863
|
+
server.on("connection", (socket) => trackSocket(sockets, socket));
|
|
864
|
+
server.on("upgrade", (request, downstream, head) => {
|
|
865
|
+
if (closed || downstreamAccepted || !isValidDownstreamUpgrade(request, clientAuthorization) || options.upstream.socket.destroyed) {
|
|
866
|
+
downstream.end("HTTP/1.1 404 Not Found\r\nConnection: close\r\nContent-Length: 0\r\n\r\n");
|
|
867
|
+
return;
|
|
868
|
+
}
|
|
869
|
+
downstreamAccepted = true;
|
|
870
|
+
const key = request.headers["sec-websocket-key"];
|
|
871
|
+
if (typeof key !== "string") {
|
|
872
|
+
downstream.destroy();
|
|
668
873
|
return;
|
|
669
874
|
}
|
|
875
|
+
const accept = createHash("sha1").update(`${key}${WEBSOCKET_GUID}`).digest("base64");
|
|
876
|
+
downstream.write([
|
|
877
|
+
"HTTP/1.1 101 Switching Protocols",
|
|
878
|
+
"Connection: Upgrade",
|
|
879
|
+
"Upgrade: websocket",
|
|
880
|
+
`Sec-WebSocket-Accept: ${accept}`,
|
|
881
|
+
"",
|
|
882
|
+
""
|
|
883
|
+
].join("\r\n"));
|
|
884
|
+
if (options.upstream.head.length > 0)
|
|
885
|
+
downstream.write(options.upstream.head);
|
|
886
|
+
if (head.length > 0)
|
|
887
|
+
options.upstream.socket.write(head);
|
|
888
|
+
options.upstream.socket.once("error", () => downstream.destroy());
|
|
889
|
+
options.upstream.socket.once("close", () => downstream.destroy());
|
|
890
|
+
downstream.once("error", () => options.upstream.socket.destroy());
|
|
891
|
+
downstream.once("close", () => options.upstream.socket.destroy());
|
|
892
|
+
downstream.pipe(options.upstream.socket).pipe(downstream);
|
|
893
|
+
server.close();
|
|
894
|
+
});
|
|
895
|
+
const onUpstreamClose = () => {
|
|
896
|
+
void closeServer(server, sockets);
|
|
897
|
+
};
|
|
898
|
+
options.upstream.socket.once("close", onUpstreamClose);
|
|
899
|
+
try {
|
|
900
|
+
await new Promise((resolve, reject) => {
|
|
901
|
+
const onError = (error) => reject(error);
|
|
902
|
+
server.once("error", onError);
|
|
903
|
+
server.listen(0, "127.0.0.1", () => {
|
|
904
|
+
server.off("error", onError);
|
|
905
|
+
resolve();
|
|
906
|
+
});
|
|
907
|
+
});
|
|
908
|
+
} catch (error) {
|
|
909
|
+
closeUpstream(options.upstream.socket);
|
|
670
910
|
throw error;
|
|
671
911
|
}
|
|
912
|
+
const address = server.address();
|
|
913
|
+
if (!address || address.address !== "127.0.0.1") {
|
|
914
|
+
closeUpstream(options.upstream.socket);
|
|
915
|
+
server.close();
|
|
916
|
+
throw new Error("Chrome relay proxy failed to bind to IPv4 loopback.");
|
|
917
|
+
}
|
|
918
|
+
let authorizationAvailable = true;
|
|
919
|
+
return {
|
|
920
|
+
endpoint: `ws://127.0.0.1:${address.port}/cdp`,
|
|
921
|
+
consumeClientAuthorization() {
|
|
922
|
+
if (!authorizationAvailable)
|
|
923
|
+
throw new Error("Chrome relay authorization handoff already consumed.");
|
|
924
|
+
authorizationAvailable = false;
|
|
925
|
+
return clientAuthorization;
|
|
926
|
+
},
|
|
927
|
+
async close() {
|
|
928
|
+
if (closed)
|
|
929
|
+
return;
|
|
930
|
+
closed = true;
|
|
931
|
+
options.upstream.socket.off("close", onUpstreamClose);
|
|
932
|
+
closeUpstream(options.upstream.socket);
|
|
933
|
+
await closeServer(server, sockets);
|
|
934
|
+
}
|
|
935
|
+
};
|
|
672
936
|
}
|
|
673
|
-
|
|
674
|
-
if (!
|
|
675
|
-
return
|
|
937
|
+
function isValidDownstreamUpgrade(request, expectedAuthorization) {
|
|
938
|
+
if (request.url !== "/cdp" || request.method !== "GET" || request.headers.upgrade?.toLowerCase() !== "websocket" || !hasToken(request.headers.connection, "upgrade") || request.headers["sec-websocket-version"] !== "13" || countRawHeader(request, "authorization") !== 1 || countRawHeader(request, "sec-websocket-key") !== 1 || !safeHeaderEqual(request.headers.authorization, expectedAuthorization)) {
|
|
939
|
+
return false;
|
|
676
940
|
}
|
|
677
|
-
|
|
678
|
-
|
|
941
|
+
const key = request.headers["sec-websocket-key"];
|
|
942
|
+
if (typeof key !== "string" || !/^[A-Za-z0-9+/]{22}==$/u.test(key))
|
|
943
|
+
return false;
|
|
944
|
+
const decoded = Buffer.from(key, "base64");
|
|
945
|
+
return decoded.byteLength === 16 && decoded.toString("base64") === key;
|
|
946
|
+
}
|
|
947
|
+
function countRawHeader(request, expectedName) {
|
|
948
|
+
let count = 0;
|
|
949
|
+
for (let index = 0; index < request.rawHeaders.length; index += 2) {
|
|
950
|
+
if (request.rawHeaders[index]?.toLowerCase() === expectedName)
|
|
951
|
+
count += 1;
|
|
952
|
+
}
|
|
953
|
+
return count;
|
|
954
|
+
}
|
|
955
|
+
function safeHeaderEqual(actual, expected) {
|
|
956
|
+
if (!actual)
|
|
957
|
+
return false;
|
|
958
|
+
const actualBytes = Buffer.from(actual);
|
|
959
|
+
const expectedBytes = Buffer.from(expected);
|
|
960
|
+
return actualBytes.length === expectedBytes.length && timingSafeEqual(actualBytes, expectedBytes);
|
|
961
|
+
}
|
|
962
|
+
function hasToken(value, expected) {
|
|
963
|
+
return Boolean(value?.split(",").some((token) => token.trim().toLowerCase() === expected));
|
|
964
|
+
}
|
|
965
|
+
function trackSocket(sockets, socket) {
|
|
966
|
+
if (sockets.has(socket))
|
|
967
|
+
return;
|
|
968
|
+
sockets.add(socket);
|
|
969
|
+
socket.once("close", () => sockets.delete(socket));
|
|
970
|
+
}
|
|
971
|
+
function closeUpstream(socket) {
|
|
972
|
+
if (socket.destroyed)
|
|
973
|
+
return;
|
|
974
|
+
if (!socket.remoteAddress) {
|
|
975
|
+
socket.destroy();
|
|
976
|
+
return;
|
|
679
977
|
}
|
|
680
978
|
try {
|
|
681
|
-
|
|
682
|
-
const children = /* @__PURE__ */ new Map();
|
|
683
|
-
for (const line of stdout.split("\n")) {
|
|
684
|
-
const trimmed = line.trim();
|
|
685
|
-
if (!trimmed) {
|
|
686
|
-
continue;
|
|
687
|
-
}
|
|
688
|
-
const [pidText, ppidText] = trimmed.split(/\s+/, 2);
|
|
689
|
-
const pid = Number.parseInt(pidText ?? "", 10);
|
|
690
|
-
const ppid = Number.parseInt(ppidText ?? "", 10);
|
|
691
|
-
if (!Number.isFinite(pid) || !Number.isFinite(ppid)) {
|
|
692
|
-
continue;
|
|
693
|
-
}
|
|
694
|
-
const bucket = children.get(ppid) ?? [];
|
|
695
|
-
bucket.push(pid);
|
|
696
|
-
children.set(ppid, bucket);
|
|
697
|
-
}
|
|
698
|
-
return collectDescendantsFromChildren(rootPid, children);
|
|
979
|
+
socket.resetAndDestroy();
|
|
699
980
|
} catch {
|
|
700
|
-
|
|
981
|
+
socket.destroy();
|
|
701
982
|
}
|
|
702
983
|
}
|
|
703
|
-
async function
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
984
|
+
async function closeServer(server, sockets) {
|
|
985
|
+
for (const socket of sockets)
|
|
986
|
+
socket.destroy();
|
|
987
|
+
if (!server.listening)
|
|
988
|
+
return;
|
|
989
|
+
await new Promise((resolve) => server.close(() => resolve()));
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
// node_modules/.pnpm/mcporter@0.13.10/node_modules/mcporter/dist/browser-relay-auth-v2.js
|
|
993
|
+
import { createHmac, hash, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
|
|
994
|
+
var BROWSER_RELAY_AUTH_LABEL = "openclaw.browser-relay.auth";
|
|
995
|
+
var BROWSER_RELAY_AUTH_VERSION = 2;
|
|
996
|
+
var BROWSER_RELAY_CHALLENGE_MAX_LIFETIME_MS = 1e4;
|
|
997
|
+
var BROWSER_RELAY_CLOCK_SKEW_MS = 3e4;
|
|
998
|
+
var BROWSER_RELAY_AUTH_CHALLENGE_PATH = "/_openclaw/relay/auth/v2/challenge";
|
|
999
|
+
var BROWSER_RELAY_AUTH_COMPLETE_PATH = "/_openclaw/relay/auth/v2/complete";
|
|
1000
|
+
var BROWSER_RELAY_CDP_PATH = "/cdp";
|
|
1001
|
+
var BROWSER_RELAY_CDP_ROLE = "cdp";
|
|
1002
|
+
var BROWSER_RELAY_CDP_TRANSPORT = "connection";
|
|
1003
|
+
var BROWSER_RELAY_CDP_METHOD = "SEQUENCE";
|
|
1004
|
+
var BROWSER_RELAY_CDP_RESOURCE = "/json/version -> /cdp";
|
|
1005
|
+
var BROWSER_RELAY_CDP_FLOW = "cdp";
|
|
1006
|
+
var BROWSER_RELAY_VERSION_PATH = "/json/version";
|
|
1007
|
+
function deriveBrowserRelayKeyId(key) {
|
|
1008
|
+
assertRelayKey(key);
|
|
1009
|
+
return hash("sha256", key, "base64url").slice(0, 22);
|
|
1010
|
+
}
|
|
1011
|
+
function canonicalizeBrowserRelayProof(proofKind, fields, clientProof) {
|
|
1012
|
+
const canonical = [
|
|
1013
|
+
BROWSER_RELAY_AUTH_LABEL,
|
|
1014
|
+
BROWSER_RELAY_AUTH_VERSION,
|
|
1015
|
+
proofKind,
|
|
1016
|
+
fields.keyId,
|
|
1017
|
+
fields.instanceId,
|
|
1018
|
+
fields.sessionId,
|
|
1019
|
+
fields.clientNonce,
|
|
1020
|
+
fields.serverNonce,
|
|
1021
|
+
fields.issuedAtMs,
|
|
1022
|
+
fields.expiresAtMs,
|
|
1023
|
+
fields.role,
|
|
1024
|
+
fields.transport,
|
|
1025
|
+
fields.method,
|
|
1026
|
+
fields.resource,
|
|
1027
|
+
fields.flow
|
|
1028
|
+
];
|
|
1029
|
+
if (proofKind === "accept") {
|
|
1030
|
+
if (!clientProof || !isCanonicalBase64Url(clientProof, 32)) {
|
|
1031
|
+
throw new Error("Browser relay accept proof requires a 32-byte client proof.");
|
|
728
1032
|
}
|
|
729
|
-
|
|
730
|
-
}
|
|
731
|
-
|
|
1033
|
+
canonical.push(clientProof);
|
|
1034
|
+
} else if (clientProof !== void 0) {
|
|
1035
|
+
throw new Error("Browser relay client proof is only valid for an accept proof.");
|
|
732
1036
|
}
|
|
1037
|
+
return Buffer.from(JSON.stringify(canonical), "utf8");
|
|
733
1038
|
}
|
|
734
|
-
function
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
1039
|
+
function createBrowserRelayProof(key, proofKind, fields, clientProof) {
|
|
1040
|
+
assertRelayKey(key);
|
|
1041
|
+
return createHmac("sha256", key).update(canonicalizeBrowserRelayProof(proofKind, fields, clientProof)).digest("base64url");
|
|
1042
|
+
}
|
|
1043
|
+
function verifyBrowserRelayProof(key, proofKind, fields, proof, clientProof) {
|
|
1044
|
+
if (!isCanonicalBase64Url(proof, 32))
|
|
1045
|
+
return false;
|
|
1046
|
+
const actual = Buffer.from(proof, "base64url");
|
|
1047
|
+
const expected = Buffer.from(createBrowserRelayProof(key, proofKind, fields, clientProof), "base64url");
|
|
1048
|
+
return actual.byteLength === expected.byteLength && timingSafeEqual2(actual, expected);
|
|
1049
|
+
}
|
|
1050
|
+
function assertRelayKey(key) {
|
|
1051
|
+
if (key.byteLength !== 32)
|
|
1052
|
+
throw new Error("Browser relay key must contain 32 bytes.");
|
|
1053
|
+
}
|
|
1054
|
+
function isCanonicalBase64Url(value, bytes) {
|
|
1055
|
+
if (!/^[A-Za-z0-9_-]+$/u.test(value))
|
|
1056
|
+
return false;
|
|
1057
|
+
const decoded = Buffer.from(value, "base64url");
|
|
1058
|
+
return decoded.byteLength === bytes && decoded.toString("base64url") === value;
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
// node_modules/.pnpm/mcporter@0.13.10/node_modules/mcporter/dist/chrome-devtools-relay-client.js
|
|
1062
|
+
import { createHash as createHash2, randomBytes as randomBytes2 } from "node:crypto";
|
|
1063
|
+
import { lookup } from "node:dns/promises";
|
|
1064
|
+
import net from "node:net";
|
|
1065
|
+
import { TextDecoder } from "node:util";
|
|
1066
|
+
var MAX_HEADER_BYTES = 16 * 1024;
|
|
1067
|
+
var MAX_JSON_BYTES = 64 * 1024;
|
|
1068
|
+
var WEBSOCKET_GUID2 = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
|
1069
|
+
var RelayV2Error = class extends Error {
|
|
1070
|
+
reason;
|
|
1071
|
+
status;
|
|
1072
|
+
constructor(reason, status) {
|
|
1073
|
+
super(`Chrome relay authentication failed (${reason}).`);
|
|
1074
|
+
this.reason = reason;
|
|
1075
|
+
this.status = status;
|
|
1076
|
+
this.name = "RelayV2Error";
|
|
1077
|
+
}
|
|
1078
|
+
};
|
|
1079
|
+
async function connectChromeDevtoolsRelayV2(options) {
|
|
1080
|
+
const startedAt = performance.now();
|
|
1081
|
+
const durationMs = () => Math.max(0, Math.round(performance.now() - startedAt));
|
|
1082
|
+
let socket;
|
|
1083
|
+
let timer;
|
|
1084
|
+
try {
|
|
1085
|
+
let timedOut = false;
|
|
1086
|
+
const timeout = new Promise((_resolve, reject) => {
|
|
1087
|
+
timer = setTimeout(() => {
|
|
1088
|
+
timedOut = true;
|
|
1089
|
+
const error = new RelayV2Error("timeout");
|
|
1090
|
+
socket?.destroy(error);
|
|
738
1091
|
reject(error);
|
|
739
|
-
|
|
1092
|
+
}, options.timeoutMs);
|
|
1093
|
+
});
|
|
1094
|
+
timer?.unref();
|
|
1095
|
+
const addresses = await Promise.race([
|
|
1096
|
+
resolveLoopbackAddresses(options.baseUrl.hostname, options.resolve ?? lookup),
|
|
1097
|
+
timeout
|
|
1098
|
+
]);
|
|
1099
|
+
const port = resolvePort(options.baseUrl);
|
|
1100
|
+
let lastConnectionError;
|
|
1101
|
+
for (const address of addresses) {
|
|
1102
|
+
socket = net.createConnection({ host: address.address, family: address.family, port });
|
|
1103
|
+
try {
|
|
1104
|
+
await waitForConnect(socket);
|
|
1105
|
+
lastConnectionError = void 0;
|
|
1106
|
+
break;
|
|
1107
|
+
} catch (error) {
|
|
1108
|
+
socket.destroy();
|
|
1109
|
+
if (timedOut || isTimeoutError(error))
|
|
1110
|
+
throw new RelayV2Error("timeout");
|
|
1111
|
+
lastConnectionError = error;
|
|
740
1112
|
}
|
|
741
|
-
|
|
1113
|
+
}
|
|
1114
|
+
if (lastConnectionError)
|
|
1115
|
+
throw lastConnectionError;
|
|
1116
|
+
if (!socket || socket.destroyed)
|
|
1117
|
+
throw new RelayV2Error(timedOut ? "timeout" : "network-error");
|
|
1118
|
+
if (!socket.remoteAddress || !isNumericLoopback(socket.remoteAddress))
|
|
1119
|
+
throw new RelayV2Error("protocol");
|
|
1120
|
+
const reader = new SocketReader(socket);
|
|
1121
|
+
const flow = options.flow ?? "cdp";
|
|
1122
|
+
const clientNonce = randomBytes2(32).toString("base64url");
|
|
1123
|
+
const hello = {
|
|
1124
|
+
v: BROWSER_RELAY_AUTH_VERSION,
|
|
1125
|
+
keyId: options.credential.keyId,
|
|
1126
|
+
clientNonce,
|
|
1127
|
+
role: BROWSER_RELAY_CDP_ROLE,
|
|
1128
|
+
transport: BROWSER_RELAY_CDP_TRANSPORT,
|
|
1129
|
+
method: flow === BROWSER_RELAY_CDP_FLOW ? BROWSER_RELAY_CDP_METHOD : "GET",
|
|
1130
|
+
resource: flow === BROWSER_RELAY_CDP_FLOW ? BROWSER_RELAY_CDP_RESOURCE : "/json/list",
|
|
1131
|
+
flow
|
|
1132
|
+
};
|
|
1133
|
+
await writeJsonRequest(socket, options.baseUrl.host, BROWSER_RELAY_AUTH_CHALLENGE_PATH, hello);
|
|
1134
|
+
const challengeResponse = await reader.readHttpResponse(MAX_JSON_BYTES);
|
|
1135
|
+
ensureAuthStatus(challengeResponse, "challenge");
|
|
1136
|
+
const challenge = parseStrictJsonObject(challengeResponse.body);
|
|
1137
|
+
const fields = validateChallenge(challenge, hello, options.now?.() ?? Date.now());
|
|
1138
|
+
const serverProof = requireString(challenge, "serverProof");
|
|
1139
|
+
if (!verifyBrowserRelayProof(options.credential.key, "server", fields, serverProof)) {
|
|
1140
|
+
throw new RelayV2Error("bad-server-proof");
|
|
1141
|
+
}
|
|
1142
|
+
const clientProof = createBrowserRelayProof(options.credential.key, "client", fields);
|
|
1143
|
+
await writeJsonRequest(socket, options.baseUrl.host, BROWSER_RELAY_AUTH_COMPLETE_PATH, {
|
|
1144
|
+
v: BROWSER_RELAY_AUTH_VERSION,
|
|
1145
|
+
sessionId: fields.sessionId,
|
|
1146
|
+
clientProof
|
|
1147
|
+
});
|
|
1148
|
+
const completeResponse = await reader.readHttpResponse(MAX_JSON_BYTES);
|
|
1149
|
+
ensureAuthStatus(completeResponse, "complete");
|
|
1150
|
+
const accepted = parseStrictJsonObject(completeResponse.body);
|
|
1151
|
+
validateAccept(accepted, fields.sessionId);
|
|
1152
|
+
if (!verifyBrowserRelayProof(options.credential.key, "accept", fields, requireString(accepted, "acceptProof"), clientProof)) {
|
|
1153
|
+
throw new RelayV2Error("bad-server-proof");
|
|
1154
|
+
}
|
|
1155
|
+
if (flow === "json-list") {
|
|
1156
|
+
await writeRequest(socket, options.baseUrl.host, "GET", "/json/list", []);
|
|
1157
|
+
const listResponse = await reader.readHttpResponse(MAX_JSON_BYTES, true);
|
|
1158
|
+
if (listResponse.status !== 200)
|
|
1159
|
+
throw classifyStatus(listResponse.status, true);
|
|
1160
|
+
const json = parseStrictJson(listResponse.body);
|
|
1161
|
+
if (!Array.isArray(json))
|
|
1162
|
+
throw new RelayV2Error("protocol", 200);
|
|
1163
|
+
socket.destroy();
|
|
1164
|
+
return { reason: "success", durationMs: durationMs(), status: 200, json };
|
|
1165
|
+
}
|
|
1166
|
+
await writeRequest(socket, options.baseUrl.host, "GET", BROWSER_RELAY_VERSION_PATH, []);
|
|
1167
|
+
const versionResponse = await reader.readHttpResponse(MAX_JSON_BYTES);
|
|
1168
|
+
if (versionResponse.status === 503) {
|
|
1169
|
+
socket.destroy();
|
|
1170
|
+
return { reason: "extension-disconnected", durationMs: durationMs(), status: 503 };
|
|
1171
|
+
}
|
|
1172
|
+
if (versionResponse.status !== 200)
|
|
1173
|
+
throw classifyStatus(versionResponse.status, true);
|
|
1174
|
+
parseStrictJsonObject(versionResponse.body);
|
|
1175
|
+
const websocketKey = randomBytes2(16).toString("base64");
|
|
1176
|
+
await writeRequest(socket, options.baseUrl.host, "GET", BROWSER_RELAY_CDP_PATH, [
|
|
1177
|
+
["Connection", "Upgrade"],
|
|
1178
|
+
["Upgrade", "websocket"],
|
|
1179
|
+
["Sec-WebSocket-Key", websocketKey],
|
|
1180
|
+
["Sec-WebSocket-Version", "13"]
|
|
1181
|
+
]);
|
|
1182
|
+
const upgrade = await reader.readHttpUpgrade();
|
|
1183
|
+
if (upgrade.status !== 101)
|
|
1184
|
+
throw classifyStatus(upgrade.status, true);
|
|
1185
|
+
const expectedAccept = createHash2("sha1").update(`${websocketKey}${WEBSOCKET_GUID2}`).digest("base64");
|
|
1186
|
+
if (upgrade.headers.get("upgrade")?.toLowerCase() !== "websocket" || !hasToken2(upgrade.headers.get("connection"), "upgrade") || upgrade.headers.get("sec-websocket-accept") !== expectedAccept) {
|
|
1187
|
+
throw new RelayV2Error("sequence", 101);
|
|
1188
|
+
}
|
|
1189
|
+
const head = reader.release();
|
|
1190
|
+
if (timer)
|
|
1191
|
+
clearTimeout(timer);
|
|
1192
|
+
return { reason: "success", durationMs: durationMs(), status: 200, upstream: { socket, head } };
|
|
1193
|
+
} catch (error) {
|
|
1194
|
+
socket?.destroy();
|
|
1195
|
+
if (error instanceof RelayV2Error) {
|
|
1196
|
+
return { reason: error.reason, durationMs: durationMs(), status: error.status };
|
|
1197
|
+
}
|
|
1198
|
+
return { reason: isTimeoutError(error) ? "timeout" : "network-error", durationMs: durationMs() };
|
|
1199
|
+
} finally {
|
|
1200
|
+
if (timer)
|
|
1201
|
+
clearTimeout(timer);
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
async function resolveLoopbackAddresses(hostname, resolver) {
|
|
1205
|
+
const normalized = hostname.replace(/^\[(.*)\]$/u, "$1");
|
|
1206
|
+
const addresses = await resolver(normalized, { all: true, verbatim: true });
|
|
1207
|
+
if (addresses.length === 0 || addresses.some((address) => !isNumericLoopback(address.address))) {
|
|
1208
|
+
throw new RelayV2Error("protocol");
|
|
1209
|
+
}
|
|
1210
|
+
return addresses;
|
|
1211
|
+
}
|
|
1212
|
+
function resolvePort(url) {
|
|
1213
|
+
const port = url.port === "" ? 80 : Number(url.port);
|
|
1214
|
+
if (!Number.isSafeInteger(port) || port < 1 || port > 65535)
|
|
1215
|
+
throw new RelayV2Error("protocol");
|
|
1216
|
+
return port;
|
|
1217
|
+
}
|
|
1218
|
+
function isNumericLoopback(address) {
|
|
1219
|
+
if (net.isIPv4(address))
|
|
1220
|
+
return address.startsWith("127.");
|
|
1221
|
+
if (!net.isIPv6(address))
|
|
1222
|
+
return false;
|
|
1223
|
+
const normalized = address.toLowerCase();
|
|
1224
|
+
return normalized === "::1" || normalized.startsWith("::ffff:127.");
|
|
1225
|
+
}
|
|
1226
|
+
async function waitForConnect(socket) {
|
|
1227
|
+
await new Promise((resolve, reject) => {
|
|
1228
|
+
const onConnect = () => {
|
|
1229
|
+
cleanup();
|
|
1230
|
+
resolve();
|
|
1231
|
+
};
|
|
1232
|
+
const onError = (error) => {
|
|
1233
|
+
cleanup();
|
|
1234
|
+
reject(error);
|
|
1235
|
+
};
|
|
1236
|
+
const cleanup = () => {
|
|
1237
|
+
socket.off("connect", onConnect);
|
|
1238
|
+
socket.off("error", onError);
|
|
1239
|
+
};
|
|
1240
|
+
socket.once("connect", onConnect);
|
|
1241
|
+
socket.once("error", onError);
|
|
1242
|
+
});
|
|
1243
|
+
}
|
|
1244
|
+
var SocketReader = class {
|
|
1245
|
+
socket;
|
|
1246
|
+
buffered = Buffer.alloc(0);
|
|
1247
|
+
failure;
|
|
1248
|
+
ended = false;
|
|
1249
|
+
onData = (chunk) => {
|
|
1250
|
+
this.buffered = Buffer.concat([this.buffered, chunk]);
|
|
1251
|
+
};
|
|
1252
|
+
onError = (error) => {
|
|
1253
|
+
this.failure = error;
|
|
1254
|
+
};
|
|
1255
|
+
onClose = () => {
|
|
1256
|
+
this.ended = true;
|
|
1257
|
+
};
|
|
1258
|
+
constructor(socket) {
|
|
1259
|
+
this.socket = socket;
|
|
1260
|
+
socket.on("data", this.onData);
|
|
1261
|
+
socket.on("error", this.onError);
|
|
1262
|
+
socket.on("close", this.onClose);
|
|
1263
|
+
}
|
|
1264
|
+
async readHttpResponse(maxBodyBytes, allowConnectionClose = false) {
|
|
1265
|
+
const parsed = await this.readHeaders();
|
|
1266
|
+
if (parsed.status === 101)
|
|
1267
|
+
throw new RelayV2Error("protocol", 101);
|
|
1268
|
+
if (parsed.headers.has("transfer-encoding"))
|
|
1269
|
+
throw new RelayV2Error("protocol", parsed.status);
|
|
1270
|
+
if (!allowConnectionClose && parsed.status === 200 && hasToken2(parsed.headers.get("connection"), "close")) {
|
|
1271
|
+
throw new RelayV2Error("protocol", parsed.status);
|
|
1272
|
+
}
|
|
1273
|
+
const rawLength = parsed.headers.get("content-length");
|
|
1274
|
+
if (!rawLength || !/^\d+$/u.test(rawLength))
|
|
1275
|
+
throw new RelayV2Error("protocol", parsed.status);
|
|
1276
|
+
const length = Number(rawLength);
|
|
1277
|
+
if (!Number.isSafeInteger(length) || length > maxBodyBytes)
|
|
1278
|
+
throw new RelayV2Error("protocol", parsed.status);
|
|
1279
|
+
await this.waitForBytes(length);
|
|
1280
|
+
const body = this.buffered.subarray(0, length);
|
|
1281
|
+
this.buffered = this.buffered.subarray(length);
|
|
1282
|
+
if (this.buffered.length > 0)
|
|
1283
|
+
throw new RelayV2Error("protocol", parsed.status);
|
|
1284
|
+
return { ...parsed, body };
|
|
1285
|
+
}
|
|
1286
|
+
async readHttpUpgrade() {
|
|
1287
|
+
const parsed = await this.readHeaders();
|
|
1288
|
+
if (parsed.headers.has("transfer-encoding"))
|
|
1289
|
+
throw new RelayV2Error("protocol", parsed.status);
|
|
1290
|
+
if (parsed.status === 101) {
|
|
1291
|
+
if (parsed.headers.has("content-length"))
|
|
1292
|
+
throw new RelayV2Error("protocol", parsed.status);
|
|
1293
|
+
return { ...parsed, body: Buffer.alloc(0) };
|
|
1294
|
+
}
|
|
1295
|
+
const rawLength = parsed.headers.get("content-length");
|
|
1296
|
+
if (!rawLength || !/^\d+$/u.test(rawLength))
|
|
1297
|
+
throw new RelayV2Error("protocol", parsed.status);
|
|
1298
|
+
const length = Number(rawLength);
|
|
1299
|
+
if (!Number.isSafeInteger(length) || length > MAX_JSON_BYTES)
|
|
1300
|
+
throw new RelayV2Error("protocol", parsed.status);
|
|
1301
|
+
await this.waitForBytes(length);
|
|
1302
|
+
const body = this.buffered.subarray(0, length);
|
|
1303
|
+
this.buffered = this.buffered.subarray(length);
|
|
1304
|
+
if (this.buffered.length > 0)
|
|
1305
|
+
throw new RelayV2Error("protocol", parsed.status);
|
|
1306
|
+
return { ...parsed, body };
|
|
1307
|
+
}
|
|
1308
|
+
release() {
|
|
1309
|
+
this.socket.off("data", this.onData);
|
|
1310
|
+
this.socket.off("error", this.onError);
|
|
1311
|
+
this.socket.off("close", this.onClose);
|
|
1312
|
+
const head = this.buffered;
|
|
1313
|
+
this.buffered = Buffer.alloc(0);
|
|
1314
|
+
return head;
|
|
1315
|
+
}
|
|
1316
|
+
async readHeaders() {
|
|
1317
|
+
let boundary = this.buffered.indexOf("\r\n\r\n");
|
|
1318
|
+
while (boundary < 0) {
|
|
1319
|
+
if (this.buffered.length > MAX_HEADER_BYTES)
|
|
1320
|
+
throw new RelayV2Error("protocol");
|
|
1321
|
+
await this.waitForData();
|
|
1322
|
+
boundary = this.buffered.indexOf("\r\n\r\n");
|
|
1323
|
+
}
|
|
1324
|
+
if (boundary > MAX_HEADER_BYTES)
|
|
1325
|
+
throw new RelayV2Error("protocol");
|
|
1326
|
+
const raw = this.buffered.subarray(0, boundary).toString("latin1");
|
|
1327
|
+
this.buffered = this.buffered.subarray(boundary + 4);
|
|
1328
|
+
const lines = raw.split("\r\n");
|
|
1329
|
+
const statusLine = lines.shift();
|
|
1330
|
+
const match = /^HTTP\/1\.1 ([1-5]\d\d)(?: [\x20-\x7e]*)?$/u.exec(statusLine ?? "");
|
|
1331
|
+
if (!match)
|
|
1332
|
+
throw new RelayV2Error("protocol");
|
|
1333
|
+
const status = Number(match[1]);
|
|
1334
|
+
const headers = /* @__PURE__ */ new Map();
|
|
1335
|
+
for (const line of lines) {
|
|
1336
|
+
if (/^[ \t]/u.test(line))
|
|
1337
|
+
throw new RelayV2Error("protocol", status);
|
|
1338
|
+
const separator = line.indexOf(":");
|
|
1339
|
+
if (separator <= 0)
|
|
1340
|
+
throw new RelayV2Error("protocol", status);
|
|
1341
|
+
const name = line.slice(0, separator).toLowerCase();
|
|
1342
|
+
const value = line.slice(separator + 1).trim();
|
|
1343
|
+
if (!/^[!#$%&'*+.^_`|~0-9a-z-]+$/u.test(name) || value.includes("\0") || value.includes("\r") || value.includes("\n") || value.includes("\x7F")) {
|
|
1344
|
+
throw new RelayV2Error("protocol", status);
|
|
1345
|
+
}
|
|
1346
|
+
if (headers.has(name))
|
|
1347
|
+
throw new RelayV2Error("protocol", status);
|
|
1348
|
+
headers.set(name, value);
|
|
1349
|
+
}
|
|
1350
|
+
return { status, headers };
|
|
1351
|
+
}
|
|
1352
|
+
async waitForBytes(length) {
|
|
1353
|
+
while (this.buffered.length < length)
|
|
1354
|
+
await this.waitForData();
|
|
1355
|
+
}
|
|
1356
|
+
async waitForData() {
|
|
1357
|
+
if (this.failure)
|
|
1358
|
+
throw this.failure;
|
|
1359
|
+
if (this.ended)
|
|
1360
|
+
throw new RelayV2Error("network-error");
|
|
1361
|
+
await new Promise((resolve, reject) => {
|
|
1362
|
+
const onData = () => {
|
|
1363
|
+
cleanup();
|
|
1364
|
+
resolve();
|
|
1365
|
+
};
|
|
1366
|
+
const onError = (error) => {
|
|
1367
|
+
cleanup();
|
|
1368
|
+
reject(error);
|
|
1369
|
+
};
|
|
1370
|
+
const onClose = () => {
|
|
1371
|
+
cleanup();
|
|
1372
|
+
reject(new RelayV2Error("network-error"));
|
|
1373
|
+
};
|
|
1374
|
+
const cleanup = () => {
|
|
1375
|
+
this.socket.off("data", onData);
|
|
1376
|
+
this.socket.off("error", onError);
|
|
1377
|
+
this.socket.off("close", onClose);
|
|
1378
|
+
};
|
|
1379
|
+
this.socket.once("data", onData);
|
|
1380
|
+
this.socket.once("error", onError);
|
|
1381
|
+
this.socket.once("close", onClose);
|
|
1382
|
+
});
|
|
1383
|
+
}
|
|
1384
|
+
};
|
|
1385
|
+
async function writeJsonRequest(socket, host, pathname, body) {
|
|
1386
|
+
const encoded = Buffer.from(JSON.stringify(body), "utf8");
|
|
1387
|
+
await writeRequest(socket, host, "POST", pathname, [
|
|
1388
|
+
["Content-Type", "application/json"],
|
|
1389
|
+
["Content-Length", String(encoded.byteLength)]
|
|
1390
|
+
], encoded);
|
|
1391
|
+
}
|
|
1392
|
+
async function writeRequest(socket, host, method, pathname, headers, body) {
|
|
1393
|
+
const lines = [`${method} ${pathname} HTTP/1.1`, `Host: ${host}`, "Connection: keep-alive"];
|
|
1394
|
+
for (const [name, value] of headers) {
|
|
1395
|
+
if (name.toLowerCase() === "connection")
|
|
1396
|
+
lines[2] = `${name}: ${value}`;
|
|
1397
|
+
else
|
|
1398
|
+
lines.push(`${name}: ${value}`);
|
|
1399
|
+
}
|
|
1400
|
+
const head = Buffer.from(`${lines.join("\r\n")}\r
|
|
1401
|
+
\r
|
|
1402
|
+
`, "ascii");
|
|
1403
|
+
const output = body ? Buffer.concat([head, body]) : head;
|
|
1404
|
+
await new Promise((resolve, reject) => {
|
|
1405
|
+
socket.write(output, (error) => error ? reject(error) : resolve());
|
|
1406
|
+
});
|
|
1407
|
+
}
|
|
1408
|
+
function ensureAuthStatus(response, phase) {
|
|
1409
|
+
if (response.status === 200)
|
|
1410
|
+
return;
|
|
1411
|
+
if (phase === "challenge" && response.status === 401 && responseError(response) === "Relay auth challenge rejected") {
|
|
1412
|
+
throw new RelayV2Error("replay", response.status);
|
|
1413
|
+
}
|
|
1414
|
+
if (response.status === 409)
|
|
1415
|
+
throw new RelayV2Error("sequence", response.status);
|
|
1416
|
+
throw classifyStatus(response.status, false);
|
|
1417
|
+
}
|
|
1418
|
+
function classifyStatus(status, authenticated) {
|
|
1419
|
+
if (!authenticated && (status === 401 || status === 404 || status === 426)) {
|
|
1420
|
+
return new RelayV2Error("unsupported-auth", status);
|
|
1421
|
+
}
|
|
1422
|
+
if (!authenticated && status === 503)
|
|
1423
|
+
return new RelayV2Error("server-auth-failed", status);
|
|
1424
|
+
if (status === 408 || status === 410)
|
|
1425
|
+
return new RelayV2Error("freshness", status);
|
|
1426
|
+
if (status === 409)
|
|
1427
|
+
return new RelayV2Error("sequence", status);
|
|
1428
|
+
if (status === 412)
|
|
1429
|
+
return new RelayV2Error("sequence", status);
|
|
1430
|
+
return new RelayV2Error("protocol", status);
|
|
1431
|
+
}
|
|
1432
|
+
function responseError(response) {
|
|
1433
|
+
try {
|
|
1434
|
+
const value = parseStrictJsonObject(response.body);
|
|
1435
|
+
return typeof value.error === "string" ? value.error : void 0;
|
|
1436
|
+
} catch {
|
|
1437
|
+
return void 0;
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
function validateChallenge(value, hello, nowMs) {
|
|
1441
|
+
assertExactKeys(value, [
|
|
1442
|
+
"type",
|
|
1443
|
+
"v",
|
|
1444
|
+
"keyId",
|
|
1445
|
+
"instanceId",
|
|
1446
|
+
"sessionId",
|
|
1447
|
+
"clientNonce",
|
|
1448
|
+
"serverNonce",
|
|
1449
|
+
"issuedAtMs",
|
|
1450
|
+
"expiresAtMs",
|
|
1451
|
+
"role",
|
|
1452
|
+
"transport",
|
|
1453
|
+
"method",
|
|
1454
|
+
"resource",
|
|
1455
|
+
"flow",
|
|
1456
|
+
"serverProof"
|
|
1457
|
+
]);
|
|
1458
|
+
if (value.type !== "auth.challenge")
|
|
1459
|
+
throw new RelayV2Error("protocol");
|
|
1460
|
+
if (value.v !== BROWSER_RELAY_AUTH_VERSION)
|
|
1461
|
+
throw new RelayV2Error("protocol");
|
|
1462
|
+
for (const key of ["keyId", "clientNonce", "role", "transport", "method", "resource", "flow"]) {
|
|
1463
|
+
if (value[key] !== hello[key])
|
|
1464
|
+
throw new RelayV2Error("protocol");
|
|
1465
|
+
}
|
|
1466
|
+
const fields = {
|
|
1467
|
+
keyId: requireToken(value, "keyId", 22),
|
|
1468
|
+
instanceId: requireEncoded(value, "instanceId", 22),
|
|
1469
|
+
sessionId: requireEncoded(value, "sessionId", 22),
|
|
1470
|
+
clientNonce: requireEncoded(value, "clientNonce", 43),
|
|
1471
|
+
serverNonce: requireEncoded(value, "serverNonce", 43),
|
|
1472
|
+
issuedAtMs: requireInteger(value, "issuedAtMs"),
|
|
1473
|
+
expiresAtMs: requireInteger(value, "expiresAtMs"),
|
|
1474
|
+
role: requireString(value, "role"),
|
|
1475
|
+
transport: requireString(value, "transport"),
|
|
1476
|
+
method: requireString(value, "method"),
|
|
1477
|
+
resource: requireString(value, "resource"),
|
|
1478
|
+
flow: requireString(value, "flow")
|
|
1479
|
+
};
|
|
1480
|
+
requireEncoded(value, "serverProof", 43);
|
|
1481
|
+
const lifetime = fields.expiresAtMs - fields.issuedAtMs;
|
|
1482
|
+
if (lifetime <= 0 || lifetime > BROWSER_RELAY_CHALLENGE_MAX_LIFETIME_MS || fields.issuedAtMs - nowMs > BROWSER_RELAY_CLOCK_SKEW_MS || nowMs > fields.expiresAtMs) {
|
|
1483
|
+
throw new RelayV2Error("freshness");
|
|
1484
|
+
}
|
|
1485
|
+
return fields;
|
|
1486
|
+
}
|
|
1487
|
+
function validateAccept(value, sessionId) {
|
|
1488
|
+
assertExactKeys(value, ["type", "v", "sessionId", "acceptProof"]);
|
|
1489
|
+
if (value.type !== "auth.ok")
|
|
1490
|
+
throw new RelayV2Error("protocol");
|
|
1491
|
+
if (value.v !== BROWSER_RELAY_AUTH_VERSION || value.sessionId !== sessionId)
|
|
1492
|
+
throw new RelayV2Error("protocol");
|
|
1493
|
+
requireEncoded(value, "acceptProof", 43);
|
|
1494
|
+
}
|
|
1495
|
+
function parseStrictJsonObject(body) {
|
|
1496
|
+
const value = parseStrictJson(body);
|
|
1497
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
1498
|
+
throw new RelayV2Error("protocol");
|
|
1499
|
+
return value;
|
|
1500
|
+
}
|
|
1501
|
+
function parseStrictJson(body) {
|
|
1502
|
+
let source;
|
|
1503
|
+
try {
|
|
1504
|
+
source = new TextDecoder("utf-8", { fatal: true }).decode(body);
|
|
1505
|
+
} catch {
|
|
1506
|
+
throw new RelayV2Error("protocol");
|
|
1507
|
+
}
|
|
1508
|
+
const errors = [];
|
|
1509
|
+
const tree = parseTree(source, errors, { allowTrailingComma: false, disallowComments: true });
|
|
1510
|
+
if (!tree || errors.length > 0)
|
|
1511
|
+
throw new RelayV2Error("protocol");
|
|
1512
|
+
rejectDuplicateJsonKeys(tree);
|
|
1513
|
+
try {
|
|
1514
|
+
return JSON.parse(source);
|
|
1515
|
+
} catch {
|
|
1516
|
+
throw new RelayV2Error("protocol");
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
function rejectDuplicateJsonKeys(node) {
|
|
1520
|
+
if (node.type === "object") {
|
|
1521
|
+
const keys = /* @__PURE__ */ new Set();
|
|
1522
|
+
for (const property of node.children ?? []) {
|
|
1523
|
+
const key = property.children?.[0]?.value;
|
|
1524
|
+
if (typeof key !== "string" || keys.has(key))
|
|
1525
|
+
throw new RelayV2Error("protocol");
|
|
1526
|
+
keys.add(key);
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
for (const child of node.children ?? [])
|
|
1530
|
+
rejectDuplicateJsonKeys(child);
|
|
1531
|
+
}
|
|
1532
|
+
function assertExactKeys(value, required) {
|
|
1533
|
+
const actual = Object.keys(value);
|
|
1534
|
+
if (actual.length !== required.length || required.some((key) => !Object.hasOwn(value, key))) {
|
|
1535
|
+
throw new RelayV2Error("protocol");
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
function requireString(value, key) {
|
|
1539
|
+
const field = value[key];
|
|
1540
|
+
if (typeof field !== "string")
|
|
1541
|
+
throw new RelayV2Error("protocol");
|
|
1542
|
+
return field;
|
|
1543
|
+
}
|
|
1544
|
+
function requireEncoded(value, key, length) {
|
|
1545
|
+
const field = requireString(value, key);
|
|
1546
|
+
const bytes = length === 22 ? 16 : length === 43 ? 32 : 0;
|
|
1547
|
+
const decoded = Buffer.from(field, "base64url");
|
|
1548
|
+
if (bytes === 0 || field.length !== length || !/^[A-Za-z0-9_-]+$/u.test(field) || decoded.byteLength !== bytes || decoded.toString("base64url") !== field) {
|
|
1549
|
+
throw new RelayV2Error("protocol");
|
|
1550
|
+
}
|
|
1551
|
+
return field;
|
|
1552
|
+
}
|
|
1553
|
+
function requireToken(value, key, length) {
|
|
1554
|
+
const field = requireString(value, key);
|
|
1555
|
+
if (field.length !== length || !/^[A-Za-z0-9_-]+$/u.test(field))
|
|
1556
|
+
throw new RelayV2Error("protocol");
|
|
1557
|
+
return field;
|
|
1558
|
+
}
|
|
1559
|
+
function requireInteger(value, key) {
|
|
1560
|
+
const field = value[key];
|
|
1561
|
+
if (typeof field !== "number" || !Number.isSafeInteger(field))
|
|
1562
|
+
throw new RelayV2Error("protocol");
|
|
1563
|
+
return field;
|
|
1564
|
+
}
|
|
1565
|
+
function hasToken2(value, expected) {
|
|
1566
|
+
return Boolean(value?.split(",").some((token) => token.trim().toLowerCase() === expected));
|
|
1567
|
+
}
|
|
1568
|
+
function isTimeoutError(error) {
|
|
1569
|
+
return Boolean(error && typeof error === "object" && error.reason === "timeout");
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1572
|
+
// node_modules/.pnpm/mcporter@0.13.10/node_modules/mcporter/dist/chrome-devtools-relay-discovery.js
|
|
1573
|
+
import { spawn } from "node:child_process";
|
|
1574
|
+
import { statSync } from "node:fs";
|
|
1575
|
+
import net2 from "node:net";
|
|
1576
|
+
import path from "node:path";
|
|
1577
|
+
var OPENCLAW_CDP_ARGS = ["browser", "extension", "cdp", "--json"];
|
|
1578
|
+
var OPENCLAW_WINDOWS_COMMAND_SUFFIX = "browser extension cdp --json";
|
|
1579
|
+
var OPENCLAW_WINDOWS_DEFAULT_PATHEXT = ".COM;.EXE;.BAT;.CMD";
|
|
1580
|
+
var OPENCLAW_WINDOWS_TREE_KILL_TIMEOUT_MS = 1e3;
|
|
1581
|
+
var OPENCLAW_PROFILE_PATTERN = /^[a-z0-9][a-z0-9_-]{0,63}$/i;
|
|
1582
|
+
var OPENCLAW_RELAY_DISCOVERY_MAX_OUTPUT_BYTES = 64 * 1024;
|
|
1583
|
+
var OPENCLAW_RELAY_DISCOVERY_ENV_KEYS = [
|
|
1584
|
+
"APPDATA",
|
|
1585
|
+
"COMSPEC",
|
|
1586
|
+
"ComSpec",
|
|
1587
|
+
"HOME",
|
|
1588
|
+
"HOMEDRIVE",
|
|
1589
|
+
"HOMEPATH",
|
|
1590
|
+
"LANG",
|
|
1591
|
+
"LC_ALL",
|
|
1592
|
+
"LOCALAPPDATA",
|
|
1593
|
+
"OPENCLAW_CONFIG_PATH",
|
|
1594
|
+
"OPENCLAW_GATEWAY_PORT",
|
|
1595
|
+
"OPENCLAW_HOME",
|
|
1596
|
+
"OPENCLAW_OAUTH_DIR",
|
|
1597
|
+
"OPENCLAW_PROFILE",
|
|
1598
|
+
"OPENCLAW_STATE_DIR",
|
|
1599
|
+
"PATH",
|
|
1600
|
+
"Path",
|
|
1601
|
+
"PATHEXT",
|
|
1602
|
+
"SYSTEMROOT",
|
|
1603
|
+
"SystemRoot",
|
|
1604
|
+
"TEMP",
|
|
1605
|
+
"TMP",
|
|
1606
|
+
"TMPDIR",
|
|
1607
|
+
"USERPROFILE",
|
|
1608
|
+
"WINDIR",
|
|
1609
|
+
"XDG_CONFIG_HOME",
|
|
1610
|
+
"XDG_STATE_HOME"
|
|
1611
|
+
];
|
|
1612
|
+
function normalizeOpenClawProfile(raw) {
|
|
1613
|
+
const profile = raw?.trim();
|
|
1614
|
+
if (!profile || profile.toLowerCase() === "default" || !OPENCLAW_PROFILE_PATTERN.test(profile)) {
|
|
1615
|
+
return void 0;
|
|
1616
|
+
}
|
|
1617
|
+
return profile;
|
|
1618
|
+
}
|
|
1619
|
+
async function discoverOpenClawRelayUrl(options) {
|
|
1620
|
+
const platform = options.platform ?? process.platform;
|
|
1621
|
+
const cwd = options.cwd ?? process.cwd();
|
|
1622
|
+
const filteredEnv = openClawDiscoveryEnvironment(options.env);
|
|
1623
|
+
const command = resolveOpenClawRelayDiscoveryCommandPlan(filteredEnv, platform, options.isFile, cwd);
|
|
1624
|
+
if (!command)
|
|
1625
|
+
return { reason: "unavailable" };
|
|
1626
|
+
const env = sanitizeOpenClawDiscoveryCommandEnvironment(filteredEnv, command, platform, cwd);
|
|
1627
|
+
const result = await (options.run ?? runOpenClawRelayDiscovery)({
|
|
1628
|
+
...command,
|
|
1629
|
+
env,
|
|
1630
|
+
timeoutMs: options.timeoutMs,
|
|
1631
|
+
maxOutputBytes: OPENCLAW_RELAY_DISCOVERY_MAX_OUTPUT_BYTES,
|
|
1632
|
+
shell: false
|
|
1633
|
+
});
|
|
1634
|
+
return validateDiscoveryCommandResult(result, options.keyId);
|
|
1635
|
+
}
|
|
1636
|
+
function runOpenClawRelayDiscovery(options, runtime = {}) {
|
|
1637
|
+
return new Promise((resolve) => {
|
|
1638
|
+
const child = (runtime.spawn ?? spawn)(options.executable, [...options.args], {
|
|
1639
|
+
cwd: options.cwd,
|
|
1640
|
+
env: options.env,
|
|
1641
|
+
shell: options.shell,
|
|
1642
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
1643
|
+
windowsVerbatimArguments: options.platform === "win32",
|
|
1644
|
+
windowsHide: true
|
|
1645
|
+
});
|
|
1646
|
+
const stdout = [];
|
|
1647
|
+
let outputBytes = 0;
|
|
1648
|
+
let settled = false;
|
|
1649
|
+
let terminating = false;
|
|
1650
|
+
let timer;
|
|
1651
|
+
const finish = (result) => {
|
|
1652
|
+
if (settled)
|
|
1653
|
+
return;
|
|
1654
|
+
settled = true;
|
|
1655
|
+
if (timer)
|
|
1656
|
+
clearTimeout(timer);
|
|
1657
|
+
resolve(result);
|
|
1658
|
+
};
|
|
1659
|
+
const terminate = (kind) => {
|
|
1660
|
+
if (settled || terminating)
|
|
1661
|
+
return;
|
|
1662
|
+
terminating = true;
|
|
1663
|
+
child.stdout.destroy();
|
|
1664
|
+
child.stderr.destroy();
|
|
1665
|
+
const plan = createOpenClawRelayDiscoveryTerminationPlan(options, child.pid);
|
|
1666
|
+
const terminateProcessTree = runtime.terminateProcessTree ?? terminateOpenClawRelayDiscoveryProcessTree;
|
|
1667
|
+
let termination = Promise.resolve();
|
|
1668
|
+
if (plan) {
|
|
1669
|
+
try {
|
|
1670
|
+
termination = terminateProcessTree(plan, child);
|
|
1671
|
+
} catch {
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
void boundedTreeTermination(termination).finally(() => {
|
|
1675
|
+
child.unref();
|
|
1676
|
+
finish({ kind });
|
|
1677
|
+
});
|
|
1678
|
+
};
|
|
1679
|
+
const collect = (chunk, keep) => {
|
|
1680
|
+
if (settled || terminating)
|
|
1681
|
+
return;
|
|
1682
|
+
outputBytes += chunk.byteLength;
|
|
1683
|
+
if (outputBytes > options.maxOutputBytes) {
|
|
1684
|
+
terminate("overflow");
|
|
1685
|
+
return;
|
|
1686
|
+
}
|
|
1687
|
+
if (keep)
|
|
1688
|
+
stdout.push(chunk);
|
|
1689
|
+
};
|
|
1690
|
+
child.stdout.on("data", (chunk) => collect(chunk, true));
|
|
1691
|
+
child.stderr.on("data", (chunk) => collect(chunk, false));
|
|
1692
|
+
child.once("error", (error) => {
|
|
1693
|
+
if (!terminating)
|
|
1694
|
+
finish({ kind: classifyCommandError(error) });
|
|
1695
|
+
});
|
|
1696
|
+
child.once("close", (code) => {
|
|
1697
|
+
if (!terminating) {
|
|
1698
|
+
finish(code === 0 ? { kind: "success", stdout: Buffer.concat(stdout) } : { kind: "nonzero" });
|
|
1699
|
+
}
|
|
1700
|
+
});
|
|
1701
|
+
timer = setTimeout(() => terminate("timeout"), options.timeoutMs);
|
|
1702
|
+
timer.unref();
|
|
1703
|
+
});
|
|
1704
|
+
}
|
|
1705
|
+
function resolveOpenClawRelayDiscoveryCommandPlan(env, platform = process.platform, isFile = isRegularFile, cwd = process.cwd()) {
|
|
1706
|
+
if (platform !== "win32")
|
|
1707
|
+
return { executable: "openclaw", args: OPENCLAW_CDP_ARGS, platform };
|
|
1708
|
+
const executable = resolveWindowsCommandInterpreter(env);
|
|
1709
|
+
const openClawShim = resolveWindowsOpenClawShim(env, isFile, cwd);
|
|
1710
|
+
if (!executable || !openClawShim)
|
|
1711
|
+
return void 0;
|
|
1712
|
+
const taskkillExecutable = path.win32.join(path.win32.dirname(executable), "taskkill.exe");
|
|
1713
|
+
if (!isSafeWindowsSystemToolPath(taskkillExecutable, "taskkill.exe"))
|
|
1714
|
+
return void 0;
|
|
1715
|
+
return {
|
|
1716
|
+
executable,
|
|
1717
|
+
// The only variable bytes are the validated absolute shim path. No config,
|
|
1718
|
+
// profile, URL, metadata, or unresolved command name reaches cmd.exe.
|
|
1719
|
+
args: ["/d", "/s", "/c", `""${openClawShim}" ${OPENCLAW_WINDOWS_COMMAND_SUFFIX}"`],
|
|
1720
|
+
cwd: path.win32.dirname(openClawShim),
|
|
1721
|
+
platform,
|
|
1722
|
+
taskkillExecutable
|
|
1723
|
+
};
|
|
1724
|
+
}
|
|
1725
|
+
function resolveWindowsCommandInterpreter(env) {
|
|
1726
|
+
const systemRoot = env.SYSTEMROOT?.trim() || env.SystemRoot?.trim();
|
|
1727
|
+
const systemCmd = systemRoot && isSafeWindowsSystemRoot(systemRoot) ? path.win32.join(systemRoot, "System32", "cmd.exe") : void 0;
|
|
1728
|
+
const comSpec = env.COMSPEC?.trim() || env.ComSpec?.trim();
|
|
1729
|
+
if (comSpec && isSafeWindowsCmdPath(comSpec)) {
|
|
1730
|
+
const normalizedComSpec = path.win32.normalize(comSpec);
|
|
1731
|
+
if (systemCmd && normalizedComSpec.toLowerCase() === systemCmd.toLowerCase() || !systemCmd && isConventionalWindowsSystemCmd(normalizedComSpec)) {
|
|
1732
|
+
return normalizedComSpec;
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
return systemCmd;
|
|
1736
|
+
}
|
|
1737
|
+
function resolveWindowsOpenClawShim(env, isFile, cwd) {
|
|
1738
|
+
const pathEntries = safeWindowsPathEntries(env.PATH ?? env.Path, cwd);
|
|
1739
|
+
const extensions = windowsPathExtensions(env.PATHEXT);
|
|
1740
|
+
for (const directory of pathEntries) {
|
|
1741
|
+
for (const extension of extensions) {
|
|
1742
|
+
if (extension.toLowerCase() !== ".cmd")
|
|
1743
|
+
continue;
|
|
1744
|
+
const candidate = path.win32.join(directory, `openclaw${extension}`);
|
|
1745
|
+
if (isSafeWindowsCommandPath(candidate) && isFile(candidate))
|
|
1746
|
+
return path.win32.normalize(candidate);
|
|
1747
|
+
}
|
|
1748
|
+
}
|
|
1749
|
+
return void 0;
|
|
1750
|
+
}
|
|
1751
|
+
function isSafeWindowsCmdPath(value) {
|
|
1752
|
+
return isSafeWindowsSystemToolPath(value, "cmd.exe");
|
|
1753
|
+
}
|
|
1754
|
+
function isSafeWindowsSystemToolPath(value, basename) {
|
|
1755
|
+
const parent = path.win32.dirname(value);
|
|
1756
|
+
const root = path.win32.dirname(parent);
|
|
1757
|
+
return isSafeWindowsCommandPath(value) && path.win32.basename(value).toLowerCase() === basename && path.win32.basename(parent).toLowerCase() === "system32" && ["windows", "winnt"].includes(path.win32.basename(root).toLowerCase());
|
|
1758
|
+
}
|
|
1759
|
+
function isSafeWindowsSystemRoot(value) {
|
|
1760
|
+
const basename = path.win32.basename(value).toLowerCase();
|
|
1761
|
+
return isSafeWindowsAbsolutePath(value) && basename !== "cmd.exe" && basename !== "system32";
|
|
1762
|
+
}
|
|
1763
|
+
function isConventionalWindowsSystemCmd(value) {
|
|
1764
|
+
const parent = path.win32.dirname(value);
|
|
1765
|
+
const root = path.win32.dirname(parent);
|
|
1766
|
+
return path.win32.basename(parent).toLowerCase() === "system32" && ["windows", "winnt"].includes(path.win32.basename(root).toLowerCase());
|
|
1767
|
+
}
|
|
1768
|
+
function isSafeWindowsAbsolutePath(value) {
|
|
1769
|
+
if (!/^[a-z]:[\\/]/i.test(value) || /["<>|?*]/u.test(value) || hasControlCharacter(value) || value.slice(2).includes(":")) {
|
|
1770
|
+
return false;
|
|
1771
|
+
}
|
|
1772
|
+
const segments = value.replaceAll("/", "\\").split("\\");
|
|
1773
|
+
return !segments.some((segment) => segment === "." || segment === "..");
|
|
1774
|
+
}
|
|
1775
|
+
function isSafeWindowsCommandPath(value) {
|
|
1776
|
+
return isSafeWindowsAbsolutePath(value) && !/["%&|<>^!]/u.test(value) && !hasControlCharacter(value);
|
|
1777
|
+
}
|
|
1778
|
+
function hasControlCharacter(value) {
|
|
1779
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
1780
|
+
if (value.charCodeAt(index) < 32)
|
|
1781
|
+
return true;
|
|
1782
|
+
}
|
|
1783
|
+
return false;
|
|
1784
|
+
}
|
|
1785
|
+
function safeWindowsPathEntries(value, excludedDirectory) {
|
|
1786
|
+
if (!value)
|
|
1787
|
+
return [];
|
|
1788
|
+
const excluded = excludedDirectory && isSafeWindowsCommandPath(excludedDirectory) ? path.win32.resolve(excludedDirectory).toLowerCase() : void 0;
|
|
1789
|
+
return value.split(";").map((entry) => entry.trim()).filter((entry) => entry !== "" && path.win32.isAbsolute(entry) && isSafeWindowsCommandPath(entry)).map((entry) => path.win32.resolve(entry)).filter((entry) => entry.toLowerCase() !== excluded);
|
|
1790
|
+
}
|
|
1791
|
+
function windowsPathExtensions(value) {
|
|
1792
|
+
const raw = value?.trim() || OPENCLAW_WINDOWS_DEFAULT_PATHEXT;
|
|
1793
|
+
return raw.split(";").map((extension) => extension.trim()).filter((extension) => /^\.[a-z0-9]+$/iu.test(extension));
|
|
1794
|
+
}
|
|
1795
|
+
function isRegularFile(candidate) {
|
|
1796
|
+
try {
|
|
1797
|
+
return statSync(candidate).isFile();
|
|
1798
|
+
} catch {
|
|
1799
|
+
return false;
|
|
1800
|
+
}
|
|
1801
|
+
}
|
|
1802
|
+
function sanitizeOpenClawDiscoveryCommandEnvironment(env, command, platform, sourceCwd) {
|
|
1803
|
+
if (platform !== "win32")
|
|
1804
|
+
return env;
|
|
1805
|
+
const sanitized = { ...env };
|
|
1806
|
+
delete sanitized.ComSpec;
|
|
1807
|
+
delete sanitized.COMSPEC;
|
|
1808
|
+
sanitized.ComSpec = command.executable;
|
|
1809
|
+
const safePath = safeWindowsPathEntries(sanitized.PATH ?? sanitized.Path, sourceCwd).join(";");
|
|
1810
|
+
delete sanitized.Path;
|
|
1811
|
+
delete sanitized.PATH;
|
|
1812
|
+
if (safePath)
|
|
1813
|
+
sanitized.Path = safePath;
|
|
1814
|
+
const systemRoot = sanitized.SYSTEMROOT?.trim() || sanitized.SystemRoot?.trim();
|
|
1815
|
+
if (systemRoot && !isSafeWindowsSystemRoot(systemRoot)) {
|
|
1816
|
+
delete sanitized.SystemRoot;
|
|
1817
|
+
delete sanitized.SYSTEMROOT;
|
|
1818
|
+
}
|
|
1819
|
+
return sanitized;
|
|
1820
|
+
}
|
|
1821
|
+
function createOpenClawRelayDiscoveryTerminationPlan(options, pid) {
|
|
1822
|
+
if (!Number.isSafeInteger(pid) || !pid || pid <= 0)
|
|
1823
|
+
return void 0;
|
|
1824
|
+
if (options.platform !== "win32")
|
|
1825
|
+
return { kind: "posix-sigkill", pid, signal: "SIGKILL" };
|
|
1826
|
+
if (!options.taskkillExecutable || !isSafeWindowsSystemToolPath(options.taskkillExecutable, "taskkill.exe")) {
|
|
1827
|
+
return void 0;
|
|
1828
|
+
}
|
|
1829
|
+
return {
|
|
1830
|
+
kind: "windows-taskkill",
|
|
1831
|
+
pid,
|
|
1832
|
+
executable: options.taskkillExecutable,
|
|
1833
|
+
args: ["/PID", String(pid), "/T", "/F"],
|
|
1834
|
+
shell: false,
|
|
1835
|
+
windowsHide: true
|
|
1836
|
+
};
|
|
1837
|
+
}
|
|
1838
|
+
async function terminateOpenClawRelayDiscoveryProcessTree(plan, child) {
|
|
1839
|
+
if (plan.kind === "posix-sigkill") {
|
|
1840
|
+
try {
|
|
1841
|
+
child.kill(plan.signal);
|
|
1842
|
+
} catch {
|
|
1843
|
+
}
|
|
1844
|
+
return;
|
|
1845
|
+
}
|
|
1846
|
+
await new Promise((resolve) => {
|
|
1847
|
+
let killer;
|
|
1848
|
+
try {
|
|
1849
|
+
killer = spawn(plan.executable, [...plan.args], {
|
|
1850
|
+
shell: plan.shell,
|
|
1851
|
+
stdio: "ignore",
|
|
1852
|
+
windowsHide: plan.windowsHide
|
|
1853
|
+
});
|
|
1854
|
+
} catch {
|
|
1855
|
+
resolve();
|
|
1856
|
+
return;
|
|
1857
|
+
}
|
|
1858
|
+
let done = false;
|
|
1859
|
+
const finish = () => {
|
|
1860
|
+
if (done)
|
|
1861
|
+
return;
|
|
1862
|
+
done = true;
|
|
1863
|
+
clearTimeout(timer);
|
|
1864
|
+
resolve();
|
|
1865
|
+
};
|
|
1866
|
+
killer.once("error", finish);
|
|
1867
|
+
killer.once("close", finish);
|
|
1868
|
+
const timer = setTimeout(() => {
|
|
1869
|
+
try {
|
|
1870
|
+
killer.kill("SIGKILL");
|
|
1871
|
+
} catch {
|
|
1872
|
+
}
|
|
1873
|
+
finish();
|
|
1874
|
+
}, OPENCLAW_WINDOWS_TREE_KILL_TIMEOUT_MS);
|
|
1875
|
+
timer.unref();
|
|
1876
|
+
});
|
|
1877
|
+
}
|
|
1878
|
+
async function boundedTreeTermination(termination) {
|
|
1879
|
+
let timer;
|
|
1880
|
+
try {
|
|
1881
|
+
await Promise.race([
|
|
1882
|
+
termination.catch(() => void 0),
|
|
1883
|
+
new Promise((resolve) => {
|
|
1884
|
+
timer = setTimeout(resolve, OPENCLAW_WINDOWS_TREE_KILL_TIMEOUT_MS + 250);
|
|
1885
|
+
timer.unref();
|
|
1886
|
+
})
|
|
1887
|
+
]);
|
|
1888
|
+
} finally {
|
|
1889
|
+
if (timer)
|
|
1890
|
+
clearTimeout(timer);
|
|
1891
|
+
}
|
|
1892
|
+
}
|
|
1893
|
+
function classifyCommandError(error) {
|
|
1894
|
+
const details = error;
|
|
1895
|
+
if (details.code === "ENOENT")
|
|
1896
|
+
return "unavailable";
|
|
1897
|
+
if (details.code === "ETIMEDOUT" || details.killed === true)
|
|
1898
|
+
return "timeout";
|
|
1899
|
+
if (details.code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER" || details.code === "ENOBUFS")
|
|
1900
|
+
return "overflow";
|
|
1901
|
+
return "nonzero";
|
|
1902
|
+
}
|
|
1903
|
+
function validateDiscoveryCommandResult(result, expectedKeyId) {
|
|
1904
|
+
if (result.kind !== "success")
|
|
1905
|
+
return { reason: result.kind };
|
|
1906
|
+
if (result.stdout.byteLength > OPENCLAW_RELAY_DISCOVERY_MAX_OUTPUT_BYTES)
|
|
1907
|
+
return { reason: "overflow" };
|
|
1908
|
+
const encoded = result.stdout.toString("utf8").trim();
|
|
1909
|
+
const parseErrors = [];
|
|
1910
|
+
const tree = parseTree(encoded, parseErrors, { allowTrailingComma: false, disallowComments: true });
|
|
1911
|
+
if (!tree || parseErrors.length > 0 || hasDuplicateObjectKeys(tree))
|
|
1912
|
+
return { reason: "malformed" };
|
|
1913
|
+
let metadata;
|
|
1914
|
+
try {
|
|
1915
|
+
metadata = JSON.parse(encoded);
|
|
1916
|
+
} catch {
|
|
1917
|
+
return { reason: "malformed" };
|
|
1918
|
+
}
|
|
1919
|
+
if (!isPlainObject(metadata) || !hasExactKeys(metadata, ["auth", "browserUrl", "wsEndpoint"])) {
|
|
1920
|
+
return { reason: "incompatible" };
|
|
1921
|
+
}
|
|
1922
|
+
if (!isPlainObject(metadata.auth) || !hasExactKeys(metadata.auth, [
|
|
1923
|
+
"challengeUrl",
|
|
1924
|
+
"completeUrl",
|
|
1925
|
+
"flow",
|
|
1926
|
+
"keyId",
|
|
1927
|
+
"label",
|
|
1928
|
+
"method",
|
|
1929
|
+
"resource",
|
|
1930
|
+
"role",
|
|
1931
|
+
"transport",
|
|
1932
|
+
"version"
|
|
1933
|
+
])) {
|
|
1934
|
+
return { reason: "incompatible" };
|
|
1935
|
+
}
|
|
1936
|
+
const browserUrl = parseBrowserUrl(metadata.browserUrl);
|
|
1937
|
+
if (!browserUrl)
|
|
1938
|
+
return { reason: "unsafe" };
|
|
1939
|
+
const wsEndpoint = parseWebSocketUrl(metadata.wsEndpoint);
|
|
1940
|
+
if (!wsEndpoint || wsEndpoint.host !== browserUrl.host)
|
|
1941
|
+
return { reason: "unsafe" };
|
|
1942
|
+
const auth = metadata.auth;
|
|
1943
|
+
if (auth.label !== BROWSER_RELAY_AUTH_LABEL || auth.version !== BROWSER_RELAY_AUTH_VERSION || auth.role !== BROWSER_RELAY_CDP_ROLE || auth.transport !== BROWSER_RELAY_CDP_TRANSPORT || auth.method !== BROWSER_RELAY_CDP_METHOD || auth.resource !== BROWSER_RELAY_CDP_RESOURCE || auth.flow !== BROWSER_RELAY_CDP_FLOW || auth.challengeUrl !== new URL(BROWSER_RELAY_AUTH_CHALLENGE_PATH, browserUrl).toString() || auth.completeUrl !== new URL(BROWSER_RELAY_AUTH_COMPLETE_PATH, browserUrl).toString()) {
|
|
1944
|
+
return { reason: "incompatible" };
|
|
1945
|
+
}
|
|
1946
|
+
if (auth.keyId !== expectedKeyId)
|
|
1947
|
+
return { reason: "key-id-mismatch" };
|
|
1948
|
+
return { reason: "success", url: browserUrl };
|
|
1949
|
+
}
|
|
1950
|
+
function parseBrowserUrl(value) {
|
|
1951
|
+
if (typeof value !== "string")
|
|
1952
|
+
return void 0;
|
|
1953
|
+
let url;
|
|
1954
|
+
try {
|
|
1955
|
+
url = new URL(value);
|
|
1956
|
+
} catch {
|
|
1957
|
+
return void 0;
|
|
1958
|
+
}
|
|
1959
|
+
if (url.protocol !== "http:" || !isLoopbackHostname(url.hostname) || url.username !== "" || url.password !== "" || !hasValidPort(url) || url.pathname !== "/" || url.search !== "" || url.hash !== "") {
|
|
1960
|
+
return void 0;
|
|
1961
|
+
}
|
|
1962
|
+
return new URL(url.origin);
|
|
1963
|
+
}
|
|
1964
|
+
function parseWebSocketUrl(value) {
|
|
1965
|
+
if (typeof value !== "string")
|
|
1966
|
+
return void 0;
|
|
1967
|
+
let url;
|
|
1968
|
+
try {
|
|
1969
|
+
url = new URL(value);
|
|
1970
|
+
} catch {
|
|
1971
|
+
return void 0;
|
|
1972
|
+
}
|
|
1973
|
+
if (url.protocol !== "ws:" || !isLoopbackHostname(url.hostname) || url.username !== "" || url.password !== "" || !hasValidPort(url) || url.pathname !== BROWSER_RELAY_CDP_PATH || url.search !== "" || url.hash !== "") {
|
|
1974
|
+
return void 0;
|
|
1975
|
+
}
|
|
1976
|
+
return url;
|
|
1977
|
+
}
|
|
1978
|
+
function isLoopbackHostname(hostname) {
|
|
1979
|
+
const normalized = hostname.replace(/^\[(.*)\]$/u, "$1").toLowerCase();
|
|
1980
|
+
if (normalized === "localhost" || normalized === "::1")
|
|
1981
|
+
return true;
|
|
1982
|
+
if (net2.isIPv4(normalized))
|
|
1983
|
+
return normalized.startsWith("127.");
|
|
1984
|
+
if (!net2.isIPv6(normalized))
|
|
1985
|
+
return false;
|
|
1986
|
+
return normalized.startsWith("::ffff:127.") || /^::ffff:7f[0-9a-f]{2}:[0-9a-f]{1,4}$/u.test(normalized);
|
|
1987
|
+
}
|
|
1988
|
+
function hasValidPort(url) {
|
|
1989
|
+
if (url.port === "")
|
|
1990
|
+
return true;
|
|
1991
|
+
const port = Number(url.port);
|
|
1992
|
+
return Number.isSafeInteger(port) && port >= 1 && port <= 65535;
|
|
1993
|
+
}
|
|
1994
|
+
function isPlainObject(value) {
|
|
1995
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
1996
|
+
}
|
|
1997
|
+
function hasExactKeys(value, expected) {
|
|
1998
|
+
const actual = Object.keys(value).toSorted();
|
|
1999
|
+
return actual.length === expected.length && actual.every((key, index) => key === expected[index]);
|
|
2000
|
+
}
|
|
2001
|
+
function hasDuplicateObjectKeys(node) {
|
|
2002
|
+
if (node.type === "object") {
|
|
2003
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2004
|
+
for (const property of node.children ?? []) {
|
|
2005
|
+
const key = property.children?.[0]?.value;
|
|
2006
|
+
if (typeof key !== "string" || seen.has(key))
|
|
2007
|
+
return true;
|
|
2008
|
+
seen.add(key);
|
|
2009
|
+
}
|
|
2010
|
+
}
|
|
2011
|
+
return (node.children ?? []).some(hasDuplicateObjectKeys);
|
|
2012
|
+
}
|
|
2013
|
+
function openClawDiscoveryEnvironment(env) {
|
|
2014
|
+
const filtered = Object.fromEntries(OPENCLAW_RELAY_DISCOVERY_ENV_KEYS.flatMap((key) => {
|
|
2015
|
+
const value = env[key];
|
|
2016
|
+
return value === void 0 ? [] : [[key, value]];
|
|
2017
|
+
}));
|
|
2018
|
+
const profile = normalizeOpenClawProfile(filtered.OPENCLAW_PROFILE);
|
|
2019
|
+
if (profile)
|
|
2020
|
+
filtered.OPENCLAW_PROFILE = profile;
|
|
2021
|
+
else
|
|
2022
|
+
delete filtered.OPENCLAW_PROFILE;
|
|
2023
|
+
return filtered;
|
|
2024
|
+
}
|
|
2025
|
+
|
|
2026
|
+
// node_modules/.pnpm/mcporter@0.13.10/node_modules/mcporter/dist/chrome-devtools-relay.js
|
|
2027
|
+
var DEFAULT_RELAY_PROBE_TIMEOUT_MS = 5e3;
|
|
2028
|
+
var MIN_RELAY_PROBE_TIMEOUT_MS = 100;
|
|
2029
|
+
var MAX_RELAY_PROBE_TIMEOUT_MS = 3e4;
|
|
2030
|
+
var DEFAULT_RELAY_URL = "http://127.0.0.1:18799";
|
|
2031
|
+
var DEFAULT_RELAY_IDENTITY_URL = new URL(DEFAULT_RELAY_URL).toString();
|
|
2032
|
+
var RELAY_DEPENDENCY_SENTINEL = "$MCPORTER_CHROME_RELAY_DEPENDENCY:";
|
|
2033
|
+
var RELAY_DISCOVERY_SENTINEL = "$MCPORTER_CHROME_RELAY_DISCOVERY:";
|
|
2034
|
+
var CHROME_DEVTOOLS_RELAY_RUNTIME_ENV_KEYS = [
|
|
2035
|
+
"MCPORTER_CHROME_DEVTOOLS_RELAY_POLICY",
|
|
2036
|
+
"MCPORTER_DISABLE_CHROME_DEVTOOLS_RELAY",
|
|
2037
|
+
"MCPORTER_CHROME_DEVTOOLS_RELAY_URL",
|
|
2038
|
+
"MCPORTER_CHROME_DEVTOOLS_RELAY_TIMEOUT_MS",
|
|
2039
|
+
...OPENCLAW_RELAY_DISCOVERY_ENV_KEYS
|
|
2040
|
+
];
|
|
2041
|
+
var ChromeDevtoolsRelayRequiredError = class extends Error {
|
|
2042
|
+
decision;
|
|
2043
|
+
constructor(decision) {
|
|
2044
|
+
super(`Chrome DevTools relay policy 'require' could not use the OpenClaw extension relay (${decision.reason}). No chrome-devtools-mcp process was launched.`);
|
|
2045
|
+
this.decision = decision;
|
|
2046
|
+
this.name = "ChromeDevtoolsRelayRequiredError";
|
|
2047
|
+
}
|
|
2048
|
+
};
|
|
2049
|
+
var lastDecisions = /* @__PURE__ */ new Map();
|
|
2050
|
+
function recordChromeDevtoolsRelayDecision(server, decision) {
|
|
2051
|
+
lastDecisions.set(server, decision);
|
|
2052
|
+
}
|
|
2053
|
+
function getChromeDevtoolsRelayDecision(server) {
|
|
2054
|
+
return lastDecisions.get(server);
|
|
2055
|
+
}
|
|
2056
|
+
function formatChromeDevtoolsRelayDecision(decision) {
|
|
2057
|
+
return JSON.stringify(decision);
|
|
2058
|
+
}
|
|
2059
|
+
function resolveChromeDevtoolsRelayProbeTimeoutMs(env = process.env) {
|
|
2060
|
+
const raw = env.MCPORTER_CHROME_DEVTOOLS_RELAY_TIMEOUT_MS?.trim();
|
|
2061
|
+
if (!raw || !/^\d+$/.test(raw))
|
|
2062
|
+
return DEFAULT_RELAY_PROBE_TIMEOUT_MS;
|
|
2063
|
+
const parsed = Number(raw);
|
|
2064
|
+
if (!Number.isSafeInteger(parsed) || parsed <= 0)
|
|
2065
|
+
return DEFAULT_RELAY_PROBE_TIMEOUT_MS;
|
|
2066
|
+
return Math.min(MAX_RELAY_PROBE_TIMEOUT_MS, Math.max(MIN_RELAY_PROBE_TIMEOUT_MS, parsed));
|
|
2067
|
+
}
|
|
2068
|
+
function resolveChromeDevtoolsRelayPolicy(configured, env = process.env) {
|
|
2069
|
+
if (env.MCPORTER_DISABLE_CHROME_DEVTOOLS_RELAY === "1")
|
|
2070
|
+
return "off";
|
|
2071
|
+
const raw = env.MCPORTER_CHROME_DEVTOOLS_RELAY_POLICY?.trim().toLowerCase();
|
|
2072
|
+
if (!raw)
|
|
2073
|
+
return configured ?? "prefer";
|
|
2074
|
+
if (raw === "off" || raw === "prefer" || raw === "require")
|
|
2075
|
+
return raw;
|
|
2076
|
+
throw new Error(`Invalid MCPORTER_CHROME_DEVTOOLS_RELAY_POLICY '${raw}'. Expected off, prefer, or require.`);
|
|
2077
|
+
}
|
|
2078
|
+
function isChromeDevtoolsAutoConnectCommand(command, args) {
|
|
2079
|
+
return resolveChromeDevtoolsAutoConnectCommand(command, args).enabled;
|
|
2080
|
+
}
|
|
2081
|
+
function explicitRelayBaseUrl(env) {
|
|
2082
|
+
const raw = env.MCPORTER_CHROME_DEVTOOLS_RELAY_URL?.trim();
|
|
2083
|
+
if (!raw)
|
|
2084
|
+
return { explicit: false };
|
|
2085
|
+
let url;
|
|
2086
|
+
try {
|
|
2087
|
+
url = new URL(raw);
|
|
2088
|
+
} catch {
|
|
2089
|
+
return { explicit: true };
|
|
2090
|
+
}
|
|
2091
|
+
if (url.protocol !== "http:" || !["127.0.0.1", "localhost", "[::1]", "::1"].includes(url.hostname) || url.username !== "" || url.password !== "" || url.search !== "" || url.hash !== "") {
|
|
2092
|
+
return { explicit: true };
|
|
2093
|
+
}
|
|
2094
|
+
return { explicit: true, url };
|
|
2095
|
+
}
|
|
2096
|
+
function resolveOpenClawCredentialDir(env) {
|
|
2097
|
+
const oauthOverride = env.OPENCLAW_OAUTH_DIR?.trim();
|
|
2098
|
+
if (oauthOverride)
|
|
2099
|
+
return resolveOpenClawPath(oauthOverride, env);
|
|
2100
|
+
const stateOverride = env.OPENCLAW_STATE_DIR?.trim();
|
|
2101
|
+
const profile = normalizeOpenClawProfile(env.OPENCLAW_PROFILE);
|
|
2102
|
+
const stateDir = stateOverride ? resolveOpenClawPath(stateOverride, env) : path2.join(resolveOpenClawHome(env), profile ? `.openclaw-${profile}` : ".openclaw");
|
|
2103
|
+
return path2.join(stateDir, "credentials");
|
|
2104
|
+
}
|
|
2105
|
+
function resolveOpenClawHome(env) {
|
|
2106
|
+
const override = env.OPENCLAW_HOME?.trim();
|
|
2107
|
+
const osHome = resolveOpenClawOsHome(env);
|
|
2108
|
+
if (!override)
|
|
2109
|
+
return osHome;
|
|
2110
|
+
if (override === "~")
|
|
2111
|
+
return osHome;
|
|
2112
|
+
if (override.startsWith("~/") || override.startsWith("~\\")) {
|
|
2113
|
+
return path2.resolve(osHome, override.slice(2));
|
|
2114
|
+
}
|
|
2115
|
+
return path2.resolve(override);
|
|
2116
|
+
}
|
|
2117
|
+
function resolveOpenClawOsHome(env) {
|
|
2118
|
+
for (const value of [env.HOME, env.USERPROFILE]) {
|
|
2119
|
+
const normalized = value?.trim();
|
|
2120
|
+
if (normalized && normalized !== "undefined" && normalized !== "null")
|
|
2121
|
+
return path2.resolve(normalized);
|
|
2122
|
+
}
|
|
2123
|
+
return os.homedir();
|
|
2124
|
+
}
|
|
2125
|
+
function resolveOpenClawPath(value, env) {
|
|
2126
|
+
if (value === "~")
|
|
2127
|
+
return resolveOpenClawHome(env);
|
|
2128
|
+
if (value.startsWith("~/") || value.startsWith("~\\")) {
|
|
2129
|
+
return path2.resolve(resolveOpenClawHome(env), value.slice(2));
|
|
2130
|
+
}
|
|
2131
|
+
return path2.resolve(value);
|
|
2132
|
+
}
|
|
2133
|
+
function defaultReadCredential(env) {
|
|
2134
|
+
const secretPath = path2.join(resolveOpenClawCredentialDir(env), "browser-extension-relay.secret");
|
|
2135
|
+
return readCredentialPath(secretPath);
|
|
2136
|
+
}
|
|
2137
|
+
function readCredentialPath(secretPath) {
|
|
2138
|
+
let descriptor;
|
|
2139
|
+
try {
|
|
2140
|
+
descriptor = fs.openSync(secretPath, fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW ?? 0));
|
|
2141
|
+
} catch (error) {
|
|
2142
|
+
return { reason: isErrno(error, "ENOENT") ? "missing-credential" : "invalid-credential" };
|
|
2143
|
+
}
|
|
2144
|
+
try {
|
|
2145
|
+
const stat = fs.fstatSync(descriptor);
|
|
2146
|
+
if (!stat.isFile())
|
|
2147
|
+
return { reason: "invalid-credential" };
|
|
2148
|
+
if (process.platform !== "win32") {
|
|
2149
|
+
if ((stat.mode & 63) !== 0)
|
|
2150
|
+
return { reason: "invalid-credential" };
|
|
2151
|
+
if (typeof process.getuid === "function" && stat.uid !== process.getuid())
|
|
2152
|
+
return { reason: "invalid-credential" };
|
|
2153
|
+
}
|
|
2154
|
+
const encoded = fs.readFileSync(descriptor, "utf8").trim();
|
|
2155
|
+
if (!/^[0-9a-f]{64}$/.test(encoded))
|
|
2156
|
+
return { reason: "invalid-credential" };
|
|
2157
|
+
const key = Buffer.from(encoded, "hex");
|
|
2158
|
+
return { credential: { key, keyId: deriveBrowserRelayKeyId(key) } };
|
|
2159
|
+
} catch {
|
|
2160
|
+
return { reason: "invalid-credential" };
|
|
2161
|
+
} finally {
|
|
2162
|
+
fs.closeSync(descriptor);
|
|
2163
|
+
}
|
|
2164
|
+
}
|
|
2165
|
+
async function defaultConnect(baseUrl, credential, timeoutMs) {
|
|
2166
|
+
return await connectChromeDevtoolsRelayV2({ baseUrl, credential, timeoutMs });
|
|
2167
|
+
}
|
|
2168
|
+
async function rewriteChromeDevtoolsArgsForRelay(command, args, env = process.env, options = {}, configuredPolicy) {
|
|
2169
|
+
const relayEnv = selectChromeDevtoolsRelayRuntimeEnvironment(env);
|
|
2170
|
+
const eligible = isChromeDevtoolsAutoConnectCommand(command, args);
|
|
2171
|
+
if (!eligible) {
|
|
2172
|
+
const ambiguous = isAmbiguousChromeDevtoolsAutoConnectCommand(command, args);
|
|
2173
|
+
if (!ambiguous) {
|
|
2174
|
+
return decide({
|
|
2175
|
+
args,
|
|
2176
|
+
applied: false,
|
|
2177
|
+
decision: { route: "legacy", reason: "not-eligible", policy: configuredPolicy ?? "prefer" }
|
|
2178
|
+
}, options);
|
|
2179
|
+
}
|
|
2180
|
+
const ambiguousPolicy = resolveChromeDevtoolsRelayPolicy(configuredPolicy, relayEnv);
|
|
2181
|
+
if (ambiguousPolicy === "require") {
|
|
2182
|
+
return unavailableOrLegacy(args, ambiguousPolicy, "unsupported-command", void 0, options);
|
|
2183
|
+
}
|
|
2184
|
+
return decide({
|
|
2185
|
+
args,
|
|
2186
|
+
applied: false,
|
|
2187
|
+
decision: { route: "legacy", reason: "not-eligible", policy: ambiguousPolicy }
|
|
2188
|
+
}, options);
|
|
2189
|
+
}
|
|
2190
|
+
const policy = resolveChromeDevtoolsRelayPolicy(configuredPolicy, relayEnv);
|
|
2191
|
+
if (policy === "off") {
|
|
2192
|
+
return decide({ args, applied: false, decision: { route: "legacy", reason: "disabled", policy } }, options);
|
|
2193
|
+
}
|
|
2194
|
+
const explicitBase = explicitRelayBaseUrl(relayEnv);
|
|
2195
|
+
if (explicitBase.explicit && !explicitBase.url) {
|
|
2196
|
+
return unavailableOrLegacy(args, policy, "invalid-endpoint", void 0, options);
|
|
2197
|
+
}
|
|
2198
|
+
const loaded = options.readToken ? (() => {
|
|
2199
|
+
const encoded = options.readToken?.();
|
|
2200
|
+
if (!encoded)
|
|
2201
|
+
return { reason: "missing-credential" };
|
|
2202
|
+
if (!/^[0-9a-f]{64}$/u.test(encoded))
|
|
2203
|
+
return { reason: "invalid-credential" };
|
|
2204
|
+
const key = Buffer.from(encoded, "hex");
|
|
2205
|
+
return { credential: { key, keyId: deriveBrowserRelayKeyId(key) } };
|
|
2206
|
+
})() : defaultReadCredential(relayEnv);
|
|
2207
|
+
if (!loaded.credential) {
|
|
2208
|
+
const base = explicitBase.url ?? new URL(DEFAULT_RELAY_URL);
|
|
2209
|
+
const upstreamEndpoint = new URL("/cdp", base);
|
|
2210
|
+
upstreamEndpoint.protocol = "ws:";
|
|
2211
|
+
return unavailableOrLegacy(args, policy, loaded.reason ?? "invalid-credential", upstreamEndpoint.toString(), options);
|
|
2212
|
+
}
|
|
2213
|
+
const timeoutMs = resolveChromeDevtoolsRelayProbeTimeoutMs(relayEnv);
|
|
2214
|
+
try {
|
|
2215
|
+
const base = explicitBase.url ?? (await discoverOpenClawRelayUrl({
|
|
2216
|
+
env: relayEnv,
|
|
2217
|
+
keyId: loaded.credential.keyId,
|
|
2218
|
+
timeoutMs,
|
|
2219
|
+
run: options.discover,
|
|
2220
|
+
platform: options.discovery?.platform,
|
|
2221
|
+
isFile: options.discovery?.isFile,
|
|
2222
|
+
cwd: options.discovery?.cwd
|
|
2223
|
+
})).url ?? new URL(DEFAULT_RELAY_URL);
|
|
2224
|
+
const upstreamEndpoint = new URL("/cdp", base);
|
|
2225
|
+
upstreamEndpoint.protocol = "ws:";
|
|
2226
|
+
const endpoint = upstreamEndpoint.toString();
|
|
2227
|
+
let probe;
|
|
2228
|
+
try {
|
|
2229
|
+
probe = await (options.connect ?? defaultConnect)(base, loaded.credential, timeoutMs);
|
|
2230
|
+
} catch {
|
|
2231
|
+
return unavailableOrLegacy(args, policy, "network-error", endpoint, options);
|
|
2232
|
+
}
|
|
2233
|
+
const probeDetails = { probeDurationMs: probe.durationMs, probeStatus: probe.status };
|
|
2234
|
+
if (probe.reason !== "success" || !probe.upstream) {
|
|
2235
|
+
probe.upstream?.socket.destroy();
|
|
2236
|
+
const reason = probe.reason === "success" ? "protocol" : probe.reason;
|
|
2237
|
+
return unavailableOrLegacy(args, policy, reason, endpoint, options, probeDetails);
|
|
2238
|
+
}
|
|
2239
|
+
let proxy;
|
|
2240
|
+
try {
|
|
2241
|
+
proxy = await (options.startProxy ?? startChromeDevtoolsRelayProxy)({ upstream: probe.upstream });
|
|
2242
|
+
} catch {
|
|
2243
|
+
probe.upstream.socket.destroy();
|
|
2244
|
+
return unavailableOrLegacy(args, policy, "network-error", endpoint, options, probeDetails);
|
|
2245
|
+
}
|
|
2246
|
+
const rewritten = replaceChromeDevtoolsAutoConnectArgs(command, args, ["--wsEndpoint", proxy.endpoint]);
|
|
2247
|
+
try {
|
|
2248
|
+
return decide({
|
|
2249
|
+
args: rewritten,
|
|
2250
|
+
applied: true,
|
|
2251
|
+
endpoint: proxy.endpoint,
|
|
2252
|
+
proxy,
|
|
2253
|
+
decision: { route: "relay", reason: "success", policy, endpoint, ...probeDetails }
|
|
2254
|
+
}, options);
|
|
2255
|
+
} catch (error) {
|
|
2256
|
+
await proxy.close().catch(() => {
|
|
2257
|
+
});
|
|
2258
|
+
throw error;
|
|
2259
|
+
}
|
|
2260
|
+
} finally {
|
|
2261
|
+
loaded.credential.key.fill(0);
|
|
2262
|
+
}
|
|
2263
|
+
}
|
|
2264
|
+
function unavailableOrLegacy(args, policy, reason, endpoint, options, probe = {}) {
|
|
2265
|
+
const decision = {
|
|
2266
|
+
route: policy === "require" ? "unavailable" : "legacy",
|
|
2267
|
+
reason,
|
|
2268
|
+
policy,
|
|
2269
|
+
endpoint,
|
|
2270
|
+
...probe
|
|
2271
|
+
};
|
|
2272
|
+
options.onDecision?.(decision);
|
|
2273
|
+
if (policy === "require")
|
|
2274
|
+
throw new ChromeDevtoolsRelayRequiredError(decision);
|
|
2275
|
+
return { args, applied: false, decision };
|
|
2276
|
+
}
|
|
2277
|
+
function decide(result, options) {
|
|
2278
|
+
options.onDecision?.(result.decision);
|
|
2279
|
+
return result;
|
|
2280
|
+
}
|
|
2281
|
+
function resolveChromeDevtoolsRelayEnvironment(overrides, env = process.env) {
|
|
2282
|
+
return { ...env, ...resolveRelayDefinitionEnvOverrides(overrides, env) };
|
|
2283
|
+
}
|
|
2284
|
+
function selectChromeDevtoolsRelayRuntimeEnvironment(env) {
|
|
2285
|
+
return Object.fromEntries(CHROME_DEVTOOLS_RELAY_RUNTIME_ENV_KEYS.flatMap((key) => {
|
|
2286
|
+
const value = env[key];
|
|
2287
|
+
return value === void 0 ? [] : [[key, value]];
|
|
2288
|
+
}));
|
|
2289
|
+
}
|
|
2290
|
+
function sanitizeRelayIdentityEnvironment(env) {
|
|
2291
|
+
const selected = selectChromeDevtoolsRelayRuntimeEnvironment(env);
|
|
2292
|
+
if (selected.MCPORTER_CHROME_DEVTOOLS_RELAY_URL !== void 0) {
|
|
2293
|
+
const explicit = explicitRelayBaseUrl(selected);
|
|
2294
|
+
selected.MCPORTER_CHROME_DEVTOOLS_RELAY_URL = explicit.explicit ? explicit.url?.toString() ?? "invalid-endpoint" : "";
|
|
2295
|
+
}
|
|
2296
|
+
return selected;
|
|
2297
|
+
}
|
|
2298
|
+
function chromeDevtoolsRelayEnvironmentKeys(definitions, env = process.env) {
|
|
2299
|
+
const keys = /* @__PURE__ */ new Set();
|
|
2300
|
+
for (const definition of definitions) {
|
|
2301
|
+
if (definition.command.kind !== "stdio")
|
|
2302
|
+
continue;
|
|
2303
|
+
const commandValues = [definition.command.command, ...definition.command.args];
|
|
2304
|
+
const commandEnvironmentKeys = new Set(commandValues.flatMap(referencedEnvironmentVariables));
|
|
2305
|
+
const relevant = commandEnvironmentKeys.size > 0 || isChromeDevtoolsAutoConnectCommand(definition.command.command, definition.command.args) || isAmbiguousChromeDevtoolsAutoConnectCommand(definition.command.command, definition.command.args);
|
|
2306
|
+
if (!relevant) {
|
|
2307
|
+
continue;
|
|
2308
|
+
}
|
|
2309
|
+
for (const key of commandEnvironmentKeys)
|
|
2310
|
+
keys.add(`${RELAY_DEPENDENCY_SENTINEL}${key}`);
|
|
2311
|
+
const resolvedDefinitionEnv = resolveRelayIdentityEnvOverrides(definition.env, env);
|
|
2312
|
+
for (const key of CHROME_DEVTOOLS_RELAY_RUNTIME_ENV_KEYS) {
|
|
2313
|
+
const override = definition.env?.[key];
|
|
2314
|
+
if (!override)
|
|
2315
|
+
continue;
|
|
2316
|
+
for (const referenced of referencedEnvironmentVariables(override))
|
|
2317
|
+
keys.add(`${RELAY_DEPENDENCY_SENTINEL}${referenced}`);
|
|
2318
|
+
}
|
|
2319
|
+
const resolvedCommand = resolveCommandArgument(definition.command.command, env);
|
|
2320
|
+
const resolvedArgs = resolveCommandArguments(definition.command.args, env);
|
|
2321
|
+
const eligible = isChromeDevtoolsAutoConnectCommand(resolvedCommand, resolvedArgs);
|
|
2322
|
+
if (!eligible && !isAmbiguousChromeDevtoolsAutoConnectCommand(resolvedCommand, resolvedArgs))
|
|
2323
|
+
continue;
|
|
2324
|
+
const environment = sanitizeRelayIdentityEnvironment(resolvedDefinitionEnv);
|
|
2325
|
+
keys.add(`${RELAY_DISCOVERY_SENTINEL}${Buffer.from(JSON.stringify({
|
|
2326
|
+
server: definition.name,
|
|
2327
|
+
eligible,
|
|
2328
|
+
environment,
|
|
2329
|
+
configuredPolicy: definition.chromeDevtoolsRelay
|
|
2330
|
+
}), "utf8").toString("base64url")}`);
|
|
2331
|
+
}
|
|
2332
|
+
return [...keys].toSorted();
|
|
2333
|
+
}
|
|
2334
|
+
async function resolveChromeDevtoolsRelayRuntimeIdentity(keys, env = process.env, options = {}) {
|
|
2335
|
+
const { values, relays } = relayEnvironmentIdentityValues(keys, env);
|
|
2336
|
+
for (const relay of relays) {
|
|
2337
|
+
let endpoint = relay.endpoint;
|
|
2338
|
+
if (relay.discover && relay.keyId !== "missing-credential" && relay.keyId !== "invalid-credential") {
|
|
2339
|
+
try {
|
|
2340
|
+
const discovered = await discoverOpenClawRelayUrl({
|
|
2341
|
+
env: relay.env,
|
|
2342
|
+
keyId: relay.keyId,
|
|
2343
|
+
timeoutMs: relay.timeoutMs,
|
|
2344
|
+
run: options.discover,
|
|
2345
|
+
platform: options.discovery?.platform,
|
|
2346
|
+
isFile: options.discovery?.isFile,
|
|
2347
|
+
cwd: options.discovery?.cwd
|
|
2348
|
+
});
|
|
2349
|
+
endpoint = discovered.url?.toString() ?? DEFAULT_RELAY_IDENTITY_URL;
|
|
2350
|
+
} catch {
|
|
2351
|
+
endpoint = DEFAULT_RELAY_IDENTITY_URL;
|
|
2352
|
+
}
|
|
2353
|
+
}
|
|
2354
|
+
values.push([relay.server, endpoint]);
|
|
2355
|
+
}
|
|
2356
|
+
return hashRelayIdentityValues(values);
|
|
2357
|
+
}
|
|
2358
|
+
function createRelayCredentialIdResolver() {
|
|
2359
|
+
const credentialIds = /* @__PURE__ */ new Map();
|
|
2360
|
+
return (credentialPath) => {
|
|
2361
|
+
const cached = credentialIds.get(credentialPath);
|
|
2362
|
+
if (cached)
|
|
2363
|
+
return cached;
|
|
2364
|
+
const loaded = readCredentialPath(credentialPath);
|
|
2365
|
+
const value = loaded.credential?.keyId ?? loaded.reason ?? "missing-credential";
|
|
2366
|
+
loaded.credential?.key.fill(0);
|
|
2367
|
+
credentialIds.set(credentialPath, value);
|
|
2368
|
+
return value;
|
|
2369
|
+
};
|
|
2370
|
+
}
|
|
2371
|
+
function relayEnvironmentIdentityValues(keys, env) {
|
|
2372
|
+
const credentialId = createRelayCredentialIdResolver();
|
|
2373
|
+
const values = [];
|
|
2374
|
+
const relays = [];
|
|
2375
|
+
for (const key of keys) {
|
|
2376
|
+
if (key.startsWith(RELAY_DEPENDENCY_SENTINEL)) {
|
|
2377
|
+
values.push([key, env[key.slice(RELAY_DEPENDENCY_SENTINEL.length)] ?? null]);
|
|
2378
|
+
continue;
|
|
2379
|
+
}
|
|
2380
|
+
if (!key.startsWith(RELAY_DISCOVERY_SENTINEL)) {
|
|
2381
|
+
values.push([key, relayEnvironmentIdentityValue(key, env)]);
|
|
2382
|
+
continue;
|
|
2383
|
+
}
|
|
2384
|
+
const payload = parseRelayDiscoveryIdentity(key.slice(RELAY_DISCOVERY_SENTINEL.length));
|
|
2385
|
+
if (!payload)
|
|
2386
|
+
throw new Error("Invalid Chrome DevTools relay identity metadata");
|
|
2387
|
+
const effectiveEnv = { ...selectChromeDevtoolsRelayRuntimeEnvironment(env), ...payload.environment };
|
|
2388
|
+
const policy = resolveChromeDevtoolsRelayPolicy(payload.configuredPolicy, effectiveEnv);
|
|
2389
|
+
const timeoutMs = resolveChromeDevtoolsRelayProbeTimeoutMs(effectiveEnv);
|
|
2390
|
+
const explicit = explicitRelayBaseUrl(effectiveEnv);
|
|
2391
|
+
const credentialDir = resolveOpenClawCredentialDir(effectiveEnv);
|
|
2392
|
+
const keyId = payload.eligible ? credentialId(path2.join(credentialDir, "browser-extension-relay.secret")) : "not-eligible";
|
|
2393
|
+
const server = `$relay:${payload.server}`;
|
|
2394
|
+
values.push([
|
|
2395
|
+
server,
|
|
2396
|
+
JSON.stringify({
|
|
2397
|
+
policy,
|
|
2398
|
+
timeoutMs,
|
|
2399
|
+
eligible: payload.eligible,
|
|
2400
|
+
credentialDir,
|
|
2401
|
+
keyId,
|
|
2402
|
+
// These values also reach the discovery subprocess; retain inputs whose equivalence it does not guarantee.
|
|
2403
|
+
discovery: OPENCLAW_RELAY_DISCOVERY_ENV_KEYS.map((name) => [
|
|
2404
|
+
name,
|
|
2405
|
+
relayEnvironmentIdentityValue(name, effectiveEnv)
|
|
2406
|
+
]),
|
|
2407
|
+
cwd: relayIdentityDependsOnCwd(effectiveEnv) ? process.cwd() : null
|
|
2408
|
+
})
|
|
2409
|
+
]);
|
|
2410
|
+
relays.push({
|
|
2411
|
+
server,
|
|
2412
|
+
env: effectiveEnv,
|
|
2413
|
+
timeoutMs,
|
|
2414
|
+
keyId,
|
|
2415
|
+
endpoint: policy === "off" ? "disabled" : explicit.explicit ? explicit.url?.toString() ?? "invalid-endpoint" : DEFAULT_RELAY_IDENTITY_URL,
|
|
2416
|
+
discover: payload.eligible && policy !== "off" && !explicit.explicit
|
|
2417
|
+
});
|
|
2418
|
+
}
|
|
2419
|
+
if (keys.length > 0)
|
|
2420
|
+
values.push(["$relayAuth", "v2-effective-metadata-discovery"]);
|
|
2421
|
+
return { values, relays };
|
|
2422
|
+
}
|
|
2423
|
+
function relayEnvironmentIdentityValue(key, env) {
|
|
2424
|
+
switch (key) {
|
|
2425
|
+
case "MCPORTER_CHROME_DEVTOOLS_RELAY_POLICY":
|
|
2426
|
+
case "MCPORTER_DISABLE_CHROME_DEVTOOLS_RELAY":
|
|
2427
|
+
return resolveChromeDevtoolsRelayPolicy(void 0, env);
|
|
2428
|
+
case "MCPORTER_CHROME_DEVTOOLS_RELAY_TIMEOUT_MS":
|
|
2429
|
+
return resolveChromeDevtoolsRelayProbeTimeoutMs(env);
|
|
2430
|
+
case "MCPORTER_CHROME_DEVTOOLS_RELAY_URL": {
|
|
2431
|
+
const explicit = explicitRelayBaseUrl(env);
|
|
2432
|
+
return explicit.explicit ? explicit.url?.toString() ?? "invalid-endpoint" : DEFAULT_RELAY_IDENTITY_URL;
|
|
2433
|
+
}
|
|
2434
|
+
case "OPENCLAW_PROFILE":
|
|
2435
|
+
return normalizeOpenClawProfile(env.OPENCLAW_PROFILE) ?? null;
|
|
2436
|
+
default:
|
|
2437
|
+
return env[key] ?? null;
|
|
2438
|
+
}
|
|
2439
|
+
}
|
|
2440
|
+
function relayIdentityDependsOnCwd(env) {
|
|
2441
|
+
const paths = [
|
|
2442
|
+
env.HOME,
|
|
2443
|
+
env.USERPROFILE,
|
|
2444
|
+
env.OPENCLAW_HOME,
|
|
2445
|
+
env.OPENCLAW_STATE_DIR,
|
|
2446
|
+
env.OPENCLAW_OAUTH_DIR,
|
|
2447
|
+
env.OPENCLAW_CONFIG_PATH,
|
|
2448
|
+
...(env.PATH ?? env.Path)?.split(path2.delimiter) ?? []
|
|
2449
|
+
];
|
|
2450
|
+
return paths.some((value) => value !== void 0 && !path2.isAbsolute(value) && !value.startsWith("~"));
|
|
2451
|
+
}
|
|
2452
|
+
function hashRelayIdentityValues(values) {
|
|
2453
|
+
return createHash3("sha256").update(JSON.stringify(values.toSorted((a, b) => a[0].localeCompare(b[0]) || JSON.stringify(a[1]).localeCompare(JSON.stringify(b[1]))))).digest("hex").slice(0, 16);
|
|
2454
|
+
}
|
|
2455
|
+
function parseRelayDiscoveryIdentity(encoded) {
|
|
2456
|
+
try {
|
|
2457
|
+
const value = JSON.parse(Buffer.from(encoded, "base64url").toString("utf8"));
|
|
2458
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
2459
|
+
return void 0;
|
|
2460
|
+
const candidate = value;
|
|
2461
|
+
if (typeof candidate.server !== "string" || typeof candidate.eligible !== "boolean" || !candidate.environment || typeof candidate.environment !== "object")
|
|
2462
|
+
return void 0;
|
|
2463
|
+
if (candidate.configuredPolicy !== void 0 && !["off", "prefer", "require"].includes(candidate.configuredPolicy))
|
|
2464
|
+
return void 0;
|
|
2465
|
+
const environment = {};
|
|
2466
|
+
for (const [key, raw] of Object.entries(candidate.environment)) {
|
|
2467
|
+
if (!CHROME_DEVTOOLS_RELAY_RUNTIME_ENV_KEYS.includes(key) || typeof raw !== "string")
|
|
2468
|
+
return void 0;
|
|
2469
|
+
environment[key] = raw;
|
|
2470
|
+
}
|
|
2471
|
+
return {
|
|
2472
|
+
server: candidate.server,
|
|
2473
|
+
eligible: candidate.eligible,
|
|
2474
|
+
environment,
|
|
2475
|
+
configuredPolicy: candidate.configuredPolicy
|
|
2476
|
+
};
|
|
2477
|
+
} catch {
|
|
2478
|
+
return void 0;
|
|
2479
|
+
}
|
|
2480
|
+
}
|
|
2481
|
+
function referencedEnvironmentVariables(value) {
|
|
2482
|
+
const names = /* @__PURE__ */ new Set();
|
|
2483
|
+
if (value.startsWith("$env:"))
|
|
2484
|
+
names.add(value.slice("$env:".length));
|
|
2485
|
+
for (const match of value.matchAll(/\$\{([A-Za-z_][A-Za-z0-9_]*)/gu)) {
|
|
2486
|
+
if (match[1])
|
|
2487
|
+
names.add(match[1]);
|
|
2488
|
+
}
|
|
2489
|
+
return [...names];
|
|
2490
|
+
}
|
|
2491
|
+
function resolveRelayDefinitionEnvOverrides(overrides, env) {
|
|
2492
|
+
const resolved = {};
|
|
2493
|
+
for (const [key, raw] of Object.entries(overrides ?? {})) {
|
|
2494
|
+
const value = resolveEnvValue(raw, env);
|
|
2495
|
+
if (value !== "")
|
|
2496
|
+
resolved[key] = value;
|
|
2497
|
+
}
|
|
2498
|
+
return resolved;
|
|
2499
|
+
}
|
|
2500
|
+
function resolveRelayIdentityEnvOverrides(overrides, env) {
|
|
2501
|
+
const relevant = Object.fromEntries(CHROME_DEVTOOLS_RELAY_RUNTIME_ENV_KEYS.flatMap((key) => {
|
|
2502
|
+
const value = overrides?.[key];
|
|
2503
|
+
return value === void 0 ? [] : [[key, value]];
|
|
2504
|
+
}));
|
|
2505
|
+
return resolveRelayDefinitionEnvOverrides(relevant, env);
|
|
2506
|
+
}
|
|
2507
|
+
function isErrno(error, code) {
|
|
2508
|
+
return Boolean(error && typeof error === "object" && error.code === code);
|
|
2509
|
+
}
|
|
2510
|
+
|
|
2511
|
+
// node_modules/.pnpm/mcporter@0.13.10/node_modules/mcporter/dist/daemon/connection-identity.js
|
|
2512
|
+
var BROKER_ENV = /* @__PURE__ */ new Set([
|
|
2513
|
+
"MCPORTER_DAEMON_DIR",
|
|
2514
|
+
"MCPORTER_DAEMON_CHILD",
|
|
2515
|
+
"MCPORTER_DAEMON_SOCKET",
|
|
2516
|
+
"MCPORTER_DAEMON_METADATA",
|
|
2517
|
+
"MCPORTER_CONFIG",
|
|
2518
|
+
"MCPORTER_DISABLE_AUTORUN",
|
|
2519
|
+
"MCPORTER_DAEMON_LOG",
|
|
2520
|
+
"MCPORTER_DAEMON_LOG_PATH",
|
|
2521
|
+
"MCPORTER_DAEMON_LOG_SERVERS",
|
|
2522
|
+
"MCPORTER_NO_FORCE_EXIT"
|
|
2523
|
+
]);
|
|
2524
|
+
async function effectiveDefinition(definition, inherited = process.env, purpose = "connection") {
|
|
2525
|
+
const env = Object.fromEntries(Object.entries(resolveChromeDevtoolsRelayEnvironment(definition.env, inherited)).filter(([key, value]) => value !== void 0 && !BROKER_ENV.has(key)));
|
|
2526
|
+
const configuredEnv = Object.fromEntries(Object.keys(definition.env ?? {}).filter((key) => !BROKER_ENV.has(key)).map((key) => [key, env[key] ?? ""]));
|
|
2527
|
+
normalizeChromeEnvironment(definition, env);
|
|
2528
|
+
const chrome = isChromeDefinition(definition);
|
|
2529
|
+
const launchCommand = definition.command.kind === "stdio" ? resolveCommandArgument(definition.command.command, env) : void 0;
|
|
2530
|
+
const command = definition.command.kind === "stdio" ? {
|
|
2531
|
+
...definition.command,
|
|
2532
|
+
command: chrome && purpose === "view" ? launchCommand : await resolveExecutable(launchCommand, env, definition.command.cwd),
|
|
2533
|
+
args: resolveCommandArguments(definition.command.args, env),
|
|
2534
|
+
cwd: await fs2.realpath(definition.command.cwd)
|
|
2535
|
+
} : { ...definition.command, url: new URL(definition.command.url) };
|
|
2536
|
+
if (command.kind === "stdio") {
|
|
2537
|
+
if (!Object.hasOwn(configuredEnv, "PWD"))
|
|
2538
|
+
env.PWD = command.cwd;
|
|
2539
|
+
if (!Object.hasOwn(configuredEnv, "SHLVL"))
|
|
2540
|
+
delete env.SHLVL;
|
|
2541
|
+
}
|
|
2542
|
+
return { ...definition, command, env, configuredEnv, ...chrome ? { launchCommand } : {} };
|
|
2543
|
+
}
|
|
2544
|
+
function normalizeChromeEnvironment(definition, env) {
|
|
2545
|
+
if (isChromeDefinition(definition)) {
|
|
2546
|
+
env.MCPORTER_CHROME_DEVTOOLS_RELAY_POLICY = resolveChromeDevtoolsRelayPolicy(definition.chromeDevtoolsRelay, env);
|
|
2547
|
+
env.MCPORTER_CHROME_DEVTOOLS_RELAY_TIMEOUT_MS = String(resolveChromeDevtoolsRelayProbeTimeoutMs(env));
|
|
2548
|
+
delete env.MCPORTER_DISABLE_CHROME_DEVTOOLS_RELAY;
|
|
2549
|
+
if (!env.OPENCLAW_PROFILE?.trim() || env.OPENCLAW_PROFILE.trim() === "default")
|
|
2550
|
+
delete env.OPENCLAW_PROFILE;
|
|
2551
|
+
if (env.MCPORTER_CHROME_DEVTOOLS_RELAY_URL?.trim())
|
|
2552
|
+
env.MCPORTER_CHROME_DEVTOOLS_RELAY_URL = new URL(env.MCPORTER_CHROME_DEVTOOLS_RELAY_URL.trim()).toString();
|
|
2553
|
+
else
|
|
2554
|
+
delete env.MCPORTER_CHROME_DEVTOOLS_RELAY_URL;
|
|
2555
|
+
}
|
|
2556
|
+
}
|
|
2557
|
+
async function resolveExecutable(command, env, cwd) {
|
|
2558
|
+
const roots = /[/\\]/.test(command) ? [path3.resolve(cwd, command)] : (env.PATH ?? env.Path ?? "").split(path3.delimiter).map((dir) => path3.resolve(cwd, dir, command));
|
|
2559
|
+
const extensions = process.platform === "win32" && !path3.extname(command) ? ["", ...(env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";")] : [""];
|
|
2560
|
+
const candidates = roots.flatMap((root) => extensions.map((extension) => root + extension));
|
|
2561
|
+
for (const candidate of candidates) {
|
|
2562
|
+
try {
|
|
2563
|
+
await fs2.access(candidate, fs2.constants.X_OK);
|
|
2564
|
+
return path3.join(await fs2.realpath(path3.dirname(candidate)), path3.basename(candidate));
|
|
2565
|
+
} catch {
|
|
2566
|
+
}
|
|
2567
|
+
}
|
|
2568
|
+
throw new Error("Unable to resolve configured executable from the effective PATH.");
|
|
2569
|
+
}
|
|
2570
|
+
function connectionIdentity(definition) {
|
|
2571
|
+
const { name, description: _description, source: _source, sources: _sources, allowedTools: _allowed, blockedTools: _blocked, lifecycle: _lifecycle, configuredEnv: _configuredEnv, launchCommand: _launchCommand, logging: _logging, ...connection } = definition;
|
|
2572
|
+
const credentialOwner = definition.auth || definition.command.kind === "http" ? name : void 0;
|
|
2573
|
+
const env = { ...definition.env };
|
|
2574
|
+
const chrome = isChromeDefinition(definition);
|
|
2575
|
+
if (chrome) {
|
|
2576
|
+
env.MCPORTER_CHROME_DEVTOOLS_RELAY_POLICY = resolveChromeDevtoolsRelayPolicy(definition.chromeDevtoolsRelay, env);
|
|
2577
|
+
delete env.MCPORTER_DISABLE_CHROME_DEVTOOLS_RELAY;
|
|
2578
|
+
env.MCPORTER_CHROME_DEVTOOLS_RELAY_TIMEOUT_MS = String(resolveChromeDevtoolsRelayProbeTimeoutMs(env));
|
|
2579
|
+
}
|
|
2580
|
+
return createHash4("sha256").update(stableJsonStringify({
|
|
2581
|
+
...connection,
|
|
2582
|
+
env,
|
|
2583
|
+
credentialOwner,
|
|
2584
|
+
idleTimeoutMs: !chrome && definition.lifecycle?.mode === "keep-alive" ? definition.lifecycle.idleTimeoutMs : void 0,
|
|
2585
|
+
protocolVersion: definition.protocolVersion ?? "auto",
|
|
2586
|
+
httpFetch: definition.httpFetch ?? "default",
|
|
2587
|
+
chromeDevtoolsRelay: chrome ? env.MCPORTER_CHROME_DEVTOOLS_RELAY_POLICY : void 0
|
|
2588
|
+
})).digest("hex");
|
|
2589
|
+
}
|
|
2590
|
+
function isChromeDefinition(definition) {
|
|
2591
|
+
return definition.command.kind === "stdio" && [definition.name, definition.command.command, ...definition.command.args].some((value) => /chrome[-_]devtools(?:[-_]mcp)?/i.test(value));
|
|
2592
|
+
}
|
|
2593
|
+
|
|
2594
|
+
// node_modules/.pnpm/mcporter@0.13.10/node_modules/mcporter/dist/daemon/browser-owner.js
|
|
2595
|
+
import os4 from "node:os";
|
|
2596
|
+
import path6 from "node:path";
|
|
2597
|
+
|
|
2598
|
+
// node_modules/.pnpm/mcporter@0.13.10/node_modules/mcporter/dist/daemon/paths.js
|
|
2599
|
+
import fs4 from "node:fs/promises";
|
|
2600
|
+
import { constants } from "node:fs";
|
|
2601
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
2602
|
+
|
|
2603
|
+
// node_modules/.pnpm/mcporter@0.13.10/node_modules/mcporter/dist/chrome-devtools-relay-handoff.js
|
|
2604
|
+
import { spawnSync } from "node:child_process";
|
|
2605
|
+
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
2606
|
+
import fs3 from "node:fs";
|
|
2607
|
+
import os2 from "node:os";
|
|
2608
|
+
import path4 from "node:path";
|
|
2609
|
+
import { pathToFileURL } from "node:url";
|
|
2610
|
+
var CHROME_RELAY_HANDOFF_ENV = "MCPORTER_CHROME_RELAY_HANDOFF_PATH";
|
|
2611
|
+
var HANDOFF_DIR_PREFIX = "mcporter-chrome-relay-";
|
|
2612
|
+
var HANDOFF_FILENAME = "headers.json";
|
|
2613
|
+
var PRELOAD_FILENAME = "preload.mjs";
|
|
2614
|
+
var WINDOWS_ACL_PATH_ENV = "MCPORTER_CHROME_RELAY_ACL_PATH";
|
|
2615
|
+
function createChromeDevtoolsRelayHandoff(env, clientAuthorization) {
|
|
2616
|
+
let directory;
|
|
2617
|
+
try {
|
|
2618
|
+
if (process.platform === "win32")
|
|
2619
|
+
directory = createWindowsHandoffDirectory();
|
|
2620
|
+
else {
|
|
2621
|
+
directory = fs3.mkdtempSync(path4.join(os2.tmpdir(), HANDOFF_DIR_PREFIX));
|
|
2622
|
+
fs3.chmodSync(directory, 448);
|
|
2623
|
+
}
|
|
2624
|
+
assertSecurePath(directory, "directory");
|
|
2625
|
+
const handoffPath = path4.join(directory, HANDOFF_FILENAME);
|
|
2626
|
+
const preloadPath = path4.join(directory, PRELOAD_FILENAME);
|
|
2627
|
+
writeExclusiveFile(preloadPath, renderChromeDevtoolsRelayPreloadSource());
|
|
2628
|
+
writeExclusiveFile(handoffPath, JSON.stringify({ Authorization: clientAuthorization }));
|
|
2629
|
+
const importFlag = `--import=${pathToFileURL(preloadPath).href}`;
|
|
2630
|
+
const existingOptions = env.NODE_OPTIONS?.trim();
|
|
2631
|
+
let closed = false;
|
|
2632
|
+
return {
|
|
2633
|
+
env: {
|
|
2634
|
+
...env,
|
|
2635
|
+
NODE_OPTIONS: existingOptions ? `${existingOptions} ${importFlag}` : importFlag,
|
|
2636
|
+
[CHROME_RELAY_HANDOFF_ENV]: handoffPath
|
|
2637
|
+
},
|
|
2638
|
+
handoffPath,
|
|
2639
|
+
preloadPath,
|
|
2640
|
+
async close() {
|
|
2641
|
+
if (closed)
|
|
2642
|
+
return;
|
|
2643
|
+
closed = true;
|
|
2644
|
+
cleanupHandoff(directory, handoffPath, preloadPath);
|
|
2645
|
+
}
|
|
2646
|
+
};
|
|
2647
|
+
} catch (error) {
|
|
2648
|
+
if (directory)
|
|
2649
|
+
cleanupHandoff(directory, path4.join(directory, HANDOFF_FILENAME), path4.join(directory, PRELOAD_FILENAME));
|
|
2650
|
+
const diagnostic = error instanceof Error && error.message.startsWith("Windows ACL setup failed:") ? ` ${error.message}` : "";
|
|
2651
|
+
throw new Error(`Unable to install secure Chrome relay authorization handoff.${diagnostic}`, { cause: error });
|
|
2652
|
+
}
|
|
2653
|
+
}
|
|
2654
|
+
function createWindowsHandoffDirectory() {
|
|
2655
|
+
const directory = path4.join(os2.tmpdir(), `${HANDOFF_DIR_PREFIX}${randomBytes3(16).toString("hex")}`);
|
|
2656
|
+
ensureWindowsPrivateDirectory(directory);
|
|
2657
|
+
return directory;
|
|
2658
|
+
}
|
|
2659
|
+
function ensureWindowsPrivateDirectory(directory, allowExisting = false) {
|
|
2660
|
+
const script = String.raw`
|
|
2661
|
+
$ErrorActionPreference = 'Stop'
|
|
2662
|
+
$stage = 'identity'
|
|
2663
|
+
try {
|
|
2664
|
+
$target = $env:${WINDOWS_ACL_PATH_ENV}
|
|
2665
|
+
${allowExisting ? "" : "if ([System.IO.Directory]::Exists($target)) { throw 'handoff path already exists' }"}
|
|
2666
|
+
if ([System.IO.Directory]::Exists($target) -and (([System.IO.File]::GetAttributes($target) -band [System.IO.FileAttributes]::ReparsePoint) -ne 0)) { throw 'unsafe reparse point' }
|
|
2667
|
+
$sid = [System.Security.Principal.WindowsIdentity]::GetCurrent().User
|
|
2668
|
+
$stage = 'descriptor'
|
|
2669
|
+
$security = New-Object System.Security.AccessControl.DirectorySecurity
|
|
2670
|
+
$sidValue = $sid.Value
|
|
2671
|
+
$security.SetSecurityDescriptorSddlForm("O:$($sidValue)G:$($sidValue)D:P(A;OICI;FA;;;$($sidValue))")
|
|
2672
|
+
$directory = New-Object System.IO.DirectoryInfo -ArgumentList @($target)
|
|
2673
|
+
$stage = 'create'
|
|
2674
|
+
$directory.Create($security)
|
|
2675
|
+
$stage = 'acl-read'
|
|
2676
|
+
$check = $directory.GetAccessControl([System.Security.AccessControl.AccessControlSections]::All)
|
|
2677
|
+
$rules = @($check.GetAccessRules($true, $true, [System.Security.Principal.SecurityIdentifier]))
|
|
2678
|
+
$ownerSid = $check.GetOwner([System.Security.Principal.SecurityIdentifier]).Value
|
|
2679
|
+
$stage = 'acl-verify'
|
|
2680
|
+
if (-not $check.AreAccessRulesProtected -or $ownerSid -ne $sid.Value -or $rules.Count -ne 1) { throw 'unsafe ACL' }
|
|
2681
|
+
$ruleSid = $rules[0].IdentityReference.Value
|
|
2682
|
+
$hasFullControl = ($rules[0].FileSystemRights -band [System.Security.AccessControl.FileSystemRights]::FullControl) -eq [System.Security.AccessControl.FileSystemRights]::FullControl
|
|
2683
|
+
if ($ruleSid -ne $sid.Value -or $rules[0].IsInherited -or -not $hasFullControl -or $rules[0].AccessControlType -ne [System.Security.AccessControl.AccessControlType]::Allow) { throw 'unsafe ACL' }
|
|
2684
|
+
} catch {
|
|
2685
|
+
$type = $_.Exception.GetType().FullName
|
|
2686
|
+
$hresult = ('0x{0:X8}' -f ($_.Exception.HResult -band 0xffffffffL))
|
|
2687
|
+
[Console]::Error.WriteLine("stage=$stage type=$type hresult=$hresult")
|
|
2688
|
+
exit 1
|
|
2689
|
+
}
|
|
2690
|
+
`;
|
|
2691
|
+
const result = spawnSync(resolveSystemPowerShellPath(), ["-NoLogo", "-NoProfile", "-NonInteractive", "-Command", script], {
|
|
2692
|
+
encoding: "utf8",
|
|
2693
|
+
env: { ...process.env, [WINDOWS_ACL_PATH_ENV]: directory },
|
|
2694
|
+
stdio: ["ignore", "ignore", "pipe"],
|
|
2695
|
+
windowsHide: true
|
|
2696
|
+
});
|
|
2697
|
+
if (result.status !== 0) {
|
|
2698
|
+
try {
|
|
2699
|
+
if (!allowExisting)
|
|
2700
|
+
fs3.rmdirSync(directory);
|
|
2701
|
+
} catch {
|
|
2702
|
+
}
|
|
2703
|
+
const stderr = typeof result.stderr === "string" ? result.stderr.trim() : "";
|
|
2704
|
+
const safeDetail = /^[A-Za-z0-9_.:=\- ]{1,240}$/u.test(stderr) ? stderr : `stage=spawn type=${result.error?.code ?? "unknown"} hresult=unknown`;
|
|
2705
|
+
throw new Error(`Windows ACL setup failed: ${safeDetail}`);
|
|
2706
|
+
}
|
|
2707
|
+
}
|
|
2708
|
+
function resolveSystemPowerShellPath() {
|
|
2709
|
+
const systemRoot = process.env.SystemRoot?.trim() || process.env.WINDIR?.trim();
|
|
2710
|
+
if (!systemRoot || !path4.win32.isAbsolute(systemRoot))
|
|
2711
|
+
throw new Error("unsafe Windows system root");
|
|
2712
|
+
const executable = path4.win32.join(systemRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe");
|
|
2713
|
+
const stat = fs3.lstatSync(executable);
|
|
2714
|
+
if (!stat.isFile() || stat.isSymbolicLink())
|
|
2715
|
+
throw new Error("unsafe Windows PowerShell path");
|
|
2716
|
+
return executable;
|
|
2717
|
+
}
|
|
2718
|
+
function writeExclusiveFile(filePath, contents) {
|
|
2719
|
+
const descriptor = fs3.openSync(filePath, fs3.constants.O_WRONLY | fs3.constants.O_CREAT | fs3.constants.O_EXCL | (fs3.constants.O_NOFOLLOW ?? 0), 384);
|
|
2720
|
+
try {
|
|
2721
|
+
if (process.platform !== "win32")
|
|
2722
|
+
fs3.fchmodSync(descriptor, 384);
|
|
2723
|
+
fs3.writeFileSync(descriptor, contents, "utf8");
|
|
2724
|
+
} finally {
|
|
2725
|
+
fs3.closeSync(descriptor);
|
|
2726
|
+
}
|
|
2727
|
+
assertSecurePath(filePath, "file");
|
|
2728
|
+
}
|
|
2729
|
+
function assertSecurePath(filePath, kind) {
|
|
2730
|
+
const stat = fs3.lstatSync(filePath);
|
|
2731
|
+
if ((kind === "directory" ? !stat.isDirectory() : !stat.isFile()) || stat.isSymbolicLink())
|
|
2732
|
+
throw new Error("unsafe");
|
|
2733
|
+
if (process.platform === "win32")
|
|
2734
|
+
return;
|
|
2735
|
+
if ((stat.mode & 63) !== 0)
|
|
2736
|
+
throw new Error("unsafe");
|
|
2737
|
+
if (typeof process.getuid === "function" && stat.uid !== process.getuid())
|
|
2738
|
+
throw new Error("unsafe");
|
|
2739
|
+
}
|
|
2740
|
+
function cleanupHandoff(directory, handoffPath, preloadPath) {
|
|
2741
|
+
for (const filePath of [handoffPath, preloadPath]) {
|
|
2742
|
+
try {
|
|
2743
|
+
fs3.unlinkSync(filePath);
|
|
2744
|
+
} catch {
|
|
2745
|
+
}
|
|
2746
|
+
}
|
|
2747
|
+
try {
|
|
2748
|
+
fs3.rmdirSync(directory);
|
|
2749
|
+
} catch {
|
|
2750
|
+
}
|
|
2751
|
+
}
|
|
2752
|
+
function renderChromeDevtoolsRelayPreloadSource() {
|
|
2753
|
+
return `import fs from 'node:fs';
|
|
2754
|
+
import path from 'node:path';
|
|
2755
|
+
|
|
2756
|
+
const targetPath = process.argv[1]?.replaceAll('\\\\', '/').toLowerCase();
|
|
2757
|
+
if (targetPath?.includes('chrome-devtools-mcp')) {
|
|
2758
|
+
const failureMessage = 'MCPorter Chrome relay authorization handoff unavailable.';
|
|
2759
|
+
const fail = () => {
|
|
2760
|
+
throw new Error(failureMessage);
|
|
2761
|
+
};
|
|
2762
|
+
const envName = ${JSON.stringify(CHROME_RELAY_HANDOFF_ENV)};
|
|
2763
|
+
const handoffPath = process.env[envName];
|
|
2764
|
+
delete process.env[envName];
|
|
2765
|
+
if (!handoffPath || !path.isAbsolute(handoffPath)) fail();
|
|
2766
|
+
|
|
2767
|
+
const directoryStat = (() => {
|
|
2768
|
+
try {
|
|
2769
|
+
return fs.lstatSync(path.dirname(handoffPath));
|
|
2770
|
+
} catch {
|
|
2771
|
+
return fail();
|
|
2772
|
+
}
|
|
2773
|
+
})();
|
|
2774
|
+
if (!directoryStat.isDirectory() || directoryStat.isSymbolicLink()) fail();
|
|
2775
|
+
if (process.platform !== 'win32') {
|
|
2776
|
+
if ((directoryStat.mode & 0o077) !== 0) fail();
|
|
2777
|
+
if (typeof process.getuid === 'function' && directoryStat.uid !== process.getuid()) fail();
|
|
2778
|
+
}
|
|
2779
|
+
|
|
2780
|
+
const descriptor = (() => {
|
|
2781
|
+
try {
|
|
2782
|
+
return fs.openSync(handoffPath, fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW ?? 0));
|
|
2783
|
+
} catch {
|
|
2784
|
+
return fail();
|
|
2785
|
+
}
|
|
2786
|
+
})();
|
|
2787
|
+
|
|
2788
|
+
let raw = '';
|
|
2789
|
+
let consumed = false;
|
|
2790
|
+
try {
|
|
2791
|
+
const stat = fs.fstatSync(descriptor);
|
|
2792
|
+
if (!stat.isFile() || stat.size <= 0 || stat.size > 4_096) fail();
|
|
2793
|
+
if (process.platform !== 'win32') {
|
|
2794
|
+
if ((stat.mode & 0o077) !== 0) fail();
|
|
2795
|
+
if (typeof process.getuid === 'function' && stat.uid !== process.getuid()) fail();
|
|
2796
|
+
}
|
|
2797
|
+
raw = fs.readFileSync(descriptor, 'utf8');
|
|
2798
|
+
try {
|
|
2799
|
+
fs.unlinkSync(handoffPath);
|
|
2800
|
+
consumed = true;
|
|
2801
|
+
} catch {
|
|
2802
|
+
try {
|
|
2803
|
+
fs.ftruncateSync(descriptor, 0);
|
|
2804
|
+
consumed = true;
|
|
2805
|
+
} catch {
|
|
2806
|
+
fail();
|
|
2807
|
+
}
|
|
2808
|
+
}
|
|
2809
|
+
} finally {
|
|
2810
|
+
fs.closeSync(descriptor);
|
|
2811
|
+
}
|
|
2812
|
+
if (!consumed) fail();
|
|
2813
|
+
|
|
2814
|
+
let parsed;
|
|
2815
|
+
try {
|
|
2816
|
+
parsed = JSON.parse(raw);
|
|
2817
|
+
} catch {
|
|
2818
|
+
fail();
|
|
2819
|
+
}
|
|
2820
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) fail();
|
|
2821
|
+
const entries = Object.entries(parsed);
|
|
2822
|
+
if (entries.length !== 1 || entries[0]?.[0] !== 'Authorization') fail();
|
|
2823
|
+
const authorization = entries[0]?.[1];
|
|
2824
|
+
if (typeof authorization !== 'string' || !/^Bearer [A-Za-z0-9_-]{43}$/u.test(authorization)) fail();
|
|
2825
|
+
|
|
2826
|
+
const terminatorIndex = process.argv.indexOf('--', 2);
|
|
2827
|
+
const insertionIndex = terminatorIndex >= 0 ? terminatorIndex : process.argv.length;
|
|
2828
|
+
process.argv.splice(insertionIndex, 0, '--wsHeaders', JSON.stringify({ Authorization: authorization }));
|
|
2829
|
+
}
|
|
2830
|
+
`;
|
|
2831
|
+
}
|
|
2832
|
+
|
|
2833
|
+
// node_modules/.pnpm/mcporter@0.13.10/node_modules/mcporter/dist/daemon/paths.js
|
|
2834
|
+
import os3 from "node:os";
|
|
2835
|
+
import path5 from "node:path";
|
|
2836
|
+
function daemonBaseDir() {
|
|
2837
|
+
const override = process.env.MCPORTER_DAEMON_DIR?.trim();
|
|
2838
|
+
if (override)
|
|
2839
|
+
return path5.resolve(override);
|
|
2840
|
+
return path5.join(os3.userInfo().homedir, ".mcporter");
|
|
2841
|
+
}
|
|
2842
|
+
function daemonRunDir() {
|
|
2843
|
+
return path5.join(daemonBaseDir(), "daemon");
|
|
2844
|
+
}
|
|
2845
|
+
var verifiedWindowsDirectory;
|
|
2846
|
+
async function secureDaemonDirectory() {
|
|
2847
|
+
if (process.platform === "win32") {
|
|
2848
|
+
const directory = daemonRunDir();
|
|
2849
|
+
const previous = await fs4.lstat(directory).catch((error) => {
|
|
2850
|
+
if (error.code !== "ENOENT")
|
|
2851
|
+
throw error;
|
|
2852
|
+
return void 0;
|
|
2853
|
+
});
|
|
2854
|
+
await verifyWindowsDaemonDirectory(directory, previous);
|
|
2855
|
+
return;
|
|
2856
|
+
}
|
|
2857
|
+
await fs4.mkdir(daemonRunDir(), { recursive: true, mode: 448 });
|
|
2858
|
+
assertPosixDaemonDirectory(await fs4.lstat(daemonRunDir()), false);
|
|
2859
|
+
}
|
|
2860
|
+
async function verifyWindowsDaemonDirectory(directory, previous) {
|
|
2861
|
+
if (previous && (!previous.isDirectory() || previous.isSymbolicLink()))
|
|
2862
|
+
throw new Error("Unsafe daemon directory.");
|
|
2863
|
+
if (verifiedWindowsDirectory?.path !== directory || !previous || previous.dev !== verifiedWindowsDirectory.dev || previous.ino !== verifiedWindowsDirectory.ino || previous.birthtimeMs !== verifiedWindowsDirectory.birthtimeMs) {
|
|
2864
|
+
ensureWindowsPrivateDirectory(directory, true);
|
|
2865
|
+
const info = await fs4.lstat(directory);
|
|
2866
|
+
if (!info.isDirectory() || info.isSymbolicLink())
|
|
2867
|
+
throw new Error("Unsafe daemon directory.");
|
|
2868
|
+
verifiedWindowsDirectory = { path: directory, dev: info.dev, ino: info.ino, birthtimeMs: info.birthtimeMs };
|
|
2869
|
+
}
|
|
2870
|
+
}
|
|
2871
|
+
function assertPosixDaemonDirectory(info, allowLegacy) {
|
|
2872
|
+
if (info.isDirectory() && !info.isSymbolicLink() && info.uid === process.getuid?.()) {
|
|
2873
|
+
const mode = info.mode & 4095;
|
|
2874
|
+
if (mode === 448 || allowLegacy && mode === 493)
|
|
2875
|
+
return;
|
|
2876
|
+
if (mode === 493)
|
|
2877
|
+
throw new Error("Legacy daemon directory has mode 0755. Inspect with daemon migrate; after draining all old clients, run daemon migrate --stop-legacy --confirmed-drained to upgrade it to mode 0700.");
|
|
2878
|
+
}
|
|
2879
|
+
throw new Error("Daemon directory must be a current-user-owned directory (mode 0700), without symlinks.");
|
|
2880
|
+
}
|
|
2881
|
+
async function inspectLegacyDaemonDirectory(directory) {
|
|
2882
|
+
const info = await fs4.lstat(directory).catch((error) => {
|
|
2883
|
+
if (error.code !== "ENOENT")
|
|
2884
|
+
throw error;
|
|
2885
|
+
return void 0;
|
|
2886
|
+
});
|
|
2887
|
+
if (!info)
|
|
2888
|
+
return false;
|
|
2889
|
+
if (process.platform === "win32") {
|
|
2890
|
+
await verifyWindowsDaemonDirectory(directory, info);
|
|
2891
|
+
} else
|
|
2892
|
+
assertPosixDaemonDirectory(info, true);
|
|
2893
|
+
return true;
|
|
2894
|
+
}
|
|
2895
|
+
async function upgradeLegacyDaemonDirectory(directory) {
|
|
2896
|
+
if (!await inspectLegacyDaemonDirectory(directory) || process.platform === "win32")
|
|
2897
|
+
return;
|
|
2898
|
+
const before = await fs4.lstat(directory);
|
|
2899
|
+
assertPosixDaemonDirectory(before, true);
|
|
2900
|
+
const handle = await fs4.open(directory, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
|
|
2901
|
+
const assertIdentity = (info) => {
|
|
2902
|
+
assertPosixDaemonDirectory(info, true);
|
|
2903
|
+
if (info.dev !== before.dev || info.ino !== before.ino)
|
|
2904
|
+
throw new Error("Daemon directory changed during migration; inspect it before retrying.");
|
|
2905
|
+
};
|
|
2906
|
+
try {
|
|
2907
|
+
const opened = await handle.stat();
|
|
2908
|
+
assertIdentity(opened);
|
|
2909
|
+
assertIdentity(await fs4.lstat(directory));
|
|
2910
|
+
if ((opened.mode & 4095) === 493)
|
|
2911
|
+
await handle.chmod(448);
|
|
2912
|
+
assertPosixDaemonDirectory(await handle.stat(), false);
|
|
2913
|
+
const after = await fs4.lstat(directory);
|
|
2914
|
+
assertIdentity(after);
|
|
2915
|
+
assertPosixDaemonDirectory(after, false);
|
|
2916
|
+
} finally {
|
|
2917
|
+
await handle.close();
|
|
2918
|
+
}
|
|
2919
|
+
}
|
|
2920
|
+
function getDaemonMetadataPath(_configKey) {
|
|
2921
|
+
return path5.join(daemonRunDir(), "user.json");
|
|
2922
|
+
}
|
|
2923
|
+
function getDaemonSocketPath(_configKey) {
|
|
2924
|
+
if (process.platform === "win32")
|
|
2925
|
+
return `\\\\.\\pipe\\mcporter-user-${createHash5("sha256").update(daemonRunDir().toLowerCase()).digest("hex").slice(0, 24)}`;
|
|
2926
|
+
return path5.join(daemonRunDir(), "user.sock");
|
|
2927
|
+
}
|
|
2928
|
+
function getDaemonLogPath(_configKey) {
|
|
2929
|
+
return path5.join(daemonRunDir(), "user.log");
|
|
2930
|
+
}
|
|
2931
|
+
|
|
2932
|
+
// node_modules/.pnpm/mcporter@0.13.10/node_modules/mcporter/dist/daemon/browser-owner.js
|
|
2933
|
+
var BrowserOwnerConflict = class extends Error {
|
|
2934
|
+
code = "browser_owner_conflict";
|
|
2935
|
+
constructor(reason) {
|
|
2936
|
+
super(`Existing Chrome owner conflict: ${reason}. Use the canonical Chrome definition; drain and stop the daemon before deliberately changing ownership. This request was not retried.`);
|
|
2937
|
+
}
|
|
2938
|
+
};
|
|
2939
|
+
function isCanonicalChromeNamespace() {
|
|
2940
|
+
return path6.resolve(daemonBaseDir()) === path6.join(os4.userInfo().homedir, ".mcporter");
|
|
2941
|
+
}
|
|
2942
|
+
async function canonicalUserConfiguration() {
|
|
2943
|
+
if (!isCanonicalChromeNamespace())
|
|
2944
|
+
return { definitions: [] };
|
|
2945
|
+
const home = os4.userInfo().homedir;
|
|
2946
|
+
const file = mcporterConfigCandidates({ home }).find(pathExists);
|
|
2947
|
+
if (!file)
|
|
2948
|
+
return { definitions: [] };
|
|
2949
|
+
const definitions = [];
|
|
2950
|
+
const canonicalEnv = { ...process.env, HOME: home };
|
|
2951
|
+
if (canonicalEnv.USERPROFILE !== void 0)
|
|
2952
|
+
canonicalEnv.USERPROFILE = home;
|
|
2953
|
+
for (const name of Object.keys(canonicalEnv))
|
|
2954
|
+
if (name.startsWith("MCPORTER_CHROME_DEVTOOLS_RELAY_") || name === "MCPORTER_DISABLE_CHROME_DEVTOOLS_RELAY" || name.startsWith("OPENCLAW_"))
|
|
2955
|
+
delete canonicalEnv[name];
|
|
2956
|
+
const raw = await readConfigFile(file, true);
|
|
2957
|
+
for (const [name, entry] of Object.entries(raw.mcpServers)) {
|
|
2958
|
+
const launchTokens = [
|
|
2959
|
+
name,
|
|
2960
|
+
...Array.isArray(entry.command) ? entry.command : [entry.command ?? ""],
|
|
2961
|
+
...entry.args ?? []
|
|
2962
|
+
];
|
|
2963
|
+
if (!launchTokens.some((token) => /chrome[-_]devtools(?:[-_]mcp)?/i.test(token)))
|
|
2964
|
+
continue;
|
|
2965
|
+
const definition = normalizeServerEntry(name, entry, path6.dirname(file), { kind: "local", path: file }, [], {
|
|
2966
|
+
env: canonicalEnv,
|
|
2967
|
+
home
|
|
2968
|
+
});
|
|
2969
|
+
if (supportedOwner(definition))
|
|
2970
|
+
definitions.push(await effectiveDefinition(definition, canonicalEnv));
|
|
2971
|
+
}
|
|
2972
|
+
return { definitions, idleTimeoutMs: raw.daemonIdleTimeoutMs ?? raw.daemon_idle_timeout_ms };
|
|
2973
|
+
}
|
|
2974
|
+
function supportedOwner(definition) {
|
|
2975
|
+
return isChromeDefinition(definition) && definition.command.kind === "stdio" && resolveChromeDevtoolsAutoConnectCommand(definition.command.command, definition.command.args).enabled;
|
|
2976
|
+
}
|
|
2977
|
+
var BrowserOwner = class {
|
|
2978
|
+
identity;
|
|
2979
|
+
logicalIdentity;
|
|
2980
|
+
canonicalKeys;
|
|
2981
|
+
canonical;
|
|
2982
|
+
get reserved() {
|
|
2983
|
+
return this.identity !== void 0;
|
|
2984
|
+
}
|
|
2985
|
+
constructor(canonical) {
|
|
2986
|
+
this.canonical = canonical.filter(supportedOwner);
|
|
2987
|
+
this.canonicalKeys = new Set(this.canonical.map(connectionIdentity));
|
|
2988
|
+
}
|
|
2989
|
+
reserve(definition) {
|
|
2990
|
+
if (!isChromeDefinition(definition))
|
|
2991
|
+
return definition;
|
|
2992
|
+
if (!isCanonicalChromeNamespace())
|
|
2993
|
+
throw new BrowserOwnerConflict("existing local Chrome is forbidden in an isolated daemon directory");
|
|
2994
|
+
if (definition.command.kind !== "stdio")
|
|
2995
|
+
return definition;
|
|
2996
|
+
if (!resolveChromeDevtoolsAutoConnectCommand(definition.command.command, definition.command.args).enabled)
|
|
2997
|
+
throw new BrowserOwnerConflict("direct endpoints, wrappers and profile labels do not prove a distinct browser");
|
|
2998
|
+
const configured = this.canonical[0];
|
|
2999
|
+
if (this.canonicalKeys.size !== 1 || !configured)
|
|
3000
|
+
throw new BrowserOwnerConflict("canonical global Chrome definitions are missing or conflicting");
|
|
3001
|
+
const overrides = definition.configuredEnv ?? definition.env;
|
|
3002
|
+
if (definition.chromeDevtoolsRelay && resolveChromeDevtoolsRelayPolicy(definition.chromeDevtoolsRelay, overrides ?? {}) !== resolveChromeDevtoolsRelayPolicy(configured.chromeDevtoolsRelay, configured.env))
|
|
3003
|
+
throw new BrowserOwnerConflict("canonical relay policy differs");
|
|
3004
|
+
const env = { ...configured.env, ...overrides };
|
|
3005
|
+
normalizeChromeEnvironment(definition, env);
|
|
3006
|
+
const command = configured.command.kind === "stdio" && definition.command.command === configured.launchCommand ? { ...definition.command, command: configured.command.command } : definition.command;
|
|
3007
|
+
const candidate = { ...definition, command, env };
|
|
3008
|
+
const identity = connectionIdentity(candidate);
|
|
3009
|
+
if (!this.canonicalKeys.has(identity))
|
|
3010
|
+
throw new BrowserOwnerConflict("request does not match the complete canonical global Chrome connection contract");
|
|
3011
|
+
const policy = resolveChromeDevtoolsRelayPolicy(definition.chromeDevtoolsRelay, env);
|
|
3012
|
+
if (policy !== resolveChromeDevtoolsRelayPolicy(configured.chromeDevtoolsRelay, configured.env))
|
|
3013
|
+
throw new BrowserOwnerConflict("canonical relay policy differs");
|
|
3014
|
+
if (this.identity && this.identity !== identity)
|
|
3015
|
+
throw new BrowserOwnerConflict("an incompatible pending or live connection already owns the browser");
|
|
3016
|
+
this.identity = identity;
|
|
3017
|
+
return configured;
|
|
3018
|
+
}
|
|
3019
|
+
async resolveIdentity(definition) {
|
|
3020
|
+
const resolve = () => resolveChromeDevtoolsRelayRuntimeIdentity(chromeDevtoolsRelayEnvironmentKeys([definition], definition.env), definition.env);
|
|
3021
|
+
if (!this.logicalIdentity)
|
|
3022
|
+
this.logicalIdentity = resolve();
|
|
3023
|
+
const retained = await this.logicalIdentity;
|
|
3024
|
+
const current = await resolve();
|
|
3025
|
+
if (retained !== current)
|
|
3026
|
+
throw new BrowserOwnerConflict("relay endpoint, credential generation or security context changed; explicit retirement is required");
|
|
3027
|
+
return retained;
|
|
3028
|
+
}
|
|
3029
|
+
};
|
|
3030
|
+
|
|
3031
|
+
// node_modules/.pnpm/mcporter@0.13.10/node_modules/mcporter/dist/daemon/transport-authority.js
|
|
3032
|
+
var authority = /* @__PURE__ */ Symbol("broker transport authority");
|
|
3033
|
+
function authorizeBrokerDefinition(definition, validateRequest) {
|
|
3034
|
+
Object.defineProperty(definition, authority, { value: { validateRequest }, enumerable: true });
|
|
3035
|
+
}
|
|
3036
|
+
function validateBrokerRequestAuthority(definition) {
|
|
3037
|
+
return definition?.[authority]?.validateRequest?.();
|
|
3038
|
+
}
|
|
3039
|
+
function isBrokerDefinition(definition) {
|
|
3040
|
+
return Boolean(definition?.[authority]);
|
|
3041
|
+
}
|
|
3042
|
+
function assertChromeBrokerAuthority(definition) {
|
|
3043
|
+
if (isChromeDefinition(definition) && !isBrokerDefinition(definition)) {
|
|
3044
|
+
throw new BrowserOwnerConflict("programmatic or ephemeral Chrome attachment must use the daemon-backed runtime");
|
|
3045
|
+
}
|
|
3046
|
+
}
|
|
3047
|
+
|
|
3048
|
+
// node_modules/.pnpm/mcporter@0.13.10/node_modules/mcporter/dist/runtime-process-utils.js
|
|
3049
|
+
import { execFile } from "node:child_process";
|
|
3050
|
+
async function closeTransportAndWait(logger, transport, options = {}) {
|
|
3051
|
+
const pidBeforeClose = getTransportPid(transport);
|
|
3052
|
+
const targetsBeforeClose = pidBeforeClose ? await collectProcessTreePids(pidBeforeClose) : [];
|
|
3053
|
+
let closeError;
|
|
3054
|
+
let closeSettled = false;
|
|
3055
|
+
const closePromise = (options.close ?? (() => transport.close()))().catch((error) => {
|
|
3056
|
+
closeError = error;
|
|
3057
|
+
if (!options.throwOnCloseError) {
|
|
3058
|
+
logger.warn(`Failed to close transport cleanly: ${error.message}`);
|
|
3059
|
+
}
|
|
3060
|
+
}).finally(() => {
|
|
3061
|
+
closeSettled = true;
|
|
3062
|
+
});
|
|
3063
|
+
if (pidBeforeClose) {
|
|
3064
|
+
destroyTransportStreams(transport);
|
|
3065
|
+
await ensureProcessTreeTerminated(logger, pidBeforeClose, targetsBeforeClose, options.requireRetirement);
|
|
3066
|
+
if (!closeSettled) {
|
|
3067
|
+
await Promise.race([closePromise, delay(100)]);
|
|
3068
|
+
}
|
|
3069
|
+
} else {
|
|
3070
|
+
await closePromise;
|
|
3071
|
+
}
|
|
3072
|
+
if (closeError && options.throwOnCloseError) {
|
|
3073
|
+
if (options.requireRetirement)
|
|
3074
|
+
throw Object.assign(new Error("Transport retirement failed.", { cause: closeError }), {
|
|
3075
|
+
code: "transport_retirement_failed"
|
|
3076
|
+
});
|
|
3077
|
+
throw closeError;
|
|
3078
|
+
}
|
|
3079
|
+
}
|
|
3080
|
+
function getTransportPid(transport) {
|
|
3081
|
+
if ("pid" in transport) {
|
|
3082
|
+
const candidate = transport.pid;
|
|
3083
|
+
if (typeof candidate === "number" && candidate > 0) {
|
|
3084
|
+
return candidate;
|
|
3085
|
+
}
|
|
3086
|
+
}
|
|
3087
|
+
return null;
|
|
3088
|
+
}
|
|
3089
|
+
function isProcessAlive(pid) {
|
|
3090
|
+
try {
|
|
3091
|
+
process.kill(pid, 0);
|
|
3092
|
+
return true;
|
|
3093
|
+
} catch {
|
|
3094
|
+
return false;
|
|
3095
|
+
}
|
|
3096
|
+
}
|
|
3097
|
+
async function ensureProcessTreeTerminated(logger, rootPid, capturedTargets = [], requireRetirement = false) {
|
|
3098
|
+
let targets = uniquePids([...capturedTargets, ...await collectProcessTreePids(rootPid)]);
|
|
3099
|
+
if (targets.length === 0 || await waitForTreeExit(targets, 700)) {
|
|
3100
|
+
return;
|
|
3101
|
+
}
|
|
3102
|
+
await sendSignalToTargets(targets, "SIGTERM");
|
|
3103
|
+
targets = uniquePids([...targets, ...await collectProcessTreePids(rootPid)]);
|
|
3104
|
+
if (await waitForTreeExit(targets, 700)) {
|
|
3105
|
+
return;
|
|
3106
|
+
}
|
|
3107
|
+
targets = uniquePids([...targets, ...await collectProcessTreePids(rootPid)]);
|
|
3108
|
+
await sendSignalToTargets(targets, "SIGKILL");
|
|
3109
|
+
if (await waitForTreeExit(targets, 500)) {
|
|
3110
|
+
return;
|
|
3111
|
+
}
|
|
3112
|
+
if (requireRetirement)
|
|
3113
|
+
throw Object.assign(new Error("Transport process retirement could not be verified."), {
|
|
3114
|
+
code: "transport_retirement_failed"
|
|
3115
|
+
});
|
|
3116
|
+
logger.warn(`Process tree rooted at pid=${rootPid} did not exit after SIGKILL.`);
|
|
3117
|
+
}
|
|
3118
|
+
function uniquePids(pids) {
|
|
3119
|
+
return [...new Set(pids.filter((pid) => Number.isInteger(pid) && pid > 0))];
|
|
3120
|
+
}
|
|
3121
|
+
function destroyTransportStreams(transport) {
|
|
3122
|
+
const candidate = transport;
|
|
3123
|
+
for (const key of ["stdin", "stdout", "stderr"]) {
|
|
3124
|
+
try {
|
|
3125
|
+
const stream = candidate[key];
|
|
3126
|
+
stream?.destroy?.();
|
|
3127
|
+
} catch {
|
|
3128
|
+
}
|
|
3129
|
+
}
|
|
3130
|
+
}
|
|
3131
|
+
async function sendSignalToTargets(pids, signal) {
|
|
3132
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3133
|
+
for (const pid of pids) {
|
|
3134
|
+
if (seen.has(pid)) {
|
|
3135
|
+
continue;
|
|
3136
|
+
}
|
|
3137
|
+
seen.add(pid);
|
|
3138
|
+
sendSignal(pid, signal);
|
|
3139
|
+
}
|
|
3140
|
+
}
|
|
3141
|
+
function sendSignal(pid, signal) {
|
|
3142
|
+
try {
|
|
3143
|
+
process.kill(pid, signal);
|
|
3144
|
+
} catch (error) {
|
|
3145
|
+
if (typeof error === "object" && error && "code" in error && error.code === "ESRCH") {
|
|
3146
|
+
return;
|
|
3147
|
+
}
|
|
3148
|
+
throw error;
|
|
3149
|
+
}
|
|
3150
|
+
}
|
|
3151
|
+
async function listDescendantPids(rootPid) {
|
|
3152
|
+
if (!isProcessAlive(rootPid)) {
|
|
3153
|
+
return [];
|
|
3154
|
+
}
|
|
3155
|
+
if (process.platform === "win32") {
|
|
3156
|
+
return listDescendantPidsWindows(rootPid);
|
|
3157
|
+
}
|
|
3158
|
+
try {
|
|
3159
|
+
const { stdout } = await execFileAsync("ps", ["-eo", "pid=,ppid="]);
|
|
3160
|
+
const children = /* @__PURE__ */ new Map();
|
|
3161
|
+
for (const line of stdout.split("\n")) {
|
|
3162
|
+
const trimmed = line.trim();
|
|
3163
|
+
if (!trimmed) {
|
|
3164
|
+
continue;
|
|
3165
|
+
}
|
|
3166
|
+
const [pidText, ppidText] = trimmed.split(/\s+/, 2);
|
|
3167
|
+
const pid = Number.parseInt(pidText ?? "", 10);
|
|
3168
|
+
const ppid = Number.parseInt(ppidText ?? "", 10);
|
|
3169
|
+
if (!Number.isFinite(pid) || !Number.isFinite(ppid)) {
|
|
3170
|
+
continue;
|
|
3171
|
+
}
|
|
3172
|
+
const bucket = children.get(ppid) ?? [];
|
|
3173
|
+
bucket.push(pid);
|
|
3174
|
+
children.set(ppid, bucket);
|
|
3175
|
+
}
|
|
3176
|
+
return collectDescendantsFromChildren(rootPid, children);
|
|
3177
|
+
} catch {
|
|
3178
|
+
return [];
|
|
3179
|
+
}
|
|
3180
|
+
}
|
|
3181
|
+
async function listDescendantPidsWindows(rootPid) {
|
|
3182
|
+
try {
|
|
3183
|
+
const powershellScript = "Get-CimInstance Win32_Process | Select-Object ProcessId,ParentProcessId | ConvertTo-Json -Compress";
|
|
3184
|
+
const { stdout } = await execFileAsync("powershell.exe", ["-NoProfile", "-Command", powershellScript]);
|
|
3185
|
+
const trimmed = stdout.trim();
|
|
3186
|
+
if (!trimmed) {
|
|
3187
|
+
return [];
|
|
3188
|
+
}
|
|
3189
|
+
const parsed = JSON.parse(trimmed);
|
|
3190
|
+
const entries = Array.isArray(parsed) ? parsed : [parsed];
|
|
3191
|
+
const children = /* @__PURE__ */ new Map();
|
|
3192
|
+
for (const entry of entries) {
|
|
3193
|
+
const pidCandidate = entry?.ProcessId;
|
|
3194
|
+
const ppidCandidate = entry?.ParentProcessId;
|
|
3195
|
+
if (typeof pidCandidate !== "number" || typeof ppidCandidate !== "number") {
|
|
3196
|
+
continue;
|
|
3197
|
+
}
|
|
3198
|
+
const pid = Number.isFinite(pidCandidate) ? pidCandidate : void 0;
|
|
3199
|
+
const ppid = Number.isFinite(ppidCandidate) ? ppidCandidate : void 0;
|
|
3200
|
+
if (pid === void 0 || ppid === void 0) {
|
|
3201
|
+
continue;
|
|
3202
|
+
}
|
|
3203
|
+
const bucket = children.get(ppid) ?? [];
|
|
3204
|
+
bucket.push(pid);
|
|
3205
|
+
children.set(ppid, bucket);
|
|
3206
|
+
}
|
|
3207
|
+
return collectDescendantsFromChildren(rootPid, children);
|
|
3208
|
+
} catch {
|
|
3209
|
+
return [];
|
|
3210
|
+
}
|
|
3211
|
+
}
|
|
3212
|
+
function execFileAsync(command, args) {
|
|
3213
|
+
return new Promise((resolve, reject) => {
|
|
3214
|
+
execFile(command, args, { encoding: "utf8" }, (error, stdout, stderr) => {
|
|
3215
|
+
if (error) {
|
|
3216
|
+
reject(error);
|
|
3217
|
+
return;
|
|
3218
|
+
}
|
|
3219
|
+
resolve({ stdout, stderr });
|
|
742
3220
|
});
|
|
743
3221
|
});
|
|
744
3222
|
}
|
|
@@ -782,7 +3260,7 @@ function delay(ms) {
|
|
|
782
3260
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
783
3261
|
}
|
|
784
3262
|
|
|
785
|
-
// node_modules/.pnpm/mcporter@0.13.
|
|
3263
|
+
// node_modules/.pnpm/mcporter@0.13.10/node_modules/mcporter/dist/runtime/errors.js
|
|
786
3264
|
var NON_FATAL_MCP_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
787
3265
|
ProtocolErrorCode.InvalidRequest,
|
|
788
3266
|
ProtocolErrorCode.MethodNotFound,
|
|
@@ -804,8 +3282,8 @@ function protocolErrorCode(error) {
|
|
|
804
3282
|
return typeof error.code === "number" ? error.code : void 0;
|
|
805
3283
|
}
|
|
806
3284
|
|
|
807
|
-
// node_modules/.pnpm/mcporter@0.13.
|
|
808
|
-
import
|
|
3285
|
+
// node_modules/.pnpm/mcporter@0.13.10/node_modules/mcporter/dist/runtime/replay-transport.js
|
|
3286
|
+
import fs5 from "node:fs";
|
|
809
3287
|
import { isDeepStrictEqual } from "node:util";
|
|
810
3288
|
var CLIENT_IDENTITY_META_KEYS = [
|
|
811
3289
|
"io.modelcontextprotocol/clientInfo",
|
|
@@ -860,7 +3338,7 @@ var ReplayTransport = class {
|
|
|
860
3338
|
};
|
|
861
3339
|
function readRecordedMessages(recordPath) {
|
|
862
3340
|
try {
|
|
863
|
-
const contents =
|
|
3341
|
+
const contents = fs5.readFileSync(recordPath, "utf8");
|
|
864
3342
|
return contents.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0).map((line, index) => {
|
|
865
3343
|
try {
|
|
866
3344
|
return JSON.parse(line);
|
|
@@ -997,44 +3475,250 @@ function adaptInitializeResponse(method, response, requestParams) {
|
|
|
997
3475
|
if (typeof protocolVersion !== "string" || !isJsonRpcRecord(response.result)) {
|
|
998
3476
|
return response;
|
|
999
3477
|
}
|
|
1000
|
-
return {
|
|
1001
|
-
...response,
|
|
1002
|
-
result: { ...response.result, protocolVersion }
|
|
1003
|
-
};
|
|
1004
|
-
}
|
|
1005
|
-
function isJsonRpcRecord(value) {
|
|
1006
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1007
|
-
}
|
|
1008
|
-
function withActiveRequestId(response, requestId) {
|
|
1009
|
-
if (requestId === void 0) {
|
|
1010
|
-
return response;
|
|
3478
|
+
return {
|
|
3479
|
+
...response,
|
|
3480
|
+
result: { ...response.result, protocolVersion }
|
|
3481
|
+
};
|
|
3482
|
+
}
|
|
3483
|
+
function isJsonRpcRecord(value) {
|
|
3484
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3485
|
+
}
|
|
3486
|
+
function withActiveRequestId(response, requestId) {
|
|
3487
|
+
if (requestId === void 0) {
|
|
3488
|
+
return response;
|
|
3489
|
+
}
|
|
3490
|
+
return {
|
|
3491
|
+
...response,
|
|
3492
|
+
id: requestId
|
|
3493
|
+
};
|
|
3494
|
+
}
|
|
3495
|
+
function formatReplayMismatch(server, request, expected) {
|
|
3496
|
+
const expectedText = expected ? `${expected.method} ${JSON.stringify(expected.params ?? {})}` : "no remaining recorded recv";
|
|
3497
|
+
return `Replay mismatch for server '${server}': request ${request.method} ${JSON.stringify(request.params ?? {})} did not match next expected recv ${expectedText}.`;
|
|
3498
|
+
}
|
|
3499
|
+
function formatReplayRemainder(server, expectedSends) {
|
|
3500
|
+
const expected = expectedSends[0];
|
|
3501
|
+
const count = expectedSends.length;
|
|
3502
|
+
const requestText = count === 1 ? "request" : "requests";
|
|
3503
|
+
const expectedText = expected ? `${expected.method} ${JSON.stringify(expected.params ?? {})}` : "no remaining recorded recv";
|
|
3504
|
+
return `Replay ended for server '${server}' with ${count} recorded ${requestText} still unused; next expected recv ${expectedText}.`;
|
|
3505
|
+
}
|
|
3506
|
+
|
|
3507
|
+
// node_modules/.pnpm/@modelcontextprotocol+client@2.0.0/node_modules/@modelcontextprotocol/client/dist/stdio.mjs
|
|
3508
|
+
var import_cross_spawn = __toESM(require_cross_spawn(), 1);
|
|
3509
|
+
import process2 from "node:process";
|
|
3510
|
+
import { PassThrough } from "node:stream";
|
|
3511
|
+
var DEFAULT_INHERITED_ENV_VARS = process2.platform === "win32" ? [
|
|
3512
|
+
"APPDATA",
|
|
3513
|
+
"HOMEDRIVE",
|
|
3514
|
+
"HOMEPATH",
|
|
3515
|
+
"LOCALAPPDATA",
|
|
3516
|
+
"PATH",
|
|
3517
|
+
"PROCESSOR_ARCHITECTURE",
|
|
3518
|
+
"SYSTEMDRIVE",
|
|
3519
|
+
"SYSTEMROOT",
|
|
3520
|
+
"TEMP",
|
|
3521
|
+
"USERNAME",
|
|
3522
|
+
"USERPROFILE",
|
|
3523
|
+
"PROGRAMFILES"
|
|
3524
|
+
] : [
|
|
3525
|
+
"HOME",
|
|
3526
|
+
"LOGNAME",
|
|
3527
|
+
"PATH",
|
|
3528
|
+
"SHELL",
|
|
3529
|
+
"TERM",
|
|
3530
|
+
"USER"
|
|
3531
|
+
];
|
|
3532
|
+
function getDefaultEnvironment() {
|
|
3533
|
+
const env = {};
|
|
3534
|
+
for (const key of DEFAULT_INHERITED_ENV_VARS) {
|
|
3535
|
+
const value = process2.env[key];
|
|
3536
|
+
if (value === void 0) continue;
|
|
3537
|
+
if (value.startsWith("()")) continue;
|
|
3538
|
+
env[key] = value;
|
|
3539
|
+
}
|
|
3540
|
+
return env;
|
|
3541
|
+
}
|
|
3542
|
+
var StdioClientTransport = class {
|
|
3543
|
+
_process;
|
|
3544
|
+
_readBuffer;
|
|
3545
|
+
_serverParams;
|
|
3546
|
+
_stderrStream = null;
|
|
3547
|
+
onclose;
|
|
3548
|
+
onerror;
|
|
3549
|
+
onmessage;
|
|
3550
|
+
constructor(server) {
|
|
3551
|
+
this._serverParams = server;
|
|
3552
|
+
this._readBuffer = new ReadBuffer({ maxBufferSize: server.maxBufferSize });
|
|
3553
|
+
if (server.stderr === "pipe" || server.stderr === "overlapped") this._stderrStream = new PassThrough();
|
|
3554
|
+
}
|
|
3555
|
+
/**
|
|
3556
|
+
* Starts the server process and prepares to communicate with it.
|
|
3557
|
+
*/
|
|
3558
|
+
async start() {
|
|
3559
|
+
if (this._process) throw new Error("StdioClientTransport already started! If using Client class, note that connect() calls start() automatically.");
|
|
3560
|
+
return new Promise((resolve, reject) => {
|
|
3561
|
+
this._process = (0, import_cross_spawn.default)(this._serverParams.command, this._serverParams.args ?? [], {
|
|
3562
|
+
env: {
|
|
3563
|
+
...getDefaultEnvironment(),
|
|
3564
|
+
...this._serverParams.env
|
|
3565
|
+
},
|
|
3566
|
+
stdio: [
|
|
3567
|
+
"pipe",
|
|
3568
|
+
"pipe",
|
|
3569
|
+
this._serverParams.stderr ?? "inherit"
|
|
3570
|
+
],
|
|
3571
|
+
shell: false,
|
|
3572
|
+
windowsHide: process2.platform === "win32",
|
|
3573
|
+
cwd: this._serverParams.cwd
|
|
3574
|
+
});
|
|
3575
|
+
this._process.on("error", (error) => {
|
|
3576
|
+
reject(error);
|
|
3577
|
+
this.onerror?.(error);
|
|
3578
|
+
});
|
|
3579
|
+
this._process.on("spawn", () => {
|
|
3580
|
+
resolve();
|
|
3581
|
+
});
|
|
3582
|
+
this._process.on("close", (_code) => {
|
|
3583
|
+
this._process = void 0;
|
|
3584
|
+
this.onclose?.();
|
|
3585
|
+
});
|
|
3586
|
+
this._process.stdin?.on("error", (error) => {
|
|
3587
|
+
this.onerror?.(error);
|
|
3588
|
+
});
|
|
3589
|
+
this._process.stdout?.on("data", (chunk) => {
|
|
3590
|
+
try {
|
|
3591
|
+
this._readBuffer.append(chunk);
|
|
3592
|
+
this.processReadBuffer();
|
|
3593
|
+
} catch (error) {
|
|
3594
|
+
this.onerror?.(error);
|
|
3595
|
+
this.close().catch(() => {
|
|
3596
|
+
});
|
|
3597
|
+
}
|
|
3598
|
+
});
|
|
3599
|
+
this._process.stdout?.on("error", (error) => {
|
|
3600
|
+
this.onerror?.(error);
|
|
3601
|
+
});
|
|
3602
|
+
if (this._stderrStream && this._process.stderr) this._process.stderr.pipe(this._stderrStream);
|
|
3603
|
+
});
|
|
3604
|
+
}
|
|
3605
|
+
/**
|
|
3606
|
+
* The `stderr` stream of the child process, if {@linkcode StdioServerParameters.stderr} was set to `"pipe"` or `"overlapped"`.
|
|
3607
|
+
*
|
|
3608
|
+
* If `stderr` piping was requested, a `PassThrough` stream is returned _immediately_, allowing callers to
|
|
3609
|
+
* attach listeners before the `start` method is invoked. This prevents loss of any early
|
|
3610
|
+
* error output emitted by the child process.
|
|
3611
|
+
*/
|
|
3612
|
+
get stderr() {
|
|
3613
|
+
if (this._stderrStream) return this._stderrStream;
|
|
3614
|
+
return this._process?.stderr ?? null;
|
|
3615
|
+
}
|
|
3616
|
+
/**
|
|
3617
|
+
* The child process pid spawned by this transport.
|
|
3618
|
+
*
|
|
3619
|
+
* This is only available after the transport has been started.
|
|
3620
|
+
*/
|
|
3621
|
+
get pid() {
|
|
3622
|
+
return this._process?.pid ?? null;
|
|
3623
|
+
}
|
|
3624
|
+
processReadBuffer() {
|
|
3625
|
+
while (true) try {
|
|
3626
|
+
const message = this._readBuffer.readMessage();
|
|
3627
|
+
if (message === null) break;
|
|
3628
|
+
this.onmessage?.(message);
|
|
3629
|
+
} catch (error) {
|
|
3630
|
+
this.onerror?.(error);
|
|
3631
|
+
}
|
|
3632
|
+
}
|
|
3633
|
+
/**
|
|
3634
|
+
* Reap a disposable probe sibling (see the version-negotiation sibling
|
|
3635
|
+
* flow): signal-first teardown awaiting process `exit` — never the `close`
|
|
3636
|
+
* event, so a helper process holding the child's stdio pipes can never
|
|
3637
|
+
* block disposal. Not part of the public transport lifecycle.
|
|
3638
|
+
*
|
|
3639
|
+
* @internal
|
|
3640
|
+
*/
|
|
3641
|
+
async _dispose() {
|
|
3642
|
+
const proc = this._process;
|
|
3643
|
+
this._process = void 0;
|
|
3644
|
+
if (proc && proc.exitCode === null && proc.signalCode === null) {
|
|
3645
|
+
const exited = new Promise((resolve) => proc.once("exit", () => resolve()));
|
|
3646
|
+
try {
|
|
3647
|
+
proc.stdin?.end();
|
|
3648
|
+
} catch {
|
|
3649
|
+
}
|
|
3650
|
+
try {
|
|
3651
|
+
proc.kill("SIGTERM");
|
|
3652
|
+
} catch {
|
|
3653
|
+
}
|
|
3654
|
+
await Promise.race([exited, new Promise((resolve) => setTimeout(resolve, 1e3).unref())]);
|
|
3655
|
+
if (proc.exitCode === null && proc.signalCode === null) try {
|
|
3656
|
+
proc.kill("SIGKILL");
|
|
3657
|
+
} catch {
|
|
3658
|
+
}
|
|
3659
|
+
await exited;
|
|
3660
|
+
}
|
|
3661
|
+
try {
|
|
3662
|
+
proc?.stdout?.destroy();
|
|
3663
|
+
} catch {
|
|
3664
|
+
}
|
|
3665
|
+
try {
|
|
3666
|
+
proc?.stdin?.destroy();
|
|
3667
|
+
} catch {
|
|
3668
|
+
}
|
|
3669
|
+
try {
|
|
3670
|
+
proc?.stderr?.destroy();
|
|
3671
|
+
} catch {
|
|
3672
|
+
}
|
|
3673
|
+
this._readBuffer.clear();
|
|
3674
|
+
}
|
|
3675
|
+
async close() {
|
|
3676
|
+
if (this._process) {
|
|
3677
|
+
const processToClose = this._process;
|
|
3678
|
+
this._process = void 0;
|
|
3679
|
+
const closePromise = new Promise((resolve) => {
|
|
3680
|
+
processToClose.once("close", () => {
|
|
3681
|
+
resolve();
|
|
3682
|
+
});
|
|
3683
|
+
});
|
|
3684
|
+
try {
|
|
3685
|
+
processToClose.stdin?.end();
|
|
3686
|
+
} catch {
|
|
3687
|
+
}
|
|
3688
|
+
await Promise.race([closePromise, new Promise((resolve) => setTimeout(resolve, 2e3).unref())]);
|
|
3689
|
+
if (processToClose.exitCode === null) {
|
|
3690
|
+
try {
|
|
3691
|
+
processToClose.kill("SIGTERM");
|
|
3692
|
+
} catch {
|
|
3693
|
+
}
|
|
3694
|
+
await Promise.race([closePromise, new Promise((resolve) => setTimeout(resolve, 2e3).unref())]);
|
|
3695
|
+
}
|
|
3696
|
+
if (processToClose.exitCode === null) try {
|
|
3697
|
+
processToClose.kill("SIGKILL");
|
|
3698
|
+
} catch {
|
|
3699
|
+
}
|
|
3700
|
+
}
|
|
3701
|
+
this._readBuffer.clear();
|
|
3702
|
+
}
|
|
3703
|
+
send(message) {
|
|
3704
|
+
return new Promise((resolve) => {
|
|
3705
|
+
if (!this._process?.stdin) throw new SdkError(SdkErrorCode.NotConnected, "Not connected");
|
|
3706
|
+
const json = serializeMessage(message);
|
|
3707
|
+
if (this._process.stdin.write(json)) resolve();
|
|
3708
|
+
else this._process.stdin.once("drain", resolve);
|
|
3709
|
+
});
|
|
1011
3710
|
}
|
|
1012
|
-
|
|
1013
|
-
...response,
|
|
1014
|
-
id: requestId
|
|
1015
|
-
};
|
|
1016
|
-
}
|
|
1017
|
-
function formatReplayMismatch(server, request, expected) {
|
|
1018
|
-
const expectedText = expected ? `${expected.method} ${JSON.stringify(expected.params ?? {})}` : "no remaining recorded recv";
|
|
1019
|
-
return `Replay mismatch for server '${server}': request ${request.method} ${JSON.stringify(request.params ?? {})} did not match next expected recv ${expectedText}.`;
|
|
1020
|
-
}
|
|
1021
|
-
function formatReplayRemainder(server, expectedSends) {
|
|
1022
|
-
const expected = expectedSends[0];
|
|
1023
|
-
const count = expectedSends.length;
|
|
1024
|
-
const requestText = count === 1 ? "request" : "requests";
|
|
1025
|
-
const expectedText = expected ? `${expected.method} ${JSON.stringify(expected.params ?? {})}` : "no remaining recorded recv";
|
|
1026
|
-
return `Replay ended for server '${server}' with ${count} recorded ${requestText} still unused; next expected recv ${expectedText}.`;
|
|
1027
|
-
}
|
|
3711
|
+
};
|
|
1028
3712
|
|
|
1029
|
-
// node_modules/.pnpm/mcporter@0.13.
|
|
1030
|
-
import
|
|
1031
|
-
import
|
|
1032
|
-
import
|
|
1033
|
-
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
3713
|
+
// node_modules/.pnpm/mcporter@0.13.10/node_modules/mcporter/dist/chrome-devtools-compat.js
|
|
3714
|
+
import fs7 from "node:fs";
|
|
3715
|
+
import os5 from "node:os";
|
|
3716
|
+
import path8 from "node:path";
|
|
3717
|
+
import { fileURLToPath, pathToFileURL as pathToFileURL2 } from "node:url";
|
|
1034
3718
|
|
|
1035
|
-
// node_modules/.pnpm/mcporter@0.13.
|
|
1036
|
-
import
|
|
1037
|
-
import
|
|
3719
|
+
// node_modules/.pnpm/mcporter@0.13.10/node_modules/mcporter/dist/chrome-devtools-auto-connect-patch.js
|
|
3720
|
+
import fs6 from "node:fs";
|
|
3721
|
+
import path7 from "node:path";
|
|
1038
3722
|
var MARKER = "MCPORTER_DEVTOOLS_TIMEOUT_PATCH";
|
|
1039
3723
|
var HELPER = `// ${MARKER}
|
|
1040
3724
|
const MCPORTER_DEVTOOLS_DETECTION_TIMEOUT = 1_000;
|
|
@@ -1064,7 +3748,7 @@ var PATCHED_DETECTION_BLOCK = `if (await mcporterWithTimeout(page.hasDevTools(),
|
|
|
1064
3748
|
}`;
|
|
1065
3749
|
patchChromeDevtoolsMcp();
|
|
1066
3750
|
function patchChromeDevtoolsMcp(mainPath = process.argv[1]) {
|
|
1067
|
-
patchChromeDevtoolsMcpWithDependencies(mainPath,
|
|
3751
|
+
patchChromeDevtoolsMcpWithDependencies(mainPath, fs6, path7, MARKER, HELPER, DETECTION_BLOCK, PATCHED_DETECTION_BLOCK);
|
|
1068
3752
|
}
|
|
1069
3753
|
function renderChromeDevtoolsAutoConnectPatchSource() {
|
|
1070
3754
|
return `import fs from 'node:fs';
|
|
@@ -1119,7 +3803,7 @@ ${helper}`);
|
|
|
1119
3803
|
}
|
|
1120
3804
|
}
|
|
1121
3805
|
|
|
1122
|
-
// node_modules/.pnpm/mcporter@0.13.
|
|
3806
|
+
// node_modules/.pnpm/mcporter@0.13.10/node_modules/mcporter/dist/chrome-devtools-compat.js
|
|
1123
3807
|
var FALLBACK_PATCH_FILENAME = "mcporter-chrome-devtools-auto-connect-patch.js";
|
|
1124
3808
|
function applyChromeDevtoolsCompat(env, command, args) {
|
|
1125
3809
|
if (!shouldApplyChromeDevtoolsCompat(command, args, env)) {
|
|
@@ -1129,276 +3813,51 @@ function applyChromeDevtoolsCompat(env, command, args) {
|
|
|
1129
3813
|
if (!patchPath) {
|
|
1130
3814
|
return { env, applied: false };
|
|
1131
3815
|
}
|
|
1132
|
-
const importFlag = `--import=${
|
|
3816
|
+
const importFlag = `--import=${pathToFileURL2(patchPath).href}`;
|
|
1133
3817
|
const existingOptions = env.NODE_OPTIONS?.trim();
|
|
1134
3818
|
if (existingOptions?.includes(importFlag)) {
|
|
1135
|
-
return { env, applied: true, patchPath };
|
|
1136
|
-
}
|
|
1137
|
-
return {
|
|
1138
|
-
env: {
|
|
1139
|
-
...env,
|
|
1140
|
-
NODE_OPTIONS: existingOptions ? `${existingOptions} ${importFlag}` : importFlag
|
|
1141
|
-
},
|
|
1142
|
-
applied: true,
|
|
1143
|
-
patchPath
|
|
1144
|
-
};
|
|
1145
|
-
}
|
|
1146
|
-
function shouldApplyChromeDevtoolsCompat(command, args, env = process.env) {
|
|
1147
|
-
if (env.MCPORTER_DISABLE_CHROME_DEVTOOLS_COMPAT === "1") {
|
|
1148
|
-
return false;
|
|
1149
|
-
}
|
|
1150
|
-
return resolveChromeDevtoolsAutoConnectCommand(command, args).enabled;
|
|
1151
|
-
}
|
|
1152
|
-
function resolveChromeDevtoolsCompatPatchPath(candidates = defaultChromeDevtoolsPatchCandidates(), fallbackDir = os.tmpdir()) {
|
|
1153
|
-
const existing = candidates.find((candidate) => fs3.existsSync(candidate));
|
|
1154
|
-
if (existing) {
|
|
1155
|
-
return existing;
|
|
1156
|
-
}
|
|
1157
|
-
return writeFallbackPatch(fallbackDir);
|
|
1158
|
-
}
|
|
1159
|
-
function defaultChromeDevtoolsPatchCandidates() {
|
|
1160
|
-
const here = path2.dirname(fileURLToPath(import.meta.url));
|
|
1161
|
-
return [
|
|
1162
|
-
path2.join(here, "chrome-devtools-auto-connect-patch.js"),
|
|
1163
|
-
path2.resolve(here, "..", "dist", "chrome-devtools-auto-connect-patch.js")
|
|
1164
|
-
];
|
|
1165
|
-
}
|
|
1166
|
-
function writeFallbackPatch(fallbackDir) {
|
|
1167
|
-
const patchPath = path2.join(fallbackDir, FALLBACK_PATCH_FILENAME);
|
|
1168
|
-
try {
|
|
1169
|
-
fs3.writeFileSync(patchPath, renderChromeDevtoolsAutoConnectPatchSource(), { mode: 384 });
|
|
1170
|
-
return patchPath;
|
|
1171
|
-
} catch {
|
|
1172
|
-
return void 0;
|
|
1173
|
-
}
|
|
1174
|
-
}
|
|
1175
|
-
|
|
1176
|
-
// node_modules/.pnpm/mcporter@0.13.8/node_modules/mcporter/dist/chrome-devtools-relay-handoff.js
|
|
1177
|
-
import { spawnSync } from "node:child_process";
|
|
1178
|
-
import { randomBytes } from "node:crypto";
|
|
1179
|
-
import fs4 from "node:fs";
|
|
1180
|
-
import os2 from "node:os";
|
|
1181
|
-
import path3 from "node:path";
|
|
1182
|
-
import { pathToFileURL as pathToFileURL2 } from "node:url";
|
|
1183
|
-
var CHROME_RELAY_HANDOFF_ENV = "MCPORTER_CHROME_RELAY_HANDOFF_PATH";
|
|
1184
|
-
var HANDOFF_DIR_PREFIX = "mcporter-chrome-relay-";
|
|
1185
|
-
var HANDOFF_FILENAME = "headers.json";
|
|
1186
|
-
var PRELOAD_FILENAME = "preload.mjs";
|
|
1187
|
-
var WINDOWS_ACL_PATH_ENV = "MCPORTER_CHROME_RELAY_ACL_PATH";
|
|
1188
|
-
function createChromeDevtoolsRelayHandoff(env, clientAuthorization) {
|
|
1189
|
-
let directory;
|
|
1190
|
-
try {
|
|
1191
|
-
if (process.platform === "win32")
|
|
1192
|
-
directory = createWindowsHandoffDirectory();
|
|
1193
|
-
else {
|
|
1194
|
-
directory = fs4.mkdtempSync(path3.join(os2.tmpdir(), HANDOFF_DIR_PREFIX));
|
|
1195
|
-
fs4.chmodSync(directory, 448);
|
|
1196
|
-
}
|
|
1197
|
-
assertSecurePath(directory, "directory");
|
|
1198
|
-
const handoffPath = path3.join(directory, HANDOFF_FILENAME);
|
|
1199
|
-
const preloadPath = path3.join(directory, PRELOAD_FILENAME);
|
|
1200
|
-
writeExclusiveFile(preloadPath, renderChromeDevtoolsRelayPreloadSource());
|
|
1201
|
-
writeExclusiveFile(handoffPath, JSON.stringify({ Authorization: clientAuthorization }));
|
|
1202
|
-
const importFlag = `--import=${pathToFileURL2(preloadPath).href}`;
|
|
1203
|
-
const existingOptions = env.NODE_OPTIONS?.trim();
|
|
1204
|
-
let closed = false;
|
|
1205
|
-
return {
|
|
1206
|
-
env: {
|
|
1207
|
-
...env,
|
|
1208
|
-
NODE_OPTIONS: existingOptions ? `${existingOptions} ${importFlag}` : importFlag,
|
|
1209
|
-
[CHROME_RELAY_HANDOFF_ENV]: handoffPath
|
|
1210
|
-
},
|
|
1211
|
-
handoffPath,
|
|
1212
|
-
preloadPath,
|
|
1213
|
-
async close() {
|
|
1214
|
-
if (closed)
|
|
1215
|
-
return;
|
|
1216
|
-
closed = true;
|
|
1217
|
-
cleanupHandoff(directory, handoffPath, preloadPath);
|
|
1218
|
-
}
|
|
1219
|
-
};
|
|
1220
|
-
} catch (error) {
|
|
1221
|
-
if (directory)
|
|
1222
|
-
cleanupHandoff(directory, path3.join(directory, HANDOFF_FILENAME), path3.join(directory, PRELOAD_FILENAME));
|
|
1223
|
-
const diagnostic = error instanceof Error && error.message.startsWith("Windows ACL setup failed:") ? ` ${error.message}` : "";
|
|
1224
|
-
throw new Error(`Unable to install secure Chrome relay authorization handoff.${diagnostic}`, { cause: error });
|
|
1225
|
-
}
|
|
1226
|
-
}
|
|
1227
|
-
function createWindowsHandoffDirectory() {
|
|
1228
|
-
const directory = path3.join(os2.tmpdir(), `${HANDOFF_DIR_PREFIX}${randomBytes(16).toString("hex")}`);
|
|
1229
|
-
const script = String.raw`
|
|
1230
|
-
$ErrorActionPreference = 'Stop'
|
|
1231
|
-
$stage = 'identity'
|
|
1232
|
-
try {
|
|
1233
|
-
$target = $env:${WINDOWS_ACL_PATH_ENV}
|
|
1234
|
-
if ([System.IO.Directory]::Exists($target)) { throw 'handoff path already exists' }
|
|
1235
|
-
$sid = [System.Security.Principal.WindowsIdentity]::GetCurrent().User
|
|
1236
|
-
$stage = 'descriptor'
|
|
1237
|
-
$security = New-Object System.Security.AccessControl.DirectorySecurity
|
|
1238
|
-
$sidValue = $sid.Value
|
|
1239
|
-
$security.SetSecurityDescriptorSddlForm("O:$($sidValue)G:$($sidValue)D:P(A;OICI;FA;;;$($sidValue))")
|
|
1240
|
-
$directory = New-Object System.IO.DirectoryInfo -ArgumentList @($target)
|
|
1241
|
-
$stage = 'create'
|
|
1242
|
-
$directory.Create($security)
|
|
1243
|
-
$stage = 'acl-read'
|
|
1244
|
-
$check = $directory.GetAccessControl([System.Security.AccessControl.AccessControlSections]::All)
|
|
1245
|
-
$rules = @($check.GetAccessRules($true, $true, [System.Security.Principal.SecurityIdentifier]))
|
|
1246
|
-
$ownerSid = $check.GetOwner([System.Security.Principal.SecurityIdentifier]).Value
|
|
1247
|
-
$stage = 'acl-verify'
|
|
1248
|
-
if (-not $check.AreAccessRulesProtected -or $ownerSid -ne $sid.Value -or $rules.Count -ne 1) { throw 'unsafe ACL' }
|
|
1249
|
-
$ruleSid = $rules[0].IdentityReference.Value
|
|
1250
|
-
$hasFullControl = ($rules[0].FileSystemRights -band [System.Security.AccessControl.FileSystemRights]::FullControl) -eq [System.Security.AccessControl.FileSystemRights]::FullControl
|
|
1251
|
-
if ($ruleSid -ne $sid.Value -or $rules[0].IsInherited -or -not $hasFullControl -or $rules[0].AccessControlType -ne [System.Security.AccessControl.AccessControlType]::Allow) { throw 'unsafe ACL' }
|
|
1252
|
-
} catch {
|
|
1253
|
-
$type = $_.Exception.GetType().FullName
|
|
1254
|
-
$hresult = ('0x{0:X8}' -f ($_.Exception.HResult -band 0xffffffffL))
|
|
1255
|
-
[Console]::Error.WriteLine("stage=$stage type=$type hresult=$hresult")
|
|
1256
|
-
exit 1
|
|
1257
|
-
}
|
|
1258
|
-
`;
|
|
1259
|
-
const result = spawnSync(resolveSystemPowerShellPath(), ["-NoLogo", "-NoProfile", "-NonInteractive", "-Command", script], {
|
|
1260
|
-
encoding: "utf8",
|
|
1261
|
-
env: { ...process.env, [WINDOWS_ACL_PATH_ENV]: directory },
|
|
1262
|
-
stdio: ["ignore", "ignore", "pipe"],
|
|
1263
|
-
windowsHide: true
|
|
1264
|
-
});
|
|
1265
|
-
if (result.status !== 0) {
|
|
1266
|
-
try {
|
|
1267
|
-
fs4.rmdirSync(directory);
|
|
1268
|
-
} catch {
|
|
1269
|
-
}
|
|
1270
|
-
const stderr = typeof result.stderr === "string" ? result.stderr.trim() : "";
|
|
1271
|
-
const safeDetail = /^[A-Za-z0-9_.:=\- ]{1,240}$/u.test(stderr) ? stderr : `stage=spawn type=${result.error?.code ?? "unknown"} hresult=unknown`;
|
|
1272
|
-
throw new Error(`Windows ACL setup failed: ${safeDetail}`);
|
|
1273
|
-
}
|
|
1274
|
-
return directory;
|
|
1275
|
-
}
|
|
1276
|
-
function resolveSystemPowerShellPath() {
|
|
1277
|
-
const systemRoot = process.env.SystemRoot?.trim() || process.env.WINDIR?.trim();
|
|
1278
|
-
if (!systemRoot || !path3.win32.isAbsolute(systemRoot))
|
|
1279
|
-
throw new Error("unsafe Windows system root");
|
|
1280
|
-
const executable = path3.win32.join(systemRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe");
|
|
1281
|
-
const stat = fs4.lstatSync(executable);
|
|
1282
|
-
if (!stat.isFile() || stat.isSymbolicLink())
|
|
1283
|
-
throw new Error("unsafe Windows PowerShell path");
|
|
1284
|
-
return executable;
|
|
1285
|
-
}
|
|
1286
|
-
function writeExclusiveFile(filePath, contents) {
|
|
1287
|
-
const descriptor = fs4.openSync(filePath, fs4.constants.O_WRONLY | fs4.constants.O_CREAT | fs4.constants.O_EXCL | (fs4.constants.O_NOFOLLOW ?? 0), 384);
|
|
1288
|
-
try {
|
|
1289
|
-
if (process.platform !== "win32")
|
|
1290
|
-
fs4.fchmodSync(descriptor, 384);
|
|
1291
|
-
fs4.writeFileSync(descriptor, contents, "utf8");
|
|
1292
|
-
} finally {
|
|
1293
|
-
fs4.closeSync(descriptor);
|
|
1294
|
-
}
|
|
1295
|
-
assertSecurePath(filePath, "file");
|
|
1296
|
-
}
|
|
1297
|
-
function assertSecurePath(filePath, kind) {
|
|
1298
|
-
const stat = fs4.lstatSync(filePath);
|
|
1299
|
-
if ((kind === "directory" ? !stat.isDirectory() : !stat.isFile()) || stat.isSymbolicLink())
|
|
1300
|
-
throw new Error("unsafe");
|
|
1301
|
-
if (process.platform === "win32")
|
|
1302
|
-
return;
|
|
1303
|
-
if ((stat.mode & 63) !== 0)
|
|
1304
|
-
throw new Error("unsafe");
|
|
1305
|
-
if (typeof process.getuid === "function" && stat.uid !== process.getuid())
|
|
1306
|
-
throw new Error("unsafe");
|
|
1307
|
-
}
|
|
1308
|
-
function cleanupHandoff(directory, handoffPath, preloadPath) {
|
|
1309
|
-
for (const filePath of [handoffPath, preloadPath]) {
|
|
1310
|
-
try {
|
|
1311
|
-
fs4.unlinkSync(filePath);
|
|
1312
|
-
} catch {
|
|
1313
|
-
}
|
|
1314
|
-
}
|
|
1315
|
-
try {
|
|
1316
|
-
fs4.rmdirSync(directory);
|
|
1317
|
-
} catch {
|
|
1318
|
-
}
|
|
1319
|
-
}
|
|
1320
|
-
function renderChromeDevtoolsRelayPreloadSource() {
|
|
1321
|
-
return `import fs from 'node:fs';
|
|
1322
|
-
import path from 'node:path';
|
|
1323
|
-
|
|
1324
|
-
const targetPath = process.argv[1]?.replaceAll('\\\\', '/').toLowerCase();
|
|
1325
|
-
if (targetPath?.includes('chrome-devtools-mcp')) {
|
|
1326
|
-
const failureMessage = 'MCPorter Chrome relay authorization handoff unavailable.';
|
|
1327
|
-
const fail = () => {
|
|
1328
|
-
throw new Error(failureMessage);
|
|
1329
|
-
};
|
|
1330
|
-
const envName = ${JSON.stringify(CHROME_RELAY_HANDOFF_ENV)};
|
|
1331
|
-
const handoffPath = process.env[envName];
|
|
1332
|
-
delete process.env[envName];
|
|
1333
|
-
if (!handoffPath || !path.isAbsolute(handoffPath)) fail();
|
|
1334
|
-
|
|
1335
|
-
const directoryStat = (() => {
|
|
1336
|
-
try {
|
|
1337
|
-
return fs.lstatSync(path.dirname(handoffPath));
|
|
1338
|
-
} catch {
|
|
1339
|
-
return fail();
|
|
1340
|
-
}
|
|
1341
|
-
})();
|
|
1342
|
-
if (!directoryStat.isDirectory() || directoryStat.isSymbolicLink()) fail();
|
|
1343
|
-
if (process.platform !== 'win32') {
|
|
1344
|
-
if ((directoryStat.mode & 0o077) !== 0) fail();
|
|
1345
|
-
if (typeof process.getuid === 'function' && directoryStat.uid !== process.getuid()) fail();
|
|
1346
|
-
}
|
|
1347
|
-
|
|
1348
|
-
const descriptor = (() => {
|
|
1349
|
-
try {
|
|
1350
|
-
return fs.openSync(handoffPath, fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW ?? 0));
|
|
1351
|
-
} catch {
|
|
1352
|
-
return fail();
|
|
1353
|
-
}
|
|
1354
|
-
})();
|
|
1355
|
-
|
|
1356
|
-
let raw = '';
|
|
1357
|
-
let consumed = false;
|
|
1358
|
-
try {
|
|
1359
|
-
const stat = fs.fstatSync(descriptor);
|
|
1360
|
-
if (!stat.isFile() || stat.size <= 0 || stat.size > 4_096) fail();
|
|
1361
|
-
if (process.platform !== 'win32') {
|
|
1362
|
-
if ((stat.mode & 0o077) !== 0) fail();
|
|
1363
|
-
if (typeof process.getuid === 'function' && stat.uid !== process.getuid()) fail();
|
|
1364
|
-
}
|
|
1365
|
-
raw = fs.readFileSync(descriptor, 'utf8');
|
|
1366
|
-
try {
|
|
1367
|
-
fs.unlinkSync(handoffPath);
|
|
1368
|
-
consumed = true;
|
|
1369
|
-
} catch {
|
|
1370
|
-
try {
|
|
1371
|
-
fs.ftruncateSync(descriptor, 0);
|
|
1372
|
-
consumed = true;
|
|
1373
|
-
} catch {
|
|
1374
|
-
fail();
|
|
1375
|
-
}
|
|
1376
|
-
}
|
|
1377
|
-
} finally {
|
|
1378
|
-
fs.closeSync(descriptor);
|
|
3819
|
+
return { env, applied: true, patchPath };
|
|
1379
3820
|
}
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
3821
|
+
return {
|
|
3822
|
+
env: {
|
|
3823
|
+
...env,
|
|
3824
|
+
NODE_OPTIONS: existingOptions ? `${existingOptions} ${importFlag}` : importFlag
|
|
3825
|
+
},
|
|
3826
|
+
applied: true,
|
|
3827
|
+
patchPath
|
|
3828
|
+
};
|
|
3829
|
+
}
|
|
3830
|
+
function shouldApplyChromeDevtoolsCompat(command, args, env = process.env) {
|
|
3831
|
+
if (env.MCPORTER_DISABLE_CHROME_DEVTOOLS_COMPAT === "1") {
|
|
3832
|
+
return false;
|
|
3833
|
+
}
|
|
3834
|
+
return resolveChromeDevtoolsAutoConnectCommand(command, args).enabled;
|
|
3835
|
+
}
|
|
3836
|
+
function resolveChromeDevtoolsCompatPatchPath(candidates = defaultChromeDevtoolsPatchCandidates(), fallbackDir = os5.tmpdir()) {
|
|
3837
|
+
const existing = candidates.find((candidate) => fs7.existsSync(candidate));
|
|
3838
|
+
if (existing) {
|
|
3839
|
+
return existing;
|
|
3840
|
+
}
|
|
3841
|
+
return writeFallbackPatch(fallbackDir);
|
|
3842
|
+
}
|
|
3843
|
+
function defaultChromeDevtoolsPatchCandidates() {
|
|
3844
|
+
const here = path8.dirname(fileURLToPath(import.meta.url));
|
|
3845
|
+
return [
|
|
3846
|
+
path8.join(here, "chrome-devtools-auto-connect-patch.js"),
|
|
3847
|
+
path8.resolve(here, "..", "dist", "chrome-devtools-auto-connect-patch.js")
|
|
3848
|
+
];
|
|
3849
|
+
}
|
|
3850
|
+
function writeFallbackPatch(fallbackDir) {
|
|
3851
|
+
const patchPath = path8.join(fallbackDir, FALLBACK_PATCH_FILENAME);
|
|
1383
3852
|
try {
|
|
1384
|
-
|
|
3853
|
+
fs7.writeFileSync(patchPath, renderChromeDevtoolsAutoConnectPatchSource(), { mode: 384 });
|
|
3854
|
+
return patchPath;
|
|
1385
3855
|
} catch {
|
|
1386
|
-
|
|
3856
|
+
return void 0;
|
|
1387
3857
|
}
|
|
1388
|
-
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) fail();
|
|
1389
|
-
const entries = Object.entries(parsed);
|
|
1390
|
-
if (entries.length !== 1 || entries[0]?.[0] !== 'Authorization') fail();
|
|
1391
|
-
const authorization = entries[0]?.[1];
|
|
1392
|
-
if (typeof authorization !== 'string' || !/^Bearer [A-Za-z0-9_-]{43}$/u.test(authorization)) fail();
|
|
1393
|
-
|
|
1394
|
-
const terminatorIndex = process.argv.indexOf('--', 2);
|
|
1395
|
-
const insertionIndex = terminatorIndex >= 0 ? terminatorIndex : process.argv.length;
|
|
1396
|
-
process.argv.splice(insertionIndex, 0, '--wsHeaders', JSON.stringify({ Authorization: authorization }));
|
|
1397
|
-
}
|
|
1398
|
-
`;
|
|
1399
3858
|
}
|
|
1400
3859
|
|
|
1401
|
-
// node_modules/.pnpm/mcporter@0.13.
|
|
3860
|
+
// node_modules/.pnpm/mcporter@0.13.10/node_modules/mcporter/dist/runtime/cached-auth.js
|
|
1402
3861
|
function hasAuthorizationHeader(headers) {
|
|
1403
3862
|
return Boolean(headers && Object.keys(headers).some((key) => key.toLowerCase() === "authorization"));
|
|
1404
3863
|
}
|
|
@@ -1461,15 +3920,15 @@ async function applyCachedAuthIfAvailable(definition, logger, allowCachedAuth) {
|
|
|
1461
3920
|
}
|
|
1462
3921
|
}
|
|
1463
3922
|
|
|
1464
|
-
// node_modules/.pnpm/mcporter@0.13.
|
|
1465
|
-
function materializeHeaders(headers, serverName) {
|
|
3923
|
+
// node_modules/.pnpm/mcporter@0.13.10/node_modules/mcporter/dist/runtime-header-utils.js
|
|
3924
|
+
function materializeHeaders(headers, serverName, env = process.env) {
|
|
1466
3925
|
if (!headers) {
|
|
1467
3926
|
return void 0;
|
|
1468
3927
|
}
|
|
1469
3928
|
const resolved = {};
|
|
1470
3929
|
for (const [key, value] of Object.entries(headers)) {
|
|
1471
3930
|
try {
|
|
1472
|
-
resolved[key] = resolveEnvPlaceholders(value);
|
|
3931
|
+
resolved[key] = resolveEnvPlaceholders(value, env);
|
|
1473
3932
|
} catch (error) {
|
|
1474
3933
|
const message = error instanceof Error ? error.message : String(error);
|
|
1475
3934
|
throw new Error(`Failed to resolve header '${key}' for server '${serverName}': ${message}`, { cause: error });
|
|
@@ -1478,8 +3937,8 @@ function materializeHeaders(headers, serverName) {
|
|
|
1478
3937
|
return resolved;
|
|
1479
3938
|
}
|
|
1480
3939
|
|
|
1481
|
-
// node_modules/.pnpm/mcporter@0.13.
|
|
1482
|
-
import
|
|
3940
|
+
// node_modules/.pnpm/mcporter@0.13.10/node_modules/mcporter/dist/runtime/node-http-fetch.js
|
|
3941
|
+
import http2 from "node:http";
|
|
1483
3942
|
import https from "node:https";
|
|
1484
3943
|
import { Buffer as Buffer2 } from "node:buffer";
|
|
1485
3944
|
import { Readable } from "node:stream";
|
|
@@ -1517,7 +3976,7 @@ async function nodeHttp1FetchWithRedirects(input, init, redirectCount) {
|
|
|
1517
3976
|
headers["content-length"] = String(Buffer2.byteLength(body));
|
|
1518
3977
|
}
|
|
1519
3978
|
return new Promise((resolve, reject) => {
|
|
1520
|
-
const client = url.protocol === "https:" ? https :
|
|
3979
|
+
const client = url.protocol === "https:" ? https : http2;
|
|
1521
3980
|
const request = client.request(url, {
|
|
1522
3981
|
method: init.method ?? "GET",
|
|
1523
3982
|
headers
|
|
@@ -1637,7 +4096,7 @@ async function materializeRequestBody(body) {
|
|
|
1637
4096
|
throw new TypeError("node-http1 fetch does not support streaming request bodies.");
|
|
1638
4097
|
}
|
|
1639
4098
|
|
|
1640
|
-
// node_modules/.pnpm/mcporter@0.13.
|
|
4099
|
+
// node_modules/.pnpm/mcporter@0.13.10/node_modules/mcporter/dist/runtime/http-transport.js
|
|
1641
4100
|
function extractTransportStatusCode(error) {
|
|
1642
4101
|
if (!error || typeof error !== "object")
|
|
1643
4102
|
return void 0;
|
|
@@ -1688,7 +4147,7 @@ function createHttpTransportOptions(definition, oauthSession, shouldEstablishOAu
|
|
|
1688
4147
|
const command = definition.command;
|
|
1689
4148
|
if (command.kind !== "http")
|
|
1690
4149
|
throw new Error(`Server '${definition.name}' is not configured for HTTP transport.`);
|
|
1691
|
-
const resolvedHeaders = materializeHeaders(command.headers, definition.name);
|
|
4150
|
+
const resolvedHeaders = materializeHeaders(command.headers, definition.name, definition.env);
|
|
1692
4151
|
const effectiveHeaders = shouldEstablishOAuth ? removeAuthorizationHeader(resolvedHeaders) : resolvedHeaders;
|
|
1693
4152
|
const trackedFetch = trackStandaloneSseFetch(resolveHttpFetchOverride(definition));
|
|
1694
4153
|
return {
|
|
@@ -1763,8 +4222,7 @@ async function connectHttpTransport(client, transport, oauthSession, logger, con
|
|
|
1763
4222
|
return await connectWithAuth(client, transport, oauthSession, logger, connectOptions);
|
|
1764
4223
|
} catch (error) {
|
|
1765
4224
|
if (!connectOptions.signal?.aborted) {
|
|
1766
|
-
await closeTransportAndWait(logger, transport
|
|
1767
|
-
});
|
|
4225
|
+
await closeTransportAndWait(logger, transport, { throwOnCloseError: true, requireRetirement: true });
|
|
1768
4226
|
}
|
|
1769
4227
|
throw error;
|
|
1770
4228
|
}
|
|
@@ -1861,213 +4319,7 @@ async function connectSseFallbackTransport(client, definition, command, transpor
|
|
|
1861
4319
|
}
|
|
1862
4320
|
}
|
|
1863
4321
|
|
|
1864
|
-
// node_modules/.pnpm
|
|
1865
|
-
var import_cross_spawn = __toESM(require_cross_spawn(), 1);
|
|
1866
|
-
import process2 from "node:process";
|
|
1867
|
-
import { PassThrough } from "node:stream";
|
|
1868
|
-
var DEFAULT_INHERITED_ENV_VARS = process2.platform === "win32" ? [
|
|
1869
|
-
"APPDATA",
|
|
1870
|
-
"HOMEDRIVE",
|
|
1871
|
-
"HOMEPATH",
|
|
1872
|
-
"LOCALAPPDATA",
|
|
1873
|
-
"PATH",
|
|
1874
|
-
"PROCESSOR_ARCHITECTURE",
|
|
1875
|
-
"SYSTEMDRIVE",
|
|
1876
|
-
"SYSTEMROOT",
|
|
1877
|
-
"TEMP",
|
|
1878
|
-
"USERNAME",
|
|
1879
|
-
"USERPROFILE",
|
|
1880
|
-
"PROGRAMFILES"
|
|
1881
|
-
] : [
|
|
1882
|
-
"HOME",
|
|
1883
|
-
"LOGNAME",
|
|
1884
|
-
"PATH",
|
|
1885
|
-
"SHELL",
|
|
1886
|
-
"TERM",
|
|
1887
|
-
"USER"
|
|
1888
|
-
];
|
|
1889
|
-
function getDefaultEnvironment() {
|
|
1890
|
-
const env = {};
|
|
1891
|
-
for (const key of DEFAULT_INHERITED_ENV_VARS) {
|
|
1892
|
-
const value = process2.env[key];
|
|
1893
|
-
if (value === void 0) continue;
|
|
1894
|
-
if (value.startsWith("()")) continue;
|
|
1895
|
-
env[key] = value;
|
|
1896
|
-
}
|
|
1897
|
-
return env;
|
|
1898
|
-
}
|
|
1899
|
-
var StdioClientTransport = class {
|
|
1900
|
-
_process;
|
|
1901
|
-
_readBuffer;
|
|
1902
|
-
_serverParams;
|
|
1903
|
-
_stderrStream = null;
|
|
1904
|
-
onclose;
|
|
1905
|
-
onerror;
|
|
1906
|
-
onmessage;
|
|
1907
|
-
constructor(server) {
|
|
1908
|
-
this._serverParams = server;
|
|
1909
|
-
this._readBuffer = new ReadBuffer({ maxBufferSize: server.maxBufferSize });
|
|
1910
|
-
if (server.stderr === "pipe" || server.stderr === "overlapped") this._stderrStream = new PassThrough();
|
|
1911
|
-
}
|
|
1912
|
-
/**
|
|
1913
|
-
* Starts the server process and prepares to communicate with it.
|
|
1914
|
-
*/
|
|
1915
|
-
async start() {
|
|
1916
|
-
if (this._process) throw new Error("StdioClientTransport already started! If using Client class, note that connect() calls start() automatically.");
|
|
1917
|
-
return new Promise((resolve, reject) => {
|
|
1918
|
-
this._process = (0, import_cross_spawn.default)(this._serverParams.command, this._serverParams.args ?? [], {
|
|
1919
|
-
env: {
|
|
1920
|
-
...getDefaultEnvironment(),
|
|
1921
|
-
...this._serverParams.env
|
|
1922
|
-
},
|
|
1923
|
-
stdio: [
|
|
1924
|
-
"pipe",
|
|
1925
|
-
"pipe",
|
|
1926
|
-
this._serverParams.stderr ?? "inherit"
|
|
1927
|
-
],
|
|
1928
|
-
shell: false,
|
|
1929
|
-
windowsHide: process2.platform === "win32",
|
|
1930
|
-
cwd: this._serverParams.cwd
|
|
1931
|
-
});
|
|
1932
|
-
this._process.on("error", (error) => {
|
|
1933
|
-
reject(error);
|
|
1934
|
-
this.onerror?.(error);
|
|
1935
|
-
});
|
|
1936
|
-
this._process.on("spawn", () => {
|
|
1937
|
-
resolve();
|
|
1938
|
-
});
|
|
1939
|
-
this._process.on("close", (_code) => {
|
|
1940
|
-
this._process = void 0;
|
|
1941
|
-
this.onclose?.();
|
|
1942
|
-
});
|
|
1943
|
-
this._process.stdin?.on("error", (error) => {
|
|
1944
|
-
this.onerror?.(error);
|
|
1945
|
-
});
|
|
1946
|
-
this._process.stdout?.on("data", (chunk) => {
|
|
1947
|
-
try {
|
|
1948
|
-
this._readBuffer.append(chunk);
|
|
1949
|
-
this.processReadBuffer();
|
|
1950
|
-
} catch (error) {
|
|
1951
|
-
this.onerror?.(error);
|
|
1952
|
-
this.close().catch(() => {
|
|
1953
|
-
});
|
|
1954
|
-
}
|
|
1955
|
-
});
|
|
1956
|
-
this._process.stdout?.on("error", (error) => {
|
|
1957
|
-
this.onerror?.(error);
|
|
1958
|
-
});
|
|
1959
|
-
if (this._stderrStream && this._process.stderr) this._process.stderr.pipe(this._stderrStream);
|
|
1960
|
-
});
|
|
1961
|
-
}
|
|
1962
|
-
/**
|
|
1963
|
-
* The `stderr` stream of the child process, if {@linkcode StdioServerParameters.stderr} was set to `"pipe"` or `"overlapped"`.
|
|
1964
|
-
*
|
|
1965
|
-
* If `stderr` piping was requested, a `PassThrough` stream is returned _immediately_, allowing callers to
|
|
1966
|
-
* attach listeners before the `start` method is invoked. This prevents loss of any early
|
|
1967
|
-
* error output emitted by the child process.
|
|
1968
|
-
*/
|
|
1969
|
-
get stderr() {
|
|
1970
|
-
if (this._stderrStream) return this._stderrStream;
|
|
1971
|
-
return this._process?.stderr ?? null;
|
|
1972
|
-
}
|
|
1973
|
-
/**
|
|
1974
|
-
* The child process pid spawned by this transport.
|
|
1975
|
-
*
|
|
1976
|
-
* This is only available after the transport has been started.
|
|
1977
|
-
*/
|
|
1978
|
-
get pid() {
|
|
1979
|
-
return this._process?.pid ?? null;
|
|
1980
|
-
}
|
|
1981
|
-
processReadBuffer() {
|
|
1982
|
-
while (true) try {
|
|
1983
|
-
const message = this._readBuffer.readMessage();
|
|
1984
|
-
if (message === null) break;
|
|
1985
|
-
this.onmessage?.(message);
|
|
1986
|
-
} catch (error) {
|
|
1987
|
-
this.onerror?.(error);
|
|
1988
|
-
}
|
|
1989
|
-
}
|
|
1990
|
-
/**
|
|
1991
|
-
* Reap a disposable probe sibling (see the version-negotiation sibling
|
|
1992
|
-
* flow): signal-first teardown awaiting process `exit` — never the `close`
|
|
1993
|
-
* event, so a helper process holding the child's stdio pipes can never
|
|
1994
|
-
* block disposal. Not part of the public transport lifecycle.
|
|
1995
|
-
*
|
|
1996
|
-
* @internal
|
|
1997
|
-
*/
|
|
1998
|
-
async _dispose() {
|
|
1999
|
-
const proc = this._process;
|
|
2000
|
-
this._process = void 0;
|
|
2001
|
-
if (proc && proc.exitCode === null && proc.signalCode === null) {
|
|
2002
|
-
const exited = new Promise((resolve) => proc.once("exit", () => resolve()));
|
|
2003
|
-
try {
|
|
2004
|
-
proc.stdin?.end();
|
|
2005
|
-
} catch {
|
|
2006
|
-
}
|
|
2007
|
-
try {
|
|
2008
|
-
proc.kill("SIGTERM");
|
|
2009
|
-
} catch {
|
|
2010
|
-
}
|
|
2011
|
-
await Promise.race([exited, new Promise((resolve) => setTimeout(resolve, 1e3).unref())]);
|
|
2012
|
-
if (proc.exitCode === null && proc.signalCode === null) try {
|
|
2013
|
-
proc.kill("SIGKILL");
|
|
2014
|
-
} catch {
|
|
2015
|
-
}
|
|
2016
|
-
await exited;
|
|
2017
|
-
}
|
|
2018
|
-
try {
|
|
2019
|
-
proc?.stdout?.destroy();
|
|
2020
|
-
} catch {
|
|
2021
|
-
}
|
|
2022
|
-
try {
|
|
2023
|
-
proc?.stdin?.destroy();
|
|
2024
|
-
} catch {
|
|
2025
|
-
}
|
|
2026
|
-
try {
|
|
2027
|
-
proc?.stderr?.destroy();
|
|
2028
|
-
} catch {
|
|
2029
|
-
}
|
|
2030
|
-
this._readBuffer.clear();
|
|
2031
|
-
}
|
|
2032
|
-
async close() {
|
|
2033
|
-
if (this._process) {
|
|
2034
|
-
const processToClose = this._process;
|
|
2035
|
-
this._process = void 0;
|
|
2036
|
-
const closePromise = new Promise((resolve) => {
|
|
2037
|
-
processToClose.once("close", () => {
|
|
2038
|
-
resolve();
|
|
2039
|
-
});
|
|
2040
|
-
});
|
|
2041
|
-
try {
|
|
2042
|
-
processToClose.stdin?.end();
|
|
2043
|
-
} catch {
|
|
2044
|
-
}
|
|
2045
|
-
await Promise.race([closePromise, new Promise((resolve) => setTimeout(resolve, 2e3).unref())]);
|
|
2046
|
-
if (processToClose.exitCode === null) {
|
|
2047
|
-
try {
|
|
2048
|
-
processToClose.kill("SIGTERM");
|
|
2049
|
-
} catch {
|
|
2050
|
-
}
|
|
2051
|
-
await Promise.race([closePromise, new Promise((resolve) => setTimeout(resolve, 2e3).unref())]);
|
|
2052
|
-
}
|
|
2053
|
-
if (processToClose.exitCode === null) try {
|
|
2054
|
-
processToClose.kill("SIGKILL");
|
|
2055
|
-
} catch {
|
|
2056
|
-
}
|
|
2057
|
-
}
|
|
2058
|
-
this._readBuffer.clear();
|
|
2059
|
-
}
|
|
2060
|
-
send(message) {
|
|
2061
|
-
return new Promise((resolve) => {
|
|
2062
|
-
if (!this._process?.stdin) throw new SdkError(SdkErrorCode.NotConnected, "Not connected");
|
|
2063
|
-
const json = serializeMessage(message);
|
|
2064
|
-
if (this._process.stdin.write(json)) resolve();
|
|
2065
|
-
else this._process.stdin.once("drain", resolve);
|
|
2066
|
-
});
|
|
2067
|
-
}
|
|
2068
|
-
};
|
|
2069
|
-
|
|
2070
|
-
// node_modules/.pnpm/mcporter@0.13.8/node_modules/mcporter/dist/runtime/stdio-transport.js
|
|
4322
|
+
// node_modules/.pnpm/mcporter@0.13.10/node_modules/mcporter/dist/runtime/stdio-transport.js
|
|
2071
4323
|
var McporterStdioTransport = class extends StdioClientTransport {
|
|
2072
4324
|
parameters;
|
|
2073
4325
|
meta;
|
|
@@ -2081,7 +4333,7 @@ var McporterStdioTransport = class extends StdioClientTransport {
|
|
|
2081
4333
|
this.closeDelegate?.();
|
|
2082
4334
|
};
|
|
2083
4335
|
messageInterceptor = (message) => {
|
|
2084
|
-
if (STDIO_TRACE_ENABLED)
|
|
4336
|
+
if (STDIO_TRACE_ENABLED && !this.parameters.redactDiagnostics)
|
|
2085
4337
|
this.meta.stdoutChunks?.push(JSON.stringify(message));
|
|
2086
4338
|
this.messageDelegate?.(message);
|
|
2087
4339
|
};
|
|
@@ -2101,7 +4353,8 @@ var McporterStdioTransport = class extends StdioClientTransport {
|
|
|
2101
4353
|
if (stderr) {
|
|
2102
4354
|
stderr.setEncoding?.("utf8");
|
|
2103
4355
|
const handleChunk = (chunk) => {
|
|
2104
|
-
this.
|
|
4356
|
+
if (!this.parameters.redactDiagnostics)
|
|
4357
|
+
this.meta.stderrChunks.push(Buffer.isBuffer(chunk) ? chunk.toString("utf8") : String(chunk));
|
|
2105
4358
|
};
|
|
2106
4359
|
stderr.on("data", handleChunk);
|
|
2107
4360
|
stderr.on("error", ignoreStdioEmitterError);
|
|
@@ -2109,17 +4362,17 @@ var McporterStdioTransport = class extends StdioClientTransport {
|
|
|
2109
4362
|
}
|
|
2110
4363
|
}
|
|
2111
4364
|
async start() {
|
|
2112
|
-
if (STDIO_TRACE_ENABLED)
|
|
4365
|
+
if (STDIO_TRACE_ENABLED && !this.parameters.redactDiagnostics)
|
|
2113
4366
|
console.log("[mcporter] STDIO trace: start() invoked for stdio transport.");
|
|
2114
4367
|
this.installInterceptors();
|
|
2115
4368
|
await super.start();
|
|
2116
|
-
if (STDIO_TRACE_ENABLED) {
|
|
4369
|
+
if (STDIO_TRACE_ENABLED && !this.parameters.redactDiagnostics) {
|
|
2117
4370
|
console.log(`[mcporter] STDIO trace: spawned ${this.meta.command ?? "stdio server"} (pid=${this.pid ?? "unknown"}).`);
|
|
2118
4371
|
}
|
|
2119
4372
|
}
|
|
2120
4373
|
async send(message) {
|
|
2121
4374
|
this.installInterceptors();
|
|
2122
|
-
if (STDIO_TRACE_ENABLED)
|
|
4375
|
+
if (STDIO_TRACE_ENABLED && !this.parameters.redactDiagnostics)
|
|
2123
4376
|
this.meta.stdinChunks?.push(JSON.stringify(message));
|
|
2124
4377
|
await super.send(message);
|
|
2125
4378
|
}
|
|
@@ -2151,7 +4404,7 @@ var McporterStdioTransport = class extends StdioClientTransport {
|
|
|
2151
4404
|
}
|
|
2152
4405
|
};
|
|
2153
4406
|
|
|
2154
|
-
// node_modules/.pnpm/mcporter@0.13.
|
|
4407
|
+
// node_modules/.pnpm/mcporter@0.13.10/node_modules/mcporter/dist/runtime/transport.js
|
|
2155
4408
|
function shouldUseModeForServer(definition, serverFilter) {
|
|
2156
4409
|
return !serverFilter || serverFilter === definition.name;
|
|
2157
4410
|
}
|
|
@@ -2212,9 +4465,16 @@ async function createReplayClientContext(definition, replayPath, clientInfo, opt
|
|
|
2212
4465
|
return { client, transport, definition, oauthSession: void 0 };
|
|
2213
4466
|
}
|
|
2214
4467
|
async function createStdioClientContext(client, definition, logger, options) {
|
|
2215
|
-
const mergedEnv =
|
|
2216
|
-
|
|
2217
|
-
|
|
4468
|
+
const mergedEnv = {
|
|
4469
|
+
...Object.fromEntries(DEFAULT_INHERITED_ENV_VARS.map((key) => [key, void 0])),
|
|
4470
|
+
...definition.env
|
|
4471
|
+
};
|
|
4472
|
+
if (isBrokerDefinition(definition) && isChromeDefinition(definition) && resolveChromeDevtoolsRelayPolicy(definition.chromeDevtoolsRelay, definition.env) !== "off") {
|
|
4473
|
+
mergedEnv.MCPORTER_CHROME_DEVTOOLS_RELAY_POLICY = "require";
|
|
4474
|
+
delete mergedEnv.MCPORTER_DISABLE_CHROME_DEVTOOLS_RELAY;
|
|
4475
|
+
}
|
|
4476
|
+
const command = resolveCommandArgument(definition.command.command, mergedEnv);
|
|
4477
|
+
const resolvedArgs = resolveCommandArguments(definition.command.args, mergedEnv);
|
|
2218
4478
|
const publishRelayDecision = (decision) => {
|
|
2219
4479
|
if (decision.reason === "not-eligible")
|
|
2220
4480
|
return;
|
|
@@ -2224,7 +4484,7 @@ async function createStdioClientContext(client, definition, logger, options) {
|
|
|
2224
4484
|
let relayDecision;
|
|
2225
4485
|
let relay;
|
|
2226
4486
|
try {
|
|
2227
|
-
relay = await rewriteChromeDevtoolsArgsForRelay(command, resolvedArgs, mergedEnv, { onDecision: (decision) => relayDecision = decision }, definition.chromeDevtoolsRelay);
|
|
4487
|
+
relay = await rewriteChromeDevtoolsArgsForRelay(command, resolvedArgs, mergedEnv, { onDecision: (decision) => relayDecision = decision }, isBrokerDefinition(definition) && isChromeDefinition(definition) && resolveChromeDevtoolsRelayPolicy(definition.chromeDevtoolsRelay, definition.env) !== "off" ? "require" : definition.chromeDevtoolsRelay);
|
|
2228
4488
|
} catch (error) {
|
|
2229
4489
|
if (relayDecision)
|
|
2230
4490
|
publishRelayDecision(relayDecision);
|
|
@@ -2266,6 +4526,7 @@ async function createStdioClientContext(client, definition, logger, options) {
|
|
|
2266
4526
|
args: commandArgs,
|
|
2267
4527
|
cwd: definition.command.cwd,
|
|
2268
4528
|
env: compat.env,
|
|
4529
|
+
redactDiagnostics: isBrokerDefinition(definition),
|
|
2269
4530
|
cleanup: activeProxy ? async () => {
|
|
2270
4531
|
await handoff?.close();
|
|
2271
4532
|
await activeProxy?.close();
|
|
@@ -2282,15 +4543,13 @@ async function createStdioClientContext(client, definition, logger, options) {
|
|
|
2282
4543
|
try {
|
|
2283
4544
|
transport = wrapRecordTransport(rawTransport, definition, options);
|
|
2284
4545
|
} catch (error) {
|
|
2285
|
-
await closeTransportAndWait(logger, rawTransport
|
|
2286
|
-
});
|
|
4546
|
+
await closeTransportAndWait(logger, rawTransport, { throwOnCloseError: true, requireRetirement: true });
|
|
2287
4547
|
throw error;
|
|
2288
4548
|
}
|
|
2289
4549
|
try {
|
|
2290
4550
|
await client.connect(transport, { signal: options.signal });
|
|
2291
4551
|
} catch (error) {
|
|
2292
|
-
await closeTransportAndWait(logger, transport
|
|
2293
|
-
});
|
|
4552
|
+
await closeTransportAndWait(logger, transport, { throwOnCloseError: true, requireRetirement: true });
|
|
2294
4553
|
throw error;
|
|
2295
4554
|
}
|
|
2296
4555
|
return { client, transport, definition, oauthSession: void 0 };
|
|
@@ -2299,8 +4558,10 @@ async function createClientContext(definition, logger, clientInfo, options = {})
|
|
|
2299
4558
|
if (options.replayPath && shouldUseModeForServer(definition, process.env.MCPORTER_REPLAY_SERVER)) {
|
|
2300
4559
|
return createReplayClientContext(definition, options.replayPath, clientInfo, options);
|
|
2301
4560
|
}
|
|
2302
|
-
|
|
2303
|
-
|
|
4561
|
+
assertChromeBrokerAuthority(definition);
|
|
4562
|
+
const env = isBrokerDefinition(definition) ? definition.env : resolveChromeDevtoolsRelayEnvironment(definition.env);
|
|
4563
|
+
return withRuntimeEnvironment(env ?? {}, async () => {
|
|
4564
|
+
const activeDefinition = await applyCachedAuthIfAvailable({ ...definition, env }, logger, options.allowCachedAuth);
|
|
2304
4565
|
if (activeDefinition.command.kind === "stdio") {
|
|
2305
4566
|
const stdioDefinition = activeDefinition;
|
|
2306
4567
|
try {
|
|
@@ -2327,7 +4588,7 @@ async function createClientContext(definition, logger, clientInfo, options = {})
|
|
|
2327
4588
|
});
|
|
2328
4589
|
}
|
|
2329
4590
|
|
|
2330
|
-
// node_modules/.pnpm/mcporter@0.13.
|
|
4591
|
+
// node_modules/.pnpm/mcporter@0.13.10/node_modules/mcporter/dist/runtime/connection-cache.js
|
|
2331
4592
|
var RuntimeConnectionCache = class {
|
|
2332
4593
|
definitions;
|
|
2333
4594
|
options;
|
|
@@ -2704,11 +4965,12 @@ var RuntimeConnectionCache = class {
|
|
|
2704
4965
|
}
|
|
2705
4966
|
}
|
|
2706
4967
|
async closeContext(context) {
|
|
2707
|
-
const propagateReplayCloseErrors = context.transport instanceof ReplayTransport;
|
|
4968
|
+
const propagateReplayCloseErrors = context.transport instanceof ReplayTransport || isBrokerDefinition(context.definition);
|
|
2708
4969
|
let closeError;
|
|
2709
4970
|
try {
|
|
2710
4971
|
await closeTransportAndWait(this.logger, context.transport, {
|
|
2711
4972
|
throwOnCloseError: propagateReplayCloseErrors,
|
|
4973
|
+
requireRetirement: isBrokerDefinition(context.definition),
|
|
2712
4974
|
close: async () => {
|
|
2713
4975
|
let firstError;
|
|
2714
4976
|
const clientClose = context.client.close;
|
|
@@ -2741,7 +5003,7 @@ var RuntimeConnectionCache = class {
|
|
|
2741
5003
|
}
|
|
2742
5004
|
}
|
|
2743
5005
|
async resetConnectionOnError(server, error, failedContext) {
|
|
2744
|
-
if (!shouldResetConnection(error)) {
|
|
5006
|
+
if (isBrokerDefinition(this.definitions.get(server.trim())) || !shouldResetConnection(error)) {
|
|
2745
5007
|
return;
|
|
2746
5008
|
}
|
|
2747
5009
|
const normalized = server.trim();
|
|
@@ -2880,7 +5142,7 @@ function connectionInfoFromClient(client) {
|
|
|
2880
5142
|
};
|
|
2881
5143
|
}
|
|
2882
5144
|
|
|
2883
|
-
// node_modules/.pnpm/mcporter@0.13.
|
|
5145
|
+
// node_modules/.pnpm/mcporter@0.13.10/node_modules/mcporter/dist/tool-filters.js
|
|
2884
5146
|
function validateToolFilters(name, filter) {
|
|
2885
5147
|
if (filter.allowedTools !== void 0 && filter.blockedTools !== void 0) {
|
|
2886
5148
|
throw new Error(`Server '${name}' cannot specify both allowedTools and blockedTools.`);
|
|
@@ -2902,7 +5164,7 @@ function filterTools(tools, filter) {
|
|
|
2902
5164
|
return tools.filter((tool) => isToolAllowed(tool.name, filter));
|
|
2903
5165
|
}
|
|
2904
5166
|
|
|
2905
|
-
// node_modules/.pnpm/mcporter@0.13.
|
|
5167
|
+
// node_modules/.pnpm/mcporter@0.13.10/node_modules/mcporter/dist/runtime.js
|
|
2906
5168
|
var PACKAGE_NAME = "mcporter";
|
|
2907
5169
|
var OAUTH_CODE_TIMEOUT_MS = resolveOAuthTimeoutFromEnv();
|
|
2908
5170
|
var MAX_RESOURCE_LIST_PAGES = 100;
|
|
@@ -2929,6 +5191,7 @@ var McpRuntime = class {
|
|
|
2929
5191
|
definitions;
|
|
2930
5192
|
connectionCache;
|
|
2931
5193
|
logger;
|
|
5194
|
+
clientInfo;
|
|
2932
5195
|
constructor(servers, options = {}) {
|
|
2933
5196
|
for (const server of servers) {
|
|
2934
5197
|
validateToolFilters(server.name, server);
|
|
@@ -2939,6 +5202,7 @@ var McpRuntime = class {
|
|
|
2939
5202
|
name: PACKAGE_NAME,
|
|
2940
5203
|
version: MCPORTER_VERSION
|
|
2941
5204
|
};
|
|
5205
|
+
this.clientInfo = clientInfo;
|
|
2942
5206
|
const recordSession = process.env.MCPORTER_RECORD;
|
|
2943
5207
|
const replaySession = process.env.MCPORTER_REPLAY;
|
|
2944
5208
|
if (recordSession && replaySession) {
|
|
@@ -2964,6 +5228,9 @@ var McpRuntime = class {
|
|
|
2964
5228
|
elicitationHandler: options.elicitationHandler ?? defaultResponder.handler
|
|
2965
5229
|
});
|
|
2966
5230
|
}
|
|
5231
|
+
getClientInfo() {
|
|
5232
|
+
return { ...this.clientInfo };
|
|
5233
|
+
}
|
|
2967
5234
|
// listServers returns configured names sorted alphabetically for stable CLI output.
|
|
2968
5235
|
listServers() {
|
|
2969
5236
|
return [...this.definitions.keys()].toSorted((a, b) => a.localeCompare(b));
|
|
@@ -3013,6 +5280,7 @@ var McpRuntime = class {
|
|
|
3013
5280
|
let closeError;
|
|
3014
5281
|
let tools = [];
|
|
3015
5282
|
try {
|
|
5283
|
+
await validateBrokerRequestAuthority(this.definitions.get(server.trim()));
|
|
3016
5284
|
const listPromise = context.client.listTools(void 0, timeoutMs ? { timeout: timeoutMs, resetTimeoutOnProgress: true, maxTotalTimeout: timeoutMs } : void 0);
|
|
3017
5285
|
const response = timeoutMs ? await raceWithTimeout(listPromise, timeoutMs) : await listPromise;
|
|
3018
5286
|
tools = (response.tools ?? []).map((tool) => ({
|
|
@@ -3057,6 +5325,7 @@ var McpRuntime = class {
|
|
|
3057
5325
|
arguments: options.args ?? {}
|
|
3058
5326
|
};
|
|
3059
5327
|
const timeoutMs = normalizeTimeout(options.timeoutMs);
|
|
5328
|
+
await validateBrokerRequestAuthority(definition);
|
|
3060
5329
|
const resultPromise = client.callTool(params, {
|
|
3061
5330
|
timeout: timeoutMs,
|
|
3062
5331
|
// Long runs (e.g., GPT-5 Pro) emit progress/logging; allow that to refresh the timer.
|
|
@@ -3086,8 +5355,10 @@ var McpRuntime = class {
|
|
|
3086
5355
|
const { client } = context;
|
|
3087
5356
|
const requestParams = params;
|
|
3088
5357
|
if (requestParams.cursor !== void 0) {
|
|
5358
|
+
await validateBrokerRequestAuthority(this.definitions.get(server.trim()));
|
|
3089
5359
|
return await client.listResources(requestParams);
|
|
3090
5360
|
}
|
|
5361
|
+
await validateBrokerRequestAuthority(this.definitions.get(server.trim()));
|
|
3091
5362
|
let response = await client.request({ method: "resources/list", params: requestParams }, specTypeSchemas.ListResourcesResult);
|
|
3092
5363
|
const resources = [...response.resources];
|
|
3093
5364
|
const seenCursors = /* @__PURE__ */ new Set();
|
|
@@ -3095,6 +5366,7 @@ var McpRuntime = class {
|
|
|
3095
5366
|
if (seenCursors.has(response.nextCursor))
|
|
3096
5367
|
break;
|
|
3097
5368
|
seenCursors.add(response.nextCursor);
|
|
5369
|
+
await validateBrokerRequestAuthority(this.definitions.get(server.trim()));
|
|
3098
5370
|
response = await client.request({ method: "resources/list", params: { ...requestParams, cursor: response.nextCursor } }, specTypeSchemas.ListResourcesResult);
|
|
3099
5371
|
resources.push(...response.resources);
|
|
3100
5372
|
}
|
|
@@ -3114,6 +5386,7 @@ var McpRuntime = class {
|
|
|
3114
5386
|
disableOAuth: effectiveDisableOAuth
|
|
3115
5387
|
});
|
|
3116
5388
|
const { client } = context;
|
|
5389
|
+
await validateBrokerRequestAuthority(this.definitions.get(server.trim()));
|
|
3117
5390
|
return await client.readResource({ uri });
|
|
3118
5391
|
} catch (error) {
|
|
3119
5392
|
await this.resetConnectionOnError(server, error, context);
|
|
@@ -3144,6 +5417,23 @@ function createConsoleLogger(level = resolveLogLevelFromEnv()) {
|
|
|
3144
5417
|
}
|
|
3145
5418
|
|
|
3146
5419
|
export {
|
|
5420
|
+
getChromeDevtoolsRelayDecision,
|
|
5421
|
+
effectiveDefinition,
|
|
5422
|
+
connectionIdentity,
|
|
5423
|
+
isChromeDefinition,
|
|
5424
|
+
resolveSystemPowerShellPath,
|
|
5425
|
+
daemonRunDir,
|
|
5426
|
+
secureDaemonDirectory,
|
|
5427
|
+
inspectLegacyDaemonDirectory,
|
|
5428
|
+
upgradeLegacyDaemonDirectory,
|
|
5429
|
+
getDaemonMetadataPath,
|
|
5430
|
+
getDaemonSocketPath,
|
|
5431
|
+
getDaemonLogPath,
|
|
5432
|
+
canonicalUserConfiguration,
|
|
5433
|
+
BrowserOwner,
|
|
5434
|
+
authorizeBrokerDefinition,
|
|
5435
|
+
isToolAllowed,
|
|
5436
|
+
filterTools,
|
|
3147
5437
|
createRuntime,
|
|
3148
5438
|
callOnce
|
|
3149
5439
|
};
|