opera-devtools-mcp 0.2.4 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (330) hide show
  1. package/README.md +108 -8
  2. package/build/src/McpContext.js +365 -365
  3. package/build/src/McpPage.js +475 -48
  4. package/build/src/McpResponse.js +462 -288
  5. package/build/src/SlimMcpResponse.js +1 -1
  6. package/build/src/TextSnapshot.js +101 -22
  7. package/build/src/ToolHandler.js +142 -20
  8. package/build/src/bin/check-latest-version.js +27 -1
  9. package/build/src/bin/chrome-devtools-cli-options.js +614 -10
  10. package/build/src/bin/chrome-devtools-mcp-cli-options.js +119 -32
  11. package/build/src/bin/chrome-devtools-mcp-main.js +50 -7
  12. package/build/src/bin/chrome-devtools-mcp.js +7 -4
  13. package/build/src/bin/chrome-devtools.js +57 -14
  14. package/build/src/bin/opera-devtools-cli-options.js +3 -829
  15. package/build/src/bin/opera-devtools-mcp-cli-options.js +3 -348
  16. package/build/src/bin/opera-devtools-mcp-main.js +3 -37
  17. package/build/src/bin/opera-devtools-mcp.js +4 -19
  18. package/build/src/bin/opera-devtools.js +4 -185
  19. package/build/src/browser.js +45 -5
  20. package/build/src/{PageCollector.js → collectors/PageCollector.js} +64 -133
  21. package/build/src/collectors/ServiceWorkerCollector.js +171 -0
  22. package/build/src/daemon/client.js +75 -17
  23. package/build/src/daemon/daemon.js +92 -30
  24. package/build/src/daemon/utils.js +24 -8
  25. package/build/src/{DevtoolsUtils.js → devtools/DevtoolsUtils.js} +94 -88
  26. package/build/src/devtools/McpHostBindingAdapter.js +165 -0
  27. package/build/src/{issue-descriptions.js → devtools/issueDescriptions.js} +8 -6
  28. package/build/src/formatters/ConsoleFormatter.js +44 -11
  29. package/build/src/formatters/HeapSnapshotFormatter.js +140 -9
  30. package/build/src/formatters/IssueFormatter.js +7 -7
  31. package/build/src/formatters/NetworkFormatter.js +10 -8
  32. package/build/src/index.js +101 -232
  33. package/build/src/opera/branding.js +34 -0
  34. package/build/src/opera/browserFlags.js +85 -0
  35. package/build/src/opera/browserLaunch.js +52 -0
  36. package/build/src/opera/policy.js +50 -0
  37. package/build/src/opera/serviceWorkerRetry.js +37 -0
  38. package/build/src/opera/toolHandlerHooks.js +39 -0
  39. package/build/src/{tools → opera/tools}/opera.js +116 -21
  40. package/build/src/processors/HeapSnapshotManager.js +258 -0
  41. package/build/src/{trace-processing/parse.js → processors/PerformanceTrace.js} +9 -9
  42. package/build/src/telemetry/ClearcutLogger.js +12 -119
  43. package/build/src/telemetry/WatchdogClient.js +5 -5
  44. package/build/src/telemetry/errors.js +1 -1
  45. package/build/src/telemetry/flagUtils.js +6 -6
  46. package/build/src/telemetry/{toolMetricsUtils.js → metricsRegistry.js} +4 -11
  47. package/build/src/telemetry/persistence.js +3 -3
  48. package/build/src/telemetry/transformation.js +193 -0
  49. package/build/src/telemetry/watchdog/ClearcutSender.js +11 -11
  50. package/build/src/telemetry/watchdog/main.js +6 -6
  51. package/build/src/third_party/THIRD_PARTY_NOTICES +232 -1061
  52. package/build/src/third_party/bundled-packages.json +7 -8
  53. package/build/src/third_party/devtools-formatter-worker.js +63 -22
  54. package/build/src/third_party/devtools-heap-snapshot-worker.js +809 -182
  55. package/build/src/third_party/index.js +69558 -74691
  56. package/build/src/third_party/issue-descriptions/CoepCorpNotSameOrigin.md +2 -2
  57. package/build/src/third_party/issue-descriptions/CoepCorpNotSameOriginAfterDefaultedToSameOriginByCoep.md +3 -3
  58. package/build/src/third_party/issue-descriptions/CoepCorpNotSameSite.md +2 -2
  59. package/build/src/third_party/issue-descriptions/CoepFrameResourceNeedsCoepHeader.md +1 -1
  60. package/build/src/third_party/issue-descriptions/CompatibilityModeQuirks.md +1 -1
  61. package/build/src/third_party/issue-descriptions/CookieAttributeValueExceedsMaxSize.md +2 -2
  62. package/build/src/third_party/issue-descriptions/LowTextContrast.md +1 -1
  63. package/build/src/third_party/issue-descriptions/SameSiteNoneInsecureErrorRead.md +1 -1
  64. package/build/src/third_party/issue-descriptions/SameSiteNoneInsecureErrorSet.md +1 -1
  65. package/build/src/third_party/issue-descriptions/SameSiteNoneInsecureWarnRead.md +1 -1
  66. package/build/src/third_party/issue-descriptions/SameSiteNoneInsecureWarnSet.md +1 -1
  67. package/build/src/third_party/issue-descriptions/SameSiteUnspecifiedLaxAllowUnsafeRead.md +3 -3
  68. package/build/src/third_party/issue-descriptions/SameSiteUnspecifiedLaxAllowUnsafeSet.md +3 -3
  69. package/build/src/third_party/issue-descriptions/bounceTrackingMitigations.md +1 -1
  70. package/build/src/third_party/issue-descriptions/connectionAllowlistEmbeddingRequirementNotSatisfied.md +26 -0
  71. package/build/src/third_party/issue-descriptions/connectionAllowlistIFrameAttributeLoosensEmbeddingRequirement.md +19 -0
  72. package/build/src/third_party/issue-descriptions/connectionAllowlistInvalidAllowConnectionAllowlistFrom.md +21 -0
  73. package/build/src/third_party/issue-descriptions/connectionAllowlistInvalidAllowlistItemType.md +2 -2
  74. package/build/src/third_party/issue-descriptions/connectionAllowlistInvalidHeader.md +2 -2
  75. package/build/src/third_party/issue-descriptions/connectionAllowlistInvalidUrlPattern.md +3 -3
  76. package/build/src/third_party/issue-descriptions/connectionAllowlistItemNotInnerList.md +2 -2
  77. package/build/src/third_party/issue-descriptions/connectionAllowlistMoreThanOneList.md +2 -2
  78. package/build/src/third_party/issue-descriptions/connectionAllowlistReportingEndpointNotToken.md +1 -1
  79. package/build/src/third_party/issue-descriptions/cookieCrossSiteRedirectDowngrade.md +3 -3
  80. package/build/src/third_party/issue-descriptions/cookieExcludeBlockedWithinRelatedWebsiteSet.md +1 -1
  81. package/build/src/third_party/issue-descriptions/cookieExcludePortMismatch.md +1 -1
  82. package/build/src/third_party/issue-descriptions/cookieExcludeSchemeMismatch.md +1 -1
  83. package/build/src/third_party/issue-descriptions/cookieExcludeThirdPartyPhaseoutSet.md +1 -1
  84. package/build/src/third_party/issue-descriptions/cookieWarnThirdPartyPhaseoutRead.md +1 -1
  85. package/build/src/third_party/issue-descriptions/corsAllowCredentialsRequired.md +1 -1
  86. package/build/src/third_party/issue-descriptions/corsDisabledScheme.md +2 -2
  87. package/build/src/third_party/issue-descriptions/corsDisallowedByMode.md +2 -2
  88. package/build/src/third_party/issue-descriptions/corsHeaderDisallowedByPreflightResponse.md +1 -1
  89. package/build/src/third_party/issue-descriptions/corsInvalidHeaderValues.md +3 -3
  90. package/build/src/third_party/issue-descriptions/corsLocalNetworkAccessPermissionDenied.md +1 -1
  91. package/build/src/third_party/issue-descriptions/corsMethodDisallowedByPreflightResponse.md +1 -1
  92. package/build/src/third_party/issue-descriptions/corsNoCorsRedirectModeNotFollow.md +1 -1
  93. package/build/src/third_party/issue-descriptions/corsOriginMismatch.md +3 -3
  94. package/build/src/third_party/issue-descriptions/corsPreflightResponseInvalid.md +2 -2
  95. package/build/src/third_party/issue-descriptions/corsRedirectContainsCredentials.md +3 -3
  96. package/build/src/third_party/issue-descriptions/corsWildcardOriginNotAllowed.md +4 -3
  97. package/build/src/third_party/issue-descriptions/cspEvalViolation.md +4 -4
  98. package/build/src/third_party/issue-descriptions/cspInlineViolation.md +2 -2
  99. package/build/src/third_party/issue-descriptions/cspTrustedTypesPolicyViolation.md +2 -2
  100. package/build/src/third_party/issue-descriptions/cspTrustedTypesSinkViolation.md +4 -4
  101. package/build/src/third_party/issue-descriptions/cspURLViolation.md +4 -4
  102. package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsEmptyList.md +1 -0
  103. package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsHttpNotFound.md +1 -0
  104. package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsInvalidContentType.md +1 -0
  105. package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsInvalidResponse.md +1 -0
  106. package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsNoResponse.md +1 -0
  107. package/build/src/third_party/issue-descriptions/emailVerificationRequestDnsFetchFailed.md +1 -0
  108. package/build/src/third_party/issue-descriptions/emailVerificationRequestDnsInvalidRecord.md +1 -0
  109. package/build/src/third_party/issue-descriptions/emailVerificationRequestEmailVerificationWellKnownHttpNotFound.md +1 -0
  110. package/build/src/third_party/issue-descriptions/emailVerificationRequestEmailVerificationWellKnownInvalidContentType.md +1 -0
  111. package/build/src/third_party/issue-descriptions/emailVerificationRequestEmailVerificationWellKnownInvalidResponse.md +1 -0
  112. package/build/src/third_party/issue-descriptions/emailVerificationRequestEmailVerificationWellKnownNoResponse.md +1 -0
  113. package/build/src/third_party/issue-descriptions/emailVerificationRequestInvalidEmail.md +1 -0
  114. package/build/src/third_party/issue-descriptions/emailVerificationRequestJwksHttpNotFound.md +1 -0
  115. package/build/src/third_party/issue-descriptions/emailVerificationRequestJwksInvalidResponse.md +1 -0
  116. package/build/src/third_party/issue-descriptions/emailVerificationRequestKeyBindingSigningFailed.md +1 -0
  117. package/build/src/third_party/issue-descriptions/emailVerificationRequestRpOriginIsOpaque.md +1 -0
  118. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenHttpNotFound.md +1 -0
  119. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenInvalidContentType.md +1 -0
  120. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenInvalidResponse.md +1 -0
  121. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenInvalidSdJwt.md +1 -0
  122. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenMalformedSdJwt.md +1 -0
  123. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenNoResponse.md +1 -0
  124. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidAudience.md +1 -0
  125. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidIssuedAt.md +1 -0
  126. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidNonce.md +1 -0
  127. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidSdHash.md +1 -0
  128. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidTyp.md +1 -0
  129. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingAud.md +1 -0
  130. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingCnf.md +1 -0
  131. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingIat.md +1 -0
  132. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingNonce.md +1 -0
  133. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingSdHash.md +1 -0
  134. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbSignatureFailed.md +1 -0
  135. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidEmail.md +1 -0
  136. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidEmailVerified.md +1 -0
  137. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidHolderKey.md +1 -0
  138. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidIssuedAt.md +1 -0
  139. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidIssuer.md +1 -0
  140. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtJwksMissingKeys.md +1 -0
  141. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtMissingCnf.md +1 -0
  142. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtMissingEmail.md +1 -0
  143. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtMissingIat.md +1 -0
  144. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtMissingIss.md +1 -0
  145. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtSignatureFailed.md +1 -0
  146. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtUnsupportedHeaderAlg.md +1 -0
  147. package/build/src/third_party/issue-descriptions/emailVerificationRequestUserLoggedOut.md +1 -0
  148. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownAccountsEndpointCrossOrigin.md +1 -0
  149. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownHttpNotFound.md +1 -0
  150. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownInvalidContentType.md +1 -0
  151. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownInvalidResponse.md +1 -0
  152. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownIssuanceEndpointCrossOrigin.md +1 -0
  153. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownListEmpty.md +1 -0
  154. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownMissingAccountsEndpoint.md +1 -0
  155. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownMissingIssuanceEndpoint.md +1 -0
  156. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownNoResponse.md +1 -0
  157. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownUnsupportedSigningAlgorithm.md +1 -0
  158. package/build/src/third_party/issue-descriptions/federatedAuthRequestAccountsHttpNotFound.md +1 -1
  159. package/build/src/third_party/issue-descriptions/federatedAuthRequestAccountsInvalidResponse.md +1 -1
  160. package/build/src/third_party/issue-descriptions/federatedAuthRequestAccountsNoResponse.md +1 -1
  161. package/build/src/third_party/issue-descriptions/federatedAuthRequestApprovalDeclined.md +1 -1
  162. package/build/src/third_party/issue-descriptions/federatedAuthRequestCanceled.md +1 -1
  163. package/build/src/third_party/issue-descriptions/federatedAuthRequestErrorFetchingSignin.md +1 -1
  164. package/build/src/third_party/issue-descriptions/federatedAuthRequestErrorIdToken.md +1 -1
  165. package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenHttpNotFound.md +1 -1
  166. package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenInvalidRequest.md +1 -1
  167. package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenInvalidResponse.md +1 -1
  168. package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenNoResponse.md +1 -1
  169. package/build/src/third_party/issue-descriptions/federatedAuthRequestInvalidSigninResponse.md +1 -1
  170. package/build/src/third_party/issue-descriptions/federatedAuthRequestManifestHttpNotFound.md +1 -1
  171. package/build/src/third_party/issue-descriptions/federatedAuthRequestManifestInvalidResponse.md +1 -1
  172. package/build/src/third_party/issue-descriptions/federatedAuthRequestManifestNoResponse.md +1 -1
  173. package/build/src/third_party/issue-descriptions/federatedAuthRequestTooManyRequests.md +1 -1
  174. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestInvalidAccountsResponse.md +1 -1
  175. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestInvalidConfigOrWellKnown.md +1 -1
  176. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNoAccountSharingPermission.md +1 -1
  177. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNoApiPermission.md +1 -1
  178. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNoReturningUserFromFetchedAccounts.md +1 -1
  179. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotIframe.md +1 -1
  180. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotPotentiallyTrustworthy.md +1 -1
  181. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotSameOrigin.md +1 -1
  182. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotSignedInWithIdp.md +1 -1
  183. package/build/src/third_party/issue-descriptions/fetchingPartitionedBlobURL.md +2 -2
  184. package/build/src/third_party/issue-descriptions/genericBackUINavigationWouldSkipAd.md +4 -0
  185. package/build/src/third_party/issue-descriptions/genericFormAriaLabelledByToNonExistingIdError.md +2 -2
  186. package/build/src/third_party/issue-descriptions/genericFormAutocompleteAttributeEmptyError.md +1 -1
  187. package/build/src/third_party/issue-descriptions/genericFormDuplicateIdForInputError.md +1 -1
  188. package/build/src/third_party/issue-descriptions/genericFormEmptyIdAndNameAttributesForInputError.md +2 -2
  189. package/build/src/third_party/issue-descriptions/genericFormInputAssignedAutocompleteValueToIdOrNameAttributeError.md +2 -2
  190. package/build/src/third_party/issue-descriptions/genericFormInputHasWrongButWellIntendedAutocompleteValueError.md +1 -1
  191. package/build/src/third_party/issue-descriptions/genericFormInputWithNoLabelError.md +1 -1
  192. package/build/src/third_party/issue-descriptions/genericFormLabelForMatchesNonExistingIdError.md +2 -2
  193. package/build/src/third_party/issue-descriptions/genericFormLabelForNameError.md +1 -1
  194. package/build/src/third_party/issue-descriptions/genericFormLabelHasNeitherForNorNestedInputError.md +1 -1
  195. package/build/src/third_party/issue-descriptions/genericFormModelContextMissingToolName.md +1 -1
  196. package/build/src/third_party/issue-descriptions/genericFormModelContextParameterMissingName.md +1 -1
  197. package/build/src/third_party/issue-descriptions/genericFormModelContextRequiredParameterMissingName.md +1 -1
  198. package/build/src/third_party/issue-descriptions/genericNavigationEntryMarkedSkippable.md +2 -2
  199. package/build/src/third_party/issue-descriptions/heavyAd.md +1 -1
  200. package/build/src/third_party/issue-descriptions/lazyLoadImageZeroSize.md +7 -0
  201. package/build/src/third_party/issue-descriptions/navigatingPartitionedBlobURL.md +3 -3
  202. package/build/src/third_party/issue-descriptions/permissionElementActivationDisabled.md +1 -1
  203. package/build/src/third_party/issue-descriptions/permissionElementActivationDisabledWithOccluder.md +1 -1
  204. package/build/src/third_party/issue-descriptions/permissionElementActivationDisabledWithOccluderParent.md +1 -1
  205. package/build/src/third_party/issue-descriptions/permissionElementFencedFrameDisallowed.md +2 -2
  206. package/build/src/third_party/issue-descriptions/permissionElementFontSizeTooLarge.md +2 -2
  207. package/build/src/third_party/issue-descriptions/permissionElementFontSizeTooSmall.md +2 -2
  208. package/build/src/third_party/issue-descriptions/permissionElementInsetBoxShadowUnsupported.md +1 -1
  209. package/build/src/third_party/issue-descriptions/permissionElementInvalidDisplayStyle.md +2 -2
  210. package/build/src/third_party/issue-descriptions/permissionElementInvalidSizeValue.md +2 -2
  211. package/build/src/third_party/issue-descriptions/permissionElementInvalidType.md +2 -2
  212. package/build/src/third_party/issue-descriptions/permissionElementInvalidTypeActivation.md +2 -2
  213. package/build/src/third_party/issue-descriptions/permissionElementNonOpaqueColor.md +1 -1
  214. package/build/src/third_party/issue-descriptions/permissionElementPaddingBottomUnsupported.md +2 -2
  215. package/build/src/third_party/issue-descriptions/permissionElementPaddingRightUnsupported.md +2 -2
  216. package/build/src/third_party/issue-descriptions/permissionElementRequestInProgress.md +2 -2
  217. package/build/src/third_party/issue-descriptions/permissionElementSecurityChecksFailed.md +1 -1
  218. package/build/src/third_party/issue-descriptions/permissionElementTypeNotSupported.md +2 -2
  219. package/build/src/third_party/issue-descriptions/permissionElementUntrustedEvent.md +1 -1
  220. package/build/src/third_party/issue-descriptions/placeholderDescriptionForInvisibleIssues.md +1 -1
  221. package/build/src/third_party/issue-descriptions/selectElementAccessibilityDisallowedOptGroupChild.md +1 -1
  222. package/build/src/third_party/issue-descriptions/selectElementAccessibilityDisallowedSelectChild.md +1 -1
  223. package/build/src/third_party/issue-descriptions/selectElementAccessibilityInteractiveContentAttributesSelectDescendant.md +1 -1
  224. package/build/src/third_party/issue-descriptions/selectElementAccessibilityInteractiveContentLegendChild.md +1 -1
  225. package/build/src/third_party/issue-descriptions/selectElementAccessibilityInteractiveContentOptionChild.md +1 -1
  226. package/build/src/third_party/issue-descriptions/selectElementAccessibilityNonPhrasingContentOptionChild.md +1 -1
  227. package/build/src/third_party/issue-descriptions/selectivePermissionsIntervention.md +3 -3
  228. package/build/src/third_party/issue-descriptions/sharedArrayBuffer.md +1 -1
  229. package/build/src/third_party/issue-descriptions/sharedDictionaryUseErrorCrossOriginNoCorsRequest.md +1 -1
  230. package/build/src/third_party/issue-descriptions/sharedDictionaryUseErrorMatchingDictionaryNotUsed.md +1 -1
  231. package/build/src/third_party/issue-descriptions/sharedDictionaryUseErrorUnexpectedContentDictionaryHeader.md +1 -1
  232. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorCossOriginNoCorsRequest.md +1 -1
  233. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorDisallowedBySettings.md +1 -1
  234. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorExpiredResponse.md +1 -1
  235. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorFeatureDisabled.md +2 -2
  236. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorInvalidStructuredHeader.md +1 -1
  237. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNavigationRequest.md +2 -2
  238. package/build/src/third_party/issue-descriptions/sriInvalidSignatureHeader.md +4 -4
  239. package/build/src/third_party/issue-descriptions/sriInvalidSignatureInputHeader.md +4 -4
  240. package/build/src/third_party/issue-descriptions/sriMissingSignatureHeader.md +1 -1
  241. package/build/src/third_party/issue-descriptions/sriMissingSignatureInputHeader.md +2 -2
  242. package/build/src/third_party/issue-descriptions/sriSignatureHeaderValueIsIncorrectLength.md +2 -2
  243. package/build/src/third_party/issue-descriptions/sriSignatureHeaderValueIsNotByteSequence.md +4 -4
  244. package/build/src/third_party/issue-descriptions/sriSignatureHeaderValueIsParameterized.md +4 -4
  245. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidComponentName.md +1 -1
  246. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidComponentType.md +2 -2
  247. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidDerivedComponentParameter.md +1 -1
  248. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidHeaderComponentParameter.md +1 -1
  249. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidParameter.md +2 -2
  250. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderKeyIdLength.md +5 -5
  251. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderMissingLabel.md +3 -3
  252. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderMissingRequiredParameters.md +2 -2
  253. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderValueMissingComponents.md +2 -2
  254. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderValueNotInnerList.md +2 -2
  255. package/build/src/third_party/issue-descriptions/sriValidationFailedIntegrityMismatch.md +4 -4
  256. package/build/src/third_party/issue-descriptions/sriValidationFailedInvalidLength.md +3 -3
  257. package/build/src/third_party/issue-descriptions/sriValidationFailedSignatureExpired.md +2 -2
  258. package/build/src/third_party/issue-descriptions/sriValidationFailedSignatureMismatch.md +2 -2
  259. package/build/src/third_party/issue-descriptions/stylesheetLateImport.md +1 -1
  260. package/build/src/third_party/issue-descriptions/summaryElementAccessibilityInteractiveContentSummaryDescendant.md +1 -1
  261. package/build/src/third_party/issue-descriptions/unencodedDigestIncorrectDigestLength.md +2 -2
  262. package/build/src/third_party/issue-descriptions/unencodedDigestIncorrectDigestType.md +3 -3
  263. package/build/src/third_party/issue-descriptions/unencodedDigestMalformedDictionary.md +2 -2
  264. package/build/src/third_party/issue-descriptions/unencodedDigestUnknownAlgorithm.md +1 -1
  265. package/build/src/third_party/lighthouse-devtools-mcp-bundle.js +3177 -6340
  266. package/build/src/tools/ToolDefinition.js +2 -2
  267. package/build/src/tools/categories.js +3 -0
  268. package/build/src/tools/console.js +13 -0
  269. package/build/src/tools/emulation.js +30 -3
  270. package/build/src/tools/extensions.js +8 -1
  271. package/build/src/tools/input.js +246 -128
  272. package/build/src/tools/lighthouse.js +14 -7
  273. package/build/src/tools/memory.js +289 -25
  274. package/build/src/tools/network.js +10 -7
  275. package/build/src/tools/pages.js +33 -92
  276. package/build/src/tools/performance.js +67 -52
  277. package/build/src/tools/pwa.js +128 -0
  278. package/build/src/tools/screencast.js +35 -12
  279. package/build/src/tools/screenshot.js +234 -75
  280. package/build/src/tools/script.js +130 -45
  281. package/build/src/tools/slim/tools.js +3 -0
  282. package/build/src/tools/snapshot.js +7 -4
  283. package/build/src/tools/thirdPartyDeveloper.js +19 -9
  284. package/build/src/tools/tools.js +3 -1
  285. package/build/src/tools/webmcp.js +3 -1
  286. package/build/src/utils/WaitForHelper.js +299 -0
  287. package/build/src/utils/check-for-updates.js +8 -2
  288. package/build/src/utils/files.js +42 -3
  289. package/build/src/utils/logger.js +51 -0
  290. package/build/src/utils/polyfill.js +11 -0
  291. package/build/src/utils/url.js +43 -0
  292. package/build/src/version.js +1 -4
  293. package/package.json +43 -33
  294. package/build/src/DevToolsConnectionAdapter.js +0 -70
  295. package/build/src/HeapSnapshotManager.js +0 -110
  296. package/build/src/Mutex.js +0 -38
  297. package/build/src/WaitForHelper.js +0 -195
  298. package/build/src/bin/cliDefinitions.js +0 -616
  299. package/build/src/logger.js +0 -37
  300. package/build/src/polyfill.js +0 -8
  301. package/build/src/telemetry/metricUtils.js +0 -15
  302. package/build/src/third_party/issue-descriptions/SameSiteExcludeContextDowngradeRead.md +0 -8
  303. package/build/src/third_party/issue-descriptions/SameSiteExcludeContextDowngradeSet.md +0 -8
  304. package/build/src/third_party/issue-descriptions/SameSiteExcludeNavigationContextDowngrade.md +0 -8
  305. package/build/src/third_party/issue-descriptions/SameSiteWarnCrossDowngradeRead.md +0 -8
  306. package/build/src/third_party/issue-descriptions/SameSiteWarnCrossDowngradeSet.md +0 -8
  307. package/build/src/third_party/issue-descriptions/SameSiteWarnStrictLaxDowngradeStrict.md +0 -8
  308. package/build/src/third_party/issue-descriptions/arInsecureContext.md +0 -7
  309. package/build/src/third_party/issue-descriptions/arInvalidInfoHeader.md +0 -5
  310. package/build/src/third_party/issue-descriptions/arInvalidRegisterOsSourceHeader.md +0 -5
  311. package/build/src/third_party/issue-descriptions/arInvalidRegisterOsTriggerHeader.md +0 -5
  312. package/build/src/third_party/issue-descriptions/arInvalidRegisterSourceHeader.md +0 -5
  313. package/build/src/third_party/issue-descriptions/arInvalidRegisterTriggerHeader.md +0 -5
  314. package/build/src/third_party/issue-descriptions/arNavigationRegistrationUniqueScopeAlreadySet.md +0 -5
  315. package/build/src/third_party/issue-descriptions/arNavigationRegistrationWithoutTransientUserActivation.md +0 -6
  316. package/build/src/third_party/issue-descriptions/arNoRegisterOsSourceHeader.md +0 -5
  317. package/build/src/third_party/issue-descriptions/arNoRegisterOsTriggerHeader.md +0 -5
  318. package/build/src/third_party/issue-descriptions/arNoRegisterSourceHeader.md +0 -5
  319. package/build/src/third_party/issue-descriptions/arNoRegisterTriggerHeader.md +0 -5
  320. package/build/src/third_party/issue-descriptions/arNoWebOrOsSupport.md +0 -4
  321. package/build/src/third_party/issue-descriptions/arOsSourceIgnored.md +0 -18
  322. package/build/src/third_party/issue-descriptions/arOsTriggerIgnored.md +0 -19
  323. package/build/src/third_party/issue-descriptions/arPermissionPolicyDisabled.md +0 -8
  324. package/build/src/third_party/issue-descriptions/arSourceAndTriggerHeaders.md +0 -9
  325. package/build/src/third_party/issue-descriptions/arSourceIgnored.md +0 -13
  326. package/build/src/third_party/issue-descriptions/arTriggerIgnored.md +0 -12
  327. package/build/src/third_party/issue-descriptions/arUntrustworthyReportingOrigin.md +0 -10
  328. package/build/src/third_party/issue-descriptions/arWebAndOsHeaders.md +0 -11
  329. package/build/src/utils/string.js +0 -37
  330. package/build/src/utils/types.js +0 -7
