shadow-claw 1.23.4 → 1.24.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.
Files changed (267) hide show
  1. package/README.md +42 -5
  2. package/bin/cli.mjs +166 -0
  3. package/bin/cli.test.mjs +60 -0
  4. package/bin/commands/backup.mjs +70 -0
  5. package/bin/commands/clients.mjs +41 -0
  6. package/bin/commands/peer-id.mjs +76 -0
  7. package/bin/commands/peer-id.test.mjs +104 -0
  8. package/bin/commands/send.mjs +51 -0
  9. package/bin/commands/tasks.mjs +59 -0
  10. package/bin/commands/webrtc-listen.mjs +254 -0
  11. package/bin/prerender-dsd-shell/prerender-dsd-shell.test.mjs +46 -0
  12. package/bin/rolldown.config.test.mjs +401 -8
  13. package/bin/utils/control-client.mjs +243 -0
  14. package/bin/utils/control-client.test.mjs +83 -0
  15. package/bin/utils/ulid.mjs +45 -0
  16. package/bin/utils/webrtc-control-client.mjs +699 -0
  17. package/bin/utils/webrtc-control-client.test.mjs +318 -0
  18. package/dist/electron/main.cjs +1 -1
  19. package/dist/public/AGENTS.md +12 -3
  20. package/dist/public/README.md +42 -5
  21. package/dist/public/agent.worker.js +50 -50
  22. package/dist/public/backup-controller-CvzDOq4T.js +1 -0
  23. package/dist/public/components/settings/shadow-claw-control-plane/shadow-claw-control-plane.css +1 -0
  24. package/dist/public/components/settings/shadow-claw-control-plane/shadow-claw-control-plane.html +1 -0
  25. package/dist/public/components/settings/shadow-claw-task-server/shadow-claw-task-server.css +1 -1
  26. package/dist/public/components/settings/shadow-claw-task-server/shadow-claw-task-server.html +1 -1
  27. package/dist/public/components/shadow-claw-settings/shadow-claw-settings.html +1 -1
  28. package/dist/public/config-CrHchneq.js +1 -0
  29. package/dist/public/{configurePeerJs-B4Ym9VMj.js → configurePeerJs-UfQ6HUlv.js} +1 -1
  30. package/dist/public/{connections-BEn6JYdu.js → connections-DvbgdkaB.js} +1 -1
  31. package/dist/public/{custom-element-security-DdYlUozq.js → custom-element-security-NCqQ5lmC.js} +1 -1
  32. package/dist/public/docs/README.md +3 -1
  33. package/dist/public/docs/architecture/overview.md +12 -12
  34. package/dist/public/docs/decisions/bundled-typescript-architecture.md +1 -1
  35. package/dist/public/docs/decisions/indexeddb-and-opfs-storage.md +2 -2
  36. package/dist/public/docs/decisions/native-web-components-and-signals.md +1 -1
  37. package/dist/public/docs/example/article/index.html +1 -1
  38. package/dist/public/docs/guides/configuring-messaging-channels.md +41 -6
  39. package/dist/public/docs/guides/server-development-configuration.md +24 -0
  40. package/dist/public/docs/publishing/index.html +1 -1
  41. package/dist/public/docs/skill-creator/index.html +1 -1
  42. package/dist/public/docs/subsystems/backup.md +56 -0
  43. package/dist/public/docs/subsystems/cli.md +124 -0
  44. package/dist/public/docs/subsystems/control-plane.md +169 -0
  45. package/dist/public/docs/subsystems/notifications.md +54 -2
  46. package/dist/public/docs/subsystems/reactive-ui.md +1 -0
  47. package/dist/public/downloadGroupFile-9niHBj9n.js +1 -0
  48. package/dist/public/e2e/README.md +5 -1
  49. package/dist/public/{e2e-bridge-BQ4KzUt5.js → e2e-bridge-D28tOggc.js} +1 -1
  50. package/dist/public/file-viewer-BVg3B_JH.js +1 -0
  51. package/dist/public/getAllTasks-vpBlgGsc.js +1 -0
  52. package/dist/public/getGroupDir-P1h9wl6S.js +1 -0
  53. package/dist/public/{git-DEUvQTUA.js → git-7Vq_SJSp.js} +2 -2
  54. package/dist/public/{iframe-sanitizer-0NS4XLOd.js → iframe-sanitizer-y2Z1pPwY.js} +1 -1
  55. package/dist/public/index.html +1 -1
  56. package/dist/public/index.js +1 -1
  57. package/dist/public/{initChatSplitResize-llGkSJXg.js → initChatSplitResize-CzV2E7Ul.js} +1 -1
  58. package/dist/public/initControlPlane-DKNht5ph.js +1 -0
  59. package/dist/public/llms.txt +1 -1
  60. package/dist/public/main/index.html +1 -1
  61. package/dist/public/main/memory/index.html +1 -1
  62. package/dist/public/{markdown-CyYR4App.js → markdown-SiFq0PTy.js} +1 -1
  63. package/dist/public/{mcp-reconnect-CSx4aMwy.js → mcp-reconnect-9emsqGRe.js} +1 -1
  64. package/dist/public/{memoryStorage-Dwg8NhAZ.js → memoryStorage-C0KvLNUp.js} +1 -1
  65. package/dist/public/orchestrator-8w9-exfN.js +15 -0
  66. package/dist/public/{orchestrator-DRfEudXk.js → orchestrator-tHWrqi_F.js} +676 -676
  67. package/dist/public/{peerjs-CurBuKvk.js → peerjs-D8hck8Ih.js} +1 -1
  68. package/dist/public/push-client-D0lkwrK0.js +1 -0
  69. package/dist/public/service-worker/fetch-proxy.js +1 -1
  70. package/dist/public/service-worker/push-handler.js +1 -1
  71. package/dist/public/service-worker.js +1 -1
  72. package/dist/public/{shadow-claw-5IHaDDMJ.js → shadow-claw-Cs8wbk3d.js} +2 -2
  73. package/dist/public/{shadow-claw-accounts-CZvz4iQz.js → shadow-claw-accounts-B4sBTQEj.js} +1 -1
  74. package/dist/public/{shadow-claw-channels-BKXFyRS3.js → shadow-claw-channels-mY-JMvib.js} +1 -1
  75. package/dist/public/{shadow-claw-chat-CCLuhWdI.js → shadow-claw-chat-DGPKpmU5.js} +6 -6
  76. package/dist/public/shadow-claw-control-plane-B1F0jnPk.js +199 -0
  77. package/dist/public/{shadow-claw-conversations-DV-87UUh.js → shadow-claw-conversations-0Q-VC_7N.js} +1 -1
  78. package/dist/public/shadow-claw-dialog-BrOwAOdk.js +1 -0
  79. package/dist/public/{shadow-claw-file-viewer-Dk2dbAUd.js → shadow-claw-file-viewer-AZfG4D-u.js} +1 -1
  80. package/dist/public/shadow-claw-files-Bx4v0Ggp.js +840 -0
  81. package/dist/public/{shadow-claw-git-DRA7_qck.js → shadow-claw-git-DD7iQRxq.js} +2 -2
  82. package/dist/public/{shadow-claw-integrations-1Xu5snuV.js → shadow-claw-integrations-D7ufpwhX.js} +1 -1
  83. package/dist/public/{shadow-claw-llm-6Aci1BDP.js → shadow-claw-llm-CfeDBZKB.js} +2 -2
  84. package/dist/public/{shadow-claw-mcp-remote-BUjAeVXC.js → shadow-claw-mcp-remote-kY4LMFNN.js} +1 -1
  85. package/dist/public/{shadow-claw-networking-FQe4nCp3.js → shadow-claw-networking-e0EarDVs.js} +1 -1
  86. package/dist/public/{shadow-claw-notifications-DV2wibL3.js → shadow-claw-notifications-BkxuDEQI.js} +3 -3
  87. package/dist/public/{shadow-claw-page-header-B63S4HTU.js → shadow-claw-page-header-DMKbkv3s.js} +1 -1
  88. package/dist/public/{shadow-claw-pages-DE3jWj5K.js → shadow-claw-pages-BWSE91I1.js} +1 -1
  89. package/dist/public/{shadow-claw-settings-S6DAKfYx.js → shadow-claw-settings-D6lPBn6m.js} +10 -3
  90. package/dist/public/{shadow-claw-storage-DaacA3_K.js → shadow-claw-storage-Crs6LY15.js} +2 -2
  91. package/dist/public/{shadow-claw-task-server-IjKX042p.js → shadow-claw-task-server-CKm_Ennl.js} +25 -4
  92. package/dist/public/{shadow-claw-tasks-Cy0JYmGU.js → shadow-claw-tasks-Czx6Ucf5.js} +1 -1
  93. package/dist/public/{shadow-claw-terminal-znAt7HyP.js → shadow-claw-terminal-Dmtr9KY1.js} +1 -1
  94. package/dist/public/{shadow-claw-tools-Cutv9Si5.js → shadow-claw-tools-t3Qq4TDO.js} +1 -1
  95. package/dist/public/{shadow-claw-webvm-BH8mPDEu.js → shadow-claw-webvm-CIuSywD_.js} +1 -1
  96. package/dist/public/{syncWebMcpRegistration-BqJxaMGR.js → syncWebMcpRegistration-DzC80wVG.js} +1 -1
  97. package/dist/server.js +286 -189
  98. package/package.json +2 -1
  99. package/src/__mocks__/jszip.cjs +15 -5
  100. package/src/components/settings/shadow-claw-accounts/shadow-claw-accounts.test.ts +35 -0
  101. package/src/components/settings/shadow-claw-control-plane/shadow-claw-control-plane.css +117 -0
  102. package/src/components/settings/shadow-claw-control-plane/shadow-claw-control-plane.html +81 -0
  103. package/src/components/settings/shadow-claw-control-plane/shadow-claw-control-plane.test.ts +127 -0
  104. package/src/components/settings/shadow-claw-control-plane/shadow-claw-control-plane.ts +292 -0
  105. package/src/components/settings/shadow-claw-git/shadow-claw-git.test.ts +72 -0
  106. package/src/components/settings/shadow-claw-integrations/connection-test-auth.test.ts +20 -30
  107. package/src/components/settings/shadow-claw-integrations/shadow-claw-integrations.test.ts +116 -0
  108. package/src/components/settings/shadow-claw-llm/model-ranking.test.ts +143 -51
  109. package/src/components/settings/shadow-claw-llm/shadow-claw-llm.test.ts +307 -4
  110. package/src/components/settings/shadow-claw-llm/shadow-claw-llm.ts +8 -8
  111. package/src/components/settings/shadow-claw-mcp-remote/shadow-claw-mcp-remote.test.ts +31 -0
  112. package/src/components/settings/shadow-claw-notifications/shadow-claw-notifications.test.ts +3 -0
  113. package/src/components/settings/shadow-claw-notifications/shadow-claw-notifications.ts +43 -9
  114. package/src/components/settings/shadow-claw-peerjs/shadow-claw-peerjs.test.ts +186 -0
  115. package/src/components/settings/shadow-claw-storage/shadow-claw-storage.test.ts +76 -0
  116. package/src/components/settings/shadow-claw-task-server/shadow-claw-task-server.css +23 -2
  117. package/src/components/settings/shadow-claw-task-server/shadow-claw-task-server.html +1 -1
  118. package/src/components/settings/shadow-claw-task-server/shadow-claw-task-server.test.ts +19 -0
  119. package/src/components/settings/shadow-claw-task-server/shadow-claw-task-server.ts +2 -1
  120. package/src/components/shadow-claw/utils/initChatSplitResize.test.ts +180 -13
  121. package/src/components/shadow-claw/utils/setupEffects.test.ts +39 -0
  122. package/src/components/shadow-claw-a2ui/catalog/basic/shared.test.ts +117 -0
  123. package/src/components/shadow-claw-a2ui/shadow-claw-a2ui.test.ts +1159 -1
  124. package/src/components/shadow-claw-chat/shadow-claw-chat.test.ts +1523 -19
  125. package/src/components/shadow-claw-chat/utils/escapeHtml.test.ts +13 -0
  126. package/src/components/shadow-claw-chat/utils/getPeerChatDisplayStatus.test.ts +20 -0
  127. package/src/components/shadow-claw-conversations/shadow-claw-conversations.test.ts +494 -0
  128. package/src/components/shadow-claw-dialog/shadow-claw-dialog.ts +13 -2
  129. package/src/components/shadow-claw-file-viewer/shadow-claw-file-viewer.test.ts +273 -0
  130. package/src/components/shadow-claw-files/shadow-claw-files.test.ts +408 -1
  131. package/src/components/shadow-claw-pdf-viewer/utils/installGetOrInsertComputedPolyfill.test.ts +65 -0
  132. package/src/components/shadow-claw-settings/shadow-claw-settings.html +7 -0
  133. package/src/components/shadow-claw-settings/shadow-claw-settings.test.ts +221 -0
  134. package/src/components/shadow-claw-settings/shadow-claw-settings.ts +2 -0
  135. package/src/components/shadow-claw-tasks/shadow-claw-tasks.test.ts +407 -0
  136. package/src/components/shadow-claw-terminal/utils/consumeEscapeSequence.test.ts +58 -0
  137. package/src/components/shadow-claw-tools/shadow-claw-tools.test.ts +80 -0
  138. package/src/config/config.ts +10 -0
  139. package/src/core/backup-controller.test.ts +101 -0
  140. package/src/core/backup-controller.ts +166 -0
  141. package/src/core/control-plane-client.test.ts +415 -0
  142. package/src/core/control-plane-client.ts +441 -0
  143. package/src/core/orchestrator/orchestrator.ts +4 -0
  144. package/src/core/orchestrator/utils/compactContext.test.ts +78 -0
  145. package/src/core/orchestrator/utils/operations/channel.test.ts +254 -9
  146. package/src/core/orchestrator/utils/operations/provider.test.ts +326 -34
  147. package/src/core/orchestrator/utils/operations/task.ts +46 -15
  148. package/src/core/orchestrator/utils/operations/vm.test.ts +114 -2
  149. package/src/core/orchestrator/utils/syncProxyConfigToServiceWorker.test.ts +60 -0
  150. package/src/core/utils/initControlPlane.test.ts +276 -0
  151. package/src/core/utils/initControlPlane.ts +437 -0
  152. package/src/core/utils/initializeApp.ts +18 -0
  153. package/src/db/getSession.test.ts +13 -1
  154. package/src/server/app.test.ts +12 -0
  155. package/src/server/app.ts +20 -8
  156. package/src/server/client-registry.test.ts +250 -0
  157. package/src/server/client-registry.ts +477 -0
  158. package/src/server/config.ts +15 -0
  159. package/src/server/control-plane-types.ts +120 -0
  160. package/src/server/control-plane.test.ts +508 -0
  161. package/src/server/control-plane.ts +607 -0
  162. package/src/server/middleware/pna.test.ts +15 -3
  163. package/src/server/middleware/pna.ts +6 -2
  164. package/src/server/routes/backup.test.ts +277 -0
  165. package/src/server/routes/backup.ts +291 -0
  166. package/src/server/routes/integrations-email.test.ts +532 -0
  167. package/src/server/routes/llamafile.test.ts +172 -48
  168. package/src/server/routes/mesh-llm.test.ts +192 -0
  169. package/src/server/routes/ollama.test.ts +49 -0
  170. package/src/server/server-peer.test.ts +86 -0
  171. package/src/server/server-peer.ts +330 -0
  172. package/src/server/server.ts +37 -0
  173. package/src/server/services/llamafile-manager.test.ts +230 -6
  174. package/src/server/services/transformers-runtime.test.ts +186 -18
  175. package/src/server/webrtc.test.ts +21 -0
  176. package/src/server/webrtc.ts +72 -0
  177. package/src/service-worker/fetch-proxy-rules.test.ts +41 -0
  178. package/src/service-worker/fetch-proxy-rules.ts +15 -8
  179. package/src/service-worker/fetch-proxy.test.ts +64 -1
  180. package/src/service-worker/fetch-proxy.ts +18 -2
  181. package/src/service-worker/push-handler.ts +39 -0
  182. package/src/shell/vm.test.ts +744 -12
  183. package/src/storage/ensureMainGroupIndex.test.ts +153 -0
  184. package/src/storage/getGroupFile.test.ts +66 -0
  185. package/src/storage/memoryStorage.test.ts +82 -2
  186. package/src/storage/writeGroupFileBytes.test.ts +143 -0
  187. package/src/stores/chat-ui.test.ts +20 -0
  188. package/src/stores/orchestrator.test.ts +134 -0
  189. package/src/stores/toast.test.ts +50 -0
  190. package/src/stores/tools.test.ts +147 -10
  191. package/src/subsystems/channels/imessage.test.ts +59 -0
  192. package/src/subsystems/channels/peerjs.test.ts +500 -62
  193. package/src/subsystems/channels/peerjs.ts +71 -4
  194. package/src/subsystems/channels/telegram.test.ts +177 -0
  195. package/src/subsystems/email/catalog.test.ts +1 -0
  196. package/src/subsystems/email/connections.test.ts +39 -1
  197. package/src/subsystems/git/credentials.test.ts +160 -825
  198. package/src/subsystems/git/git.test.ts +463 -0
  199. package/src/subsystems/notifications/__mocks__/node-sqlite.cjs +12 -1
  200. package/src/subsystems/notifications/push-client.test.ts +5 -1
  201. package/src/subsystems/notifications/push-client.ts +50 -12
  202. package/src/subsystems/notifications/push-routes.test.ts +33 -0
  203. package/src/subsystems/notifications/push-routes.ts +23 -0
  204. package/src/subsystems/notifications/push-store.ts +1 -1
  205. package/src/subsystems/providers/builtin-ai-tasks.test.ts +74 -0
  206. package/src/subsystems/providers/litert-lm-provider.test.ts +212 -51
  207. package/src/subsystems/providers/model-registry.test.ts +115 -0
  208. package/src/subsystems/providers/providers.test.ts +94 -1
  209. package/src/subsystems/providers/transformers-js-provider.test.ts +279 -0
  210. package/src/subsystems/providers/utils/createModelCacheFetch.test.ts +2 -2
  211. package/src/subsystems/providers/utils/downloadModelToCache.test.ts +2 -2
  212. package/src/subsystems/providers/utils/loadModelStream.test.ts +2 -2
  213. package/src/subsystems/skills/activateSkill.test.ts +101 -0
  214. package/src/subsystems/skills/discoverSkills.test.ts +251 -0
  215. package/src/subsystems/tools/cron.test.ts +64 -0
  216. package/src/subsystems/tools/declarative.test.ts +255 -89
  217. package/src/testing/assets/v86.9pfs/libv86.mjs +63 -0
  218. package/src/testing/assets/v86.ext2/libv86.mjs +3 -0
  219. package/src/testing/assets_missing_v86/assets/v86.9pfs/libv86.mjs +1 -0
  220. package/src/testing/jest-setup.ts +328 -40
  221. package/src/testing/mock-v86.mjs +49 -0
  222. package/src/ui/a2ui/types.ts +4 -3
  223. package/src/ui/a2ui/utils/normaliseComponentsToMap.test.ts +102 -0
  224. package/src/ui/a2ui/utils/resolveChildIds.test.ts +63 -0
  225. package/src/ui/a2ui/utils/resolveDynamicNumber.test.ts +13 -1
  226. package/src/worker/tools/bash/utils/waitForVMReady.test.ts +40 -0
  227. package/src/worker/tools/email/email.test.ts +193 -8
  228. package/src/worker/tools/email/utils/asEmailAttachmentInputs.test.ts +47 -0
  229. package/src/worker/tools/git/git.test.ts +214 -3
  230. package/src/worker/tools/rooms/invite-to-room.test.ts +54 -0
  231. package/src/worker/tools/rooms/leave-room.test.ts +40 -0
  232. package/src/worker/tools/rooms/list-room-members.test.ts +75 -0
  233. package/src/worker/tools/tasks/tasks-utils.test.ts +22 -0
  234. package/src/worker/tools/ui/ask-user.test.ts +50 -0
  235. package/src/worker/tools/ui/clear-chat.test.ts +19 -0
  236. package/src/worker/tools/ui/get-current-time.test.ts +19 -0
  237. package/src/worker/tools/ui/javascript.test.ts +116 -0
  238. package/src/worker/tools/ui/list-components.test.ts +10 -0
  239. package/src/worker/tools/ui/list-tool-profiles.test.ts +42 -0
  240. package/src/worker/tools/ui/manage-tools.test.ts +34 -0
  241. package/src/worker/tools/ui/render-component.test.ts +346 -0
  242. package/src/worker/tools/ui/send-notification.test.ts +40 -0
  243. package/src/worker/tools/ui/utils/getAllowFullInternetAccess.test.ts +31 -0
  244. package/src/worker/tools/ui/utils/parseBooleanConfig.test.ts +25 -0
  245. package/src/worker/tools/workspace/attach-file.test.ts +84 -0
  246. package/src/worker/tools/workspace/copy-file.test.ts +69 -0
  247. package/src/worker/tools/workspace/create-directory.test.ts +40 -0
  248. package/src/worker/tools/workspace/delete-file.test.ts +73 -0
  249. package/src/worker/tools/workspace/diff-files.test.ts +94 -0
  250. package/src/worker/tools/workspace/list-files.test.ts +36 -0
  251. package/src/worker/tools/workspace/move-file.test.ts +66 -0
  252. package/src/worker/tools/workspace/open-file.test.ts +57 -0
  253. package/src/worker/tools/workspace/patch-file.test.ts +81 -0
  254. package/src/worker/tools/workspace/read-file.test.ts +116 -0
  255. package/src/worker/tools/workspace/send-file.test.ts +82 -0
  256. package/src/worker/tools/workspace/update-memory.test.ts +29 -0
  257. package/src/worker/tools/workspace/write-file.test.ts +28 -0
  258. package/src/worker/utils/rate-limit.test.ts +199 -0
  259. package/src/worker/utils/tool-state.test.ts +29 -0
  260. package/src/worker/utils/transformers-js-utils.test.ts +59 -0
  261. package/dist/public/config-BYAuxx3j.js +0 -1
  262. package/dist/public/downloadGroupFile-Drdvg1Ev.js +0 -1
  263. package/dist/public/file-viewer-CcZYw-PS.js +0 -1
  264. package/dist/public/orchestrator-o9Fj67BN.js +0 -15
  265. package/dist/public/push-client-Dii-vPEh.js +0 -1
  266. package/dist/public/shadow-claw-dialog-ByyHgxM9.js +0 -1
  267. package/dist/public/shadow-claw-files-BpfpJIuP.js +0 -840
