chainlesschain 0.162.157 → 0.162.158

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 (173) 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-CfMP2Oud.js → AIOps-BwpTJTN_.js} +1 -1
  5. package/src/assets/web-panel/assets/{ActionButton-Zu0N8BQL.js → ActionButton-BtQOoo3l.js} +1 -1
  6. package/src/assets/web-panel/assets/{Analytics-DZTwj5T_.js → Analytics-Duf_rXZa.js} +3 -3
  7. package/src/assets/web-panel/assets/{AppLayout-CVFJh47v.js → AppLayout-XQK3wjvB.js} +5 -5
  8. package/src/assets/web-panel/assets/Artifacts-CTVt96zW.css +1 -0
  9. package/src/assets/web-panel/assets/Artifacts-DQvJoQ6E.js +1 -0
  10. package/src/assets/web-panel/assets/{Audit-BMfiLj7T.js → Audit-DNjOGQLh.js} +1 -1
  11. package/src/assets/web-panel/assets/{BackgroundAgents-BslPCdGF.js → BackgroundAgents-FH9RAfB5.js} +1 -1
  12. package/src/assets/web-panel/assets/{Backup-CQ_fMC1r.js → Backup-ZIbrR4-U.js} +1 -1
  13. package/src/assets/web-panel/assets/{BaseInput-CsFsARiC.js → BaseInput-DSt3NaND.js} +1 -1
  14. package/src/assets/web-panel/assets/{Chat-CwfBobco.js → Chat-CY-wvbjv.js} +6 -6
  15. package/src/assets/web-panel/assets/Chat-KSGPtU0L.css +1 -0
  16. package/src/assets/web-panel/assets/{ChatBubbleRenderer-K6Nf3xeN.js → ChatBubbleRenderer-BAV9V-t-.js} +1 -1
  17. package/src/assets/web-panel/assets/{Checkbox-DkSyjjPp.js → Checkbox-8JVadE_B.js} +1 -1
  18. package/src/assets/web-panel/assets/{Codegen-B7Fx3zZ6.js → Codegen-CN83nPYI.js} +1 -1
  19. package/src/assets/web-panel/assets/{Col-Dzfqn8Ep.js → Col-xPSvqdN8.js} +1 -1
  20. package/src/assets/web-panel/assets/{Community-DhHPdzMM.js → Community-D78cnIjG.js} +1 -1
  21. package/src/assets/web-panel/assets/{Compact-Ct18_8y7.js → Compact-CSw6Csbp.js} +1 -1
  22. package/src/assets/web-panel/assets/{Compliance-CY64IqBi.js → Compliance-E_HhjcYR.js} +1 -1
  23. package/src/assets/web-panel/assets/{Cowork-BYSmWlUl.js → Cowork-DybeQjmm.js} +3 -3
  24. package/src/assets/web-panel/assets/{Cron-_4xtP3Ug.js → Cron-DS0BJeLY.js} +2 -2
  25. package/src/assets/web-panel/assets/{Crosschain-B4qY-Ycr.js → Crosschain-BOwVgkbs.js} +1 -1
  26. package/src/assets/web-panel/assets/{DID-CkCzWkx1.js → DID-DRUr9YuX.js} +2 -2
  27. package/src/assets/web-panel/assets/{Dashboard-C1scT7Ke.js → Dashboard-Cht-yUKW.js} +2 -2
  28. package/src/assets/web-panel/assets/{Dropdown-DBnQ2EWd.js → Dropdown-BLr_EJ9X.js} +1 -1
  29. package/src/assets/web-panel/assets/{EmailListRenderer-C9JhOTq0.js → EmailListRenderer-Drexuh5W.js} +1 -1
  30. package/src/assets/web-panel/assets/{FamilyGuardDashboard-CWXv5RUv.js → FamilyGuardDashboard-D9Q2Kq63.js} +1 -1
  31. package/src/assets/web-panel/assets/{Federation-fYdcbz9W.js → Federation-C-Z1xI-T.js} +1 -1
  32. package/src/assets/web-panel/assets/{FormItemContext-CJqOT3Yj.js → FormItemContext-DLHPMkQF.js} +1 -1
  33. package/src/assets/web-panel/assets/{GenericCardRenderer-DGCsHc5K.js → GenericCardRenderer-DxxEdn2r.js} +1 -1
  34. package/src/assets/web-panel/assets/{Git-EH2K-H0Q.js → Git-syATcaBt.js} +2 -2
  35. package/src/assets/web-panel/assets/{Governance-DJn1X2pR.js → Governance-CYe_r8Zf.js} +1 -1
  36. package/src/assets/web-panel/assets/{Inference-Dch1E8qd.js → Inference-GWxErB7k.js} +1 -1
  37. package/src/assets/web-panel/assets/{KnowledgeGraph-QIur8j8-.js → KnowledgeGraph-qQvVCZoi.js} +1 -1
  38. package/src/assets/web-panel/assets/{Logs-DUgHldPg.js → Logs-BenAU2W8.js} +2 -2
  39. package/src/assets/web-panel/assets/MarkdownRenderer-7agE3Or1.js +1 -0
  40. package/src/assets/web-panel/assets/MarkdownRenderer-DbzhFwvz.css +1 -0
  41. package/src/assets/web-panel/assets/{Marketplace-DafrSfvd.js → Marketplace-D4Jn5fpD.js} +1 -1
  42. package/src/assets/web-panel/assets/{McpTools-D2xRbfJx.js → McpTools-DtAwoqiQ.js} +4 -4
  43. package/src/assets/web-panel/assets/{Memory-C6RbVHAo.js → Memory-BI_O50I8.js} +2 -2
  44. package/src/assets/web-panel/assets/{MobileBridge-BPtVBKqn.js → MobileBridge-CQWE2lqx.js} +1 -1
  45. package/src/assets/web-panel/assets/{MobileProjects-Cuw832cr.js → MobileProjects-CVtTqgtg.js} +1 -1
  46. package/src/assets/web-panel/assets/{Mtc-D6O9fWFa.js → Mtc-Dnr16qsn.js} +4 -4
  47. package/src/assets/web-panel/assets/{MtcAudit-CWZcXIH1.js → MtcAudit-CQ5Xe4-a.js} +4 -4
  48. package/src/assets/web-panel/assets/{Multisig-DhIBxTFd.js → Multisig-Bjr8O-wy.js} +3 -3
  49. package/src/assets/web-panel/assets/{NLProgramming-wkBFS33e.js → NLProgramming-CEe-KirU.js} +1 -1
  50. package/src/assets/web-panel/assets/{Notes-B09FxncA.js → Notes-spKJIKF9.js} +4 -4
  51. package/src/assets/web-panel/assets/{NotificationSettings-oQwtH6-9.js → NotificationSettings-DRp83LFG.js} +1 -1
  52. package/src/assets/web-panel/assets/OrderTableRenderer-h1A0XB2a.js +1 -0
  53. package/src/assets/web-panel/assets/{Organization-CsF-NIjz.js → Organization-BlgB0IYg.js} +4 -4
  54. package/src/assets/web-panel/assets/{Overflow-bLxv9Dqi.js → Overflow-a_35HqZI.js} +1 -1
  55. package/src/assets/web-panel/assets/{P2P-DVwQtm4q.js → P2P-CSNHCNPC.js} +2 -2
  56. package/src/assets/web-panel/assets/{PdhVaultBrowser-Bgic3QKm.js → PdhVaultBrowser-C8M1JH-1.js} +5 -5
  57. package/src/assets/web-panel/assets/{Permissions-as80-9ep.js → Permissions-DmjJi9Ms.js} +3 -3
  58. package/src/assets/web-panel/assets/{PersonalDataHub-BGmUw36a.js → PersonalDataHub-DfuwRyf4.js} +2 -2
  59. package/src/assets/web-panel/assets/{Pipeline-BM96zBxG.js → Pipeline-B2-S4ekE.js} +1 -1
  60. package/src/assets/web-panel/assets/{Privacy-BBOzBSWe.js → Privacy-B1dOiBLC.js} +1 -1
  61. package/src/assets/web-panel/assets/{ProjectInit-GHX41V28.js → ProjectInit-_pNmc-DX.js} +2 -2
  62. package/src/assets/web-panel/assets/{ProjectSettings-DraqzxKD.js → ProjectSettings-DmwG2Ii1.js} +2 -2
  63. package/src/assets/web-panel/assets/{Projects-BzzdUml4.js → Projects-COATmHxF.js} +1 -1
  64. package/src/assets/web-panel/assets/{Providers-CSeYNAtW.js → Providers-C6HSjg1d.js} +1 -1
  65. package/src/assets/web-panel/assets/{QrScannerModal-DlzBCdXh.js → QrScannerModal-CrDmS7l1.js} +1 -1
  66. package/src/assets/web-panel/assets/{QuickAsk-p8iB7od_.js → QuickAsk-CfKvWt-x.js} +1 -1
  67. package/src/assets/web-panel/assets/{Recommend-DjOVXacd.js → Recommend-DMa6Kuzv.js} +1 -1
  68. package/src/assets/web-panel/assets/RemoteSession-DrktUo5x.js +4 -0
  69. package/src/assets/web-panel/assets/{Reputation-CjT3M0Di.js → Reputation-BTPQ1vjL.js} +1 -1
  70. package/src/assets/web-panel/assets/{Row-tw8m6T0c.js → Row-B6Q7QkMG.js} +1 -1
  71. package/src/assets/web-panel/assets/{RssFeed-B47KBx_f.js → RssFeed-B1IJhAmX.js} +3 -3
  72. package/src/assets/web-panel/assets/{Search-D2sx1PBe.js → Search-xbea1kGD.js} +1 -1
  73. package/src/assets/web-panel/assets/{Security-JmtL8g7D.js → Security-7rs0cO3F.js} +4 -4
  74. package/src/assets/web-panel/assets/{Services-BxrNqjin.js → Services-0fjixK0S.js} +2 -2
  75. package/src/assets/web-panel/assets/{Skeleton-udk8L18t.js → Skeleton-B_rVq9Cu.js} +1 -1
  76. package/src/assets/web-panel/assets/{Skills-DooLT9of.js → Skills-C5uM90b9.js} +1 -1
  77. package/src/assets/web-panel/assets/{Sla-ITVZE1xI.js → Sla-U4pYCIZH.js} +1 -1
  78. package/src/assets/web-panel/assets/{SpeechSettings-DCkfqY1n.js → SpeechSettings-Cdz4yTWx.js} +1 -1
  79. package/src/assets/web-panel/assets/{SyncSettings-CkNlgFjM.js → SyncSettings-Me3aetHv.js} +2 -2
  80. package/src/assets/web-panel/assets/{Tasks-BMAoyCM_.js → Tasks-Cf6aNkL_.js} +1 -1
  81. package/src/assets/web-panel/assets/{Templates-BnrFTlrz.js → Templates-CYatk4tU.js} +1 -1
  82. package/src/assets/web-panel/assets/{Tenant-BjNsy3-i.js → Tenant-DIMKLw7o.js} +1 -1
  83. package/src/assets/web-panel/assets/{Terminal-C0gxLL7V.js → Terminal-C_cp-uSJ.js} +2 -2
  84. package/src/assets/web-panel/assets/{TimelineRenderer-CUtj_KHA.js → TimelineRenderer-cUPVNADp.js} +1 -1
  85. package/src/assets/web-panel/assets/{Tokens-DOg61zlq.js → Tokens-CiaEIXVf.js} +1 -1
  86. package/src/assets/web-panel/assets/{Trigger-D4ldCewj.js → Trigger-DB1WU5lQ.js} +1 -1
  87. package/src/assets/web-panel/assets/{Trust-3lk_Zd8j.js → Trust-Cfht9Ijy.js} +1 -1
  88. package/src/assets/web-panel/assets/{UkeySign-kWhFR6Li.js → UkeySign-K3P4Vt9S.js} +1 -1
  89. package/src/assets/web-panel/assets/{VideoEditing-D3GOFVWg.js → VideoEditing-DWjhjXRh.js} +1 -1
  90. package/src/assets/web-panel/assets/{Wallet-DzrZfBuh.js → Wallet-D9-Q1Kan.js} +4 -4
  91. package/src/assets/web-panel/assets/{WebAuthn-CyVJVp_g.js → WebAuthn-BDszy-oV.js} +5 -5
  92. package/src/assets/web-panel/assets/{WorkflowEditor-i_b0wi9b.js → WorkflowEditor-DIhij4rA.js} +1 -1
  93. package/src/assets/web-panel/assets/{chat-DwPCK8I7.js → chat-D7VCiKbh.js} +1 -1
  94. package/src/assets/web-panel/assets/{colors-D0vaVqZU.js → colors-BvOd9pVn.js} +1 -1
  95. package/src/assets/web-panel/assets/{compact-item-Don7nGW2.js → compact-item-DoqXBiAO.js} +1 -1
  96. package/src/assets/web-panel/assets/{createContext-D84R5qy2.js → createContext-CrKRzUtp.js} +1 -1
  97. package/src/assets/web-panel/assets/devWarning-nDB--2xr.js +1 -0
  98. package/src/assets/web-panel/assets/{hasIn-BeUqVVKU.js → hasIn-BWAev4WC.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-BohrJpBf.js → index-1poMpMU0.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-YKLCotoo.js → index-8g5TJCZ3.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-D8r7WKk-.js → index-B0vGEbJT.js} +1 -1
  102. package/src/assets/web-panel/assets/index-B1_pRTtB.js +1 -0
  103. package/src/assets/web-panel/assets/{index-m9bn0F1p.js → index-B30NvR21.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-7QUwcGTg.js → index-BCyP071m.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-4MkfPLGv.js → index-BHOcVYrx.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-BZ1N07Sp.js → index-BN9m4Ux2.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-DFFzwrNs.js → index-BWWsaimP.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-LMo9bBLJ.js → index-BXyl8O23.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-BlfTf0ka.js → index-BZGk8AbZ.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-CnnxzIx0.js → index-Bf9YlbDn.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-CAdCTTBC.js → index-Bj7EgmOp.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-B57al0Z5.js → index-BkgnGX7v.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-CzT56x-_.js → index-BoYrS_L2.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-L73NrmlW.js → index-Bq_ctRpz.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-CL71rIwo.js → index-C5kRmj3P.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-WRw1pM5E.js → index-CDGLdn12.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-hjgrIX5I.js → index-CEPHMSih.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-IGGttsCv.js → index-CEXfKSiI.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-D84BnuFg.js → index-CSb-tnIo.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-x7VYlV41.js → index-CmJ6xKBO.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-DQor_7NF.js → index-CoD31Ndm.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-CfYRv-zA.js → index-Cx7M7PSF.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-DBzBLX-K.js → index-D23L1V76.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-DMT8n9vB.js → index-DV-Rfmfx.js} +3 -3
  125. package/src/assets/web-panel/assets/{index-BER98o4P.js → index-DlGu7As6.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-CrnVEEQO.js → index-DvDOpsJB.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-D4b5XqIy.js → index-HwHdTusY.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-Br3SOdER.js → index-ISvPZ05r.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-MKZrJrP_.js → index-JzUmJMvn.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-DT40zd7K.js → index-KR1-Em9C.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-C0_sKVi7.js → index-PDxGjKRu.js} +1 -1
  132. package/src/assets/web-panel/assets/index-TMM9FuW-.js +1 -0
  133. package/src/assets/web-panel/assets/{index-wesQQ2cH.js → index-YGDy7nNq.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-DA0-EJKx.js → index-gmxqu5Kk.js} +1 -1
  135. package/src/assets/web-panel/assets/{index-BNpJf-sf.js → index-oY45XrkG.js} +1 -1
  136. package/src/assets/web-panel/assets/{index-CExWtm98.js → index-ww1QtLnX.js} +1 -1
  137. package/src/assets/web-panel/assets/{index-De85D9Pc.js → index-zH-tuuGZ.js} +1 -1
  138. package/src/assets/web-panel/assets/{initDefaultProps-DGeU4vPS.js → initDefaultProps-XW-OR3mL.js} +1 -1
  139. package/src/assets/web-panel/assets/{motion-Dgqj9ab3.js → motion-CeuEHh20.js} +1 -1
  140. package/src/assets/web-panel/assets/{move-BFyEU6Lr.js → move-Dva79Keg.js} +1 -1
  141. package/src/assets/web-panel/assets/{mtc-parser-CueCXXPC.js → mtc-parser-C0B6bD2j.js} +1 -1
  142. package/src/assets/web-panel/assets/{omit-D2pU5OyE.js → omit-AkZvO-a2.js} +1 -1
  143. package/src/assets/web-panel/assets/{pickAttrs-CKTf6zD6.js → pickAttrs-B4r55qGc.js} +1 -1
  144. package/src/assets/web-panel/assets/{placementArrow-DieJaGCN.js → placementArrow-BLH-z7YE.js} +1 -1
  145. package/src/assets/web-panel/assets/{responsiveObserve-Bxr-rmU6.js → responsiveObserve-ksEGZ2Wf.js} +1 -1
  146. package/src/assets/web-panel/assets/{slide-CDEDk3Se.js → slide-Cth4OD1Z.js} +1 -1
  147. package/src/assets/web-panel/assets/{statusUtils-icf2eCTx.js → statusUtils-9S83ezxw.js} +1 -1
  148. package/src/assets/web-panel/assets/{styleChecker-qKvgmBhE.js → styleChecker-7yQmQGZ4.js} +1 -1
  149. package/src/assets/web-panel/assets/{useFlexGapSupport-1_UhiyJZ.js → useFlexGapSupport-DpGnMmPc.js} +1 -1
  150. package/src/assets/web-panel/assets/{useFs-CFGLXDWh.js → useFs-C4zDZIXH.js} +1 -1
  151. package/src/assets/web-panel/assets/{usePersonalDataHub-UafOXvQ6.js → usePersonalDataHub-CoLICdUZ.js} +1 -1
  152. package/src/assets/web-panel/assets/{vnode-BfPqSx79.js → vnode-CKyH-35X.js} +1 -1
  153. package/src/assets/web-panel/assets/{zoom-CXsVcRB6.js → zoom-DRDGit17.js} +1 -1
  154. package/src/assets/web-panel/index.html +1 -1
  155. package/src/commands/agent.js +4 -1
  156. package/src/data/changelog.json +7 -2
  157. package/src/lib/chrome-connector.js +39 -4
  158. package/src/lib/remote-control.js +35 -0
  159. package/src/lib/web-ui-server.js +120 -7
  160. package/src/repl/agent-repl.js +231 -58
  161. package/src/repl/remote-approval.js +111 -0
  162. package/src/runtime/agent-core.js +36 -0
  163. package/src/runtime/coding-agent-contract-shared.cjs +53 -0
  164. package/src/runtime/coding-agent-policy.cjs +16 -0
  165. package/src/runtime/policies/agent-policy.js +3 -0
  166. package/src/assets/web-panel/assets/Artifacts-BbN5V7au.js +0 -1
  167. package/src/assets/web-panel/assets/Artifacts-CRkepl4t.css +0 -1
  168. package/src/assets/web-panel/assets/Chat-DDh58PCf.css +0 -1
  169. package/src/assets/web-panel/assets/OrderTableRenderer-iFKeNf6Y.js +0 -1
  170. package/src/assets/web-panel/assets/RemoteSession-CIGk7gk-.js +0 -4
  171. package/src/assets/web-panel/assets/devWarning-BBwWehQ7.js +0 -1
  172. package/src/assets/web-panel/assets/index-DJ8etkBT.js +0 -1
  173. package/src/assets/web-panel/assets/index-OeaK-zXZ.js +0 -1
