@agenticmail/enterprise 0.5.319 → 0.5.321

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 (330) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/CODE_OF_CONDUCT.md +31 -0
  3. package/README.md +118 -38
  4. package/SECURITY.md +42 -0
  5. package/dist/agent-heartbeat-3FWNHZFX.js +510 -0
  6. package/dist/agent-heartbeat-4RWHZR7H.js +510 -0
  7. package/dist/agent-heartbeat-6ZGB5ILY.js +510 -0
  8. package/dist/agent-heartbeat-BIVHLKFM.js +510 -0
  9. package/dist/agent-heartbeat-HRKVFK2T.js +510 -0
  10. package/dist/agent-heartbeat-JC5GWVXD.js +510 -0
  11. package/dist/agent-heartbeat-K6A4HMHB.js +510 -0
  12. package/dist/agent-heartbeat-LCDXWFVB.js +510 -0
  13. package/dist/agent-heartbeat-P7HZCZAQ.js +510 -0
  14. package/dist/agent-heartbeat-PUIRSNIO.js +510 -0
  15. package/dist/agent-heartbeat-SN5ILQ6Y.js +510 -0
  16. package/dist/agent-heartbeat-TW5YTDYC.js +510 -0
  17. package/dist/agent-heartbeat-Z2QQXROL.js +510 -0
  18. package/dist/agent-notify-OEQBCZLN.js +43 -0
  19. package/dist/{agent-tools-263HM5QU.js → agent-tools-3W7XLUYA.js} +1 -1
  20. package/dist/agent-tools-4QK7LLNP.js +9 -0
  21. package/dist/agent-tools-54VZGT6L.js +9 -0
  22. package/dist/{agent-tools-AT4D276V.js → agent-tools-AYYDPO27.js} +7 -7
  23. package/dist/{agent-tools-MSTAPX2I.js → agent-tools-F2X47FKF.js} +7 -7
  24. package/dist/{agent-tools-FA26SY5O.js → agent-tools-O6W3QAZL.js} +11 -6
  25. package/dist/agent-tools-OAWVZBMW.js +9 -0
  26. package/dist/agent-tools-QCCU74PN.js +13949 -0
  27. package/dist/chunk-2LHUARN6.js +4929 -0
  28. package/dist/chunk-2WVCNCYC.js +5087 -0
  29. package/dist/{chunk-6PWDS7KY.js → chunk-3FM6YQUK.js} +20 -20
  30. package/dist/chunk-3UAFHUEC.js +212 -0
  31. package/dist/{chunk-WJO57PMO.js → chunk-46GOWZT4.js} +20 -20
  32. package/dist/{chunk-BNRE7TSX.js → chunk-5KYJAUZV.js} +3 -3
  33. package/dist/chunk-6C5PKREN.js +467 -0
  34. package/dist/{chunk-447MTPZF.js → chunk-6ZMLNEHB.js} +3 -3
  35. package/dist/chunk-BPZQT5N5.js +25652 -0
  36. package/dist/chunk-BQM7MBPS.js +1380 -0
  37. package/dist/{chunk-ZRFKGPIU.js → chunk-C52OQNNY.js} +20 -20
  38. package/dist/chunk-C7HGQF4Y.js +25652 -0
  39. package/dist/chunk-CAHNZGGK.js +25656 -0
  40. package/dist/{chunk-FL3CH3ET.js → chunk-CK7R6UHE.js} +51 -27
  41. package/dist/chunk-D36RPWB7.js +25652 -0
  42. package/dist/{chunk-36NM2B4C.js → chunk-DJK2UPFH.js} +63 -93
  43. package/dist/chunk-DM7FTF7W.js +4929 -0
  44. package/dist/chunk-DMD24UFZ.js +5101 -0
  45. package/dist/{chunk-36XNMIHA.js → chunk-DXZGPUAF.js} +20 -20
  46. package/dist/chunk-F46WB5IL.js +5087 -0
  47. package/dist/chunk-F5QG5SQH.js +5087 -0
  48. package/dist/{chunk-JGEVQZDR.js → chunk-FLQ5FLHW.js} +13 -16
  49. package/dist/chunk-H7GP733U.js +5087 -0
  50. package/dist/{chunk-OZSQLOV6.js → chunk-HHBXWB5U.js} +415 -19
  51. package/dist/{chunk-D24JY75H.js → chunk-IMXS4N6W.js} +3 -3
  52. package/dist/{chunk-6PVBV6ZP.js → chunk-JNMDD7JY.js} +3 -3
  53. package/dist/chunk-JTV5LA47.js +1519 -0
  54. package/dist/chunk-KV6G7NZX.js +1519 -0
  55. package/dist/chunk-MU5MEBIK.js +1519 -0
  56. package/dist/chunk-NLT5MC7X.js +465 -0
  57. package/dist/{chunk-GTFZZUXX.js → chunk-NVLYIM4J.js} +51 -27
  58. package/dist/{chunk-6G5SXLXC.js → chunk-NZY2BIZH.js} +63 -93
  59. package/dist/chunk-O42L6G67.js +1519 -0
  60. package/dist/chunk-OCNERGGM.js +4891 -0
  61. package/dist/chunk-OJSNHONE.js +1519 -0
  62. package/dist/{chunk-2TAZJWJN.js → chunk-OWL3QVH7.js} +18 -0
  63. package/dist/{chunk-P3HVY2HS.js → chunk-OWTLNV4Q.js} +382 -7
  64. package/dist/chunk-PCNYEP6T.js +4891 -0
  65. package/dist/{chunk-YL3Z5KPR.js → chunk-PI4AQ4Z6.js} +438 -15
  66. package/dist/chunk-PN3EGTCA.js +194 -0
  67. package/dist/chunk-Q37UKNRC.js +1519 -0
  68. package/dist/chunk-QXTC6J7H.js +5087 -0
  69. package/dist/{chunk-SPBQVNDI.js → chunk-RKERL5LZ.js} +25 -21
  70. package/dist/chunk-RVBK2IOX.js +25652 -0
  71. package/dist/chunk-SAKODCZ5.js +4891 -0
  72. package/dist/{chunk-XV4TU65E.js → chunk-SALGFC5L.js} +51 -27
  73. package/dist/chunk-STGWZ2MS.js +1519 -0
  74. package/dist/chunk-UY3ZVQDP.js +25652 -0
  75. package/dist/chunk-V6OSD62M.js +5087 -0
  76. package/dist/chunk-VP6YAHX4.js +1519 -0
  77. package/dist/chunk-WDYJOEAI.js +5087 -0
  78. package/dist/chunk-WEAFQNOS.js +195 -0
  79. package/dist/chunk-XKUSAZGP.js +5087 -0
  80. package/dist/chunk-Z6K5FKAB.js +548 -0
  81. package/dist/chunk-ZGE3XAXY.js +1519 -0
  82. package/dist/chunk-ZGYVXYQQ.js +3296 -0
  83. package/dist/cli-agent-7TB2BWS6.js +2370 -0
  84. package/dist/cli-agent-AKXFFST2.js +2370 -0
  85. package/dist/cli-agent-DZTKLITB.js +2357 -0
  86. package/dist/cli-agent-FOF7PFEP.js +2357 -0
  87. package/dist/cli-agent-H74M2ZYN.js +2357 -0
  88. package/dist/cli-agent-HORWVPHB.js +2370 -0
  89. package/dist/cli-agent-HSZT6SKF.js +2423 -0
  90. package/dist/cli-agent-JLUQ4ZU6.js +2424 -0
  91. package/dist/cli-agent-MVCDH4HV.js +2370 -0
  92. package/dist/cli-agent-NZXOEPJ2.js +2357 -0
  93. package/dist/cli-agent-PADN3QRC.js +2357 -0
  94. package/dist/cli-agent-QAYEX3BE.js +2441 -0
  95. package/dist/cli-agent-QT64DT5J.js +2370 -0
  96. package/dist/cli-agent-TFL2M6UK.js +2424 -0
  97. package/dist/cli-agent-UIKXATTD.js +2357 -0
  98. package/dist/cli-agent-UJN6FYTO.js +2370 -0
  99. package/dist/cli-agent-VIQAYVY4.js +2357 -0
  100. package/dist/cli-agent-WNWFVOFM.js +2370 -0
  101. package/dist/cli-agent-XBQX67VJ.js +2423 -0
  102. package/dist/cli-agent-ZLSC6FF4.js +2357 -0
  103. package/dist/cli-serve-2IL5DTEY.js +153 -0
  104. package/dist/cli-serve-47N5UKKW.js +153 -0
  105. package/dist/cli-serve-4XGZFUV2.js +140 -0
  106. package/dist/cli-serve-6OT3UEAN.js +140 -0
  107. package/dist/cli-serve-7L6EY5UH.js +153 -0
  108. package/dist/cli-serve-BDGOOOKQ.js +260 -0
  109. package/dist/cli-serve-BFNIW2LF.js +153 -0
  110. package/dist/cli-serve-C7MN6U5Q.js +153 -0
  111. package/dist/cli-serve-CR3OY3IM.js +153 -0
  112. package/dist/cli-serve-DAJFRWQ7.js +153 -0
  113. package/dist/cli-serve-FW6FHFW4.js +153 -0
  114. package/dist/cli-serve-GEEOQS77.js +153 -0
  115. package/dist/cli-serve-H562I3ZK.js +153 -0
  116. package/dist/cli-serve-HDQZF4C4.js +153 -0
  117. package/dist/cli-serve-LICAOMEB.js +140 -0
  118. package/dist/cli-serve-LLGYLWFS.js +153 -0
  119. package/dist/cli-serve-N3OISDNB.js +153 -0
  120. package/dist/cli-serve-TIZ27EVR.js +153 -0
  121. package/dist/cli-serve-TUNI2RCN.js +153 -0
  122. package/dist/cli-serve-WNOZMAWD.js +153 -0
  123. package/dist/cli-validate-Z726VJCN.js +150 -0
  124. package/dist/cli.js +4 -4
  125. package/dist/connection-manager-KAWEUWUR.js +9 -0
  126. package/dist/dashboard/app.js +9 -3
  127. package/dist/dashboard/components/knowledge-link.js +15 -0
  128. package/dist/dashboard/components/settings-help.js +4 -2
  129. package/dist/dashboard/docs/agent-deployment.html +33 -1
  130. package/dist/dashboard/docs/settings-network.html +321 -0
  131. package/dist/dashboard/docs/settings-security.html +347 -0
  132. package/dist/dashboard/docs/settings-tool-security.html +176 -0
  133. package/dist/dashboard/docs/settings.html +36 -16
  134. package/dist/dashboard/pages/agent-detail/deployment.js +39 -6
  135. package/dist/dashboard/pages/agent-detail/tools.js +10 -0
  136. package/dist/dashboard/pages/database-access.js +4 -3
  137. package/dist/dashboard/pages/settings.js +174 -37
  138. package/dist/dashboard/pages/task-pipeline.js +400 -843
  139. package/dist/db-adapter-2T56ORSD.js +7 -0
  140. package/dist/db-adapter-IRHOUMVC.js +7 -0
  141. package/dist/index.js +41 -41
  142. package/dist/microsoft-VREAZ7M2.js +3955 -0
  143. package/dist/routes-3MMLQTB6.js +90 -0
  144. package/dist/routes-4ZUIJ4HE.js +90 -0
  145. package/dist/routes-5MXHKKH4.js +90 -0
  146. package/dist/routes-64NJFK3B.js +90 -0
  147. package/dist/routes-6AKQ2LBV.js +90 -0
  148. package/dist/routes-CRRBUDO4.js +90 -0
  149. package/dist/routes-DIAF3MC3.js +90 -0
  150. package/dist/routes-KMUNU6CY.js +90 -0
  151. package/dist/routes-LRRLXIZR.js +90 -0
  152. package/dist/routes-N647AJYG.js +90 -0
  153. package/dist/routes-SSSELAAR.js +90 -0
  154. package/dist/routes-STERVGKJ.js +90 -0
  155. package/dist/routes-ZEZZACZP.js +90 -0
  156. package/dist/runtime-5EQN4GFM.js +45 -0
  157. package/dist/runtime-5LP7PUD4.js +45 -0
  158. package/dist/runtime-6BULDBR3.js +45 -0
  159. package/dist/runtime-6YEENDN3.js +45 -0
  160. package/dist/runtime-7LQFRG3B.js +45 -0
  161. package/dist/runtime-AMXJU2MB.js +45 -0
  162. package/dist/runtime-D6WSE7FG.js +45 -0
  163. package/dist/runtime-EYVN7NFJ.js +45 -0
  164. package/dist/runtime-F6RPWQVW.js +45 -0
  165. package/dist/runtime-FYMJURFC.js +45 -0
  166. package/dist/runtime-JRNBL4O4.js +45 -0
  167. package/dist/runtime-OM2NIBMI.js +45 -0
  168. package/dist/runtime-QWPVD7CY.js +45 -0
  169. package/dist/runtime-YLIIPTE4.js +45 -0
  170. package/dist/runtime-YU6P22CG.js +45 -0
  171. package/dist/screen-unlock-4RPZBHOI.js +118 -0
  172. package/dist/server-AMCSXINC.js +28 -0
  173. package/dist/server-CU6LVQS4.js +28 -0
  174. package/dist/server-DFYGH2CV.js +28 -0
  175. package/dist/server-EELWOC3X.js +28 -0
  176. package/dist/server-EN5E2OWQ.js +28 -0
  177. package/dist/server-GW2HYJYI.js +28 -0
  178. package/dist/server-J25NCRWJ.js +28 -0
  179. package/dist/server-JDGNOTFV.js +28 -0
  180. package/dist/server-NE5HD5DJ.js +28 -0
  181. package/dist/server-NQOT7W77.js +28 -0
  182. package/dist/server-PWE5PQTR.js +28 -0
  183. package/dist/server-Q2Q32H2B.js +28 -0
  184. package/dist/server-Q77ME7TL.js +28 -0
  185. package/dist/server-WLLH4WST.js +28 -0
  186. package/dist/server-WTUJ2O3F.js +28 -0
  187. package/dist/server-X4CJTHHF.js +28 -0
  188. package/dist/server-XK3ILCJC.js +28 -0
  189. package/dist/server-ZRD3NDJE.js +28 -0
  190. package/dist/setup-44VBAO4J.js +20 -0
  191. package/dist/setup-4ONNQBWB.js +20 -0
  192. package/dist/setup-4OSBXSCL.js +20 -0
  193. package/dist/setup-4QFGRBLZ.js +20 -0
  194. package/dist/setup-6766SGAR.js +20 -0
  195. package/dist/setup-AYY24DKM.js +20 -0
  196. package/dist/setup-B34N4HPU.js +20 -0
  197. package/dist/setup-E2YLC2EY.js +20 -0
  198. package/dist/setup-ER6NXTY5.js +20 -0
  199. package/dist/setup-H2AGCBW5.js +20 -0
  200. package/dist/setup-ICOZRKCX.js +20 -0
  201. package/dist/setup-JFTJH7UF.js +20 -0
  202. package/dist/setup-PRFNI6YW.js +20 -0
  203. package/dist/setup-RAHBMYHE.js +20 -0
  204. package/dist/setup-TXPR5UQX.js +20 -0
  205. package/dist/setup-XCJMELVU.js +20 -0
  206. package/dist/setup-XIYEIFVK.js +20 -0
  207. package/dist/setup-Z4PZSHBI.js +20 -0
  208. package/dist/skills-FR7I5V7H.js +16 -0
  209. package/dist/skills-HCVBA6PK.js +16 -0
  210. package/dist/system-prompts-TM7OA32C.js +913 -0
  211. package/dist/task-queue-O7IVZYUO.js +9 -0
  212. package/dist/transport-encryption-2T7PIXKG.js +25 -0
  213. package/logs/cloudflared-error.log +61 -0
  214. package/logs/cloudflared-out.log +0 -0
  215. package/logs/enterprise-error.log +0 -0
  216. package/logs/enterprise-out.log +3 -0
  217. package/logs/fola-error.log +0 -0
  218. package/logs/fola-out.log +0 -0
  219. package/logs/john-error.log +8 -0
  220. package/logs/john-out.log +0 -0
  221. package/package.json +31 -3
  222. package/src/agent-tools/tool-resolver.ts +50 -61
  223. package/src/agent-tools/tools/enterprise-database.ts +5 -5
  224. package/src/agent-tools/tools/local/dependency-manager.ts +2 -2
  225. package/src/agent-tools/tools/microsoft/graph-api.ts +137 -26
  226. package/src/agent-tools/tools/microsoft/outlook-mail.ts +392 -100
  227. package/src/agent-tools/tools/microsoft/teams.ts +267 -48
  228. package/src/auth/routes.ts +4 -4
  229. package/src/cli-agent.ts +108 -8
  230. package/src/cli-serve.ts +140 -0
  231. package/src/dashboard/app.js +9 -3
  232. package/src/dashboard/components/knowledge-link.js +15 -0
  233. package/src/dashboard/components/settings-help.js +4 -2
  234. package/src/dashboard/docs/agent-deployment.html +33 -1
  235. package/src/dashboard/docs/settings-network.html +321 -0
  236. package/src/dashboard/docs/settings-security.html +347 -0
  237. package/src/dashboard/docs/settings-tool-security.html +176 -0
  238. package/src/dashboard/docs/settings.html +36 -16
  239. package/src/dashboard/pages/agent-detail/deployment.js +39 -6
  240. package/src/dashboard/pages/agent-detail/tools.js +10 -0
  241. package/src/dashboard/pages/database-access.js +4 -3
  242. package/src/dashboard/pages/settings.js +174 -37
  243. package/src/dashboard/pages/task-pipeline.js +400 -843
  244. package/src/database-access/agent-tools.ts +78 -63
  245. package/src/database-access/connection-manager.ts +13 -2
  246. package/src/database-access/routes.ts +13 -1
  247. package/src/db/adapter.ts +1 -0
  248. package/src/engine/agent-memory.ts +2 -1
  249. package/src/engine/agent-notify.ts +50 -0
  250. package/src/engine/agent-routes.ts +257 -4
  251. package/src/engine/db-adapter.ts +16 -0
  252. package/src/engine/lifecycle.ts +4 -0
  253. package/src/engine/routes.ts +4 -3
  254. package/src/engine/screen-unlock.ts +136 -0
  255. package/src/engine/skills/database-access.ts +78 -0
  256. package/src/engine/skills/index.ts +3 -2
  257. package/src/engine/skills.ts +2 -0
  258. package/src/engine/task-queue-routes.ts +18 -0
  259. package/src/engine/task-queue.ts +15 -2
  260. package/src/middleware/transport-encryption.ts +1 -4
  261. package/src/runtime/agent-loop.ts +4 -0
  262. package/src/runtime/index.ts +15 -6
  263. package/src/server.ts +14 -1
  264. package/src/system-prompts/google/index.ts +1 -2
  265. package/src/system-prompts/index.ts +1 -1
  266. package/src/system-prompts/microsoft/contacts.ts +34 -0
  267. package/src/system-prompts/microsoft/excel.ts +52 -0
  268. package/src/system-prompts/microsoft/index.ts +31 -0
  269. package/src/system-prompts/microsoft/onedrive.ts +41 -0
  270. package/src/system-prompts/microsoft/onenote.ts +36 -0
  271. package/src/system-prompts/microsoft/outlook-calendar.ts +37 -0
  272. package/src/system-prompts/microsoft/outlook-mail.ts +46 -0
  273. package/src/system-prompts/microsoft/planner.ts +37 -0
  274. package/src/system-prompts/microsoft/powerbi.ts +38 -0
  275. package/src/system-prompts/microsoft/powerpoint.ts +35 -0
  276. package/src/system-prompts/microsoft/sharepoint.ts +44 -0
  277. package/src/system-prompts/microsoft/teams.ts +49 -0
  278. package/src/system-prompts/microsoft/todo.ts +37 -0
  279. package/src/types/hono-env.ts +4 -0
  280. package/.github/CODEOWNERS +0 -23
  281. package/.github/workflows/publish-community-skills.yml +0 -121
  282. package/.github/workflows/validate-community-skills.yml +0 -172
  283. package/agriculture_southwest_nigeria_research.txt +0 -10
  284. package/boa_credit_cards_research.txt +0 -10
  285. package/customer_support_research_feb2026.txt +0 -10
  286. package/dist/agent-tools-LRA7PPXG.js +0 -13922
  287. package/dist/agent-tools-VAU5DOQB.js +0 -13910
  288. package/dist/agent-tools-VWV7OWXU.js +0 -13922
  289. package/dist/chunk-2Z7MWTCX.js +0 -4977
  290. package/dist/chunk-3T4XU3VV.js +0 -5010
  291. package/dist/chunk-445QM4NX.js +0 -5061
  292. package/dist/chunk-5TW3Y7DJ.js +0 -1519
  293. package/dist/chunk-6I7VY3LT.js +0 -5060
  294. package/dist/chunk-6W5EK3UP.js +0 -4977
  295. package/dist/chunk-AQMSHJQT.js +0 -5069
  296. package/dist/chunk-ASSQW7HX.js +0 -5051
  297. package/dist/chunk-CIN27FGC.js +0 -5037
  298. package/dist/chunk-CMXY3NUB.js +0 -4977
  299. package/dist/chunk-DRLMRUDP.js +0 -5052
  300. package/dist/chunk-EHI7Z446.js +0 -1519
  301. package/dist/chunk-FEAILFAQ.js +0 -1519
  302. package/dist/chunk-GA3PYBZL.js +0 -1519
  303. package/dist/chunk-GWX63G5J.js +0 -1519
  304. package/dist/chunk-HHMZ4UY6.js +0 -1519
  305. package/dist/chunk-HVQMNF7E.js +0 -4921
  306. package/dist/chunk-HXM7F3YN.js +0 -1519
  307. package/dist/chunk-K6NGOUXG.js +0 -5060
  308. package/dist/chunk-KPG5WINJ.js +0 -4977
  309. package/dist/chunk-LBCUBYDL.js +0 -1519
  310. package/dist/chunk-LIRQSWLR.js +0 -5014
  311. package/dist/chunk-LRCKO5KE.js +0 -1519
  312. package/dist/chunk-M7XL3DJD.js +0 -5069
  313. package/dist/chunk-MHJULEIQ.js +0 -1519
  314. package/dist/chunk-MJGGW6MC.js +0 -106
  315. package/dist/chunk-MMYBDHDB.js +0 -4921
  316. package/dist/chunk-MQT5FXKD.js +0 -1519
  317. package/dist/chunk-OIMPEQF5.js +0 -4977
  318. package/dist/chunk-OOU7JUYE.js +0 -542
  319. package/dist/chunk-OW4GLBHP.js +0 -1519
  320. package/dist/chunk-Q4K4MMLU.js +0 -4977
  321. package/dist/chunk-RUK4CRPF.js +0 -1519
  322. package/dist/chunk-T7H65XQY.js +0 -1519
  323. package/dist/chunk-TQVFWG57.js +0 -5064
  324. package/dist/chunk-UEPK3IMC.js +0 -1519
  325. package/dist/chunk-VUWTXJH6.js +0 -1519
  326. package/dist/chunk-WCPGGSAD.js +0 -1519
  327. package/dist/chunk-WO63NZOJ.js +0 -1519
  328. package/dist/chunk-YPJDRVUM.js +0 -5064
  329. package/dist/chunk-ZROMH5DL.js +0 -4921
  330. package/src/dashboard/docs/_template.txt +0 -92
