chainlesschain 0.161.7 → 0.161.9

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 (149) hide show
  1. package/bin/chainlesschain.js +0 -0
  2. package/package.json +2 -1
  3. package/src/assets/web-panel/.build-hash +1 -1
  4. package/src/assets/web-panel/assets/{AIOps-C06IYJkC.js → AIOps-Cbnj25uU.js} +1 -1
  5. package/src/assets/web-panel/assets/{ActionButton-Cq4f1js5.js → ActionButton-DY69UkxV.js} +1 -1
  6. package/src/assets/web-panel/assets/{Analytics-DyJsowHu.js → Analytics-DUzoL75H.js} +2 -2
  7. package/src/assets/web-panel/assets/AppLayout-DATUppSv.js +1 -0
  8. package/src/assets/web-panel/assets/{AppLayout-P7jhSfLy.css → AppLayout-qr7ghxW7.css} +1 -1
  9. package/src/assets/web-panel/assets/{Audit-C7y4wGzh.js → Audit-CFJh6huz.js} +1 -1
  10. package/src/assets/web-panel/assets/{Backup-BxkJJwr5.js → Backup-CMTir4tV.js} +1 -1
  11. package/src/assets/web-panel/assets/{BaseInput-BraWNP3N.js → BaseInput-BaWXBDdp.js} +1 -1
  12. package/src/assets/web-panel/assets/{Chat-BhZPLetb.js → Chat-DjToexEx.js} +3 -3
  13. package/src/assets/web-panel/assets/{Checkbox-DuDAToKQ.js → Checkbox-DJnhKXhi.js} +1 -1
  14. package/src/assets/web-panel/assets/{Codegen-DWHT3ZCT.js → Codegen-CgsgvRJX.js} +1 -1
  15. package/src/assets/web-panel/assets/{Col-rZ1FbPeP.js → Col-MvMGDJAB.js} +1 -1
  16. package/src/assets/web-panel/assets/{Community-BHefT_qI.js → Community-q26VGOGu.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compact-D68vLPVh.js → Compact-DyLYzWoX.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compliance-cxmKFzWJ.js → Compliance-D_TF9eBc.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cowork-Dzt1uT50.js → Cowork-B9bg988c.js} +3 -3
  20. package/src/assets/web-panel/assets/{Cron-oWxj9K9p.js → Cron-6k9vTFfd.js} +2 -2
  21. package/src/assets/web-panel/assets/{Crosschain-C9UdRkm2.js → Crosschain-B7fk2aHi.js} +1 -1
  22. package/src/assets/web-panel/assets/{DID-D5WEHKMO.js → DID-XhCfTFm2.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dashboard-dP1_OGh-.js → Dashboard-Bqk7vtZM.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dropdown-C55GjQPR.js → Dropdown-CDOOZGv7.js} +1 -1
  25. package/src/assets/web-panel/assets/{Federation-oJxrz7TD.js → Federation-NajN4oRO.js} +1 -1
  26. package/src/assets/web-panel/assets/{FormItemContext-CeLP5Pvz.js → FormItemContext-DgMHYlMB.js} +1 -1
  27. package/src/assets/web-panel/assets/{Git-CWs6CrRt.js → Git-CZC5n1Zu.js} +2 -2
  28. package/src/assets/web-panel/assets/{Governance-wo9Gz6OK.js → Governance-Br131eUW.js} +1 -1
  29. package/src/assets/web-panel/assets/{Inference-75Ob1OAF.js → Inference-BHlUfcT-.js} +1 -1
  30. package/src/assets/web-panel/assets/{KnowledgeGraph-DNw3Xz30.js → KnowledgeGraph-CByROhg-.js} +1 -1
  31. package/src/assets/web-panel/assets/{Logs-Bo7_f07I.js → Logs-D8Ghzk4B.js} +1 -1
  32. package/src/assets/web-panel/assets/{Marketplace-Dm0yQkoQ.js → Marketplace-CnjHPFm7.js} +1 -1
  33. package/src/assets/web-panel/assets/{McpTools-oh6SZgBB.js → McpTools-C_vszCBU.js} +1 -1
  34. package/src/assets/web-panel/assets/{Memory-DANDCrzX.js → Memory-Cl5c_8In.js} +2 -2
  35. package/src/assets/web-panel/assets/MobileBridge-DkkMCDx9.css +1 -0
  36. package/src/assets/web-panel/assets/MobileBridge-K3KwwCKl.js +3 -0
  37. package/src/assets/web-panel/assets/{Mtc-4D52E6FP.js → Mtc-NfZ8xzyu.js} +4 -4
  38. package/src/assets/web-panel/assets/{MtcAudit-BmlorrLr.js → MtcAudit-BkktYSXj.js} +1 -1
  39. package/src/assets/web-panel/assets/Multisig-BLRYDn7_.css +1 -0
  40. package/src/assets/web-panel/assets/Multisig-qP51bs1T.js +1 -0
  41. package/src/assets/web-panel/assets/{NLProgramming-DolGSSOm.js → NLProgramming-BhFRuXyi.js} +1 -1
  42. package/src/assets/web-panel/assets/{Notes-BTyTq1Qk.js → Notes-BpcAFy8i.js} +3 -3
  43. package/src/assets/web-panel/assets/{NotificationSettings-Db1AaWp_.js → NotificationSettings-DKNtYYeo.js} +1 -1
  44. package/src/assets/web-panel/assets/{Organization-DC4RfB2j.js → Organization-C61VwQEW.js} +4 -4
  45. package/src/assets/web-panel/assets/{Overflow-cYKtW74T.js → Overflow-WBloac1x.js} +1 -1
  46. package/src/assets/web-panel/assets/{P2P-CY5dBoF-.js → P2P-DGm7EvDg.js} +2 -2
  47. package/src/assets/web-panel/assets/{Permissions-B-U6t8Wf.js → Permissions-KFPtoOV6.js} +2 -2
  48. package/src/assets/web-panel/assets/{Pipeline-a2pkdq6t.js → Pipeline-CFjFtzZT.js} +1 -1
  49. package/src/assets/web-panel/assets/{Privacy-ClgLdkPP.js → Privacy-T_Kewe_f.js} +1 -1
  50. package/src/assets/web-panel/assets/{ProjectSettings-D4Fi9bEQ.js → ProjectSettings-BQl8qTPQ.js} +1 -1
  51. package/src/assets/web-panel/assets/{Projects-BiSWtWMm.js → Projects-xGNn4Ov9.js} +2 -2
  52. package/src/assets/web-panel/assets/{Providers-ydpAao66.js → Providers-ByuUztXe.js} +1 -1
  53. package/src/assets/web-panel/assets/{QuickAsk-Bg955QRg.js → QuickAsk-BwSXmXvL.js} +1 -1
  54. package/src/assets/web-panel/assets/{Recommend-BqynHnTn.js → Recommend-Cp433_5Z.js} +1 -1
  55. package/src/assets/web-panel/assets/{Reputation-B8xfdWQt.js → Reputation-DE_JSjyi.js} +1 -1
  56. package/src/assets/web-panel/assets/{Row-D4zoYiqD.js → Row-DI36Fic1.js} +1 -1
  57. package/src/assets/web-panel/assets/{RssFeed-BI0FPtP6.js → RssFeed-DswpSUiI.js} +3 -3
  58. package/src/assets/web-panel/assets/{Search-BG975ISr.js → Search-Crs2UKhh.js} +1 -1
  59. package/src/assets/web-panel/assets/{Security-BObRR-p6.js → Security-W8ViXXbq.js} +3 -3
  60. package/src/assets/web-panel/assets/{Services-B2TScn1O.js → Services-BIQdyLJM.js} +2 -2
  61. package/src/assets/web-panel/assets/{Skeleton-PYzlv5Fw.js → Skeleton-DDiOevKX.js} +1 -1
  62. package/src/assets/web-panel/assets/{Skills-DbsTF84K.js → Skills-v-Vw_jGa.js} +1 -1
  63. package/src/assets/web-panel/assets/{Sla-D8q9j9bo.js → Sla-B-ULmz5m.js} +1 -1
  64. package/src/assets/web-panel/assets/{SpeechSettings-CjjNbmVH.js → SpeechSettings--8IIPHri.js} +1 -1
  65. package/src/assets/web-panel/assets/{SyncSettings-UEp5lB2T.js → SyncSettings-D0QiEwJn.js} +1 -1
  66. package/src/assets/web-panel/assets/{Tasks-CoCW4gWI.js → Tasks-BNSR9FlM.js} +1 -1
  67. package/src/assets/web-panel/assets/{Templates-hd7UB-8K.js → Templates-CFVkaS-d.js} +1 -1
  68. package/src/assets/web-panel/assets/{Tenant-CqebAGfR.js → Tenant-quU-tldS.js} +1 -1
  69. package/src/assets/web-panel/assets/{Tokens-9b0DDm86.js → Tokens-DTqFjS4k.js} +1 -1
  70. package/src/assets/web-panel/assets/{Trigger-CG_ZXii2.js → Trigger-ZWVirxhN.js} +1 -1
  71. package/src/assets/web-panel/assets/{Trust-CoCtornh.js → Trust-DmaYf5tg.js} +1 -1
  72. package/src/assets/web-panel/assets/{UkeySign-mBEc6SBj.js → UkeySign-BeYyXtZW.js} +1 -1
  73. package/src/assets/web-panel/assets/{VideoEditing-41j-ZZYu.js → VideoEditing-CRdDwD9C.js} +1 -1
  74. package/src/assets/web-panel/assets/{Wallet-DX4iBaEj.js → Wallet-H-38f7Pv.js} +4 -4
  75. package/src/assets/web-panel/assets/{WebAuthn-9BlQYiNI.js → WebAuthn-CCQVzZ0W.js} +4 -4
  76. package/src/assets/web-panel/assets/{WorkflowEditor-BVm3icIg.js → WorkflowEditor-B1RFE2oN.js} +1 -1
  77. package/src/assets/web-panel/assets/{chat-D98zBL7S.js → chat-Z4UoBAeF.js} +1 -1
  78. package/src/assets/web-panel/assets/{colors-ARL9W9UC.js → colors-AecMhEAZ.js} +1 -1
  79. package/src/assets/web-panel/assets/{compact-item-MYI7UP_X.js → compact-item-Dfhm05kA.js} +1 -1
  80. package/src/assets/web-panel/assets/{createContext-BddTmM7g.js → createContext-C7gBVuRm.js} +1 -1
  81. package/src/assets/web-panel/assets/{hasIn-2OsB0OBj.js → hasIn-DQarK3iI.js} +1 -1
  82. package/src/assets/web-panel/assets/icons-B2mcO3YM.js +57 -0
  83. package/src/assets/web-panel/assets/{index-kNDK9K4y.js → index-1d6L3YWV.js} +1 -1
  84. package/src/assets/web-panel/assets/{index-CXMgUcC_.js → index-5I_kEh5L.js} +1 -1
  85. package/src/assets/web-panel/assets/{index-BA8yc-bG.js → index-5P_X26-m.js} +1 -1
  86. package/src/assets/web-panel/assets/{index-tSE1B4ri.js → index-78eTUfD9.js} +1 -1
  87. package/src/assets/web-panel/assets/{index-BIpkHBFo.js → index-B1XdBIuG.js} +1 -1
  88. package/src/assets/web-panel/assets/{index-BNjLDXWZ.js → index-B52lOAOU.js} +1 -1
  89. package/src/assets/web-panel/assets/{index-CndUS0Yo.js → index-BLYMiBRw.js} +1 -1
  90. package/src/assets/web-panel/assets/{index-BKl1SKpb.js → index-BxjOmfVA.js} +9 -9
  91. package/src/assets/web-panel/assets/{index-awm9n2jp.js → index-C-4d1ug2.js} +1 -1
  92. package/src/assets/web-panel/assets/{index-CtCi5cvq.js → index-C0kyGID6.js} +1 -1
  93. package/src/assets/web-panel/assets/{index-TepTGolv.js → index-C7nHW5ZW.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-DK71wnJK.js → index-CCUfW7hO.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-N9X-UOF0.js → index-CFR1Scu8.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-DpAOSmby.js → index-CS7_x1br.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-B7XKJHg8.js → index-Ch9ExDNs.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-B5UMIkIp.js → index-CiVezMja.js} +1 -1
  99. package/src/assets/web-panel/assets/index-Cp5G30tN.js +1 -0
  100. package/src/assets/web-panel/assets/index-CuPmKar9.js +1 -0
  101. package/src/assets/web-panel/assets/{index-CuoiTv71.js → index-CxDBoEkf.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-BhgLJAQi.js → index-Cy_80Fj7.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-DVB0ott1.js → index-D3Pxfwcc.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-DnL-YqLO.js → index-D4_1yL1c.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-N8eKGGPN.js → index-D841tepe.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-hP2fDMJo.js → index-DWbRHjgk.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-D2lcW3-z.js → index-DX4CKtlL.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-BP5G8FRT.js → index-DdtxWGjo.js} +2 -2
  109. package/src/assets/web-panel/assets/{index-BO0TQWkU.js → index-DfA9S79c.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-BYbgYr7r.js → index-DfAZSzZc.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-C5qBLUjM.js → index-Dkh_4r1F.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-BSKiudeY.js → index-DtrMx44i.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-GxbpL_A7.js → index-Dy2fLm3i.js} +1 -1
  114. package/src/assets/web-panel/assets/index-L6kKxFAO.js +21 -0
  115. package/src/assets/web-panel/assets/{index-DqCyINtK.js → index-OqY41c_z.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-CISWdYQV.js → index-QgJGoGAK.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-Bc-fiOB-.js → index-YJxjR7t9.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-CHfRLYw8.js → index-aHqD7CZz.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-BYKZplcs.js → index-d0Iy6r2J.js} +1 -1
  120. package/src/assets/web-panel/assets/index-eoNKQdFC.js +1 -0
  121. package/src/assets/web-panel/assets/{index-Dz5ro_F6.js → index-nqKLHlmq.js} +1 -1
  122. package/src/assets/web-panel/assets/{initDefaultProps-CJbHV9hH.js → initDefaultProps-CDOGbz6v.js} +1 -1
  123. package/src/assets/web-panel/assets/{markdown-xjUYbPSW.js → markdown-CsiA8-E5.js} +1 -1
  124. package/src/assets/web-panel/assets/{markdown-vYH_ziAO.js → markdown-Yo_c6Krv.js} +1 -1
  125. package/src/assets/web-panel/assets/{motion-BTVrWA7N.js → motion-ChIpIzUN.js} +1 -1
  126. package/src/assets/web-panel/assets/{move-CRIIhWv7.js → move-DbH3dyN8.js} +1 -1
  127. package/src/assets/web-panel/assets/{omit-CDcQHhGi.js → omit-BJ1K-IDI.js} +1 -1
  128. package/src/assets/web-panel/assets/{pickAttrs-Dplb8z8U.js → pickAttrs-B0q7EpvV.js} +1 -1
  129. package/src/assets/web-panel/assets/{placementArrow-CP1cLg6P.js → placementArrow-D7XkOUjs.js} +1 -1
  130. package/src/assets/web-panel/assets/{responsiveObserve-PFMAIMDu.js → responsiveObserve-efcdE4ju.js} +1 -1
  131. package/src/assets/web-panel/assets/{slide-_CVxhTa3.js → slide-BgAktMzM.js} +1 -1
  132. package/src/assets/web-panel/assets/{statusUtils-6j8grlgm.js → statusUtils-CV83LVbI.js} +1 -1
  133. package/src/assets/web-panel/assets/{styleChecker-BbC7i8Sl.js → styleChecker-D1npYi6G.js} +1 -1
  134. package/src/assets/web-panel/assets/{useFlexGapSupport-Ctha7v2w.js → useFlexGapSupport-DTH3yXTD.js} +1 -1
  135. package/src/assets/web-panel/assets/{useFs-CeTUZIh2.js → useFs-BEvSw9aX.js} +1 -1
  136. package/src/assets/web-panel/assets/{vnode-kefkre4N.js → vnode-Duw65sZ1.js} +1 -1
  137. package/src/assets/web-panel/assets/{zoom-CZjCXV_O.js → zoom-CLMtA8PG.js} +1 -1
  138. package/src/assets/web-panel/index.html +2 -2
  139. package/src/commands/marketplace.js +286 -0
  140. package/src/commands/multisig.js +471 -0
  141. package/src/commands/p2p.js +114 -7
  142. package/src/index.js +2 -0
  143. package/src/lib/multisig-runtime.js +160 -0
  144. package/src/lib/p2p-manager.js +96 -2
  145. package/src/assets/web-panel/assets/AppLayout-DSFSWtOZ.js +0 -1
  146. package/src/assets/web-panel/assets/icons-B2G69bhT.js +0 -57
  147. package/src/assets/web-panel/assets/index-BNXp47dX.js +0 -21
  148. package/src/assets/web-panel/assets/index-G5xmB5Af.js +0 -1
  149. package/src/assets/web-panel/assets/index-qbPE-_18.js +0 -1
