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
@@ -6,14 +6,13 @@
6
6
  * already exists under src/gateways + src/harness; this module only composes
7
7
  * it into one entry point:
8
8
  *
9
- * - resolveRemoteControlOptions() flags > env > config > defaults
9
+ * - resolveRemoteControlOptions() explicit authority > safe defaults
10
10
  * - buildDirectPairingUri() LAN pairing descriptor (no relay needed)
11
11
  * - parseDirectPairingUri() inverse, used by clients + tests
12
12
  * - pickLanAddress() best-effort non-internal IPv4
13
13
  * - state file read/write ~/.chainlesschain/remote-control/<port>.json
14
14
  * so `cc remote-control status/stop` can discover a running host process.
15
- * The file is 0600 and CONTAINS THE SERVER TOKEN — same local trust model
16
- * as ~/.chainlesschain/ide/<port>.json (readable only by the OS user).
15
+ * The file is 0600 and deliberately contains no bearer/pairing token.
17
16
  * - renderQrCode() lazy OPTIONAL `qrcode` import; returns
18
17
  * null when the package is not installed (URI printing is the contract,
19
18
  * the QR is progressive enhancement — no hard dependency, trap #6/#27).
@@ -23,19 +22,123 @@ import fs from "fs";
23
22
  import os from "os";
24
23
  import path from "path";
25
24
  import { randomBytes } from "crypto";
25
+ import { isIP } from "net";
26
26
  import { REMOTE_SESSION_SCOPES } from "../harness/remote-session-registry.js";
27
27
  import { extractHost, isPrivateHost } from "./sandbox-network-policy.js";
28
28
 
29
29
  export const REMOTE_CONTROL_DEFAULT_PORT = 18800;
30
+ export const REMOTE_CONTROL_DEFAULT_HOST = "127.0.0.1";
30
31
  export const REMOTE_CONTROL_PAIRING_SCHEME =
31
32
  "chainlesschain://remote-control/pair#";
32
33
  export const REMOTE_CONTROL_DEFAULT_SCOPES = Object.freeze([
33
34
  "observe",
34
35
  "prompt",
35
- "approve",
36
- "interrupt",
37
36
  ]);
38
37
 
38
+ function cleanHost(value) {
39
+ if (value == null) return null;
40
+ const host = String(value).trim();
41
+ return host || null;
42
+ }
43
+
44
+ function normalizeBindHost(value, label) {
45
+ let host = cleanHost(value);
46
+ if (!host) return null;
47
+ if (host.startsWith("[") && host.endsWith("]")) {
48
+ host = host.slice(1, -1);
49
+ }
50
+ host = host.replace(/\.+$/, "").toLowerCase();
51
+ if (host.includes("%")) {
52
+ throw new Error(
53
+ `Invalid ${label} "${host}": scoped IPv6 bind hosts are not supported.`,
54
+ );
55
+ }
56
+ if (host === "localhost" || isIP(host) !== 0) return host;
57
+ throw new Error(
58
+ `Invalid ${label} "${host}": use localhost or an IPv4/IPv6 literal without a port.`,
59
+ );
60
+ }
61
+
62
+ function mappedIpv4FirstOctet(host) {
63
+ const dotted = host.match(/^::ffff:(\d{1,3})\.\d{1,3}\.\d{1,3}\.\d{1,3}$/i);
64
+ if (dotted) return Number(dotted[1]);
65
+ const hex = host.match(/^::ffff:([0-9a-f]{1,4}):[0-9a-f]{1,4}$/i);
66
+ if (!hex) return null;
67
+ return (parseInt(hex[1], 16) >> 8) & 0xff;
68
+ }
69
+
70
+ /** Whether a bind host is restricted to this machine. */
71
+ export function isLoopbackBindHost(value) {
72
+ let host = cleanHost(value)?.toLowerCase();
73
+ if (!host) return false;
74
+ if (host.startsWith("[") && host.endsWith("]")) {
75
+ host = host.slice(1, -1);
76
+ }
77
+ host = host.replace(/\.+$/, "");
78
+ return (
79
+ host === "localhost" ||
80
+ (isIP(host) === 6 && host === "::1") ||
81
+ (isIP(host) === 4 && host.split(".")[0] === "127") ||
82
+ mappedIpv4FirstOctet(host) === 127
83
+ );
84
+ }
85
+
86
+ function isWildcardBindHost(value) {
87
+ let host = cleanHost(value)?.toLowerCase();
88
+ if (host?.startsWith("[") && host.endsWith("]")) {
89
+ host = host.slice(1, -1);
90
+ }
91
+ return host === "0.0.0.0" || host === "::";
92
+ }
93
+
94
+ function isLanBindHost(value) {
95
+ let host = cleanHost(value)?.toLowerCase();
96
+ if (host?.startsWith("[") && host.endsWith("]")) {
97
+ host = host.slice(1, -1);
98
+ }
99
+ return (
100
+ isWildcardBindHost(host) ||
101
+ isLoopbackBindHost(host) ||
102
+ (isIP(host) !== 0 && isPrivateHost(host))
103
+ );
104
+ }
105
+
106
+ function isAdvertisableLanAddress(value) {
107
+ const host = cleanHost(value)?.toLowerCase();
108
+ return (
109
+ isIP(host) !== 0 &&
110
+ isPrivateHost(host) &&
111
+ !isLoopbackBindHost(host) &&
112
+ !isWildcardBindHost(host) &&
113
+ host !== "169.254.169.254"
114
+ );
115
+ }
116
+
117
+ function wsHost(value) {
118
+ const host = cleanHost(value) || REMOTE_CONTROL_DEFAULT_HOST;
119
+ if (host.startsWith("[") && host.endsWith("]")) return host;
120
+ return host.includes(":") ? `[${host}]` : host;
121
+ }
122
+
123
+ /** Resolve the direct endpoint that is safe to advertise to pairing clients. */
124
+ export function resolveRemoteControlWsUrl(
125
+ { host = REMOTE_CONTROL_DEFAULT_HOST, port, allowLan = false } = {},
126
+ { lanAddress = null } = {},
127
+ ) {
128
+ let endpointHost;
129
+ if (allowLan && !isLoopbackBindHost(host)) {
130
+ endpointHost = isWildcardBindHost(host)
131
+ ? isAdvertisableLanAddress(lanAddress)
132
+ ? cleanHost(lanAddress)
133
+ : REMOTE_CONTROL_DEFAULT_HOST
134
+ : cleanHost(host);
135
+ } else if (isLoopbackBindHost(host)) {
136
+ endpointHost = cleanHost(host);
137
+ } else {
138
+ endpointHost = REMOTE_CONTROL_DEFAULT_HOST;
139
+ }
140
+ return `ws://${wsHost(endpointHost)}:${port}`;
141
+ }
39
142
  function b64url(value) {
40
143
  return Buffer.from(value, "utf8").toString("base64url");
41
144
  }
@@ -63,22 +166,70 @@ export function parseScopes(raw) {
63
166
  }
64
167
 
65
168
  /**
66
- * Resolve effective start options. Precedence: explicit flags > environment >
67
- * config (`remoteControl` / legacy `remoteSession` blocks) > defaults. A
169
+ * Resolve effective start options. Remote Control is always command/flag
170
+ * activated by its caller; repository/project configuration is only allowed
171
+ * to disable it. In particular, checked-in config cannot widen the bind host,
172
+ * grant approve/interrupt, select a relay, or install a long-lived token.
173
+ *
174
+ * Direct LAN and privileged scopes are distinct command-line authorities. A
68
175
  * missing token is AUTO-GENERATED (32 hex chars) — the WS server must never
69
- * come up unauthenticated on the unified entry, even on loopback, because the
70
- * pairing URI embeds the token and pairing is the whole point.
176
+ * come up unauthenticated, even on loopback.
71
177
  */
72
178
  export function resolveRemoteControlOptions({
73
179
  flags = {},
74
180
  env = process.env,
75
181
  config = {},
76
182
  } = {}) {
77
- const rc = config.remoteControl || {};
78
- const legacy = config.remoteSession || {};
79
- const port = Number(
80
- flags.port ?? env.CC_REMOTE_CONTROL_PORT ?? rc.port ?? undefined,
81
- );
183
+ const rc =
184
+ config.remoteControl && typeof config.remoteControl === "object"
185
+ ? config.remoteControl
186
+ : {};
187
+ const legacy =
188
+ config.remoteSession && typeof config.remoteSession === "object"
189
+ ? config.remoteSession
190
+ : {};
191
+ if (
192
+ config.remoteControl === false ||
193
+ rc.enabled === false ||
194
+ legacy.remoteControlEnabled === false
195
+ ) {
196
+ throw new Error("Remote Control is disabled by configuration");
197
+ }
198
+
199
+ const warnings = [];
200
+ if (
201
+ Object.prototype.hasOwnProperty.call(rc, "host") ||
202
+ Object.prototype.hasOwnProperty.call(rc, "allowLan")
203
+ ) {
204
+ warnings.push(
205
+ "Ignoring project Remote Control exposure settings; use explicit --host/--allow-lan for this invocation.",
206
+ );
207
+ }
208
+
209
+ const flagHost = normalizeBindHost(flags.host, "--host");
210
+ const allowLan = flags.allowLan === true;
211
+ if (flagHost && !isLoopbackBindHost(flagHost) && !allowLan) {
212
+ throw new Error(
213
+ `Refusing non-loopback --host "${flagHost}" without --allow-lan. ` +
214
+ "Add --allow-lan on a trusted network, or remove --host and configure --relay-url.",
215
+ );
216
+ }
217
+ const host = flagHost || (allowLan ? "0.0.0.0" : REMOTE_CONTROL_DEFAULT_HOST);
218
+ if (allowLan && host === "::") {
219
+ throw new Error(
220
+ 'Refusing IPv6 wildcard host "::": use an explicit private IPv6 address ' +
221
+ "or 0.0.0.0 so the advertised endpoint matches the listener.",
222
+ );
223
+ }
224
+ if (allowLan && !isLanBindHost(host)) {
225
+ throw new Error(
226
+ `Refusing --allow-lan with non-private host "${host}". ` +
227
+ "Use a loopback, wildcard, or private LAN address; use a relay for public networks.",
228
+ );
229
+ }
230
+ const lanAccessible = allowLan && !isLoopbackBindHost(host);
231
+
232
+ const port = Number(flags.port ?? env.CC_REMOTE_CONTROL_PORT ?? undefined);
82
233
  const resolvedPort =
83
234
  Number.isFinite(port) && port >= 1 && port <= 65535
84
235
  ? Math.floor(port)
@@ -86,40 +237,59 @@ export function resolveRemoteControlOptions({
86
237
  const token =
87
238
  flags.token ||
88
239
  env.CC_REMOTE_CONTROL_TOKEN ||
89
- rc.token ||
90
240
  randomBytes(16).toString("hex");
91
- const relayUrl =
92
- flags.relayUrl ||
93
- env.CC_REMOTE_SESSION_RELAY_URL ||
94
- rc.relayUrl ||
95
- legacy.relayUrl ||
96
- null;
241
+ const relayUrl = flags.relayUrl || env.CC_REMOTE_SESSION_RELAY_URL || null;
97
242
  const peerId =
98
243
  flags.peerId ||
99
244
  env.CC_REMOTE_SESSION_PEER_ID ||
100
- rc.peerId ||
101
- legacy.peerId ||
102
245
  (relayUrl
103
246
  ? `cc-host-${os.hostname()}-${randomBytes(4).toString("hex")}`
104
247
  : null);
248
+
249
+ const requestedScopes = parseScopes(flags.scopes ?? null);
250
+ if (requestedScopes.includes("approve") && flags.allowApprove !== true) {
251
+ throw new Error(
252
+ 'Remote Control scope "approve" requires explicit --allow-approve',
253
+ );
254
+ }
255
+ if (requestedScopes.includes("interrupt") && flags.allowInterrupt !== true) {
256
+ throw new Error(
257
+ 'Remote Control scope "interrupt" requires explicit --allow-interrupt',
258
+ );
259
+ }
260
+ const scopes = [...requestedScopes];
261
+ if (flags.allowApprove === true && !scopes.includes("approve")) {
262
+ scopes.push("approve");
263
+ }
264
+ if (flags.allowInterrupt === true && !scopes.includes("interrupt")) {
265
+ scopes.push("interrupt");
266
+ }
267
+
105
268
  return {
106
269
  port: resolvedPort,
107
- host: flags.host || rc.host || "0.0.0.0",
270
+ host,
271
+ allowLan,
272
+ exposesLan: lanAccessible,
273
+ lanAccessible,
274
+ warnings,
108
275
  token,
109
276
  relayUrl,
110
277
  peerId,
111
- scopes: parseScopes(flags.scopes ?? rc.scopes ?? null),
112
- name: flags.name || rc.name || `remote-control @ ${os.hostname()}`,
278
+ scopes,
279
+ name: flags.name || `remote-control @ ${os.hostname()}`,
113
280
  };
114
281
  }
115
-
116
282
  /** First non-internal IPv4 address, or null. Pure over an interfaces map. */
117
283
  export function pickLanAddress(interfaces = os.networkInterfaces()) {
118
284
  for (const entries of Object.values(interfaces || {})) {
119
285
  for (const entry of entries || []) {
120
286
  const family =
121
287
  entry.family === "IPv4" || entry.family === 4 ? "IPv4" : entry.family;
122
- if (family === "IPv4" && !entry.internal && entry.address) {
288
+ if (
289
+ family === "IPv4" &&
290
+ !entry.internal &&
291
+ isAdvertisableLanAddress(entry.address)
292
+ ) {
123
293
  return entry.address;
124
294
  }
125
295
  }
@@ -226,14 +396,22 @@ function stateFilePath(dir, port) {
226
396
  }
227
397
 
228
398
  /**
229
- * Persist a running host's discovery record (0600 file, 0700 dir). Contains
230
- * the server token local-user trust domain only, mirroring the IDE bridge.
399
+ * Persist a running host's non-secret discovery record (0600 file, 0700 dir).
400
+ * Pairing/server tokens are intentionally rejected so status, crash recovery,
401
+ * diagnostics, and uploaded state artifacts cannot disclose them.
231
402
  */
232
403
  export function writeRemoteControlState(state, { dir } = {}) {
233
404
  const stateDir = dir || remoteControlStateDir();
234
405
  fs.mkdirSync(stateDir, { recursive: true, mode: 0o700 });
235
406
  const file = stateFilePath(stateDir, state.port);
236
- fs.writeFileSync(file, JSON.stringify(state, null, 2), {
407
+ const {
408
+ token: _token,
409
+ serverToken: _serverToken,
410
+ pairingToken: _pairingToken,
411
+ pairingUri: _pairingUri,
412
+ ...safeState
413
+ } = state;
414
+ fs.writeFileSync(file, JSON.stringify(safeState, null, 2), {
237
415
  encoding: "utf-8",
238
416
  mode: 0o600,
239
417
  });
@@ -279,8 +457,16 @@ export function readRemoteControlStates({ dir } = {}) {
279
457
  const file = path.join(stateDir, name);
280
458
  try {
281
459
  const state = JSON.parse(fs.readFileSync(file, "utf-8"));
460
+ const legacyExposure = state.exposure == null;
461
+ const exposure = legacyExposure
462
+ ? state.host && !isLoopbackBindHost(state.host)
463
+ ? "legacy-unknown-lan"
464
+ : "legacy-unknown"
465
+ : state.exposure;
282
466
  states.push({
283
467
  ...state,
468
+ exposure,
469
+ legacyExposure,
284
470
  stateFile: file,
285
471
  alive: isPidAlive(state.pid),
286
472
  });