@vibebrowser/chrome-devtools-mcp 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +873 -0
- package/build/src/DevToolsConnectionAdapter.js +70 -0
- package/build/src/DevtoolsUtils.js +295 -0
- package/build/src/HeapSnapshotManager.js +110 -0
- package/build/src/McpContext.js +605 -0
- package/build/src/McpPage.js +315 -0
- package/build/src/McpResponse.js +858 -0
- package/build/src/Mutex.js +38 -0
- package/build/src/PageCollector.js +297 -0
- package/build/src/SlimMcpResponse.js +19 -0
- package/build/src/TextSnapshot.js +236 -0
- package/build/src/ToolHandler.js +217 -0
- package/build/src/WaitForHelper.js +190 -0
- package/build/src/bin/check-latest-version.js +50 -0
- package/build/src/bin/chrome-devtools-cli-options.js +840 -0
- package/build/src/bin/chrome-devtools-mcp-cli-options.js +350 -0
- package/build/src/bin/chrome-devtools-mcp-main.js +94 -0
- package/build/src/bin/chrome-devtools-mcp.js +31 -0
- package/build/src/bin/chrome-devtools.js +189 -0
- package/build/src/bin/install-service.js +246 -0
- package/build/src/bin/service/chrome-devtools-mcp.service.template +17 -0
- package/build/src/bin/service/com.vibebrowser.chrome-devtools-mcp.plist.template +37 -0
- package/build/src/browser.js +204 -0
- package/build/src/daemon/client.js +154 -0
- package/build/src/daemon/daemon.js +204 -0
- package/build/src/daemon/types.js +7 -0
- package/build/src/daemon/utils.js +115 -0
- package/build/src/formatters/ConsoleFormatter.js +288 -0
- package/build/src/formatters/HeapSnapshotFormatter.js +54 -0
- package/build/src/formatters/IssueFormatter.js +193 -0
- package/build/src/formatters/NetworkFormatter.js +236 -0
- package/build/src/formatters/SnapshotFormatter.js +135 -0
- package/build/src/index.js +140 -0
- package/build/src/issue-descriptions.js +40 -0
- package/build/src/logger.js +37 -0
- package/build/src/polyfill.js +8 -0
- package/build/src/telemetry/ClearcutLogger.js +169 -0
- package/build/src/telemetry/WatchdogClient.js +61 -0
- package/build/src/telemetry/errors.js +18 -0
- package/build/src/telemetry/flagUtils.js +89 -0
- package/build/src/telemetry/metricsRegistry.js +89 -0
- package/build/src/telemetry/persistence.js +72 -0
- package/build/src/telemetry/transformation.js +134 -0
- package/build/src/telemetry/types.js +31 -0
- package/build/src/telemetry/watchdog/ClearcutSender.js +205 -0
- package/build/src/telemetry/watchdog/main.js +128 -0
- package/build/src/third_party/devtools-formatter-worker.js +8 -0
- package/build/src/third_party/devtools-heap-snapshot-worker.js +8 -0
- package/build/src/third_party/index.js +32 -0
- package/build/src/third_party/issue-descriptions/CoepCoopSandboxedIframeCannotNavigateToCoopPage.md +4 -0
- package/build/src/third_party/issue-descriptions/CoepCorpNotSameOrigin.md +8 -0
- package/build/src/third_party/issue-descriptions/CoepCorpNotSameOriginAfterDefaultedToSameOriginByCoep.md +18 -0
- package/build/src/third_party/issue-descriptions/CoepCorpNotSameSite.md +7 -0
- package/build/src/third_party/issue-descriptions/CoepFrameResourceNeedsCoepHeader.md +10 -0
- package/build/src/third_party/issue-descriptions/CompatibilityModeQuirks.md +5 -0
- package/build/src/third_party/issue-descriptions/CookieAttributeValueExceedsMaxSize.md +5 -0
- package/build/src/third_party/issue-descriptions/LowTextContrast.md +5 -0
- package/build/src/third_party/issue-descriptions/SameSiteExcludeContextDowngradeRead.md +8 -0
- package/build/src/third_party/issue-descriptions/SameSiteExcludeContextDowngradeSet.md +8 -0
- package/build/src/third_party/issue-descriptions/SameSiteExcludeNavigationContextDowngrade.md +8 -0
- package/build/src/third_party/issue-descriptions/SameSiteNoneInsecureErrorRead.md +8 -0
- package/build/src/third_party/issue-descriptions/SameSiteNoneInsecureErrorSet.md +8 -0
- package/build/src/third_party/issue-descriptions/SameSiteNoneInsecureWarnRead.md +8 -0
- package/build/src/third_party/issue-descriptions/SameSiteNoneInsecureWarnSet.md +8 -0
- package/build/src/third_party/issue-descriptions/SameSiteUnspecifiedLaxAllowUnsafeRead.md +9 -0
- package/build/src/third_party/issue-descriptions/SameSiteUnspecifiedLaxAllowUnsafeSet.md +9 -0
- package/build/src/third_party/issue-descriptions/SameSiteWarnCrossDowngradeRead.md +8 -0
- package/build/src/third_party/issue-descriptions/SameSiteWarnCrossDowngradeSet.md +8 -0
- package/build/src/third_party/issue-descriptions/SameSiteWarnStrictLaxDowngradeStrict.md +8 -0
- package/build/src/third_party/issue-descriptions/arInsecureContext.md +7 -0
- package/build/src/third_party/issue-descriptions/arInvalidInfoHeader.md +5 -0
- package/build/src/third_party/issue-descriptions/arInvalidRegisterOsSourceHeader.md +5 -0
- package/build/src/third_party/issue-descriptions/arInvalidRegisterOsTriggerHeader.md +5 -0
- package/build/src/third_party/issue-descriptions/arInvalidRegisterSourceHeader.md +5 -0
- package/build/src/third_party/issue-descriptions/arInvalidRegisterTriggerHeader.md +5 -0
- package/build/src/third_party/issue-descriptions/arNavigationRegistrationUniqueScopeAlreadySet.md +5 -0
- package/build/src/third_party/issue-descriptions/arNavigationRegistrationWithoutTransientUserActivation.md +6 -0
- package/build/src/third_party/issue-descriptions/arNoRegisterOsSourceHeader.md +5 -0
- package/build/src/third_party/issue-descriptions/arNoRegisterOsTriggerHeader.md +5 -0
- package/build/src/third_party/issue-descriptions/arNoRegisterSourceHeader.md +5 -0
- package/build/src/third_party/issue-descriptions/arNoRegisterTriggerHeader.md +5 -0
- package/build/src/third_party/issue-descriptions/arNoWebOrOsSupport.md +4 -0
- package/build/src/third_party/issue-descriptions/arOsSourceIgnored.md +18 -0
- package/build/src/third_party/issue-descriptions/arOsTriggerIgnored.md +19 -0
- package/build/src/third_party/issue-descriptions/arPermissionPolicyDisabled.md +8 -0
- package/build/src/third_party/issue-descriptions/arSourceAndTriggerHeaders.md +9 -0
- package/build/src/third_party/issue-descriptions/arSourceIgnored.md +13 -0
- package/build/src/third_party/issue-descriptions/arTriggerIgnored.md +12 -0
- package/build/src/third_party/issue-descriptions/arUntrustworthyReportingOrigin.md +10 -0
- package/build/src/third_party/issue-descriptions/arWebAndOsHeaders.md +11 -0
- package/build/src/third_party/issue-descriptions/bounceTrackingMitigations.md +3 -0
- package/build/src/third_party/issue-descriptions/clientHintMetaTagAllowListInvalidOrigin.md +4 -0
- package/build/src/third_party/issue-descriptions/clientHintMetaTagModifiedHTML.md +4 -0
- package/build/src/third_party/issue-descriptions/connectionAllowlistInvalidAllowlistItemType.md +12 -0
- package/build/src/third_party/issue-descriptions/connectionAllowlistInvalidHeader.md +12 -0
- package/build/src/third_party/issue-descriptions/connectionAllowlistInvalidUrlPattern.md +8 -0
- package/build/src/third_party/issue-descriptions/connectionAllowlistItemNotInnerList.md +12 -0
- package/build/src/third_party/issue-descriptions/connectionAllowlistMoreThanOneList.md +7 -0
- package/build/src/third_party/issue-descriptions/connectionAllowlistReportingEndpointNotToken.md +10 -0
- package/build/src/third_party/issue-descriptions/cookieCrossSiteRedirectDowngrade.md +12 -0
- package/build/src/third_party/issue-descriptions/cookieExcludeBlockedWithinRelatedWebsiteSet.md +4 -0
- package/build/src/third_party/issue-descriptions/cookieExcludeDomainNonAscii.md +11 -0
- package/build/src/third_party/issue-descriptions/cookieExcludePortMismatch.md +8 -0
- package/build/src/third_party/issue-descriptions/cookieExcludeSchemeMismatch.md +7 -0
- package/build/src/third_party/issue-descriptions/cookieExcludeThirdPartyPhaseoutRead.md +6 -0
- package/build/src/third_party/issue-descriptions/cookieExcludeThirdPartyPhaseoutSet.md +6 -0
- package/build/src/third_party/issue-descriptions/cookieWarnDomainNonAscii.md +11 -0
- package/build/src/third_party/issue-descriptions/cookieWarnMetadataGrantRead.md +4 -0
- package/build/src/third_party/issue-descriptions/cookieWarnMetadataGrantSet.md +4 -0
- package/build/src/third_party/issue-descriptions/cookieWarnThirdPartyPhaseoutRead.md +6 -0
- package/build/src/third_party/issue-descriptions/cookieWarnThirdPartyPhaseoutSet.md +6 -0
- package/build/src/third_party/issue-descriptions/corsAllowCredentialsRequired.md +6 -0
- package/build/src/third_party/issue-descriptions/corsDisabledScheme.md +7 -0
- package/build/src/third_party/issue-descriptions/corsDisallowedByMode.md +7 -0
- package/build/src/third_party/issue-descriptions/corsHeaderDisallowedByPreflightResponse.md +5 -0
- package/build/src/third_party/issue-descriptions/corsInvalidHeaderValues.md +7 -0
- package/build/src/third_party/issue-descriptions/corsLocalNetworkAccessPermissionDenied.md +19 -0
- package/build/src/third_party/issue-descriptions/corsMethodDisallowedByPreflightResponse.md +5 -0
- package/build/src/third_party/issue-descriptions/corsNoCorsRedirectModeNotFollow.md +5 -0
- package/build/src/third_party/issue-descriptions/corsOriginMismatch.md +6 -0
- package/build/src/third_party/issue-descriptions/corsPreflightResponseInvalid.md +5 -0
- package/build/src/third_party/issue-descriptions/corsRedirectContainsCredentials.md +5 -0
- package/build/src/third_party/issue-descriptions/corsWildcardOriginNotAllowed.md +8 -0
- package/build/src/third_party/issue-descriptions/cspEvalViolation.md +9 -0
- package/build/src/third_party/issue-descriptions/cspInlineViolation.md +10 -0
- package/build/src/third_party/issue-descriptions/cspTrustedTypesPolicyViolation.md +5 -0
- package/build/src/third_party/issue-descriptions/cspTrustedTypesSinkViolation.md +8 -0
- package/build/src/third_party/issue-descriptions/cspURLViolation.md +10 -0
- package/build/src/third_party/issue-descriptions/deprecation.md +3 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestAccountsHttpNotFound.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestAccountsInvalidResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestAccountsNoResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestApprovalDeclined.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestCanceled.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestErrorFetchingSignin.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestErrorIdToken.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenHttpNotFound.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenInvalidRequest.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenInvalidResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenNoResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestInvalidSigninResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestManifestHttpNotFound.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestManifestInvalidResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestManifestNoResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestTooManyRequests.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestInvalidAccountsResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestInvalidConfigOrWellKnown.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNoAccountSharingPermission.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNoApiPermission.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNoReturningUserFromFetchedAccounts.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotIframe.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotPotentiallyTrustworthy.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotSameOrigin.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotSignedInWithIdp.md +1 -0
- package/build/src/third_party/issue-descriptions/fetchingPartitionedBlobURL.md +7 -0
- package/build/src/third_party/issue-descriptions/genericFormAriaLabelledByToNonExistingIdError.md +8 -0
- package/build/src/third_party/issue-descriptions/genericFormAutocompleteAttributeEmptyError.md +5 -0
- package/build/src/third_party/issue-descriptions/genericFormDuplicateIdForInputError.md +5 -0
- package/build/src/third_party/issue-descriptions/genericFormEmptyIdAndNameAttributesForInputError.md +5 -0
- package/build/src/third_party/issue-descriptions/genericFormInputAssignedAutocompleteValueToIdOrNameAttributeError.md +5 -0
- package/build/src/third_party/issue-descriptions/genericFormInputHasWrongButWellIntendedAutocompleteValueError.md +5 -0
- package/build/src/third_party/issue-descriptions/genericFormInputWithNoLabelError.md +5 -0
- package/build/src/third_party/issue-descriptions/genericFormLabelForMatchesNonExistingIdError.md +5 -0
- package/build/src/third_party/issue-descriptions/genericFormLabelForNameError.md +5 -0
- package/build/src/third_party/issue-descriptions/genericFormLabelHasNeitherForNorNestedInputError.md +5 -0
- package/build/src/third_party/issue-descriptions/genericFormModelContextMissingToolDescription.md +5 -0
- package/build/src/third_party/issue-descriptions/genericFormModelContextMissingToolName.md +5 -0
- package/build/src/third_party/issue-descriptions/genericFormModelContextParameterMissingName.md +5 -0
- package/build/src/third_party/issue-descriptions/genericFormModelContextParameterMissingTitleAndDescription.md +5 -0
- package/build/src/third_party/issue-descriptions/genericFormModelContextRequiredParameterMissingName.md +5 -0
- package/build/src/third_party/issue-descriptions/genericNavigationEntryMarkedSkippable.md +7 -0
- package/build/src/third_party/issue-descriptions/genericResponseWasBlockedByORB.md +4 -0
- package/build/src/third_party/issue-descriptions/heavyAd.md +10 -0
- package/build/src/third_party/issue-descriptions/mixedContent.md +5 -0
- package/build/src/third_party/issue-descriptions/navigatingPartitionedBlobURL.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementActivationDisabled.md +7 -0
- package/build/src/third_party/issue-descriptions/permissionElementActivationDisabledWithOccluder.md +9 -0
- package/build/src/third_party/issue-descriptions/permissionElementActivationDisabledWithOccluderParent.md +9 -0
- package/build/src/third_party/issue-descriptions/permissionElementCspFrameAncestorsMissing.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementFencedFrameDisallowed.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementFontSizeTooLarge.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementFontSizeTooSmall.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementGeolocationDeprecated.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementInsetBoxShadowUnsupported.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementInvalidDisplayStyle.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementInvalidSizeValue.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementInvalidType.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementInvalidTypeActivation.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementLowContrast.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementNonOpaqueColor.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementPaddingBottomUnsupported.md +6 -0
- package/build/src/third_party/issue-descriptions/permissionElementPaddingRightUnsupported.md +6 -0
- package/build/src/third_party/issue-descriptions/permissionElementPermissionsPolicyBlocked.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementRegistrationFailed.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementRequestInProgress.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementSecurityChecksFailed.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementTypeNotSupported.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementUntrustedEvent.md +7 -0
- package/build/src/third_party/issue-descriptions/placeholderDescriptionForInvisibleIssues.md +3 -0
- package/build/src/third_party/issue-descriptions/propertyRuleInvalidNameIssue.md +3 -0
- package/build/src/third_party/issue-descriptions/propertyRuleIssue.md +7 -0
- package/build/src/third_party/issue-descriptions/selectElementAccessibilityDisallowedOptGroupChild.md +7 -0
- package/build/src/third_party/issue-descriptions/selectElementAccessibilityDisallowedSelectChild.md +7 -0
- package/build/src/third_party/issue-descriptions/selectElementAccessibilityInteractiveContentAttributesSelectDescendant.md +3 -0
- package/build/src/third_party/issue-descriptions/selectElementAccessibilityInteractiveContentLegendChild.md +3 -0
- package/build/src/third_party/issue-descriptions/selectElementAccessibilityInteractiveContentOptionChild.md +3 -0
- package/build/src/third_party/issue-descriptions/selectElementAccessibilityNonPhrasingContentOptionChild.md +3 -0
- package/build/src/third_party/issue-descriptions/selectivePermissionsIntervention.md +7 -0
- package/build/src/third_party/issue-descriptions/sharedArrayBuffer.md +7 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryUseErrorCrossOriginNoCorsRequest.md +1 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryUseErrorDictionaryLoadFailure.md +3 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryUseErrorMatchingDictionaryNotUsed.md +3 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryUseErrorUnexpectedContentDictionaryHeader.md +1 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorCossOriginNoCorsRequest.md +1 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorDisallowedBySettings.md +1 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorExpiredResponse.md +3 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorFeatureDisabled.md +3 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorInsufficientResources.md +1 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorInvalidMatchField.md +1 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorInvalidStructuredHeader.md +1 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorInvalidTTLField.md +1 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNavigationRequest.md +3 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNoMatchField.md +1 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonIntegerTTLField.md +1 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonListMatchDestField.md +1 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonSecureContext.md +3 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonStringIdField.md +1 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonStringInMatchDestList.md +1 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonStringMatchField.md +1 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonTokenTypeField.md +1 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorRequestAborted.md +1 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorShuttingDown.md +1 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorTooLongIdField.md +3 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorUnsupportedType.md +3 -0
- package/build/src/third_party/issue-descriptions/sriInvalidSignatureHeader.md +14 -0
- package/build/src/third_party/issue-descriptions/sriInvalidSignatureInputHeader.md +15 -0
- package/build/src/third_party/issue-descriptions/sriMissingSignatureHeader.md +8 -0
- package/build/src/third_party/issue-descriptions/sriMissingSignatureInputHeader.md +7 -0
- package/build/src/third_party/issue-descriptions/sriSignatureHeaderValueIsIncorrectLength.md +11 -0
- package/build/src/third_party/issue-descriptions/sriSignatureHeaderValueIsNotByteSequence.md +14 -0
- package/build/src/third_party/issue-descriptions/sriSignatureHeaderValueIsParameterized.md +15 -0
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidComponentName.md +8 -0
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidComponentType.md +13 -0
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidDerivedComponentParameter.md +4 -0
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidHeaderComponentParameter.md +5 -0
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidParameter.md +11 -0
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderKeyIdLength.md +12 -0
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderMissingLabel.md +6 -0
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderMissingRequiredParameters.md +8 -0
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderValueMissingComponents.md +11 -0
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderValueNotInnerList.md +11 -0
- package/build/src/third_party/issue-descriptions/sriValidationFailedIntegrityMismatch.md +10 -0
- package/build/src/third_party/issue-descriptions/sriValidationFailedInvalidLength.md +5 -0
- package/build/src/third_party/issue-descriptions/sriValidationFailedSignatureExpired.md +6 -0
- package/build/src/third_party/issue-descriptions/sriValidationFailedSignatureMismatch.md +11 -0
- package/build/src/third_party/issue-descriptions/stylesheetLateImport.md +4 -0
- package/build/src/third_party/issue-descriptions/stylesheetRequestFailed.md +3 -0
- package/build/src/third_party/issue-descriptions/summaryElementAccessibilityInteractiveContentSummaryDescendant.md +3 -0
- package/build/src/third_party/issue-descriptions/unencodedDigestIncorrectDigestLength.md +12 -0
- package/build/src/third_party/issue-descriptions/unencodedDigestIncorrectDigestType.md +17 -0
- package/build/src/third_party/issue-descriptions/unencodedDigestMalformedDictionary.md +14 -0
- package/build/src/third_party/issue-descriptions/unencodedDigestUnknownAlgorithm.md +15 -0
- package/build/src/third_party/lighthouse-devtools-mcp-bundle.js +61598 -0
- package/build/src/tools/ToolDefinition.js +73 -0
- package/build/src/tools/categories.js +36 -0
- package/build/src/tools/console.js +91 -0
- package/build/src/tools/emulation.js +57 -0
- package/build/src/tools/extensions.js +96 -0
- package/build/src/tools/input.js +461 -0
- package/build/src/tools/lighthouse.js +131 -0
- package/build/src/tools/memory.js +106 -0
- package/build/src/tools/network.js +125 -0
- package/build/src/tools/pages.js +411 -0
- package/build/src/tools/performance.js +196 -0
- package/build/src/tools/screencast.js +95 -0
- package/build/src/tools/screenshot.js +87 -0
- package/build/src/tools/script.js +151 -0
- package/build/src/tools/slim/tools.js +85 -0
- package/build/src/tools/snapshot.js +60 -0
- package/build/src/tools/thirdPartyDeveloper.js +75 -0
- package/build/src/tools/tools.js +56 -0
- package/build/src/tools/webmcp.js +64 -0
- package/build/src/trace-processing/parse.js +85 -0
- package/build/src/types.js +7 -0
- package/build/src/utils/check-for-updates.js +74 -0
- package/build/src/utils/files.js +18 -0
- package/build/src/utils/id.js +16 -0
- package/build/src/utils/keyboard.js +297 -0
- package/build/src/utils/pagination.js +50 -0
- package/build/src/utils/string.js +37 -0
- package/build/src/utils/types.js +7 -0
- package/build/src/version.js +10 -0
- package/package.json +93 -0
|
@@ -0,0 +1,461 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { logger } from '../logger.js';
|
|
7
|
+
import { zod } from '../third_party/index.js';
|
|
8
|
+
import { parseKey } from '../utils/keyboard.js';
|
|
9
|
+
import { ToolCategory } from './categories.js';
|
|
10
|
+
import { definePageTool } from './ToolDefinition.js';
|
|
11
|
+
const dblClickSchema = zod
|
|
12
|
+
.boolean()
|
|
13
|
+
.optional()
|
|
14
|
+
.describe('Set to true for double clicks. Default is false.');
|
|
15
|
+
const includeSnapshotSchema = zod
|
|
16
|
+
.boolean()
|
|
17
|
+
.optional()
|
|
18
|
+
.describe('Whether to include a snapshot in the response. Default is false.');
|
|
19
|
+
const submitKeySchema = zod
|
|
20
|
+
.string()
|
|
21
|
+
.optional()
|
|
22
|
+
.describe('Optional key to press after typing. E.g., "Enter", "Tab", "Escape"');
|
|
23
|
+
function handleActionError(error, uid) {
|
|
24
|
+
logger('failed to act using a locator', error);
|
|
25
|
+
throw new Error(`Failed to interact with the element with uid ${uid}. The element did not become interactive within the configured timeout.`, {
|
|
26
|
+
cause: error,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
async function selectNativeSelectOption(handle) {
|
|
30
|
+
const selectHandle = await handle.evaluateHandle(node => {
|
|
31
|
+
if (!(node instanceof HTMLOptionElement)) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
const select = node.closest('select');
|
|
35
|
+
if (!select || select.multiple || select.disabled || node.disabled) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
const parentElement = node.parentElement;
|
|
39
|
+
if (parentElement instanceof HTMLOptGroupElement &&
|
|
40
|
+
parentElement.disabled) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
return select;
|
|
44
|
+
});
|
|
45
|
+
try {
|
|
46
|
+
const select = selectHandle.asElement();
|
|
47
|
+
if (!select) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
const valueHandle = await handle.getProperty('value');
|
|
51
|
+
try {
|
|
52
|
+
const value = await valueHandle.jsonValue();
|
|
53
|
+
if (typeof value !== 'string') {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
await select.asLocator().fill(value);
|
|
57
|
+
}
|
|
58
|
+
finally {
|
|
59
|
+
void valueHandle.dispose();
|
|
60
|
+
}
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
finally {
|
|
64
|
+
void selectHandle.dispose();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
export const click = definePageTool({
|
|
68
|
+
name: 'click',
|
|
69
|
+
description: `Clicks on the provided element`,
|
|
70
|
+
annotations: {
|
|
71
|
+
category: ToolCategory.INPUT,
|
|
72
|
+
readOnlyHint: false,
|
|
73
|
+
},
|
|
74
|
+
schema: {
|
|
75
|
+
uid: zod
|
|
76
|
+
.string()
|
|
77
|
+
.describe('The uid of an element on the page from the page content snapshot'),
|
|
78
|
+
dblClick: dblClickSchema,
|
|
79
|
+
includeSnapshot: includeSnapshotSchema,
|
|
80
|
+
},
|
|
81
|
+
blockedByDialog: true,
|
|
82
|
+
handler: async (request, response) => {
|
|
83
|
+
const uid = request.params.uid;
|
|
84
|
+
const handle = await request.page.getElementByUid(uid);
|
|
85
|
+
const aXNode = request.page.getAXNodeByUid(uid);
|
|
86
|
+
const shouldSelectNativeOption = !request.params.dblClick && aXNode?.role === 'option';
|
|
87
|
+
try {
|
|
88
|
+
const result = await request.page.waitForEventsAfterAction(async () => {
|
|
89
|
+
if (shouldSelectNativeOption &&
|
|
90
|
+
(await selectNativeSelectOption(handle))) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
await handle.asLocator().click({
|
|
94
|
+
count: request.params.dblClick ? 2 : 1,
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
response.appendResponseLine(request.params.dblClick
|
|
98
|
+
? `Successfully double clicked on the element`
|
|
99
|
+
: `Successfully clicked on the element`);
|
|
100
|
+
response.attachWaitForResult(result);
|
|
101
|
+
if (request.params.includeSnapshot) {
|
|
102
|
+
response.includeSnapshot();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
handleActionError(error, uid);
|
|
107
|
+
}
|
|
108
|
+
finally {
|
|
109
|
+
void handle.dispose();
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
export const clickAt = definePageTool({
|
|
114
|
+
name: 'click_at',
|
|
115
|
+
description: `Clicks at the provided coordinates`,
|
|
116
|
+
annotations: {
|
|
117
|
+
category: ToolCategory.INPUT,
|
|
118
|
+
readOnlyHint: false,
|
|
119
|
+
conditions: ['experimentalVision'],
|
|
120
|
+
},
|
|
121
|
+
schema: {
|
|
122
|
+
x: zod.number().describe('The x coordinate'),
|
|
123
|
+
y: zod.number().describe('The y coordinate'),
|
|
124
|
+
dblClick: dblClickSchema,
|
|
125
|
+
includeSnapshot: includeSnapshotSchema,
|
|
126
|
+
},
|
|
127
|
+
blockedByDialog: true,
|
|
128
|
+
handler: async (request, response) => {
|
|
129
|
+
const page = request.page;
|
|
130
|
+
const result = await page.waitForEventsAfterAction(async () => {
|
|
131
|
+
await page.pptrPage.mouse.click(request.params.x, request.params.y, {
|
|
132
|
+
clickCount: request.params.dblClick ? 2 : 1,
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
response.appendResponseLine(request.params.dblClick
|
|
136
|
+
? `Successfully double clicked at the coordinates`
|
|
137
|
+
: `Successfully clicked at the coordinates`);
|
|
138
|
+
response.attachWaitForResult(result);
|
|
139
|
+
if (request.params.includeSnapshot) {
|
|
140
|
+
response.includeSnapshot();
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
export const hover = definePageTool({
|
|
145
|
+
name: 'hover',
|
|
146
|
+
description: `Hover over the provided element`,
|
|
147
|
+
annotations: {
|
|
148
|
+
category: ToolCategory.INPUT,
|
|
149
|
+
readOnlyHint: false,
|
|
150
|
+
},
|
|
151
|
+
schema: {
|
|
152
|
+
uid: zod
|
|
153
|
+
.string()
|
|
154
|
+
.describe('The uid of an element on the page from the page content snapshot'),
|
|
155
|
+
includeSnapshot: includeSnapshotSchema,
|
|
156
|
+
},
|
|
157
|
+
blockedByDialog: true,
|
|
158
|
+
handler: async (request, response) => {
|
|
159
|
+
const uid = request.params.uid;
|
|
160
|
+
const handle = await request.page.getElementByUid(uid);
|
|
161
|
+
try {
|
|
162
|
+
const result = await request.page.waitForEventsAfterAction(async () => {
|
|
163
|
+
await handle.asLocator().hover();
|
|
164
|
+
});
|
|
165
|
+
response.appendResponseLine(`Successfully hovered over the element`);
|
|
166
|
+
response.attachWaitForResult(result);
|
|
167
|
+
if (request.params.includeSnapshot) {
|
|
168
|
+
response.includeSnapshot();
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
handleActionError(error, uid);
|
|
173
|
+
}
|
|
174
|
+
finally {
|
|
175
|
+
void handle.dispose();
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
// The AXNode for an option doesn't contain its `value`. We set text content of the option as value.
|
|
180
|
+
// If the form is a combobox, we need to find the correct option by its text value.
|
|
181
|
+
// To do that, loop through the children while checking which child's text matches the requested value (requested value is actually the text content).
|
|
182
|
+
// When the correct option is found, use the element handle to get the real value.
|
|
183
|
+
async function selectOption(handle, aXNode, value) {
|
|
184
|
+
let optionFound = false;
|
|
185
|
+
for (const child of aXNode.children) {
|
|
186
|
+
if (child.role === 'option' && child.name === value && child.value) {
|
|
187
|
+
optionFound = true;
|
|
188
|
+
const childHandle = await child.elementHandle();
|
|
189
|
+
if (childHandle) {
|
|
190
|
+
try {
|
|
191
|
+
const childValueHandle = await childHandle.getProperty('value');
|
|
192
|
+
try {
|
|
193
|
+
const childValue = await childValueHandle.jsonValue();
|
|
194
|
+
if (childValue) {
|
|
195
|
+
await handle.asLocator().fill(childValue.toString());
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
finally {
|
|
199
|
+
void childValueHandle.dispose();
|
|
200
|
+
}
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
finally {
|
|
204
|
+
void childHandle.dispose();
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
if (!optionFound) {
|
|
210
|
+
throw new Error(`Could not find option with text "${value}"`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
function hasOptionChildren(aXNode) {
|
|
214
|
+
return aXNode.children.some(child => child.role === 'option');
|
|
215
|
+
}
|
|
216
|
+
async function fillFormElement(uid, value, context, page) {
|
|
217
|
+
const handle = await page.getElementByUid(uid);
|
|
218
|
+
try {
|
|
219
|
+
const aXNode = context.getAXNodeByUid(uid);
|
|
220
|
+
// We assume that combobox needs to be handled as select if it has
|
|
221
|
+
// role='combobox' and option children.
|
|
222
|
+
if (aXNode && aXNode.role === 'combobox' && hasOptionChildren(aXNode)) {
|
|
223
|
+
await selectOption(handle, aXNode, value);
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
const isToggle = await handle.evaluate(el => {
|
|
227
|
+
if (el instanceof HTMLInputElement) {
|
|
228
|
+
return el.type === 'checkbox' || el.type === 'radio';
|
|
229
|
+
}
|
|
230
|
+
const role = el.getAttribute('role');
|
|
231
|
+
return role === 'checkbox' || role === 'radio' || role === 'switch';
|
|
232
|
+
});
|
|
233
|
+
if (isToggle) {
|
|
234
|
+
if (['true', 'false'].includes(value)) {
|
|
235
|
+
await handle.asLocator().fill(value === 'true');
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
throw new Error(`Checkboxes, radio boxes and toggles require "true" or "false" value, but ${value} was used`);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
// Increase timeout for longer input values.
|
|
243
|
+
const timeoutPerChar = 10; // ms
|
|
244
|
+
const fillTimeout = page.pptrPage.getDefaultTimeout() + value.length * timeoutPerChar;
|
|
245
|
+
await handle.asLocator().setTimeout(fillTimeout).fill(value);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
catch (error) {
|
|
250
|
+
handleActionError(error, uid);
|
|
251
|
+
}
|
|
252
|
+
finally {
|
|
253
|
+
void handle.dispose();
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
export const fill = definePageTool({
|
|
257
|
+
name: 'fill',
|
|
258
|
+
description: `Type text into an input, text area or select an option from a <select> element.`,
|
|
259
|
+
annotations: {
|
|
260
|
+
category: ToolCategory.INPUT,
|
|
261
|
+
readOnlyHint: false,
|
|
262
|
+
},
|
|
263
|
+
schema: {
|
|
264
|
+
uid: zod
|
|
265
|
+
.string()
|
|
266
|
+
.describe('The uid of an element on the page from the page content snapshot'),
|
|
267
|
+
value: zod
|
|
268
|
+
.string()
|
|
269
|
+
.describe('The value to fill in. "true" or "false" for checkboxes and toggles, "true" for radio buttons.'),
|
|
270
|
+
includeSnapshot: includeSnapshotSchema,
|
|
271
|
+
},
|
|
272
|
+
blockedByDialog: true,
|
|
273
|
+
handler: async (request, response, context) => {
|
|
274
|
+
const page = request.page;
|
|
275
|
+
const result = await page.waitForEventsAfterAction(async () => {
|
|
276
|
+
await fillFormElement(request.params.uid, request.params.value, context, page);
|
|
277
|
+
});
|
|
278
|
+
response.appendResponseLine(`Successfully filled out the element`);
|
|
279
|
+
response.attachWaitForResult(result);
|
|
280
|
+
if (request.params.includeSnapshot) {
|
|
281
|
+
response.includeSnapshot();
|
|
282
|
+
}
|
|
283
|
+
},
|
|
284
|
+
});
|
|
285
|
+
export const typeText = definePageTool({
|
|
286
|
+
name: 'type_text',
|
|
287
|
+
description: `Type text using keyboard into a previously focused input`,
|
|
288
|
+
annotations: {
|
|
289
|
+
category: ToolCategory.INPUT,
|
|
290
|
+
readOnlyHint: false,
|
|
291
|
+
},
|
|
292
|
+
schema: {
|
|
293
|
+
text: zod.string().describe('The text to type'),
|
|
294
|
+
submitKey: submitKeySchema,
|
|
295
|
+
},
|
|
296
|
+
blockedByDialog: true,
|
|
297
|
+
handler: async (request, response) => {
|
|
298
|
+
const page = request.page;
|
|
299
|
+
const result = await page.waitForEventsAfterAction(async () => {
|
|
300
|
+
await page.pptrPage.keyboard.type(request.params.text);
|
|
301
|
+
if (request.params.submitKey) {
|
|
302
|
+
await page.pptrPage.keyboard.press(request.params.submitKey);
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
response.appendResponseLine(`Typed text "${request.params.text}${request.params.submitKey ? ` + ${request.params.submitKey}` : ''}"`);
|
|
306
|
+
response.attachWaitForResult(result);
|
|
307
|
+
},
|
|
308
|
+
});
|
|
309
|
+
export const drag = definePageTool({
|
|
310
|
+
name: 'drag',
|
|
311
|
+
description: `Drag an element onto another element`,
|
|
312
|
+
annotations: {
|
|
313
|
+
category: ToolCategory.INPUT,
|
|
314
|
+
readOnlyHint: false,
|
|
315
|
+
},
|
|
316
|
+
schema: {
|
|
317
|
+
from_uid: zod.string().describe('The uid of the element to drag'),
|
|
318
|
+
to_uid: zod.string().describe('The uid of the element to drop into'),
|
|
319
|
+
includeSnapshot: includeSnapshotSchema,
|
|
320
|
+
},
|
|
321
|
+
blockedByDialog: true,
|
|
322
|
+
handler: async (request, response) => {
|
|
323
|
+
const fromHandle = await request.page.getElementByUid(request.params.from_uid);
|
|
324
|
+
const toHandle = await request.page.getElementByUid(request.params.to_uid);
|
|
325
|
+
try {
|
|
326
|
+
const result = await request.page.waitForEventsAfterAction(async () => {
|
|
327
|
+
await fromHandle.drag(toHandle);
|
|
328
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
329
|
+
await toHandle.drop(fromHandle);
|
|
330
|
+
});
|
|
331
|
+
response.appendResponseLine(`Successfully dragged an element`);
|
|
332
|
+
response.attachWaitForResult(result);
|
|
333
|
+
if (request.params.includeSnapshot) {
|
|
334
|
+
response.includeSnapshot();
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
finally {
|
|
338
|
+
void fromHandle.dispose();
|
|
339
|
+
void toHandle.dispose();
|
|
340
|
+
}
|
|
341
|
+
},
|
|
342
|
+
});
|
|
343
|
+
export const fillForm = definePageTool({
|
|
344
|
+
name: 'fill_form',
|
|
345
|
+
description: `Fill out multiple form elements (inputs, selects, checkboxes, radios) at once. ALWAYS prefer this tool over multiple individual 'fill' or 'click' calls when interacting with forms. It is significantly faster, more reliable, and reduces turn count. Example: Fill username, password, and check "Remember Me" in one call.`,
|
|
346
|
+
annotations: {
|
|
347
|
+
category: ToolCategory.INPUT,
|
|
348
|
+
readOnlyHint: false,
|
|
349
|
+
},
|
|
350
|
+
schema: {
|
|
351
|
+
elements: zod
|
|
352
|
+
.array(
|
|
353
|
+
// eslint-disable-next-line @local/enforce-zod-schema
|
|
354
|
+
zod.object({
|
|
355
|
+
uid: zod.string().describe('The uid of the element to fill out'),
|
|
356
|
+
value: zod
|
|
357
|
+
.string()
|
|
358
|
+
.describe('Value for the element. "true" or "false" for checkboxes and toggles, "true" for radio buttons.'),
|
|
359
|
+
}))
|
|
360
|
+
.describe('Elements from snapshot to fill out.'),
|
|
361
|
+
includeSnapshot: includeSnapshotSchema,
|
|
362
|
+
},
|
|
363
|
+
blockedByDialog: true,
|
|
364
|
+
handler: async (request, response, context) => {
|
|
365
|
+
const page = request.page;
|
|
366
|
+
let lastResult = {};
|
|
367
|
+
for (const element of request.params.elements) {
|
|
368
|
+
lastResult = await page.waitForEventsAfterAction(async () => {
|
|
369
|
+
await fillFormElement(element.uid, element.value, context, page);
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
response.appendResponseLine(`Successfully filled out the form`);
|
|
373
|
+
response.attachWaitForResult(lastResult);
|
|
374
|
+
if (request.params.includeSnapshot) {
|
|
375
|
+
response.includeSnapshot();
|
|
376
|
+
}
|
|
377
|
+
},
|
|
378
|
+
});
|
|
379
|
+
export const uploadFile = definePageTool({
|
|
380
|
+
name: 'upload_file',
|
|
381
|
+
description: 'Upload a file through a provided element.',
|
|
382
|
+
annotations: {
|
|
383
|
+
category: ToolCategory.INPUT,
|
|
384
|
+
readOnlyHint: false,
|
|
385
|
+
},
|
|
386
|
+
schema: {
|
|
387
|
+
uid: zod
|
|
388
|
+
.string()
|
|
389
|
+
.describe('The uid of the file input element or an element that will open file chooser on the page from the page content snapshot'),
|
|
390
|
+
filePath: zod.string().describe('The local path of the file to upload'),
|
|
391
|
+
includeSnapshot: includeSnapshotSchema,
|
|
392
|
+
},
|
|
393
|
+
blockedByDialog: true,
|
|
394
|
+
handler: async (request, response, context) => {
|
|
395
|
+
const { uid, filePath } = request.params;
|
|
396
|
+
context.validatePath(filePath);
|
|
397
|
+
const handle = (await request.page.getElementByUid(uid));
|
|
398
|
+
try {
|
|
399
|
+
try {
|
|
400
|
+
await handle.uploadFile(filePath);
|
|
401
|
+
}
|
|
402
|
+
catch {
|
|
403
|
+
// Some sites use a proxy element to trigger file upload instead of
|
|
404
|
+
// a type=file element. In this case, we want to default to
|
|
405
|
+
// Page.waitForFileChooser() and upload the file this way.
|
|
406
|
+
try {
|
|
407
|
+
const [fileChooser] = await Promise.all([
|
|
408
|
+
request.page.pptrPage.waitForFileChooser({ timeout: 3000 }),
|
|
409
|
+
handle.asLocator().click(),
|
|
410
|
+
]);
|
|
411
|
+
await fileChooser.accept([filePath]);
|
|
412
|
+
}
|
|
413
|
+
catch {
|
|
414
|
+
throw new Error(`Failed to upload file. The element could not accept the file directly, and clicking it did not trigger a file chooser.`);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
if (request.params.includeSnapshot) {
|
|
418
|
+
response.includeSnapshot();
|
|
419
|
+
}
|
|
420
|
+
response.appendResponseLine(`File uploaded from ${filePath}.`);
|
|
421
|
+
}
|
|
422
|
+
finally {
|
|
423
|
+
void handle.dispose();
|
|
424
|
+
}
|
|
425
|
+
},
|
|
426
|
+
});
|
|
427
|
+
export const pressKey = definePageTool({
|
|
428
|
+
name: 'press_key',
|
|
429
|
+
description: `Press a key or key combination. Use this when other input methods like fill() cannot be used (e.g., keyboard shortcuts, navigation keys, or special key combinations).`,
|
|
430
|
+
annotations: {
|
|
431
|
+
category: ToolCategory.INPUT,
|
|
432
|
+
readOnlyHint: false,
|
|
433
|
+
},
|
|
434
|
+
schema: {
|
|
435
|
+
key: zod
|
|
436
|
+
.string()
|
|
437
|
+
.describe('A key or a combination (e.g., "Enter", "Control+A", "Control++", "Control+Shift+R"). Modifiers: Control, Shift, Alt, Meta'),
|
|
438
|
+
includeSnapshot: includeSnapshotSchema,
|
|
439
|
+
},
|
|
440
|
+
blockedByDialog: true,
|
|
441
|
+
handler: async (request, response) => {
|
|
442
|
+
const page = request.page;
|
|
443
|
+
const tokens = parseKey(request.params.key);
|
|
444
|
+
const [key, ...modifiers] = tokens;
|
|
445
|
+
const result = await page.waitForEventsAfterAction(async () => {
|
|
446
|
+
for (const modifier of modifiers) {
|
|
447
|
+
await page.pptrPage.keyboard.down(modifier);
|
|
448
|
+
}
|
|
449
|
+
await page.pptrPage.keyboard.press(key);
|
|
450
|
+
for (const modifier of modifiers.toReversed()) {
|
|
451
|
+
await page.pptrPage.keyboard.up(modifier);
|
|
452
|
+
}
|
|
453
|
+
});
|
|
454
|
+
response.appendResponseLine(`Successfully pressed key: ${request.params.key}`);
|
|
455
|
+
response.attachWaitForResult(result);
|
|
456
|
+
if (request.params.includeSnapshot) {
|
|
457
|
+
response.includeSnapshot();
|
|
458
|
+
}
|
|
459
|
+
},
|
|
460
|
+
});
|
|
461
|
+
//# sourceMappingURL=input.js.map
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import { snapshot, navigation, generateReport, zod, } from '../third_party/index.js';
|
|
8
|
+
import { ToolCategory } from './categories.js';
|
|
9
|
+
import { startTrace } from './performance.js';
|
|
10
|
+
import { definePageTool } from './ToolDefinition.js';
|
|
11
|
+
export const lighthouseAudit = definePageTool({
|
|
12
|
+
name: 'lighthouse_audit',
|
|
13
|
+
description: `Get Lighthouse score and reports for accessibility, SEO, best practices, and agentic browsing. This excludes performance. For performance audits, run ${startTrace.name}`,
|
|
14
|
+
annotations: {
|
|
15
|
+
category: ToolCategory.DEBUGGING,
|
|
16
|
+
readOnlyHint: false,
|
|
17
|
+
},
|
|
18
|
+
schema: {
|
|
19
|
+
mode: zod
|
|
20
|
+
.enum(['navigation', 'snapshot'])
|
|
21
|
+
.default('navigation')
|
|
22
|
+
.describe('"navigation" reloads & audits. "snapshot" analyzes current state.'),
|
|
23
|
+
device: zod
|
|
24
|
+
.enum(['desktop', 'mobile'])
|
|
25
|
+
.default('desktop')
|
|
26
|
+
.describe('Device to emulate.'),
|
|
27
|
+
outputDirPath: zod
|
|
28
|
+
.string()
|
|
29
|
+
.optional()
|
|
30
|
+
.describe('Directory for reports. If omitted, uses temporary files.'),
|
|
31
|
+
},
|
|
32
|
+
blockedByDialog: true,
|
|
33
|
+
handler: async (request, response, context) => {
|
|
34
|
+
const page = request.page;
|
|
35
|
+
const categories = [
|
|
36
|
+
'accessibility',
|
|
37
|
+
'seo',
|
|
38
|
+
'best-practices',
|
|
39
|
+
'agentic-browsing',
|
|
40
|
+
];
|
|
41
|
+
const formats = ['json', 'html'];
|
|
42
|
+
const { mode = 'navigation', device = 'desktop', outputDirPath, } = request.params;
|
|
43
|
+
context.validatePath(outputDirPath);
|
|
44
|
+
const flags = {
|
|
45
|
+
onlyCategories: categories,
|
|
46
|
+
output: formats,
|
|
47
|
+
// Default 30 second timeout for page load.
|
|
48
|
+
maxWaitForLoad: 30_000,
|
|
49
|
+
};
|
|
50
|
+
if (device === 'desktop') {
|
|
51
|
+
flags.formFactor = 'desktop';
|
|
52
|
+
flags.screenEmulation = {
|
|
53
|
+
mobile: false,
|
|
54
|
+
width: 1350,
|
|
55
|
+
height: 940,
|
|
56
|
+
deviceScaleFactor: 1,
|
|
57
|
+
disabled: false,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
flags.formFactor = 'mobile';
|
|
62
|
+
flags.screenEmulation = {
|
|
63
|
+
mobile: true,
|
|
64
|
+
width: 412,
|
|
65
|
+
height: 823,
|
|
66
|
+
deviceScaleFactor: 1.75,
|
|
67
|
+
disabled: false,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
let result;
|
|
71
|
+
try {
|
|
72
|
+
if (mode === 'navigation') {
|
|
73
|
+
result = await navigation(page.pptrPage, page.pptrPage.url(), {
|
|
74
|
+
flags,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
result = await snapshot(page.pptrPage, {
|
|
79
|
+
flags,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
if (!result) {
|
|
83
|
+
throw new Error('Lighthouse audit failed.');
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
finally {
|
|
87
|
+
await context.restoreEmulation(page);
|
|
88
|
+
}
|
|
89
|
+
const lhr = result.lhr;
|
|
90
|
+
const reportPaths = [];
|
|
91
|
+
const encoder = new TextEncoder();
|
|
92
|
+
for (const format of formats) {
|
|
93
|
+
const report = generateReport(lhr, format);
|
|
94
|
+
const data = encoder.encode(report);
|
|
95
|
+
if (outputDirPath) {
|
|
96
|
+
const reportPath = path.join(outputDirPath, `report`);
|
|
97
|
+
const { filename } = await context.saveFile(data, reportPath, `.${format}`);
|
|
98
|
+
reportPaths.push(filename);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
const { filepath } = await context.saveTemporaryFile(data, `report.${format}`);
|
|
102
|
+
reportPaths.push(filepath);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
const categoryScores = Object.values(lhr.categories).map(c => ({
|
|
106
|
+
id: c.id,
|
|
107
|
+
title: c.title,
|
|
108
|
+
score: c.score,
|
|
109
|
+
}));
|
|
110
|
+
const failedAudits = Object.values(lhr.audits).filter(a => a.score !== null && a.score < 1).length;
|
|
111
|
+
const passedAudits = Object.values(lhr.audits).filter(a => a.score === 1).length;
|
|
112
|
+
const output = {
|
|
113
|
+
summary: {
|
|
114
|
+
mode,
|
|
115
|
+
device,
|
|
116
|
+
url: lhr.mainDocumentUrl,
|
|
117
|
+
scores: categoryScores,
|
|
118
|
+
audits: {
|
|
119
|
+
failed: failedAudits,
|
|
120
|
+
passed: passedAudits,
|
|
121
|
+
},
|
|
122
|
+
timing: {
|
|
123
|
+
total: lhr.timing.total,
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
reports: reportPaths,
|
|
127
|
+
};
|
|
128
|
+
response.attachLighthouseResult(output);
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
//# sourceMappingURL=lighthouse.js.map
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { zod } from '../third_party/index.js';
|
|
7
|
+
import { ensureExtension } from '../utils/files.js';
|
|
8
|
+
import { ToolCategory } from './categories.js';
|
|
9
|
+
import { definePageTool, defineTool } from './ToolDefinition.js';
|
|
10
|
+
export const takeMemorySnapshot = definePageTool({
|
|
11
|
+
name: 'take_memory_snapshot',
|
|
12
|
+
description: `Capture a heap snapshot of the currently selected page. Use to analyze the memory distribution of JavaScript objects and debug memory leaks.`,
|
|
13
|
+
annotations: {
|
|
14
|
+
category: ToolCategory.MEMORY,
|
|
15
|
+
readOnlyHint: false,
|
|
16
|
+
},
|
|
17
|
+
schema: {
|
|
18
|
+
filePath: zod
|
|
19
|
+
.string()
|
|
20
|
+
.describe('A path to a .heapsnapshot file to save the heapsnapshot to.'),
|
|
21
|
+
},
|
|
22
|
+
blockedByDialog: true,
|
|
23
|
+
handler: async (request, response, context) => {
|
|
24
|
+
const page = request.page;
|
|
25
|
+
context.validatePath(request.params.filePath);
|
|
26
|
+
await page.pptrPage.captureHeapSnapshot({
|
|
27
|
+
path: ensureExtension(request.params.filePath, '.heapsnapshot'),
|
|
28
|
+
});
|
|
29
|
+
response.appendResponseLine(`Heap snapshot saved to ${request.params.filePath}`);
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
export const exploreMemorySnapshot = defineTool({
|
|
33
|
+
name: 'load_memory_snapshot',
|
|
34
|
+
description: 'Loads a memory heapsnapshot and returns snapshot summary stats.',
|
|
35
|
+
annotations: {
|
|
36
|
+
category: ToolCategory.MEMORY,
|
|
37
|
+
readOnlyHint: true,
|
|
38
|
+
conditions: ['experimentalMemory'],
|
|
39
|
+
},
|
|
40
|
+
schema: {
|
|
41
|
+
filePath: zod.string().describe('A path to a .heapsnapshot file to read.'),
|
|
42
|
+
},
|
|
43
|
+
blockedByDialog: false,
|
|
44
|
+
handler: async (request, response, context) => {
|
|
45
|
+
context.validatePath(request.params.filePath);
|
|
46
|
+
const stats = await context.getHeapSnapshotStats(request.params.filePath);
|
|
47
|
+
const staticData = await context.getHeapSnapshotStaticData(request.params.filePath);
|
|
48
|
+
response.setHeapSnapshotStats(stats, staticData);
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
export const getMemorySnapshotDetails = defineTool({
|
|
52
|
+
name: 'get_memory_snapshot_details',
|
|
53
|
+
description: 'Loads a memory heapsnapshot and returns all available information including statistics, static data, and aggregated node information. Supports pagination for aggregates.',
|
|
54
|
+
annotations: {
|
|
55
|
+
category: ToolCategory.MEMORY,
|
|
56
|
+
readOnlyHint: true,
|
|
57
|
+
conditions: ['experimentalMemory'],
|
|
58
|
+
},
|
|
59
|
+
schema: {
|
|
60
|
+
filePath: zod.string().describe('A path to a .heapsnapshot file to read.'),
|
|
61
|
+
pageIdx: zod
|
|
62
|
+
.number()
|
|
63
|
+
.optional()
|
|
64
|
+
.describe('The page index for pagination of aggregates.'),
|
|
65
|
+
pageSize: zod
|
|
66
|
+
.number()
|
|
67
|
+
.optional()
|
|
68
|
+
.describe('The page size for pagination of aggregates.'),
|
|
69
|
+
},
|
|
70
|
+
blockedByDialog: false,
|
|
71
|
+
handler: async (request, response, context) => {
|
|
72
|
+
context.validatePath(request.params.filePath);
|
|
73
|
+
const aggregates = await context.getHeapSnapshotAggregates(request.params.filePath);
|
|
74
|
+
response.setHeapSnapshotAggregates(aggregates, {
|
|
75
|
+
pageIdx: request.params.pageIdx,
|
|
76
|
+
pageSize: request.params.pageSize,
|
|
77
|
+
});
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
export const getNodesByClass = defineTool({
|
|
81
|
+
name: 'get_nodes_by_class',
|
|
82
|
+
description: 'Loads a memory heapsnapshot and returns instances of a specific class with their stable IDs.',
|
|
83
|
+
annotations: {
|
|
84
|
+
category: ToolCategory.MEMORY,
|
|
85
|
+
readOnlyHint: true,
|
|
86
|
+
conditions: ['experimentalMemory'],
|
|
87
|
+
},
|
|
88
|
+
schema: {
|
|
89
|
+
filePath: zod.string().describe('A path to a .heapsnapshot file to read.'),
|
|
90
|
+
uid: zod
|
|
91
|
+
.number()
|
|
92
|
+
.describe('The unique UID for the class, obtained from aggregates listing.'),
|
|
93
|
+
pageIdx: zod.number().optional().describe('The page index for pagination.'),
|
|
94
|
+
pageSize: zod.number().optional().describe('The page size for pagination.'),
|
|
95
|
+
},
|
|
96
|
+
blockedByDialog: false,
|
|
97
|
+
handler: async (request, response, context) => {
|
|
98
|
+
context.validatePath(request.params.filePath);
|
|
99
|
+
const nodes = await context.getHeapSnapshotNodesByUid(request.params.filePath, request.params.uid);
|
|
100
|
+
response.setHeapSnapshotNodes(nodes, {
|
|
101
|
+
pageIdx: request.params.pageIdx,
|
|
102
|
+
pageSize: request.params.pageSize,
|
|
103
|
+
});
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
//# sourceMappingURL=memory.js.map
|