chainlesschain 0.37.10 → 0.37.11
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/README.md +10 -10
- package/package.json +1 -1
- package/src/commands/a2a.js +374 -0
- package/src/commands/bi.js +240 -0
- package/src/commands/cowork.js +317 -0
- package/src/commands/economy.js +375 -0
- package/src/commands/evolution.js +398 -0
- package/src/commands/hmemory.js +273 -0
- package/src/commands/hook.js +260 -0
- package/src/commands/init.js +184 -0
- package/src/commands/lowcode.js +320 -0
- package/src/commands/plugin.js +55 -2
- package/src/commands/sandbox.js +366 -0
- package/src/commands/skill.js +254 -201
- package/src/commands/workflow.js +359 -0
- package/src/commands/zkp.js +277 -0
- package/src/index.js +44 -0
- package/src/lib/a2a-protocol.js +371 -0
- package/src/lib/agent-coordinator.js +273 -0
- package/src/lib/agent-economy.js +369 -0
- package/src/lib/app-builder.js +377 -0
- package/src/lib/bi-engine.js +299 -0
- package/src/lib/cowork/ab-comparator-cli.js +180 -0
- package/src/lib/cowork/code-knowledge-graph-cli.js +232 -0
- package/src/lib/cowork/debate-review-cli.js +144 -0
- package/src/lib/cowork/decision-kb-cli.js +153 -0
- package/src/lib/cowork/project-style-analyzer-cli.js +168 -0
- package/src/lib/cowork-adapter.js +106 -0
- package/src/lib/evolution-system.js +508 -0
- package/src/lib/hierarchical-memory.js +471 -0
- package/src/lib/hook-manager.js +387 -0
- package/src/lib/plugin-manager.js +118 -0
- package/src/lib/project-detector.js +53 -0
- package/src/lib/sandbox-v2.js +503 -0
- package/src/lib/service-container.js +183 -0
- package/src/lib/skill-loader.js +274 -0
- package/src/lib/workflow-engine.js +503 -0
- package/src/lib/zkp-engine.js +241 -0
- package/src/repl/agent-repl.js +117 -112
package/README.md
CHANGED
|
@@ -554,7 +554,7 @@ Configuration is stored at `~/.chainlesschain/config.json`. The CLI creates and
|
|
|
554
554
|
```bash
|
|
555
555
|
cd packages/cli
|
|
556
556
|
npm install
|
|
557
|
-
npm test # Run all tests (
|
|
557
|
+
npm test # Run all tests (1009 tests across 55 files)
|
|
558
558
|
npm run test:unit # Unit tests only
|
|
559
559
|
npm run test:integration # Integration tests
|
|
560
560
|
npm run test:e2e # End-to-end tests
|
|
@@ -562,15 +562,15 @@ npm run test:e2e # End-to-end tests
|
|
|
562
562
|
|
|
563
563
|
### Test Coverage
|
|
564
564
|
|
|
565
|
-
| Category | Files | Tests
|
|
566
|
-
| ------------------------ | ------ |
|
|
567
|
-
| Unit — lib modules |
|
|
568
|
-
| Unit — commands | 2 | 43
|
|
569
|
-
| Unit — runtime | 1 | 6
|
|
570
|
-
| Integration | 3 | 7
|
|
571
|
-
| E2E |
|
|
572
|
-
| Core packages (external) | — | 118
|
|
573
|
-
| **CLI Total** | **
|
|
565
|
+
| Category | Files | Tests | Status |
|
|
566
|
+
| ------------------------ | ------ | -------- | --------------- |
|
|
567
|
+
| Unit — lib modules | 32 | 672 | All passing |
|
|
568
|
+
| Unit — commands | 2 | 43 | All passing |
|
|
569
|
+
| Unit — runtime | 1 | 6 | All passing |
|
|
570
|
+
| Integration | 3 | 7 | All passing |
|
|
571
|
+
| E2E | 10 | 109 | All passing |
|
|
572
|
+
| Core packages (external) | — | 118 | All passing |
|
|
573
|
+
| **CLI Total** | **55** | **1009** | **All passing** |
|
|
574
574
|
|
|
575
575
|
## License
|
|
576
576
|
|
package/package.json
CHANGED
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A2A (Agent-to-Agent) Protocol commands
|
|
3
|
+
* chainlesschain a2a register|discover|submit|status|complete|fail|peers|cards|negotiate
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import chalk from "chalk";
|
|
7
|
+
import ora from "ora";
|
|
8
|
+
import { logger } from "../lib/logger.js";
|
|
9
|
+
import { bootstrap, shutdown } from "../runtime/bootstrap.js";
|
|
10
|
+
import {
|
|
11
|
+
registerCard,
|
|
12
|
+
updateCard,
|
|
13
|
+
discoverAgents,
|
|
14
|
+
sendTask,
|
|
15
|
+
completeTask,
|
|
16
|
+
failTask,
|
|
17
|
+
getTaskStatus,
|
|
18
|
+
negotiateCapability,
|
|
19
|
+
listPeers,
|
|
20
|
+
} from "../lib/a2a-protocol.js";
|
|
21
|
+
|
|
22
|
+
export function registerA2aCommand(program) {
|
|
23
|
+
const a2a = program
|
|
24
|
+
.command("a2a")
|
|
25
|
+
.description("A2A Protocol — agent-to-agent communication");
|
|
26
|
+
|
|
27
|
+
// a2a register <name>
|
|
28
|
+
a2a
|
|
29
|
+
.command("register")
|
|
30
|
+
.description("Register an agent card")
|
|
31
|
+
.argument("<name>", "Agent name")
|
|
32
|
+
.option("--description <desc>", "Agent description", "")
|
|
33
|
+
.option("--url <url>", "Agent endpoint URL", "")
|
|
34
|
+
.option("--capabilities <csv>", "Comma-separated capabilities", "")
|
|
35
|
+
.option("--skills <csv>", "Comma-separated skills", "")
|
|
36
|
+
.option("--json", "Output as JSON")
|
|
37
|
+
.action(async (name, options) => {
|
|
38
|
+
try {
|
|
39
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
40
|
+
if (!ctx.db) {
|
|
41
|
+
logger.error("Database not available");
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
const db = ctx.db.getDatabase();
|
|
45
|
+
const card = registerCard(db, {
|
|
46
|
+
name,
|
|
47
|
+
description: options.description,
|
|
48
|
+
url: options.url,
|
|
49
|
+
capabilities: options.capabilities
|
|
50
|
+
? options.capabilities.split(",").map((s) => s.trim())
|
|
51
|
+
: [],
|
|
52
|
+
skills: options.skills
|
|
53
|
+
? options.skills.split(",").map((s) => s.trim())
|
|
54
|
+
: [],
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
if (options.json) {
|
|
58
|
+
console.log(JSON.stringify(card, null, 2));
|
|
59
|
+
} else {
|
|
60
|
+
logger.success(
|
|
61
|
+
`Agent registered: ${chalk.cyan(card.name)} ${chalk.gray(card.id)}`,
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
await shutdown();
|
|
66
|
+
} catch (err) {
|
|
67
|
+
logger.error(`Failed: ${err.message}`);
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// a2a discover
|
|
73
|
+
a2a
|
|
74
|
+
.command("discover")
|
|
75
|
+
.description("Discover agents by capability or skill")
|
|
76
|
+
.option("--capability <name>", "Filter by capability")
|
|
77
|
+
.option("--skill <name>", "Filter by skill")
|
|
78
|
+
.option("--name <filter>", "Filter by name")
|
|
79
|
+
.option("--json", "Output as JSON")
|
|
80
|
+
.action(async (options) => {
|
|
81
|
+
try {
|
|
82
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
83
|
+
if (!ctx.db) {
|
|
84
|
+
logger.error("Database not available");
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
const db = ctx.db.getDatabase();
|
|
88
|
+
const agents = discoverAgents(db, {
|
|
89
|
+
capability: options.capability,
|
|
90
|
+
skill: options.skill,
|
|
91
|
+
name: options.name,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
if (options.json) {
|
|
95
|
+
console.log(JSON.stringify(agents, null, 2));
|
|
96
|
+
} else if (agents.length === 0) {
|
|
97
|
+
logger.info("No agents found matching criteria");
|
|
98
|
+
} else {
|
|
99
|
+
logger.log(chalk.bold(`Discovered ${agents.length} agents:\n`));
|
|
100
|
+
for (const a of agents) {
|
|
101
|
+
logger.log(` ${chalk.cyan(a.name)} ${chalk.gray(a.id)}`);
|
|
102
|
+
if (a.description) logger.log(` ${chalk.white(a.description)}`);
|
|
103
|
+
if (a.capabilities.length)
|
|
104
|
+
logger.log(
|
|
105
|
+
` Capabilities: ${chalk.yellow(a.capabilities.join(", "))}`,
|
|
106
|
+
);
|
|
107
|
+
if (a.skills.length)
|
|
108
|
+
logger.log(` Skills: ${chalk.yellow(a.skills.join(", "))}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
await shutdown();
|
|
113
|
+
} catch (err) {
|
|
114
|
+
logger.error(`Failed: ${err.message}`);
|
|
115
|
+
process.exit(1);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// a2a submit <agent-id> <input>
|
|
120
|
+
a2a
|
|
121
|
+
.command("submit")
|
|
122
|
+
.description("Submit a task to an agent")
|
|
123
|
+
.argument("<agent-id>", "Target agent ID")
|
|
124
|
+
.argument("<input>", "Task input")
|
|
125
|
+
.option("--json", "Output as JSON")
|
|
126
|
+
.action(async (agentId, input, options) => {
|
|
127
|
+
try {
|
|
128
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
129
|
+
if (!ctx.db) {
|
|
130
|
+
logger.error("Database not available");
|
|
131
|
+
process.exit(1);
|
|
132
|
+
}
|
|
133
|
+
const db = ctx.db.getDatabase();
|
|
134
|
+
const result = sendTask(db, agentId, input);
|
|
135
|
+
|
|
136
|
+
if (options.json) {
|
|
137
|
+
console.log(JSON.stringify(result, null, 2));
|
|
138
|
+
} else {
|
|
139
|
+
logger.success(
|
|
140
|
+
`Task submitted: ${chalk.gray(result.taskId)} [${chalk.yellow(result.status)}]`,
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
await shutdown();
|
|
145
|
+
} catch (err) {
|
|
146
|
+
logger.error(`Failed: ${err.message}`);
|
|
147
|
+
process.exit(1);
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
// a2a status <task-id>
|
|
152
|
+
a2a
|
|
153
|
+
.command("status")
|
|
154
|
+
.description("Get task status")
|
|
155
|
+
.argument("<task-id>", "Task ID")
|
|
156
|
+
.option("--json", "Output as JSON")
|
|
157
|
+
.action(async (taskId, options) => {
|
|
158
|
+
try {
|
|
159
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
160
|
+
if (!ctx.db) {
|
|
161
|
+
logger.error("Database not available");
|
|
162
|
+
process.exit(1);
|
|
163
|
+
}
|
|
164
|
+
const db = ctx.db.getDatabase();
|
|
165
|
+
const task = getTaskStatus(db, taskId);
|
|
166
|
+
|
|
167
|
+
if (options.json) {
|
|
168
|
+
console.log(JSON.stringify(task, null, 2));
|
|
169
|
+
} else {
|
|
170
|
+
logger.log(chalk.bold("Task Status:\n"));
|
|
171
|
+
logger.log(` ID: ${chalk.gray(task.id)}`);
|
|
172
|
+
logger.log(` Agent: ${chalk.cyan(task.agent_id)}`);
|
|
173
|
+
logger.log(` Status: ${chalk.yellow(task.status)}`);
|
|
174
|
+
if (task.input)
|
|
175
|
+
logger.log(
|
|
176
|
+
` Input: ${chalk.white(task.input.substring(0, 100))}`,
|
|
177
|
+
);
|
|
178
|
+
if (task.output)
|
|
179
|
+
logger.log(
|
|
180
|
+
` Output: ${chalk.green(task.output.substring(0, 100))}`,
|
|
181
|
+
);
|
|
182
|
+
if (task.error) logger.log(` Error: ${chalk.red(task.error)}`);
|
|
183
|
+
if (task.history.length) {
|
|
184
|
+
logger.log(" History:");
|
|
185
|
+
for (const h of task.history) {
|
|
186
|
+
logger.log(
|
|
187
|
+
` ${chalk.gray(h.timestamp)} → ${chalk.yellow(h.status)}`,
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
await shutdown();
|
|
194
|
+
} catch (err) {
|
|
195
|
+
logger.error(`Failed: ${err.message}`);
|
|
196
|
+
process.exit(1);
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
// a2a complete <task-id> <output>
|
|
201
|
+
a2a
|
|
202
|
+
.command("complete")
|
|
203
|
+
.description("Mark a task as completed")
|
|
204
|
+
.argument("<task-id>", "Task ID")
|
|
205
|
+
.argument("<output>", "Task output")
|
|
206
|
+
.action(async (taskId, output) => {
|
|
207
|
+
try {
|
|
208
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
209
|
+
if (!ctx.db) {
|
|
210
|
+
logger.error("Database not available");
|
|
211
|
+
process.exit(1);
|
|
212
|
+
}
|
|
213
|
+
const db = ctx.db.getDatabase();
|
|
214
|
+
const result = completeTask(db, taskId, output);
|
|
215
|
+
logger.success(
|
|
216
|
+
`Task ${chalk.gray(taskId)} → ${chalk.green(result.status)}`,
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
await shutdown();
|
|
220
|
+
} catch (err) {
|
|
221
|
+
logger.error(`Failed: ${err.message}`);
|
|
222
|
+
process.exit(1);
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
// a2a fail <task-id> <error>
|
|
227
|
+
a2a
|
|
228
|
+
.command("fail")
|
|
229
|
+
.description("Mark a task as failed")
|
|
230
|
+
.argument("<task-id>", "Task ID")
|
|
231
|
+
.argument("<error>", "Error message")
|
|
232
|
+
.action(async (taskId, error) => {
|
|
233
|
+
try {
|
|
234
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
235
|
+
if (!ctx.db) {
|
|
236
|
+
logger.error("Database not available");
|
|
237
|
+
process.exit(1);
|
|
238
|
+
}
|
|
239
|
+
const db = ctx.db.getDatabase();
|
|
240
|
+
const result = failTask(db, taskId, error);
|
|
241
|
+
logger.success(
|
|
242
|
+
`Task ${chalk.gray(taskId)} → ${chalk.red(result.status)}`,
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
await shutdown();
|
|
246
|
+
} catch (err) {
|
|
247
|
+
logger.error(`Failed: ${err.message}`);
|
|
248
|
+
process.exit(1);
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
// a2a peers
|
|
253
|
+
a2a
|
|
254
|
+
.command("peers")
|
|
255
|
+
.description("List all registered agents")
|
|
256
|
+
.option("--json", "Output as JSON")
|
|
257
|
+
.action(async (options) => {
|
|
258
|
+
try {
|
|
259
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
260
|
+
if (!ctx.db) {
|
|
261
|
+
logger.error("Database not available");
|
|
262
|
+
process.exit(1);
|
|
263
|
+
}
|
|
264
|
+
const db = ctx.db.getDatabase();
|
|
265
|
+
const peers = listPeers(db);
|
|
266
|
+
|
|
267
|
+
if (options.json) {
|
|
268
|
+
console.log(JSON.stringify(peers, null, 2));
|
|
269
|
+
} else if (peers.length === 0) {
|
|
270
|
+
logger.info("No agents registered. Use 'a2a register' to add one.");
|
|
271
|
+
} else {
|
|
272
|
+
logger.log(chalk.bold(`${peers.length} registered agents:\n`));
|
|
273
|
+
for (const p of peers) {
|
|
274
|
+
const statusColor =
|
|
275
|
+
p.status === "active" ? chalk.green : chalk.gray;
|
|
276
|
+
logger.log(
|
|
277
|
+
` ${chalk.cyan(p.name)} ${chalk.gray(p.id)} ${statusColor(p.status)}`,
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
await shutdown();
|
|
283
|
+
} catch (err) {
|
|
284
|
+
logger.error(`Failed: ${err.message}`);
|
|
285
|
+
process.exit(1);
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
// a2a cards
|
|
290
|
+
a2a
|
|
291
|
+
.command("cards")
|
|
292
|
+
.description("List all agent cards with details")
|
|
293
|
+
.option("--json", "Output as JSON")
|
|
294
|
+
.action(async (options) => {
|
|
295
|
+
try {
|
|
296
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
297
|
+
if (!ctx.db) {
|
|
298
|
+
logger.error("Database not available");
|
|
299
|
+
process.exit(1);
|
|
300
|
+
}
|
|
301
|
+
const db = ctx.db.getDatabase();
|
|
302
|
+
const peers = listPeers(db);
|
|
303
|
+
|
|
304
|
+
if (options.json) {
|
|
305
|
+
console.log(JSON.stringify(peers, null, 2));
|
|
306
|
+
} else if (peers.length === 0) {
|
|
307
|
+
logger.info("No agent cards registered.");
|
|
308
|
+
} else {
|
|
309
|
+
logger.log(chalk.bold(`${peers.length} agent cards:\n`));
|
|
310
|
+
for (const p of peers) {
|
|
311
|
+
logger.log(` ${chalk.cyan(p.name)} ${chalk.gray(p.id)}`);
|
|
312
|
+
if (p.description) logger.log(` Description: ${p.description}`);
|
|
313
|
+
if (p.url) logger.log(` URL: ${chalk.blue(p.url)}`);
|
|
314
|
+
logger.log(` Auth: ${p.auth_type}`);
|
|
315
|
+
if (p.capabilities.length)
|
|
316
|
+
logger.log(
|
|
317
|
+
` Capabilities: ${chalk.yellow(p.capabilities.join(", "))}`,
|
|
318
|
+
);
|
|
319
|
+
if (p.skills.length)
|
|
320
|
+
logger.log(` Skills: ${chalk.yellow(p.skills.join(", "))}`);
|
|
321
|
+
logger.log("");
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
await shutdown();
|
|
326
|
+
} catch (err) {
|
|
327
|
+
logger.error(`Failed: ${err.message}`);
|
|
328
|
+
process.exit(1);
|
|
329
|
+
}
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
// a2a negotiate <agent-id> <capabilities>
|
|
333
|
+
a2a
|
|
334
|
+
.command("negotiate")
|
|
335
|
+
.description("Check if an agent supports required capabilities")
|
|
336
|
+
.argument("<agent-id>", "Agent ID")
|
|
337
|
+
.argument("<capabilities>", "Comma-separated required capabilities")
|
|
338
|
+
.option("--json", "Output as JSON")
|
|
339
|
+
.action(async (agentId, capabilities, options) => {
|
|
340
|
+
try {
|
|
341
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
342
|
+
if (!ctx.db) {
|
|
343
|
+
logger.error("Database not available");
|
|
344
|
+
process.exit(1);
|
|
345
|
+
}
|
|
346
|
+
const db = ctx.db.getDatabase();
|
|
347
|
+
const required = capabilities.split(",").map((s) => s.trim());
|
|
348
|
+
const result = negotiateCapability(db, agentId, required);
|
|
349
|
+
|
|
350
|
+
if (options.json) {
|
|
351
|
+
console.log(JSON.stringify(result, null, 2));
|
|
352
|
+
} else {
|
|
353
|
+
if (result.compatible) {
|
|
354
|
+
logger.success("Agent is fully compatible");
|
|
355
|
+
} else {
|
|
356
|
+
logger.warn("Agent is not fully compatible");
|
|
357
|
+
}
|
|
358
|
+
if (result.supported.length) {
|
|
359
|
+
logger.log(
|
|
360
|
+
` Supported: ${chalk.green(result.supported.join(", "))}`,
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
if (result.missing.length) {
|
|
364
|
+
logger.log(` Missing: ${chalk.red(result.missing.join(", "))}`);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
await shutdown();
|
|
369
|
+
} catch (err) {
|
|
370
|
+
logger.error(`Failed: ${err.message}`);
|
|
371
|
+
process.exit(1);
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BI commands
|
|
3
|
+
* chainlesschain bi query|dashboard|report|schedule|anomaly|predict|templates
|
|
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
|
+
ensureBITables,
|
|
11
|
+
nlQuery,
|
|
12
|
+
createDashboard,
|
|
13
|
+
generateReport,
|
|
14
|
+
scheduleReport,
|
|
15
|
+
detectAnomaly,
|
|
16
|
+
predictTrend,
|
|
17
|
+
listTemplates,
|
|
18
|
+
} from "../lib/bi-engine.js";
|
|
19
|
+
|
|
20
|
+
export function registerBiCommand(program) {
|
|
21
|
+
const bi = program
|
|
22
|
+
.command("bi")
|
|
23
|
+
.description(
|
|
24
|
+
"Business intelligence — queries, dashboards, reports, analytics",
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
// bi query
|
|
28
|
+
bi.command("query <question>")
|
|
29
|
+
.description("Natural-language query (NL→SQL)")
|
|
30
|
+
.option("--json", "Output as JSON")
|
|
31
|
+
.action(async (question, options) => {
|
|
32
|
+
try {
|
|
33
|
+
const result = nlQuery(question);
|
|
34
|
+
if (options.json) {
|
|
35
|
+
console.log(JSON.stringify(result, null, 2));
|
|
36
|
+
} else {
|
|
37
|
+
logger.success("Query translated");
|
|
38
|
+
logger.log(
|
|
39
|
+
` ${chalk.bold("SQL:")} ${chalk.cyan(result.generatedSQL)}`,
|
|
40
|
+
);
|
|
41
|
+
logger.log(` ${chalk.bold("Rows:")} ${result.rowCount}`);
|
|
42
|
+
logger.log(` ${chalk.bold("Visual:")} ${result.visualization.type}`);
|
|
43
|
+
}
|
|
44
|
+
} catch (err) {
|
|
45
|
+
logger.error(`Failed: ${err.message}`);
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// bi dashboard
|
|
51
|
+
bi.command("dashboard <name>")
|
|
52
|
+
.description("Create a dashboard")
|
|
53
|
+
.option("--widgets <json>", "Widgets as JSON array")
|
|
54
|
+
.option("--layout <json>", "Layout as JSON")
|
|
55
|
+
.option("--json", "Output as JSON")
|
|
56
|
+
.action(async (name, options) => {
|
|
57
|
+
try {
|
|
58
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
59
|
+
if (!ctx.db) {
|
|
60
|
+
logger.error("Database not available");
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
const db = ctx.db.getDatabase();
|
|
64
|
+
ensureBITables(db);
|
|
65
|
+
|
|
66
|
+
const widgets = options.widgets ? JSON.parse(options.widgets) : [];
|
|
67
|
+
const layout = options.layout ? JSON.parse(options.layout) : undefined;
|
|
68
|
+
const dashboard = createDashboard(db, name, widgets, layout);
|
|
69
|
+
|
|
70
|
+
if (options.json) {
|
|
71
|
+
console.log(JSON.stringify(dashboard, null, 2));
|
|
72
|
+
} else {
|
|
73
|
+
logger.success(`Dashboard created: ${chalk.cyan(name)}`);
|
|
74
|
+
logger.log(` ${chalk.bold("ID:")} ${chalk.cyan(dashboard.id)}`);
|
|
75
|
+
logger.log(` ${chalk.bold("Widgets:")} ${dashboard.widgets.length}`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
await shutdown();
|
|
79
|
+
} catch (err) {
|
|
80
|
+
logger.error(`Failed: ${err.message}`);
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// bi report
|
|
86
|
+
bi.command("report <name>")
|
|
87
|
+
.description("Generate a report")
|
|
88
|
+
.option("--format <pdf|excel>", "Report format", "pdf")
|
|
89
|
+
.option("--sections <csv>", "Comma-separated section names")
|
|
90
|
+
.option("--json", "Output as JSON")
|
|
91
|
+
.action(async (name, options) => {
|
|
92
|
+
try {
|
|
93
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
94
|
+
if (!ctx.db) {
|
|
95
|
+
logger.error("Database not available");
|
|
96
|
+
process.exit(1);
|
|
97
|
+
}
|
|
98
|
+
const db = ctx.db.getDatabase();
|
|
99
|
+
ensureBITables(db);
|
|
100
|
+
|
|
101
|
+
const sections = options.sections
|
|
102
|
+
? options.sections.split(",").map((s) => s.trim())
|
|
103
|
+
: undefined;
|
|
104
|
+
const report = generateReport(db, name, {
|
|
105
|
+
format: options.format,
|
|
106
|
+
sections,
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
if (options.json) {
|
|
110
|
+
console.log(JSON.stringify(report, null, 2));
|
|
111
|
+
} else {
|
|
112
|
+
logger.success(`Report generated: ${chalk.cyan(name)}`);
|
|
113
|
+
logger.log(` ${chalk.bold("ID:")} ${chalk.cyan(report.id)}`);
|
|
114
|
+
logger.log(` ${chalk.bold("Format:")} ${report.format}`);
|
|
115
|
+
logger.log(` ${chalk.bold("Sections:")} ${report.sections.length}`);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
await shutdown();
|
|
119
|
+
} catch (err) {
|
|
120
|
+
logger.error(`Failed: ${err.message}`);
|
|
121
|
+
process.exit(1);
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// bi schedule
|
|
126
|
+
bi.command("schedule <report-id> <cron>")
|
|
127
|
+
.description("Schedule a report for recurring generation")
|
|
128
|
+
.option("--recipients <csv>", "Comma-separated recipient emails")
|
|
129
|
+
.action(async (reportId, cron, options) => {
|
|
130
|
+
try {
|
|
131
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
132
|
+
if (!ctx.db) {
|
|
133
|
+
logger.error("Database not available");
|
|
134
|
+
process.exit(1);
|
|
135
|
+
}
|
|
136
|
+
const db = ctx.db.getDatabase();
|
|
137
|
+
ensureBITables(db);
|
|
138
|
+
|
|
139
|
+
const recipients = options.recipients
|
|
140
|
+
? options.recipients.split(",").map((s) => s.trim())
|
|
141
|
+
: [];
|
|
142
|
+
const schedule = scheduleReport(db, reportId, cron, recipients);
|
|
143
|
+
logger.success("Report scheduled");
|
|
144
|
+
logger.log(` ${chalk.bold("ID:")} ${chalk.cyan(schedule.id)}`);
|
|
145
|
+
logger.log(` ${chalk.bold("Cron:")} ${schedule.cron}`);
|
|
146
|
+
logger.log(
|
|
147
|
+
` ${chalk.bold("Recipients:")} ${schedule.recipients.length}`,
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
await shutdown();
|
|
151
|
+
} catch (err) {
|
|
152
|
+
logger.error(`Failed: ${err.message}`);
|
|
153
|
+
process.exit(1);
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// bi anomaly
|
|
158
|
+
bi.command("anomaly")
|
|
159
|
+
.description("Detect anomalies in data using Z-score")
|
|
160
|
+
.option("--data <json>", "Data as JSON array of numbers")
|
|
161
|
+
.option("--threshold <n>", "Z-score threshold", "2")
|
|
162
|
+
.option("--json", "Output as JSON")
|
|
163
|
+
.action(async (options) => {
|
|
164
|
+
try {
|
|
165
|
+
const data = options.data ? JSON.parse(options.data) : [];
|
|
166
|
+
const result = detectAnomaly(data, {
|
|
167
|
+
threshold: parseFloat(options.threshold),
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
if (options.json) {
|
|
171
|
+
console.log(JSON.stringify(result, null, 2));
|
|
172
|
+
} else {
|
|
173
|
+
logger.log(` ${chalk.bold("Mean:")} ${result.mean.toFixed(2)}`);
|
|
174
|
+
logger.log(` ${chalk.bold("Std Dev:")} ${result.std.toFixed(2)}`);
|
|
175
|
+
logger.log(` ${chalk.bold("Threshold:")} ${result.threshold}`);
|
|
176
|
+
logger.log(
|
|
177
|
+
` ${chalk.bold("Anomalies:")} ${result.anomalies.length}`,
|
|
178
|
+
);
|
|
179
|
+
for (const a of result.anomalies) {
|
|
180
|
+
logger.log(
|
|
181
|
+
` [${a.index}] value=${a.value} z=${a.zScore.toFixed(2)}`,
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
} catch (err) {
|
|
186
|
+
logger.error(`Failed: ${err.message}`);
|
|
187
|
+
process.exit(1);
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
// bi predict
|
|
192
|
+
bi.command("predict")
|
|
193
|
+
.description("Predict trend using linear regression")
|
|
194
|
+
.option("--data <json>", "Data as JSON array of numbers")
|
|
195
|
+
.option("--periods <n>", "Number of periods to predict", "3")
|
|
196
|
+
.option("--json", "Output as JSON")
|
|
197
|
+
.action(async (options) => {
|
|
198
|
+
try {
|
|
199
|
+
const data = options.data ? JSON.parse(options.data) : [];
|
|
200
|
+
const result = predictTrend(data, parseInt(options.periods));
|
|
201
|
+
|
|
202
|
+
if (options.json) {
|
|
203
|
+
console.log(JSON.stringify(result, null, 2));
|
|
204
|
+
} else {
|
|
205
|
+
logger.log(
|
|
206
|
+
` ${chalk.bold("Trend:")} ${chalk.cyan(result.trend)}`,
|
|
207
|
+
);
|
|
208
|
+
logger.log(` ${chalk.bold("Slope:")} ${result.slope}`);
|
|
209
|
+
logger.log(
|
|
210
|
+
` ${chalk.bold("Predictions:")} ${result.predictions.join(", ")}`,
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
} catch (err) {
|
|
214
|
+
logger.error(`Failed: ${err.message}`);
|
|
215
|
+
process.exit(1);
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
// bi templates
|
|
220
|
+
bi.command("templates")
|
|
221
|
+
.description("List available BI templates")
|
|
222
|
+
.option("--json", "Output as JSON")
|
|
223
|
+
.action(async (options) => {
|
|
224
|
+
try {
|
|
225
|
+
const templates = listTemplates();
|
|
226
|
+
if (options.json) {
|
|
227
|
+
console.log(JSON.stringify(templates, null, 2));
|
|
228
|
+
} else {
|
|
229
|
+
for (const t of templates) {
|
|
230
|
+
logger.log(
|
|
231
|
+
` ${chalk.cyan(t.id)} ${chalk.bold(t.name)} — ${t.description}`,
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
} catch (err) {
|
|
236
|
+
logger.error(`Failed: ${err.message}`);
|
|
237
|
+
process.exit(1);
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
}
|