@@ -0,0 +1 @@
1
+ import{t as e}from"./ulid-BY7rQVLN.js";import{t}from"./buffer-9oRIc-5Z.js";var n=t();function r(e){try{let t=new URL(e).hostname.toLowerCase();if(t===`localhost`||t===`127.0.0.1`||t===`::1`||t===`[::1]`)return`loopback`;if(/^(10\.|172\.(1[6-9]|2\d|3[01])\.|192\.168\.)/.test(t))return`private`}catch{}}var i=class{_clientId;_token;_serverBaseUrl;_fileEnumerator;_fileReader;_onProgress;constructor(e){this._clientId=e.clientId,this._token=e.token,this._serverBaseUrl=e.serverBaseUrl||``,this._fileEnumerator=e.fileEnumerator||(async()=>[]),this._fileReader=e.fileReader||(async()=>null),this._onProgress=e.onProgress}async initiate(){let t=e(),i=await this._fileEnumerator(),a=0,o=0,s={"Content-Type":`application/json`};this._token&&(s[`x-control-token`]=this._token);for(let e=0;e<i.length;e++){let c=i[e],l=await this._fileReader(c);if(l==null)continue;let u,d=`utf-8`;typeof l==`string`?(u=l,a+=n.Buffer.from(l,`utf-8`).length):(u=n.Buffer.from(l).toString(`base64`),d=`base64`,a+=l.byteLength||l.length||0);let f=`${this._serverBaseUrl}/api/backup/upload`,p={method:`POST`,headers:s,body:JSON.stringify({clientId:this._clientId,backupId:t,path:c,content:u,encoding:d})},m=r(f);m&&(p.targetAddressSpace=m);let h=await fetch(f,p);if(!h.ok){let e=await h.text().catch(()=>h.statusText);throw Error(`Upload failed for ${c} (${h.status}): ${e}`)}o++,this._onProgress?.({current:o,total:i.length,file:c})}let c=`${this._serverBaseUrl}/api/backup/complete`,l={method:`POST`,headers:s,body:JSON.stringify({clientId:this._clientId,backupId:t,fileCount:o,totalBytes:a})},u=r(c);u&&(l.targetAddressSpace=u);let d=await fetch(c,l);if(!d.ok){let e=await d.text().catch(()=>d.statusText);throw Error(`Failed to complete backup: ${e}`)}return{success:!0,backupId:t,fileCount:o,totalBytes:a}}};export{i as BackupController};
@@ -0,0 +1 @@
1
+ *,:after,:before{font-family:var(--shadow-claw-font-sans)}:host{display:block}.settings-section{margin-bottom:1.75rem}.settings-section h3{align-items:center;display:flex;font-size:1rem;font-weight:600;gap:.5rem;margin:0 0 .75rem}.form-group{margin-bottom:1rem}.form-label{display:block;font-size:.8125rem;font-weight:500;margin-bottom:.375rem}.form-input,.form-label,.form-select{color:var(--shadow-claw-text-primary)}.form-input,.form-select{background-color:var(--shadow-claw-bg-primary);border:.0625rem solid var(--shadow-claw-border-color);border-radius:var(--shadow-claw-radius-s,.625rem);box-sizing:border-box;font-family:var(--shadow-claw-font-sans);font-size:var(--shadow-claw-font-size-sm);padding:.625rem .75rem;transition:border-color .15s,box-shadow .15s;width:100%}.form-input:focus,.form-select:focus{border-color:var(--shadow-claw-accent-primary);box-shadow:0 0 0 .125rem rgba(0,0,0,.06);outline:none}.form-toggle{align-items:center;display:flex;gap:.75rem}.form-toggle input[type=checkbox]{accent-color:var(--shadow-claw-accent-primary);cursor:pointer;height:1.125rem;width:1.125rem}.form-toggle .form-label{cursor:pointer;display:inline;margin-bottom:0}.form-helper{color:var(--shadow-claw-text-tertiary);font-size:.75rem;line-height:1.45;margin-top:.375rem;max-width:48rem}.form-helper code{background-color:var(--shadow-claw-bg-secondary);border:.0625rem solid var(--shadow-claw-border-color);border-radius:.375rem;color:var(--shadow-claw-text-primary);font-family:var(--shadow-claw-font-mono,monospace);font-size:.72rem;padding:.08rem .3rem}.save-btn{background-color:var(--shadow-claw-text-primary);border:none;border-radius:62.5rem;color:var(--shadow-claw-bg-primary);cursor:pointer;font-size:var(--shadow-claw-font-size-sm);font-weight:600;margin-top:.75rem;padding:.625rem 1.5rem;transition:background-color .15s cubic-bezier(.33,1,.68,1)}.save-btn:hover{background-color:var(--shadow-claw-accent-primary);color:var(--shadow-claw-on-primary)}.save-btn:focus-visible{outline:.125rem solid var(--shadow-claw-accent-primary);outline-offset:.125rem}
@@ -0,0 +1 @@
1
+ <template><div class="settings-section"> <h3>🎛️ Control Plane</h3> <div class="form-group"> <div class="form-helper"> Configure the remote bridge allowing external tools, CLI commands (<code>shadow-claw</code>), and MCP clients to discover, query, and communicate with this client instance. </div> </div> <div class="form-group"> <div class="form-toggle"> <input data-setting="control-plane-enabled-toggle" id="control-plane-enabled-toggle" type="checkbox"> <label class="form-label" for="control-plane-enabled-toggle"> Enable Remote Control Plane Client </label> </div> <div class="form-helper"> Allow the <code>shadow-claw</code> CLI and remote server commands to discover and communicate with this client. When disabled, this client operates in standalone/serverless mode without opening a control plane connection. </div> </div> <div class="form-group"> <label class="form-label" for="control-plane-url"> Control Plane Server URL </label> <input class="form-input" data-setting="control-plane-url-input" id="control-plane-url" placeholder="http://127.0.0.1:8888" spellcheck="false" type="url"> <div class="form-helper"> The base URL of the ShadowClaw server or local dev instance for the CLI and remote control plane (e.g. <code>http://127.0.0.1:8888</code> or <code>https://my-server.example.com</code>). Defaults to <code>127.0.0.1:8888</code> when hosted on static sites (like GitHub Pages), or same-origin on server deployments. </div> <button class="save-btn" data-action="save-control-plane-url"> 💾 Save Control Plane URL </button> </div> <div class="form-group"> <label class="form-label" for="control-plane-transport"> Control Plane Transport </label> <select class="form-select" id="control-plane-transport" data-setting="control-plane-transport-select"> <option value="sse"> Server-Sent Events (SSE + HTTP POST) — Recommended / Proxy Friendly (Default) </option> <option value="websocket">WebSocket (Full Duplex)</option> </select> <div class="form-helper"> Select the protocol used for the CLI and remote client control plane. SSE + HTTP POST works through standard reverse proxies and firewalls without requiring connection upgrades. </div> <button class="save-btn" data-action="save-control-plane-transport"> 💾 Save Transport </button> </div> </div></template>
@@ -1 +1 @@
1
- *,:after,:before{font-family:var(--shadow-claw-font-sans)}:host{display:block}.settings-section{margin-bottom:1.75rem}.settings-section h3{align-items:center;display:flex;font-size:1rem;font-weight:600;gap:.5rem;margin:0 0 .75rem}.form-group{margin-bottom:1rem}.form-label{display:block;font-size:.8125rem;font-weight:500;margin-bottom:.375rem}.form-input,.form-label{color:var(--shadow-claw-text-primary)}.form-input{background-color:var(--shadow-claw-bg-primary);border:.0625rem solid var(--shadow-claw-border-color);border-radius:var(--shadow-claw-radius-s,.625rem);box-sizing:border-box;font-family:var(--shadow-claw-font-sans);font-size:var(--shadow-claw-font-size-sm);padding:.625rem .75rem;transition:border-color .15s,box-shadow .15s;width:100%}.form-input:focus{border-color:var(--shadow-claw-accent-primary);box-shadow:0 0 0 .125rem rgba(0,0,0,.06);outline:none}.form-helper{color:var(--shadow-claw-text-tertiary);font-size:.75rem;line-height:1.45;margin-top:.375rem;max-width:48rem}.form-helper code{background-color:var(--shadow-claw-bg-secondary);border:.0625rem solid var(--shadow-claw-border-color);border-radius:.375rem;color:var(--shadow-claw-text-primary);font-family:var(--shadow-claw-font-mono,monospace);font-size:.72rem;padding:.08rem .3rem}.save-btn{background-color:var(--shadow-claw-text-primary);border:none;border-radius:62.5rem;color:var(--shadow-claw-bg-primary);cursor:pointer;font-size:var(--shadow-claw-font-size-sm);font-weight:600;margin-top:.75rem;padding:.625rem 1.5rem;transition:background-color .15s cubic-bezier(.33,1,.68,1)}.save-btn:hover{background-color:var(--shadow-claw-accent-primary);color:var(--shadow-claw-on-primary)}.save-btn:focus-visible{outline:.125rem solid var(--shadow-claw-accent-primary);outline-offset:.125rem}
1
+ *,:after,:before{font-family:var(--shadow-claw-font-sans)}:host{display:block}.settings-section{margin-bottom:1.75rem}.settings-section h3{align-items:center;display:flex;font-size:1rem;font-weight:600;gap:.5rem;margin:0 0 .75rem}.form-group{margin-bottom:1rem}.form-label{display:block;font-size:.8125rem;font-weight:500;margin-bottom:.375rem}.form-input,.form-label,.form-select{color:var(--shadow-claw-text-primary)}.form-input,.form-select{background-color:var(--shadow-claw-bg-primary);border:.0625rem solid var(--shadow-claw-border-color);border-radius:var(--shadow-claw-radius-s,.625rem);box-sizing:border-box;font-family:var(--shadow-claw-font-sans);font-size:var(--shadow-claw-font-size-sm);padding:.625rem .75rem;transition:border-color .15s,box-shadow .15s;width:100%}.form-input:focus,.form-select:focus{border-color:var(--shadow-claw-accent-primary);box-shadow:0 0 0 .125rem rgba(0,0,0,.06);outline:none}.form-toggle{align-items:center;display:flex;gap:.75rem}.form-toggle input[type=checkbox]{accent-color:var(--shadow-claw-accent-primary);cursor:pointer;height:1.125rem;width:1.125rem}.form-toggle .form-label{cursor:pointer;display:inline;margin-bottom:0}.form-helper{color:var(--shadow-claw-text-tertiary);font-size:.75rem;line-height:1.45;margin-top:.375rem;max-width:48rem}.form-helper code{background-color:var(--shadow-claw-bg-secondary);border:.0625rem solid var(--shadow-claw-border-color);border-radius:.375rem;color:var(--shadow-claw-text-primary);font-family:var(--shadow-claw-font-mono,monospace);font-size:.72rem;padding:.08rem .3rem}.save-btn{background-color:var(--shadow-claw-text-primary);border:none;border-radius:62.5rem;color:var(--shadow-claw-bg-primary);cursor:pointer;font-size:var(--shadow-claw-font-size-sm);font-weight:600;margin-top:.75rem;padding:.625rem 1.5rem;transition:background-color .15s cubic-bezier(.33,1,.68,1)}.save-btn:hover{background-color:var(--shadow-claw-accent-primary);color:var(--shadow-claw-on-primary)}.save-btn:focus-visible{outline:.125rem solid var(--shadow-claw-accent-primary);outline-offset:.125rem}
@@ -1 +1 @@
1
- <template><div class="settings-section"> <h3>📅 Task Server</h3> <div class="form-group"> <div class="form-helper"> Configure where scheduled task requests are sent. Use a local path when the scheduler backend is hosted alongside the app, or a full URL when the app runs on a static host and scheduling is handled elsewhere. </div> </div> <div class="form-group"> <div class="form-toggle"> <input data-setting="task-server-enabled-toggle" id="task-server-enabled-toggle" type="checkbox"> <label class="form-label" for="task-server-enabled-toggle"> Enable Server-Side Tasks </label> </div> <div class="form-helper"> Opt in to server-side task scheduling. When disabled, the application will not attempt to probe or communicate with a task server. </div> </div> <div class="form-group"> <label class="form-label" for="task-server-url">Task Server URL</label> <input class="form-input" data-setting="task-server-url-input" id="task-server-url" placeholder="/schedule" spellcheck="false" type="url"> <div class="form-helper"> The schedule API base on this host, like <code>/schedule</code>, or a full URL like <code>http://localhost:8888/schedule</code>. Leave it blank to use the default. Set this when ShadowClaw is hosted on a static site, and task scheduling runs on a separate backend. </div> <button class="save-btn" data-action="save-task-server-url"> 💾 Save Task Server URL </button> </div> </div></template>
1
+ <template><div class="settings-section"> <h3>📅 Task Server</h3> <div class="form-group"> <div class="form-helper"> Configure where scheduled task requests are sent. Use a local path when the scheduler backend is hosted alongside the app, or a full URL when the app runs on a static host and scheduling is handled elsewhere. </div> </div> <div class="form-group"> <div class="form-toggle"> <input data-setting="task-server-enabled-toggle" id="task-server-enabled-toggle" type="checkbox"> <label class="form-label" for="task-server-enabled-toggle"> Enable Server-Side Tasks </label> </div> <div class="form-helper"> Opt in to server-side task scheduling. When disabled, the application will not attempt to probe or communicate with a task server. </div> </div> <div class="form-group"> <label class="form-label" for="task-server-url">Task Server URL</label> <input class="form-input" data-setting="task-server-url-input" id="task-server-url" placeholder="/schedule" spellcheck="false" type="url"> <div class="form-helper"> The schedule API base on this host, like <code>/schedule</code>, or a full URL like <code>http://localhost:8888/schedule</code>. Leave it blank to use the default. Set this when ShadowClaw is hosted on a static site, and task scheduling runs on a separate backend. </div> <button class="save-btn" data-action="save-task-server-url"> 💾 Save URL </button> </div> </div></template>
@@ -1 +1 @@
1
- <template><shadow-claw-page-header icon="⚙️" title="Settings"> <shadow-claw-page-header-action-button slot="actions" data-action="backup-settings" title="Backup settings"> 💾 Backup </shadow-claw-page-header-action-button> <shadow-claw-page-header-action-button slot="actions" data-action="restore-settings" title="Restore settings"> ♻️ Restore </shadow-claw-page-header-action-button> <shadow-claw-page-header-action-button slot="actions" data-action="clear-settings" title="Clear all settings" variant="danger"> 🗑️ Clear </shadow-claw-page-header-action-button> </shadow-claw-page-header><input class="settings__restore-input" type="file" accept=".json,application/json" aria-label="Restore settings from JSON backup" hidden><shadow-claw-dialog dialog-class="settings__confirm-dialog settings__backup-dialog" dialog-aria-label="Backup settings"> <form class="settings__confirm-form" method="dialog"> <h3 class="settings__confirm-title">Backup Settings</h3> <p class="settings__confirm-message"> Create a backup of all saved settings, accounts, integrations, and profiles. </p> <label class="settings__confirm-checkbox-row"> <input type="checkbox" data-setting="include-plaintext-passwords"> <span>Include passwords in plaintext</span> </label> <p class="settings__confirm-note"> Default is off. When disabled, password fields are excluded from the backup. </p> <div class="settings__confirm-actions"> <button type="button" data-action="cancel-backup-settings"> Cancel </button> <button type="button" data-action="confirm-backup-settings"> Backup </button> </div> </form> </shadow-claw-dialog><shadow-claw-dialog dialog-class="settings__confirm-dialog settings__restore-dialog" dialog-aria-label="Restore settings"> <form class="settings__confirm-form" method="dialog"> <h3 class="settings__confirm-title">Restore Settings</h3> <p class="settings__confirm-message"> Restoring will replace all current settings, accounts, integrations, and profiles. </p> <p class="settings__confirm-note" data-info="restore-filename"> No file selected. </p> <div class="settings__confirm-actions"> <button type="button" data-action="cancel-restore-settings"> Cancel </button> <button type="button" data-action="confirm-restore-settings"> Restore </button> </div> </form> </shadow-claw-dialog><shadow-claw-dialog dialog-class="settings__confirm-dialog settings__clear-dialog" dialog-aria-label="Clear settings"> <form class="settings__confirm-form" method="dialog"> <h3 class="settings__confirm-title">Clear Settings</h3> <p class="settings__confirm-message"> This will permanently remove all settings, accounts, integrations, and profiles. </p> <div class="settings__confirm-actions"> <button type="button" data-action="cancel-clear-settings"> Cancel </button> <button type="button" data-action="confirm-clear-settings" class="settings__danger-btn"> Clear </button> </div> </form> </shadow-claw-dialog><div class="settings-page"> <div class="settings-tab-nav" role="tablist" aria-label="Settings groups" aria-orientation="horizontal"> <button class="settings-tab-btn" role="tab" id="settings-tab-ai" data-tab-target="ai" aria-controls="settings-panel-ai"> AI </button> <button class="settings-tab-btn" role="tab" id="settings-tab-environment" data-tab-target="environment" aria-controls="settings-panel-environment"> Environment </button> <button class="settings-tab-btn" role="tab" id="settings-tab-integrations" data-tab-target="integrations" aria-controls="settings-panel-integrations"> Integrations </button> </div> <div class="settings-tab-panels"> <section class="settings-tab-panel" role="tabpanel" id="settings-panel-ai" data-tab-panel="ai" aria-labelledby="settings-tab-ai"> <details class="settings-collapsible"> <summary>🤖 AI</summary> <div class="settings-collapsible-content"> <div class="settings-section"> <h3>👤 Assistant</h3> <div class="form-group"> <label class="form-label">Name</label> <input class="form-input" data-setting="assistant-name-input" placeholder="ShadowClaw" type="text" value="ShadowClaw"> <div class="form-helper"> Used to mention the assistant in chat (@name) </div> </div> <button class="save-btn" data-action="save-assistant-name"> 💾 Save Name </button> </div> <div class="settings-section"> <h3>📝 Activity Log</h3> <div class="form-group"> <div class="form-toggle"> <input data-setting="activity-log-disk-logging-toggle" id="activity-log-disk-logging-toggle" type="checkbox"> <label class="form-label" for="activity-log-disk-logging-toggle"> Log activity details to disk </label> </div> <div class="form-helper"> When enabled, activity log entries are automatically written to a plain text file under .cache/logs in the app data directory. Default: disabled. </div> </div> </div> </div> </details> <details class="settings-collapsible"> <summary>🔑 Model Provider</summary> <div class="settings-collapsible-content"> <shadow-claw-llm></shadow-claw-llm> </div> </details> <details class="settings-collapsible"> <summary>🛠️ Tools</summary> <div class="settings-collapsible-content"> <div class="settings-section"> <div class="form-group"> <div class="form-helper"> Configure which tools are available to the AI, edit the system prompt, and manage custom tools. </div> </div> <button class="save-btn" data-action="show-tools-config"> 🛠️ Configure Tools </button> </div> </div> </details> </section> <section class="settings-tab-panel" role="tabpanel" id="settings-panel-environment" data-tab-panel="environment" aria-labelledby="settings-tab-environment" hidden> <details class="settings-collapsible"> <summary>🌐 Networking</summary> <div class="settings-collapsible-content"> <shadow-claw-networking></shadow-claw-networking> </div> </details> <details class="settings-collapsible"> <summary>🖥️ WebVM</summary> <div class="settings-collapsible-content"> <shadow-claw-webvm></shadow-claw-webvm> </div> </details> <details class="settings-collapsible"> <summary>🗃️ Misc</summary> <div class="settings-collapsible-content"> <div class="settings-section"> <h3>🧭 Navigation</h3> <div class="form-group"> <div class="form-toggle"> <input data-setting="sidebar-hide-pages-toggle" id="sidebar-hide-pages-toggle" type="checkbox"> <label class="form-label" for="sidebar-hide-pages-toggle"> Hide Pages in sidebar </label> </div> <div class="form-helper"> When enabled, the Pages item is hidden from the sidebar. </div> </div> <div class="form-group"> <div class="form-toggle"> <input data-setting="sidebar-hide-chat-toggle" id="sidebar-hide-chat-toggle" type="checkbox"> <label class="form-label" for="sidebar-hide-chat-toggle"> Hide Chat in sidebar </label> </div> <div class="form-helper"> When enabled, the Chat item is hidden from the sidebar. </div> </div> <div class="form-group"> <div class="form-toggle"> <input data-setting="sidebar-hide-tasks-toggle" id="sidebar-hide-tasks-toggle" type="checkbox"> <label class="form-label" for="sidebar-hide-tasks-toggle"> Hide Tasks in sidebar </label> </div> <div class="form-helper"> When enabled, the Tasks item is hidden from the sidebar. </div> </div> <div class="form-group"> <div class="form-toggle"> <input data-setting="sidebar-hide-files-toggle" id="sidebar-hide-files-toggle" type="checkbox"> <label class="form-label" for="sidebar-hide-files-toggle"> Hide Files in sidebar </label> </div> <div class="form-helper"> When enabled, the Files item is hidden from the sidebar. </div> </div> <div class="form-group"> <div class="form-toggle"> <input data-setting="chat-split-view-toggle" id="chat-split-view-toggle" type="checkbox"> <label class="form-label" for="chat-split-view-toggle"> Horizontal Chat split view </label> </div> <div class="form-helper"> When enabled, pins the Chat panel at the bottom of the main view alongside other pages. </div> </div> <div class="form-group"> <label class="form-label" for="pages-auto-refresh-input"> Pages Automatic Refresh (seconds, 0-86400) </label> <input class="form-input" data-setting="pages-auto-refresh-input" id="pages-auto-refresh-input" type="number" min="0" max="86400" step="1" placeholder="0"> <div class="form-helper"> Interval in seconds (0 to 86400, e.g. 5 = 5s, 60 = 1 min, 3600 = 1 hr) to automatically refresh the content of the pinned page (billboard style). Set to 0 to disable automatic refresh. </div> </div> <div class="form-group"> <div class="form-toggle"> <input data-setting="override-prerender-skeleton-toggle" id="override-prerender-skeleton-toggle" type="checkbox"> <label class="form-label" for="override-prerender-skeleton-toggle"> Override pre-rendered content with workspace skeleton loader </label> </div> <div class="form-helper"> When enabled, pre-rendered static content is hidden on startup and replaced with the workspace skeleton loader until app initialization completes. </div> </div> </div> <div class="settings-section"> <h3>🖼️ DOM</h3> <div class="settings-section"> <h4>📝 Markdown Frontmatter</h4> <div class="form-group"> <div class="form-toggle"> <input data-setting="markdown-frontmatter-pages-toggle" id="markdown-frontmatter-pages-toggle" type="checkbox"> <label class="form-label" for="markdown-frontmatter-pages-toggle"> Render frontmatter in Pages previews </label> </div> </div> <div class="form-group"> <div class="form-toggle"> <input data-setting="markdown-frontmatter-file-viewer-toggle" id="markdown-frontmatter-file-viewer-toggle" type="checkbox"> <label class="form-label" for="markdown-frontmatter-file-viewer-toggle"> Render frontmatter in File Viewer previews </label> </div> </div> <div class="form-group"> <div class="form-toggle"> <input data-setting="markdown-frontmatter-chat-toggle" id="markdown-frontmatter-chat-toggle" type="checkbox"> <label class="form-label" for="markdown-frontmatter-chat-toggle"> Render frontmatter in Chat messages </label> </div> </div> <div class="form-group"> <div class="form-toggle"> <input data-setting="markdown-frontmatter-tasks-toggle" id="markdown-frontmatter-tasks-toggle" type="checkbox"> <label class="form-label" for="markdown-frontmatter-tasks-toggle"> Render frontmatter in Tasks previews </label> </div> </div> </div> <div class="form-group"> <label class="form-label" for="dom-allowed-iframe-hosts"> Allowed Iframe Embed Hosts </label> <textarea class="form-textarea" id="dom-allowed-iframe-hosts" data-setting="dom-allowed-iframe-hosts" rows="5" placeholder="youtube.com&#10;youtube-nocookie.com&#10;youtu.be&#10;xt-ml.github.io&#10;kherrick.github.io"></textarea> <div class="form-helper"> Specify hostnames or regex patterns (one per line) allowed for iframe embeds. Default: YouTube and github.io pages. </div> </div> <button class="save-btn" data-action="save-dom-allowed-iframe-hosts"> 💾 Save DOM Settings </button> </div> <shadow-claw-storage></shadow-claw-storage> </div> </details> </section> <section class="settings-tab-panel" role="tabpanel" id="settings-panel-integrations" data-tab-panel="integrations" aria-labelledby="settings-tab-integrations" hidden> <details class="settings-collapsible"> <summary>🔐 Git Credentials</summary> <div class="settings-collapsible-content"> <shadow-claw-git></shadow-claw-git> </div> </details> <details class="settings-collapsible"> <summary>👤 Accounts</summary> <div class="settings-collapsible-content"> <shadow-claw-accounts></shadow-claw-accounts> </div> </details> <details class="settings-collapsible"> <summary>🔌 Remote MCP Connections</summary> <div class="settings-collapsible-content"> <shadow-claw-mcp-remote></shadow-claw-mcp-remote> </div> </details> <details class="settings-collapsible"> <summary>📧 Email</summary> <div class="settings-collapsible-content"> <shadow-claw-integrations></shadow-claw-integrations> </div> </details> <details class="settings-collapsible"> <summary>📅 Server Task Scheduling</summary> <div class="settings-collapsible-content"> <shadow-claw-task-server></shadow-claw-task-server> </div> </details> <details class="settings-collapsible"> <summary>🔔 Notifications</summary> <div class="settings-collapsible-content"> <shadow-claw-notifications></shadow-claw-notifications> </div> </details> <details class="settings-collapsible"> <summary>💬 Messaging Channels</summary> <div class="settings-collapsible-content"> <div class="settings-section"> <div class="form-group"> <div class="form-helper"> Configure Telegram and iMessage bridge integrations in a dedicated channel configuration interface. </div> </div> <button class="save-btn" data-action="show-channels-config"> 💬 Configure Channels </button> </div> </div> </details> </section> </div> <div class="settings-version-text" data-info="deployed-revision"> Deployed revision: unknown </div> </div></template>
1
+ <template><shadow-claw-page-header icon="⚙️" title="Settings"> <shadow-claw-page-header-action-button slot="actions" data-action="backup-settings" title="Backup settings"> 💾 Backup </shadow-claw-page-header-action-button> <shadow-claw-page-header-action-button slot="actions" data-action="restore-settings" title="Restore settings"> ♻️ Restore </shadow-claw-page-header-action-button> <shadow-claw-page-header-action-button slot="actions" data-action="clear-settings" title="Clear all settings" variant="danger"> 🗑️ Clear </shadow-claw-page-header-action-button> </shadow-claw-page-header><input class="settings__restore-input" type="file" accept=".json,application/json" aria-label="Restore settings from JSON backup" hidden><shadow-claw-dialog dialog-class="settings__confirm-dialog settings__backup-dialog" dialog-aria-label="Backup settings"> <form class="settings__confirm-form" method="dialog"> <h3 class="settings__confirm-title">Backup Settings</h3> <p class="settings__confirm-message"> Create a backup of all saved settings, accounts, integrations, and profiles. </p> <label class="settings__confirm-checkbox-row"> <input type="checkbox" data-setting="include-plaintext-passwords"> <span>Include passwords in plaintext</span> </label> <p class="settings__confirm-note"> Default is off. When disabled, password fields are excluded from the backup. </p> <div class="settings__confirm-actions"> <button type="button" data-action="cancel-backup-settings"> Cancel </button> <button type="button" data-action="confirm-backup-settings"> Backup </button> </div> </form> </shadow-claw-dialog><shadow-claw-dialog dialog-class="settings__confirm-dialog settings__restore-dialog" dialog-aria-label="Restore settings"> <form class="settings__confirm-form" method="dialog"> <h3 class="settings__confirm-title">Restore Settings</h3> <p class="settings__confirm-message"> Restoring will replace all current settings, accounts, integrations, and profiles. </p> <p class="settings__confirm-note" data-info="restore-filename"> No file selected. </p> <div class="settings__confirm-actions"> <button type="button" data-action="cancel-restore-settings"> Cancel </button> <button type="button" data-action="confirm-restore-settings"> Restore </button> </div> </form> </shadow-claw-dialog><shadow-claw-dialog dialog-class="settings__confirm-dialog settings__clear-dialog" dialog-aria-label="Clear settings"> <form class="settings__confirm-form" method="dialog"> <h3 class="settings__confirm-title">Clear Settings</h3> <p class="settings__confirm-message"> This will permanently remove all settings, accounts, integrations, and profiles. </p> <div class="settings__confirm-actions"> <button type="button" data-action="cancel-clear-settings"> Cancel </button> <button type="button" data-action="confirm-clear-settings" class="settings__danger-btn"> Clear </button> </div> </form> </shadow-claw-dialog><div class="settings-page"> <div class="settings-tab-nav" role="tablist" aria-label="Settings groups" aria-orientation="horizontal"> <button class="settings-tab-btn" role="tab" id="settings-tab-ai" data-tab-target="ai" aria-controls="settings-panel-ai"> AI </button> <button class="settings-tab-btn" role="tab" id="settings-tab-environment" data-tab-target="environment" aria-controls="settings-panel-environment"> Environment </button> <button class="settings-tab-btn" role="tab" id="settings-tab-integrations" data-tab-target="integrations" aria-controls="settings-panel-integrations"> Integrations </button> </div> <div class="settings-tab-panels"> <section class="settings-tab-panel" role="tabpanel" id="settings-panel-ai" data-tab-panel="ai" aria-labelledby="settings-tab-ai"> <details class="settings-collapsible"> <summary>🤖 AI</summary> <div class="settings-collapsible-content"> <div class="settings-section"> <h3>👤 Assistant</h3> <div class="form-group"> <label class="form-label">Name</label> <input class="form-input" data-setting="assistant-name-input" placeholder="ShadowClaw" type="text" value="ShadowClaw"> <div class="form-helper"> Used to mention the assistant in chat (@name) </div> </div> <button class="save-btn" data-action="save-assistant-name"> 💾 Save Name </button> </div> <div class="settings-section"> <h3>📝 Activity Log</h3> <div class="form-group"> <div class="form-toggle"> <input data-setting="activity-log-disk-logging-toggle" id="activity-log-disk-logging-toggle" type="checkbox"> <label class="form-label" for="activity-log-disk-logging-toggle"> Log activity details to disk </label> </div> <div class="form-helper"> When enabled, activity log entries are automatically written to a plain text file under .cache/logs in the app data directory. Default: disabled. </div> </div> </div> </div> </details> <details class="settings-collapsible"> <summary>🔑 Model Provider</summary> <div class="settings-collapsible-content"> <shadow-claw-llm></shadow-claw-llm> </div> </details> <details class="settings-collapsible"> <summary>🛠️ Tools</summary> <div class="settings-collapsible-content"> <div class="settings-section"> <div class="form-group"> <div class="form-helper"> Configure which tools are available to the AI, edit the system prompt, and manage custom tools. </div> </div> <button class="save-btn" data-action="show-tools-config"> 🛠️ Configure Tools </button> </div> </div> </details> </section> <section class="settings-tab-panel" role="tabpanel" id="settings-panel-environment" data-tab-panel="environment" aria-labelledby="settings-tab-environment" hidden> <details class="settings-collapsible"> <summary>🌐 Networking</summary> <div class="settings-collapsible-content"> <shadow-claw-networking></shadow-claw-networking> </div> </details> <details class="settings-collapsible"> <summary>🖥️ WebVM</summary> <div class="settings-collapsible-content"> <shadow-claw-webvm></shadow-claw-webvm> </div> </details> <details class="settings-collapsible"> <summary>🗃️ Misc</summary> <div class="settings-collapsible-content"> <div class="settings-section"> <h3>🧭 Navigation</h3> <div class="form-group"> <div class="form-toggle"> <input data-setting="sidebar-hide-pages-toggle" id="sidebar-hide-pages-toggle" type="checkbox"> <label class="form-label" for="sidebar-hide-pages-toggle"> Hide Pages in sidebar </label> </div> <div class="form-helper"> When enabled, the Pages item is hidden from the sidebar. </div> </div> <div class="form-group"> <div class="form-toggle"> <input data-setting="sidebar-hide-chat-toggle" id="sidebar-hide-chat-toggle" type="checkbox"> <label class="form-label" for="sidebar-hide-chat-toggle"> Hide Chat in sidebar </label> </div> <div class="form-helper"> When enabled, the Chat item is hidden from the sidebar. </div> </div> <div class="form-group"> <div class="form-toggle"> <input data-setting="sidebar-hide-tasks-toggle" id="sidebar-hide-tasks-toggle" type="checkbox"> <label class="form-label" for="sidebar-hide-tasks-toggle"> Hide Tasks in sidebar </label> </div> <div class="form-helper"> When enabled, the Tasks item is hidden from the sidebar. </div> </div> <div class="form-group"> <div class="form-toggle"> <input data-setting="sidebar-hide-files-toggle" id="sidebar-hide-files-toggle" type="checkbox"> <label class="form-label" for="sidebar-hide-files-toggle"> Hide Files in sidebar </label> </div> <div class="form-helper"> When enabled, the Files item is hidden from the sidebar. </div> </div> <div class="form-group"> <div class="form-toggle"> <input data-setting="chat-split-view-toggle" id="chat-split-view-toggle" type="checkbox"> <label class="form-label" for="chat-split-view-toggle"> Horizontal Chat split view </label> </div> <div class="form-helper"> When enabled, pins the Chat panel at the bottom of the main view alongside other pages. </div> </div> <div class="form-group"> <label class="form-label" for="pages-auto-refresh-input"> Pages Automatic Refresh (seconds, 0-86400) </label> <input class="form-input" data-setting="pages-auto-refresh-input" id="pages-auto-refresh-input" type="number" min="0" max="86400" step="1" placeholder="0"> <div class="form-helper"> Interval in seconds (0 to 86400, e.g. 5 = 5s, 60 = 1 min, 3600 = 1 hr) to automatically refresh the content of the pinned page (billboard style). Set to 0 to disable automatic refresh. </div> </div> <div class="form-group"> <div class="form-toggle"> <input data-setting="override-prerender-skeleton-toggle" id="override-prerender-skeleton-toggle" type="checkbox"> <label class="form-label" for="override-prerender-skeleton-toggle"> Override pre-rendered content with workspace skeleton loader </label> </div> <div class="form-helper"> When enabled, pre-rendered static content is hidden on startup and replaced with the workspace skeleton loader until app initialization completes. </div> </div> </div> <div class="settings-section"> <h3>🖼️ DOM</h3> <div class="settings-section"> <h4>📝 Markdown Frontmatter</h4> <div class="form-group"> <div class="form-toggle"> <input data-setting="markdown-frontmatter-pages-toggle" id="markdown-frontmatter-pages-toggle" type="checkbox"> <label class="form-label" for="markdown-frontmatter-pages-toggle"> Render frontmatter in Pages previews </label> </div> </div> <div class="form-group"> <div class="form-toggle"> <input data-setting="markdown-frontmatter-file-viewer-toggle" id="markdown-frontmatter-file-viewer-toggle" type="checkbox"> <label class="form-label" for="markdown-frontmatter-file-viewer-toggle"> Render frontmatter in File Viewer previews </label> </div> </div> <div class="form-group"> <div class="form-toggle"> <input data-setting="markdown-frontmatter-chat-toggle" id="markdown-frontmatter-chat-toggle" type="checkbox"> <label class="form-label" for="markdown-frontmatter-chat-toggle"> Render frontmatter in Chat messages </label> </div> </div> <div class="form-group"> <div class="form-toggle"> <input data-setting="markdown-frontmatter-tasks-toggle" id="markdown-frontmatter-tasks-toggle" type="checkbox"> <label class="form-label" for="markdown-frontmatter-tasks-toggle"> Render frontmatter in Tasks previews </label> </div> </div> </div> <div class="form-group"> <label class="form-label" for="dom-allowed-iframe-hosts"> Allowed Iframe Embed Hosts </label> <textarea class="form-textarea" id="dom-allowed-iframe-hosts" data-setting="dom-allowed-iframe-hosts" rows="5" placeholder="youtube.com&#10;youtube-nocookie.com&#10;youtu.be&#10;xt-ml.github.io&#10;kherrick.github.io"></textarea> <div class="form-helper"> Specify hostnames or regex patterns (one per line) allowed for iframe embeds. Default: YouTube and github.io pages. </div> </div> <button class="save-btn" data-action="save-dom-allowed-iframe-hosts"> 💾 Save DOM Settings </button> </div> <shadow-claw-storage></shadow-claw-storage> </div> </details> </section> <section class="settings-tab-panel" role="tabpanel" id="settings-panel-integrations" data-tab-panel="integrations" aria-labelledby="settings-tab-integrations" hidden> <details class="settings-collapsible"> <summary>🔐 Git Credentials</summary> <div class="settings-collapsible-content"> <shadow-claw-git></shadow-claw-git> </div> </details> <details class="settings-collapsible"> <summary>👤 Accounts</summary> <div class="settings-collapsible-content"> <shadow-claw-accounts></shadow-claw-accounts> </div> </details> <details class="settings-collapsible"> <summary>🔌 Remote MCP Connections</summary> <div class="settings-collapsible-content"> <shadow-claw-mcp-remote></shadow-claw-mcp-remote> </div> </details> <details class="settings-collapsible"> <summary>🎛️ Control Plane</summary> <div class="settings-collapsible-content"> <shadow-claw-control-plane></shadow-claw-control-plane> </div> </details> <details class="settings-collapsible"> <summary>📧 Email</summary> <div class="settings-collapsible-content"> <shadow-claw-integrations></shadow-claw-integrations> </div> </details> <details class="settings-collapsible"> <summary>📅 Server Task Scheduling</summary> <div class="settings-collapsible-content"> <shadow-claw-task-server></shadow-claw-task-server> </div> </details> <details class="settings-collapsible"> <summary>🔔 Notifications</summary> <div class="settings-collapsible-content"> <shadow-claw-notifications></shadow-claw-notifications> </div> </details> <details class="settings-collapsible"> <summary>💬 Messaging Channels</summary> <div class="settings-collapsible-content"> <div class="settings-section"> <div class="form-group"> <div class="form-helper"> Configure Telegram and iMessage bridge integrations in a dedicated channel configuration interface. </div> </div> <button class="save-btn" data-action="show-channels-config"> 💬 Configure Channels </button> </div> </div> </details> </section> </div> <div class="settings-version-text" data-info="deployed-revision"> Deployed revision: unknown </div> </div></template>
@@ -0,0 +1 @@
1
+ import{n as e}from"./rolldown-runtime-aKtaBQYM.js";import{i as t}from"./app-routes-Bs-riIvr.js";const n=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)))}};var r=e({ASSISTANT_NAME:()=>i,BASH_DEFAULT_TIMEOUT_SEC:()=>900,BASH_MAX_TIMEOUT_SEC:()=>f,BEDROCK_PROXY_MODELS_URL:()=>E,BEDROCK_PROXY_URL:()=>T,CONFIG_KEYS:()=>H,CONTEXT_WINDOW_SIZE:()=>50,DB_VERSION:()=>2,DEFAULT_CONTROL_PLANE_ENABLED:()=>!1,DEFAULT_CONTROL_PLANE_TRANSPORT:()=>`sse`,DEFAULT_DEV_HOST:()=>U,DEFAULT_DEV_IP:()=>DEFAULT_DEV_IP,DEFAULT_DEV_PORT:()=>W,DEFAULT_FILES_UPLOAD_APPEND_ULID:()=>!1,DEFAULT_GROUP_ID:()=>y,DEFAULT_MAX_ITERATIONS:()=>50,DEFAULT_MAX_TOKENS:()=>o,DEFAULT_PROMPT_API_BACKEND:()=>DEFAULT_PROMPT_API_BACKEND,DEFAULT_PROMPT_API_DTYPE_STRATEGY:()=>DEFAULT_PROMPT_API_DTYPE_STRATEGY,DEFAULT_PROMPT_API_FALLBACK_MODEL:()=>K,DEFAULT_PROVIDER:()=>l,DEFAULT_SUBAGENT_MAX_PARALLEL:()=>5,DEFAULT_SUBAGENT_WORKSPACE_MODE:()=>G,DEFAULT_TASK_SERVER_ENABLED:()=>!1,DEFAULT_TASK_SERVER_URL:()=>q,DEFAULT_TRANSFORMERS_JS_BACKEND:()=>DEFAULT_TRANSFORMERS_JS_BACKEND,DEFAULT_TRANSFORMERS_JS_DTYPE_STRATEGY:()=>DEFAULT_TRANSFORMERS_JS_DTYPE_STRATEGY,DEFAULT_TRANSFORMERS_JS_MODEL:()=>DEFAULT_TRANSFORMERS_JS_MODEL,DEFAULT_VM_BOOT_HOST:()=>m,DEFAULT_VM_NETWORK_RELAY_URL:()=>p,FETCH_MAX_RESPONSE:()=>d,GENERAL_ACCOUNT_PROVIDER_CAPABILITIES:()=>N,IMESSAGE_POLL_TIMEOUT:()=>25,IMESSAGE_REQUEST_TIMEOUT_MS:()=>C,LEGACY_DB_NAME:()=>h,LEGACY_OPFS_ROOT:()=>_,LLAMAFILE_PROXY_MODELS_URL:()=>A,LLAMAFILE_PROXY_URL:()=>k,MODEL_OUTPUT_LIMITS:()=>s,OAUTH_PROVIDER_DEFINITIONS:()=>P,OLLAMA_PROXY_MODELS_URL:()=>M,OLLAMA_PROXY_URL:()=>j,OPFS_ROOT:()=>v,PEERJS_DEFAULT_HOST:()=>w,PEERJS_DEFAULT_PATH:()=>`/`,PEERJS_DEFAULT_PORT:()=>443,PEERJS_DEFAULT_SECURE:()=>!0,PROCESS_LOOP_INTERVAL:()=>100,PROVIDERS:()=>F,SCHEDULER_INTERVAL:()=>u,TELEGRAM_API_BASE:()=>b,TELEGRAM_MAX_LENGTH:()=>S,TELEGRAM_POLL_TIMEOUT:()=>30,TELEGRAM_PROXY_BASE:()=>x,TRANSFORMERS_JS_PROXY_MODELS_URL:()=>O,TRANSFORMERS_JS_PROXY_URL:()=>D,buildTriggerPattern:()=>a,getAvailableProviders:()=>R,getDbName:()=>g,getDefaultProvider:()=>L,getGeneralAccountProviderCapabilities:()=>B,getModelMaxTokens:()=>c,getProvider:()=>I,getProviderApiKeyConfigKey:()=>z,getProviderTokenAuthScheme:()=>V});const i=`ShadowClaw`;function a(e){let t=e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`);return RegExp(`(^|\\s)@${t}\\b`,`i`)}a(i);const o=8192,s=[{pattern:`claude-fable-5`,maxTokens:128e3},{pattern:`claude-sonnet-5`,maxTokens:128e3},{pattern:`claude-opus-5`,maxTokens:128e3},{pattern:`claude-opus-4-8`,maxTokens:128e3},{pattern:`claude-opus-4`,maxTokens:128e3},{pattern:`claude-sonnet-4`,maxTokens:64e3},{pattern:`claude-haiku-4`,maxTokens:64e3},{pattern:`claude-3-5`,maxTokens:8192},{pattern:`claude-3`,maxTokens:4096},{pattern:`gpt-5`,maxTokens:128e3},{pattern:`gpt-4o`,maxTokens:16384},{pattern:`gpt-4.1`,maxTokens:32768},{pattern:`gpt-4-turbo`,maxTokens:4096},{pattern:`gpt-4`,maxTokens:8192},{pattern:`gpt-3.5`,maxTokens:4096},{pattern:`qwen-2.5`,maxTokens:32768},{pattern:`Qwen3.5`,maxTokens:32768},{pattern:`qwen3.5`,maxTokens:32768},{pattern:`Qwen3`,maxTokens:32768},{pattern:`qwen3`,maxTokens:32768},{pattern:`gemma-4`,maxTokens:16384},{pattern:`gemini-nano`,maxTokens:4096},{pattern:`phi-mini`,maxTokens:9216},{pattern:`aion`,maxTokens:9216},{pattern:`gemma-3-1b-it-ONNX-GQA`,maxTokens:32768},{pattern:`gemma-3-1b-it-ONNX`,maxTokens:32768},{pattern:`gemma-3`,maxTokens:32768},{pattern:`Qwen3-0.6B-ONNX`,maxTokens:32768},{pattern:`Qwen3.5`,maxTokens:32768},{pattern:`Llama-3.2`,maxTokens:32768},{pattern:`SmolLM`,maxTokens:8192},{pattern:`DeepSeek-R1`,maxTokens:32768},{pattern:`Phi-4`,maxTokens:16384},{pattern:`Phi-3.5`,maxTokens:16384},{pattern:`LFM2`,maxTokens:16384},{pattern:`gpt-oss`,maxTokens:32768},{pattern:`browser-built-in`,maxTokens:4096},{pattern:`mesh`,maxTokens:32768},{pattern:`openrouter/free`,maxTokens:32768},{pattern:`qwen3:8b`,maxTokens:4096},{pattern:`llama3:8b`,maxTokens:8192}];function c(e){if(!e)return o;let t=n.getModelInfo(e);if(t){if(typeof t.maxOutput==`number`){let e=t.contextWindow?Math.max(8192,Math.floor(t.contextWindow/2)):32768;return Math.min(t.maxOutput,e)}if(t.contextWindow){let e;return e=t.contextWindow>=2e5?131072:t.contextWindow>=1e5?65536:32768,Math.min(e,Math.floor(t.contextWindow/2))}}for(let{pattern:t,maxTokens:n}of s)if(e.includes(t))return n;return o}const l=`prompt_api`,u=6e4,d=102400,f=1800,p=`wss://relay.widgetry.org/`,m=`http://localhost:8888`,h=`shadowclaw`;function g(){let e=t();return e?`shadowclaw-${e}`:h}g();const _=`shadowclaw`,v=`shadowclaw`,y=`br:main`,b=`https://api.telegram.org/bot`,x=`/telegram/bot`,S=4096,C=15e3,w=`0.peerjs.com`,T=`http://localhost:8888/bedrock-proxy/invoke`,E=`http://localhost:8888/bedrock-proxy/models`,D=`http://localhost:8888/transformers-js-proxy/chat/completions`,O=`http://localhost:8888/transformers-js-proxy/models`,k=`http://localhost:8888/llamafile-proxy/chat/completions`,A=`http://localhost:8888/llamafile-proxy/models`,j=`http://localhost:8888/ollama-proxy/chat/completions`,M=`http://localhost:8888/ollama-proxy/models`,N={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`}}},P=Object.fromEntries(Object.values(N).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(N).map(e=>[e.providerId,{providerId:e.providerId,modes:e.modes,defaultMode:e.defaultMode}]));const F={openrouter:{id:`openrouter`,name:`OpenRouter`,baseUrl:`https://openrouter.ai/api/v1/chat/completions`,format:`openai`,apiKeyHeader:`Authorization`,apiKeyHeaderFormat:`Bearer {key}`,headers:{"HTTP-Referer":`https://xt-ml.github.io/shadow-claw/`,"X-OpenRouter-Title":`ShadowClaw`},defaultModel:`openrouter/free`,modelsUrl:`https://openrouter.ai/api/v1/models`,requiresApiKey:!0,supportsStreaming:!0,reasoningParam:`reasoning`},huggingface:{id:`huggingface`,name:`HuggingFace`,baseUrl:`https://router.huggingface.co/v1/chat/completions`,format:`openai`,apiKeyHeader:`Authorization`,apiKeyHeaderFormat:`Bearer {key}`,headers:{},defaultModel:`meta-llama/Llama-3.1-8B-Instruct`,modelsUrl:`https://router.huggingface.co/v1/models`,requiresApiKey:!0,supportsStreaming:!0},bedrock_proxy:{id:`bedrock_proxy`,name:`AWS Bedrock (Local Proxy)`,baseUrl:T,format:`anthropic`,apiKeyHeader:`Authorization`,headers:{},defaultModel:`anthropic.claude-sonnet-4-6-v1:0`,modelsUrl:E,requiresApiKey:!1,supportsStreaming:!0,reasoningParam:`thinking`},gemini_proxy:{id:`gemini_proxy`,name:`Gemini (Local Proxy)`,baseUrl:`http://localhost:8888/gemini-proxy/chat/completions`,format:`openai`,apiKeyHeader:`x-goog-api-key`,headers:{},defaultModel:`gemini-2.0-flash`,modelsUrl:`http://localhost:8888/gemini-proxy/models`,requiresApiKey:!0,supportsStreaming:!0,reasoningParam:`thinkingConfig`},vertex_ai:{id:`vertex_ai`,name:`Vertex AI (Local Proxy)`,baseUrl:`http://localhost:8888/vertex-ai-proxy/chat/completions`,format:`openai`,apiKeyHeader:`x-vertex-project`,headers:{},defaultModel:`gemini-2.5-flash`,modelsUrl:`http://localhost:8888/vertex-ai-proxy/models`,requiresApiKey:!0,supportsStreaming:!0,reasoningParam:`thinkingConfig`},"mesh-llm":{id:`mesh-llm`,name:`Mesh LLM (Local Proxy)`,baseUrl:`http://localhost:8888/mesh-llm-proxy/chat/completions`,format:`mesh-llm`,apiKeyHeader:`Authorization`,headers:{},defaultModel:`mesh`,modelsUrl:`http://localhost:8888/mesh-llm-proxy/models`,requiresApiKey:!1,supportsStreaming:!0},ollama:{id:`ollama`,name:`Ollama (Local Proxy)`,baseUrl:j,format:`openai`,apiKeyHeader:`Authorization`,headers:{},defaultModel:`qwen3:8b`,modelsUrl:M,requiresApiKey:!1,supportsStreaming:!0},llamafile:{id:`llamafile`,name:`Llamafile (Local Proxy)`,baseUrl:k,format:`openai`,apiKeyHeader:`Authorization`,headers:{},defaultModel:`Qwen3.5-9B-Q5_K_S`,modelsUrl:A,requiresApiKey:!1,supportsStreaming:!0},transformers_js_local:{id:`transformers_js_local`,name:`Transformers.js (Local Proxy)`,baseUrl:D,format:`openai`,apiKeyHeader:`Authorization`,headers:{},defaultModel:`onnx-community/Qwen3-0.6B-ONNX`,modelsUrl:O,models:[`onnx-community/DeepSeek-R1-Distill-Qwen-1.5B-ONNX`,`onnx-community/gemma-3-1b-it-ONNX-GQA`,`onnx-community/gemma-3-1b-it-ONNX`,`onnx-community/gemma-4-E2B-it-ONNX`,`onnx-community/gemma-4-E2B-it-qat-mobile-ONNX`,`onnx-community/gemma-4-E4B-it-ONNX`,`onnx-community/gemma-4-E4B-it-qat-mobile-ONNX`,`onnx-community/gemma-4-E9B-it-ONNX`,`onnx-community/gemma-4-12B-it-ONNX`,`onnx-community/gemma-4-12B-it-qat-mobile-ONNX`,`onnx-community/gemma-4-E27B-it-ONNX`,`onnx-community/gpt-oss-20b-ONNX`,`onnx-community/LFM2-1.2B-ONNX`,`onnx-community/Llama-3.2-1B-Instruct-ONNX`,`onnx-community/Llama-3.2-3B-Instruct-ONNX`,`onnx-community/Phi-3.5-mini-instruct-onnx-web`,`onnx-community/Phi-4-mini-instruct-ONNX`,`onnx-community/Qwen3-0.6B-ONNX`,`onnx-community/Qwen3.5-0.8B-ONNX-OPT`,`onnx-community/Qwen3.5-4B-ONNX-OPT`,`HuggingFaceTB/SmolLM3-3B-ONNX`,`LiquidAI/LFM2.5-1.2B-Thinking-ONNX`,`webgpu/Qwen3-4B-ONNX`],requiresApiKey:!1,supportsStreaming:!0},transformers_js_browser:{id:`transformers_js_browser`,name:`Transformers.js (Browser)`,baseUrl:`local://transformers-js`,format:`transformers_js`,apiKeyHeader:`Authorization`,headers:{},defaultModel:`onnx-community/Qwen3-0.6B-ONNX`,modelsUrl:`https://huggingface.co/api/models?author=onnx-community`,models:[`onnx-community/Qwen3-0.6B-ONNX`,`onnx-community/gemma-3-1b-it-ONNX`,`onnx-community/gemma-3-1b-it-ONNX-GQA`,`onnx-community/gemma-4-E2B-it-ONNX`,`onnx-community/gemma-4-E4B-it-ONNX`,`onnx-community/gemma-4-E9B-it-ONNX`,`onnx-community/gemma-4-E27B-it-ONNX`,`onnx-community/gemma-4-12B-it-ONNX`,`onnx-community/gemma-4-E2B-it-qat-mobile-ONNX`,`onnx-community/gemma-4-E4B-it-qat-mobile-ONNX`,`onnx-community/gemma-4-12B-it-qat-mobile-ONNX`,`onnx-community/Phi-3.5-mini-instruct-onnx-web`,`onnx-community/Phi-4-mini-instruct-ONNX`,`onnx-community/Llama-3.2-1B-Instruct-ONNX`,`onnx-community/Llama-3.2-3B-Instruct-ONNX`,`webgpu/Qwen3-4B-ONNX`,`onnx-community/DeepSeek-R1-Distill-Qwen-1.5B-ONNX`,`onnx-community/gpt-oss-20b-ONNX`,`onnx-community/LFM2-1.2B-ONNX`,`LiquidAI/LFM2.5-1.2B-Thinking-ONNX`,`HuggingFaceTB/SmolLM3-3B-ONNX`,`onnx-community/Qwen3.5-0.8B-ONNX-OPT`,`onnx-community/Qwen3.5-4B-ONNX-OPT`],requiresApiKey:!1,supportsStreaming:!1},litert_lm_browser:{id:`litert_lm_browser`,name:`LiteRT-LM (Browser)`,baseUrl:`local://litert-lm`,format:`transformers_js`,apiKeyHeader:`Authorization`,headers:{},defaultModel:`litert-community/gemma-4-E2B-it-litert-lm`,models:[`litert-community/gemma-4-E2B-it-litert-lm`,`litert-community/gemma-4-E4B-it-litert-lm`],requiresApiKey:!1,supportsStreaming:!1},prompt_api:{id:`prompt_api`,name:`Prompt API (Browser)`,baseUrl:`builtin://language-model`,format:`prompt_api`,apiKeyHeader:`Authorization`,headers:{},defaultModel:`browser-built-in`,models:[`browser-built-in`],requiresApiKey:!1,supportsStreaming:!1}};function I(e){return F[e]||null}function L(){return F[l]}function R(){return Object.keys(F)}function z(e){return`api_key:${e}`}function B(e){return N[e]||null}function V(e,t,n=`http_api`){let r=B(e);return r&&(r.tokenAuthByServiceType?.[n]?.[t]||r.tokenAuth[t])||null}const H={ACTIVE_TOOL_PROFILE:`active_tool_profile`,ACTIVITY_LOG_DISK_LOGGING_ENABLED:`activity_log_disk_logging_enabled`,ALLOWED_CUSTOM_ELEMENTS:`allowed_custom_elements`,ALLOWED_CUSTOM_ELEMENT_HOST_PATTERNS:`allowed_custom_element_host_patterns`,ALLOWED_IFRAME_HOST_PATTERNS:`allowed_iframe_host_patterns`,API_KEY:`api_key`,ASSISTANT_NAME:`assistant_name`,BEDROCK_AUTH_MODE:`bedrock_auth_mode`,BEDROCK_PROFILE_FALLBACK:`bedrock_profile_fallback`,BEDROCK_REGION_FALLBACK:`bedrock_region_fallback`,BUILTIN_AI_SUBAGENT_ROUTING:`builtin_ai_subagent_routing`,BUILTIN_AI_TOOLS_BACKEND:`builtin_ai_tools_backend`,CHANNEL_ENABLED_PREFIX:`channel_enabled:`,CHAT_INPUT_AREA_HEIGHT:`chat_input_area_height`,CHAT_SPLIT_VIEW_ENABLED:`chat_split_view_enabled`,CHAT_SPLIT_VIEW_HEIGHT:`chat_split_view_height`,COMPACTION_ENGINE_PREFERENCE:`compaction_engine_preference`,CONTEXT_COMPRESSION_ENABLED:`context_compression_enabled`,CONTROL_PLANE_CLIENT_ID:`control_plane_client_id`,CONTROL_PLANE_ENABLED:`control_plane_enabled`,CONTROL_PLANE_TRANSPORT:`control_plane_transport`,CONTROL_PLANE_URL:`control_plane_url`,CONVERSATIONS_HEIGHT:`conversations_height`,CUSTOM_TOOLS:`custom_tools`,DB_MIGRATED_FROM_LEGACY:`db_migrated_from_legacy`,DECLARATIVE_TOOLS_ENABLED:`declarative_tools_enabled`,DEFAULT_PINNED_PAGE:`default_pinned_page`,DIRECT_TOOL_COMMAND_POLICY:`direct_tool_command_policy`,ENABLED_TOOLS:`enabled_tools`,FILES_UPLOAD_APPEND_ULID:`files_upload_append_ulid`,GIT_ACCOUNTS:`git_accounts`,GIT_AUTHOR_EMAIL:`git_author_email`,GIT_AUTHOR_NAME:`git_author_name`,GIT_CORS_PROXY:`git_cors_proxy`,GIT_DEFAULT_ACCOUNT:`git_default_account`,GIT_PASSWORD:`git_password`,GIT_PROXY_URL:`git_proxy_url`,GIT_TOKEN:`git_token`,GIT_USERNAME:`git_username`,IMESSAGE_API_KEY:`imessage_api_key`,IMESSAGE_CHAT_IDS:`imessage_chat_ids`,IMESSAGE_SERVER_URL:`imessage_server_url`,INTEGRATION_CONNECTIONS:`integration_connections`,MARKDOWN_FRONTMATTER_CHAT:`markdown_frontmatter_chat`,MARKDOWN_FRONTMATTER_FILE_VIEWER:`markdown_frontmatter_file_viewer`,MARKDOWN_FRONTMATTER_PAGES:`markdown_frontmatter_pages`,MARKDOWN_FRONTMATTER_TASKS:`markdown_frontmatter_tasks`,LAST_ACTIVE_GROUP:`last_active_group`,LAST_ACTIVE_PAGE:`last_active_page`,LAST_SELECTED_PINNED_PAGE:`last_selected_pinned_page`,LLAMAFILE_HOST:`llamafile_host`,LLAMAFILE_MODE:`llamafile_mode`,LLAMAFILE_OFFLINE:`llamafile_offline`,LLAMAFILE_PORT:`llamafile_port`,MAIN_GROUP_INDEX_SUPPRESSED:`main_group_index_suppressed`,MAIN_GROUP_README_SUPPRESSED:`main_group_readme_suppressed`,MAX_ITERATIONS:`max_iterations`,MAX_TOKENS:`max_tokens`,MESH_LLM_HOST:`mesh_llm_host`,MODEL:`model`,OAUTH_PROVIDER_DEFINITIONS:`oauth_provider_definitions`,OPFS_MIGRATED_FROM_LEGACY:`opfs_migrated_from_legacy`,OVERRIDE_PRERENDER_SKELETON:`override_prerender_skeleton`,PAGES_AUTO_REFRESH_INTERVAL:`pages_auto_refresh_interval`,PAGES_LIST:`pages_list`,PASSPHRASE_SALT:`passphrase_salt`,PASSPHRASE_VERIFY:`passphrase_verify`,PROMPT_API_BACKEND:`prompt_api_backend`,PROMPT_API_DTYPE_STRATEGY:`prompt_api_dtype_strategy`,PROMPT_API_FALLBACK_MODEL:`prompt_api_fallback_model`,PROMPT_API_ONBOARDING_SEEN:`prompt_api_onboarding_seen`,PEERJS_MY_ALIAS:`peerjs_my_alias`,PEERJS_MY_PEER_ID:`peerjs_my_peer_id`,PEERJS_PEER_ALIASES:`peerjs_peer_aliases`,PEERJS_SERVER_HOST:`peerjs_server_host`,PEERJS_SERVER_PATH:`peerjs_server_path`,PEERJS_SERVER_PORT:`peerjs_server_port`,PEERJS_SERVER_SECURE:`peerjs_server_secure`,PEERJS_TRUSTED_PEER_IDS:`peerjs_trusted_peer_ids`,PROVIDER:`provider`,PROXY_URL:`proxy_url`,PUSH_PROXY_URL:`push_proxy_url`,RATE_LIMIT_AUTO_ADAPT:`rate_limit_auto_adapt`,RATE_LIMIT_CALLS_PER_MINUTE:`rate_limit_calls_per_minute`,REASONING_EFFORT:`reasoning_effort`,REMOTE_MCP_CONNECTIONS:`remote_mcp_connections`,SERVICE_ACCOUNTS:`service_accounts`,SERVICE_DEFAULT_ACCOUNT:`service_default_account`,SIDEBAR_CHAT_HIDDEN:`sidebar_chat_hidden`,SIDEBAR_DEFAULT_PAGE:`sidebar_default_page`,SIDEBAR_FILES_HIDDEN:`sidebar_files_hidden`,SIDEBAR_PAGES_HIDDEN:`sidebar_pages_hidden`,SIDEBAR_TASKS_HIDDEN:`sidebar_tasks_hidden`,SIDEBAR_WIDTH:`sidebar_width`,SITE_CONFIG_SEEDED:`site_config_seeded`,STATIC_MAIN_SITE_SEEDED:`static_main_site_seeded`,STORAGE_HANDLE:`storage_handle`,SUBSCRIBER_ID:`subscriber_id`,STREAMING_ENABLED:`streaming_enabled`,SUBAGENT_MAX_PARALLEL:`subagent_max_parallel`,SUBAGENT_WORKSPACE_MODE:`subagent_workspace_mode`,SUPPRESSED_PAGES_LIST:`suppressed_pages_list`,SYSTEM_PROMPT_OVERRIDE:`system_prompt_override`,TASK_SERVER_ENABLED:`task_server_enabled`,TASK_SERVER_URL:`task_server_url`,TASK_SYNC_OUTBOX:`task_sync_outbox`,TELEGRAM_BOT_TOKEN:`telegram_bot_token`,TELEGRAM_CHAT_IDS:`telegram_chat_ids`,TELEGRAM_USE_PROXY:`telegram_use_proxy`,TOOL_PROFILES:`tool_profiles`,TRANSFORMERS_JS_BACKEND:`transformers_js_backend`,TRANSFORMERS_JS_DTYPE_STRATEGY:`transformers_js_dtype_strategy`,TRIGGER_PATTERN:`trigger_pattern`,USE_PROXY:`use_proxy`,VAPID_SUBJECT:`vapid_subject`,VM_BASH_FULL_INTERNET_ACCESS:`vm_bash_full_internet_access`,VM_BASH_TIMEOUT_SEC:`vm_bash_timeout_sec`,VM_BOOT_HOST:`vm_boot_host`,VM_BOOT_MODE:`vm_boot_mode`,VM_NETWORK_RELAY_URL:`vm_network_relay_url`,WEBMCP_MODE:`webmcp_mode`,WEBMCP_TOOLS_ENABLED:`webmcp_tools_enabled`,WEB_SEARCH_PROXY_URL:`web_search_proxy_url`,WEB_SEARCH_URL:`web_search_url`,WEB_SEARCH_USE_PROXY:`web_search_use_proxy`,SEARCH_FILES_MAX_FILE_BYTES:`search_files_max_file_bytes`,SEARCH_FILES_MAX_FILES_VISITED:`search_files_max_files_visited`,SEARCH_FILES_SKIP_DIRS:`search_files_skip_dirs`},U=`localhost`,W=8888,G=`automatic`,K=`onnx-community/Qwen3-0.6B-ONNX`,q=`http://localhost:8888/schedule`;export{R as A,F as C,x as D,S as E,I as F,z as I,V as L,L as M,B as N,a as O,c as P,n as R,w as S,b as T,h as _,W as a,P as b,K as c,q as d,m as f,C as g,N as h,U as i,g as j,r as k,l,d as m,f as n,y as o,p,H as r,o as s,i as t,G as u,_ as v,u as w,v as x,k as y};
@@ -1 +1 @@
1
- import{r as e}from"./config-BYAuxx3j.js";import{Ct as t,ht as n}from"./orchestrator-DRfEudXk.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};
1
+ import{r as e}from"./config-CrHchneq.js";import{Ct as t,ht as n}from"./orchestrator-tHWrqi_F.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};
@@ -1 +1 @@
1
- import{r as e}from"./config-BYAuxx3j.js";import{t}from"./getConfig-D89uJgo5.js";import{t as n}from"./setConfig-DFMYnYLE.js";import{t as r}from"./ulid-BY7rQVLN.js";const i=[{id:`imap`,name:`IMAP Mail`,protocol:`imap`,description:`Read mailboxes and fetch message metadata/content.`,version:`0.1.0`,actions:[`messages.read`,`messages.send`],authTypes:[`basic_userpass`,`oauth`],serviceTypes:[`http_api`],configurableFields:[`host`,`port`,`secure`,`mailboxPath`,`smtpHost`,`smtpPort`,`smtpSecure`,`fromAddress`,`executionMode`,`pollIntervalSec`]}];function a(){return[...i].sort((e,t)=>e.id.localeCompare(t.id))}function o(e){return e&&i.find(t=>t.id===e)||null}function s(e){if(!e||typeof e!=`object`)return null;let t=e;return!t.serviceType||!t.authType?null:{serviceType:t.serviceType,authType:t.authType,providerId:t.providerId||void 0,accountId:t.accountId||void 0,username:t.username||void 0,headerName:t.headerName||void 0,encryptedSecret:t.encryptedSecret||void 0}}function c(e){if(!e||typeof e!=`object`)return null;let t=e;return!t.id||!t.label||!t.pluginId||typeof t.enabled!=`boolean`||typeof t.createdAt!=`number`||typeof t.updatedAt!=`number`||!o(t.pluginId)?null:{id:t.id,label:t.label,pluginId:t.pluginId,enabled:t.enabled,config:t.config&&typeof t.config==`object`?{...t.config}:{},credentialRef:s(t.credentialRef),createdAt:t.createdAt,updatedAt:t.updatedAt}}async function l(n){let r=await t(n,e.INTEGRATION_CONNECTIONS);if(!Array.isArray(r))return[];let i=[];for(let e of r){let t=c(e);t&&i.push(t)}return i}async function u(t,r){await n(t,e.INTEGRATION_CONNECTIONS,r)}async function d(e){return l(e)}async function f(e,t){if(!t)return null;let n=await l(e);return n.find(e=>e.id===t)||n.find(e=>e.label.toLowerCase()===t.toLowerCase())||null}async function p(e,t){if(!t.label?.trim())throw Error(`Email connection label is required`);let n=o(t.pluginId);if(!n)throw Error(`Unknown email plugin: ${t.pluginId}`);let i=await l(e),a=Date.now();if(t.id){let r=i.findIndex(e=>e.id===t.id);if(r!==-1){let o=i[r],s={...o,label:t.label.trim(),pluginId:n.id,enabled:t.enabled??o.enabled,config:t.config?{...t.config}:o.config,updatedAt:a};return i[r]=s,await u(e,i),s}}let s={id:t.id||r(),label:t.label.trim(),pluginId:n.id,enabled:t.enabled??!0,config:t.config?{...t.config}:{},credentialRef:null,createdAt:a,updatedAt:a};return i.push(s),await u(e,i),s}async function m(e,t,n){if(!t)return null;let r=await l(e),i=r.findIndex(e=>e.id===t);if(i===-1)return null;let a={...r[i],credentialRef:n?s(n):null,updatedAt:Date.now()};return r[i]=a,await u(e,r),a}async function h(e,t){if(!t)return!1;let n=await l(e),r=n.filter(e=>e.id!==t);return r.length===n.length?!1:(await u(e,r),!0)}export{p as a,d as i,h as n,o,f as r,a as s,m as t};
1
+ import{r as e}from"./config-CrHchneq.js";import{t}from"./getConfig-D89uJgo5.js";import{t as n}from"./setConfig-DFMYnYLE.js";import{t as r}from"./ulid-BY7rQVLN.js";const i=[{id:`imap`,name:`IMAP Mail`,protocol:`imap`,description:`Read mailboxes and fetch message metadata/content.`,version:`0.1.0`,actions:[`messages.read`,`messages.send`],authTypes:[`basic_userpass`,`oauth`],serviceTypes:[`http_api`],configurableFields:[`host`,`port`,`secure`,`mailboxPath`,`smtpHost`,`smtpPort`,`smtpSecure`,`fromAddress`,`executionMode`,`pollIntervalSec`]}];function a(){return[...i].sort((e,t)=>e.id.localeCompare(t.id))}function o(e){return e&&i.find(t=>t.id===e)||null}function s(e){if(!e||typeof e!=`object`)return null;let t=e;return!t.serviceType||!t.authType?null:{serviceType:t.serviceType,authType:t.authType,providerId:t.providerId||void 0,accountId:t.accountId||void 0,username:t.username||void 0,headerName:t.headerName||void 0,encryptedSecret:t.encryptedSecret||void 0}}function c(e){if(!e||typeof e!=`object`)return null;let t=e;return!t.id||!t.label||!t.pluginId||typeof t.enabled!=`boolean`||typeof t.createdAt!=`number`||typeof t.updatedAt!=`number`||!o(t.pluginId)?null:{id:t.id,label:t.label,pluginId:t.pluginId,enabled:t.enabled,config:t.config&&typeof t.config==`object`?{...t.config}:{},credentialRef:s(t.credentialRef),createdAt:t.createdAt,updatedAt:t.updatedAt}}async function l(n){let r=await t(n,e.INTEGRATION_CONNECTIONS);if(!Array.isArray(r))return[];let i=[];for(let e of r){let t=c(e);t&&i.push(t)}return i}async function u(t,r){await n(t,e.INTEGRATION_CONNECTIONS,r)}async function d(e){return l(e)}async function f(e,t){if(!t)return null;let n=await l(e);return n.find(e=>e.id===t)||n.find(e=>e.label.toLowerCase()===t.toLowerCase())||null}async function p(e,t){if(!t.label?.trim())throw Error(`Email connection label is required`);let n=o(t.pluginId);if(!n)throw Error(`Unknown email plugin: ${t.pluginId}`);let i=await l(e),a=Date.now();if(t.id){let r=i.findIndex(e=>e.id===t.id);if(r!==-1){let o=i[r],s={...o,label:t.label.trim(),pluginId:n.id,enabled:t.enabled??o.enabled,config:t.config?{...t.config}:o.config,updatedAt:a};return i[r]=s,await u(e,i),s}}let s={id:t.id||r(),label:t.label.trim(),pluginId:n.id,enabled:t.enabled??!0,config:t.config?{...t.config}:{},credentialRef:null,createdAt:a,updatedAt:a};return i.push(s),await u(e,i),s}async function m(e,t,n){if(!t)return null;let r=await l(e),i=r.findIndex(e=>e.id===t);if(i===-1)return null;let a={...r[i],credentialRef:n?s(n):null,updatedAt:Date.now()};return r[i]=a,await u(e,r),a}async function h(e,t){if(!t)return!1;let n=await l(e),r=n.filter(e=>e.id!==t);return r.length===n.length?!1:(await u(e,r),!0)}export{p as a,d as i,h as n,o,f as r,a as s,m as t};
@@ -1 +1 @@
1
- import{r as e}from"./config-BYAuxx3j.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-0NS4XLOd.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(),ie(),a().sanitize(e,{ADD_TAGS:[`iframe`,`figure`,`figcaption`],ADD_ATTR:[`allow`,`allowfullscreen`,`frameborder`,`scrolling`,`referrerpolicy`,`loading`],CUSTOM_ELEMENT_HANDLING:{tagNameCheck:e=>M(e),attributeNameCheck:()=>!0,allowCustomizedBuiltInElements:!1},...t})}function d(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 f=`shadowclaw`;let p;function m(){let e=globalThis.trustedTypes;return!e||typeof e.createPolicy!=`function`?null:e}function h(){if(p!==void 0)return p;let e=m();if(!e)return p=null,p;if(typeof e.getPolicy==`function`){let t=e.getPolicy(f);if(t)return p=t,p}try{p=e.createPolicy(f,{createHTML:e=>e,createScriptURL:e=>e})}catch{p=typeof e.getPolicy==`function`?e.getPolicy(f)??null:null}return p}function g(e=``,t={}){let n=u(e,t),r=h();return r?r.createHTML(n):n}function _(e,t={}){return u(e,t)}function v(e){let t=h();return t?t.createHTML(e):e}function y(e,t=``,n={}){let r=g(t,n);return e.innerHTML=r,r}function ee(e,t){let n=v(t);return e.srcdoc=n,n}function b(e){let t=h();return!t||typeof t.createScriptURL!=`function`?e:t.createScriptURL(e)}const x=[],S=[];let C=new Set(x.map(e=>e.toLowerCase().trim())),w=[...S],T=[];function E(e){T=e.map(e=>i(e)).filter(Boolean)}E(w);function D(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 O(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 k(e){let t=D(e);C=new Set(t)}function A(e){let t=O(e);w=t,E(t)}function j(){return[...w]}function M(e){if(!e||typeof e!=`string`)return!1;let t=e.trim().toLowerCase();return!t.includes(`-`)||t===`shadow-claw`||t.startsWith(`shadow-claw-`)?!0:C.has(t)}function te(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 window<`u`&&window.location?.origin&&window.location.origin!==`null`?window.location.origin:`http://localhost`,n=new URL(t,e);if(n.protocol!==`http:`&&n.protocol!==`https:`)return!1;if(typeof window<`u`&&window.location?.origin&&window.location.origin!==`null`&&n.origin===window.location.origin)return!0;let r=n.hostname.toLowerCase();return T.some(e=>e(r))}catch{return!1}}const N=`allow-modals allow-scripts allow-popups allow-popups-to-escape-sandbox`,P=[`'self'`,`blob:`,`data:`],F=[`'self'`,`blob:`,`data:`],I=[`'self'`,`'unsafe-inline'`,`https:`,`http:`],L=[`'self'`,`blob:`,`data:`,`https:`,`http:`];let R=N,z=[...P],B=[...F],V=[],H=[...I],U=[...L];function W(){return R}function G(e){R=Array.isArray(e)?e.map(e=>typeof e==`string`?e.trim():``).filter(Boolean).join(` `):typeof e==`string`&&e.trim()?e.trim():N}function K(e){let t=j(),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 window<`u`&&window.location?.origin?window.location.origin:`http://localhost`);r.add(t.origin)}catch{}let i=[`'nonce-${e}'`,...Array.from(r),...V],a=[...B,...Array.from(r)];return[`script-src ${Array.from(new Set(i)).join(` `)}`,`worker-src ${Array.from(new Set(z)).join(` `)}`,`connect-src ${Array.from(new Set(a)).join(` `)}`,`style-src ${Array.from(new Set(H)).join(` `)}`,`img-src ${Array.from(new Set(U)).join(` `)}`].join(`; `)}function q(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&&k(t);let n=e.allowedCustomElementDomains||(typeof e.customElements==`object`&&!Array.isArray(e.customElements)?e.customElements?.allowedDomains:void 0);n!==void 0&&A(n),e.security?.iframeSandbox===void 0?(e.security?.allowSameOrigin===!0||e.security?.allowIframeSameOrigin===!0)&&(R.includes(`allow-same-origin`)||(R=`${R} allow-same-origin`.trim())):G(e.security.iframeSandbox),e.security?.workerSrc!==void 0&&(z=O(e.security.workerSrc)),e.security?.connectSrc!==void 0&&(B=O(e.security.connectSrc)),e.security?.scriptSrc!==void 0&&(V=O(e.security.scriptSrc)),e.security?.styleSrc!==void 0&&(H=O(e.security.styleSrc)),e.security?.imgSrc!==void 0&&(U=O(e.security.imgSrc))}function J(){if(typeof document>`u`)return;let e=document.getElementById(`shadow-claw-site-config`);if(e&&e.textContent)try{q(JSON.parse(e.textContent))}catch{}}function Y(){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=>typeof e==`string`?e:e?.src).filter(e=>typeof e==`string`&&te(e))}catch{}return[]}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{k(JSON.parse(a))}catch{k(a)}if(typeof o==`string`&&o.trim())try{A(JSON.parse(o))}catch{A(o)}}catch(e){console.warn(`Failed to load custom element security from db:`,e)}}let Z=null,Q=!1;function ne(){J(),!(Q||typeof customElements>`u`||typeof customElements.define!=`function`)&&(Z=customElements.define.bind(customElements),customElements.define=function(e,t,n){if(!M((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 re(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(`-`)&&!M(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(`-`)&&!M(n)&&(console.warn(`[Security] Removing unapproved custom element: <${n}>`),t.remove())}})});return t.observe(e,{childList:!0,subtree:!0}),t}let $=!1;function ie(){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(`-`)&&!M(n)&&(typeof t.remove==`function`&&t.remove(),t.parentNode&&t.parentNode.removeChild(t))}};e.addHook(`uponSanitizeElement`,t),e.addHook(`afterSanitizeElements`,t),$=!0}}export{d as _,re as a,X as c,ee as d,v as f,c as g,s as h,W as i,_ as l,l as m,Y as n,ne as o,b as p,K as r,M as s,q as t,y as u};
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(),ie(),a().sanitize(e,{ADD_TAGS:[`iframe`,`figure`,`figcaption`],ADD_ATTR:[`allow`,`allowfullscreen`,`frameborder`,`scrolling`,`referrerpolicy`,`loading`],CUSTOM_ELEMENT_HANDLING:{tagNameCheck:e=>M(e),attributeNameCheck:()=>!0,allowCustomizedBuiltInElements:!1},...t})}function d(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 f=`shadowclaw`;let p;function m(){let e=globalThis.trustedTypes;return!e||typeof e.createPolicy!=`function`?null:e}function h(){if(p!==void 0)return p;let e=m();if(!e)return p=null,p;if(typeof e.getPolicy==`function`){let t=e.getPolicy(f);if(t)return p=t,p}try{p=e.createPolicy(f,{createHTML:e=>e,createScriptURL:e=>e})}catch{p=typeof e.getPolicy==`function`?e.getPolicy(f)??null:null}return p}function g(e=``,t={}){let n=u(e,t),r=h();return r?r.createHTML(n):n}function _(e,t={}){return u(e,t)}function v(e){let t=h();return t?t.createHTML(e):e}function y(e,t=``,n={}){let r=g(t,n);return e.innerHTML=r,r}function ee(e,t){let n=v(t);return e.srcdoc=n,n}function b(e){let t=h();return!t||typeof t.createScriptURL!=`function`?e:t.createScriptURL(e)}const x=[],S=[];let C=new Set(x.map(e=>e.toLowerCase().trim())),w=[...S],T=[];function E(e){T=e.map(e=>i(e)).filter(Boolean)}E(w);function D(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 O(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 k(e){let t=D(e);C=new Set(t)}function A(e){let t=O(e);w=t,E(t)}function j(){return[...w]}function M(e){if(!e||typeof e!=`string`)return!1;let t=e.trim().toLowerCase();return!t.includes(`-`)||t===`shadow-claw`||t.startsWith(`shadow-claw-`)?!0:C.has(t)}function te(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 window<`u`&&window.location?.origin&&window.location.origin!==`null`?window.location.origin:`http://localhost`,n=new URL(t,e);if(n.protocol!==`http:`&&n.protocol!==`https:`)return!1;if(typeof window<`u`&&window.location?.origin&&window.location.origin!==`null`&&n.origin===window.location.origin)return!0;let r=n.hostname.toLowerCase();return T.some(e=>e(r))}catch{return!1}}const N=`allow-modals allow-scripts allow-popups allow-popups-to-escape-sandbox`,P=[`'self'`,`blob:`,`data:`],F=[`'self'`,`blob:`,`data:`],I=[`'self'`,`'unsafe-inline'`,`https:`,`http:`],L=[`'self'`,`blob:`,`data:`,`https:`,`http:`];let R=N,z=[...P],B=[...F],V=[],H=[...I],U=[...L];function W(){return R}function G(e){R=Array.isArray(e)?e.map(e=>typeof e==`string`?e.trim():``).filter(Boolean).join(` `):typeof e==`string`&&e.trim()?e.trim():N}function K(e){let t=j(),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 window<`u`&&window.location?.origin?window.location.origin:`http://localhost`);r.add(t.origin)}catch{}let i=[`'nonce-${e}'`,...Array.from(r),...V],a=[...B,...Array.from(r)];return[`script-src ${Array.from(new Set(i)).join(` `)}`,`worker-src ${Array.from(new Set(z)).join(` `)}`,`connect-src ${Array.from(new Set(a)).join(` `)}`,`style-src ${Array.from(new Set(H)).join(` `)}`,`img-src ${Array.from(new Set(U)).join(` `)}`].join(`; `)}function q(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&&k(t);let n=e.allowedCustomElementDomains||(typeof e.customElements==`object`&&!Array.isArray(e.customElements)?e.customElements?.allowedDomains:void 0);n!==void 0&&A(n),e.security?.iframeSandbox===void 0?(e.security?.allowSameOrigin===!0||e.security?.allowIframeSameOrigin===!0)&&(R.includes(`allow-same-origin`)||(R=`${R} allow-same-origin`.trim())):G(e.security.iframeSandbox),e.security?.workerSrc!==void 0&&(z=O(e.security.workerSrc)),e.security?.connectSrc!==void 0&&(B=O(e.security.connectSrc)),e.security?.scriptSrc!==void 0&&(V=O(e.security.scriptSrc)),e.security?.styleSrc!==void 0&&(H=O(e.security.styleSrc)),e.security?.imgSrc!==void 0&&(U=O(e.security.imgSrc))}function J(){if(typeof document>`u`)return;let e=document.getElementById(`shadow-claw-site-config`);if(e&&e.textContent)try{q(JSON.parse(e.textContent))}catch{}}function Y(){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=>typeof e==`string`?e:e?.src).filter(e=>typeof e==`string`&&te(e))}catch{}return[]}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{k(JSON.parse(a))}catch{k(a)}if(typeof o==`string`&&o.trim())try{A(JSON.parse(o))}catch{A(o)}}catch(e){console.warn(`Failed to load custom element security from db:`,e)}}let Z=null,Q=!1;function ne(){J(),!(Q||typeof customElements>`u`||typeof customElements.define!=`function`)&&(Z=customElements.define.bind(customElements),customElements.define=function(e,t,n){if(!M((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 re(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(`-`)&&!M(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(`-`)&&!M(n)&&(console.warn(`[Security] Removing unapproved custom element: <${n}>`),t.remove())}})});return t.observe(e,{childList:!0,subtree:!0}),t}let $=!1;function ie(){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(`-`)&&!M(n)&&(typeof t.remove==`function`&&t.remove(),t.parentNode&&t.parentNode.removeChild(t))}};e.addHook(`uponSanitizeElement`,t),e.addHook(`afterSanitizeElements`,t),$=!0}}export{d as _,re as a,X as c,ee as d,v as f,c as g,s as h,W as i,_ as l,l as m,Y as n,ne as o,b as p,K as r,M as s,q as t,y as u};
@@ -47,7 +47,9 @@ Detailed docs for each major subsystem.
47
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
48
  | [Custom Element Security](subsystems/custom-element-security.md) | Custom element registry & DOM guards, allowlists, iframe sandbox policies, and nonce-gated CSP |
