@j-256/ccam 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +209 -0
  3. package/dist/auth/browser-login.d.ts +14 -0
  4. package/dist/auth/browser-login.js +72 -0
  5. package/dist/auth/manual-login.d.ts +10 -0
  6. package/dist/auth/manual-login.js +33 -0
  7. package/dist/auth/paths.d.ts +4 -0
  8. package/dist/auth/paths.js +15 -0
  9. package/dist/auth/profile-resolver.d.ts +26 -0
  10. package/dist/auth/profile-resolver.js +42 -0
  11. package/dist/auth/profile-store.d.ts +38 -0
  12. package/dist/auth/profile-store.js +125 -0
  13. package/dist/auth/prompt.d.ts +9 -0
  14. package/dist/auth/prompt.js +70 -0
  15. package/dist/bin.d.ts +3 -0
  16. package/dist/bin.js +4 -0
  17. package/dist/client-factory.d.ts +6 -0
  18. package/dist/client-factory.js +40 -0
  19. package/dist/commands/auth.d.ts +77 -0
  20. package/dist/commands/auth.js +387 -0
  21. package/dist/commands/client.d.ts +3 -0
  22. package/dist/commands/client.js +365 -0
  23. package/dist/commands/instance.d.ts +11 -0
  24. package/dist/commands/instance.js +128 -0
  25. package/dist/commands/org-config.d.ts +3 -0
  26. package/dist/commands/org-config.js +31 -0
  27. package/dist/commands/org.d.ts +11 -0
  28. package/dist/commands/org.js +234 -0
  29. package/dist/commands/permission.d.ts +3 -0
  30. package/dist/commands/permission.js +60 -0
  31. package/dist/commands/realm.d.ts +3 -0
  32. package/dist/commands/realm.js +58 -0
  33. package/dist/commands/role.d.ts +3 -0
  34. package/dist/commands/role.js +77 -0
  35. package/dist/commands/service-type.d.ts +3 -0
  36. package/dist/commands/service-type.js +57 -0
  37. package/dist/commands/user.d.ts +14 -0
  38. package/dist/commands/user.js +573 -0
  39. package/dist/error-handler.d.ts +2 -0
  40. package/dist/error-handler.js +28 -0
  41. package/dist/index.d.ts +2 -0
  42. package/dist/index.js +2 -0
  43. package/dist/output/csv.d.ts +3 -0
  44. package/dist/output/csv.js +57 -0
  45. package/dist/output/default-columns.d.ts +2 -0
  46. package/dist/output/default-columns.js +16 -0
  47. package/dist/output/detect.d.ts +4 -0
  48. package/dist/output/detect.js +6 -0
  49. package/dist/output/index.d.ts +15 -0
  50. package/dist/output/index.js +34 -0
  51. package/dist/output/json.d.ts +2 -0
  52. package/dist/output/json.js +10 -0
  53. package/dist/output/shared.d.ts +13 -0
  54. package/dist/output/shared.js +41 -0
  55. package/dist/output/table.d.ts +2 -0
  56. package/dist/output/table.js +72 -0
  57. package/dist/output/types.d.ts +2 -0
  58. package/dist/output/types.js +2 -0
  59. package/dist/output/yaml-fmt.d.ts +2 -0
  60. package/dist/output/yaml-fmt.js +11 -0
  61. package/dist/program.d.ts +3 -0
  62. package/dist/program.js +37 -0
  63. package/dist/shared.d.ts +46 -0
  64. package/dist/shared.js +96 -0
  65. package/dist/tui/App.d.ts +7 -0
  66. package/dist/tui/App.js +30 -0
  67. package/dist/tui/components/AuditTab.d.ts +8 -0
  68. package/dist/tui/components/AuditTab.js +80 -0
  69. package/dist/tui/components/FooterBar.d.ts +17 -0
  70. package/dist/tui/components/FooterBar.js +23 -0
  71. package/dist/tui/components/FullScreenLayout.d.ts +6 -0
  72. package/dist/tui/components/FullScreenLayout.js +11 -0
  73. package/dist/tui/components/HeaderBar.d.ts +5 -0
  74. package/dist/tui/components/HeaderBar.js +10 -0
  75. package/dist/tui/components/InfoTab.d.ts +8 -0
  76. package/dist/tui/components/InfoTab.js +70 -0
  77. package/dist/tui/components/ResourcePicker.d.ts +10 -0
  78. package/dist/tui/components/ResourcePicker.js +36 -0
  79. package/dist/tui/components/SubResourceTab.d.ts +7 -0
  80. package/dist/tui/components/SubResourceTab.js +193 -0
  81. package/dist/tui/components/TabBar.d.ts +11 -0
  82. package/dist/tui/components/TabBar.js +13 -0
  83. package/dist/tui/components/Table.d.ts +32 -0
  84. package/dist/tui/components/Table.js +175 -0
  85. package/dist/tui/context/client.d.ts +7 -0
  86. package/dist/tui/context/client.js +14 -0
  87. package/dist/tui/context/navigation.d.ts +14 -0
  88. package/dist/tui/context/navigation.js +25 -0
  89. package/dist/tui/context/terminal-size.d.ts +9 -0
  90. package/dist/tui/context/terminal-size.js +26 -0
  91. package/dist/tui/format.d.ts +20 -0
  92. package/dist/tui/format.js +57 -0
  93. package/dist/tui/hooks/use-audit-log.d.ts +12 -0
  94. package/dist/tui/hooks/use-audit-log.js +71 -0
  95. package/dist/tui/hooks/use-local-collection.d.ts +8 -0
  96. package/dist/tui/hooks/use-local-collection.js +30 -0
  97. package/dist/tui/hooks/use-paginated-resource.d.ts +23 -0
  98. package/dist/tui/hooks/use-paginated-resource.js +115 -0
  99. package/dist/tui/hooks/use-resource-detail.d.ts +7 -0
  100. package/dist/tui/hooks/use-resource-detail.js +30 -0
  101. package/dist/tui/hooks/use-scroll-window.d.ts +7 -0
  102. package/dist/tui/hooks/use-scroll-window.js +29 -0
  103. package/dist/tui/index.d.ts +2 -0
  104. package/dist/tui/index.js +22 -0
  105. package/dist/tui/navigation.d.ts +11 -0
  106. package/dist/tui/navigation.js +29 -0
  107. package/dist/tui/resource-configs/api-clients.d.ts +3 -0
  108. package/dist/tui/resource-configs/api-clients.js +118 -0
  109. package/dist/tui/resource-configs/index.d.ts +14 -0
  110. package/dist/tui/resource-configs/index.js +28 -0
  111. package/dist/tui/resource-configs/instances.d.ts +3 -0
  112. package/dist/tui/resource-configs/instances.js +24 -0
  113. package/dist/tui/resource-configs/org-configuration.d.ts +3 -0
  114. package/dist/tui/resource-configs/org-configuration.js +28 -0
  115. package/dist/tui/resource-configs/organizations.d.ts +3 -0
  116. package/dist/tui/resource-configs/organizations.js +104 -0
  117. package/dist/tui/resource-configs/permissions.d.ts +3 -0
  118. package/dist/tui/resource-configs/permissions.js +25 -0
  119. package/dist/tui/resource-configs/realms.d.ts +3 -0
  120. package/dist/tui/resource-configs/realms.js +36 -0
  121. package/dist/tui/resource-configs/roles.d.ts +3 -0
  122. package/dist/tui/resource-configs/roles.js +56 -0
  123. package/dist/tui/resource-configs/service-types.d.ts +3 -0
  124. package/dist/tui/resource-configs/service-types.js +24 -0
  125. package/dist/tui/resource-configs/users.d.ts +3 -0
  126. package/dist/tui/resource-configs/users.js +126 -0
  127. package/dist/tui/types.d.ts +99 -0
  128. package/dist/tui/types.js +23 -0
  129. package/dist/tui/views/ResourceDetailView.d.ts +7 -0
  130. package/dist/tui/views/ResourceDetailView.js +123 -0
  131. package/dist/tui/views/ResourceListView.d.ts +6 -0
  132. package/dist/tui/views/ResourceListView.js +140 -0
  133. package/dist/tui/views/ViewRouter.d.ts +2 -0
  134. package/dist/tui/views/ViewRouter.js +60 -0
  135. package/package.json +62 -0
