chainlesschain 0.162.158 → 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.
- package/package.json +1 -1
- package/src/assets/web-panel/assets/{AIOps-BwpTJTN_.js → AIOps-ByIiF5bT.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-BtQOoo3l.js → ActionButton-NlgSFZq5.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-Duf_rXZa.js → Analytics-D17cOUcb.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-XQK3wjvB.js → AppLayout-B4mEZcbl.js} +4 -4
- package/src/assets/web-panel/assets/Artifacts-CASsmyQw.js +1 -0
- package/src/assets/web-panel/assets/{Audit-DNjOGQLh.js → Audit-DYAia2e2.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-FH9RAfB5.js → BackgroundAgents-C1lvYUSu.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-ZIbrR4-U.js → Backup-B6agK6bA.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-DSt3NaND.js → BaseInput-DM43R2vy.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-CY-wvbjv.js → Chat-Dtdp3eXp.js} +5 -5
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-BAV9V-t-.js → ChatBubbleRenderer-BL5c_OQ1.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-8JVadE_B.js → Checkbox-D2xf7uZt.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-CN83nPYI.js → Codegen-D3j45jJL.js} +1 -1
- package/src/assets/web-panel/assets/{Col-xPSvqdN8.js → Col-voPJ9fUZ.js} +1 -1
- package/src/assets/web-panel/assets/{Community-D78cnIjG.js → Community-DEsyTESF.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-CSw6Csbp.js → Compact-OHV_QXLD.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-E_HhjcYR.js → Compliance-eDTJWC-J.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-DybeQjmm.js → Cowork-BBvy-6d1.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-DS0BJeLY.js → Cron-Dw2eoJxu.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-BOwVgkbs.js → Crosschain-DQHms_Bh.js} +1 -1
- package/src/assets/web-panel/assets/{DID-DRUr9YuX.js → DID-Cyu7Id1c.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-Cht-yUKW.js → Dashboard-sIDzkG92.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-BLr_EJ9X.js → Dropdown-DUW3T2ey.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-Drexuh5W.js → EmailListRenderer-CECELXW8.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-D9Q2Kq63.js → FamilyGuardDashboard-BIZR37ov.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-C-Z1xI-T.js → Federation-BEZIQX84.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-DLHPMkQF.js → FormItemContext-BcdUMznL.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-DxxEdn2r.js → GenericCardRenderer-DsN_fr5H.js} +1 -1
- package/src/assets/web-panel/assets/{Git-syATcaBt.js → Git-63LhjBx2.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-CYe_r8Zf.js → Governance-CYtQtIcH.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-GWxErB7k.js → Inference-BwR6UUox.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-qQvVCZoi.js → KnowledgeGraph-D9lVNTUA.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-BenAU2W8.js → Logs-C4q5h7Ut.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-7agE3Or1.js → MarkdownRenderer-DexLlXfN.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-D4Jn5fpD.js → Marketplace-Plksliuw.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-DtAwoqiQ.js → McpTools-DZUFfeuG.js} +4 -4
- package/src/assets/web-panel/assets/{Memory-BI_O50I8.js → Memory-NoDYt29V.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-CQWE2lqx.js → MobileBridge-CEXGTkdh.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-CVtTqgtg.js → MobileProjects-DnbJkbZo.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-Dnr16qsn.js → Mtc-DTAa0cwy.js} +5 -5
- package/src/assets/web-panel/assets/{MtcAudit-CQ5Xe4-a.js → MtcAudit-CVV7LnNS.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-Bjr8O-wy.js → Multisig-BqqGrjt_.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-CEe-KirU.js → NLProgramming-C4WP8DRp.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-spKJIKF9.js → Notes-Zo4DJUVI.js} +4 -4
- package/src/assets/web-panel/assets/{NotificationSettings-DRp83LFG.js → NotificationSettings-DBcjRaz2.js} +1 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-BCQel9bD.js +1 -0
- package/src/assets/web-panel/assets/{Organization-BlgB0IYg.js → Organization-BT9WPBOX.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-a_35HqZI.js → Overflow-D6b4bZ3q.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-CSNHCNPC.js → P2P-Cw3OsYx-.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-C8M1JH-1.js → PdhVaultBrowser-C0nyiK2b.js} +5 -5
- package/src/assets/web-panel/assets/{Permissions-DmjJi9Ms.js → Permissions-zVBZEB_C.js} +3 -3
- package/src/assets/web-panel/assets/{PersonalDataHub-DfuwRyf4.js → PersonalDataHub-BOspsFew.js} +2 -2
- package/src/assets/web-panel/assets/{Pipeline-B2-S4ekE.js → Pipeline-CQWfZ4XH.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-B1dOiBLC.js → Privacy-C2oGJ_WL.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-_pNmc-DX.js → ProjectInit-BZMR4qY1.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-DmwG2Ii1.js → ProjectSettings-DAujoP63.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-COATmHxF.js → Projects-DvH_P2Uq.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-C6HSjg1d.js → Providers-D_RGHM-v.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-CrDmS7l1.js → QrScannerModal-Mf6KfL2J.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-CfKvWt-x.js → QuickAsk-5D8URoI_.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-DMa6Kuzv.js → Recommend-Df4kC8Gy.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-DrktUo5x.js → RemoteSession-DGi3f3vO.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-BTPQ1vjL.js → Reputation-jmkGFNWb.js} +1 -1
- package/src/assets/web-panel/assets/{Row-B6Q7QkMG.js → Row-DP6Pcu3m.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-B1IJhAmX.js → RssFeed-D60hSuPO.js} +3 -3
- package/src/assets/web-panel/assets/{Search-xbea1kGD.js → Search-0mf1BQUK.js} +1 -1
- package/src/assets/web-panel/assets/{Security-7rs0cO3F.js → Security-CfZtLFYV.js} +4 -4
- package/src/assets/web-panel/assets/{Services-0fjixK0S.js → Services-D9zpZLWz.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-B_rVq9Cu.js → Skeleton-D1_44eMD.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-C5uM90b9.js → Skills-cVFa8zkz.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-U4pYCIZH.js → Sla-Cinpzc3L.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-Cdz4yTWx.js → SpeechSettings-bqdiJE_P.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-Me3aetHv.js → SyncSettings-Dgl_SLgj.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-Cf6aNkL_.js → Tasks-DawWAEhy.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-CYatk4tU.js → Templates-Ca0nY651.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-DIMKLw7o.js → Tenant-CgaGEhLm.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-C_cp-uSJ.js → Terminal-D-9EnpRx.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-cUPVNADp.js → TimelineRenderer-CSOxDHZ6.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-CiaEIXVf.js → Tokens-BpwLZpL3.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-DB1WU5lQ.js → Trigger-DgowwBK5.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-Cfht9Ijy.js → Trust-BZutapjE.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-K3P4Vt9S.js → UkeySign-QZPt8_Sp.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-DWjhjXRh.js → VideoEditing-DlQx5UDJ.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-D9-Q1Kan.js → Wallet-ZfdTgtWA.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-BDszy-oV.js → WebAuthn-CAp0y_Cr.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-DIhij4rA.js → WorkflowEditor-BVITZEmr.js} +1 -1
- package/src/assets/web-panel/assets/{chat-D7VCiKbh.js → chat-CC2tPaNg.js} +1 -1
- package/src/assets/web-panel/assets/{colors-BvOd9pVn.js → colors-31brc9CA.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-DoqXBiAO.js → compact-item-DLMkC9C-.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-CrKRzUtp.js → createContext-BMtveI6F.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-CB8pPOtd.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-BWAev4WC.js → hasIn-DbD6WHW2.js} +1 -1
- package/src/assets/web-panel/assets/{index-B30NvR21.js → index-3oBsXZmF.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cx7M7PSF.js → index-ALvoNTRF.js} +1 -1
- package/src/assets/web-panel/assets/{index-BHOcVYrx.js → index-AaIKG60c.js} +1 -1
- package/src/assets/web-panel/assets/{index-BXyl8O23.js → index-BFRLmCR3.js} +1 -1
- package/src/assets/web-panel/assets/{index-YGDy7nNq.js → index-BKAB6-6V.js} +1 -1
- package/src/assets/web-panel/assets/{index-BoYrS_L2.js → index-BOM-VFlv.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bq_ctRpz.js → index-BPpS-lGb.js} +1 -1
- package/src/assets/web-panel/assets/{index-CDGLdn12.js → index-Bbc2JeQG.js} +1 -1
- package/src/assets/web-panel/assets/index-BhXiaiev.js +1 -0
- package/src/assets/web-panel/assets/{index-ISvPZ05r.js → index-BiRQUoyE.js} +1 -1
- package/src/assets/web-panel/assets/{index-B0vGEbJT.js → index-BnFLzejT.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bj7EgmOp.js → index-BoO4WEJB.js} +1 -1
- package/src/assets/web-panel/assets/{index-BZGk8AbZ.js → index-C-cGlCDW.js} +1 -1
- package/src/assets/web-panel/assets/{index-BkgnGX7v.js → index-C1PfeI88.js} +1 -1
- package/src/assets/web-panel/assets/{index-HwHdTusY.js → index-C21pdBqs.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bf9YlbDn.js → index-C7LHNw_N.js} +1 -1
- package/src/assets/web-panel/assets/{index-oY45XrkG.js → index-CEXa-4zd.js} +1 -1
- package/src/assets/web-panel/assets/{index-KR1-Em9C.js → index-CEwjBA9q.js} +1 -1
- package/src/assets/web-panel/assets/{index-C5kRmj3P.js → index-CFXKbOek.js} +1 -1
- package/src/assets/web-panel/assets/{index-BN9m4Ux2.js → index-CJhREOlG.js} +1 -1
- package/src/assets/web-panel/assets/{index-PDxGjKRu.js → index-COHHQ9cU.js} +1 -1
- package/src/assets/web-panel/assets/{index-D23L1V76.js → index-CUmLMFsy.js} +1 -1
- package/src/assets/web-panel/assets/{index-zH-tuuGZ.js → index-CWgfipcC.js} +1 -1
- package/src/assets/web-panel/assets/{index-CmJ6xKBO.js → index-C_eUKGYP.js} +1 -1
- package/src/assets/web-panel/assets/{index-CEPHMSih.js → index-CcVszn2b.js} +1 -1
- package/src/assets/web-panel/assets/{index-DV-Rfmfx.js → index-Cod97Urs.js} +3 -3
- package/src/assets/web-panel/assets/{index-BWWsaimP.js → index-Cwbqh0KZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-8g5TJCZ3.js → index-D67kIagl.js} +1 -1
- package/src/assets/web-panel/assets/{index-CEXfKSiI.js → index-D7cV0tu0.js} +1 -1
- package/src/assets/web-panel/assets/{index-CoD31Ndm.js → index-DJvxFyIz.js} +1 -1
- package/src/assets/web-panel/assets/{index-CSb-tnIo.js → index-DZl-UXB6.js} +1 -1
- package/src/assets/web-panel/assets/{index-DvDOpsJB.js → index-Dm0byjm1.js} +1 -1
- package/src/assets/web-panel/assets/{index-DlGu7As6.js → index-DndNnYc0.js} +1 -1
- package/src/assets/web-panel/assets/{index-gmxqu5Kk.js → index-WQUO2vvK.js} +1 -1
- package/src/assets/web-panel/assets/index-WpaYaE3D.js +1 -0
- package/src/assets/web-panel/assets/{index-ww1QtLnX.js → index-bdaJwTXJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-JzUmJMvn.js → index-ep6JchNG.js} +1 -1
- package/src/assets/web-panel/assets/{index-BCyP071m.js → index-jG6h22Qg.js} +1 -1
- package/src/assets/web-panel/assets/{index-1poMpMU0.js → index-ntVQqSML.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-XW-OR3mL.js → initDefaultProps-D5UoBRhN.js} +1 -1
- package/src/assets/web-panel/assets/{motion-CeuEHh20.js → motion-DltOw0CR.js} +1 -1
- package/src/assets/web-panel/assets/{move-Dva79Keg.js → move-_oPkk5zj.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-C0B6bD2j.js → mtc-parser-EES8ROCS.js} +1 -1
- package/src/assets/web-panel/assets/{omit-AkZvO-a2.js → omit-B-_39x_M.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-B4r55qGc.js → pickAttrs-l6D0yJHz.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-BLH-z7YE.js → placementArrow-DIeUK31L.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-ksEGZ2Wf.js → responsiveObserve-dG4EzUQo.js} +1 -1
- package/src/assets/web-panel/assets/{slide-Cth4OD1Z.js → slide-DWFzYGdH.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-9S83ezxw.js → statusUtils-CECdTA5H.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-7yQmQGZ4.js → styleChecker-D6kP-mMK.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-DpGnMmPc.js → useFlexGapSupport-BWfna_rd.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-C4zDZIXH.js → useFs-Bb-j6rzo.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-CoLICdUZ.js → usePersonalDataHub-D5OIsYM4.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-CKyH-35X.js → vnode-DNpwzFTM.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-DRDGit17.js → zoom-vNLpFQBW.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-manifest.json +17 -3
- package/src/commands/agent.js +7 -0
- package/src/commands/background-session.js +143 -3
- package/src/commands/browse.js +86 -0
- package/src/commands/cloud.js +232 -0
- package/src/commands/doctor.js +61 -6
- package/src/commands/mcp.js +61 -0
- package/src/commands/plugin.js +63 -1
- package/src/commands/routine.js +367 -0
- package/src/commands/session.js +326 -0
- package/src/data/changelog.json +7 -2
- package/src/harness/jsonl-session-store.js +82 -2
- package/src/harness/session-index.js +295 -0
- package/src/harness/session-mirror.js +142 -0
- package/src/harness/transcript-integrity.js +182 -0
- package/src/index.js +8 -0
- package/src/lib/background-agent-supervisor.js +126 -2
- package/src/lib/channels/channel-manager.js +126 -0
- package/src/lib/channels/telegram-channel.js +112 -0
- package/src/lib/channels/webhook-channel.js +140 -0
- package/src/lib/chrome-connector.js +386 -0
- package/src/lib/cloud/bundle.js +110 -0
- package/src/lib/cloud/cloud-client.js +152 -0
- package/src/lib/computer-use/authorization.js +173 -0
- package/src/lib/computer-use/computer-use-server.js +276 -0
- package/src/lib/computer-use/control-backend.js +150 -0
- package/src/lib/doctor-checkup.js +652 -0
- package/src/lib/jsonl-session-store.js +4 -0
- package/src/lib/lsp/lsp-server-registry.js +48 -21
- package/src/lib/plugin-runtime/governance.js +174 -0
- package/src/lib/pr-link-ledger.js +221 -0
- package/src/lib/routine-store.js +352 -0
- package/src/lib/session-store-guard.js +40 -0
- package/src/lib/session-usage.js +318 -2
- package/src/lib/sub-agent-context.js +23 -0
- package/src/repl/agent-repl.js +361 -3
- package/src/repl/bg-dashboard.js +355 -0
- package/src/repl/fast-mode.js +103 -0
- package/src/repl/fullscreen-tui.js +197 -0
- package/src/repl/slash-command-registry.js +11 -3
- package/src/repl/voice-dictation.js +132 -0
- package/src/runtime/agent-core.js +242 -1
- package/src/runtime/coding-agent-contract-shared.cjs +88 -0
- package/src/runtime/coding-agent-policy.cjs +17 -0
- package/src/runtime/headless-runner.js +71 -7
- package/src/runtime/headless-stream.js +14 -8
- package/src/runtime/policies/agent-policy.js +3 -0
- package/src/assets/web-panel/assets/Artifacts-DQvJoQ6E.js +0 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-h1A0XB2a.js +0 -1
- package/src/assets/web-panel/assets/devWarning-nDB--2xr.js +0 -1
- package/src/assets/web-panel/assets/index-B1_pRTtB.js +0 -1
- package/src/assets/web-panel/assets/index-TMM9FuW-.js +0 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import chalk from "chalk";
|
|
3
3
|
import { logger } from "../lib/logger.js";
|
|
4
|
+
import { readPrLinkLedger } from "../lib/pr-link-ledger.js";
|
|
4
5
|
|
|
5
6
|
function formatAge(ms) {
|
|
6
7
|
const n = Math.max(0, Math.round(Number(ms || 0) / 1000));
|
|
@@ -67,6 +68,11 @@ export function formatBackgroundAgentDetails(
|
|
|
67
68
|
` log: ${session.logFile || "-"}`,
|
|
68
69
|
];
|
|
69
70
|
if (session.phase) lines.push(` phase: ${session.phase}`);
|
|
71
|
+
if (session.pr?.number) {
|
|
72
|
+
lines.push(
|
|
73
|
+
` pr: #${session.pr.number}${session.pr.state ? ` ${session.pr.state}` : ""}${session.pr.url ? ` ${session.pr.url}` : ""}`,
|
|
74
|
+
);
|
|
75
|
+
}
|
|
70
76
|
if (Number.isFinite(Number(session.turnCount))) {
|
|
71
77
|
lines.push(` turns: ${session.turnCount}`);
|
|
72
78
|
}
|
|
@@ -146,7 +152,7 @@ async function followBackgroundAgent(id, options = {}) {
|
|
|
146
152
|
* when the transport is unavailable so the caller can fall back to the
|
|
147
153
|
* log-only follow.
|
|
148
154
|
*/
|
|
149
|
-
async function interactiveAttach(id, state, options = {}) {
|
|
155
|
+
export async function interactiveAttach(id, state, options = {}) {
|
|
150
156
|
const { connectBackgroundSession } =
|
|
151
157
|
await import("../lib/background-session-transport.js");
|
|
152
158
|
const { readBackgroundAgentLog, logPath } = await loadSupervisor();
|
|
@@ -279,6 +285,134 @@ async function interactiveAttach(id, state, options = {}) {
|
|
|
279
285
|
return true;
|
|
280
286
|
}
|
|
281
287
|
|
|
288
|
+
/**
|
|
289
|
+
* `cc daemon view` (no id) — the interactive agent dashboard. Wires the real
|
|
290
|
+
* supervisor/transport into the injectable controller (repl/bg-dashboard.js).
|
|
291
|
+
* Non-TTY (or --json) degrades to the status listing.
|
|
292
|
+
*/
|
|
293
|
+
async function runDashboardView(options = {}) {
|
|
294
|
+
const supervisor = await loadSupervisor();
|
|
295
|
+
const {
|
|
296
|
+
listBackgroundAgents,
|
|
297
|
+
readBackgroundAgentLog,
|
|
298
|
+
stopBackgroundAgent,
|
|
299
|
+
renameBackgroundAgent,
|
|
300
|
+
setBackgroundAgentPinned,
|
|
301
|
+
resumeBackgroundAgent,
|
|
302
|
+
readBackgroundAgentState,
|
|
303
|
+
effectiveBackgroundAgentState,
|
|
304
|
+
} = supervisor;
|
|
305
|
+
|
|
306
|
+
const listAll = () => {
|
|
307
|
+
const sessions = listBackgroundAgents({ all: true });
|
|
308
|
+
// PR/session linking: decorate rows whose conversation touched a PR
|
|
309
|
+
// (`#123 open`). One ledger read per refresh; best-effort.
|
|
310
|
+
try {
|
|
311
|
+
const ledger = readPrLinkLedger();
|
|
312
|
+
for (const s of sessions) {
|
|
313
|
+
const links = s.sessionId ? ledger[s.sessionId] : null;
|
|
314
|
+
if (Array.isArray(links) && links.length > 0 && !s.pr) {
|
|
315
|
+
s.pr = [...links].sort(
|
|
316
|
+
(a, b) => (b.updatedAt || 0) - (a.updatedAt || 0),
|
|
317
|
+
)[0];
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
} catch {
|
|
321
|
+
/* PR decoration is cosmetic */
|
|
322
|
+
}
|
|
323
|
+
return sessions;
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
if (options.json || !process.stdout.isTTY || !process.stdin.isTTY) {
|
|
327
|
+
const sessions = listAll();
|
|
328
|
+
if (options.json) {
|
|
329
|
+
console.log(JSON.stringify({ sessions }, null, 2));
|
|
330
|
+
} else {
|
|
331
|
+
printBackgroundAgents(sessions);
|
|
332
|
+
logger.log(
|
|
333
|
+
chalk.gray("(interactive dashboard needs a TTY — showing a snapshot)"),
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const { runBgDashboard } = await import("../repl/bg-dashboard.js");
|
|
340
|
+
const { fileURLToPath } = await import("node:url");
|
|
341
|
+
const { spawn } = await import("node:child_process");
|
|
342
|
+
const BIN_PATH = fileURLToPath(
|
|
343
|
+
new URL("../../bin/chainlesschain.js", import.meta.url),
|
|
344
|
+
);
|
|
345
|
+
|
|
346
|
+
await runBgDashboard({
|
|
347
|
+
listAgents: listAll,
|
|
348
|
+
readLog: (id, lines) => {
|
|
349
|
+
try {
|
|
350
|
+
return readBackgroundAgentLog(id, { lines });
|
|
351
|
+
} catch {
|
|
352
|
+
return "";
|
|
353
|
+
}
|
|
354
|
+
},
|
|
355
|
+
stopAgent: (id) => stopBackgroundAgent(id),
|
|
356
|
+
renameAgent: (id, title) => renameBackgroundAgent(id, title),
|
|
357
|
+
pinAgent: (id, pinned) => setBackgroundAgentPinned(id, pinned),
|
|
358
|
+
replyAgent: async (session, text) => {
|
|
359
|
+
// Running with a live transport → queue a follow-up turn; otherwise
|
|
360
|
+
// continue the finished conversation as a new background session.
|
|
361
|
+
if (
|
|
362
|
+
session.status === "running" &&
|
|
363
|
+
session.transport?.pipe &&
|
|
364
|
+
session.transport?.token
|
|
365
|
+
) {
|
|
366
|
+
const { connectBackgroundSession } =
|
|
367
|
+
await import("../lib/background-session-transport.js");
|
|
368
|
+
const conn = await connectBackgroundSession({
|
|
369
|
+
pipePath: session.transport.pipe,
|
|
370
|
+
token: session.transport.token,
|
|
371
|
+
onEvent: () => {},
|
|
372
|
+
onClose: () => {},
|
|
373
|
+
});
|
|
374
|
+
try {
|
|
375
|
+
conn.send({ type: "prompt", text });
|
|
376
|
+
// give the frame a beat to flush before closing
|
|
377
|
+
await new Promise((r) => setTimeout(r, 150));
|
|
378
|
+
} finally {
|
|
379
|
+
try {
|
|
380
|
+
conn.close();
|
|
381
|
+
} catch {
|
|
382
|
+
/* worker already closed it */
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
resumeBackgroundAgent(session.id, text);
|
|
388
|
+
},
|
|
389
|
+
attachAgent: async (session) => {
|
|
390
|
+
const fresh = effectiveBackgroundAgentState(
|
|
391
|
+
readBackgroundAgentState(session.id),
|
|
392
|
+
);
|
|
393
|
+
if (!fresh) return;
|
|
394
|
+
if (
|
|
395
|
+
fresh.status === "running" &&
|
|
396
|
+
fresh.transport?.pipe &&
|
|
397
|
+
fresh.transport?.token
|
|
398
|
+
) {
|
|
399
|
+
const attached = await interactiveAttach(session.id, fresh, {});
|
|
400
|
+
if (attached) return;
|
|
401
|
+
}
|
|
402
|
+
await followBackgroundAgent(session.id, { lines: 40 });
|
|
403
|
+
},
|
|
404
|
+
dispatchAgent: async (text) => {
|
|
405
|
+
const child = spawn(
|
|
406
|
+
process.execPath,
|
|
407
|
+
[BIN_PATH, "agent", "--bg", "-p", text],
|
|
408
|
+
{ detached: true, stdio: "ignore", windowsHide: true },
|
|
409
|
+
);
|
|
410
|
+
child.unref();
|
|
411
|
+
return null; // id is minted by the launcher; the next refresh shows it
|
|
412
|
+
},
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
|
|
282
416
|
export function registerBackgroundSessionCommands(program) {
|
|
283
417
|
program
|
|
284
418
|
.command("logs <id>")
|
|
@@ -378,12 +512,18 @@ export function registerBackgroundSessionCommands(program) {
|
|
|
378
512
|
});
|
|
379
513
|
|
|
380
514
|
daemon
|
|
381
|
-
.command("view
|
|
382
|
-
.description(
|
|
515
|
+
.command("view [id]")
|
|
516
|
+
.description(
|
|
517
|
+
"Detailed view of one background agent, or the interactive agent dashboard when no id is given",
|
|
518
|
+
)
|
|
383
519
|
.option("-n, --lines <n>", "Recent log lines to include", "40")
|
|
384
520
|
.option("--json", "Output as JSON")
|
|
385
521
|
.action(async (id, options) => {
|
|
386
522
|
try {
|
|
523
|
+
if (!id) {
|
|
524
|
+
await runDashboardView(options);
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
387
527
|
const {
|
|
388
528
|
readBackgroundAgentState,
|
|
389
529
|
effectiveBackgroundAgentState,
|
package/src/commands/browse.js
CHANGED
|
@@ -391,6 +391,92 @@ export function registerBrowseChromeCommand(browse) {
|
|
|
391
391
|
logger.success(`Screenshot: ${state.screenshotPath}`);
|
|
392
392
|
}
|
|
393
393
|
});
|
|
394
|
+
|
|
395
|
+
chrome
|
|
396
|
+
.command("act")
|
|
397
|
+
.description(
|
|
398
|
+
"Perform EXPLICIT actions (click/type/press/navigate/waitForSelector/screenshot/assertText) in the connected Chrome — unlike 'state', this DRIVES your logged-in browser",
|
|
399
|
+
)
|
|
400
|
+
.option("-p, --port <port>", "CDP port", "9222")
|
|
401
|
+
.option("-t, --tab <n>", "Tab index", "0")
|
|
402
|
+
.requiredOption(
|
|
403
|
+
"--actions <json|@file>",
|
|
404
|
+
'JSON array of steps, e.g. \'[{"type":"click","selector":"#go"}]\', or @steps.json',
|
|
405
|
+
)
|
|
406
|
+
.option(
|
|
407
|
+
"--continue-on-error",
|
|
408
|
+
"Keep executing the remaining steps after a failed one (default: fail fast)",
|
|
409
|
+
)
|
|
410
|
+
.option("--json", "Output as JSON")
|
|
411
|
+
.action(async (options) => {
|
|
412
|
+
const { discoverCdp, performActions } =
|
|
413
|
+
await import("../lib/chrome-connector.js");
|
|
414
|
+
const port = numericOption(options.port, {
|
|
415
|
+
name: "--port",
|
|
416
|
+
integer: true,
|
|
417
|
+
min: 1,
|
|
418
|
+
fallback: 9222,
|
|
419
|
+
});
|
|
420
|
+
let actions;
|
|
421
|
+
try {
|
|
422
|
+
const raw = options.actions.startsWith("@")
|
|
423
|
+
? (await import("fs")).readFileSync(options.actions.slice(1), "utf-8")
|
|
424
|
+
: options.actions;
|
|
425
|
+
actions = JSON.parse(raw);
|
|
426
|
+
} catch (err) {
|
|
427
|
+
logger.error(
|
|
428
|
+
`--actions must be a JSON array (or @file.json): ${err.message}`,
|
|
429
|
+
);
|
|
430
|
+
process.exit(1);
|
|
431
|
+
}
|
|
432
|
+
// Refuse outright when nothing is listening — an action command must
|
|
433
|
+
// never silently no-op or launch a browser on its own.
|
|
434
|
+
const probe = await discoverCdp({ port });
|
|
435
|
+
if (!probe.ok) {
|
|
436
|
+
const msg = `No debuggable Chrome on port ${port} — refusing to act. Start one first: cc browse chrome launch`;
|
|
437
|
+
if (options.json) {
|
|
438
|
+
console.log(JSON.stringify({ ok: false, error: msg }, null, 2));
|
|
439
|
+
} else {
|
|
440
|
+
logger.error(msg);
|
|
441
|
+
}
|
|
442
|
+
process.exitCode = 1;
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
const res = await performActions(actions, {
|
|
446
|
+
port,
|
|
447
|
+
tab: numericOption(options.tab, {
|
|
448
|
+
name: "--tab",
|
|
449
|
+
integer: true,
|
|
450
|
+
min: 0,
|
|
451
|
+
fallback: 0,
|
|
452
|
+
}),
|
|
453
|
+
continueOnError: options.continueOnError === true,
|
|
454
|
+
});
|
|
455
|
+
if (options.json) {
|
|
456
|
+
console.log(JSON.stringify(res, null, 2));
|
|
457
|
+
if (!res.ok) process.exitCode = 1;
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
if (res.error) {
|
|
461
|
+
logger.error(res.error);
|
|
462
|
+
process.exitCode = 1;
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
if (res.profileWarning) logger.warn(res.profileWarning);
|
|
466
|
+
for (const step of res.steps) {
|
|
467
|
+
const mark = step.ok ? chalk.green("ok") : chalk.red("FAIL");
|
|
468
|
+
logger.log(
|
|
469
|
+
` [${mark}] ${step.action} ${chalk.gray(`(${step.durationMs}ms)`)} ${step.detail}`,
|
|
470
|
+
);
|
|
471
|
+
}
|
|
472
|
+
logger.log(
|
|
473
|
+
` ${res.executed}/${actions.length} step(s) executed → ${chalk.cyan(res.url)}${res.title ? ` (${res.title})` : ""}`,
|
|
474
|
+
);
|
|
475
|
+
if (res.auditError) {
|
|
476
|
+
logger.warn(`audit log write failed: ${res.auditError}`);
|
|
477
|
+
}
|
|
478
|
+
if (!res.ok) process.exitCode = 1;
|
|
479
|
+
});
|
|
394
480
|
}
|
|
395
481
|
|
|
396
482
|
import {
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `cc cloud` — self-hosted cloud handoff (gap-2026-07-11 P1#7).
|
|
3
|
+
*
|
|
4
|
+
* cc cloud run "<task>" bundle branch → submit to the private runner → record job
|
|
5
|
+
* cc cloud status [id] poll job(s) (last job when id omitted)
|
|
6
|
+
* cc cloud attach <id> wait for completion, reflow patch/plan/pr/artifacts locally
|
|
7
|
+
* cc cloud list local job ledger
|
|
8
|
+
*
|
|
9
|
+
* Runner endpoint + token come from config (cloud.baseUrl / cloud.token) or
|
|
10
|
+
* env (CC_CLOUD_URL / CC_CLOUD_TOKEN). See src/lib/cloud/cloud-client.js for
|
|
11
|
+
* the runner protocol.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
import { existsSync, mkdirSync } from "node:fs";
|
|
16
|
+
import chalk from "chalk";
|
|
17
|
+
import { logger } from "../lib/logger.js";
|
|
18
|
+
|
|
19
|
+
async function resolveRunner() {
|
|
20
|
+
let cfg = {};
|
|
21
|
+
try {
|
|
22
|
+
cfg = (await import("../lib/config-manager.js")).loadConfig()?.cloud || {};
|
|
23
|
+
} catch {
|
|
24
|
+
cfg = {};
|
|
25
|
+
}
|
|
26
|
+
const baseUrl = process.env.CC_CLOUD_URL || cfg.baseUrl || "";
|
|
27
|
+
const token = process.env.CC_CLOUD_TOKEN || cfg.token || null;
|
|
28
|
+
return { baseUrl, token };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function statusColor(status) {
|
|
32
|
+
if (status === "done") return chalk.green;
|
|
33
|
+
if (status === "failed" || status === "timeout") return chalk.red;
|
|
34
|
+
if (status === "running") return chalk.cyan;
|
|
35
|
+
return chalk.gray;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function registerCloudCommand(program) {
|
|
39
|
+
const cloud = program
|
|
40
|
+
.command("cloud")
|
|
41
|
+
.description(
|
|
42
|
+
"Self-hosted cloud handoff — offload a task to a private runner (bundle → run → reflow)",
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
cloud
|
|
46
|
+
.command("run <task...>")
|
|
47
|
+
.description(
|
|
48
|
+
"Bundle the current branch and submit a task to the private runner",
|
|
49
|
+
)
|
|
50
|
+
.option("--json", "Output as JSON")
|
|
51
|
+
.option("--attach", "Wait for completion and reflow the result")
|
|
52
|
+
.action(async (taskWords, options) => {
|
|
53
|
+
try {
|
|
54
|
+
const task = taskWords.join(" ");
|
|
55
|
+
const { baseUrl, token } = await resolveRunner();
|
|
56
|
+
const { CloudClient, saveJob } =
|
|
57
|
+
await import("../lib/cloud/cloud-client.js");
|
|
58
|
+
const { bundleBranch } = await import("../lib/cloud/bundle.js");
|
|
59
|
+
const client = new CloudClient({ baseUrl, token });
|
|
60
|
+
|
|
61
|
+
logger.log(chalk.gray("Bundling current branch…"));
|
|
62
|
+
const snap = bundleBranch(process.cwd());
|
|
63
|
+
logger.log(
|
|
64
|
+
chalk.gray(
|
|
65
|
+
` ${snap.branch} @ ${snap.baseSha.slice(0, 8)} (${(snap.bytes / 1024).toFixed(0)} KB)`,
|
|
66
|
+
),
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
const submitted = await client.submit({
|
|
70
|
+
task,
|
|
71
|
+
bundle: snap.bundle,
|
|
72
|
+
branch: snap.branch,
|
|
73
|
+
baseSha: snap.baseSha,
|
|
74
|
+
});
|
|
75
|
+
const job = saveJob({
|
|
76
|
+
jobId: submitted.jobId,
|
|
77
|
+
task,
|
|
78
|
+
branch: snap.branch,
|
|
79
|
+
baseSha: snap.baseSha,
|
|
80
|
+
baseUrl,
|
|
81
|
+
status: "queued",
|
|
82
|
+
submittedAt: Date.now(),
|
|
83
|
+
cwd: process.cwd(),
|
|
84
|
+
});
|
|
85
|
+
if (options.json && !options.attach) {
|
|
86
|
+
return console.log(JSON.stringify(job, null, 2));
|
|
87
|
+
}
|
|
88
|
+
logger.success(`Submitted cloud job ${job.jobId} → ${baseUrl}`);
|
|
89
|
+
logger.log(chalk.gray(` cc cloud attach ${job.jobId}`));
|
|
90
|
+
if (options.attach) await attachJob(job.jobId, options);
|
|
91
|
+
} catch (err) {
|
|
92
|
+
logger.error(err.message);
|
|
93
|
+
process.exitCode = 1;
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
cloud
|
|
98
|
+
.command("status [id]")
|
|
99
|
+
.description("Poll a cloud job's status (most recent when id omitted)")
|
|
100
|
+
.option("--json", "Output as JSON")
|
|
101
|
+
.action(async (id, options) => {
|
|
102
|
+
try {
|
|
103
|
+
const { CloudClient, readJob, listJobs, saveJob } =
|
|
104
|
+
await import("../lib/cloud/cloud-client.js");
|
|
105
|
+
const job = id ? readJob(id) : listJobs()[0];
|
|
106
|
+
if (!job)
|
|
107
|
+
throw new Error(id ? `job not found: ${id}` : "no cloud jobs");
|
|
108
|
+
const client = new CloudClient({
|
|
109
|
+
baseUrl: job.baseUrl,
|
|
110
|
+
token: (await resolveRunner()).token,
|
|
111
|
+
});
|
|
112
|
+
const status = await client.status(job.jobId);
|
|
113
|
+
saveJob({ ...job, status: status.status, lastSummary: status.summary });
|
|
114
|
+
if (options.json) return console.log(JSON.stringify(status, null, 2));
|
|
115
|
+
logger.log(
|
|
116
|
+
`${statusColor(status.status)(status.status)} ${job.jobId} ${chalk.gray(job.task)}`,
|
|
117
|
+
);
|
|
118
|
+
if (status.summary) logger.log(chalk.gray(` ${status.summary}`));
|
|
119
|
+
} catch (err) {
|
|
120
|
+
logger.error(err.message);
|
|
121
|
+
process.exitCode = 1;
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
cloud
|
|
126
|
+
.command("attach <id>")
|
|
127
|
+
.description("Wait for a cloud job to finish and reflow its result locally")
|
|
128
|
+
.option("--json", "Output as JSON")
|
|
129
|
+
.option("--no-apply", "Fetch the result but do not apply the patch")
|
|
130
|
+
.action(async (id, options) => {
|
|
131
|
+
try {
|
|
132
|
+
await attachJob(id, options);
|
|
133
|
+
} catch (err) {
|
|
134
|
+
logger.error(err.message);
|
|
135
|
+
process.exitCode = 1;
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
cloud
|
|
140
|
+
.command("list", { isDefault: true })
|
|
141
|
+
.description("List local cloud jobs")
|
|
142
|
+
.option("--json", "Output as JSON")
|
|
143
|
+
.action(async (options) => {
|
|
144
|
+
try {
|
|
145
|
+
const { listJobs } = await import("../lib/cloud/cloud-client.js");
|
|
146
|
+
const jobs = listJobs();
|
|
147
|
+
if (options.json) return console.log(JSON.stringify(jobs, null, 2));
|
|
148
|
+
if (jobs.length === 0) {
|
|
149
|
+
logger.log(
|
|
150
|
+
chalk.gray("No cloud jobs. Submit one with `cc cloud run`."),
|
|
151
|
+
);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
for (const j of jobs) {
|
|
155
|
+
logger.log(
|
|
156
|
+
`${statusColor(j.status)((j.status || "?").padEnd(8))} ${j.jobId} ${chalk.gray(j.branch)} ${j.task}`,
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
} catch (err) {
|
|
160
|
+
logger.error(err.message);
|
|
161
|
+
process.exitCode = 1;
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
async function attachJob(id, options = {}) {
|
|
167
|
+
const { CloudClient, readJob, saveJob, pollUntilDone } =
|
|
168
|
+
await import("../lib/cloud/cloud-client.js");
|
|
169
|
+
const { applyResultPatch, persistResultArtifacts } =
|
|
170
|
+
await import("../lib/cloud/bundle.js");
|
|
171
|
+
const { token } = await resolveRunner();
|
|
172
|
+
|
|
173
|
+
const job = readJob(id);
|
|
174
|
+
if (!job) throw new Error(`job not found: ${id}`);
|
|
175
|
+
const client = new CloudClient({ baseUrl: job.baseUrl, token });
|
|
176
|
+
|
|
177
|
+
logger.log(chalk.gray(`Waiting for ${id}…`));
|
|
178
|
+
const final = await pollUntilDone(client, id, {
|
|
179
|
+
onTick: (s) =>
|
|
180
|
+
logger.log(
|
|
181
|
+
chalk.gray(` ${s.status}${s.summary ? `: ${s.summary}` : ""}`),
|
|
182
|
+
),
|
|
183
|
+
});
|
|
184
|
+
saveJob({ ...job, status: final.status, lastSummary: final.summary });
|
|
185
|
+
|
|
186
|
+
if (final.status !== "done") {
|
|
187
|
+
logger.error(`Job ${id} ended ${final.status}: ${final.summary || ""}`);
|
|
188
|
+
process.exitCode = 1;
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const result = await client.result(id);
|
|
193
|
+
|
|
194
|
+
// Persist plan/artifacts under the job's local dir.
|
|
195
|
+
const { cloudJobsDir } = await import("../lib/cloud/cloud-client.js");
|
|
196
|
+
const destDir = join(cloudJobsDir(), `${id}-result`);
|
|
197
|
+
if (!existsSync(destDir)) mkdirSync(destDir, { recursive: true });
|
|
198
|
+
const written = persistResultArtifacts(result, destDir);
|
|
199
|
+
|
|
200
|
+
let applied = null;
|
|
201
|
+
if (result.patch && options.apply !== false) {
|
|
202
|
+
applied = applyResultPatch(result.patch, job.cwd || process.cwd());
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (options.json) {
|
|
206
|
+
return console.log(
|
|
207
|
+
JSON.stringify(
|
|
208
|
+
{ jobId: id, status: final.status, applied, written, pr: result.pr },
|
|
209
|
+
null,
|
|
210
|
+
2,
|
|
211
|
+
),
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
logger.success(`Job ${id} done`);
|
|
215
|
+
if (result.pr)
|
|
216
|
+
logger.log(chalk.magenta(` PR: ${result.pr.url || result.pr}`));
|
|
217
|
+
if (applied) {
|
|
218
|
+
logger.log(
|
|
219
|
+
applied.applied
|
|
220
|
+
? chalk.green(` patch: ${applied.reason}`)
|
|
221
|
+
: chalk.yellow(` patch: ${applied.reason} (saved under ${destDir})`),
|
|
222
|
+
);
|
|
223
|
+
if (!applied.applied) {
|
|
224
|
+
// Save the unapplied patch so the user can resolve it by hand.
|
|
225
|
+
persistResultArtifacts(
|
|
226
|
+
{ artifacts: [{ name: "result.patch", content: result.patch }] },
|
|
227
|
+
destDir,
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
for (const w of written) logger.log(chalk.gray(` wrote ${w}`));
|
|
232
|
+
}
|
package/src/commands/doctor.js
CHANGED
|
@@ -1,18 +1,40 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
import logger from "../lib/logger.js";
|
|
3
3
|
import { collectDoctorReport } from "../runtime/diagnostics.js";
|
|
4
|
+
import {
|
|
5
|
+
collectCheckupSections,
|
|
6
|
+
runCheckupFixes,
|
|
7
|
+
unsafeFixCommands,
|
|
8
|
+
} from "../lib/doctor-checkup.js";
|
|
9
|
+
|
|
10
|
+
const LEVEL_ICON = {
|
|
11
|
+
ok: () => chalk.green("✔"),
|
|
12
|
+
warn: () => chalk.yellow("⚠"),
|
|
13
|
+
err: () => chalk.red("✖"),
|
|
14
|
+
info: () => chalk.gray("·"),
|
|
15
|
+
};
|
|
4
16
|
|
|
5
17
|
export function registerDoctorCommand(program) {
|
|
6
18
|
program
|
|
7
19
|
.command("doctor")
|
|
8
|
-
.
|
|
20
|
+
.alias("checkup")
|
|
21
|
+
.description("Diagnose your ChainlessChain environment (alias: checkup)")
|
|
9
22
|
.option("--json", "Output as machine-readable JSON")
|
|
23
|
+
.option("--fix", "Apply safe fixes (stale job files, git worktree prune)")
|
|
10
24
|
.action(async (options) => {
|
|
11
25
|
const report = await collectDoctorReport();
|
|
26
|
+
const sections = await collectCheckupSections({ cwd: process.cwd() });
|
|
27
|
+
const fixResults = options.fix ? await runCheckupFixes(sections) : null;
|
|
28
|
+
|
|
29
|
+
const checkupErrs = sections
|
|
30
|
+
.flatMap((s) => s.checks)
|
|
31
|
+
.filter((c) => c.level === "err").length;
|
|
12
32
|
|
|
13
33
|
if (options.json) {
|
|
14
|
-
console.log(
|
|
15
|
-
|
|
34
|
+
console.log(
|
|
35
|
+
JSON.stringify({ ...report, checkup: sections, fixResults }, null, 2),
|
|
36
|
+
);
|
|
37
|
+
if (report.summary.criticalFailed > 0 || checkupErrs > 0) {
|
|
16
38
|
process.exitCode = 1;
|
|
17
39
|
}
|
|
18
40
|
return;
|
|
@@ -41,6 +63,17 @@ export function registerDoctorCommand(program) {
|
|
|
41
63
|
logger.log(` ${icon} ${check.name}${detail}`);
|
|
42
64
|
}
|
|
43
65
|
|
|
66
|
+
// Layered checkup sections (config chain / provider / MCP / IDE /
|
|
67
|
+
// plugins / subagents / transcripts / background agents / worktrees).
|
|
68
|
+
for (const section of sections) {
|
|
69
|
+
logger.log(chalk.bold(`\n ${section.title}\n`));
|
|
70
|
+
for (const c of section.checks) {
|
|
71
|
+
const icon = (LEVEL_ICON[c.level] || LEVEL_ICON.info)();
|
|
72
|
+
const detail = c.detail ? chalk.gray(` — ${c.detail}`) : "";
|
|
73
|
+
logger.log(` ${icon} ${c.name}${detail}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
44
77
|
logger.log(chalk.bold("\n Port Status\n"));
|
|
45
78
|
for (const p of report.ports) {
|
|
46
79
|
const icon = p.open ? chalk.green("●") : chalk.gray("○");
|
|
@@ -54,13 +87,35 @@ export function registerDoctorCommand(program) {
|
|
|
54
87
|
logger.log(` ${icon} Free space: ${report.disk.freeGB.toFixed(1)} GB`);
|
|
55
88
|
}
|
|
56
89
|
|
|
90
|
+
// Applied fixes (--fix) and copyable commands for the rest.
|
|
91
|
+
if (fixResults && fixResults.length > 0) {
|
|
92
|
+
logger.log(chalk.bold("\n Fixes applied\n"));
|
|
93
|
+
for (const f of fixResults) {
|
|
94
|
+
const icon = f.applied ? chalk.green("✔") : chalk.red("✖");
|
|
95
|
+
logger.log(` ${icon} ${f.id}: ${f.detail}`);
|
|
96
|
+
}
|
|
97
|
+
} else if (options.fix) {
|
|
98
|
+
logger.log(chalk.gray("\n No safe fixes needed.\n"));
|
|
99
|
+
}
|
|
100
|
+
const manual = unsafeFixCommands(sections);
|
|
101
|
+
if (manual.length > 0) {
|
|
102
|
+
logger.log(
|
|
103
|
+
chalk.bold("\n Suggested commands (need your judgement)\n"),
|
|
104
|
+
);
|
|
105
|
+
for (const m of manual) {
|
|
106
|
+
logger.log(` ${chalk.gray("#")} ${m.description}`);
|
|
107
|
+
logger.log(` ${chalk.cyan(m.command)}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
57
111
|
logger.newline();
|
|
58
|
-
|
|
112
|
+
const totalIssues = report.summary.criticalFailed + checkupErrs;
|
|
113
|
+
if (report.summary.failed === 0 && checkupErrs === 0) {
|
|
59
114
|
logger.log(chalk.bold.green(" All checks passed!\n"));
|
|
60
|
-
} else if (
|
|
115
|
+
} else if (totalIssues > 0) {
|
|
61
116
|
logger.log(
|
|
62
117
|
chalk.bold.red(
|
|
63
|
-
` ${
|
|
118
|
+
` ${totalIssues} issue(s) found. See details above.\n`,
|
|
64
119
|
),
|
|
65
120
|
);
|
|
66
121
|
process.exitCode = 1;
|
package/src/commands/mcp.js
CHANGED
|
@@ -342,6 +342,67 @@ export function registerMcpCommand(program) {
|
|
|
342
342
|
}
|
|
343
343
|
});
|
|
344
344
|
|
|
345
|
+
// mcp computer-use — expose GUI control verbs as an MCP server (opt-in)
|
|
346
|
+
mcp
|
|
347
|
+
.command("computer-use")
|
|
348
|
+
.description(
|
|
349
|
+
"Expose GUI control (screenshot/window-list/click/type/scroll/clipboard/app-launch) as a Streamable-HTTP MCP server. OFF by default; input verbs need confirm:true; high-risk apps (terminals/IDEs/settings/password managers) always confirm",
|
|
350
|
+
)
|
|
351
|
+
.option("--port <n>", "Port to listen on (default: random free port)", "0")
|
|
352
|
+
.option(
|
|
353
|
+
"--capabilities <list>",
|
|
354
|
+
"Comma-separated subset (default: all): screenshot,window_list,app_launch,click,type,scroll,clipboard_read,clipboard_write",
|
|
355
|
+
)
|
|
356
|
+
.option(
|
|
357
|
+
"--allow-apps <list>",
|
|
358
|
+
"Comma-separated app allowlist for input/launch verbs (default: any app)",
|
|
359
|
+
)
|
|
360
|
+
.option(
|
|
361
|
+
"--confirm-all",
|
|
362
|
+
"Require confirm:true for every verb, not just input verbs",
|
|
363
|
+
)
|
|
364
|
+
.option("--token <token>", "Fixed Bearer token (default: random)")
|
|
365
|
+
.option("--no-auth", "Disable Bearer auth (server binds 127.0.0.1 only)")
|
|
366
|
+
.action(async (options) => {
|
|
367
|
+
try {
|
|
368
|
+
const { startComputerUseServer } =
|
|
369
|
+
await import("../lib/computer-use/computer-use-server.js");
|
|
370
|
+
const handle = await startComputerUseServer({
|
|
371
|
+
port: Number(options.port) || 0,
|
|
372
|
+
token: options.auth === false ? false : options.token || null,
|
|
373
|
+
config: {
|
|
374
|
+
capabilities: options.capabilities
|
|
375
|
+
? options.capabilities.split(",").map((s) => s.trim())
|
|
376
|
+
: undefined,
|
|
377
|
+
appAllowlist: options.allowApps
|
|
378
|
+
? options.allowApps.split(",").map((s) => s.trim())
|
|
379
|
+
: undefined,
|
|
380
|
+
confirmAll: options.confirmAll === true,
|
|
381
|
+
},
|
|
382
|
+
});
|
|
383
|
+
logger.log(
|
|
384
|
+
chalk.bold("Computer-use MCP server ready (Streamable-HTTP)"),
|
|
385
|
+
);
|
|
386
|
+
logger.log(` URL: ${chalk.cyan(handle.url)}`);
|
|
387
|
+
logger.log(` Capabilities: ${handle.capabilities.join(", ")}`);
|
|
388
|
+
if (handle.token) {
|
|
389
|
+
logger.log(` Auth: Bearer ${handle.token}`);
|
|
390
|
+
} else {
|
|
391
|
+
logger.log(chalk.yellow(" Auth: disabled (--no-auth)"));
|
|
392
|
+
}
|
|
393
|
+
logger.log(
|
|
394
|
+
chalk.yellow(
|
|
395
|
+
" ⚠ GUI control can act on anything on screen. Input verbs need confirm:true; high-risk apps always confirm.",
|
|
396
|
+
),
|
|
397
|
+
);
|
|
398
|
+
logger.log(chalk.gray("\nCtrl+C to stop."));
|
|
399
|
+
await new Promise(() => {});
|
|
400
|
+
} catch (err) {
|
|
401
|
+
logger.error(chalk.red(`mcp computer-use failed: ${err.message}`));
|
|
402
|
+
process.exitCode = 1;
|
|
403
|
+
}
|
|
404
|
+
});
|
|
405
|
+
|
|
345
406
|
// mcp servers — list configured servers
|
|
346
407
|
mcp
|
|
347
408
|
.command("servers")
|