agim-cli 1.2.124 → 1.2.126

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 (182) hide show
  1. package/CHANGELOG.md +116 -0
  2. package/dist/core/llm/auto-compact.d.ts +33 -0
  3. package/dist/core/llm/auto-compact.d.ts.map +1 -1
  4. package/dist/core/llm/auto-compact.js +64 -11
  5. package/dist/core/llm/auto-compact.js.map +1 -1
  6. package/dist/core/llm/web-dispatcher.d.ts +19 -0
  7. package/dist/core/llm/web-dispatcher.d.ts.map +1 -1
  8. package/dist/core/llm/web-dispatcher.js +24 -0
  9. package/dist/core/llm/web-dispatcher.js.map +1 -1
  10. package/dist/web/public/assets/{a2a-DoPVhKvM.js → a2a-DGmlA1kI.js} +3 -3
  11. package/dist/web/public/assets/{a2a-DoPVhKvM.js.map → a2a-DGmlA1kI.js.map} +1 -1
  12. package/dist/web/public/assets/{activity-CegIax_4.js → activity-s8eXJqQ-.js} +2 -2
  13. package/dist/web/public/assets/{activity-CegIax_4.js.map → activity-s8eXJqQ-.js.map} +1 -1
  14. package/dist/web/public/assets/{admins-BCOor7EH.js → admins-BSDr-HXb.js} +2 -2
  15. package/dist/web/public/assets/{admins-BCOor7EH.js.map → admins-BSDr-HXb.js.map} +1 -1
  16. package/dist/web/public/assets/{agents-BLw0_jKM.js → agents-Dp4swPPC.js} +2 -2
  17. package/dist/web/public/assets/{agents-BLw0_jKM.js.map → agents-Dp4swPPC.js.map} +1 -1
  18. package/dist/web/public/assets/{approvals-CXQok6Cz.js → approvals-BzShPdMC.js} +2 -2
  19. package/dist/web/public/assets/{approvals-CXQok6Cz.js.map → approvals-BzShPdMC.js.map} +1 -1
  20. package/dist/web/public/assets/arrow-up-B0EZtRDW.js +12 -0
  21. package/dist/web/public/assets/arrow-up-B0EZtRDW.js.map +1 -0
  22. package/dist/web/public/assets/{asks-xP2Otb6U.js → asks-BQtIx6ky.js} +2 -2
  23. package/dist/web/public/assets/{asks-xP2Otb6U.js.map → asks-BQtIx6ky.js.map} +1 -1
  24. package/dist/web/public/assets/audit-BDIEW7z9.js +2 -0
  25. package/dist/web/public/assets/{audit-DNYwnXg8.js.map → audit-BDIEW7z9.js.map} +1 -1
  26. package/dist/web/public/assets/{bell-WsDs6GmV.js → bell-Dw3rtAnD.js} +2 -2
  27. package/dist/web/public/assets/{bell-WsDs6GmV.js.map → bell-Dw3rtAnD.js.map} +1 -1
  28. package/dist/web/public/assets/{bgjobs-B2BaMU2j.js → bgjobs-DEQOeNl0.js} +3 -3
  29. package/dist/web/public/assets/{bgjobs-B2BaMU2j.js.map → bgjobs-DEQOeNl0.js.map} +1 -1
  30. package/dist/web/public/assets/{brain-B8fJEQyw.js → brain-BqR8-Zwv.js} +2 -2
  31. package/dist/web/public/assets/{brain-B8fJEQyw.js.map → brain-BqR8-Zwv.js.map} +1 -1
  32. package/dist/web/public/assets/{briefcase-Cwk6Nkg7.js → briefcase-_b-XDwyU.js} +2 -2
  33. package/dist/web/public/assets/{briefcase-Cwk6Nkg7.js.map → briefcase-_b-XDwyU.js.map} +1 -1
  34. package/dist/web/public/assets/{chevron-right-D1NFHRso.js → chevron-right-DkURvNIb.js} +2 -2
  35. package/dist/web/public/assets/{chevron-right-D1NFHRso.js.map → chevron-right-DkURvNIb.js.map} +1 -1
  36. package/dist/web/public/assets/{circle-check-C5jWAGrG.js → circle-check-D0tRr1mi.js} +2 -2
  37. package/dist/web/public/assets/{circle-check-C5jWAGrG.js.map → circle-check-D0tRr1mi.js.map} +1 -1
  38. package/dist/web/public/assets/{circle-check-big-BMc_m83k.js → circle-check-big-C737-DM7.js} +2 -2
  39. package/dist/web/public/assets/{circle-check-big-BMc_m83k.js.map → circle-check-big-C737-DM7.js.map} +1 -1
  40. package/dist/web/public/assets/{circle-x-DK_VnW5O.js → circle-x-KWl9wiZn.js} +2 -2
  41. package/dist/web/public/assets/{circle-x-DK_VnW5O.js.map → circle-x-KWl9wiZn.js.map} +1 -1
  42. package/dist/web/public/assets/{confirm-dialog-CBbIPd8e.js → confirm-dialog-CyTBb_Dj.js} +2 -2
  43. package/dist/web/public/assets/{confirm-dialog-CBbIPd8e.js.map → confirm-dialog-CyTBb_Dj.js.map} +1 -1
  44. package/dist/web/public/assets/data-table-Br_AhsiO.js +7 -0
  45. package/dist/web/public/assets/data-table-Br_AhsiO.js.map +1 -0
  46. package/dist/web/public/assets/{dialog-Byj1GNwS.js → dialog-ChaF-Zu5.js} +2 -2
  47. package/dist/web/public/assets/{dialog-Byj1GNwS.js.map → dialog-ChaF-Zu5.js.map} +1 -1
  48. package/dist/web/public/assets/{download-PHySCcQT.js → download-B7FO0Y5B.js} +2 -2
  49. package/dist/web/public/assets/{download-PHySCcQT.js.map → download-B7FO0Y5B.js.map} +1 -1
  50. package/dist/web/public/assets/{email-BRL-m3Nq.js → email-DIEmIapc.js} +2 -2
  51. package/dist/web/public/assets/{email-BRL-m3Nq.js.map → email-DIEmIapc.js.map} +1 -1
  52. package/dist/web/public/assets/{empty-state-C4sK7D2H.js → empty-state-CP9D3-8U.js} +2 -2
  53. package/dist/web/public/assets/{empty-state-C4sK7D2H.js.map → empty-state-CP9D3-8U.js.map} +1 -1
  54. package/dist/web/public/assets/{external-link-CFqp81U8.js → external-link-Ba2-bs-3.js} +2 -2
  55. package/dist/web/public/assets/{external-link-CFqp81U8.js.map → external-link-Ba2-bs-3.js.map} +1 -1
  56. package/dist/web/public/assets/{eye-znt0VqpD.js → eye-CcL-RUbp.js} +2 -2
  57. package/dist/web/public/assets/{eye-znt0VqpD.js.map → eye-CcL-RUbp.js.map} +1 -1
  58. package/dist/web/public/assets/facts-DqwcWab7.js +2 -0
  59. package/dist/web/public/assets/{facts-C-z_MHuR.js.map → facts-DqwcWab7.js.map} +1 -1
  60. package/dist/web/public/assets/{goals-DjW7xUUU.js → goals-DBghjX3s.js} +2 -2
  61. package/dist/web/public/assets/{goals-DjW7xUUU.js.map → goals-DBghjX3s.js.map} +1 -1
  62. package/dist/web/public/assets/{health-u3wVVbRE.js → health-DV_mII-T.js} +2 -2
  63. package/dist/web/public/assets/{health-u3wVVbRE.js.map → health-DV_mII-T.js.map} +1 -1
  64. package/dist/web/public/assets/{heart-pulse-CuRQDV1Z.js → heart-pulse-C3TFU_Th.js} +2 -2
  65. package/dist/web/public/assets/{heart-pulse-CuRQDV1Z.js.map → heart-pulse-C3TFU_Th.js.map} +1 -1
  66. package/dist/web/public/assets/{heartbeat-fKsOewwO.js → heartbeat-NRTRObLx.js} +2 -2
  67. package/dist/web/public/assets/{heartbeat-fKsOewwO.js.map → heartbeat-NRTRObLx.js.map} +1 -1
  68. package/dist/web/public/assets/{hot-ClV4mA4o.js → hot-BNH34Amf.js} +2 -2
  69. package/dist/web/public/assets/{hot-ClV4mA4o.js.map → hot-BNH34Amf.js.map} +1 -1
  70. package/dist/web/public/assets/index-De8LfCtL.css +1 -0
  71. package/dist/web/public/assets/{index-7yc9y9We.js → index-igqJknUt.js} +36 -36
  72. package/dist/web/public/assets/index-igqJknUt.js.map +1 -0
  73. package/dist/web/public/assets/{installed-BtgOuIiG.js → installed-B2goeV3S.js} +2 -2
  74. package/dist/web/public/assets/{installed-BtgOuIiG.js.map → installed-B2goeV3S.js.map} +1 -1
  75. package/dist/web/public/assets/jobs-CZSLi1IN.js +2 -0
  76. package/dist/web/public/assets/{jobs-eiyYlyJG.js.map → jobs-CZSLi1IN.js.map} +1 -1
  77. package/dist/web/public/assets/{layout-DuYDIza7.js → layout-BOtnmIxG.js} +2 -2
  78. package/dist/web/public/assets/{layout-DuYDIza7.js.map → layout-BOtnmIxG.js.map} +1 -1
  79. package/dist/web/public/assets/{layout-Oy_rbq06.js → layout-BboemGJs.js} +2 -2
  80. package/dist/web/public/assets/{layout-Oy_rbq06.js.map → layout-BboemGJs.js.map} +1 -1
  81. package/dist/web/public/assets/{layout-BpwuHk-s.js → layout-CBbV3YNj.js} +2 -2
  82. package/dist/web/public/assets/{layout-BpwuHk-s.js.map → layout-CBbV3YNj.js.map} +1 -1
  83. package/dist/web/public/assets/layout-CsScgPAG.js +2 -0
  84. package/dist/web/public/assets/layout-CsScgPAG.js.map +1 -0
  85. package/dist/web/public/assets/{layout-CQ-wMpff.js → layout-D8Kes1e7.js} +2 -2
  86. package/dist/web/public/assets/{layout-CQ-wMpff.js.map → layout-D8Kes1e7.js.map} +1 -1
  87. package/dist/web/public/assets/{llm-Bs-G20DR.js → llm-78WCRfgL.js} +2 -2
  88. package/dist/web/public/assets/{llm-Bs-G20DR.js.map → llm-78WCRfgL.js.map} +1 -1
  89. package/dist/web/public/assets/{loader-circle-CwM-6o9f.js → loader-circle-BMJKoafx.js} +2 -2
  90. package/dist/web/public/assets/{loader-circle-CwM-6o9f.js.map → loader-circle-BMJKoafx.js.map} +1 -1
  91. package/dist/web/public/assets/{map-pin-MuWUOLcD.js → map-pin-QiHcQUfd.js} +2 -2
  92. package/dist/web/public/assets/{map-pin-MuWUOLcD.js.map → map-pin-QiHcQUfd.js.map} +1 -1
  93. package/dist/web/public/assets/{mcp-D9ob2tYX.js → mcp-oeRvo7Aw.js} +2 -2
  94. package/dist/web/public/assets/{mcp-D9ob2tYX.js.map → mcp-oeRvo7Aw.js.map} +1 -1
  95. package/dist/web/public/assets/{memos-Bd7EttaU.js → memos-D7RJn57X.js} +3 -3
  96. package/dist/web/public/assets/{memos-Bd7EttaU.js.map → memos-D7RJn57X.js.map} +1 -1
  97. package/dist/web/public/assets/{messengers-CbnTejKi.js → messengers-BORNy_SW.js} +2 -2
  98. package/dist/web/public/assets/{messengers-CbnTejKi.js.map → messengers-BORNy_SW.js.map} +1 -1
  99. package/dist/web/public/assets/native-agent-CNPVWQ9T.js +12 -0
  100. package/dist/web/public/assets/native-agent-CNPVWQ9T.js.map +1 -0
  101. package/dist/web/public/assets/{network-Cdb-7ukZ.js → network-D--kF74M.js} +2 -2
  102. package/dist/web/public/assets/{network-Cdb-7ukZ.js.map → network-D--kF74M.js.map} +1 -1
  103. package/dist/web/public/assets/{outbox-BsdSXMUW.js → outbox-BvhcDpdc.js} +3 -3
  104. package/dist/web/public/assets/{outbox-BsdSXMUW.js.map → outbox-BvhcDpdc.js.map} +1 -1
  105. package/dist/web/public/assets/{pagination-BuNA-fxU.js → pagination-Dl8k4a91.js} +2 -2
  106. package/dist/web/public/assets/{pagination-BuNA-fxU.js.map → pagination-Dl8k4a91.js.map} +1 -1
  107. package/dist/web/public/assets/{persona-V0lGwDex.js → persona-Dy-fuCBR.js} +2 -2
  108. package/dist/web/public/assets/{persona-V0lGwDex.js.map → persona-Dy-fuCBR.js.map} +1 -1
  109. package/dist/web/public/assets/{play-BdDkAgqw.js → play-C0gIFP8e.js} +2 -2
  110. package/dist/web/public/assets/{play-BdDkAgqw.js.map → play-C0gIFP8e.js.map} +1 -1
  111. package/dist/web/public/assets/{plus-FlZ1UWzO.js → plus-DW6xBDJv.js} +2 -2
  112. package/dist/web/public/assets/{plus-FlZ1UWzO.js.map → plus-DW6xBDJv.js.map} +1 -1
  113. package/dist/web/public/assets/{policy-CpWgbIqb.js → policy-DW_kZo8b.js} +2 -2
  114. package/dist/web/public/assets/{policy-CpWgbIqb.js.map → policy-DW_kZo8b.js.map} +1 -1
  115. package/dist/web/public/assets/{refresh-ccw-l9Z6wukO.js → refresh-ccw-D6fZ_xYR.js} +2 -2
  116. package/dist/web/public/assets/{refresh-ccw-l9Z6wukO.js.map → refresh-ccw-D6fZ_xYR.js.map} +1 -1
  117. package/dist/web/public/assets/{reminders-Coh0D3Fv.js → reminders-1V1_6Rij.js} +3 -3
  118. package/dist/web/public/assets/{reminders-Coh0D3Fv.js.map → reminders-1V1_6Rij.js.map} +1 -1
  119. package/dist/web/public/assets/{save-CllPj34m.js → save-S79SG1Qr.js} +2 -2
  120. package/dist/web/public/assets/{save-CllPj34m.js.map → save-S79SG1Qr.js.map} +1 -1
  121. package/dist/web/public/assets/{schedules-0rlWAXrz.js → schedules-BsknKkCe.js} +3 -3
  122. package/dist/web/public/assets/{schedules-0rlWAXrz.js.map → schedules-BsknKkCe.js.map} +1 -1
  123. package/dist/web/public/assets/{search-DDbspILM.js → search-CKf3g91u.js} +2 -2
  124. package/dist/web/public/assets/{search-DDbspILM.js.map → search-CKf3g91u.js.map} +1 -1
  125. package/dist/web/public/assets/search-HQWlCBKK.js +17 -0
  126. package/dist/web/public/assets/search-HQWlCBKK.js.map +1 -0
  127. package/dist/web/public/assets/{security-SlM_YhUr.js → security-B8sBUf6e.js} +2 -2
  128. package/dist/web/public/assets/{security-SlM_YhUr.js.map → security-B8sBUf6e.js.map} +1 -1
  129. package/dist/web/public/assets/{service-BnbEtpH9.js → service-CB-aBmLS.js} +2 -2
  130. package/dist/web/public/assets/{service-BnbEtpH9.js.map → service-CB-aBmLS.js.map} +1 -1
  131. package/dist/web/public/assets/{status-badge-DKc8aeGe.js → status-badge-BYFw2Sxd.js} +2 -2
  132. package/dist/web/public/assets/{status-badge-DKc8aeGe.js.map → status-badge-BYFw2Sxd.js.map} +1 -1
  133. package/dist/web/public/assets/{subtasks-2PcTOMXV.js → subtasks-CBrBqNyr.js} +3 -3
  134. package/dist/web/public/assets/{subtasks-2PcTOMXV.js.map → subtasks-CBrBqNyr.js.map} +1 -1
  135. package/dist/web/public/assets/{table-BIc6RbM1.js → table-D5hzEZ1F.js} +2 -2
  136. package/dist/web/public/assets/{table-BIc6RbM1.js.map → table-D5hzEZ1F.js.map} +1 -1
  137. package/dist/web/public/assets/{topn-Bn-Y2tpa.js → topn-7_FUU4Zs.js} +2 -2
  138. package/dist/web/public/assets/{topn-Bn-Y2tpa.js.map → topn-7_FUU4Zs.js.map} +1 -1
  139. package/dist/web/public/assets/{trash-2-UPbZm-9k.js → trash-2-BkaJ1KrC.js} +2 -2
  140. package/dist/web/public/assets/{trash-2-UPbZm-9k.js.map → trash-2-BkaJ1KrC.js.map} +1 -1
  141. package/dist/web/public/assets/{use-background-tasks-DyaXWxmJ.js → use-background-tasks-Crs1dlm6.js} +2 -2
  142. package/dist/web/public/assets/{use-background-tasks-DyaXWxmJ.js.map → use-background-tasks-Crs1dlm6.js.map} +1 -1
  143. package/dist/web/public/assets/{use-llm-admin-DO9nbZeO.js → use-llm-admin-CQ3lbm_y.js} +2 -2
  144. package/dist/web/public/assets/{use-llm-admin-DO9nbZeO.js.map → use-llm-admin-CQ3lbm_y.js.map} +1 -1
  145. package/dist/web/public/assets/{use-memory-Bf-AR2xS.js → use-memory-CMOa52NU.js} +2 -2
  146. package/dist/web/public/assets/{use-memory-Bf-AR2xS.js.map → use-memory-CMOa52NU.js.map} +1 -1
  147. package/dist/web/public/assets/{use-observability-Beido9rh.js → use-observability-BTRXt3kO.js} +2 -2
  148. package/dist/web/public/assets/{use-observability-Beido9rh.js.map → use-observability-BTRXt3kO.js.map} +1 -1
  149. package/dist/web/public/assets/use-settings-D2TsP4vN.js +2 -0
  150. package/dist/web/public/assets/use-settings-D2TsP4vN.js.map +1 -0
  151. package/dist/web/public/assets/{use-workspace-BoRhOeiK.js → use-workspace-BobEMtXw.js} +2 -2
  152. package/dist/web/public/assets/{use-workspace-BoRhOeiK.js.map → use-workspace-BobEMtXw.js.map} +1 -1
  153. package/dist/web/public/assets/{useQuery-dWspFqPU.js → useQuery-B6dy499I.js} +2 -2
  154. package/dist/web/public/assets/{useQuery-dWspFqPU.js.map → useQuery-B6dy499I.js.map} +1 -1
  155. package/dist/web/public/assets/{vector-DX4RPkXe.js → vector-v7xjphrN.js} +2 -2
  156. package/dist/web/public/assets/{vector-DX4RPkXe.js.map → vector-v7xjphrN.js.map} +1 -1
  157. package/dist/web/public/assets/{viewer-CRv__j_2.js → viewer-BSC-2Lhp.js} +2 -2
  158. package/dist/web/public/assets/{viewer-CRv__j_2.js.map → viewer-BSC-2Lhp.js.map} +1 -1
  159. package/dist/web/public/assets/{workspace-DU5F-0GM.js → workspace-BtGPH9Sx.js} +2 -2
  160. package/dist/web/public/assets/{workspace-DU5F-0GM.js.map → workspace-BtGPH9Sx.js.map} +1 -1
  161. package/dist/web/public/assets/{workspaces-DCEYab_X.js → workspaces-_PTrUhIq.js} +2 -2
  162. package/dist/web/public/assets/{workspaces-DCEYab_X.js.map → workspaces-_PTrUhIq.js.map} +1 -1
  163. package/dist/web/public/assets/{x-CioZGQfq.js → x-BHW436CN.js} +2 -2
  164. package/dist/web/public/assets/{x-CioZGQfq.js.map → x-BHW436CN.js.map} +1 -1
  165. package/dist/web/public/index.html +2 -2
  166. package/dist/web/server.d.ts.map +1 -1
  167. package/dist/web/server.js +120 -1
  168. package/dist/web/server.js.map +1 -1
  169. package/package.json +1 -1
  170. package/dist/web/public/assets/audit-DNYwnXg8.js +0 -2
  171. package/dist/web/public/assets/data-table-Dw8WxmGK.js +0 -17
  172. package/dist/web/public/assets/data-table-Dw8WxmGK.js.map +0 -1
  173. package/dist/web/public/assets/facts-C-z_MHuR.js +0 -2
  174. package/dist/web/public/assets/index-7yc9y9We.js.map +0 -1
  175. package/dist/web/public/assets/index-hiagPF9i.css +0 -1
  176. package/dist/web/public/assets/jobs-eiyYlyJG.js +0 -2
  177. package/dist/web/public/assets/layout-CChyylLw.js +0 -2
  178. package/dist/web/public/assets/layout-CChyylLw.js.map +0 -1
  179. package/dist/web/public/assets/native-agent-BmBH1fBn.js +0 -7
  180. package/dist/web/public/assets/native-agent-BmBH1fBn.js.map +0 -1
  181. package/dist/web/public/assets/use-settings-J28YEhhv.js +0 -2
  182. package/dist/web/public/assets/use-settings-J28YEhhv.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"workspace-DU5F-0GM.js","sources":["../../node_modules/lucide-react/dist/esm/icons/file.js","../../node_modules/lucide-react/dist/esm/icons/folder-tree.js","../../node_modules/lucide-react/dist/esm/icons/folder.js","../../src/routes/workspace.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 File = createLucideIcon(\"File\", [\n [\"path\", { d: \"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z\", key: \"1rqfz7\" }],\n [\"path\", { d: \"M14 2v4a2 2 0 0 0 2 2h4\", key: \"tnqrlb\" }]\n]);\n\nexport { File as default };\n//# sourceMappingURL=file.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 FolderTree = createLucideIcon(\"FolderTree\", [\n [\n \"path\",\n {\n d: \"M20 10a1 1 0 0 0 1-1V6a1 1 0 0 0-1-1h-2.5a1 1 0 0 1-.8-.4l-.9-1.2A1 1 0 0 0 15 3h-2a1 1 0 0 0-1 1v5a1 1 0 0 0 1 1Z\",\n key: \"hod4my\"\n }\n ],\n [\n \"path\",\n {\n d: \"M20 21a1 1 0 0 0 1-1v-3a1 1 0 0 0-1-1h-2.9a1 1 0 0 1-.88-.55l-.42-.85a1 1 0 0 0-.92-.6H13a1 1 0 0 0-1 1v5a1 1 0 0 0 1 1Z\",\n key: \"w4yl2u\"\n }\n ],\n [\"path\", { d: \"M3 5a2 2 0 0 0 2 2h3\", key: \"f2jnh7\" }],\n [\"path\", { d: \"M3 3v13a2 2 0 0 0 2 2h3\", key: \"k8epm1\" }]\n]);\n\nexport { FolderTree as default };\n//# sourceMappingURL=folder-tree.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 Folder = createLucideIcon(\"Folder\", [\n [\n \"path\",\n {\n d: \"M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z\",\n key: \"1kt360\"\n }\n ]\n]);\n\nexport { Folder as default };\n//# sourceMappingURL=folder.js.map\n","/**\n * /workspace — file browser + editor for the per-agent CWD.\n *\n * Single-column finder UI: pick an agent → drill into the workspace\n * via ?path=, click a file → opens the editor pane below the\n * listing. No recursive tree because the listing handler is cheap\n * and the typical workspace has ≤ 30 entries per dir.\n *\n * Editor is a plain `<textarea>` — Monaco / CodeMirror would balloon\n * the bundle for what is realistically operator-edits to\n * CLAUDE.md / AGENTS.md / config snippets. Binary files render a\n * read-only placeholder. Truncated files (> 1 MiB) block save to\n * prevent destructive overwrites.\n */\n\nimport { useEffect, useMemo, useState } from 'react'\nimport { useSearchParams } from 'react-router-dom'\nimport { useTranslation } from 'react-i18next'\nimport { toast } from 'sonner'\nimport { ChevronUp, File, Folder, FolderTree, Loader2, RefreshCcw } from 'lucide-react'\n\nimport { Topbar } from '@/components/shell/topbar'\nimport { EmptyState } from '@/components/common/empty-state'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { Label } from '@/components/ui/label'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@/components/ui/select'\nimport {\n useAgentsStatus,\n useWorkspaceFiles,\n useWriteWorkspaceFile,\n} from '@/hooks/use-workspace'\nimport { describeError } from '@/lib/api/errors'\nimport type {\n WorkspaceFileContent,\n WorkspaceFileEntry,\n} from '@/types/api'\nimport { cn } from '@/lib/utils'\n\nexport default function WorkspaceRoute(): JSX.Element {\n const { t } = useTranslation(['workspace', 'common'])\n const [params, setParams] = useSearchParams()\n const agent = params.get('agent') ?? ''\n const path = params.get('path') ?? ''\n\n const agentsQuery = useAgentsStatus()\n const agents = agentsQuery.data ?? {}\n const agentNames = useMemo(() => Object.keys(agents).sort(), [agents])\n\n /** Query may resolve to either a dir listing or file content — we\n * branch on `data.type` in the render. */\n const filesQuery = useWorkspaceFiles(agent, path, { enabled: Boolean(agent) })\n const data = filesQuery.data\n const isDir = data?.type === 'dir'\n const isFile = data?.type === 'file'\n\n function patchParams(patch: Record<string, string | null>): void {\n const next = new URLSearchParams(params)\n for (const [k, v] of Object.entries(patch)) {\n if (v == null || v === '') next.delete(k)\n else next.set(k, v)\n }\n setParams(next, { replace: false })\n }\n\n function setAgent(next: string): void {\n // Switching agent always resets the path so we don't drag a\n // claude-specific path into codex's workspace.\n patchParams({ agent: next || null, path: null })\n }\n function navigateTo(nextPath: string): void {\n patchParams({ path: nextPath || null })\n }\n function navigateUp(): void {\n if (!path) return\n const idx = path.lastIndexOf('/')\n const parent = idx >= 0 ? path.slice(0, idx) : ''\n navigateTo(parent)\n }\n\n return (\n <div className=\"flex min-h-dvh flex-col bg-bg\">\n <Topbar />\n\n <main className=\"mx-auto flex w-full max-w-7xl flex-1 flex-col gap-4 px-3 py-4 sm:px-4 pb-safe\">\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('pageTitle')}</h1>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"ml-auto\"\n onClick={() => filesQuery.refetch()}\n disabled={!agent || filesQuery.isFetching}\n aria-label={t('actions.refresh', { ns: 'common' })}\n >\n {filesQuery.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('subtitle')}</p>\n </header>\n\n {/* Agent picker */}\n <div className=\"flex flex-wrap items-end gap-2\">\n <div className=\"flex flex-col gap-1\">\n <Label htmlFor=\"agent\" className=\"text-xs text-text-dim\">\n {t('agentPicker.label')}\n </Label>\n <Select value={agent || '__placeholder__'} onValueChange={(v) => setAgent(v === '__placeholder__' ? '' : v)}>\n <SelectTrigger id=\"agent\" className=\"w-64\">\n <SelectValue placeholder={t('agentPicker.placeholder')} />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"__placeholder__\" disabled>\n {t('agentPicker.placeholder')}\n </SelectItem>\n {agentNames.map((name) => (\n <SelectItem key={name} value={name}>\n <span className=\"flex items-center gap-2\">\n <span className=\"font-mono text-xs\">{name}</span>\n <span className={cn(\n 'inline-block h-1.5 w-1.5 rounded-full',\n agents[name] ? 'bg-success' : 'bg-text-muted',\n )} />\n <span className=\"text-text-dim text-xs\">\n {agents[name] ? t('agentPicker.online') : t('agentPicker.offline')}\n </span>\n </span>\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n\n {!agent ? (\n <EmptyState\n icon={<FolderTree />}\n title={t('noAgent.title')}\n description={t('noAgent.description')}\n />\n ) : (\n <>\n {/* Breadcrumb + Up */}\n <Breadcrumb\n path={path}\n onNavigate={navigateTo}\n onUp={navigateUp}\n />\n\n {filesQuery.isLoading ? (\n <div className=\"h-32 rounded-md bg-surface-2 animate-pulse\" />\n ) : isDir && data?.type === 'dir' ? (\n <DirListing\n entries={data.entries}\n onEnter={(entry) => navigateTo(\n path ? `${path}/${entry.name}` : entry.name,\n )}\n />\n ) : isFile && data?.type === 'file' ? (\n <FileEditor\n file={data}\n agent={agent}\n onBack={navigateUp}\n />\n ) : (\n <EmptyState\n icon={<FolderTree />}\n title={t('editor.loadFailed')}\n description={filesQuery.error?.message ?? ''}\n />\n )}\n </>\n )}\n </main>\n </div>\n )\n}\n\ninterface BreadcrumbProps {\n path: string\n onNavigate: (path: string) => void\n onUp: () => void\n}\n\nfunction Breadcrumb({ path, onNavigate, onUp }: BreadcrumbProps): JSX.Element {\n const { t } = useTranslation('workspace')\n const parts = path ? path.split('/') : []\n return (\n <div className=\"flex flex-wrap items-center gap-1 text-sm\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={onUp}\n disabled={!path}\n aria-label={t('tree.up')}\n >\n <ChevronUp className=\"h-4 w-4\" />\n </Button>\n <button\n type=\"button\"\n onClick={() => onNavigate('')}\n className=\"text-text-dim hover:text-text rounded px-1 py-0.5\"\n >\n {t('tree.rootLabel')}\n </button>\n {parts.map((part, i) => {\n const upto = parts.slice(0, i + 1).join('/')\n return (\n <span key={upto} className=\"flex items-center gap-1\">\n <span className=\"text-text-muted\">/</span>\n <button\n type=\"button\"\n onClick={() => onNavigate(upto)}\n className=\"text-text-dim hover:text-text rounded px-1 py-0.5\"\n >\n {part}\n </button>\n </span>\n )\n })}\n </div>\n )\n}\n\ninterface DirListingProps {\n entries: WorkspaceFileEntry[]\n onEnter: (entry: WorkspaceFileEntry) => void\n}\n\nfunction DirListing({ entries, onEnter }: DirListingProps): JSX.Element {\n const { t } = useTranslation('workspace')\n if (entries.length === 0) {\n return <div className=\"rounded-md border border-border bg-surface p-3 text-sm text-text-dim\">{t('tree.empty')}</div>\n }\n return (\n <ul className=\"divide-y divide-border rounded-md border border-border bg-surface\">\n {entries.map((e) => (\n <li key={e.name}>\n <button\n type=\"button\"\n onClick={() => onEnter(e)}\n disabled={e.broken || e.symlink_escape}\n className={cn(\n 'flex w-full items-center gap-2 px-3 py-2 text-sm text-left',\n 'transition-colors hover:bg-surface-hover disabled:opacity-50 disabled:cursor-not-allowed',\n )}\n >\n {e.isDir ? <Folder className=\"h-4 w-4 text-accent\" /> : <File className=\"h-4 w-4 text-text-dim\" />}\n <span className={e.isDir ? 'font-medium text-text' : 'text-text'}>{e.name}</span>\n {e.symlink_escape && (\n <Badge variant=\"warning\" className=\"ml-2\">{t('tree.symlinkEscape')}</Badge>\n )}\n {e.broken && !e.symlink_escape && (\n <Badge variant=\"danger\" className=\"ml-2\">{t('tree.broken')}</Badge>\n )}\n {!e.isDir && e.size != null && (\n <span className=\"ml-auto text-xs text-text-muted tabular-nums\">{formatSize(e.size)}</span>\n )}\n {e.mtime && (\n <span className={cn('text-xs text-text-muted tabular-nums', !e.isDir && e.size != null ? 'ml-3' : 'ml-auto')}>\n {formatTime(e.mtime)}\n </span>\n )}\n </button>\n </li>\n ))}\n </ul>\n )\n}\n\ninterface FileEditorProps {\n file: WorkspaceFileContent\n agent: string\n onBack: () => void\n}\n\nfunction FileEditor({ file, agent, onBack }: FileEditorProps): JSX.Element {\n const { t } = useTranslation(['workspace', 'common'])\n const [draft, setDraft] = useState(file.content)\n useEffect(() => { setDraft(file.content) }, [file.content])\n const dirty = draft !== file.content\n\n const write = useWriteWorkspaceFile()\n\n async function onSave(): Promise<void> {\n try {\n await write.mutateAsync({ agent, path: file.path, body: { content: draft } })\n toast.success(t('toast.saved', { path: file.path }))\n } catch (err) {\n const { message } = describeError(err, t)\n toast.error(message)\n }\n }\n\n if (file.encoding === 'base64') {\n return (\n <div className=\"rounded-md border border-border bg-surface p-4 text-sm\">\n <Button variant=\"ghost\" size=\"sm\" onClick={onBack} className=\"mb-3\">\n <ChevronUp className=\"h-4 w-4\" />\n {t('tree.up')}\n </Button>\n <p className=\"text-text-dim\">{t('editor.binary')}</p>\n <p className=\"mt-2 text-xs text-text-muted tabular-nums\">\n {file.path} · {formatSize(file.size)}\n </p>\n </div>\n )\n }\n\n return (\n <div className=\"flex flex-col gap-2\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <Button variant=\"ghost\" size=\"sm\" onClick={onBack}>\n <ChevronUp className=\"h-4 w-4\" />\n {t('tree.up')}\n </Button>\n <code className=\"rounded bg-surface-2 px-1.5 py-0.5 text-xs font-mono\">{file.path}</code>\n {file.truncated && <Badge variant=\"warning\">truncated</Badge>}\n {dirty && <Badge variant=\"info\">{t('editor.dirtyHint')}</Badge>}\n <Button\n type=\"button\"\n size=\"sm\"\n className=\"ml-auto\"\n onClick={() => void onSave()}\n disabled={!dirty || write.isPending || file.truncated}\n >\n {write.isPending ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : null}\n {write.isPending ? t('editor.saving') : t('editor.save')}\n </Button>\n </div>\n {file.truncated && (\n <p className=\"text-xs text-warning\">{t('editor.truncated')}</p>\n )}\n <textarea\n value={draft}\n onChange={(e) => setDraft(e.target.value)}\n rows={24}\n spellCheck={false}\n className={cn(\n 'w-full resize-y rounded-md border bg-bg px-3 py-2',\n 'font-mono text-xs leading-5',\n 'focus:outline-none focus:ring-2 focus:ring-accent focus:ring-offset-1 focus:ring-offset-bg',\n dirty ? 'border-accent' : 'border-border',\n )}\n />\n {!dirty && (\n <p className=\"text-xs text-text-muted\">\n {t('editor.savedMeta', { size: file.size, at: formatTime(file.mtime) })}\n </p>\n )}\n </div>\n )\n}\n\nfunction formatTime(iso: string | 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\nfunction formatSize(n: number): string {\n if (n < 1024) return `${n} B`\n if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KiB`\n return `${(n / 1024 / 1024).toFixed(1)} MiB`\n}\n"],"names":["File","createLucideIcon","FolderTree","Folder","WorkspaceRoute","useTranslation","params","setParams","useSearchParams","agent","path","agents","useAgentsStatus","agentNames","useMemo","filesQuery","useWorkspaceFiles","data","isDir","isFile","patchParams","patch","next","k","v","setAgent","navigateTo","nextPath","navigateUp","idx","parent","jsxs","jsx","Topbar","Button","Loader2","RefreshCcw","Label","Select","SelectTrigger","SelectValue","SelectContent","SelectItem","name","cn","Fragment","Breadcrumb","DirListing","entry","FileEditor","EmptyState","onNavigate","onUp","t","parts","ChevronUp","part","upto","entries","onEnter","e","Badge","formatSize","formatTime","file","onBack","draft","setDraft","useState","useEffect","dirty","write","useWriteWorkspaceFile","onSave","toast","err","message","describeError","iso","d","n"],"mappings":"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","x_google_ignoreList":[0,1,2]}
