shadow-claw 1.32.0 → 1.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.well-known/mcp/server-card.json +1 -1
- package/.well-known/mcp.json +1 -1
- package/dist/electron/main.cjs +1 -1
- package/dist/lib/components/index.js +26 -26
- package/dist/lib/components/settings/shadow-claw-accounts.js +1 -1
- package/dist/lib/components/settings/shadow-claw-channel-config.js +1 -1
- package/dist/lib/components/settings/shadow-claw-control-plane.js +1 -1
- package/dist/lib/components/settings/shadow-claw-git.js +1 -1
- package/dist/lib/components/settings/shadow-claw-integrations.js +1 -1
- package/dist/lib/components/settings/shadow-claw-llm.js +1 -1
- package/dist/lib/components/settings/shadow-claw-mcp-remote.js +1 -1
- package/dist/lib/components/settings/shadow-claw-networking.js +1 -1
- package/dist/lib/components/settings/shadow-claw-notifications.js +1 -1
- package/dist/lib/components/settings/shadow-claw-peerjs.js +1 -1
- package/dist/lib/components/settings/shadow-claw-storage.js +1 -1
- package/dist/lib/components/settings/shadow-claw-task-server.js +1 -1
- package/dist/lib/components/settings/shadow-claw-webvm.js +1 -1
- package/dist/lib/components/shadow-claw-a2ui.js +1 -1
- package/dist/lib/components/shadow-claw-accounts.js +1 -1
- package/dist/lib/components/shadow-claw-channel-config.js +1 -1
- package/dist/lib/components/shadow-claw-channels.js +1 -1
- package/dist/lib/components/shadow-claw-chat.js +1 -1
- package/dist/lib/components/shadow-claw-control-plane.js +1 -1
- package/dist/lib/components/shadow-claw-conversations.js +1 -1
- package/dist/lib/components/shadow-claw-file-viewer.js +1 -1
- package/dist/lib/components/shadow-claw-files.js +1 -1
- package/dist/lib/components/shadow-claw-git.js +1 -1
- package/dist/lib/components/shadow-claw-integrations.js +1 -1
- package/dist/lib/components/shadow-claw-llm.js +1 -1
- package/dist/lib/components/shadow-claw-mcp-remote.js +1 -1
- package/dist/lib/components/shadow-claw-networking.js +1 -1
- package/dist/lib/components/shadow-claw-notifications.js +1 -1
- package/dist/lib/components/shadow-claw-page-header.js +1 -1
- package/dist/lib/components/shadow-claw-pages.js +1 -1
- package/dist/lib/components/shadow-claw-peerjs.js +1 -1
- package/dist/lib/components/shadow-claw-settings.js +1 -1
- package/dist/lib/components/shadow-claw-storage.js +1 -1
- package/dist/lib/components/shadow-claw-task-server.js +1 -1
- package/dist/lib/components/shadow-claw-tasks.js +1 -1
- package/dist/lib/components/shadow-claw-terminal.js +1 -1
- package/dist/lib/components/shadow-claw-tools.js +1 -1
- package/dist/lib/components/shadow-claw-webvm.js +1 -1
- package/dist/lib/components/shadow-claw.js +1 -1
- package/dist/lib/{config-DjZ9QWc4.js → config-BcFbkaQv.js} +21 -4
- package/dist/lib/config-BcFbkaQv.js.map +1 -0
- package/dist/lib/{configurePeerJs-DD3G3Pms.js → configurePeerJs-CBMPHbwa.js} +3 -3
- package/dist/lib/{configurePeerJs-DD3G3Pms.js.map → configurePeerJs-CBMPHbwa.js.map} +1 -1
- package/dist/lib/{connections-BURqMIUv.js → connections-PZrUDf49.js} +2 -2
- package/dist/lib/{connections-BURqMIUv.js.map → connections-PZrUDf49.js.map} +1 -1
- package/dist/lib/{downloadGroupFile-B8J4up2a.js → downloadGroupFile-DSRUa9Au.js} +2 -2
- package/dist/lib/{downloadGroupFile-B8J4up2a.js.map → downloadGroupFile-DSRUa9Au.js.map} +1 -1
- package/dist/lib/{file-viewer-DoHM75Jz.js → file-viewer-11VVOpJv.js} +5 -5
- package/dist/lib/{file-viewer-DoHM75Jz.js.map → file-viewer-11VVOpJv.js.map} +1 -1
- package/dist/lib/{getGroupDir-QrN2aTo8.js → getGroupDir-BxKMYPPm.js} +3 -3
- package/dist/lib/{getGroupDir-QrN2aTo8.js.map → getGroupDir-BxKMYPPm.js.map} +1 -1
- package/dist/lib/{git-Brr7JIbs.js → git-BppibnJW.js} +3 -3
- package/dist/lib/{git-Brr7JIbs.js.map → git-BppibnJW.js.map} +1 -1
- package/dist/lib/{iframe-sanitizer-Bujgq14T.js → iframe-sanitizer-Dp8EYffI.js} +2 -2
- package/dist/lib/{iframe-sanitizer-Bujgq14T.js.map → iframe-sanitizer-Dp8EYffI.js.map} +1 -1
- package/dist/lib/index.js +27 -27
- package/dist/lib/{initChatSplitResize-BjiWoqC_.js → initChatSplitResize-CE_kWaLf.js} +2 -2
- package/dist/lib/{initChatSplitResize-BjiWoqC_.js.map → initChatSplitResize-CE_kWaLf.js.map} +1 -1
- package/dist/lib/{initControlPlane-3TUdhRBM.js → initControlPlane-BRIIOzoH.js} +18 -17
- package/dist/lib/{initControlPlane-3TUdhRBM.js.map → initControlPlane-BRIIOzoH.js.map} +1 -1
- package/dist/lib/{markdown-BFUTPQoO.js → markdown-Cq4sVwUT.js} +4 -4
- package/dist/lib/{markdown-BFUTPQoO.js.map → markdown-Cq4sVwUT.js.map} +1 -1
- package/dist/lib/{mcp-reconnect-BxJ77WSb.js → mcp-reconnect-CoHDMjmY.js} +2 -2
- package/dist/lib/{mcp-reconnect-BxJ77WSb.js.map → mcp-reconnect-CoHDMjmY.js.map} +1 -1
- package/dist/lib/{memoryStorage-D3FBwO9T.js → memoryStorage-DcFWrn_y.js} +2 -2
- package/dist/lib/{memoryStorage-D3FBwO9T.js.map → memoryStorage-DcFWrn_y.js.map} +1 -1
- package/dist/lib/{orchestrator-DW5l2bbW.js → orchestrator-C3TgDZvq.js} +81 -18
- package/dist/lib/{orchestrator-DW5l2bbW.js.map → orchestrator-C3TgDZvq.js.map} +1 -1
- package/dist/lib/{orchestrator-DLnCmeTZ.js → orchestrator-Ce9_lmL2.js} +12 -12
- package/dist/lib/{orchestrator-DLnCmeTZ.js.map → orchestrator-Ce9_lmL2.js.map} +1 -1
- package/dist/lib/{peerjs-BXlMuIQn.js → peerjs-PTYQ7yEG.js} +6 -6
- package/dist/lib/{peerjs-BXlMuIQn.js.map → peerjs-PTYQ7yEG.js.map} +1 -1
- package/dist/lib/{push-client-B419nn-i.js → push-client-DDV5Rcg6.js} +3 -3
- package/dist/lib/{push-client-B419nn-i.js.map → push-client-DDV5Rcg6.js.map} +1 -1
- package/dist/lib/{readGroupFileBytes-CSA7u-nJ.js → readGroupFileBytes-7C8G-yps.js} +2 -2
- package/dist/lib/{readGroupFileBytes-CSA7u-nJ.js.map → readGroupFileBytes-7C8G-yps.js.map} +1 -1
- package/dist/lib/{shadow-claw-DmMO4vVG.js → shadow-claw-WrWoPqFx.js} +10 -10
- package/dist/lib/{shadow-claw-DmMO4vVG.js.map → shadow-claw-WrWoPqFx.js.map} +1 -1
- package/dist/lib/{shadow-claw-a2ui-2I5nffIn.js → shadow-claw-a2ui-CcmJNXQP.js} +2 -2
- package/dist/lib/{shadow-claw-a2ui-2I5nffIn.js.map → shadow-claw-a2ui-CcmJNXQP.js.map} +1 -1
- package/dist/lib/{shadow-claw-accounts-DMb0y4ca.js → shadow-claw-accounts-CcB2429E.js} +3 -3
- package/dist/lib/{shadow-claw-accounts-DMb0y4ca.js.map → shadow-claw-accounts-CcB2429E.js.map} +1 -1
- package/dist/lib/{shadow-claw-channel-config-BhxHf6wZ.js → shadow-claw-channel-config-C4v0h8oa.js} +3 -3
- package/dist/lib/{shadow-claw-channel-config-BhxHf6wZ.js.map → shadow-claw-channel-config-C4v0h8oa.js.map} +1 -1
- package/dist/lib/{shadow-claw-channels-B5vzE9Z2.js → shadow-claw-channels-CyV88k9T.js} +3 -3
- package/dist/lib/{shadow-claw-channels-B5vzE9Z2.js.map → shadow-claw-channels-CyV88k9T.js.map} +1 -1
- package/dist/lib/{shadow-claw-chat-BsYnJ4-G.js → shadow-claw-chat-DSYtuZod.js} +11 -11
- package/dist/lib/{shadow-claw-chat-BsYnJ4-G.js.map → shadow-claw-chat-DSYtuZod.js.map} +1 -1
- package/dist/lib/{shadow-claw-control-plane-DqcgQsun.js → shadow-claw-control-plane-DBSGuDT1.js} +7 -7
- package/dist/lib/{shadow-claw-control-plane-DqcgQsun.js.map → shadow-claw-control-plane-DBSGuDT1.js.map} +1 -1
- package/dist/lib/{shadow-claw-conversations-B9Pn58g3.js → shadow-claw-conversations-Cp8pK4PM.js} +5 -5
- package/dist/lib/{shadow-claw-conversations-B9Pn58g3.js.map → shadow-claw-conversations-Cp8pK4PM.js.map} +1 -1
- package/dist/lib/{shadow-claw-file-viewer-DL6c4oPK.js → shadow-claw-file-viewer-B8G8yo48.js} +8 -8
- package/dist/lib/{shadow-claw-file-viewer-DL6c4oPK.js.map → shadow-claw-file-viewer-B8G8yo48.js.map} +1 -1
- package/dist/lib/{shadow-claw-files-C_ksPb8W.js → shadow-claw-files-D1COKzuC.js} +9 -9
- package/dist/lib/{shadow-claw-files-C_ksPb8W.js.map → shadow-claw-files-D1COKzuC.js.map} +1 -1
- package/dist/lib/{shadow-claw-git-jwsY7L1N.js → shadow-claw-git-60tWU4Ee.js} +6 -6
- package/dist/lib/{shadow-claw-git-jwsY7L1N.js.map → shadow-claw-git-60tWU4Ee.js.map} +1 -1
- package/dist/lib/{shadow-claw-integrations-yT7j07TM.js → shadow-claw-integrations-Bw1cG1ze.js} +4 -4
- package/dist/lib/{shadow-claw-integrations-yT7j07TM.js.map → shadow-claw-integrations-Bw1cG1ze.js.map} +1 -1
- package/dist/lib/{shadow-claw-llm-CEPJh1Jh.js → shadow-claw-llm-By5CvsFJ.js} +6 -6
- package/dist/lib/{shadow-claw-llm-CEPJh1Jh.js.map → shadow-claw-llm-By5CvsFJ.js.map} +1 -1
- package/dist/lib/{shadow-claw-mcp-remote-DGDWjByQ.js → shadow-claw-mcp-remote-BaNODd1v.js} +4 -4
- package/dist/lib/{shadow-claw-mcp-remote-DGDWjByQ.js.map → shadow-claw-mcp-remote-BaNODd1v.js.map} +1 -1
- package/dist/lib/{shadow-claw-networking-Bi4yvc73.js → shadow-claw-networking-DtZ5bsTz.js} +2 -2
- package/dist/lib/{shadow-claw-networking-Bi4yvc73.js.map → shadow-claw-networking-DtZ5bsTz.js.map} +1 -1
- package/dist/lib/{shadow-claw-notifications-C-JgGqQx.js → shadow-claw-notifications-C3VEba_A.js} +3 -3
- package/dist/lib/{shadow-claw-notifications-C-JgGqQx.js.map → shadow-claw-notifications-C3VEba_A.js.map} +1 -1
- package/dist/lib/{shadow-claw-page-header-DrAcWCZ9.js → shadow-claw-page-header-BLowJopA.js} +2 -2
- package/dist/lib/{shadow-claw-page-header-DrAcWCZ9.js.map → shadow-claw-page-header-BLowJopA.js.map} +1 -1
- package/dist/lib/{shadow-claw-pages-OEOClgA8.js → shadow-claw-pages-CpeUV-hw.js} +9 -9
- package/dist/lib/{shadow-claw-pages-OEOClgA8.js.map → shadow-claw-pages-CpeUV-hw.js.map} +1 -1
- package/dist/lib/{shadow-claw-peerjs-DmC2CV-9.js → shadow-claw-peerjs-DYAzJbTy.js} +3 -3
- package/dist/lib/{shadow-claw-peerjs-DmC2CV-9.js.map → shadow-claw-peerjs-DYAzJbTy.js.map} +1 -1
- package/dist/lib/{shadow-claw-settings-xAtFog0T.js → shadow-claw-settings-ByxwW3WK.js} +9 -9
- package/dist/lib/{shadow-claw-settings-xAtFog0T.js.map → shadow-claw-settings-ByxwW3WK.js.map} +1 -1
- package/dist/lib/{shadow-claw-storage-B4UHl-po.js → shadow-claw-storage-CsSPYVDJ.js} +5 -5
- package/dist/lib/{shadow-claw-storage-B4UHl-po.js.map → shadow-claw-storage-CsSPYVDJ.js.map} +1 -1
- package/dist/lib/{shadow-claw-task-server-B0eWzg57.js → shadow-claw-task-server-De2WCEw4.js} +2 -2
- package/dist/lib/{shadow-claw-task-server-B0eWzg57.js.map → shadow-claw-task-server-De2WCEw4.js.map} +1 -1
- package/dist/lib/{shadow-claw-tasks-HHU_8Gyg.js → shadow-claw-tasks-zHWT4GaE.js} +7 -7
- package/dist/lib/{shadow-claw-tasks-HHU_8Gyg.js.map → shadow-claw-tasks-zHWT4GaE.js.map} +1 -1
- package/dist/lib/{shadow-claw-terminal-CdC2G6Zz.js → shadow-claw-terminal-Bpawfv4N.js} +2 -2
- package/dist/lib/{shadow-claw-terminal-CdC2G6Zz.js.map → shadow-claw-terminal-Bpawfv4N.js.map} +1 -1
- package/dist/lib/{shadow-claw-tools-2AICxVXA.js → shadow-claw-tools-BbKdWcps.js} +7 -7
- package/dist/lib/{shadow-claw-tools-2AICxVXA.js.map → shadow-claw-tools-BbKdWcps.js.map} +1 -1
- package/dist/lib/{shadow-claw-webvm-Cy-Ia2vs.js → shadow-claw-webvm-DLqK0AxW.js} +3 -3
- package/dist/lib/{shadow-claw-webvm-Cy-Ia2vs.js.map → shadow-claw-webvm-DLqK0AxW.js.map} +1 -1
- package/dist/lib/subsystems/mcp/webmcp.d.ts +37 -0
- package/dist/lib/subsystems/providers/model-registry.d.ts +4 -1
- package/dist/lib/subsystems/providers/providers.d.ts +10 -0
- package/dist/lib/{syncWebMcpRegistration-CaAPPg2V.js → syncWebMcpRegistration-mlbbvcdm.js} +5 -5
- package/dist/lib/{syncWebMcpRegistration-CaAPPg2V.js.map → syncWebMcpRegistration-mlbbvcdm.js.map} +1 -1
- package/dist/lib/{tools-f1WPNDSS.js → tools-Bb2ujM2E.js} +4 -4
- package/dist/lib/{tools-f1WPNDSS.js.map → tools-Bb2ujM2E.js.map} +1 -1
- package/dist/lib/utils/index.js +1 -1
- package/dist/lib/utils/utils.js +1 -1
- package/dist/lib/{utils-B99XP70b.js → utils-mpsYPfS7.js} +3 -3
- package/dist/lib/{utils-B99XP70b.js.map → utils-mpsYPfS7.js.map} +1 -1
- package/dist/lib/webmcp-BVR2lBVo.js +1544 -0
- package/dist/lib/webmcp-BVR2lBVo.js.map +1 -0
- package/dist/public/.well-known/mcp/server-card.json +1 -1
- package/dist/public/.well-known/mcp.json +1 -1
- package/dist/public/agent.worker.js +44 -44
- package/dist/public/config-BqecTktB.js +1 -0
- package/dist/public/{configurePeerJs--JGpJVti.js → configurePeerJs-BPKfWuEt.js} +1 -1
- package/dist/public/{connections-6575KDGE.js → connections-C_3tYsmX.js} +1 -1
- package/dist/public/{custom-element-security-Da_6HQ6X.js → custom-element-security-DXwU3Lae.js} +1 -1
- package/dist/public/docs/example/article/index.html +2 -2
- package/dist/public/docs/publishing/index.html +2 -2
- package/dist/public/docs/skill-creator/index.html +2 -2
- package/dist/public/docs/subsystems/tools.md +4 -3
- package/dist/public/docs/subsystems/webmcp.md +58 -13
- package/dist/public/{downloadGroupFile-Dj-AMNsd.js → downloadGroupFile-DTyf5DIC.js} +1 -1
- package/dist/public/{e2e-bridge-CbYqdMp-.js → e2e-bridge-DXwWCQKw.js} +1 -1
- package/dist/public/{file-viewer-vHJ5Zsgk.js → file-viewer-ConEyDDG.js} +1 -1
- package/dist/public/{getGroupDir-BwprrdrQ.js → getGroupDir-ZfZsiq6d.js} +1 -1
- package/dist/public/{git-8F7wvoUB.js → git-BtBaLC1-.js} +2 -2
- package/dist/public/{iframe-sanitizer-viROQkRn.js → iframe-sanitizer-Corkvo_d.js} +1 -1
- package/dist/public/index.html +2 -2
- package/dist/public/index.js +1 -1
- package/dist/public/{initChatSplitResize-BA18wLQQ.js → initChatSplitResize-CUc-TEmT.js} +1 -1
- package/dist/public/{initControlPlane-C5v0FtJE.js → initControlPlane-B_uYS8a4.js} +1 -1
- package/dist/public/main/index.html +2 -2
- package/dist/public/main/memory/index.html +2 -2
- package/dist/public/{markdown-eFKxxpVP.js → markdown-Cfa6GuJs.js} +1 -1
- package/dist/public/{mcp-reconnect-CBHuaYdF.js → mcp-reconnect-CUOkdPEz.js} +1 -1
- package/dist/public/{memoryStorage-C1llaYus.js → memoryStorage-Bh8fjmip.js} +1 -1
- package/dist/public/{orchestrator-DNr2HDI2.js → orchestrator-BcSzrg6E.js} +2 -2
- package/dist/public/{orchestrator-BeJfwtvz.js → orchestrator-Bi5OdVZD.js} +11 -11
- package/dist/public/{peerjs-mG30TAJ_.js → peerjs-CzCXG-NW.js} +1 -1
- package/dist/public/{push-client-C2zMT3Sf.js → push-client-Cr3Ruahp.js} +1 -1
- package/dist/public/service-worker.js +1 -1
- package/dist/public/{shadow-claw-Ble6l0Iw.js → shadow-claw-CzUYuuOa.js} +2 -2
- package/dist/public/{shadow-claw-accounts-D1y-zbMs.js → shadow-claw-accounts-BgIHB_aP.js} +1 -1
- package/dist/public/{shadow-claw-channels-D_xfBoWp.js → shadow-claw-channels-Ce_trJ5D.js} +1 -1
- package/dist/public/{shadow-claw-chat-D_Lbi5oH.js → shadow-claw-chat-CdECLLwL.js} +1 -1
- package/dist/public/{shadow-claw-control-plane-CrnA5lM0.js → shadow-claw-control-plane-DM62UmOz.js} +2 -2
- package/dist/public/{shadow-claw-conversations-BN-hFmN1.js → shadow-claw-conversations-AM5g3C00.js} +1 -1
- package/dist/public/{shadow-claw-file-viewer-BKEpzpbx.js → shadow-claw-file-viewer-BP7gWS5g.js} +1 -1
- package/dist/public/{shadow-claw-files-DDZMk5AA.js → shadow-claw-files-RPPGszcK.js} +1 -1
- package/dist/public/{shadow-claw-git-CqOO97Re.js → shadow-claw-git-BsHEvLrb.js} +2 -2
- package/dist/public/{shadow-claw-integrations-CRGL78Fy.js → shadow-claw-integrations-B0vbcOqg.js} +1 -1
- package/dist/public/{shadow-claw-llm-DoGxEHQL.js → shadow-claw-llm-FNNamrc_.js} +2 -2
- package/dist/public/{shadow-claw-mcp-remote-DyGnK4S_.js → shadow-claw-mcp-remote-7mDgniQu.js} +1 -1
- package/dist/public/{shadow-claw-networking-DJeCdb7e.js → shadow-claw-networking-CGsetaLp.js} +1 -1
- package/dist/public/{shadow-claw-notifications-hSKJeMXb.js → shadow-claw-notifications-pnwRcR-4.js} +1 -1
- package/dist/public/{shadow-claw-page-header-DA-FG08G.js → shadow-claw-page-header-BUy15Poz.js} +1 -1
- package/dist/public/{shadow-claw-pages-DTS98Zbi.js → shadow-claw-pages-ILAjGWNk.js} +1 -1
- package/dist/public/{shadow-claw-settings-5w0ce8SP.js → shadow-claw-settings-Bx5IgVqN.js} +3 -3
- package/dist/public/{shadow-claw-storage-DqlVt27X.js → shadow-claw-storage-PAtNykaF.js} +1 -1
- package/dist/public/{shadow-claw-task-server-ByZwKI6S.js → shadow-claw-task-server-Cbbi_2OO.js} +1 -1
- package/dist/public/{shadow-claw-tasks-Dc4G4zBu.js → shadow-claw-tasks-B-jKYWZJ.js} +1 -1
- package/dist/public/{shadow-claw-terminal-DvusP7wg.js → shadow-claw-terminal-BRejND-Z.js} +1 -1
- package/dist/public/{shadow-claw-tools-BEsid8q-.js → shadow-claw-tools-DNQe3oa-.js} +1 -1
- package/dist/public/{shadow-claw-webvm-BWlIeK78.js → shadow-claw-webvm-BFuwRifU.js} +1 -1
- package/dist/public/{syncWebMcpRegistration-CqVCB6m8.js → syncWebMcpRegistration-FxKAPwaz.js} +1 -1
- package/dist/public/theme-init.js +1 -1
- package/dist/public/{tools-Cp1gequ8.js → tools-BwQKGsro.js} +1 -1
- package/dist/public/transformers-js.worker.js +1 -1
- package/dist/public/webmcp-C28PAlfy.js +1 -0
- package/dist/server.js +1 -1
- package/package.json +3 -3
- package/src/core/utils/initControlPlane.test.ts +122 -1
- package/src/core/utils/initControlPlane.ts +12 -8
- package/src/subsystems/mcp/webmcp.test.ts +253 -1
- package/src/subsystems/mcp/webmcp.ts +147 -4
- package/src/subsystems/providers/model-registry.test.ts +139 -0
- package/src/subsystems/providers/model-registry.ts +54 -2
- package/src/subsystems/providers/providers.test.ts +329 -0
- package/src/subsystems/providers/providers.ts +135 -11
- package/dist/lib/config-DjZ9QWc4.js.map +0 -1
- package/dist/lib/webmcp-BC8_WafM.js +0 -1710
- package/dist/lib/webmcp-BC8_WafM.js.map +0 -1
- package/dist/public/config-Bnwx4EfS.js +0 -1
- package/dist/public/webmcp-DBONjQWd.js +0 -2
package/dist/server.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import e,{createRequire as t}from"node:module";import n from"process";import{env as r,exit as i}from"node:process";import a from"node:http";import o from"node:https";import s,{basename as c}from"node:path";import l,{constants as u,createReadStream as d,promises as f,statSync as p}from"node:fs";import m,{tmpdir as h}from"node:os";import g,{URL as _,fileURLToPath as v,format as y}from"node:url";import b from"tcp-port-used";import x from"compression";import S from"express";import C from"web-push";import{DatabaseSync as w}from"node:sqlite";import{createHash as T,randomBytes as E,randomUUID as D}from"node:crypto";import O from"cors";import{Buffer as k}from"buffer";import A from"express-urlrewrite";import{BedrockClient as j,ListFoundationModelsCommand as M,ListInferenceProfilesCommand as N}from"@aws-sdk/client-bedrock";import{BedrockRuntimeClient as P,ConverseCommand as F,ConverseStreamCommand as I}from"@aws-sdk/client-bedrock-runtime";import{fromNodeProviderChain as L,fromSSO as ee}from"@aws-sdk/credential-providers";import*as te from"fs";import{createWriteStream as ne}from"fs";import*as R from"stream";import re from"node:zlib";import ie,{PassThrough as ae,Readable as oe,pipeline as se}from"node:stream";import{Buffer as z}from"node:buffer";import{deprecate as ce,promisify as le,types as ue}from"node:util";import{isIP as de}from"node:net";import*as fe from"path";import*as pe from"fs/promises";import{writeFile as me}from"fs/promises";import{finished as he}from"node:stream/promises";import{access as ge,mkdir as _e,readFile as ve,readdir as ye,stat as be,unlink as xe,writeFile as Se}from"node:fs/promises";import{execFileSync as Ce,spawn as we}from"node:child_process";import{EventEmitter as Te}from"node:events";var Ee=Object.create,De=Object.defineProperty,Oe=Object.getOwnPropertyDescriptor,ke=Object.getOwnPropertyNames,Ae=Object.getPrototypeOf,je=Object.prototype.hasOwnProperty,B=(e,t,n)=>()=>{if(n)throw n[0];try{return e&&(t=e(e=0)),t}catch(e){throw n=[e],e}},V=(e,t)=>()=>(t||(e((t={exports:{}}).exports,t),e=null),t.exports),Me=(e,t)=>{let n={};for(var r in e)De(n,r,{get:e[r],enumerable:!0});return t||De(n,Symbol.toStringTag,{value:`Module`}),n},Ne=(e,t,n,r)=>{if(t&&typeof t==`object`||typeof t==`function`)for(var i=ke(t),a=0,o=i.length,s;a<o;a++)s=i[a],!je.call(e,s)&&s!==n&&De(e,s,{get:(e=>t[e]).bind(null,s),enumerable:!(r=Oe(t,s))||r.enumerable});return e},Pe=(e,t,n)=>(n=e==null?{}:Ee(Ae(e)),Ne(t||!e||!e.__esModule?De(n,`default`,{value:e,enumerable:!0}):n,e)),H=t(import.meta.url);const Fe=new class{loading=!1;models=new Map;getModelInfo(e){return this.models.get(e.toLowerCase())??null}registerModelInfo(e,t){this.models.set(e.toLowerCase(),t)}clear(){this.models.clear()}async fetchModelInfo(e,t,n){if(e.modelsUrl){this.loading=!0;try{let r=new Headers;if(r.set(`Content-Type`,`application/json`),e.headers)for(let[t,n]of Object.entries(e.headers))r.set(t,n);if(n)for(let[e,t]of Object.entries(n))r.set(e,t);if(t&&e.apiKeyHeader){let n=e.apiKeyHeaderFormat;r.set(e.apiKeyHeader,n?n.replace(`{key}`,t):t)}let i=await fetch(e.modelsUrl,{headers:r});if(!i.ok)throw Error(`HTTP ${i.status} ${i.statusText}`);let a=await i.json(),o=Array.isArray(a.data)?a.data:Array.isArray(a.models)?a.models:[];for(let e of o){if(!e.id)continue;let t=e.context_length??e.context_window??0,n=e.max_completion_tokens||e.per_request_limits?.completion_tokens||e.top_provider?.max_completion_tokens||null,r=typeof e.supports_tools==`boolean`?e.supports_tools:typeof e.supportsTools==`boolean`?e.supportsTools:void 0,i=this.extractModalities(e,`input`),a=this.extractModalities(e,`output`),o=this.modalitiesInclude(i,`image`,`vision`),s=this.modalitiesInclude(i,`audio`,`voice`,`hearing`),c=this.modalitiesInclude(i,`video`),l=e.id===`openrouter/free`||this.supportedParametersInclude(e,`image`,`vision`,`audio`,`tool`,`structured`),u=this.extractReasoning(e);!t&&Array.isArray(e.providers)&&e.providers.length>0&&(t=e.providers[0]?.context_length||0),this.registerModelInfo(e.id,{contextWindow:t,maxOutput:n,...r!==void 0&&{supportsTools:r},...i.length>0&&{inputModalities:i},...a.length>0&&{outputModalities:a},...o!==void 0&&{supportsImageInput:o},...s!==void 0&&{supportsAudioInput:s},...c!==void 0&&{supportsVideoInput:c},...u&&{reasoning:u},...l&&{routesByRequestFeatures:l}})}o.length>0&&console.log(`[ModelRegistry] Registered ${o.length} models for provider "${e.id}"`)}catch(t){console.error(`[ModelRegistry] Error fetching models for ${e.id}:`,t)}finally{this.loading=!1}}}extractModalities(e,t){let n=t===`input`?e.input_modalities||e.inputModalities:e.output_modalities||e.outputModalities,r=e.architecture||{},i=t===`input`?r.input_modalities||r.inputModalities:r.output_modalities||r.outputModalities,a=typeof r.modality==`string`?r.modality:``,o=this.parseArchitectureModality(a,t),s=[...this.normalizeStringArray(n),...this.normalizeStringArray(i),...o];return Array.from(new Set(s))}extractReasoning(e){let t=e?.reasoning;if(!t||typeof t!=`object`)return;let n=this.normalizeStringArray(t.supported_efforts),r=typeof t.default_effort==`string`?t.default_effort.toLowerCase():void 0,i=typeof t.default_enabled==`boolean`?t.default_enabled:void 0,a=typeof t.supports_max_tokens==`boolean`?t.supports_max_tokens:void 0,o=typeof t.mandatory==`boolean`?t.mandatory:void 0;if(!(n.length===0&&r===void 0&&i===void 0&&a===void 0&&o===void 0))return{...n.length>0&&{supportedEfforts:n},...r!==void 0&&{defaultEffort:r},...i!==void 0&&{defaultEnabled:i},...a!==void 0&&{supportsMaxTokens:a},...o!==void 0&&{mandatory:o}}}modalitiesInclude(e,...t){if(e.length!==0)return t.some(t=>e.some(e=>e.includes(t)))}normalizeStringArray(e){return Array.isArray(e)?e.filter(e=>typeof e==`string`).map(e=>e.toLowerCase()):[]}parseArchitectureModality(e,t){if(!e)return[];let[n=``,r=``]=e.toLowerCase().split(`->`).map(e=>e.trim());return(t===`input`?n:r).split(/[+,/\s]+/).map(e=>e.trim()).filter(Boolean)}supportedParametersInclude(e,...t){let n=this.normalizeStringArray(e.supported_parameters||e.supportedParameters);return t.some(e=>n.some(t=>t.includes(e)))}};function Ie(e){return e.replace(/^\/+|\/+$/g,``)}function Le(e){let t=Ie(e);return t.endsWith(`/index.html`)?t=t.slice(0,-11):t.endsWith(`index.html`)&&(t=t.slice(0,-10)),Ie(t)}function Re(e){let t=Ie(e),n=t.split(`/`).filter(Boolean);if(n.length===0)return null;let r=n[0].toLowerCase();if(r===`pages`){if(n.length>=3){let e=n[1];return{page:`pages`,groupId:e===`main`?`br:main`:e.startsWith(`br-`)?`br:${e.slice(3)}`:e,path:n.slice(2).join(`/`)}}return{page:`pages`,groupId:`br:main`,path:n.slice(1).join(`/`)}}if(r===`files`){let e=n[1]||`main`;return{page:`files`,groupId:e===`main`?`br:main`:e.startsWith(`br-`)?`br:${e.slice(3)}`:e,path:n.slice(2).join(`/`)||void 0}}if(r===`chat`){let e=n[1]||`main`;return{page:`chat`,groupId:e===`main`?`br:main`:e.startsWith(`br-`)?`br:${e.slice(3)}`:e}}if(r===`tasks`){let e=n[1]||`main`;return{page:`tasks`,groupId:e===`main`?`br:main`:e.startsWith(`br-`)?`br:${e.slice(3)}`:e}}return r===`main`?{page:`pages`,groupId:`br:main`,path:n.slice(1).join(`/`)}:{page:`pages`,groupId:`br:main`,path:t}}function ze(){if(typeof document<`u`){let e=document.getElementById(`shadow-claw-static-routing`);if(e&&e.textContent)try{let t=JSON.parse(e.textContent);if(t&&typeof t==`object`&&t.routes)return t}catch(e){console.warn(`Failed to parse embedded static routing manifest:`,e)}}return null}function Be(e,t){let n=t||ze();if(!n||!n.routes)return null;let r=Le(e);if(!r)return null;for(let[e,t]of Object.entries(n.routes))if(!(!t||!t.prettyPath)&&Le(t.prettyPath)===r)return Re(e);return null}let Ve=null;const He=new Set([`chat`,`files`,`tasks`,`pages`,`settings`,`tools`,`channels`]);function Ue(){Ve=null}typeof globalThis<`u`&&(globalThis.__applyBasePathCacheReset=Ue);function We(){let e=typeof window<`u`?window:typeof self<`u`?self:globalThis;if(e&&e.__SHADOWCLAW_DEPLOY_ID__){let t=String(e.__SHADOWCLAW_DEPLOY_ID__).trim();if(t)return t.replace(/[^a-zA-Z0-9_-]/g,`-`)}let t=globalThis.process;if(t&&t.env&&t.env.SHADOWCLAW_DEPLOY_ID){let e=String(t.env.SHADOWCLAW_DEPLOY_ID).trim();if(e)return e.replace(/[^a-zA-Z0-9_-]/g,`-`)}let n=Ge();return!n||n===`/`?``:n.replace(/^\/+|\/+$/g,``).replace(/[^a-zA-Z0-9_-]/g,`-`)}function Ge(){if(Ve!==null)return Ve;if((typeof window>`u`||globalThis.WorkerGlobalScope!==void 0&&typeof self<`u`&&self instanceof globalThis.WorkerGlobalScope)&&typeof self<`u`&&self.location){let e=(self.location.pathname||`/`).split(`/`).filter(Boolean);if(e.length<=1)return Ve=`/`,Ve;let t=e[0].toLowerCase(),n=t===`service-worker.js`||t===`sw.js`||t===`manifest.json`||t===`sitemap.xml`||t===`favicon.ico`||t===`index.html`||t===`service-worker`||t===`assets`||t===`dist`||t===`public`;if(n&&e.length===2)return Ve=`/`,Ve;if(!n)return Ve=`/`+e[0]+`/`,Ve}let e=typeof window<`u`&&window.location?window.location:typeof self<`u`&&self.location?self.location:null;if(!e)return`/`;if(typeof document<`u`&&typeof document.querySelector==`function`){let t=document.querySelector(`base[href]`);if(t){let n=t.getAttribute(`href`);if(n)try{let t=new URL(n,e.origin).pathname;return t.endsWith(`/`)||(t+=`/`),Ve=t,Ve}catch{}}}let t=e.pathname||`/`;if(t===`/`)return Ve=`/`,Ve;let n=t.split(`/`).filter(Boolean),r=n.findIndex(e=>He.has(e.toLowerCase()));return r>=0?r===0?(Ve=`/`,Ve):(Ve=`/`+n.slice(0,r).join(`/`)+`/`,Ve):Be(t)?(Ve=`/`,Ve):n.length===1&&!t.includes(`.`)?(Ve=`/`+n[0]+`/`,Ve):(Ve=`/`,Ve)}function Ke(e){let t=e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`);return RegExp(`(^|\\s)@${t}\\b`,`i`)}Ke(`ShadowClaw`);function qe(){let e=We();return e?`shadowclaw-${e}`:`shadowclaw`}qe();const Je={github:{providerId:`github`,name:`GitHub`,aliases:[`github`,`api.github.com`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`,`git_remote`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`token `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://github.com/login/oauth/authorize`,tokenUrl:`https://github.com/login/oauth/access_token`,defaultScopes:[`repo`,`read:user`],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}},gitlab:{providerId:`gitlab`,name:`GitLab`,aliases:[`gitlab`,`gitlab.com`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`,`git_remote`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`PRIVATE-TOKEN`,headerPrefix:``},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},tokenAuthByServiceType:{git_remote:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}}},oauth:{authorizeUrl:`https://gitlab.com/oauth/authorize`,tokenUrl:`https://gitlab.com/oauth/token`,defaultScopes:[`read_api`,`read_user`],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}},figma:{providerId:`figma`,name:`Figma`,aliases:[`figma`,`api.figma.com`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`X-Figma-Token`,headerPrefix:``},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://www.figma.com/oauth`,tokenUrl:`https://api.figma.com/v1/oauth/token`,refreshUrl:`https://api.figma.com/v1/oauth/refresh`,defaultScopes:[],usePkce:!0,clientAuthMethod:`basic_header`,scopeSeparator:`space`}},notion:{providerId:`notion`,name:`Notion`,aliases:[`notion`,`api.notion.com`,`notion.so`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://api.notion.com/v1/oauth/authorize`,tokenUrl:`https://api.notion.com/v1/oauth/token`,defaultScopes:[],usePkce:!0,clientAuthMethod:`basic_header`,scopeSeparator:`space`}},microsoft_graph:{providerId:`microsoft_graph`,name:`Microsoft Graph`,aliases:[`microsoft`,`graph.microsoft.com`,`microsoft_graph`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://login.microsoftonline.com/common/oauth2/v2.0/authorize`,tokenUrl:`https://login.microsoftonline.com/common/oauth2/v2.0/token`,defaultScopes:[`openid`,`profile`,`offline_access`,`User.Read`],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}},yahoo_mail:{providerId:`yahoo_mail`,name:`Yahoo Mail`,aliases:[`yahoo`,`yahoo_mail`,`mail.yahoo.com`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://api.login.yahoo.com/oauth2/request_auth`,tokenUrl:`https://api.login.yahoo.com/oauth2/get_token`,defaultScopes:[`mail-r`,`mail-w`],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}},google:{providerId:`google`,name:`Google`,aliases:[`google`,`googleapis.com`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://accounts.google.com/o/oauth2/v2/auth`,tokenUrl:`https://oauth2.googleapis.com/token`,defaultScopes:[`openid`,`profile`,`email`],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}},atlassian:{providerId:`atlassian`,name:`Atlassian`,aliases:[`atlassian`,`api.atlassian.com`],modes:[`oauth`,`token`,`basic`],defaultMode:`basic`,serviceTypes:[`http_api`,`git_remote`],authTypes:[`token`,`oauth`,`basic_userpass`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `},basic:{headerName:`Authorization`,headerPrefix:`Basic `}},oauth:{authorizeUrl:`https://auth.atlassian.com/authorize`,tokenUrl:`https://auth.atlassian.com/oauth/token`,defaultScopes:[],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}},slack:{providerId:`slack`,name:`Slack`,aliases:[`slack`,`slack.com`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://slack.com/oauth/v2/authorize`,tokenUrl:`https://slack.com/api/oauth.v2.access`,defaultScopes:[],usePkce:!1,clientAuthMethod:`request_body`,scopeSeparator:`comma`}},linear:{providerId:`linear`,name:`Linear`,aliases:[`linear`,`api.linear.app`,`linear.app`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://linear.app/oauth/authorize`,tokenUrl:`https://api.linear.app/oauth/token`,defaultScopes:[],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}},azure_devops:{providerId:`azure_devops`,name:`Azure DevOps`,aliases:[`azure_devops`,`dev.azure.com`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`,`git_remote`],authTypes:[`token`,`oauth`,`basic_userpass`,`ssh_key`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},tokenAuthByServiceType:{git_remote:{token:{headerName:`Authorization`,headerPrefix:`Basic `},oauth:{headerName:`Authorization`,headerPrefix:`Basic `}}},oauth:{authorizeUrl:`https://app.vssps.visualstudio.com/oauth2/authorize`,tokenUrl:`https://app.vssps.visualstudio.com/oauth2/token`,defaultScopes:[`vso.code`,`vso.profile`,`offline_access`],usePkce:!1,clientAuthMethod:`request_body`,scopeSeparator:`space`}},custom_mcp:{providerId:`custom_mcp`,name:`Custom MCP`,aliases:[`custom_mcp`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`mcp_remote`,`webmcp_local`],authTypes:[`none`,`token`,`oauth`,`custom_header`,`ssh_key`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://example.com/oauth/authorize`,tokenUrl:`https://example.com/oauth/token`,defaultScopes:[],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}}},Ye=Object.fromEntries(Object.values(Je).filter(e=>!!e.oauth).map(e=>{let t=e.oauth;return[e.providerId,{id:e.providerId,name:e.name,authorizeUrl:t.authorizeUrl,tokenUrl:t.tokenUrl,redirectUri:t.redirectUri,defaultScopes:t.defaultScopes,usePkce:t.usePkce,clientAuthMethod:t.clientAuthMethod,scopeSeparator:t.scopeSeparator}]}));Object.fromEntries(Object.values(Je).map(e=>[e.providerId,{providerId:e.providerId,modes:e.modes,defaultMode:e.defaultMode}]));function Xe(e){return Ye[e]||null}let Ze=null;function Qe(e=`database/push-subscriptions.db`){if(Ze)return Ze;Ze=new w(e),Ze.exec(`
|
|
1
|
+
import e,{createRequire as t}from"node:module";import n from"process";import{env as r,exit as i}from"node:process";import a from"node:http";import o from"node:https";import s,{basename as c}from"node:path";import l,{constants as u,createReadStream as d,promises as f,statSync as p}from"node:fs";import m,{tmpdir as h}from"node:os";import g,{URL as _,fileURLToPath as v,format as y}from"node:url";import b from"tcp-port-used";import x from"compression";import S from"express";import C from"web-push";import{DatabaseSync as w}from"node:sqlite";import{createHash as T,randomBytes as E,randomUUID as D}from"node:crypto";import O from"cors";import{Buffer as k}from"buffer";import A from"express-urlrewrite";import{BedrockClient as j,ListFoundationModelsCommand as M,ListInferenceProfilesCommand as N}from"@aws-sdk/client-bedrock";import{BedrockRuntimeClient as P,ConverseCommand as F,ConverseStreamCommand as I}from"@aws-sdk/client-bedrock-runtime";import{fromNodeProviderChain as L,fromSSO as ee}from"@aws-sdk/credential-providers";import*as te from"fs";import{createWriteStream as ne}from"fs";import*as R from"stream";import re from"node:zlib";import ie,{PassThrough as ae,Readable as oe,pipeline as se}from"node:stream";import{Buffer as z}from"node:buffer";import{deprecate as ce,promisify as le,types as ue}from"node:util";import{isIP as de}from"node:net";import*as fe from"path";import*as pe from"fs/promises";import{writeFile as me}from"fs/promises";import{finished as he}from"node:stream/promises";import{access as ge,mkdir as _e,readFile as ve,readdir as ye,stat as be,unlink as xe,writeFile as Se}from"node:fs/promises";import{execFileSync as Ce,spawn as we}from"node:child_process";import{EventEmitter as Te}from"node:events";var Ee=Object.create,De=Object.defineProperty,Oe=Object.getOwnPropertyDescriptor,ke=Object.getOwnPropertyNames,Ae=Object.getPrototypeOf,je=Object.prototype.hasOwnProperty,B=(e,t,n)=>()=>{if(n)throw n[0];try{return e&&(t=e(e=0)),t}catch(e){throw n=[e],e}},V=(e,t)=>()=>(t||(e((t={exports:{}}).exports,t),e=null),t.exports),Me=(e,t)=>{let n={};for(var r in e)De(n,r,{get:e[r],enumerable:!0});return t||De(n,Symbol.toStringTag,{value:`Module`}),n},Ne=(e,t,n,r)=>{if(t&&typeof t==`object`||typeof t==`function`)for(var i=ke(t),a=0,o=i.length,s;a<o;a++)s=i[a],!je.call(e,s)&&s!==n&&De(e,s,{get:(e=>t[e]).bind(null,s),enumerable:!(r=Oe(t,s))||r.enumerable});return e},Pe=(e,t,n)=>(n=e==null?{}:Ee(Ae(e)),Ne(t||!e||!e.__esModule?De(n,`default`,{value:e,enumerable:!0}):n,e)),H=t(import.meta.url);const Fe=new class{loading=!1;models=new Map;getModelInfo(e){if(!e||typeof e!=`string`)return null;let t=e.toLowerCase().trim(),n=this.models.get(t);if(n)return n;if(t.includes(`/`)){let e=t.split(`/`).pop();if(e&&this.models.has(e))return this.models.get(e)}else for(let[e,n]of this.models.entries())if(e.endsWith(`/${t}`))return n;if(t.includes(`:`)){let e=t.split(`:`)[0],n=this.getModelInfo(e);if(n)return n}return null}registerModelInfo(e,t){this.models.set(e.toLowerCase(),t)}clear(){this.models.clear()}async fetchModelInfo(e,t,n){if(e.modelsUrl){this.loading=!0;try{let r=new Headers;if(r.set(`Content-Type`,`application/json`),e.headers)for(let[t,n]of Object.entries(e.headers))r.set(t,n);if(n)for(let[e,t]of Object.entries(n))r.set(e,t);if(t&&e.apiKeyHeader){let n=e.apiKeyHeaderFormat;r.set(e.apiKeyHeader,n?n.replace(`{key}`,t):t)}let i=await fetch(e.modelsUrl,{headers:r});if(!i.ok)throw Error(`HTTP ${i.status} ${i.statusText}`);let a=await i.json(),o=Array.isArray(a.data)?a.data:Array.isArray(a.models)?a.models:[];for(let e of o){if(!e.id)continue;let t=e.context_length??e.context_window??0,n=e.max_completion_tokens||e.per_request_limits?.completion_tokens||e.top_provider?.max_completion_tokens||null,r=typeof e.supports_tools==`boolean`?e.supports_tools:typeof e.supportsTools==`boolean`?e.supportsTools:void 0,i=this.extractModalities(e,`input`),a=this.extractModalities(e,`output`),o=this.modalitiesInclude(i,`image`,`vision`),s=this.modalitiesInclude(i,`audio`,`voice`,`hearing`),c=this.modalitiesInclude(i,`video`),l=e.id===`openrouter/free`||this.supportedParametersInclude(e,`image`,`vision`,`audio`,`tool`,`structured`),u=this.extractReasoning(e),d=typeof e.supports_prompt_caching==`boolean`?e.supports_prompt_caching:typeof e.supportsPromptCaching==`boolean`?e.supportsPromptCaching:this.supportedParametersInclude(e,`cache_control`,`prompt_caching`)?!0:void 0;!t&&Array.isArray(e.providers)&&e.providers.length>0&&(t=e.providers[0]?.context_length||0),this.registerModelInfo(e.id,{contextWindow:t,maxOutput:n,...r!==void 0&&{supportsTools:r},...i.length>0&&{inputModalities:i},...a.length>0&&{outputModalities:a},...o!==void 0&&{supportsImageInput:o},...s!==void 0&&{supportsAudioInput:s},...c!==void 0&&{supportsVideoInput:c},...u&&{reasoning:u},...l&&{routesByRequestFeatures:l},...d!==void 0&&{supportsPromptCaching:d}})}o.length>0&&console.log(`[ModelRegistry] Registered ${o.length} models for provider "${e.id}"`)}catch(t){console.error(`[ModelRegistry] Error fetching models for ${e.id}:`,t)}finally{this.loading=!1}}}extractModalities(e,t){let n=t===`input`?e.input_modalities||e.inputModalities:e.output_modalities||e.outputModalities,r=e.architecture||{},i=t===`input`?r.input_modalities||r.inputModalities:r.output_modalities||r.outputModalities,a=typeof r.modality==`string`?r.modality:``,o=this.parseArchitectureModality(a,t),s=[...this.normalizeStringArray(n),...this.normalizeStringArray(i),...o];return Array.from(new Set(s))}extractReasoning(e){let t=e?.reasoning;if(!t||typeof t!=`object`)return;let n=this.normalizeStringArray(t.supported_efforts),r=typeof t.default_effort==`string`?t.default_effort.toLowerCase():void 0,i=typeof t.default_enabled==`boolean`?t.default_enabled:void 0,a=typeof t.supports_max_tokens==`boolean`?t.supports_max_tokens:void 0,o=typeof t.mandatory==`boolean`?t.mandatory:void 0;if(!(n.length===0&&r===void 0&&i===void 0&&a===void 0&&o===void 0))return{...n.length>0&&{supportedEfforts:n},...r!==void 0&&{defaultEffort:r},...i!==void 0&&{defaultEnabled:i},...a!==void 0&&{supportsMaxTokens:a},...o!==void 0&&{mandatory:o}}}modalitiesInclude(e,...t){if(e.length!==0)return t.some(t=>e.some(e=>e.includes(t)))}normalizeStringArray(e){return Array.isArray(e)?e.filter(e=>typeof e==`string`).map(e=>e.toLowerCase()):[]}parseArchitectureModality(e,t){if(!e)return[];let[n=``,r=``]=e.toLowerCase().split(`->`).map(e=>e.trim());return(t===`input`?n:r).split(/[+,/\s]+/).map(e=>e.trim()).filter(Boolean)}supportedParametersInclude(e,...t){let n=this.normalizeStringArray(e.supported_parameters||e.supportedParameters);return t.some(e=>n.some(t=>t.includes(e)))}};function Ie(e){return e.replace(/^\/+|\/+$/g,``)}function Le(e){let t=Ie(e);return t.endsWith(`/index.html`)?t=t.slice(0,-11):t.endsWith(`index.html`)&&(t=t.slice(0,-10)),Ie(t)}function Re(e){let t=Ie(e),n=t.split(`/`).filter(Boolean);if(n.length===0)return null;let r=n[0].toLowerCase();if(r===`pages`){if(n.length>=3){let e=n[1];return{page:`pages`,groupId:e===`main`?`br:main`:e.startsWith(`br-`)?`br:${e.slice(3)}`:e,path:n.slice(2).join(`/`)}}return{page:`pages`,groupId:`br:main`,path:n.slice(1).join(`/`)}}if(r===`files`){let e=n[1]||`main`;return{page:`files`,groupId:e===`main`?`br:main`:e.startsWith(`br-`)?`br:${e.slice(3)}`:e,path:n.slice(2).join(`/`)||void 0}}if(r===`chat`){let e=n[1]||`main`;return{page:`chat`,groupId:e===`main`?`br:main`:e.startsWith(`br-`)?`br:${e.slice(3)}`:e}}if(r===`tasks`){let e=n[1]||`main`;return{page:`tasks`,groupId:e===`main`?`br:main`:e.startsWith(`br-`)?`br:${e.slice(3)}`:e}}return r===`main`?{page:`pages`,groupId:`br:main`,path:n.slice(1).join(`/`)}:{page:`pages`,groupId:`br:main`,path:t}}function ze(){if(typeof document<`u`){let e=document.getElementById(`shadow-claw-static-routing`);if(e&&e.textContent)try{let t=JSON.parse(e.textContent);if(t&&typeof t==`object`&&t.routes)return t}catch(e){console.warn(`Failed to parse embedded static routing manifest:`,e)}}return null}function Be(e,t){let n=t||ze();if(!n||!n.routes)return null;let r=Le(e);if(!r)return null;for(let[e,t]of Object.entries(n.routes))if(!(!t||!t.prettyPath)&&Le(t.prettyPath)===r)return Re(e);return null}let Ve=null;const He=new Set([`chat`,`files`,`tasks`,`pages`,`settings`,`tools`,`channels`]);function Ue(){Ve=null}typeof globalThis<`u`&&(globalThis.__applyBasePathCacheReset=Ue);function We(){let e=typeof window<`u`?window:typeof self<`u`?self:globalThis;if(e&&e.__SHADOWCLAW_DEPLOY_ID__){let t=String(e.__SHADOWCLAW_DEPLOY_ID__).trim();if(t)return t.replace(/[^a-zA-Z0-9_-]/g,`-`)}let t=globalThis.process;if(t&&t.env&&t.env.SHADOWCLAW_DEPLOY_ID){let e=String(t.env.SHADOWCLAW_DEPLOY_ID).trim();if(e)return e.replace(/[^a-zA-Z0-9_-]/g,`-`)}let n=Ge();return!n||n===`/`?``:n.replace(/^\/+|\/+$/g,``).replace(/[^a-zA-Z0-9_-]/g,`-`)}function Ge(){if(Ve!==null)return Ve;if((typeof window>`u`||globalThis.WorkerGlobalScope!==void 0&&typeof self<`u`&&self instanceof globalThis.WorkerGlobalScope)&&typeof self<`u`&&self.location){let e=(self.location.pathname||`/`).split(`/`).filter(Boolean);if(e.length<=1)return Ve=`/`,Ve;let t=e[0].toLowerCase(),n=t===`service-worker.js`||t===`sw.js`||t===`manifest.json`||t===`sitemap.xml`||t===`favicon.ico`||t===`index.html`||t===`service-worker`||t===`assets`||t===`dist`||t===`public`;if(n&&e.length===2)return Ve=`/`,Ve;if(!n)return Ve=`/`+e[0]+`/`,Ve}let e=typeof window<`u`&&window.location?window.location:typeof self<`u`&&self.location?self.location:null;if(!e)return`/`;if(typeof document<`u`&&typeof document.querySelector==`function`){let t=document.querySelector(`base[href]`);if(t){let n=t.getAttribute(`href`);if(n)try{let t=new URL(n,e.origin).pathname;return t.endsWith(`/`)||(t+=`/`),Ve=t,Ve}catch{}}}let t=e.pathname||`/`;if(t===`/`)return Ve=`/`,Ve;let n=t.split(`/`).filter(Boolean),r=n.findIndex(e=>He.has(e.toLowerCase()));return r>=0?r===0?(Ve=`/`,Ve):(Ve=`/`+n.slice(0,r).join(`/`)+`/`,Ve):Be(t)?(Ve=`/`,Ve):n.length===1&&!t.includes(`.`)?(Ve=`/`+n[0]+`/`,Ve):(Ve=`/`,Ve)}function Ke(e){let t=e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`);return RegExp(`(^|\\s)@${t}\\b`,`i`)}Ke(`ShadowClaw`);function qe(){let e=We();return e?`shadowclaw-${e}`:`shadowclaw`}qe();const Je={github:{providerId:`github`,name:`GitHub`,aliases:[`github`,`api.github.com`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`,`git_remote`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`token `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://github.com/login/oauth/authorize`,tokenUrl:`https://github.com/login/oauth/access_token`,defaultScopes:[`repo`,`read:user`],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}},gitlab:{providerId:`gitlab`,name:`GitLab`,aliases:[`gitlab`,`gitlab.com`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`,`git_remote`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`PRIVATE-TOKEN`,headerPrefix:``},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},tokenAuthByServiceType:{git_remote:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}}},oauth:{authorizeUrl:`https://gitlab.com/oauth/authorize`,tokenUrl:`https://gitlab.com/oauth/token`,defaultScopes:[`read_api`,`read_user`],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}},figma:{providerId:`figma`,name:`Figma`,aliases:[`figma`,`api.figma.com`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`X-Figma-Token`,headerPrefix:``},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://www.figma.com/oauth`,tokenUrl:`https://api.figma.com/v1/oauth/token`,refreshUrl:`https://api.figma.com/v1/oauth/refresh`,defaultScopes:[],usePkce:!0,clientAuthMethod:`basic_header`,scopeSeparator:`space`}},notion:{providerId:`notion`,name:`Notion`,aliases:[`notion`,`api.notion.com`,`notion.so`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://api.notion.com/v1/oauth/authorize`,tokenUrl:`https://api.notion.com/v1/oauth/token`,defaultScopes:[],usePkce:!0,clientAuthMethod:`basic_header`,scopeSeparator:`space`}},microsoft_graph:{providerId:`microsoft_graph`,name:`Microsoft Graph`,aliases:[`microsoft`,`graph.microsoft.com`,`microsoft_graph`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://login.microsoftonline.com/common/oauth2/v2.0/authorize`,tokenUrl:`https://login.microsoftonline.com/common/oauth2/v2.0/token`,defaultScopes:[`openid`,`profile`,`offline_access`,`User.Read`],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}},yahoo_mail:{providerId:`yahoo_mail`,name:`Yahoo Mail`,aliases:[`yahoo`,`yahoo_mail`,`mail.yahoo.com`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://api.login.yahoo.com/oauth2/request_auth`,tokenUrl:`https://api.login.yahoo.com/oauth2/get_token`,defaultScopes:[`mail-r`,`mail-w`],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}},google:{providerId:`google`,name:`Google`,aliases:[`google`,`googleapis.com`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://accounts.google.com/o/oauth2/v2/auth`,tokenUrl:`https://oauth2.googleapis.com/token`,defaultScopes:[`openid`,`profile`,`email`],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}},atlassian:{providerId:`atlassian`,name:`Atlassian`,aliases:[`atlassian`,`api.atlassian.com`],modes:[`oauth`,`token`,`basic`],defaultMode:`basic`,serviceTypes:[`http_api`,`git_remote`],authTypes:[`token`,`oauth`,`basic_userpass`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `},basic:{headerName:`Authorization`,headerPrefix:`Basic `}},oauth:{authorizeUrl:`https://auth.atlassian.com/authorize`,tokenUrl:`https://auth.atlassian.com/oauth/token`,defaultScopes:[],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}},slack:{providerId:`slack`,name:`Slack`,aliases:[`slack`,`slack.com`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://slack.com/oauth/v2/authorize`,tokenUrl:`https://slack.com/api/oauth.v2.access`,defaultScopes:[],usePkce:!1,clientAuthMethod:`request_body`,scopeSeparator:`comma`}},linear:{providerId:`linear`,name:`Linear`,aliases:[`linear`,`api.linear.app`,`linear.app`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://linear.app/oauth/authorize`,tokenUrl:`https://api.linear.app/oauth/token`,defaultScopes:[],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}},azure_devops:{providerId:`azure_devops`,name:`Azure DevOps`,aliases:[`azure_devops`,`dev.azure.com`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`,`git_remote`],authTypes:[`token`,`oauth`,`basic_userpass`,`ssh_key`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},tokenAuthByServiceType:{git_remote:{token:{headerName:`Authorization`,headerPrefix:`Basic `},oauth:{headerName:`Authorization`,headerPrefix:`Basic `}}},oauth:{authorizeUrl:`https://app.vssps.visualstudio.com/oauth2/authorize`,tokenUrl:`https://app.vssps.visualstudio.com/oauth2/token`,defaultScopes:[`vso.code`,`vso.profile`,`offline_access`],usePkce:!1,clientAuthMethod:`request_body`,scopeSeparator:`space`}},custom_mcp:{providerId:`custom_mcp`,name:`Custom MCP`,aliases:[`custom_mcp`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`mcp_remote`,`webmcp_local`],authTypes:[`none`,`token`,`oauth`,`custom_header`,`ssh_key`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://example.com/oauth/authorize`,tokenUrl:`https://example.com/oauth/token`,defaultScopes:[],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}}},Ye=Object.fromEntries(Object.values(Je).filter(e=>!!e.oauth).map(e=>{let t=e.oauth;return[e.providerId,{id:e.providerId,name:e.name,authorizeUrl:t.authorizeUrl,tokenUrl:t.tokenUrl,redirectUri:t.redirectUri,defaultScopes:t.defaultScopes,usePkce:t.usePkce,clientAuthMethod:t.clientAuthMethod,scopeSeparator:t.scopeSeparator}]}));Object.fromEntries(Object.values(Je).map(e=>[e.providerId,{providerId:e.providerId,modes:e.modes,defaultMode:e.defaultMode}]));function Xe(e){return Ye[e]||null}let Ze=null;function Qe(e=`database/push-subscriptions.db`){if(Ze)return Ze;Ze=new w(e),Ze.exec(`
|
|
2
2
|
CREATE TABLE IF NOT EXISTS vapid_keys (
|
|
3
3
|
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
4
4
|
public_key TEXT NOT NULL,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "shadow-claw",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.33.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "AGPL-3.0",
|
|
6
6
|
"description": "Browser-native personal AI assistant.",
|
|
@@ -132,8 +132,8 @@
|
|
|
132
132
|
"web-push": "^3.6.7"
|
|
133
133
|
},
|
|
134
134
|
"devDependencies": {
|
|
135
|
-
"@mcp-b/webmcp-polyfill": "^
|
|
136
|
-
"@mcp-b/webmcp-types": "^
|
|
135
|
+
"@mcp-b/webmcp-polyfill": "^5.1.0",
|
|
136
|
+
"@mcp-b/webmcp-types": "^5.1.0",
|
|
137
137
|
"@playwright/test": "^1.59.2",
|
|
138
138
|
"@storybook/addon-docs": "^10.6.0",
|
|
139
139
|
"@storybook/web-components-vite": "^10.6.0",
|
|
@@ -703,7 +703,8 @@ describe("initControlPlane", () => {
|
|
|
703
703
|
});
|
|
704
704
|
expect(mockExecuteTool).toHaveBeenCalledWith(
|
|
705
705
|
nativeModelContextTool,
|
|
706
|
-
|
|
706
|
+
{},
|
|
707
|
+
undefined,
|
|
707
708
|
);
|
|
708
709
|
} finally {
|
|
709
710
|
if (origDocMC)
|
|
@@ -713,6 +714,126 @@ describe("initControlPlane", () => {
|
|
|
713
714
|
}
|
|
714
715
|
});
|
|
715
716
|
|
|
717
|
+
it("handles Chrome 155+ WebMCP invoke-tool passing JavaScript object directly to executeTool", async () => {
|
|
718
|
+
const origDocMC = Object.getOwnPropertyDescriptor(
|
|
719
|
+
document,
|
|
720
|
+
"modelContext",
|
|
721
|
+
);
|
|
722
|
+
const origNavMC = Object.getOwnPropertyDescriptor(
|
|
723
|
+
navigator,
|
|
724
|
+
"modelContext",
|
|
725
|
+
);
|
|
726
|
+
delete (navigator as any).modelContext;
|
|
727
|
+
|
|
728
|
+
const nativeModelContextTool = {
|
|
729
|
+
name: "list_files",
|
|
730
|
+
description: "List files and directories",
|
|
731
|
+
inputSchema: { type: "object" },
|
|
732
|
+
title: "list_files",
|
|
733
|
+
origin: "http://localhost:8888",
|
|
734
|
+
window: globalThis.window,
|
|
735
|
+
};
|
|
736
|
+
|
|
737
|
+
const mockExecuteTool = jest.fn(async (tool: any, input: any) => {
|
|
738
|
+
if (tool !== nativeModelContextTool) {
|
|
739
|
+
throw new TypeError("parameter 1 is not of type 'ModelContextTool'");
|
|
740
|
+
}
|
|
741
|
+
if (typeof input !== "object" || input === null) {
|
|
742
|
+
throw new TypeError("parameter 2 is not of type 'Object'");
|
|
743
|
+
}
|
|
744
|
+
return "chrome-155-object-result";
|
|
745
|
+
});
|
|
746
|
+
|
|
747
|
+
Object.defineProperty(document, "modelContext", {
|
|
748
|
+
value: {
|
|
749
|
+
getTools: jest.fn(async () => [nativeModelContextTool]),
|
|
750
|
+
executeTool: mockExecuteTool,
|
|
751
|
+
},
|
|
752
|
+
configurable: true,
|
|
753
|
+
});
|
|
754
|
+
|
|
755
|
+
try {
|
|
756
|
+
const res = await executeClientControlCommand(
|
|
757
|
+
"invoke-tool",
|
|
758
|
+
{ toolName: "list_files", input: { recursive: true } },
|
|
759
|
+
{},
|
|
760
|
+
);
|
|
761
|
+
|
|
762
|
+
expect(res).toEqual({ result: "chrome-155-object-result" });
|
|
763
|
+
expect(mockExecuteTool).toHaveBeenCalledTimes(1);
|
|
764
|
+
expect(mockExecuteTool).toHaveBeenCalledWith(
|
|
765
|
+
nativeModelContextTool,
|
|
766
|
+
{
|
|
767
|
+
recursive: true,
|
|
768
|
+
},
|
|
769
|
+
undefined,
|
|
770
|
+
);
|
|
771
|
+
} finally {
|
|
772
|
+
if (origDocMC)
|
|
773
|
+
Object.defineProperty(document, "modelContext", origDocMC);
|
|
774
|
+
if (origNavMC)
|
|
775
|
+
Object.defineProperty(navigator, "modelContext", origNavMC);
|
|
776
|
+
}
|
|
777
|
+
});
|
|
778
|
+
|
|
779
|
+
it("gracefully falls back to stringified input in invoke-tool when executeTool throws 'Failed to parse input'", async () => {
|
|
780
|
+
const origDocMC = Object.getOwnPropertyDescriptor(
|
|
781
|
+
document,
|
|
782
|
+
"modelContext",
|
|
783
|
+
);
|
|
784
|
+
const origNavMC = Object.getOwnPropertyDescriptor(
|
|
785
|
+
navigator,
|
|
786
|
+
"modelContext",
|
|
787
|
+
);
|
|
788
|
+
delete (navigator as any).modelContext;
|
|
789
|
+
|
|
790
|
+
const nativeModelContextTool = {
|
|
791
|
+
name: "list_files",
|
|
792
|
+
description: "List files and directories",
|
|
793
|
+
inputSchema: '{"type":"object"}',
|
|
794
|
+
title: "list_files",
|
|
795
|
+
origin: "http://localhost:8888",
|
|
796
|
+
window: globalThis.window,
|
|
797
|
+
};
|
|
798
|
+
|
|
799
|
+
const calls: any[] = [];
|
|
800
|
+
const mockExecuteTool = jest.fn(async (_tool: any, input: any) => {
|
|
801
|
+
calls.push(input);
|
|
802
|
+
if (typeof input !== "string") {
|
|
803
|
+
throw new Error(
|
|
804
|
+
"Failed to parse input arguments: expected DOMString",
|
|
805
|
+
);
|
|
806
|
+
}
|
|
807
|
+
return "legacy-chrome-string-result";
|
|
808
|
+
});
|
|
809
|
+
|
|
810
|
+
Object.defineProperty(document, "modelContext", {
|
|
811
|
+
value: {
|
|
812
|
+
getTools: jest.fn(async () => [nativeModelContextTool]),
|
|
813
|
+
executeTool: mockExecuteTool,
|
|
814
|
+
},
|
|
815
|
+
configurable: true,
|
|
816
|
+
});
|
|
817
|
+
|
|
818
|
+
try {
|
|
819
|
+
const res = await executeClientControlCommand(
|
|
820
|
+
"invoke-tool",
|
|
821
|
+
{ toolName: "list_files", input: { path: "src" } },
|
|
822
|
+
{},
|
|
823
|
+
);
|
|
824
|
+
|
|
825
|
+
expect(res).toEqual({ result: "legacy-chrome-string-result" });
|
|
826
|
+
expect(calls).toHaveLength(2);
|
|
827
|
+
expect(calls[0]).toEqual({ path: "src" });
|
|
828
|
+
expect(calls[1]).toBe(JSON.stringify({ path: "src" }));
|
|
829
|
+
} finally {
|
|
830
|
+
if (origDocMC)
|
|
831
|
+
Object.defineProperty(document, "modelContext", origDocMC);
|
|
832
|
+
if (origNavMC)
|
|
833
|
+
Object.defineProperty(navigator, "modelContext", origNavMC);
|
|
834
|
+
}
|
|
835
|
+
});
|
|
836
|
+
|
|
716
837
|
it("refuses to execute tool when tool is not registered on client or not enabled in active conversation", async () => {
|
|
717
838
|
const origDocMC = Object.getOwnPropertyDescriptor(
|
|
718
839
|
document,
|
|
@@ -466,36 +466,40 @@ export async function executeClientControlCommand(
|
|
|
466
466
|
}
|
|
467
467
|
}
|
|
468
468
|
|
|
469
|
-
const
|
|
470
|
-
|
|
471
|
-
? args.input
|
|
472
|
-
: JSON.stringify(args.input || {});
|
|
469
|
+
const { executeWebMcpTool } =
|
|
470
|
+
await import("../../subsystems/mcp/webmcp.js");
|
|
473
471
|
|
|
474
472
|
if (typeof ctx.executeTool === "function") {
|
|
475
473
|
// 1. Pass the actual ModelContextTool object from getTools() if available (required by native Chromium)
|
|
476
474
|
if (matchedTool) {
|
|
477
475
|
try {
|
|
478
|
-
const res = await ctx
|
|
476
|
+
const res = await executeWebMcpTool(ctx, matchedTool, args.input);
|
|
479
477
|
return { result: res };
|
|
480
478
|
} catch (_) {}
|
|
481
479
|
}
|
|
482
480
|
|
|
483
481
|
// 2. Pass { name: toolName } object (accepted by polyfill / mocks)
|
|
484
482
|
try {
|
|
485
|
-
const res = await
|
|
483
|
+
const res = await executeWebMcpTool(
|
|
484
|
+
ctx,
|
|
486
485
|
{ name: args.toolName },
|
|
487
|
-
|
|
486
|
+
args.input,
|
|
488
487
|
);
|
|
489
488
|
return { result: res };
|
|
490
489
|
} catch (_) {}
|
|
491
490
|
|
|
492
491
|
// 3. Pass toolName string directly (accepted by some preview shims)
|
|
493
492
|
try {
|
|
494
|
-
const res = await ctx
|
|
493
|
+
const res = await executeWebMcpTool(ctx, args.toolName, args.input);
|
|
495
494
|
return { result: res };
|
|
496
495
|
} catch (_) {}
|
|
497
496
|
}
|
|
498
497
|
|
|
498
|
+
const inputStr =
|
|
499
|
+
typeof args.input === "string"
|
|
500
|
+
? args.input
|
|
501
|
+
: JSON.stringify(args.input || {});
|
|
502
|
+
|
|
499
503
|
// 4. Check navigator.modelContextTesting if present
|
|
500
504
|
if (
|
|
501
505
|
typeof navigator !== "undefined" &&
|
|
@@ -330,10 +330,33 @@ describe("webmcp integration", () => {
|
|
|
330
330
|
|
|
331
331
|
expect(listTasksRegistration).toBeDefined();
|
|
332
332
|
expect(listTasksRegistration.annotations).toEqual({
|
|
333
|
-
readOnlyHint:
|
|
333
|
+
readOnlyHint: true,
|
|
334
|
+
consequentialHint: false,
|
|
334
335
|
untrustedContentHint: true,
|
|
335
336
|
});
|
|
336
337
|
|
|
338
|
+
// Verify consequential tools receive consequentialHint: true
|
|
339
|
+
const consequentialToolNames = [
|
|
340
|
+
"bash",
|
|
341
|
+
"delete_file",
|
|
342
|
+
"delete_task",
|
|
343
|
+
"git_delete_repo",
|
|
344
|
+
"git_delete_branch",
|
|
345
|
+
"git_reset",
|
|
346
|
+
"git_push",
|
|
347
|
+
"email_send_message",
|
|
348
|
+
"clear_chat",
|
|
349
|
+
];
|
|
350
|
+
|
|
351
|
+
for (const toolName of consequentialToolNames) {
|
|
352
|
+
const reg = mockRegisterTool.mock.calls
|
|
353
|
+
.map((args: any[]) => args[0])
|
|
354
|
+
.find((r: any) => r.name === toolName);
|
|
355
|
+
expect(reg).toBeDefined();
|
|
356
|
+
expect(reg.annotations.consequentialHint).toBe(true);
|
|
357
|
+
expect(reg.annotations.readOnlyHint).toBe(false);
|
|
358
|
+
}
|
|
359
|
+
|
|
337
360
|
// Test that execute sends a postMessage
|
|
338
361
|
const executePromise = listTasksRegistration.execute({ foo: "bar" });
|
|
339
362
|
|
|
@@ -486,4 +509,233 @@ describe("webmcp integration", () => {
|
|
|
486
509
|
await registerWebMcpTools(null, jest.fn(), "group-webmcp");
|
|
487
510
|
expect(mockRegisterTool.mock.calls.length).toBe(firstCount * 2);
|
|
488
511
|
});
|
|
512
|
+
|
|
513
|
+
describe("executeWebMcpTool", () => {
|
|
514
|
+
it("executes tool passing JavaScript object directly on Chrome 155+ (PR #246/#251)", async () => {
|
|
515
|
+
const { executeWebMcpTool } = await import("./webmcp.js");
|
|
516
|
+
|
|
517
|
+
const mockTool = { name: "my_tool" };
|
|
518
|
+
const mockModelContext = {
|
|
519
|
+
executeTool: jest.fn(async (tool: any, input: any) => {
|
|
520
|
+
expect(tool).toBe(mockTool);
|
|
521
|
+
expect(typeof input).toBe("object");
|
|
522
|
+
expect(input).toEqual({ answer: 42 });
|
|
523
|
+
return "result-ok";
|
|
524
|
+
}),
|
|
525
|
+
};
|
|
526
|
+
|
|
527
|
+
const res = await executeWebMcpTool(mockModelContext, mockTool, {
|
|
528
|
+
answer: 42,
|
|
529
|
+
});
|
|
530
|
+
expect(res).toBe("result-ok");
|
|
531
|
+
expect(mockModelContext.executeTool).toHaveBeenCalledTimes(1);
|
|
532
|
+
expect(mockModelContext.executeTool).toHaveBeenCalledWith(
|
|
533
|
+
mockTool,
|
|
534
|
+
{ answer: 42 },
|
|
535
|
+
undefined,
|
|
536
|
+
);
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
it("falls back to stringified JSON when executeTool throws 'Failed to parse input' (Chrome < 155 / polyfill)", async () => {
|
|
540
|
+
const { executeWebMcpTool } = await import("./webmcp.js");
|
|
541
|
+
|
|
542
|
+
const mockTool = { name: "my_tool" };
|
|
543
|
+
const calls: any[] = [];
|
|
544
|
+
const mockModelContext = {
|
|
545
|
+
executeTool: jest.fn(async (tool: any, input: any) => {
|
|
546
|
+
calls.push({ tool, input });
|
|
547
|
+
if (typeof input !== "string") {
|
|
548
|
+
throw new Error(
|
|
549
|
+
"Failed to parse input arguments: expected DOMString",
|
|
550
|
+
);
|
|
551
|
+
}
|
|
552
|
+
return "legacy-result";
|
|
553
|
+
}),
|
|
554
|
+
};
|
|
555
|
+
|
|
556
|
+
const res = await executeWebMcpTool(mockModelContext, mockTool, {
|
|
557
|
+
answer: 42,
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
expect(res).toBe("legacy-result");
|
|
561
|
+
expect(calls).toHaveLength(2);
|
|
562
|
+
expect(calls[0].input).toEqual({ answer: 42 });
|
|
563
|
+
expect(calls[1].input).toBe(JSON.stringify({ answer: 42 }));
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
it("falls back to stringified JSON when executeTool throws TypeError (WebIDL parameter rejection)", async () => {
|
|
567
|
+
const { executeWebMcpTool } = await import("./webmcp.js");
|
|
568
|
+
|
|
569
|
+
const mockTool = { name: "my_tool" };
|
|
570
|
+
const calls: any[] = [];
|
|
571
|
+
const mockModelContext = {
|
|
572
|
+
executeTool: jest.fn(async (_tool: any, input: any) => {
|
|
573
|
+
calls.push(input);
|
|
574
|
+
if (typeof input !== "string") {
|
|
575
|
+
throw new TypeError(
|
|
576
|
+
"Failed to execute 'executeTool' on 'ModelContext': parameter 2 is not of type 'DOMString'",
|
|
577
|
+
);
|
|
578
|
+
}
|
|
579
|
+
return "typeerror-fallback-result";
|
|
580
|
+
}),
|
|
581
|
+
};
|
|
582
|
+
|
|
583
|
+
const res = await executeWebMcpTool(mockModelContext, mockTool, {
|
|
584
|
+
param: "value",
|
|
585
|
+
});
|
|
586
|
+
|
|
587
|
+
expect(res).toBe("typeerror-fallback-result");
|
|
588
|
+
expect(calls).toHaveLength(2);
|
|
589
|
+
expect(calls[0]).toEqual({ param: "value" });
|
|
590
|
+
expect(calls[1]).toBe(JSON.stringify({ param: "value" }));
|
|
591
|
+
});
|
|
592
|
+
|
|
593
|
+
it("normalizes string input and executes with parsed object first then string fallback", async () => {
|
|
594
|
+
const { executeWebMcpTool } = await import("./webmcp.js");
|
|
595
|
+
|
|
596
|
+
const mockTool = { name: "my_tool" };
|
|
597
|
+
const calls: any[] = [];
|
|
598
|
+
const mockModelContext = {
|
|
599
|
+
executeTool: jest.fn(async (_tool: any, input: any) => {
|
|
600
|
+
calls.push(input);
|
|
601
|
+
if (typeof input !== "string") {
|
|
602
|
+
throw new Error("Failed to parse input");
|
|
603
|
+
}
|
|
604
|
+
return "string-input-result";
|
|
605
|
+
}),
|
|
606
|
+
};
|
|
607
|
+
|
|
608
|
+
const res = await executeWebMcpTool(
|
|
609
|
+
mockModelContext,
|
|
610
|
+
mockTool,
|
|
611
|
+
'{"foo":"bar"}',
|
|
612
|
+
);
|
|
613
|
+
|
|
614
|
+
expect(res).toBe("string-input-result");
|
|
615
|
+
expect(calls).toHaveLength(2);
|
|
616
|
+
expect(calls[0]).toEqual({ foo: "bar" });
|
|
617
|
+
expect(calls[1]).toBe('{"foo":"bar"}');
|
|
618
|
+
});
|
|
619
|
+
|
|
620
|
+
it("handles undefined/null input by passing empty object then empty JSON string on fallback", async () => {
|
|
621
|
+
const { executeWebMcpTool } = await import("./webmcp.js");
|
|
622
|
+
|
|
623
|
+
const mockTool = { name: "my_tool" };
|
|
624
|
+
const calls: any[] = [];
|
|
625
|
+
const mockModelContext = {
|
|
626
|
+
executeTool: jest.fn(async (_tool: any, input: any) => {
|
|
627
|
+
calls.push(input);
|
|
628
|
+
if (typeof input !== "string") {
|
|
629
|
+
throw new Error("Failed to parse input");
|
|
630
|
+
}
|
|
631
|
+
return "empty-result";
|
|
632
|
+
}),
|
|
633
|
+
};
|
|
634
|
+
|
|
635
|
+
const res = await executeWebMcpTool(mockModelContext, mockTool);
|
|
636
|
+
|
|
637
|
+
expect(res).toBe("empty-result");
|
|
638
|
+
expect(calls).toHaveLength(2);
|
|
639
|
+
expect(calls[0]).toEqual({});
|
|
640
|
+
expect(calls[1]).toBe("{}");
|
|
641
|
+
});
|
|
642
|
+
|
|
643
|
+
it("propagates other errors without attempting string fallback", async () => {
|
|
644
|
+
const { executeWebMcpTool } = await import("./webmcp.js");
|
|
645
|
+
|
|
646
|
+
const mockTool = { name: "my_tool" };
|
|
647
|
+
const mockModelContext = {
|
|
648
|
+
executeTool: jest.fn(async () => {
|
|
649
|
+
throw new Error("Tool internal execution failed");
|
|
650
|
+
}),
|
|
651
|
+
};
|
|
652
|
+
|
|
653
|
+
await expect(
|
|
654
|
+
executeWebMcpTool(mockModelContext, mockTool, { foo: "bar" }),
|
|
655
|
+
).rejects.toThrow("Tool internal execution failed");
|
|
656
|
+
|
|
657
|
+
expect(mockModelContext.executeTool).toHaveBeenCalledTimes(1);
|
|
658
|
+
});
|
|
659
|
+
|
|
660
|
+
it("passes execution options or signal context to executeTool", async () => {
|
|
661
|
+
const { executeWebMcpTool } = await import("./webmcp.js");
|
|
662
|
+
|
|
663
|
+
const mockTool = { name: "my_tool" };
|
|
664
|
+
const controller = new AbortController();
|
|
665
|
+
const mockModelContext = {
|
|
666
|
+
executeTool: jest.fn(async (_tool: any, _input: any, options: any) => {
|
|
667
|
+
expect(options).toEqual({ signal: controller.signal });
|
|
668
|
+
return "with-signal";
|
|
669
|
+
}),
|
|
670
|
+
};
|
|
671
|
+
|
|
672
|
+
const res = await executeWebMcpTool(
|
|
673
|
+
mockModelContext,
|
|
674
|
+
mockTool,
|
|
675
|
+
{ foo: "bar" },
|
|
676
|
+
{ signal: controller.signal },
|
|
677
|
+
);
|
|
678
|
+
|
|
679
|
+
expect(res).toBe("with-signal");
|
|
680
|
+
});
|
|
681
|
+
});
|
|
682
|
+
|
|
683
|
+
describe("resolveWebMcpToolAnnotations", () => {
|
|
684
|
+
it("marks destructive and high-stakes tools with consequentialHint: true", async () => {
|
|
685
|
+
const { resolveWebMcpToolAnnotations, CONSEQUENTIAL_TOOL_NAMES } =
|
|
686
|
+
await import("./webmcp.js");
|
|
687
|
+
|
|
688
|
+
for (const toolName of CONSEQUENTIAL_TOOL_NAMES) {
|
|
689
|
+
const annotations = resolveWebMcpToolAnnotations(toolName);
|
|
690
|
+
expect(annotations.consequentialHint).toBe(true);
|
|
691
|
+
expect(annotations.readOnlyHint).toBe(false);
|
|
692
|
+
expect(annotations.untrustedContentHint).toBe(true);
|
|
693
|
+
}
|
|
694
|
+
});
|
|
695
|
+
|
|
696
|
+
it("marks read-only query tools with readOnlyHint: true and consequentialHint: false", async () => {
|
|
697
|
+
const { resolveWebMcpToolAnnotations, READ_ONLY_TOOL_NAMES } =
|
|
698
|
+
await import("./webmcp.js");
|
|
699
|
+
|
|
700
|
+
for (const toolName of READ_ONLY_TOOL_NAMES) {
|
|
701
|
+
const annotations = resolveWebMcpToolAnnotations(toolName);
|
|
702
|
+
expect(annotations.readOnlyHint).toBe(true);
|
|
703
|
+
expect(annotations.consequentialHint).toBe(false);
|
|
704
|
+
expect(annotations.untrustedContentHint).toBe(true);
|
|
705
|
+
}
|
|
706
|
+
});
|
|
707
|
+
|
|
708
|
+
it("marks regular state-mutating tools with readOnlyHint: false and consequentialHint: false", async () => {
|
|
709
|
+
const { resolveWebMcpToolAnnotations } = await import("./webmcp.js");
|
|
710
|
+
|
|
711
|
+
const mutatingTools = [
|
|
712
|
+
"write_file",
|
|
713
|
+
"patch_file",
|
|
714
|
+
"create_task",
|
|
715
|
+
"update_task",
|
|
716
|
+
"show_toast",
|
|
717
|
+
"send_notification",
|
|
718
|
+
];
|
|
719
|
+
|
|
720
|
+
for (const toolName of mutatingTools) {
|
|
721
|
+
const annotations = resolveWebMcpToolAnnotations(toolName);
|
|
722
|
+
expect(annotations.readOnlyHint).toBe(false);
|
|
723
|
+
expect(annotations.consequentialHint).toBe(false);
|
|
724
|
+
expect(annotations.untrustedContentHint).toBe(true);
|
|
725
|
+
}
|
|
726
|
+
});
|
|
727
|
+
|
|
728
|
+
it("allows custom annotations to override defaults", async () => {
|
|
729
|
+
const { resolveWebMcpToolAnnotations } = await import("./webmcp.js");
|
|
730
|
+
|
|
731
|
+
const custom = resolveWebMcpToolAnnotations("read_file", {
|
|
732
|
+
consequentialHint: true,
|
|
733
|
+
customMeta: "important",
|
|
734
|
+
});
|
|
735
|
+
|
|
736
|
+
expect(custom.consequentialHint).toBe(true);
|
|
737
|
+
expect(custom.readOnlyHint).toBe(true);
|
|
738
|
+
expect(custom.customMeta).toBe("important");
|
|
739
|
+
});
|
|
740
|
+
});
|
|
489
741
|
});
|