agim-cli 1.3.8 → 1.3.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/dist/cli-ui/tui/app.d.ts.map +1 -1
- package/dist/cli-ui/tui/app.js +67 -13
- package/dist/cli-ui/tui/app.js.map +1 -1
- package/dist/cli-ui/tui/stream-reveal.d.ts +13 -0
- package/dist/cli-ui/tui/stream-reveal.d.ts.map +1 -0
- package/dist/cli-ui/tui/stream-reveal.js +28 -0
- package/dist/cli-ui/tui/stream-reveal.js.map +1 -0
- package/dist/core/approval-router.d.ts.map +1 -1
- package/dist/core/approval-router.js +8 -11
- package/dist/core/approval-router.js.map +1 -1
- package/dist/core/ask-user-router.d.ts +13 -1
- package/dist/core/ask-user-router.d.ts.map +1 -1
- package/dist/core/ask-user-router.js +58 -23
- package/dist/core/ask-user-router.js.map +1 -1
- package/dist/core/ask-user-rpc.d.ts +33 -6
- package/dist/core/ask-user-rpc.d.ts.map +1 -1
- package/dist/core/ask-user-rpc.js +251 -65
- package/dist/core/ask-user-rpc.js.map +1 -1
- package/dist/core/llm/imhub-dispatcher.d.ts.map +1 -1
- package/dist/core/llm/imhub-dispatcher.js +2 -0
- package/dist/core/llm/imhub-dispatcher.js.map +1 -1
- package/dist/core/types.d.ts +21 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/plugins/agents/claude-code/index.d.ts +8 -0
- package/dist/plugins/agents/claude-code/index.d.ts.map +1 -1
- package/dist/plugins/agents/claude-code/index.js +34 -3
- package/dist/plugins/agents/claude-code/index.js.map +1 -1
- package/dist/plugins/messengers/telegram/telegram-adapter.d.ts +8 -0
- package/dist/plugins/messengers/telegram/telegram-adapter.d.ts.map +1 -1
- package/dist/plugins/messengers/telegram/telegram-adapter.js +67 -8
- package/dist/plugins/messengers/telegram/telegram-adapter.js.map +1 -1
- package/dist/web/public/assets/{a2a-D_I9ZX8i.js → a2a-BJDnrQvr.js} +2 -2
- package/dist/web/public/assets/{a2a-D_I9ZX8i.js.map → a2a-BJDnrQvr.js.map} +1 -1
- package/dist/web/public/assets/{activity-ZxWknBf9.js → activity-ODVTDapn.js} +2 -2
- package/dist/web/public/assets/{activity-ZxWknBf9.js.map → activity-ODVTDapn.js.map} +1 -1
- package/dist/web/public/assets/{admins-DFZcKgq0.js → admins-DUDFhuCJ.js} +2 -2
- package/dist/web/public/assets/{admins-DFZcKgq0.js.map → admins-DUDFhuCJ.js.map} +1 -1
- package/dist/web/public/assets/{agents-DTONSU0R.js → agents-DFrinQ8S.js} +2 -2
- package/dist/web/public/assets/{agents-DTONSU0R.js.map → agents-DFrinQ8S.js.map} +1 -1
- package/dist/web/public/assets/{approvals-BdDJvH8b.js → approvals-BBhAiOwe.js} +2 -2
- package/dist/web/public/assets/{approvals-BdDJvH8b.js.map → approvals-BBhAiOwe.js.map} +1 -1
- package/dist/web/public/assets/{arrow-down-Bhl7hNwa.js → arrow-down-Czj6Xxqp.js} +2 -2
- package/dist/web/public/assets/{arrow-down-Bhl7hNwa.js.map → arrow-down-Czj6Xxqp.js.map} +1 -1
- package/dist/web/public/assets/{arrow-up-DZLw3qr1.js → arrow-up-DMJEvzmu.js} +2 -2
- package/dist/web/public/assets/{arrow-up-DZLw3qr1.js.map → arrow-up-DMJEvzmu.js.map} +1 -1
- package/dist/web/public/assets/{asks-Cj2QazUD.js → asks-p9bF8Q81.js} +2 -2
- package/dist/web/public/assets/{asks-Cj2QazUD.js.map → asks-p9bF8Q81.js.map} +1 -1
- package/dist/web/public/assets/{audit-sxS7UUa6.js → audit-D3Rwft_U.js} +2 -2
- package/dist/web/public/assets/{audit-sxS7UUa6.js.map → audit-D3Rwft_U.js.map} +1 -1
- package/dist/web/public/assets/{bell-CqimaF3k.js → bell-BcfPrP44.js} +2 -2
- package/dist/web/public/assets/{bell-CqimaF3k.js.map → bell-BcfPrP44.js.map} +1 -1
- package/dist/web/public/assets/{bgjobs-BnXdz4TP.js → bgjobs-DI-G4o1P.js} +2 -2
- package/dist/web/public/assets/{bgjobs-BnXdz4TP.js.map → bgjobs-DI-G4o1P.js.map} +1 -1
- package/dist/web/public/assets/{brain-4TIClaVZ.js → brain-j7MTWb2W.js} +2 -2
- package/dist/web/public/assets/{brain-4TIClaVZ.js.map → brain-j7MTWb2W.js.map} +1 -1
- package/dist/web/public/assets/{briefcase-iKF60CJd.js → briefcase-D6VTVxcr.js} +2 -2
- package/dist/web/public/assets/{briefcase-iKF60CJd.js.map → briefcase-D6VTVxcr.js.map} +1 -1
- package/dist/web/public/assets/{chat-Cjp0ECm5.js → chat-Sh71acST.js} +2 -2
- package/dist/web/public/assets/{chat-Cjp0ECm5.js.map → chat-Sh71acST.js.map} +1 -1
- package/dist/web/public/assets/{chevron-left-sZyMkeG-.js → chevron-left-CJRMeoEU.js} +2 -2
- package/dist/web/public/assets/{chevron-left-sZyMkeG-.js.map → chevron-left-CJRMeoEU.js.map} +1 -1
- package/dist/web/public/assets/{chevron-right-CPHPRLSX.js → chevron-right-D7RvBcn9.js} +2 -2
- package/dist/web/public/assets/{chevron-right-CPHPRLSX.js.map → chevron-right-D7RvBcn9.js.map} +1 -1
- package/dist/web/public/assets/{circle-check-BB11MxVm.js → circle-check-DyXDqWS7.js} +2 -2
- package/dist/web/public/assets/{circle-check-BB11MxVm.js.map → circle-check-DyXDqWS7.js.map} +1 -1
- package/dist/web/public/assets/{circle-check-big-Sj-6H_3x.js → circle-check-big-4AFRLlHX.js} +2 -2
- package/dist/web/public/assets/{circle-check-big-Sj-6H_3x.js.map → circle-check-big-4AFRLlHX.js.map} +1 -1
- package/dist/web/public/assets/{circle-x-DYcD4BUu.js → circle-x-CKhp9N2v.js} +2 -2
- package/dist/web/public/assets/{circle-x-DYcD4BUu.js.map → circle-x-CKhp9N2v.js.map} +1 -1
- package/dist/web/public/assets/{clock-BVXa81yB.js → clock-BFwIA0Jz.js} +2 -2
- package/dist/web/public/assets/{clock-BVXa81yB.js.map → clock-BFwIA0Jz.js.map} +1 -1
- package/dist/web/public/assets/{confirm-dialog-C2melodU.js → confirm-dialog-Cm_B6a_Y.js} +2 -2
- package/dist/web/public/assets/{confirm-dialog-C2melodU.js.map → confirm-dialog-Cm_B6a_Y.js.map} +1 -1
- package/dist/web/public/assets/{copy-D1OIgfLU.js → copy-C2cKu0ar.js} +2 -2
- package/dist/web/public/assets/{copy-D1OIgfLU.js.map → copy-C2cKu0ar.js.map} +1 -1
- package/dist/web/public/assets/{data-table-BYJ2qMyY.js → data-table-DP1nlFsW.js} +2 -2
- package/dist/web/public/assets/{data-table-BYJ2qMyY.js.map → data-table-DP1nlFsW.js.map} +1 -1
- package/dist/web/public/assets/{download-BChufhnl.js → download-PfYhaNlR.js} +2 -2
- package/dist/web/public/assets/{download-BChufhnl.js.map → download-PfYhaNlR.js.map} +1 -1
- package/dist/web/public/assets/{email-exon2FvQ.js → email-CAS5_GZJ.js} +2 -2
- package/dist/web/public/assets/{email-exon2FvQ.js.map → email-CAS5_GZJ.js.map} +1 -1
- package/dist/web/public/assets/{empty-state-De5ls0uC.js → empty-state-CJm-vEmY.js} +2 -2
- package/dist/web/public/assets/{empty-state-De5ls0uC.js.map → empty-state-CJm-vEmY.js.map} +1 -1
- package/dist/web/public/assets/{external-link-CiJZi5YL.js → external-link-CBqMMl-X.js} +2 -2
- package/dist/web/public/assets/{external-link-CiJZi5YL.js.map → external-link-CBqMMl-X.js.map} +1 -1
- package/dist/web/public/assets/{eye-DowB5M1R.js → eye-CqZWsMY2.js} +2 -2
- package/dist/web/public/assets/{eye-DowB5M1R.js.map → eye-CqZWsMY2.js.map} +1 -1
- package/dist/web/public/assets/{facts-BdnrzWfV.js → facts-DzePN4sU.js} +2 -2
- package/dist/web/public/assets/{facts-BdnrzWfV.js.map → facts-DzePN4sU.js.map} +1 -1
- package/dist/web/public/assets/{goals-D6ibCBqb.js → goals-B3nMxvT5.js} +2 -2
- package/dist/web/public/assets/{goals-D6ibCBqb.js.map → goals-B3nMxvT5.js.map} +1 -1
- package/dist/web/public/assets/{health-BA8ydz1A.js → health-Br5o8-3r.js} +2 -2
- package/dist/web/public/assets/{health-BA8ydz1A.js.map → health-Br5o8-3r.js.map} +1 -1
- package/dist/web/public/assets/{heart-pulse-ClrbGrB7.js → heart-pulse-tKr9RbAP.js} +2 -2
- package/dist/web/public/assets/{heart-pulse-ClrbGrB7.js.map → heart-pulse-tKr9RbAP.js.map} +1 -1
- package/dist/web/public/assets/{heartbeat-j03zupAy.js → heartbeat-C1wrwDfB.js} +2 -2
- package/dist/web/public/assets/{heartbeat-j03zupAy.js.map → heartbeat-C1wrwDfB.js.map} +1 -1
- package/dist/web/public/assets/{hot-D3bwuObi.js → hot-Cg_tG6vW.js} +2 -2
- package/dist/web/public/assets/{hot-D3bwuObi.js.map → hot-Cg_tG6vW.js.map} +1 -1
- package/dist/web/public/assets/{index-CokfQGcz.css → index-DknVjPYB.css} +1 -1
- package/dist/web/public/assets/index-kq3mrzTq.js +209 -0
- package/dist/web/public/assets/index-kq3mrzTq.js.map +1 -0
- package/dist/web/public/assets/{injection-C89g9123.js → injection-DlagYMTW.js} +2 -2
- package/dist/web/public/assets/{injection-C89g9123.js.map → injection-DlagYMTW.js.map} +1 -1
- package/dist/web/public/assets/{installed-1Hal8dxW.js → installed-1JZZhebV.js} +2 -2
- package/dist/web/public/assets/{installed-1Hal8dxW.js.map → installed-1JZZhebV.js.map} +1 -1
- package/dist/web/public/assets/{jobs-cQ7V-7ec.js → jobs-x1N93chr.js} +2 -2
- package/dist/web/public/assets/{jobs-cQ7V-7ec.js.map → jobs-x1N93chr.js.map} +1 -1
- package/dist/web/public/assets/{layout-CZ9hb8_a.js → layout-B-1hwvUx.js} +2 -2
- package/dist/web/public/assets/{layout-CZ9hb8_a.js.map → layout-B-1hwvUx.js.map} +1 -1
- package/dist/web/public/assets/{layout-CXGgZCd2.js → layout-BL59PdSJ.js} +2 -2
- package/dist/web/public/assets/{layout-CXGgZCd2.js.map → layout-BL59PdSJ.js.map} +1 -1
- package/dist/web/public/assets/{layout-BbRAk_tA.js → layout-BiPpIZhD.js} +2 -2
- package/dist/web/public/assets/{layout-BbRAk_tA.js.map → layout-BiPpIZhD.js.map} +1 -1
- package/dist/web/public/assets/{layout-U5o6RgqN.js → layout-vc5vV1Pk.js} +2 -2
- package/dist/web/public/assets/{layout-U5o6RgqN.js.map → layout-vc5vV1Pk.js.map} +1 -1
- package/dist/web/public/assets/{layout-Y9JSZczb.js → layout-x8HdhDD2.js} +2 -2
- package/dist/web/public/assets/{layout-Y9JSZczb.js.map → layout-x8HdhDD2.js.map} +1 -1
- package/dist/web/public/assets/{llm-CumYZM83.js → llm-BpQBNzJC.js} +2 -2
- package/dist/web/public/assets/{llm-CumYZM83.js.map → llm-BpQBNzJC.js.map} +1 -1
- package/dist/web/public/assets/{loader-circle-CZQXsfz7.js → loader-circle-wNz3NK0s.js} +2 -2
- package/dist/web/public/assets/{loader-circle-CZQXsfz7.js.map → loader-circle-wNz3NK0s.js.map} +1 -1
- package/dist/web/public/assets/{map-pin-CYj0xBbt.js → map-pin-Cr5e7O3j.js} +2 -2
- package/dist/web/public/assets/{map-pin-CYj0xBbt.js.map → map-pin-Cr5e7O3j.js.map} +1 -1
- package/dist/web/public/assets/{mcp-DP4Vj1Nh.js → mcp-rajqKYei.js} +2 -2
- package/dist/web/public/assets/{mcp-DP4Vj1Nh.js.map → mcp-rajqKYei.js.map} +1 -1
- package/dist/web/public/assets/{memos-ZDpt1S28.js → memos-phDNldWv.js} +2 -2
- package/dist/web/public/assets/{memos-ZDpt1S28.js.map → memos-phDNldWv.js.map} +1 -1
- package/dist/web/public/assets/{messengers-B007J8et.js → messengers-DRUmbKlC.js} +2 -2
- package/dist/web/public/assets/{messengers-B007J8et.js.map → messengers-DRUmbKlC.js.map} +1 -1
- package/dist/web/public/assets/{mobile-Crm0YJ36.js → mobile-DL7Gu4eC.js} +2 -2
- package/dist/web/public/assets/{mobile-Crm0YJ36.js.map → mobile-DL7Gu4eC.js.map} +1 -1
- package/dist/web/public/assets/{network-BYbGwOEp.js → network-CYY94XAD.js} +2 -2
- package/dist/web/public/assets/{network-BYbGwOEp.js.map → network-CYY94XAD.js.map} +1 -1
- package/dist/web/public/assets/{outbox-D7xZ5y2T.js → outbox-DlK7zwpJ.js} +2 -2
- package/dist/web/public/assets/{outbox-D7xZ5y2T.js.map → outbox-DlK7zwpJ.js.map} +1 -1
- package/dist/web/public/assets/{pagination-bzSoQOAd.js → pagination-CEtERXmh.js} +2 -2
- package/dist/web/public/assets/{pagination-bzSoQOAd.js.map → pagination-CEtERXmh.js.map} +1 -1
- package/dist/web/public/assets/{persona-VT6A3VYL.js → persona-BAKPqEFa.js} +2 -2
- package/dist/web/public/assets/{persona-VT6A3VYL.js.map → persona-BAKPqEFa.js.map} +1 -1
- package/dist/web/public/assets/{plans-b4DAc_3a.js → plans-B6HmsMZO.js} +2 -2
- package/dist/web/public/assets/{plans-b4DAc_3a.js.map → plans-B6HmsMZO.js.map} +1 -1
- package/dist/web/public/assets/{play-SPYhVFeC.js → play-CcC6v05z.js} +2 -2
- package/dist/web/public/assets/{play-SPYhVFeC.js.map → play-CcC6v05z.js.map} +1 -1
- package/dist/web/public/assets/{plus-D__n9IVW.js → plus-8qdCcw3J.js} +2 -2
- package/dist/web/public/assets/{plus-D__n9IVW.js.map → plus-8qdCcw3J.js.map} +1 -1
- package/dist/web/public/assets/{policy-CWeiR9UO.js → policy-Cs-i-TJY.js} +2 -2
- package/dist/web/public/assets/{policy-CWeiR9UO.js.map → policy-Cs-i-TJY.js.map} +1 -1
- package/dist/web/public/assets/{qr-code-BqDQw03Z.js → qr-code-D37cggaF.js} +2 -2
- package/dist/web/public/assets/{qr-code-BqDQw03Z.js.map → qr-code-D37cggaF.js.map} +1 -1
- package/dist/web/public/assets/{refresh-ccw-CW2loqyz.js → refresh-ccw-BKRs7nNV.js} +2 -2
- package/dist/web/public/assets/{refresh-ccw-CW2loqyz.js.map → refresh-ccw-BKRs7nNV.js.map} +1 -1
- package/dist/web/public/assets/{reminders-BAl8NC0R.js → reminders-D4Y9FJy5.js} +2 -2
- package/dist/web/public/assets/{reminders-BAl8NC0R.js.map → reminders-D4Y9FJy5.js.map} +1 -1
- package/dist/web/public/assets/{save-DzWp78MC.js → save-BkYBfkO0.js} +2 -2
- package/dist/web/public/assets/{save-DzWp78MC.js.map → save-BkYBfkO0.js.map} +1 -1
- package/dist/web/public/assets/{schedules-6cyfjv0O.js → schedules-DFywK1ml.js} +2 -2
- package/dist/web/public/assets/{schedules-6cyfjv0O.js.map → schedules-DFywK1ml.js.map} +1 -1
- package/dist/web/public/assets/{search-Bh7gD1Aq.js → search-Dd1xSL9p.js} +2 -2
- package/dist/web/public/assets/{search-Bh7gD1Aq.js.map → search-Dd1xSL9p.js.map} +1 -1
- package/dist/web/public/assets/{search-DVWPME2t.js → search-k6ZRvy1D.js} +2 -2
- package/dist/web/public/assets/{search-DVWPME2t.js.map → search-k6ZRvy1D.js.map} +1 -1
- package/dist/web/public/assets/{security-DlZ_rDwC.js → security-DAMeDugi.js} +2 -2
- package/dist/web/public/assets/{security-DlZ_rDwC.js.map → security-DAMeDugi.js.map} +1 -1
- package/dist/web/public/assets/{service-DKkji52r.js → service-COQVoStD.js} +2 -2
- package/dist/web/public/assets/{service-DKkji52r.js.map → service-COQVoStD.js.map} +1 -1
- package/dist/web/public/assets/{shield-alert-5pYX52s1.js → shield-alert-CqDAxRHU.js} +2 -2
- package/dist/web/public/assets/{shield-alert-5pYX52s1.js.map → shield-alert-CqDAxRHU.js.map} +1 -1
- package/dist/web/public/assets/{status-badge-W8PRNBT2.js → status-badge-Dgws2vye.js} +2 -2
- package/dist/web/public/assets/{status-badge-W8PRNBT2.js.map → status-badge-Dgws2vye.js.map} +1 -1
- package/dist/web/public/assets/{subtasks-DSvOTpF0.js → subtasks-Chl5tnyq.js} +2 -2
- package/dist/web/public/assets/{subtasks-DSvOTpF0.js.map → subtasks-Chl5tnyq.js.map} +1 -1
- package/dist/web/public/assets/{table-B695Fh_p.js → table-BwEvaqRa.js} +2 -2
- package/dist/web/public/assets/{table-B695Fh_p.js.map → table-BwEvaqRa.js.map} +1 -1
- package/dist/web/public/assets/{topn-DqUC9bYh.js → topn-Bphz3dp-.js} +2 -2
- package/dist/web/public/assets/{topn-DqUC9bYh.js.map → topn-Bphz3dp-.js.map} +1 -1
- package/dist/web/public/assets/{trash-2-DH4UNSUm.js → trash-2-CqlgSEso.js} +2 -2
- package/dist/web/public/assets/{trash-2-DH4UNSUm.js.map → trash-2-CqlgSEso.js.map} +1 -1
- package/dist/web/public/assets/{use-agim-skills-BD17YQCK.js → use-agim-skills-itOpYud_.js} +2 -2
- package/dist/web/public/assets/{use-agim-skills-BD17YQCK.js.map → use-agim-skills-itOpYud_.js.map} +1 -1
- package/dist/web/public/assets/{use-background-tasks-Clyo1IGE.js → use-background-tasks-DSsDsdbz.js} +2 -2
- package/dist/web/public/assets/{use-background-tasks-Clyo1IGE.js.map → use-background-tasks-DSsDsdbz.js.map} +1 -1
- package/dist/web/public/assets/{use-memory-BGvVWIQX.js → use-memory-Bf43EjHT.js} +2 -2
- package/dist/web/public/assets/{use-memory-BGvVWIQX.js.map → use-memory-Bf43EjHT.js.map} +1 -1
- package/dist/web/public/assets/{use-observability-IlURblVq.js → use-observability-DNpZkKkn.js} +2 -2
- package/dist/web/public/assets/{use-observability-IlURblVq.js.map → use-observability-DNpZkKkn.js.map} +1 -1
- package/dist/web/public/assets/{use-settings-DFTGCq8J.js → use-settings-C10E-yWx.js} +2 -2
- package/dist/web/public/assets/{use-settings-DFTGCq8J.js.map → use-settings-C10E-yWx.js.map} +1 -1
- package/dist/web/public/assets/{use-workspace-DaXX6QdK.js → use-workspace-D48X-jjW.js} +2 -2
- package/dist/web/public/assets/{use-workspace-DaXX6QdK.js.map → use-workspace-D48X-jjW.js.map} +1 -1
- package/dist/web/public/assets/{vector-CKUwZBiK.js → vector-LvPxtapk.js} +2 -2
- package/dist/web/public/assets/{vector-CKUwZBiK.js.map → vector-LvPxtapk.js.map} +1 -1
- package/dist/web/public/assets/{viewer-edlHJiVg.js → viewer-BTSQ5Kiy.js} +2 -2
- package/dist/web/public/assets/{viewer-edlHJiVg.js.map → viewer-BTSQ5Kiy.js.map} +1 -1
- package/dist/web/public/assets/{workspace-B0jlv7Gj.js → workspace-vy7c8qMt.js} +2 -2
- package/dist/web/public/assets/{workspace-B0jlv7Gj.js.map → workspace-vy7c8qMt.js.map} +1 -1
- package/dist/web/public/assets/{workspaces-_Ho1uiD-.js → workspaces-BL_1nr0f.js} +2 -2
- package/dist/web/public/assets/{workspaces-_Ho1uiD-.js.map → workspaces-BL_1nr0f.js.map} +1 -1
- package/dist/web/public/index.html +2 -2
- package/dist/web/server.d.ts.map +1 -1
- package/dist/web/server.js +56 -37
- package/dist/web/server.js.map +1 -1
- package/package.json +1 -1
- package/dist/web/public/assets/index-DvWldAJY.js +0 -204
- package/dist/web/public/assets/index-DvWldAJY.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace-B0jlv7Gj.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 { agentDisplayName } from '@/lib/agent-label'\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\">{agentDisplayName(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\">{t('editor.truncatedBadge')}</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","agentDisplayName","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-vy7c8qMt.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 { agentDisplayName } from '@/lib/agent-label'\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\">{agentDisplayName(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\">{t('editor.truncatedBadge')}</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","agentDisplayName","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{o as S,u as y,j as e,B as u,l as C,m as j,n as F,L as T,I as E}from"./index-
|
|
1
|
+
import{o as S,u as y,j as e,B as u,l as C,m as j,n as F,L as T,I as E}from"./index-kq3mrzTq.js";import{r as f}from"./react-DlP5eolq.js";import{E as B}from"./empty-state-CJm-vEmY.js";import{T as z,a as D,b as g,c as d,d as P,e as x}from"./table-BwEvaqRa.js";import{q as R,r as H}from"./use-settings-C10E-yWx.js";import{L as v}from"./loader-circle-wNz3NK0s.js";import{R as I}from"./refresh-ccw-BKRs7nNV.js";import{B as W}from"./briefcase-D6VTVxcr.js";import{S as A}from"./save-BkYBfkO0.js";/**
|
|
2
2
|
* @license lucide-react v0.469.0 - ISC
|
|
3
3
|
*
|
|
4
4
|
* This source code is licensed under the ISC license.
|
|
5
5
|
* See the LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/const M=S("Pencil",[["path",{d:"M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z",key:"1a8usu"}],["path",{d:"m15 5 4 4",key:"1mk7zo"}]]),N={id:"",name:"",agents:"",members:"",rate:"",intervalSec:"",burst:""};function O(s){return{id:s.id,name:s.name,agents:s.agents.join(", "),members:(s.members??[]).join(", "),rate:s.rateLimit?String(s.rateLimit.rate):"",intervalSec:s.rateLimit?String(s.rateLimit.intervalSec):"",burst:s.rateLimit?String(s.rateLimit.burst):""}}function _(s){const n=o=>o.split(",").map(p=>p.trim()).filter(Boolean),m={id:s.id.trim(),name:s.name.trim()||s.id.trim(),agents:n(s.agents),members:n(s.members)},t=Number(s.rate),i=Number(s.intervalSec),l=Number(s.burst);return Number.isFinite(t)&&t>0&&Number.isFinite(i)&&i>0&&Number.isFinite(l)&&l>0&&(m.rateLimit={rate:t,intervalSec:i,burst:l}),m}function Z(){const{t:s}=y(["settings","common"]),n=R(),m=H(),[t,i]=f.useState(N),[l,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(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")}),l&&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(c,{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(c,{id:"ws-name",label:s("workspaces.form.name"),value:t.name,onChange:a=>i(r=>({...r,name:a})),placeholder:"Team Foo"}),e.jsx(c,{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(c,{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(c,{id:"ws-rate",label:s("workspaces.form.rate"),value:t.rate,onChange:a=>i(r=>({...r,rate:a})),placeholder:"10",mono:!0}),e.jsx(c,{id:"ws-ivl",label:s("workspaces.form.intervalSec"),value:t.intervalSec,onChange:a=>i(r=>({...r,intervalSec:a})),placeholder:"60",mono:!0}),e.jsx(c,{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 c({id:s,label:n,hint:m,value:t,onChange:i,placeholder:l,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:l,className:o?"font-mono text-xs":""}),m&&e.jsx("span",{className:"text-[10px] text-text-muted",children:m})]})}export{Z as default};
|
|
7
|
-
//# sourceMappingURL=workspaces-
|
|
7
|
+
//# sourceMappingURL=workspaces-BL_1nr0f.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspaces-_Ho1uiD-.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-BL_1nr0f.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]}
|
|
@@ -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-
|
|
38
|
+
<script type="module" crossorigin src="/assets/index-kq3mrzTq.js"></script>
|
|
39
39
|
<link rel="modulepreload" crossorigin href="/assets/react-DlP5eolq.js">
|
|
40
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
40
|
+
<link rel="stylesheet" crossorigin href="/assets/index-DknVjPYB.css">
|
|
41
41
|
</head>
|
|
42
42
|
<body>
|
|
43
43
|
<div id="root"></div>
|
package/dist/web/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/web/server.ts"],"names":[],"mappings":"AAEA,OAAO,EAAgB,KAAK,eAAe,EAAuB,MAAM,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/web/server.ts"],"names":[],"mappings":"AAEA,OAAO,EAAgB,KAAK,eAAe,EAAuB,MAAM,WAAW,CAAA;AAmGnF,iBAAS,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAO/D;AAwBD,iBAAS,qBAAqB,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAQ5D;AAyID;;;;;;;;;;;;;0EAa0E;AAC1E,iBAAS,cAAc,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAcrD;AAaD,eAAO,MAAM,mBAAmB;;;;wBAIV,cAAc,yBAAyB,CAAC,GAAG,IAAI,GAAG,IAAI;CAG3E,CAAA;AAED,wBAAgB,iBAAiB,IAAI,CAAC,EAAE,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,IAAI,CAOrE;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE;IAC5C,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,YAAY,EAAE,MAAM,CAAA;CACrB,GAAG,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAwwC/C"}
|
package/dist/web/server.js
CHANGED
|
@@ -25,11 +25,13 @@ const webLog = rootLogger.child({ component: 'web' });
|
|
|
25
25
|
/**
|
|
26
26
|
* Module-level reference to the button-callback handler that approval-router
|
|
27
27
|
* registers on our synthetic web messenger. The WS message switch dispatches
|
|
28
|
-
* `approval-action` events through
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
28
|
+
* `approval-action` AND `choice-action` events through it: approval-router is
|
|
29
|
+
* the unified button dispatcher and already routes both `apv:` (approvals)
|
|
30
|
+
* and `ask:` (ask_user choice cards) callbacks, so a single handler covers
|
|
31
|
+
* both card types — same path as a Telegram inline-button tap. Set by the web
|
|
32
|
+
* messenger's `onButtonCallback`; remains undefined until approval-router
|
|
33
|
+
* installs (which happens before this file's exported startWebServer is
|
|
34
|
+
* called from cli.ts).
|
|
33
35
|
*/
|
|
34
36
|
let webButtonHandler;
|
|
35
37
|
import { formatAgentNotAvailableError, isAgentAvailableCached, loadConfig, saveConfig, } from '../core/onboarding.js';
|
|
@@ -1406,51 +1408,51 @@ export async function startWebServer(options) {
|
|
|
1406
1408
|
// the run was dead and the user's card UI had been stuck at
|
|
1407
1409
|
// "已发送,等待结果…" the whole time.
|
|
1408
1410
|
//
|
|
1409
|
-
// Fix: parse the frame inline first; if it's an `approval-action
|
|
1410
|
-
// dispatch directly to the click
|
|
1411
|
-
// are the agent's UNBLOCKING input — they have no
|
|
1412
|
-
// on chat input. All other types (chat, switch-agent,
|
|
1413
|
-
// …) still go through enqueueInbound to preserve their
|
|
1414
|
-
// FIFO ordering.
|
|
1415
|
-
async function
|
|
1411
|
+
// Fix: parse the frame inline first; if it's an `approval-action` (or a
|
|
1412
|
+
// `choice-action` from an ask_user card), dispatch directly to the click
|
|
1413
|
+
// handler (no queue). Both are the agent's UNBLOCKING input — they have no
|
|
1414
|
+
// ordering dependency on chat input. All other types (chat, switch-agent,
|
|
1415
|
+
// get-history, …) still go through enqueueInbound to preserve their
|
|
1416
|
+
// per-client FIFO ordering.
|
|
1417
|
+
async function handleButtonAction(msg) {
|
|
1416
1418
|
const actionData = String(msg.data || '');
|
|
1417
1419
|
const messageId = String(msg.messageId || '');
|
|
1418
1420
|
// Use `client.id` (mutable; updated on session rekey) — not the
|
|
1419
1421
|
// original `clientId` const — so the threadId we hand to the bus
|
|
1420
|
-
// matches the threadId the bus stamped on the pending
|
|
1421
|
-
//
|
|
1422
|
-
//
|
|
1423
|
-
// open req to resolve.
|
|
1422
|
+
// matches the threadId the bus stamped on the pending request.
|
|
1423
|
+
// Without this, a session that was rekeyed via `get-history` reports
|
|
1424
|
+
// the stale id and the bus can't find the open req to resolve.
|
|
1424
1425
|
const liveId = client.id;
|
|
1425
1426
|
webLog.info({
|
|
1426
|
-
event: '
|
|
1427
|
+
event: 'button.web.click_received',
|
|
1427
1428
|
clientId: liveId, data: actionData, messageId,
|
|
1428
1429
|
handlerBound: !!webButtonHandler,
|
|
1429
1430
|
});
|
|
1430
1431
|
if (!actionData || !messageId) {
|
|
1431
|
-
sendToClient(ws, { type: 'error', message: '
|
|
1432
|
+
sendToClient(ws, { type: 'error', message: 'button action missing data/messageId' });
|
|
1432
1433
|
return;
|
|
1433
1434
|
}
|
|
1434
1435
|
if (!webButtonHandler) {
|
|
1435
|
-
const why = '
|
|
1436
|
-
webLog.warn({ event: '
|
|
1436
|
+
const why = 'button handler not bound (router not installed?). Restart agim to rebind.';
|
|
1437
|
+
webLog.warn({ event: 'button.web.no_handler', clientId: liveId, data: actionData, messageId }, why);
|
|
1437
1438
|
sendToClient(ws, { type: 'error', message: why });
|
|
1438
1439
|
return;
|
|
1439
1440
|
}
|
|
1440
1441
|
try {
|
|
1441
1442
|
// Web client doesn't have a platform-native toast, so ack is a
|
|
1442
1443
|
// no-op resolving to the in-page status the page itself chose
|
|
1443
|
-
// to render after click.
|
|
1444
|
+
// to render after click. approval-router dispatches both `apv:`
|
|
1445
|
+
// and `ask:` data, so one handler serves approvals and asks.
|
|
1444
1446
|
await webButtonHandler({
|
|
1445
1447
|
data: actionData, threadId: liveId, userId: `web:${liveId}`,
|
|
1446
1448
|
userDisplay: 'Web', messageId, ack: async () => { },
|
|
1447
1449
|
});
|
|
1448
|
-
webLog.info({ event: '
|
|
1450
|
+
webLog.info({ event: 'button.web.click_resolved', clientId: liveId, data: actionData });
|
|
1449
1451
|
}
|
|
1450
1452
|
catch (err) {
|
|
1451
1453
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
1452
|
-
webLog.error({ event: '
|
|
1453
|
-
sendToClient(ws, { type: 'error', message: `
|
|
1454
|
+
webLog.error({ event: 'button.web.click_failed', clientId: liveId, data: actionData, err: errMsg });
|
|
1455
|
+
sendToClient(ws, { type: 'error', message: `Button click failed: ${errMsg}` });
|
|
1454
1456
|
}
|
|
1455
1457
|
}
|
|
1456
1458
|
ws.on('message', (data) => {
|
|
@@ -1466,10 +1468,11 @@ export async function startWebServer(options) {
|
|
|
1466
1468
|
sendToClient(ws, { type: 'error', message: 'Invalid message format' });
|
|
1467
1469
|
return;
|
|
1468
1470
|
}
|
|
1469
|
-
|
|
1471
|
+
const msgType = msg && typeof msg === 'object' ? msg.type : undefined;
|
|
1472
|
+
if (msgType === 'approval-action' || msgType === 'choice-action') {
|
|
1470
1473
|
// Fast path — bypass enqueueInbound. Errors are surfaced to the
|
|
1471
|
-
// client inside
|
|
1472
|
-
void
|
|
1474
|
+
// client inside handleButtonAction.
|
|
1475
|
+
void handleButtonAction(msg);
|
|
1473
1476
|
return;
|
|
1474
1477
|
}
|
|
1475
1478
|
enqueueInbound(async () => {
|
|
@@ -1577,32 +1580,48 @@ export async function startWebServer(options) {
|
|
|
1577
1580
|
return;
|
|
1578
1581
|
sendToClient(c.ws, { type: 'approval-card-edit', messageId, outcome });
|
|
1579
1582
|
},
|
|
1583
|
+
async sendChoiceCard(threadId, prompt) {
|
|
1584
|
+
const c = clients.get(threadId);
|
|
1585
|
+
const messageId = `web-card-${++cardSeq}-${Date.now().toString(36)}`;
|
|
1586
|
+
if (c && c.ws.readyState === c.ws.OPEN) {
|
|
1587
|
+
sendToClient(c.ws, { type: 'choice-card', messageId, prompt });
|
|
1588
|
+
}
|
|
1589
|
+
return { messageId };
|
|
1590
|
+
},
|
|
1591
|
+
async editChoiceCard(threadId, messageId, finalText) {
|
|
1592
|
+
const c = clients.get(threadId);
|
|
1593
|
+
if (!c || c.ws.readyState !== c.ws.OPEN)
|
|
1594
|
+
return;
|
|
1595
|
+
sendToClient(c.ws, { type: 'choice-card-edit', messageId, finalText });
|
|
1596
|
+
},
|
|
1580
1597
|
onButtonCallback(handler) {
|
|
1581
1598
|
webButtonHandler = handler;
|
|
1582
|
-
webLog.info({ event: '
|
|
1599
|
+
webLog.info({ event: 'button.web.handler_bound' }, 'web messenger button-callback handler attached');
|
|
1583
1600
|
},
|
|
1584
1601
|
};
|
|
1585
1602
|
registry.registerMessenger(webMessenger);
|
|
1586
1603
|
// approval-router's install() loop bound buttonCallback only for messengers
|
|
1587
1604
|
// registered BEFORE install. Our web messenger was just registered (after
|
|
1588
|
-
// install), so we
|
|
1589
|
-
// card clicks fire WS
|
|
1590
|
-
//
|
|
1591
|
-
//
|
|
1592
|
-
//
|
|
1605
|
+
// install), so we wire it ourselves — otherwise in-page approval/choice
|
|
1606
|
+
// card clicks fire WS messages with no handler on the server side and
|
|
1607
|
+
// silently do nothing. approval-router is the unified dispatcher: it routes
|
|
1608
|
+
// `apv:` (approvals) and `ask:` (ask_user) callbacks alike, so this single
|
|
1609
|
+
// binding covers both card types. bindButtonHandlerForPlatform is a no-op
|
|
1610
|
+
// if approval-router hasn't been install()'d yet (e.g. degraded mode where
|
|
1611
|
+
// the bus failed to start).
|
|
1593
1612
|
try {
|
|
1594
1613
|
const { bindButtonHandlerForPlatform } = await import('../core/approval-router.js');
|
|
1595
1614
|
bindButtonHandlerForPlatform('web');
|
|
1596
1615
|
if (!webButtonHandler) {
|
|
1597
1616
|
// bindButtonHandlerForPlatform is a silent no-op when `installed` is
|
|
1598
1617
|
// null on the router (bus failed to start, or cli skipped install).
|
|
1599
|
-
// Log so an operator who's confused why
|
|
1600
|
-
//
|
|
1601
|
-
webLog.warn({ event: '
|
|
1618
|
+
// Log so an operator who's confused why card clicks don't work sees
|
|
1619
|
+
// a clear breadcrumb at startup.
|
|
1620
|
+
webLog.warn({ event: 'button.web.bind_skipped' }, 'approval-router not installed — web card clicks will fail until restart');
|
|
1602
1621
|
}
|
|
1603
1622
|
}
|
|
1604
1623
|
catch (err) {
|
|
1605
|
-
webLog.warn({ event: '
|
|
1624
|
+
webLog.warn({ event: 'button.web.bind_error', err: err instanceof Error ? err.message : String(err) }, 'web button-handler binding threw');
|
|
1606
1625
|
}
|
|
1607
1626
|
// PR-C: periodic metrics tick. Publishes a per-agent snapshot every 5s
|
|
1608
1627
|
// so the dashboard's Health sparkline can advance even when there are
|