chainlesschain 0.47.8 → 0.49.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/chainlesschain.js +0 -0
- package/package.json +10 -8
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/{AppLayout-6SPt_8Y_.js → AppLayout-Rvi759IS.js} +1 -1
- package/src/assets/web-panel/assets/Dashboard-BS-tzGNj.css +1 -0
- package/src/assets/web-panel/assets/{Dashboard-Br7kCwKJ.js → Dashboard-DBhFxXYQ.js} +2 -2
- package/src/assets/web-panel/assets/{index-tN-8TosE.js → index-uL0cZ8N_.js} +2 -2
- package/src/assets/web-panel/index.html +2 -2
- package/src/commands/activitypub.js +533 -0
- package/src/commands/codegen.js +303 -0
- package/src/commands/collab.js +482 -0
- package/src/commands/compliance.js +597 -6
- package/src/commands/crosschain.js +382 -0
- package/src/commands/dbevo.js +388 -0
- package/src/commands/dev.js +411 -0
- package/src/commands/federation.js +427 -0
- package/src/commands/fusion.js +332 -0
- package/src/commands/governance.js +505 -0
- package/src/commands/hardening.js +110 -0
- package/src/commands/incentive.js +373 -0
- package/src/commands/inference.js +304 -0
- package/src/commands/infra.js +361 -0
- package/src/commands/kg.js +371 -0
- package/src/commands/marketplace.js +326 -0
- package/src/commands/matrix.js +283 -0
- package/src/commands/mcp.js +441 -18
- package/src/commands/nlprog.js +329 -0
- package/src/commands/nostr.js +196 -7
- package/src/commands/ops.js +408 -0
- package/src/commands/perception.js +385 -0
- package/src/commands/pqc.js +34 -0
- package/src/commands/privacy.js +345 -0
- package/src/commands/quantization.js +280 -0
- package/src/commands/recommend.js +336 -0
- package/src/commands/reputation.js +349 -0
- package/src/commands/runtime.js +500 -0
- package/src/commands/sla.js +352 -0
- package/src/commands/social.js +265 -0
- package/src/commands/stress.js +252 -0
- package/src/commands/tech.js +268 -0
- package/src/commands/tenant.js +576 -0
- package/src/commands/trust.js +366 -0
- package/src/harness/mcp-client.js +330 -54
- package/src/index.js +114 -0
- package/src/lib/activitypub-bridge.js +623 -0
- package/src/lib/aiops.js +523 -0
- package/src/lib/autonomous-developer.js +524 -0
- package/src/lib/code-agent.js +442 -0
- package/src/lib/collaboration-governance.js +556 -0
- package/src/lib/community-governance.js +649 -0
- package/src/lib/compliance-framework-reporter.js +600 -0
- package/src/lib/content-recommendation.js +600 -0
- package/src/lib/cross-chain.js +669 -0
- package/src/lib/dbevo.js +669 -0
- package/src/lib/decentral-infra.js +445 -0
- package/src/lib/federation-hardening.js +587 -0
- package/src/lib/hardening-manager.js +409 -0
- package/src/lib/inference-network.js +407 -0
- package/src/lib/knowledge-graph.js +530 -0
- package/src/lib/matrix-bridge.js +252 -0
- package/src/lib/mcp-client.js +3 -0
- package/src/lib/mcp-registry.js +347 -0
- package/src/lib/mcp-scaffold.js +385 -0
- package/src/lib/multimodal.js +698 -0
- package/src/lib/nl-programming.js +595 -0
- package/src/lib/nostr-bridge.js +214 -38
- package/src/lib/perception.js +500 -0
- package/src/lib/pqc-manager.js +141 -9
- package/src/lib/privacy-computing.js +575 -0
- package/src/lib/protocol-fusion.js +535 -0
- package/src/lib/quantization.js +362 -0
- package/src/lib/reputation-optimizer.js +509 -0
- package/src/lib/skill-marketplace.js +397 -0
- package/src/lib/sla-manager.js +484 -0
- package/src/lib/social-graph.js +408 -0
- package/src/lib/stix-parser.js +167 -0
- package/src/lib/stress-tester.js +383 -0
- package/src/lib/tech-learning-engine.js +651 -0
- package/src/lib/tenant-saas.js +831 -0
- package/src/lib/threat-intel.js +268 -0
- package/src/lib/token-incentive.js +513 -0
- package/src/lib/topic-classifier.js +400 -0
- package/src/lib/trust-security.js +473 -0
- package/src/lib/ueba.js +403 -0
- package/src/lib/universal-runtime.js +771 -0
- package/src/assets/web-panel/assets/Dashboard-CKeMmCoT.css +0 -1
|
@@ -0,0 +1,505 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Community Governance commands (Phase 54)
|
|
3
|
+
* chainlesschain governance types|statuses|impact-levels|create|list|show|
|
|
4
|
+
* activate|close|expire|vote|votes|tally|
|
|
5
|
+
* analyze|predict|stats
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import chalk from "chalk";
|
|
9
|
+
import { logger } from "../lib/logger.js";
|
|
10
|
+
import { bootstrap, shutdown } from "../runtime/bootstrap.js";
|
|
11
|
+
import {
|
|
12
|
+
ensureGovernanceTables,
|
|
13
|
+
listProposalTypes,
|
|
14
|
+
listProposalStatuses,
|
|
15
|
+
listImpactLevels,
|
|
16
|
+
createProposal,
|
|
17
|
+
getProposal,
|
|
18
|
+
listProposals,
|
|
19
|
+
activateProposal,
|
|
20
|
+
closeProposal,
|
|
21
|
+
expireProposal,
|
|
22
|
+
castVote,
|
|
23
|
+
listVotes,
|
|
24
|
+
tallyVotes,
|
|
25
|
+
analyzeImpact,
|
|
26
|
+
predictVote,
|
|
27
|
+
getGovernanceStats,
|
|
28
|
+
} from "../lib/community-governance.js";
|
|
29
|
+
|
|
30
|
+
function _dbFromCtx(ctx) {
|
|
31
|
+
if (!ctx.db) {
|
|
32
|
+
logger.error("Database not available");
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
const db = ctx.db.getDatabase();
|
|
36
|
+
ensureGovernanceTables(db);
|
|
37
|
+
return db;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function _printProposal(p) {
|
|
41
|
+
logger.log(` ${chalk.bold("ID:")} ${chalk.cyan(p.id)}`);
|
|
42
|
+
logger.log(` ${chalk.bold("Title:")} ${p.title}`);
|
|
43
|
+
logger.log(` ${chalk.bold("Type:")} ${chalk.yellow(p.type)}`);
|
|
44
|
+
logger.log(` ${chalk.bold("Status:")} ${_statusColor(p.status)}`);
|
|
45
|
+
if (p.proposerDid) {
|
|
46
|
+
logger.log(` ${chalk.bold("Proposer:")} ${p.proposerDid}`);
|
|
47
|
+
}
|
|
48
|
+
if (p.description) {
|
|
49
|
+
logger.log(` ${chalk.bold("Description:")} ${p.description}`);
|
|
50
|
+
}
|
|
51
|
+
if (p.impactLevel) {
|
|
52
|
+
logger.log(` ${chalk.bold("Impact:")} ${p.impactLevel}`);
|
|
53
|
+
}
|
|
54
|
+
logger.log(
|
|
55
|
+
` ${chalk.bold("Votes:")} ${chalk.green("yes:" + p.voteYes)} ${chalk.red("no:" + p.voteNo)} ${chalk.gray("abstain:" + p.voteAbstain)}`,
|
|
56
|
+
);
|
|
57
|
+
if (p.votingStartsAt) {
|
|
58
|
+
logger.log(
|
|
59
|
+
` ${chalk.bold("Voting:")} ${new Date(p.votingStartsAt).toISOString()} → ${new Date(p.votingEndsAt).toISOString()}`,
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function _statusColor(status) {
|
|
65
|
+
switch (status) {
|
|
66
|
+
case "active":
|
|
67
|
+
return chalk.blue(status);
|
|
68
|
+
case "passed":
|
|
69
|
+
return chalk.green(status);
|
|
70
|
+
case "rejected":
|
|
71
|
+
return chalk.red(status);
|
|
72
|
+
case "expired":
|
|
73
|
+
return chalk.gray(status);
|
|
74
|
+
default:
|
|
75
|
+
return chalk.yellow(status);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function registerGovernanceCommand(program) {
|
|
80
|
+
const gov = program
|
|
81
|
+
.command("governance")
|
|
82
|
+
.description(
|
|
83
|
+
"Community governance — proposals, voting, impact analysis, prediction",
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
gov
|
|
87
|
+
.command("types")
|
|
88
|
+
.description("List proposal types")
|
|
89
|
+
.option("--json", "Output as JSON")
|
|
90
|
+
.action((options) => {
|
|
91
|
+
const types = listProposalTypes();
|
|
92
|
+
if (options.json) {
|
|
93
|
+
console.log(JSON.stringify(types, null, 2));
|
|
94
|
+
} else {
|
|
95
|
+
for (const t of types) {
|
|
96
|
+
logger.log(
|
|
97
|
+
` ${chalk.cyan(t.id.padEnd(20))} ${t.name} — ${t.description}`,
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
gov
|
|
104
|
+
.command("statuses")
|
|
105
|
+
.description("List proposal statuses")
|
|
106
|
+
.option("--json", "Output as JSON")
|
|
107
|
+
.action((options) => {
|
|
108
|
+
const statuses = listProposalStatuses();
|
|
109
|
+
if (options.json) {
|
|
110
|
+
console.log(JSON.stringify(statuses, null, 2));
|
|
111
|
+
} else {
|
|
112
|
+
for (const s of statuses) {
|
|
113
|
+
logger.log(` ${_statusColor(s)}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
gov
|
|
119
|
+
.command("impact-levels")
|
|
120
|
+
.description("List impact levels")
|
|
121
|
+
.option("--json", "Output as JSON")
|
|
122
|
+
.action((options) => {
|
|
123
|
+
const levels = listImpactLevels();
|
|
124
|
+
if (options.json) {
|
|
125
|
+
console.log(JSON.stringify(levels, null, 2));
|
|
126
|
+
} else {
|
|
127
|
+
for (const l of levels) {
|
|
128
|
+
logger.log(
|
|
129
|
+
` ${chalk.cyan(l.id.padEnd(10))} ${l.name} — ${l.description}`,
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
gov
|
|
136
|
+
.command("create <title>")
|
|
137
|
+
.description("Create a governance proposal (starts as draft)")
|
|
138
|
+
.option(
|
|
139
|
+
"-t, --type <type>",
|
|
140
|
+
"Proposal type (parameter_change|feature_request|policy_update|budget_allocation)",
|
|
141
|
+
"feature_request",
|
|
142
|
+
)
|
|
143
|
+
.option("-d, --description <text>", "Proposal description")
|
|
144
|
+
.option("-p, --proposer <did>", "Proposer DID")
|
|
145
|
+
.option("--json", "Output as JSON")
|
|
146
|
+
.action(async (title, options) => {
|
|
147
|
+
try {
|
|
148
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
149
|
+
const db = _dbFromCtx(ctx);
|
|
150
|
+
const p = createProposal(db, {
|
|
151
|
+
title,
|
|
152
|
+
type: options.type,
|
|
153
|
+
description: options.description,
|
|
154
|
+
proposerDid: options.proposer,
|
|
155
|
+
});
|
|
156
|
+
if (options.json) {
|
|
157
|
+
console.log(JSON.stringify(p, null, 2));
|
|
158
|
+
} else {
|
|
159
|
+
logger.success(`Proposal created (draft)`);
|
|
160
|
+
_printProposal(p);
|
|
161
|
+
}
|
|
162
|
+
await shutdown();
|
|
163
|
+
} catch (err) {
|
|
164
|
+
logger.error(`Failed: ${err.message}`);
|
|
165
|
+
process.exit(1);
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
gov
|
|
170
|
+
.command("list")
|
|
171
|
+
.description("List proposals")
|
|
172
|
+
.option("-s, --status <status>", "Filter by status")
|
|
173
|
+
.option("-t, --type <type>", "Filter by type")
|
|
174
|
+
.option("--limit <n>", "Maximum entries", parseInt, 50)
|
|
175
|
+
.option("--json", "Output as JSON")
|
|
176
|
+
.action(async (options) => {
|
|
177
|
+
try {
|
|
178
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
179
|
+
_dbFromCtx(ctx);
|
|
180
|
+
const rows = listProposals({
|
|
181
|
+
status: options.status,
|
|
182
|
+
type: options.type,
|
|
183
|
+
limit: options.limit,
|
|
184
|
+
});
|
|
185
|
+
if (options.json) {
|
|
186
|
+
console.log(JSON.stringify(rows, null, 2));
|
|
187
|
+
} else if (rows.length === 0) {
|
|
188
|
+
logger.info("No proposals.");
|
|
189
|
+
} else {
|
|
190
|
+
for (const p of rows) {
|
|
191
|
+
logger.log(
|
|
192
|
+
` ${chalk.cyan(p.id.slice(0, 8))} ${_statusColor(p.status.padEnd(9))} ${chalk.yellow(p.type.padEnd(20))} ${p.title}`,
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
await shutdown();
|
|
197
|
+
} catch (err) {
|
|
198
|
+
logger.error(`Failed: ${err.message}`);
|
|
199
|
+
process.exit(1);
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
gov
|
|
204
|
+
.command("show <proposal-id>")
|
|
205
|
+
.description("Show proposal details")
|
|
206
|
+
.option("--json", "Output as JSON")
|
|
207
|
+
.action(async (proposalId, options) => {
|
|
208
|
+
try {
|
|
209
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
210
|
+
_dbFromCtx(ctx);
|
|
211
|
+
const p = getProposal(proposalId);
|
|
212
|
+
if (!p) {
|
|
213
|
+
logger.error(`Proposal not found: ${proposalId}`);
|
|
214
|
+
process.exit(1);
|
|
215
|
+
}
|
|
216
|
+
if (options.json) {
|
|
217
|
+
console.log(JSON.stringify(p, null, 2));
|
|
218
|
+
} else {
|
|
219
|
+
_printProposal(p);
|
|
220
|
+
}
|
|
221
|
+
await shutdown();
|
|
222
|
+
} catch (err) {
|
|
223
|
+
logger.error(`Failed: ${err.message}`);
|
|
224
|
+
process.exit(1);
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
gov
|
|
229
|
+
.command("activate <proposal-id>")
|
|
230
|
+
.description("Activate a draft proposal for voting")
|
|
231
|
+
.option(
|
|
232
|
+
"-d, --duration-ms <ms>",
|
|
233
|
+
"Voting duration in ms (default 7d)",
|
|
234
|
+
parseInt,
|
|
235
|
+
)
|
|
236
|
+
.option("--json", "Output as JSON")
|
|
237
|
+
.action(async (proposalId, options) => {
|
|
238
|
+
try {
|
|
239
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
240
|
+
const db = _dbFromCtx(ctx);
|
|
241
|
+
const p = activateProposal(db, proposalId, {
|
|
242
|
+
durationMs: options.durationMs,
|
|
243
|
+
});
|
|
244
|
+
if (options.json) {
|
|
245
|
+
console.log(JSON.stringify(p, null, 2));
|
|
246
|
+
} else {
|
|
247
|
+
logger.success(`Proposal activated for voting`);
|
|
248
|
+
_printProposal(p);
|
|
249
|
+
}
|
|
250
|
+
await shutdown();
|
|
251
|
+
} catch (err) {
|
|
252
|
+
logger.error(`Failed: ${err.message}`);
|
|
253
|
+
process.exit(1);
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
gov
|
|
258
|
+
.command("close <proposal-id>")
|
|
259
|
+
.description("Close voting — auto-resolves to passed/rejected")
|
|
260
|
+
.option("-q, --quorum <n>", "Quorum threshold (0-1)", parseFloat)
|
|
261
|
+
.option("-t, --threshold <n>", "Pass threshold (0-1)", parseFloat)
|
|
262
|
+
.option(
|
|
263
|
+
"-n, --total-voters <n>",
|
|
264
|
+
"Total eligible voters (for quorum)",
|
|
265
|
+
parseInt,
|
|
266
|
+
)
|
|
267
|
+
.option("--json", "Output as JSON")
|
|
268
|
+
.action(async (proposalId, options) => {
|
|
269
|
+
try {
|
|
270
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
271
|
+
const db = _dbFromCtx(ctx);
|
|
272
|
+
const { proposal, tally } = closeProposal(db, proposalId, {
|
|
273
|
+
quorum: options.quorum,
|
|
274
|
+
threshold: options.threshold,
|
|
275
|
+
totalVoters: options.totalVoters,
|
|
276
|
+
});
|
|
277
|
+
if (options.json) {
|
|
278
|
+
console.log(JSON.stringify({ proposal, tally }, null, 2));
|
|
279
|
+
} else {
|
|
280
|
+
const color = proposal.status === "passed" ? chalk.green : chalk.red;
|
|
281
|
+
logger.log(color(`Proposal ${proposal.status.toUpperCase()}`));
|
|
282
|
+
logger.log(
|
|
283
|
+
` yes: ${tally.yesWeight} no: ${tally.noWeight} abstain: ${tally.abstainWeight} ratio: ${tally.yesRatio}`,
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
await shutdown();
|
|
287
|
+
} catch (err) {
|
|
288
|
+
logger.error(`Failed: ${err.message}`);
|
|
289
|
+
process.exit(1);
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
gov
|
|
294
|
+
.command("expire <proposal-id>")
|
|
295
|
+
.description("Mark a draft/active proposal as expired")
|
|
296
|
+
.action(async (proposalId) => {
|
|
297
|
+
try {
|
|
298
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
299
|
+
const db = _dbFromCtx(ctx);
|
|
300
|
+
expireProposal(db, proposalId);
|
|
301
|
+
logger.success(`Proposal expired: ${proposalId}`);
|
|
302
|
+
await shutdown();
|
|
303
|
+
} catch (err) {
|
|
304
|
+
logger.error(`Failed: ${err.message}`);
|
|
305
|
+
process.exit(1);
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
gov
|
|
310
|
+
.command("vote <proposal-id> <voter-did> <yes|no|abstain>")
|
|
311
|
+
.description("Cast a vote (replaces any prior vote by same voter)")
|
|
312
|
+
.option("-r, --reason <text>", "Reason for vote")
|
|
313
|
+
.option("-w, --weight <n>", "Vote weight", parseFloat, 1.0)
|
|
314
|
+
.option("--json", "Output as JSON")
|
|
315
|
+
.action(async (proposalId, voterDid, voteValue, options) => {
|
|
316
|
+
try {
|
|
317
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
318
|
+
const db = _dbFromCtx(ctx);
|
|
319
|
+
const v = castVote(db, proposalId, voterDid, voteValue, {
|
|
320
|
+
reason: options.reason,
|
|
321
|
+
weight: options.weight,
|
|
322
|
+
});
|
|
323
|
+
if (options.json) {
|
|
324
|
+
console.log(JSON.stringify(v, null, 2));
|
|
325
|
+
} else {
|
|
326
|
+
logger.success(`Vote cast: ${voteValue} by ${voterDid}`);
|
|
327
|
+
}
|
|
328
|
+
await shutdown();
|
|
329
|
+
} catch (err) {
|
|
330
|
+
logger.error(`Failed: ${err.message}`);
|
|
331
|
+
process.exit(1);
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
gov
|
|
336
|
+
.command("votes <proposal-id>")
|
|
337
|
+
.description("List votes for a proposal")
|
|
338
|
+
.option("--limit <n>", "Maximum entries", parseInt, 50)
|
|
339
|
+
.option("--json", "Output as JSON")
|
|
340
|
+
.action(async (proposalId, options) => {
|
|
341
|
+
try {
|
|
342
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
343
|
+
_dbFromCtx(ctx);
|
|
344
|
+
const rows = listVotes(proposalId, { limit: options.limit });
|
|
345
|
+
if (options.json) {
|
|
346
|
+
console.log(JSON.stringify(rows, null, 2));
|
|
347
|
+
} else if (rows.length === 0) {
|
|
348
|
+
logger.info("No votes.");
|
|
349
|
+
} else {
|
|
350
|
+
for (const v of rows) {
|
|
351
|
+
const vColor =
|
|
352
|
+
v.vote === "yes"
|
|
353
|
+
? chalk.green
|
|
354
|
+
: v.vote === "no"
|
|
355
|
+
? chalk.red
|
|
356
|
+
: chalk.gray;
|
|
357
|
+
logger.log(
|
|
358
|
+
` ${chalk.cyan(v.id.slice(0, 8))} ${vColor(v.vote.padEnd(8))} w=${v.weight} ${v.voterDid}`,
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
await shutdown();
|
|
363
|
+
} catch (err) {
|
|
364
|
+
logger.error(`Failed: ${err.message}`);
|
|
365
|
+
process.exit(1);
|
|
366
|
+
}
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
gov
|
|
370
|
+
.command("tally <proposal-id>")
|
|
371
|
+
.description("Show vote tally with quorum/threshold check")
|
|
372
|
+
.option("-q, --quorum <n>", "Quorum threshold (0-1)", parseFloat)
|
|
373
|
+
.option("-t, --threshold <n>", "Pass threshold (0-1)", parseFloat)
|
|
374
|
+
.option("-n, --total-voters <n>", "Total eligible voters", parseInt)
|
|
375
|
+
.option("--json", "Output as JSON")
|
|
376
|
+
.action(async (proposalId, options) => {
|
|
377
|
+
try {
|
|
378
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
379
|
+
_dbFromCtx(ctx);
|
|
380
|
+
const tally = tallyVotes(proposalId, {
|
|
381
|
+
quorum: options.quorum,
|
|
382
|
+
threshold: options.threshold,
|
|
383
|
+
totalVoters: options.totalVoters,
|
|
384
|
+
});
|
|
385
|
+
if (options.json) {
|
|
386
|
+
console.log(JSON.stringify(tally, null, 2));
|
|
387
|
+
} else {
|
|
388
|
+
const color = tally.passed ? chalk.green : chalk.red;
|
|
389
|
+
logger.log(
|
|
390
|
+
` ${chalk.bold("Result:")} ${color(tally.passed ? "PASSED" : "NOT PASSED")}`,
|
|
391
|
+
);
|
|
392
|
+
logger.log(
|
|
393
|
+
` ${chalk.bold("Votes:")} ${tally.voteCount} (yes=${tally.yesWeight}, no=${tally.noWeight}, abstain=${tally.abstainWeight})`,
|
|
394
|
+
);
|
|
395
|
+
logger.log(
|
|
396
|
+
` ${chalk.bold("Yes ratio:")} ${tally.yesRatio} (threshold=${tally.threshold})`,
|
|
397
|
+
);
|
|
398
|
+
logger.log(
|
|
399
|
+
` ${chalk.bold("Quorum:")} ${tally.quorumMet ? "met" : "NOT met"} (${tally.quorum})`,
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
await shutdown();
|
|
403
|
+
} catch (err) {
|
|
404
|
+
logger.error(`Failed: ${err.message}`);
|
|
405
|
+
process.exit(1);
|
|
406
|
+
}
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
gov
|
|
410
|
+
.command("analyze <proposal-id>")
|
|
411
|
+
.description("Run heuristic impact analysis on a proposal")
|
|
412
|
+
.option("--json", "Output as JSON")
|
|
413
|
+
.action(async (proposalId, options) => {
|
|
414
|
+
try {
|
|
415
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
416
|
+
_dbFromCtx(ctx);
|
|
417
|
+
const analysis = analyzeImpact(proposalId);
|
|
418
|
+
if (options.json) {
|
|
419
|
+
console.log(JSON.stringify(analysis, null, 2));
|
|
420
|
+
} else {
|
|
421
|
+
logger.log(` ${chalk.bold("Impact:")} ${analysis.impactLevel}`);
|
|
422
|
+
logger.log(
|
|
423
|
+
` ${chalk.bold("Risk:")} ${analysis.riskScore} ${chalk.bold("Benefit:")} ${analysis.benefitScore}`,
|
|
424
|
+
);
|
|
425
|
+
logger.log(
|
|
426
|
+
` ${chalk.bold("Effort:")} ${analysis.estimatedEffort}`,
|
|
427
|
+
);
|
|
428
|
+
logger.log(
|
|
429
|
+
` ${chalk.bold("Sentiment:")} ${analysis.communitySentiment}`,
|
|
430
|
+
);
|
|
431
|
+
logger.log(
|
|
432
|
+
` ${chalk.bold("Components:")} ${analysis.affectedComponents.join(", ")}`,
|
|
433
|
+
);
|
|
434
|
+
for (const rec of analysis.recommendations) {
|
|
435
|
+
logger.log(` ${chalk.yellow("→")} ${rec}`);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
await shutdown();
|
|
439
|
+
} catch (err) {
|
|
440
|
+
logger.error(`Failed: ${err.message}`);
|
|
441
|
+
process.exit(1);
|
|
442
|
+
}
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
gov
|
|
446
|
+
.command("predict <proposal-id>")
|
|
447
|
+
.description("Predict voting outcome (heuristic or vote-based)")
|
|
448
|
+
.option("--json", "Output as JSON")
|
|
449
|
+
.action(async (proposalId, options) => {
|
|
450
|
+
try {
|
|
451
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
452
|
+
_dbFromCtx(ctx);
|
|
453
|
+
const pred = predictVote(proposalId);
|
|
454
|
+
if (options.json) {
|
|
455
|
+
console.log(JSON.stringify(pred, null, 2));
|
|
456
|
+
} else {
|
|
457
|
+
const color =
|
|
458
|
+
pred.predictedOutcome === "pass" ? chalk.green : chalk.red;
|
|
459
|
+
logger.log(
|
|
460
|
+
` ${chalk.bold("Predicted:")} ${color(pred.predictedOutcome.toUpperCase())}`,
|
|
461
|
+
);
|
|
462
|
+
logger.log(
|
|
463
|
+
` ${chalk.bold("Confidence:")} ${pred.confidence} (${pred.basedOn}, n=${pred.sampleSize})`,
|
|
464
|
+
);
|
|
465
|
+
logger.log(
|
|
466
|
+
` ${chalk.bold("Probs:")} yes=${pred.yesProb} no=${pred.noProb} abstain=${pred.abstainProb}`,
|
|
467
|
+
);
|
|
468
|
+
}
|
|
469
|
+
await shutdown();
|
|
470
|
+
} catch (err) {
|
|
471
|
+
logger.error(`Failed: ${err.message}`);
|
|
472
|
+
process.exit(1);
|
|
473
|
+
}
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
gov
|
|
477
|
+
.command("stats")
|
|
478
|
+
.description("Governance statistics")
|
|
479
|
+
.option("--json", "Output as JSON")
|
|
480
|
+
.action(async (options) => {
|
|
481
|
+
try {
|
|
482
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
483
|
+
_dbFromCtx(ctx);
|
|
484
|
+
const stats = getGovernanceStats();
|
|
485
|
+
if (options.json) {
|
|
486
|
+
console.log(JSON.stringify(stats, null, 2));
|
|
487
|
+
} else {
|
|
488
|
+
logger.log(`${chalk.bold("Proposals:")} ${stats.proposalCount}`);
|
|
489
|
+
logger.log(`${chalk.bold("Votes:")} ${stats.voteCount}`);
|
|
490
|
+
logger.log(chalk.bold("By status:"));
|
|
491
|
+
for (const [s, n] of Object.entries(stats.byStatus)) {
|
|
492
|
+
logger.log(` ${_statusColor(s.padEnd(12))} ${n}`);
|
|
493
|
+
}
|
|
494
|
+
logger.log(chalk.bold("By type:"));
|
|
495
|
+
for (const [t, n] of Object.entries(stats.byType)) {
|
|
496
|
+
logger.log(` ${t.padEnd(20)} ${n}`);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
await shutdown();
|
|
500
|
+
} catch (err) {
|
|
501
|
+
logger.error(`Failed: ${err.message}`);
|
|
502
|
+
process.exit(1);
|
|
503
|
+
}
|
|
504
|
+
});
|
|
505
|
+
}
|
|
@@ -14,6 +14,8 @@ import {
|
|
|
14
14
|
runAudit,
|
|
15
15
|
getAuditReports,
|
|
16
16
|
getAuditReport,
|
|
17
|
+
runConfigAudit,
|
|
18
|
+
deployCheck,
|
|
17
19
|
} from "../lib/hardening-manager.js";
|
|
18
20
|
|
|
19
21
|
export function registerHardeningCommand(program) {
|
|
@@ -194,6 +196,114 @@ export function registerHardeningCommand(program) {
|
|
|
194
196
|
}
|
|
195
197
|
});
|
|
196
198
|
|
|
199
|
+
// config-check subcommand — real config-file audit
|
|
200
|
+
hardening
|
|
201
|
+
.command("config-check <config-path>")
|
|
202
|
+
.description("Audit a config file (presence, required keys, placeholders)")
|
|
203
|
+
.option("-n, --name <name>", "Audit name", "default")
|
|
204
|
+
.option(
|
|
205
|
+
"-r, --required <keys>",
|
|
206
|
+
"Comma-separated required key paths (e.g. db.host,server.port)",
|
|
207
|
+
)
|
|
208
|
+
.option(
|
|
209
|
+
"-f, --forbidden <placeholders>",
|
|
210
|
+
"Comma-separated forbidden placeholder substrings",
|
|
211
|
+
)
|
|
212
|
+
.option("--json", "Output as JSON")
|
|
213
|
+
.action(async (configPath, options) => {
|
|
214
|
+
try {
|
|
215
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
216
|
+
if (!ctx.db) {
|
|
217
|
+
logger.error("Database not available");
|
|
218
|
+
process.exit(1);
|
|
219
|
+
}
|
|
220
|
+
const db = ctx.db.getDatabase();
|
|
221
|
+
ensureHardeningTables(db);
|
|
222
|
+
|
|
223
|
+
const requiredKeys = options.required
|
|
224
|
+
? options.required
|
|
225
|
+
.split(",")
|
|
226
|
+
.map((s) => s.trim())
|
|
227
|
+
.filter(Boolean)
|
|
228
|
+
: undefined;
|
|
229
|
+
const forbiddenPlaceholders = options.forbidden
|
|
230
|
+
? options.forbidden
|
|
231
|
+
.split(",")
|
|
232
|
+
.map((s) => s.trim())
|
|
233
|
+
.filter(Boolean)
|
|
234
|
+
: undefined;
|
|
235
|
+
|
|
236
|
+
const result = runConfigAudit(db, {
|
|
237
|
+
name: options.name,
|
|
238
|
+
configPath,
|
|
239
|
+
requiredKeys,
|
|
240
|
+
forbiddenPlaceholders,
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
if (options.json) {
|
|
244
|
+
console.log(JSON.stringify(result, null, 2));
|
|
245
|
+
} else {
|
|
246
|
+
logger.success(`Config audit complete: score ${result.score}%`);
|
|
247
|
+
logger.log(` ${chalk.bold("ID:")} ${chalk.cyan(result.id)}`);
|
|
248
|
+
logger.log(` ${chalk.bold("Path:")} ${result.configPath}`);
|
|
249
|
+
logger.log(
|
|
250
|
+
` ${chalk.bold("Passed:")} ${result.passed}/${result.checks.length}`,
|
|
251
|
+
);
|
|
252
|
+
for (const c of result.checks) {
|
|
253
|
+
const icon =
|
|
254
|
+
c.status === "pass" ? chalk.green("✓") : chalk.red("✗");
|
|
255
|
+
const sev = c.severity ? chalk.dim(`[${c.severity}]`) : "";
|
|
256
|
+
logger.log(` ${icon} ${sev} ${c.name}: ${c.detail}`);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
await shutdown();
|
|
260
|
+
} catch (err) {
|
|
261
|
+
logger.error(`Failed: ${err.message}`);
|
|
262
|
+
process.exit(1);
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
// deploy-check subcommand — checklist from docs/design/modules/29_生产强化系统.md §八
|
|
267
|
+
hardening
|
|
268
|
+
.command("deploy-check")
|
|
269
|
+
.description("Evaluate the 6-item production-deployment checklist")
|
|
270
|
+
.option("--json", "Output as JSON")
|
|
271
|
+
.action(async (options) => {
|
|
272
|
+
try {
|
|
273
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
274
|
+
if (!ctx.db) {
|
|
275
|
+
logger.error("Database not available");
|
|
276
|
+
process.exit(1);
|
|
277
|
+
}
|
|
278
|
+
const db = ctx.db.getDatabase();
|
|
279
|
+
ensureHardeningTables(db);
|
|
280
|
+
|
|
281
|
+
const result = deployCheck();
|
|
282
|
+
if (options.json) {
|
|
283
|
+
console.log(JSON.stringify(result, null, 2));
|
|
284
|
+
} else {
|
|
285
|
+
logger.log(
|
|
286
|
+
` ${chalk.bold("Ready:")} ${result.ready ? chalk.green("YES") : chalk.red("NO")}`,
|
|
287
|
+
);
|
|
288
|
+
logger.log(` ${chalk.bold("Summary:")} ${result.summary}`);
|
|
289
|
+
for (const it of result.items) {
|
|
290
|
+
const icon =
|
|
291
|
+
it.status === "pass"
|
|
292
|
+
? chalk.green("✓")
|
|
293
|
+
: it.status === "skipped"
|
|
294
|
+
? chalk.yellow("—")
|
|
295
|
+
: chalk.red("✗");
|
|
296
|
+
logger.log(` ${icon} ${it.label}: ${it.detail}`);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
if (!result.ready) process.exitCode = 2;
|
|
300
|
+
await shutdown();
|
|
301
|
+
} catch (err) {
|
|
302
|
+
logger.error(`Failed: ${err.message}`);
|
|
303
|
+
process.exit(1);
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
|
|
197
307
|
audit
|
|
198
308
|
.command("report <audit-id>")
|
|
199
309
|
.description("Show a specific audit report")
|