openclaw-multi-auto 1.3.6 → 1.3.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (328) hide show
  1. package/dist/{audio-preflight-5FEeDooz.js → audio-preflight-DDBLZBdb.js} +4 -4
  2. package/dist/{audio-transcription-runner-B-UvoDjZ.js → audio-transcription-runner-DZbSWT9E.js} +1 -1
  3. package/dist/build-info.json +3 -3
  4. package/dist/canvas-host/a2ui/.bundle.hash +1 -1
  5. package/dist/{chrome-D45SyhQL.js → chrome-CMU2WVFh.js} +8 -8
  6. package/dist/{deliver-B9cys0EZ.js → deliver-BXVcFIHL.js} +1 -1
  7. package/dist/{deliver-runtime-DhaQJ0pI.js → deliver-runtime-DTaIS-1i.js} +3 -3
  8. package/dist/{deps-send-whatsapp.runtime-DvTL2tzN.js → deps-send-whatsapp.runtime-CIZqFAqb.js} +7 -7
  9. package/dist/extensionAPI.js +6 -6
  10. package/dist/{image-DAOPwVXi.js → image-BCVLo0qw.js} +1 -1
  11. package/dist/{image-runtime-wlCLVvVv.js → image-runtime-DtCKpMPZ.js} +3 -3
  12. package/dist/{pi-embedded-DYU79yGe.js → pi-embedded-CgQ_W6Xs.js} +24 -24
  13. package/dist/{pi-embedded-helpers-uTRAmQ4n.js → pi-embedded-helpers-CwuBTKza.js} +3 -3
  14. package/dist/plugin-sdk/{accounts-DyFCXtHv.js → accounts-BslAlVYS.js} +2 -2
  15. package/dist/plugin-sdk/{accounts-BJAXxY46.js → accounts-C3m65--E.js} +2 -2
  16. package/dist/plugin-sdk/{accounts-C1j7HSL0.js → accounts-CNCCkdEF.js} +3 -3
  17. package/dist/plugin-sdk/{active-listener-CftX5jLD.js → active-listener-CkPnMUkB.js} +2 -2
  18. package/dist/plugin-sdk/{api-key-rotation-8nyyt1kx.js → api-key-rotation-BXnNsojA.js} +2 -2
  19. package/dist/plugin-sdk/{audio-preflight-C_aSAPR1.js → audio-preflight-CtO4fFvp.js} +26 -26
  20. package/dist/plugin-sdk/{audio-transcription-runner-CB53F7_7.js → audio-transcription-runner-DnxvOS1-.js} +11 -11
  21. package/dist/plugin-sdk/{audit-membership-runtime-BXndI4LG.js → audit-membership-runtime-BpfoSk8M.js} +2 -2
  22. package/dist/plugin-sdk/{channel-activity-C5y8AgAV.js → channel-activity-WJYxcJ3S.js} +3 -3
  23. package/dist/plugin-sdk/{channel-web-DBTRO03V.js → channel-web-dO5k3ubM.js} +18 -18
  24. package/dist/plugin-sdk/{chrome-f00sZkDX.js → chrome-CjNTuJML.js} +6 -6
  25. package/dist/plugin-sdk/{commands-registry-BJ_NxG2F.js → commands-registry-CdYjoI0i.js} +4 -4
  26. package/dist/plugin-sdk/{common-Cf27Jwxu.js → common-oYc5vPFl.js} +2 -2
  27. package/dist/plugin-sdk/{config-CHQrpx-Q.js → config-B1z-UxQ3.js} +7 -7
  28. package/dist/plugin-sdk/{deliver-DNEuetST.js → deliver-D5_6T567.js} +10 -10
  29. package/dist/plugin-sdk/deliver-runtime-C5dgvvga.js +32 -0
  30. package/dist/plugin-sdk/deps-send-discord.runtime-Dg4N7PHJ.js +23 -0
  31. package/dist/plugin-sdk/deps-send-imessage.runtime-0OEwzMQm.js +22 -0
  32. package/dist/plugin-sdk/deps-send-signal.runtime-BM1jRt3G.js +21 -0
  33. package/dist/plugin-sdk/deps-send-slack.runtime-1E3BYRdF.js +19 -0
  34. package/dist/plugin-sdk/deps-send-telegram.runtime-DNCxIflA.js +24 -0
  35. package/dist/plugin-sdk/deps-send-whatsapp.runtime-OLwr-9c8.js +57 -0
  36. package/dist/plugin-sdk/{diagnostic-LYUUmjJ5.js → diagnostic-Bxxu0ig-.js} +2 -2
  37. package/dist/plugin-sdk/{errors-CtMWwS2Z.js → errors-B3cHyZZA.js} +1 -1
  38. package/dist/plugin-sdk/{fetch-guard-CxYB5Kg6.js → fetch-guard-Dcgod0tg.js} +2 -2
  39. package/dist/plugin-sdk/{fs-safe-DtfhxbrI.js → fs-safe-BaKqI3G4.js} +3 -3
  40. package/dist/plugin-sdk/{image-BwjYjRHx.js → image-B2mQW9Rb.js} +6 -6
  41. package/dist/plugin-sdk/{image-ops-BnZKcbd6.js → image-ops-Cbzr4U9l.js} +2 -2
  42. package/dist/plugin-sdk/image-runtime-BFm45j49.js +25 -0
  43. package/dist/plugin-sdk/{ir-Z4hX67TJ.js → ir-ZEmrTr4J.js} +7 -7
  44. package/dist/plugin-sdk/{local-roots-KhjQw04O.js → local-roots-CIPRxA-4.js} +4 -4
  45. package/dist/plugin-sdk/{logger-DHIIvMxj.js → logger-CvPFVOgT.js} +2 -2
  46. package/dist/plugin-sdk/{login-C31642Ld.js → login-CCTew9bt.js} +4 -4
  47. package/dist/plugin-sdk/{login-qr--y2SG_Ue.js → login-qr-BI3Vi_wJ.js} +5 -5
  48. package/dist/plugin-sdk/{manager-2UZBMCc7.js → manager-BEoYPn7R.js} +8 -8
  49. package/dist/plugin-sdk/manager-runtime-DxclHQ4U.js +15 -0
  50. package/dist/plugin-sdk/{outbound-Ba0QUI5h.js → outbound-ByOw1K6W.js} +5 -5
  51. package/dist/plugin-sdk/{outbound-attachment-B1Laso-8.js → outbound-attachment-BzVhxRRw.js} +2 -2
  52. package/dist/plugin-sdk/{path-alias-guards-C7Vm5DZ1.js → path-alias-guards-sWayacde.js} +1 -1
  53. package/dist/plugin-sdk/{paths-DopV9PQG.js → paths-Dpg3qxcl.js} +1 -1
  54. package/dist/plugin-sdk/{pi-embedded-helpers-DnA_OCzP.js → pi-embedded-helpers-DIxXkGJf.js} +16 -16
  55. package/dist/plugin-sdk/{pi-model-discovery-DdPqXk8f.js → pi-model-discovery-DM_2uFtj.js} +1 -1
  56. package/dist/plugin-sdk/pi-model-discovery-runtime-BuzvkvNR.js +8 -0
  57. package/dist/plugin-sdk/{pi-tools.before-tool-call.runtime-DxFHiLUE.js → pi-tools.before-tool-call.runtime-w1dqL_ty.js} +4 -4
  58. package/dist/plugin-sdk/{plugins-CbCt4osF.js → plugins-C4USiH29.js} +4 -4
  59. package/dist/plugin-sdk/{proxy-env-C63mMdas.js → proxy-env-ET-rp8eg.js} +1 -1
  60. package/dist/plugin-sdk/{proxy-fetch-Ch95c_Y2.js → proxy-fetch-uDXGKG3Z.js} +1 -1
  61. package/dist/plugin-sdk/{pw-ai-DpJk62D4.js → pw-ai-CyOt3RDA.js} +9 -9
  62. package/dist/plugin-sdk/{qmd-manager-Ca-iSfEE.js → qmd-manager-BySdoVR7.js} +7 -7
  63. package/dist/plugin-sdk/{query-expansion-B_Xe41Ab.js → query-expansion-C6uS-7lj.js} +4 -4
  64. package/dist/plugin-sdk/{redact-hp9TOulW.js → redact-Bvxt1T_Q.js} +1 -1
  65. package/dist/plugin-sdk/{reply-CovBlFea.js → reply-CTCSeQqW.js} +73 -73
  66. package/dist/plugin-sdk/{resolve-outbound-target-BbrHgyUk.js → resolve-outbound-target-Bw8YNANu.js} +2 -2
  67. package/dist/plugin-sdk/{run-with-concurrency-BR1DXa8T.js → run-with-concurrency-C_KCHwvf.js} +1 -1
  68. package/dist/plugin-sdk/runtime-whatsapp-login.runtime-BxgRDkhc.js +10 -0
  69. package/dist/plugin-sdk/runtime-whatsapp-outbound.runtime-elOqrkfg.js +19 -0
  70. package/dist/plugin-sdk/{send-BvAtLLPl.js → send-BZ6nYFZr.js} +5 -5
  71. package/dist/plugin-sdk/{send-BTztm3D2.js → send-C0w6xP2x.js} +6 -6
  72. package/dist/plugin-sdk/{send-CWJUuG0i.js → send-CFf-1V89.js} +8 -8
  73. package/dist/plugin-sdk/{send-EcglC4cG.js → send-CY-Qfwia.js} +7 -7
  74. package/dist/plugin-sdk/{send-BXpXBwM_.js → send-qPyNGSe4.js} +13 -13
  75. package/dist/plugin-sdk/{session-k256LJZT.js → session-COrvpvUQ.js} +3 -3
  76. package/dist/plugin-sdk/signal.js +2 -2
  77. package/dist/plugin-sdk/{skill-commands-DoRqLzxm.js → skill-commands-DZqhtmiv.js} +4 -4
  78. package/dist/plugin-sdk/{skills-QudILG6e.js → skills-Cw_vXEJb.js} +6 -6
  79. package/dist/plugin-sdk/slash-commands.runtime-D67JLweo.js +13 -0
  80. package/dist/plugin-sdk/slash-dispatch.runtime-DvcpvCJ0.js +52 -0
  81. package/dist/plugin-sdk/slash-skill-commands.runtime-BM1x3azR.js +16 -0
  82. package/dist/plugin-sdk/{store-BbDQw3g6.js → store-CMHj6IIw.js} +2 -2
  83. package/dist/plugin-sdk/subagent-registry-runtime-1lbDyRzz.js +52 -0
  84. package/dist/plugin-sdk/{tables-BhvloMKN.js → tables-CSqrHsKL.js} +1 -1
  85. package/dist/plugin-sdk/{thinking-URzkT-3p.js → thinking-DOnsR_A8.js} +7 -7
  86. package/dist/plugin-sdk/{tokens-B1PW5Ayy.js → tokens-BDr0Z9o3.js} +1 -1
  87. package/dist/plugin-sdk/{tool-images-xpqbP6RR.js → tool-images-eEfOVkzf.js} +2 -2
  88. package/dist/plugin-sdk/web-BLyT64pW.js +56 -0
  89. package/dist/plugin-sdk/{whatsapp-actions-RcZ6vp61.js → whatsapp-actions-xcleMoMv.js} +17 -17
  90. package/dist/plugin-sdk/whatsapp.js +50 -50
  91. package/dist/{pw-ai-GcYO6HPE.js → pw-ai-CmphSzHx.js} +1 -1
  92. package/dist/{slash-dispatch.runtime-Dh053pQK.js → slash-dispatch.runtime-131yup2e.js} +6 -6
  93. package/dist/{subagent-registry-runtime-DSi5mnCQ.js → subagent-registry-runtime-DbSf_Je6.js} +6 -6
  94. package/dist/{web-1hWJDzNA.js → web-MR9d7KyB.js} +6 -6
  95. package/package.json +5 -2
  96. package/scripts/create-instance.sh +44 -19
  97. package/scripts/install-maca.sh +39 -28
  98. package/scripts/npm_publish.sh +8 -6
  99. package/ui/index.html +16 -0
  100. package/ui/node_modules/.bin/jiti +21 -0
  101. package/ui/node_modules/.bin/lessc +21 -0
  102. package/ui/node_modules/.bin/marked +21 -0
  103. package/ui/node_modules/.bin/playwright +21 -0
  104. package/ui/node_modules/.bin/sass +21 -0
  105. package/ui/node_modules/.bin/tsx +21 -0
  106. package/ui/node_modules/.bin/vite +21 -0
  107. package/ui/node_modules/.bin/vitest +21 -0
  108. package/ui/node_modules/.bin/yaml +21 -0
  109. package/ui/package.json +27 -0
  110. package/ui/public/apple-touch-icon.png +0 -0
  111. package/ui/public/favicon-32.png +0 -0
  112. package/ui/public/favicon.ico +0 -0
  113. package/ui/public/favicon.svg +22 -0
  114. package/ui/src/css.d.ts +1 -0
  115. package/ui/src/i18n/index.ts +3 -0
  116. package/ui/src/i18n/lib/lit-controller.ts +22 -0
  117. package/ui/src/i18n/lib/registry.ts +64 -0
  118. package/ui/src/i18n/lib/translate.ts +123 -0
  119. package/ui/src/i18n/lib/types.ts +9 -0
  120. package/ui/src/i18n/locales/de.ts +129 -0
  121. package/ui/src/i18n/locales/en.ts +337 -0
  122. package/ui/src/i18n/locales/pt-BR.ts +128 -0
  123. package/ui/src/i18n/locales/zh-CN.ts +330 -0
  124. package/ui/src/i18n/locales/zh-TW.ts +125 -0
  125. package/ui/src/i18n/test/translate.test.ts +56 -0
  126. package/ui/src/main.ts +2 -0
  127. package/ui/src/styles/base.css +385 -0
  128. package/ui/src/styles/chat/grouped.css +300 -0
  129. package/ui/src/styles/chat/layout.css +481 -0
  130. package/ui/src/styles/chat/sidebar.css +117 -0
  131. package/ui/src/styles/chat/text.css +146 -0
  132. package/ui/src/styles/chat/tool-cards.css +202 -0
  133. package/ui/src/styles/chat.css +5 -0
  134. package/ui/src/styles/components.css +2612 -0
  135. package/ui/src/styles/config.css +1658 -0
  136. package/ui/src/styles/layout.css +621 -0
  137. package/ui/src/styles/layout.mobile.css +374 -0
  138. package/ui/src/styles.css +5 -0
  139. package/ui/src/ui/__screenshots__/config-form.browser.test.ts/config-form-renderer-flags-unsupported-unions-1.png +0 -0
  140. package/ui/src/ui/__screenshots__/config-form.browser.test.ts/config-form-renderer-renders-inputs-and-patches-values-1.png +0 -0
  141. package/ui/src/ui/__screenshots__/config-form.browser.test.ts/config-form-renderer-renders-union-literals-as-select-options-1.png +0 -0
  142. package/ui/src/ui/__screenshots__/navigation.browser.test.ts/control-UI-routing-auto-scrolls-chat-history-to-the-latest-message-1.png +0 -0
  143. package/ui/src/ui/app-channels.ts +279 -0
  144. package/ui/src/ui/app-chat.ts +266 -0
  145. package/ui/src/ui/app-defaults.ts +50 -0
  146. package/ui/src/ui/app-events.ts +5 -0
  147. package/ui/src/ui/app-gateway.node.test.ts +229 -0
  148. package/ui/src/ui/app-gateway.ts +349 -0
  149. package/ui/src/ui/app-lifecycle.node.test.ts +44 -0
  150. package/ui/src/ui/app-lifecycle.ts +109 -0
  151. package/ui/src/ui/app-polling.ts +69 -0
  152. package/ui/src/ui/app-render-usage-tab.ts +273 -0
  153. package/ui/src/ui/app-render.helpers.node.test.ts +286 -0
  154. package/ui/src/ui/app-render.helpers.ts +574 -0
  155. package/ui/src/ui/app-render.ts +1168 -0
  156. package/ui/src/ui/app-scroll.test.ts +275 -0
  157. package/ui/src/ui/app-scroll.ts +179 -0
  158. package/ui/src/ui/app-settings.test.ts +70 -0
  159. package/ui/src/ui/app-settings.ts +440 -0
  160. package/ui/src/ui/app-tool-stream.node.test.ts +139 -0
  161. package/ui/src/ui/app-tool-stream.ts +455 -0
  162. package/ui/src/ui/app-view-state.ts +321 -0
  163. package/ui/src/ui/app.ts +621 -0
  164. package/ui/src/ui/assistant-identity.ts +23 -0
  165. package/ui/src/ui/chat/constants.ts +12 -0
  166. package/ui/src/ui/chat/copy-as-markdown.ts +97 -0
  167. package/ui/src/ui/chat/grouped-render.ts +287 -0
  168. package/ui/src/ui/chat/message-extract.test.ts +64 -0
  169. package/ui/src/ui/chat/message-extract.ts +122 -0
  170. package/ui/src/ui/chat/message-normalizer.test.ts +179 -0
  171. package/ui/src/ui/chat/message-normalizer.ts +101 -0
  172. package/ui/src/ui/chat/tool-cards.ts +156 -0
  173. package/ui/src/ui/chat/tool-helpers.test.ts +141 -0
  174. package/ui/src/ui/chat/tool-helpers.ts +37 -0
  175. package/ui/src/ui/chat-event-reload.test.ts +47 -0
  176. package/ui/src/ui/chat-event-reload.ts +16 -0
  177. package/ui/src/ui/chat-markdown.browser.test.ts +37 -0
  178. package/ui/src/ui/components/resizable-divider.ts +110 -0
  179. package/ui/src/ui/config-form.browser.test.ts +443 -0
  180. package/ui/src/ui/controllers/agent-files.ts +126 -0
  181. package/ui/src/ui/controllers/agent-identity.ts +59 -0
  182. package/ui/src/ui/controllers/agent-skills.ts +33 -0
  183. package/ui/src/ui/controllers/agents.test.ts +61 -0
  184. package/ui/src/ui/controllers/agents.ts +64 -0
  185. package/ui/src/ui/controllers/assistant-identity.ts +34 -0
  186. package/ui/src/ui/controllers/channels.ts +94 -0
  187. package/ui/src/ui/controllers/channels.types.ts +15 -0
  188. package/ui/src/ui/controllers/chat.test.ts +568 -0
  189. package/ui/src/ui/controllers/chat.ts +318 -0
  190. package/ui/src/ui/controllers/config/form-coerce.ts +160 -0
  191. package/ui/src/ui/controllers/config/form-utils.node.test.ts +455 -0
  192. package/ui/src/ui/controllers/config/form-utils.ts +90 -0
  193. package/ui/src/ui/controllers/config.test.ts +289 -0
  194. package/ui/src/ui/controllers/config.ts +219 -0
  195. package/ui/src/ui/controllers/control-ui-bootstrap.test.ts +82 -0
  196. package/ui/src/ui/controllers/control-ui-bootstrap.ts +49 -0
  197. package/ui/src/ui/controllers/cron-filters.test.ts +81 -0
  198. package/ui/src/ui/controllers/cron.test.ts +1070 -0
  199. package/ui/src/ui/controllers/cron.ts +921 -0
  200. package/ui/src/ui/controllers/debug.ts +60 -0
  201. package/ui/src/ui/controllers/devices.ts +159 -0
  202. package/ui/src/ui/controllers/exec-approval.ts +100 -0
  203. package/ui/src/ui/controllers/exec-approvals.ts +170 -0
  204. package/ui/src/ui/controllers/logs.ts +147 -0
  205. package/ui/src/ui/controllers/nodes.ts +32 -0
  206. package/ui/src/ui/controllers/presence.ts +37 -0
  207. package/ui/src/ui/controllers/sessions.test.ts +104 -0
  208. package/ui/src/ui/controllers/sessions.ts +127 -0
  209. package/ui/src/ui/controllers/skills.ts +157 -0
  210. package/ui/src/ui/controllers/usage.node.test.ts +181 -0
  211. package/ui/src/ui/controllers/usage.ts +315 -0
  212. package/ui/src/ui/data/moonshot-kimi-k2.ts +45 -0
  213. package/ui/src/ui/device-auth.ts +73 -0
  214. package/ui/src/ui/device-identity.ts +112 -0
  215. package/ui/src/ui/external-link.test.ts +18 -0
  216. package/ui/src/ui/external-link.ts +19 -0
  217. package/ui/src/ui/focus-mode.browser.test.ts +39 -0
  218. package/ui/src/ui/format.test.ts +101 -0
  219. package/ui/src/ui/format.ts +60 -0
  220. package/ui/src/ui/gateway.ts +360 -0
  221. package/ui/src/ui/icons.ts +256 -0
  222. package/ui/src/ui/markdown.test.ts +85 -0
  223. package/ui/src/ui/markdown.ts +139 -0
  224. package/ui/src/ui/navigation.browser.test.ts +188 -0
  225. package/ui/src/ui/navigation.test.ts +189 -0
  226. package/ui/src/ui/navigation.ts +165 -0
  227. package/ui/src/ui/open-external-url.test.ts +108 -0
  228. package/ui/src/ui/open-external-url.ts +73 -0
  229. package/ui/src/ui/presenter.ts +85 -0
  230. package/ui/src/ui/storage.node.test.ts +63 -0
  231. package/ui/src/ui/storage.ts +99 -0
  232. package/ui/src/ui/test-helpers/app-mount.ts +27 -0
  233. package/ui/src/ui/text-direction.test.ts +24 -0
  234. package/ui/src/ui/text-direction.ts +30 -0
  235. package/ui/src/ui/theme-transition.ts +109 -0
  236. package/ui/src/ui/theme.ts +16 -0
  237. package/ui/src/ui/tool-display.ts +159 -0
  238. package/ui/src/ui/types/chat-types.ts +44 -0
  239. package/ui/src/ui/types.ts +627 -0
  240. package/ui/src/ui/ui-types.ts +54 -0
  241. package/ui/src/ui/usage-helpers.node.test.ts +43 -0
  242. package/ui/src/ui/usage-helpers.ts +321 -0
  243. package/ui/src/ui/usage-types.ts +22 -0
  244. package/ui/src/ui/uuid.test.ts +41 -0
  245. package/ui/src/ui/uuid.ts +57 -0
  246. package/ui/src/ui/views/agents-panels-status-files.ts +461 -0
  247. package/ui/src/ui/views/agents-panels-tools-skills.browser.test.ts +102 -0
  248. package/ui/src/ui/views/agents-panels-tools-skills.ts +537 -0
  249. package/ui/src/ui/views/agents-utils.test.ts +100 -0
  250. package/ui/src/ui/views/agents-utils.ts +502 -0
  251. package/ui/src/ui/views/agents.ts +499 -0
  252. package/ui/src/ui/views/channel-config-extras.ts +49 -0
  253. package/ui/src/ui/views/channels.config.ts +155 -0
  254. package/ui/src/ui/views/channels.discord.ts +65 -0
  255. package/ui/src/ui/views/channels.googlechat.ts +79 -0
  256. package/ui/src/ui/views/channels.imessage.ts +65 -0
  257. package/ui/src/ui/views/channels.nostr-profile-form.ts +321 -0
  258. package/ui/src/ui/views/channels.nostr.ts +237 -0
  259. package/ui/src/ui/views/channels.shared.ts +38 -0
  260. package/ui/src/ui/views/channels.signal.ts +69 -0
  261. package/ui/src/ui/views/channels.slack.ts +65 -0
  262. package/ui/src/ui/views/channels.telegram.ts +120 -0
  263. package/ui/src/ui/views/channels.ts +325 -0
  264. package/ui/src/ui/views/channels.types.ts +62 -0
  265. package/ui/src/ui/views/channels.whatsapp.ts +118 -0
  266. package/ui/src/ui/views/chat-image-open.browser.test.ts +70 -0
  267. package/ui/src/ui/views/chat.test.ts +227 -0
  268. package/ui/src/ui/views/chat.ts +616 -0
  269. package/ui/src/ui/views/config-form.analyze.ts +267 -0
  270. package/ui/src/ui/views/config-form.node.ts +1073 -0
  271. package/ui/src/ui/views/config-form.render.ts +478 -0
  272. package/ui/src/ui/views/config-form.search.node.test.ts +69 -0
  273. package/ui/src/ui/views/config-form.shared.ts +96 -0
  274. package/ui/src/ui/views/config-form.ts +4 -0
  275. package/ui/src/ui/views/config-search.node.test.ts +50 -0
  276. package/ui/src/ui/views/config-search.ts +92 -0
  277. package/ui/src/ui/views/config.browser.test.ts +233 -0
  278. package/ui/src/ui/views/config.ts +820 -0
  279. package/ui/src/ui/views/cron.test.ts +741 -0
  280. package/ui/src/ui/views/cron.ts +1758 -0
  281. package/ui/src/ui/views/debug.ts +151 -0
  282. package/ui/src/ui/views/exec-approval.ts +89 -0
  283. package/ui/src/ui/views/gateway-url-confirmation.ts +40 -0
  284. package/ui/src/ui/views/instances.ts +89 -0
  285. package/ui/src/ui/views/logs.ts +155 -0
  286. package/ui/src/ui/views/markdown-sidebar.ts +40 -0
  287. package/ui/src/ui/views/nodes-exec-approvals.ts +617 -0
  288. package/ui/src/ui/views/nodes-shared.ts +67 -0
  289. package/ui/src/ui/views/nodes.ts +485 -0
  290. package/ui/src/ui/views/overview-hints.ts +16 -0
  291. package/ui/src/ui/views/overview.node.test.ts +39 -0
  292. package/ui/src/ui/views/overview.ts +361 -0
  293. package/ui/src/ui/views/sessions.test.ts +81 -0
  294. package/ui/src/ui/views/sessions.ts +321 -0
  295. package/ui/src/ui/views/skills-grouping.ts +40 -0
  296. package/ui/src/ui/views/skills-shared.ts +52 -0
  297. package/ui/src/ui/views/skills.ts +192 -0
  298. package/ui/src/ui/views/usage-metrics.ts +578 -0
  299. package/ui/src/ui/views/usage-query.ts +277 -0
  300. package/ui/src/ui/views/usage-render-details.test.ts +136 -0
  301. package/ui/src/ui/views/usage-render-details.ts +1083 -0
  302. package/ui/src/ui/views/usage-render-overview.ts +796 -0
  303. package/ui/src/ui/views/usage-styles/usageStyles-part1.ts +701 -0
  304. package/ui/src/ui/views/usage-styles/usageStyles-part2.ts +702 -0
  305. package/ui/src/ui/views/usage-styles/usageStyles-part3.ts +551 -0
  306. package/ui/src/ui/views/usage.ts +836 -0
  307. package/ui/src/ui/views/usageStyles.ts +5 -0
  308. package/ui/src/ui/views/usageTypes.ts +105 -0
  309. package/ui/vite.config.ts +43 -0
  310. package/ui/vitest.config.ts +15 -0
  311. package/ui/vitest.node.config.ts +10 -0
  312. package/dist/plugin-sdk/deliver-runtime-BFdqklJM.js +0 -32
  313. package/dist/plugin-sdk/deps-send-discord.runtime-DuqpYwU0.js +0 -23
  314. package/dist/plugin-sdk/deps-send-imessage.runtime-CZ2rS8Lb.js +0 -22
  315. package/dist/plugin-sdk/deps-send-signal.runtime-BdqiWhIh.js +0 -21
  316. package/dist/plugin-sdk/deps-send-slack.runtime-04s36qiC.js +0 -19
  317. package/dist/plugin-sdk/deps-send-telegram.runtime-LE5tkPvr.js +0 -24
  318. package/dist/plugin-sdk/deps-send-whatsapp.runtime-Bz57lobC.js +0 -57
  319. package/dist/plugin-sdk/image-runtime-B8twoubs.js +0 -25
  320. package/dist/plugin-sdk/manager-runtime-CMeLwose.js +0 -15
  321. package/dist/plugin-sdk/pi-model-discovery-runtime-D8CJhtJY.js +0 -8
  322. package/dist/plugin-sdk/runtime-whatsapp-login.runtime-SkO91TZH.js +0 -10
  323. package/dist/plugin-sdk/runtime-whatsapp-outbound.runtime-B0VWK5hm.js +0 -19
  324. package/dist/plugin-sdk/slash-commands.runtime-DS6vCNSL.js +0 -13
  325. package/dist/plugin-sdk/slash-dispatch.runtime-BXrxb2wd.js +0 -52
  326. package/dist/plugin-sdk/slash-skill-commands.runtime-Bd6qQ2oT.js +0 -16
  327. package/dist/plugin-sdk/subagent-registry-runtime-1uwQbuXj.js +0 -52
  328. package/dist/plugin-sdk/web-B74yhL2N.js +0 -56
