chainlesschain 0.37.9 → 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 +309 -19
- package/bin/chainlesschain.js +4 -0
- package/package.json +1 -1
- package/src/commands/a2a.js +374 -0
- package/src/commands/audit.js +286 -0
- package/src/commands/auth.js +387 -0
- package/src/commands/bi.js +240 -0
- package/src/commands/browse.js +184 -0
- package/src/commands/cowork.js +317 -0
- package/src/commands/did.js +376 -0
- package/src/commands/economy.js +375 -0
- package/src/commands/encrypt.js +233 -0
- package/src/commands/evolution.js +398 -0
- package/src/commands/export.js +125 -0
- package/src/commands/git.js +215 -0
- package/src/commands/hmemory.js +273 -0
- package/src/commands/hook.js +260 -0
- package/src/commands/import.js +259 -0
- package/src/commands/init.js +184 -0
- package/src/commands/instinct.js +202 -0
- package/src/commands/llm.js +155 -4
- package/src/commands/lowcode.js +320 -0
- package/src/commands/mcp.js +302 -0
- package/src/commands/memory.js +282 -0
- package/src/commands/note.js +187 -0
- package/src/commands/org.js +505 -0
- package/src/commands/p2p.js +274 -0
- package/src/commands/plugin.js +451 -0
- package/src/commands/sandbox.js +366 -0
- package/src/commands/search.js +237 -0
- package/src/commands/session.js +238 -0
- package/src/commands/skill.js +254 -201
- package/src/commands/sync.js +249 -0
- package/src/commands/tokens.js +214 -0
- package/src/commands/wallet.js +416 -0
- package/src/commands/workflow.js +359 -0
- package/src/commands/zkp.js +277 -0
- package/src/index.js +93 -1
- 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/audit-logger.js +364 -0
- package/src/lib/bi-engine.js +299 -0
- package/src/lib/bm25-search.js +322 -0
- package/src/lib/browser-automation.js +216 -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/crypto-manager.js +246 -0
- package/src/lib/did-manager.js +270 -0
- package/src/lib/ensure-utf8.js +59 -0
- package/src/lib/evolution-system.js +508 -0
- package/src/lib/git-integration.js +220 -0
- package/src/lib/hierarchical-memory.js +471 -0
- package/src/lib/hook-manager.js +387 -0
- package/src/lib/instinct-manager.js +190 -0
- package/src/lib/knowledge-exporter.js +302 -0
- package/src/lib/knowledge-importer.js +293 -0
- package/src/lib/llm-providers.js +325 -0
- package/src/lib/mcp-client.js +413 -0
- package/src/lib/memory-manager.js +211 -0
- package/src/lib/note-versioning.js +244 -0
- package/src/lib/org-manager.js +424 -0
- package/src/lib/p2p-manager.js +317 -0
- package/src/lib/pdf-parser.js +96 -0
- package/src/lib/permission-engine.js +374 -0
- package/src/lib/plan-mode.js +333 -0
- package/src/lib/plugin-manager.js +430 -0
- package/src/lib/project-detector.js +53 -0
- package/src/lib/response-cache.js +156 -0
- package/src/lib/sandbox-v2.js +503 -0
- package/src/lib/service-container.js +183 -0
- package/src/lib/session-manager.js +189 -0
- package/src/lib/skill-loader.js +274 -0
- package/src/lib/sync-manager.js +347 -0
- package/src/lib/token-tracker.js +200 -0
- package/src/lib/wallet-manager.js +348 -0
- package/src/lib/workflow-engine.js +503 -0
- package/src/lib/zkp-engine.js +241 -0
- package/src/repl/agent-repl.js +259 -124
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth / RBAC commands
|
|
3
|
+
* chainlesschain auth roles|grant|revoke|check|permissions|users
|
|
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
|
+
getRoles,
|
|
11
|
+
createRole,
|
|
12
|
+
deleteRole,
|
|
13
|
+
grantRole,
|
|
14
|
+
revokeRole,
|
|
15
|
+
grantPermission,
|
|
16
|
+
revokePermission,
|
|
17
|
+
getUserPermissions,
|
|
18
|
+
checkPermission,
|
|
19
|
+
listUserRoles,
|
|
20
|
+
PERMISSION_SCOPES,
|
|
21
|
+
} from "../lib/permission-engine.js";
|
|
22
|
+
|
|
23
|
+
export function registerAuthCommand(program) {
|
|
24
|
+
const auth = program
|
|
25
|
+
.command("auth")
|
|
26
|
+
.description("RBAC permission management");
|
|
27
|
+
|
|
28
|
+
// auth roles
|
|
29
|
+
auth
|
|
30
|
+
.command("roles", { isDefault: true })
|
|
31
|
+
.description("List all roles")
|
|
32
|
+
.option("--json", "Output as JSON")
|
|
33
|
+
.action(async (options) => {
|
|
34
|
+
try {
|
|
35
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
36
|
+
if (!ctx.db) {
|
|
37
|
+
logger.error("Database not available");
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
const db = ctx.db.getDatabase();
|
|
41
|
+
const roles = getRoles(db);
|
|
42
|
+
|
|
43
|
+
if (options.json) {
|
|
44
|
+
console.log(JSON.stringify(roles, null, 2));
|
|
45
|
+
} else if (roles.length === 0) {
|
|
46
|
+
logger.info("No roles defined");
|
|
47
|
+
} else {
|
|
48
|
+
logger.log(chalk.bold(`Roles (${roles.length}):\n`));
|
|
49
|
+
for (const role of roles) {
|
|
50
|
+
const tag = role.isBuiltin
|
|
51
|
+
? chalk.gray(" [built-in]")
|
|
52
|
+
: chalk.blue(" [custom]");
|
|
53
|
+
logger.log(
|
|
54
|
+
` ${chalk.cyan(role.name.padEnd(15))}${tag} ${role.description || ""}`,
|
|
55
|
+
);
|
|
56
|
+
logger.log(
|
|
57
|
+
` ${chalk.gray("permissions:")} ${role.permissions.join(", ")}`,
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
await shutdown();
|
|
63
|
+
} catch (err) {
|
|
64
|
+
logger.error(`Failed: ${err.message}`);
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// auth create-role
|
|
70
|
+
auth
|
|
71
|
+
.command("create-role")
|
|
72
|
+
.description("Create a custom role")
|
|
73
|
+
.argument("<name>", "Role name")
|
|
74
|
+
.option("-d, --description <desc>", "Role description")
|
|
75
|
+
.option("-p, --permissions <perms>", "Comma-separated permissions")
|
|
76
|
+
.action(async (name, options) => {
|
|
77
|
+
try {
|
|
78
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
79
|
+
if (!ctx.db) {
|
|
80
|
+
logger.error("Database not available");
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
const db = ctx.db.getDatabase();
|
|
84
|
+
|
|
85
|
+
const perms = options.permissions
|
|
86
|
+
? options.permissions.split(",").map((s) => s.trim())
|
|
87
|
+
: [];
|
|
88
|
+
const role = createRole(db, name, options.description, perms);
|
|
89
|
+
|
|
90
|
+
logger.success(`Role created: ${role.name}`);
|
|
91
|
+
if (role.permissions.length > 0) {
|
|
92
|
+
logger.log(
|
|
93
|
+
` ${chalk.bold("Permissions:")} ${role.permissions.join(", ")}`,
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
await shutdown();
|
|
98
|
+
} catch (err) {
|
|
99
|
+
logger.error(`Failed: ${err.message}`);
|
|
100
|
+
process.exit(1);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// auth delete-role
|
|
105
|
+
auth
|
|
106
|
+
.command("delete-role")
|
|
107
|
+
.description("Delete a custom role")
|
|
108
|
+
.argument("<name>", "Role name")
|
|
109
|
+
.option("--force", "Skip confirmation")
|
|
110
|
+
.action(async (name, options) => {
|
|
111
|
+
try {
|
|
112
|
+
if (!options.force) {
|
|
113
|
+
const { confirm } = await import("@inquirer/prompts");
|
|
114
|
+
const ok = await confirm({
|
|
115
|
+
message: `Delete role "${name}"? All grants for this role will be removed.`,
|
|
116
|
+
});
|
|
117
|
+
if (!ok) {
|
|
118
|
+
logger.info("Cancelled");
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
124
|
+
if (!ctx.db) {
|
|
125
|
+
logger.error("Database not available");
|
|
126
|
+
process.exit(1);
|
|
127
|
+
}
|
|
128
|
+
const db = ctx.db.getDatabase();
|
|
129
|
+
const ok = deleteRole(db, name);
|
|
130
|
+
|
|
131
|
+
if (ok) {
|
|
132
|
+
logger.success(`Role deleted: ${name}`);
|
|
133
|
+
} else {
|
|
134
|
+
logger.error(`Role not found or is built-in: ${name}`);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
await shutdown();
|
|
138
|
+
} catch (err) {
|
|
139
|
+
logger.error(`Failed: ${err.message}`);
|
|
140
|
+
process.exit(1);
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// auth grant
|
|
145
|
+
auth
|
|
146
|
+
.command("grant")
|
|
147
|
+
.description("Grant a role to a user")
|
|
148
|
+
.argument("<user-did>", "User DID")
|
|
149
|
+
.argument("<role>", "Role name")
|
|
150
|
+
.option("--expires <date>", "Expiration date (ISO 8601)")
|
|
151
|
+
.action(async (userDid, role, options) => {
|
|
152
|
+
try {
|
|
153
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
154
|
+
if (!ctx.db) {
|
|
155
|
+
logger.error("Database not available");
|
|
156
|
+
process.exit(1);
|
|
157
|
+
}
|
|
158
|
+
const db = ctx.db.getDatabase();
|
|
159
|
+
const grant = grantRole(db, userDid, role, null, options.expires);
|
|
160
|
+
|
|
161
|
+
logger.success(`Granted role "${role}" to ${userDid}`);
|
|
162
|
+
if (grant.expiresAt) {
|
|
163
|
+
logger.log(` ${chalk.bold("Expires:")} ${grant.expiresAt}`);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
await shutdown();
|
|
167
|
+
} catch (err) {
|
|
168
|
+
logger.error(`Failed: ${err.message}`);
|
|
169
|
+
process.exit(1);
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// auth revoke
|
|
174
|
+
auth
|
|
175
|
+
.command("revoke")
|
|
176
|
+
.description("Revoke a role from a user")
|
|
177
|
+
.argument("<user-did>", "User DID")
|
|
178
|
+
.argument("<role>", "Role name")
|
|
179
|
+
.action(async (userDid, role) => {
|
|
180
|
+
try {
|
|
181
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
182
|
+
if (!ctx.db) {
|
|
183
|
+
logger.error("Database not available");
|
|
184
|
+
process.exit(1);
|
|
185
|
+
}
|
|
186
|
+
const db = ctx.db.getDatabase();
|
|
187
|
+
const ok = revokeRole(db, userDid, role);
|
|
188
|
+
|
|
189
|
+
if (ok) {
|
|
190
|
+
logger.success(`Revoked role "${role}" from ${userDid}`);
|
|
191
|
+
} else {
|
|
192
|
+
logger.error("Grant not found");
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
await shutdown();
|
|
196
|
+
} catch (err) {
|
|
197
|
+
logger.error(`Failed: ${err.message}`);
|
|
198
|
+
process.exit(1);
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
// auth grant-permission (direct permission)
|
|
203
|
+
auth
|
|
204
|
+
.command("grant-permission")
|
|
205
|
+
.description("Grant a direct permission to a user")
|
|
206
|
+
.argument("<user-did>", "User DID")
|
|
207
|
+
.argument("<permission>", "Permission scope (e.g., note:read)")
|
|
208
|
+
.action(async (userDid, permission) => {
|
|
209
|
+
try {
|
|
210
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
211
|
+
if (!ctx.db) {
|
|
212
|
+
logger.error("Database not available");
|
|
213
|
+
process.exit(1);
|
|
214
|
+
}
|
|
215
|
+
const db = ctx.db.getDatabase();
|
|
216
|
+
grantPermission(db, userDid, permission);
|
|
217
|
+
logger.success(`Granted permission "${permission}" to ${userDid}`);
|
|
218
|
+
|
|
219
|
+
await shutdown();
|
|
220
|
+
} catch (err) {
|
|
221
|
+
logger.error(`Failed: ${err.message}`);
|
|
222
|
+
process.exit(1);
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
// auth revoke-permission
|
|
227
|
+
auth
|
|
228
|
+
.command("revoke-permission")
|
|
229
|
+
.description("Revoke a direct permission from a user")
|
|
230
|
+
.argument("<user-did>", "User DID")
|
|
231
|
+
.argument("<permission>", "Permission scope")
|
|
232
|
+
.action(async (userDid, permission) => {
|
|
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 ok = revokePermission(db, userDid, permission);
|
|
241
|
+
|
|
242
|
+
if (ok) {
|
|
243
|
+
logger.success(`Revoked permission "${permission}" from ${userDid}`);
|
|
244
|
+
} else {
|
|
245
|
+
logger.error("Permission grant not found");
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
await shutdown();
|
|
249
|
+
} catch (err) {
|
|
250
|
+
logger.error(`Failed: ${err.message}`);
|
|
251
|
+
process.exit(1);
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
// auth check
|
|
256
|
+
auth
|
|
257
|
+
.command("check")
|
|
258
|
+
.description("Check if a user has a specific permission")
|
|
259
|
+
.argument("<user-did>", "User DID")
|
|
260
|
+
.argument("<permission>", "Permission to check")
|
|
261
|
+
.option("--json", "Output as JSON")
|
|
262
|
+
.action(async (userDid, permission, options) => {
|
|
263
|
+
try {
|
|
264
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
265
|
+
if (!ctx.db) {
|
|
266
|
+
logger.error("Database not available");
|
|
267
|
+
process.exit(1);
|
|
268
|
+
}
|
|
269
|
+
const db = ctx.db.getDatabase();
|
|
270
|
+
const allowed = checkPermission(db, userDid, permission);
|
|
271
|
+
|
|
272
|
+
if (options.json) {
|
|
273
|
+
console.log(
|
|
274
|
+
JSON.stringify({ userDid, permission, allowed }, null, 2),
|
|
275
|
+
);
|
|
276
|
+
} else if (allowed) {
|
|
277
|
+
logger.success(
|
|
278
|
+
`${chalk.green("ALLOWED")} — ${userDid} has permission: ${permission}`,
|
|
279
|
+
);
|
|
280
|
+
} else {
|
|
281
|
+
logger.log(
|
|
282
|
+
`${chalk.red("DENIED")} — ${userDid} does not have permission: ${permission}`,
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
await shutdown();
|
|
287
|
+
} catch (err) {
|
|
288
|
+
logger.error(`Failed: ${err.message}`);
|
|
289
|
+
process.exit(1);
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
// auth permissions
|
|
294
|
+
auth
|
|
295
|
+
.command("permissions")
|
|
296
|
+
.description("Show all permissions for a user")
|
|
297
|
+
.argument("<user-did>", "User DID")
|
|
298
|
+
.option("--json", "Output as JSON")
|
|
299
|
+
.action(async (userDid, options) => {
|
|
300
|
+
try {
|
|
301
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
302
|
+
if (!ctx.db) {
|
|
303
|
+
logger.error("Database not available");
|
|
304
|
+
process.exit(1);
|
|
305
|
+
}
|
|
306
|
+
const db = ctx.db.getDatabase();
|
|
307
|
+
const perms = getUserPermissions(db, userDid);
|
|
308
|
+
|
|
309
|
+
if (options.json) {
|
|
310
|
+
console.log(JSON.stringify(perms, null, 2));
|
|
311
|
+
} else {
|
|
312
|
+
logger.log(chalk.bold(`Permissions for ${chalk.cyan(userDid)}:\n`));
|
|
313
|
+
if (perms.isAdmin) {
|
|
314
|
+
logger.log(` ${chalk.green("ADMIN")} — Full access (wildcard *)`);
|
|
315
|
+
}
|
|
316
|
+
if (perms.roles.length > 0) {
|
|
317
|
+
logger.log(` ${chalk.bold("Roles:")} ${perms.roles.join(", ")}`);
|
|
318
|
+
}
|
|
319
|
+
if (perms.directPermissions.length > 0) {
|
|
320
|
+
logger.log(
|
|
321
|
+
` ${chalk.bold("Direct:")} ${perms.directPermissions.join(", ")}`,
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
if (perms.effectivePermissions.length > 0) {
|
|
325
|
+
logger.log(
|
|
326
|
+
` ${chalk.bold("Effective:")} ${perms.effectivePermissions.join(", ")}`,
|
|
327
|
+
);
|
|
328
|
+
} else {
|
|
329
|
+
logger.log(` ${chalk.gray("No permissions assigned")}`);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
await shutdown();
|
|
334
|
+
} catch (err) {
|
|
335
|
+
logger.error(`Failed: ${err.message}`);
|
|
336
|
+
process.exit(1);
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
// auth users
|
|
341
|
+
auth
|
|
342
|
+
.command("users")
|
|
343
|
+
.description("List all users with role assignments")
|
|
344
|
+
.option("--json", "Output as JSON")
|
|
345
|
+
.action(async (options) => {
|
|
346
|
+
try {
|
|
347
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
348
|
+
if (!ctx.db) {
|
|
349
|
+
logger.error("Database not available");
|
|
350
|
+
process.exit(1);
|
|
351
|
+
}
|
|
352
|
+
const db = ctx.db.getDatabase();
|
|
353
|
+
const users = listUserRoles(db);
|
|
354
|
+
|
|
355
|
+
if (options.json) {
|
|
356
|
+
console.log(JSON.stringify(users, null, 2));
|
|
357
|
+
} else if (users.length === 0) {
|
|
358
|
+
logger.info("No role assignments yet");
|
|
359
|
+
} else {
|
|
360
|
+
logger.log(chalk.bold(`Users with roles (${users.length}):\n`));
|
|
361
|
+
for (const u of users) {
|
|
362
|
+
logger.log(` ${chalk.cyan(u.userDid)}`);
|
|
363
|
+
logger.log(` ${chalk.gray("roles:")} ${u.roles.join(", ")}`);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
await shutdown();
|
|
368
|
+
} catch (err) {
|
|
369
|
+
logger.error(`Failed: ${err.message}`);
|
|
370
|
+
process.exit(1);
|
|
371
|
+
}
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
// auth scopes
|
|
375
|
+
auth
|
|
376
|
+
.command("scopes")
|
|
377
|
+
.description("List all available permission scopes")
|
|
378
|
+
.action(async () => {
|
|
379
|
+
logger.log(chalk.bold("Available Permission Scopes:\n"));
|
|
380
|
+
for (const scope of PERMISSION_SCOPES) {
|
|
381
|
+
const [resource, action] = scope.split(":");
|
|
382
|
+
logger.log(
|
|
383
|
+
` ${chalk.cyan(resource.padEnd(12))}:${chalk.white(action)}`,
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
});
|
|
387
|
+
}
|
|
@@ -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
|
+
}
|