agim-cli 1.2.139 → 1.2.140
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 +44 -0
- package/dist/core/live-config.d.ts.map +1 -1
- package/dist/core/live-config.js +8 -0
- package/dist/core/live-config.js.map +1 -1
- package/dist/web/public/assets/{a2a-COCxV3Cc.js → a2a-DczMMkbl.js} +2 -2
- package/dist/web/public/assets/{a2a-COCxV3Cc.js.map → a2a-DczMMkbl.js.map} +1 -1
- package/dist/web/public/assets/{activity-lssr7XnJ.js → activity-cbLHkzca.js} +2 -2
- package/dist/web/public/assets/{activity-lssr7XnJ.js.map → activity-cbLHkzca.js.map} +1 -1
- package/dist/web/public/assets/{admins-DSW8IUl9.js → admins-C-YsGMj7.js} +2 -2
- package/dist/web/public/assets/{admins-DSW8IUl9.js.map → admins-C-YsGMj7.js.map} +1 -1
- package/dist/web/public/assets/agents-BWfov_1-.js +7 -0
- package/dist/web/public/assets/agents-BWfov_1-.js.map +1 -0
- package/dist/web/public/assets/{approvals-ClzA_Sl1.js → approvals-HSssmXKS.js} +2 -2
- package/dist/web/public/assets/{approvals-ClzA_Sl1.js.map → approvals-HSssmXKS.js.map} +1 -1
- package/dist/web/public/assets/{arrow-down-B6b32UC0.js → arrow-down-BXvC8Al2.js} +2 -2
- package/dist/web/public/assets/{arrow-down-B6b32UC0.js.map → arrow-down-BXvC8Al2.js.map} +1 -1
- package/dist/web/public/assets/{arrow-up-B-nP5kYY.js → arrow-up-63xELY5Q.js} +2 -2
- package/dist/web/public/assets/{arrow-up-B-nP5kYY.js.map → arrow-up-63xELY5Q.js.map} +1 -1
- package/dist/web/public/assets/{asks-DyXhQTTK.js → asks-COLEFOvK.js} +2 -2
- package/dist/web/public/assets/{asks-DyXhQTTK.js.map → asks-COLEFOvK.js.map} +1 -1
- package/dist/web/public/assets/{audit-BI7SCouF.js → audit-D4ZEiZub.js} +2 -2
- package/dist/web/public/assets/{audit-BI7SCouF.js.map → audit-D4ZEiZub.js.map} +1 -1
- package/dist/web/public/assets/{bell-B3N02OLo.js → bell-Cg2Bvv06.js} +2 -2
- package/dist/web/public/assets/{bell-B3N02OLo.js.map → bell-Cg2Bvv06.js.map} +1 -1
- package/dist/web/public/assets/{bgjobs-CicZiNip.js → bgjobs-CEjCzwtd.js} +2 -2
- package/dist/web/public/assets/{bgjobs-CicZiNip.js.map → bgjobs-CEjCzwtd.js.map} +1 -1
- package/dist/web/public/assets/{brain-C6ukvGuf.js → brain-euvl6F6C.js} +2 -2
- package/dist/web/public/assets/{brain-C6ukvGuf.js.map → brain-euvl6F6C.js.map} +1 -1
- package/dist/web/public/assets/{briefcase-hn1X3B5h.js → briefcase-DPWLbCnA.js} +2 -2
- package/dist/web/public/assets/{briefcase-hn1X3B5h.js.map → briefcase-DPWLbCnA.js.map} +1 -1
- package/dist/web/public/assets/{chat-BDQj16Vz.js → chat-Dz9kfaxH.js} +2 -2
- package/dist/web/public/assets/{chat-BDQj16Vz.js.map → chat-Dz9kfaxH.js.map} +1 -1
- package/dist/web/public/assets/{chevron-left-RxNVjq1Y.js → chevron-left-BeIh5thq.js} +2 -2
- package/dist/web/public/assets/{chevron-left-RxNVjq1Y.js.map → chevron-left-BeIh5thq.js.map} +1 -1
- package/dist/web/public/assets/{chevron-right-BUp0DVnl.js → chevron-right-uP_l9MMb.js} +2 -2
- package/dist/web/public/assets/{chevron-right-BUp0DVnl.js.map → chevron-right-uP_l9MMb.js.map} +1 -1
- package/dist/web/public/assets/{circle-check-Bm4WZ21u.js → circle-check-CewnjFgv.js} +2 -2
- package/dist/web/public/assets/{circle-check-Bm4WZ21u.js.map → circle-check-CewnjFgv.js.map} +1 -1
- package/dist/web/public/assets/{circle-check-big-BIrgy-jJ.js → circle-check-big-C2RTc48c.js} +2 -2
- package/dist/web/public/assets/{circle-check-big-BIrgy-jJ.js.map → circle-check-big-C2RTc48c.js.map} +1 -1
- package/dist/web/public/assets/{circle-x-D3x0-M6I.js → circle-x-Ccg1HyV-.js} +2 -2
- package/dist/web/public/assets/{circle-x-D3x0-M6I.js.map → circle-x-Ccg1HyV-.js.map} +1 -1
- package/dist/web/public/assets/{clock-DS7DQ1w2.js → clock-qxbYSynv.js} +2 -2
- package/dist/web/public/assets/{clock-DS7DQ1w2.js.map → clock-qxbYSynv.js.map} +1 -1
- package/dist/web/public/assets/{confirm-dialog-C68_XUSd.js → confirm-dialog-DmJq4Td9.js} +2 -2
- package/dist/web/public/assets/{confirm-dialog-C68_XUSd.js.map → confirm-dialog-DmJq4Td9.js.map} +1 -1
- package/dist/web/public/assets/{copy-Bzjhsg1p.js → copy-DxSHRdbc.js} +2 -2
- package/dist/web/public/assets/{copy-Bzjhsg1p.js.map → copy-DxSHRdbc.js.map} +1 -1
- package/dist/web/public/assets/{data-table-DEXi61Ud.js → data-table-S7rIjwdO.js} +2 -2
- package/dist/web/public/assets/{data-table-DEXi61Ud.js.map → data-table-S7rIjwdO.js.map} +1 -1
- package/dist/web/public/assets/{dialog-gd2B3olO.js → dialog-bAIDaO-6.js} +2 -2
- package/dist/web/public/assets/{dialog-gd2B3olO.js.map → dialog-bAIDaO-6.js.map} +1 -1
- package/dist/web/public/assets/{download-DCcaTdc6.js → download-OhsGtnO-.js} +2 -2
- package/dist/web/public/assets/{download-DCcaTdc6.js.map → download-OhsGtnO-.js.map} +1 -1
- package/dist/web/public/assets/{email-Bp4hAFCc.js → email-C1-HxWLF.js} +2 -2
- package/dist/web/public/assets/{email-Bp4hAFCc.js.map → email-C1-HxWLF.js.map} +1 -1
- package/dist/web/public/assets/{empty-state-Bzkp-ADK.js → empty-state-C-qjOHyu.js} +2 -2
- package/dist/web/public/assets/{empty-state-Bzkp-ADK.js.map → empty-state-C-qjOHyu.js.map} +1 -1
- package/dist/web/public/assets/{external-link-Deer-Lh8.js → external-link-DRVp9-lb.js} +2 -2
- package/dist/web/public/assets/{external-link-Deer-Lh8.js.map → external-link-DRVp9-lb.js.map} +1 -1
- package/dist/web/public/assets/{eye-aJAK4qYW.js → eye-CFhg5BTa.js} +2 -2
- package/dist/web/public/assets/{eye-aJAK4qYW.js.map → eye-CFhg5BTa.js.map} +1 -1
- package/dist/web/public/assets/{facts-CX8Slc_J.js → facts-CGaLWhzi.js} +2 -2
- package/dist/web/public/assets/{facts-CX8Slc_J.js.map → facts-CGaLWhzi.js.map} +1 -1
- package/dist/web/public/assets/{goals-CoO5qR3l.js → goals-C-dJANmn.js} +2 -2
- package/dist/web/public/assets/{goals-CoO5qR3l.js.map → goals-C-dJANmn.js.map} +1 -1
- package/dist/web/public/assets/{health-QRL6KLLZ.js → health-CWcti5h3.js} +2 -2
- package/dist/web/public/assets/{health-QRL6KLLZ.js.map → health-CWcti5h3.js.map} +1 -1
- package/dist/web/public/assets/{heart-pulse-DTuy4Tma.js → heart-pulse-DmGhKR2W.js} +2 -2
- package/dist/web/public/assets/{heart-pulse-DTuy4Tma.js.map → heart-pulse-DmGhKR2W.js.map} +1 -1
- package/dist/web/public/assets/{heartbeat-BLutWB3t.js → heartbeat-kLoGBNCo.js} +2 -2
- package/dist/web/public/assets/{heartbeat-BLutWB3t.js.map → heartbeat-kLoGBNCo.js.map} +1 -1
- package/dist/web/public/assets/{hot-B96_1Z-G.js → hot-BITDoax1.js} +2 -2
- package/dist/web/public/assets/{hot-B96_1Z-G.js.map → hot-BITDoax1.js.map} +1 -1
- package/dist/web/public/assets/{index-wVLKSZpI.js → index-O0BQoyzo.js} +22 -22
- package/dist/web/public/assets/{index-wVLKSZpI.js.map → index-O0BQoyzo.js.map} +1 -1
- package/dist/web/public/assets/{installed-5bpbtNzF.js → installed-Co9WrtQ7.js} +2 -2
- package/dist/web/public/assets/{installed-5bpbtNzF.js.map → installed-Co9WrtQ7.js.map} +1 -1
- package/dist/web/public/assets/{jobs-CPLjCaI5.js → jobs-hdHhBEvi.js} +2 -2
- package/dist/web/public/assets/{jobs-CPLjCaI5.js.map → jobs-hdHhBEvi.js.map} +1 -1
- package/dist/web/public/assets/{layout-Cdi09rZy.js → layout-BMXC1Uh1.js} +2 -2
- package/dist/web/public/assets/{layout-Cdi09rZy.js.map → layout-BMXC1Uh1.js.map} +1 -1
- package/dist/web/public/assets/{layout-CN-hNeUX.js → layout-CQtbOBag.js} +2 -2
- package/dist/web/public/assets/{layout-CN-hNeUX.js.map → layout-CQtbOBag.js.map} +1 -1
- package/dist/web/public/assets/{layout-nYKX1VY4.js → layout-CyBGneZ9.js} +2 -2
- package/dist/web/public/assets/{layout-nYKX1VY4.js.map → layout-CyBGneZ9.js.map} +1 -1
- package/dist/web/public/assets/{layout-DOvroJYL.js → layout-CysVsySh.js} +2 -2
- package/dist/web/public/assets/{layout-DOvroJYL.js.map → layout-CysVsySh.js.map} +1 -1
- package/dist/web/public/assets/{layout-K_oWND8q.js → layout-bDMXIKIR.js} +2 -2
- package/dist/web/public/assets/{layout-K_oWND8q.js.map → layout-bDMXIKIR.js.map} +1 -1
- package/dist/web/public/assets/{llm-BDW-Xpvk.js → llm-CPIRNQU2.js} +2 -2
- package/dist/web/public/assets/{llm-BDW-Xpvk.js.map → llm-CPIRNQU2.js.map} +1 -1
- package/dist/web/public/assets/{loader-circle-DPc6botW.js → loader-circle-9VUMGitw.js} +2 -2
- package/dist/web/public/assets/{loader-circle-DPc6botW.js.map → loader-circle-9VUMGitw.js.map} +1 -1
- package/dist/web/public/assets/{map-pin-ECh-25H3.js → map-pin-BXYvvHry.js} +2 -2
- package/dist/web/public/assets/{map-pin-ECh-25H3.js.map → map-pin-BXYvvHry.js.map} +1 -1
- package/dist/web/public/assets/{mcp-BV-k5VFD.js → mcp-BgLdlwSn.js} +2 -2
- package/dist/web/public/assets/{mcp-BV-k5VFD.js.map → mcp-BgLdlwSn.js.map} +1 -1
- package/dist/web/public/assets/{memos-BTsHVXah.js → memos-CfneX9DH.js} +2 -2
- package/dist/web/public/assets/{memos-BTsHVXah.js.map → memos-CfneX9DH.js.map} +1 -1
- package/dist/web/public/assets/{messengers-fyjNQwDX.js → messengers-7Phqea62.js} +2 -2
- package/dist/web/public/assets/{messengers-fyjNQwDX.js.map → messengers-7Phqea62.js.map} +1 -1
- package/dist/web/public/assets/{mobile-CeRuQZ-W.js → mobile-CV5b6D2W.js} +2 -2
- package/dist/web/public/assets/{mobile-CeRuQZ-W.js.map → mobile-CV5b6D2W.js.map} +1 -1
- package/dist/web/public/assets/{native-agent-Cg-Sab4t.js → native-agent-QvIa6LjE.js} +2 -2
- package/dist/web/public/assets/{native-agent-Cg-Sab4t.js.map → native-agent-QvIa6LjE.js.map} +1 -1
- package/dist/web/public/assets/{network-FK3KNI1a.js → network-BXhEjGhE.js} +2 -2
- package/dist/web/public/assets/{network-FK3KNI1a.js.map → network-BXhEjGhE.js.map} +1 -1
- package/dist/web/public/assets/{outbox-DhlxB8l0.js → outbox-DHQL7TQb.js} +2 -2
- package/dist/web/public/assets/{outbox-DhlxB8l0.js.map → outbox-DHQL7TQb.js.map} +1 -1
- package/dist/web/public/assets/{pagination-DzNnFLlI.js → pagination-VKuPb1Ot.js} +2 -2
- package/dist/web/public/assets/{pagination-DzNnFLlI.js.map → pagination-VKuPb1Ot.js.map} +1 -1
- package/dist/web/public/assets/{persona-B_EkauQB.js → persona-CWug2GLR.js} +2 -2
- package/dist/web/public/assets/{persona-B_EkauQB.js.map → persona-CWug2GLR.js.map} +1 -1
- package/dist/web/public/assets/{plans-D4aczyt4.js → plans-CZoEs5SY.js} +2 -2
- package/dist/web/public/assets/{plans-D4aczyt4.js.map → plans-CZoEs5SY.js.map} +1 -1
- package/dist/web/public/assets/{play-BdTIFReu.js → play-CfSn5Vdl.js} +2 -2
- package/dist/web/public/assets/{play-BdTIFReu.js.map → play-CfSn5Vdl.js.map} +1 -1
- package/dist/web/public/assets/{plus-CjK8x5Ta.js → plus-Z8l4CiqJ.js} +2 -2
- package/dist/web/public/assets/{plus-CjK8x5Ta.js.map → plus-Z8l4CiqJ.js.map} +1 -1
- package/dist/web/public/assets/{policy-D7vnZQeQ.js → policy-CutDSEPW.js} +2 -2
- package/dist/web/public/assets/{policy-D7vnZQeQ.js.map → policy-CutDSEPW.js.map} +1 -1
- package/dist/web/public/assets/{qr-code-CpmzdYE3.js → qr-code-DgU5aiM6.js} +2 -2
- package/dist/web/public/assets/{qr-code-CpmzdYE3.js.map → qr-code-DgU5aiM6.js.map} +1 -1
- package/dist/web/public/assets/{refresh-ccw-C2Q06LWj.js → refresh-ccw-D2CWiyU_.js} +2 -2
- package/dist/web/public/assets/{refresh-ccw-C2Q06LWj.js.map → refresh-ccw-D2CWiyU_.js.map} +1 -1
- package/dist/web/public/assets/{reminders-BW3VfYCD.js → reminders-Cb6Izedg.js} +2 -2
- package/dist/web/public/assets/{reminders-BW3VfYCD.js.map → reminders-Cb6Izedg.js.map} +1 -1
- package/dist/web/public/assets/{save-3XXDgdfD.js → save-DB0BDYTs.js} +2 -2
- package/dist/web/public/assets/{save-3XXDgdfD.js.map → save-DB0BDYTs.js.map} +1 -1
- package/dist/web/public/assets/{schedules-Bg5oBl6i.js → schedules-8mSjE14D.js} +2 -2
- package/dist/web/public/assets/{schedules-Bg5oBl6i.js.map → schedules-8mSjE14D.js.map} +1 -1
- package/dist/web/public/assets/{search-oQrctQNZ.js → search-B4fHilZ0.js} +2 -2
- package/dist/web/public/assets/{search-oQrctQNZ.js.map → search-B4fHilZ0.js.map} +1 -1
- package/dist/web/public/assets/{search-BjW_KGfJ.js → search-Con69NhG.js} +2 -2
- package/dist/web/public/assets/{search-BjW_KGfJ.js.map → search-Con69NhG.js.map} +1 -1
- package/dist/web/public/assets/{security-ongR2bsH.js → security-BTe3zUg8.js} +2 -2
- package/dist/web/public/assets/{security-ongR2bsH.js.map → security-BTe3zUg8.js.map} +1 -1
- package/dist/web/public/assets/{service-CNTTFkAV.js → service-C7SqcwfL.js} +2 -2
- package/dist/web/public/assets/{service-CNTTFkAV.js.map → service-C7SqcwfL.js.map} +1 -1
- package/dist/web/public/assets/{shield-alert-DW0flso9.js → shield-alert-CKFVsGgI.js} +2 -2
- package/dist/web/public/assets/{shield-alert-DW0flso9.js.map → shield-alert-CKFVsGgI.js.map} +1 -1
- package/dist/web/public/assets/{status-badge-CnqMu-7R.js → status-badge-BSkpyN4D.js} +2 -2
- package/dist/web/public/assets/{status-badge-CnqMu-7R.js.map → status-badge-BSkpyN4D.js.map} +1 -1
- package/dist/web/public/assets/{subtasks-ChtI7jpP.js → subtasks-Bel-I1Sk.js} +2 -2
- package/dist/web/public/assets/{subtasks-ChtI7jpP.js.map → subtasks-Bel-I1Sk.js.map} +1 -1
- package/dist/web/public/assets/{table-DVQPjWsi.js → table-CPn1MRcy.js} +2 -2
- package/dist/web/public/assets/{table-DVQPjWsi.js.map → table-CPn1MRcy.js.map} +1 -1
- package/dist/web/public/assets/{topn-Cs-wP6Tf.js → topn-Ba3RjcK1.js} +2 -2
- package/dist/web/public/assets/{topn-Cs-wP6Tf.js.map → topn-Ba3RjcK1.js.map} +1 -1
- package/dist/web/public/assets/{trash-2-BXhBc6TL.js → trash-2-Dfov8aHD.js} +2 -2
- package/dist/web/public/assets/{trash-2-BXhBc6TL.js.map → trash-2-Dfov8aHD.js.map} +1 -1
- package/dist/web/public/assets/{use-background-tasks-DWe_iYgk.js → use-background-tasks-BQrEeUwY.js} +2 -2
- package/dist/web/public/assets/{use-background-tasks-DWe_iYgk.js.map → use-background-tasks-BQrEeUwY.js.map} +1 -1
- package/dist/web/public/assets/{use-llm-admin-SE-2iSIe.js → use-llm-admin-DYekqogG.js} +2 -2
- package/dist/web/public/assets/{use-llm-admin-SE-2iSIe.js.map → use-llm-admin-DYekqogG.js.map} +1 -1
- package/dist/web/public/assets/{use-memory-tCgpqYla.js → use-memory-DbJ4pP2Z.js} +2 -2
- package/dist/web/public/assets/{use-memory-tCgpqYla.js.map → use-memory-DbJ4pP2Z.js.map} +1 -1
- package/dist/web/public/assets/{use-observability-CMUSiy5z.js → use-observability-C2M6WZ9W.js} +2 -2
- package/dist/web/public/assets/{use-observability-CMUSiy5z.js.map → use-observability-C2M6WZ9W.js.map} +1 -1
- package/dist/web/public/assets/{use-settings-BUM0BmtA.js → use-settings-DMdaoWsB.js} +2 -2
- package/dist/web/public/assets/{use-settings-BUM0BmtA.js.map → use-settings-DMdaoWsB.js.map} +1 -1
- package/dist/web/public/assets/{use-workspace-BOUxEbaB.js → use-workspace-BHG7h3jQ.js} +2 -2
- package/dist/web/public/assets/{use-workspace-BOUxEbaB.js.map → use-workspace-BHG7h3jQ.js.map} +1 -1
- package/dist/web/public/assets/{useQuery-II-YwTUw.js → useQuery-PdiC7-sY.js} +2 -2
- package/dist/web/public/assets/{useQuery-II-YwTUw.js.map → useQuery-PdiC7-sY.js.map} +1 -1
- package/dist/web/public/assets/{vector-C83bHtEW.js → vector-DnZM3OXU.js} +2 -2
- package/dist/web/public/assets/{vector-C83bHtEW.js.map → vector-DnZM3OXU.js.map} +1 -1
- package/dist/web/public/assets/{viewer-rMDo9pIb.js → viewer-Dz6k0YKp.js} +2 -2
- package/dist/web/public/assets/{viewer-rMDo9pIb.js.map → viewer-Dz6k0YKp.js.map} +1 -1
- package/dist/web/public/assets/{workspace-tVixiuWJ.js → workspace-BnXrWS3j.js} +2 -2
- package/dist/web/public/assets/{workspace-tVixiuWJ.js.map → workspace-BnXrWS3j.js.map} +1 -1
- package/dist/web/public/assets/{workspaces-CsLKDstm.js → workspaces-CSS_UBEi.js} +2 -2
- package/dist/web/public/assets/{workspaces-CsLKDstm.js.map → workspaces-CSS_UBEi.js.map} +1 -1
- package/dist/web/public/assets/{x-B0lqsHGi.js → x-DG-JKVw_.js} +2 -2
- package/dist/web/public/assets/{x-B0lqsHGi.js.map → x-DG-JKVw_.js.map} +1 -1
- package/dist/web/public/index.html +1 -1
- package/package.json +1 -1
- package/dist/web/public/assets/agents-0IMyFVui.js +0 -7
- package/dist/web/public/assets/agents-0IMyFVui.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"approvals-ClzA_Sl1.js","sources":["../../src/components/approvals/approval-card.tsx","../../src/hooks/use-approvals.ts","../../src/routes/approvals.tsx"],"sourcesContent":["/**\n * ApprovalCard — one HITL approval, rendered as a card.\n *\n * The /approvals route uses cards (not table rows) on purpose:\n * * approvals are time-sensitive and few — visual urgency matters\n * * each row's `input` is a free-form record that's hard to squeeze\n * into a single cell. The card body has room for a JSON preview.\n * * Allow / Deny are 1-click decisions; we want big tap targets\n * even on desktop, which doesn't fit cleanly into a TableCell.\n *\n * Layout:\n * ┌─ tool name · platform · age ─────────────────┐\n * │ thread id (monospaced) │\n * │ │\n * │ <pre>input json (truncated to ~12 lines)</pre>│\n * │ │\n * │ [ Deny ] [ Allow ] │\n * └──────────────────────────────────────────────┘\n *\n * The buttons are disabled while a resolution is in flight (so a\n * second click can't double-fire). Async failure surfaces via the\n * parent's toast; the card stays put.\n */\n\nimport { useTranslation } from 'react-i18next'\nimport { Check, X, AlertTriangle } from 'lucide-react'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { cn } from '@/lib/utils'\nimport type { Approval, ApprovalBehavior } from '@/types/api'\n\nexport interface ApprovalCardProps {\n approval: Approval\n /** Async resolve callback. The card calls this with the behavior\n * + the current reqId; parent handles toast + mutation state.\n * The card disables its buttons while the promise is in flight. */\n onResolve: (behavior: ApprovalBehavior) => Promise<void> | void\n /** When true, both action buttons are disabled. Parent flips this\n * on while its mutation is in flight. */\n busy?: boolean\n /** Optional className for the outer card. */\n className?: string\n}\n\nexport function ApprovalCard({\n approval,\n onResolve,\n busy = false,\n className,\n}: ApprovalCardProps): JSX.Element {\n const { t } = useTranslation('approvals')\n\n const aged = formatAge(approval.ageMs)\n const inputPreview = previewJson(approval.input, 12)\n\n return (\n <div\n className={cn(\n 'flex flex-col gap-3',\n 'rounded-md border bg-surface p-4',\n // Cards older than 90s carry a soft danger border to draw\n // the operator's eye to stale requests.\n approval.ageMs > 90_000 ? 'border-danger/60' : 'border-border',\n className,\n )}\n >\n <header className=\"flex flex-wrap items-center gap-2\">\n <span className=\"font-medium text-text\">{approval.toolName}</span>\n <Badge variant=\"secondary\">{approval.platform}</Badge>\n {approval.autoAllow && (\n <Badge variant=\"info\" title={t('autoAllowedTitle')}>\n {t('autoAllowedTag')}\n </Badge>\n )}\n <span className=\"ml-auto inline-flex items-center gap-1 text-xs text-text-dim\">\n {approval.ageMs > 90_000 && <AlertTriangle className=\"h-3 w-3 text-danger\" />}\n {aged}\n </span>\n </header>\n\n <div className=\"flex flex-col gap-1 text-xs\">\n <div className=\"text-text-muted\">{t('threadLabel')}</div>\n <code\n className=\"rounded bg-surface-2 px-1.5 py-0.5 font-mono text-xs text-text-dim line-clamp-1\"\n title={approval.threadId}\n >\n {approval.threadId}\n </code>\n </div>\n\n <pre\n className={cn(\n 'max-h-48 overflow-auto rounded-md',\n 'bg-surface-2 px-2 py-2',\n 'font-mono text-xs leading-5 text-text-dim',\n 'whitespace-pre-wrap break-words',\n )}\n >\n {inputPreview}\n </pre>\n\n <footer className=\"flex justify-end gap-2\">\n <Button\n type=\"button\"\n variant=\"destructive\"\n size=\"sm\"\n disabled={busy}\n onClick={() => void onResolve('deny')}\n >\n <X className=\"h-4 w-4\" />\n {t('deny')}\n </Button>\n <Button\n type=\"button\"\n size=\"sm\"\n disabled={busy}\n onClick={() => void onResolve('allow')}\n >\n <Check className=\"h-4 w-4\" />\n {t('allow')}\n </Button>\n </footer>\n </div>\n )\n}\n\n/** Human-readable age string (\"12s\", \"4m\", \"2h\"). Tracks the\n * bus's ageMs at render time; SSE invalidation keeps it fresh. */\nfunction formatAge(ms: number): string {\n if (ms < 1000) return `${ms}ms`\n const s = Math.floor(ms / 1000)\n if (s < 60) return `${s}s`\n const m = Math.floor(s / 60)\n if (m < 60) return `${m}m`\n const h = Math.floor(m / 60)\n return `${h}h`\n}\n\n/** Pretty-print the tool input JSON with a line cap. Falls back to\n * `String(value)` if JSON.stringify throws (e.g. circular). */\nfunction previewJson(value: unknown, maxLines: number): string {\n let text: string\n try {\n text = JSON.stringify(value, null, 2)\n } catch {\n text = String(value)\n }\n const lines = text.split('\\n')\n if (lines.length <= maxLines) return text\n return `${lines.slice(0, maxLines).join('\\n')}\\n… +${lines.length - maxLines} more`\n}\n","/**\n * useApprovals — react-query wrappers for the HITL approval queue.\n *\n * useApprovals() — list pending + bus metrics\n * useResolveApproval() — allow / deny a specific reqId\n *\n * SSE: the route subscribes to the `approval` SSE event and\n * invalidates this query on any phase change, so a `requested` from\n * another tab (or `resolved` we don't initiate) shows up live\n * without a poll.\n *\n * No background poll — approvals are short-lived, the SSE channel\n * is the live source, and the v1 admin's 3 s poll caused enough\n * jitter that we'd rather rely on SSE plus an explicit refresh\n * button.\n */\n\nimport { useQueryClient, useQuery, useMutation } from '@tanstack/react-query'\nimport { api } from '@/lib/api/endpoints'\nimport type { ListApprovalsResponse, ResolveApprovalBody, ResolveApprovalResponse } from '@/types/api'\n\nexport const approvalsKeys = {\n all: ['approvals'] as const,\n list: ['approvals', 'list'] as const,\n}\n\nexport function useApprovals() {\n return useQuery<ListApprovalsResponse>({\n queryKey: approvalsKeys.list,\n queryFn: () => api.listApprovals(),\n })\n}\n\nexport function useInvalidateApprovals() {\n const qc = useQueryClient()\n return () => qc.invalidateQueries({ queryKey: approvalsKeys.all })\n}\n\nexport function useResolveApproval() {\n const qc = useQueryClient()\n return useMutation<\n ResolveApprovalResponse,\n Error,\n { reqId: string; body: ResolveApprovalBody }\n >({\n mutationFn: ({ reqId, body }) => api.resolveApproval(reqId, body),\n onSuccess: () => qc.invalidateQueries({ queryKey: approvalsKeys.all }),\n })\n}\n","/**\n * /approvals — Human-in-the-loop tool approval queue.\n *\n * Cards (one per pending request) instead of a DataTable: approvals\n * are short-lived, time-sensitive, and each one wants a JSON-preview\n * block that doesn't fit cleanly in a row. Allow / Deny are 1-click\n * decisions — no ConfirmDialog (the user already weighed the call\n * when they clicked).\n *\n * SSE: the `approval` event invalidates the list on every requested /\n * resolved transition, so a request from another tab (or a backend\n * resolve we didn't initiate) flips off the screen instantly.\n *\n * KPI strip:\n * pending | totalAllowed | totalDenied | totalTimedOut\n *\n * Empty state is the desired steady-state — the friendly empty card\n * matches \"all caught up\", not \"broken\".\n */\n\nimport { useState } from 'react'\nimport { useTranslation } from 'react-i18next'\nimport { toast } from 'sonner'\nimport { Loader2, RefreshCcw, ShieldCheck } from 'lucide-react'\n\nimport { Topbar } from '@/components/shell/topbar'\nimport { Button } from '@/components/ui/button'\nimport { ApprovalCard } from '@/components/approvals/approval-card'\nimport { EmptyState } from '@/components/common/empty-state'\nimport {\n useApprovals,\n useInvalidateApprovals,\n useResolveApproval,\n} from '@/hooks/use-approvals'\nimport { useEventStream, type SseStatus } from '@/hooks/use-event-stream'\nimport { describeError } from '@/lib/api/errors'\nimport type { ApprovalBehavior } from '@/types/api'\nimport { cn } from '@/lib/utils'\n\nconst LIVE_STATUS_CLASS: Record<SseStatus, string> = {\n connected: 'text-success',\n connecting: 'text-text-dim',\n reconnecting: 'text-warning',\n closed: 'text-text-muted',\n}\n\nexport default function ApprovalsRoute(): JSX.Element {\n const { t } = useTranslation(['approvals', 'common'])\n const invalidate = useInvalidateApprovals()\n\n const { data, isLoading, isFetching, refetch } = useApprovals()\n const pending = data?.pending ?? []\n const metrics = data?.metrics\n\n const live = useEventStream({\n approval: () => invalidate(),\n })\n\n const resolve = useResolveApproval()\n /** Tracks the reqId currently being resolved so the card can\n * disable its buttons. Multiple cards in flight is fine (we\n * bake one key per card), but each card stays inert until its\n * own promise lands. */\n const [resolvingId, setResolvingId] = useState<string | null>(null)\n\n async function onResolve(reqId: string, behavior: ApprovalBehavior): Promise<void> {\n setResolvingId(reqId)\n try {\n await resolve.mutateAsync({ reqId, body: { behavior } })\n toast.success(\n behavior === 'allow' ? t('toast.allowedShort') : t('toast.deniedShort'),\n )\n } catch (err) {\n const { message } = describeError(err, t)\n toast.error(message)\n } finally {\n setResolvingId((id) => (id === reqId ? null : id))\n }\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-5xl 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 <span className={cn('text-xs font-medium tabular-nums', LIVE_STATUS_CLASS[live])}>\n {t(`live.${live}`)}\n </span>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"ml-auto\"\n onClick={() => refetch()}\n disabled={isFetching}\n aria-label={t('actions.refresh', { ns: 'common' })}\n >\n {isFetching ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <RefreshCcw className=\"h-4 w-4\" />}\n <span className=\"hidden sm:inline\">{t('actions.refresh', { ns: 'common' })}</span>\n </Button>\n </div>\n <p className=\"text-sm text-text-dim\">{t('subtitle')}</p>\n </header>\n\n {/* Metrics strip */}\n {metrics && (\n <div className=\"grid grid-cols-2 gap-2 sm:grid-cols-4\">\n <StatCard label={t('stats.pending')} value={metrics.pending} tone=\"info\" />\n <StatCard label={t('stats.totalAllowed')} value={metrics.totalAllowed} tone=\"success\" />\n <StatCard label={t('stats.totalDenied')} value={metrics.totalDenied} tone=\"danger\" />\n <StatCard label={t('stats.totalTimedOut')} value={metrics.totalTimedOut} tone=\"warning\" />\n </div>\n )}\n\n {/* List */}\n {isLoading ? (\n <SkeletonList />\n ) : pending.length === 0 ? (\n <EmptyState\n icon={<ShieldCheck />}\n title={t('empty.title')}\n description={t('empty.description')}\n />\n ) : (\n <div className=\"flex flex-col gap-3\">\n {pending.map((p) => (\n <ApprovalCard\n key={p.reqId}\n approval={p}\n busy={resolvingId === p.reqId}\n onResolve={(behavior) => onResolve(p.reqId, behavior)}\n />\n ))}\n </div>\n )}\n </main>\n </div>\n )\n}\n\ninterface StatCardProps {\n label: string\n value: number\n tone: 'info' | 'success' | 'warning' | 'danger'\n}\n\nconst TONE_STYLES: Record<StatCardProps['tone'], string> = {\n info: 'border-info/30 text-info',\n success: 'border-success/30 text-success',\n warning: 'border-warning/30 text-warning',\n danger: 'border-danger/30 text-danger',\n}\n\nfunction StatCard({ label, value, tone }: StatCardProps): JSX.Element {\n return (\n <div\n className={cn(\n 'rounded-md border bg-surface px-3 py-2',\n TONE_STYLES[tone],\n )}\n >\n <div className=\"text-xs uppercase tracking-wide text-text-dim\">{label}</div>\n <div className=\"text-2xl font-semibold tabular-nums\">{value}</div>\n </div>\n )\n}\n\nfunction SkeletonList(): JSX.Element {\n return (\n <div className=\"flex flex-col gap-3\">\n {Array.from({ length: 2 }).map((_, i) => (\n <div\n key={`skel-${i}`}\n className=\"flex flex-col gap-3 rounded-md border border-border bg-surface p-4\"\n >\n <div className=\"h-4 w-1/3 rounded bg-surface-2 animate-pulse\" />\n <div className=\"h-3 w-1/2 rounded bg-surface-2 animate-pulse\" />\n <div className=\"h-24 w-full rounded bg-surface-2 animate-pulse\" />\n <div className=\"ml-auto flex gap-2\">\n <div className=\"h-8 w-16 rounded bg-surface-2 animate-pulse\" />\n <div className=\"h-8 w-16 rounded bg-surface-2 animate-pulse\" />\n </div>\n </div>\n ))}\n </div>\n )\n}\n"],"names":["ApprovalCard","approval","onResolve","busy","className","t","useTranslation","aged","formatAge","inputPreview","previewJson","jsxs","cn","jsx","Badge","AlertTriangle","Button","X","Check","ms","s","m","value","maxLines","text","lines","approvalsKeys","useApprovals","useQuery","api","useInvalidateApprovals","qc","useQueryClient","useResolveApproval","useMutation","reqId","body","LIVE_STATUS_CLASS","ApprovalsRoute","invalidate","data","isLoading","isFetching","refetch","pending","metrics","live","useEventStream","resolve","resolvingId","setResolvingId","useState","behavior","toast","err","message","describeError","id","Topbar","Loader2","RefreshCcw","StatCard","SkeletonList","EmptyState","ShieldCheck","p","TONE_STYLES","label","tone","_","i"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"approvals-HSssmXKS.js","sources":["../../src/components/approvals/approval-card.tsx","../../src/hooks/use-approvals.ts","../../src/routes/approvals.tsx"],"sourcesContent":["/**\n * ApprovalCard — one HITL approval, rendered as a card.\n *\n * The /approvals route uses cards (not table rows) on purpose:\n * * approvals are time-sensitive and few — visual urgency matters\n * * each row's `input` is a free-form record that's hard to squeeze\n * into a single cell. The card body has room for a JSON preview.\n * * Allow / Deny are 1-click decisions; we want big tap targets\n * even on desktop, which doesn't fit cleanly into a TableCell.\n *\n * Layout:\n * ┌─ tool name · platform · age ─────────────────┐\n * │ thread id (monospaced) │\n * │ │\n * │ <pre>input json (truncated to ~12 lines)</pre>│\n * │ │\n * │ [ Deny ] [ Allow ] │\n * └──────────────────────────────────────────────┘\n *\n * The buttons are disabled while a resolution is in flight (so a\n * second click can't double-fire). Async failure surfaces via the\n * parent's toast; the card stays put.\n */\n\nimport { useTranslation } from 'react-i18next'\nimport { Check, X, AlertTriangle } from 'lucide-react'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { cn } from '@/lib/utils'\nimport type { Approval, ApprovalBehavior } from '@/types/api'\n\nexport interface ApprovalCardProps {\n approval: Approval\n /** Async resolve callback. The card calls this with the behavior\n * + the current reqId; parent handles toast + mutation state.\n * The card disables its buttons while the promise is in flight. */\n onResolve: (behavior: ApprovalBehavior) => Promise<void> | void\n /** When true, both action buttons are disabled. Parent flips this\n * on while its mutation is in flight. */\n busy?: boolean\n /** Optional className for the outer card. */\n className?: string\n}\n\nexport function ApprovalCard({\n approval,\n onResolve,\n busy = false,\n className,\n}: ApprovalCardProps): JSX.Element {\n const { t } = useTranslation('approvals')\n\n const aged = formatAge(approval.ageMs)\n const inputPreview = previewJson(approval.input, 12)\n\n return (\n <div\n className={cn(\n 'flex flex-col gap-3',\n 'rounded-md border bg-surface p-4',\n // Cards older than 90s carry a soft danger border to draw\n // the operator's eye to stale requests.\n approval.ageMs > 90_000 ? 'border-danger/60' : 'border-border',\n className,\n )}\n >\n <header className=\"flex flex-wrap items-center gap-2\">\n <span className=\"font-medium text-text\">{approval.toolName}</span>\n <Badge variant=\"secondary\">{approval.platform}</Badge>\n {approval.autoAllow && (\n <Badge variant=\"info\" title={t('autoAllowedTitle')}>\n {t('autoAllowedTag')}\n </Badge>\n )}\n <span className=\"ml-auto inline-flex items-center gap-1 text-xs text-text-dim\">\n {approval.ageMs > 90_000 && <AlertTriangle className=\"h-3 w-3 text-danger\" />}\n {aged}\n </span>\n </header>\n\n <div className=\"flex flex-col gap-1 text-xs\">\n <div className=\"text-text-muted\">{t('threadLabel')}</div>\n <code\n className=\"rounded bg-surface-2 px-1.5 py-0.5 font-mono text-xs text-text-dim line-clamp-1\"\n title={approval.threadId}\n >\n {approval.threadId}\n </code>\n </div>\n\n <pre\n className={cn(\n 'max-h-48 overflow-auto rounded-md',\n 'bg-surface-2 px-2 py-2',\n 'font-mono text-xs leading-5 text-text-dim',\n 'whitespace-pre-wrap break-words',\n )}\n >\n {inputPreview}\n </pre>\n\n <footer className=\"flex justify-end gap-2\">\n <Button\n type=\"button\"\n variant=\"destructive\"\n size=\"sm\"\n disabled={busy}\n onClick={() => void onResolve('deny')}\n >\n <X className=\"h-4 w-4\" />\n {t('deny')}\n </Button>\n <Button\n type=\"button\"\n size=\"sm\"\n disabled={busy}\n onClick={() => void onResolve('allow')}\n >\n <Check className=\"h-4 w-4\" />\n {t('allow')}\n </Button>\n </footer>\n </div>\n )\n}\n\n/** Human-readable age string (\"12s\", \"4m\", \"2h\"). Tracks the\n * bus's ageMs at render time; SSE invalidation keeps it fresh. */\nfunction formatAge(ms: number): string {\n if (ms < 1000) return `${ms}ms`\n const s = Math.floor(ms / 1000)\n if (s < 60) return `${s}s`\n const m = Math.floor(s / 60)\n if (m < 60) return `${m}m`\n const h = Math.floor(m / 60)\n return `${h}h`\n}\n\n/** Pretty-print the tool input JSON with a line cap. Falls back to\n * `String(value)` if JSON.stringify throws (e.g. circular). */\nfunction previewJson(value: unknown, maxLines: number): string {\n let text: string\n try {\n text = JSON.stringify(value, null, 2)\n } catch {\n text = String(value)\n }\n const lines = text.split('\\n')\n if (lines.length <= maxLines) return text\n return `${lines.slice(0, maxLines).join('\\n')}\\n… +${lines.length - maxLines} more`\n}\n","/**\n * useApprovals — react-query wrappers for the HITL approval queue.\n *\n * useApprovals() — list pending + bus metrics\n * useResolveApproval() — allow / deny a specific reqId\n *\n * SSE: the route subscribes to the `approval` SSE event and\n * invalidates this query on any phase change, so a `requested` from\n * another tab (or `resolved` we don't initiate) shows up live\n * without a poll.\n *\n * No background poll — approvals are short-lived, the SSE channel\n * is the live source, and the v1 admin's 3 s poll caused enough\n * jitter that we'd rather rely on SSE plus an explicit refresh\n * button.\n */\n\nimport { useQueryClient, useQuery, useMutation } from '@tanstack/react-query'\nimport { api } from '@/lib/api/endpoints'\nimport type { ListApprovalsResponse, ResolveApprovalBody, ResolveApprovalResponse } from '@/types/api'\n\nexport const approvalsKeys = {\n all: ['approvals'] as const,\n list: ['approvals', 'list'] as const,\n}\n\nexport function useApprovals() {\n return useQuery<ListApprovalsResponse>({\n queryKey: approvalsKeys.list,\n queryFn: () => api.listApprovals(),\n })\n}\n\nexport function useInvalidateApprovals() {\n const qc = useQueryClient()\n return () => qc.invalidateQueries({ queryKey: approvalsKeys.all })\n}\n\nexport function useResolveApproval() {\n const qc = useQueryClient()\n return useMutation<\n ResolveApprovalResponse,\n Error,\n { reqId: string; body: ResolveApprovalBody }\n >({\n mutationFn: ({ reqId, body }) => api.resolveApproval(reqId, body),\n onSuccess: () => qc.invalidateQueries({ queryKey: approvalsKeys.all }),\n })\n}\n","/**\n * /approvals — Human-in-the-loop tool approval queue.\n *\n * Cards (one per pending request) instead of a DataTable: approvals\n * are short-lived, time-sensitive, and each one wants a JSON-preview\n * block that doesn't fit cleanly in a row. Allow / Deny are 1-click\n * decisions — no ConfirmDialog (the user already weighed the call\n * when they clicked).\n *\n * SSE: the `approval` event invalidates the list on every requested /\n * resolved transition, so a request from another tab (or a backend\n * resolve we didn't initiate) flips off the screen instantly.\n *\n * KPI strip:\n * pending | totalAllowed | totalDenied | totalTimedOut\n *\n * Empty state is the desired steady-state — the friendly empty card\n * matches \"all caught up\", not \"broken\".\n */\n\nimport { useState } from 'react'\nimport { useTranslation } from 'react-i18next'\nimport { toast } from 'sonner'\nimport { Loader2, RefreshCcw, ShieldCheck } from 'lucide-react'\n\nimport { Topbar } from '@/components/shell/topbar'\nimport { Button } from '@/components/ui/button'\nimport { ApprovalCard } from '@/components/approvals/approval-card'\nimport { EmptyState } from '@/components/common/empty-state'\nimport {\n useApprovals,\n useInvalidateApprovals,\n useResolveApproval,\n} from '@/hooks/use-approvals'\nimport { useEventStream, type SseStatus } from '@/hooks/use-event-stream'\nimport { describeError } from '@/lib/api/errors'\nimport type { ApprovalBehavior } from '@/types/api'\nimport { cn } from '@/lib/utils'\n\nconst LIVE_STATUS_CLASS: Record<SseStatus, string> = {\n connected: 'text-success',\n connecting: 'text-text-dim',\n reconnecting: 'text-warning',\n closed: 'text-text-muted',\n}\n\nexport default function ApprovalsRoute(): JSX.Element {\n const { t } = useTranslation(['approvals', 'common'])\n const invalidate = useInvalidateApprovals()\n\n const { data, isLoading, isFetching, refetch } = useApprovals()\n const pending = data?.pending ?? []\n const metrics = data?.metrics\n\n const live = useEventStream({\n approval: () => invalidate(),\n })\n\n const resolve = useResolveApproval()\n /** Tracks the reqId currently being resolved so the card can\n * disable its buttons. Multiple cards in flight is fine (we\n * bake one key per card), but each card stays inert until its\n * own promise lands. */\n const [resolvingId, setResolvingId] = useState<string | null>(null)\n\n async function onResolve(reqId: string, behavior: ApprovalBehavior): Promise<void> {\n setResolvingId(reqId)\n try {\n await resolve.mutateAsync({ reqId, body: { behavior } })\n toast.success(\n behavior === 'allow' ? t('toast.allowedShort') : t('toast.deniedShort'),\n )\n } catch (err) {\n const { message } = describeError(err, t)\n toast.error(message)\n } finally {\n setResolvingId((id) => (id === reqId ? null : id))\n }\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-5xl 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 <span className={cn('text-xs font-medium tabular-nums', LIVE_STATUS_CLASS[live])}>\n {t(`live.${live}`)}\n </span>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"ml-auto\"\n onClick={() => refetch()}\n disabled={isFetching}\n aria-label={t('actions.refresh', { ns: 'common' })}\n >\n {isFetching ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <RefreshCcw className=\"h-4 w-4\" />}\n <span className=\"hidden sm:inline\">{t('actions.refresh', { ns: 'common' })}</span>\n </Button>\n </div>\n <p className=\"text-sm text-text-dim\">{t('subtitle')}</p>\n </header>\n\n {/* Metrics strip */}\n {metrics && (\n <div className=\"grid grid-cols-2 gap-2 sm:grid-cols-4\">\n <StatCard label={t('stats.pending')} value={metrics.pending} tone=\"info\" />\n <StatCard label={t('stats.totalAllowed')} value={metrics.totalAllowed} tone=\"success\" />\n <StatCard label={t('stats.totalDenied')} value={metrics.totalDenied} tone=\"danger\" />\n <StatCard label={t('stats.totalTimedOut')} value={metrics.totalTimedOut} tone=\"warning\" />\n </div>\n )}\n\n {/* List */}\n {isLoading ? (\n <SkeletonList />\n ) : pending.length === 0 ? (\n <EmptyState\n icon={<ShieldCheck />}\n title={t('empty.title')}\n description={t('empty.description')}\n />\n ) : (\n <div className=\"flex flex-col gap-3\">\n {pending.map((p) => (\n <ApprovalCard\n key={p.reqId}\n approval={p}\n busy={resolvingId === p.reqId}\n onResolve={(behavior) => onResolve(p.reqId, behavior)}\n />\n ))}\n </div>\n )}\n </main>\n </div>\n )\n}\n\ninterface StatCardProps {\n label: string\n value: number\n tone: 'info' | 'success' | 'warning' | 'danger'\n}\n\nconst TONE_STYLES: Record<StatCardProps['tone'], string> = {\n info: 'border-info/30 text-info',\n success: 'border-success/30 text-success',\n warning: 'border-warning/30 text-warning',\n danger: 'border-danger/30 text-danger',\n}\n\nfunction StatCard({ label, value, tone }: StatCardProps): JSX.Element {\n return (\n <div\n className={cn(\n 'rounded-md border bg-surface px-3 py-2',\n TONE_STYLES[tone],\n )}\n >\n <div className=\"text-xs uppercase tracking-wide text-text-dim\">{label}</div>\n <div className=\"text-2xl font-semibold tabular-nums\">{value}</div>\n </div>\n )\n}\n\nfunction SkeletonList(): JSX.Element {\n return (\n <div className=\"flex flex-col gap-3\">\n {Array.from({ length: 2 }).map((_, i) => (\n <div\n key={`skel-${i}`}\n className=\"flex flex-col gap-3 rounded-md border border-border bg-surface p-4\"\n >\n <div className=\"h-4 w-1/3 rounded bg-surface-2 animate-pulse\" />\n <div className=\"h-3 w-1/2 rounded bg-surface-2 animate-pulse\" />\n <div className=\"h-24 w-full rounded bg-surface-2 animate-pulse\" />\n <div className=\"ml-auto flex gap-2\">\n <div className=\"h-8 w-16 rounded bg-surface-2 animate-pulse\" />\n <div className=\"h-8 w-16 rounded bg-surface-2 animate-pulse\" />\n </div>\n </div>\n ))}\n </div>\n )\n}\n"],"names":["ApprovalCard","approval","onResolve","busy","className","t","useTranslation","aged","formatAge","inputPreview","previewJson","jsxs","cn","jsx","Badge","AlertTriangle","Button","X","Check","ms","s","m","value","maxLines","text","lines","approvalsKeys","useApprovals","useQuery","api","useInvalidateApprovals","qc","useQueryClient","useResolveApproval","useMutation","reqId","body","LIVE_STATUS_CLASS","ApprovalsRoute","invalidate","data","isLoading","isFetching","refetch","pending","metrics","live","useEventStream","resolve","resolvingId","setResolvingId","useState","behavior","toast","err","message","describeError","id","Topbar","Loader2","RefreshCcw","StatCard","SkeletonList","EmptyState","ShieldCheck","p","TONE_STYLES","label","tone","_","i"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import{n as o}from"./index-
|
|
1
|
+
import{n as o}from"./index-O0BQoyzo.js";/**
|
|
2
2
|
* @license lucide-react v0.469.0 - ISC
|
|
3
3
|
*
|
|
4
4
|
* This source code is licensed under the ISC license.
|
|
5
5
|
* See the LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/const r=o("ArrowDown",[["path",{d:"M12 5v14",key:"s699le"}],["path",{d:"m19 12-7 7-7-7",key:"1idqje"}]]);export{r as A};
|
|
7
|
-
//# sourceMappingURL=arrow-down-
|
|
7
|
+
//# sourceMappingURL=arrow-down-BXvC8Al2.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"arrow-down-
|
|
1
|
+
{"version":3,"file":"arrow-down-BXvC8Al2.js","sources":["../../node_modules/lucide-react/dist/esm/icons/arrow-down.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 ArrowDown = createLucideIcon(\"ArrowDown\", [\n [\"path\", { d: \"M12 5v14\", key: \"s699le\" }],\n [\"path\", { d: \"m19 12-7 7-7-7\", key: \"1idqje\" }]\n]);\n\nexport { ArrowDown as default };\n//# sourceMappingURL=arrow-down.js.map\n"],"names":["ArrowDown","createLucideIcon"],"mappings":"wCAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GASK,MAACA,EAAYC,EAAiB,YAAa,CAC9C,CAAC,OAAQ,CAAE,EAAG,WAAY,IAAK,QAAQ,CAAE,EACzC,CAAC,OAAQ,CAAE,EAAG,iBAAkB,IAAK,QAAQ,CAAE,CACjD,CAAC","x_google_ignoreList":[0]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import{n as r}from"./index-
|
|
1
|
+
import{n as r}from"./index-O0BQoyzo.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 a=r("ArrowUp",[["path",{d:"m5 12 7-7 7 7",key:"hav0vg"}],["path",{d:"M12 19V5",key:"x0mq9r"}]]);export{a as A};
|
|
7
|
-
//# sourceMappingURL=arrow-up-
|
|
7
|
+
//# sourceMappingURL=arrow-up-63xELY5Q.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"arrow-up-
|
|
1
|
+
{"version":3,"file":"arrow-up-63xELY5Q.js","sources":["../../node_modules/lucide-react/dist/esm/icons/arrow-up.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 ArrowUp = createLucideIcon(\"ArrowUp\", [\n [\"path\", { d: \"m5 12 7-7 7 7\", key: \"hav0vg\" }],\n [\"path\", { d: \"M12 19V5\", key: \"x0mq9r\" }]\n]);\n\nexport { ArrowUp as default };\n//# sourceMappingURL=arrow-up.js.map\n"],"names":["ArrowUp","createLucideIcon"],"mappings":"wCAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GASK,MAACA,EAAUC,EAAiB,UAAW,CAC1C,CAAC,OAAQ,CAAE,EAAG,gBAAiB,IAAK,QAAQ,CAAE,EAC9C,CAAC,OAAQ,CAAE,EAAG,WAAY,IAAK,QAAQ,CAAE,CAC3C,CAAC","x_google_ignoreList":[0]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import{n as u,u as j,j as s,k as o,B as d,l as m}from"./index-
|
|
1
|
+
import{n as u,u as j,j as s,k as o,B as d,l as m}from"./index-O0BQoyzo.js";import{T as f,a as k,b as x,c as r,d as p,e as n}from"./table-CPn1MRcy.js";import{d as g,e as b}from"./use-background-tasks-BQrEeUwY.js";import{L as N}from"./loader-circle-9VUMGitw.js";import{R as v}from"./refresh-ccw-D2CWiyU_.js";import{C as w}from"./circle-x-Ccg1HyV-.js";import"./react-Cb2sDjhD.js";import"./useQuery-PdiC7-sY.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 y=u("Hourglass",[["path",{d:"M5 22h14",key:"ehvnwv"}],["path",{d:"M5 2h14",key:"pdyrp9"}],["path",{d:"M17 22v-4.172a2 2 0 0 0-.586-1.414L12 12l-4.414 4.414A2 2 0 0 0 7 17.828V22",key:"1d314k"}],["path",{d:"M7 2v4.172a2 2 0 0 0 .586 1.414L12 12l4.414-4.414A2 2 0 0 0 17 6.172V2",key:"1vvvr6"}]]);function H(){const{t:e}=j(["tasks","common"]),t=g(),i=b();async function h(a){if(confirm(e("asks.cancelConfirm",{id:a.slice(0,8)})))try{await i.mutateAsync(a),m.success(e("asks.cancelledToast"))}catch(l){m.error(l?.message??String(l))}}const c=t.data?.pending??[];return s.jsxs("div",{className:"mx-auto flex max-w-6xl flex-col gap-4 p-4",children:[s.jsxs("header",{className:"flex items-center gap-3",children:[s.jsx(y,{className:"h-5 w-5 text-text-dim"}),s.jsx("h1",{className:"text-xl font-semibold",children:e("asks.title")}),s.jsx(o,{variant:"info",children:c.length}),s.jsxs(d,{variant:"ghost",size:"sm",className:"ml-auto",onClick:()=>void t.refetch(),disabled:t.isFetching,"aria-label":e("actions.refresh",{ns:"common"}),children:[t.isFetching?s.jsx(N,{className:"h-4 w-4 animate-spin"}):s.jsx(v,{className:"h-4 w-4"}),s.jsx("span",{className:"hidden sm:inline",children:e("asks.refresh")})]})]}),s.jsx("p",{className:"text-sm text-text-dim",children:e("asks.subtitle")}),t.isLoading?s.jsx("div",{className:"h-32 rounded-md bg-surface-2 animate-pulse"}):c.length===0?s.jsx("section",{className:"rounded-md border border-border bg-surface p-6 text-center text-sm text-text-dim",children:e("asks.empty")}):s.jsx("section",{className:"rounded-md border border-border bg-surface",children:s.jsxs(f,{children:[s.jsx(k,{children:s.jsxs(x,{children:[s.jsx(r,{children:e("asks.col.reqId")}),s.jsx(r,{children:e("asks.col.thread")}),s.jsx(r,{children:e("asks.col.question")}),s.jsx(r,{children:e("asks.col.choices")}),s.jsx(r,{children:e("asks.col.age")}),s.jsx(r,{children:e("asks.col.actions")})]})}),s.jsx(p,{children:c.map(a=>s.jsxs(x,{children:[s.jsxs(n,{className:"font-mono text-[11px]",children:[a.reqId.slice(0,8),"…"]}),s.jsxs(n,{className:"font-mono text-[11px]",children:[a.platform,":",a.threadId]}),s.jsx(n,{className:"max-w-md break-words",children:a.question}),s.jsx(n,{children:s.jsx(o,{variant:"outline",children:a.choices})}),s.jsx(n,{className:"text-xs text-text-dim",children:C(a.ageMs,e)}),s.jsx(n,{children:s.jsxs(d,{variant:"ghost",size:"sm",onClick:()=>void h(a.reqId),disabled:i.isPending,className:"text-danger hover:text-danger",children:[s.jsx(w,{className:"h-4 w-4"}),e("asks.cancel")]})})]},a.reqId))})]})})]})}function C(e,t){return e<1e3?t("asks.justNow"):e<6e4?t("asks.seconds",{count:Math.floor(e/1e3)}):e<36e5?t("asks.minutes",{count:Math.floor(e/6e4)}):t("asks.hours",{count:Math.floor(e/36e5)})}export{H as default};
|
|
7
|
-
//# sourceMappingURL=asks-
|
|
7
|
+
//# sourceMappingURL=asks-COLEFOvK.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"asks-
|
|
1
|
+
{"version":3,"file":"asks-COLEFOvK.js","sources":["../../node_modules/lucide-react/dist/esm/icons/hourglass.js","../../src/routes/tasks/asks.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 Hourglass = createLucideIcon(\"Hourglass\", [\n [\"path\", { d: \"M5 22h14\", key: \"ehvnwv\" }],\n [\"path\", { d: \"M5 2h14\", key: \"pdyrp9\" }],\n [\n \"path\",\n {\n d: \"M17 22v-4.172a2 2 0 0 0-.586-1.414L12 12l-4.414 4.414A2 2 0 0 0 7 17.828V22\",\n key: \"1d314k\"\n }\n ],\n [\n \"path\",\n { d: \"M7 2v4.172a2 2 0 0 0 .586 1.414L12 12l4.414-4.414A2 2 0 0 0 17 6.172V2\", key: \"1vvvr6\" }\n ]\n]);\n\nexport { Hourglass as default };\n//# sourceMappingURL=hourglass.js.map\n","/**\n * /tasks/asks — admin view of pending ask_user (P1).\n */\n\nimport { useTranslation } from 'react-i18next'\nimport { toast } from 'sonner'\nimport { Hourglass, Loader2, RefreshCcw, XCircle } from 'lucide-react'\n\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport {\n Table, TableBody, TableCell, TableHead, TableHeader, TableRow,\n} from '@/components/ui/table'\nimport {\n useAsksList, useAskCancel,\n} from '@/hooks/use-background-tasks'\n\nexport default function TasksAsksRoute(): JSX.Element {\n const { t } = useTranslation(['tasks', 'common'])\n const listQuery = useAsksList()\n const cancel = useAskCancel()\n\n async function onCancel(reqId: string): Promise<void> {\n if (!confirm(t('asks.cancelConfirm', { id: reqId.slice(0, 8) }))) return\n try {\n await cancel.mutateAsync(reqId)\n toast.success(t('asks.cancelledToast'))\n } catch (err) {\n toast.error(((err as Error)?.message ?? String(err)))\n }\n }\n\n const rows = listQuery.data?.pending ?? []\n return (\n <div className=\"mx-auto flex max-w-6xl flex-col gap-4 p-4\">\n <header className=\"flex items-center gap-3\">\n <Hourglass className=\"h-5 w-5 text-text-dim\" />\n <h1 className=\"text-xl font-semibold\">{t('asks.title')}</h1>\n <Badge variant=\"info\">{rows.length}</Badge>\n <Button\n variant=\"ghost\" size=\"sm\" className=\"ml-auto\"\n onClick={() => void listQuery.refetch()}\n disabled={listQuery.isFetching}\n aria-label={t('actions.refresh', { ns: 'common' })}\n >\n {listQuery.isFetching\n ? <Loader2 className=\"h-4 w-4 animate-spin\" />\n : <RefreshCcw className=\"h-4 w-4\" />}\n <span className=\"hidden sm:inline\">{t('asks.refresh')}</span>\n </Button>\n </header>\n <p className=\"text-sm text-text-dim\">{t('asks.subtitle')}</p>\n\n {listQuery.isLoading ? (\n <div className=\"h-32 rounded-md bg-surface-2 animate-pulse\" />\n ) : rows.length === 0 ? (\n <section className=\"rounded-md border border-border bg-surface p-6 text-center text-sm text-text-dim\">\n {t('asks.empty')}\n </section>\n ) : (\n <section className=\"rounded-md border border-border bg-surface\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead>{t('asks.col.reqId')}</TableHead>\n <TableHead>{t('asks.col.thread')}</TableHead>\n <TableHead>{t('asks.col.question')}</TableHead>\n <TableHead>{t('asks.col.choices')}</TableHead>\n <TableHead>{t('asks.col.age')}</TableHead>\n <TableHead>{t('asks.col.actions')}</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {rows.map((r) => (\n <TableRow key={r.reqId}>\n <TableCell className=\"font-mono text-[11px]\">{r.reqId.slice(0, 8)}…</TableCell>\n <TableCell className=\"font-mono text-[11px]\">{r.platform}:{r.threadId}</TableCell>\n <TableCell className=\"max-w-md break-words\">{r.question}</TableCell>\n <TableCell><Badge variant=\"outline\">{r.choices}</Badge></TableCell>\n <TableCell className=\"text-xs text-text-dim\">{fmtAge(r.ageMs, t)}</TableCell>\n <TableCell>\n <Button\n variant=\"ghost\" size=\"sm\"\n onClick={() => void onCancel(r.reqId)}\n disabled={cancel.isPending}\n className=\"text-danger hover:text-danger\"\n >\n <XCircle className=\"h-4 w-4\" />\n {t('asks.cancel')}\n </Button>\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </section>\n )}\n </div>\n )\n}\n\nfunction fmtAge(ms: number, t: (key: string, opts?: Record<string, unknown>) => string): string {\n if (ms < 1_000) return t('asks.justNow')\n if (ms < 60_000) return t('asks.seconds', { count: Math.floor(ms / 1_000) })\n if (ms < 3_600_000) return t('asks.minutes', { count: Math.floor(ms / 60_000) })\n return t('asks.hours', { count: Math.floor(ms / 3_600_000) })\n}\n"],"names":["Hourglass","createLucideIcon","TasksAsksRoute","t","useTranslation","listQuery","useAsksList","cancel","useAskCancel","onCancel","reqId","toast","err","rows","jsxs","jsx","Badge","Button","Loader2","RefreshCcw","Table","TableHeader","TableRow","TableHead","TableBody","r","TableCell","XCircle","fmtAge","ms"],"mappings":"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","x_google_ignoreList":[0]}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{u as A,e as O,j as t,k as F,B as I,L as P,I as k,c as B}from"./index-
|
|
2
|
-
//# sourceMappingURL=audit-
|
|
1
|
+
import{u as A,e as O,j as t,k as F,B as I,L as P,I as k,c as B}from"./index-O0BQoyzo.js";import{r as i}from"./react-Cb2sDjhD.js";import{D as _}from"./data-table-S7rIjwdO.js";import{E as U}from"./empty-state-C-qjOHyu.js";import{b as $}from"./use-observability-C2M6WZ9W.js";import{L as q}from"./loader-circle-9VUMGitw.js";import{R as z}from"./refresh-ccw-D2CWiyU_.js";import{A as H}from"./activity-cbLHkzca.js";import"./table-CPn1MRcy.js";import"./arrow-up-63xELY5Q.js";import"./arrow-down-BXvC8Al2.js";import"./useQuery-PdiC7-sY.js";const G=300;function nt(){const{t:e}=A(["observability","common"]),[s,d]=O(),c=Math.max(1,Number(s.get("days"))||7),n=s.get("agent")??"",l=s.get("platform")??"",r=s.get("user")??"",o=s.get("intent")??"",[u,j]=i.useState(n),[x,b]=i.useState(l),[f,w]=i.useState(r),[h,C]=i.useState(o);i.useEffect(()=>{j(n)},[n]),i.useEffect(()=>{b(l)},[l]),i.useEffect(()=>{w(r)},[r]),i.useEffect(()=>{C(o)},[o]),i.useEffect(()=>{const a=window.setTimeout(()=>{R({agent:u||null,platform:x||null,user:f||null,intent:h||null})},G);return()=>window.clearTimeout(a)},[u,x,f,h]);const E=i.useMemo(()=>({days:c,limit:200,...n?{agent:n}:{},...l?{platform:l}:{},...r?{user:r}:{},...o?{intent:o}:{}}),[c,n,l,r,o]),{data:v,isLoading:S,isFetching:y,refetch:M}=$(E),L=v?.invocations??[],p=v?.stats;function R(a){const g=new URLSearchParams(s);for(const[D,N]of Object.entries(a))N==null||N===""?g.delete(D):g.set(D,N);d(g,{replace:!0})}const T=i.useMemo(()=>[{id:"ts",header:e("audit.col.ts"),cell:a=>t.jsx("span",{className:"text-text-dim text-xs tabular-nums",children:J(a.ts)}),headClassName:"w-32"},{id:"agent",header:e("audit.col.agent"),cell:a=>t.jsx("span",{className:"font-medium",children:a.agent}),headClassName:"w-32"},{id:"platform",header:e("audit.col.platform"),cell:a=>t.jsx("span",{className:"text-text-dim",children:a.platform}),headClassName:"w-24",hideOnMobile:!0},{id:"user",header:e("audit.col.user"),cell:a=>t.jsx("span",{className:"font-mono text-xs text-text-dim line-clamp-1",children:a.user_id}),headClassName:"w-32",hideOnMobile:!0},{id:"intent",header:e("audit.col.intent"),cell:a=>t.jsx("span",{className:"text-text-dim line-clamp-1",children:a.intent||"—"}),asCardTitle:!0},{id:"duration",header:e("audit.col.duration"),cell:a=>t.jsxs("span",{className:"tabular-nums text-text-dim",children:[Math.round(a.duration_ms),"ms"]}),headClassName:"w-20"},{id:"cost",header:e("audit.col.cost"),cell:a=>t.jsxs("span",{className:"tabular-nums text-text-dim",children:["$",a.cost.toFixed(4)]}),headClassName:"w-24",hideOnMobile:!0},{id:"outcome",header:e("audit.col.outcome"),cell:a=>t.jsx(F,{variant:a.success?"success":"danger",title:a.error??"",children:a.success?e("audit.outcome.ok"):e("audit.outcome.fail")}),headClassName:"w-20"}],[e]);return t.jsxs("div",{className:"mx-auto flex max-w-7xl flex-col gap-4",children:[t.jsx("header",{className:"flex flex-col gap-1",children:t.jsxs("div",{className:"flex flex-wrap items-center gap-3",children:[t.jsx("h1",{className:"text-xl font-semibold",children:e("audit.title")}),t.jsxs(I,{variant:"ghost",size:"sm",className:"ml-auto",onClick:()=>M(),disabled:y,"aria-label":e("actions.refresh",{ns:"common"}),children:[y?t.jsx(q,{className:"h-4 w-4 animate-spin"}):t.jsx(z,{className:"h-4 w-4"}),t.jsx("span",{className:"hidden sm:inline",children:e("actions.refresh",{ns:"common"})})]})]})}),p&&t.jsxs("div",{className:"flex flex-wrap items-center gap-3 text-sm",children:[t.jsxs("span",{className:"text-text-dim",children:[e("audit.stats.total"),": ",t.jsx("span",{className:"tabular-nums font-medium text-text",children:p.total})]}),t.jsxs("span",{className:"text-text-dim",children:[e("audit.stats.totalCost"),": ",t.jsxs("span",{className:"tabular-nums font-medium text-text",children:["$",p.totalCost.toFixed(4)]})]})]}),t.jsxs("div",{className:"grid grid-cols-2 gap-2 sm:grid-cols-4",children:[t.jsx(m,{id:"agent",label:e("audit.filter.agent"),value:u,onChange:j}),t.jsx(m,{id:"platform",label:e("audit.filter.platform"),value:x,onChange:b}),t.jsx(m,{id:"user",label:e("audit.filter.user"),value:f,onChange:w}),t.jsx(m,{id:"intent",label:e("audit.filter.intent"),value:h,onChange:C})]}),t.jsx(_,{columns:T,rows:L,getRowId:a=>String(a.id),loading:S,emptyState:t.jsx(U,{icon:t.jsx(H,{}),title:e("audit.empty.title"),description:e("audit.empty.description")})})]})}function m({id:e,label:s,value:d,onChange:c}){return t.jsxs("div",{className:"flex flex-col gap-1",children:[t.jsx(P,{htmlFor:e,className:"text-xs text-text-dim",children:s}),t.jsx(k,{id:e,value:d,onChange:n=>c(n.target.value),className:B("h-9")})]})}function J(e){try{const s=new Date(e);return Number.isNaN(s.getTime())?e:s.toLocaleString(void 0,{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit",second:"2-digit"})}catch{return e}}export{nt as default};
|
|
2
|
+
//# sourceMappingURL=audit-D4ZEiZub.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit-BI7SCouF.js","sources":["../../src/routes/observability/audit.tsx"],"sourcesContent":["/**\n * /observability/audit — recent invocations timeline. URL state for\n * the four filter slots (agent / platform / user / intent) so deep-\n * linking works; layout's ?days= window applies too.\n */\n\nimport { useEffect, useMemo, useState } from 'react'\nimport { useSearchParams } from 'react-router-dom'\nimport { useTranslation } from 'react-i18next'\nimport { Activity, Loader2, RefreshCcw } from 'lucide-react'\n\nimport { DataTable, type DataTableColumn } from '@/components/common/data-table'\nimport { EmptyState } from '@/components/common/empty-state'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { Input } from '@/components/ui/input'\nimport { Label } from '@/components/ui/label'\nimport { useAudit } from '@/hooks/use-observability'\nimport type { InvocationRow, ListAuditQuery } from '@/types/api'\nimport { cn } from '@/lib/utils'\n\nconst SEARCH_DEBOUNCE_MS = 300\n\nexport default function ObservabilityAuditRoute(): JSX.Element {\n const { t } = useTranslation(['observability', 'common'])\n const [params, setParams] = useSearchParams()\n const days = Math.max(1, Number(params.get('days')) || 7)\n\n const agent = params.get('agent') ?? ''\n const platform = params.get('platform') ?? ''\n const user = params.get('user') ?? ''\n const intent = params.get('intent') ?? ''\n\n // Local drafts for the four free-text filter inputs — debounce so\n // typing doesn't fire one /api/audit per keystroke.\n const [agentDraft, setAgentDraft] = useState(agent)\n const [platformDraft, setPlatformDraft] = useState(platform)\n const [userDraft, setUserDraft] = useState(user)\n const [intentDraft, setIntentDraft] = useState(intent)\n\n useEffect(() => { setAgentDraft(agent) }, [agent])\n useEffect(() => { setPlatformDraft(platform) }, [platform])\n useEffect(() => { setUserDraft(user) }, [user])\n useEffect(() => { setIntentDraft(intent) }, [intent])\n\n useEffect(() => {\n const timer = window.setTimeout(() => {\n patchParams({\n agent: agentDraft || null,\n platform: platformDraft || null,\n user: userDraft || null,\n intent: intentDraft || null,\n })\n }, SEARCH_DEBOUNCE_MS)\n return () => window.clearTimeout(timer)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [agentDraft, platformDraft, userDraft, intentDraft])\n\n const query: ListAuditQuery = useMemo(\n () => ({\n days,\n limit: 200,\n ...(agent ? { agent } : {}),\n ...(platform ? { platform } : {}),\n ...(user ? { user } : {}),\n ...(intent ? { intent } : {}),\n }),\n [days, agent, platform, user, intent],\n )\n\n const { data, isLoading, isFetching, refetch } = useAudit(query)\n const rows = data?.invocations ?? []\n const stats = data?.stats\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: true })\n }\n\n const columns: DataTableColumn<InvocationRow>[] = useMemo(\n () => [\n {\n id: 'ts',\n header: t('audit.col.ts'),\n cell: (r) => <span className=\"text-text-dim text-xs tabular-nums\">{formatTime(r.ts)}</span>,\n headClassName: 'w-32',\n },\n {\n id: 'agent',\n header: t('audit.col.agent'),\n cell: (r) => <span className=\"font-medium\">{r.agent}</span>,\n headClassName: 'w-32',\n },\n {\n id: 'platform',\n header: t('audit.col.platform'),\n cell: (r) => <span className=\"text-text-dim\">{r.platform}</span>,\n headClassName: 'w-24',\n hideOnMobile: true,\n },\n {\n id: 'user',\n header: t('audit.col.user'),\n cell: (r) => <span className=\"font-mono text-xs text-text-dim line-clamp-1\">{r.user_id}</span>,\n headClassName: 'w-32',\n hideOnMobile: true,\n },\n {\n id: 'intent',\n header: t('audit.col.intent'),\n cell: (r) => <span className=\"text-text-dim line-clamp-1\">{r.intent || '—'}</span>,\n asCardTitle: true,\n },\n {\n id: 'duration',\n header: t('audit.col.duration'),\n cell: (r) => <span className=\"tabular-nums text-text-dim\">{Math.round(r.duration_ms)}ms</span>,\n headClassName: 'w-20',\n },\n {\n id: 'cost',\n header: t('audit.col.cost'),\n cell: (r) => <span className=\"tabular-nums text-text-dim\">${r.cost.toFixed(4)}</span>,\n headClassName: 'w-24',\n hideOnMobile: true,\n },\n {\n id: 'outcome',\n header: t('audit.col.outcome'),\n cell: (r) => (\n <Badge variant={r.success ? 'success' : 'danger'} title={r.error ?? ''}>\n {r.success ? t('audit.outcome.ok') : t('audit.outcome.fail')}\n </Badge>\n ),\n headClassName: 'w-20',\n },\n ],\n [t],\n )\n\n return (\n <div className=\"mx-auto flex max-w-7xl flex-col gap-4\">\n <header className=\"flex flex-col gap-1\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <h1 className=\"text-xl font-semibold\">{t('audit.title')}</h1>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"ml-auto\"\n onClick={() => refetch()}\n disabled={isFetching}\n aria-label={t('actions.refresh', { ns: 'common' })}\n >\n {isFetching ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <RefreshCcw className=\"h-4 w-4\" />}\n <span className=\"hidden sm:inline\">{t('actions.refresh', { ns: 'common' })}</span>\n </Button>\n </div>\n </header>\n\n {stats && (\n <div className=\"flex flex-wrap items-center gap-3 text-sm\">\n <span className=\"text-text-dim\">{t('audit.stats.total')}: <span className=\"tabular-nums font-medium text-text\">{stats.total}</span></span>\n <span className=\"text-text-dim\">{t('audit.stats.totalCost')}: <span className=\"tabular-nums font-medium text-text\">${stats.totalCost.toFixed(4)}</span></span>\n </div>\n )}\n\n {/* Filter row */}\n <div className=\"grid grid-cols-2 gap-2 sm:grid-cols-4\">\n <FilterInput id=\"agent\" label={t('audit.filter.agent')} value={agentDraft} onChange={setAgentDraft} />\n <FilterInput id=\"platform\" label={t('audit.filter.platform')} value={platformDraft} onChange={setPlatformDraft} />\n <FilterInput id=\"user\" label={t('audit.filter.user')} value={userDraft} onChange={setUserDraft} />\n <FilterInput id=\"intent\" label={t('audit.filter.intent')} value={intentDraft} onChange={setIntentDraft} />\n </div>\n\n <DataTable\n columns={columns}\n rows={rows}\n getRowId={(r) => String(r.id)}\n loading={isLoading}\n emptyState={\n <EmptyState\n icon={<Activity />}\n title={t('audit.empty.title')}\n description={t('audit.empty.description')}\n />\n }\n />\n </div>\n )\n}\n\ninterface FilterInputProps {\n id: string\n label: string\n value: string\n onChange: (v: string) => void\n}\n\nfunction FilterInput({ id, label, value, onChange }: FilterInputProps): 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 className={cn('h-9')}\n />\n </div>\n )\n}\n\nfunction formatTime(iso: string): string {\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', second: '2-digit' })\n } catch {\n return iso\n }\n}\n"],"names":["SEARCH_DEBOUNCE_MS","ObservabilityAuditRoute","t","useTranslation","params","setParams","useSearchParams","days","agent","platform","user","intent","agentDraft","setAgentDraft","useState","platformDraft","setPlatformDraft","userDraft","setUserDraft","intentDraft","setIntentDraft","useEffect","timer","patchParams","query","useMemo","data","isLoading","isFetching","refetch","useAudit","rows","stats","patch","next","k","v","columns","r","jsx","formatTime","jsxs","Badge","Button","Loader2","RefreshCcw","FilterInput","DataTable","EmptyState","Activity","id","label","value","onChange","Label","Input","e","cn","iso","d"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"audit-D4ZEiZub.js","sources":["../../src/routes/observability/audit.tsx"],"sourcesContent":["/**\n * /observability/audit — recent invocations timeline. URL state for\n * the four filter slots (agent / platform / user / intent) so deep-\n * linking works; layout's ?days= window applies too.\n */\n\nimport { useEffect, useMemo, useState } from 'react'\nimport { useSearchParams } from 'react-router-dom'\nimport { useTranslation } from 'react-i18next'\nimport { Activity, Loader2, RefreshCcw } from 'lucide-react'\n\nimport { DataTable, type DataTableColumn } from '@/components/common/data-table'\nimport { EmptyState } from '@/components/common/empty-state'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { Input } from '@/components/ui/input'\nimport { Label } from '@/components/ui/label'\nimport { useAudit } from '@/hooks/use-observability'\nimport type { InvocationRow, ListAuditQuery } from '@/types/api'\nimport { cn } from '@/lib/utils'\n\nconst SEARCH_DEBOUNCE_MS = 300\n\nexport default function ObservabilityAuditRoute(): JSX.Element {\n const { t } = useTranslation(['observability', 'common'])\n const [params, setParams] = useSearchParams()\n const days = Math.max(1, Number(params.get('days')) || 7)\n\n const agent = params.get('agent') ?? ''\n const platform = params.get('platform') ?? ''\n const user = params.get('user') ?? ''\n const intent = params.get('intent') ?? ''\n\n // Local drafts for the four free-text filter inputs — debounce so\n // typing doesn't fire one /api/audit per keystroke.\n const [agentDraft, setAgentDraft] = useState(agent)\n const [platformDraft, setPlatformDraft] = useState(platform)\n const [userDraft, setUserDraft] = useState(user)\n const [intentDraft, setIntentDraft] = useState(intent)\n\n useEffect(() => { setAgentDraft(agent) }, [agent])\n useEffect(() => { setPlatformDraft(platform) }, [platform])\n useEffect(() => { setUserDraft(user) }, [user])\n useEffect(() => { setIntentDraft(intent) }, [intent])\n\n useEffect(() => {\n const timer = window.setTimeout(() => {\n patchParams({\n agent: agentDraft || null,\n platform: platformDraft || null,\n user: userDraft || null,\n intent: intentDraft || null,\n })\n }, SEARCH_DEBOUNCE_MS)\n return () => window.clearTimeout(timer)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [agentDraft, platformDraft, userDraft, intentDraft])\n\n const query: ListAuditQuery = useMemo(\n () => ({\n days,\n limit: 200,\n ...(agent ? { agent } : {}),\n ...(platform ? { platform } : {}),\n ...(user ? { user } : {}),\n ...(intent ? { intent } : {}),\n }),\n [days, agent, platform, user, intent],\n )\n\n const { data, isLoading, isFetching, refetch } = useAudit(query)\n const rows = data?.invocations ?? []\n const stats = data?.stats\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: true })\n }\n\n const columns: DataTableColumn<InvocationRow>[] = useMemo(\n () => [\n {\n id: 'ts',\n header: t('audit.col.ts'),\n cell: (r) => <span className=\"text-text-dim text-xs tabular-nums\">{formatTime(r.ts)}</span>,\n headClassName: 'w-32',\n },\n {\n id: 'agent',\n header: t('audit.col.agent'),\n cell: (r) => <span className=\"font-medium\">{r.agent}</span>,\n headClassName: 'w-32',\n },\n {\n id: 'platform',\n header: t('audit.col.platform'),\n cell: (r) => <span className=\"text-text-dim\">{r.platform}</span>,\n headClassName: 'w-24',\n hideOnMobile: true,\n },\n {\n id: 'user',\n header: t('audit.col.user'),\n cell: (r) => <span className=\"font-mono text-xs text-text-dim line-clamp-1\">{r.user_id}</span>,\n headClassName: 'w-32',\n hideOnMobile: true,\n },\n {\n id: 'intent',\n header: t('audit.col.intent'),\n cell: (r) => <span className=\"text-text-dim line-clamp-1\">{r.intent || '—'}</span>,\n asCardTitle: true,\n },\n {\n id: 'duration',\n header: t('audit.col.duration'),\n cell: (r) => <span className=\"tabular-nums text-text-dim\">{Math.round(r.duration_ms)}ms</span>,\n headClassName: 'w-20',\n },\n {\n id: 'cost',\n header: t('audit.col.cost'),\n cell: (r) => <span className=\"tabular-nums text-text-dim\">${r.cost.toFixed(4)}</span>,\n headClassName: 'w-24',\n hideOnMobile: true,\n },\n {\n id: 'outcome',\n header: t('audit.col.outcome'),\n cell: (r) => (\n <Badge variant={r.success ? 'success' : 'danger'} title={r.error ?? ''}>\n {r.success ? t('audit.outcome.ok') : t('audit.outcome.fail')}\n </Badge>\n ),\n headClassName: 'w-20',\n },\n ],\n [t],\n )\n\n return (\n <div className=\"mx-auto flex max-w-7xl flex-col gap-4\">\n <header className=\"flex flex-col gap-1\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <h1 className=\"text-xl font-semibold\">{t('audit.title')}</h1>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"ml-auto\"\n onClick={() => refetch()}\n disabled={isFetching}\n aria-label={t('actions.refresh', { ns: 'common' })}\n >\n {isFetching ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <RefreshCcw className=\"h-4 w-4\" />}\n <span className=\"hidden sm:inline\">{t('actions.refresh', { ns: 'common' })}</span>\n </Button>\n </div>\n </header>\n\n {stats && (\n <div className=\"flex flex-wrap items-center gap-3 text-sm\">\n <span className=\"text-text-dim\">{t('audit.stats.total')}: <span className=\"tabular-nums font-medium text-text\">{stats.total}</span></span>\n <span className=\"text-text-dim\">{t('audit.stats.totalCost')}: <span className=\"tabular-nums font-medium text-text\">${stats.totalCost.toFixed(4)}</span></span>\n </div>\n )}\n\n {/* Filter row */}\n <div className=\"grid grid-cols-2 gap-2 sm:grid-cols-4\">\n <FilterInput id=\"agent\" label={t('audit.filter.agent')} value={agentDraft} onChange={setAgentDraft} />\n <FilterInput id=\"platform\" label={t('audit.filter.platform')} value={platformDraft} onChange={setPlatformDraft} />\n <FilterInput id=\"user\" label={t('audit.filter.user')} value={userDraft} onChange={setUserDraft} />\n <FilterInput id=\"intent\" label={t('audit.filter.intent')} value={intentDraft} onChange={setIntentDraft} />\n </div>\n\n <DataTable\n columns={columns}\n rows={rows}\n getRowId={(r) => String(r.id)}\n loading={isLoading}\n emptyState={\n <EmptyState\n icon={<Activity />}\n title={t('audit.empty.title')}\n description={t('audit.empty.description')}\n />\n }\n />\n </div>\n )\n}\n\ninterface FilterInputProps {\n id: string\n label: string\n value: string\n onChange: (v: string) => void\n}\n\nfunction FilterInput({ id, label, value, onChange }: FilterInputProps): 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 className={cn('h-9')}\n />\n </div>\n )\n}\n\nfunction formatTime(iso: string): string {\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', second: '2-digit' })\n } catch {\n return iso\n }\n}\n"],"names":["SEARCH_DEBOUNCE_MS","ObservabilityAuditRoute","t","useTranslation","params","setParams","useSearchParams","days","agent","platform","user","intent","agentDraft","setAgentDraft","useState","platformDraft","setPlatformDraft","userDraft","setUserDraft","intentDraft","setIntentDraft","useEffect","timer","patchParams","query","useMemo","data","isLoading","isFetching","refetch","useAudit","rows","stats","patch","next","k","v","columns","r","jsx","formatTime","jsxs","Badge","Button","Loader2","RefreshCcw","FilterInput","DataTable","EmptyState","Activity","id","label","value","onChange","Label","Input","e","cn","iso","d"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import{n as e}from"./index-
|
|
1
|
+
import{n as e}from"./index-O0BQoyzo.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 t=e("Bell",[["path",{d:"M10.268 21a2 2 0 0 0 3.464 0",key:"vwvbt9"}],["path",{d:"M3.262 15.326A1 1 0 0 0 4 17h16a1 1 0 0 0 .74-1.673C19.41 13.956 18 12.499 18 8A6 6 0 0 0 6 8c0 4.499-1.411 5.956-2.738 7.326",key:"11g9vi"}]]);export{t as B};
|
|
7
|
-
//# sourceMappingURL=bell-
|
|
7
|
+
//# sourceMappingURL=bell-Cg2Bvv06.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bell-
|
|
1
|
+
{"version":3,"file":"bell-Cg2Bvv06.js","sources":["../../node_modules/lucide-react/dist/esm/icons/bell.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 Bell = createLucideIcon(\"Bell\", [\n [\"path\", { d: \"M10.268 21a2 2 0 0 0 3.464 0\", key: \"vwvbt9\" }],\n [\n \"path\",\n {\n d: \"M3.262 15.326A1 1 0 0 0 4 17h16a1 1 0 0 0 .74-1.673C19.41 13.956 18 12.499 18 8A6 6 0 0 0 6 8c0 4.499-1.411 5.956-2.738 7.326\",\n key: \"11g9vi\"\n }\n ]\n]);\n\nexport { Bell as default };\n//# sourceMappingURL=bell.js.map\n"],"names":["Bell","createLucideIcon"],"mappings":"wCAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GASK,MAACA,EAAOC,EAAiB,OAAQ,CACpC,CAAC,OAAQ,CAAE,EAAG,+BAAgC,IAAK,QAAQ,CAAE,EAC7D,CACE,OACA,CACE,EAAG,gIACH,IAAK,QACX,CACA,CACA,CAAC","x_google_ignoreList":[0]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import{n as f,d as N,u as y,e as w,j as t,B as _,S,f as C,g as v,h as I,i as d}from"./index-
|
|
1
|
+
import{n as f,d as N,u as y,e as w,j as t,B as _,S,f as C,g as v,h as I,i as d}from"./index-O0BQoyzo.js";import{r as m}from"./react-Cb2sDjhD.js";import{D as T}from"./data-table-S7rIjwdO.js";import{E as D}from"./empty-state-C-qjOHyu.js";import{S as R}from"./status-badge-BSkpyN4D.js";import{u as k}from"./useQuery-PdiC7-sY.js";import{L as B}from"./loader-circle-9VUMGitw.js";import{R as L}from"./refresh-ccw-D2CWiyU_.js";import"./table-CPn1MRcy.js";import"./arrow-up-63xELY5Q.js";import"./arrow-down-BXvC8Al2.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=f("Terminal",[["polyline",{points:"4 17 10 11 4 5",key:"akl6gq"}],["line",{x1:"12",x2:"20",y1:"19",y2:"19",key:"q2wloq"}]]),A={all:["bgjobs"],list:s=>["bgjobs","list",s]};function q(s){return k({queryKey:A.list(s),queryFn:()=>N.listBgJobs(s),refetchInterval:5e3,refetchIntervalInBackground:!1})}function G(){const{t:s}=y(["tasks","common"]),[a,i]=w(),l=a.get("root")??null,{data:o,isLoading:x,isFetching:c,refetch:h}=q(l?{root:l}:{}),n=o?.roots??[],b=m.useMemo(()=>o?o.jobs?o.jobs:o.groups?o.groups.flatMap(e=>e.jobs):[]:[],[o]);function j(e){const r=new URLSearchParams(a);e?r.set("root",e):r.delete("root"),i(r,{replace:!0})}const p=m.useMemo(()=>[{id:"name",header:s("bgjobs.col.name"),cell:e=>t.jsx("span",{className:"font-medium",children:e.name}),asCardTitle:!0},{id:"status",header:s("bgjobs.col.status"),cell:e=>t.jsx(R,{status:e.status,children:e.status}),headClassName:"w-28"},{id:"root",header:s("bgjobs.col.root"),cell:e=>{const r=n.find(g=>g.id===e.rootId);return t.jsx("span",{className:"text-text-dim",title:r?.path??e.rootId,children:r?.label??e.rootId})},headClassName:"w-32"},{id:"pid",header:s("bgjobs.col.pid"),cell:e=>t.jsx("span",{className:"tabular-nums text-text-dim",children:e.pid??"—"}),headClassName:"w-20",hideOnMobile:!0},{id:"startedAt",header:s("bgjobs.col.startedAt"),cell:e=>t.jsx("span",{className:"text-text-dim",children:u(e.started_at)}),headClassName:"w-40",hideOnMobile:!0},{id:"endedAt",header:s("bgjobs.col.endedAt"),cell:e=>t.jsx("span",{className:"text-text-dim",children:u(e.ended_at)}),headClassName:"w-40",hideOnMobile:!0},{id:"exit",header:s("bgjobs.col.exit"),cell:e=>e.ended_at==null?t.jsx("span",{className:"text-text-muted",children:s("bgjobs.stillRunning")}):e.exit_code===0?t.jsx("span",{className:"text-success",children:s("bgjobs.exitOk")}):e.exit_code!=null?t.jsx("span",{className:"text-danger tabular-nums",children:e.exit_code}):t.jsx("span",{className:"text-text-muted",children:"—"}),headClassName:"w-20"}],[s,n]);return t.jsxs("div",{className:"mx-auto flex max-w-7xl flex-col gap-4",children:[t.jsxs("header",{className:"flex flex-col gap-1",children:[t.jsxs("div",{className:"flex flex-wrap items-center gap-3",children:[t.jsx("h1",{className:"text-xl font-semibold",children:s("bgjobs.title")}),t.jsxs(_,{variant:"ghost",size:"sm",className:"ml-auto",onClick:()=>h(),disabled:c,"aria-label":s("actions.refresh",{ns:"common"}),children:[c?t.jsx(B,{className:"h-4 w-4 animate-spin"}):t.jsx(L,{className:"h-4 w-4"}),t.jsx("span",{className:"hidden sm:inline",children:s("actions.refresh",{ns:"common"})})]})]}),t.jsx("p",{className:"text-sm text-text-dim",children:s("bgjobs.subtitle")})]}),t.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[t.jsx("label",{className:"text-sm text-text-dim",htmlFor:"root-filter",children:s("bgjobs.filter.root")}),t.jsxs(S,{value:l??"__any__",onValueChange:e=>j(e==="__any__"?null:e),children:[t.jsx(C,{id:"root-filter",className:"w-[180px]",children:t.jsx(v,{})}),t.jsxs(I,{children:[t.jsx(d,{value:"__any__",children:s("bgjobs.filter.rootAny")}),n.map(e=>t.jsx(d,{value:e.id,children:e.label},e.id))]})]})]}),t.jsx(T,{columns:p,rows:b,getRowId:e=>`${e.rootId}:${e.id}`,loading:x,emptyState:t.jsx(D,{icon:t.jsx(M,{}),title:s("bgjobs.empty.title"),description:s("bgjobs.empty.description")})})]})}function u(s){if(s==null)return"—";try{const a=new Date(s);if(Number.isNaN(a.getTime()))return s;const i=new Date;return a.toDateString()===i.toDateString()?a.toLocaleTimeString(void 0,{hour:"2-digit",minute:"2-digit",second:"2-digit"}):a.toLocaleString(void 0,{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"})}catch{return s}}export{G as default};
|
|
7
|
-
//# sourceMappingURL=bgjobs-
|
|
7
|
+
//# sourceMappingURL=bgjobs-CEjCzwtd.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bgjobs-CicZiNip.js","sources":["../../node_modules/lucide-react/dist/esm/icons/terminal.js","../../src/hooks/use-bgjobs.ts","../../src/routes/tasks/bgjobs.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 Terminal = createLucideIcon(\"Terminal\", [\n [\"polyline\", { points: \"4 17 10 11 4 5\", key: \"akl6gq\" }],\n [\"line\", { x1: \"12\", x2: \"20\", y1: \"19\", y2: \"19\", key: \"q2wloq\" }]\n]);\n\nexport { Terminal as default };\n//# sourceMappingURL=terminal.js.map\n","/**\n * useBgjobs — react-query wrapper for /api/bgjobs.\n *\n * Bgjobs are a read-only window into the wrapper-managed bgjob\n * directories (Claude Code's ~/.claude/bgjobs/, opencode's\n * ~/.config/opencode/bgjobs/, codex's ~/.codex/bgjobs/, plus any\n * IMHUB_BGJOB_ROOTS custom paths). No SSE channel for bgjob events;\n * the page polls or refresh-on-demand.\n *\n * Response shape varies based on whether `root` is set — when no\n * root filter is applied the backend returns groups keyed by rootId.\n * The hook leaves that shape intact; flattening logic lives in the\n * route's `useMemo` so a future \"show per-root\" view can pivot\n * cheaply.\n */\n\nimport { useQueryClient, useQuery } from '@tanstack/react-query'\nimport { api } from '@/lib/api/endpoints'\nimport type { ListBgjobsQuery, ListBgjobsResponse } from '@/types/api'\n\nexport const bgjobsKeys = {\n all: ['bgjobs'] as const,\n list: (q: ListBgjobsQuery) => ['bgjobs', 'list', q] as const,\n}\n\nexport function useBgjobs(query: ListBgjobsQuery) {\n return useQuery<ListBgjobsResponse>({\n queryKey: bgjobsKeys.list(query),\n queryFn: () => api.listBgJobs(query),\n // Bgjobs file scans are cheap (FS reads) but we don't want to\n // hammer when the user idles on the tab. Auto-refetch every 5s\n // while the tab is focused matches the v1 admin's behavior.\n refetchInterval: 5000,\n refetchIntervalInBackground: false,\n })\n}\n\nexport function useInvalidateBgjobs() {\n const qc = useQueryClient()\n return () => qc.invalidateQueries({ queryKey: bgjobsKeys.all })\n}\n","/**\n * /tasks/bgjobs — long-running scripts launched by agent wrappers\n * via their bgjob facility. Read-only; the wrapper is the source of\n * truth for writes (kill / restart).\n *\n * Response shape: when no `?root=` filter, backend returns\n * `{ roots, groups: [{ rootId, jobs }] }`; with a root filter,\n * `{ roots: [oneRoot], jobs }`. We flatten via useMemo so the\n * DataTable consumes one flat list regardless of mode.\n *\n * No SSE: bgjob events aren't on the event-bus. We rely on the 5s\n * react-query poll set in use-bgjobs.ts.\n */\n\nimport { useMemo } from 'react'\nimport { useSearchParams } from 'react-router-dom'\nimport { useTranslation } from 'react-i18next'\nimport { Loader2, RefreshCcw, Terminal } from 'lucide-react'\n\nimport { DataTable, type DataTableColumn } from '@/components/common/data-table'\nimport { EmptyState } from '@/components/common/empty-state'\nimport { StatusBadge } from '@/components/common/status-badge'\nimport { Button } from '@/components/ui/button'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@/components/ui/select'\nimport { useBgjobs } from '@/hooks/use-bgjobs'\nimport type { BgjobSummary } from '@/types/api'\n\nexport default function BgjobsRoute(): JSX.Element {\n const { t } = useTranslation(['tasks', 'common'])\n const [params, setParams] = useSearchParams()\n const rootFilter = params.get('root') ?? null\n\n const { data, isLoading, isFetching, refetch } = useBgjobs(\n rootFilter ? { root: rootFilter } : {},\n )\n\n const roots = data?.roots ?? []\n const rows: BgjobSummary[] = useMemo(() => {\n if (!data) return []\n if (data.jobs) return data.jobs\n if (data.groups) return data.groups.flatMap((g) => g.jobs)\n return []\n }, [data])\n\n function setRoot(next: string | null): void {\n const p = new URLSearchParams(params)\n if (!next) p.delete('root')\n else p.set('root', next)\n setParams(p, { replace: true })\n }\n\n const columns: DataTableColumn<BgjobSummary>[] = useMemo(\n () => [\n {\n id: 'name',\n header: t('bgjobs.col.name'),\n cell: (r) => <span className=\"font-medium\">{r.name}</span>,\n asCardTitle: true,\n },\n {\n id: 'status',\n header: t('bgjobs.col.status'),\n cell: (r) => <StatusBadge status={r.status}>{r.status}</StatusBadge>,\n headClassName: 'w-28',\n },\n {\n id: 'root',\n header: t('bgjobs.col.root'),\n cell: (r) => {\n const root = roots.find((rt) => rt.id === r.rootId)\n return (\n <span className=\"text-text-dim\" title={root?.path ?? r.rootId}>\n {root?.label ?? r.rootId}\n </span>\n )\n },\n headClassName: 'w-32',\n },\n {\n id: 'pid',\n header: t('bgjobs.col.pid'),\n cell: (r) => (\n <span className=\"tabular-nums text-text-dim\">{r.pid ?? '—'}</span>\n ),\n headClassName: 'w-20',\n hideOnMobile: true,\n },\n {\n id: 'startedAt',\n header: t('bgjobs.col.startedAt'),\n cell: (r) => <span className=\"text-text-dim\">{formatTime(r.started_at)}</span>,\n headClassName: 'w-40',\n hideOnMobile: true,\n },\n {\n id: 'endedAt',\n header: t('bgjobs.col.endedAt'),\n cell: (r) => <span className=\"text-text-dim\">{formatTime(r.ended_at)}</span>,\n headClassName: 'w-40',\n hideOnMobile: true,\n },\n {\n id: 'exit',\n header: t('bgjobs.col.exit'),\n cell: (r) => {\n if (r.ended_at == null) return <span className=\"text-text-muted\">{t('bgjobs.stillRunning')}</span>\n if (r.exit_code === 0) return <span className=\"text-success\">{t('bgjobs.exitOk')}</span>\n if (r.exit_code != null) return <span className=\"text-danger tabular-nums\">{r.exit_code}</span>\n return <span className=\"text-text-muted\">—</span>\n },\n headClassName: 'w-20',\n },\n ],\n [t, roots],\n )\n\n return (\n <div className=\"mx-auto flex max-w-7xl flex-col gap-4\">\n <header className=\"flex flex-col gap-1\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <h1 className=\"text-xl font-semibold\">{t('bgjobs.title')}</h1>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"ml-auto\"\n onClick={() => refetch()}\n disabled={isFetching}\n aria-label={t('actions.refresh', { ns: 'common' })}\n >\n {isFetching ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <RefreshCcw className=\"h-4 w-4\" />}\n <span className=\"hidden sm:inline\">{t('actions.refresh', { ns: 'common' })}</span>\n </Button>\n </div>\n <p className=\"text-sm text-text-dim\">{t('bgjobs.subtitle')}</p>\n </header>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <label className=\"text-sm text-text-dim\" htmlFor=\"root-filter\">\n {t('bgjobs.filter.root')}\n </label>\n <Select\n value={rootFilter ?? '__any__'}\n onValueChange={(v) => setRoot(v === '__any__' ? null : v)}\n >\n <SelectTrigger id=\"root-filter\" className=\"w-[180px]\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"__any__\">{t('bgjobs.filter.rootAny')}</SelectItem>\n {roots.map((r) => (\n <SelectItem key={r.id} value={r.id}>\n {r.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n <DataTable\n columns={columns}\n rows={rows}\n getRowId={(r) => `${r.rootId}:${r.id}`}\n loading={isLoading}\n emptyState={\n <EmptyState\n icon={<Terminal />}\n title={t('bgjobs.empty.title')}\n description={t('bgjobs.empty.description')}\n />\n }\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 const now = new Date()\n const sameDay = d.toDateString() === now.toDateString()\n if (sameDay) {\n return d.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit', second: '2-digit' })\n }\n return d.toLocaleString(undefined, { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' })\n } catch {\n return iso\n }\n}\n"],"names":["Terminal","createLucideIcon","bgjobsKeys","q","useBgjobs","query","useQuery","api","BgjobsRoute","t","useTranslation","params","setParams","useSearchParams","rootFilter","data","isLoading","isFetching","refetch","roots","rows","useMemo","g","setRoot","next","p","columns","r","jsx","StatusBadge","root","rt","formatTime","jsxs","Button","Loader2","RefreshCcw","Select","v","SelectTrigger","SelectValue","SelectContent","SelectItem","DataTable","EmptyState","iso","d","now"],"mappings":"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","x_google_ignoreList":[0]}
|
|
1
|
+
{"version":3,"file":"bgjobs-CEjCzwtd.js","sources":["../../node_modules/lucide-react/dist/esm/icons/terminal.js","../../src/hooks/use-bgjobs.ts","../../src/routes/tasks/bgjobs.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 Terminal = createLucideIcon(\"Terminal\", [\n [\"polyline\", { points: \"4 17 10 11 4 5\", key: \"akl6gq\" }],\n [\"line\", { x1: \"12\", x2: \"20\", y1: \"19\", y2: \"19\", key: \"q2wloq\" }]\n]);\n\nexport { Terminal as default };\n//# sourceMappingURL=terminal.js.map\n","/**\n * useBgjobs — react-query wrapper for /api/bgjobs.\n *\n * Bgjobs are a read-only window into the wrapper-managed bgjob\n * directories (Claude Code's ~/.claude/bgjobs/, opencode's\n * ~/.config/opencode/bgjobs/, codex's ~/.codex/bgjobs/, plus any\n * IMHUB_BGJOB_ROOTS custom paths). No SSE channel for bgjob events;\n * the page polls or refresh-on-demand.\n *\n * Response shape varies based on whether `root` is set — when no\n * root filter is applied the backend returns groups keyed by rootId.\n * The hook leaves that shape intact; flattening logic lives in the\n * route's `useMemo` so a future \"show per-root\" view can pivot\n * cheaply.\n */\n\nimport { useQueryClient, useQuery } from '@tanstack/react-query'\nimport { api } from '@/lib/api/endpoints'\nimport type { ListBgjobsQuery, ListBgjobsResponse } from '@/types/api'\n\nexport const bgjobsKeys = {\n all: ['bgjobs'] as const,\n list: (q: ListBgjobsQuery) => ['bgjobs', 'list', q] as const,\n}\n\nexport function useBgjobs(query: ListBgjobsQuery) {\n return useQuery<ListBgjobsResponse>({\n queryKey: bgjobsKeys.list(query),\n queryFn: () => api.listBgJobs(query),\n // Bgjobs file scans are cheap (FS reads) but we don't want to\n // hammer when the user idles on the tab. Auto-refetch every 5s\n // while the tab is focused matches the v1 admin's behavior.\n refetchInterval: 5000,\n refetchIntervalInBackground: false,\n })\n}\n\nexport function useInvalidateBgjobs() {\n const qc = useQueryClient()\n return () => qc.invalidateQueries({ queryKey: bgjobsKeys.all })\n}\n","/**\n * /tasks/bgjobs — long-running scripts launched by agent wrappers\n * via their bgjob facility. Read-only; the wrapper is the source of\n * truth for writes (kill / restart).\n *\n * Response shape: when no `?root=` filter, backend returns\n * `{ roots, groups: [{ rootId, jobs }] }`; with a root filter,\n * `{ roots: [oneRoot], jobs }`. We flatten via useMemo so the\n * DataTable consumes one flat list regardless of mode.\n *\n * No SSE: bgjob events aren't on the event-bus. We rely on the 5s\n * react-query poll set in use-bgjobs.ts.\n */\n\nimport { useMemo } from 'react'\nimport { useSearchParams } from 'react-router-dom'\nimport { useTranslation } from 'react-i18next'\nimport { Loader2, RefreshCcw, Terminal } from 'lucide-react'\n\nimport { DataTable, type DataTableColumn } from '@/components/common/data-table'\nimport { EmptyState } from '@/components/common/empty-state'\nimport { StatusBadge } from '@/components/common/status-badge'\nimport { Button } from '@/components/ui/button'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@/components/ui/select'\nimport { useBgjobs } from '@/hooks/use-bgjobs'\nimport type { BgjobSummary } from '@/types/api'\n\nexport default function BgjobsRoute(): JSX.Element {\n const { t } = useTranslation(['tasks', 'common'])\n const [params, setParams] = useSearchParams()\n const rootFilter = params.get('root') ?? null\n\n const { data, isLoading, isFetching, refetch } = useBgjobs(\n rootFilter ? { root: rootFilter } : {},\n )\n\n const roots = data?.roots ?? []\n const rows: BgjobSummary[] = useMemo(() => {\n if (!data) return []\n if (data.jobs) return data.jobs\n if (data.groups) return data.groups.flatMap((g) => g.jobs)\n return []\n }, [data])\n\n function setRoot(next: string | null): void {\n const p = new URLSearchParams(params)\n if (!next) p.delete('root')\n else p.set('root', next)\n setParams(p, { replace: true })\n }\n\n const columns: DataTableColumn<BgjobSummary>[] = useMemo(\n () => [\n {\n id: 'name',\n header: t('bgjobs.col.name'),\n cell: (r) => <span className=\"font-medium\">{r.name}</span>,\n asCardTitle: true,\n },\n {\n id: 'status',\n header: t('bgjobs.col.status'),\n cell: (r) => <StatusBadge status={r.status}>{r.status}</StatusBadge>,\n headClassName: 'w-28',\n },\n {\n id: 'root',\n header: t('bgjobs.col.root'),\n cell: (r) => {\n const root = roots.find((rt) => rt.id === r.rootId)\n return (\n <span className=\"text-text-dim\" title={root?.path ?? r.rootId}>\n {root?.label ?? r.rootId}\n </span>\n )\n },\n headClassName: 'w-32',\n },\n {\n id: 'pid',\n header: t('bgjobs.col.pid'),\n cell: (r) => (\n <span className=\"tabular-nums text-text-dim\">{r.pid ?? '—'}</span>\n ),\n headClassName: 'w-20',\n hideOnMobile: true,\n },\n {\n id: 'startedAt',\n header: t('bgjobs.col.startedAt'),\n cell: (r) => <span className=\"text-text-dim\">{formatTime(r.started_at)}</span>,\n headClassName: 'w-40',\n hideOnMobile: true,\n },\n {\n id: 'endedAt',\n header: t('bgjobs.col.endedAt'),\n cell: (r) => <span className=\"text-text-dim\">{formatTime(r.ended_at)}</span>,\n headClassName: 'w-40',\n hideOnMobile: true,\n },\n {\n id: 'exit',\n header: t('bgjobs.col.exit'),\n cell: (r) => {\n if (r.ended_at == null) return <span className=\"text-text-muted\">{t('bgjobs.stillRunning')}</span>\n if (r.exit_code === 0) return <span className=\"text-success\">{t('bgjobs.exitOk')}</span>\n if (r.exit_code != null) return <span className=\"text-danger tabular-nums\">{r.exit_code}</span>\n return <span className=\"text-text-muted\">—</span>\n },\n headClassName: 'w-20',\n },\n ],\n [t, roots],\n )\n\n return (\n <div className=\"mx-auto flex max-w-7xl flex-col gap-4\">\n <header className=\"flex flex-col gap-1\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <h1 className=\"text-xl font-semibold\">{t('bgjobs.title')}</h1>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"ml-auto\"\n onClick={() => refetch()}\n disabled={isFetching}\n aria-label={t('actions.refresh', { ns: 'common' })}\n >\n {isFetching ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <RefreshCcw className=\"h-4 w-4\" />}\n <span className=\"hidden sm:inline\">{t('actions.refresh', { ns: 'common' })}</span>\n </Button>\n </div>\n <p className=\"text-sm text-text-dim\">{t('bgjobs.subtitle')}</p>\n </header>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <label className=\"text-sm text-text-dim\" htmlFor=\"root-filter\">\n {t('bgjobs.filter.root')}\n </label>\n <Select\n value={rootFilter ?? '__any__'}\n onValueChange={(v) => setRoot(v === '__any__' ? null : v)}\n >\n <SelectTrigger id=\"root-filter\" className=\"w-[180px]\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"__any__\">{t('bgjobs.filter.rootAny')}</SelectItem>\n {roots.map((r) => (\n <SelectItem key={r.id} value={r.id}>\n {r.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n <DataTable\n columns={columns}\n rows={rows}\n getRowId={(r) => `${r.rootId}:${r.id}`}\n loading={isLoading}\n emptyState={\n <EmptyState\n icon={<Terminal />}\n title={t('bgjobs.empty.title')}\n description={t('bgjobs.empty.description')}\n />\n }\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 const now = new Date()\n const sameDay = d.toDateString() === now.toDateString()\n if (sameDay) {\n return d.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit', second: '2-digit' })\n }\n return d.toLocaleString(undefined, { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' })\n } catch {\n return iso\n }\n}\n"],"names":["Terminal","createLucideIcon","bgjobsKeys","q","useBgjobs","query","useQuery","api","BgjobsRoute","t","useTranslation","params","setParams","useSearchParams","rootFilter","data","isLoading","isFetching","refetch","roots","rows","useMemo","g","setRoot","next","p","columns","r","jsx","StatusBadge","root","rt","formatTime","jsxs","Button","Loader2","RefreshCcw","Select","v","SelectTrigger","SelectValue","SelectContent","SelectItem","DataTable","EmptyState","iso","d","now"],"mappings":"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","x_google_ignoreList":[0]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import{n as a}from"./index-
|
|
1
|
+
import{n as a}from"./index-O0BQoyzo.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 t=a("Brain",[["path",{d:"M12 5a3 3 0 1 0-5.997.125 4 4 0 0 0-2.526 5.77 4 4 0 0 0 .556 6.588A4 4 0 1 0 12 18Z",key:"l5xja"}],["path",{d:"M12 5a3 3 0 1 1 5.997.125 4 4 0 0 1 2.526 5.77 4 4 0 0 1-.556 6.588A4 4 0 1 1 12 18Z",key:"ep3f8r"}],["path",{d:"M15 13a4.5 4.5 0 0 1-3-4 4.5 4.5 0 0 1-3 4",key:"1p4c4q"}],["path",{d:"M17.599 6.5a3 3 0 0 0 .399-1.375",key:"tmeiqw"}],["path",{d:"M6.003 5.125A3 3 0 0 0 6.401 6.5",key:"105sqy"}],["path",{d:"M3.477 10.896a4 4 0 0 1 .585-.396",key:"ql3yin"}],["path",{d:"M19.938 10.5a4 4 0 0 1 .585.396",key:"1qfode"}],["path",{d:"M6 18a4 4 0 0 1-1.967-.516",key:"2e4loj"}],["path",{d:"M19.967 17.484A4 4 0 0 1 18 18",key:"159ez6"}]]);export{t as B};
|
|
7
|
-
//# sourceMappingURL=brain-
|
|
7
|
+
//# sourceMappingURL=brain-euvl6F6C.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"brain-
|
|
1
|
+
{"version":3,"file":"brain-euvl6F6C.js","sources":["../../node_modules/lucide-react/dist/esm/icons/brain.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 Brain = createLucideIcon(\"Brain\", [\n [\n \"path\",\n {\n d: \"M12 5a3 3 0 1 0-5.997.125 4 4 0 0 0-2.526 5.77 4 4 0 0 0 .556 6.588A4 4 0 1 0 12 18Z\",\n key: \"l5xja\"\n }\n ],\n [\n \"path\",\n {\n d: \"M12 5a3 3 0 1 1 5.997.125 4 4 0 0 1 2.526 5.77 4 4 0 0 1-.556 6.588A4 4 0 1 1 12 18Z\",\n key: \"ep3f8r\"\n }\n ],\n [\"path\", { d: \"M15 13a4.5 4.5 0 0 1-3-4 4.5 4.5 0 0 1-3 4\", key: \"1p4c4q\" }],\n [\"path\", { d: \"M17.599 6.5a3 3 0 0 0 .399-1.375\", key: \"tmeiqw\" }],\n [\"path\", { d: \"M6.003 5.125A3 3 0 0 0 6.401 6.5\", key: \"105sqy\" }],\n [\"path\", { d: \"M3.477 10.896a4 4 0 0 1 .585-.396\", key: \"ql3yin\" }],\n [\"path\", { d: \"M19.938 10.5a4 4 0 0 1 .585.396\", key: \"1qfode\" }],\n [\"path\", { d: \"M6 18a4 4 0 0 1-1.967-.516\", key: \"2e4loj\" }],\n [\"path\", { d: \"M19.967 17.484A4 4 0 0 1 18 18\", key: \"159ez6\" }]\n]);\n\nexport { Brain as default };\n//# sourceMappingURL=brain.js.map\n"],"names":["Brain","createLucideIcon"],"mappings":"wCAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GASK,MAACA,EAAQC,EAAiB,QAAS,CACtC,CACE,OACA,CACE,EAAG,uFACH,IAAK,OACX,CACA,EACE,CACE,OACA,CACE,EAAG,uFACH,IAAK,QACX,CACA,EACE,CAAC,OAAQ,CAAE,EAAG,6CAA8C,IAAK,QAAQ,CAAE,EAC3E,CAAC,OAAQ,CAAE,EAAG,mCAAoC,IAAK,QAAQ,CAAE,EACjE,CAAC,OAAQ,CAAE,EAAG,mCAAoC,IAAK,QAAQ,CAAE,EACjE,CAAC,OAAQ,CAAE,EAAG,oCAAqC,IAAK,QAAQ,CAAE,EAClE,CAAC,OAAQ,CAAE,EAAG,kCAAmC,IAAK,QAAQ,CAAE,EAChE,CAAC,OAAQ,CAAE,EAAG,6BAA8B,IAAK,QAAQ,CAAE,EAC3D,CAAC,OAAQ,CAAE,EAAG,iCAAkC,IAAK,QAAQ,CAAE,CACjE,CAAC","x_google_ignoreList":[0]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import{n as e}from"./index-
|
|
1
|
+
import{n as e}from"./index-O0BQoyzo.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 a=e("Briefcase",[["path",{d:"M16 20V4a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v16",key:"jecpp"}],["rect",{width:"20",height:"14",x:"2",y:"6",rx:"2",key:"i6l2r4"}]]);export{a as B};
|
|
7
|
-
//# sourceMappingURL=briefcase-
|
|
7
|
+
//# sourceMappingURL=briefcase-DPWLbCnA.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"briefcase-
|
|
1
|
+
{"version":3,"file":"briefcase-DPWLbCnA.js","sources":["../../node_modules/lucide-react/dist/esm/icons/briefcase.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 Briefcase = createLucideIcon(\"Briefcase\", [\n [\"path\", { d: \"M16 20V4a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v16\", key: \"jecpp\" }],\n [\"rect\", { width: \"20\", height: \"14\", x: \"2\", y: \"6\", rx: \"2\", key: \"i6l2r4\" }]\n]);\n\nexport { Briefcase as default };\n//# sourceMappingURL=briefcase.js.map\n"],"names":["Briefcase","createLucideIcon"],"mappings":"wCAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GASK,MAACA,EAAYC,EAAiB,YAAa,CAC9C,CAAC,OAAQ,CAAE,EAAG,6CAA8C,IAAK,OAAO,CAAE,EAC1E,CAAC,OAAQ,CAAE,MAAO,KAAM,OAAQ,KAAM,EAAG,IAAK,EAAG,IAAK,GAAI,IAAK,IAAK,QAAQ,CAAE,CAChF,CAAC","x_google_ignoreList":[0]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import{n as E,u as R,ag as A,l as d,j as e,B as m,c as p,ah as B,ai as D,aj as M,$ as H,k as L}from"./index-
|
|
1
|
+
import{n as E,u as R,ag as A,l as d,j as e,B as m,c as p,ah as B,ai as D,aj as M,$ as H,k as L}from"./index-O0BQoyzo.js";import{r as l}from"./react-Cb2sDjhD.js";import{C as z}from"./chevron-left-BeIh5thq.js";import{A as I}from"./arrow-down-BXvC8Al2.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 F=E("EllipsisVertical",[["circle",{cx:"12",cy:"12",r:"1",key:"41hilf"}],["circle",{cx:"12",cy:"5",r:"1",key:"gxeob9"}],["circle",{cx:"12",cy:"19",r:"1",key:"lyex9k"}]]),O={open:"bg-success",connecting:"bg-warning animate-pulse",closed:"bg-text-muted",error:"bg-danger"};function K(){const{t:a}=R(["chat","common"]),{state:s,send:g,switchAgent:b,approvalClick:f,newConversation:v,clearError:h}=A(),[o,x]=l.useState(""),[j,c]=l.useState(!1),[w,y]=l.useState(!1),i=l.useRef(null),u=l.useRef(null),N=l.useRef(null);l.useEffect(()=>{const t=i.current;!t||t.scrollHeight-t.scrollTop-t.clientHeight>200&&s.currentReplyId==null||requestAnimationFrame(()=>{t.scrollTop=t.scrollHeight})},[s.messages,s.currentReplyId]),l.useEffect(()=>{const t=i.current;if(!t)return;const n=()=>{const r=t.scrollHeight-t.scrollTop-t.clientHeight;y(r>200)};return t.addEventListener("scroll",n,{passive:!0}),n(),()=>t.removeEventListener("scroll",n)},[]),l.useEffect(()=>{s.error&&(d.error(s.error),h())},[s.error,h]),l.useEffect(()=>{const t=u.current;t&&(t.style.height="auto",t.style.height=`${Math.min(t.scrollHeight,24*6+16)}px`)},[o]);function k(t){t?.preventDefault();const n=o.trim();if(n){if(s.status!=="open"){d.error(a("chat.notConnected",{defaultValue:"Not connected. Reconnecting…"}));return}g(n),x(""),requestAnimationFrame(()=>u.current?.focus())}}function S(t){t.nativeEvent.isComposing||t.keyCode}function C(){const t=i.current;t&&t.scrollTo({top:t.scrollHeight,behavior:"smooth"})}function V(){fetch("/api/auth/logout",{method:"POST"}).finally(()=>{try{localStorage.removeItem("agim_token")}catch{}window.location.replace("/login")})}return e.jsxs("div",{className:"flex h-dvh flex-col bg-bg",children:[e.jsxs("header",{className:"flex items-center gap-2 border-b border-border bg-surface px-3 py-2",style:{paddingTop:"calc(env(safe-area-inset-top) + 0.5rem)"},children:[e.jsx(m,{variant:"ghost",size:"sm","aria-label":a("chat.back",{defaultValue:"Back"}),onClick:()=>window.history.length>1?window.history.back():window.location.assign("/"),className:"h-9 w-9 p-0",children:e.jsx(z,{className:"h-5 w-5"})}),e.jsxs("div",{className:"flex min-w-0 flex-1 flex-col items-center",children:[e.jsx("span",{className:"truncate text-sm font-semibold",children:a("chat.mobileTitle",{defaultValue:"agim chat"})}),e.jsxs("span",{className:"flex items-center gap-1.5 text-[11px] text-text-dim",children:[e.jsx("span",{className:p("inline-block h-1.5 w-1.5 rounded-full",O[s.status])}),e.jsx("span",{children:s.agent}),e.jsx("span",{children:"·"}),e.jsx("span",{children:s.status})]})]}),e.jsxs("div",{className:"relative",children:[e.jsx(m,{variant:"ghost",size:"sm","aria-label":a("chat.menu",{defaultValue:"Menu"}),onClick:()=>c(t=>!t),className:"h-9 w-9 p-0",children:e.jsx(F,{className:"h-5 w-5"})}),j&&e.jsxs(e.Fragment,{children:[e.jsx("div",{className:"fixed inset-0 z-30",onClick:()=>c(!1),"aria-hidden":!0}),e.jsxs("div",{className:"absolute right-0 top-10 z-40 w-56 rounded-md border border-border bg-surface p-2 shadow-lg",role:"menu",children:[e.jsx("div",{className:"px-2 py-1 text-[11px] uppercase text-text-dim",children:a("chat.agent",{defaultValue:"Agent"})}),e.jsx("div",{className:"px-1 pb-2",children:e.jsx(B,{agents:s.agents,value:s.agent,onChange:t=>{b(t),c(!1)}})}),e.jsx("button",{type:"button",className:"block w-full rounded px-2 py-2 text-left text-sm hover:bg-surface-hover disabled:opacity-50",disabled:s.status!=="open",onClick:()=>{v(),c(!1),d.success(a("chat.newConvToast",{defaultValue:"Started a new conversation"}))},children:a("chat.newConv",{defaultValue:"New conversation"})}),e.jsx("button",{type:"button",className:"block w-full rounded px-2 py-2 text-left text-sm hover:bg-surface-hover",onClick:V,children:a("chat.logout",{defaultValue:"Log out"})})]})]})]})]}),e.jsx("div",{ref:i,className:"flex-1 overflow-y-auto",children:e.jsxs("div",{className:"flex flex-col gap-2 px-3 py-3",children:[s.messages.length===0&&s.status==="open"&&e.jsxs("div",{className:"flex flex-col items-center gap-2 py-16 text-center",children:[e.jsx("div",{className:"flex h-12 w-12 items-center justify-center rounded-full bg-surface-2 text-2xl",children:"💬"}),e.jsx("h2",{className:"text-base font-semibold",children:a("chat.welcomeTitle",{defaultValue:"Start a conversation"})}),e.jsx("p",{className:"max-w-xs text-sm text-text-dim",children:a("chat.welcomeBodyMobile",{defaultValue:"Type a message below. Switch agent or start over from the ⋯ menu."})})]}),s.messages.length===0&&s.status!=="open"&&e.jsx("div",{className:"grid place-items-center py-16 text-center",children:e.jsx("p",{className:"text-sm text-text-dim",children:s.status==="connecting"?a("chat.connecting",{defaultValue:"Connecting to agim…"}):a("chat.disconnected",{defaultValue:"Disconnected. Reconnecting automatically."})})}),s.messages.filter(t=>!t.approval).map(t=>e.jsx(D,{msg:t,onApprove:(n,r,T)=>f(n,r,T)},t.id))]})}),w&&e.jsx("button",{type:"button",onClick:C,className:"absolute bottom-24 left-1/2 z-20 -translate-x-1/2 rounded-full border border-border bg-surface p-2 shadow-md","aria-label":a("chat.scrollDown",{defaultValue:"Scroll to bottom"}),children:e.jsx(I,{className:"h-4 w-4"})}),e.jsx(M,{approvals:s.pendingApprovals,onApprove:(t,n,r)=>f(t,n,r)}),e.jsxs("form",{onSubmit:k,className:"sticky bottom-0 border-t border-border bg-surface px-3 py-2",style:{paddingBottom:"calc(env(safe-area-inset-bottom) + 0.5rem)"},children:[e.jsxs("div",{ref:N,className:"flex items-end gap-2",children:[e.jsx("textarea",{ref:u,value:o,onChange:t=>x(t.target.value),onKeyDown:S,placeholder:a("chat.inputPlaceholderMobile",{defaultValue:"Message…"}),rows:1,className:p("flex-1 resize-none rounded-2xl border border-border bg-bg","px-3 py-2 text-[15px] leading-6","focus:outline-none focus:ring-2 focus:ring-accent focus:ring-offset-1 focus:ring-offset-bg")}),e.jsx(m,{type:"submit",disabled:!o.trim()||s.status!=="open","aria-label":a("chat.send",{defaultValue:"Send"}),className:"h-10 w-10 shrink-0 rounded-full p-0",children:e.jsx(H,{className:"h-4 w-4"})})]}),s.currentReplyId&&e.jsx("div",{className:"mt-2 flex items-center gap-2",children:e.jsx(L,{variant:"info",children:a("chat.streaming",{defaultValue:"… streaming"})})})]})]})}export{K as default};
|
|
7
|
-
//# sourceMappingURL=chat-
|
|
7
|
+
//# sourceMappingURL=chat-Dz9kfaxH.js.map
|