chainlesschain 0.47.9 → 0.51.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.
Files changed (73) hide show
  1. package/bin/chainlesschain.js +0 -0
  2. package/package.json +1 -1
  3. package/src/assets/web-panel/.build-hash +1 -1
  4. package/src/assets/web-panel/assets/{AppLayout-6SPt_8Y_.js → AppLayout-Rvi759IS.js} +1 -1
  5. package/src/assets/web-panel/assets/Dashboard-BS-tzGNj.css +1 -0
  6. package/src/assets/web-panel/assets/{Dashboard-Br7kCwKJ.js → Dashboard-DBhFxXYQ.js} +2 -2
  7. package/src/assets/web-panel/assets/{index-tN-8TosE.js → index-uL0cZ8N_.js} +2 -2
  8. package/src/assets/web-panel/index.html +2 -2
  9. package/src/commands/codegen.js +303 -0
  10. package/src/commands/collab.js +482 -0
  11. package/src/commands/crosschain.js +382 -0
  12. package/src/commands/dbevo.js +388 -0
  13. package/src/commands/dev.js +411 -0
  14. package/src/commands/federation.js +427 -0
  15. package/src/commands/fusion.js +332 -0
  16. package/src/commands/governance.js +505 -0
  17. package/src/commands/hardening.js +110 -0
  18. package/src/commands/incentive.js +373 -0
  19. package/src/commands/inference.js +304 -0
  20. package/src/commands/infra.js +361 -0
  21. package/src/commands/ipfs.js +392 -0
  22. package/src/commands/kg.js +371 -0
  23. package/src/commands/marketplace.js +326 -0
  24. package/src/commands/mcp.js +97 -18
  25. package/src/commands/multimodal.js +404 -0
  26. package/src/commands/nlprog.js +329 -0
  27. package/src/commands/ops.js +408 -0
  28. package/src/commands/perception.js +385 -0
  29. package/src/commands/pqc.js +34 -0
  30. package/src/commands/privacy.js +345 -0
  31. package/src/commands/quantization.js +280 -0
  32. package/src/commands/recommend.js +336 -0
  33. package/src/commands/reputation.js +349 -0
  34. package/src/commands/runtime.js +500 -0
  35. package/src/commands/sla.js +352 -0
  36. package/src/commands/stress.js +252 -0
  37. package/src/commands/tech.js +268 -0
  38. package/src/commands/tenant.js +576 -0
  39. package/src/commands/trust.js +366 -0
  40. package/src/harness/mcp-client.js +330 -54
  41. package/src/index.js +118 -0
  42. package/src/lib/aiops.js +523 -0
  43. package/src/lib/autonomous-developer.js +524 -0
  44. package/src/lib/code-agent.js +442 -0
  45. package/src/lib/collaboration-governance.js +556 -0
  46. package/src/lib/community-governance.js +649 -0
  47. package/src/lib/content-recommendation.js +600 -0
  48. package/src/lib/cross-chain.js +669 -0
  49. package/src/lib/dbevo.js +669 -0
  50. package/src/lib/decentral-infra.js +445 -0
  51. package/src/lib/federation-hardening.js +587 -0
  52. package/src/lib/hardening-manager.js +409 -0
  53. package/src/lib/inference-network.js +407 -0
  54. package/src/lib/ipfs-storage.js +575 -0
  55. package/src/lib/knowledge-graph.js +530 -0
  56. package/src/lib/mcp-client.js +3 -0
  57. package/src/lib/multimodal.js +725 -0
  58. package/src/lib/nl-programming.js +595 -0
  59. package/src/lib/perception.js +500 -0
  60. package/src/lib/pqc-manager.js +141 -9
  61. package/src/lib/privacy-computing.js +575 -0
  62. package/src/lib/protocol-fusion.js +535 -0
  63. package/src/lib/quantization.js +362 -0
  64. package/src/lib/reputation-optimizer.js +509 -0
  65. package/src/lib/skill-marketplace.js +397 -0
  66. package/src/lib/sla-manager.js +484 -0
  67. package/src/lib/stress-tester.js +383 -0
  68. package/src/lib/tech-learning-engine.js +651 -0
  69. package/src/lib/tenant-saas.js +831 -0
  70. package/src/lib/token-incentive.js +513 -0
  71. package/src/lib/trust-security.js +473 -0
  72. package/src/lib/universal-runtime.js +771 -0
  73. 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")