promptfoo 0.125.0 → 0.125.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 +3 -3
- package/dist/src/accounts-BOxNYxVN.js +229 -0
- package/dist/src/accounts-BezXLdAW.cjs +276 -0
- package/dist/src/accounts-CPISMNlh.js +227 -0
- package/dist/src/accounts-vJBPs89d.js +222 -0
- package/dist/src/agentWorkspace-Bom7SpF2.js +592 -0
- package/dist/src/agentWorkspace-CEomAcws.cjs +619 -0
- package/dist/src/agentWorkspace-DPtMLrlI.js +593 -0
- package/dist/src/agentWorkspace-DkAeEYLz.js +592 -0
- package/dist/src/agentic-utils-121v7mJo.js +197 -0
- package/dist/src/agentic-utils-CBWD_rcd.cjs +237 -0
- package/dist/src/agentic-utils-D8wyQwPG.js +198 -0
- package/dist/src/agentic-utils-EP1QkQCs.js +197 -0
- package/dist/src/agents-Br3ah8iA.js +778 -0
- package/dist/src/agents-C5wj_UTv.js +777 -0
- package/dist/src/agents-C9Efp4mC.js +266 -0
- package/dist/src/agents-CQYwaVCO.js +267 -0
- package/dist/src/agents-CjCd4Vo8.js +777 -0
- package/dist/src/agents-DRWiCHYz.js +266 -0
- package/dist/src/agents-F2rhx0iL.cjs +266 -0
- package/dist/src/agents-RTYyy69h.cjs +777 -0
- package/dist/src/agents-api-CY72O86e.js +711 -0
- package/dist/src/agents-api-CsSsC21b.js +710 -0
- package/dist/src/agents-api-DN-ibDc8.js +710 -0
- package/dist/src/agents-api-cNDsCCv1.cjs +710 -0
- package/dist/src/ai.studio-Bj6Dyi6o.js +2 -0
- package/dist/src/ai.studio-C-e0LzyW.js +369 -0
- package/dist/src/ai.studio-CR8M6Aqf.cjs +391 -0
- package/dist/src/ai.studio-YKk7F0tv.js +374 -0
- package/dist/src/aimlapi-BGunsuTu.js +30 -0
- package/dist/src/aimlapi-BkzcfBsL.js +29 -0
- package/dist/src/aimlapi-DXFcEdP8.js +28 -0
- package/dist/src/aimlapi-r-8iCcAw.cjs +29 -0
- package/dist/src/anthropicMessages-7-YNRgQR.js +114 -0
- package/dist/src/anthropicMessages-C8m9Whrd.cjs +114 -0
- package/dist/src/anthropicMessages-DS1vXD0f.js +115 -0
- package/dist/src/anthropicMessages-DmvGkz-0.js +114 -0
- package/dist/src/app/assets/index-CrdXWTXm.js +392 -0
- package/dist/src/app/browser-inventory.json +4 -4
- package/dist/src/app/index.html +1 -1
- package/dist/src/audio-BLgFgyw-.cjs +104 -0
- package/dist/src/audio-C2sibT7Q.js +104 -0
- package/dist/src/audio-DXEXb03H.js +104 -0
- package/dist/src/audio-TkvojpwO.js +105 -0
- package/dist/src/authoritativeMarkupInjection-BAT9B-Wj.js +79 -0
- package/dist/src/authoritativeMarkupInjection-B_yHETKT.js +78 -0
- package/dist/src/authoritativeMarkupInjection-BfjecQ3Q.cjs +78 -0
- package/dist/src/authoritativeMarkupInjection-D-hDKrhD.js +78 -0
- package/dist/src/aws-B_9unI_-.js +1149 -0
- package/dist/src/aws-D0FY3qkc.js +2433 -0
- package/dist/src/aws-Dh1-a90n.js +2433 -0
- package/dist/src/aws-qskKGczY.cjs +2454 -0
- package/dist/src/base-CaWlBkVz.js +462 -0
- package/dist/src/base-CcViNwVs.js +255 -0
- package/dist/src/base-CsVbPJkU.cjs +467 -0
- package/dist/src/base-DNK9py25.js +255 -0
- package/dist/src/base-DlH5tjEZ.js +463 -0
- package/dist/src/base-DyLOZ_iX.js +256 -0
- package/dist/src/base-RRYXfG7l.cjs +305 -0
- package/dist/src/base-oXVgvtVn.js +462 -0
- package/dist/src/bedrock-BP17ZXbR.cjs +2 -0
- package/dist/src/bedrock-CsjM1dOt.js +1300 -0
- package/dist/src/bedrock-CzPwj8GO.js +1 -0
- package/dist/src/bedrock-eGXZw8oh.js +1 -0
- package/dist/src/bedrock-tSWjmXdj.js +2 -0
- package/dist/src/bestOfN-BQ93nUKV.js +160 -0
- package/dist/src/bestOfN-ByGGwlcb.cjs +161 -0
- package/dist/src/bestOfN-CbP7csHr.js +159 -0
- package/dist/src/bestOfN-xn6i2Xs2.js +159 -0
- package/dist/src/cache-CHq9hPz9.js +2 -0
- package/dist/src/cache-D-9eZN3I.js +754 -0
- package/dist/src/cache-DSm6t1MZ.js +741 -0
- package/dist/src/cache-DeF5zIEN.js +782 -0
- package/dist/src/cache-iG87oYan.cjs +856 -0
- package/dist/src/cerebras-CTtM4yMH.js +98 -0
- package/dist/src/cerebras-CogcJ3ce.cjs +97 -0
- package/dist/src/cerebras-DmkRypTh.js +97 -0
- package/dist/src/cerebras-GUp0B8JY.js +97 -0
- package/dist/src/chat-B4ihd3fw.js +226 -0
- package/dist/src/chat-B9cAQ_rS.js +227 -0
- package/dist/src/chat-BU7EB4R-.cjs +76 -0
- package/dist/src/chat-C8yxJ2-s.cjs +226 -0
- package/dist/src/chat-ClTFM3qq.js +934 -0
- package/dist/src/chat-Cnq0Is68.js +123 -0
- package/dist/src/chat-CwrurR57.cjs +987 -0
- package/dist/src/chat-D0lsojPv.js +124 -0
- package/dist/src/chat-D7_8yFIx.js +76 -0
- package/dist/src/chat-DCoETlLG.js +226 -0
- package/dist/src/chat-DHE7xaTR.js +76 -0
- package/dist/src/chat-DmhT6rz2.cjs +123 -0
- package/dist/src/chat-DvfMe8KI.js +123 -0
- package/dist/src/chat-DwP5kVnl.js +935 -0
- package/dist/src/chat-MB7U-QE2.js +77 -0
- package/dist/src/chat-zCWuBKAj.js +934 -0
- package/dist/src/claude-agent-sdk-23bIVI75.js +999 -0
- package/dist/src/claude-agent-sdk-BACpnIJi.cjs +1005 -0
- package/dist/src/claude-agent-sdk-BlYlmcEr.js +999 -0
- package/dist/src/claude-agent-sdk-Cr-jqJZl.js +1000 -0
- package/dist/src/client-BktbiPx9.cjs +357 -0
- package/dist/src/client-CjztEBmW.js +351 -0
- package/dist/src/client-mRs2UvH9.js +351 -0
- package/dist/src/client-uCz0ydjC.js +352 -0
- package/dist/src/cloud-BtYF555L.js +2 -0
- package/dist/src/cloud-CVSUEmAy.js +287 -0
- package/dist/src/cloud-D7Ifb-Fl.js +599 -0
- package/dist/src/cloud-DB5oK5NX.cjs +666 -0
- package/dist/src/cloud-ecu989Em.js +657 -0
- package/dist/src/cloud-ramjGURF.js +599 -0
- package/dist/src/cloudflare-ai-BfDkIjO8.js +133 -0
- package/dist/src/cloudflare-ai-D9HaXz1M.js +134 -0
- package/dist/src/cloudflare-ai-HrD5WUK8.js +132 -0
- package/dist/src/cloudflare-ai-Qdj8aKP2.cjs +133 -0
- package/dist/src/cloudflare-gateway-C81uKETQ.js +276 -0
- package/dist/src/cloudflare-gateway-CX5Z9X0k.js +276 -0
- package/dist/src/cloudflare-gateway-DW-og8gb.js +277 -0
- package/dist/src/cloudflare-gateway-iRyXo9n2.cjs +276 -0
- package/dist/src/codex-app-server-BFIrSgdy.js +2155 -0
- package/dist/src/codex-app-server-By1eDVD4.js +2 -0
- package/dist/src/codex-app-server-DKsF4qmY.js +2156 -0
- package/dist/src/codex-app-server-n7n4uZzC.cjs +2162 -0
- package/dist/src/codex-app-server-xAoELVxu.js +2155 -0
- package/dist/src/codex-sdk-C2ZLxa6r.js +1552 -0
- package/dist/src/codex-sdk-CzG02tQt.cjs +1588 -0
- package/dist/src/codex-sdk-DNxAOhiV.js +2 -0
- package/dist/src/codex-sdk-DT3aQezK.js +1557 -0
- package/dist/src/codex-sdk-pw9DpcJP.js +1557 -0
- package/dist/src/codex-security-B0bLmXJF.js +406 -0
- package/dist/src/codex-security-BhYs-3iY.js +406 -0
- package/dist/src/codex-security-DQcOfxYW.js +407 -0
- package/dist/src/codex-security-DsCWr6GZ.cjs +411 -0
- package/dist/src/cometapi-B16asPLX.cjs +55 -0
- package/dist/src/cometapi-MgTbZA_H.js +53 -0
- package/dist/src/cometapi-i33vKzPV.js +55 -0
- package/dist/src/cometapi-wK2UWK0X.js +56 -0
- package/dist/src/compatible-COj8ZCy9.js +24 -0
- package/dist/src/compatible-CRSsIUGb.cjs +24 -0
- package/dist/src/compatible-Dy1vCcNF.js +25 -0
- package/dist/src/compatible-aUItQrpk.js +24 -0
- package/dist/src/completion-Bn7COFMk.js +119 -0
- package/dist/src/completion-Bub2N48b.js +118 -0
- package/dist/src/completion-Cf2G9OlM.cjs +123 -0
- package/dist/src/conversationScoring-BiTYJ9MD.js +374 -0
- package/dist/src/conversationScoring-CKL47uIu.js +373 -0
- package/dist/src/conversationScoring-DCvQKp6k.cjs +398 -0
- package/dist/src/conversationScoring-DLNJtDZI.js +373 -0
- package/dist/src/crescendo-5iLk9WiJ.cjs +734 -0
- package/dist/src/crescendo-B5ylG7kW.js +733 -0
- package/dist/src/crescendo-BD7k6TIx.js +730 -0
- package/dist/src/crescendo-DqO2Ch0P.js +732 -0
- package/dist/src/custom-8rv_aG2K.js +617 -0
- package/dist/src/custom-BOtrIHFN.js +619 -0
- package/dist/src/custom-BvN8seCs.cjs +620 -0
- package/dist/src/custom-Cld9v_Y4.js +618 -0
- package/dist/src/dataUrl-BdHUey2l.cjs +3187 -0
- package/dist/src/dataUrl-DBMifX6C.js +3133 -0
- package/dist/src/dataUrl-DUSr-ZvI.js +2868 -0
- package/dist/src/decisions-CLazADTw.js +516 -0
- package/dist/src/decisions-D5X93awq.js +515 -0
- package/dist/src/decisions-DmaeArEn.js +515 -0
- package/dist/src/decisions-DyQJyHJz.cjs +514 -0
- package/dist/src/docker-C9NYQFeu.js +94 -0
- package/dist/src/docker-CGq3mMol.cjs +94 -0
- package/dist/src/docker-DOm7nmpl.js +95 -0
- package/dist/src/docker-skFN-Kfo.js +93 -0
- package/dist/src/embedding-0UbzkTk9.js +82 -0
- package/dist/src/embedding-DTrSDt7k.cjs +86 -0
- package/dist/src/embedding-j2ORNl5H.js +81 -0
- package/dist/src/envoy-BvS8XTQO.js +60 -0
- package/dist/src/envoy-Cn6Hzw0e.js +60 -0
- package/dist/src/envoy-DC3KWcUY.cjs +60 -0
- package/dist/src/envoy-DetHjo5B.js +61 -0
- package/dist/src/eval-Cer9dPe9.js +1920 -0
- package/dist/src/eval-CytYmI2Z.js +2 -0
- package/dist/src/evalResult-DzOq5wt9.js +1300 -0
- package/dist/src/evaluator-BxCmvGtv.js +10578 -0
- package/dist/src/evaluator-Ct7YDELm.js +2 -0
- package/dist/src/evaluatorHelpers-B_AwIrsl.js +600 -0
- package/dist/src/evaluatorHelpers-D1Tpc7gd.js +601 -0
- package/dist/src/evaluatorHelpers-DRR3rLq0.cjs +622 -0
- package/dist/src/fetch-B5KxZuKu.js +1559 -0
- package/dist/src/fetch-BG_Jl7ty.js +1556 -0
- package/dist/src/fetch-BKlgUg08.cjs +1853 -0
- package/dist/src/fetch-C9Fsja0R.js +2 -0
- package/dist/src/fetch-CM7P8KWA.cjs +14 -0
- package/dist/src/fetch-D0FkIB3G.js +9 -0
- package/dist/src/fetch-RXHsZF-J.js +1280 -0
- package/dist/src/fetch-rSECy8jR.js +10 -0
- package/dist/src/functionCallbackUtils-BB9tmf6a.cjs +262 -0
- package/dist/src/functionCallbackUtils-BXk2iY01.js +246 -0
- package/dist/src/functionCallbackUtils-B_cYp7P5.js +245 -0
- package/dist/src/functionCallbackUtils-BfXnPo0o.js +245 -0
- package/dist/src/gemini-image-BXLMADDz.js +471 -0
- package/dist/src/gemini-image-BYUZyEUx.js +472 -0
- package/dist/src/gemini-image-DSt1QubZ.cjs +471 -0
- package/dist/src/gemini-image-DdNDMiZ_.js +471 -0
- package/dist/src/goat-9b-uU_k6.js +514 -0
- package/dist/src/goat-BCJuiDd0.js +515 -0
- package/dist/src/goat-CmAHbemm.js +512 -0
- package/dist/src/goat-DdOTdyoS.cjs +515 -0
- package/dist/src/goblin-Bp5C5lyb.js +22 -0
- package/dist/src/goblin-Dmr7GItI.js +23 -0
- package/dist/src/goblin-DpM8wxlf.cjs +22 -0
- package/dist/src/goblin-cJyH6Pq-.js +22 -0
- package/dist/src/google-B6eaPAbE.js +142 -0
- package/dist/src/google-D-FStdN6.cjs +142 -0
- package/dist/src/google-DAC7BsCW.js +142 -0
- package/dist/src/google-Id18H0Va.js +143 -0
- package/dist/src/graders-BjNQWkaB.js +2 -0
- package/dist/src/graders-CqdIp_vl.js +15470 -0
- package/dist/src/hydra-6Ouh74DE.js +610 -0
- package/dist/src/hydra-BHjhZnLB.cjs +612 -0
- package/dist/src/hydra-BL6k2REV.js +2 -0
- package/dist/src/hydra-BWUc0o67.js +613 -0
- package/dist/src/hydra-CUkEOLKw.js +612 -0
- package/dist/src/image-B6hG_xhb.js +481 -0
- package/dist/src/image-BFaq7Krd.cjs +209 -0
- package/dist/src/image-Bql5C0N9.cjs +504 -0
- package/dist/src/image-BtZfI2pT.js +163 -0
- package/dist/src/image-CIM4oN7Z.cjs +163 -0
- package/dist/src/image-CbAl7Jpu.js +209 -0
- package/dist/src/image-CiYm_yFw.js +209 -0
- package/dist/src/image-DCMNvBiV.js +163 -0
- package/dist/src/image-DL7pIuUZ.js +210 -0
- package/dist/src/image-ey0kxF8o.js +164 -0
- package/dist/src/image-smgIk3rE.js +482 -0
- package/dist/src/index.cjs +27 -27
- package/dist/src/index.d.cts +8 -8
- package/dist/src/index.js +27 -27
- package/dist/src/indirectWebPwn-B_-UpWY5.js +285 -0
- package/dist/src/indirectWebPwn-Cz3SLiEq.js +434 -0
- package/dist/src/indirectWebPwn-D2s8zGiM.cjs +463 -0
- package/dist/src/indirectWebPwn-DXbj2wGu.cjs +285 -0
- package/dist/src/indirectWebPwn-D_4H44E6.js +285 -0
- package/dist/src/indirectWebPwn-L044SrpV.js +434 -0
- package/dist/src/indirectWebPwn-cnmvdavH.js +435 -0
- package/dist/src/indirectWebPwn-zUBzsqTt.js +286 -0
- package/dist/src/interactions-Bu3oVQ-j.js +850 -0
- package/dist/src/interactions-CND010Lh.cjs +850 -0
- package/dist/src/interactions-DAx7tqUz.js +851 -0
- package/dist/src/interactions-DPkIumeh.js +850 -0
- package/dist/src/iterative-BO4JRnpG.js +545 -0
- package/dist/src/iterative-CAK-k9rz.js +548 -0
- package/dist/src/iterative-CDgHc0xO.cjs +549 -0
- package/dist/src/iterative-CXJlHLl9.js +547 -0
- package/dist/src/iterativeImage-BEdGKSmh.cjs +419 -0
- package/dist/src/iterativeImage-BfL0QZw5.js +417 -0
- package/dist/src/iterativeImage-C9KhnibC.js +417 -0
- package/dist/src/iterativeImage-btjkOfWG.js +418 -0
- package/dist/src/iterativeMeta-B5Bdw6QA.js +437 -0
- package/dist/src/iterativeMeta-C3mI4gpT.js +440 -0
- package/dist/src/iterativeMeta-Co2BKyqS.cjs +439 -0
- package/dist/src/iterativeMeta-DD8ihRWD.js +439 -0
- package/dist/src/iterativeTree-AMi4GV2O.js +878 -0
- package/dist/src/iterativeTree-C8FJOEje.cjs +879 -0
- package/dist/src/iterativeTree-CvrY1f2Z.js +877 -0
- package/dist/src/iterativeTree-bPtESmaD.js +876 -0
- package/dist/src/knowledgeBase-Cf5871e3.js +163 -0
- package/dist/src/knowledgeBase-D2PQhqLH.js +164 -0
- package/dist/src/knowledgeBase-DpoMw1IH.cjs +163 -0
- package/dist/src/knowledgeBase-npGFl763.js +163 -0
- package/dist/src/litellm-BBB5DQdt.cjs +146 -0
- package/dist/src/litellm-Bo43ow7r.js +145 -0
- package/dist/src/litellm-CaU8CMLS.js +146 -0
- package/dist/src/litellm-wY7Unh8o.js +147 -0
- package/dist/src/live-5g2w-M-E.js +891 -0
- package/dist/src/live-BfaXsDyD.cjs +891 -0
- package/dist/src/live-C1xewnGD.js +2 -0
- package/dist/src/live-DaF7nYy5.js +892 -0
- package/dist/src/live-lp3urLUw.js +891 -0
- package/dist/src/luma-ray-B7zvKGVG.js +309 -0
- package/dist/src/luma-ray-DCf5fnrz.cjs +309 -0
- package/dist/src/luma-ray-DepBcn6z.js +309 -0
- package/dist/src/luma-ray-XQC4M1pt.js +310 -0
- package/dist/src/main.js +26 -26
- package/dist/src/mantleChat-7xm95vSV.js +154 -0
- package/dist/src/mantleChat-CXhAs5RD.js +155 -0
- package/dist/src/mantleChat-CcE-zjfj.cjs +154 -0
- package/dist/src/mantleChat-Cpefl7Zm.js +154 -0
- package/dist/src/mcp-B0V0ikdM.js +84 -0
- package/dist/src/mcp-CV2k2gwj.js +85 -0
- package/dist/src/memoryPoisoning-BC5FQQWZ.js +111 -0
- package/dist/src/memoryPoisoning-BDwrtZKX.js +111 -0
- package/dist/src/memoryPoisoning-BFemS_FF.js +112 -0
- package/dist/src/memoryPoisoning-DRs2rbVV.cjs +111 -0
- package/dist/src/messages-Bc5oZoAJ.js +1128 -0
- package/dist/src/messages-BkaH8fRW.js +1127 -0
- package/dist/src/messages-CoHlziS5.js +1127 -0
- package/dist/src/messages-Cv_m6kYs.cjs +1161 -0
- package/dist/src/mischievousUser-BcyFxrqJ.js +51 -0
- package/dist/src/mischievousUser-BlhOQSd2.js +52 -0
- package/dist/src/mischievousUser-BuQiKE8D.js +51 -0
- package/dist/src/mischievousUser-ChIOk6cT.cjs +51 -0
- package/dist/src/modelslab-BXIWBTgX.js +171 -0
- package/dist/src/modelslab-ChVsUiaJ.js +170 -0
- package/dist/src/modelslab-DZ6BFlDv.js +170 -0
- package/dist/src/modelslab-DhRr-vyo.cjs +170 -0
- package/dist/src/nova-reel-BL8DrN4_.js +272 -0
- package/dist/src/nova-reel-BiOMUm5q.cjs +272 -0
- package/dist/src/nova-reel-CTsya1hV.js +273 -0
- package/dist/src/nova-reel-kgBY4Svd.js +272 -0
- package/dist/src/nova-sonic-CaSSA3Jq.cjs +374 -0
- package/dist/src/nova-sonic-DLpmoGsR.js +375 -0
- package/dist/src/nova-sonic-DwWc4SoN.js +374 -0
- package/dist/src/nova-sonic-qnReTaXm.js +374 -0
- package/dist/src/novita-B5mAt3Ui.js +114 -0
- package/dist/src/novita-D57vCjDD.js +116 -0
- package/dist/src/novita-ITRtG8IJ.js +115 -0
- package/dist/src/novita-Rh3T13YO.cjs +115 -0
- package/dist/src/nscale-8V7iZ_pI.js +210 -0
- package/dist/src/nscale-BQnMv4pH.cjs +209 -0
- package/dist/src/nscale-BWZXHZVF.js +209 -0
- package/dist/src/nscale-BqrHiUqU.js +207 -0
- package/dist/src/openaiResponses-BMtGTu8R.js +317 -0
- package/dist/src/openaiResponses-CvcI_viE.js +318 -0
- package/dist/src/openaiResponses-DYPZEMX1.cjs +318 -0
- package/dist/src/openaiResponses-DZTg1-kb.js +319 -0
- package/dist/src/openclaw-BeKuig4g.js +1338 -0
- package/dist/src/openclaw-COkdQ83y.js +1337 -0
- package/dist/src/openclaw-CSoX_HQ5.cjs +1344 -0
- package/dist/src/openclaw-DJlhPGCb.js +1336 -0
- package/dist/src/opencode-sdk-BEJftEs6.cjs +1018 -0
- package/dist/src/opencode-sdk-DMvBB3V7.js +1013 -0
- package/dist/src/opencode-sdk-Df7hMjLO.js +1012 -0
- package/dist/src/opencode-sdk-bQp5Xf3W.js +1012 -0
- package/dist/src/openinterpreter-B3GgVZjK.js +431 -0
- package/dist/src/openinterpreter-BiGcU-HN.cjs +435 -0
- package/dist/src/openinterpreter-Buj2l3Eh.js +432 -0
- package/dist/src/openinterpreter-C3_Cg5b_.js +431 -0
- package/dist/src/pricing-0Pampf0q.cjs +999 -0
- package/dist/src/pricing-B8VdhEhY.js +976 -0
- package/dist/src/pricing-CtLA-rKA.js +976 -0
- package/dist/src/promptfoo-4OkHtH9P.cjs +341 -0
- package/dist/src/promptfoo-Bhpm7sH6.js +286 -0
- package/dist/src/promptfoo-BqV8ogVA.js +285 -0
- package/dist/src/promptfoo-S1Q9OHk_.js +286 -0
- package/dist/src/providers-B5zs1ZRv.js +24437 -0
- package/dist/src/providers-Cd6CJh6m.js +2 -0
- package/dist/src/providers-CpJlgHET.js +24039 -0
- package/dist/src/providers-CzvLvK6G.cjs +24180 -0
- package/dist/src/quiverai-1E_IvLHn.js +325 -0
- package/dist/src/quiverai-BXH004Vv.js +325 -0
- package/dist/src/quiverai-Ccya2z8Y.cjs +325 -0
- package/dist/src/quiverai-Cez57oe-.js +326 -0
- package/dist/src/registry-3zX_M9Ik.js +131 -0
- package/dist/src/registry-AuCrKTvS.js +132 -0
- package/dist/src/registry-CxXWKtQG.js +131 -0
- package/dist/src/registry-DG3o66JQ.cjs +131 -0
- package/dist/src/remoteGeneration-CAtvamG6.js +370 -0
- package/dist/src/remoteGeneration-CF225pDl.cjs +482 -0
- package/dist/src/remoteGeneration-DtnqocnL.js +370 -0
- package/dist/src/remoteGeneration-g3KvW-sP.js +371 -0
- package/dist/src/responses-BMbaUXQI.js +1098 -0
- package/dist/src/responses-B_TOSof-.js +1099 -0
- package/dist/src/responses-v-yANwTA.cjs +1109 -0
- package/dist/src/runtimeTransform-BBZv-oUZ.js +156 -0
- package/dist/src/runtimeTransform-DT2v5ovW.js +156 -0
- package/dist/src/runtimeTransform-GXXEIXdS.cjs +161 -0
- package/dist/src/runtimeTransform-JTwBQc-P.js +157 -0
- package/dist/src/sagemaker-B8D0L4rj.cjs +716 -0
- package/dist/src/sagemaker-CR2_AGdB.js +713 -0
- package/dist/src/sagemaker-Cd9qSQUV.js +714 -0
- package/dist/src/sagemaker-mKlltN4p.js +713 -0
- package/dist/src/scanner-PG_8lh8r.js +1971 -0
- package/dist/src/server/index.js +55 -55
- package/dist/src/server-Bmh7sd9F.js +155 -0
- package/dist/src/server-C7b8z6aC.js +63 -0
- package/dist/src/server-CAcovgtL.js +2 -0
- package/dist/src/server-CuuMfKlh.cjs +74 -0
- package/dist/src/server-DMPCj9jy.js +189 -0
- package/dist/src/session-BksXvRH_.cjs +69 -0
- package/dist/src/session-DOxUjqGQ.js +63 -0
- package/dist/src/session-Dujsc-b9.js +62 -0
- package/dist/src/session-VjtIxREd.js +62 -0
- package/dist/src/shared-Bkyz_7e5.cjs +2185 -0
- package/dist/src/shared-DDJg8ycC.js +1929 -0
- package/dist/src/shared-Dh3yfRBd.js +1927 -0
- package/dist/src/shared-tfPT8Tna.js +1928 -0
- package/dist/src/simulatedUser-6JdkyL3k.cjs +252 -0
- package/dist/src/simulatedUser-CFr2mr3M.js +248 -0
- package/dist/src/simulatedUser-DjwwUqx-.js +247 -0
- package/dist/src/slack-C_gIYiUs.js +144 -0
- package/dist/src/slack-DhNbz9CM.cjs +144 -0
- package/dist/src/slack-Dm2iI1zu.js +145 -0
- package/dist/src/strategies-BIY-bNul.js +2248 -0
- package/dist/src/strategies-C1KQQj9G.cjs +2420 -0
- package/dist/src/strategies-DM_3mReG.js +2385 -0
- package/dist/src/telemetry-BhMTibZO.js +2 -0
- package/dist/src/telemetry-C04d25BM.cjs +216 -0
- package/dist/src/telemetry-CIqy4fSC.js +201 -0
- package/dist/src/telemetry-CXozp_6C.js +205 -0
- package/dist/src/telemetry-g7PkBxmY.js +205 -0
- package/dist/src/togetherai-BvCim68-.js +50 -0
- package/dist/src/togetherai-CWyDCA9m.cjs +49 -0
- package/dist/src/togetherai-CeSE31vK.js +49 -0
- package/dist/src/togetherai-_2W0URf8.js +48 -0
- package/dist/src/traceContext-Brw3Oe41.js +1016 -0
- package/dist/src/traceContext-DpAYv27G.js +1015 -0
- package/dist/src/traceContext-KSqhzZI2.cjs +1032 -0
- package/dist/src/transcription-BySopazW.js +175 -0
- package/dist/src/transcription-DO4UPkRI.cjs +178 -0
- package/dist/src/transcription-RJGBa-pC.js +176 -0
- package/dist/src/transcription-fZenF3G5.js +175 -0
- package/dist/src/transform-BzhRc5z2.js +125 -0
- package/dist/src/transform-CPQu05u6.cjs +154 -0
- package/dist/src/transform-DAED8imp.js +126 -0
- package/dist/src/transform-DIOIQDmM.js +125 -0
- package/dist/src/typesafe-ByE0Kxfg.js +253 -0
- package/dist/src/typesafe-CFqGFY34.js +253 -0
- package/dist/src/typesafe-DCWIw-CP.cjs +253 -0
- package/dist/src/typesafe-DyHFV5Qk.js +254 -0
- package/dist/src/util-BHkj9joz.js +2049 -0
- package/dist/src/util-CICwIpmu.cjs +934 -0
- package/dist/src/util-CePiY2xZ.js +286 -0
- package/dist/src/util-CgT3uf4p.js +743 -0
- package/dist/src/util-Cj9drvOY.js +1770 -0
- package/dist/src/util-ClJRjlQA.cjs +357 -0
- package/dist/src/util-Cxf8VF2M.js +395 -0
- package/dist/src/util-D7RdV7rS.js +395 -0
- package/dist/src/util-DMNFhk47.js +351 -0
- package/dist/src/util-DNzh3M0z.js +396 -0
- package/dist/src/util-Df7rfMGb.cjs +2305 -0
- package/dist/src/util-Kf0KnG_6.js +2051 -0
- package/dist/src/util-LEbeiYw_.js +743 -0
- package/dist/src/util-WH8CgVs4.js +352 -0
- package/dist/src/util-cx6Xw8Lw.cjs +478 -0
- package/dist/src/util-qzBqirBH.js +2050 -0
- package/dist/src/vertex-CBAMfRz3.js +854 -0
- package/dist/src/vertex-CMP2IpN9.js +2 -0
- package/dist/src/vertex-CZ5YOotT.js +848 -0
- package/dist/src/vertex-DNsh1F1M.cjs +871 -0
- package/dist/src/vertexLive-BTXYj3-F.js +55 -0
- package/dist/src/vertexLive-Ba_LAqEF.cjs +55 -0
- package/dist/src/vertexLive-DM4sFaaj.js +56 -0
- package/dist/src/vertexLive-JqZOw7HC.js +55 -0
- package/dist/src/video-BY7Q2Gii.cjs +706 -0
- package/dist/src/video-CYElP-CK.js +703 -0
- package/dist/src/video-C_AdEDH0.js +702 -0
- package/dist/src/video-DGvjQ_4j.js +702 -0
- package/package.json +1 -1
- package/dist/src/accounts-Dpb43yde.cjs +0 -276
- package/dist/src/accounts-GKrO1olH.js +0 -229
- package/dist/src/accounts-e4FGOsji.js +0 -227
- package/dist/src/accounts-sUiEJfkB.js +0 -222
- package/dist/src/agentWorkspace-B8A_wuMR.cjs +0 -619
- package/dist/src/agentWorkspace-DSPIscXR.js +0 -593
- package/dist/src/agentWorkspace-DxUqd-XK.js +0 -592
- package/dist/src/agentWorkspace-n0uOJXhD.js +0 -592
- package/dist/src/agentic-utils-9bCj4twQ.js +0 -197
- package/dist/src/agentic-utils-CyCLuLoj.js +0 -197
- package/dist/src/agentic-utils-D23qtOlx.cjs +0 -237
- package/dist/src/agentic-utils-DuBMMNCw.js +0 -198
- package/dist/src/agents-6hDRNoYD.js +0 -777
- package/dist/src/agents-B8w2o7Sc.cjs +0 -777
- package/dist/src/agents-Ck0NXl7v.js +0 -777
- package/dist/src/agents-CwT3B1qB.js +0 -778
- package/dist/src/agents-DOLjfOEq.js +0 -266
- package/dist/src/agents-OcF9hq--.js +0 -267
- package/dist/src/agents-api-CnZZt8bn.cjs +0 -710
- package/dist/src/agents-api-D04u3bmL.js +0 -711
- package/dist/src/agents-api-DWw0_Vio.js +0 -710
- package/dist/src/agents-api-IWHEwp8i.js +0 -710
- package/dist/src/agents-mHt2Mh9O.cjs +0 -266
- package/dist/src/agents-sICccZCA.js +0 -266
- package/dist/src/ai.studio-Ci4rypDT.js +0 -2
- package/dist/src/ai.studio-KkQ6KaHa.js +0 -369
- package/dist/src/ai.studio-gSXK2cGI.js +0 -374
- package/dist/src/ai.studio-nQF_B4DH.cjs +0 -391
- package/dist/src/aimlapi-BeSk5BDV.js +0 -30
- package/dist/src/aimlapi-CzNUhdKu.cjs +0 -29
- package/dist/src/aimlapi-DCkazDPI.js +0 -29
- package/dist/src/aimlapi-WeH5B6K1.js +0 -28
- package/dist/src/anthropicMessages-AcNWTpYO.js +0 -115
- package/dist/src/anthropicMessages-BUtvzbpL.js +0 -114
- package/dist/src/anthropicMessages-DTWhZDCd.js +0 -114
- package/dist/src/anthropicMessages-lGVnSgyx.cjs +0 -114
- package/dist/src/app/assets/index-CyEI5aDN.js +0 -392
- package/dist/src/audio-BE_Duiu1.js +0 -104
- package/dist/src/audio-CiUd00k2.cjs +0 -104
- package/dist/src/audio-Dop6omna.js +0 -105
- package/dist/src/audio-EZNTInr_.js +0 -104
- package/dist/src/authoritativeMarkupInjection-BUqH6hNk.js +0 -78
- package/dist/src/authoritativeMarkupInjection-BmbJdJO0.cjs +0 -78
- package/dist/src/authoritativeMarkupInjection-Cpz12Y3w.js +0 -78
- package/dist/src/authoritativeMarkupInjection-W4XfcH91.js +0 -79
- package/dist/src/aws-B2Jqbnet.js +0 -2433
- package/dist/src/aws-DHtuUIzQ.js +0 -1149
- package/dist/src/aws-DLqXP6wY.js +0 -2433
- package/dist/src/aws-DfduhmwL.cjs +0 -2454
- package/dist/src/base-B9L2Xvpc.js +0 -255
- package/dist/src/base-BTZnIsuH.cjs +0 -305
- package/dist/src/base-BhaWNQPJ.js +0 -462
- package/dist/src/base-Br-G_Xou.js +0 -256
- package/dist/src/base-Bzo8hiUS.cjs +0 -467
- package/dist/src/base-C82fd2jq.js +0 -463
- package/dist/src/base-CRdQwC1W.js +0 -462
- package/dist/src/base-CjGwZNOQ.js +0 -255
- package/dist/src/bedrock-BdMru_ro.js +0 -1300
- package/dist/src/bedrock-CBUmfLX1.cjs +0 -2
- package/dist/src/bedrock-DBDxBaF9.js +0 -2
- package/dist/src/bedrock-DDrECM21.js +0 -1
- package/dist/src/bedrock-DVwkgvRS.js +0 -1
- package/dist/src/bestOfN-AKF5beq4.js +0 -159
- package/dist/src/bestOfN-C1UIVGJV.js +0 -160
- package/dist/src/bestOfN-DlkETbXy.cjs +0 -161
- package/dist/src/bestOfN-XIfEgU-g.js +0 -159
- package/dist/src/cache-BZaVoKaA.js +0 -2
- package/dist/src/cache-CEYhFUsH.js +0 -741
- package/dist/src/cache-Cbw1zH6q.js +0 -782
- package/dist/src/cache-CdaZNb75.js +0 -754
- package/dist/src/cache-DpuIzPxh.cjs +0 -856
- package/dist/src/cerebras-Bpn6FPkJ.js +0 -97
- package/dist/src/cerebras-CqJ0BRCJ.cjs +0 -97
- package/dist/src/cerebras-D4ILa_TY.js +0 -97
- package/dist/src/cerebras-DR8uc7ee.js +0 -98
- package/dist/src/chat-B7PZfaYp.cjs +0 -987
- package/dist/src/chat-BVuSjdEb.js +0 -227
- package/dist/src/chat-BsaZRjXN.js +0 -76
- package/dist/src/chat-C5liqe3Y.js +0 -226
- package/dist/src/chat-C9hyHf5B.js +0 -124
- package/dist/src/chat-CFLalAck.js +0 -226
- package/dist/src/chat-CN8JGrHu.js +0 -76
- package/dist/src/chat-CaHBBpCb.js +0 -123
- package/dist/src/chat-CcbLSghx.js +0 -123
- package/dist/src/chat-CoSN6sr2.js +0 -934
- package/dist/src/chat-CsJRFX8a.cjs +0 -226
- package/dist/src/chat-DFoZLvO_.js +0 -934
- package/dist/src/chat-DiLQvL7r.js +0 -77
- package/dist/src/chat-F6qVSDk3.cjs +0 -123
- package/dist/src/chat-Oq4FaJgI.cjs +0 -76
- package/dist/src/chat-knMfQUKz.js +0 -935
- package/dist/src/claude-agent-sdk-CJkOLpnI.js +0 -999
- package/dist/src/claude-agent-sdk-Ckn76PRD.js +0 -999
- package/dist/src/claude-agent-sdk-DX0gTr11.cjs +0 -1005
- package/dist/src/claude-agent-sdk-LMeikxoR.js +0 -1000
- package/dist/src/client-BFf1r6Z8.js +0 -352
- package/dist/src/client-CdXyDdDi.js +0 -351
- package/dist/src/client-DV8XMu7i.js +0 -351
- package/dist/src/client-iEXtOBKW.cjs +0 -357
- package/dist/src/cloud-B02qcpjf.js +0 -2
- package/dist/src/cloud-BlHP-g1v.cjs +0 -666
- package/dist/src/cloud-C9BI4isF.js +0 -287
- package/dist/src/cloud-CTTLNdvj.js +0 -657
- package/dist/src/cloud-D5X5yQ_d.js +0 -599
- package/dist/src/cloud-QxPkpsHA.js +0 -599
- package/dist/src/cloudflare-ai-DRTRT1fc.js +0 -132
- package/dist/src/cloudflare-ai-DgaXapAV.cjs +0 -133
- package/dist/src/cloudflare-ai-DkuJXijS.js +0 -134
- package/dist/src/cloudflare-ai-wy9QCHku.js +0 -133
- package/dist/src/cloudflare-gateway-9cAN8g29.cjs +0 -276
- package/dist/src/cloudflare-gateway-BVFMD292.js +0 -277
- package/dist/src/cloudflare-gateway-BuUI4p6I.js +0 -276
- package/dist/src/cloudflare-gateway-TlYHizbW.js +0 -276
- package/dist/src/codex-app-server-BPz_feug.cjs +0 -2162
- package/dist/src/codex-app-server-C9noXM9C.js +0 -2155
- package/dist/src/codex-app-server-CEmD-Y5U.js +0 -2
- package/dist/src/codex-app-server-DZolc18r.js +0 -2156
- package/dist/src/codex-app-server-Dd0A37nO.js +0 -2155
- package/dist/src/codex-sdk-BbxjBvBk.cjs +0 -1588
- package/dist/src/codex-sdk-BpXeIs-_.js +0 -1557
- package/dist/src/codex-sdk-BtQp5X0f.js +0 -1552
- package/dist/src/codex-sdk-CH0v9Bk1.js +0 -2
- package/dist/src/codex-sdk-CgrN7JB8.js +0 -1557
- package/dist/src/codex-security-B5fENNUr.js +0 -406
- package/dist/src/codex-security-C6uuJx-y.cjs +0 -411
- package/dist/src/codex-security-CifWtZR_.js +0 -407
- package/dist/src/codex-security-Kg2bOEKy.js +0 -406
- package/dist/src/cometapi-BFpIDLIx.js +0 -55
- package/dist/src/cometapi-C6O2PIrN.js +0 -56
- package/dist/src/cometapi-D5Yupc6F.js +0 -53
- package/dist/src/cometapi-DzgIwJzJ.cjs +0 -55
- package/dist/src/compatible-0jhmVfOp.js +0 -25
- package/dist/src/compatible-DdtwWdJi.cjs +0 -24
- package/dist/src/compatible-Di4nR3sU.js +0 -24
- package/dist/src/compatible-iJKLWdnf.js +0 -24
- package/dist/src/completion-B0_hVUDG.js +0 -119
- package/dist/src/completion-BdI6v0-Q.cjs +0 -123
- package/dist/src/completion-CWcJscwv.js +0 -118
- package/dist/src/conversationScoring-99EOPJRa.js +0 -373
- package/dist/src/conversationScoring-BIq4J0Cq.js +0 -374
- package/dist/src/conversationScoring-DARyeATr.cjs +0 -398
- package/dist/src/conversationScoring-DS48t4Hb.js +0 -373
- package/dist/src/crescendo-CebvXIu4.js +0 -730
- package/dist/src/crescendo-CxrLS0oo.js +0 -732
- package/dist/src/crescendo-DZQXBSkj.js +0 -733
- package/dist/src/crescendo-wjEx68uD.cjs +0 -734
- package/dist/src/custom-BiQ-aUNw.cjs +0 -620
- package/dist/src/custom-ByyB5Z93.js +0 -619
- package/dist/src/custom-C5jOevNb.js +0 -617
- package/dist/src/custom-CCrsCz8o.js +0 -618
- package/dist/src/dataUrl-C9J7wz9H.js +0 -3133
- package/dist/src/dataUrl-CGsBT7UZ.cjs +0 -3187
- package/dist/src/dataUrl-CzKvvWNO.js +0 -2868
- package/dist/src/decisions-BTusXCUx.cjs +0 -514
- package/dist/src/decisions-BYtL9edD.js +0 -515
- package/dist/src/decisions-jYix_gCF.js +0 -515
- package/dist/src/decisions-nxxfHRAN.js +0 -516
- package/dist/src/docker-DDIv4zkg.js +0 -93
- package/dist/src/docker-DVLp_5OG.js +0 -94
- package/dist/src/docker-INm9w0SY.cjs +0 -94
- package/dist/src/docker-sGuoCul_.js +0 -95
- package/dist/src/embedding-CAhqLLrR.js +0 -82
- package/dist/src/embedding-D94GAsti.cjs +0 -86
- package/dist/src/embedding-zb4MAzQy.js +0 -81
- package/dist/src/envoy-BMPszwEa.cjs +0 -60
- package/dist/src/envoy-Bp-s39_8.js +0 -60
- package/dist/src/envoy-BswDt-Ou.js +0 -61
- package/dist/src/envoy-rVbYcV80.js +0 -60
- package/dist/src/eval-B1-XfrC7.js +0 -2
- package/dist/src/eval-pC4qCvLw.js +0 -1920
- package/dist/src/evalResult-CzjCb5bd.js +0 -1300
- package/dist/src/evaluator-CNq2K7J_.js +0 -10578
- package/dist/src/evaluator-LtfxdxEu.js +0 -2
- package/dist/src/evaluatorHelpers-C5J0NYpQ.js +0 -601
- package/dist/src/evaluatorHelpers-DcdCglCs.cjs +0 -622
- package/dist/src/evaluatorHelpers-Rvb88W6B.js +0 -600
- package/dist/src/fetch-5HtnNhHg.js +0 -1556
- package/dist/src/fetch-BCwjoIFd.js +0 -1559
- package/dist/src/fetch-BhfUnKen.js +0 -1280
- package/dist/src/fetch-C08w4vVl.js +0 -9
- package/dist/src/fetch-DBxy8Tlb.cjs +0 -14
- package/dist/src/fetch-DYte6oQX.cjs +0 -1853
- package/dist/src/fetch-ElpfcAVG.js +0 -10
- package/dist/src/fetch-lM1OIaSt.js +0 -2
- package/dist/src/functionCallbackUtils-BCAdWfHO.js +0 -246
- package/dist/src/functionCallbackUtils-BYq9qOhG.js +0 -245
- package/dist/src/functionCallbackUtils-ByWkmVud.cjs +0 -262
- package/dist/src/functionCallbackUtils-D_C_JaFd.js +0 -245
- package/dist/src/gemini-image-CAlwaJhZ.js +0 -471
- package/dist/src/gemini-image-DJoVoOnZ.cjs +0 -471
- package/dist/src/gemini-image-Waio-9Xx.js +0 -472
- package/dist/src/gemini-image-vbY6Ftbf.js +0 -471
- package/dist/src/goat-BWbtyY9c.js +0 -512
- package/dist/src/goat-CXzzeCOQ.cjs +0 -515
- package/dist/src/goat-CfgvzSgZ.js +0 -514
- package/dist/src/goat-DLeoIHBp.js +0 -515
- package/dist/src/goblin-CmfCfwqk.cjs +0 -22
- package/dist/src/goblin-CtcaORRH.js +0 -22
- package/dist/src/goblin-DF6DDhM-.js +0 -22
- package/dist/src/goblin-Nai0xxBV.js +0 -23
- package/dist/src/google-ClpkLtKL.cjs +0 -142
- package/dist/src/google-CrFgKu9k.js +0 -143
- package/dist/src/google-Dj_EIBqS.js +0 -142
- package/dist/src/google-e4-vARv6.js +0 -142
- package/dist/src/graders-BN1IIa71.js +0 -15470
- package/dist/src/graders-C5sCfSP5.js +0 -2
- package/dist/src/hydra-B7RfXVBK.js +0 -2
- package/dist/src/hydra-C1cFN-Jg.js +0 -613
- package/dist/src/hydra-CK6Uc1ia.js +0 -612
- package/dist/src/hydra-Dg65rAP_.js +0 -610
- package/dist/src/hydra-Fqx8l_Xu.cjs +0 -612
- package/dist/src/image-BdlUDAtp.js +0 -163
- package/dist/src/image-C-KwEhwt.cjs +0 -163
- package/dist/src/image-C76fp6NK.cjs +0 -209
- package/dist/src/image-D75ehv3D.js +0 -482
- package/dist/src/image-DKtc5v5W.js +0 -209
- package/dist/src/image-DT_xOkJY.js +0 -163
- package/dist/src/image-DVpdllEE.js +0 -209
- package/dist/src/image-DXZ8AzMf.js +0 -481
- package/dist/src/image-DZl_1mvy.js +0 -164
- package/dist/src/image-DcTD7Ak0.cjs +0 -504
- package/dist/src/image-ndxEvBKB.js +0 -210
- package/dist/src/indirectWebPwn-70WA5x_q.js +0 -435
- package/dist/src/indirectWebPwn-BRVyd3qV.cjs +0 -463
- package/dist/src/indirectWebPwn-BsJVsu0q.js +0 -285
- package/dist/src/indirectWebPwn-Cw6yJu0i.js +0 -285
- package/dist/src/indirectWebPwn-D-PhJzo4.js +0 -286
- package/dist/src/indirectWebPwn-DRPHt2I8.js +0 -434
- package/dist/src/indirectWebPwn-DXtfFhd9.cjs +0 -285
- package/dist/src/indirectWebPwn-DvpNouzc.js +0 -434
- package/dist/src/interactions-5ixfW7Cl.js +0 -850
- package/dist/src/interactions-BOUzfmg1.cjs +0 -850
- package/dist/src/interactions-CSSXcGNc.js +0 -851
- package/dist/src/interactions-WB_sgkN0.js +0 -850
- package/dist/src/iterative-BShRNqjC.cjs +0 -549
- package/dist/src/iterative-BcW_ELtB.js +0 -548
- package/dist/src/iterative-BpHbHz8B.js +0 -547
- package/dist/src/iterative-YcchP12d.js +0 -545
- package/dist/src/iterativeImage-BMFE5Hl7.cjs +0 -419
- package/dist/src/iterativeImage-CkSaYLMY.js +0 -417
- package/dist/src/iterativeImage-DAEUlQX_.js +0 -418
- package/dist/src/iterativeImage-DeUSkEnj.js +0 -417
- package/dist/src/iterativeMeta-DbdaKdEO.cjs +0 -439
- package/dist/src/iterativeMeta-DcF9JWEK.js +0 -437
- package/dist/src/iterativeMeta-DkfPqWIC.js +0 -439
- package/dist/src/iterativeMeta-DkrpnVSs.js +0 -440
- package/dist/src/iterativeTree-CEMY1FhE.cjs +0 -879
- package/dist/src/iterativeTree-Cv5W4tX9.js +0 -877
- package/dist/src/iterativeTree-DGvomd3M.js +0 -876
- package/dist/src/iterativeTree-KbtC2Mkz.js +0 -878
- package/dist/src/knowledgeBase-AYRXn4wO.js +0 -163
- package/dist/src/knowledgeBase-BNqQfx01.js +0 -163
- package/dist/src/knowledgeBase-C0IaCCVs.js +0 -164
- package/dist/src/knowledgeBase-C2uj6wK4.cjs +0 -163
- package/dist/src/litellm-CN1ScAwZ.js +0 -147
- package/dist/src/litellm-CTjwiKSa.js +0 -146
- package/dist/src/litellm-Cd4Nj5ue.js +0 -145
- package/dist/src/litellm-DMqLpEBg.cjs +0 -146
- package/dist/src/live-Bt50-InO.cjs +0 -891
- package/dist/src/live-Bu2ujadG.js +0 -2
- package/dist/src/live-C3M52i3p.js +0 -891
- package/dist/src/live-CRfHMO3B.js +0 -892
- package/dist/src/live-iCIpO14m.js +0 -891
- package/dist/src/luma-ray-B8eHC4c8.cjs +0 -309
- package/dist/src/luma-ray-BIvkPw0s.js +0 -309
- package/dist/src/luma-ray-ChsiBxMA.js +0 -309
- package/dist/src/luma-ray-DAvKrInz.js +0 -310
- package/dist/src/mantleChat-5YeEXAZI.js +0 -154
- package/dist/src/mantleChat-D--mW3hl.js +0 -154
- package/dist/src/mantleChat-DHP3siNg.cjs +0 -154
- package/dist/src/mantleChat-wBfAX46o.js +0 -155
- package/dist/src/mcp-49jlEcTQ.js +0 -85
- package/dist/src/mcp-__PIz81I.js +0 -84
- package/dist/src/memoryPoisoning-Chxge74M.js +0 -111
- package/dist/src/memoryPoisoning-QH6NCUVE.js +0 -112
- package/dist/src/memoryPoisoning-tCxZt8Nz.js +0 -111
- package/dist/src/memoryPoisoning-ymrRItRR.cjs +0 -111
- package/dist/src/messages-BE_eZ7NA.js +0 -1127
- package/dist/src/messages-Bo638ShI.js +0 -1127
- package/dist/src/messages-BzR0-NQ1.js +0 -1128
- package/dist/src/messages-Cbg5Pfd0.cjs +0 -1161
- package/dist/src/mischievousUser-BtED2Z5Z.cjs +0 -51
- package/dist/src/mischievousUser-KqSOjrDJ.js +0 -51
- package/dist/src/mischievousUser-YMD41YNZ.js +0 -51
- package/dist/src/mischievousUser-zBxlNdCu.js +0 -52
- package/dist/src/modelslab-B8gZxtfE.js +0 -171
- package/dist/src/modelslab-C7bmVpTB.js +0 -170
- package/dist/src/modelslab-CCeQCTZg.js +0 -170
- package/dist/src/modelslab-CvBJGv7y.cjs +0 -170
- package/dist/src/nova-reel-BOEWsEKA.cjs +0 -272
- package/dist/src/nova-reel-BtyBXq5M.js +0 -273
- package/dist/src/nova-reel-C1eF3-EE.js +0 -272
- package/dist/src/nova-reel-YhgrxEKT.js +0 -272
- package/dist/src/nova-sonic-BaERP20w.js +0 -374
- package/dist/src/nova-sonic-CxgM3O-P.cjs +0 -374
- package/dist/src/nova-sonic-DJS22fC3.js +0 -375
- package/dist/src/nova-sonic-DwvwHyRX.js +0 -374
- package/dist/src/novita-D1l2tG0O.js +0 -116
- package/dist/src/novita-D84VM3v0.cjs +0 -115
- package/dist/src/novita-DGInvwfR.js +0 -115
- package/dist/src/novita-vXHkXhTg.js +0 -114
- package/dist/src/nscale-CU0Ldr00.js +0 -209
- package/dist/src/nscale-ClfKlQeM.cjs +0 -209
- package/dist/src/nscale-DxaVnF0x.js +0 -210
- package/dist/src/nscale-_poVxIrO.js +0 -207
- package/dist/src/openaiResponses-CJGsneNj.js +0 -317
- package/dist/src/openaiResponses-CKSl5Sdg.js +0 -319
- package/dist/src/openaiResponses-DC8rzFUc.js +0 -318
- package/dist/src/openaiResponses-rMHIA3dV.cjs +0 -318
- package/dist/src/openclaw-CCbvsNFW.cjs +0 -1344
- package/dist/src/openclaw-CZw1lCDZ.js +0 -1336
- package/dist/src/openclaw-ChFLgC-a.js +0 -1338
- package/dist/src/openclaw-DrPMoRrH.js +0 -1337
- package/dist/src/opencode-sdk-2EfIDhFp.cjs +0 -1018
- package/dist/src/opencode-sdk-CHw3F845.js +0 -1013
- package/dist/src/opencode-sdk-D0EGGxvy.js +0 -1012
- package/dist/src/opencode-sdk-jSS7NI2Q.js +0 -1012
- package/dist/src/openinterpreter-CY622xHC.js +0 -431
- package/dist/src/openinterpreter-DATyhuhP.js +0 -432
- package/dist/src/openinterpreter-DDUMyIf1.js +0 -431
- package/dist/src/openinterpreter-s5Tz6Lid.cjs +0 -435
- package/dist/src/pricing-C498twd2.js +0 -976
- package/dist/src/pricing-DBd_bMIh.cjs +0 -999
- package/dist/src/pricing-ISrMCskv.js +0 -976
- package/dist/src/promptfoo-6p0WG2ih.js +0 -286
- package/dist/src/promptfoo-BQRfgNs_.js +0 -285
- package/dist/src/promptfoo-Bqo88z20.cjs +0 -341
- package/dist/src/promptfoo-Cq5AVI3I.js +0 -286
- package/dist/src/providers-ATbeMX5E.cjs +0 -24180
- package/dist/src/providers-B7F1FiNI.js +0 -2
- package/dist/src/providers-DazIw8mI.js +0 -24039
- package/dist/src/providers-QvGlNAD0.js +0 -24437
- package/dist/src/quiverai-BPlNcLkV.cjs +0 -325
- package/dist/src/quiverai-By99-YmJ.js +0 -325
- package/dist/src/quiverai-CmmByDqo.js +0 -325
- package/dist/src/quiverai-HZkoJJqw.js +0 -326
- package/dist/src/registry-BLTnTfjT.cjs +0 -131
- package/dist/src/registry-BMC-COHT.js +0 -132
- package/dist/src/registry-C8UZtmBp.js +0 -131
- package/dist/src/registry-CfwEUYXG.js +0 -131
- package/dist/src/remoteGeneration-Bgg6kC5V.js +0 -370
- package/dist/src/remoteGeneration-CKJFE960.cjs +0 -482
- package/dist/src/remoteGeneration-DaxwJDKv.js +0 -370
- package/dist/src/remoteGeneration-vHMGVKr1.js +0 -371
- package/dist/src/responses-BvnyIHrq.js +0 -1099
- package/dist/src/responses-CERAgvf-.js +0 -1098
- package/dist/src/responses-CwgjeuOg.cjs +0 -1109
- package/dist/src/runtimeTransform-3RF9Avyg.cjs +0 -161
- package/dist/src/runtimeTransform-C51Z5j4H.js +0 -156
- package/dist/src/runtimeTransform-CKE_Iyv8.js +0 -157
- package/dist/src/runtimeTransform-Cs11xJpj.js +0 -156
- package/dist/src/sagemaker-BJ4dOCxE.js +0 -713
- package/dist/src/sagemaker-Bt3JUAWq.js +0 -714
- package/dist/src/sagemaker-QVKyLgLF.cjs +0 -716
- package/dist/src/sagemaker-l3IB8iTs.js +0 -713
- package/dist/src/scanner-DGjIiTZ1.js +0 -1971
- package/dist/src/server-BNaLZH6u.js +0 -63
- package/dist/src/server-CBMuQul3.js +0 -189
- package/dist/src/server-CMKKIzKv.cjs +0 -74
- package/dist/src/server-CN2IS9Cs.js +0 -155
- package/dist/src/server-ua4cqbjC.js +0 -2
- package/dist/src/session-CQIjq_HZ.cjs +0 -69
- package/dist/src/session-FTlg8sEu.js +0 -62
- package/dist/src/session-GBQXtPWp.js +0 -63
- package/dist/src/session-_0-rTdwY.js +0 -62
- package/dist/src/shared-BtuO-OAM.js +0 -1927
- package/dist/src/shared-Ci78wTZX.js +0 -1928
- package/dist/src/shared-Dd3rimdM.js +0 -1929
- package/dist/src/shared-KAjFxRre.cjs +0 -2185
- package/dist/src/simulatedUser--OjW6xCx.js +0 -247
- package/dist/src/simulatedUser-D0-slRye.cjs +0 -252
- package/dist/src/simulatedUser-D1Kh_cwA.js +0 -248
- package/dist/src/slack-BX0FUevO.js +0 -144
- package/dist/src/slack-CcwXFiJE.cjs +0 -144
- package/dist/src/slack-CnfqKfXp.js +0 -145
- package/dist/src/strategies-2ntcK4tN.js +0 -2248
- package/dist/src/strategies-DZIW7Zcs.js +0 -2385
- package/dist/src/strategies-YRDF2j-N.cjs +0 -2420
- package/dist/src/telemetry-CL8CVA4p.js +0 -205
- package/dist/src/telemetry-Cix90qqz.js +0 -201
- package/dist/src/telemetry-CkmlqTjV.js +0 -205
- package/dist/src/telemetry-DQH7l5bb.js +0 -2
- package/dist/src/telemetry-DUCXc1-r.cjs +0 -216
- package/dist/src/togetherai-3yUt5Ck7.js +0 -49
- package/dist/src/togetherai-BJUByLwt.cjs +0 -49
- package/dist/src/togetherai-Bh6KZkmd.js +0 -50
- package/dist/src/togetherai-DDl9yk3b.js +0 -48
- package/dist/src/traceContext-BiJVppjT.cjs +0 -1032
- package/dist/src/traceContext-CKKluULP.js +0 -1016
- package/dist/src/traceContext-D3Sk8fO-.js +0 -1015
- package/dist/src/transcription-BoVQKIVz.js +0 -175
- package/dist/src/transcription-Bs5u8780.js +0 -175
- package/dist/src/transcription-Dl654d1p.cjs +0 -178
- package/dist/src/transcription-VEvhM1Fu.js +0 -176
- package/dist/src/transform-BAbkGpvl.js +0 -125
- package/dist/src/transform-C_j4hUH8.js +0 -126
- package/dist/src/transform-Da86hs4M.cjs +0 -154
- package/dist/src/transform-_i1raQNo.js +0 -125
- package/dist/src/typesafe-CKXBeh82.cjs +0 -253
- package/dist/src/typesafe-D3VaGeNm.js +0 -253
- package/dist/src/typesafe-DYM-SFNH.js +0 -253
- package/dist/src/typesafe-LXmSbw8F.js +0 -254
- package/dist/src/util-41rb0XHX.js +0 -743
- package/dist/src/util-B0WlHYFM.js +0 -2049
- package/dist/src/util-BD1R_PsR.js +0 -1770
- package/dist/src/util-BLbM_ItC.js +0 -395
- package/dist/src/util-C72P0iIa.js +0 -352
- package/dist/src/util-C7MpGlJ6.cjs +0 -2305
- package/dist/src/util-CCtqagKc.js +0 -286
- package/dist/src/util-CM2Ec_uY.js +0 -2051
- package/dist/src/util-CSSAWO8v.js +0 -351
- package/dist/src/util-CkQc-fx3.cjs +0 -934
- package/dist/src/util-DcOlFTFa.cjs +0 -478
- package/dist/src/util-DluGTGVB.js +0 -395
- package/dist/src/util-PfQ60chz.js +0 -743
- package/dist/src/util-f14-CPij.js +0 -396
- package/dist/src/util-l9pXX3Eu.cjs +0 -357
- package/dist/src/util-o_BnlYqS.js +0 -2050
- package/dist/src/vertex-B_-gQgzc.js +0 -2
- package/dist/src/vertex-CU4gqoYa.js +0 -854
- package/dist/src/vertex-DZ42UHb3.js +0 -848
- package/dist/src/vertex-DsYQJA0T.cjs +0 -871
- package/dist/src/vertexLive-BvIaHKGB.js +0 -55
- package/dist/src/vertexLive-CTULqNx7.js +0 -56
- package/dist/src/vertexLive-DjJ_YQBe.cjs +0 -55
- package/dist/src/vertexLive-STUGcteB.js +0 -55
- package/dist/src/video-CAQGmtEV.js +0 -703
- package/dist/src/video-_FSTWlwX.js +0 -702
- package/dist/src/video-aBPJPWdt.js +0 -702
- package/dist/src/video-gDjkWv5m.cjs +0 -706
|
@@ -0,0 +1,1971 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { W as loadYaml, c as logger, lt as state, r as getLogLevel, u as setLogLevel } from "./logger-pCNnmVuz.js";
|
|
3
|
+
import { U as TERMINAL_MAX_WIDTH, Y as VERSION } from "./fetch-RXHsZF-J.js";
|
|
4
|
+
import { r as sleepWithAbort } from "./time-Ct6CnABs.js";
|
|
5
|
+
import { t as printBorder } from "./util-Cj9drvOY.js";
|
|
6
|
+
import { n as sha256 } from "./createHash-DqA6GWxE.js";
|
|
7
|
+
import { t as formatDuration } from "./formatDuration-22XOO9FJ.js";
|
|
8
|
+
import { t as runCommand } from "./runCommand-sU-vcN5n.js";
|
|
9
|
+
import chalk from "chalk";
|
|
10
|
+
import { z as z$1 } from "zod";
|
|
11
|
+
import fs, { existsSync, realpathSync } from "fs";
|
|
12
|
+
import path, { dirname, isAbsolute, join, relative, resolve, sep } from "path";
|
|
13
|
+
import { promisify } from "util";
|
|
14
|
+
import { execFile, spawn } from "child_process";
|
|
15
|
+
import crypto from "crypto";
|
|
16
|
+
import async from "async";
|
|
17
|
+
import { pathToFileURL } from "url";
|
|
18
|
+
import ora from "ora";
|
|
19
|
+
import { io } from "socket.io-client";
|
|
20
|
+
import binaryExtensions from "binary-extensions";
|
|
21
|
+
import { isText } from "istextorbinary";
|
|
22
|
+
import textExtensions from "text-extensions";
|
|
23
|
+
import { minimatch } from "minimatch";
|
|
24
|
+
import { simpleGit } from "simple-git";
|
|
25
|
+
import { createRequire } from "module";
|
|
26
|
+
//#region src/types/codeScan.ts
|
|
27
|
+
const CodeScanSeverity = {
|
|
28
|
+
CRITICAL: "critical",
|
|
29
|
+
HIGH: "high",
|
|
30
|
+
MEDIUM: "medium",
|
|
31
|
+
LOW: "low",
|
|
32
|
+
NONE: "none"
|
|
33
|
+
};
|
|
34
|
+
const CodeScanSeveritySchema = z$1.enum([
|
|
35
|
+
"critical",
|
|
36
|
+
"high",
|
|
37
|
+
"medium",
|
|
38
|
+
"low",
|
|
39
|
+
"none"
|
|
40
|
+
]);
|
|
41
|
+
const CodeScanOutputFormat = {
|
|
42
|
+
TEXT: "text",
|
|
43
|
+
JSON: "json",
|
|
44
|
+
SARIF: "sarif"
|
|
45
|
+
};
|
|
46
|
+
const CodeScanOutputFormatSchema = z$1.enum([
|
|
47
|
+
"text",
|
|
48
|
+
"json",
|
|
49
|
+
"sarif"
|
|
50
|
+
]);
|
|
51
|
+
function getSeverityValues(severity) {
|
|
52
|
+
switch (severity) {
|
|
53
|
+
case CodeScanSeverity.CRITICAL: return ["🔴", 4];
|
|
54
|
+
case CodeScanSeverity.HIGH: return ["🟠", 3];
|
|
55
|
+
case CodeScanSeverity.MEDIUM: return ["🟡", 2];
|
|
56
|
+
case CodeScanSeverity.LOW: return ["🔵", 1];
|
|
57
|
+
case CodeScanSeverity.NONE: return ["👍", -1];
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Get emoji representation for a severity level
|
|
62
|
+
* @param severity - The severity level
|
|
63
|
+
* @returns Emoji string representing the severity
|
|
64
|
+
*/
|
|
65
|
+
function getSeverityEmoji(severity) {
|
|
66
|
+
return getSeverityValues(severity)?.[0];
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Get numeric rank for a severity level (used for sorting)
|
|
70
|
+
* @param severity - The severity level
|
|
71
|
+
* @returns Numeric rank (higher = more severe)
|
|
72
|
+
*/
|
|
73
|
+
function getSeverityRank(severity) {
|
|
74
|
+
return getSeverityValues(severity)?.[1];
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Format severity level for display
|
|
78
|
+
* @param severity - The severity level (optional, returns empty string if undefined)
|
|
79
|
+
* @param style - Display style ('plain' or 'markdown')
|
|
80
|
+
* @returns Formatted severity string
|
|
81
|
+
*/
|
|
82
|
+
function formatSeverity(severity, style = "plain") {
|
|
83
|
+
if (!severity) return "";
|
|
84
|
+
const emoji = getSeverityEmoji(severity);
|
|
85
|
+
const displayText = severity === CodeScanSeverity.NONE ? "All Clear" : capitalize(severity);
|
|
86
|
+
if (style === "markdown") return `_${emoji} ${displayText}_\n\n`;
|
|
87
|
+
return `${emoji} ${displayText}`;
|
|
88
|
+
}
|
|
89
|
+
function filterBySeverity(comments) {
|
|
90
|
+
const validSeverities = [
|
|
91
|
+
CodeScanSeverity.CRITICAL,
|
|
92
|
+
CodeScanSeverity.HIGH,
|
|
93
|
+
CodeScanSeverity.MEDIUM,
|
|
94
|
+
CodeScanSeverity.LOW
|
|
95
|
+
];
|
|
96
|
+
return comments.filter((c) => c.severity && validSeverities.includes(c.severity));
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Count comments by severity level
|
|
100
|
+
* @param comments - Array of comments with severity property (optional severity)
|
|
101
|
+
* @returns Object with counts for each severity level
|
|
102
|
+
*/
|
|
103
|
+
function countBySeverity(comments) {
|
|
104
|
+
const issuesOnly = filterBySeverity(comments);
|
|
105
|
+
return {
|
|
106
|
+
total: issuesOnly.length,
|
|
107
|
+
critical: issuesOnly.filter((c) => c.severity === CodeScanSeverity.CRITICAL).length,
|
|
108
|
+
high: issuesOnly.filter((c) => c.severity === CodeScanSeverity.HIGH).length,
|
|
109
|
+
medium: issuesOnly.filter((c) => c.severity === CodeScanSeverity.MEDIUM).length,
|
|
110
|
+
low: issuesOnly.filter((c) => c.severity === CodeScanSeverity.LOW).length
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Helper function to capitalize the first letter of a string
|
|
115
|
+
*/
|
|
116
|
+
function capitalize(str) {
|
|
117
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Validates and parses a severity string into CodeScanSeverity enum
|
|
121
|
+
* Normalizes input by trimming whitespace and converting to lowercase
|
|
122
|
+
* @param severity - String input to validate (e.g., "high", "CRITICAL", " medium ")
|
|
123
|
+
* @returns Validated CodeScanSeverity enum value
|
|
124
|
+
* @throws {z.ZodError} if severity is not a valid CodeScanSeverity value
|
|
125
|
+
* @example
|
|
126
|
+
* validateSeverity('high') // Returns CodeScanSeverity.HIGH
|
|
127
|
+
* validateSeverity('CRITICAL') // Returns CodeScanSeverity.CRITICAL
|
|
128
|
+
* validateSeverity('invalid') // Throws ZodError
|
|
129
|
+
*/
|
|
130
|
+
function validateSeverity(severity) {
|
|
131
|
+
const normalized = severity.trim().toLowerCase();
|
|
132
|
+
return CodeScanSeveritySchema.parse(normalized);
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Schema for a valid line range in a file's diff.
|
|
136
|
+
* Used for validating PR comment line numbers.
|
|
137
|
+
*/
|
|
138
|
+
const LineRangeSchema = z$1.object({
|
|
139
|
+
start: z$1.number(),
|
|
140
|
+
end: z$1.number()
|
|
141
|
+
});
|
|
142
|
+
const FileRecordSchema = z$1.object({
|
|
143
|
+
path: z$1.string(),
|
|
144
|
+
status: z$1.string(),
|
|
145
|
+
shaA: z$1.string().nullable(),
|
|
146
|
+
shaB: z$1.string().nullable(),
|
|
147
|
+
linesAdded: z$1.number().optional(),
|
|
148
|
+
linesRemoved: z$1.number().optional(),
|
|
149
|
+
beforeSizeBytes: z$1.number().optional(),
|
|
150
|
+
afterSizeBytes: z$1.number().optional(),
|
|
151
|
+
isText: z$1.boolean().optional(),
|
|
152
|
+
skipReason: z$1.string().optional(),
|
|
153
|
+
patch: z$1.string().optional(),
|
|
154
|
+
/** Valid line ranges in the NEW file (for PR comment validation) */
|
|
155
|
+
lineRanges: z$1.array(LineRangeSchema).optional()
|
|
156
|
+
});
|
|
157
|
+
const GitMetadataSchema = z$1.object({
|
|
158
|
+
branch: z$1.string(),
|
|
159
|
+
baseBranch: z$1.string(),
|
|
160
|
+
baseRef: z$1.string(),
|
|
161
|
+
baseSha: z$1.string(),
|
|
162
|
+
compareRef: z$1.string(),
|
|
163
|
+
compareSha: z$1.string(),
|
|
164
|
+
commitMessages: z$1.array(z$1.string()),
|
|
165
|
+
author: z$1.string(),
|
|
166
|
+
timestamp: z$1.string()
|
|
167
|
+
});
|
|
168
|
+
const ScanConfigSchema = z$1.object({
|
|
169
|
+
minimumSeverity: CodeScanSeveritySchema,
|
|
170
|
+
diffsOnly: z$1.boolean(),
|
|
171
|
+
guidance: z$1.string().optional()
|
|
172
|
+
});
|
|
173
|
+
const PullRequestContextSchema = z$1.object({
|
|
174
|
+
owner: z$1.string(),
|
|
175
|
+
repo: z$1.string(),
|
|
176
|
+
number: z$1.number(),
|
|
177
|
+
sha: z$1.string()
|
|
178
|
+
});
|
|
179
|
+
z$1.object({
|
|
180
|
+
files: z$1.array(FileRecordSchema).min(1, "Files array cannot be empty"),
|
|
181
|
+
metadata: GitMetadataSchema,
|
|
182
|
+
config: ScanConfigSchema,
|
|
183
|
+
sessionId: z$1.string(),
|
|
184
|
+
pullRequest: PullRequestContextSchema.optional()
|
|
185
|
+
});
|
|
186
|
+
const CommentSchema = z$1.object({
|
|
187
|
+
file: z$1.string().nullable(),
|
|
188
|
+
startLine: z$1.number().nullable().optional(),
|
|
189
|
+
line: z$1.number().nullable(),
|
|
190
|
+
finding: z$1.string(),
|
|
191
|
+
fix: z$1.string().nullable().optional(),
|
|
192
|
+
severity: CodeScanSeveritySchema.optional(),
|
|
193
|
+
aiAgentPrompt: z$1.string().nullable().optional()
|
|
194
|
+
});
|
|
195
|
+
z$1.object({
|
|
196
|
+
inventory: z$1.string(),
|
|
197
|
+
tracing: z$1.string(),
|
|
198
|
+
analysis: z$1.string(),
|
|
199
|
+
filtering: z$1.string(),
|
|
200
|
+
fixes: z$1.string(),
|
|
201
|
+
comments: z$1.string()
|
|
202
|
+
});
|
|
203
|
+
z$1.object({
|
|
204
|
+
success: z$1.boolean(),
|
|
205
|
+
review: z$1.string().optional(),
|
|
206
|
+
comments: z$1.array(CommentSchema),
|
|
207
|
+
commentsPosted: z$1.boolean().optional(),
|
|
208
|
+
batchCount: z$1.number().optional(),
|
|
209
|
+
error: z$1.string().optional(),
|
|
210
|
+
skipReason: z$1.string().optional()
|
|
211
|
+
});
|
|
212
|
+
/**
|
|
213
|
+
* Error thrown when git operations fail
|
|
214
|
+
*/
|
|
215
|
+
var GitError = class extends Error {
|
|
216
|
+
constructor(message) {
|
|
217
|
+
super(message);
|
|
218
|
+
this.name = "GitError";
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
/**
|
|
222
|
+
* Error thrown when git metadata extraction fails
|
|
223
|
+
*/
|
|
224
|
+
var GitMetadataError = class extends Error {
|
|
225
|
+
constructor(message) {
|
|
226
|
+
super(message);
|
|
227
|
+
this.name = "GitMetadataError";
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
/**
|
|
231
|
+
* Error thrown when diff processing fails
|
|
232
|
+
*/
|
|
233
|
+
var DiffProcessorError = class extends Error {
|
|
234
|
+
constructor(message) {
|
|
235
|
+
super(message);
|
|
236
|
+
this.name = "DiffProcessorError";
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
/**
|
|
240
|
+
* Error thrown when MCP filesystem server startup fails
|
|
241
|
+
*/
|
|
242
|
+
var FilesystemMcpError = class extends Error {
|
|
243
|
+
constructor(message) {
|
|
244
|
+
super(message);
|
|
245
|
+
this.name = "FilesystemMcpError";
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
/**
|
|
249
|
+
* Error thrown when Socket.io MCP bridge connection fails
|
|
250
|
+
*/
|
|
251
|
+
var SocketIoMcpBridgeError = class extends Error {
|
|
252
|
+
constructor(message) {
|
|
253
|
+
super(message);
|
|
254
|
+
this.name = "SocketIoMcpBridgeError";
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
/**
|
|
258
|
+
* Error thrown when config file loading or parsing fails
|
|
259
|
+
*/
|
|
260
|
+
var ConfigLoadError = class extends Error {
|
|
261
|
+
constructor(message) {
|
|
262
|
+
super(message);
|
|
263
|
+
this.name = "ConfigLoadError";
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
//#endregion
|
|
267
|
+
//#region src/util/agent/agentAuth.ts
|
|
268
|
+
/**
|
|
269
|
+
* Shared auth credential resolution for agent clients.
|
|
270
|
+
*
|
|
271
|
+
* Common waterfall: CLI arg → PROMPTFOO_API_KEY env var → cloud config stored key.
|
|
272
|
+
* Agent-specific auth (e.g., OIDC, fork PR) wraps this function and adds extra strategies.
|
|
273
|
+
*/
|
|
274
|
+
let cloudConfig$1;
|
|
275
|
+
try {
|
|
276
|
+
cloudConfig$1 = (await import("./cloud-BtYF555L.js")).cloudConfig;
|
|
277
|
+
} catch (error) {
|
|
278
|
+
if (error instanceof Error && "code" in error && error.code === "MODULE_NOT_FOUND") {} else logger.debug(`Unexpected error loading cloud config: ${error}`);
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Resolve base authentication credentials using waterfall approach:
|
|
282
|
+
* 1. API key from argument (CLI --api-key or config file)
|
|
283
|
+
* 2. PROMPTFOO_API_KEY environment variable
|
|
284
|
+
* 3. API key from promptfoo auth (cloudConfig)
|
|
285
|
+
*
|
|
286
|
+
* @param opts - Optional overrides
|
|
287
|
+
* @returns Resolved auth credentials (may be empty if no auth found)
|
|
288
|
+
*/
|
|
289
|
+
function resolveBaseAuthCredentials(opts) {
|
|
290
|
+
if (opts?.apiKey) {
|
|
291
|
+
logger.debug("Using API key from CLI/config");
|
|
292
|
+
return { apiKey: opts.apiKey };
|
|
293
|
+
}
|
|
294
|
+
const envApiKey = process.env.PROMPTFOO_API_KEY;
|
|
295
|
+
if (envApiKey) {
|
|
296
|
+
logger.debug("Using API key from PROMPTFOO_API_KEY env var");
|
|
297
|
+
return { apiKey: envApiKey };
|
|
298
|
+
}
|
|
299
|
+
if (cloudConfig$1) {
|
|
300
|
+
const storedApiKey = cloudConfig$1.getApiKey();
|
|
301
|
+
if (storedApiKey) {
|
|
302
|
+
logger.debug("Using API key from promptfoo auth");
|
|
303
|
+
return { apiKey: storedApiKey };
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
return {};
|
|
307
|
+
}
|
|
308
|
+
//#endregion
|
|
309
|
+
//#region src/util/agent/agentClient.ts
|
|
310
|
+
/**
|
|
311
|
+
* Client-side agent connection utility.
|
|
312
|
+
*
|
|
313
|
+
* Creates a Socket.IO client pre-configured for the agent protocol:
|
|
314
|
+
* - Connects with agent name in handshake
|
|
315
|
+
* - Joins a session room
|
|
316
|
+
* - Provides typed lifecycle methods (start, cancel, onComplete, onError)
|
|
317
|
+
* - Passes through domain-specific events via on()/emit()
|
|
318
|
+
*/
|
|
319
|
+
let cloudConfig;
|
|
320
|
+
try {
|
|
321
|
+
cloudConfig = (await import("./cloud-BtYF555L.js")).cloudConfig;
|
|
322
|
+
} catch (error) {
|
|
323
|
+
if (error instanceof Error && "code" in error && error.code === "MODULE_NOT_FOUND") {} else logger.debug(`Unexpected error loading cloud config: ${error}`);
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Create an agent client that connects to the shared Socket.IO server.
|
|
327
|
+
*
|
|
328
|
+
* Only `agent` is required. Host, auth, and sessionId are resolved automatically
|
|
329
|
+
* but can be overridden for agents with custom needs.
|
|
330
|
+
*
|
|
331
|
+
* @returns Promise that resolves to an AgentClient once connected and joined to a room.
|
|
332
|
+
* @throws Error if connection fails or times out.
|
|
333
|
+
*/
|
|
334
|
+
async function createAgentClient(opts) {
|
|
335
|
+
const { agent, timeoutMs = 5e3 } = opts;
|
|
336
|
+
const host = opts.host ?? cloudConfig?.getApiHost();
|
|
337
|
+
if (!host) throw new Error("No API host available. Set PROMPTFOO_CLOUD_API_URL or pass host explicitly.");
|
|
338
|
+
const auth = opts.auth ?? resolveBaseAuthCredentials();
|
|
339
|
+
const sessionId = opts.sessionId ?? crypto.randomUUID();
|
|
340
|
+
return new Promise((resolve, reject) => {
|
|
341
|
+
let settled = false;
|
|
342
|
+
const cleanup = () => {
|
|
343
|
+
clearTimeout(timeoutId);
|
|
344
|
+
socket.io.reconnection(false);
|
|
345
|
+
socket.removeAllListeners();
|
|
346
|
+
socket.close();
|
|
347
|
+
};
|
|
348
|
+
const socket = io(host, {
|
|
349
|
+
transports: ["websocket"],
|
|
350
|
+
reconnection: true,
|
|
351
|
+
reconnectionDelay: 500,
|
|
352
|
+
reconnectionDelayMax: 1e4,
|
|
353
|
+
reconnectionAttempts: 5,
|
|
354
|
+
auth: {
|
|
355
|
+
agent,
|
|
356
|
+
...auth
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
socket.on("connect", () => {
|
|
360
|
+
logger.debug(`Agent client connected (agent: ${agent}, id: ${socket.id})`);
|
|
361
|
+
socket.emit("agent:join", { sessionId });
|
|
362
|
+
if (settled) return;
|
|
363
|
+
settled = true;
|
|
364
|
+
clearTimeout(timeoutId);
|
|
365
|
+
resolve({
|
|
366
|
+
sessionId,
|
|
367
|
+
start(payload) {
|
|
368
|
+
socket.emit("agent:start", payload);
|
|
369
|
+
},
|
|
370
|
+
cancel() {
|
|
371
|
+
socket.emit("agent:cancel");
|
|
372
|
+
},
|
|
373
|
+
onComplete(cb) {
|
|
374
|
+
socket.once("agent:complete", cb);
|
|
375
|
+
},
|
|
376
|
+
onError(cb) {
|
|
377
|
+
socket.once("agent:error", cb);
|
|
378
|
+
},
|
|
379
|
+
onCancelled(cb) {
|
|
380
|
+
socket.once("agent:cancelled", cb);
|
|
381
|
+
},
|
|
382
|
+
on(event, handler) {
|
|
383
|
+
socket.on(event, handler);
|
|
384
|
+
},
|
|
385
|
+
emit(event, ...args) {
|
|
386
|
+
socket.emit(event, ...args);
|
|
387
|
+
},
|
|
388
|
+
disconnect() {
|
|
389
|
+
socket.disconnect();
|
|
390
|
+
},
|
|
391
|
+
socket
|
|
392
|
+
});
|
|
393
|
+
});
|
|
394
|
+
socket.on("connect_error", (error) => {
|
|
395
|
+
if (settled) return;
|
|
396
|
+
settled = true;
|
|
397
|
+
logger.debug(`Agent client connection error: ${error.message}`);
|
|
398
|
+
cleanup();
|
|
399
|
+
reject(/* @__PURE__ */ new Error(`Failed to connect to server: ${error.message}`));
|
|
400
|
+
});
|
|
401
|
+
socket.on("disconnect", (reason) => {
|
|
402
|
+
logger.debug(`Agent client disconnected: ${reason}`);
|
|
403
|
+
});
|
|
404
|
+
socket.on("error", (error) => {
|
|
405
|
+
logger.debug(`Agent client error: ${String(error)}`);
|
|
406
|
+
});
|
|
407
|
+
socket.io.on("reconnect_failed", () => {
|
|
408
|
+
logger.error(`Agent client reconnection failed after all attempts (agent: ${agent})`);
|
|
409
|
+
});
|
|
410
|
+
const timeoutId = setTimeout(() => {
|
|
411
|
+
if (!socket.connected && !settled) {
|
|
412
|
+
settled = true;
|
|
413
|
+
cleanup();
|
|
414
|
+
reject(/* @__PURE__ */ new Error(`Connection timeout after ${timeoutMs}ms`));
|
|
415
|
+
}
|
|
416
|
+
}, timeoutMs);
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
//#endregion
|
|
420
|
+
//#region src/codeScan/config/schema.ts
|
|
421
|
+
/**
|
|
422
|
+
* Configuration Schema
|
|
423
|
+
*
|
|
424
|
+
* Zod schema for validating YAML configuration files.
|
|
425
|
+
*/
|
|
426
|
+
const ConfigSchema = z$1.object({
|
|
427
|
+
minSeverity: CodeScanSeveritySchema.optional().describe("Minimum severity level for reporting vulnerabilities"),
|
|
428
|
+
minimumSeverity: CodeScanSeveritySchema.optional().describe("Alias for minSeverity"),
|
|
429
|
+
diffsOnly: z$1.boolean().prefault(false).describe("Only scan PR diffs, skip filesystem exploration (default: explore full repo)"),
|
|
430
|
+
apiHost: z$1.string().optional().describe("Scan server URL (default: https://api.promptfoo.app)"),
|
|
431
|
+
guidance: z$1.string().optional().describe("Custom guidance for the security scan"),
|
|
432
|
+
guidanceFile: z$1.string().optional().describe("Path to file containing custom guidance")
|
|
433
|
+
}).refine((data) => !(data.guidance && data.guidanceFile), { message: "Cannot specify both guidance and guidanceFile" }).transform((data) => {
|
|
434
|
+
const minimumSeverity = data.minSeverity ?? data.minimumSeverity ?? CodeScanSeverity.MEDIUM;
|
|
435
|
+
const { minimumSeverity: _, ...rest } = data;
|
|
436
|
+
return {
|
|
437
|
+
...rest,
|
|
438
|
+
minimumSeverity
|
|
439
|
+
};
|
|
440
|
+
});
|
|
441
|
+
const DEFAULT_CONFIG = {
|
|
442
|
+
minimumSeverity: CodeScanSeverity.MEDIUM,
|
|
443
|
+
diffsOnly: false
|
|
444
|
+
};
|
|
445
|
+
//#endregion
|
|
446
|
+
//#region src/codeScan/config/loader.ts
|
|
447
|
+
/**
|
|
448
|
+
* Configuration Loader
|
|
449
|
+
*
|
|
450
|
+
* Loads and validates configuration from YAML files.
|
|
451
|
+
*/
|
|
452
|
+
/**
|
|
453
|
+
* Load configuration from a YAML file
|
|
454
|
+
* @param configPath Path to the configuration file
|
|
455
|
+
* @returns Validated configuration object
|
|
456
|
+
* @throws ConfigLoadError if file cannot be read or parsed
|
|
457
|
+
*/
|
|
458
|
+
function loadConfig(configPath) {
|
|
459
|
+
if (!fs.existsSync(configPath)) throw new ConfigLoadError(`Configuration file not found: ${configPath}`);
|
|
460
|
+
let fileContents;
|
|
461
|
+
try {
|
|
462
|
+
fileContents = fs.readFileSync(configPath, "utf8");
|
|
463
|
+
} catch (error) {
|
|
464
|
+
throw new ConfigLoadError(`Failed to read configuration file: ${error instanceof Error ? error.message : String(error)}`);
|
|
465
|
+
}
|
|
466
|
+
let rawConfig;
|
|
467
|
+
try {
|
|
468
|
+
rawConfig = loadYaml(fileContents);
|
|
469
|
+
} catch (error) {
|
|
470
|
+
throw new ConfigLoadError(`Failed to parse YAML: ${error instanceof Error ? error.message : String(error)}`);
|
|
471
|
+
}
|
|
472
|
+
const result = ConfigSchema.safeParse(rawConfig);
|
|
473
|
+
if (!result.success) throw new ConfigLoadError(`Invalid configuration: ${result.error.issues.map((err) => `${err.path.join(".")}: ${err.message}`).join(", ")}`);
|
|
474
|
+
const config = result.data;
|
|
475
|
+
if (config.guidanceFile) {
|
|
476
|
+
const guidanceFilePath = path.isAbsolute(config.guidanceFile) ? config.guidanceFile : path.resolve(path.dirname(configPath), config.guidanceFile);
|
|
477
|
+
try {
|
|
478
|
+
config.guidance = fs.readFileSync(guidanceFilePath, "utf-8");
|
|
479
|
+
} catch (error) {
|
|
480
|
+
throw new ConfigLoadError(`Failed to read guidance file "${guidanceFilePath}": ${error instanceof Error ? error.message : String(error)}`);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
return config;
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* Load configuration with defaults
|
|
487
|
+
* If no config path provided, returns default configuration
|
|
488
|
+
* Returns a clone to prevent mutation of the singleton default
|
|
489
|
+
*/
|
|
490
|
+
function loadConfigOrDefault(configPath) {
|
|
491
|
+
if (!configPath) return { ...DEFAULT_CONFIG };
|
|
492
|
+
return loadConfig(configPath);
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* Merge CLI options with configuration
|
|
496
|
+
*
|
|
497
|
+
* CLI options take precedence over config file settings
|
|
498
|
+
*
|
|
499
|
+
* @param config - Base configuration
|
|
500
|
+
* @param options - CLI options to merge
|
|
501
|
+
* @returns Merged configuration
|
|
502
|
+
*/
|
|
503
|
+
function mergeConfigWithOptions(config, options) {
|
|
504
|
+
const merged = { ...config };
|
|
505
|
+
if (options.diffsOnly !== void 0) merged.diffsOnly = options.diffsOnly;
|
|
506
|
+
if (options.minSeverity || options.minimumSeverity) merged.minimumSeverity = validateSeverity(options.minSeverity || options.minimumSeverity);
|
|
507
|
+
if (options.apiHost) merged.apiHost = options.apiHost;
|
|
508
|
+
return merged;
|
|
509
|
+
}
|
|
510
|
+
/**
|
|
511
|
+
* Resolve guidance from options or config
|
|
512
|
+
*
|
|
513
|
+
* CLI options take precedence over config file settings
|
|
514
|
+
*
|
|
515
|
+
* @param options - Options that may contain guidance
|
|
516
|
+
* @param config - Configuration that may contain guidance
|
|
517
|
+
* @returns Guidance string or undefined
|
|
518
|
+
* @throws Error if both guidance and guidanceFile are specified
|
|
519
|
+
*/
|
|
520
|
+
function resolveGuidance(options, config) {
|
|
521
|
+
if (options.guidance && options.guidanceFile) throw new Error("Cannot specify both --guidance and --guidance-file options");
|
|
522
|
+
if (options.guidance) return options.guidance;
|
|
523
|
+
if (options.guidanceFile) {
|
|
524
|
+
const absoluteGuidancePath = path.resolve(options.guidanceFile);
|
|
525
|
+
try {
|
|
526
|
+
return fs.readFileSync(absoluteGuidancePath, "utf-8");
|
|
527
|
+
} catch (error) {
|
|
528
|
+
throw new Error(`Failed to read guidance file: ${absoluteGuidancePath} - ${error instanceof Error ? error.message : String(error)}`);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
return config.guidance;
|
|
532
|
+
}
|
|
533
|
+
//#endregion
|
|
534
|
+
//#region src/codeScan/git/diff.ts
|
|
535
|
+
/**
|
|
536
|
+
* Git Branch Validation
|
|
537
|
+
*/
|
|
538
|
+
/**
|
|
539
|
+
* Validate that we're on a branch (not detached HEAD)
|
|
540
|
+
* @param git Simple git instance
|
|
541
|
+
* @throws GitError if not on a branch
|
|
542
|
+
*/
|
|
543
|
+
async function validateOnBranch(git) {
|
|
544
|
+
try {
|
|
545
|
+
const status = await git.status();
|
|
546
|
+
if (status.detached) throw new GitError("Not on a branch. Please checkout a branch before running the scan.");
|
|
547
|
+
if (!status.current) throw new GitError("Could not determine current branch.");
|
|
548
|
+
return status.current;
|
|
549
|
+
} catch (error) {
|
|
550
|
+
if (error instanceof GitError) throw error;
|
|
551
|
+
throw new GitError(`Failed to validate branch: ${error instanceof Error ? error.message : String(error)}`);
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
//#endregion
|
|
555
|
+
//#region src/codeScan/constants/filtering.ts
|
|
556
|
+
/**
|
|
557
|
+
* File Filtering Constants
|
|
558
|
+
*
|
|
559
|
+
* Shared constants for code scan file filtering used by both CLI and server.
|
|
560
|
+
* Kept separate so server callers do not load the diff-processing pipeline.
|
|
561
|
+
*/
|
|
562
|
+
const DENYLIST_PATTERNS = [
|
|
563
|
+
"**/node_modules/**",
|
|
564
|
+
"**/dist/**",
|
|
565
|
+
"**/build/**",
|
|
566
|
+
"**/.next/**",
|
|
567
|
+
"**/.venv/**",
|
|
568
|
+
"**/__pycache__/**",
|
|
569
|
+
"**/*.lock",
|
|
570
|
+
"**/package-lock.json",
|
|
571
|
+
"**/yarn.lock",
|
|
572
|
+
"**/pnpm-lock.yaml",
|
|
573
|
+
"**/Cargo.lock",
|
|
574
|
+
"**/poetry.lock",
|
|
575
|
+
"**/composer.lock",
|
|
576
|
+
"**/Pipfile.lock",
|
|
577
|
+
"**/*.min.js",
|
|
578
|
+
"**/*.map",
|
|
579
|
+
"**/*.bin",
|
|
580
|
+
"**/*.exe",
|
|
581
|
+
"**/*.dll",
|
|
582
|
+
"**/*.so",
|
|
583
|
+
"**/*.dylib",
|
|
584
|
+
"**/*.zip",
|
|
585
|
+
"**/*.tar",
|
|
586
|
+
"**/*.gz",
|
|
587
|
+
"**/*.jpg",
|
|
588
|
+
"**/*.jpeg",
|
|
589
|
+
"**/*.png",
|
|
590
|
+
"**/*.gif",
|
|
591
|
+
"**/*.pdf",
|
|
592
|
+
"**/*.mp4",
|
|
593
|
+
"**/*.mov"
|
|
594
|
+
];
|
|
595
|
+
const MAX_PATCH_SIZE_BYTES = 204800;
|
|
596
|
+
function isInDenylist(filePath) {
|
|
597
|
+
return DENYLIST_PATTERNS.some((pattern) => minimatch(filePath, pattern));
|
|
598
|
+
}
|
|
599
|
+
//#endregion
|
|
600
|
+
//#region src/codeScan/util/diffHunkParser.ts
|
|
601
|
+
/**
|
|
602
|
+
* Regular expression to match unified diff hunk headers.
|
|
603
|
+
*
|
|
604
|
+
* Format: @@ -oldStart[,oldCount] +newStart[,newCount] @@
|
|
605
|
+
* The count is optional and defaults to 1 if not present.
|
|
606
|
+
*
|
|
607
|
+
* Examples:
|
|
608
|
+
* - @@ -10,7 +20,8 @@ -> old: 10,7 new: 20,8
|
|
609
|
+
* - @@ -1 +1 @@ -> old: 1,1 new: 1,1
|
|
610
|
+
* - @@ -0,0 +1,5 @@ -> old: 0,0 new: 1,5 (new file)
|
|
611
|
+
*/
|
|
612
|
+
const HUNK_HEADER_REGEX = /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/;
|
|
613
|
+
/**
|
|
614
|
+
* Parse a unified diff hunk header line.
|
|
615
|
+
*
|
|
616
|
+
* @param line - A line that may be a hunk header
|
|
617
|
+
* @returns Parsed hunk information, or null if not a hunk header
|
|
618
|
+
*
|
|
619
|
+
* @example
|
|
620
|
+
* ```typescript
|
|
621
|
+
* parseHunkHeader('@@ -10,7 +20,8 @@ function foo() {')
|
|
622
|
+
* // Returns: { oldStart: 10, oldCount: 7, newStart: 20, newCount: 8 }
|
|
623
|
+
*
|
|
624
|
+
* parseHunkHeader('regular code line')
|
|
625
|
+
* // Returns: null
|
|
626
|
+
* ```
|
|
627
|
+
*/
|
|
628
|
+
function parseHunkHeader(line) {
|
|
629
|
+
const match = line.match(HUNK_HEADER_REGEX);
|
|
630
|
+
if (!match) return null;
|
|
631
|
+
return {
|
|
632
|
+
oldStart: parseInt(match[1], 10),
|
|
633
|
+
oldCount: match[2] ? parseInt(match[2], 10) : 1,
|
|
634
|
+
newStart: parseInt(match[3], 10),
|
|
635
|
+
newCount: match[4] ? parseInt(match[4], 10) : 1
|
|
636
|
+
};
|
|
637
|
+
}
|
|
638
|
+
//#endregion
|
|
639
|
+
//#region src/codeScan/git/diffAnnotator.ts
|
|
640
|
+
/**
|
|
641
|
+
* Diff Annotator
|
|
642
|
+
*
|
|
643
|
+
* Adds absolute line numbers to unified diff format for easier LLM processing.
|
|
644
|
+
* This eliminates the need for LLMs to manually calculate line numbers from hunk headers.
|
|
645
|
+
*
|
|
646
|
+
* Also extracts valid line ranges for each file, which can be used to validate
|
|
647
|
+
* and clamp comment line numbers for GitHub PR reviews.
|
|
648
|
+
*/
|
|
649
|
+
/**
|
|
650
|
+
* Annotate a unified diff patch with absolute line numbers and extract valid line ranges.
|
|
651
|
+
*/
|
|
652
|
+
function annotateDiffWithLineRanges(patch) {
|
|
653
|
+
if (!patch || patch.trim() === "") return {
|
|
654
|
+
annotatedDiff: patch,
|
|
655
|
+
lineRanges: []
|
|
656
|
+
};
|
|
657
|
+
const lines = patch.split("\n");
|
|
658
|
+
const result = [];
|
|
659
|
+
const lineRanges = [];
|
|
660
|
+
let currentNewLine = 0;
|
|
661
|
+
let inHunk = false;
|
|
662
|
+
let hunkStartLine = 0;
|
|
663
|
+
for (let i = 0; i < lines.length; i++) {
|
|
664
|
+
const line = lines[i];
|
|
665
|
+
const isLastLine = i === lines.length - 1;
|
|
666
|
+
const hunkHeader = parseHunkHeader(line);
|
|
667
|
+
if (hunkHeader) {
|
|
668
|
+
if (hunkStartLine > 0 && currentNewLine > hunkStartLine) lineRanges.push({
|
|
669
|
+
start: hunkStartLine,
|
|
670
|
+
end: currentNewLine - 1
|
|
671
|
+
});
|
|
672
|
+
currentNewLine = hunkHeader.newStart;
|
|
673
|
+
inHunk = true;
|
|
674
|
+
hunkStartLine = hunkHeader.newCount === 0 ? 0 : hunkHeader.newStart;
|
|
675
|
+
result.push(line);
|
|
676
|
+
continue;
|
|
677
|
+
}
|
|
678
|
+
if (!inHunk) {
|
|
679
|
+
result.push(line);
|
|
680
|
+
continue;
|
|
681
|
+
}
|
|
682
|
+
if (line.startsWith("-") || line.startsWith("\\") || line === "" && isLastLine) result.push(line);
|
|
683
|
+
else {
|
|
684
|
+
result.push(`L${currentNewLine}: ${line}`);
|
|
685
|
+
currentNewLine++;
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
if (hunkStartLine > 0 && currentNewLine > hunkStartLine) lineRanges.push({
|
|
689
|
+
start: hunkStartLine,
|
|
690
|
+
end: currentNewLine - 1
|
|
691
|
+
});
|
|
692
|
+
return {
|
|
693
|
+
annotatedDiff: result.join("\n"),
|
|
694
|
+
lineRanges
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
//#endregion
|
|
698
|
+
//#region src/codeScan/git/rawDiffParser.ts
|
|
699
|
+
/**
|
|
700
|
+
* Parse git diff --raw -z output
|
|
701
|
+
*
|
|
702
|
+
* Format for normal operations (M, A, D):
|
|
703
|
+
* :oldmode newmode oldsha newsha status\0path\0
|
|
704
|
+
*
|
|
705
|
+
* Format for renames/copies (R, C):
|
|
706
|
+
* :oldmode newmode oldsha newsha status\0oldpath\0newpath\0
|
|
707
|
+
*
|
|
708
|
+
* Note: Rename/Copy status includes similarity (e.g., R100, R90, C100)
|
|
709
|
+
*/
|
|
710
|
+
function parseRawDiff(rawOutput) {
|
|
711
|
+
const entries = rawOutput.split("\0").filter(Boolean);
|
|
712
|
+
const results = [];
|
|
713
|
+
let i = 0;
|
|
714
|
+
while (i < entries.length) {
|
|
715
|
+
const metaLine = entries[i];
|
|
716
|
+
i++;
|
|
717
|
+
if (!metaLine || i >= entries.length) break;
|
|
718
|
+
const parts = metaLine.trim().split(/\s+/);
|
|
719
|
+
if (parts.length < 5) continue;
|
|
720
|
+
const shaA = parts[2] === "0000000000000000000000000000000000000000" ? null : parts[2];
|
|
721
|
+
const shaB = parts[3] === "0000000000000000000000000000000000000000" ? null : parts[3];
|
|
722
|
+
const status = parts[4];
|
|
723
|
+
if (status.startsWith("R") || status.startsWith("C")) i++;
|
|
724
|
+
const filePath = entries[i];
|
|
725
|
+
i++;
|
|
726
|
+
if (!filePath) continue;
|
|
727
|
+
results.push({
|
|
728
|
+
path: filePath,
|
|
729
|
+
status,
|
|
730
|
+
shaA,
|
|
731
|
+
shaB
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
return results;
|
|
735
|
+
}
|
|
736
|
+
//#endregion
|
|
737
|
+
//#region src/codeScan/git/diffProcessor.ts
|
|
738
|
+
/**
|
|
739
|
+
* Diff Processor
|
|
740
|
+
*
|
|
741
|
+
* Focused pipeline for processing git diffs:
|
|
742
|
+
* 1. Discover changed files
|
|
743
|
+
* 2. Filter denylist (early exit)
|
|
744
|
+
* 3. Collect blob sizes and filter large files
|
|
745
|
+
* 4. Determine text/binary status
|
|
746
|
+
* 5. Generate per-file patches
|
|
747
|
+
*/
|
|
748
|
+
const PATCH_CONCURRENCY = 8;
|
|
749
|
+
const TEXT_DETECTION_CONCURRENCY = 16;
|
|
750
|
+
/**
|
|
751
|
+
* Parse git diff --numstat -z output.
|
|
752
|
+
* Normal entries: added\tremoved\tpath\0
|
|
753
|
+
* Renames/copies: added\tremoved\t\0oldpath\0newpath\0
|
|
754
|
+
*/
|
|
755
|
+
function parseNumstat(numstatOutput) {
|
|
756
|
+
const map = /* @__PURE__ */ new Map();
|
|
757
|
+
const records = numstatOutput.split("\0");
|
|
758
|
+
for (let i = 0; i < records.length; i++) {
|
|
759
|
+
const record = records[i];
|
|
760
|
+
const firstTab = record.indexOf(" ");
|
|
761
|
+
const secondTab = record.indexOf(" ", firstTab + 1);
|
|
762
|
+
if (firstTab === -1 || secondTab === -1) continue;
|
|
763
|
+
const added = record.slice(0, firstTab);
|
|
764
|
+
const removed = record.slice(firstTab + 1, secondTab);
|
|
765
|
+
let filePath = record.slice(secondTab + 1);
|
|
766
|
+
if (!filePath) {
|
|
767
|
+
i += 2;
|
|
768
|
+
filePath = records[i];
|
|
769
|
+
}
|
|
770
|
+
if (!filePath) continue;
|
|
771
|
+
map.set(filePath, {
|
|
772
|
+
linesAdded: added === "-" ? 0 : Number.parseInt(added, 10),
|
|
773
|
+
linesRemoved: removed === "-" ? 0 : Number.parseInt(removed, 10)
|
|
774
|
+
});
|
|
775
|
+
}
|
|
776
|
+
return map;
|
|
777
|
+
}
|
|
778
|
+
async function discoverChangedFiles(repoPath, base, compare) {
|
|
779
|
+
const [rawResult, numstatResult] = await Promise.all([runCommand("git", [
|
|
780
|
+
"diff",
|
|
781
|
+
"--raw",
|
|
782
|
+
"-z",
|
|
783
|
+
"--no-color",
|
|
784
|
+
"--no-ext-diff",
|
|
785
|
+
"--no-abbrev",
|
|
786
|
+
`${base}...${compare}`
|
|
787
|
+
], { cwd: repoPath }), runCommand("git", [
|
|
788
|
+
"diff",
|
|
789
|
+
"--numstat",
|
|
790
|
+
"-z",
|
|
791
|
+
`${base}...${compare}`
|
|
792
|
+
], { cwd: repoPath })]);
|
|
793
|
+
const rawFiles = parseRawDiff(rawResult.stdout);
|
|
794
|
+
const numstatMap = parseNumstat(numstatResult.stdout);
|
|
795
|
+
return rawFiles.map((file) => {
|
|
796
|
+
const stats = numstatMap.get(file.path);
|
|
797
|
+
return {
|
|
798
|
+
path: file.path,
|
|
799
|
+
status: file.status,
|
|
800
|
+
shaA: file.shaA,
|
|
801
|
+
shaB: file.shaB,
|
|
802
|
+
linesAdded: stats?.linesAdded,
|
|
803
|
+
linesRemoved: stats?.linesRemoved
|
|
804
|
+
};
|
|
805
|
+
});
|
|
806
|
+
}
|
|
807
|
+
function filterDenylist(files) {
|
|
808
|
+
return files.map((file) => {
|
|
809
|
+
if (isInDenylist(file.path)) return {
|
|
810
|
+
...file,
|
|
811
|
+
skipReason: "denylist"
|
|
812
|
+
};
|
|
813
|
+
return file;
|
|
814
|
+
});
|
|
815
|
+
}
|
|
816
|
+
async function collectBlobSizes(repoPath, files) {
|
|
817
|
+
const shas = /* @__PURE__ */ new Set();
|
|
818
|
+
for (const file of files) {
|
|
819
|
+
if (file.skipReason) continue;
|
|
820
|
+
if (file.shaA) shas.add(file.shaA);
|
|
821
|
+
if (file.shaB) shas.add(file.shaB);
|
|
822
|
+
}
|
|
823
|
+
if (shas.size === 0) return /* @__PURE__ */ new Map();
|
|
824
|
+
const shaList = Array.from(shas).join("\n");
|
|
825
|
+
const result = await runCommand("git", ["cat-file", "--batch-check=%(objectname) %(objecttype) %(objectsize)"], {
|
|
826
|
+
cwd: repoPath,
|
|
827
|
+
input: shaList
|
|
828
|
+
});
|
|
829
|
+
const sizeMap = /* @__PURE__ */ new Map();
|
|
830
|
+
for (const line of result.stdout.split("\n")) {
|
|
831
|
+
if (!line.trim()) continue;
|
|
832
|
+
const parts = line.split(/\s+/);
|
|
833
|
+
if (parts.length < 3) continue;
|
|
834
|
+
const sha = parts[0];
|
|
835
|
+
const size = Number.parseInt(parts[2], 10);
|
|
836
|
+
sizeMap.set(sha, size);
|
|
837
|
+
}
|
|
838
|
+
return sizeMap;
|
|
839
|
+
}
|
|
840
|
+
function attachBlobSizesAndFilter(files, sizeMap) {
|
|
841
|
+
return files.map((file) => {
|
|
842
|
+
if (file.skipReason) return file;
|
|
843
|
+
const beforeSize = file.shaA ? sizeMap.get(file.shaA) : void 0;
|
|
844
|
+
const afterSize = file.shaB ? sizeMap.get(file.shaB) : void 0;
|
|
845
|
+
const tooLarge = beforeSize !== void 0 && beforeSize > 512e3 || afterSize !== void 0 && afterSize > 512e3;
|
|
846
|
+
return {
|
|
847
|
+
...file,
|
|
848
|
+
beforeSizeBytes: beforeSize,
|
|
849
|
+
afterSizeBytes: afterSize,
|
|
850
|
+
...tooLarge && { skipReason: "too large" }
|
|
851
|
+
};
|
|
852
|
+
});
|
|
853
|
+
}
|
|
854
|
+
async function isBlobText(repoPath, sha) {
|
|
855
|
+
try {
|
|
856
|
+
const result = await runCommand("git", [
|
|
857
|
+
"cat-file",
|
|
858
|
+
"blob",
|
|
859
|
+
sha
|
|
860
|
+
], {
|
|
861
|
+
cwd: repoPath,
|
|
862
|
+
encoding: "buffer"
|
|
863
|
+
});
|
|
864
|
+
return isText(null, result.stdout) === true;
|
|
865
|
+
} catch {
|
|
866
|
+
return false;
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
/**
|
|
870
|
+
* Check file extension against known text and binary extension lists
|
|
871
|
+
* @returns 'text' if known text extension, 'binary' if known binary extension, 'unknown' otherwise
|
|
872
|
+
*/
|
|
873
|
+
function getExtensionType(filePath) {
|
|
874
|
+
const ext = path.extname(filePath).toLowerCase().slice(1);
|
|
875
|
+
if (textExtensions.includes(ext)) return "text";
|
|
876
|
+
if (binaryExtensions.includes(ext)) return "binary";
|
|
877
|
+
return "unknown";
|
|
878
|
+
}
|
|
879
|
+
/**
|
|
880
|
+
* Determine text/binary status for a single file
|
|
881
|
+
* Uses 2-tier approach: extension lists first, then blob content analysis for unknown extensions
|
|
882
|
+
*/
|
|
883
|
+
async function determineTextStatusForFile(repoPath, file) {
|
|
884
|
+
if (file.skipReason) return file;
|
|
885
|
+
const extensionType = getExtensionType(file.path);
|
|
886
|
+
let textStatus = extensionType === "text";
|
|
887
|
+
if (extensionType === "unknown") {
|
|
888
|
+
const checkSha = file.shaB || file.shaA;
|
|
889
|
+
textStatus = checkSha ? await isBlobText(repoPath, checkSha) : false;
|
|
890
|
+
}
|
|
891
|
+
return {
|
|
892
|
+
...file,
|
|
893
|
+
isText: textStatus,
|
|
894
|
+
...!textStatus && { skipReason: "binary" }
|
|
895
|
+
};
|
|
896
|
+
}
|
|
897
|
+
async function determineTextStatus(repoPath, files) {
|
|
898
|
+
return async.mapLimit(files, TEXT_DETECTION_CONCURRENCY, async (file) => determineTextStatusForFile(repoPath, file));
|
|
899
|
+
}
|
|
900
|
+
async function generatePatchForFile(repoPath, base, compare, file) {
|
|
901
|
+
const filePath = file.path;
|
|
902
|
+
try {
|
|
903
|
+
const patch = (await runCommand("git", [
|
|
904
|
+
"diff",
|
|
905
|
+
"--patch",
|
|
906
|
+
"--unified=3",
|
|
907
|
+
"--no-color",
|
|
908
|
+
"--no-ext-diff",
|
|
909
|
+
`${base}...${compare}`,
|
|
910
|
+
"--",
|
|
911
|
+
filePath
|
|
912
|
+
], {
|
|
913
|
+
cwd: repoPath,
|
|
914
|
+
maxBuffer: MAX_PATCH_SIZE_BYTES
|
|
915
|
+
})).stdout;
|
|
916
|
+
if (Buffer.byteLength(patch, "utf8") > 204800) return {
|
|
917
|
+
...file,
|
|
918
|
+
skipReason: "patch too large"
|
|
919
|
+
};
|
|
920
|
+
const { annotatedDiff, lineRanges } = annotateDiffWithLineRanges(patch);
|
|
921
|
+
return {
|
|
922
|
+
...file,
|
|
923
|
+
patch: annotatedDiff,
|
|
924
|
+
lineRanges
|
|
925
|
+
};
|
|
926
|
+
} catch (err) {
|
|
927
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
928
|
+
if (errorMessage.includes("maxBuffer") || errorMessage.includes("stdout maxBuffer")) {
|
|
929
|
+
logger.debug(`git diff --patch ${filePath} exceeded maxBuffer (${MAX_PATCH_SIZE_BYTES} bytes) - patch too large`);
|
|
930
|
+
return {
|
|
931
|
+
...file,
|
|
932
|
+
skipReason: "patch too large"
|
|
933
|
+
};
|
|
934
|
+
}
|
|
935
|
+
logger.debug(`git diff --patch ${filePath} failed: ${errorMessage} - skipping file`);
|
|
936
|
+
return {
|
|
937
|
+
...file,
|
|
938
|
+
skipReason: "diff error"
|
|
939
|
+
};
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
async function generatePatches(repoPath, base, compare, files) {
|
|
943
|
+
return async.mapLimit(files, PATCH_CONCURRENCY, async (file) => {
|
|
944
|
+
if (file.skipReason) return file;
|
|
945
|
+
return await generatePatchForFile(repoPath, base, compare, file);
|
|
946
|
+
});
|
|
947
|
+
}
|
|
948
|
+
async function processDiff(repoPath, base, compare = "HEAD") {
|
|
949
|
+
try {
|
|
950
|
+
let files = await discoverChangedFiles(repoPath, base, compare);
|
|
951
|
+
if (files.length === 0) return files;
|
|
952
|
+
files = filterDenylist(files);
|
|
953
|
+
if (!files.some((file) => !file.skipReason)) return files;
|
|
954
|
+
const sizeMap = await collectBlobSizes(repoPath, files);
|
|
955
|
+
files = attachBlobSizesAndFilter(files, sizeMap);
|
|
956
|
+
if (!files.some((file) => !file.skipReason)) return files;
|
|
957
|
+
files = await determineTextStatus(repoPath, files);
|
|
958
|
+
if (!files.some((file) => !file.skipReason)) return files;
|
|
959
|
+
return await generatePatches(repoPath, base, compare, files);
|
|
960
|
+
} catch (error) {
|
|
961
|
+
if (error instanceof DiffProcessorError) throw error;
|
|
962
|
+
throw new DiffProcessorError(`Failed to process diff: ${error instanceof Error ? error.message : String(error)}`);
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
//#endregion
|
|
966
|
+
//#region src/codeScan/git/metadata.ts
|
|
967
|
+
/**
|
|
968
|
+
* Git Metadata Extraction
|
|
969
|
+
*
|
|
970
|
+
* Extracts metadata about the current branch and commits.
|
|
971
|
+
*/
|
|
972
|
+
/**
|
|
973
|
+
* Extract git metadata for the comparison
|
|
974
|
+
* @param repoPath Path to the git repository
|
|
975
|
+
* @param baseBranch Base branch or commit
|
|
976
|
+
* @param compareRef Compare branch or commit
|
|
977
|
+
* @returns Git metadata object
|
|
978
|
+
*/
|
|
979
|
+
async function extractMetadata(repoPath, baseBranch, compareRef) {
|
|
980
|
+
const git = simpleGit(repoPath);
|
|
981
|
+
try {
|
|
982
|
+
const baseSha = (await git.revparse([baseBranch])).trim();
|
|
983
|
+
const compareSha = (await git.revparse([compareRef])).trim();
|
|
984
|
+
const log = await git.log({
|
|
985
|
+
from: baseBranch,
|
|
986
|
+
to: compareRef
|
|
987
|
+
});
|
|
988
|
+
return {
|
|
989
|
+
branch: compareRef,
|
|
990
|
+
baseBranch,
|
|
991
|
+
baseRef: baseBranch,
|
|
992
|
+
baseSha,
|
|
993
|
+
compareRef,
|
|
994
|
+
compareSha,
|
|
995
|
+
commitMessages: log.all.map((commit) => {
|
|
996
|
+
return `${commit.hash.substring(0, 7)}: ${commit.message}`;
|
|
997
|
+
}),
|
|
998
|
+
author: log.latest?.author_name || "Unknown",
|
|
999
|
+
timestamp: log.latest?.date || (/* @__PURE__ */ new Date()).toISOString()
|
|
1000
|
+
};
|
|
1001
|
+
} catch (error) {
|
|
1002
|
+
throw new GitMetadataError(`Failed to extract git metadata: ${error instanceof Error ? error.message : String(error)}`);
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
//#endregion
|
|
1006
|
+
//#region src/codeScan/mcp/filesystem.ts
|
|
1007
|
+
/**
|
|
1008
|
+
* Filesystem MCP Server Management
|
|
1009
|
+
*
|
|
1010
|
+
* Spawns and manages the @modelcontextprotocol/server-filesystem child process.
|
|
1011
|
+
*/
|
|
1012
|
+
const require = createRequire(import.meta.url);
|
|
1013
|
+
const execFileAsync = promisify(execFile);
|
|
1014
|
+
const FILESYSTEM_MCP_READY_MARKER = "running on stdio";
|
|
1015
|
+
const FILESYSTEM_MCP_READY_TIMEOUT_MS = 3e4;
|
|
1016
|
+
function formatFilesystemMcpExitReason(code, signal) {
|
|
1017
|
+
return code === null ? signal ? `signal ${signal}` : "unknown reason" : `code ${code}`;
|
|
1018
|
+
}
|
|
1019
|
+
const FILESYSTEM_MCP_ENV_KEYS = /* @__PURE__ */ new Set([
|
|
1020
|
+
"HOME",
|
|
1021
|
+
"PATH",
|
|
1022
|
+
"SYSTEMROOT",
|
|
1023
|
+
"TEMP",
|
|
1024
|
+
"TMP",
|
|
1025
|
+
"TMPDIR",
|
|
1026
|
+
"USERPROFILE"
|
|
1027
|
+
]);
|
|
1028
|
+
function createFilesystemMcpEnv() {
|
|
1029
|
+
return Object.fromEntries(Object.entries(process.env).filter(([key]) => FILESYSTEM_MCP_ENV_KEYS.has(key.toUpperCase())));
|
|
1030
|
+
}
|
|
1031
|
+
function resolveFilesystemMcpServerEntry() {
|
|
1032
|
+
try {
|
|
1033
|
+
return require.resolve("@modelcontextprotocol/server-filesystem/dist/index.js");
|
|
1034
|
+
} catch {
|
|
1035
|
+
throw new Error("The @modelcontextprotocol/server-filesystem package is required to scan beyond the diff. Reinstall promptfoo with optional dependencies or use --diffs-only.");
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
function getFilesystemMcpLaunch(rootDir) {
|
|
1039
|
+
const heapLimit = [...(process.env.NODE_OPTIONS ?? "").matchAll(/(?:^|\s)--max[-_]old[-_]space[-_]size(?:=|\s+)([1-9]\d*)(?=\s|$)/g)].at(-1)?.[1];
|
|
1040
|
+
const nodeArgs = heapLimit ? [`--max-old-space-size=${heapLimit}`] : [];
|
|
1041
|
+
const canonicalRoot = realpathSync(rootDir);
|
|
1042
|
+
const outsideRoot = (file) => {
|
|
1043
|
+
const canonicalFile = realpathSync(file);
|
|
1044
|
+
const relativePath = relative(canonicalRoot, canonicalFile);
|
|
1045
|
+
if (relativePath !== ".." && !relativePath.startsWith(`..${sep}`) && !isAbsolute(relativePath)) throw new Error("The filesystem MCP launcher must be installed outside the scanned repository. Use an external promptfoo installation or --diffs-only.");
|
|
1046
|
+
return canonicalFile;
|
|
1047
|
+
};
|
|
1048
|
+
const entry = outsideRoot(resolveFilesystemMcpServerEntry());
|
|
1049
|
+
if (!process.versions.pnp) return {
|
|
1050
|
+
args: [
|
|
1051
|
+
...nodeArgs,
|
|
1052
|
+
entry,
|
|
1053
|
+
canonicalRoot
|
|
1054
|
+
],
|
|
1055
|
+
cwd: dirname(entry)
|
|
1056
|
+
};
|
|
1057
|
+
const pnpLoader = outsideRoot(require.resolve("pnpapi"));
|
|
1058
|
+
const esmLoader = join(dirname(pnpLoader), ".pnp.loader.mjs");
|
|
1059
|
+
if (!existsSync(esmLoader)) throw new Error("The filesystem MCP server needs Yarn ESM support. Enable pnpEnableEsmLoader and reinstall, use nodeLinker: node-modules, or use --diffs-only.");
|
|
1060
|
+
return {
|
|
1061
|
+
args: [
|
|
1062
|
+
...nodeArgs,
|
|
1063
|
+
"--require",
|
|
1064
|
+
pnpLoader,
|
|
1065
|
+
"--experimental-loader",
|
|
1066
|
+
pathToFileURL(outsideRoot(esmLoader)).href,
|
|
1067
|
+
entry,
|
|
1068
|
+
canonicalRoot
|
|
1069
|
+
],
|
|
1070
|
+
cwd: dirname(pnpLoader)
|
|
1071
|
+
};
|
|
1072
|
+
}
|
|
1073
|
+
/**
|
|
1074
|
+
* Start the filesystem MCP server as a child process
|
|
1075
|
+
* @param rootDir Absolute path to root directory for filesystem access
|
|
1076
|
+
* @returns Child process handle
|
|
1077
|
+
*/
|
|
1078
|
+
function startFilesystemMcpServer(rootDir) {
|
|
1079
|
+
if (!isAbsolute(rootDir)) throw new FilesystemMcpError(`Root directory must be an absolute path, got: ${rootDir}`);
|
|
1080
|
+
const absoluteRootDir = resolve(rootDir);
|
|
1081
|
+
logger.debug("Starting filesystem MCP server...");
|
|
1082
|
+
logger.debug(`Root directory: ${absoluteRootDir}`);
|
|
1083
|
+
try {
|
|
1084
|
+
const { args, cwd } = getFilesystemMcpLaunch(absoluteRootDir);
|
|
1085
|
+
const mcpProcess = spawn(process.execPath, args, {
|
|
1086
|
+
stdio: [
|
|
1087
|
+
"pipe",
|
|
1088
|
+
"pipe",
|
|
1089
|
+
"pipe"
|
|
1090
|
+
],
|
|
1091
|
+
cwd,
|
|
1092
|
+
env: createFilesystemMcpEnv()
|
|
1093
|
+
});
|
|
1094
|
+
mcpProcess.stderr?.on("data", (chunk) => {
|
|
1095
|
+
const message = chunk.toString("utf8");
|
|
1096
|
+
if (message.includes("Failed to request initial roots from client")) return;
|
|
1097
|
+
logger.debug(`MCP server stderr: ${message.trim()}`);
|
|
1098
|
+
});
|
|
1099
|
+
mcpProcess.on("error", (error) => {
|
|
1100
|
+
logger.error(`MCP server process error: ${error.message}`);
|
|
1101
|
+
});
|
|
1102
|
+
mcpProcess.on("exit", (code, signal) => {
|
|
1103
|
+
if (code !== null && code !== 0) logger.debug(`MCP server exited with code ${code}`);
|
|
1104
|
+
else if (signal) logger.debug(`MCP server terminated by signal ${signal}`);
|
|
1105
|
+
});
|
|
1106
|
+
logger.debug(`MCP server started (pid: ${mcpProcess.pid})`);
|
|
1107
|
+
return mcpProcess;
|
|
1108
|
+
} catch (error) {
|
|
1109
|
+
throw new FilesystemMcpError(`Failed to start filesystem MCP server: ${error instanceof Error ? error.message : String(error)}`);
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
/**
|
|
1113
|
+
* Wait until the filesystem MCP server is ready to accept JSON-RPC messages.
|
|
1114
|
+
*
|
|
1115
|
+
* The server starts reading stdin only after it validates its root directory.
|
|
1116
|
+
* Announcing the runner before that point lets the cloud side send initialize too early.
|
|
1117
|
+
*/
|
|
1118
|
+
function waitForFilesystemMcpServerReady(mcpProcess, timeoutMs = FILESYSTEM_MCP_READY_TIMEOUT_MS) {
|
|
1119
|
+
if (mcpProcess.exitCode !== null || mcpProcess.signalCode !== null || mcpProcess.killed) return Promise.reject(new FilesystemMcpError(`Filesystem MCP server exited before ready: ${formatFilesystemMcpExitReason(mcpProcess.exitCode, mcpProcess.signalCode)}`));
|
|
1120
|
+
const stderr = mcpProcess.stderr;
|
|
1121
|
+
if (!stderr) return Promise.reject(new FilesystemMcpError("Filesystem MCP server stderr pipe unavailable"));
|
|
1122
|
+
return new Promise((resolve, reject) => {
|
|
1123
|
+
let stderrBuffer = "";
|
|
1124
|
+
let settled = false;
|
|
1125
|
+
const cleanup = () => {
|
|
1126
|
+
clearTimeout(timeout);
|
|
1127
|
+
stderr.off("data", onStderr);
|
|
1128
|
+
mcpProcess.off("error", onError);
|
|
1129
|
+
mcpProcess.off("exit", onExit);
|
|
1130
|
+
};
|
|
1131
|
+
const settle = (callback) => {
|
|
1132
|
+
if (!settled) {
|
|
1133
|
+
settled = true;
|
|
1134
|
+
cleanup();
|
|
1135
|
+
callback();
|
|
1136
|
+
}
|
|
1137
|
+
};
|
|
1138
|
+
const onStderr = (chunk) => {
|
|
1139
|
+
stderrBuffer += chunk.toString("utf8");
|
|
1140
|
+
if (stderrBuffer.includes(FILESYSTEM_MCP_READY_MARKER)) {
|
|
1141
|
+
settle(resolve);
|
|
1142
|
+
return;
|
|
1143
|
+
}
|
|
1144
|
+
if (stderrBuffer.length > 4096) stderrBuffer = stderrBuffer.slice(-4096);
|
|
1145
|
+
};
|
|
1146
|
+
const onError = (error) => {
|
|
1147
|
+
settle(() => {
|
|
1148
|
+
reject(new FilesystemMcpError(`Filesystem MCP server error before ready: ${error.message}`));
|
|
1149
|
+
});
|
|
1150
|
+
};
|
|
1151
|
+
const onExit = (code, signal) => {
|
|
1152
|
+
settle(() => {
|
|
1153
|
+
reject(new FilesystemMcpError(`Filesystem MCP server exited before ready: ${formatFilesystemMcpExitReason(code, signal)}`));
|
|
1154
|
+
});
|
|
1155
|
+
};
|
|
1156
|
+
const timeout = setTimeout(() => {
|
|
1157
|
+
settle(() => {
|
|
1158
|
+
reject(new FilesystemMcpError(`Timed out waiting for filesystem MCP server to be ready after ${timeoutMs}ms`));
|
|
1159
|
+
});
|
|
1160
|
+
}, timeoutMs);
|
|
1161
|
+
stderr.on("data", onStderr);
|
|
1162
|
+
mcpProcess.once("error", onError);
|
|
1163
|
+
mcpProcess.once("exit", onExit);
|
|
1164
|
+
});
|
|
1165
|
+
}
|
|
1166
|
+
/**
|
|
1167
|
+
* Stop the filesystem MCP server process
|
|
1168
|
+
* @param mcpProcess Child process to terminate
|
|
1169
|
+
*/
|
|
1170
|
+
async function stopFilesystemMcpServer(mcpProcess) {
|
|
1171
|
+
if (!mcpProcess.pid || mcpProcess.exitCode !== null || mcpProcess.signalCode !== null) {
|
|
1172
|
+
logger.debug("MCP server already stopped");
|
|
1173
|
+
return;
|
|
1174
|
+
}
|
|
1175
|
+
logger.debug(`Stopping MCP server (pid: ${mcpProcess.pid})...`);
|
|
1176
|
+
if (process.platform === "win32") {
|
|
1177
|
+
const windowsDir = process.env.SystemRoot;
|
|
1178
|
+
if (!windowsDir || !isAbsolute(windowsDir)) throw new FilesystemMcpError("Cannot locate the Windows system directory for MCP cleanup");
|
|
1179
|
+
try {
|
|
1180
|
+
await execFileAsync(join(windowsDir, "System32", "taskkill.exe"), [
|
|
1181
|
+
"/PID",
|
|
1182
|
+
String(mcpProcess.pid),
|
|
1183
|
+
"/T",
|
|
1184
|
+
"/F"
|
|
1185
|
+
], {
|
|
1186
|
+
windowsHide: true,
|
|
1187
|
+
timeout: 5e3
|
|
1188
|
+
});
|
|
1189
|
+
} catch (error) {
|
|
1190
|
+
throw new FilesystemMcpError(`Failed to stop filesystem MCP process tree: ${error instanceof Error ? error.message : String(error)}`);
|
|
1191
|
+
}
|
|
1192
|
+
return;
|
|
1193
|
+
}
|
|
1194
|
+
return new Promise((resolve, reject) => {
|
|
1195
|
+
let settled = false;
|
|
1196
|
+
let exitTimeout;
|
|
1197
|
+
const cleanup = () => {
|
|
1198
|
+
clearTimeout(timeout);
|
|
1199
|
+
clearTimeout(exitTimeout);
|
|
1200
|
+
mcpProcess.off("exit", onExit);
|
|
1201
|
+
mcpProcess.off("error", onError);
|
|
1202
|
+
};
|
|
1203
|
+
const onExit = () => {
|
|
1204
|
+
if (!settled) {
|
|
1205
|
+
settled = true;
|
|
1206
|
+
cleanup();
|
|
1207
|
+
logger.debug("MCP server stopped");
|
|
1208
|
+
resolve();
|
|
1209
|
+
}
|
|
1210
|
+
};
|
|
1211
|
+
const onError = (error) => {
|
|
1212
|
+
if (!settled) {
|
|
1213
|
+
settled = true;
|
|
1214
|
+
cleanup();
|
|
1215
|
+
reject(new FilesystemMcpError(`Failed to stop filesystem MCP server: ${error.message}`));
|
|
1216
|
+
}
|
|
1217
|
+
};
|
|
1218
|
+
const sendSignal = (signal) => {
|
|
1219
|
+
try {
|
|
1220
|
+
mcpProcess.kill(signal);
|
|
1221
|
+
} catch (error) {
|
|
1222
|
+
onError(error instanceof Error ? error : new Error(String(error)));
|
|
1223
|
+
}
|
|
1224
|
+
};
|
|
1225
|
+
const timeout = setTimeout(() => {
|
|
1226
|
+
logger.debug("MCP server did not exit gracefully, force killing...");
|
|
1227
|
+
sendSignal("SIGKILL");
|
|
1228
|
+
if (!settled) exitTimeout = setTimeout(() => {
|
|
1229
|
+
onError(/* @__PURE__ */ new Error(`Timed out waiting for process ${mcpProcess.pid} to exit`));
|
|
1230
|
+
}, 5e3);
|
|
1231
|
+
}, 5e3);
|
|
1232
|
+
mcpProcess.once("exit", onExit);
|
|
1233
|
+
mcpProcess.once("error", onError);
|
|
1234
|
+
sendSignal("SIGTERM");
|
|
1235
|
+
});
|
|
1236
|
+
}
|
|
1237
|
+
//#endregion
|
|
1238
|
+
//#region src/codeScan/mcp/transport.ts
|
|
1239
|
+
/**
|
|
1240
|
+
* Bridge between MCP stdio and socket.io
|
|
1241
|
+
*/
|
|
1242
|
+
var SocketIoMcpBridge = class {
|
|
1243
|
+
mcpProcess;
|
|
1244
|
+
socket;
|
|
1245
|
+
sessionId;
|
|
1246
|
+
readBuffer = "";
|
|
1247
|
+
wireIdSeq = 0;
|
|
1248
|
+
wireIdMap = /* @__PURE__ */ new Map();
|
|
1249
|
+
constructor(mcpProcess, socket, sessionId) {
|
|
1250
|
+
this.mcpProcess = mcpProcess;
|
|
1251
|
+
this.socket = socket;
|
|
1252
|
+
this.sessionId = sessionId;
|
|
1253
|
+
}
|
|
1254
|
+
/**
|
|
1255
|
+
* Start bridging MCP stdio with the provided socket
|
|
1256
|
+
*/
|
|
1257
|
+
async connect() {
|
|
1258
|
+
if (!this.socket.connected) throw new SocketIoMcpBridgeError("Socket must be connected before starting bridge");
|
|
1259
|
+
logger.debug(`Using existing socket connection (id: ${this.socket.id})`);
|
|
1260
|
+
this.startBridging();
|
|
1261
|
+
}
|
|
1262
|
+
/**
|
|
1263
|
+
* Start bridging MCP stdio and socket.io
|
|
1264
|
+
*/
|
|
1265
|
+
startBridging() {
|
|
1266
|
+
if (!this.socket) throw new SocketIoMcpBridgeError("Socket not initialized");
|
|
1267
|
+
this.mcpProcess.stdout?.on("data", (chunk) => {
|
|
1268
|
+
this.readBuffer += chunk.toString("utf8");
|
|
1269
|
+
while (true) {
|
|
1270
|
+
const newlineIndex = this.readBuffer.indexOf("\n");
|
|
1271
|
+
if (newlineIndex === -1) break;
|
|
1272
|
+
const line = this.readBuffer.slice(0, newlineIndex);
|
|
1273
|
+
this.readBuffer = this.readBuffer.slice(newlineIndex + 1);
|
|
1274
|
+
if (!line.trim()) continue;
|
|
1275
|
+
try {
|
|
1276
|
+
const message = JSON.parse(line);
|
|
1277
|
+
const isResponse = "result" in message || "error" in message;
|
|
1278
|
+
const isRequest = "method" in message && !isResponse;
|
|
1279
|
+
let batchId;
|
|
1280
|
+
let restoredMessage = message;
|
|
1281
|
+
if (isResponse && message.id !== void 0) {
|
|
1282
|
+
const wireId = String(message.id);
|
|
1283
|
+
const mapping = this.wireIdMap.get(wireId);
|
|
1284
|
+
if (mapping) {
|
|
1285
|
+
batchId = mapping.batchId;
|
|
1286
|
+
restoredMessage = {
|
|
1287
|
+
...message,
|
|
1288
|
+
id: mapping.originalId
|
|
1289
|
+
};
|
|
1290
|
+
this.wireIdMap.delete(wireId);
|
|
1291
|
+
} else batchId = 0;
|
|
1292
|
+
}
|
|
1293
|
+
const eventName = isResponse ? "mcp:response" : isRequest ? "mcp:server-request" : "mcp:server-notification";
|
|
1294
|
+
this.socket?.emit(eventName, {
|
|
1295
|
+
session_id: this.sessionId,
|
|
1296
|
+
batch_id: batchId ?? 0,
|
|
1297
|
+
message: restoredMessage
|
|
1298
|
+
});
|
|
1299
|
+
} catch (_error) {
|
|
1300
|
+
logger.debug(`Failed to parse MCP output: ${line}`);
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
});
|
|
1304
|
+
this.socket.on("mcp:request", (message) => {
|
|
1305
|
+
try {
|
|
1306
|
+
const batchId = message._batch_id ?? 0;
|
|
1307
|
+
const { _batch_id, ...cleanMessage } = message;
|
|
1308
|
+
let messageToSend = cleanMessage;
|
|
1309
|
+
if (cleanMessage.id !== void 0 && cleanMessage.id !== null) {
|
|
1310
|
+
const seq = this.wireIdSeq++;
|
|
1311
|
+
const wireId = `b${batchId}:${String(cleanMessage.id)}:${seq}`;
|
|
1312
|
+
this.wireIdMap.set(wireId, {
|
|
1313
|
+
batchId,
|
|
1314
|
+
originalId: cleanMessage.id
|
|
1315
|
+
});
|
|
1316
|
+
messageToSend = {
|
|
1317
|
+
...cleanMessage,
|
|
1318
|
+
id: wireId
|
|
1319
|
+
};
|
|
1320
|
+
}
|
|
1321
|
+
const jsonLine = JSON.stringify(messageToSend) + "\n";
|
|
1322
|
+
this.mcpProcess.stdin?.write(jsonLine);
|
|
1323
|
+
} catch (error) {
|
|
1324
|
+
logger.error(`Failed to write to MCP stdin: ${error instanceof Error ? error.message : String(error)}`);
|
|
1325
|
+
}
|
|
1326
|
+
});
|
|
1327
|
+
logger.debug("MCP ↔ Socket.io bridge active");
|
|
1328
|
+
}
|
|
1329
|
+
/**
|
|
1330
|
+
* Stop bridging (socket lifecycle managed externally)
|
|
1331
|
+
*/
|
|
1332
|
+
async disconnect() {
|
|
1333
|
+
logger.debug("Stopping MCP bridge...");
|
|
1334
|
+
this.wireIdMap.clear();
|
|
1335
|
+
this.wireIdSeq = 0;
|
|
1336
|
+
logger.debug("MCP bridge stopped");
|
|
1337
|
+
}
|
|
1338
|
+
/**
|
|
1339
|
+
* Get socket ID (if connected)
|
|
1340
|
+
*/
|
|
1341
|
+
get socketId() {
|
|
1342
|
+
return this.socket?.id || null;
|
|
1343
|
+
}
|
|
1344
|
+
/**
|
|
1345
|
+
* Check if currently connected
|
|
1346
|
+
*/
|
|
1347
|
+
get connected() {
|
|
1348
|
+
return this.socket?.connected === true;
|
|
1349
|
+
}
|
|
1350
|
+
};
|
|
1351
|
+
//#endregion
|
|
1352
|
+
//#region src/codeScan/util/auth.ts
|
|
1353
|
+
/**
|
|
1354
|
+
* Code scan authentication utilities.
|
|
1355
|
+
*
|
|
1356
|
+
* Wraps the shared agent auth resolution and adds code-scan-specific
|
|
1357
|
+
* strategies: GitHub OIDC token and fork PR context.
|
|
1358
|
+
*/
|
|
1359
|
+
/**
|
|
1360
|
+
* Resolve authentication credentials using waterfall approach:
|
|
1361
|
+
* 1. API key from CLI argument or config file (passed as parameter)
|
|
1362
|
+
* 2. PROMPTFOO_API_KEY environment variable
|
|
1363
|
+
* 3. API key from promptfoo auth (cloudConfig)
|
|
1364
|
+
* 4. GitHub OIDC token (environment variable)
|
|
1365
|
+
* 5. Fork PR context (for fork PRs where OIDC is unavailable)
|
|
1366
|
+
*
|
|
1367
|
+
* @param apiKey Optional API key from CLI arg or config file
|
|
1368
|
+
* @param forkPR Optional fork PR context for authentication
|
|
1369
|
+
* @returns Resolved auth credentials
|
|
1370
|
+
*/
|
|
1371
|
+
function resolveAuthCredentials(apiKey, forkPR) {
|
|
1372
|
+
const baseAuth = resolveBaseAuthCredentials({ apiKey });
|
|
1373
|
+
if (baseAuth.apiKey) return baseAuth;
|
|
1374
|
+
const oidcToken = process.env.GITHUB_OIDC_TOKEN;
|
|
1375
|
+
if (oidcToken) {
|
|
1376
|
+
logger.debug("Using GitHub OIDC token");
|
|
1377
|
+
return { oidcToken };
|
|
1378
|
+
}
|
|
1379
|
+
if (forkPR) {
|
|
1380
|
+
logger.debug("Using fork PR context for authentication");
|
|
1381
|
+
return { forkPR };
|
|
1382
|
+
}
|
|
1383
|
+
return {};
|
|
1384
|
+
}
|
|
1385
|
+
//#endregion
|
|
1386
|
+
//#region src/codeScan/util/github.ts
|
|
1387
|
+
/**
|
|
1388
|
+
* Parse GitHub PR string
|
|
1389
|
+
*
|
|
1390
|
+
* @param prString - GitHub PR string in format: owner/repo#number (e.g., promptfoo/promptfoo#123)
|
|
1391
|
+
* @returns Parsed PR object or null if invalid format
|
|
1392
|
+
*/
|
|
1393
|
+
function parseGitHubPr(prString) {
|
|
1394
|
+
const match = prString.match(/^([^/]+)\/([^#]+)#(\d+)$/);
|
|
1395
|
+
if (!match) return null;
|
|
1396
|
+
const [, owner, repo, prNumber] = match;
|
|
1397
|
+
return {
|
|
1398
|
+
owner,
|
|
1399
|
+
repo,
|
|
1400
|
+
number: parseInt(prNumber, 10)
|
|
1401
|
+
};
|
|
1402
|
+
}
|
|
1403
|
+
//#endregion
|
|
1404
|
+
//#region src/codeScan/scanner/cleanup.ts
|
|
1405
|
+
/**
|
|
1406
|
+
* Cleanup and Signal Handling
|
|
1407
|
+
*
|
|
1408
|
+
* Manages graceful shutdown and resource cleanup for scan operations.
|
|
1409
|
+
*/
|
|
1410
|
+
/**
|
|
1411
|
+
* Register cleanup handlers for process signals
|
|
1412
|
+
*
|
|
1413
|
+
* Handles SIGINT (Ctrl+C), SIGTERM, and SIGQUIT signals by aborting the scan.
|
|
1414
|
+
* The scanner's catch/finally blocks perform resource cleanup.
|
|
1415
|
+
*
|
|
1416
|
+
* @param abortController - Controller for the in-flight scan
|
|
1417
|
+
*/
|
|
1418
|
+
function registerCleanupHandlers(abortController) {
|
|
1419
|
+
const cleanup = (signal) => {
|
|
1420
|
+
logger.debug(`Received ${signal}, aborting scan...`);
|
|
1421
|
+
abortController.abort();
|
|
1422
|
+
};
|
|
1423
|
+
process.once("SIGINT", () => cleanup("SIGINT"));
|
|
1424
|
+
process.once("SIGTERM", () => cleanup("SIGTERM"));
|
|
1425
|
+
process.once("SIGQUIT", () => cleanup("SIGQUIT"));
|
|
1426
|
+
}
|
|
1427
|
+
//#endregion
|
|
1428
|
+
//#region src/codeScan/util/sarif.ts
|
|
1429
|
+
const SARIF_SCHEMA_URL = "https://json.schemastore.org/sarif-2.1.0.json";
|
|
1430
|
+
const SARIF_VERSION = "2.1.0";
|
|
1431
|
+
const TOOL_INFORMATION_URI = "https://www.promptfoo.dev/docs/code-scanning/cli/";
|
|
1432
|
+
const GENERIC_RULE_ID = "promptfoo/code-scan-finding";
|
|
1433
|
+
const RULE_HELP_TEXT = "Review the finding and suggested fix, then validate the affected code path before merging.";
|
|
1434
|
+
const FINGERPRINT_FINDING_MAX_CHARS = 160;
|
|
1435
|
+
const SARIF_RULE = {
|
|
1436
|
+
id: GENERIC_RULE_ID,
|
|
1437
|
+
name: "Promptfoo Code Scan Finding",
|
|
1438
|
+
shortDescription: { text: "Potential LLM security issue identified by Promptfoo Code Scan." },
|
|
1439
|
+
fullDescription: { text: "Promptfoo Code Scan reviews code changes for LLM-related security risks such as prompt injection, unsafe tool use, and sensitive data exposure." },
|
|
1440
|
+
defaultConfiguration: { level: "warning" },
|
|
1441
|
+
help: {
|
|
1442
|
+
text: RULE_HELP_TEXT,
|
|
1443
|
+
markdown: RULE_HELP_TEXT
|
|
1444
|
+
},
|
|
1445
|
+
properties: {
|
|
1446
|
+
tags: ["security", "llm-security"],
|
|
1447
|
+
precision: "medium"
|
|
1448
|
+
}
|
|
1449
|
+
};
|
|
1450
|
+
function isSarifReportableFinding(comment) {
|
|
1451
|
+
return comment.file != null && comment.file !== "" && comment.severity !== CodeScanSeverity.NONE;
|
|
1452
|
+
}
|
|
1453
|
+
function toSarifLevel(severity) {
|
|
1454
|
+
if (severity === void 0) return "note";
|
|
1455
|
+
switch (severity) {
|
|
1456
|
+
case CodeScanSeverity.CRITICAL:
|
|
1457
|
+
case CodeScanSeverity.HIGH: return "error";
|
|
1458
|
+
case CodeScanSeverity.MEDIUM: return "warning";
|
|
1459
|
+
case CodeScanSeverity.LOW:
|
|
1460
|
+
case CodeScanSeverity.NONE: return "note";
|
|
1461
|
+
default: return severity;
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
function normalizeFindingForFingerprint(finding) {
|
|
1465
|
+
return finding.toLowerCase().replace(/\s+/g, " ").trim().slice(0, FINGERPRINT_FINDING_MAX_CHARS);
|
|
1466
|
+
}
|
|
1467
|
+
function toArtifactUri(filePath) {
|
|
1468
|
+
return filePath.replace(/\\/g, "/").split("/").map(encodeURIComponent).join("/");
|
|
1469
|
+
}
|
|
1470
|
+
function toSarifLocation(comment) {
|
|
1471
|
+
if (!comment.file) return;
|
|
1472
|
+
const physicalLocation = { artifactLocation: { uri: toArtifactUri(comment.file) } };
|
|
1473
|
+
if (comment.line) {
|
|
1474
|
+
const startLine = comment.startLine ?? comment.line;
|
|
1475
|
+
const endLine = comment.line;
|
|
1476
|
+
const region = { startLine };
|
|
1477
|
+
if (endLine > startLine) region.endLine = endLine;
|
|
1478
|
+
physicalLocation.region = region;
|
|
1479
|
+
}
|
|
1480
|
+
return [{ physicalLocation }];
|
|
1481
|
+
}
|
|
1482
|
+
function toSarifResult(comment) {
|
|
1483
|
+
return {
|
|
1484
|
+
ruleId: GENERIC_RULE_ID,
|
|
1485
|
+
level: toSarifLevel(comment.severity),
|
|
1486
|
+
message: { text: comment.finding },
|
|
1487
|
+
locations: toSarifLocation(comment),
|
|
1488
|
+
partialFingerprints: { promptfooFindingHash: sha256([
|
|
1489
|
+
comment.file ?? "",
|
|
1490
|
+
GENERIC_RULE_ID,
|
|
1491
|
+
comment.severity ?? "",
|
|
1492
|
+
normalizeFindingForFingerprint(comment.finding)
|
|
1493
|
+
].join(" ")) },
|
|
1494
|
+
properties: {
|
|
1495
|
+
severity: comment.severity,
|
|
1496
|
+
fix: comment.fix,
|
|
1497
|
+
aiAgentPrompt: comment.aiAgentPrompt
|
|
1498
|
+
}
|
|
1499
|
+
};
|
|
1500
|
+
}
|
|
1501
|
+
function scanResponseToSarif(response) {
|
|
1502
|
+
const results = response.comments.filter(isSarifReportableFinding).map(toSarifResult);
|
|
1503
|
+
const driver = {
|
|
1504
|
+
name: "Promptfoo Code Scan",
|
|
1505
|
+
informationUri: TOOL_INFORMATION_URI,
|
|
1506
|
+
rules: results.length > 0 ? [SARIF_RULE] : []
|
|
1507
|
+
};
|
|
1508
|
+
driver.semanticVersion = VERSION;
|
|
1509
|
+
const run = {
|
|
1510
|
+
tool: { driver },
|
|
1511
|
+
results
|
|
1512
|
+
};
|
|
1513
|
+
if (response.review) run.properties = { promptfoo: { review: response.review } };
|
|
1514
|
+
return {
|
|
1515
|
+
$schema: SARIF_SCHEMA_URL,
|
|
1516
|
+
version: SARIF_VERSION,
|
|
1517
|
+
runs: [run]
|
|
1518
|
+
};
|
|
1519
|
+
}
|
|
1520
|
+
//#endregion
|
|
1521
|
+
//#region src/codeScan/scanner/output.ts
|
|
1522
|
+
/**
|
|
1523
|
+
* Output Formatting and Display
|
|
1524
|
+
*
|
|
1525
|
+
* Handles formatting and displaying scan results in various formats (JSON, pretty-print).
|
|
1526
|
+
*/
|
|
1527
|
+
/**
|
|
1528
|
+
* Create spinner if appropriate for the current environment
|
|
1529
|
+
*
|
|
1530
|
+
* @param options - Options for spinner creation
|
|
1531
|
+
* @returns Spinner instance or undefined if spinner should not be shown
|
|
1532
|
+
*/
|
|
1533
|
+
function createSpinner(options) {
|
|
1534
|
+
if (!options.isWebUI && options.format === CodeScanOutputFormat.TEXT && options.logLevel !== "debug") return ora({
|
|
1535
|
+
text: "",
|
|
1536
|
+
color: "green"
|
|
1537
|
+
}).start();
|
|
1538
|
+
}
|
|
1539
|
+
/**
|
|
1540
|
+
* Display scan results in the appropriate format
|
|
1541
|
+
*
|
|
1542
|
+
* @param response - Scan response from server
|
|
1543
|
+
* @param duration - Duration of scan in milliseconds
|
|
1544
|
+
* @param options - Output options
|
|
1545
|
+
*/
|
|
1546
|
+
function displayScanResults(response, duration, options) {
|
|
1547
|
+
if (options.format === CodeScanOutputFormat.JSON) console.log(JSON.stringify(response, null, 2));
|
|
1548
|
+
else if (options.format === CodeScanOutputFormat.SARIF) console.log(JSON.stringify(scanResponseToSarif(response), null, 2));
|
|
1549
|
+
else {
|
|
1550
|
+
const { comments, review } = response;
|
|
1551
|
+
const severityCounts = countBySeverity(comments || []);
|
|
1552
|
+
printBorder();
|
|
1553
|
+
logger.info(`${chalk.green("✓")} Scan complete (${formatDuration(duration / 1e3)})`);
|
|
1554
|
+
if (severityCounts.total > 0) logger.info(chalk.yellow(`⚠ Found ${severityCounts.total} issue${severityCounts.total === 1 ? "" : "s"}`));
|
|
1555
|
+
printBorder();
|
|
1556
|
+
let reviewText = review;
|
|
1557
|
+
if (!reviewText && comments && comments.length > 0) {
|
|
1558
|
+
const noneComment = comments.find((c) => c.severity === CodeScanSeverity.NONE);
|
|
1559
|
+
if (noneComment) reviewText = noneComment.finding;
|
|
1560
|
+
}
|
|
1561
|
+
if (reviewText) {
|
|
1562
|
+
logger.info("");
|
|
1563
|
+
logger.info(reviewText);
|
|
1564
|
+
logger.info("");
|
|
1565
|
+
printBorder();
|
|
1566
|
+
}
|
|
1567
|
+
if (severityCounts.total > 0) {
|
|
1568
|
+
const sortedComments = [...filterBySeverity(comments || [])].sort((a, b) => {
|
|
1569
|
+
const rankA = a.severity ? getSeverityRank(a.severity) : 0;
|
|
1570
|
+
return (b.severity ? getSeverityRank(b.severity) : 0) - rankA;
|
|
1571
|
+
});
|
|
1572
|
+
logger.info("");
|
|
1573
|
+
for (let i = 0; i < sortedComments.length; i++) {
|
|
1574
|
+
const comment = sortedComments[i];
|
|
1575
|
+
const severity = formatSeverity(comment.severity);
|
|
1576
|
+
const location = comment.line ? `${comment.file}:${comment.line}` : comment.file || "";
|
|
1577
|
+
logger.info(`${severity} ${chalk.gray(location)}`);
|
|
1578
|
+
logger.info("");
|
|
1579
|
+
logger.info(comment.finding);
|
|
1580
|
+
if (comment.fix) {
|
|
1581
|
+
logger.info("");
|
|
1582
|
+
logger.info(chalk.bold("Suggested Fix:"));
|
|
1583
|
+
logger.info(comment.fix);
|
|
1584
|
+
}
|
|
1585
|
+
if (comment.aiAgentPrompt) {
|
|
1586
|
+
logger.info("");
|
|
1587
|
+
logger.info(chalk.bold("AI Agent Prompt:"));
|
|
1588
|
+
logger.info(comment.aiAgentPrompt);
|
|
1589
|
+
}
|
|
1590
|
+
if (i < sortedComments.length - 1) {
|
|
1591
|
+
logger.info("");
|
|
1592
|
+
logger.info(chalk.gray("─".repeat(TERMINAL_MAX_WIDTH)));
|
|
1593
|
+
logger.info("");
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
printBorder();
|
|
1597
|
+
if (options.githubPr) {
|
|
1598
|
+
logger.info(`» Comments posted to PR: ${chalk.cyan(options.githubPr)}`);
|
|
1599
|
+
printBorder();
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
//#endregion
|
|
1605
|
+
//#region src/codeScan/scanner/request.ts
|
|
1606
|
+
/**
|
|
1607
|
+
* Scan Request Building and Execution
|
|
1608
|
+
*
|
|
1609
|
+
* Handles building scan requests and executing them via the agent client.
|
|
1610
|
+
*/
|
|
1611
|
+
const CAPACITY_ERROR_MESSAGE = "Server at capacity";
|
|
1612
|
+
const MCP_REQUEST_TIMEOUT_ERROR_MESSAGE = "MCP error -32001: Request timed out";
|
|
1613
|
+
const MAX_CAPACITY_ATTEMPTS = 7;
|
|
1614
|
+
const MAX_MCP_TIMEOUT_ATTEMPTS = 2;
|
|
1615
|
+
const BASE_DELAY_MS = 1e3;
|
|
1616
|
+
/**
|
|
1617
|
+
* Build scan request from inputs
|
|
1618
|
+
*
|
|
1619
|
+
* @param files - Files to scan
|
|
1620
|
+
* @param metadata - Git metadata
|
|
1621
|
+
* @param config - Scan configuration
|
|
1622
|
+
* @param sessionId - Session ID for scan tracking and cancellation
|
|
1623
|
+
* @param pullRequest - Optional PR context
|
|
1624
|
+
* @param guidance - Optional custom guidance
|
|
1625
|
+
* @returns Scan request object
|
|
1626
|
+
*/
|
|
1627
|
+
function buildScanRequest(files, metadata, config, sessionId, pullRequest, guidance) {
|
|
1628
|
+
return {
|
|
1629
|
+
files,
|
|
1630
|
+
metadata,
|
|
1631
|
+
config: {
|
|
1632
|
+
minimumSeverity: config.minimumSeverity,
|
|
1633
|
+
diffsOnly: config.diffsOnly,
|
|
1634
|
+
guidance
|
|
1635
|
+
},
|
|
1636
|
+
sessionId,
|
|
1637
|
+
pullRequest
|
|
1638
|
+
};
|
|
1639
|
+
}
|
|
1640
|
+
/**
|
|
1641
|
+
* Execute scan request via agent client
|
|
1642
|
+
*
|
|
1643
|
+
* Uses the agent lifecycle protocol:
|
|
1644
|
+
* - client.start(request) → emits agent:start
|
|
1645
|
+
* - client.onComplete(cb) → listens agent:complete
|
|
1646
|
+
* - client.onError(cb) → listens agent:error
|
|
1647
|
+
* - client.cancel() → emits agent:cancel
|
|
1648
|
+
*
|
|
1649
|
+
* @param client - Connected agent client
|
|
1650
|
+
* @param request - Scan request to send
|
|
1651
|
+
* @param options - Execution options
|
|
1652
|
+
* @returns Promise resolving to scan response
|
|
1653
|
+
* @throws Error if scan fails, connection lost, or user cancels
|
|
1654
|
+
*/
|
|
1655
|
+
async function executeScanRequest(client, request, options) {
|
|
1656
|
+
const { spinner, abortController } = options;
|
|
1657
|
+
if (spinner) spinner.text = "Scanning...";
|
|
1658
|
+
let heartbeatInterval;
|
|
1659
|
+
let firstPulseTimeout;
|
|
1660
|
+
if (spinner) {
|
|
1661
|
+
const pulse = () => {
|
|
1662
|
+
spinner.text = "Still scanning...";
|
|
1663
|
+
setTimeout(() => {
|
|
1664
|
+
if (spinner?.isSpinning) spinner.text = "Scanning...";
|
|
1665
|
+
}, 4e3);
|
|
1666
|
+
};
|
|
1667
|
+
firstPulseTimeout = setTimeout(() => {
|
|
1668
|
+
pulse();
|
|
1669
|
+
heartbeatInterval = setInterval(pulse, 12e3);
|
|
1670
|
+
}, 8e3);
|
|
1671
|
+
}
|
|
1672
|
+
return await new Promise((resolve, reject) => {
|
|
1673
|
+
const cleanupTimers = () => {
|
|
1674
|
+
clearTimeout(firstPulseTimeout);
|
|
1675
|
+
clearInterval(heartbeatInterval);
|
|
1676
|
+
};
|
|
1677
|
+
client.onComplete((response) => {
|
|
1678
|
+
cleanupTimers();
|
|
1679
|
+
abortController.signal.removeEventListener("abort", onAbort);
|
|
1680
|
+
client.socket.io.off("reconnect_failed", onReconnectFailed);
|
|
1681
|
+
resolve(response);
|
|
1682
|
+
});
|
|
1683
|
+
client.onError((error) => {
|
|
1684
|
+
cleanupTimers();
|
|
1685
|
+
abortController.signal.removeEventListener("abort", onAbort);
|
|
1686
|
+
client.socket.io.off("reconnect_failed", onReconnectFailed);
|
|
1687
|
+
reject(new Error(error.message || error.type));
|
|
1688
|
+
});
|
|
1689
|
+
client.onCancelled(() => {
|
|
1690
|
+
cleanupTimers();
|
|
1691
|
+
abortController.signal.removeEventListener("abort", onAbort);
|
|
1692
|
+
client.socket.io.off("reconnect_failed", onReconnectFailed);
|
|
1693
|
+
reject(/* @__PURE__ */ new Error("Scan cancelled by server"));
|
|
1694
|
+
});
|
|
1695
|
+
const onReconnectFailed = () => {
|
|
1696
|
+
cleanupTimers();
|
|
1697
|
+
abortController.signal.removeEventListener("abort", onAbort);
|
|
1698
|
+
reject(/* @__PURE__ */ new Error("Lost connection to server during scan"));
|
|
1699
|
+
};
|
|
1700
|
+
const onAbort = () => {
|
|
1701
|
+
client.cancel();
|
|
1702
|
+
cleanupTimers();
|
|
1703
|
+
client.socket.io.off("reconnect_failed", onReconnectFailed);
|
|
1704
|
+
abortController.signal.removeEventListener("abort", onAbort);
|
|
1705
|
+
reject(/* @__PURE__ */ new Error("cancelled by user"));
|
|
1706
|
+
};
|
|
1707
|
+
client.socket.io.once("reconnect_failed", onReconnectFailed);
|
|
1708
|
+
abortController.signal.addEventListener("abort", onAbort);
|
|
1709
|
+
client.start(request);
|
|
1710
|
+
});
|
|
1711
|
+
}
|
|
1712
|
+
/**
|
|
1713
|
+
* Execute scan request with retry for transient scanner errors
|
|
1714
|
+
*
|
|
1715
|
+
* When the server is at capacity, it returns "Server at capacity. Please retry."
|
|
1716
|
+
* This wrapper retries with exponential backoff + jitter to spread load. Remote
|
|
1717
|
+
* MCP request timeouts get one retry because each failed attempt already waited
|
|
1718
|
+
* on the server-side request timeout.
|
|
1719
|
+
*
|
|
1720
|
+
* Capacity backoff schedule: ~1s, ~2s, ~4s, ~8s, ~16s, ~32s (up to 7 attempts, ~63s max)
|
|
1721
|
+
* MCP timeout backoff: ~1s (up to 2 attempts)
|
|
1722
|
+
*
|
|
1723
|
+
* @param client - Connected agent client
|
|
1724
|
+
* @param request - Scan request to send
|
|
1725
|
+
* @param options - Execution options
|
|
1726
|
+
* @returns Promise resolving to scan response
|
|
1727
|
+
* @throws Error if scan fails after all retries
|
|
1728
|
+
*/
|
|
1729
|
+
async function executeScanRequestWithRetry(client, request, options) {
|
|
1730
|
+
const { spinner } = options;
|
|
1731
|
+
const attemptsByPolicy = /* @__PURE__ */ new Map();
|
|
1732
|
+
while (true) try {
|
|
1733
|
+
return await executeScanRequest(client, request, options);
|
|
1734
|
+
} catch (error) {
|
|
1735
|
+
const getRetryPolicy = () => {
|
|
1736
|
+
if (error instanceof Error && error.message.includes(CAPACITY_ERROR_MESSAGE)) return {
|
|
1737
|
+
key: "capacity",
|
|
1738
|
+
maxAttempts: MAX_CAPACITY_ATTEMPTS,
|
|
1739
|
+
status: "Server busy",
|
|
1740
|
+
waitStatus: "Server busy"
|
|
1741
|
+
};
|
|
1742
|
+
if (error instanceof Error && error.message.includes(MCP_REQUEST_TIMEOUT_ERROR_MESSAGE)) return {
|
|
1743
|
+
key: "mcp_timeout",
|
|
1744
|
+
maxAttempts: MAX_MCP_TIMEOUT_ATTEMPTS,
|
|
1745
|
+
status: "Code scan timed out waiting for repository access",
|
|
1746
|
+
waitStatus: "Repository access timed out"
|
|
1747
|
+
};
|
|
1748
|
+
};
|
|
1749
|
+
const retryPolicy = getRetryPolicy();
|
|
1750
|
+
if (!retryPolicy) throw error;
|
|
1751
|
+
const policyAttempts = (attemptsByPolicy.get(retryPolicy.key) ?? 0) + 1;
|
|
1752
|
+
attemptsByPolicy.set(retryPolicy.key, policyAttempts);
|
|
1753
|
+
if (policyAttempts >= retryPolicy.maxAttempts) throw error;
|
|
1754
|
+
const delay = BASE_DELAY_MS * Math.pow(2, policyAttempts - 1) * (.7 + .6 * Math.random());
|
|
1755
|
+
logger.debug(`${retryPolicy.status}, retrying in ${Math.round(delay / 1e3)}s (attempt ${policyAttempts}/${retryPolicy.maxAttempts})`);
|
|
1756
|
+
if (spinner) {
|
|
1757
|
+
const originalText = spinner.text;
|
|
1758
|
+
spinner.text = `${retryPolicy.waitStatus}, retrying in ${Math.round(delay / 1e3)}s...`;
|
|
1759
|
+
await sleepWithAbort(delay, options.abortController.signal);
|
|
1760
|
+
spinner.text = originalText;
|
|
1761
|
+
} else await sleepWithAbort(delay, options.abortController.signal);
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1764
|
+
//#endregion
|
|
1765
|
+
//#region src/codeScan/scanner/index.ts
|
|
1766
|
+
/**
|
|
1767
|
+
* Core Scan Execution Logic
|
|
1768
|
+
*
|
|
1769
|
+
* Main entry point for scanner module - orchestrates the complete scan process.
|
|
1770
|
+
*/
|
|
1771
|
+
async function describeEmptyScan(git, files, baseBranch, compareRef) {
|
|
1772
|
+
const details = [`No files to scan in ${baseBranch}...${compareRef}.`];
|
|
1773
|
+
const skipped = files.filter((file) => file.skipReason);
|
|
1774
|
+
const reasons = [...new Set(skipped.map((file) => file.skipReason))].join(", ");
|
|
1775
|
+
details.push(files.length === 0 ? "There are no net file changes in this range." : `${files.length} changed file(s) contain no scannable patches.`);
|
|
1776
|
+
if (skipped.length) details.push(`${skipped.length} file(s) skipped: ${reasons}.`);
|
|
1777
|
+
try {
|
|
1778
|
+
const dirtyCount = (await git.status()).files.length;
|
|
1779
|
+
if (dirtyCount) details.push(`${dirtyCount} uncommitted file(s) are not part of the scan.`);
|
|
1780
|
+
} catch {}
|
|
1781
|
+
details.push("Commit relevant changes, or use --base/--compare to select another range.");
|
|
1782
|
+
return details.join(" ");
|
|
1783
|
+
}
|
|
1784
|
+
function resolveOutputFormat(options) {
|
|
1785
|
+
const parsed = CodeScanOutputFormatSchema.safeParse(options.format ?? CodeScanOutputFormat.TEXT);
|
|
1786
|
+
if (!parsed.success) throw new Error(`Invalid output format "${options.format}". Expected one of: text, json, sarif`);
|
|
1787
|
+
if (options.json && parsed.data === CodeScanOutputFormat.SARIF) throw new Error("Cannot combine --json with --format sarif");
|
|
1788
|
+
return options.json ? CodeScanOutputFormat.JSON : parsed.data;
|
|
1789
|
+
}
|
|
1790
|
+
const createFlushCallback = (exitCode) => async () => {
|
|
1791
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
1792
|
+
process.exitCode = exitCode;
|
|
1793
|
+
};
|
|
1794
|
+
/**
|
|
1795
|
+
* Execute a complete security scan
|
|
1796
|
+
*
|
|
1797
|
+
* This is the main entry point for the scanner - it orchestrates:
|
|
1798
|
+
* - Configuration loading
|
|
1799
|
+
* - Agent client connection (shared Socket.IO layer)
|
|
1800
|
+
* - MCP bridge setup (if not diffs-only)
|
|
1801
|
+
* - Git diff processing
|
|
1802
|
+
* - Scan request execution
|
|
1803
|
+
* - Result display
|
|
1804
|
+
* - Cleanup
|
|
1805
|
+
*
|
|
1806
|
+
* @param repoPath - Path to repository to scan
|
|
1807
|
+
* @param options - Scan options from CLI
|
|
1808
|
+
*/
|
|
1809
|
+
async function executeScan(repoPath, options) {
|
|
1810
|
+
let client = null;
|
|
1811
|
+
let mcpProcess = null;
|
|
1812
|
+
let mcpBridge = null;
|
|
1813
|
+
let sessionId = void 0;
|
|
1814
|
+
const abortController = new AbortController();
|
|
1815
|
+
const originalLogLevel = getLogLevel();
|
|
1816
|
+
const structuredOutputRequested = options.json === true || options.format === CodeScanOutputFormat.JSON || options.format === CodeScanOutputFormat.SARIF;
|
|
1817
|
+
const absoluteRepoPath = path.resolve(repoPath);
|
|
1818
|
+
let outputFormat = null;
|
|
1819
|
+
let spinner;
|
|
1820
|
+
const startTime = Date.now();
|
|
1821
|
+
const displayResults = (response, format) => displayScanResults(response, Date.now() - startTime, {
|
|
1822
|
+
format,
|
|
1823
|
+
githubPr: options.githubPr
|
|
1824
|
+
});
|
|
1825
|
+
try {
|
|
1826
|
+
outputFormat = resolveOutputFormat(options);
|
|
1827
|
+
if (outputFormat !== CodeScanOutputFormat.TEXT) setLogLevel("error");
|
|
1828
|
+
const config = mergeConfigWithOptions(loadConfigOrDefault(options.config), options);
|
|
1829
|
+
const guidance = resolveGuidance(options, config);
|
|
1830
|
+
if (outputFormat === CodeScanOutputFormat.TEXT) {
|
|
1831
|
+
logger.info("Beginning scan for LLM-related vulnerabilities in your code.");
|
|
1832
|
+
logger.info(` Minimum severity: ${config.minimumSeverity}`);
|
|
1833
|
+
if (config.diffsOnly) logger.info(` Mode: diffs only`);
|
|
1834
|
+
else logger.info(` Mode: diffs + tracing into repo`);
|
|
1835
|
+
logger.info("");
|
|
1836
|
+
}
|
|
1837
|
+
logger.debug(`Repository: ${absoluteRepoPath}`);
|
|
1838
|
+
registerCleanupHandlers(abortController);
|
|
1839
|
+
spinner = createSpinner({
|
|
1840
|
+
format: outputFormat,
|
|
1841
|
+
isWebUI: Boolean(state.webUI),
|
|
1842
|
+
logLevel: getLogLevel()
|
|
1843
|
+
});
|
|
1844
|
+
let parsedPR;
|
|
1845
|
+
if (options.githubPr) {
|
|
1846
|
+
const parsed = parseGitHubPr(options.githubPr);
|
|
1847
|
+
if (!parsed) throw new Error(`Invalid --github-pr format: "${options.githubPr}". Expected format: owner/repo#number (e.g., promptfoo/promptfoo#123)`);
|
|
1848
|
+
parsedPR = parsed;
|
|
1849
|
+
}
|
|
1850
|
+
if (!spinner) logger.debug("Connecting to server...");
|
|
1851
|
+
client = await createAgentClient({
|
|
1852
|
+
agent: "code-scan",
|
|
1853
|
+
host: config.apiHost || "https://api.promptfoo.app",
|
|
1854
|
+
auth: resolveAuthCredentials(options.apiKey, parsedPR)
|
|
1855
|
+
});
|
|
1856
|
+
sessionId = client.sessionId;
|
|
1857
|
+
if (!config.diffsOnly) {
|
|
1858
|
+
logger.debug("Setting up repo MCP access...");
|
|
1859
|
+
logger.debug(`Using session ID: ${sessionId}`);
|
|
1860
|
+
const startedMcpProcess = startFilesystemMcpServer(absoluteRepoPath);
|
|
1861
|
+
try {
|
|
1862
|
+
await waitForFilesystemMcpServerReady(startedMcpProcess);
|
|
1863
|
+
logger.debug("Filesystem MCP server ready");
|
|
1864
|
+
const connectedMcpBridge = new SocketIoMcpBridge(startedMcpProcess, client.socket, sessionId);
|
|
1865
|
+
await connectedMcpBridge.connect();
|
|
1866
|
+
client.socket.emit("runner:hello", {
|
|
1867
|
+
session_id: sessionId,
|
|
1868
|
+
repo_root: absoluteRepoPath
|
|
1869
|
+
});
|
|
1870
|
+
mcpProcess = startedMcpProcess;
|
|
1871
|
+
mcpBridge = connectedMcpBridge;
|
|
1872
|
+
} catch (error) {
|
|
1873
|
+
await stopFilesystemMcpServer(startedMcpProcess);
|
|
1874
|
+
throw error;
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
logger.debug("Processing git diff...");
|
|
1878
|
+
const { simpleGit } = await import("simple-git");
|
|
1879
|
+
const git = simpleGit(absoluteRepoPath);
|
|
1880
|
+
if (!options.compare) await validateOnBranch(git);
|
|
1881
|
+
let baseBranch;
|
|
1882
|
+
if (options.base) baseBranch = options.base;
|
|
1883
|
+
else {
|
|
1884
|
+
const branches = await git.branch();
|
|
1885
|
+
baseBranch = branches.all.includes("main") || branches.all.includes("origin/main") ? "main" : branches.all.includes("master") || branches.all.includes("origin/master") ? "master" : "main";
|
|
1886
|
+
}
|
|
1887
|
+
const compareRef = options.compare || "HEAD";
|
|
1888
|
+
logger.debug(`Comparing: ${baseBranch}...${compareRef}`);
|
|
1889
|
+
const files = await processDiff(absoluteRepoPath, baseBranch, compareRef);
|
|
1890
|
+
const includedFiles = files.filter((f) => !f.skipReason && f.patch);
|
|
1891
|
+
logger.debug(`Files changed: ${files.length} (${includedFiles.length} included, ${files.filter((f) => f.skipReason).length} skipped)`);
|
|
1892
|
+
if (includedFiles.length === 0) {
|
|
1893
|
+
if (outputFormat === CodeScanOutputFormat.TEXT) {
|
|
1894
|
+
const msg = await describeEmptyScan(git, files, baseBranch, compareRef);
|
|
1895
|
+
if (spinner) spinner.succeed(msg);
|
|
1896
|
+
else logger.info(msg);
|
|
1897
|
+
} else displayResults({
|
|
1898
|
+
success: true,
|
|
1899
|
+
comments: [],
|
|
1900
|
+
review: "No files to scan"
|
|
1901
|
+
}, outputFormat);
|
|
1902
|
+
state.postActionCallback = createFlushCallback(0);
|
|
1903
|
+
return;
|
|
1904
|
+
}
|
|
1905
|
+
const metadata = await extractMetadata(absoluteRepoPath, baseBranch, compareRef);
|
|
1906
|
+
logger.debug(`Compare ref: ${metadata.branch}`);
|
|
1907
|
+
logger.debug(`Commits: ${metadata.commitMessages.length}`);
|
|
1908
|
+
let pullRequest = void 0;
|
|
1909
|
+
if (parsedPR) {
|
|
1910
|
+
const currentCommit = await git.revparse(["HEAD"]);
|
|
1911
|
+
pullRequest = {
|
|
1912
|
+
owner: parsedPR.owner,
|
|
1913
|
+
repo: parsedPR.repo,
|
|
1914
|
+
number: parsedPR.number,
|
|
1915
|
+
sha: currentCommit.trim()
|
|
1916
|
+
};
|
|
1917
|
+
logger.debug(`GitHub PR context: ${parsedPR.owner}/${parsedPR.repo}#${parsedPR.number} (${pullRequest.sha.substring(0, 7)})`);
|
|
1918
|
+
}
|
|
1919
|
+
if (!spinner) logger.debug("Scanning code...");
|
|
1920
|
+
const scanRequest = buildScanRequest(files, metadata, config, sessionId, pullRequest, guidance);
|
|
1921
|
+
const scanResponse = await executeScanRequestWithRetry(client, scanRequest, {
|
|
1922
|
+
spinner,
|
|
1923
|
+
abortController
|
|
1924
|
+
});
|
|
1925
|
+
if (spinner) spinner.stop();
|
|
1926
|
+
displayResults(scanResponse, outputFormat);
|
|
1927
|
+
} catch (error) {
|
|
1928
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1929
|
+
if (errorMessage.includes("Fork PR scanning not authorized")) {
|
|
1930
|
+
const msg = "Fork PR scanning requires maintainer approval. See PR comment for options.";
|
|
1931
|
+
if (outputFormat === CodeScanOutputFormat.JSON) displayResults({
|
|
1932
|
+
success: true,
|
|
1933
|
+
comments: [],
|
|
1934
|
+
skipReason: msg
|
|
1935
|
+
}, outputFormat);
|
|
1936
|
+
else if (outputFormat === CodeScanOutputFormat.SARIF) {
|
|
1937
|
+
console.error(`Scan skipped: ${msg} SARIF output was not generated because the scan did not complete.`);
|
|
1938
|
+
state.postActionCallback = createFlushCallback(1);
|
|
1939
|
+
return;
|
|
1940
|
+
} else if (spinner) spinner.succeed(msg);
|
|
1941
|
+
else logger.info(msg);
|
|
1942
|
+
state.postActionCallback = createFlushCallback(0);
|
|
1943
|
+
return;
|
|
1944
|
+
}
|
|
1945
|
+
const msg = `Scan failed: ${errorMessage}`;
|
|
1946
|
+
if (spinner) spinner.fail(msg);
|
|
1947
|
+
else if (structuredOutputRequested) console.error(msg);
|
|
1948
|
+
else logger.error(msg);
|
|
1949
|
+
state.postActionCallback = async () => {
|
|
1950
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
1951
|
+
if (error instanceof Error && error.message === "cancelled by user") process.exitCode = 130;
|
|
1952
|
+
else process.exitCode = 1;
|
|
1953
|
+
};
|
|
1954
|
+
} finally {
|
|
1955
|
+
if (mcpBridge) await mcpBridge.disconnect().catch(() => {
|
|
1956
|
+
logger.debug("MCP bridge cleanup completed");
|
|
1957
|
+
});
|
|
1958
|
+
if (mcpProcess) await stopFilesystemMcpServer(mcpProcess).catch(() => {
|
|
1959
|
+
logger.debug("MCP server cleanup completed");
|
|
1960
|
+
});
|
|
1961
|
+
if (client) {
|
|
1962
|
+
client.disconnect();
|
|
1963
|
+
logger.debug("Agent client disconnected");
|
|
1964
|
+
}
|
|
1965
|
+
if (outputFormat !== null && outputFormat !== CodeScanOutputFormat.TEXT && getLogLevel() !== originalLogLevel) setLogLevel(originalLogLevel);
|
|
1966
|
+
}
|
|
1967
|
+
}
|
|
1968
|
+
//#endregion
|
|
1969
|
+
export { executeScan };
|
|
1970
|
+
|
|
1971
|
+
//# sourceMappingURL=scanner-PG_8lh8r.js.map
|