chainlesschain 0.162.148 → 0.162.150

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (322) hide show
  1. package/README.md +1 -1
  2. package/package.json +1 -1
  3. package/src/assets/web-panel/.build-hash +1 -1
  4. package/src/assets/web-panel/assets/{AIOps-DwpCCp64.js → AIOps-CBLvrjeG.js} +1 -1
  5. package/src/assets/web-panel/assets/{ActionButton-C9pBYocA.js → ActionButton-DYv2GtE0.js} +1 -1
  6. package/src/assets/web-panel/assets/{Analytics-BWAkeXxK.js → Analytics-CL6PZ-Ww.js} +3 -3
  7. package/src/assets/web-panel/assets/{AppLayout-CivFGH6B.js → AppLayout-xuyHt4uA.js} +3 -3
  8. package/src/assets/web-panel/assets/{Audit-Cm8hRpZm.js → Audit-DOvSVycq.js} +1 -1
  9. package/src/assets/web-panel/assets/{Backup-DXdFMsE8.js → Backup-DMhfTswr.js} +1 -1
  10. package/src/assets/web-panel/assets/{BaseInput-uAwSTeql.js → BaseInput-CQxDdm63.js} +1 -1
  11. package/src/assets/web-panel/assets/{Chat-KU8eeJJE.js → Chat-BEeNhK2a.js} +5 -5
  12. package/src/assets/web-panel/assets/ChatBubbleRenderer-8nul8eTq.js +1 -0
  13. package/src/assets/web-panel/assets/{Checkbox-C6AMDkjd.js → Checkbox-CL04O-ER.js} +1 -1
  14. package/src/assets/web-panel/assets/{Codegen-t2moDxcO.js → Codegen-C50o-n7x.js} +1 -1
  15. package/src/assets/web-panel/assets/{Col-DCcsRRhN.js → Col-CH-bDkzs.js} +1 -1
  16. package/src/assets/web-panel/assets/{Community-DtB-8SAC.js → Community-DqAIMvSe.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compact-CZm8THYA.js → Compact-C43fo_my.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compliance-LiQgC7g1.js → Compliance-BERnMrdP.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cowork-CA8SAxht.js → Cowork-Baw3w1gp.js} +4 -4
  20. package/src/assets/web-panel/assets/{Cron-CtRaF1wD.js → Cron-BEqAHF1-.js} +2 -2
  21. package/src/assets/web-panel/assets/{Crosschain-6-br6Hub.js → Crosschain-CvnmKwK4.js} +1 -1
  22. package/src/assets/web-panel/assets/{DID-Do2EccrL.js → DID-iMPxgVdt.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dashboard-wrXcbzGe.js → Dashboard--4SJX3YR.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dropdown-p3FsT245.js → Dropdown-BgLV6xgz.js} +1 -1
  25. package/src/assets/web-panel/assets/{EmailListRenderer-D95A2L8q.js → EmailListRenderer-Bvb8oUD8.js} +1 -1
  26. package/src/assets/web-panel/assets/{FamilyGuardDashboard-cLuJW2zo.js → FamilyGuardDashboard-DpXPjJB5.js} +1 -1
  27. package/src/assets/web-panel/assets/{Federation-Brgq_cbN.js → Federation-BbFfJ1Xi.js} +1 -1
  28. package/src/assets/web-panel/assets/{FormItemContext-DWLCkNgh.js → FormItemContext-DeGq1B-q.js} +1 -1
  29. package/src/assets/web-panel/assets/{GenericCardRenderer-BBqUfQd6.js → GenericCardRenderer-CNW7s9zM.js} +1 -1
  30. package/src/assets/web-panel/assets/{Git-8F090w4I.js → Git-L1PjW-lT.js} +2 -2
  31. package/src/assets/web-panel/assets/{Governance-CCogEbxb.js → Governance-PK-X58to.js} +1 -1
  32. package/src/assets/web-panel/assets/{Inference-CfLD7v7C.js → Inference-BePWEH-d.js} +1 -1
  33. package/src/assets/web-panel/assets/{KnowledgeGraph-DDuHJewd.js → KnowledgeGraph-LF_sBvdL.js} +1 -1
  34. package/src/assets/web-panel/assets/{Logs-CE8KSEVC.js → Logs-BKkfk9hy.js} +2 -2
  35. package/src/assets/web-panel/assets/{Marketplace-DmwpZmhT.js → Marketplace-BNMqUDla.js} +1 -1
  36. package/src/assets/web-panel/assets/{McpTools-Bf7AazU8.js → McpTools-w4WPiyz2.js} +5 -5
  37. package/src/assets/web-panel/assets/{Memory-D0QU_00S.js → Memory-DUKMVGNi.js} +2 -2
  38. package/src/assets/web-panel/assets/{MobileBridge-yXS9xdhx.js → MobileBridge-BWhiEyTJ.js} +1 -1
  39. package/src/assets/web-panel/assets/{MobileProjects-BuDUoGUP.js → MobileProjects-Ocf5JEkX.js} +1 -1
  40. package/src/assets/web-panel/assets/{Mtc-BYyHy28p.js → Mtc-D2B6MMhU.js} +4 -4
  41. package/src/assets/web-panel/assets/{MtcAudit-CK0aqpiZ.js → MtcAudit-DdYjCq1O.js} +4 -4
  42. package/src/assets/web-panel/assets/{Multisig-mNimKYeb.js → Multisig-Cb1hfuUZ.js} +3 -3
  43. package/src/assets/web-panel/assets/{NLProgramming-AioAJ0YA.js → NLProgramming-CTeImGp7.js} +1 -1
  44. package/src/assets/web-panel/assets/{Notes-CVVNCLHE.js → Notes-BjYNc7eQ.js} +4 -4
  45. package/src/assets/web-panel/assets/{NotificationSettings-C4ABj-TK.js → NotificationSettings-Dn_NtRXQ.js} +1 -1
  46. package/src/assets/web-panel/assets/{OrderTableRenderer-DIp8Xcbd.js → OrderTableRenderer-DVYYIizh.js} +1 -1
  47. package/src/assets/web-panel/assets/{Organization-uozl_gWK.js → Organization-Be2Kmr5j.js} +4 -4
  48. package/src/assets/web-panel/assets/{Overflow-BCZOM2hK.js → Overflow-Cu_-qRlR.js} +1 -1
  49. package/src/assets/web-panel/assets/{P2P-CArcaiaj.js → P2P-DD2HoGzE.js} +2 -2
  50. package/src/assets/web-panel/assets/{PdhVaultBrowser-Sgq2Srr8.js → PdhVaultBrowser-BRI7DVth.js} +3 -3
  51. package/src/assets/web-panel/assets/{Permissions-BYrQrXnH.js → Permissions-C9Srcs2M.js} +4 -4
  52. package/src/assets/web-panel/assets/{PersonalDataHub-BzHStAbz.js → PersonalDataHub-CUqcMgo1.js} +3 -3
  53. package/src/assets/web-panel/assets/{Pipeline-k7KqxX1M.js → Pipeline-HBrQGLXt.js} +1 -1
  54. package/src/assets/web-panel/assets/{Privacy-IumTUWqx.js → Privacy-D-B0vjwh.js} +1 -1
  55. package/src/assets/web-panel/assets/{ProjectInit-BRyImYTf.js → ProjectInit-eTeLIu9e.js} +2 -2
  56. package/src/assets/web-panel/assets/{ProjectSettings-BFIqTBFk.js → ProjectSettings-DrRqCsC6.js} +2 -2
  57. package/src/assets/web-panel/assets/{Projects-Dpid9CDg.js → Projects-1G9DNOhI.js} +1 -1
  58. package/src/assets/web-panel/assets/{Providers-CyyFnUPs.js → Providers-RkTZzpxP.js} +1 -1
  59. package/src/assets/web-panel/assets/{QrScannerModal-C82cRx-X.js → QrScannerModal-BSCUgsgC.js} +1 -1
  60. package/src/assets/web-panel/assets/{QuickAsk-DGxDF521.js → QuickAsk-CHgyM3Bz.js} +1 -1
  61. package/src/assets/web-panel/assets/{Recommend-Cns-Am1y.js → Recommend-0lry4OZq.js} +1 -1
  62. package/src/assets/web-panel/assets/{RemoteSession-R7OqAIlg.js → RemoteSession-Cn45UroZ.js} +2 -2
  63. package/src/assets/web-panel/assets/{Reputation-D7mgPIYV.js → Reputation-Q-Zjb707.js} +1 -1
  64. package/src/assets/web-panel/assets/{Row-B3lEURyd.js → Row-BdM70oda.js} +1 -1
  65. package/src/assets/web-panel/assets/{RssFeed-9_UVrpBt.js → RssFeed-DmOAKKap.js} +2 -2
  66. package/src/assets/web-panel/assets/{Search-ClZeO80q.js → Search-Dwavbm07.js} +1 -1
  67. package/src/assets/web-panel/assets/{Security-BNNJczEp.js → Security-DukXFCnk.js} +3 -3
  68. package/src/assets/web-panel/assets/{Services-C5SjrWUj.js → Services-BIlRnITu.js} +2 -2
  69. package/src/assets/web-panel/assets/{Skeleton-Ci_obQ-g.js → Skeleton-YYVQdhhQ.js} +1 -1
  70. package/src/assets/web-panel/assets/{Skills-DdebN15P.js → Skills-B8_iYHz2.js} +1 -1
  71. package/src/assets/web-panel/assets/{Sla-OinZP2vi.js → Sla-BdVrhT31.js} +1 -1
  72. package/src/assets/web-panel/assets/{SpeechSettings-CxzbNF51.js → SpeechSettings-CD3ggRx7.js} +1 -1
  73. package/src/assets/web-panel/assets/{SyncSettings-D06N_66b.js → SyncSettings-Bp02VZFH.js} +2 -2
  74. package/src/assets/web-panel/assets/{Tasks-BdEdW0AZ.js → Tasks-DEVmCEsr.js} +1 -1
  75. package/src/assets/web-panel/assets/{Templates-F1ctKmPa.js → Templates-B6czWc4N.js} +1 -1
  76. package/src/assets/web-panel/assets/{Tenant-CVz1Urot.js → Tenant-BCJLv17r.js} +1 -1
  77. package/src/assets/web-panel/assets/Terminal-CD132d2Y.js +3 -0
  78. package/src/assets/web-panel/assets/{TimelineRenderer-Doitzjmf.js → TimelineRenderer-ZB-CvkZl.js} +1 -1
  79. package/src/assets/web-panel/assets/{Tokens-BpyVRpVA.js → Tokens-BnSY0S-s.js} +1 -1
  80. package/src/assets/web-panel/assets/{Trigger-nWhWYTbU.js → Trigger-VZw9Oy2w.js} +1 -1
  81. package/src/assets/web-panel/assets/{Trust-DK_G-wLx.js → Trust-pZq7Hjd2.js} +1 -1
  82. package/src/assets/web-panel/assets/{UkeySign-B5yBUojO.js → UkeySign-DnQaqk4q.js} +1 -1
  83. package/src/assets/web-panel/assets/{VideoEditing-C5D51qAe.js → VideoEditing-BEUfmvJV.js} +1 -1
  84. package/src/assets/web-panel/assets/{Wallet-CLbL9K7v.js → Wallet-CoZKMXJ2.js} +4 -4
  85. package/src/assets/web-panel/assets/{WebAuthn-DZUelI3V.js → WebAuthn-CBOGVx1I.js} +5 -5
  86. package/src/assets/web-panel/assets/{WorkflowEditor-8RPxt4KW.js → WorkflowEditor-huSKn7TT.js} +1 -1
  87. package/src/assets/web-panel/assets/{chat-4N4tOA3d.js → chat-A_3w6FBO.js} +1 -1
  88. package/src/assets/web-panel/assets/{colors-zbbGVrua.js → colors-D1Bem9Oy.js} +1 -1
  89. package/src/assets/web-panel/assets/{compact-item-D7iMkbnE.js → compact-item-CJYJj0oO.js} +1 -1
  90. package/src/assets/web-panel/assets/{createContext-CBzPJv-w.js → createContext-BkbFiKT4.js} +1 -1
  91. package/src/assets/web-panel/assets/devWarning-WDr3_M_N.js +1 -0
  92. package/src/assets/web-panel/assets/{hasIn-bHdrQSqZ.js → hasIn-CKvUafVY.js} +1 -1
  93. package/src/assets/web-panel/assets/{index-DbE5D0WL.js → index-AjrSPnvv.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-BCr0geV9.js → index-B8i0lViZ.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-CiG1IZao.js → index-B9Svn0zc.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-B1s0Bz9O.js → index-BHdHSX06.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-QFObYeo1.js → index-BHdailyo.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-BnBOpqv3.js → index-BJwlEnYo.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-CdmMoYN1.js → index-BOoAQjJz.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-DUyjUkY7.js → index-BSgWxAOG.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-CqdPyWm5.js → index-BdutMsne.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-DmqlJed-.js → index-BiDWxzbn.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-S74FpAIn.js → index-By5a0T_W.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-kmh1TxRa.js → index-C-WIY-FQ.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-BTIjjXry.js → index-C37f6iey.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-BS4_Mwk6.js → index-C9bWmTcO.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-BBNr77E1.js → index-CBaosWRO.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-QNAG4JtR.js → index-CBlBVJ_m.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-Bqmx24kh.js → index-CD62EEGo.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-CQfu1U78.js → index-CUj-l7GM.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-BJ4ZGyW0.js → index-CVS8Ymuq.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-Ci009ijp.js → index-ChkXUj3f.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-oDKNgCsP.js → index-Cj0OZ-37.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-BZb8F8YG.js → index-Co3OFL0t.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-308gCGh7.js → index-Crp8CbRg.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-CPxkoKgA.js → index-D22zNXiS.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-xYCm6W2h.js → index-DTs_D1OL.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-BtbbdLnf.js → index-DfgCdFrN.js} +3 -3
  119. package/src/assets/web-panel/assets/{index-BsKehmmS.js → index-DjYrecNb.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-CkxK86vf.js → index-DpW9cf3e.js} +1 -1
  121. package/src/assets/web-panel/assets/index-Dwix3p4g.js +1 -0
  122. package/src/assets/web-panel/assets/{index-CvRMA9uT.js → index-F--HuPG1.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-DE9j5YgT.js → index-Fuk6t_3K.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-B7b1hGry.js → index-JEAGMii9.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-D1YDh7Wr.js → index-PF-mpv8K.js} +1 -1
  126. package/src/assets/web-panel/assets/index-SHaoZ0CA.js +1 -0
  127. package/src/assets/web-panel/assets/{index-CQ6NcfWz.js → index-b-sbVdjQ.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-DyoNgbXl.js → index-pyvMVX8r.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-DISWAYce.js → index-qNTtOVi_.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-D4XKpj6c.js → index-tgO8iza6.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-CahryTX0.js → index-ttDQVhZy.js} +1 -1
  132. package/src/assets/web-panel/assets/{initDefaultProps-BD55yNBt.js → initDefaultProps-DtrnfsZH.js} +1 -1
  133. package/src/assets/web-panel/assets/{motion-B5Bbe77U.js → motion-D4zqSaX3.js} +1 -1
  134. package/src/assets/web-panel/assets/{move-CTvIqHEH.js → move-CGFGOUQj.js} +1 -1
  135. package/src/assets/web-panel/assets/{mtc-parser-BkNyPQKB.js → mtc-parser-DH2HvkJl.js} +1 -1
  136. package/src/assets/web-panel/assets/{omit-BaXJXgHA.js → omit-jJ2at5HX.js} +1 -1
  137. package/src/assets/web-panel/assets/{pickAttrs-Bw6-YTxH.js → pickAttrs-CRyMT1Fv.js} +1 -1
  138. package/src/assets/web-panel/assets/{placementArrow-BLdbkLqJ.js → placementArrow-DZ_CX0Pt.js} +1 -1
  139. package/src/assets/web-panel/assets/{responsiveObserve-IVYkzntU.js → responsiveObserve-wruHk-h5.js} +1 -1
  140. package/src/assets/web-panel/assets/{slide-DFMFwwtY.js → slide-BGgx8q67.js} +1 -1
  141. package/src/assets/web-panel/assets/{statusUtils-CURYRtZ1.js → statusUtils-Do5M7gKm.js} +1 -1
  142. package/src/assets/web-panel/assets/{styleChecker-Cfz63s1r.js → styleChecker-CRa-GZpX.js} +1 -1
  143. package/src/assets/web-panel/assets/{useFlexGapSupport-COJL0KE3.js → useFlexGapSupport-BxPcq6pj.js} +1 -1
  144. package/src/assets/web-panel/assets/{useFs-YLLojQQf.js → useFs-v5O2ZcJm.js} +1 -1
  145. package/src/assets/web-panel/assets/{usePersonalDataHub-BF_21qUC.js → usePersonalDataHub--z2FU0Px.js} +1 -1
  146. package/src/assets/web-panel/assets/{vnode-9ZpkA7bb.js → vnode-1emidVKd.js} +1 -1
  147. package/src/assets/web-panel/assets/{zoom-DhtAfhl8.js → zoom-CcJaRyHs.js} +1 -1
  148. package/src/assets/web-panel/index.html +1 -1
  149. package/src/assets/web-panel/remote-session-sw.js +43 -0
  150. package/src/command-manifest.json +22 -1
  151. package/src/commands/a2a.js +19 -4
  152. package/src/commands/activitypub.js +20 -3
  153. package/src/commands/agent.js +71 -10
  154. package/src/commands/audit.js +40 -24
  155. package/src/commands/codeintel.js +320 -0
  156. package/src/commands/collab.js +15 -2
  157. package/src/commands/compliance.js +5 -0
  158. package/src/commands/config.js +4 -1
  159. package/src/commands/dao.js +86 -14
  160. package/src/commands/dev.js +2 -0
  161. package/src/commands/did.js +7 -1
  162. package/src/commands/dlp.js +23 -1
  163. package/src/commands/economy.js +29 -3
  164. package/src/commands/eval.js +255 -0
  165. package/src/commands/evomap.js +26 -0
  166. package/src/commands/governance.js +17 -3
  167. package/src/commands/hardening.js +18 -0
  168. package/src/commands/incentive.js +5 -0
  169. package/src/commands/init.js +46 -2
  170. package/src/commands/kg.js +4 -0
  171. package/src/commands/loop.js +6 -1
  172. package/src/commands/lowcode.js +15 -2
  173. package/src/commands/marketplace.js +40 -6
  174. package/src/commands/matrix.js +20 -1
  175. package/src/commands/memory.js +4 -1
  176. package/src/commands/mtc.js +7 -1
  177. package/src/commands/nostr.js +31 -4
  178. package/src/commands/note.js +7 -4
  179. package/src/commands/plugin.js +474 -0
  180. package/src/commands/pqc.js +5 -0
  181. package/src/commands/reputation.js +10 -1
  182. package/src/commands/scim.js +6 -0
  183. package/src/commands/session.js +32 -10
  184. package/src/commands/siem.js +11 -0
  185. package/src/commands/sla.js +18 -3
  186. package/src/commands/social.js +38 -5
  187. package/src/commands/sso.js +10 -2
  188. package/src/commands/stress.js +23 -4
  189. package/src/commands/team.js +463 -0
  190. package/src/commands/tech.js +2 -0
  191. package/src/commands/tenant.js +10 -1
  192. package/src/commands/terraform.js +6 -0
  193. package/src/commands/update.js +1 -1
  194. package/src/commands/zkp.js +20 -0
  195. package/src/gateways/ws/message-dispatcher.js +48 -2
  196. package/src/gateways/ws/remote-session-protocol.js +140 -42
  197. package/src/gateways/ws/ws-server.js +1 -1
  198. package/src/harness/jsonl-session-store.js +12 -5
  199. package/src/harness/plugin-manager.js +16 -4
  200. package/src/harness/prompt-compressor.js +27 -1
  201. package/src/harness/remote-command-ledger.js +189 -0
  202. package/src/harness/remote-session-push-apns.js +246 -0
  203. package/src/harness/remote-session-push-errors.js +15 -0
  204. package/src/harness/remote-session-push-fcm.js +9 -25
  205. package/src/harness/remote-session-push-huawei.js +174 -0
  206. package/src/harness/remote-session-push-oppo.js +171 -0
  207. package/src/harness/remote-session-push-senders.js +42 -0
  208. package/src/harness/remote-session-push-vivo.js +179 -0
  209. package/src/harness/remote-session-push-web.js +299 -0
  210. package/src/harness/remote-session-push-xiaomi.js +99 -0
  211. package/src/harness/worktree-isolator.js +16 -6
  212. package/src/index.js +6 -0
  213. package/src/lib/__tests__/logger.test.js +5 -2
  214. package/src/lib/a2a-protocol.js +25 -1
  215. package/src/lib/activitypub-bridge.js +61 -0
  216. package/src/lib/agent-core.js +4 -0
  217. package/src/lib/agent-economy.js +262 -29
  218. package/src/lib/agent-network.js +7 -1
  219. package/src/lib/agent-sandbox.js +161 -5
  220. package/src/lib/agent-team/task-lease.js +434 -0
  221. package/src/lib/agent-team/team-budget.js +165 -0
  222. package/src/lib/agent-team/team-mailbox.js +106 -0
  223. package/src/lib/agent-team/team-runner.js +282 -0
  224. package/src/lib/agent-team/team-worktree.js +236 -0
  225. package/src/lib/agents.js +18 -0
  226. package/src/lib/async-hook-supervisor.cjs +391 -0
  227. package/src/lib/audit-logger.js +7 -6
  228. package/src/lib/autonomous-developer.js +60 -0
  229. package/src/lib/chat-core.js +17 -4
  230. package/src/lib/collaboration-governance.js +56 -0
  231. package/src/lib/community-governance.js +68 -0
  232. package/src/lib/compliance-manager.js +63 -0
  233. package/src/lib/cross-chain.js +5 -0
  234. package/src/lib/dao-governance.js +151 -2
  235. package/src/lib/did-manager.js +23 -10
  236. package/src/lib/dlp-engine.js +70 -0
  237. package/src/lib/eval/runner.js +251 -0
  238. package/src/lib/eval/tasks.js +626 -0
  239. package/src/lib/eval/trend.js +200 -0
  240. package/src/lib/evolution-system.js +92 -0
  241. package/src/lib/evomap-federation.js +55 -0
  242. package/src/lib/evomap-governance.js +94 -0
  243. package/src/lib/fatal-handler.js +17 -1
  244. package/src/lib/hardening-manager.js +73 -0
  245. package/src/lib/hierarchical-memory.js +14 -8
  246. package/src/lib/knowledge-exporter.js +8 -2
  247. package/src/lib/knowledge-graph.js +79 -0
  248. package/src/lib/llm-providers.js +23 -14
  249. package/src/lib/logger.js +4 -1
  250. package/src/lib/lsp/__tests__/benchmark.test.js +88 -0
  251. package/src/lib/lsp/__tests__/code-intelligence.integration.test.js +129 -0
  252. package/src/lib/lsp/__tests__/code-intelligence.test.js +228 -0
  253. package/src/lib/lsp/__tests__/jsonrpc-stream.test.js +104 -0
  254. package/src/lib/lsp/__tests__/lsp-client.test.js +269 -0
  255. package/src/lib/lsp/__tests__/lsp-manager.test.js +333 -0
  256. package/src/lib/lsp/__tests__/lsp-server-registry.test.js +128 -0
  257. package/src/lib/lsp/benchmark.js +257 -0
  258. package/src/lib/lsp/code-intelligence.js +356 -0
  259. package/src/lib/lsp/jsonrpc-stream.js +139 -0
  260. package/src/lib/lsp/lsp-client.js +339 -0
  261. package/src/lib/lsp/lsp-manager.js +375 -0
  262. package/src/lib/lsp/lsp-server-registry.js +196 -0
  263. package/src/lib/matrix-bridge.js +84 -0
  264. package/src/lib/memory-manager.js +5 -3
  265. package/src/lib/nostr-bridge.js +53 -0
  266. package/src/lib/permission-engine.js +22 -4
  267. package/src/lib/plugin-monitor-supervisor.js +238 -0
  268. package/src/lib/plugin-runtime/__tests__/remote-source.test.js +256 -0
  269. package/src/lib/plugin-runtime/agents.js +51 -0
  270. package/src/lib/plugin-runtime/bin.js +100 -0
  271. package/src/lib/plugin-runtime/hooks.js +100 -0
  272. package/src/lib/plugin-runtime/install.js +396 -0
  273. package/src/lib/plugin-runtime/lsp.js +75 -0
  274. package/src/lib/plugin-runtime/manifest.js +458 -0
  275. package/src/lib/plugin-runtime/mcp.js +89 -0
  276. package/src/lib/plugin-runtime/monitors.js +100 -0
  277. package/src/lib/plugin-runtime/policy.js +152 -0
  278. package/src/lib/plugin-runtime/reload.js +79 -0
  279. package/src/lib/plugin-runtime/remote-source.js +240 -0
  280. package/src/lib/plugin-runtime/scopes.js +197 -0
  281. package/src/lib/plugin-runtime/settings.js +119 -0
  282. package/src/lib/plugin-runtime/signature.js +107 -0
  283. package/src/lib/plugin-runtime/skills.js +43 -0
  284. package/src/lib/plugin-runtime/trust.js +140 -0
  285. package/src/lib/pqc-manager.js +64 -0
  286. package/src/lib/reputation-optimizer.js +101 -0
  287. package/src/lib/sandbox-egress-proxy.js +262 -0
  288. package/src/lib/sandbox-fs-policy.js +112 -0
  289. package/src/lib/sandbox-network-policy.js +151 -0
  290. package/src/lib/sandbox-v2.js +24 -18
  291. package/src/lib/scim-manager.js +36 -0
  292. package/src/lib/session-manager.js +5 -2
  293. package/src/lib/settings-hook-events.cjs +78 -6
  294. package/src/lib/settings-hooks.cjs +30 -3
  295. package/src/lib/settings-loader.cjs +35 -1
  296. package/src/lib/siem-exporter.js +45 -0
  297. package/src/lib/skill-loader.js +38 -2
  298. package/src/lib/skill-marketplace.js +83 -0
  299. package/src/lib/sla-manager.js +88 -0
  300. package/src/lib/social-manager.js +74 -0
  301. package/src/lib/stress-tester.js +65 -0
  302. package/src/lib/tech-learning-engine.js +75 -0
  303. package/src/lib/telemetry/span-recorder.js +237 -0
  304. package/src/lib/tenant-saas.js +107 -0
  305. package/src/lib/terraform-manager.js +67 -0
  306. package/src/lib/token-incentive.js +180 -29
  307. package/src/lib/wallet-manager.js +81 -35
  308. package/src/lib/zkp-engine.js +87 -0
  309. package/src/repl/agent-repl.js +417 -6
  310. package/src/runtime/__tests__/auto-pin.test.js +104 -0
  311. package/src/runtime/agent-core.js +771 -104
  312. package/src/runtime/auto-pin.js +117 -0
  313. package/src/runtime/coding-agent-contract-shared.cjs +82 -8
  314. package/src/runtime/coding-agent-policy.cjs +32 -7
  315. package/src/runtime/fallback-model.js +134 -7
  316. package/src/runtime/headless-runner.js +388 -108
  317. package/src/runtime/mcp-config.js +46 -0
  318. package/src/assets/web-panel/assets/ChatBubbleRenderer-DCNfbdlx.js +0 -1
  319. package/src/assets/web-panel/assets/Terminal-BvAK_Zel.js +0 -3
  320. package/src/assets/web-panel/assets/devWarning-C2Z5LRgq.js +0 -1
  321. package/src/assets/web-panel/assets/index-BGp6Yr-Y.js +0 -1
  322. package/src/assets/web-panel/assets/index-BXiw9dQf.js +0 -1
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Managed org policy for plugin LOADING (Phase 3.3j — fail-closed allow/deny at
3
+ * discovery time, not just at `cc plugin install`).
4
+ *
5
+ * The security primitives already exist in ../plugin-security.js
6
+ * (`enforcePluginPolicy` for name/source allow-deny, `verifyPluginManifest` for
7
+ * SHA-256 + Ed25519 signature). But before this, they were consulted only when a
8
+ * user ran `cc plugin validate`/`install` — a plugin that an org later added to
9
+ * `deniedPlugins`, or one dropped into a scope dir out-of-band, still loaded its
10
+ * skills/agents/hooks/MCP/LSP/monitors at runtime.
11
+ *
12
+ * This wires the SAME `enforcePluginPolicy` into `discoverPlugins`, the single
13
+ * chokepoint every component collector funnels through, so an org's
14
+ * allowedPlugins / deniedPlugins is enforced fail-closed across ALL six
15
+ * component types in one place. When there is no managed settings file the
16
+ * policy is null and nothing is filtered (zero impact on the common case).
17
+ *
18
+ * NOTE: source allow/deny (blockedPluginSources / allowedPluginSources) stays an
19
+ * INSTALL-time gate — an installed plugin doesn't carry its origin on disk, so
20
+ * only the name allow/deny applies at load. Signature enforcement
21
+ * (requireSignedPlugins) is a separate follow-up: it needs the installer to
22
+ * persist a verified-signature marker, else every plugin would fail closed.
23
+ */
24
+
25
+ import { enforcePluginPolicy } from "../plugin-security.js";
26
+ import { loadManagedSettings } from "../settings-loader.cjs";
27
+ import { verifyInstalledSignature } from "./signature.js";
28
+
29
+ // Managed settings are org-admin controlled and effectively static for a
30
+ // session, and discoverPlugins is called by every collector — memoize the load
31
+ // (keyed by resolved file path) so we don't re-read the file on every call.
32
+ let _cache = new Map();
33
+
34
+ export const _deps = { loadManagedSettings };
35
+
36
+ /**
37
+ * Load the managed plugin policy (memoized). Returns the managed settings object
38
+ * or null when there is no managed settings file.
39
+ * @param {object} [opts] { env, managedSettingsFile }
40
+ */
41
+ export function loadManagedPluginPolicy(opts = {}) {
42
+ const env = opts.env || process.env;
43
+ const key = String(
44
+ opts.managedSettingsFile || env.CC_MANAGED_SETTINGS || "<default>",
45
+ );
46
+ if (_cache.has(key)) {
47
+ const cached = _cache.get(key);
48
+ // A cached failure sentinel must re-throw on EVERY call — otherwise the
49
+ // first call fails closed but later callers get the truthy {__invalid}
50
+ // object, treat it as an empty policy, and silently bypass the org's
51
+ // deny/allow/requireSignedPlugins enforcement (fail-open).
52
+ if (cached && cached.__invalid) throw cached.__invalid;
53
+ return cached;
54
+ }
55
+ let settings = null;
56
+ try {
57
+ const loaded = _deps.loadManagedSettings({
58
+ env: opts.env,
59
+ managedSettingsFile: opts.managedSettingsFile,
60
+ });
61
+ settings = loaded.settings;
62
+ } catch (err) {
63
+ // Malformed managed settings → fail closed by propagating: a caller that
64
+ // wants org enforcement must not silently proceed with no policy.
65
+ _cache.set(key, { __invalid: err });
66
+ throw err;
67
+ }
68
+ _cache.set(key, settings);
69
+ return settings;
70
+ }
71
+
72
+ /** Clear the memoized policy (tests / after a managed-settings change). */
73
+ export function _resetPolicyCache() {
74
+ _cache = new Map();
75
+ }
76
+
77
+ /**
78
+ * Filter a discovered-plugin list by managed name allow/deny. Non-throwing:
79
+ * a plugin the org denies (or omits from a required allowlist) is DROPPED and
80
+ * collected in `dropped`, rather than throwing — so one denied plugin never
81
+ * breaks loading of the rest.
82
+ *
83
+ * @param {Array<{name}>} plugins
84
+ * @param {object|null} managed managed settings (null = no policy → keep all)
85
+ * @returns {{ kept: Array, dropped: Array<{name, reason}> }}
86
+ */
87
+ export function filterByManagedPolicy(plugins, managed) {
88
+ if (!managed) return { kept: plugins, dropped: [] };
89
+ // requireSignedPlugins (fail-closed): every plugin must carry a valid recorded
90
+ // signature whose locked manifest hash still matches on disk. Optionally the
91
+ // signing key must be among managed.trustedPluginKeySha256.
92
+ const requireSigned =
93
+ managed.requireSignedPlugins === true ||
94
+ managed.requireSignedPlugins === "require";
95
+ const trustedKeys = requireSigned
96
+ ? new Set(
97
+ (Array.isArray(managed.trustedPluginKeySha256)
98
+ ? managed.trustedPluginKeySha256
99
+ : []
100
+ )
101
+ .map((k) => String(k || "").trim())
102
+ .filter(Boolean),
103
+ )
104
+ : null;
105
+ const kept = [];
106
+ const dropped = [];
107
+ for (const p of plugins) {
108
+ try {
109
+ enforcePluginPolicy(
110
+ { name: p.name, source: null, action: "load" },
111
+ managed,
112
+ );
113
+ } catch (err) {
114
+ dropped.push({ name: p.name, reason: err.message });
115
+ continue;
116
+ }
117
+ if (requireSigned) {
118
+ const v = verifyInstalledSignature(p, { trustedKeys });
119
+ if (!v.signed) {
120
+ dropped.push({
121
+ name: p.name,
122
+ reason: `requireSignedPlugins: ${v.reason}`,
123
+ });
124
+ continue;
125
+ }
126
+ }
127
+ kept.push(p);
128
+ }
129
+ return { kept, dropped };
130
+ }
131
+
132
+ let _warnedDropped = new Set();
133
+
134
+ /** One-time stderr warning naming plugins an org policy blocked from loading. */
135
+ export function warnDroppedOnce(dropped) {
136
+ const fresh = dropped.filter((d) => !_warnedDropped.has(d.name));
137
+ if (fresh.length === 0) return;
138
+ for (const d of fresh) _warnedDropped.add(d.name);
139
+ try {
140
+ process.stderr.write(
141
+ `[plugins] blocked by managed policy: ${fresh
142
+ .map((d) => `${d.name} (${d.reason})`)
143
+ .join(", ")}\n`,
144
+ );
145
+ } catch {
146
+ /* best-effort */
147
+ }
148
+ }
149
+
150
+ export function _resetPolicyWarnings() {
151
+ _warnedDropped = new Set();
152
+ }
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Hot-reload the plugin runtime (Phase 3.3m — `/reload-plugins`).
3
+ *
4
+ * Installing / trusting / upgrading a plugin mid-session normally needs a
5
+ * restart because several collectors memoize their work (LSP registration is
6
+ * registered once per root; the managed-policy load + trust/untrusted warnings
7
+ * are cached). This resets that memoized state and re-runs the declarative
8
+ * collectors so a freshly-installed/trusted plugin's components appear without
9
+ * restarting.
10
+ *
11
+ * Returns a summary of what is now discovered (per-component counts) so the REPL
12
+ * can report it. The caller (agent-repl) additionally re-merges hooks and
13
+ * restarts the monitor supervisor with the fresh set — those hold live session
14
+ * state (the effective hook map, running child processes) the pure collectors
15
+ * can't touch. Already-connected MCP servers stay connected for this session
16
+ * (reconnecting mid-turn is out of scope); newly-added MCP servers are picked up
17
+ * on the next session.
18
+ */
19
+
20
+ import { discoverPlugins } from "./scopes.js";
21
+ import { ensurePluginLspServers, _resetPluginLspLoadState } from "./lsp.js";
22
+ import { _resetPluginServers } from "../lsp/lsp-server-registry.js";
23
+ import { _resetPolicyCache, _resetPolicyWarnings } from "./policy.js";
24
+ import { _resetTrustWarnings } from "./trust.js";
25
+ import { discoverPluginSkillLayers } from "./skills.js";
26
+ import { discoverPluginAgentLayers } from "./agents.js";
27
+ import { collectPluginHooks } from "./hooks.js";
28
+ import { collectPluginMcpServers } from "./mcp.js";
29
+ import { collectPluginMonitors } from "./monitors.js";
30
+
31
+ /**
32
+ * Reset memoized plugin-runtime state and re-scan. Pure w.r.t. session state —
33
+ * the REPL layer applies the hooks/monitors side of the reload.
34
+ *
35
+ * @param {object} [opts] { cwd, scopes }
36
+ * @returns {{ plugins, lspRegistered, skills, agents, hooks, mcp, monitors }}
37
+ */
38
+ export function reloadPluginRuntime(opts = {}) {
39
+ const cwd = opts.cwd || process.cwd();
40
+ const scopes = opts.scopes;
41
+
42
+ // 1) Clear caches so the next discovery re-reads managed policy + re-evaluates
43
+ // trust, and LSP servers can be cleanly re-registered.
44
+ _resetPolicyCache();
45
+ _resetPolicyWarnings();
46
+ _resetTrustWarnings();
47
+ _resetPluginServers();
48
+ _resetPluginLspLoadState();
49
+
50
+ // 2) Re-register plugin LSP servers from scratch (force past the per-root memo).
51
+ let lspRegistered = 0;
52
+ try {
53
+ const res = ensurePluginLspServers({ cwd, scopes, force: true });
54
+ lspRegistered = res?.registered?.length || 0;
55
+ } catch {
56
+ lspRegistered = 0;
57
+ }
58
+
59
+ // 3) Re-scan the declarative collectors for a fresh summary.
60
+ const count = (fn, pick) => {
61
+ try {
62
+ const r = fn({ cwd, scopes });
63
+ return pick(r);
64
+ } catch {
65
+ return 0;
66
+ }
67
+ };
68
+ const plugins = count(discoverPlugins, (r) => r.length);
69
+ const skills = count(discoverPluginSkillLayers, (r) => r.length);
70
+ const agents = count(discoverPluginAgentLayers, (r) => r.length);
71
+ const hooks = count(collectPluginHooks, (r) => Object.keys(r).length);
72
+ const mcp = count(
73
+ collectPluginMcpServers,
74
+ (r) => Object.keys(r.servers).length,
75
+ );
76
+ const monitors = count(collectPluginMonitors, (r) => r.length);
77
+
78
+ return { plugins, lspRegistered, skills, agents, hooks, mcp, monitors };
79
+ }
@@ -0,0 +1,240 @@
1
+ /**
2
+ * Remote plugin sources — install from a hosted registry / manifest URL, not
3
+ * just a local dir or git URL (Phase 3 "远程 manifest" + "私有仓认证" + "离线
4
+ * seed cache" work items).
5
+ *
6
+ * A remote source is an `https?://…/*.json` document, in one of two shapes:
7
+ *
8
+ * 1. Registry index — many plugins, selected by name:
9
+ * { "name": "acme-registry",
10
+ * "plugins": [
11
+ * { "name": "toml-tools", "source": "https://github.com/x/toml.git",
12
+ * "ref": "v1.0.0", "version": "1.0.0", "description": "…", "sha256": "…" },
13
+ * { "name": "py-helpers", "source": "owner/py-helpers" }
14
+ * ] }
15
+ *
16
+ * 2. Single-plugin manifest — one plugin, no selection needed:
17
+ * { "name": "toml-tools", "source": "https://github.com/x/toml.git", "ref": "v1.0.0" }
18
+ *
19
+ * The remote layer is an INDIRECTION: it resolves a name → a git source string
20
+ * that the existing installer (`installFromSource`) already knows how to clone.
21
+ * This keeps the sync install core untouched and avoids bundling a tarball
22
+ * extractor. Downloaded registry JSON is cached content-addressed so browsing /
23
+ * installing works OFFLINE, and a bearer token can gate a PRIVATE registry.
24
+ */
25
+
26
+ import fs from "fs";
27
+ import os from "os";
28
+ import path from "path";
29
+ import crypto from "crypto";
30
+ import { getElectronUserDataDir } from "../paths.js";
31
+
32
+ export const _deps = {
33
+ // Node 22+ global fetch; injectable for tests.
34
+ fetch: (...args) => globalThis.fetch(...args),
35
+ readFileSync: fs.readFileSync,
36
+ writeFileSync: fs.writeFileSync,
37
+ existsSync: fs.existsSync,
38
+ mkdirSync: fs.mkdirSync,
39
+ };
40
+
41
+ const DEFAULT_TIMEOUT_MS = 15_000;
42
+
43
+ /** True when `raw` looks like a remote registry/manifest URL (http(s) + .json). */
44
+ export function isRemoteSource(raw) {
45
+ const s = String(raw || "").trim();
46
+ return /^https?:\/\//i.test(s) && /\.json(\?.*)?(#.*)?$/i.test(s);
47
+ }
48
+
49
+ /** Where a fetched registry is cached (content-addressed by its URL). */
50
+ export function registryCachePath(url, cacheDir) {
51
+ const dir =
52
+ cacheDir || path.join(getElectronUserDataDir(), "plugin-registry-cache");
53
+ const hash = crypto
54
+ .createHash("sha256")
55
+ .update(String(url))
56
+ .digest("hex")
57
+ .slice(0, 32);
58
+ return path.join(dir, `${hash}.json`);
59
+ }
60
+
61
+ /**
62
+ * Resolve the bearer token for a registry host from (in order): explicit
63
+ * `opts.token`, `CC_PLUGIN_REGISTRY_TOKEN` env, or a per-host map in config
64
+ * (`config.plugins.registryTokens[host]`). Returns null if none — a public
65
+ * registry needs no token.
66
+ */
67
+ export function resolveRegistryToken(url, { token, config } = {}) {
68
+ if (token) return token;
69
+ if (process.env.CC_PLUGIN_REGISTRY_TOKEN)
70
+ return process.env.CC_PLUGIN_REGISTRY_TOKEN;
71
+ try {
72
+ const host = new URL(url).host;
73
+ const map = config?.plugins?.registryTokens;
74
+ if (map && map[host]) return map[host];
75
+ } catch {
76
+ /* bad URL — handled by fetch */
77
+ }
78
+ return null;
79
+ }
80
+
81
+ /**
82
+ * GET a registry/manifest URL as JSON, with optional bearer auth. On success the
83
+ * body is written to the offline cache; on a network/HTTP failure we fall back
84
+ * to that cache (so a previously-seen registry still installs offline). Throws
85
+ * only when neither the network NOR the cache can produce a valid document.
86
+ *
87
+ * @returns {Promise<{ registry: object, fromCache: boolean }>}
88
+ */
89
+ export async function fetchRegistry(url, opts = {}) {
90
+ const {
91
+ token,
92
+ cacheDir,
93
+ timeoutMs = DEFAULT_TIMEOUT_MS,
94
+ allowCache = true,
95
+ } = opts;
96
+ const cachePath = registryCachePath(url, cacheDir);
97
+ const headers = { Accept: "application/json" };
98
+ if (token) headers.Authorization = `Bearer ${token}`;
99
+
100
+ let networkErr = null;
101
+ try {
102
+ const controller = new AbortController();
103
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
104
+ let res;
105
+ try {
106
+ res = await _deps.fetch(url, {
107
+ headers,
108
+ redirect: "follow",
109
+ signal: controller.signal,
110
+ });
111
+ } finally {
112
+ clearTimeout(timer);
113
+ }
114
+ if (!res.ok) {
115
+ throw new Error(
116
+ `registry fetch failed: HTTP ${res.status} ${res.statusText || ""}`.trim(),
117
+ );
118
+ }
119
+ const text = await res.text();
120
+ const registry = validateRegistry(JSON.parse(text), url);
121
+ if (allowCache) writeCache(cachePath, text);
122
+ return { registry, fromCache: false };
123
+ } catch (err) {
124
+ networkErr = err;
125
+ }
126
+
127
+ // Network failed — try the offline cache.
128
+ if (allowCache && _deps.existsSync(cachePath)) {
129
+ try {
130
+ const text = _deps.readFileSync(cachePath, "utf8");
131
+ const registry = validateRegistry(JSON.parse(text), url);
132
+ return { registry, fromCache: true };
133
+ } catch {
134
+ /* cache unreadable — fall through to throw the network error */
135
+ }
136
+ }
137
+ throw new Error(
138
+ `could not fetch registry ${url}: ${networkErr ? networkErr.message : "unknown error"}` +
139
+ (allowCache ? " (no offline cache available)" : ""),
140
+ );
141
+ }
142
+
143
+ function writeCache(cachePath, text) {
144
+ try {
145
+ _deps.mkdirSync(path.dirname(cachePath), { recursive: true });
146
+ _deps.writeFileSync(cachePath, text, "utf8");
147
+ } catch {
148
+ /* caching is best-effort; a read-only cache dir must not break install */
149
+ }
150
+ }
151
+
152
+ /** Validate + normalize a fetched document into a registry object. Throws on garbage. */
153
+ export function validateRegistry(doc, url = "") {
154
+ if (!doc || typeof doc !== "object" || Array.isArray(doc)) {
155
+ throw new Error(`registry at ${url} is not a JSON object`);
156
+ }
157
+ if (Array.isArray(doc.plugins)) {
158
+ for (const p of doc.plugins) {
159
+ if (!p || typeof p !== "object" || !p.name || !p.source) {
160
+ throw new Error(
161
+ `registry at ${url} has a plugin entry missing name/source`,
162
+ );
163
+ }
164
+ }
165
+ return doc;
166
+ }
167
+ // Single-plugin manifest.
168
+ if (doc.source) {
169
+ return { plugins: [doc] };
170
+ }
171
+ throw new Error(
172
+ `registry at ${url} must have a "plugins" array or a top-level "source"`,
173
+ );
174
+ }
175
+
176
+ /** List installable entries from a registry (for `cc plugin search`). */
177
+ export function listRegistryPlugins(registry) {
178
+ return (registry.plugins || []).map((p) => ({
179
+ name: p.name,
180
+ version: p.version || null,
181
+ source: p.source,
182
+ ref: p.ref || null,
183
+ description: p.description || "",
184
+ }));
185
+ }
186
+
187
+ /**
188
+ * Pick one plugin entry from a registry. When the registry has exactly one
189
+ * plugin, `name` is optional; otherwise it must match. Returns the entry, or
190
+ * throws with a helpful list.
191
+ */
192
+ export function resolvePluginEntry(registry, name) {
193
+ const plugins = registry.plugins || [];
194
+ if (plugins.length === 0) throw new Error("registry has no plugins");
195
+ if (!name) {
196
+ if (plugins.length === 1) return plugins[0];
197
+ throw new Error(
198
+ `registry has ${plugins.length} plugins — pick one with --name <plugin>:\n ` +
199
+ plugins.map((p) => p.name).join(", "),
200
+ );
201
+ }
202
+ const entry = plugins.find((p) => p.name === name);
203
+ if (!entry) {
204
+ throw new Error(
205
+ `plugin "${name}" not found in registry. Available:\n ` +
206
+ plugins.map((p) => p.name).join(", "),
207
+ );
208
+ }
209
+ return entry;
210
+ }
211
+
212
+ /**
213
+ * Full resolution: fetch the registry (with auth + offline cache), select the
214
+ * entry, and return the git source string the existing installer consumes plus
215
+ * carry-through metadata (ref pins the checkout; sha256 becomes an install
216
+ * integrity check).
217
+ *
218
+ * @returns {Promise<{ source: string, ref: string|null, sha256: string|null,
219
+ * entry: object, fromCache: boolean }>}
220
+ */
221
+ export async function resolveRemoteSource(url, opts = {}) {
222
+ const token = resolveRegistryToken(url, opts);
223
+ const { registry, fromCache } = await fetchRegistry(url, { ...opts, token });
224
+ const entry = resolvePluginEntry(registry, opts.name);
225
+ // Carry a `#ref` on the source string so installFromSource pins the checkout,
226
+ // matching its existing `owner/repo#ref` / `url#ref` convention.
227
+ const source = entry.ref ? `${entry.source}#${entry.ref}` : entry.source;
228
+ return {
229
+ source,
230
+ ref: entry.ref || null,
231
+ sha256: entry.sha256 || null,
232
+ entry,
233
+ fromCache,
234
+ };
235
+ }
236
+
237
+ /** Best-effort local temp dir helper (kept here so tests can stub fs deps). */
238
+ export function _tmpRoot() {
239
+ return os.tmpdir();
240
+ }
@@ -0,0 +1,197 @@
1
+ /**
2
+ * Plugin install scopes + on-disk discovery (Phase 3).
3
+ *
4
+ * A plugin can be installed at three scopes, mirroring the skill-loader layering:
5
+ *
6
+ * user — <userData>/plugins/ global to the machine/user
7
+ * project — <root>/.chainlesschain/plugins/ committed, shared via the repo
8
+ * local — <root>/.chainlesschain/plugins.local/ gitignored, per-developer
9
+ *
10
+ * Precedence on a name collision: local > project > user (the most specific
11
+ * wins, same spirit as the skill layers).
12
+ *
13
+ * Each plugin lives in an IMMUTABLE version directory:
14
+ *
15
+ * <scopeRoot>/<encodedName>/<version>/ (+ the plugin body)
16
+ *
17
+ * so an in-flight session keeps running its version even while another is
18
+ * installed. The active version per (scope,name) is the one named by a
19
+ * `.active` file, or the highest semver present if none is set.
20
+ */
21
+
22
+ import fs from "fs";
23
+ import path from "path";
24
+ import semver from "semver";
25
+ import { getElectronUserDataDir } from "../paths.js";
26
+ import { parsePluginManifest } from "./manifest.js";
27
+ import {
28
+ loadManagedPluginPolicy,
29
+ filterByManagedPolicy,
30
+ warnDroppedOnce,
31
+ } from "./policy.js";
32
+
33
+ export const _deps = {
34
+ existsSync: fs.existsSync,
35
+ readdirSync: fs.readdirSync,
36
+ readFileSync: fs.readFileSync,
37
+ statSync: fs.statSync,
38
+ };
39
+
40
+ // Lowest → highest precedence.
41
+ export const SCOPES = ["user", "project", "local"];
42
+
43
+ /** Filesystem-safe encoding of a (possibly scoped, e.g. @org/name) plugin name. */
44
+ export function encodeName(name) {
45
+ return String(name || "").replace(/[^a-zA-Z0-9._-]/g, "__");
46
+ }
47
+
48
+ /**
49
+ * Resolve the root directory for a scope.
50
+ * @param {"user"|"project"|"local"} scope
51
+ * @param {object} [opts] { cwd }
52
+ */
53
+ export function scopeRoot(scope, opts = {}) {
54
+ const cwd = opts.cwd || process.cwd();
55
+ switch (scope) {
56
+ case "user":
57
+ return path.join(getElectronUserDataDir(), "plugins");
58
+ case "project":
59
+ return path.join(cwd, ".chainlesschain", "plugins");
60
+ case "local":
61
+ return path.join(cwd, ".chainlesschain", "plugins.local");
62
+ default:
63
+ throw new Error(`unknown plugin scope: ${scope}`);
64
+ }
65
+ }
66
+
67
+ /** Directory that holds all versions of one plugin at a scope. */
68
+ export function pluginNameDir(scope, name, opts = {}) {
69
+ return path.join(scopeRoot(scope, opts), encodeName(name));
70
+ }
71
+
72
+ /** Immutable install dir for a specific plugin version. */
73
+ export function pluginVersionDir(scope, name, version, opts = {}) {
74
+ return path.join(pluginNameDir(scope, name, opts), String(version));
75
+ }
76
+
77
+ /** Versions present on disk for a plugin, newest semver first. */
78
+ export function listInstalledVersions(scope, name, opts = {}) {
79
+ const dir = pluginNameDir(scope, name, opts);
80
+ if (!dirExists(dir)) return [];
81
+ return _deps
82
+ .readdirSync(dir, { withFileTypes: true })
83
+ .filter((e) => e.isDirectory())
84
+ .map((e) => e.name)
85
+ .filter((v) => semver.valid(v))
86
+ .sort(semver.rcompare);
87
+ }
88
+
89
+ /** The active version for a plugin: `.active` file if valid, else highest semver. */
90
+ export function activeVersion(scope, name, opts = {}) {
91
+ const versions = listInstalledVersions(scope, name, opts);
92
+ if (versions.length === 0) return null;
93
+ const activeFile = path.join(pluginNameDir(scope, name, opts), ".active");
94
+ if (_deps.existsSync(activeFile)) {
95
+ try {
96
+ const pinned = _deps.readFileSync(activeFile, "utf8").trim();
97
+ if (versions.includes(pinned)) return pinned;
98
+ } catch {
99
+ /* fall through to highest */
100
+ }
101
+ }
102
+ return versions[0];
103
+ }
104
+
105
+ /**
106
+ * Discover every installed plugin's ACTIVE version across all scopes, applying
107
+ * scope precedence (local > project > user) so a name installed at multiple
108
+ * scopes resolves to one parsed manifest.
109
+ *
110
+ * Managed org policy (allowedPlugins / deniedPlugins) is enforced fail-closed
111
+ * here — the single chokepoint every component collector funnels through — so a
112
+ * denied plugin loads NONE of its six component types. Pass `skipPolicy:true`
113
+ * for tooling that must see even blocked plugins (e.g. `cc plugin installed`
114
+ * showing why something is blocked). No managed settings file → no filtering.
115
+ *
116
+ * @param {object} [opts] { cwd, scopes?, skipPolicy?, env?, managedSettingsFile? }
117
+ * @returns {Array<{scope, name, version, root, manifest}>}
118
+ */
119
+ export function discoverPlugins(opts = {}) {
120
+ const scopes = opts.scopes || SCOPES;
121
+ const byName = new Map(); // name → record (later scope overrides earlier)
122
+ for (const scope of scopes) {
123
+ const root = scopeRoot(scope, opts);
124
+ if (!dirExists(root)) continue;
125
+ for (const encoded of listDirs(root)) {
126
+ const nameDir = path.join(root, encoded);
127
+ const version = activeVersionForDir(nameDir);
128
+ if (!version) continue;
129
+ const versionDir = path.join(nameDir, version);
130
+ const manifest = parsePluginManifest(versionDir);
131
+ manifest.scope = scope;
132
+ const name = manifest.metadata?.name || encoded;
133
+ byName.set(name, {
134
+ scope,
135
+ name,
136
+ version: manifest.metadata?.version || version,
137
+ root: versionDir,
138
+ manifest,
139
+ });
140
+ }
141
+ }
142
+ const all = [...byName.values()];
143
+ if (opts.skipPolicy) return all;
144
+ // Enforce managed org allow/deny at load time (fail-closed). A malformed
145
+ // managed settings file throws out of loadManagedPluginPolicy — that
146
+ // propagates so a broken org policy never silently degrades to "no policy".
147
+ const managed = loadManagedPluginPolicy({
148
+ env: opts.env,
149
+ managedSettingsFile: opts.managedSettingsFile,
150
+ });
151
+ if (!managed) return all;
152
+ const { kept, dropped } = filterByManagedPolicy(all, managed);
153
+ if (dropped.length > 0) warnDroppedOnce(dropped);
154
+ return kept;
155
+ }
156
+
157
+ // ── internals ────────────────────────────────────────────────────────────
158
+
159
+ function activeVersionForDir(nameDir) {
160
+ if (!dirExists(nameDir)) return null;
161
+ const versions = _deps
162
+ .readdirSync(nameDir, { withFileTypes: true })
163
+ .filter((e) => e.isDirectory())
164
+ .map((e) => e.name)
165
+ .filter((v) => semver.valid(v))
166
+ .sort(semver.rcompare);
167
+ if (versions.length === 0) return null;
168
+ const activeFile = path.join(nameDir, ".active");
169
+ if (_deps.existsSync(activeFile)) {
170
+ try {
171
+ const pinned = _deps.readFileSync(activeFile, "utf8").trim();
172
+ if (versions.includes(pinned)) return pinned;
173
+ } catch {
174
+ /* fall through */
175
+ }
176
+ }
177
+ return versions[0];
178
+ }
179
+
180
+ function dirExists(dir) {
181
+ try {
182
+ return _deps.existsSync(dir) && _deps.statSync(dir).isDirectory();
183
+ } catch {
184
+ return false;
185
+ }
186
+ }
187
+
188
+ function listDirs(dir) {
189
+ try {
190
+ return _deps
191
+ .readdirSync(dir, { withFileTypes: true })
192
+ .filter((e) => e.isDirectory())
193
+ .map((e) => e.name);
194
+ } catch {
195
+ return [];
196
+ }
197
+ }