@@ -32,6 +32,10 @@ export function DeploymentSection(props) {
32
32
  var installingPm2 = _installingPm2[0]; var setInstallingPm2 = _installingPm2[1];
33
33
  var _syncingKbs = useState(false);
34
34
  var syncingKbs = _syncingKbs[0]; var setSyncingKbs = _syncingKbs[1];
35
+ var _portCheck = useState(null); // { checking, available, error, process }
36
+ var portCheck = _portCheck[0]; var setPortCheck = _portCheck[1];
37
+ var _portCheckTimer = useState(null);
38
+ var portCheckTimer = _portCheckTimer[0]; var setPortCheckTimer = _portCheckTimer[1];
35
39
 
36
40
  var load = function() {
37
41
  setLoading(true);
@@ -106,7 +110,7 @@ export function DeploymentSection(props) {
106
110
  dockerTag: docker.tag || 'latest',
107
111
  dockerMemory: docker.memory || '512m',
108
112
  dockerCpu: docker.cpu || '0.5',
109
- dockerPorts: (docker.ports || [3000]).join(', '),
113
+ dockerPorts: (docker.ports || [4100]).join(', '),
110
114
  dockerNetwork: docker.network || '',
111
115
  dockerRestart: docker.restart || 'unless-stopped',
112
116
  // VPS
@@ -170,13 +174,18 @@ export function DeploymentSection(props) {
170
174
  };
171
175
 
172
176
  var saveDeploy = function() {
177
+ // Block save if local port is in use
178
+ if (deployForm.target === 'local' && deployForm.localPort && portCheck && !portCheck.checking && !portCheck.available) {
179
+ toast('Port ' + deployForm.localPort + ' is already in use. Please choose a different port.', 'error');
180
+ return;
181
+ }
173
182
  setSavingDeploy(true);
174
183
  var t = deployForm.target;
175
184
  var deployConfig = {};
176
185
  if (t === 'fly') {
177
186
  deployConfig = { cloud: { provider: 'fly', region: deployForm.region || 'iad', apiToken: deployForm.flyApiToken || undefined, appName: deployForm.flyAppName || undefined, org: deployForm.flyOrg || 'personal', vmSize: deployForm.flyVmSize || 'shared-cpu-1x', vmMemory: deployForm.flyVmMemory || '256' } };
178
187
  } else if (t === 'docker') {
179
- deployConfig = { docker: { image: deployForm.dockerImage || 'agenticmail/agent', tag: deployForm.dockerTag || 'latest', ports: (deployForm.dockerPorts || '3000').split(',').map(function(p) { return parseInt(p.trim()) || 3000; }), memory: deployForm.dockerMemory || '512m', cpu: deployForm.dockerCpu || '0.5', network: deployForm.dockerNetwork || undefined, restart: deployForm.dockerRestart || 'unless-stopped' } };
188
+ deployConfig = { docker: { image: deployForm.dockerImage || 'agenticmail/agent', tag: deployForm.dockerTag || 'latest', ports: (deployForm.dockerPorts || '4100').split(',').map(function(p) { return parseInt(p.trim()) || 4100; }), memory: deployForm.dockerMemory || '512m', cpu: deployForm.dockerCpu || '0.5', network: deployForm.dockerNetwork || undefined, restart: deployForm.dockerRestart || 'unless-stopped' } };
180
189
  } else if (t === 'vps') {
181
190
  deployConfig = { vps: { host: deployForm.vpsHost, port: parseInt(deployForm.vpsPort) || 22, user: deployForm.vpsUser || 'root', keyPath: deployForm.vpsKeyPath || '~/.ssh/id_rsa', workDir: deployForm.vpsWorkDir || '/opt/agenticmail' } };
182
191
  } else if (t === 'aws') {
@@ -209,7 +218,27 @@ export function DeploymentSection(props) {
209
218
  .catch(function(err) { toast('Failed to save: ' + err.message, 'error'); setSavingDeploy(false); });
210
219
  };
211
220
 
212
- var setDf = function(k, v) { setDeployForm(function(f) { var n = Object.assign({}, f); n[k] = v; return n; }); };
221
+ var setDf = function(k, v) {
222
+ setDeployForm(function(f) { var n = Object.assign({}, f); n[k] = v; return n; });
223
+ // Auto-check port when localPort changes
224
+ if (k === 'localPort' && v) {
225
+ var p = parseInt(v);
226
+ if (p > 0 && p <= 65535) {
227
+ setPortCheck({ checking: true });
228
+ if (portCheckTimer) clearTimeout(portCheckTimer);
229
+ var timer = setTimeout(function() {
230
+ engineCall('/system/check-port', { method: 'POST', body: JSON.stringify({ port: p }) })
231
+ .then(function(d) { setPortCheck(d); })
232
+ .catch(function(e) { setPortCheck({ available: false, error: e.message }); });
233
+ }, 500); // debounce 500ms
234
+ setPortCheckTimer(timer);
235
+ } else if (v && (p <= 0 || p > 65535)) {
236
+ setPortCheck({ available: false, error: 'Port must be between 1 and 65535' });
237
+ } else {
238
+ setPortCheck(null);
239
+ }
240
+ }
241
+ };
213
242
 
214
243
  // ─── Actions ────────────────────────────────────────────
215
244
 
@@ -558,7 +587,8 @@ export function DeploymentSection(props) {
558
587
  ),
559
588
  h('div', { className: 'form-group' },
560
589
  h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Ports'),
561
- h('input', { className: 'input', value: deployForm.dockerPorts, onChange: function(e) { setDf('dockerPorts', e.target.value); }, placeholder: '3000' })
590
+ h('input', { className: 'input', value: deployForm.dockerPorts, onChange: function(e) { setDf('dockerPorts', e.target.value); }, placeholder: '4100' }),
591
+ h('div', { style: { fontSize: 11, color: 'var(--text-muted)', marginTop: 2 } }, 'Comma-separated host:container ports')
562
592
  )
563
593
  ),
564
594
  h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr 1fr', gap: 16, marginTop: 12 } },
@@ -618,8 +648,11 @@ export function DeploymentSection(props) {
618
648
  h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 16 } },
619
649
  h('div', { className: 'form-group' },
620
650
  h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Port'),
621
- h('input', { className: 'input', type: 'number', value: deployForm.localPort, onChange: function(e) { setDf('localPort', e.target.value); }, placeholder: '3101' }),
622
- h('div', { style: { fontSize: 11, color: 'var(--text-muted)', marginTop: 2 } }, 'HTTP port the agent listens on')
651
+ h('input', { className: 'input', type: 'number', value: deployForm.localPort, onChange: function(e) { setDf('localPort', e.target.value); }, placeholder: '4100', style: portCheck && !portCheck.checking && !portCheck.available ? { borderColor: 'var(--danger)' } : portCheck && portCheck.available ? { borderColor: 'var(--success)' } : {} }),
652
+ portCheck && portCheck.checking && h('div', { style: { fontSize: 11, color: 'var(--text-muted)', marginTop: 2, display: 'flex', alignItems: 'center', gap: 4 } }, '\u23F3', ' Checking port availability...'),
653
+ portCheck && !portCheck.checking && portCheck.available && h('div', { style: { fontSize: 11, color: 'var(--success)', marginTop: 2 } }, '\u2713 Port ' + deployForm.localPort + ' is available'),
654
+ portCheck && !portCheck.checking && !portCheck.available && h('div', { style: { fontSize: 11, color: 'var(--danger)', marginTop: 2 } }, '\u2717 Port ' + deployForm.localPort + ' is in use' + (portCheck.process ? ' by ' + portCheck.process : '') + (portCheck.error ? ': ' + portCheck.error : '') + ' \u2014 choose a different port'),
655
+ !portCheck && h('div', { style: { fontSize: 11, color: 'var(--text-muted)', marginTop: 2 } }, 'HTTP port the agent listens on')
623
656
  ),
624
657
  h('div', { className: 'form-group' },
625
658
  h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Host'),
@@ -170,6 +170,7 @@ export function ToolsSection(props) {
170
170
  if (filter === 'enabled') return c.enabled;
171
171
  if (filter === 'disabled') return !c.enabled;
172
172
  if (filter === 'google') return c.requiresOAuth === 'google' || c.id.startsWith('google_');
173
+ if (filter === 'microsoft') return c.requiresOAuth === 'microsoft' || c.id.startsWith('outlook_') || c.id.startsWith('teams') || c.id.startsWith('onedrive') || c.id.startsWith('excel') || c.id.startsWith('sharepoint') || c.id.startsWith('onenote') || c.id.startsWith('powerpoint') || c.id.startsWith('planner') || c.id.startsWith('powerbi') || c.id.startsWith('todo');
173
174
  if (filter === 'messaging') return ['whatsapp', 'telegram'].includes(c.id);
174
175
  if (filter === 'local') return c.id.startsWith('local_');
175
176
  if (filter === 'enterprise') return c.id.startsWith('enterprise_');
@@ -179,6 +180,8 @@ export function ToolsSection(props) {
179
180
 
180
181
  var googleCats = cats.filter(function(c) { return c.requiresOAuth === 'google' || c.id.startsWith('google_'); });
181
182
  var googleAvailable = googleCats.some(function(c) { return c.isAvailable; });
183
+ var microsoftCats = cats.filter(function(c) { return c.requiresOAuth === 'microsoft'; });
184
+ var microsoftAvailable = microsoftCats.some(function(c) { return c.isAvailable; });
182
185
 
183
186
  return h('div', null,
184
187
  // Org context banner
@@ -219,6 +222,12 @@ export function ToolsSection(props) {
219
222
  'Connect a Google account in the ', h('strong', null, 'Email'), ' tab to unlock Gmail, Calendar, Drive, Sheets, Docs, and Contacts tools.'
220
223
  ),
221
224
 
225
+ // Microsoft 365 notice
226
+ !microsoftAvailable && microsoftCats.length > 0 && h('div', { style: { padding: '12px 16px', background: 'var(--warning-soft)', borderRadius: 'var(--radius)', marginBottom: 16, fontSize: 12 } },
227
+ h('strong', { style: { display: 'inline-flex', alignItems: 'center', gap: 6 } }, E.warning(16), ' Microsoft 365 tools require OAuth'), ' — ',
228
+ 'Connect a Microsoft account in the ', h('strong', null, 'Email'), ' tab to unlock Outlook, Teams, OneDrive, Excel, SharePoint, and more.'
229
+ ),
230
+
222
231
  // Filter tabs
223
232
  h('div', { className: 'tabs', style: { marginBottom: 16 } },
224
233
  [
@@ -226,6 +235,7 @@ export function ToolsSection(props) {
226
235
  { id: 'enabled', label: 'Enabled' },
227
236
  { id: 'disabled', label: 'Disabled' },
228
237
  { id: 'google', label: 'Google Workspace' },
238
+ { id: 'microsoft', label: 'Microsoft 365' },
229
239
  { id: 'messaging', label: 'Messaging' },
230
240
  { id: 'local', label: 'Local System' },
231
241
  { id: 'enterprise', label: 'Enterprise' },
@@ -132,10 +132,11 @@ export function DatabaseAccessPage() {
132
132
  try {
133
133
  var [conns, agts] = await Promise.all([
134
134
  engineCall('/database/connections'),
135
- engineCall('/agents').catch(function() { return []; }),
135
+ engineCall('/agents').catch(function() { return { agents: [] }; }),
136
136
  ]);
137
137
  setConnections(Array.isArray(conns) ? conns : []);
138
- setAgents(Array.isArray(agts) ? agts : []);
138
+ var agentList = Array.isArray(agts) ? agts : (agts && Array.isArray(agts.agents) ? agts.agents : []);
139
+ setAgents(agentList);
139
140
  } catch (e) { console.error('Load failed:', e); }
140
141
  setLoading(false);
141
142
  }, []);
@@ -694,7 +695,7 @@ function GrantAccessModal(props) {
694
695
  h('select', { style: s.select, value: agentId, onChange: function(e) { setAgentId(e.target.value); } },
695
696
  h('option', { value: '' }, '— Select Agent —'),
696
697
  props.agents.map(function(a) {
697
- return h('option', { key: a.id, value: a.id }, a.displayName || a.name);
698
+ return h('option', { key: a.id, value: a.id }, a.displayName || a.display_name || a.name || (a.config && a.config.name) || a.id);
698
699
  })
699
700
  ),
700
701
  ),
@@ -1,4 +1,4 @@
1
- import { h, useState, useEffect, useCallback, Fragment, useApp, apiCall, engineCall, applyBrandColor, showConfirm, setOrgId, getOrgId } from '../components/utils.js';
1
+ import { h, useState, useEffect, useCallback, useRef, Fragment, useApp, apiCall, engineCall, applyBrandColor, showConfirm, setOrgId, getOrgId } from '../components/utils.js';
2
2
  import { I } from '../components/icons.js';
3
3
  import { E } from '../assets/icons/emoji-icons.js';
4
4
  import { Modal } from '../components/modal.js';
@@ -6,12 +6,12 @@ import { TagInput } from '../components/tag-input.js';
6
6
  import { COUNTRIES } from '../data/countries.js?v=6';
7
7
  import { HelpButton } from '../components/help-button.js';
8
8
  import { SETTINGS_HELP } from '../components/settings-help.js';
9
- import { KnowledgeLink } from '../components/knowledge-link.js';
9
+ import { KnowledgeLink, SETTINGS_TAB_DOCS } from '../components/knowledge-link.js';
10
10
  import { ProviderLogo } from '../assets/provider-logos.js';
11
11
  import { useOrgContext } from '../components/org-switcher.js';
12
12
 
13
13
  export function SettingsPage() {
14
- const { toast } = useApp();
14
+ const { toast, setCompanyName } = useApp();
15
15
  var orgCtx = useOrgContext();
16
16
  var effectiveOrgId = orgCtx.selectedOrgId || '';
17
17
  const [tab, setTab] = useState('general');
@@ -280,6 +280,11 @@ export function SettingsPage() {
280
280
  })
281
281
  ),
282
282
 
283
+ // ─── Knowledge Link for current tab ─────────────────
284
+ SETTINGS_TAB_DOCS[tab] && h('div', { style: { display: 'flex', justifyContent: 'flex-end', marginBottom: 8 } },
285
+ h(KnowledgeLink, { page: SETTINGS_TAB_DOCS[tab], label: (TAB_LABELS[tab] || tab) + ' Docs' })
286
+ ),
287
+
283
288
  tab === 'general' && h('div', null,
284
289
  h('div', { className: 'card', style: { marginBottom: 16 } },
285
290
  h('div', { className: 'card-header' }, h('h3', { style: { display: 'flex', alignItems: 'center' } }, 'Organization', h(HelpButton, { label: 'Organization Settings' },
@@ -326,7 +331,7 @@ export function SettingsPage() {
326
331
  h('input', { className: 'input', value: settings.primaryColor || '', onChange: e => { setSettings(s => ({ ...s, primaryColor: e.target.value })); if (/^#[0-9a-fA-F]{6}$/.test(e.target.value)) applyBrandColor(e.target.value); }, style: { maxWidth: 120, fontFamily: 'var(--font-mono)', fontSize: 12 } })
327
332
  )
328
333
  ),
329
- h('button', { className: 'btn btn-primary', onClick: () => apiCall('/settings', { method: 'PATCH', body: JSON.stringify({ name: settings.name, domain: settings.domain, subdomain: settings.subdomain, logoUrl: settings.logoUrl, primaryColor: settings.primaryColor, plan: settings.plan }) }).then(d => { setSettings(d); toast('Settings saved', 'success'); }).catch(e => toast(e.message, 'error')) }, 'Save Changes')
334
+ h('button', { className: 'btn btn-primary', onClick: () => apiCall('/settings', { method: 'PATCH', body: JSON.stringify({ name: settings.name, domain: settings.domain, subdomain: settings.subdomain, logoUrl: settings.logoUrl, primaryColor: settings.primaryColor, plan: settings.plan }) }).then(d => { setSettings(d); if (d.name && setCompanyName) setCompanyName(d.name); toast('Settings saved', 'success'); }).catch(e => toast(e.message, 'error')) }, 'Save Changes')
330
335
  )
331
336
  ),
332
337
 
@@ -1603,24 +1608,61 @@ function ComprehensiveSecurityTab(props) {
1603
1608
  );
1604
1609
  }
1605
1610
 
1611
+ // ── Per-section editing ──
1612
+ var _editState = useState(null); // which section key is being edited
1613
+ var editingSection = _editState[0]; var setEditingSection = _editState[1];
1614
+ var _snapshot = useRef(null); // snapshot of securityConfig before editing
1615
+
1616
+ function startEdit(section) {
1617
+ _snapshot.current = JSON.parse(JSON.stringify(securityConfig));
1618
+ setEditingSection(section);
1619
+ }
1620
+ function cancelEdit() {
1621
+ if (_snapshot.current) setSecurityConfig(_snapshot.current);
1622
+ _snapshot.current = null;
1623
+ setEditingSection(null);
1624
+ }
1625
+ function saveSection() {
1626
+ onSave();
1627
+ // onSave triggers async save; clear edit state optimistically
1628
+ setTimeout(function() {
1629
+ _snapshot.current = null;
1630
+ setEditingSection(null);
1631
+ }, 500);
1632
+ }
1633
+
1634
+ function sectionHeader(icon, title, sectionKey) {
1635
+ var isEditing = editingSection === sectionKey;
1636
+ return h('div', { style: Object.assign({}, _cardTitleStyle, { justifyContent: 'space-between' }) },
1637
+ h('span', { style: { display: 'flex', alignItems: 'center', gap: 8 } }, icon, title),
1638
+ isEditing
1639
+ ? h('div', { style: { display: 'flex', gap: 6 } },
1640
+ h('button', { className: 'btn btn-primary btn-sm', disabled: saving, onClick: saveSection }, saving ? 'Saving...' : 'Save'),
1641
+ h('button', { className: 'btn btn-ghost btn-sm', onClick: cancelEdit }, 'Cancel')
1642
+ )
1643
+ : h('button', { className: 'btn btn-ghost btn-sm', onClick: function() { startEdit(sectionKey); }, style: { fontSize: 12 } }, I.journal(), ' Edit')
1644
+ );
1645
+ }
1646
+
1647
+ // Helper: section content wrapper — dims + disables when not editing
1648
+ function sectionBody(sectionKey, content) {
1649
+ var isEditing = editingSection === sectionKey;
1650
+ return h('div', { style: isEditing ? {} : { opacity: 0.7, pointerEvents: 'none' } }, content);
1651
+ }
1652
+
1606
1653
  return h('div', null,
1607
1654
  h('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 24 } },
1608
1655
  h('div', null,
1609
1656
  h('h2', { style: { fontSize: 18, fontWeight: 700, margin: 0 } }, 'Security System'),
1610
1657
  h('p', { style: { fontSize: 14, color: 'var(--text-muted)', margin: '4px 0 0' } }, 'Comprehensive security configuration for your AgenticMail deployment')
1611
- ),
1612
- h('button', {
1613
- className: 'btn btn-primary',
1614
- onClick: onSave,
1615
- disabled: !dirty || saving,
1616
- style: { minWidth: 80 }
1617
- }, saving ? 'Saving...' : 'Save Changes')
1658
+ )
1618
1659
  ),
1619
1660
 
1620
1661
  // Prompt Injection Defense
1621
1662
  h('div', { style: _cardStyle },
1622
- h('div', { style: _cardTitleStyle }, I.shield(), 'Prompt Injection Defense'),
1663
+ sectionHeader(I.shield(), 'Prompt Injection Defense', 'promptInjection'),
1623
1664
  h('p', { style: _cardDescStyle }, 'Multi-layer detection and prevention of prompt injection attacks'),
1665
+ sectionBody('promptInjection', h(Fragment, null,
1624
1666
  h('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 12 } },
1625
1667
  h('span', { style: { fontWeight: 500 } }, 'Enable Protection'),
1626
1668
  h(ToggleSwitch, { checked: promptInjection.enabled, onChange: function(v) { updateSection('promptInjection', { enabled: v }); } })
@@ -1672,13 +1714,14 @@ function ComprehensiveSecurityTab(props) {
1672
1714
  rows: 2
1673
1715
  })
1674
1716
  )
1675
- )
1717
+ )))
1676
1718
  ),
1677
1719
 
1678
1720
  // SQL Injection Prevention
1679
1721
  h('div', { style: _cardStyle },
1680
- h('div', { style: _cardTitleStyle }, I.shield(), 'SQL Injection Prevention'),
1722
+ sectionHeader(I.shield(), 'SQL Injection Prevention', 'sqlInjection'),
1681
1723
  h('p', { style: _cardDescStyle }, 'Detect and block SQL injection attempts in tool inputs and API requests'),
1724
+ sectionBody('sqlInjection', h(Fragment, null,
1682
1725
  h('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 12 } },
1683
1726
  h('span', { style: { fontWeight: 500 } }, 'Enable Protection'),
1684
1727
  h(ToggleSwitch, { checked: sqlInjection.enabled, onChange: function(v) { updateSection('sqlInjection', { enabled: v }); } })
@@ -1717,13 +1760,14 @@ function ComprehensiveSecurityTab(props) {
1717
1760
  )
1718
1761
  )
1719
1762
  )
1720
- )
1763
+ )))
1721
1764
  ),
1722
1765
 
1723
1766
  // Input Validation
1724
1767
  h('div', { style: _cardStyle },
1725
- h('div', { style: _cardTitleStyle }, I.shield(), 'Input Validation'),
1768
+ sectionHeader(I.shield(), 'Input Validation', 'inputValidation'),
1726
1769
  h('p', { style: _cardDescStyle }, 'Sanitize and validate all input data'),
1770
+ sectionBody('inputValidation', h(Fragment, null,
1727
1771
  h('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 12 } },
1728
1772
  h('span', { style: { fontWeight: 500 } }, 'Enable Validation'),
1729
1773
  h(ToggleSwitch, { checked: inputValidation.enabled, onChange: function(v) { updateSection('inputValidation', { enabled: v }); } })
@@ -1782,13 +1826,14 @@ function ComprehensiveSecurityTab(props) {
1782
1826
  h('span', null, 'Sanitize Unicode')
1783
1827
  )
1784
1828
  )
1785
- )
1829
+ )))
1786
1830
  ),
1787
1831
 
1788
1832
  // Output Filtering
1789
1833
  h('div', { style: _cardStyle },
1790
- h('div', { style: _cardTitleStyle }, I.shield(), 'Output Filtering'),
1834
+ sectionHeader(I.shield(), 'Output Filtering', 'outputFiltering'),
1791
1835
  h('p', { style: _cardDescStyle }, 'Scan agent outputs for secrets and personal information'),
1836
+ sectionBody('outputFiltering', h(Fragment, null,
1792
1837
  h('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 12 } },
1793
1838
  h('span', { style: { fontWeight: 500 } }, 'Enable Filtering'),
1794
1839
  h(ToggleSwitch, { checked: outputFiltering.enabled, onChange: function(v) { updateSection('outputFiltering', { enabled: v }); } })
@@ -1828,7 +1873,7 @@ function ComprehensiveSecurityTab(props) {
1828
1873
  )
1829
1874
  )
1830
1875
  )
1831
- )
1876
+ )))
1832
1877
  ),
1833
1878
 
1834
1879
  // Transport Encryption
@@ -1907,8 +1952,9 @@ function ComprehensiveSecurityTab(props) {
1907
1952
  var groupCount = Object.keys(enabledGroups).filter(function(k) { return enabledGroups[k]; }).length;
1908
1953
 
1909
1954
  return h('div', { style: _cardStyle },
1910
- h('div', { style: _cardTitleStyle }, I.lock(), 'Transport Encryption'),
1955
+ sectionHeader(I.lock(), 'Transport Encryption', 'transportEncryption'),
1911
1956
  h('p', { style: _cardDescStyle }, 'Encrypt API data in transit between the dashboard and server using AES-256-CBC with HMAC verification. Protects against network sniffing, MITM attacks, and compromised TLS proxies. Choose to encrypt all API calls or select specific endpoint groups.'),
1957
+ sectionBody('transportEncryption', h(Fragment, null,
1912
1958
 
1913
1959
  // Master toggle
1914
1960
  h('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 16 } },
@@ -2021,7 +2067,7 @@ function ComprehensiveSecurityTab(props) {
2021
2067
  ),
2022
2068
  h('p', { style: { marginTop: 8, color: 'var(--text-muted)' } }, 'HTTPS already encrypts all traffic at the transport layer. This adds application-layer encryption for defense-in-depth, protecting against compromised TLS proxies, corporate SSL inspection, or man-in-the-middle attacks.')
2023
2069
  )
2024
- )
2070
+ )))
2025
2071
  );
2026
2072
  })(),
