@introspection-ai/recipes 0.26.0 → 0.27.2
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/api/mcp.d.ts +3 -3
- package/dist/api/mcp.d.ts.map +1 -1
- package/dist/api/mcp.js +1 -1
- package/dist/api/mcp.js.map +1 -1
- package/dist/{child-agent-completions.d.ts → child/agent-completions.d.ts} +2 -2
- package/dist/child/agent-completions.d.ts.map +1 -0
- package/dist/{child-agent-completions.js → child/agent-completions.js} +1 -1
- package/dist/child/agent-completions.js.map +1 -0
- package/dist/{child-agent-store.d.ts → child/agent-store.d.ts} +1 -1
- package/dist/child/agent-store.d.ts.map +1 -0
- package/dist/{child-agent-store.js → child/agent-store.js} +1 -1
- package/dist/child/agent-store.js.map +1 -0
- package/dist/{child-agent.d.ts → child/agent.d.ts} +2 -2
- package/dist/child/agent.d.ts.map +1 -0
- package/dist/{child-agent.js → child/agent.js} +5 -5
- package/dist/child/agent.js.map +1 -0
- package/dist/{child-session.d.ts → child/session.d.ts} +4 -4
- package/dist/child/session.d.ts.map +1 -0
- package/dist/{child-session.js → child/session.js} +2 -2
- package/dist/child/session.js.map +1 -0
- package/dist/connector-tools.d.ts +1 -1
- package/dist/connector-tools.js +2 -2
- package/dist/inspect.d.ts +1 -1
- package/dist/inspect.js +1 -1
- package/dist/{mcp-catalog.d.ts → mcp/catalog.d.ts} +3 -3
- package/dist/mcp/catalog.d.ts.map +1 -0
- package/dist/{mcp-catalog.js → mcp/catalog.js} +2 -2
- package/dist/mcp/catalog.js.map +1 -0
- package/dist/{mcp-command-context.d.ts → mcp/cli/command-context.d.ts} +1 -1
- package/dist/mcp/cli/command-context.d.ts.map +1 -0
- package/dist/{mcp-command-context.js → mcp/cli/command-context.js} +1 -1
- package/dist/mcp/cli/command-context.js.map +1 -0
- package/dist/{mcp-cli-core.d.ts → mcp/cli/core.d.ts} +14 -8
- package/dist/mcp/cli/core.d.ts.map +1 -0
- package/dist/{mcp-cli-core.js → mcp/cli/core.js} +105 -146
- package/dist/mcp/cli/core.js.map +1 -0
- package/dist/mcp/cli/help.d.ts +6 -0
- package/dist/mcp/cli/help.d.ts.map +1 -0
- package/dist/mcp/cli/help.js +116 -0
- package/dist/mcp/cli/help.js.map +1 -0
- package/dist/mcp/cli/index.d.ts +3 -0
- package/dist/mcp/cli/index.d.ts.map +1 -0
- package/dist/{mcp-cli.js → mcp/cli/index.js} +4 -4
- package/dist/mcp/cli/index.js.map +1 -0
- package/dist/{mcp-cli-policy.d.ts → mcp/cli/policy.d.ts} +2 -2
- package/dist/mcp/cli/policy.d.ts.map +1 -0
- package/dist/{mcp-cli-policy.js → mcp/cli/policy.js} +2 -2
- package/dist/mcp/cli/policy.js.map +1 -0
- package/dist/mcp/cli/run-worker.d.ts +2 -0
- package/dist/mcp/cli/run-worker.d.ts.map +1 -0
- package/dist/mcp/cli/run-worker.js +54 -0
- package/dist/mcp/cli/run-worker.js.map +1 -0
- package/dist/mcp/client-entry.d.ts +3 -0
- package/dist/mcp/client-entry.d.ts.map +1 -0
- package/dist/{mcp-client.js → mcp/client-entry.js} +3 -3
- package/dist/mcp/client-entry.js.map +1 -0
- package/dist/{mcp-contract.d.ts → mcp/contract.d.ts} +2 -2
- package/dist/mcp/contract.d.ts.map +1 -0
- package/dist/{mcp-contract.js → mcp/contract.js} +1 -1
- package/dist/mcp/contract.js.map +1 -0
- package/dist/{mcp-daemon-client.d.ts → mcp/daemon/client.d.ts} +3 -2
- package/dist/mcp/daemon/client.d.ts.map +1 -0
- package/dist/{mcp-daemon-client.js → mcp/daemon/client.js} +12 -8
- package/dist/mcp/daemon/client.js.map +1 -0
- package/dist/mcp/daemon/index.d.ts +3 -0
- package/dist/mcp/daemon/index.d.ts.map +1 -0
- package/dist/mcp/daemon/index.js +453 -0
- package/dist/{mcp-daemon.js.map → mcp/daemon/index.js.map} +1 -1
- package/dist/{mcp-daemon-protocol.d.ts → mcp/daemon/protocol.d.ts} +2 -2
- package/dist/mcp/daemon/protocol.d.ts.map +1 -0
- package/dist/{mcp-daemon-protocol.js → mcp/daemon/protocol.js} +1 -1
- package/dist/mcp/daemon/protocol.js.map +1 -0
- package/dist/{mcp.d.ts → mcp/index.d.ts} +5 -5
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/{mcp.js → mcp/index.js} +16 -10
- package/dist/mcp/index.js.map +1 -0
- package/dist/{mcp-policy.d.ts → mcp/policy.d.ts} +3 -3
- package/dist/mcp/policy.d.ts.map +1 -0
- package/dist/{mcp-policy.js → mcp/policy.js} +1 -1
- package/dist/mcp/policy.js.map +1 -0
- package/dist/{mcp-tools.d.ts → mcp/tools.d.ts} +4 -4
- package/dist/mcp/tools.d.ts.map +1 -0
- package/dist/{mcp-tools.js → mcp/tools.js} +4 -4
- package/dist/mcp/tools.js.map +1 -0
- package/dist/{mcp-trace-context.d.ts → mcp/trace-context.d.ts} +2 -2
- package/dist/mcp/trace-context.d.ts.map +1 -0
- package/dist/{mcp-trace-context.js → mcp/trace-context.js} +1 -1
- package/dist/mcp/trace-context.js.map +1 -0
- package/dist/mcp-chunks/{auth-command-JBYNHHMV.js → auth-command-GQFV6QYP.js} +22 -21
- package/dist/mcp-chunks/call-arguments-VT7JLNJY.js +16 -0
- package/dist/mcp-chunks/{call-command-467BVXTE.js → call-command-LMAREWKD.js} +45 -44
- package/dist/mcp-chunks/{chunk-I7JR2GIC.js → chunk-2HOMVWSA.js} +2 -2
- package/dist/mcp-chunks/{chunk-CZ43ZITK.js → chunk-2P2HVZ3G.js} +1 -1
- package/dist/mcp-chunks/{chunk-SUNOYTQJ.js → chunk-34QXE62Y.js} +175 -147
- package/dist/mcp-chunks/{chunk-TFU4NKFJ.js → chunk-5B7GRMUK.js} +1 -1
- package/dist/mcp-chunks/{chunk-LG3GLOSA.js → chunk-5BMCVE2J.js} +14 -11
- package/dist/mcp-chunks/{chunk-MJHMXYMO.js → chunk-6QZQZXDJ.js} +3 -3
- package/dist/mcp-chunks/{chunk-DMBE47LM.js → chunk-6RG6AD5X.js} +6 -6
- package/dist/mcp-chunks/{chunk-UCJ7YDFN.js → chunk-77BZYP72.js} +1 -1
- package/dist/mcp-chunks/{chunk-NTABJCUL.js → chunk-7PFJCLXL.js} +10 -10
- package/dist/mcp-chunks/{chunk-66P5FXP3.js → chunk-7WL4TMON.js} +4 -4
- package/dist/mcp-chunks/{chunk-HKZZGSDV.js → chunk-C2VLY7ED.js} +4 -4
- package/dist/mcp-chunks/{chunk-XCNWICLD.js → chunk-CLR6DJV3.js} +1 -1
- package/dist/mcp-chunks/{chunk-REEFOL44.js → chunk-D6B2M5V7.js} +1 -1
- package/dist/mcp-chunks/{chunk-EP22XMWK.js → chunk-D6SVCPN6.js} +1 -1
- package/dist/mcp-chunks/{chunk-BTU34BUG.js → chunk-FBVRU4Y3.js} +43 -30
- package/dist/mcp-chunks/{chunk-TGPIW6F6.js → chunk-FONFU4G5.js} +1 -1
- package/dist/mcp-chunks/chunk-H4KL74PV.js +2073 -0
- package/dist/mcp-chunks/{chunk-QIBGY4IX.js → chunk-H7ARMBV7.js} +4 -9
- package/dist/mcp-chunks/{chunk-OABFKFSL.js → chunk-HZ2SX5U6.js} +39 -34
- package/dist/mcp-chunks/{chunk-HTXQPQPB.js → chunk-I6UMKX5E.js} +4 -4
- package/dist/mcp-chunks/{chunk-Z6QEOUMK.js → chunk-IF6LRV3J.js} +8 -14
- package/dist/mcp-chunks/{chunk-HBN3ESMH.js → chunk-JVYCENPP.js} +4 -4
- package/dist/mcp-chunks/{chunk-IO2IMWYS.js → chunk-KOWNVUL2.js} +34 -7
- package/dist/mcp-chunks/{chunk-AS27BTCS.js → chunk-KQCLM7R7.js} +4 -4
- package/dist/mcp-chunks/{chunk-AP5VHS73.js → chunk-L6OOGZVA.js} +2 -2
- package/dist/mcp-chunks/{chunk-BC2HDCYV.js → chunk-LCBDVFVA.js} +1 -1
- package/dist/mcp-chunks/{chunk-EUDC3ICZ.js → chunk-LIEFPSSB.js} +5 -5
- package/dist/mcp-chunks/{chunk-YPTN46JQ.js → chunk-LQIE4PKL.js} +2 -2
- package/dist/mcp-chunks/{chunk-GK4LA4E6.js → chunk-LXHPDKEF.js} +1 -1
- package/dist/mcp-chunks/{chunk-XFQSUC7T.js → chunk-MJFZM5EV.js} +5 -5
- package/dist/mcp-chunks/{chunk-WUEDIOR5.js → chunk-NQW3ES2G.js} +2 -8
- package/dist/mcp-chunks/{chunk-3Q2Q7SBU.js → chunk-O2VOMKAS.js} +13 -13
- package/dist/mcp-chunks/{chunk-GL4AA5QV.js → chunk-O4JIUOWF.js} +2 -2
- package/dist/mcp-chunks/{chunk-GEVQ2GJW.js → chunk-P3UVILEW.js} +2 -2
- package/dist/mcp-chunks/{chunk-N7ZWX3WW.js → chunk-P463YBJM.js} +1 -1
- package/dist/mcp-chunks/{chunk-Y3L5LWBY.js → chunk-PJCMJ65D.js} +277 -2110
- package/dist/mcp-chunks/{chunk-LTR626QF.js → chunk-QALB3DRW.js} +1 -1
- package/dist/mcp-chunks/{chunk-D2P2IV4H.js → chunk-QJNF3A3F.js} +1 -1
- package/dist/mcp-chunks/{chunk-KJNNO5Z6.js → chunk-RIHPVCGI.js} +1 -1
- package/dist/mcp-chunks/{chunk-J2WP6ERZ.js → chunk-RZIJUJGL.js} +44 -1055
- package/dist/mcp-chunks/{chunk-L2LGMX5P.js → chunk-S2YF442K.js} +2 -2
- package/dist/mcp-chunks/{chunk-CN5YF3IQ.js → chunk-S5F3HEZZ.js} +1848 -1046
- package/dist/mcp-chunks/{chunk-6DVS7RBO.js → chunk-S6GJ42I2.js} +1 -1
- package/dist/mcp-chunks/chunk-T4MQCFIK.js +982 -0
- package/dist/mcp-chunks/{chunk-OB256NMD.js → chunk-TUQGOYK6.js} +1 -1
- package/dist/mcp-chunks/{chunk-M62ESEOY.js → chunk-TYYSBILI.js} +10 -22
- package/dist/mcp-chunks/{chunk-LYWEATXZ.js → chunk-UHO6KIUJ.js} +2 -2
- package/dist/mcp-chunks/{chunk-BSCNF4NY.js → chunk-UIPG6W7N.js} +2 -2
- package/dist/mcp-chunks/{chunk-AK5ZEDEF.js → chunk-UKM35J67.js} +2 -2
- package/dist/mcp-chunks/{chunk-G4H535MI.js → chunk-UYJZJXC2.js} +21 -3
- package/dist/mcp-chunks/{chunk-GA4ZYSRX.js → chunk-VJIAHZZG.js} +4 -4
- package/dist/mcp-chunks/{chunk-OZQTZWW4.js → chunk-WHJUDBPR.js} +1 -1
- package/dist/mcp-chunks/{chunk-YI5SMRFH.js → chunk-XNHGMWN2.js} +7 -7
- package/dist/mcp-chunks/{cli-TN6JTLTS.js → cli-JI7MEAAX.js} +70 -69
- package/dist/mcp-chunks/client-QU7ABSNB.js +30 -0
- package/dist/mcp-chunks/{config-command-LICU56ZH.js → config-command-GRZLKD3R.js} +36 -28
- package/dist/mcp-chunks/daemon-command-3266CYHO.js +30 -0
- package/dist/mcp-chunks/{dist-MFAWSKGH.js → dist-USMCQ22V.js} +71 -47
- package/dist/mcp-chunks/{emit-ts-command-FVX3JMSN.js → emit-ts-command-7VLESP7H.js} +18 -17
- package/dist/mcp-chunks/{generate-cli-runner-L2L7VMCA.js → generate-cli-runner-SSW2B72K.js} +43 -42
- package/dist/mcp-chunks/generated-daemon-runtime-7CFVWLC7.js +30 -0
- package/dist/mcp-chunks/{inspect-cli-command-Y5D3GRE2.js → inspect-cli-command-NCGCMOIS.js} +9 -9
- package/dist/mcp-chunks/{launch-ZFY6CM2A.js → launch-NBONSPNK.js} +1 -1
- package/dist/mcp-chunks/{lifecycle-H7DIW5DM.js → lifecycle-CXAKUCN4.js} +1 -1
- package/dist/mcp-chunks/{list-command-55LCLW4S.js → list-command-5T4UF4PF.js} +56 -69
- package/dist/mcp-chunks/{output-utils-YYHI637S.js → output-utils-YFTWQZ5V.js} +3 -3
- package/dist/mcp-chunks/{prompt-CH6TK0bC-DZLFAAPK.js → prompt-CH6TK0bC-6DI4RA4E.js} +1 -1
- package/dist/mcp-chunks/{record-command-PWIBYC5J.js → record-command-TE5O7WBL.js} +5 -5
- package/dist/mcp-chunks/{replay-command-QHZZZMXF.js → replay-command-QCUL5ASR.js} +6 -6
- package/dist/mcp-chunks/{resource-command-HMR2KXWK.js → resource-command-XMGF7EAK.js} +9 -9
- package/dist/mcp-chunks/{result-utils-UGCQSRJ4.js → result-utils-HKB42M7Y.js} +3 -3
- package/dist/mcp-chunks/runtime-3X2XVS6X.js +37 -0
- package/dist/mcp-chunks/{runtime-wrapper-I4B3OROR.js → runtime-wrapper-MI3HF45J.js} +3 -3
- package/dist/mcp-chunks/{serve-command-NTDI3YPN.js → serve-command-5HV3ZE25.js} +25 -24
- package/dist/mcp-chunks/{timeouts-35UDWFA7.js → timeouts-YBFBTLV2.js} +8 -5
- package/dist/mcp-chunks/{vault-command-D5QZL4FI.js → vault-command-FC2PD5U6.js} +4 -4
- package/dist/mcp-chunks/webapi-IFUPO72B.js +2481 -0
- package/dist/mcp-daemon.js +35 -28
- package/dist/mcp-run-worker.js +28 -27
- package/dist/pi-extension.d.ts +1 -1
- package/dist/pi-extension.d.ts.map +1 -1
- package/dist/pi-extension.js +10 -9
- package/dist/pi-extension.js.map +1 -1
- package/dist/{recipe-agent.d.ts → recipe/agent.d.ts} +3 -3
- package/dist/recipe/agent.d.ts.map +1 -0
- package/dist/{recipe-agent.js → recipe/agent.js} +5 -5
- package/dist/recipe/agent.js.map +1 -0
- package/dist/{recipe-extensions.d.ts → recipe/extensions.d.ts} +1 -1
- package/dist/recipe/extensions.d.ts.map +1 -0
- package/dist/{recipe-extensions.js → recipe/extensions.js} +1 -1
- package/dist/recipe/extensions.js.map +1 -0
- package/dist/{recipe-mcp-config.d.ts → recipe/mcp-config.d.ts} +2 -2
- package/dist/recipe/mcp-config.d.ts.map +1 -0
- package/dist/{recipe-mcp-config.js → recipe/mcp-config.js} +1 -1
- package/dist/recipe/mcp-config.js.map +1 -0
- package/dist/{recipe-model.d.ts → recipe/model.d.ts} +1 -1
- package/dist/recipe/model.d.ts.map +1 -0
- package/dist/{recipe-model.js → recipe/model.js} +1 -1
- package/dist/recipe/model.js.map +1 -0
- package/dist/{recipe-package.d.ts → recipe/package.d.ts} +1 -1
- package/dist/recipe/package.d.ts.map +1 -0
- package/dist/{recipe-package.js → recipe/package.js} +1 -1
- package/dist/recipe/package.js.map +1 -0
- package/dist/recipe/resolve.d.ts +6 -6
- package/dist/recipe/resolve.d.ts.map +1 -1
- package/dist/recipe/resolve.js +4 -4
- package/dist/recipe/resolve.js.map +1 -1
- package/dist/{recipe-session.d.ts → recipe/session.d.ts} +1 -1
- package/dist/recipe/session.d.ts.map +1 -0
- package/dist/{recipe-session.js → recipe/session.js} +1 -1
- package/dist/recipe/session.js.map +1 -0
- package/dist/recipe/skills-prompt.d.ts +30 -0
- package/dist/recipe/skills-prompt.d.ts.map +1 -0
- package/dist/recipe/skills-prompt.js +28 -0
- package/dist/recipe/skills-prompt.js.map +1 -0
- package/dist/{recipe-skills.d.ts → recipe/skills.d.ts} +1 -1
- package/dist/recipe/skills.d.ts.map +1 -0
- package/dist/{recipe-skills.js → recipe/skills.js} +2 -2
- package/dist/recipe/skills.js.map +1 -0
- package/dist/run-controller.js +2 -2
- package/dist/session.d.ts +1 -1
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +5 -5
- package/dist/session.js.map +1 -1
- package/docs/agent-composition.md +4 -1
- package/docs/channels.md +1 -1
- package/docs/recipe-format.md +4 -2
- package/docs/slack.md +1 -1
- package/package.json +6 -6
- package/dist/child-agent-completions.d.ts.map +0 -1
- package/dist/child-agent-completions.js.map +0 -1
- package/dist/child-agent-store.d.ts.map +0 -1
- package/dist/child-agent-store.js.map +0 -1
- package/dist/child-agent.d.ts.map +0 -1
- package/dist/child-agent.js.map +0 -1
- package/dist/child-session.d.ts.map +0 -1
- package/dist/child-session.js.map +0 -1
- package/dist/mcp-catalog.d.ts.map +0 -1
- package/dist/mcp-catalog.js.map +0 -1
- package/dist/mcp-chunks/call-arguments-S4EGFKUQ.js +0 -15
- package/dist/mcp-chunks/chunk-YVH36MGQ.js +0 -92
- package/dist/mcp-chunks/client-G33HPQAC.js +0 -29
- package/dist/mcp-chunks/daemon-command-JLVHGWHB.js +0 -29
- package/dist/mcp-chunks/generated-daemon-runtime-DC6IL7E7.js +0 -29
- package/dist/mcp-chunks/runtime-ZNFPZXKE.js +0 -36
- package/dist/mcp-chunks/webapi-4GWO2UBA.js +0 -3508
- package/dist/mcp-cli-core.d.ts.map +0 -1
- package/dist/mcp-cli-core.js.map +0 -1
- package/dist/mcp-cli-help.d.ts +0 -2
- package/dist/mcp-cli-help.d.ts.map +0 -1
- package/dist/mcp-cli-help.js +0 -36
- package/dist/mcp-cli-help.js.map +0 -1
- package/dist/mcp-cli-policy.d.ts.map +0 -1
- package/dist/mcp-cli-policy.js.map +0 -1
- package/dist/mcp-cli.d.ts +0 -3
- package/dist/mcp-cli.d.ts.map +0 -1
- package/dist/mcp-cli.js.map +0 -1
- package/dist/mcp-client.d.ts +0 -3
- package/dist/mcp-client.d.ts.map +0 -1
- package/dist/mcp-client.js.map +0 -1
- package/dist/mcp-command-context.d.ts.map +0 -1
- package/dist/mcp-command-context.js.map +0 -1
- package/dist/mcp-contract.d.ts.map +0 -1
- package/dist/mcp-contract.js.map +0 -1
- package/dist/mcp-daemon-client.d.ts.map +0 -1
- package/dist/mcp-daemon-client.js.map +0 -1
- package/dist/mcp-daemon-protocol.d.ts.map +0 -1
- package/dist/mcp-daemon-protocol.js.map +0 -1
- package/dist/mcp-daemon.d.ts +0 -3
- package/dist/mcp-daemon.d.ts.map +0 -1
- package/dist/mcp-policy.d.ts.map +0 -1
- package/dist/mcp-policy.js.map +0 -1
- package/dist/mcp-run-worker.d.ts +0 -2
- package/dist/mcp-run-worker.d.ts.map +0 -1
- package/dist/mcp-run-worker.js.map +0 -1
- package/dist/mcp-tools.d.ts.map +0 -1
- package/dist/mcp-tools.js.map +0 -1
- package/dist/mcp-trace-context.d.ts.map +0 -1
- package/dist/mcp-trace-context.js.map +0 -1
- package/dist/mcp.d.ts.map +0 -1
- package/dist/mcp.js.map +0 -1
- package/dist/recipe-agent.d.ts.map +0 -1
- package/dist/recipe-agent.js.map +0 -1
- package/dist/recipe-extensions.d.ts.map +0 -1
- package/dist/recipe-extensions.js.map +0 -1
- package/dist/recipe-mcp-config.d.ts.map +0 -1
- package/dist/recipe-mcp-config.js.map +0 -1
- package/dist/recipe-model.d.ts.map +0 -1
- package/dist/recipe-model.js.map +0 -1
- package/dist/recipe-package.d.ts.map +0 -1
- package/dist/recipe-package.js.map +0 -1
- package/dist/recipe-session.d.ts.map +0 -1
- package/dist/recipe-session.js.map +0 -1
- package/dist/recipe-skills.d.ts.map +0 -1
- package/dist/recipe-skills.js.map +0 -1
|
@@ -0,0 +1,2073 @@
|
|
|
1
|
+
import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
parseTree
|
|
4
|
+
} from "./chunk-T4MQCFIK.js";
|
|
5
|
+
import {
|
|
6
|
+
resolveEnvPlaceholders,
|
|
7
|
+
resolveEnvValue
|
|
8
|
+
} from "./chunk-D6SVCPN6.js";
|
|
9
|
+
|
|
10
|
+
// node_modules/.pnpm/mcporter@0.13.13/node_modules/mcporter/dist/chrome-devtools-command.js
|
|
11
|
+
var AUTO_CONNECT_FLAGS = /* @__PURE__ */ new Set(["--autoConnect", "--auto-connect"]);
|
|
12
|
+
var NEGATED_AUTO_CONNECT_FLAGS = /* @__PURE__ */ new Set(["--no-autoConnect", "--no-auto-connect"]);
|
|
13
|
+
var CONNECTION_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
14
|
+
"--browserUrl",
|
|
15
|
+
"--browser-url",
|
|
16
|
+
"--channel",
|
|
17
|
+
"--executablePath",
|
|
18
|
+
"--executable-path",
|
|
19
|
+
"--userDataDir",
|
|
20
|
+
"--user-data-dir",
|
|
21
|
+
"--wsEndpoint",
|
|
22
|
+
"--ws-endpoint",
|
|
23
|
+
"--wsHeaders",
|
|
24
|
+
"--ws-headers",
|
|
25
|
+
"-e",
|
|
26
|
+
"-u",
|
|
27
|
+
"-w"
|
|
28
|
+
]);
|
|
29
|
+
var NPX_VALUE_OPTIONS = /* @__PURE__ */ new Set([
|
|
30
|
+
"--cache",
|
|
31
|
+
"--package",
|
|
32
|
+
"--prefix",
|
|
33
|
+
"--registry",
|
|
34
|
+
"--userconfig",
|
|
35
|
+
"--workspace",
|
|
36
|
+
"-p",
|
|
37
|
+
"-w"
|
|
38
|
+
]);
|
|
39
|
+
var NPX_COMMAND_STRING_OPTIONS = /* @__PURE__ */ new Set(["--call", "-c"]);
|
|
40
|
+
var NPX_NO_VALUE_OPTIONS = /* @__PURE__ */ new Set(["--ignore-existing", "--no-install", "--quiet", "--silent", "--yes", "-y"]);
|
|
41
|
+
var COMMON_WRAPPERS = /* @__PURE__ */ new Set([
|
|
42
|
+
"ash",
|
|
43
|
+
"bash",
|
|
44
|
+
"csh",
|
|
45
|
+
"dash",
|
|
46
|
+
"cmd",
|
|
47
|
+
"doas",
|
|
48
|
+
"env",
|
|
49
|
+
"fish",
|
|
50
|
+
"ksh",
|
|
51
|
+
"mksh",
|
|
52
|
+
"node",
|
|
53
|
+
"npm",
|
|
54
|
+
"npx",
|
|
55
|
+
"bunx",
|
|
56
|
+
"pnpm",
|
|
57
|
+
"powershell",
|
|
58
|
+
"pwsh",
|
|
59
|
+
"sh",
|
|
60
|
+
"sudo",
|
|
61
|
+
"tcsh",
|
|
62
|
+
"yarn",
|
|
63
|
+
"zsh"
|
|
64
|
+
]);
|
|
65
|
+
function isChromeDevtoolsToken(token) {
|
|
66
|
+
const normalized = token.replaceAll("\\", "/").toLowerCase();
|
|
67
|
+
const basename = normalized.slice(normalized.lastIndexOf("/") + 1);
|
|
68
|
+
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);
|
|
69
|
+
}
|
|
70
|
+
function isExistingChromeDevtoolsCommand(command, args) {
|
|
71
|
+
if (isAmbiguousChromeDevtoolsAutoConnectCommand(command, args))
|
|
72
|
+
return true;
|
|
73
|
+
if (resolveChromeDevtoolsAutoConnectCommand(command, args).enabled)
|
|
74
|
+
return true;
|
|
75
|
+
const target = resolveChromeDevtoolsTarget(command, args);
|
|
76
|
+
if (!target)
|
|
77
|
+
return true;
|
|
78
|
+
return target.args.some((arg) => {
|
|
79
|
+
const option = arg.split("=")[0] ?? arg;
|
|
80
|
+
return CONNECTION_VALUE_FLAGS.has(option) || /^-[^-]/u.test(option) && [...CONNECTION_VALUE_FLAGS].some((flag) => flag.length === 2 && option.includes(flag.slice(1)));
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
function isAmbiguousChromeDevtoolsAutoConnectCommand(command, args) {
|
|
84
|
+
if (isChromeDevtoolsToken(command))
|
|
85
|
+
return false;
|
|
86
|
+
if (isSupportedPackageLauncher(command)) {
|
|
87
|
+
const target = findPackageLauncherTarget(args);
|
|
88
|
+
if (!target)
|
|
89
|
+
return false;
|
|
90
|
+
if (target.ambiguous)
|
|
91
|
+
return true;
|
|
92
|
+
const nestedTarget = commandBasename(args[target.index] ?? "");
|
|
93
|
+
return COMMON_WRAPPERS.has(nestedTarget) && containsChromeAutoConnectInvocation(args.slice(target.index + 1));
|
|
94
|
+
}
|
|
95
|
+
const launcher = commandBasename(command);
|
|
96
|
+
if (COMMON_WRAPPERS.has(launcher))
|
|
97
|
+
return containsChromeAutoConnectInvocation(args);
|
|
98
|
+
const scopedArgs = args.slice(0, optionTerminator(args));
|
|
99
|
+
const tokenIndex = scopedArgs.findIndex(isChromeDevtoolsToken);
|
|
100
|
+
return tokenIndex >= 0 && parseChromeDevtoolsAutoConnectArgs(scopedArgs.slice(tokenIndex + 1)).enabled;
|
|
101
|
+
}
|
|
102
|
+
function resolveChromeDevtoolsAutoConnectCommand(command, args) {
|
|
103
|
+
const target = resolveChromeDevtoolsTarget(command, args);
|
|
104
|
+
if (!target)
|
|
105
|
+
return { enabled: false, withoutAutoConnect: args };
|
|
106
|
+
const parsed = parseChromeDevtoolsAutoConnectArgs(target.args);
|
|
107
|
+
return {
|
|
108
|
+
enabled: parsed.enabled,
|
|
109
|
+
withoutAutoConnect: [...target.prefix, ...parsed.withoutAutoConnect]
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
function replaceChromeDevtoolsAutoConnectArgs(command, args, replacement) {
|
|
113
|
+
const target = resolveChromeDevtoolsTarget(command, args);
|
|
114
|
+
if (!target)
|
|
115
|
+
return args;
|
|
116
|
+
const parsed = parseChromeDevtoolsAutoConnectArgs(target.args);
|
|
117
|
+
const stripped = stripConnectionSelectionArgs(parsed.withoutAutoConnect);
|
|
118
|
+
const targetArgs = [...stripped.args];
|
|
119
|
+
targetArgs.splice(stripped.insertionIndex, 0, ...replacement);
|
|
120
|
+
return [...target.prefix, ...targetArgs];
|
|
121
|
+
}
|
|
122
|
+
function resolveChromeDevtoolsTarget(command, args) {
|
|
123
|
+
if (isChromeDevtoolsToken(command))
|
|
124
|
+
return { prefix: [], args };
|
|
125
|
+
if (!isSupportedPackageLauncher(command))
|
|
126
|
+
return void 0;
|
|
127
|
+
const target = findPackageLauncherTarget(args);
|
|
128
|
+
if (!target || target.ambiguous || !isChromeDevtoolsToken(args[target.index] ?? ""))
|
|
129
|
+
return void 0;
|
|
130
|
+
const separatorIndex = target.index + 1;
|
|
131
|
+
const consumesSeparator = target.consumePostTargetSeparator && args[separatorIndex] === "--";
|
|
132
|
+
const targetStart = consumesSeparator ? separatorIndex + 1 : separatorIndex;
|
|
133
|
+
return { prefix: args.slice(0, targetStart), args: args.slice(targetStart) };
|
|
134
|
+
}
|
|
135
|
+
function findPackageLauncherTarget(args) {
|
|
136
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
137
|
+
const arg = args[index];
|
|
138
|
+
if (arg === void 0)
|
|
139
|
+
continue;
|
|
140
|
+
if (arg === "--") {
|
|
141
|
+
return index + 1 < args.length ? { index: index + 1, ambiguous: false, consumePostTargetSeparator: false } : void 0;
|
|
142
|
+
}
|
|
143
|
+
if (arg.startsWith("--call=")) {
|
|
144
|
+
const value = arg.slice("--call=".length);
|
|
145
|
+
return containsInlineChromeAutoConnect([value]) ? { index, ambiguous: true, consumePostTargetSeparator: false } : void 0;
|
|
146
|
+
}
|
|
147
|
+
if (NPX_COMMAND_STRING_OPTIONS.has(arg)) {
|
|
148
|
+
const value = args[index + 1] ?? "";
|
|
149
|
+
return containsInlineChromeAutoConnect([value]) ? { index: index + 1, ambiguous: true, consumePostTargetSeparator: false } : void 0;
|
|
150
|
+
}
|
|
151
|
+
if (NPX_VALUE_OPTIONS.has(arg)) {
|
|
152
|
+
index += 1;
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
if (NPX_NO_VALUE_OPTIONS.has(arg) || /^--[^=]+=.+/u.test(arg))
|
|
156
|
+
continue;
|
|
157
|
+
if (arg.startsWith("-")) {
|
|
158
|
+
return {
|
|
159
|
+
index,
|
|
160
|
+
ambiguous: containsChromeAutoConnectInvocation(args.slice(index)),
|
|
161
|
+
consumePostTargetSeparator: false
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
return { index, ambiguous: false, consumePostTargetSeparator: true };
|
|
165
|
+
}
|
|
166
|
+
return void 0;
|
|
167
|
+
}
|
|
168
|
+
function isSupportedPackageLauncher(command) {
|
|
169
|
+
const launcher = commandBasename(command);
|
|
170
|
+
return launcher === "npx" || launcher === "bunx";
|
|
171
|
+
}
|
|
172
|
+
function containsChromeAutoConnectInvocation(args) {
|
|
173
|
+
const tokenIndex = args.findIndex(isChromeDevtoolsToken);
|
|
174
|
+
if (tokenIndex >= 0)
|
|
175
|
+
return parseChromeDevtoolsAutoConnectArgs(args.slice(tokenIndex + 1)).enabled;
|
|
176
|
+
return containsInlineChromeAutoConnect(args);
|
|
177
|
+
}
|
|
178
|
+
function containsInlineChromeAutoConnect(args) {
|
|
179
|
+
const rendered = args.join(" ").toLowerCase();
|
|
180
|
+
return rendered.includes("chrome-devtools-mcp") && /--(?:auto-connect|autoconnect)(?:=true)?(?:[;'"&|)\]}]|\s|$)/u.test(rendered);
|
|
181
|
+
}
|
|
182
|
+
function commandBasename(command) {
|
|
183
|
+
const normalized = command.replaceAll("\\", "/").toLowerCase();
|
|
184
|
+
return normalized.slice(normalized.lastIndexOf("/") + 1).replace(/\.(?:cmd|exe)$/u, "");
|
|
185
|
+
}
|
|
186
|
+
function optionTerminator(args) {
|
|
187
|
+
const index = args.indexOf("--");
|
|
188
|
+
return index >= 0 ? index : args.length;
|
|
189
|
+
}
|
|
190
|
+
function stripConnectionSelectionArgs(args) {
|
|
191
|
+
const result = [];
|
|
192
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
193
|
+
const arg = args[index];
|
|
194
|
+
if (arg === void 0)
|
|
195
|
+
continue;
|
|
196
|
+
if (arg === "--") {
|
|
197
|
+
result.push(...args.slice(index));
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
if (CONNECTION_VALUE_FLAGS.has(arg)) {
|
|
201
|
+
index += 1;
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
if ([...CONNECTION_VALUE_FLAGS].some((flag) => arg.startsWith(`${flag}=`)))
|
|
205
|
+
continue;
|
|
206
|
+
result.push(arg);
|
|
207
|
+
}
|
|
208
|
+
const terminatorIndex = result.indexOf("--");
|
|
209
|
+
return { args: result, insertionIndex: terminatorIndex >= 0 ? terminatorIndex : result.length };
|
|
210
|
+
}
|
|
211
|
+
function parseChromeDevtoolsAutoConnectArgs(args) {
|
|
212
|
+
const consumed = /* @__PURE__ */ new Set();
|
|
213
|
+
let enabled = false;
|
|
214
|
+
let sawOption = false;
|
|
215
|
+
let terminatorIndex = args.length;
|
|
216
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
217
|
+
const arg = args[index];
|
|
218
|
+
if (arg === void 0)
|
|
219
|
+
continue;
|
|
220
|
+
if (arg === "--") {
|
|
221
|
+
terminatorIndex = index;
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
224
|
+
if (NEGATED_AUTO_CONNECT_FLAGS.has(arg)) {
|
|
225
|
+
sawOption = true;
|
|
226
|
+
enabled = false;
|
|
227
|
+
consumed.add(index);
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
let matchedNegatedInline = false;
|
|
231
|
+
for (const flag of NEGATED_AUTO_CONNECT_FLAGS) {
|
|
232
|
+
if (!arg.startsWith(`${flag}=`))
|
|
233
|
+
continue;
|
|
234
|
+
sawOption = true;
|
|
235
|
+
enabled = false;
|
|
236
|
+
consumed.add(index);
|
|
237
|
+
matchedNegatedInline = true;
|
|
238
|
+
break;
|
|
239
|
+
}
|
|
240
|
+
if (matchedNegatedInline)
|
|
241
|
+
continue;
|
|
242
|
+
if (AUTO_CONNECT_FLAGS.has(arg)) {
|
|
243
|
+
sawOption = true;
|
|
244
|
+
consumed.add(index);
|
|
245
|
+
const pairedValue = booleanLiteral(args[index + 1]);
|
|
246
|
+
if (pairedValue !== void 0) {
|
|
247
|
+
enabled = pairedValue;
|
|
248
|
+
consumed.add(index + 1);
|
|
249
|
+
index += 1;
|
|
250
|
+
} else {
|
|
251
|
+
enabled = true;
|
|
252
|
+
}
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
for (const flag of AUTO_CONNECT_FLAGS) {
|
|
256
|
+
if (!arg.startsWith(`${flag}=`))
|
|
257
|
+
continue;
|
|
258
|
+
sawOption = true;
|
|
259
|
+
enabled = arg.slice(flag.length + 1) === "true";
|
|
260
|
+
consumed.add(index);
|
|
261
|
+
break;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
const withoutAutoConnect = args.filter((_arg, index) => !consumed.has(index));
|
|
265
|
+
let insertionIndex = 0;
|
|
266
|
+
for (let index = 0; index < terminatorIndex; index += 1) {
|
|
267
|
+
if (!consumed.has(index))
|
|
268
|
+
insertionIndex += 1;
|
|
269
|
+
}
|
|
270
|
+
return { enabled: sawOption && enabled, withoutAutoConnect, insertionIndex };
|
|
271
|
+
}
|
|
272
|
+
function booleanLiteral(value) {
|
|
273
|
+
if (value === "true")
|
|
274
|
+
return true;
|
|
275
|
+
if (value === "false")
|
|
276
|
+
return false;
|
|
277
|
+
return void 0;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// node_modules/.pnpm/mcporter@0.13.13/node_modules/mcporter/dist/runtime/utils.js
|
|
281
|
+
var ENV_PLACEHOLDER_PATTERN = /\$\{(?:env:[^}]*|[A-Za-z_][A-Za-z0-9_]*(?::-[^}]*)?)\}/;
|
|
282
|
+
var MAX_TIMER_DELAY_MS = 2147483647;
|
|
283
|
+
function resolveCommandArgument(value, env = process.env) {
|
|
284
|
+
if (!value) {
|
|
285
|
+
return value;
|
|
286
|
+
}
|
|
287
|
+
if (!value.includes("$")) {
|
|
288
|
+
return value;
|
|
289
|
+
}
|
|
290
|
+
const needsInterpolation = value.startsWith("$env:") || ENV_PLACEHOLDER_PATTERN.test(value);
|
|
291
|
+
if (!needsInterpolation) {
|
|
292
|
+
return value;
|
|
293
|
+
}
|
|
294
|
+
return resolveEnvPlaceholders(value, env);
|
|
295
|
+
}
|
|
296
|
+
function resolveCommandArguments(args, env = process.env) {
|
|
297
|
+
if (args.length === 0) {
|
|
298
|
+
return [];
|
|
299
|
+
}
|
|
300
|
+
return args.map((arg) => resolveCommandArgument(arg, env));
|
|
301
|
+
}
|
|
302
|
+
function normalizeTimeout(raw) {
|
|
303
|
+
if (raw == null) {
|
|
304
|
+
return void 0;
|
|
305
|
+
}
|
|
306
|
+
if (!Number.isFinite(raw)) {
|
|
307
|
+
return void 0;
|
|
308
|
+
}
|
|
309
|
+
const coerced = Math.trunc(raw);
|
|
310
|
+
return coerced > 0 ? Math.min(coerced, MAX_TIMER_DELAY_MS) : void 0;
|
|
311
|
+
}
|
|
312
|
+
function raceWithTimeout(promise, timeoutMs) {
|
|
313
|
+
return new Promise((resolve, reject) => {
|
|
314
|
+
const delayMs = Number.isFinite(timeoutMs) ? Math.min(Math.max(Math.trunc(timeoutMs), 1), MAX_TIMER_DELAY_MS) : MAX_TIMER_DELAY_MS;
|
|
315
|
+
const cancelTimeout = scheduleDeadline(() => {
|
|
316
|
+
reject(new Error("Timeout"));
|
|
317
|
+
}, delayMs);
|
|
318
|
+
promise.then((value) => {
|
|
319
|
+
cancelTimeout();
|
|
320
|
+
resolve(value);
|
|
321
|
+
}, (error) => {
|
|
322
|
+
cancelTimeout();
|
|
323
|
+
reject(error);
|
|
324
|
+
});
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
function scheduleDeadline(callback, delayMs) {
|
|
328
|
+
const deadline = performance.now() + delayMs;
|
|
329
|
+
let timer;
|
|
330
|
+
let cancelled = false;
|
|
331
|
+
const scheduleNext = () => {
|
|
332
|
+
if (cancelled)
|
|
333
|
+
return;
|
|
334
|
+
const remainingMs = deadline - performance.now();
|
|
335
|
+
if (remainingMs <= 0) {
|
|
336
|
+
callback();
|
|
337
|
+
} else if (remainingMs > 864e5) {
|
|
338
|
+
timer = setTimeout(scheduleNext, 864e5);
|
|
339
|
+
} else if (remainingMs > 36e5) {
|
|
340
|
+
timer = setTimeout(scheduleNext, 36e5);
|
|
341
|
+
} else if (remainingMs > 6e4) {
|
|
342
|
+
timer = setTimeout(scheduleNext, 6e4);
|
|
343
|
+
} else if (remainingMs > 1e3) {
|
|
344
|
+
timer = setTimeout(scheduleNext, 1e3);
|
|
345
|
+
} else if (remainingMs > 100) {
|
|
346
|
+
timer = setTimeout(scheduleNext, 100);
|
|
347
|
+
} else if (remainingMs > 10) {
|
|
348
|
+
timer = setTimeout(scheduleNext, 10);
|
|
349
|
+
} else {
|
|
350
|
+
timer = setTimeout(scheduleNext, 1);
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
scheduleNext();
|
|
354
|
+
return () => {
|
|
355
|
+
cancelled = true;
|
|
356
|
+
if (timer)
|
|
357
|
+
clearTimeout(timer);
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// node_modules/.pnpm/mcporter@0.13.13/node_modules/mcporter/dist/chrome-devtools-relay.js
|
|
362
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
363
|
+
import fs from "node:fs";
|
|
364
|
+
import os from "node:os";
|
|
365
|
+
import path2 from "node:path";
|
|
366
|
+
|
|
367
|
+
// node_modules/.pnpm/mcporter@0.13.13/node_modules/mcporter/dist/chrome-devtools-relay-proxy.js
|
|
368
|
+
import { createHash, randomBytes, timingSafeEqual } from "node:crypto";
|
|
369
|
+
import http, {} from "node:http";
|
|
370
|
+
var WEBSOCKET_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
|
371
|
+
async function startChromeDevtoolsRelayProxy(options) {
|
|
372
|
+
const sockets = /* @__PURE__ */ new Set();
|
|
373
|
+
const clientAuthorization = `Bearer ${randomBytes(32).toString("base64url")}`;
|
|
374
|
+
let downstreamAccepted = false;
|
|
375
|
+
let closed = false;
|
|
376
|
+
const lifetime = new AbortController();
|
|
377
|
+
let closing;
|
|
378
|
+
const server = http.createServer((_request, response) => {
|
|
379
|
+
response.writeHead(404, { Connection: "close", "Content-Length": "0" }).end();
|
|
380
|
+
});
|
|
381
|
+
const binding = Promise.withResolvers();
|
|
382
|
+
const terminate = (reason) => {
|
|
383
|
+
if (closing)
|
|
384
|
+
return closing;
|
|
385
|
+
closed = true;
|
|
386
|
+
closing = binding.promise.catch(() => {
|
|
387
|
+
}).then(() => closeServer(server, sockets));
|
|
388
|
+
lifetime.abort(reason);
|
|
389
|
+
closeUpstream(options.upstream.socket);
|
|
390
|
+
for (const socket of sockets)
|
|
391
|
+
socket.destroy();
|
|
392
|
+
return closing;
|
|
393
|
+
};
|
|
394
|
+
const onUpstreamError = () => {
|
|
395
|
+
void terminate("upstream");
|
|
396
|
+
};
|
|
397
|
+
const onUpstreamClose = () => {
|
|
398
|
+
options.upstream.socket.off("error", onUpstreamError);
|
|
399
|
+
void terminate("upstream");
|
|
400
|
+
};
|
|
401
|
+
options.upstream.socket.on("error", onUpstreamError);
|
|
402
|
+
options.upstream.socket.once("close", onUpstreamClose);
|
|
403
|
+
server.on("connection", (socket) => trackSocket(sockets, socket));
|
|
404
|
+
server.on("upgrade", (request, downstream, head) => {
|
|
405
|
+
if (closed || downstreamAccepted || !isValidDownstreamUpgrade(request, clientAuthorization) || options.upstream.socket.destroyed) {
|
|
406
|
+
downstream.end("HTTP/1.1 404 Not Found\r\nConnection: close\r\nContent-Length: 0\r\n\r\n");
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
downstreamAccepted = true;
|
|
410
|
+
const key = request.headers["sec-websocket-key"];
|
|
411
|
+
if (typeof key !== "string") {
|
|
412
|
+
downstream.destroy();
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
const accept = createHash("sha1").update(`${key}${WEBSOCKET_GUID}`).digest("base64");
|
|
416
|
+
downstream.write([
|
|
417
|
+
"HTTP/1.1 101 Switching Protocols",
|
|
418
|
+
"Connection: Upgrade",
|
|
419
|
+
"Upgrade: websocket",
|
|
420
|
+
`Sec-WebSocket-Accept: ${accept}`,
|
|
421
|
+
"",
|
|
422
|
+
""
|
|
423
|
+
].join("\r\n"));
|
|
424
|
+
if (options.upstream.head.length > 0)
|
|
425
|
+
downstream.write(options.upstream.head);
|
|
426
|
+
if (head.length > 0)
|
|
427
|
+
options.upstream.socket.write(head);
|
|
428
|
+
downstream.once("error", () => void terminate("downstream"));
|
|
429
|
+
downstream.once("close", () => void terminate("downstream"));
|
|
430
|
+
downstream.pipe(options.upstream.socket).pipe(downstream);
|
|
431
|
+
server.close();
|
|
432
|
+
});
|
|
433
|
+
const onBindError = (error) => binding.reject(error);
|
|
434
|
+
server.once("error", onBindError);
|
|
435
|
+
try {
|
|
436
|
+
server.listen(0, "127.0.0.1", () => {
|
|
437
|
+
server.off("error", onBindError);
|
|
438
|
+
binding.resolve();
|
|
439
|
+
});
|
|
440
|
+
await binding.promise;
|
|
441
|
+
} catch (error) {
|
|
442
|
+
binding.reject(error);
|
|
443
|
+
await terminate("owner");
|
|
444
|
+
throw error;
|
|
445
|
+
}
|
|
446
|
+
if (closed || options.upstream.socket.destroyed) {
|
|
447
|
+
await terminate("upstream");
|
|
448
|
+
throw new Error("Chrome relay connection closed before child handoff.");
|
|
449
|
+
}
|
|
450
|
+
const address = server.address();
|
|
451
|
+
if (!address || address.address !== "127.0.0.1") {
|
|
452
|
+
await terminate("owner");
|
|
453
|
+
throw new Error("Chrome relay proxy failed to bind to IPv4 loopback.");
|
|
454
|
+
}
|
|
455
|
+
let authorizationAvailable = true;
|
|
456
|
+
return {
|
|
457
|
+
endpoint: `ws://127.0.0.1:${address.port}/cdp`,
|
|
458
|
+
signal: lifetime.signal,
|
|
459
|
+
consumeClientAuthorization() {
|
|
460
|
+
if (!authorizationAvailable)
|
|
461
|
+
throw new Error("Chrome relay authorization handoff already consumed.");
|
|
462
|
+
authorizationAvailable = false;
|
|
463
|
+
return clientAuthorization;
|
|
464
|
+
},
|
|
465
|
+
close: () => terminate("owner")
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
function isValidDownstreamUpgrade(request, expectedAuthorization) {
|
|
469
|
+
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)) {
|
|
470
|
+
return false;
|
|
471
|
+
}
|
|
472
|
+
const key = request.headers["sec-websocket-key"];
|
|
473
|
+
if (typeof key !== "string" || !/^[A-Za-z0-9+/]{22}==$/u.test(key))
|
|
474
|
+
return false;
|
|
475
|
+
const decoded = Buffer.from(key, "base64");
|
|
476
|
+
return decoded.byteLength === 16 && decoded.toString("base64") === key;
|
|
477
|
+
}
|
|
478
|
+
function countRawHeader(request, expectedName) {
|
|
479
|
+
let count = 0;
|
|
480
|
+
for (let index = 0; index < request.rawHeaders.length; index += 2) {
|
|
481
|
+
if (request.rawHeaders[index]?.toLowerCase() === expectedName)
|
|
482
|
+
count += 1;
|
|
483
|
+
}
|
|
484
|
+
return count;
|
|
485
|
+
}
|
|
486
|
+
function safeHeaderEqual(actual, expected) {
|
|
487
|
+
if (!actual)
|
|
488
|
+
return false;
|
|
489
|
+
const actualBytes = Buffer.from(actual);
|
|
490
|
+
const expectedBytes = Buffer.from(expected);
|
|
491
|
+
return actualBytes.length === expectedBytes.length && timingSafeEqual(actualBytes, expectedBytes);
|
|
492
|
+
}
|
|
493
|
+
function hasToken(value, expected) {
|
|
494
|
+
return Boolean(value?.split(",").some((token) => token.trim().toLowerCase() === expected));
|
|
495
|
+
}
|
|
496
|
+
function trackSocket(sockets, socket) {
|
|
497
|
+
if (sockets.has(socket))
|
|
498
|
+
return;
|
|
499
|
+
sockets.add(socket);
|
|
500
|
+
socket.once("close", () => sockets.delete(socket));
|
|
501
|
+
}
|
|
502
|
+
function closeUpstream(socket) {
|
|
503
|
+
if (socket.destroyed)
|
|
504
|
+
return;
|
|
505
|
+
if (!socket.remoteAddress) {
|
|
506
|
+
socket.destroy();
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
try {
|
|
510
|
+
socket.resetAndDestroy();
|
|
511
|
+
} catch {
|
|
512
|
+
socket.destroy();
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
async function closeServer(server, sockets) {
|
|
516
|
+
for (const socket of sockets)
|
|
517
|
+
socket.destroy();
|
|
518
|
+
if (!server.listening)
|
|
519
|
+
return;
|
|
520
|
+
await new Promise((resolve) => server.close(() => resolve()));
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// node_modules/.pnpm/mcporter@0.13.13/node_modules/mcporter/dist/browser-relay-auth-v2.js
|
|
524
|
+
import { createHmac, hash, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
|
|
525
|
+
var BROWSER_RELAY_AUTH_LABEL = "openclaw.browser-relay.auth";
|
|
526
|
+
var BROWSER_RELAY_AUTH_VERSION = 2;
|
|
527
|
+
var BROWSER_RELAY_CHALLENGE_MAX_LIFETIME_MS = 1e4;
|
|
528
|
+
var BROWSER_RELAY_CLOCK_SKEW_MS = 3e4;
|
|
529
|
+
var BROWSER_RELAY_AUTH_CHALLENGE_PATH = "/_openclaw/relay/auth/v2/challenge";
|
|
530
|
+
var BROWSER_RELAY_AUTH_COMPLETE_PATH = "/_openclaw/relay/auth/v2/complete";
|
|
531
|
+
var BROWSER_RELAY_CDP_PATH = "/cdp";
|
|
532
|
+
var BROWSER_RELAY_CDP_ROLE = "cdp";
|
|
533
|
+
var BROWSER_RELAY_CDP_TRANSPORT = "connection";
|
|
534
|
+
var BROWSER_RELAY_CDP_METHOD = "SEQUENCE";
|
|
535
|
+
var BROWSER_RELAY_CDP_RESOURCE = "/json/version -> /cdp";
|
|
536
|
+
var BROWSER_RELAY_CDP_FLOW = "cdp";
|
|
537
|
+
var BROWSER_RELAY_VERSION_PATH = "/json/version";
|
|
538
|
+
function deriveBrowserRelayKeyId(key) {
|
|
539
|
+
assertRelayKey(key);
|
|
540
|
+
return hash("sha256", key, "base64url").slice(0, 22);
|
|
541
|
+
}
|
|
542
|
+
function canonicalizeBrowserRelayProof(proofKind, fields, clientProof) {
|
|
543
|
+
const canonical = [
|
|
544
|
+
BROWSER_RELAY_AUTH_LABEL,
|
|
545
|
+
BROWSER_RELAY_AUTH_VERSION,
|
|
546
|
+
proofKind,
|
|
547
|
+
fields.keyId,
|
|
548
|
+
fields.instanceId,
|
|
549
|
+
fields.sessionId,
|
|
550
|
+
fields.clientNonce,
|
|
551
|
+
fields.serverNonce,
|
|
552
|
+
fields.issuedAtMs,
|
|
553
|
+
fields.expiresAtMs,
|
|
554
|
+
fields.role,
|
|
555
|
+
fields.transport,
|
|
556
|
+
fields.method,
|
|
557
|
+
fields.resource,
|
|
558
|
+
fields.flow
|
|
559
|
+
];
|
|
560
|
+
if (proofKind === "accept") {
|
|
561
|
+
if (!clientProof || !isCanonicalBase64Url(clientProof, 32)) {
|
|
562
|
+
throw new Error("Browser relay accept proof requires a 32-byte client proof.");
|
|
563
|
+
}
|
|
564
|
+
canonical.push(clientProof);
|
|
565
|
+
} else if (clientProof !== void 0) {
|
|
566
|
+
throw new Error("Browser relay client proof is only valid for an accept proof.");
|
|
567
|
+
}
|
|
568
|
+
return Buffer.from(JSON.stringify(canonical), "utf8");
|
|
569
|
+
}
|
|
570
|
+
function createBrowserRelayProof(key, proofKind, fields, clientProof) {
|
|
571
|
+
assertRelayKey(key);
|
|
572
|
+
return createHmac("sha256", key).update(canonicalizeBrowserRelayProof(proofKind, fields, clientProof)).digest("base64url");
|
|
573
|
+
}
|
|
574
|
+
function verifyBrowserRelayProof(key, proofKind, fields, proof, clientProof) {
|
|
575
|
+
if (!isCanonicalBase64Url(proof, 32))
|
|
576
|
+
return false;
|
|
577
|
+
const actual = Buffer.from(proof, "base64url");
|
|
578
|
+
const expected = Buffer.from(createBrowserRelayProof(key, proofKind, fields, clientProof), "base64url");
|
|
579
|
+
return actual.byteLength === expected.byteLength && timingSafeEqual2(actual, expected);
|
|
580
|
+
}
|
|
581
|
+
function assertRelayKey(key) {
|
|
582
|
+
if (key.byteLength !== 32)
|
|
583
|
+
throw new Error("Browser relay key must contain 32 bytes.");
|
|
584
|
+
}
|
|
585
|
+
function isCanonicalBase64Url(value, bytes) {
|
|
586
|
+
if (!/^[A-Za-z0-9_-]+$/u.test(value))
|
|
587
|
+
return false;
|
|
588
|
+
const decoded = Buffer.from(value, "base64url");
|
|
589
|
+
return decoded.byteLength === bytes && decoded.toString("base64url") === value;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
// node_modules/.pnpm/mcporter@0.13.13/node_modules/mcporter/dist/chrome-devtools-relay-client.js
|
|
593
|
+
import { createHash as createHash2, randomBytes as randomBytes2 } from "node:crypto";
|
|
594
|
+
import { lookup } from "node:dns/promises";
|
|
595
|
+
import net from "node:net";
|
|
596
|
+
import { TextDecoder } from "node:util";
|
|
597
|
+
var MAX_HEADER_BYTES = 16 * 1024;
|
|
598
|
+
var MAX_JSON_BYTES = 64 * 1024;
|
|
599
|
+
var WEBSOCKET_GUID2 = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
|
600
|
+
var RelayV2Error = class extends Error {
|
|
601
|
+
reason;
|
|
602
|
+
status;
|
|
603
|
+
constructor(reason, status) {
|
|
604
|
+
super(`Chrome relay authentication failed (${reason}).`);
|
|
605
|
+
this.reason = reason;
|
|
606
|
+
this.status = status;
|
|
607
|
+
this.name = "RelayV2Error";
|
|
608
|
+
}
|
|
609
|
+
};
|
|
610
|
+
async function connectChromeDevtoolsRelayV2(options) {
|
|
611
|
+
const startedAt = performance.now();
|
|
612
|
+
const durationMs = () => Math.max(0, Math.round(performance.now() - startedAt));
|
|
613
|
+
let socket;
|
|
614
|
+
let timer;
|
|
615
|
+
try {
|
|
616
|
+
let timedOut = false;
|
|
617
|
+
const timeout = new Promise((_resolve, reject) => {
|
|
618
|
+
timer = setTimeout(() => {
|
|
619
|
+
timedOut = true;
|
|
620
|
+
const error = new RelayV2Error("timeout");
|
|
621
|
+
socket?.destroy(error);
|
|
622
|
+
reject(error);
|
|
623
|
+
}, options.timeoutMs);
|
|
624
|
+
});
|
|
625
|
+
timer?.unref();
|
|
626
|
+
const addresses = await Promise.race([
|
|
627
|
+
resolveLoopbackAddresses(options.baseUrl.hostname, options.resolve ?? lookup),
|
|
628
|
+
timeout
|
|
629
|
+
]);
|
|
630
|
+
const port = resolvePort(options.baseUrl);
|
|
631
|
+
let lastConnectionError;
|
|
632
|
+
for (const address of addresses) {
|
|
633
|
+
socket = net.createConnection({ host: address.address, family: address.family, port });
|
|
634
|
+
try {
|
|
635
|
+
await waitForConnect(socket);
|
|
636
|
+
lastConnectionError = void 0;
|
|
637
|
+
break;
|
|
638
|
+
} catch (error) {
|
|
639
|
+
socket.destroy();
|
|
640
|
+
if (timedOut || isTimeoutError(error))
|
|
641
|
+
throw new RelayV2Error("timeout");
|
|
642
|
+
lastConnectionError = error;
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
if (lastConnectionError)
|
|
646
|
+
throw lastConnectionError;
|
|
647
|
+
if (!socket || socket.destroyed)
|
|
648
|
+
throw new RelayV2Error(timedOut ? "timeout" : "network-error");
|
|
649
|
+
if (!socket.remoteAddress || !isNumericLoopback(socket.remoteAddress))
|
|
650
|
+
throw new RelayV2Error("protocol");
|
|
651
|
+
const reader = new SocketReader(socket);
|
|
652
|
+
const flow = options.flow ?? "cdp";
|
|
653
|
+
const clientNonce = randomBytes2(32).toString("base64url");
|
|
654
|
+
const hello = {
|
|
655
|
+
v: BROWSER_RELAY_AUTH_VERSION,
|
|
656
|
+
keyId: options.credential.keyId,
|
|
657
|
+
clientNonce,
|
|
658
|
+
role: BROWSER_RELAY_CDP_ROLE,
|
|
659
|
+
transport: BROWSER_RELAY_CDP_TRANSPORT,
|
|
660
|
+
method: flow === BROWSER_RELAY_CDP_FLOW ? BROWSER_RELAY_CDP_METHOD : "GET",
|
|
661
|
+
resource: flow === BROWSER_RELAY_CDP_FLOW ? BROWSER_RELAY_CDP_RESOURCE : "/json/list",
|
|
662
|
+
flow
|
|
663
|
+
};
|
|
664
|
+
await writeJsonRequest(socket, options.baseUrl.host, BROWSER_RELAY_AUTH_CHALLENGE_PATH, hello);
|
|
665
|
+
const challengeResponse = await reader.readHttpResponse(MAX_JSON_BYTES);
|
|
666
|
+
ensureAuthStatus(challengeResponse, "challenge");
|
|
667
|
+
const challenge = parseStrictJsonObject(challengeResponse.body);
|
|
668
|
+
const fields = validateChallenge(challenge, hello, options.now?.() ?? Date.now());
|
|
669
|
+
const serverProof = requireString(challenge, "serverProof");
|
|
670
|
+
if (!verifyBrowserRelayProof(options.credential.key, "server", fields, serverProof)) {
|
|
671
|
+
throw new RelayV2Error("bad-server-proof");
|
|
672
|
+
}
|
|
673
|
+
const clientProof = createBrowserRelayProof(options.credential.key, "client", fields);
|
|
674
|
+
await writeJsonRequest(socket, options.baseUrl.host, BROWSER_RELAY_AUTH_COMPLETE_PATH, {
|
|
675
|
+
v: BROWSER_RELAY_AUTH_VERSION,
|
|
676
|
+
sessionId: fields.sessionId,
|
|
677
|
+
clientProof
|
|
678
|
+
});
|
|
679
|
+
const completeResponse = await reader.readHttpResponse(MAX_JSON_BYTES);
|
|
680
|
+
ensureAuthStatus(completeResponse, "complete");
|
|
681
|
+
const accepted = parseStrictJsonObject(completeResponse.body);
|
|
682
|
+
validateAccept(accepted, fields.sessionId);
|
|
683
|
+
if (!verifyBrowserRelayProof(options.credential.key, "accept", fields, requireString(accepted, "acceptProof"), clientProof)) {
|
|
684
|
+
throw new RelayV2Error("bad-server-proof");
|
|
685
|
+
}
|
|
686
|
+
if (flow === "json-list") {
|
|
687
|
+
await writeRequest(socket, options.baseUrl.host, "GET", "/json/list", []);
|
|
688
|
+
const listResponse = await reader.readHttpResponse(MAX_JSON_BYTES, true);
|
|
689
|
+
if (listResponse.status !== 200)
|
|
690
|
+
throw classifyStatus(listResponse.status, true);
|
|
691
|
+
const json = parseStrictJson(listResponse.body);
|
|
692
|
+
if (!Array.isArray(json))
|
|
693
|
+
throw new RelayV2Error("protocol", 200);
|
|
694
|
+
socket.destroy();
|
|
695
|
+
return { reason: "success", durationMs: durationMs(), status: 200, json };
|
|
696
|
+
}
|
|
697
|
+
await writeRequest(socket, options.baseUrl.host, "GET", BROWSER_RELAY_VERSION_PATH, []);
|
|
698
|
+
const versionResponse = await reader.readHttpResponse(MAX_JSON_BYTES);
|
|
699
|
+
if (versionResponse.status === 503) {
|
|
700
|
+
socket.destroy();
|
|
701
|
+
return { reason: "extension-disconnected", durationMs: durationMs(), status: 503 };
|
|
702
|
+
}
|
|
703
|
+
if (versionResponse.status !== 200)
|
|
704
|
+
throw classifyStatus(versionResponse.status, true);
|
|
705
|
+
parseStrictJsonObject(versionResponse.body);
|
|
706
|
+
const websocketKey = randomBytes2(16).toString("base64");
|
|
707
|
+
await writeRequest(socket, options.baseUrl.host, "GET", BROWSER_RELAY_CDP_PATH, [
|
|
708
|
+
["Connection", "Upgrade"],
|
|
709
|
+
["Upgrade", "websocket"],
|
|
710
|
+
["Sec-WebSocket-Key", websocketKey],
|
|
711
|
+
["Sec-WebSocket-Version", "13"]
|
|
712
|
+
]);
|
|
713
|
+
const upgrade = await reader.readHttpUpgrade();
|
|
714
|
+
if (upgrade.status !== 101)
|
|
715
|
+
throw classifyStatus(upgrade.status, true);
|
|
716
|
+
const expectedAccept = createHash2("sha1").update(`${websocketKey}${WEBSOCKET_GUID2}`).digest("base64");
|
|
717
|
+
if (upgrade.headers.get("upgrade")?.toLowerCase() !== "websocket" || !hasToken2(upgrade.headers.get("connection"), "upgrade") || upgrade.headers.get("sec-websocket-accept") !== expectedAccept) {
|
|
718
|
+
throw new RelayV2Error("sequence", 101);
|
|
719
|
+
}
|
|
720
|
+
const head = reader.release();
|
|
721
|
+
if (timer)
|
|
722
|
+
clearTimeout(timer);
|
|
723
|
+
return { reason: "success", durationMs: durationMs(), status: 200, upstream: { socket, head } };
|
|
724
|
+
} catch (error) {
|
|
725
|
+
socket?.destroy();
|
|
726
|
+
if (error instanceof RelayV2Error) {
|
|
727
|
+
return { reason: error.reason, durationMs: durationMs(), status: error.status };
|
|
728
|
+
}
|
|
729
|
+
return { reason: isTimeoutError(error) ? "timeout" : "network-error", durationMs: durationMs() };
|
|
730
|
+
} finally {
|
|
731
|
+
if (timer)
|
|
732
|
+
clearTimeout(timer);
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
async function resolveLoopbackAddresses(hostname, resolver) {
|
|
736
|
+
const normalized = hostname.replace(/^\[(.*)\]$/u, "$1");
|
|
737
|
+
const addresses = await resolver(normalized, { all: true, verbatim: true });
|
|
738
|
+
if (addresses.length === 0 || addresses.some((address) => !isNumericLoopback(address.address))) {
|
|
739
|
+
throw new RelayV2Error("protocol");
|
|
740
|
+
}
|
|
741
|
+
return addresses;
|
|
742
|
+
}
|
|
743
|
+
function resolvePort(url) {
|
|
744
|
+
const port = url.port === "" ? 80 : Number(url.port);
|
|
745
|
+
if (!Number.isSafeInteger(port) || port < 1 || port > 65535)
|
|
746
|
+
throw new RelayV2Error("protocol");
|
|
747
|
+
return port;
|
|
748
|
+
}
|
|
749
|
+
function isNumericLoopback(address) {
|
|
750
|
+
if (net.isIPv4(address))
|
|
751
|
+
return address.startsWith("127.");
|
|
752
|
+
if (!net.isIPv6(address))
|
|
753
|
+
return false;
|
|
754
|
+
const normalized = address.toLowerCase();
|
|
755
|
+
return normalized === "::1" || normalized.startsWith("::ffff:127.");
|
|
756
|
+
}
|
|
757
|
+
async function waitForConnect(socket) {
|
|
758
|
+
await new Promise((resolve, reject) => {
|
|
759
|
+
const onConnect = () => {
|
|
760
|
+
cleanup();
|
|
761
|
+
resolve();
|
|
762
|
+
};
|
|
763
|
+
const onError = (error) => {
|
|
764
|
+
cleanup();
|
|
765
|
+
reject(error);
|
|
766
|
+
};
|
|
767
|
+
const cleanup = () => {
|
|
768
|
+
socket.off("connect", onConnect);
|
|
769
|
+
socket.off("error", onError);
|
|
770
|
+
};
|
|
771
|
+
socket.once("connect", onConnect);
|
|
772
|
+
socket.once("error", onError);
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
var SocketReader = class {
|
|
776
|
+
socket;
|
|
777
|
+
buffered = Buffer.alloc(0);
|
|
778
|
+
failure;
|
|
779
|
+
ended = false;
|
|
780
|
+
onData = (chunk) => {
|
|
781
|
+
this.buffered = Buffer.concat([this.buffered, chunk]);
|
|
782
|
+
};
|
|
783
|
+
onError = (error) => {
|
|
784
|
+
this.failure = error;
|
|
785
|
+
};
|
|
786
|
+
onClose = () => {
|
|
787
|
+
this.ended = true;
|
|
788
|
+
};
|
|
789
|
+
constructor(socket) {
|
|
790
|
+
this.socket = socket;
|
|
791
|
+
socket.on("data", this.onData);
|
|
792
|
+
socket.on("error", this.onError);
|
|
793
|
+
socket.on("close", this.onClose);
|
|
794
|
+
}
|
|
795
|
+
async readHttpResponse(maxBodyBytes, allowConnectionClose = false) {
|
|
796
|
+
const parsed = await this.readHeaders();
|
|
797
|
+
if (parsed.status === 101)
|
|
798
|
+
throw new RelayV2Error("protocol", 101);
|
|
799
|
+
if (parsed.headers.has("transfer-encoding"))
|
|
800
|
+
throw new RelayV2Error("protocol", parsed.status);
|
|
801
|
+
if (!allowConnectionClose && parsed.status === 200 && hasToken2(parsed.headers.get("connection"), "close")) {
|
|
802
|
+
throw new RelayV2Error("protocol", parsed.status);
|
|
803
|
+
}
|
|
804
|
+
const rawLength = parsed.headers.get("content-length");
|
|
805
|
+
if (!rawLength || !/^\d+$/u.test(rawLength))
|
|
806
|
+
throw new RelayV2Error("protocol", parsed.status);
|
|
807
|
+
const length = Number(rawLength);
|
|
808
|
+
if (!Number.isSafeInteger(length) || length > maxBodyBytes)
|
|
809
|
+
throw new RelayV2Error("protocol", parsed.status);
|
|
810
|
+
await this.waitForBytes(length);
|
|
811
|
+
const body = this.buffered.subarray(0, length);
|
|
812
|
+
this.buffered = this.buffered.subarray(length);
|
|
813
|
+
if (this.buffered.length > 0)
|
|
814
|
+
throw new RelayV2Error("protocol", parsed.status);
|
|
815
|
+
return { ...parsed, body };
|
|
816
|
+
}
|
|
817
|
+
async readHttpUpgrade() {
|
|
818
|
+
const parsed = await this.readHeaders();
|
|
819
|
+
if (parsed.headers.has("transfer-encoding"))
|
|
820
|
+
throw new RelayV2Error("protocol", parsed.status);
|
|
821
|
+
if (parsed.status === 101) {
|
|
822
|
+
if (parsed.headers.has("content-length"))
|
|
823
|
+
throw new RelayV2Error("protocol", parsed.status);
|
|
824
|
+
return { ...parsed, body: Buffer.alloc(0) };
|
|
825
|
+
}
|
|
826
|
+
const rawLength = parsed.headers.get("content-length");
|
|
827
|
+
if (!rawLength || !/^\d+$/u.test(rawLength))
|
|
828
|
+
throw new RelayV2Error("protocol", parsed.status);
|
|
829
|
+
const length = Number(rawLength);
|
|
830
|
+
if (!Number.isSafeInteger(length) || length > MAX_JSON_BYTES)
|
|
831
|
+
throw new RelayV2Error("protocol", parsed.status);
|
|
832
|
+
await this.waitForBytes(length);
|
|
833
|
+
const body = this.buffered.subarray(0, length);
|
|
834
|
+
this.buffered = this.buffered.subarray(length);
|
|
835
|
+
if (this.buffered.length > 0)
|
|
836
|
+
throw new RelayV2Error("protocol", parsed.status);
|
|
837
|
+
return { ...parsed, body };
|
|
838
|
+
}
|
|
839
|
+
release() {
|
|
840
|
+
this.socket.off("data", this.onData);
|
|
841
|
+
this.socket.off("error", this.onError);
|
|
842
|
+
this.socket.off("close", this.onClose);
|
|
843
|
+
const head = this.buffered;
|
|
844
|
+
this.buffered = Buffer.alloc(0);
|
|
845
|
+
return head;
|
|
846
|
+
}
|
|
847
|
+
async readHeaders() {
|
|
848
|
+
let boundary = this.buffered.indexOf("\r\n\r\n");
|
|
849
|
+
while (boundary < 0) {
|
|
850
|
+
if (this.buffered.length > MAX_HEADER_BYTES)
|
|
851
|
+
throw new RelayV2Error("protocol");
|
|
852
|
+
await this.waitForData();
|
|
853
|
+
boundary = this.buffered.indexOf("\r\n\r\n");
|
|
854
|
+
}
|
|
855
|
+
if (boundary > MAX_HEADER_BYTES)
|
|
856
|
+
throw new RelayV2Error("protocol");
|
|
857
|
+
const raw = this.buffered.subarray(0, boundary).toString("latin1");
|
|
858
|
+
this.buffered = this.buffered.subarray(boundary + 4);
|
|
859
|
+
const lines = raw.split("\r\n");
|
|
860
|
+
const statusLine = lines.shift();
|
|
861
|
+
const match = /^HTTP\/1\.1 ([1-5]\d\d)(?: [\x20-\x7e]*)?$/u.exec(statusLine ?? "");
|
|
862
|
+
if (!match)
|
|
863
|
+
throw new RelayV2Error("protocol");
|
|
864
|
+
const status = Number(match[1]);
|
|
865
|
+
const headers = /* @__PURE__ */ new Map();
|
|
866
|
+
for (const line of lines) {
|
|
867
|
+
if (/^[ \t]/u.test(line))
|
|
868
|
+
throw new RelayV2Error("protocol", status);
|
|
869
|
+
const separator = line.indexOf(":");
|
|
870
|
+
if (separator <= 0)
|
|
871
|
+
throw new RelayV2Error("protocol", status);
|
|
872
|
+
const name = line.slice(0, separator).toLowerCase();
|
|
873
|
+
const value = line.slice(separator + 1).trim();
|
|
874
|
+
if (!/^[!#$%&'*+.^_`|~0-9a-z-]+$/u.test(name) || value.includes("\0") || value.includes("\r") || value.includes("\n") || value.includes("\x7F")) {
|
|
875
|
+
throw new RelayV2Error("protocol", status);
|
|
876
|
+
}
|
|
877
|
+
if (headers.has(name))
|
|
878
|
+
throw new RelayV2Error("protocol", status);
|
|
879
|
+
headers.set(name, value);
|
|
880
|
+
}
|
|
881
|
+
return { status, headers };
|
|
882
|
+
}
|
|
883
|
+
async waitForBytes(length) {
|
|
884
|
+
while (this.buffered.length < length)
|
|
885
|
+
await this.waitForData();
|
|
886
|
+
}
|
|
887
|
+
async waitForData() {
|
|
888
|
+
if (this.failure)
|
|
889
|
+
throw this.failure;
|
|
890
|
+
if (this.ended)
|
|
891
|
+
throw new RelayV2Error("network-error");
|
|
892
|
+
await new Promise((resolve, reject) => {
|
|
893
|
+
const onData = () => {
|
|
894
|
+
cleanup();
|
|
895
|
+
resolve();
|
|
896
|
+
};
|
|
897
|
+
const onError = (error) => {
|
|
898
|
+
cleanup();
|
|
899
|
+
reject(error);
|
|
900
|
+
};
|
|
901
|
+
const onClose = () => {
|
|
902
|
+
cleanup();
|
|
903
|
+
reject(new RelayV2Error("network-error"));
|
|
904
|
+
};
|
|
905
|
+
const cleanup = () => {
|
|
906
|
+
this.socket.off("data", onData);
|
|
907
|
+
this.socket.off("error", onError);
|
|
908
|
+
this.socket.off("close", onClose);
|
|
909
|
+
};
|
|
910
|
+
this.socket.once("data", onData);
|
|
911
|
+
this.socket.once("error", onError);
|
|
912
|
+
this.socket.once("close", onClose);
|
|
913
|
+
});
|
|
914
|
+
}
|
|
915
|
+
};
|
|
916
|
+
async function writeJsonRequest(socket, host, pathname, body) {
|
|
917
|
+
const encoded = Buffer.from(JSON.stringify(body), "utf8");
|
|
918
|
+
await writeRequest(socket, host, "POST", pathname, [
|
|
919
|
+
["Content-Type", "application/json"],
|
|
920
|
+
["Content-Length", String(encoded.byteLength)]
|
|
921
|
+
], encoded);
|
|
922
|
+
}
|
|
923
|
+
async function writeRequest(socket, host, method, pathname, headers, body) {
|
|
924
|
+
const lines = [`${method} ${pathname} HTTP/1.1`, `Host: ${host}`, "Connection: keep-alive"];
|
|
925
|
+
for (const [name, value] of headers) {
|
|
926
|
+
if (name.toLowerCase() === "connection")
|
|
927
|
+
lines[2] = `${name}: ${value}`;
|
|
928
|
+
else
|
|
929
|
+
lines.push(`${name}: ${value}`);
|
|
930
|
+
}
|
|
931
|
+
const head = Buffer.from(`${lines.join("\r\n")}\r
|
|
932
|
+
\r
|
|
933
|
+
`, "ascii");
|
|
934
|
+
const output = body ? Buffer.concat([head, body]) : head;
|
|
935
|
+
await new Promise((resolve, reject) => {
|
|
936
|
+
socket.write(output, (error) => error ? reject(error) : resolve());
|
|
937
|
+
});
|
|
938
|
+
}
|
|
939
|
+
function ensureAuthStatus(response, phase) {
|
|
940
|
+
if (response.status === 200)
|
|
941
|
+
return;
|
|
942
|
+
if (phase === "challenge" && response.status === 401 && responseError(response) === "Relay auth challenge rejected") {
|
|
943
|
+
throw new RelayV2Error("replay", response.status);
|
|
944
|
+
}
|
|
945
|
+
if (response.status === 409)
|
|
946
|
+
throw new RelayV2Error("sequence", response.status);
|
|
947
|
+
throw classifyStatus(response.status, false);
|
|
948
|
+
}
|
|
949
|
+
function classifyStatus(status, authenticated) {
|
|
950
|
+
if (!authenticated && (status === 401 || status === 404 || status === 426)) {
|
|
951
|
+
return new RelayV2Error("unsupported-auth", status);
|
|
952
|
+
}
|
|
953
|
+
if (!authenticated && status === 503)
|
|
954
|
+
return new RelayV2Error("server-auth-failed", status);
|
|
955
|
+
if (status === 408 || status === 410)
|
|
956
|
+
return new RelayV2Error("freshness", status);
|
|
957
|
+
if (status === 409)
|
|
958
|
+
return new RelayV2Error("sequence", status);
|
|
959
|
+
if (status === 412)
|
|
960
|
+
return new RelayV2Error("sequence", status);
|
|
961
|
+
return new RelayV2Error("protocol", status);
|
|
962
|
+
}
|
|
963
|
+
function responseError(response) {
|
|
964
|
+
try {
|
|
965
|
+
const value = parseStrictJsonObject(response.body);
|
|
966
|
+
return typeof value.error === "string" ? value.error : void 0;
|
|
967
|
+
} catch {
|
|
968
|
+
return void 0;
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
function validateChallenge(value, hello, nowMs) {
|
|
972
|
+
assertExactKeys(value, [
|
|
973
|
+
"type",
|
|
974
|
+
"v",
|
|
975
|
+
"keyId",
|
|
976
|
+
"instanceId",
|
|
977
|
+
"sessionId",
|
|
978
|
+
"clientNonce",
|
|
979
|
+
"serverNonce",
|
|
980
|
+
"issuedAtMs",
|
|
981
|
+
"expiresAtMs",
|
|
982
|
+
"role",
|
|
983
|
+
"transport",
|
|
984
|
+
"method",
|
|
985
|
+
"resource",
|
|
986
|
+
"flow",
|
|
987
|
+
"serverProof"
|
|
988
|
+
]);
|
|
989
|
+
if (value.type !== "auth.challenge")
|
|
990
|
+
throw new RelayV2Error("protocol");
|
|
991
|
+
if (value.v !== BROWSER_RELAY_AUTH_VERSION)
|
|
992
|
+
throw new RelayV2Error("protocol");
|
|
993
|
+
for (const key of ["keyId", "clientNonce", "role", "transport", "method", "resource", "flow"]) {
|
|
994
|
+
if (value[key] !== hello[key])
|
|
995
|
+
throw new RelayV2Error("protocol");
|
|
996
|
+
}
|
|
997
|
+
const fields = {
|
|
998
|
+
keyId: requireToken(value, "keyId", 22),
|
|
999
|
+
instanceId: requireEncoded(value, "instanceId", 22),
|
|
1000
|
+
sessionId: requireEncoded(value, "sessionId", 22),
|
|
1001
|
+
clientNonce: requireEncoded(value, "clientNonce", 43),
|
|
1002
|
+
serverNonce: requireEncoded(value, "serverNonce", 43),
|
|
1003
|
+
issuedAtMs: requireInteger(value, "issuedAtMs"),
|
|
1004
|
+
expiresAtMs: requireInteger(value, "expiresAtMs"),
|
|
1005
|
+
role: requireString(value, "role"),
|
|
1006
|
+
transport: requireString(value, "transport"),
|
|
1007
|
+
method: requireString(value, "method"),
|
|
1008
|
+
resource: requireString(value, "resource"),
|
|
1009
|
+
flow: requireString(value, "flow")
|
|
1010
|
+
};
|
|
1011
|
+
requireEncoded(value, "serverProof", 43);
|
|
1012
|
+
const lifetime = fields.expiresAtMs - fields.issuedAtMs;
|
|
1013
|
+
if (lifetime <= 0 || lifetime > BROWSER_RELAY_CHALLENGE_MAX_LIFETIME_MS || fields.issuedAtMs - nowMs > BROWSER_RELAY_CLOCK_SKEW_MS || nowMs > fields.expiresAtMs) {
|
|
1014
|
+
throw new RelayV2Error("freshness");
|
|
1015
|
+
}
|
|
1016
|
+
return fields;
|
|
1017
|
+
}
|
|
1018
|
+
function validateAccept(value, sessionId) {
|
|
1019
|
+
assertExactKeys(value, ["type", "v", "sessionId", "acceptProof"]);
|
|
1020
|
+
if (value.type !== "auth.ok")
|
|
1021
|
+
throw new RelayV2Error("protocol");
|
|
1022
|
+
if (value.v !== BROWSER_RELAY_AUTH_VERSION || value.sessionId !== sessionId)
|
|
1023
|
+
throw new RelayV2Error("protocol");
|
|
1024
|
+
requireEncoded(value, "acceptProof", 43);
|
|
1025
|
+
}
|
|
1026
|
+
function parseStrictJsonObject(body) {
|
|
1027
|
+
const value = parseStrictJson(body);
|
|
1028
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
1029
|
+
throw new RelayV2Error("protocol");
|
|
1030
|
+
return value;
|
|
1031
|
+
}
|
|
1032
|
+
function parseStrictJson(body) {
|
|
1033
|
+
let source;
|
|
1034
|
+
try {
|
|
1035
|
+
source = new TextDecoder("utf-8", { fatal: true }).decode(body);
|
|
1036
|
+
} catch {
|
|
1037
|
+
throw new RelayV2Error("protocol");
|
|
1038
|
+
}
|
|
1039
|
+
const errors = [];
|
|
1040
|
+
const tree = parseTree(source, errors, { allowTrailingComma: false, disallowComments: true });
|
|
1041
|
+
if (!tree || errors.length > 0)
|
|
1042
|
+
throw new RelayV2Error("protocol");
|
|
1043
|
+
rejectDuplicateJsonKeys(tree);
|
|
1044
|
+
try {
|
|
1045
|
+
return JSON.parse(source);
|
|
1046
|
+
} catch {
|
|
1047
|
+
throw new RelayV2Error("protocol");
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
function rejectDuplicateJsonKeys(node) {
|
|
1051
|
+
if (node.type === "object") {
|
|
1052
|
+
const keys = /* @__PURE__ */ new Set();
|
|
1053
|
+
for (const property of node.children ?? []) {
|
|
1054
|
+
const key = property.children?.[0]?.value;
|
|
1055
|
+
if (typeof key !== "string" || keys.has(key))
|
|
1056
|
+
throw new RelayV2Error("protocol");
|
|
1057
|
+
keys.add(key);
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
for (const child of node.children ?? [])
|
|
1061
|
+
rejectDuplicateJsonKeys(child);
|
|
1062
|
+
}
|
|
1063
|
+
function assertExactKeys(value, required) {
|
|
1064
|
+
const actual = Object.keys(value);
|
|
1065
|
+
if (actual.length !== required.length || required.some((key) => !Object.hasOwn(value, key))) {
|
|
1066
|
+
throw new RelayV2Error("protocol");
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
function requireString(value, key) {
|
|
1070
|
+
const field = value[key];
|
|
1071
|
+
if (typeof field !== "string")
|
|
1072
|
+
throw new RelayV2Error("protocol");
|
|
1073
|
+
return field;
|
|
1074
|
+
}
|
|
1075
|
+
function requireEncoded(value, key, length) {
|
|
1076
|
+
const field = requireString(value, key);
|
|
1077
|
+
const bytes = length === 22 ? 16 : length === 43 ? 32 : 0;
|
|
1078
|
+
const decoded = Buffer.from(field, "base64url");
|
|
1079
|
+
if (bytes === 0 || field.length !== length || !/^[A-Za-z0-9_-]+$/u.test(field) || decoded.byteLength !== bytes || decoded.toString("base64url") !== field) {
|
|
1080
|
+
throw new RelayV2Error("protocol");
|
|
1081
|
+
}
|
|
1082
|
+
return field;
|
|
1083
|
+
}
|
|
1084
|
+
function requireToken(value, key, length) {
|
|
1085
|
+
const field = requireString(value, key);
|
|
1086
|
+
if (field.length !== length || !/^[A-Za-z0-9_-]+$/u.test(field))
|
|
1087
|
+
throw new RelayV2Error("protocol");
|
|
1088
|
+
return field;
|
|
1089
|
+
}
|
|
1090
|
+
function requireInteger(value, key) {
|
|
1091
|
+
const field = value[key];
|
|
1092
|
+
if (typeof field !== "number" || !Number.isSafeInteger(field))
|
|
1093
|
+
throw new RelayV2Error("protocol");
|
|
1094
|
+
return field;
|
|
1095
|
+
}
|
|
1096
|
+
function hasToken2(value, expected) {
|
|
1097
|
+
return Boolean(value?.split(",").some((token) => token.trim().toLowerCase() === expected));
|
|
1098
|
+
}
|
|
1099
|
+
function isTimeoutError(error) {
|
|
1100
|
+
return Boolean(error && typeof error === "object" && error.reason === "timeout");
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
// node_modules/.pnpm/mcporter@0.13.13/node_modules/mcporter/dist/chrome-devtools-relay-discovery.js
|
|
1104
|
+
import { spawn } from "node:child_process";
|
|
1105
|
+
import { statSync } from "node:fs";
|
|
1106
|
+
import net2 from "node:net";
|
|
1107
|
+
import path from "node:path";
|
|
1108
|
+
var OPENCLAW_CDP_ARGS = ["browser", "extension", "cdp", "--json"];
|
|
1109
|
+
var OPENCLAW_WINDOWS_COMMAND_SUFFIX = "browser extension cdp --json";
|
|
1110
|
+
var OPENCLAW_WINDOWS_DEFAULT_PATHEXT = ".COM;.EXE;.BAT;.CMD";
|
|
1111
|
+
var OPENCLAW_WINDOWS_TREE_KILL_TIMEOUT_MS = 1e3;
|
|
1112
|
+
var OPENCLAW_PROFILE_PATTERN = /^[a-z0-9][a-z0-9_-]{0,63}$/i;
|
|
1113
|
+
var OPENCLAW_RELAY_DISCOVERY_MAX_OUTPUT_BYTES = 64 * 1024;
|
|
1114
|
+
var OPENCLAW_RELAY_DISCOVERY_ENV_KEYS = [
|
|
1115
|
+
"APPDATA",
|
|
1116
|
+
"COMSPEC",
|
|
1117
|
+
"ComSpec",
|
|
1118
|
+
"HOME",
|
|
1119
|
+
"HOMEDRIVE",
|
|
1120
|
+
"HOMEPATH",
|
|
1121
|
+
"LANG",
|
|
1122
|
+
"LC_ALL",
|
|
1123
|
+
"LOCALAPPDATA",
|
|
1124
|
+
"OPENCLAW_CONFIG_PATH",
|
|
1125
|
+
"OPENCLAW_GATEWAY_PORT",
|
|
1126
|
+
"OPENCLAW_HOME",
|
|
1127
|
+
"OPENCLAW_OAUTH_DIR",
|
|
1128
|
+
"OPENCLAW_PROFILE",
|
|
1129
|
+
"OPENCLAW_STATE_DIR",
|
|
1130
|
+
"PATH",
|
|
1131
|
+
"Path",
|
|
1132
|
+
"PATHEXT",
|
|
1133
|
+
"SYSTEMROOT",
|
|
1134
|
+
"SystemRoot",
|
|
1135
|
+
"TEMP",
|
|
1136
|
+
"TMP",
|
|
1137
|
+
"TMPDIR",
|
|
1138
|
+
"USERPROFILE",
|
|
1139
|
+
"WINDIR",
|
|
1140
|
+
"XDG_CONFIG_HOME",
|
|
1141
|
+
"XDG_STATE_HOME"
|
|
1142
|
+
];
|
|
1143
|
+
function normalizeOpenClawProfile(raw) {
|
|
1144
|
+
const profile = raw?.trim();
|
|
1145
|
+
if (!profile || profile.toLowerCase() === "default" || !OPENCLAW_PROFILE_PATTERN.test(profile)) {
|
|
1146
|
+
return void 0;
|
|
1147
|
+
}
|
|
1148
|
+
return profile;
|
|
1149
|
+
}
|
|
1150
|
+
async function discoverOpenClawRelayUrl(options) {
|
|
1151
|
+
const platform = options.platform ?? process.platform;
|
|
1152
|
+
const cwd = options.cwd ?? process.cwd();
|
|
1153
|
+
const filteredEnv = openClawDiscoveryEnvironment(options.env);
|
|
1154
|
+
const command = resolveOpenClawRelayDiscoveryCommandPlan(filteredEnv, platform, options.isFile, cwd);
|
|
1155
|
+
if (!command)
|
|
1156
|
+
return { reason: "unavailable" };
|
|
1157
|
+
const env = sanitizeOpenClawDiscoveryCommandEnvironment(filteredEnv, command, platform, cwd);
|
|
1158
|
+
const result = await (options.run ?? runOpenClawRelayDiscovery)({
|
|
1159
|
+
...command,
|
|
1160
|
+
env,
|
|
1161
|
+
timeoutMs: options.timeoutMs,
|
|
1162
|
+
maxOutputBytes: OPENCLAW_RELAY_DISCOVERY_MAX_OUTPUT_BYTES,
|
|
1163
|
+
shell: false
|
|
1164
|
+
});
|
|
1165
|
+
return validateDiscoveryCommandResult(result, options.keyId);
|
|
1166
|
+
}
|
|
1167
|
+
function runOpenClawRelayDiscovery(options, runtime = {}) {
|
|
1168
|
+
return new Promise((resolve) => {
|
|
1169
|
+
const child = (runtime.spawn ?? spawn)(options.executable, [...options.args], {
|
|
1170
|
+
cwd: options.cwd,
|
|
1171
|
+
env: options.env,
|
|
1172
|
+
shell: options.shell,
|
|
1173
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
1174
|
+
windowsVerbatimArguments: options.platform === "win32",
|
|
1175
|
+
windowsHide: true
|
|
1176
|
+
});
|
|
1177
|
+
const stdout = [];
|
|
1178
|
+
let outputBytes = 0;
|
|
1179
|
+
let settled = false;
|
|
1180
|
+
let terminating = false;
|
|
1181
|
+
let timer;
|
|
1182
|
+
const finish = (result) => {
|
|
1183
|
+
if (settled)
|
|
1184
|
+
return;
|
|
1185
|
+
settled = true;
|
|
1186
|
+
if (timer)
|
|
1187
|
+
clearTimeout(timer);
|
|
1188
|
+
resolve(result);
|
|
1189
|
+
};
|
|
1190
|
+
const terminate = (kind) => {
|
|
1191
|
+
if (settled || terminating)
|
|
1192
|
+
return;
|
|
1193
|
+
terminating = true;
|
|
1194
|
+
child.stdout.destroy();
|
|
1195
|
+
child.stderr.destroy();
|
|
1196
|
+
const plan = createOpenClawRelayDiscoveryTerminationPlan(options, child.pid);
|
|
1197
|
+
const terminateProcessTree = runtime.terminateProcessTree ?? terminateOpenClawRelayDiscoveryProcessTree;
|
|
1198
|
+
let termination = Promise.resolve();
|
|
1199
|
+
if (plan) {
|
|
1200
|
+
try {
|
|
1201
|
+
termination = terminateProcessTree(plan, child);
|
|
1202
|
+
} catch {
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
void boundedTreeTermination(termination).finally(() => {
|
|
1206
|
+
child.unref();
|
|
1207
|
+
finish({ kind });
|
|
1208
|
+
});
|
|
1209
|
+
};
|
|
1210
|
+
const collect = (chunk, keep) => {
|
|
1211
|
+
if (settled || terminating)
|
|
1212
|
+
return;
|
|
1213
|
+
outputBytes += chunk.byteLength;
|
|
1214
|
+
if (outputBytes > options.maxOutputBytes) {
|
|
1215
|
+
terminate("overflow");
|
|
1216
|
+
return;
|
|
1217
|
+
}
|
|
1218
|
+
if (keep)
|
|
1219
|
+
stdout.push(chunk);
|
|
1220
|
+
};
|
|
1221
|
+
child.stdout.on("data", (chunk) => collect(chunk, true));
|
|
1222
|
+
child.stderr.on("data", (chunk) => collect(chunk, false));
|
|
1223
|
+
child.once("error", (error) => {
|
|
1224
|
+
if (!terminating)
|
|
1225
|
+
finish({ kind: classifyCommandError(error) });
|
|
1226
|
+
});
|
|
1227
|
+
child.once("close", (code) => {
|
|
1228
|
+
if (!terminating) {
|
|
1229
|
+
finish(code === 0 ? { kind: "success", stdout: Buffer.concat(stdout) } : { kind: "nonzero" });
|
|
1230
|
+
}
|
|
1231
|
+
});
|
|
1232
|
+
timer = setTimeout(() => terminate("timeout"), options.timeoutMs);
|
|
1233
|
+
timer.unref();
|
|
1234
|
+
});
|
|
1235
|
+
}
|
|
1236
|
+
function resolveOpenClawRelayDiscoveryCommandPlan(env, platform = process.platform, isFile = isRegularFile, cwd = process.cwd()) {
|
|
1237
|
+
if (platform !== "win32")
|
|
1238
|
+
return { executable: "openclaw", args: OPENCLAW_CDP_ARGS, platform };
|
|
1239
|
+
const executable = resolveWindowsCommandInterpreter(env);
|
|
1240
|
+
const openClawShim = resolveWindowsOpenClawShim(env, isFile, cwd);
|
|
1241
|
+
if (!executable || !openClawShim)
|
|
1242
|
+
return void 0;
|
|
1243
|
+
const taskkillExecutable = path.win32.join(path.win32.dirname(executable), "taskkill.exe");
|
|
1244
|
+
if (!isSafeWindowsSystemToolPath(taskkillExecutable, "taskkill.exe"))
|
|
1245
|
+
return void 0;
|
|
1246
|
+
return {
|
|
1247
|
+
executable,
|
|
1248
|
+
// The only variable bytes are the validated absolute shim path. No config,
|
|
1249
|
+
// profile, URL, metadata, or unresolved command name reaches cmd.exe.
|
|
1250
|
+
args: ["/d", "/s", "/c", `""${openClawShim}" ${OPENCLAW_WINDOWS_COMMAND_SUFFIX}"`],
|
|
1251
|
+
cwd: path.win32.dirname(openClawShim),
|
|
1252
|
+
platform,
|
|
1253
|
+
taskkillExecutable
|
|
1254
|
+
};
|
|
1255
|
+
}
|
|
1256
|
+
function resolveWindowsCommandInterpreter(env) {
|
|
1257
|
+
const systemRoot = env.SYSTEMROOT?.trim() || env.SystemRoot?.trim();
|
|
1258
|
+
const systemCmd = systemRoot && isSafeWindowsSystemRoot(systemRoot) ? path.win32.join(systemRoot, "System32", "cmd.exe") : void 0;
|
|
1259
|
+
const comSpec = env.COMSPEC?.trim() || env.ComSpec?.trim();
|
|
1260
|
+
if (comSpec && isSafeWindowsCmdPath(comSpec)) {
|
|
1261
|
+
const normalizedComSpec = path.win32.normalize(comSpec);
|
|
1262
|
+
if (systemCmd && normalizedComSpec.toLowerCase() === systemCmd.toLowerCase() || !systemCmd && isConventionalWindowsSystemCmd(normalizedComSpec)) {
|
|
1263
|
+
return normalizedComSpec;
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
return systemCmd;
|
|
1267
|
+
}
|
|
1268
|
+
function resolveWindowsOpenClawShim(env, isFile, cwd) {
|
|
1269
|
+
const pathEntries = safeWindowsPathEntries(env.PATH ?? env.Path, cwd);
|
|
1270
|
+
const extensions = windowsPathExtensions(env.PATHEXT);
|
|
1271
|
+
for (const directory of pathEntries) {
|
|
1272
|
+
for (const extension of extensions) {
|
|
1273
|
+
if (extension.toLowerCase() !== ".cmd")
|
|
1274
|
+
continue;
|
|
1275
|
+
const candidate = path.win32.join(directory, `openclaw${extension}`);
|
|
1276
|
+
if (isSafeWindowsCommandPath(candidate) && isFile(candidate))
|
|
1277
|
+
return path.win32.normalize(candidate);
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
return void 0;
|
|
1281
|
+
}
|
|
1282
|
+
function isSafeWindowsCmdPath(value) {
|
|
1283
|
+
return isSafeWindowsSystemToolPath(value, "cmd.exe");
|
|
1284
|
+
}
|
|
1285
|
+
function isSafeWindowsSystemToolPath(value, basename) {
|
|
1286
|
+
const parent = path.win32.dirname(value);
|
|
1287
|
+
const root = path.win32.dirname(parent);
|
|
1288
|
+
return isSafeWindowsCommandPath(value) && path.win32.basename(value).toLowerCase() === basename && path.win32.basename(parent).toLowerCase() === "system32" && ["windows", "winnt"].includes(path.win32.basename(root).toLowerCase());
|
|
1289
|
+
}
|
|
1290
|
+
function isSafeWindowsSystemRoot(value) {
|
|
1291
|
+
const basename = path.win32.basename(value).toLowerCase();
|
|
1292
|
+
return isSafeWindowsAbsolutePath(value) && basename !== "cmd.exe" && basename !== "system32";
|
|
1293
|
+
}
|
|
1294
|
+
function isConventionalWindowsSystemCmd(value) {
|
|
1295
|
+
const parent = path.win32.dirname(value);
|
|
1296
|
+
const root = path.win32.dirname(parent);
|
|
1297
|
+
return path.win32.basename(parent).toLowerCase() === "system32" && ["windows", "winnt"].includes(path.win32.basename(root).toLowerCase());
|
|
1298
|
+
}
|
|
1299
|
+
function isSafeWindowsAbsolutePath(value) {
|
|
1300
|
+
if (!/^[a-z]:[\\/]/i.test(value) || /["<>|?*]/u.test(value) || hasControlCharacter(value) || value.slice(2).includes(":")) {
|
|
1301
|
+
return false;
|
|
1302
|
+
}
|
|
1303
|
+
const segments = value.replaceAll("/", "\\").split("\\");
|
|
1304
|
+
return !segments.some((segment) => segment === "." || segment === "..");
|
|
1305
|
+
}
|
|
1306
|
+
function isSafeWindowsCommandPath(value) {
|
|
1307
|
+
return isSafeWindowsAbsolutePath(value) && !/["%&|<>^!]/u.test(value) && !hasControlCharacter(value);
|
|
1308
|
+
}
|
|
1309
|
+
function hasControlCharacter(value) {
|
|
1310
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
1311
|
+
if (value.charCodeAt(index) < 32)
|
|
1312
|
+
return true;
|
|
1313
|
+
}
|
|
1314
|
+
return false;
|
|
1315
|
+
}
|
|
1316
|
+
function safeWindowsPathEntries(value, excludedDirectory) {
|
|
1317
|
+
if (!value)
|
|
1318
|
+
return [];
|
|
1319
|
+
const excluded = excludedDirectory && isSafeWindowsCommandPath(excludedDirectory) ? path.win32.resolve(excludedDirectory).toLowerCase() : void 0;
|
|
1320
|
+
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);
|
|
1321
|
+
}
|
|
1322
|
+
function windowsPathExtensions(value) {
|
|
1323
|
+
const raw = value?.trim() || OPENCLAW_WINDOWS_DEFAULT_PATHEXT;
|
|
1324
|
+
return raw.split(";").map((extension) => extension.trim()).filter((extension) => /^\.[a-z0-9]+$/iu.test(extension));
|
|
1325
|
+
}
|
|
1326
|
+
function isRegularFile(candidate) {
|
|
1327
|
+
try {
|
|
1328
|
+
return statSync(candidate).isFile();
|
|
1329
|
+
} catch {
|
|
1330
|
+
return false;
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
function sanitizeOpenClawDiscoveryCommandEnvironment(env, command, platform, sourceCwd) {
|
|
1334
|
+
if (platform !== "win32")
|
|
1335
|
+
return env;
|
|
1336
|
+
const sanitized = { ...env };
|
|
1337
|
+
delete sanitized.ComSpec;
|
|
1338
|
+
delete sanitized.COMSPEC;
|
|
1339
|
+
sanitized.ComSpec = command.executable;
|
|
1340
|
+
const safePath = safeWindowsPathEntries(sanitized.PATH ?? sanitized.Path, sourceCwd).join(";");
|
|
1341
|
+
delete sanitized.Path;
|
|
1342
|
+
delete sanitized.PATH;
|
|
1343
|
+
if (safePath)
|
|
1344
|
+
sanitized.Path = safePath;
|
|
1345
|
+
const systemRoot = sanitized.SYSTEMROOT?.trim() || sanitized.SystemRoot?.trim();
|
|
1346
|
+
if (systemRoot && !isSafeWindowsSystemRoot(systemRoot)) {
|
|
1347
|
+
delete sanitized.SystemRoot;
|
|
1348
|
+
delete sanitized.SYSTEMROOT;
|
|
1349
|
+
}
|
|
1350
|
+
return sanitized;
|
|
1351
|
+
}
|
|
1352
|
+
function createOpenClawRelayDiscoveryTerminationPlan(options, pid) {
|
|
1353
|
+
if (!Number.isSafeInteger(pid) || !pid || pid <= 0)
|
|
1354
|
+
return void 0;
|
|
1355
|
+
if (options.platform !== "win32")
|
|
1356
|
+
return { kind: "posix-sigkill", pid, signal: "SIGKILL" };
|
|
1357
|
+
if (!options.taskkillExecutable || !isSafeWindowsSystemToolPath(options.taskkillExecutable, "taskkill.exe")) {
|
|
1358
|
+
return void 0;
|
|
1359
|
+
}
|
|
1360
|
+
return {
|
|
1361
|
+
kind: "windows-taskkill",
|
|
1362
|
+
pid,
|
|
1363
|
+
executable: options.taskkillExecutable,
|
|
1364
|
+
args: ["/PID", String(pid), "/T", "/F"],
|
|
1365
|
+
shell: false,
|
|
1366
|
+
windowsHide: true
|
|
1367
|
+
};
|
|
1368
|
+
}
|
|
1369
|
+
async function terminateOpenClawRelayDiscoveryProcessTree(plan, child) {
|
|
1370
|
+
if (plan.kind === "posix-sigkill") {
|
|
1371
|
+
try {
|
|
1372
|
+
child.kill(plan.signal);
|
|
1373
|
+
} catch {
|
|
1374
|
+
}
|
|
1375
|
+
return;
|
|
1376
|
+
}
|
|
1377
|
+
await new Promise((resolve) => {
|
|
1378
|
+
let killer;
|
|
1379
|
+
try {
|
|
1380
|
+
killer = spawn(plan.executable, [...plan.args], {
|
|
1381
|
+
shell: plan.shell,
|
|
1382
|
+
stdio: "ignore",
|
|
1383
|
+
windowsHide: plan.windowsHide
|
|
1384
|
+
});
|
|
1385
|
+
} catch {
|
|
1386
|
+
resolve();
|
|
1387
|
+
return;
|
|
1388
|
+
}
|
|
1389
|
+
let done = false;
|
|
1390
|
+
const finish = () => {
|
|
1391
|
+
if (done)
|
|
1392
|
+
return;
|
|
1393
|
+
done = true;
|
|
1394
|
+
clearTimeout(timer);
|
|
1395
|
+
resolve();
|
|
1396
|
+
};
|
|
1397
|
+
killer.once("error", finish);
|
|
1398
|
+
killer.once("close", finish);
|
|
1399
|
+
const timer = setTimeout(() => {
|
|
1400
|
+
try {
|
|
1401
|
+
killer.kill("SIGKILL");
|
|
1402
|
+
} catch {
|
|
1403
|
+
}
|
|
1404
|
+
finish();
|
|
1405
|
+
}, OPENCLAW_WINDOWS_TREE_KILL_TIMEOUT_MS);
|
|
1406
|
+
timer.unref();
|
|
1407
|
+
});
|
|
1408
|
+
}
|
|
1409
|
+
async function boundedTreeTermination(termination) {
|
|
1410
|
+
let timer;
|
|
1411
|
+
try {
|
|
1412
|
+
await Promise.race([
|
|
1413
|
+
termination.catch(() => void 0),
|
|
1414
|
+
new Promise((resolve) => {
|
|
1415
|
+
timer = setTimeout(resolve, OPENCLAW_WINDOWS_TREE_KILL_TIMEOUT_MS + 250);
|
|
1416
|
+
timer.unref();
|
|
1417
|
+
})
|
|
1418
|
+
]);
|
|
1419
|
+
} finally {
|
|
1420
|
+
if (timer)
|
|
1421
|
+
clearTimeout(timer);
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
function classifyCommandError(error) {
|
|
1425
|
+
const details = error;
|
|
1426
|
+
if (details.code === "ENOENT")
|
|
1427
|
+
return "unavailable";
|
|
1428
|
+
if (details.code === "ETIMEDOUT" || details.killed === true)
|
|
1429
|
+
return "timeout";
|
|
1430
|
+
if (details.code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER" || details.code === "ENOBUFS")
|
|
1431
|
+
return "overflow";
|
|
1432
|
+
return "nonzero";
|
|
1433
|
+
}
|
|
1434
|
+
function validateDiscoveryCommandResult(result, expectedKeyId) {
|
|
1435
|
+
if (result.kind !== "success")
|
|
1436
|
+
return { reason: result.kind };
|
|
1437
|
+
if (result.stdout.byteLength > OPENCLAW_RELAY_DISCOVERY_MAX_OUTPUT_BYTES)
|
|
1438
|
+
return { reason: "overflow" };
|
|
1439
|
+
const encoded = result.stdout.toString("utf8").trim();
|
|
1440
|
+
const parseErrors = [];
|
|
1441
|
+
const tree = parseTree(encoded, parseErrors, { allowTrailingComma: false, disallowComments: true });
|
|
1442
|
+
if (!tree || parseErrors.length > 0 || hasDuplicateObjectKeys(tree))
|
|
1443
|
+
return { reason: "malformed" };
|
|
1444
|
+
let metadata;
|
|
1445
|
+
try {
|
|
1446
|
+
metadata = JSON.parse(encoded);
|
|
1447
|
+
} catch {
|
|
1448
|
+
return { reason: "malformed" };
|
|
1449
|
+
}
|
|
1450
|
+
if (!isPlainObject(metadata) || !hasExactKeys(metadata, ["auth", "browserUrl", "wsEndpoint"])) {
|
|
1451
|
+
return { reason: "incompatible" };
|
|
1452
|
+
}
|
|
1453
|
+
if (!isPlainObject(metadata.auth) || !hasExactKeys(metadata.auth, [
|
|
1454
|
+
"challengeUrl",
|
|
1455
|
+
"completeUrl",
|
|
1456
|
+
"flow",
|
|
1457
|
+
"keyId",
|
|
1458
|
+
"label",
|
|
1459
|
+
"method",
|
|
1460
|
+
"resource",
|
|
1461
|
+
"role",
|
|
1462
|
+
"transport",
|
|
1463
|
+
"version"
|
|
1464
|
+
])) {
|
|
1465
|
+
return { reason: "incompatible" };
|
|
1466
|
+
}
|
|
1467
|
+
const browserUrl = parseBrowserUrl(metadata.browserUrl);
|
|
1468
|
+
if (!browserUrl)
|
|
1469
|
+
return { reason: "unsafe" };
|
|
1470
|
+
const wsEndpoint = parseWebSocketUrl(metadata.wsEndpoint);
|
|
1471
|
+
if (!wsEndpoint || wsEndpoint.host !== browserUrl.host)
|
|
1472
|
+
return { reason: "unsafe" };
|
|
1473
|
+
const auth = metadata.auth;
|
|
1474
|
+
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()) {
|
|
1475
|
+
return { reason: "incompatible" };
|
|
1476
|
+
}
|
|
1477
|
+
if (auth.keyId !== expectedKeyId)
|
|
1478
|
+
return { reason: "key-id-mismatch" };
|
|
1479
|
+
return { reason: "success", url: browserUrl };
|
|
1480
|
+
}
|
|
1481
|
+
function parseBrowserUrl(value) {
|
|
1482
|
+
if (typeof value !== "string")
|
|
1483
|
+
return void 0;
|
|
1484
|
+
let url;
|
|
1485
|
+
try {
|
|
1486
|
+
url = new URL(value);
|
|
1487
|
+
} catch {
|
|
1488
|
+
return void 0;
|
|
1489
|
+
}
|
|
1490
|
+
if (url.protocol !== "http:" || !isLoopbackHostname(url.hostname) || url.username !== "" || url.password !== "" || !hasValidPort(url) || url.pathname !== "/" || url.search !== "" || url.hash !== "") {
|
|
1491
|
+
return void 0;
|
|
1492
|
+
}
|
|
1493
|
+
return new URL(url.origin);
|
|
1494
|
+
}
|
|
1495
|
+
function parseWebSocketUrl(value) {
|
|
1496
|
+
if (typeof value !== "string")
|
|
1497
|
+
return void 0;
|
|
1498
|
+
let url;
|
|
1499
|
+
try {
|
|
1500
|
+
url = new URL(value);
|
|
1501
|
+
} catch {
|
|
1502
|
+
return void 0;
|
|
1503
|
+
}
|
|
1504
|
+
if (url.protocol !== "ws:" || !isLoopbackHostname(url.hostname) || url.username !== "" || url.password !== "" || !hasValidPort(url) || url.pathname !== BROWSER_RELAY_CDP_PATH || url.search !== "" || url.hash !== "") {
|
|
1505
|
+
return void 0;
|
|
1506
|
+
}
|
|
1507
|
+
return url;
|
|
1508
|
+
}
|
|
1509
|
+
function isLoopbackHostname(hostname) {
|
|
1510
|
+
const normalized = hostname.replace(/^\[(.*)\]$/u, "$1").toLowerCase();
|
|
1511
|
+
if (normalized === "localhost" || normalized === "::1")
|
|
1512
|
+
return true;
|
|
1513
|
+
if (net2.isIPv4(normalized))
|
|
1514
|
+
return normalized.startsWith("127.");
|
|
1515
|
+
if (!net2.isIPv6(normalized))
|
|
1516
|
+
return false;
|
|
1517
|
+
return normalized.startsWith("::ffff:127.") || /^::ffff:7f[0-9a-f]{2}:[0-9a-f]{1,4}$/u.test(normalized);
|
|
1518
|
+
}
|
|
1519
|
+
function hasValidPort(url) {
|
|
1520
|
+
if (url.port === "")
|
|
1521
|
+
return true;
|
|
1522
|
+
const port = Number(url.port);
|
|
1523
|
+
return Number.isSafeInteger(port) && port >= 1 && port <= 65535;
|
|
1524
|
+
}
|
|
1525
|
+
function isPlainObject(value) {
|
|
1526
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
1527
|
+
}
|
|
1528
|
+
function hasExactKeys(value, expected) {
|
|
1529
|
+
const actual = Object.keys(value).toSorted();
|
|
1530
|
+
return actual.length === expected.length && actual.every((key, index) => key === expected[index]);
|
|
1531
|
+
}
|
|
1532
|
+
function hasDuplicateObjectKeys(node) {
|
|
1533
|
+
if (node.type === "object") {
|
|
1534
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1535
|
+
for (const property of node.children ?? []) {
|
|
1536
|
+
const key = property.children?.[0]?.value;
|
|
1537
|
+
if (typeof key !== "string" || seen.has(key))
|
|
1538
|
+
return true;
|
|
1539
|
+
seen.add(key);
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
return (node.children ?? []).some(hasDuplicateObjectKeys);
|
|
1543
|
+
}
|
|
1544
|
+
function openClawDiscoveryEnvironment(env) {
|
|
1545
|
+
const filtered = Object.fromEntries(OPENCLAW_RELAY_DISCOVERY_ENV_KEYS.flatMap((key) => {
|
|
1546
|
+
const value = env[key];
|
|
1547
|
+
return value === void 0 ? [] : [[key, value]];
|
|
1548
|
+
}));
|
|
1549
|
+
const profile = normalizeOpenClawProfile(filtered.OPENCLAW_PROFILE);
|
|
1550
|
+
if (profile)
|
|
1551
|
+
filtered.OPENCLAW_PROFILE = profile;
|
|
1552
|
+
else
|
|
1553
|
+
delete filtered.OPENCLAW_PROFILE;
|
|
1554
|
+
return filtered;
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1557
|
+
// node_modules/.pnpm/mcporter@0.13.13/node_modules/mcporter/dist/chrome-devtools-relay.js
|
|
1558
|
+
var DEFAULT_RELAY_PROBE_TIMEOUT_MS = 2e4;
|
|
1559
|
+
var MIN_RELAY_PROBE_TIMEOUT_MS = 100;
|
|
1560
|
+
var MAX_RELAY_PROBE_TIMEOUT_MS = 3e4;
|
|
1561
|
+
var UNRESOLVED_RELAY_IDENTITY = "undiscovered";
|
|
1562
|
+
var RELAY_DEPENDENCY_SENTINEL = "$MCPORTER_CHROME_RELAY_DEPENDENCY:";
|
|
1563
|
+
var RELAY_DISCOVERY_SENTINEL = "$MCPORTER_CHROME_RELAY_DISCOVERY:";
|
|
1564
|
+
var CHROME_DEVTOOLS_RELAY_RUNTIME_ENV_KEYS = [
|
|
1565
|
+
"MCPORTER_CHROME_DEVTOOLS_RELAY_POLICY",
|
|
1566
|
+
"MCPORTER_DISABLE_CHROME_DEVTOOLS_RELAY",
|
|
1567
|
+
"MCPORTER_CHROME_DEVTOOLS_RELAY_URL",
|
|
1568
|
+
"MCPORTER_CHROME_DEVTOOLS_RELAY_TIMEOUT_MS",
|
|
1569
|
+
...OPENCLAW_RELAY_DISCOVERY_ENV_KEYS
|
|
1570
|
+
];
|
|
1571
|
+
var ChromeDevtoolsRelayRequiredError = class extends Error {
|
|
1572
|
+
decision;
|
|
1573
|
+
constructor(decision) {
|
|
1574
|
+
super(`Chrome DevTools relay policy 'require' could not use the OpenClaw extension relay (${decision.reason}). No chrome-devtools-mcp process was launched.`);
|
|
1575
|
+
this.decision = decision;
|
|
1576
|
+
this.name = "ChromeDevtoolsRelayRequiredError";
|
|
1577
|
+
}
|
|
1578
|
+
};
|
|
1579
|
+
var ChromeDevtoolsRelayDiscoveryError = class extends Error {
|
|
1580
|
+
code = "relay_discovery_failed";
|
|
1581
|
+
decision;
|
|
1582
|
+
constructor(reason, policy, timeoutMs) {
|
|
1583
|
+
super(`OpenClaw relay discovery failed (discovery-${reason}; timeout budget ${timeoutMs}ms). No relay endpoint was selected; this request was not dispatched. Check the OpenClaw installation and the canonical Chrome definition in the global mcporter config. For slow cold starts, set MCPORTER_CHROME_DEVTOOLS_RELAY_TIMEOUT_MS in that definition\u2019s env map; drain and stop the daemon after changing its configuration.`);
|
|
1584
|
+
this.name = "ChromeDevtoolsRelayDiscoveryError";
|
|
1585
|
+
this.decision = { route: "unavailable", reason: `discovery-${reason}`, policy, endpoint: void 0 };
|
|
1586
|
+
}
|
|
1587
|
+
};
|
|
1588
|
+
async function discoverRelayBaseUrl(env, keyId, timeoutMs, policy, options) {
|
|
1589
|
+
const discovered = await discoverOpenClawRelayUrl({
|
|
1590
|
+
env,
|
|
1591
|
+
keyId,
|
|
1592
|
+
timeoutMs,
|
|
1593
|
+
run: options.discover,
|
|
1594
|
+
platform: options.discovery?.platform,
|
|
1595
|
+
isFile: options.discovery?.isFile,
|
|
1596
|
+
cwd: options.discovery?.cwd
|
|
1597
|
+
}).catch(() => ({ reason: "unavailable" }));
|
|
1598
|
+
if (discovered.reason === "success" && discovered.url)
|
|
1599
|
+
return discovered.url;
|
|
1600
|
+
throw new ChromeDevtoolsRelayDiscoveryError(discovered.reason === "success" ? "malformed" : discovered.reason, policy, timeoutMs);
|
|
1601
|
+
}
|
|
1602
|
+
var lastDecisions = /* @__PURE__ */ new Map();
|
|
1603
|
+
function recordChromeDevtoolsRelayDecision(server, decision) {
|
|
1604
|
+
lastDecisions.set(server, decision);
|
|
1605
|
+
}
|
|
1606
|
+
function getChromeDevtoolsRelayDecision(server) {
|
|
1607
|
+
return lastDecisions.get(server);
|
|
1608
|
+
}
|
|
1609
|
+
function formatChromeDevtoolsRelayDecision(decision) {
|
|
1610
|
+
return JSON.stringify(decision);
|
|
1611
|
+
}
|
|
1612
|
+
function resolveChromeDevtoolsRelayProbeTimeoutMs(env = process.env) {
|
|
1613
|
+
const raw = env.MCPORTER_CHROME_DEVTOOLS_RELAY_TIMEOUT_MS?.trim();
|
|
1614
|
+
if (!raw || !/^\d+$/.test(raw))
|
|
1615
|
+
return DEFAULT_RELAY_PROBE_TIMEOUT_MS;
|
|
1616
|
+
const parsed = Number(raw);
|
|
1617
|
+
if (!Number.isSafeInteger(parsed) || parsed <= 0)
|
|
1618
|
+
return DEFAULT_RELAY_PROBE_TIMEOUT_MS;
|
|
1619
|
+
return Math.min(MAX_RELAY_PROBE_TIMEOUT_MS, Math.max(MIN_RELAY_PROBE_TIMEOUT_MS, parsed));
|
|
1620
|
+
}
|
|
1621
|
+
function resolveChromeDevtoolsRelayPolicy(configured, env = process.env) {
|
|
1622
|
+
if (env.MCPORTER_DISABLE_CHROME_DEVTOOLS_RELAY === "1")
|
|
1623
|
+
return "off";
|
|
1624
|
+
const raw = env.MCPORTER_CHROME_DEVTOOLS_RELAY_POLICY?.trim().toLowerCase();
|
|
1625
|
+
if (!raw)
|
|
1626
|
+
return configured ?? "prefer";
|
|
1627
|
+
if (raw === "off" || raw === "prefer" || raw === "require")
|
|
1628
|
+
return raw;
|
|
1629
|
+
throw new Error(`Invalid MCPORTER_CHROME_DEVTOOLS_RELAY_POLICY '${raw}'. Expected off, prefer, or require.`);
|
|
1630
|
+
}
|
|
1631
|
+
function isChromeDevtoolsAutoConnectCommand(command, args) {
|
|
1632
|
+
return resolveChromeDevtoolsAutoConnectCommand(command, args).enabled;
|
|
1633
|
+
}
|
|
1634
|
+
function explicitRelayBaseUrl(env) {
|
|
1635
|
+
const raw = env.MCPORTER_CHROME_DEVTOOLS_RELAY_URL?.trim();
|
|
1636
|
+
if (!raw)
|
|
1637
|
+
return { explicit: false };
|
|
1638
|
+
let url;
|
|
1639
|
+
try {
|
|
1640
|
+
url = new URL(raw);
|
|
1641
|
+
} catch {
|
|
1642
|
+
return { explicit: true };
|
|
1643
|
+
}
|
|
1644
|
+
if (url.protocol !== "http:" || !["127.0.0.1", "localhost", "[::1]", "::1"].includes(url.hostname) || url.username !== "" || url.password !== "" || url.search !== "" || url.hash !== "") {
|
|
1645
|
+
return { explicit: true };
|
|
1646
|
+
}
|
|
1647
|
+
return { explicit: true, url };
|
|
1648
|
+
}
|
|
1649
|
+
function resolveOpenClawCredentialDir(env) {
|
|
1650
|
+
const oauthOverride = env.OPENCLAW_OAUTH_DIR?.trim();
|
|
1651
|
+
if (oauthOverride)
|
|
1652
|
+
return resolveOpenClawPath(oauthOverride, env);
|
|
1653
|
+
const stateOverride = env.OPENCLAW_STATE_DIR?.trim();
|
|
1654
|
+
const profile = normalizeOpenClawProfile(env.OPENCLAW_PROFILE);
|
|
1655
|
+
const stateDir = stateOverride ? resolveOpenClawPath(stateOverride, env) : path2.join(resolveOpenClawHome(env), profile ? `.openclaw-${profile}` : ".openclaw");
|
|
1656
|
+
return path2.join(stateDir, "credentials");
|
|
1657
|
+
}
|
|
1658
|
+
function resolveOpenClawHome(env) {
|
|
1659
|
+
const override = env.OPENCLAW_HOME?.trim();
|
|
1660
|
+
const osHome = resolveOpenClawOsHome(env);
|
|
1661
|
+
if (!override)
|
|
1662
|
+
return osHome;
|
|
1663
|
+
if (override === "~")
|
|
1664
|
+
return osHome;
|
|
1665
|
+
if (override.startsWith("~/") || override.startsWith("~\\")) {
|
|
1666
|
+
return path2.resolve(osHome, override.slice(2));
|
|
1667
|
+
}
|
|
1668
|
+
return path2.resolve(override);
|
|
1669
|
+
}
|
|
1670
|
+
function resolveOpenClawOsHome(env) {
|
|
1671
|
+
for (const value of [env.HOME, env.USERPROFILE]) {
|
|
1672
|
+
const normalized = value?.trim();
|
|
1673
|
+
if (normalized && normalized !== "undefined" && normalized !== "null")
|
|
1674
|
+
return path2.resolve(normalized);
|
|
1675
|
+
}
|
|
1676
|
+
return os.homedir();
|
|
1677
|
+
}
|
|
1678
|
+
function resolveOpenClawPath(value, env) {
|
|
1679
|
+
if (value === "~")
|
|
1680
|
+
return resolveOpenClawHome(env);
|
|
1681
|
+
if (value.startsWith("~/") || value.startsWith("~\\")) {
|
|
1682
|
+
return path2.resolve(resolveOpenClawHome(env), value.slice(2));
|
|
1683
|
+
}
|
|
1684
|
+
return path2.resolve(value);
|
|
1685
|
+
}
|
|
1686
|
+
function defaultReadCredential(env) {
|
|
1687
|
+
const secretPath = path2.join(resolveOpenClawCredentialDir(env), "browser-extension-relay.secret");
|
|
1688
|
+
return readCredentialPath(secretPath);
|
|
1689
|
+
}
|
|
1690
|
+
function readCredentialPath(secretPath) {
|
|
1691
|
+
let descriptor;
|
|
1692
|
+
try {
|
|
1693
|
+
descriptor = fs.openSync(secretPath, fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW ?? 0));
|
|
1694
|
+
} catch (error) {
|
|
1695
|
+
return { reason: isErrno(error, "ENOENT") ? "missing-credential" : "invalid-credential" };
|
|
1696
|
+
}
|
|
1697
|
+
try {
|
|
1698
|
+
const stat = fs.fstatSync(descriptor);
|
|
1699
|
+
if (!stat.isFile())
|
|
1700
|
+
return { reason: "invalid-credential" };
|
|
1701
|
+
if (process.platform !== "win32") {
|
|
1702
|
+
if ((stat.mode & 63) !== 0)
|
|
1703
|
+
return { reason: "invalid-credential" };
|
|
1704
|
+
if (typeof process.getuid === "function" && stat.uid !== process.getuid())
|
|
1705
|
+
return { reason: "invalid-credential" };
|
|
1706
|
+
}
|
|
1707
|
+
const encoded = fs.readFileSync(descriptor, "utf8").trim();
|
|
1708
|
+
if (!/^[0-9a-f]{64}$/.test(encoded))
|
|
1709
|
+
return { reason: "invalid-credential" };
|
|
1710
|
+
const key = Buffer.from(encoded, "hex");
|
|
1711
|
+
return { credential: { key, keyId: deriveBrowserRelayKeyId(key) } };
|
|
1712
|
+
} catch {
|
|
1713
|
+
return { reason: "invalid-credential" };
|
|
1714
|
+
} finally {
|
|
1715
|
+
fs.closeSync(descriptor);
|
|
1716
|
+
}
|
|
1717
|
+
}
|
|
1718
|
+
async function defaultConnect(baseUrl, credential, timeoutMs) {
|
|
1719
|
+
return await connectChromeDevtoolsRelayV2({ baseUrl, credential, timeoutMs });
|
|
1720
|
+
}
|
|
1721
|
+
async function rewriteChromeDevtoolsArgsForRelay(command, args, env = process.env, options = {}, configuredPolicy) {
|
|
1722
|
+
const relayEnv = selectChromeDevtoolsRelayRuntimeEnvironment(env);
|
|
1723
|
+
const eligible = isChromeDevtoolsAutoConnectCommand(command, args);
|
|
1724
|
+
if (!eligible) {
|
|
1725
|
+
const ambiguous = isAmbiguousChromeDevtoolsAutoConnectCommand(command, args);
|
|
1726
|
+
if (!ambiguous) {
|
|
1727
|
+
return decide({
|
|
1728
|
+
args,
|
|
1729
|
+
applied: false,
|
|
1730
|
+
decision: { route: "legacy", reason: "not-eligible", policy: configuredPolicy ?? "prefer" }
|
|
1731
|
+
}, options);
|
|
1732
|
+
}
|
|
1733
|
+
const ambiguousPolicy = resolveChromeDevtoolsRelayPolicy(configuredPolicy, relayEnv);
|
|
1734
|
+
if (ambiguousPolicy === "require") {
|
|
1735
|
+
return unavailableOrLegacy(args, ambiguousPolicy, "unsupported-command", void 0, options);
|
|
1736
|
+
}
|
|
1737
|
+
return decide({
|
|
1738
|
+
args,
|
|
1739
|
+
applied: false,
|
|
1740
|
+
decision: { route: "legacy", reason: "not-eligible", policy: ambiguousPolicy }
|
|
1741
|
+
}, options);
|
|
1742
|
+
}
|
|
1743
|
+
const policy = resolveChromeDevtoolsRelayPolicy(configuredPolicy, relayEnv);
|
|
1744
|
+
if (policy === "off") {
|
|
1745
|
+
return decide({ args, applied: false, decision: { route: "legacy", reason: "disabled", policy } }, options);
|
|
1746
|
+
}
|
|
1747
|
+
const explicitBase = explicitRelayBaseUrl(relayEnv);
|
|
1748
|
+
if (explicitBase.explicit && !explicitBase.url) {
|
|
1749
|
+
return unavailableOrLegacy(args, policy, "invalid-endpoint", void 0, options);
|
|
1750
|
+
}
|
|
1751
|
+
const loaded = options.readToken ? (() => {
|
|
1752
|
+
const encoded = options.readToken?.();
|
|
1753
|
+
if (!encoded)
|
|
1754
|
+
return { reason: "missing-credential" };
|
|
1755
|
+
if (!/^[0-9a-f]{64}$/u.test(encoded))
|
|
1756
|
+
return { reason: "invalid-credential" };
|
|
1757
|
+
const key = Buffer.from(encoded, "hex");
|
|
1758
|
+
return { credential: { key, keyId: deriveBrowserRelayKeyId(key) } };
|
|
1759
|
+
})() : defaultReadCredential(relayEnv);
|
|
1760
|
+
if (!loaded.credential) {
|
|
1761
|
+
const upstreamEndpoint = explicitBase.url ? new URL("/cdp", explicitBase.url) : void 0;
|
|
1762
|
+
if (upstreamEndpoint)
|
|
1763
|
+
upstreamEndpoint.protocol = "ws:";
|
|
1764
|
+
return unavailableOrLegacy(args, policy, loaded.reason ?? "invalid-credential", upstreamEndpoint?.toString(), options);
|
|
1765
|
+
}
|
|
1766
|
+
const timeoutMs = resolveChromeDevtoolsRelayProbeTimeoutMs(relayEnv);
|
|
1767
|
+
try {
|
|
1768
|
+
let base;
|
|
1769
|
+
try {
|
|
1770
|
+
base = explicitBase.url ?? await discoverRelayBaseUrl(relayEnv, loaded.credential.keyId, timeoutMs, policy, options);
|
|
1771
|
+
} catch (error) {
|
|
1772
|
+
if (!(error instanceof ChromeDevtoolsRelayDiscoveryError))
|
|
1773
|
+
throw error;
|
|
1774
|
+
if (policy === "require") {
|
|
1775
|
+
options.onDecision?.(error.decision);
|
|
1776
|
+
throw error;
|
|
1777
|
+
}
|
|
1778
|
+
return unavailableOrLegacy(args, policy, error.decision.reason, void 0, options);
|
|
1779
|
+
}
|
|
1780
|
+
const upstreamEndpoint = new URL("/cdp", base);
|
|
1781
|
+
upstreamEndpoint.protocol = "ws:";
|
|
1782
|
+
const endpoint = upstreamEndpoint.toString();
|
|
1783
|
+
let probe;
|
|
1784
|
+
try {
|
|
1785
|
+
probe = await (options.connect ?? defaultConnect)(base, loaded.credential, timeoutMs);
|
|
1786
|
+
} catch {
|
|
1787
|
+
return unavailableOrLegacy(args, policy, "network-error", endpoint, options);
|
|
1788
|
+
}
|
|
1789
|
+
const probeDetails = { probeDurationMs: probe.durationMs, probeStatus: probe.status };
|
|
1790
|
+
if (probe.reason !== "success" || !probe.upstream) {
|
|
1791
|
+
probe.upstream?.socket.destroy();
|
|
1792
|
+
const reason = probe.reason === "success" ? "protocol" : probe.reason;
|
|
1793
|
+
return unavailableOrLegacy(args, policy, reason, endpoint, options, probeDetails);
|
|
1794
|
+
}
|
|
1795
|
+
let proxy;
|
|
1796
|
+
try {
|
|
1797
|
+
proxy = await (options.startProxy ?? startChromeDevtoolsRelayProxy)({ upstream: probe.upstream });
|
|
1798
|
+
} catch {
|
|
1799
|
+
probe.upstream.socket.destroy();
|
|
1800
|
+
return unavailableOrLegacy(args, policy, "network-error", endpoint, options, probeDetails);
|
|
1801
|
+
}
|
|
1802
|
+
const rewritten = replaceChromeDevtoolsAutoConnectArgs(command, args, ["--wsEndpoint", proxy.endpoint]);
|
|
1803
|
+
try {
|
|
1804
|
+
return decide({
|
|
1805
|
+
args: rewritten,
|
|
1806
|
+
applied: true,
|
|
1807
|
+
endpoint: proxy.endpoint,
|
|
1808
|
+
proxy,
|
|
1809
|
+
decision: { route: "relay", reason: "success", policy, endpoint, ...probeDetails }
|
|
1810
|
+
}, options);
|
|
1811
|
+
} catch (error) {
|
|
1812
|
+
await proxy.close().catch(() => {
|
|
1813
|
+
});
|
|
1814
|
+
throw error;
|
|
1815
|
+
}
|
|
1816
|
+
} finally {
|
|
1817
|
+
loaded.credential.key.fill(0);
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
function unavailableOrLegacy(args, policy, reason, endpoint, options, probe = {}) {
|
|
1821
|
+
const decision = {
|
|
1822
|
+
route: policy === "require" ? "unavailable" : "legacy",
|
|
1823
|
+
reason,
|
|
1824
|
+
policy,
|
|
1825
|
+
endpoint,
|
|
1826
|
+
...probe
|
|
1827
|
+
};
|
|
1828
|
+
options.onDecision?.(decision);
|
|
1829
|
+
if (policy === "require")
|
|
1830
|
+
throw new ChromeDevtoolsRelayRequiredError(decision);
|
|
1831
|
+
return { args, applied: false, decision };
|
|
1832
|
+
}
|
|
1833
|
+
function decide(result, options) {
|
|
1834
|
+
options.onDecision?.(result.decision);
|
|
1835
|
+
return result;
|
|
1836
|
+
}
|
|
1837
|
+
function resolveChromeDevtoolsRelayEnvironment(overrides, env = process.env) {
|
|
1838
|
+
return { ...env, ...resolveRelayDefinitionEnvOverrides(overrides, env) };
|
|
1839
|
+
}
|
|
1840
|
+
function selectChromeDevtoolsRelayRuntimeEnvironment(env) {
|
|
1841
|
+
return Object.fromEntries(CHROME_DEVTOOLS_RELAY_RUNTIME_ENV_KEYS.flatMap((key) => {
|
|
1842
|
+
const value = env[key];
|
|
1843
|
+
return value === void 0 ? [] : [[key, value]];
|
|
1844
|
+
}));
|
|
1845
|
+
}
|
|
1846
|
+
function sanitizeRelayIdentityEnvironment(env) {
|
|
1847
|
+
const selected = selectChromeDevtoolsRelayRuntimeEnvironment(env);
|
|
1848
|
+
if (selected.MCPORTER_CHROME_DEVTOOLS_RELAY_URL !== void 0) {
|
|
1849
|
+
const explicit = explicitRelayBaseUrl(selected);
|
|
1850
|
+
selected.MCPORTER_CHROME_DEVTOOLS_RELAY_URL = explicit.explicit ? explicit.url?.toString() ?? "invalid-endpoint" : "";
|
|
1851
|
+
}
|
|
1852
|
+
return selected;
|
|
1853
|
+
}
|
|
1854
|
+
function chromeDevtoolsRelayEnvironmentKeys(definitions, env = process.env) {
|
|
1855
|
+
const keys = /* @__PURE__ */ new Set();
|
|
1856
|
+
for (const definition of definitions) {
|
|
1857
|
+
if (definition.command.kind !== "stdio")
|
|
1858
|
+
continue;
|
|
1859
|
+
const commandValues = [definition.command.command, ...definition.command.args];
|
|
1860
|
+
const commandEnvironmentKeys = new Set(commandValues.flatMap(referencedEnvironmentVariables));
|
|
1861
|
+
const relevant = commandEnvironmentKeys.size > 0 || isChromeDevtoolsAutoConnectCommand(definition.command.command, definition.command.args) || isAmbiguousChromeDevtoolsAutoConnectCommand(definition.command.command, definition.command.args);
|
|
1862
|
+
if (!relevant) {
|
|
1863
|
+
continue;
|
|
1864
|
+
}
|
|
1865
|
+
for (const key of commandEnvironmentKeys)
|
|
1866
|
+
keys.add(`${RELAY_DEPENDENCY_SENTINEL}${key}`);
|
|
1867
|
+
const resolvedDefinitionEnv = resolveRelayIdentityEnvOverrides(definition.env, env);
|
|
1868
|
+
for (const key of CHROME_DEVTOOLS_RELAY_RUNTIME_ENV_KEYS) {
|
|
1869
|
+
const override = definition.env?.[key];
|
|
1870
|
+
if (!override)
|
|
1871
|
+
continue;
|
|
1872
|
+
for (const referenced of referencedEnvironmentVariables(override))
|
|
1873
|
+
keys.add(`${RELAY_DEPENDENCY_SENTINEL}${referenced}`);
|
|
1874
|
+
}
|
|
1875
|
+
const resolvedCommand = resolveCommandArgument(definition.command.command, env);
|
|
1876
|
+
const resolvedArgs = resolveCommandArguments(definition.command.args, env);
|
|
1877
|
+
const eligible = isChromeDevtoolsAutoConnectCommand(resolvedCommand, resolvedArgs);
|
|
1878
|
+
if (!eligible && !isAmbiguousChromeDevtoolsAutoConnectCommand(resolvedCommand, resolvedArgs))
|
|
1879
|
+
continue;
|
|
1880
|
+
const environment = sanitizeRelayIdentityEnvironment(resolvedDefinitionEnv);
|
|
1881
|
+
keys.add(`${RELAY_DISCOVERY_SENTINEL}${Buffer.from(JSON.stringify({
|
|
1882
|
+
server: definition.name,
|
|
1883
|
+
eligible,
|
|
1884
|
+
environment,
|
|
1885
|
+
configuredPolicy: definition.chromeDevtoolsRelay
|
|
1886
|
+
}), "utf8").toString("base64url")}`);
|
|
1887
|
+
}
|
|
1888
|
+
return [...keys].toSorted();
|
|
1889
|
+
}
|
|
1890
|
+
async function resolveChromeDevtoolsRelayRuntimeIdentity(keys, env = process.env, options = {}) {
|
|
1891
|
+
const { values, relays } = relayEnvironmentIdentityValues(keys, env);
|
|
1892
|
+
for (const relay of relays) {
|
|
1893
|
+
let endpoint = relay.endpoint;
|
|
1894
|
+
if (relay.discover && relay.keyId !== "missing-credential" && relay.keyId !== "invalid-credential") {
|
|
1895
|
+
endpoint = (await discoverRelayBaseUrl(relay.env, relay.keyId, relay.timeoutMs, relay.policy, options)).toString();
|
|
1896
|
+
}
|
|
1897
|
+
values.push([relay.server, endpoint]);
|
|
1898
|
+
}
|
|
1899
|
+
return hashRelayIdentityValues(values);
|
|
1900
|
+
}
|
|
1901
|
+
function createRelayCredentialIdResolver() {
|
|
1902
|
+
const credentialIds = /* @__PURE__ */ new Map();
|
|
1903
|
+
return (credentialPath) => {
|
|
1904
|
+
const cached = credentialIds.get(credentialPath);
|
|
1905
|
+
if (cached)
|
|
1906
|
+
return cached;
|
|
1907
|
+
const loaded = readCredentialPath(credentialPath);
|
|
1908
|
+
const value = loaded.credential?.keyId ?? loaded.reason ?? "missing-credential";
|
|
1909
|
+
loaded.credential?.key.fill(0);
|
|
1910
|
+
credentialIds.set(credentialPath, value);
|
|
1911
|
+
return value;
|
|
1912
|
+
};
|
|
1913
|
+
}
|
|
1914
|
+
function relayEnvironmentIdentityValues(keys, env) {
|
|
1915
|
+
const credentialId = createRelayCredentialIdResolver();
|
|
1916
|
+
const values = [];
|
|
1917
|
+
const relays = [];
|
|
1918
|
+
for (const key of keys) {
|
|
1919
|
+
if (key.startsWith(RELAY_DEPENDENCY_SENTINEL)) {
|
|
1920
|
+
values.push([key, env[key.slice(RELAY_DEPENDENCY_SENTINEL.length)] ?? null]);
|
|
1921
|
+
continue;
|
|
1922
|
+
}
|
|
1923
|
+
if (!key.startsWith(RELAY_DISCOVERY_SENTINEL)) {
|
|
1924
|
+
values.push([key, relayEnvironmentIdentityValue(key, env)]);
|
|
1925
|
+
continue;
|
|
1926
|
+
}
|
|
1927
|
+
const payload = parseRelayDiscoveryIdentity(key.slice(RELAY_DISCOVERY_SENTINEL.length));
|
|
1928
|
+
if (!payload)
|
|
1929
|
+
throw new Error("Invalid Chrome DevTools relay identity metadata");
|
|
1930
|
+
const effectiveEnv = { ...selectChromeDevtoolsRelayRuntimeEnvironment(env), ...payload.environment };
|
|
1931
|
+
const policy = resolveChromeDevtoolsRelayPolicy(payload.configuredPolicy, effectiveEnv);
|
|
1932
|
+
const timeoutMs = resolveChromeDevtoolsRelayProbeTimeoutMs(effectiveEnv);
|
|
1933
|
+
const explicit = explicitRelayBaseUrl(effectiveEnv);
|
|
1934
|
+
const credentialDir = resolveOpenClawCredentialDir(effectiveEnv);
|
|
1935
|
+
const keyId = payload.eligible ? credentialId(path2.join(credentialDir, "browser-extension-relay.secret")) : "not-eligible";
|
|
1936
|
+
const server = `$relay:${payload.server}`;
|
|
1937
|
+
values.push([
|
|
1938
|
+
server,
|
|
1939
|
+
JSON.stringify({
|
|
1940
|
+
policy,
|
|
1941
|
+
timeoutMs,
|
|
1942
|
+
eligible: payload.eligible,
|
|
1943
|
+
credentialDir,
|
|
1944
|
+
keyId,
|
|
1945
|
+
// These values also reach the discovery subprocess; retain inputs whose equivalence it does not guarantee.
|
|
1946
|
+
discovery: OPENCLAW_RELAY_DISCOVERY_ENV_KEYS.map((name) => [
|
|
1947
|
+
name,
|
|
1948
|
+
relayEnvironmentIdentityValue(name, effectiveEnv)
|
|
1949
|
+
]),
|
|
1950
|
+
cwd: relayIdentityDependsOnCwd(effectiveEnv) ? process.cwd() : null
|
|
1951
|
+
})
|
|
1952
|
+
]);
|
|
1953
|
+
relays.push({
|
|
1954
|
+
server,
|
|
1955
|
+
policy,
|
|
1956
|
+
env: effectiveEnv,
|
|
1957
|
+
timeoutMs,
|
|
1958
|
+
keyId,
|
|
1959
|
+
endpoint: policy === "off" ? "disabled" : explicit.explicit ? explicit.url?.toString() ?? "invalid-endpoint" : UNRESOLVED_RELAY_IDENTITY,
|
|
1960
|
+
discover: payload.eligible && policy !== "off" && !explicit.explicit
|
|
1961
|
+
});
|
|
1962
|
+
}
|
|
1963
|
+
if (keys.length > 0)
|
|
1964
|
+
values.push(["$relayAuth", "v2-effective-metadata-discovery"]);
|
|
1965
|
+
return { values, relays };
|
|
1966
|
+
}
|
|
1967
|
+
function relayEnvironmentIdentityValue(key, env) {
|
|
1968
|
+
switch (key) {
|
|
1969
|
+
case "MCPORTER_CHROME_DEVTOOLS_RELAY_POLICY":
|
|
1970
|
+
case "MCPORTER_DISABLE_CHROME_DEVTOOLS_RELAY":
|
|
1971
|
+
return resolveChromeDevtoolsRelayPolicy(void 0, env);
|
|
1972
|
+
case "MCPORTER_CHROME_DEVTOOLS_RELAY_TIMEOUT_MS":
|
|
1973
|
+
return resolveChromeDevtoolsRelayProbeTimeoutMs(env);
|
|
1974
|
+
case "MCPORTER_CHROME_DEVTOOLS_RELAY_URL": {
|
|
1975
|
+
const explicit = explicitRelayBaseUrl(env);
|
|
1976
|
+
return explicit.explicit ? explicit.url?.toString() ?? "invalid-endpoint" : UNRESOLVED_RELAY_IDENTITY;
|
|
1977
|
+
}
|
|
1978
|
+
case "OPENCLAW_PROFILE":
|
|
1979
|
+
return normalizeOpenClawProfile(env.OPENCLAW_PROFILE) ?? null;
|
|
1980
|
+
default:
|
|
1981
|
+
return env[key] ?? null;
|
|
1982
|
+
}
|
|
1983
|
+
}
|
|
1984
|
+
function relayIdentityDependsOnCwd(env) {
|
|
1985
|
+
const paths = [
|
|
1986
|
+
env.HOME,
|
|
1987
|
+
env.USERPROFILE,
|
|
1988
|
+
env.OPENCLAW_HOME,
|
|
1989
|
+
env.OPENCLAW_STATE_DIR,
|
|
1990
|
+
env.OPENCLAW_OAUTH_DIR,
|
|
1991
|
+
env.OPENCLAW_CONFIG_PATH,
|
|
1992
|
+
...(env.PATH ?? env.Path)?.split(path2.delimiter) ?? []
|
|
1993
|
+
];
|
|
1994
|
+
return paths.some((value) => value !== void 0 && !path2.isAbsolute(value) && !value.startsWith("~"));
|
|
1995
|
+
}
|
|
1996
|
+
function hashRelayIdentityValues(values) {
|
|
1997
|
+
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);
|
|
1998
|
+
}
|
|
1999
|
+
function parseRelayDiscoveryIdentity(encoded) {
|
|
2000
|
+
try {
|
|
2001
|
+
const value = JSON.parse(Buffer.from(encoded, "base64url").toString("utf8"));
|
|
2002
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
2003
|
+
return void 0;
|
|
2004
|
+
const candidate = value;
|
|
2005
|
+
if (typeof candidate.server !== "string" || typeof candidate.eligible !== "boolean" || !candidate.environment || typeof candidate.environment !== "object")
|
|
2006
|
+
return void 0;
|
|
2007
|
+
if (candidate.configuredPolicy !== void 0 && !["off", "prefer", "require"].includes(candidate.configuredPolicy))
|
|
2008
|
+
return void 0;
|
|
2009
|
+
const environment = {};
|
|
2010
|
+
for (const [key, raw] of Object.entries(candidate.environment)) {
|
|
2011
|
+
if (!CHROME_DEVTOOLS_RELAY_RUNTIME_ENV_KEYS.includes(key) || typeof raw !== "string")
|
|
2012
|
+
return void 0;
|
|
2013
|
+
environment[key] = raw;
|
|
2014
|
+
}
|
|
2015
|
+
return {
|
|
2016
|
+
server: candidate.server,
|
|
2017
|
+
eligible: candidate.eligible,
|
|
2018
|
+
environment,
|
|
2019
|
+
configuredPolicy: candidate.configuredPolicy
|
|
2020
|
+
};
|
|
2021
|
+
} catch {
|
|
2022
|
+
return void 0;
|
|
2023
|
+
}
|
|
2024
|
+
}
|
|
2025
|
+
function referencedEnvironmentVariables(value) {
|
|
2026
|
+
const names = /* @__PURE__ */ new Set();
|
|
2027
|
+
if (value.startsWith("$env:"))
|
|
2028
|
+
names.add(value.slice("$env:".length));
|
|
2029
|
+
for (const match of value.matchAll(/\$\{([A-Za-z_][A-Za-z0-9_]*)/gu)) {
|
|
2030
|
+
if (match[1])
|
|
2031
|
+
names.add(match[1]);
|
|
2032
|
+
}
|
|
2033
|
+
return [...names];
|
|
2034
|
+
}
|
|
2035
|
+
function resolveRelayDefinitionEnvOverrides(overrides, env) {
|
|
2036
|
+
const resolved = {};
|
|
2037
|
+
for (const [key, raw] of Object.entries(overrides ?? {})) {
|
|
2038
|
+
const value = resolveEnvValue(raw, env);
|
|
2039
|
+
if (value !== "")
|
|
2040
|
+
resolved[key] = value;
|
|
2041
|
+
}
|
|
2042
|
+
return resolved;
|
|
2043
|
+
}
|
|
2044
|
+
function resolveRelayIdentityEnvOverrides(overrides, env) {
|
|
2045
|
+
const relevant = Object.fromEntries(CHROME_DEVTOOLS_RELAY_RUNTIME_ENV_KEYS.flatMap((key) => {
|
|
2046
|
+
const value = overrides?.[key];
|
|
2047
|
+
return value === void 0 ? [] : [[key, value]];
|
|
2048
|
+
}));
|
|
2049
|
+
return resolveRelayDefinitionEnvOverrides(relevant, env);
|
|
2050
|
+
}
|
|
2051
|
+
function isErrno(error, code) {
|
|
2052
|
+
return Boolean(error && typeof error === "object" && error.code === code);
|
|
2053
|
+
}
|
|
2054
|
+
|
|
2055
|
+
export {
|
|
2056
|
+
isExistingChromeDevtoolsCommand,
|
|
2057
|
+
resolveChromeDevtoolsAutoConnectCommand,
|
|
2058
|
+
resolveCommandArgument,
|
|
2059
|
+
resolveCommandArguments,
|
|
2060
|
+
normalizeTimeout,
|
|
2061
|
+
raceWithTimeout,
|
|
2062
|
+
ChromeDevtoolsRelayRequiredError,
|
|
2063
|
+
ChromeDevtoolsRelayDiscoveryError,
|
|
2064
|
+
recordChromeDevtoolsRelayDecision,
|
|
2065
|
+
getChromeDevtoolsRelayDecision,
|
|
2066
|
+
formatChromeDevtoolsRelayDecision,
|
|
2067
|
+
resolveChromeDevtoolsRelayProbeTimeoutMs,
|
|
2068
|
+
resolveChromeDevtoolsRelayPolicy,
|
|
2069
|
+
rewriteChromeDevtoolsArgsForRelay,
|
|
2070
|
+
resolveChromeDevtoolsRelayEnvironment,
|
|
2071
|
+
chromeDevtoolsRelayEnvironmentKeys,
|
|
2072
|
+
resolveChromeDevtoolsRelayRuntimeIdentity
|
|
2073
|
+
};
|