chainlesschain 0.162.157 → 0.162.159

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 (212) 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-ByIiF5bT.js} +1 -1
  5. package/src/assets/web-panel/assets/{ActionButton-Zu0N8BQL.js → ActionButton-NlgSFZq5.js} +1 -1
  6. package/src/assets/web-panel/assets/{Analytics-DZTwj5T_.js → Analytics-D17cOUcb.js} +3 -3
  7. package/src/assets/web-panel/assets/{AppLayout-CVFJh47v.js → AppLayout-B4mEZcbl.js} +5 -5
  8. package/src/assets/web-panel/assets/Artifacts-CASsmyQw.js +1 -0
  9. package/src/assets/web-panel/assets/Artifacts-CTVt96zW.css +1 -0
  10. package/src/assets/web-panel/assets/{Audit-BMfiLj7T.js → Audit-DYAia2e2.js} +1 -1
  11. package/src/assets/web-panel/assets/{BackgroundAgents-BslPCdGF.js → BackgroundAgents-C1lvYUSu.js} +1 -1
  12. package/src/assets/web-panel/assets/{Backup-CQ_fMC1r.js → Backup-B6agK6bA.js} +1 -1
  13. package/src/assets/web-panel/assets/{BaseInput-CsFsARiC.js → BaseInput-DM43R2vy.js} +1 -1
  14. package/src/assets/web-panel/assets/{Chat-CwfBobco.js → Chat-Dtdp3eXp.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-BL5c_OQ1.js} +1 -1
  17. package/src/assets/web-panel/assets/{Checkbox-DkSyjjPp.js → Checkbox-D2xf7uZt.js} +1 -1
  18. package/src/assets/web-panel/assets/{Codegen-B7Fx3zZ6.js → Codegen-D3j45jJL.js} +1 -1
  19. package/src/assets/web-panel/assets/{Col-Dzfqn8Ep.js → Col-voPJ9fUZ.js} +1 -1
  20. package/src/assets/web-panel/assets/{Community-DhHPdzMM.js → Community-DEsyTESF.js} +1 -1
  21. package/src/assets/web-panel/assets/{Compact-Ct18_8y7.js → Compact-OHV_QXLD.js} +1 -1
  22. package/src/assets/web-panel/assets/{Compliance-CY64IqBi.js → Compliance-eDTJWC-J.js} +1 -1
  23. package/src/assets/web-panel/assets/{Cowork-BYSmWlUl.js → Cowork-BBvy-6d1.js} +2 -2
  24. package/src/assets/web-panel/assets/{Cron-_4xtP3Ug.js → Cron-Dw2eoJxu.js} +2 -2
  25. package/src/assets/web-panel/assets/{Crosschain-B4qY-Ycr.js → Crosschain-DQHms_Bh.js} +1 -1
  26. package/src/assets/web-panel/assets/{DID-CkCzWkx1.js → DID-Cyu7Id1c.js} +2 -2
  27. package/src/assets/web-panel/assets/{Dashboard-C1scT7Ke.js → Dashboard-sIDzkG92.js} +2 -2
  28. package/src/assets/web-panel/assets/{Dropdown-DBnQ2EWd.js → Dropdown-DUW3T2ey.js} +1 -1
  29. package/src/assets/web-panel/assets/{EmailListRenderer-C9JhOTq0.js → EmailListRenderer-CECELXW8.js} +1 -1
  30. package/src/assets/web-panel/assets/{FamilyGuardDashboard-CWXv5RUv.js → FamilyGuardDashboard-BIZR37ov.js} +1 -1
  31. package/src/assets/web-panel/assets/{Federation-fYdcbz9W.js → Federation-BEZIQX84.js} +1 -1
  32. package/src/assets/web-panel/assets/{FormItemContext-CJqOT3Yj.js → FormItemContext-BcdUMznL.js} +1 -1
  33. package/src/assets/web-panel/assets/{GenericCardRenderer-DGCsHc5K.js → GenericCardRenderer-DsN_fr5H.js} +1 -1
  34. package/src/assets/web-panel/assets/{Git-EH2K-H0Q.js → Git-63LhjBx2.js} +2 -2
  35. package/src/assets/web-panel/assets/{Governance-DJn1X2pR.js → Governance-CYtQtIcH.js} +1 -1
  36. package/src/assets/web-panel/assets/{Inference-Dch1E8qd.js → Inference-BwR6UUox.js} +1 -1
  37. package/src/assets/web-panel/assets/{KnowledgeGraph-QIur8j8-.js → KnowledgeGraph-D9lVNTUA.js} +1 -1
  38. package/src/assets/web-panel/assets/{Logs-DUgHldPg.js → Logs-C4q5h7Ut.js} +2 -2
  39. package/src/assets/web-panel/assets/MarkdownRenderer-DbzhFwvz.css +1 -0
  40. package/src/assets/web-panel/assets/MarkdownRenderer-DexLlXfN.js +1 -0
  41. package/src/assets/web-panel/assets/{Marketplace-DafrSfvd.js → Marketplace-Plksliuw.js} +1 -1
  42. package/src/assets/web-panel/assets/{McpTools-D2xRbfJx.js → McpTools-DZUFfeuG.js} +3 -3
  43. package/src/assets/web-panel/assets/{Memory-C6RbVHAo.js → Memory-NoDYt29V.js} +2 -2
  44. package/src/assets/web-panel/assets/{MobileBridge-BPtVBKqn.js → MobileBridge-CEXGTkdh.js} +1 -1
  45. package/src/assets/web-panel/assets/{MobileProjects-Cuw832cr.js → MobileProjects-DnbJkbZo.js} +1 -1
  46. package/src/assets/web-panel/assets/{Mtc-D6O9fWFa.js → Mtc-DTAa0cwy.js} +5 -5
  47. package/src/assets/web-panel/assets/{MtcAudit-CWZcXIH1.js → MtcAudit-CVV7LnNS.js} +4 -4
  48. package/src/assets/web-panel/assets/{Multisig-DhIBxTFd.js → Multisig-BqqGrjt_.js} +3 -3
  49. package/src/assets/web-panel/assets/{NLProgramming-wkBFS33e.js → NLProgramming-C4WP8DRp.js} +1 -1
  50. package/src/assets/web-panel/assets/{Notes-B09FxncA.js → Notes-Zo4DJUVI.js} +3 -3
  51. package/src/assets/web-panel/assets/{NotificationSettings-oQwtH6-9.js → NotificationSettings-DBcjRaz2.js} +1 -1
  52. package/src/assets/web-panel/assets/OrderTableRenderer-BCQel9bD.js +1 -0
  53. package/src/assets/web-panel/assets/{Organization-CsF-NIjz.js → Organization-BT9WPBOX.js} +4 -4
  54. package/src/assets/web-panel/assets/{Overflow-bLxv9Dqi.js → Overflow-D6b4bZ3q.js} +1 -1
  55. package/src/assets/web-panel/assets/{P2P-DVwQtm4q.js → P2P-Cw3OsYx-.js} +2 -2
  56. package/src/assets/web-panel/assets/{PdhVaultBrowser-Bgic3QKm.js → PdhVaultBrowser-C0nyiK2b.js} +4 -4
  57. package/src/assets/web-panel/assets/{Permissions-as80-9ep.js → Permissions-zVBZEB_C.js} +3 -3
  58. package/src/assets/web-panel/assets/{PersonalDataHub-BGmUw36a.js → PersonalDataHub-BOspsFew.js} +2 -2
  59. package/src/assets/web-panel/assets/{Pipeline-BM96zBxG.js → Pipeline-CQWfZ4XH.js} +1 -1
  60. package/src/assets/web-panel/assets/{Privacy-BBOzBSWe.js → Privacy-C2oGJ_WL.js} +1 -1
  61. package/src/assets/web-panel/assets/{ProjectInit-GHX41V28.js → ProjectInit-BZMR4qY1.js} +2 -2
  62. package/src/assets/web-panel/assets/{ProjectSettings-DraqzxKD.js → ProjectSettings-DAujoP63.js} +2 -2
  63. package/src/assets/web-panel/assets/{Projects-BzzdUml4.js → Projects-DvH_P2Uq.js} +1 -1
  64. package/src/assets/web-panel/assets/{Providers-CSeYNAtW.js → Providers-D_RGHM-v.js} +1 -1
  65. package/src/assets/web-panel/assets/{QrScannerModal-DlzBCdXh.js → QrScannerModal-Mf6KfL2J.js} +1 -1
  66. package/src/assets/web-panel/assets/{QuickAsk-p8iB7od_.js → QuickAsk-5D8URoI_.js} +1 -1
  67. package/src/assets/web-panel/assets/{Recommend-DjOVXacd.js → Recommend-Df4kC8Gy.js} +1 -1
  68. package/src/assets/web-panel/assets/RemoteSession-DGi3f3vO.js +4 -0
  69. package/src/assets/web-panel/assets/{Reputation-CjT3M0Di.js → Reputation-jmkGFNWb.js} +1 -1
  70. package/src/assets/web-panel/assets/{Row-tw8m6T0c.js → Row-DP6Pcu3m.js} +1 -1
  71. package/src/assets/web-panel/assets/{RssFeed-B47KBx_f.js → RssFeed-D60hSuPO.js} +3 -3
  72. package/src/assets/web-panel/assets/{Search-D2sx1PBe.js → Search-0mf1BQUK.js} +1 -1
  73. package/src/assets/web-panel/assets/{Security-JmtL8g7D.js → Security-CfZtLFYV.js} +3 -3
  74. package/src/assets/web-panel/assets/{Services-BxrNqjin.js → Services-D9zpZLWz.js} +2 -2
  75. package/src/assets/web-panel/assets/{Skeleton-udk8L18t.js → Skeleton-D1_44eMD.js} +1 -1
  76. package/src/assets/web-panel/assets/{Skills-DooLT9of.js → Skills-cVFa8zkz.js} +1 -1
  77. package/src/assets/web-panel/assets/{Sla-ITVZE1xI.js → Sla-Cinpzc3L.js} +1 -1
  78. package/src/assets/web-panel/assets/{SpeechSettings-DCkfqY1n.js → SpeechSettings-bqdiJE_P.js} +1 -1
  79. package/src/assets/web-panel/assets/{SyncSettings-CkNlgFjM.js → SyncSettings-Dgl_SLgj.js} +2 -2
  80. package/src/assets/web-panel/assets/{Tasks-BMAoyCM_.js → Tasks-DawWAEhy.js} +1 -1
  81. package/src/assets/web-panel/assets/{Templates-BnrFTlrz.js → Templates-Ca0nY651.js} +1 -1
  82. package/src/assets/web-panel/assets/{Tenant-BjNsy3-i.js → Tenant-CgaGEhLm.js} +1 -1
  83. package/src/assets/web-panel/assets/{Terminal-C0gxLL7V.js → Terminal-D-9EnpRx.js} +2 -2
  84. package/src/assets/web-panel/assets/{TimelineRenderer-CUtj_KHA.js → TimelineRenderer-CSOxDHZ6.js} +1 -1
  85. package/src/assets/web-panel/assets/{Tokens-DOg61zlq.js → Tokens-BpwLZpL3.js} +1 -1
  86. package/src/assets/web-panel/assets/{Trigger-D4ldCewj.js → Trigger-DgowwBK5.js} +1 -1
  87. package/src/assets/web-panel/assets/{Trust-3lk_Zd8j.js → Trust-BZutapjE.js} +1 -1
  88. package/src/assets/web-panel/assets/{UkeySign-kWhFR6Li.js → UkeySign-QZPt8_Sp.js} +1 -1
  89. package/src/assets/web-panel/assets/{VideoEditing-D3GOFVWg.js → VideoEditing-DlQx5UDJ.js} +1 -1
  90. package/src/assets/web-panel/assets/{Wallet-DzrZfBuh.js → Wallet-ZfdTgtWA.js} +4 -4
  91. package/src/assets/web-panel/assets/{WebAuthn-CyVJVp_g.js → WebAuthn-CAp0y_Cr.js} +5 -5
  92. package/src/assets/web-panel/assets/{WorkflowEditor-i_b0wi9b.js → WorkflowEditor-BVITZEmr.js} +1 -1
  93. package/src/assets/web-panel/assets/{chat-DwPCK8I7.js → chat-CC2tPaNg.js} +1 -1
  94. package/src/assets/web-panel/assets/{colors-D0vaVqZU.js → colors-31brc9CA.js} +1 -1
  95. package/src/assets/web-panel/assets/{compact-item-Don7nGW2.js → compact-item-DLMkC9C-.js} +1 -1
  96. package/src/assets/web-panel/assets/{createContext-D84R5qy2.js → createContext-BMtveI6F.js} +1 -1
  97. package/src/assets/web-panel/assets/devWarning-CB8pPOtd.js +1 -0
  98. package/src/assets/web-panel/assets/{hasIn-BeUqVVKU.js → hasIn-DbD6WHW2.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-m9bn0F1p.js → index-3oBsXZmF.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-CfYRv-zA.js → index-ALvoNTRF.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-4MkfPLGv.js → index-AaIKG60c.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-LMo9bBLJ.js → index-BFRLmCR3.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-wesQQ2cH.js → index-BKAB6-6V.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-CzT56x-_.js → index-BOM-VFlv.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-L73NrmlW.js → index-BPpS-lGb.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-WRw1pM5E.js → index-Bbc2JeQG.js} +1 -1
  107. package/src/assets/web-panel/assets/index-BhXiaiev.js +1 -0
  108. package/src/assets/web-panel/assets/{index-Br3SOdER.js → index-BiRQUoyE.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-D8r7WKk-.js → index-BnFLzejT.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-CAdCTTBC.js → index-BoO4WEJB.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-BlfTf0ka.js → index-C-cGlCDW.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-B57al0Z5.js → index-C1PfeI88.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-D4b5XqIy.js → index-C21pdBqs.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-CnnxzIx0.js → index-C7LHNw_N.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-BNpJf-sf.js → index-CEXa-4zd.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-DT40zd7K.js → index-CEwjBA9q.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-CL71rIwo.js → index-CFXKbOek.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-BZ1N07Sp.js → index-CJhREOlG.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-C0_sKVi7.js → index-COHHQ9cU.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-DBzBLX-K.js → index-CUmLMFsy.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-De85D9Pc.js → index-CWgfipcC.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-x7VYlV41.js → index-C_eUKGYP.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-hjgrIX5I.js → index-CcVszn2b.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-DMT8n9vB.js → index-Cod97Urs.js} +3 -3
  125. package/src/assets/web-panel/assets/{index-DFFzwrNs.js → index-Cwbqh0KZ.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-YKLCotoo.js → index-D67kIagl.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-IGGttsCv.js → index-D7cV0tu0.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-DQor_7NF.js → index-DJvxFyIz.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-D84BnuFg.js → index-DZl-UXB6.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-CrnVEEQO.js → index-Dm0byjm1.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-BER98o4P.js → index-DndNnYc0.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-DA0-EJKx.js → index-WQUO2vvK.js} +1 -1
  133. package/src/assets/web-panel/assets/index-WpaYaE3D.js +1 -0
  134. package/src/assets/web-panel/assets/{index-CExWtm98.js → index-bdaJwTXJ.js} +1 -1
  135. package/src/assets/web-panel/assets/{index-MKZrJrP_.js → index-ep6JchNG.js} +1 -1
  136. package/src/assets/web-panel/assets/{index-7QUwcGTg.js → index-jG6h22Qg.js} +1 -1
  137. package/src/assets/web-panel/assets/{index-BohrJpBf.js → index-ntVQqSML.js} +1 -1
  138. package/src/assets/web-panel/assets/{initDefaultProps-DGeU4vPS.js → initDefaultProps-D5UoBRhN.js} +1 -1
  139. package/src/assets/web-panel/assets/{motion-Dgqj9ab3.js → motion-DltOw0CR.js} +1 -1
  140. package/src/assets/web-panel/assets/{move-BFyEU6Lr.js → move-_oPkk5zj.js} +1 -1
  141. package/src/assets/web-panel/assets/{mtc-parser-CueCXXPC.js → mtc-parser-EES8ROCS.js} +1 -1
  142. package/src/assets/web-panel/assets/{omit-D2pU5OyE.js → omit-B-_39x_M.js} +1 -1
  143. package/src/assets/web-panel/assets/{pickAttrs-CKTf6zD6.js → pickAttrs-l6D0yJHz.js} +1 -1
  144. package/src/assets/web-panel/assets/{placementArrow-DieJaGCN.js → placementArrow-DIeUK31L.js} +1 -1
  145. package/src/assets/web-panel/assets/{responsiveObserve-Bxr-rmU6.js → responsiveObserve-dG4EzUQo.js} +1 -1
  146. package/src/assets/web-panel/assets/{slide-CDEDk3Se.js → slide-DWFzYGdH.js} +1 -1
  147. package/src/assets/web-panel/assets/{statusUtils-icf2eCTx.js → statusUtils-CECdTA5H.js} +1 -1
  148. package/src/assets/web-panel/assets/{styleChecker-qKvgmBhE.js → styleChecker-D6kP-mMK.js} +1 -1
  149. package/src/assets/web-panel/assets/{useFlexGapSupport-1_UhiyJZ.js → useFlexGapSupport-BWfna_rd.js} +1 -1
  150. package/src/assets/web-panel/assets/{useFs-CFGLXDWh.js → useFs-Bb-j6rzo.js} +1 -1
  151. package/src/assets/web-panel/assets/{usePersonalDataHub-UafOXvQ6.js → usePersonalDataHub-D5OIsYM4.js} +1 -1
  152. package/src/assets/web-panel/assets/{vnode-BfPqSx79.js → vnode-DNpwzFTM.js} +1 -1
  153. package/src/assets/web-panel/assets/{zoom-CXsVcRB6.js → zoom-vNLpFQBW.js} +1 -1
  154. package/src/assets/web-panel/index.html +1 -1
  155. package/src/command-manifest.json +17 -3
  156. package/src/commands/agent.js +11 -1
  157. package/src/commands/background-session.js +143 -3
  158. package/src/commands/browse.js +86 -0
  159. package/src/commands/cloud.js +232 -0
  160. package/src/commands/doctor.js +61 -6
  161. package/src/commands/mcp.js +61 -0
  162. package/src/commands/plugin.js +63 -1
  163. package/src/commands/routine.js +367 -0
  164. package/src/commands/session.js +326 -0
  165. package/src/data/changelog.json +12 -2
  166. package/src/harness/jsonl-session-store.js +82 -2
  167. package/src/harness/session-index.js +295 -0
  168. package/src/harness/session-mirror.js +142 -0
  169. package/src/harness/transcript-integrity.js +182 -0
  170. package/src/index.js +8 -0
  171. package/src/lib/background-agent-supervisor.js +126 -2
  172. package/src/lib/channels/channel-manager.js +126 -0
  173. package/src/lib/channels/telegram-channel.js +112 -0
  174. package/src/lib/channels/webhook-channel.js +140 -0
  175. package/src/lib/chrome-connector.js +425 -4
  176. package/src/lib/cloud/bundle.js +110 -0
  177. package/src/lib/cloud/cloud-client.js +152 -0
  178. package/src/lib/computer-use/authorization.js +173 -0
  179. package/src/lib/computer-use/computer-use-server.js +276 -0
  180. package/src/lib/computer-use/control-backend.js +150 -0
  181. package/src/lib/doctor-checkup.js +652 -0
  182. package/src/lib/jsonl-session-store.js +4 -0
  183. package/src/lib/lsp/lsp-server-registry.js +48 -21
  184. package/src/lib/plugin-runtime/governance.js +174 -0
  185. package/src/lib/pr-link-ledger.js +221 -0
  186. package/src/lib/remote-control.js +35 -0
  187. package/src/lib/routine-store.js +352 -0
  188. package/src/lib/session-store-guard.js +40 -0
  189. package/src/lib/session-usage.js +318 -2
  190. package/src/lib/sub-agent-context.js +23 -0
  191. package/src/lib/web-ui-server.js +120 -7
  192. package/src/repl/agent-repl.js +592 -61
  193. package/src/repl/bg-dashboard.js +355 -0
  194. package/src/repl/fast-mode.js +103 -0
  195. package/src/repl/fullscreen-tui.js +197 -0
  196. package/src/repl/remote-approval.js +111 -0
  197. package/src/repl/slash-command-registry.js +11 -3
  198. package/src/repl/voice-dictation.js +132 -0
  199. package/src/runtime/agent-core.js +278 -1
  200. package/src/runtime/coding-agent-contract-shared.cjs +141 -0
  201. package/src/runtime/coding-agent-policy.cjs +33 -0
  202. package/src/runtime/headless-runner.js +71 -7
  203. package/src/runtime/headless-stream.js +14 -8
  204. package/src/runtime/policies/agent-policy.js +6 -0
  205. package/src/assets/web-panel/assets/Artifacts-BbN5V7au.js +0 -1
  206. package/src/assets/web-panel/assets/Artifacts-CRkepl4t.css +0 -1
  207. package/src/assets/web-panel/assets/Chat-DDh58PCf.css +0 -1
  208. package/src/assets/web-panel/assets/OrderTableRenderer-iFKeNf6Y.js +0 -1
  209. package/src/assets/web-panel/assets/RemoteSession-CIGk7gk-.js +0 -4
  210. package/src/assets/web-panel/assets/devWarning-BBwWehQ7.js +0 -1
  211. package/src/assets/web-panel/assets/index-DJ8etkBT.js +0 -1
  212. package/src/assets/web-panel/assets/index-OeaK-zXZ.js +0 -1
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Self-hosted cloud handoff (gap-2026-07-11 P1#7) — the private-runner version
3
+ * of Claude Code's --cloud/--teleport. `cc cloud run "<task>"` bundles the
4
+ * current git branch, POSTs it to a configured private runner, and records the
5
+ * job locally; `cc cloud status/attach` poll and reflow the runner's result
6
+ * (plan / patch / PR / artifacts) back into the local workspace.
7
+ *
8
+ * No Anthropic cloud dependency: the runner is any HTTP endpoint speaking the
9
+ * small protocol below (a reference runner can be a box running `cc agent`).
10
+ *
11
+ * POST {base}/v1/jobs {task, bundle(base64), branch, baseSha} → {jobId}
12
+ * GET {base}/v1/jobs/{id} → {status: queued|running|done|failed, summary?}
13
+ * GET {base}/v1/jobs/{id}/result → {patch?, plan?, pr?, artifacts?, log?}
14
+ *
15
+ * Auth: Bearer token (config cloud.token / CC_CLOUD_TOKEN). Transport (fetch)
16
+ * and the bundle builder are injected so this is unit-testable offline.
17
+ *
18
+ * Local job ledger: ~/.chainlesschain/cloud-jobs/<jobId>.json
19
+ */
20
+
21
+ import {
22
+ existsSync,
23
+ mkdirSync,
24
+ readdirSync,
25
+ readFileSync,
26
+ writeFileSync,
27
+ } from "node:fs";
28
+ import { join } from "node:path";
29
+ import { getHomeDir } from "../paths.js";
30
+
31
+ export const _deps = { fetch: (...a) => globalThis.fetch(...a) };
32
+
33
+ export function cloudJobsDir() {
34
+ const dir = join(getHomeDir(), "cloud-jobs");
35
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
36
+ return dir;
37
+ }
38
+
39
+ function jobPath(jobId) {
40
+ if (!/^[\w.-]+$/.test(String(jobId || ""))) {
41
+ throw new Error(`invalid job id: ${jobId}`);
42
+ }
43
+ return join(cloudJobsDir(), `${jobId}.json`);
44
+ }
45
+
46
+ export function saveJob(job) {
47
+ writeFileSync(jobPath(job.jobId), JSON.stringify(job, null, 2), "utf-8");
48
+ return job;
49
+ }
50
+
51
+ export function readJob(jobId) {
52
+ try {
53
+ return JSON.parse(readFileSync(jobPath(jobId), "utf-8"));
54
+ } catch {
55
+ return null;
56
+ }
57
+ }
58
+
59
+ export function listJobs() {
60
+ const dir = cloudJobsDir();
61
+ if (!existsSync(dir)) return [];
62
+ return readdirSync(dir)
63
+ .filter((f) => f.endsWith(".json"))
64
+ .map((f) => {
65
+ try {
66
+ return JSON.parse(readFileSync(join(dir, f), "utf-8"));
67
+ } catch {
68
+ return null;
69
+ }
70
+ })
71
+ .filter(Boolean)
72
+ .sort((a, b) => (b.submittedAt || 0) - (a.submittedAt || 0));
73
+ }
74
+
75
+ export class CloudClient {
76
+ /**
77
+ * @param {object} opts { baseUrl, token, deps? }
78
+ */
79
+ constructor(opts = {}) {
80
+ this.baseUrl = String(opts.baseUrl || "").replace(/\/+$/, "");
81
+ if (!this.baseUrl) {
82
+ throw new Error(
83
+ "no cloud runner configured — set cloud.baseUrl / CC_CLOUD_URL",
84
+ );
85
+ }
86
+ this.token = opts.token || null;
87
+ this.deps = { ..._deps, ...(opts.deps || {}) };
88
+ }
89
+
90
+ _headers(extra = {}) {
91
+ return {
92
+ "Content-Type": "application/json",
93
+ ...(this.token ? { Authorization: `Bearer ${this.token}` } : {}),
94
+ ...extra,
95
+ };
96
+ }
97
+
98
+ async _json(method, path, body) {
99
+ const res = await this.deps.fetch(`${this.baseUrl}${path}`, {
100
+ method,
101
+ headers: this._headers(),
102
+ ...(body ? { body: JSON.stringify(body) } : {}),
103
+ });
104
+ if (!res.ok) {
105
+ throw new Error(`cloud runner ${method} ${path} → HTTP ${res.status}`);
106
+ }
107
+ return res.json();
108
+ }
109
+
110
+ /** Submit a job. `bundle` is a base64 git bundle of the branch. */
111
+ async submit({ task, bundle, branch, baseSha }) {
112
+ const data = await this._json("POST", "/v1/jobs", {
113
+ task,
114
+ bundle,
115
+ branch,
116
+ baseSha,
117
+ });
118
+ if (!data?.jobId) throw new Error("runner did not return a jobId");
119
+ return data;
120
+ }
121
+
122
+ async status(jobId) {
123
+ return this._json("GET", `/v1/jobs/${encodeURIComponent(jobId)}`);
124
+ }
125
+
126
+ async result(jobId) {
127
+ return this._json("GET", `/v1/jobs/${encodeURIComponent(jobId)}/result`);
128
+ }
129
+ }
130
+
131
+ export const TERMINAL_CLOUD_STATUS = new Set(["done", "failed"]);
132
+
133
+ /**
134
+ * Poll a job until terminal or the deadline. `onTick(status)` reports progress.
135
+ * `sleep`/`now` injected for tests.
136
+ */
137
+ export async function pollUntilDone(client, jobId, opts = {}) {
138
+ const intervalMs = opts.intervalMs ?? 3000;
139
+ const timeoutMs = opts.timeoutMs ?? 30 * 60_000;
140
+ const sleep = opts.sleep || ((ms) => new Promise((r) => setTimeout(r, ms)));
141
+ const now = opts.now || (() => Date.now());
142
+ const deadline = now() + timeoutMs;
143
+ for (;;) {
144
+ const status = await client.status(jobId);
145
+ opts.onTick?.(status);
146
+ if (TERMINAL_CLOUD_STATUS.has(status.status)) return status;
147
+ if (now() >= deadline) {
148
+ return { status: "timeout", summary: "poll deadline reached" };
149
+ }
150
+ await sleep(intervalMs);
151
+ }
152
+ }
@@ -0,0 +1,173 @@
1
+ /**
2
+ * Computer-use authorization (gap-2026-07-11 P1#6) — the trust core shared by
3
+ * the computer-use MCP server. GUI control is the highest-privilege surface an
4
+ * agent can hold (it can click "OK" on anything), so it is:
5
+ * - OFF unless explicitly enabled (`cc mcp computer-use` / --enable);
6
+ * - capability-gated: each verb (screenshot/click/type/scroll/clipboard/
7
+ * app-launch/window-list) can be individually withheld;
8
+ * - per-app scoped: an allowlist confines control to named apps, and a
9
+ * high-risk denylist (terminals, IDEs, file managers, system settings,
10
+ * password managers) always requires confirmation even when allowed.
11
+ *
12
+ * Pure + dependency-free so it is exhaustively unit-tested; the server and the
13
+ * OS backend consume its verdicts.
14
+ */
15
+
16
+ export const COMPUTER_USE_CAPABILITIES = [
17
+ "screenshot",
18
+ "window_list",
19
+ "app_launch",
20
+ "click",
21
+ "type",
22
+ "scroll",
23
+ "clipboard_read",
24
+ "clipboard_write",
25
+ ];
26
+
27
+ // Read-only observation is lower risk than input injection; the tool-priority
28
+ // guidance (MCP/API > shell > browser action > computer use) lives here so the
29
+ // server can surface it and the model prefers cheaper, auditable paths.
30
+ export const TOOL_PRIORITY = [
31
+ "mcp/api",
32
+ "shell",
33
+ "browser action",
34
+ "computer use",
35
+ ];
36
+
37
+ // Apps where an unattended click/type is disproportionately dangerous — a
38
+ // terminal runs commands, a password manager reveals secrets, system settings
39
+ // change the machine. Matched case-insensitively as a substring of the app /
40
+ // window identifier. Confirmation is forced even inside the allowlist.
41
+ export const HIGH_RISK_APP_PATTERNS = [
42
+ "terminal",
43
+ "powershell",
44
+ "cmd.exe",
45
+ "conhost",
46
+ "windows terminal",
47
+ "iterm",
48
+ "code", // VS Code / VSCodium
49
+ "jetbrains",
50
+ "intellij",
51
+ "explorer.exe",
52
+ "finder",
53
+ "nautilus",
54
+ "settings",
55
+ "control panel",
56
+ "regedit",
57
+ "keychain",
58
+ "1password",
59
+ "bitwarden",
60
+ "lastpass",
61
+ "keepass",
62
+ ];
63
+
64
+ /**
65
+ * @param {object} config computer-use config subtree
66
+ * { enabled, capabilities?: string[], appAllowlist?: string[],
67
+ * highRiskPatterns?: string[], confirmAll?: boolean }
68
+ */
69
+ export function createAuthorizer(config = {}) {
70
+ const enabled = config.enabled === true;
71
+ const caps = new Set(
72
+ Array.isArray(config.capabilities) && config.capabilities.length > 0
73
+ ? config.capabilities
74
+ : COMPUTER_USE_CAPABILITIES,
75
+ );
76
+ const allowlist = Array.isArray(config.appAllowlist)
77
+ ? config.appAllowlist.map((a) => String(a).toLowerCase())
78
+ : null; // null = no app restriction (any app), [] = deny every app
79
+ const highRisk = (
80
+ Array.isArray(config.highRiskPatterns)
81
+ ? config.highRiskPatterns
82
+ : HIGH_RISK_APP_PATTERNS
83
+ ).map((p) => String(p).toLowerCase());
84
+ const confirmAll = config.confirmAll === true;
85
+
86
+ const INPUT_VERBS = new Set([
87
+ "click",
88
+ "type",
89
+ "scroll",
90
+ "app_launch",
91
+ "clipboard_write",
92
+ ]);
93
+
94
+ function isHighRiskApp(app) {
95
+ const a = String(app || "").toLowerCase();
96
+ if (!a) return false;
97
+ return highRisk.some((p) => a.includes(p));
98
+ }
99
+
100
+ /**
101
+ * @returns {{ allowed:boolean, requiresConfirmation:boolean, reason?:string }}
102
+ */
103
+ function authorize(capability, context = {}) {
104
+ if (!enabled) {
105
+ return {
106
+ allowed: false,
107
+ requiresConfirmation: false,
108
+ reason:
109
+ "computer-use is disabled — enable it explicitly (cc mcp computer-use --enable)",
110
+ };
111
+ }
112
+ if (!COMPUTER_USE_CAPABILITIES.includes(capability)) {
113
+ return {
114
+ allowed: false,
115
+ requiresConfirmation: false,
116
+ reason: `unknown capability: ${capability}`,
117
+ };
118
+ }
119
+ if (!caps.has(capability)) {
120
+ return {
121
+ allowed: false,
122
+ requiresConfirmation: false,
123
+ reason: `capability "${capability}" is not enabled for this session`,
124
+ };
125
+ }
126
+
127
+ const app = context.app || context.window || null;
128
+
129
+ // App allowlist applies to app-targeted verbs (input + launch). Pure
130
+ // observation (screenshot / window_list / clipboard_read) is not app-scoped.
131
+ if (allowlist && (INPUT_VERBS.has(capability) || app)) {
132
+ const target = String(app || context.target || "").toLowerCase();
133
+ if (!target) {
134
+ return {
135
+ allowed: false,
136
+ requiresConfirmation: false,
137
+ reason: `capability "${capability}" needs a target app while an allowlist is configured`,
138
+ };
139
+ }
140
+ const ok = allowlist.some(
141
+ (a) => target.includes(a) || a.includes(target),
142
+ );
143
+ if (!ok) {
144
+ return {
145
+ allowed: false,
146
+ requiresConfirmation: false,
147
+ reason: `app "${app || context.target}" is not in the computer-use allowlist`,
148
+ };
149
+ }
150
+ }
151
+
152
+ // High-risk apps and input verbs (or confirmAll) require confirmation.
153
+ const highRiskApp = isHighRiskApp(app);
154
+ const requiresConfirmation =
155
+ confirmAll || highRiskApp || INPUT_VERBS.has(capability);
156
+ return {
157
+ allowed: true,
158
+ requiresConfirmation,
159
+ reason: highRiskApp
160
+ ? `high-risk app (${app}) — confirmation required`
161
+ : requiresConfirmation
162
+ ? `${capability} injects input — confirmation required`
163
+ : undefined,
164
+ };
165
+ }
166
+
167
+ return {
168
+ enabled,
169
+ capabilities: [...caps],
170
+ authorize,
171
+ isHighRiskApp,
172
+ };
173
+ }
@@ -0,0 +1,276 @@
1
+ /**
2
+ * Computer-use MCP server (gap-2026-07-11 P1#6) — exposes GUI control verbs to
3
+ * an MCP client, gated by the authorization core (authorization.js) and driven
4
+ * by an OS backend (control-backend.js). Streamable-HTTP MCP, same transport
5
+ * shape as `cc mcp serve`: POST JSON-RPC → application/json.
6
+ *
7
+ * Every tools/call runs through the authorizer first. A verb the authorizer
8
+ * marks `requiresConfirmation` is refused unless the caller supplied
9
+ * `confirm: true` in the tool args (the MCP client's approval surface / the
10
+ * agent's confirm flow owns the human check) — an unattended agent can never
11
+ * silently click a high-risk app.
12
+ */
13
+
14
+ import http from "node:http";
15
+ import { join } from "node:path";
16
+ import { tmpdir } from "node:os";
17
+ import { randomBytes, timingSafeEqual } from "node:crypto";
18
+ import {
19
+ createAuthorizer,
20
+ COMPUTER_USE_CAPABILITIES,
21
+ TOOL_PRIORITY,
22
+ } from "./authorization.js";
23
+ import { createWindowsBackend } from "./control-backend.js";
24
+
25
+ const rpcResult = (id, result) =>
26
+ JSON.stringify({ jsonrpc: "2.0", id, result });
27
+ const rpcError = (id, code, message) =>
28
+ JSON.stringify({ jsonrpc: "2.0", id, error: { code, message } });
29
+ const ok = (data) => ({
30
+ content: [{ type: "text", text: JSON.stringify(data) }],
31
+ });
32
+ const fail = (msg) => ({
33
+ content: [{ type: "text", text: msg }],
34
+ isError: true,
35
+ });
36
+
37
+ function bearerMatches(authHeader, token) {
38
+ const m = /^Bearer\s+(.+)$/.exec(String(authHeader || ""));
39
+ if (!m) return false;
40
+ const a = Buffer.from(m[1]);
41
+ const b = Buffer.from(token);
42
+ return a.length === b.length && timingSafeEqual(a, b);
43
+ }
44
+
45
+ const TOOL_SCHEMAS = {
46
+ computer_screenshot: {
47
+ description: "Capture the screen to a PNG (read-only observation).",
48
+ inputSchema: { type: "object", properties: {} },
49
+ },
50
+ computer_window_list: {
51
+ description: "List visible windows (pid, app, title).",
52
+ inputSchema: { type: "object", properties: {} },
53
+ },
54
+ computer_clipboard_read: {
55
+ description: "Read the system clipboard text.",
56
+ inputSchema: { type: "object", properties: {} },
57
+ },
58
+ computer_clipboard_write: {
59
+ description: "Write text to the system clipboard.",
60
+ inputSchema: {
61
+ type: "object",
62
+ properties: { text: { type: "string" } },
63
+ required: ["text"],
64
+ },
65
+ },
66
+ computer_click: {
67
+ description:
68
+ "Click at screen coordinates (input injection; requires confirm:true for high-risk apps).",
69
+ inputSchema: {
70
+ type: "object",
71
+ properties: {
72
+ x: { type: "number" },
73
+ y: { type: "number" },
74
+ app: { type: "string" },
75
+ confirm: { type: "boolean" },
76
+ },
77
+ required: ["x", "y"],
78
+ },
79
+ },
80
+ computer_type: {
81
+ description: "Type text into the focused window (input injection).",
82
+ inputSchema: {
83
+ type: "object",
84
+ properties: {
85
+ text: { type: "string" },
86
+ app: { type: "string" },
87
+ confirm: { type: "boolean" },
88
+ },
89
+ required: ["text"],
90
+ },
91
+ },
92
+ computer_scroll: {
93
+ description: "Scroll by N notches (positive up, negative down).",
94
+ inputSchema: {
95
+ type: "object",
96
+ properties: {
97
+ amount: { type: "number" },
98
+ app: { type: "string" },
99
+ confirm: { type: "boolean" },
100
+ },
101
+ required: ["amount"],
102
+ },
103
+ },
104
+ computer_app_launch: {
105
+ description: "Launch an application by path/name.",
106
+ inputSchema: {
107
+ type: "object",
108
+ properties: {
109
+ app: { type: "string" },
110
+ args: { type: "array", items: { type: "string" } },
111
+ confirm: { type: "boolean" },
112
+ },
113
+ required: ["app"],
114
+ },
115
+ },
116
+ };
117
+
118
+ const TOOL_CAPABILITY = {
119
+ computer_screenshot: "screenshot",
120
+ computer_window_list: "window_list",
121
+ computer_clipboard_read: "clipboard_read",
122
+ computer_clipboard_write: "clipboard_write",
123
+ computer_click: "click",
124
+ computer_type: "type",
125
+ computer_scroll: "scroll",
126
+ computer_app_launch: "app_launch",
127
+ };
128
+
129
+ /**
130
+ * Execute one tool call through authorize → confirm-gate → backend.
131
+ * Pure orchestration (no HTTP) so it is unit-tested directly.
132
+ */
133
+ export function handleToolCall(name, args = {}, { authorizer, backend }) {
134
+ const capability = TOOL_CAPABILITY[name];
135
+ if (!capability) return fail(`unknown tool: ${name}`);
136
+
137
+ const context = { app: args.app || null, target: args.app || null };
138
+ const verdict = authorizer.authorize(capability, context);
139
+ if (!verdict.allowed) return fail(`[computer-use denied] ${verdict.reason}`);
140
+ if (verdict.requiresConfirmation && args.confirm !== true) {
141
+ return fail(
142
+ `[computer-use needs confirmation] ${verdict.reason || capability} — re-call with confirm:true after the user approves`,
143
+ );
144
+ }
145
+
146
+ let result;
147
+ switch (name) {
148
+ case "computer_screenshot":
149
+ result = backend.screenshot(
150
+ args.path ||
151
+ join(tmpdir(), `cc-screenshot-${randomBytes(4).toString("hex")}.png`),
152
+ );
153
+ break;
154
+ case "computer_window_list":
155
+ result = backend.windowList();
156
+ break;
157
+ case "computer_clipboard_read":
158
+ result = backend.clipboardRead();
159
+ break;
160
+ case "computer_clipboard_write":
161
+ result = backend.clipboardWrite(args.text);
162
+ break;
163
+ case "computer_click":
164
+ result = backend.click(args.x, args.y);
165
+ break;
166
+ case "computer_type":
167
+ result = backend.type(args.text);
168
+ break;
169
+ case "computer_scroll":
170
+ result = backend.scroll(args.amount);
171
+ break;
172
+ case "computer_app_launch":
173
+ result = backend.appLaunch(args.app, args.args || []);
174
+ break;
175
+ default:
176
+ return fail(`unhandled tool: ${name}`);
177
+ }
178
+ return result.ok ? ok(result) : fail(result.error || `${name} failed`);
179
+ }
180
+
181
+ export function availableTools(authorizer) {
182
+ return Object.entries(TOOL_SCHEMAS)
183
+ .filter(([name]) => authorizer.capabilities.includes(TOOL_CAPABILITY[name]))
184
+ .map(([name, t]) => ({
185
+ name,
186
+ description: t.description,
187
+ inputSchema: t.inputSchema,
188
+ }));
189
+ }
190
+
191
+ export function startComputerUseServer(opts = {}) {
192
+ const authorizer = createAuthorizer({
193
+ enabled: true, // starting the server IS the explicit opt-in
194
+ ...opts.config,
195
+ });
196
+ const backend = opts.backend || createWindowsBackend();
197
+ const token =
198
+ opts.token === false ? null : opts.token || randomBytes(16).toString("hex");
199
+
200
+ const server = http.createServer((req, res) => {
201
+ const send = (status, body) => {
202
+ res.writeHead(status, { "Content-Type": "application/json" });
203
+ res.end(body);
204
+ };
205
+ if (req.method !== "POST")
206
+ return send(405, rpcError(null, -32600, "POST only"));
207
+ if (token && !bearerMatches(req.headers.authorization, token)) {
208
+ return send(401, rpcError(null, -32001, "unauthorized"));
209
+ }
210
+ let raw = "";
211
+ let bytes = 0;
212
+ let aborted = false;
213
+ req.on("data", (c) => {
214
+ if (aborted) return;
215
+ bytes += c.length;
216
+ if (bytes > 1024 * 1024) {
217
+ aborted = true;
218
+ send(413, rpcError(null, -32600, "request too large"));
219
+ req.destroy();
220
+ } else raw += c;
221
+ });
222
+ req.on("end", () => {
223
+ if (aborted) return;
224
+ let msg;
225
+ try {
226
+ msg = JSON.parse(raw);
227
+ } catch {
228
+ return send(400, rpcError(null, -32700, "parse error"));
229
+ }
230
+ const { id, method, params } = msg || {};
231
+ if (method === "initialize") {
232
+ return send(
233
+ 200,
234
+ rpcResult(id, {
235
+ protocolVersion: params?.protocolVersion || "2025-03-26",
236
+ capabilities: { tools: {} },
237
+ serverInfo: { name: "cc-computer-use", version: "1.0.0" },
238
+ instructions: `Tool priority (prefer the cheapest, most auditable): ${TOOL_PRIORITY.join(" > ")}. Input verbs need confirm:true.`,
239
+ }),
240
+ );
241
+ }
242
+ if (method === "notifications/initialized") {
243
+ res.writeHead(202);
244
+ return res.end();
245
+ }
246
+ if (method === "tools/list") {
247
+ return send(200, rpcResult(id, { tools: availableTools(authorizer) }));
248
+ }
249
+ if (method === "tools/call") {
250
+ const result = handleToolCall(params?.name, params?.arguments || {}, {
251
+ authorizer,
252
+ backend,
253
+ });
254
+ return send(200, rpcResult(id, result));
255
+ }
256
+ return send(200, rpcError(id, -32601, `unknown method: ${method}`));
257
+ });
258
+ });
259
+
260
+ return new Promise((resolve, reject) => {
261
+ server.once("error", reject);
262
+ server.listen(opts.port || 0, "127.0.0.1", () => {
263
+ const port = server.address().port;
264
+ resolve({
265
+ server,
266
+ port,
267
+ token,
268
+ url: `http://127.0.0.1:${port}`,
269
+ capabilities: authorizer.capabilities,
270
+ stop: () => new Promise((r) => server.close(() => r())),
271
+ });
272
+ });
273
+ });
274
+ }
275
+
276
+ export { COMPUTER_USE_CAPABILITIES };