agim-cli 1.2.143 → 1.2.147
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 +186 -0
- package/dist/cli-ui/setup-llm.d.ts.map +1 -1
- package/dist/cli-ui/setup-llm.js +3 -1
- package/dist/cli-ui/setup-llm.js.map +1 -1
- package/dist/cli-ui/tui/app.d.ts +1 -0
- package/dist/cli-ui/tui/app.d.ts.map +1 -1
- package/dist/cli-ui/tui/app.js +24 -9
- package/dist/cli-ui/tui/app.js.map +1 -1
- package/dist/cli-ui/tui/markdown.d.ts.map +1 -1
- package/dist/cli-ui/tui/markdown.js +12 -3
- package/dist/cli-ui/tui/markdown.js.map +1 -1
- package/dist/cli.js +23 -3
- package/dist/cli.js.map +1 -1
- package/dist/core/access-token.d.ts.map +1 -1
- package/dist/core/access-token.js +4 -2
- package/dist/core/access-token.js.map +1 -1
- package/dist/core/circuit-breaker.d.ts +28 -0
- package/dist/core/circuit-breaker.d.ts.map +1 -1
- package/dist/core/circuit-breaker.js +45 -0
- package/dist/core/circuit-breaker.js.map +1 -1
- package/dist/core/intent.d.ts.map +1 -1
- package/dist/core/intent.js +3 -1
- package/dist/core/intent.js.map +1 -1
- package/dist/core/llm/agent-loop.d.ts +9 -1
- package/dist/core/llm/agent-loop.d.ts.map +1 -1
- package/dist/core/llm/agent-loop.js +80 -1
- package/dist/core/llm/agent-loop.js.map +1 -1
- package/dist/core/llm/anthropic-provider.d.ts.map +1 -1
- package/dist/core/llm/anthropic-provider.js +18 -4
- package/dist/core/llm/anthropic-provider.js.map +1 -1
- package/dist/core/llm/hallucination-detector.d.ts +33 -0
- package/dist/core/llm/hallucination-detector.d.ts.map +1 -0
- package/dist/core/llm/hallucination-detector.js +103 -0
- package/dist/core/llm/hallucination-detector.js.map +1 -0
- package/dist/core/llm/imhub-dispatcher.d.ts.map +1 -1
- package/dist/core/llm/imhub-dispatcher.js +7 -0
- package/dist/core/llm/imhub-dispatcher.js.map +1 -1
- package/dist/core/llm/provider-base.d.ts +9 -0
- package/dist/core/llm/provider-base.d.ts.map +1 -1
- package/dist/core/llm/provider-base.js.map +1 -1
- package/dist/core/memory-distill.d.ts.map +1 -1
- package/dist/core/memory-distill.js +18 -3
- package/dist/core/memory-distill.js.map +1 -1
- package/dist/core/memory.d.ts +14 -0
- package/dist/core/memory.d.ts.map +1 -1
- package/dist/core/memory.js +39 -0
- package/dist/core/memory.js.map +1 -1
- package/dist/core/message-sink.d.ts +6 -0
- package/dist/core/message-sink.d.ts.map +1 -1
- package/dist/core/message-sink.js +18 -3
- package/dist/core/message-sink.js.map +1 -1
- package/dist/core/outbox.d.ts +30 -2
- package/dist/core/outbox.d.ts.map +1 -1
- package/dist/core/outbox.js +102 -10
- package/dist/core/outbox.js.map +1 -1
- package/dist/core/reminders.d.ts.map +1 -1
- package/dist/core/reminders.js +11 -1
- package/dist/core/reminders.js.map +1 -1
- package/dist/core/router.d.ts.map +1 -1
- package/dist/core/router.js +16 -4
- package/dist/core/router.js.map +1 -1
- package/dist/core/schedule.d.ts +18 -0
- package/dist/core/schedule.d.ts.map +1 -1
- package/dist/core/schedule.js +80 -17
- package/dist/core/schedule.js.map +1 -1
- package/dist/core/sensitive-paths.d.ts.map +1 -1
- package/dist/core/sensitive-paths.js +53 -9
- package/dist/core/sensitive-paths.js.map +1 -1
- package/dist/core/types.d.ts +6 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/plugins/agents/native/index.d.ts +47 -8
- package/dist/plugins/agents/native/index.d.ts.map +1 -1
- package/dist/plugins/agents/native/index.js +253 -102
- package/dist/plugins/agents/native/index.js.map +1 -1
- package/dist/plugins/agents/native/tool-registry.d.ts +33 -0
- package/dist/plugins/agents/native/tool-registry.d.ts.map +1 -0
- package/dist/plugins/agents/native/tool-registry.js +82 -0
- package/dist/plugins/agents/native/tool-registry.js.map +1 -0
- package/dist/plugins/messengers/dingtalk/dingtalk-client.d.ts.map +1 -1
- package/dist/plugins/messengers/dingtalk/dingtalk-client.js +11 -11
- package/dist/plugins/messengers/dingtalk/dingtalk-client.js.map +1 -1
- package/dist/plugins/messengers/feishu/feishu-adapter.d.ts.map +1 -1
- package/dist/plugins/messengers/feishu/feishu-adapter.js +9 -5
- package/dist/plugins/messengers/feishu/feishu-adapter.js.map +1 -1
- package/dist/plugins/messengers/wechat/ilink-adapter.d.ts.map +1 -1
- package/dist/plugins/messengers/wechat/ilink-adapter.js +11 -1
- package/dist/plugins/messengers/wechat/ilink-adapter.js.map +1 -1
- package/dist/web/public/assets/{a2a-DczMMkbl.js → a2a-Cll3P4QN.js} +2 -2
- package/dist/web/public/assets/{a2a-DczMMkbl.js.map → a2a-Cll3P4QN.js.map} +1 -1
- package/dist/web/public/assets/{activity-cbLHkzca.js → activity-B7T7YFlD.js} +2 -2
- package/dist/web/public/assets/{activity-cbLHkzca.js.map → activity-B7T7YFlD.js.map} +1 -1
- package/dist/web/public/assets/{admins-C-YsGMj7.js → admins-CN7P018S.js} +2 -2
- package/dist/web/public/assets/{admins-C-YsGMj7.js.map → admins-CN7P018S.js.map} +1 -1
- package/dist/web/public/assets/{agents-BWfov_1-.js → agents-Bqgq7GBF.js} +2 -2
- package/dist/web/public/assets/{agents-BWfov_1-.js.map → agents-Bqgq7GBF.js.map} +1 -1
- package/dist/web/public/assets/{approvals-HSssmXKS.js → approvals-C8IUJQ_A.js} +2 -2
- package/dist/web/public/assets/{approvals-HSssmXKS.js.map → approvals-C8IUJQ_A.js.map} +1 -1
- package/dist/web/public/assets/{arrow-down-BXvC8Al2.js → arrow-down-SLWKqtDc.js} +2 -2
- package/dist/web/public/assets/{arrow-down-BXvC8Al2.js.map → arrow-down-SLWKqtDc.js.map} +1 -1
- package/dist/web/public/assets/{arrow-up-63xELY5Q.js → arrow-up-BOADc9ce.js} +2 -2
- package/dist/web/public/assets/{arrow-up-63xELY5Q.js.map → arrow-up-BOADc9ce.js.map} +1 -1
- package/dist/web/public/assets/{asks-COLEFOvK.js → asks-C-j-DypC.js} +2 -2
- package/dist/web/public/assets/{asks-COLEFOvK.js.map → asks-C-j-DypC.js.map} +1 -1
- package/dist/web/public/assets/{audit-D4ZEiZub.js → audit-DQb-RuXh.js} +2 -2
- package/dist/web/public/assets/{audit-D4ZEiZub.js.map → audit-DQb-RuXh.js.map} +1 -1
- package/dist/web/public/assets/{bell-Cg2Bvv06.js → bell-CV88-ul6.js} +2 -2
- package/dist/web/public/assets/{bell-Cg2Bvv06.js.map → bell-CV88-ul6.js.map} +1 -1
- package/dist/web/public/assets/{bgjobs-CEjCzwtd.js → bgjobs-CDrK0d-W.js} +2 -2
- package/dist/web/public/assets/{bgjobs-CEjCzwtd.js.map → bgjobs-CDrK0d-W.js.map} +1 -1
- package/dist/web/public/assets/{brain-euvl6F6C.js → brain-B7HtSOQU.js} +2 -2
- package/dist/web/public/assets/{brain-euvl6F6C.js.map → brain-B7HtSOQU.js.map} +1 -1
- package/dist/web/public/assets/{briefcase-DPWLbCnA.js → briefcase-mdzuIa__.js} +2 -2
- package/dist/web/public/assets/{briefcase-DPWLbCnA.js.map → briefcase-mdzuIa__.js.map} +1 -1
- package/dist/web/public/assets/{browser-ponyfill-BUutOaRz.js → browser-ponyfill-DBWdeCTC.js} +2 -2
- package/dist/web/public/assets/{browser-ponyfill-BUutOaRz.js.map → browser-ponyfill-DBWdeCTC.js.map} +1 -1
- package/dist/web/public/assets/{chat-Dz9kfaxH.js → chat-CSjtY2rN.js} +3 -3
- package/dist/web/public/assets/{chat-Dz9kfaxH.js.map → chat-CSjtY2rN.js.map} +1 -1
- package/dist/web/public/assets/{chevron-left-BeIh5thq.js → chevron-left-uSfPn636.js} +2 -2
- package/dist/web/public/assets/{chevron-left-BeIh5thq.js.map → chevron-left-uSfPn636.js.map} +1 -1
- package/dist/web/public/assets/{chevron-right-uP_l9MMb.js → chevron-right-CtelqacW.js} +2 -2
- package/dist/web/public/assets/{chevron-right-uP_l9MMb.js.map → chevron-right-CtelqacW.js.map} +1 -1
- package/dist/web/public/assets/{circle-check-CewnjFgv.js → circle-check-8dbL-u7O.js} +2 -2
- package/dist/web/public/assets/{circle-check-CewnjFgv.js.map → circle-check-8dbL-u7O.js.map} +1 -1
- package/dist/web/public/assets/{circle-check-big-C2RTc48c.js → circle-check-big-D8-svk9a.js} +2 -2
- package/dist/web/public/assets/{circle-check-big-C2RTc48c.js.map → circle-check-big-D8-svk9a.js.map} +1 -1
- package/dist/web/public/assets/{circle-x-Ccg1HyV-.js → circle-x-rUxzIz5P.js} +2 -2
- package/dist/web/public/assets/{circle-x-Ccg1HyV-.js.map → circle-x-rUxzIz5P.js.map} +1 -1
- package/dist/web/public/assets/{clock-qxbYSynv.js → clock-CG5dlBGB.js} +2 -2
- package/dist/web/public/assets/{clock-qxbYSynv.js.map → clock-CG5dlBGB.js.map} +1 -1
- package/dist/web/public/assets/{confirm-dialog-DmJq4Td9.js → confirm-dialog-DlUsSur3.js} +2 -2
- package/dist/web/public/assets/{confirm-dialog-DmJq4Td9.js.map → confirm-dialog-DlUsSur3.js.map} +1 -1
- package/dist/web/public/assets/{copy-DxSHRdbc.js → copy-DnC76wFT.js} +2 -2
- package/dist/web/public/assets/{copy-DxSHRdbc.js.map → copy-DnC76wFT.js.map} +1 -1
- package/dist/web/public/assets/{data-table-S7rIjwdO.js → data-table-DswkWUfG.js} +2 -2
- package/dist/web/public/assets/{data-table-S7rIjwdO.js.map → data-table-DswkWUfG.js.map} +1 -1
- package/dist/web/public/assets/dialog-Ceo4YuXy.js +6 -0
- package/dist/web/public/assets/dialog-Ceo4YuXy.js.map +1 -0
- package/dist/web/public/assets/{download-OhsGtnO-.js → download-DF-46tS4.js} +2 -2
- package/dist/web/public/assets/{download-OhsGtnO-.js.map → download-DF-46tS4.js.map} +1 -1
- package/dist/web/public/assets/{email-C1-HxWLF.js → email-CZee26-_.js} +3 -3
- package/dist/web/public/assets/{email-C1-HxWLF.js.map → email-CZee26-_.js.map} +1 -1
- package/dist/web/public/assets/{empty-state-C-qjOHyu.js → empty-state-D9Hi0Atm.js} +2 -2
- package/dist/web/public/assets/{empty-state-C-qjOHyu.js.map → empty-state-D9Hi0Atm.js.map} +1 -1
- package/dist/web/public/assets/{external-link-DRVp9-lb.js → external-link-D64iZa9P.js} +2 -2
- package/dist/web/public/assets/{external-link-DRVp9-lb.js.map → external-link-D64iZa9P.js.map} +1 -1
- package/dist/web/public/assets/{eye-CFhg5BTa.js → eye-sY6WZb7D.js} +2 -2
- package/dist/web/public/assets/{eye-CFhg5BTa.js.map → eye-sY6WZb7D.js.map} +1 -1
- package/dist/web/public/assets/{facts-CGaLWhzi.js → facts-B7bGGwvi.js} +2 -2
- package/dist/web/public/assets/{facts-CGaLWhzi.js.map → facts-B7bGGwvi.js.map} +1 -1
- package/dist/web/public/assets/{goals-C-dJANmn.js → goals-BfQbsvZv.js} +2 -2
- package/dist/web/public/assets/{goals-C-dJANmn.js.map → goals-BfQbsvZv.js.map} +1 -1
- package/dist/web/public/assets/{health-CWcti5h3.js → health-Ba_mY0Ts.js} +2 -2
- package/dist/web/public/assets/{health-CWcti5h3.js.map → health-Ba_mY0Ts.js.map} +1 -1
- package/dist/web/public/assets/{heart-pulse-DmGhKR2W.js → heart-pulse-BjikOVwU.js} +2 -2
- package/dist/web/public/assets/{heart-pulse-DmGhKR2W.js.map → heart-pulse-BjikOVwU.js.map} +1 -1
- package/dist/web/public/assets/{heartbeat-kLoGBNCo.js → heartbeat-BM8LlPes.js} +2 -2
- package/dist/web/public/assets/{heartbeat-kLoGBNCo.js.map → heartbeat-BM8LlPes.js.map} +1 -1
- package/dist/web/public/assets/{hot-BITDoax1.js → hot-BtuLL6n8.js} +2 -2
- package/dist/web/public/assets/{hot-BITDoax1.js.map → hot-BtuLL6n8.js.map} +1 -1
- package/dist/web/public/assets/index-DEWFfW_Z.js +199 -0
- package/dist/web/public/assets/index-DEWFfW_Z.js.map +1 -0
- package/dist/web/public/assets/{installed-Co9WrtQ7.js → installed-Xr8p31ij.js} +2 -2
- package/dist/web/public/assets/{installed-Co9WrtQ7.js.map → installed-Xr8p31ij.js.map} +1 -1
- package/dist/web/public/assets/{jobs-hdHhBEvi.js → jobs-Ddy81Udm.js} +2 -2
- package/dist/web/public/assets/{jobs-hdHhBEvi.js.map → jobs-Ddy81Udm.js.map} +1 -1
- package/dist/web/public/assets/{layout-CQtbOBag.js → layout-BL74fT-L.js} +2 -2
- package/dist/web/public/assets/{layout-CQtbOBag.js.map → layout-BL74fT-L.js.map} +1 -1
- package/dist/web/public/assets/{layout-bDMXIKIR.js → layout-Bn2qUxcK.js} +2 -2
- package/dist/web/public/assets/{layout-bDMXIKIR.js.map → layout-Bn2qUxcK.js.map} +1 -1
- package/dist/web/public/assets/{layout-BMXC1Uh1.js → layout-Bp4SAA8_.js} +2 -2
- package/dist/web/public/assets/{layout-BMXC1Uh1.js.map → layout-Bp4SAA8_.js.map} +1 -1
- package/dist/web/public/assets/{layout-CysVsySh.js → layout-CZ9pGnW8.js} +2 -2
- package/dist/web/public/assets/{layout-CysVsySh.js.map → layout-CZ9pGnW8.js.map} +1 -1
- package/dist/web/public/assets/{layout-CyBGneZ9.js → layout-pasFRkKV.js} +2 -2
- package/dist/web/public/assets/{layout-CyBGneZ9.js.map → layout-pasFRkKV.js.map} +1 -1
- package/dist/web/public/assets/llm-yp7b5xxL.js +7 -0
- package/dist/web/public/assets/llm-yp7b5xxL.js.map +1 -0
- package/dist/web/public/assets/{loader-circle-9VUMGitw.js → loader-circle-Bbw4pEyE.js} +2 -2
- package/dist/web/public/assets/{loader-circle-9VUMGitw.js.map → loader-circle-Bbw4pEyE.js.map} +1 -1
- package/dist/web/public/assets/{map-pin-BXYvvHry.js → map-pin-DIXHUQgM.js} +2 -2
- package/dist/web/public/assets/{map-pin-BXYvvHry.js.map → map-pin-DIXHUQgM.js.map} +1 -1
- package/dist/web/public/assets/{mcp-BgLdlwSn.js → mcp-DyaljIM_.js} +2 -2
- package/dist/web/public/assets/{mcp-BgLdlwSn.js.map → mcp-DyaljIM_.js.map} +1 -1
- package/dist/web/public/assets/memos-Dkoc157i.js +12 -0
- package/dist/web/public/assets/memos-Dkoc157i.js.map +1 -0
- package/dist/web/public/assets/{messengers-7Phqea62.js → messengers-CcyGDeUI.js} +2 -2
- package/dist/web/public/assets/{messengers-7Phqea62.js.map → messengers-CcyGDeUI.js.map} +1 -1
- package/dist/web/public/assets/{mobile-CV5b6D2W.js → mobile-DqzIv4Xb.js} +2 -2
- package/dist/web/public/assets/{mobile-CV5b6D2W.js.map → mobile-DqzIv4Xb.js.map} +1 -1
- package/dist/web/public/assets/{native-agent-QvIa6LjE.js → native-agent-BQ7WaRGK.js} +2 -2
- package/dist/web/public/assets/{native-agent-QvIa6LjE.js.map → native-agent-BQ7WaRGK.js.map} +1 -1
- package/dist/web/public/assets/{network-BXhEjGhE.js → network-B_yUFAqC.js} +2 -2
- package/dist/web/public/assets/{network-BXhEjGhE.js.map → network-B_yUFAqC.js.map} +1 -1
- package/dist/web/public/assets/{outbox-DHQL7TQb.js → outbox-l8aVOZqO.js} +2 -2
- package/dist/web/public/assets/{outbox-DHQL7TQb.js.map → outbox-l8aVOZqO.js.map} +1 -1
- package/dist/web/public/assets/{pagination-VKuPb1Ot.js → pagination-BAKRGKa9.js} +2 -2
- package/dist/web/public/assets/{pagination-VKuPb1Ot.js.map → pagination-BAKRGKa9.js.map} +1 -1
- package/dist/web/public/assets/{persona-CWug2GLR.js → persona-D3VL9Rg1.js} +2 -2
- package/dist/web/public/assets/{persona-CWug2GLR.js.map → persona-D3VL9Rg1.js.map} +1 -1
- package/dist/web/public/assets/{plans-CZoEs5SY.js → plans-BBB5e9my.js} +2 -2
- package/dist/web/public/assets/{plans-CZoEs5SY.js.map → plans-BBB5e9my.js.map} +1 -1
- package/dist/web/public/assets/{play-CfSn5Vdl.js → play-7-Wd369f.js} +2 -2
- package/dist/web/public/assets/{play-CfSn5Vdl.js.map → play-7-Wd369f.js.map} +1 -1
- package/dist/web/public/assets/{plus-Z8l4CiqJ.js → plus-B0sfZy-j.js} +2 -2
- package/dist/web/public/assets/{plus-Z8l4CiqJ.js.map → plus-B0sfZy-j.js.map} +1 -1
- package/dist/web/public/assets/{policy-CutDSEPW.js → policy-BM1WRXH0.js} +2 -2
- package/dist/web/public/assets/{policy-CutDSEPW.js.map → policy-BM1WRXH0.js.map} +1 -1
- package/dist/web/public/assets/{qr-code-DgU5aiM6.js → qr-code-DcKs5fi3.js} +2 -2
- package/dist/web/public/assets/{qr-code-DgU5aiM6.js.map → qr-code-DcKs5fi3.js.map} +1 -1
- package/dist/web/public/assets/{react-Cb2sDjhD.js → react-DlP5eolq.js} +2 -2
- package/dist/web/public/assets/{react-Cb2sDjhD.js.map → react-DlP5eolq.js.map} +1 -1
- package/dist/web/public/assets/{refresh-ccw-D2CWiyU_.js → refresh-ccw-uNKeBeRl.js} +2 -2
- package/dist/web/public/assets/{refresh-ccw-D2CWiyU_.js.map → refresh-ccw-uNKeBeRl.js.map} +1 -1
- package/dist/web/public/assets/{reminders-Cb6Izedg.js → reminders-DHM8K0_O.js} +2 -2
- package/dist/web/public/assets/{reminders-Cb6Izedg.js.map → reminders-DHM8K0_O.js.map} +1 -1
- package/dist/web/public/assets/{save-DB0BDYTs.js → save-qwJa5_SA.js} +2 -2
- package/dist/web/public/assets/{save-DB0BDYTs.js.map → save-qwJa5_SA.js.map} +1 -1
- package/dist/web/public/assets/{schedules-8mSjE14D.js → schedules-Bcd0wbT4.js} +2 -2
- package/dist/web/public/assets/{schedules-8mSjE14D.js.map → schedules-Bcd0wbT4.js.map} +1 -1
- package/dist/web/public/assets/{search-Con69NhG.js → search-BUlzNWrj.js} +2 -2
- package/dist/web/public/assets/{search-Con69NhG.js.map → search-BUlzNWrj.js.map} +1 -1
- package/dist/web/public/assets/{search-B4fHilZ0.js → search-i1tP2maJ.js} +2 -2
- package/dist/web/public/assets/{search-B4fHilZ0.js.map → search-i1tP2maJ.js.map} +1 -1
- package/dist/web/public/assets/{security-BTe3zUg8.js → security-DgJyTT4g.js} +2 -2
- package/dist/web/public/assets/{security-BTe3zUg8.js.map → security-DgJyTT4g.js.map} +1 -1
- package/dist/web/public/assets/{service-C7SqcwfL.js → service-A0Hzear0.js} +2 -2
- package/dist/web/public/assets/{service-C7SqcwfL.js.map → service-A0Hzear0.js.map} +1 -1
- package/dist/web/public/assets/{shield-alert-CKFVsGgI.js → shield-alert-DrnN6fz_.js} +2 -2
- package/dist/web/public/assets/{shield-alert-CKFVsGgI.js.map → shield-alert-DrnN6fz_.js.map} +1 -1
- package/dist/web/public/assets/{status-badge-BSkpyN4D.js → status-badge-Ryzf96Pl.js} +2 -2
- package/dist/web/public/assets/{status-badge-BSkpyN4D.js.map → status-badge-Ryzf96Pl.js.map} +1 -1
- package/dist/web/public/assets/{subtasks-Bel-I1Sk.js → subtasks-Bzh3o3EF.js} +2 -2
- package/dist/web/public/assets/{subtasks-Bel-I1Sk.js.map → subtasks-Bzh3o3EF.js.map} +1 -1
- package/dist/web/public/assets/{table-CPn1MRcy.js → table-BbAOSyc8.js} +2 -2
- package/dist/web/public/assets/{table-CPn1MRcy.js.map → table-BbAOSyc8.js.map} +1 -1
- package/dist/web/public/assets/{topn-Ba3RjcK1.js → topn-DkhYw-Gp.js} +2 -2
- package/dist/web/public/assets/{topn-Ba3RjcK1.js.map → topn-DkhYw-Gp.js.map} +1 -1
- package/dist/web/public/assets/{trash-2-Dfov8aHD.js → trash-2-CA0cLpnU.js} +2 -2
- package/dist/web/public/assets/{trash-2-Dfov8aHD.js.map → trash-2-CA0cLpnU.js.map} +1 -1
- package/dist/web/public/assets/{use-background-tasks-BQrEeUwY.js → use-background-tasks-B64YjlA8.js} +2 -2
- package/dist/web/public/assets/{use-background-tasks-BQrEeUwY.js.map → use-background-tasks-B64YjlA8.js.map} +1 -1
- package/dist/web/public/assets/{use-event-stream-DgGpGKop.js → use-event-stream-I1lMFEfh.js} +2 -2
- package/dist/web/public/assets/{use-event-stream-DgGpGKop.js.map → use-event-stream-I1lMFEfh.js.map} +1 -1
- package/dist/web/public/assets/{use-llm-admin-DYekqogG.js → use-llm-admin-DY2axI4D.js} +2 -2
- package/dist/web/public/assets/{use-llm-admin-DYekqogG.js.map → use-llm-admin-DY2axI4D.js.map} +1 -1
- package/dist/web/public/assets/{use-memory-DbJ4pP2Z.js → use-memory-BYEjVWbU.js} +2 -2
- package/dist/web/public/assets/{use-memory-DbJ4pP2Z.js.map → use-memory-BYEjVWbU.js.map} +1 -1
- package/dist/web/public/assets/{use-observability-C2M6WZ9W.js → use-observability-Coj02yDo.js} +2 -2
- package/dist/web/public/assets/{use-observability-C2M6WZ9W.js.map → use-observability-Coj02yDo.js.map} +1 -1
- package/dist/web/public/assets/{use-settings-DMdaoWsB.js → use-settings-i1MhlkyC.js} +2 -2
- package/dist/web/public/assets/{use-settings-DMdaoWsB.js.map → use-settings-i1MhlkyC.js.map} +1 -1
- package/dist/web/public/assets/{use-workspace-BHG7h3jQ.js → use-workspace-DgEM35PY.js} +2 -2
- package/dist/web/public/assets/{use-workspace-BHG7h3jQ.js.map → use-workspace-DgEM35PY.js.map} +1 -1
- package/dist/web/public/assets/{useQuery-PdiC7-sY.js → useQuery-CY2iazjN.js} +2 -2
- package/dist/web/public/assets/{useQuery-PdiC7-sY.js.map → useQuery-CY2iazjN.js.map} +1 -1
- package/dist/web/public/assets/{vector-DnZM3OXU.js → vector-Ic76u2hY.js} +2 -2
- package/dist/web/public/assets/{vector-DnZM3OXU.js.map → vector-Ic76u2hY.js.map} +1 -1
- package/dist/web/public/assets/{viewer-Dz6k0YKp.js → viewer-BXbUN1Rl.js} +2 -2
- package/dist/web/public/assets/{viewer-Dz6k0YKp.js.map → viewer-BXbUN1Rl.js.map} +1 -1
- package/dist/web/public/assets/{workspace-BnXrWS3j.js → workspace-CUg0JPn6.js} +3 -3
- package/dist/web/public/assets/{workspace-BnXrWS3j.js.map → workspace-CUg0JPn6.js.map} +1 -1
- package/dist/web/public/assets/{workspaces-CSS_UBEi.js → workspaces-C-wb5FQj.js} +2 -2
- package/dist/web/public/assets/{workspaces-CSS_UBEi.js.map → workspaces-C-wb5FQj.js.map} +1 -1
- package/dist/web/public/assets/{x-DG-JKVw_.js → x-D1iSuoqg.js} +2 -2
- package/dist/web/public/assets/{x-DG-JKVw_.js.map → x-D1iSuoqg.js.map} +1 -1
- package/dist/web/public/index.html +2 -2
- package/dist/web/server.d.ts +23 -0
- package/dist/web/server.d.ts.map +1 -1
- package/dist/web/server.js +109 -20
- package/dist/web/server.js.map +1 -1
- package/package.json +3 -2
- package/dist/web/public/assets/dialog-bAIDaO-6.js +0 -6
- package/dist/web/public/assets/dialog-bAIDaO-6.js.map +0 -1
- package/dist/web/public/assets/index-O0BQoyzo.js +0 -199
- package/dist/web/public/assets/index-O0BQoyzo.js.map +0 -1
- package/dist/web/public/assets/llm-CPIRNQU2.js +0 -7
- package/dist/web/public/assets/llm-CPIRNQU2.js.map +0 -1
- package/dist/web/public/assets/memos-CfneX9DH.js +0 -12
- package/dist/web/public/assets/memos-CfneX9DH.js.map +0 -1
package/dist/web/server.d.ts
CHANGED
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
import { type IncomingMessage } from 'node:http';
|
|
2
|
+
declare function isTrustedLoopbackPeer(req: IncomingMessage): boolean;
|
|
3
|
+
/** Resolve whether the request's actor has admin role. Used to gate
|
|
4
|
+
* mutation + privileged-read endpoints so a stolen viewer-role token
|
|
5
|
+
* can't elevate to control plane (R13 A1).
|
|
6
|
+
*
|
|
7
|
+
* Trust order:
|
|
8
|
+
* 1. IMHUB_WEB_AUTH=off → admin (operator explicitly disabled auth)
|
|
9
|
+
* 2. Trusted loopback → admin (operator on the host)
|
|
10
|
+
* 3. Bearer token → token.role === 'admin'
|
|
11
|
+
* 4. Otherwise → not admin
|
|
12
|
+
*
|
|
13
|
+
* Note: when no token has been created yet (pre-bootstrap), the
|
|
14
|
+
* trusted-loopback branch still grants admin so the CLI bootstrap flow
|
|
15
|
+
* works. Disable it with IMHUB_TRUST_LOOPBACK=off. Reverse-proxied
|
|
16
|
+
* requests with Forwarded / X-Forwarded-* peer headers never qualify. */
|
|
17
|
+
declare function isRequestAdmin(req: IncomingMessage): boolean;
|
|
18
|
+
export declare const __webAuthForTesting: {
|
|
19
|
+
isTrustedLoopbackPeer: typeof isTrustedLoopbackPeer;
|
|
20
|
+
isRequestAdmin: typeof isRequestAdmin;
|
|
21
|
+
setTokenModule(mod: typeof import("../core/access-token.js") | null): void;
|
|
22
|
+
};
|
|
1
23
|
export declare function createSerialQueue(): (fn: () => Promise<void>) => void;
|
|
2
24
|
/**
|
|
3
25
|
* Start the web chat server
|
|
@@ -9,4 +31,5 @@ export declare function startWebServer(options: {
|
|
|
9
31
|
close: () => void;
|
|
10
32
|
port: number;
|
|
11
33
|
}>;
|
|
34
|
+
export {};
|
|
12
35
|
//# sourceMappingURL=server.d.ts.map
|
package/dist/web/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/web/server.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/web/server.ts"],"names":[],"mappings":"AAEA,OAAO,EAAgB,KAAK,eAAe,EAAuB,MAAM,WAAW,CAAA;AA4GnF,iBAAS,qBAAqB,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAQ5D;AAyID;;;;;;;;;;;;;0EAa0E;AAC1E,iBAAS,cAAc,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAcrD;AAWD,eAAO,MAAM,mBAAmB;;;wBAGV,cAAc,yBAAyB,CAAC,GAAG,IAAI,GAAG,IAAI;CAG3E,CAAA;AAED,wBAAgB,iBAAiB,IAAI,CAAC,EAAE,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,IAAI,CAOrE;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE;IAC5C,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,YAAY,EAAE,MAAM,CAAA;CACrB,GAAG,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAuvC/C"}
|
package/dist/web/server.js
CHANGED
|
@@ -75,6 +75,29 @@ function isLoopbackPeer(req) {
|
|
|
75
75
|
const ip = (req.socket.remoteAddress || '').replace(/^::ffff:/, '');
|
|
76
76
|
return ip === '127.0.0.1' || ip === '::1';
|
|
77
77
|
}
|
|
78
|
+
function isEnvOff(name) {
|
|
79
|
+
const v = (process.env[name] || '').trim().toLowerCase();
|
|
80
|
+
return v === 'off' || v === '0' || v === 'false' || v === 'no';
|
|
81
|
+
}
|
|
82
|
+
function hasForwardedPeerHeaders(req) {
|
|
83
|
+
return req.headers.forwarded !== undefined
|
|
84
|
+
|| req.headers['x-forwarded-for'] !== undefined
|
|
85
|
+
|| req.headers['x-forwarded-host'] !== undefined
|
|
86
|
+
|| req.headers['x-real-ip'] !== undefined
|
|
87
|
+
|| req.headers['cf-connecting-ip'] !== undefined;
|
|
88
|
+
}
|
|
89
|
+
function isTrustedLoopbackPeer(req) {
|
|
90
|
+
if (!isLoopbackPeer(req))
|
|
91
|
+
return false;
|
|
92
|
+
if (isEnvOff('IMHUB_TRUST_LOOPBACK'))
|
|
93
|
+
return false;
|
|
94
|
+
// A reverse proxy on the same host makes remote users appear as
|
|
95
|
+
// 127.0.0.1. Treat forwarded requests as network traffic and require
|
|
96
|
+
// the normal token path instead of granting the local bootstrap bypass.
|
|
97
|
+
if (hasForwardedPeerHeaders(req))
|
|
98
|
+
return false;
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
78
101
|
/** R13 A5 — track once-per-process whether the deprecated `?token=`
|
|
79
102
|
* URL fallback has been used, so we warn at most once per service
|
|
80
103
|
* lifetime instead of spamming the journal. Cleared by tests via
|
|
@@ -142,8 +165,8 @@ function checkAuth(req, res, url) {
|
|
|
142
165
|
// 1. Disabled by env → pass through.
|
|
143
166
|
if ((process.env.IMHUB_WEB_AUTH || '').toLowerCase() === 'off')
|
|
144
167
|
return true;
|
|
145
|
-
// 2.
|
|
146
|
-
if (
|
|
168
|
+
// 2. Trusted loopback → pass through (local CLI / browser-on-same-host).
|
|
169
|
+
if (isTrustedLoopbackPeer(req))
|
|
147
170
|
return true;
|
|
148
171
|
// 3. Public-by-design path → pass through.
|
|
149
172
|
if (isPublicPath(url.pathname, req.method || 'GET'))
|
|
@@ -201,7 +224,7 @@ function verifyTokenSync(raw) {
|
|
|
201
224
|
function getRequestActor(req) {
|
|
202
225
|
if ((process.env.IMHUB_WEB_AUTH || '').toLowerCase() === 'off')
|
|
203
226
|
return 'web:auth-off';
|
|
204
|
-
if (
|
|
227
|
+
if (isTrustedLoopbackPeer(req))
|
|
205
228
|
return 'web:loopback';
|
|
206
229
|
let url;
|
|
207
230
|
try {
|
|
@@ -224,17 +247,18 @@ function getRequestActor(req) {
|
|
|
224
247
|
*
|
|
225
248
|
* Trust order:
|
|
226
249
|
* 1. IMHUB_WEB_AUTH=off → admin (operator explicitly disabled auth)
|
|
227
|
-
* 2.
|
|
250
|
+
* 2. Trusted loopback → admin (operator on the host)
|
|
228
251
|
* 3. Bearer token → token.role === 'admin'
|
|
229
252
|
* 4. Otherwise → not admin
|
|
230
253
|
*
|
|
231
254
|
* Note: when no token has been created yet (pre-bootstrap), the
|
|
232
|
-
* loopback
|
|
233
|
-
* works.
|
|
255
|
+
* trusted-loopback branch still grants admin so the CLI bootstrap flow
|
|
256
|
+
* works. Disable it with IMHUB_TRUST_LOOPBACK=off. Reverse-proxied
|
|
257
|
+
* requests with Forwarded / X-Forwarded-* peer headers never qualify. */
|
|
234
258
|
function isRequestAdmin(req) {
|
|
235
259
|
if ((process.env.IMHUB_WEB_AUTH || '').toLowerCase() === 'off')
|
|
236
260
|
return true;
|
|
237
|
-
if (
|
|
261
|
+
if (isTrustedLoopbackPeer(req))
|
|
238
262
|
return true;
|
|
239
263
|
let url;
|
|
240
264
|
try {
|
|
@@ -265,6 +289,13 @@ function requireAdmin(req, res) {
|
|
|
265
289
|
res.end(JSON.stringify({ error: 'forbidden', message: 'admin role required' }));
|
|
266
290
|
return false;
|
|
267
291
|
}
|
|
292
|
+
export const __webAuthForTesting = {
|
|
293
|
+
isTrustedLoopbackPeer,
|
|
294
|
+
isRequestAdmin,
|
|
295
|
+
setTokenModule(mod) {
|
|
296
|
+
_tokenModule = mod;
|
|
297
|
+
},
|
|
298
|
+
};
|
|
268
299
|
export function createSerialQueue() {
|
|
269
300
|
let queue = Promise.resolve();
|
|
270
301
|
return (fn) => {
|
|
@@ -357,6 +388,7 @@ export async function startWebServer(options) {
|
|
|
357
388
|
event: 'web.auth_mode',
|
|
358
389
|
bind: bindHost,
|
|
359
390
|
enabled: isAuthEnabled(),
|
|
391
|
+
trustLoopback: !isEnvOff('IMHUB_TRUST_LOOPBACK'),
|
|
360
392
|
}, `Web console auth: ${isAuthEnabled() ? 'token-gated' : 'disabled (IMHUB_WEB_AUTH=off)'}`);
|
|
361
393
|
// HTTP request handler — static files + REST API
|
|
362
394
|
const httpServer = createServer(async (req, res) => {
|
|
@@ -643,21 +675,31 @@ export async function startWebServer(options) {
|
|
|
643
675
|
}
|
|
644
676
|
// Jobs
|
|
645
677
|
if (url.pathname === '/api/jobs' && req.method === 'GET') {
|
|
678
|
+
if (!requireAdmin(req, res))
|
|
679
|
+
return;
|
|
646
680
|
return handleListJobs(req, res, url);
|
|
647
681
|
}
|
|
648
682
|
const jobIdMatch = url.pathname.match(/^\/api\/jobs\/(\d+)$/);
|
|
649
683
|
if (jobIdMatch && req.method === 'GET') {
|
|
684
|
+
if (!requireAdmin(req, res))
|
|
685
|
+
return;
|
|
650
686
|
return handleGetJob(req, res, parseInt(jobIdMatch[1], 10));
|
|
651
687
|
}
|
|
652
688
|
const jobCancelMatch = url.pathname.match(/^\/api\/jobs\/(\d+)\/cancel$/);
|
|
653
689
|
if (jobCancelMatch && req.method === 'POST') {
|
|
690
|
+
if (!requireAdmin(req, res))
|
|
691
|
+
return;
|
|
654
692
|
return handleCancelJob(req, res, parseInt(jobCancelMatch[1], 10));
|
|
655
693
|
}
|
|
656
694
|
const jobRunMatch = url.pathname.match(/^\/api\/jobs\/(\d+)\/run$/);
|
|
657
695
|
if (jobRunMatch && req.method === 'POST') {
|
|
696
|
+
if (!requireAdmin(req, res))
|
|
697
|
+
return;
|
|
658
698
|
return handleRunJob(req, res, parseInt(jobRunMatch[1], 10));
|
|
659
699
|
}
|
|
660
700
|
if (url.pathname === '/api/jobs' && req.method === 'POST') {
|
|
701
|
+
if (!requireAdmin(req, res))
|
|
702
|
+
return;
|
|
661
703
|
return handleCreateJob(req, res);
|
|
662
704
|
}
|
|
663
705
|
// bgjobs (read-only view of ~/.claude/bgjobs, ~/.config/opencode/bgjobs, ~/.codex/bgjobs)
|
|
@@ -677,34 +719,46 @@ export async function startWebServer(options) {
|
|
|
677
719
|
return handleListSchedules(req, res, url);
|
|
678
720
|
}
|
|
679
721
|
// Reminders — list / cancel / snooze. Web-only path (the IM-side path
|
|
680
|
-
// is /remind slash command).
|
|
681
|
-
//
|
|
682
|
-
// single-operator deployments only.
|
|
722
|
+
// is /remind slash command). This exposes global reminders, so it is
|
|
723
|
+
// admin-only until a per-user mobile scope exists.
|
|
683
724
|
if (url.pathname === '/api/reminders' && req.method === 'GET') {
|
|
725
|
+
if (!requireAdmin(req, res))
|
|
726
|
+
return;
|
|
684
727
|
return handleListReminders(req, res, url);
|
|
685
728
|
}
|
|
686
729
|
const reminderCancelMatch = url.pathname.match(/^\/api\/reminders\/(\d+)\/cancel$/);
|
|
687
730
|
if (reminderCancelMatch && req.method === 'POST') {
|
|
731
|
+
if (!requireAdmin(req, res))
|
|
732
|
+
return;
|
|
688
733
|
return handleCancelReminderApi(req, res, Number.parseInt(reminderCancelMatch[1], 10));
|
|
689
734
|
}
|
|
690
735
|
const reminderSnoozeMatch = url.pathname.match(/^\/api\/reminders\/(\d+)\/snooze$/);
|
|
691
736
|
if (reminderSnoozeMatch && req.method === 'POST') {
|
|
737
|
+
if (!requireAdmin(req, res))
|
|
738
|
+
return;
|
|
692
739
|
return handleSnoozeReminderApi(req, res, Number.parseInt(reminderSnoozeMatch[1], 10));
|
|
693
740
|
}
|
|
694
741
|
// /api/memos — search / list / delete. List uses the same searchMemos
|
|
695
742
|
// function the MCP tool exposes; query/who/what/has_location/limit
|
|
696
743
|
// come through as URL params.
|
|
697
744
|
if (url.pathname === '/api/memos' && req.method === 'GET') {
|
|
745
|
+
if (!requireAdmin(req, res))
|
|
746
|
+
return;
|
|
698
747
|
return handleListMemos(req, res, url);
|
|
699
748
|
}
|
|
700
749
|
const memoIdMatch = url.pathname.match(/^\/api\/memos\/(\d+)$/);
|
|
701
750
|
if (memoIdMatch && req.method === 'DELETE') {
|
|
751
|
+
if (!requireAdmin(req, res))
|
|
752
|
+
return;
|
|
702
753
|
return handleDeleteMemo(req, res, Number.parseInt(memoIdMatch[1], 10));
|
|
703
754
|
}
|
|
704
755
|
// /api/env — read/write SMTP + Baidu AK + IMHUB_WEB_BIND. Values
|
|
705
756
|
// sensitive enough that GET returns them masked (only the last 4 chars
|
|
706
|
-
// visible) unless an explicit ?reveal=1 is passed
|
|
757
|
+
// visible) unless an explicit ?reveal=1 is passed. Keep the settings
|
|
758
|
+
// surface admin-only: even masked values disclose configured providers.
|
|
707
759
|
if (url.pathname === '/api/env' && req.method === 'GET') {
|
|
760
|
+
if (!requireAdmin(req, res))
|
|
761
|
+
return;
|
|
708
762
|
return handleGetEnv(req, res, url);
|
|
709
763
|
}
|
|
710
764
|
if (url.pathname === '/api/env' && req.method === 'PUT') {
|
|
@@ -847,6 +901,12 @@ export async function startWebServer(options) {
|
|
|
847
901
|
return handleViewerGet(req, res, viewerIdMatch[1]);
|
|
848
902
|
}
|
|
849
903
|
if (viewerIdMatch && req.method === 'DELETE') {
|
|
904
|
+
// Deleting a viewer paste is a mutation; per the security model all
|
|
905
|
+
// mutation endpoints require admin. Previously this route had no guard,
|
|
906
|
+
// so any authenticated token (incl. role=user, e.g. a mobile-QR token)
|
|
907
|
+
// could delete any paste by its id (ids leak via IM `/v/<uuid>` links).
|
|
908
|
+
if (!requireAdmin(req, res))
|
|
909
|
+
return;
|
|
850
910
|
return handleViewerDelete(req, res, viewerIdMatch[1]);
|
|
851
911
|
}
|
|
852
912
|
// PR-B: agent health snapshot (circuit breaker + rate-limiter remaining
|
|
@@ -866,9 +926,13 @@ export async function startWebServer(options) {
|
|
|
866
926
|
// v1.5 — Memory admin: enumerate users, list / delete facts, view /
|
|
867
927
|
// edit / delete persona, export. Backs the Memory tab in /tasks.
|
|
868
928
|
if (url.pathname === '/api/memory/users' && req.method === 'GET') {
|
|
929
|
+
if (!requireAdmin(req, res))
|
|
930
|
+
return;
|
|
869
931
|
return handleMemoryUsers(req, res);
|
|
870
932
|
}
|
|
871
933
|
if (url.pathname === '/api/memory/facts' && req.method === 'GET') {
|
|
934
|
+
if (!requireAdmin(req, res))
|
|
935
|
+
return;
|
|
872
936
|
return handleMemoryFacts(req, res, url);
|
|
873
937
|
}
|
|
874
938
|
if (url.pathname === '/api/memory/facts' && req.method === 'DELETE') {
|
|
@@ -878,9 +942,13 @@ export async function startWebServer(options) {
|
|
|
878
942
|
}
|
|
879
943
|
const memFactIdMatch = url.pathname.match(/^\/api\/memory\/facts\/(\d+)$/);
|
|
880
944
|
if (memFactIdMatch && req.method === 'DELETE') {
|
|
945
|
+
if (!requireAdmin(req, res))
|
|
946
|
+
return;
|
|
881
947
|
return handleMemoryDeleteOne(req, res, url, parseInt(memFactIdMatch[1], 10));
|
|
882
948
|
}
|
|
883
949
|
if (url.pathname === '/api/memory/persona' && req.method === 'GET') {
|
|
950
|
+
if (!requireAdmin(req, res))
|
|
951
|
+
return;
|
|
884
952
|
return handleMemoryPersona(req, res, url);
|
|
885
953
|
}
|
|
886
954
|
if (url.pathname === '/api/memory/persona' && req.method === 'PUT') {
|
|
@@ -894,6 +962,8 @@ export async function startWebServer(options) {
|
|
|
894
962
|
return handleMemoryPersonaDelete(req, res, url);
|
|
895
963
|
}
|
|
896
964
|
if (url.pathname === '/api/memory/export' && req.method === 'GET') {
|
|
965
|
+
if (!requireAdmin(req, res))
|
|
966
|
+
return;
|
|
897
967
|
return handleMemoryExport(req, res, url);
|
|
898
968
|
}
|
|
899
969
|
// v1.6 — vector backend control + index ops.
|
|
@@ -928,6 +998,8 @@ export async function startWebServer(options) {
|
|
|
928
998
|
return handleMemoryConsolidate(req, res);
|
|
929
999
|
}
|
|
930
1000
|
if (url.pathname === '/api/memory/consolidate/status' && req.method === 'GET') {
|
|
1001
|
+
if (!requireAdmin(req, res))
|
|
1002
|
+
return;
|
|
931
1003
|
return handleMemoryConsolidateStatus(req, res);
|
|
932
1004
|
}
|
|
933
1005
|
// v1.2.3 — Skills browser. Lists locally-installed claude/opencode
|
|
@@ -944,10 +1016,14 @@ export async function startWebServer(options) {
|
|
|
944
1016
|
}
|
|
945
1017
|
// PR-B: HITL approvals — global pending list + per-reqId resolve.
|
|
946
1018
|
if (url.pathname === '/api/approvals' && req.method === 'GET') {
|
|
1019
|
+
if (!requireAdmin(req, res))
|
|
1020
|
+
return;
|
|
947
1021
|
return handleListApprovals(req, res);
|
|
948
1022
|
}
|
|
949
1023
|
const approvalResolveMatch = url.pathname.match(/^\/api\/approvals\/([^/]+)\/resolve$/);
|
|
950
1024
|
if (approvalResolveMatch && req.method === 'POST') {
|
|
1025
|
+
if (!requireAdmin(req, res))
|
|
1026
|
+
return;
|
|
951
1027
|
return handleResolveApproval(req, res, approvalResolveMatch[1]);
|
|
952
1028
|
}
|
|
953
1029
|
// PR-D: Agent workspace file browser. Read-only inspection of
|
|
@@ -955,6 +1031,8 @@ export async function startWebServer(options) {
|
|
|
955
1031
|
// text files. PUT path supports inline editing (annotate CLAUDE.md,
|
|
956
1032
|
// AGENTS.md, etc.) — same traversal/size guards as GET.
|
|
957
1033
|
if (url.pathname === '/api/workspace-files' && req.method === 'GET') {
|
|
1034
|
+
if (!requireAdmin(req, res))
|
|
1035
|
+
return;
|
|
958
1036
|
return handleWorkspaceFiles(req, res, url);
|
|
959
1037
|
}
|
|
960
1038
|
if (url.pathname === '/api/workspace-files' && req.method === 'PUT') {
|
|
@@ -976,9 +1054,12 @@ export async function startWebServer(options) {
|
|
|
976
1054
|
return handleBatchJob(req, res, 'run', getDefaultAgent(options.defaultAgent));
|
|
977
1055
|
}
|
|
978
1056
|
// PR-C: SSE event stream — audit / approval / job / metrics events
|
|
979
|
-
// pushed real-time so the dashboard stops polling.
|
|
980
|
-
//
|
|
1057
|
+
// pushed real-time so the dashboard stops polling. Global control-plane
|
|
1058
|
+
// telemetry is admin-only; user/mobile-scoped streams should use a
|
|
1059
|
+
// separate endpoint when added.
|
|
981
1060
|
if (url.pathname === '/events' && req.method === 'GET') {
|
|
1061
|
+
if (!requireAdmin(req, res))
|
|
1062
|
+
return;
|
|
982
1063
|
return handleEventsSSE(req, res);
|
|
983
1064
|
}
|
|
984
1065
|
if (url.pathname === '/api/notify' && req.method === 'POST') {
|
|
@@ -1110,11 +1191,11 @@ export async function startWebServer(options) {
|
|
|
1110
1191
|
}, 'WS upgrade refused (per-IP rate limit)');
|
|
1111
1192
|
return cb(false, 429, 'rate limited');
|
|
1112
1193
|
}
|
|
1113
|
-
// Auth-off / loopback bypass — mirror checkAuth's two short-circuits
|
|
1194
|
+
// Auth-off / trusted-loopback bypass — mirror checkAuth's two short-circuits
|
|
1114
1195
|
// so dev / local CLI sessions still work without a token.
|
|
1115
1196
|
if ((process.env.IMHUB_WEB_AUTH || '').toLowerCase() === 'off')
|
|
1116
1197
|
return cb(true);
|
|
1117
|
-
if (
|
|
1198
|
+
if (isTrustedLoopbackPeer(info.req))
|
|
1118
1199
|
return cb(true);
|
|
1119
1200
|
// Origin check: cookie SameSite=Lax handles most of the CSWSH
|
|
1120
1201
|
// surface, but defence-in-depth — reject when Origin's host
|
|
@@ -1168,8 +1249,9 @@ export async function startWebServer(options) {
|
|
|
1168
1249
|
// IMHUB_WS_MAX_PER_IP active connections per IP (default 20)
|
|
1169
1250
|
// IMHUB_WS_MAX_NEW_PER_IP_PER_MIN new connections per IP per minute (default 30)
|
|
1170
1251
|
//
|
|
1171
|
-
//
|
|
1172
|
-
// short connections legitimately.
|
|
1252
|
+
// Trusted loopback bypasses both — local dev / CLI tooling makes many
|
|
1253
|
+
// short connections legitimately. Reverse-proxied loopback traffic is
|
|
1254
|
+
// still counted as network traffic.
|
|
1173
1255
|
const wsMaxPerIp = (() => {
|
|
1174
1256
|
const raw = process.env.IMHUB_WS_MAX_PER_IP;
|
|
1175
1257
|
if (raw) {
|
|
@@ -1198,7 +1280,7 @@ export async function startWebServer(options) {
|
|
|
1198
1280
|
}
|
|
1199
1281
|
/** Returns {ok:true} when the IP may open a new WS, else {ok:false, reason}. */
|
|
1200
1282
|
function checkWsIpRateLimit(req) {
|
|
1201
|
-
if (
|
|
1283
|
+
if (isTrustedLoopbackPeer(req))
|
|
1202
1284
|
return { ok: true };
|
|
1203
1285
|
const ip = peerIp(req);
|
|
1204
1286
|
if (!ip)
|
|
@@ -1216,7 +1298,7 @@ export async function startWebServer(options) {
|
|
|
1216
1298
|
return { ok: true };
|
|
1217
1299
|
}
|
|
1218
1300
|
function recordWsIpOpen(req) {
|
|
1219
|
-
if (
|
|
1301
|
+
if (isTrustedLoopbackPeer(req))
|
|
1220
1302
|
return;
|
|
1221
1303
|
const ip = peerIp(req);
|
|
1222
1304
|
if (!ip)
|
|
@@ -1227,7 +1309,7 @@ export async function startWebServer(options) {
|
|
|
1227
1309
|
wsPerIp.set(ip, slot);
|
|
1228
1310
|
}
|
|
1229
1311
|
function recordWsIpClose(req) {
|
|
1230
|
-
if (
|
|
1312
|
+
if (isTrustedLoopbackPeer(req))
|
|
1231
1313
|
return;
|
|
1232
1314
|
const ip = peerIp(req);
|
|
1233
1315
|
if (!ip)
|
|
@@ -4655,6 +4737,13 @@ function readBody(req, res) {
|
|
|
4655
4737
|
aborted = true;
|
|
4656
4738
|
if (res && !res.headersSent) {
|
|
4657
4739
|
sendJson(res, 413, { error: 'Request body too large' });
|
|
4740
|
+
res.once('finish', () => {
|
|
4741
|
+
if (!req.destroyed)
|
|
4742
|
+
req.destroy();
|
|
4743
|
+
});
|
|
4744
|
+
}
|
|
4745
|
+
else if (!req.destroyed) {
|
|
4746
|
+
req.destroy();
|
|
4658
4747
|
}
|
|
4659
4748
|
const err = new Error('Request body too large');
|
|
4660
4749
|
err.statusCode = 413;
|