@@ -0,0 +1,573 @@
1
+ import { addGlobalOptions, parseExpand, resolveGlobalOptions, writePageInfoIfTable } from '../shared.js';
2
+ import { resolveProfile } from '../auth/profile-resolver.js';
3
+ import { createClientFromResolved } from '../client-factory.js';
4
+ import { handleError } from '../error-handler.js';
5
+ import { renderOutput, resolveFormat } from '../output/index.js';
6
+ import { DEFAULT_COLUMNS } from '../output/default-columns.js';
7
+ const USER_GET_EXPAND = ['organizations', 'roles', 'organizations,roles'];
8
+ const USER_GET_BY_LOGIN_EXPAND = ['organizations'];
9
+ export function selectUserFinder(filters) {
10
+ // Check for specific combinations
11
+ if (filters.org && filters.role) {
12
+ return 'findByOrgAndRole';
13
+ }
14
+ if (filters.orgRealmAccess) {
15
+ return 'findByOrgRealmAccess';
16
+ }
17
+ if (filters.role) {
18
+ return 'findByRole';
19
+ }
20
+ if (filters.org) {
21
+ return filters.all ? 'findAllByOrg' : 'findByOrg';
22
+ }
23
+ if (filters.login) {
24
+ return 'findByLogin';
25
+ }
26
+ return 'list';
27
+ }
28
+ async function listUsers(options) {
29
+ try {
30
+ const resolved = resolveGlobalOptions(options);
31
+ // Validate modifiedAfter requires role
32
+ if (options.modifiedAfter && !options.role) {
33
+ process.stderr.write('Error: --modified-after requires --role\n');
34
+ process.exit(1);
35
+ }
36
+ // Validate --all requires --org
37
+ if (options.all && !options.org) {
38
+ process.stderr.write('Error: --all requires --org\n');
39
+ process.exit(1);
40
+ }
41
+ const profileResolved = await resolveProfile({
42
+ flags: { profile: options.profile, host: resolved.host },
43
+ });
44
+ const client = await createClientFromResolved(profileResolved);
45
+ const filters = {
46
+ login: options.login,
47
+ org: options.org,
48
+ role: options.role,
49
+ orgRealmAccess: options.orgRealmAccess,
50
+ modifiedAfter: options.modifiedAfter,
51
+ all: options.all,
52
+ };
53
+ const finder = selectUserFinder(filters);
54
+ let result;
55
+ switch (finder) {
56
+ case 'list':
57
+ result = await client.users.list({
58
+ page: resolved.page,
59
+ size: resolved.size,
60
+ sort: resolved.sort,
61
+ });
62
+ break;
63
+ case 'findByLogin':
64
+ result = await client.users.getByLogin(filters.login);
65
+ break;
66
+ case 'findByOrg':
67
+ result = await client.users.search.findByOrg({
68
+ organization: filters.org,
69
+ page: resolved.page,
70
+ size: resolved.size,
71
+ });
72
+ break;
73
+ case 'findAllByOrg':
74
+ result = await client.users.search.findAllByOrg({
75
+ organization: filters.org,
76
+ page: resolved.page,
77
+ size: resolved.size,
78
+ });
79
+ break;
80
+ case 'findByRole':
81
+ result = await client.users.search.findByRole({
82
+ role: filters.role,
83
+ modifiedAfter: filters.modifiedAfter,
84
+ page: resolved.page,
85
+ size: resolved.size,
86
+ });
87
+ break;
88
+ case 'findByOrgAndRole':
89
+ result = await client.users.search.findByOrgAndRole({
90
+ organization: filters.org,
91
+ role: filters.role,
92
+ page: resolved.page,
93
+ size: resolved.size,
94
+ });
95
+ break;
96
+ case 'findByOrgRealmAccess':
97
+ result = await client.users.search.findByOrgRealmAccess({
98
+ organization: filters.orgRealmAccess,
99
+ page: resolved.page,
100
+ size: resolved.size,
101
+ });
102
+ break;
103
+ }
104
+ const format = resolveFormat(resolved.format, process.stdout.isTTY);
105
+ const data = result && typeof result === 'object' && 'content' in result ? result.content : result;
106
+ renderOutput(data, { format, fields: resolved.fields, defaultFields: DEFAULT_COLUMNS.user });
107
+ writePageInfoIfTable(format, result);
108
+ }
109
+ catch (err) {
110
+ handleError(err);
111
+ }
112
+ }
113
+ async function getUser(loginOrId, options) {
114
+ try {
115
+ const resolved = resolveGlobalOptions(options);
116
+ const profileResolved = await resolveProfile({
117
+ flags: { profile: options.profile, host: resolved.host },
118
+ });
119
+ const client = await createClientFromResolved(profileResolved);
120
+ let result;
121
+ if (options.id) {
122
+ const expand = parseExpand(options.expand, USER_GET_EXPAND);
123
+ // Branch on the literal expand value so TypeScript can pick the right overload.
124
+ if (expand === 'organizations') {
125
+ result = await client.users.get(loginOrId, { expand });
126
+ }
127
+ else if (expand === 'roles') {
128
+ result = await client.users.get(loginOrId, { expand });
129
+ }
130
+ else if (expand === 'organizations,roles') {
131
+ result = await client.users.get(loginOrId, { expand });
132
+ }
133
+ else {
134
+ result = await client.users.get(loginOrId);
135
+ }
136
+ }
137
+ else {
138
+ const expand = parseExpand(options.expand, USER_GET_BY_LOGIN_EXPAND);
139
+ result = expand ? await client.users.getByLogin(loginOrId, { expand }) : await client.users.getByLogin(loginOrId);
140
+ }
141
+ const format = resolveFormat(resolved.format, process.stdout.isTTY);
142
+ renderOutput(result, { format, fields: resolved.fields, defaultFields: DEFAULT_COLUMNS.userDetail });
143
+ }
144
+ catch (err) {
145
+ handleError(err);
146
+ }
147
+ }
148
+ async function currentUser(options) {
149
+ try {
150
+ const resolved = resolveGlobalOptions(options);
151
+ const profileResolved = await resolveProfile({
152
+ flags: { profile: options.profile, host: resolved.host },
153
+ });
154
+ const client = await createClientFromResolved(profileResolved);
155
+ const expand = parseExpand(options.expand, USER_GET_EXPAND);
156
+ // Branch on the literal expand value so TypeScript can pick the right overload.
157
+ let result;
158
+ if (expand === 'organizations') {
159
+ result = await client.users.current({ expand });
160
+ }
161
+ else if (expand === 'roles') {
162
+ result = await client.users.current({ expand });
163
+ }
164
+ else if (expand === 'organizations,roles') {
165
+ result = await client.users.current({ expand });
166
+ }
167
+ else {
168
+ result = await client.users.current();
169
+ }
170
+ const format = resolveFormat(resolved.format, process.stdout.isTTY);
171
+ renderOutput(result, { format, fields: resolved.fields, defaultFields: DEFAULT_COLUMNS.userDetail });
172
+ }
173
+ catch (err) {
174
+ handleError(err);
175
+ }
176
+ }
177
+ async function auditUser(login, options) {
178
+ try {
179
+ const resolved = resolveGlobalOptions(options);
180
+ const profileResolved = await resolveProfile({
181
+ flags: { profile: options.profile, host: resolved.host },
182
+ });
183
+ const client = await createClientFromResolved(profileResolved);
184
+ // Resolve login to ID first
185
+ const user = await client.users.getByLogin(login);
186
+ const querySize = options.querySize !== undefined ? parseInt(options.querySize, 10) : undefined;
187
+ const result = await client.users.auditLogs(user.id, querySize !== undefined ? { querySize } : undefined);
188
+ const format = resolveFormat(resolved.format, process.stdout.isTTY);
189
+ renderOutput(result.content, { format, fields: resolved.fields, defaultFields: DEFAULT_COLUMNS.auditLog });
190
+ }
191
+ catch (err) {
192
+ handleError(err);
193
+ }
194
+ }
195
+ async function userRoles(login, options) {
196
+ try {
197
+ const resolved = resolveGlobalOptions(options);
198
+ const profileResolved = await resolveProfile({
199
+ flags: { profile: options.profile, host: resolved.host },
200
+ });
201
+ const client = await createClientFromResolved(profileResolved);
202
+ const user = await client.users.getByLogin(login);
203
+ const result = await client.users.roles(user.id);
204
+ const format = resolveFormat(resolved.format, process.stdout.isTTY);
205
+ renderOutput(result.content, { format, fields: resolved.fields, defaultFields: DEFAULT_COLUMNS.role });
206
+ }
207
+ catch (err) {
208
+ handleError(err);
209
+ }
210
+ }
211
+ async function userInstances(login, options) {
212
+ try {
213
+ const resolved = resolveGlobalOptions(options);
214
+ const profileResolved = await resolveProfile({
215
+ flags: { profile: options.profile, host: resolved.host },
216
+ });
217
+ const client = await createClientFromResolved(profileResolved);
218
+ const user = await client.users.getByLogin(login);
219
+ const result = await client.users.instances(user.id);
220
+ const format = resolveFormat(resolved.format, process.stdout.isTTY);
221
+ renderOutput(result.content, { format, fields: resolved.fields, defaultFields: DEFAULT_COLUMNS.instance });
222
+ }
223
+ catch (err) {
224
+ handleError(err);
225
+ }
226
+ }
227
+ async function userAssignedRealms(login, options) {
228
+ try {
229
+ const resolved = resolveGlobalOptions(options);
230
+ const profileResolved = await resolveProfile({
231
+ flags: { profile: options.profile, host: resolved.host },
232
+ });
233
+ const client = await createClientFromResolved(profileResolved);
234
+ const user = await client.users.getByLogin(login);
235
+ const result = await client.users.assignedRealms(user.id);
236
+ const format = resolveFormat(resolved.format, process.stdout.isTTY);
237
+ renderOutput(result.content, { format, fields: resolved.fields, defaultFields: DEFAULT_COLUMNS.realm });
238
+ }
239
+ catch (err) {
240
+ handleError(err);
241
+ }
242
+ }
243
+ async function userAssignedInstances(login, options) {
244
+ try {
245
+ const resolved = resolveGlobalOptions(options);
246
+ const profileResolved = await resolveProfile({
247
+ flags: { profile: options.profile, host: resolved.host },
248
+ });
249
+ const client = await createClientFromResolved(profileResolved);
250
+ const user = await client.users.getByLogin(login);
251
+ const result = await client.users.assignedInstances(user.id);
252
+ const format = resolveFormat(resolved.format, process.stdout.isTTY);
253
+ renderOutput(result.content, { format, fields: resolved.fields, defaultFields: DEFAULT_COLUMNS.instance });
254
+ }
255
+ catch (err) {
256
+ handleError(err);
257
+ }
258
+ }
259
+ async function createUser(options) {
260
+ try {
261
+ const resolved = resolveGlobalOptions(options);
262
+ const profileResolved = await resolveProfile({
263
+ flags: { profile: options.profile, host: resolved.host },
264
+ });
265
+ const client = await createClientFromResolved(profileResolved);
266
+ const data = {
267
+ mail: options.mail,
268
+ firstName: options.firstName,
269
+ lastName: options.lastName,
270
+ primaryOrganization: options.primaryOrg,
271
+ };
272
+ if (options.displayName !== undefined)
273
+ data.displayName = options.displayName;
274
+ if (options.roles)
275
+ data.roles = options.roles.split(',');
276
+ if (options.organizations)
277
+ data.organizations = options.organizations.split(',');
278
+ const result = await client.users.create(data);
279
+ const format = resolveFormat(resolved.format, process.stdout.isTTY);
280
+ renderOutput(result, {
281
+ format,
282
+ fields: resolved.fields,
283
+ defaultFields: DEFAULT_COLUMNS.userDetail,
284
+ });
285
+ }
286
+ catch (err) {
287
+ handleError(err);
288
+ }
289
+ }
290
+ async function updateUser(loginOrId, options) {
291
+ try {
292
+ const resolved = resolveGlobalOptions(options);
293
+ const profileResolved = await resolveProfile({
294
+ flags: { profile: options.profile, host: resolved.host },
295
+ });
296
+ const client = await createClientFromResolved(profileResolved);
297
+ const userId = options.id ? loginOrId : (await client.users.getByLogin(loginOrId)).id;
298
+ const data = {};
299
+ if (options.mail !== undefined)
300
+ data.mail = options.mail;
301
+ if (options.firstName !== undefined)
302
+ data.firstName = options.firstName;
303
+ if (options.lastName !== undefined)
304
+ data.lastName = options.lastName;
305
+ if (options.displayName !== undefined)
306
+ data.displayName = options.displayName;
307
+ if (options.primaryOrg !== undefined)
308
+ data.primaryOrganization = options.primaryOrg;
309
+ if (options.roles)
310
+ data.roles = options.roles.split(',');
311
+ if (options.organizations)
312
+ data.organizations = options.organizations.split(',');
313
+ const result = await client.users.update(userId, data);
314
+ const format = resolveFormat(resolved.format, process.stdout.isTTY);
315
+ renderOutput(result, {
316
+ format,
317
+ fields: resolved.fields,
318
+ defaultFields: DEFAULT_COLUMNS.userDetail,
319
+ });
320
+ }
321
+ catch (err) {
322
+ handleError(err);
323
+ }
324
+ }
325
+ async function deleteUser(loginOrId, options) {
326
+ try {
327
+ const resolved = resolveGlobalOptions(options);
328
+ const profileResolved = await resolveProfile({
329
+ flags: { profile: options.profile, host: resolved.host },
330
+ });
331
+ const client = await createClientFromResolved(profileResolved);
332
+ const userId = options.id ? loginOrId : (await client.users.getByLogin(loginOrId)).id;
333
+ await client.users.delete(userId);
334
+ process.stderr.write(`Deleted user ${userId}\n`);
335
+ }
336
+ catch (err) {
337
+ handleError(err);
338
+ }
339
+ }
340
+ async function resetUser(login, options) {
341
+ try {
342
+ const resolved = resolveGlobalOptions(options);
343
+ const profileResolved = await resolveProfile({
344
+ flags: { profile: options.profile, host: resolved.host },
345
+ });
346
+ const client = await createClientFromResolved(profileResolved);
347
+ const user = await client.users.getByLogin(login);
348
+ const opts = options.supportTicket ? { supportTicketId: options.supportTicket } : undefined;
349
+ await client.users.reset(user.id, opts);
350
+ process.stderr.write(`Reset password for user ${login}\n`);
351
+ }
352
+ catch (err) {
353
+ handleError(err);
354
+ }
355
+ }
356
+ async function disableUser(login, options) {
357
+ try {
358
+ const resolved = resolveGlobalOptions(options);
359
+ const profileResolved = await resolveProfile({
360
+ flags: { profile: options.profile, host: resolved.host },
361
+ });
362
+ const client = await createClientFromResolved(profileResolved);
363
+ const user = await client.users.getByLogin(login);
364
+ const opts = options.supportTicket ? { supportTicketId: options.supportTicket } : undefined;
365
+ await client.users.disable(user.id, opts);
366
+ process.stderr.write(`Disabled user ${login}\n`);
367
+ }
368
+ catch (err) {
369
+ handleError(err);
370
+ }
371
+ }
372
+ async function revokeUserVerifier(login, verifierId, options) {
373
+ try {
374
+ const resolved = resolveGlobalOptions(options);
375
+ const profileResolved = await resolveProfile({
376
+ flags: { profile: options.profile, host: resolved.host },
377
+ });
378
+ const client = await createClientFromResolved(profileResolved);
379
+ const user = await client.users.getByLogin(login);
380
+ await client.users.revokeVerifier(user.id, verifierId);
381
+ process.stderr.write(`Revoked verifier ${verifierId} for user ${login}\n`);
382
+ }
383
+ catch (err) {
384
+ handleError(err);
385
+ }
386
+ }
387
+ async function grantUserRole(loginOrId, roleId, options) {
388
+ try {
389
+ const resolved = resolveGlobalOptions(options);
390
+ const profileResolved = await resolveProfile({
391
+ flags: { profile: options.profile, host: resolved.host },
392
+ });
393
+ const client = await createClientFromResolved(profileResolved);
394
+ const userId = options.id ? loginOrId : (await client.users.getByLogin(loginOrId)).id;
395
+ const tenants = options.tenants
396
+ ? options.tenants.split(',').map((t) => t.trim()).filter((t) => t.length > 0)
397
+ : undefined;
398
+ const opts = tenants !== undefined ? { tenants } : undefined;
399
+ const result = await client.users.grantRole(userId, roleId, opts);
400
+ const subject = loginOrId;
401
+ if (result.changed) {
402
+ process.stderr.write(`Granted role ${roleId} to user ${subject}\n`);
403
+ }
404
+ else {
405
+ process.stderr.write(`User ${subject} already has role ${roleId} (no changes)\n`);
406
+ }
407
+ if (result.roleScope !== 'GLOBAL' && (tenants === undefined || tenants.length === 0)) {
408
+ process.stderr.write(`Warning: role ${roleId} has scope ${result.roleScope}; it will be inert until tenants are set\n`);
409
+ }
410
+ }
411
+ catch (err) {
412
+ handleError(err);
413
+ }
414
+ }
415
+ async function revokeUserRole(loginOrId, roleId, options) {
416
+ try {
417
+ const resolved = resolveGlobalOptions(options);
418
+ const profileResolved = await resolveProfile({
419
+ flags: { profile: options.profile, host: resolved.host },
420
+ });
421
+ const client = await createClientFromResolved(profileResolved);
422
+ const userId = options.id ? loginOrId : (await client.users.getByLogin(loginOrId)).id;
423
+ const result = await client.users.revokeRole(userId, roleId);
424
+ const subject = loginOrId;
425
+ if (result.changed) {
426
+ process.stderr.write(`Revoked role ${roleId} from user ${subject}\n`);
427
+ }
428
+ else {
429
+ process.stderr.write(`User ${subject} does not have role ${roleId} (no changes)\n`);
430
+ }
431
+ }
432
+ catch (err) {
433
+ handleError(err);
434
+ }
435
+ }
436
+ export function registerUserCommands(program) {
437
+ const user = program
438
+ .command('user')
439
+ .description('Manage users');
440
+ // user list
441
+ const list = user
442
+ .command('list')
443
+ .description('List users with optional filters');
444
+ addGlobalOptions(list)
445
+ .option('--login <email>', 'Find user by login (email address)')
446
+ .option('--org <id>', 'Filter users by organization ID')
447
+ .option('--role <id>', 'Filter users by role ID')
448
+ .option('--org-realm-access <id>', 'Find users with realm access in organization')
449
+ .option('--modified-after <date>', 'Filter by modification date (requires --role)')
450
+ .option('--all', 'Include deleted users (requires --org)')
451
+ .action(listUsers);
452
+ // user get
453
+ const get = user
454
+ .command('get')
455
+ .argument('<login>', 'User login (email) or ID')
456
+ .description('Get a specific user');
457
+ addGlobalOptions(get)
458
+ .option('--id', 'Treat argument as user ID instead of login')
459
+ .option('--expand <fields>', 'Expand related resources (organizations, roles, organizations,roles)')
460
+ .action(getUser);
461
+ // user current
462
+ const current = user
463
+ .command('current')
464
+ .description('Get the current authenticated user (requires user-context token)');
465
+ addGlobalOptions(current)
466
+ .option('--expand <fields>', 'Expand related resources (organizations, roles, organizations,roles)')
467
+ .action(currentUser);
468
+ // user audit
469
+ const audit = user
470
+ .command('audit')
471
+ .argument('<login>', 'User login (email)')
472
+ .description('Get audit log for a user');
473
+ addGlobalOptions(audit)
474
+ .option('--query-size <n>', 'Limit audit log query window size')
475
+ .action(auditUser);
476
+ // user roles
477
+ const roles = user
478
+ .command('roles')
479
+ .argument('<login>', 'User login (email)')
480
+ .description('List roles assigned to a user');
481
+ addGlobalOptions(roles).action(userRoles);
482
+ // user instances
483
+ const instances = user
484
+ .command('instances')
485
+ .argument('<login>', 'User login (email)')
486
+ .description('List instances accessible to a user');
487
+ addGlobalOptions(instances).action(userInstances);
488
+ // user assigned-realms
489
+ const assignedRealms = user
490
+ .command('assigned-realms')
491
+ .argument('<login>', 'User login (email)')
492
+ .description('List realms assigned to a user via role-tenant filter');
493
+ addGlobalOptions(assignedRealms).action(userAssignedRealms);
494
+ // user assigned-instances
495
+ const assignedInstances = user
496
+ .command('assigned-instances')
497
+ .argument('<login>', 'User login (email)')
498
+ .description('List instances assigned to a user via role-tenant filter');
499
+ addGlobalOptions(assignedInstances).action(userAssignedInstances);
500
+ // user create
501
+ const create = user.command('create').description('Create a new user');
502
+ addGlobalOptions(create)
503
+ .requiredOption('--mail <email>', 'Email address')
504
+ .requiredOption('--first-name <name>', 'First name')
505
+ .requiredOption('--last-name <name>', 'Last name')
506
+ .requiredOption('--primary-org <id>', 'Primary organization ID')
507
+ .option('--display-name <name>', 'Display name')
508
+ .option('--roles <roles>', 'Comma-separated role IDs')
509
+ .option('--organizations <orgs>', 'Comma-separated organization IDs')
510
+ .action(createUser);
511
+ // user update
512
+ const update = user
513
+ .command('update')
514
+ .argument('<login-or-id>', 'User login (email) or ID')
515
+ .description('Update a user');
516
+ addGlobalOptions(update)
517
+ .option('--id', 'Treat argument as user ID instead of login')
518
+ .option('--mail <email>', 'Email address')
519
+ .option('--first-name <name>', 'First name')
520
+ .option('--last-name <name>', 'Last name')
521
+ .option('--display-name <name>', 'Display name')
522
+ .option('--primary-org <id>', 'Primary organization ID')
523
+ .option('--roles <roles>', 'Comma-separated role IDs')
524
+ .option('--organizations <orgs>', 'Comma-separated organization IDs')
525
+ .action(updateUser);
526
+ // user delete
527
+ const del = user
528
+ .command('delete')
529
+ .argument('<login-or-id>', 'User login (email) or ID')
530
+ .description('Delete a user');
531
+ addGlobalOptions(del)
532
+ .option('--id', 'Treat argument as user ID instead of login')
533
+ .action(deleteUser);
534
+ // user reset
535
+ const reset = user
536
+ .command('reset')
537
+ .argument('<login>', 'User login (email)')
538
+ .description('Reset a user password');
539
+ addGlobalOptions(reset).option('--support-ticket <id>', 'Support ticket ID').action(resetUser);
540
+ // user disable
541
+ const disable = user
542
+ .command('disable')
543
+ .argument('<login>', 'User login (email)')
544
+ .description('Disable (deactivate) a user');
545
+ addGlobalOptions(disable).option('--support-ticket <id>', 'Support ticket ID').action(disableUser);
546
+ // user revoke-verifier
547
+ const revokeVerifier = user
548
+ .command('revoke-verifier')
549
+ .argument('<login>', 'User login (email)')
550
+ .argument('<verifier-id>', 'Verifier ID to revoke')
551
+ .description('Revoke an MFA verifier for a user');
552
+ addGlobalOptions(revokeVerifier).action(revokeUserVerifier);
553
+ // user grant-role
554
+ const grantRole = user
555
+ .command('grant-role')
556
+ .argument('<login-or-id>', 'User login (email) or ID')
557
+ .argument('<role-id>', 'Role ID to grant (e.g. "ccdx-sbx-user")')
558
+ .description('Grant a role to a user (idempotent; read-modify-write on the user resource)');
559
+ addGlobalOptions(grantRole)
560
+ .option('--id', 'Treat first argument as user ID instead of login')
561
+ .option('--tenants <csv>', 'Comma-separated tenants for scoped roles; union-merged into roleTenantFilter')
562
+ .action(grantUserRole);
563
+ // user revoke-role
564
+ const revokeRole = user
565
+ .command('revoke-role')
566
+ .argument('<login-or-id>', 'User login (email) or ID')
567
+ .argument('<role-id>', 'Role ID to revoke')
568
+ .description('Revoke a role from a user (idempotent; server auto-strips any tenant-filter entry)');
569
+ addGlobalOptions(revokeRole)
570
+ .option('--id', 'Treat first argument as user ID instead of login')
571
+ .action(revokeUserRole);
572
+ }
573
+ //# sourceMappingURL=user.js.map
@@ -0,0 +1,2 @@
1
+ export declare function handleError(err: unknown): never;
2
+ //# sourceMappingURL=error-handler.d.ts.map
@@ -0,0 +1,28 @@
1
+ import chalk from 'chalk';
2
+ import { CcamError, CcamAuthError, CcamNotFoundError } from 'ccam-sdk';
3
+ export function handleError(err) {
4
+ if (err instanceof CcamAuthError) {
5
+ process.stderr.write(chalk.red(`Error: ${err.message}`) + '\n');
6
+ process.stderr.write(chalk.yellow('Run `ccam auth login` to re-authenticate.') + '\n');
7
+ process.exit(1);
8
+ }
9
+ if (err instanceof CcamNotFoundError) {
10
+ process.stderr.write(chalk.red(`Error: ${err.message}`) + '\n');
11
+ process.exit(1);
12
+ }
13
+ if (err instanceof CcamError) {
14
+ let message = chalk.red(`Error: ${err.message}`);
15
+ if (err.code) {
16
+ message += chalk.dim(` (${err.code})`);
17
+ }
18
+ process.stderr.write(message + '\n');
19
+ process.exit(1);
20
+ }
21
+ if (err instanceof Error) {
22
+ process.stderr.write(chalk.red(`Error: ${err.message}`) + '\n');
23
+ process.exit(1);
24
+ }
25
+ process.stderr.write(chalk.red(`Error: ${String(err)}`) + '\n');
26
+ process.exit(1);
27
+ }
28
+ //# sourceMappingURL=error-handler.js.map
@@ -0,0 +1,2 @@
1
+ export { program } from './program.js';
2
+ //# sourceMappingURL=index.d.ts.map
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { program } from './program.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,3 @@
1
+ export declare function formatCsv(data: unknown, fields?: string[]): string;
2
+ export declare function formatTsv(data: unknown, fields?: string[]): string;
3
+ //# sourceMappingURL=csv.d.ts.map
@@ -0,0 +1,57 @@
1
+ import { extractColumns, getNestedValue } from './shared.js';
2
+ export function formatCsv(data, fields) {
3
+ return formatDelimited(data, ',', fields, true);
4
+ }
5
+ export function formatTsv(data, fields) {
6
+ return formatDelimited(data, '\t', fields, false);
7
+ }
8
+ function formatDelimited(data, delimiter, fields, quote = false) {
9
+ let items;
10
+ if (!Array.isArray(data)) {
11
+ items = [data];
12
+ }
13
+ else {
14
+ items = data;
15
+ }
16
+ if (items.length === 0) {
17
+ return '';
18
+ }
19
+ // Determine columns
20
+ const columns = fields || extractColumns(items);
21
+ if (columns.length === 0) {
22
+ return '';
23
+ }
24
+ // Build header row
25
+ const header = columns.map((col) => (quote ? escapeCsv(col) : col)).join(delimiter);
26
+ // Build data rows
27
+ const rows = items.map((item) => {
28
+ return columns
29
+ .map((col) => {
30
+ const value = getNestedValue(item, col);
31
+ const stringValue = formatValue(value);
32
+ return quote ? escapeCsv(stringValue) : stringValue;
33
+ })
34
+ .join(delimiter);
35
+ });
36
+ return [header, ...rows].join('\n');
37
+ }
38
+ function formatValue(value) {
39
+ if (value === null || value === undefined) {
40
+ return '';
41
+ }
42
+ if (Array.isArray(value)) {
43
+ return value.join(';');
44
+ }
45
+ if (typeof value === 'object') {
46
+ return JSON.stringify(value);
47
+ }
48
+ return String(value);
49
+ }
50
+ function escapeCsv(value) {
51
+ // Quote if contains comma, double quote, or newline
52
+ if (value.includes(',') || value.includes('"') || value.includes('\n')) {
53
+ return `"${value.replace(/"/g, '""')}"`;
54
+ }
55
+ return value;
56
+ }
57
+ //# sourceMappingURL=csv.js.map
@@ -0,0 +1,2 @@
1
+ export declare const DEFAULT_COLUMNS: Record<string, string[]>;
2
+ //# sourceMappingURL=default-columns.d.ts.map