1
+ {"version":3,"file":"workspace-BtGPH9Sx.js","sources":["../../node_modules/lucide-react/dist/esm/icons/file.js","../../node_modules/lucide-react/dist/esm/icons/folder-tree.js","../../node_modules/lucide-react/dist/esm/icons/folder.js","../../src/routes/workspace.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 File = createLucideIcon(\"File\", [\n [\"path\", { d: \"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z\", key: \"1rqfz7\" }],\n [\"path\", { d: \"M14 2v4a2 2 0 0 0 2 2h4\", key: \"tnqrlb\" }]\n]);\n\nexport { File as default };\n//# sourceMappingURL=file.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 FolderTree = createLucideIcon(\"FolderTree\", [\n [\n \"path\",\n {\n d: \"M20 10a1 1 0 0 0 1-1V6a1 1 0 0 0-1-1h-2.5a1 1 0 0 1-.8-.4l-.9-1.2A1 1 0 0 0 15 3h-2a1 1 0 0 0-1 1v5a1 1 0 0 0 1 1Z\",\n key: \"hod4my\"\n }\n ],\n [\n \"path\",\n {\n d: \"M20 21a1 1 0 0 0 1-1v-3a1 1 0 0 0-1-1h-2.9a1 1 0 0 1-.88-.55l-.42-.85a1 1 0 0 0-.92-.6H13a1 1 0 0 0-1 1v5a1 1 0 0 0 1 1Z\",\n key: \"w4yl2u\"\n }\n ],\n [\"path\", { d: \"M3 5a2 2 0 0 0 2 2h3\", key: \"f2jnh7\" }],\n [\"path\", { d: \"M3 3v13a2 2 0 0 0 2 2h3\", key: \"k8epm1\" }]\n]);\n\nexport { FolderTree as default };\n//# sourceMappingURL=folder-tree.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 Folder = createLucideIcon(\"Folder\", [\n [\n \"path\",\n {\n d: \"M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z\",\n key: \"1kt360\"\n }\n ]\n]);\n\nexport { Folder as default };\n//# sourceMappingURL=folder.js.map\n","/**\n * /workspace — file browser + editor for the per-agent CWD.\n *\n * Single-column finder UI: pick an agent → drill into the workspace\n * via ?path=, click a file → opens the editor pane below the\n * listing. No recursive tree because the listing handler is cheap\n * and the typical workspace has ≤ 30 entries per dir.\n *\n * Editor is a plain `<textarea>` — Monaco / CodeMirror would balloon\n * the bundle for what is realistically operator-edits to\n * CLAUDE.md / AGENTS.md / config snippets. Binary files render a\n * read-only placeholder. Truncated files (> 1 MiB) block save to\n * prevent destructive overwrites.\n */\n\nimport { useEffect, useMemo, useState } from 'react'\nimport { useSearchParams } from 'react-router-dom'\nimport { useTranslation } from 'react-i18next'\nimport { toast } from 'sonner'\nimport { ChevronUp, File, Folder, FolderTree, Loader2, RefreshCcw } from 'lucide-react'\n\nimport { Topbar } from '@/components/shell/topbar'\nimport { EmptyState } from '@/components/common/empty-state'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { Label } from '@/components/ui/label'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@/components/ui/select'\nimport {\n useAgentsStatus,\n useWorkspaceFiles,\n useWriteWorkspaceFile,\n} from '@/hooks/use-workspace'\nimport { describeError } from '@/lib/api/errors'\nimport type {\n WorkspaceFileContent,\n WorkspaceFileEntry,\n} from '@/types/api'\nimport { cn } from '@/lib/utils'\n\nexport default function WorkspaceRoute(): JSX.Element {\n const { t } = useTranslation(['workspace', 'common'])\n const [params, setParams] = useSearchParams()\n const agent = params.get('agent') ?? ''\n const path = params.get('path') ?? ''\n\n const agentsQuery = useAgentsStatus()\n const agents = agentsQuery.data ?? {}\n const agentNames = useMemo(() => Object.keys(agents).sort(), [agents])\n\n /** Query may resolve to either a dir listing or file content — we\n * branch on `data.type` in the render. */\n const filesQuery = useWorkspaceFiles(agent, path, { enabled: Boolean(agent) })\n const data = filesQuery.data\n const isDir = data?.type === 'dir'\n const isFile = data?.type === 'file'\n\n function patchParams(patch: Record<string, string | null>): void {\n const next = new URLSearchParams(params)\n for (const [k, v] of Object.entries(patch)) {\n if (v == null || v === '') next.delete(k)\n else next.set(k, v)\n }\n setParams(next, { replace: false })\n }\n\n function setAgent(next: string): void {\n // Switching agent always resets the path so we don't drag a\n // claude-specific path into codex's workspace.\n patchParams({ agent: next || null, path: null })\n }\n function navigateTo(nextPath: string): void {\n patchParams({ path: nextPath || null })\n }\n function navigateUp(): void {\n if (!path) return\n const idx = path.lastIndexOf('/')\n const parent = idx >= 0 ? path.slice(0, idx) : ''\n navigateTo(parent)\n }\n\n return (\n <div className=\"flex min-h-dvh flex-col bg-bg\">\n <Topbar />\n\n <main className=\"mx-auto flex w-full max-w-7xl flex-1 flex-col gap-4 px-3 py-4 sm:px-4 pb-safe\">\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('pageTitle')}</h1>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"ml-auto\"\n onClick={() => filesQuery.refetch()}\n disabled={!agent || filesQuery.isFetching}\n aria-label={t('actions.refresh', { ns: 'common' })}\n >\n {filesQuery.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('subtitle')}</p>\n </header>\n\n {/* Agent picker */}\n <div className=\"flex flex-wrap items-end gap-2\">\n <div className=\"flex flex-col gap-1\">\n <Label htmlFor=\"agent\" className=\"text-xs text-text-dim\">\n {t('agentPicker.label')}\n </Label>\n <Select value={agent || '__placeholder__'} onValueChange={(v) => setAgent(v === '__placeholder__' ? '' : v)}>\n <SelectTrigger id=\"agent\" className=\"w-64\">\n <SelectValue placeholder={t('agentPicker.placeholder')} />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"__placeholder__\" disabled>\n {t('agentPicker.placeholder')}\n </SelectItem>\n {agentNames.map((name) => (\n <SelectItem key={name} value={name}>\n <span className=\"flex items-center gap-2\">\n <span className=\"font-mono text-xs\">{name}</span>\n <span className={cn(\n 'inline-block h-1.5 w-1.5 rounded-full',\n agents[name] ? 'bg-success' : 'bg-text-muted',\n )} />\n <span className=\"text-text-dim text-xs\">\n {agents[name] ? t('agentPicker.online') : t('agentPicker.offline')}\n </span>\n </span>\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n\n {!agent ? (\n <EmptyState\n icon={<FolderTree />}\n title={t('noAgent.title')}\n description={t('noAgent.description')}\n />\n ) : (\n <>\n {/* Breadcrumb + Up */}\n <Breadcrumb\n path={path}\n onNavigate={navigateTo}\n onUp={navigateUp}\n />\n\n {filesQuery.isLoading ? (\n <div className=\"h-32 rounded-md bg-surface-2 animate-pulse\" />\n ) : isDir && data?.type === 'dir' ? (\n <DirListing\n entries={data.entries}\n onEnter={(entry) => navigateTo(\n path ? `${path}/${entry.name}` : entry.name,\n )}\n />\n ) : isFile && data?.type === 'file' ? (\n <FileEditor\n file={data}\n agent={agent}\n onBack={navigateUp}\n />\n ) : (\n <EmptyState\n icon={<FolderTree />}\n title={t('editor.loadFailed')}\n description={filesQuery.error?.message ?? ''}\n />\n )}\n </>\n )}\n </main>\n </div>\n )\n}\n\ninterface BreadcrumbProps {\n path: string\n onNavigate: (path: string) => void\n onUp: () => void\n}\n\nfunction Breadcrumb({ path, onNavigate, onUp }: BreadcrumbProps): JSX.Element {\n const { t } = useTranslation('workspace')\n const parts = path ? path.split('/') : []\n return (\n <div className=\"flex flex-wrap items-center gap-1 text-sm\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={onUp}\n disabled={!path}\n aria-label={t('tree.up')}\n >\n <ChevronUp className=\"h-4 w-4\" />\n </Button>\n <button\n type=\"button\"\n onClick={() => onNavigate('')}\n className=\"text-text-dim hover:text-text rounded px-1 py-0.5\"\n >\n {t('tree.rootLabel')}\n </button>\n {parts.map((part, i) => {\n const upto = parts.slice(0, i + 1).join('/')\n return (\n <span key={upto} className=\"flex items-center gap-1\">\n <span className=\"text-text-muted\">/</span>\n <button\n type=\"button\"\n onClick={() => onNavigate(upto)}\n className=\"text-text-dim hover:text-text rounded px-1 py-0.5\"\n >\n {part}\n </button>\n </span>\n )\n })}\n </div>\n )\n}\n\ninterface DirListingProps {\n entries: WorkspaceFileEntry[]\n onEnter: (entry: WorkspaceFileEntry) => void\n}\n\nfunction DirListing({ entries, onEnter }: DirListingProps): JSX.Element {\n const { t } = useTranslation('workspace')\n if (entries.length === 0) {\n return <div className=\"rounded-md border border-border bg-surface p-3 text-sm text-text-dim\">{t('tree.empty')}</div>\n }\n return (\n <ul className=\"divide-y divide-border rounded-md border border-border bg-surface\">\n {entries.map((e) => (\n <li key={e.name}>\n <button\n type=\"button\"\n onClick={() => onEnter(e)}\n disabled={e.broken || e.symlink_escape}\n className={cn(\n 'flex w-full items-center gap-2 px-3 py-2 text-sm text-left',\n 'transition-colors hover:bg-surface-hover disabled:opacity-50 disabled:cursor-not-allowed',\n )}\n >\n {e.isDir ? <Folder className=\"h-4 w-4 text-accent\" /> : <File className=\"h-4 w-4 text-text-dim\" />}\n <span className={e.isDir ? 'font-medium text-text' : 'text-text'}>{e.name}</span>\n {e.symlink_escape && (\n <Badge variant=\"warning\" className=\"ml-2\">{t('tree.symlinkEscape')}</Badge>\n )}\n {e.broken && !e.symlink_escape && (\n <Badge variant=\"danger\" className=\"ml-2\">{t('tree.broken')}</Badge>\n )}\n {!e.isDir && e.size != null && (\n <span className=\"ml-auto text-xs text-text-muted tabular-nums\">{formatSize(e.size)}</span>\n )}\n {e.mtime && (\n <span className={cn('text-xs text-text-muted tabular-nums', !e.isDir && e.size != null ? 'ml-3' : 'ml-auto')}>\n {formatTime(e.mtime)}\n </span>\n )}\n </button>\n </li>\n ))}\n </ul>\n )\n}\n\ninterface FileEditorProps {\n file: WorkspaceFileContent\n agent: string\n onBack: () => void\n}\n\nfunction FileEditor({ file, agent, onBack }: FileEditorProps): JSX.Element {\n const { t } = useTranslation(['workspace', 'common'])\n const [draft, setDraft] = useState(file.content)\n useEffect(() => { setDraft(file.content) }, [file.content])\n const dirty = draft !== file.content\n\n const write = useWriteWorkspaceFile()\n\n async function onSave(): Promise<void> {\n try {\n await write.mutateAsync({ agent, path: file.path, body: { content: draft } })\n toast.success(t('toast.saved', { path: file.path }))\n } catch (err) {\n const { message } = describeError(err, t)\n toast.error(message)\n }\n }\n\n if (file.encoding === 'base64') {\n return (\n <div className=\"rounded-md border border-border bg-surface p-4 text-sm\">\n <Button variant=\"ghost\" size=\"sm\" onClick={onBack} className=\"mb-3\">\n <ChevronUp className=\"h-4 w-4\" />\n {t('tree.up')}\n </Button>\n <p className=\"text-text-dim\">{t('editor.binary')}</p>\n <p className=\"mt-2 text-xs text-text-muted tabular-nums\">\n {file.path} · {formatSize(file.size)}\n </p>\n </div>\n )\n }\n\n return (\n <div className=\"flex flex-col gap-2\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <Button variant=\"ghost\" size=\"sm\" onClick={onBack}>\n <ChevronUp className=\"h-4 w-4\" />\n {t('tree.up')}\n </Button>\n <code className=\"rounded bg-surface-2 px-1.5 py-0.5 text-xs font-mono\">{file.path}</code>\n {file.truncated && <Badge variant=\"warning\">truncated</Badge>}\n {dirty && <Badge variant=\"info\">{t('editor.dirtyHint')}</Badge>}\n <Button\n type=\"button\"\n size=\"sm\"\n className=\"ml-auto\"\n onClick={() => void onSave()}\n disabled={!dirty || write.isPending || file.truncated}\n >\n {write.isPending ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : null}\n {write.isPending ? t('editor.saving') : t('editor.save')}\n </Button>\n </div>\n {file.truncated && (\n <p className=\"text-xs text-warning\">{t('editor.truncated')}</p>\n )}\n <textarea\n value={draft}\n onChange={(e) => setDraft(e.target.value)}\n rows={24}\n spellCheck={false}\n className={cn(\n 'w-full resize-y rounded-md border bg-bg px-3 py-2',\n 'font-mono text-xs leading-5',\n 'focus:outline-none focus:ring-2 focus:ring-accent focus:ring-offset-1 focus:ring-offset-bg',\n dirty ? 'border-accent' : 'border-border',\n )}\n />\n {!dirty && (\n <p className=\"text-xs text-text-muted\">\n {t('editor.savedMeta', { size: file.size, at: formatTime(file.mtime) })}\n </p>\n )}\n </div>\n )\n}\n\nfunction formatTime(iso: string | 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\nfunction formatSize(n: number): string {\n if (n < 1024) return `${n} B`\n if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KiB`\n return `${(n / 1024 / 1024).toFixed(1)} MiB`\n}\n"],"names":["File","createLucideIcon","FolderTree","Folder","WorkspaceRoute","useTranslation","params","setParams","useSearchParams","agent","path","agents","useAgentsStatus","agentNames","useMemo","filesQuery","useWorkspaceFiles","data","isDir","isFile","patchParams","patch","next","k","v","setAgent","navigateTo","nextPath","navigateUp","idx","parent","jsxs","jsx","Topbar","Button","Loader2","RefreshCcw","Label","Select","SelectTrigger","SelectValue","SelectContent","SelectItem","name","cn","Fragment","Breadcrumb","DirListing","entry","FileEditor","EmptyState","onNavigate","onUp","t","parts","ChevronUp","part","upto","entries","onEnter","e","Badge","formatSize","formatTime","file","onBack","draft","setDraft","useState","useEffect","dirty","write","useWriteWorkspaceFile","onSave","toast","err","message","describeError","iso","d","n"],"mappings":"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","x_google_ignoreList":[0,1,2]}
@@ -1,7 +1,7 @@
1
- import{z as S,ae as y,V as e,c as u,B as C,a4 as j,G as F,L as T,I as E}from"./index-7yc9y9We.js";import{e as f}from"./react-C9F3QeMB.js";import{E as z}from"./empty-state-C4sK7D2H.js";import{T as B,d as D,e as g,c as d,a as P,b as x}from"./table-BIc6RbM1.js";import{r as R,o as H}from"./use-settings-J28YEhhv.js";import{L as v}from"./loader-circle-CwM-6o9f.js";import{R as I}from"./refresh-ccw-l9Z6wukO.js";import{B as W}from"./briefcase-Cwk6Nkg7.js";import{S as A}from"./save-CllPj34m.js";import"./useQuery-dWspFqPU.js";/**
1
+ import{z as S,ae as y,V as e,c as u,B as C,a4 as j,G as F,L as T,I as E}from"./index-igqJknUt.js";import{e as f}from"./react-C9F3QeMB.js";import{E as z}from"./empty-state-CP9D3-8U.js";import{T as B,d as D,e as g,c as d,a as P,b as x}from"./table-D5hzEZ1F.js";import{t as R,q as H}from"./use-settings-D2TsP4vN.js";import{L as v}from"./loader-circle-BMJKoafx.js";import{R as I}from"./refresh-ccw-D6fZ_xYR.js";import{B as W}from"./briefcase-_b-XDwyU.js";import{S as A}from"./save-S79SG1Qr.js";import"./useQuery-B6dy499I.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 M=S("Pencil",[["path",{d:"M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z",key:"1a8usu"}],["path",{d:"m15 5 4 4",key:"1mk7zo"}]]),N={id:"",name:"",agents:"",members:"",rate:"",intervalSec:"",burst:""};function O(s){return{id:s.id,name:s.name,agents:s.agents.join(", "),members:(s.members??[]).join(", "),rate:s.rateLimit?String(s.rateLimit.rate):"",intervalSec:s.rateLimit?String(s.rateLimit.intervalSec):"",burst:s.rateLimit?String(s.rateLimit.burst):""}}function _(s){const n=o=>o.split(",").map(p=>p.trim()).filter(Boolean),m={id:s.id.trim(),name:s.name.trim()||s.id.trim(),agents:n(s.agents),members:n(s.members)},t=Number(s.rate),i=Number(s.intervalSec),c=Number(s.burst);return Number.isFinite(t)&&t>0&&Number.isFinite(i)&&i>0&&Number.isFinite(c)&&c>0&&(m.rateLimit={rate:t,intervalSec:i,burst:c}),m}function $(){const{t:s}=y(["settings","common"]),n=R(),m=H(),[t,i]=f.useState(N),[c,o]=f.useState(null);f.useEffect(()=>{t.id||t.name},[t.id,t.name]);function p(a){i(O(a)),o(a.id)}function b(){i(N),o(null)}async function k(a){a.preventDefault();const r=_(t);if(r.id)try{await m.mutateAsync(r),j.success(s("workspaces.toast.saved")),b()}catch(L){j.error(F(L,s).message)}}const h=n.data?.workspaces??[],w=h.length<=1&&h.every(a=>a.id==="default");return e.jsxs("div",{className:"mx-auto flex max-w-5xl 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("workspaces.title")}),e.jsxs(u,{variant:"ghost",size:"sm",className:"ml-auto",onClick:()=>n.refetch(),disabled:n.isFetching,"aria-label":s("actions.refresh",{ns:"common"}),children:[n.isFetching?e.jsx(v,{className:"h-4 w-4 animate-spin"}):e.jsx(I,{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("workspaces.subtitle")})]}),n.isLoading?e.jsx("div",{className:"h-32 rounded-md bg-surface-2 animate-pulse"}):w?e.jsx(z,{icon:e.jsx(W,{}),title:s("workspaces.empty.title"),description:s("workspaces.empty.description")}):e.jsxs(B,{children:[e.jsx(D,{children:e.jsxs(g,{children:[e.jsx(d,{className:"w-32",children:s("workspaces.col.id")}),e.jsx(d,{children:s("workspaces.col.name")}),e.jsx(d,{children:s("workspaces.col.agents")}),e.jsx(d,{children:s("workspaces.col.members")}),e.jsx(d,{children:s("workspaces.col.rateLimit")}),e.jsx(d,{className:"w-20"})]})}),e.jsx(P,{children:h.map(a=>e.jsxs(g,{children:[e.jsx(x,{className:"font-mono text-xs",children:a.id}),e.jsx(x,{className:"font-medium",children:a.name}),e.jsx(x,{children:a.agents.length===0?e.jsx("span",{className:"text-text-muted text-xs",children:"—"}):e.jsx("div",{className:"flex flex-wrap gap-1",children:a.agents.map(r=>e.jsx(C,{variant:"secondary",children:r},r))})}),e.jsx(x,{children:(a.members??[]).length===0?e.jsx("span",{className:"text-text-muted text-xs",children:s("workspaces.membersOpen")}):e.jsx("span",{className:"text-text-dim text-xs",children:s("workspaces.membersCount",{count:a.members.length})})}),e.jsx(x,{className:"text-xs text-text-dim",children:a.rateLimit?s("workspaces.rateLimitFormat",{rate:a.rateLimit.rate,intervalSec:a.rateLimit.intervalSec,burst:a.rateLimit.burst}):"—"}),e.jsx(x,{children:e.jsxs(u,{type:"button",variant:"ghost",size:"sm",onClick:()=>p(a),children:[e.jsx(M,{className:"h-3 w-3"}),s("workspaces.edit")]})})]},a.id))})]}),e.jsxs("form",{onSubmit:a=>void k(a),className:"rounded-md border border-border bg-surface p-4",children:[e.jsx("div",{className:"mb-1 text-sm font-medium",children:s("workspaces.form.title")}),c&&e.jsx("p",{className:"mb-3 text-xs text-text-dim",children:s("workspaces.form.subtitleEdit")}),e.jsxs("div",{className:"grid grid-cols-1 gap-3 sm:grid-cols-2",children:[e.jsx(l,{id:"ws-id",label:s("workspaces.form.id"),hint:s("workspaces.form.idHint"),value:t.id,onChange:a=>i(r=>({...r,id:a})),placeholder:"team-foo",mono:!0}),e.jsx(l,{id:"ws-name",label:s("workspaces.form.name"),value:t.name,onChange:a=>i(r=>({...r,name:a})),placeholder:"Team Foo"}),e.jsx(l,{id:"ws-agents",label:s("workspaces.form.agents"),hint:s("workspaces.form.agentsHint"),value:t.agents,onChange:a=>i(r=>({...r,agents:a})),placeholder:"claude-code, codex",mono:!0}),e.jsx(l,{id:"ws-members",label:s("workspaces.form.members"),hint:s("workspaces.form.membersHint"),value:t.members,onChange:a=>i(r=>({...r,members:a})),placeholder:"wechat:wxid_abc, telegram:12345",mono:!0})]}),e.jsxs("div",{className:"mt-3",children:[e.jsx("div",{className:"text-xs font-medium text-text-dim",children:s("workspaces.form.rateLimit")}),e.jsxs("div",{className:"mt-1 grid grid-cols-3 gap-2",children:[e.jsx(l,{id:"ws-rate",label:s("workspaces.form.rate"),value:t.rate,onChange:a=>i(r=>({...r,rate:a})),placeholder:"10",mono:!0}),e.jsx(l,{id:"ws-ivl",label:s("workspaces.form.intervalSec"),value:t.intervalSec,onChange:a=>i(r=>({...r,intervalSec:a})),placeholder:"60",mono:!0}),e.jsx(l,{id:"ws-burst",label:s("workspaces.form.burst"),value:t.burst,onChange:a=>i(r=>({...r,burst:a})),placeholder:"15",mono:!0})]})]}),e.jsxs("div",{className:"mt-4 flex flex-wrap gap-2",children:[e.jsx(u,{type:"button",variant:"secondary",size:"sm",onClick:b,disabled:m.isPending,children:s("workspaces.form.reset")}),e.jsxs(u,{type:"submit",size:"sm",className:"ml-auto",disabled:m.isPending||!t.id.trim(),children:[m.isPending?e.jsx(v,{className:"h-4 w-4 animate-spin"}):e.jsx(A,{className:"h-4 w-4"}),s("workspaces.form.submit")]})]})]})]})}function l({id:s,label:n,hint:m,value:t,onChange:i,placeholder:c,mono:o}){return e.jsxs("div",{className:"flex flex-col gap-1",children:[e.jsx(T,{htmlFor:s,className:"text-xs text-text-dim",children:n}),e.jsx(E,{id:s,value:t,onChange:p=>i(p.target.value),placeholder:c,className:o?"font-mono text-xs":""}),m&&e.jsx("span",{className:"text-[10px] text-text-muted",children:m})]})}export{$ as default};
7
- //# sourceMappingURL=workspaces-DCEYab_X.js.map
7
+ //# sourceMappingURL=workspaces-_PTrUhIq.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"workspaces-DCEYab_X.js","sources":["../../node_modules/lucide-react/dist/esm/icons/pencil.js","../../src/routes/settings/workspaces.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 Pencil = createLucideIcon(\"Pencil\", [\n [\n \"path\",\n {\n d: \"M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z\",\n key: \"1a8usu\"\n }\n ],\n [\"path\", { d: \"m15 5 4 4\", key: \"1mk7zo\" }]\n]);\n\nexport { Pencil as default };\n//# sourceMappingURL=pencil.js.map\n","/**\n * /settings/workspaces — multi-tenant workspace registry editor.\n *\n * Lists every workspace (id / name / agent whitelist count / member\n * count / rate-limit) and provides an inline form to add new ones\n * or upsert existing. The backend's POST handler is upsert-by-id, so\n * editing means \"click Edit on a row → form pre-fills → tweak → Save\"\n * — same endpoint, same semantics as Add.\n *\n * Open vs gated workspaces:\n * * `members: []` (empty array) means the workspace is open to any\n * user. The backend treats `undefined` and `[]` identically; we\n * normalise to `[]` for the round-trip.\n * * `agents: []` means \"allow every registered agent\".\n *\n * Rate-limit fields are optional triple (rate / intervalSec / burst).\n * Leaving them blank submits `rateLimit: undefined` and the backend\n * falls back to defaults.\n */\n\nimport { useEffect, useState } from 'react'\nimport { useTranslation } from 'react-i18next'\nimport { toast } from 'sonner'\nimport { Briefcase, Loader2, Pencil, RefreshCcw, Save } from 'lucide-react'\n\nimport { EmptyState } from '@/components/common/empty-state'\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '@/components/ui/table'\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 { useUpsertWorkspace, useWorkspaces } from '@/hooks/use-settings'\nimport { describeError } from '@/lib/api/errors'\nimport type { WorkspaceConfig } from '@/types/api'\n\ninterface DraftFields {\n id: string\n name: string\n agents: string // comma-separated\n members: string // comma-separated\n rate: string\n intervalSec: string\n burst: string\n}\n\nconst EMPTY_DRAFT: DraftFields = {\n id: '', name: '',\n agents: '', members: '',\n rate: '', intervalSec: '', burst: '',\n}\n\nfunction toDraft(ws: WorkspaceConfig): DraftFields {\n return {\n id: ws.id,\n name: ws.name,\n agents: ws.agents.join(', '),\n members: (ws.members ?? []).join(', '),\n rate: ws.rateLimit ? String(ws.rateLimit.rate) : '',\n intervalSec: ws.rateLimit ? String(ws.rateLimit.intervalSec) : '',\n burst: ws.rateLimit ? String(ws.rateLimit.burst) : '',\n }\n}\n\nfunction fromDraft(d: DraftFields): WorkspaceConfig {\n const splitCsv = (s: string): string[] =>\n s.split(',').map((x) => x.trim()).filter(Boolean)\n const cfg: WorkspaceConfig = {\n id: d.id.trim(),\n name: d.name.trim() || d.id.trim(),\n agents: splitCsv(d.agents),\n members: splitCsv(d.members),\n }\n // Rate limit only when all three fields parse as positive numbers.\n const rate = Number(d.rate)\n const ivl = Number(d.intervalSec)\n const burst = Number(d.burst)\n if (Number.isFinite(rate) && rate > 0\n && Number.isFinite(ivl) && ivl > 0\n && Number.isFinite(burst) && burst > 0) {\n cfg.rateLimit = { rate, intervalSec: ivl, burst }\n }\n return cfg\n}\n\nexport default function SettingsWorkspacesRoute(): JSX.Element {\n const { t } = useTranslation(['settings', 'common'])\n const list = useWorkspaces()\n const upsert = useUpsertWorkspace()\n\n const [draft, setDraft] = useState<DraftFields>(EMPTY_DRAFT)\n const [editing, setEditing] = useState<string | null>(null)\n\n useEffect(() => {\n if (draft.id || draft.name) return\n // Keep the form blank on first paint; pre-fill happens via Edit.\n }, [draft.id, draft.name])\n\n function startEdit(ws: WorkspaceConfig): void {\n setDraft(toDraft(ws))\n setEditing(ws.id)\n }\n\n function resetForm(): void {\n setDraft(EMPTY_DRAFT)\n setEditing(null)\n }\n\n async function onSubmit(e: React.FormEvent): Promise<void> {\n e.preventDefault()\n const cfg = fromDraft(draft)\n if (!cfg.id) return\n try {\n await upsert.mutateAsync(cfg)\n toast.success(t('workspaces.toast.saved'))\n resetForm()\n } catch (err) {\n toast.error(describeError(err, t).message)\n }\n }\n\n const workspaces = list.data?.workspaces ?? []\n const hasOnlyDefault = workspaces.length <= 1\n && workspaces.every((w) => w.id === 'default')\n\n return (\n <div className=\"mx-auto flex max-w-5xl 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('workspaces.title')}</h1>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"ml-auto\"\n onClick={() => list.refetch()}\n disabled={list.isFetching}\n aria-label={t('actions.refresh', { ns: 'common' })}\n >\n {list.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('workspaces.subtitle')}</p>\n </header>\n\n {list.isLoading ? (\n <div className=\"h-32 rounded-md bg-surface-2 animate-pulse\" />\n ) : hasOnlyDefault ? (\n <EmptyState\n icon={<Briefcase />}\n title={t('workspaces.empty.title')}\n description={t('workspaces.empty.description')}\n />\n ) : (\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead className=\"w-32\">{t('workspaces.col.id')}</TableHead>\n <TableHead>{t('workspaces.col.name')}</TableHead>\n <TableHead>{t('workspaces.col.agents')}</TableHead>\n <TableHead>{t('workspaces.col.members')}</TableHead>\n <TableHead>{t('workspaces.col.rateLimit')}</TableHead>\n <TableHead className=\"w-20\" />\n </TableRow>\n </TableHeader>\n <TableBody>\n {workspaces.map((ws) => (\n <TableRow key={ws.id}>\n <TableCell className=\"font-mono text-xs\">{ws.id}</TableCell>\n <TableCell className=\"font-medium\">{ws.name}</TableCell>\n <TableCell>\n {ws.agents.length === 0 ? (\n <span className=\"text-text-muted text-xs\">—</span>\n ) : (\n <div className=\"flex flex-wrap gap-1\">\n {ws.agents.map((a) => (\n <Badge key={a} variant=\"secondary\">{a}</Badge>\n ))}\n </div>\n )}\n </TableCell>\n <TableCell>\n {(ws.members ?? []).length === 0 ? (\n <span className=\"text-text-muted text-xs\">{t('workspaces.membersOpen')}</span>\n ) : (\n <span className=\"text-text-dim text-xs\">\n {t('workspaces.membersCount', { count: ws.members!.length })}\n </span>\n )}\n </TableCell>\n <TableCell className=\"text-xs text-text-dim\">\n {ws.rateLimit\n ? t('workspaces.rateLimitFormat', {\n rate: ws.rateLimit.rate,\n intervalSec: ws.rateLimit.intervalSec,\n burst: ws.rateLimit.burst,\n })\n : '—'}\n </TableCell>\n <TableCell>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => startEdit(ws)}\n >\n <Pencil className=\"h-3 w-3\" />\n {t('workspaces.edit')}\n </Button>\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n )}\n\n {/* Form */}\n <form\n onSubmit={(e) => void onSubmit(e)}\n className=\"rounded-md border border-border bg-surface p-4\"\n >\n <div className=\"mb-1 text-sm font-medium\">{t('workspaces.form.title')}</div>\n {editing && (\n <p className=\"mb-3 text-xs text-text-dim\">{t('workspaces.form.subtitleEdit')}</p>\n )}\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n <FormField\n id=\"ws-id\"\n label={t('workspaces.form.id')}\n hint={t('workspaces.form.idHint')}\n value={draft.id}\n onChange={(v) => setDraft((d) => ({ ...d, id: v }))}\n placeholder=\"team-foo\"\n mono\n />\n <FormField\n id=\"ws-name\"\n label={t('workspaces.form.name')}\n value={draft.name}\n onChange={(v) => setDraft((d) => ({ ...d, name: v }))}\n placeholder=\"Team Foo\"\n />\n <FormField\n id=\"ws-agents\"\n label={t('workspaces.form.agents')}\n hint={t('workspaces.form.agentsHint')}\n value={draft.agents}\n onChange={(v) => setDraft((d) => ({ ...d, agents: v }))}\n placeholder=\"claude-code, codex\"\n mono\n />\n <FormField\n id=\"ws-members\"\n label={t('workspaces.form.members')}\n hint={t('workspaces.form.membersHint')}\n value={draft.members}\n onChange={(v) => setDraft((d) => ({ ...d, members: v }))}\n placeholder=\"wechat:wxid_abc, telegram:12345\"\n mono\n />\n </div>\n <div className=\"mt-3\">\n <div className=\"text-xs font-medium text-text-dim\">{t('workspaces.form.rateLimit')}</div>\n <div className=\"mt-1 grid grid-cols-3 gap-2\">\n <FormField\n id=\"ws-rate\"\n label={t('workspaces.form.rate')}\n value={draft.rate}\n onChange={(v) => setDraft((d) => ({ ...d, rate: v }))}\n placeholder=\"10\"\n mono\n />\n <FormField\n id=\"ws-ivl\"\n label={t('workspaces.form.intervalSec')}\n value={draft.intervalSec}\n onChange={(v) => setDraft((d) => ({ ...d, intervalSec: v }))}\n placeholder=\"60\"\n mono\n />\n <FormField\n id=\"ws-burst\"\n label={t('workspaces.form.burst')}\n value={draft.burst}\n onChange={(v) => setDraft((d) => ({ ...d, burst: v }))}\n placeholder=\"15\"\n mono\n />\n </div>\n </div>\n <div className=\"mt-4 flex flex-wrap gap-2\">\n <Button\n type=\"button\"\n variant=\"secondary\"\n size=\"sm\"\n onClick={resetForm}\n disabled={upsert.isPending}\n >\n {t('workspaces.form.reset')}\n </Button>\n <Button\n type=\"submit\"\n size=\"sm\"\n className=\"ml-auto\"\n disabled={upsert.isPending || !draft.id.trim()}\n >\n {upsert.isPending ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <Save className=\"h-4 w-4\" />}\n {t('workspaces.form.submit')}\n </Button>\n </div>\n </form>\n </div>\n )\n}\n\ninterface FormFieldProps {\n id: string\n label: string\n hint?: string\n value: string\n onChange: (v: string) => void\n placeholder?: string\n mono?: boolean\n}\n\nfunction FormField({ id, label, hint, value, onChange, placeholder, mono }: FormFieldProps): JSX.Element {\n return (\n <div className=\"flex flex-col gap-1\">\n <Label htmlFor={id} className=\"text-xs text-text-dim\">{label}</Label>\n <Input\n id={id}\n value={value}\n onChange={(e) => onChange(e.target.value)}\n placeholder={placeholder}\n className={mono ? 'font-mono text-xs' : ''}\n />\n {hint && <span className=\"text-[10px] text-text-muted\">{hint}</span>}\n </div>\n )\n}\n"],"names":["Pencil","createLucideIcon","EMPTY_DRAFT","toDraft","ws","fromDraft","d","splitCsv","s","x","cfg","rate","ivl","burst","SettingsWorkspacesRoute","t","useTranslation","list","useWorkspaces","upsert","useUpsertWorkspace","draft","setDraft","useState","editing","setEditing","useEffect","startEdit","resetForm","onSubmit","e","toast","err","describeError","workspaces","hasOnlyDefault","w","jsxs","jsx","Button","Loader2","RefreshCcw","EmptyState","Briefcase","Table","TableHeader","TableRow","TableHead","TableBody","TableCell","a","Badge","FormField","v","Save","id","label","hint","value","onChange","placeholder","mono","Label","Input"],"mappings":"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","x_google_ignoreList":[0]}
1
+ {"version":3,"file":"workspaces-_PTrUhIq.js","sources":["../../node_modules/lucide-react/dist/esm/icons/pencil.js","../../src/routes/settings/workspaces.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 Pencil = createLucideIcon(\"Pencil\", [\n [\n \"path\",\n {\n d: \"M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z\",\n key: \"1a8usu\"\n }\n ],\n [\"path\", { d: \"m15 5 4 4\", key: \"1mk7zo\" }]\n]);\n\nexport { Pencil as default };\n//# sourceMappingURL=pencil.js.map\n","/**\n * /settings/workspaces — multi-tenant workspace registry editor.\n *\n * Lists every workspace (id / name / agent whitelist count / member\n * count / rate-limit) and provides an inline form to add new ones\n * or upsert existing. The backend's POST handler is upsert-by-id, so\n * editing means \"click Edit on a row → form pre-fills → tweak → Save\"\n * — same endpoint, same semantics as Add.\n *\n * Open vs gated workspaces:\n * * `members: []` (empty array) means the workspace is open to any\n * user. The backend treats `undefined` and `[]` identically; we\n * normalise to `[]` for the round-trip.\n * * `agents: []` means \"allow every registered agent\".\n *\n * Rate-limit fields are optional triple (rate / intervalSec / burst).\n * Leaving them blank submits `rateLimit: undefined` and the backend\n * falls back to defaults.\n */\n\nimport { useEffect, useState } from 'react'\nimport { useTranslation } from 'react-i18next'\nimport { toast } from 'sonner'\nimport { Briefcase, Loader2, Pencil, RefreshCcw, Save } from 'lucide-react'\n\nimport { EmptyState } from '@/components/common/empty-state'\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '@/components/ui/table'\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 { useUpsertWorkspace, useWorkspaces } from '@/hooks/use-settings'\nimport { describeError } from '@/lib/api/errors'\nimport type { WorkspaceConfig } from '@/types/api'\n\ninterface DraftFields {\n id: string\n name: string\n agents: string // comma-separated\n members: string // comma-separated\n rate: string\n intervalSec: string\n burst: string\n}\n\nconst EMPTY_DRAFT: DraftFields = {\n id: '', name: '',\n agents: '', members: '',\n rate: '', intervalSec: '', burst: '',\n}\n\nfunction toDraft(ws: WorkspaceConfig): DraftFields {\n return {\n id: ws.id,\n name: ws.name,\n agents: ws.agents.join(', '),\n members: (ws.members ?? []).join(', '),\n rate: ws.rateLimit ? String(ws.rateLimit.rate) : '',\n intervalSec: ws.rateLimit ? String(ws.rateLimit.intervalSec) : '',\n burst: ws.rateLimit ? String(ws.rateLimit.burst) : '',\n }\n}\n\nfunction fromDraft(d: DraftFields): WorkspaceConfig {\n const splitCsv = (s: string): string[] =>\n s.split(',').map((x) => x.trim()).filter(Boolean)\n const cfg: WorkspaceConfig = {\n id: d.id.trim(),\n name: d.name.trim() || d.id.trim(),\n agents: splitCsv(d.agents),\n members: splitCsv(d.members),\n }\n // Rate limit only when all three fields parse as positive numbers.\n const rate = Number(d.rate)\n const ivl = Number(d.intervalSec)\n const burst = Number(d.burst)\n if (Number.isFinite(rate) && rate > 0\n && Number.isFinite(ivl) && ivl > 0\n && Number.isFinite(burst) && burst > 0) {\n cfg.rateLimit = { rate, intervalSec: ivl, burst }\n }\n return cfg\n}\n\nexport default function SettingsWorkspacesRoute(): JSX.Element {\n const { t } = useTranslation(['settings', 'common'])\n const list = useWorkspaces()\n const upsert = useUpsertWorkspace()\n\n const [draft, setDraft] = useState<DraftFields>(EMPTY_DRAFT)\n const [editing, setEditing] = useState<string | null>(null)\n\n useEffect(() => {\n if (draft.id || draft.name) return\n // Keep the form blank on first paint; pre-fill happens via Edit.\n }, [draft.id, draft.name])\n\n function startEdit(ws: WorkspaceConfig): void {\n setDraft(toDraft(ws))\n setEditing(ws.id)\n }\n\n function resetForm(): void {\n setDraft(EMPTY_DRAFT)\n setEditing(null)\n }\n\n async function onSubmit(e: React.FormEvent): Promise<void> {\n e.preventDefault()\n const cfg = fromDraft(draft)\n if (!cfg.id) return\n try {\n await upsert.mutateAsync(cfg)\n toast.success(t('workspaces.toast.saved'))\n resetForm()\n } catch (err) {\n toast.error(describeError(err, t).message)\n }\n }\n\n const workspaces = list.data?.workspaces ?? []\n const hasOnlyDefault = workspaces.length <= 1\n && workspaces.every((w) => w.id === 'default')\n\n return (\n <div className=\"mx-auto flex max-w-5xl 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('workspaces.title')}</h1>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"ml-auto\"\n onClick={() => list.refetch()}\n disabled={list.isFetching}\n aria-label={t('actions.refresh', { ns: 'common' })}\n >\n {list.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('workspaces.subtitle')}</p>\n </header>\n\n {list.isLoading ? (\n <div className=\"h-32 rounded-md bg-surface-2 animate-pulse\" />\n ) : hasOnlyDefault ? (\n <EmptyState\n icon={<Briefcase />}\n title={t('workspaces.empty.title')}\n description={t('workspaces.empty.description')}\n />\n ) : (\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead className=\"w-32\">{t('workspaces.col.id')}</TableHead>\n <TableHead>{t('workspaces.col.name')}</TableHead>\n <TableHead>{t('workspaces.col.agents')}</TableHead>\n <TableHead>{t('workspaces.col.members')}</TableHead>\n <TableHead>{t('workspaces.col.rateLimit')}</TableHead>\n <TableHead className=\"w-20\" />\n </TableRow>\n </TableHeader>\n <TableBody>\n {workspaces.map((ws) => (\n <TableRow key={ws.id}>\n <TableCell className=\"font-mono text-xs\">{ws.id}</TableCell>\n <TableCell className=\"font-medium\">{ws.name}</TableCell>\n <TableCell>\n {ws.agents.length === 0 ? (\n <span className=\"text-text-muted text-xs\">—</span>\n ) : (\n <div className=\"flex flex-wrap gap-1\">\n {ws.agents.map((a) => (\n <Badge key={a} variant=\"secondary\">{a}</Badge>\n ))}\n </div>\n )}\n </TableCell>\n <TableCell>\n {(ws.members ?? []).length === 0 ? (\n <span className=\"text-text-muted text-xs\">{t('workspaces.membersOpen')}</span>\n ) : (\n <span className=\"text-text-dim text-xs\">\n {t('workspaces.membersCount', { count: ws.members!.length })}\n </span>\n )}\n </TableCell>\n <TableCell className=\"text-xs text-text-dim\">\n {ws.rateLimit\n ? t('workspaces.rateLimitFormat', {\n rate: ws.rateLimit.rate,\n intervalSec: ws.rateLimit.intervalSec,\n burst: ws.rateLimit.burst,\n })\n : '—'}\n </TableCell>\n <TableCell>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => startEdit(ws)}\n >\n <Pencil className=\"h-3 w-3\" />\n {t('workspaces.edit')}\n </Button>\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n )}\n\n {/* Form */}\n <form\n onSubmit={(e) => void onSubmit(e)}\n className=\"rounded-md border border-border bg-surface p-4\"\n >\n <div className=\"mb-1 text-sm font-medium\">{t('workspaces.form.title')}</div>\n {editing && (\n <p className=\"mb-3 text-xs text-text-dim\">{t('workspaces.form.subtitleEdit')}</p>\n )}\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n <FormField\n id=\"ws-id\"\n label={t('workspaces.form.id')}\n hint={t('workspaces.form.idHint')}\n value={draft.id}\n onChange={(v) => setDraft((d) => ({ ...d, id: v }))}\n placeholder=\"team-foo\"\n mono\n />\n <FormField\n id=\"ws-name\"\n label={t('workspaces.form.name')}\n value={draft.name}\n onChange={(v) => setDraft((d) => ({ ...d, name: v }))}\n placeholder=\"Team Foo\"\n />\n <FormField\n id=\"ws-agents\"\n label={t('workspaces.form.agents')}\n hint={t('workspaces.form.agentsHint')}\n value={draft.agents}\n onChange={(v) => setDraft((d) => ({ ...d, agents: v }))}\n placeholder=\"claude-code, codex\"\n mono\n />\n <FormField\n id=\"ws-members\"\n label={t('workspaces.form.members')}\n hint={t('workspaces.form.membersHint')}\n value={draft.members}\n onChange={(v) => setDraft((d) => ({ ...d, members: v }))}\n placeholder=\"wechat:wxid_abc, telegram:12345\"\n mono\n />\n </div>\n <div className=\"mt-3\">\n <div className=\"text-xs font-medium text-text-dim\">{t('workspaces.form.rateLimit')}</div>\n <div className=\"mt-1 grid grid-cols-3 gap-2\">\n <FormField\n id=\"ws-rate\"\n label={t('workspaces.form.rate')}\n value={draft.rate}\n onChange={(v) => setDraft((d) => ({ ...d, rate: v }))}\n placeholder=\"10\"\n mono\n />\n <FormField\n id=\"ws-ivl\"\n label={t('workspaces.form.intervalSec')}\n value={draft.intervalSec}\n onChange={(v) => setDraft((d) => ({ ...d, intervalSec: v }))}\n placeholder=\"60\"\n mono\n />\n <FormField\n id=\"ws-burst\"\n label={t('workspaces.form.burst')}\n value={draft.burst}\n onChange={(v) => setDraft((d) => ({ ...d, burst: v }))}\n placeholder=\"15\"\n mono\n />\n </div>\n </div>\n <div className=\"mt-4 flex flex-wrap gap-2\">\n <Button\n type=\"button\"\n variant=\"secondary\"\n size=\"sm\"\n onClick={resetForm}\n disabled={upsert.isPending}\n >\n {t('workspaces.form.reset')}\n </Button>\n <Button\n type=\"submit\"\n size=\"sm\"\n className=\"ml-auto\"\n disabled={upsert.isPending || !draft.id.trim()}\n >\n {upsert.isPending ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <Save className=\"h-4 w-4\" />}\n {t('workspaces.form.submit')}\n </Button>\n </div>\n </form>\n </div>\n )\n}\n\ninterface FormFieldProps {\n id: string\n label: string\n hint?: string\n value: string\n onChange: (v: string) => void\n placeholder?: string\n mono?: boolean\n}\n\nfunction FormField({ id, label, hint, value, onChange, placeholder, mono }: FormFieldProps): JSX.Element {\n return (\n <div className=\"flex flex-col gap-1\">\n <Label htmlFor={id} className=\"text-xs text-text-dim\">{label}</Label>\n <Input\n id={id}\n value={value}\n onChange={(e) => onChange(e.target.value)}\n placeholder={placeholder}\n className={mono ? 'font-mono text-xs' : ''}\n />\n {hint && <span className=\"text-[10px] text-text-muted\">{hint}</span>}\n </div>\n )\n}\n"],"names":["Pencil","createLucideIcon","EMPTY_DRAFT","toDraft","ws","fromDraft","d","splitCsv","s","x","cfg","rate","ivl","burst","SettingsWorkspacesRoute","t","useTranslation","list","useWorkspaces","upsert","useUpsertWorkspace","draft","setDraft","useState","editing","setEditing","useEffect","startEdit","resetForm","onSubmit","e","toast","err","describeError","workspaces","hasOnlyDefault","w","jsxs","jsx","Button","Loader2","RefreshCcw","EmptyState","Briefcase","Table","TableHeader","TableRow","TableHead","TableBody","TableCell","a","Badge","FormField","v","Save","id","label","hint","value","onChange","placeholder","mono","Label","Input"],"mappings":"ygBAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GASA,MAAMA,EAASC,EAAiB,SAAU,CACxC,CACE,OACA,CACE,EAAG,mIACH,IAAK,QACX,CACA,EACE,CAAC,OAAQ,CAAE,EAAG,YAAa,IAAK,QAAQ,CAAE,CAC5C,CAAC,ECkCKC,EAA2B,CAC/B,GAAI,GAAI,KAAM,GACd,OAAQ,GAAI,QAAS,GACrB,KAAM,GAAI,YAAa,GAAI,MAAO,EACpC,EAEA,SAASC,EAAQC,EAAkC,CACjD,MAAO,CACL,GAAIA,EAAG,GACP,KAAMA,EAAG,KACT,OAAQA,EAAG,OAAO,KAAK,IAAI,EAC3B,SAAUA,EAAG,SAAW,CAAA,GAAI,KAAK,IAAI,EACrC,KAAaA,EAAG,UAAY,OAAOA,EAAG,UAAU,IAAI,EAAW,GAC/D,YAAaA,EAAG,UAAY,OAAOA,EAAG,UAAU,WAAW,EAAI,GAC/D,MAAaA,EAAG,UAAY,OAAOA,EAAG,UAAU,KAAK,EAAU,EAAA,CAEnE,CAEA,SAASC,EAAUC,EAAiC,CAClD,MAAMC,EAAYC,GAChBA,EAAE,MAAM,GAAG,EAAE,IAAKC,GAAMA,EAAE,KAAA,CAAM,EAAE,OAAO,OAAO,EAC5CC,EAAuB,CAC3B,GAASJ,EAAE,GAAG,KAAA,EACd,KAASA,EAAE,KAAK,QAAUA,EAAE,GAAG,KAAA,EAC/B,OAASC,EAASD,EAAE,MAAM,EAC1B,QAASC,EAASD,EAAE,OAAO,CAAA,EAGvBK,EAAU,OAAOL,EAAE,IAAI,EACvBM,EAAU,OAAON,EAAE,WAAW,EAC9BO,EAAU,OAAOP,EAAE,KAAK,EAC9B,OAAI,OAAO,SAASK,CAAI,GAAKA,EAAO,GAChC,OAAO,SAASC,CAAG,GAAMA,EAAM,GAC/B,OAAO,SAASC,CAAK,GAAKA,EAAQ,IACpCH,EAAI,UAAY,CAAE,KAAAC,EAAM,YAAaC,EAAK,MAAAC,CAAA,GAErCH,CACT,CAEA,SAAwBI,GAAuC,CAC7D,KAAM,CAAE,EAAAC,CAAA,EAAMC,EAAe,CAAC,WAAY,QAAQ,CAAC,EAC7CC,EAAOC,EAAA,EACPC,EAASC,EAAA,EAET,CAACC,EAAOC,CAAQ,EAAIC,EAAAA,SAAsBrB,CAAW,EACrD,CAACsB,EAASC,CAAU,EAAIF,EAAAA,SAAwB,IAAI,EAE1DG,EAAAA,UAAU,IAAM,CACVL,EAAM,IAAMA,EAAM,IAExB,EAAG,CAACA,EAAM,GAAIA,EAAM,IAAI,CAAC,EAEzB,SAASM,EAAUvB,EAA2B,CAC5CkB,EAASnB,EAAQC,CAAE,CAAC,EACpBqB,EAAWrB,EAAG,EAAE,CAClB,CAEA,SAASwB,GAAkB,CACzBN,EAASpB,CAAW,EACpBuB,EAAW,IAAI,CACjB,CAEA,eAAeI,EAASC,EAAmC,CACzDA,EAAE,eAAA,EACF,MAAMpB,EAAML,EAAUgB,CAAK,EAC3B,GAAKX,EAAI,GACT,GAAI,CACF,MAAMS,EAAO,YAAYT,CAAG,EAC5BqB,EAAM,QAAQhB,EAAE,wBAAwB,CAAC,EACzCa,EAAA,CACF,OAASI,EAAK,CACZD,EAAM,MAAME,EAAcD,EAAKjB,CAAC,EAAE,OAAO,CAC3C,CACF,CAEA,MAAMmB,EAAajB,EAAK,MAAM,YAAc,CAAA,EACtCkB,EAAiBD,EAAW,QAAU,GACvCA,EAAW,MAAOE,GAAMA,EAAE,KAAO,SAAS,EAE/C,OACEC,EAAAA,KAAC,MAAA,CAAI,UAAU,wCACb,SAAA,CAAAA,EAAAA,KAAC,SAAA,CAAO,UAAU,sBAChB,SAAA,CAAAA,EAAAA,KAAC,MAAA,CAAI,UAAU,oCACb,SAAA,CAAAC,MAAC,KAAA,CAAG,UAAU,wBAAyB,SAAAvB,EAAE,kBAAkB,EAAE,EAC7DsB,EAAAA,KAACE,EAAA,CACC,QAAQ,QACR,KAAK,KACL,UAAU,UACV,QAAS,IAAMtB,EAAK,QAAA,EACpB,SAAUA,EAAK,WACf,aAAYF,EAAE,kBAAmB,CAAE,GAAI,SAAU,EAEhD,SAAA,CAAAE,EAAK,iBAAcuB,EAAA,CAAQ,UAAU,uBAAuB,EAAKF,EAAAA,IAACG,EAAA,CAAW,UAAU,SAAA,CAAU,EAClGH,EAAAA,IAAC,OAAA,CAAK,UAAU,mBAAoB,SAAAvB,EAAE,kBAAmB,CAAE,GAAI,QAAA,CAAU,CAAA,CAAE,CAAA,CAAA,CAAA,CAC7E,EACF,QACC,IAAA,CAAE,UAAU,wBAAyB,SAAAA,EAAE,qBAAqB,CAAA,CAAE,CAAA,EACjE,EAECE,EAAK,UACJqB,EAAAA,IAAC,OAAI,UAAU,4CAAA,CAA6C,EAC1DH,EACFG,EAAAA,IAACI,EAAA,CACC,WAAOC,EAAA,EAAU,EACjB,MAAO5B,EAAE,wBAAwB,EACjC,YAAaA,EAAE,8BAA8B,CAAA,CAAA,SAG9C6B,EAAA,CACC,SAAA,CAAAN,EAAAA,IAACO,EAAA,CACC,gBAACC,EAAA,CACC,SAAA,CAAAR,MAACS,EAAA,CAAU,UAAU,OAAQ,SAAAhC,EAAE,mBAAmB,EAAE,EACpDuB,EAAAA,IAACS,EAAA,CAAW,SAAAhC,EAAE,qBAAqB,CAAA,CAAE,EACrCuB,EAAAA,IAACS,EAAA,CAAW,SAAAhC,EAAE,uBAAuB,CAAA,CAAE,EACvCuB,EAAAA,IAACS,EAAA,CAAW,SAAAhC,EAAE,wBAAwB,CAAA,CAAE,EACxCuB,EAAAA,IAACS,EAAA,CAAW,SAAAhC,EAAE,0BAA0B,CAAA,CAAE,EAC1CuB,EAAAA,IAACS,EAAA,CAAU,UAAU,MAAA,CAAO,CAAA,CAAA,CAC9B,CAAA,CACF,QACCC,EAAA,CACE,SAAAd,EAAW,IAAK9B,UACd0C,EAAA,CACC,SAAA,CAAAR,EAAAA,IAACW,EAAA,CAAU,UAAU,oBAAqB,SAAA7C,EAAG,GAAG,EAChDkC,EAAAA,IAACW,EAAA,CAAU,UAAU,cAAe,WAAG,KAAK,EAC5CX,EAAAA,IAACW,EAAA,CACE,SAAA7C,EAAG,OAAO,SAAW,EACpBkC,EAAAA,IAAC,OAAA,CAAK,UAAU,0BAA0B,SAAA,IAAC,EAE3CA,EAAAA,IAAC,MAAA,CAAI,UAAU,uBACZ,SAAAlC,EAAG,OAAO,IAAK8C,GACdZ,MAACa,EAAA,CAAc,QAAQ,YAAa,SAAAD,CAAA,EAAxBA,CAA0B,CACvC,EACH,EAEJ,EACAZ,EAAAA,IAACW,EAAA,CACG,UAAA7C,EAAG,SAAW,IAAI,SAAW,EAC7BkC,EAAAA,IAAC,OAAA,CAAK,UAAU,0BAA2B,SAAAvB,EAAE,wBAAwB,CAAA,CAAE,EAEvEuB,EAAAA,IAAC,OAAA,CAAK,UAAU,wBACb,SAAAvB,EAAE,0BAA2B,CAAE,MAAOX,EAAG,QAAS,MAAA,CAAQ,EAC7D,EAEJ,QACC6C,EAAA,CAAU,UAAU,wBAClB,SAAA7C,EAAG,UACAW,EAAE,6BAA8B,CAC9B,KAAMX,EAAG,UAAU,KACnB,YAAaA,EAAG,UAAU,YAC1B,MAAOA,EAAG,UAAU,KAAA,CACrB,EACD,GAAA,CACN,QACC6C,EAAA,CACC,SAAAZ,EAAAA,KAACE,EAAA,CACC,KAAK,SACL,QAAQ,QACR,KAAK,KACL,QAAS,IAAMZ,EAAUvB,CAAE,EAE3B,SAAA,CAAAkC,EAAAA,IAACtC,EAAA,CAAO,UAAU,SAAA,CAAU,EAC3Be,EAAE,iBAAiB,CAAA,CAAA,CAAA,CACtB,CACF,CAAA,GA1CaX,EAAG,EA2ClB,CACD,CAAA,CACH,CAAA,EACF,EAIFiC,EAAAA,KAAC,OAAA,CACC,SAAWP,GAAM,KAAKD,EAASC,CAAC,EAChC,UAAU,iDAEV,SAAA,CAAAQ,MAAC,MAAA,CAAI,UAAU,2BAA4B,SAAAvB,EAAE,uBAAuB,EAAE,EACrES,GACCc,EAAAA,IAAC,IAAA,CAAE,UAAU,6BAA8B,SAAAvB,EAAE,8BAA8B,EAAE,EAE/EsB,EAAAA,KAAC,MAAA,CAAI,UAAU,wCACb,SAAA,CAAAC,EAAAA,IAACc,EAAA,CACC,GAAG,QACH,MAAOrC,EAAE,oBAAoB,EAC7B,KAAMA,EAAE,wBAAwB,EAChC,MAAOM,EAAM,GACb,SAAWgC,GAAM/B,EAAUhB,IAAO,CAAE,GAAGA,EAAG,GAAI+C,CAAA,EAAI,EAClD,YAAY,WACZ,KAAI,EAAA,CAAA,EAENf,EAAAA,IAACc,EAAA,CACC,GAAG,UACH,MAAOrC,EAAE,sBAAsB,EAC/B,MAAOM,EAAM,KACb,SAAWgC,GAAM/B,EAAUhB,IAAO,CAAE,GAAGA,EAAG,KAAM+C,CAAA,EAAI,EACpD,YAAY,UAAA,CAAA,EAEdf,EAAAA,IAACc,EAAA,CACC,GAAG,YACH,MAAOrC,EAAE,wBAAwB,EACjC,KAAMA,EAAE,4BAA4B,EACpC,MAAOM,EAAM,OACb,SAAWgC,GAAM/B,EAAUhB,IAAO,CAAE,GAAGA,EAAG,OAAQ+C,CAAA,EAAI,EACtD,YAAY,qBACZ,KAAI,EAAA,CAAA,EAENf,EAAAA,IAACc,EAAA,CACC,GAAG,aACH,MAAOrC,EAAE,yBAAyB,EAClC,KAAMA,EAAE,6BAA6B,EACrC,MAAOM,EAAM,QACb,SAAWgC,GAAM/B,EAAUhB,IAAO,CAAE,GAAGA,EAAG,QAAS+C,CAAA,EAAI,EACvD,YAAY,kCACZ,KAAI,EAAA,CAAA,CACN,EACF,EACAhB,EAAAA,KAAC,MAAA,CAAI,UAAU,OACb,SAAA,CAAAC,MAAC,MAAA,CAAI,UAAU,oCAAqC,SAAAvB,EAAE,2BAA2B,EAAE,EACnFsB,EAAAA,KAAC,MAAA,CAAI,UAAU,8BACb,SAAA,CAAAC,EAAAA,IAACc,EAAA,CACC,GAAG,UACH,MAAOrC,EAAE,sBAAsB,EAC/B,MAAOM,EAAM,KACb,SAAWgC,GAAM/B,EAAUhB,IAAO,CAAE,GAAGA,EAAG,KAAM+C,CAAA,EAAI,EACpD,YAAY,KACZ,KAAI,EAAA,CAAA,EAENf,EAAAA,IAACc,EAAA,CACC,GAAG,SACH,MAAOrC,EAAE,6BAA6B,EACtC,MAAOM,EAAM,YACb,SAAWgC,GAAM/B,EAAUhB,IAAO,CAAE,GAAGA,EAAG,YAAa+C,CAAA,EAAI,EAC3D,YAAY,KACZ,KAAI,EAAA,CAAA,EAENf,EAAAA,IAACc,EAAA,CACC,GAAG,WACH,MAAOrC,EAAE,uBAAuB,EAChC,MAAOM,EAAM,MACb,SAAWgC,GAAM/B,EAAUhB,IAAO,CAAE,GAAGA,EAAG,MAAO+C,CAAA,EAAI,EACrD,YAAY,KACZ,KAAI,EAAA,CAAA,CACN,CAAA,CACF,CAAA,EACF,EACAhB,EAAAA,KAAC,MAAA,CAAI,UAAU,4BACb,SAAA,CAAAC,EAAAA,IAACC,EAAA,CACC,KAAK,SACL,QAAQ,YACR,KAAK,KACL,QAASX,EACT,SAAUT,EAAO,UAEhB,WAAE,uBAAuB,CAAA,CAAA,EAE5BkB,EAAAA,KAACE,EAAA,CACC,KAAK,SACL,KAAK,KACL,UAAU,UACV,SAAUpB,EAAO,WAAa,CAACE,EAAM,GAAG,KAAA,EAEvC,SAAA,CAAAF,EAAO,gBAAaqB,EAAA,CAAQ,UAAU,uBAAuB,EAAKF,EAAAA,IAACgB,EAAA,CAAK,UAAU,SAAA,CAAU,EAC5FvC,EAAE,wBAAwB,CAAA,CAAA,CAAA,CAC7B,CAAA,CACF,CAAA,CAAA,CAAA,CACF,EACF,CAEJ,CAYA,SAASqC,EAAU,CAAE,GAAAG,EAAI,MAAAC,EAAO,KAAAC,EAAM,MAAAC,EAAO,SAAAC,EAAU,YAAAC,EAAa,KAAAC,GAAqC,CACvG,OACExB,EAAAA,KAAC,MAAA,CAAI,UAAU,sBACb,SAAA,CAAAC,MAACwB,EAAA,CAAM,QAASP,EAAI,UAAU,wBAAyB,SAAAC,EAAM,EAC7DlB,EAAAA,IAACyB,EAAA,CACC,GAAAR,EACA,MAAAG,EACA,SAAW5B,GAAM6B,EAAS7B,EAAE,OAAO,KAAK,EACxC,YAAA8B,EACA,UAAWC,EAAO,oBAAsB,EAAA,CAAA,EAEzCJ,GAAQnB,EAAAA,IAAC,OAAA,CAAK,UAAU,8BAA+B,SAAAmB,CAAA,CAAK,CAAA,EAC/D,CAEJ","x_google_ignoreList":[0]}
@@ -1,7 +1,7 @@
1
- import{z as e}from"./index-7yc9y9We.js";/**
1
+ import{z as e}from"./index-igqJknUt.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 o=e("X",[["path",{d:"M18 6 6 18",key:"1bl5f8"}],["path",{d:"m6 6 12 12",key:"d8bk6v"}]]);export{o as X};
7
- //# sourceMappingURL=x-CioZGQfq.js.map
7
+ //# sourceMappingURL=x-BHW436CN.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"x-CioZGQfq.js","sources":["../../node_modules/lucide-react/dist/esm/icons/x.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 X = createLucideIcon(\"X\", [\n [\"path\", { d: \"M18 6 6 18\", key: \"1bl5f8\" }],\n [\"path\", { d: \"m6 6 12 12\", key: \"d8bk6v\" }]\n]);\n\nexport { X as default };\n//# sourceMappingURL=x.js.map\n"],"names":["X","createLucideIcon"],"mappings":"wCAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GASK,MAACA,EAAIC,EAAiB,IAAK,CAC9B,CAAC,OAAQ,CAAE,EAAG,aAAc,IAAK,QAAQ,CAAE,EAC3C,CAAC,OAAQ,CAAE,EAAG,aAAc,IAAK,QAAQ,CAAE,CAC7C,CAAC","x_google_ignoreList":[0]}
1
+ {"version":3,"file":"x-BHW436CN.js","sources":["../../node_modules/lucide-react/dist/esm/icons/x.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 X = createLucideIcon(\"X\", [\n [\"path\", { d: \"M18 6 6 18\", key: \"1bl5f8\" }],\n [\"path\", { d: \"m6 6 12 12\", key: \"d8bk6v\" }]\n]);\n\nexport { X as default };\n//# sourceMappingURL=x.js.map\n"],"names":["X","createLucideIcon"],"mappings":"wCAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GASK,MAACA,EAAIC,EAAiB,IAAK,CAC9B,CAAC,OAAQ,CAAE,EAAG,aAAc,IAAK,QAAQ,CAAE,EAC3C,CAAC,OAAQ,CAAE,EAAG,aAAc,IAAK,QAAQ,CAAE,CAC7C,CAAC","x_google_ignoreList":[0]}
@@ -35,9 +35,9 @@
35
35
  } catch (e) { /* private-mode storage exceptions: noop */ }
