opera-devtools-mcp 0.2.3 → 0.3.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 +104 -7
  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 +573 -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 +9 -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 +184 -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 +3493 -0
  52. package/build/src/third_party/bundled-packages.json +9 -0
  53. package/build/src/third_party/devtools-formatter-worker.js +15340 -7
  54. package/build/src/third_party/devtools-heap-snapshot-worker.js +10337 -7
  55. package/build/src/third_party/index.js +179960 -26
  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 -1
  293. package/package.json +42 -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
@@ -0,0 +1,39 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Opera Norway AS. All rights reserved.
4
+ *
5
+ * This file is an original work developed by Opera.
6
+ */
7
+ import { ToolCategory } from '../tools/categories.js';
8
+ import { ensureBrowserFlagsForTool } from './browserFlags.js';
9
+ export function createOperaToolHooks(deps) {
10
+ return {
11
+ bypassMutex(tool) {
12
+ return tool.annotations.category === ToolCategory.OPERA;
13
+ },
14
+ async beforeInvoke(tool) {
15
+ await ensureBrowserFlagsForTool(tool.name, deps.serverArgs, deps.logFile, { resetContext: deps.resetContext });
16
+ },
17
+ makeLogCallback(extra) {
18
+ const sendNotification = extra?.sendNotification;
19
+ if (!sendNotification) {
20
+ return undefined;
21
+ }
22
+ return (message) => {
23
+ // `logger` carries the MCP request ID so the opera-cli bridge can route
24
+ // this chunk to the correct HTTP response (see bridge.ts requestLoggers).
25
+ // `data` stays a plain string so non-bridge MCP hosts (Claude Desktop,
26
+ // VS Code, etc.) continue to render it as readable text.
27
+ void sendNotification.call(extra, {
28
+ method: 'notifications/message',
29
+ params: {
30
+ level: 'info',
31
+ data: message,
32
+ logger: String(extra?.requestId),
33
+ },
34
+ });
35
+ };
36
+ },
37
+ };
38
+ }
39
+ //# sourceMappingURL=toolHandlerHooks.js.map
@@ -4,28 +4,11 @@
4
4
  *
5
5
  * This file is an original work developed by Opera.
6
6
  */
7
- import { zod } from '../third_party/index.js';
8
- import { ToolCategory } from './categories.js';
9
- import { definePageTool } from './ToolDefinition.js';
7
+ import { zod } from '../../third_party/index.js';
8
+ import { ToolCategory } from '../../tools/categories.js';
9
+ import { definePageTool } from '../../tools/ToolDefinition.js';
10
+ import { withServiceWorkerRetry } from '../serviceWorkerRetry.js';
10
11
  const getCDPSession = (page) => page._client();
