agim-cli 1.2.113 → 1.2.115

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 (172) hide show
  1. package/CHANGELOG.md +84 -0
  2. package/dist/core/approval-router.d.ts.map +1 -1
  3. package/dist/core/approval-router.js +4 -0
  4. package/dist/core/approval-router.js.map +1 -1
  5. package/dist/web/public/assets/{a2a-DjyqPnWh.js → a2a-BzsMbsts.js} +3 -3
  6. package/dist/web/public/assets/{a2a-DjyqPnWh.js.map → a2a-BzsMbsts.js.map} +1 -1
  7. package/dist/web/public/assets/{activity-Dy3elxiH.js → activity-CupFilVp.js} +2 -2
  8. package/dist/web/public/assets/{activity-Dy3elxiH.js.map → activity-CupFilVp.js.map} +1 -1
  9. package/dist/web/public/assets/{admins-p5qJnsCS.js → admins-BT1N7cZ-.js} +3 -3
  10. package/dist/web/public/assets/{admins-p5qJnsCS.js.map → admins-BT1N7cZ-.js.map} +1 -1
  11. package/dist/web/public/assets/{agents-BK-LNF6u.js → agents-BDRnYRTJ.js} +3 -3
  12. package/dist/web/public/assets/{agents-BK-LNF6u.js.map → agents-BDRnYRTJ.js.map} +1 -1
  13. package/dist/web/public/assets/approvals-DQpE8vhd.js +5 -0
  14. package/dist/web/public/assets/approvals-DQpE8vhd.js.map +1 -0
  15. package/dist/web/public/assets/{asks-1MaTIfZO.js → asks-DPBCUpfo.js} +2 -2
  16. package/dist/web/public/assets/{asks-1MaTIfZO.js.map → asks-DPBCUpfo.js.map} +1 -1
  17. package/dist/web/public/assets/{audit-jSgqqxdG.js → audit-DeLpvLdc.js} +2 -2
  18. package/dist/web/public/assets/{audit-jSgqqxdG.js.map → audit-DeLpvLdc.js.map} +1 -1
  19. package/dist/web/public/assets/{bell-LeCqn72n.js → bell-FJcNdfO1.js} +2 -2
  20. package/dist/web/public/assets/{bell-LeCqn72n.js.map → bell-FJcNdfO1.js.map} +1 -1
  21. package/dist/web/public/assets/{bgjobs-BPyKhOuq.js → bgjobs-2igxfgdb.js} +3 -3
  22. package/dist/web/public/assets/{bgjobs-BPyKhOuq.js.map → bgjobs-2igxfgdb.js.map} +1 -1
  23. package/dist/web/public/assets/{brain-CPuDSbdC.js → brain-DVE7F0fi.js} +2 -2
  24. package/dist/web/public/assets/{brain-CPuDSbdC.js.map → brain-DVE7F0fi.js.map} +1 -1
  25. package/dist/web/public/assets/{briefcase-Ck3PTo9z.js → briefcase-CZ7IbO42.js} +2 -2
  26. package/dist/web/public/assets/{briefcase-Ck3PTo9z.js.map → briefcase-CZ7IbO42.js.map} +1 -1
  27. package/dist/web/public/assets/{chevron-right-CNLUFBr6.js → chevron-right-Ckog5ufj.js} +2 -2
  28. package/dist/web/public/assets/{chevron-right-CNLUFBr6.js.map → chevron-right-Ckog5ufj.js.map} +1 -1
  29. package/dist/web/public/assets/{circle-check-CsSqoqeq.js → circle-check-BsvOPmQ8.js} +2 -2
  30. package/dist/web/public/assets/{circle-check-CsSqoqeq.js.map → circle-check-BsvOPmQ8.js.map} +1 -1
  31. package/dist/web/public/assets/{circle-check-big-NZvva0KN.js → circle-check-big-B6Cm3QrD.js} +2 -2
  32. package/dist/web/public/assets/{circle-check-big-NZvva0KN.js.map → circle-check-big-B6Cm3QrD.js.map} +1 -1
  33. package/dist/web/public/assets/{circle-x-CFWz6gEd.js → circle-x-B4eNkmrg.js} +2 -2
  34. package/dist/web/public/assets/{circle-x-CFWz6gEd.js.map → circle-x-B4eNkmrg.js.map} +1 -1
  35. package/dist/web/public/assets/confirm-dialog-BlS0v42Y.js +2 -0
  36. package/dist/web/public/assets/{confirm-dialog-DgO--07o.js.map → confirm-dialog-BlS0v42Y.js.map} +1 -1
  37. package/dist/web/public/assets/{data-table-D-Zgmc4f.js → data-table-DBKQnMMT.js} +5 -5
  38. package/dist/web/public/assets/{data-table-D-Zgmc4f.js.map → data-table-DBKQnMMT.js.map} +1 -1
  39. package/dist/web/public/assets/{dialog-CQgpA4K2.js → dialog-C3s_8PVJ.js} +3 -3
  40. package/dist/web/public/assets/{dialog-CQgpA4K2.js.map → dialog-C3s_8PVJ.js.map} +1 -1
  41. package/dist/web/public/assets/{download-DKzhn49i.js → download-Bjh71c-L.js} +2 -2
  42. package/dist/web/public/assets/{download-DKzhn49i.js.map → download-Bjh71c-L.js.map} +1 -1
  43. package/dist/web/public/assets/{email-j2wp12wu.js → email-GQHRSZUn.js} +3 -3
  44. package/dist/web/public/assets/{email-j2wp12wu.js.map → email-GQHRSZUn.js.map} +1 -1
  45. package/dist/web/public/assets/{empty-state-C_hR--ro.js → empty-state-D1DCgdU3.js} +2 -2
  46. package/dist/web/public/assets/{empty-state-C_hR--ro.js.map → empty-state-D1DCgdU3.js.map} +1 -1
  47. package/dist/web/public/assets/{external-link-CoqEoAcO.js → external-link-e7BaJ7SA.js} +2 -2
  48. package/dist/web/public/assets/{external-link-CoqEoAcO.js.map → external-link-e7BaJ7SA.js.map} +1 -1
  49. package/dist/web/public/assets/{eye-CXOBz4Cl.js → eye-CsEevkPC.js} +4 -4
  50. package/dist/web/public/assets/{eye-CXOBz4Cl.js.map → eye-CsEevkPC.js.map} +1 -1
  51. package/dist/web/public/assets/{facts-CJDnig7y.js → facts-CJfoY5hF.js} +2 -2
  52. package/dist/web/public/assets/{facts-CJDnig7y.js.map → facts-CJfoY5hF.js.map} +1 -1
  53. package/dist/web/public/assets/{goals-D89OCyJN.js → goals-D7UWxamV.js} +4 -4
  54. package/dist/web/public/assets/{goals-D89OCyJN.js.map → goals-D7UWxamV.js.map} +1 -1
  55. package/dist/web/public/assets/{health-BQwZeXAS.js → health-BTexN0L8.js} +2 -2
  56. package/dist/web/public/assets/{health-BQwZeXAS.js.map → health-BTexN0L8.js.map} +1 -1
  57. package/dist/web/public/assets/{heart-pulse-BzJZEKl7.js → heart-pulse-BM1ux0EY.js} +2 -2
  58. package/dist/web/public/assets/{heart-pulse-BzJZEKl7.js.map → heart-pulse-BM1ux0EY.js.map} +1 -1
  59. package/dist/web/public/assets/{heartbeat-D4kJCmkw.js → heartbeat-DVjVtdhR.js} +3 -3
  60. package/dist/web/public/assets/{heartbeat-D4kJCmkw.js.map → heartbeat-DVjVtdhR.js.map} +1 -1
  61. package/dist/web/public/assets/{hot-R6aOoPMY.js → hot-BDP_pB4z.js} +4 -4
  62. package/dist/web/public/assets/{hot-R6aOoPMY.js.map → hot-BDP_pB4z.js.map} +1 -1
  63. package/dist/web/public/assets/index-B2MYJZsl.css +1 -0
  64. package/dist/web/public/assets/{index-DVf2XlVZ.js → index-mOwnXPKu.js} +35 -30
  65. package/dist/web/public/assets/index-mOwnXPKu.js.map +1 -0
  66. package/dist/web/public/assets/{installed-DSJ7AiMe.js → installed-atOe7sV_.js} +4 -4
  67. package/dist/web/public/assets/{installed-DSJ7AiMe.js.map → installed-atOe7sV_.js.map} +1 -1
  68. package/dist/web/public/assets/{jobs-CdVDgq8m.js → jobs-dNZjmnF_.js} +2 -2
  69. package/dist/web/public/assets/{jobs-CdVDgq8m.js.map → jobs-dNZjmnF_.js.map} +1 -1
  70. package/dist/web/public/assets/{layout-BLKq93Z4.js → layout-B2VPwyz-.js} +2 -2
  71. package/dist/web/public/assets/{layout-BLKq93Z4.js.map → layout-B2VPwyz-.js.map} +1 -1
  72. package/dist/web/public/assets/{layout-Dj03GsQJ.js → layout-BTEf3WdH.js} +2 -2
  73. package/dist/web/public/assets/{layout-Dj03GsQJ.js.map → layout-BTEf3WdH.js.map} +1 -1
  74. package/dist/web/public/assets/{layout-CvmElCgO.js → layout-BncWEIS9.js} +2 -2
  75. package/dist/web/public/assets/{layout-CvmElCgO.js.map → layout-BncWEIS9.js.map} +1 -1
  76. package/dist/web/public/assets/{layout-BqilVxGS.js → layout-CI8hrEc2.js} +2 -2
  77. package/dist/web/public/assets/{layout-BqilVxGS.js.map → layout-CI8hrEc2.js.map} +1 -1
  78. package/dist/web/public/assets/{layout-BKjENLxQ.js → layout-MBS8rn-e.js} +2 -2
  79. package/dist/web/public/assets/{layout-BKjENLxQ.js.map → layout-MBS8rn-e.js.map} +1 -1
  80. package/dist/web/public/assets/{llm-C5Uob7Ci.js → llm-DLtUwugt.js} +3 -3
  81. package/dist/web/public/assets/{llm-C5Uob7Ci.js.map → llm-DLtUwugt.js.map} +1 -1
  82. package/dist/web/public/assets/{loader-circle-DdWIiO8L.js → loader-circle-CtFCiPu1.js} +2 -2
  83. package/dist/web/public/assets/{loader-circle-DdWIiO8L.js.map → loader-circle-CtFCiPu1.js.map} +1 -1
  84. package/dist/web/public/assets/{map-pin-kO-wD4fU.js → map-pin-C15wH78Y.js} +2 -2
  85. package/dist/web/public/assets/{map-pin-kO-wD4fU.js.map → map-pin-C15wH78Y.js.map} +1 -1
  86. package/dist/web/public/assets/{mcp-DL5oUQgm.js → mcp-BaQwFIce.js} +3 -3
  87. package/dist/web/public/assets/{mcp-DL5oUQgm.js.map → mcp-BaQwFIce.js.map} +1 -1
  88. package/dist/web/public/assets/{memos-Ab9Rp5v_.js → memos-DYQOmtCk.js} +3 -3
  89. package/dist/web/public/assets/{memos-Ab9Rp5v_.js.map → memos-DYQOmtCk.js.map} +1 -1
  90. package/dist/web/public/assets/{messengers-D0a8jlLw.js → messengers-BY8LZqe9.js} +3 -3
  91. package/dist/web/public/assets/{messengers-D0a8jlLw.js.map → messengers-BY8LZqe9.js.map} +1 -1
  92. package/dist/web/public/assets/{native-agent-Bnz7Inoh.js → native-agent-BB6EQ2LY.js} +3 -3
  93. package/dist/web/public/assets/{native-agent-Bnz7Inoh.js.map → native-agent-BB6EQ2LY.js.map} +1 -1
  94. package/dist/web/public/assets/{network-BrQvlSjl.js → network-DZJYq1Mj.js} +2 -2
  95. package/dist/web/public/assets/{network-BrQvlSjl.js.map → network-DZJYq1Mj.js.map} +1 -1
  96. package/dist/web/public/assets/{outbox-BNDPAlVy.js → outbox-BDqtRSxv.js} +3 -3
  97. package/dist/web/public/assets/{outbox-BNDPAlVy.js.map → outbox-BDqtRSxv.js.map} +1 -1
  98. package/dist/web/public/assets/pagination-Ba3Mjh77.js +17 -0
  99. package/dist/web/public/assets/{pagination-CyoXKAm9.js.map → pagination-Ba3Mjh77.js.map} +1 -1
  100. package/dist/web/public/assets/{persona-Bxqqj1yV.js → persona-D_4-IJmo.js} +2 -2
  101. package/dist/web/public/assets/{persona-Bxqqj1yV.js.map → persona-D_4-IJmo.js.map} +1 -1
  102. package/dist/web/public/assets/{play-B7ZN5aFX.js → play-DPwqjccL.js} +2 -2
  103. package/dist/web/public/assets/{play-B7ZN5aFX.js.map → play-DPwqjccL.js.map} +1 -1
  104. package/dist/web/public/assets/{plus-DzvDGrh0.js → plus-_UEfPKDJ.js} +2 -2
  105. package/dist/web/public/assets/{plus-DzvDGrh0.js.map → plus-_UEfPKDJ.js.map} +1 -1
  106. package/dist/web/public/assets/policy-DYDO0S91.js +2 -0
  107. package/dist/web/public/assets/{policy-CSl5ENVs.js.map → policy-DYDO0S91.js.map} +1 -1
  108. package/dist/web/public/assets/{refresh-ccw-B8Fzl2uq.js → refresh-ccw-DV-LGOe8.js} +2 -2
  109. package/dist/web/public/assets/{refresh-ccw-B8Fzl2uq.js.map → refresh-ccw-DV-LGOe8.js.map} +1 -1
  110. package/dist/web/public/assets/{reminders-CmLd8DLt.js → reminders-dpL0vI2y.js} +3 -3
  111. package/dist/web/public/assets/{reminders-CmLd8DLt.js.map → reminders-dpL0vI2y.js.map} +1 -1
  112. package/dist/web/public/assets/{save-JKib3YIn.js → save-HyYEfaEh.js} +2 -2
  113. package/dist/web/public/assets/{save-JKib3YIn.js.map → save-HyYEfaEh.js.map} +1 -1
  114. package/dist/web/public/assets/{schedules-B-770HIa.js → schedules-S_21sNfk.js} +3 -3
  115. package/dist/web/public/assets/{schedules-B-770HIa.js.map → schedules-S_21sNfk.js.map} +1 -1
  116. package/dist/web/public/assets/{search-Bc7OS8FB.js → search-Djy367Rt.js} +2 -2
  117. package/dist/web/public/assets/{search-Bc7OS8FB.js.map → search-Djy367Rt.js.map} +1 -1
  118. package/dist/web/public/assets/{security-CD8INSD5.js → security-CEMQ0kCc.js} +2 -2
  119. package/dist/web/public/assets/{security-CD8INSD5.js.map → security-CEMQ0kCc.js.map} +1 -1
  120. package/dist/web/public/assets/{service-CFB2QfK6.js → service-CKUTO48Q.js} +3 -3
  121. package/dist/web/public/assets/{service-CFB2QfK6.js.map → service-CKUTO48Q.js.map} +1 -1
  122. package/dist/web/public/assets/{status-badge---MleYSt.js → status-badge-B3slbwsq.js} +2 -2
  123. package/dist/web/public/assets/{status-badge---MleYSt.js.map → status-badge-B3slbwsq.js.map} +1 -1
  124. package/dist/web/public/assets/{subtasks-CzToK_65.js → subtasks-DdPpimPU.js} +2 -2
  125. package/dist/web/public/assets/{subtasks-CzToK_65.js.map → subtasks-DdPpimPU.js.map} +1 -1
  126. package/dist/web/public/assets/table-DWSx-SNj.js +2 -0
  127. package/dist/web/public/assets/{table-Dg3N5NYh.js.map → table-DWSx-SNj.js.map} +1 -1
  128. package/dist/web/public/assets/{topn-CePuMziu.js → topn-S7eTYdrL.js} +2 -2
  129. package/dist/web/public/assets/{topn-CePuMziu.js.map → topn-S7eTYdrL.js.map} +1 -1
  130. package/dist/web/public/assets/{trash-2-LmyUghia.js → trash-2-CLAmlNXR.js} +3 -3
  131. package/dist/web/public/assets/{trash-2-LmyUghia.js.map → trash-2-CLAmlNXR.js.map} +1 -1
  132. package/dist/web/public/assets/{use-background-tasks-CmVigPtZ.js → use-background-tasks-vNs4a0ZJ.js} +2 -2
  133. package/dist/web/public/assets/{use-background-tasks-CmVigPtZ.js.map → use-background-tasks-vNs4a0ZJ.js.map} +1 -1
  134. package/dist/web/public/assets/use-llm-admin-D7dW4gjp.js +2 -0
  135. package/dist/web/public/assets/{use-llm-admin-D8Go_PKM.js.map → use-llm-admin-D7dW4gjp.js.map} +1 -1
  136. package/dist/web/public/assets/use-memory-DKKNkzgN.js +2 -0
  137. package/dist/web/public/assets/{use-memory-BDuQTDX0.js.map → use-memory-DKKNkzgN.js.map} +1 -1
  138. package/dist/web/public/assets/{use-observability-CiO9A5Z9.js → use-observability-BOI93kfB.js} +2 -2
  139. package/dist/web/public/assets/{use-observability-CiO9A5Z9.js.map → use-observability-BOI93kfB.js.map} +1 -1
  140. package/dist/web/public/assets/{use-settings-fG28ShvX.js → use-settings-4nvtqd3e.js} +2 -2
  141. package/dist/web/public/assets/{use-settings-fG28ShvX.js.map → use-settings-4nvtqd3e.js.map} +1 -1
  142. package/dist/web/public/assets/use-workspace-Dueu86Od.js +2 -0
  143. package/dist/web/public/assets/{use-workspace-CE3JTeap.js.map → use-workspace-Dueu86Od.js.map} +1 -1
  144. package/dist/web/public/assets/useQuery-D2xXEtPQ.js +2 -0
  145. package/dist/web/public/assets/{useQuery-BtY7wSLM.js.map → useQuery-D2xXEtPQ.js.map} +1 -1
  146. package/dist/web/public/assets/{vector-CakzHMP0.js → vector-C8P2Ue4j.js} +2 -2
  147. package/dist/web/public/assets/{vector-CakzHMP0.js.map → vector-C8P2Ue4j.js.map} +1 -1
  148. package/dist/web/public/assets/{viewer-DWOOXMMI.js → viewer-yPQqtQrA.js} +4 -4
  149. package/dist/web/public/assets/{viewer-DWOOXMMI.js.map → viewer-yPQqtQrA.js.map} +1 -1
  150. package/dist/web/public/assets/{workspace-BnlWyXR1.js → workspace-w-27u5cT.js} +3 -3
  151. package/dist/web/public/assets/{workspace-BnlWyXR1.js.map → workspace-w-27u5cT.js.map} +1 -1
  152. package/dist/web/public/assets/{workspaces-DTXat9Xq.js → workspaces-8_zB1j-c.js} +3 -3
  153. package/dist/web/public/assets/{workspaces-DTXat9Xq.js.map → workspaces-8_zB1j-c.js.map} +1 -1
  154. package/dist/web/public/assets/{x-BVwIUHdL.js → x-B5fwYKOg.js} +2 -2
  155. package/dist/web/public/assets/{x-BVwIUHdL.js.map → x-B5fwYKOg.js.map} +1 -1
  156. package/dist/web/public/index.html +2 -2
  157. package/dist/web/server.d.ts.map +1 -1
  158. package/dist/web/server.js +81 -53
  159. package/dist/web/server.js.map +1 -1
  160. package/package.json +1 -1
  161. package/dist/web/public/assets/approvals-BR1f5zmP.js +0 -10
  162. package/dist/web/public/assets/approvals-BR1f5zmP.js.map +0 -1
  163. package/dist/web/public/assets/confirm-dialog-DgO--07o.js +0 -2
  164. package/dist/web/public/assets/index-BS4QrJNL.css +0 -1
  165. package/dist/web/public/assets/index-DVf2XlVZ.js.map +0 -1
  166. package/dist/web/public/assets/pagination-CyoXKAm9.js +0 -17
  167. package/dist/web/public/assets/policy-CSl5ENVs.js +0 -2
  168. package/dist/web/public/assets/table-Dg3N5NYh.js +0 -2
  169. package/dist/web/public/assets/use-llm-admin-D8Go_PKM.js +0 -2
  170. package/dist/web/public/assets/use-memory-BDuQTDX0.js +0 -2
  171. package/dist/web/public/assets/use-workspace-CE3JTeap.js +0 -2
  172. package/dist/web/public/assets/useQuery-BtY7wSLM.js +0 -2
