chainlesschain 0.47.9 → 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 +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/kg.js +371 -0
- package/src/commands/marketplace.js +326 -0
- package/src/commands/mcp.js +97 -18
- 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 +112 -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/knowledge-graph.js +530 -0
- package/src/lib/mcp-client.js +3 -0
- package/src/lib/multimodal.js +698 -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,371 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Knowledge Graph commands (Phase 94)
|
|
3
|
+
* chainlesschain kg entity-types|add|list|show|remove|add-relation|relations|
|
|
4
|
+
* query|reason|stats|export|import
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import fs from "fs";
|
|
8
|
+
import chalk from "chalk";
|
|
9
|
+
import { logger } from "../lib/logger.js";
|
|
10
|
+
import { bootstrap, shutdown } from "../runtime/bootstrap.js";
|
|
11
|
+
import {
|
|
12
|
+
ensureKnowledgeGraphTables,
|
|
13
|
+
listEntityTypes,
|
|
14
|
+
addEntity,
|
|
15
|
+
getEntity,
|
|
16
|
+
listEntities,
|
|
17
|
+
removeEntity,
|
|
18
|
+
addRelation,
|
|
19
|
+
listRelations,
|
|
20
|
+
reason,
|
|
21
|
+
getStats,
|
|
22
|
+
exportGraph,
|
|
23
|
+
importGraph,
|
|
24
|
+
} from "../lib/knowledge-graph.js";
|
|
25
|
+
|
|
26
|
+
function _dbFromCtx(ctx) {
|
|
27
|
+
if (!ctx.db) {
|
|
28
|
+
logger.error("Database not available");
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
const db = ctx.db.getDatabase();
|
|
32
|
+
ensureKnowledgeGraphTables(db);
|
|
33
|
+
return db;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function _printEntity(e) {
|
|
37
|
+
logger.log(` ${chalk.bold("ID:")} ${chalk.cyan(e.id)}`);
|
|
38
|
+
logger.log(` ${chalk.bold("Name:")} ${e.name}`);
|
|
39
|
+
logger.log(` ${chalk.bold("Type:")} ${chalk.yellow(e.type)}`);
|
|
40
|
+
if (e.tags && e.tags.length > 0) {
|
|
41
|
+
logger.log(` ${chalk.bold("Tags:")} ${e.tags.join(", ")}`);
|
|
42
|
+
}
|
|
43
|
+
if (e.properties) {
|
|
44
|
+
logger.log(
|
|
45
|
+
` ${chalk.bold("Properties:")} ${JSON.stringify(e.properties)}`,
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function registerKgCommand(program) {
|
|
51
|
+
const kg = program
|
|
52
|
+
.command("kg")
|
|
53
|
+
.description(
|
|
54
|
+
"Knowledge graph — entities, relations, multi-hop reasoning, stats",
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
kg.command("entity-types")
|
|
58
|
+
.description("List recommended entity types")
|
|
59
|
+
.option("--json", "Output as JSON")
|
|
60
|
+
.action((options) => {
|
|
61
|
+
const types = listEntityTypes();
|
|
62
|
+
if (options.json) {
|
|
63
|
+
console.log(JSON.stringify(types, null, 2));
|
|
64
|
+
} else {
|
|
65
|
+
for (const t of types) {
|
|
66
|
+
logger.log(` ${chalk.cyan(t.name.padEnd(14))} ${t.description}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
kg.command("add <name> <type>")
|
|
72
|
+
.description("Add an entity")
|
|
73
|
+
.option("-p, --properties <json>", "Properties JSON")
|
|
74
|
+
.option("-g, --tags <csv>", "Comma-separated tags")
|
|
75
|
+
.option("--json", "Output as JSON")
|
|
76
|
+
.action(async (name, type, options) => {
|
|
77
|
+
try {
|
|
78
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
79
|
+
const db = _dbFromCtx(ctx);
|
|
80
|
+
const properties = options.properties
|
|
81
|
+
? JSON.parse(options.properties)
|
|
82
|
+
: null;
|
|
83
|
+
const tags = options.tags
|
|
84
|
+
? options.tags
|
|
85
|
+
.split(",")
|
|
86
|
+
.map((s) => s.trim())
|
|
87
|
+
.filter(Boolean)
|
|
88
|
+
: null;
|
|
89
|
+
const entity = addEntity(db, { name, type, properties, tags });
|
|
90
|
+
if (options.json) {
|
|
91
|
+
console.log(JSON.stringify(entity, null, 2));
|
|
92
|
+
} else {
|
|
93
|
+
logger.success(`Entity added: ${name} [${type}]`);
|
|
94
|
+
_printEntity(entity);
|
|
95
|
+
}
|
|
96
|
+
await shutdown();
|
|
97
|
+
} catch (err) {
|
|
98
|
+
logger.error(`Failed: ${err.message}`);
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
kg.command("list")
|
|
104
|
+
.description("List entities")
|
|
105
|
+
.option("-t, --type <t>", "Filter by type")
|
|
106
|
+
.option("-n, --name <substr>", "Filter by name substring")
|
|
107
|
+
.option("-g, --tag <tag>", "Filter by tag")
|
|
108
|
+
.option("--limit <n>", "Maximum entries", parseInt, 50)
|
|
109
|
+
.option("--json", "Output as JSON")
|
|
110
|
+
.action(async (options) => {
|
|
111
|
+
try {
|
|
112
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
113
|
+
_dbFromCtx(ctx);
|
|
114
|
+
const rows = listEntities({
|
|
115
|
+
type: options.type,
|
|
116
|
+
name: options.name,
|
|
117
|
+
tag: options.tag,
|
|
118
|
+
limit: options.limit,
|
|
119
|
+
});
|
|
120
|
+
if (options.json) {
|
|
121
|
+
console.log(JSON.stringify(rows, null, 2));
|
|
122
|
+
} else if (rows.length === 0) {
|
|
123
|
+
logger.info("No entities.");
|
|
124
|
+
} else {
|
|
125
|
+
for (const e of rows) {
|
|
126
|
+
logger.log(
|
|
127
|
+
` ${chalk.cyan(e.id.slice(0, 8))} ${chalk.yellow(e.type.padEnd(14))} ${e.name}`,
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
await shutdown();
|
|
132
|
+
} catch (err) {
|
|
133
|
+
logger.error(`Failed: ${err.message}`);
|
|
134
|
+
process.exit(1);
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
kg.command("show <entity-id>")
|
|
139
|
+
.description("Show entity details")
|
|
140
|
+
.option("--json", "Output as JSON")
|
|
141
|
+
.action(async (entityId, options) => {
|
|
142
|
+
try {
|
|
143
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
144
|
+
_dbFromCtx(ctx);
|
|
145
|
+
const entity = getEntity(entityId);
|
|
146
|
+
if (!entity) {
|
|
147
|
+
logger.error(`Entity not found: ${entityId}`);
|
|
148
|
+
process.exit(1);
|
|
149
|
+
}
|
|
150
|
+
if (options.json) {
|
|
151
|
+
console.log(JSON.stringify(entity, null, 2));
|
|
152
|
+
} else {
|
|
153
|
+
_printEntity(entity);
|
|
154
|
+
}
|
|
155
|
+
await shutdown();
|
|
156
|
+
} catch (err) {
|
|
157
|
+
logger.error(`Failed: ${err.message}`);
|
|
158
|
+
process.exit(1);
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
kg.command("remove <entity-id>")
|
|
163
|
+
.description("Remove entity (cascades to relations)")
|
|
164
|
+
.action(async (entityId) => {
|
|
165
|
+
try {
|
|
166
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
167
|
+
const db = _dbFromCtx(ctx);
|
|
168
|
+
const ok = removeEntity(db, entityId);
|
|
169
|
+
if (ok) {
|
|
170
|
+
logger.success(`Entity removed: ${entityId}`);
|
|
171
|
+
} else {
|
|
172
|
+
logger.info(`No such entity: ${entityId}`);
|
|
173
|
+
}
|
|
174
|
+
await shutdown();
|
|
175
|
+
} catch (err) {
|
|
176
|
+
logger.error(`Failed: ${err.message}`);
|
|
177
|
+
process.exit(1);
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
kg.command("add-relation <source-id> <target-id> <relation-type>")
|
|
182
|
+
.description("Add a directed relation between two entities")
|
|
183
|
+
.option("-w, --weight <n>", "Relation weight", parseFloat, 1.0)
|
|
184
|
+
.option("-p, --properties <json>", "Properties JSON")
|
|
185
|
+
.option("--json", "Output as JSON")
|
|
186
|
+
.action(async (sourceId, targetId, relationType, options) => {
|
|
187
|
+
try {
|
|
188
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
189
|
+
const db = _dbFromCtx(ctx);
|
|
190
|
+
const properties = options.properties
|
|
191
|
+
? JSON.parse(options.properties)
|
|
192
|
+
: null;
|
|
193
|
+
const relation = addRelation(db, {
|
|
194
|
+
sourceId,
|
|
195
|
+
targetId,
|
|
196
|
+
relationType,
|
|
197
|
+
weight: options.weight,
|
|
198
|
+
properties,
|
|
199
|
+
});
|
|
200
|
+
if (options.json) {
|
|
201
|
+
console.log(JSON.stringify(relation, null, 2));
|
|
202
|
+
} else {
|
|
203
|
+
logger.success(
|
|
204
|
+
`Relation added: ${sourceId.slice(0, 8)} ─[${relationType}]─> ${targetId.slice(0, 8)}`,
|
|
205
|
+
);
|
|
206
|
+
logger.log(
|
|
207
|
+
` ${chalk.bold("ID:")} ${chalk.cyan(relation.id.slice(0, 8))}`,
|
|
208
|
+
);
|
|
209
|
+
logger.log(` ${chalk.bold("Weight:")} ${relation.weight}`);
|
|
210
|
+
}
|
|
211
|
+
await shutdown();
|
|
212
|
+
} catch (err) {
|
|
213
|
+
logger.error(`Failed: ${err.message}`);
|
|
214
|
+
process.exit(1);
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
kg.command("relations")
|
|
219
|
+
.description("List relations")
|
|
220
|
+
.option("-s, --source <id>", "Filter by source entity")
|
|
221
|
+
.option("-t, --target <id>", "Filter by target entity")
|
|
222
|
+
.option("-r, --type <type>", "Filter by relation type")
|
|
223
|
+
.option("--limit <n>", "Maximum entries", parseInt, 50)
|
|
224
|
+
.option("--json", "Output as JSON")
|
|
225
|
+
.action(async (options) => {
|
|
226
|
+
try {
|
|
227
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
228
|
+
_dbFromCtx(ctx);
|
|
229
|
+
const rows = listRelations({
|
|
230
|
+
sourceId: options.source,
|
|
231
|
+
targetId: options.target,
|
|
232
|
+
relationType: options.type,
|
|
233
|
+
limit: options.limit,
|
|
234
|
+
});
|
|
235
|
+
if (options.json) {
|
|
236
|
+
console.log(JSON.stringify(rows, null, 2));
|
|
237
|
+
} else if (rows.length === 0) {
|
|
238
|
+
logger.info("No relations.");
|
|
239
|
+
} else {
|
|
240
|
+
for (const r of rows) {
|
|
241
|
+
logger.log(
|
|
242
|
+
` ${chalk.cyan(r.id.slice(0, 8))} ${r.sourceId.slice(0, 8)} ─[${chalk.yellow(r.relationType)}:${r.weight}]─> ${r.targetId.slice(0, 8)}`,
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
await shutdown();
|
|
247
|
+
} catch (err) {
|
|
248
|
+
logger.error(`Failed: ${err.message}`);
|
|
249
|
+
process.exit(1);
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
kg.command("reason <start-id>")
|
|
254
|
+
.description("BFS multi-hop reasoning from a start entity")
|
|
255
|
+
.option("-d, --max-depth <n>", "Maximum hops", parseInt, 3)
|
|
256
|
+
.option("--direction <d>", "out | in | both", "out")
|
|
257
|
+
.option("-r, --relation-type <t>", "Filter edges by relation type")
|
|
258
|
+
.option("--include-start", "Include the start entity in result")
|
|
259
|
+
.option("--json", "Output as JSON")
|
|
260
|
+
.action(async (startId, options) => {
|
|
261
|
+
try {
|
|
262
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
263
|
+
_dbFromCtx(ctx);
|
|
264
|
+
const results = reason(startId, {
|
|
265
|
+
maxDepth: options.maxDepth,
|
|
266
|
+
direction: options.direction,
|
|
267
|
+
relationType: options.relationType,
|
|
268
|
+
includeStart: options.includeStart,
|
|
269
|
+
});
|
|
270
|
+
if (options.json) {
|
|
271
|
+
console.log(JSON.stringify(results, null, 2));
|
|
272
|
+
} else if (results.length === 0) {
|
|
273
|
+
logger.info("No reachable entities.");
|
|
274
|
+
} else {
|
|
275
|
+
for (const r of results) {
|
|
276
|
+
logger.log(
|
|
277
|
+
` [depth=${r.depth}] ${chalk.cyan(r.entity.id.slice(0, 8))} ${chalk.yellow(r.entity.type.padEnd(14))} ${r.entity.name}`,
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
await shutdown();
|
|
282
|
+
} catch (err) {
|
|
283
|
+
logger.error(`Failed: ${err.message}`);
|
|
284
|
+
process.exit(1);
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
kg.command("stats")
|
|
289
|
+
.description("Graph statistics — counts, type distribution, density")
|
|
290
|
+
.option("--json", "Output as JSON")
|
|
291
|
+
.action(async (options) => {
|
|
292
|
+
try {
|
|
293
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
294
|
+
_dbFromCtx(ctx);
|
|
295
|
+
const stats = getStats();
|
|
296
|
+
if (options.json) {
|
|
297
|
+
console.log(JSON.stringify(stats, null, 2));
|
|
298
|
+
} else {
|
|
299
|
+
logger.log(` ${chalk.bold("Entities:")} ${stats.entityCount}`);
|
|
300
|
+
logger.log(` ${chalk.bold("Relations:")} ${stats.relationCount}`);
|
|
301
|
+
logger.log(` ${chalk.bold("Avg Degree:")} ${stats.avgDegree}`);
|
|
302
|
+
logger.log(` ${chalk.bold("Density:")} ${stats.density}`);
|
|
303
|
+
if (Object.keys(stats.typeDistribution).length > 0) {
|
|
304
|
+
logger.log(chalk.bold(" Entity types:"));
|
|
305
|
+
for (const [t, n] of Object.entries(stats.typeDistribution)) {
|
|
306
|
+
logger.log(` ${chalk.yellow(t.padEnd(14))} ${n}`);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
if (Object.keys(stats.relationTypeDistribution).length > 0) {
|
|
310
|
+
logger.log(chalk.bold(" Relation types:"));
|
|
311
|
+
for (const [t, n] of Object.entries(
|
|
312
|
+
stats.relationTypeDistribution,
|
|
313
|
+
)) {
|
|
314
|
+
logger.log(` ${chalk.yellow(t.padEnd(14))} ${n}`);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
await shutdown();
|
|
319
|
+
} catch (err) {
|
|
320
|
+
logger.error(`Failed: ${err.message}`);
|
|
321
|
+
process.exit(1);
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
kg.command("export [output-file]")
|
|
326
|
+
.description("Export entire graph as JSON (stdout if no file)")
|
|
327
|
+
.action(async (outputFile) => {
|
|
328
|
+
try {
|
|
329
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
330
|
+
_dbFromCtx(ctx);
|
|
331
|
+
const data = exportGraph();
|
|
332
|
+
const json = JSON.stringify(data, null, 2);
|
|
333
|
+
if (outputFile) {
|
|
334
|
+
fs.writeFileSync(outputFile, json, "utf-8");
|
|
335
|
+
logger.success(
|
|
336
|
+
`Exported ${data.entities.length} entities + ${data.relations.length} relations → ${outputFile}`,
|
|
337
|
+
);
|
|
338
|
+
} else {
|
|
339
|
+
console.log(json);
|
|
340
|
+
}
|
|
341
|
+
await shutdown();
|
|
342
|
+
} catch (err) {
|
|
343
|
+
logger.error(`Failed: ${err.message}`);
|
|
344
|
+
process.exit(1);
|
|
345
|
+
}
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
kg.command("import <input-file>")
|
|
349
|
+
.description("Import graph from JSON file ({entities[], relations[]})")
|
|
350
|
+
.option("--json", "Output result as JSON")
|
|
351
|
+
.action(async (inputFile, options) => {
|
|
352
|
+
try {
|
|
353
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
354
|
+
const db = _dbFromCtx(ctx);
|
|
355
|
+
const raw = fs.readFileSync(inputFile, "utf-8");
|
|
356
|
+
const data = JSON.parse(raw);
|
|
357
|
+
const result = importGraph(db, data);
|
|
358
|
+
if (options.json) {
|
|
359
|
+
console.log(JSON.stringify(result, null, 2));
|
|
360
|
+
} else {
|
|
361
|
+
logger.success(
|
|
362
|
+
`Imported ${result.importedEntities} entities + ${result.importedRelations} relations (skipped ${result.skippedRelations})`,
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
await shutdown();
|
|
366
|
+
} catch (err) {
|
|
367
|
+
logger.error(`Failed: ${err.message}`);
|
|
368
|
+
process.exit(1);
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
}
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill Marketplace commands (Phase 65)
|
|
3
|
+
* chainlesschain marketplace status-types|invocation-statuses|publish|list|
|
|
4
|
+
* show|status|record|invocations|stats
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import chalk from "chalk";
|
|
8
|
+
import { logger } from "../lib/logger.js";
|
|
9
|
+
import { bootstrap, shutdown } from "../runtime/bootstrap.js";
|
|
10
|
+
import {
|
|
11
|
+
ensureMarketplaceTables,
|
|
12
|
+
listServiceStatus,
|
|
13
|
+
listInvocationStatus,
|
|
14
|
+
publishService,
|
|
15
|
+
getService,
|
|
16
|
+
listServices,
|
|
17
|
+
updateServiceStatus,
|
|
18
|
+
recordInvocation,
|
|
19
|
+
listInvocations,
|
|
20
|
+
getInvocationStats,
|
|
21
|
+
} from "../lib/skill-marketplace.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
|
+
ensureMarketplaceTables(db);
|
|
30
|
+
return db;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function _printService(s) {
|
|
34
|
+
logger.log(
|
|
35
|
+
` ${chalk.bold("ID:")} ${chalk.cyan(s.id.slice(0, 8))}`,
|
|
36
|
+
);
|
|
37
|
+
logger.log(` ${chalk.bold("Name:")} ${s.name} @ ${s.version}`);
|
|
38
|
+
logger.log(` ${chalk.bold("Status:")} ${s.status}`);
|
|
39
|
+
if (s.description) {
|
|
40
|
+
logger.log(` ${chalk.bold("Description:")} ${s.description}`);
|
|
41
|
+
}
|
|
42
|
+
if (s.endpoint) {
|
|
43
|
+
logger.log(` ${chalk.bold("Endpoint:")} ${s.endpoint}`);
|
|
44
|
+
}
|
|
45
|
+
if (s.owner) {
|
|
46
|
+
logger.log(` ${chalk.bold("Owner:")} ${s.owner}`);
|
|
47
|
+
}
|
|
48
|
+
logger.log(` ${chalk.bold("Invocations:")} ${s.invocationCount}`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function registerMarketplaceCommand(program) {
|
|
52
|
+
const mp = program
|
|
53
|
+
.command("marketplace")
|
|
54
|
+
.description(
|
|
55
|
+
"Skill marketplace — publish skill services, record invocations, view stats",
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
mp.command("status-types")
|
|
59
|
+
.description("List known service statuses")
|
|
60
|
+
.option("--json", "Output as JSON")
|
|
61
|
+
.action((options) => {
|
|
62
|
+
const statuses = listServiceStatus();
|
|
63
|
+
if (options.json) {
|
|
64
|
+
console.log(JSON.stringify(statuses, null, 2));
|
|
65
|
+
} else {
|
|
66
|
+
for (const s of statuses) logger.log(` ${chalk.cyan(s)}`);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
mp.command("invocation-statuses")
|
|
71
|
+
.description("List known invocation statuses")
|
|
72
|
+
.option("--json", "Output as JSON")
|
|
73
|
+
.action((options) => {
|
|
74
|
+
const statuses = listInvocationStatus();
|
|
75
|
+
if (options.json) {
|
|
76
|
+
console.log(JSON.stringify(statuses, null, 2));
|
|
77
|
+
} else {
|
|
78
|
+
for (const s of statuses) logger.log(` ${chalk.cyan(s)}`);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
mp.command("publish <name>")
|
|
83
|
+
.description("Publish a skill service")
|
|
84
|
+
.option("-v, --version <v>", "Version", "1.0.0")
|
|
85
|
+
.option("-d, --description <text>", "Description", "")
|
|
86
|
+
.option("-e, --endpoint <url>", "Invocation endpoint")
|
|
87
|
+
.option("-o, --owner <did>", "Service owner (DID)")
|
|
88
|
+
.option("-p, --pricing <json>", "Pricing info (JSON)")
|
|
89
|
+
.option(
|
|
90
|
+
"-s, --status <s>",
|
|
91
|
+
"Initial status (draft|published|deprecated|suspended)",
|
|
92
|
+
"published",
|
|
93
|
+
)
|
|
94
|
+
.option("--json", "Output as JSON")
|
|
95
|
+
.action(async (name, options) => {
|
|
96
|
+
try {
|
|
97
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
98
|
+
const db = _dbFromCtx(ctx);
|
|
99
|
+
const pricing = options.pricing ? JSON.parse(options.pricing) : null;
|
|
100
|
+
const service = publishService(db, {
|
|
101
|
+
name,
|
|
102
|
+
version: options.version,
|
|
103
|
+
description: options.description,
|
|
104
|
+
endpoint: options.endpoint,
|
|
105
|
+
owner: options.owner,
|
|
106
|
+
pricing,
|
|
107
|
+
status: options.status,
|
|
108
|
+
});
|
|
109
|
+
if (options.json) {
|
|
110
|
+
console.log(JSON.stringify(service, null, 2));
|
|
111
|
+
} else {
|
|
112
|
+
logger.success("Service published");
|
|
113
|
+
_printService(service);
|
|
114
|
+
}
|
|
115
|
+
await shutdown();
|
|
116
|
+
} catch (err) {
|
|
117
|
+
logger.error(`Failed: ${err.message}`);
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
mp.command("list")
|
|
123
|
+
.description("List skill services")
|
|
124
|
+
.option(
|
|
125
|
+
"-s, --status <s>",
|
|
126
|
+
"Filter by status (draft|published|deprecated|suspended)",
|
|
127
|
+
)
|
|
128
|
+
.option("-o, --owner <did>", "Filter by owner DID")
|
|
129
|
+
.option("-n, --name <substring>", "Name substring filter")
|
|
130
|
+
.option("--limit <n>", "Maximum entries", parseInt, 50)
|
|
131
|
+
.option("--json", "Output as JSON")
|
|
132
|
+
.action(async (options) => {
|
|
133
|
+
try {
|
|
134
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
135
|
+
_dbFromCtx(ctx);
|
|
136
|
+
const rows = listServices({
|
|
137
|
+
status: options.status,
|
|
138
|
+
owner: options.owner,
|
|
139
|
+
name: options.name,
|
|
140
|
+
limit: options.limit,
|
|
141
|
+
});
|
|
142
|
+
if (options.json) {
|
|
143
|
+
console.log(JSON.stringify(rows, null, 2));
|
|
144
|
+
} else if (rows.length === 0) {
|
|
145
|
+
logger.info("No services.");
|
|
146
|
+
} else {
|
|
147
|
+
for (const s of rows) {
|
|
148
|
+
logger.log(
|
|
149
|
+
` ${chalk.cyan(s.id.slice(0, 8))} [${s.status.padEnd(10)}] ${chalk.bold(s.name.padEnd(22))} v${s.version.padEnd(8)} invocations=${s.invocationCount}`,
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
await shutdown();
|
|
154
|
+
} catch (err) {
|
|
155
|
+
logger.error(`Failed: ${err.message}`);
|
|
156
|
+
process.exit(1);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
mp.command("show <service-id>")
|
|
161
|
+
.description("Show full details of a skill service")
|
|
162
|
+
.option("--json", "Output as JSON")
|
|
163
|
+
.action(async (serviceId, options) => {
|
|
164
|
+
try {
|
|
165
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
166
|
+
_dbFromCtx(ctx);
|
|
167
|
+
const service = getService(serviceId);
|
|
168
|
+
if (!service) {
|
|
169
|
+
logger.error(`Service not found: ${serviceId}`);
|
|
170
|
+
process.exit(1);
|
|
171
|
+
}
|
|
172
|
+
if (options.json) {
|
|
173
|
+
console.log(JSON.stringify(service, null, 2));
|
|
174
|
+
} else {
|
|
175
|
+
_printService(service);
|
|
176
|
+
}
|
|
177
|
+
await shutdown();
|
|
178
|
+
} catch (err) {
|
|
179
|
+
logger.error(`Failed: ${err.message}`);
|
|
180
|
+
process.exit(1);
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
mp.command("status <service-id> <new-status>")
|
|
185
|
+
.description(
|
|
186
|
+
"Transition service status (draft|published|deprecated|suspended)",
|
|
187
|
+
)
|
|
188
|
+
.option("--json", "Output as JSON")
|
|
189
|
+
.action(async (serviceId, newStatus, options) => {
|
|
190
|
+
try {
|
|
191
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
192
|
+
const db = _dbFromCtx(ctx);
|
|
193
|
+
const service = updateServiceStatus(db, serviceId, newStatus);
|
|
194
|
+
if (options.json) {
|
|
195
|
+
console.log(JSON.stringify(service, null, 2));
|
|
196
|
+
} else {
|
|
197
|
+
logger.success(`Status updated → ${service.status}`);
|
|
198
|
+
}
|
|
199
|
+
await shutdown();
|
|
200
|
+
} catch (err) {
|
|
201
|
+
logger.error(`Failed: ${err.message}`);
|
|
202
|
+
process.exit(1);
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
mp.command("record <service-id>")
|
|
207
|
+
.description("Record a skill invocation against a published service")
|
|
208
|
+
.option("-c, --caller <did>", "Caller DID")
|
|
209
|
+
.option("-i, --input <json>", "Invocation input (JSON)")
|
|
210
|
+
.option("-o, --output <json>", "Invocation output (JSON)")
|
|
211
|
+
.option(
|
|
212
|
+
"-s, --status <s>",
|
|
213
|
+
"Status (pending|running|success|failed|timeout)",
|
|
214
|
+
"success",
|
|
215
|
+
)
|
|
216
|
+
.option("-d, --duration-ms <n>", "Duration in ms", parseInt)
|
|
217
|
+
.option("-e, --error <text>", "Error message when failed")
|
|
218
|
+
.option("--json", "Output as JSON")
|
|
219
|
+
.action(async (serviceId, options) => {
|
|
220
|
+
try {
|
|
221
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
222
|
+
const db = _dbFromCtx(ctx);
|
|
223
|
+
const input = options.input ? JSON.parse(options.input) : null;
|
|
224
|
+
const output = options.output ? JSON.parse(options.output) : null;
|
|
225
|
+
const inv = recordInvocation(db, {
|
|
226
|
+
serviceId,
|
|
227
|
+
callerId: options.caller,
|
|
228
|
+
input,
|
|
229
|
+
output,
|
|
230
|
+
status: options.status,
|
|
231
|
+
durationMs: options.durationMs,
|
|
232
|
+
error: options.error,
|
|
233
|
+
});
|
|
234
|
+
if (options.json) {
|
|
235
|
+
console.log(JSON.stringify(inv, null, 2));
|
|
236
|
+
} else {
|
|
237
|
+
logger.success("Invocation recorded");
|
|
238
|
+
logger.log(
|
|
239
|
+
` ${chalk.bold("ID:")} ${chalk.cyan(inv.id.slice(0, 8))}`,
|
|
240
|
+
);
|
|
241
|
+
logger.log(
|
|
242
|
+
` ${chalk.bold("Service:")} ${inv.serviceId.slice(0, 8)}`,
|
|
243
|
+
);
|
|
244
|
+
logger.log(` ${chalk.bold("Status:")} ${inv.status}`);
|
|
245
|
+
if (inv.durationMs != null) {
|
|
246
|
+
logger.log(` ${chalk.bold("Duration:")} ${inv.durationMs}ms`);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
await shutdown();
|
|
250
|
+
} catch (err) {
|
|
251
|
+
logger.error(`Failed: ${err.message}`);
|
|
252
|
+
process.exit(1);
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
mp.command("invocations")
|
|
257
|
+
.description("List skill invocations")
|
|
258
|
+
.option("-s, --service <id>", "Filter by service")
|
|
259
|
+
.option("-c, --caller <did>", "Filter by caller")
|
|
260
|
+
.option("-S, --status <s>", "Filter by status")
|
|
261
|
+
.option("--limit <n>", "Maximum entries", parseInt, 50)
|
|
262
|
+
.option("--json", "Output as JSON")
|
|
263
|
+
.action(async (options) => {
|
|
264
|
+
try {
|
|
265
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
266
|
+
_dbFromCtx(ctx);
|
|
267
|
+
const rows = listInvocations({
|
|
268
|
+
serviceId: options.service,
|
|
269
|
+
callerId: options.caller,
|
|
270
|
+
status: options.status,
|
|
271
|
+
limit: options.limit,
|
|
272
|
+
});
|
|
273
|
+
if (options.json) {
|
|
274
|
+
console.log(JSON.stringify(rows, null, 2));
|
|
275
|
+
} else if (rows.length === 0) {
|
|
276
|
+
logger.info("No invocations.");
|
|
277
|
+
} else {
|
|
278
|
+
for (const i of rows) {
|
|
279
|
+
const duration = i.durationMs != null ? `${i.durationMs}ms` : "-";
|
|
280
|
+
logger.log(
|
|
281
|
+
` ${chalk.cyan(i.id.slice(0, 8))} svc=${i.serviceId.slice(0, 8)} [${i.status.padEnd(8)}] ${duration.padStart(7)} caller=${i.callerId || "anon"}`,
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
await shutdown();
|
|
286
|
+
} catch (err) {
|
|
287
|
+
logger.error(`Failed: ${err.message}`);
|
|
288
|
+
process.exit(1);
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
mp.command("stats")
|
|
293
|
+
.description("Aggregate invocation stats (optionally scoped to a service)")
|
|
294
|
+
.option("-s, --service <id>", "Scope to a single service")
|
|
295
|
+
.option("--json", "Output as JSON")
|
|
296
|
+
.action(async (options) => {
|
|
297
|
+
try {
|
|
298
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
299
|
+
_dbFromCtx(ctx);
|
|
300
|
+
const stats = getInvocationStats({ serviceId: options.service });
|
|
301
|
+
if (options.json) {
|
|
302
|
+
console.log(JSON.stringify(stats, null, 2));
|
|
303
|
+
} else {
|
|
304
|
+
logger.log(` ${chalk.bold("Total:")} ${stats.total}`);
|
|
305
|
+
logger.log(
|
|
306
|
+
` ${chalk.bold("Success rate:")} ${(stats.successRate * 100).toFixed(1)}%`,
|
|
307
|
+
);
|
|
308
|
+
logger.log(
|
|
309
|
+
` ${chalk.bold("Avg duration:")} ${stats.avgDurationMs}ms (success-only)`,
|
|
310
|
+
);
|
|
311
|
+
logger.log(
|
|
312
|
+
` ${chalk.bold("Counts:")} success=${stats.counts.success} failed=${stats.counts.failed} timeout=${stats.counts.timeout} pending=${stats.counts.pending} running=${stats.counts.running}`,
|
|
313
|
+
);
|
|
314
|
+
if (stats.scopedToService) {
|
|
315
|
+
logger.log(
|
|
316
|
+
` ${chalk.bold("Scope:")} service ${stats.scopedToService.slice(0, 8)}`,
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
await shutdown();
|
|
321
|
+
} catch (err) {
|
|
322
|
+
logger.error(`Failed: ${err.message}`);
|
|
323
|
+
process.exit(1);
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
}
|