chainlesschain 0.161.11 → 0.161.12
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/package.json +4 -1
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/{AIOps-Cv7DnNSv.js → AIOps-B1dwBvzW.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-B1m9njbz.js → ActionButton-DfR4oLvh.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-CU5lrpIq.js → Analytics-D3ZYGXjr.js} +1 -1
- package/src/assets/web-panel/assets/AppLayout-CsmOoh-7.js +3 -0
- package/src/assets/web-panel/assets/{AppLayout-BpbfrB7k.css → AppLayout-DBgtkmP7.css} +1 -1
- package/src/assets/web-panel/assets/{Audit-DUkzhAbX.js → Audit-B4gwDm63.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-L1i5rG4e.js → Backup-T42uSArV.js} +1 -1
- package/src/assets/web-panel/assets/BaseInput-CFi52nMs.js +1 -0
- package/src/assets/web-panel/assets/{Chat-ySuja9_N.js → Chat-D7Vvok1V.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-5jHkbotn.js → Checkbox-Dwaflpww.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-CpTSti2I.js → Codegen-BIqx3G0b.js} +1 -1
- package/src/assets/web-panel/assets/{Col-C5SJSGcO.js → Col-DzIUYUNu.js} +1 -1
- package/src/assets/web-panel/assets/{Community-0PMz9hPX.js → Community-DKePtzhk.js} +1 -1
- package/src/assets/web-panel/assets/Compact-CirVV9Wq.js +1 -0
- package/src/assets/web-panel/assets/{Compliance-BEVqh-i7.js → Compliance-CrXOr0sy.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-C_UEYiR5.js → Cowork-BEBP6Tht.js} +1 -1
- package/src/assets/web-panel/assets/{Cron-B4f0_E2W.js → Cron-k7nNUuqh.js} +1 -1
- package/src/assets/web-panel/assets/{Crosschain-CZT6FKhl.js → Crosschain-CBnX0Dhq.js} +1 -1
- package/src/assets/web-panel/assets/{DID-LDSJdg_E.js → DID-B48FszWS.js} +1 -1
- package/src/assets/web-panel/assets/{Dashboard-CczHBzZy.js → Dashboard-Pb3qfFpp.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-KJXQPCdN.js → Dropdown-pUHy4CQ2.js} +1 -1
- package/src/assets/web-panel/assets/Federation-CT0Qs-kR.js +1 -0
- package/src/assets/web-panel/assets/{FormItemContext-CenKDKRv.js → FormItemContext-CbJJp5BR.js} +1 -1
- package/src/assets/web-panel/assets/{Git-Ck3yvIOj.js → Git-B3mGNLQe.js} +1 -1
- package/src/assets/web-panel/assets/{Governance-C-Wqvp5q.js → Governance-BKf4733q.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-D1Q4GQsE.js → Inference-DZjU541G.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-6-id5kqA.js → KnowledgeGraph-C8L-7Dd1.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-BIrBldyi.js → Logs-CwDwOiKv.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-DuEYH2k1.js → Marketplace-C2YWWU0M.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-CJyrjpOa.js → McpTools-dIbkOypF.js} +1 -1
- package/src/assets/web-panel/assets/{Memory-25VBuGGA.js → Memory-7eF8WzcY.js} +1 -1
- package/src/assets/web-panel/assets/{MobileBridge-DDepR_yK.js → MobileBridge-C74GHLbX.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-B52jkX63.js → Mtc-CSEDo5Fo.js} +1 -1
- package/src/assets/web-panel/assets/{MtcAudit-BimrvH_0.js → MtcAudit-DiJXxOrB.js} +6 -6
- package/src/assets/web-panel/assets/{Multisig-DWsGuuyz.js → Multisig-FZTU5ri6.js} +1 -1
- package/src/assets/web-panel/assets/{NLProgramming-D9sqtzgg.js → NLProgramming-DjF-gIUw.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-BtaoLR6l.js → Notes-BUE5CvMO.js} +1 -1
- package/src/assets/web-panel/assets/{NotificationSettings-DGc4Pnhc.js → NotificationSettings-Dfbrobje.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-CH8YH1WC.js → Organization-C6YvqjQB.js} +1 -1
- package/src/assets/web-panel/assets/{Overflow-BEfCQNlN.js → Overflow-BvHNhdMR.js} +1 -1
- package/src/assets/web-panel/assets/{OverrideContext-DYxbd09I.js → OverrideContext-x9ZzjLwk.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-Bp_HTJ00.js → P2P-BO0hQHFS.js} +1 -1
- package/src/assets/web-panel/assets/{Permissions-UHQF_4gL.js → Permissions-CCPlrJeP.js} +1 -1
- package/src/assets/web-panel/assets/{Pipeline-yCVADMbj.js → Pipeline-DTCL3FjJ.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-C1oNG_1P.js → Privacy-08DYgOe_.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-DMW2d6Cb.js → ProjectInit-B7j-Z8sa.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-5G8pJeC_.js → ProjectSettings-CFqLhV1w.js} +1 -1
- package/src/assets/web-panel/assets/Projects-0OrfK90x.css +1 -0
- package/src/assets/web-panel/assets/Projects-BPlpx2UN.js +1 -0
- package/src/assets/web-panel/assets/{Providers-CL_oE5tl.js → Providers-BCBPbVbF.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-BV38kJpn.js → QuickAsk-C8Job6zl.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-SAj9Q6gO.js → Recommend-DOV_Aje-.js} +1 -1
- package/src/assets/web-panel/assets/{Reputation-1kCD0gaB.js → Reputation-DOgK_dIK.js} +1 -1
- package/src/assets/web-panel/assets/Row-8jdU1xYg.js +1 -0
- package/src/assets/web-panel/assets/{RssFeed-CAJCjHjh.js → RssFeed-B289OgNZ.js} +1 -1
- package/src/assets/web-panel/assets/{Search-cvcqK6k5.js → Search-cr0AndFE.js} +1 -1
- package/src/assets/web-panel/assets/{Security-CQg7wTKN.js → Security-w4diykaE.js} +1 -1
- package/src/assets/web-panel/assets/{Services-CsUFvNqx.js → Services-dTxAI5cG.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-xBd6cpeg.js → Skeleton-B3FiUiRo.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-CaroVcXP.js → Skills-DgtBTAFC.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-BtpCM7Ko.js → Sla-B4Y6bvbL.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-BqMJfygi.js → SpeechSettings-C8FEvArc.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-CZouEnTT.js → SyncSettings-CHvV5L0m.js} +1 -1
- package/src/assets/web-panel/assets/{Tasks-CXRSaKbt.js → Tasks-B4Py5ORS.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-DJY8TQ5i.js → Templates-MeTyXM5u.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-BvR-WVDe.js → Tenant-Doiz7wyQ.js} +1 -1
- package/src/assets/web-panel/assets/Terminal-DSEAdwWN.js +3 -0
- package/src/assets/web-panel/assets/Terminal-G1DrFtKr.css +1 -0
- package/src/assets/web-panel/assets/{Tokens-BvsTsita.js → Tokens-BBrtaEKt.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-BpAXwpX5.js → Trigger-nPvjho27.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-CQn3-pr1.js → Trust-BFwPyS_6.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-mdcVVgmW.js → UkeySign-DFsHoY1J.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-B64a6mxs.js → VideoEditing-FkSKqHE9.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-CD6sh797.js → Wallet-AXo-_4-w.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-CBMAhuJk.js → WebAuthn-DoScnQ-4.js} +1 -1
- package/src/assets/web-panel/assets/{WorkflowEditor-BL2e4pEf.js → WorkflowEditor-IEMfIax-.js} +1 -1
- package/src/assets/web-panel/assets/addon-fit-CK6X9sAG.js +1 -0
- package/src/assets/web-panel/assets/{chat-D0R75-Vf.js → chat-pc1ciH6T.js} +1 -1
- package/src/assets/web-panel/assets/{collapseMotion-CZQhO80h.js → collapseMotion-CjFH_Jop.js} +1 -1
- package/src/assets/web-panel/assets/{colors-Z5R3KUYW.js → colors-CjkIkB0e.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-Buyen0aR.js → compact-item-CQZnkP5p.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-D9l1X-OX.js → createContext-B1McGnV-.js} +1 -1
- package/src/assets/web-panel/assets/{echarts-TwhxblyG.js → echarts-Bq-n0MtJ.js} +1 -1
- package/src/assets/web-panel/assets/{hasIn-DV4wtumO.js → hasIn-CbkA6peP.js} +1 -1
- package/src/assets/web-panel/assets/{icons-Q99MnhG8.js → icons-NT6gy8Ee.js} +2 -2
- package/src/assets/web-panel/assets/{index-0P8ZPtKp.js → index-5c4JzwY3.js} +1 -1
- package/src/assets/web-panel/assets/{index-CoV9UGnc.js → index-B8r6OBuk.js} +2 -2
- package/src/assets/web-panel/assets/{index-BAf0-ayI.js → index-BNLeseG1.js} +2 -2
- package/src/assets/web-panel/assets/index-BTyhXFDW.js +1 -0
- package/src/assets/web-panel/assets/index-BXfePRef.js +1 -0
- package/src/assets/web-panel/assets/index-Bs-romIz.js +1 -0
- package/src/assets/web-panel/assets/index-C052wi94.js +1 -0
- package/src/assets/web-panel/assets/{index-BBFrPlY4.js → index-C0Lj7Yl0.js} +1 -1
- package/src/assets/web-panel/assets/{index-CT46jhtC.js → index-C5mpCgak.js} +1 -1
- package/src/assets/web-panel/assets/{index-D_g5wyjB.js → index-CEENN81t.js} +1 -1
- package/src/assets/web-panel/assets/{index-CRvOaSuy.js → index-CFCQl1hk.js} +2 -2
- package/src/assets/web-panel/assets/{index-DMcK4Son.js → index-CGke5qZp.js} +1 -1
- package/src/assets/web-panel/assets/index-CJbqE5Sw.js +1 -0
- package/src/assets/web-panel/assets/index-CJr12ypE.js +21 -0
- package/src/assets/web-panel/assets/{index-Z-i_sfIN.js → index-CKledOCh.js} +1 -1
- package/src/assets/web-panel/assets/index-CKwkP66s.js +13 -0
- package/src/assets/web-panel/assets/{index--pPzkOu2.js → index-ChCCGHwz.js} +1 -1
- package/src/assets/web-panel/assets/{index-CprQ5h5e.js → index-CzZ3LxPK.js} +1 -1
- package/src/assets/web-panel/assets/{index-BLDanIEV.js → index-D4rhVRSV.js} +1 -1
- package/src/assets/web-panel/assets/index-DGb36exe.js +3 -0
- package/src/assets/web-panel/assets/{index-DBBX0xZN.js → index-DHpwwlmv.js} +1 -1
- package/src/assets/web-panel/assets/index-DHtiecyM.js +1 -0
- package/src/assets/web-panel/assets/{index-UAkLBWLE.js → index-DJWYN-AT.js} +2 -2
- package/src/assets/web-panel/assets/{index-Cs2QeQFi.js → index-DMjPzhsp.js} +2 -2
- package/src/assets/web-panel/assets/{index-BRWO_MUe.js → index-DPzT5L14.js} +4 -4
- package/src/assets/web-panel/assets/{index-vSjbNv8F.js → index-DWq64zmv.js} +1 -1
- package/src/assets/web-panel/assets/{index-BSXvEQFS.js → index-DYAWIXFt.js} +1 -1
- package/src/assets/web-panel/assets/index-Dc4fj_Ys.js +1 -0
- package/src/assets/web-panel/assets/{index-RpJYGfg0.js → index-DcSe5y5O.js} +1 -1
- package/src/assets/web-panel/assets/{index-AXmFZTsT.js → index-Di5tuS0d.js} +2 -2
- package/src/assets/web-panel/assets/{index-CHs1Rtgi.js → index-DjM0jsm1.js} +2 -2
- package/src/assets/web-panel/assets/{index-DO9gd9wz.js → index-DjxVsyn_.js} +1 -1
- package/src/assets/web-panel/assets/index-Dn7SHV2e.js +55 -0
- package/src/assets/web-panel/assets/index-DsChgzu2.js +12 -0
- package/src/assets/web-panel/assets/{index-CpemtT_B.js → index-Dzfj71tf.js} +2 -2
- package/src/assets/web-panel/assets/index-NRWBOo4F.js +1 -0
- package/src/assets/web-panel/assets/{index-DtRv6Tqv.js → index-fqI1KnP_.js} +8 -8
- package/src/assets/web-panel/assets/{index-Cs7GKmFk.js → index-uWvLy_3T.js} +6 -6
- package/src/assets/web-panel/assets/{index-C4m8PjHn.js → index-wwQ_ZkWN.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-C5tyEoyp.js → initDefaultProps-BTloFqyf.js} +1 -1
- package/src/assets/web-panel/assets/{motion-By5VpSMZ.js → motion-44iMBc1o.js} +2 -2
- package/src/assets/web-panel/assets/{move-CNRFLi9k.js → move-CyWQI5eW.js} +1 -1
- package/src/assets/web-panel/assets/{omit-DYc7DRhT.js → omit-CXfJtuFy.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-D667btUF.js → pickAttrs-ANFpSZes.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-Ce-Y2-Qq.js → placementArrow-CigX-url.js} +1 -1
- package/src/assets/web-panel/assets/responsiveObserve-Ch48RA0m.js +1 -0
- package/src/assets/web-panel/assets/{slide-CyKGKfJ8.js → slide-BEz3LORF.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-DffXAKmJ.js → statusUtils-D9EniG8V.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-CeeSLisW.js → styleChecker-C1IvuY3L.js} +1 -1
- package/src/assets/web-panel/assets/useFlexGapSupport-C92oHeXB.js +1 -0
- package/src/assets/web-panel/assets/{useFs--Vkrwu51.js → useFs-CJhvFpgB.js} +1 -1
- package/src/assets/web-panel/assets/{useMergedState-D3PX36GT.js → useMergedState-O7QXt4P5.js} +1 -1
- package/src/assets/web-panel/assets/{useRefs-opfcK7Pm.js → useRefs-0J6m8UWN.js} +1 -1
- package/src/assets/web-panel/assets/{useState-Cp5WlLTS.js → useState-CSzR8F8O.js} +1 -1
- package/src/assets/web-panel/assets/{vendor-BVLz_z7V.js → vendor-M5lGV-wr.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-ChnNrJNw.js → vnode-BPkyunN_.js} +1 -1
- package/src/assets/web-panel/assets/xterm-BZcWGsqw.js +9 -0
- package/src/assets/web-panel/assets/xterm-DFuMZ0ql.css +1 -0
- package/src/assets/web-panel/assets/{zoom-DE1Teifl.js → zoom-B6Ipb64r.js} +1 -1
- package/src/assets/web-panel/index.html +3 -3
- package/src/gateways/terminal/PtyManager.js +248 -0
- package/src/gateways/terminal/RingBuffer.js +61 -0
- package/src/gateways/terminal/terminal-handlers.js +121 -0
- package/src/gateways/ws/topic-handler-attachment.js +211 -0
- package/src/runtime/agent-runtime.js +33 -0
- package/src/assets/web-panel/assets/AppLayout-CrQBcjC1.js +0 -3
- package/src/assets/web-panel/assets/BaseInput-CQSPRZVh.js +0 -1
- package/src/assets/web-panel/assets/Compact-CwrJK_94.js +0 -1
- package/src/assets/web-panel/assets/Federation-Bu085RVF.js +0 -1
- package/src/assets/web-panel/assets/Projects-BpLpgCga.js +0 -1
- package/src/assets/web-panel/assets/Projects-C34BWmmy.css +0 -1
- package/src/assets/web-panel/assets/Row-CUF6ofec.js +0 -1
- package/src/assets/web-panel/assets/index-BBvRNeKy.js +0 -1
- package/src/assets/web-panel/assets/index-BJzerWCW.js +0 -1
- package/src/assets/web-panel/assets/index-BbZ_pXN3.js +0 -1
- package/src/assets/web-panel/assets/index-BloaIh8i.js +0 -1
- package/src/assets/web-panel/assets/index-C1u_F_WR.js +0 -12
- package/src/assets/web-panel/assets/index-CHPsWSeL.js +0 -1
- package/src/assets/web-panel/assets/index-CLk9-Adx.js +0 -13
- package/src/assets/web-panel/assets/index-Cyal1Os0.js +0 -1
- package/src/assets/web-panel/assets/index-DYq0yh8a.js +0 -1
- package/src/assets/web-panel/assets/index-KvlSpGws.js +0 -21
- package/src/assets/web-panel/assets/index-WbY1RGjg.js +0 -3
- package/src/assets/web-panel/assets/index-cTf60nCj.js +0 -1
- package/src/assets/web-panel/assets/index-ry4j00pG.js +0 -55
- package/src/assets/web-panel/assets/responsiveObserve-BzzJzAds.js +0 -1
- package/src/assets/web-panel/assets/useFlexGapSupport-CCzODKgI.js +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.xterm{cursor:text;position:relative;user-select:none;-ms-user-select:none;-webkit-user-select:none}.xterm.focus,.xterm:focus{outline:none}.xterm .xterm-helpers{position:absolute;top:0;z-index:5}.xterm .xterm-helper-textarea{padding:0;border:0;margin:0;position:absolute;opacity:0;left:-9999em;top:0;width:0;height:0;z-index:-5;white-space:nowrap;overflow:hidden;resize:none}.xterm .composition-view{background:#000;color:#fff;display:none;position:absolute;white-space:nowrap;z-index:1}.xterm .composition-view.active{display:block}.xterm .xterm-viewport{background-color:#000;overflow-y:scroll;cursor:default;position:absolute;inset:0}.xterm .xterm-screen{position:relative}.xterm .xterm-screen canvas{position:absolute;left:0;top:0}.xterm .xterm-scroll-area{visibility:hidden}.xterm-char-measure-element{display:inline-block;visibility:hidden;position:absolute;top:0;left:-9999em;line-height:normal}.xterm.enable-mouse-events{cursor:default}.xterm.xterm-cursor-pointer,.xterm .xterm-cursor-pointer{cursor:pointer}.xterm.column-select.focus{cursor:crosshair}.xterm .xterm-accessibility:not(.debug),.xterm .xterm-message{position:absolute;inset:0;z-index:10;color:transparent;pointer-events:none}.xterm .xterm-accessibility-tree:not(.debug) *::selection{color:transparent}.xterm .xterm-accessibility-tree{-webkit-user-select:text;user-select:text;white-space:pre}.xterm .live-region{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}.xterm-dim{opacity:1!important}.xterm-underline-1{text-decoration:underline}.xterm-underline-2{text-decoration:double underline}.xterm-underline-3{text-decoration:wavy underline}.xterm-underline-4{text-decoration:dotted underline}.xterm-underline-5{text-decoration:dashed underline}.xterm-overline{text-decoration:overline}.xterm-overline.xterm-underline-1{text-decoration:overline underline}.xterm-overline.xterm-underline-2{text-decoration:overline double underline}.xterm-overline.xterm-underline-3{text-decoration:overline wavy underline}.xterm-overline.xterm-underline-4{text-decoration:overline dotted underline}.xterm-overline.xterm-underline-5{text-decoration:overline dashed underline}.xterm-strikethrough{text-decoration:line-through}.xterm-screen .xterm-decoration-container .xterm-decoration{z-index:6;position:absolute}.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer{z-index:7}.xterm-decoration-overview-ruler{z-index:8;position:absolute;top:0;right:0;pointer-events:none}.xterm-decoration-top{z-index:2;position:relative}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{K as o}from"./index-
|
|
1
|
+
import{K as o}from"./index-DPzT5L14.js";import{i as f}from"./motion-44iMBc1o.js";const c=new o("antZoomIn",{"0%":{transform:"scale(0.2)",opacity:0},"100%":{transform:"scale(1)",opacity:1}}),y=new o("antZoomOut",{"0%":{transform:"scale(1)"},"100%":{transform:"scale(0.2)",opacity:0}}),a=new o("antZoomBigIn",{"0%":{transform:"scale(0.8)",opacity:0},"100%":{transform:"scale(1)",opacity:1}}),s=new o("antZoomBigOut",{"0%":{transform:"scale(1)"},"100%":{transform:"scale(0.8)",opacity:0}}),g=new o("antZoomUpIn",{"0%":{transform:"scale(0.8)",transformOrigin:"50% 0%",opacity:0},"100%":{transform:"scale(1)",transformOrigin:"50% 0%"}}),O=new o("antZoomUpOut",{"0%":{transform:"scale(1)",transformOrigin:"50% 0%"},"100%":{transform:"scale(0.8)",transformOrigin:"50% 0%",opacity:0}}),l=new o("antZoomLeftIn",{"0%":{transform:"scale(0.8)",transformOrigin:"0% 50%",opacity:0},"100%":{transform:"scale(1)",transformOrigin:"0% 50%"}}),u=new o("antZoomLeftOut",{"0%":{transform:"scale(1)",transformOrigin:"0% 50%"},"100%":{transform:"scale(0.8)",transformOrigin:"0% 50%",opacity:0}}),p=new o("antZoomRightIn",{"0%":{transform:"scale(0.8)",transformOrigin:"100% 50%",opacity:0},"100%":{transform:"scale(1)",transformOrigin:"100% 50%"}}),z=new o("antZoomRightOut",{"0%":{transform:"scale(1)",transformOrigin:"100% 50%"},"100%":{transform:"scale(0.8)",transformOrigin:"100% 50%",opacity:0}}),K=new o("antZoomDownIn",{"0%":{transform:"scale(0.8)",transformOrigin:"50% 100%",opacity:0},"100%":{transform:"scale(1)",transformOrigin:"50% 100%"}}),w=new o("antZoomDownOut",{"0%":{transform:"scale(1)",transformOrigin:"50% 100%"},"100%":{transform:"scale(0.8)",transformOrigin:"50% 100%",opacity:0}}),I={zoom:{inKeyframes:c,outKeyframes:y},"zoom-big":{inKeyframes:a,outKeyframes:s},"zoom-big-fast":{inKeyframes:a,outKeyframes:s},"zoom-left":{inKeyframes:l,outKeyframes:u},"zoom-right":{inKeyframes:p,outKeyframes:z},"zoom-up":{inKeyframes:g,outKeyframes:O},"zoom-down":{inKeyframes:K,outKeyframes:w}},h=(n,r)=>{const{antCls:m}=n,t=`${m}-${r}`,{inKeyframes:i,outKeyframes:e}=I[r];return[f(t,i,e,r==="zoom-big-fast"?n.motionDurationFast:n.motionDurationMid),{[`
|
|
2
2
|
${t}-enter,
|
|
3
3
|
${t}-appear
|
|
4
4
|
`]:{transform:"scale(0)",opacity:0,animationTimingFunction:n.motionEaseOutCirc,"&-prepare":{transform:"none"}},[`${t}-leave`]:{animationTimingFunction:n.motionEaseInOutCirc}}]};export{h as i,c as z};
|
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
// Injected by web-ui-server.js at serve time
|
|
9
9
|
window.__CC_CONFIG__ = __CC_CONFIG_PLACEHOLDER__;
|
|
10
10
|
</script>
|
|
11
|
-
<script type="module" crossorigin src="./assets/index-
|
|
12
|
-
<link rel="modulepreload" crossorigin href="./assets/vendor-
|
|
13
|
-
<link rel="modulepreload" crossorigin href="./assets/icons-
|
|
11
|
+
<script type="module" crossorigin src="./assets/index-DPzT5L14.js"></script>
|
|
12
|
+
<link rel="modulepreload" crossorigin href="./assets/vendor-M5lGV-wr.js">
|
|
13
|
+
<link rel="modulepreload" crossorigin href="./assets/icons-NT6gy8Ee.js">
|
|
14
14
|
<link rel="stylesheet" crossorigin href="./assets/index-CfX1DEtk.css">
|
|
15
15
|
</head>
|
|
16
16
|
<body>
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PtyManager — ESM mirror of `desktop-app-vue/src/main/terminal/PtyManager.js`.
|
|
3
|
+
*
|
|
4
|
+
* Keep both copies in sync. The desktop copy targets Electron's CJS main
|
|
5
|
+
* process; this one targets `cc ui` (Node ESM) so `cc ui` users also get
|
|
6
|
+
* remote-terminal support via the same WS protocol.
|
|
7
|
+
*
|
|
8
|
+
* Design notes — see docs/design/Android_Remote_Terminal_Plan_A.md §4:
|
|
9
|
+
* - node-pty loaded lazily so the module loads without the native
|
|
10
|
+
* binding installed (tests, headless CI, machines without it).
|
|
11
|
+
* - Per-session RingBuffer (256 KiB default, memory-only — no disk
|
|
12
|
+
* persistence: terminal stdout often contains secrets).
|
|
13
|
+
* - EventEmitter fans out `stdout` / `exit` so handlers translate to
|
|
14
|
+
* WS envelopes without coupling.
|
|
15
|
+
* - Idle kill default 24h, resets on stdin OR stdout activity.
|
|
16
|
+
* - Shell whitelist enforced at create() — unknown shell → throws
|
|
17
|
+
* `shell_not_allowed`.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import EventEmitter from "events";
|
|
21
|
+
import { randomUUID } from "crypto";
|
|
22
|
+
import { createRequire } from "module";
|
|
23
|
+
import { RingBuffer } from "./RingBuffer.js";
|
|
24
|
+
|
|
25
|
+
const require = createRequire(import.meta.url);
|
|
26
|
+
|
|
27
|
+
export const DEFAULT_CONFIG = Object.freeze({
|
|
28
|
+
shellWhitelist: ["pwsh", "cmd", "bash", "wsl"],
|
|
29
|
+
defaultShell: process.platform === "win32" ? "pwsh" : "bash",
|
|
30
|
+
defaultCwd: null,
|
|
31
|
+
maxConcurrentSessions: 8,
|
|
32
|
+
ringBufferBytes: 256 * 1024,
|
|
33
|
+
idleKillMs: 24 * 60 * 60 * 1000,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
function resolveShellCmd(shell) {
|
|
37
|
+
if (process.platform === "win32") {
|
|
38
|
+
if (shell === "cmd") return process.env.ComSpec || "cmd.exe";
|
|
39
|
+
if (shell === "pwsh") return "pwsh.exe";
|
|
40
|
+
if (shell === "wsl") return "wsl.exe";
|
|
41
|
+
if (shell === "bash") return "bash.exe";
|
|
42
|
+
}
|
|
43
|
+
return shell;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
class PtySession {
|
|
47
|
+
constructor({ id, shell, cwd, cols, rows, proc, createdAt, ringBuffer }) {
|
|
48
|
+
this.id = id;
|
|
49
|
+
this.shell = shell;
|
|
50
|
+
this.cwd = cwd;
|
|
51
|
+
this.cols = cols;
|
|
52
|
+
this.rows = rows;
|
|
53
|
+
this.proc = proc;
|
|
54
|
+
this.createdAt = createdAt;
|
|
55
|
+
this.ringBuffer = ringBuffer;
|
|
56
|
+
this.alive = true;
|
|
57
|
+
this.exitCode = null;
|
|
58
|
+
this.signal = null;
|
|
59
|
+
this.lastActivityAt = createdAt;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export class PtyManager extends EventEmitter {
|
|
64
|
+
constructor(opts = {}) {
|
|
65
|
+
super();
|
|
66
|
+
this.config = { ...DEFAULT_CONFIG, ...(opts.config || {}) };
|
|
67
|
+
this._deps = {
|
|
68
|
+
loadNodePty: opts._deps?.loadNodePty || (() => require("node-pty")),
|
|
69
|
+
now: opts._deps?.now || (() => Date.now()),
|
|
70
|
+
};
|
|
71
|
+
this._sessions = new Map();
|
|
72
|
+
this._idleTimer = null;
|
|
73
|
+
this._stopped = false;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
create(req = {}) {
|
|
77
|
+
if (this._stopped) throw new Error("pty_manager_stopped");
|
|
78
|
+
if (this._sessions.size >= this.config.maxConcurrentSessions) {
|
|
79
|
+
throw new Error("max_concurrent_sessions_exceeded");
|
|
80
|
+
}
|
|
81
|
+
const shell = req.shell || this.config.defaultShell;
|
|
82
|
+
if (!this.config.shellWhitelist.includes(shell)) {
|
|
83
|
+
throw new Error("shell_not_allowed");
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
let pty;
|
|
87
|
+
try {
|
|
88
|
+
pty = this._deps.loadNodePty();
|
|
89
|
+
} catch {
|
|
90
|
+
const err = new Error("pty_native_unavailable");
|
|
91
|
+
err.cause = "node-pty failed to load (native binding missing)";
|
|
92
|
+
throw err;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const cwd = req.cwd || this.config.defaultCwd || process.cwd();
|
|
96
|
+
const cols = Number.isFinite(req.cols) ? req.cols : 80;
|
|
97
|
+
const rows = Number.isFinite(req.rows) ? req.rows : 24;
|
|
98
|
+
const cmd = resolveShellCmd(shell);
|
|
99
|
+
|
|
100
|
+
const proc = pty.spawn(cmd, [], {
|
|
101
|
+
name: "xterm-256color",
|
|
102
|
+
cols,
|
|
103
|
+
rows,
|
|
104
|
+
cwd,
|
|
105
|
+
env: { ...process.env, ...(req.env || {}) },
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
const sessionId = randomUUID();
|
|
109
|
+
const session = new PtySession({
|
|
110
|
+
id: sessionId,
|
|
111
|
+
shell,
|
|
112
|
+
cwd,
|
|
113
|
+
cols,
|
|
114
|
+
rows,
|
|
115
|
+
proc,
|
|
116
|
+
createdAt: this._deps.now(),
|
|
117
|
+
ringBuffer: new RingBuffer({ maxBytes: this.config.ringBufferBytes }),
|
|
118
|
+
});
|
|
119
|
+
this._sessions.set(sessionId, session);
|
|
120
|
+
|
|
121
|
+
proc.onData((data) => {
|
|
122
|
+
const buf = typeof data === "string" ? Buffer.from(data, "utf-8") : data;
|
|
123
|
+
const seq = session.ringBuffer.push(buf);
|
|
124
|
+
session.lastActivityAt = this._deps.now();
|
|
125
|
+
this.emit("stdout", { sessionId, data: buf, seq });
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
proc.onExit(({ exitCode, signal }) => {
|
|
129
|
+
session.alive = false;
|
|
130
|
+
session.exitCode = exitCode ?? null;
|
|
131
|
+
session.signal = signal ?? null;
|
|
132
|
+
this.emit("exit", {
|
|
133
|
+
sessionId,
|
|
134
|
+
exitCode: session.exitCode,
|
|
135
|
+
signal: session.signal,
|
|
136
|
+
});
|
|
137
|
+
setTimeout(() => {
|
|
138
|
+
if (this._sessions.get(sessionId) === session) {
|
|
139
|
+
this._sessions.delete(sessionId);
|
|
140
|
+
}
|
|
141
|
+
}, 60 * 1000).unref?.();
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
this._ensureIdleSweeper();
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
sessionId,
|
|
148
|
+
pid: proc.pid,
|
|
149
|
+
shell,
|
|
150
|
+
createdAt: session.createdAt,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
write(sessionId, data) {
|
|
155
|
+
const session = this._sessions.get(sessionId);
|
|
156
|
+
if (!session) throw new Error("session_not_found");
|
|
157
|
+
if (!session.alive) throw new Error("session_not_alive");
|
|
158
|
+
const str = Buffer.isBuffer(data) ? data.toString("utf-8") : data;
|
|
159
|
+
session.proc.write(str);
|
|
160
|
+
session.lastActivityAt = this._deps.now();
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
resize(sessionId, cols, rows) {
|
|
164
|
+
const session = this._sessions.get(sessionId);
|
|
165
|
+
if (!session) throw new Error("session_not_found");
|
|
166
|
+
if (!session.alive) throw new Error("session_not_alive");
|
|
167
|
+
if (!Number.isFinite(cols) || !Number.isFinite(rows)) {
|
|
168
|
+
throw new Error("invalid_dimensions");
|
|
169
|
+
}
|
|
170
|
+
session.proc.resize(cols, rows);
|
|
171
|
+
session.cols = cols;
|
|
172
|
+
session.rows = rows;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
close(sessionId) {
|
|
176
|
+
const session = this._sessions.get(sessionId);
|
|
177
|
+
if (!session) throw new Error("session_not_found");
|
|
178
|
+
if (session.alive) {
|
|
179
|
+
try {
|
|
180
|
+
session.proc.kill();
|
|
181
|
+
} catch {
|
|
182
|
+
/* onExit will mark dead */
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
list() {
|
|
188
|
+
const out = [];
|
|
189
|
+
for (const s of this._sessions.values()) {
|
|
190
|
+
out.push({
|
|
191
|
+
id: s.id,
|
|
192
|
+
shell: s.shell,
|
|
193
|
+
cwd: s.cwd,
|
|
194
|
+
createdAt: s.createdAt,
|
|
195
|
+
alive: s.alive,
|
|
196
|
+
lastSeq: s.ringBuffer.lastSeq,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
return out;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
history(sessionId, fromSeq = 0) {
|
|
203
|
+
const session = this._sessions.get(sessionId);
|
|
204
|
+
if (!session) throw new Error("session_not_found");
|
|
205
|
+
return session.ringBuffer.since(fromSeq);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
_ensureIdleSweeper() {
|
|
209
|
+
if (this._idleTimer) return;
|
|
210
|
+
this._idleTimer = setInterval(() => this._sweepIdle(), 60 * 60 * 1000);
|
|
211
|
+
this._idleTimer.unref?.();
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
_sweepIdle() {
|
|
215
|
+
const now = this._deps.now();
|
|
216
|
+
for (const session of this._sessions.values()) {
|
|
217
|
+
if (!session.alive) continue;
|
|
218
|
+
if (now - session.lastActivityAt > this.config.idleKillMs) {
|
|
219
|
+
try {
|
|
220
|
+
session.proc.kill();
|
|
221
|
+
} catch {
|
|
222
|
+
/* onExit will clean up */
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
shutdown() {
|
|
229
|
+
this._stopped = true;
|
|
230
|
+
if (this._idleTimer) {
|
|
231
|
+
clearInterval(this._idleTimer);
|
|
232
|
+
this._idleTimer = null;
|
|
233
|
+
}
|
|
234
|
+
for (const session of this._sessions.values()) {
|
|
235
|
+
if (session.alive) {
|
|
236
|
+
try {
|
|
237
|
+
session.proc.kill();
|
|
238
|
+
} catch {
|
|
239
|
+
/* best effort */
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
get _sessionCount() {
|
|
246
|
+
return this._sessions.size;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RingBuffer — bounded byte-aware FIFO for PTY stdout chunks.
|
|
3
|
+
*
|
|
4
|
+
* ESM mirror of `desktop-app-vue/src/main/terminal/RingBuffer.js`. Both
|
|
5
|
+
* shells (Electron web-shell + `cc ui`) ship their own copy because the
|
|
6
|
+
* runtime module systems are incompatible (CJS vs ESM). Keep them in
|
|
7
|
+
* sync — future consolidation goes into a workspace package once the
|
|
8
|
+
* surface stabilises.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export class RingBuffer {
|
|
12
|
+
constructor(opts = {}) {
|
|
13
|
+
const maxBytes = opts.maxBytes ?? 256 * 1024;
|
|
14
|
+
if (typeof maxBytes !== "number" || maxBytes <= 0) {
|
|
15
|
+
throw new TypeError("RingBuffer: maxBytes must be a positive number");
|
|
16
|
+
}
|
|
17
|
+
this.maxBytes = maxBytes;
|
|
18
|
+
this._entries = [];
|
|
19
|
+
this._totalBytes = 0;
|
|
20
|
+
this._lastSeq = 0;
|
|
21
|
+
this._firstRetainedSeq = 1;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
push(data) {
|
|
25
|
+
const buf = Buffer.isBuffer(data)
|
|
26
|
+
? data
|
|
27
|
+
: typeof data === "string"
|
|
28
|
+
? Buffer.from(data, "utf-8")
|
|
29
|
+
: Buffer.from(data);
|
|
30
|
+
this._lastSeq += 1;
|
|
31
|
+
const seq = this._lastSeq;
|
|
32
|
+
this._entries.push({ seq, data: buf });
|
|
33
|
+
this._totalBytes += buf.byteLength;
|
|
34
|
+
this._evictUntilUnderCap();
|
|
35
|
+
return seq;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
_evictUntilUnderCap() {
|
|
39
|
+
while (this._totalBytes > this.maxBytes && this._entries.length > 0) {
|
|
40
|
+
const evicted = this._entries.shift();
|
|
41
|
+
this._totalBytes -= evicted.data.byteLength;
|
|
42
|
+
this._firstRetainedSeq = evicted.seq + 1;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
since(fromSeq = 0) {
|
|
47
|
+
const truncated = fromSeq > 0 && fromSeq < this._firstRetainedSeq;
|
|
48
|
+
const chunks = this._entries.filter((e) => e.seq >= fromSeq);
|
|
49
|
+
return { chunks, truncated };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
get lastSeq() {
|
|
53
|
+
return this._lastSeq;
|
|
54
|
+
}
|
|
55
|
+
get totalBytes() {
|
|
56
|
+
return this._totalBytes;
|
|
57
|
+
}
|
|
58
|
+
get size() {
|
|
59
|
+
return this._entries.length;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* terminal-handlers — WS topic handlers for Plan A remote-terminal.
|
|
3
|
+
*
|
|
4
|
+
* ESM mirror of `desktop-app-vue/src/main/web-shell/handlers/terminal-handlers.js`.
|
|
5
|
+
* Keep both copies in sync (see the matching mirror docstring there).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export const DEFAULT_DANGEROUS_PATTERNS = [
|
|
9
|
+
/\brm\s+-rf\b/i,
|
|
10
|
+
/\bformat\s+[a-z]:/i,
|
|
11
|
+
/\bshutdown\b/i,
|
|
12
|
+
/\bdel\s+\/[sq]/i,
|
|
13
|
+
/\bdiskpart\b/i,
|
|
14
|
+
/:\(\)\s*{\s*:\s*\|\s*:\s*&\s*}\s*;\s*:/,
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
export function bufferToBase64(buf) {
|
|
18
|
+
return Buffer.isBuffer(buf)
|
|
19
|
+
? buf.toString("base64")
|
|
20
|
+
: Buffer.from(buf, "utf-8").toString("base64");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function base64ToBuffer(s) {
|
|
24
|
+
if (typeof s !== "string") {
|
|
25
|
+
throw new Error("data_must_be_base64_string");
|
|
26
|
+
}
|
|
27
|
+
return Buffer.from(s, "base64");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function createTerminalHandlers(options) {
|
|
31
|
+
const { ptyManager, broadcast } = options;
|
|
32
|
+
if (!ptyManager) throw new TypeError("ptyManager is required");
|
|
33
|
+
if (typeof broadcast !== "function") {
|
|
34
|
+
throw new TypeError("broadcast must be a function");
|
|
35
|
+
}
|
|
36
|
+
const dangerousPatterns =
|
|
37
|
+
options.dangerousPatterns || DEFAULT_DANGEROUS_PATTERNS;
|
|
38
|
+
const requireConfirmation =
|
|
39
|
+
options.requireConfirmation || (async () => false);
|
|
40
|
+
const verifyTrustedSource = options.verifyTrustedSource || (() => true);
|
|
41
|
+
|
|
42
|
+
function attachServerEvents() {
|
|
43
|
+
ptyManager.on("stdout", ({ sessionId, data, seq }) => {
|
|
44
|
+
broadcast({
|
|
45
|
+
type: "terminal.stdout",
|
|
46
|
+
payload: { sessionId, data: bufferToBase64(data), seq },
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
ptyManager.on("exit", ({ sessionId, exitCode, signal }) => {
|
|
50
|
+
broadcast({
|
|
51
|
+
type: "terminal.exit",
|
|
52
|
+
payload: { sessionId, exitCode, signal },
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const handlers = {
|
|
58
|
+
"terminal.create": async (frame) => {
|
|
59
|
+
if (!verifyTrustedSource(frame)) return null;
|
|
60
|
+
const payload = frame?.payload || frame || {};
|
|
61
|
+
return ptyManager.create({
|
|
62
|
+
shell: payload.shell,
|
|
63
|
+
cwd: payload.cwd,
|
|
64
|
+
env: payload.env,
|
|
65
|
+
cols: payload.cols,
|
|
66
|
+
rows: payload.rows,
|
|
67
|
+
});
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
"terminal.list": async (frame) => {
|
|
71
|
+
if (!verifyTrustedSource(frame)) return null;
|
|
72
|
+
return { sessions: ptyManager.list() };
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
"terminal.stdin": async (frame) => {
|
|
76
|
+
if (!verifyTrustedSource(frame)) return null;
|
|
77
|
+
const { sessionId, data } = frame?.payload || frame || {};
|
|
78
|
+
if (!sessionId) throw new Error("session_id_required");
|
|
79
|
+
const buf = base64ToBuffer(data);
|
|
80
|
+
const text = buf.toString("utf-8");
|
|
81
|
+
if (dangerousPatterns.some((re) => re.test(text))) {
|
|
82
|
+
const ok = await requireConfirmation(text, sessionId);
|
|
83
|
+
if (!ok) throw new Error("dangerous_keyword_blocked");
|
|
84
|
+
}
|
|
85
|
+
ptyManager.write(sessionId, buf);
|
|
86
|
+
return { ok: true };
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
"terminal.resize": async (frame) => {
|
|
90
|
+
if (!verifyTrustedSource(frame)) return null;
|
|
91
|
+
const { sessionId, cols, rows } = frame?.payload || frame || {};
|
|
92
|
+
if (!sessionId) throw new Error("session_id_required");
|
|
93
|
+
ptyManager.resize(sessionId, cols, rows);
|
|
94
|
+
return { ok: true };
|
|
95
|
+
},
|
|
96
|
+
|
|
97
|
+
"terminal.close": async (frame) => {
|
|
98
|
+
if (!verifyTrustedSource(frame)) return null;
|
|
99
|
+
const { sessionId } = frame?.payload || frame || {};
|
|
100
|
+
if (!sessionId) throw new Error("session_id_required");
|
|
101
|
+
ptyManager.close(sessionId);
|
|
102
|
+
return { ok: true };
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
"terminal.history": async (frame) => {
|
|
106
|
+
if (!verifyTrustedSource(frame)) return null;
|
|
107
|
+
const { sessionId, fromSeq } = frame?.payload || frame || {};
|
|
108
|
+
if (!sessionId) throw new Error("session_id_required");
|
|
109
|
+
const { chunks, truncated } = ptyManager.history(sessionId, fromSeq || 0);
|
|
110
|
+
return {
|
|
111
|
+
chunks: chunks.map((c) => ({
|
|
112
|
+
seq: c.seq,
|
|
113
|
+
data: bufferToBase64(c.data),
|
|
114
|
+
})),
|
|
115
|
+
truncated,
|
|
116
|
+
};
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
return { handlers, attachServerEvents };
|
|
121
|
+
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* topic-handler-attachment — wrap a `ChainlessChainWSServer` instance with
|
|
3
|
+
* custom topic handlers, mirroring the existing dispatcher.
|
|
4
|
+
*
|
|
5
|
+
* Extracted from `desktop-app-vue/src/main/web-shell/ws-cli-loader.js` so
|
|
6
|
+
* both consumers (desktop web-shell AND `cc ui`) get the same protocol
|
|
7
|
+
* features:
|
|
8
|
+
*
|
|
9
|
+
* - id + auth gates (token-protected servers reject unauthenticated
|
|
10
|
+
* custom-topic calls; native CLI dispatch is untouched)
|
|
11
|
+
* - plain async handler → `{type:"<topic>.result", ok, result}`
|
|
12
|
+
* - async generator handler → `{type:"<topic>.chunk", ok, chunk}` *N +
|
|
13
|
+
* terminal `{type:"<topic>.result", ok, result}`
|
|
14
|
+
* - `<topic>.cancel` frame → `gen.return("client_cancel")` for in-flight
|
|
15
|
+
* streams (keyed by request id, not topic)
|
|
16
|
+
* - `ws.on("close")` → unwind every stream the client owned
|
|
17
|
+
*
|
|
18
|
+
* Single call site contract:
|
|
19
|
+
*
|
|
20
|
+
* const handle = attachTopicHandlers(server, {
|
|
21
|
+
* handlers: { 'terminal.create': async (frame, ctx) => {...}, ... },
|
|
22
|
+
* });
|
|
23
|
+
* handle.register('extra.topic', fn);
|
|
24
|
+
* handle.broadcast({type:'terminal.stdout', payload:{...}});
|
|
25
|
+
*
|
|
26
|
+
* Returns `{ register, broadcast, topicHandlers }`. The server's
|
|
27
|
+
* `_dispatcher` is mutated in place — calling this twice on the same
|
|
28
|
+
* server stacks wrappers (each falls through to the previous), which is
|
|
29
|
+
* fine but usually unnecessary.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @typedef {(frame: any, ctx: { topic: string, id: string, server: any, ws: any, clientId: string }) => (Promise<any> | any | AsyncIterable<any>)} TopicHandler
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @param {any} server ChainlessChainWSServer instance (post-start())
|
|
38
|
+
* @param {object} [opts]
|
|
39
|
+
* @param {Record<string, TopicHandler>} [opts.handlers]
|
|
40
|
+
* @returns {{
|
|
41
|
+
* register: (topic: string, fn: TopicHandler) => void,
|
|
42
|
+
* broadcast: (frame: any) => void,
|
|
43
|
+
* topicHandlers: Map<string, TopicHandler>,
|
|
44
|
+
* }}
|
|
45
|
+
*/
|
|
46
|
+
export function attachTopicHandlers(server, opts = {}) {
|
|
47
|
+
/** @type {Map<string, TopicHandler>} */
|
|
48
|
+
const topicHandlers = new Map();
|
|
49
|
+
if (opts.handlers) {
|
|
50
|
+
for (const [topic, fn] of Object.entries(opts.handlers)) {
|
|
51
|
+
if (typeof fn !== "function") {
|
|
52
|
+
throw new TypeError(
|
|
53
|
+
`attachTopicHandlers: handler for "${topic}" must be a function`,
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
topicHandlers.set(topic, fn);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** @type {Map<string, { gen: AsyncIterator<any>, clientId: string }>} */
|
|
61
|
+
const inFlightStreams = new Map();
|
|
62
|
+
|
|
63
|
+
async function unwindStream(id, reason) {
|
|
64
|
+
const entry = inFlightStreams.get(id);
|
|
65
|
+
if (!entry) return;
|
|
66
|
+
inFlightStreams.delete(id);
|
|
67
|
+
try {
|
|
68
|
+
if (typeof entry.gen.return === "function") {
|
|
69
|
+
await entry.gen.return(reason);
|
|
70
|
+
}
|
|
71
|
+
} catch {
|
|
72
|
+
// Generator throw on unwind is non-fatal.
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const wsCloseHooked = new WeakSet();
|
|
77
|
+
function ensureWsCloseHook(ws, clientId) {
|
|
78
|
+
if (!ws || typeof ws.on !== "function") return;
|
|
79
|
+
if (wsCloseHooked.has(ws)) return;
|
|
80
|
+
wsCloseHooked.add(ws);
|
|
81
|
+
ws.on("close", () => {
|
|
82
|
+
for (const [id, entry] of inFlightStreams) {
|
|
83
|
+
if (entry.clientId === clientId) {
|
|
84
|
+
unwindStream(id, "ws_close").catch(() => {});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const originalDispatcher = server._dispatcher;
|
|
91
|
+
server._dispatcher = {
|
|
92
|
+
async dispatch(clientId, ws, message) {
|
|
93
|
+
const { id, type } = message || {};
|
|
94
|
+
ensureWsCloseHook(ws, clientId);
|
|
95
|
+
|
|
96
|
+
if (id && typeof type === "string" && type.endsWith(".cancel")) {
|
|
97
|
+
await unwindStream(id, "client_cancel");
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (id && type && topicHandlers.has(type)) {
|
|
102
|
+
const client = server.clients.get(clientId);
|
|
103
|
+
if (server.token && (!client || !client.authenticated)) {
|
|
104
|
+
server._send(ws, {
|
|
105
|
+
id,
|
|
106
|
+
type: "error",
|
|
107
|
+
code: "AUTH_REQUIRED",
|
|
108
|
+
message: "Authentication required. Send an auth message first.",
|
|
109
|
+
});
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const handler = topicHandlers.get(type);
|
|
114
|
+
try {
|
|
115
|
+
const ret = handler(message, {
|
|
116
|
+
topic: type,
|
|
117
|
+
id,
|
|
118
|
+
server,
|
|
119
|
+
ws,
|
|
120
|
+
clientId,
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
if (ret && typeof ret[Symbol.asyncIterator] === "function") {
|
|
124
|
+
inFlightStreams.set(id, { gen: ret, clientId });
|
|
125
|
+
let finalReturn = null;
|
|
126
|
+
try {
|
|
127
|
+
for (;;) {
|
|
128
|
+
const step = await ret.next();
|
|
129
|
+
if (step.done) {
|
|
130
|
+
finalReturn = step.value ?? null;
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
if (ws.readyState !== 1) {
|
|
134
|
+
await unwindStream(id, "readyState_closed");
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
server._send(ws, {
|
|
138
|
+
id,
|
|
139
|
+
type: `${type}.chunk`,
|
|
140
|
+
ok: true,
|
|
141
|
+
chunk: step.value ?? null,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
server._send(ws, {
|
|
145
|
+
id,
|
|
146
|
+
type: `${type}.result`,
|
|
147
|
+
ok: true,
|
|
148
|
+
result: finalReturn,
|
|
149
|
+
});
|
|
150
|
+
} catch (streamErr) {
|
|
151
|
+
server._send(ws, {
|
|
152
|
+
id,
|
|
153
|
+
type: `${type}.result`,
|
|
154
|
+
ok: false,
|
|
155
|
+
error:
|
|
156
|
+
streamErr instanceof Error
|
|
157
|
+
? streamErr.message
|
|
158
|
+
: String(streamErr),
|
|
159
|
+
});
|
|
160
|
+
} finally {
|
|
161
|
+
inFlightStreams.delete(id);
|
|
162
|
+
}
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const result = await ret;
|
|
167
|
+
server._send(ws, {
|
|
168
|
+
id,
|
|
169
|
+
type: `${type}.result`,
|
|
170
|
+
ok: true,
|
|
171
|
+
result: result ?? null,
|
|
172
|
+
});
|
|
173
|
+
} catch (err) {
|
|
174
|
+
server._send(ws, {
|
|
175
|
+
id,
|
|
176
|
+
type: `${type}.result`,
|
|
177
|
+
ok: false,
|
|
178
|
+
error: err instanceof Error ? err.message : String(err),
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return originalDispatcher.dispatch(clientId, ws, message);
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
function register(topic, fn) {
|
|
189
|
+
if (typeof topic !== "string" || !topic) {
|
|
190
|
+
throw new TypeError(
|
|
191
|
+
"attachTopicHandlers: topic must be a non-empty string",
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
if (typeof fn !== "function") {
|
|
195
|
+
throw new TypeError("attachTopicHandlers: handler must be a function");
|
|
196
|
+
}
|
|
197
|
+
topicHandlers.set(topic, fn);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function broadcast(frame) {
|
|
201
|
+
if (!frame || typeof frame !== "object") return;
|
|
202
|
+
try {
|
|
203
|
+
server._broadcast(frame);
|
|
204
|
+
} catch {
|
|
205
|
+
// _send is readyState-guarded inside the server; the only path here
|
|
206
|
+
// would be an internal CLI ws-server bug. Don't crash the caller.
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return { register, broadcast, topicHandlers };
|
|
211
|
+
}
|