chainlesschain 0.162.78 → 0.162.80
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/README.md +37 -1
- package/bin/chainlesschain.js +20 -1
- package/package.json +1 -1
- package/src/assets/web-panel/assets/{AIOps-BhKMd38k.js → AIOps-CwebRiRI.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-BzhKY5C_.js → ActionButton-C7h2xsW3.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-CATIz2Jc.js → Analytics-BRdOQzzK.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-eCx64YWg.js → AppLayout-D_i-Jbsu.js} +5 -5
- package/src/assets/web-panel/assets/{Audit-CGOHfCHj.js → Audit-AgCF_nLK.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-Dyr6R0Ra.js → Backup-Be9up1Uo.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-CVhBu7NZ.js → BaseInput-2j-7gyTU.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-DOCCKp2k.js → Chat-ZyYadHdk.js} +6 -6
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-DcXCCnjN.js → ChatBubbleRenderer-CwnbmcAg.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-3yjnENud.js → Checkbox-Di0bejSO.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-D06sn8kB.js → Codegen-BgEwqrVh.js} +1 -1
- package/src/assets/web-panel/assets/{Col-Bjn5vFES.js → Col-FIduoerd.js} +1 -1
- package/src/assets/web-panel/assets/{Community-BYHpQHmf.js → Community-Bf7olKXg.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-MKRmnUDQ.js → Compact-CsjIF6B3.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-CVtPe8dh.js → Compliance-CQchRaQh.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-BRu5M3dv.js → Cowork-gJzDgX9E.js} +2 -2
- package/src/assets/web-panel/assets/{Cron-D_7eU5Ut.js → Cron-CDjeagXb.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-BFyVp_e9.js → Crosschain-D4UE5bt0.js} +1 -1
- package/src/assets/web-panel/assets/{DID-DRoG7638.js → DID-OwvsxTzD.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-NfM_v-Oe.js → Dashboard-CbmOlZ1l.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-CGN1Ksu0.js → Dropdown-B6tBUMev.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-DhsY_z_a.js → EmailListRenderer-DnM2-O3n.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-vW_VsKUG.js → FamilyGuardDashboard-DPD9znJH.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-D2ob_c7h.js → Federation-DjUj87Vr.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-Cpem15Pr.js → FormItemContext-DyPgrKLf.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-P6XfmXwT.js → GenericCardRenderer-D3fmbO1W.js} +1 -1
- package/src/assets/web-panel/assets/{Git-CfSeec1R.js → Git-IvwC_R2h.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-4ipIpqNl.js → Governance-CTVEvxpW.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-CiQY_P9Y.js → Inference-DQlp7Rf1.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-B3Qem78R.js → KnowledgeGraph-Cg8pVh5j.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-De2zWVy6.js → Logs-D7kLXyaK.js} +2 -2
- package/src/assets/web-panel/assets/{Marketplace-CTNu4c1A.js → Marketplace-QOj6g-oT.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-Xx25EA2f.js → McpTools-PO9yrTD6.js} +5 -5
- package/src/assets/web-panel/assets/{Memory-YzzCCrch.js → Memory-B1FwSFmt.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-CJZY98f0.js → MobileBridge-CYiQSoKu.js} +3 -3
- package/src/assets/web-panel/assets/{MobileProjects-Dw7yl4KN.js → MobileProjects-BThUga4r.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-D7TzGJhH.js → Mtc-B4pMzmr7.js} +4 -4
- package/src/assets/web-panel/assets/{MtcAudit-THyGhf0h.js → MtcAudit-D6-KGsvR.js} +6 -6
- package/src/assets/web-panel/assets/{Multisig-RVxuGPUR.js → Multisig-BlZigJMj.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-DK7TCzKF.js → NLProgramming-Dhy92OLw.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-BaOU0psj.js → Notes-DOSv2Lb0.js} +4 -4
- package/src/assets/web-panel/assets/{NotificationSettings-BMivJy85.js → NotificationSettings-FRWsHQfi.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-BZOiY8Yw.js → OrderTableRenderer-xRSRWRZg.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-zVRtW1n_.js → Organization-DApwYY-B.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-C0YLldFH.js → Overflow-DyJQHxIY.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-Xxgzghqp.js → P2P-NKTPd_Bn.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-DH_LO13b.js → PdhVaultBrowser-DCgGt1BE.js} +5 -5
- package/src/assets/web-panel/assets/{Permissions-CvAd1VBw.js → Permissions-BX9MCb2z.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-CWQGgCAK.js → PersonalDataHub-Bcg4az84.js} +3 -3
- package/src/assets/web-panel/assets/{Pipeline-BNAoh-Lb.js → Pipeline-DjsbfwbG.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-CNO5pFq-.js → Privacy-kDWl06vo.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-WaVVDsm3.js → ProjectInit-yCe-Imkv.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-D1WfkuJ3.js → ProjectSettings-tVcxlGJ5.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-BzjvJYMW.js → Projects-DGL4Za4o.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-IOOJ4_wy.js → Providers-CciGxskW.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-ChHZqVZy.js → QuickAsk-It17rT4F.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-CSiW6Qv9.js → Recommend-CixKdpBl.js} +1 -1
- package/src/assets/web-panel/assets/{Reputation-BQe0rkfF.js → Reputation-LB0_D2lx.js} +1 -1
- package/src/assets/web-panel/assets/{Row-Dem0Wxxb.js → Row-BoiDd-zb.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-pBY5G41C.js → RssFeed-yA5FdTgh.js} +2 -2
- package/src/assets/web-panel/assets/{Search-CtRepO6B.js → Search-xYldUFeJ.js} +1 -1
- package/src/assets/web-panel/assets/{Security-nrSlKpWq.js → Security-BtRMnkFm.js} +4 -4
- package/src/assets/web-panel/assets/{Services-DeaDBASi.js → Services-CNKTgE2v.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-Cz9R-Wjb.js → Skeleton-B9goiUo_.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-B3U-XLH3.js → Skills-CUf2Z5Ge.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-Bu46dIA_.js → Sla-TPga8c2I.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-C9Z0V0pk.js → SpeechSettings-B8zrmLP6.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-Ctj9KHHr.js → SyncSettings-DGyAbZ84.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-D4upQgR_.js → Tasks-DPeVrQNM.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-JHsPGU_c.js → Templates-CNLco6pc.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-uoaQL3fB.js → Tenant-CL9Eczo8.js} +1 -1
- package/src/assets/web-panel/assets/Terminal-DGLvbp97.js +3 -0
- package/src/assets/web-panel/assets/{TimelineRenderer-BTicmSAV.js → TimelineRenderer-Cv0LxMwd.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-Bp3BUe2K.js → Tokens-DLhHgtcS.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-CgoISw5d.js → Trigger-DzDaE-An.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-CC29awNT.js → Trust-CRh-fhYe.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-CB1SB6Nc.js → UkeySign-_xBJ16UC.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-D7vptDUg.js → VideoEditing-Z5m_edIa.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-BWfjzF7p.js → Wallet-DgmchNit.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-Dzz5OnPc.js → WebAuthn-BxuKxjuf.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-CiDeVmsG.js → WorkflowEditor-luJ180aM.js} +1 -1
- package/src/assets/web-panel/assets/{chat-DQbciNb5.js → chat-DzglnTps.js} +1 -1
- package/src/assets/web-panel/assets/{colors-DcLbPJzb.js → colors-DelLNoxZ.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-CvYrR3rc.js → compact-item-BZaabUge.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-BR4P7Rgm.js → createContext-DqTSTjmk.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-CJLMPKYL.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-IQ88RNRJ.js → hasIn-CAeHUQj2.js} +1 -1
- package/src/assets/web-panel/assets/index-7CJalvEf.js +1 -0
- package/src/assets/web-panel/assets/{index-Bw0Dm_P6.js → index-7FxBHcH8.js} +1 -1
- package/src/assets/web-panel/assets/{index-Db5LFFCN.js → index-BAcpfWwI.js} +1 -1
- package/src/assets/web-panel/assets/{index-B5W1vQHV.js → index-BAfdWN9t.js} +1 -1
- package/src/assets/web-panel/assets/{index-BUTN1VlO.js → index-BR-DF81e.js} +3 -3
- package/src/assets/web-panel/assets/{index-BmPuR0aA.js → index-BTbN0V4A.js} +1 -1
- package/src/assets/web-panel/assets/{index-D8OJdOc_.js → index-Bv2Tp7kz.js} +1 -1
- package/src/assets/web-panel/assets/{index-BhkZZXtI.js → index-BzLgm3Jm.js} +1 -1
- package/src/assets/web-panel/assets/{index-CzERBV9P.js → index-CBZPDGTg.js} +1 -1
- package/src/assets/web-panel/assets/{index-UbB2IcFR.js → index-CBtnHlYF.js} +1 -1
- package/src/assets/web-panel/assets/{index-eKd1n8pw.js → index-CDw1am9U.js} +1 -1
- package/src/assets/web-panel/assets/{index-JqOP7puJ.js → index-CI5cynRw.js} +1 -1
- package/src/assets/web-panel/assets/{index-BiMlLIZ-.js → index-CKZQVcH1.js} +1 -1
- package/src/assets/web-panel/assets/{index-CgP5aQmA.js → index-CV4FisuU.js} +1 -1
- package/src/assets/web-panel/assets/{index-BH2RT15D.js → index-CaSLz8-6.js} +1 -1
- package/src/assets/web-panel/assets/{index-DgaCUxpi.js → index-Cj7oeTxA.js} +1 -1
- package/src/assets/web-panel/assets/{index-DdQBxvpt.js → index-Clq1OP4B.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bl1TSbTE.js → index-CppTZ4SW.js} +1 -1
- package/src/assets/web-panel/assets/{index-BQXs-5db.js → index-D-QuIaEh.js} +1 -1
- package/src/assets/web-panel/assets/{index-DGwa8mnJ.js → index-D-lVDXUg.js} +1 -1
- package/src/assets/web-panel/assets/{index-DHIp5msb.js → index-DE4-6oHW.js} +1 -1
- package/src/assets/web-panel/assets/{index-CCO8yc1h.js → index-DM7xncnU.js} +1 -1
- package/src/assets/web-panel/assets/{index-b6FjzfoJ.js → index-DRt2lx0X.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bo7HAK6G.js → index-DSATjRyg.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dpmnk2qv.js → index-DU9QWJO5.js} +1 -1
- package/src/assets/web-panel/assets/{index-Mn8_ryOe.js → index-DXvcxNo5.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dox9vEhP.js → index-DhMSUhbW.js} +1 -1
- package/src/assets/web-panel/assets/{index-bRT7u-51.js → index-Dk1R9vFq.js} +1 -1
- package/src/assets/web-panel/assets/{index-BHxJnExB.js → index-DrSuq6t6.js} +1 -1
- package/src/assets/web-panel/assets/{index-CPOupQSX.js → index-DtfTElxo.js} +1 -1
- package/src/assets/web-panel/assets/{index-BxY0ozve.js → index-KeadEGaZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-BvQpTO67.js → index-NZBXGj64.js} +1 -1
- package/src/assets/web-panel/assets/{index-CiOZ_Whh.js → index-OGKhEFZZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-CeCWyiFl.js → index-RZ23Wlp8.js} +1 -1
- package/src/assets/web-panel/assets/{index-BVb6RI7f.js → index-WzAdJ0PX.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cm74AosZ.js → index-Xo2WWPZ4.js} +1 -1
- package/src/assets/web-panel/assets/index-_hLbeSOT.js +1 -0
- package/src/assets/web-panel/assets/{index-CE2mqX8w.js → index-kJ30C4m8.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bu8931Yi.js → index-vLR-ssxc.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-C0arzCLE.js → initDefaultProps-BiHvIjo1.js} +1 -1
- package/src/assets/web-panel/assets/{motion-C1K6JxwD.js → motion-COD0OBOe.js} +1 -1
- package/src/assets/web-panel/assets/{move-DREsRLHj.js → move-DJNLMhIj.js} +1 -1
- package/src/assets/web-panel/assets/{omit-BtPS3EDq.js → omit-4qrDRhlN.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-BPz6tHoT.js → pickAttrs-3jv8tAgW.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-B0CR_CSI.js → placementArrow-N1UVUOH_.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-Ch2ojiNn.js → responsiveObserve-D67_gjCH.js} +1 -1
- package/src/assets/web-panel/assets/{slide-9qU9vOhj.js → slide-DiDh7_u4.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-Cr4fICjV.js → statusUtils-Dzz3tSiz.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-Cor2-FwV.js → styleChecker-L-tgt7xx.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-BINo_rNH.js → useFlexGapSupport-vAgElNal.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-Dm1tDNYC.js → useFs-af0c_HYI.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-__JgBEkX.js → usePersonalDataHub-V9U2Mbny.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-1hQKpRgP.js → vnode-C7zS_LLr.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-C1EY9X2J.js → zoom-DdXBDemd.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/audit.js +4 -3
- package/src/commands/automation.js +6 -14
- package/src/commands/bi.js +10 -9
- package/src/commands/codegen.js +5 -13
- package/src/commands/dao.js +8 -6
- package/src/commands/dbevo.js +13 -14
- package/src/commands/economy.js +3 -2
- package/src/commands/evolution.js +3 -2
- package/src/commands/federation.js +4 -3
- package/src/commands/governance.js +9 -4
- package/src/commands/hardening.js +5 -4
- package/src/commands/incentive.js +6 -5
- package/src/commands/kg.js +17 -10
- package/src/commands/lowcode.js +23 -11
- package/src/commands/marketplace.js +4 -3
- package/src/commands/mcp.js +17 -5
- package/src/commands/ops.js +9 -4
- package/src/commands/recommend.js +7 -5
- package/src/commands/scim.js +3 -2
- package/src/commands/session.js +9 -6
- package/src/commands/social.js +4 -3
- package/src/commands/sync.js +3 -2
- package/src/commands/tenant.js +11 -6
- package/src/commands/zkp.js +8 -9
- package/src/gateways/ws/ws-agent-handler.js +12 -3
- package/src/gateways/ws/ws-server.js +6 -0
- package/src/harness/background-task-manager.js +44 -18
- package/src/harness/mcp-client.js +125 -46
- package/src/lib/chat-core.js +209 -107
- package/src/lib/claude-code-bridge.js +13 -1
- package/src/lib/dao-governance.js +3 -3
- package/src/lib/downloader.js +82 -25
- package/src/lib/headless-config-command.js +62 -0
- package/src/lib/json-schema-output.js +55 -11
- package/src/lib/mcp-oauth.js +110 -21
- package/src/lib/mcp-serve.js +70 -11
- package/src/lib/multisig-runtime.js +22 -3
- package/src/lib/parse-json-option.js +35 -0
- package/src/lib/parse-number-option.js +27 -0
- package/src/lib/runnable-provider.js +216 -0
- package/src/repl/agent-repl.js +76 -17
- package/src/repl/config-summary.js +66 -0
- package/src/runtime/agent-core.js +210 -37
- package/src/runtime/headless-runner.js +49 -1
- package/src/runtime/headless-stream.js +34 -0
- package/src/assets/web-panel/assets/Terminal-CWRWr8bq.js +0 -3
- package/src/assets/web-panel/assets/devWarning-CnV02N63.js +0 -1
- package/src/assets/web-panel/assets/index-DJ2gkaIH.js +0 -1
- package/src/assets/web-panel/assets/index-Dvm_-AOi.js +0 -1
|
@@ -1207,6 +1207,37 @@ export async function executeTool(name, args, context = {}) {
|
|
|
1207
1207
|
return toolResult;
|
|
1208
1208
|
}
|
|
1209
1209
|
|
|
1210
|
+
/**
|
|
1211
|
+
* Write a file then verify the on-disk byte count matches the intended
|
|
1212
|
+
* content. Network drives and cloud-synced folders (OneDrive / Dropbox /
|
|
1213
|
+
* Google Drive) can silently truncate a write or leave a 0-byte file; without
|
|
1214
|
+
* this check the agent reports `success` on a corrupted write and moves on.
|
|
1215
|
+
* Parity with Claude-Code 2.1.181 ("Fixed Write/Edit producing 0-byte or
|
|
1216
|
+
* truncated files on network drives and cloud-synced folders").
|
|
1217
|
+
*
|
|
1218
|
+
* Returns `{ size }` (actual on-disk bytes) on success, or `{ error }`
|
|
1219
|
+
* describing the truncation so the caller surfaces it as a tool error instead
|
|
1220
|
+
* of a false success. `fsImpl` is injectable for unit tests.
|
|
1221
|
+
*/
|
|
1222
|
+
export function writeFileVerified(filePath, content, fsImpl = fs) {
|
|
1223
|
+
const expected = Buffer.byteLength(content, "utf8");
|
|
1224
|
+
fsImpl.writeFileSync(filePath, content, "utf8");
|
|
1225
|
+
let actual;
|
|
1226
|
+
try {
|
|
1227
|
+
actual = fsImpl.statSync(filePath).size;
|
|
1228
|
+
} catch (err) {
|
|
1229
|
+
return {
|
|
1230
|
+
error: `Write verification failed: cannot stat ${filePath} after writing (${err.message}). The file may be on an unreliable network or cloud-synced drive.`,
|
|
1231
|
+
};
|
|
1232
|
+
}
|
|
1233
|
+
if (actual !== expected) {
|
|
1234
|
+
return {
|
|
1235
|
+
error: `Write truncated: expected ${expected} bytes but only ${actual} reached disk for ${filePath}. A network drive or cloud-sync folder (OneDrive/Dropbox/Google Drive) may have interrupted the write — retry, or write to a local path.`,
|
|
1236
|
+
};
|
|
1237
|
+
}
|
|
1238
|
+
return { size: actual };
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1210
1241
|
/**
|
|
1211
1242
|
* Inner tool execution — no hooks, no plan-mode checks.
|
|
1212
1243
|
*/
|
|
@@ -1297,11 +1328,12 @@ async function executeToolInner(
|
|
|
1297
1328
|
if (!fs.existsSync(dir)) {
|
|
1298
1329
|
fs.mkdirSync(dir, { recursive: true });
|
|
1299
1330
|
}
|
|
1300
|
-
|
|
1331
|
+
const wrote = writeFileVerified(filePath, args.content);
|
|
1332
|
+
if (wrote.error) return attachDescriptor({ error: wrote.error });
|
|
1301
1333
|
return attachDescriptor({
|
|
1302
1334
|
success: true,
|
|
1303
1335
|
path: filePath,
|
|
1304
|
-
size:
|
|
1336
|
+
size: wrote.size,
|
|
1305
1337
|
});
|
|
1306
1338
|
}
|
|
1307
1339
|
|
|
@@ -1315,8 +1347,13 @@ async function executeToolInner(
|
|
|
1315
1347
|
return attachDescriptor({ error: "old_string not found in file" });
|
|
1316
1348
|
}
|
|
1317
1349
|
const newContent = content.replace(args.old_string, args.new_string);
|
|
1318
|
-
|
|
1319
|
-
return attachDescriptor({
|
|
1350
|
+
const wrote = writeFileVerified(filePath, newContent);
|
|
1351
|
+
if (wrote.error) return attachDescriptor({ error: wrote.error });
|
|
1352
|
+
return attachDescriptor({
|
|
1353
|
+
success: true,
|
|
1354
|
+
path: filePath,
|
|
1355
|
+
size: wrote.size,
|
|
1356
|
+
});
|
|
1320
1357
|
}
|
|
1321
1358
|
|
|
1322
1359
|
case "edit_file_hashed": {
|
|
@@ -1358,10 +1395,12 @@ async function executeToolInner(
|
|
|
1358
1395
|
...(snippet && { current_snippet: snippet }),
|
|
1359
1396
|
});
|
|
1360
1397
|
}
|
|
1361
|
-
|
|
1398
|
+
const wrote = writeFileVerified(filePath, result.content);
|
|
1399
|
+
if (wrote.error) return attachDescriptor({ error: wrote.error });
|
|
1362
1400
|
return attachDescriptor({
|
|
1363
1401
|
success: true,
|
|
1364
1402
|
path: filePath,
|
|
1403
|
+
size: wrote.size,
|
|
1365
1404
|
lineNumber: result.lineNumber,
|
|
1366
1405
|
previousContent: result.previousContent,
|
|
1367
1406
|
});
|
|
@@ -2566,11 +2605,15 @@ export async function chatWithTools(rawMessages, options) {
|
|
|
2566
2605
|
// same {message, usage} shape is returned, so the agent loop is unchanged.
|
|
2567
2606
|
// Without onToken we keep the cheaper single-shot non-streaming request.
|
|
2568
2607
|
if (typeof options.onToken === "function") {
|
|
2569
|
-
return await
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2608
|
+
return await _retryStreamingChat(
|
|
2609
|
+
() =>
|
|
2610
|
+
_chatOllamaStreaming(
|
|
2611
|
+
apiUrl,
|
|
2612
|
+
{ model, messages: ollamaMessages, tools },
|
|
2613
|
+
options.onToken,
|
|
2614
|
+
signal,
|
|
2615
|
+
),
|
|
2616
|
+
{ signal, onRetry: options.onStreamRetry },
|
|
2574
2617
|
);
|
|
2575
2618
|
}
|
|
2576
2619
|
const response = await fetch(apiUrl, {
|
|
@@ -2585,7 +2628,7 @@ export async function chatWithTools(rawMessages, options) {
|
|
|
2585
2628
|
}),
|
|
2586
2629
|
});
|
|
2587
2630
|
if (!response.ok) {
|
|
2588
|
-
throw new Error(
|
|
2631
|
+
throw new Error(formatProviderHttpError("ollama", response.status));
|
|
2589
2632
|
}
|
|
2590
2633
|
const data = await response.json();
|
|
2591
2634
|
if (data.prompt_eval_count || data.eval_count) {
|
|
@@ -2652,13 +2695,17 @@ export async function chatWithTools(rawMessages, options) {
|
|
|
2652
2695
|
// and forward text deltas live, assembling tool_use blocks back into the
|
|
2653
2696
|
// same {message, usage} shape the non-streaming path returns.
|
|
2654
2697
|
if (typeof options.onToken === "function") {
|
|
2655
|
-
return await
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2698
|
+
return await _retryStreamingChat(
|
|
2699
|
+
() =>
|
|
2700
|
+
_chatAnthropicStreaming(
|
|
2701
|
+
`${url}/messages`,
|
|
2702
|
+
{ ...body, stream: true },
|
|
2703
|
+
{ "x-api-key": key, "anthropic-version": "2023-06-01" },
|
|
2704
|
+
options.onToken,
|
|
2705
|
+
signal,
|
|
2706
|
+
options.onThinking,
|
|
2707
|
+
),
|
|
2708
|
+
{ signal, onRetry: options.onStreamRetry },
|
|
2662
2709
|
);
|
|
2663
2710
|
}
|
|
2664
2711
|
|
|
@@ -2674,7 +2721,7 @@ export async function chatWithTools(rawMessages, options) {
|
|
|
2674
2721
|
});
|
|
2675
2722
|
|
|
2676
2723
|
if (!response.ok) {
|
|
2677
|
-
throw new Error(
|
|
2724
|
+
throw new Error(formatProviderHttpError("anthropic", response.status));
|
|
2678
2725
|
}
|
|
2679
2726
|
|
|
2680
2727
|
const data = await response.json();
|
|
@@ -2736,19 +2783,23 @@ export async function chatWithTools(rawMessages, options) {
|
|
|
2736
2783
|
// stream the SSE response, forward content deltas live, and reassemble the
|
|
2737
2784
|
// delta-fragmented tool_calls into the standard {message, usage} shape.
|
|
2738
2785
|
if (typeof options.onToken === "function") {
|
|
2739
|
-
return await
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2786
|
+
return await _retryStreamingChat(
|
|
2787
|
+
() =>
|
|
2788
|
+
_chatOpenAIStreaming(
|
|
2789
|
+
`${url}/chat/completions`,
|
|
2790
|
+
{
|
|
2791
|
+
model: model || defaultModels[provider] || "gpt-4o-mini",
|
|
2792
|
+
messages,
|
|
2793
|
+
tools,
|
|
2794
|
+
stream: true,
|
|
2795
|
+
stream_options: { include_usage: true },
|
|
2796
|
+
},
|
|
2797
|
+
key,
|
|
2798
|
+
options.onToken,
|
|
2799
|
+
signal,
|
|
2800
|
+
provider,
|
|
2801
|
+
),
|
|
2802
|
+
{ signal, onRetry: options.onStreamRetry },
|
|
2752
2803
|
);
|
|
2753
2804
|
}
|
|
2754
2805
|
|
|
@@ -2767,7 +2818,7 @@ export async function chatWithTools(rawMessages, options) {
|
|
|
2767
2818
|
});
|
|
2768
2819
|
|
|
2769
2820
|
if (!response.ok) {
|
|
2770
|
-
throw new Error(
|
|
2821
|
+
throw new Error(formatProviderHttpError(provider, response.status));
|
|
2771
2822
|
}
|
|
2772
2823
|
|
|
2773
2824
|
const data = await response.json();
|
|
@@ -2880,6 +2931,108 @@ export function _streamErrorDisposition(err, signal, partialText) {
|
|
|
2880
2931
|
return "rethrow";
|
|
2881
2932
|
}
|
|
2882
2933
|
|
|
2934
|
+
/**
|
|
2935
|
+
* Format a provider HTTP error with an actionable hint. 401/403 almost always
|
|
2936
|
+
* means a missing/invalid API key for the ACTIVE provider — and because the
|
|
2937
|
+
* provider is resolved from config, a surprise "anthropic 401" usually means
|
|
2938
|
+
* the effective provider differs from what the user configured. Name the
|
|
2939
|
+
* provider and point at the fix instead of dumping a bare status code. Pure +
|
|
2940
|
+
* exported for tests.
|
|
2941
|
+
*/
|
|
2942
|
+
export function formatProviderHttpError(provider, status) {
|
|
2943
|
+
const base = `${provider} API error: HTTP ${status}`;
|
|
2944
|
+
if (status === 401 || status === 403) {
|
|
2945
|
+
return (
|
|
2946
|
+
`${base} — authentication failed: the API key for provider "${provider}" ` +
|
|
2947
|
+
`is missing or invalid. Check "cc config get llm.provider" and ` +
|
|
2948
|
+
`"cc config get llm.apiKey" (or run Configure LLM). A surprise "${provider}" ` +
|
|
2949
|
+
`here usually means the effective provider differs from the one you configured.`
|
|
2950
|
+
);
|
|
2951
|
+
}
|
|
2952
|
+
if (status === 429) return `${base} — rate limited; please retry shortly.`;
|
|
2953
|
+
return base;
|
|
2954
|
+
}
|
|
2955
|
+
|
|
2956
|
+
/**
|
|
2957
|
+
* Is this error from a streaming chat request a transient API CONNECTION drop
|
|
2958
|
+
* that is safe to retry? True only for genuine network failures (reset /
|
|
2959
|
+
* timeout / DNS / refused / socket hangup / undici "terminated" / "fetch
|
|
2960
|
+
* failed"). False for user aborts and for HTTP/status errors (a 4xx/auth/5xx is
|
|
2961
|
+
* the server's verdict carried in the message, not a dropped pipe — retrying a
|
|
2962
|
+
* connection that never dropped won't help and could double-bill).
|
|
2963
|
+
*
|
|
2964
|
+
* Safe to act on at the dispatch seam because any error that propagates OUT of
|
|
2965
|
+
* `_chat*Streaming` is either an abort or a drop with ZERO output already
|
|
2966
|
+
* streamed (partial-output drops are preserved internally and never throw) — so
|
|
2967
|
+
* a retry can never duplicate visible text. Pure + exported for tests.
|
|
2968
|
+
*/
|
|
2969
|
+
export function _isRetryableStreamError(err, signal) {
|
|
2970
|
+
if (isAbortError(err)) return false;
|
|
2971
|
+
if (signal && signal.aborted) return false;
|
|
2972
|
+
if (!err) return false;
|
|
2973
|
+
const code = String(err.code || err.cause?.code || "").toUpperCase();
|
|
2974
|
+
if (
|
|
2975
|
+
[
|
|
2976
|
+
"ECONNRESET",
|
|
2977
|
+
"ETIMEDOUT",
|
|
2978
|
+
"ECONNREFUSED",
|
|
2979
|
+
"EAI_AGAIN",
|
|
2980
|
+
"EPIPE",
|
|
2981
|
+
"ENETUNREACH",
|
|
2982
|
+
"ENOTFOUND",
|
|
2983
|
+
"UND_ERR_SOCKET",
|
|
2984
|
+
].includes(code)
|
|
2985
|
+
) {
|
|
2986
|
+
return true;
|
|
2987
|
+
}
|
|
2988
|
+
const msg = String(err.message || err).toLowerCase();
|
|
2989
|
+
return /econnreset|etimedout|econnrefused|eai_again|socket hang ?up|terminated|fetch failed|network error|timed?\s*out|premature close/.test(
|
|
2990
|
+
msg,
|
|
2991
|
+
);
|
|
2992
|
+
}
|
|
2993
|
+
|
|
2994
|
+
/** Bounded auto-retry for streaming connection drops (Claude-Code 2.1.181). */
|
|
2995
|
+
const STREAM_RETRY_MAX = 2;
|
|
2996
|
+
const STREAM_RETRY_BASE_MS = 400;
|
|
2997
|
+
|
|
2998
|
+
/**
|
|
2999
|
+
* Run a streaming chat attempt with bounded auto-retry on transient API
|
|
3000
|
+
* connection drops (Claude-Code 2.1.181: "auto-retry for API connection drops
|
|
3001
|
+
* during thinking"). Only connection-level failures are retried (see
|
|
3002
|
+
* `_isRetryableStreamError`); user aborts and HTTP/status errors surface
|
|
3003
|
+
* immediately. Backoff is exponential and abort-aware. Transparent to the
|
|
3004
|
+
* caller: on success returns the attempt's result; on exhaustion rethrows the
|
|
3005
|
+
* last error — strictly better than today (one drop → instant error).
|
|
3006
|
+
*
|
|
3007
|
+
* @param {() => Promise<any>} streamFn invokes one `_chat*Streaming` attempt
|
|
3008
|
+
* @param {object} opts { signal?, retries?, baseDelayMs?, onRetry?, sleep? }
|
|
3009
|
+
*/
|
|
3010
|
+
export async function _retryStreamingChat(streamFn, opts = {}) {
|
|
3011
|
+
const retries = opts.retries ?? STREAM_RETRY_MAX;
|
|
3012
|
+
const base = opts.baseDelayMs ?? STREAM_RETRY_BASE_MS;
|
|
3013
|
+
const signal = opts.signal;
|
|
3014
|
+
const sleep = opts.sleep || ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
3015
|
+
let attempt = 0;
|
|
3016
|
+
for (;;) {
|
|
3017
|
+
try {
|
|
3018
|
+
return await streamFn();
|
|
3019
|
+
} catch (err) {
|
|
3020
|
+
if (attempt >= retries || !_isRetryableStreamError(err, signal))
|
|
3021
|
+
throw err;
|
|
3022
|
+
attempt++;
|
|
3023
|
+
if (typeof opts.onRetry === "function") {
|
|
3024
|
+
try {
|
|
3025
|
+
opts.onRetry(attempt, err);
|
|
3026
|
+
} catch {
|
|
3027
|
+
/* the retry notice is best-effort; never let it mask the retry */
|
|
3028
|
+
}
|
|
3029
|
+
}
|
|
3030
|
+
await sleep(base * Math.pow(2, attempt - 1));
|
|
3031
|
+
if (signal && signal.aborted) throw err; // user bailed during backoff
|
|
3032
|
+
}
|
|
3033
|
+
}
|
|
3034
|
+
}
|
|
3035
|
+
|
|
2883
3036
|
/**
|
|
2884
3037
|
* Finalize a partial stream into the standard {message, usage} shape after a
|
|
2885
3038
|
* mid-stream connection drop: marks the message truncated and drops any
|
|
@@ -2902,7 +3055,7 @@ async function _chatOllamaStreaming(apiUrl, body, onToken, signal) {
|
|
|
2902
3055
|
body: JSON.stringify({ ...body, stream: true }),
|
|
2903
3056
|
});
|
|
2904
3057
|
if (!response.ok) {
|
|
2905
|
-
throw new Error(
|
|
3058
|
+
throw new Error(formatProviderHttpError("ollama", response.status));
|
|
2906
3059
|
}
|
|
2907
3060
|
const state = _ollamaInitState();
|
|
2908
3061
|
const reader = response.body.getReader();
|
|
@@ -3064,7 +3217,7 @@ async function _chatAnthropicStreaming(
|
|
|
3064
3217
|
body: JSON.stringify(body),
|
|
3065
3218
|
});
|
|
3066
3219
|
if (!response.ok) {
|
|
3067
|
-
throw new Error(
|
|
3220
|
+
throw new Error(formatProviderHttpError("anthropic", response.status));
|
|
3068
3221
|
}
|
|
3069
3222
|
const state = _anthropicInitState();
|
|
3070
3223
|
const reader = response.body.getReader();
|
|
@@ -3184,7 +3337,7 @@ async function _chatOpenAIStreaming(
|
|
|
3184
3337
|
body: JSON.stringify(body),
|
|
3185
3338
|
});
|
|
3186
3339
|
if (!response.ok) {
|
|
3187
|
-
throw new Error(
|
|
3340
|
+
throw new Error(formatProviderHttpError(provider, response.status));
|
|
3188
3341
|
}
|
|
3189
3342
|
const state = _openaiInitState();
|
|
3190
3343
|
const reader = response.body.getReader();
|
|
@@ -3578,7 +3731,27 @@ export async function* agentLoop(messages, options) {
|
|
|
3578
3731
|
// `options.chatFn` to drive the loop deterministically without hitting a
|
|
3579
3732
|
// real provider. Production code path is unchanged — the fallback is the
|
|
3580
3733
|
// real `chatWithTools`.
|
|
3581
|
-
|
|
3734
|
+
let llmCall = options.chatFn || chatWithTools;
|
|
3735
|
+
|
|
3736
|
+
// Runnable-first auth recovery: if the resolved provider's key is missing /
|
|
3737
|
+
// wrong / expired, self-heal to a provider we can actually run (endpoint-
|
|
3738
|
+
// inferred, then env-keyed) instead of failing the turn. Opt out with
|
|
3739
|
+
// `runnableProviderFallback: false`. Transparent on the happy path.
|
|
3740
|
+
if (options.runnableProviderFallback !== false) {
|
|
3741
|
+
const { makeRunnableProviderFallback } =
|
|
3742
|
+
await import("../lib/runnable-provider.js");
|
|
3743
|
+
llmCall = makeRunnableProviderFallback(llmCall, {
|
|
3744
|
+
onFallback: ({ from, to, reason }) => {
|
|
3745
|
+
try {
|
|
3746
|
+
process.stderr.write(
|
|
3747
|
+
`\x1b[2m[provider] "${from}" auth failed (${reason}) — retrying with "${to}"\x1b[0m\n`,
|
|
3748
|
+
);
|
|
3749
|
+
} catch {
|
|
3750
|
+
/* notice is best-effort */
|
|
3751
|
+
}
|
|
3752
|
+
},
|
|
3753
|
+
});
|
|
3754
|
+
}
|
|
3582
3755
|
|
|
3583
3756
|
// Phase 5 run bookends — a stable runId lets envelope subscribers correlate
|
|
3584
3757
|
// every tool_call / tool_result / message / ended event back to one run.
|
|
@@ -46,6 +46,7 @@ import {
|
|
|
46
46
|
import { expandFileRefsAsync } from "./file-ref-expander.js";
|
|
47
47
|
import { composeSystemPrompt } from "./system-prompt.js";
|
|
48
48
|
import { buildUserContent } from "../lib/image-input.js";
|
|
49
|
+
import { isHeadlessConfigCommand } from "../lib/headless-config-command.js";
|
|
49
50
|
import { withQuietStdout } from "./quiet-stdout.js";
|
|
50
51
|
import { CostBudget } from "../lib/cost-budget.js";
|
|
51
52
|
|
|
@@ -324,6 +325,50 @@ export async function runAgentHeadless(options = {}, deps = {}) {
|
|
|
324
325
|
const isJson = outputFormat === "json";
|
|
325
326
|
const isText = outputFormat === "text";
|
|
326
327
|
|
|
328
|
+
// ── Headless `/config` directive (Claude-Code 2.1.181: /config in -p mode) ──
|
|
329
|
+
// A leading `/config …` prompt is a one-shot config get/set/show, not a task
|
|
330
|
+
// for the LLM — handled before bootstrap/session/model so it never spends a
|
|
331
|
+
// turn or touches a provider. Mirrors the REPL `/config`; secrets stay masked.
|
|
332
|
+
if (isHeadlessConfigCommand(prompt)) {
|
|
333
|
+
const cm = await import("../lib/config-manager.js");
|
|
334
|
+
const { getConfigPath } = await import("../lib/paths.js");
|
|
335
|
+
const { runConfigDirective } =
|
|
336
|
+
await import("../lib/headless-config-command.js");
|
|
337
|
+
const { text, isError } = runConfigDirective(prompt, {
|
|
338
|
+
configManager: cm,
|
|
339
|
+
getConfigPath,
|
|
340
|
+
});
|
|
341
|
+
const subtype = isError ? "error" : "success";
|
|
342
|
+
if (isStream) {
|
|
343
|
+
writeOut(
|
|
344
|
+
JSON.stringify({
|
|
345
|
+
type: "result",
|
|
346
|
+
subtype,
|
|
347
|
+
is_error: isError,
|
|
348
|
+
result: text,
|
|
349
|
+
}) + "\n",
|
|
350
|
+
);
|
|
351
|
+
} else if (isJson) {
|
|
352
|
+
writeOut(
|
|
353
|
+
JSON.stringify(
|
|
354
|
+
buildResultEnvelope({
|
|
355
|
+
subtype,
|
|
356
|
+
isError,
|
|
357
|
+
result: text,
|
|
358
|
+
sessionId: null,
|
|
359
|
+
toolCalls: [],
|
|
360
|
+
usage: null,
|
|
361
|
+
numTurns: 0,
|
|
362
|
+
durationMs: 0,
|
|
363
|
+
}),
|
|
364
|
+
) + "\n",
|
|
365
|
+
);
|
|
366
|
+
} else {
|
|
367
|
+
writeOut(text + (text.endsWith("\n") ? "" : "\n"));
|
|
368
|
+
}
|
|
369
|
+
return { exitCode: isError ? 1 : 0, result: text, isError };
|
|
370
|
+
}
|
|
371
|
+
|
|
327
372
|
// ── Expand @file references in the prompt (Claude-Code parity) ─────────
|
|
328
373
|
// `@path/to/file` tokens are augmented with the referenced file contents (or
|
|
329
374
|
// a dir listing) so `cc agent -p "review @src/x.js"` works without a manual
|
|
@@ -673,7 +718,10 @@ export async function runAgentHeadless(options = {}, deps = {}) {
|
|
|
673
718
|
// per-call cost from token-usage events and stops the loop before the next
|
|
674
719
|
// paid call once the cap is reached. null → no cap (unchanged behavior).
|
|
675
720
|
const costBudget = options.maxCostUsd
|
|
676
|
-
? new CostBudget({
|
|
721
|
+
? new CostBudget({
|
|
722
|
+
limitUsd: options.maxCostUsd,
|
|
723
|
+
table: options.priceTable,
|
|
724
|
+
})
|
|
677
725
|
: null;
|
|
678
726
|
|
|
679
727
|
const startedAt = deps.now ? deps.now() : Date.now();
|
|
@@ -90,6 +90,11 @@ export function parseInputEvent(line) {
|
|
|
90
90
|
if (obj && typeof obj === "object" && obj.type === "interrupt") {
|
|
91
91
|
return { interrupt: true };
|
|
92
92
|
}
|
|
93
|
+
// Manual compaction (panel `/compact`, Claude-Code IDE parity): trim the
|
|
94
|
+
// live conversation history in place between turns. {"type":"compact"}
|
|
95
|
+
if (obj && typeof obj === "object" && obj.type === "compact") {
|
|
96
|
+
return { compact: true };
|
|
97
|
+
}
|
|
93
98
|
// Approval verdicts (panel Approve/Deny for --interactive-approvals):
|
|
94
99
|
// {"type":"approval","id":"appr-1","approve":true|false}
|
|
95
100
|
if (obj && typeof obj === "object" && obj.type === "approval") {
|
|
@@ -591,6 +596,16 @@ export async function runAgentHeadlessStream(options = {}, deps = {}) {
|
|
|
591
596
|
},
|
|
592
597
|
})
|
|
593
598
|
: undefined,
|
|
599
|
+
// Auto-retry notice (Claude-Code 2.1.181): the streaming call hit a
|
|
600
|
+
// transient API connection drop and is retrying. Surfaced unconditionally
|
|
601
|
+
// (not gated on --include-partial-messages) so programmatic NDJSON
|
|
602
|
+
// consumers can log/monitor reconnects rather than seeing an opaque pause.
|
|
603
|
+
onStreamRetry: (attempt) =>
|
|
604
|
+
emit({
|
|
605
|
+
type: "stream_retry",
|
|
606
|
+
attempt,
|
|
607
|
+
message: "API connection dropped — retrying",
|
|
608
|
+
}),
|
|
594
609
|
};
|
|
595
610
|
|
|
596
611
|
// --max-budget-usd: a SESSION-WIDE USD spend cap across all turns. Folded
|
|
@@ -673,6 +688,25 @@ export async function runAgentHeadlessStream(options = {}, deps = {}) {
|
|
|
673
688
|
continue;
|
|
674
689
|
}
|
|
675
690
|
|
|
691
|
+
// Manual `/compact` (Claude-Code IDE parity): trim the live history in
|
|
692
|
+
// place between turns — no LLM call (microCompact shortens large old tool
|
|
693
|
+
// results, preserving recent turns + tool pairs). Answers with a
|
|
694
|
+
// `compaction` event the panel renders as "compacted N→M, saved …".
|
|
695
|
+
if (parsed.compact) {
|
|
696
|
+
const { microCompact } = await import("../lib/micro-compact.js");
|
|
697
|
+
const before = messages.length;
|
|
698
|
+
const { messages: compacted, stats } = microCompact(messages);
|
|
699
|
+
messages.length = 0;
|
|
700
|
+
messages.push(...compacted);
|
|
701
|
+
emit({
|
|
702
|
+
type: "compaction",
|
|
703
|
+
stats,
|
|
704
|
+
messages_before: before,
|
|
705
|
+
messages_after: messages.length,
|
|
706
|
+
});
|
|
707
|
+
continue;
|
|
708
|
+
}
|
|
709
|
+
|
|
676
710
|
// Plan-mode control events (chat-panel plan UI). Mirrors the REPL's
|
|
677
711
|
// /plan verbs: enter blocks write tools (blocked calls become plan items),
|
|
678
712
|
// approve unlocks them and IMMEDIATELY runs a continuation turn, reject
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./xterm-BZcWGsqw.js","./markdown-CsiA8-E5.js","./markdown-Dfs9RUU9.css","./addon-fit-CK6X9sAG.js","./xterm-DFuMZ0ql.css"])))=>i.map(i=>d[i]);
|
|
2
|
-
import{u as fe,_ as me,d as U,e as V}from"./index-BUTN1VlO.js";import{I as z,J as x,U as _,Q as D,S as J,K as T,V as R,c as A,F as Q,Z,R as E,o as G,f as H,w as $,n as q,b as ee,r as S,P as y,_ as ve,a2 as we,a3 as he,a4 as _e}from"./vendor-BvqAck49.js";import{R as ye,b as pe,as as ge}from"./icons-DP3uiYxy.js";const k=new Map,C=new Map,I=new Set,N=new Set;let te=!1;function xe(c){te||(te=!0,c.onMessage(o=>{if(!(!o||typeof o.type!="string")){if(o.type==="terminal.stdout"){const{sessionId:s,data:e,seq:i}=o.payload||{};if(!s)return;let w;try{const d=atob(e||""),f=new Uint8Array(d.length);for(let n=0;n<d.length;n++)f[n]=d.charCodeAt(n);w=new TextDecoder("utf-8").decode(f)}catch{w=""}const u={sessionId:s,data:w,seq:i};k.get(s)?.forEach(d=>d(u)),I.forEach(d=>d(u))}else if(o.type==="terminal.exit"){const{sessionId:s,exitCode:e,signal:i}=o.payload||{};if(!s)return;const w={sessionId:s,exitCode:e,signal:i};C.get(s)?.forEach(u=>u(w)),N.forEach(u=>u(w))}}}))}function ne(c){const o=new TextEncoder().encode(c);let s="";for(let e=0;e<o.length;e++)s+=String.fromCharCode(o[e]);return btoa(s)}function ae(c){const o=atob(c||""),s=new Uint8Array(o.length);for(let e=0;e<o.length;e++)s[e]=o.charCodeAt(e);return new TextDecoder("utf-8").decode(s)}function oe(){const c=fe();xe(c);async function o(n={}){const a=await c.sendRaw({type:"terminal.create",payload:{shell:n.shell,cwd:n.cwd,env:n.env,cols:n.cols,rows:n.rows}});if(a.ok===!1)throw new Error(a.error||"terminal_create_failed");return a.result??a}async function s(){const n=await c.sendRaw({type:"terminal.list",payload:{}});if(n.ok===!1)throw new Error(n.error||"terminal_list_failed");const a=n.result??n;return Array.isArray(a.sessions)?a.sessions:[]}async function e(n,a){const r=await c.sendRaw({type:"terminal.stdin",payload:{sessionId:n,data:ne(String(a))}});if(r.ok===!1)throw new Error(r.error||"terminal_stdin_failed");return r.result??r}async function i(n,a,r){const h=await c.sendRaw({type:"terminal.resize",payload:{sessionId:n,cols:a,rows:r}});if(h.ok===!1)throw new Error(h.error||"terminal_resize_failed");return h.result??h}async function w(n){const a=await c.sendRaw({type:"terminal.close",payload:{sessionId:n}});if(a.ok===!1)throw new Error(a.error||"terminal_close_failed");return a.result??a}async function u(n,a=0){const r=await c.sendRaw({type:"terminal.history",payload:{sessionId:n,fromSeq:a}});if(r.ok===!1)throw new Error(r.error||"terminal_history_failed");const h=r.result??r;return{truncated:!!h.truncated,chunks:(h.chunks||[]).map(L=>({seq:L.seq,data:ae(L.data)}))}}function d(n,a){return n?(k.has(n)||k.set(n,new Set),k.get(n).add(a),()=>{k.get(n)?.delete(a),k.get(n)?.size===0&&k.delete(n)}):(I.add(a),()=>I.delete(a))}function f(n,a){return n?(C.has(n)||C.set(n,new Set),C.get(n).add(a),()=>{C.get(n)?.delete(a),C.get(n)?.size===0&&C.delete(n)}):(N.add(a),()=>N.delete(a))}return{create:o,list:s,stdin:e,resize:i,close:w,history:u,onStdout:d,onExit:f,_internal:{stdoutSubs:k,exitSubs:C,toBase64Utf8:ne,fromBase64Utf8:ae}}}const Se={__name:"Terminal",setup(c,{expose:o}){o();const s=oe(),e=S([]),i=S(null),w=S("pwsh"),u=S(!1),d=S(!1),f=S(""),n=S(""),a=S([]),r=[{value:"pwsh",label:"PowerShell"},{value:"cmd",label:"CMD"},{value:"bash",label:"Bash"},{value:"wsl",label:"WSL"}],h=ee(()=>e.value.find(t=>t.id===i.value));function L(t){return t?t.slice(0,8):""}let O=null,M=null;async function F(){if(O)return{xtermMod:O,fitAddonMod:M};try{O=await V(()=>import("./xterm-BZcWGsqw.js").then(t=>t.x),__vite__mapDeps([0,1,2]),import.meta.url),M=await V(()=>import("./addon-fit-CK6X9sAG.js").then(t=>t.a),__vite__mapDeps([3,1,2]),import.meta.url),await V(()=>Promise.resolve({}),__vite__mapDeps([4]),import.meta.url)}catch(t){throw n.value="xterm 资源加载失败:"+(t?.message||"未知错误"),t}return{xtermMod:O,fitAddonMod:M}}async function B(t){await q();const{xtermMod:l,fitAddonMod:p}=await F(),b=a.value.find(v=>v?.dataset?.sessionId===t.id);if(!b)return;const m=new l.Terminal({cursorBlink:!0,fontFamily:'Consolas, "Courier New", monospace',fontSize:13,theme:{background:"#1e1e1e",foreground:"#d4d4d4"},convertEol:!1}),P=new p.FitAddon;m.loadAddon(P),m.open(b);try{P.fit()}catch{}t.xterm=m,t.fitAddon=P;const ie=m.onData(v=>{s.stdin(t.id,v).catch(g=>{String(g?.message||"").includes("dangerous_keyword_blocked")?U.warning("该命令被桌面端拦截(高危关键字)"):U.error("stdin 失败: "+(g?.message||g))})}),ce=s.onStdout(t.id,({data:v,seq:g})=>{t.lastSeq=g,m.write(v)}),de=s.onExit(t.id,({exitCode:v,signal:g})=>{t.alive=!1,t.exitCode=v,t.signal=g,m.writeln(`\r
|
|
3
|
-
\x1B[33m[session exited, code=${v}, signal=${g??"-"}]\x1B[0m`)});t.offs=()=>{try{ie.dispose?.()}catch{}ce(),de()};try{const{chunks:v,truncated:g}=await s.history(t.id,0);g&&m.writeln("\x1B[2m[history truncated — earlier output was evicted]\x1B[0m");for(const j of v)m.write(j.data),t.lastSeq=j.seq}catch(v){m.writeln(`\x1B[31m[history fetch failed: ${v?.message||v}]\x1B[0m`)}const Y=new ResizeObserver(()=>{try{P.fit(),s.resize(t.id,m.cols,m.rows).catch(()=>{})}catch{}});Y.observe(b);const ue=t.offs;t.offs=()=>{try{Y.disconnect()}catch{}ue()}}async function re(){u.value=!0,f.value="";try{const t=await s.create({shell:w.value,cols:80,rows:24}),l={id:t.sessionId,shell:t.shell,cwd:"",alive:!0,lastSeq:0,exitCode:null,xterm:null,fitAddon:null,offs:()=>{}};e.value.push(l),i.value=l.id,await B(l)}catch(t){f.value=t?.message||String(t)}finally{u.value=!1}}async function se(t){try{await s.close(t)}catch(l){f.value=l?.message||String(l)}setTimeout(()=>W(t),500)}function W(t){const l=e.value.findIndex(b=>b.id===t);if(l===-1)return;const p=e.value[l];try{p.offs?.()}catch{}try{p.xterm?.dispose?.()}catch{}e.value.splice(l,1),i.value===t&&(i.value=e.value[0]?.id||null)}function le(t){i.value=t,q(()=>{const l=e.value.find(p=>p.id===t);try{l?.fitAddon?.fit()}catch{}})}async function K(){d.value=!0,f.value="";try{const t=await s.list();for(const l of t){const p=e.value.find(m=>m.id===l.id);if(p){p.alive=l.alive,p.lastSeq=l.lastSeq;continue}const b={id:l.id,shell:l.shell,cwd:l.cwd,alive:l.alive,lastSeq:l.lastSeq,exitCode:null,xterm:null,fitAddon:null,offs:()=>{}};e.value.push(b),await B(b)}!i.value&&e.value.length>0&&(i.value=e.value[0].id)}catch(t){f.value=t?.message||String(t)}finally{d.value=!1}}G(async()=>{await K()}),H(()=>{for(const t of e.value){try{t.offs?.()}catch{}try{t.xterm?.dispose?.()}catch{}}}),$(i,()=>{q(()=>{const t=h.value;try{t?.fitAddon?.fit()}catch{}})});const X={term:s,sessions:e,activeId:i,newShell:w,creating:u,loadingList:d,error:f,warning:n,xtermContainers:a,shellOptions:r,active:h,shortId:L,get xtermMod(){return O},set xtermMod(t){O=t},get fitAddonMod(){return M},set fitAddonMod(t){M=t},loadXterm:F,mountXterm:B,onCreate:re,onClose:se,removeSession:W,activate:le,refreshList:K,ref:S,computed:ee,onMounted:G,onBeforeUnmount:H,nextTick:q,watch:$,get message(){return U},get PlusOutlined(){return ge},get CloseOutlined(){return pe},get ReloadOutlined(){return ye},get useTerminal(){return oe}};return Object.defineProperty(X,"__isScriptSetup",{enumerable:!1,value:!0}),X}},be={class:"terminal-page"},ke={class:"terminal-header"},Ce={class:"page-sub"},Ae={class:"terminal-body"},Ee={class:"session-tabs"},Oe=["onClick"],Te={class:"session-shell"},Re={class:"session-id"},Me={key:0,class:"session-empty"},ze={class:"xterm-host"},Le=["data-session-id"],Pe={key:0,class:"xterm-placeholder"},De={key:1,class:"terminal-footer"},qe={key:0,class:"footer-exit"};function Be(c,o,s,e,i,w){const u=z("a-tag"),d=z("a-select"),f=z("a-button"),n=z("a-space"),a=z("a-alert");return y(),x("div",be,[_("div",ke,[_("div",null,[o[3]||(o[3]=_("h2",{class:"page-title"},"远程终端",-1)),_("p",Ce,[o[2]||(o[2]=D(" 桌面端托管的 PTY 会话;Android 端可远程操控同一通道 ",-1)),e.warning?(y(),J(u,{key:0,color:"orange",style:{"margin-left":"8px"}},{default:T(()=>[D(E(e.warning),1)]),_:1})):R("v-if",!0)])]),A(n,null,{default:T(()=>[A(d,{value:e.newShell,"onUpdate:value":o[0]||(o[0]=r=>e.newShell=r),style:{width:"130px"},size:"small",options:e.shellOptions},null,8,["value"]),A(f,{type:"primary",size:"small",loading:e.creating,onClick:e.onCreate},{icon:T(()=>[A(e.PlusOutlined)]),default:T(()=>[o[4]||(o[4]=D(" 新会话 ",-1))]),_:1},8,["loading"]),A(f,{size:"small",loading:e.loadingList,onClick:e.refreshList},{icon:T(()=>[A(e.ReloadOutlined)]),default:T(()=>[o[5]||(o[5]=D(" 刷新 ",-1))]),_:1},8,["loading"])]),_:1})]),e.error?(y(),J(a,{key:0,message:e.error,type:"error","show-icon":"",closable:"",style:{"margin-bottom":"12px"},onClose:o[1]||(o[1]=r=>e.error="")},null,8,["message"])):R("v-if",!0),_("div",Ae,[_("div",Ee,[(y(!0),x(Q,null,Z(e.sessions,r=>(y(),x("div",{key:r.id,class:ve(["session-tab",{active:r.id===e.activeId,dead:!r.alive}]),onClick:h=>e.activate(r.id)},[_("span",Te,E(r.shell),1),_("span",Re,E(e.shortId(r.id)),1),A(e.CloseOutlined,{class:"session-close",onClick:we(h=>e.onClose(r.id),["stop"])},null,8,["onClick"])],10,Oe))),128)),e.sessions.length===0?(y(),x("div",Me,' 点击 "新会话" 创建第一个终端 ')):R("v-if",!0)]),_("div",ze,[(y(!0),x(Q,null,Z(e.sessions,r=>he((y(),x("div",{key:r.id,ref_for:!0,ref:"xtermContainers","data-session-id":r.id,class:"xterm-container"},null,8,Le)),[[_e,r.id===e.activeId]])),128)),e.sessions.length===0?(y(),x("div",Pe,[...o[6]||(o[6]=[_("span",null,"无活跃会话",-1)])])):R("v-if",!0)])]),e.active?(y(),x("div",De,[_("span",null,E(e.active.shell)+" · "+E(e.active.cwd||"(默认 cwd)")+" · seq "+E(e.active.lastSeq),1),e.active.alive?R("v-if",!0):(y(),x("span",qe,"已退出 (code="+E(e.active.exitCode??"-")+")",1))])):R("v-if",!0)])}const Ne=me(Se,[["render",Be],["__scopeId","data-v-65366a29"],["__file","/tmp/cc-web-panel-UKI3LY/repo/packages/web-panel/src/views/Terminal.vue"]]);export{Ne as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{O as r}from"./index-BUTN1VlO.js";const o=((n,a,e)=>{r(n,`[ant-design-vue: ${a}] ${e}`)});export{o as d};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{C as o}from"./Col-Bjn5vFES.js";import{U as t}from"./index-BUTN1VlO.js";import"./vendor-BvqAck49.js";import"./index-B5W1vQHV.js";import"./icons-DP3uiYxy.js";const s=t(o);export{s as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{A as o}from"./Row-Dem0Wxxb.js";import{U as t}from"./index-BUTN1VlO.js";import"./vendor-BvqAck49.js";import"./responsiveObserve-Ch2ojiNn.js";import"./useFlexGapSupport-BINo_rNH.js";import"./styleChecker-Cor2-FwV.js";import"./index-B5W1vQHV.js";import"./icons-DP3uiYxy.js";const l=t(o);export{l as default};
|