@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.
- package/LICENSE +21 -0
- package/README.md +209 -0
- package/dist/auth/browser-login.d.ts +14 -0
- package/dist/auth/browser-login.js +72 -0
- package/dist/auth/manual-login.d.ts +10 -0
- package/dist/auth/manual-login.js +33 -0
- package/dist/auth/paths.d.ts +4 -0
- package/dist/auth/paths.js +15 -0
- package/dist/auth/profile-resolver.d.ts +26 -0
- package/dist/auth/profile-resolver.js +42 -0
- package/dist/auth/profile-store.d.ts +38 -0
- package/dist/auth/profile-store.js +125 -0
- package/dist/auth/prompt.d.ts +9 -0
- package/dist/auth/prompt.js +70 -0
- package/dist/bin.d.ts +3 -0
- package/dist/bin.js +4 -0
- package/dist/client-factory.d.ts +6 -0
- package/dist/client-factory.js +40 -0
- package/dist/commands/auth.d.ts +77 -0
- package/dist/commands/auth.js +387 -0
- package/dist/commands/client.d.ts +3 -0
- package/dist/commands/client.js +365 -0
- package/dist/commands/instance.d.ts +11 -0
- package/dist/commands/instance.js +128 -0
- package/dist/commands/org-config.d.ts +3 -0
- package/dist/commands/org-config.js +31 -0
- package/dist/commands/org.d.ts +11 -0
- package/dist/commands/org.js +234 -0
- package/dist/commands/permission.d.ts +3 -0
- package/dist/commands/permission.js +60 -0
- package/dist/commands/realm.d.ts +3 -0
- package/dist/commands/realm.js +58 -0
- package/dist/commands/role.d.ts +3 -0
- package/dist/commands/role.js +77 -0
- package/dist/commands/service-type.d.ts +3 -0
- package/dist/commands/service-type.js +57 -0
- package/dist/commands/user.d.ts +14 -0
- package/dist/commands/user.js +573 -0
- package/dist/error-handler.d.ts +2 -0
- package/dist/error-handler.js +28 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/output/csv.d.ts +3 -0
- package/dist/output/csv.js +57 -0
- package/dist/output/default-columns.d.ts +2 -0
- package/dist/output/default-columns.js +16 -0
- package/dist/output/detect.d.ts +4 -0
- package/dist/output/detect.js +6 -0
- package/dist/output/index.d.ts +15 -0
- package/dist/output/index.js +34 -0
- package/dist/output/json.d.ts +2 -0
- package/dist/output/json.js +10 -0
- package/dist/output/shared.d.ts +13 -0
- package/dist/output/shared.js +41 -0
- package/dist/output/table.d.ts +2 -0
- package/dist/output/table.js +72 -0
- package/dist/output/types.d.ts +2 -0
- package/dist/output/types.js +2 -0
- package/dist/output/yaml-fmt.d.ts +2 -0
- package/dist/output/yaml-fmt.js +11 -0
- package/dist/program.d.ts +3 -0
- package/dist/program.js +37 -0
- package/dist/shared.d.ts +46 -0
- package/dist/shared.js +96 -0
- package/dist/tui/App.d.ts +7 -0
- package/dist/tui/App.js +30 -0
- package/dist/tui/components/AuditTab.d.ts +8 -0
- package/dist/tui/components/AuditTab.js +80 -0
- package/dist/tui/components/FooterBar.d.ts +17 -0
- package/dist/tui/components/FooterBar.js +23 -0
- package/dist/tui/components/FullScreenLayout.d.ts +6 -0
- package/dist/tui/components/FullScreenLayout.js +11 -0
- package/dist/tui/components/HeaderBar.d.ts +5 -0
- package/dist/tui/components/HeaderBar.js +10 -0
- package/dist/tui/components/InfoTab.d.ts +8 -0
- package/dist/tui/components/InfoTab.js +70 -0
- package/dist/tui/components/ResourcePicker.d.ts +10 -0
- package/dist/tui/components/ResourcePicker.js +36 -0
- package/dist/tui/components/SubResourceTab.d.ts +7 -0
- package/dist/tui/components/SubResourceTab.js +193 -0
- package/dist/tui/components/TabBar.d.ts +11 -0
- package/dist/tui/components/TabBar.js +13 -0
- package/dist/tui/components/Table.d.ts +32 -0
- package/dist/tui/components/Table.js +175 -0
- package/dist/tui/context/client.d.ts +7 -0
- package/dist/tui/context/client.js +14 -0
- package/dist/tui/context/navigation.d.ts +14 -0
- package/dist/tui/context/navigation.js +25 -0
- package/dist/tui/context/terminal-size.d.ts +9 -0
- package/dist/tui/context/terminal-size.js +26 -0
- package/dist/tui/format.d.ts +20 -0
- package/dist/tui/format.js +57 -0
- package/dist/tui/hooks/use-audit-log.d.ts +12 -0
- package/dist/tui/hooks/use-audit-log.js +71 -0
- package/dist/tui/hooks/use-local-collection.d.ts +8 -0
- package/dist/tui/hooks/use-local-collection.js +30 -0
- package/dist/tui/hooks/use-paginated-resource.d.ts +23 -0
- package/dist/tui/hooks/use-paginated-resource.js +115 -0
- package/dist/tui/hooks/use-resource-detail.d.ts +7 -0
- package/dist/tui/hooks/use-resource-detail.js +30 -0
- package/dist/tui/hooks/use-scroll-window.d.ts +7 -0
- package/dist/tui/hooks/use-scroll-window.js +29 -0
- package/dist/tui/index.d.ts +2 -0
- package/dist/tui/index.js +22 -0
- package/dist/tui/navigation.d.ts +11 -0
- package/dist/tui/navigation.js +29 -0
- package/dist/tui/resource-configs/api-clients.d.ts +3 -0
- package/dist/tui/resource-configs/api-clients.js +118 -0
- package/dist/tui/resource-configs/index.d.ts +14 -0
- package/dist/tui/resource-configs/index.js +28 -0
- package/dist/tui/resource-configs/instances.d.ts +3 -0
- package/dist/tui/resource-configs/instances.js +24 -0
- package/dist/tui/resource-configs/org-configuration.d.ts +3 -0
- package/dist/tui/resource-configs/org-configuration.js +28 -0
- package/dist/tui/resource-configs/organizations.d.ts +3 -0
- package/dist/tui/resource-configs/organizations.js +104 -0
- package/dist/tui/resource-configs/permissions.d.ts +3 -0
- package/dist/tui/resource-configs/permissions.js +25 -0
- package/dist/tui/resource-configs/realms.d.ts +3 -0
- package/dist/tui/resource-configs/realms.js +36 -0
- package/dist/tui/resource-configs/roles.d.ts +3 -0
- package/dist/tui/resource-configs/roles.js +56 -0
- package/dist/tui/resource-configs/service-types.d.ts +3 -0
- package/dist/tui/resource-configs/service-types.js +24 -0
- package/dist/tui/resource-configs/users.d.ts +3 -0
- package/dist/tui/resource-configs/users.js +126 -0
- package/dist/tui/types.d.ts +99 -0
- package/dist/tui/types.js +23 -0
- package/dist/tui/views/ResourceDetailView.d.ts +7 -0
- package/dist/tui/views/ResourceDetailView.js +123 -0
- package/dist/tui/views/ResourceListView.d.ts +6 -0
- package/dist/tui/views/ResourceListView.js +140 -0
- package/dist/tui/views/ViewRouter.d.ts +2 -0
- package/dist/tui/views/ViewRouter.js +60 -0
- package/package.json +62 -0
|
@@ -0,0 +1,365 @@
|
|
|
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 CLIENT_GET_EXPAND = ['organizations', 'roles', 'organizations,roles'];
|
|
8
|
+
async function listClients(options) {
|
|
9
|
+
try {
|
|
10
|
+
const resolved = resolveGlobalOptions(options);
|
|
11
|
+
const profileResolved = await resolveProfile({
|
|
12
|
+
flags: { profile: options.profile, host: resolved.host },
|
|
13
|
+
});
|
|
14
|
+
const client = await createClientFromResolved(profileResolved);
|
|
15
|
+
const result = await client.apiClients.list({
|
|
16
|
+
page: resolved.page,
|
|
17
|
+
size: resolved.size,
|
|
18
|
+
sort: resolved.sort,
|
|
19
|
+
});
|
|
20
|
+
const format = resolveFormat(resolved.format, process.stdout.isTTY);
|
|
21
|
+
renderOutput(result.content, { format, fields: resolved.fields, defaultFields: DEFAULT_COLUMNS.client });
|
|
22
|
+
writePageInfoIfTable(format, result);
|
|
23
|
+
}
|
|
24
|
+
catch (err) {
|
|
25
|
+
handleError(err);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
async function getClient(id, options) {
|
|
29
|
+
try {
|
|
30
|
+
const resolved = resolveGlobalOptions(options);
|
|
31
|
+
const profileResolved = await resolveProfile({
|
|
32
|
+
flags: { profile: options.profile, host: resolved.host },
|
|
33
|
+
});
|
|
34
|
+
const client = await createClientFromResolved(profileResolved);
|
|
35
|
+
const expand = parseExpand(options.expand, CLIENT_GET_EXPAND);
|
|
36
|
+
// Branch on the literal expand value so TypeScript can pick the right overload.
|
|
37
|
+
let result;
|
|
38
|
+
if (expand === 'organizations') {
|
|
39
|
+
result = await client.apiClients.get(id, { expand });
|
|
40
|
+
}
|
|
41
|
+
else if (expand === 'roles') {
|
|
42
|
+
result = await client.apiClients.get(id, { expand });
|
|
43
|
+
}
|
|
44
|
+
else if (expand === 'organizations,roles') {
|
|
45
|
+
result = await client.apiClients.get(id, { expand });
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
result = await client.apiClients.get(id);
|
|
49
|
+
}
|
|
50
|
+
const format = resolveFormat(resolved.format, process.stdout.isTTY);
|
|
51
|
+
renderOutput(result, { format, fields: resolved.fields, defaultFields: DEFAULT_COLUMNS.clientDetail });
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
handleError(err);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
async function auditClient(id, options) {
|
|
58
|
+
try {
|
|
59
|
+
const resolved = resolveGlobalOptions(options);
|
|
60
|
+
const profileResolved = await resolveProfile({
|
|
61
|
+
flags: { profile: options.profile, host: resolved.host },
|
|
62
|
+
});
|
|
63
|
+
const client = await createClientFromResolved(profileResolved);
|
|
64
|
+
const querySize = options.querySize !== undefined ? parseInt(options.querySize, 10) : undefined;
|
|
65
|
+
const result = await client.apiClients.auditLogs(id, querySize !== undefined ? { querySize } : undefined);
|
|
66
|
+
const format = resolveFormat(resolved.format, process.stdout.isTTY);
|
|
67
|
+
renderOutput(result.content, { format, fields: resolved.fields, defaultFields: DEFAULT_COLUMNS.auditLog });
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
handleError(err);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
async function clientAssignedRealms(id, options) {
|
|
74
|
+
try {
|
|
75
|
+
const resolved = resolveGlobalOptions(options);
|
|
76
|
+
const profileResolved = await resolveProfile({
|
|
77
|
+
flags: { profile: options.profile, host: resolved.host },
|
|
78
|
+
});
|
|
79
|
+
const client = await createClientFromResolved(profileResolved);
|
|
80
|
+
const result = await client.apiClients.assignedRealms(id);
|
|
81
|
+
const format = resolveFormat(resolved.format, process.stdout.isTTY);
|
|
82
|
+
renderOutput(result.content, { format, fields: resolved.fields, defaultFields: DEFAULT_COLUMNS.realm });
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
handleError(err);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
async function clientAssignedInstances(id, options) {
|
|
89
|
+
try {
|
|
90
|
+
const resolved = resolveGlobalOptions(options);
|
|
91
|
+
const profileResolved = await resolveProfile({
|
|
92
|
+
flags: { profile: options.profile, host: resolved.host },
|
|
93
|
+
});
|
|
94
|
+
const client = await createClientFromResolved(profileResolved);
|
|
95
|
+
const result = await client.apiClients.assignedInstances(id);
|
|
96
|
+
const format = resolveFormat(resolved.format, process.stdout.isTTY);
|
|
97
|
+
renderOutput(result.content, { format, fields: resolved.fields, defaultFields: DEFAULT_COLUMNS.instance });
|
|
98
|
+
}
|
|
99
|
+
catch (err) {
|
|
100
|
+
handleError(err);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
async function createApiClient(options) {
|
|
104
|
+
try {
|
|
105
|
+
const resolved = resolveGlobalOptions(options);
|
|
106
|
+
const profileResolved = await resolveProfile({
|
|
107
|
+
flags: { profile: options.profile, host: resolved.host },
|
|
108
|
+
});
|
|
109
|
+
const client = await createClientFromResolved(profileResolved);
|
|
110
|
+
const data = {
|
|
111
|
+
id: options.id,
|
|
112
|
+
name: options.name,
|
|
113
|
+
};
|
|
114
|
+
if (options.description !== undefined)
|
|
115
|
+
data.description = options.description;
|
|
116
|
+
if (options.organizations)
|
|
117
|
+
data.organizations = options.organizations.split(',');
|
|
118
|
+
if (options.scopes)
|
|
119
|
+
data.scopes = options.scopes.split(',');
|
|
120
|
+
if (options.redirectUrls)
|
|
121
|
+
data.redirectUrls = options.redirectUrls.split(',');
|
|
122
|
+
if (options.tokenEndpointAuthMethod)
|
|
123
|
+
data.tokenEndpointAuthMethod = options.tokenEndpointAuthMethod;
|
|
124
|
+
const result = await client.apiClients.create(data);
|
|
125
|
+
const format = resolveFormat(resolved.format, process.stdout.isTTY);
|
|
126
|
+
renderOutput(result, { format, fields: resolved.fields, defaultFields: DEFAULT_COLUMNS.clientDetail });
|
|
127
|
+
}
|
|
128
|
+
catch (err) {
|
|
129
|
+
handleError(err);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
async function updateApiClient(id, options) {
|
|
133
|
+
try {
|
|
134
|
+
const resolved = resolveGlobalOptions(options);
|
|
135
|
+
const profileResolved = await resolveProfile({
|
|
136
|
+
flags: { profile: options.profile, host: resolved.host },
|
|
137
|
+
});
|
|
138
|
+
const client = await createClientFromResolved(profileResolved);
|
|
139
|
+
const data = {};
|
|
140
|
+
if (options.name !== undefined)
|
|
141
|
+
data.name = options.name;
|
|
142
|
+
if (options.description !== undefined)
|
|
143
|
+
data.description = options.description;
|
|
144
|
+
if (options.organizations)
|
|
145
|
+
data.organizations = options.organizations.split(',');
|
|
146
|
+
if (options.scopes)
|
|
147
|
+
data.scopes = options.scopes.split(',');
|
|
148
|
+
if (options.redirectUrls)
|
|
149
|
+
data.redirectUrls = options.redirectUrls.split(',');
|
|
150
|
+
if (options.tokenEndpointAuthMethod)
|
|
151
|
+
data.tokenEndpointAuthMethod = options.tokenEndpointAuthMethod;
|
|
152
|
+
if (options.active)
|
|
153
|
+
data.active = true;
|
|
154
|
+
if (options.inactive)
|
|
155
|
+
data.active = false;
|
|
156
|
+
const result = await client.apiClients.update(id, data);
|
|
157
|
+
const format = resolveFormat(resolved.format, process.stdout.isTTY);
|
|
158
|
+
renderOutput(result, { format, fields: resolved.fields, defaultFields: DEFAULT_COLUMNS.clientDetail });
|
|
159
|
+
}
|
|
160
|
+
catch (err) {
|
|
161
|
+
handleError(err);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
async function deleteApiClient(id, options) {
|
|
165
|
+
try {
|
|
166
|
+
const resolved = resolveGlobalOptions(options);
|
|
167
|
+
const profileResolved = await resolveProfile({
|
|
168
|
+
flags: { profile: options.profile, host: resolved.host },
|
|
169
|
+
});
|
|
170
|
+
const client = await createClientFromResolved(profileResolved);
|
|
171
|
+
await client.apiClients.delete(id);
|
|
172
|
+
process.stderr.write(`Deleted API client ${id}\n`);
|
|
173
|
+
}
|
|
174
|
+
catch (err) {
|
|
175
|
+
handleError(err);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
async function setClientPassword(id, options) {
|
|
179
|
+
try {
|
|
180
|
+
const resolved = resolveGlobalOptions(options);
|
|
181
|
+
const profileResolved = await resolveProfile({
|
|
182
|
+
flags: { profile: options.profile, host: resolved.host },
|
|
183
|
+
});
|
|
184
|
+
const client = await createClientFromResolved(profileResolved);
|
|
185
|
+
const data = options.oldPassword
|
|
186
|
+
? { new: options.password, old: options.oldPassword }
|
|
187
|
+
: { new: options.password };
|
|
188
|
+
await client.apiClients.setPassword(id, data);
|
|
189
|
+
process.stderr.write(`Updated password for API client ${id}\n`);
|
|
190
|
+
}
|
|
191
|
+
catch (err) {
|
|
192
|
+
handleError(err);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
async function setClientAuthType(id, options) {
|
|
196
|
+
try {
|
|
197
|
+
if (!options.public && !options.confidential) {
|
|
198
|
+
process.stderr.write('Error: specify --public or --confidential\n');
|
|
199
|
+
process.exit(1);
|
|
200
|
+
}
|
|
201
|
+
const resolved = resolveGlobalOptions(options);
|
|
202
|
+
const profileResolved = await resolveProfile({
|
|
203
|
+
flags: { profile: options.profile, host: resolved.host },
|
|
204
|
+
});
|
|
205
|
+
const client = await createClientFromResolved(profileResolved);
|
|
206
|
+
const isPublic = !!options.public;
|
|
207
|
+
await client.apiClients.setAuthType(id, isPublic);
|
|
208
|
+
process.stderr.write(`Set API client ${id} to ${isPublic ? 'public' : 'confidential'}\n`);
|
|
209
|
+
}
|
|
210
|
+
catch (err) {
|
|
211
|
+
handleError(err);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
async function grantClientRole(id, roleId, options) {
|
|
215
|
+
try {
|
|
216
|
+
const resolved = resolveGlobalOptions(options);
|
|
217
|
+
const profileResolved = await resolveProfile({
|
|
218
|
+
flags: { profile: options.profile, host: resolved.host },
|
|
219
|
+
});
|
|
220
|
+
const client = await createClientFromResolved(profileResolved);
|
|
221
|
+
const tenants = options.tenants
|
|
222
|
+
? options.tenants.split(',').map((t) => t.trim()).filter((t) => t.length > 0)
|
|
223
|
+
: undefined;
|
|
224
|
+
const opts = tenants !== undefined ? { tenants } : undefined;
|
|
225
|
+
const result = await client.apiClients.grantRole(id, roleId, opts);
|
|
226
|
+
if (result.changed) {
|
|
227
|
+
process.stderr.write(`Granted role ${roleId} to API client ${id}\n`);
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
process.stderr.write(`API client ${id} already has role ${roleId} (no changes)\n`);
|
|
231
|
+
}
|
|
232
|
+
if (result.roleScope !== 'GLOBAL' && (tenants === undefined || tenants.length === 0)) {
|
|
233
|
+
process.stderr.write(`Warning: role ${roleId} has scope ${result.roleScope}; it will be inert until tenants are set\n`);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
catch (err) {
|
|
237
|
+
handleError(err);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
async function revokeClientRole(id, roleId, options) {
|
|
241
|
+
try {
|
|
242
|
+
const resolved = resolveGlobalOptions(options);
|
|
243
|
+
const profileResolved = await resolveProfile({
|
|
244
|
+
flags: { profile: options.profile, host: resolved.host },
|
|
245
|
+
});
|
|
246
|
+
const client = await createClientFromResolved(profileResolved);
|
|
247
|
+
const result = await client.apiClients.revokeRole(id, roleId);
|
|
248
|
+
if (result.changed) {
|
|
249
|
+
process.stderr.write(`Revoked role ${roleId} from API client ${id}\n`);
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
process.stderr.write(`API client ${id} does not have role ${roleId} (no changes)\n`);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
catch (err) {
|
|
256
|
+
handleError(err);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
export function registerClientCommands(program) {
|
|
260
|
+
const apiClient = program
|
|
261
|
+
.command('client')
|
|
262
|
+
.description('Manage API clients');
|
|
263
|
+
// client list
|
|
264
|
+
const list = apiClient
|
|
265
|
+
.command('list')
|
|
266
|
+
.description('List API clients');
|
|
267
|
+
addGlobalOptions(list).action(listClients);
|
|
268
|
+
// client get
|
|
269
|
+
const get = apiClient
|
|
270
|
+
.command('get')
|
|
271
|
+
.argument('<id>', 'API client ID')
|
|
272
|
+
.description('Get a specific API client');
|
|
273
|
+
addGlobalOptions(get)
|
|
274
|
+
.option('--expand <fields>', 'Expand related resources (organizations, roles, organizations,roles)')
|
|
275
|
+
.action(getClient);
|
|
276
|
+
// client audit
|
|
277
|
+
const audit = apiClient
|
|
278
|
+
.command('audit')
|
|
279
|
+
.argument('<id>', 'API client ID')
|
|
280
|
+
.description('Get audit log for an API client');
|
|
281
|
+
addGlobalOptions(audit)
|
|
282
|
+
.option('--query-size <n>', 'Limit audit log query window size')
|
|
283
|
+
.action(auditClient);
|
|
284
|
+
// client assigned-realms
|
|
285
|
+
const assignedRealms = apiClient
|
|
286
|
+
.command('assigned-realms')
|
|
287
|
+
.argument('<id>', 'API client ID')
|
|
288
|
+
.description('List realms assigned to an API client via role-tenant filter');
|
|
289
|
+
addGlobalOptions(assignedRealms).action(clientAssignedRealms);
|
|
290
|
+
// client assigned-instances
|
|
291
|
+
const assignedInstances = apiClient
|
|
292
|
+
.command('assigned-instances')
|
|
293
|
+
.argument('<id>', 'API client ID')
|
|
294
|
+
.description('List instances assigned to an API client via role-tenant filter');
|
|
295
|
+
addGlobalOptions(assignedInstances).action(clientAssignedInstances);
|
|
296
|
+
// client create
|
|
297
|
+
const create = apiClient
|
|
298
|
+
.command('create')
|
|
299
|
+
.description('Create a new API client');
|
|
300
|
+
addGlobalOptions(create)
|
|
301
|
+
.requiredOption('--id <id>', 'Client ID')
|
|
302
|
+
.requiredOption('--name <name>', 'Client name')
|
|
303
|
+
.option('--description <desc>', 'Description')
|
|
304
|
+
.option('--organizations <orgs>', 'Comma-separated organization IDs')
|
|
305
|
+
.option('--scopes <scopes>', 'Comma-separated OAuth scopes')
|
|
306
|
+
.option('--redirect-urls <urls>', 'Comma-separated redirect URIs')
|
|
307
|
+
.option('--token-endpoint-auth-method <method>', 'Auth method (private_key_jwt, client_secret_post, client_secret_basic, none)')
|
|
308
|
+
.action(createApiClient);
|
|
309
|
+
// client update
|
|
310
|
+
const update = apiClient
|
|
311
|
+
.command('update')
|
|
312
|
+
.argument('<id>', 'API client ID')
|
|
313
|
+
.description('Update an API client');
|
|
314
|
+
addGlobalOptions(update)
|
|
315
|
+
.option('--name <name>', 'Client name')
|
|
316
|
+
.option('--description <desc>', 'Description')
|
|
317
|
+
.option('--organizations <orgs>', 'Comma-separated organization IDs')
|
|
318
|
+
.option('--scopes <scopes>', 'Comma-separated OAuth scopes')
|
|
319
|
+
.option('--redirect-urls <urls>', 'Comma-separated redirect URIs')
|
|
320
|
+
.option('--token-endpoint-auth-method <method>', 'Auth method')
|
|
321
|
+
.option('--active', 'Set client as active')
|
|
322
|
+
.option('--inactive', 'Set client as inactive')
|
|
323
|
+
.action(updateApiClient);
|
|
324
|
+
// client delete
|
|
325
|
+
const del = apiClient
|
|
326
|
+
.command('delete')
|
|
327
|
+
.argument('<id>', 'API client ID')
|
|
328
|
+
.description('Delete an API client (requires the client to have been disabled for 7+ days)');
|
|
329
|
+
addGlobalOptions(del).action(deleteApiClient);
|
|
330
|
+
// client set-password
|
|
331
|
+
const setPassword = apiClient
|
|
332
|
+
.command('set-password')
|
|
333
|
+
.argument('<id>', 'API client ID')
|
|
334
|
+
.description('Change an API client secret');
|
|
335
|
+
addGlobalOptions(setPassword)
|
|
336
|
+
.requiredOption('--password <secret>', 'New client secret')
|
|
337
|
+
.option('--old-password <secret>', 'Current client secret (required when the client already has a password)')
|
|
338
|
+
.action(setClientPassword);
|
|
339
|
+
// client set-auth-type
|
|
340
|
+
const setAuthType = apiClient
|
|
341
|
+
.command('set-auth-type')
|
|
342
|
+
.argument('<id>', 'API client ID')
|
|
343
|
+
.description('Switch API client between public and confidential');
|
|
344
|
+
addGlobalOptions(setAuthType)
|
|
345
|
+
.option('--public', 'Set as public client')
|
|
346
|
+
.option('--confidential', 'Set as confidential client')
|
|
347
|
+
.action(setClientAuthType);
|
|
348
|
+
// client grant-role
|
|
349
|
+
const grantRole = apiClient
|
|
350
|
+
.command('grant-role')
|
|
351
|
+
.argument('<id>', 'API client ID')
|
|
352
|
+
.argument('<role-id>', 'Role ID to grant (e.g. "ccdx-sbx-user")')
|
|
353
|
+
.description('Grant a role to an API client (idempotent; read-modify-write on the client resource)');
|
|
354
|
+
addGlobalOptions(grantRole)
|
|
355
|
+
.option('--tenants <csv>', 'Comma-separated tenants for scoped roles; union-merged into roleTenantFilter')
|
|
356
|
+
.action(grantClientRole);
|
|
357
|
+
// client revoke-role
|
|
358
|
+
const revokeRole = apiClient
|
|
359
|
+
.command('revoke-role')
|
|
360
|
+
.argument('<id>', 'API client ID')
|
|
361
|
+
.argument('<role-id>', 'Role ID to revoke')
|
|
362
|
+
.description('Revoke a role from an API client (idempotent; server auto-strips any tenant-filter entry)');
|
|
363
|
+
addGlobalOptions(revokeRole).action(revokeClientRole);
|
|
364
|
+
}
|
|
365
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
interface InstanceFilters {
|
|
3
|
+
org?: string;
|
|
4
|
+
realm?: string;
|
|
5
|
+
ids?: string;
|
|
6
|
+
}
|
|
7
|
+
type InstanceFinder = 'list' | 'findByOrganization' | 'findByRealm' | 'findById';
|
|
8
|
+
export declare function selectInstanceFinder(filters: InstanceFilters): InstanceFinder;
|
|
9
|
+
export declare function registerInstanceCommands(program: Command): void;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=instance.d.ts.map
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { addGlobalOptions, 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
|
+
export function selectInstanceFinder(filters) {
|
|
8
|
+
if (filters.org) {
|
|
9
|
+
return 'findByOrganization';
|
|
10
|
+
}
|
|
11
|
+
if (filters.realm) {
|
|
12
|
+
return 'findByRealm';
|
|
13
|
+
}
|
|
14
|
+
if (filters.ids) {
|
|
15
|
+
return 'findById';
|
|
16
|
+
}
|
|
17
|
+
return 'list';
|
|
18
|
+
}
|
|
19
|
+
async function listInstances(options) {
|
|
20
|
+
try {
|
|
21
|
+
const resolved = resolveGlobalOptions(options);
|
|
22
|
+
const profileResolved = await resolveProfile({
|
|
23
|
+
flags: { profile: options.profile, host: resolved.host },
|
|
24
|
+
});
|
|
25
|
+
const client = await createClientFromResolved(profileResolved);
|
|
26
|
+
const filters = {
|
|
27
|
+
org: options.org,
|
|
28
|
+
realm: options.realm,
|
|
29
|
+
ids: options.ids,
|
|
30
|
+
};
|
|
31
|
+
const finder = selectInstanceFinder(filters);
|
|
32
|
+
let result;
|
|
33
|
+
switch (finder) {
|
|
34
|
+
case 'list':
|
|
35
|
+
result = await client.instances.list({
|
|
36
|
+
page: resolved.page,
|
|
37
|
+
size: resolved.size,
|
|
38
|
+
sort: resolved.sort,
|
|
39
|
+
});
|
|
40
|
+
break;
|
|
41
|
+
case 'findByOrganization':
|
|
42
|
+
result = await client.instances.search.findByOrganization({
|
|
43
|
+
organization: filters.org,
|
|
44
|
+
page: resolved.page,
|
|
45
|
+
size: resolved.size,
|
|
46
|
+
});
|
|
47
|
+
break;
|
|
48
|
+
case 'findByRealm':
|
|
49
|
+
result = await client.instances.search.findByRealm({
|
|
50
|
+
realm: filters.realm,
|
|
51
|
+
page: resolved.page,
|
|
52
|
+
size: resolved.size,
|
|
53
|
+
});
|
|
54
|
+
break;
|
|
55
|
+
case 'findById':
|
|
56
|
+
result = await client.instances.search.findById({
|
|
57
|
+
id: filters.ids,
|
|
58
|
+
page: resolved.page,
|
|
59
|
+
size: resolved.size,
|
|
60
|
+
});
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
const format = resolveFormat(resolved.format, process.stdout.isTTY);
|
|
64
|
+
const data = result && typeof result === 'object' && 'content' in result ? result.content : result;
|
|
65
|
+
renderOutput(data, { format, fields: resolved.fields, defaultFields: DEFAULT_COLUMNS.instance });
|
|
66
|
+
writePageInfoIfTable(format, result);
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
handleError(err);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
async function getInstance(id, options) {
|
|
73
|
+
try {
|
|
74
|
+
const resolved = resolveGlobalOptions(options);
|
|
75
|
+
const profileResolved = await resolveProfile({
|
|
76
|
+
flags: { profile: options.profile, host: resolved.host },
|
|
77
|
+
});
|
|
78
|
+
const client = await createClientFromResolved(profileResolved);
|
|
79
|
+
const result = await client.instances.get(id);
|
|
80
|
+
const format = resolveFormat(resolved.format, process.stdout.isTTY);
|
|
81
|
+
renderOutput(result, { format, fields: resolved.fields, defaultFields: DEFAULT_COLUMNS.instance });
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
handleError(err);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
async function validateFilter(options) {
|
|
88
|
+
try {
|
|
89
|
+
const resolved = resolveGlobalOptions(options);
|
|
90
|
+
const profileResolved = await resolveProfile({
|
|
91
|
+
flags: { profile: options.profile, host: resolved.host },
|
|
92
|
+
});
|
|
93
|
+
const client = await createClientFromResolved(profileResolved);
|
|
94
|
+
await client.instances.validateFilter(options.filter);
|
|
95
|
+
process.stderr.write(`Filter "${options.filter}" is valid\n`);
|
|
96
|
+
}
|
|
97
|
+
catch (err) {
|
|
98
|
+
handleError(err);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
export function registerInstanceCommands(program) {
|
|
102
|
+
const instance = program
|
|
103
|
+
.command('instance')
|
|
104
|
+
.description('Manage instances');
|
|
105
|
+
// instance list
|
|
106
|
+
const list = instance
|
|
107
|
+
.command('list')
|
|
108
|
+
.description('List instances with optional filters');
|
|
109
|
+
addGlobalOptions(list)
|
|
110
|
+
.option('--org <id>', 'Filter instances by organization ID')
|
|
111
|
+
.option('--realm <ids>', 'Filter instances by realm (comma-separated realm IDs)')
|
|
112
|
+
.option('--ids <ids>', 'Filter instances by ID (comma-separated instance IDs)')
|
|
113
|
+
.action(listInstances);
|
|
114
|
+
// instance get
|
|
115
|
+
const get = instance
|
|
116
|
+
.command('get')
|
|
117
|
+
.argument('<id>', 'Instance ID (REALM_TYPE format, e.g. "aabc_prd")')
|
|
118
|
+
.description('Get a specific instance');
|
|
119
|
+
addGlobalOptions(get).action(getInstance);
|
|
120
|
+
// instance validate-filter
|
|
121
|
+
const validate = instance
|
|
122
|
+
.command('validate-filter')
|
|
123
|
+
.description('Validate a tenant filter string');
|
|
124
|
+
addGlobalOptions(validate)
|
|
125
|
+
.requiredOption('--filter <filter>', 'Tenant filter string (e.g. "aalm_prd")')
|
|
126
|
+
.action(validateFilter);
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=instance.js.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { addGlobalOptions, resolveGlobalOptions } 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
|
+
async function getOrgConfig(options) {
|
|
7
|
+
try {
|
|
8
|
+
const resolved = resolveGlobalOptions(options);
|
|
9
|
+
const profileResolved = await resolveProfile({
|
|
10
|
+
flags: { profile: options.profile, host: resolved.host },
|
|
11
|
+
});
|
|
12
|
+
const client = await createClientFromResolved(profileResolved);
|
|
13
|
+
const result = await client.organizationConfiguration.get();
|
|
14
|
+
const format = resolveFormat(resolved.format, process.stdout.isTTY);
|
|
15
|
+
renderOutput(result, { format, fields: resolved.fields });
|
|
16
|
+
}
|
|
17
|
+
catch (err) {
|
|
18
|
+
handleError(err);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export function registerOrgConfigCommands(program) {
|
|
22
|
+
const orgConfig = program
|
|
23
|
+
.command('org-config')
|
|
24
|
+
.description('Manage organization configuration');
|
|
25
|
+
// org-config get
|
|
26
|
+
const get = orgConfig
|
|
27
|
+
.command('get')
|
|
28
|
+
.description('Get the current organization configuration');
|
|
29
|
+
addGlobalOptions(get).action(getOrgConfig);
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=org-config.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
interface OrgFilters {
|
|
3
|
+
name?: string;
|
|
4
|
+
startsWith?: string;
|
|
5
|
+
sfAccountId?: string;
|
|
6
|
+
}
|
|
7
|
+
type OrgFinder = 'list' | 'findByName' | 'findBySfAccountId';
|
|
8
|
+
export declare function selectOrgFinder(filters: OrgFilters): OrgFinder;
|
|
9
|
+
export declare function registerOrgCommands(program: Command): void;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=org.d.ts.map
|