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
package/src/commands/note.js
CHANGED
|
@@ -6,6 +6,15 @@
|
|
|
6
6
|
import chalk from "chalk";
|
|
7
7
|
import { logger } from "../lib/logger.js";
|
|
8
8
|
import { bootstrap, shutdown } from "../runtime/bootstrap.js";
|
|
9
|
+
import {
|
|
10
|
+
ensureVersionsTable,
|
|
11
|
+
saveVersion,
|
|
12
|
+
getHistory,
|
|
13
|
+
getVersion,
|
|
14
|
+
simpleDiff,
|
|
15
|
+
formatDiff,
|
|
16
|
+
revertToVersion,
|
|
17
|
+
} from "../lib/note-versioning.js";
|
|
9
18
|
|
|
10
19
|
/**
|
|
11
20
|
* Ensure the notes table exists in the database
|
|
@@ -299,4 +308,182 @@ export function registerNoteCommand(program) {
|
|
|
299
308
|
process.exit(1);
|
|
300
309
|
}
|
|
301
310
|
});
|
|
311
|
+
|
|
312
|
+
// note history
|
|
313
|
+
note
|
|
314
|
+
.command("history")
|
|
315
|
+
.description("Show version history for a note")
|
|
316
|
+
.argument("<id>", "Note ID (or prefix)")
|
|
317
|
+
.option("--json", "Output as JSON")
|
|
318
|
+
.action(async (id, options) => {
|
|
319
|
+
try {
|
|
320
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
321
|
+
if (!ctx.db) {
|
|
322
|
+
logger.error("Database not available");
|
|
323
|
+
process.exit(1);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const rawDb = ctx.db.getDatabase();
|
|
327
|
+
ensureNotesTable(rawDb);
|
|
328
|
+
ensureVersionsTable(rawDb);
|
|
329
|
+
|
|
330
|
+
// Find the note
|
|
331
|
+
const noteRow = rawDb
|
|
332
|
+
.prepare(
|
|
333
|
+
"SELECT id FROM notes WHERE id LIKE ? AND deleted_at IS NULL",
|
|
334
|
+
)
|
|
335
|
+
.get(`${id}%`);
|
|
336
|
+
|
|
337
|
+
if (!noteRow) {
|
|
338
|
+
logger.error(`Note not found: ${id}`);
|
|
339
|
+
process.exit(1);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const history = getHistory(rawDb, noteRow.id);
|
|
343
|
+
|
|
344
|
+
if (options.json) {
|
|
345
|
+
console.log(JSON.stringify(history, null, 2));
|
|
346
|
+
} else if (history.length === 0) {
|
|
347
|
+
logger.info("No version history found");
|
|
348
|
+
} else {
|
|
349
|
+
logger.log(
|
|
350
|
+
chalk.bold(`Version history (${history.length} versions):\n`),
|
|
351
|
+
);
|
|
352
|
+
for (const v of history) {
|
|
353
|
+
logger.log(
|
|
354
|
+
` ${chalk.cyan(`v${v.version}`)} ${chalk.white(v.title)} ${chalk.gray(v.change_type)} ${chalk.gray(v.created_at || "")}`,
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
await shutdown();
|
|
360
|
+
} catch (err) {
|
|
361
|
+
logger.error(`Failed to get history: ${err.message}`);
|
|
362
|
+
process.exit(1);
|
|
363
|
+
}
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
// note diff
|
|
367
|
+
note
|
|
368
|
+
.command("diff")
|
|
369
|
+
.description("Show diff between two versions of a note")
|
|
370
|
+
.argument("<id>", "Note ID (or prefix)")
|
|
371
|
+
.argument("<v1>", "First version number")
|
|
372
|
+
.argument("<v2>", "Second version number")
|
|
373
|
+
.option("--json", "Output as JSON")
|
|
374
|
+
.action(async (id, v1, v2, options) => {
|
|
375
|
+
try {
|
|
376
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
377
|
+
if (!ctx.db) {
|
|
378
|
+
logger.error("Database not available");
|
|
379
|
+
process.exit(1);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
const rawDb = ctx.db.getDatabase();
|
|
383
|
+
ensureNotesTable(rawDb);
|
|
384
|
+
ensureVersionsTable(rawDb);
|
|
385
|
+
|
|
386
|
+
const noteRow = rawDb
|
|
387
|
+
.prepare(
|
|
388
|
+
"SELECT id FROM notes WHERE id LIKE ? AND deleted_at IS NULL",
|
|
389
|
+
)
|
|
390
|
+
.get(`${id}%`);
|
|
391
|
+
|
|
392
|
+
if (!noteRow) {
|
|
393
|
+
logger.error(`Note not found: ${id}`);
|
|
394
|
+
process.exit(1);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
const ver1 = getVersion(rawDb, noteRow.id, parseInt(v1));
|
|
398
|
+
const ver2 = getVersion(rawDb, noteRow.id, parseInt(v2));
|
|
399
|
+
|
|
400
|
+
if (!ver1) {
|
|
401
|
+
logger.error(`Version ${v1} not found`);
|
|
402
|
+
process.exit(1);
|
|
403
|
+
}
|
|
404
|
+
if (!ver2) {
|
|
405
|
+
logger.error(`Version ${v2} not found`);
|
|
406
|
+
process.exit(1);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
const diff = simpleDiff(ver1.content, ver2.content);
|
|
410
|
+
|
|
411
|
+
if (options.json) {
|
|
412
|
+
console.log(
|
|
413
|
+
JSON.stringify(
|
|
414
|
+
{ v1: parseInt(v1), v2: parseInt(v2), diff },
|
|
415
|
+
null,
|
|
416
|
+
2,
|
|
417
|
+
),
|
|
418
|
+
);
|
|
419
|
+
} else {
|
|
420
|
+
logger.log(chalk.bold(`Diff: v${v1} → v${v2}\n`));
|
|
421
|
+
logger.log(formatDiff(diff));
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
await shutdown();
|
|
425
|
+
} catch (err) {
|
|
426
|
+
logger.error(`Failed to compute diff: ${err.message}`);
|
|
427
|
+
process.exit(1);
|
|
428
|
+
}
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
// note revert
|
|
432
|
+
note
|
|
433
|
+
.command("revert")
|
|
434
|
+
.description("Revert a note to a specific version")
|
|
435
|
+
.argument("<id>", "Note ID (or prefix)")
|
|
436
|
+
.argument("<version>", "Version number to revert to")
|
|
437
|
+
.option("--force", "Skip confirmation")
|
|
438
|
+
.action(async (id, version, options) => {
|
|
439
|
+
try {
|
|
440
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
441
|
+
if (!ctx.db) {
|
|
442
|
+
logger.error("Database not available");
|
|
443
|
+
process.exit(1);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
const rawDb = ctx.db.getDatabase();
|
|
447
|
+
ensureNotesTable(rawDb);
|
|
448
|
+
ensureVersionsTable(rawDb);
|
|
449
|
+
|
|
450
|
+
const noteRow = rawDb
|
|
451
|
+
.prepare(
|
|
452
|
+
"SELECT id, title FROM notes WHERE id LIKE ? AND deleted_at IS NULL",
|
|
453
|
+
)
|
|
454
|
+
.get(`${id}%`);
|
|
455
|
+
|
|
456
|
+
if (!noteRow) {
|
|
457
|
+
logger.error(`Note not found: ${id}`);
|
|
458
|
+
process.exit(1);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
if (!options.force) {
|
|
462
|
+
const { confirm } = await import("@inquirer/prompts");
|
|
463
|
+
const ok = await confirm({
|
|
464
|
+
message: `Revert note "${noteRow.title}" to version ${version}?`,
|
|
465
|
+
});
|
|
466
|
+
if (!ok) {
|
|
467
|
+
logger.info("Cancelled");
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
const result = revertToVersion(rawDb, noteRow.id, parseInt(version));
|
|
473
|
+
|
|
474
|
+
if (!result) {
|
|
475
|
+
logger.error(`Version ${version} not found or note unavailable`);
|
|
476
|
+
process.exit(1);
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
logger.success(
|
|
480
|
+
`Note reverted to v${result.reverted_to} → new version v${result.new_version}`,
|
|
481
|
+
);
|
|
482
|
+
|
|
483
|
+
await shutdown();
|
|
484
|
+
} catch (err) {
|
|
485
|
+
logger.error(`Failed to revert: ${err.message}`);
|
|
486
|
+
process.exit(1);
|
|
487
|
+
}
|
|
488
|
+
});
|
|
302
489
|
}
|
|
@@ -0,0 +1,505 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Organization & team commands
|
|
3
|
+
* chainlesschain org create|list|show|update|delete|invite|members|team|approval
|
|
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
|
+
createOrg,
|
|
11
|
+
getOrg,
|
|
12
|
+
listOrgs,
|
|
13
|
+
updateOrg,
|
|
14
|
+
deleteOrg,
|
|
15
|
+
inviteMember,
|
|
16
|
+
acceptInvite,
|
|
17
|
+
getMembers,
|
|
18
|
+
updateMemberRole,
|
|
19
|
+
removeMember,
|
|
20
|
+
createTeam,
|
|
21
|
+
listTeams,
|
|
22
|
+
addTeamMember,
|
|
23
|
+
getTeamMembers,
|
|
24
|
+
removeTeamMember,
|
|
25
|
+
deleteTeam,
|
|
26
|
+
submitApproval,
|
|
27
|
+
approveRequest,
|
|
28
|
+
rejectRequest,
|
|
29
|
+
getApprovals,
|
|
30
|
+
getOrgSummary,
|
|
31
|
+
} from "../lib/org-manager.js";
|
|
32
|
+
|
|
33
|
+
export function registerOrgCommand(program) {
|
|
34
|
+
const org = program
|
|
35
|
+
.command("org")
|
|
36
|
+
.description("Organization, team, and approval management");
|
|
37
|
+
|
|
38
|
+
// org create
|
|
39
|
+
org
|
|
40
|
+
.command("create")
|
|
41
|
+
.description("Create a new organization")
|
|
42
|
+
.argument("<name>", "Organization name")
|
|
43
|
+
.option("--owner <id>", "Owner user ID", "cli-user")
|
|
44
|
+
.option("--description <desc>", "Organization description")
|
|
45
|
+
.option("--json", "Output as JSON")
|
|
46
|
+
.action(async (name, options) => {
|
|
47
|
+
try {
|
|
48
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
49
|
+
if (!ctx.db) {
|
|
50
|
+
logger.error("Database not available");
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
const db = ctx.db.getDatabase();
|
|
54
|
+
const result = createOrg(db, name, options.owner, options.description);
|
|
55
|
+
|
|
56
|
+
if (options.json) {
|
|
57
|
+
console.log(JSON.stringify(result, null, 2));
|
|
58
|
+
} else {
|
|
59
|
+
logger.success("Organization created");
|
|
60
|
+
logger.log(` ${chalk.bold("ID:")} ${chalk.cyan(result.id)}`);
|
|
61
|
+
logger.log(` ${chalk.bold("Name:")} ${result.name}`);
|
|
62
|
+
logger.log(` ${chalk.bold("Owner:")} ${result.ownerId}`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
await shutdown();
|
|
66
|
+
} catch (err) {
|
|
67
|
+
logger.error(`Failed: ${err.message}`);
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// org list
|
|
73
|
+
org
|
|
74
|
+
.command("list", { isDefault: true })
|
|
75
|
+
.description("List all organizations")
|
|
76
|
+
.option("--json", "Output as JSON")
|
|
77
|
+
.action(async (options) => {
|
|
78
|
+
try {
|
|
79
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
80
|
+
if (!ctx.db) {
|
|
81
|
+
logger.error("Database not available");
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
const db = ctx.db.getDatabase();
|
|
85
|
+
const orgs = listOrgs(db);
|
|
86
|
+
|
|
87
|
+
if (options.json) {
|
|
88
|
+
console.log(JSON.stringify(orgs, null, 2));
|
|
89
|
+
} else if (orgs.length === 0) {
|
|
90
|
+
logger.info(
|
|
91
|
+
'No organizations. Create one with "chainlesschain org create <name>"',
|
|
92
|
+
);
|
|
93
|
+
} else {
|
|
94
|
+
logger.log(chalk.bold(`Organizations (${orgs.length}):\n`));
|
|
95
|
+
for (const o of orgs) {
|
|
96
|
+
const status =
|
|
97
|
+
o.status === "active"
|
|
98
|
+
? chalk.green("active")
|
|
99
|
+
: chalk.gray(o.status);
|
|
100
|
+
logger.log(` ${chalk.cyan(o.id)} - ${o.name} [${status}]`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
await shutdown();
|
|
105
|
+
} catch (err) {
|
|
106
|
+
logger.error(`Failed: ${err.message}`);
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// org show
|
|
112
|
+
org
|
|
113
|
+
.command("show")
|
|
114
|
+
.description("Show organization details")
|
|
115
|
+
.argument("<org-id>", "Organization ID")
|
|
116
|
+
.option("--json", "Output as JSON")
|
|
117
|
+
.action(async (orgId, options) => {
|
|
118
|
+
try {
|
|
119
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
120
|
+
if (!ctx.db) {
|
|
121
|
+
logger.error("Database not available");
|
|
122
|
+
process.exit(1);
|
|
123
|
+
}
|
|
124
|
+
const db = ctx.db.getDatabase();
|
|
125
|
+
const orgData = getOrg(db, orgId);
|
|
126
|
+
|
|
127
|
+
if (!orgData) {
|
|
128
|
+
logger.error(`Organization not found: ${orgId}`);
|
|
129
|
+
process.exit(1);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const summary = getOrgSummary(db, orgId);
|
|
133
|
+
|
|
134
|
+
if (options.json) {
|
|
135
|
+
console.log(JSON.stringify({ ...orgData, ...summary }, null, 2));
|
|
136
|
+
} else {
|
|
137
|
+
logger.log(chalk.bold("Organization:\n"));
|
|
138
|
+
logger.log(
|
|
139
|
+
` ${chalk.bold("ID:")} ${chalk.cyan(orgData.id)}`,
|
|
140
|
+
);
|
|
141
|
+
logger.log(` ${chalk.bold("Name:")} ${orgData.name}`);
|
|
142
|
+
logger.log(
|
|
143
|
+
` ${chalk.bold("Description:")} ${orgData.description || chalk.gray("(none)")}`,
|
|
144
|
+
);
|
|
145
|
+
logger.log(` ${chalk.bold("Owner:")} ${orgData.owner_id}`);
|
|
146
|
+
logger.log(` ${chalk.bold("Members:")} ${summary.memberCount}`);
|
|
147
|
+
logger.log(` ${chalk.bold("Teams:")} ${summary.teamCount}`);
|
|
148
|
+
logger.log(
|
|
149
|
+
` ${chalk.bold("Pending:")} ${summary.pendingApprovals}`,
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
await shutdown();
|
|
154
|
+
} catch (err) {
|
|
155
|
+
logger.error(`Failed: ${err.message}`);
|
|
156
|
+
process.exit(1);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// org delete
|
|
161
|
+
org
|
|
162
|
+
.command("delete")
|
|
163
|
+
.description("Delete an organization")
|
|
164
|
+
.argument("<org-id>", "Organization ID")
|
|
165
|
+
.option("--force", "Skip confirmation")
|
|
166
|
+
.action(async (orgId, options) => {
|
|
167
|
+
try {
|
|
168
|
+
if (!options.force) {
|
|
169
|
+
const { confirm } = await import("@inquirer/prompts");
|
|
170
|
+
const ok = await confirm({
|
|
171
|
+
message: "Delete this organization and all its data?",
|
|
172
|
+
});
|
|
173
|
+
if (!ok) {
|
|
174
|
+
logger.info("Cancelled");
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
180
|
+
if (!ctx.db) {
|
|
181
|
+
logger.error("Database not available");
|
|
182
|
+
process.exit(1);
|
|
183
|
+
}
|
|
184
|
+
const db = ctx.db.getDatabase();
|
|
185
|
+
const ok = deleteOrg(db, orgId);
|
|
186
|
+
|
|
187
|
+
if (ok) {
|
|
188
|
+
logger.success("Organization deleted");
|
|
189
|
+
} else {
|
|
190
|
+
logger.error(`Organization not found: ${orgId}`);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
await shutdown();
|
|
194
|
+
} catch (err) {
|
|
195
|
+
logger.error(`Failed: ${err.message}`);
|
|
196
|
+
process.exit(1);
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
// org invite
|
|
201
|
+
org
|
|
202
|
+
.command("invite")
|
|
203
|
+
.description("Invite a member to an organization")
|
|
204
|
+
.argument("<org-id>", "Organization ID")
|
|
205
|
+
.argument("<user-id>", "User ID to invite")
|
|
206
|
+
.option("--name <name>", "Display name")
|
|
207
|
+
.option("--role <role>", "Role (admin/member/viewer)", "member")
|
|
208
|
+
.action(async (orgId, userId, options) => {
|
|
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
|
+
const result = inviteMember(
|
|
217
|
+
db,
|
|
218
|
+
orgId,
|
|
219
|
+
userId,
|
|
220
|
+
options.name,
|
|
221
|
+
options.role,
|
|
222
|
+
);
|
|
223
|
+
logger.success(`Invited ${userId} as ${result.role}`);
|
|
224
|
+
await shutdown();
|
|
225
|
+
} catch (err) {
|
|
226
|
+
logger.error(`Failed: ${err.message}`);
|
|
227
|
+
process.exit(1);
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
// org members
|
|
232
|
+
org
|
|
233
|
+
.command("members")
|
|
234
|
+
.description("List organization members")
|
|
235
|
+
.argument("<org-id>", "Organization ID")
|
|
236
|
+
.option("--json", "Output as JSON")
|
|
237
|
+
.action(async (orgId, options) => {
|
|
238
|
+
try {
|
|
239
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
240
|
+
if (!ctx.db) {
|
|
241
|
+
logger.error("Database not available");
|
|
242
|
+
process.exit(1);
|
|
243
|
+
}
|
|
244
|
+
const db = ctx.db.getDatabase();
|
|
245
|
+
const members = getMembers(db, orgId);
|
|
246
|
+
|
|
247
|
+
if (options.json) {
|
|
248
|
+
console.log(JSON.stringify(members, null, 2));
|
|
249
|
+
} else if (members.length === 0) {
|
|
250
|
+
logger.info("No members");
|
|
251
|
+
} else {
|
|
252
|
+
logger.log(chalk.bold(`Members (${members.length}):\n`));
|
|
253
|
+
for (const m of members) {
|
|
254
|
+
const role =
|
|
255
|
+
m.role === "admin" ? chalk.yellow(m.role) : chalk.gray(m.role);
|
|
256
|
+
const status =
|
|
257
|
+
m.status === "active"
|
|
258
|
+
? chalk.green("active")
|
|
259
|
+
: chalk.gray(m.status);
|
|
260
|
+
logger.log(` ${m.user_id} [${role}] ${status}`);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
await shutdown();
|
|
265
|
+
} catch (err) {
|
|
266
|
+
logger.error(`Failed: ${err.message}`);
|
|
267
|
+
process.exit(1);
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
// org team create
|
|
272
|
+
org
|
|
273
|
+
.command("team-create")
|
|
274
|
+
.description("Create a team")
|
|
275
|
+
.argument("<org-id>", "Organization ID")
|
|
276
|
+
.argument("<team-name>", "Team name")
|
|
277
|
+
.option("--description <desc>", "Team description")
|
|
278
|
+
.option("--lead <id>", "Team lead user ID")
|
|
279
|
+
.option("--json", "Output as JSON")
|
|
280
|
+
.action(async (orgId, teamName, options) => {
|
|
281
|
+
try {
|
|
282
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
283
|
+
if (!ctx.db) {
|
|
284
|
+
logger.error("Database not available");
|
|
285
|
+
process.exit(1);
|
|
286
|
+
}
|
|
287
|
+
const db = ctx.db.getDatabase();
|
|
288
|
+
const team = createTeam(
|
|
289
|
+
db,
|
|
290
|
+
orgId,
|
|
291
|
+
teamName,
|
|
292
|
+
options.description,
|
|
293
|
+
options.lead,
|
|
294
|
+
);
|
|
295
|
+
|
|
296
|
+
if (options.json) {
|
|
297
|
+
console.log(JSON.stringify(team, null, 2));
|
|
298
|
+
} else {
|
|
299
|
+
logger.success("Team created");
|
|
300
|
+
logger.log(` ${chalk.bold("ID:")} ${chalk.cyan(team.id)}`);
|
|
301
|
+
logger.log(` ${chalk.bold("Name:")} ${team.name}`);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
await shutdown();
|
|
305
|
+
} catch (err) {
|
|
306
|
+
logger.error(`Failed: ${err.message}`);
|
|
307
|
+
process.exit(1);
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
// org teams
|
|
312
|
+
org
|
|
313
|
+
.command("teams")
|
|
314
|
+
.description("List teams in an organization")
|
|
315
|
+
.argument("<org-id>", "Organization ID")
|
|
316
|
+
.option("--json", "Output as JSON")
|
|
317
|
+
.action(async (orgId, options) => {
|
|
318
|
+
try {
|
|
319
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
320
|
+
if (!ctx.db) {
|
|
321
|
+
logger.error("Database not available");
|
|
322
|
+
process.exit(1);
|
|
323
|
+
}
|
|
324
|
+
const db = ctx.db.getDatabase();
|
|
325
|
+
const teams = listTeams(db, orgId);
|
|
326
|
+
|
|
327
|
+
if (options.json) {
|
|
328
|
+
console.log(JSON.stringify(teams, null, 2));
|
|
329
|
+
} else if (teams.length === 0) {
|
|
330
|
+
logger.info("No teams");
|
|
331
|
+
} else {
|
|
332
|
+
logger.log(chalk.bold(`Teams (${teams.length}):\n`));
|
|
333
|
+
for (const t of teams) {
|
|
334
|
+
logger.log(` ${chalk.cyan(t.id)} - ${t.name}`);
|
|
335
|
+
if (t.description) logger.log(` ${chalk.gray(t.description)}`);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
await shutdown();
|
|
340
|
+
} catch (err) {
|
|
341
|
+
logger.error(`Failed: ${err.message}`);
|
|
342
|
+
process.exit(1);
|
|
343
|
+
}
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
// org approval submit
|
|
347
|
+
org
|
|
348
|
+
.command("approval-submit")
|
|
349
|
+
.description("Submit an approval request")
|
|
350
|
+
.argument("<org-id>", "Organization ID")
|
|
351
|
+
.argument("<title>", "Request title")
|
|
352
|
+
.option("--type <type>", "Request type", "general")
|
|
353
|
+
.option("--description <desc>", "Request description")
|
|
354
|
+
.option("--requester <id>", "Requester ID", "cli-user")
|
|
355
|
+
.option("--json", "Output as JSON")
|
|
356
|
+
.action(async (orgId, title, options) => {
|
|
357
|
+
try {
|
|
358
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
359
|
+
if (!ctx.db) {
|
|
360
|
+
logger.error("Database not available");
|
|
361
|
+
process.exit(1);
|
|
362
|
+
}
|
|
363
|
+
const db = ctx.db.getDatabase();
|
|
364
|
+
const result = submitApproval(
|
|
365
|
+
db,
|
|
366
|
+
orgId,
|
|
367
|
+
options.requester,
|
|
368
|
+
options.type,
|
|
369
|
+
title,
|
|
370
|
+
options.description,
|
|
371
|
+
);
|
|
372
|
+
|
|
373
|
+
if (options.json) {
|
|
374
|
+
console.log(JSON.stringify(result, null, 2));
|
|
375
|
+
} else {
|
|
376
|
+
logger.success("Approval request submitted");
|
|
377
|
+
logger.log(` ${chalk.bold("ID:")} ${chalk.cyan(result.id)}`);
|
|
378
|
+
logger.log(
|
|
379
|
+
` ${chalk.bold("Status:")} ${chalk.yellow(result.status)}`,
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
await shutdown();
|
|
384
|
+
} catch (err) {
|
|
385
|
+
logger.error(`Failed: ${err.message}`);
|
|
386
|
+
process.exit(1);
|
|
387
|
+
}
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
// org approval list
|
|
391
|
+
org
|
|
392
|
+
.command("approvals")
|
|
393
|
+
.description("List approval requests")
|
|
394
|
+
.option("--org <id>", "Filter by organization")
|
|
395
|
+
.option("--status <status>", "Filter by status (pending/approved/rejected)")
|
|
396
|
+
.option("--json", "Output as JSON")
|
|
397
|
+
.action(async (options) => {
|
|
398
|
+
try {
|
|
399
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
400
|
+
if (!ctx.db) {
|
|
401
|
+
logger.error("Database not available");
|
|
402
|
+
process.exit(1);
|
|
403
|
+
}
|
|
404
|
+
const db = ctx.db.getDatabase();
|
|
405
|
+
const approvals = getApprovals(db, {
|
|
406
|
+
orgId: options.org,
|
|
407
|
+
status: options.status,
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
if (options.json) {
|
|
411
|
+
console.log(JSON.stringify(approvals, null, 2));
|
|
412
|
+
} else if (approvals.length === 0) {
|
|
413
|
+
logger.info("No approval requests");
|
|
414
|
+
} else {
|
|
415
|
+
logger.log(chalk.bold(`Approvals (${approvals.length}):\n`));
|
|
416
|
+
for (const a of approvals) {
|
|
417
|
+
const statusColor =
|
|
418
|
+
a.status === "approved"
|
|
419
|
+
? chalk.green
|
|
420
|
+
: a.status === "rejected"
|
|
421
|
+
? chalk.red
|
|
422
|
+
: chalk.yellow;
|
|
423
|
+
logger.log(
|
|
424
|
+
` ${chalk.cyan(a.id)} ${a.title} [${statusColor(a.status)}]`,
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
await shutdown();
|
|
430
|
+
} catch (err) {
|
|
431
|
+
logger.error(`Failed: ${err.message}`);
|
|
432
|
+
process.exit(1);
|
|
433
|
+
}
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
// org approval approve
|
|
437
|
+
org
|
|
438
|
+
.command("approve")
|
|
439
|
+
.description("Approve a request")
|
|
440
|
+
.argument("<approval-id>", "Approval request ID")
|
|
441
|
+
.option("--approver <id>", "Approver ID", "cli-user")
|
|
442
|
+
.option("--note <note>", "Decision note")
|
|
443
|
+
.action(async (approvalId, options) => {
|
|
444
|
+
try {
|
|
445
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
446
|
+
if (!ctx.db) {
|
|
447
|
+
logger.error("Database not available");
|
|
448
|
+
process.exit(1);
|
|
449
|
+
}
|
|
450
|
+
const db = ctx.db.getDatabase();
|
|
451
|
+
const ok = approveRequest(
|
|
452
|
+
db,
|
|
453
|
+
approvalId,
|
|
454
|
+
options.approver,
|
|
455
|
+
options.note,
|
|
456
|
+
);
|
|
457
|
+
|
|
458
|
+
if (ok) {
|
|
459
|
+
logger.success("Request approved");
|
|
460
|
+
} else {
|
|
461
|
+
logger.error(`Request not found: ${approvalId}`);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
await shutdown();
|
|
465
|
+
} catch (err) {
|
|
466
|
+
logger.error(`Failed: ${err.message}`);
|
|
467
|
+
process.exit(1);
|
|
468
|
+
}
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
// org approval reject
|
|
472
|
+
org
|
|
473
|
+
.command("reject")
|
|
474
|
+
.description("Reject a request")
|
|
475
|
+
.argument("<approval-id>", "Approval request ID")
|
|
476
|
+
.option("--approver <id>", "Approver ID", "cli-user")
|
|
477
|
+
.option("--note <note>", "Decision note")
|
|
478
|
+
.action(async (approvalId, options) => {
|
|
479
|
+
try {
|
|
480
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
481
|
+
if (!ctx.db) {
|
|
482
|
+
logger.error("Database not available");
|
|
483
|
+
process.exit(1);
|
|
484
|
+
}
|
|
485
|
+
const db = ctx.db.getDatabase();
|
|
486
|
+
const ok = rejectRequest(
|
|
487
|
+
db,
|
|
488
|
+
approvalId,
|
|
489
|
+
options.approver,
|
|
490
|
+
options.note,
|
|
491
|
+
);
|
|
492
|
+
|
|
493
|
+
if (ok) {
|
|
494
|
+
logger.success("Request rejected");
|
|
495
|
+
} else {
|
|
496
|
+
logger.error(`Request not found: ${approvalId}`);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
await shutdown();
|
|
500
|
+
} catch (err) {
|
|
501
|
+
logger.error(`Failed: ${err.message}`);
|
|
502
|
+
process.exit(1);
|
|
503
|
+
}
|
|
504
|
+
});
|
|
505
|
+
}
|