chainlesschain 0.165.4 → 0.165.6

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 (214) hide show
  1. package/package.json +1 -1
  2. package/src/assets/web-panel/.build-hash +1 -1
  3. package/src/assets/web-panel/assets/{AIOps-C__nnraW.js → AIOps-vfoDb6HZ.js} +1 -1
  4. package/src/assets/web-panel/assets/{ActionButton-CsKWHEZ-.js → ActionButton-Cm5f7p93.js} +1 -1
  5. package/src/assets/web-panel/assets/{Analytics-BhIjfjWO.js → Analytics-yRuvgWxa.js} +3 -3
  6. package/src/assets/web-panel/assets/{AppLayout-BjuXFzWZ.js → AppLayout-BROsC4h1.js} +4 -4
  7. package/src/assets/web-panel/assets/{Artifacts-k6whDkZJ.js → Artifacts-CimHApb1.js} +1 -1
  8. package/src/assets/web-panel/assets/{Audit-C4FBMYRh.js → Audit-DI8kr8zI.js} +1 -1
  9. package/src/assets/web-panel/assets/{BackgroundAgents-C_vRrSXk.js → BackgroundAgents-DSLzKy-n.js} +1 -1
  10. package/src/assets/web-panel/assets/{Backup-CngFZeVp.js → Backup-DkH_n2r1.js} +1 -1
  11. package/src/assets/web-panel/assets/{BaseInput-B_H9EBRm.js → BaseInput-CKSrUoRA.js} +1 -1
  12. package/src/assets/web-panel/assets/{Chat-DLEC5kt7.js → Chat-ClwfMXUS.js} +3 -3
  13. package/src/assets/web-panel/assets/ChatBubbleRenderer-CjMaqfL7.js +1 -0
  14. package/src/assets/web-panel/assets/{Checkbox-m3ClR4xh.js → Checkbox-BYTehcQG.js} +1 -1
  15. package/src/assets/web-panel/assets/{Codegen-CmLSFrgp.js → Codegen-B8AMcp6J.js} +1 -1
  16. package/src/assets/web-panel/assets/{Col-DFGQteHN.js → Col-BNSogPo3.js} +1 -1
  17. package/src/assets/web-panel/assets/{Community-EUUVORHE.js → Community-JxPvGb8O.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compact-2PSg0Yr7.js → Compact-DF7bFAgE.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compliance-CcKur7gB.js → Compliance-Bvhqnwrq.js} +1 -1
  20. package/src/assets/web-panel/assets/{Cowork-DK_Fezfa.js → Cowork-BDS0CJvM.js} +3 -3
  21. package/src/assets/web-panel/assets/{Cron-Dbw89xl7.js → Cron-DqvRfxoQ.js} +2 -2
  22. package/src/assets/web-panel/assets/{Crosschain-CH66h_Ec.js → Crosschain-CDcIvnqS.js} +1 -1
  23. package/src/assets/web-panel/assets/{DID-D1igRb17.js → DID-D7jaUxU3.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dashboard-DVAsrobk.js → Dashboard-B5A18wEV.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dropdown-Dl7W8AQs.js → Dropdown-_6IVkGag.js} +1 -1
  26. package/src/assets/web-panel/assets/{EmailListRenderer-D5tpaEQC.js → EmailListRenderer-C9PeiKCG.js} +1 -1
  27. package/src/assets/web-panel/assets/{FamilyGuardDashboard-O_VQf8gq.js → FamilyGuardDashboard-DXjuEHrX.js} +1 -1
  28. package/src/assets/web-panel/assets/{Federation-BTVcgS9n.js → Federation-B4IAR9zv.js} +1 -1
  29. package/src/assets/web-panel/assets/{FormItemContext-BRnkJAIz.js → FormItemContext-CxQeSNx1.js} +1 -1
  30. package/src/assets/web-panel/assets/{GenericCardRenderer-8OWSCkDj.js → GenericCardRenderer-B8GS-Y3N.js} +1 -1
  31. package/src/assets/web-panel/assets/{Git-WXLYTQj2.js → Git-BS8SouUX.js} +2 -2
  32. package/src/assets/web-panel/assets/{Governance-BezaUhtc.js → Governance-Dq9aYb5Q.js} +1 -1
  33. package/src/assets/web-panel/assets/{Inference-CuRay3Gu.js → Inference-BrCEe9AJ.js} +1 -1
  34. package/src/assets/web-panel/assets/{KnowledgeGraph-DfWLDW1m.js → KnowledgeGraph-DybOpiY2.js} +1 -1
  35. package/src/assets/web-panel/assets/{Logs-C55y2pPY.js → Logs-DBpzbRfx.js} +2 -2
  36. package/src/assets/web-panel/assets/MarkdownRenderer-CFyt8iUd.js +1 -0
  37. package/src/assets/web-panel/assets/{Marketplace-Dk08Uycg.js → Marketplace-Cc7CCSCu.js} +1 -1
  38. package/src/assets/web-panel/assets/{McpTools-C11fSB5V.js → McpTools-DFaIhOfu.js} +3 -3
  39. package/src/assets/web-panel/assets/{Memory-mr0eZc97.js → Memory-KCqlp9ol.js} +2 -2
  40. package/src/assets/web-panel/assets/{MobileBridge-C0iCSRbb.js → MobileBridge-Bys84ySN.js} +1 -1
  41. package/src/assets/web-panel/assets/{MobileProjects-CY-xMG8Z.js → MobileProjects-B-6qU-je.js} +1 -1
  42. package/src/assets/web-panel/assets/{Mtc-Bplqc_Wc.js → Mtc-BgY-fvCq.js} +4 -4
  43. package/src/assets/web-panel/assets/{MtcAudit-BlzwsoSM.js → MtcAudit-B8pCmqQJ.js} +4 -4
  44. package/src/assets/web-panel/assets/{Multisig-BBLmTty6.js → Multisig-BbOmYutA.js} +3 -3
  45. package/src/assets/web-panel/assets/{NLProgramming-DtT1uf1W.js → NLProgramming-BocLQz_M.js} +1 -1
  46. package/src/assets/web-panel/assets/{Notes-DcqQAxTK.js → Notes-CSwbxEop.js} +3 -3
  47. package/src/assets/web-panel/assets/{NotificationSettings-cb0kWoZe.js → NotificationSettings-CqFNSYG3.js} +1 -1
  48. package/src/assets/web-panel/assets/{OrderTableRenderer-kw9HZQNN.js → OrderTableRenderer-C6gMfAk7.js} +1 -1
  49. package/src/assets/web-panel/assets/{Organization-DlTlbkUN.js → Organization-QrDXVdMJ.js} +4 -4
  50. package/src/assets/web-panel/assets/{Overflow-pp1qs-oY.js → Overflow-Cxntotjt.js} +1 -1
  51. package/src/assets/web-panel/assets/{P2P-BESLnyYz.js → P2P-CIdBOwNq.js} +2 -2
  52. package/src/assets/web-panel/assets/{PdhVaultBrowser-B-J1mGuf.js → PdhVaultBrowser-CT5aP8rD.js} +3 -3
  53. package/src/assets/web-panel/assets/{Permissions-B36PeT1_.js → Permissions-DH7OM8XU.js} +4 -4
  54. package/src/assets/web-panel/assets/{PersonalDataHub-CTK2ad-3.js → PersonalDataHub-CnJqbWce.js} +3 -3
  55. package/src/assets/web-panel/assets/{Pipeline-zJNGXsac.js → Pipeline-5C3HG1kH.js} +1 -1
  56. package/src/assets/web-panel/assets/{Privacy-b9fz0Qf0.js → Privacy-B4yIp7xK.js} +1 -1
  57. package/src/assets/web-panel/assets/{ProjectInit-DjZbZgiB.js → ProjectInit-BMvt-2M0.js} +2 -2
  58. package/src/assets/web-panel/assets/{ProjectSettings-C-hHJXlD.js → ProjectSettings-CvSTEMRG.js} +2 -2
  59. package/src/assets/web-panel/assets/Projects-BKBJxEek.js +1 -0
  60. package/src/assets/web-panel/assets/Providers-BdwUqFWN.js +1 -0
  61. package/src/assets/web-panel/assets/{QrScannerModal-A0yoQ3-3.js → QrScannerModal-DtnCKlTm.js} +1 -1
  62. package/src/assets/web-panel/assets/{QuickAsk-DrFgFJNC.js → QuickAsk-sTbgwyNr.js} +1 -1
  63. package/src/assets/web-panel/assets/{Recommend-KC8hF1g4.js → Recommend-Bzr14gSb.js} +1 -1
  64. package/src/assets/web-panel/assets/{RemoteSession-DGTPIk6u.js → RemoteSession-C_TF5IaJ.js} +2 -2
  65. package/src/assets/web-panel/assets/{Reputation-QmbwM7Yk.js → Reputation-GCAM4H2K.js} +1 -1
  66. package/src/assets/web-panel/assets/{Row-BxjJylia.js → Row-Dir0r6OQ.js} +1 -1
  67. package/src/assets/web-panel/assets/{RssFeed-B7Wda1J6.js → RssFeed-CpGnlecX.js} +2 -2
  68. package/src/assets/web-panel/assets/{Search-_plQ_YSA.js → Search-DMMamC6u.js} +1 -1
  69. package/src/assets/web-panel/assets/{Security-CD2YJZfx.js → Security-yqay5yG-.js} +4 -4
  70. package/src/assets/web-panel/assets/{Services-qQE7NmwE.js → Services-C5i2wh9G.js} +2 -2
  71. package/src/assets/web-panel/assets/{Skeleton-DgQqkFf7.js → Skeleton-vUqk35yj.js} +1 -1
  72. package/src/assets/web-panel/assets/{Skills-C4C0oFFt.js → Skills-DzcSkhot.js} +1 -1
  73. package/src/assets/web-panel/assets/{Sla-Px2N2B5c.js → Sla-CB6qbYNr.js} +1 -1
  74. package/src/assets/web-panel/assets/{SpeechSettings-DN6C3lQd.js → SpeechSettings-BHUI7cox.js} +1 -1
  75. package/src/assets/web-panel/assets/{SyncSettings-jm5aH_m7.js → SyncSettings-D-RrsxTJ.js} +2 -2
  76. package/src/assets/web-panel/assets/{Tasks-DlkJcv67.js → Tasks-DFxdOJiq.js} +1 -1
  77. package/src/assets/web-panel/assets/{Templates-DyrTHUBs.js → Templates-SKAob_Aq.js} +1 -1
  78. package/src/assets/web-panel/assets/{Tenant-mC434PtL.js → Tenant-BPfsfHQ-.js} +1 -1
  79. package/src/assets/web-panel/assets/{Terminal-o0_s3NgK.js → Terminal-8iw7FpUf.js} +2 -2
  80. package/src/assets/web-panel/assets/{TimelineRenderer-DAGlAXOw.js → TimelineRenderer-C4Gjj5Su.js} +1 -1
  81. package/src/assets/web-panel/assets/{Tokens-BnulS_bs.js → Tokens-COYhN18e.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trigger-suQbVXYM.js → Trigger-B-JzXcf6.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trust-DbIJAPo5.js → Trust-BJ5bg0nI.js} +1 -1
  84. package/src/assets/web-panel/assets/{UkeySign-CQNpbIHi.js → UkeySign-BG_ByRMf.js} +1 -1
  85. package/src/assets/web-panel/assets/{VideoEditing-D8aWCERk.js → VideoEditing-B3kOnSiU.js} +1 -1
  86. package/src/assets/web-panel/assets/{Wallet-CuLpYfZc.js → Wallet-CcoGFnK7.js} +2 -2
  87. package/src/assets/web-panel/assets/{WebAuthn-CsfG8ZMc.js → WebAuthn-DdT8vRC_.js} +4 -4
  88. package/src/assets/web-panel/assets/{WorkflowEditor-BYsEAZbd.js → WorkflowEditor-CZu3M9N8.js} +1 -1
  89. package/src/assets/web-panel/assets/{chat-FT4Um3-I.js → chat-DxJt3Qba.js} +1 -1
  90. package/src/assets/web-panel/assets/{colors-CKbS_Wlt.js → colors-DdYfwJaB.js} +1 -1
  91. package/src/assets/web-panel/assets/{compact-item-CFJtzuJM.js → compact-item-BG3SUzwj.js} +1 -1
  92. package/src/assets/web-panel/assets/{createContext-B9kSRW6K.js → createContext-By51z2-1.js} +1 -1
  93. package/src/assets/web-panel/assets/devWarning-DorRZcI9.js +1 -0
  94. package/src/assets/web-panel/assets/{hasIn-DR-x0Xpi.js → hasIn-B0apZ3KJ.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-DjEP7WkH.js → index-3jWkIu5j.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-7jHr_A_k.js → index-B3rWuPMW.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-DcfxhKXR.js → index-B5rLiEo0.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-tqSEHPRT.js → index-BBPf3TpM.js} +1 -1
  99. package/src/assets/web-panel/assets/index-BETiL0a3.js +1 -0
  100. package/src/assets/web-panel/assets/{index-Cr2FLUsD.js → index-BIrJfqO4.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-Bt4fS2eL.js → index-BLIbGOLX.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-BwR7yc2h.js → index-BP94CrBV.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-BVPU43b1.js → index-BWF8_mkx.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-DShSkww-.js → index-BYINoRLK.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-Bzz0C1BZ.js → index-BZ_0qm4g.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-KqDaCApw.js → index-BewKYrX9.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-BW-t9n-q.js → index-BfWYsB9t.js} +1 -1
  108. package/src/assets/web-panel/assets/index-BhAVHuW8.js +1 -0
  109. package/src/assets/web-panel/assets/{index-B7osrXqy.js → index-Bissefox.js} +3 -3
  110. package/src/assets/web-panel/assets/{index-DW4BWjTk.js → index-BkvcEo4p.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-DdkIhfi-.js → index-Bos5_aMV.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-DTxNy0ZU.js → index-C1C9i79A.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-QBSeNVO3.js → index-CAhHirVH.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-D4LfPvWA.js → index-CNIxXMmN.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-C_enk9I_.js → index-CW93tZDv.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-WE08gMJP.js → index-CWEuZWCK.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-r2VI1Ikk.js → index-Cp6e-hGw.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-Dap9c3eD.js → index-CpjZhgNd.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-DWdqx7zI.js → index-CzmQElZy.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-BGbPnC5x.js → index-DCn8IcxV.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-DiNUEpSQ.js → index-DE76D-kA.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-DtkFqPVA.js → index-DaIz0NAW.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-CGaXUsIs.js → index-Da_WBdLX.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-B3ZX81mK.js → index-Dz49VOyQ.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-BK3TiKVq.js → index-Q2SCX-MI.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-CJmIQ9XR.js → index-RD6O-0C6.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-CvrCCqJt.js → index-SQqOlR3d.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-BI5hP_qy.js → index-ZFbps9po.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-DeSgw-WV.js → index-ZxtP8jG7.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-DMGmg497.js → index-bP_VlDb4.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-BfFCQHL3.js → index-kf57LJ93.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-OSzjTzGM.js → index-oxRrmFO9.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-C2JvmYRc.js → index-p1_8SbI1.js} +1 -1
  134. package/src/assets/web-panel/assets/{initDefaultProps-Dd7n7VgI.js → initDefaultProps-BWJktcg8.js} +1 -1
  135. package/src/assets/web-panel/assets/{motion-BwxouRw1.js → motion-BEXBzUFV.js} +1 -1
  136. package/src/assets/web-panel/assets/{move-DObN0PG9.js → move-BUKwn2sx.js} +1 -1
  137. package/src/assets/web-panel/assets/{mtc-parser-B3nOUQc7.js → mtc-parser-FTI7btqQ.js} +1 -1
  138. package/src/assets/web-panel/assets/{omit-o9WU2Nbl.js → omit-CsLtSPH3.js} +1 -1
  139. package/src/assets/web-panel/assets/parsers-YNLm7sQ0.js +4 -0
  140. package/src/assets/web-panel/assets/{pickAttrs-CL_vvfQ_.js → pickAttrs-DWZxsT_S.js} +1 -1
  141. package/src/assets/web-panel/assets/{placementArrow-CYrM2tyK.js → placementArrow-Bcfs43CX.js} +1 -1
  142. package/src/assets/web-panel/assets/{responsiveObserve-k_06cQ96.js → responsiveObserve-a9bcC1p-.js} +1 -1
  143. package/src/assets/web-panel/assets/{slide-CPKG0pxS.js → slide-BDTzgOXN.js} +1 -1
  144. package/src/assets/web-panel/assets/{statusUtils-BDDEeLTw.js → statusUtils-Iq1j_HUg.js} +1 -1
  145. package/src/assets/web-panel/assets/{styleChecker-Cmb7RTh7.js → styleChecker-DAZEYxeh.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFlexGapSupport-e2IedC4N.js → useFlexGapSupport-DVkjyDrh.js} +1 -1
  147. package/src/assets/web-panel/assets/{useFs-CE_gxVnm.js → useFs-wXTpNTHt.js} +1 -1
  148. package/src/assets/web-panel/assets/{usePersonalDataHub-BkMIIKMz.js → usePersonalDataHub-CtQM_aZr.js} +1 -1
  149. package/src/assets/web-panel/assets/{vnode-DW886XV1.js → vnode-hjEoG85v.js} +1 -1
  150. package/src/assets/web-panel/assets/{zoom-Dwo_adaf.js → zoom-BHVpyKg_.js} +1 -1
  151. package/src/assets/web-panel/index.html +1 -1
  152. package/src/command-help-index.json +3 -3
  153. package/src/commands/agent.js +14 -2
  154. package/src/commands/artifacts.js +235 -0
  155. package/src/commands/background-session.js +100 -10
  156. package/src/commands/cowork.js +86 -57
  157. package/src/commands/mcp.js +5 -0
  158. package/src/commands/plugin.js +145 -29
  159. package/src/commands/remote-control.js +295 -110
  160. package/src/commands/session-location.js +20 -1
  161. package/src/commands/session-message.js +171 -0
  162. package/src/commands/session.js +75 -69
  163. package/src/data/changelog.json +29 -3
  164. package/src/gateways/ws/message-dispatcher.js +57 -0
  165. package/src/harness/mcp-client.js +141 -10
  166. package/src/lib/artifact-access-ledger.js +1 -0
  167. package/src/lib/artifact-deletion-ledger.js +1 -0
  168. package/src/lib/artifact-recovery.js +970 -0
  169. package/src/lib/artifact-workbench-projection.js +171 -0
  170. package/src/lib/background-session-transport.js +153 -27
  171. package/src/lib/dynamic-workflow-runtime.js +408 -1
  172. package/src/lib/execution-location-target.js +4 -2
  173. package/src/lib/mcp-client.js +2 -0
  174. package/src/lib/mcp-headers-helper.js +47 -5
  175. package/src/lib/mcp-oauth.js +55 -6
  176. package/src/lib/mcp-stdio-executable-identity.js +16 -0
  177. package/src/lib/mcp-stdio-native-code-policy.js +74 -0
  178. package/src/lib/mcp-stdio-package-materialization.js +12 -3
  179. package/src/lib/mcp-tls.js +230 -0
  180. package/src/lib/output-styles.js +12 -0
  181. package/src/lib/plugin-runtime/install.js +614 -71
  182. package/src/lib/plugin-runtime/marketplace-archive-source.js +331 -0
  183. package/src/lib/plugin-runtime/marketplace-catalog.js +52 -6
  184. package/src/lib/plugin-runtime/marketplace-remote-artifacts.js +1 -0
  185. package/src/lib/plugin-runtime/marketplace-source-adapter.js +163 -0
  186. package/src/lib/plugin-runtime/marketplace-source-cache.js +11 -1
  187. package/src/lib/plugin-runtime/remote-source.js +435 -55
  188. package/src/lib/plugin-security.js +393 -41
  189. package/src/lib/plugin-source-identity.js +850 -0
  190. package/src/lib/process-execution-broker/index.js +112 -0
  191. package/src/lib/process-execution-broker/platform-sandbox.js +57 -3
  192. package/src/lib/process-execution-broker/workspace-transaction.js +26 -0
  193. package/src/lib/remote-approval-bridge.js +136 -46
  194. package/src/lib/remote-control.js +217 -31
  195. package/src/lib/session-message-fabric.js +1105 -0
  196. package/src/lib/session-projection.js +171 -12
  197. package/src/lib/session-runtime-retention.js +155 -0
  198. package/src/lib/settings-loader.cjs +29 -0
  199. package/src/repl/agent-repl.js +51 -14
  200. package/src/repl/permission-prompt.js +31 -8
  201. package/src/repl/repl-keybindings.js +55 -0
  202. package/src/runtime/agent-core.js +63 -2
  203. package/src/runtime/headless-runner.js +47 -66
  204. package/src/runtime/headless-stream.js +18 -24
  205. package/src/runtime/mcp-config.js +2 -0
  206. package/src/runtime/policies/agent-policy.js +2 -0
  207. package/src/assets/web-panel/assets/ChatBubbleRenderer-xijcWkJe.js +0 -1
  208. package/src/assets/web-panel/assets/MarkdownRenderer-hDpWdiBc.js +0 -1
  209. package/src/assets/web-panel/assets/Projects-BC9KSDM6.js +0 -1
  210. package/src/assets/web-panel/assets/Providers-Dz-CAYYU.js +0 -1
  211. package/src/assets/web-panel/assets/devWarning-DJZ0gAMu.js +0 -1
  212. package/src/assets/web-panel/assets/index-Bizb4hkw.js +0 -1
  213. package/src/assets/web-panel/assets/index-OHSl-xCh.js +0 -1
  214. package/src/assets/web-panel/assets/parsers-UEvh_ShA.js +0 -4