2027
2073
 
@@ -2035,8 +2081,9 @@ function ComprehensiveSecurityTab(props) {
2035
2081
  }
2036
2082
 
2037
2083
  return h('div', { style: _cardStyle },
2038
- h('div', { style: _cardTitleStyle }, I.settings(), 'Dependency & Package Management'),
2084
+ sectionHeader(I.settings(), 'Dependency & Package Management', 'dependencyDefaults'),
2039
2085
  h('p', { style: _cardDescStyle }, 'Organization-wide defaults for agent package installation. Individual agents can override these in their Permissions tab.'),
2086
+ sectionBody('dependencyDefaults', h(Fragment, null,
2040
2087
 
2041
2088
  // Mode
2042
2089
  h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16, marginBottom: 16 } },
@@ -2099,14 +2146,104 @@ function ComprehensiveSecurityTab(props) {
2099
2146
  h('label', { className: 'form-label' }, 'Blocked Packages'),
2100
2147
  h('p', { style: { fontSize: 12, color: 'var(--text-muted)', margin: '0 0 6px' } }, 'Package names that agents can never install, regardless of other settings.'),
2101
2148
  h(TagInput, { value: depDefaults.blockedPackages || [], onChange: function(v) { updateDep({ blockedPackages: v }); }, placeholder: 'e.g. nmap, metasploit', mono: true })
2102
- )
2149
+ )))
2150
+ );
2151
+ })(),
2152
+
2153
+ // ── Screen Unlock & Machine Access ──
2154
+ (function() {
2155
+ var screenAccess = securityConfig.screenAccess || { enabled: false, systemPassword: '', autoUnlock: false, preventSleep: false };
2156
+ function updateScreen(updates) {
2157
+ updateSection('screenAccess', Object.assign({}, screenAccess, updates));
2158
+ }
2159
+ var _screenStatus = useState(null);
2160
+ var screenStatus = _screenStatus[0]; var setScreenStatus = _screenStatus[1];
2161
+ var _unlocking = useState(false);
2162
+ var unlocking = _unlocking[0]; var setUnlocking = _unlocking[1];
2163
+
2164
+ return h('div', { style: _cardStyle },
2165
+ sectionHeader(I.lock(), 'Screen Unlock & Machine Access', 'screenAccess'),
2166
+ h('p', { style: _cardDescStyle }, 'Allow the system to automatically unlock the screen when agents need to work. Without this, agents cannot operate when the machine is locked.'),
2167
+ sectionBody('screenAccess', h(Fragment, null,
2168
+
2169
+ h('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 16 } },
2170
+ h('div', null,
2171
+ h('span', { style: { fontWeight: 600, fontSize: 14 } }, 'Enable Screen Auto-Unlock'),
2172
+ h('p', { style: { fontSize: 12, color: 'var(--text-muted)', margin: '2px 0 0' } }, 'When enabled, the system will automatically unlock the screen when agents need access (e.g., during heartbeat checks, browser automation, or scheduled tasks)')
2173
+ ),
2174
+ h(ToggleSwitch, { checked: screenAccess.enabled, onChange: function(v) { updateScreen({ enabled: v }); } })
2175
+ ),
2176
+
2177
+ screenAccess.enabled && h(Fragment, null,
2178
+ h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16, marginBottom: 16 } },
2179
+ h('div', null,
2180
+ h('label', { className: 'form-label' }, 'System / Computer Password'),
2181
+ h('input', { className: 'input', type: 'password', value: screenAccess.systemPassword || '', onChange: function(e) { updateScreen({ systemPassword: e.target.value }); }, placeholder: 'Your macOS/Linux login password' }),
2182
+ h('p', { style: { fontSize: 11, color: 'var(--text-muted)', marginTop: 4 } }, 'Stored encrypted. Used to unlock the screen when agents need to work. This is the password you use to log into your computer.')
2183
+ ),
2184
+ h('div', null,
2185
+ h('label', { className: 'form-label' }, 'Screen Status'),
2186
+ h('div', { style: { marginTop: 4 } },
2187
+ h('button', { className: 'btn btn-secondary btn-sm', style: { marginBottom: 8 }, onClick: function() {
2188
+ engineCall('/system/screen-status').then(function(d) { setScreenStatus(d); }).catch(function(e) { setScreenStatus({ error: e.message }); });
2189
+ } }, 'Check Screen Status'),
2190
+ screenStatus && h('div', { style: { fontSize: 12, marginTop: 4 } },
2191
+ screenStatus.error ? h('span', { style: { color: 'var(--danger)' } }, screenStatus.error) :
2192
+ h('span', { style: { color: screenStatus.locked ? 'var(--warning-text, #b45309)' : 'var(--success)' } },
2193
+ screenStatus.locked ? 'Screen is LOCKED' + (screenStatus.displayAsleep ? ' (display asleep)' : '') : 'Screen is unlocked',
2194
+ ' \u2014 ', screenStatus.platform
2195
+ )
2196
+ )
2197
+ ),
2198
+ screenStatus && screenStatus.locked && h('button', { className: 'btn btn-primary btn-sm', style: { marginTop: 8 }, disabled: unlocking || !screenAccess.systemPassword, onClick: function() {
2199
+ setUnlocking(true);
2200
+ engineCall('/system/unlock-screen', { method: 'POST', body: JSON.stringify({ password: screenAccess.systemPassword }) })
2201
+ .then(function(d) {
2202
+ if (d.success) { toast(d.message, 'success'); setScreenStatus(null); }
2203
+ else { toast(d.error || 'Unlock failed', 'error'); }
2204
+ })
2205
+ .catch(function(e) { toast(e.message, 'error'); })
2206
+ .finally(function() { setUnlocking(false); });
2207
+ } }, unlocking ? 'Unlocking...' : 'Unlock Now')
2208
+ )
2209
+ ),
2210
+
2211
+ h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 } },
2212
+ h('div', null,
2213
+ h('div', { style: { display: 'flex', alignItems: 'center', gap: 8, marginBottom: 4 } },
2214
+ h(ToggleSwitch, { checked: screenAccess.autoUnlock === true, onChange: function(v) { updateScreen({ autoUnlock: v }); } }),
2215
+ h('span', { style: { fontWeight: 500 } }, 'Auto-Unlock on Agent Activity')
2216
+ ),
2217
+ h('p', { style: { fontSize: 12, color: 'var(--text-muted)', margin: 0 } }, 'Automatically unlock the screen when an agent needs to use the browser, run desktop automation, or start a scheduled task.')
2218
+ ),
2219
+ h('div', null,
2220
+ h('div', { style: { display: 'flex', alignItems: 'center', gap: 8, marginBottom: 4 } },
2221
+ h(ToggleSwitch, { checked: screenAccess.preventSleep === true, onChange: function(v) { updateScreen({ preventSleep: v }); } }),
2222
+ h('span', { style: { fontWeight: 500 } }, 'Prevent System Sleep')
2223
+ ),
2224
+ h('p', { style: { fontSize: 12, color: 'var(--text-muted)', margin: 0 } }, 'Keep the system awake using caffeinate (macOS) or systemd-inhibit (Linux). Prevents the machine from sleeping while agents are active.')
2225
+ )
2226
+ ),
2227
+
2228
+ h('div', { style: { marginTop: 16, padding: 12, background: 'var(--bg-tertiary)', borderRadius: 8, fontSize: 12, color: 'var(--text-muted)', border: '1px solid var(--border)' } },
2229
+ h('strong', null, 'How it works: '),
2230
+ 'When an agent needs to interact with the system (browser automation, desktop tools, etc.) and detects the screen is locked, it will automatically: ',
2231
+ h('br'), '1. Wake the display if asleep',
2232
+ h('br'), '2. Type your password to unlock the screen',
2233
+ h('br'), '3. Perform the required action',
2234
+ h('br'), h('br'),
2235
+ h('strong', null, 'Security note: '),
2236
+ 'The password is stored in the server\'s encrypted security config. Only the server process has access to it \u2014 agents never see the raw password.'
2237
+ )
2238
+ )))
2103
2239
  );