@@ -0,0 +1,617 @@
1
+ import { html, nothing } from "lit";
2
+ import type {
3
+ ExecApprovalsAllowlistEntry,
4
+ ExecApprovalsFile,
5
+ } from "../controllers/exec-approvals.ts";
6
+ import { clampText, formatRelativeTimestamp } from "../format.ts";
7
+ import {
8
+ resolveConfigAgents as resolveSharedConfigAgents,
9
+ resolveNodeTargets,
10
+ type NodeTargetOption,
11
+ } from "./nodes-shared.ts";
12
+ import type { NodesProps } from "./nodes.ts";
13
+
14
+ type ExecSecurity = "deny" | "allowlist" | "full";
15
+ type ExecAsk = "off" | "on-miss" | "always";
16
+
17
+ type ExecApprovalsResolvedDefaults = {
18
+ security: ExecSecurity;
19
+ ask: ExecAsk;
20
+ askFallback: ExecSecurity;
21
+ autoAllowSkills: boolean;
22
+ };
23
+
24
+ type ExecApprovalsAgentOption = {
25
+ id: string;
26
+ name?: string;
27
+ isDefault?: boolean;
28
+ };
29
+
30
+ type ExecApprovalsTargetNode = NodeTargetOption;
31
+
32
+ type ExecApprovalsState = {
33
+ ready: boolean;
34
+ disabled: boolean;
35
+ dirty: boolean;
36
+ loading: boolean;
37
+ saving: boolean;
38
+ form: ExecApprovalsFile | null;
39
+ defaults: ExecApprovalsResolvedDefaults;
40
+ selectedScope: string;
41
+ selectedAgent: Record<string, unknown> | null;
42
+ agents: ExecApprovalsAgentOption[];
43
+ allowlist: ExecApprovalsAllowlistEntry[];
44
+ target: "gateway" | "node";
45
+ targetNodeId: string | null;
46
+ targetNodes: ExecApprovalsTargetNode[];
47
+ onSelectScope: (agentId: string) => void;
48
+ onSelectTarget: (kind: "gateway" | "node", nodeId: string | null) => void;
49
+ onPatch: (path: Array<string | number>, value: unknown) => void;
50
+ onRemove: (path: Array<string | number>) => void;
51
+ onLoad: () => void;
52
+ onSave: () => void;
53
+ };
54
+
55
+ const EXEC_APPROVALS_DEFAULT_SCOPE = "__defaults__";
56
+
57
+ const SECURITY_OPTIONS: Array<{ value: ExecSecurity; label: string }> = [
58
+ { value: "deny", label: "Deny" },
59
+ { value: "allowlist", label: "Allowlist" },
60
+ { value: "full", label: "Full" },
61
+ ];
62
+
63
+ const ASK_OPTIONS: Array<{ value: ExecAsk; label: string }> = [
64
+ { value: "off", label: "Off" },
65
+ { value: "on-miss", label: "On miss" },
66
+ { value: "always", label: "Always" },
67
+ ];
68
+
69
+ function normalizeSecurity(value?: string): ExecSecurity {
70
+ if (value === "allowlist" || value === "full" || value === "deny") {
71
+ return value;
72
+ }
73
+ return "deny";
74
+ }
75
+
76
+ function normalizeAsk(value?: string): ExecAsk {
77
+ if (value === "always" || value === "off" || value === "on-miss") {
78
+ return value;
79
+ }
80
+ return "on-miss";
81
+ }
82
+
83
+ function resolveExecApprovalsDefaults(
84
+ form: ExecApprovalsFile | null,
85
+ ): ExecApprovalsResolvedDefaults {
86
+ const defaults = form?.defaults ?? {};
87
+ return {
88
+ security: normalizeSecurity(defaults.security),
89
+ ask: normalizeAsk(defaults.ask),
90
+ askFallback: normalizeSecurity(defaults.askFallback ?? "deny"),
91
+ autoAllowSkills: Boolean(defaults.autoAllowSkills ?? false),
92
+ };
93
+ }
94
+
95
+ function resolveConfigAgents(config: Record<string, unknown> | null): ExecApprovalsAgentOption[] {
96
+ return resolveSharedConfigAgents(config).map((entry) => ({
97
+ id: entry.id,
98
+ name: entry.name,
99
+ isDefault: entry.isDefault,
100
+ }));
101
+ }
102
+
103
+ function resolveExecApprovalsAgents(
104
+ config: Record<string, unknown> | null,
105
+ form: ExecApprovalsFile | null,
106
+ ): ExecApprovalsAgentOption[] {
107
+ const configAgents = resolveConfigAgents(config);
108
+ const approvalsAgents = Object.keys(form?.agents ?? {});
109
+ const merged = new Map<string, ExecApprovalsAgentOption>();
110
+ configAgents.forEach((agent) => merged.set(agent.id, agent));
111
+ approvalsAgents.forEach((id) => {
112
+ if (merged.has(id)) {
113
+ return;
114
+ }
115
+ merged.set(id, { id });
116
+ });
117
+ const agents = Array.from(merged.values());
118
+ if (agents.length === 0) {
119
+ agents.push({ id: "main", isDefault: true });
120
+ }
121
+ agents.sort((a, b) => {
122
+ if (a.isDefault && !b.isDefault) {
123
+ return -1;
124
+ }
125
+ if (!a.isDefault && b.isDefault) {
126
+ return 1;
127
+ }
128
+ const aLabel = a.name?.trim() ? a.name : a.id;
129
+ const bLabel = b.name?.trim() ? b.name : b.id;
130
+ return aLabel.localeCompare(bLabel);
131
+ });
132
+ return agents;
133
+ }
134
+
135
+ function resolveExecApprovalsScope(
136
+ selected: string | null,
137
+ agents: ExecApprovalsAgentOption[],
138
+ ): string {
139
+ if (selected === EXEC_APPROVALS_DEFAULT_SCOPE) {
140
+ return EXEC_APPROVALS_DEFAULT_SCOPE;
141
+ }
142
+ if (selected && agents.some((agent) => agent.id === selected)) {
143
+ return selected;
144
+ }
145
+ return EXEC_APPROVALS_DEFAULT_SCOPE;
146
+ }
147
+
148
+ export function resolveExecApprovalsState(props: NodesProps): ExecApprovalsState {
149
+ const form = props.execApprovalsForm ?? props.execApprovalsSnapshot?.file ?? null;
150
+ const ready = Boolean(form);
151
+ const defaults = resolveExecApprovalsDefaults(form);
152
+ const agents = resolveExecApprovalsAgents(props.configForm, form);
153
+ const targetNodes = resolveExecApprovalsNodes(props.nodes);
154
+ const target = props.execApprovalsTarget;
155
+ let targetNodeId =
156
+ target === "node" && props.execApprovalsTargetNodeId ? props.execApprovalsTargetNodeId : null;
157
+ if (target === "node" && targetNodeId && !targetNodes.some((node) => node.id === targetNodeId)) {
158
+ targetNodeId = null;
159
+ }
160
+ const selectedScope = resolveExecApprovalsScope(props.execApprovalsSelectedAgent, agents);
161
+ const selectedAgent =
162
+ selectedScope !== EXEC_APPROVALS_DEFAULT_SCOPE
163
+ ? (((form?.agents ?? {})[selectedScope] as Record<string, unknown> | undefined) ?? null)
164
+ : null;
165
+ const allowlist = Array.isArray((selectedAgent as { allowlist?: unknown })?.allowlist)
166
+ ? ((selectedAgent as { allowlist?: ExecApprovalsAllowlistEntry[] }).allowlist ?? [])
167
+ : [];
168
+ return {
169
+ ready,
170
+ disabled: props.execApprovalsSaving || props.execApprovalsLoading,
171
+ dirty: props.execApprovalsDirty,
172
+ loading: props.execApprovalsLoading,
173
+ saving: props.execApprovalsSaving,
174
+ form,
175
+ defaults,
176
+ selectedScope,
177
+ selectedAgent,
178
+ agents,
179
+ allowlist,
180
+ target,
181
+ targetNodeId,
182
+ targetNodes,
183
+ onSelectScope: props.onExecApprovalsSelectAgent,
184
+ onSelectTarget: props.onExecApprovalsTargetChange,
185
+ onPatch: props.onExecApprovalsPatch,
186
+ onRemove: props.onExecApprovalsRemove,
187
+ onLoad: props.onLoadExecApprovals,
188
+ onSave: props.onSaveExecApprovals,
189
+ };
190
+ }
191
+
192
+ export function renderExecApprovals(state: ExecApprovalsState) {
193
+ const ready = state.ready;
194
+ const targetReady = state.target !== "node" || Boolean(state.targetNodeId);
195
+ return html`
196
+ <section class="card">
197
+ <div class="row" style="justify-content: space-between; align-items: center;">
198
+ <div>
199
+ <div class="card-title">Exec approvals</div>
200
+ <div class="card-sub">
201
+ Allowlist and approval policy for <span class="mono">exec host=gateway/node</span>.
202
+ </div>
203
+ </div>
204
+ <button
205
+ class="btn"
206
+ ?disabled=${state.disabled || !state.dirty || !targetReady}
207
+ @click=${state.onSave}
208
+ >
209
+ ${state.saving ? "Saving…" : "Save"}
210
+ </button>
211
+ </div>
212
+
213
+ ${renderExecApprovalsTarget(state)}
214
+
215
+ ${
216
+ !ready
217
+ ? html`<div class="row" style="margin-top: 12px; gap: 12px;">
218
+ <div class="muted">Load exec approvals to edit allowlists.</div>
219
+ <button class="btn" ?disabled=${state.loading || !targetReady} @click=${state.onLoad}>
220
+ ${state.loading ? "Loading…" : "Load approvals"}
221
+ </button>
222
+ </div>`
223
+ : html`
224
+ ${renderExecApprovalsTabs(state)}
225
+ ${renderExecApprovalsPolicy(state)}
226
+ ${
227
+ state.selectedScope === EXEC_APPROVALS_DEFAULT_SCOPE
228
+ ? nothing
229
+ : renderExecApprovalsAllowlist(state)
230
+ }
231
+ `
232
+ }
233
+ </section>
234
+ `;
235
+ }
236
+
237
+ function renderExecApprovalsTarget(state: ExecApprovalsState) {
238
+ const hasNodes = state.targetNodes.length > 0;
239
+ const nodeValue = state.targetNodeId ?? "";
240
+ return html`
241
+ <div class="list" style="margin-top: 12px;">
242
+ <div class="list-item">
243
+ <div class="list-main">
244
+ <div class="list-title">Target</div>
245
+ <div class="list-sub">
246
+ Gateway edits local approvals; node edits the selected node.
247
+ </div>
248
+ </div>
249
+ <div class="list-meta">
250
+ <label class="field">
251
+ <span>Host</span>
252
+ <select
253
+ ?disabled=${state.disabled}
254
+ @change=${(event: Event) => {
255
+ const target = event.target as HTMLSelectElement;
256
+ const value = target.value;
257
+ if (value === "node") {
258
+ const first = state.targetNodes[0]?.id ?? null;
259
+ state.onSelectTarget("node", nodeValue || first);
260
+ } else {
261
+ state.onSelectTarget("gateway", null);
262
+ }
263
+ }}
264
+ >
265
+ <option value="gateway" ?selected=${state.target === "gateway"}>Gateway</option>
266
+ <option value="node" ?selected=${state.target === "node"}>Node</option>
267
+ </select>
268
+ </label>
269
+ ${
270
+ state.target === "node"
271
+ ? html`
272
+ <label class="field">
273
+ <span>Node</span>
274
+ <select
275
+ ?disabled=${state.disabled || !hasNodes}
276
+ @change=${(event: Event) => {
277
+ const target = event.target as HTMLSelectElement;
278
+ const value = target.value.trim();
279
+ state.onSelectTarget("node", value ? value : null);
280
+ }}
281
+ >
282
+ <option value="" ?selected=${nodeValue === ""}>Select node</option>
283
+ ${state.targetNodes.map(
284
+ (node) =>
285
+ html`<option
286
+ value=${node.id}
287
+ ?selected=${nodeValue === node.id}
288
+ >
289
+ ${node.label}
290
+ </option>`,
291
+ )}
292
+ </select>
293
+ </label>
294
+ `
295
+ : nothing
296
+ }
297
+ </div>
298
+ </div>
299
+ ${
300
+ state.target === "node" && !hasNodes
301
+ ? html`
302
+ <div class="muted">No nodes advertise exec approvals yet.</div>
303
+ `
304
+ : nothing
305
+ }
306
+ </div>
307
+ `;
308
+ }
309
+
310
+ function renderExecApprovalsTabs(state: ExecApprovalsState) {
311
+ return html`
312
+ <div class="row" style="margin-top: 12px; gap: 8px; flex-wrap: wrap;">
313
+ <span class="label">Scope</span>
314
+ <div class="row" style="gap: 8px; flex-wrap: wrap;">
315
+ <button
316
+ class="btn btn--sm ${state.selectedScope === EXEC_APPROVALS_DEFAULT_SCOPE ? "active" : ""}"
317
+ @click=${() => state.onSelectScope(EXEC_APPROVALS_DEFAULT_SCOPE)}
318
+ >
319
+ Defaults
320
+ </button>
321
+ ${state.agents.map((agent) => {
322
+ const label = agent.name?.trim() ? `${agent.name} (${agent.id})` : agent.id;
323
+ return html`
324
+ <button
325
+ class="btn btn--sm ${state.selectedScope === agent.id ? "active" : ""}"
326
+ @click=${() => state.onSelectScope(agent.id)}
327
+ >
328
+ ${label}
329
+ </button>
330
+ `;
331
+ })}
332
+ </div>
333
+ </div>
334
+ `;
335
+ }
336
+
337
+ function renderExecApprovalsPolicy(state: ExecApprovalsState) {
338
+ const isDefaults = state.selectedScope === EXEC_APPROVALS_DEFAULT_SCOPE;
339
+ const defaults = state.defaults;
340
+ const agent = state.selectedAgent ?? {};
341
+ const basePath = isDefaults ? ["defaults"] : ["agents", state.selectedScope];
342
+ const agentSecurity = typeof agent.security === "string" ? agent.security : undefined;
343
+ const agentAsk = typeof agent.ask === "string" ? agent.ask : undefined;
344
+ const agentAskFallback = typeof agent.askFallback === "string" ? agent.askFallback : undefined;
345
+ const securityValue = isDefaults ? defaults.security : (agentSecurity ?? "__default__");
346
+ const askValue = isDefaults ? defaults.ask : (agentAsk ?? "__default__");
347
+ const askFallbackValue = isDefaults ? defaults.askFallback : (agentAskFallback ?? "__default__");
348
+ const autoOverride =
349
+ typeof agent.autoAllowSkills === "boolean" ? agent.autoAllowSkills : undefined;
350
+ const autoEffective = autoOverride ?? defaults.autoAllowSkills;
351
+ const autoIsDefault = autoOverride == null;
352
+
353
+ return html`
354
+ <div class="list" style="margin-top: 16px;">
355
+ <div class="list-item">
356
+ <div class="list-main">
357
+ <div class="list-title">Security</div>
358
+ <div class="list-sub">
359
+ ${isDefaults ? "Default security mode." : `Default: ${defaults.security}.`}
360
+ </div>
361
+ </div>
362
+ <div class="list-meta">
363
+ <label class="field">
364
+ <span>Mode</span>
365
+ <select
366
+ ?disabled=${state.disabled}
367
+ @change=${(event: Event) => {
368
+ const target = event.target as HTMLSelectElement;
369
+ const value = target.value;
370
+ if (!isDefaults && value === "__default__") {
371
+ state.onRemove([...basePath, "security"]);
372
+ } else {
373
+ state.onPatch([...basePath, "security"], value);
374
+ }
375
+ }}
376
+ >
377
+ ${
378
+ !isDefaults
379
+ ? html`<option value="__default__" ?selected=${securityValue === "__default__"}>
380
+ Use default (${defaults.security})
381
+ </option>`
382
+ : nothing
383
+ }
384
+ ${SECURITY_OPTIONS.map(
385
+ (option) =>
386
+ html`<option
387
+ value=${option.value}
388
+ ?selected=${securityValue === option.value}
389
+ >
390
+ ${option.label}
391
+ </option>`,
392
+ )}
393
+ </select>
394
+ </label>
395
+ </div>
396
+ </div>
397
+
398
+ <div class="list-item">
399
+ <div class="list-main">
400
+ <div class="list-title">Ask</div>
401
+ <div class="list-sub">
402
+ ${isDefaults ? "Default prompt policy." : `Default: ${defaults.ask}.`}
403
+ </div>
404
+ </div>
405
+ <div class="list-meta">
406
+ <label class="field">
407
+ <span>Mode</span>
408
+ <select
409
+ ?disabled=${state.disabled}
410
+ @change=${(event: Event) => {
411
+ const target = event.target as HTMLSelectElement;
412
+ const value = target.value;
413
+ if (!isDefaults && value === "__default__") {
414
+ state.onRemove([...basePath, "ask"]);
415
+ } else {
416
+ state.onPatch([...basePath, "ask"], value);
417
+ }
418
+ }}
419
+ >
420
+ ${
421
+ !isDefaults
422
+ ? html`<option value="__default__" ?selected=${askValue === "__default__"}>
423
+ Use default (${defaults.ask})
424
+ </option>`
425
+ : nothing
426
+ }
427
+ ${ASK_OPTIONS.map(
428
+ (option) =>
429
+ html`<option
430
+ value=${option.value}
431
+ ?selected=${askValue === option.value}
432
+ >
433
+ ${option.label}
434
+ </option>`,
435
+ )}
436
+ </select>
437
+ </label>
438
+ </div>
439
+ </div>
440
+
441
+ <div class="list-item">
442
+ <div class="list-main">
443
+ <div class="list-title">Ask fallback</div>
444
+ <div class="list-sub">
445
+ ${
446
+ isDefaults
447
+ ? "Applied when the UI prompt is unavailable."
448
+ : `Default: ${defaults.askFallback}.`
449
+ }
450
+ </div>
451
+ </div>
452
+ <div class="list-meta">
453
+ <label class="field">
454
+ <span>Fallback</span>
455
+ <select
456
+ ?disabled=${state.disabled}
457
+ @change=${(event: Event) => {
458
+ const target = event.target as HTMLSelectElement;
459
+ const value = target.value;
460
+ if (!isDefaults && value === "__default__") {
461
+ state.onRemove([...basePath, "askFallback"]);
462
+ } else {
463
+ state.onPatch([...basePath, "askFallback"], value);
464
+ }
465
+ }}
466
+ >
467
+ ${
468
+ !isDefaults
469
+ ? html`<option value="__default__" ?selected=${askFallbackValue === "__default__"}>
470
+ Use default (${defaults.askFallback})
471
+ </option>`
472
+ : nothing
473
+ }
474
+ ${SECURITY_OPTIONS.map(
475
+ (option) =>
476
+ html`<option
477
+ value=${option.value}
478
+ ?selected=${askFallbackValue === option.value}
479
+ >
480
+ ${option.label}
481
+ </option>`,
482
+ )}
483
+ </select>
484
+ </label>
485
+ </div>
486
+ </div>
487
+
488
+ <div class="list-item">
489
+ <div class="list-main">
490
+ <div class="list-title">Auto-allow skill CLIs</div>
491
+ <div class="list-sub">
492
+ ${
493
+ isDefaults
494
+ ? "Allow skill executables listed by the Gateway."
495
+ : autoIsDefault
496
+ ? `Using default (${defaults.autoAllowSkills ? "on" : "off"}).`
497
+ : `Override (${autoEffective ? "on" : "off"}).`
498
+ }
499
+ </div>
500
+ </div>
501
+ <div class="list-meta">
502
+ <label class="field">
503
+ <span>Enabled</span>
504
+ <input
505
+ type="checkbox"
506
+ ?disabled=${state.disabled}
507
+ .checked=${autoEffective}
508
+ @change=${(event: Event) => {
509
+ const target = event.target as HTMLInputElement;
510
+ state.onPatch([...basePath, "autoAllowSkills"], target.checked);
511
+ }}
512
+ />
513
+ </label>
514
+ ${
515
+ !isDefaults && !autoIsDefault
516
+ ? html`<button
517
+ class="btn btn--sm"
518
+ ?disabled=${state.disabled}
519
+ @click=${() => state.onRemove([...basePath, "autoAllowSkills"])}
520
+ >
521
+ Use default
522
+ </button>`
523
+ : nothing
524
+ }
525
+ </div>
526
+ </div>
527
+ </div>
528
+ `;
529
+ }
530
+
531
+ function renderExecApprovalsAllowlist(state: ExecApprovalsState) {
532
+ const allowlistPath = ["agents", state.selectedScope, "allowlist"];
533
+ const entries = state.allowlist;
534
+ return html`
535
+ <div class="row" style="margin-top: 18px; justify-content: space-between;">
536
+ <div>
537
+ <div class="card-title">Allowlist</div>
538
+ <div class="card-sub">Case-insensitive glob patterns.</div>
539
+ </div>
540
+ <button
541
+ class="btn btn--sm"
542
+ ?disabled=${state.disabled}
543
+ @click=${() => {
544
+ const next = [...entries, { pattern: "" }];
545
+ state.onPatch(allowlistPath, next);
546
+ }}
547
+ >
548
+ Add pattern
549
+ </button>
550
+ </div>
551
+ <div class="list" style="margin-top: 12px;">
552
+ ${
553
+ entries.length === 0
554
+ ? html`
555
+ <div class="muted">No allowlist entries yet.</div>
556
+ `
557
+ : entries.map((entry, index) => renderAllowlistEntry(state, entry, index))
558
+ }
559
+ </div>
560
+ `;
561
+ }
562
+
563
+ function renderAllowlistEntry(
564
+ state: ExecApprovalsState,
565
+ entry: ExecApprovalsAllowlistEntry,
566
+ index: number,
567
+ ) {
568
+ const lastUsed = entry.lastUsedAt ? formatRelativeTimestamp(entry.lastUsedAt) : "never";
569
+ const lastCommand = entry.lastUsedCommand ? clampText(entry.lastUsedCommand, 120) : null;
570
+ const lastPath = entry.lastResolvedPath ? clampText(entry.lastResolvedPath, 120) : null;
571
+ return html`
572
+ <div class="list-item">
573
+ <div class="list-main">
574
+ <div class="list-title">${entry.pattern?.trim() ? entry.pattern : "New pattern"}</div>
575
+ <div class="list-sub">Last used: ${lastUsed}</div>
576
+ ${lastCommand ? html`<div class="list-sub mono">${lastCommand}</div>` : nothing}
577
+ ${lastPath ? html`<div class="list-sub mono">${lastPath}</div>` : nothing}
578
+ </div>
579
+ <div class="list-meta">
580
+ <label class="field">
581
+ <span>Pattern</span>
582
+ <input
583
+ type="text"
584
+ .value=${entry.pattern ?? ""}
585
+ ?disabled=${state.disabled}
586
+ @input=${(event: Event) => {
587
+ const target = event.target as HTMLInputElement;
588
+ state.onPatch(
589
+ ["agents", state.selectedScope, "allowlist", index, "pattern"],
590
+ target.value,
591
+ );
592
+ }}
593
+ />
594
+ </label>
595
+ <button
596
+ class="btn btn--sm danger"
597
+ ?disabled=${state.disabled}
598
+ @click=${() => {
599
+ if (state.allowlist.length <= 1) {
600
+ state.onRemove(["agents", state.selectedScope, "allowlist"]);
601
+ return;
602
+ }
603
+ state.onRemove(["agents", state.selectedScope, "allowlist", index]);
604
+ }}
605
+ >
606
+ Remove
607
+ </button>
608
+ </div>
609
+ </div>
610
+ `;
611
+ }
612
+
613
+ function resolveExecApprovalsNodes(
614
+ nodes: Array<Record<string, unknown>>,
615
+ ): ExecApprovalsTargetNode[] {
616
+ return resolveNodeTargets(nodes, ["system.execApprovals.get", "system.execApprovals.set"]);
617
+ }
@@ -0,0 +1,67 @@
1
+ export type NodeTargetOption = {
2
+ id: string;
3
+ label: string;
4
+ };
5
+
6
+ export type ConfigAgentOption = {
7
+ id: string;
8
+ name?: string;
9
+ isDefault: boolean;
10
+ index: number;
11
+ record: Record<string, unknown>;
12
+ };
13
+
14
+ export function resolveConfigAgents(config: Record<string, unknown> | null): ConfigAgentOption[] {
15
+ const agentsNode = (config?.agents ?? {}) as Record<string, unknown>;
16
+ const list = Array.isArray(agentsNode.list) ? agentsNode.list : [];
17
+ const agents: ConfigAgentOption[] = [];
18
+
19
+ list.forEach((entry, index) => {
20
+ if (!entry || typeof entry !== "object") {
21
+ return;
22
+ }
23
+ const record = entry as Record<string, unknown>;
24
+ const id = typeof record.id === "string" ? record.id.trim() : "";
25
+ if (!id) {
26
+ return;
27
+ }
28
+ const name = typeof record.name === "string" ? record.name.trim() : undefined;
29
+ const isDefault = record.default === true;
30
+ agents.push({ id, name: name || undefined, isDefault, index, record });
31
+ });
32
+
33
+ return agents;
34
+ }
35
+
36
+ export function resolveNodeTargets(
37
+ nodes: Array<Record<string, unknown>>,
38
+ requiredCommands: string[],
39
+ ): NodeTargetOption[] {
40
+ const required = new Set(requiredCommands);
41
+ const list: NodeTargetOption[] = [];
42
+
43
+ for (const node of nodes) {
44
+ const commands = Array.isArray(node.commands) ? node.commands : [];
45
+ const supports = commands.some((cmd) => required.has(String(cmd)));
46
+ if (!supports) {
47
+ continue;
48
+ }
49
+
50
+ const nodeId = typeof node.nodeId === "string" ? node.nodeId.trim() : "";
51
+ if (!nodeId) {
52
+ continue;
53
+ }
54
+
55
+ const displayName =
56
+ typeof node.displayName === "string" && node.displayName.trim()
57
+ ? node.displayName.trim()
58
+ : nodeId;
59
+ list.push({
60
+ id: nodeId,
61
+ label: displayName === nodeId ? nodeId : `${displayName} · ${nodeId}`,
62
+ });
63
+ }
64
+
65
+ list.sort((a, b) => a.label.localeCompare(b.label));
66
+ return list;
67
+ }