36
36
  })()
37
37
  </script>
38
- <script type="module" crossorigin src="/assets/index-7yc9y9We.js"></script>
38
+ <script type="module" crossorigin src="/assets/index-igqJknUt.js"></script>
39
39
  <link rel="modulepreload" crossorigin href="/assets/react-C9F3QeMB.js">
40
- <link rel="stylesheet" crossorigin href="/assets/index-hiagPF9i.css">
40
+ <link rel="stylesheet" crossorigin href="/assets/index-De8LfCtL.css">
41
41
  </head>
42
42
  <body>
43
43
  <div id="root"></div>
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/web/server.ts"],"names":[],"mappings":"AAyQA,wBAAgB,iBAAiB,IAAI,CAAC,EAAE,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,IAAI,CAOrE;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE;IAC5C,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,YAAY,EAAE,MAAM,CAAA;CACrB,GAAG,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAmrC/C"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/web/server.ts"],"names":[],"mappings":"AAyQA,wBAAgB,iBAAiB,IAAI,CAAC,EAAE,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,IAAI,CAOrE;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE;IAC5C,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,YAAY,EAAE,MAAM,CAAA;CACrB,GAAG,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CA+rC/C"}
@@ -711,6 +711,20 @@ export async function startWebServer(options) {
711
711
  return;
712
712
  return handleEmailTest(req, res);
713
713
  }
