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,427 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `cc federation` — CLI surface for Phase 58 Federation Hardening.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Command } from "commander";
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
CIRCUIT_STATE,
|
|
9
|
+
HEALTH_STATUS,
|
|
10
|
+
HEALTH_METRIC,
|
|
11
|
+
POOL_DEFAULTS,
|
|
12
|
+
ensureFederationHardeningTables,
|
|
13
|
+
registerNode,
|
|
14
|
+
getCircuitBreaker,
|
|
15
|
+
listCircuitBreakers,
|
|
16
|
+
recordFailure,
|
|
17
|
+
recordSuccess,
|
|
18
|
+
tryHalfOpen,
|
|
19
|
+
resetCircuitBreaker,
|
|
20
|
+
removeNode,
|
|
21
|
+
recordHealthCheck,
|
|
22
|
+
getHealthCheck,
|
|
23
|
+
listHealthChecks,
|
|
24
|
+
getNodeHealth,
|
|
25
|
+
initPool,
|
|
26
|
+
acquireConnection,
|
|
27
|
+
releaseConnection,
|
|
28
|
+
getPoolStats,
|
|
29
|
+
listPools,
|
|
30
|
+
destroyPool,
|
|
31
|
+
getFederationHardeningStats,
|
|
32
|
+
} from "../lib/federation-hardening.js";
|
|
33
|
+
|
|
34
|
+
function _dbFromCtx(cmd) {
|
|
35
|
+
const root = cmd?.parent?.parent ?? cmd?.parent;
|
|
36
|
+
return root?._db;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function registerFederationCommand(program) {
|
|
40
|
+
const fed = new Command("federation")
|
|
41
|
+
.description("Federation hardening system (Phase 58)")
|
|
42
|
+
.hook("preAction", (thisCmd) => {
|
|
43
|
+
const db = _dbFromCtx(thisCmd);
|
|
44
|
+
if (db) ensureFederationHardeningTables(db);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
/* ── Catalogs ────────────────────────────────────── */
|
|
48
|
+
|
|
49
|
+
fed
|
|
50
|
+
.command("circuit-states")
|
|
51
|
+
.description("List circuit breaker states")
|
|
52
|
+
.option("--json", "JSON output")
|
|
53
|
+
.action((opts) => {
|
|
54
|
+
const states = Object.values(CIRCUIT_STATE);
|
|
55
|
+
if (opts.json) return console.log(JSON.stringify(states, null, 2));
|
|
56
|
+
for (const s of states) console.log(` ${s}`);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
fed
|
|
60
|
+
.command("health-statuses")
|
|
61
|
+
.description("List health statuses")
|
|
62
|
+
.option("--json", "JSON output")
|
|
63
|
+
.action((opts) => {
|
|
64
|
+
const statuses = Object.values(HEALTH_STATUS);
|
|
65
|
+
if (opts.json) return console.log(JSON.stringify(statuses, null, 2));
|
|
66
|
+
for (const s of statuses) console.log(` ${s}`);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
fed
|
|
70
|
+
.command("health-metrics")
|
|
71
|
+
.description("List health check metric types")
|
|
72
|
+
.option("--json", "JSON output")
|
|
73
|
+
.action((opts) => {
|
|
74
|
+
const metrics = Object.values(HEALTH_METRIC);
|
|
75
|
+
if (opts.json) return console.log(JSON.stringify(metrics, null, 2));
|
|
76
|
+
for (const m of metrics) console.log(` ${m}`);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
/* ── Node Registration ──────────────────────────── */
|
|
80
|
+
|
|
81
|
+
fed
|
|
82
|
+
.command("register <node-id>")
|
|
83
|
+
.description("Register a federation node with circuit breaker")
|
|
84
|
+
.option(
|
|
85
|
+
"-f, --failure-threshold <n>",
|
|
86
|
+
"Failure threshold before tripping",
|
|
87
|
+
parseInt,
|
|
88
|
+
)
|
|
89
|
+
.option("-s, --success-threshold <n>", "Success probes to close", parseInt)
|
|
90
|
+
.option("-t, --open-timeout <ms>", "Open timeout in ms", parseInt)
|
|
91
|
+
.option("-m, --metadata <json>", "Metadata JSON")
|
|
92
|
+
.option("--json", "JSON output")
|
|
93
|
+
.action((nodeId, opts) => {
|
|
94
|
+
const db = _dbFromCtx(fed);
|
|
95
|
+
const result = registerNode(db, nodeId, {
|
|
96
|
+
failureThreshold: opts.failureThreshold,
|
|
97
|
+
successThreshold: opts.successThreshold,
|
|
98
|
+
openTimeout: opts.openTimeout,
|
|
99
|
+
metadata: opts.metadata,
|
|
100
|
+
});
|
|
101
|
+
if (opts.json) return console.log(JSON.stringify(result, null, 2));
|
|
102
|
+
if (result.registered) console.log(`Node registered: ${result.nodeId}`);
|
|
103
|
+
else console.log(`Failed: ${result.reason}`);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
fed
|
|
107
|
+
.command("remove <node-id>")
|
|
108
|
+
.description("Remove a federation node")
|
|
109
|
+
.option("--json", "JSON output")
|
|
110
|
+
.action((nodeId, opts) => {
|
|
111
|
+
const db = _dbFromCtx(fed);
|
|
112
|
+
const result = removeNode(db, nodeId);
|
|
113
|
+
if (opts.json) return console.log(JSON.stringify(result, null, 2));
|
|
114
|
+
console.log(
|
|
115
|
+
result.removed ? "Node removed." : `Failed: ${result.reason}`,
|
|
116
|
+
);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
/* ── Circuit Breaker ────────────────────────────── */
|
|
120
|
+
|
|
121
|
+
fed
|
|
122
|
+
.command("breaker-show <node-id>")
|
|
123
|
+
.description("Show circuit breaker state for a node")
|
|
124
|
+
.option("--json", "JSON output")
|
|
125
|
+
.action((nodeId, opts) => {
|
|
126
|
+
const db = _dbFromCtx(fed);
|
|
127
|
+
const b = getCircuitBreaker(db, nodeId);
|
|
128
|
+
if (!b) return console.log("Node not found.");
|
|
129
|
+
if (opts.json) return console.log(JSON.stringify(b, null, 2));
|
|
130
|
+
console.log(`Node: ${b.node_id}`);
|
|
131
|
+
console.log(`State: ${b.state}`);
|
|
132
|
+
console.log(
|
|
133
|
+
`Failures: ${b.failure_count} (threshold: ${b.failure_threshold})`,
|
|
134
|
+
);
|
|
135
|
+
console.log(
|
|
136
|
+
`Successes: ${b.success_count} (threshold: ${b.success_threshold})`,
|
|
137
|
+
);
|
|
138
|
+
console.log(`Timeout: ${b.open_timeout}ms`);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
fed
|
|
142
|
+
.command("breakers")
|
|
143
|
+
.description("List circuit breakers")
|
|
144
|
+
.option("-s, --state <state>", "Filter by state (closed/open/half_open)")
|
|
145
|
+
.option("--limit <n>", "Max results", parseInt)
|
|
146
|
+
.option("--json", "JSON output")
|
|
147
|
+
.action((opts) => {
|
|
148
|
+
const db = _dbFromCtx(fed);
|
|
149
|
+
const breakers = listCircuitBreakers(db, {
|
|
150
|
+
state: opts.state,
|
|
151
|
+
limit: opts.limit,
|
|
152
|
+
});
|
|
153
|
+
if (opts.json) return console.log(JSON.stringify(breakers, null, 2));
|
|
154
|
+
if (breakers.length === 0) return console.log("No circuit breakers.");
|
|
155
|
+
for (const b of breakers) {
|
|
156
|
+
console.log(
|
|
157
|
+
` ${b.state.padEnd(10)} fail:${String(b.failure_count).padEnd(4)} succ:${String(b.success_count).padEnd(4)} ${b.node_id}`,
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
fed
|
|
163
|
+
.command("failure <node-id>")
|
|
164
|
+
.description("Record a failure for a node (may trip breaker)")
|
|
165
|
+
.option("--json", "JSON output")
|
|
166
|
+
.action((nodeId, opts) => {
|
|
167
|
+
const db = _dbFromCtx(fed);
|
|
168
|
+
const result = recordFailure(db, nodeId);
|
|
169
|
+
if (opts.json) return console.log(JSON.stringify(result, null, 2));
|
|
170
|
+
if (result.updated) {
|
|
171
|
+
const tripped =
|
|
172
|
+
result.previousState !== result.state ? ` → ${result.state}` : "";
|
|
173
|
+
console.log(
|
|
174
|
+
`Failure recorded (count: ${result.failureCount})${tripped}`,
|
|
175
|
+
);
|
|
176
|
+
} else console.log(`Failed: ${result.reason}`);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
fed
|
|
180
|
+
.command("success <node-id>")
|
|
181
|
+
.description("Record a success for a node (may close breaker)")
|
|
182
|
+
.option("--json", "JSON output")
|
|
183
|
+
.action((nodeId, opts) => {
|
|
184
|
+
const db = _dbFromCtx(fed);
|
|
185
|
+
const result = recordSuccess(db, nodeId);
|
|
186
|
+
if (opts.json) return console.log(JSON.stringify(result, null, 2));
|
|
187
|
+
if (result.updated) {
|
|
188
|
+
const closed =
|
|
189
|
+
result.previousState !== result.state ? ` → ${result.state}` : "";
|
|
190
|
+
console.log(
|
|
191
|
+
`Success recorded (count: ${result.successCount})${closed}`,
|
|
192
|
+
);
|
|
193
|
+
} else console.log(`Failed: ${result.reason}`);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
fed
|
|
197
|
+
.command("half-open <node-id>")
|
|
198
|
+
.description("Try transitioning an open breaker to half-open")
|
|
199
|
+
.option("--json", "JSON output")
|
|
200
|
+
.action((nodeId, opts) => {
|
|
201
|
+
const db = _dbFromCtx(fed);
|
|
202
|
+
const result = tryHalfOpen(db, nodeId);
|
|
203
|
+
if (opts.json) return console.log(JSON.stringify(result, null, 2));
|
|
204
|
+
if (result.updated) console.log(`Breaker transitioned to half_open`);
|
|
205
|
+
else
|
|
206
|
+
console.log(
|
|
207
|
+
`Failed: ${result.reason}${result.remainingMs ? ` (${result.remainingMs}ms remaining)` : ""}`,
|
|
208
|
+
);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
fed
|
|
212
|
+
.command("reset <node-id>")
|
|
213
|
+
.description("Reset circuit breaker to closed state")
|
|
214
|
+
.option("--json", "JSON output")
|
|
215
|
+
.action((nodeId, opts) => {
|
|
216
|
+
const db = _dbFromCtx(fed);
|
|
217
|
+
const result = resetCircuitBreaker(db, nodeId);
|
|
218
|
+
if (opts.json) return console.log(JSON.stringify(result, null, 2));
|
|
219
|
+
console.log(
|
|
220
|
+
result.reset ? "Breaker reset to closed." : `Failed: ${result.reason}`,
|
|
221
|
+
);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
/* ── Health Checks ──────────────────────────────── */
|
|
225
|
+
|
|
226
|
+
fed
|
|
227
|
+
.command("check <node-id>")
|
|
228
|
+
.description("Record a health check result")
|
|
229
|
+
.requiredOption(
|
|
230
|
+
"-t, --type <type>",
|
|
231
|
+
"Check type (heartbeat/latency/success_rate/cpu_usage/memory_usage)",
|
|
232
|
+
)
|
|
233
|
+
.requiredOption(
|
|
234
|
+
"-s, --status <status>",
|
|
235
|
+
"Health status (healthy/degraded/unhealthy/unknown)",
|
|
236
|
+
)
|
|
237
|
+
.option("-m, --metrics <json>", "Metrics JSON")
|
|
238
|
+
.option("--json", "JSON output")
|
|
239
|
+
.action((nodeId, opts) => {
|
|
240
|
+
const db = _dbFromCtx(fed);
|
|
241
|
+
const result = recordHealthCheck(db, {
|
|
242
|
+
nodeId,
|
|
243
|
+
checkType: opts.type,
|
|
244
|
+
status: opts.status,
|
|
245
|
+
metrics: opts.metrics,
|
|
246
|
+
});
|
|
247
|
+
if (opts.json) return console.log(JSON.stringify(result, null, 2));
|
|
248
|
+
if (result.recorded)
|
|
249
|
+
console.log(`Health check recorded: ${result.checkId}`);
|
|
250
|
+
else console.log(`Failed: ${result.reason}`);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
fed
|
|
254
|
+
.command("check-show <check-id>")
|
|
255
|
+
.description("Show health check details")
|
|
256
|
+
.option("--json", "JSON output")
|
|
257
|
+
.action((checkId, opts) => {
|
|
258
|
+
const db = _dbFromCtx(fed);
|
|
259
|
+
const c = getHealthCheck(db, checkId);
|
|
260
|
+
if (!c) return console.log("Check not found.");
|
|
261
|
+
if (opts.json) return console.log(JSON.stringify(c, null, 2));
|
|
262
|
+
console.log(`ID: ${c.check_id}`);
|
|
263
|
+
console.log(`Node: ${c.node_id}`);
|
|
264
|
+
console.log(`Type: ${c.check_type}`);
|
|
265
|
+
console.log(`Status: ${c.status}`);
|
|
266
|
+
if (c.metrics) console.log(`Metrics: ${c.metrics}`);
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
fed
|
|
270
|
+
.command("checks")
|
|
271
|
+
.description("List health checks")
|
|
272
|
+
.option("-n, --node <id>", "Filter by node ID")
|
|
273
|
+
.option("-t, --type <type>", "Filter by check type")
|
|
274
|
+
.option("-s, --status <status>", "Filter by status")
|
|
275
|
+
.option("--limit <n>", "Max results", parseInt)
|
|
276
|
+
.option("--json", "JSON output")
|
|
277
|
+
.action((opts) => {
|
|
278
|
+
const db = _dbFromCtx(fed);
|
|
279
|
+
const checks = listHealthChecks(db, {
|
|
280
|
+
nodeId: opts.node,
|
|
281
|
+
checkType: opts.type,
|
|
282
|
+
status: opts.status,
|
|
283
|
+
limit: opts.limit,
|
|
284
|
+
});
|
|
285
|
+
if (opts.json) return console.log(JSON.stringify(checks, null, 2));
|
|
286
|
+
if (checks.length === 0) return console.log("No health checks.");
|
|
287
|
+
for (const c of checks) {
|
|
288
|
+
console.log(
|
|
289
|
+
` ${c.status.padEnd(12)} ${c.check_type.padEnd(14)} ${c.node_id.padEnd(20)} ${c.check_id.slice(0, 8)}`,
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
fed
|
|
295
|
+
.command("node-health <node-id>")
|
|
296
|
+
.description("Get aggregated health status for a node")
|
|
297
|
+
.option("--json", "JSON output")
|
|
298
|
+
.action((nodeId, opts) => {
|
|
299
|
+
const db = _dbFromCtx(fed);
|
|
300
|
+
const health = getNodeHealth(db, nodeId);
|
|
301
|
+
if (opts.json) return console.log(JSON.stringify(health, null, 2));
|
|
302
|
+
console.log(`Node: ${health.nodeId}`);
|
|
303
|
+
console.log(`Status: ${health.status}`);
|
|
304
|
+
console.log(`Checks: ${health.checks}`);
|
|
305
|
+
if (health.latestChecks) {
|
|
306
|
+
for (const c of health.latestChecks) {
|
|
307
|
+
console.log(` ${c.checkType.padEnd(14)} ${c.status}`);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
/* ── Connection Pool ────────────────────────────── */
|
|
313
|
+
|
|
314
|
+
fed
|
|
315
|
+
.command("pool-init <node-id>")
|
|
316
|
+
.description("Initialize a connection pool for a node")
|
|
317
|
+
.option("--min <n>", "Min connections", parseInt)
|
|
318
|
+
.option("--max <n>", "Max connections", parseInt)
|
|
319
|
+
.option("--idle-timeout <ms>", "Idle timeout in ms", parseInt)
|
|
320
|
+
.option("--json", "JSON output")
|
|
321
|
+
.action((nodeId, opts) => {
|
|
322
|
+
const result = initPool(nodeId, {
|
|
323
|
+
minConnections: opts.min,
|
|
324
|
+
maxConnections: opts.max,
|
|
325
|
+
idleTimeout: opts.idleTimeout,
|
|
326
|
+
});
|
|
327
|
+
if (opts.json) return console.log(JSON.stringify(result, null, 2));
|
|
328
|
+
if (result.initialized) console.log(`Pool initialized for ${nodeId}`);
|
|
329
|
+
else console.log(`Failed: ${result.reason}`);
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
fed
|
|
333
|
+
.command("pool-acquire <node-id>")
|
|
334
|
+
.description("Acquire a connection from pool")
|
|
335
|
+
.option("--json", "JSON output")
|
|
336
|
+
.action((nodeId, opts) => {
|
|
337
|
+
const result = acquireConnection(nodeId);
|
|
338
|
+
if (opts.json) return console.log(JSON.stringify(result, null, 2));
|
|
339
|
+
if (result.acquired)
|
|
340
|
+
console.log(
|
|
341
|
+
`Connection acquired (active: ${result.active}, idle: ${result.idle})`,
|
|
342
|
+
);
|
|
343
|
+
else console.log(`Failed: ${result.reason}`);
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
fed
|
|
347
|
+
.command("pool-release <node-id>")
|
|
348
|
+
.description("Release a connection back to pool")
|
|
349
|
+
.option("--json", "JSON output")
|
|
350
|
+
.action((nodeId, opts) => {
|
|
351
|
+
const result = releaseConnection(nodeId);
|
|
352
|
+
if (opts.json) return console.log(JSON.stringify(result, null, 2));
|
|
353
|
+
if (result.released)
|
|
354
|
+
console.log(
|
|
355
|
+
`Connection released (active: ${result.active}, idle: ${result.idle})`,
|
|
356
|
+
);
|
|
357
|
+
else console.log(`Failed: ${result.reason}`);
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
fed
|
|
361
|
+
.command("pool-stats <node-id>")
|
|
362
|
+
.description("Show connection pool stats")
|
|
363
|
+
.option("--json", "JSON output")
|
|
364
|
+
.action((nodeId, opts) => {
|
|
365
|
+
const pool = getPoolStats(nodeId);
|
|
366
|
+
if (!pool) return console.log("Pool not found.");
|
|
367
|
+
if (opts.json) return console.log(JSON.stringify(pool, null, 2));
|
|
368
|
+
console.log(`Node: ${pool.nodeId}`);
|
|
369
|
+
console.log(`Active: ${pool.activeConnections}`);
|
|
370
|
+
console.log(`Idle: ${pool.idleConnections}`);
|
|
371
|
+
console.log(`Max: ${pool.maxConnections}`);
|
|
372
|
+
console.log(`Created: ${pool.totalCreated}`);
|
|
373
|
+
console.log(`Waiting: ${pool.waitingRequests}`);
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
fed
|
|
377
|
+
.command("pools")
|
|
378
|
+
.description("List all connection pools")
|
|
379
|
+
.option("--json", "JSON output")
|
|
380
|
+
.action((opts) => {
|
|
381
|
+
const pools = listPools();
|
|
382
|
+
if (opts.json) return console.log(JSON.stringify(pools, null, 2));
|
|
383
|
+
if (pools.length === 0) return console.log("No pools.");
|
|
384
|
+
for (const p of pools) {
|
|
385
|
+
console.log(
|
|
386
|
+
` ${p.nodeId.padEnd(20)} active:${String(p.activeConnections).padEnd(4)} idle:${String(p.idleConnections).padEnd(4)} max:${p.maxConnections}`,
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
fed
|
|
392
|
+
.command("pool-destroy <node-id>")
|
|
393
|
+
.description("Destroy a connection pool")
|
|
394
|
+
.option("--json", "JSON output")
|
|
395
|
+
.action((nodeId, opts) => {
|
|
396
|
+
const result = destroyPool(nodeId);
|
|
397
|
+
if (opts.json) return console.log(JSON.stringify(result, null, 2));
|
|
398
|
+
console.log(
|
|
399
|
+
result.destroyed ? "Pool destroyed." : `Failed: ${result.reason}`,
|
|
400
|
+
);
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
/* ── Stats ──────────────────────────────────────── */
|
|
404
|
+
|
|
405
|
+
fed
|
|
406
|
+
.command("stats")
|
|
407
|
+
.description("Federation hardening statistics")
|
|
408
|
+
.option("--json", "JSON output")
|
|
409
|
+
.action((opts) => {
|
|
410
|
+
const db = _dbFromCtx(fed);
|
|
411
|
+
const s = getFederationHardeningStats(db);
|
|
412
|
+
if (opts.json) return console.log(JSON.stringify(s, null, 2));
|
|
413
|
+
console.log(`Circuit breakers: ${s.circuitBreakers.total}`);
|
|
414
|
+
for (const [state, count] of Object.entries(s.circuitBreakers.byState)) {
|
|
415
|
+
if (count > 0) console.log(` ${state.padEnd(10)} ${count}`);
|
|
416
|
+
}
|
|
417
|
+
console.log(`Health checks: ${s.healthChecks.total}`);
|
|
418
|
+
for (const [status, count] of Object.entries(s.healthChecks.byStatus)) {
|
|
419
|
+
if (count > 0) console.log(` ${status.padEnd(12)} ${count}`);
|
|
420
|
+
}
|
|
421
|
+
console.log(
|
|
422
|
+
`Connection pools: ${s.connectionPools.total} (active: ${s.connectionPools.totalActive}, idle: ${s.connectionPools.totalIdle})`,
|
|
423
|
+
);
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
program.addCommand(fed);
|
|
427
|
+
}
|