chainlesschain 0.161.8 → 0.161.10

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 (165) hide show
  1. package/package.json +2 -1
  2. package/src/assets/web-panel/.build-hash +1 -1
  3. package/src/assets/web-panel/assets/{AIOps-Uls1XIO4.js → AIOps-CmCGAwDv.js} +1 -1
  4. package/src/assets/web-panel/assets/{ActionButton-BpVrefl1.js → ActionButton-eeaISbP3.js} +1 -1
  5. package/src/assets/web-panel/assets/{Analytics-Z1Rgg9PA.js → Analytics-CJEX10hA.js} +2 -2
  6. package/src/assets/web-panel/assets/AppLayout-BpbfrB7k.css +1 -0
  7. package/src/assets/web-panel/assets/AppLayout-CrYEU1PZ.js +3 -0
  8. package/src/assets/web-panel/assets/{Audit-Co6yYgmX.js → Audit-B4BUSPse.js} +1 -1
  9. package/src/assets/web-panel/assets/{Backup-1IZDB3QD.js → Backup-DIDcWoL9.js} +1 -1
  10. package/src/assets/web-panel/assets/{BaseInput-LYB_wIZP.js → BaseInput-CuJjCIcU.js} +1 -1
  11. package/src/assets/web-panel/assets/{Chat-D-6LDWGl.js → Chat-D-yEkssi.js} +3 -3
  12. package/src/assets/web-panel/assets/{Checkbox-dLnnw7OE.js → Checkbox-Bww7tFNj.js} +1 -1
  13. package/src/assets/web-panel/assets/{Codegen-Wh6xypMx.js → Codegen-D9k-kRxr.js} +1 -1
  14. package/src/assets/web-panel/assets/{Col-BT8lkjSw.js → Col-CWhmQejT.js} +1 -1
  15. package/src/assets/web-panel/assets/{Community-CdAeMHde.js → Community-Ck1ojsb1.js} +1 -1
  16. package/src/assets/web-panel/assets/{Compact-VsImrjgV.js → Compact-CuDFE-4P.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compliance-PScuVx8d.js → Compliance-BSOLLyKA.js} +1 -1
  18. package/src/assets/web-panel/assets/{Cowork-qB8hRW0r.js → Cowork-BiOMErjG.js} +3 -3
  19. package/src/assets/web-panel/assets/{Cron-CuSjuE-h.js → Cron-DnVm70q-.js} +2 -2
  20. package/src/assets/web-panel/assets/{Crosschain-BqIRd8bv.js → Crosschain-CqIVrbR9.js} +1 -1
  21. package/src/assets/web-panel/assets/{DID-BT5BJ0DF.js → DID-DS9MUwUx.js} +2 -2
  22. package/src/assets/web-panel/assets/{Dashboard-BX-06zEm.js → Dashboard-BlrXX2bG.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dropdown-Pjo3G3Vf.js → Dropdown-CqBG_led.js} +1 -1
  24. package/src/assets/web-panel/assets/{Federation-BbnYcYhr.js → Federation-DCBYjpOX.js} +1 -1
  25. package/src/assets/web-panel/assets/{FormItemContext-C3hf4K1_.js → FormItemContext-DQUjKw5j.js} +1 -1
  26. package/src/assets/web-panel/assets/{Git-Cmw6_HXW.js → Git-pitws4KW.js} +2 -2
  27. package/src/assets/web-panel/assets/{Governance-BFBvB2G6.js → Governance-BNG31zqe.js} +1 -1
  28. package/src/assets/web-panel/assets/{Inference-BBdX3YjM.js → Inference-W32jdXsU.js} +1 -1
  29. package/src/assets/web-panel/assets/{KnowledgeGraph-BC-bcvNY.js → KnowledgeGraph-_qBNRolE.js} +1 -1
  30. package/src/assets/web-panel/assets/{Logs-B9DuqFeW.js → Logs-BnnANM_j.js} +1 -1
  31. package/src/assets/web-panel/assets/{Marketplace-Bff1ja4R.js → Marketplace-C6OwNTw7.js} +1 -1
  32. package/src/assets/web-panel/assets/{McpTools-DnWqjtkb.js → McpTools-D7cbYTwR.js} +3 -3
  33. package/src/assets/web-panel/assets/{Memory-C6YXBuM7.js → Memory-7kL8dh9_.js} +2 -2
  34. package/src/assets/web-panel/assets/MobileBridge-B5ZSxpt9.js +3 -0
  35. package/src/assets/web-panel/assets/MobileBridge-DkkMCDx9.css +1 -0
  36. package/src/assets/web-panel/assets/{Mtc-r-J4WRfp.js → Mtc-DOm5jweE.js} +4 -4
  37. package/src/assets/web-panel/assets/{MtcAudit-DFqXLHn6.js → MtcAudit-D9rDFqWR.js} +1 -1
  38. package/src/assets/web-panel/assets/Multisig-CVqb7ayL.js +1 -0
  39. package/src/assets/web-panel/assets/Multisig-D-IuEDLa.css +1 -0
  40. package/src/assets/web-panel/assets/{NLProgramming-s5CrjYto.js → NLProgramming-BETAJaqD.js} +1 -1
  41. package/src/assets/web-panel/assets/{Notes-TAbnvDCd.js → Notes-CUJ1PDmG.js} +3 -3
  42. package/src/assets/web-panel/assets/{NotificationSettings-BZIKI219.js → NotificationSettings-BauSiotO.js} +1 -1
  43. package/src/assets/web-panel/assets/{Organization-BXbSljYa.js → Organization-B0T5PGg4.js} +4 -4
  44. package/src/assets/web-panel/assets/{Overflow-O2RWHZxH.js → Overflow-CHLIn241.js} +1 -1
  45. package/src/assets/web-panel/assets/{OverrideContext-DSwW21YS.js → OverrideContext-DYxbd09I.js} +1 -1
  46. package/src/assets/web-panel/assets/{P2P-B8jBZLh6.js → P2P-yqfDCE9U.js} +2 -2
  47. package/src/assets/web-panel/assets/{Permissions-Cexi8Jr6.js → Permissions-DWu7ec_n.js} +4 -4
  48. package/src/assets/web-panel/assets/{Pipeline-BwIBnNzj.js → Pipeline-BPXGoHep.js} +1 -1
  49. package/src/assets/web-panel/assets/{Privacy-D-LOR9um.js → Privacy-B2fRmhNs.js} +1 -1
  50. package/src/assets/web-panel/assets/ProjectInit-CteEo-wF.css +1 -0
  51. package/src/assets/web-panel/assets/{Projects-Bis42Cb6.js → ProjectInit-dPr0sgSZ.js} +2 -2
  52. package/src/assets/web-panel/assets/{ProjectSettings-DZ_fdRRJ.js → ProjectSettings-dKjwdfXX.js} +1 -1
  53. package/src/assets/web-panel/assets/Projects-C34BWmmy.css +1 -0
  54. package/src/assets/web-panel/assets/Projects-fRCTrULX.js +1 -0
  55. package/src/assets/web-panel/assets/{Providers-DH6hY4Gx.js → Providers-OpB_FLki.js} +1 -1
  56. package/src/assets/web-panel/assets/{QuickAsk-Ur0bUXHK.js → QuickAsk-B0gFMvI8.js} +1 -1
  57. package/src/assets/web-panel/assets/{Recommend-CLDQjSf6.js → Recommend-C2NwxWiR.js} +1 -1
  58. package/src/assets/web-panel/assets/{Reputation-StG-zGil.js → Reputation-CTBtQ8Bi.js} +1 -1
  59. package/src/assets/web-panel/assets/{Row-DAWA1cwa.js → Row-CQ36tMlO.js} +1 -1
  60. package/src/assets/web-panel/assets/{RssFeed-rb2CP-IS.js → RssFeed-ChOug33H.js} +3 -3
  61. package/src/assets/web-panel/assets/{Search-Y9qsWaAE.js → Search-DrlVYajZ.js} +1 -1
  62. package/src/assets/web-panel/assets/{Security-BanZDWBe.js → Security-BxWm_HMW.js} +3 -3
  63. package/src/assets/web-panel/assets/{Services-DFOMZwqJ.js → Services-Bq__zdi1.js} +2 -2
  64. package/src/assets/web-panel/assets/{Skeleton-Cy48VmyC.js → Skeleton-D2BPihBx.js} +1 -1
  65. package/src/assets/web-panel/assets/{Skills-BqdkGRyO.js → Skills-Q1ZtavwR.js} +1 -1
  66. package/src/assets/web-panel/assets/{Sla-B8lXq9rJ.js → Sla-CyIKjjSg.js} +1 -1
  67. package/src/assets/web-panel/assets/{SpeechSettings-CmIkYpWT.js → SpeechSettings-DPddWDCV.js} +1 -1
  68. package/src/assets/web-panel/assets/{SyncSettings-tZDaJZ0S.js → SyncSettings-CY-Fdii1.js} +1 -1
  69. package/src/assets/web-panel/assets/{Tasks-DxBlrylF.js → Tasks-DAWxFePY.js} +1 -1
  70. package/src/assets/web-panel/assets/{Templates-ByshgJtu.js → Templates-CuIQmfXP.js} +1 -1
  71. package/src/assets/web-panel/assets/{Tenant-DCpEpRf1.js → Tenant-CYsUC_mL.js} +1 -1
  72. package/src/assets/web-panel/assets/{Tokens-BIUug_M_.js → Tokens-DpwM8qKh.js} +1 -1
  73. package/src/assets/web-panel/assets/{Trigger-DY4iT_Sv.js → Trigger-CBHUHnc-.js} +1 -1
  74. package/src/assets/web-panel/assets/{Trust-fAtm0ohe.js → Trust-DfQZgxcX.js} +1 -1
  75. package/src/assets/web-panel/assets/{UkeySign-CwbV_ITc.js → UkeySign-BcOnuHWe.js} +1 -1
  76. package/src/assets/web-panel/assets/{VideoEditing-CBJ5dw8E.js → VideoEditing-Ci-c6HHb.js} +1 -1
  77. package/src/assets/web-panel/assets/{Wallet-B1fyWeNC.js → Wallet-DBuVM9t_.js} +4 -4
  78. package/src/assets/web-panel/assets/{WebAuthn-by6IZMeB.js → WebAuthn-Dc3XC6-l.js} +5 -5
  79. package/src/assets/web-panel/assets/{WorkflowEditor-DZkBKsSU.js → WorkflowEditor-BFBZCgWB.js} +1 -1
  80. package/src/assets/web-panel/assets/{chat-CKAWhBMH.js → chat-tqUunmHQ.js} +1 -1
  81. package/src/assets/web-panel/assets/{collapseMotion-6-_FbiKl.js → collapseMotion-CZQhO80h.js} +1 -1
  82. package/src/assets/web-panel/assets/{colors-DgRS7Mb2.js → colors-7dfqj18R.js} +1 -1
  83. package/src/assets/web-panel/assets/{compact-item-CQUZCSRU.js → compact-item-_8YefjuR.js} +1 -1
  84. package/src/assets/web-panel/assets/{createContext-DgOciZHM.js → createContext-CPZ59Zcw.js} +1 -1
  85. package/src/assets/web-panel/assets/{echarts-BrEnqXDv.js → echarts-TwhxblyG.js} +1 -1
  86. package/src/assets/web-panel/assets/{hasIn-DEXVOypF.js → hasIn-BidaS7SK.js} +1 -1
  87. package/src/assets/web-panel/assets/icons-Q99MnhG8.js +57 -0
  88. package/src/assets/web-panel/assets/{index-wPVZuwWm.js → index-51Z_iRMU.js} +2 -2
  89. package/src/assets/web-panel/assets/{index-BYrBEkTv.js → index-B3-Fdfqy.js} +1 -1
  90. package/src/assets/web-panel/assets/{index-nMw96bEM.js → index-BJmJfXGw.js} +1 -1
  91. package/src/assets/web-panel/assets/{index-HE37tYPs.js → index-BUlPL2uR.js} +1 -1
  92. package/src/assets/web-panel/assets/{index-ZOpqRGzA.js → index-B_pQvbuN.js} +1 -1
  93. package/src/assets/web-panel/assets/{index-D8S-7oDQ.js → index-BqsvxElz.js} +9 -9
  94. package/src/assets/web-panel/assets/{index-CS3fI6_d.js → index-BtBRkcWM.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-CCuIY7Ff.js → index-C0MN4j0q.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-D1dIizuf.js → index-CAUH-6ta.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-o6vVX9C_.js → index-CG0swGFV.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-Bo43zHIV.js → index-CLceS3rm.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-DkBSIb9k.js → index-CW90j-dZ.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-hfGZzUzb.js → index-Ca0EAS0W.js} +1 -1
  101. package/src/assets/web-panel/assets/index-Ca5Qc31r.js +1 -0
  102. package/src/assets/web-panel/assets/{index-B1USa9q3.js → index-CmCovo5r.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-CQepaPId.js → index-CvkR7sYL.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-D3tqZAdL.js → index-Cw1VkvZr.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-D2PDzVV6.js → index-Cxwyyzpd.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-DBRA1wFF.js → index-Cxy6yDHf.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-DpL-43E_.js → index-CzARuKje.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-BQDGHgXQ.js → index-D7yjk-E8.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-1EDbawpk.js → index-D8upWkW0.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-DW_-28b4.js → index-D95E7B-a.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-DjQzFo-O.js → index-DJa3dvQf.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-BEnMJ3rw.js → index-DJfKgyA-.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-Ce57JGJq.js → index-DMRsx5W4.js} +1 -1
  114. package/src/assets/web-panel/assets/index-DP7namG9.js +21 -0
  115. package/src/assets/web-panel/assets/{index-jLpDpunJ.js → index-DTmWBWkJ.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-CH5YkJC9.js → index-DTwrQg1G.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-_Oe8Zr99.js → index-DWSEcCO0.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-BMx0R9Vw.js → index-D_vErh-N.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-BjpS0vcp.js → index-DccwP9Op.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-CetlLBjh.js → index-Dev3hWia.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-CEt0RfAY.js → index-DfHN8qWn.js} +1 -1
  122. package/src/assets/web-panel/assets/index-Ds3hIeDc.js +1 -0
  123. package/src/assets/web-panel/assets/{index-nIXYg_tf.js → index-DsykGbFs.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-bRI-SvZ-.js → index-OZ08PLpO.js} +1 -1
  125. package/src/assets/web-panel/assets/index-QVPLcn-I.js +1 -0
  126. package/src/assets/web-panel/assets/{index-B-g1My7q.js → index-Xg1JWxyg.js} +1 -1
  127. package/src/assets/web-panel/assets/{initDefaultProps-BSTz5E8D.js → initDefaultProps-DBQ94EVq.js} +1 -1
  128. package/src/assets/web-panel/assets/{markdown-xjUYbPSW.js → markdown-CsiA8-E5.js} +1 -1
  129. package/src/assets/web-panel/assets/{markdown-vYH_ziAO.js → markdown-Yo_c6Krv.js} +1 -1
  130. package/src/assets/web-panel/assets/{motion-B10pjDA-.js → motion-BAgV2nZt.js} +1 -1
  131. package/src/assets/web-panel/assets/{move-B_PFOXXn.js → move-CPhu8BHp.js} +1 -1
  132. package/src/assets/web-panel/assets/{omit-Bf31gUOR.js → omit-BKpLUN0q.js} +1 -1
  133. package/src/assets/web-panel/assets/{pickAttrs-CTOOvnV9.js → pickAttrs-BicOrtU0.js} +1 -1
  134. package/src/assets/web-panel/assets/{placementArrow-DfXqIy1B.js → placementArrow-D2-Ct0PH.js} +1 -1
  135. package/src/assets/web-panel/assets/{responsiveObserve-BonUzZVT.js → responsiveObserve-DGFr293p.js} +1 -1
  136. package/src/assets/web-panel/assets/{slide-DtkoQrl3.js → slide-C7zAHcKr.js} +1 -1
  137. package/src/assets/web-panel/assets/{statusUtils-BJlmtjwo.js → statusUtils-ByfnDIgi.js} +1 -1
  138. package/src/assets/web-panel/assets/{styleChecker-CoQ2DFN_.js → styleChecker-C-ldcMcB.js} +1 -1
  139. package/src/assets/web-panel/assets/useFlexGapSupport-CKCvqfWX.js +1 -0
  140. package/src/assets/web-panel/assets/{useFs-DNCH6P7V.js → useFs-yX-MUKbZ.js} +1 -1
  141. package/src/assets/web-panel/assets/{useMergedState-n0f81Aew.js → useMergedState-D3PX36GT.js} +1 -1
  142. package/src/assets/web-panel/assets/{useRefs-CTh0C9xI.js → useRefs-opfcK7Pm.js} +1 -1
  143. package/src/assets/web-panel/assets/{useState-4T1Ntfpx.js → useState-Cp5WlLTS.js} +1 -1
  144. package/src/assets/web-panel/assets/{vendor-Ci2pTZ_t.js → vendor-BVLz_z7V.js} +1 -1
  145. package/src/assets/web-panel/assets/{vnode-CAFNU-3N.js → vnode-CW-Z-Xwr.js} +1 -1
  146. package/src/assets/web-panel/assets/{zoom-Beg44Pk9.js → zoom-DR60LSPO.js} +1 -1
  147. package/src/assets/web-panel/index.html +3 -3
  148. package/src/commands/marketplace.js +286 -0
  149. package/src/commands/mtc.js +1 -1
  150. package/src/commands/multisig.js +471 -0
  151. package/src/commands/p2p.js +114 -7
  152. package/src/commands/project.js +258 -0
  153. package/src/index.js +5 -0
  154. package/src/lib/audit-mtc.js +1 -1
  155. package/src/lib/multisig-runtime.js +160 -0
  156. package/src/lib/p2p-manager.js +96 -2
  157. package/src/lib/project-runtime.js +171 -0
  158. package/src/assets/web-panel/assets/AppLayout-GvY1b95g.js +0 -1
  159. package/src/assets/web-panel/assets/AppLayout-P7jhSfLy.css +0 -1
  160. package/src/assets/web-panel/assets/Projects-B5IgXt-x.css +0 -1
  161. package/src/assets/web-panel/assets/icons-B2G69bhT.js +0 -57
  162. package/src/assets/web-panel/assets/index-DLnvncqr.js +0 -1
  163. package/src/assets/web-panel/assets/index-DsmrJv8Y.js +0 -21
  164. package/src/assets/web-panel/assets/index-Rvjbak1o.js +0 -1
  165. package/src/assets/web-panel/assets/useFlexGapSupport-BD9H_ICR.js +0 -1