2104
2240
  })(),
2105
2241
 
2106
2242
  // Security Audit Log
2107
2243
  h('div', { style: _cardStyle },
2108
- h('div', { style: _cardTitleStyle }, I.journal(), 'Security Audit Log'),
2244
+ sectionHeader(I.journal(), 'Security Audit Log', 'auditSecurity'),
2109
2245
  h('p', { style: _cardDescStyle }, 'Log and monitor security events'),
2246
+ sectionBody('auditSecurity', h(Fragment, null,
2110
2247
  h('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 12 } },
2111
2248
  h('span', { style: { fontWeight: 500 } }, 'Enable Audit Logging'),
2112
2249
  h(ToggleSwitch, { checked: auditSecurity.enabled, onChange: function(v) { updateSection('auditSecurity', { enabled: v }); } })
@@ -2181,7 +2318,7 @@ function ComprehensiveSecurityTab(props) {
2181
2318
  )
2182
2319
  )
2183
2320
  )
2184
- )
2321
+ )))
2185
2322
  )
2186
2323
  );
2187
2324
  }
@@ -2278,7 +2415,7 @@ function NetworkFirewallTab(props) {
2278
2415
  // ── IP ACCESS CONTROL ──
2279
2416
  h('div', { style: _sectionTitleStyle }, 'IP Access Control'),
2280
2417
  h('div', { style: _cardStyle },
2281
- h('div', { style: _cardTitleStyle }, I.shield(), ' Inbound IP Filtering'),
2418
+ h('div', { style: _cardTitleStyle }, I.shield(), ' Inbound IP Filtering', h(HelpButton, { label: 'Inbound IP Filtering' }, h('div', null, h('p', null, 'Controls which IP addresses can reach your dashboard and API endpoints.'), h('p', null, h('strong', null, 'Allowlist mode:'), ' Only listed IPs can access. Everything else is blocked.'), h('p', null, h('strong', null, 'Blocklist mode:'), ' All IPs allowed except listed ones.'), h('p', null, 'Supports CIDR notation (e.g. 10.0.0.0/8). Bypass paths like /health are always accessible.')))),
2282
2419
  h('div', { style: _cardDescStyle }, 'Restrict which IP addresses can access the dashboard, APIs, and engine endpoints. Supports individual IPs and CIDR ranges.'),
2283
2420
  h(ToggleSwitch, { label: 'Enable IP access control', checked: ipAccess.enabled === true, onChange: function(v) { var next = Object.assign({}, ipAccess, { enabled: v }); if (!next.mode) next.mode = 'allowlist'; patchFw('ipAccess', next); } }),
2284
2421
  ipAccess.enabled && h(Fragment, null,
@@ -2310,7 +2447,7 @@ function NetworkFirewallTab(props) {
2310
2447
  // ── OUTBOUND EGRESS ──
2311
2448
  h('div', { style: _sectionTitleStyle }, 'Outbound Egress Rules'),
2312
2449
  h('div', { style: _cardStyle },
2313
- h('div', { style: _cardTitleStyle }, I.globe(), ' Egress Filtering'),
2450
+ h('div', { style: _cardTitleStyle }, I.globe(), ' Egress Filtering', h(HelpButton, { label: 'Egress Filtering' }, h('div', null, h('p', null, 'Controls outbound network access for agents. Prevents agents from reaching unauthorized external services.'), h('p', null, h('strong', null, 'Allowlist:'), ' Agents can only connect to listed hosts/ports.'), h('p', null, h('strong', null, 'Blocklist:'), ' Agents can connect anywhere except listed hosts/ports.'), h('p', null, 'Wildcards supported (e.g. *.googleapis.com). Applies to web fetch, browser automation, and HTTP tools.')))),
2314
2451
  h('div', { style: _cardDescStyle }, 'Control which external hosts and ports agents can reach when using web fetch, browser, and other network tools.'),
2315
2452
  h(ToggleSwitch, { label: 'Enable egress filtering', checked: egress.enabled === true, onChange: function(v) { var next = Object.assign({}, egress, { enabled: v }); if (!next.mode) next.mode = 'blocklist'; patchFw('egress', next); } }),
2316
2453
  egress.enabled && h(Fragment, null,
@@ -2338,7 +2475,7 @@ function NetworkFirewallTab(props) {
2338
2475
 
2339
2476
  // Proxy config
2340
2477
  h('div', { style: _cardStyle },
2341
- h('div', { style: _cardTitleStyle }, I.link(), ' Proxy Configuration'),
2478
+ h('div', { style: _cardTitleStyle }, I.link(), ' Proxy Configuration', h(HelpButton, { label: 'Proxy Configuration' }, h('div', null, h('p', null, 'Route agent outbound traffic through a corporate proxy. Required in air-gapped or restricted network environments.'), h('p', null, 'Set HTTP and HTTPS proxy URLs. Use No-Proxy to bypass the proxy for internal hosts (e.g. *.internal, localhost).')))),
2342
2479
  h('div', { style: _cardDescStyle }, 'Configure HTTP/HTTPS proxies for agent outbound traffic in air-gapped or restricted environments.'),
2343
2480
  h('div', { className: 'form-group', style: { marginBottom: 12 } },
2344
2481
  h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'HTTP Proxy'),
@@ -2353,7 +2490,7 @@ function NetworkFirewallTab(props) {
2353
2490
 
2354
2491
  // Trusted proxies
2355
2492
  h('div', { style: _cardStyle },
2356
- h('div', { style: _cardTitleStyle }, I.shield(), ' Trusted Proxies'),
2493
+ h('div', { style: _cardTitleStyle }, I.shield(), ' Trusted Proxies', h(HelpButton, { label: 'Trusted Proxies' }, h('div', null, h('p', null, 'When behind a load balancer or reverse proxy (e.g. Cloudflare, nginx), the real client IP comes from X-Forwarded-For headers.'), h('p', null, 'List your proxy IPs/CIDRs here so the system extracts the correct client IP for IP access control and rate limiting.')))),
2357
2494
  h('div', { style: _cardDescStyle }, 'Specify which reverse proxies are trusted for X-Forwarded-For header extraction. Required for accurate IP-based access control behind load balancers.'),
2358
2495
  h(ToggleSwitch, { label: 'Enable trusted proxy validation', checked: tp.enabled === true, onChange: function(v) { patchTp('enabled', v); } }),
2359
2496
  tp.enabled && h(TagInput, { label: 'Trusted Proxy IPs / CIDRs', value: tp.ips || [], onChange: function(v) { patchTp('ips', v); }, placeholder: '10.0.0.0/8', mono: true })
@@ -2366,14 +2503,14 @@ function NetworkFirewallTab(props) {
2366
2503
 
2367
2504
  // CORS
2368
2505
  h('div', { style: _cardStyle },
2369
- h('div', { style: _cardTitleStyle }, I.globe(), ' CORS Origins'),
2506
+ h('div', { style: _cardTitleStyle }, I.globe(), ' CORS Origins', h(HelpButton, { label: 'CORS Origins' }, h('div', null, h('p', null, 'Cross-Origin Resource Sharing (CORS) controls which domains can make API requests to your server from a browser.'), h('p', null, 'Add your dashboard URL and any custom frontend domains. Leave empty to allow all origins (not recommended for production).')))),
2370
2507
  h('div', { style: _cardDescStyle }, 'Allowed origins for cross-origin requests. Leave empty to allow all origins (*).'),
2371
2508
  h(TagInput, { label: 'Allowed Origins', value: net.corsOrigins || [], onChange: function(v) { patchNet('corsOrigins', v); }, placeholder: 'https://dashboard.example.com', mono: true })
2372
2509
  ),
2373
2510
 
2374
2511
  // Rate Limiting
2375
2512
  h('div', { style: _cardStyle },
2376
- h('div', { style: _cardTitleStyle }, I.clock(), ' Rate Limiting'),
2513
+ h('div', { style: _cardTitleStyle }, I.clock(), ' Rate Limiting', h(HelpButton, { label: 'Rate Limiting' }, h('div', null, h('p', null, 'Limits API requests per IP address using a token bucket algorithm. Prevents brute-force attacks and API abuse.'), h('p', null, 'Skip paths (like /health) are excluded from rate limiting. Adjust requests per minute based on your expected traffic.')))),
2377
2514
  h('div', { style: _cardDescStyle }, 'Per-IP rate limiting using token bucket algorithm. Protects against abuse and DDoS.'),
2378
2515
  h(ToggleSwitch, { label: 'Enable rate limiting', checked: rl.enabled !== false, onChange: function(v) { patchRl('enabled', v); } }),
2379
2516
  h('div', { className: 'form-group', style: { marginBottom: 12 } },
@@ -2385,7 +2522,7 @@ function NetworkFirewallTab(props) {
2385
2522
 
2386
2523
  // HTTPS Enforcement
2387
2524
  h('div', { style: _cardStyle },
2388
- h('div', { style: _cardTitleStyle }, I.key(), ' HTTPS Enforcement'),
2525
+ h('div', { style: _cardTitleStyle }, I.key(), ' HTTPS Enforcement', h(HelpButton, { label: 'HTTPS Enforcement' }, h('div', null, h('p', null, 'Redirects all HTTP requests to HTTPS in production. Essential for protecting data in transit.'), h('p', null, 'Uses X-Forwarded-Proto header detection for reverse proxy setups (Cloudflare, nginx, etc.). Exclude specific paths like health checks if needed.')))),
2389
2526
  h('div', { style: _cardDescStyle }, 'Require HTTPS for all requests in production. Checks X-Forwarded-Proto header for reverse proxy setups.'),
2390
2527
  h(ToggleSwitch, { label: 'Enforce HTTPS', checked: https.enabled === true, onChange: function(v) { patchHttps('enabled', v); } }),
2391
2528
  https.enabled && h(TagInput, { label: 'Exclude Paths', value: https.excludePaths || [], onChange: function(v) { patchHttps('excludePaths', v); }, placeholder: '/health', mono: true })
@@ -2393,7 +2530,7 @@ function NetworkFirewallTab(props) {
2393
2530
 
2394
2531
  // Security Headers
2395
2532
  h('div', { style: _cardStyle },
2396
- h('div', { style: _cardTitleStyle }, I.shield(), ' Security Headers'),
2533
+ h('div', { style: _cardTitleStyle }, I.shield(), ' Security Headers', h(HelpButton, { label: 'Security Headers' }, h('div', null, h('p', null, 'HTTP headers added to every response for defense-in-depth browser security.'), h('p', null, h('strong', null, 'HSTS:'), ' Forces browsers to use HTTPS for future visits.'), h('p', null, h('strong', null, 'X-Frame-Options:'), ' Prevents clickjacking by controlling iframe embedding.'), h('p', null, h('strong', null, 'Referrer-Policy:'), ' Controls how much referrer info is sent with requests.'), h('p', null, h('strong', null, 'Permissions-Policy:'), ' Disables browser features like camera/microphone access.')))),
2397
2534
  h('div', { style: _cardDescStyle }, 'HTTP security headers applied to all responses. Protects against clickjacking, MIME sniffing, and other browser-level attacks.'),
2398
2535
  h(ToggleSwitch, { label: 'Strict-Transport-Security (HSTS)', checked: sh.hsts !== false, onChange: function(v) { patchSh('hsts', v); } }),
2399
2536
  sh.hsts !== false && h('div', { className: 'form-group', style: { marginBottom: 12 } },
@@ -2431,7 +2568,7 @@ function NetworkFirewallTab(props) {
2431
2568
 
2432
2569
  // DNS Rebinding Protection
2433
2570
  h('div', { style: _cardStyle },
2434
- h('div', { style: _cardTitleStyle }, I.shield(), ' DNS Rebinding Protection'),
2571
+ h('div', { style: _cardTitleStyle }, I.shield(), ' DNS Rebinding Protection', h(HelpButton, { label: 'DNS Rebinding Protection' }, h('div', null, h('p', null, 'Prevents DNS rebinding attacks where a malicious website resolves its domain to your internal server IP.'), h('p', null, 'When enabled, requests with a Host header not in the allowlist are rejected. Add your domain(s) to the allowed hosts list.')))),
2435
2572
  h('div', { style: _cardDescStyle }, 'Validates the Host header against an allowlist to prevent DNS rebinding attacks targeting internal services.'),
2436
2573
  h(ToggleSwitch, { label: 'Enable DNS rebinding protection', checked: dnsReb.enabled === true, onChange: function(v) { patchFw('dnsRebinding', Object.assign({}, dnsReb, { enabled: v })); } }),
2437
2574
  dnsReb.enabled && h(TagInput, { label: 'Allowed Hosts', value: dnsReb.allowedHosts || [], onChange: function(v) { patchFw('dnsRebinding', Object.assign({}, dnsReb, { allowedHosts: v })); }, placeholder: 'enterprise.example.com', mono: true })
@@ -2439,7 +2576,7 @@ function NetworkFirewallTab(props) {
2439
2576
 
2440
2577
  // Request Body Size Limit
2441
2578
  h('div', { style: _cardStyle },
2442
- h('div', { style: _cardTitleStyle }, I.shield(), ' Request Body Limits'),
2579
+ h('div', { style: _cardTitleStyle }, I.shield(), ' Request Body Limits', h(HelpButton, { label: 'Request Body Limits' }, h('div', null, h('p', null, 'Limits the maximum size of incoming request bodies to prevent denial-of-service via oversized payloads.'), h('p', null, 'Default is 10 MB. Increase if agents need to upload large files. Decrease for tighter security in exposed environments.')))),
2443
2580
  h('div', { style: _cardDescStyle }, 'Maximum request body size for API endpoints. Prevents excessively large payloads from consuming server resources.'),
2444
2581
  h('div', { className: 'form-group', style: { marginBottom: 12 } },
2445
2582
  h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Max Body Size (KB)'),
@@ -2450,7 +2587,7 @@ function NetworkFirewallTab(props) {
2450
2587
 
2451
2588
  // Geo-IP Restrictions
2452
2589
  h('div', { style: _cardStyle },
2453
- h('div', { style: _cardTitleStyle }, I.globe(), ' Geo-IP Restrictions'),
2590
+ h('div', { style: _cardTitleStyle }, I.globe(), ' Geo-IP Restrictions', h(HelpButton, { label: 'Geo-IP Restrictions' }, h('div', null, h('p', null, 'Restricts access based on the geographic location of the client IP address.'), h('p', null, h('strong', null, 'Allowlist:'), ' Only selected countries can access.'), h('p', null, h('strong', null, 'Blocklist:'), ' Selected countries are blocked, everyone else allowed.'), h('p', null, 'Uses built-in IP geolocation — works without Cloudflare or any reverse proxy.')))),
2454
2591
  h('div', { style: _cardDescStyle }, 'Restrict access by country using built-in IP geolocation. Works with any setup — no reverse proxy headers required.'),
2455
2592
  h(ToggleSwitch, { label: 'Enable geo-IP filtering', checked: geoIp.enabled === true, onChange: function(v) { patchFw('geoIp', Object.assign({}, geoIp, { enabled: v, mode: geoIp.mode || 'blocklist' })); } }),
2456
2593
  geoIp.enabled && h(Fragment, null,
@@ -2470,7 +2607,7 @@ function NetworkFirewallTab(props) {
2470
2607
 
2471
2608
  // Webhook Security
2472
2609
  h('div', { style: _cardStyle },
2473
- h('div', { style: _cardTitleStyle }, I.key(), ' Webhook Security'),
2610
+ h('div', { style: _cardTitleStyle }, I.key(), ' Webhook Security', h(HelpButton, { label: 'Webhook Security' }, h('div', null, h('p', null, 'Secures inbound webhook endpoints used by Slack, Google Chat, and other integrations.'), h('p', null, h('strong', null, 'HMAC validation:'), ' Requires incoming webhooks to include a valid signature, preventing spoofed requests.'), h('p', null, h('strong', null, 'Source IP filtering:'), ' Only accept webhooks from known provider IP ranges (e.g. Google, Slack).')))),
2474
2611
  h('div', { style: _cardDescStyle }, 'Security controls for inbound webhook endpoints (Google Chat, Slack, third-party integrations).'),
2475
2612
  h(ToggleSwitch, { label: 'Enable webhook security', checked: webhookSec.enabled === true, onChange: function(v) { patchFw('webhookSecurity', Object.assign({}, webhookSec, { enabled: v })); } }),
2476
2613
  webhookSec.enabled && h(Fragment, null,