49
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`), dual-root path resolution, and npm packaging |
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
+ | [File Backup Subsystem](subsystems/backup.md) | Remote workspace and OPFS file backups to server storage |
51
53
 
52
54
  ### Guides
53
55
 
@@ -6,7 +6,7 @@
6
6
 
7
7
  ## What is ShadowClaw?
8
8
 
9
- ShadowClaw is a **browser-native AI assistant** — a fully functional agent runtime that runs entirely in the browser with zero server-side AI logic. It's built with TypeScript and bundled with Rolldown.
9
+ ShadowClaw is a **browser-native AI assistant** — a fully functional agent runtime whose orchestration and tool-use loop run client-side, with AI inference routing to remote APIs, local servers, or in-browser models depending on your configuration. It runs as a PWA in any modern browser, as a native desktop app via Electron, as a standalone Node.js server, and is driven via the `shadow-claw` CLI.
10
10
 
11
11
  **Stack at a glance:**
12
12
 
@@ -52,7 +52,7 @@ graph TD
52
52
 
53
53
  ### 1. Browser-native first
54
54
 
55
- Everything runs in the browser. The Express server provides a suite of backend services including:
55
+ The AI runtime and UI are built on browser-native technology regardless of deployment target. The Express server provides a suite of backend services including:
56
56
 
57
57
  - CORS proxying for LLM providers (Bedrock, Vertex AI, Gemini, etc.)
58
58
  - Web Push notification delivery and VAPID management
@@ -159,15 +159,15 @@ sequenceDiagram
159
159
 
160
160
  ## Entry Points
161
161
 
162
- | File | Thread | Role |
163
- | ------------------------------------ | ------- | ----------------------------------------------------------------------------- |
164
- | `src/core/index.ts` | Main | App bootstrap — opens IndexedDB, boots orchestrator, registers service worker |
165
- | `src/worker/worker.ts` | Worker | Agent worker — owns LLM tool-use loop, VM, tool execution |
166
- | `src/server/server.ts` | Node.js | Express dev/prod server — proxy, push, scheduling, static files |
167
- | `electron/main.ts` | Node.js | Electron main process — same Express server in-process |
168
- | `src/service-worker/init.ts` | SW | Service worker bootstrap — Workbox cache registration |
169
- | `src/service-worker/push-handler.ts` | SW | Web Push event handler |
170
- | `src/service-worker/fetch-proxy.ts` | SW | Fetch interception |
162
+ | File | Thread | Role |
163
+ | ------------------------------------ | ------- | ------------------------------------------------------------------------------ |
164
+ | `src/core/index.ts` | Main | App bootstrap — opens IndexedDB, boots orchestrator, registers service worker |
165
+ | `src/worker/worker.ts` | Worker | Agent worker — owns LLM tool-use loop, VM, tool execution |
166
+ | `src/server/server.ts` | Node.js | Express dev/prod server — proxy, push, scheduling, control plane, static files |
167
+ | `electron/main.ts` | Node.js | Electron main process — same Express server in-process |
168
+ | `src/service-worker/init.ts` | SW | Service worker bootstrap — Workbox cache registration |
169
+ | `src/service-worker/push-handler.ts` | SW | Web Push event handler |
170
+ | `src/service-worker/fetch-proxy.ts` | SW | Fetch interception |
171
171
 
172
172
  ## Key Directories
173
173
 
@@ -181,7 +181,7 @@ sequenceDiagram
181
181
  | `src/components/settings/` | Recommended home for settings feature components (incremental migration) |
182
182
  | `src/context/` | Token estimation, dynamic windowing, output truncation |
183
183
  | `src/db/` | IndexedDB layer (granular modules for each DB operation) |
184
- | `src/server/` | Express server + proxy routes |
184
+ | `src/server/` | Express server, control plane, proxy routes, WebRTC, and backup APIs |
185
185
  | `src/service-worker/` | Service worker modules |
186
186
  | `src/shell/` | JS shell emulator + OPFS bridge |
187
187
  | `src/storage/` | OPFS + File System Access API abstractions |
@@ -6,7 +6,7 @@
6
6
 
7
7
  ## Context
8
8
 
9
- ShadowClaw originally shipped as a collection of browser-native ES modules (`.mjs` files) loaded directly by the browser with no build step. This approach allowed for a rapid development loop (edit and refresh).
9
+ ShadowClaw originally shipped as a collection of browser-native ES modules (`.mjs` files) loaded directly by the browser with no build step — before Electron, a Node.js server, CLI tooling, and a production build pipeline were added. This approach allowed for a rapid development loop (edit and refresh).
10
10
 
11
11
  ## Decision
12
12
 
@@ -5,7 +5,7 @@
5
5
 
6
6
  ## Context
7
7
 
8
- ShadowClaw is browser-native. It needs to persist:
8
+ ShadowClaw is browser-native — its storage layer uses Web Platform APIs whether running as a PWA, via Electron, or against a Node.js backend. It needs to persist:
9
9
 
10
10
  1. **Message history** — structured data: role, content, timestamps, groupId
11
11
  2. **Configuration** — API keys, provider selection, model, settings
@@ -140,7 +140,7 @@ To prevent storage state leakage when multiple instances are deployed under subp
140
140
  When booting a namespaced deployment for the first time, ShadowClaw checks if data was previously stored under `"shadowclaw"`.
141
141
  - **IndexedDB**: Copies all object stores into the new namespaced database in a single non-destructive pass, setting `DB_MIGRATED_FROM_LEGACY`.
142
142
  - **OPFS**: Recursively copies all legacy workspace directories and files from `"shadowclaw"` into `"shadowclaw-${namespace}"` (with Safari worker write fallback), setting `OPFS_MIGRATED_FROM_LEGACY`.
143
- The legacy storage is left intact as a fallback seed.
143
+ The legacy storage is left intact as a fallback seed.
144
144
  4. **Namespaced `localStorage` (`namespacedStorage.ts`):**
145
145
  Key pattern `shadowclaw:${namespace}:${key}` with automatic one-time copy-on-read from legacy unprefixed keys.
146
146
  5. **Isolation Assurance:**
@@ -6,7 +6,7 @@
6
6
 
7
7
  ## Context
8
8
 
9
- Building a browser-native AI assistant requires a reactive UI that updates when async operations complete (streaming tokens, tool results, typing indicators). The question was: what framework (if any) to use?
9
+ Building a browser-native AI assistant (one that runs as a PWA, Electron desktop app, Node.js server, and via the CLI) requires a reactive UI that updates when async operations complete (streaming tokens, tool results, typing indicators). The question was: what framework (if any) to use?
10
10
 
11
11
  The original options were:
12
12
 
@@ -1,4 +1,4 @@
1
- <!doctype html><html lang="en"><head><title>ShadowClaw</title><meta charset="utf-8"><meta name="revision" content="e8b19d47c288391f11db48df7a8549298b7de0a7" /><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>
1
+ <!doctype html><html lang="en"><head><title>ShadowClaw</title><meta charset="utf-8"><meta name="revision" content="3859d7ea031a1daeefa92fcdec55ca9b3862bd3f" /><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.
@@ -1,14 +1,15 @@
1
1
  # Configuring Messaging Channels
2
2
 
3
- > Step-by-step setup guides for Telegram and iMessage channel integration in ShadowClaw.
3
+ > Step-by-step setup guides for Telegram, iMessage, and PeerJS WebRTC channel integration in ShadowClaw.
4
4
 
5
- **Settings:** Open Settings → Messaging Channels to configure any of these options.
5
+ **Settings:** Open Settings → Messaging Channels or Settings → WebRTC/PeerJS to configure these options.
6
6
 
7
7
  ## Overview
8
8
 
9
- ShadowClaw supports three messaging channels:
9
+ ShadowClaw supports four messaging channels:
10
10
 
11
11
  - **Browser Chat** (`br:`) — In-app chat interface (always available)
12
+ - **PeerJS WebRTC** (`peer:`) — Peer-to-peer WebRTC DataChannel messaging and remote CLI bridge
12
13
  - **Telegram** (`tg:`) — Telegram Bot API integration
13
14
  - **iMessage** (`im:`) — iMessage bridge via HTTP relay
14
15
 
@@ -210,6 +211,39 @@ if __name__ == '__main__':
210
211
  app.run(port=5000)
211
212
  ```