@@ -3,7 +3,7 @@
3
3
  "releases": [
4
4
  {
5
5
  "productVersion": "Unreleased",
6
- "cliVersion": "0.162.157",
6
+ "cliVersion": "0.162.158",
7
7
  "cliVersions": [
8
8
  "0.4.46",
9
9
  "0.4.51",
@@ -15,11 +15,16 @@
15
15
  "0.162.154",
16
16
  "0.162.155",
17
17
  "0.162.156",
18
- "0.162.157"
18
+ "0.162.157",
19
+ "0.162.158"
19
20
  ],
20
21
  "date": null,
21
22
  "title": "",
22
23
  "sections": [
24
+ {
25
+ "heading": "Added — cc CLI 0.162.158:browser_state agent 工具 + REPL /remote-control + web-panel 直连配对/审批卡片 + 交付物预览下载(CLI-only npm 发版)",
26
+ "body": "> `chainlesschain` 0.162.157 → **0.162.158** 发 npm `latest`(经 `npm-publish.yml`,`--provenance --access public`)。纯 `packages/cli/src` + web-panel dist 随包;未触 `pdh/lib` → 无 Android cc bundle rollover / 无 USR_VERSION 改动。命令数不变(**173**);AGENT_TOOLS **24 → 25**(新增 `browser_state`)。\n\n- **`browser_state` 一等 agent 工具(IDE gap P1 #8 收口)**:Chrome CDP 连接器(`cc browse chrome`)暴露为第 25 个 agent 工具——URL/标题/标签页 + 观察窗口 console/失败网络请求 + DOM 快照(agent 面 40k cap,CLI 命令面 150k)+ 可选截图(**生成的**临时路径,防 agent 自选路径经读工具覆盖任意文件);LOW/READ/plan 模式可用,纯观察不点击不输入不导航,仅 loopback CDP。\n- **REPL `/remote-control`(别名 `/rc`)on|off|status(批 26)**:交互式会话内起自托管 WS + RemoteApprovalBridge 配对(URI + 可选二维码),TAB 补全 + /help;`cc agent --remote-control` 对交互式会话同样生效(启动失败降级 local-only 不中断会话)。**交互式远程审批赛跑语义**(新 `src/repl/remote-approval.js`):两个交互 confirmer(ApprovalGate + settings/hook ask)把终端 prompt 与已配对设备赛跑,先答先赢;与 headless 刻意不同——远端超时/断开**不是决定**(静默的手机永不代键盘用户 auto-deny),设备先答则取消本地 readline 并压掉其搁浅的 idle-timeout。\n- **web-panel 直连(direct-LAN)配对 + `permission.request` 审批卡片(批 28)**:remoteSession store 自动识别双 URI 方案——relay E2EE 路径不变,新增 `chainlesschain://remote-control/pair#` 直连 transport(browser 版 parseDirectPairingUri + 客户端过期检查);直连控制走 remote-session-publish **顶层 commandId+seq**(applyControlIdempotent direct-client 契约);一次性 pairing token → 直连断开**刻意不自动重连**(重 join 必失败,诚实 disconnected 态 + 重配对提示);两种 transport 喂同一 pendingApprovals 卡片(requestId 去重 + permission.resolved 任意裁决方清除 + 首等公民审批卡列表批准/拒绝)。\n- **交付物 Markdown 预览 + 浏览器下载(批 25)**:面板 text/markdown 预览走既有 MarkdownRenderer(marked+DOMPurify+hljs,零新依赖);新 `GET /api/artifacts/<id>/download` 流式下载完整存档副本(WS 预览仍 cap)——Content-Disposition(RFC 5987)+ no-store/nosniff + 配置 wsToken 时同 WS 强度 token 门(sha256+timingSafeEqual,Bearer 头不进 URL)+ basename 防篡改守卫。\n- **安全修复 ×2**:① **直连配对信任边界**——直连(LAN)配对无密码学主机身份(区别 relay E2EE),钓鱼配对 URI 可把面板指向任意公网 ws:// 端点导走 serverToken/prompt/审批决定;两端 parser(web-panel + CLI 孪生)现拒绝指向非私网主机的明文 ws://(仅 loopback/RFC-1918/link-local/ULA,即 pickLanAddress 可能产出的全集;wss:// 不限),IPv6 括号 + IPv4-mapped 点分/hex 双形态识别,RED 反证过。② **chrome-connector launch-arg 加固**——launch URL 曾逐字进 Chrome argv(`--` 开头值被 Chrome 当开关解析=潜在命令执行面),现限 http(s)://、about:(URL parse 校验字节原样透传)+ CDP 端口处处整型边界校验。\n- **健壮性**:web-panel 审批卡发送失败恢复——approve() 乐观清除后若答复未离开本端(直连 publish 拒绝/socket 断/relay 未配对)恢复卡片并浮错,可重新裁决;失败窗口内别设备已 resolved 则抑制恢复(无僵尸卡)。RED 反证 + web-panel 全套 2512 绿。\n- **测试稳健性(仓库面,不进 npm 包)**:checkpoint-store `vi.setConfig 30s` + web-panel doctor 90s 两处负载敏感预算加固(trap #31 形态);ai-doc-creator 集成测试改 chdir tmpDir,不再向仓库根泄漏 12 个产物文件。"
27
+ },
23
28
  {
24
29
  "heading": "Added — cc CLI 0.162.157:diff 审阅修订摘要 + plan 审阅快照 + Chrome 连接器 + Artifacts + Windows/PowerShell 一等 + --bare/无斜杠/读屏模式(CLI-only npm 发版)",
25
30
  "body": "> `chainlesschain` 0.162.156 → **0.162.157** 发 npm `latest`(经 `npm-publish.yml`,`--provenance --access public`)。纯 `packages/cli/src` + 依赖指针(`@chainlesschain/personal-data-hub` 0.4.50→**0.4.51**,交易短信→金额事件,已先行在 npm);命令数 **172 → 173**(新增顶层 `cc artifacts`,13 文档面已扫);无 Android cc bundle rollover / 无 USR_VERSION 改动。agent-sdk 0.1.1(plan review payload additive 类型)此前已在 npm。\n\n- **Artifacts v1:`publish_artifact` agent 工具 + `cc artifacts` 命令**:agent 完成的交付物(报告/图表/构建产物)发布到 `~/.chainlesschain/artifacts`(index.jsonl + 按 id 存文件,title/kind/mime/sha256/session/TTL 元数据,100MB 守卫)——**只有元数据进对话/transcript**,大产物不再撑爆上下文;`cc artifacts list|show|open|remove|clean`(别名 `artifact`)管理与打开;工具 LOW 风险、plan 模式禁用(AGENT_TOOLS 23→24)。\n- **Windows/PowerShell 一等公民(批 22)**:run_shell 新增可选 `shell` 参数(`default|cmd|powershell|pwsh`),选择链 per-call > settings `shell.windowsDefault`(win32-only 分层,`CC_WINDOWS_SHELL` env 胜出)> 平台默认(未配置路径字节不变);PowerShell 经**显式 argv** 运行(`-NoProfile [-ExecutionPolicy <enum 校验>] -Command`,防 argv 走私);权限规则新增 `PowerShell(...)` 伞形族与 `shell:pwsh` 参数域;settings hooks 每 hook 可声明 `shell: powershell|pwsh`。结果携带 `shell`/`shell_note`。\n\n- **IDE diff 审阅:agent 能看到你改了什么(gap #4 收口的 CLI 面)**:在 IDE diff 里编辑 proposed content 后 Accept,工具结果此前只带 `userEdited: true` 布尔——agent 不知道你具体改了什么,其对文件的心智模型仍是它自己的原提案。现在新增 `userAmendments`:新纯核 `summarizeUserAmendments`(`lib/ide-context.js`)产出提案→实际写入的 -/+ 行级摘要,按最终文件 1-based 行锚定分块(复用 note-versioning 的 simpleDiff LCS;纯插入/删除直排防幻影空行;1M cell 守卫降级粗块;行数/字符双 cap)。配合 JetBrains 0.4.55(可编辑右栏回读)/ VS Code 0.37.x(原生支持)形成端到端闭环。\n- **plan 审阅快照写入会话(IDE plan review 的 CLI 协议面)**:`{type:\"plan\", action:\"approve\"|\"reject\"}` 控制消息现可携带 `review` 载荷(IDE 端 Markdown 审阅文档的快照 + 元数据,24k cap),CLI 把它写进会话 transcript——计划批准/拒绝留档可审计可回放(headless-stream.js;agent-sdk 0.1.1 `PlanControlInput.review` 类型同步)。\n- **`--bare`(`CC_BARE`)裸模式**:`--safe-mode` 全开关之上再关 skills(`CC_SKILLS=0`)、plugins(`CC_PLUGINS=0`)与 MCP/IDE/PDH/JetBrains 自动连接——两个新 env 各在单一咽喉点生效(skill-loader `loadAll` / plugin-runtime `discoverPlugins`);显式 `--mcp-config/--ide/--pdh/--jetbrains` 仍胜出;权限规则不放松。最小攻击面/最快启动的干净 agent 环境。\n- **`--disable-slash-commands`(`CC_DISABLE_SLASH_COMMANDS`)**:REPL 里 `/` 开头输入原样送模型(纯谓词 + NUL 哨兵,`lib/slash-dispatch`);`/exit`、`/quit` 保留;TAB 补全不再提示斜杠命令;宏与 MCP prompt 展开一并旁路。粘贴含 `/` 行首文本不再误触发。\n- **`--ax-screen-reader`(`CC_SCREEN_READER`)读屏友好模式**:mono 主题(零 ANSI 色)+ 状态栏关闭(新 `lib/accessibility.js`)——读屏器不再逐字念转义序列与高频刷新的状态行。\n- **`cc browse chrome status|launch|state`**:与 `browse fetch/scrape`(每次冷启空白 headless 浏览器)不同,连接器经 CDP attach 到**用户自己的 Chrome**,页面状态带着登录会话。`launch` 启动可调试 Chrome(默认**专用配置目录** `~/.chainlesschain/chrome-profile`——登录一次持久复用;`--default-profile` 可选复用真实配置但要求所有 Chrome 窗口先关闭);`status` 探测 CDP 端口;`state` attach 后观察窗口期内的 console 消息与失败/4xx-5xx 网络请求(CDP 无追溯历史,`--reload` 可捕获加载期输出),快照 URL/标题/DOM(默认 150k cap)/可选截图,断开不杀浏览器。安全注记:CDP 端口是本机无鉴权控制通道,仅绑 loopback,用完关闭该 Chrome。查找 Chrome/Edge 可执行文件跨三平台(`CHROME_PATH` 优先)。attach 后 page targets 有挂载延迟——轮询 3s 而非误报\"无标签页\"。6 单测(注入 deps)+ 真 Chrome 145 live e2e(discover→attach→console/network 捕获→DOM→截图→干净断开)。两 IDE 插件入口(VS `ChainlessChain: Chrome Connector` / JB Tools 同名)已随 VS Code 0.37.10 / JetBrains 0.4.53 上市场——本版补上其依赖的 CLI 端命令。"
@@ -33,6 +33,38 @@ import path from "path";
33
33
  import http from "http";
34
34
 
35
35
  export const DEFAULT_CDP_PORT = 9222;
36
+
37
+ /** Coerce + bound-check a CDP port (goes into URLs and Chrome argv). */
38
+ export function normalizeCdpPort(port) {
39
+ const n = Number(port ?? DEFAULT_CDP_PORT);
40
+ if (!Number.isInteger(n) || n < 1 || n > 65535) {
41
+ throw new Error(`Invalid CDP port: ${port}`);
42
+ }
43
+ return n;
44
+ }
45
+
46
+ /**
47
+ * A launch URL must be a real web page. Anything else — and especially a
48
+ * value starting with `--`, which Chrome would parse as a SWITCH (e.g.
49
+ * --renderer-cmd-prefix=… is command execution) — is refused. Today `url`
50
+ * only comes from the local `cc browse chrome launch --url` flag, but this
51
+ * keeps the argv safe if it ever becomes agent- or remote-driven.
52
+ */
53
+ export function normalizeLaunchUrl(url) {
54
+ if (url == null || url === "") return null;
55
+ let parsed;
56
+ try {
57
+ parsed = new URL(String(url));
58
+ } catch {
59
+ throw new Error(`Invalid launch URL: ${url}`);
60
+ }
61
+ if (!["http:", "https:", "about:"].includes(parsed.protocol)) {
62
+ throw new Error(
63
+ `Launch URL must be http(s):// or about: (got ${parsed.protocol})`,
64
+ );
65
+ }
66
+ return String(url); // validated — pass through byte-identical
67
+ }
36
68
  export const DEFAULT_DOM_CAP = 150000;
37
69
 
38
70
  const _deps = {
@@ -65,7 +97,9 @@ export async function discoverCdp({
65
97
  port = DEFAULT_CDP_PORT,
66
98
  deps = _deps,
67
99
  } = {}) {
68
- const res = await deps.httpGet(`http://127.0.0.1:${port}/json/version`);
100
+ const res = await deps.httpGet(
101
+ `http://127.0.0.1:${normalizeCdpPort(port)}/json/version`,
102
+ );
69
103
  if (res.status !== 200) return { ok: false, port };
70
104
  try {
71
105
  const info = JSON.parse(res.body);
@@ -150,7 +184,7 @@ export function buildChromeLaunchArgs({
150
184
  deps = _deps,
151
185
  } = {}) {
152
186
  const args = [
153
- `--remote-debugging-port=${port}`,
187
+ `--remote-debugging-port=${normalizeCdpPort(port)}`,
154
188
  // No first-run wizards in the dedicated profile.
155
189
  "--no-first-run",
156
190
  "--no-default-browser-check",
@@ -158,7 +192,8 @@ export function buildChromeLaunchArgs({
158
192
  if (!defaultProfile) {
159
193
  args.push(`--user-data-dir=${profileDir || connectorProfileDir({ deps })}`);
160
194
  }
161
- if (url) args.push(String(url));
195
+ const launchUrl = normalizeLaunchUrl(url);
196
+ if (launchUrl) args.push(launchUrl);
162
197
  return args;
163
198
  }
164
199
 
@@ -216,7 +251,7 @@ export async function captureState({
216
251
  let browser;
217
252
  try {
218
253
  browser = await playwright.chromium.connectOverCDP(
219
- `http://127.0.0.1:${port}`,
254
+ `http://127.0.0.1:${normalizeCdpPort(port)}`,
220
255
  );
221
256
  } catch (err) {
222
257
  return {
@@ -24,6 +24,7 @@ import os from "os";
24
24
  import path from "path";
25
25
  import { randomBytes } from "crypto";
26
26
  import { REMOTE_SESSION_SCOPES } from "../harness/remote-session-registry.js";
27
+ import { extractHost, isPrivateHost } from "./sandbox-network-policy.js";
27
28
 
28
29
  export const REMOTE_CONTROL_DEFAULT_PORT = 18800;
29
30
  export const REMOTE_CONTROL_PAIRING_SCHEME =
@@ -159,6 +160,39 @@ export function buildDirectPairingUri({
159
160
  return REMOTE_CONTROL_PAIRING_SCHEME + b64url(JSON.stringify(payload));
160
161
  }
161
162
 
163
+ /**
164
+ * Direct pairing carries NO cryptographic host identity (unlike relay E2EE),
165
+ * so the endpoint itself is the trust boundary: plaintext `ws://` is only
166
+ * acceptable to loopback/RFC-1918/link-local hosts (the only thing
167
+ * `pickLanAddress()` ever emits); anything else must be `wss://`. Throws on
168
+ * violation — mirrored by the web-panel parser (remote-control-pairing.js).
169
+ */
170
+ export function assertDirectWsUrlAllowed(wsUrl) {
171
+ let parsed;
172
+ try {
173
+ parsed = new URL(String(wsUrl));
174
+ } catch {
175
+ throw new Error(`Invalid direct pairing endpoint: ${wsUrl}`);
176
+ }
177
+ if (parsed.protocol !== "ws:" && parsed.protocol !== "wss:") {
178
+ throw new Error(
179
+ `Direct pairing endpoint must be ws:// or wss:// (got ${parsed.protocol})`,
180
+ );
181
+ }
182
+ // extractHost strips IPv6 brackets ("[::1]" → "::1") — isPrivateHost alone
183
+ // would miss bracketed literals (the documented new URL() gotcha).
184
+ if (
185
+ parsed.protocol === "ws:" &&
186
+ !isPrivateHost(extractHost(parsed.hostname))
187
+ ) {
188
+ throw new Error(
189
+ `Refusing plaintext ws:// to non-private host "${parsed.hostname}" — ` +
190
+ "direct pairing is LAN/loopback only; use wss:// for anything else",
191
+ );
192
+ }
193
+ return parsed;
194
+ }
195
+
162
196
  /** Inverse of buildDirectPairingUri. Throws on malformed input. */
163
197
  export function parseDirectPairingUri(uri) {
164
198
  if (
@@ -175,6 +209,7 @@ export function parseDirectPairingUri(uri) {
175
209
  `Unsupported remote-control pairing payload (v=${payload.v}, transport=${payload.transport})`,
176
210
  );
177
211
  }
212
+ if (payload.wsUrl) assertDirectWsUrlAllowed(payload.wsUrl);
178
213
  return payload;
179
214
  }
180
215
 
@@ -19,6 +19,7 @@
19
19
  import http from "http";
20
20
  import fs from "fs";
21
21
  import path from "path";
22
+ import crypto from "crypto";
22
23
  import { fileURLToPath } from "url";
23
24
  import { getInlineSource as getEnvelopeInlineSource } from "./web-ui-envelope.js";
24
25
  import { CLISkillLoader } from "./skill-loader.js";
@@ -1224,28 +1225,140 @@ function findWebPanelDist(staticDir) {
1224
1225
  return null;
1225
1226
  }
1226
1227
 
1228
+ /** Constant-time token check (same construction as ws-server's): hash both
1229
+ * sides to a fixed length so length differences don't leak, then compare in
1230
+ * constant time. `null`/absent configured token → no auth required. */
1231
+ function downloadTokenOk(configuredToken, req) {
1232
+ if (!configuredToken) return true;
1233
+ let supplied = "";
1234
+ const auth = String(req.headers?.authorization || "");
1235
+ if (auth.toLowerCase().startsWith("bearer ")) {
1236
+ supplied = auth.slice(7).trim();
1237
+ } else {
1238
+ try {
1239
+ supplied =
1240
+ new URL(req.url, "http://localhost").searchParams.get("token") || "";
1241
+ } catch {
1242
+ supplied = "";
1243
+ }
1244
+ }
1245
+ const a = crypto.createHash("sha256").update(String(supplied)).digest();
1246
+ const b = crypto
1247
+ .createHash("sha256")
1248
+ .update(String(configuredToken))
1249
+ .digest();
1250
+ return crypto.timingSafeEqual(a, b);
1251
+ }
1252
+
1253
+ /** RFC 5987 filename* value for a Content-Disposition header. */
1254
+ function encodeDispositionFilename(name) {
1255
+ const base = String(name || "artifact").replace(/[\r\n"]/g, "_");
1256
+ return `attachment; filename*=UTF-8''${encodeURIComponent(base)}`;
1257
+ }
1258
+
1227
1259
  /**
1228
1260
  * Handle `/api/*` routes. Returns `true` if the request was handled (caller
1229
1261
  * must stop further processing) and `false` for everything else (caller
1230
1262
  * continues with the static/SPA handler).
1231
1263
  *
1232
- * Currently exposes a single read-only endpoint:
1264
+ * Endpoints:
1233
1265
  *
1234
1266
  * GET /api/skills → 200 { schema: 1, skills: [{name, source, category,
1235
1267
  * description, version}] }
1268
+ * GET /api/artifacts/<id>/download → the artifact's stored copy as an
1269
+ * attachment (full file — the WS artifact-content preview
1270
+ * route is capped; this is the uncapped download path the
1271
+ * web-panel's 下载 button uses). When the server was
1272
+ * created with a wsToken, the same token must accompany
1273
+ * the request (`Authorization: Bearer` or `?token=`).
1236
1274
  *
1237
- * The smoke-runner's project-mode probe (packer/smoke-runner.js) hits this
1238
- * to verify bundled skills were materialized and registered. The shape is
1239
- * deliberately minimal — the Web UI's richer skill views use WS.
1275
+ * The smoke-runner's project-mode probe (packer/smoke-runner.js) hits
1276
+ * /api/skills to verify bundled skills were materialized and registered. The
1277
+ * shapes are deliberately minimal — the Web UI's richer views use WS.
1240
1278
  *
1241
1279
  * @param {import("http").IncomingMessage} req
1242
1280
  * @param {import("http").ServerResponse} res
1281
+ * @param {{ wsToken?: string|null }} [ctx] server context (auth for downloads)
1243
1282
  * @returns {boolean} true if handled, false if the caller should continue
1244
1283
  */
1245
- export function handleApiRequest(req, res) {
1284
+ export function handleApiRequest(req, res, ctx = {}) {
1246
1285
  const urlPath = req.url.split("?")[0];
1247
1286
  if (!urlPath.startsWith("/api/")) return false;
1248
1287
 
1288
+ const artifactMatch = urlPath.match(
1289
+ /^\/api\/artifacts\/([A-Za-z0-9_]+)\/download$/,
1290
+ );
1291
+ if (artifactMatch) {
1292
+ if (req.method !== "GET") {
1293
+ res.writeHead(405, { "Content-Type": "application/json" });
1294
+ res.end(JSON.stringify({ error: "Method Not Allowed" }));
1295
+ return true;
1296
+ }
1297
+ // The panel authenticates its WS with this token; the download endpoint
1298
+ // serves artifact FILE CONTENT, so it must not be weaker than the WS.
1299
+ if (!downloadTokenOk(ctx.wsToken || null, req)) {
1300
+ res.writeHead(401, { "Content-Type": "application/json" });
1301
+ res.end(JSON.stringify({ error: "Unauthorized" }));
1302
+ return true;
1303
+ }
1304
+ // Async body in a sync router: the route is claimed (return true) and the
1305
+ // response completes when the store import resolves.
1306
+ (async () => {
1307
+ try {
1308
+ const { ArtifactStore } = await import("./artifact-store.js");
1309
+ const store = new ArtifactStore();
1310
+ const entry = store.get(artifactMatch[1]);
1311
+ if (!entry) {
1312
+ res.writeHead(404, { "Content-Type": "application/json" });
1313
+ res.end(JSON.stringify({ error: "Not Found" }));
1314
+ return;
1315
+ }
1316
+ // Same tamper guard as the WS content route: the stored filename must
1317
+ // be a plain basename or the index row is refused.
1318
+ if (
1319
+ !entry.file ||
1320
+ entry.file !== path.basename(entry.file) ||
1321
+ entry.file.includes("..")
1322
+ ) {
1323
+ res.writeHead(400, { "Content-Type": "application/json" });
1324
+ res.end(JSON.stringify({ error: "Bad artifact index row" }));
1325
+ return;
1326
+ }
1327
+ const storedPath = store.storedPath(entry);
1328
+ let size;
1329
+ try {
1330
+ size = fs.statSync(storedPath).size;
1331
+ } catch {
1332
+ res.writeHead(404, { "Content-Type": "application/json" });
1333
+ res.end(JSON.stringify({ error: "Stored copy missing" }));
1334
+ return;
1335
+ }
1336
+ const downloadName =
1337
+ entry.title && path.extname(entry.title)
1338
+ ? entry.title
1339
+ : path.basename(entry.sourcePath || entry.file);
1340
+ res.writeHead(200, {
1341
+ "Content-Type": entry.mime || "application/octet-stream",
1342
+ "Content-Length": size,
1343
+ "Content-Disposition": encodeDispositionFilename(downloadName),
1344
+ "Cache-Control": "no-store",
1345
+ "X-Content-Type-Options": "nosniff",
1346
+ });
1347
+ const stream = fs.createReadStream(storedPath);
1348
+ stream.on("error", () => res.destroy());
1349
+ stream.pipe(res);
1350
+ } catch (err) {
1351
+ try {
1352
+ res.writeHead(500, { "Content-Type": "application/json" });
1353
+ res.end(JSON.stringify({ error: err.message }));
1354
+ } catch {
1355
+ /* headers already sent — connection is torn down by the stream */
1356
+ }
1357
+ }
1358
+ })();
1359
+ return true;
1360
+ }
1361
+
1249
1362
  if (urlPath === "/api/skills") {
1250
1363
  if (req.method !== "GET") {
1251
1364
  res.writeHead(405, { "Content-Type": "application/json" });
@@ -1328,7 +1441,7 @@ export function createWebUIServer(opts) {
1328
1441
  return http.createServer((req, res) => {
1329
1442
  // /api/* routes are handled before the static file logic so GET /api/skills
1330
1443
  // doesn't accidentally resolve to index.html via the SPA fallback.
1331
- if (handleApiRequest(req, res)) return;
1444
+ if (handleApiRequest(req, res, { wsToken: opts.wsToken || null })) return;
1332
1445
 
1333
1446
  if (req.method !== "GET") {
1334
1447
  res.writeHead(405, { "Content-Type": "text/plain" });
@@ -1415,7 +1528,7 @@ export function createWebUIServer(opts) {
1415
1528
  // ── Fallback: embedded classic single-page HTML ─────────────────────────
1416
1529
  const html = buildHtml(opts);
1417
1530
  return http.createServer((req, res) => {
1418
- if (handleApiRequest(req, res)) return;
1531
+ if (handleApiRequest(req, res, { wsToken: opts.wsToken || null })) return;
1419
1532
 
1420
1533
  const urlPath = req.url.split("?")[0];
1421
1534
  if (