agim-cli 1.2.129 → 1.2.131

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (186) hide show
  1. package/CHANGELOG.md +96 -0
  2. package/dist/core/llm/agent-loop.d.ts.map +1 -1
  3. package/dist/core/llm/agent-loop.js +9 -1
  4. package/dist/core/llm/agent-loop.js.map +1 -1
  5. package/dist/core/llm/plan-exit-dispatcher.d.ts +27 -0
  6. package/dist/core/llm/plan-exit-dispatcher.d.ts.map +1 -0
  7. package/dist/core/llm/plan-exit-dispatcher.js +210 -0
  8. package/dist/core/llm/plan-exit-dispatcher.js.map +1 -0
  9. package/dist/core/plan-history.d.ts +34 -0
  10. package/dist/core/plan-history.d.ts.map +1 -0
  11. package/dist/core/plan-history.js +89 -0
  12. package/dist/core/plan-history.js.map +1 -0
  13. package/dist/plugins/agents/native/index.d.ts.map +1 -1
  14. package/dist/plugins/agents/native/index.js +29 -2
  15. package/dist/plugins/agents/native/index.js.map +1 -1
  16. package/dist/web/public/assets/{a2a-Bp_OpTfW.js → a2a-Rq5d7Xk5.js} +2 -2
  17. package/dist/web/public/assets/{a2a-Bp_OpTfW.js.map → a2a-Rq5d7Xk5.js.map} +1 -1
  18. package/dist/web/public/assets/{activity-B5erGuWa.js → activity-FCJjBNRM.js} +2 -2
  19. package/dist/web/public/assets/{activity-B5erGuWa.js.map → activity-FCJjBNRM.js.map} +1 -1
  20. package/dist/web/public/assets/{admins-D3j7wkvL.js → admins-9fFdLRCv.js} +2 -2
  21. package/dist/web/public/assets/{admins-D3j7wkvL.js.map → admins-9fFdLRCv.js.map} +1 -1
  22. package/dist/web/public/assets/{agents-Cb3Vb2-8.js → agents-CiWiDAvY.js} +2 -2
  23. package/dist/web/public/assets/{agents-Cb3Vb2-8.js.map → agents-CiWiDAvY.js.map} +1 -1
  24. package/dist/web/public/assets/{approvals-BZULwrl5.js → approvals-4gNB4E5r.js} +2 -2
  25. package/dist/web/public/assets/{approvals-BZULwrl5.js.map → approvals-4gNB4E5r.js.map} +1 -1
  26. package/dist/web/public/assets/{arrow-down-OdSfpgG4.js → arrow-down-CL6JRd2c.js} +2 -2
  27. package/dist/web/public/assets/{arrow-down-OdSfpgG4.js.map → arrow-down-CL6JRd2c.js.map} +1 -1
  28. package/dist/web/public/assets/{arrow-up-DtrvOH7Z.js → arrow-up-B47-Pj-X.js} +2 -2
  29. package/dist/web/public/assets/{arrow-up-DtrvOH7Z.js.map → arrow-up-B47-Pj-X.js.map} +1 -1
  30. package/dist/web/public/assets/{asks-BaVT-eFe.js → asks-DPTxh_Os.js} +2 -2
  31. package/dist/web/public/assets/{asks-BaVT-eFe.js.map → asks-DPTxh_Os.js.map} +1 -1
  32. package/dist/web/public/assets/{audit-CSk2rv6z.js → audit-Dw3mL-g7.js} +2 -2
  33. package/dist/web/public/assets/{audit-CSk2rv6z.js.map → audit-Dw3mL-g7.js.map} +1 -1
  34. package/dist/web/public/assets/{bell-DifbuUaI.js → bell-ojwGPpS7.js} +2 -2
  35. package/dist/web/public/assets/{bell-DifbuUaI.js.map → bell-ojwGPpS7.js.map} +1 -1
  36. package/dist/web/public/assets/{bgjobs-D3wsuZBR.js → bgjobs-DDTjejCS.js} +2 -2
  37. package/dist/web/public/assets/{bgjobs-D3wsuZBR.js.map → bgjobs-DDTjejCS.js.map} +1 -1
  38. package/dist/web/public/assets/{brain-BH9ObIfA.js → brain-wzaKeCbt.js} +2 -2
  39. package/dist/web/public/assets/{brain-BH9ObIfA.js.map → brain-wzaKeCbt.js.map} +1 -1
  40. package/dist/web/public/assets/{briefcase-CZqK5oy3.js → briefcase-CCAq-ik6.js} +2 -2
  41. package/dist/web/public/assets/{briefcase-CZqK5oy3.js.map → briefcase-CCAq-ik6.js.map} +1 -1
  42. package/dist/web/public/assets/{chat-gOU1pe0E.js → chat-CbSvPFpG.js} +2 -2
  43. package/dist/web/public/assets/{chat-gOU1pe0E.js.map → chat-CbSvPFpG.js.map} +1 -1
  44. package/dist/web/public/assets/{chevron-left-BtuaBNRZ.js → chevron-left-bpKHh9Bu.js} +2 -2
  45. package/dist/web/public/assets/{chevron-left-BtuaBNRZ.js.map → chevron-left-bpKHh9Bu.js.map} +1 -1
  46. package/dist/web/public/assets/{chevron-right-uDVVnuGu.js → chevron-right-CUlLpETY.js} +2 -2
  47. package/dist/web/public/assets/{chevron-right-uDVVnuGu.js.map → chevron-right-CUlLpETY.js.map} +1 -1
  48. package/dist/web/public/assets/{circle-check-VgYKnBhy.js → circle-check-D98QQwz0.js} +2 -2
  49. package/dist/web/public/assets/{circle-check-VgYKnBhy.js.map → circle-check-D98QQwz0.js.map} +1 -1
  50. package/dist/web/public/assets/{circle-check-big-7idAFtyL.js → circle-check-big-BP-0K1le.js} +2 -2
  51. package/dist/web/public/assets/{circle-check-big-7idAFtyL.js.map → circle-check-big-BP-0K1le.js.map} +1 -1
  52. package/dist/web/public/assets/{circle-x-8dhkvHlT.js → circle-x-Et3rwVPd.js} +2 -2
  53. package/dist/web/public/assets/{circle-x-8dhkvHlT.js.map → circle-x-Et3rwVPd.js.map} +1 -1
  54. package/dist/web/public/assets/{confirm-dialog-_BtUs6oW.js → confirm-dialog-6dKowsEt.js} +2 -2
  55. package/dist/web/public/assets/{confirm-dialog-_BtUs6oW.js.map → confirm-dialog-6dKowsEt.js.map} +1 -1
  56. package/dist/web/public/assets/{copy-Ba1min4z.js → copy-kCWCP9Wu.js} +2 -2
  57. package/dist/web/public/assets/{copy-Ba1min4z.js.map → copy-kCWCP9Wu.js.map} +1 -1
  58. package/dist/web/public/assets/{data-table-BVcXWtPw.js → data-table-BbNkr6tW.js} +2 -2
  59. package/dist/web/public/assets/{data-table-BVcXWtPw.js.map → data-table-BbNkr6tW.js.map} +1 -1
  60. package/dist/web/public/assets/{dialog-BVYFRXlI.js → dialog-Cv87qelJ.js} +2 -2
  61. package/dist/web/public/assets/{dialog-BVYFRXlI.js.map → dialog-Cv87qelJ.js.map} +1 -1
  62. package/dist/web/public/assets/{download-DYc5g_1W.js → download-Cv8u9PxC.js} +2 -2
  63. package/dist/web/public/assets/{download-DYc5g_1W.js.map → download-Cv8u9PxC.js.map} +1 -1
  64. package/dist/web/public/assets/{email-6vlGBJXY.js → email-BS3f2y7i.js} +2 -2
  65. package/dist/web/public/assets/{email-6vlGBJXY.js.map → email-BS3f2y7i.js.map} +1 -1
  66. package/dist/web/public/assets/{empty-state-WHzNYVaT.js → empty-state-CRnid5FY.js} +2 -2
  67. package/dist/web/public/assets/{empty-state-WHzNYVaT.js.map → empty-state-CRnid5FY.js.map} +1 -1
  68. package/dist/web/public/assets/{external-link-CfQZDer8.js → external-link-CzQc-c_P.js} +2 -2
  69. package/dist/web/public/assets/{external-link-CfQZDer8.js.map → external-link-CzQc-c_P.js.map} +1 -1
  70. package/dist/web/public/assets/{eye-PzqPB6N6.js → eye-DEBkfJiC.js} +2 -2
  71. package/dist/web/public/assets/{eye-PzqPB6N6.js.map → eye-DEBkfJiC.js.map} +1 -1
  72. package/dist/web/public/assets/{facts-NmBeyMpC.js → facts-O3FFqdD1.js} +2 -2
  73. package/dist/web/public/assets/{facts-NmBeyMpC.js.map → facts-O3FFqdD1.js.map} +1 -1
  74. package/dist/web/public/assets/{goals-BfMwQtYm.js → goals-pr5ky9rG.js} +2 -2
  75. package/dist/web/public/assets/{goals-BfMwQtYm.js.map → goals-pr5ky9rG.js.map} +1 -1
  76. package/dist/web/public/assets/{health-D46iQ6Hj.js → health-D_rfBsgq.js} +2 -2
  77. package/dist/web/public/assets/{health-D46iQ6Hj.js.map → health-D_rfBsgq.js.map} +1 -1
  78. package/dist/web/public/assets/{heart-pulse-BHz53Ggd.js → heart-pulse-203oH09r.js} +2 -2
  79. package/dist/web/public/assets/{heart-pulse-BHz53Ggd.js.map → heart-pulse-203oH09r.js.map} +1 -1
  80. package/dist/web/public/assets/{heartbeat-DBpc9rKL.js → heartbeat-B5yJoH-X.js} +2 -2
  81. package/dist/web/public/assets/{heartbeat-DBpc9rKL.js.map → heartbeat-B5yJoH-X.js.map} +1 -1
  82. package/dist/web/public/assets/{hot-D_-tARKX.js → hot-CXGeh4Oo.js} +2 -2
  83. package/dist/web/public/assets/{hot-D_-tARKX.js.map → hot-CXGeh4Oo.js.map} +1 -1
  84. package/dist/web/public/assets/{index-DXI13nSQ.js → index-CuNnG-5O.js} +16 -16
  85. package/dist/web/public/assets/index-CuNnG-5O.js.map +1 -0
  86. package/dist/web/public/assets/{installed-BtOgT0Ea.js → installed-CQKXZNEU.js} +2 -2
  87. package/dist/web/public/assets/{installed-BtOgT0Ea.js.map → installed-CQKXZNEU.js.map} +1 -1
  88. package/dist/web/public/assets/{jobs-DUoPbupO.js → jobs-DPxN8Ko0.js} +2 -2
  89. package/dist/web/public/assets/{jobs-DUoPbupO.js.map → jobs-DPxN8Ko0.js.map} +1 -1
  90. package/dist/web/public/assets/{layout-BpS7td-S.js → layout-4C_GhacH.js} +2 -2
  91. package/dist/web/public/assets/{layout-BpS7td-S.js.map → layout-4C_GhacH.js.map} +1 -1
  92. package/dist/web/public/assets/{layout-wuoOyazR.js → layout-CQXWA5Ak.js} +2 -2
  93. package/dist/web/public/assets/{layout-wuoOyazR.js.map → layout-CQXWA5Ak.js.map} +1 -1
  94. package/dist/web/public/assets/{layout-C3e8vPrG.js → layout-CxQy-DtV.js} +2 -2
  95. package/dist/web/public/assets/{layout-C3e8vPrG.js.map → layout-CxQy-DtV.js.map} +1 -1
  96. package/dist/web/public/assets/{layout-C1EVvCs8.js → layout-VOwsqNph.js} +2 -2
  97. package/dist/web/public/assets/{layout-C1EVvCs8.js.map → layout-VOwsqNph.js.map} +1 -1
  98. package/dist/web/public/assets/{layout-DkoRqhBs.js → layout-tAyuz3-F.js} +2 -2
  99. package/dist/web/public/assets/{layout-DkoRqhBs.js.map → layout-tAyuz3-F.js.map} +1 -1
  100. package/dist/web/public/assets/{llm-iuvigJxr.js → llm-i15cVS6k.js} +2 -2
  101. package/dist/web/public/assets/{llm-iuvigJxr.js.map → llm-i15cVS6k.js.map} +1 -1
  102. package/dist/web/public/assets/{loader-circle-BS5FFFg-.js → loader-circle-CVEo3Hwx.js} +2 -2
  103. package/dist/web/public/assets/{loader-circle-BS5FFFg-.js.map → loader-circle-CVEo3Hwx.js.map} +1 -1
  104. package/dist/web/public/assets/{map-pin-CRS6YW4i.js → map-pin-BlmDrPGJ.js} +2 -2
  105. package/dist/web/public/assets/{map-pin-CRS6YW4i.js.map → map-pin-BlmDrPGJ.js.map} +1 -1
  106. package/dist/web/public/assets/{mcp-PWEuZOJ_.js → mcp-9MlUdHAS.js} +2 -2
  107. package/dist/web/public/assets/{mcp-PWEuZOJ_.js.map → mcp-9MlUdHAS.js.map} +1 -1
  108. package/dist/web/public/assets/{memos-C6i1hbv9.js → memos-MDYe436b.js} +2 -2
  109. package/dist/web/public/assets/{memos-C6i1hbv9.js.map → memos-MDYe436b.js.map} +1 -1
  110. package/dist/web/public/assets/{messengers-CZ3eL15d.js → messengers-D0F-cgJ0.js} +2 -2
  111. package/dist/web/public/assets/{messengers-CZ3eL15d.js.map → messengers-D0F-cgJ0.js.map} +1 -1
  112. package/dist/web/public/assets/{mobile-_rEEG_kX.js → mobile-Bezz5MnW.js} +2 -2
  113. package/dist/web/public/assets/{mobile-_rEEG_kX.js.map → mobile-Bezz5MnW.js.map} +1 -1
  114. package/dist/web/public/assets/{native-agent-B-Id8sOl.js → native-agent-bRS7DRfj.js} +2 -2
  115. package/dist/web/public/assets/{native-agent-B-Id8sOl.js.map → native-agent-bRS7DRfj.js.map} +1 -1
  116. package/dist/web/public/assets/{network-MVE8s4TA.js → network-3O1O1jsk.js} +2 -2
  117. package/dist/web/public/assets/{network-MVE8s4TA.js.map → network-3O1O1jsk.js.map} +1 -1
  118. package/dist/web/public/assets/{outbox-BRpgAP1d.js → outbox-D2iX9mcs.js} +2 -2
  119. package/dist/web/public/assets/{outbox-BRpgAP1d.js.map → outbox-D2iX9mcs.js.map} +1 -1
  120. package/dist/web/public/assets/{pagination-dm3r6K_2.js → pagination-79ms7QFI.js} +2 -2
  121. package/dist/web/public/assets/{pagination-dm3r6K_2.js.map → pagination-79ms7QFI.js.map} +1 -1
  122. package/dist/web/public/assets/{persona-Oq3C-gEw.js → persona-D37NzRbz.js} +2 -2
  123. package/dist/web/public/assets/{persona-Oq3C-gEw.js.map → persona-D37NzRbz.js.map} +1 -1
  124. package/dist/web/public/assets/{play-COQw7BqX.js → play-BedplEoY.js} +2 -2
  125. package/dist/web/public/assets/{play-COQw7BqX.js.map → play-BedplEoY.js.map} +1 -1
  126. package/dist/web/public/assets/{plus-CdOyGoU1.js → plus-4GdEpvTV.js} +2 -2
  127. package/dist/web/public/assets/{plus-CdOyGoU1.js.map → plus-4GdEpvTV.js.map} +1 -1
  128. package/dist/web/public/assets/{policy-docXezae.js → policy-Gmd4rmzp.js} +2 -2
  129. package/dist/web/public/assets/{policy-docXezae.js.map → policy-Gmd4rmzp.js.map} +1 -1
  130. package/dist/web/public/assets/{qr-code-8putJTrW.js → qr-code-DVgHWDky.js} +2 -2
  131. package/dist/web/public/assets/{qr-code-8putJTrW.js.map → qr-code-DVgHWDky.js.map} +1 -1
  132. package/dist/web/public/assets/{refresh-ccw-BPKXoMZa.js → refresh-ccw-DZHRYuFA.js} +2 -2
  133. package/dist/web/public/assets/{refresh-ccw-BPKXoMZa.js.map → refresh-ccw-DZHRYuFA.js.map} +1 -1
  134. package/dist/web/public/assets/{reminders-CP2qtNSr.js → reminders-C00V-Fs9.js} +2 -2
  135. package/dist/web/public/assets/{reminders-CP2qtNSr.js.map → reminders-C00V-Fs9.js.map} +1 -1
  136. package/dist/web/public/assets/{save-BXCmgeEj.js → save-DQlBAXAl.js} +2 -2
  137. package/dist/web/public/assets/{save-BXCmgeEj.js.map → save-DQlBAXAl.js.map} +1 -1
  138. package/dist/web/public/assets/{schedules-DVaY38v1.js → schedules-BtuWAAd7.js} +2 -2
  139. package/dist/web/public/assets/{schedules-DVaY38v1.js.map → schedules-BtuWAAd7.js.map} +1 -1
  140. package/dist/web/public/assets/{search-DzexDAbr.js → search-DJXxphzz.js} +2 -2
  141. package/dist/web/public/assets/{search-DzexDAbr.js.map → search-DJXxphzz.js.map} +1 -1
  142. package/dist/web/public/assets/{search-B-wDhzjs.js → search-dBGBHpnH.js} +2 -2
  143. package/dist/web/public/assets/{search-B-wDhzjs.js.map → search-dBGBHpnH.js.map} +1 -1
  144. package/dist/web/public/assets/{security-FHN-b43T.js → security-CP0MP_Gx.js} +2 -2
  145. package/dist/web/public/assets/{security-FHN-b43T.js.map → security-CP0MP_Gx.js.map} +1 -1
  146. package/dist/web/public/assets/{service-DXGUZTCp.js → service-BJFnNe7Y.js} +2 -2
  147. package/dist/web/public/assets/{service-DXGUZTCp.js.map → service-BJFnNe7Y.js.map} +1 -1
  148. package/dist/web/public/assets/{shield-alert-BM0-khVy.js → shield-alert-DRh8fC8Z.js} +2 -2
  149. package/dist/web/public/assets/{shield-alert-BM0-khVy.js.map → shield-alert-DRh8fC8Z.js.map} +1 -1
  150. package/dist/web/public/assets/{status-badge-DeESb2dc.js → status-badge-CTRG6Pb2.js} +2 -2
  151. package/dist/web/public/assets/{status-badge-DeESb2dc.js.map → status-badge-CTRG6Pb2.js.map} +1 -1
  152. package/dist/web/public/assets/{subtasks-DOwo6FnZ.js → subtasks-BiZ2mCPs.js} +2 -2
  153. package/dist/web/public/assets/{subtasks-DOwo6FnZ.js.map → subtasks-BiZ2mCPs.js.map} +1 -1
  154. package/dist/web/public/assets/{table-oUi0mGOn.js → table-Dbriy3Jz.js} +2 -2
  155. package/dist/web/public/assets/{table-oUi0mGOn.js.map → table-Dbriy3Jz.js.map} +1 -1
  156. package/dist/web/public/assets/{topn-D1LQXoLo.js → topn-BEi7Uvlg.js} +2 -2
  157. package/dist/web/public/assets/{topn-D1LQXoLo.js.map → topn-BEi7Uvlg.js.map} +1 -1
  158. package/dist/web/public/assets/{trash-2-CTkSvnZX.js → trash-2-BgltvQpz.js} +2 -2
  159. package/dist/web/public/assets/{trash-2-CTkSvnZX.js.map → trash-2-BgltvQpz.js.map} +1 -1
  160. package/dist/web/public/assets/{use-background-tasks-BkUIDOxX.js → use-background-tasks-DHo3Hnu9.js} +2 -2
  161. package/dist/web/public/assets/{use-background-tasks-BkUIDOxX.js.map → use-background-tasks-DHo3Hnu9.js.map} +1 -1
  162. package/dist/web/public/assets/{use-llm-admin-BrJMMEz5.js → use-llm-admin-EU1V1WQ5.js} +2 -2
  163. package/dist/web/public/assets/{use-llm-admin-BrJMMEz5.js.map → use-llm-admin-EU1V1WQ5.js.map} +1 -1
  164. package/dist/web/public/assets/{use-memory-CbTYEBTc.js → use-memory-D0FD5n0A.js} +2 -2
  165. package/dist/web/public/assets/{use-memory-CbTYEBTc.js.map → use-memory-D0FD5n0A.js.map} +1 -1
  166. package/dist/web/public/assets/{use-observability-Bum0mmDO.js → use-observability-CZg_sS2t.js} +2 -2
  167. package/dist/web/public/assets/{use-observability-Bum0mmDO.js.map → use-observability-CZg_sS2t.js.map} +1 -1
  168. package/dist/web/public/assets/{use-settings-DJlVLnjo.js → use-settings-D3vI1Dwg.js} +2 -2
  169. package/dist/web/public/assets/{use-settings-DJlVLnjo.js.map → use-settings-D3vI1Dwg.js.map} +1 -1
  170. package/dist/web/public/assets/{use-workspace-BJZUfkqw.js → use-workspace-DWm4dRVD.js} +2 -2
  171. package/dist/web/public/assets/{use-workspace-BJZUfkqw.js.map → use-workspace-DWm4dRVD.js.map} +1 -1
  172. package/dist/web/public/assets/{useQuery-CEwGD94N.js → useQuery-DIlm0vJ-.js} +2 -2
  173. package/dist/web/public/assets/{useQuery-CEwGD94N.js.map → useQuery-DIlm0vJ-.js.map} +1 -1
  174. package/dist/web/public/assets/{vector-BqffHZmp.js → vector-CaijGbpn.js} +2 -2
  175. package/dist/web/public/assets/{vector-BqffHZmp.js.map → vector-CaijGbpn.js.map} +1 -1
  176. package/dist/web/public/assets/{viewer-CKTTV-Wt.js → viewer-nU6uZJWq.js} +2 -2
  177. package/dist/web/public/assets/{viewer-CKTTV-Wt.js.map → viewer-nU6uZJWq.js.map} +1 -1
  178. package/dist/web/public/assets/{workspace-DQphgYwy.js → workspace-BluXJmIg.js} +2 -2
  179. package/dist/web/public/assets/{workspace-DQphgYwy.js.map → workspace-BluXJmIg.js.map} +1 -1
  180. package/dist/web/public/assets/{workspaces-BOA3TuDS.js → workspaces-B2nSpx9t.js} +2 -2
  181. package/dist/web/public/assets/{workspaces-BOA3TuDS.js.map → workspaces-B2nSpx9t.js.map} +1 -1
  182. package/dist/web/public/assets/{x-OHUicFfn.js → x-D_eFtxsK.js} +2 -2
  183. package/dist/web/public/assets/{x-OHUicFfn.js.map → x-D_eFtxsK.js.map} +1 -1
  184. package/dist/web/public/index.html +1 -1
  185. package/package.json +1 -1
  186. package/dist/web/public/assets/index-DXI13nSQ.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"schedules-DVaY38v1.js","sources":["../../node_modules/lucide-react/dist/esm/icons/calendar-clock.js","../../src/hooks/use-schedules.ts","../../src/routes/tasks/schedules.tsx"],"sourcesContent":["/**\n * @license lucide-react v0.469.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst CalendarClock = createLucideIcon(\"CalendarClock\", [\n [\"path\", { d: \"M21 7.5V6a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h3.5\", key: \"1osxxc\" }],\n [\"path\", { d: \"M16 2v4\", key: \"4m81vk\" }],\n [\"path\", { d: \"M8 2v4\", key: \"1cmpym\" }],\n [\"path\", { d: \"M3 10h5\", key: \"r794hk\" }],\n [\"path\", { d: \"M17.5 17.5 16 16.3V14\", key: \"akvzfd\" }],\n [\"circle\", { cx: \"16\", cy: \"16\", r: \"6\", key: \"qoo3c4\" }]\n]);\n\nexport { CalendarClock as default };\n//# sourceMappingURL=calendar-clock.js.map\n","/**\n * useSchedules — react-query wrapper for /api/schedules.\n *\n * Read-only: schedule create / delete / toggle isn't exposed via\n * /api today (only via CLI). When that lands the mutations slot in\n * here next to useSchedules.\n */\n\nimport { useQueryClient, useQuery } from '@tanstack/react-query'\nimport { api } from '@/lib/api/endpoints'\nimport type { ListSchedulesQuery, ListSchedulesResponse } from '@/types/api'\n\nexport const schedulesKeys = {\n all: ['schedules'] as const,\n list: (q: ListSchedulesQuery) => ['schedules', 'list', q] as const,\n}\n\nexport function useSchedules(query: ListSchedulesQuery) {\n return useQuery<ListSchedulesResponse>({\n queryKey: schedulesKeys.list(query),\n queryFn: () => api.listSchedules(query),\n })\n}\n\nexport function useInvalidateSchedules() {\n const qc = useQueryClient()\n return () => qc.invalidateQueries({ queryKey: schedulesKeys.all })\n}\n","/**\n * /tasks/schedules — cron-style triggers list. Read-only; mutations\n * (create / delete / toggle) only exist via CLI today; the page\n * shows what's scheduled and when the next run lands.\n */\n\nimport { useMemo } from 'react'\nimport { useSearchParams } from 'react-router-dom'\nimport { useTranslation } from 'react-i18next'\nimport { CalendarClock, Loader2, RefreshCcw } from 'lucide-react'\n\nimport { DataTable, type DataTableColumn } from '@/components/common/data-table'\nimport { EmptyState } from '@/components/common/empty-state'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { Input } from '@/components/ui/input'\nimport { Label } from '@/components/ui/label'\nimport { useSchedules } from '@/hooks/use-schedules'\nimport type { Schedule } from '@/types/api'\n\nexport default function SchedulesRoute(): JSX.Element {\n const { t } = useTranslation(['tasks', 'common'])\n const [params, setParams] = useSearchParams()\n const agent = params.get('agent') ?? ''\n\n const { data, isLoading, isFetching, refetch } = useSchedules(\n agent ? { agent } : {},\n )\n const schedules = data?.schedules ?? []\n\n function setAgentFilter(v: string): void {\n const next = new URLSearchParams(params)\n if (!v) next.delete('agent')\n else next.set('agent', v)\n setParams(next, { replace: true })\n }\n\n const columns: DataTableColumn<Schedule>[] = useMemo(\n () => [\n {\n id: 'id',\n header: t('schedules.col.id'),\n cell: (r) => <span className=\"tabular-nums text-text-dim\">#{r.id}</span>,\n headClassName: 'w-16',\n },\n {\n id: 'name',\n header: t('schedules.col.name'),\n cell: (r) => <span className=\"font-medium\">{r.name}</span>,\n asCardTitle: true,\n },\n {\n id: 'agent',\n header: t('schedules.col.agent'),\n cell: (r) => <span className=\"text-text-dim\">{r.agent}</span>,\n headClassName: 'w-32',\n },\n {\n id: 'cron',\n header: t('schedules.col.cron'),\n cell: (r) => <code className=\"rounded bg-surface-2 px-1.5 py-0.5 text-xs\">{r.cron}</code>,\n headClassName: 'w-32',\n },\n {\n id: 'prompt',\n header: t('schedules.col.prompt'),\n cell: (r) => <span className=\"line-clamp-2 text-text-dim\">{r.prompt}</span>,\n hideOnMobile: true,\n },\n {\n id: 'enabled',\n header: t('schedules.col.enabled'),\n cell: (r) =>\n r.enabled ? (\n <Badge variant=\"success\">{t('schedules.enabled')}</Badge>\n ) : (\n <Badge variant=\"outline\">{t('schedules.disabled')}</Badge>\n ),\n headClassName: 'w-20',\n },\n {\n id: 'nextRun',\n header: t('schedules.col.nextRun'),\n cell: (r) => <span className=\"text-text-dim\">{formatTime(r.next_run)}</span>,\n headClassName: 'w-40',\n },\n {\n id: 'lastRun',\n header: t('schedules.col.lastRun'),\n cell: (r) => <span className=\"text-text-dim\">{formatTime(r.last_run)}</span>,\n headClassName: 'w-40',\n hideOnMobile: true,\n },\n ],\n [t],\n )\n\n return (\n <div className=\"mx-auto flex max-w-7xl flex-col gap-4\">\n <header className=\"flex flex-col gap-1\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <h1 className=\"text-xl font-semibold\">{t('schedules.title')}</h1>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"ml-auto\"\n onClick={() => refetch()}\n disabled={isFetching}\n aria-label={t('actions.refresh', { ns: 'common' })}\n >\n {isFetching ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <RefreshCcw className=\"h-4 w-4\" />}\n <span className=\"hidden sm:inline\">{t('actions.refresh', { ns: 'common' })}</span>\n </Button>\n </div>\n <p className=\"text-sm text-text-dim\">{t('schedules.subtitle')}</p>\n </header>\n\n <div className=\"flex flex-wrap items-end gap-2\">\n <div className=\"flex flex-col gap-1\">\n <Label htmlFor=\"agent-filter\" className=\"text-xs text-text-dim\">\n {t('schedules.filter.agent')}\n </Label>\n <Input\n id=\"agent-filter\"\n value={agent}\n onChange={(e) => setAgentFilter(e.target.value)}\n placeholder={t('schedules.filter.agentAny')}\n className=\"w-48\"\n />\n </div>\n </div>\n\n <DataTable\n columns={columns}\n rows={schedules}\n getRowId={(r) => String(r.id)}\n loading={isLoading}\n emptyState={\n <EmptyState\n icon={<CalendarClock />}\n title={t('schedules.empty.title')}\n description={t('schedules.empty.description')}\n />\n }\n />\n </div>\n )\n}\n\nfunction formatTime(iso: string | undefined | null): string {\n if (iso == null) return '—'\n try {\n const d = new Date(iso)\n if (Number.isNaN(d.getTime())) return iso\n return d.toLocaleString(undefined, { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' })\n } catch {\n return iso\n }\n}\n"],"names":["CalendarClock","createLucideIcon","schedulesKeys","q","useSchedules","query","useQuery","api","SchedulesRoute","t","useTranslation","params","setParams","useSearchParams","agent","data","isLoading","isFetching","refetch","schedules","setAgentFilter","v","next","columns","useMemo","r","jsxs","jsx","Badge","formatTime","Button","Loader2","RefreshCcw","Label","Input","e","DataTable","EmptyState","iso","d"],"mappings":"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","x_google_ignoreList":[0]}