212
213
 
214
+ ## PeerJS WebRTC Setup
215
+
216
+ ### Prerequisites
217
+
218
+ 1. PeerJS signaling server enabled on dev server (`--peerjs` flag, default with `shadow-claw dev`) or custom PeerJS cloud / self-hosted host.
219
+ 2. CLI peer ID generated via `shadow-claw peer-id` or running `shadow-claw webrtc listen`.
220
+
221
+ ### PeerJS Step-by-Step
222
+
223
+ 1. **Configure Signaling Server in Browser**
224
+ - Open **Settings → WebRTC/PeerJS**.
225
+ - Ensure the signaling host/port matches your server (defaults to `127.0.0.1:8888`).
226
+ - Copy or note your browser peer ID displayed at the top.
227
+
228
+ 2. **Connect to CLI or Remote Peers**
229
+ - In **Settings → WebRTC/PeerJS → Trusted Peer IDs**, add the peer ID of the CLI (e.g. `cli-01m1...`) or remote peer.
230
+ - You can also allow all peers by leaving the trusted peer IDs list empty.
231
+
232
+ 3. **Interact via CLI**
233
+ - In one terminal, start the listener:
234
+ ```bash
235
+ npx shadow-claw webrtc listen
236
+ ```
237
+ - In another terminal, send a message to a specific conversation or the browser chat:
238
+
239
+ ```bash
240
+ # Send directly to the peer channel conversation
241
+ npx shadow-claw send --transport webrtc --client <browser-peer-id> --group "peer:<cli-peer-id>" "Hello from CLI"
242
+
243
+ # Send to the main browser AI assistant
244
+ npx shadow-claw send --transport webrtc --client <browser-peer-id> --group "br:main" "Hello AI"
245
+ ```
246
+
213
247
  ## Managing Channel Conversations
