@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.
- package/CHANGELOG.md +68 -0
- package/CODE_OF_CONDUCT.md +31 -0
- package/README.md +118 -38
- package/SECURITY.md +42 -0
- package/dist/agent-heartbeat-3FWNHZFX.js +510 -0
- package/dist/agent-heartbeat-4RWHZR7H.js +510 -0
- package/dist/agent-heartbeat-6ZGB5ILY.js +510 -0
- package/dist/agent-heartbeat-BIVHLKFM.js +510 -0
- package/dist/agent-heartbeat-HRKVFK2T.js +510 -0
- package/dist/agent-heartbeat-JC5GWVXD.js +510 -0
- package/dist/agent-heartbeat-K6A4HMHB.js +510 -0
- package/dist/agent-heartbeat-LCDXWFVB.js +510 -0
- package/dist/agent-heartbeat-P7HZCZAQ.js +510 -0
- package/dist/agent-heartbeat-PUIRSNIO.js +510 -0
- package/dist/agent-heartbeat-SN5ILQ6Y.js +510 -0
- package/dist/agent-heartbeat-TW5YTDYC.js +510 -0
- package/dist/agent-heartbeat-Z2QQXROL.js +510 -0
- package/dist/agent-notify-OEQBCZLN.js +43 -0
- package/dist/{agent-tools-263HM5QU.js → agent-tools-3W7XLUYA.js} +1 -1
- package/dist/agent-tools-4QK7LLNP.js +9 -0
- package/dist/agent-tools-54VZGT6L.js +9 -0
- package/dist/{agent-tools-AT4D276V.js → agent-tools-AYYDPO27.js} +7 -7
- package/dist/{agent-tools-MSTAPX2I.js → agent-tools-F2X47FKF.js} +7 -7
- package/dist/{agent-tools-FA26SY5O.js → agent-tools-O6W3QAZL.js} +11 -6
- package/dist/agent-tools-OAWVZBMW.js +9 -0
- package/dist/agent-tools-QCCU74PN.js +13949 -0
- package/dist/chunk-2LHUARN6.js +4929 -0
- package/dist/chunk-2WVCNCYC.js +5087 -0
- package/dist/{chunk-6PWDS7KY.js → chunk-3FM6YQUK.js} +20 -20
- package/dist/chunk-3UAFHUEC.js +212 -0
- package/dist/{chunk-WJO57PMO.js → chunk-46GOWZT4.js} +20 -20
- package/dist/{chunk-BNRE7TSX.js → chunk-5KYJAUZV.js} +3 -3
- package/dist/chunk-6C5PKREN.js +467 -0
- package/dist/{chunk-447MTPZF.js → chunk-6ZMLNEHB.js} +3 -3
- package/dist/chunk-BPZQT5N5.js +25652 -0
- package/dist/chunk-BQM7MBPS.js +1380 -0
- package/dist/{chunk-ZRFKGPIU.js → chunk-C52OQNNY.js} +20 -20
- package/dist/chunk-C7HGQF4Y.js +25652 -0
- package/dist/chunk-CAHNZGGK.js +25656 -0
- package/dist/{chunk-FL3CH3ET.js → chunk-CK7R6UHE.js} +51 -27
- package/dist/chunk-D36RPWB7.js +25652 -0
- package/dist/{chunk-36NM2B4C.js → chunk-DJK2UPFH.js} +63 -93
- package/dist/chunk-DM7FTF7W.js +4929 -0
- package/dist/chunk-DMD24UFZ.js +5101 -0
- package/dist/{chunk-36XNMIHA.js → chunk-DXZGPUAF.js} +20 -20
- package/dist/chunk-F46WB5IL.js +5087 -0
- package/dist/chunk-F5QG5SQH.js +5087 -0
- package/dist/{chunk-JGEVQZDR.js → chunk-FLQ5FLHW.js} +13 -16
- package/dist/chunk-H7GP733U.js +5087 -0
- package/dist/{chunk-OZSQLOV6.js → chunk-HHBXWB5U.js} +415 -19
- package/dist/{chunk-D24JY75H.js → chunk-IMXS4N6W.js} +3 -3
- package/dist/{chunk-6PVBV6ZP.js → chunk-JNMDD7JY.js} +3 -3
- package/dist/chunk-JTV5LA47.js +1519 -0
- package/dist/chunk-KV6G7NZX.js +1519 -0
- package/dist/chunk-MU5MEBIK.js +1519 -0
- package/dist/chunk-NLT5MC7X.js +465 -0
- package/dist/{chunk-GTFZZUXX.js → chunk-NVLYIM4J.js} +51 -27
- package/dist/{chunk-6G5SXLXC.js → chunk-NZY2BIZH.js} +63 -93
- package/dist/chunk-O42L6G67.js +1519 -0
- package/dist/chunk-OCNERGGM.js +4891 -0
- package/dist/chunk-OJSNHONE.js +1519 -0
- package/dist/{chunk-2TAZJWJN.js → chunk-OWL3QVH7.js} +18 -0
- package/dist/{chunk-P3HVY2HS.js → chunk-OWTLNV4Q.js} +382 -7
- package/dist/chunk-PCNYEP6T.js +4891 -0
- package/dist/{chunk-YL3Z5KPR.js → chunk-PI4AQ4Z6.js} +438 -15
- package/dist/chunk-PN3EGTCA.js +194 -0
- package/dist/chunk-Q37UKNRC.js +1519 -0
- package/dist/chunk-QXTC6J7H.js +5087 -0
- package/dist/{chunk-SPBQVNDI.js → chunk-RKERL5LZ.js} +25 -21
- package/dist/chunk-RVBK2IOX.js +25652 -0
- package/dist/chunk-SAKODCZ5.js +4891 -0
- package/dist/{chunk-XV4TU65E.js → chunk-SALGFC5L.js} +51 -27
- package/dist/chunk-STGWZ2MS.js +1519 -0
- package/dist/chunk-UY3ZVQDP.js +25652 -0
- package/dist/chunk-V6OSD62M.js +5087 -0
- package/dist/chunk-VP6YAHX4.js +1519 -0
- package/dist/chunk-WDYJOEAI.js +5087 -0
- package/dist/chunk-WEAFQNOS.js +195 -0
- package/dist/chunk-XKUSAZGP.js +5087 -0
- package/dist/chunk-Z6K5FKAB.js +548 -0
- package/dist/chunk-ZGE3XAXY.js +1519 -0
- package/dist/chunk-ZGYVXYQQ.js +3296 -0
- package/dist/cli-agent-7TB2BWS6.js +2370 -0
- package/dist/cli-agent-AKXFFST2.js +2370 -0
- package/dist/cli-agent-DZTKLITB.js +2357 -0
- package/dist/cli-agent-FOF7PFEP.js +2357 -0
- package/dist/cli-agent-H74M2ZYN.js +2357 -0
- package/dist/cli-agent-HORWVPHB.js +2370 -0
- package/dist/cli-agent-HSZT6SKF.js +2423 -0
- package/dist/cli-agent-JLUQ4ZU6.js +2424 -0
- package/dist/cli-agent-MVCDH4HV.js +2370 -0
- package/dist/cli-agent-NZXOEPJ2.js +2357 -0
- package/dist/cli-agent-PADN3QRC.js +2357 -0
- package/dist/cli-agent-QAYEX3BE.js +2441 -0
- package/dist/cli-agent-QT64DT5J.js +2370 -0
- package/dist/cli-agent-TFL2M6UK.js +2424 -0
- package/dist/cli-agent-UIKXATTD.js +2357 -0
- package/dist/cli-agent-UJN6FYTO.js +2370 -0
- package/dist/cli-agent-VIQAYVY4.js +2357 -0
- package/dist/cli-agent-WNWFVOFM.js +2370 -0
- package/dist/cli-agent-XBQX67VJ.js +2423 -0
- package/dist/cli-agent-ZLSC6FF4.js +2357 -0
- package/dist/cli-serve-2IL5DTEY.js +153 -0
- package/dist/cli-serve-47N5UKKW.js +153 -0
- package/dist/cli-serve-4XGZFUV2.js +140 -0
- package/dist/cli-serve-6OT3UEAN.js +140 -0
- package/dist/cli-serve-7L6EY5UH.js +153 -0
- package/dist/cli-serve-BDGOOOKQ.js +260 -0
- package/dist/cli-serve-BFNIW2LF.js +153 -0
- package/dist/cli-serve-C7MN6U5Q.js +153 -0
- package/dist/cli-serve-CR3OY3IM.js +153 -0
- package/dist/cli-serve-DAJFRWQ7.js +153 -0
- package/dist/cli-serve-FW6FHFW4.js +153 -0
- package/dist/cli-serve-GEEOQS77.js +153 -0
- package/dist/cli-serve-H562I3ZK.js +153 -0
- package/dist/cli-serve-HDQZF4C4.js +153 -0
- package/dist/cli-serve-LICAOMEB.js +140 -0
- package/dist/cli-serve-LLGYLWFS.js +153 -0
- package/dist/cli-serve-N3OISDNB.js +153 -0
- package/dist/cli-serve-TIZ27EVR.js +153 -0
- package/dist/cli-serve-TUNI2RCN.js +153 -0
- package/dist/cli-serve-WNOZMAWD.js +153 -0
- package/dist/cli-validate-Z726VJCN.js +150 -0
- package/dist/cli.js +4 -4
- package/dist/connection-manager-KAWEUWUR.js +9 -0
- package/dist/dashboard/app.js +9 -3
- package/dist/dashboard/components/knowledge-link.js +15 -0
- package/dist/dashboard/components/settings-help.js +4 -2
- package/dist/dashboard/docs/agent-deployment.html +33 -1
- package/dist/dashboard/docs/settings-network.html +321 -0
- package/dist/dashboard/docs/settings-security.html +347 -0
- package/dist/dashboard/docs/settings-tool-security.html +176 -0
- package/dist/dashboard/docs/settings.html +36 -16
- package/dist/dashboard/pages/agent-detail/deployment.js +39 -6
- package/dist/dashboard/pages/agent-detail/tools.js +10 -0
- package/dist/dashboard/pages/database-access.js +4 -3
- package/dist/dashboard/pages/settings.js +174 -37
- package/dist/dashboard/pages/task-pipeline.js +400 -843
- package/dist/db-adapter-2T56ORSD.js +7 -0
- package/dist/db-adapter-IRHOUMVC.js +7 -0
- package/dist/index.js +41 -41
- package/dist/microsoft-VREAZ7M2.js +3955 -0
- package/dist/routes-3MMLQTB6.js +90 -0
- package/dist/routes-4ZUIJ4HE.js +90 -0
- package/dist/routes-5MXHKKH4.js +90 -0
- package/dist/routes-64NJFK3B.js +90 -0
- package/dist/routes-6AKQ2LBV.js +90 -0
- package/dist/routes-CRRBUDO4.js +90 -0
- package/dist/routes-DIAF3MC3.js +90 -0
- package/dist/routes-KMUNU6CY.js +90 -0
- package/dist/routes-LRRLXIZR.js +90 -0
- package/dist/routes-N647AJYG.js +90 -0
- package/dist/routes-SSSELAAR.js +90 -0
- package/dist/routes-STERVGKJ.js +90 -0
- package/dist/routes-ZEZZACZP.js +90 -0
- package/dist/runtime-5EQN4GFM.js +45 -0
- package/dist/runtime-5LP7PUD4.js +45 -0
- package/dist/runtime-6BULDBR3.js +45 -0
- package/dist/runtime-6YEENDN3.js +45 -0
- package/dist/runtime-7LQFRG3B.js +45 -0
- package/dist/runtime-AMXJU2MB.js +45 -0
- package/dist/runtime-D6WSE7FG.js +45 -0
- package/dist/runtime-EYVN7NFJ.js +45 -0
- package/dist/runtime-F6RPWQVW.js +45 -0
- package/dist/runtime-FYMJURFC.js +45 -0
- package/dist/runtime-JRNBL4O4.js +45 -0
- package/dist/runtime-OM2NIBMI.js +45 -0
- package/dist/runtime-QWPVD7CY.js +45 -0
- package/dist/runtime-YLIIPTE4.js +45 -0
- package/dist/runtime-YU6P22CG.js +45 -0
- package/dist/screen-unlock-4RPZBHOI.js +118 -0
- package/dist/server-AMCSXINC.js +28 -0
- package/dist/server-CU6LVQS4.js +28 -0
- package/dist/server-DFYGH2CV.js +28 -0
- package/dist/server-EELWOC3X.js +28 -0
- package/dist/server-EN5E2OWQ.js +28 -0
- package/dist/server-GW2HYJYI.js +28 -0
- package/dist/server-J25NCRWJ.js +28 -0
- package/dist/server-JDGNOTFV.js +28 -0
- package/dist/server-NE5HD5DJ.js +28 -0
- package/dist/server-NQOT7W77.js +28 -0
- package/dist/server-PWE5PQTR.js +28 -0
- package/dist/server-Q2Q32H2B.js +28 -0
- package/dist/server-Q77ME7TL.js +28 -0
- package/dist/server-WLLH4WST.js +28 -0
- package/dist/server-WTUJ2O3F.js +28 -0
- package/dist/server-X4CJTHHF.js +28 -0
- package/dist/server-XK3ILCJC.js +28 -0
- package/dist/server-ZRD3NDJE.js +28 -0
- package/dist/setup-44VBAO4J.js +20 -0
- package/dist/setup-4ONNQBWB.js +20 -0
- package/dist/setup-4OSBXSCL.js +20 -0
- package/dist/setup-4QFGRBLZ.js +20 -0
- package/dist/setup-6766SGAR.js +20 -0
- package/dist/setup-AYY24DKM.js +20 -0
- package/dist/setup-B34N4HPU.js +20 -0
- package/dist/setup-E2YLC2EY.js +20 -0
- package/dist/setup-ER6NXTY5.js +20 -0
- package/dist/setup-H2AGCBW5.js +20 -0
- package/dist/setup-ICOZRKCX.js +20 -0
- package/dist/setup-JFTJH7UF.js +20 -0
- package/dist/setup-PRFNI6YW.js +20 -0
- package/dist/setup-RAHBMYHE.js +20 -0
- package/dist/setup-TXPR5UQX.js +20 -0
- package/dist/setup-XCJMELVU.js +20 -0
- package/dist/setup-XIYEIFVK.js +20 -0
- package/dist/setup-Z4PZSHBI.js +20 -0
- package/dist/skills-FR7I5V7H.js +16 -0
- package/dist/skills-HCVBA6PK.js +16 -0
- package/dist/system-prompts-TM7OA32C.js +913 -0
- package/dist/task-queue-O7IVZYUO.js +9 -0
- package/dist/transport-encryption-2T7PIXKG.js +25 -0
- package/logs/cloudflared-error.log +61 -0
- package/logs/cloudflared-out.log +0 -0
- package/logs/enterprise-error.log +0 -0
- package/logs/enterprise-out.log +3 -0
- package/logs/fola-error.log +0 -0
- package/logs/fola-out.log +0 -0
- package/logs/john-error.log +8 -0
- package/logs/john-out.log +0 -0
- package/package.json +31 -3
- package/src/agent-tools/tool-resolver.ts +50 -61
- package/src/agent-tools/tools/enterprise-database.ts +5 -5
- package/src/agent-tools/tools/local/dependency-manager.ts +2 -2
- package/src/agent-tools/tools/microsoft/graph-api.ts +137 -26
- package/src/agent-tools/tools/microsoft/outlook-mail.ts +392 -100
- package/src/agent-tools/tools/microsoft/teams.ts +267 -48
- package/src/auth/routes.ts +4 -4
- package/src/cli-agent.ts +108 -8
- package/src/cli-serve.ts +140 -0
- package/src/dashboard/app.js +9 -3
- package/src/dashboard/components/knowledge-link.js +15 -0
- package/src/dashboard/components/settings-help.js +4 -2
- package/src/dashboard/docs/agent-deployment.html +33 -1
- package/src/dashboard/docs/settings-network.html +321 -0
- package/src/dashboard/docs/settings-security.html +347 -0
- package/src/dashboard/docs/settings-tool-security.html +176 -0
- package/src/dashboard/docs/settings.html +36 -16
- package/src/dashboard/pages/agent-detail/deployment.js +39 -6
- package/src/dashboard/pages/agent-detail/tools.js +10 -0
- package/src/dashboard/pages/database-access.js +4 -3
- package/src/dashboard/pages/settings.js +174 -37
- package/src/dashboard/pages/task-pipeline.js +400 -843
- package/src/database-access/agent-tools.ts +78 -63
- package/src/database-access/connection-manager.ts +13 -2
- package/src/database-access/routes.ts +13 -1
- package/src/db/adapter.ts +1 -0
- package/src/engine/agent-memory.ts +2 -1
- package/src/engine/agent-notify.ts +50 -0
- package/src/engine/agent-routes.ts +257 -4
- package/src/engine/db-adapter.ts +16 -0
- package/src/engine/lifecycle.ts +4 -0
- package/src/engine/routes.ts +4 -3
- package/src/engine/screen-unlock.ts +136 -0
- package/src/engine/skills/database-access.ts +78 -0
- package/src/engine/skills/index.ts +3 -2
- package/src/engine/skills.ts +2 -0
- package/src/engine/task-queue-routes.ts +18 -0
- package/src/engine/task-queue.ts +15 -2
- package/src/middleware/transport-encryption.ts +1 -4
- package/src/runtime/agent-loop.ts +4 -0
- package/src/runtime/index.ts +15 -6
- package/src/server.ts +14 -1
- package/src/system-prompts/google/index.ts +1 -2
- package/src/system-prompts/index.ts +1 -1
- package/src/system-prompts/microsoft/contacts.ts +34 -0
- package/src/system-prompts/microsoft/excel.ts +52 -0
- package/src/system-prompts/microsoft/index.ts +31 -0
- package/src/system-prompts/microsoft/onedrive.ts +41 -0
- package/src/system-prompts/microsoft/onenote.ts +36 -0
- package/src/system-prompts/microsoft/outlook-calendar.ts +37 -0
- package/src/system-prompts/microsoft/outlook-mail.ts +46 -0
- package/src/system-prompts/microsoft/planner.ts +37 -0
- package/src/system-prompts/microsoft/powerbi.ts +38 -0
- package/src/system-prompts/microsoft/powerpoint.ts +35 -0
- package/src/system-prompts/microsoft/sharepoint.ts +44 -0
- package/src/system-prompts/microsoft/teams.ts +49 -0
- package/src/system-prompts/microsoft/todo.ts +37 -0
- package/src/types/hono-env.ts +4 -0
- package/.github/CODEOWNERS +0 -23
- package/.github/workflows/publish-community-skills.yml +0 -121
- package/.github/workflows/validate-community-skills.yml +0 -172
- package/agriculture_southwest_nigeria_research.txt +0 -10
- package/boa_credit_cards_research.txt +0 -10
- package/customer_support_research_feb2026.txt +0 -10
- package/dist/agent-tools-LRA7PPXG.js +0 -13922
- package/dist/agent-tools-VAU5DOQB.js +0 -13910
- package/dist/agent-tools-VWV7OWXU.js +0 -13922
- package/dist/chunk-2Z7MWTCX.js +0 -4977
- package/dist/chunk-3T4XU3VV.js +0 -5010
- package/dist/chunk-445QM4NX.js +0 -5061
- package/dist/chunk-5TW3Y7DJ.js +0 -1519
- package/dist/chunk-6I7VY3LT.js +0 -5060
- package/dist/chunk-6W5EK3UP.js +0 -4977
- package/dist/chunk-AQMSHJQT.js +0 -5069
- package/dist/chunk-ASSQW7HX.js +0 -5051
- package/dist/chunk-CIN27FGC.js +0 -5037
- package/dist/chunk-CMXY3NUB.js +0 -4977
- package/dist/chunk-DRLMRUDP.js +0 -5052
- package/dist/chunk-EHI7Z446.js +0 -1519
- package/dist/chunk-FEAILFAQ.js +0 -1519
- package/dist/chunk-GA3PYBZL.js +0 -1519
- package/dist/chunk-GWX63G5J.js +0 -1519
- package/dist/chunk-HHMZ4UY6.js +0 -1519
- package/dist/chunk-HVQMNF7E.js +0 -4921
- package/dist/chunk-HXM7F3YN.js +0 -1519
- package/dist/chunk-K6NGOUXG.js +0 -5060
- package/dist/chunk-KPG5WINJ.js +0 -4977
- package/dist/chunk-LBCUBYDL.js +0 -1519
- package/dist/chunk-LIRQSWLR.js +0 -5014
- package/dist/chunk-LRCKO5KE.js +0 -1519
- package/dist/chunk-M7XL3DJD.js +0 -5069
- package/dist/chunk-MHJULEIQ.js +0 -1519
- package/dist/chunk-MJGGW6MC.js +0 -106
- package/dist/chunk-MMYBDHDB.js +0 -4921
- package/dist/chunk-MQT5FXKD.js +0 -1519
- package/dist/chunk-OIMPEQF5.js +0 -4977
- package/dist/chunk-OOU7JUYE.js +0 -542
- package/dist/chunk-OW4GLBHP.js +0 -1519
- package/dist/chunk-Q4K4MMLU.js +0 -4977
- package/dist/chunk-RUK4CRPF.js +0 -1519
- package/dist/chunk-T7H65XQY.js +0 -1519
- package/dist/chunk-TQVFWG57.js +0 -5064
- package/dist/chunk-UEPK3IMC.js +0 -1519
- package/dist/chunk-VUWTXJH6.js +0 -1519
- package/dist/chunk-WCPGGSAD.js +0 -1519
- package/dist/chunk-WO63NZOJ.js +0 -1519
- package/dist/chunk-YPJDRVUM.js +0 -5064
- package/dist/chunk-ZROMH5DL.js +0 -4921
- 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 || [
|
|
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 || '
|
|
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) {
|
|
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: '
|
|
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: '
|
|
622
|
-
h('div', { style: { fontSize: 11, color: 'var(--text-muted)', marginTop: 2 } }, '
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|