@@ -0,0 +1,258 @@
1
+ /**
2
+ * v1.3+ #21 P1 — `cc project` CLI surface.
3
+ *
4
+ * 子命令:
5
+ * cc project init <name> Create a new project in desktop DB
6
+ * --description <text> Optional description
7
+ * --type <type> web|document|data|app|presentation|
8
+ * spreadsheet|design|code|workflow|knowledge
9
+ * (default: document)
10
+ * --user <userId> User ID (default: "default")
11
+ * --root <path> Optional root_path on filesystem
12
+ * --json JSON output
13
+ *
14
+ * cc project list [--user <id>] [--status <s>] [--limit <n>] [--json]
15
+ * cc project show <id> [--json]
16
+ * cc project delete <id> [--hard] [--json]
17
+ *
18
+ * 设计 (per #21 v1.2 GA 反馈 "目标在手机端做ai项目的交互要像在电脑端那样丝滑"):
19
+ * CLI 直接读写 desktop chainlesschain.db, 立刻在 desktop UI 出现,
20
+ * Phase 3d sync 同步到手机端 — 桌面/CLI/手机 三端零延迟一致。
21
+ */
22
+
23
+ import chalk from "chalk";
24
+ import {
25
+ openProjectsDb,
26
+ defaultProjectDbPath,
27
+ newProjectId,
28
+ VALID_PROJECT_TYPES,
29
+ VALID_PROJECT_STATUSES,
30
+ } from "../lib/project-runtime.js";
31
+
32
+ const DEFAULT_USER = "default";
33
+
34
+ function _now() {
35
+ return Date.now();
36
+ }
37
+
38
+ function _formatProjectTable(p) {
39
+ return [
40
+ `${chalk.bold("ID")} ${p.id}`,
41
+ `${chalk.bold("Name")} ${p.name}`,
42
+ `${chalk.bold("Type")} ${p.project_type}`,
43
+ `${chalk.bold("Status")} ${_colorStatus(p.status)}`,
44
+ `${chalk.bold("User")} ${p.user_id}`,
45
+ `${chalk.bold("Root")} ${p.root_path || "(none)"}`,
46
+ `${chalk.bold("Files")} ${p.file_count || 0}`,
47
+ `${chalk.bold("Created")} ${new Date(p.created_at).toISOString()}`,
48
+ `${chalk.bold("Updated")} ${new Date(p.updated_at).toISOString()}`,
49
+ p.description ? `${chalk.bold("Desc")} ${p.description}` : null,
50
+ ]
51
+ .filter(Boolean)
52
+ .join("\n");
53
+ }
54
+
55
+ function _colorStatus(s) {
56
+ switch (s) {
57
+ case "draft":
58
+ return chalk.gray(s);
59
+ case "active":
60
+ return chalk.cyan(s);
61
+ case "completed":
62
+ return chalk.green(s);
63
+ case "archived":
64
+ return chalk.yellow(s);
65
+ default:
66
+ return s;
67
+ }
68
+ }
69
+
70
+ export function registerProjectCommand(program) {
71
+ const cmd = program
72
+ .command("project")
73
+ .description(
74
+ "Manage desktop projects (shared SQLite, syncs to mobile via Phase 3d)",
75
+ );
76
+
77
+ // ===== init =====
78
+ cmd
79
+ .command("init <name>")
80
+ .description("Create a new project in desktop DB")
81
+ .option("--description <text>", "Project description")
82
+ .option(
83
+ "--type <type>",
84
+ `Project type (${VALID_PROJECT_TYPES.join("|")})`,
85
+ "document",
86
+ )
87
+ .option("--user <userId>", "Owner user ID", DEFAULT_USER)
88
+ .option("--root <path>", "Root filesystem path")
89
+ .option("--db <path>", "DB path override", defaultProjectDbPath())
90
+ .option("--json", "Output JSON")
91
+ .action(async (name, options) => {
92
+ if (!VALID_PROJECT_TYPES.includes(options.type)) {
93
+ console.error(
94
+ chalk.red(
95
+ `Invalid type "${options.type}". Must be one of: ${VALID_PROJECT_TYPES.join(", ")}`,
96
+ ),
97
+ );
98
+ process.exit(2);
99
+ }
100
+ const { db, close } = await openProjectsDb(options.db);
101
+ try {
102
+ const id = newProjectId();
103
+ const now = _now();
104
+ db.prepare(
105
+ `INSERT INTO projects (
106
+ id, user_id, name, description, project_type, status,
107
+ root_path, created_at, updated_at, sync_status, deleted
108
+ ) VALUES (?, ?, ?, ?, ?, 'active', ?, ?, ?, 'pending', 0)`,
109
+ ).run(
110
+ id,
111
+ options.user,
112
+ name,
113
+ options.description || null,
114
+ options.type,
115
+ options.root || null,
116
+ now,
117
+ now,
118
+ );
119
+ const p = db.prepare("SELECT * FROM projects WHERE id = ?").get(id);
120
+ if (options.json) {
121
+ console.log(JSON.stringify(p, null, 2));
122
+ } else {
123
+ console.log(chalk.green("✓ Project created"));
124
+ console.log(_formatProjectTable(p));
125
+ console.log(
126
+ chalk.gray(
127
+ `\n 桌面 UI 会立刻看到此项目。Phase 3d sync 自动同步到手机端。`,
128
+ ),
129
+ );
130
+ }
131
+ } finally {
132
+ close();
133
+ }
134
+ });
135
+
136
+ // ===== list =====
137
+ cmd
138
+ .command("list")
139
+ .description("List projects")
140
+ .option("--user <userId>", "Filter by user ID", DEFAULT_USER)
141
+ .option(
142
+ "--status <s>",
143
+ `Filter by status (${VALID_PROJECT_STATUSES.join("|")})`,
144
+ )
145
+ .option("--limit <n>", "Max rows", (v) => parseInt(v, 10), 50)
146
+ .option("--db <path>", "DB path override", defaultProjectDbPath())
147
+ .option("--json", "Output JSON")
148
+ .action(async (options) => {
149
+ const { db, close } = await openProjectsDb(options.db);
150
+ try {
151
+ let sql = `SELECT id, user_id, name, description, project_type, status,
152
+ root_path, file_count, created_at, updated_at, sync_status
153
+ FROM projects WHERE user_id = ? AND deleted = 0`;
154
+ const params = [options.user];
155
+ if (options.status) {
156
+ if (!VALID_PROJECT_STATUSES.includes(options.status)) {
157
+ console.error(
158
+ chalk.red(
159
+ `Invalid status "${options.status}". Must be: ${VALID_PROJECT_STATUSES.join(", ")}`,
160
+ ),
161
+ );
162
+ process.exit(2);
163
+ }
164
+ sql += " AND status = ?";
165
+ params.push(options.status);
166
+ }
167
+ sql += " ORDER BY updated_at DESC LIMIT ?";
168
+ params.push(options.limit);
169
+ const rows = db.prepare(sql).all(...params);
170
+ if (options.json) {
171
+ console.log(JSON.stringify(rows, null, 2));
172
+ } else {
173
+ if (rows.length === 0) {
174
+ console.log(chalk.gray("(no projects)"));
175
+ return;
176
+ }
177
+ for (const p of rows) {
178
+ console.log(
179
+ `${chalk.gray(p.id.slice(0, 8))}… ${_colorStatus(p.status.padEnd(10))} ${chalk.bold(
180
+ p.name.padEnd(28),
181
+ )} ${chalk.gray(p.project_type.padEnd(12))} ${new Date(p.updated_at).toISOString()}`,
182
+ );
183
+ }
184
+ }
185
+ } finally {
186
+ close();
187
+ }
188
+ });
189
+
190
+ // ===== show =====
191
+ cmd
192
+ .command("show <id>")
193
+ .description("Show project details")
194
+ .option("--db <path>", "DB path override", defaultProjectDbPath())
195
+ .option("--json", "Output JSON")
196
+ .action(async (id, options) => {
197
+ const { db, close } = await openProjectsDb(options.db);
198
+ try {
199
+ const p = db.prepare("SELECT * FROM projects WHERE id = ?").get(id);
200
+ if (!p) {
201
+ console.error(chalk.red(`Project not found: ${id}`));
202
+ process.exit(2);
203
+ }
204
+ if (options.json) {
205
+ console.log(JSON.stringify(p, null, 2));
206
+ } else {
207
+ console.log(_formatProjectTable(p));
208
+ }
209
+ } finally {
210
+ close();
211
+ }
212
+ });
213
+
214
+ // ===== delete =====
215
+ cmd
216
+ .command("delete <id>")
217
+ .description("Delete a project (soft by default; --hard to truly remove)")
218
+ .option("--hard", "Hard delete (also removes project_files cascade)", false)
219
+ .option("--db <path>", "DB path override", defaultProjectDbPath())
220
+ .option("--json", "Output JSON")
221
+ .action(async (id, options) => {
222
+ const { db, close } = await openProjectsDb(options.db);
223
+ try {
224
+ const existing = db
225
+ .prepare("SELECT id FROM projects WHERE id = ?")
226
+ .get(id);
227
+ if (!existing) {
228
+ console.error(chalk.red(`Project not found: ${id}`));
229
+ process.exit(2);
230
+ }
231
+ if (options.hard) {
232
+ db.prepare("DELETE FROM projects WHERE id = ?").run(id);
233
+ } else {
234
+ db.prepare(
235
+ "UPDATE projects SET deleted = 1, sync_status = 'pending', updated_at = ? WHERE id = ?",
236
+ ).run(_now(), id);
237
+ }
238
+ if (options.json) {
239
+ console.log(
240
+ JSON.stringify({ ok: true, id, hard: !!options.hard }, null, 2),
241
+ );
242
+ } else {
243
+ console.log(
244
+ chalk.green(
245
+ `✓ Project ${options.hard ? "hard-deleted" : "deleted"}: ${id}`,
246
+ ),
247
+ );
248
+ }
249
+ } finally {
250
+ close();
251
+ }
252
+ });
253
+ }
254
+
255
+ // 暴露给单测
256
+ export const _projectInternals = {
257
+ defaultProjectDbPath,
258
+ };
package/src/index.js CHANGED
@@ -195,6 +195,9 @@ import { registerRuntimeCommand } from "./commands/runtime.js";
195
195
  import { registerIpfsCommand } from "./commands/ipfs.js";