@@ -6,11 +6,13 @@
6
6
  import { spawn } from 'node:child_process';
7
7
  import fs from 'node:fs';
8
8
  import net from 'node:net';
9
- import { logger } from '../logger.js';
10
9
  import { PipeTransport } from '../third_party/index.js';
11
10
  import { getTempFilePath } from '../utils/files.js';
11
+ import { logger, puppeteerLogger } from '../utils/logger.js';
12
12
  import { DAEMON_SCRIPT_PATH, getSocketPath, getPidFilePath, isDaemonRunning, } from './utils.js';
13
13
  const FILE_TIMEOUT = 10_000;
14
+ const READY_CHECK_INTERVAL = 100;
15
+ const READY_CHECK_COMMAND_TIMEOUT = 1_000;
14
16
  /**
15
17
  * Waits for a file to be created and populated (removed = false) or removed (removed = true).
16
18
  */
@@ -48,16 +50,43 @@ function waitForFile(filePath, removed = false) {
48
50
  });
49
51
  });
50
52
  }
53
+ function delay(ms) {
54
+ return new Promise(resolve => {
55
+ setTimeout(resolve, ms);
56
+ });
57
+ }
58
+ async function waitForDaemonReady(sessionId) {
59
+ const deadline = Date.now() + FILE_TIMEOUT;
60
+ let lastError;
61
+ while (Date.now() < deadline) {
62
+ try {
63
+ const response = await sendCommand({ method: 'status' }, sessionId, READY_CHECK_COMMAND_TIMEOUT);
64
+ if (response.success) {
65
+ return;
66
+ }
67
+ lastError = new Error(String(response.error));
68
+ }
69
+ catch (error) {
70
+ lastError = error;
71
+ }
72
+ const timeLeft = deadline - Date.now();
73
+ if (timeLeft > 0) {
74
+ await delay(Math.min(READY_CHECK_INTERVAL, timeLeft));
75
+ }
76
+ }
77
+ throw new Error(`Timeout: daemon not ready within ${FILE_TIMEOUT}ms`, lastError === undefined ? undefined : { cause: lastError });
78
+ }
51
79
  export async function startDaemon(mcpArgs = [], sessionId) {
52
80
  if (isDaemonRunning(sessionId)) {
53
- logger('Daemon is already running');
81
+ logger?.('Daemon is already running');
82
+ await waitForDaemonReady(sessionId);
54
83
  return;
55
84
  }
56
85
  const pidFilePath = getPidFilePath(sessionId);
57
86
  if (fs.existsSync(pidFilePath)) {
58
87
  fs.unlinkSync(pidFilePath);
59
88
  }
60
- logger('Starting daemon...', ...mcpArgs);
89
+ logger?.('Starting daemon...', ...mcpArgs);
61
90
  const child = spawn(process.execPath, [DAEMON_SCRIPT_PATH, ...mcpArgs], {
62
91
  detached: true,
63
92
  stdio: 'ignore',
@@ -67,12 +96,17 @@ export async function startDaemon(mcpArgs = [], sessionId) {
67
96
  });
68
97
  child.unref();
69
98
  await waitForFile(pidFilePath);
99
+ await waitForDaemonReady(sessionId);
70
100
  }
71
101
  const SEND_COMMAND_TIMEOUT = 60_000; // ms
72
102
  /**
73
103
  * `sendCommand` opens a socket connection sends a single command and disconnects.
74
104
  */
75
- export async function sendCommand(command, sessionId) {
105
+ export async function sendCommand(command, sessionId, timeout = SEND_COMMAND_TIMEOUT) {
106
+ // Before connecting and sending, verify the daemon is still alive.
107
+ if (!isDaemonRunning(sessionId)) {
108
+ throw new Error('Daemon is not running.');
109
+ }
76
110
  const socketPath = getSocketPath(sessionId);
77
111
  const socket = net.createConnection({
78
112
  path: socketPath,
@@ -81,47 +115,60 @@ export async function sendCommand(command, sessionId) {
81
115
  const timer = setTimeout(() => {
82
116
  socket.destroy();
83
117
  reject(new Error('Timeout waiting for daemon response'));
84
- }, SEND_COMMAND_TIMEOUT);
85
- const transport = new PipeTransport(socket, socket);
118
+ }, timeout);
119
+ const transport = new PipeTransport(socket, socket, puppeteerLogger);
86
120
  transport.onmessage = async (message) => {
87
121
  clearTimeout(timer);
88
- logger('onmessage', message);
122
+ logger?.('onmessage', message);
89
123
  resolve(JSON.parse(message));
90
124
  };
91
125
  socket.on('error', error => {
92
126
  clearTimeout(timer);
93
- logger('Socket error:', error);
127
+ logger?.('Socket error:', error);
94
128
  reject(error);
95
129
  });
96
130
  socket.on('close', () => {
97
131
  clearTimeout(timer);
98
- logger('Socket closed:');
132
+ logger?.('Socket closed:');
99
133
  reject(new Error('Socket closed'));
100
134
  });
101
- logger('Sending message', command);
135
+ logger?.('Sending message', command);
102
136
  transport.send(JSON.stringify(command));
103
137
  });
104
138
  }
105
139
  export async function stopDaemon(sessionId) {
106
140
  if (!isDaemonRunning(sessionId)) {
107
- logger('Daemon is not running');
141
+ logger?.('Daemon is not running');
108
142
  return;
109
143
  }
110
144
  const pidFilePath = getPidFilePath(sessionId);
111
145
  await sendCommand({ method: 'stop' }, sessionId);
112
146
  await waitForFile(pidFilePath, /*removed=*/ true);
113
147
  }
148
+ export async function verifyDaemonVersion(sessionId, cliVersion) {
149
+ if (!isDaemonRunning(sessionId)) {
150
+ return undefined;
151
+ }
152
+ try {
153
+ const response = await sendCommand({ method: 'status' }, sessionId);
154
+ if (response.success) {
155
+ const data = JSON.parse(response.result);
156
+ if (data?.version && data.version !== cliVersion) {
157
+ return `Warning: Daemon server version (${data.version}) does not match CLI version (${cliVersion}). Run 'chrome-devtools start' to update and restart the daemon.`;
158
+ }
159
+ }
160
+ }
161
+ catch {
162
+ // Suppress communication failures during check; command execution handles unreachable daemon errors.
163
+ }
164
+ return undefined;
165
+ }
114
166
  export async function handleResponse(response, format) {
115
167
  if (response.isError) {
116
168
  return JSON.stringify(response.content);
117
169
  }
118
- if (format === 'json') {
119
- if (response.structuredContent) {
120
- return JSON.stringify(response.structuredContent);
121
- }
122
- // Fall-through to text for backward compatibility.
123
- }
124
170
  const chunks = [];
171
+ const images = [];
125
172
  for (const content of response.content) {
126
173
  if (content.type === 'text') {
127
174
  chunks.push(content.text);
@@ -143,12 +190,23 @@ export async function handleResponse(response, format) {
143
190
  const name = crypto.randomUUID();
144
191
  const filepath = await getTempFilePath(`${name}${extension}`);
145
192
  fs.writeFileSync(filepath, data);
193
+ images.push({ filePath: filepath, mimeType });
146
194
  chunks.push(`Saved to ${filepath}.`);
147
195
  }
148
196
  else {
149
197
  throw new Error('Not supported response content type');
150
198
  }
151
199
  }
200
+ if (format === 'json') {
201
+ if (response.structuredContent) {
202
+ const structuredContent = {
203
+ ...response.structuredContent,
204
+ ...(images.length ? { images } : {}),
205
+ };
206
+ return JSON.stringify(structuredContent);
207
+ }
208
+ // Fall-through to text for backward compatibility.
209
+ }
152
210
  return format === 'md' ? chunks.join(' ') : JSON.stringify(chunks);
153
211
  }
154
212
  //# sourceMappingURL=client.js.map
@@ -4,26 +4,84 @@
4
4
  * Copyright 2026 Google LLC
5
5
  * SPDX-License-Identifier: Apache-2.0
6
6
  */
7
- import fs from 'node:fs';
7
+ import fs, { constants, openSync, writeSync, closeSync } from 'node:fs';
8
8
  import { createServer } from 'node:net';
9
+ import os from 'node:os';
9
10
  import path from 'node:path';
10
11
  import process from 'node:process';
11
- import { logger } from '../logger.js';
12
12
  import { Client, PipeTransport, StdioClientTransport, } from '../third_party/index.js';
13
+ import { logger, puppeteerLogger } from '../utils/logger.js';
13
14
  import { VERSION } from '../version.js';
14
- import { DAEMON_CLIENT_NAME, getPidFilePath, getSocketPath, INDEX_SCRIPT_PATH, IS_WINDOWS, isDaemonRunning, } from './utils.js';
15
+ import { DAEMON_CLIENT_NAME, getPidFilePath, getSocketPath, INDEX_SCRIPT_PATH, IS_WINDOWS, isDaemonRunning, assertValidSessionId, } from './utils.js';
15
16
  const sessionId = process.env.CHROME_DEVTOOLS_MCP_SESSION_ID || '';
16
- logger(`Daemon sessionId: ${sessionId}`);
17
+ assertValidSessionId(sessionId);
18
+ logger?.(`Daemon sessionId: ${sessionId}`);
17
19
  if (isDaemonRunning(sessionId)) {
18
- logger('Another daemon process is running.');
20
+ logger?.('Another daemon process is running.');
19
21
  process.exit(1);
20
22
  }
21
23
  const pidFilePath = getPidFilePath(sessionId);
22
- fs.mkdirSync(path.dirname(pidFilePath), {
23
- recursive: true,
24
- });
25
- fs.writeFileSync(pidFilePath, process.pid.toString());
26
- logger(`Writing ${process.pid.toString()} to ${pidFilePath}`);
24
+ const pidDir = path.dirname(pidFilePath);
25
+ const currentUserUid = os.userInfo().uid;
26
+ try {
27
+ fs.mkdirSync(pidDir, { recursive: true, mode: 0o700 });
28
+ if (os.platform() !== 'win32') {
29
+ // POSIX specific checks
30
+ try {
31
+ const stats = fs.statSync(pidDir);
32
+ // 1. Check Ownership: Ensure the directory is owned by the current user.
33
+ if (stats.uid !== currentUserUid) {
34
+ console.error(`[MCP Daemon] Critical error: PID directory ${pidDir} is not owned by the current user (Expected: ${currentUserUid}, Found: ${stats.uid}). Possible tampering.`);
35
+ process.exit(1);
36
+ }
37
+ // 2. Check Permissions: Ensure the directory is not group or world-writable.
38
+ // Mode is a number, e.g., 0o700. We check if bits for group/world write are set.
39
+ const mode = stats.mode;
40
+ if (mode & constants.S_IWGRP || mode & constants.S_IWOTH) {
41
+ console.error(`[MCP Daemon] Critical error: PID directory ${pidDir} has insecure permissions (Mode: ${mode.toString(8)}). It should not be writable by group or others.`);
42
+ process.exit(1);
43
+ }
44
+ }
45
+ catch (statErr) {
46
+ console.error(`[MCP Daemon] Critical error stating PID directory ${pidDir}:`, statErr);
47
+ process.exit(1);
48
+ }
49
+ }
50
+ }
51
+ catch (err) {
52
+ console.error(`[MCP Daemon] Critical error creating/validating PID directory: ${pidDir}`, err);
53
+ process.exit(1);
54
+ }
55
+ let fd = -1;
56
+ try {
57
+ // Open the file with flags to:
58
+ // - O_WRONLY: Write-only
59
+ // - O_CREAT: Create if it doesn't exist
60
+ // - O_TRUNC: Truncate to zero length if it exists
61
+ // - O_NOFOLLOW: DO NOT follow symlinks.
62
+ // - 0o600: Permissions: read/write for owner, no permissions for others.
63
+ fd = openSync(pidFilePath, constants.O_WRONLY |
64
+ constants.O_CREAT |
65
+ constants.O_TRUNC |
66
+ constants.O_NOFOLLOW, 0o600);
67
+ writeSync(fd, process.pid.toString());
68
+ }
69
+ catch (err) {
70
+ console.error(`[MCP Daemon] Critical error writing PID file: ${pidFilePath}`, err);
71
+ // If openSync fails due to O_NOFOLLOW on a symlink, the error will be caught here.
72
+ process.exit(1);
73
+ }
74
+ finally {
75
+ if (fd !== -1) {
76
+ try {
77
+ closeSync(fd);
78
+ }
79
+ catch (err) {
80
+ console.error(`[MCP Daemon] Error closing PID file: ${pidFilePath}`, err);
81
+ }
82
+ }
83
+ }
84
+ logger?.(`Writing ${process.pid.toString()} to ${pidFilePath}`);
27
85
  const socketPath = getSocketPath(sessionId);
28
86
  const startDate = new Date();
29
87
  const mcpServerArgs = process.argv.slice(2);
@@ -76,15 +134,17 @@ async function handleRequest(msg) {
76
134
  };
77
135
  }
78
136
  else if (msg.method === 'status') {
137
+ await started;
138
+ const statusResult = {
139
+ pid: process.pid,
140
+ socketPath,
141
+ startDate: startDate.toISOString(),
142
+ version: VERSION,
143
+ args: mcpServerArgs,
144
+ };
79
145
  return {
80
146
  success: true,
81
- result: JSON.stringify({
82
- pid: process.pid,
83
- socketPath,
84
- startDate: startDate.toISOString(),
85
- version: VERSION,
86
- args: mcpServerArgs,
87
- }),
147
+ result: JSON.stringify(statusResult),
88
148
  };
89
149
  }
90
150
  {
@@ -114,15 +174,15 @@ async function startSocketServer() {
114
174
  }
115
175
  return await new Promise((resolve, reject) => {
116
176
  server = createServer(socket => {
117
- const transport = new PipeTransport(socket, socket);
177
+ const transport = new PipeTransport(socket, socket, puppeteerLogger);
118
178
  transport.onmessage = async (message) => {
119
- logger('onmessage', message);
179
+ logger?.('onmessage', message);
120
180
  const response = await handleRequest(JSON.parse(message));
121
181
  transport.send(JSON.stringify(response));
122
182
  socket.end();
123
183
  };
124
184
  socket.on('error', error => {
125
- logger('Socket error:', error);
185
+ logger?.('Socket error:', error);
126
186
  });
127
187
  });
128
188
  server.listen({
@@ -141,24 +201,24 @@ async function startSocketServer() {
141
201
  }
142
202
  });
143
203
  server.on('error', error => {
144
- logger('Server error:', error);
204
+ logger?.('Server error:', error);
145
205
  reject(error);
146
206
  });
147
207
  });
148
208
  }
149
- async function cleanup() {
209
+ async function cleanup(exitCode = 0) {
150
210
  console.log('Cleaning up daemon...');
151
211
  try {
152
212
  await mcpClient?.close();
153
213
  }
154
214
  catch (error) {
155
- logger('Error closing MCP client:', error);
215
+ logger?.('Error closing MCP client:', error);
156
216
  }
157
217
  try {
158
218
  await mcpTransport?.close();
159
219
  }
160
220
  catch (error) {
161
- logger('Error closing MCP transport:', error);
221
+ logger?.('Error closing MCP transport:', error);
162
222
  }
163
223
  if (server) {
164
224
  await new Promise(resolve => {
@@ -173,11 +233,11 @@ async function cleanup() {
173
233
  // ignore errors
174
234
  }
175
235
  }
176
- logger(`unlinking ${pidFilePath}`);
236
+ logger?.(`unlinking ${pidFilePath}`);
177
237
  if (fs.existsSync(pidFilePath)) {
178
238
  fs.unlinkSync(pidFilePath);
179
239
  }
180
- process.exit(0);
240
+ process.exit(exitCode);
181
241
  }
182
242
  // Handle shutdown signals
183
243
  process.on('SIGTERM', () => {
@@ -191,14 +251,16 @@ process.on('SIGHUP', () => {
191
251
  });
192
252
  // Handle uncaught errors
193
253
  process.on('uncaughtException', error => {
194
- logger('Uncaught exception:', error);
254
+ logger?.('Uncaught exception:', error);
255
+ void cleanup(1);
195
256
  });
196
257
  process.on('unhandledRejection', error => {
197
- logger('Unhandled rejection:', error);
258
+ logger?.('Unhandled rejection:', error);
259
+ void cleanup(1);
198
260
  });
199
261
  // Start the server
200
262
  const started = startSocketServer().catch(error => {
201
- logger('Failed to start daemon server:', error);
202
- process.exit(1);
263
+ logger?.('Failed to start daemon server:', error);
264
+ void cleanup(1);
203
265
  });
204
266
  //# sourceMappingURL=daemon.js.map
@@ -9,20 +9,32 @@ import fs from 'node:fs';
9
9
  import os from 'node:os';
10
10
  import path from 'node:path';
11
11
  import process from 'node:process';
12
- import { logger } from '../logger.js';
12
+ import { logger } from '../utils/logger.js';
13
+ import { INDEX_SCRIPT_NAME, MCP_BIN_NAME } from '../opera/branding.js';
13
14
  export const DAEMON_SCRIPT_PATH = path.join(import.meta.dirname, 'daemon.js');
14
- export const INDEX_SCRIPT_PATH = path.join(import.meta.dirname, '..', 'bin', 'opera-devtools-mcp.js');
15
- const APP_NAME = 'opera-devtools-mcp';
15
+ export const INDEX_SCRIPT_PATH = path.join(import.meta.dirname, '..', 'bin', INDEX_SCRIPT_NAME);
16
+ const APP_NAME = MCP_BIN_NAME;
16
17
  export const DAEMON_CLIENT_NAME = 'chrome-devtools-cli-daemon';
18
+ export function assertValidSessionId(sessionId) {
19
+ if (!sessionId) {
20
+ return;
21
+ }
22
+ if (!/^[a-fA-F0-9-]+$/.test(sessionId)) {
23
+ throw new Error(`Invalid sessionId: ${sessionId}`);
24
+ }
25
+ }
17
26
  // Using these paths due to strict limits on the POSIX socket path length.
18
27
  export function getSocketPath(sessionId) {
28
+ assertValidSessionId(sessionId);
19
29
  const uid = os.userInfo().uid;
30
+ const username = os.userInfo().username;
20
31
  const suffix = sessionId ? `-${sessionId}` : '';
21
32
  const appName = APP_NAME + suffix;
22
33
  if (IS_WINDOWS) {
23
34
  // Windows uses Named Pipes, not file paths.
24
35
  // This format is required for server.listen()
25
- return path.join('\\\\.\\pipe', appName, 'server.sock');
36
+ // Append username to prevent cross-user named pipe squatting
37
+ return path.join('\\\\.\\pipe', `${appName}-${username}`, 'server.sock');
26
38
  }
27
39
  // 1. Try XDG_RUNTIME_DIR (Linux standard, sometimes macOS)
28
40
  if (process.env.XDG_RUNTIME_DIR) {
@@ -34,6 +46,7 @@ export function getSocketPath(sessionId) {
34
46
  return path.join('/tmp', `${appName}-${uid}.sock`);
35
47
  }
36
48
  export function getRuntimeHome(sessionId) {
49
+ assertValidSessionId(sessionId);
37
50
  const platform = os.platform();
38
51
  const uid = os.userInfo().uid;
39
52
  const suffix = sessionId ? `-${sessionId}` : '';
@@ -52,19 +65,21 @@ export function getRuntimeHome(sessionId) {
52
65
  }
53
66
  export const IS_WINDOWS = os.platform() === 'win32';
54
67
  export function getPidFilePath(sessionId) {
68
+ assertValidSessionId(sessionId);
55
69
  const runtimeDir = getRuntimeHome(sessionId);
56
70
  return path.join(runtimeDir, 'daemon.pid');
57
71
  }
58
72
  export function getDaemonPid(sessionId) {
73
+ assertValidSessionId(sessionId);
59
74
  try {
60
75
  const pidFile = getPidFilePath(sessionId);
61
- logger(`Daemon pid file ${pidFile} sessionId=${sessionId}`);
76
+ logger?.(`Daemon pid file ${pidFile} sessionId=${sessionId}`);
62
77
  if (!fs.existsSync(pidFile)) {
63
78
  return null;
64
79
  }
65
80
  const pidContent = fs.readFileSync(pidFile, 'utf-8');
66
81
  const pid = parseInt(pidContent.trim(), 10);
67
- logger(`Daemon pid: ${pid}`);
82
+ logger?.(`Daemon pid: ${pid}`);
68
83
  if (isNaN(pid)) {
69
84
  return null;
70
85
  }
@@ -75,6 +90,7 @@ export function getDaemonPid(sessionId) {
75
90
  }
76
91
  }
77
92
  export function isDaemonRunning(sessionId) {
93
+ assertValidSessionId(sessionId);
78
94
  const pid = getDaemonPid(sessionId);
79
95
  if (pid) {
80
96
  try {
@@ -105,11 +121,11 @@ export function serializeArgs(options, argv) {
105
121
  }
106
122
  else if (Array.isArray(value)) {
107
123
  for (const item of value) {
108
- args.push(`--${kebabKey}`, String(item));
124
+ args.push(`--${kebabKey}=${String(item)}`);
109
125
  }
110
126
  }
111
127
  else {
112
- args.push(`--${kebabKey}`, String(value));
128
+ args.push(`--${kebabKey}=${String(value)}`);
113
129
  }
114
130
  }
115
131
  return args;