1
+ {"version":3,"file":"schedules-BtuWAAd7.js","sources":["../../node_modules/lucide-react/dist/esm/icons/calendar-clock.js","../../src/hooks/use-schedules.ts","../../src/routes/tasks/schedules.tsx"],"sourcesContent":["/**\n * @license lucide-react v0.469.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst CalendarClock = createLucideIcon(\"CalendarClock\", [\n [\"path\", { d: \"M21 7.5V6a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h3.5\", key: \"1osxxc\" }],\n [\"path\", { d: \"M16 2v4\", key: \"4m81vk\" }],\n [\"path\", { d: \"M8 2v4\", key: \"1cmpym\" }],\n [\"path\", { d: \"M3 10h5\", key: \"r794hk\" }],\n [\"path\", { d: \"M17.5 17.5 16 16.3V14\", key: \"akvzfd\" }],\n [\"circle\", { cx: \"16\", cy: \"16\", r: \"6\", key: \"qoo3c4\" }]\n]);\n\nexport { CalendarClock as default };\n//# sourceMappingURL=calendar-clock.js.map\n","/**\n * useSchedules — react-query wrapper for /api/schedules.\n *\n * Read-only: schedule create / delete / toggle isn't exposed via\n * /api today (only via CLI). When that lands the mutations slot in\n * here next to useSchedules.\n */\n\nimport { useQueryClient, useQuery } from '@tanstack/react-query'\nimport { api } from '@/lib/api/endpoints'\nimport type { ListSchedulesQuery, ListSchedulesResponse } from '@/types/api'\n\nexport const schedulesKeys = {\n all: ['schedules'] as const,\n list: (q: ListSchedulesQuery) => ['schedules', 'list', q] as const,\n}\n\nexport function useSchedules(query: ListSchedulesQuery) {\n return useQuery<ListSchedulesResponse>({\n queryKey: schedulesKeys.list(query),\n queryFn: () => api.listSchedules(query),\n })\n}\n\nexport function useInvalidateSchedules() {\n const qc = useQueryClient()\n return () => qc.invalidateQueries({ queryKey: schedulesKeys.all })\n}\n","/**\n * /tasks/schedules — cron-style triggers list. Read-only; mutations\n * (create / delete / toggle) only exist via CLI today; the page\n * shows what's scheduled and when the next run lands.\n */\n\nimport { useMemo } from 'react'\nimport { useSearchParams } from 'react-router-dom'\nimport { useTranslation } from 'react-i18next'\nimport { CalendarClock, Loader2, RefreshCcw } from 'lucide-react'\n\nimport { DataTable, type DataTableColumn } from '@/components/common/data-table'\nimport { EmptyState } from '@/components/common/empty-state'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { Input } from '@/components/ui/input'\nimport { Label } from '@/components/ui/label'\nimport { useSchedules } from '@/hooks/use-schedules'\nimport type { Schedule } from '@/types/api'\n\nexport default function SchedulesRoute(): JSX.Element {\n const { t } = useTranslation(['tasks', 'common'])\n const [params, setParams] = useSearchParams()\n const agent = params.get('agent') ?? ''\n\n const { data, isLoading, isFetching, refetch } = useSchedules(\n agent ? { agent } : {},\n )\n const schedules = data?.schedules ?? []\n\n function setAgentFilter(v: string): void {\n const next = new URLSearchParams(params)\n if (!v) next.delete('agent')\n else next.set('agent', v)\n setParams(next, { replace: true })\n }\n\n const columns: DataTableColumn<Schedule>[] = useMemo(\n () => [\n {\n id: 'id',\n header: t('schedules.col.id'),\n cell: (r) => <span className=\"tabular-nums text-text-dim\">#{r.id}</span>,\n headClassName: 'w-16',\n },\n {\n id: 'name',\n header: t('schedules.col.name'),\n cell: (r) => <span className=\"font-medium\">{r.name}</span>,\n asCardTitle: true,\n },\n {\n id: 'agent',\n header: t('schedules.col.agent'),\n cell: (r) => <span className=\"text-text-dim\">{r.agent}</span>,\n headClassName: 'w-32',\n },\n {\n id: 'cron',\n header: t('schedules.col.cron'),\n cell: (r) => <code className=\"rounded bg-surface-2 px-1.5 py-0.5 text-xs\">{r.cron}</code>,\n headClassName: 'w-32',\n },\n {\n id: 'prompt',\n header: t('schedules.col.prompt'),\n cell: (r) => <span className=\"line-clamp-2 text-text-dim\">{r.prompt}</span>,\n hideOnMobile: true,\n },\n {\n id: 'enabled',\n header: t('schedules.col.enabled'),\n cell: (r) =>\n r.enabled ? (\n <Badge variant=\"success\">{t('schedules.enabled')}</Badge>\n ) : (\n <Badge variant=\"outline\">{t('schedules.disabled')}</Badge>\n ),\n headClassName: 'w-20',\n },\n {\n id: 'nextRun',\n header: t('schedules.col.nextRun'),\n cell: (r) => <span className=\"text-text-dim\">{formatTime(r.next_run)}</span>,\n headClassName: 'w-40',\n },\n {\n id: 'lastRun',\n header: t('schedules.col.lastRun'),\n cell: (r) => <span className=\"text-text-dim\">{formatTime(r.last_run)}</span>,\n headClassName: 'w-40',\n hideOnMobile: true,\n },\n ],\n [t],\n )\n\n return (\n <div className=\"mx-auto flex max-w-7xl flex-col gap-4\">\n <header className=\"flex flex-col gap-1\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <h1 className=\"text-xl font-semibold\">{t('schedules.title')}</h1>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"ml-auto\"\n onClick={() => refetch()}\n disabled={isFetching}\n aria-label={t('actions.refresh', { ns: 'common' })}\n >\n {isFetching ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <RefreshCcw className=\"h-4 w-4\" />}\n <span className=\"hidden sm:inline\">{t('actions.refresh', { ns: 'common' })}</span>\n </Button>\n </div>\n <p className=\"text-sm text-text-dim\">{t('schedules.subtitle')}</p>\n </header>\n\n <div className=\"flex flex-wrap items-end gap-2\">\n <div className=\"flex flex-col gap-1\">\n <Label htmlFor=\"agent-filter\" className=\"text-xs text-text-dim\">\n {t('schedules.filter.agent')}\n </Label>\n <Input\n id=\"agent-filter\"\n value={agent}\n onChange={(e) => setAgentFilter(e.target.value)}\n placeholder={t('schedules.filter.agentAny')}\n className=\"w-48\"\n />\n </div>\n </div>\n\n <DataTable\n columns={columns}\n rows={schedules}\n getRowId={(r) => String(r.id)}\n loading={isLoading}\n emptyState={\n <EmptyState\n icon={<CalendarClock />}\n title={t('schedules.empty.title')}\n description={t('schedules.empty.description')}\n />\n }\n />\n </div>\n )\n}\n\nfunction formatTime(iso: string | undefined | null): string {\n if (iso == null) return '—'\n try {\n const d = new Date(iso)\n if (Number.isNaN(d.getTime())) return iso\n return d.toLocaleString(undefined, { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' })\n } catch {\n return iso\n }\n}\n"],"names":["CalendarClock","createLucideIcon","schedulesKeys","q","useSchedules","query","useQuery","api","SchedulesRoute","t","useTranslation","params","setParams","useSearchParams","agent","data","isLoading","isFetching","refetch","schedules","setAgentFilter","v","next","columns","useMemo","r","jsxs","jsx","Badge","formatTime","Button","Loader2","RefreshCcw","Label","Input","e","DataTable","EmptyState","iso","d"],"mappings":"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","x_google_ignoreList":[0]}
@@ -1,7 +1,7 @@
1
- import{n as e}from"./index-DXI13nSQ.js";/**
1
+ import{n as e}from"./index-CuNnG-5O.js";/**
2
2
  * @license lucide-react v0.469.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
5
5
  * See the LICENSE file in the root directory of this source tree.
6
6
  */const r=e("Search",[["circle",{cx:"11",cy:"11",r:"8",key:"4ej97u"}],["path",{d:"m21 21-4.3-4.3",key:"1qie3q"}]]);export{r as S};