@@ -0,0 +1,471 @@
1
+ /**
2
+ * v1.2 m-of-n Phase 1d — `cc multisig` CLI surface.
3
+ *
4
+ * Subcommands:
5
+ * cc multisig propose <domain> --payload-file <path> [--initiator <did>] [--key <hex|path>]
6
+ * cc multisig sign <proposalId> --signer <did> [--key <hex|path>]
7
+ * cc multisig cancel <proposalId> [--reason <text>]
8
+ * cc multisig list [--state <s>] [--domain <d>] [--limit <n>] [--json]
9
+ * cc multisig show <proposalId> [--json]
10
+ * cc multisig finalize <proposalId> # 业务方完成后标 consumed
11
+ * cc multisig policy show <domain> [--json]
12
+ * cc multisig policy set <domain> --m <M> --members <json|file>
13
+ * cc multisig sweep # expire stale pending
14
+ *
15
+ * 设计:CLI 端持 better-sqlite3 DB (默认 ~/.chainlesschain/multisig.db) +
16
+ * governance log (默认 ~/.chainlesschain/multisig.governance.log)。
17
+ * secretKey 来源:--key <hex> 直接读 / --key-file <path> 读文件 hex。
18
+ * Phase 1d 内 keystore 集成留 v1.3 (与 core-did/UnifiedKeyStore 接通)。
19
+ */
20
+
21
+ import chalk from "chalk";
22
+ import multisig from "@chainlesschain/core-multisig";
23
+ import {
24
+ openMultisigManager,
25
+ defaultMultisigDbPath,
26
+ defaultMultisigLogPath,
27
+ readSecretKey,
28
+ readJsonArg,
29
+ } from "../lib/multisig-runtime.js";
30
+
31
+ const { normalizePolicy } = multisig;
32
+
33
+ // Phase 2 refactor: open / readKey / readJsonArg moved to lib/multisig-runtime.js
34
+ // so commands/marketplace.js can reuse the same SQLite cascade + manager loader.
35
+ // Aliases below preserve Phase 1 internal names without rewriting every callsite.
36
+ const _openManager = openMultisigManager;
37
+ const _defaultDbPath = defaultMultisigDbPath;
38
+ const _defaultLogPath = defaultMultisigLogPath;
39
+ const _readKey = readSecretKey;
40
+ const _readJsonArg = readJsonArg;
41
+
42
+ function _formatProposalTable(proposal, sigs = []) {
43
+ const lines = [
44
+ `${chalk.bold("ID")} ${proposal.id}`,
45
+ `${chalk.bold("Domain")} ${proposal.domain}`,
46
+ `${chalk.bold("State")} ${_colorState(proposal.state)}`,
47
+ `${chalk.bold("M / N")} ${proposal.thresholdM} / ${proposal.memberSet.length}`,
48
+ `${chalk.bold("Sigs")} ${sigs.length} / ${proposal.thresholdM}`,
49
+ `${chalk.bold("Initiator")} ${proposal.initiatorDid}`,
50
+ `${chalk.bold("Created")} ${new Date(proposal.createdAtMs).toISOString()}`,
51
+ `${chalk.bold("Expires")} ${new Date(proposal.expiresAtMs).toISOString()}`,
52
+ ];
53
+ return lines.join("\n");
54
+ }
55
+
56
+ function _colorState(state) {
57
+ switch (state) {
58
+ case "pending":
59
+ return chalk.yellow(state);
60
+ case "reached":
61
+ return chalk.cyan(state);
62
+ case "consumed":
63
+ return chalk.green(state);
64
+ case "cancelled":
65
+ return chalk.gray(state);
66
+ case "expired":
67
+ return chalk.red(state);
68
+ default:
69
+ return state;
70
+ }
71
+ }
72
+
73
+ export function registerMultisigCommand(program) {
74
+ const cmd = program
75
+ .command("multisig")
76
+ .description(
77
+ "M-of-N multi-signature proposals beyond MTC publisher_signature",
78
+ );
79
+
80
+ // ===== propose =====
81
+ cmd
82
+ .command("propose <domain>")
83
+ .description("Create a new M-of-N proposal and sign as initiator")
84
+ .requiredOption(
85
+ "--payload-file <path>",
86
+ "Path to JSON file containing the payload to be signed",
87
+ )
88
+ .requiredOption(
89
+ "--initiator <did>",
90
+ "Initiator DID — must be in policy.members",
91
+ )
92
+ .option("--alg <alg>", "Initiator signing alg", "Ed25519")
93
+ .option(
94
+ "--key <hex|path>",
95
+ "Initiator secret key hex string or path to hex file",
96
+ )
97
+ .option("--db <path>", "SQLite DB path", _defaultDbPath())
98
+ .option("--log <path>", "Governance log path", _defaultLogPath())
99
+ .option("--json", "Output JSON instead of human-readable")
100
+ .action(async (domain, options) => {
101
+ const { db, store, mgr, close } = await _openManager(
102
+ options.db,
103
+ options.log,
104
+ );
105
+ try {
106
+ const policy = store.getPolicy(domain);
107
+ if (!policy) {
108
+ console.error(
109
+ chalk.red(
110
+ `No policy set for domain "${domain}". Run: cc multisig policy set ${domain} --m <M> --members <json>`,
111
+ ),
112
+ );
113
+ process.exit(2);
114
+ }
115
+ const payload = _readJsonArg(options.payloadFile);
116
+ const secretKey = _readKey(options.key);
117
+ const result = mgr.propose({
118
+ domain,
119
+ payload,
120
+ policy,
121
+ initiator: { did: options.initiator, alg: options.alg, secretKey },
122
+ });
123
+ if (options.json) {
124
+ console.log(
125
+ JSON.stringify(
126
+ {
127
+ proposalId: result.proposal.id,
128
+ reachedThreshold: result.reachedThreshold,
129
+ },
130
+ null,
131
+ 2,
132
+ ),
133
+ );
134
+ } else {
135
+ console.log(chalk.green("✓ Proposal created"));
136
+ console.log(_formatProposalTable(result.proposal, [{}]));
137
+ if (result.reachedThreshold) {
138
+ console.log(
139
+ chalk.cyan("\nThreshold reached on first signature (1-of-N)."),
140
+ );
141
+ }
142
+ }
143
+ } finally {
144
+ close();
145
+ }
146
+ });
147
+
148
+ // ===== sign =====
149
+ cmd
150
+ .command("sign <proposalId>")
151
+ .description("Add a signature to an existing pending proposal")
152
+ .requiredOption(
153
+ "--signer <did>",
154
+ "Signer DID — must be in proposal.memberSet",
155
+ )
156
+ .option("--alg <alg>", "Signer signing alg", "Ed25519")
157
+ .option("--key <hex|path>", "Signer secret key hex or path")
158
+ .option("--db <path>", "SQLite DB path", _defaultDbPath())
159
+ .option("--log <path>", "Governance log path", _defaultLogPath())
160
+ .option("--json", "Output JSON")
161
+ .action(async (proposalId, options) => {
162
+ const { mgr, close } = await _openManager(options.db, options.log);
163
+ try {
164
+ const secretKey = _readKey(options.key);
165
+ const r = mgr.sign({
166
+ proposalId,
167
+ signer: { did: options.signer, alg: options.alg, secretKey },
168
+ });
169
+ if (options.json) {
170
+ console.log(JSON.stringify(r, null, 2));
171
+ } else if (r.accepted) {
172
+ console.log(chalk.green("✓ Signature accepted"));
173
+ if (r.reachedThreshold) {
174
+ console.log(
175
+ chalk.cyan("✓ Threshold reached — proposal ready for finalize"),
176
+ );
177
+ }
178
+ } else {
179
+ console.log(chalk.red(`✗ Signature rejected: ${r.reason}`));
180
+ }
181
+ if (!r.accepted) process.exit(1);
182
+ } finally {
183
+ close();
184
+ }
185
+ });
186
+
187
+ // ===== cancel =====
188
+ cmd
189
+ .command("cancel <proposalId>")
190
+ .description("Cancel a pending or reached proposal")
191
+ .option("--reason <text>", "Free-text reason logged to governance.log")
192
+ .option("--db <path>", "SQLite DB path", _defaultDbPath())
193
+ .option("--log <path>", "Governance log path", _defaultLogPath())
194
+ .option("--json", "Output JSON")
195
+ .action(async (proposalId, options) => {
196
+ const { mgr, close } = await _openManager(options.db, options.log);
197
+ try {
198
+ const r = mgr.cancel(proposalId, options.reason);
199
+ if (options.json) {
200
+ console.log(JSON.stringify(r, null, 2));
201
+ } else if (r.ok) {
202
+ console.log(chalk.gray("✓ Proposal cancelled"));
203
+ } else {
204
+ console.log(chalk.red(`✗ Cancel rejected: ${r.reason}`));
205
+ process.exit(1);
206
+ }
207
+ } finally {
208
+ close();
209
+ }
210
+ });
211
+
212
+ // ===== finalize =====
213
+ cmd
214
+ .command("finalize <proposalId>")
215
+ .description(
216
+ "Mark a reached proposal as consumed (business operation complete)",
217
+ )
218
+ .option("--db <path>", "SQLite DB path", _defaultDbPath())
219
+ .option("--log <path>", "Governance log path", _defaultLogPath())
220
+ .option("--json", "Output JSON")
221
+ .action(async (proposalId, options) => {
222
+ const { mgr, close } = await _openManager(options.db, options.log);
223
+ try {
224
+ const r = mgr.finalize(proposalId);
225
+ if (options.json) {
226
+ console.log(JSON.stringify(r, null, 2));
227
+ } else if (r.ok) {
228
+ console.log(chalk.green("✓ Proposal finalized (state=consumed)"));
229
+ } else {
230
+ console.log(chalk.red(`✗ Finalize rejected: ${r.reason}`));
231
+ process.exit(1);
232
+ }
233
+ } finally {
234
+ close();
235
+ }
236
+ });
237
+
238
+ // ===== list =====
239
+ cmd
240
+ .command("list")
241
+ .description("List proposals")
242
+ .option(
243
+ "--state <s>",
244
+ "Filter by state (pending|reached|consumed|cancelled|expired)",
245
+ )
246
+ .option("--domain <d>", "Filter by domain")
247
+ .option("--limit <n>", "Max rows", (v) => parseInt(v, 10), 50)
248
+ .option("--db <path>", "SQLite DB path", _defaultDbPath())
249
+ .option("--log <path>", "Governance log path", _defaultLogPath())
250
+ .option("--json", "Output JSON")
251
+ .action(async (options) => {
252
+ const { store, close } = await _openManager(options.db, options.log);
253
+ try {
254
+ const proposals = store.listProposals({
255
+ state: options.state,
256
+ domain: options.domain,
257
+ limit: options.limit,
258
+ });
259
+ if (options.json) {
260
+ console.log(
261
+ JSON.stringify(
262
+ proposals.map((p) => ({
263
+ id: p.id,
264
+ domain: p.domain,
265
+ state: p.state,
266
+ m: p.thresholdM,
267
+ n: p.memberSet.length,
268
+ initiatorDid: p.initiatorDid,
269
+ createdAtMs: p.createdAtMs,
270
+ expiresAtMs: p.expiresAtMs,
271
+ })),
272
+ null,
273
+ 2,
274
+ ),
275
+ );
276
+ } else {
277
+ if (proposals.length === 0) {
278
+ console.log(chalk.gray("(no proposals)"));
279
+ return;
280
+ }
281
+ for (const p of proposals) {
282
+ const sigCount = store.getSignatures(p.id).length;
283
+ console.log(
284
+ `${chalk.gray(p.id.padEnd(28))} ${_colorState(p.state.padEnd(10))} ${chalk.bold(
285
+ p.domain.padEnd(24),
286
+ )} ${sigCount}/${p.thresholdM} ${new Date(p.createdAtMs).toISOString()}`,
287
+ );
288
+ }
289
+ }
290
+ } finally {
291
+ close();
292
+ }
293
+ });
294
+
295
+ // ===== show =====
296
+ cmd
297
+ .command("show <proposalId>")
298
+ .description("Show detailed proposal with all signatures")
299
+ .option("--db <path>", "SQLite DB path", _defaultDbPath())
300
+ .option("--log <path>", "Governance log path", _defaultLogPath())
301
+ .option("--json", "Output JSON")
302
+ .action(async (proposalId, options) => {
303
+ const { mgr, close } = await _openManager(options.db, options.log);
304
+ try {
305
+ const got = mgr.get(proposalId);
306
+ if (!got) {
307
+ console.error(chalk.red(`No proposal: ${proposalId}`));
308
+ process.exit(2);
309
+ }
310
+ if (options.json) {
311
+ console.log(
312
+ JSON.stringify(
313
+ {
314
+ proposal: {
315
+ ...got.proposal,
316
+ payloadHash: got.proposal.payloadHash.toString("hex"),
317
+ payload: JSON.parse(got.proposal.payloadJcs),
318
+ },
319
+ signatures: got.signatures.map((s) => ({
320
+ signerDid: s.signerDid,
321
+ alg: s.alg,
322
+ signedAtMs: s.signedAtMs,
323
+ sigBytes: s.sig.length,
324
+ })),
325
+ },
326
+ null,
327
+ 2,
328
+ ),
329
+ );
330
+ } else {
331
+ console.log(_formatProposalTable(got.proposal, got.signatures));
332
+ console.log(`\n${chalk.bold("Payload:")}`);
333
+ console.log(got.proposal.payloadJcs);
334
+ console.log(`\n${chalk.bold("Signatures:")}`);
335
+ for (const s of got.signatures) {
336
+ console.log(
337
+ ` ${chalk.green("✓")} ${s.signerDid.padEnd(36)} ${s.alg.padEnd(20)} ${new Date(
338
+ s.signedAtMs,
339
+ ).toISOString()}`,
340
+ );
341
+ }
342
+ }
343
+ } finally {
344
+ close();
345
+ }
346
+ });
347
+
348
+ // ===== sweep =====
349
+ cmd
350
+ .command("sweep")
351
+ .description("Expire all pending proposals past their deadline")
352
+ .option("--db <path>", "SQLite DB path", _defaultDbPath())
353
+ .option("--log <path>", "Governance log path", _defaultLogPath())
354
+ .option("--json", "Output JSON")
355
+ .action(async (options) => {
356
+ const { mgr, close } = await _openManager(options.db, options.log);
357
+ try {
358
+ const count = mgr.expireStale();
359
+ if (options.json) {
360
+ console.log(JSON.stringify({ expired: count }, null, 2));
361
+ } else {
362
+ console.log(
363
+ count === 0
364
+ ? chalk.gray("Nothing to expire")
365
+ : chalk.yellow(`✓ Expired ${count} stale proposal(s)`),
366
+ );
367
+ }
368
+ } finally {
369
+ close();
370
+ }
371
+ });
372
+
373
+ // ===== policy =====
374
+ const policy = cmd
375
+ .command("policy")
376
+ .description("Manage per-domain multisig policies");
377
+
378
+ policy
379
+ .command("show <domain>")
380
+ .description("Show the policy for a domain")
381
+ .option("--db <path>", "SQLite DB path", _defaultDbPath())
382
+ .option("--log <path>", "Governance log path", _defaultLogPath())
383
+ .option("--json", "Output JSON")
384
+ .action(async (domain, options) => {
385
+ const { store, close } = await _openManager(options.db, options.log);
386
+ try {
387
+ const p = store.getPolicy(domain);
388
+ if (!p) {
389
+ console.error(chalk.red(`No policy: ${domain}`));
390
+ process.exit(2);
391
+ }
392
+ if (options.json) {
393
+ console.log(JSON.stringify(p, null, 2));
394
+ } else {
395
+ console.log(`${chalk.bold("Domain:")} ${p.domain}`);
396
+ console.log(`${chalk.bold("M / N:")} ${p.m} / ${p.members.length}`);
397
+ console.log(
398
+ `${chalk.bold("requirePqc:")} ${p.requirePqc === true ? "yes" : "no"}`,
399
+ );
400
+ console.log(`${chalk.bold("Members:")}`);
401
+ for (const m of p.members) {
402
+ console.log(` ${m.did.padEnd(36)} ${m.alg}`);
403
+ }
404
+ }
405
+ } finally {
406
+ close();
407
+ }
408
+ });
409
+
410
+ policy
411
+ .command("set <domain>")
412
+ .description("Set or update the policy for a domain")
413
+ .requiredOption("--m <M>", "Threshold M", (v) => parseInt(v, 10))
414
+ .requiredOption(
415
+ "--members <json|file>",
416
+ "JSON array of {did, alg, pubkeyJwk} or path to file",
417
+ )
418
+ .option("--require-pqc", "Require at least one SLH-DSA signature", false)
419
+ .option("--expiry-ms <ms>", "Default proposal expiry in ms", (v) =>
420
+ parseInt(v, 10),
421
+ )
422
+ .option("--db <path>", "SQLite DB path", _defaultDbPath())
423
+ .option("--log <path>", "Governance log path", _defaultLogPath())
424
+ .option("--json", "Output JSON")
425
+ .action(async (domain, options) => {
426
+ const { store, close } = await _openManager(options.db, options.log);
427
+ try {
428
+ const members = _readJsonArg(options.members);
429
+ if (!Array.isArray(members) || members.length === 0) {
430
+ console.error(chalk.red("--members must be a non-empty JSON array"));
431
+ process.exit(2);
432
+ }
433
+ const policy = {
434
+ domain,
435
+ m: options.m,
436
+ n: members.length,
437
+ members,
438
+ requirePqc: options.requirePqc === true,
439
+ };
440
+ if (options.expiryMs) policy.defaultExpiryMs = options.expiryMs;
441
+ const normalized = normalizePolicy(policy); // validate + fill defaults
442
+ store.setPolicy(domain, JSON.stringify(normalized), Date.now());
443
+ if (options.json) {
444
+ console.log(
445
+ JSON.stringify(
446
+ { ok: true, domain, m: normalized.m, n: normalized.n },
447
+ null,
448
+ 2,
449
+ ),
450
+ );
451
+ } else {
452
+ console.log(
453
+ chalk.green(
454
+ `✓ Policy set: ${domain} (${normalized.m}-of-${normalized.n})`,
455
+ ),
456
+ );
457
+ }
458
+ } finally {
459
+ close();
460
+ }
461
+ });
462
+ }
463
+
464
+ // 暴露给单测 / 集成测试
465
+ export const _multisigInternals = {
466
+ _defaultDbPath,
467
+ _defaultLogPath,
468
+ _openManager,
469
+ _readKey,
470
+ _readJsonArg,
471
+ };
@@ -15,6 +15,7 @@ import {
15
15
  markMessageRead,
16
16
  getMessageCount,
17
17
  pairDevice,
18
+ pairDeviceFromQr,
18
19
  confirmPairing,
19
20
  getPairedDevices,
20
21
  unpairDevice,
@@ -234,27 +235,112 @@ export function registerP2pCommand(program) {
234
235
  }
235
236
  });
