@jshookmcp/jshook 0.3.0 → 0.3.1
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 +25 -50
- package/README.zh.md +25 -48
- package/dist/AntiCheatDetector-CGVGNfy5.mjs +1 -0
- package/dist/CacheAdapters-CdAxBmVW.mjs +1 -0
- package/dist/CodeInjector-BlgyqTOk.mjs +1 -0
- package/dist/ConsoleMonitor-Dkqc0HNi.mjs +490 -0
- package/dist/DOMInspector-BYY_EJ0C.mjs +95 -0
- package/dist/DarwinAPI-DC4HGGLl.mjs +1 -0
- package/dist/DetailedDataManager-BniBJlVv.mjs +1 -0
- package/dist/EventBus-DgciURGg.mjs +1 -0
- package/dist/EvidenceGraphBridge-BIfgB7HP.mjs +1 -0
- package/dist/ExtensionManager-erMpqcLk.mjs +1 -0
- package/dist/FingerprintManager-N7BZqjxP.mjs +1 -0
- package/dist/HardwareBreakpoint-OcJqNFVc.mjs +1 -0
- package/dist/HeapAnalyzer-CqAxZzeS.mjs +1 -0
- package/dist/{HookGeneratorBuilders.core.generators.storage-CTbB4Lcx.mjs → HookGeneratorBuilders.core.generators.storage-Bf1fbrNK.mjs} +66 -101
- package/dist/InstrumentationSession-DxXs0sCp.mjs +1 -0
- package/dist/MCPServer.search.handlers.domain-DVbWL1bT.mjs +1 -0
- package/dist/MemoryController-BaqstM5w.mjs +2 -0
- package/dist/MemoryScanSession-CaxAjZJf.mjs +1 -0
- package/dist/MemoryScanner-BLYnMJy6.mjs +1 -0
- package/dist/NativeMemoryManager.impl-CI554XbY.mjs +1 -0
- package/dist/NativeMemoryManager.utils-DM4NC3FE.mjs +1 -0
- package/dist/PEAnalyzer-DJyaJTQJ.mjs +1 -0
- package/dist/PageController-D9jVkH0i.mjs +1 -0
- package/dist/PointerChainEngine-5nF9eNlu.mjs +1 -0
- package/dist/PrerequisiteError-Bl3dK8XA.mjs +1 -0
- package/dist/ProcessRegistry-Hf12LlR9.mjs +1 -0
- package/dist/ResponseBuilder-B2lu4KEl.mjs +1 -0
- package/dist/ReverseEvidenceGraph-B931HeoW.mjs +2 -0
- package/dist/ScriptManager-fgqiALgj.mjs +7 -0
- package/dist/Speedhack-l6s8L2Qw.mjs +1 -0
- package/dist/StealthVerifier-Dhbj4B4P.mjs +1 -0
- package/dist/StructureAnalyzer-A-WamfYE.mjs +2 -0
- package/dist/ToolCatalog-D_IKl1Hu.mjs +1 -0
- package/dist/ToolError-DWU_z7gp.mjs +1 -0
- package/dist/ToolProbe-xsfALmN3.mjs +1 -0
- package/dist/ToolRegistry-B0Zs-phN.mjs +1 -0
- package/dist/ToolRouter.policy-CFHoN_Lw.mjs +4 -0
- package/dist/TraceRecorder-Dd8jLXpi.mjs +272 -0
- package/dist/VersionDetector-DMoUWyNm.mjs +9 -0
- package/dist/Win32API-Bhi5xFBe.mjs +1 -0
- package/dist/Win32Debug-CQteFL4F.mjs +1 -0
- package/dist/WorkflowEngine-CxEp2WXH.mjs +1 -0
- package/dist/analysis-BuR-NgX8.mjs +5 -0
- package/dist/{antidebug-BRKeyt27.mjs → antidebug-BOTZH6-0.mjs} +8 -259
- package/dist/artifactRetention-NBdncOEW.mjs +1 -0
- package/dist/artifacts-B5xQuEa_.mjs +1 -0
- package/dist/authorization-schema-B40obG1A.mjs +1 -0
- package/dist/betterSqlite3-CGaxz4AX.mjs +1 -0
- package/dist/binary-instrument-Cf9qqLlM.mjs +7 -0
- package/dist/bind-helpers-BlAOQrFQ.mjs +1 -0
- package/dist/boringssl-inspector-BST5vtKx.mjs +2 -0
- package/dist/browser-C4Le3xqA.mjs +11 -0
- package/dist/capabilities-DbYCv-HF.mjs +1 -0
- package/dist/chunk-C_pMuVsO.mjs +1 -0
- package/dist/collector-CKO8RPK8.mjs +1 -0
- package/dist/concurrency-CcK46d0h.mjs +1 -0
- package/dist/constants-Cp6hBrrx.mjs +1 -0
- package/dist/coordination-BbijHEHH.mjs +1 -0
- package/dist/debugger-CRJq_krh.mjs +1 -0
- package/dist/definitions-BGobEDQa.mjs +1 -0
- package/dist/definitions-BGwNSkVm.mjs +1 -0
- package/dist/definitions-BbxOUiP-.mjs +1 -0
- package/dist/definitions-CCP9gphV.mjs +1 -0
- package/dist/definitions-CIO9O-Sw.mjs +1 -0
- package/dist/definitions-CYFbewnd.mjs +1 -0
- package/dist/definitions-CdWEuIkI.mjs +1 -0
- package/dist/definitions-CoQFbggH.mjs +1 -0
- package/dist/definitions-CuJRsJ6N.mjs +1 -0
- package/dist/definitions-DI9YXsJk.mjs +1 -0
- package/dist/definitions-DJklW2sS.mjs +1 -0
- package/dist/definitions-DZ8uKusP.mjs +1 -0
- package/dist/definitions-Dds_zrWx.mjs +1 -0
- package/dist/definitions-Dgrg7f3D.mjs +1 -0
- package/dist/definitions-DtE0XLrT.mjs +1 -0
- package/dist/definitions-LaYTuwQd.mjs +26 -0
- package/dist/definitions-NoVp_9Pm.mjs +1 -0
- package/dist/definitions-OvGsfxdt.mjs +1 -0
- package/dist/definitions-jXPaVy4P.mjs +1 -0
- package/dist/encoding-DGcr6Aj_.mjs +2 -0
- package/dist/ensure-browser-core-Buls24LQ.mjs +1 -0
- package/dist/evidence-graph-bridge-B0yhGPcs.mjs +1 -0
- package/dist/factory-Cx_1LorX.mjs +1 -0
- package/dist/flat-target-session-CO5g78k3.mjs +1 -0
- package/dist/formatAddress-C7j2fDlM.mjs +1 -0
- package/dist/graphql-HLf3MS8H.mjs +62 -0
- package/dist/handlers-BLMa4X7l.mjs +54 -0
- package/dist/handlers-BP12ZsWc.mjs +4 -0
- package/dist/handlers-BZoPla6E.mjs +1 -0
- package/dist/handlers-BggKiVx9.mjs +2 -0
- package/dist/handlers-D3iev8g1.mjs +1 -0
- package/dist/handlers-D49r1-1P.mjs +1 -0
- package/dist/handlers-DCE45Ww8.mjs +2 -0
- package/dist/handlers-DW5AbYs5.mjs +5 -0
- package/dist/handlers-De5u62Ga2.mjs +1 -0
- package/dist/handlers-DmQzIc44.mjs +31 -0
- package/dist/handlers-DnJRGp7t.mjs +302 -0
- package/dist/handlers-Dv_runVv.mjs +2 -0
- package/dist/handlers-S9Ws0IGy.mjs +2 -0
- package/dist/{handlers-Dz9PYsCa.mjs → handlers-pVNpaw4A.mjs} +118 -904
- package/dist/handlers.impl-CD2_kOcC.mjs +1 -0
- package/dist/hooks-DDKppogd.mjs +600 -0
- package/dist/index.mjs +12 -5235
- package/dist/logger-sBC6IdRT.mjs +1 -0
- package/dist/maintenance-CutEO84j.mjs +1 -0
- package/dist/manifest-BFGxlDRh.mjs +123 -0
- package/dist/manifest-BPuE6oH2.mjs +1 -0
- package/dist/manifest-BXry5N09.mjs +1 -0
- package/dist/manifest-BeP_zJGb2.mjs +1 -0
- package/dist/manifest-C0g67k6U.mjs +1 -0
- package/dist/manifest-C1nZkTkO.mjs +1 -0
- package/dist/manifest-C7qV1z7F.mjs +1 -0
- package/dist/manifest-CDeUZGUZ.mjs +1 -0
- package/dist/manifest-CDiCtaQT.mjs +1 -0
- package/dist/manifest-CFn0359q2.mjs +1 -0
- package/dist/manifest-CGq4NpqH2.mjs +1 -0
- package/dist/manifest-CJMGt7Qy.mjs +1 -0
- package/dist/manifest-CRIJq4Hs.mjs +1 -0
- package/dist/manifest-C_hEIjSx.mjs +1 -0
- package/dist/manifest-CeQmtQOY.mjs +1 -0
- package/dist/manifest-Cq0j7GZt.mjs +1 -0
- package/dist/manifest-CtPmHAdn.mjs +1 -0
- package/dist/manifest-Cx2IVMUY.mjs +1 -0
- package/dist/manifest-D16xPXro.mjs +1 -0
- package/dist/manifest-D44TaRJU.mjs +1 -0
- package/dist/manifest-D610kxZr.mjs +2 -0
- package/dist/manifest-DC-SMF6b.mjs +1 -0
- package/dist/manifest-DD3rtxvV.mjs +1 -0
- package/dist/manifest-DKUorv5M.mjs +1 -0
- package/dist/manifest-DMJlcsTR.mjs +1 -0
- package/dist/manifest-DWUUWBz0.mjs +1 -0
- package/dist/manifest-De-6Wf2R.mjs +1 -0
- package/dist/manifest-Dgh0uDW-.mjs +1 -0
- package/dist/manifest-Dm0o3i2U.mjs +1 -0
- package/dist/manifest-DsVh7Y4U.mjs +1 -0
- package/dist/manifest-DtEFSRaq.mjs +1 -0
- package/dist/manifest-H-EpAyZQ.mjs +1 -0
- package/dist/manifest-ais9Afrw.mjs +1 -0
- package/dist/manifest-tmb54wmA.mjs +1 -0
- package/dist/manifest-yu2xiQqe.mjs +1 -0
- package/dist/manifest-zrbrpKCC.mjs +1 -0
- package/dist/matchesWildcardPattern-BGqLSmEs.mjs +1 -0
- package/dist/modules-p-PUNv9r.mjs +332 -0
- package/dist/mojo-ipc-VGlv3Qyp.mjs +9 -0
- package/dist/network-BjZ1Y-GB.mjs +7 -0
- package/dist/outputPaths-BonGThuc.mjs +2 -0
- package/dist/parse-args-Cuk7-xUt.mjs +1 -0
- package/dist/platform-C446Lf97.mjs +93 -0
- package/dist/playwright-cdp-fallback-BwVR-_T3.mjs +1 -0
- package/dist/process-C9f2A5zk.mjs +962 -0
- package/dist/proxy-CvRepxgV.mjs +1 -0
- package/dist/registry-DUHIPE-v.mjs +1 -0
- package/dist/response-C7rKQst4.mjs +1 -0
- package/dist/search-defaults-D2bY-rzH.mjs +1 -0
- package/dist/server/plugin-api.mjs +1 -293
- package/dist/shared-state-board-Cyg-xh_k.mjs +1 -0
- package/dist/sourcemap-D6Q1UuAp.mjs +1 -0
- package/dist/ssrf-policy-T96MR3r6.mjs +1 -0
- package/dist/streaming-CTX58tbb.mjs +1 -0
- package/dist/tool-builder-CI9914Tf.mjs +1 -0
- package/dist/transform-Cv9P2vVD.mjs +103 -0
- package/dist/types-CuyefmGT.mjs +1 -0
- package/dist/types-DtThH00r.mjs +1 -0
- package/dist/wasm-DaJa8J0V.mjs +174 -0
- package/dist/webcrack-CsLLJIs9.mjs +46 -0
- package/dist/workflow-CYIXtrWD.mjs +101 -0
- package/package.json +50 -78
- package/dist/AntiCheatDetector-CqGDXmfc.mjs +0 -350
- package/dist/CacheAdapters-jJFy20G-.mjs +0 -80
- package/dist/CodeInjector-BdjRfNx7.mjs +0 -150
- package/dist/ConsoleMonitor-DykL3IAw.mjs +0 -2269
- package/dist/DarwinAPI-ETyy0xyo.mjs +0 -363
- package/dist/DetailedDataManager-HT49OrvF.mjs +0 -217
- package/dist/EventBus-DFKvADm3.mjs +0 -141
- package/dist/EvidenceGraphBridge-318Oi0Lf.mjs +0 -153
- package/dist/ExtensionManager-BDMsY2Dz.mjs +0 -721
- package/dist/FingerprintManager-BN4UQWnX.mjs +0 -96
- package/dist/HardwareBreakpoint-Cc2AFq1Y.mjs +0 -239
- package/dist/HeapAnalyzer-DruMgsgj.mjs +0 -284
- package/dist/InstrumentationSession-DLH0vd-z.mjs +0 -244
- package/dist/MemoryController-CMtviNW_.mjs +0 -167
- package/dist/MemoryScanSession-ITgb_NMi.mjs +0 -278
- package/dist/MemoryScanner-CiL7Z3ey.mjs +0 -428
- package/dist/NativeMemoryManager.impl-D9Lkovvn.mjs +0 -485
- package/dist/NativeMemoryManager.utils-BBlAixF5.mjs +0 -165
- package/dist/PEAnalyzer-DMQ44gen.mjs +0 -385
- package/dist/PageController-BPJNqqBN.mjs +0 -431
- package/dist/PointerChainEngine-K7wN8Z-w.mjs +0 -325
- package/dist/PrerequisiteError-TuyZIs6n.mjs +0 -20
- package/dist/ProcessRegistry-zGg12QbE.mjs +0 -74
- package/dist/ResponseBuilder-CJXWmWNw.mjs +0 -143
- package/dist/ReverseEvidenceGraph-C02-gXOh.mjs +0 -269
- package/dist/ScriptManager-ZuWD-0Jg.mjs +0 -3003
- package/dist/Speedhack-D-z0umeT.mjs +0 -156
- package/dist/StealthVerifier-BWmPgQsv.mjs +0 -135
- package/dist/StructureAnalyzer-Cav5AVSL.mjs +0 -429
- package/dist/ToolCatalog-5OJdMiF0.mjs +0 -582
- package/dist/ToolError-jh9whhMd.mjs +0 -15
- package/dist/ToolProbe-DbCFGyrg.mjs +0 -45
- package/dist/ToolRegistry-B9krbTtI.mjs +0 -180
- package/dist/ToolRouter.policy-BGDAGyeH.mjs +0 -344
- package/dist/TraceRecorder-B41Z5XBj.mjs +0 -1286
- package/dist/VersionDetector-K3V4vGsw.mjs +0 -104
- package/dist/Win32API-C2kjj0ze.mjs +0 -346
- package/dist/Win32Debug-CKrGOTpo.mjs +0 -274
- package/dist/WorkflowEngine-DJ6M4opp.mjs +0 -569
- package/dist/analysis-BHeJW2Nb.mjs +0 -1234
- package/dist/artifactRetention-CPXkUJXp.mjs +0 -598
- package/dist/artifacts-DkfosXH3.mjs +0 -59
- package/dist/authorization-schema-DRqyJMSk.mjs +0 -31
- package/dist/betterSqlite3-DLSBZodi.mjs +0 -74
- package/dist/binary-instrument--V3MAhJ4.mjs +0 -971
- package/dist/bind-helpers-ClV34xdn.mjs +0 -42
- package/dist/boringssl-inspector-Bo_LOLaS.mjs +0 -180
- package/dist/browser-Dx3_S2cG.mjs +0 -4369
- package/dist/capabilities-CcHlvWgK.mjs +0 -33
- package/dist/chunk-CjcI7cDX.mjs +0 -15
- package/dist/concurrency-Drev_Vz9.mjs +0 -41
- package/dist/constants-CDZLOoVv.mjs +0 -534
- package/dist/coordination-DgItD9DL.mjs +0 -259
- package/dist/debugger-RS3RSAqs.mjs +0 -1288
- package/dist/definitions-BEoYofW5.mjs +0 -47
- package/dist/definitions-BRaefg3u.mjs +0 -365
- package/dist/definitions-BbkvZkiv.mjs +0 -96
- package/dist/definitions-BtWSHJ3o.mjs +0 -17
- package/dist/definitions-C1gCHO0i.mjs +0 -43
- package/dist/definitions-CDOg_b-l.mjs +0 -138
- package/dist/definitions-CVPD9hzZ.mjs +0 -54
- package/dist/definitions-Cea8Lgl7.mjs +0 -94
- package/dist/definitions-DAgIyjxM.mjs +0 -10
- package/dist/definitions-DJA27nsL.mjs +0 -66
- package/dist/definitions-DKPFU3LW.mjs +0 -25
- package/dist/definitions-DPRpZQ96.mjs +0 -47
- package/dist/definitions-DUE5gmdn.mjs +0 -18
- package/dist/definitions-DYVjOtxa.mjs +0 -26
- package/dist/definitions-DcYLVLCo.mjs +0 -37
- package/dist/definitions-Pp5LI2H4.mjs +0 -27
- package/dist/definitions-j9KdHVNR.mjs +0 -14
- package/dist/definitions-uzkjBwa7.mjs +0 -258
- package/dist/definitions-va-AnLuQ.mjs +0 -28
- package/dist/encoding-DJeqHmpd.mjs +0 -1079
- package/dist/evidence-graph-bridge-DcYizFk2.mjs +0 -136
- package/dist/factory-C90tBff6.mjs +0 -575
- package/dist/flat-target-session-Dgax2Cy3.mjs +0 -29
- package/dist/formatAddress-nnMvEohD.mjs +0 -17
- package/dist/graphql-CoHrhweh.mjs +0 -1197
- package/dist/handlers-4jmR0nMs.mjs +0 -898
- package/dist/handlers-BAHPxcch.mjs +0 -789
- package/dist/handlers-BOs9b907.mjs +0 -2600
- package/dist/handlers-BWXEy6ef.mjs +0 -917
- package/dist/handlers-Bndn6QvE.mjs +0 -111
- package/dist/handlers-BqC4bD4s.mjs +0 -681
- package/dist/handlers-BtYq60bM2.mjs +0 -276
- package/dist/handlers-BzgcB4iv.mjs +0 -799
- package/dist/handlers-CRyRWj2b.mjs +0 -859
- package/dist/handlers-CVv2H1uq.mjs +0 -592
- package/dist/handlers-Dl5a7JS4.mjs +0 -572
- package/dist/handlers-Dx2d7jt7.mjs +0 -2537
- package/dist/handlers-HujRKC3b.mjs +0 -661
- package/dist/handlers.impl-XWXkQfyi.mjs +0 -807
- package/dist/hooks-B1B8NRHL.mjs +0 -898
- package/dist/logger-Dh_xb7_2.mjs +0 -93
- package/dist/maintenance-PRMkLVRW.mjs +0 -835
- package/dist/manifest-67Bok-Si.mjs +0 -58
- package/dist/manifest-6lNTMZAB2.mjs +0 -87
- package/dist/manifest-B2duEHiH.mjs +0 -90
- package/dist/manifest-B6EY9Vm8.mjs +0 -57
- package/dist/manifest-B6nKSbyY.mjs +0 -95
- package/dist/manifest-BL8AQNPF.mjs +0 -106
- package/dist/manifest-BSZvJJmV.mjs +0 -47
- package/dist/manifest-BU7qzUyX.mjs +0 -418
- package/dist/manifest-Bl62e8WK.mjs +0 -49
- package/dist/manifest-Bo5cXjdt.mjs +0 -82
- package/dist/manifest-BpS4gtUK.mjs +0 -1347
- package/dist/manifest-Bv65_e2W.mjs +0 -101
- package/dist/manifest-BytNIF4Z.mjs +0 -117
- package/dist/manifest-C-xtsjS3.mjs +0 -81
- package/dist/manifest-CDYl7OhA.mjs +0 -66
- package/dist/manifest-CRZ3xmkD.mjs +0 -61
- package/dist/manifest-CoW6u4Tp.mjs +0 -132
- package/dist/manifest-Cq5zN_8A.mjs +0 -50
- package/dist/manifest-D7YZM_2e.mjs +0 -194
- package/dist/manifest-DE_VrAeQ.mjs +0 -314
- package/dist/manifest-DGsXSCpT.mjs +0 -39
- package/dist/manifest-DJ2vfEuW.mjs +0 -156
- package/dist/manifest-DPXDYhEu.mjs +0 -80
- package/dist/manifest-Dd4fQb0a.mjs +0 -322
- package/dist/manifest-Deq6opGg.mjs +0 -223
- package/dist/manifest-DfJTafJK.mjs +0 -37
- package/dist/manifest-DgOdgN_j.mjs +0 -50
- package/dist/manifest-DlbMW4v4.mjs +0 -47
- package/dist/manifest-DmVfbH0w.mjs +0 -374
- package/dist/manifest-Dog6Ddjr.mjs +0 -109
- package/dist/manifest-DvgU5FWb.mjs +0 -58
- package/dist/manifest-HsfDBs7j.mjs +0 -50
- package/dist/manifest-I8oQHvCG.mjs +0 -186
- package/dist/manifest-NvH_a-av.mjs +0 -786
- package/dist/manifest-cEJU1v0Z.mjs +0 -129
- package/dist/manifest-wOl5XLB12.mjs +0 -112
- package/dist/modules-tZozf0LQ.mjs +0 -10635
- package/dist/mojo-ipc-DXNEXEqb.mjs +0 -640
- package/dist/network-CPVvwvFg.mjs +0 -3852
- package/dist/outputPaths-um7lCRY3.mjs +0 -1141
- package/dist/parse-args-B4cY5Vx5.mjs +0 -39
- package/dist/platform-CYeFoTWp.mjs +0 -2161
- package/dist/process-BTbgcVc6.mjs +0 -1306
- package/dist/proxy-r8YN6nP1.mjs +0 -192
- package/dist/registry-Bl8ZQW61.mjs +0 -34
- package/dist/response-CWhh2aLo.mjs +0 -34
- package/dist/shared-state-board-BoZnSoj-.mjs +0 -586
- package/dist/sourcemap-BIDHUVXy.mjs +0 -934
- package/dist/ssrf-policy-Dsqd-DTX.mjs +0 -166
- package/dist/streaming-Dal6utPp.mjs +0 -725
- package/dist/tool-builder-BHJp32mV.mjs +0 -186
- package/dist/transform-DRVgGG90.mjs +0 -1011
- package/dist/types-Bx92KJfT.mjs +0 -4
- package/dist/types-DDBWs9UP.mjs +0 -37
- package/dist/wasm-BYx5UOeG.mjs +0 -1044
- package/dist/webcrack-Be0_FccV.mjs +0 -747
- package/dist/workflow-BpuKEtvn.mjs +0 -725
|
@@ -1,721 +0,0 @@
|
|
|
1
|
-
import { t as __exportAll } from "./chunk-CjcI7cDX.mjs";
|
|
2
|
-
import { t as logger } from "./logger-Dh_xb7_2.mjs";
|
|
3
|
-
import { t as allTools } from "./ToolCatalog-5OJdMiF0.mjs";
|
|
4
|
-
import { t as INSTALLED_EXTENSION_METADATA_FILENAME } from "./types-Bx92KJfT.mjs";
|
|
5
|
-
import { createHash } from "node:crypto";
|
|
6
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
7
|
-
import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
8
|
-
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
9
|
-
import { readFile } from "node:fs/promises";
|
|
10
|
-
import { glob } from "tinyglobby";
|
|
11
|
-
//#region src/server/extensions/ExtensionManager.roots.ts
|
|
12
|
-
/**
|
|
13
|
-
* Extension path resolution — root directories for plugins and workflows.
|
|
14
|
-
*/
|
|
15
|
-
/**
|
|
16
|
-
* Walk up the directory tree from the given start to find the project root
|
|
17
|
-
* (the nearest ancestor that contains a package.json).
|
|
18
|
-
*
|
|
19
|
-
* This is robust across both dev (`src/server/extensions/`) and production
|
|
20
|
-
* (`dist/src/server/extensions/`) layouts — no hard-coded level count needed.
|
|
21
|
-
*/
|
|
22
|
-
function findProjectRoot(startDir) {
|
|
23
|
-
let dir = startDir;
|
|
24
|
-
while (true) {
|
|
25
|
-
if (existsSync(join(dir, "package.json"))) return dir;
|
|
26
|
-
const parent = dirname(dir);
|
|
27
|
-
if (parent === dir) break;
|
|
28
|
-
dir = parent;
|
|
29
|
-
}
|
|
30
|
-
return resolve(startDir, "..", "..", "..", "..");
|
|
31
|
-
}
|
|
32
|
-
const EXTENSION_INSTALL_ROOT = findProjectRoot(fileURLToPath(new URL(".", import.meta.url)));
|
|
33
|
-
const DEFAULT_PLUGIN_ROOTS = [join(EXTENSION_INSTALL_ROOT, "plugins")];
|
|
34
|
-
const DEFAULT_WORKFLOW_ROOTS = [join(EXTENSION_INSTALL_ROOT, "workflows")];
|
|
35
|
-
function parseRoots(raw, fallback) {
|
|
36
|
-
const value = raw?.trim();
|
|
37
|
-
if (!value) return fallback;
|
|
38
|
-
const roots = value.split(",").map((item) => item.trim()).filter(Boolean);
|
|
39
|
-
return roots.length > 0 ? [...new Set(roots)] : fallback;
|
|
40
|
-
}
|
|
41
|
-
function resolveRoots(roots, baseDir = EXTENSION_INSTALL_ROOT) {
|
|
42
|
-
const resolved = roots.map((root) => isAbsolute(root) ? root : resolve(baseDir, root));
|
|
43
|
-
return [...new Set(resolved)].toSorted((a, b) => a.localeCompare(b));
|
|
44
|
-
}
|
|
45
|
-
//#endregion
|
|
46
|
-
//#region src/server/extensions/ExtensionManager.version.ts
|
|
47
|
-
/**
|
|
48
|
-
* Semver parsing and compatibility checks for extension version constraints.
|
|
49
|
-
*/
|
|
50
|
-
function parseVersionParts(version) {
|
|
51
|
-
const match = version.trim().match(/^(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/);
|
|
52
|
-
if (!match) return null;
|
|
53
|
-
return [
|
|
54
|
-
Number(match[1]),
|
|
55
|
-
Number(match[2]),
|
|
56
|
-
Number(match[3])
|
|
57
|
-
];
|
|
58
|
-
}
|
|
59
|
-
function compareVersion(a, b) {
|
|
60
|
-
const aa = parseVersionParts(a);
|
|
61
|
-
const bb = parseVersionParts(b);
|
|
62
|
-
if (!aa || !bb) return null;
|
|
63
|
-
const [aMajor, aMinor, aPatch] = aa;
|
|
64
|
-
const [bMajor, bMinor, bPatch] = bb;
|
|
65
|
-
if (aMajor > bMajor) return 1;
|
|
66
|
-
if (aMajor < bMajor) return -1;
|
|
67
|
-
if (aMinor > bMinor) return 1;
|
|
68
|
-
if (aMinor < bMinor) return -1;
|
|
69
|
-
if (aPatch > bPatch) return 1;
|
|
70
|
-
if (aPatch < bPatch) return -1;
|
|
71
|
-
return 0;
|
|
72
|
-
}
|
|
73
|
-
function isCompatibleVersion(range, currentVersion) {
|
|
74
|
-
const input = range.trim();
|
|
75
|
-
if (!input || input === "*") return true;
|
|
76
|
-
if (input.startsWith(">=")) {
|
|
77
|
-
const cmp = compareVersion(currentVersion, input.slice(2).trim());
|
|
78
|
-
return cmp !== null && cmp >= 0;
|
|
79
|
-
}
|
|
80
|
-
if (input.startsWith("^")) {
|
|
81
|
-
const base = input.slice(1).trim();
|
|
82
|
-
const cc = parseVersionParts(currentVersion);
|
|
83
|
-
const bb = parseVersionParts(base);
|
|
84
|
-
if (!cc || !bb) return false;
|
|
85
|
-
const cmp = compareVersion(currentVersion, base);
|
|
86
|
-
return cmp !== null && cmp >= 0 && cc[0] === bb[0];
|
|
87
|
-
}
|
|
88
|
-
if (input.startsWith("~")) {
|
|
89
|
-
const base = input.slice(1).trim();
|
|
90
|
-
const cc = parseVersionParts(currentVersion);
|
|
91
|
-
const bb = parseVersionParts(base);
|
|
92
|
-
if (!cc || !bb) return false;
|
|
93
|
-
const cmp = compareVersion(currentVersion, base);
|
|
94
|
-
return cmp !== null && cmp >= 0 && cc[0] === bb[0] && cc[1] === bb[1];
|
|
95
|
-
}
|
|
96
|
-
const cmp = compareVersion(currentVersion, input);
|
|
97
|
-
return cmp !== null && cmp === 0;
|
|
98
|
-
}
|
|
99
|
-
//#endregion
|
|
100
|
-
//#region src/server/extensions/ExtensionManager.integrity.ts
|
|
101
|
-
/**
|
|
102
|
-
* Plugin integrity verification — digest allowlists, env guards, compatibility checks.
|
|
103
|
-
*/
|
|
104
|
-
async function sha256Hex(filePath) {
|
|
105
|
-
const content = await readFile(filePath);
|
|
106
|
-
return createHash("sha256").update(content).digest("hex");
|
|
107
|
-
}
|
|
108
|
-
function normalizeHex(value) {
|
|
109
|
-
return value.trim().toLowerCase().replace(/^0x/, "");
|
|
110
|
-
}
|
|
111
|
-
function isTruthyEnv(value) {
|
|
112
|
-
return ["1", "true"].includes(value.toLowerCase());
|
|
113
|
-
}
|
|
114
|
-
function isPluginSignatureRequired() {
|
|
115
|
-
const raw = process.env.MCP_PLUGIN_SIGNATURE_REQUIRED;
|
|
116
|
-
if (raw === void 0 || raw.trim() === "") return process.env.NODE_ENV === "production";
|
|
117
|
-
return isTruthyEnv(raw);
|
|
118
|
-
}
|
|
119
|
-
function isPluginStrictLoad() {
|
|
120
|
-
const raw = process.env.MCP_PLUGIN_STRICT_LOAD;
|
|
121
|
-
if (raw === void 0 || raw.trim() === "") return isPluginSignatureRequired();
|
|
122
|
-
return isTruthyEnv(raw) || isPluginSignatureRequired();
|
|
123
|
-
}
|
|
124
|
-
function parseDigestAllowlist(raw) {
|
|
125
|
-
const value = raw?.trim();
|
|
126
|
-
if (!value) return /* @__PURE__ */ new Set();
|
|
127
|
-
return new Set(value.split(",").map((item) => normalizeHex(item)).filter((item) => item.length > 0));
|
|
128
|
-
}
|
|
129
|
-
async function verifyPluginIntegrity(plugin, currentVersion) {
|
|
130
|
-
const errors = [];
|
|
131
|
-
const warnings = [];
|
|
132
|
-
if (!isCompatibleVersion(plugin.compatibleCoreRange, currentVersion)) errors.push(`Plugin ${plugin.id} incompatible with core ${currentVersion}; requires ${plugin.compatibleCoreRange}`);
|
|
133
|
-
return {
|
|
134
|
-
ok: errors.length === 0,
|
|
135
|
-
errors,
|
|
136
|
-
warnings
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
//#endregion
|
|
140
|
-
//#region src/server/extensions/ExtensionManager.guards.ts
|
|
141
|
-
function isExtensionBuilder(value) {
|
|
142
|
-
if (!value || typeof value !== "object") return false;
|
|
143
|
-
const candidate = value;
|
|
144
|
-
return !!(typeof candidate.id === "string" && typeof candidate.version === "string" && Array.isArray(candidate.tools) && (candidate.workflows === void 0 || Array.isArray(candidate.workflows)));
|
|
145
|
-
}
|
|
146
|
-
function isWorkflowContract(value) {
|
|
147
|
-
if (!value || typeof value !== "object") return false;
|
|
148
|
-
const candidate = value;
|
|
149
|
-
return !!(candidate.kind === "workflow-contract" && candidate.version === 1 && typeof candidate.id === "string" && typeof candidate.displayName === "string" && typeof candidate.build === "function");
|
|
150
|
-
}
|
|
151
|
-
//#endregion
|
|
152
|
-
//#region src/server/extensions/ExtensionManager.discovery.ts
|
|
153
|
-
/**
|
|
154
|
-
* Extension file discovery — prefers installed registry metadata, then falls back
|
|
155
|
-
* to scanning plugin/workflow roots for legacy manifest files.
|
|
156
|
-
*/
|
|
157
|
-
function isJavaScriptLikeFile(file) {
|
|
158
|
-
return file.endsWith(".js") || file.endsWith(".mjs") || file.endsWith(".cjs");
|
|
159
|
-
}
|
|
160
|
-
function isTypeScriptLikeFile(file) {
|
|
161
|
-
return file.endsWith(".ts") || file.endsWith(".mts") || file.endsWith(".cts");
|
|
162
|
-
}
|
|
163
|
-
async function collectMatchingFiles(roots, matcher) {
|
|
164
|
-
const files = /* @__PURE__ */ new Set();
|
|
165
|
-
for (const root of roots) {
|
|
166
|
-
let matchedPaths;
|
|
167
|
-
try {
|
|
168
|
-
matchedPaths = await glob("**/*", {
|
|
169
|
-
cwd: root,
|
|
170
|
-
absolute: true,
|
|
171
|
-
onlyFiles: true,
|
|
172
|
-
dot: true,
|
|
173
|
-
ignore: [
|
|
174
|
-
"**/node_modules/**",
|
|
175
|
-
"**/.git/**",
|
|
176
|
-
"**/.pnpm/**"
|
|
177
|
-
]
|
|
178
|
-
});
|
|
179
|
-
} catch {
|
|
180
|
-
continue;
|
|
181
|
-
}
|
|
182
|
-
for (const file of matchedPaths) if (matcher(basename(file))) files.add(file);
|
|
183
|
-
}
|
|
184
|
-
return [...files].toSorted((a, b) => a.localeCompare(b));
|
|
185
|
-
}
|
|
186
|
-
function normalizeExtensionCandidateKey(root, file) {
|
|
187
|
-
const normalizedRoot = root.replace(/\\/g, "/").replace(/\/+/g, "/").replace(/^\/+|\/+$/g, "").toLowerCase();
|
|
188
|
-
const relDir = relative(root, dirname(file)).replace(/^[/\\]+/, "").replace(/\\/g, "/").replace(/\/+/g, "/").replace(/^\/+|\/+$/g, "").toLowerCase();
|
|
189
|
-
if (!relDir || relDir === "dist") return `${normalizedRoot}::`;
|
|
190
|
-
return `${normalizedRoot}::${relDir.endsWith("/dist") ? relDir.slice(0, -5) : relDir}`;
|
|
191
|
-
}
|
|
192
|
-
function extensionRank(candidate) {
|
|
193
|
-
if (candidate.isJs) return 0;
|
|
194
|
-
if (candidate.isTs) return 1;
|
|
195
|
-
return 2;
|
|
196
|
-
}
|
|
197
|
-
function isInstalledExtensionMetadata(value, kind) {
|
|
198
|
-
if (!value || typeof value !== "object") return false;
|
|
199
|
-
const record = value;
|
|
200
|
-
if (record.version !== 1 || record.kind !== kind) return false;
|
|
201
|
-
if (typeof record.slug !== "string" || typeof record.id !== "string") return false;
|
|
202
|
-
if (!record.source || typeof record.source !== "object") return false;
|
|
203
|
-
const source = record.source;
|
|
204
|
-
return typeof source.type === "string" && typeof source.repo === "string" && typeof source.ref === "string" && typeof source.commit === "string" && typeof source.subpath === "string" && typeof source.entry === "string";
|
|
205
|
-
}
|
|
206
|
-
async function collectInstalledEntryCandidates(roots, kind) {
|
|
207
|
-
const candidates = [];
|
|
208
|
-
for (const [rootIndex, root] of roots.entries()) {
|
|
209
|
-
const metadataFiles = await collectMatchingFiles([root], (filename) => filename === INSTALLED_EXTENSION_METADATA_FILENAME);
|
|
210
|
-
for (const metadataFile of metadataFiles) {
|
|
211
|
-
let metadataRaw;
|
|
212
|
-
try {
|
|
213
|
-
metadataRaw = JSON.parse(await readFile(metadataFile, "utf8"));
|
|
214
|
-
} catch {
|
|
215
|
-
continue;
|
|
216
|
-
}
|
|
217
|
-
if (!isInstalledExtensionMetadata(metadataRaw, kind)) continue;
|
|
218
|
-
const entryPath = metadataRaw.source.entry.trim();
|
|
219
|
-
if (!entryPath) continue;
|
|
220
|
-
const file = resolve(dirname(metadataFile), entryPath);
|
|
221
|
-
if (!existsSync(file)) continue;
|
|
222
|
-
candidates.push({
|
|
223
|
-
file,
|
|
224
|
-
key: normalizeExtensionCandidateKey(root, file),
|
|
225
|
-
isJs: isJavaScriptLikeFile(file),
|
|
226
|
-
isTs: isTypeScriptLikeFile(file),
|
|
227
|
-
rootIndex,
|
|
228
|
-
priority: 0
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
return candidates;
|
|
233
|
-
}
|
|
234
|
-
function deduplicateCandidates(candidates) {
|
|
235
|
-
const byKey = /* @__PURE__ */ new Map();
|
|
236
|
-
for (const candidate of candidates.toSorted((a, b) => a.file.localeCompare(b.file))) {
|
|
237
|
-
const existing = byKey.get(candidate.key);
|
|
238
|
-
if (!existing) {
|
|
239
|
-
byKey.set(candidate.key, candidate);
|
|
240
|
-
continue;
|
|
241
|
-
}
|
|
242
|
-
const existingRoot = existing.rootIndex;
|
|
243
|
-
const candidateRoot = candidate.rootIndex;
|
|
244
|
-
const existingPriority = existing.priority;
|
|
245
|
-
const candidatePriority = candidate.priority;
|
|
246
|
-
const existingExtRank = extensionRank(existing);
|
|
247
|
-
const candidateExtRank = extensionRank(candidate);
|
|
248
|
-
if (candidateRoot < existingRoot || candidateRoot === existingRoot && candidatePriority < existingPriority || candidateRoot === existingRoot && candidatePriority === existingPriority && candidateExtRank < existingExtRank || candidateRoot === existingRoot && candidatePriority === existingPriority && candidateExtRank === existingExtRank && candidate.file.localeCompare(existing.file) < 0) byKey.set(candidate.key, candidate);
|
|
249
|
-
}
|
|
250
|
-
return [...byKey.values()].map((item) => item.file).toSorted((a, b) => a.localeCompare(b));
|
|
251
|
-
}
|
|
252
|
-
async function discoverPluginFiles(pluginRoots) {
|
|
253
|
-
const candidates = await collectInstalledEntryCandidates(pluginRoots, "plugin");
|
|
254
|
-
for (const [rootIndex, root] of pluginRoots.entries()) {
|
|
255
|
-
const files = await collectMatchingFiles([root], (filename) => filename === "manifest.js" || filename === "manifest.ts");
|
|
256
|
-
for (const file of files) candidates.push({
|
|
257
|
-
file,
|
|
258
|
-
key: normalizeExtensionCandidateKey(root, file),
|
|
259
|
-
isJs: isJavaScriptLikeFile(file),
|
|
260
|
-
isTs: isTypeScriptLikeFile(file),
|
|
261
|
-
rootIndex,
|
|
262
|
-
priority: 1
|
|
263
|
-
});
|
|
264
|
-
}
|
|
265
|
-
return deduplicateCandidates(candidates);
|
|
266
|
-
}
|
|
267
|
-
async function discoverWorkflowFiles(workflowRoots) {
|
|
268
|
-
const candidates = await collectInstalledEntryCandidates(workflowRoots, "workflow");
|
|
269
|
-
for (const [rootIndex, root] of workflowRoots.entries()) {
|
|
270
|
-
const files = await collectMatchingFiles([root], (filename) => filename.endsWith(".workflow.js") || filename.endsWith(".workflow.mjs") || filename.endsWith(".workflow.cjs") || filename.endsWith(".workflow.ts") || filename.endsWith(".workflow.mts") || filename.endsWith(".workflow.cts") || filename === "workflow.js" || filename === "workflow.mjs" || filename === "workflow.cjs" || filename === "workflow.ts" || filename === "workflow.mts" || filename === "workflow.cts");
|
|
271
|
-
for (const file of files) candidates.push({
|
|
272
|
-
file,
|
|
273
|
-
key: normalizeExtensionCandidateKey(root, file),
|
|
274
|
-
isJs: isJavaScriptLikeFile(file),
|
|
275
|
-
isTs: isTypeScriptLikeFile(file),
|
|
276
|
-
rootIndex,
|
|
277
|
-
priority: 1
|
|
278
|
-
});
|
|
279
|
-
}
|
|
280
|
-
return deduplicateCandidates(candidates);
|
|
281
|
-
}
|
|
282
|
-
//#endregion
|
|
283
|
-
//#region src/server/extensions/ExtensionManager.lifecycle.ts
|
|
284
|
-
/**
|
|
285
|
-
* Extension lifecycle helpers — import, cleanup, config extraction, list building.
|
|
286
|
-
*/
|
|
287
|
-
function extractConfigValue(ctx, path, fallback) {
|
|
288
|
-
const segments = path.split(".").filter(Boolean);
|
|
289
|
-
let current = ctx.config;
|
|
290
|
-
for (const segment of segments) {
|
|
291
|
-
if (!current || typeof current !== "object") return fallback;
|
|
292
|
-
current = current[segment];
|
|
293
|
-
}
|
|
294
|
-
return current ?? fallback;
|
|
295
|
-
}
|
|
296
|
-
function createFreshImportUrl(modulePath, kind) {
|
|
297
|
-
const moduleUrl = new URL(pathToFileURL(modulePath).href);
|
|
298
|
-
moduleUrl.searchParams.set("reloadTs", String(Date.now()));
|
|
299
|
-
logger.debug(`[extensions] Loading fresh ${kind} module: ${modulePath}`);
|
|
300
|
-
return moduleUrl.href;
|
|
301
|
-
}
|
|
302
|
-
async function clearLoadedExtensionTools(ctx) {
|
|
303
|
-
let removed = 0;
|
|
304
|
-
for (const [pluginId, runtime] of ctx.extensionPluginRuntimeById.entries()) {
|
|
305
|
-
try {
|
|
306
|
-
if (runtime.plugin.onDeactivateHandler && runtime.state === "activated") {
|
|
307
|
-
await runtime.plugin.onDeactivateHandler(runtime.lifecycleContext);
|
|
308
|
-
runtime.state = "deactivated";
|
|
309
|
-
}
|
|
310
|
-
} catch (error) {
|
|
311
|
-
logger.warn(`Plugin onDeactivate failed for "${pluginId}":`, error);
|
|
312
|
-
}
|
|
313
|
-
try {
|
|
314
|
-
if (runtime.plugin.onDeactivateHandler) runtime.state = "unloaded";
|
|
315
|
-
} catch (error) {
|
|
316
|
-
logger.warn(`Plugin onUnload failed for "${pluginId}":`, error);
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
for (const record of ctx.extensionToolsByName.values()) {
|
|
320
|
-
try {
|
|
321
|
-
record.registeredTool?.remove();
|
|
322
|
-
} catch (error) {
|
|
323
|
-
logger.warn(`Failed to remove extension tool "${record.name}":`, error);
|
|
324
|
-
}
|
|
325
|
-
ctx.router.removeHandler(record.name);
|
|
326
|
-
ctx.activatedToolNames.delete(record.name);
|
|
327
|
-
ctx.activatedRegisteredTools.delete(record.name);
|
|
328
|
-
removed++;
|
|
329
|
-
}
|
|
330
|
-
ctx.extensionToolsByName.clear();
|
|
331
|
-
ctx.extensionPluginsById.clear();
|
|
332
|
-
ctx.extensionPluginRuntimeById.clear();
|
|
333
|
-
ctx.extensionWorkflowsById.clear();
|
|
334
|
-
ctx.extensionWorkflowRuntimeById.clear();
|
|
335
|
-
return removed;
|
|
336
|
-
}
|
|
337
|
-
function buildListResult(ctx, pluginRoots, workflowRoots) {
|
|
338
|
-
return {
|
|
339
|
-
pluginRoots,
|
|
340
|
-
workflowRoots,
|
|
341
|
-
pluginCount: ctx.extensionPluginsById.size,
|
|
342
|
-
workflowCount: ctx.extensionWorkflowsById.size,
|
|
343
|
-
toolCount: ctx.extensionToolsByName.size,
|
|
344
|
-
lastReloadAt: ctx.lastExtensionReloadAt,
|
|
345
|
-
plugins: [...ctx.extensionPluginsById.values()],
|
|
346
|
-
workflows: [...ctx.extensionWorkflowsById.values()],
|
|
347
|
-
tools: [...ctx.extensionToolsByName.values()].map((record) => ({
|
|
348
|
-
name: record.name,
|
|
349
|
-
domain: record.domain,
|
|
350
|
-
source: record.source
|
|
351
|
-
}))
|
|
352
|
-
};
|
|
353
|
-
}
|
|
354
|
-
//#endregion
|
|
355
|
-
//#region src/server/extensions/ExtensionManager.ts
|
|
356
|
-
var ExtensionManager_exports = /* @__PURE__ */ __exportAll({
|
|
357
|
-
ensureWorkflowsLoaded: () => ensureWorkflowsLoaded,
|
|
358
|
-
listExtensions: () => listExtensions,
|
|
359
|
-
reloadExtensions: () => reloadExtensions
|
|
360
|
-
});
|
|
361
|
-
function listExtensions(ctx) {
|
|
362
|
-
return buildListResult(ctx, resolveRoots(parseRoots(process.env.MCP_PLUGIN_ROOTS, DEFAULT_PLUGIN_ROOTS)), resolveRoots(parseRoots(process.env.MCP_WORKFLOW_ROOTS, DEFAULT_WORKFLOW_ROOTS)));
|
|
363
|
-
}
|
|
364
|
-
/**
|
|
365
|
-
* Parse a flat key: value YAML file (no nesting, no arrays).
|
|
366
|
-
* Returns a Record<string, string> of trimmed key/value pairs.
|
|
367
|
-
* Returns empty object on any error (file missing, malformed, etc.).
|
|
368
|
-
*/
|
|
369
|
-
function parseSimpleYaml(filePath) {
|
|
370
|
-
try {
|
|
371
|
-
const text = readFileSync(filePath, "utf-8");
|
|
372
|
-
const result = {};
|
|
373
|
-
for (const line of text.split(/\r?\n/)) {
|
|
374
|
-
const trimmed = line.trim();
|
|
375
|
-
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
376
|
-
const colonIdx = trimmed.indexOf(":");
|
|
377
|
-
if (colonIdx < 1) continue;
|
|
378
|
-
const key = trimmed.slice(0, colonIdx).trim();
|
|
379
|
-
const value = trimmed.slice(colonIdx + 1).trim();
|
|
380
|
-
if (key) result[key] = value;
|
|
381
|
-
}
|
|
382
|
-
return result;
|
|
383
|
-
} catch {
|
|
384
|
-
return {};
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
function findInstalledMetadataRoot(startDir) {
|
|
388
|
-
let currentDir = startDir;
|
|
389
|
-
while (true) {
|
|
390
|
-
if (existsSync(join(currentDir, ".jshook-install.json"))) return currentDir;
|
|
391
|
-
const parentDir = dirname(currentDir);
|
|
392
|
-
if (parentDir === currentDir) return null;
|
|
393
|
-
currentDir = parentDir;
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
function resolvePluginProjectRoot(pluginFile) {
|
|
397
|
-
const entryDir = dirname(pluginFile);
|
|
398
|
-
const metadataRoot = findInstalledMetadataRoot(entryDir);
|
|
399
|
-
if (metadataRoot) return metadataRoot;
|
|
400
|
-
if (basename(entryDir).toLowerCase() === "dist") return dirname(entryDir);
|
|
401
|
-
return entryDir;
|
|
402
|
-
}
|
|
403
|
-
let reloadMutex = Promise.resolve();
|
|
404
|
-
const lazyWorkflowLoadAttempted = /* @__PURE__ */ new WeakSet();
|
|
405
|
-
const STRICT_PLUGIN_ALLOWLIST_ERROR = "MCP_PLUGIN_ALLOWED_DIGESTS is required when MCP_PLUGIN_SIGNATURE_REQUIRED=true or MCP_PLUGIN_STRICT_LOAD=true. The digest allowlist is the only pre-import trust boundary — without it, plugin code executes before integrity verification. No plugins will be loaded.";
|
|
406
|
-
const MISSING_PLUGIN_ALLOWLIST_WARNING = "[extensions] Loading plugins WITHOUT MCP_PLUGIN_ALLOWED_DIGESTS allowlist. Plugin code will execute on import() before post-load integrity checks. Set MCP_PLUGIN_STRICT_LOAD=true to enforce allowlist requirement.";
|
|
407
|
-
async function withReloadMutex(operation) {
|
|
408
|
-
const prev = reloadMutex;
|
|
409
|
-
let resolve;
|
|
410
|
-
reloadMutex = new Promise((r) => {
|
|
411
|
-
resolve = r;
|
|
412
|
-
});
|
|
413
|
-
await prev;
|
|
414
|
-
try {
|
|
415
|
-
return await operation();
|
|
416
|
-
} finally {
|
|
417
|
-
resolve();
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
async function reloadExtensions(ctx) {
|
|
421
|
-
return withReloadMutex(() => reloadExtensionsInner(ctx));
|
|
422
|
-
}
|
|
423
|
-
async function ensureWorkflowsLoaded(ctx) {
|
|
424
|
-
if (ctx.extensionWorkflowRuntimeById.size > 0 || lazyWorkflowLoadAttempted.has(ctx)) return;
|
|
425
|
-
await withReloadMutex(async () => {
|
|
426
|
-
if (ctx.extensionWorkflowRuntimeById.size > 0 || lazyWorkflowLoadAttempted.has(ctx)) return;
|
|
427
|
-
lazyWorkflowLoadAttempted.add(ctx);
|
|
428
|
-
const warnings = [];
|
|
429
|
-
const errors = [];
|
|
430
|
-
const pluginRoots = resolveRoots(parseRoots(process.env.MCP_PLUGIN_ROOTS, DEFAULT_PLUGIN_ROOTS));
|
|
431
|
-
const workflowRoots = resolveRoots(parseRoots(process.env.MCP_WORKFLOW_ROOTS, DEFAULT_WORKFLOW_ROOTS));
|
|
432
|
-
await loadPluginWorkflowContributions(ctx, pluginRoots, warnings, errors);
|
|
433
|
-
await loadWorkflows(ctx, await discoverWorkflowFiles(workflowRoots), warnings, errors);
|
|
434
|
-
for (const warning of warnings) logger.warn(`[extensions] ${warning}`);
|
|
435
|
-
for (const error of errors) logger.error(`[extensions] ${error}`);
|
|
436
|
-
});
|
|
437
|
-
}
|
|
438
|
-
async function loadWorkflows(ctx, workflowFiles, warnings, errors) {
|
|
439
|
-
for (const workflowFile of workflowFiles) try {
|
|
440
|
-
const mod = await import(createFreshImportUrl(workflowFile, "workflow"));
|
|
441
|
-
const candidate = mod.default ?? mod;
|
|
442
|
-
if (!isWorkflowContract(candidate)) {
|
|
443
|
-
warnings.push(`Skip workflow file without valid WorkflowContract: ${workflowFile}`);
|
|
444
|
-
continue;
|
|
445
|
-
}
|
|
446
|
-
registerWorkflowContract(ctx, candidate, workflowFile, warnings);
|
|
447
|
-
} catch (error) {
|
|
448
|
-
errors.push(`Failed to import workflow file ${workflowFile}: ${String(error)}`);
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
function registerWorkflowContract(ctx, workflow, source, warnings) {
|
|
452
|
-
if (ctx.extensionWorkflowsById.has(workflow.id)) {
|
|
453
|
-
warnings.push(`Skip workflow "${workflow.id}" from ${source}: duplicate id`);
|
|
454
|
-
return false;
|
|
455
|
-
}
|
|
456
|
-
const record = {
|
|
457
|
-
id: workflow.id,
|
|
458
|
-
displayName: workflow.displayName,
|
|
459
|
-
source,
|
|
460
|
-
description: workflow.description,
|
|
461
|
-
tags: workflow.tags,
|
|
462
|
-
timeoutMs: workflow.timeoutMs,
|
|
463
|
-
defaultMaxConcurrency: workflow.defaultMaxConcurrency,
|
|
464
|
-
route: workflow.route
|
|
465
|
-
};
|
|
466
|
-
ctx.extensionWorkflowsById.set(record.id, record);
|
|
467
|
-
const runtimeRecord = {
|
|
468
|
-
workflow,
|
|
469
|
-
source,
|
|
470
|
-
route: workflow.route
|
|
471
|
-
};
|
|
472
|
-
ctx.extensionWorkflowRuntimeById.set(record.id, runtimeRecord);
|
|
473
|
-
return true;
|
|
474
|
-
}
|
|
475
|
-
function buildPluginRecord(plugin, pluginFile, loadedTools, loadedWorkflows) {
|
|
476
|
-
return {
|
|
477
|
-
id: plugin.id,
|
|
478
|
-
name: plugin.pluginName,
|
|
479
|
-
source: pluginFile,
|
|
480
|
-
author: plugin.pluginAuthor || void 0,
|
|
481
|
-
sourceRepo: plugin.pluginSourceRepo || void 0,
|
|
482
|
-
domains: [],
|
|
483
|
-
workflows: loadedWorkflows,
|
|
484
|
-
tools: loadedTools
|
|
485
|
-
};
|
|
486
|
-
}
|
|
487
|
-
async function loadPluginWorkflowContributions(ctx, pluginRoots, warnings, errors) {
|
|
488
|
-
const allowedDigests = parseDigestAllowlist(process.env.MCP_PLUGIN_ALLOWED_DIGESTS);
|
|
489
|
-
if (isPluginStrictLoad() && allowedDigests.size === 0) {
|
|
490
|
-
errors.push(STRICT_PLUGIN_ALLOWLIST_ERROR);
|
|
491
|
-
logger.error("[extensions] " + STRICT_PLUGIN_ALLOWLIST_ERROR);
|
|
492
|
-
return;
|
|
493
|
-
}
|
|
494
|
-
if (allowedDigests.size === 0) logger.warn(MISSING_PLUGIN_ALLOWLIST_WARNING);
|
|
495
|
-
const pluginFiles = await discoverPluginFiles(pluginRoots);
|
|
496
|
-
const coreVersion = ctx.config?.mcp?.version ?? "0.0.0";
|
|
497
|
-
for (const pluginFile of pluginFiles) {
|
|
498
|
-
let fileDigest;
|
|
499
|
-
try {
|
|
500
|
-
fileDigest = normalizeHex(await sha256Hex(pluginFile));
|
|
501
|
-
if (allowedDigests.size > 0 && !allowedDigests.has(fileDigest)) {
|
|
502
|
-
warnings.push(`Skip plugin file not in MCP_PLUGIN_ALLOWED_DIGESTS allowlist: ${pluginFile}`);
|
|
503
|
-
continue;
|
|
504
|
-
}
|
|
505
|
-
} catch (error) {
|
|
506
|
-
errors.push(`Failed to hash plugin file ${pluginFile}: ${String(error)}`);
|
|
507
|
-
continue;
|
|
508
|
-
}
|
|
509
|
-
let plugin;
|
|
510
|
-
try {
|
|
511
|
-
const mod = await import(createFreshImportUrl(pluginFile, "plugin"));
|
|
512
|
-
const candidate = mod.default ?? mod;
|
|
513
|
-
if (!isExtensionBuilder(candidate)) {
|
|
514
|
-
warnings.push(`Skip plugin file without valid ExtensionBuilder: ${pluginFile}`);
|
|
515
|
-
continue;
|
|
516
|
-
}
|
|
517
|
-
plugin = candidate;
|
|
518
|
-
} catch (error) {
|
|
519
|
-
errors.push(`Failed to import plugin file ${pluginFile}: ${String(error)}`);
|
|
520
|
-
continue;
|
|
521
|
-
}
|
|
522
|
-
const meta = parseSimpleYaml(join(resolvePluginProjectRoot(pluginFile), "meta.yaml"));
|
|
523
|
-
plugin.mergeMetadata(meta);
|
|
524
|
-
if (ctx.extensionPluginsById.has(plugin.id)) {
|
|
525
|
-
warnings.push(`Skip plugin "${plugin.id}" from ${pluginFile}: duplicate plugin id`);
|
|
526
|
-
continue;
|
|
527
|
-
}
|
|
528
|
-
try {
|
|
529
|
-
const verification = await verifyPluginIntegrity(plugin, coreVersion);
|
|
530
|
-
warnings.push(...verification.warnings);
|
|
531
|
-
if (!verification.ok) {
|
|
532
|
-
errors.push(...verification.errors);
|
|
533
|
-
continue;
|
|
534
|
-
}
|
|
535
|
-
} catch (error) {
|
|
536
|
-
errors.push(`Failed to verify plugin ${plugin.id}: ${String(error)}`);
|
|
537
|
-
continue;
|
|
538
|
-
}
|
|
539
|
-
const loadedWorkflows = [];
|
|
540
|
-
const pluginWorkflows = Array.isArray(plugin.workflows) ? plugin.workflows : [];
|
|
541
|
-
for (const candidate of pluginWorkflows) {
|
|
542
|
-
if (!isWorkflowContract(candidate)) {
|
|
543
|
-
warnings.push(`Skip invalid workflow contribution from plugin "${plugin.id}" in ${pluginFile}`);
|
|
544
|
-
continue;
|
|
545
|
-
}
|
|
546
|
-
if (registerWorkflowContract(ctx, candidate, `${pluginFile}#workflow:${candidate.id}`, warnings)) loadedWorkflows.push(candidate.id);
|
|
547
|
-
}
|
|
548
|
-
if (loadedWorkflows.length === 0) continue;
|
|
549
|
-
ctx.extensionPluginsById.set(plugin.id, buildPluginRecord(plugin, pluginFile, [], loadedWorkflows));
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
async function reloadExtensionsInner(ctx) {
|
|
553
|
-
const warnings = [];
|
|
554
|
-
const errors = [];
|
|
555
|
-
const removedTools = await clearLoadedExtensionTools(ctx);
|
|
556
|
-
const pluginRoots = resolveRoots(parseRoots(process.env.MCP_PLUGIN_ROOTS, DEFAULT_PLUGIN_ROOTS));
|
|
557
|
-
const workflowRoots = resolveRoots(parseRoots(process.env.MCP_WORKFLOW_ROOTS, DEFAULT_WORKFLOW_ROOTS));
|
|
558
|
-
const allowedDigests = parseDigestAllowlist(process.env.MCP_PLUGIN_ALLOWED_DIGESTS);
|
|
559
|
-
if (isPluginStrictLoad() && allowedDigests.size === 0) {
|
|
560
|
-
const msg = STRICT_PLUGIN_ALLOWLIST_ERROR;
|
|
561
|
-
errors.push(msg);
|
|
562
|
-
logger.error("[extensions] " + msg);
|
|
563
|
-
await loadWorkflows(ctx, await discoverWorkflowFiles(workflowRoots), warnings, errors);
|
|
564
|
-
ctx.lastExtensionReloadAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
565
|
-
return {
|
|
566
|
-
...buildListResult(ctx, pluginRoots, workflowRoots),
|
|
567
|
-
addedTools: 0,
|
|
568
|
-
removedTools,
|
|
569
|
-
warnings,
|
|
570
|
-
errors
|
|
571
|
-
};
|
|
572
|
-
}
|
|
573
|
-
if (allowedDigests.size === 0) logger.warn(MISSING_PLUGIN_ALLOWLIST_WARNING);
|
|
574
|
-
const baseToolNames = new Set(allTools.map((tool) => tool.name));
|
|
575
|
-
const pluginFiles = await discoverPluginFiles(pluginRoots);
|
|
576
|
-
const coreVersion = ctx.config?.mcp?.version ?? "0.0.0";
|
|
577
|
-
for (const pluginFile of pluginFiles) {
|
|
578
|
-
let fileDigest;
|
|
579
|
-
try {
|
|
580
|
-
fileDigest = normalizeHex(await sha256Hex(pluginFile));
|
|
581
|
-
if (allowedDigests.size > 0 && !allowedDigests.has(fileDigest)) {
|
|
582
|
-
warnings.push(`Skip plugin file not in MCP_PLUGIN_ALLOWED_DIGESTS allowlist: ${pluginFile}`);
|
|
583
|
-
continue;
|
|
584
|
-
}
|
|
585
|
-
} catch (error) {
|
|
586
|
-
errors.push(`Failed to hash plugin file ${pluginFile}: ${String(error)}`);
|
|
587
|
-
continue;
|
|
588
|
-
}
|
|
589
|
-
let plugin;
|
|
590
|
-
try {
|
|
591
|
-
const mod = await import(createFreshImportUrl(pluginFile, "plugin"));
|
|
592
|
-
const candidate = mod.default ?? mod;
|
|
593
|
-
if (!isExtensionBuilder(candidate)) {
|
|
594
|
-
warnings.push(`Skip plugin file without valid ExtensionBuilder: ${pluginFile}`);
|
|
595
|
-
continue;
|
|
596
|
-
}
|
|
597
|
-
plugin = candidate;
|
|
598
|
-
} catch (error) {
|
|
599
|
-
errors.push(`Failed to import plugin file ${pluginFile}: ${String(error)}`);
|
|
600
|
-
continue;
|
|
601
|
-
}
|
|
602
|
-
const pluginProjectRoot = resolvePluginProjectRoot(pluginFile);
|
|
603
|
-
const meta = parseSimpleYaml(join(pluginProjectRoot, "meta.yaml"));
|
|
604
|
-
plugin.mergeMetadata(meta);
|
|
605
|
-
if (ctx.extensionPluginsById.has(plugin.id)) {
|
|
606
|
-
warnings.push(`Skip plugin "${plugin.id}" from ${pluginFile}: duplicate plugin id`);
|
|
607
|
-
continue;
|
|
608
|
-
}
|
|
609
|
-
try {
|
|
610
|
-
const verification = await verifyPluginIntegrity(plugin, coreVersion);
|
|
611
|
-
warnings.push(...verification.warnings);
|
|
612
|
-
if (!verification.ok) {
|
|
613
|
-
errors.push(...verification.errors);
|
|
614
|
-
continue;
|
|
615
|
-
}
|
|
616
|
-
} catch (error) {
|
|
617
|
-
errors.push(`Failed to verify plugin ${plugin.id}: ${String(error)}`);
|
|
618
|
-
continue;
|
|
619
|
-
}
|
|
620
|
-
const runtimeData = /* @__PURE__ */ new Map();
|
|
621
|
-
const metrics = /* @__PURE__ */ new Set();
|
|
622
|
-
let pluginState = "loaded";
|
|
623
|
-
const allowInvokeAll = plugin.allowedTools.includes("*");
|
|
624
|
-
const lifecycleContext = {
|
|
625
|
-
pluginId: plugin.id,
|
|
626
|
-
pluginRoot: pluginProjectRoot,
|
|
627
|
-
config: ctx.config,
|
|
628
|
-
get state() {
|
|
629
|
-
return pluginState;
|
|
630
|
-
},
|
|
631
|
-
registerMetric(metricName) {
|
|
632
|
-
metrics.add(metricName);
|
|
633
|
-
},
|
|
634
|
-
async invokeTool(name, args = {}) {
|
|
635
|
-
if (typeof name !== "string" || name.length === 0) throw new Error("invokeTool requires a non-empty tool name");
|
|
636
|
-
if (!allowInvokeAll && !plugin.allowedTools.includes(name)) throw new Error(`Plugin "${plugin.id}" is not allowed to invoke "${name}". Declare it in allowTool calls.`);
|
|
637
|
-
if (!baseToolNames.has(name)) throw new Error(`Plugin "${plugin.id}" can only invoke built-in tools. "${name}" is not built-in.`);
|
|
638
|
-
if (!ctx.router.has(name)) throw new Error(`Tool "${name}" is not available in the current active profile.`);
|
|
639
|
-
return ctx.executeToolWithTracking(name, args ?? {});
|
|
640
|
-
},
|
|
641
|
-
hasPermission(_capability) {
|
|
642
|
-
return true;
|
|
643
|
-
},
|
|
644
|
-
getConfig(path, fallback) {
|
|
645
|
-
return extractConfigValue(ctx, path, fallback);
|
|
646
|
-
},
|
|
647
|
-
setRuntimeData(key, value) {
|
|
648
|
-
runtimeData.set(key, value);
|
|
649
|
-
},
|
|
650
|
-
getRuntimeData(key) {
|
|
651
|
-
return runtimeData.get(key);
|
|
652
|
-
}
|
|
653
|
-
};
|
|
654
|
-
const runtimeRecord = {
|
|
655
|
-
plugin,
|
|
656
|
-
lifecycleContext,
|
|
657
|
-
state: pluginState,
|
|
658
|
-
source: pluginFile
|
|
659
|
-
};
|
|
660
|
-
try {
|
|
661
|
-
if (plugin.onLoadHandler) await plugin.onLoadHandler(lifecycleContext);
|
|
662
|
-
pluginState = "loaded";
|
|
663
|
-
runtimeRecord.state = pluginState;
|
|
664
|
-
if (plugin.onValidateHandler) {
|
|
665
|
-
const validation = await plugin.onValidateHandler(lifecycleContext);
|
|
666
|
-
if (!validation.valid) {
|
|
667
|
-
warnings.push(`Plugin ${plugin.id} validation failed: ${validation.errors.join("; ")}`);
|
|
668
|
-
continue;
|
|
669
|
-
}
|
|
670
|
-
pluginState = "validated";
|
|
671
|
-
runtimeRecord.state = pluginState;
|
|
672
|
-
}
|
|
673
|
-
if (plugin.onActivateHandler) {
|
|
674
|
-
await plugin.onActivateHandler(lifecycleContext);
|
|
675
|
-
pluginState = "activated";
|
|
676
|
-
runtimeRecord.state = pluginState;
|
|
677
|
-
}
|
|
678
|
-
ctx.extensionPluginRuntimeById.set(plugin.id, runtimeRecord);
|
|
679
|
-
} catch (error) {
|
|
680
|
-
try {
|
|
681
|
-
if (plugin.onDeactivateHandler && pluginState === "activated") {
|
|
682
|
-
await plugin.onDeactivateHandler(lifecycleContext);
|
|
683
|
-
pluginState = "deactivated";
|
|
684
|
-
runtimeRecord.state = pluginState;
|
|
685
|
-
}
|
|
686
|
-
} catch (deactivateError) {
|
|
687
|
-
logger.warn(`Plugin onDeactivate failed during rollback for ${plugin.id}:`, deactivateError);
|
|
688
|
-
}
|
|
689
|
-
errors.push(`Plugin lifecycle failed for ${plugin.id}: ${String(error)}`);
|
|
690
|
-
continue;
|
|
691
|
-
}
|
|
692
|
-
const loadedTools = plugin.tools.map((t) => t.name);
|
|
693
|
-
const loadedWorkflows = [];
|
|
694
|
-
const pluginWorkflows = Array.isArray(plugin.workflows) ? plugin.workflows : [];
|
|
695
|
-
for (const candidate of pluginWorkflows) {
|
|
696
|
-
if (!isWorkflowContract(candidate)) {
|
|
697
|
-
warnings.push(`Skip invalid workflow contribution from plugin "${plugin.id}" in ${pluginFile}`);
|
|
698
|
-
continue;
|
|
699
|
-
}
|
|
700
|
-
if (registerWorkflowContract(ctx, candidate, `${pluginFile}#workflow:${candidate.id}`, warnings)) loadedWorkflows.push(candidate.id);
|
|
701
|
-
}
|
|
702
|
-
const record = buildPluginRecord(plugin, pluginFile, loadedTools, loadedWorkflows);
|
|
703
|
-
ctx.extensionPluginsById.set(record.id, record);
|
|
704
|
-
}
|
|
705
|
-
await loadWorkflows(ctx, await discoverWorkflowFiles(workflowRoots), warnings, errors);
|
|
706
|
-
if (ctx.extensionToolsByName.size > 0 || removedTools > 0) try {
|
|
707
|
-
await ctx.server.sendToolListChanged();
|
|
708
|
-
} catch (error) {
|
|
709
|
-
logger.warn("sendToolListChanged failed after extension reload:", error);
|
|
710
|
-
}
|
|
711
|
-
ctx.lastExtensionReloadAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
712
|
-
return {
|
|
713
|
-
...buildListResult(ctx, pluginRoots, workflowRoots),
|
|
714
|
-
addedTools: ctx.extensionToolsByName.size,
|
|
715
|
-
removedTools,
|
|
716
|
-
warnings,
|
|
717
|
-
errors
|
|
718
|
-
};
|
|
719
|
-
}
|
|
720
|
-
//#endregion
|
|
721
|
-
export { reloadExtensions as i, ensureWorkflowsLoaded as n, listExtensions as r, ExtensionManager_exports as t };
|