214
248
 
215
249
  ### Creating a Conversation
@@ -218,11 +252,11 @@ When you create a new conversation in ShadowClaw:
218
252
 
219
253
  1. Open the conversation sidebar
220
254
  2. Click the **+** (Create) button
221
- 3. Choose the channel (Browser, Telegram, iMessage)
222
- 4. If Telegram/iMessage: select the chat ID
255
+ 3. Choose the channel (Browser, PeerJS, Telegram, iMessage)
256
+ 4. If PeerJS/Telegram/iMessage: specify or select the peer/chat ID
223
257
  5. Name the conversation and save
224
258
 
225
- The conversation ID is auto-prefixed (`br:`, `tg:`, `im:`) so you can track which channel it belongs to.
259
+ The conversation ID is auto-prefixed (`br:`, `peer:`, `tg:`, `im:`) so you can track which channel it belongs to.
226
260
 
227
261
  ### Switching Between Channels
228
262
 
@@ -233,6 +267,7 @@ All active conversations appear in the sidebar. Click any conversation to switch
233
267
  | Channel | Auto-trigger | Mention required? |
234
268
  | -------- | ------------ | ----------------- |
235
269
  | Browser | Manual only | N/A (click send) |
270
+ | PeerJS | Yes | No |
236
271
  | Telegram | Yes | No |
