chainlesschain 0.37.8 → 0.37.10
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 +403 -8
- package/bin/chainlesschain.js +4 -0
- package/package.json +7 -2
- package/src/commands/agent.js +30 -0
- package/src/commands/ask.js +114 -0
- package/src/commands/audit.js +286 -0
- package/src/commands/auth.js +387 -0
- package/src/commands/browse.js +184 -0
- package/src/commands/chat.js +35 -0
- package/src/commands/db.js +152 -0
- package/src/commands/did.js +376 -0
- package/src/commands/encrypt.js +233 -0
- package/src/commands/export.js +125 -0
- package/src/commands/git.js +215 -0
- package/src/commands/import.js +259 -0
- package/src/commands/instinct.js +202 -0
- package/src/commands/llm.js +288 -0
- package/src/commands/mcp.js +302 -0
- package/src/commands/memory.js +282 -0
- package/src/commands/note.js +489 -0
- package/src/commands/org.js +505 -0
- package/src/commands/p2p.js +274 -0
- package/src/commands/plugin.js +398 -0
- package/src/commands/search.js +237 -0
- package/src/commands/session.js +238 -0
- package/src/commands/skill.js +479 -0
- package/src/commands/sync.js +249 -0
- package/src/commands/tokens.js +214 -0
- package/src/commands/wallet.js +416 -0
- package/src/index.js +65 -0
- package/src/lib/audit-logger.js +364 -0
- package/src/lib/bm25-search.js +322 -0
- package/src/lib/browser-automation.js +216 -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/git-integration.js +220 -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/platform.js +15 -0
- package/src/lib/plugin-manager.js +312 -0
- package/src/lib/response-cache.js +156 -0
- package/src/lib/session-manager.js +189 -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/repl/agent-repl.js +912 -0
- package/src/repl/chat-repl.js +262 -0
- package/src/runtime/bootstrap.js +159 -0
|
@@ -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
|
+
}
|