@@ -1,4 +1,4 @@
1
- import{y,ad as f,ac as B,U as e,T as D,c as u,L as A,S as R,l as U,m as V,j as $,k as C,s as p,d as N,B as h,a3 as _,E as H}from"./index-DVf2XlVZ.js";import{e as v}from"./react-C9F3QeMB.js";import{E as z}from"./empty-state-C_hR--ro.js";import{u as W,a as Z,b as I}from"./use-workspace-CE3JTeap.js";import{L as P}from"./loader-circle-DdWIiO8L.js";import{R as O}from"./refresh-ccw-B8Fzl2uq.js";import"./useQuery-BtY7wSLM.js";/**
1
+ import{z as y,ae as f,ad as B,V as e,T as D,c as u,L as A,S as V,l as R,m as $,j as H,k as z,t as p,d as N,B as h,a4 as C,G as U}from"./index-mOwnXPKu.js";import{e as v}from"./react-C9F3QeMB.js";import{E as _}from"./empty-state-D1DCgdU3.js";import{u as W,a as Z,b as I}from"./use-workspace-Dueu86Od.js";import{L as P}from"./loader-circle-CtFCiPu1.js";import{R as O}from"./refresh-ccw-DV-LGOe8.js";import"./useQuery-D2xXEtPQ.js";/**
2
2
  * @license lucide-react v0.469.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -13,5 +13,5 @@ import{y,ad as f,ac as B,U as e,T as D,c as u,L as A,S as R,l as U,m as V,j as $
13
13
  *
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
- */const q=y("Folder",[["path",{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",key:"1kt360"}]]);function re(){const{t}=f(["workspace","common"]),[r,c]=B(),s=r.get("agent")??"",n=r.get("path")??"",i=W().data??{},o=v.useMemo(()=>Object.keys(i).sort(),[i]),d=Z(s,n,{enabled:!!s}),l=d.data,g=l?.type==="dir",M=l?.type==="file";function k(a){const m=new URLSearchParams(r);for(const[F,b]of Object.entries(a))b==null||b===""?m.delete(F):m.set(F,b);c(m,{replace:!1})}function T(a){k({agent:a||null,path:null})}function j(a){k({path:a||null})}function w(){if(!n)return;const a=n.lastIndexOf("/"),m=a>=0?n.slice(0,a):"";j(m)}return e.jsxs("div",{className:"flex min-h-dvh flex-col bg-bg",children:[e.jsx(D,{}),e.jsxs("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",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:t("pageTitle")}),e.jsxs(u,{variant:"ghost",size:"sm",className:"ml-auto",onClick:()=>d.refetch(),disabled:!s||d.isFetching,"aria-label":t("actions.refresh",{ns:"common"}),children:[d.isFetching?e.jsx(P,{className:"h-4 w-4 animate-spin"}):e.jsx(O,{className:"h-4 w-4"}),e.jsx("span",{className:"hidden sm:inline",children:t("actions.refresh",{ns:"common"})})]})]}),e.jsx("p",{className:"text-sm text-text-dim",children:t("subtitle")})]}),e.jsx("div",{className:"flex flex-wrap items-end gap-2",children:e.jsxs("div",{className:"flex flex-col gap-1",children:[e.jsx(A,{htmlFor:"agent",className:"text-xs text-text-dim",children:t("agentPicker.label")}),e.jsxs(R,{value:s||"__placeholder__",onValueChange:a=>T(a==="__placeholder__"?"":a),children:[e.jsx(U,{id:"agent",className:"w-64",children:e.jsx(V,{placeholder:t("agentPicker.placeholder")})}),e.jsxs($,{children:[e.jsx(C,{value:"__placeholder__",disabled:!0,children:t("agentPicker.placeholder")}),o.map(a=>e.jsx(C,{value:a,children:e.jsxs("span",{className:"flex items-center gap-2",children:[e.jsx("span",{className:"font-mono text-xs",children:a}),e.jsx("span",{className:p("inline-block h-1.5 w-1.5 rounded-full",i[a]?"bg-success":"bg-text-muted")}),e.jsx("span",{className:"text-text-dim text-xs",children:i[a]?t("agentPicker.online"):t("agentPicker.offline")})]})},a))]})]})]})}),s?e.jsxs(e.Fragment,{children:[e.jsx(K,{path:n,onNavigate:j,onUp:w}),d.isLoading?e.jsx("div",{className:"h-32 rounded-md bg-surface-2 animate-pulse"}):g&&l?.type==="dir"?e.jsx(G,{entries:l.entries,onEnter:a=>j(n?`${n}/${a.name}`:a.name)}):M&&l?.type==="file"?e.jsx(J,{file:l,agent:s,onBack:w}):e.jsx(z,{icon:e.jsx(S,{}),title:t("editor.loadFailed"),description:d.error?.message??""})]}):e.jsx(z,{icon:e.jsx(S,{}),title:t("noAgent.title"),description:t("noAgent.description")})]})]})}function K({path:t,onNavigate:r,onUp:c}){const{t:s}=f("workspace"),n=t?t.split("/"):[];return e.jsxs("div",{className:"flex flex-wrap items-center gap-1 text-sm",children:[e.jsx(u,{type:"button",variant:"ghost",size:"sm",onClick:c,disabled:!t,"aria-label":s("tree.up"),children:e.jsx(N,{className:"h-4 w-4"})}),e.jsx("button",{type:"button",onClick:()=>r(""),className:"text-text-dim hover:text-text rounded px-1 py-0.5",children:s("tree.rootLabel")}),n.map((x,i)=>{const o=n.slice(0,i+1).join("/");return e.jsxs("span",{className:"flex items-center gap-1",children:[e.jsx("span",{className:"text-text-muted",children:"/"}),e.jsx("button",{type:"button",onClick:()=>r(o),className:"text-text-dim hover:text-text rounded px-1 py-0.5",children:x})]},o)})]})}function G({entries:t,onEnter:r}){const{t:c}=f("workspace");return t.length===0?e.jsx("div",{className:"rounded-md border border-border bg-surface p-3 text-sm text-text-dim",children:c("tree.empty")}):e.jsx("ul",{className:"divide-y divide-border rounded-md border border-border bg-surface",children:t.map(s=>e.jsx("li",{children:e.jsxs("button",{type:"button",onClick:()=>r(s),disabled:s.broken||s.symlink_escape,className:p("flex w-full items-center gap-2 px-3 py-2 text-sm text-left","transition-colors hover:bg-surface-hover disabled:opacity-50 disabled:cursor-not-allowed"),children:[s.isDir?e.jsx(q,{className:"h-4 w-4 text-accent"}):e.jsx(Q,{className:"h-4 w-4 text-text-dim"}),e.jsx("span",{className:s.isDir?"font-medium text-text":"text-text",children:s.name}),s.symlink_escape&&e.jsx(h,{variant:"warning",className:"ml-2",children:c("tree.symlinkEscape")}),s.broken&&!s.symlink_escape&&e.jsx(h,{variant:"danger",className:"ml-2",children:c("tree.broken")}),!s.isDir&&s.size!=null&&e.jsx("span",{className:"ml-auto text-xs text-text-muted tabular-nums",children:E(s.size)}),s.mtime&&e.jsx("span",{className:p("text-xs text-text-muted tabular-nums",!s.isDir&&s.size!=null?"ml-3":"ml-auto"),children:L(s.mtime)})]})},s.name))})}function J({file:t,agent:r,onBack:c}){const{t:s}=f(["workspace","common"]),[n,x]=v.useState(t.content);v.useEffect(()=>{x(t.content)},[t.content]);const i=n!==t.content,o=I();async function d(){try{await o.mutateAsync({agent:r,path:t.path,body:{content:n}}),_.success(s("toast.saved",{path:t.path}))}catch(l){const{message:g}=H(l,s);_.error(g)}}return t.encoding==="base64"?e.jsxs("div",{className:"rounded-md border border-border bg-surface p-4 text-sm",children:[e.jsxs(u,{variant:"ghost",size:"sm",onClick:c,className:"mb-3",children:[e.jsx(N,{className:"h-4 w-4"}),s("tree.up")]}),e.jsx("p",{className:"text-text-dim",children:s("editor.binary")}),e.jsxs("p",{className:"mt-2 text-xs text-text-muted tabular-nums",children:[t.path," · ",E(t.size)]})]}):e.jsxs("div",{className:"flex flex-col gap-2",children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[e.jsxs(u,{variant:"ghost",size:"sm",onClick:c,children:[e.jsx(N,{className:"h-4 w-4"}),s("tree.up")]}),e.jsx("code",{className:"rounded bg-surface-2 px-1.5 py-0.5 text-xs font-mono",children:t.path}),t.truncated&&e.jsx(h,{variant:"warning",children:"truncated"}),i&&e.jsx(h,{variant:"info",children:s("editor.dirtyHint")}),e.jsxs(u,{type:"button",size:"sm",className:"ml-auto",onClick:()=>void d(),disabled:!i||o.isPending||t.truncated,children:[o.isPending?e.jsx(P,{className:"h-4 w-4 animate-spin"}):null,o.isPending?s("editor.saving"):s("editor.save")]})]}),t.truncated&&e.jsx("p",{className:"text-xs text-warning",children:s("editor.truncated")}),e.jsx("textarea",{value:n,onChange:l=>x(l.target.value),rows:24,spellCheck:!1,className:p("w-full resize-y rounded-md border bg-bg px-3 py-2","font-mono text-xs leading-5","focus:outline-none focus:ring-2 focus:ring-accent focus:ring-offset-1 focus:ring-offset-bg",i?"border-accent":"border-border")}),!i&&e.jsx("p",{className:"text-xs text-text-muted",children:s("editor.savedMeta",{size:t.size,at:L(t.mtime)})})]})}function L(t){if(t==null)return"—";try{const r=new Date(t);return Number.isNaN(r.getTime())?t:r.toLocaleString(void 0,{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"})}catch{return t}}function E(t){return t<1024?`${t} B`:t<1024*1024?`${(t/1024).toFixed(1)} KiB`:`${(t/1024/1024).toFixed(1)} MiB`}export{re as default};
17
- //# sourceMappingURL=workspace-BnlWyXR1.js.map
16
+ */const q=y("Folder",[["path",{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",key:"1kt360"}]]);function re(){const{t}=f(["workspace","common"]),[r,c]=B(),s=r.get("agent")??"",n=r.get("path")??"",i=W().data??{},o=v.useMemo(()=>Object.keys(i).sort(),[i]),d=Z(s,n,{enabled:!!s}),l=d.data,g=l?.type==="dir",T=l?.type==="file";function k(a){const m=new URLSearchParams(r);for(const[F,b]of Object.entries(a))b==null||b===""?m.delete(F):m.set(F,b);c(m,{replace:!1})}function E(a){k({agent:a||null,path:null})}function j(a){k({path:a||null})}function w(){if(!n)return;const a=n.lastIndexOf("/"),m=a>=0?n.slice(0,a):"";j(m)}return e.jsxs("div",{className:"flex min-h-dvh flex-col bg-bg",children:[e.jsx(D,{}),e.jsxs("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",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:t("pageTitle")}),e.jsxs(u,{variant:"ghost",size:"sm",className:"ml-auto",onClick:()=>d.refetch(),disabled:!s||d.isFetching,"aria-label":t("actions.refresh",{ns:"common"}),children:[d.isFetching?e.jsx(P,{className:"h-4 w-4 animate-spin"}):e.jsx(O,{className:"h-4 w-4"}),e.jsx("span",{className:"hidden sm:inline",children:t("actions.refresh",{ns:"common"})})]})]}),e.jsx("p",{className:"text-sm text-text-dim",children:t("subtitle")})]}),e.jsx("div",{className:"flex flex-wrap items-end gap-2",children:e.jsxs("div",{className:"flex flex-col gap-1",children:[e.jsx(A,{htmlFor:"agent",className:"text-xs text-text-dim",children:t("agentPicker.label")}),e.jsxs(V,{value:s||"__placeholder__",onValueChange:a=>E(a==="__placeholder__"?"":a),children:[e.jsx(R,{id:"agent",className:"w-64",children:e.jsx($,{placeholder:t("agentPicker.placeholder")})}),e.jsxs(H,{children:[e.jsx(z,{value:"__placeholder__",disabled:!0,children:t("agentPicker.placeholder")}),o.map(a=>e.jsx(z,{value:a,children:e.jsxs("span",{className:"flex items-center gap-2",children:[e.jsx("span",{className:"font-mono text-xs",children:a}),e.jsx("span",{className:p("inline-block h-1.5 w-1.5 rounded-full",i[a]?"bg-success":"bg-text-muted")}),e.jsx("span",{className:"text-text-dim text-xs",children:i[a]?t("agentPicker.online"):t("agentPicker.offline")})]})},a))]})]})]})}),s?e.jsxs(e.Fragment,{children:[e.jsx(G,{path:n,onNavigate:j,onUp:w}),d.isLoading?e.jsx("div",{className:"h-32 rounded-md bg-surface-2 animate-pulse"}):g&&l?.type==="dir"?e.jsx(K,{entries:l.entries,onEnter:a=>j(n?`${n}/${a.name}`:a.name)}):T&&l?.type==="file"?e.jsx(J,{file:l,agent:s,onBack:w}):e.jsx(_,{icon:e.jsx(S,{}),title:t("editor.loadFailed"),description:d.error?.message??""})]}):e.jsx(_,{icon:e.jsx(S,{}),title:t("noAgent.title"),description:t("noAgent.description")})]})]})}function G({path:t,onNavigate:r,onUp:c}){const{t:s}=f("workspace"),n=t?t.split("/"):[];return e.jsxs("div",{className:"flex flex-wrap items-center gap-1 text-sm",children:[e.jsx(u,{type:"button",variant:"ghost",size:"sm",onClick:c,disabled:!t,"aria-label":s("tree.up"),children:e.jsx(N,{className:"h-4 w-4"})}),e.jsx("button",{type:"button",onClick:()=>r(""),className:"text-text-dim hover:text-text rounded px-1 py-0.5",children:s("tree.rootLabel")}),n.map((x,i)=>{const o=n.slice(0,i+1).join("/");return e.jsxs("span",{className:"flex items-center gap-1",children:[e.jsx("span",{className:"text-text-muted",children:"/"}),e.jsx("button",{type:"button",onClick:()=>r(o),className:"text-text-dim hover:text-text rounded px-1 py-0.5",children:x})]},o)})]})}function K({entries:t,onEnter:r}){const{t:c}=f("workspace");return t.length===0?e.jsx("div",{className:"rounded-md border border-border bg-surface p-3 text-sm text-text-dim",children:c("tree.empty")}):e.jsx("ul",{className:"divide-y divide-border rounded-md border border-border bg-surface",children:t.map(s=>e.jsx("li",{children:e.jsxs("button",{type:"button",onClick:()=>r(s),disabled:s.broken||s.symlink_escape,className:p("flex w-full items-center gap-2 px-3 py-2 text-sm text-left","transition-colors hover:bg-surface-hover disabled:opacity-50 disabled:cursor-not-allowed"),children:[s.isDir?e.jsx(q,{className:"h-4 w-4 text-accent"}):e.jsx(Q,{className:"h-4 w-4 text-text-dim"}),e.jsx("span",{className:s.isDir?"font-medium text-text":"text-text",children:s.name}),s.symlink_escape&&e.jsx(h,{variant:"warning",className:"ml-2",children:c("tree.symlinkEscape")}),s.broken&&!s.symlink_escape&&e.jsx(h,{variant:"danger",className:"ml-2",children:c("tree.broken")}),!s.isDir&&s.size!=null&&e.jsx("span",{className:"ml-auto text-xs text-text-muted tabular-nums",children:M(s.size)}),s.mtime&&e.jsx("span",{className:p("text-xs text-text-muted tabular-nums",!s.isDir&&s.size!=null?"ml-3":"ml-auto"),children:L(s.mtime)})]})},s.name))})}function J({file:t,agent:r,onBack:c}){const{t:s}=f(["workspace","common"]),[n,x]=v.useState(t.content);v.useEffect(()=>{x(t.content)},[t.content]);const i=n!==t.content,o=I();async function d(){try{await o.mutateAsync({agent:r,path:t.path,body:{content:n}}),C.success(s("toast.saved",{path:t.path}))}catch(l){const{message:g}=U(l,s);C.error(g)}}return t.encoding==="base64"?e.jsxs("div",{className:"rounded-md border border-border bg-surface p-4 text-sm",children:[e.jsxs(u,{variant:"ghost",size:"sm",onClick:c,className:"mb-3",children:[e.jsx(N,{className:"h-4 w-4"}),s("tree.up")]}),e.jsx("p",{className:"text-text-dim",children:s("editor.binary")}),e.jsxs("p",{className:"mt-2 text-xs text-text-muted tabular-nums",children:[t.path," · ",M(t.size)]})]}):e.jsxs("div",{className:"flex flex-col gap-2",children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[e.jsxs(u,{variant:"ghost",size:"sm",onClick:c,children:[e.jsx(N,{className:"h-4 w-4"}),s("tree.up")]}),e.jsx("code",{className:"rounded bg-surface-2 px-1.5 py-0.5 text-xs font-mono",children:t.path}),t.truncated&&e.jsx(h,{variant:"warning",children:"truncated"}),i&&e.jsx(h,{variant:"info",children:s("editor.dirtyHint")}),e.jsxs(u,{type:"button",size:"sm",className:"ml-auto",onClick:()=>void d(),disabled:!i||o.isPending||t.truncated,children:[o.isPending?e.jsx(P,{className:"h-4 w-4 animate-spin"}):null,o.isPending?s("editor.saving"):s("editor.save")]})]}),t.truncated&&e.jsx("p",{className:"text-xs text-warning",children:s("editor.truncated")}),e.jsx("textarea",{value:n,onChange:l=>x(l.target.value),rows:24,spellCheck:!1,className:p("w-full resize-y rounded-md border bg-bg px-3 py-2","font-mono text-xs leading-5","focus:outline-none focus:ring-2 focus:ring-accent focus:ring-offset-1 focus:ring-offset-bg",i?"border-accent":"border-border")}),!i&&e.jsx("p",{className:"text-xs text-text-muted",children:s("editor.savedMeta",{size:t.size,at:L(t.mtime)})})]})}function L(t){if(t==null)return"—";try{const r=new Date(t);return Number.isNaN(r.getTime())?t:r.toLocaleString(void 0,{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"})}catch{return t}}function M(t){return t<1024?`${t} B`:t<1024*1024?`${(t/1024).toFixed(1)} KiB`:`${(t/1024/1024).toFixed(1)} MiB`}export{re as default};
17
+ //# sourceMappingURL=workspace-w-27u5cT.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"workspace-BnlWyXR1.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-w-27u5cT.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{y as S,ad as y,U as e,c as u,B as C,a3 as j,E as F,L as E,I as T}from"./index-DVf2XlVZ.js";import{e as f}from"./react-C9F3QeMB.js";import{E as B}from"./empty-state-C_hR--ro.js";import{T as z,d as D,e as g,c as d,a as P,b as x}from"./table-Dg3N5NYh.js";import{r as R,o as H}from"./use-settings-fG28ShvX.js";import{L as v}from"./loader-circle-DdWIiO8L.js";import{R as I}from"./refresh-ccw-B8Fzl2uq.js";import{B as W}from"./briefcase-Ck3PTo9z.js";import{S as A}from"./save-JKib3YIn.js";import"./useQuery-BtY7wSLM.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-mOwnXPKu.js";import{e as f}from"./react-C9F3QeMB.js";import{E as z}from"./empty-state-D1DCgdU3.js";import{T as B,d as D,e as g,c as d,a as P,b as x}from"./table-DWSx-SNj.js";import{r as R,o as H}from"./use-settings-4nvtqd3e.js";import{L as v}from"./loader-circle-CtFCiPu1.js";import{R as I}from"./refresh-ccw-DV-LGOe8.js";import{B as W}from"./briefcase-CZ7IbO42.js";import{S as A}from"./save-HyYEfaEh.js";import"./useQuery-D2xXEtPQ.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
- */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 U(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=U(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(B,{icon:e.jsx(W,{}),title:s("workspaces.empty.title"),description:s("workspaces.empty.description")}):e.jsxs(z,{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(E,{htmlFor:s,className:"text-xs text-text-dim",children:n}),e.jsx(T,{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-DTXat9Xq.js.map
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-8_zB1j-c.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"workspaces-DTXat9Xq.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-8_zB1j-c.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{y as e}from"./index-DVf2XlVZ.js";/**
1
+ import{z as e}from"./index-mOwnXPKu.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-BVwIUHdL.js.map
7
+ //# sourceMappingURL=x-B5fwYKOg.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"x-BVwIUHdL.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-B5fwYKOg.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-DVf2XlVZ.js"></script>
38
+ <script type="module" crossorigin src="/assets/index-mOwnXPKu.js"></script>
39
39
  <link rel="modulepreload" crossorigin href="/assets/react-C9F3QeMB.js">
40
- <link rel="stylesheet" crossorigin href="/assets/index-BS4QrJNL.css">
40
+ <link rel="stylesheet" crossorigin href="/assets/index-B2MYJZsl.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,CAspC/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,CAmrC/C"}
@@ -1239,66 +1239,94 @@ export async function startWebServer(options) {
1239
1239
  // Load existing session history if available
1240
1240
  sendSessionHistory(ws, clientId, getDefaultAgent(options.defaultAgent));
1241
1241
  const enqueueInbound = createSerialQueue();
1242
+ // v1.2.114 — approval-action clicks MUST bypass the serial queue.
1243
+ //
1244
+ // Pre-fix: every inbound WS frame went through `enqueueInbound`, which
1245
+ // serialised everything against `handleClientMessage`. A chat message
1246
+ // kicked off an agent run that could run for the full 30-min hard
1247
+ // timeout; any approval-card click during that window was QUEUED
1248
+ // behind the still-running chat handler. Result: deadlock — agent
1249
+ // waits for approval, approval waits for agent. The clicks finally
1250
+ // drained the queue at minute 30 when the agent aborted, but by then
1251
+ // the run was dead and the user's card UI had been stuck at
1252
+ // "已发送,等待结果…" the whole time.
1253
+ //
1254
+ // Fix: parse the frame inline first; if it's an `approval-action`,
1255
+ // dispatch directly to the click handler (no queue). Approval clicks
1256
+ // are the agent's UNBLOCKING input — they have no ordering dependency
1257
+ // on chat input. All other types (chat, switch-agent, get-history,
1258
+ // …) still go through enqueueInbound to preserve their per-client
1259
+ // FIFO ordering.
1260
+ async function handleApprovalAction(msg) {
1261
+ const actionData = String(msg.data || '');
1262
+ const messageId = String(msg.messageId || '');
1263
+ // Use `client.id` (mutable; updated on session rekey) — not the
1264
+ // original `clientId` const — so the threadId we hand to the bus
1265
+ // matches the threadId the bus stamped on the pending approval
1266
+ // request. Without this, a session that was rekeyed via
1267
+ // `get-history` reports the stale id and the bus can't find the
1268
+ // open req to resolve.
1269
+ const liveId = client.id;
1270
+ webLog.info({
1271
+ event: 'approval.web.click_received',
1272
+ clientId: liveId, data: actionData, messageId,
1273
+ handlerBound: !!webButtonHandler,
1274
+ });
1275
+ if (!actionData || !messageId) {
1276
+ sendToClient(ws, { type: 'error', message: 'approval-action missing data/messageId' });
1277
+ return;
1278
+ }
1279
+ if (!webButtonHandler) {
1280
+ const why = 'approval handler not bound (router not installed?). Restart agim to rebind.';
1281
+ webLog.warn({ event: 'approval.web.no_handler', clientId: liveId, data: actionData, messageId }, why);
1282
+ sendToClient(ws, { type: 'error', message: why });
1283
+ return;
1284
+ }
1285
+ try {
1286
+ // Web client doesn't have a platform-native toast, so ack is a
1287
+ // no-op resolving to the in-page status the page itself chose
1288
+ // to render after click.
1289
+ await webButtonHandler({
1290
+ data: actionData, threadId: liveId, userId: `web:${liveId}`,
1291
+ userDisplay: 'Web', messageId, ack: async () => { },
1292
+ });
1293
+ webLog.info({ event: 'approval.web.click_resolved', clientId: liveId, data: actionData });
1294
+ }
1295
+ catch (err) {
1296
+ const errMsg = err instanceof Error ? err.message : String(err);
1297
+ webLog.error({ event: 'approval.web.click_failed', clientId: liveId, data: actionData, err: errMsg });
1298
+ sendToClient(ws, { type: 'error', message: `Approval click failed: ${errMsg}` });
1299
+ }
1300
+ }
1242
1301
  ws.on('message', (data) => {
1302
+ // Parse once, here, so we can route approval-action around the
1303
+ // serial queue. Bad JSON falls through to the error branch below
1304
+ // (we still need to surface a parse error to the client).
1305
+ let msg;
1306
+ try {
1307
+ msg = JSON.parse(data.toString());
1308
+ }
1309
+ catch (err) {
1310
+ webLog.error({ clientId: client.id, err: err instanceof Error ? err.message : String(err) }, 'Error parsing client message');
1311
+ sendToClient(ws, { type: 'error', message: 'Invalid message format' });
1312
+ return;
1313
+ }
1314
+ if (msg && typeof msg === 'object' && msg.type === 'approval-action') {
1315
+ // Fast path — bypass enqueueInbound. Errors are surfaced to the
1316
+ // client inside handleApprovalAction.
1317
+ void handleApprovalAction(msg);
1318
+ return;
1319
+ }
1243
1320
  enqueueInbound(async () => {
1244
1321
  try {
1245
- const msg = JSON.parse(data.toString());
1246
- // Approval-button click intercept. The user tapped an in-page
1247
- // approval card button; route it through the web messenger's
1248
- // button handler (registered by approval-router on install) the
1249
- // same way a Telegram inline-keyboard tap is routed. We don't
1250
- // call handleClientMessage for these — they're not chat input.
1251
- if (msg && msg.type === 'approval-action') {
1252
- const actionData = String(msg.data || '');
1253
- const messageId = String(msg.messageId || '');
1254
- // Use `client.id` (mutable; updated on session rekey) — not
1255
- // the original `clientId` const — so the threadId we hand
1256
- // to the bus matches the threadId the bus stamped on the
1257
- // pending approval request. Without this, a session that
1258
- // was rekeyed via `get-history` reports the stale id and
1259
- // the bus can't find the open req to resolve.
1260
- const liveId = client.id;
1261
- webLog.info({
1262
- event: 'approval.web.click_received',
1263
- clientId: liveId, data: actionData, messageId,
1264
- handlerBound: !!webButtonHandler,
1265
- });
1266
- if (!actionData || !messageId) {
1267
- sendToClient(ws, { type: 'error', message: 'approval-action missing data/messageId' });
1268
- return;
1269
- }
1270
- if (!webButtonHandler) {
1271
- // Without the handler, a click would silently no-op forever — the
1272
- // failure mode that PR-A's fix patches. Tell the user and the
1273
- // operator (via log) instead of dropping the click.
1274
- const why = 'approval handler not bound (router not installed?). Restart agim to rebind.';
1275
- webLog.warn({ event: 'approval.web.no_handler', clientId: liveId, data: actionData, messageId }, why);
1276
- sendToClient(ws, { type: 'error', message: why });
1277
- return;
1278
- }
1279
- try {
1280
- // Most messengers' ButtonCallback#ack updates a platform-native
1281
- // toast / loading spinner. The web client doesn't have one, so
1282
- // ack is a no-op resolving to the in-page status the page itself
1283
- // chose to render after click.
1284
- await webButtonHandler({
1285
- data: actionData, threadId: liveId, userId: `web:${liveId}`,
1286
- userDisplay: 'Web', messageId, ack: async () => { },
1287
- });
1288
- webLog.info({ event: 'approval.web.click_resolved', clientId: liveId, data: actionData });
1289
- }
1290
- catch (err) {
1291
- const errMsg = err instanceof Error ? err.message : String(err);
1292
- webLog.error({ event: 'approval.web.click_failed', clientId: liveId, data: actionData, err: errMsg });
1293
- sendToClient(ws, { type: 'error', message: `Approval click failed: ${errMsg}` });
1294
- }
1295
- return;
1296
- }
1297
1322
  // Pass an onRekey callback so handleClientMessage can keep
1298
1323
  // the `clients` map in sync when a browser refresh resumes
1299
1324
  // with its persisted threadId. Otherwise the map stays keyed
1300
1325
  // by the WS-assigned id, and the web messenger's outbound
1301
1326
  // approval-card lookup misses the live client.
1327
+ // handleClientMessage validates the message shape itself; we
1328
+ // pass the parsed value through as the typed shape it expects
1329
+ // (cast is safe because validation runs on the receiver side).
1302
1330
  await handleClientMessage(client, msg, getDefaultAgent(options.defaultAgent), (oldId, newId) => {
1303
1331
  // Only reassign if we actually owned `oldId` — defensive
1304
1332
  // against the (impossible-but-cheap-to-guard) case of two
@@ -1317,7 +1345,7 @@ export async function startWebServer(options) {
1317
1345
  }, tokenId, threadOwners);
1318
1346
  }
1319
1347
  catch (err) {
1320
- webLog.error({ clientId: client.id, err: err instanceof Error ? err.message : String(err) }, 'Error parsing client message');
1348
+ webLog.error({ clientId: client.id, err: err instanceof Error ? err.message : String(err) }, 'Error in client message handler');
1321
1349
  sendToClient(ws, { type: 'error', message: 'Invalid message format' });
1322
1350
  }
1323
1351
  });