714
+ // v1.2.126 — web_search provider introspection + ping. GET lists
715
+ // every registered provider (name + envKey + current available),
716
+ // POST sends a one-shot test query through a single named
717
+ // provider and reports latency / hit count / error.
718
+ if (url.pathname === '/api/search/providers' && req.method === 'GET') {
719
+ if (!requireAdmin(req, res))
720
+ return;
721
+ return handleSearchProviders(req, res);
722
+ }
723
+ if (url.pathname === '/api/search/test' && req.method === 'POST') {
724
+ if (!requireAdmin(req, res))
725
+ return;
726
+ return handleSearchTest(req, res);
727
+ }
714
728
  if (url.pathname === '/api/workspaces' && req.method === 'GET') {
715
729
  return handleListWorkspaces(req, res, url);
716
730
  }
@@ -2729,8 +2743,38 @@ const ENV_EDITABLE_KEYS = [
2729
2743
  // empty (no blocks); listed platforms vanish from web + CLI + are
2730
2744
  // refused at registry.registerMessenger.
2731
2745
  'IMHUB_PLATFORM_BLACKLIST',
2746
+ // v1.2.126 — web_search provider chain (CSV) + each provider's API
2747
+ // key. `IMHUB_WEB_SEARCH_PROVIDERS` orders the fallback chain (e.g.
2748
+ // 'tavily,brave,duckduckgo'); a missing/empty value falls back to
2749
+ // DEFAULT_PROVIDER_ORDER in web-dispatcher. The 5 *_API_KEY values
2750
+ // gate per-provider availability (duckduckgo needs no key).
2751
+ 'IMHUB_WEB_SEARCH_PROVIDERS',
2752
+ 'TAVILY_API_KEY',
2753
+ 'BRAVE_API_KEY',
2754
+ 'SERPER_API_KEY',
2755
+ 'EXA_API_KEY',
2756
+ 'METASO_API_KEY',
2757
+ // v1.2.126 — native runtime caps (per-tool result chars / per-tool
2758
+ // timeout / blank-response retry / length-recovery retry). Hot-
2759
+ // reload: each subsystem re-reads process.env on every call, so
2760
+ // editing these takes effect without a restart.
2761
+ 'IMHUB_NATIVE_TOOL_RESULT_MAX_CHARS',
2762
+ 'IMHUB_NATIVE_TOOL_TIMEOUT_MS',
2763
+ 'IMHUB_NATIVE_BLANK_RETRY_MAX',
2764
+ 'IMHUB_NATIVE_LENGTH_RECOVERY_MAX',
2732
2765
  ];