236
237
 
238
+ // p2p pair-from-qr (v1.1 W3.5 issue #19)
239
+ p2p
240
+ .command("pair-from-qr")
241
+ .description("Pair a device from scanned QR JSON payload")
242
+ .argument(
243
+ "<json>",
244
+ "QR payload JSON string (from mobile DesktopPairingScreen)",
245
+ )
246
+ .option("--json", "Output result as JSON")
247
+ .action(async (jsonString, options) => {
248
+ const respondJson = options.json;
249
+ try {
250
+ let payload;
251
+ try {
252
+ payload = JSON.parse(jsonString);
253
+ } catch (e) {
254
+ if (respondJson) {
255
+ console.log(
256
+ JSON.stringify({
257
+ success: false,
258
+ error: `JSON parse: ${e.message}`,
259
+ }),
260
+ );
261
+ } else {
262
+ logger.error(`Failed to parse QR JSON: ${e.message}`);
263
+ }
264
+ process.exit(1);
265
+ }
266
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
267
+ if (!ctx.db) {
268
+ if (respondJson) {
269
+ console.log(
270
+ JSON.stringify({ success: false, error: "db unavailable" }),
271
+ );
272
+ } else {
273
+ logger.error("Database not available");
274
+ }
275
+ process.exit(1);
276
+ }
277
+ const db = ctx.db.getDatabase();
278
+ const result = pairDeviceFromQr(db, payload);
279
+
280
+ if (respondJson) {
281
+ console.log(JSON.stringify(result));
282
+ } else if (result.success) {
283
+ logger.success("Device paired from QR");
284
+ logger.log(
285
+ ` ${chalk.bold("Device ID:")} ${chalk.cyan(result.deviceId)}`,
286
+ );
287
+ logger.log(` ${chalk.bold("Device Name:")} ${result.deviceName}`);
288
+ logger.log(
289
+ ` ${chalk.bold("DID:")} ${chalk.gray(result.did)}`,
290
+ );
291
+ } else {
292
+ logger.error(`Pair failed: ${result.error}`);
293
+ }
294
+
295
+ await shutdown();
296
+ if (!result.success) process.exit(1);
297
+ } catch (err) {
298
+ if (respondJson) {
299
+ console.log(JSON.stringify({ success: false, error: err.message }));
300
+ } else {
301
+ logger.error(`Failed: ${err.message}`);
302
+ }
303
+ process.exit(1);
304
+ }
305
+ });
306
+
237
307
  // p2p devices
