chainlesschain 0.162.141 → 0.162.143

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 (164) hide show
  1. package/bin/chainlesschain.js +5 -3
  2. package/package.json +2 -2
  3. package/src/assets/web-panel/assets/{AIOps-DHSKT_Kw.js → AIOps-Bq1t-B4V.js} +1 -1
  4. package/src/assets/web-panel/assets/{ActionButton-D9g8IXSh.js → ActionButton-BHb3KMc3.js} +1 -1
  5. package/src/assets/web-panel/assets/{Analytics-BOybzlhP.js → Analytics-DtJrE99O.js} +3 -3
  6. package/src/assets/web-panel/assets/{AppLayout-CGYlunfU.js → AppLayout-HXt309zh.js} +5 -5
  7. package/src/assets/web-panel/assets/{Audit-CyLdwiKk.js → Audit-B-0384hX.js} +1 -1
  8. package/src/assets/web-panel/assets/{Backup-CrwuuvpA.js → Backup-BXNwUJ7y.js} +1 -1
  9. package/src/assets/web-panel/assets/{BaseInput-DZwbbupy.js → BaseInput-CYdSBe24.js} +1 -1
  10. package/src/assets/web-panel/assets/{Chat-B2LSHLfs.js → Chat-C5hSt-bf.js} +5 -5
  11. package/src/assets/web-panel/assets/{ChatBubbleRenderer-CBfsPCW2.js → ChatBubbleRenderer-BJQ6SG49.js} +1 -1
  12. package/src/assets/web-panel/assets/{Checkbox-BjqqK9kp.js → Checkbox-DZSpGJs8.js} +1 -1
  13. package/src/assets/web-panel/assets/{Codegen-gExdY73m.js → Codegen-BKfMmpq3.js} +1 -1
  14. package/src/assets/web-panel/assets/{Col-C3zy_YrV.js → Col-DgsxR9Gb.js} +1 -1
  15. package/src/assets/web-panel/assets/{Community-BmvHMdZm.js → Community-B3GhKOp_.js} +1 -1
  16. package/src/assets/web-panel/assets/{Compact-Bce53fzv.js → Compact-Dtk1xHSY.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compliance-BWHc_PDx.js → Compliance-DmkkUDze.js} +1 -1
  18. package/src/assets/web-panel/assets/{Cowork-BevGQSVc.js → Cowork-CB_Tby_i.js} +2 -2
  19. package/src/assets/web-panel/assets/{Cron-BLe98ST1.js → Cron-BLQFn3UU.js} +2 -2
  20. package/src/assets/web-panel/assets/{Crosschain-CcKFUfq5.js → Crosschain-LCVSqtiL.js} +1 -1
  21. package/src/assets/web-panel/assets/{DID-DiHtK6r6.js → DID-Cv3xor-m.js} +2 -2
  22. package/src/assets/web-panel/assets/{Dashboard-BDsFZ7Fs.js → Dashboard-DHAEYeUk.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dropdown-2Y_2a0o8.js → Dropdown-FAp99VHO.js} +1 -1
  24. package/src/assets/web-panel/assets/{EmailListRenderer-CDo7ThLw.js → EmailListRenderer-Cl4enKIT.js} +1 -1
  25. package/src/assets/web-panel/assets/{FamilyGuardDashboard-DS-FpdxI.js → FamilyGuardDashboard-CgbuK_Rv.js} +1 -1
  26. package/src/assets/web-panel/assets/{Federation-DreLqQ83.js → Federation-C2I5-34o.js} +1 -1
  27. package/src/assets/web-panel/assets/{FormItemContext-ibNHpb5N.js → FormItemContext-4RNlhf3D.js} +1 -1
  28. package/src/assets/web-panel/assets/{GenericCardRenderer-D_SVwVBw.js → GenericCardRenderer-DmaKYzyo.js} +1 -1
  29. package/src/assets/web-panel/assets/{Git-CLoJSOLu.js → Git-BMrCOEGV.js} +2 -2
  30. package/src/assets/web-panel/assets/{Governance-ChoJD5Ad.js → Governance-BLwFR0Vd.js} +1 -1
  31. package/src/assets/web-panel/assets/{Inference-Dj6kj2ik.js → Inference-Cdlx3Y4t.js} +1 -1
  32. package/src/assets/web-panel/assets/{KnowledgeGraph-BGTAUPBs.js → KnowledgeGraph-CiwGb2h3.js} +1 -1
  33. package/src/assets/web-panel/assets/{Logs-CunWZkkF.js → Logs-C6A4r33C.js} +2 -2
  34. package/src/assets/web-panel/assets/{Marketplace-CakUJnCO.js → Marketplace-DdybH2YZ.js} +1 -1
  35. package/src/assets/web-panel/assets/{McpTools-CKHxg1sP.js → McpTools-pgwwEzwg.js} +3 -3
  36. package/src/assets/web-panel/assets/{Memory-B_mw2AyJ.js → Memory-BQ31MLPN.js} +2 -2
  37. package/src/assets/web-panel/assets/{MobileBridge-Cjipj0mU.js → MobileBridge-CnRXuSP5.js} +1 -1
  38. package/src/assets/web-panel/assets/{MobileProjects-BMi3sma3.js → MobileProjects-CmnCTggB.js} +1 -1
  39. package/src/assets/web-panel/assets/{Mtc-B6f2nsT2.js → Mtc-3K6RfaUB.js} +2 -2
  40. package/src/assets/web-panel/assets/{MtcAudit-Dh6PeEcb.js → MtcAudit-BN9hXvXq.js} +2 -2
  41. package/src/assets/web-panel/assets/{Multisig-CTQC41G9.js → Multisig-CRxIT2RV.js} +3 -3
  42. package/src/assets/web-panel/assets/{NLProgramming-CuRDweBQ.js → NLProgramming-CdPvh_dA.js} +1 -1
  43. package/src/assets/web-panel/assets/{Notes-D2fVdUiq.js → Notes-DzQSZ0ps.js} +3 -3
  44. package/src/assets/web-panel/assets/{NotificationSettings-D2r_E8om.js → NotificationSettings-DkqRVvfb.js} +1 -1
  45. package/src/assets/web-panel/assets/{OrderTableRenderer-C-uX9jZ8.js → OrderTableRenderer-ClTA64ji.js} +1 -1
  46. package/src/assets/web-panel/assets/{Organization-Cs5bzAsJ.js → Organization-B-Pndhhn.js} +4 -4
  47. package/src/assets/web-panel/assets/{Overflow-BYTDh7lA.js → Overflow-Dt56UM9b.js} +1 -1
  48. package/src/assets/web-panel/assets/{P2P-DPdj7uXM.js → P2P-JDGQmED0.js} +2 -2
  49. package/src/assets/web-panel/assets/PdhVaultBrowser-C56Oi811.js +7 -0
  50. package/src/assets/web-panel/assets/{Permissions-CyK09XG_.js → Permissions-Dnqe1sny.js} +4 -4
  51. package/src/assets/web-panel/assets/{PersonalDataHub-DNqRJSzF.js → PersonalDataHub-mTkYbIR-.js} +2 -2
  52. package/src/assets/web-panel/assets/{Pipeline-Bj_sG4hn.js → Pipeline-BG6J9UZO.js} +1 -1
  53. package/src/assets/web-panel/assets/{Privacy-BEvscTpp.js → Privacy-BdEydH6w.js} +1 -1
  54. package/src/assets/web-panel/assets/{ProjectInit-CM8y-eHP.js → ProjectInit-QPxCoUzs.js} +2 -2
  55. package/src/assets/web-panel/assets/{ProjectSettings-CgdKfjOh.js → ProjectSettings-hU_phubM.js} +2 -2
  56. package/src/assets/web-panel/assets/{Projects-DncM3TEh.js → Projects-BBzAEGk4.js} +1 -1
  57. package/src/assets/web-panel/assets/{Providers-DLaVk_5X.js → Providers-DJ_bIDRG.js} +1 -1
  58. package/src/assets/web-panel/assets/{QuickAsk-ClTx4PJt.js → QuickAsk-BNmX7Jgh.js} +1 -1
  59. package/src/assets/web-panel/assets/{Recommend-CF2Qh_4H.js → Recommend-Bo1I5nAG.js} +1 -1
  60. package/src/assets/web-panel/assets/{Reputation-D6VtOEkm.js → Reputation-Bc9mfsh7.js} +1 -1
  61. package/src/assets/web-panel/assets/{Row-BPBVO2Bz.js → Row-D12ZZx5m.js} +1 -1
  62. package/src/assets/web-panel/assets/{RssFeed-PhuLKw-_.js → RssFeed-CXSlmgHl.js} +3 -3
  63. package/src/assets/web-panel/assets/{Search-Dfr9Hf8J.js → Search-bNXObahl.js} +1 -1
  64. package/src/assets/web-panel/assets/{Security-CUZwVWZH.js → Security-Bt7yBkJc.js} +4 -4
  65. package/src/assets/web-panel/assets/{Services-GSyropqf.js → Services-ECQDydWI.js} +2 -2
  66. package/src/assets/web-panel/assets/{Skeleton-C1JLm9Ld.js → Skeleton-BcgzLbTM.js} +1 -1
  67. package/src/assets/web-panel/assets/{Skills-B6XIvm1t.js → Skills-py_xMG8b.js} +1 -1
  68. package/src/assets/web-panel/assets/{Sla-ClKWHZN7.js → Sla-CzNvjwaW.js} +1 -1
  69. package/src/assets/web-panel/assets/{SpeechSettings-CS1BFRxe.js → SpeechSettings-S3zMMqbB.js} +1 -1
  70. package/src/assets/web-panel/assets/{SyncSettings-DnkV0dyL.js → SyncSettings-BpDfhFv6.js} +2 -2
  71. package/src/assets/web-panel/assets/{Tasks-mxakS_p3.js → Tasks-CUn36tpM.js} +1 -1
  72. package/src/assets/web-panel/assets/{Templates-uunZWqWX.js → Templates-yzCxc3lI.js} +1 -1
  73. package/src/assets/web-panel/assets/{Tenant-BgDpoKMQ.js → Tenant-BTMrYsHv.js} +1 -1
  74. package/src/assets/web-panel/assets/{Terminal-DbpCYH4S.js → Terminal-BjZry1Jj.js} +2 -2
  75. package/src/assets/web-panel/assets/TimelineRenderer-BwJ4nL9S.js +1 -0
  76. package/src/assets/web-panel/assets/{Tokens-CPrmCAvn.js → Tokens-B1OwTaT_.js} +1 -1
  77. package/src/assets/web-panel/assets/{Trigger-vqV0vJH0.js → Trigger-BDxi9fib.js} +1 -1
  78. package/src/assets/web-panel/assets/{Trust-D8uYln3s.js → Trust-B_5hXqzI.js} +1 -1
  79. package/src/assets/web-panel/assets/{UkeySign-C76T9c4i.js → UkeySign-BpIMO611.js} +1 -1
  80. package/src/assets/web-panel/assets/{VideoEditing-z7LRi6XN.js → VideoEditing-Ds-9lxOW.js} +1 -1
  81. package/src/assets/web-panel/assets/{Wallet-ByTZx3Zx.js → Wallet-CNfFp8if.js} +3 -3
  82. package/src/assets/web-panel/assets/{WebAuthn-CvbPJS-R.js → WebAuthn-DbCj1-qn.js} +2 -2
  83. package/src/assets/web-panel/assets/{WorkflowEditor-DN4NZPn7.js → WorkflowEditor-DjEeb957.js} +1 -1
  84. package/src/assets/web-panel/assets/{chat-Hduk8M5W.js → chat-BX-27gI4.js} +1 -1
  85. package/src/assets/web-panel/assets/{colors-C4aSpfm9.js → colors-CfM5FmdA.js} +1 -1
  86. package/src/assets/web-panel/assets/{compact-item-BcZZzDuG.js → compact-item-Cb0Fm64g.js} +1 -1
  87. package/src/assets/web-panel/assets/{createContext-gQTY7-rq.js → createContext-D99MZ4gz.js} +1 -1
  88. package/src/assets/web-panel/assets/devWarning-CDh1y3xt.js +1 -0
  89. package/src/assets/web-panel/assets/{hasIn-BMDyRJ_b.js → hasIn-CW7wPkfb.js} +1 -1
  90. package/src/assets/web-panel/assets/{index-C6NL29KD.js → index--Y8IzVi2.js} +1 -1
  91. package/src/assets/web-panel/assets/{index-C5r83RS4.js → index-3TDpYBKK.js} +1 -1
  92. package/src/assets/web-panel/assets/{index-BAME2b4J.js → index-7cahuPKG.js} +1 -1
  93. package/src/assets/web-panel/assets/index-ACeVMo7x.js +1 -0
  94. package/src/assets/web-panel/assets/{index-vzCX5kKE.js → index-B8tI4Hg_.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-B3YTb3-m.js → index-BPXhpO02.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-BDrQn5X7.js → index-BTlOIaQj.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-DxGnNEe8.js → index-BcCSEGzu.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-C_Bma_jt.js → index-Bf7G5mYl.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-BpkYcoxz.js → index-BoCp5SY7.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-DX3vVD_p.js → index-BqOy4TS5.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-BzB9jre7.js → index-CNhZanpM.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-DuLf56zk.js → index-CPmtR1Wr.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-Avyg94fC.js → index-CUENEaPU.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-CokE787j.js → index-CXrNkD5j.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-BhCV8HNu.js → index-CaPluhcX.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-7g2ZIavu.js → index-Cavk4P2D.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-CZycNtw0.js → index-ChGWrUQv.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-Bgb3FpWr.js → index-ClSExghA.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-DUyKn657.js → index-CvFAgMxx.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-CJQNyLLn.js → index-CxFhtfoo.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-DxkncAgs.js → index-CxG5S21O.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-C7w6ptCa.js → index-CzwoBX8m.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-B-HaNhiQ.js → index-D8U1HFcm.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-C2Cq0uOo.js → index-DAIk483F.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-Cu4kyuM9.js → index-DAd31-6W.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-0siNWZzF.js → index-DDGBasj6.js} +3 -3
  117. package/src/assets/web-panel/assets/{index-YzZY9C4u.js → index-DFRrP3LL.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-DCmSxOLR.js → index-DUxnWbi6.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-BS2MmwPs.js → index-DZjN-U7A.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-CXkWwgZH.js → index-DcRbrdqR.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-DhvhKjn1.js → index-Dt-h8idq.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-CBrZEuWf.js → index-DwhDhfZ8.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-BPAdzgkJ.js → index-ab4T5cPr.js} +1 -1
  124. package/src/assets/web-panel/assets/index-gYKDzWA9.js +1 -0
  125. package/src/assets/web-panel/assets/{index-DeAd5kjz.js → index-kRlIAKNw.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-L2-DzQDE.js → index-kt0S8nXp.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-CsznkthI.js → index-rIKv98lY.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-Dy7qY2oa.js → index-yBiqcb9Z.js} +1 -1
  129. package/src/assets/web-panel/assets/{initDefaultProps-BB0LhORQ.js → initDefaultProps-mzQV01we.js} +1 -1
  130. package/src/assets/web-panel/assets/{motion-BbZGG8Xd.js → motion-DgWAtKps.js} +1 -1
  131. package/src/assets/web-panel/assets/{move-xWt9T2uE.js → move-Dm9NMjIw.js} +1 -1
  132. package/src/assets/web-panel/assets/{mtc-parser-DK5uoYkZ.js → mtc-parser-BpuQIoKH.js} +1 -1
  133. package/src/assets/web-panel/assets/{omit-CpO5euLm.js → omit-C507FdFH.js} +1 -1
  134. package/src/assets/web-panel/assets/{pickAttrs-BoRbPCt6.js → pickAttrs-BL5eykDR.js} +1 -1
  135. package/src/assets/web-panel/assets/{placementArrow-Baz9Bw0X.js → placementArrow-uSE1U6yg.js} +1 -1
  136. package/src/assets/web-panel/assets/{responsiveObserve-DtI9TKvU.js → responsiveObserve-BVhSz_U1.js} +1 -1
  137. package/src/assets/web-panel/assets/{slide-CAdWovo_.js → slide-CWCp5XZc.js} +1 -1
  138. package/src/assets/web-panel/assets/{statusUtils-C3G4_zkW.js → statusUtils-B_rS9tlX.js} +1 -1
  139. package/src/assets/web-panel/assets/{styleChecker-KOEg3Waq.js → styleChecker-DRo4TNq4.js} +1 -1
  140. package/src/assets/web-panel/assets/{useFlexGapSupport-CnjZzTrc.js → useFlexGapSupport-QsPzwpvi.js} +1 -1
  141. package/src/assets/web-panel/assets/{useFs-Vv4rxsEX.js → useFs-DhsLsCr_.js} +1 -1
  142. package/src/assets/web-panel/assets/{usePersonalDataHub-W3UdGGh6.js → usePersonalDataHub-BXI-FLwS.js} +1 -1
  143. package/src/assets/web-panel/assets/{vnode-CtmUaEkL.js → vnode-CYE0CVkI.js} +1 -1
  144. package/src/assets/web-panel/assets/{zoom-siEw2_N4.js → zoom-B2-MtXVF.js} +1 -1
  145. package/src/assets/web-panel/index.html +1 -1
  146. package/src/command-manifest.json +1126 -0
  147. package/src/commands/note.js +2 -1
  148. package/src/commands/search.js +2 -1
  149. package/src/harness/worktree-isolator.js +14 -7
  150. package/src/index.js +4 -12
  151. package/src/lazy-dispatch.js +107 -0
  152. package/src/lib/ensure-utf8.js +25 -7
  153. package/src/lib/knowledge-exporter.js +3 -2
  154. package/src/lib/permission-engine.js +1 -1
  155. package/src/lib/session-manager.js +2 -1
  156. package/src/program-base.js +24 -0
  157. package/src/repl/agent-repl.js +25 -1
  158. package/src/repl/think-command.js +31 -0
  159. package/src/runtime/file-ref-expander.js +100 -14
  160. package/src/assets/web-panel/assets/PdhVaultBrowser-B5owsY_X.js +0 -7
  161. package/src/assets/web-panel/assets/TimelineRenderer-MPUsr2be.js +0 -1
  162. package/src/assets/web-panel/assets/devWarning-Xj7YW6_0.js +0 -1
  163. package/src/assets/web-panel/assets/index-BXV40xrU.js +0 -1
  164. package/src/assets/web-panel/assets/index-BdBtw5m8.js +0 -1