@@ -16,11 +16,12 @@
16
16
  * 2. Single-plugin manifest — one plugin, no selection needed:
17
17
  * { "name": "toml-tools", "source": "https://github.com/x/toml.git", "ref": "v1.0.0" }
18
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.
19
+ * The remote layer is an INDIRECTION: it resolves a name to a static package
20
+ * source that the existing installer (`installFromSource`) can consume.
21
+ * Static git and digest-bound archive sources both reuse the existing install
22
+ * core. Downloaded registry JSON and archives are cached content-addressed so
23
+ * browsing / installing works OFFLINE, while bearer tokens never become
24
+ * persisted source authority.
24
25
  */
25
26
 
26
27
  import fs from "fs";
@@ -28,7 +29,21 @@ import os from "os";
28
29
  import path from "path";
29
30
  import crypto from "crypto";
30
31
  import { getElectronUserDataDir } from "../paths.js";
32
+ import {
33
+ enforcePluginSourcePolicy,
34
+ resolvePluginManagedPolicy,
35
+ } from "../plugin-security.js";
36
+ import {
37
+ assertPluginGitTransportSafe,
38
+ parsePluginGitSource,
39
+ redactPluginSourceForDisplay,
40
+ } from "../plugin-source-identity.js";
31
41
  import { createMarketplaceNetworkTransport } from "./marketplace-network.js";