11
- const MAX_SW_RETRIES = 5;
12
- const SW_RETRY_DELAY_MS = 2500;
13
- const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
14
- async function withServiceWorkerRetry(fn) {
15
- let lastError;
16
- for (let attempt = 0; attempt < MAX_SW_RETRIES; attempt++) {
17
- try {
18
- return await fn();
19
- }
20
- catch (e) {
21
- lastError = e;
22
- if (attempt < MAX_SW_RETRIES - 1) {
23
- await sleep(SW_RETRY_DELAY_MS);
24
- }
25
- }
26
- }
27
- throw lastError;
28
- }
29
12
  const dispatchAction = async (session, payload) => {
30
13
  const response = (await withServiceWorkerRetry(() => session.send('Opera.dispatchAction', { payload })));
31
14
  return response.result;
@@ -77,6 +60,7 @@ export const operaChat = definePageTool({
77
60
  name: 'opera_chat',
78
61
  description: "Send a chat prompt to Opera's built-in AI and return the response. Only available when connected to Opera Neon.",
79
62
  blockedByDialog: false,
63
+ verifyFilesSchema: {},
80
64
  annotations: {
81
65
  category: ToolCategory.OPERA,
82
66
  readOnlyHint: false,
@@ -111,6 +95,7 @@ export const operaDo = definePageTool({
111
95
  name: 'opera_do',
112
96
  description: "Instruct Opera's built-in AI to perform an action on the current page and return the result. Only available when connected to Opera Neon.",
113
97
  blockedByDialog: false,
98
+ verifyFilesSchema: {},
114
99
  annotations: {
115
100
  category: ToolCategory.OPERA,
116
101
  readOnlyHint: false,
@@ -142,6 +127,7 @@ export const operaMake = definePageTool({
142
127
  name: 'opera_make',
143
128
  description: "Ask Opera's built-in AI to create or generate content and return the result. Only available when connected to Opera Neon.",
144
129
  blockedByDialog: false,
130
+ verifyFilesSchema: {},
145
131
  annotations: {
146
132
  category: ToolCategory.OPERA,
147
133
  readOnlyHint: false,
@@ -168,6 +154,7 @@ export const operaResearch = definePageTool({
168
154
  name: 'opera_research',
169
155
  description: "Ask Opera's built-in AI to research a topic and return a summary. Only available when connected to Opera Neon.",
170
156
  blockedByDialog: false,
157
+ verifyFilesSchema: {},
171
158
  annotations: {
172
159
  category: ToolCategory.OPERA,
173
160
  readOnlyHint: false,
@@ -205,6 +192,7 @@ export const operaListModels = definePageTool({
205
192
  name: 'opera_list_models',
206
193
  description: 'List available AI models for Opera chat. Returns model IDs, display names, and which is the default. Only available when connected to Opera Neon.',
207
194
  blockedByDialog: false,
195
+ verifyFilesSchema: {},
208
196
  annotations: {
209
197
  category: ToolCategory.OPERA,
210
198
  readOnlyHint: true,
@@ -0,0 +1,258 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import fsSync from 'node:fs';
7
+ import path from 'node:path';
8
+ import { DevTools } from '../third_party/index.js';
9
+ import { createIdGenerator, stableIdSymbol, } from '../utils/id.js';
10
+ export class HeapSnapshotManager {
11
+ #snapshotIdGenerator = createIdGenerator();
12
+ #snapshots = new Map();
13
+ async getSnapshot(filePath) {
14
+ const absolutePath = path.resolve(filePath);
15
+ const cached = this.#snapshots.get(absolutePath);
16
+ if (cached) {
17
+ return cached.snapshot;
18
+ }
19
+ const uid = this.#snapshotIdGenerator();
20
+ const { snapshot, worker } = await this.#loadSnapshot(absolutePath, uid);
21
+ this.#snapshots.set(absolutePath, {
22
+ snapshot,
23
+ worker,
24
+ idToClassKey: [''],
25
+ classKeyToId: new Map(),
26
+ });
27
+ return snapshot;
28
+ }
29
+ async #applyNodeFilter(snapshot, filter, filterName, objectId) {
30
+ if (filterName === 'attributedToSpecificNativeContext') {
31
+ if (objectId === undefined) {
32
+ throw new Error('objectId is required when filterName is attributedToSpecificNativeContext');
33
+ }
34
+ const nodeIndex = await snapshot.nodeIndexForId(objectId);
35
+ if (nodeIndex === undefined) {
36
+ throw new Error(`Node with ID ${objectId} not found`);
37
+ }
38
+ filter.filterName = `nativeContext_${nodeIndex}`;
39
+ }
40
+ else if (filterName) {
41
+ filter.filterName = filterName;
42
+ }
43
+ }
44
+ async getAggregates(filePath, filterName, objectId) {
45
+ const snapshot = await this.getSnapshot(filePath);
46
+ const filter = new DevTools.HeapSnapshotModel.HeapSnapshotModel.NodeFilter();
47
+ await this.#applyNodeFilter(snapshot, filter, filterName, objectId);
48
+ const aggregates = await snapshot.aggregatesWithFilter(filter);
49
+ let objectCount = 0;
50
+ let totalSelfSize = 0;
51
+ for (const [key, aggregate] of Object.entries(aggregates)) {
52
+ const id = await this.getOrCreateIdForClassKey(filePath, key);
53
+ aggregate[stableIdSymbol] = id;
54
+ objectCount += aggregate.count;
55
+ totalSelfSize += aggregate.self;
56
+ }
57
+ return {
58
+ aggregates,
59
+ objectCount,
60
+ totalSelfSize,
61
+ };
62
+ }
63
+ async getStats(filePath) {
64
+ const snapshot = await this.getSnapshot(filePath);
65
+ return await snapshot.getStatistics();
66
+ }
67
+ async getStaticData(filePath) {
68
+ const snapshot = await this.getSnapshot(filePath);
69
+ return snapshot.staticData;
70
+ }
71
+ async getNativeContextSizes(filePath) {
72
+ const snapshot = await this.getSnapshot(filePath);
73
+ return await snapshot.getNativeContextSizes();
74
+ }
75
+ async getRetainedByContextSummary(filePath) {
76
+ const snapshot = await this.getSnapshot(filePath);
77
+ return await snapshot.getRetainedByContextSummary();
78
+ }
79
+ async getOrCreateIdForClassKey(filePath, classKey) {
80
+ const cached = this.#getCachedSnapshot(filePath);
81
+ let id = cached.classKeyToId.get(classKey);
82
+ if (!id) {
83
+ id = cached.idToClassKey.length;
84
+ cached.classKeyToId.set(classKey, id);
85
+ cached.idToClassKey.push(classKey);
86
+ }
87
+ return id;
88
+ }
89
+ async getNodesById(filePath, id, filterName, objectId) {
90
+ const snapshot = await this.getSnapshot(filePath);
91
+ const filter = new DevTools.HeapSnapshotModel.HeapSnapshotModel.NodeFilter();
92
+ await this.#applyNodeFilter(snapshot, filter, filterName, objectId);
93
+ const className = await this.resolveClassKeyFromId(filePath, id);
94
+ if (!className) {
95
+ throw new Error(`Class with ID ${id} not found in heap snapshot`);
96
+ }
97
+ const provider = snapshot.createNodesProviderForClass(className, filter);
98
+ return await provider.serializeItemsRange(0, Infinity);
99
+ }
100
+ async getRetainers(filePath, nodeId) {
101
+ const snapshot = await this.getSnapshot(filePath);
102
+ const nodeIndex = await snapshot.nodeIndexForId(nodeId);
103
+ if (nodeIndex === undefined) {
104
+ throw new Error(`Node with ID ${nodeId} not found`);
105
+ }
106
+ const provider = snapshot.createRetainingEdgesProvider(nodeIndex);
107
+ return await provider.serializeItemsRange(0, Infinity);
108
+ }
109
+ async getObjectInfo(filePath, nodeId) {
110
+ const snapshot = await this.getSnapshot(filePath);
111
+ const nodeIndex = await snapshot.nodeIndexForId(nodeId);
112
+ if (nodeIndex === undefined) {
113
+ throw new Error(`Node with ID ${nodeId} not found`);
114
+ }
115
+ return await snapshot.getObjectInfo(nodeIndex);
116
+ }
117
+ async getRetainingPaths(filePath, nodeId, maxDepth, maxNodes, maxSiblings) {
118
+ const snapshot = await this.getSnapshot(filePath);
119
+ const nodeIndex = await snapshot.nodeIndexForId(nodeId);
120
+ if (nodeIndex === undefined) {
121
+ throw new Error(`Node with ID ${nodeId} not found`);
122
+ }
123
+ return await snapshot.getRetainingPaths(nodeIndex, maxDepth, maxNodes, maxSiblings);
124
+ }
125
+ async getDominatorsOf(filePath, nodeId) {
126
+ const snapshot = await this.getSnapshot(filePath);
127
+ const nodeIndex = await snapshot.nodeIndexForId(nodeId);
128
+ if (nodeIndex === undefined) {
129
+ throw new Error(`Node with ID ${nodeId} not found`);
130
+ }
131
+ return await snapshot.getDominatorsOf(nodeIndex);
132
+ }
133
+ async getEdges(filePath, nodeId, options) {
134
+ const snapshot = await this.getSnapshot(filePath);
135
+ const nodeIndex = await snapshot.nodeIndexForId(nodeId);
136
+ if (nodeIndex === undefined) {
137
+ throw new Error(`Node with ID ${nodeId} not found`);
138
+ }
139
+ const provider = snapshot.createEdgesProvider(nodeIndex, options);
140
+ return await provider.serializeItemsRange(0, Infinity);
141
+ }
142
+ async getClassDiffs(baseFilePath, currentFilePath) {
143
+ const rawDiffs = await this.#getSortedRawClassDiffs(baseFilePath, currentFilePath);
144
+ return rawDiffs.map(rawDiff => ({
145
+ className: rawDiff.name,
146
+ addedCount: rawDiff.addedCount,
147
+ removedCount: rawDiff.removedCount,
148
+ countDelta: rawDiff.countDelta,
149
+ addedSize: rawDiff.addedSize,
150
+ removedSize: rawDiff.removedSize,
151
+ sizeDelta: rawDiff.sizeDelta,
152
+ }));
153
+ }
154
+ async getDetailedClassDiff(baseFilePath, currentFilePath, classIndex) {
155
+ const classDiffs = await this.#getSortedRawClassDiffs(baseFilePath, currentFilePath);
156
+ const rawDiff = classDiffs[classIndex];
157
+ if (!rawDiff) {
158
+ throw new Error(`Invalid classIndex: ${classIndex}. Total classes with changes: ${classDiffs.length}`);
159
+ }
160
+ return {
161
+ className: rawDiff.name,
162
+ addedCount: rawDiff.addedCount,
163
+ removedCount: rawDiff.removedCount,
164
+ countDelta: rawDiff.countDelta,
165
+ addedSize: rawDiff.addedSize,
166
+ removedSize: rawDiff.removedSize,
167
+ sizeDelta: rawDiff.sizeDelta,
168
+ addedIds: rawDiff.addedIds ?? [],
169
+ addedSelfSizes: rawDiff.addedSelfSizes ?? [],
170
+ deletedIds: rawDiff.deletedIds ?? [],
171
+ deletedSelfSizes: rawDiff.deletedSelfSizes ?? [],
172
+ };
173
+ }
174
+ #getCachedSnapshot(filePath) {
175
+ const absolutePath = path.resolve(filePath);
176
+ const cached = this.#snapshots.get(absolutePath);
177
+ if (!cached) {
178
+ throw new Error(`Snapshot not loaded for ${filePath}`);
179
+ }
180
+ return cached;
181
+ }
182
+ async #getSortedRawClassDiffs(baseFilePath, currentFilePath) {
183
+ const baseSnapshot = await this.getSnapshot(baseFilePath);
184
+ const currentSnapshot = await this.getSnapshot(currentFilePath);
185
+ const interfaceDefinitions = await currentSnapshot.interfaceDefinitions();
186
+ const aggregatesForDiff = await baseSnapshot.aggregatesForDiff(interfaceDefinitions);
187
+ const baseSnapshotId = baseSnapshot.uid;
188
+ if (baseSnapshotId === undefined) {
189
+ throw new Error('Base snapshot UID is undefined');
190
+ }
191
+ // DevTools calculateSnapshotDiff uses the first parameter (baseSnapshotId)
192
+ // as a cache key. We pass the unique UID of the base snapshot.
193
+ const rawDiffs = await currentSnapshot.calculateSnapshotDiff(baseSnapshotId, aggregatesForDiff);
194
+ // Return a filtered and sorted array here to ensure that
195
+ // compare_heapsnapshot_summary and compare_heapsnapshot_details agree
196
+ // on indices.
197
+ return Object.values(rawDiffs)
198
+ .filter(diff => diff.addedCount > 0 || diff.removedCount > 0)
199
+ .sort((a, b) => b.sizeDelta - a.sizeDelta);
200
+ }
201
+ async resolveClassKeyFromId(filePath, id) {
202
+ const cached = this.#getCachedSnapshot(filePath);
203
+ return cached.idToClassKey[id];
204
+ }
205
+ async #loadSnapshot(absolutePath, uid) {
206
+ const workerProxy = new DevTools.HeapSnapshotModel.HeapSnapshotProxy.HeapSnapshotWorkerProxy(() => {
207
+ /* noop */
208
+ }, DevTools.Common.Console.Console.instance(), import.meta.resolve('../third_party/devtools-heap-snapshot-worker.js'));
209
+ try {
210
+ const { promise: snapshotPromise, resolve: resolveSnapshot } = Promise.withResolvers();
211
+ const loaderProxy = workerProxy.createLoader(uid, snapshotProxy => {
212
+ resolveSnapshot(snapshotProxy);
213
+ });
214
+ const fileStream = fsSync.createReadStream(absolutePath, {
215
+ encoding: 'utf-8',
216
+ highWaterMark: 1024 * 1024,
217
+ });
218
+ for await (const chunk of fileStream) {
219
+ await loaderProxy.write(chunk);
220
+ }
221
+ await loaderProxy.close();
222
+ const snapshot = await snapshotPromise;
223
+ return { snapshot, worker: workerProxy };
224
+ }
225
+ catch (error) {
226
+ // The worker is created before the read, and a failed load never reaches
227
+ // the #snapshots map, so dispose()/disposeAll() can never clean it up.
228
+ // Dispose it here to avoid leaking a worker on every failed load (e.g. a
229
+ // missing or invalid .heapsnapshot path).
230
+ workerProxy.dispose();
231
+ throw error;
232
+ }
233
+ }
234
+ async getDuplicateStrings(filePath) {
235
+ const snapshot = await this.getSnapshot(filePath);
236
+ return await snapshot.getDuplicateStrings();
237
+ }
238
+ hasSnapshots() {
239
+ return this.#snapshots.size > 0;
240
+ }
241
+ disposeSnapshot(filePath) {
242
+ const absolutePath = path.resolve(filePath);
243
+ const cached = this.#snapshots.get(absolutePath);
244
+ if (cached) {
245
+ cached.worker.dispose();
246
+ this.#snapshots.delete(absolutePath);
247
+ return true;
248
+ }
249
+ return false;
250
+ }
251
+ dispose() {
252
+ for (const cached of this.#snapshots.values()) {
253
+ cached.worker.dispose();
254
+ }
255
+ this.#snapshots.clear();
256
+ }
257
+ }
258
+ //# sourceMappingURL=HeapSnapshotManager.js.map
@@ -3,13 +3,13 @@
3
3
  * Copyright 2025 Google LLC
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
- import { logger } from '../logger.js';
7
6
  import { DevTools } from '../third_party/index.js';
7
+ import { logger } from '../utils/logger.js';
8
8
  const engine = DevTools.TraceEngine.TraceModel.Model.createWithAllHandlers();
9
9
  export function traceResultIsSuccess(x) {
10
10
  return 'parsedTrace' in x;
11
11
  }
12
- export async function parseRawTraceBuffer(buffer) {
12
+ export async function parseRawTraceBuffer(buffer, metadata) {
13
13
  engine.resetProcessor();
14
14
  if (!buffer) {
15
15
  return {
@@ -25,7 +25,7 @@ export async function parseRawTraceBuffer(buffer) {
25
25
  try {
26
26
  const data = JSON.parse(asString);
27
27
  const events = Array.isArray(data) ? data : data.traceEvents;
28
- await engine.parse(events);
28
+ await engine.parse(events, { metadata });
29
29
  const parsedTrace = engine.parsedTrace();
30
30
  if (!parsedTrace) {
31
31
  return {
@@ -40,7 +40,7 @@ export async function parseRawTraceBuffer(buffer) {
40
40
  }
41
41
  catch (e) {
42
42
  const errorText = e instanceof Error ? e.message : JSON.stringify(e);
43
- logger(`Unexpected error parsing trace: ${errorText}`);
43
+ logger?.(`Unexpected error parsing trace: ${errorText}`);
44
44
  return {
45
45
  error: errorText,
46
46
  };
@@ -51,9 +51,9 @@ const extraFormatDescriptions = `Information on performance traces may contain m
51
51
  ${DevTools.PerformanceTraceFormatter.callFrameDataFormatDescription}
52
52
 
53
53
  ${DevTools.PerformanceTraceFormatter.networkDataFormatDescription}`;
54
- export function getTraceSummary(result) {
54
+ export function getTraceSummary(result, deviceScope) {
55
55
  const focus = DevTools.AgentFocus.fromParsedTrace(result.parsedTrace);
56
- const formatter = new DevTools.PerformanceTraceFormatter(focus);
56
+ const formatter = new DevTools.PerformanceTraceFormatter(focus, deviceScope);
57
57
  const summaryText = formatter.formatTraceSummary();
58
58
  return `## Summary of Performance trace findings:
59
59
  ${summaryText}
@@ -61,7 +61,7 @@ ${summaryText}
61
61
  ## Details on call tree & network request formats:
62
62
  ${extraFormatDescriptions}`;
63
63
  }
64
- export function getInsightOutput(result, insightSetId, insightName) {
64
+ export function getInsightOutput(result, insightSetId, insightName, deviceScope) {
65
65
  if (!result.insights) {
66
66
  return {
67
67
  error: 'No Performance insights are available for this trace.',
@@ -79,7 +79,7 @@ export function getInsightOutput(result, insightSetId, insightName) {
79
79
  error: `No Insight with the name ${insightName} found. Double check the name you provided is accurate and try again.`,
80
80
  };
81
81
  }
82
- const formatter = new DevTools.PerformanceInsightFormatter(DevTools.AgentFocus.fromParsedTrace(result.parsedTrace), matchingInsight);
82
+ const formatter = new DevTools.PerformanceInsightFormatter(DevTools.AgentFocus.fromParsedTrace(result.parsedTrace), matchingInsight, deviceScope);
83
83
  return { output: formatter.formatInsight() };
84
84
  }
85
- //# sourceMappingURL=parse.js.map
85
+ //# sourceMappingURL=PerformanceTrace.js.map
@@ -5,124 +5,11 @@
5
5
  */
6
6
  import process from 'node:process';
7
7
  import { DAEMON_CLIENT_NAME } from '../daemon/utils.js';
8
- import { logger } from '../logger.js';
8
+ import { logger } from '../utils/logger.js';
9
+ import { sanitizeParams, stripUnderscoreBeforeNumber } from './transformation.js';
9
10
  import { McpClient, WatchdogMessageType, OsType, } from './types.js';
10
11
  import { WatchdogClient } from './WatchdogClient.js';
11
12
  const MS_PER_DAY = 24 * 60 * 60 * 1000;
12
- export const PARAM_BLOCKLIST = new Set(['uid', 'reqid', 'msgid']);
13
- const SUPPORTED_ZOD_TYPES = [
14
- 'ZodString',
15
- 'ZodNumber',
16
- 'ZodBoolean',
17
- 'ZodArray',
18
- 'ZodEnum',
19
- ];
20
- function isZodType(type) {
21
- return SUPPORTED_ZOD_TYPES.includes(type);
22
- }
23
- export function getZodType(zodType) {
24
- const def = zodType._def;
25
- const typeName = def.typeName;
26
- if (typeName === 'ZodOptional' ||
27
- typeName === 'ZodDefault' ||
28
- typeName === 'ZodNullable') {
29
- return getZodType(def.innerType);
30
- }
31
- if (typeName === 'ZodEffects') {
32
- return getZodType(def.schema);
33
- }
34
- if (isZodType(typeName)) {
35
- return typeName;
36
- }
37
- throw new Error(`Unsupported zod type for tool parameter: ${typeName}`);
38
- }
39
- export function transformArgName(zodType, name) {
40
- const snakeCaseName = name.replace(/[A-Z]/g, letter => `_${letter.toLowerCase()}`);
41
- if (zodType === 'ZodString') {
42
- return `${snakeCaseName}_length`;
43
- }
44
- else if (zodType === 'ZodArray') {
45
- return `${snakeCaseName}_count`;
46
- }
47
- else {
48
- return snakeCaseName;
49
- }
50
- }
51
- export function transformArgType(zodType) {
52
- if (zodType === 'ZodString' || zodType === 'ZodArray') {
53
- return 'number';
54
- }
55
- switch (zodType) {
56
- case 'ZodNumber':
57
- return 'number';
58
- case 'ZodBoolean':
59
- return 'boolean';
60
- case 'ZodEnum':
61
- return 'enum';
62
- default:
63
- throw new Error(`Unsupported zod type for tool parameter: ${zodType}`);
64
- }
65
- }
66
- const BUCKETS = [
67
- 0, 1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000,
68
- ];
69
- function bucketize(value) {
70
- for (const bucket of BUCKETS) {
71
- if (bucket >= value) {
72
- return bucket;
73
- }
74
- }
75
- return BUCKETS[BUCKETS.length - 1];
76
- }
77
- function transformValue(zodType, value) {
78
- if (zodType === 'ZodString') {
79
- return bucketize(value.length);
80
- }
81
- else if (zodType === 'ZodArray') {
82
- return value.length;
83
- }
84
- else {
85
- return value;
86
- }
87
- }
88
- function hasEquivalentType(zodType, value) {
89
- if (zodType === 'ZodString') {
90
- return typeof value === 'string';
91
- }
92
- else if (zodType === 'ZodArray') {
93
- return Array.isArray(value);
94
- }
95
- else if (zodType === 'ZodNumber') {
96
- return typeof value === 'number';
97
- }
98
- else if (zodType === 'ZodBoolean') {
99
- return typeof value === 'boolean';
100
- }
101
- else if (zodType === 'ZodEnum') {
102
- return (typeof value === 'string' ||
103
- typeof value === 'number' ||
104
- typeof value === 'boolean');
105
- }
106
- else {
107
- return false;
108
- }
109
- }
110
- export function sanitizeParams(params, schema) {
111
- const transformed = {};
112
- for (const [name, value] of Object.entries(params)) {
113
- if (PARAM_BLOCKLIST.has(name)) {
114
- continue;
115
- }
116
- const zodType = getZodType(schema[name]);
117
- if (!hasEquivalentType(zodType, value)) {
118
- throw new Error(`parameter ${name} has type ${zodType} but value ${value} is not of equivalent type`);
119
- }
120
- const transformedName = transformArgName(zodType, name);
121
- const transformedValue = transformValue(zodType, value);
122
- transformed[transformedName] = transformedValue;
123
- }
124
- return transformed;
125
- }
126
13
  function detectOsType() {
127
14
  switch (process.platform) {
128
15
  case 'win32':
@@ -194,14 +81,18 @@ export class ClearcutLogger {
194
81
  }
195
82
  }
196
83
  async logToolInvocation(args) {
84
+ const sanitizedToolName = stripUnderscoreBeforeNumber(args.toolName);
197
85
  const tool_invocation = {
198
- tool_name: args.toolName,
86
+ tool_name: sanitizedToolName,
199
87
  success: args.success,
200
88
  latency_ms: args.latencyMs,
201
89
  };
90
+ if (Object.keys(args.context).length > 0) {
91
+ tool_invocation.context = args.context;
92
+ }
202
93
  if (Object.keys(args.params).length > 0) {
203
94
  tool_invocation.tool_params = {
204
- [`${args.toolName}_params`]: sanitizeParams(args.params, args.schema),
95
+ [`${sanitizedToolName}_params`]: sanitizeParams(args.params, args.schema),
205
96
  };
206
97
  }
207
98
  this.#watchdog.send({
@@ -248,7 +139,7 @@ export class ClearcutLogger {
248
139
  }
249
140
  }
250
141
  catch (err) {
251
- logger('Error in logDailyActiveIfNeeded:', err);
142
+ logger?.('Error in logDailyActiveIfNeeded:', err);
252
143
  }
253
144
  }
254
145
  async logServerError(args) {
@@ -257,7 +148,9 @@ export class ClearcutLogger {
257
148
  payload: {
258
149
  mcp_client: this.#mcpClient,
259
150
  server_error: {
260
- tool_name: args.toolName ?? '',
151
+ tool_name: args.toolName
152
+ ? stripUnderscoreBeforeNumber(args.toolName)
153
+ : '',
261
154
  error_code: args.errorCode,
262
155
  },
263
156
  },
@@ -5,7 +5,7 @@
5
5
  */
6
6
  import { spawn } from 'node:child_process';
7
7
  import { fileURLToPath } from 'node:url';
8
- import { logger } from '../logger.js';
8
+ import { logger } from '../utils/logger.js';
9
9
  export class WatchdogClient {
10
10
  #childProcess;
11
11
  constructor(config, options) {
@@ -35,10 +35,10 @@ export class WatchdogClient {
35
35
  });
36
36
  this.#childProcess.unref();
37
37
  this.#childProcess.on('error', err => {
38
- logger('Watchdog process error:', err);
38
+ logger?.('Watchdog process error:', err);
39
39
  });
40
40
  this.#childProcess.on('exit', (code, signal) => {
41
- logger(`Watchdog exited with code ${code} and signal ${signal}`);
41
+ logger?.(`Watchdog exited with code ${code} and signal ${signal}`);
42
42
  });
43
43
  }
44
44
  send(message) {
@@ -50,11 +50,11 @@ export class WatchdogClient {
50
50
  this.#childProcess.stdin.write(line);
51
51
  }
52
52
  catch (err) {
53
- logger('Failed to write to watchdog stdin', err);
53
+ logger?.('Failed to write to watchdog stdin', err);
54
54
  }
55
55
  }
56
56
  else {
57
- logger('Watchdog stdin not available, dropping message');
57
+ logger?.('Watchdog stdin not available, dropping message');
58
58
  }
59
59
  }
60
60
  }
@@ -13,6 +13,6 @@ export var ErrorCode;
13
13
  (function (ErrorCode) {
14
14
  ErrorCode[ErrorCode["ERROR_CODE_UNSPECIFIED"] = 0] = "ERROR_CODE_UNSPECIFIED";
15
15
  ErrorCode[ErrorCode["ERROR_CODE_PERSISTENCE_FILE_READ_FAILED"] = 1] = "ERROR_CODE_PERSISTENCE_FILE_READ_FAILED";
16
- ErrorCode[ErrorCode["ERROR_CODE_PERSISTENCE_FILE_SAVE_FAILED"] = 3] = "ERROR_CODE_PERSISTENCE_FILE_SAVE_FAILED";
16
+ ErrorCode[ErrorCode["ERROR_CODE_PERSISTENCE_FILE_SAVE_FAILED"] = 2] = "ERROR_CODE_PERSISTENCE_FILE_SAVE_FAILED";
17
17
  })(ErrorCode || (ErrorCode = {}));
18
18
  //# sourceMappingURL=errors.js.map
@@ -2,17 +2,17 @@
2
2
  * @license
3
3
  * Copyright 2026 Google LLC
4
4
  * SPDX-License-Identifier: Apache-2.0
5
- *
6
- * Modified by Opera Software AS.
7
5
  */
8
- import { toSnakeCase } from '../utils/string.js';
6
+ import { DevTools } from '../third_party/index.js';
7
+ import { stripUnderscoreBeforeNumber } from './transformation.js';
8
+ const { StringUtilities } = DevTools.Platform;
9
9
  /**
10
10
  * For enums, log the value as uppercase.
11
11
  * We're going to have an enum for such flags with choices represented
12
12
  * as an `enum` where the keys of the enum will map to the uppercase `choice`.
13
13
  */
14
14
  function formatEnumChoice(snakeCaseName, choice) {
15
- return `${snakeCaseName}_${choice}`.toUpperCase();
15
+ return stripUnderscoreBeforeNumber(`${snakeCaseName}_${choice}`).toUpperCase();
16
16
  }
17
17
  /**
18
18
  * Computes telemetry flag usage from parsed arguments and CLI options.
@@ -31,7 +31,7 @@ export function computeFlagUsage(args, options) {
31
31
  const usage = {};
32
32
  for (const [flagName, config] of Object.entries(options)) {
33
33
  const value = args[flagName];
34
- const snakeCaseName = toSnakeCase(flagName);
34
+ const snakeCaseName = stripUnderscoreBeforeNumber(StringUtilities.toSnakeCase(flagName));
35
35
  // If there isn't a default value provided for the flag,
36
36
  // we're going to log whether it's present on the args user
37
37
  // provided or not. If there is a default value, we only log presence
@@ -60,7 +60,7 @@ export function computeFlagUsage(args, options) {
60
60
  export function getPossibleFlagMetrics(options) {
61
61
  const metrics = [];
62
62
  for (const [flagName, config] of Object.entries(options)) {
63
- const snakeCaseName = toSnakeCase(flagName);
63
+ const snakeCaseName = stripUnderscoreBeforeNumber(StringUtilities.toSnakeCase(flagName));
64
64
  // _present is always a possible metric
65
65
  metrics.push({
66
66
  name: `${snakeCaseName}_present`,