chainlesschain 0.162.115 → 0.162.117

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 (163) hide show
  1. package/package.json +2 -2
  2. package/src/assets/web-panel/.build-hash +1 -1
  3. package/src/assets/web-panel/assets/{AIOps-Co3Wa2si.js → AIOps-D1_eyrEP.js} +1 -1
  4. package/src/assets/web-panel/assets/{ActionButton-CLu_iLWH.js → ActionButton-tnrMeU0Q.js} +1 -1
  5. package/src/assets/web-panel/assets/{Analytics-BZM53XMx.js → Analytics-SmbOMqoN.js} +3 -3
  6. package/src/assets/web-panel/assets/{AppLayout-BvWMuVj5.js → AppLayout-B4m4Ic3a.js} +4 -4
  7. package/src/assets/web-panel/assets/{Audit-LnZR0-SB.js → Audit-DTC34Qp6.js} +1 -1
  8. package/src/assets/web-panel/assets/{Backup-m1_KWvdD.js → Backup-CU_llIBF.js} +1 -1
  9. package/src/assets/web-panel/assets/{BaseInput-DCOyDzK5.js → BaseInput-vP66eF17.js} +1 -1
  10. package/src/assets/web-panel/assets/{Chat-BxctrF91.js → Chat-B8-4iHVJ.js} +6 -6
  11. package/src/assets/web-panel/assets/ChatBubbleRenderer-CFD0Xh5e.js +1 -0
  12. package/src/assets/web-panel/assets/{Checkbox-CiI-q7XC.js → Checkbox-zV2Jzmv8.js} +1 -1
  13. package/src/assets/web-panel/assets/{Codegen-Bs5RpmWs.js → Codegen-C7kfk-rY.js} +1 -1
  14. package/src/assets/web-panel/assets/{Col-By1dCa5E.js → Col-t_vUszuS.js} +1 -1
  15. package/src/assets/web-panel/assets/{Community-WuZZQrIQ.js → Community-pRCzzaCc.js} +1 -1
  16. package/src/assets/web-panel/assets/{Compact-Bgjv17zY.js → Compact-7IgSGjnW.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compliance-CANmPNTe.js → Compliance-BqDi7iuY.js} +1 -1
  18. package/src/assets/web-panel/assets/{Cowork-B5vtDpAA.js → Cowork-BL84ZaWT.js} +2 -2
  19. package/src/assets/web-panel/assets/{Cron-CD1EDrV4.js → Cron-yzv32Scr.js} +2 -2
  20. package/src/assets/web-panel/assets/{Crosschain-C8VDvGgO.js → Crosschain-6DTzS8P5.js} +1 -1
  21. package/src/assets/web-panel/assets/{DID-DpZV0N2H.js → DID-CKP63O4a.js} +2 -2
  22. package/src/assets/web-panel/assets/{Dashboard-NhU8jHSF.js → Dashboard-CsV3eP9F.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dropdown-D-r8yZE9.js → Dropdown-CByjFKzJ.js} +1 -1
  24. package/src/assets/web-panel/assets/{EmailListRenderer-DG89eV-l.js → EmailListRenderer-C6Hn7n75.js} +1 -1
  25. package/src/assets/web-panel/assets/{FamilyGuardDashboard-G-b864Cs.js → FamilyGuardDashboard-e1c1vXTT.js} +1 -1
  26. package/src/assets/web-panel/assets/{Federation-yBmOj1ob.js → Federation-D9oejCYY.js} +1 -1
  27. package/src/assets/web-panel/assets/{FormItemContext-Cb7_kwNJ.js → FormItemContext-DZ4d_-AT.js} +1 -1
  28. package/src/assets/web-panel/assets/{GenericCardRenderer-DH-hOAyY.js → GenericCardRenderer-QoGFWBcP.js} +1 -1
  29. package/src/assets/web-panel/assets/{Git-DsxDUo_n.js → Git-DVZHPXw8.js} +2 -2
  30. package/src/assets/web-panel/assets/Governance-NwdvDPTB.js +1 -0
  31. package/src/assets/web-panel/assets/{Inference-BBp_JKRI.js → Inference-BYz9MiEu.js} +1 -1
  32. package/src/assets/web-panel/assets/{KnowledgeGraph-Dh1Hy3KR.js → KnowledgeGraph-BUdGIZKt.js} +1 -1
  33. package/src/assets/web-panel/assets/{Logs-B4dKW6iL.js → Logs-CPGewhlD.js} +2 -2
  34. package/src/assets/web-panel/assets/{Marketplace-BwG7IJMb.js → Marketplace-BgRk4ZMW.js} +1 -1
  35. package/src/assets/web-panel/assets/{McpTools-Bkem_Aad.js → McpTools-DLY9Fd7m.js} +5 -5
  36. package/src/assets/web-panel/assets/{Memory--XBiW9hE.js → Memory-B2QishHE.js} +2 -2
  37. package/src/assets/web-panel/assets/{MobileBridge-Cw3bfWS4.js → MobileBridge-DXMQwB_y.js} +2 -2
  38. package/src/assets/web-panel/assets/{MobileProjects-DxtImZQU.js → MobileProjects-4uY4YbFm.js} +1 -1
  39. package/src/assets/web-panel/assets/{Mtc-BGF20OtO.js → Mtc-BVASCKUv.js} +2 -2
  40. package/src/assets/web-panel/assets/{MtcAudit-D7z4gsQ5.js → MtcAudit-ByLFJ4Zu.js} +2 -2
  41. package/src/assets/web-panel/assets/{Multisig-yXwos6j_.js → Multisig-DRAZYcC6.js} +3 -3
  42. package/src/assets/web-panel/assets/{NLProgramming-CDHJQX6y.js → NLProgramming-EiptSKn1.js} +1 -1
  43. package/src/assets/web-panel/assets/{Notes-D39k9gda.js → Notes-BHh5Akqh.js} +4 -4
  44. package/src/assets/web-panel/assets/{NotificationSettings-DRPq8tRI.js → NotificationSettings-XWFl4_3j.js} +1 -1
  45. package/src/assets/web-panel/assets/{OrderTableRenderer-D2hEsaLV.js → OrderTableRenderer-Bdc_O7wT.js} +1 -1
  46. package/src/assets/web-panel/assets/{Organization--o8Lvsg2.js → Organization--VWlMdKp.js} +4 -4
  47. package/src/assets/web-panel/assets/{Overflow-D2ohI9Qx.js → Overflow-DuYUvyh7.js} +1 -1
  48. package/src/assets/web-panel/assets/{P2P-2c8cEXWp.js → P2P-BnHaPvFP.js} +2 -2
  49. package/src/assets/web-panel/assets/{PdhVaultBrowser-D7NaNkzj.js → PdhVaultBrowser-WfbjffWL.js} +5 -5
  50. package/src/assets/web-panel/assets/{Permissions-ypORwVpA.js → Permissions-BIFhAsZO.js} +4 -4
  51. package/src/assets/web-panel/assets/{PersonalDataHub-CwGL8rYc.js → PersonalDataHub-Dt_m7YZ6.js} +2 -2
  52. package/src/assets/web-panel/assets/{Pipeline-sOxtzc6E.js → Pipeline-BsNDbhs7.js} +1 -1
  53. package/src/assets/web-panel/assets/{Privacy-DQn-uEJl.js → Privacy-C1SJp30I.js} +1 -1
  54. package/src/assets/web-panel/assets/{ProjectInit-8C5fy5dR.js → ProjectInit-DIOkwlK-.js} +2 -2
  55. package/src/assets/web-panel/assets/{ProjectSettings-CX_i2PB9.js → ProjectSettings-BZIL8hvl.js} +2 -2
  56. package/src/assets/web-panel/assets/{Projects-CHTItBCE.js → Projects-LHqWtiu2.js} +1 -1
  57. package/src/assets/web-panel/assets/{Providers-hTVigloe.js → Providers-DlC7SSJT.js} +1 -1
  58. package/src/assets/web-panel/assets/{QuickAsk-BqHj37NO.js → QuickAsk-DeaVcEma.js} +1 -1
  59. package/src/assets/web-panel/assets/{Recommend-W6Fz6lz7.js → Recommend-BbQI1g_u.js} +1 -1
  60. package/src/assets/web-panel/assets/{Reputation-BgEa2pxb.js → Reputation-Cro6z2uR.js} +1 -1
  61. package/src/assets/web-panel/assets/{Row-DyIFWI6T.js → Row-D7LuPQVP.js} +1 -1
  62. package/src/assets/web-panel/assets/{RssFeed-CEYCVpMw.js → RssFeed-BowXZJOX.js} +3 -3
  63. package/src/assets/web-panel/assets/{Search-zVLBmQ7o.js → Search-8oufqOmB.js} +1 -1
  64. package/src/assets/web-panel/assets/{Security-CqsG8Ps9.js → Security-CRKFB58y.js} +3 -3
  65. package/src/assets/web-panel/assets/{Services-BfBS_Jgz.js → Services-DzRJ4Kus.js} +2 -2
  66. package/src/assets/web-panel/assets/{Skeleton-Soo_Vk86.js → Skeleton-DtZmLaQr.js} +1 -1
  67. package/src/assets/web-panel/assets/{Skills-PuZUk7l4.js → Skills-CPQFZvJ3.js} +1 -1
  68. package/src/assets/web-panel/assets/{Sla-CyXFxluF.js → Sla-C599OilU.js} +1 -1
  69. package/src/assets/web-panel/assets/{SpeechSettings-Bix3Aslu.js → SpeechSettings-VdhwHyYN.js} +1 -1
  70. package/src/assets/web-panel/assets/{SyncSettings-CIpNxBVI.js → SyncSettings-BvX8fTlf.js} +2 -2
  71. package/src/assets/web-panel/assets/{Tasks-S3P_gUGK.js → Tasks-B63Y5zWj.js} +1 -1
  72. package/src/assets/web-panel/assets/{Templates-ApPjFp6V.js → Templates-DZtjohnP.js} +1 -1
  73. package/src/assets/web-panel/assets/{Tenant-CA39QBQx.js → Tenant-C8WHv74j.js} +1 -1
  74. package/src/assets/web-panel/assets/Terminal-D0IC-PsM.js +3 -0
  75. package/src/assets/web-panel/assets/{TimelineRenderer-BH_bLWth.js → TimelineRenderer-C392mTJ7.js} +1 -1
  76. package/src/assets/web-panel/assets/{Tokens-CnM7BBQo.js → Tokens-BnmIK-7p.js} +1 -1
  77. package/src/assets/web-panel/assets/{Trigger-BaTkvjag.js → Trigger-CLYUm45n.js} +1 -1
  78. package/src/assets/web-panel/assets/{Trust-BL5_D9F2.js → Trust-DmKkB9n0.js} +1 -1
  79. package/src/assets/web-panel/assets/{UkeySign-RL5tPg28.js → UkeySign-Btsb1cq9.js} +1 -1
  80. package/src/assets/web-panel/assets/{VideoEditing-DpziQ6j6.js → VideoEditing-KbSQiQ31.js} +1 -1
  81. package/src/assets/web-panel/assets/{Wallet-C5TJX6y1.js → Wallet-C7T1JpB8.js} +4 -4
  82. package/src/assets/web-panel/assets/{WebAuthn-BRmIn8F_.js → WebAuthn-B7fk675F.js} +5 -5
  83. package/src/assets/web-panel/assets/{WorkflowEditor-Dcq3e-ik.js → WorkflowEditor-BFEqg2-F.js} +1 -1
  84. package/src/assets/web-panel/assets/{chat-CIxM8WKc.js → chat-CCONImPU.js} +1 -1
  85. package/src/assets/web-panel/assets/{colors-C4KulwfK.js → colors-BO_usQco.js} +1 -1
  86. package/src/assets/web-panel/assets/{compact-item-BuSPdvu3.js → compact-item-DPYH8VmY.js} +1 -1
  87. package/src/assets/web-panel/assets/{createContext-DnkNir9Q.js → createContext-DuzinRx4.js} +1 -1
  88. package/src/assets/web-panel/assets/devWarning-BJYFLmEO.js +1 -0
  89. package/src/assets/web-panel/assets/{hasIn-Cf4nLc1Z.js → hasIn-CGE1laax.js} +1 -1
  90. package/src/assets/web-panel/assets/{index-DeV7mBjK.js → index-B5edbioK.js} +1 -1
  91. package/src/assets/web-panel/assets/{index-DLj6Tgel.js → index-B6faWLbM.js} +1 -1
  92. package/src/assets/web-panel/assets/{index-DdA5Ua1b.js → index-BBdlh4S9.js} +1 -1
  93. package/src/assets/web-panel/assets/{index-C3K7gFx4.js → index-BJrz4R1Q.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-DUZ5Kool.js → index-BMvB7bwt.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-CIMAqOlD.js → index-BOrH71r5.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-B0Mg9TNh.js → index-BQieJh3P.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-DeMVCFm4.js → index-BhhReyyv.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-Bxrdsatm.js → index-BoiTovAz.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-E9E5_8nm.js → index-BxSrpYMm.js} +1 -1
  100. package/src/assets/web-panel/assets/index-BznL1yIS.js +1 -0
  101. package/src/assets/web-panel/assets/{index-DXmlFKkG.js → index-C0J2olcT.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-CrjJWis4.js → index-C4OJtXb2.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-DGVc7PRP.js → index-C6ORJpUJ.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-BKxfssGi.js → index-C8E575em.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-DlHHHyRx.js → index-C9a-SDrB.js} +3 -3
  106. package/src/assets/web-panel/assets/{index-BSSHH2wV.js → index-CCFBPPL3.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-BixeTMRH.js → index-CFMqyOfS.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-yzjY8Axn.js → index-CHBCKhGQ.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-LeyWnzub.js → index-CLevxAAl.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-DyWheeVw.js → index-CiJrDiUF.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-CBAXN3jR.js → index-Cjl3wd7u.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-ClY2B2K3.js → index-CsWempjh.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-Djm5GtZl.js → index-CyNkEBra.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-BhJdIewr.js → index-CzJpaaYk.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-BiSVvB5h.js → index-D7PjxVkg.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-EdjA-3Bc.js → index-DEsKGcgq.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-D6_QgZN5.js → index-DFeaJhkI.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-Cq1Fi_sP.js → index-DPLKoezy.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-DjRLVmY5.js → index-DcDzDXuF.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-CWDu1oDR.js → index-DhWFRGxq.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-BkuD99JZ.js → index-Djr_9TsG.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-xUqyfQDD.js → index-Dr_bswHZ.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-Bz60EXNV.js → index-Njlbk8o6.js} +1 -1
  124. package/src/assets/web-panel/assets/index-SDk3qvNU.js +1 -0
  125. package/src/assets/web-panel/assets/{index-Ch5ijBA1.js → index-VjWPHPNA.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-CZrWQUeU.js → index-WNRdmI1g.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-N1ncDYAO.js → index-_6PfzLBb.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-2EWeBL-U.js → index-k_kmmJE3.js} +1 -1
  129. package/src/assets/web-panel/assets/{initDefaultProps--8dT0H6B.js → initDefaultProps-V7_xvS9N.js} +1 -1
  130. package/src/assets/web-panel/assets/{motion-D90Oz3kN.js → motion-DRGZ-HPK.js} +1 -1
  131. package/src/assets/web-panel/assets/{move-CXTvl7NU.js → move-CZrcQSMV.js} +1 -1
  132. package/src/assets/web-panel/assets/{omit-0trPxlmr.js → omit-pd-_NH99.js} +1 -1
  133. package/src/assets/web-panel/assets/{pickAttrs-CgAAFR7h.js → pickAttrs-CTupbn-v.js} +1 -1
  134. package/src/assets/web-panel/assets/{placementArrow-CS-hpoJS.js → placementArrow-Bn7FYxHg.js} +1 -1
  135. package/src/assets/web-panel/assets/{responsiveObserve-9S3fBAtz.js → responsiveObserve-WfzZR_7N.js} +1 -1
  136. package/src/assets/web-panel/assets/{slide-B-R8yXs_.js → slide-CYH80N3q.js} +1 -1
  137. package/src/assets/web-panel/assets/{statusUtils-DQiprWuZ.js → statusUtils-C4hqTJzV.js} +1 -1
  138. package/src/assets/web-panel/assets/{styleChecker-02H_AFJW.js → styleChecker-BQyUObAw.js} +1 -1
  139. package/src/assets/web-panel/assets/{useFlexGapSupport-9DGfLuf2.js → useFlexGapSupport-BGDH_gNt.js} +1 -1
  140. package/src/assets/web-panel/assets/{useFs-CrPyzwar.js → useFs-D_3Y1Yh6.js} +1 -1
  141. package/src/assets/web-panel/assets/{usePersonalDataHub-BeLgaZq9.js → usePersonalDataHub-vnU0oI0p.js} +1 -1
  142. package/src/assets/web-panel/assets/{vnode-Cln6ZUUD.js → vnode-DnTqtAMj.js} +1 -1
  143. package/src/assets/web-panel/assets/{zoom-BauTGPPs.js → zoom-ChHcZlUk.js} +1 -1
  144. package/src/assets/web-panel/index.html +1 -1
  145. package/src/commands/hub.js +163 -2
  146. package/src/harness/mcp-client.js +17 -0
  147. package/src/lib/checkpoint-store.js +42 -5
  148. package/src/lib/cost-budget.js +14 -1
  149. package/src/lib/cross-chain-mtc.js +17 -0
  150. package/src/lib/dlp-engine.js +3 -1
  151. package/src/lib/hook-runner.cjs +35 -6
  152. package/src/lib/loop.js +16 -9
  153. package/src/lib/permission-rules.cjs +6 -2
  154. package/src/lib/skill-loader.js +9 -0
  155. package/src/lib/web-fetch.js +33 -4
  156. package/src/repl/agent-repl.js +12 -0
  157. package/src/runtime/headless-runner.js +14 -0
  158. package/src/assets/web-panel/assets/ChatBubbleRenderer-Cr-ifTsG.js +0 -1
  159. package/src/assets/web-panel/assets/Governance-9tswXx9m.js +0 -1
  160. package/src/assets/web-panel/assets/Terminal-CZCQVy4D.js +0 -3
  161. package/src/assets/web-panel/assets/devWarning-CMQKGI8R.js +0 -1
  162. package/src/assets/web-panel/assets/index-D1b1GHRk.js +0 -1
  163. package/src/assets/web-panel/assets/index-DGZzQZUz.js +0 -1