237
272
  | iMessage | Yes | No |
238
273
 
@@ -115,6 +115,30 @@ npm start -- 8888
115
115
  | `SHADOWCLAW_ALLOW_PRIVATE_PROXY` | Allow `/proxy` to reach private IPs | `1`, `true`, `yes` |
116
116
  | `SHADOWCLAW_ROOT_PATH` | Static files directory root | `/path/to/dist/public` |
117
117
  | `SHADOWCLAW_DATABASE_DIR` | Server SQLite database directory | `/path/to/.cache` |
118
+ | `SHADOWCLAW_CONTROL_TOKEN` | Secret token for control-plane auth | `mysecrettoken` |
119
+
120
+ ## Control Plane
121
+
122
+ Every server instance starts a **Control Plane** (supporting both Server-Sent Events at `/api/control/events` and WebSockets at `/ws/control`) that allows CLI commands and external tools to communicate with connected browser/Electron clients. Connected browser clients use proxy-friendly SSE + HTTP POST by default, with an opt-in WebSocket setting.
123
+
124
+ ```bash
125
+ # Override the auto-generated token
126
+ npx shadow-claw dev --control-token mysecrettoken
127
+
128
+ # Or via environment variable
129
+ SHADOWCLAW_CONTROL_TOKEN=mysecrettoken npm start
130
+ ```
131
+
132
+ On startup the server prints:
133
+
134
+ ```text
135
+ Control plane active at ws://127.0.0.1:8888/ws/control
136
+ Control token: <auto-generated-or-configured-token>
137
+ ```
138
+
139
+ Use this token with the CLI runtime commands (`clients`, `send`, `backup`, `tasks`) or with direct REST/SSE/WebSocket calls to `/api/control/*`.
140
+
141
+ **Full reference:** [docs/subsystems/control-plane.md](../subsystems/control-plane.md)
118
142
 
119
143
  ## Common Scenarios
120
144