7
- //# sourceMappingURL=search-DzexDAbr.js.map
7
+ //# sourceMappingURL=search-DJXxphzz.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"search-DzexDAbr.js","sources":["../../node_modules/lucide-react/dist/esm/icons/search.js"],"sourcesContent":["/**\n * @license lucide-react v0.469.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst Search = createLucideIcon(\"Search\", [\n [\"circle\", { cx: \"11\", cy: \"11\", r: \"8\", key: \"4ej97u\" }],\n [\"path\", { d: \"m21 21-4.3-4.3\", key: \"1qie3q\" }]\n]);\n\nexport { Search as default };\n//# sourceMappingURL=search.js.map\n"],"names":["Search","createLucideIcon"],"mappings":"wCAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GASK,MAACA,EAASC,EAAiB,SAAU,CACxC,CAAC,SAAU,CAAE,GAAI,KAAM,GAAI,KAAM,EAAG,IAAK,IAAK,SAAU,EACxD,CAAC,OAAQ,CAAE,EAAG,iBAAkB,IAAK,QAAQ,CAAE,CACjD,CAAC","x_google_ignoreList":[0]}
1
+ {"version":3,"file":"search-DJXxphzz.js","sources":["../../node_modules/lucide-react/dist/esm/icons/search.js"],"sourcesContent":["/**\n * @license lucide-react v0.469.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst Search = createLucideIcon(\"Search\", [\n [\"circle\", { cx: \"11\", cy: \"11\", r: \"8\", key: \"4ej97u\" }],\n [\"path\", { d: \"m21 21-4.3-4.3\", key: \"1qie3q\" }]\n]);\n\nexport { Search as default };\n//# sourceMappingURL=search.js.map\n"],"names":["Search","createLucideIcon"],"mappings":"wCAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GASK,MAACA,EAASC,EAAiB,SAAU,CACxC,CAAC,SAAU,CAAE,GAAI,KAAM,GAAI,KAAM,EAAG,IAAK,IAAK,SAAU,EACxD,CAAC,OAAQ,CAAE,EAAG,iBAAkB,IAAK,QAAQ,CAAE,CACjD,CAAC","x_google_ignoreList":[0]}
@@ -1,4 +1,4 @@
1
- import{n as T,u as E,j as e,B as g,k as j,l as v,L as D,I}from"./index-DXI13nSQ.js";import{r as w}from"./react-Cb2sDjhD.js";import{u as L,s as _,a as z,t as F}from"./use-settings-DJlVLnjo.js";import{L as K}from"./loader-circle-BS5FFFg-.js";import{R as U}from"./refresh-ccw-BPKXoMZa.js";import{X as M}from"./x-OHUicFfn.js";import{S as V}from"./save-BXCmgeEj.js";import{A as q}from"./arrow-up-DtrvOH7Z.js";import{A as J}from"./arrow-down-OdSfpgG4.js";import{C as W}from"./circle-check-VgYKnBhy.js";import{C as G}from"./circle-x-8dhkvHlT.js";import"./useQuery-CEwGD94N.js";/**
1
+ import{n as T,u as E,j as e,B as g,k as j,l as v,L as D,I}from"./index-CuNnG-5O.js";import{r as w}from"./react-Cb2sDjhD.js";import{u as L,s as _,a as z,t as F}from"./use-settings-D3vI1Dwg.js";import{L as K}from"./loader-circle-CVEo3Hwx.js";import{R as U}from"./refresh-ccw-DZHRYuFA.js";import{X as M}from"./x-D_eFtxsK.js";import{S as V}from"./save-DQlBAXAl.js";import{A as q}from"./arrow-up-B47-Pj-X.js";import{A as J}from"./arrow-down-CL6JRd2c.js";import{C as W}from"./circle-check-D98QQwz0.js";import{C as G}from"./circle-x-Et3rwVPd.js";import"./useQuery-DIlm0vJ-.js";/**
2
2
  * @license lucide-react v0.469.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -14,4 +14,4 @@ import{n as T,u as E,j as e,B as g,k as j,l as v,L as D,I}from"./index-DXI13nSQ.
14
14
  * This source code is licensed under the ISC license.
15
15
  * See the LICENSE file in the root directory of this source tree.
16
16
  */const Y=T("Key",[["path",{d:"m15.5 7.5 2.3 2.3a1 1 0 0 0 1.4 0l2.1-2.1a1 1 0 0 0 0-1.4L19 4",key:"g0fldk"}],["path",{d:"m21 2-9.6 9.6",key:"1j0ho8"}],["circle",{cx:"7.5",cy:"15.5",r:"5.5",key:"yqb3hr"}]]);function Z(r,a){const o=(r??"").split(",").map(i=>i.trim()).filter(Boolean),h=new Set(a.map(i=>i.name)),m=new Set,x=[];for(const i of o)!h.has(i)||m.has(i)||(x.push({name:i,enabled:!0}),m.add(i));for(const i of a)m.has(i.name)||x.push({name:i.name,enabled:!1});return x}function B(r){return r.filter(a=>a.enabled).map(a=>a.name).join(",")}function ue(){const{t:r}=E(["settings","common"]),a=L({reveal:!1}),o=_(),h=z(),m=F(),x=a.data?.env??{},i=o.data?.providers??[],y=o.data?.chain??[],d=w.useMemo(()=>({chain:Z(x.IMHUB_WEB_SEARCH_PROVIDERS,i),keys:{},keysTouched:{}}),[x.IMHUB_WEB_SEARCH_PROVIDERS,i]),[l,u]=w.useState(d),[b,C]=w.useState("");w.useEffect(()=>{const s=JSON.stringify(d);s!==b&&(u(d),C(s))},[d,b]);const k=JSON.stringify(l.chain)!==JSON.stringify(d.chain)||Object.keys(l.keysTouched).length>0;function N(s,t){u(c=>{const n=[...c.chain],f=s+t;return f<0||f>=n.length?c:([n[s],n[f]]=[n[f],n[s]],{...c,chain:n})})}function S(s,t){u(c=>{const n=[...c.chain];return n[s]={...n[s],enabled:t},{...c,chain:n}})}function p(s,t){u(c=>({...c,keys:{...c.keys,[s]:t},keysTouched:{...c.keysTouched,[s]:!0}}))}function P(s){u(t=>({...t,keys:{...t.keys,[s]:""},keysTouched:{...t.keysTouched,[s]:!0}}))}async function A(){const s={},t=B(l.chain),c=B(d.chain);t!==c&&(s.IMHUB_WEB_SEARCH_PROVIDERS=t||null);for(const[n,f]of Object.entries(l.keysTouched)){if(!f)continue;const R=l.keys[n]??"";s[n]=R.trim()===""?null:R}if(Object.keys(s).length!==0)try{await h.mutateAsync({updates:s}),o.refetch(),v.success(r("search.savedToast")),u(n=>({...n,keys:{},keysTouched:{}}))}catch(n){v.error(n?.message??String(n))}}function H(){u(d)}async function O(s){try{const t=await m.mutateAsync({provider:s,query:"agim ping"});t.ok?v.success(r("search.testOk",{provider:s,hits:t.hits,ms:t.latencyMs})):v.error(r("search.testFail",{provider:s,error:t.error??"unknown error"}))}catch(t){v.error(t?.message??String(t))}}return e.jsxs("div",{className:"mx-auto flex max-w-4xl flex-col gap-6",children:[e.jsxs("header",{className:"flex items-center gap-3",children:[e.jsx("h1",{className:"text-xl font-semibold",children:r("search.title")}),e.jsxs(g,{variant:"ghost",size:"sm",className:"ml-auto",onClick:()=>{a.refetch(),o.refetch()},disabled:a.isFetching||o.isFetching,"aria-label":r("actions.refresh",{ns:"common"}),children:[a.isFetching||o.isFetching?e.jsx(K,{className:"h-4 w-4 animate-spin"}):e.jsx(U,{className:"h-4 w-4"}),e.jsx("span",{className:"hidden sm:inline",children:r("search.refresh")})]})]}),e.jsx("p",{className:"text-sm text-text-dim",children:r("search.subtitle")}),e.jsx($,{live:y}),a.isLoading||o.isLoading?e.jsx("div",{className:"h-64 rounded-md bg-surface-2 animate-pulse"}):e.jsx("section",{className:"flex flex-col gap-2",children:l.chain.map((s,t)=>{const c=i.find(n=>n.name===s.name);return e.jsx(ee,{info:c,entry:s,idx:t,count:l.chain.length,keyValue:c?.envKey?l.keys[c.envKey]:void 0,keyTouched:c?.envKey?!!l.keysTouched[c.envKey]:!1,onMove:n=>N(t,n),onToggle:n=>S(t,n),onSetKey:n=>c?.envKey&&p(c.envKey,n),onClearKey:()=>c?.envKey&&P(c.envKey),onTest:()=>O(s.name),testing:m.isPending},s.name)})}),k&&e.jsxs("div",{className:"sticky bottom-2 flex items-center gap-2 rounded-md border border-border bg-surface px-4 py-3 shadow",children:[e.jsx(j,{variant:"info",children:r("search.unsaved")}),e.jsxs(g,{variant:"ghost",size:"sm",className:"ml-auto",onClick:H,disabled:h.isPending,children:[e.jsx(M,{className:"h-4 w-4"})," ",r("search.discard")]}),e.jsxs(g,{size:"sm",onClick:()=>void A(),disabled:h.isPending,children:[h.isPending?e.jsx(K,{className:"h-4 w-4 animate-spin"}):e.jsx(V,{className:"h-4 w-4"}),r("search.save")]})]})]})}function $({live:r}){const{t:a}=E("settings");return e.jsxs("section",{className:"rounded-md border border-border bg-surface px-4 py-3",children:[e.jsxs("div",{className:"flex items-center gap-2 text-sm",children:[e.jsx(X,{className:"h-4 w-4 text-text-dim"}),e.jsx("span",{className:"text-text-dim",children:a("search.chainLabel")}),r.length===0?e.jsx(j,{variant:"outline",children:a("search.chainEmpty")}):e.jsx("span",{className:"font-mono text-xs",children:r.join(" → ")})]}),e.jsx("p",{className:"mt-1 text-[11px] text-text-dim",children:a("search.chainHint")})]})}function ee(r){const{t:a}=E("settings"),{info:o,entry:h,idx:m,count:x,keyValue:i,keyTouched:y,onMove:d,onToggle:l,onSetKey:u,onClearKey:b,onTest:C,testing:k}=r,N=!!o?.envKey,S=!!o?.available||y&&!!i;return e.jsxs("div",{className:"rounded-md border border-border bg-surface p-3",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsxs("div",{className:"flex flex-col",children:[e.jsx("button",{type:"button",className:"h-5 w-5 rounded text-text-dim hover:bg-surface-hover disabled:opacity-30",onClick:()=>d(-1),disabled:m===0,"aria-label":a("search.moveUp"),children:e.jsx(q,{className:"h-4 w-4"})}),e.jsx("button",{type:"button",className:"h-5 w-5 rounded text-text-dim hover:bg-surface-hover disabled:opacity-30",onClick:()=>d(1),disabled:m===x-1,"aria-label":a("search.moveDown"),children:e.jsx(J,{className:"h-4 w-4"})})]}),e.jsxs("label",{className:"flex items-center gap-2 text-sm",children:[e.jsx("input",{type:"checkbox",checked:h.enabled,onChange:p=>l(p.target.checked),className:"h-4 w-4 accent-accent cursor-pointer"}),e.jsx("span",{className:"font-mono text-sm",children:h.name})]}),N?S?e.jsxs(j,{variant:"success",children:[e.jsx(W,{className:"mr-1 h-3 w-3"})," ",a("search.configured")]}):e.jsxs(j,{variant:"warning",children:[e.jsx(G,{className:"mr-1 h-3 w-3"})," ",a("search.missingKey")]}):e.jsx(j,{variant:"outline",children:a("search.noKey")}),e.jsx("div",{className:"ml-auto flex items-center gap-1",children:e.jsxs(g,{size:"sm",variant:"ghost",onClick:C,disabled:k||!h.enabled,"aria-label":a("search.testBtn"),children:[k?e.jsx(K,{className:"h-4 w-4 animate-spin"}):e.jsx(Q,{className:"h-4 w-4"}),e.jsx("span",{className:"hidden sm:inline",children:a("search.testBtn")})]})})]}),N&&e.jsxs("div",{className:"mt-3 flex flex-col gap-1",children:[e.jsxs(D,{className:"text-xs font-medium",children:[e.jsx(Y,{className:"mr-1 inline h-3 w-3"}),o?.envKey]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(I,{type:"password",value:i??"",onChange:p=>u(p.target.value),placeholder:o?.available&&!y?a("search.placeholderConfigured"):a("search.placeholderEnter"),className:"font-mono text-xs"}),o?.available&&!y&&e.jsx(g,{size:"sm",variant:"ghost",onClick:b,"aria-label":a("search.clearKey"),children:e.jsx(M,{className:"h-4 w-4"})})]}),e.jsx("p",{className:"text-[11px] text-text-dim",children:a("search.keyHint",{envKey:o?.envKey})})]})]})}export{ue as default};
