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.
- package/bin/chainlesschain.js +0 -0
- package/package.json +1 -1
- 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/codegen.js +303 -0
- package/src/commands/collab.js +482 -0
- 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/ipfs.js +392 -0
- package/src/commands/kg.js +371 -0
- package/src/commands/marketplace.js +326 -0
- package/src/commands/mcp.js +97 -18
- package/src/commands/multimodal.js +404 -0
- package/src/commands/nlprog.js +329 -0
- 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/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 +118 -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/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/ipfs-storage.js +575 -0
- package/src/lib/knowledge-graph.js +530 -0
- package/src/lib/mcp-client.js +3 -0
- package/src/lib/multimodal.js +725 -0
- package/src/lib/nl-programming.js +595 -0
- 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/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/token-incentive.js +513 -0
- package/src/lib/trust-security.js +473 -0
- package/src/lib/universal-runtime.js +771 -0
- package/src/assets/web-panel/assets/Dashboard-CKeMmCoT.css +0 -1
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `cc recommend` — CLI surface for Phase 48 Smart Content Recommendation.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Command } from "commander";
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
CONTENT_TYPES,
|
|
9
|
+
RECOMMENDATION_STATUS,
|
|
10
|
+
FEEDBACK_VALUES,
|
|
11
|
+
DEFAULT_CONFIG,
|
|
12
|
+
ensureRecommendationTables,
|
|
13
|
+
getProfile,
|
|
14
|
+
createProfile,
|
|
15
|
+
updateProfile,
|
|
16
|
+
deleteProfile,
|
|
17
|
+
listProfiles,
|
|
18
|
+
applyDecay,
|
|
19
|
+
scoreContent,
|
|
20
|
+
generateRecommendations,
|
|
21
|
+
getRecommendation,
|
|
22
|
+
listRecommendations,
|
|
23
|
+
markViewed,
|
|
24
|
+
provideFeedback,
|
|
25
|
+
dismissRecommendation,
|
|
26
|
+
getRecommendationStats,
|
|
27
|
+
getTopInterests,
|
|
28
|
+
suggestTopics,
|
|
29
|
+
} from "../lib/content-recommendation.js";
|
|
30
|
+
|
|
31
|
+
function _dbFromCtx(cmd) {
|
|
32
|
+
const root = cmd?.parent?.parent ?? cmd?.parent;
|
|
33
|
+
return root?._db;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function registerRecommendCommand(program) {
|
|
37
|
+
const rec = new Command("recommend")
|
|
38
|
+
.description("Smart content recommendation (Phase 48)")
|
|
39
|
+
.hook("preAction", (thisCmd) => {
|
|
40
|
+
const db = _dbFromCtx(thisCmd);
|
|
41
|
+
if (db) ensureRecommendationTables(db);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
/* ── Catalogs ────────────────────────────────────── */
|
|
45
|
+
|
|
46
|
+
rec
|
|
47
|
+
.command("content-types")
|
|
48
|
+
.description("List supported content types")
|
|
49
|
+
.option("--json", "JSON output")
|
|
50
|
+
.action((opts) => {
|
|
51
|
+
const types = Object.values(CONTENT_TYPES);
|
|
52
|
+
if (opts.json) return console.log(JSON.stringify(types, null, 2));
|
|
53
|
+
for (const t of types)
|
|
54
|
+
console.log(` ${t.id.padEnd(12)} ${t.name} — ${t.description}`);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
rec
|
|
58
|
+
.command("statuses")
|
|
59
|
+
.description("List recommendation statuses")
|
|
60
|
+
.option("--json", "JSON output")
|
|
61
|
+
.action((opts) => {
|
|
62
|
+
const statuses = Object.values(RECOMMENDATION_STATUS);
|
|
63
|
+
if (opts.json) return console.log(JSON.stringify(statuses, null, 2));
|
|
64
|
+
for (const s of statuses) console.log(` ${s}`);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
rec
|
|
68
|
+
.command("feedback-values")
|
|
69
|
+
.description("List feedback values")
|
|
70
|
+
.option("--json", "JSON output")
|
|
71
|
+
.action((opts) => {
|
|
72
|
+
const vals = Object.values(FEEDBACK_VALUES);
|
|
73
|
+
if (opts.json) return console.log(JSON.stringify(vals, null, 2));
|
|
74
|
+
for (const v of vals) console.log(` ${v}`);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
/* ── Profile Management ──────────────────────────── */
|
|
78
|
+
|
|
79
|
+
rec
|
|
80
|
+
.command("profile <user-id>")
|
|
81
|
+
.description("Show user interest profile")
|
|
82
|
+
.option("--json", "JSON output")
|
|
83
|
+
.action((userId, opts) => {
|
|
84
|
+
const db = _dbFromCtx(rec);
|
|
85
|
+
const p = getProfile(db, userId);
|
|
86
|
+
if (!p) return console.log("Profile not found.");
|
|
87
|
+
if (opts.json) return console.log(JSON.stringify(p, null, 2));
|
|
88
|
+
console.log(`User: ${p.user_id}`);
|
|
89
|
+
console.log(`Updated: ${new Date(p.last_updated).toISOString()}`);
|
|
90
|
+
console.log(`Updates: ${p.update_count}`);
|
|
91
|
+
console.log(`Decay: ${p.decay_factor}`);
|
|
92
|
+
console.log(`Topics:`);
|
|
93
|
+
for (const [t, w] of Object.entries(p.topics))
|
|
94
|
+
console.log(` ${t}: ${w}`);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
rec
|
|
98
|
+
.command("create-profile <user-id>")
|
|
99
|
+
.description("Create interest profile for a user")
|
|
100
|
+
.option("-t, --topics <json>", "Topic weights JSON")
|
|
101
|
+
.option("-w, --weights <json>", "Interaction weights JSON")
|
|
102
|
+
.option("--json", "JSON output")
|
|
103
|
+
.action((userId, opts) => {
|
|
104
|
+
const db = _dbFromCtx(rec);
|
|
105
|
+
const topics = opts.topics ? JSON.parse(opts.topics) : {};
|
|
106
|
+
const interactionWeights = opts.weights ? JSON.parse(opts.weights) : {};
|
|
107
|
+
const result = createProfile(db, userId, { topics, interactionWeights });
|
|
108
|
+
if (opts.json) return console.log(JSON.stringify(result, null, 2));
|
|
109
|
+
if (result.profileId) console.log(`Profile created: ${result.profileId}`);
|
|
110
|
+
else console.log(`Failed: ${result.reason}`);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
rec
|
|
114
|
+
.command("update-profile <user-id>")
|
|
115
|
+
.description("Update interest profile")
|
|
116
|
+
.option("-t, --topics <json>", "Topic weights JSON")
|
|
117
|
+
.option("-w, --weights <json>", "Interaction weights JSON")
|
|
118
|
+
.option("-d, --decay <factor>", "Decay factor", parseFloat)
|
|
119
|
+
.option("--json", "JSON output")
|
|
120
|
+
.action((userId, opts) => {
|
|
121
|
+
const db = _dbFromCtx(rec);
|
|
122
|
+
const params = {};
|
|
123
|
+
if (opts.topics) params.topics = JSON.parse(opts.topics);
|
|
124
|
+
if (opts.weights) params.interactionWeights = JSON.parse(opts.weights);
|
|
125
|
+
if (opts.decay !== undefined) params.decayFactor = opts.decay;
|
|
126
|
+
const result = updateProfile(db, userId, params);
|
|
127
|
+
if (opts.json) return console.log(JSON.stringify(result, null, 2));
|
|
128
|
+
console.log(
|
|
129
|
+
result.updated ? "Profile updated." : `Failed: ${result.reason}`,
|
|
130
|
+
);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
rec
|
|
134
|
+
.command("delete-profile <user-id>")
|
|
135
|
+
.description("Delete interest profile")
|
|
136
|
+
.option("--json", "JSON output")
|
|
137
|
+
.action((userId, opts) => {
|
|
138
|
+
const db = _dbFromCtx(rec);
|
|
139
|
+
const result = deleteProfile(db, userId);
|
|
140
|
+
if (opts.json) return console.log(JSON.stringify(result, null, 2));
|
|
141
|
+
console.log(
|
|
142
|
+
result.deleted ? "Profile deleted." : `Failed: ${result.reason}`,
|
|
143
|
+
);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
rec
|
|
147
|
+
.command("profiles")
|
|
148
|
+
.description("List all interest profiles")
|
|
149
|
+
.option("--limit <n>", "Max results", parseInt)
|
|
150
|
+
.option("--json", "JSON output")
|
|
151
|
+
.action((opts) => {
|
|
152
|
+
const db = _dbFromCtx(rec);
|
|
153
|
+
const profiles = listProfiles(db, { limit: opts.limit });
|
|
154
|
+
if (opts.json) return console.log(JSON.stringify(profiles, null, 2));
|
|
155
|
+
if (profiles.length === 0) return console.log("No profiles.");
|
|
156
|
+
for (const p of profiles) {
|
|
157
|
+
const topicCount = Object.keys(p.topics).length;
|
|
158
|
+
console.log(
|
|
159
|
+
` ${p.user_id.padEnd(20)} ${topicCount} topics updated ${new Date(p.last_updated).toISOString()}`,
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
rec
|
|
165
|
+
.command("decay <user-id>")
|
|
166
|
+
.description("Apply time decay to profile topics")
|
|
167
|
+
.option("--json", "JSON output")
|
|
168
|
+
.action((userId, opts) => {
|
|
169
|
+
const db = _dbFromCtx(rec);
|
|
170
|
+
const result = applyDecay(db, userId);
|
|
171
|
+
if (opts.json) return console.log(JSON.stringify(result, null, 2));
|
|
172
|
+
if (result.applied)
|
|
173
|
+
console.log(`Decay applied. ${result.topicCount} topics remaining.`);
|
|
174
|
+
else console.log(`Failed: ${result.reason}`);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
/* ── Recommendation Generation ───────────────────── */
|
|
178
|
+
|
|
179
|
+
rec
|
|
180
|
+
.command("generate <user-id>")
|
|
181
|
+
.description(
|
|
182
|
+
"Generate recommendations from content pool (JSON stdin or --pool)",
|
|
183
|
+
)
|
|
184
|
+
.option("-p, --pool <json>", "Content pool as JSON array")
|
|
185
|
+
.option("-l, --limit <n>", "Max recommendations", parseInt)
|
|
186
|
+
.option("-m, --min-score <n>", "Minimum score", parseFloat)
|
|
187
|
+
.option("--json", "JSON output")
|
|
188
|
+
.action((userId, opts) => {
|
|
189
|
+
const db = _dbFromCtx(rec);
|
|
190
|
+
if (!opts.pool) {
|
|
191
|
+
return console.log("Provide --pool as JSON array of content items.");
|
|
192
|
+
}
|
|
193
|
+
const pool = JSON.parse(opts.pool);
|
|
194
|
+
const result = generateRecommendations(db, userId, pool, {
|
|
195
|
+
limit: opts.limit,
|
|
196
|
+
minScore: opts.minScore,
|
|
197
|
+
});
|
|
198
|
+
if (opts.json) return console.log(JSON.stringify(result, null, 2));
|
|
199
|
+
if (result.reason) return console.log(`Failed: ${result.reason}`);
|
|
200
|
+
console.log(`Generated ${result.generated} recommendations.`);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
/* ── Recommendation CRUD ─────────────────────────── */
|
|
204
|
+
|
|
205
|
+
rec
|
|
206
|
+
.command("show <rec-id>")
|
|
207
|
+
.description("Show recommendation details")
|
|
208
|
+
.option("--json", "JSON output")
|
|
209
|
+
.action((recId, opts) => {
|
|
210
|
+
const db = _dbFromCtx(rec);
|
|
211
|
+
const r = getRecommendation(db, recId);
|
|
212
|
+
if (!r) return console.log("Recommendation not found.");
|
|
213
|
+
if (opts.json) return console.log(JSON.stringify(r, null, 2));
|
|
214
|
+
console.log(`ID: ${r.id}`);
|
|
215
|
+
console.log(`User: ${r.user_id}`);
|
|
216
|
+
console.log(`Content: ${r.content_id} (${r.content_type})`);
|
|
217
|
+
console.log(`Title: ${r.title}`);
|
|
218
|
+
console.log(`Score: ${r.score}`);
|
|
219
|
+
console.log(`Reason: ${r.reason}`);
|
|
220
|
+
console.log(`Status: ${r.status}`);
|
|
221
|
+
if (r.feedback) console.log(`Feedback: ${r.feedback}`);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
rec
|
|
225
|
+
.command("list <user-id>")
|
|
226
|
+
.description("List recommendations for a user")
|
|
227
|
+
.option("-s, --status <status>", "Filter by status")
|
|
228
|
+
.option("-t, --type <type>", "Filter by content type")
|
|
229
|
+
.option("-m, --min-score <n>", "Minimum score", parseFloat)
|
|
230
|
+
.option("--limit <n>", "Max results", parseInt)
|
|
231
|
+
.option("--json", "JSON output")
|
|
232
|
+
.action((userId, opts) => {
|
|
233
|
+
const db = _dbFromCtx(rec);
|
|
234
|
+
const recs = listRecommendations(db, userId, {
|
|
235
|
+
status: opts.status,
|
|
236
|
+
contentType: opts.type,
|
|
237
|
+
limit: opts.limit,
|
|
238
|
+
minScore: opts.minScore,
|
|
239
|
+
});
|
|
240
|
+
if (opts.json) return console.log(JSON.stringify(recs, null, 2));
|
|
241
|
+
if (recs.length === 0) return console.log("No recommendations.");
|
|
242
|
+
for (const r of recs) {
|
|
243
|
+
const fb = r.feedback ? ` [${r.feedback}]` : "";
|
|
244
|
+
console.log(
|
|
245
|
+
` ${r.score.toFixed(3)} ${r.status.padEnd(10)} ${r.title || r.content_id}${fb}`,
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
rec
|
|
251
|
+
.command("view <rec-id>")
|
|
252
|
+
.description("Mark recommendation as viewed")
|
|
253
|
+
.option("--json", "JSON output")
|
|
254
|
+
.action((recId, opts) => {
|
|
255
|
+
const db = _dbFromCtx(rec);
|
|
256
|
+
const result = markViewed(db, recId);
|
|
257
|
+
if (opts.json) return console.log(JSON.stringify(result, null, 2));
|
|
258
|
+
console.log(
|
|
259
|
+
result.marked ? "Marked as viewed." : `Failed: ${result.reason}`,
|
|
260
|
+
);
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
rec
|
|
264
|
+
.command("feedback <rec-id> <value>")
|
|
265
|
+
.description("Provide feedback (like / dislike / later)")
|
|
266
|
+
.option("--json", "JSON output")
|
|
267
|
+
.action((recId, value, opts) => {
|
|
268
|
+
const db = _dbFromCtx(rec);
|
|
269
|
+
const result = provideFeedback(db, recId, value);
|
|
270
|
+
if (opts.json) return console.log(JSON.stringify(result, null, 2));
|
|
271
|
+
console.log(
|
|
272
|
+
result.recorded ? "Feedback recorded." : `Failed: ${result.reason}`,
|
|
273
|
+
);
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
rec
|
|
277
|
+
.command("dismiss <rec-id>")
|
|
278
|
+
.description("Dismiss a recommendation")
|
|
279
|
+
.option("--json", "JSON output")
|
|
280
|
+
.action((recId, opts) => {
|
|
281
|
+
const db = _dbFromCtx(rec);
|
|
282
|
+
const result = dismissRecommendation(db, recId);
|
|
283
|
+
if (opts.json) return console.log(JSON.stringify(result, null, 2));
|
|
284
|
+
console.log(result.dismissed ? "Dismissed." : `Failed: ${result.reason}`);
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
/* ── Stats & Insights ────────────────────────────── */
|
|
288
|
+
|
|
289
|
+
rec
|
|
290
|
+
.command("stats <user-id>")
|
|
291
|
+
.description("Recommendation statistics")
|
|
292
|
+
.option("--json", "JSON output")
|
|
293
|
+
.action((userId, opts) => {
|
|
294
|
+
const db = _dbFromCtx(rec);
|
|
295
|
+
const stats = getRecommendationStats(db, userId);
|
|
296
|
+
if (opts.json) return console.log(JSON.stringify(stats, null, 2));
|
|
297
|
+
console.log(`Total: ${stats.total}`);
|
|
298
|
+
console.log(`Pending: ${stats.pending}`);
|
|
299
|
+
console.log(`Viewed: ${stats.viewed}`);
|
|
300
|
+
console.log(`Dismissed: ${stats.dismissed}`);
|
|
301
|
+
console.log(
|
|
302
|
+
`Feedback: ${stats.feedbackCount} (${(stats.feedbackRate * 100).toFixed(1)}%)`,
|
|
303
|
+
);
|
|
304
|
+
console.log(`Avg score: ${stats.avgScore}`);
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
rec
|
|
308
|
+
.command("top-interests <user-id>")
|
|
309
|
+
.description("Show top interests from profile")
|
|
310
|
+
.option("--limit <n>", "Max results", parseInt)
|
|
311
|
+
.option("--json", "JSON output")
|
|
312
|
+
.action((userId, opts) => {
|
|
313
|
+
const db = _dbFromCtx(rec);
|
|
314
|
+
const interests = getTopInterests(db, userId, { limit: opts.limit });
|
|
315
|
+
if (opts.json) return console.log(JSON.stringify(interests, null, 2));
|
|
316
|
+
if (interests.length === 0) return console.log("No interests found.");
|
|
317
|
+
for (const i of interests)
|
|
318
|
+
console.log(` ${i.topic.padEnd(20)} ${i.weight}`);
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
rec
|
|
322
|
+
.command("suggest <user-id>")
|
|
323
|
+
.description("Suggest profile adjustments from feedback patterns")
|
|
324
|
+
.option("--json", "JSON output")
|
|
325
|
+
.action((userId, opts) => {
|
|
326
|
+
const db = _dbFromCtx(rec);
|
|
327
|
+
const suggestions = suggestTopics(db, userId);
|
|
328
|
+
if (opts.json) return console.log(JSON.stringify(suggestions, null, 2));
|
|
329
|
+
if (suggestions.length === 0)
|
|
330
|
+
return console.log("No suggestions — need more feedback.");
|
|
331
|
+
for (const s of suggestions)
|
|
332
|
+
console.log(` ${s.action.padEnd(8)} ${s.topic} by ${s.amount}`);
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
program.addCommand(rec);
|
|
336
|
+
}
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reputation commands
|
|
3
|
+
* chainlesschain reputation observe|score|list|decay-preview|anomalies|optimize|status|analytics|apply
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import chalk from "chalk";
|
|
7
|
+
import { logger } from "../lib/logger.js";
|
|
8
|
+
import { bootstrap, shutdown } from "../runtime/bootstrap.js";
|
|
9
|
+
import {
|
|
10
|
+
ensureReputationTables,
|
|
11
|
+
addObservation,
|
|
12
|
+
computeScore,
|
|
13
|
+
listScores,
|
|
14
|
+
detectAnomalies,
|
|
15
|
+
startOptimization,
|
|
16
|
+
getOptimizationStatus,
|
|
17
|
+
getAnalytics,
|
|
18
|
+
listOptimizationRuns,
|
|
19
|
+
applyOptimizedParams,
|
|
20
|
+
OPTIMIZATION_OBJECTIVES,
|
|
21
|
+
} from "../lib/reputation-optimizer.js";
|
|
22
|
+
|
|
23
|
+
function _dbFromCtx(ctx) {
|
|
24
|
+
if (!ctx.db) {
|
|
25
|
+
logger.error("Database not available");
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
const db = ctx.db.getDatabase();
|
|
29
|
+
ensureReputationTables(db);
|
|
30
|
+
return db;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function registerReputationCommand(program) {
|
|
34
|
+
const rep = program
|
|
35
|
+
.command("reputation")
|
|
36
|
+
.alias("rep")
|
|
37
|
+
.description(
|
|
38
|
+
"Reputation optimizer — observations, decay, anomaly detection, Bayesian optimization",
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
rep
|
|
42
|
+
.command("observe <did> <score>")
|
|
43
|
+
.description("Record a reputation observation (score in [0,1])")
|
|
44
|
+
.option(
|
|
45
|
+
"-k, --kind <kind>",
|
|
46
|
+
"Observation kind (generic|task|review|vote)",
|
|
47
|
+
"generic",
|
|
48
|
+
)
|
|
49
|
+
.option("-w, --weight <n>", "Observation weight", parseFloat, 1)
|
|
50
|
+
.option("--json", "Output as JSON")
|
|
51
|
+
.action(async (did, score, options) => {
|
|
52
|
+
try {
|
|
53
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
54
|
+
const db = _dbFromCtx(ctx);
|
|
55
|
+
const obs = addObservation(db, did, Number(score), {
|
|
56
|
+
kind: options.kind,
|
|
57
|
+
weight: options.weight,
|
|
58
|
+
});
|
|
59
|
+
if (options.json) {
|
|
60
|
+
console.log(JSON.stringify(obs, null, 2));
|
|
61
|
+
} else {
|
|
62
|
+
logger.success(`Observation recorded`);
|
|
63
|
+
logger.log(` ${chalk.bold("DID:")} ${chalk.cyan(obs.did)}`);
|
|
64
|
+
logger.log(` ${chalk.bold("Score:")} ${obs.score}`);
|
|
65
|
+
logger.log(` ${chalk.bold("Kind:")} ${obs.kind}`);
|
|
66
|
+
}
|
|
67
|
+
await shutdown();
|
|
68
|
+
} catch (err) {
|
|
69
|
+
logger.error(`Failed: ${err.message}`);
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
rep
|
|
75
|
+
.command("score <did>")
|
|
76
|
+
.description("Compute aggregated reputation score for a DID")
|
|
77
|
+
.option(
|
|
78
|
+
"-d, --decay <model>",
|
|
79
|
+
"Decay model (none|exponential|linear|step)",
|
|
80
|
+
"none",
|
|
81
|
+
)
|
|
82
|
+
.option("--lambda <n>", "Exponential decay lambda", parseFloat)
|
|
83
|
+
.option("--alpha <n>", "Linear decay alpha", parseFloat)
|
|
84
|
+
.option("--json", "Output as JSON")
|
|
85
|
+
.action(async (did, options) => {
|
|
86
|
+
try {
|
|
87
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
88
|
+
_dbFromCtx(ctx);
|
|
89
|
+
const result = computeScore(did, {
|
|
90
|
+
decay: options.decay,
|
|
91
|
+
lambda: options.lambda,
|
|
92
|
+
alpha: options.alpha,
|
|
93
|
+
});
|
|
94
|
+
if (options.json) {
|
|
95
|
+
console.log(JSON.stringify(result, null, 2));
|
|
96
|
+
} else {
|
|
97
|
+
logger.log(
|
|
98
|
+
` ${chalk.bold("DID:")} ${chalk.cyan(result.did)}`,
|
|
99
|
+
);
|
|
100
|
+
logger.log(` ${chalk.bold("Score:")} ${result.score}`);
|
|
101
|
+
logger.log(` ${chalk.bold("Observations:")} ${result.observations}`);
|
|
102
|
+
logger.log(` ${chalk.bold("Decay:")} ${result.decay}`);
|
|
103
|
+
}
|
|
104
|
+
await shutdown();
|
|
105
|
+
} catch (err) {
|
|
106
|
+
logger.error(`Failed: ${err.message}`);
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
rep
|
|
112
|
+
.command("list")
|
|
113
|
+
.description("List all tracked DIDs with computed scores")
|
|
114
|
+
.option("-d, --decay <model>", "Decay model", "none")
|
|
115
|
+
.option("--limit <n>", "Maximum entries", parseInt, 50)
|
|
116
|
+
.option("--json", "Output as JSON")
|
|
117
|
+
.action(async (options) => {
|
|
118
|
+
try {
|
|
119
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
120
|
+
_dbFromCtx(ctx);
|
|
121
|
+
const rows = listScores({
|
|
122
|
+
decay: options.decay,
|
|
123
|
+
limit: options.limit,
|
|
124
|
+
});
|
|
125
|
+
if (options.json) {
|
|
126
|
+
console.log(JSON.stringify(rows, null, 2));
|
|
127
|
+
} else if (rows.length === 0) {
|
|
128
|
+
logger.info("No reputation data recorded.");
|
|
129
|
+
} else {
|
|
130
|
+
for (const r of rows) {
|
|
131
|
+
logger.log(
|
|
132
|
+
` ${chalk.cyan(r.did.slice(0, 24).padEnd(24))} score=${r.score} obs=${r.observations}`,
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
await shutdown();
|
|
137
|
+
} catch (err) {
|
|
138
|
+
logger.error(`Failed: ${err.message}`);
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
rep
|
|
144
|
+
.command("anomalies")
|
|
145
|
+
.description("Detect anomalies in current reputation scores")
|
|
146
|
+
.option(
|
|
147
|
+
"-m, --method <method>",
|
|
148
|
+
"Detection method (z_score|iqr)",
|
|
149
|
+
"z_score",
|
|
150
|
+
)
|
|
151
|
+
.option("-t, --threshold <n>", "Detection threshold", parseFloat)
|
|
152
|
+
.option(
|
|
153
|
+
"-d, --decay <model>",
|
|
154
|
+
"Decay model applied before detection",
|
|
155
|
+
"none",
|
|
156
|
+
)
|
|
157
|
+
.option("--json", "Output as JSON")
|
|
158
|
+
.action(async (options) => {
|
|
159
|
+
try {
|
|
160
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
161
|
+
_dbFromCtx(ctx);
|
|
162
|
+
const result = detectAnomalies({
|
|
163
|
+
method: options.method,
|
|
164
|
+
threshold: options.threshold,
|
|
165
|
+
decay: options.decay,
|
|
166
|
+
});
|
|
167
|
+
if (options.json) {
|
|
168
|
+
console.log(JSON.stringify(result, null, 2));
|
|
169
|
+
} else {
|
|
170
|
+
logger.log(` ${chalk.bold("Method:")} ${result.method}`);
|
|
171
|
+
logger.log(` ${chalk.bold("Samples:")} ${result.totalSamples}`);
|
|
172
|
+
if (result.message) {
|
|
173
|
+
logger.info(` ${result.message}`);
|
|
174
|
+
} else {
|
|
175
|
+
logger.log(` ${chalk.bold("Summary:")} ${result.summary}`);
|
|
176
|
+
for (const a of result.anomalies) {
|
|
177
|
+
logger.log(
|
|
178
|
+
` ${chalk.yellow(a.did.slice(0, 24))} score=${a.score} ${a.reason}`,
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
await shutdown();
|
|
184
|
+
} catch (err) {
|
|
185
|
+
logger.error(`Failed: ${err.message}`);
|
|
186
|
+
process.exit(1);
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
rep
|
|
191
|
+
.command("optimize")
|
|
192
|
+
.description("Run parameter optimization over the reputation algorithm")
|
|
193
|
+
.option(
|
|
194
|
+
"-o, --objective <name>",
|
|
195
|
+
"Objective (accuracy|fairness|resilience|convergence_speed)",
|
|
196
|
+
"accuracy",
|
|
197
|
+
)
|
|
198
|
+
.option("-i, --iterations <n>", "Iteration count", parseInt, 50)
|
|
199
|
+
.option("--json", "Output as JSON")
|
|
200
|
+
.action(async (options) => {
|
|
201
|
+
try {
|
|
202
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
203
|
+
const db = _dbFromCtx(ctx);
|
|
204
|
+
const run = startOptimization(db, {
|
|
205
|
+
objective: options.objective,
|
|
206
|
+
iterations: options.iterations,
|
|
207
|
+
});
|
|
208
|
+
if (options.json) {
|
|
209
|
+
console.log(JSON.stringify(run, null, 2));
|
|
210
|
+
} else {
|
|
211
|
+
logger.success(`Optimization ${run.status}`);
|
|
212
|
+
logger.log(
|
|
213
|
+
` ${chalk.bold("Run ID:")} ${chalk.cyan(run.runId)}`,
|
|
214
|
+
);
|
|
215
|
+
logger.log(` ${chalk.bold("Objective:")} ${run.objective}`);
|
|
216
|
+
logger.log(` ${chalk.bold("Iterations:")} ${run.iterations}`);
|
|
217
|
+
logger.log(` ${chalk.bold("Best score:")} ${run.bestScore}`);
|
|
218
|
+
logger.log(
|
|
219
|
+
` ${chalk.bold("Best params:")} ${JSON.stringify(run.bestParams)}`,
|
|
220
|
+
);
|
|
221
|
+
for (const rec of run.analytics.recommendations) {
|
|
222
|
+
logger.log(` ${chalk.green("→")} ${rec}`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
await shutdown();
|
|
226
|
+
} catch (err) {
|
|
227
|
+
logger.error(`Failed: ${err.message}`);
|
|
228
|
+
process.exit(1);
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
rep
|
|
233
|
+
.command("status <run-id>")
|
|
234
|
+
.description("Show optimization run status")
|
|
235
|
+
.option("--json", "Output as JSON")
|
|
236
|
+
.action(async (runId, options) => {
|
|
237
|
+
try {
|
|
238
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
239
|
+
_dbFromCtx(ctx);
|
|
240
|
+
const run = getOptimizationStatus(runId);
|
|
241
|
+
if (options.json) {
|
|
242
|
+
console.log(JSON.stringify(run, null, 2));
|
|
243
|
+
} else {
|
|
244
|
+
logger.log(
|
|
245
|
+
` ${chalk.bold("Run ID:")} ${chalk.cyan(run.runId)}`,
|
|
246
|
+
);
|
|
247
|
+
logger.log(` ${chalk.bold("Status:")} ${run.status}`);
|
|
248
|
+
logger.log(` ${chalk.bold("Objective:")} ${run.objective}`);
|
|
249
|
+
logger.log(` ${chalk.bold("Best score:")} ${run.bestScore}`);
|
|
250
|
+
logger.log(
|
|
251
|
+
` ${chalk.bold("Best params:")} ${JSON.stringify(run.bestParams)}`,
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
await shutdown();
|
|
255
|
+
} catch (err) {
|
|
256
|
+
logger.error(`Failed: ${err.message}`);
|
|
257
|
+
process.exit(1);
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
rep
|
|
262
|
+
.command("analytics <run-id>")
|
|
263
|
+
.description("Show analytics for an optimization run")
|
|
264
|
+
.option("--json", "Output as JSON")
|
|
265
|
+
.action(async (runId, options) => {
|
|
266
|
+
try {
|
|
267
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
268
|
+
_dbFromCtx(ctx);
|
|
269
|
+
const a = getAnalytics(runId);
|
|
270
|
+
if (options.json) {
|
|
271
|
+
console.log(JSON.stringify(a, null, 2));
|
|
272
|
+
} else {
|
|
273
|
+
logger.log(` ${chalk.bold("Run ID:")} ${chalk.cyan(a.runId)}`);
|
|
274
|
+
logger.log(` ${chalk.bold("Distribution:")}`);
|
|
275
|
+
for (const b of a.reputationDistribution.buckets) {
|
|
276
|
+
logger.log(` ${b.label.padEnd(15)} ${b.count}`);
|
|
277
|
+
}
|
|
278
|
+
logger.log(
|
|
279
|
+
` ${chalk.bold("Anomalies:")} ${a.anomalies.summary || a.anomalies.message || ""}`,
|
|
280
|
+
);
|
|
281
|
+
for (const r of a.recommendations) {
|
|
282
|
+
logger.log(` ${chalk.green("→")} ${r}`);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
await shutdown();
|
|
286
|
+
} catch (err) {
|
|
287
|
+
logger.error(`Failed: ${err.message}`);
|
|
288
|
+
process.exit(1);
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
rep
|
|
293
|
+
.command("runs")
|
|
294
|
+
.description("List optimization run history")
|
|
295
|
+
.option("--limit <n>", "Maximum entries", parseInt, 10)
|
|
296
|
+
.option("--json", "Output as JSON")
|
|
297
|
+
.action(async (options) => {
|
|
298
|
+
try {
|
|
299
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
300
|
+
_dbFromCtx(ctx);
|
|
301
|
+
const rows = listOptimizationRuns({ limit: options.limit });
|
|
302
|
+
if (options.json) {
|
|
303
|
+
console.log(JSON.stringify(rows, null, 2));
|
|
304
|
+
} else if (rows.length === 0) {
|
|
305
|
+
logger.info("No optimization runs recorded.");
|
|
306
|
+
} else {
|
|
307
|
+
for (const r of rows) {
|
|
308
|
+
logger.log(
|
|
309
|
+
` ${chalk.cyan(r.runId.slice(0, 8))} ${r.objective.padEnd(20)} iters=${r.iterations} best=${r.bestScore} [${r.status}]`,
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
await shutdown();
|
|
314
|
+
} catch (err) {
|
|
315
|
+
logger.error(`Failed: ${err.message}`);
|
|
316
|
+
process.exit(1);
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
rep
|
|
321
|
+
.command("apply <run-id>")
|
|
322
|
+
.description("Mark an optimization run as applied")
|
|
323
|
+
.action(async (runId) => {
|
|
324
|
+
try {
|
|
325
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
326
|
+
_dbFromCtx(ctx);
|
|
327
|
+
const r = applyOptimizedParams(runId);
|
|
328
|
+
logger.success(`Applied run ${runId.slice(0, 8)}`);
|
|
329
|
+
logger.log(` ${chalk.bold("Params:")} ${JSON.stringify(r.params)}`);
|
|
330
|
+
await shutdown();
|
|
331
|
+
} catch (err) {
|
|
332
|
+
logger.error(`Failed: ${err.message}`);
|
|
333
|
+
process.exit(1);
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
rep
|
|
338
|
+
.command("objectives")
|
|
339
|
+
.description("List supported optimization objectives")
|
|
340
|
+
.option("--json", "Output as JSON")
|
|
341
|
+
.action((options) => {
|
|
342
|
+
const values = Object.values(OPTIMIZATION_OBJECTIVES);
|
|
343
|
+
if (options.json) {
|
|
344
|
+
console.log(JSON.stringify(values, null, 2));
|
|
345
|
+
} else {
|
|
346
|
+
for (const v of values) logger.log(` ${chalk.cyan(v)}`);
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
}
|