@@ -1114,9 +1114,13 @@ async function cmdCollectQq(options) {
1114
1114
  `qqnt-dec-${process.pid}.db`,
1115
1115
  );
1116
1116
  fs.writeFileSync(tmp, result.decrypted);
1117
- let events;
1117
+ let parsed;
1118
1118
  try {
1119
- events = qq.parseEvents(Database, tmp, options.self || "self");
1119
+ // selfUid = the matched account uid → reliable self attribution.
1120
+ parsed = qq.parseEvents(Database, tmp, {
1121
+ selfUid: result.uid,
1122
+ self: options.self || "self",
1123
+ });
1120
1124
  } finally {
1121
1125
  try {
1122
1126
  fs.unlinkSync(tmp);
@@ -1124,7 +1128,24 @@ async function cmdCollectQq(options) {
1124
1128
  /* best-effort wipe */
1125
1129
  }
1126
1130
  }
1131
+ const events = Array.isArray(parsed) ? parsed : parsed.events || [];
1132
+ const persons = Array.isArray(parsed) ? [] : parsed.persons || [];
1133
+ const topics = Array.isArray(parsed) ? [] : parsed.topics || [];
1127
1134
 
1135
+ for (const p of persons) {
1136
+ try {
1137
+ vault.putPerson(p);
1138
+ } catch {
1139
+ /* dup / optional */
1140
+ }
1141
+ }
1142
+ for (const t of topics) {
1143
+ try {
1144
+ if (typeof vault.putTopic === "function") vault.putTopic(t);
1145
+ } catch {
1146
+ /* dup / optional */
1147
+ }
1148
+ }
1128
1149
  let ingested = 0;
1129
1150
  for (const ev of events) {
1130
1151
  try {
@@ -1139,6 +1160,8 @@ async function cmdCollectQq(options) {
1139
1160
  matchedUid: result.uid,
1140
1161
  kdf: result.kdf,
1141
1162
  messages: events.length,
1163
+ persons: persons.length,
1164
+ topics: topics.length,
1142
1165
  ingested,
1143
1166
  };
1144
1167
  if (spinner) spinner.succeed(`collect-qq: ${ingested} QQ messages → vault`);
@@ -1380,6 +1403,121 @@ async function cmdCollectWechat(options) {
1380
1403
  }
1381
1404
  }
1382
1405
 
1406
+ // ─── collect-qzone (QQ空间 说说/留言板/相册 via API → vault) ──────────────
1407
+ //
1408
+ // module 101 — Qzone has no local DB, so this is the cookie+g_tk API path. The
1409
+ // Android in-app WebView (or a desktop browser) captures the qzone-domain
1410
+ // p_skey cookie; this derives g_tk and pulls the owner's feeds. The cookie is
1411
+ // passed via --cookie or --cookie-file (kept out of argv/history when staged).
1412
+ async function cmdCollectQzone(options) {
1413
+ const spinner = options.json
1414
+ ? null
1415
+ : ora("collect-qzone (API + ingest)...").start();
1416
+ try {
1417
+ const { createRequire } = await import("module");
1418
+ const require = createRequire(import.meta.url);
1419
+ const fs = require("fs");
1420
+ const qz = options._qzoneCore
1421
+ ? options._qzoneCore
1422
+ : await importPdh(
1423
+ "@chainlesschain/personal-data-hub/forensics/qzone-collect",
1424
+ );
1425
+ let cookie = options.cookie;
1426
+ if (!cookie && options.cookieFile && fs.existsSync(options.cookieFile))
1427
+ cookie = fs.readFileSync(options.cookieFile, "utf8").trim();
1428
+ if (!cookie)
1429
+ throw new Error(
1430
+ 'need --cookie "<qzone cookie>" or --cookie-file <path> (must contain uin + p_skey)',
1431
+ );
1432
+ const what = (options.what || "shuoshuo,msgb,album")
1433
+ .split(",")
1434
+ .map((s) => s.trim())
1435
+ .filter(Boolean);
1436
+ const max = Number.isFinite(+options.max) ? +options.max : 1000;
1437
+
1438
+ const result = await qz.collectQzone({
1439
+ uin: options.uin,
1440
+ cookie,
1441
+ what,
1442
+ max,
1443
+ fetchImpl: options._fetch,
1444
+ });
1445
+ if (!result.ok) {
1446
+ const report = { ok: false, reason: result.reason };
1447
+ if (options.json) {
1448
+ jsonAndExit(report);
1449
+ return;
1450
+ }
1451
+ if (spinner) spinner.fail(`collect-qzone: ${result.reason}`);
1452
+ process.exit(2);
1453
+ }
1454
+
1455
+ const hub = await (options._getHub || getHub)();
1456
+ const vault = hub.vault;
1457
+ try {
1458
+ vault.putPerson({
1459
+ type: "person",
1460
+ subtype: "contact",
1461
+ id: qz.SELF_ID,
1462
+ names: ["我(QQ空间)"],
1463
+ source: {
1464
+ adapter: "qzone",
1465
+ adapterVersion: "0.1.0",
1466
+ originalId: qz.SELF_ID,
1467
+ capturedAt: Date.now(),
1468
+ capturedBy: "api",
1469
+ },
1470
+ ingestedAt: Date.now(),
1471
+ });
1472
+ } catch {
1473
+ /* dup */
1474
+ }
1475
+ for (const p of result.persons) {
1476
+ try {
1477
+ vault.putPerson(p);
1478
+ } catch {
1479
+ /* dup */
1480
+ }
1481
+ }
1482
+ let ingested = 0;
1483
+ for (const ev of result.events) {
1484
+ try {
1485
+ vault.putEvent(ev);
1486
+ ingested++;
1487
+ } catch {
1488
+ /* dup */
1489
+ }
1490
+ }
1491
+
1492
+ const report = {
1493
+ ok: true,
1494
+ uin: result.uin,
1495
+ counts: result.counts,
1496
+ events: result.events.length,
1497
+ persons: result.persons.length,
1498
+ ingested,
1499
+ };
1500
+ if (spinner)
1501
+ spinner.succeed(
1502
+ `collect-qzone: ${ingested} events → vault (${Object.entries(
1503
+ result.counts,
1504
+ )
1505
+ .map(([k, v]) => k + ":" + v)
1506
+ .join(" ")})`,
1507
+ );
1508
+ if (options.json) {
1509
+ jsonAndExit(report);
1510
+ return;
1511
+ }
1512
+ logger.log(chalk.green("✓ Qzone collect succeeded"));
1513
+ logger.log(` counts: ${JSON.stringify(result.counts)}`);
1514
+ logger.log(` ingested: ${ingested}`);
1515
+ process.exit(0);
1516
+ } catch (err) {
1517
+ fail(spinner, err, options.json);
1518
+ }
1519
+ }
1520
+
1383
1521
  // ─── Phase 10.3 — cc hub aichat <verb> wizard subcommand surface ─────
1384
1522
  //
1385
1523
  // Mirrors the WS topics (personal-data-hub.aichat-*) so scripts and the
@@ -3188,6 +3326,29 @@ export function registerHubCommand(program) {
3188
3326
  .option("--json", "Output JSON")
3189
3327
  .action(cmdCollectWechat);
3190
3328
 
3329
+ hub
3330
+ .command("collect-qzone")
3331
+ .description(
3332
+ "QQ空间 (Qzone): collect 说说/留言板/相册 via the Qzone CGI API. No local DB — needs the qzone-domain p_skey cookie (from the in-app WebView or a browser login to user.qzone.qq.com; base .qq.com skey is rejected). Derives g_tk from p_skey and ingests as post/message/media events.",
3333
+ )
3334
+ .option(
3335
+ "--cookie <cookie>",
3336
+ "Qzone cookie string (must contain uin + p_skey)",
3337
+ )
3338
+ .option(
3339
+ "--cookie-file <path>",
3340
+ "File holding the cookie (preferred — keeps it out of argv)",
3341
+ )
3342
+ .option("--uin <uin>", "Account uin (else parsed from cookie)")
3343
+ .option(
3344
+ "--what <list>",
3345
+ "Comma list: shuoshuo,msgb,album (default all)",
3346
+ "shuoshuo,msgb,album",
3347
+ )
3348
+ .option("--max <n>", "Max items per source", "1000")
3349
+ .option("--json", "Output JSON")
3350
+ .action(cmdCollectQzone);
3351
+
3191
3352
  hub
3192
3353
  .command("event-detail <eventId>")
3193
3354
  .description(
@@ -347,6 +347,23 @@ export class MCPClient extends EventEmitter {
347
347
  };
348
348
  } catch (err) {
349
349
  entry.state = ServerState.ERROR;
350
+ // A stdio child spawned above but failed to initialize (handshake
351
+ // timeout / broken pipe / non-MCP command) would otherwise leak: we
352
+ // delete the entry from `this.servers` below, so disconnect() can never
353
+ // reach it, and an alive-but-unresponsive process fires neither `close`
354
+ // nor `error` — it would run orphaned with its stdio listeners bound for
355
+ // the lifetime of the CLI. Tear it down on the way out (best-effort;
356
+ // never mask the original connect error).
357
+ if (entry.process) {
358
+ try {
359
+ entry.process.stdout?.removeAllListeners();
360
+ entry.process.stderr?.removeAllListeners();
361
+ entry.process.removeAllListeners();
362
+ entry.process.kill();
363
+ } catch {
364
+ // teardown is best-effort — the connect error is what matters
365
+ }
366
+ }
350
367
  this.servers.delete(name);
351
368
  throw err;
352
369
  }
@@ -91,6 +91,36 @@ function tempIndexPath(dir) {
91
91
  return path.join(dir, `cc-checkpoint-index-${process.pid}-${Date.now()}`);
92
92
  }
93
93
 
94
+ /**
95
+ * rmSync that never throws. Used for best-effort teardown (temp index files,
96
+ * created-since files) so a transient unlink failure — e.g. a Windows file
97
+ * lock (EBUSY/EPERM) on the temp index — can't mask the operation's real
98
+ * result. Without this, a `finally { rmSync(tmpIndex) }` could turn a
99
+ * *successful* checkpoint into a thrown error, or replace the original git
100
+ * error with the cleanup error.
101
+ */
102
+ function rmQuiet(target) {
103
+ try {
104
+ rmSync(target, { force: true });
105
+ } catch {
106
+ /* best-effort cleanup — the operation's real result is what matters */
107
+ }
108
+ }
109
+
110
+ /**
111
+ * True if `abs` is the repo root or lives inside it (containment guard).
112
+ * Both sides go through path.resolve so the comparison is robust to separator
113
+ * style — `repoRoot()` comes from `git rev-parse --show-toplevel` with forward
114
+ * slashes, while path.resolve yields native (backslash) paths on Windows.
115
+ */
116
+ export function withinRoot(root, abs) {
117
+ const r = path.resolve(root);
118
+ const a = path.resolve(abs);
119
+ if (a === r) return true;
120
+ const prefix = r.endsWith(path.sep) ? r : r + path.sep;
121
+ return a.startsWith(prefix);
122
+ }
123
+
94
124
  /**
95
125
  * Snapshot the current working tree to a git tree object WITHOUT creating a
96
126
  * commit and WITHOUT touching the real index. Returns the tree sha.
@@ -107,7 +137,7 @@ function snapshotTree(root, dir) {
107
137
  git(["add", "-A"], { cwd: root, env });
108
138
  return git(["write-tree"], { cwd: root, env });
109
139
  } finally {
110
- rmSync(tmpIndex, { force: true });
140
+ rmQuiet(tmpIndex);
111
141
  }
112
142
  }
113
143
 
@@ -228,7 +258,7 @@ export function createCheckpoint(cwd = process.cwd(), opts = {}) {
228
258
 
229
259
  return { id, ref, commit, tree, parent, label, session, createdAt, files };
230
260
  } finally {
231
- rmSync(tmpIndex, { force: true });
261
+ rmQuiet(tmpIndex);
232
262
  }
233
263
  }
234
264
 
@@ -380,12 +410,19 @@ export function rewindTo(cwd = process.cwd(), idOrRef, opts = {}) {
380
410
  git(["read-tree", targetTree], { cwd: root, env });
381
411
  git(["checkout-index", "-a", "-f"], { cwd: root, env });
382
412
  } finally {
383
- rmSync(tmpIndex, { force: true });
413
+ rmQuiet(tmpIndex);
384
414
  }
385
415
 
386
- // Remove files the target snapshot does not contain (created since).
416
+ // Remove files the target snapshot does not contain (created since). These
417
+ // paths come from git's tree diff (repo-relative, git rejects `..` in tree
418
+ // entries), but this is a force-delete over the user's working tree — guard
419
+ // each resolved path against the repo root before unlinking, as
420
+ // defense-in-depth, and keep it best-effort so one locked file can't abort
421
+ // the whole rewind.
387
422
  for (const rel of added) {
388
- rmSync(path.resolve(root, rel), { force: true });
423
+ const abs = path.resolve(root, rel);
424
+ if (!withinRoot(root, abs)) continue; // never delete outside the repo
425
+ rmQuiet(abs);
389
426
  }
390
427
 
391
428
  return {
@@ -62,14 +62,27 @@ export class CostBudget {
62
62
  * @returns {object} the estimateCost() result for this record
63
63
  */
64
64
  add({ provider, model, usage } = {}) {
65
+ // Prompt-cache tokens are billed too — cache CREATION at ~125% of the input
66
+ // rate (Anthropic), cache READ at 10–50%. The usage events carry
67
+ // cache_read_input_tokens / cache_creation_input_tokens, and `cc cost`
68
+ // already prices them; the hard --max-budget-usd cap must count them as well
69
+ // or it undercounts real spend on a cached Anthropic run and stops too late.
70
+ const cacheReadTokens = usage?.cache_read_input_tokens || 0;
71
+ const cacheCreationTokens = usage?.cache_creation_input_tokens || 0;
65
72
  const est = estimateCost({
66
73
  provider,
67
74
  model,
68
75
  inputTokens: usage?.input_tokens || 0,
69
76
  outputTokens: usage?.output_tokens || 0,
77
+ cacheReadTokens,
78
+ cacheCreationTokens,
70
79
  table: this.table,
71
80
  });
72
- const tokens = (usage?.input_tokens || 0) + (usage?.output_tokens || 0);
81
+ const tokens =
82
+ (usage?.input_tokens || 0) +
83
+ (usage?.output_tokens || 0) +
84
+ cacheReadTokens +
85
+ cacheCreationTokens;
73
86
  if (est.free) {
74
87
  this.sawFree = true;
75
88
  } else if (est.matched) {
@@ -749,6 +749,16 @@ export function verifyMultisigProvenance(envelope, policy) {
749
749
  if (prov.signers.length !== prov.partial_sigs.length) {
750
750
  return { ok: false, code: "SIGNERS_SIGS_LENGTH_MISMATCH" };
751
751
  }
752
+ // A group of N members cannot produce more than N distinct signers; a
753
+ // provenance that lists more is self-inconsistent (and, without a
754
+ // policy.members roster below, would otherwise pass).
755
+ if (prov.signers.length > prov.member_count_n) {
756
+ return {
757
+ ok: false,
758
+ code: "TOO_MANY_SIGNERS",
759
+ detail: `${prov.signers.length} > ${prov.member_count_n}`,
760
+ };
761
+ }
752
762
  const requiredM =
753
763
  policy && Number.isInteger(policy.m) ? policy.m : prov.threshold_m;
754
764
  if (prov.partial_sigs.length < requiredM) {
@@ -764,6 +774,13 @@ export function verifyMultisigProvenance(envelope, policy) {
764
774
  return { ok: false, code: "SIGNERS_NOT_SORTED" };
765
775
  }
766
776
  }
777
+ // Signers must be distinct: duplicates (e.g. ["a","a","a"]) would otherwise
778
+ // let a single member meet an M-of-N threshold by repeating themselves.
779
+ for (let i = 1; i < sorted.length; i++) {
780
+ if (sorted[i] === sorted[i - 1]) {
781
+ return { ok: false, code: "DUPLICATE_SIGNER", detail: sorted[i] };
782
+ }
783
+ }
767
784
  const allowed =
768
785
  policy && Array.isArray(policy.members)
769
786
  ? new Set(policy.members.map((m) => m.did))
@@ -193,7 +193,9 @@ export function listIncidents(filter = {}) {
193
193
  if (filter.severity) {
194
194
  incidents = incidents.filter((i) => i.severity === filter.severity);
195
195
  }
196
- if (filter.resolved === false) {
196
+ if (filter.resolved === true) {
197
+ incidents = incidents.filter((i) => !!i.resolvedAt);
198
+ } else if (filter.resolved === false) {
197
199
  incidents = incidents.filter((i) => !i.resolvedAt);
198
200
  }
199
201
  const limit = filter.limit || 50;
@@ -31,16 +31,30 @@ const HOOK_DECISIONS = Object.freeze({
31
31
  CONTINUE: "continue",
32
32
  });
33
33
 
34
+ /** JSON.parse that returns undefined (not throws) on failure. */
35
+ function tryJson(text) {
36
+ try {
37
+ return JSON.parse(text);
38
+ } catch {
39
+ return undefined;
40
+ }
41
+ }
42
+
34
43
  /** Parse a hook's stdout JSON into a normalized decision, or null if not JSON. */
35
44
  function tryParseDecision(stdout) {
36
45
  const text = String(stdout || "").trim();
37
46
  if (!text || text[0] !== "{") return null;
38
- let obj;
39
- try {
40
- obj = JSON.parse(text);
41
- } catch {
42
- return null;
47
+ let obj = tryJson(text);
48
+ if (obj === undefined) {
49
+ // A hook that emits its decision as a JSON line followed by diagnostics
50
+ // (`{"decision":"block"}\n…log lines…`) would fail a whole-text parse, so
51
+ // the documented JSON-to-block pattern would be silently dropped and the
52
+ // tool would proceed. Recover the decision from the first line. (Pure
53
+ // pretty-printed JSON still parses via the whole-text attempt above.)
54
+ const firstLine = text.split("\n", 1)[0].trim();
55
+ if (firstLine && firstLine[0] === "{") obj = tryJson(firstLine);
43
56
  }
57
+ if (obj === undefined || obj === null || typeof obj !== "object") return null;
44
58
  // PreToolUse-specific permission decision
45
59
  const hso = obj.hookSpecificOutput;
46
60
  if (hso && hso.permissionDecision) {
@@ -148,7 +162,22 @@ function runCommandHook(command, input = {}, opts = {}) {
148
162
  if (exitCode === 0) {
149
163
  const parsed = tryParseDecision(stdout);
150
164
  if (parsed) return { ...parsed, exitCode, stdout, stderr };
151
- return { decision: HOOK_DECISIONS.CONTINUE, reason: null, exitCode, stdout, stderr };
165
+ // stdout that *looks* like a decision (`{`-leading) but parses as neither
166
+ // whole-text nor first-line JSON is most likely a corrupt/truncated
167
+ // decision — don't swallow it silently (a block hook could be neutered).
168
+ // The exit code (0) still governs the flow (continue), but flag it so the
169
+ // caller/logs can surface the misconfiguration.
170
+ const looksLikeDecision = stdout.trim()[0] === "{";
171
+ return {
172
+ decision: HOOK_DECISIONS.CONTINUE,
173
+ reason: looksLikeDecision
174
+ ? "hook stdout looked like a decision but did not parse as JSON"
175
+ : null,
176
+ exitCode,
177
+ stdout,
178
+ stderr,
179
+ ...(looksLikeDecision ? { malformedDecision: true } : {}),
180
+ };
152
181
  }
153
182
  // Other non-zero → non-blocking error.
154
183
  return {
package/src/lib/loop.js CHANGED
@@ -108,15 +108,22 @@ export function makeSleep(signal) {
108
108
  // process alive between rounds. Under a TTY the active stdin would mask
109
109
  // an unref'd timer, but headless (piped stdin / CI / cron) the loop would
110
110
  // exit after the first round. SIGINT aborts the wait via the signal.
111
- const t = setTimeout(resolve, ms);
112
- signal?.addEventListener(
113
- "abort",
114
- () => {
115
- clearTimeout(t);
116
- resolve();
117
- },
118
- { once: true },
119
- );
111
+ let t;
112
+ const onAbort = () => {
113
+ clearTimeout(t);
114
+ resolve();
115
+ };
116
+ t = setTimeout(() => {
117
+ // Normal completion: drop the abort listener so it can't accumulate on
118
+ // a shared signal across rounds. A long `cc loop` reuses one
119
+ // AbortController for the whole run, and `{once:true}` only removes the
120
+ // listener if it FIRES — a sleep that completes normally never aborts,
121
+ // so without this each round would leak a dangling listener (N rounds →
122
+ // N retained closures + a MaxListenersExceededWarning past 10).
123
+ signal?.removeEventListener("abort", onAbort);
124
+ resolve();
125
+ }, ms);
126
+ signal?.addEventListener("abort", onAbort, { once: true });
120
127
  });
121
128
  }
122
129
 
@@ -206,10 +206,14 @@ function matchCommand(pattern, command) {
206
206
  function matchUrl(pattern, url) {
207
207
  const u = String(url || "");
208
208
  if (pattern.startsWith("domain:")) {
209
- const host = pattern.slice("domain:".length).trim();
209
+ // Domains are case-insensitive (and `new URL().host` already lowercases the
210
+ // host), so lowercase the pattern too — otherwise a rule with any uppercase
211
+ // (`domain:Example.com`) silently never matches, turning a deny rule into a
212
+ // bypass and an allow rule into a no-op.
213
+ const host = pattern.slice("domain:".length).trim().toLowerCase();
210
214
  let actualHost = "";
211
215
  try {
212
- actualHost = new URL(u).host;
216
+ actualHost = new URL(u).host.toLowerCase();
213
217
  } catch {
214
218
  return false;
215
219
  }
@@ -120,6 +120,15 @@ export function parseSkillMd(content) {
120
120
  .slice(2)
121
121
  .trim()
122
122
  .replace(/^['"]|['"]$/g, "");
123
+ // Lazily start a block array on the first item under an empty-value key
124
+ // (`tools:\n - Read\n - Write`). Without this, currentArray was never
125
+ // assigned anywhere, so block-style YAML lists were silently dropped —
126
+ // e.g. an `allowed-tools:` list would vanish and its restriction be lost.
127
+ // Only create when the key carried no inline value (data[key] still
128
+ // undefined) so a scalar isn't clobbered by a stray dash.
129
+ if (!currentArray && currentKey && data[currentKey] === undefined) {
130
+ currentArray = data[currentKey] = [];
131
+ }
123
132
  if (currentArray) currentArray.push(value);
124
133
  continue;
125
134
  }
@@ -12,21 +12,50 @@ import { URL } from "url";
12
12
  const DEFAULT_MAX_BYTES = 2_000_000;
13
13
  const DEFAULT_TIMEOUT_MS = 15_000;
14
14
 
15
- // RFC 1918 + loopback + link-local. Blocked by default unless config.allowPrivateHosts.
15
+ // RFC 1918 + loopback + link-local (IPv4 and IPv6). Blocked by default unless
16
+ // config.allowPrivateHosts. Decimal/integer IPv4 forms (http://2130706433,
17
+ // http://0) are normalized to dotted-decimal by `new URL()` before they reach
18
+ // here, so the IPv4 patterns catch them too.
16
19
  const PRIVATE_HOST_PATTERNS = [
17
20
  /^127\./,
18
21
  /^10\./,
19
22
  /^192\.168\./,
20
23
  /^172\.(1[6-9]|2\d|3[01])\./,
21
- /^169\.254\./,
24
+ /^169\.254\./, // link-local incl. cloud metadata 169.254.169.254
22
25
  /^0\.0\.0\.0$/,
23
- /^::1$/,
26
+ /^0$/, // bare 0 → 0.0.0.0
27
+ /^::1$/, // IPv6 loopback
28
+ /^::$/, // IPv6 unspecified (== 0.0.0.0)
29
+ /^f[cd][0-9a-f]{2}:/, // fc00::/7 unique-local (ULA)
30
+ /^fe[89ab][0-9a-f]:/, // fe80::/10 link-local
24
31
  /^localhost$/i,
25
32
  ];
26
33
 
34
+ // Decode the IPv4 embedded in an IPv4-mapped IPv6 address back to dotted form,
35
+ // or null if `h` is not such an address. `new URL()` normalizes the embedded
36
+ // IPv4 to hex (::ffff:127.0.0.1 → ::ffff:7f00:1), so without decoding, a fetch
37
+ // to [::ffff:127.0.0.1] would bypass the IPv4 loopback/private rules.
38
+ function mappedIPv4(h) {
39
+ const m = /^::ffff:(.+)$/.exec(h);
40
+ if (!m) return null;
41
+ const tail = m[1];
42
+ if (/^\d{1,3}(\.\d{1,3}){3}$/.test(tail)) return tail; // already dotted
43
+ const hx = /^([0-9a-f]{1,4}):([0-9a-f]{1,4})$/.exec(tail);
44
+ if (!hx) return null;
45
+ const a = parseInt(hx[1], 16);
46
+ const b = parseInt(hx[2], 16);
47
+ return [(a >> 8) & 255, a & 255, (b >> 8) & 255, b & 255].join(".");
48
+ }
49
+
27
50
  export function isPrivateHost(host) {
28
51
  if (!host) return true;
29
- return PRIVATE_HOST_PATTERNS.some((re) => re.test(host));
52
+ let h = String(host).toLowerCase();
53
+ // `new URL().hostname` returns IPv6 hosts bracketed ("[::1]") — strip so the
54
+ // IPv6 patterns (and the previously-dead ::1 rule) actually match.
55
+ if (h.startsWith("[") && h.endsWith("]")) h = h.slice(1, -1);
56
+ const v4 = mappedIPv4(h);
57
+ if (v4) h = v4; // re-check the embedded IPv4 against the IPv4 rules
58
+ return PRIVATE_HOST_PATTERNS.some((re) => re.test(h));
30
59
  }
31
60
 
32
61
  export function checkAllowed(urlStr, config = {}) {
@@ -1562,6 +1562,18 @@ export async function startAgentRepl(options = {}) {
1562
1562
  model = arg;
1563
1563
  _curModel = model; // keep the status-line readout in sync
1564
1564
  logger.info(`Model: ${chalk.cyan(model)}`);
1565
+ // Claude-Code 2.1.183 parity: warn when the newly-selected model is a
1566
+ // provider-retired/deprecated snapshot. Headless paths already warn via
1567
+ // maybeWarnDeprecatedModel; the interactive /model switch did not, so a
1568
+ // user could silently switch to a retired id and only learn of it when
1569
+ // the next turn fails with an opaque "model not found".
1570
+ try {
1571
+ const { maybeWarnDeprecatedModel } =
1572
+ await import("../lib/model-deprecation.js");
1573
+ maybeWarnDeprecatedModel({ model });
1574
+ } catch {
1575
+ // deprecation notice is best-effort
1576
+ }
1565
1577
  } else {
1566
1578
  logger.info(`Current model: ${chalk.cyan(model)}`);
1567
1579
  }
@@ -768,6 +768,20 @@ export async function runAgentHeadless(options = {}, deps = {}) {
768
768
  // chatFn passthrough lets tests drive the loop deterministically.
769
769
  chatFn: deps.chatFn || options.chatFn || undefined,
770
770
  signal: options.signal || undefined,
771
+ // Stream-stall hint (Claude-Code 2.1.185): when the connection is alive but
772
+ // the API has gone silent mid-response, a headless run would otherwise look
773
+ // frozen with no feedback. The REPL already surfaces this; mirror it here to
774
+ // stderr — out-of-band for every output format (text answer / json envelope
775
+ // / stream-json NDJSON all go to stdout) so machine consumers are unaffected
776
+ // while a human watching a long `cc agent -p` run learns we're still waiting
777
+ // and, when a hard inactivity timeout is set, when it will auto-retry. Plain
778
+ // text (no chalk) since headless stderr is frequently piped/non-TTY.
779
+ onStall: (ms, timeoutMs) => {
780
+ const silent = Math.round(ms / 1000);
781
+ const retryIn = timeoutMs > ms ? Math.round((timeoutMs - ms) / 1000) : 0;
782
+ const suffix = retryIn > 0 ? ` · will retry in ${retryIn}s` : "";
783
+ writeErr(` ⏳ waiting for API response (silent ${silent}s)${suffix}…\n`);
784
+ },
771
785
  };
772
786
 
773
787
  // Goal binding (cc goal, Phase 1). `--goal <id>` binds explicitly; `--goal`
@@ -1 +0,0 @@
1
- import{I as v,P as u,J as S,U as n,R as a,S as y,K as C,Q as x,V as w,_ as k,b as s}from"./vendor-BvqAck49.js";import{_ as B}from"./index-DlHHHyRx.js";import"./icons-DP3uiYxy.js";const M={__name:"ChatBubbleRenderer",props:{event:{type:Object,required:!0}},setup(c,{expose:d}){d();const t=c,r=s(()=>{const e=t.event.content||{};return e.text||e.body||e.message||e.title||JSON.stringify(e).slice(0,200)}),i=s(()=>{const e=t.event.content||{};return e.from||e.sender||e.senderName||t.event.actor||"(unknown)"}),l=s(()=>{const e=(t.event.actor||"").toLowerCase();return e.includes("self")||e==="me"||e.endsWith("_self")}),o=s(()=>{const e=t.event.source.adapter||"";return e.startsWith("messaging-qq")?"magenta":e==="wechat"?"green":"blue"}),m=s(()=>{if(!t.event.occurredAt)return"";try{const e=new Date(t.event.occurredAt),p=e.getFullYear(),b=String(e.getMonth()+1).padStart(2,"0"),f=String(e.getDate()).padStart(2,"0"),g=String(e.getHours()).padStart(2,"0"),h=String(e.getMinutes()).padStart(2,"0");return`${p}-${b}-${f} ${g}:${h}`}catch{return""}}),_={props:t,messageText:r,actorLabel:i,isMine:l,adapterColor:o,formattedTime:m,computed:s};return Object.defineProperty(_,"__isScriptSetup",{enumerable:!1,value:!0}),_}},N={class:"bubble"},T={class:"meta"},R={class:"actor"},V={class:"time"},q={class:"body"};function D(c,d,t,r,i,l){const o=v("a-tag");return u(),S("div",{class:k(["chat-row",{mine:r.isMine}])},[n("div",N,[n("div",T,[n("span",R,a(r.actorLabel),1),n("span",V,a(r.formattedTime),1)]),n("div",q,a(r.messageText),1),t.event.source.adapter?(u(),y(o,{key:0,class:"src",color:r.adapterColor},{default:C(()=>[x(a(t.event.source.adapter),1)]),_:1},8,["color"])):w("v-if",!0)])],2)}const O=B(M,[["render",D],["__scopeId","data-v-49238629"],["__file","/tmp/cc-web-panel-kIMJb5/repo/packages/web-panel/src/components/pdh/renderers/ChatBubbleRenderer.vue"]]);export{O as default};