2733
- const SECRET_KEYS = new Set(['IMHUB_SMTP_PASS', 'IMHUB_BAIDU_MAP_AK', 'IMHUB_MEMORY_VECTOR_OPENAI_API_KEY']);
2766
+ const SECRET_KEYS = new Set([
2767
+ 'IMHUB_SMTP_PASS',
2768
+ 'IMHUB_BAIDU_MAP_AK',
2769
+ 'IMHUB_MEMORY_VECTOR_OPENAI_API_KEY',
2770
+ // v1.2.126 — web search keys are bearer tokens / API keys, so
2771
+ // they're masked on GET and refused if echoed back on PUT.
2772
+ 'TAVILY_API_KEY',
2773
+ 'BRAVE_API_KEY',
2774
+ 'SERPER_API_KEY',
2775
+ 'EXA_API_KEY',
2776
+ 'METASO_API_KEY',
2777
+ ]);
2734
2778
  // maskSecret moved to ./env-mask.ts (imported at the top of this file
2735
2779
  // alongside isMasked).
2736
2780
  /** v1.2.67 — Security diagnostics endpoint. Returns the boot-time
@@ -2918,6 +2962,81 @@ async function handleEmailTest(req, res) {
2918
2962
  });
2919
2963
  }
2920
2964
  }
2965
+ // ─── v1.2.126 web_search provider config helpers ────────────────────────
2966
+ /** GET /api/search/providers — list every registered web_search
2967
+ * backend with its env-key requirement, current availability, and
2968
+ * the resolved chain (the order the dispatcher will try at next
2969
+ * call). The SPA renders this as the search-settings table. */
2970
+ async function handleSearchProviders(_req, res) {
2971
+ try {
2972
+ const { listAllSearchProviders, resolveSearchProviderChain } = await import('../core/llm/web-dispatcher.js');
2973
+ const providers = listAllSearchProviders();
2974
+ const chain = resolveSearchProviderChain().map((p) => p.name);
2975
+ sendJson(res, 200, { providers, chain });
2976
+ }
2977
+ catch (err) {
2978
+ sendJson(res, 500, { error: err instanceof Error ? err.message : String(err) });
2979
+ }
2980
+ }
2981
+ /** POST /api/search/test — `{ provider, query }`. Sends a one-shot
2982
+ * query through a single provider and returns `{ ok, latencyMs, hits, error? }`.
2983
+ * Does NOT walk the fallback chain — operators want to know "is THIS
2984
+ * provider's key working", not "did anything turn up". */
2985
+ async function handleSearchTest(req, res) {
2986
+ try {
2987
+ const body = await readBody(req, res);
2988
+ const parsed = JSON.parse(body || '{}');
2989
+ const provider = (parsed.provider || '').trim();
2990
+ const query = (parsed.query || '').trim();
2991
+ if (!provider) {
2992
+ sendError(res, 400, 'SEARCH_TEST_PROVIDER_REQUIRED', { message: 'provider required' });
2993
+ return;
2994
+ }
2995
+ if (!query) {
2996
+ sendError(res, 400, 'SEARCH_TEST_QUERY_REQUIRED', { message: 'query required' });
2997
+ return;
2998
+ }
2999
+ const { getSearchProviderByName } = await import('../core/llm/web-dispatcher.js');
3000
+ const p = getSearchProviderByName(provider);
3001
+ if (!p) {
3002
+ sendError(res, 400, 'SEARCH_TEST_UNKNOWN_PROVIDER', {
3003
+ message: `unknown provider "${provider}"`,
3004
+ });
3005
+ return;
3006
+ }
3007
+ if (!p.available()) {
3008
+ sendJson(res, 200, {
3009
+ ok: false, provider, latencyMs: 0, hits: 0,
3010
+ error: 'provider is not available (missing key?)',
3011
+ });
3012
+ return;
3013
+ }
3014
+ const controller = new AbortController();
3015
+ // Operators expect "test" to either succeed quickly or fail with a
3016
+ // clear timeout. 10 s is long enough for most search providers but
3017
+ // short enough that a hung backend doesn't hold the request open.
3018
+ const tm = setTimeout(() => controller.abort(), 10_000);
3019
+ const t0 = Date.now();
3020
+ try {
3021
+ const hits = await p.search(query, 3, controller.signal);
3022
+ sendJson(res, 200, {
3023
+ ok: true, provider, latencyMs: Date.now() - t0, hits: hits.length,
3024
+ });
3025
+ }
3026
+ catch (err) {
3027
+ sendJson(res, 200, {
3028
+ ok: false, provider, latencyMs: Date.now() - t0, hits: 0,
3029
+ error: err instanceof Error ? err.message : String(err),
3030
+ });
3031
+ }
3032
+ finally {
3033
+ clearTimeout(tm);
3034
+ }
3035
+ }
3036
+ catch (err) {
3037
+ sendJson(res, 500, { error: err instanceof Error ? err.message : String(err) });
3038
+ }
3039
+ }
2921
3040
  async function handleListBgjobs(_req, res, url) {
2922
3041
  try {
2923
3042
  const { resolveRoots, listJobsForRoot, listAllJobs } = await import('../core/bgjob-reader.js');