shadow-claw 1.26.0 → 1.27.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/skills/main/skill-creator/SKILL.md +39 -0
- package/.agents/skills/main/skill-creator/references/declarative-pipelines.md +44 -0
- package/.agents/skills/main/skill-creator/references/declarative-tools.md +35 -0
- package/.agents/skills/main/skill-creator/references/frontmatter-spec.md +50 -0
- package/.well-known/agent-skills/index.json +14 -0
- package/README.md +5 -3
- package/bin/build/build.mjs +148 -15
- package/bin/build/build.test.mjs +128 -0
- package/bin/cli.mjs +35 -0
- package/bin/commands/mcp.mjs +277 -55
- package/bin/commands/mcp.test.mjs +275 -1
- package/bin/commands/skills-index.mjs +447 -0
- package/bin/commands/skills-index.test.mjs +243 -0
- package/bin/site-config/utils/custom-elements/get-approved-custom-element-scripts.test.mjs +2 -0
- package/bin/site-config/utils/custom-elements/sanitize-embedded-custom-element-scripts.test.mjs +10 -1
- package/dist/lib/components/index.js +25 -25
- 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-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-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/{configurePeerJs-CrlGan0m.js → configurePeerJs-oODzltaC.js} +2 -2
- package/dist/lib/{configurePeerJs-CrlGan0m.js.map → configurePeerJs-oODzltaC.js.map} +1 -1
- package/dist/lib/core/control-plane-client.d.ts +1 -0
- package/dist/lib/{downloadGroupFile-DboUjmp2.js → downloadGroupFile-DmcBQU1n.js} +2 -2
- package/dist/lib/{downloadGroupFile-DboUjmp2.js.map → downloadGroupFile-DmcBQU1n.js.map} +1 -1
- package/dist/lib/{file-viewer-BrK2Wxgh.js → file-viewer-C3YKnq_-.js} +4 -3
- package/dist/lib/{file-viewer-BrK2Wxgh.js.map → file-viewer-C3YKnq_-.js.map} +1 -1
- package/dist/lib/index.js +26 -26
- package/dist/lib/{initControlPlane-CsO4UR6_.js → initControlPlane-C2zURVIS.js} +86 -13
- package/dist/lib/initControlPlane-C2zURVIS.js.map +1 -0
- package/dist/lib/{markdown-eeSTFnMA.js → markdown-C6EHl9zs.js} +3 -3
- package/dist/lib/{markdown-eeSTFnMA.js.map → markdown-C6EHl9zs.js.map} +1 -1
- package/dist/lib/{orchestrator-CJvoC2Uu.js → orchestrator-CACLa_Ro.js} +7 -6
- package/dist/lib/{orchestrator-CJvoC2Uu.js.map → orchestrator-CACLa_Ro.js.map} +1 -1
- package/dist/lib/{orchestrator-DCfomsn9.js → orchestrator-KRjx0eba.js} +6 -534
- package/dist/lib/{orchestrator-DCfomsn9.js.map → orchestrator-KRjx0eba.js.map} +1 -1
- package/dist/lib/parsePath-DwC_YZjw.js +16 -0
- package/dist/lib/parsePath-DwC_YZjw.js.map +1 -0
- package/dist/lib/{peerjs-Dp02bXcG.js → peerjs-DFWOBcXf.js} +4 -4
- package/dist/lib/{peerjs-Dp02bXcG.js.map → peerjs-DFWOBcXf.js.map} +1 -1
- package/dist/lib/{readGroupFileBytes-Bve-e5e_.js → readGroupFileBytes-C8oOe8Iu.js} +3 -16
- package/dist/lib/readGroupFileBytes-C8oOe8Iu.js.map +1 -0
- package/dist/lib/security/custom-element-security.d.ts +8 -0
- package/dist/lib/server/control-plane-types.d.ts +1 -1
- package/dist/lib/{shadow-claw-D8xINM3c.js → shadow-claw-6UHdiy4a.js} +5 -5
- package/dist/lib/{shadow-claw-D8xINM3c.js.map → shadow-claw-6UHdiy4a.js.map} +1 -1
- package/dist/lib/{shadow-claw-a2ui-7rJqG4Ke.js → shadow-claw-a2ui-CpZUTzlh.js} +5 -4
- package/dist/lib/{shadow-claw-a2ui-7rJqG4Ke.js.map → shadow-claw-a2ui-CpZUTzlh.js.map} +1 -1
- package/dist/lib/{shadow-claw-accounts-CdruwN8A.js → shadow-claw-accounts-03oJ__XC.js} +2 -2
- package/dist/lib/{shadow-claw-accounts-CdruwN8A.js.map → shadow-claw-accounts-03oJ__XC.js.map} +1 -1
- package/dist/lib/{shadow-claw-channel-config-Bj0-Nk3t.js → shadow-claw-channel-config-DmfwJc6G.js} +3 -3
- package/dist/lib/{shadow-claw-channel-config-Bj0-Nk3t.js.map → shadow-claw-channel-config-DmfwJc6G.js.map} +1 -1
- package/dist/lib/{shadow-claw-channels-C_jI4KhP.js → shadow-claw-channels-1N6Jl-By.js} +3 -3
- package/dist/lib/{shadow-claw-channels-C_jI4KhP.js.map → shadow-claw-channels-1N6Jl-By.js.map} +1 -1
- package/dist/lib/{shadow-claw-chat-B16MspoP.js → shadow-claw-chat-BMoqQJRK.js} +10 -10
- package/dist/lib/{shadow-claw-chat-B16MspoP.js.map → shadow-claw-chat-BMoqQJRK.js.map} +1 -1
- package/dist/lib/{shadow-claw-control-plane-x8Ldid_h.js → shadow-claw-control-plane-UG0o7pCz.js} +6 -6
- package/dist/lib/{shadow-claw-control-plane-x8Ldid_h.js.map → shadow-claw-control-plane-UG0o7pCz.js.map} +1 -1
- package/dist/lib/{shadow-claw-conversations-Ct1kAYdB.js → shadow-claw-conversations-BwXep3iu.js} +4 -3
- package/dist/lib/{shadow-claw-conversations-Ct1kAYdB.js.map → shadow-claw-conversations-BwXep3iu.js.map} +1 -1
- package/dist/lib/{shadow-claw-file-viewer-BfDHHgpA.js → shadow-claw-file-viewer-DUxzVxDT.js} +14 -10
- package/dist/lib/shadow-claw-file-viewer-DUxzVxDT.js.map +1 -0
- package/dist/lib/{shadow-claw-files-EYhOPYIH.js → shadow-claw-files-DIREEnT2.js} +7 -7
- package/dist/lib/{shadow-claw-files-EYhOPYIH.js.map → shadow-claw-files-DIREEnT2.js.map} +1 -1
- package/dist/lib/{shadow-claw-git-tcSpT-P0.js → shadow-claw-git-Bg1SWgsF.js} +4 -4
- package/dist/lib/{shadow-claw-git-tcSpT-P0.js.map → shadow-claw-git-Bg1SWgsF.js.map} +1 -1
- package/dist/lib/{shadow-claw-integrations-NbcCx_ZI.js → shadow-claw-integrations-Ds-CLPhj.js} +2 -2
- package/dist/lib/{shadow-claw-integrations-NbcCx_ZI.js.map → shadow-claw-integrations-Ds-CLPhj.js.map} +1 -1
- package/dist/lib/{shadow-claw-llm-BazJdD5f.js → shadow-claw-llm-CazBrVpM.js} +3 -3
- package/dist/lib/{shadow-claw-llm-BazJdD5f.js.map → shadow-claw-llm-CazBrVpM.js.map} +1 -1
- package/dist/lib/{shadow-claw-mcp-remote-CYmhpg7a.js → shadow-claw-mcp-remote-CT5Q-54E.js} +2 -2
- package/dist/lib/{shadow-claw-mcp-remote-CYmhpg7a.js.map → shadow-claw-mcp-remote-CT5Q-54E.js.map} +1 -1
- package/dist/lib/{shadow-claw-networking-DImPxe0H.js → shadow-claw-networking-Df85tCbO.js} +2 -2
- package/dist/lib/{shadow-claw-networking-DImPxe0H.js.map → shadow-claw-networking-Df85tCbO.js.map} +1 -1
- package/dist/lib/{shadow-claw-page-header-CcHUfFUx.js → shadow-claw-page-header-Cti2fzf-.js} +2 -2
- package/dist/lib/{shadow-claw-page-header-CcHUfFUx.js.map → shadow-claw-page-header-Cti2fzf-.js.map} +1 -1
- package/dist/lib/{shadow-claw-pages-D54jF1Qq.js → shadow-claw-pages-BgPyIEZO.js} +15 -11
- package/dist/lib/shadow-claw-pages-BgPyIEZO.js.map +1 -0
- package/dist/lib/{shadow-claw-peerjs-B9-T8Nlx.js → shadow-claw-peerjs-xkBXILed.js} +3 -3
- package/dist/lib/{shadow-claw-peerjs-B9-T8Nlx.js.map → shadow-claw-peerjs-xkBXILed.js.map} +1 -1
- package/dist/lib/{shadow-claw-settings-Bt1VAe-1.js → shadow-claw-settings-CN89xRVU.js} +4 -4
- package/dist/lib/{shadow-claw-settings-Bt1VAe-1.js.map → shadow-claw-settings-CN89xRVU.js.map} +1 -1
- package/dist/lib/{shadow-claw-storage-CFOjGf1d.js → shadow-claw-storage-VnhcObCe.js} +3 -3
- package/dist/lib/{shadow-claw-storage-CFOjGf1d.js.map → shadow-claw-storage-VnhcObCe.js.map} +1 -1
- package/dist/lib/{shadow-claw-task-server-BiKa2GOm.js → shadow-claw-task-server-jgEC2hHB.js} +2 -2
- package/dist/lib/{shadow-claw-task-server-BiKa2GOm.js.map → shadow-claw-task-server-jgEC2hHB.js.map} +1 -1
- package/dist/lib/{shadow-claw-tasks-DDHd8W4d.js → shadow-claw-tasks-i_evIufy.js} +6 -6
- package/dist/lib/{shadow-claw-tasks-DDHd8W4d.js.map → shadow-claw-tasks-i_evIufy.js.map} +1 -1
- package/dist/lib/{shadow-claw-terminal-kB6_tWKz.js → shadow-claw-terminal-CwScROlu.js} +2 -2
- package/dist/lib/{shadow-claw-terminal-kB6_tWKz.js.map → shadow-claw-terminal-CwScROlu.js.map} +1 -1
- package/dist/lib/{shadow-claw-tools-DxXIm0ku.js → shadow-claw-tools-DD3fyXP4.js} +5 -4
- package/dist/lib/{shadow-claw-tools-DxXIm0ku.js.map → shadow-claw-tools-DD3fyXP4.js.map} +1 -1
- package/dist/lib/{shadow-claw-webvm-Dc8oo7ao.js → shadow-claw-webvm-DbJq4I3-.js} +2 -2
- package/dist/lib/{shadow-claw-webvm-Dc8oo7ao.js.map → shadow-claw-webvm-DbJq4I3-.js.map} +1 -1
- package/dist/lib/{syncWebMcpRegistration-jyRGbC9w.js → syncWebMcpRegistration-DNLnQ-jz.js} +3 -2
- package/dist/lib/{syncWebMcpRegistration-jyRGbC9w.js.map → syncWebMcpRegistration-DNLnQ-jz.js.map} +1 -1
- package/dist/lib/tools-DwwB9RBa.js +546 -0
- package/dist/lib/tools-DwwB9RBa.js.map +1 -0
- package/dist/lib/{trusted-types-CkLDpLiZ.js → trusted-types-Dx0RZlrd.js} +19 -8
- package/dist/lib/trusted-types-Dx0RZlrd.js.map +1 -0
- package/dist/lib/utils/index.js +1 -1
- package/dist/lib/utils/utils.js +1 -1
- package/dist/public/.well-known/agent-skills/index.json +14 -0
- package/dist/public/AGENTS.md +5 -3
- package/dist/public/README.md +5 -3
- package/dist/public/agent.worker.js +1 -1
- package/dist/public/{configurePeerJs-COcwE0E7.js → configurePeerJs-BqgeBjP7.js} +1 -1
- package/dist/public/custom-element-security-FU04Cq05.js +1 -0
- package/dist/public/dist-B9m1QM9v.js +58 -0
- package/dist/public/docs/README.md +31 -31
- package/dist/public/docs/example/article/index.html +1 -1
- package/dist/public/docs/publishing/index.html +1 -1
- package/dist/public/docs/skill-creator/index.html +1 -1
- package/dist/public/docs/subsystems/a2ui.md +11 -1
- package/dist/public/docs/subsystems/cli.md +32 -0
- package/dist/public/docs/subsystems/control-plane.md +8 -2
- package/dist/public/docs/subsystems/custom-element-security.md +19 -9
- package/dist/public/docs/subsystems/mcp-server.md +39 -15
- package/dist/public/docs/subsystems/skills.md +30 -0
- package/dist/public/docs/subsystems/webmcp.md +9 -0
- package/dist/public/downloadGroupFile-D8KRJGGL.js +1 -0
- package/dist/public/e2e/README.md +7 -4
- package/dist/public/{e2e-bridge-CHARpbpW.js → e2e-bridge-Ceqr5kGc.js} +1 -1
- package/dist/public/effect-BJCrpFdp.js +1 -0
- package/dist/public/file-viewer-C-6Jc_tb.js +1 -0
- package/dist/public/index.html +1 -1
- package/dist/public/index.js +1 -1
- package/dist/public/initControlPlane-BN9cWyO0.js +1 -0
- package/dist/public/main/index.html +1 -1
- package/dist/public/main/memory/index.html +1 -1
- package/dist/public/{markdown-1I3O6Mmd.js → markdown-C3M4OW_A.js} +1 -1
- package/dist/public/{orchestrator-Cl95w2vb.js → orchestrator-CMbCTjBf.js} +7 -7
- package/dist/public/{orchestrator-D-xlXG92.js → orchestrator-DHjdLtqa.js} +663 -663
- package/dist/public/peerjs-DUCKH-m9.js +1 -0
- package/dist/public/service-worker.js +1 -1
- package/dist/public/{shadow-claw-CaoKmIIB.js → shadow-claw-EAt2ml5Y.js} +3 -3
- package/dist/public/{shadow-claw-accounts-CJvYC4li.js → shadow-claw-accounts-xUOLBsQZ.js} +1 -1
- package/dist/public/{shadow-claw-channels-3K62sU5f.js → shadow-claw-channels-XsfuWFF8.js} +4 -4
- package/dist/public/{shadow-claw-chat-Cu2keU2I.js → shadow-claw-chat-CbSaOZ4q.js} +7 -7
- package/dist/public/{shadow-claw-control-plane-D1OwlQI_.js → shadow-claw-control-plane-DHvN3Ukg.js} +2 -2
- package/dist/public/{shadow-claw-conversations-DDtg3BEZ.js → shadow-claw-conversations-BWs5VVfs.js} +2 -2
- package/dist/public/{shadow-claw-file-viewer-BR__1z9S.js → shadow-claw-file-viewer-BWsoW5QS.js} +5 -4
- package/dist/public/{shadow-claw-files-BJSyZ-Bl.js → shadow-claw-files-CG7MqWiV.js} +8 -8
- package/dist/public/{shadow-claw-git-MAjhXRLc.js → shadow-claw-git-DBRPZNav.js} +6 -6
- package/dist/public/{shadow-claw-integrations-82UoFuDR.js → shadow-claw-integrations-BBVnO0Fc.js} +8 -8
- package/dist/public/{shadow-claw-llm-R8j0lwPP.js → shadow-claw-llm-DMvLgKXo.js} +2 -2
- package/dist/public/{shadow-claw-mcp-remote-BzZoz4LX.js → shadow-claw-mcp-remote-Bkx0gsmF.js} +2 -2
- package/dist/public/{shadow-claw-networking-DLEd9cNm.js → shadow-claw-networking-Dd8Rw31D.js} +2 -2
- package/dist/public/{shadow-claw-notifications-1vwR7TbT.js → shadow-claw-notifications-Dbevj-lq.js} +2 -2
- package/dist/public/{shadow-claw-page-header-DMKbkv3s.js → shadow-claw-page-header-BccSHItx.js} +1 -1
- package/dist/public/{shadow-claw-pages-CjyeIV__.js → shadow-claw-pages-3_lQAv0t.js} +4 -3
- package/dist/public/{shadow-claw-settings-BfbT4dBU.js → shadow-claw-settings-t8TIlgrM.js} +5 -5
- package/dist/public/{shadow-claw-storage-C-mWQQML.js → shadow-claw-storage-CfEXoSIE.js} +2 -2
- package/dist/public/{shadow-claw-task-server-D3lHp0Nc.js → shadow-claw-task-server-DxQSsgRZ.js} +2 -2
- package/dist/public/{shadow-claw-tasks-DVVWEm-c.js → shadow-claw-tasks-DNpicQwz.js} +14 -14
- package/dist/public/{shadow-claw-terminal-BwuTRp-O.js → shadow-claw-terminal-DpY6zU_r.js} +1 -1
- package/dist/public/{shadow-claw-toast-ClCwgzFG.js → shadow-claw-toast-BkrQIP-3.js} +1 -1
- package/dist/public/{shadow-claw-tools-zFLk2nbV.js → shadow-claw-tools-WSwWohnk.js} +2 -2
- package/dist/public/{shadow-claw-webvm-qR9X7fg7.js → shadow-claw-webvm-CP6oFjg5.js} +2 -2
- package/dist/public/syncWebMcpRegistration-DOn9gLnD.js +1 -0
- package/dist/public/toast-BXgUfbyh.js +1 -0
- package/dist/public/tools-DvckJZG2.js +1 -0
- package/dist/server.js +188 -188
- package/package.json +3 -1
- package/src/components/shadow-claw-a2ui/shadow-claw-a2ui.stories.ts +1380 -93
- package/src/components/shadow-claw-a2ui/shadow-claw-a2ui.ts +11 -6
- package/src/components/shadow-claw-file-viewer/shadow-claw-file-viewer.test.ts +8 -1
- package/src/components/shadow-claw-file-viewer/shadow-claw-file-viewer.ts +13 -5
- package/src/components/shadow-claw-pages/utils/buildHtmlPageSrcdoc.ts +13 -5
- package/src/core/control-plane-client.ts +15 -0
- package/src/core/utils/initControlPlane.test.ts +187 -0
- package/src/core/utils/initControlPlane.ts +194 -20
- package/src/security/custom-element-security.test.ts +51 -0
- package/src/security/custom-element-security.ts +33 -15
- package/src/server/control-plane-types.ts +1 -0
- package/src/server/control-plane.test.ts +69 -0
- package/src/server/control-plane.ts +45 -3
- package/src/server/mcp/tools/built-in-tools.ts +80 -5
- package/src/server/mcp/tools/client-tool-relay.ts +183 -26
- package/src/server/mcp/tools/mcp-tools.test.ts +140 -0
- package/src/server/middleware/static-files.test.ts +142 -0
- package/src/server/middleware/static-files.ts +66 -8
- package/src/testing/jest-setup.ts +14 -0
- package/src/worker/utils/handleMessage.ts +4 -1
- package/dist/lib/initControlPlane-CsO4UR6_.js.map +0 -1
- package/dist/lib/readGroupFileBytes-Bve-e5e_.js.map +0 -1
- package/dist/lib/shadow-claw-file-viewer-BfDHHgpA.js.map +0 -1
- package/dist/lib/shadow-claw-pages-D54jF1Qq.js.map +0 -1
- package/dist/lib/trusted-types-CkLDpLiZ.js.map +0 -1
- package/dist/public/custom-element-security-NCqQ5lmC.js +0 -1
- package/dist/public/downloadGroupFile-G0BZQgQg.js +0 -1
- package/dist/public/effect-rHYr4nfu.js +0 -1
- package/dist/public/file-viewer-8PdCURDD.js +0 -1
- package/dist/public/initControlPlane-CcGXax0A.js +0 -1
- package/dist/public/peerjs-BZdxWQFv.js +0 -1
- package/dist/public/syncWebMcpRegistration-DD42YsYV.js +0 -1
- package/dist/public/toast-HAq8XWFj.js +0 -58
|
@@ -75,10 +75,10 @@ function isSafeCustomElementSource(urlStr) {
|
|
|
75
75
|
if (/^(?:javascript|data|vbscript):/i.test(trimmed)) return false;
|
|
76
76
|
if (/^blob:/i.test(trimmed)) return true;
|
|
77
77
|
try {
|
|
78
|
-
const base = typeof
|
|
78
|
+
const base = typeof globalThis !== "undefined" && globalThis.location?.origin && globalThis.location.origin !== "null" ? globalThis.location.origin : "http://localhost";
|
|
79
79
|
const url = new URL(trimmed, base);
|
|
80
80
|
if (url.protocol !== "http:" && url.protocol !== "https:") return false;
|
|
81
|
-
if (typeof
|
|
81
|
+
if (typeof globalThis !== "undefined" && globalThis.location?.origin && globalThis.location.origin !== "null" && url.origin === globalThis.location.origin) return true;
|
|
82
82
|
const hostname = url.hostname.toLowerCase();
|
|
83
83
|
return activeHostMatchers.some((matcher) => matcher(hostname));
|
|
84
84
|
} catch {
|
|
@@ -141,7 +141,7 @@ function getIframeCsp(nonce) {
|
|
|
141
141
|
allowedScriptHosts.add(`http://${pattern}`);
|
|
142
142
|
}
|
|
143
143
|
for (const scriptUrl of approvedScripts) try {
|
|
144
|
-
const parsed = new URL(scriptUrl, typeof
|
|
144
|
+
const parsed = new URL(scriptUrl, typeof globalThis !== "undefined" && globalThis.location?.origin ? globalThis.location.origin : "http://localhost");
|
|
145
145
|
allowedScriptHosts.add(parsed.origin);
|
|
146
146
|
} catch {}
|
|
147
147
|
const scriptSources = [
|
|
@@ -188,20 +188,31 @@ function initCustomElementSecurityFromEmbeddedConfig() {
|
|
|
188
188
|
} catch {}
|
|
189
189
|
}
|
|
190
190
|
/**
|
|
191
|
-
* Retrieve approved custom element script
|
|
191
|
+
* Retrieve approved custom element script descriptors from the embedded site config.
|
|
192
192
|
*/
|
|
193
|
-
function
|
|
193
|
+
function getApprovedCustomElementScriptDescriptors() {
|
|
194
194
|
if (typeof document === "undefined") return [];
|
|
195
195
|
const configScript = document.getElementById("shadow-claw-site-config");
|
|
196
196
|
if (!configScript || !configScript.textContent) return [];
|
|
197
197
|
try {
|
|
198
198
|
const config = JSON.parse(configScript.textContent);
|
|
199
199
|
const scripts = typeof config.customElements === "object" && !Array.isArray(config.customElements) ? config.customElements?.scripts : void 0;
|
|
200
|
-
if (Array.isArray(scripts)) return scripts.map((s) =>
|
|
200
|
+
if (Array.isArray(scripts)) return scripts.map((s) => {
|
|
201
|
+
return {
|
|
202
|
+
src: typeof s === "string" ? s : s?.src,
|
|
203
|
+
hasInit: typeof s === "object" && s !== null ? Boolean(s.hasInit) : false
|
|
204
|
+
};
|
|
205
|
+
}).filter((entry) => typeof entry.src === "string" && isSafeCustomElementSource(entry.src));
|
|
201
206
|
} catch {}
|
|
202
207
|
return [];
|
|
203
208
|
}
|
|
204
209
|
/**
|
|
210
|
+
* Retrieve approved custom element script URLs from the embedded site config.
|
|
211
|
+
*/
|
|
212
|
+
function getApprovedCustomElementScripts() {
|
|
213
|
+
return getApprovedCustomElementScriptDescriptors().map((d) => d.src);
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
205
216
|
* Load allowed custom elements and host patterns from IndexedDB.
|
|
206
217
|
*/
|
|
207
218
|
async function loadCustomElementSecurityFromDb(db) {
|
|
@@ -536,5 +547,5 @@ function toTrustedScriptUrl(url) {
|
|
|
536
547
|
}
|
|
537
548
|
|
|
538
549
|
//#endregion
|
|
539
|
-
export { getIframeSandboxPolicy as _, toTrustedScriptUrl as a, isAllowedCustomElement as b, escapeHtml as c, handleSpecialLinkNavigation as d, sanitizeHtml as f, getIframeCsp as g,
|
|
540
|
-
//# sourceMappingURL=trusted-types-
|
|
550
|
+
export { getIframeSandboxPolicy as _, toTrustedScriptUrl as a, isAllowedCustomElement as b, escapeHtml as c, handleSpecialLinkNavigation as d, sanitizeHtml as f, getIframeCsp as g, getApprovedCustomElementScriptDescriptors as h, toTrustedHtmlPresanitized as i, formatDateForFilename as l, configureCustomElementSecurity as m, setSanitizedHtml as n, bufferToHex as o, toArrayBuffer as p, setTrustedSrcdoc as r, computeSha256 as s, sanitizeSrcdocHtml as t, formatTimestamp as u, installCustomElementDomGuard as v, loadCustomElementSecurityFromDb as x, installCustomElementsRegistryGuard as y };
|
|
551
|
+
//# sourceMappingURL=trusted-types-Dx0RZlrd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trusted-types-Dx0RZlrd.js","names":[],"sources":["../../src/security/custom-element-security.ts","../../src/utils/utils.ts","../../src/security/trusted-types.ts"],"sourcesContent":["import { CONFIG_KEYS } from \"../config/config.js\";\nimport { getDb } from \"../db/db.js\";\nimport { getConfig } from \"../db/getConfig.js\";\nimport { getDOMPurify, parseHostPatternMatch } from \"./iframe-sanitizer.js\";\nimport { toTrustedScriptUrl } from \"./trusted-types.js\";\n\nimport type { Config } from \"dompurify\";\nimport type { ShadowClawDatabase } from \"../db/types.js\";\n\n/** Default allowed custom elements: none by default */\nexport const DEFAULT_ALLOWED_CUSTOM_ELEMENTS: string[] = [];\n\n/** Default allowed custom element host patterns: none by default */\nexport const DEFAULT_ALLOWED_CUSTOM_ELEMENT_HOST_PATTERNS: string[] = [];\n\nlet activeAllowedElements: Set<string> = new Set(\n DEFAULT_ALLOWED_CUSTOM_ELEMENTS.map((el) => el.toLowerCase().trim()),\n);\n\nlet activeHostPatterns: string[] = [\n ...DEFAULT_ALLOWED_CUSTOM_ELEMENT_HOST_PATTERNS,\n];\nlet activeHostMatchers: Array<(hostname: string) => boolean> = [];\n\nfunction rebuildHostMatchers(patterns: string[]): void {\n activeHostMatchers = patterns\n .map((p) => parseHostPatternMatch(p))\n .filter(Boolean);\n}\n\nrebuildHostMatchers(activeHostPatterns);\n\n/**\n * Normalizes an element tag name or list into an array of lowercase strings.\n */\nfunction normalizeElementList(elements: string[] | string): string[] {\n if (Array.isArray(elements)) {\n return elements\n .map((s) => (typeof s === \"string\" ? s.toLowerCase().trim() : \"\"))\n .filter(Boolean);\n }\n if (typeof elements === \"string\") {\n return elements\n .split(/[\\n,]+/)\n .map((s) => s.toLowerCase().trim())\n .filter(Boolean);\n }\n return [];\n}\n\n/**\n * Normalizes a list of host patterns into an array of trimmed strings.\n */\nfunction normalizePatternList(patterns: string[] | string): string[] {\n if (Array.isArray(patterns)) {\n return patterns\n .map((s) => (typeof s === \"string\" ? s.trim() : \"\"))\n .filter(Boolean);\n }\n if (typeof patterns === \"string\") {\n return patterns\n .split(/[\\n,]+/)\n .map((s) => s.trim())\n .filter(Boolean);\n }\n return [];\n}\n\n/**\n * Set the allowed custom elements in memory.\n */\nexport function setAllowedCustomElements(elements: string[] | string): void {\n const list = normalizeElementList(elements);\n activeAllowedElements = new Set(list);\n}\n\n/**\n * Get the current list of allowed custom elements.\n */\nexport function getAllowedCustomElements(): string[] {\n return Array.from(activeAllowedElements);\n}\n\n/**\n * Set the allowed custom element host patterns in memory.\n */\nexport function setAllowedCustomElementHostPatterns(\n patterns: string[] | string,\n): void {\n const list = normalizePatternList(patterns);\n activeHostPatterns = list;\n rebuildHostMatchers(list);\n}\n\n/**\n * Get the current list of allowed custom element host patterns.\n */\nexport function getAllowedCustomElementHostPatterns(): string[] {\n return [...activeHostPatterns];\n}\n\n/**\n * Check if a custom element tag name is approved.\n * Built-in ShadowClaw elements (\"shadow-claw\" and \"shadow-claw-*\") are always allowed.\n * Standard non-hyphenated HTML elements pass tag name check.\n */\nexport function isAllowedCustomElement(tagName: string): boolean {\n if (!tagName || typeof tagName !== \"string\") {\n return false;\n }\n\n const lower = tagName.trim().toLowerCase();\n\n // If not a custom element (no hyphen), allow standard HTML tag handling\n if (!lower.includes(\"-\")) {\n return true;\n }\n\n // ShadowClaw core elements are built-in and always permitted\n if (lower === \"shadow-claw\" || lower.startsWith(\"shadow-claw-\")) {\n return true;\n }\n\n // Check against explicit allowlist\n return activeAllowedElements.has(lower);\n}\n\n/**\n * Check if a script URL source is from an approved domain or same-origin.\n */\nexport function isSafeCustomElementSource(urlStr: string): boolean {\n if (!urlStr || typeof urlStr !== \"string\") {\n return false;\n }\n\n const trimmed = urlStr.trim();\n if (!trimmed) {\n return false;\n }\n\n // Reject dangerous pseudo-protocols\n if (/^(?:javascript|data|vbscript):/i.test(trimmed)) {\n return false;\n }\n\n // Allow blob: URLs (used by internal web workers)\n if (/^blob:/i.test(trimmed)) {\n return true;\n }\n\n try {\n const base =\n typeof globalThis !== \"undefined\" &&\n globalThis.location?.origin &&\n globalThis.location.origin !== \"null\"\n ? globalThis.location.origin\n : \"http://localhost\";\n const url = new URL(trimmed, base);\n\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") {\n return false;\n }\n\n // Same-origin is trusted\n if (\n typeof globalThis !== \"undefined\" &&\n globalThis.location?.origin &&\n globalThis.location.origin !== \"null\" &&\n url.origin === globalThis.location.origin\n ) {\n return true;\n }\n\n const hostname = url.hostname.toLowerCase();\n\n return activeHostMatchers.some((matcher) => matcher(hostname));\n } catch {\n return false;\n }\n}\n\nexport const DEFAULT_IFRAME_SANDBOX_POLICY =\n \"allow-modals allow-scripts allow-popups allow-popups-to-escape-sandbox\";\n\nexport const DEFAULT_WORKER_SRC = [\"'self'\", \"blob:\", \"data:\"];\nexport const DEFAULT_CONNECT_SRC = [\"'self'\", \"blob:\", \"data:\"];\nexport const DEFAULT_STYLE_SRC = [\n \"'self'\",\n \"'unsafe-inline'\",\n \"https:\",\n \"http:\",\n];\nexport const DEFAULT_IMG_SRC = [\"'self'\", \"blob:\", \"data:\", \"https:\", \"http:\"];\n\nlet activeIframeSandboxPolicy: string = DEFAULT_IFRAME_SANDBOX_POLICY;\nlet activeWorkerSrc: string[] = [...DEFAULT_WORKER_SRC];\nlet activeConnectSrc: string[] = [...DEFAULT_CONNECT_SRC];\nlet activeScriptSrc: string[] = [];\nlet activeStyleSrc: string[] = [...DEFAULT_STYLE_SRC];\nlet activeImgSrc: string[] = [...DEFAULT_IMG_SRC];\n\n/**\n * Get the active iframe sandbox policy string.\n */\nexport function getIframeSandboxPolicy(): string {\n return activeIframeSandboxPolicy;\n}\n\n/**\n * Set the iframe sandbox policy.\n */\nexport function setIframeSandboxPolicy(policy: string[] | string): void {\n if (Array.isArray(policy)) {\n activeIframeSandboxPolicy = policy\n .map((s) => (typeof s === \"string\" ? s.trim() : \"\"))\n .filter(Boolean)\n .join(\" \");\n } else if (typeof policy === \"string\" && policy.trim()) {\n activeIframeSandboxPolicy = policy.trim();\n } else {\n activeIframeSandboxPolicy = DEFAULT_IFRAME_SANDBOX_POLICY;\n }\n}\n\n/**\n * Generate CSP header string for the sandboxed preview iframe.\n */\nexport function getIframeCsp(nonce: string): string {\n const allowedPatterns = getAllowedCustomElementHostPatterns();\n const approvedScripts = getApprovedCustomElementScripts();\n\n const allowedScriptHosts = new Set<string>();\n for (const pattern of allowedPatterns) {\n if (pattern && /^(\\*\\.)?[a-zA-Z0-9.-]+(?::[0-9]+)?$/.test(pattern)) {\n allowedScriptHosts.add(`https://${pattern}`);\n allowedScriptHosts.add(`http://${pattern}`);\n }\n }\n for (const scriptUrl of approvedScripts) {\n try {\n const parsed = new URL(\n scriptUrl,\n typeof globalThis !== \"undefined\" && globalThis.location?.origin\n ? globalThis.location.origin\n : \"http://localhost\",\n );\n allowedScriptHosts.add(parsed.origin);\n } catch {}\n }\n\n const scriptSources = [\n `'nonce-${nonce}'`,\n ...Array.from(allowedScriptHosts),\n ...activeScriptSrc,\n ];\n\n const connectSources = [\n ...activeConnectSrc,\n ...Array.from(allowedScriptHosts),\n ];\n\n const directives: string[] = [\n `script-src ${Array.from(new Set(scriptSources)).join(\" \")}`,\n `worker-src ${Array.from(new Set(activeWorkerSrc)).join(\" \")}`,\n `connect-src ${Array.from(new Set(connectSources)).join(\" \")}`,\n `style-src ${Array.from(new Set(activeStyleSrc)).join(\" \")}`,\n `img-src ${Array.from(new Set(activeImgSrc)).join(\" \")}`,\n ];\n\n return directives.join(\"; \");\n}\n\n/**\n * Configure custom element security and iframe sandbox policy from a site config object or options.\n */\nexport function configureCustomElementSecurity(config: {\n allowedCustomElements?: string[] | string;\n allowedCustomElementDomains?: string[] | string;\n customElements?:\n | {\n allowedElements?: string[] | string;\n allowedDomains?: string[] | string;\n scripts?: Array<string | { src: string }>;\n }\n | Array<string | { src: string }>;\n security?: {\n iframeSandbox?: string[] | string;\n allowSameOrigin?: boolean;\n allowIframeSameOrigin?: boolean;\n workerSrc?: string[] | string;\n connectSrc?: string[] | string;\n scriptSrc?: string[] | string;\n styleSrc?: string[] | string;\n imgSrc?: string[] | string;\n };\n}): void {\n if (!config || typeof config !== \"object\") {\n return;\n }\n\n // Extract allowed elements\n const elements =\n config.allowedCustomElements ||\n (typeof config.customElements === \"object\" &&\n !Array.isArray(config.customElements)\n ? config.customElements?.allowedElements\n : undefined);\n\n if (elements !== undefined) {\n setAllowedCustomElements(elements);\n }\n\n // Extract allowed domains\n const domains =\n config.allowedCustomElementDomains ||\n (typeof config.customElements === \"object\" &&\n !Array.isArray(config.customElements)\n ? config.customElements?.allowedDomains\n : undefined);\n\n if (domains !== undefined) {\n setAllowedCustomElementHostPatterns(domains);\n }\n\n // Extract security iframe sandbox settings\n if (config.security?.iframeSandbox !== undefined) {\n setIframeSandboxPolicy(config.security.iframeSandbox);\n } else if (\n config.security?.allowSameOrigin === true ||\n config.security?.allowIframeSameOrigin === true\n ) {\n if (!activeIframeSandboxPolicy.includes(\"allow-same-origin\")) {\n activeIframeSandboxPolicy =\n `${activeIframeSandboxPolicy} allow-same-origin`.trim();\n }\n }\n\n // Extract CSP overrides if configured\n if (config.security?.workerSrc !== undefined) {\n activeWorkerSrc = normalizePatternList(config.security.workerSrc);\n }\n if (config.security?.connectSrc !== undefined) {\n activeConnectSrc = normalizePatternList(config.security.connectSrc);\n }\n if (config.security?.scriptSrc !== undefined) {\n activeScriptSrc = normalizePatternList(config.security.scriptSrc);\n }\n if (config.security?.styleSrc !== undefined) {\n activeStyleSrc = normalizePatternList(config.security.styleSrc);\n }\n if (config.security?.imgSrc !== undefined) {\n activeImgSrc = normalizePatternList(config.security.imgSrc);\n }\n}\n\n/**\n * Initializes custom element security synchronously from embedded site-config script in the DOM if available.\n */\nexport function initCustomElementSecurityFromEmbeddedConfig(): void {\n if (typeof document === \"undefined\") {\n return;\n }\n\n const configScript = document.getElementById(\"shadow-claw-site-config\");\n if (configScript && configScript.textContent) {\n try {\n const config = JSON.parse(configScript.textContent);\n configureCustomElementSecurity(config);\n } catch {\n // ignore JSON parse error\n }\n }\n}\n\nexport interface CustomElementScriptDescriptor {\n src: string;\n hasInit: boolean;\n}\n\n/**\n * Retrieve approved custom element script descriptors from the embedded site config.\n */\nexport function getApprovedCustomElementScriptDescriptors(): CustomElementScriptDescriptor[] {\n if (typeof document === \"undefined\") {\n return [];\n }\n\n const configScript = document.getElementById(\"shadow-claw-site-config\");\n if (!configScript || !configScript.textContent) {\n return [];\n }\n\n try {\n const config = JSON.parse(configScript.textContent);\n const scripts =\n typeof config.customElements === \"object\" &&\n !Array.isArray(config.customElements)\n ? config.customElements?.scripts\n : undefined;\n\n if (Array.isArray(scripts)) {\n return scripts\n .map((s: any) => {\n const src = typeof s === \"string\" ? s : s?.src;\n const hasInit =\n typeof s === \"object\" && s !== null ? Boolean(s.hasInit) : false;\n return { src, hasInit };\n })\n .filter(\n (entry): entry is CustomElementScriptDescriptor =>\n typeof entry.src === \"string\" &&\n isSafeCustomElementSource(entry.src),\n );\n }\n } catch {}\n\n return [];\n}\n\n/**\n * Retrieve approved custom element script URLs from the embedded site config.\n */\nexport function getApprovedCustomElementScripts(): string[] {\n return getApprovedCustomElementScriptDescriptors().map((d) => d.src);\n}\n\n/**\n * Load allowed custom elements and host patterns from IndexedDB.\n */\nexport async function loadCustomElementSecurityFromDb(\n db?: ShadowClawDatabase | null,\n): Promise<void> {\n try {\n const database = db ?? (await getDb());\n if (!database) {\n return;\n }\n\n const [elementsVal, hostsVal] = await Promise.all([\n getConfig(database, CONFIG_KEYS.ALLOWED_CUSTOM_ELEMENTS),\n getConfig(database, CONFIG_KEYS.ALLOWED_CUSTOM_ELEMENT_HOST_PATTERNS),\n ]);\n\n if (typeof elementsVal === \"string\" && elementsVal.trim()) {\n try {\n const parsed = JSON.parse(elementsVal);\n setAllowedCustomElements(parsed);\n } catch {\n setAllowedCustomElements(elementsVal);\n }\n }\n\n if (typeof hostsVal === \"string\" && hostsVal.trim()) {\n try {\n const parsed = JSON.parse(hostsVal);\n setAllowedCustomElementHostPatterns(parsed);\n } catch {\n setAllowedCustomElementHostPatterns(hostsVal);\n }\n }\n } catch (err) {\n console.warn(\"Failed to load custom element security from db:\", err);\n }\n}\n\nlet originalCustomElementsDefine: typeof customElements.define | null = null;\nlet isRegistryGuardInstalled = false;\n\n/**\n * Monkey-patches customElements.define to prevent registration of unapproved custom elements.\n */\nexport function installCustomElementsRegistryGuard(): void {\n initCustomElementSecurityFromEmbeddedConfig();\n\n if (\n isRegistryGuardInstalled ||\n typeof customElements === \"undefined\" ||\n typeof customElements.define !== \"function\"\n ) {\n return;\n }\n\n originalCustomElementsDefine = customElements.define.bind(customElements);\n\n customElements.define = function (\n name: string,\n constructor: CustomElementConstructor,\n options?: ElementDefinitionOptions,\n ) {\n const lowerName = (name || \"\").toLowerCase().trim();\n if (!isAllowedCustomElement(lowerName)) {\n const errorMsg = `[Security] Registration blocked: <${name}> is not an approved custom element.`;\n console.error(errorMsg);\n throw new Error(errorMsg);\n }\n\n return originalCustomElementsDefine!(name, constructor, options);\n };\n\n isRegistryGuardInstalled = true;\n}\n\n/**\n * Restores the original customElements.define (useful for testing).\n */\nexport function uninstallCustomElementsRegistryGuard(): void {\n if (!isRegistryGuardInstalled || !originalCustomElementsDefine) {\n return;\n }\n\n customElements.define = originalCustomElementsDefine;\n originalCustomElementsDefine = null;\n isRegistryGuardInstalled = false;\n}\n\n/**\n * Installs a MutationObserver on a DOM tree to detect and strip unapproved custom elements.\n */\nexport function installCustomElementDomGuard(\n root: Node = typeof document !== \"undefined\"\n ? document.documentElement\n : (null as any),\n): MutationObserver | null {\n if (typeof MutationObserver === \"undefined\" || !root) {\n return null;\n }\n\n // Sweep existing unapproved custom elements already present in root\n if (typeof (root as any).querySelectorAll === \"function\") {\n try {\n const existing = (root as Element).querySelectorAll(\"*\");\n existing.forEach((el) => {\n const tagName = el.tagName ? el.tagName.toLowerCase() : \"\";\n if (tagName.includes(\"-\") && !isAllowedCustomElement(tagName)) {\n console.warn(\n `[Security] Removing existing unapproved custom element: <${tagName}>`,\n );\n el.remove();\n }\n });\n } catch {}\n }\n\n const observer = new MutationObserver((mutations) => {\n for (const mutation of mutations) {\n mutation.addedNodes.forEach((node) => {\n if (node.nodeType === 1 /* Node.ELEMENT_NODE */) {\n const el = node as Element;\n const tagName = el.tagName ? el.tagName.toLowerCase() : \"\";\n if (tagName.includes(\"-\") && !isAllowedCustomElement(tagName)) {\n console.warn(\n `[Security] Removing unapproved custom element: <${tagName}>`,\n );\n el.remove();\n }\n }\n });\n }\n });\n\n observer.observe(root, { childList: true, subtree: true });\n\n return observer;\n}\n\n/**\n * Load an approved custom element script module dynamically.\n * Enforces domain validation and Trusted Types.\n */\nexport async function loadApprovedCustomElementScript(\n src: string,\n options: {\n type?: string;\n async?: boolean;\n defer?: boolean;\n } = {},\n): Promise<HTMLScriptElement> {\n if (!isSafeCustomElementSource(src)) {\n throw new Error(\n `[Security] Refused to load script from unapproved host: ${src}`,\n );\n }\n\n if (typeof document === \"undefined\") {\n throw new Error(\"Cannot load script outside browser document environment.\");\n }\n\n const existingScript = document.querySelector(`script[src=\"${src}\"]`);\n if (existingScript instanceof HTMLScriptElement) {\n return existingScript;\n }\n\n const trustedUrl = toTrustedScriptUrl(src) as string;\n\n return new Promise((resolve, reject) => {\n const script = document.createElement(\"script\");\n script.type = options.type || \"module\";\n if (options.async) script.async = true;\n if (options.defer) script.defer = true;\n script.src = trustedUrl;\n\n script.onload = () => resolve(script);\n script.onerror = (err) =>\n reject(\n new Error(`Failed to load custom element script: ${src} (${err})`),\n );\n\n document.head.appendChild(script);\n });\n}\n\nlet purifyHookRegistered = false;\n\n/**\n * Ensures DOMPurify has the custom element sanitization hook registered.\n */\nexport function ensureCustomElementSanitizerHook(): void {\n if (purifyHookRegistered) {\n return;\n }\n\n const purify = getDOMPurify();\n\n if (purify && typeof purify.addHook === \"function\") {\n const sanitizeCustomElementNode = (node: Node) => {\n if (node && node.nodeType === 1 /* ELEMENT_NODE */) {\n const el = node as Element;\n const tagName = (el.tagName || \"\").toLowerCase();\n if (tagName.includes(\"-\") && !isAllowedCustomElement(tagName)) {\n if (typeof el.remove === \"function\") {\n el.remove();\n }\n if (el.parentNode) {\n el.parentNode.removeChild(el);\n }\n }\n }\n };\n\n purify.addHook(\"uponSanitizeElement\", sanitizeCustomElementNode);\n purify.addHook(\"afterSanitizeElements\", sanitizeCustomElementNode);\n purifyHookRegistered = true;\n }\n}\n\n/**\n * Get DOMPurify configuration for custom element handling.\n */\nexport function getCustomElementPurifyConfig(): Config {\n ensureCustomElementSanitizerHook();\n\n return {\n CUSTOM_ELEMENT_HANDLING: {\n tagNameCheck: (tagName: string) => isAllowedCustomElement(tagName),\n attributeNameCheck: () => true,\n allowCustomizedBuiltInElements: false,\n },\n };\n}\n","/// <reference lib=\"dom\" />\nimport type { Config } from \"dompurify\";\nimport { parseRouteFromUrl } from \"../core/app-routes.js\";\nimport {\n ensureIframeSanitizerHook,\n getDOMPurify,\n} from \"../security/iframe-sanitizer.js\";\nimport {\n ensureCustomElementSanitizerHook,\n isAllowedCustomElement,\n} from \"../security/custom-element-security.js\";\n\nexport type HashInput = File | Blob | ArrayBuffer | ArrayBufferView;\n\n/**\n * Format a date for use in a filename: yyyy-mm-dd_hh-mm-ss\n */\nexport function formatDateForFilename(date = new Date()): string {\n const pad = (n: number) => String(n).padStart(2, \"0\");\n\n const yyyy = date.getFullYear();\n const mm = pad(date.getMonth() + 1);\n const dd = pad(date.getDate());\n\n const hh = pad(date.getHours());\n const min = pad(date.getMinutes());\n const ss = pad(date.getSeconds());\n\n return `${yyyy}-${mm}-${dd}_${hh}-${min}-${ss}`;\n}\n\n/**\n * Format a timestamp for display: e.g. \"Sun, Mar 1, 1:25 PM\"\n */\nexport function formatTimestamp(timestamp: number): string {\n const date = new Date(timestamp);\n\n return new Intl.DateTimeFormat(\"en-US\", {\n weekday: \"short\",\n month: \"short\",\n day: \"numeric\",\n hour: \"numeric\",\n minute: \"2-digit\",\n hour12: true,\n }).format(date);\n}\n\n/**\n * Escape HTML special characters in a string.\n */\nexport function escapeHtml(str: string): string {\n const div = document.createElement(\"div\");\n div.textContent = str;\n\n return div.innerHTML;\n}\n\n/**\n * Sanitize HTML content using DOMPurify, allowing custom element tags.\n */\nexport function sanitizeHtml(\n dirty: string | Node = \"\",\n options: Config = {},\n): string {\n ensureIframeSanitizerHook();\n ensureCustomElementSanitizerHook();\n\n return getDOMPurify().sanitize(dirty, {\n ADD_TAGS: [\"iframe\", \"figure\", \"figcaption\"],\n ADD_ATTR: [\n \"allow\",\n \"allowfullscreen\",\n \"frameborder\",\n \"scrolling\",\n \"referrerpolicy\",\n \"loading\",\n ],\n CUSTOM_ELEMENT_HANDLING: {\n tagNameCheck: (tagName: string) => isAllowedCustomElement(tagName),\n attributeNameCheck: () => true,\n allowCustomizedBuiltInElements: false,\n },\n ...options,\n });\n}\n\n/**\n * Interface representing the detailed internal navigation parameters\n */\nexport interface ShadowClawNavigateDetail {\n page: string;\n groupId?: string;\n path?: string;\n anchor?: string;\n}\n\n/**\n * Parses a special or relative link and dispatches the 'shadow-claw-navigate' custom event on document.\n * Returns true if the link was handled/intercepted, false otherwise.\n */\nexport function handleSpecialLinkNavigation(\n href: string,\n basePath: string = \"\",\n currentGroupId?: string,\n): boolean {\n let trimmed = href.trim();\n if (!trimmed) {\n return false;\n }\n\n // Handle malformed query strings where '?' is used instead of '&' (e.g. #Page?param1=val1?param2=val2)\n const firstQuestion = trimmed.indexOf(\"?\");\n if (firstQuestion !== -1) {\n const before = trimmed.slice(0, firstQuestion + 1);\n const after = trimmed.slice(firstQuestion + 1);\n trimmed = before + after.replace(/\\?/g, \"&\");\n }\n\n // 1. Check if it is a case-insensitive special link starting with \"/#\" or \"#\" matching standard pages\n if (trimmed.startsWith(\"/#\") || trimmed.startsWith(\"#\")) {\n const withoutHash = trimmed.startsWith(\"/#\")\n ? trimmed.slice(2)\n : trimmed.slice(1);\n\n // Parse parts: Page?query#anchor\n const pagePart = withoutHash.split(/[?#]/, 1)[0];\n const pageName = pagePart.toLowerCase();\n\n // Valid pages\n const validPages = [\"chat\", \"files\", \"tasks\", \"pages\", \"settings\"];\n if (validPages.includes(pageName)) {\n // Create a dummy absolute URL to parse searchParams easily\n const fakeUrl = trimmed.startsWith(\"/#\")\n ? trimmed.replace(/^\\/#/, \"http://local/\")\n : trimmed.replace(/^#/, \"http://local/\");\n const urlLike = new URL(fakeUrl, \"http://local/\");\n\n const groupId =\n urlLike.searchParams.get(\"groupId\") ||\n urlLike.searchParams.get(\"conversationId\") ||\n currentGroupId;\n const path =\n urlLike.searchParams.get(\"file\") ||\n urlLike.searchParams.get(\"path\") ||\n undefined;\n // Support: ?anchor=some-heading, ?anchor=L10-L20, or #fragment\n const anchorParam = urlLike.searchParams.get(\"anchor\");\n const anchor = anchorParam\n ? anchorParam\n : urlLike.hash\n ? urlLike.hash.slice(1)\n : undefined;\n\n const detail: ShadowClawNavigateDetail = {\n page: pageName,\n groupId: groupId || undefined,\n path: path || undefined,\n anchor: anchor || undefined,\n };\n\n document.dispatchEvent(\n new CustomEvent(\"shadow-claw-navigate\", {\n detail,\n bubbles: true,\n composed: true,\n }),\n );\n\n return true;\n }\n }\n\n // 1.5. Check if it is an absolute application route (e.g., /files/room:123/path, /chat/...)\n if (trimmed.startsWith(\"/\")) {\n try {\n const urlLike = new URL(trimmed, window.location.origin);\n const route = parseRouteFromUrl(urlLike, currentGroupId);\n if (route) {\n document.dispatchEvent(\n new CustomEvent(\"shadow-claw-navigate\", {\n detail: {\n page: route.page,\n groupId: route.groupId,\n path: route.path,\n anchor:\n route.anchor ||\n (urlLike.hash ? urlLike.hash.slice(1) : undefined),\n },\n bubbles: true,\n composed: true,\n }),\n );\n\n return true;\n }\n } catch {\n // Fallback to relative workspace path resolution below\n }\n }\n\n // 2. Relative or absolute workspace paths, e.g., \"docs/README.md\"\n // If it does not have a scheme (http, https, mailto, etc.) and is not external:\n const hasScheme = /^[a-zA-Z][a-zA-Z\\d+.-]*:/.test(trimmed);\n if (\n !hasScheme &&\n !trimmed.startsWith(\"//\") &&\n !trimmed.startsWith(\"javascript:\")\n ) {\n // Resolve absolute vs relative\n let resolvedPath = trimmed.split(/[?#]/, 1)[0].replace(/\\\\/g, \"/\");\n const isAbsolute = resolvedPath.startsWith(\"/\");\n resolvedPath = resolvedPath.replace(/^\\/+/, \"\");\n\n const urlLike = new URL(trimmed, \"http://local/\");\n const anchor = urlLike.hash ? urlLike.hash.slice(1) : undefined;\n\n let finalPath = resolvedPath;\n if (!isAbsolute && basePath) {\n const baseParts = basePath\n .replace(/\\\\/g, \"/\")\n .replace(/^\\/+/, \"\")\n .split(\"/\")\n .filter(Boolean);\n baseParts.pop(); // Remove the filename of base path\n\n const pathParts = resolvedPath.split(\"/\");\n const stack = [...baseParts];\n for (const part of pathParts) {\n if (!part || part === \".\") {\n continue;\n }\n\n if (part === \"..\") {\n stack.pop();\n } else {\n stack.push(part);\n }\n }\n\n finalPath = stack.join(\"/\");\n }\n\n if (finalPath) {\n const detail: ShadowClawNavigateDetail = {\n page: \"files\",\n groupId: currentGroupId || undefined,\n path: finalPath,\n anchor: anchor || undefined,\n };\n\n document.dispatchEvent(\n new CustomEvent(\"shadow-claw-navigate\", {\n detail,\n bubbles: true,\n composed: true,\n }),\n );\n\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Convert various input types to an ArrayBuffer for hashing.\n */\nexport function toArrayBuffer(input: HashInput): Promise<ArrayBuffer> {\n if (input instanceof Blob) {\n return input.arrayBuffer();\n }\n\n if (input instanceof ArrayBuffer) {\n return Promise.resolve(input.slice(0) as ArrayBuffer);\n }\n\n const { buffer, byteOffset, byteLength } = input;\n\n return Promise.resolve(\n buffer.slice(byteOffset, byteOffset + byteLength) as ArrayBuffer,\n );\n}\n\n/**\n * Convert an ArrayBuffer to a hex string.\n */\nexport function bufferToHex(buffer: ArrayBuffer): string {\n return Array.from(new Uint8Array(buffer))\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n}\n\n/**\n * Compute the SHA-256 hash and return it as a hex string.\n */\nexport async function computeSha256(input: HashInput): Promise<string> {\n const data = await toArrayBuffer(input);\n const hashBuffer = await crypto.subtle.digest(\"SHA-256\", data);\n\n return bufferToHex(hashBuffer);\n}\n","/// <reference lib=\"dom\" />\nimport type { Config } from \"dompurify\";\n\nimport { escapeHtml, sanitizeHtml } from \"../utils/utils.js\";\n\ntype TrustedMarkup = string | { toString: () => string };\n\ntype TrustedTypesPolicyLike = {\n createHTML: (input: string) => TrustedMarkup;\n createScriptURL?: (input: string) => TrustedMarkup;\n};\n\ntype TrustedTypesFactoryLike = {\n createPolicy: (\n name: string,\n rules: {\n createHTML?: (input: string) => string;\n createScriptURL?: (input: string) => string;\n },\n ) => TrustedTypesPolicyLike;\n getPolicy?: (name: string) => TrustedTypesPolicyLike | null;\n};\n\nconst APP_TRUSTED_TYPES_POLICY_NAME = \"shadowclaw\";\n\nlet cachedPolicy: TrustedTypesPolicyLike | null | undefined;\n\nfunction getTrustedTypesFactory(): TrustedTypesFactoryLike | null {\n const factory = globalThis.trustedTypes;\n if (!factory || typeof factory.createPolicy !== \"function\") {\n return null;\n }\n\n return factory as TrustedTypesFactoryLike;\n}\n\nfunction getTrustedTypesPolicy(): TrustedTypesPolicyLike | null {\n if (cachedPolicy !== undefined) {\n return cachedPolicy;\n }\n\n const factory = getTrustedTypesFactory();\n if (!factory) {\n cachedPolicy = null;\n\n return cachedPolicy;\n }\n\n if (typeof factory.getPolicy === \"function\") {\n const existingPolicy = factory.getPolicy(APP_TRUSTED_TYPES_POLICY_NAME);\n if (existingPolicy) {\n cachedPolicy = existingPolicy;\n\n return cachedPolicy;\n }\n }\n\n try {\n cachedPolicy = factory.createPolicy(APP_TRUSTED_TYPES_POLICY_NAME, {\n // Input is already sanitized by sanitizeToTrustedHtml before policy use.\n // Keep this an identity transform so caller-provided sanitize options\n // (for example blob URL allowlists) are not lost.\n createHTML: (input: string) => input,\n createScriptURL: (input: string) => input,\n });\n } catch {\n if (typeof factory.getPolicy === \"function\") {\n cachedPolicy = factory.getPolicy(APP_TRUSTED_TYPES_POLICY_NAME) ?? null;\n } else {\n cachedPolicy = null;\n }\n }\n\n return cachedPolicy;\n}\n\nexport function getTrustedTypesPolicyName(): string {\n return APP_TRUSTED_TYPES_POLICY_NAME;\n}\n\nexport function sanitizeToTrustedHtml(\n dirty: string | Node = \"\",\n options: Config = {},\n): TrustedMarkup {\n const sanitized = sanitizeHtml(dirty, options);\n const policy = getTrustedTypesPolicy();\n\n if (!policy) {\n return sanitized;\n }\n\n return policy.createHTML(sanitized);\n}\n\nexport function escapeToTrustedHtml(text: string): TrustedMarkup {\n return sanitizeToTrustedHtml(escapeHtml(text));\n}\n\nexport function sanitizeSrcdocHtml(html: string, options: Config = {}): string {\n return sanitizeHtml(html, options);\n}\n\nexport function toTrustedHtmlPresanitized(html: string): TrustedMarkup {\n const policy = getTrustedTypesPolicy();\n\n if (!policy) {\n return html;\n }\n\n return policy.createHTML(html);\n}\n\nexport function setSanitizedHtml(\n element: Element,\n dirty: string | Node = \"\",\n options: Config = {},\n): TrustedMarkup {\n const trustedHtml = sanitizeToTrustedHtml(dirty, options);\n\n element.innerHTML = trustedHtml as string;\n\n return trustedHtml;\n}\n\nexport function setEscapedHtml(element: Element, text: string): TrustedMarkup {\n const trustedHtml = escapeToTrustedHtml(text);\n\n element.innerHTML = trustedHtml as string;\n\n return trustedHtml;\n}\n\nexport function setSanitizedSrcdoc(\n iframe: HTMLIFrameElement,\n dirty: string,\n options: Config = {},\n): TrustedMarkup {\n const trustedHtml = sanitizeToTrustedHtml(dirty, options);\n\n iframe.srcdoc = trustedHtml as string;\n\n return trustedHtml;\n}\n\nexport function setTrustedSrcdoc(\n iframe: HTMLIFrameElement,\n html: string,\n): TrustedMarkup {\n const trustedHtml = toTrustedHtmlPresanitized(html);\n\n iframe.srcdoc = trustedHtml as string;\n\n return trustedHtml;\n}\n\nexport function toTrustedScriptUrl(url: string): TrustedMarkup {\n const policy = getTrustedTypesPolicy();\n\n if (!policy || typeof policy.createScriptURL !== \"function\") {\n return url;\n }\n\n return policy.createScriptURL(url);\n}\n"],"mappings":"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"}
|
package/dist/lib/utils/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { n as __exportAll } from "../rolldown-runtime-DZ8SGSJo.js";
|
|
2
2
|
import { ulid } from "./ulid.js";
|
|
3
|
-
import { c as escapeHtml, d as handleSpecialLinkNavigation, f as sanitizeHtml, l as formatDateForFilename, o as bufferToHex, p as toArrayBuffer, s as computeSha256, u as formatTimestamp } from "../trusted-types-
|
|
3
|
+
import { c as escapeHtml, d as handleSpecialLinkNavigation, f as sanitizeHtml, l as formatDateForFilename, o as bufferToHex, p as toArrayBuffer, s as computeSha256, u as formatTimestamp } from "../trusted-types-Dx0RZlrd.js";
|
|
4
4
|
import { getNamespacedItem, getNamespacedStorageKey, removeNamespacedItem, setNamespacedItem } from "./namespacedStorage.js";
|
|
5
5
|
import { applyJsonPatch } from "./jsonPatch.js";
|
|
6
6
|
import { base64ToBytes, bytesToBase64 } from "./base64.js";
|
package/dist/lib/utils/utils.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { c as escapeHtml, d as handleSpecialLinkNavigation, f as sanitizeHtml, l as formatDateForFilename, o as bufferToHex, p as toArrayBuffer, s as computeSha256, u as formatTimestamp } from "../trusted-types-
|
|
1
|
+
import { c as escapeHtml, d as handleSpecialLinkNavigation, f as sanitizeHtml, l as formatDateForFilename, o as bufferToHex, p as toArrayBuffer, s as computeSha256, u as formatTimestamp } from "../trusted-types-Dx0RZlrd.js";
|
|
2
2
|
|
|
3
3
|
export { bufferToHex, computeSha256, escapeHtml, formatDateForFilename, formatTimestamp, handleSpecialLinkNavigation, sanitizeHtml, toArrayBuffer };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://schemas.agentskills.io/discovery/0.2.0/schema.json",
|
|
3
|
+
"name": "ShadowClaw",
|
|
4
|
+
"description": "ShadowClaw - Browser-native AI agent.",
|
|
5
|
+
"skills": [
|
|
6
|
+
{
|
|
7
|
+
"name": "skill-creator",
|
|
8
|
+
"type": "skill-md",
|
|
9
|
+
"description": "Create, edit, review, and validate ShadowClaw declarative skills and their supporting resources.",
|
|
10
|
+
"url": "../../.agents/skills/main/skill-creator/SKILL.md",
|
|
11
|
+
"digest": "sha256:e9876a79ab38c3a9acd0a990f2a26a578e1971abc2f3cd0eeda1988e6529f684"
|
|
12
|
+
}
|
|
13
|
+
]
|
|
14
|
+
}
|
package/dist/public/AGENTS.md
CHANGED
|
@@ -142,6 +142,7 @@ Markdown and HTML preview work should preserve the Settings-backed iframe host a
|
|
|
142
142
|
### Agent Skills & Declarative Tools
|
|
143
143
|
|
|
144
144
|
- **Agent Skills:** Skills are discovered from `.agents/skills/**/SKILL.md`; preserve required frontmatter validation, duplicate-name diagnostics, the model-invocation opt-out, and the 2,000-directory discovery limit. Update `docs/subsystems/skills.md` when this contract changes.
|
|
145
|
+
- **Agent Skills Discovery Index:** `generateSkillsIndex` (`bin/commands/skills-index.mjs`) generates `.well-known/agent-skills/index.json` complying with Agent Skills Discovery RFC v0.2.0, calculating SHA-256 digests and RFC 3986 relative URLs for skills, tools, and scripts. The build pipeline (`bin/build/build.mjs`) automatically generates this index in `dist/public` indexing both content and bundled skills (such as `skill-creator`) using content site metadata, while keeping `.agents/scripts` and repository indexes synchronized. CLI command: `shadow-claw skills:index [dir]` (alias `agent-skills`).
|
|
145
146
|
- **Declarative Skill Tool Chains:** Skills with `user-invocable: true` support `/skill-name` slash-command routing. Skills with `execution.type: "tools"` dispatch directly to `executeToolChain` on the worker thread via `execute-skill-tools` messages, executing deterministic tool pipelines (with `$pipe` output resolution) without scheduling a Task or calling model LLM prompts. Setting `suppressToast: true` or `suppressOutput: true` on the execution block cascades down to all steps in the tool chain. Default bundled skills include `skill-creator` (with example skills like `toast-random-number` provided in starter templates).
|
|
146
147
|
- **Declarative Tools:** Definitions are loaded from `.agents/tools/main/**/*.json` and support `bash`, `javascript`, or delegated `tool` executors. Preserve built-in-name protection, active runtime allowlists, diagnostics for invalid definitions, and the eight-level delegation limit. Declarative tools (such as `generate_random_number.json` in starter templates) can be provided by content repositories. Update `docs/subsystems/tools.md` when this contract changes.
|
|
147
148
|
- **JS Tool Expression Evaluation:** `executeJavascript` in `src/worker/tools/ui/javascript.ts` automatically evaluates single expressions without an explicit `return` statement by wrapping them in `return (<expression>);` (with fallback to the raw code if syntax errors occur).
|
|
@@ -151,12 +152,12 @@ Markdown and HTML preview work should preserve the Settings-backed iframe host a
|
|
|
151
152
|
- **Custom Element Guards:** `installCustomElementsRegistryGuard` and `installCustomElementDomGuard` (`src/security/custom-element-security.ts`) intercept unauthorized custom element registrations and dynamically strip unapproved custom elements from the DOM tree. Only core elements (`shadow-claw` and `shadow-claw-*`) or elements explicitly allowlisted via `shadow-claw.config.json` (or legacy `site-config.json`) or `CONFIG_KEYS.ALLOWED_CUSTOM_ELEMENTS` are permitted.
|
|
152
153
|
- **Iframe Sandboxing & CSP:** Sandboxed preview iframes omit `allow-same-origin` by default to enforce opaque-origin (`null`) isolation and eliminate Chrome sandbox escape warnings, using a nonce-gated Content Security Policy with domain restrictions generated via `getIframeCsp(nonce)`. Persistent storage for sandboxed custom elements (`IndexedDB` and `localStorage`) is provided via a transparent `postMessage` storage proxy bridge (`iframe-storage-bridge.js`) handled by `dispatchStorageProxyCommand.ts` and `iframe-storage-proxy.ts`.
|
|
153
154
|
- **Iframe Attribute Cleanups:** When rendering preview iframes (such as in `shadow-claw-pages` and `shadow-claw-file-viewer`), rely on `allow="fullscreen"` and omit redundant `allowfullscreen` boolean attributes to avoid browser DevTools precedence warnings.
|
|
154
|
-
- **Approved Script Loading:** Dynamic loading of custom element scripts is restricted to safe protocols and validated host patterns using `loadApprovedCustomElementScript`.
|
|
155
|
+
- **Approved Script Loading & Script Descriptors:** Dynamic loading of custom element scripts is restricted to safe protocols and validated host patterns using `loadApprovedCustomElementScript`. Custom element scripts (`customElements.scripts`) support string URLs and object descriptors `{ src: string, hasInit?: boolean }` (standardized on `src`). The sandboxed preview iframe invokes `init()` exclusively when `hasInit: true` is explicitly configured (`if (typeof m?.init === "function") m.init();`). Helpers: `getApprovedCustomElementScriptDescriptors()` and `getApprovedCustomElementScripts()`.
|
|
155
156
|
|
|
156
157
|
### Component Lifecycle & Storybook Workbench
|
|
157
158
|
|
|
158
159
|
- **Observed Attributes on Custom Elements:** Custom elements extending `ShadowClawElement` that dynamically react to attribute modifications must declare `static observedAttributes` (for example, `ShadowClawEmptyState` observing `["message", "hint", "compact", "warning"]`) so `attributeChangedCallback` triggers component re-rendering.
|
|
159
|
-
- **Storybook Workbench:** Storybook (`npm run storybook` on port 6006, `npm run build:storybook` for static output in `dist/storybook`) uses `@storybook/web-components-vite` (v10) with custom HTML/CSS import attribute handling. Add co-located `*.stories.ts` files for UI components. Storybook defaults to dark mode (`.storybook/preview.ts`) and isolates component visual verification from end-to-end integration tests.
|
|
160
|
+
- **Storybook Workbench:** Storybook (`npm run storybook` on port 6006, `npm run build:storybook` for static output in `dist/storybook`) uses `@storybook/web-components-vite` (v10) with custom HTML/CSS import attribute handling. Add co-located `*.stories.ts` files for UI components. Storybook defaults to dark mode (`.storybook/preview.ts`), covers all 18 A2UI catalog components (`shadow-claw-a2ui.stories.ts`), and isolates component visual verification from end-to-end integration tests.
|
|
160
161
|
- **Toast Inline Mode & Instance API:** `<shadow-claw-toast>` supports an `inline` attribute (`:host([inline])`) for non-fixed rendering inside documentation or story environments, and exposes a `.show(message, options)` instance method alongside `showToast` and `toastStore`.
|
|
161
162
|
|
|
162
163
|
### Declarative Configuration, Storage Namespacing & Dynamic Navigation
|
|
@@ -168,9 +169,10 @@ Markdown and HTML preview work should preserve the Settings-backed iframe host a
|
|
|
168
169
|
### CLI & Dual-Root Build Pipeline
|
|
169
170
|
|
|
170
171
|
- **Dual-Root Path Resolution:** The build toolchain (`bin/build/build.mjs`) cleanly decouples `toolchainRoot` (the ShadowClaw package/repo root) from `contentRoot` (the consumer template project). In-repo builds (`resolve(contentRoot) === resolve(toolchainRoot)`) preserve the standalone in-tree compilation path. CLI/template consumer builds read pre-bundled web assets from `toolchainRoot/dist/public` and inject `pages/`, `shadow-claw.config.json` (or `site-config.json`), `assets/`, `.agents/`, and pretty routes from `contentRoot`, outputting to `<contentRoot>/dist/public`.
|
|
171
|
-
- **CLI Commands (`bin/cli.mjs`):** The `shadow-claw` / `shadowclaw` CLI provides `build`, `dev`, `run`, `serve`, `server` (aliases: `services`, `api`), `init`, `clients`, `send`, `backup`, `tasks`, `webrtc`, and `peer-id` commands. It supports running dev and headless service servers programmatically via `startServer` (`src/server/server.ts`) with custom `--root-path`, `--cache-dir <dir>`, and `--database-dir` arguments.
|
|
172
|
+
- **CLI Commands (`bin/cli.mjs`):** The `shadow-claw` / `shadowclaw` CLI provides `build`, `dev`, `run`, `serve`, `server` (aliases: `services`, `api`), `init`, `clients`, `send`, `backup`, `tasks`, `mcp`, `skills:index` (alias `agent-skills`), `webrtc`, and `peer-id` commands. It supports running dev and headless service servers programmatically via `startServer` (`src/server/server.ts`) with custom `--root-path`, `--cache-dir <dir>`, and `--database-dir` arguments.
|
|
172
173
|
- **Cache Directory Selection & Storage Paths:** When launching `dev`, `run`, `serve`, or `server` and no existing cache is detected, ShadowClaw displays an upfront skip tip and interactively prompts to select between the current working directory (`.cache`), system temporary storage (`node:os` `tmpdir()`), or a custom directory. Prompting can be skipped via `--tmp`, `-y`, `--cache-dir <dir>`, `SHADOWCLAW_TMP`, or `SHADOWCLAW_CACHE_DIR`. Cancellation via SIGINT / Ctrl+C is caught cleanly without error traces. All server storage paths (SQLite databases under `<cacheDir>/database`, TLS certs under `<cacheDir>/tls`, logs under `<cacheDir>/logs`, backups under `<cacheDir>/backups`, control tokens at `<cacheDir>/control-token.json`, and WebRTC IPC sockets at `<cacheDir>/webrtc-ipc.sock`) resolve under the configured `<cacheDir>`.
|
|
173
174
|
- **HTTPS & Control Plane:** Dev/run/serve/server commands accept `--https`, `--cert <path>`, `--key <path>`, and `--ssl-dir <path>` for opt-in HTTPS with auto-generated self-signed certs; control plane commands (`clients`, `send`, `backup`, `tasks`) accept `--https` and `-k, --insecure` to reach an HTTPS control plane server.
|
|
175
|
+
- **Stateless MCP Server & Multi-Client Targeting:** The MCP server engine (`bin/commands/mcp.mjs`, `src/server/mcp/`) dynamically queries connected clients and exposes an optional `clientId` enum on relayed tools restricted to clients supporting each tool. Active client routing can be inspected and switched via `shadowclaw_set_active_client`. Server-side validation rejects unsupported client tool calls early, and client-side control plane handlers strictly validate active conversation allowlists (`allowedTools` / conversation tool tags). The interactive `ask_user` tool is relayed directly to the browser UI with an extended 300s timeout or fulfilled via MRTR.
|
|
174
176
|
- **Naming Conventions:** Refer to the product/brand in prose and documentation as **ShadowClaw**. Use kebab-case **`shadow-claw`** for package name, CLI commands (`npx shadow-claw`), repositories, directory paths, and custom elements.
|
|
175
177
|
|
|
176
178
|
## What to Avoid
|
package/dist/public/README.md
CHANGED
|
@@ -62,7 +62,7 @@ A fully-functional agent runtime built on browser-native technology — the orch
|
|
|
62
62
|
- **Model registry** — Dynamic metadata fetch (context window, modality support)
|
|
63
63
|
- **Attachment capabilities** — Native multimodal delivery with automatic text fallback
|
|
64
64
|
- **Remote MCP** — Discover and execute tools from external MCP servers
|
|
65
|
-
- **Stateless MCP server** — Expose ShadowClaw CLI commands and live browser tools to external agent hosts (Claude Desktop, Cursor, Goose) via STDIO (`npx shadow-claw mcp`) or Streamable HTTP (`POST /mcp`)
|
|
65
|
+
- **Stateless MCP server** — Expose ShadowClaw CLI commands and live browser tools to external agent hosts (Claude Desktop, Cursor, Goose) via STDIO (`npx shadow-claw mcp`) or Streamable HTTP (`POST /mcp`), featuring multi-client tool targeting (`shadowclaw_set_active_client`), per-tool client capability validation, client-side execution guards, and interactive `ask_user` relaying
|
|
66
66
|
- **A2UI interactive surfaces** — Render responsive UI components (Text, Button, TextField, Row/Column layouts) from agents via PeerJS WebRTC with two-way data binding
|
|
67
67
|
- **Multi-Agent Shared State** — Synchronize agent knowledge across participants using `STATE_SNAPSHOT` and `STATE_DELTA` events
|
|
68
68
|
- **Email integration** — IMAP/SMTP support with encrypted credentials
|
|
@@ -243,6 +243,7 @@ Pages complement the **main group MEMORY** (auto-created as `MEMORY.md` on first
|
|
|
243
243
|
## Agent Skills and Declarative Tools
|
|
244
244
|
|
|
245
245
|
- **Workspace Agent Skills** — Discovers `.agents/skills/**/SKILL.md` instruction packages, presents model-invocable skill descriptions in the system prompt, and loads instructions plus bundled resources through `activate_skill`. Default bundled skills include `skill-creator`.
|
|
246
|
+
- **Agent Skills Discovery Index** — Automatically indexes skills, tools, and scripts into `/.well-known/agent-skills/index.json` complying with Agent Skills Discovery RFC v0.2.0, computing SHA-256 digests and RFC 3986 relative URLs. Integrated into static builds (`bin/build/build.mjs`) and runnable via `npx shadow-claw skills:index [dir]` (alias `agent-skills`).
|
|
246
247
|
- **Slash Commands & Declarative Execution Pipelines** — User-invocable skills support `/skill-name` slash-command triggers. Skills with `execution: { type: "tools", tools: [...] }` execute tool pipelines directly on the worker thread via `executeToolChain` without scheduling Tasks or invoking LLM prompts. Supports `$pipe` output chaining and step-level or cascaded `suppressToast: true` and `suppressOutput: true` options.
|
|
247
248
|
- **Declarative Tools** — Content repositories can define executable tools as JSON under `.agents/tools/main/` using sandboxed Bash or JavaScript, or delegate to existing tools without changing ShadowClaw source (e.g. `generate_random_number` in the starter template repository).
|
|
248
249
|
- **Enhanced JS Tool Expression Evaluation** — The `javascript` tool automatically evaluates single expressions without explicit `return` statements by wrapping them in `return (<expression>);`.
|
|
@@ -252,7 +253,7 @@ Pages complement the **main group MEMORY** (auto-created as `MEMORY.md` on first
|
|
|
252
253
|
ShadowClaw packages its UI components as standard Web Components built on TC39 Signals and native Custom Elements:
|
|
253
254
|
|
|
254
255
|
- **Modular Package Exports** — Import pre-bundled components and utilities directly via `shadow-claw/components` (e.g. `ShadowClawToast`, `ShadowClawCard`, `ShadowClawDialog`, `ShadowClawEmptyState`, `ShadowClawPageHeader`) and `shadow-claw/utils` (e.g. `namespacedStorage`, `ulid`). TypeScript declaration files (`.d.ts`) are provided out of the box.
|
|
255
|
-
- **Storybook Workbench** — Interactive visual development environment powered by Storybook (`@storybook/web-components-vite`) with dark mode default, live controls, and isolated component testing (`npm run storybook`).
|
|
256
|
+
- **Storybook Workbench** — Interactive visual development environment powered by Storybook (`@storybook/web-components-vite`) with dark mode default, live controls, and isolated component testing (`npm run storybook`), including all 18 A2UI catalog components and UI primitives.
|
|
256
257
|
- **Reactive Element Lifecycle** — Custom elements extending `ShadowClawElement` observe attributes (`observedAttributes`) for declarative reactive DOM updates.
|
|
257
258
|
|
|
258
259
|
## WebVM (Optional Alpine Linux)
|
|
@@ -277,7 +278,7 @@ ShadowClaw uses **IndexedDB** for structured data (messages, config, tasks) and
|
|
|
277
278
|
- **30-second key expiry** for plaintext operations
|
|
278
279
|
- **No plaintext secrets on disk** — encrypted before storage
|
|
279
280
|
- **Trusted Types enforcement** — idempotent `"default"` policy (`src/security/default-trusted-types-policy.ts`) registered at boot via `theme-init.ts`; `getPolicy()` fallback prevents duplicate-creation errors on module reload
|
|
280
|
-
- **Custom element security guards** — `installCustomElementsRegistryGuard` and `installCustomElementDomGuard` prevent unauthorized custom element registration and dynamic DOM injection, strictly enforcing allowlists from `shadow-claw.config.json` (or `site-config.json`) or storage
|
|
281
|
+
- **Custom element security guards** — `installCustomElementsRegistryGuard` and `installCustomElementDomGuard` prevent unauthorized custom element registration and dynamic DOM injection, strictly enforcing allowlists from `shadow-claw.config.json` (or `site-config.json`) or storage. Script declarations support URLs and `{ src, hasInit }` descriptors (standardized on `src`), invoking `init()` exclusively when `hasInit: true` is configured.
|
|
281
282
|
- **Iframe sandbox & CSP hardening** — sandboxed preview iframes omit `allow-same-origin` by default to enforce opaque-origin (`null`) isolation without Chrome sandbox escape warnings, while injecting a transparent `postMessage` storage proxy bridge (`iframe-storage-bridge.js`) to provide namespaced `IndexedDB` and `localStorage` persistence for custom elements, trapping `ServiceWorker`/`caches` `SecurityError` rejections, polyfilling `showOpenFilePicker`/`showSaveFilePicker`, and relaying programmatic/link navigation and declarative `BroadcastChannel` messages via `IframeBroadcastProxy`
|
|
282
283
|
- **Iframe embed sanitization** — DOMPurify-based iframe allowlisting protects markdown and HTML previews, with Settings-backed host patterns and a safe default host list
|
|
283
284
|
- **SSRF proxy hardening** — `/proxy` blocks non-HTTP/S schemes and private/loopback IP ranges by default; bypassed via `--allow-private-proxy` flag or the authenticated service-worker JSON format
|
|
@@ -430,6 +431,7 @@ npx shadow-claw server --tmp # Run services with tempora
|
|
|
430
431
|
npx shadow-claw server --cache-dir <dir> # Run services with custom cache directory
|
|
431
432
|
npx shadow-claw webrtc listen # Start headless WebRTC DataChannel daemon
|
|
432
433
|
npx shadow-claw peer-id # Get or generate persistent CLI Peer ID
|
|
434
|
+
npx shadow-claw skills:index # Generate or update .well-known/agent-skills/index.json
|
|
433
435
|
```
|
|
434
436
|
|
|
435
437
|
When launching `dev`, `run`, `serve`, or `server` without an existing cache, ShadowClaw prompts interactively to select between the local directory (`.cache`), system temporary storage (`tmpdir()`), or a custom path (skip prompting via `--tmp`, `-y`, `--cache-dir <dir>`, or `SHADOWCLAW_CACHE_DIR`).
|
|
@@ -2240,4 +2240,4 @@ t.exports=function(e){return typeof e==`string`&&e.charAt(0)===``?e.slice(1):
|
|
|
2240
2240
|
`)}});break}case`execute-skill-tools`:{let e=n?.groupId,t=Array.isArray(n?.tools)?n.tools:[];if(!e||t.length===0){W({type:`error`,payload:{groupId:e||`br:main`,error:`Invalid execute-skill-tools payload.`}});break}let{results:i}=await o0(r,e,t,{isManual:!0,isTaskExecution:!1,onStep:e=>{e.suppressToast||W({type:`show-toast`,payload:{message:`Running skill tool: ${e.name}...`,duration:3e3}})}});i.length>0&&W({type:`response`,payload:{groupId:e,text:i.join(`
|
|
2241
2241
|
|
|
2242
2242
|
`)}});break}case`set-storage`:n.storageHandle&&xe(n.storageHandle);break;case`set-vm-mode`:{let e=n?.mode,t=e===`disabled`||e===`auto`||e===`9p`||e===`ext2`,r=Object.prototype.hasOwnProperty.call(n||{},`bootHost`),i=Object.prototype.hasOwnProperty.call(n||{},`networkRelayUrl`);if(t||r||i){b0()&&W({type:`vm-terminal-closed`,payload:{ok:!0}}),t&&Oi(e),r&&Ai(n?.bootHost),i&&ji(n?.networkRelayUrl);let a=t?e:ki();await Ti(),a!==`disabled`&&Promise.resolve(Ci()).catch(e=>{console.warn(`[WebVM] Reboot after mode change failed:`,e)})}break}case`vm-terminal-open`:{let e=typeof n?.groupId==`string`&&n.groupId?n.groupId:w;if(f0){p0=e,W({type:`vm-terminal-opened`,payload:{ok:!0}});break}if(v0)break;v0=!0,(async()=>{let t=null;try{Di().ready||(t=Li(e=>{W({type:`vm-terminal-output`,payload:{chunk:e}})}),await Ci().catch(e=>{console.warn(`[WebVM] Terminal boot failed:`,e)}),t?.(),t=null);let n=Di();if(!n.ready){W({type:`vm-terminal-error`,payload:{error:n.error||`WebVM is still booting.`}});return}let i={db:r,groupId:e};p0=e,f0=Ii(e=>{W({type:`vm-terminal-output`,payload:{chunk:e}})}),W({type:`vm-terminal-opened`,payload:{ok:!0}}),f0.send(`
|
|
2243
|
-
`),y0(i.db,e,{emitSyncedOnSuccess:!0,showWarningOnFailure:!0}),m0=zi(i,()=>{W({type:`vm-workspace-synced`,payload:{groupId:e}})})}catch(e){W({type:`vm-terminal-error`,payload:{error:e instanceof Error?e.message:String(e)}})}finally{v0=!1,t?.()}})();break}case`vm-terminal-input`:{let e=n?.data;if(!f0){W({type:`vm-terminal-error`,payload:{error:`WebVM terminal is not connected.`}});break}typeof e==`string`&&e.length>0&&f0.send(e);break}case`vm-terminal-close`:{let e=typeof n?.groupId==`string`&&n.groupId?n.groupId:p0;b0(),Bi({db:r,groupId:e}).then(()=>{W({type:`vm-workspace-synced`,payload:{groupId:e}})}).catch(()=>{}),W({type:`vm-terminal-closed`,payload:{ok:!0}});break}case`vm-workspace-sync`:{let e=typeof n?.groupId==`string`&&n.groupId?n.groupId:p0;y0(r,e);break}case`vm-workspace-flush`:{let e=typeof n?.groupId==`string`&&n.groupId?n.groupId:p0;Bi({db:r,groupId:e}).then(()=>{W({type:`vm-workspace-synced`,payload:{groupId:e}})}).catch(()=>{});break}case`task-list-response`:{let{groupId:e,tasks:t}=n,r=l0.get(e);r&&(r(t),l0.delete(e));break}case`cancel`:if(n?.groupId){let e=d0.get(n.groupId);e&&(e.abort(),d0.delete(n.groupId))}else{for(let e of d0.values())e.abort();d0.clear()}break;case`update-tools`:{let{groupId:e,enabledTools:t,systemPromptOverride:r}=n;K1(e,t,r);break}case`execute-tool`:try{let t=await c1(r,n.name,n.input,n.groupId);self.postMessage({type:`execute-tool-result`,callId:e.data.callId,result:
|
|
2243
|
+
`),y0(i.db,e,{emitSyncedOnSuccess:!0,showWarningOnFailure:!0}),m0=zi(i,()=>{W({type:`vm-workspace-synced`,payload:{groupId:e}})})}catch(e){W({type:`vm-terminal-error`,payload:{error:e instanceof Error?e.message:String(e)}})}finally{v0=!1,t?.()}})();break}case`vm-terminal-input`:{let e=n?.data;if(!f0){W({type:`vm-terminal-error`,payload:{error:`WebVM terminal is not connected.`}});break}typeof e==`string`&&e.length>0&&f0.send(e);break}case`vm-terminal-close`:{let e=typeof n?.groupId==`string`&&n.groupId?n.groupId:p0;b0(),Bi({db:r,groupId:e}).then(()=>{W({type:`vm-workspace-synced`,payload:{groupId:e}})}).catch(()=>{}),W({type:`vm-terminal-closed`,payload:{ok:!0}});break}case`vm-workspace-sync`:{let e=typeof n?.groupId==`string`&&n.groupId?n.groupId:p0;y0(r,e);break}case`vm-workspace-flush`:{let e=typeof n?.groupId==`string`&&n.groupId?n.groupId:p0;Bi({db:r,groupId:e}).then(()=>{W({type:`vm-workspace-synced`,payload:{groupId:e}})}).catch(()=>{});break}case`task-list-response`:{let{groupId:e,tasks:t}=n,r=l0.get(e);r&&(r(t),l0.delete(e));break}case`cancel`:if(n?.groupId){let e=d0.get(n.groupId);e&&(e.abort(),d0.delete(n.groupId))}else{for(let e of d0.values())e.abort();d0.clear()}break;case`update-tools`:{let{groupId:e,enabledTools:t,systemPromptOverride:r}=n;K1(e,t,r);break}case`execute-tool`:try{let t=q1(n.groupId)?.enabledTools,i=await c1(r,n.name,n.input,n.groupId,{allowedTools:t});self.postMessage({type:`execute-tool-result`,callId:e.data.callId,result:i})}catch(t){self.postMessage({type:`execute-tool-result`,callId:e.data.callId,error:t instanceof Error?t.message:String(t)})}break;case`mcp-reauth-result`:{let{connectionId:e,success:t}=n;u0(e,!!t);break}case`ask-user-response`:{let{id:e,response:t}=n,r=globalThis.pendingAskUserResolvers;r&&r[e]&&(r[e](t??``),delete r[e]);break}case`native-ai-task-response`:{let{id:e,response:t,error:r}=n,i=globalThis.pendingNativeAiResolvers;i&&i[e]&&(r?i[e].reject(Error(r)):i[e].resolve(t),delete i[e]);break}}}Fi(e=>{self.postMessage({type:`vm-status`,payload:e})}),(async()=>{try{let e=await a0(),t=await F(e,j.VM_BOOT_MODE),n=t===`disabled`||t===`9p`||t===`ext2`||t===`auto`?t:`disabled`,r=await F(e,j.VM_BOOT_HOST),i=typeof r==`string`?r:`http://localhost:8888`,a=await F(e,j.VM_NETWORK_RELAY_URL);Oi(n),Ai(i),ji(a),(n===`ext2`||n===`9p`)&&await Ci()}catch(e){console.warn(`[WebVM] Eager boot failed:`,e)}})();function S0(e,t=`info`,n){self.postMessage({type:`show-toast`,payload:{message:e,type:t,duration:n}})}function C0(e,t){S0(e,`success`,t)}function w0(e,t){S0(e,`error`,t)}function T0(e,t){S0(e,`warning`,t)}function E0(e,t){S0(e,`info`,t)}globalThis.showToast=S0,globalThis.showSuccess=C0,globalThis.showError=w0,globalThis.showWarning=T0,globalThis.showInfo=E0,self.onmessage=x0;export{Ea as _,Zc as a,ne as b,dc as c,xs as d,bs as f,ka as g,cs as h,tl as i,Rs as l,ls as m,al as n,Xc as o,_s as p,nl as r,Dc as s,ol as t,ks as u,se as v,te as y};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{r as e}from"./config-CrHchneq.js";import{Ct as t,ht as n}from"./orchestrator-
|
|
1
|
+
import{r as e}from"./config-CrHchneq.js";import{Ct as t,ht as n}from"./orchestrator-DHjdLtqa.js";import{t as r}from"./setConfig-DFMYnYLE.js";async function i(i,a,o,s,c=``,l=0,u=``,d=!0){let f=o.trim(),p=t(s),m=c.trim(),h=Number.isFinite(l)?Math.max(0,Math.floor(l)):0,g=u.trim(),_=!!d;i.peerjsMyPeerId=f,i.peerjsTrustedPeerIds=p,i.peerjsServerHost=m,i.peerjsServerPort=h,i.peerjsServerPath=g,i.peerjsServerSecure=_,await r(a,e.PEERJS_MY_PEER_ID,f),await r(a,e.PEERJS_TRUSTED_PEER_IDS,JSON.stringify(p)),await r(a,e.PEERJS_SERVER_HOST,m),await r(a,e.PEERJS_SERVER_PORT,h?String(h):``),await r(a,e.PEERJS_SERVER_PATH,g),await r(a,e.PEERJS_SERVER_SECURE,_?`true`:`false`);let v=m?{host:m,path:g||void 0,port:h||void 0,secure:_}:{};i.peerjs.stop(),i.peerjs.configure(f,p,v),f&&n(i,`peerjs`)&&i.peerjs.start()}export{i as t};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{r as e}from"./config-CrHchneq.js";import{c as t}from"./app-routes-Bs-riIvr.js";import{n}from"./txPromise-EBECky1b.js";import{t as r}from"./getConfig-D89uJgo5.js";import{i,n as a,t as o}from"./iframe-sanitizer-y2Z1pPwY.js";function s(e=new Date){let t=e=>String(e).padStart(2,`0`);return`${e.getFullYear()}-${t(e.getMonth()+1)}-${t(e.getDate())}_${t(e.getHours())}-${t(e.getMinutes())}-${t(e.getSeconds())}`}function c(e){let t=new Date(e);return new Intl.DateTimeFormat(`en-US`,{weekday:`short`,month:`short`,day:`numeric`,hour:`numeric`,minute:`2-digit`,hour12:!0}).format(t)}function l(e){let t=document.createElement(`div`);return t.textContent=e,t.innerHTML}function u(e=``,t={}){return o(),ae(),a().sanitize(e,{ADD_TAGS:[`iframe`,`figure`,`figcaption`],ADD_ATTR:[`allow`,`allowfullscreen`,`frameborder`,`scrolling`,`referrerpolicy`,`loading`],CUSTOM_ELEMENT_HANDLING:{tagNameCheck:e=>j(e),attributeNameCheck:()=>!0,allowCustomizedBuiltInElements:!1},...t})}function ee(e,n=``,r){let i=e.trim();if(!i)return!1;let a=i.indexOf(`?`);if(a!==-1&&(i=i.slice(0,a+1)+i.slice(a+1).replace(/\?/g,`&`)),i.startsWith(`/#`)||i.startsWith(`#`)){let e=(i.startsWith(`/#`)?i.slice(2):i.slice(1)).split(/[?#]/,1)[0].toLowerCase();if([`chat`,`files`,`tasks`,`pages`,`settings`].includes(e)){let t=i.startsWith(`/#`)?i.replace(/^\/#/,`http://local/`):i.replace(/^#/,`http://local/`),n=new URL(t,`http://local/`),a=n.searchParams.get(`groupId`)||n.searchParams.get(`conversationId`)||r,o=n.searchParams.get(`file`)||n.searchParams.get(`path`)||void 0,s=n.searchParams.get(`anchor`)||(n.hash?n.hash.slice(1):void 0),c={page:e,groupId:a||void 0,path:o||void 0,anchor:s||void 0};return document.dispatchEvent(new CustomEvent(`shadow-claw-navigate`,{detail:c,bubbles:!0,composed:!0})),!0}}if(i.startsWith(`/`))try{let e=new URL(i,window.location.origin),n=t(e,r);if(n)return document.dispatchEvent(new CustomEvent(`shadow-claw-navigate`,{detail:{page:n.page,groupId:n.groupId,path:n.path,anchor:n.anchor||(e.hash?e.hash.slice(1):void 0)},bubbles:!0,composed:!0})),!0}catch{}if(!/^[a-zA-Z][a-zA-Z\d+.-]*:/.test(i)&&!i.startsWith(`//`)&&!i.startsWith(`javascript:`)){let e=i.split(/[?#]/,1)[0].replace(/\\/g,`/`),t=e.startsWith(`/`);e=e.replace(/^\/+/,``);let a=new URL(i,`http://local/`),o=a.hash?a.hash.slice(1):void 0,s=e;if(!t&&n){let t=n.replace(/\\/g,`/`).replace(/^\/+/,``).split(`/`).filter(Boolean);t.pop();let r=e.split(`/`),i=[...t];for(let e of r)!e||e===`.`||(e===`..`?i.pop():i.push(e));s=i.join(`/`)}if(s){let e={page:`files`,groupId:r||void 0,path:s,anchor:o||void 0};return document.dispatchEvent(new CustomEvent(`shadow-claw-navigate`,{detail:e,bubbles:!0,composed:!0})),!0}}return!1}const d=`shadowclaw`;let f;function p(){let e=globalThis.trustedTypes;return!e||typeof e.createPolicy!=`function`?null:e}function m(){if(f!==void 0)return f;let e=p();if(!e)return f=null,f;if(typeof e.getPolicy==`function`){let t=e.getPolicy(d);if(t)return f=t,f}try{f=e.createPolicy(d,{createHTML:e=>e,createScriptURL:e=>e})}catch{f=typeof e.getPolicy==`function`?e.getPolicy(d)??null:null}return f}function h(e=``,t={}){let n=u(e,t),r=m();return r?r.createHTML(n):n}function g(e,t={}){return u(e,t)}function _(e){let t=m();return t?t.createHTML(e):e}function v(e,t=``,n={}){let r=h(t,n);return e.innerHTML=r,r}function te(e,t){let n=_(t);return e.srcdoc=n,n}function y(e){let t=m();return!t||typeof t.createScriptURL!=`function`?e:t.createScriptURL(e)}const b=[],x=[];let S=new Set(b.map(e=>e.toLowerCase().trim())),C=[...x],w=[];function T(e){w=e.map(e=>i(e)).filter(Boolean)}T(C);function E(e){return Array.isArray(e)?e.map(e=>typeof e==`string`?e.toLowerCase().trim():``).filter(Boolean):typeof e==`string`?e.split(/[\n,]+/).map(e=>e.toLowerCase().trim()).filter(Boolean):[]}function D(e){return Array.isArray(e)?e.map(e=>typeof e==`string`?e.trim():``).filter(Boolean):typeof e==`string`?e.split(/[\n,]+/).map(e=>e.trim()).filter(Boolean):[]}function O(e){let t=E(e);S=new Set(t)}function k(e){let t=D(e);C=t,T(t)}function A(){return[...C]}function j(e){if(!e||typeof e!=`string`)return!1;let t=e.trim().toLowerCase();return!t.includes(`-`)||t===`shadow-claw`||t.startsWith(`shadow-claw-`)?!0:S.has(t)}function ne(e){if(!e||typeof e!=`string`)return!1;let t=e.trim();if(!t||/^(?:javascript|data|vbscript):/i.test(t))return!1;if(/^blob:/i.test(t))return!0;try{let e=typeof globalThis<`u`&&globalThis.location?.origin&&globalThis.location.origin!==`null`?globalThis.location.origin:`http://localhost`,n=new URL(t,e);if(n.protocol!==`http:`&&n.protocol!==`https:`)return!1;if(typeof globalThis<`u`&&globalThis.location?.origin&&globalThis.location.origin!==`null`&&n.origin===globalThis.location.origin)return!0;let r=n.hostname.toLowerCase();return w.some(e=>e(r))}catch{return!1}}const M=`allow-modals allow-scripts allow-popups allow-popups-to-escape-sandbox`,N=[`'self'`,`blob:`,`data:`],P=[`'self'`,`blob:`,`data:`],F=[`'self'`,`'unsafe-inline'`,`https:`,`http:`],I=[`'self'`,`blob:`,`data:`,`https:`,`http:`];let L=M,R=[...N],z=[...P],B=[],V=[...F],H=[...I];function U(){return L}function W(e){L=Array.isArray(e)?e.map(e=>typeof e==`string`?e.trim():``).filter(Boolean).join(` `):typeof e==`string`&&e.trim()?e.trim():M}function G(e){let t=A(),n=Y(),r=new Set;for(let e of t)e&&/^(\*\.)?[a-zA-Z0-9.-]+(?::[0-9]+)?$/.test(e)&&(r.add(`https://${e}`),r.add(`http://${e}`));for(let e of n)try{let t=new URL(e,typeof globalThis<`u`&&globalThis.location?.origin?globalThis.location.origin:`http://localhost`);r.add(t.origin)}catch{}let i=[`'nonce-${e}'`,...Array.from(r),...B],a=[...z,...Array.from(r)];return[`script-src ${Array.from(new Set(i)).join(` `)}`,`worker-src ${Array.from(new Set(R)).join(` `)}`,`connect-src ${Array.from(new Set(a)).join(` `)}`,`style-src ${Array.from(new Set(V)).join(` `)}`,`img-src ${Array.from(new Set(H)).join(` `)}`].join(`; `)}function K(e){if(!e||typeof e!=`object`)return;let t=e.allowedCustomElements||(typeof e.customElements==`object`&&!Array.isArray(e.customElements)?e.customElements?.allowedElements:void 0);t!==void 0&&O(t);let n=e.allowedCustomElementDomains||(typeof e.customElements==`object`&&!Array.isArray(e.customElements)?e.customElements?.allowedDomains:void 0);n!==void 0&&k(n),e.security?.iframeSandbox===void 0?(e.security?.allowSameOrigin===!0||e.security?.allowIframeSameOrigin===!0)&&(L.includes(`allow-same-origin`)||(L=`${L} allow-same-origin`.trim())):W(e.security.iframeSandbox),e.security?.workerSrc!==void 0&&(R=D(e.security.workerSrc)),e.security?.connectSrc!==void 0&&(z=D(e.security.connectSrc)),e.security?.scriptSrc!==void 0&&(B=D(e.security.scriptSrc)),e.security?.styleSrc!==void 0&&(V=D(e.security.styleSrc)),e.security?.imgSrc!==void 0&&(H=D(e.security.imgSrc))}function q(){if(typeof document>`u`)return;let e=document.getElementById(`shadow-claw-site-config`);if(e&&e.textContent)try{K(JSON.parse(e.textContent))}catch{}}function J(){if(typeof document>`u`)return[];let e=document.getElementById(`shadow-claw-site-config`);if(!e||!e.textContent)return[];try{let t=JSON.parse(e.textContent),n=typeof t.customElements==`object`&&!Array.isArray(t.customElements)?t.customElements?.scripts:void 0;if(Array.isArray(n))return n.map(e=>({src:typeof e==`string`?e:e?.src,hasInit:typeof e==`object`&&e?!!e.hasInit:!1})).filter(e=>typeof e.src==`string`&&ne(e.src))}catch{}return[]}function Y(){return J().map(e=>e.src)}async function X(t){try{let i=t??await n();if(!i)return;let[a,o]=await Promise.all([r(i,e.ALLOWED_CUSTOM_ELEMENTS),r(i,e.ALLOWED_CUSTOM_ELEMENT_HOST_PATTERNS)]);if(typeof a==`string`&&a.trim())try{O(JSON.parse(a))}catch{O(a)}if(typeof o==`string`&&o.trim())try{k(JSON.parse(o))}catch{k(o)}}catch(e){console.warn(`Failed to load custom element security from db:`,e)}}let Z=null,Q=!1;function re(){q(),!(Q||typeof customElements>`u`||typeof customElements.define!=`function`)&&(Z=customElements.define.bind(customElements),customElements.define=function(e,t,n){if(!j((e||``).toLowerCase().trim())){let t=`[Security] Registration blocked: <${e}> is not an approved custom element.`;throw console.error(t),Error(t)}return Z(e,t,n)},Q=!0)}function ie(e=typeof document<`u`?document.documentElement:null){if(typeof MutationObserver>`u`||!e)return null;if(typeof e.querySelectorAll==`function`)try{e.querySelectorAll(`*`).forEach(e=>{let t=e.tagName?e.tagName.toLowerCase():``;t.includes(`-`)&&!j(t)&&(console.warn(`[Security] Removing existing unapproved custom element: <${t}>`),e.remove())})}catch{}let t=new MutationObserver(e=>{for(let t of e)t.addedNodes.forEach(e=>{if(e.nodeType===1){let t=e,n=t.tagName?t.tagName.toLowerCase():``;n.includes(`-`)&&!j(n)&&(console.warn(`[Security] Removing unapproved custom element: <${n}>`),t.remove())}})});return t.observe(e,{childList:!0,subtree:!0}),t}let $=!1;function ae(){if($)return;let e=a();if(e&&typeof e.addHook==`function`){let t=e=>{if(e&&e.nodeType===1){let t=e,n=(t.tagName||``).toLowerCase();n.includes(`-`)&&!j(n)&&(typeof t.remove==`function`&&t.remove(),t.parentNode&&t.parentNode.removeChild(t))}};e.addHook(`uponSanitizeElement`,t),e.addHook(`afterSanitizeElements`,t),$=!0}}export{ee as _,ie as a,X as c,te as d,_ as f,c as g,s as h,U as i,g as l,l as m,J as n,re as o,y as p,G as r,j as s,K as t,v as u};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
var e=Object.defineProperty,t=(t,n,r)=>n in t?e(t,n,{enumerable:!0,configurable:!0,writable:!0,value:r}):t[n]=r,n=(e,n,r)=>(t(e,typeof n==`symbol`?n:n+``,r),r),r=(e,t,n)=>{if(!t.has(e))throw TypeError(`Cannot `+n)},i=(e,t)=>{if(Object(t)!==t)throw TypeError(`Cannot use the "in" operator on this value`);return e.has(t)},a=(e,t,n)=>{if(t.has(e))throw TypeError(`Cannot add the same private member more than once`);t instanceof WeakSet?t.add(e):t.set(e,n)},o=(e,t,n)=>(r(e,t,`access private method`),n);
|
|
2
|
+
/**
|
|
3
|
+
* @license
|
|
4
|
+
* Copyright Google LLC All Rights Reserved.
|
|
5
|
+
*
|
|
6
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
7
|
+
* found in the LICENSE file at https://angular.io/license
|
|
8
|
+
*/
|
|
9
|
+
function s(e,t){return Object.is(e,t)}
|
|
10
|
+
/**
|
|
11
|
+
* @license
|
|
12
|
+
* Copyright Google LLC All Rights Reserved.
|
|
13
|
+
*
|
|
14
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
15
|
+
* found in the LICENSE file at https://angular.io/license
|
|
16
|
+
*/
|
|
17
|
+
let c=null,l=!1,u=1;const d=Symbol(`SIGNAL`);function f(e){let t=c;return c=e,t}function p(){return c}function m(){return l}const h={version:0,lastCleanEpoch:0,dirty:!1,producerNode:void 0,producerLastReadVersion:void 0,producerIndexOfThis:void 0,nextProducerIndex:0,liveConsumerNode:void 0,liveConsumerIndexOfThis:void 0,consumerAllowSignalWrites:!1,consumerIsAlwaysLive:!1,producerMustRecompute:()=>!1,producerRecomputeValue:()=>{},consumerMarkedDirty:()=>{},consumerOnSignalRead:()=>{}};function g(e){if(l)throw Error(typeof ngDevMode<`u`&&ngDevMode?`Assertion error: signal read during notification phase`:``);if(c===null)return;c.consumerOnSignalRead(e);let t=c.nextProducerIndex++;if(O(c),t<c.producerNode.length&&c.producerNode[t]!==e&&D(c)){let e=c.producerNode[t];E(e,c.producerIndexOfThis[t])}c.producerNode[t]!==e&&(c.producerNode[t]=e,c.producerIndexOfThis[t]=D(c)?T(e,c,t):0),c.producerLastReadVersion[t]=e.version}function _(){u++}function v(e){if(!(!e.dirty&&e.lastCleanEpoch===u)){if(!e.producerMustRecompute(e)&&!w(e)){e.dirty=!1,e.lastCleanEpoch=u;return}e.producerRecomputeValue(e),e.dirty=!1,e.lastCleanEpoch=u}}function y(e){if(e.liveConsumerNode===void 0)return;let t=l;l=!0;try{for(let t of e.liveConsumerNode)t.dirty||x(t)}finally{l=t}}function b(){return c?.consumerAllowSignalWrites!==!1}function x(e){var t;e.dirty=!0,y(e),(t=e.consumerMarkedDirty)==null||t.call(e.wrapper??e)}function S(e){return e&&(e.nextProducerIndex=0),f(e)}function C(e,t){if(f(t),!(!e||e.producerNode===void 0||e.producerIndexOfThis===void 0||e.producerLastReadVersion===void 0)){if(D(e))for(let t=e.nextProducerIndex;t<e.producerNode.length;t++)E(e.producerNode[t],e.producerIndexOfThis[t]);for(;e.producerNode.length>e.nextProducerIndex;)e.producerNode.pop(),e.producerLastReadVersion.pop(),e.producerIndexOfThis.pop()}}function w(e){O(e);for(let t=0;t<e.producerNode.length;t++){let n=e.producerNode[t],r=e.producerLastReadVersion[t];if(r!==n.version||(v(n),r!==n.version))return!0}return!1}function T(e,t,n){var r;if(k(e),O(e),e.liveConsumerNode.length===0){(r=e.watched)==null||r.call(e.wrapper);for(let t=0;t<e.producerNode.length;t++)e.producerIndexOfThis[t]=T(e.producerNode[t],e,t)}return e.liveConsumerIndexOfThis.push(n),e.liveConsumerNode.push(t)-1}function E(e,t){var n;if(k(e),O(e),typeof ngDevMode<`u`&&ngDevMode&&t>=e.liveConsumerNode.length)throw Error(`Assertion error: active consumer index ${t} is out of bounds of ${e.liveConsumerNode.length} consumers)`);if(e.liveConsumerNode.length===1){(n=e.unwatched)==null||n.call(e.wrapper);for(let t=0;t<e.producerNode.length;t++)E(e.producerNode[t],e.producerIndexOfThis[t])}let r=e.liveConsumerNode.length-1;if(e.liveConsumerNode[t]=e.liveConsumerNode[r],e.liveConsumerIndexOfThis[t]=e.liveConsumerIndexOfThis[r],e.liveConsumerNode.length--,e.liveConsumerIndexOfThis.length--,t<e.liveConsumerNode.length){let n=e.liveConsumerIndexOfThis[t],r=e.liveConsumerNode[t];O(r),r.producerIndexOfThis[n]=t}}function D(e){return e.consumerIsAlwaysLive||(e?.liveConsumerNode?.length??0)>0}function O(e){e.producerNode??=[],e.producerIndexOfThis??=[],e.producerLastReadVersion??=[]}function k(e){e.liveConsumerNode??=[],e.liveConsumerIndexOfThis??=[]}
|
|
18
|
+
/**
|
|
19
|
+
* @license
|
|
20
|
+
* Copyright Google LLC All Rights Reserved.
|
|
21
|
+
*
|
|
22
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
23
|
+
* found in the LICENSE file at https://angular.io/license
|
|
24
|
+
*/
|
|
25
|
+
function A(e){if(v(e),g(e),e.value===P)throw e.error;return e.value}function j(e){let t=Object.create(F);t.computation=e;let n=()=>A(t);return n[d]=t,n}const M=Symbol(`UNSET`),N=Symbol(`COMPUTING`),P=Symbol(`ERRORED`),F={...h,value:M,dirty:!0,error:null,equal:s,producerMustRecompute(e){return e.value===M||e.value===N},producerRecomputeValue(e){if(e.value===N)throw Error(`Detected cycle in computations.`);let t=e.value;e.value=N;let n=S(e),r,i=!1;try{r=e.computation.call(e.wrapper),i=t!==M&&t!==P&&e.equal.call(e.wrapper,t,r)}catch(t){r=P,e.error=t}finally{C(e,n)}if(i){e.value=t;return}e.value=r,e.version++}};
|
|
26
|
+
/**
|
|
27
|
+
* @license
|
|
28
|
+
* Copyright Google LLC All Rights Reserved.
|
|
29
|
+
*
|
|
30
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
31
|
+
* found in the LICENSE file at https://angular.io/license
|
|
32
|
+
*/
|
|
33
|
+
function I(){throw Error()}let L=I;function R(){L()}
|
|
34
|
+
/**
|
|
35
|
+
* @license
|
|
36
|
+
* Copyright Google LLC All Rights Reserved.
|
|
37
|
+
*
|
|
38
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
39
|
+
* found in the LICENSE file at https://angular.io/license
|
|
40
|
+
*/
|
|
41
|
+
function z(e){let t=Object.create(H);t.value=e;let n=()=>(g(t),t.value);return n[d]=t,n}function B(){return g(this),this.value}function V(e,t){b()||R(),e.equal.call(e.wrapper,e.value,t)||(e.value=t,U(e))}const H={...h,equal:s,value:void 0};function U(e){e.version++,_(),y(e)}
|
|
42
|
+
/**
|
|
43
|
+
* @license
|
|
44
|
+
* Copyright 2024 Bloomberg Finance L.P.
|
|
45
|
+
*
|
|
46
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
47
|
+
* you may not use this file except in compliance with the License.
|
|
48
|
+
* You may obtain a copy of the License at
|
|
49
|
+
*
|
|
50
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
51
|
+
*
|
|
52
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
53
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
54
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
55
|
+
* See the License for the specific language governing permissions and
|
|
56
|
+
* limitations under the License.
|
|
57
|
+
*/
|
|
58
|
+
const W=Symbol(`node`);var G;(e=>{var t,r,s,c;class l{constructor(i,o={}){a(this,r),n(this,t);let s=z(i)[d];if(this[W]=s,s.wrapper=this,o){let t=o.equals;t&&(s.equal=t),s.watched=o[e.subtle.watched],s.unwatched=o[e.subtle.unwatched]}}get(){if(!(0,e.isState)(this))throw TypeError(`Wrong receiver type for Signal.State.prototype.get`);return B.call(this[W])}set(t){if(!(0,e.isState)(this))throw TypeError(`Wrong receiver type for Signal.State.prototype.set`);if(m())throw Error(`Writes to signals not permitted during Watcher callback`);let n=this[W];V(n,t)}}t=W,r=new WeakSet,e.isState=e=>typeof e==`object`&&i(r,e),e.State=l;class u{constructor(t,r){a(this,c),n(this,s);let i=j(t)[d];if(i.consumerAllowSignalWrites=!0,this[W]=i,i.wrapper=this,r){let t=r.equals;t&&(i.equal=t),i.watched=r[e.subtle.watched],i.unwatched=r[e.subtle.unwatched]}}get(){if(!(0,e.isComputed)(this))throw TypeError(`Wrong receiver type for Signal.Computed.prototype.get`);return A(this[W])}}s=W,c=new WeakSet,e.isComputed=e=>typeof e==`object`&&i(c,e),e.Computed=u,(t=>{var r,s,c,l;function u(e){let t,n=null;try{n=f(null),t=e()}finally{f(n)}return t}t.untrack=u;function d(t){if(!(0,e.isComputed)(t)&&!(0,e.isWatcher)(t))throw TypeError(`Called introspectSources without a Computed or Watcher argument`);return t[W].producerNode?.map(e=>e.wrapper)??[]}t.introspectSources=d;function m(t){if(!(0,e.isComputed)(t)&&!(0,e.isState)(t))throw TypeError(`Called introspectSinks without a Signal argument`);return t[W].liveConsumerNode?.map(e=>e.wrapper)??[]}t.introspectSinks=m;function _(t){if(!(0,e.isComputed)(t)&&!(0,e.isState)(t))throw TypeError(`Called hasSinks without a Signal argument`);let n=t[W].liveConsumerNode;return n?n.length>0:!1}t.hasSinks=_;function v(t){if(!(0,e.isComputed)(t)&&!(0,e.isWatcher)(t))throw TypeError(`Called hasSources without a Computed or Watcher argument`);let n=t[W].producerNode;return n?n.length>0:!1}t.hasSources=v;class y{constructor(e){a(this,s),a(this,c),n(this,r);let t=Object.create(h);t.wrapper=this,t.consumerMarkedDirty=e,t.consumerIsAlwaysLive=!0,t.consumerAllowSignalWrites=!1,t.producerNode=[],this[W]=t}watch(...t){if(!(0,e.isWatcher)(this))throw TypeError(`Called unwatch without Watcher receiver`);o(this,c,l).call(this,t);let n=this[W];n.dirty=!1;let r=f(n);for(let e of t)g(e[W]);f(r)}unwatch(...t){if(!(0,e.isWatcher)(this))throw TypeError(`Called unwatch without Watcher receiver`);o(this,c,l).call(this,t);let n=this[W];O(n);for(let e=n.producerNode.length-1;e>=0;e--)if(t.includes(n.producerNode[e].wrapper)){E(n.producerNode[e],n.producerIndexOfThis[e]);let t=n.producerNode.length-1;if(n.producerNode[e]=n.producerNode[t],n.producerIndexOfThis[e]=n.producerIndexOfThis[t],n.producerNode.length--,n.producerIndexOfThis.length--,n.nextProducerIndex--,e<n.producerNode.length){let t=n.producerIndexOfThis[e],r=n.producerNode[e];k(r),r.liveConsumerIndexOfThis[t]=e}}}getPending(){if(!(0,e.isWatcher)(this))throw TypeError(`Called getPending without Watcher receiver`);return this[W].producerNode.filter(e=>e.dirty).map(e=>e.wrapper)}}r=W,s=new WeakSet,c=new WeakSet,l=function(t){for(let n of t)if(!(0,e.isComputed)(n)&&!(0,e.isState)(n))throw TypeError(`Called watch/unwatch without a Computed or State argument`)},e.isWatcher=e=>i(s,e),t.Watcher=y;function b(){return p()?.wrapper}t.currentComputed=b,t.watched=Symbol(`watched`),t.unwatched=Symbol(`unwatched`)})(e.subtle||={})})(G||={});export{G as t};
|
|
@@ -21,37 +21,37 @@ How the core pieces fit together.
|
|
|
21
21
|
|
|
22
22
|
Detailed docs for each major subsystem.
|
|
23
23
|
|
|
24
|
-
| Document | What it covers
|
|
25
|
-
| ----------------------------------------------------------------------- |
|
|
26
|
-
| [Shell Emulator](subsystems/shell.md) | JS shell via `just-bash` AST evaluation, OPFS bridge, supported commands
|
|
27
|
-
| [WebVM](subsystems/vm.md) | v86 Alpine Linux, boot modes, exclusivity guard, terminal bridge, 9p sync
|
|
28
|
-
| [Git Integration](subsystems/git.md) | isomorphic-git on native filesystem handles, merge conflicts, credentials
|
|
29
|
-
| [Channel System](subsystems/channels.md) | Channel registry, browser/Telegram/iMessage channels, router, multi-channel flow
|
|
30
|
-
| [Remote MCP](subsystems/remote-mcp.md) | External MCP servers, tool discovery, authentication, JSON-RPC protocol, OAuth reconnection
|
|
31
|
-
| [Accounts & Credentials](subsystems/accounts.md) | Service account management, credential storage, auth bridges
|
|
32
|
-
| [Crypto & Secrets](subsystems/crypto.md) | AES-256-GCM key storage, runtime key handling, environment hardening; idempotent default Trusted Types policy
|
|
33
|
-
| [Tools & Profiles](subsystems/tools.md) | Tool definitions, execution dispatch, profiles, tool configuration panel, adding new tools
|
|
34
|
-
| [Agent Skills](subsystems/skills.md) | Workspace-local skill discovery, slash-command routing, declarative tool execution pipelines, output/toast suppression, resources, and static publishing
|
|
35
|
-
| [Notifications & Scheduling](subsystems/notifications.md) | Web Push, VAPID, server-side SQLite scheduler, recursion guards
|
|
36
|
-
| [Providers & Rate Limiting](subsystems/providers.md) | LLM provider registry, adapter pattern, Prompt API (default), dynamic CPU/WASM fallback, CacheStorage model downloads, and 30s auto-closing error dialogs
|
|
37
|
-
| [Electron Desktop](subsystems/electron.md) | Desktop app architecture, in-process server, power management
|
|
38
|
-
| [Reactive UI & Component Library](subsystems/reactive-ui.md) | Signals, `ShadowClawElement`, `reconcileList`, Web Components, stores, ESM package exports, and Storybook workbench
|
|
39
|
-
| [Model Registry & Capabilities](subsystems/providers.md#model-registry) | Dynamic model metadata fetching and modality capability detection
|
|
40
|
-
| [Attachment Capabilities](subsystems/attachment-capabilities.md) | MIME-aware attachment handling and native vs fallback delivery
|
|
41
|
-
| [Chat Template Sanitizer](subsystems/sanitizer.md) | Strip control tokens and structural markers from local model output
|
|
42
|
-
| [A2UI Interactive Surfaces](subsystems/a2ui.md) | A2UI v1.0 catalog renderer, component rendering, PeerJS surface delivery, data binding
|
|
43
|
-
| [AGUI Events & Adapter](subsystems/agui.md) | Translates orchestrator EventBus events to standardized AG-UI protocol events for UI visibility
|
|
44
|
-
| [Trusted Types Tinyfill](subsystems/trusted-types-tinyfill.md) | Polyfill for Trusted Types API, browser compatibility, security rationale
|
|
45
|
-
| [Email Integration](subsystems/email.md) | IMAP/SMTP support with encrypted credentials
|
|
46
|
-
| [WebMCP](subsystems/webmcp.md) | Browser's Model Context Protocol integration and tool execution, supporting Chrome 154+ object input schemas, getWebMcpTools querying, and graceful degradation
|
|
47
|
-
| [Pages System](subsystems/pages.md) | Workspace-relative pages rendering, sidebar navigation, static site seeding, pretty paths, same-origin route validation, and production asset inlining
|
|
48
|
-
| [Custom Element Security](subsystems/custom-element-security.md) | Custom element registry & DOM guards, allowlists, iframe sandbox policies, and nonce-gated CSP
|
|
49
|
-
| [LiteRT Provider](subsystems/providers.md) | In-browser WebGPU inference provider using Google LiteRT-LM
|
|
50
|
-
| [CLI & Static Site Publishing](subsystems/cli.md) | `shadow-claw` CLI commands (`build`, `dev`, `serve`, `init`, `clients`, `send`, `backup`, `tasks`), dual-root path resolution, and npm packaging
|
|
51
|
-
| [Control Plane & Client Bridge](subsystems/control-plane.md) | Dual SSE/WebSocket control plane for CLI driving and persistent execution surface
|
|
52
|
-
| [Stateless MCP Server](subsystems/mcp-server.md) | Official Stateless Model Context Protocol (2026-07-28) server for external agent hosts, CLI driving, and dynamic browser tool relay
|
|
53
|
-
| [File Backup Subsystem](subsystems/backup.md) | Remote workspace and OPFS file backups to server storage
|
|
54
|
-
| [OpenAPI & Endpoint Discoverability](subsystems/openapi.md) | Official OpenAPI 3.1 specification, interactive Scalar documentation UI, and route-coverage contract testing
|
|
24
|
+
| Document | What it covers |
|
|
25
|
+
| ----------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
26
|
+
| [Shell Emulator](subsystems/shell.md) | JS shell via `just-bash` AST evaluation, OPFS bridge, supported commands |
|
|
27
|
+
| [WebVM](subsystems/vm.md) | v86 Alpine Linux, boot modes, exclusivity guard, terminal bridge, 9p sync |
|
|
28
|
+
| [Git Integration](subsystems/git.md) | isomorphic-git on native filesystem handles, merge conflicts, credentials |
|
|
29
|
+
| [Channel System](subsystems/channels.md) | Channel registry, browser/Telegram/iMessage channels, router, multi-channel flow |
|
|
30
|
+
| [Remote MCP](subsystems/remote-mcp.md) | External MCP servers, tool discovery, authentication, JSON-RPC protocol, OAuth reconnection |
|
|
31
|
+
| [Accounts & Credentials](subsystems/accounts.md) | Service account management, credential storage, auth bridges |
|
|
32
|
+
| [Crypto & Secrets](subsystems/crypto.md) | AES-256-GCM key storage, runtime key handling, environment hardening; idempotent default Trusted Types policy |
|
|
33
|
+
| [Tools & Profiles](subsystems/tools.md) | Tool definitions, execution dispatch, profiles, tool configuration panel, adding new tools |
|
|
34
|
+
| [Agent Skills](subsystems/skills.md) | Workspace-local skill discovery, slash-command routing, declarative tool execution pipelines, output/toast suppression, discovery index generation (.well-known/agent-skills/index.json), resources, and static publishing |
|
|
35
|
+
| [Notifications & Scheduling](subsystems/notifications.md) | Web Push, VAPID, server-side SQLite scheduler, recursion guards |
|
|
36
|
+
| [Providers & Rate Limiting](subsystems/providers.md) | LLM provider registry, adapter pattern, Prompt API (default), dynamic CPU/WASM fallback, CacheStorage model downloads, and 30s auto-closing error dialogs |
|
|
37
|
+
| [Electron Desktop](subsystems/electron.md) | Desktop app architecture, in-process server, power management |
|
|
38
|
+
| [Reactive UI & Component Library](subsystems/reactive-ui.md) | Signals, `ShadowClawElement`, `reconcileList`, Web Components, stores, ESM package exports, and Storybook workbench |
|
|
39
|
+
| [Model Registry & Capabilities](subsystems/providers.md#model-registry) | Dynamic model metadata fetching and modality capability detection |
|
|
40
|
+
| [Attachment Capabilities](subsystems/attachment-capabilities.md) | MIME-aware attachment handling and native vs fallback delivery |
|
|
41
|
+
| [Chat Template Sanitizer](subsystems/sanitizer.md) | Strip control tokens and structural markers from local model output |
|
|
42
|
+
| [A2UI Interactive Surfaces](subsystems/a2ui.md) | A2UI v1.0 catalog renderer, component rendering, PeerJS surface delivery, data binding, and Storybook workbench coverage for all 18 components |
|
|
43
|
+
| [AGUI Events & Adapter](subsystems/agui.md) | Translates orchestrator EventBus events to standardized AG-UI protocol events for UI visibility |
|
|
44
|
+
| [Trusted Types Tinyfill](subsystems/trusted-types-tinyfill.md) | Polyfill for Trusted Types API, browser compatibility, security rationale |
|
|
45
|
+
| [Email Integration](subsystems/email.md) | IMAP/SMTP support with encrypted credentials |
|
|
46
|
+
| [WebMCP](subsystems/webmcp.md) | Browser's Model Context Protocol integration and tool execution, supporting Chrome 154+ object input schemas, getWebMcpTools querying, and graceful degradation |
|
|
47
|
+
| [Pages System](subsystems/pages.md) | Workspace-relative pages rendering, sidebar navigation, static site seeding, pretty paths, same-origin route validation, and production asset inlining |
|
|
48
|
+
| [Custom Element Security](subsystems/custom-element-security.md) | Custom element registry & DOM guards, allowlists, script descriptors ({ src, hasInit }), iframe sandbox policies, and nonce-gated CSP |
|
|
49
|
+
| [LiteRT Provider](subsystems/providers.md) | In-browser WebGPU inference provider using Google LiteRT-LM |
|
|
50
|
+
| [CLI & Static Site Publishing](subsystems/cli.md) | `shadow-claw` CLI commands (`build`, `dev`, `serve`, `server`, `init`, `clients`, `send`, `backup`, `tasks`, `mcp`, `skills:index`), dual-root path resolution, and npm packaging |
|
|
51
|
+
| [Control Plane & Client Bridge](subsystems/control-plane.md) | Dual SSE/WebSocket control plane for CLI driving and persistent execution surface |
|
|
52
|
+
| [Stateless MCP Server](subsystems/mcp-server.md) | Official Stateless Model Context Protocol (2026-07-28) server for external agent hosts, multi-client tool targeting, CLI driving, and dynamic browser tool relay |
|
|
53
|
+
| [File Backup Subsystem](subsystems/backup.md) | Remote workspace and OPFS file backups to server storage |
|
|
54
|
+
| [OpenAPI & Endpoint Discoverability](subsystems/openapi.md) | Official OpenAPI 3.1 specification, interactive Scalar documentation UI, and route-coverage contract testing |
|
|
55
55
|
|
|
56
56
|
### Guides
|
|
57
57
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!doctype html><html lang="en"><head><title>ShadowClaw</title><meta charset="utf-8"><meta name="revision" content="
|
|
1
|
+
<!doctype html><html lang="en"><head><title>ShadowClaw</title><meta charset="utf-8"><meta name="revision" content="d4528f07589dc2f45cb9926f9bbd5966080d46dc" /><meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover"><meta name="description" content="ShadowClaw - Browser-native AI agent." /><meta name="theme-color" content="#fff" /><meta http-equiv="origin-trial" content="A88uTgESYWcVgREKVReKB6jom41uP8TzW6ei3jNdidf+Xl6ONqKRjKNBphsxhrZmdkakLK3oXDgkGq53rtZYSAMAAAB2eyJvcmlnaW4iOiJodHRwczovL3h0LW1sLmdpdGh1Yi5pbzo0NDMiLCJmZWF0dXJlIjoiV2ViTUNQIiwiZXhwaXJ5IjoxNzk0ODczNjAwLCJpc1N1YmRvbWFpbiI6dHJ1ZSwiaXNUaGlyZFBhcnR5Ijp0cnVlfQ=="><base href="/shadow-claw/"><script id="shadow-claw-site-config" type="application/json">{"$schema":"https:\u002f\u002fjson-schema.org\u002fdraft\u002f2020-12\u002fschema","site":{"title":"ShadowClaw","description":"ShadowClaw - Browser-native AI agent.","themeColor":"#fff"},"branding":{"faviconPath":"assets\u002ficons\u002ffavicon.ico","appleTouchIconPath":"assets\u002ficons\u002f180.png","notFoundPath":"404.html"},"pwa":{"manifestPath":"manifest.json","themeColor":"#000000"},"sitemapPath":"sitemap.xml","assets":["assets"],"sidebar":{"pagesHidden":false,"chatHidden":false,"tasksHidden":false,"filesHidden":false,"defaultPage":"pages"},"settings":{"defaultToolsProfile":"__builtin_default"}}</script>
|
|
2
2
|
<script>var ShadowClawThemeInit=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});function t(){if(globalThis.trustedTypes)return;let e=new Map;globalThis.trustedTypes={createPolicy:(t,n)=>{if(e.has(t))throw TypeError(`Policy with name "${t}" already exists.`);let r={createHTML:n.createHTML?e=>n.createHTML(e):void 0,createScriptURL:n.createScriptURL?e=>n.createScriptURL(e):void 0,createScript:n.createScript?e=>n.createScript(e):void 0};return e.set(t,r),r},getPolicy:t=>e.get(t)??null,isHTML:e=>typeof e==`string`||e instanceof Object&&`toString`in e,isScriptURL:e=>typeof e==`string`||e instanceof Object&&`toString`in e,isScript:e=>typeof e==`string`||e instanceof Object&&`toString`in e}}let n=`__shadowClawDefaultTrustedTypesPolicyState`,r=`default`,i=`shadowclaw-sandbox`;function a(){let e=globalThis;return e[n]||(e[n]={initialized:!1,policy:null}),e[n]}function o(){let e=a();if(e.initialized)return;let t=Reflect.get(globalThis,`trustedTypes`);if(!(!t||typeof t.createPolicy!=`function`)){e.initialized=!0;try{if(typeof t.getPolicy==`function`){let n=t.getPolicy(r);if(n){e.policy=n;return}}let n=!1;if(typeof t.getPolicyNames==`function`){let e=t.getPolicyNames();Array.isArray(e)&&e.includes(r)&&(n=!0)}if(n){if(typeof t.getPolicy==`function`){let n=t.getPolicy(i);if(n){e.policy=n;return}}e.policy=t.createPolicy(i,{createHTML:e=>e,createScriptURL:e=>e});return}e.policy=t.createPolicy(r,{createHTML:e=>e,createScriptURL:e=>e})}catch{typeof t.getPolicy==`function`&&(e.policy=t.getPolicy(r)??t.getPolicy(i)??null)}}}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 s(e){return e.replace(/^\/+|\/+$/g,``)}function c(e){let t=s(e);return t.endsWith(`/index.html`)?t=t.slice(0,-11):t.endsWith(`index.html`)&&(t=t.slice(0,-10)),s(t)}function l(e){let t=s(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 u(){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 d(e,t){let n=t||u();if(!n||!n.routes)return null;let r=c(e);if(!r)return null;for(let[e,t]of Object.entries(n.routes))if(!(!t||!t.prettyPath)&&c(t.prettyPath)===r)return l(e);return null}let f=null,ee=new Set([`chat`,`files`,`tasks`,`pages`,`settings`,`tools`,`channels`]);function te(){f=null}typeof globalThis<`u`&&(globalThis.__applyBasePathCacheReset=te);function p(){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=m();return!n||n===`/`?``:n.replace(/^\/+|\/+$/g,``).replace(/[^a-zA-Z0-9_-]/g,`-`)}function m(){if(f!==null)return f;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 f=`/`,f;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 f=`/`,f;if(!n)return f=`/`+e[0]+`/`,f}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+=`/`),f=t,f}catch{}}}let t=e.pathname||`/`;if(t===`/`)return f=`/`,f;let n=t.split(`/`).filter(Boolean),r=n.findIndex(e=>ee.has(e.toLowerCase()));return r>=0?r===0?(f=`/`,f):(f=`/`+n.slice(0,r).join(`/`)+`/`,f):d(t)?(f=`/`,f):n.length===1&&!t.includes(`.`)?(f=`/`+n[0]+`/`,f):(f=`/`,f)}function ne(e){let t=e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`);return RegExp(`(^|\\s)@${t}\\b`,`i`)}ne(`ShadowClaw`);function re(){let e=p();return e?`shadowclaw-${e}`:`shadowclaw`}re();let h={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`}}};Object.fromEntries(Object.values(h).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(h).map(e=>[e.providerId,{providerId:e.providerId,modes:e.modes,defaultMode:e.defaultMode}])),new Promise(e=>{});
|
|
3
3
|
/*! @license DOMPurify 3.4.13 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.13/LICENSE */
|
|
4
4
|
function g(e,t){(t==null||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n<t;n++)r[n]=e[n];return r}function ie(e){if(Array.isArray(e))return e}function _(e,t){var n=e==null?null:typeof Symbol<`u`&&e[Symbol.iterator]||e[`@@iterator`];if(n!=null){var r,i,a,o,s=[],c=!0,l=!1;try{if(a=(n=n.call(e)).next,t!==0)for(;!(c=(r=a.call(n)).done)&&(s.push(r.value),s.length!==t);c=!0);}catch(e){l=!0,i=e}finally{try{if(!c&&n.return!=null&&(o=n.return(),Object(o)!==o))return}finally{if(l)throw i}}return s}}function v(){throw TypeError(`Invalid attempt to destructure non-iterable instance.
|