196
196
  // MTC: Merkle Tree Certificates (Phase 1 Week 3)
197
197
  import { registerMtcCommand } from "./commands/mtc.js";
198
+ import { registerMultisigCommand } from "./commands/multisig.js";
199
+ // #21 P1: cc project — shared desktop SQLite, Phase 3d sync to mobile
200
+ import { registerProjectCommand } from "./commands/project.js";
198
201
  // Phase 27: Multimodal Collaboration
199
202
  import { registerMultimodalCommand } from "./commands/multimodal.js";
200
203
  // Phase 22: Performance auto-tuning
@@ -579,6 +582,8 @@ export function createProgram(opts = {}) {
579
582
  registerRuntimeCommand(program);
580
583
  registerIpfsCommand(program);
581
584
  registerMtcCommand(program);
585
+ registerMultisigCommand(program);
586
+ registerProjectCommand(program);
582
587
  registerMultimodalCommand(program);
583
588
  registerPerfCommand(program);
584
589
  registerAgentNetworkCommand(program);
@@ -20,7 +20,7 @@
20
20
  import fs from "node:fs";
21
21
  import path from "node:path";
22
22
  import crypto from "node:crypto";
23
- import { ed25519 as nobleEd25519 } from "@noble/curves/ed25519";
23
+ import { ed25519 as nobleEd25519 } from "@noble/curves/ed25519.js";
24
24
  import mtcLib from "@chainlesschain/core-mtc";
25
25
 
26
26
  const { sha256, jcs, encodeHashStr, ed25519, assembleBatch } = mtcLib;
@@ -0,0 +1,160 @@
1
+ /**
2
+ * v1.2 m-of-n Phase 2 — shared multisig DB + manager loader.
3
+ *
4
+ * Phase 1 inlined this in commands/multisig.js. Phase 2 marketplace mediator
5
+ * also needs it (cc marketplace purchase / consume), so factor out here.
6
+ *
7
+ * SQLite driver cascade: better-sqlite3-multiple-ciphers → better-sqlite3 →
8
+ * sql.js (WASM) per feedback_sqlite_wasm_fallback memo. sql.js gets a
9
+ * better-sqlite3-style adapter for the subset core-multisig/store.js needs.
10
+ */
11
+
12
+ import fs from "node:fs";
13
+ import path from "node:path";
14
+ import { createRequire } from "node:module";
15
+ import multisig from "@chainlesschain/core-multisig";
16
+ import { getHomeDir } from "./paths.js";
17
+
18
+ const requireCjs = createRequire(import.meta.url);
19
+
20
+ const {
21
+ applySchema,
22
+ createStore,
23
+ createProposalsManager,
24
+ appendGovernanceEvent,
25
+ } = multisig;
26
+
27
+ export function defaultMultisigDbPath() {
28
+ return path.join(getHomeDir(), "multisig.db");
29
+ }
30
+
31
+ export function defaultMultisigLogPath() {
32
+ return path.join(getHomeDir(), "multisig.governance.log");
33
+ }
34
+
35
+ async function _openDatabase(dbPath) {
36
+ for (const pkg of ["better-sqlite3-multiple-ciphers", "better-sqlite3"]) {
37
+ try {
38
+ const Database = requireCjs(pkg);
39
+ const db = new Database(dbPath);
40
+ db.pragma("journal_mode = WAL");
41
+ db.pragma("foreign_keys = ON");
42
+ return { kind: "native", db, close: () => db.close() };
43
+ } catch (_e) {
44
+ /* try next */
45
+ }
46
+ }
47
+ const initSqlJs = requireCjs("sql.js");
48
+ const SQL = await initSqlJs();
49
+ const buf = fs.existsSync(dbPath) ? fs.readFileSync(dbPath) : undefined;
50
+ const sqlDb = new SQL.Database(buf);
51
+ return {
52
+ kind: "wasm",
53
+ db: _adaptSqlJs(sqlDb),
54
+ close: () => {
55
+ const out = sqlDb.export();
56
+ fs.writeFileSync(dbPath, Buffer.from(out));
57
+ sqlDb.close();
58
+ },
59
+ };
60
+ }
61
+
62
+ function _adaptSqlJs(sqlDb) {
63
+ const norm = (params) =>
64
+ params.map((p) => (Buffer.isBuffer(p) ? new Uint8Array(p) : p));
65
+ return {
66
+ prepare(sql) {
67
+ return {
68
+ run(...params) {
69
+ const stmt = sqlDb.prepare(sql);
70
+ try {
71
+ stmt.bind(norm(params));
72
+ stmt.step();
73
+ return { changes: sqlDb.getRowsModified(), lastInsertRowid: 0 };
74
+ } finally {
75
+ stmt.free();
76
+ }
77
+ },
78
+ get(...params) {
79
+ const stmt = sqlDb.prepare(sql);
80
+ try {
81
+ stmt.bind(norm(params));
82
+ return stmt.step() ? stmt.getAsObject() : undefined;
83
+ } finally {
84
+ stmt.free();
85
+ }
86
+ },
87
+ all(...params) {
88
+ const stmt = sqlDb.prepare(sql);
89
+ try {
90
+ stmt.bind(norm(params));
91
+ const rows = [];
92
+ while (stmt.step()) rows.push(stmt.getAsObject());
93
+ return rows;
94
+ } finally {
95
+ stmt.free();
96
+ }
97
+ },
98
+ };
99
+ },
100
+ exec(sql) {
101
+ sqlDb.exec(sql);
102
+ },
103
+ };
104
+ }
105
+
106
+ /**
107
+ * Open a multisig manager backed by SQLite + governance log.
108
+ *
109
+ * @param {string} [dbPath]
110
+ * @param {string} [logPath]
111
+ * @returns {Promise<{ db, store, mgr, close }>}
112
+ */
113
+ export async function openMultisigManager(
114
+ dbPath = defaultMultisigDbPath(),
115
+ logPath = defaultMultisigLogPath(),
116
+ ) {
117
+ fs.mkdirSync(path.dirname(dbPath), { recursive: true });
118
+ const handle = await _openDatabase(dbPath);
119
+ applySchema(handle.db);
120
+ const store = createStore(handle.db);
121
+ const mgr = createProposalsManager(store, {
122
+ logEvent: (event) => appendGovernanceEvent(logPath, event),
123
+ });
124
+ return {
125
+ db: handle.db,
126
+ store,
127
+ mgr,
128
+ close: handle.close,
129
+ };
130
+ }
131
+
132
+ /**
133
+ * Read a secret key from hex string or hex file path.
134
+ *
135
+ * @param {string} arg
136
+ * @returns {Buffer}
137
+ */
138
+ export function readSecretKey(arg) {
139
+ if (!arg) {
140
+ throw new Error("--key required (hex string or path to hex file)");
141
+ }
142
+ if (/^[0-9a-fA-F]+$/.test(arg)) {
143
+ return Buffer.from(arg, "hex");
144
+ }
145
+ if (fs.existsSync(arg)) {
146
+ const raw = fs.readFileSync(arg, "utf-8").trim();
147
+ return Buffer.from(raw, "hex");
148
+ }
149
+ throw new Error(`--key: not hex and not an existing file path: ${arg}`);
150
+ }
151
+
152
+ /**
153
+ * Read JSON from inline string or file path.
154
+ */
155
+ export function readJsonArg(arg) {
156
+ if (fs.existsSync(arg)) {
157
+ return JSON.parse(fs.readFileSync(arg, "utf-8"));
158
+ }
159
+ return JSON.parse(arg);
160
+ }
@@ -223,6 +223,90 @@ export function pairDevice(db, deviceName, deviceType) {
223
223
  return { deviceId, deviceName, deviceType, pairingCode, status: "pending" };
224
224
  }
225
225
 
226
+ /**
227
+ * v1.1 W3.5 (issue #19): Pair a device from a scanned QR payload.
228
+ *
229
+ * payload 字段对齐 desktop `device-pairing-handler.js::validatePairingCode`
230
+ * + Android `DesktopPairingViewModel::PairingQrPayload`:
231
+ * {type:"device-pairing", code, did, deviceInfo:{deviceId,name,platform}, timestamp}
232
+ *
233
+ * 与 [pairDevice] 区别:deviceId 来自 QR(移动端的稳定 ID)不是 host 生成,
234
+ * 这样 desktop unpair → 重扫 QR 能复用同 deviceId 而非每次生新条。INSERT OR
235
+ * REPLACE upsert,重复扫同一 QR 是 idempotent。
236
+ *
237
+ * Validate 项与 desktop validatePairingCode 对齐:
238
+ * - payload.type === "device-pairing"
239
+ * - payload.code 匹配 /^\d{6}$/
240
+ * - payload.did 非空
241
+ * - payload.deviceInfo.deviceId 非空
242
+ * - payload.timestamp 不超 5min(与 desktop pairingTimeout 对齐)
243
+ *
244
+ * 返回 {success: true, deviceId, deviceName, did, status:'active'}
245
+ * 或 {success: false, error}
246
+ */
247
+ export function pairDeviceFromQr(db, payload, opts = {}) {
248
+ ensureP2PTables(db);
249
+ const { now = Date.now(), maxAgeMs = 5 * 60 * 1000 } = opts;
250
+
251
+ if (!payload || typeof payload !== "object") {
252
+ return { success: false, error: "payload 不是对象" };
253
+ }
254
+ if (payload.type !== "device-pairing") {
255
+ return { success: false, error: "无效的 QR 类型,期望 device-pairing" };
256
+ }
257
+ if (!/^\d{6}$/.test(String(payload.code || ""))) {
258
+ return { success: false, error: "code 必须是 6 位数字" };
259
+ }
260
+ if (!payload.did || typeof payload.did !== "string") {
261
+ return { success: false, error: "did 必填" };
262
+ }
263
+ const info = payload.deviceInfo || {};
264
+ if (!info.deviceId || typeof info.deviceId !== "string") {
265
+ return { success: false, error: "deviceInfo.deviceId 必填" };
266
+ }
267
+ const ts = Number(payload.timestamp);
268
+ if (!Number.isFinite(ts)) {
269
+ return { success: false, error: "timestamp 必填且为数字" };
270
+ }
271
+ const age = now - ts;
272
+ if (age > maxAgeMs) {
273
+ return {
274
+ success: false,
275
+ error: `QR 已过期(${Math.round(age / 1000)}s > ${maxAgeMs / 1000}s)`,
276
+ };
277
+ }
278
+
279
+ const deviceName = info.name || "(unnamed)";
280
+ const platform = (info.platform || "mobile").toLowerCase();
281
+ // desktop validatePairingCode 接受 platform 为 string 不限值;这里 normalize
282
+ // 进 p2p_paired_devices.device_type 的 desktop/mobile/tablet 三档:
283
+ // - "android"/"ios" → "mobile"
284
+ // - 其它已知值保留
285
+ // - 缺省 "mobile"
286
+ const deviceType = ["mobile", "tablet", "desktop"].includes(platform)
287
+ ? platform
288
+ : ["android", "ios"].includes(platform)
289
+ ? "mobile"
290
+ : "mobile";
291
+
292
+ // INSERT OR REPLACE 让重复扫同 deviceId 是 idempotent;status="active" 因
293
+ // QR 流程的 6 位 code 视作 desktop 已确认,无需 pending 中间态。
294
+ db.prepare(
295
+ `INSERT OR REPLACE INTO p2p_paired_devices
296
+ (device_id, device_name, device_type, pairing_code, paired_at, status)
297
+ VALUES (?, ?, ?, ?, datetime('now'), 'active')`,
298
+ ).run(info.deviceId, deviceName, deviceType, payload.code);
299
+
300
+ return {
301
+ success: true,
302
+ deviceId: info.deviceId,
303
+ deviceName,
304
+ deviceType,
305
+ did: payload.did,
306
+ status: "active",
307
+ };
308
+ }
309
+
226
310
  /**
227
311
  * Confirm device pairing with code.
228
312
  */
@@ -242,10 +326,20 @@ export function confirmPairing(db, deviceId, code) {
242
326
  }
243
327
 
244
328
  /**
245
- * Get all paired devices.
329
+ * Get all paired devices, optionally filtered by device_type.
330
+ *
331
+ * v1.1 W3.4a (issue #19):`type` filter 让 web-panel MobileBridge.vue
332
+ * 只拉 mobile 子集。`type` 必须 ∈ {'desktop','mobile','tablet'} 或不传。
246
333
  */
247
- export function getPairedDevices(db) {
334
+ export function getPairedDevices(db, type = null) {
248
335
  ensureP2PTables(db);
336
+ if (type) {
337
+ return db
338
+ .prepare(
339
+ "SELECT * FROM p2p_paired_devices WHERE device_type = ? ORDER BY paired_at DESC",
340
+ )
341
+ .all(type);
342
+ }
249
343
  return db
250
344
  .prepare("SELECT * FROM p2p_paired_devices ORDER BY paired_at DESC")
251
345
  .all();