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