238
308
  p2p
239
309
  .command("devices")
240
310
  .description("List paired devices")
241
311
  .option("--json", "Output as JSON")
312
+ .option("--type <type>", "Filter by device type (desktop|mobile|tablet)")
242
313
  .action(async (options) => {
243
314
  try {
315
+ // v1.1 W3.4a: validate --type before DB query (避免 SQL 注入 + 友好报错)
316
+ const validTypes = ["desktop", "mobile", "tablet"];
317
+ if (options.type && !validTypes.includes(options.type)) {
318
+ logger.error(
319
+ `Invalid --type "${options.type}". Must be: ${validTypes.join(", ")}`,
320
+ );
321
+ process.exit(1);
322
+ }
244
323
  const ctx = await bootstrap({ verbose: program.opts().verbose });
245
324
  if (!ctx.db) {
246
325
  logger.error("Database not available");
247
326
  process.exit(1);
248
327
  }
249
328
  const db = ctx.db.getDatabase();
250
- const devices = getPairedDevices(db);
329
+ const devices = getPairedDevices(db, options.type || null);
251
330
 
252
331
  if (options.json) {
253
332
  console.log(JSON.stringify(devices, null, 2));
254
333
  } else if (devices.length === 0) {
255
- logger.info("No paired devices");
334
+ logger.info(
335
+ options.type
336
+ ? `No paired devices of type "${options.type}"`
337
+ : "No paired devices",
338
+ );
256
339
  } else {
257
- logger.log(chalk.bold(`Paired Devices (${devices.length}):\n`));
340
+ const header = options.type
341
+ ? `Paired ${options.type} Devices (${devices.length})`
342
+ : `Paired Devices (${devices.length})`;
343
+ logger.log(chalk.bold(`${header}:\n`));
258
344
  for (const d of devices) {
259
345
  const status =
260
346
  d.status === "active"
@@ -278,17 +364,32 @@ export function registerP2pCommand(program) {
278
364
  .command("unpair")
279
365
  .description("Unpair a device")
280
366
  .argument("<device-id>", "Device ID to unpair")
281
- .action(async (deviceId) => {
367
+ .option("--json", "Output result as JSON")
368
+ .action(async (deviceId, options) => {
282
369
  try {
283
370
  const ctx = await bootstrap({ verbose: program.opts().verbose });
284
371
  if (!ctx.db) {
285
- logger.error("Database not available");
372
+ if (options.json) {
373
+ console.log(JSON.stringify({ ok: false, error: "db unavailable" }));
374
+ } else {
375
+ logger.error("Database not available");
376
+ }
286
377
  process.exit(1);
287
378
  }
288
379
  const db = ctx.db.getDatabase();
289
380
  const ok = unpairDevice(db, deviceId);
290
381
 
291
- if (ok) {
382
+ // v1.1 W3.4a: --json shape `{ok, deviceId, error?}` 让 web-panel
383
+ // MobileBridge.vue 解析可靠。device 不存在不 exit(1),由 ok:false 传达。
384
+ if (options.json) {
385
+ console.log(
386
+ JSON.stringify(
387
+ ok
388
+ ? { ok: true, deviceId }
389
+ : { ok: false, deviceId, error: "not_found" },
390
+ ),
391
+ );
392
+ } else if (ok) {
292
393
  logger.success("Device unpaired");
293
394
  } else {
294
395
  logger.error(`Device not found: ${deviceId}`);
@@ -296,7 +397,13 @@ export function registerP2pCommand(program) {
296
397
 
297
398
  await shutdown();
298
399
  } catch (err) {
299
- logger.error(`Failed: ${err.message}`);
400
+ if (options.json) {
401
+ console.log(
402
+ JSON.stringify({ ok: false, deviceId, error: err.message }),
403
+ );
404
+ } else {
405
+ logger.error(`Failed: ${err.message}`);
406
+ }
300
407
  process.exit(1);
301
408
  }
302
409
  });
package/src/index.js CHANGED
@@ -195,6 +195,7 @@ 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";
198
199
  // Phase 27: Multimodal Collaboration
199
200
  import { registerMultimodalCommand } from "./commands/multimodal.js";
200
201
  // Phase 22: Performance auto-tuning
@@ -579,6 +580,7 @@ export function createProgram(opts = {}) {
579
580
  registerRuntimeCommand(program);
580
581
  registerIpfsCommand(program);
581
582
  registerMtcCommand(program);
583
+ registerMultisigCommand(program);
582
584
  registerMultimodalCommand(program);
583
585
  registerPerfCommand(program);
584
586
  registerAgentNetworkCommand(program);