17
- //# sourceMappingURL=search-B-wDhzjs.js.map
17
+ //# sourceMappingURL=search-dBGBHpnH.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"search-B-wDhzjs.js","sources":["../../node_modules/lucide-react/dist/esm/icons/flask-conical.js","../../node_modules/lucide-react/dist/esm/icons/globe.js","../../node_modules/lucide-react/dist/esm/icons/key.js","../../src/routes/settings/search.tsx"],"sourcesContent":["/**\n * @license lucide-react v0.469.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst FlaskConical = createLucideIcon(\"FlaskConical\", [\n [\n \"path\",\n {\n d: \"M14 2v6a2 2 0 0 0 .245.96l5.51 10.08A2 2 0 0 1 18 22H6a2 2 0 0 1-1.755-2.96l5.51-10.08A2 2 0 0 0 10 8V2\",\n key: \"18mbvz\"\n }\n ],\n [\"path\", { d: \"M6.453 15h11.094\", key: \"3shlmq\" }],\n [\"path\", { d: \"M8.5 2h7\", key: \"csnxdl\" }]\n]);\n\nexport { FlaskConical as default };\n//# sourceMappingURL=flask-conical.js.map\n","/**\n * @license lucide-react v0.469.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst Globe = createLucideIcon(\"Globe\", [\n [\"circle\", { cx: \"12\", cy: \"12\", r: \"10\", key: \"1mglay\" }],\n [\"path\", { d: \"M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20\", key: \"13o1zl\" }],\n [\"path\", { d: \"M2 12h20\", key: \"9i4pu4\" }]\n]);\n\nexport { Globe as default };\n//# sourceMappingURL=globe.js.map\n","/**\n * @license lucide-react v0.469.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst Key = createLucideIcon(\"Key\", [\n [\"path\", { d: \"m15.5 7.5 2.3 2.3a1 1 0 0 0 1.4 0l2.1-2.1a1 1 0 0 0 0-1.4L19 4\", key: \"g0fldk\" }],\n [\"path\", { d: \"m21 2-9.6 9.6\", key: \"1j0ho8\" }],\n [\"circle\", { cx: \"7.5\", cy: \"15.5\", r: \"5.5\", key: \"yqb3hr\" }]\n]);\n\nexport { Key as default };\n//# sourceMappingURL=key.js.map\n","/**\n * /settings/search — web_search provider config (v1.2.126).\n *\n * One row per registered provider (Tavily / Brave / Serper / Exa /\n * DuckDuckGo / Metaso): API-key input (masked-by-default), enable\n * toggle (= \"is this name in the chain?\"), reorder via up / down,\n * one-shot test button. The chain preview at the top shows what\n * IMHUB_WEB_SEARCH_PROVIDERS resolves to right now.\n *\n * Persistence: API keys go via PUT /api/env (which masks GET +\n * refuses echoed masks on PUT); the chain order is a single CSV\n * env key — same endpoint. Save is one round-trip per dirty key.\n */\n\nimport { useEffect, useMemo, useState } from 'react'\nimport { useTranslation } from 'react-i18next'\nimport { toast } from 'sonner'\nimport {\n ArrowDown, ArrowUp, CheckCircle2, FlaskConical, Globe, Key,\n Loader2, RefreshCcw, Save, X, XCircle,\n} from 'lucide-react'\n\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { Input } from '@/components/ui/input'\nimport { Label } from '@/components/ui/label'\nimport {\n useEnv,\n useSearchProviders,\n useTestSearchProvider,\n useUpdateEnv,\n} from '@/hooks/use-settings'\nimport type { SearchProviderInfo } from '@/types/api'\n\ninterface ChainEntry {\n name: string\n enabled: boolean\n}\n\ninterface Draft {\n /** Provider chain — every known provider, with `enabled`\n * reflecting \"is name in the CSV\". Order is the row order. */\n chain: ChainEntry[]\n /** Each provider's API-key input. Keyed by envKey (e.g.\n * TAVILY_API_KEY). Holds either '' (clear) or the new plaintext\n * the operator typed. We never load values back from the server. */\n keys: Record<string, string>\n /** Tracks which key fields the operator actually touched — so\n * Save only sends the diff and untouched masked keys are left\n * alone. */\n keysTouched: Record<string, boolean>\n}\n\nfunction envChainOrder(envValue: string | undefined, providers: SearchProviderInfo[]): ChainEntry[] {\n // Parse the CSV chain; preserve operator order; tail-append any\n // provider not in the CSV as disabled so they remain renderable.\n const raw = (envValue ?? '').split(',').map((s) => s.trim()).filter(Boolean)\n const known = new Set(providers.map((p) => p.name))\n const seen = new Set<string>()\n const out: ChainEntry[] = []\n for (const name of raw) {\n if (!known.has(name) || seen.has(name)) continue\n out.push({ name, enabled: true })\n seen.add(name)\n }\n for (const p of providers) {\n if (seen.has(p.name)) continue\n out.push({ name: p.name, enabled: false })\n }\n return out\n}\n\nfunction chainToCsv(chain: ChainEntry[]): string {\n return chain.filter((c) => c.enabled).map((c) => c.name).join(',')\n}\n\nexport default function SettingsSearchRoute(): JSX.Element {\n const { t } = useTranslation(['settings', 'common'])\n const envQuery = useEnv({ reveal: false })\n const providersQuery = useSearchProviders()\n const updateEnv = useUpdateEnv()\n const testProvider = useTestSearchProvider()\n\n const envData = envQuery.data?.env ?? {}\n const providers = providersQuery.data?.providers ?? []\n const live = providersQuery.data?.chain ?? []\n\n const initial = useMemo<Draft>(\n () => ({\n chain: envChainOrder(envData.IMHUB_WEB_SEARCH_PROVIDERS, providers),\n keys: {},\n keysTouched: {},\n }),\n [envData.IMHUB_WEB_SEARCH_PROVIDERS, providers],\n )\n\n const [draft, setDraft] = useState<Draft>(initial)\n const [syncedHash, setSyncedHash] = useState('')\n\n useEffect(() => {\n const hash = JSON.stringify(initial)\n if (hash !== syncedHash) {\n setDraft(initial)\n setSyncedHash(hash)\n }\n }, [initial, syncedHash])\n\n const isDirty =\n JSON.stringify(draft.chain) !== JSON.stringify(initial.chain) ||\n Object.keys(draft.keysTouched).length > 0\n\n function moveRow(idx: number, delta: -1 | 1): void {\n setDraft((d) => {\n const next = [...d.chain]\n const swap = idx + delta\n if (swap < 0 || swap >= next.length) return d\n ;[next[idx], next[swap]] = [next[swap], next[idx]]\n return { ...d, chain: next }\n })\n }\n\n function toggleRow(idx: number, enabled: boolean): void {\n setDraft((d) => {\n const next = [...d.chain]\n next[idx] = { ...next[idx], enabled }\n return { ...d, chain: next }\n })\n }\n\n function setKey(envKey: string, value: string): void {\n setDraft((d) => ({\n ...d,\n keys: { ...d.keys, [envKey]: value },\n keysTouched: { ...d.keysTouched, [envKey]: true },\n }))\n }\n\n function clearKey(envKey: string): void {\n setDraft((d) => ({\n ...d,\n keys: { ...d.keys, [envKey]: '' },\n keysTouched: { ...d.keysTouched, [envKey]: true },\n }))\n }\n\n async function onSave(): Promise<void> {\n const updates: Record<string, string | null> = {}\n const nextCsv = chainToCsv(draft.chain)\n const initialCsv = chainToCsv(initial.chain)\n if (nextCsv !== initialCsv) {\n updates.IMHUB_WEB_SEARCH_PROVIDERS = nextCsv || null\n }\n for (const [envKey, touched] of Object.entries(draft.keysTouched)) {\n if (!touched) continue\n const v = draft.keys[envKey] ?? ''\n updates[envKey] = v.trim() === '' ? null : v\n }\n if (Object.keys(updates).length === 0) return\n try {\n await updateEnv.mutateAsync({ updates })\n // Refetch both — env keys changed availability, chain order changed.\n void providersQuery.refetch()\n toast.success(t('search.savedToast'))\n setDraft((d) => ({ ...d, keys: {}, keysTouched: {} }))\n } catch (err) {\n toast.error(((err as Error)?.message ?? String(err)))\n }\n }\n\n function onDiscard(): void {\n setDraft(initial)\n }\n\n async function onTest(provider: string): Promise<void> {\n try {\n const result = await testProvider.mutateAsync({\n provider,\n // Short, harmless probe — the dispatcher only needs to\n // verify the upstream auth + transport.\n query: 'agim ping',\n })\n if (result.ok) {\n toast.success(\n t('search.testOk', { provider, hits: result.hits, ms: result.latencyMs }),\n )\n } else {\n toast.error(\n t('search.testFail', { provider, error: result.error ?? 'unknown error' }),\n )\n }\n } catch (err) {\n toast.error((err as Error)?.message ?? String(err))\n }\n }\n\n return (\n <div className=\"mx-auto flex max-w-4xl flex-col gap-6\">\n <header className=\"flex items-center gap-3\">\n <h1 className=\"text-xl font-semibold\">{t('search.title')}</h1>\n <Button\n variant=\"ghost\" size=\"sm\" className=\"ml-auto\"\n onClick={() => {\n void envQuery.refetch()\n void providersQuery.refetch()\n }}\n disabled={envQuery.isFetching || providersQuery.isFetching}\n aria-label={t('actions.refresh', { ns: 'common' })}\n >\n {envQuery.isFetching || providersQuery.isFetching\n ? <Loader2 className=\"h-4 w-4 animate-spin\" />\n : <RefreshCcw className=\"h-4 w-4\" />}\n <span className=\"hidden sm:inline\">{t('search.refresh')}</span>\n </Button>\n </header>\n <p className=\"text-sm text-text-dim\">{t('search.subtitle')}</p>\n\n <ChainPreview live={live} />\n\n {envQuery.isLoading || providersQuery.isLoading ? (\n <div className=\"h-64 rounded-md bg-surface-2 animate-pulse\" />\n ) : (\n <section className=\"flex flex-col gap-2\">\n {draft.chain.map((entry, idx) => {\n const info = providers.find((p) => p.name === entry.name)\n return (\n <ProviderRow\n key={entry.name}\n info={info}\n entry={entry}\n idx={idx}\n count={draft.chain.length}\n keyValue={info?.envKey ? draft.keys[info.envKey] : undefined}\n keyTouched={info?.envKey ? !!draft.keysTouched[info.envKey] : false}\n onMove={(d) => moveRow(idx, d)}\n onToggle={(v) => toggleRow(idx, v)}\n onSetKey={(v) => info?.envKey && setKey(info.envKey, v)}\n onClearKey={() => info?.envKey && clearKey(info.envKey)}\n onTest={() => onTest(entry.name)}\n testing={testProvider.isPending}\n />\n )\n })}\n </section>\n )}\n\n {isDirty && (\n <div className=\"sticky bottom-2 flex items-center gap-2 rounded-md border border-border bg-surface px-4 py-3 shadow\">\n <Badge variant=\"info\">{t('search.unsaved')}</Badge>\n <Button\n variant=\"ghost\" size=\"sm\" className=\"ml-auto\"\n onClick={onDiscard}\n disabled={updateEnv.isPending}\n >\n <X className=\"h-4 w-4\" /> {t('search.discard')}\n </Button>\n <Button\n size=\"sm\"\n onClick={() => void onSave()}\n disabled={updateEnv.isPending}\n >\n {updateEnv.isPending\n ? <Loader2 className=\"h-4 w-4 animate-spin\" />\n : <Save className=\"h-4 w-4\" />}\n {t('search.save')}\n </Button>\n </div>\n )}\n </div>\n )\n}\n\nfunction ChainPreview({ live }: { live: string[] }): JSX.Element {\n const { t } = useTranslation('settings')\n return (\n <section className=\"rounded-md border border-border bg-surface px-4 py-3\">\n <div className=\"flex items-center gap-2 text-sm\">\n <Globe className=\"h-4 w-4 text-text-dim\" />\n <span className=\"text-text-dim\">{t('search.chainLabel')}</span>\n {live.length === 0 ? (\n <Badge variant=\"outline\">{t('search.chainEmpty')}</Badge>\n ) : (\n <span className=\"font-mono text-xs\">{live.join(' → ')}</span>\n )}\n </div>\n <p className=\"mt-1 text-[11px] text-text-dim\">{t('search.chainHint')}</p>\n </section>\n )\n}\n\ninterface ProviderRowProps {\n info: SearchProviderInfo | undefined\n entry: ChainEntry\n idx: number\n count: number\n keyValue: string | undefined\n keyTouched: boolean\n onMove: (delta: -1 | 1) => void\n onToggle: (v: boolean) => void\n onSetKey: (v: string) => void\n onClearKey: () => void\n onTest: () => void\n testing: boolean\n}\n\nfunction ProviderRow(props: ProviderRowProps): JSX.Element {\n const { t } = useTranslation('settings')\n const { info, entry, idx, count, keyValue, keyTouched, onMove, onToggle,\n onSetKey, onClearKey, onTest, testing } = props\n const needsKey = !!info?.envKey\n const available = !!info?.available || (keyTouched && !!keyValue)\n return (\n <div className=\"rounded-md border border-border bg-surface p-3\">\n <div className=\"flex items-center gap-2\">\n <div className=\"flex flex-col\">\n <button\n type=\"button\"\n className=\"h-5 w-5 rounded text-text-dim hover:bg-surface-hover disabled:opacity-30\"\n onClick={() => onMove(-1)}\n disabled={idx === 0}\n aria-label={t('search.moveUp')}\n >\n <ArrowUp className=\"h-4 w-4\" />\n </button>\n <button\n type=\"button\"\n className=\"h-5 w-5 rounded text-text-dim hover:bg-surface-hover disabled:opacity-30\"\n onClick={() => onMove(1)}\n disabled={idx === count - 1}\n aria-label={t('search.moveDown')}\n >\n <ArrowDown className=\"h-4 w-4\" />\n </button>\n </div>\n\n <label className=\"flex items-center gap-2 text-sm\">\n <input\n type=\"checkbox\"\n checked={entry.enabled}\n onChange={(e) => onToggle(e.target.checked)}\n className=\"h-4 w-4 accent-accent cursor-pointer\"\n />\n <span className=\"font-mono text-sm\">{entry.name}</span>\n </label>\n\n {!needsKey ? (\n <Badge variant=\"outline\">{t('search.noKey')}</Badge>\n ) : available ? (\n <Badge variant=\"success\">\n <CheckCircle2 className=\"mr-1 h-3 w-3\" /> {t('search.configured')}\n </Badge>\n ) : (\n <Badge variant=\"warning\">\n <XCircle className=\"mr-1 h-3 w-3\" /> {t('search.missingKey')}\n </Badge>\n )}\n\n <div className=\"ml-auto flex items-center gap-1\">\n <Button\n size=\"sm\" variant=\"ghost\"\n onClick={onTest}\n disabled={testing || !entry.enabled}\n aria-label={t('search.testBtn')}\n >\n {testing\n ? <Loader2 className=\"h-4 w-4 animate-spin\" />\n : <FlaskConical className=\"h-4 w-4\" />}\n <span className=\"hidden sm:inline\">{t('search.testBtn')}</span>\n </Button>\n </div>\n </div>\n\n {needsKey && (\n <div className=\"mt-3 flex flex-col gap-1\">\n <Label className=\"text-xs font-medium\">\n <Key className=\"mr-1 inline h-3 w-3\" />\n {info?.envKey}\n </Label>\n <div className=\"flex items-center gap-2\">\n <Input\n type=\"password\"\n value={keyValue ?? ''}\n onChange={(e) => onSetKey(e.target.value)}\n placeholder={\n info?.available && !keyTouched\n ? t('search.placeholderConfigured')\n : t('search.placeholderEnter')\n }\n className=\"font-mono text-xs\"\n />\n {info?.available && !keyTouched && (\n <Button\n size=\"sm\" variant=\"ghost\"\n onClick={onClearKey}\n aria-label={t('search.clearKey')}\n >\n <X className=\"h-4 w-4\" />\n </Button>\n )}\n </div>\n <p className=\"text-[11px] text-text-dim\">\n {t('search.keyHint', { envKey: info?.envKey })}\n </p>\n </div>\n )}\n </div>\n )\n}\n"],"names":["FlaskConical","createLucideIcon","Globe","Key","envChainOrder","envValue","providers","raw","s","known","p","seen","out","name","chainToCsv","chain","c","SettingsSearchRoute","t","useTranslation","envQuery","useEnv","providersQuery","useSearchProviders","updateEnv","useUpdateEnv","testProvider","useTestSearchProvider","envData","live","initial","useMemo","draft","setDraft","useState","syncedHash","setSyncedHash","useEffect","hash","isDirty","moveRow","idx","delta","d","next","swap","toggleRow","enabled","setKey","envKey","value","clearKey","onSave","updates","nextCsv","initialCsv","touched","v","toast","err","onDiscard","onTest","provider","result","jsxs","jsx","Button","Loader2","RefreshCcw","ChainPreview","entry","info","ProviderRow","Badge","X","Save","props","count","keyValue","keyTouched","onMove","onToggle","onSetKey","onClearKey","testing","needsKey","available","ArrowUp","ArrowDown","e","CheckCircle2","XCircle","Label","Input"],"mappings":"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","x_google_ignoreList":[0,1,2]}
1
+ {"version":3,"file":"search-dBGBHpnH.js","sources":["../../node_modules/lucide-react/dist/esm/icons/flask-conical.js","../../node_modules/lucide-react/dist/esm/icons/globe.js","../../node_modules/lucide-react/dist/esm/icons/key.js","../../src/routes/settings/search.tsx"],"sourcesContent":["/**\n * @license lucide-react v0.469.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst FlaskConical = createLucideIcon(\"FlaskConical\", [\n [\n \"path\",\n {\n d: \"M14 2v6a2 2 0 0 0 .245.96l5.51 10.08A2 2 0 0 1 18 22H6a2 2 0 0 1-1.755-2.96l5.51-10.08A2 2 0 0 0 10 8V2\",\n key: \"18mbvz\"\n }\n ],\n [\"path\", { d: \"M6.453 15h11.094\", key: \"3shlmq\" }],\n [\"path\", { d: \"M8.5 2h7\", key: \"csnxdl\" }]\n]);\n\nexport { FlaskConical as default };\n//# sourceMappingURL=flask-conical.js.map\n","/**\n * @license lucide-react v0.469.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst Globe = createLucideIcon(\"Globe\", [\n [\"circle\", { cx: \"12\", cy: \"12\", r: \"10\", key: \"1mglay\" }],\n [\"path\", { d: \"M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20\", key: \"13o1zl\" }],\n [\"path\", { d: \"M2 12h20\", key: \"9i4pu4\" }]\n]);\n\nexport { Globe as default };\n//# sourceMappingURL=globe.js.map\n","/**\n * @license lucide-react v0.469.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst Key = createLucideIcon(\"Key\", [\n [\"path\", { d: \"m15.5 7.5 2.3 2.3a1 1 0 0 0 1.4 0l2.1-2.1a1 1 0 0 0 0-1.4L19 4\", key: \"g0fldk\" }],\n [\"path\", { d: \"m21 2-9.6 9.6\", key: \"1j0ho8\" }],\n [\"circle\", { cx: \"7.5\", cy: \"15.5\", r: \"5.5\", key: \"yqb3hr\" }]\n]);\n\nexport { Key as default };\n//# sourceMappingURL=key.js.map\n","/**\n * /settings/search — web_search provider config (v1.2.126).\n *\n * One row per registered provider (Tavily / Brave / Serper / Exa /\n * DuckDuckGo / Metaso): API-key input (masked-by-default), enable\n * toggle (= \"is this name in the chain?\"), reorder via up / down,\n * one-shot test button. The chain preview at the top shows what\n * IMHUB_WEB_SEARCH_PROVIDERS resolves to right now.\n *\n * Persistence: API keys go via PUT /api/env (which masks GET +\n * refuses echoed masks on PUT); the chain order is a single CSV\n * env key — same endpoint. Save is one round-trip per dirty key.\n */\n\nimport { useEffect, useMemo, useState } from 'react'\nimport { useTranslation } from 'react-i18next'\nimport { toast } from 'sonner'\nimport {\n ArrowDown, ArrowUp, CheckCircle2, FlaskConical, Globe, Key,\n Loader2, RefreshCcw, Save, X, XCircle,\n} from 'lucide-react'\n\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { Input } from '@/components/ui/input'\nimport { Label } from '@/components/ui/label'\nimport {\n useEnv,\n useSearchProviders,\n useTestSearchProvider,\n useUpdateEnv,\n} from '@/hooks/use-settings'\nimport type { SearchProviderInfo } from '@/types/api'\n\ninterface ChainEntry {\n name: string\n enabled: boolean\n}\n\ninterface Draft {\n /** Provider chain — every known provider, with `enabled`\n * reflecting \"is name in the CSV\". Order is the row order. */\n chain: ChainEntry[]\n /** Each provider's API-key input. Keyed by envKey (e.g.\n * TAVILY_API_KEY). Holds either '' (clear) or the new plaintext\n * the operator typed. We never load values back from the server. */\n keys: Record<string, string>\n /** Tracks which key fields the operator actually touched — so\n * Save only sends the diff and untouched masked keys are left\n * alone. */\n keysTouched: Record<string, boolean>\n}\n\nfunction envChainOrder(envValue: string | undefined, providers: SearchProviderInfo[]): ChainEntry[] {\n // Parse the CSV chain; preserve operator order; tail-append any\n // provider not in the CSV as disabled so they remain renderable.\n const raw = (envValue ?? '').split(',').map((s) => s.trim()).filter(Boolean)\n const known = new Set(providers.map((p) => p.name))\n const seen = new Set<string>()\n const out: ChainEntry[] = []\n for (const name of raw) {\n if (!known.has(name) || seen.has(name)) continue\n out.push({ name, enabled: true })\n seen.add(name)\n }\n for (const p of providers) {\n if (seen.has(p.name)) continue\n out.push({ name: p.name, enabled: false })\n }\n return out\n}\n\nfunction chainToCsv(chain: ChainEntry[]): string {\n return chain.filter((c) => c.enabled).map((c) => c.name).join(',')\n}\n\nexport default function SettingsSearchRoute(): JSX.Element {\n const { t } = useTranslation(['settings', 'common'])\n const envQuery = useEnv({ reveal: false })\n const providersQuery = useSearchProviders()\n const updateEnv = useUpdateEnv()\n const testProvider = useTestSearchProvider()\n\n const envData = envQuery.data?.env ?? {}\n const providers = providersQuery.data?.providers ?? []\n const live = providersQuery.data?.chain ?? []\n\n const initial = useMemo<Draft>(\n () => ({\n chain: envChainOrder(envData.IMHUB_WEB_SEARCH_PROVIDERS, providers),\n keys: {},\n keysTouched: {},\n }),\n [envData.IMHUB_WEB_SEARCH_PROVIDERS, providers],\n )\n\n const [draft, setDraft] = useState<Draft>(initial)\n const [syncedHash, setSyncedHash] = useState('')\n\n useEffect(() => {\n const hash = JSON.stringify(initial)\n if (hash !== syncedHash) {\n setDraft(initial)\n setSyncedHash(hash)\n }\n }, [initial, syncedHash])\n\n const isDirty =\n JSON.stringify(draft.chain) !== JSON.stringify(initial.chain) ||\n Object.keys(draft.keysTouched).length > 0\n\n function moveRow(idx: number, delta: -1 | 1): void {\n setDraft((d) => {\n const next = [...d.chain]\n const swap = idx + delta\n if (swap < 0 || swap >= next.length) return d\n ;[next[idx], next[swap]] = [next[swap], next[idx]]\n return { ...d, chain: next }\n })\n }\n\n function toggleRow(idx: number, enabled: boolean): void {\n setDraft((d) => {\n const next = [...d.chain]\n next[idx] = { ...next[idx], enabled }\n return { ...d, chain: next }\n })\n }\n\n function setKey(envKey: string, value: string): void {\n setDraft((d) => ({\n ...d,\n keys: { ...d.keys, [envKey]: value },\n keysTouched: { ...d.keysTouched, [envKey]: true },\n }))\n }\n\n function clearKey(envKey: string): void {\n setDraft((d) => ({\n ...d,\n keys: { ...d.keys, [envKey]: '' },\n keysTouched: { ...d.keysTouched, [envKey]: true },\n }))\n }\n\n async function onSave(): Promise<void> {\n const updates: Record<string, string | null> = {}\n const nextCsv = chainToCsv(draft.chain)\n const initialCsv = chainToCsv(initial.chain)\n if (nextCsv !== initialCsv) {\n updates.IMHUB_WEB_SEARCH_PROVIDERS = nextCsv || null\n }\n for (const [envKey, touched] of Object.entries(draft.keysTouched)) {\n if (!touched) continue\n const v = draft.keys[envKey] ?? ''\n updates[envKey] = v.trim() === '' ? null : v\n }\n if (Object.keys(updates).length === 0) return\n try {\n await updateEnv.mutateAsync({ updates })\n // Refetch both — env keys changed availability, chain order changed.\n void providersQuery.refetch()\n toast.success(t('search.savedToast'))\n setDraft((d) => ({ ...d, keys: {}, keysTouched: {} }))\n } catch (err) {\n toast.error(((err as Error)?.message ?? String(err)))\n }\n }\n\n function onDiscard(): void {\n setDraft(initial)\n }\n\n async function onTest(provider: string): Promise<void> {\n try {\n const result = await testProvider.mutateAsync({\n provider,\n // Short, harmless probe — the dispatcher only needs to\n // verify the upstream auth + transport.\n query: 'agim ping',\n })\n if (result.ok) {\n toast.success(\n t('search.testOk', { provider, hits: result.hits, ms: result.latencyMs }),\n )\n } else {\n toast.error(\n t('search.testFail', { provider, error: result.error ?? 'unknown error' }),\n )\n }\n } catch (err) {\n toast.error((err as Error)?.message ?? String(err))\n }\n }\n\n return (\n <div className=\"mx-auto flex max-w-4xl flex-col gap-6\">\n <header className=\"flex items-center gap-3\">\n <h1 className=\"text-xl font-semibold\">{t('search.title')}</h1>\n <Button\n variant=\"ghost\" size=\"sm\" className=\"ml-auto\"\n onClick={() => {\n void envQuery.refetch()\n void providersQuery.refetch()\n }}\n disabled={envQuery.isFetching || providersQuery.isFetching}\n aria-label={t('actions.refresh', { ns: 'common' })}\n >\n {envQuery.isFetching || providersQuery.isFetching\n ? <Loader2 className=\"h-4 w-4 animate-spin\" />\n : <RefreshCcw className=\"h-4 w-4\" />}\n <span className=\"hidden sm:inline\">{t('search.refresh')}</span>\n </Button>\n </header>\n <p className=\"text-sm text-text-dim\">{t('search.subtitle')}</p>\n\n <ChainPreview live={live} />\n\n {envQuery.isLoading || providersQuery.isLoading ? (\n <div className=\"h-64 rounded-md bg-surface-2 animate-pulse\" />\n ) : (\n <section className=\"flex flex-col gap-2\">\n {draft.chain.map((entry, idx) => {\n const info = providers.find((p) => p.name === entry.name)\n return (\n <ProviderRow\n key={entry.name}\n info={info}\n entry={entry}\n idx={idx}\n count={draft.chain.length}\n keyValue={info?.envKey ? draft.keys[info.envKey] : undefined}\n keyTouched={info?.envKey ? !!draft.keysTouched[info.envKey] : false}\n onMove={(d) => moveRow(idx, d)}\n onToggle={(v) => toggleRow(idx, v)}\n onSetKey={(v) => info?.envKey && setKey(info.envKey, v)}\n onClearKey={() => info?.envKey && clearKey(info.envKey)}\n onTest={() => onTest(entry.name)}\n testing={testProvider.isPending}\n />\n )\n })}\n </section>\n )}\n\n {isDirty && (\n <div className=\"sticky bottom-2 flex items-center gap-2 rounded-md border border-border bg-surface px-4 py-3 shadow\">\n <Badge variant=\"info\">{t('search.unsaved')}</Badge>\n <Button\n variant=\"ghost\" size=\"sm\" className=\"ml-auto\"\n onClick={onDiscard}\n disabled={updateEnv.isPending}\n >\n <X className=\"h-4 w-4\" /> {t('search.discard')}\n </Button>\n <Button\n size=\"sm\"\n onClick={() => void onSave()}\n disabled={updateEnv.isPending}\n >\n {updateEnv.isPending\n ? <Loader2 className=\"h-4 w-4 animate-spin\" />\n : <Save className=\"h-4 w-4\" />}\n {t('search.save')}\n </Button>\n </div>\n )}\n </div>\n )\n}\n\nfunction ChainPreview({ live }: { live: string[] }): JSX.Element {\n const { t } = useTranslation('settings')\n return (\n <section className=\"rounded-md border border-border bg-surface px-4 py-3\">\n <div className=\"flex items-center gap-2 text-sm\">\n <Globe className=\"h-4 w-4 text-text-dim\" />\n <span className=\"text-text-dim\">{t('search.chainLabel')}</span>\n {live.length === 0 ? (\n <Badge variant=\"outline\">{t('search.chainEmpty')}</Badge>\n ) : (\n <span className=\"font-mono text-xs\">{live.join(' → ')}</span>\n )}\n </div>\n <p className=\"mt-1 text-[11px] text-text-dim\">{t('search.chainHint')}</p>\n </section>\n )\n}\n\ninterface ProviderRowProps {\n info: SearchProviderInfo | undefined\n entry: ChainEntry\n idx: number\n count: number\n keyValue: string | undefined\n keyTouched: boolean\n onMove: (delta: -1 | 1) => void\n onToggle: (v: boolean) => void\n onSetKey: (v: string) => void\n onClearKey: () => void\n onTest: () => void\n testing: boolean\n}\n\nfunction ProviderRow(props: ProviderRowProps): JSX.Element {\n const { t } = useTranslation('settings')\n const { info, entry, idx, count, keyValue, keyTouched, onMove, onToggle,\n onSetKey, onClearKey, onTest, testing } = props\n const needsKey = !!info?.envKey\n const available = !!info?.available || (keyTouched && !!keyValue)\n return (\n <div className=\"rounded-md border border-border bg-surface p-3\">\n <div className=\"flex items-center gap-2\">\n <div className=\"flex flex-col\">\n <button\n type=\"button\"\n className=\"h-5 w-5 rounded text-text-dim hover:bg-surface-hover disabled:opacity-30\"\n onClick={() => onMove(-1)}\n disabled={idx === 0}\n aria-label={t('search.moveUp')}\n >\n <ArrowUp className=\"h-4 w-4\" />\n </button>\n <button\n type=\"button\"\n className=\"h-5 w-5 rounded text-text-dim hover:bg-surface-hover disabled:opacity-30\"\n onClick={() => onMove(1)}\n disabled={idx === count - 1}\n aria-label={t('search.moveDown')}\n >\n <ArrowDown className=\"h-4 w-4\" />\n </button>\n </div>\n\n <label className=\"flex items-center gap-2 text-sm\">\n <input\n type=\"checkbox\"\n checked={entry.enabled}\n onChange={(e) => onToggle(e.target.checked)}\n className=\"h-4 w-4 accent-accent cursor-pointer\"\n />\n <span className=\"font-mono text-sm\">{entry.name}</span>\n </label>\n\n {!needsKey ? (\n <Badge variant=\"outline\">{t('search.noKey')}</Badge>\n ) : available ? (\n <Badge variant=\"success\">\n <CheckCircle2 className=\"mr-1 h-3 w-3\" /> {t('search.configured')}\n </Badge>\n ) : (\n <Badge variant=\"warning\">\n <XCircle className=\"mr-1 h-3 w-3\" /> {t('search.missingKey')}\n </Badge>\n )}\n\n <div className=\"ml-auto flex items-center gap-1\">\n <Button\n size=\"sm\" variant=\"ghost\"\n onClick={onTest}\n disabled={testing || !entry.enabled}\n aria-label={t('search.testBtn')}\n >\n {testing\n ? <Loader2 className=\"h-4 w-4 animate-spin\" />\n : <FlaskConical className=\"h-4 w-4\" />}\n <span className=\"hidden sm:inline\">{t('search.testBtn')}</span>\n </Button>\n </div>\n </div>\n\n {needsKey && (\n <div className=\"mt-3 flex flex-col gap-1\">\n <Label className=\"text-xs font-medium\">\n <Key className=\"mr-1 inline h-3 w-3\" />\n {info?.envKey}\n </Label>\n <div className=\"flex items-center gap-2\">\n <Input\n type=\"password\"\n value={keyValue ?? ''}\n onChange={(e) => onSetKey(e.target.value)}\n placeholder={\n info?.available && !keyTouched\n ? t('search.placeholderConfigured')\n : t('search.placeholderEnter')\n }\n className=\"font-mono text-xs\"\n />\n {info?.available && !keyTouched && (\n <Button\n size=\"sm\" variant=\"ghost\"\n onClick={onClearKey}\n aria-label={t('search.clearKey')}\n >\n <X className=\"h-4 w-4\" />\n </Button>\n )}\n </div>\n <p className=\"text-[11px] text-text-dim\">\n {t('search.keyHint', { envKey: info?.envKey })}\n </p>\n </div>\n )}\n </div>\n )\n}\n"],"names":["FlaskConical","createLucideIcon","Globe","Key","envChainOrder","envValue","providers","raw","s","known","p","seen","out","name","chainToCsv","chain","c","SettingsSearchRoute","t","useTranslation","envQuery","useEnv","providersQuery","useSearchProviders","updateEnv","useUpdateEnv","testProvider","useTestSearchProvider","envData","live","initial","useMemo","draft","setDraft","useState","syncedHash","setSyncedHash","useEffect","hash","isDirty","moveRow","idx","delta","d","next","swap","toggleRow","enabled","setKey","envKey","value","clearKey","onSave","updates","nextCsv","initialCsv","touched","v","toast","err","onDiscard","onTest","provider","result","jsxs","jsx","Button","Loader2","RefreshCcw","ChainPreview","entry","info","ProviderRow","Badge","X","Save","props","count","keyValue","keyTouched","onMove","onToggle","onSetKey","onClearKey","testing","needsKey","available","ArrowUp","ArrowDown","e","CheckCircle2","XCircle","Label","Input"],"mappings":"0jBAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GASA,MAAMA,EAAeC,EAAiB,eAAgB,CACpD,CACE,OACA,CACE,EAAG,0GACH,IAAK,QACX,CACA,EACE,CAAC,OAAQ,CAAE,EAAG,mBAAoB,IAAK,QAAQ,CAAE,EACjD,CAAC,OAAQ,CAAE,EAAG,WAAY,IAAK,QAAQ,CAAE,CAC3C,CAAC,ECnBD;AAAA;AAAA;AAAA;AAAA;AAAA,GASA,MAAMC,EAAQD,EAAiB,QAAS,CACtC,CAAC,SAAU,CAAE,GAAI,KAAM,GAAI,KAAM,EAAG,KAAM,IAAK,SAAU,EACzD,CAAC,OAAQ,CAAE,EAAG,kDAAmD,IAAK,QAAQ,CAAE,EAChF,CAAC,OAAQ,CAAE,EAAG,WAAY,IAAK,QAAQ,CAAE,CAC3C,CAAC,ECbD;AAAA;AAAA;AAAA;AAAA;AAAA,GASA,MAAME,EAAMF,EAAiB,MAAO,CAClC,CAAC,OAAQ,CAAE,EAAG,iEAAkE,IAAK,QAAQ,CAAE,EAC/F,CAAC,OAAQ,CAAE,EAAG,gBAAiB,IAAK,QAAQ,CAAE,EAC9C,CAAC,SAAU,CAAE,GAAI,MAAO,GAAI,OAAQ,EAAG,MAAO,IAAK,QAAQ,CAAE,CAC/D,CAAC,ECwCD,SAASG,EAAcC,EAA8BC,EAA+C,CAGlG,MAAMC,GAAOF,GAAY,IAAI,MAAM,GAAG,EAAE,IAAKG,GAAMA,EAAE,KAAA,CAAM,EAAE,OAAO,OAAO,EACrEC,EAAQ,IAAI,IAAIH,EAAU,IAAKI,GAAMA,EAAE,IAAI,CAAC,EAC5CC,MAAW,IACXC,EAAoB,CAAA,EAC1B,UAAWC,KAAQN,EACb,CAACE,EAAM,IAAII,CAAI,GAAKF,EAAK,IAAIE,CAAI,IACrCD,EAAI,KAAK,CAAE,KAAAC,EAAM,QAAS,GAAM,EAChCF,EAAK,IAAIE,CAAI,GAEf,UAAWH,KAAKJ,EACVK,EAAK,IAAID,EAAE,IAAI,GACnBE,EAAI,KAAK,CAAE,KAAMF,EAAE,KAAM,QAAS,GAAO,EAE3C,OAAOE,CACT,CAEA,SAASE,EAAWC,EAA6B,CAC/C,OAAOA,EAAM,OAAQC,GAAMA,EAAE,OAAO,EAAE,IAAKA,GAAMA,EAAE,IAAI,EAAE,KAAK,GAAG,CACnE,CAEA,SAAwBC,IAAmC,CACzD,KAAM,CAAE,EAAAC,CAAA,EAAMC,EAAe,CAAC,WAAY,QAAQ,CAAC,EAC7CC,EAAWC,EAAO,CAAE,OAAQ,GAAO,EACnCC,EAAiBC,EAAA,EACjBC,EAAYC,EAAA,EACZC,EAAeC,EAAA,EAEfC,EAAUR,EAAS,MAAM,KAAO,CAAA,EAChCd,EAAYgB,EAAe,MAAM,WAAa,CAAA,EAC9CO,EAAOP,EAAe,MAAM,OAAS,CAAA,EAErCQ,EAAUC,EAAAA,QACd,KAAO,CACL,MAAO3B,EAAcwB,EAAQ,2BAA4BtB,CAAS,EAClE,KAAM,CAAA,EACN,YAAa,CAAA,CAAC,GAEhB,CAACsB,EAAQ,2BAA4BtB,CAAS,CAAA,EAG1C,CAAC0B,EAAOC,CAAQ,EAAIC,EAAAA,SAAgBJ,CAAO,EAC3C,CAACK,EAAYC,CAAa,EAAIF,EAAAA,SAAS,EAAE,EAE/CG,EAAAA,UAAU,IAAM,CACd,MAAMC,EAAO,KAAK,UAAUR,CAAO,EAC/BQ,IAASH,IACXF,EAASH,CAAO,EAChBM,EAAcE,CAAI,EAEtB,EAAG,CAACR,EAASK,CAAU,CAAC,EAExB,MAAMI,EACJ,KAAK,UAAUP,EAAM,KAAK,IAAM,KAAK,UAAUF,EAAQ,KAAK,GAC5D,OAAO,KAAKE,EAAM,WAAW,EAAE,OAAS,EAE1C,SAASQ,EAAQC,EAAaC,EAAqB,CACjDT,EAAUU,GAAM,CACd,MAAMC,EAAO,CAAC,GAAGD,EAAE,KAAK,EAClBE,EAAOJ,EAAMC,EACnB,OAAIG,EAAO,GAAKA,GAAQD,EAAK,OAAeD,GAC3C,CAACC,EAAKH,CAAG,EAAGG,EAAKC,CAAI,CAAC,EAAI,CAACD,EAAKC,CAAI,EAAGD,EAAKH,CAAG,CAAC,EAC1C,CAAE,GAAGE,EAAG,MAAOC,CAAA,EACxB,CAAC,CACH,CAEA,SAASE,EAAUL,EAAaM,EAAwB,CACtDd,EAAUU,GAAM,CACd,MAAMC,EAAO,CAAC,GAAGD,EAAE,KAAK,EACxB,OAAAC,EAAKH,CAAG,EAAI,CAAE,GAAGG,EAAKH,CAAG,EAAG,QAAAM,CAAA,EACrB,CAAE,GAAGJ,EAAG,MAAOC,CAAA,CACxB,CAAC,CACH,CAEA,SAASI,EAAOC,EAAgBC,EAAqB,CACnDjB,EAAUU,IAAO,CACf,GAAGA,EACH,KAAM,CAAE,GAAGA,EAAE,KAAM,CAACM,CAAM,EAAGC,CAAA,EAC7B,YAAa,CAAE,GAAGP,EAAE,YAAa,CAACM,CAAM,EAAG,EAAA,CAAK,EAChD,CACJ,CAEA,SAASE,EAASF,EAAsB,CACtChB,EAAUU,IAAO,CACf,GAAGA,EACH,KAAM,CAAE,GAAGA,EAAE,KAAM,CAACM,CAAM,EAAG,EAAA,EAC7B,YAAa,CAAE,GAAGN,EAAE,YAAa,CAACM,CAAM,EAAG,EAAA,CAAK,EAChD,CACJ,CAEA,eAAeG,GAAwB,CACrC,MAAMC,EAAyC,CAAA,EACzCC,EAAUxC,EAAWkB,EAAM,KAAK,EAChCuB,EAAazC,EAAWgB,EAAQ,KAAK,EACvCwB,IAAYC,IACdF,EAAQ,2BAA6BC,GAAW,MAElD,SAAW,CAACL,EAAQO,CAAO,IAAK,OAAO,QAAQxB,EAAM,WAAW,EAAG,CACjE,GAAI,CAACwB,EAAS,SACd,MAAMC,EAAIzB,EAAM,KAAKiB,CAAM,GAAK,GAChCI,EAAQJ,CAAM,EAAIQ,EAAE,KAAA,IAAW,GAAK,KAAOA,CAC7C,CACA,GAAI,OAAO,KAAKJ,CAAO,EAAE,SAAW,EACpC,GAAI,CACF,MAAM7B,EAAU,YAAY,CAAE,QAAA6B,EAAS,EAElC/B,EAAe,QAAA,EACpBoC,EAAM,QAAQxC,EAAE,mBAAmB,CAAC,EACpCe,EAAUU,IAAO,CAAE,GAAGA,EAAG,KAAM,GAAI,YAAa,CAAA,CAAC,EAAI,CACvD,OAASgB,EAAK,CACZD,EAAM,MAAQC,GAAe,SAAW,OAAOA,CAAG,CAAE,CACtD,CACF,CAEA,SAASC,GAAkB,CACzB3B,EAASH,CAAO,CAClB,CAEA,eAAe+B,EAAOC,EAAiC,CACrD,GAAI,CACF,MAAMC,EAAS,MAAMrC,EAAa,YAAY,CAC5C,SAAAoC,EAGA,MAAO,WAAA,CACR,EACGC,EAAO,GACTL,EAAM,QACJxC,EAAE,gBAAiB,CAAE,SAAA4C,EAAU,KAAMC,EAAO,KAAM,GAAIA,EAAO,SAAA,CAAW,CAAA,EAG1EL,EAAM,MACJxC,EAAE,kBAAmB,CAAE,SAAA4C,EAAU,MAAOC,EAAO,OAAS,gBAAiB,CAAA,CAG/E,OAASJ,EAAK,CACZD,EAAM,MAAOC,GAAe,SAAW,OAAOA,CAAG,CAAC,CACpD,CACF,CAEA,OACEK,EAAAA,KAAC,MAAA,CAAI,UAAU,wCACb,SAAA,CAAAA,EAAAA,KAAC,SAAA,CAAO,UAAU,0BAChB,SAAA,CAAAC,MAAC,KAAA,CAAG,UAAU,wBAAyB,SAAA/C,EAAE,cAAc,EAAE,EACzD8C,EAAAA,KAACE,EAAA,CACC,QAAQ,QAAQ,KAAK,KAAK,UAAU,UACpC,QAAS,IAAM,CACR9C,EAAS,QAAA,EACTE,EAAe,QAAA,CACtB,EACA,SAAUF,EAAS,YAAcE,EAAe,WAChD,aAAYJ,EAAE,kBAAmB,CAAE,GAAI,SAAU,EAEhD,SAAA,CAAAE,EAAS,YAAcE,EAAe,WACnC2C,EAAAA,IAACE,EAAA,CAAQ,UAAU,sBAAA,CAAuB,EAC1CF,EAAAA,IAACG,EAAA,CAAW,UAAU,SAAA,CAAU,QACnC,OAAA,CAAK,UAAU,mBAAoB,SAAAlD,EAAE,gBAAgB,CAAA,CAAE,CAAA,CAAA,CAAA,CAC1D,EACF,QACC,IAAA,CAAE,UAAU,wBAAyB,SAAAA,EAAE,iBAAiB,EAAE,EAE3D+C,MAACI,GAAa,KAAAxC,EAAY,EAEzBT,EAAS,WAAaE,EAAe,UACpC2C,EAAAA,IAAC,MAAA,CAAI,UAAU,4CAAA,CAA6C,EAE5DA,EAAAA,IAAC,UAAA,CAAQ,UAAU,sBAChB,SAAAjC,EAAM,MAAM,IAAI,CAACsC,EAAO7B,IAAQ,CAC/B,MAAM8B,EAAOjE,EAAU,KAAMI,GAAMA,EAAE,OAAS4D,EAAM,IAAI,EACxD,OACEL,EAAAA,IAACO,GAAA,CAEC,KAAAD,EACA,MAAAD,EACA,IAAA7B,EACA,MAAOT,EAAM,MAAM,OACnB,SAAUuC,GAAM,OAASvC,EAAM,KAAKuC,EAAK,MAAM,EAAI,OACnD,WAAYA,GAAM,OAAS,CAAC,CAACvC,EAAM,YAAYuC,EAAK,MAAM,EAAI,GAC9D,OAAS5B,GAAMH,EAAQC,EAAKE,CAAC,EAC7B,SAAWc,GAAMX,EAAUL,EAAKgB,CAAC,EACjC,SAAWA,GAAMc,GAAM,QAAUvB,EAAOuB,EAAK,OAAQd,CAAC,EACtD,WAAY,IAAMc,GAAM,QAAUpB,EAASoB,EAAK,MAAM,EACtD,OAAQ,IAAMV,EAAOS,EAAM,IAAI,EAC/B,QAAS5C,EAAa,SAAA,EAZjB4C,EAAM,IAAA,CAejB,CAAC,CAAA,CACH,EAGD/B,GACCyB,EAAAA,KAAC,MAAA,CAAI,UAAU,sGACb,SAAA,CAAAC,MAACQ,EAAA,CAAM,QAAQ,OAAQ,SAAAvD,EAAE,gBAAgB,EAAE,EAC3C8C,EAAAA,KAACE,EAAA,CACC,QAAQ,QAAQ,KAAK,KAAK,UAAU,UACpC,QAASN,EACT,SAAUpC,EAAU,UAEpB,SAAA,CAAAyC,EAAAA,IAACS,EAAA,CAAE,UAAU,SAAA,CAAU,EAAE,IAAExD,EAAE,gBAAgB,CAAA,CAAA,CAAA,EAE/C8C,EAAAA,KAACE,EAAA,CACC,KAAK,KACL,QAAS,IAAM,KAAKd,EAAA,EACpB,SAAU5B,EAAU,UAEnB,SAAA,CAAAA,EAAU,gBACN2C,EAAA,CAAQ,UAAU,uBAAuB,EAC1CF,EAAAA,IAACU,EAAA,CAAK,UAAU,SAAA,CAAU,EAC7BzD,EAAE,aAAa,CAAA,CAAA,CAAA,CAClB,CAAA,CACF,CAAA,EAEJ,CAEJ,CAEA,SAASmD,EAAa,CAAE,KAAAxC,GAAyC,CAC/D,KAAM,CAAE,EAAAX,CAAA,EAAMC,EAAe,UAAU,EACvC,OACE6C,EAAAA,KAAC,UAAA,CAAQ,UAAU,uDACjB,SAAA,CAAAA,EAAAA,KAAC,MAAA,CAAI,UAAU,kCACb,SAAA,CAAAC,EAAAA,IAAC/D,EAAA,CAAM,UAAU,uBAAA,CAAwB,QACxC,OAAA,CAAK,UAAU,gBAAiB,SAAAgB,EAAE,mBAAmB,EAAE,EACvDW,EAAK,SAAW,QACd4C,EAAA,CAAM,QAAQ,UAAW,SAAAvD,EAAE,mBAAmB,CAAA,CAAE,QAEhD,OAAA,CAAK,UAAU,oBAAqB,SAAAW,EAAK,KAAK,KAAK,CAAA,CAAE,CAAA,EAE1D,QACC,IAAA,CAAE,UAAU,iCAAkC,SAAAX,EAAE,kBAAkB,CAAA,CAAE,CAAA,EACvE,CAEJ,CAiBA,SAASsD,GAAYI,EAAsC,CACzD,KAAM,CAAE,EAAA1D,CAAA,EAAMC,EAAe,UAAU,EACjC,CAAE,KAAAoD,EAAM,MAAAD,EAAO,IAAA7B,EAAK,MAAAoC,EAAO,SAAAC,EAAU,WAAAC,EAAY,OAAAC,EAAQ,SAAAC,EAC7D,SAAAC,EAAU,WAAAC,EAAY,OAAAtB,EAAQ,QAAAuB,CAAA,EAAYR,EACtCS,EAAW,CAAC,CAACd,GAAM,OACnBe,EAAY,CAAC,CAACf,GAAM,WAAcQ,GAAc,CAAC,CAACD,EACxD,OACEd,EAAAA,KAAC,MAAA,CAAI,UAAU,iDACb,SAAA,CAAAA,EAAAA,KAAC,MAAA,CAAI,UAAU,0BACb,SAAA,CAAAA,EAAAA,KAAC,MAAA,CAAI,UAAU,gBACb,SAAA,CAAAC,EAAAA,IAAC,SAAA,CACC,KAAK,SACL,UAAU,2EACV,QAAS,IAAMe,EAAO,EAAE,EACxB,SAAUvC,IAAQ,EAClB,aAAYvB,EAAE,eAAe,EAE7B,SAAA+C,EAAAA,IAACsB,EAAA,CAAQ,UAAU,SAAA,CAAU,CAAA,CAAA,EAE/BtB,EAAAA,IAAC,SAAA,CACC,KAAK,SACL,UAAU,2EACV,QAAS,IAAMe,EAAO,CAAC,EACvB,SAAUvC,IAAQoC,EAAQ,EAC1B,aAAY3D,EAAE,iBAAiB,EAE/B,SAAA+C,EAAAA,IAACuB,EAAA,CAAU,UAAU,SAAA,CAAU,CAAA,CAAA,CACjC,EACF,EAEAxB,EAAAA,KAAC,QAAA,CAAM,UAAU,kCACf,SAAA,CAAAC,EAAAA,IAAC,QAAA,CACC,KAAK,WACL,QAASK,EAAM,QACf,SAAWmB,GAAMR,EAASQ,EAAE,OAAO,OAAO,EAC1C,UAAU,sCAAA,CAAA,EAEZxB,EAAAA,IAAC,OAAA,CAAK,UAAU,oBAAqB,WAAM,IAAA,CAAK,CAAA,EAClD,EAEEoB,EAEEC,EACFtB,EAAAA,KAACS,EAAA,CAAM,QAAQ,UACb,SAAA,CAAAR,EAAAA,IAACyB,EAAA,CAAa,UAAU,cAAA,CAAe,EAAE,IAAExE,EAAE,mBAAmB,CAAA,CAAA,CAClE,EAEA8C,EAAAA,KAACS,EAAA,CAAM,QAAQ,UACb,SAAA,CAAAR,EAAAA,IAAC0B,EAAA,CAAQ,UAAU,cAAA,CAAe,EAAE,IAAEzE,EAAE,mBAAmB,CAAA,EAC7D,EARA+C,EAAAA,IAACQ,EAAA,CAAM,QAAQ,UAAW,SAAAvD,EAAE,cAAc,CAAA,CAAE,EAW9C+C,EAAAA,IAAC,MAAA,CAAI,UAAU,kCACb,SAAAD,EAAAA,KAACE,EAAA,CACC,KAAK,KAAK,QAAQ,QAClB,QAASL,EACT,SAAUuB,GAAW,CAACd,EAAM,QAC5B,aAAYpD,EAAE,gBAAgB,EAE7B,SAAA,CAAAkE,EACGnB,EAAAA,IAACE,GAAQ,UAAU,sBAAA,CAAuB,EAC1CF,EAAAA,IAACjE,EAAA,CAAa,UAAU,SAAA,CAAU,QACrC,OAAA,CAAK,UAAU,mBAAoB,SAAAkB,EAAE,gBAAgB,CAAA,CAAE,CAAA,CAAA,CAAA,CAC1D,CACF,CAAA,EACF,EAECmE,GACCrB,EAAAA,KAAC,MAAA,CAAI,UAAU,2BACb,SAAA,CAAAA,EAAAA,KAAC4B,EAAA,CAAM,UAAU,sBACf,SAAA,CAAA3B,EAAAA,IAAC9D,EAAA,CAAI,UAAU,qBAAA,CAAsB,EACpCoE,GAAM,MAAA,EACT,EACAP,EAAAA,KAAC,MAAA,CAAI,UAAU,0BACb,SAAA,CAAAC,EAAAA,IAAC4B,EAAA,CACC,KAAK,WACL,MAAOf,GAAY,GACnB,SAAWW,GAAMP,EAASO,EAAE,OAAO,KAAK,EACxC,YACElB,GAAM,WAAa,CAACQ,EAChB7D,EAAE,8BAA8B,EAChCA,EAAE,yBAAyB,EAEjC,UAAU,mBAAA,CAAA,EAEXqD,GAAM,WAAa,CAACQ,GACnBd,EAAAA,IAACC,EAAA,CACC,KAAK,KAAK,QAAQ,QAClB,QAASiB,EACT,aAAYjE,EAAE,iBAAiB,EAE/B,SAAA+C,EAAAA,IAACS,EAAA,CAAE,UAAU,SAAA,CAAU,CAAA,CAAA,CACzB,EAEJ,EACAT,EAAAA,IAAC,IAAA,CAAE,UAAU,4BACV,SAAA/C,EAAE,iBAAkB,CAAE,OAAQqD,GAAM,MAAA,CAAQ,CAAA,CAC/C,CAAA,CAAA,CACF,CAAA,EAEJ,CAEJ","x_google_ignoreList":[0,1,2]}
@@ -1,2 +1,2 @@
1
- import{u as N,j as e,B as I,I as d,S as h,f as _,g as f,h as p,i as u,o as T,l as j,m as E,p as M,c as w,k as U}from"./index-DXI13nSQ.js";import{r as v}from"./react-Cb2sDjhD.js";import{u as S,a as L}from"./use-settings-DJlVLnjo.js";import{L as C}from"./loader-circle-BS5FFFg-.js";import{R as O}from"./refresh-ccw-BPKXoMZa.js";import{S as H}from"./shield-alert-BM0-khVy.js";import{C as R}from"./circle-check-VgYKnBhy.js";import"./useQuery-CEwGD94N.js";function V(s){if(s==null)return!1;const a=s.toLowerCase();return a==="1"||a==="true"||a==="yes"||a==="on"}function $(){const{t:s}=N(["settings","common"]),a=S(),o=L(),[i,B]=v.useState(null),[b,y]=v.useState(!1);async function g(){y(!0);try{const t=await T.get("/api/security/diagnostics");B(t.diagnostics)}catch(t){j.error(E(t,s).message)}finally{y(!1)}}v.useEffect(()=>{g()},[]);async function r(t,l){try{await o.mutateAsync({updates:{[t]:l}}),j.success(s("security.savingToast")),await g()}catch(A){j.error(E(A,s).message)}}const n=a.data?.env??{};return e.jsxs("div",{className:"mx-auto flex max-w-4xl flex-col gap-4",children:[e.jsxs("header",{className:"flex flex-col gap-1",children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-3",children:[e.jsx("h1",{className:"text-xl font-semibold",children:s("security.title")}),e.jsx(I,{variant:"ghost",size:"sm",className:"ml-auto",onClick:()=>{a.refetch(),g()},disabled:a.isFetching||b,"aria-label":s("actions.refresh",{ns:"common"}),children:a.isFetching||b?e.jsx(C,{className:"h-4 w-4 animate-spin"}):e.jsx(O,{className:"h-4 w-4"})})]}),e.jsx("p",{className:"text-sm text-muted",children:s("security.subtitle")})]}),e.jsxs("section",{className:"rounded-lg border border-border bg-card p-4",children:[e.jsxs("h2",{className:"mb-3 flex items-center gap-2 text-sm font-semibold",children:[e.jsx(H,{className:"h-4 w-4"}),s("security.diagnostics.title")]}),i?e.jsxs("ul",{className:"grid gap-2 text-sm sm:grid-cols-2",children:[e.jsx(x,{label:s("security.diagnostics.runningAsRoot"),ok:i.runningAsRoot===!1,text:i.runningAsRoot===!0?s("security.diagnostics.runningAsRootHelp"):"OK"}),e.jsx(x,{label:s("security.diagnostics.envFilePerms"),ok:i.envFile.exists&&!i.envFile.tooPermissive,text:i.envFile.exists?i.envFile.tooPermissive?`${i.envFile.mode} — ${s("security.diagnostics.envFilePermsLax")}`:s("security.diagnostics.envFilePermsOk"):"—"}),e.jsx(x,{label:s("security.diagnostics.bwrapAvailable"),ok:i.bwrapAvailable,text:i.bwrapAvailable?"/usr/bin/bwrap":s("security.diagnostics.bwrapMissing")}),e.jsx(x,{label:s("security.diagnostics.senderAllowlist"),ok:i.senderAllowlistConfigured,text:i.senderAllowlistConfigured?s("security.diagnostics.senderAllowlistOn"):s("security.diagnostics.senderAllowlistOff")}),e.jsx(x,{label:s("security.diagnostics.adminAllowlist"),ok:i.adminAllowlistConfigured,text:i.adminAllowlistConfigured?s("security.diagnostics.adminAllowlistOn"):s("security.diagnostics.adminAllowlistOff")}),e.jsx(x,{label:s("security.diagnosticsPlatformBlacklist"),ok:i.platformBlacklist.length===0,text:i.platformBlacklist.length===0?"—":i.platformBlacklist.join(", ")})]}):e.jsx("p",{className:"text-sm text-muted",children:"…"})]}),e.jsx(m,{title:s("security.sender.title"),description:s("security.sender.description"),envKey:"IMHUB_ALLOWED_USERS",children:e.jsx(d,{defaultValue:n.IMHUB_ALLOWED_USERS??"",placeholder:s("security.sender.placeholder"),onBlur:t=>{const l=t.target.value.trim();r("IMHUB_ALLOWED_USERS",l===""?null:l)}})}),e.jsx(m,{title:s("security.exec.title"),description:s("security.exec.description"),children:e.jsxs("div",{className:"grid gap-3 sm:grid-cols-2",children:[e.jsx(c,{label:s("security.exec.sandbox"),children:e.jsxs(h,{value:(n.IMHUB_EXEC_SANDBOX??"").toLowerCase()==="bwrap"?"bwrap":"__none__",onValueChange:t=>void r("IMHUB_EXEC_SANDBOX",t==="__none__"?null:t),children:[e.jsx(_,{children:e.jsx(f,{})}),e.jsxs(p,{children:[e.jsx(u,{value:"__none__",children:s("security.exec.sandboxNone")}),e.jsxs(u,{value:"bwrap",disabled:!i?.bwrapAvailable,children:[s("security.exec.sandboxBwrap"),!i?.bwrapAvailable&&e.jsxs("span",{className:"ml-2 text-xs text-muted",children:["(",s("security.exec.sandboxBwrapMissing"),")"]})]})]})]})}),e.jsx(c,{label:s("security.exec.sandboxNet"),children:e.jsxs(h,{value:(n.IMHUB_EXEC_SANDBOX_NET??"").toLowerCase()==="off"?"off":"on",onValueChange:t=>void r("IMHUB_EXEC_SANDBOX_NET",t==="off"?"off":null),children:[e.jsx(_,{children:e.jsx(f,{})}),e.jsxs(p,{children:[e.jsx(u,{value:"on",children:s("security.exec.sandboxNetOn")}),e.jsx(u,{value:"off",children:s("security.exec.sandboxNetOff")})]})]})}),e.jsx(c,{label:s("security.exec.timeoutMs"),children:e.jsx(d,{type:"number",defaultValue:n.IMHUB_EXEC_TIMEOUT_MS??"",placeholder:"60000",onBlur:t=>{const l=t.target.value.trim();r("IMHUB_EXEC_TIMEOUT_MS",l===""?null:l)}})}),e.jsx(c,{label:s("security.exec.maxOutput"),children:e.jsx(d,{type:"number",defaultValue:n.IMHUB_EXEC_MAX_OUTPUT??"",placeholder:"32768",onBlur:t=>{const l=t.target.value.trim();r("IMHUB_EXEC_MAX_OUTPUT",l===""?null:l)}})})]})}),e.jsx(m,{title:s("security.web.title"),description:s("security.web.description"),children:e.jsxs("div",{className:"grid gap-3 sm:grid-cols-2",children:[e.jsx(c,{label:s("security.web.allowPrivate"),hint:s("security.web.allowPrivateHelp"),children:e.jsxs(h,{value:V(n.IMHUB_NATIVE_WEB_ALLOW_PRIVATE)?"1":"0",onValueChange:t=>void r("IMHUB_NATIVE_WEB_ALLOW_PRIVATE",t==="1"?"1":null),children:[e.jsx(_,{children:e.jsx(f,{})}),e.jsxs(p,{children:[e.jsx(u,{value:"0",children:"OFF"}),e.jsx(u,{value:"1",children:"ON"})]})]})}),e.jsx(c,{label:s("security.web.timeoutMs"),children:e.jsx(d,{type:"number",defaultValue:n.IMHUB_NATIVE_WEB_TIMEOUT_MS??"",placeholder:"30000",onBlur:t=>{const l=t.target.value.trim();r("IMHUB_NATIVE_WEB_TIMEOUT_MS",l===""?null:l)}})}),e.jsx("div",{className:"sm:col-span-2",children:e.jsx(c,{label:s("security.web.ssrfWhitelist"),children:e.jsx(d,{defaultValue:n.IMHUB_NATIVE_WEB_SSRF_WHITELIST??"",placeholder:s("security.web.ssrfWhitelistPlaceholder"),onBlur:t=>{const l=t.target.value.trim();r("IMHUB_NATIVE_WEB_SSRF_WHITELIST",l===""?null:l)}})})})]})}),e.jsx(m,{title:s("security.fs.title"),description:s("security.fs.description"),children:e.jsxs("div",{className:"grid gap-3 sm:grid-cols-2",children:[e.jsx(c,{label:s("security.fs.restrict"),children:e.jsxs(h,{value:(n.IMHUB_NATIVE_FS_RESTRICT??"1")==="0"?"0":"1",onValueChange:t=>void r("IMHUB_NATIVE_FS_RESTRICT",t==="0"?"0":null),children:[e.jsx(_,{children:e.jsx(f,{})}),e.jsxs(p,{children:[e.jsx(u,{value:"1",children:s("security.fs.restrictOn")}),e.jsx(u,{value:"0",children:s("security.fs.restrictOff")})]})]})}),e.jsx(c,{label:s("security.fs.timeoutMs"),children:e.jsx(d,{type:"number",defaultValue:n.IMHUB_NATIVE_FS_TIMEOUT_MS??"",placeholder:"60000",onBlur:t=>{const l=t.target.value.trim();r("IMHUB_NATIVE_FS_TIMEOUT_MS",l===""?null:l)}})})]})}),e.jsx(m,{title:s("security.delegation.title"),description:s("security.delegation.description"),envKey:"IMHUB_NATIVE_CALL_AGENT_MAX_PER_TURN",children:e.jsx(d,{type:"number",defaultValue:n.IMHUB_NATIVE_CALL_AGENT_MAX_PER_TURN??"",placeholder:"2",onBlur:t=>{const l=t.target.value.trim();r("IMHUB_NATIVE_CALL_AGENT_MAX_PER_TURN",l===""?null:l)}})}),e.jsxs(m,{title:s("security.platformBlacklist.title"),description:s("security.platformBlacklist.description"),envKey:"IMHUB_PLATFORM_BLACKLIST",children:[e.jsx(d,{defaultValue:n.IMHUB_PLATFORM_BLACKLIST??"",placeholder:s("security.platformBlacklist.placeholder"),onBlur:t=>{const l=t.target.value.trim();r("IMHUB_PLATFORM_BLACKLIST",l===""?null:l)}}),i?.platformBlacklist&&i.platformBlacklist.length>0&&e.jsxs("p",{className:"mt-2 text-[10px] text-muted",children:[s("security.platformBlacklist.currentLabel"),": ",i.platformBlacklist.join(", ")]})]})]})}function x({label:s,ok:a,text:o}){return e.jsxs("li",{className:"flex items-start gap-2",children:[a?e.jsx(R,{className:"mt-0.5 h-4 w-4 shrink-0 text-success"}):e.jsx(M,{className:"mt-0.5 h-4 w-4 shrink-0 text-warning"}),e.jsxs("div",{className:"flex-1",children:[e.jsx("div",{className:"font-medium",children:s}),e.jsx("div",{className:w("text-xs",a?"text-muted":"text-warning"),children:o})]})]})}function m({title:s,description:a,envKey:o,children:i}){return e.jsxs("section",{className:"rounded-lg border border-border bg-card p-4",children:[e.jsxs("div",{className:"mb-2 flex flex-wrap items-baseline gap-2",children:[e.jsx("h2",{className:"text-sm font-semibold",children:s}),o&&e.jsx(U,{variant:"outline",className:"font-mono text-[10px]",children:o})]}),e.jsx("p",{className:"mb-3 text-xs text-muted",children:a}),i]})}function c({label:s,hint:a,children:o}){return e.jsxs("div",{className:"flex flex-col gap-1",children:[e.jsx("label",{className:"text-xs font-medium text-muted",children:s}),o,a&&e.jsx("p",{className:"text-[10px] text-muted",children:a})]})}export{$ as default};
2
- //# sourceMappingURL=security-FHN-b43T.js.map
1
+ import{u as N,j as e,B as I,I as d,S as h,f as _,g as f,h as p,i as u,o as T,l as j,m as E,p as M,c as w,k as U}from"./index-CuNnG-5O.js";import{r as v}from"./react-Cb2sDjhD.js";import{u as S,a as L}from"./use-settings-D3vI1Dwg.js";import{L as C}from"./loader-circle-CVEo3Hwx.js";import{R as O}from"./refresh-ccw-DZHRYuFA.js";import{S as H}from"./shield-alert-DRh8fC8Z.js";import{C as R}from"./circle-check-D98QQwz0.js";import"./useQuery-DIlm0vJ-.js";function V(s){if(s==null)return!1;const a=s.toLowerCase();return a==="1"||a==="true"||a==="yes"||a==="on"}function $(){const{t:s}=N(["settings","common"]),a=S(),o=L(),[i,B]=v.useState(null),[b,y]=v.useState(!1);async function g(){y(!0);try{const t=await T.get("/api/security/diagnostics");B(t.diagnostics)}catch(t){j.error(E(t,s).message)}finally{y(!1)}}v.useEffect(()=>{g()},[]);async function r(t,l){try{await o.mutateAsync({updates:{[t]:l}}),j.success(s("security.savingToast")),await g()}catch(A){j.error(E(A,s).message)}}const n=a.data?.env??{};return e.jsxs("div",{className:"mx-auto flex max-w-4xl flex-col gap-4",children:[e.jsxs("header",{className:"flex flex-col gap-1",children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-3",children:[e.jsx("h1",{className:"text-xl font-semibold",children:s("security.title")}),e.jsx(I,{variant:"ghost",size:"sm",className:"ml-auto",onClick:()=>{a.refetch(),g()},disabled:a.isFetching||b,"aria-label":s("actions.refresh",{ns:"common"}),children:a.isFetching||b?e.jsx(C,{className:"h-4 w-4 animate-spin"}):e.jsx(O,{className:"h-4 w-4"})})]}),e.jsx("p",{className:"text-sm text-muted",children:s("security.subtitle")})]}),e.jsxs("section",{className:"rounded-lg border border-border bg-card p-4",children:[e.jsxs("h2",{className:"mb-3 flex items-center gap-2 text-sm font-semibold",children:[e.jsx(H,{className:"h-4 w-4"}),s("security.diagnostics.title")]}),i?e.jsxs("ul",{className:"grid gap-2 text-sm sm:grid-cols-2",children:[e.jsx(x,{label:s("security.diagnostics.runningAsRoot"),ok:i.runningAsRoot===!1,text:i.runningAsRoot===!0?s("security.diagnostics.runningAsRootHelp"):"OK"}),e.jsx(x,{label:s("security.diagnostics.envFilePerms"),ok:i.envFile.exists&&!i.envFile.tooPermissive,text:i.envFile.exists?i.envFile.tooPermissive?`${i.envFile.mode} — ${s("security.diagnostics.envFilePermsLax")}`:s("security.diagnostics.envFilePermsOk"):"—"}),e.jsx(x,{label:s("security.diagnostics.bwrapAvailable"),ok:i.bwrapAvailable,text:i.bwrapAvailable?"/usr/bin/bwrap":s("security.diagnostics.bwrapMissing")}),e.jsx(x,{label:s("security.diagnostics.senderAllowlist"),ok:i.senderAllowlistConfigured,text:i.senderAllowlistConfigured?s("security.diagnostics.senderAllowlistOn"):s("security.diagnostics.senderAllowlistOff")}),e.jsx(x,{label:s("security.diagnostics.adminAllowlist"),ok:i.adminAllowlistConfigured,text:i.adminAllowlistConfigured?s("security.diagnostics.adminAllowlistOn"):s("security.diagnostics.adminAllowlistOff")}),e.jsx(x,{label:s("security.diagnosticsPlatformBlacklist"),ok:i.platformBlacklist.length===0,text:i.platformBlacklist.length===0?"—":i.platformBlacklist.join(", ")})]}):e.jsx("p",{className:"text-sm text-muted",children:"…"})]}),e.jsx(m,{title:s("security.sender.title"),description:s("security.sender.description"),envKey:"IMHUB_ALLOWED_USERS",children:e.jsx(d,{defaultValue:n.IMHUB_ALLOWED_USERS??"",placeholder:s("security.sender.placeholder"),onBlur:t=>{const l=t.target.value.trim();r("IMHUB_ALLOWED_USERS",l===""?null:l)}})}),e.jsx(m,{title:s("security.exec.title"),description:s("security.exec.description"),children:e.jsxs("div",{className:"grid gap-3 sm:grid-cols-2",children:[e.jsx(c,{label:s("security.exec.sandbox"),children:e.jsxs(h,{value:(n.IMHUB_EXEC_SANDBOX??"").toLowerCase()==="bwrap"?"bwrap":"__none__",onValueChange:t=>void r("IMHUB_EXEC_SANDBOX",t==="__none__"?null:t),children:[e.jsx(_,{children:e.jsx(f,{})}),e.jsxs(p,{children:[e.jsx(u,{value:"__none__",children:s("security.exec.sandboxNone")}),e.jsxs(u,{value:"bwrap",disabled:!i?.bwrapAvailable,children:[s("security.exec.sandboxBwrap"),!i?.bwrapAvailable&&e.jsxs("span",{className:"ml-2 text-xs text-muted",children:["(",s("security.exec.sandboxBwrapMissing"),")"]})]})]})]})}),e.jsx(c,{label:s("security.exec.sandboxNet"),children:e.jsxs(h,{value:(n.IMHUB_EXEC_SANDBOX_NET??"").toLowerCase()==="off"?"off":"on",onValueChange:t=>void r("IMHUB_EXEC_SANDBOX_NET",t==="off"?"off":null),children:[e.jsx(_,{children:e.jsx(f,{})}),e.jsxs(p,{children:[e.jsx(u,{value:"on",children:s("security.exec.sandboxNetOn")}),e.jsx(u,{value:"off",children:s("security.exec.sandboxNetOff")})]})]})}),e.jsx(c,{label:s("security.exec.timeoutMs"),children:e.jsx(d,{type:"number",defaultValue:n.IMHUB_EXEC_TIMEOUT_MS??"",placeholder:"60000",onBlur:t=>{const l=t.target.value.trim();r("IMHUB_EXEC_TIMEOUT_MS",l===""?null:l)}})}),e.jsx(c,{label:s("security.exec.maxOutput"),children:e.jsx(d,{type:"number",defaultValue:n.IMHUB_EXEC_MAX_OUTPUT??"",placeholder:"32768",onBlur:t=>{const l=t.target.value.trim();r("IMHUB_EXEC_MAX_OUTPUT",l===""?null:l)}})})]})}),e.jsx(m,{title:s("security.web.title"),description:s("security.web.description"),children:e.jsxs("div",{className:"grid gap-3 sm:grid-cols-2",children:[e.jsx(c,{label:s("security.web.allowPrivate"),hint:s("security.web.allowPrivateHelp"),children:e.jsxs(h,{value:V(n.IMHUB_NATIVE_WEB_ALLOW_PRIVATE)?"1":"0",onValueChange:t=>void r("IMHUB_NATIVE_WEB_ALLOW_PRIVATE",t==="1"?"1":null),children:[e.jsx(_,{children:e.jsx(f,{})}),e.jsxs(p,{children:[e.jsx(u,{value:"0",children:"OFF"}),e.jsx(u,{value:"1",children:"ON"})]})]})}),e.jsx(c,{label:s("security.web.timeoutMs"),children:e.jsx(d,{type:"number",defaultValue:n.IMHUB_NATIVE_WEB_TIMEOUT_MS??"",placeholder:"30000",onBlur:t=>{const l=t.target.value.trim();r("IMHUB_NATIVE_WEB_TIMEOUT_MS",l===""?null:l)}})}),e.jsx("div",{className:"sm:col-span-2",children:e.jsx(c,{label:s("security.web.ssrfWhitelist"),children:e.jsx(d,{defaultValue:n.IMHUB_NATIVE_WEB_SSRF_WHITELIST??"",placeholder:s("security.web.ssrfWhitelistPlaceholder"),onBlur:t=>{const l=t.target.value.trim();r("IMHUB_NATIVE_WEB_SSRF_WHITELIST",l===""?null:l)}})})})]})}),e.jsx(m,{title:s("security.fs.title"),description:s("security.fs.description"),children:e.jsxs("div",{className:"grid gap-3 sm:grid-cols-2",children:[e.jsx(c,{label:s("security.fs.restrict"),children:e.jsxs(h,{value:(n.IMHUB_NATIVE_FS_RESTRICT??"1")==="0"?"0":"1",onValueChange:t=>void r("IMHUB_NATIVE_FS_RESTRICT",t==="0"?"0":null),children:[e.jsx(_,{children:e.jsx(f,{})}),e.jsxs(p,{children:[e.jsx(u,{value:"1",children:s("security.fs.restrictOn")}),e.jsx(u,{value:"0",children:s("security.fs.restrictOff")})]})]})}),e.jsx(c,{label:s("security.fs.timeoutMs"),children:e.jsx(d,{type:"number",defaultValue:n.IMHUB_NATIVE_FS_TIMEOUT_MS??"",placeholder:"60000",onBlur:t=>{const l=t.target.value.trim();r("IMHUB_NATIVE_FS_TIMEOUT_MS",l===""?null:l)}})})]})}),e.jsx(m,{title:s("security.delegation.title"),description:s("security.delegation.description"),envKey:"IMHUB_NATIVE_CALL_AGENT_MAX_PER_TURN",children:e.jsx(d,{type:"number",defaultValue:n.IMHUB_NATIVE_CALL_AGENT_MAX_PER_TURN??"",placeholder:"2",onBlur:t=>{const l=t.target.value.trim();r("IMHUB_NATIVE_CALL_AGENT_MAX_PER_TURN",l===""?null:l)}})}),e.jsxs(m,{title:s("security.platformBlacklist.title"),description:s("security.platformBlacklist.description"),envKey:"IMHUB_PLATFORM_BLACKLIST",children:[e.jsx(d,{defaultValue:n.IMHUB_PLATFORM_BLACKLIST??"",placeholder:s("security.platformBlacklist.placeholder"),onBlur:t=>{const l=t.target.value.trim();r("IMHUB_PLATFORM_BLACKLIST",l===""?null:l)}}),i?.platformBlacklist&&i.platformBlacklist.length>0&&e.jsxs("p",{className:"mt-2 text-[10px] text-muted",children:[s("security.platformBlacklist.currentLabel"),": ",i.platformBlacklist.join(", ")]})]})]})}function x({label:s,ok:a,text:o}){return e.jsxs("li",{className:"flex items-start gap-2",children:[a?e.jsx(R,{className:"mt-0.5 h-4 w-4 shrink-0 text-success"}):e.jsx(M,{className:"mt-0.5 h-4 w-4 shrink-0 text-warning"}),e.jsxs("div",{className:"flex-1",children:[e.jsx("div",{className:"font-medium",children:s}),e.jsx("div",{className:w("text-xs",a?"text-muted":"text-warning"),children:o})]})]})}function m({title:s,description:a,envKey:o,children:i}){return e.jsxs("section",{className:"rounded-lg border border-border bg-card p-4",children:[e.jsxs("div",{className:"mb-2 flex flex-wrap items-baseline gap-2",children:[e.jsx("h2",{className:"text-sm font-semibold",children:s}),o&&e.jsx(U,{variant:"outline",className:"font-mono text-[10px]",children:o})]}),e.jsx("p",{className:"mb-3 text-xs text-muted",children:a}),i]})}function c({label:s,hint:a,children:o}){return e.jsxs("div",{className:"flex flex-col gap-1",children:[e.jsx("label",{className:"text-xs font-medium text-muted",children:s}),o,a&&e.jsx("p",{className:"text-[10px] text-muted",children:a})]})}export{$ as default};
2
+ //# sourceMappingURL=security-CP0MP_Gx.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"security-FHN-b43T.js","sources":["../../src/routes/settings/security.tsx"],"sourcesContent":["/**\n * /settings/security — central console for v1.2.66 security controls.\n *\n * Lays out:\n * • Diagnostics card (read-only) — uid 0? env file perms? bwrap? allowlists configured?\n * • Sender allowlist (IMHUB_ALLOWED_USERS)\n * • Native exec (IMHUB_EXEC_SANDBOX, IMHUB_EXEC_SANDBOX_NET, IMHUB_EXEC_TIMEOUT_MS, IMHUB_EXEC_MAX_OUTPUT)\n * • Native web (IMHUB_NATIVE_WEB_ALLOW_PRIVATE, IMHUB_NATIVE_WEB_SSRF_WHITELIST, IMHUB_NATIVE_WEB_TIMEOUT_MS)\n * • Native fs (IMHUB_NATIVE_FS_RESTRICT, IMHUB_NATIVE_FS_TIMEOUT_MS)\n * • Delegation (IMHUB_NATIVE_CALL_AGENT_MAX_PER_TURN)\n *\n * All writes go through PUT /api/env (admin-gated). Most knobs are\n * hot-reloaded (read process.env per tool call); the exec sandbox +\n * sender allowlist apply at boot — service restart required.\n */\n\nimport { useEffect, useState } from 'react'\nimport { useTranslation } from 'react-i18next'\nimport { toast } from 'sonner'\nimport { AlertTriangle, CheckCircle2, Loader2, RefreshCcw, ShieldAlert } from 'lucide-react'\n\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { Input } from '@/components/ui/input'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@/components/ui/select'\nimport { useEnv, useUpdateEnv } from '@/hooks/use-settings'\nimport { client } from '@/lib/api/client'\nimport { describeError } from '@/lib/api/errors'\nimport { cn } from '@/lib/utils'\n\ninterface Diagnostics {\n runningAsRoot: boolean | null\n envFile: { exists: boolean; mode: string | null; tooPermissive: boolean }\n bwrapAvailable: boolean\n senderAllowlistConfigured: boolean\n adminAllowlistConfigured: boolean\n platformBlacklist: string[]\n liveEnv: Record<string, string | null>\n}\n\nfunction envBool(v: string | undefined): boolean {\n if (v == null) return false\n const t = v.toLowerCase()\n return t === '1' || t === 'true' || t === 'yes' || t === 'on'\n}\n\nexport default function SettingsSecurityRoute(): JSX.Element {\n const { t } = useTranslation(['settings', 'common'])\n const envQuery = useEnv()\n const updateEnv = useUpdateEnv()\n const [diagnostics, setDiagnostics] = useState<Diagnostics | null>(null)\n const [diagLoading, setDiagLoading] = useState(false)\n\n async function loadDiagnostics(): Promise<void> {\n setDiagLoading(true)\n try {\n const res = await client.get<{ diagnostics: Diagnostics }>('/api/security/diagnostics')\n setDiagnostics(res.diagnostics)\n } catch (err) {\n toast.error(describeError(err, t).message)\n } finally {\n setDiagLoading(false)\n }\n }\n\n useEffect(() => { void loadDiagnostics() }, [])\n\n async function setValue(key: string, value: string | null): Promise<void> {\n try {\n await updateEnv.mutateAsync({ updates: { [key]: value } })\n toast.success(t('security.savingToast'))\n await loadDiagnostics()\n } catch (err) {\n toast.error(describeError(err, t).message)\n }\n }\n\n const env = envQuery.data?.env ?? {}\n\n return (\n <div className=\"mx-auto flex max-w-4xl flex-col gap-4\">\n <header className=\"flex flex-col gap-1\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <h1 className=\"text-xl font-semibold\">{t('security.title')}</h1>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"ml-auto\"\n onClick={() => { void envQuery.refetch(); void loadDiagnostics() }}\n disabled={envQuery.isFetching || diagLoading}\n aria-label={t('actions.refresh', { ns: 'common' })}\n >\n {envQuery.isFetching || diagLoading\n ? <Loader2 className=\"h-4 w-4 animate-spin\" />\n : <RefreshCcw className=\"h-4 w-4\" />\n }\n </Button>\n </div>\n <p className=\"text-sm text-muted\">{t('security.subtitle')}</p>\n </header>\n\n {/* Diagnostics card */}\n <section className=\"rounded-lg border border-border bg-card p-4\">\n <h2 className=\"mb-3 flex items-center gap-2 text-sm font-semibold\">\n <ShieldAlert className=\"h-4 w-4\" />\n {t('security.diagnostics.title')}\n </h2>\n {diagnostics ? (\n <ul className=\"grid gap-2 text-sm sm:grid-cols-2\">\n <DiagRow\n label={t('security.diagnostics.runningAsRoot')}\n ok={diagnostics.runningAsRoot === false}\n text={diagnostics.runningAsRoot === true ? t('security.diagnostics.runningAsRootHelp') : 'OK'}\n />\n <DiagRow\n label={t('security.diagnostics.envFilePerms')}\n ok={diagnostics.envFile.exists && !diagnostics.envFile.tooPermissive}\n text={\n !diagnostics.envFile.exists\n ? '—'\n : diagnostics.envFile.tooPermissive\n ? `${diagnostics.envFile.mode} — ${t('security.diagnostics.envFilePermsLax')}`\n : t('security.diagnostics.envFilePermsOk')\n }\n />\n <DiagRow\n label={t('security.diagnostics.bwrapAvailable')}\n ok={diagnostics.bwrapAvailable}\n text={diagnostics.bwrapAvailable ? '/usr/bin/bwrap' : t('security.diagnostics.bwrapMissing')}\n />\n <DiagRow\n label={t('security.diagnostics.senderAllowlist')}\n ok={diagnostics.senderAllowlistConfigured}\n text={diagnostics.senderAllowlistConfigured\n ? t('security.diagnostics.senderAllowlistOn')\n : t('security.diagnostics.senderAllowlistOff')\n }\n />\n <DiagRow\n label={t('security.diagnostics.adminAllowlist')}\n ok={diagnostics.adminAllowlistConfigured}\n text={diagnostics.adminAllowlistConfigured\n ? t('security.diagnostics.adminAllowlistOn')\n : t('security.diagnostics.adminAllowlistOff')\n }\n />\n <DiagRow\n label={t('security.diagnosticsPlatformBlacklist')}\n ok={diagnostics.platformBlacklist.length === 0}\n text={diagnostics.platformBlacklist.length === 0\n ? '—'\n : diagnostics.platformBlacklist.join(', ')\n }\n />\n </ul>\n ) : (\n <p className=\"text-sm text-muted\">…</p>\n )}\n </section>\n\n {/* Sender allowlist */}\n <ControlCard\n title={t('security.sender.title')}\n description={t('security.sender.description')}\n envKey=\"IMHUB_ALLOWED_USERS\"\n >\n <Input\n defaultValue={env.IMHUB_ALLOWED_USERS ?? ''}\n placeholder={t('security.sender.placeholder')}\n onBlur={(e) => {\n const v = e.target.value.trim()\n void setValue('IMHUB_ALLOWED_USERS', v === '' ? null : v)\n }}\n />\n </ControlCard>\n\n {/* Exec sandbox */}\n <ControlCard\n title={t('security.exec.title')}\n description={t('security.exec.description')}\n >\n <div className=\"grid gap-3 sm:grid-cols-2\">\n <LabeledField label={t('security.exec.sandbox')}>\n <Select\n value={(env.IMHUB_EXEC_SANDBOX ?? '').toLowerCase() === 'bwrap' ? 'bwrap' : '__none__'}\n onValueChange={(v) => void setValue('IMHUB_EXEC_SANDBOX', v === '__none__' ? null : v)}\n >\n <SelectTrigger><SelectValue /></SelectTrigger>\n <SelectContent>\n <SelectItem value=\"__none__\">{t('security.exec.sandboxNone')}</SelectItem>\n <SelectItem\n value=\"bwrap\"\n disabled={!diagnostics?.bwrapAvailable}\n >\n {t('security.exec.sandboxBwrap')}\n {!diagnostics?.bwrapAvailable && (\n <span className=\"ml-2 text-xs text-muted\">\n ({t('security.exec.sandboxBwrapMissing')})\n </span>\n )}\n </SelectItem>\n </SelectContent>\n </Select>\n </LabeledField>\n\n <LabeledField label={t('security.exec.sandboxNet')}>\n <Select\n value={(env.IMHUB_EXEC_SANDBOX_NET ?? '').toLowerCase() === 'off' ? 'off' : 'on'}\n onValueChange={(v) => void setValue('IMHUB_EXEC_SANDBOX_NET', v === 'off' ? 'off' : null)}\n >\n <SelectTrigger><SelectValue /></SelectTrigger>\n <SelectContent>\n <SelectItem value=\"on\">{t('security.exec.sandboxNetOn')}</SelectItem>\n <SelectItem value=\"off\">{t('security.exec.sandboxNetOff')}</SelectItem>\n </SelectContent>\n </Select>\n </LabeledField>\n\n <LabeledField label={t('security.exec.timeoutMs')}>\n <Input\n type=\"number\"\n defaultValue={env.IMHUB_EXEC_TIMEOUT_MS ?? ''}\n placeholder=\"60000\"\n onBlur={(e) => {\n const v = e.target.value.trim()\n void setValue('IMHUB_EXEC_TIMEOUT_MS', v === '' ? null : v)\n }}\n />\n </LabeledField>\n\n <LabeledField label={t('security.exec.maxOutput')}>\n <Input\n type=\"number\"\n defaultValue={env.IMHUB_EXEC_MAX_OUTPUT ?? ''}\n placeholder=\"32768\"\n onBlur={(e) => {\n const v = e.target.value.trim()\n void setValue('IMHUB_EXEC_MAX_OUTPUT', v === '' ? null : v)\n }}\n />\n </LabeledField>\n </div>\n </ControlCard>\n\n {/* Web SSRF */}\n <ControlCard\n title={t('security.web.title')}\n description={t('security.web.description')}\n >\n <div className=\"grid gap-3 sm:grid-cols-2\">\n <LabeledField label={t('security.web.allowPrivate')} hint={t('security.web.allowPrivateHelp')}>\n <Select\n value={envBool(env.IMHUB_NATIVE_WEB_ALLOW_PRIVATE) ? '1' : '0'}\n onValueChange={(v) => void setValue('IMHUB_NATIVE_WEB_ALLOW_PRIVATE', v === '1' ? '1' : null)}\n >\n <SelectTrigger><SelectValue /></SelectTrigger>\n <SelectContent>\n <SelectItem value=\"0\">OFF</SelectItem>\n <SelectItem value=\"1\">ON</SelectItem>\n </SelectContent>\n </Select>\n </LabeledField>\n\n <LabeledField label={t('security.web.timeoutMs')}>\n <Input\n type=\"number\"\n defaultValue={env.IMHUB_NATIVE_WEB_TIMEOUT_MS ?? ''}\n placeholder=\"30000\"\n onBlur={(e) => {\n const v = e.target.value.trim()\n void setValue('IMHUB_NATIVE_WEB_TIMEOUT_MS', v === '' ? null : v)\n }}\n />\n </LabeledField>\n\n <div className=\"sm:col-span-2\">\n <LabeledField label={t('security.web.ssrfWhitelist')}>\n <Input\n defaultValue={env.IMHUB_NATIVE_WEB_SSRF_WHITELIST ?? ''}\n placeholder={t('security.web.ssrfWhitelistPlaceholder')}\n onBlur={(e) => {\n const v = e.target.value.trim()\n void setValue('IMHUB_NATIVE_WEB_SSRF_WHITELIST', v === '' ? null : v)\n }}\n />\n </LabeledField>\n </div>\n </div>\n </ControlCard>\n\n {/* FS */}\n <ControlCard\n title={t('security.fs.title')}\n description={t('security.fs.description')}\n >\n <div className=\"grid gap-3 sm:grid-cols-2\">\n <LabeledField label={t('security.fs.restrict')}>\n <Select\n value={(env.IMHUB_NATIVE_FS_RESTRICT ?? '1') === '0' ? '0' : '1'}\n onValueChange={(v) => void setValue('IMHUB_NATIVE_FS_RESTRICT', v === '0' ? '0' : null)}\n >\n <SelectTrigger><SelectValue /></SelectTrigger>\n <SelectContent>\n <SelectItem value=\"1\">{t('security.fs.restrictOn')}</SelectItem>\n <SelectItem value=\"0\">{t('security.fs.restrictOff')}</SelectItem>\n </SelectContent>\n </Select>\n </LabeledField>\n\n <LabeledField label={t('security.fs.timeoutMs')}>\n <Input\n type=\"number\"\n defaultValue={env.IMHUB_NATIVE_FS_TIMEOUT_MS ?? ''}\n placeholder=\"60000\"\n onBlur={(e) => {\n const v = e.target.value.trim()\n void setValue('IMHUB_NATIVE_FS_TIMEOUT_MS', v === '' ? null : v)\n }}\n />\n </LabeledField>\n </div>\n </ControlCard>\n\n {/* Delegation cap */}\n <ControlCard\n title={t('security.delegation.title')}\n description={t('security.delegation.description')}\n envKey=\"IMHUB_NATIVE_CALL_AGENT_MAX_PER_TURN\"\n >\n <Input\n type=\"number\"\n defaultValue={env.IMHUB_NATIVE_CALL_AGENT_MAX_PER_TURN ?? ''}\n placeholder=\"2\"\n onBlur={(e) => {\n const v = e.target.value.trim()\n void setValue('IMHUB_NATIVE_CALL_AGENT_MAX_PER_TURN', v === '' ? null : v)\n }}\n />\n </ControlCard>\n\n {/* IM platform blacklist */}\n <ControlCard\n title={t('security.platformBlacklist.title')}\n description={t('security.platformBlacklist.description')}\n envKey=\"IMHUB_PLATFORM_BLACKLIST\"\n >\n <Input\n defaultValue={env.IMHUB_PLATFORM_BLACKLIST ?? ''}\n placeholder={t('security.platformBlacklist.placeholder')}\n onBlur={(e) => {\n const v = e.target.value.trim()\n void setValue('IMHUB_PLATFORM_BLACKLIST', v === '' ? null : v)\n }}\n />\n {diagnostics?.platformBlacklist && diagnostics.platformBlacklist.length > 0 && (\n <p className=\"mt-2 text-[10px] text-muted\">\n {t('security.platformBlacklist.currentLabel')}: {diagnostics.platformBlacklist.join(', ')}\n </p>\n )}\n </ControlCard>\n </div>\n )\n}\n\nfunction DiagRow({ label, ok, text }: { label: string; ok: boolean; text: string }): JSX.Element {\n return (\n <li className=\"flex items-start gap-2\">\n {ok\n ? <CheckCircle2 className=\"mt-0.5 h-4 w-4 shrink-0 text-success\" />\n : <AlertTriangle className=\"mt-0.5 h-4 w-4 shrink-0 text-warning\" />\n }\n <div className=\"flex-1\">\n <div className=\"font-medium\">{label}</div>\n <div className={cn('text-xs', ok ? 'text-muted' : 'text-warning')}>{text}</div>\n </div>\n </li>\n )\n}\n\nfunction ControlCard({\n title, description, envKey, children,\n}: { title: string; description: string; envKey?: string; children: React.ReactNode }): JSX.Element {\n return (\n <section className=\"rounded-lg border border-border bg-card p-4\">\n <div className=\"mb-2 flex flex-wrap items-baseline gap-2\">\n <h2 className=\"text-sm font-semibold\">{title}</h2>\n {envKey && <Badge variant=\"outline\" className=\"font-mono text-[10px]\">{envKey}</Badge>}\n </div>\n <p className=\"mb-3 text-xs text-muted\">{description}</p>\n {children}\n </section>\n )\n}\n\nfunction LabeledField({\n label, hint, children,\n}: { label: string; hint?: string; children: React.ReactNode }): JSX.Element {\n return (\n <div className=\"flex flex-col gap-1\">\n <label className=\"text-xs font-medium text-muted\">{label}</label>\n {children}\n {hint && <p className=\"text-[10px] text-muted\">{hint}</p>}\n </div>\n )\n}\n"],"names":["envBool","v","t","SettingsSecurityRoute","useTranslation","envQuery","useEnv","updateEnv","useUpdateEnv","diagnostics","setDiagnostics","useState","diagLoading","setDiagLoading","loadDiagnostics","res","client","err","toast","describeError","useEffect","setValue","key","value","env","jsxs","jsx","Button","Loader2","RefreshCcw","ShieldAlert","DiagRow","ControlCard","Input","e","LabeledField","Select","SelectTrigger","SelectValue","SelectContent","SelectItem","label","ok","text","CheckCircle2","AlertTriangle","cn","title","description","envKey","children","Badge","hint"],"mappings":"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"}
1
+ {"version":3,"file":"security-CP0MP_Gx.js","sources":["../../src/routes/settings/security.tsx"],"sourcesContent":["/**\n * /settings/security — central console for v1.2.66 security controls.\n *\n * Lays out:\n * • Diagnostics card (read-only) — uid 0? env file perms? bwrap? allowlists configured?\n * • Sender allowlist (IMHUB_ALLOWED_USERS)\n * • Native exec (IMHUB_EXEC_SANDBOX, IMHUB_EXEC_SANDBOX_NET, IMHUB_EXEC_TIMEOUT_MS, IMHUB_EXEC_MAX_OUTPUT)\n * • Native web (IMHUB_NATIVE_WEB_ALLOW_PRIVATE, IMHUB_NATIVE_WEB_SSRF_WHITELIST, IMHUB_NATIVE_WEB_TIMEOUT_MS)\n * • Native fs (IMHUB_NATIVE_FS_RESTRICT, IMHUB_NATIVE_FS_TIMEOUT_MS)\n * • Delegation (IMHUB_NATIVE_CALL_AGENT_MAX_PER_TURN)\n *\n * All writes go through PUT /api/env (admin-gated). Most knobs are\n * hot-reloaded (read process.env per tool call); the exec sandbox +\n * sender allowlist apply at boot — service restart required.\n */\n\nimport { useEffect, useState } from 'react'\nimport { useTranslation } from 'react-i18next'\nimport { toast } from 'sonner'\nimport { AlertTriangle, CheckCircle2, Loader2, RefreshCcw, ShieldAlert } from 'lucide-react'\n\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { Input } from '@/components/ui/input'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@/components/ui/select'\nimport { useEnv, useUpdateEnv } from '@/hooks/use-settings'\nimport { client } from '@/lib/api/client'\nimport { describeError } from '@/lib/api/errors'\nimport { cn } from '@/lib/utils'\n\ninterface Diagnostics {\n runningAsRoot: boolean | null\n envFile: { exists: boolean; mode: string | null; tooPermissive: boolean }\n bwrapAvailable: boolean\n senderAllowlistConfigured: boolean\n adminAllowlistConfigured: boolean\n platformBlacklist: string[]\n liveEnv: Record<string, string | null>\n}\n\nfunction envBool(v: string | undefined): boolean {\n if (v == null) return false\n const t = v.toLowerCase()\n return t === '1' || t === 'true' || t === 'yes' || t === 'on'\n}\n\nexport default function SettingsSecurityRoute(): JSX.Element {\n const { t } = useTranslation(['settings', 'common'])\n const envQuery = useEnv()\n const updateEnv = useUpdateEnv()\n const [diagnostics, setDiagnostics] = useState<Diagnostics | null>(null)\n const [diagLoading, setDiagLoading] = useState(false)\n\n async function loadDiagnostics(): Promise<void> {\n setDiagLoading(true)\n try {\n const res = await client.get<{ diagnostics: Diagnostics }>('/api/security/diagnostics')\n setDiagnostics(res.diagnostics)\n } catch (err) {\n toast.error(describeError(err, t).message)\n } finally {\n setDiagLoading(false)\n }\n }\n\n useEffect(() => { void loadDiagnostics() }, [])\n\n async function setValue(key: string, value: string | null): Promise<void> {\n try {\n await updateEnv.mutateAsync({ updates: { [key]: value } })\n toast.success(t('security.savingToast'))\n await loadDiagnostics()\n } catch (err) {\n toast.error(describeError(err, t).message)\n }\n }\n\n const env = envQuery.data?.env ?? {}\n\n return (\n <div className=\"mx-auto flex max-w-4xl flex-col gap-4\">\n <header className=\"flex flex-col gap-1\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <h1 className=\"text-xl font-semibold\">{t('security.title')}</h1>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"ml-auto\"\n onClick={() => { void envQuery.refetch(); void loadDiagnostics() }}\n disabled={envQuery.isFetching || diagLoading}\n aria-label={t('actions.refresh', { ns: 'common' })}\n >\n {envQuery.isFetching || diagLoading\n ? <Loader2 className=\"h-4 w-4 animate-spin\" />\n : <RefreshCcw className=\"h-4 w-4\" />\n }\n </Button>\n </div>\n <p className=\"text-sm text-muted\">{t('security.subtitle')}</p>\n </header>\n\n {/* Diagnostics card */}\n <section className=\"rounded-lg border border-border bg-card p-4\">\n <h2 className=\"mb-3 flex items-center gap-2 text-sm font-semibold\">\n <ShieldAlert className=\"h-4 w-4\" />\n {t('security.diagnostics.title')}\n </h2>\n {diagnostics ? (\n <ul className=\"grid gap-2 text-sm sm:grid-cols-2\">\n <DiagRow\n label={t('security.diagnostics.runningAsRoot')}\n ok={diagnostics.runningAsRoot === false}\n text={diagnostics.runningAsRoot === true ? t('security.diagnostics.runningAsRootHelp') : 'OK'}\n />\n <DiagRow\n label={t('security.diagnostics.envFilePerms')}\n ok={diagnostics.envFile.exists && !diagnostics.envFile.tooPermissive}\n text={\n !diagnostics.envFile.exists\n ? '—'\n : diagnostics.envFile.tooPermissive\n ? `${diagnostics.envFile.mode} — ${t('security.diagnostics.envFilePermsLax')}`\n : t('security.diagnostics.envFilePermsOk')\n }\n />\n <DiagRow\n label={t('security.diagnostics.bwrapAvailable')}\n ok={diagnostics.bwrapAvailable}\n text={diagnostics.bwrapAvailable ? '/usr/bin/bwrap' : t('security.diagnostics.bwrapMissing')}\n />\n <DiagRow\n label={t('security.diagnostics.senderAllowlist')}\n ok={diagnostics.senderAllowlistConfigured}\n text={diagnostics.senderAllowlistConfigured\n ? t('security.diagnostics.senderAllowlistOn')\n : t('security.diagnostics.senderAllowlistOff')\n }\n />\n <DiagRow\n label={t('security.diagnostics.adminAllowlist')}\n ok={diagnostics.adminAllowlistConfigured}\n text={diagnostics.adminAllowlistConfigured\n ? t('security.diagnostics.adminAllowlistOn')\n : t('security.diagnostics.adminAllowlistOff')\n }\n />\n <DiagRow\n label={t('security.diagnosticsPlatformBlacklist')}\n ok={diagnostics.platformBlacklist.length === 0}\n text={diagnostics.platformBlacklist.length === 0\n ? '—'\n : diagnostics.platformBlacklist.join(', ')\n }\n />\n </ul>\n ) : (\n <p className=\"text-sm text-muted\">…</p>\n )}\n </section>\n\n {/* Sender allowlist */}\n <ControlCard\n title={t('security.sender.title')}\n description={t('security.sender.description')}\n envKey=\"IMHUB_ALLOWED_USERS\"\n >\n <Input\n defaultValue={env.IMHUB_ALLOWED_USERS ?? ''}\n placeholder={t('security.sender.placeholder')}\n onBlur={(e) => {\n const v = e.target.value.trim()\n void setValue('IMHUB_ALLOWED_USERS', v === '' ? null : v)\n }}\n />\n </ControlCard>\n\n {/* Exec sandbox */}\n <ControlCard\n title={t('security.exec.title')}\n description={t('security.exec.description')}\n >\n <div className=\"grid gap-3 sm:grid-cols-2\">\n <LabeledField label={t('security.exec.sandbox')}>\n <Select\n value={(env.IMHUB_EXEC_SANDBOX ?? '').toLowerCase() === 'bwrap' ? 'bwrap' : '__none__'}\n onValueChange={(v) => void setValue('IMHUB_EXEC_SANDBOX', v === '__none__' ? null : v)}\n >\n <SelectTrigger><SelectValue /></SelectTrigger>\n <SelectContent>\n <SelectItem value=\"__none__\">{t('security.exec.sandboxNone')}</SelectItem>\n <SelectItem\n value=\"bwrap\"\n disabled={!diagnostics?.bwrapAvailable}\n >\n {t('security.exec.sandboxBwrap')}\n {!diagnostics?.bwrapAvailable && (\n <span className=\"ml-2 text-xs text-muted\">\n ({t('security.exec.sandboxBwrapMissing')})\n </span>\n )}\n </SelectItem>\n </SelectContent>\n </Select>\n </LabeledField>\n\n <LabeledField label={t('security.exec.sandboxNet')}>\n <Select\n value={(env.IMHUB_EXEC_SANDBOX_NET ?? '').toLowerCase() === 'off' ? 'off' : 'on'}\n onValueChange={(v) => void setValue('IMHUB_EXEC_SANDBOX_NET', v === 'off' ? 'off' : null)}\n >\n <SelectTrigger><SelectValue /></SelectTrigger>\n <SelectContent>\n <SelectItem value=\"on\">{t('security.exec.sandboxNetOn')}</SelectItem>\n <SelectItem value=\"off\">{t('security.exec.sandboxNetOff')}</SelectItem>\n </SelectContent>\n </Select>\n </LabeledField>\n\n <LabeledField label={t('security.exec.timeoutMs')}>\n <Input\n type=\"number\"\n defaultValue={env.IMHUB_EXEC_TIMEOUT_MS ?? ''}\n placeholder=\"60000\"\n onBlur={(e) => {\n const v = e.target.value.trim()\n void setValue('IMHUB_EXEC_TIMEOUT_MS', v === '' ? null : v)\n }}\n />\n </LabeledField>\n\n <LabeledField label={t('security.exec.maxOutput')}>\n <Input\n type=\"number\"\n defaultValue={env.IMHUB_EXEC_MAX_OUTPUT ?? ''}\n placeholder=\"32768\"\n onBlur={(e) => {\n const v = e.target.value.trim()\n void setValue('IMHUB_EXEC_MAX_OUTPUT', v === '' ? null : v)\n }}\n />\n </LabeledField>\n </div>\n </ControlCard>\n\n {/* Web SSRF */}\n <ControlCard\n title={t('security.web.title')}\n description={t('security.web.description')}\n >\n <div className=\"grid gap-3 sm:grid-cols-2\">\n <LabeledField label={t('security.web.allowPrivate')} hint={t('security.web.allowPrivateHelp')}>\n <Select\n value={envBool(env.IMHUB_NATIVE_WEB_ALLOW_PRIVATE) ? '1' : '0'}\n onValueChange={(v) => void setValue('IMHUB_NATIVE_WEB_ALLOW_PRIVATE', v === '1' ? '1' : null)}\n >\n <SelectTrigger><SelectValue /></SelectTrigger>\n <SelectContent>\n <SelectItem value=\"0\">OFF</SelectItem>\n <SelectItem value=\"1\">ON</SelectItem>\n </SelectContent>\n </Select>\n </LabeledField>\n\n <LabeledField label={t('security.web.timeoutMs')}>\n <Input\n type=\"number\"\n defaultValue={env.IMHUB_NATIVE_WEB_TIMEOUT_MS ?? ''}\n placeholder=\"30000\"\n onBlur={(e) => {\n const v = e.target.value.trim()\n void setValue('IMHUB_NATIVE_WEB_TIMEOUT_MS', v === '' ? null : v)\n }}\n />\n </LabeledField>\n\n <div className=\"sm:col-span-2\">\n <LabeledField label={t('security.web.ssrfWhitelist')}>\n <Input\n defaultValue={env.IMHUB_NATIVE_WEB_SSRF_WHITELIST ?? ''}\n placeholder={t('security.web.ssrfWhitelistPlaceholder')}\n onBlur={(e) => {\n const v = e.target.value.trim()\n void setValue('IMHUB_NATIVE_WEB_SSRF_WHITELIST', v === '' ? null : v)\n }}\n />\n </LabeledField>\n </div>\n </div>\n </ControlCard>\n\n {/* FS */}\n <ControlCard\n title={t('security.fs.title')}\n description={t('security.fs.description')}\n >\n <div className=\"grid gap-3 sm:grid-cols-2\">\n <LabeledField label={t('security.fs.restrict')}>\n <Select\n value={(env.IMHUB_NATIVE_FS_RESTRICT ?? '1') === '0' ? '0' : '1'}\n onValueChange={(v) => void setValue('IMHUB_NATIVE_FS_RESTRICT', v === '0' ? '0' : null)}\n >\n <SelectTrigger><SelectValue /></SelectTrigger>\n <SelectContent>\n <SelectItem value=\"1\">{t('security.fs.restrictOn')}</SelectItem>\n <SelectItem value=\"0\">{t('security.fs.restrictOff')}</SelectItem>\n </SelectContent>\n </Select>\n </LabeledField>\n\n <LabeledField label={t('security.fs.timeoutMs')}>\n <Input\n type=\"number\"\n defaultValue={env.IMHUB_NATIVE_FS_TIMEOUT_MS ?? ''}\n placeholder=\"60000\"\n onBlur={(e) => {\n const v = e.target.value.trim()\n void setValue('IMHUB_NATIVE_FS_TIMEOUT_MS', v === '' ? null : v)\n }}\n />\n </LabeledField>\n </div>\n </ControlCard>\n\n {/* Delegation cap */}\n <ControlCard\n title={t('security.delegation.title')}\n description={t('security.delegation.description')}\n envKey=\"IMHUB_NATIVE_CALL_AGENT_MAX_PER_TURN\"\n >\n <Input\n type=\"number\"\n defaultValue={env.IMHUB_NATIVE_CALL_AGENT_MAX_PER_TURN ?? ''}\n placeholder=\"2\"\n onBlur={(e) => {\n const v = e.target.value.trim()\n void setValue('IMHUB_NATIVE_CALL_AGENT_MAX_PER_TURN', v === '' ? null : v)\n }}\n />\n </ControlCard>\n\n {/* IM platform blacklist */}\n <ControlCard\n title={t('security.platformBlacklist.title')}\n description={t('security.platformBlacklist.description')}\n envKey=\"IMHUB_PLATFORM_BLACKLIST\"\n >\n <Input\n defaultValue={env.IMHUB_PLATFORM_BLACKLIST ?? ''}\n placeholder={t('security.platformBlacklist.placeholder')}\n onBlur={(e) => {\n const v = e.target.value.trim()\n void setValue('IMHUB_PLATFORM_BLACKLIST', v === '' ? null : v)\n }}\n />\n {diagnostics?.platformBlacklist && diagnostics.platformBlacklist.length > 0 && (\n <p className=\"mt-2 text-[10px] text-muted\">\n {t('security.platformBlacklist.currentLabel')}: {diagnostics.platformBlacklist.join(', ')}\n </p>\n )}\n </ControlCard>\n </div>\n )\n}\n\nfunction DiagRow({ label, ok, text }: { label: string; ok: boolean; text: string }): JSX.Element {\n return (\n <li className=\"flex items-start gap-2\">\n {ok\n ? <CheckCircle2 className=\"mt-0.5 h-4 w-4 shrink-0 text-success\" />\n : <AlertTriangle className=\"mt-0.5 h-4 w-4 shrink-0 text-warning\" />\n }\n <div className=\"flex-1\">\n <div className=\"font-medium\">{label}</div>\n <div className={cn('text-xs', ok ? 'text-muted' : 'text-warning')}>{text}</div>\n </div>\n </li>\n )\n}\n\nfunction ControlCard({\n title, description, envKey, children,\n}: { title: string; description: string; envKey?: string; children: React.ReactNode }): JSX.Element {\n return (\n <section className=\"rounded-lg border border-border bg-card p-4\">\n <div className=\"mb-2 flex flex-wrap items-baseline gap-2\">\n <h2 className=\"text-sm font-semibold\">{title}</h2>\n {envKey && <Badge variant=\"outline\" className=\"font-mono text-[10px]\">{envKey}</Badge>}\n </div>\n <p className=\"mb-3 text-xs text-muted\">{description}</p>\n {children}\n </section>\n )\n}\n\nfunction LabeledField({\n label, hint, children,\n}: { label: string; hint?: string; children: React.ReactNode }): JSX.Element {\n return (\n <div className=\"flex flex-col gap-1\">\n <label className=\"text-xs font-medium text-muted\">{label}</label>\n {children}\n {hint && <p className=\"text-[10px] text-muted\">{hint}</p>}\n </div>\n )\n}\n"],"names":["envBool","v","t","SettingsSecurityRoute","useTranslation","envQuery","useEnv","updateEnv","useUpdateEnv","diagnostics","setDiagnostics","useState","diagLoading","setDiagLoading","loadDiagnostics","res","client","err","toast","describeError","useEffect","setValue","key","value","env","jsxs","jsx","Button","Loader2","RefreshCcw","ShieldAlert","DiagRow","ControlCard","Input","e","LabeledField","Select","SelectTrigger","SelectValue","SelectContent","SelectItem","label","ok","text","CheckCircle2","AlertTriangle","cn","title","description","envKey","children","Badge","hint"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
- import{n as z,u as P,j as e,B as f,k as L,R as T,c as w,l as m,m as v,L as U,I as _}from"./index-DXI13nSQ.js";import{r as p}from"./react-Cb2sDjhD.js";import{C as y}from"./confirm-dialog-_BtUs6oW.js";import{j as O,k as B,l as W,m as q,b as M,c as H}from"./use-settings-DJlVLnjo.js";import{L as S}from"./loader-circle-BS5FFFg-.js";import{R as Q}from"./refresh-ccw-BPKXoMZa.js";import{P as V}from"./play-COQw7BqX.js";import{N as X}from"./network-MVE8s4TA.js";import{X as $}from"./x-OHUicFfn.js";import{S as G}from"./save-BXCmgeEj.js";import"./dialog-BVYFRXlI.js";import"./useQuery-CEwGD94N.js";/**
1
+ import{n as z,u as P,j as e,B as f,k as L,R as T,c as w,l as m,m as v,L as U,I as _}from"./index-CuNnG-5O.js";import{r as p}from"./react-Cb2sDjhD.js";import{C as y}from"./confirm-dialog-6dKowsEt.js";import{j as O,k as B,l as W,m as q,b as M,c as H}from"./use-settings-D3vI1Dwg.js";import{L as S}from"./loader-circle-CVEo3Hwx.js";import{R as Q}from"./refresh-ccw-DZHRYuFA.js";import{P as V}from"./play-BedplEoY.js";import{N as X}from"./network-3O1O1jsk.js";import{X as $}from"./x-D_eFtxsK.js";import{S as G}from"./save-DQlBAXAl.js";import"./dialog-Cv87qelJ.js";import"./useQuery-DIlm0vJ-.js";/**
2
2
  * @license lucide-react v0.469.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
5
5
  * See the LICENSE file in the root directory of this source tree.
6
6
  */const J=z("Square",[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",key:"afitv7"}]]),K={systemd:"success",background:"info",foreground:"warning",none:"default"},C=3e3,k=9090;function me(){const{t:s}=P(["settings","common"]),r=O(),t=r.data,a=t&&t.mode!=="none",i=B(),n=W(),o=q(),[u,x]=p.useState(!1),[h,g]=p.useState(!1);async function b(){try{await i.mutateAsync(),m.success(s("service.toast.started"))}catch(c){m.error(v(c,s).message)}}async function d(){try{await n.mutateAsync(),m.success(s("service.toast.stopped"))}catch(c){throw m.error(v(c,s).message),c}}async function l(){try{await o.mutateAsync(),m.success(s("service.toast.restarted"))}catch(c){throw m.error(v(c,s).message),c}}return e.jsxs("div",{className:"mx-auto flex max-w-3xl flex-col gap-4",children:[e.jsxs("header",{className:"flex flex-col gap-1",children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-3",children:[e.jsx("h1",{className:"text-xl font-semibold",children:s("service.title")}),e.jsxs(f,{variant:"ghost",size:"sm",className:"ml-auto",onClick:()=>r.refetch(),disabled:r.isFetching,"aria-label":s("actions.refresh",{ns:"common"}),children:[r.isFetching?e.jsx(S,{className:"h-4 w-4 animate-spin"}):e.jsx(Q,{className:"h-4 w-4"}),e.jsx("span",{className:"hidden sm:inline",children:s("actions.refresh",{ns:"common"})})]})]}),e.jsx("p",{className:"text-sm text-text-dim",children:s("service.subtitle")})]}),e.jsx(Y,{}),r.isLoading?e.jsx("div",{className:"h-48 rounded-md bg-surface-2 animate-pulse"}):t?e.jsxs("div",{className:"rounded-md border border-border bg-surface p-4",children:[e.jsxs("dl",{className:"grid grid-cols-[max-content_1fr] gap-x-4 gap-y-2 text-sm",children:[e.jsx("dt",{className:"text-text-dim",children:s("service.modeLabel")}),e.jsx("dd",{children:e.jsx(L,{variant:K[t.mode],children:s(`service.mode.${t.mode}`)})}),t.pid!=null&&e.jsxs(e.Fragment,{children:[e.jsx("dt",{className:"text-text-dim",children:s("service.pidLabel")}),e.jsx("dd",{className:"tabular-nums font-mono",children:t.pid})]}),t.uptime&&e.jsxs(e.Fragment,{children:[e.jsx("dt",{className:"text-text-dim",children:s("service.uptimeLabel")}),e.jsx("dd",{className:"tabular-nums",children:t.uptime})]}),t.web&&e.jsxs(e.Fragment,{children:[e.jsx("dt",{className:"text-text-dim",children:s("service.webLabel")}),e.jsxs("dd",{className:"font-mono text-xs",children:[t.web.bind,":",t.web.port]})]}),e.jsx("dt",{className:"text-text-dim",children:s("service.bootPhaseLabel")}),e.jsx("dd",{children:e.jsx("code",{className:"rounded bg-surface-2 px-1.5 py-0.5 text-xs",children:t.bootPhase})})]}),e.jsxs("div",{className:"mt-4 flex flex-wrap gap-2",children:[e.jsxs(f,{type:"button",variant:"default",size:"sm",disabled:a||i.isPending,onClick:()=>void b(),children:[i.isPending?e.jsx(S,{className:"h-4 w-4 animate-spin"}):e.jsx(V,{className:"h-4 w-4"}),s("service.actions.start")]}),e.jsxs(f,{type:"button",variant:"outline",size:"sm",disabled:!a||o.isPending,onClick:()=>g(!0),children:[e.jsx(T,{className:w("h-4 w-4",o.isPending&&"animate-spin")}),s("service.actions.restart")]}),e.jsxs(f,{type:"button",variant:"destructive",size:"sm",disabled:!a||n.isPending,onClick:()=>x(!0),children:[e.jsx(J,{className:"h-4 w-4"}),s("service.actions.stop")]})]})]}):null,e.jsx(y,{open:u,onOpenChange:x,title:s("service.actions.confirmStop"),description:s("service.actions.confirmStopDesc"),intent:"danger",confirmLabel:s("service.actions.stop"),onConfirm:d}),e.jsx(y,{open:h,onOpenChange:g,title:s("service.actions.confirmRestart"),description:s("service.actions.confirmRestartDesc"),intent:"danger",confirmLabel:s("service.actions.restart"),onConfirm:l})]})}function A(s){const r=s.trim();if(!r)return{ok:!0,value:void 0};const t=Number.parseInt(r,10);return!Number.isFinite(t)||t<1||t>65535?{ok:!1}:{ok:!0,value:t}}function Y(){const{t:s}=P(["settings","common"]),r=M(),t=H(),a=r.data,i=a?.webPort,n=a?.acpPort,[o,u]=p.useState(""),[x,h]=p.useState(""),[g,b]=p.useState(0);p.useEffect(()=>{a&&r.dataUpdatedAt!==g&&(u(i!=null?String(i):""),h(n!=null?String(n):""),b(r.dataUpdatedAt))},[a,r.dataUpdatedAt,i,n,g]);const d=A(o),l=A(x),c=!d.ok,j=!l.ok,D=p.useMemo(()=>{if(!a)return!1;const N=d.ok?d.value:void 0,I=l.ok?l.value:void 0;return N!==i||I!==n},[a,d,l,i,n]);function F(){u(i!=null?String(i):""),h(n!=null?String(n):"")}async function E(){if(!(!a||c||j))try{await t.mutateAsync({...a,webPort:d.ok?d.value:void 0,acpPort:l.ok?l.value:void 0}),m.success(s("service.ports.toast.saved"))}catch(N){m.error(v(N,s).message)}}return r.isLoading?e.jsx("div",{className:"h-32 rounded-md bg-surface-2 animate-pulse"}):e.jsxs("section",{className:"rounded-md border border-border bg-surface",children:[e.jsxs("header",{className:"flex items-center gap-2 border-b border-border px-4 py-3",children:[e.jsx(X,{className:"h-4 w-4 text-text-dim"}),e.jsx("h2",{className:"text-sm font-semibold",children:s("service.ports.title")})]}),e.jsx("p",{className:"px-4 pt-2 text-xs text-text-dim",children:s("service.ports.subtitle")}),e.jsxs("div",{className:"grid grid-cols-1 gap-3 px-4 py-3 sm:grid-cols-2",children:[e.jsx(R,{id:"web-port",label:s("service.ports.webPort"),hint:s("service.ports.webPortHint",{default:C}),value:o,onChange:u,invalid:c,placeholder:String(C)}),a?.features?.remoteAgent?e.jsx(R,{id:"acp-port",label:s("service.ports.acpPort"),hint:s("service.ports.acpPortHint",{default:k}),value:x,onChange:h,invalid:j,placeholder:String(k)}):null]}),D&&e.jsxs("div",{className:"flex items-center gap-2 border-t border-border px-4 py-3",children:[e.jsx(L,{variant:"warning",children:s("service.ports.restartRequired")}),e.jsxs(f,{variant:"ghost",size:"sm",className:"ml-auto",onClick:F,disabled:t.isPending,children:[e.jsx($,{className:"h-4 w-4"}),s("service.ports.discard")]}),e.jsxs(f,{size:"sm",onClick:()=>void E(),disabled:t.isPending||c||j,children:[t.isPending?e.jsx(S,{className:"h-4 w-4 animate-spin"}):e.jsx(G,{className:"h-4 w-4"}),t.isPending?s("service.ports.saving"):s("service.ports.save")]})]})]})}function R({id:s,label:r,hint:t,value:a,onChange:i,invalid:n,placeholder:o}){const{t:u}=P("settings");return e.jsxs("div",{className:"flex flex-col gap-1",children:[e.jsx(U,{htmlFor:s,className:"text-xs font-medium",children:r}),e.jsx(_,{id:s,type:"number",inputMode:"numeric",min:1,max:65535,value:a,onChange:x=>i(x.target.value),placeholder:o,"aria-invalid":n,className:w("font-mono",n&&"border-danger focus:ring-danger")}),e.jsx("p",{className:w("text-[11px]",n?"text-danger":"text-text-dim"),children:n?u("service.ports.invalidPort"):t})]})}export{me as default};
7
- //# sourceMappingURL=service-DXGUZTCp.js.map
7
+ //# sourceMappingURL=service-BJFnNe7Y.js.map