shadow-claw 1.40.0 → 1.41.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 +2 -2
- package/.well-known/mcp/servers.json +1 -1
- package/.well-known/mcp.json +2 -2
- package/README.md +1 -0
- package/dist/cli/build/build.js +1 -1
- package/dist/cli/{chunk-Bfz2NYgD.js → chunk-0IeC97mU.js} +7 -7
- package/dist/cli/chunk-3oLxlxPn.js +1 -0
- package/dist/cli/{chunk-BXg4-9Rq2.js → chunk-8CrZyP1b2.js} +1 -1
- package/dist/cli/{chunk-BP5Hg6CM.js → chunk-B--ueArB.js} +1 -1
- package/dist/cli/chunk-BIh7rXlJ2.js +1 -0
- package/dist/cli/{chunk-Drr0VSGA2.js → chunk-BKnTOLLS2.js} +1 -1
- package/dist/cli/{chunk-C5PuRV08.js → chunk-BLMC1Bxv.js} +1 -1
- package/dist/cli/{chunk-B2tNSlFy2.js → chunk-BOigzz2U2.js} +1 -1
- package/dist/cli/{chunk-DOf3DZdR.js → chunk-BOzn335y.js} +1 -1
- package/dist/cli/chunk-BjOeVLb42.js +1 -0
- package/dist/cli/{chunk-DqTx0rRp2.js → chunk-Bjqh5_SU2.js} +1 -1
- package/dist/cli/chunk-BqWBd_jF2.js +1 -0
- package/dist/cli/{chunk-Bc9nRNyN.js → chunk-BvVUnHTq.js} +12 -9
- package/dist/cli/{chunk-DIbSoQmd2.js → chunk-C6FFByBU2.js} +2 -2
- package/dist/cli/{chunk-Drq18VZe.js → chunk-C9-aL3rF.js} +1 -1
- package/dist/cli/{chunk-BUhqHeEA2.js → chunk-C9czddEh2.js} +2 -2
- package/dist/cli/chunk-CCx8WOwz2.js +1 -0
- package/dist/cli/{chunk-BkMSjVJb.js → chunk-CK6ejMig.js} +1 -1
- package/dist/cli/chunk-CUUERioR2.js +1 -0
- package/dist/cli/chunk-CXAobnJd.js +1 -0
- package/dist/cli/chunk-CY1X0pOF2.js +1 -0
- package/dist/cli/chunk-CZvvgLVv.js +1 -0
- package/dist/cli/chunk-CbcwbfJB.js +1 -0
- package/dist/cli/{chunk-quilB3TV2.js → chunk-Ci9OJGcd2.js} +1 -1
- package/dist/cli/chunk-Cj9wl0qx.js +1 -0
- package/dist/cli/{chunk-3gRhOmGo2.js → chunk-CjJsVrKI2.js} +1 -1
- package/dist/cli/{chunk-CmEfzm-_.js → chunk-ClPZ4Rea.js} +1 -1
- package/dist/cli/{chunk-WMPAfIvV2.js → chunk-DHJCGRuG2.js} +1 -1
- package/dist/cli/{chunk-C7Y2xIk4.js → chunk-DKVkYTV9.js} +1 -1
- package/dist/cli/chunk-DR2XtWRo.js +1 -0
- package/dist/cli/{chunk-DID5VvOG.js → chunk-DTES-dE6.js} +3 -3
- package/dist/cli/{chunk-BiVJVh_S.js → chunk-DUlIqkaX.js} +7 -7
- package/dist/cli/{chunk-CkpueGaZ2.js → chunk-DjWle8FF2.js} +1 -1
- package/dist/cli/{chunk-CSvHmNOk.js → chunk-DmRXVLfy.js} +1 -1
- package/dist/cli/{chunk-BT1xXZn72.js → chunk-HlSiTC-u2.js} +1 -1
- package/dist/cli/{chunk-da0bsiOn.js → chunk-P4JSeFBI.js} +1 -1
- package/dist/cli/chunk-UcNbHaRQ.js +1 -0
- package/dist/cli/{chunk-BXFHFbEd.js → chunk-UweLmfQS.js} +1 -1
- package/dist/cli/chunk-bQQkVCw-.js +94 -0
- package/dist/cli/{chunk-CGiXWHTi2.js → chunk-gwpxl-0W2.js} +1 -1
- package/dist/cli/{chunk-NH8koEis2.js → chunk-nmDhxXH_2.js} +1 -1
- package/dist/cli/chunk-p0n_c-I2.js +1 -0
- package/dist/cli/{chunk-D7xP72La.js → chunk-q7r4FosZ.js} +1 -1
- package/dist/cli/cli.js +3 -3
- package/dist/cli/prerender/dsd-shell/prerender-dsd-shell.js +1 -1
- package/dist/electron/main.cjs +1 -1
- package/dist/headless-agent.js +1 -1
- package/dist/lib/{app-routes-BAEMLu9g.js → app-routes-CKkiX12g.js} +7 -2
- package/dist/lib/app-routes-CKkiX12g.js.map +1 -0
- 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/utils/syncSlottedSidebarActiveLinks.d.ts +10 -0
- 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/shadow-claw-file-viewer.d.ts +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-CPHDmyTx.js → config-DZkF9LiO.js} +2 -2
- package/dist/lib/{config-CPHDmyTx.js.map → config-DZkF9LiO.js.map} +1 -1
- package/dist/lib/{configurePeerJs-BsJoycQd.js → configurePeerJs-B3IE9BY2.js} +3 -3
- package/dist/lib/{configurePeerJs-BsJoycQd.js.map → configurePeerJs-B3IE9BY2.js.map} +1 -1
- package/dist/lib/{connections-BdIru7bF.js → connections-CalJGWvZ.js} +2 -2
- package/dist/lib/{connections-BdIru7bF.js.map → connections-CalJGWvZ.js.map} +1 -1
- package/dist/lib/core/orchestrator/orchestrator-state.d.ts +2 -1
- package/dist/lib/core/orchestrator/orchestrator.d.ts +2 -1
- package/dist/lib/core/orchestrator/utils/operations/task.d.ts +1 -0
- package/dist/lib/{downloadGroupFile-Dn0KsMlV.js → downloadGroupFile-DDE1fIhJ.js} +2 -2
- package/dist/lib/{downloadGroupFile-Dn0KsMlV.js.map → downloadGroupFile-DDE1fIhJ.js.map} +1 -1
- package/dist/lib/{file-viewer-BYrXXu-X.js → file-viewer-Bll2EoGc.js} +5 -5
- package/dist/lib/{file-viewer-BYrXXu-X.js.map → file-viewer-Bll2EoGc.js.map} +1 -1
- package/dist/lib/{getGroupDir-DF-RZO8X.js → getGroupDir-MHv7Lvmp.js} +4 -4
- package/dist/lib/{getGroupDir-DF-RZO8X.js.map → getGroupDir-MHv7Lvmp.js.map} +1 -1
- package/dist/lib/{git-s_0cgKm1.js → git-Bv_M3dgw.js} +3 -3
- package/dist/lib/{git-s_0cgKm1.js.map → git-Bv_M3dgw.js.map} +1 -1
- package/dist/lib/{iframe-sanitizer-DFdda1jJ.js → iframe-sanitizer-C7ZGDjEI.js} +2 -2
- package/dist/lib/{iframe-sanitizer-DFdda1jJ.js.map → iframe-sanitizer-C7ZGDjEI.js.map} +1 -1
- package/dist/lib/{iframe-storage-proxy-qrhZmQyO.js → iframe-storage-proxy-CR8ZYHPd.js} +2 -2
- package/dist/lib/{iframe-storage-proxy-qrhZmQyO.js.map → iframe-storage-proxy-CR8ZYHPd.js.map} +1 -1
- package/dist/lib/index.js +27 -27
- package/dist/lib/{initChatSplitResize-DuNlwCuI.js → initChatSplitResize-DiKTI-wk.js} +2 -2
- package/dist/lib/{initChatSplitResize-DuNlwCuI.js.map → initChatSplitResize-DiKTI-wk.js.map} +1 -1
- package/dist/lib/{initControlPlane-D4UZ4EhV.js → initControlPlane-CzjtQ4aM.js} +15 -15
- package/dist/lib/{initControlPlane-D4UZ4EhV.js.map → initControlPlane-CzjtQ4aM.js.map} +1 -1
- package/dist/lib/{markdown-CRRwbN9h.js → markdown-k8wA9LKy.js} +4 -4
- package/dist/lib/{markdown-CRRwbN9h.js.map → markdown-k8wA9LKy.js.map} +1 -1
- package/dist/lib/{mcp-reconnect-COdZ-cea.js → mcp-reconnect-CGIdfZnS.js} +2 -2
- package/dist/lib/{mcp-reconnect-COdZ-cea.js.map → mcp-reconnect-CGIdfZnS.js.map} +1 -1
- package/dist/lib/{memoryStorage-BsMe2FOQ.js → memoryStorage-DL-Bo3dh.js} +2 -2
- package/dist/lib/{memoryStorage-BsMe2FOQ.js.map → memoryStorage-DL-Bo3dh.js.map} +1 -1
- package/dist/lib/{orchestrator-D9f5fDCb.js → orchestrator-DKHCoikY.js} +11 -11
- package/dist/lib/{orchestrator-D9f5fDCb.js.map → orchestrator-DKHCoikY.js.map} +1 -1
- package/dist/lib/{orchestrator-Bq9ft3uG.js → orchestrator-Dn1ZOz9B.js} +20 -24
- package/dist/lib/orchestrator-Dn1ZOz9B.js.map +1 -0
- package/dist/lib/{peerjs-6u8KtenE.js → peerjs-BK41iBvP.js} +7 -7
- package/dist/lib/{peerjs-6u8KtenE.js.map → peerjs-BK41iBvP.js.map} +1 -1
- package/dist/lib/{push-client-DCnB2-OU.js → push-client-DKgVFD2Z.js} +3 -3
- package/dist/lib/{push-client-DCnB2-OU.js.map → push-client-DKgVFD2Z.js.map} +1 -1
- package/dist/lib/{readGroupFileBytes-nKNJ6sbl.js → readGroupFileBytes-CgU_UfGv.js} +2 -2
- package/dist/lib/{readGroupFileBytes-nKNJ6sbl.js.map → readGroupFileBytes-CgU_UfGv.js.map} +1 -1
- package/dist/lib/{shadow-claw-MumteAJy.js → shadow-claw-CtCj5Rn_.js} +121 -16
- package/dist/lib/shadow-claw-CtCj5Rn_.js.map +1 -0
- package/dist/lib/{shadow-claw-a2ui-Dipe8nY2.js → shadow-claw-a2ui-CvK5w940.js} +2 -2
- package/dist/lib/{shadow-claw-a2ui-Dipe8nY2.js.map → shadow-claw-a2ui-CvK5w940.js.map} +1 -1
- package/dist/lib/{shadow-claw-accounts-BgCkz3Zv.js → shadow-claw-accounts-Br3XdHro.js} +3 -3
- package/dist/lib/{shadow-claw-accounts-BgCkz3Zv.js.map → shadow-claw-accounts-Br3XdHro.js.map} +1 -1
- package/dist/lib/{shadow-claw-channel-config-CIm_Bxd2.js → shadow-claw-channel-config-Da2dN19M.js} +3 -3
- package/dist/lib/{shadow-claw-channel-config-CIm_Bxd2.js.map → shadow-claw-channel-config-Da2dN19M.js.map} +1 -1
- package/dist/lib/{shadow-claw-channels-CaceK52s.js → shadow-claw-channels-BofYHgz8.js} +3 -3
- package/dist/lib/{shadow-claw-channels-CaceK52s.js.map → shadow-claw-channels-BofYHgz8.js.map} +1 -1
- package/dist/lib/{shadow-claw-chat-BD18-mAR.js → shadow-claw-chat-Dw32DPV2.js} +12 -12
- package/dist/lib/{shadow-claw-chat-BD18-mAR.js.map → shadow-claw-chat-Dw32DPV2.js.map} +1 -1
- package/dist/lib/{shadow-claw-control-plane-U6LsfRPm.js → shadow-claw-control-plane-CwFotFDh.js} +7 -7
- package/dist/lib/{shadow-claw-control-plane-U6LsfRPm.js.map → shadow-claw-control-plane-CwFotFDh.js.map} +1 -1
- package/dist/lib/{shadow-claw-conversations-CDsza_oB.js → shadow-claw-conversations-0UXge23k.js} +5 -5
- package/dist/lib/{shadow-claw-conversations-CDsza_oB.js.map → shadow-claw-conversations-0UXge23k.js.map} +1 -1
- package/dist/lib/{shadow-claw-file-viewer-CMOnFmcP.js → shadow-claw-file-viewer-CAsn9NxH.js} +30 -30
- package/dist/lib/{shadow-claw-file-viewer-CMOnFmcP.js.map → shadow-claw-file-viewer-CAsn9NxH.js.map} +1 -1
- package/dist/lib/{shadow-claw-files-BI0P3m8a.js → shadow-claw-files-xGGhsHNH.js} +9 -9
- package/dist/lib/{shadow-claw-files-BI0P3m8a.js.map → shadow-claw-files-xGGhsHNH.js.map} +1 -1
- package/dist/lib/{shadow-claw-git-xRApLEMN.js → shadow-claw-git-B606hcMU.js} +6 -6
- package/dist/lib/{shadow-claw-git-xRApLEMN.js.map → shadow-claw-git-B606hcMU.js.map} +1 -1
- package/dist/lib/{shadow-claw-integrations-B2e1Nfg1.js → shadow-claw-integrations-Bwq5cba1.js} +4 -4
- package/dist/lib/{shadow-claw-integrations-B2e1Nfg1.js.map → shadow-claw-integrations-Bwq5cba1.js.map} +1 -1
- package/dist/lib/{shadow-claw-llm-Bc2mWuGb.js → shadow-claw-llm-BgpcMBIy.js} +6 -6
- package/dist/lib/{shadow-claw-llm-Bc2mWuGb.js.map → shadow-claw-llm-BgpcMBIy.js.map} +1 -1
- package/dist/lib/{shadow-claw-mcp-remote-D_Bh3aeC.js → shadow-claw-mcp-remote-DvQCA-Bk.js} +4 -4
- package/dist/lib/{shadow-claw-mcp-remote-D_Bh3aeC.js.map → shadow-claw-mcp-remote-DvQCA-Bk.js.map} +1 -1
- package/dist/lib/{shadow-claw-networking-F5W3n5ci.js → shadow-claw-networking-Cxvf9c7E.js} +2 -2
- package/dist/lib/{shadow-claw-networking-F5W3n5ci.js.map → shadow-claw-networking-Cxvf9c7E.js.map} +1 -1
- package/dist/lib/{shadow-claw-notifications-lvMt9WJ1.js → shadow-claw-notifications-CGVcFYBy.js} +3 -3
- package/dist/lib/{shadow-claw-notifications-lvMt9WJ1.js.map → shadow-claw-notifications-CGVcFYBy.js.map} +1 -1
- package/dist/lib/{shadow-claw-page-header-6gq6EMqX.js → shadow-claw-page-header-DAYz3I_o.js} +2 -2
- package/dist/lib/{shadow-claw-page-header-6gq6EMqX.js.map → shadow-claw-page-header-DAYz3I_o.js.map} +1 -1
- package/dist/lib/{shadow-claw-pages-Bo8_z2S-.js → shadow-claw-pages-BLeEZb9k.js} +24 -12
- package/dist/lib/shadow-claw-pages-BLeEZb9k.js.map +1 -0
- package/dist/lib/{shadow-claw-peerjs-DRTLCVDQ.js → shadow-claw-peerjs-BcFnbK74.js} +3 -3
- package/dist/lib/{shadow-claw-peerjs-DRTLCVDQ.js.map → shadow-claw-peerjs-BcFnbK74.js.map} +1 -1
- package/dist/lib/{shadow-claw-settings-D00B4p4L.js → shadow-claw-settings-B7o0Z4hu.js} +9 -9
- package/dist/lib/{shadow-claw-settings-D00B4p4L.js.map → shadow-claw-settings-B7o0Z4hu.js.map} +1 -1
- package/dist/lib/{shadow-claw-storage-DgMe7-Zo.js → shadow-claw-storage-afMVqB7E.js} +5 -5
- package/dist/lib/{shadow-claw-storage-DgMe7-Zo.js.map → shadow-claw-storage-afMVqB7E.js.map} +1 -1
- package/dist/lib/{shadow-claw-task-server-D103YOuI.js → shadow-claw-task-server-B9-MQjl6.js} +2 -2
- package/dist/lib/{shadow-claw-task-server-D103YOuI.js.map → shadow-claw-task-server-B9-MQjl6.js.map} +1 -1
- package/dist/lib/{shadow-claw-tasks-6OhALkq1.js → shadow-claw-tasks-Dc4BC-t-.js} +8 -8
- package/dist/lib/{shadow-claw-tasks-6OhALkq1.js.map → shadow-claw-tasks-Dc4BC-t-.js.map} +1 -1
- package/dist/lib/{shadow-claw-terminal-BGtxA1qD.js → shadow-claw-terminal-BINcTdsk.js} +2 -2
- package/dist/lib/{shadow-claw-terminal-BGtxA1qD.js.map → shadow-claw-terminal-BINcTdsk.js.map} +1 -1
- package/dist/lib/{shadow-claw-tools-OeIwAnnd.js → shadow-claw-tools-BjFgXNLR.js} +7 -7
- package/dist/lib/{shadow-claw-tools-OeIwAnnd.js.map → shadow-claw-tools-BjFgXNLR.js.map} +1 -1
- package/dist/lib/{shadow-claw-webvm-gVFZ197u.js → shadow-claw-webvm-Xe3ByMDw.js} +3 -3
- package/dist/lib/{shadow-claw-webvm-gVFZ197u.js.map → shadow-claw-webvm-Xe3ByMDw.js.map} +1 -1
- package/dist/lib/subsystems/mcp/webmcp.d.ts +64 -0
- package/dist/lib/{syncWebMcpRegistration-Bbph0XZl.js → syncWebMcpRegistration-D7alChz0.js} +21 -6
- package/dist/lib/syncWebMcpRegistration-D7alChz0.js.map +1 -0
- package/dist/lib/{tools-D0OlSzMD.js → tools-Cmhpk3X8.js} +4 -4
- package/dist/lib/{tools-D0OlSzMD.js.map → tools-Cmhpk3X8.js.map} +1 -1
- package/dist/lib/utils/index.js +1 -1
- package/dist/lib/utils/namespacedStorage.js +1 -1
- package/dist/lib/utils/utils.js +1 -1
- package/dist/lib/{utils-DYdrO2KI.js → utils-CzgKe_XZ.js} +4 -4
- package/dist/lib/{utils-DYdrO2KI.js.map → utils-CzgKe_XZ.js.map} +1 -1
- package/dist/lib/{webmcp-D2bocsgh.js → webmcp-oEP9YpR2.js} +119 -3
- package/dist/lib/{webmcp-D2bocsgh.js.map → webmcp-oEP9YpR2.js.map} +1 -1
- package/dist/public/.well-known/mcp/server-card.json +2 -2
- package/dist/public/.well-known/mcp/servers.json +1 -1
- package/dist/public/.well-known/mcp.json +2 -2
- package/dist/public/AGENTS.md +2 -1
- package/dist/public/README.md +1 -0
- package/dist/public/agent.worker.js +1 -1
- package/dist/public/app-routes-C6-H2zPH.js +1 -0
- package/dist/public/components/shadow-claw/shadow-claw.css +1 -1
- package/dist/public/components/shadow-claw/shadow-claw.html +1 -1
- package/dist/public/components/shadow-claw-pages/shadow-claw-pages.css +1 -1
- package/dist/public/{config-D4lVqMs8.js → config-Dewnqegf.js} +1 -1
- package/dist/public/{configurePeerJs-DDOAVSaH.js → configurePeerJs-CaPU2sN5.js} +1 -1
- package/dist/public/{connections-CiukjGiH.js → connections-CwE7jE9l.js} +1 -1
- package/dist/public/{custom-element-security-piU2U-Ec.js → custom-element-security-DLgHMf5R.js} +1 -1
- package/dist/public/docs/decisions/native-web-components-and-signals.md +1 -1
- package/dist/public/docs/example/article/index.html +5 -5
- package/dist/public/docs/guides/publishing-to-github-pages.md +49 -6
- package/dist/public/docs/publishing/index.html +5 -5
- package/dist/public/docs/skill-creator/index.html +5 -5
- package/dist/public/docs/subsystems/pages.md +10 -3
- package/dist/public/docs/subsystems/webmcp.md +89 -1
- package/dist/public/{downloadGroupFile-BiZQYY-O.js → downloadGroupFile-ChWHuap_.js} +1 -1
- package/dist/public/e2e/README.md +2 -2
- package/dist/public/{e2e-bridge-CybB810Q.js → e2e-bridge-BkaldjV2.js} +1 -1
- package/dist/public/{file-viewer-DLUBvOVD.js → file-viewer-Df4qUJCn.js} +1 -1
- package/dist/public/{getGroupDir-CLz5c8cg.js → getGroupDir-CeSr4fzS.js} +1 -1
- package/dist/public/{git-Ba8AeviI.js → git-Bscc1tK-.js} +2 -2
- package/dist/public/{iframe-sanitizer-CKOO7cxp.js → iframe-sanitizer-BVdkQsor.js} +1 -1
- package/dist/public/{iframe-storage-proxy-BvRiccH0.js → iframe-storage-proxy-Cg8aFw7o.js} +1 -1
- package/dist/public/index.css +1 -1
- package/dist/public/index.html +5 -5
- package/dist/public/index.js +1 -1
- package/dist/public/{initChatSplitResize-osJXM2gu.js → initChatSplitResize-C3Rp1LeZ.js} +1 -1
- package/dist/public/{initControlPlane-CNggzsRw.js → initControlPlane-C1pnzG16.js} +1 -1
- package/dist/public/llms.txt +1 -1
- package/dist/public/main/index.html +5 -5
- package/dist/public/main/memory/index.html +5 -5
- package/dist/public/{markdown-CZA-Ye2Q.js → markdown-D3A902nT.js} +1 -1
- package/dist/public/{mcp-reconnect-BK1bYBdC.js → mcp-reconnect-BJWxQBm_.js} +1 -1
- package/dist/public/{memoryStorage-Bew1X8Uy.js → memoryStorage-ykv-Ri-y.js} +1 -1
- package/dist/public/{orchestrator-D6tel2Zp.js → orchestrator-BVnC4Vtt.js} +2 -2
- package/dist/public/{orchestrator-BLkS_jpm.js → orchestrator-CQcc7OFA.js} +2 -2
- package/dist/public/{peerjs-CYs1F7sq.js → peerjs-CYC2cI1Y.js} +1 -1
- package/dist/public/{push-client-_HUyJlFJ.js → push-client-BJrnOULM.js} +1 -1
- package/dist/public/service-worker/fetch-proxy.js +1 -1
- package/dist/public/service-worker.js +1 -1
- package/dist/public/{shadow-claw-DmrxnX3Q.js → shadow-claw-WwfOu-tu.js} +33 -3
- package/dist/public/{shadow-claw-accounts-BqGrm47Z.js → shadow-claw-accounts-BFZxrPsH.js} +1 -1
- package/dist/public/{shadow-claw-channels-Ci2UPaaA.js → shadow-claw-channels-C0w0c1EF.js} +1 -1
- package/dist/public/{shadow-claw-chat-BxR6cBYQ.js → shadow-claw-chat-DIJ0A-QN.js} +1 -1
- package/dist/public/{shadow-claw-control-plane-C1A-CZ9H.js → shadow-claw-control-plane-DZoxUd-1.js} +2 -2
- package/dist/public/{shadow-claw-conversations-DcwG05-H.js → shadow-claw-conversations-DMyG_5zj.js} +1 -1
- package/dist/public/{shadow-claw-file-viewer-a_uRA30F.js → shadow-claw-file-viewer-DYgZjZb9.js} +2 -2
- package/dist/public/{shadow-claw-files-B774Xz1d.js → shadow-claw-files-DIOUdKJr.js} +1 -1
- package/dist/public/{shadow-claw-git-CTIPP0vs.js → shadow-claw-git-if5S0QJr.js} +2 -2
- package/dist/public/{shadow-claw-integrations-CjtkA6l0.js → shadow-claw-integrations-BQt4hcWe.js} +1 -1
- package/dist/public/{shadow-claw-llm-DfmITHde.js → shadow-claw-llm-9uf1KFA0.js} +2 -2
- package/dist/public/{shadow-claw-mcp-remote-BPuV22So.js → shadow-claw-mcp-remote-COpNIzJd.js} +1 -1
- package/dist/public/{shadow-claw-networking-DEMMygy0.js → shadow-claw-networking-AOVRUn6B.js} +1 -1
- package/dist/public/{shadow-claw-notifications-B9Xhcj1P.js → shadow-claw-notifications-CKB-W1TA.js} +1 -1
- package/dist/public/{shadow-claw-page-header-CAXokjQy.js → shadow-claw-page-header-D4LmUigA.js} +1 -1
- package/dist/public/{shadow-claw-pages-BRQw8bTm.js → shadow-claw-pages-BpHRY4vH.js} +12 -3
- package/dist/public/{shadow-claw-settings-BGrDNyqh.js → shadow-claw-settings-CE-aUo_J.js} +3 -3
- package/dist/public/{shadow-claw-storage-DK3pf11w.js → shadow-claw-storage-CFKC-Y3V.js} +1 -1
- package/dist/public/{shadow-claw-task-server-CMp5Vjza.js → shadow-claw-task-server-B_ZjqK4B.js} +1 -1
- package/dist/public/{shadow-claw-tasks-BO8aXM2B.js → shadow-claw-tasks-CGsEv48K.js} +1 -1
- package/dist/public/{shadow-claw-terminal-BSeTuyDa.js → shadow-claw-terminal-DcaS7cPH.js} +1 -1
- package/dist/public/{shadow-claw-tools-Bj6DLZ-2.js → shadow-claw-tools-Cnu9hKy0.js} +1 -1
- package/dist/public/{shadow-claw-webvm-M7d5RH8H.js → shadow-claw-webvm-BB8SFvJ8.js} +1 -1
- package/dist/public/syncWebMcpRegistration-D2Zx4oA1.js +1 -0
- package/dist/public/theme-init.js +1 -1
- package/dist/public/{tools-nysPKnGT.js → tools-sOLCHTj8.js} +1 -1
- package/dist/public/{webmcp-Bo3KTgRb.js → webmcp-YFt6vM1_.js} +1 -1
- package/dist/server.js +189 -189
- package/package.json +1 -1
- package/src/cli/cli.ts +2 -2
- package/src/cli/prerender/dsd-shell/prerender-dsd-shell.test.ts +4 -1
- package/src/cli/prerender/dsd-shell/prerender-dsd-shell.ts +7 -1
- package/src/cli/prerender/utils/insert-before-closing-head.test.ts +12 -0
- package/src/cli/prerender/utils/insert-before-closing-head.ts +4 -6
- package/src/cli/site-config/apply.test.ts +90 -0
- package/src/cli/site-config/apply.ts +181 -13
- package/src/components/shadow-claw/shadow-claw.css +26 -0
- package/src/components/shadow-claw/shadow-claw.html +4 -0
- package/src/components/shadow-claw/utils/applyRoute.ts +3 -0
- package/src/components/shadow-claw/utils/bindEventListeners.test.ts +6 -0
- package/src/components/shadow-claw/utils/bindEventListeners.ts +30 -1
- package/src/components/shadow-claw/utils/getRoute.test.ts +52 -0
- package/src/components/shadow-claw/utils/getRoute.ts +18 -0
- package/src/components/shadow-claw/utils/navigateToRoute.test.ts +21 -0
- package/src/components/shadow-claw/utils/navigateToRoute.ts +10 -0
- package/src/components/shadow-claw/utils/sidebarVisibility.ts +12 -4
- package/src/components/shadow-claw/utils/syncSlottedSidebarActiveLinks.test.ts +93 -0
- package/src/components/shadow-claw/utils/syncSlottedSidebarActiveLinks.ts +100 -0
- package/src/components/shadow-claw-file-viewer/shadow-claw-file-viewer.ts +31 -31
- package/src/components/shadow-claw-pages/shadow-claw-pages.css +10 -1
- package/src/components/shadow-claw-pages/utils/removePreviewIframe.ts +3 -0
- package/src/core/effect.test.ts +19 -0
- package/src/core/orchestrator/orchestrator-state.ts +2 -1
- package/src/core/orchestrator/orchestrator.test.ts +686 -5
- package/src/core/orchestrator/orchestrator.ts +3 -10
- package/src/core/orchestrator/utils/compactContext.test.ts +423 -121
- package/src/core/orchestrator/utils/configurePeerJs.test.ts +27 -0
- package/src/core/orchestrator/utils/deliverResponse.test.ts +127 -0
- package/src/core/orchestrator/utils/dispatchSubagentInvoke.test.ts +23 -0
- package/src/core/orchestrator/utils/enqueue.test.ts +520 -86
- package/src/core/orchestrator/utils/handleWorkerMessage.test.ts +664 -144
- package/src/core/orchestrator/utils/initTasks.node.test.ts +147 -0
- package/src/core/orchestrator/utils/initTasks.test.ts +926 -191
- package/src/core/orchestrator/utils/initTasks.ts +5 -4
- package/src/core/orchestrator/utils/invokeAgent.test.ts +563 -110
- package/src/core/orchestrator/utils/invokeAgent.ts +8 -10
- package/src/core/orchestrator/utils/loadChannelConfigurations.test.ts +50 -0
- package/src/core/orchestrator/utils/operations/channel.test.ts +377 -170
- package/src/core/orchestrator/utils/operations/provider.test.ts +663 -85
- package/src/core/orchestrator/utils/operations/room.test.ts +74 -11
- package/src/core/orchestrator/utils/operations/task.test.ts +452 -56
- package/src/core/orchestrator/utils/operations/task.ts +1 -1
- package/src/core/orchestrator/utils/operations/vm.test.ts +66 -7
- package/src/core/orchestrator/utils/parseDirectToolCommand.test.ts +6 -0
- package/src/core/orchestrator/utils/settings.test.ts +248 -81
- package/src/core/orchestrator/utils/setupPushTaskListener.test.ts +58 -0
- package/src/core/orchestrator/utils/syncWebMcpRegistration.test.ts +392 -64
- package/src/core/orchestrator/utils/syncWebMcpRegistration.ts +32 -3
- package/src/server/middleware/static-files.test.ts +36 -0
- package/src/server/middleware/static-files.ts +14 -6
- package/src/server/routes/mcp.ts +2 -2
- package/src/storage/staticRouting.test.ts +36 -0
- package/src/storage/staticRouting.ts +31 -3
- package/src/stores/file-viewer.test.ts +11 -0
- package/src/stores/theme.test.ts +41 -2
- package/src/stores/toast.test.ts +25 -0
- package/src/subsystems/mcp/webmcp.test.ts +255 -0
- package/src/subsystems/mcp/webmcp.ts +253 -0
- package/src/worker/tools/bash/native-bash-executor.test.ts +31 -1
- package/src/worker/tools/git/utils/extractConflictPaths.test.ts +24 -0
- package/src/worker/tools/git/utils/parseConflictRegions.test.ts +46 -0
- package/src/worker/tools/git/utils/resolveCorsProxy.test.ts +26 -5
- package/src/worker/tools/git/utils/truncateSnippet.test.ts +16 -0
- package/src/worker/tools/node-transformers-executor.test.ts +105 -6
- package/src/worker/tools/remote-mcp/remote-mcp.test.ts +172 -47
- package/src/worker/tools/remote-mcp/utils/formatListToolsOutput.test.ts +19 -0
- package/src/worker/tools/remote-mcp/utils/isMcpReauthError.test.ts +45 -0
- package/src/worker/tools/remote-mcp/utils/resolveMcpReauth.test.ts +48 -0
- package/src/worker/tools/rooms/list-room-members.test.ts +24 -10
- package/src/worker/tools/tasks/list-tasks.test.ts +39 -8
- package/src/worker/tools/tasks/update-task.test.ts +78 -14
- package/src/worker/tools/ui/javascript.test.ts +70 -17
- package/src/worker/tools/ui/list-tool-profiles.test.ts +31 -9
- package/dist/cli/chunk-00GAzVPl2.js +0 -1
- package/dist/cli/chunk-2hnFoNNC.js +0 -1
- package/dist/cli/chunk-BKQL4nzF2.js +0 -1
- package/dist/cli/chunk-BQ4K3fqj2.js +0 -1
- package/dist/cli/chunk-BVCGo6uf2.js +0 -1
- package/dist/cli/chunk-BcTH_-FL.js +0 -1
- package/dist/cli/chunk-BmD_sPRt.js +0 -1
- package/dist/cli/chunk-BsxNqqer.js +0 -1
- package/dist/cli/chunk-CCcF5T-c.js +0 -1
- package/dist/cli/chunk-CFhvwUeL.js +0 -91
- package/dist/cli/chunk-CwWtvULn.js +0 -1
- package/dist/cli/chunk-Drf-hXIR2.js +0 -1
- package/dist/cli/chunk-Dt5HNYVi.js +0 -1
- package/dist/cli/chunk-DwZQi9Ax.js +0 -1
- package/dist/cli/chunk-m9ZX7TyF2.js +0 -1
- package/dist/lib/app-routes-BAEMLu9g.js.map +0 -1
- package/dist/lib/orchestrator-Bq9ft3uG.js.map +0 -1
- package/dist/lib/shadow-claw-MumteAJy.js.map +0 -1
- package/dist/lib/shadow-claw-pages-Bo8_z2S-.js.map +0 -1
- package/dist/lib/syncWebMcpRegistration-Bbph0XZl.js.map +0 -1
- package/dist/public/app-routes-BMQ4HFaM.js +0 -1
- package/dist/public/syncWebMcpRegistration-4bT0YUyZ.js +0 -1
|
@@ -5,6 +5,8 @@ import {
|
|
|
5
5
|
setWebMcpMode as applyWebMcpMode,
|
|
6
6
|
registerWebMcpTools,
|
|
7
7
|
unregisterWebMcpTools,
|
|
8
|
+
onWebMcpToolActivated,
|
|
9
|
+
onWebMcpToolCancel,
|
|
8
10
|
} from "../../../subsystems/mcp/webmcp.js";
|
|
9
11
|
|
|
10
12
|
import { effect } from "../../effect.js";
|
|
@@ -14,6 +16,8 @@ import { CONFIG_KEYS } from "../../../config/config.js";
|
|
|
14
16
|
import { setConfig } from "../../../db/setConfig.js";
|
|
15
17
|
|
|
16
18
|
import type { ShadowClawDatabase } from "../../../db/db.js";
|
|
19
|
+
import type { DeclarativeToolDefinition } from "../../../subsystems/tools/declarative.js";
|
|
20
|
+
import type { SkillRecord } from "../../../subsystems/skills/types.js";
|
|
17
21
|
import type { WebMcpMode } from "../../../subsystems/mcp/webmcp.js";
|
|
18
22
|
import type { OrchestratorState } from "../orchestrator-state.js";
|
|
19
23
|
import type { Orchestrator } from "../orchestrator.js";
|
|
@@ -36,6 +40,19 @@ export function syncWebMcpRegistration(
|
|
|
36
40
|
return;
|
|
37
41
|
}
|
|
38
42
|
|
|
43
|
+
// Subscribe to WebMCP lifecycle events (PR #245 / Chrome 156+ with backwards compatibility).
|
|
44
|
+
const unsubscribeActivated = onWebMcpToolActivated((event) => {
|
|
45
|
+
orchestrator.events?.emit("webmcp-tool-activated", {
|
|
46
|
+
toolName: event.toolName,
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const unsubscribeCancel = onWebMcpToolCancel((event) => {
|
|
51
|
+
orchestrator.events?.emit("webmcp-tool-cancel", {
|
|
52
|
+
toolName: event.toolName,
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
39
56
|
// Register WebMCP tools and re-register when tool config changes.
|
|
40
57
|
// This effect runs once immediately to perform the initial registration.
|
|
41
58
|
// We intentionally do NOT call isWebMcpSupported() here — that accesses
|
|
@@ -43,7 +60,7 @@ export function syncWebMcpRegistration(
|
|
|
43
60
|
// early-preview renderer. Instead, registerWebMcpTools handles feature detection
|
|
44
61
|
// internally and skips modelContext access entirely when 0 tools are
|
|
45
62
|
// passed.
|
|
46
|
-
|
|
63
|
+
const disposeEffect = effect(() => {
|
|
47
64
|
// Access signals to establish tracking.
|
|
48
65
|
const activeGroupId = orchestratorStore.activeGroupId;
|
|
49
66
|
const allTools = toolsStore.allTools;
|
|
@@ -60,7 +77,7 @@ export function syncWebMcpRegistration(
|
|
|
60
77
|
// Small delay to allow the browser's ModelContext to process the unregistrations.
|
|
61
78
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
62
79
|
|
|
63
|
-
let declarativeTools:
|
|
80
|
+
let declarativeTools: DeclarativeToolDefinition[] = [];
|
|
64
81
|
try {
|
|
65
82
|
declarativeTools = await toolsStore.refreshDeclarativeTools(
|
|
66
83
|
db,
|
|
@@ -80,7 +97,7 @@ export function syncWebMcpRegistration(
|
|
|
80
97
|
? candidateTools.filter((t) => group.toolTags!.includes(t.name))
|
|
81
98
|
: [...globalTools, ...enabledDeclarativeTools];
|
|
82
99
|
|
|
83
|
-
let skillDiscovery: { skills:
|
|
100
|
+
let skillDiscovery: { skills: SkillRecord[] } = { skills: [] };
|
|
84
101
|
try {
|
|
85
102
|
skillDiscovery = await discoverSkills(db, activeGroupId);
|
|
86
103
|
} catch {
|
|
@@ -107,6 +124,18 @@ export function syncWebMcpRegistration(
|
|
|
107
124
|
console.error("WebMCP registration failed:", err);
|
|
108
125
|
});
|
|
109
126
|
});
|
|
127
|
+
|
|
128
|
+
orchestrator.webMcpEffectCleanup = () => {
|
|
129
|
+
try {
|
|
130
|
+
unsubscribeActivated();
|
|
131
|
+
} catch {}
|
|
132
|
+
try {
|
|
133
|
+
unsubscribeCancel();
|
|
134
|
+
} catch {}
|
|
135
|
+
if (typeof disposeEffect === "function") {
|
|
136
|
+
disposeEffect();
|
|
137
|
+
}
|
|
138
|
+
};
|
|
110
139
|
}
|
|
111
140
|
export async function setWebMcpMode(
|
|
112
141
|
_state: Pick<
|
|
@@ -175,6 +175,42 @@ describe("static-files-middleware", () => {
|
|
|
175
175
|
}
|
|
176
176
|
});
|
|
177
177
|
|
|
178
|
+
it("serves SPA shell for app routes even when route directory exists on disk", async () => {
|
|
179
|
+
await register();
|
|
180
|
+
const middleware = app.use.mock.calls[1][0];
|
|
181
|
+
|
|
182
|
+
for (const appPath of ["/files", "/files/", "/pages", "/pages/"]) {
|
|
183
|
+
const req = {
|
|
184
|
+
method: "GET",
|
|
185
|
+
headers: {
|
|
186
|
+
accept: "text/html",
|
|
187
|
+
"sec-fetch-dest": "document",
|
|
188
|
+
"sec-fetch-mode": "navigate",
|
|
189
|
+
},
|
|
190
|
+
originalUrl: appPath,
|
|
191
|
+
url: appPath,
|
|
192
|
+
};
|
|
193
|
+
const res = { setHeader: jest.fn(), sendFile: jest.fn() };
|
|
194
|
+
const next = jest.fn();
|
|
195
|
+
|
|
196
|
+
fsMock.stat.mockImplementation((_targetPath: string, cb: any) => {
|
|
197
|
+
cb(null, { isFile: () => false, isDirectory: () => true });
|
|
198
|
+
});
|
|
199
|
+
fsMock.access.mockImplementation(
|
|
200
|
+
(_targetPath: string, _mode: any, cb: any) => {
|
|
201
|
+
cb(new Error("ENOENT"));
|
|
202
|
+
},
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
middleware(req, res, next);
|
|
206
|
+
|
|
207
|
+
expect(res.sendFile).toHaveBeenCalledWith(
|
|
208
|
+
expect.stringContaining("/root/index.html"),
|
|
209
|
+
);
|
|
210
|
+
expect(next).not.toHaveBeenCalled();
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
|
|
178
214
|
it("does not serve SPA shell for non-HTML missing requests", async () => {
|
|
179
215
|
await register();
|
|
180
216
|
const middleware = app.use.mock.calls[1][0];
|
|
@@ -112,15 +112,18 @@ export function registerStaticFilesMiddleware(app: Express, rootPath: string) {
|
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
const urlPathname = new URL(req.url || "/", "http://localhost").pathname;
|
|
115
|
+
if (
|
|
116
|
+
isSpaShellRequest(req, requestPath) ||
|
|
117
|
+
isSpaShellRequest(req, urlPathname)
|
|
118
|
+
) {
|
|
119
|
+
res.sendFile(path.join(rootPath, "index.html"));
|
|
120
|
+
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
115
124
|
const filePath = path.join(rootPath, urlPathname);
|
|
116
125
|
fs.stat(filePath, (err, stats) => {
|
|
117
126
|
if (err || !stats) {
|
|
118
|
-
if (isSpaShellRequest(req, requestPath)) {
|
|
119
|
-
res.sendFile(path.join(rootPath, "index.html"));
|
|
120
|
-
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
127
|
// Check if request has a subpath prefix matching a root file (e.g. /shadow-claw/index.js -> /index.js)
|
|
125
128
|
const strippedSegmentPath = requestPath.replace(/^\/[^/]+/, "");
|
|
126
129
|
if (strippedSegmentPath) {
|
|
@@ -212,6 +215,11 @@ export function registerStaticFilesMiddleware(app: Express, rootPath: string) {
|
|
|
212
215
|
} else {
|
|
213
216
|
res.sendFile(indexPath);
|
|
214
217
|
}
|
|
218
|
+
} else if (
|
|
219
|
+
isSpaShellRequest(req, requestPath) ||
|
|
220
|
+
isSpaShellRequest(req, urlPathname)
|
|
221
|
+
) {
|
|
222
|
+
res.sendFile(path.join(rootPath, "index.html"));
|
|
215
223
|
} else {
|
|
216
224
|
next();
|
|
217
225
|
}
|
package/src/server/routes/mcp.ts
CHANGED
|
@@ -256,7 +256,7 @@ export function registerMcpRoutes(
|
|
|
256
256
|
name: "shadow-claw",
|
|
257
257
|
version: getPackageVersion(),
|
|
258
258
|
description:
|
|
259
|
-
"
|
|
259
|
+
"Multi-runtime AI assistant with local and remote LLM orchestration, Web Workers, OPFS storage, and agentic tool execution.",
|
|
260
260
|
title: "ShadowClaw MCP Server",
|
|
261
261
|
websiteUrl: `${origin}/shadow-claw/`,
|
|
262
262
|
repository: {
|
|
@@ -332,7 +332,7 @@ export function registerMcpRoutes(
|
|
|
332
332
|
servers: [
|
|
333
333
|
{
|
|
334
334
|
name: "shadow-claw",
|
|
335
|
-
description: "ShadowClaw
|
|
335
|
+
description: "ShadowClaw Multi-Runtime AI Assistant MCP Server",
|
|
336
336
|
endpoint: `${origin}/mcp`,
|
|
337
337
|
capabilities: ["tools", "extensions"],
|
|
338
338
|
card: `${origin}/.well-known/mcp.json`,
|
|
@@ -73,12 +73,29 @@ describe("staticRouting", () => {
|
|
|
73
73
|
"/pages/main/posts/2026-07-01_03-37-38.md": {
|
|
74
74
|
prettyPath: "/2026/06/30/on-developing-loops/",
|
|
75
75
|
},
|
|
76
|
+
"/pages/main/index.html": {
|
|
77
|
+
prettyPath: "/",
|
|
78
|
+
},
|
|
76
79
|
"/pages/main/about.md": {
|
|
77
80
|
prettyPath: "/about",
|
|
78
81
|
},
|
|
79
82
|
},
|
|
80
83
|
};
|
|
81
84
|
|
|
85
|
+
// Test root path "/"
|
|
86
|
+
expect(resolvePrettyPathToRoute("/", manifest)).toEqual({
|
|
87
|
+
page: "pages",
|
|
88
|
+
groupId: "br:main",
|
|
89
|
+
path: "index.html",
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// Test root index.html
|
|
93
|
+
expect(resolvePrettyPathToRoute("/index.html", manifest)).toEqual({
|
|
94
|
+
page: "pages",
|
|
95
|
+
groupId: "br:main",
|
|
96
|
+
path: "index.html",
|
|
97
|
+
});
|
|
98
|
+
|
|
82
99
|
// Test with explicit manifest
|
|
83
100
|
expect(
|
|
84
101
|
resolvePrettyPathToRoute("/2026/06/30/on-developing-loops/", manifest),
|
|
@@ -155,6 +172,25 @@ describe("staticRouting", () => {
|
|
|
155
172
|
manifest,
|
|
156
173
|
),
|
|
157
174
|
).toBeNull();
|
|
175
|
+
|
|
176
|
+
const manifestWithRoot: StaticRoutesManifest = {
|
|
177
|
+
routes: {
|
|
178
|
+
"/pages/main/index.html": {
|
|
179
|
+
prettyPath: "/",
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
expect(
|
|
185
|
+
resolveRouteToPrettyPath(
|
|
186
|
+
{ page: "pages", groupId: "br:main" },
|
|
187
|
+
manifestWithRoot,
|
|
188
|
+
),
|
|
189
|
+
).toBe("/");
|
|
190
|
+
|
|
191
|
+
expect(resolveRouteToPrettyPath({ page: "pages" }, manifestWithRoot)).toBe(
|
|
192
|
+
"/",
|
|
193
|
+
);
|
|
158
194
|
});
|
|
159
195
|
|
|
160
196
|
it("fetches static routing manifest when not embedded in DOM", async () => {
|
|
@@ -214,9 +214,6 @@ export function resolvePrettyPathToRoute(
|
|
|
214
214
|
}
|
|
215
215
|
|
|
216
216
|
const normalizedPath = normalizePrettyPathKey(pathname);
|
|
217
|
-
if (!normalizedPath) {
|
|
218
|
-
return null;
|
|
219
|
-
}
|
|
220
217
|
|
|
221
218
|
for (const [canonicalKey, routeDef] of Object.entries(
|
|
222
219
|
routesManifest.routes,
|
|
@@ -363,6 +360,29 @@ export function resolveRouteToPrettyPath(
|
|
|
363
360
|
candidateKeys.push(`pages/${normalizedGroupId}/${cleanPath}`);
|
|
364
361
|
candidateKeys.push(`/pages/${groupId}/${cleanPath}`);
|
|
365
362
|
}
|
|
363
|
+
} else if (page === "pages" && !path) {
|
|
364
|
+
if (normalizedGroupId === "main" || !normalizedGroupId) {
|
|
365
|
+
candidateKeys.push(
|
|
366
|
+
"/pages/main/index.html",
|
|
367
|
+
"pages/main/index.html",
|
|
368
|
+
"/pages/br:main/index.html",
|
|
369
|
+
"/pages/br-main/index.html",
|
|
370
|
+
"/main/index.html",
|
|
371
|
+
"main/index.html",
|
|
372
|
+
"/index.html",
|
|
373
|
+
"index.html",
|
|
374
|
+
"/pages/main",
|
|
375
|
+
"pages/main",
|
|
376
|
+
"/pages",
|
|
377
|
+
"pages",
|
|
378
|
+
);
|
|
379
|
+
} else if (normalizedGroupId) {
|
|
380
|
+
candidateKeys.push(
|
|
381
|
+
`/pages/${normalizedGroupId}/index.html`,
|
|
382
|
+
`pages/${normalizedGroupId}/index.html`,
|
|
383
|
+
`/pages/${groupId}/index.html`,
|
|
384
|
+
);
|
|
385
|
+
}
|
|
366
386
|
} else if (page === "files" && path) {
|
|
367
387
|
const cleanPath = trimSlashes(path);
|
|
368
388
|
if (normalizedGroupId) {
|
|
@@ -386,5 +406,13 @@ export function resolveRouteToPrettyPath(
|
|
|
386
406
|
}
|
|
387
407
|
}
|
|
388
408
|
|
|
409
|
+
if (page === "pages" && !path) {
|
|
410
|
+
for (const routeDef of Object.values(routesManifest.routes)) {
|
|
411
|
+
if (routeDef && routeDef.prettyPath === "/") {
|
|
412
|
+
return "/";
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
389
417
|
return null;
|
|
390
418
|
}
|
|
@@ -155,4 +155,15 @@ describe("FileViewerStore", () => {
|
|
|
155
155
|
mimeType: "text/plain",
|
|
156
156
|
});
|
|
157
157
|
});
|
|
158
|
+
|
|
159
|
+
it("handles empty path and extension-less filenames", async () => {
|
|
160
|
+
(readGroupFile as any).mockResolvedValue("empty-content");
|
|
161
|
+
const s = new FileViewerStore();
|
|
162
|
+
|
|
163
|
+
expect(s.getPreviewBinaryMimeType("")).toBe("");
|
|
164
|
+
expect(s.getPreviewBinaryMimeType("noextension")).toBe("");
|
|
165
|
+
|
|
166
|
+
await s.openFile({} as any, "");
|
|
167
|
+
expect(s.file?.name).toBe("");
|
|
168
|
+
});
|
|
158
169
|
});
|
package/src/stores/theme.test.ts
CHANGED
|
@@ -11,8 +11,8 @@ describe("themeStore", () => {
|
|
|
11
11
|
localStorage.clear();
|
|
12
12
|
document.documentElement.className = "";
|
|
13
13
|
|
|
14
|
-
//
|
|
15
|
-
(window
|
|
14
|
+
// Spy on dispatchEvent so event listeners still fire
|
|
15
|
+
jest.spyOn(window, "dispatchEvent");
|
|
16
16
|
});
|
|
17
17
|
|
|
18
18
|
it("applies and persists explicit theme", async () => {
|
|
@@ -135,4 +135,43 @@ describe("themeStore", () => {
|
|
|
135
135
|
expect(themeStore.resolved).toBe("light");
|
|
136
136
|
expect(localStorage.getItem("shadow-claw-theme")).toBe("system");
|
|
137
137
|
});
|
|
138
|
+
|
|
139
|
+
it("handles storage event for tab synchronization", async () => {
|
|
140
|
+
const { getNamespacedStorageKey } =
|
|
141
|
+
await import("../utils/namespacedStorage.js");
|
|
142
|
+
const { themeStore } = await import("./theme.js");
|
|
143
|
+
const targetKey = getNamespacedStorageKey("shadow-claw-theme");
|
|
144
|
+
|
|
145
|
+
// Other key should be ignored
|
|
146
|
+
const otherEv = new Event("storage") as any;
|
|
147
|
+
otherEv.key = "other-key";
|
|
148
|
+
otherEv.newValue = "dark";
|
|
149
|
+
window.dispatchEvent(otherEv);
|
|
150
|
+
expect(themeStore.theme).not.toBe("dark");
|
|
151
|
+
|
|
152
|
+
// Target key with null newValue should be ignored
|
|
153
|
+
const nullEv = new Event("storage") as any;
|
|
154
|
+
nullEv.key = targetKey;
|
|
155
|
+
nullEv.newValue = null;
|
|
156
|
+
window.dispatchEvent(nullEv);
|
|
157
|
+
|
|
158
|
+
// Target key with valid newValue updates theme
|
|
159
|
+
const validEv = new Event("storage") as any;
|
|
160
|
+
validEv.key = targetKey;
|
|
161
|
+
validEv.newValue = "dark";
|
|
162
|
+
window.dispatchEvent(validEv);
|
|
163
|
+
|
|
164
|
+
expect(themeStore.theme).toBe("dark");
|
|
165
|
+
expect(themeStore.resolved).toBe("dark");
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it("falls back gracefully when matchMedia is not a function", async () => {
|
|
169
|
+
(window as any).matchMedia = undefined;
|
|
170
|
+
jest.resetModules();
|
|
171
|
+
localStorage.clear();
|
|
172
|
+
const { themeStore } = await import("./theme.js");
|
|
173
|
+
|
|
174
|
+
themeStore.setTheme("system");
|
|
175
|
+
expect(themeStore.resolved).toBe("light");
|
|
176
|
+
});
|
|
138
177
|
});
|
package/src/stores/toast.test.ts
CHANGED
|
@@ -101,6 +101,31 @@ describe("ToastStore", () => {
|
|
|
101
101
|
|
|
102
102
|
// No-op for non-existent or action-less toast
|
|
103
103
|
await store.runAction(999);
|
|
104
|
+
|
|
105
|
+
const noActionId = store.show("without action");
|
|
106
|
+
await store.runAction(noActionId);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test("pause and resume handle missing or invalid timers gracefully", () => {
|
|
110
|
+
const store = new ToastStore();
|
|
111
|
+
|
|
112
|
+
// Calling pause on nonexistent toast or already paused toast
|
|
113
|
+
expect(() => store.pause(999)).not.toThrow();
|
|
114
|
+
|
|
115
|
+
const id = store.show("toast", { duration: 1000 });
|
|
116
|
+
store.pause(id);
|
|
117
|
+
// Pause again when timeoutId is undefined
|
|
118
|
+
expect(() => store.pause(id)).not.toThrow();
|
|
119
|
+
|
|
120
|
+
// Calling resume on nonexistent toast, or already running toast, or expired toast
|
|
121
|
+
expect(() => store.resume(999)).not.toThrow();
|
|
122
|
+
|
|
123
|
+
const runningId = store.show("running", { duration: 1000 });
|
|
124
|
+
// Resume when timer.timeoutId is still active
|
|
125
|
+
expect(() => store.resume(runningId)).not.toThrow();
|
|
126
|
+
|
|
127
|
+
// Clear timer on nonexistent toast
|
|
128
|
+
expect(() => store.clearTimer(999)).not.toThrow();
|
|
104
129
|
});
|
|
105
130
|
|
|
106
131
|
test("resolvers handle invalid inputs gracefully", () => {
|
|
@@ -817,4 +817,259 @@ describe("webmcp integration", () => {
|
|
|
817
817
|
errorSpy.mockRestore();
|
|
818
818
|
});
|
|
819
819
|
});
|
|
820
|
+
|
|
821
|
+
describe("WebMCP lifecycle events (PR #245 / Chrome 156+)", () => {
|
|
822
|
+
let originalWindowOntoolactivated: any;
|
|
823
|
+
let originalWindowOntoolcancel: any;
|
|
824
|
+
|
|
825
|
+
beforeEach(() => {
|
|
826
|
+
originalWindowOntoolactivated = (globalThis as any).window
|
|
827
|
+
?.ontoolactivated;
|
|
828
|
+
originalWindowOntoolcancel = (globalThis as any).window?.ontoolcancel;
|
|
829
|
+
if (typeof window !== "undefined") {
|
|
830
|
+
delete (window as any).ontoolactivated;
|
|
831
|
+
delete (window as any).ontoolcancel;
|
|
832
|
+
}
|
|
833
|
+
});
|
|
834
|
+
|
|
835
|
+
afterEach(() => {
|
|
836
|
+
if (typeof window !== "undefined") {
|
|
837
|
+
if (originalWindowOntoolactivated !== undefined) {
|
|
838
|
+
(window as any).ontoolactivated = originalWindowOntoolactivated;
|
|
839
|
+
} else {
|
|
840
|
+
delete (window as any).ontoolactivated;
|
|
841
|
+
}
|
|
842
|
+
if (originalWindowOntoolcancel !== undefined) {
|
|
843
|
+
(window as any).ontoolcancel = originalWindowOntoolcancel;
|
|
844
|
+
} else {
|
|
845
|
+
delete (window as any).ontoolcancel;
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
});
|
|
849
|
+
|
|
850
|
+
describe("getWebMcpEventTarget", () => {
|
|
851
|
+
it("resolves to document.modelContext when ontoolactivated is in document.modelContext (Chrome 156+ / PR #245)", async () => {
|
|
852
|
+
const { getWebMcpEventTarget } = await import("./webmcp.js");
|
|
853
|
+
|
|
854
|
+
const mockTarget = new EventTarget();
|
|
855
|
+
(mockTarget as any).ontoolactivated = null;
|
|
856
|
+
(mockTarget as any).ontoolcancel = null;
|
|
857
|
+
|
|
858
|
+
Object.defineProperty((globalThis as any).document, "modelContext", {
|
|
859
|
+
configurable: true,
|
|
860
|
+
value: mockTarget,
|
|
861
|
+
});
|
|
862
|
+
|
|
863
|
+
// Also define ontoolactivated on window to ensure modelContext takes precedence
|
|
864
|
+
if (typeof window !== "undefined") {
|
|
865
|
+
(window as any).ontoolactivated = null;
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
expect(getWebMcpEventTarget("toolactivated")).toBe(mockTarget);
|
|
869
|
+
expect(getWebMcpEventTarget("toolcancel")).toBe(mockTarget);
|
|
870
|
+
});
|
|
871
|
+
|
|
872
|
+
it("falls back to window when ontoolactivated is only present on window (legacy Chrome < 156)", async () => {
|
|
873
|
+
const { getWebMcpEventTarget } = await import("./webmcp.js");
|
|
874
|
+
|
|
875
|
+
const mockModelContext = {
|
|
876
|
+
registerTool: jest.fn(),
|
|
877
|
+
// No ontoolactivated or ontoolcancel
|
|
878
|
+
};
|
|
879
|
+
|
|
880
|
+
Object.defineProperty((globalThis as any).document, "modelContext", {
|
|
881
|
+
configurable: true,
|
|
882
|
+
value: mockModelContext,
|
|
883
|
+
});
|
|
884
|
+
|
|
885
|
+
if (typeof window !== "undefined") {
|
|
886
|
+
(window as any).ontoolactivated = null;
|
|
887
|
+
(window as any).ontoolcancel = null;
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
expect(getWebMcpEventTarget("toolactivated")).toBe(window);
|
|
891
|
+
expect(getWebMcpEventTarget("toolcancel")).toBe(window);
|
|
892
|
+
});
|
|
893
|
+
|
|
894
|
+
it("resolves to navigator.modelContext if document.modelContext is absent (Chrome < 152)", async () => {
|
|
895
|
+
const { getWebMcpEventTarget } = await import("./webmcp.js");
|
|
896
|
+
|
|
897
|
+
delete ((globalThis as any).document as any).modelContext;
|
|
898
|
+
|
|
899
|
+
const navTarget = new EventTarget();
|
|
900
|
+
(navTarget as any).ontoolactivated = null;
|
|
901
|
+
|
|
902
|
+
Object.defineProperty((globalThis as any).navigator, "modelContext", {
|
|
903
|
+
configurable: true,
|
|
904
|
+
value: navTarget,
|
|
905
|
+
});
|
|
906
|
+
|
|
907
|
+
expect(getWebMcpEventTarget("toolactivated")).toBe(navTarget);
|
|
908
|
+
|
|
909
|
+
delete ((globalThis as any).navigator as any).modelContext;
|
|
910
|
+
});
|
|
911
|
+
|
|
912
|
+
it("prefers modelContext when neither target has on{type} property but modelContext is an EventTarget", async () => {
|
|
913
|
+
const { getWebMcpEventTarget } = await import("./webmcp.js");
|
|
914
|
+
|
|
915
|
+
const mockTarget = new EventTarget();
|
|
916
|
+
Object.defineProperty((globalThis as any).document, "modelContext", {
|
|
917
|
+
configurable: true,
|
|
918
|
+
value: mockTarget,
|
|
919
|
+
});
|
|
920
|
+
|
|
921
|
+
expect(getWebMcpEventTarget("toolactivated")).toBe(mockTarget);
|
|
922
|
+
expect(getWebMcpEventTarget()).toBe(mockTarget);
|
|
923
|
+
});
|
|
924
|
+
|
|
925
|
+
it("falls back to window if modelContext lacks addEventListener", async () => {
|
|
926
|
+
const { getWebMcpEventTarget } = await import("./webmcp.js");
|
|
927
|
+
|
|
928
|
+
Object.defineProperty((globalThis as any).document, "modelContext", {
|
|
929
|
+
configurable: true,
|
|
930
|
+
value: {
|
|
931
|
+
// Not an EventTarget
|
|
932
|
+
registerTool: jest.fn(),
|
|
933
|
+
},
|
|
934
|
+
});
|
|
935
|
+
|
|
936
|
+
expect(getWebMcpEventTarget("toolactivated")).toBe(window);
|
|
937
|
+
});
|
|
938
|
+
});
|
|
939
|
+
|
|
940
|
+
describe("addWebMcpEventListener and convenience helpers", () => {
|
|
941
|
+
it("listens to toolactivated on document.modelContext in Chrome 156+", async () => {
|
|
942
|
+
const { addWebMcpEventListener } = await import("./webmcp.js");
|
|
943
|
+
|
|
944
|
+
const mockTarget = new EventTarget();
|
|
945
|
+
(mockTarget as any).ontoolactivated = null;
|
|
946
|
+
|
|
947
|
+
Object.defineProperty((globalThis as any).document, "modelContext", {
|
|
948
|
+
configurable: true,
|
|
949
|
+
value: mockTarget,
|
|
950
|
+
});
|
|
951
|
+
|
|
952
|
+
const listener = jest.fn();
|
|
953
|
+
const unsubscribe = addWebMcpEventListener("toolactivated", listener);
|
|
954
|
+
|
|
955
|
+
const event = new CustomEvent("toolactivated");
|
|
956
|
+
(event as any).toolName = "read_file";
|
|
957
|
+
mockTarget.dispatchEvent(event);
|
|
958
|
+
|
|
959
|
+
expect(listener).toHaveBeenCalledTimes(1);
|
|
960
|
+
expect(listener).toHaveBeenCalledWith(
|
|
961
|
+
expect.objectContaining({ toolName: "read_file" }),
|
|
962
|
+
);
|
|
963
|
+
|
|
964
|
+
// Unsubscribe should remove the listener
|
|
965
|
+
unsubscribe();
|
|
966
|
+
mockTarget.dispatchEvent(event);
|
|
967
|
+
expect(listener).toHaveBeenCalledTimes(1);
|
|
968
|
+
});
|
|
969
|
+
|
|
970
|
+
it("normalizes toolName from detail.toolName if event.toolName is not directly set", async () => {
|
|
971
|
+
const { onWebMcpToolActivated } = await import("./webmcp.js");
|
|
972
|
+
|
|
973
|
+
const mockTarget = new EventTarget();
|
|
974
|
+
(mockTarget as any).ontoolactivated = null;
|
|
975
|
+
|
|
976
|
+
Object.defineProperty((globalThis as any).document, "modelContext", {
|
|
977
|
+
configurable: true,
|
|
978
|
+
value: mockTarget,
|
|
979
|
+
});
|
|
980
|
+
|
|
981
|
+
const listener = jest.fn();
|
|
982
|
+
const unsubscribe = onWebMcpToolActivated(listener);
|
|
983
|
+
|
|
984
|
+
const legacyEvent = new CustomEvent("toolactivated", {
|
|
985
|
+
detail: { toolName: "bash" },
|
|
986
|
+
});
|
|
987
|
+
mockTarget.dispatchEvent(legacyEvent);
|
|
988
|
+
|
|
989
|
+
expect(listener).toHaveBeenCalledTimes(1);
|
|
990
|
+
expect(listener).toHaveBeenCalledWith(
|
|
991
|
+
expect.objectContaining({ toolName: "bash" }),
|
|
992
|
+
);
|
|
993
|
+
|
|
994
|
+
unsubscribe();
|
|
995
|
+
});
|
|
996
|
+
|
|
997
|
+
it("listens to toolcancel on window in legacy Chrome < 156", async () => {
|
|
998
|
+
const { onWebMcpToolCancel } = await import("./webmcp.js");
|
|
999
|
+
|
|
1000
|
+
delete ((globalThis as any).document as any).modelContext;
|
|
1001
|
+
(window as any).ontoolcancel = null;
|
|
1002
|
+
|
|
1003
|
+
const listener = jest.fn();
|
|
1004
|
+
const unsubscribe = onWebMcpToolCancel(listener);
|
|
1005
|
+
|
|
1006
|
+
const event = new CustomEvent("toolcancel");
|
|
1007
|
+
(event as any).toolName = "fetch_url";
|
|
1008
|
+
window.dispatchEvent(event);
|
|
1009
|
+
|
|
1010
|
+
expect(listener).toHaveBeenCalledTimes(1);
|
|
1011
|
+
expect(listener).toHaveBeenCalledWith(
|
|
1012
|
+
expect.objectContaining({ toolName: "fetch_url" }),
|
|
1013
|
+
);
|
|
1014
|
+
|
|
1015
|
+
unsubscribe();
|
|
1016
|
+
window.dispatchEvent(event);
|
|
1017
|
+
expect(listener).toHaveBeenCalledTimes(1);
|
|
1018
|
+
});
|
|
1019
|
+
|
|
1020
|
+
it("automatically cleans up when AbortSignal is aborted", async () => {
|
|
1021
|
+
const { onWebMcpToolActivated } = await import("./webmcp.js");
|
|
1022
|
+
|
|
1023
|
+
const mockTarget = new EventTarget();
|
|
1024
|
+
(mockTarget as any).ontoolactivated = null;
|
|
1025
|
+
|
|
1026
|
+
Object.defineProperty((globalThis as any).document, "modelContext", {
|
|
1027
|
+
configurable: true,
|
|
1028
|
+
value: mockTarget,
|
|
1029
|
+
});
|
|
1030
|
+
|
|
1031
|
+
const controller = new AbortController();
|
|
1032
|
+
const listener = jest.fn();
|
|
1033
|
+
onWebMcpToolActivated(listener, { signal: controller.signal });
|
|
1034
|
+
|
|
1035
|
+
controller.abort();
|
|
1036
|
+
|
|
1037
|
+
const event = new CustomEvent("toolactivated");
|
|
1038
|
+
(event as any).toolName = "write_file";
|
|
1039
|
+
mockTarget.dispatchEvent(event);
|
|
1040
|
+
|
|
1041
|
+
expect(listener).not.toHaveBeenCalled();
|
|
1042
|
+
});
|
|
1043
|
+
|
|
1044
|
+
it("supports listenBoth: true and deduplicates events within same tick", async () => {
|
|
1045
|
+
const { addWebMcpEventListener } = await import("./webmcp.js");
|
|
1046
|
+
|
|
1047
|
+
const mockTarget = new EventTarget();
|
|
1048
|
+
(mockTarget as any).ontoolactivated = null;
|
|
1049
|
+
|
|
1050
|
+
Object.defineProperty((globalThis as any).document, "modelContext", {
|
|
1051
|
+
configurable: true,
|
|
1052
|
+
value: mockTarget,
|
|
1053
|
+
});
|
|
1054
|
+
|
|
1055
|
+
const listener = jest.fn();
|
|
1056
|
+
const unsubscribe = addWebMcpEventListener("toolactivated", listener, {
|
|
1057
|
+
listenBoth: true,
|
|
1058
|
+
});
|
|
1059
|
+
|
|
1060
|
+
const evt1 = new CustomEvent("toolactivated");
|
|
1061
|
+
(evt1 as any).toolName = "list_files";
|
|
1062
|
+
const evt2 = new CustomEvent("toolactivated");
|
|
1063
|
+
(evt2 as any).toolName = "list_files";
|
|
1064
|
+
|
|
1065
|
+
// Dispatch on both targets
|
|
1066
|
+
mockTarget.dispatchEvent(evt1);
|
|
1067
|
+
window.dispatchEvent(evt2);
|
|
1068
|
+
|
|
1069
|
+
expect(listener).toHaveBeenCalledTimes(1);
|
|
1070
|
+
|
|
1071
|
+
unsubscribe();
|
|
1072
|
+
});
|
|
1073
|
+
});
|
|
1074
|
+
});
|
|
820
1075
|
});
|