42
+ import { fetchAndMaterializeMarketplaceArchive } from "./marketplace-archive-source.js";
43
+ import {
44
+ assertMarketplaceSourceExecutable,
45
+ normalizeMarketplacePackageSource,
46
+ } from "./marketplace-source-adapter.js";
32
47
 
33
48
  export const _deps = {
34
49
  // Node 22+ global fetch; injectable for tests.
@@ -52,6 +67,11 @@ const DEFAULT_TIMEOUT_MS = 15_000;
52
67
  export const MAX_REGISTRY_DOCUMENT_BYTES = 4 * 1024 * 1024;
53
68
  export const MAX_REGISTRY_CACHE_CANDIDATES = 64;
54
69
  const SHA256_RE = /^[a-f0-9]{64}$/;
70
+ const CONTROL_RE = /[\u0000-\u001f\u007f]/u;
71
+ const MAX_REGISTRY_SOURCE_CHARS = 4096;
72
+ const MAX_REGISTRY_REF_CHARS = 256;
73
+ const fetchedRegistryAuthorities = new WeakMap();
74
+ const registryResolutionAuthorities = new WeakMap();
55
75
 
56
76
  /** True when `raw` looks like a remote registry/manifest URL (http(s) + .json). */
57
77
  export function isRemoteSource(raw) {
@@ -67,15 +87,19 @@ export function isRemoteSource(raw) {
67
87
  * explicit opt-in (opts.allowInsecure / CC_PLUGIN_REGISTRY_ALLOW_HTTP=1).
68
88
  */
69
89
  export function assertRegistryUrlSafe(url, { allowInsecure = false } = {}) {
90
+ const displayUrl = redactPluginSourceForDisplay(url);
70
91
  let u;
71
92
  try {
72
93
  u = new URL(String(url));
73
94
  } catch {
74
- throw new Error(`invalid registry URL: ${url}`);
95
+ throw new Error(`invalid registry URL: ${displayUrl}`);
96
+ }
97
+ if (u.username || u.password) {
98
+ throw new Error("registry URL credentials are not supported");
75
99
  }
76
100
  if (u.protocol === "https:") return;
77
101
  if (u.protocol !== "http:") {
78
- throw new Error(`registry URL must be http(s): ${url}`);
102
+ throw new Error(`registry URL must be http(s): ${displayUrl}`);
79
103
  }
80
104
  // WHATWG URL keeps the brackets on IPv6 hostnames — strip before comparing.
81
105
  const host = u.hostname.replace(/^\[|\]$/g, "").toLowerCase();
@@ -85,7 +109,7 @@ export function assertRegistryUrlSafe(url, { allowInsecure = false } = {}) {
85
109
  allowInsecure === true || process.env.CC_PLUGIN_REGISTRY_ALLOW_HTTP === "1";
86
110
  if (loopback || optIn) return;
87
111
  throw new Error(
88
- `plain-HTTP registry rejected: ${url} — a network MITM controls both the ` +
112
+ `plain-HTTP registry rejected: ${displayUrl} — a network MITM controls both the ` +
89
113
  `source and its sha256, so the integrity check verifies nothing. Use ` +
90
114
  `https, or opt in with --allow-insecure-registry / ` +
91
115
  `CC_PLUGIN_REGISTRY_ALLOW_HTTP=1 on a trusted network.`,
@@ -143,6 +167,16 @@ export function resolveRegistryToken(url, { token, config } = {}) {
143
167
  * documentSha256: string }>}
144
168
  */
145
169
  export async function fetchRegistry(url, opts = {}) {
170
+ const displayUrl = redactPluginSourceForDisplay(url);
171
+ assertRegistryUrlSafe(url, { allowInsecure: opts.allowInsecure });
172
+ const managedPolicy = resolveManagedPolicy(opts);
173
+ // This gate intentionally runs before transport construction. A blocked or
174
+ // non-allowlisted marketplace must not reach proxy helpers, DNS, or fetch.
175
+ enforcePluginSourcePolicy(url, managedPolicy, {
176
+ action: opts.policyAction || "registry-fetch",
177
+ cwd: opts.cwd,
178
+ kindHint: "registry",
179
+ });
146
180
  const {
147
181
  token,
148
182
  cacheDir,
@@ -150,7 +184,6 @@ export async function fetchRegistry(url, opts = {}) {
150
184
  allowCache = true,
151
185
  offline = false,
152
186
  } = opts;
153
- assertRegistryUrlSafe(url, { allowInsecure: opts.allowInsecure });
154
187
  const expectedSha256 = normalizeRegistrySha256(
155
188
  opts.expectedSha256,
156
189
  "expected registry document SHA-256",
@@ -163,14 +196,21 @@ export async function fetchRegistry(url, opts = {}) {
163
196
  if (!allowCache) {
164
197
  throw new Error("offline registry mode requires immutable cache access");
165
198
  }
166
- const cached = readCachedRegistry({
167
- url,
168
- cacheDir,
169
- expectedSha256,
170
- });
171
- if (cached) return cached;
199
+ let cached;
200
+ try {
201
+ cached = readCachedRegistry({
202
+ url,
203
+ cacheDir,
204
+ expectedSha256,
205
+ });
206
+ } catch (error) {
207
+ throw new Error(
208
+ `registry ${displayUrl} verified immutable cache rejected: ${publicRegistryCacheErrorMessage(error)}`,
209
+ );
210
+ }
211
+ if (cached) return bindFetchedRegistry(cached, url, managedPolicy);
172
212
  throw new Error(
173
- `registry ${url} is unavailable in the verified immutable cache` +
213
+ `registry ${displayUrl} is unavailable in the verified immutable cache` +
174
214
  (expectedSha256 ? ` at SHA-256 ${expectedSha256}` : ""),
175
215
  );
176
216
  }
@@ -190,37 +230,60 @@ export async function fetchRegistry(url, opts = {}) {
190
230
  try {
191
231
  res = await fetchImpl(url, {
192
232
  headers,
193
- redirect: "follow",
233
+ redirect: "manual",
194
234
  signal: controller.signal,
195
235
  });
196
236
  } finally {
197
237
  clearTimeout(timer);
198
238
  }
239
+ if (
240
+ res?.redirected === true ||
241
+ (Number(res?.status) >= 300 && Number(res?.status) < 400) ||
242
+ responseUrlChanged(url, res?.url)
243
+ ) {
244
+ throw publicRegistryFailure(
245
+ "registry redirects are disabled because each hop cannot be policy-validated",
246
+ );
247
+ }
199
248
  if (!res.ok) {
200
- const error = new Error(
201
- `registry fetch failed: HTTP ${res.status} ${res.statusText || ""}`.trim(),
249
+ const status = Number(res.status);
250
+ throw publicRegistryFailure(
251
+ `registry fetch failed: HTTP ${Number.isInteger(status) ? status : "unknown"}`,
252
+ { cacheFallbackAllowed: status >= 500 },
202
253
  );
203
- error.cacheFallbackAllowed = Number(res.status) >= 500;
204
- throw error;
205
254
  }
206
- const bytes = await readBoundedRegistryResponse(res);
255
+ let bytes;
256
+ try {
257
+ bytes = await readBoundedRegistryResponse(res);
258
+ } catch {
259
+ throw publicRegistryFailure("registry response body is invalid");
260
+ }
207
261
  const documentSha256 = sha256(bytes);
208
262
  if (expectedSha256 && documentSha256 !== expectedSha256) {
209
- throw new Error(
263
+ throw publicRegistryFailure(
210
264
  `registry document SHA-256 mismatch: expected ${expectedSha256}, got ${documentSha256}`,
211
265
  );
212
266
  }
213
- const text = decodeRegistryUtf8(bytes);
214
- const registry = validateRegistry(JSON.parse(text), url);
267
+ let registry;
268
+ try {
269
+ const text = decodeRegistryUtf8(bytes);
270
+ registry = validateRegistry(JSON.parse(text), url);
271
+ } catch {
272
+ throw publicRegistryFailure("registry document is invalid");
273
+ }
215
274
  if (allowCache) writeImmutableCache(url, cacheDir, bytes, documentSha256);
216
- return {
217
- registry,
218
- fromCache: false,
219
- documentSha256,
220
- ...(networkTransport?.authority
221
- ? { networkAuthority: networkTransport.authority }
222
- : {}),
223
- };
275
+ return bindFetchedRegistry(
276
+ {
277
+ registry,
278
+ fromCache: false,
279
+ documentSha256,
280
+ ...(networkTransport?.authority
281
+ ? { networkAuthority: networkTransport.authority }
282
+ : {}),
283
+ },
284
+ url,
285
+ managedPolicy,
286
+ );
224
287
  } catch (err) {
225
288
  networkErr = err;
226
289
  cacheFallbackAllowed =
@@ -228,7 +291,12 @@ export async function fetchRegistry(url, opts = {}) {
228
291
  err?.name === "AbortError" ||
229
292
  err instanceof TypeError;
230
293
  } finally {
231
- await networkTransport?.close();
294
+ try {
295
+ await networkTransport?.close();
296
+ } catch {
297
+ // Transport teardown is best-effort and must not override a verified
298
+ // response or surface an implementation error that echoed request URLs.
299
+ }
232
300
  }
233
301
 
234
302
  // Only transport failures and server-side outages may fall back. Authentication,
@@ -240,15 +308,15 @@ export async function fetchRegistry(url, opts = {}) {
240
308
  cacheDir,
241
309
  expectedSha256,
242
310
  });
243
- if (cached) return cached;
311
+ if (cached) return bindFetchedRegistry(cached, url, managedPolicy);
244
312
  } catch (cacheError) {
245
313
  throw new Error(
246
- `could not fetch registry ${url}: ${networkErr.message}; verified immutable cache rejected: ${cacheError.message}`,
314
+ `could not fetch registry ${displayUrl}: ${publicRegistryErrorMessage(networkErr)}; verified immutable cache rejected: ${publicRegistryCacheErrorMessage(cacheError)}`,
247
315
  );
248
316
  }
249
317
  }
250
318
  throw new Error(
251
- `could not fetch registry ${url}: ${networkErr ? networkErr.message : "unknown error"}` +
319
+ `could not fetch registry ${displayUrl}: ${publicRegistryErrorMessage(networkErr)}` +
252
320
  (allowCache && cacheFallbackAllowed
253
321
  ? " (no verified immutable cache available)"
254
322
  : ""),
@@ -475,37 +543,204 @@ function sha256(value) {
475
543
 
476
544
  /** Validate + normalize a fetched document into a registry object. Throws on garbage. */
477
545
  export function validateRegistry(doc, url = "") {
546
+ const displayUrl = redactPluginSourceForDisplay(url);
478
547
  if (!doc || typeof doc !== "object" || Array.isArray(doc)) {
479
- throw new Error(`registry at ${url} is not a JSON object`);
548
+ throw new Error(`registry at ${displayUrl} is not a JSON object`);
480
549
  }
481
550
  if (Array.isArray(doc.plugins)) {
482
551
  for (const p of doc.plugins) {
483
- if (!p || typeof p !== "object" || !p.name || !p.source) {
552
+ if (
553
+ !p ||
554
+ typeof p !== "object" ||
555
+ typeof p.name !== "string" ||
556
+ !p.name.trim() ||
557
+ (p.source == null && p.command == null && p.headersHelper == null)
558
+ ) {
484
559
  throw new Error(
485
- `registry at ${url} has a plugin entry missing name/source`,
560
+ `registry at ${displayUrl} has a plugin entry missing name/source`,
486
561
  );
487
562
  }
563
+ const normalized = normalizeMarketplacePackageSource(p);
564
+ if (normalized.type === "git") normalizeRegistryPluginSource(p);
488
565
  }
489
566
  return doc;
490
567
  }
491
568
  // Single-plugin manifest.
492
- if (doc.source) {
569
+ if (doc.source != null || doc.command != null || doc.headersHelper != null) {
570
+ const normalized = normalizeMarketplacePackageSource(doc);
571
+ if (normalized.type === "git") normalizeRegistryPluginSource(doc);
493
572
  return { plugins: [doc] };
494
573
  }
495
574
  throw new Error(
496
- `registry at ${url} must have a "plugins" array or a top-level "source"`,
575
+ `registry at ${displayUrl} must have a "plugins" array or a top-level "source"`,
576
+ );
577
+ }
578
+
579
+ function freezeRegistryValue(value) {
580
+ if (!value || typeof value !== "object" || Object.isFrozen(value)) {
581
+ return value;
582
+ }
583
+ for (const child of Object.values(value)) freezeRegistryValue(child);
584
+ return Object.freeze(value);
585
+ }
586
+
587
+ function bindFetchedRegistry(result, registryUrl, managedPolicy) {
588
+ const registry = freezeRegistryValue(result.registry);
589
+ fetchedRegistryAuthorities.set(
590
+ registry,
591
+ Object.freeze({
592
+ registryUrl: String(registryUrl),
593
+ documentSha256: result.documentSha256 || null,
594
+ managedPolicy,
595
+ }),
497
596
  );
597
+ return { ...result, registry };
598
+ }
599
+
600
+ /** Validate one catalog entry as a bounded, remote Git source. */
601
+ export function normalizeRegistryPluginSource(entry) {
602
+ if (!entry || typeof entry !== "object") {
603
+ throw new Error("registry plugin entry is invalid");
604
+ }
605
+ if (
606
+ typeof entry.source !== "string" ||
607
+ entry.source !== entry.source.trim() ||
608
+ !entry.source ||
609
+ entry.source.length > MAX_REGISTRY_SOURCE_CHARS ||
610
+ CONTROL_RE.test(entry.source) ||
611
+ entry.source.includes("#")
612
+ ) {
613
+ throw new Error("registry plugin source must be a bounded string");
614
+ }
615
+ let ref = null;
616
+ if (entry.ref != null && entry.ref !== "") {
617
+ if (
618
+ typeof entry.ref !== "string" ||
619
+ entry.ref !== entry.ref.trim() ||
620
+ !entry.ref ||
621
+ entry.ref.length > MAX_REGISTRY_REF_CHARS ||
622
+ CONTROL_RE.test(entry.ref) ||
623
+ entry.ref.includes("#") ||
624
+ entry.source.includes("#")
625
+ ) {
626
+ throw new Error("registry plugin ref must be a bounded exact string");
627
+ }
628
+ ref = entry.ref;
629
+ }
630
+ const source = ref ? `${entry.source}#${ref}` : entry.source;
631
+ let git;
632
+ try {
633
+ git = parsePluginGitSource(source);
634
+ assertPluginGitTransportSafe(git, {
635
+ allowFile: false,
636
+ requireRemote: true,
637
+ });
638
+ } catch {
639
+ throw new Error("registry plugin source must be a safe remote Git locator");
640
+ }
641
+ return { source, ref, git };
642
+ }
643
+
644
+ /**
645
+ * Issue an unforgeable in-process capability for a selected entry. The entry
646
+ * must be an exact member of a registry object returned by fetchRegistry;
647
+ * callers cannot manufacture registry metadata to downgrade source policy.
648
+ */
649
+ export function authorizeRegistryPluginEntry(
650
+ registry,
651
+ entry,
652
+ { registryUrl, cwd } = {},
653
+ ) {
654
+ const fetched = fetchedRegistryAuthorities.get(registry);
655
+ if (!fetched || fetched.registryUrl !== String(registryUrl)) {
656
+ throw new Error("registry source authority is missing or mismatched");
657
+ }
658
+ if (!registry.plugins?.some((candidate) => candidate === entry)) {
659
+ throw new Error("registry entry is not bound to the fetched catalog");
660
+ }
661
+ const normalized = inspectRegistryEntrySource(entry);
662
+ if (normalized.sourceType === "git") {
663
+ enforcePluginSourcePolicy(normalized.source, fetched.managedPolicy, {
664
+ action: "registry-resolved-source",
665
+ blockedOnly: true,
666
+ cwd,
667
+ kindHint: "git",
668
+ });
669
+ } else {
670
+ let registryOrigin;
671
+ let archiveOrigin;
672
+ try {
673
+ registryOrigin = new URL(fetched.registryUrl).origin;
674
+ archiveOrigin = new URL(normalized.sourceIdentity).origin;
675
+ } catch {
676
+ throw new Error("registry archive source URL authority is invalid");
677
+ }
678
+ if (archiveOrigin !== registryOrigin) {
679
+ throw new Error("registry archive source must use the registry origin");
680
+ }
681
+ }
682
+ const authority = Object.freeze({});
683
+ registryResolutionAuthorities.set(
684
+ authority,
685
+ Object.freeze({
686
+ registryUrl: fetched.registryUrl,
687
+ source: normalized.source,
688
+ declaredSource: normalized.sourceIdentity,
689
+ sourceType: normalized.sourceType,
690
+ ref: normalized.ref,
691
+ archiveSha256:
692
+ normalized.sourceType === "archive"
693
+ ? normalized.archiveSpec.sha256
694
+ : null,
695
+ payloadSha256: null,
696
+ documentSha256: fetched.documentSha256,
697
+ }),
698
+ );
699
+ return {
700
+ ...normalized,
701
+ registryResolutionAuthority: authority,
702
+ };
703
+ }
704
+
705
+ /** Consume-only validation used by the installer before any target I/O. */
706
+ export function assertRegistryResolutionAuthority(
707
+ authority,
708
+ { registryUrl, source, ref } = {},
709
+ ) {
710
+ const issued = registryResolutionAuthorities.get(authority);
711
+ if (
712
+ !issued ||
713
+ issued.registryUrl !== String(registryUrl) ||
714
+ (issued.source ?? null) !== (source ?? null) ||
715
+ (issued.ref || null) !== (ref || null)
716
+ ) {
717
+ throw new Error("registry source authority is missing or mismatched");
718
+ }
719
+ return issued;
498
720
  }
499
721
 
500
722
  /** List installable entries from a registry (for `cc plugin search`). */
501
723
  export function listRegistryPlugins(registry) {
502
- return (registry.plugins || []).map((p) => ({
503
- name: p.name,
504
- version: p.version || null,
505
- source: p.source,
506
- ref: p.ref || null,
507
- description: p.description || "",
508
- }));
724
+ return (registry.plugins || []).map((p) => {
725
+ const source = normalizeMarketplacePackageSource(p);
726
+ return {
727
+ name: p.name,
728
+ version: p.version || null,
729
+ source:
730
+ source.type === "archive"
731
+ ? source.url
732
+ : source.type === "git"
733
+ ? source.source
734
+ : null,
735
+ sourceType: source.type,
736
+ ref: source.type === "git" ? source.ref : null,
737
+ description: p.description || "",
738
+ enabled: source.type !== "dynamic-disabled",
739
+ ...(source.type === "dynamic-disabled"
740
+ ? { disabledReason: source.reason }
741
+ : {}),
742
+ };
743
+ });
509
744
  }
510
745
 
511
746
  /**
@@ -543,20 +778,30 @@ export function resolvePluginEntry(registry, name) {
543
778
  * entry: object, fromCache: boolean }>}
544
779
  */
545
780
  export async function resolveRemoteSource(url, opts = {}) {
781
+ assertRegistryUrlSafe(url, { allowInsecure: opts.allowInsecure });
782
+ const managedPolicy = resolveManagedPolicy(opts);
546
783
  const token = resolveRegistryToken(url, opts);
547
784
  const { registry, fromCache, documentSha256, networkAuthority } =
548
785
  await fetchRegistry(url, {
549
786
  ...opts,
550
787
  token,
788
+ managedPolicy,
551
789
  });
552
790
  const entry = resolvePluginEntry(registry, opts.name);
553
- // Carry a `#ref` on the source string so installFromSource pins the checkout,
554
- // matching its existing `owner/repo#ref` / `url#ref` convention.
555
- const source = entry.ref ? `${entry.source}#${entry.ref}` : entry.source;
791
+ const authorized = authorizeRegistryPluginEntry(registry, entry, {
792
+ registryUrl: url,
793
+ cwd: opts.cwd,
794
+ });
795
+ const resolvedSource =
796
+ opts.materialize === false
797
+ ? authorized
798
+ : await resolveRegistryEntrySource(url, entry, {
799
+ ...opts,
800
+ token,
801
+ registryResolutionAuthority: authorized.registryResolutionAuthority,
802
+ });
556
803
  return {
557
- source,
558
- ref: entry.ref || null,
559
- sha256: entry.sha256 || null,
804
+ ...resolvedSource,
560
805
  entry,
561
806
  fromCache,
562
807
  documentSha256,
@@ -564,6 +809,141 @@ export async function resolveRemoteSource(url, opts = {}) {
564
809
  };
565
810
  }
566
811
 
812
+ /** Inspect one selected entry without fetching any candidate source bytes. */
813
+ export function inspectRegistryEntrySource(entry) {
814
+ const packageSource = assertMarketplaceSourceExecutable(entry);
815
+ if (packageSource.type === "archive") {
816
+ return {
817
+ source: null,
818
+ sourceIdentity: packageSource.url,
819
+ sourceType: "archive",
820
+ ref: null,
821
+ sha256: entry.sha256 || null,
822
+ archiveSpec: packageSource,
823
+ };
824
+ }
825
+ // Preserve the canonical remote Git validation from the registry security
826
+ // boundary rather than accepting the adapter's more general local shape.
827
+ const normalized = normalizeRegistryPluginSource(entry);
828
+ return {
829
+ source: normalized.source,
830
+ sourceIdentity: entry.source,
831
+ sourceType: "git",
832
+ ref: normalized.ref,
833
+ sha256: entry.sha256 || null,
834
+ };
835
+ }
836
+
837
+ /** Materialize one already-selected, preflight-approved registry entry. */
838
+ export async function resolveRegistryEntrySource(url, entry, opts = {}) {
839
+ const inspected = inspectRegistryEntrySource(entry);
840
+ const issued = assertRegistryResolutionAuthority(
841
+ opts.registryResolutionAuthority,
842
+ {
843
+ registryUrl: url,
844
+ source: inspected.source,
845
+ ref: inspected.ref,
846
+ },
847
+ );
848
+ if (
849
+ issued.sourceType !== inspected.sourceType ||
850
+ issued.declaredSource !== inspected.sourceIdentity ||
851
+ issued.archiveSha256 !==
852
+ (inspected.sourceType === "archive" ? inspected.archiveSpec.sha256 : null)
853
+ ) {
854
+ throw new Error("registry source authority is missing or mismatched");
855
+ }
856
+ if (inspected.sourceType === "archive") {
857
+ const archive = await fetchAndMaterializeMarketplaceArchive({
858
+ registryUrl: url,
859
+ url: inspected.archiveSpec.requestUrl || inspected.archiveSpec.url,
860
+ sha256: inspected.archiveSpec.sha256,
861
+ token: opts.token,
862
+ allowInsecure: opts.allowInsecure === true,
863
+ offline: opts.offline === true,
864
+ artifactCacheDir: opts.artifactCacheDir,
865
+ sourceCacheDir: opts.archiveSourceCacheDir,
866
+ timeoutMs: opts.timeoutMs,
867
+ fetchImpl: opts.archiveFetchImpl,
868
+ resolveHostname: opts.resolveHostname,
869
+ proxyUrl: opts.proxyUrl,
870
+ pacFile: opts.pacFile,
871
+ caFile: opts.caFile,
872
+ });
873
+ const materializedAuthority = Object.freeze({});
874
+ registryResolutionAuthorities.set(
875
+ materializedAuthority,
876
+ Object.freeze({
877
+ ...issued,
878
+ source: archive.dir,
879
+ payloadSha256: archive.authority.payloadSha256,
880
+ }),
881
+ );
882
+ return {
883
+ ...inspected,
884
+ source: archive.dir,
885
+ archiveAuthority: archive.authority,
886
+ registryResolutionAuthority: materializedAuthority,
887
+ };
888
+ }
889
+ return {
890
+ ...inspected,
891
+ registryResolutionAuthority: opts.registryResolutionAuthority,
892
+ };
893
+ }
894
+
895
+ function resolveManagedPolicy(opts) {
896
+ return resolvePluginManagedPolicy(opts);
897
+ }
898
+
899
+ function responseUrlChanged(requested, effective) {
900
+ if (!effective) return false;
901
+ try {
902
+ const left = new URL(String(requested));
903
+ const right = new URL(String(effective));
904
+ for (const value of [left, right]) {
905
+ value.username = "";
906
+ value.password = "";
907
+ value.hash = "";
908
+ }
909
+ return left.href !== right.href;
910
+ } catch {
911
+ return true;
912
+ }
913
+ }
914
+
915
+ function publicRegistryErrorMessage(error) {
916
+ if (!error) return "unknown error";
917
+ if (typeof error.publicMessage === "string") return error.publicMessage;
918
+ if (error.name === "AbortError") return "registry request timed out";
919
+ if (error instanceof TypeError) return "registry transport failed";
920
+ return "registry request failed";
921
+ }
922
+
923
+ function publicRegistryCacheErrorMessage(error) {
924
+ const message = String(error?.message || "");
925
+ if (message.includes("digest mismatch")) {
926
+ return "registry immutable cache digest mismatch";
927
+ }
928
+ if (message.includes("ambiguous")) {
929
+ return "registry immutable cache is ambiguous";
930
+ }
931
+ if (message.includes("more than")) {
932
+ return "registry immutable cache has too many candidates";
933
+ }
934
+ if (message.includes("single-link regular file")) {
935
+ return "registry immutable cache entry is invalid";
936
+ }
937
+ return "registry immutable cache document is invalid";
938
+ }
939
+
940
+ function publicRegistryFailure(message, { cacheFallbackAllowed = false } = {}) {
941
+ const error = new Error(message);
942
+ error.publicMessage = message;
943
+ error.cacheFallbackAllowed = cacheFallbackAllowed;
944
+ return error;
945
+ }
946
+
567
947
  /** Best-effort local temp dir helper (kept here so tests can stub fs deps). */
568
948
  export function _tmpRoot() {
569
949
  return os.tmpdir();