@@ -8,6 +8,7 @@ import { numericOption } from "../lib/cli-numeric.js";
8
8
  import { logger } from "../lib/logger.js";
9
9
  import { bootstrap, shutdown } from "../runtime/bootstrap.js";
10
10
  import { likePrefix } from "../lib/sql-like.js";
11
+ import { safeJsonParse } from "../lib/safe-json.js";
11
12
  import {
12
13
  ensureVersionsTable,
13
14
  saveVersion,
@@ -204,7 +205,7 @@ export function registerNoteCommand(program) {
204
205
  } else {
205
206
  logger.log(chalk.bold(`Notes (${notes.length}):\n`));
206
207
  for (const n of notes) {
207
- const tags = JSON.parse(n.tags || "[]");
208
+ const tags = safeJsonParse(n.tags, []);
208
209
  const tagStr =
209
210
  tags.length > 0 ? chalk.gray(` [${tags.join(", ")}]`) : "";
210
211
  logger.log(
@@ -8,6 +8,7 @@ import ora from "ora";
8
8
  import { logger } from "../lib/logger.js";
9
9
  import { bootstrap, shutdown } from "../runtime/bootstrap.js";
10
10
  import { BM25Search } from "../lib/bm25-search.js";
11
+ import { safeJsonParse } from "../lib/safe-json.js";
11
12
 
12
13
  /**
13
14
  * Ensure the notes table exists (same as note.js)
@@ -213,7 +214,7 @@ export function registerSearchCommand(program) {
213
214
  ),
214
215
  );
215
216
  for (const r of results) {
216
- const tags = JSON.parse(r.doc.tags || "[]");
217
+ const tags = safeJsonParse(r.doc.tags, []);
217
218
  const tagStr =
218
219
  tags.length > 0 ? chalk.gray(` [${tags.join(", ")}]`) : "";
219
220
  const snippet = (r.doc.content || "")
@@ -857,23 +857,30 @@ function _resolveBaseBranchForMerge(repoDir, baseBranch) {
857
857
  }
858
858
 
859
859
  function _applyAutomationResolution(repoDir, candidateId, filePath) {
860
+ // filePath here is a conflicted filename straight from `git diff --name-only`
861
+ // — i.e. it reflects whatever names exist in the (possibly untrusted) branch
862
+ // being merged. The sibling public functions assertSafeGitPath their filePath,
863
+ // but this internal helper interpolated it raw into a shell command string, so
864
+ // a file named `x"; rm -rf ~; ".txt` was a command-injection vector. Use the
865
+ // array-form gitExecArgs (spawnSync, no shell) so the path is a literal arg —
866
+ // injection-proof and correct for filenames with spaces/special chars too.
860
867
  switch (candidateId) {
861
868
  case "accept-current":
862
- gitExec(`checkout --theirs -- "${filePath}"`, repoDir);
863
- gitExec(`add "${filePath}"`, repoDir);
869
+ gitExecArgs(["checkout", "--theirs", "--", filePath], repoDir);
870
+ gitExecArgs(["add", "--", filePath], repoDir);
864
871
  return;
865
872
  case "accept-incoming":
866
873
  case "restore-incoming":
867
- gitExec(`checkout --ours -- "${filePath}"`, repoDir);
868
- gitExec(`add "${filePath}"`, repoDir);
874
+ gitExecArgs(["checkout", "--ours", "--", filePath], repoDir);
875
+ gitExecArgs(["add", "--", filePath], repoDir);
869
876
  return;
870
877
  case "restore-current":
871
- gitExec(`checkout --theirs -- "${filePath}"`, repoDir);
872
- gitExec(`add "${filePath}"`, repoDir);
878
+ gitExecArgs(["checkout", "--theirs", "--", filePath], repoDir);
879
+ gitExecArgs(["add", "--", filePath], repoDir);
873
880
  return;
874
881
  case "confirm-delete":
875
882
  case "accept-delete":
876
- gitExec(`rm -- "${filePath}"`, repoDir);
883
+ gitExecArgs(["rm", "--", filePath], repoDir);
877
884
  return;
878
885
  default:
879
886
  throw new Error(`Unsupported automation resolution: ${candidateId}`);
package/src/index.js CHANGED
@@ -1,5 +1,4 @@
1
- import { Command } from "commander";
2
- import { VERSION } from "./constants.js";
1
+ import { createBaseProgram } from "./program-base.js";
3
2
  import { maybeNotifyUpdate } from "./lib/update-notice.js";
4
3
  import { registerSetupCommand } from "./commands/setup.js";
5
4
  import { registerStartCommand } from "./commands/start.js";
@@ -391,16 +390,9 @@ import { registerPipoV2Commands } from "./commands/pipeline.js";
391
390
  * opts.allowedCommands takes precedence over the env var.
392
391
  */
393
392
  export function createProgram(opts = {}) {
394
- const program = new Command();
395
-
396
- program
397
- .name("chainlesschain")
398
- .description(
399
- "CLI for ChainlessChain - install, configure, and manage your personal AI management system",
400
- )
401
- .version(VERSION, "-v, --version")
402
- .option("--verbose", "Enable verbose output")
403
- .option("--quiet", "Suppress non-essential output");
393
+ // Base (name/version/global options) is shared with the lazy fast path
394
+ // (src/lazy-dispatch.js) via program-base.js so they can never drift.
395
+ const program = createBaseProgram();
404
396
 
405
397
  // Passive update nudge (Claude-Code parity): one sync cache read + gray
406
398
  // stderr line when a newer npm version is known; stale cache refreshes in a
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Lazy CLI dispatch — the cold-start optimization.
3
+ *
4
+ * Importing src/index.js eagerly loads all ~154 command modules + their
5
+ * transitive dependency graph (~2.7s; see memory
6
+ * cli_cold_start_bottleneck_hub_repl). But a real `cc <cmd>` invocation only
7
+ * needs ONE command's module. This dispatcher resolves the target command from
8
+ * argv against the generated manifest (src/command-manifest.json) and imports
9
+ * just that module, then parses.
10
+ *
11
+ * SAFETY: anything uncertain — no command, --help, an unknown command, or a
12
+ * project-mode subcommand whitelist — falls back to the full eager program
13
+ * (src/index.js `createProgram`), i.e. exactly today's behavior. The fast path
14
+ * only activates when a single known command resolves cleanly. Worst case is
15
+ * therefore "as slow as today, still correct", never "wrong".
16
+ *
17
+ * The manifest is regenerated by scripts/gen-command-manifest.mjs and guarded
18
+ * against drift by lazy-dispatch.test.js.
19
+ */
20
+ import { readFileSync } from "fs";
21
+ import { createBaseProgram } from "./program-base.js";
22
+ import { maybeNotifyUpdate } from "./lib/update-notice.js";
23
+
24
+ const manifest = JSON.parse(
25
+ readFileSync(new URL("./command-manifest.json", import.meta.url), "utf8"),
26
+ );
27
+
28
+ // name + every alias -> manifest entry
29
+ const byName = new Map();
30
+ for (const entry of manifest.commands) {
31
+ byName.set(entry.name, entry);
32
+ for (const alias of entry.aliases || []) byName.set(alias, entry);
33
+ }
34
+
35
+ /**
36
+ * The command token is the first positional arg (the first token that is not a
37
+ * global option). The root program's only global options (--verbose, --quiet)
38
+ * and the -v/--version / -h/--help flags are all boolean and never consume a
39
+ * following value, so "first token not starting with '-'" is unambiguous.
40
+ */
41
+ export function resolveCommandToken(argv) {
42
+ const rest = argv.slice(2);
43
+ for (const arg of rest) {
44
+ if (arg === "--") break;
45
+ if (arg.startsWith("-")) continue;
46
+ return arg;
47
+ }
48
+ return null;
49
+ }
50
+
51
+ function hasVersionFlag(argv) {
52
+ return argv.slice(2).some((a) => a === "-v" || a === "--version");
53
+ }
54
+
55
+ function notify() {
56
+ // Mirror index.js: passive update nudge, suppressed under vitest.
57
+ if (!process.env.VITEST) {
58
+ try {
59
+ maybeNotifyUpdate();
60
+ } catch {
61
+ /* fail-open — never block the CLI on the nudge */
62
+ }
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Parse and run the CLI. Used by the bin entry in place of importing index.js
68
+ * directly (which would defeat lazy loading).
69
+ *
70
+ * @param {string[]} [argv] defaults to process.argv
71
+ * @returns {Promise<import('commander').Command>}
72
+ */
73
+ export async function runCli(argv = process.argv) {
74
+ // Project-mode packed exes restrict the visible command set; the filtering
75
+ // lives in the eager createProgram, so defer to it for correctness.
76
+ const whitelistActive = !!process.env.CC_PROJECT_ALLOWED_SUBCOMMANDS;
77
+ const token = resolveCommandToken(argv);
78
+
79
+ // Fast path 1: a known command resolves cleanly.
80
+ if (token && !whitelistActive && byName.has(token)) {
81
+ const entry = byName.get(token);
82
+ const program = createBaseProgram();
83
+ notify();
84
+ const mod = await import(new URL(entry.module, import.meta.url));
85
+ const register = mod[entry.register];
86
+ if (typeof register === "function") {
87
+ register(program);
88
+ return program.parseAsync(argv);
89
+ }
90
+ // Manifest pointed at a missing export (should be impossible given the
91
+ // drift test) — fall through to the eager path rather than crash.
92
+ }
93
+
94
+ // Fast path 2: bare version request, no command (base program short-circuits
95
+ // without loading any command module).
96
+ if (!token && !whitelistActive && hasVersionFlag(argv)) {
97
+ const program = createBaseProgram();
98
+ notify();
99
+ return program.parseAsync(argv);
100
+ }
101
+
102
+ // Fallback: --help, no args, unknown command, or project whitelist — load the
103
+ // full eager program (today's behavior; createProgram fires its own update
104
+ // nudge so we do not call notify() here to avoid a double nudge).
105
+ const { createProgram } = await import("./index.js");
106
+ return createProgram().parseAsync(argv);
107
+ }
@@ -9,20 +9,38 @@
9
9
 
10
10
  import { execSync } from "child_process";
11
11
 
12
+ // Injectable so tests can assert whether the (expensive) chcp spawn happened
13
+ // without actually spawning cmd.exe. See cli-dev.md `_deps` pattern.
14
+ export const _deps = { execSync };
15
+
12
16
  /**
13
17
  * Call this as early as possible in the process entry point.
14
18
  */
15
19
  export function ensureUtf8() {
16
20
  if (process.platform !== "win32") return;
17
21
 
18
- // Set Windows console codepage to UTF-8
19
- try {
20
- execSync("chcp 65001", { stdio: "ignore" });
21
- } catch (_err) {
22
- // Ignore - may fail in non-interactive environments
22
+ // Setting the Windows console codepage to UTF-8 requires spawning cmd.exe
23
+ // (`chcp 65001`), which costs ~280ms per invocation — a Windows process
24
+ // creation, paid on EVERY `cc` run. But the codepage only affects how an
25
+ // *interactive console* renders bytes; when stdout/stderr are piped or
26
+ // redirected (JSON output, headless runs, `cc` spawned as a subprocess by the
27
+ // desktop app / Android / spawnSync test harnesses), the bytes flow through
28
+ // the pipe as raw UTF-8 regardless of codepage, so the spawn is pure startup
29
+ // tax. Only pay it when a console is actually attached — the 乱码 fix is
30
+ // preserved exactly where it matters. Set CC_FORCE_CHCP=1 to restore the
31
+ // unconditional old behavior if some console edge case needs it.
32
+ const consoleAttached =
33
+ Boolean(process.stdout && process.stdout.isTTY) ||
34
+ Boolean(process.stderr && process.stderr.isTTY);
35
+ if (consoleAttached || process.env.CC_FORCE_CHCP === "1") {
36
+ try {
37
+ _deps.execSync("chcp 65001", { stdio: "ignore" });
38
+ } catch (_err) {
39
+ // Ignore - may fail in non-interactive environments
40
+ }
23
41
  }
24
42
 
25
- // Ensure stdout/stderr use UTF-8 encoding
43
+ // Ensure stdout/stderr use UTF-8 encoding (cheap; always)
26
44
  if (process.stdout.setDefaultEncoding) {
27
45
  process.stdout.setDefaultEncoding("utf8");
28
46
  }
@@ -30,7 +48,7 @@ export function ensureUtf8() {
30
48
  process.stderr.setDefaultEncoding("utf8");
31
49
  }
32
50
 
33
- // Set environment variable so child processes inherit UTF-8
51
+ // Set environment variable so child processes inherit UTF-8 (always)
34
52
  process.env.PYTHONIOENCODING = "utf-8";
35
53
  process.env.LANG = "en_US.UTF-8";
36
54
  }
@@ -4,6 +4,7 @@
4
4
 
5
5
  import { writeFileSync, mkdirSync, existsSync } from "fs";
6
6
  import { join } from "path";
7
+ import { safeJsonParse } from "./safe-json.js";
7
8
 
8
9
  /**
9
10
  * Ensure the notes table exists
@@ -50,7 +51,7 @@ export function fetchNotes(db, { category, tag, limit } = {}) {
50
51
  if (tag) {
51
52
  notes = notes.filter((n) => {
52
53
  try {
53
- const tags = JSON.parse(n.tags || "[]");
54
+ const tags = safeJsonParse(n.tags, []);
54
55
  return tags.includes(tag);
55
56
  } catch {
56
57
  return false;
@@ -173,7 +174,7 @@ export function generateIndexHtml(
173
174
  ) {
174
175
  const noteLinks = notes
175
176
  .map((n) => {
176
- const tags = JSON.parse(n.tags || "[]");
177
+ const tags = safeJsonParse(n.tags, []);
177
178
  const tagsHtml = tags
178
179
  .map((t) => `<span class="tag">${escapeHtml(t)}</span>`)
179
180
  .join(" ");
@@ -308,7 +308,7 @@ export function getUserPermissions(db, userDid) {
308
308
  .prepare("SELECT permissions FROM rbac_roles WHERE name = ?")
309
309
  .get(grant.role_name);
310
310
  if (role) {
311
- const perms = JSON.parse(role.permissions || "[]");
311
+ const perms = safeJsonParse(role.permissions, []);
312
312
  for (const p of perms) allPerms.add(p);
313
313
  }
314
314
  }
@@ -6,6 +6,7 @@
6
6
  */
7
7
 
8
8
  import { createHash } from "crypto";
9
+ import { safeJsonParse } from "./safe-json.js";
9
10
 
10
11
  function ensureSessionsTable(db) {
11
12
  db.exec(`
@@ -153,7 +154,7 @@ export function listSessions(db, options = {}) {
153
154
  ...session,
154
155
  metadata:
155
156
  typeof session.metadata === "string"
156
- ? JSON.parse(session.metadata || "{}")
157
+ ? safeJsonParse(session.metadata, {})
157
158
  : session.metadata || {},
158
159
  }));
159
160
  }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Shared base Commander program factory.
3
+ *
4
+ * Both the eager path (src/index.js `createProgram`, used by tests, help, and
5
+ * the unknown-command fallback) and the lazy fast path (src/lazy-dispatch.js,
6
+ * used by the bin for a resolved `cc <cmd>`) build their program from this so
7
+ * the program name, version flag, and global options can never drift between
8
+ * the two code paths.
9
+ */
10
+ import { Command } from "commander";
11
+ import { VERSION } from "./constants.js";
12
+
13
+ export function createBaseProgram() {
14
+ const program = new Command();
15
+ program
16
+ .name("chainlesschain")
17
+ .description(
18
+ "CLI for ChainlessChain - install, configure, and manage your personal AI management system",
19
+ )
20
+ .version(VERSION, "-v, --version")
21
+ .option("--verbose", "Enable verbose output")
22
+ .option("--quiet", "Suppress non-essential output");
23
+ return program;
24
+ }
@@ -97,7 +97,7 @@ import {
97
97
  import { resolveSlashMacro } from "./slash-macro.js";
98
98
  import { expandMcpPrompt, renderMcpSurface } from "./mcp-prompt.js";
99
99
  import { newCostStore, addUsage } from "./session-cost.js";
100
- import { parseThinkCommand } from "./think-command.js";
100
+ import { parseThinkCommand, parseEffortCommand } from "./think-command.js";
101
101
  import { shouldStreamLive } from "./stream-decision.js";
102
102
  import { emptyTurnNotice } from "./empty-turn-notice.js";
103
103
  import { buildPermissionPrompt } from "./permission-prompt.js";
@@ -1049,6 +1049,7 @@ export async function startAgentRepl(options = {}) {
1049
1049
  "/cost",
1050
1050
  "/cowork",
1051
1051
  "/doctor",
1052
+ "/effort",
1052
1053
  "/exit",
1053
1054
  "/export",
1054
1055
  "/help",
@@ -1514,6 +1515,9 @@ export async function startAgentRepl(options = {}) {
1514
1515
  logger.log(
1515
1516
  ` ${chalk.cyan("/think")} Extended thinking on/off (/think [on|off|ultra]; /ultrathink = max; Anthropic)`,
1516
1517
  );
1518
+ logger.log(
1519
+ ` ${chalk.cyan("/effort")} Reasoning effort (/effort low|medium|high|xhigh; Anthropic)`,
1520
+ );
1517
1521
  logger.log(` ${chalk.cyan("/clear")} Clear conversation`);
1518
1522
  logger.log(
1519
1523
  ` ${chalk.cyan("/vim")} Toggle vim-mode line editing (/vim [on|off]; Esc → NORMAL)`,
@@ -2006,6 +2010,26 @@ export async function startAgentRepl(options = {}) {
2006
2010
  }
2007
2011
  }
2008
2012
 
2013
+ // Reasoning-effort alias (/effort low|medium|high|xhigh) — a discrete,
2014
+ // validated front-end over the /think <level> passthrough (Claude-Code parity).
2015
+ {
2016
+ const effort = parseEffortCommand(trimmed);
2017
+ if (effort) {
2018
+ if (effort.error) {
2019
+ logger.info(effort.error);
2020
+ prompt();
2021
+ return;
2022
+ }
2023
+ thinking = effort.thinking;
2024
+ logger.info(
2025
+ `Reasoning effort: ${chalk.cyan(effort.label)} ` +
2026
+ chalk.gray("(Anthropic extended thinking; applies next turn)"),
2027
+ );
2028
+ prompt();
2029
+ return;
2030
+ }
2031
+ }
2032
+
2009
2033
  if (trimmed === "/clear") {
2010
2034
  // Stash the conversation first so `/rewind clear` can restore it
2011
2035
  // (Claude-Code 2.1.191). A no-op /clear (nothing to stash) keeps any
@@ -34,3 +34,34 @@ export function parseThinkCommand(trimmed) {
34
34
  // An explicit effort level (low/medium/high/…) — passed through to the engine.
35
35
  return { thinking: arg, label: arg, anthropic: true };
36
36
  }
37
+
38
+ /**
39
+ * Pure parser for the REPL `/effort low|medium|high|xhigh` command — a discrete,
40
+ * validated alias over `/think <level>` (Claude-Code `/effort` parity). Returns
41
+ * the same shape parseThinkCommand yields ({ thinking, label, anthropic }) so the
42
+ * REPL applies it identically, or `{ error }` for a missing/unknown level, or
43
+ * null when the input is not an effort command (REPL falls through).
44
+ *
45
+ * /effort low|medium|high|xhigh (synonyms: med→medium, max→xhigh)
46
+ */
47
+ export function parseEffortCommand(trimmed) {
48
+ const t = String(trimmed == null ? "" : trimmed).trim();
49
+ if (t !== "/effort" && !t.startsWith("/effort ")) return null;
50
+ const arg = t.slice("/effort".length).trim().toLowerCase();
51
+ if (!arg) {
52
+ return { error: "usage: /effort low|medium|high|xhigh" };
53
+ }
54
+ const LEVELS = {
55
+ low: "low",
56
+ medium: "medium",
57
+ med: "medium",
58
+ high: "high",
59
+ xhigh: "xhigh",
60
+ max: "xhigh",
61
+ };
62
+ const level = LEVELS[arg];
63
+ if (!level) {
64
+ return { error: `unknown effort "${arg}" — use low|medium|high|xhigh` };
65
+ }
66
+ return { thinking: level, label: `effort ${level}`, anthropic: true };
67
+ }
@@ -31,6 +31,33 @@ import {
31
31
 
32
32
  export const DEFAULT_MAX_BYTES = 100 * 1024; // 100 KB per referenced file
33
33
  export const DEFAULT_MAX_DIR_ENTRIES = 200;
34
+ // How deep a `@folder/` tree walks (the root's own entries are depth 1).
35
+ export const DEFAULT_MAX_DIR_DEPTH = 3;
36
+
37
+ // Directory names a `@folder/` tree lists but never DESCENDS into — heavy or
38
+ // noise trees that would burn the whole entry budget (a single `@./` would
39
+ // otherwise spend all 200 entries inside node_modules). They still appear as a
40
+ // node, marked "(not expanded)", so the structure stays honest.
41
+ const TREE_IGNORE_DIRS = new Set([
42
+ "node_modules",
43
+ ".git",
44
+ ".hg",
45
+ ".svn",
46
+ "dist",
47
+ "build",
48
+ "out",
49
+ ".next",
50
+ ".nuxt",
51
+ ".cache",
52
+ "coverage",
53
+ "__pycache__",
54
+ ".venv",
55
+ "venv",
56
+ ".idea",
57
+ ".gradle",
58
+ ".turbo",
59
+ "target",
60
+ ]);
34
61
 
35
62
  // `@` preceded by start / whitespace / opening bracket-quote, then a path run.
36
63
  // We capture greedily up to the next whitespace or a closing bracket-quote and
@@ -136,12 +163,58 @@ function readBoundedFile(fs, abs, limit) {
136
163
  }
137
164
  }
138
165
 
166
+ /**
167
+ * Walk a directory into a bounded, indented tree (Claude-Code `@folder/`
168
+ * parity — the model sees STRUCTURE, not just the top-level names). Depth- and
169
+ * entry-capped, and never descends into TREE_IGNORE_DIRS. Symlinks report
170
+ * isDirectory() === false (Dirent reads the link, not its target), so symlink
171
+ * loops can never recurse here. Returns { lines, total, truncated } where
172
+ * `total` is the node count actually emitted (`truncated` means more existed).
173
+ */
174
+ function buildDirTree(absRoot, { fs, path, maxEntries, maxDepth }) {
175
+ const lines = [];
176
+ let count = 0;
177
+ let truncated = false;
178
+
179
+ function walk(abs, depth) {
180
+ if (truncated) return;
181
+ let entries;
182
+ try {
183
+ entries = fs.readdirSync(abs, { withFileTypes: true });
184
+ } catch {
185
+ return; // an unreadable subdir is skipped silently (root errors upstream)
186
+ }
187
+ const sorted = entries
188
+ .map((e) => ({ name: e.name, isDir: e.isDirectory() }))
189
+ .sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
190
+ const indent = " ".repeat(depth - 1);
191
+ for (const e of sorted) {
192
+ if (count >= maxEntries) {
193
+ truncated = true;
194
+ return;
195
+ }
196
+ count++;
197
+ const ignored = e.isDir && TREE_IGNORE_DIRS.has(e.name);
198
+ const label = e.isDir ? `${e.name}/` : e.name;
199
+ lines.push(indent + label + (ignored ? " (not expanded)" : ""));
200
+ if (e.isDir && !ignored && depth < maxDepth) {
201
+ walk(path.resolve(abs, e.name), depth + 1);
202
+ }
203
+ }
204
+ }
205
+ walk(absRoot, 1);
206
+ return { lines, total: count, truncated };
207
+ }
208
+
139
209
  /**
140
210
  * Resolve a single raw token to an injectable ref descriptor, or null when it
141
211
  * does not resolve to an existing path. Tries the literal path first, then a
142
212
  * trailing-punctuation-stripped variant (handles "see @config.json.").
143
213
  */
144
- function resolveRef(raw, { cwd, fs, path, maxBytes, maxDirEntries }) {
214
+ function resolveRef(
215
+ raw,
216
+ { cwd, fs, path, maxBytes, maxDirEntries, maxDirDepth },
217
+ ) {
145
218
  const candidates = [raw];
146
219
  const trimmed = trimTrailingPunct(raw);
147
220
  if (trimmed && trimmed !== raw) candidates.push(trimmed);
@@ -158,10 +231,12 @@ function resolveRef(raw, { cwd, fs, path, maxBytes, maxDirEntries }) {
158
231
  continue; // not this candidate
159
232
  }
160
233
  if (stat.isDirectory()) {
161
- // A line range on a directory is meaningless — ignore it and list the dir.
162
- let entries;
234
+ // A line range on a directory is meaningless — ignore it and tree the dir.
235
+ // Probe the root once so an unreadable directory surfaces as a warning
236
+ // (buildDirTree silently skips unreadable SUBdirs, but the root the user
237
+ // explicitly pointed at deserves an error).
163
238
  try {
164
- entries = fs.readdirSync(abs, { withFileTypes: true });
239
+ fs.readdirSync(abs, { withFileTypes: true });
165
240
  } catch (err) {
166
241
  return {
167
242
  kind: "error",
@@ -170,18 +245,19 @@ function resolveRef(raw, { cwd, fs, path, maxBytes, maxDirEntries }) {
170
245
  message: `cannot read directory: ${err.message}`,
171
246
  };
172
247
  }
173
- const names = entries
174
- .slice(0, maxDirEntries)
175
- .map((e) => (e.isDirectory() ? `${e.name}/` : e.name))
176
- .sort();
177
- const truncated = entries.length > maxDirEntries;
248
+ const tree = buildDirTree(abs, {
249
+ fs,
250
+ path,
251
+ maxEntries: maxDirEntries,
252
+ maxDepth: maxDirDepth,
253
+ });
178
254
  return {
179
255
  kind: "dir",
180
256
  raw: cand,
181
257
  rel: fsPath,
182
- entries: names,
183
- total: entries.length,
184
- truncated,
258
+ entries: tree.lines,
259
+ total: tree.total,
260
+ truncated: tree.truncated,
185
261
  };
186
262
  }
187
263
  if (stat.isFile()) {
@@ -320,7 +396,7 @@ function renderBlock(refs) {
320
396
  parts.push(`<dir ${attrs}>`);
321
397
  parts.push(ref.entries.join("\n"));
322
398
  if (ref.truncated) {
323
- parts.push(`… [truncated — ${ref.total} entries total]`);
399
+ parts.push(`… [truncated — listing capped at ${ref.total} entries]`);
324
400
  }
325
401
  parts.push("</dir>");
326
402
  } else if (ref.kind === "pdf") {
@@ -385,13 +461,23 @@ function _resolveAllRefs(text, opts) {
385
461
  const maxDirEntries = Number.isFinite(opts.maxDirEntries)
386
462
  ? opts.maxDirEntries
387
463
  : DEFAULT_MAX_DIR_ENTRIES;
464
+ const maxDirDepth = Number.isFinite(opts.maxDirDepth)
465
+ ? opts.maxDirDepth
466
+ : DEFAULT_MAX_DIR_DEPTH;
388
467
 
389
468
  const tokens = findFileRefTokens(text);
390
469
  const refs = [];
391
470
  const warnings = [];
392
471
 
393
472
  for (const { raw } of tokens) {
394
- const ref = resolveRef(raw, { cwd, fs, path, maxBytes, maxDirEntries });
473
+ const ref = resolveRef(raw, {
474
+ cwd,
475
+ fs,
476
+ path,
477
+ maxBytes,
478
+ maxDirEntries,
479
+ maxDirDepth,
480
+ });
395
481
  if (!ref) {
396
482
  // Only warn for path-like tokens; bare @mentions are left alone.
397
483
  if (looksPathLike(raw)) {
@@ -1,7 +0,0 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./ChatBubbleRenderer-CBfsPCW2.js","./vendor-BvqAck49.js","./index-0siNWZzF.js","./icons-DP3uiYxy.js","./index-Cq93VfoF.css","./ChatBubbleRenderer-gy5SDGw2.css","./OrderTableRenderer-C-uX9jZ8.js","./OrderTableRenderer-CfrDjSHT.css","./TimelineRenderer-MPUsr2be.js","./TimelineRenderer-BlTAtkOc.css","./EmailListRenderer-CDo7ThLw.js","./EmailListRenderer-DTPZPx_S.css","./GenericCardRenderer-D_SVwVBw.js","./GenericCardRenderer-o85VDYQS.css"])))=>i.map(i=>d[i]);
2
- import{E as ue,r as E,b as w,I as y,P as u,J as O,U as v,c,S as C,V as R,_ as M,F as V,Z as se,a0 as le,R as x,K as m,Q as b,W as A,o as J}from"./vendor-BvqAck49.js";import{u as U}from"./usePersonalDataHub-W3UdGGh6.js";import{_ as F,e as L,d as q}from"./index-0siNWZzF.js";import{a9 as P,af as z,M as Q,aH as G,u as H,aG as K,ap as W,ab as Y,e as de,d as fe,k as pe,ah as me,az as _e,R as ge}from"./icons-DP3uiYxy.js";const I=Object.freeze({q:"",category:null,adapter:null,subtype:null,since:null,until:null}),B=50,Z=300,X=ue("pdhBrowser",()=>{const r=E({...I}),t=E([]),n=E(null),e=E(!1),o=E(!1),l=E(null),a=E(null),s=E(!1),i=E({byCategory:{},byAdapter:{},bySubtype:{},total:0});let d=0,_=null;const D=w(()=>t.value.length>0),j=w(()=>n.value!=null);function h(S={}){const p={},f=r.value;return f.q&&f.q.trim()&&(p.q=f.q.trim()),f.category&&(p.category=f.category),f.adapter&&(p.adapter=f.adapter),f.subtype&&(p.subtype=f.subtype),Number.isFinite(f.since)&&(p.since=f.since),Number.isFinite(f.until)&&(p.until=f.until),{...p,...S}}async function g(){const S=++d;e.value=!0,l.value=null;const p=U();try{const[f,T]=await Promise.all([p.searchEvents(h({limit:B})),p.facetCounts({...r.value.q&&r.value.q.trim()?{q:r.value.q.trim()}:{},...Number.isFinite(r.value.since)?{since:r.value.since}:{},...Number.isFinite(r.value.until)?{until:r.value.until}:{}})]);if(S!==d)return;t.value=f.rows||[],n.value=f.nextCursor||null,a.value=f.mode||"like",s.value=!!f.shortQuery,i.value={byCategory:T.byCategory||{},byAdapter:T.byAdapter||{},bySubtype:T.bySubtype||{},total:T.total||0}}catch(f){if(S!==d)return;l.value=f&&f.message?f.message:String(f),t.value=[],n.value=null}finally{S===d&&(e.value=!1)}}async function k(){if(!n.value||o.value)return;o.value=!0,l.value=null;const S=U();try{const p=await S.searchEvents(h({cursor:n.value,limit:B}));t.value=t.value.concat(p.rows||[]),n.value=p.nextCursor||null,a.value=p.mode||a.value}catch(p){l.value=p&&p.message?p.message:String(p)}finally{o.value=!1}}function ae(S,p){if(!(S in I))throw new Error(`pdhBrowser.setFilter: unknown key "${S}"`);r.value={...r.value,[S]:p},_&&clearTimeout(_),_=setTimeout(()=>{_=null,g()},Z)}function ie(S){return r.value={...I,...S},_&&(clearTimeout(_),_=null),g()}function ce(){r.value={...I},t.value=[],n.value=null,l.value=null,i.value={byCategory:{},byAdapter:{},bySubtype:{},total:0}}return{filters:r,results:t,cursor:n,isLoading:e,isAppending:o,error:l,mode:a,shortQuery:s,facets:i,hasResults:D,canLoadMore:j,search:g,loadMore:k,setFilter:ae,setFilters:ie,reset:ce,PAGE_SIZE:B,SEARCH_DEBOUNCE_MS:Z}}),ye=Object.freeze(["chat","social","email","shopping","travel","system","ai-chat","other"]),ve=Object.freeze({chat:"社交聊天",social:"内容平台",email:"邮件",shopping:"支付订单",travel:"出行",system:"系统数据","ai-chat":"AI 对话",other:"其他"});function N(r){return ve[r]||r||"未知"}const be=Object.freeze([["wechat","chat"],["messaging-","chat"],["social-","social"],["email-","email"],["shopping-","shopping"],["alipay-","shopping"],["travel-","travel"],["system-data","system"],["browser-","system"],["vscode","system"],["win-recent","system"],["git-activity","system"],["shell-history","system"],["local-files","system"],["ai-chat-","ai-chat"]]);function $(r){if(typeof r!="string"||r.length===0)return"other";for(const[t,n]of be)if(t.endsWith("-")||t==="system-data"){if(r.startsWith(t))return n}else if(r===t)return n;return"other"}const he={__name:"CategorySidebar",props:{selected:{type:String,default:null},facets:{type:Object,default:()=>({byCategory:{},total:0})}},emits:["select"],setup(r,{expose:t}){t();const n=r,e=w(()=>n.facets.total||0),o={chat:Y,social:W,email:K,shopping:H,travel:G,system:Q,"ai-chat":z,other:P};function l(i){return o[i]||P}function a(i){return i?{backgroundColor:"#fff",color:"#1677ff"}:{backgroundColor:"#f0f0f0",color:"#666"}}const s={props:n,total:e,ICONS:o,iconFor:l,badgeStyle:a,computed:w,get AppstoreOutlined(){return P},get MessageOutlined(){return Y},get PlayCircleOutlined(){return W},get MailOutlined(){return K},get ShoppingOutlined(){return H},get CarOutlined(){return G},get MobileOutlined(){return Q},get RobotOutlined(){return z},get CATEGORIES(){return ye},get categoryLabel(){return N}};return Object.defineProperty(s,"__isScriptSetup",{enumerable:!1,value:!0}),s}},Se={class:"pdh-sidebar"},Oe=["onClick"],we={class:"label"};function Ce(r,t,n,e,o,l){const a=y("a-badge");return u(),O("div",Se,[t[2]||(t[2]=v("div",{class:"sidebar-title"},"数据类型",-1)),v("div",{class:M(["cat-item",{active:n.selected===null}]),onClick:t[0]||(t[0]=s=>r.$emit("select",null))},[c(e.AppstoreOutlined,{class:"icon"}),t[1]||(t[1]=v("span",{class:"label"},"全部",-1)),e.total>0?(u(),C(a,{key:0,count:e.total,"number-style":e.badgeStyle(n.selected===null),"overflow-count":9999},null,8,["count","number-style"])):R("v-if",!0)],2),(u(!0),O(V,null,se(e.CATEGORIES,s=>(u(),O("div",{key:s,class:M(["cat-item",{active:n.selected===s,dim:!n.facets.byCategory[s]}]),onClick:i=>r.$emit("select",s)},[(u(),C(le(e.iconFor(s)),{class:"icon"})),v("span",we,x(e.categoryLabel(s)),1),n.facets.byCategory[s]?(u(),C(a,{key:0,count:n.facets.byCategory[s],"number-style":e.badgeStyle(n.selected===s),"overflow-count":9999},null,8,["count","number-style"])):R("v-if",!0)],10,Oe))),128))])}const ke=F(he,[["render",Ce],["__scopeId","data-v-6b62a22e"],["__file","/tmp/cc-web-panel-3qRD5g/repo/packages/web-panel/src/components/pdh/CategorySidebar.vue"]]),Re={__name:"SearchFilterBar",props:{filters:{type:Object,required:!0},facets:{type:Object,default:()=>({byAdapter:{}})},isLoading:{type:Boolean,default:!1},mode:{type:String,default:null},shortQuery:{type:Boolean,default:!1}},emits:["set-filter","reset"],setup(r,{expose:t,emit:n}){t();const e=r,o=n,l=w(()=>e.filters.category?`在「${e.filters.category}」类目内搜索…`:"搜索关键词(如:支付宝、罗技、Kotlin)…"),a=w(()=>Object.entries(e.facets.byAdapter||{}).sort((g,k)=>k[1]-g[1]).map(([g,k])=>({value:g,label:`${g} (${k})`}))),s=w(()=>!e.filters.since&&!e.filters.until?null:[e.filters.since?new Date(e.filters.since):null,e.filters.until?new Date(e.filters.until):null]),i=w(()=>!!(e.filters.q||e.filters.adapter||e.filters.subtype||e.filters.since||e.filters.until));function d(h){o("set-filter","q",h||"")}function _(h){o("set-filter","q",h||"")}function D(h){if(!h||h.length===0){o("set-filter","since",null),o("set-filter","until",null);return}const[g,k]=h;o("set-filter","since",g?+g:null),o("set-filter","until",k?+k:null)}const j={props:e,emit:o,placeholderText:l,adapterOptions:a,dateRangeValue:s,hasActiveFilters:i,onQInput:d,onSearch:_,onDateChange:D,computed:w,get SearchOutlined(){return pe},get CloseCircleOutlined(){return fe},get InfoCircleOutlined(){return de}};return Object.defineProperty(j,"__isScriptSetup",{enumerable:!1,value:!0}),j}},Ee={class:"pdh-filter-bar"},xe={key:1,class:"hint"},De={key:2,class:"hint"};function je(r,t,n,e,o,l){const a=y("a-input-search"),s=y("a-select"),i=y("a-range-picker"),d=y("a-button");return u(),O("div",Ee,[c(a,{value:n.filters.q,placeholder:e.placeholderText,"enter-button":"搜索","allow-clear":"",loading:n.isLoading,style:{flex:"1","max-width":"480px"},"onUpdate:value":e.onQInput,onSearch:e.onSearch},{prefix:m(()=>[c(e.SearchOutlined)]),_:1},8,["value","placeholder","loading"]),c(s,{value:n.filters.adapter,placeholder:"所有 adapter","allow-clear":"",options:e.adapterOptions,style:{width:"200px"},"onUpdate:value":t[0]||(t[0]=_=>r.$emit("set-filter","adapter",_||null))},null,8,["value","options"]),c(i,{value:e.dateRangeValue,placeholder:["开始日期","结束日期"],style:{width:"280px"},"onUpdate:value":e.onDateChange},null,8,["value"]),e.hasActiveFilters?(u(),C(d,{key:0,type:"link",danger:"",onClick:t[1]||(t[1]=_=>r.$emit("reset"))},{icon:m(()=>[c(e.CloseCircleOutlined)]),default:m(()=>[t[2]||(t[2]=b(" 清空筛选 ",-1))]),_:1})):R("v-if",!0),n.shortQuery?(u(),O("span",xe,[c(e.InfoCircleOutlined),t[3]||(t[3]=b(" 关键词需至少 3 字 (FTS5 trigram 限制) ",-1))])):n.mode==="like"?(u(),O("span",De,[c(e.InfoCircleOutlined),t[4]||(t[4]=b(" 当前为 LIKE 兜底模式 ",-1))])):R("v-if",!0)])}const Ae=F(Re,[["render",je],["__scopeId","data-v-d7cce6e9"],["__file","/tmp/cc-web-panel-3qRD5g/repo/packages/web-panel/src/components/pdh/SearchFilterBar.vue"]]),Le={__name:"RendererDispatcher",props:{event:{type:Object,required:!0}},setup(r,{expose:t}){t();const n=A(()=>L(()=>import("./ChatBubbleRenderer-CBfsPCW2.js"),__vite__mapDeps([0,1,2,3,4,5]),import.meta.url)),e=A(()=>L(()=>import("./OrderTableRenderer-C-uX9jZ8.js"),__vite__mapDeps([6,1,2,3,4,7]),import.meta.url)),o=A(()=>L(()=>import("./TimelineRenderer-MPUsr2be.js"),__vite__mapDeps([8,1,2,3,4,9]),import.meta.url)),l=A(()=>L(()=>import("./EmailListRenderer-CDo7ThLw.js"),__vite__mapDeps([10,1,2,3,4,11]),import.meta.url)),a=A(()=>L(()=>import("./GenericCardRenderer-D_SVwVBw.js"),__vite__mapDeps([12,1,2,3,4,13]),import.meta.url));function s(d){const _=d&&d.source&&d.source.adapter;switch($(_)){case"chat":case"ai-chat":return n;case"shopping":return e;case"travel":return o;case"email":return l;default:return a}}const i={ChatBubbleRenderer:n,OrderTableRenderer:e,TimelineRenderer:o,EmailListRenderer:l,GenericCardRenderer:a,rendererFor:s,defineAsyncComponent:A,get getCategory(){return $}};return Object.defineProperty(i,"__isScriptSetup",{enumerable:!1,value:!0}),i}};function Fe(r,t,n,e,o,l){return u(),C(le(e.rendererFor(n.event)),{event:n.event},null,8,["event"])}const Te=F(Le,[["render",Fe],["__file","/tmp/cc-web-panel-3qRD5g/repo/packages/web-panel/src/components/pdh/renderers/RendererDispatcher.vue"]]);function ee(r){return JSON.stringify(r,null,2)}function te(r){return r.map(t=>JSON.stringify(t)).join(`
3
- `)+`
4
- `}function ne(r){const n=["id","subtype","occurredAt","occurredAtIso","actor","place","adapter","summary","contentJson","extraJson"].join(","),e=r.map(o=>{const l=Number.isFinite(o.occurredAt)?o.occurredAt:null,a=l?new Date(l).toISOString():"",s=o.source&&o.source.adapter||"",i=o.content&&(o.content.text||o.content.title||o.content.subject)||"";return[o.id||"",o.subtype||"",l!=null?String(l):"",a,o.actor||"",o.place||"",s,i,o.content?JSON.stringify(o.content):"",o.extra?JSON.stringify(o.extra):""].map(Ie).join(",")});return[n,...e].join(`
5
- `)+`
6
- `}function Ie(r){const t=String(r??"");return t.includes(",")||t.includes('"')||t.includes(`
7
- `)||t.includes("\r")?'"'+t.replace(/"/g,'""')+'"':t}function re(r,t,n="application/octet-stream"){if(typeof window>"u"||typeof document>"u")return t;const e=new Blob([r],{type:n}),o=URL.createObjectURL(e),l=document.createElement("a");return l.href=o,l.download=t,document.body.appendChild(l),l.click(),document.body.removeChild(l),setTimeout(()=>URL.revokeObjectURL(o),1e3),t}function oe(r,t=null){const n=new Date,e=n.getFullYear(),o=String(n.getMonth()+1).padStart(2,"0"),l=String(n.getDate()).padStart(2,"0"),a=r==="ndjson"?"ndjson":r,s=t?`-${t}`:"";return`pdh-events-${e}-${o}-${l}${s}.${a}`}const qe={__name:"ExportDropdown",props:{events:{type:Array,required:!0},category:{type:String,default:null}},setup(r,{expose:t}){t();const n=r,e=w(()=>n.events.length>0),o={json:"application/json;charset=utf-8",ndjson:"application/x-ndjson;charset=utf-8",csv:"text/csv;charset=utf-8"};function l(i){switch(i){case"json":return ee(n.events);case"ndjson":return te(n.events);case"csv":return ne(n.events);default:throw new Error(`unknown format: ${i}`)}}function a({key:i}){try{const d=l(i),_=oe(i,n.category);re(d,_,o[i]),q.success(`已导出 ${n.events.length} 条到 ${_}`)}catch(d){q.error(`导出失败: ${d.message||d}`)}}const s={props:n,hasResults:e,MIME:o,serialize:l,onClick:a,computed:w,get DownloadOutlined(){return _e},get DownOutlined(){return me},get message(){return q},get eventsToJson(){return ee},get eventsToNdjson(){return te},get eventsToCsv(){return ne},get downloadAs(){return re},get suggestFilename(){return oe}};return Object.defineProperty(s,"__isScriptSetup",{enumerable:!1,value:!0}),s}};function Pe(r,t,n,e,o,l){const a=y("a-button"),s=y("a-menu-item"),i=y("a-menu"),d=y("a-dropdown");return u(),C(d,{disabled:!e.hasResults},{overlay:m(()=>[c(i,{onClick:e.onClick},{default:m(()=>[c(s,{key:"json"},{default:m(()=>[...t[0]||(t[0]=[b("JSON (.json)",-1)])]),_:1}),c(s,{key:"ndjson"},{default:m(()=>[...t[1]||(t[1]=[b("NDJSON (.ndjson)",-1)])]),_:1}),c(s,{key:"csv"},{default:m(()=>[...t[2]||(t[2]=[b("CSV (.csv)",-1)])]),_:1})]),_:1})]),default:m(()=>[c(a,{disabled:!e.hasResults},{icon:m(()=>[c(e.DownloadOutlined)]),default:m(()=>[b(" 导出 ("+x(n.events.length)+") ",1),c(e.DownOutlined)]),_:1},8,["disabled"])]),_:1},8,["disabled"])}const Be=F(qe,[["render",Pe],["__file","/tmp/cc-web-panel-3qRD5g/repo/packages/web-panel/src/components/pdh/ExportDropdown.vue"]]),Ve={__name:"PdhVaultBrowser",setup(r,{expose:t}){t();const n=X(),e=w(()=>n.filters.q?`没有匹配 "${n.filters.q}" 的事件 — 试试更短的关键词或换个类目`:n.filters.category?`「${N(n.filters.category)}」类目下还没有数据 — 去采集页同步对应 adapter`:"Vault 里还没有数据 — 去「个人数据中台」页面同步任意 adapter");function o(s){n.filters.category=s,n.search()}function l(s,i){n.setFilter(s,i)}J(()=>{!n.hasResults&&!n.isLoading&&n.search()});const a={store:n,emptyHint:e,onCategorySelect:o,onSetFilter:l,onMounted:J,computed:w,get ReloadOutlined(){return ge},get usePdhBrowserStore(){return X},get categoryLabel(){return N},CategorySidebar:ke,SearchFilterBar:Ae,RendererDispatcher:Te,ExportDropdown:Be};return Object.defineProperty(a,"__isScriptSetup",{enumerable:!1,value:!0}),a}},Ne={class:"pdh-browser-page"},Me={class:"page-header"},Je={class:"main-pane"},Ue={class:"results-summary"},ze={key:0},Qe={key:0},Ge={key:1},He={class:"loaded-of"},Ke={class:"results-list"},We={key:2,class:"load-more"};function Ye(r,t,n,e,o,l){const a=y("a-tag"),s=y("a-button"),i=y("a-space"),d=y("a-col"),_=y("a-spin"),D=y("a-alert"),j=y("a-empty"),h=y("a-row");return u(),O("div",Ne,[v("div",Me,[t[6]||(t[6]=v("div",null,[v("h2",{class:"page-title"},"数据浏览器"),v("p",{class:"page-sub"}," Vault 里所有已采集的数据,按类目浏览 / 全文检索 / 时间窗口筛选。 完全离线,不外传。 ")],-1)),c(i,null,{default:m(()=>[e.store.mode==="fts5"?(u(),C(a,{key:0,color:"green"},{default:m(()=>[...t[3]||(t[3]=[b("FTS5 trigram",-1)])]),_:1})):e.store.mode==="like"?(u(),C(a,{key:1,color:"orange"},{default:m(()=>[...t[4]||(t[4]=[b("LIKE 兜底",-1)])]),_:1})):R("v-if",!0),c(e.ExportDropdown,{events:e.store.results,category:e.store.filters.category},null,8,["events","category"]),c(s,{onClick:t[0]||(t[0]=g=>e.store.search()),loading:e.store.isLoading},{icon:m(()=>[c(e.ReloadOutlined)]),default:m(()=>[t[5]||(t[5]=b(" 刷新 ",-1))]),_:1},8,["loading"])]),_:1})]),c(h,{gutter:16,class:"browser-layout"},{default:m(()=>[c(d,{xs:24,sm:24,md:6,lg:5,xl:4},{default:m(()=>[c(e.CategorySidebar,{selected:e.store.filters.category,facets:e.store.facets,onSelect:e.onCategorySelect},null,8,["selected","facets"])]),_:1}),c(d,{xs:24,sm:24,md:18,lg:19,xl:20},{default:m(()=>[v("div",Je,[c(e.SearchFilterBar,{filters:e.store.filters,facets:e.store.facets,"is-loading":e.store.isLoading,mode:e.store.mode,"short-query":e.store.shortQuery,onSetFilter:e.onSetFilter,onReset:t[1]||(t[1]=g=>{e.store.reset(),e.store.search()})},null,8,["filters","facets","is-loading","mode","short-query"]),v("div",Ue,[e.store.isLoading&&e.store.results.length===0?(u(),O("span",ze,[c(_,{size:"small"}),t[7]||(t[7]=b(" 载入中… ",-1))])):(u(),O(V,{key:1},[v("strong",null,x(e.store.facets.total),1),t[11]||(t[11]=b(" 条事件 ",-1)),e.store.filters.category?(u(),O("span",Qe,[t[8]||(t[8]=b(" / 当前类目 ",-1)),v("strong",null,x(e.categoryLabel(e.store.filters.category)),1),v("strong",null,x(e.store.facets.byCategory[e.store.filters.category]||0),1)])):R("v-if",!0),e.store.filters.q?(u(),O("span",Ge,[t[9]||(t[9]=b(' / 关键词 "',-1)),v("em",null,x(e.store.filters.q),1),t[10]||(t[10]=b('"',-1))])):R("v-if",!0),v("span",He," / 已显示 "+x(e.store.results.length),1)],64))]),e.store.error?(u(),C(D,{key:0,type:"error","show-icon":"",message:e.store.error,closable:"",style:{margin:"12px 0"}},null,8,["message"])):R("v-if",!0),!e.store.isLoading&&e.store.results.length===0&&!e.store.error?(u(),C(j,{key:1,description:e.emptyHint,style:{"margin-top":"40px"}},null,8,["description"])):R("v-if",!0),v("div",Ke,[(u(!0),O(V,null,se(e.store.results,(g,k)=>(u(),C(e.RendererDispatcher,{key:`r-${k}-${g.id}`,event:g},null,8,["event"]))),128))]),e.store.canLoadMore?(u(),O("div",We,[c(s,{loading:e.store.isAppending,onClick:t[2]||(t[2]=g=>e.store.loadMore()),block:""},{default:m(()=>[...t[12]||(t[12]=[b(" 加载下一页 (50 条) ",-1)])]),_:1},8,["loading"])])):R("v-if",!0)])]),_:1})]),_:1})])}const tt=F(Ve,[["render",Ye],["__scopeId","data-v-0f7c02cd"],["__file","/tmp/cc-web-panel-3qRD5g/repo/packages/web-panel/src/views/PdhVaultBrowser.vue"]]);export{tt as default};