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.
- package/README.md +42 -5
- package/bin/cli.mjs +166 -0
- package/bin/cli.test.mjs +60 -0
- package/bin/commands/backup.mjs +70 -0
- package/bin/commands/clients.mjs +41 -0
- package/bin/commands/peer-id.mjs +76 -0
- package/bin/commands/peer-id.test.mjs +104 -0
- package/bin/commands/send.mjs +51 -0
- package/bin/commands/tasks.mjs +59 -0
- package/bin/commands/webrtc-listen.mjs +254 -0
- package/bin/prerender-dsd-shell/prerender-dsd-shell.test.mjs +46 -0
- package/bin/rolldown.config.test.mjs +401 -8
- package/bin/utils/control-client.mjs +243 -0
- package/bin/utils/control-client.test.mjs +83 -0
- package/bin/utils/ulid.mjs +45 -0
- package/bin/utils/webrtc-control-client.mjs +699 -0
- package/bin/utils/webrtc-control-client.test.mjs +318 -0
- package/dist/electron/main.cjs +1 -1
- package/dist/public/AGENTS.md +12 -3
- package/dist/public/README.md +42 -5
- package/dist/public/agent.worker.js +50 -50
- package/dist/public/backup-controller-CvzDOq4T.js +1 -0
- package/dist/public/components/settings/shadow-claw-control-plane/shadow-claw-control-plane.css +1 -0
- package/dist/public/components/settings/shadow-claw-control-plane/shadow-claw-control-plane.html +1 -0
- package/dist/public/components/settings/shadow-claw-task-server/shadow-claw-task-server.css +1 -1
- package/dist/public/components/settings/shadow-claw-task-server/shadow-claw-task-server.html +1 -1
- package/dist/public/components/shadow-claw-settings/shadow-claw-settings.html +1 -1
- package/dist/public/config-CrHchneq.js +1 -0
- package/dist/public/{configurePeerJs-B4Ym9VMj.js → configurePeerJs-UfQ6HUlv.js} +1 -1
- package/dist/public/{connections-BEn6JYdu.js → connections-DvbgdkaB.js} +1 -1
- package/dist/public/{custom-element-security-DdYlUozq.js → custom-element-security-NCqQ5lmC.js} +1 -1
- package/dist/public/docs/README.md +3 -1
- package/dist/public/docs/architecture/overview.md +12 -12
- package/dist/public/docs/decisions/bundled-typescript-architecture.md +1 -1
- package/dist/public/docs/decisions/indexeddb-and-opfs-storage.md +2 -2
- package/dist/public/docs/decisions/native-web-components-and-signals.md +1 -1
- package/dist/public/docs/example/article/index.html +1 -1
- package/dist/public/docs/guides/configuring-messaging-channels.md +41 -6
- package/dist/public/docs/guides/server-development-configuration.md +24 -0
- package/dist/public/docs/publishing/index.html +1 -1
- package/dist/public/docs/skill-creator/index.html +1 -1
- package/dist/public/docs/subsystems/backup.md +56 -0
- package/dist/public/docs/subsystems/cli.md +124 -0
- package/dist/public/docs/subsystems/control-plane.md +169 -0
- package/dist/public/docs/subsystems/notifications.md +54 -2
- package/dist/public/docs/subsystems/reactive-ui.md +1 -0
- package/dist/public/downloadGroupFile-9niHBj9n.js +1 -0
- package/dist/public/e2e/README.md +5 -1
- package/dist/public/{e2e-bridge-BQ4KzUt5.js → e2e-bridge-D28tOggc.js} +1 -1
- package/dist/public/file-viewer-BVg3B_JH.js +1 -0
- package/dist/public/getAllTasks-vpBlgGsc.js +1 -0
- package/dist/public/getGroupDir-P1h9wl6S.js +1 -0
- package/dist/public/{git-DEUvQTUA.js → git-7Vq_SJSp.js} +2 -2
- package/dist/public/{iframe-sanitizer-0NS4XLOd.js → iframe-sanitizer-y2Z1pPwY.js} +1 -1
- package/dist/public/index.html +1 -1
- package/dist/public/index.js +1 -1
- package/dist/public/{initChatSplitResize-llGkSJXg.js → initChatSplitResize-CzV2E7Ul.js} +1 -1
- package/dist/public/initControlPlane-DKNht5ph.js +1 -0
- package/dist/public/llms.txt +1 -1
- package/dist/public/main/index.html +1 -1
- package/dist/public/main/memory/index.html +1 -1
- package/dist/public/{markdown-CyYR4App.js → markdown-SiFq0PTy.js} +1 -1
- package/dist/public/{mcp-reconnect-CSx4aMwy.js → mcp-reconnect-9emsqGRe.js} +1 -1
- package/dist/public/{memoryStorage-Dwg8NhAZ.js → memoryStorage-C0KvLNUp.js} +1 -1
- package/dist/public/orchestrator-8w9-exfN.js +15 -0
- package/dist/public/{orchestrator-DRfEudXk.js → orchestrator-tHWrqi_F.js} +676 -676
- package/dist/public/{peerjs-CurBuKvk.js → peerjs-D8hck8Ih.js} +1 -1
- package/dist/public/push-client-D0lkwrK0.js +1 -0
- package/dist/public/service-worker/fetch-proxy.js +1 -1
- package/dist/public/service-worker/push-handler.js +1 -1
- package/dist/public/service-worker.js +1 -1
- package/dist/public/{shadow-claw-5IHaDDMJ.js → shadow-claw-Cs8wbk3d.js} +2 -2
- package/dist/public/{shadow-claw-accounts-CZvz4iQz.js → shadow-claw-accounts-B4sBTQEj.js} +1 -1
- package/dist/public/{shadow-claw-channels-BKXFyRS3.js → shadow-claw-channels-mY-JMvib.js} +1 -1
- package/dist/public/{shadow-claw-chat-CCLuhWdI.js → shadow-claw-chat-DGPKpmU5.js} +6 -6
- package/dist/public/shadow-claw-control-plane-B1F0jnPk.js +199 -0
- package/dist/public/{shadow-claw-conversations-DV-87UUh.js → shadow-claw-conversations-0Q-VC_7N.js} +1 -1
- package/dist/public/shadow-claw-dialog-BrOwAOdk.js +1 -0
- package/dist/public/{shadow-claw-file-viewer-Dk2dbAUd.js → shadow-claw-file-viewer-AZfG4D-u.js} +1 -1
- package/dist/public/shadow-claw-files-Bx4v0Ggp.js +840 -0
- package/dist/public/{shadow-claw-git-DRA7_qck.js → shadow-claw-git-DD7iQRxq.js} +2 -2
- package/dist/public/{shadow-claw-integrations-1Xu5snuV.js → shadow-claw-integrations-D7ufpwhX.js} +1 -1
- package/dist/public/{shadow-claw-llm-6Aci1BDP.js → shadow-claw-llm-CfeDBZKB.js} +2 -2
- package/dist/public/{shadow-claw-mcp-remote-BUjAeVXC.js → shadow-claw-mcp-remote-kY4LMFNN.js} +1 -1
- package/dist/public/{shadow-claw-networking-FQe4nCp3.js → shadow-claw-networking-e0EarDVs.js} +1 -1
- package/dist/public/{shadow-claw-notifications-DV2wibL3.js → shadow-claw-notifications-BkxuDEQI.js} +3 -3
- package/dist/public/{shadow-claw-page-header-B63S4HTU.js → shadow-claw-page-header-DMKbkv3s.js} +1 -1
- package/dist/public/{shadow-claw-pages-DE3jWj5K.js → shadow-claw-pages-BWSE91I1.js} +1 -1
- package/dist/public/{shadow-claw-settings-S6DAKfYx.js → shadow-claw-settings-D6lPBn6m.js} +10 -3
- package/dist/public/{shadow-claw-storage-DaacA3_K.js → shadow-claw-storage-Crs6LY15.js} +2 -2
- package/dist/public/{shadow-claw-task-server-IjKX042p.js → shadow-claw-task-server-CKm_Ennl.js} +25 -4
- package/dist/public/{shadow-claw-tasks-Cy0JYmGU.js → shadow-claw-tasks-Czx6Ucf5.js} +1 -1
- package/dist/public/{shadow-claw-terminal-znAt7HyP.js → shadow-claw-terminal-Dmtr9KY1.js} +1 -1
- package/dist/public/{shadow-claw-tools-Cutv9Si5.js → shadow-claw-tools-t3Qq4TDO.js} +1 -1
- package/dist/public/{shadow-claw-webvm-BH8mPDEu.js → shadow-claw-webvm-CIuSywD_.js} +1 -1
- package/dist/public/{syncWebMcpRegistration-BqJxaMGR.js → syncWebMcpRegistration-DzC80wVG.js} +1 -1
- package/dist/server.js +286 -189
- package/package.json +2 -1
- package/src/__mocks__/jszip.cjs +15 -5
- package/src/components/settings/shadow-claw-accounts/shadow-claw-accounts.test.ts +35 -0
- package/src/components/settings/shadow-claw-control-plane/shadow-claw-control-plane.css +117 -0
- package/src/components/settings/shadow-claw-control-plane/shadow-claw-control-plane.html +81 -0
- package/src/components/settings/shadow-claw-control-plane/shadow-claw-control-plane.test.ts +127 -0
- package/src/components/settings/shadow-claw-control-plane/shadow-claw-control-plane.ts +292 -0
- package/src/components/settings/shadow-claw-git/shadow-claw-git.test.ts +72 -0
- package/src/components/settings/shadow-claw-integrations/connection-test-auth.test.ts +20 -30
- package/src/components/settings/shadow-claw-integrations/shadow-claw-integrations.test.ts +116 -0
- package/src/components/settings/shadow-claw-llm/model-ranking.test.ts +143 -51
- package/src/components/settings/shadow-claw-llm/shadow-claw-llm.test.ts +307 -4
- package/src/components/settings/shadow-claw-llm/shadow-claw-llm.ts +8 -8
- package/src/components/settings/shadow-claw-mcp-remote/shadow-claw-mcp-remote.test.ts +31 -0
- package/src/components/settings/shadow-claw-notifications/shadow-claw-notifications.test.ts +3 -0
- package/src/components/settings/shadow-claw-notifications/shadow-claw-notifications.ts +43 -9
- package/src/components/settings/shadow-claw-peerjs/shadow-claw-peerjs.test.ts +186 -0
- package/src/components/settings/shadow-claw-storage/shadow-claw-storage.test.ts +76 -0
- package/src/components/settings/shadow-claw-task-server/shadow-claw-task-server.css +23 -2
- package/src/components/settings/shadow-claw-task-server/shadow-claw-task-server.html +1 -1
- package/src/components/settings/shadow-claw-task-server/shadow-claw-task-server.test.ts +19 -0
- package/src/components/settings/shadow-claw-task-server/shadow-claw-task-server.ts +2 -1
- package/src/components/shadow-claw/utils/initChatSplitResize.test.ts +180 -13
- package/src/components/shadow-claw/utils/setupEffects.test.ts +39 -0
- package/src/components/shadow-claw-a2ui/catalog/basic/shared.test.ts +117 -0
- package/src/components/shadow-claw-a2ui/shadow-claw-a2ui.test.ts +1159 -1
- package/src/components/shadow-claw-chat/shadow-claw-chat.test.ts +1523 -19
- package/src/components/shadow-claw-chat/utils/escapeHtml.test.ts +13 -0
- package/src/components/shadow-claw-chat/utils/getPeerChatDisplayStatus.test.ts +20 -0
- package/src/components/shadow-claw-conversations/shadow-claw-conversations.test.ts +494 -0
- package/src/components/shadow-claw-dialog/shadow-claw-dialog.ts +13 -2
- package/src/components/shadow-claw-file-viewer/shadow-claw-file-viewer.test.ts +273 -0
- package/src/components/shadow-claw-files/shadow-claw-files.test.ts +408 -1
- package/src/components/shadow-claw-pdf-viewer/utils/installGetOrInsertComputedPolyfill.test.ts +65 -0
- package/src/components/shadow-claw-settings/shadow-claw-settings.html +7 -0
- package/src/components/shadow-claw-settings/shadow-claw-settings.test.ts +221 -0
- package/src/components/shadow-claw-settings/shadow-claw-settings.ts +2 -0
- package/src/components/shadow-claw-tasks/shadow-claw-tasks.test.ts +407 -0
- package/src/components/shadow-claw-terminal/utils/consumeEscapeSequence.test.ts +58 -0
- package/src/components/shadow-claw-tools/shadow-claw-tools.test.ts +80 -0
- package/src/config/config.ts +10 -0
- package/src/core/backup-controller.test.ts +101 -0
- package/src/core/backup-controller.ts +166 -0
- package/src/core/control-plane-client.test.ts +415 -0
- package/src/core/control-plane-client.ts +441 -0
- package/src/core/orchestrator/orchestrator.ts +4 -0
- package/src/core/orchestrator/utils/compactContext.test.ts +78 -0
- package/src/core/orchestrator/utils/operations/channel.test.ts +254 -9
- package/src/core/orchestrator/utils/operations/provider.test.ts +326 -34
- package/src/core/orchestrator/utils/operations/task.ts +46 -15
- package/src/core/orchestrator/utils/operations/vm.test.ts +114 -2
- package/src/core/orchestrator/utils/syncProxyConfigToServiceWorker.test.ts +60 -0
- package/src/core/utils/initControlPlane.test.ts +276 -0
- package/src/core/utils/initControlPlane.ts +437 -0
- package/src/core/utils/initializeApp.ts +18 -0
- package/src/db/getSession.test.ts +13 -1
- package/src/server/app.test.ts +12 -0
- package/src/server/app.ts +20 -8
- package/src/server/client-registry.test.ts +250 -0
- package/src/server/client-registry.ts +477 -0
- package/src/server/config.ts +15 -0
- package/src/server/control-plane-types.ts +120 -0
- package/src/server/control-plane.test.ts +508 -0
- package/src/server/control-plane.ts +607 -0
- package/src/server/middleware/pna.test.ts +15 -3
- package/src/server/middleware/pna.ts +6 -2
- package/src/server/routes/backup.test.ts +277 -0
- package/src/server/routes/backup.ts +291 -0
- package/src/server/routes/integrations-email.test.ts +532 -0
- package/src/server/routes/llamafile.test.ts +172 -48
- package/src/server/routes/mesh-llm.test.ts +192 -0
- package/src/server/routes/ollama.test.ts +49 -0
- package/src/server/server-peer.test.ts +86 -0
- package/src/server/server-peer.ts +330 -0
- package/src/server/server.ts +37 -0
- package/src/server/services/llamafile-manager.test.ts +230 -6
- package/src/server/services/transformers-runtime.test.ts +186 -18
- package/src/server/webrtc.test.ts +21 -0
- package/src/server/webrtc.ts +72 -0
- package/src/service-worker/fetch-proxy-rules.test.ts +41 -0
- package/src/service-worker/fetch-proxy-rules.ts +15 -8
- package/src/service-worker/fetch-proxy.test.ts +64 -1
- package/src/service-worker/fetch-proxy.ts +18 -2
- package/src/service-worker/push-handler.ts +39 -0
- package/src/shell/vm.test.ts +744 -12
- package/src/storage/ensureMainGroupIndex.test.ts +153 -0
- package/src/storage/getGroupFile.test.ts +66 -0
- package/src/storage/memoryStorage.test.ts +82 -2
- package/src/storage/writeGroupFileBytes.test.ts +143 -0
- package/src/stores/chat-ui.test.ts +20 -0
- package/src/stores/orchestrator.test.ts +134 -0
- package/src/stores/toast.test.ts +50 -0
- package/src/stores/tools.test.ts +147 -10
- package/src/subsystems/channels/imessage.test.ts +59 -0
- package/src/subsystems/channels/peerjs.test.ts +500 -62
- package/src/subsystems/channels/peerjs.ts +71 -4
- package/src/subsystems/channels/telegram.test.ts +177 -0
- package/src/subsystems/email/catalog.test.ts +1 -0
- package/src/subsystems/email/connections.test.ts +39 -1
- package/src/subsystems/git/credentials.test.ts +160 -825
- package/src/subsystems/git/git.test.ts +463 -0
- package/src/subsystems/notifications/__mocks__/node-sqlite.cjs +12 -1
- package/src/subsystems/notifications/push-client.test.ts +5 -1
- package/src/subsystems/notifications/push-client.ts +50 -12
- package/src/subsystems/notifications/push-routes.test.ts +33 -0
- package/src/subsystems/notifications/push-routes.ts +23 -0
- package/src/subsystems/notifications/push-store.ts +1 -1
- package/src/subsystems/providers/builtin-ai-tasks.test.ts +74 -0
- package/src/subsystems/providers/litert-lm-provider.test.ts +212 -51
- package/src/subsystems/providers/model-registry.test.ts +115 -0
- package/src/subsystems/providers/providers.test.ts +94 -1
- package/src/subsystems/providers/transformers-js-provider.test.ts +279 -0
- package/src/subsystems/providers/utils/createModelCacheFetch.test.ts +2 -2
- package/src/subsystems/providers/utils/downloadModelToCache.test.ts +2 -2
- package/src/subsystems/providers/utils/loadModelStream.test.ts +2 -2
- package/src/subsystems/skills/activateSkill.test.ts +101 -0
- package/src/subsystems/skills/discoverSkills.test.ts +251 -0
- package/src/subsystems/tools/cron.test.ts +64 -0
- package/src/subsystems/tools/declarative.test.ts +255 -89
- package/src/testing/assets/v86.9pfs/libv86.mjs +63 -0
- package/src/testing/assets/v86.ext2/libv86.mjs +3 -0
- package/src/testing/assets_missing_v86/assets/v86.9pfs/libv86.mjs +1 -0
- package/src/testing/jest-setup.ts +328 -40
- package/src/testing/mock-v86.mjs +49 -0
- package/src/ui/a2ui/types.ts +4 -3
- package/src/ui/a2ui/utils/normaliseComponentsToMap.test.ts +102 -0
- package/src/ui/a2ui/utils/resolveChildIds.test.ts +63 -0
- package/src/ui/a2ui/utils/resolveDynamicNumber.test.ts +13 -1
- package/src/worker/tools/bash/utils/waitForVMReady.test.ts +40 -0
- package/src/worker/tools/email/email.test.ts +193 -8
- package/src/worker/tools/email/utils/asEmailAttachmentInputs.test.ts +47 -0
- package/src/worker/tools/git/git.test.ts +214 -3
- package/src/worker/tools/rooms/invite-to-room.test.ts +54 -0
- package/src/worker/tools/rooms/leave-room.test.ts +40 -0
- package/src/worker/tools/rooms/list-room-members.test.ts +75 -0
- package/src/worker/tools/tasks/tasks-utils.test.ts +22 -0
- package/src/worker/tools/ui/ask-user.test.ts +50 -0
- package/src/worker/tools/ui/clear-chat.test.ts +19 -0
- package/src/worker/tools/ui/get-current-time.test.ts +19 -0
- package/src/worker/tools/ui/javascript.test.ts +116 -0
- package/src/worker/tools/ui/list-components.test.ts +10 -0
- package/src/worker/tools/ui/list-tool-profiles.test.ts +42 -0
- package/src/worker/tools/ui/manage-tools.test.ts +34 -0
- package/src/worker/tools/ui/render-component.test.ts +346 -0
- package/src/worker/tools/ui/send-notification.test.ts +40 -0
- package/src/worker/tools/ui/utils/getAllowFullInternetAccess.test.ts +31 -0
- package/src/worker/tools/ui/utils/parseBooleanConfig.test.ts +25 -0
- package/src/worker/tools/workspace/attach-file.test.ts +84 -0
- package/src/worker/tools/workspace/copy-file.test.ts +69 -0
- package/src/worker/tools/workspace/create-directory.test.ts +40 -0
- package/src/worker/tools/workspace/delete-file.test.ts +73 -0
- package/src/worker/tools/workspace/diff-files.test.ts +94 -0
- package/src/worker/tools/workspace/list-files.test.ts +36 -0
- package/src/worker/tools/workspace/move-file.test.ts +66 -0
- package/src/worker/tools/workspace/open-file.test.ts +57 -0
- package/src/worker/tools/workspace/patch-file.test.ts +81 -0
- package/src/worker/tools/workspace/read-file.test.ts +116 -0
- package/src/worker/tools/workspace/send-file.test.ts +82 -0
- package/src/worker/tools/workspace/update-memory.test.ts +29 -0
- package/src/worker/tools/workspace/write-file.test.ts +28 -0
- package/src/worker/utils/rate-limit.test.ts +199 -0
- package/src/worker/utils/tool-state.test.ts +29 -0
- package/src/worker/utils/transformers-js-utils.test.ts +59 -0
- package/dist/public/config-BYAuxx3j.js +0 -1
- package/dist/public/downloadGroupFile-Drdvg1Ev.js +0 -1
- package/dist/public/file-viewer-CcZYw-PS.js +0 -1
- package/dist/public/orchestrator-o9Fj67BN.js +0 -15
- package/dist/public/push-client-Dii-vPEh.js +0 -1
- package/dist/public/shadow-claw-dialog-ByyHgxM9.js +0 -1
- 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};
|
package/dist/public/components/settings/shadow-claw-control-plane/shadow-claw-control-plane.css
ADDED
|
@@ -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}
|
package/dist/public/components/settings/shadow-claw-control-plane/shadow-claw-control-plane.html
ADDED
|
@@ -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}
|
package/dist/public/components/settings/shadow-claw-task-server/shadow-claw-task-server.html
CHANGED
|
@@ -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
|
|
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 youtube-nocookie.com youtu.be xt-ml.github.io 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 youtube-nocookie.com youtu.be xt-ml.github.io 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-
|
|
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-
|
|
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};
|
package/dist/public/{custom-element-security-DdYlUozq.js → custom-element-security-NCqQ5lmC.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{r as e}from"./config-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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="
|
|
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
|
|
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
|
|
5
|
+
**Settings:** Open Settings → Messaging Channels or Settings → WebRTC/PeerJS to configure these options.
|
|
6
6
|
|
|
7
7
|
## Overview
|
|
8
8
|
|
|
9
|
-
ShadowClaw supports
|
|
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
|
|