@hyperdrive.bot/cli 1.0.5 → 1.0.7

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 (67) hide show
  1. package/README.md +169 -63
  2. package/dist/commands/account/add.d.ts +6 -6
  3. package/dist/commands/account/remove.d.ts +3 -3
  4. package/dist/commands/auth/login.d.ts +4 -4
  5. package/dist/commands/auth/login.js +1 -0
  6. package/dist/commands/ci/account/create.d.ts +7 -6
  7. package/dist/commands/ci/account/create.js +49 -3
  8. package/dist/commands/ci/account/delete.d.ts +3 -3
  9. package/dist/commands/ci/account/list.d.ts +2 -2
  10. package/dist/commands/config/get.d.ts +1 -1
  11. package/dist/commands/config/set.d.ts +2 -2
  12. package/dist/commands/deployment/create.d.ts +10 -10
  13. package/dist/commands/deployment/get.d.ts +4 -4
  14. package/dist/commands/deployment/launch.d.ts +6 -6
  15. package/dist/commands/deployment/list.d.ts +3 -3
  16. package/dist/commands/deployment/list.js +17 -17
  17. package/dist/commands/domain/switch.d.ts +1 -1
  18. package/dist/commands/example.d.ts +3 -3
  19. package/dist/commands/git/connect.d.ts +2 -2
  20. package/dist/commands/git/disconnect.d.ts +3 -3
  21. package/dist/commands/git/list.d.ts +2 -2
  22. package/dist/commands/git/sync.d.ts +7 -7
  23. package/dist/commands/git/sync.js +24 -23
  24. package/dist/commands/jira/connect.d.ts +1 -1
  25. package/dist/commands/jira/status.d.ts +1 -1
  26. package/dist/commands/module/analyze.d.ts +5 -5
  27. package/dist/commands/module/create.d.ts +17 -17
  28. package/dist/commands/module/create.js +9 -1
  29. package/dist/commands/module/destroy.d.ts +3 -3
  30. package/dist/commands/module/get.d.ts +2 -2
  31. package/dist/commands/module/link.d.ts +4 -4
  32. package/dist/commands/module/list.d.ts +1 -1
  33. package/dist/commands/module/list.js +12 -11
  34. package/dist/commands/module/reanalyze.d.ts +6 -6
  35. package/dist/commands/module/update.d.ts +19 -19
  36. package/dist/commands/parameter/add.d.ts +7 -7
  37. package/dist/commands/parameter/backfill.d.ts +4 -4
  38. package/dist/commands/parameter/backfill.js +4 -3
  39. package/dist/commands/parameter/clear.d.ts +6 -6
  40. package/dist/commands/parameter/list.d.ts +6 -6
  41. package/dist/commands/parameter/list.js +4 -3
  42. package/dist/commands/parameter/pull.d.ts +6 -6
  43. package/dist/commands/parameter/remove.d.ts +7 -7
  44. package/dist/commands/parameter/sync.d.ts +6 -6
  45. package/dist/commands/parameter/update.d.ts +7 -7
  46. package/dist/commands/stage/access.d.ts +15 -0
  47. package/dist/commands/stage/access.js +130 -0
  48. package/dist/commands/stage/create.d.ts +11 -11
  49. package/dist/commands/stage/list.d.ts +1 -1
  50. package/dist/commands/stage/list.js +21 -20
  51. package/dist/commands/stage/revoke.d.ts +18 -0
  52. package/dist/commands/stage/revoke.js +171 -0
  53. package/dist/commands/stage/share.d.ts +23 -0
  54. package/dist/commands/stage/share.js +292 -0
  55. package/dist/commands/test-api.d.ts +1 -1
  56. package/dist/services/auth-service.d.ts +15 -82
  57. package/dist/services/auth-service.js +24 -237
  58. package/dist/services/hyperdrive-sigv4.d.ts +37 -24
  59. package/dist/services/hyperdrive-sigv4.js +62 -193
  60. package/dist/services/tenant-service.d.ts +6 -0
  61. package/dist/services/tenant-service.js +13 -0
  62. package/dist/utils/auth-flow.d.ts +1 -0
  63. package/dist/utils/auth-flow.js +2 -0
  64. package/dist/utils/table.d.ts +17 -0
  65. package/dist/utils/table.js +41 -0
  66. package/oclif.manifest.json +309 -81
  67. package/package.json +55 -15
@@ -1,3 +1,10 @@
1
+ /**
2
+ * Hyperdrive API Service with AWS SigV4 authentication
3
+ *
4
+ * This service extends @devsquad/cli-auth SigV4ApiClient to provide
5
+ * hyperdrive-specific API methods with AWS Signature V4 signing.
6
+ */
7
+ import { SigV4ApiClient, type OutdatedRoleErrorResponse } from '@hyperdrive.bot/cli-auth';
1
8
  import { LoggingConfig } from './log-tailer.js';
2
9
  interface DeploymentCheckResponse {
3
10
  commit?: string;
@@ -131,16 +138,9 @@ interface GitAuthInitiateResponse {
131
138
  }
132
139
  /**
133
140
  * Hyperdrive API Service with AWS SigV4 authentication
134
- *
135
- * This service signs all API requests using AWS Signature Version 4,
136
- * which allows both CLI and web frontend to use the same endpoints
137
- * with unified AWS IAM authentication via Cognito.
138
141
  */
139
- export declare class HyperdriveSigV4Service {
140
- private apiUrl;
141
- private authService;
142
- private region;
143
- private tenantDomain;
142
+ export declare class HyperdriveSigV4Service extends SigV4ApiClient {
143
+ private readonly userGroupsApiUrl?;
144
144
  constructor(domain?: string);
145
145
  accountAdd(params: {
146
146
  accountId: string;
@@ -268,9 +268,6 @@ export declare class HyperdriveSigV4Service {
268
268
  };
269
269
  success: boolean;
270
270
  }>;
271
- /**
272
- * Test API connection
273
- */
274
271
  makeTestRequest(): Promise<Record<string, unknown>>;
275
272
  moduleAnalyze(slug: string): Promise<{
276
273
  jobId: string;
@@ -433,17 +430,33 @@ export declare class HyperdriveSigV4Service {
433
430
  name: string;
434
431
  }): Promise<StageResponse>;
435
432
  stageList(): Promise<StageResponse[]>;
436
- private handleError;
437
- private handleOutdatedCLI;
438
- private handleOutdatedRole;
439
- private handleResponse;
440
- /**
441
- * Make a signed API request
442
- */
443
- private makeSignedRequest;
444
- /**
445
- * Sign an HTTP request using AWS Signature V4
446
- */
447
- private signRequest;
433
+ stageAccessGet(stageName: string): Promise<{
434
+ access: Array<{
435
+ role: string;
436
+ targetId: string;
437
+ targetType: 'user' | 'group';
438
+ }>;
439
+ stageId: string;
440
+ stageName: string;
441
+ }>;
442
+ stageAccessGrant(stageName: string, access: Array<{
443
+ role: 'viewer' | 'deployer' | 'manager';
444
+ targetId: string;
445
+ targetType: 'user' | 'group';
446
+ }>): Promise<{
447
+ added: number;
448
+ message: string;
449
+ }>;
450
+ stageAccessRevoke(stageName: string, targetType: 'user' | 'group', targetId: string): Promise<{
451
+ message: string;
452
+ }>;
453
+ groupList(): Promise<Array<{
454
+ groupId: string;
455
+ name: string;
456
+ description?: string;
457
+ memberCount: number;
458
+ isSystemGroup: boolean;
459
+ }>>;
460
+ protected handleOutdatedRole(errorData: OutdatedRoleErrorResponse): Promise<void>;
448
461
  }
449
462
  export {};
@@ -1,72 +1,45 @@
1
- import { Sha256 } from '@aws-crypto/sha256-js';
2
- import { HttpRequest } from '@smithy/protocol-http';
3
- import { SignatureV4 } from '@smithy/signature-v4';
4
- import axios from 'axios';
1
+ /**
2
+ * Hyperdrive API Service with AWS SigV4 authentication
3
+ *
4
+ * This service extends @devsquad/cli-auth SigV4ApiClient to provide
5
+ * hyperdrive-specific API methods with AWS Signature V4 signing.
6
+ */
7
+ import { SigV4ApiClient, } from '@hyperdrive.bot/cli-auth';
5
8
  import { readFileSync } from 'fs';
6
9
  import { dirname, join } from 'path';
7
10
  import { fileURLToPath } from 'url';
8
- import { AuthService } from './auth-service.js';
11
+ import { HYPERDRIVE_AUTH_CONFIG } from './auth-service.js';
9
12
  // Get CLI version from package.json
10
13
  const __filename = fileURLToPath(import.meta.url);
11
14
  const __dirname = dirname(__filename);
12
15
  const packageJson = JSON.parse(readFileSync(join(__dirname, '../../package.json'), 'utf-8'));
13
16
  const CLI_VERSION = packageJson.version;
14
- // Type guards
15
- function isOutdatedCLIError(data) {
16
- return (data &&
17
- data.code === 'OUTDATED_CLI_VERSION' &&
18
- data.data &&
19
- typeof data.data.currentVersion === 'string' &&
20
- typeof data.data.requiredVersion === 'string' &&
21
- typeof data.data.updateCommand === 'string');
22
- }
23
- function isOutdatedRoleError(data) {
24
- return (data &&
25
- data.code === 'OUTDATED_ROLE_VERSION' &&
26
- data.data &&
27
- typeof data.data.accountId === 'string' &&
28
- typeof data.data.currentVersion === 'string' &&
29
- typeof data.data.requiredVersion === 'string' &&
30
- typeof data.data.quickCreateUrl === 'string');
31
- }
32
17
  /**
33
18
  * Hyperdrive API Service with AWS SigV4 authentication
34
- *
35
- * This service signs all API requests using AWS Signature Version 4,
36
- * which allows both CLI and web frontend to use the same endpoints
37
- * with unified AWS IAM authentication via Cognito.
38
19
  */
39
- export class HyperdriveSigV4Service {
40
- apiUrl;
41
- authService;
42
- region;
43
- tenantDomain;
20
+ export class HyperdriveSigV4Service extends SigV4ApiClient {
21
+ userGroupsApiUrl;
44
22
  constructor(domain) {
45
- this.authService = new AuthService(domain);
46
- // Load credentials to get API URL and region
47
- const credentials = this.authService.loadCredentials();
48
- if (!credentials) {
49
- const domainMsg = domain ? ` for domain "${domain}"` : '';
50
- throw new Error(`Not authenticated${domainMsg}. Please run "hd auth login${domain ? ' --domain ' + domain : ''}" first.`);
51
- }
52
- // Use environment variable override if set, otherwise use from credentials
53
- this.apiUrl = process.env.HYPERDRIVE_API_URL || credentials.apiUrl;
54
- this.region = process.env.HYPERDRIVE_AWS_REGION || credentials.region;
55
- this.tenantDomain = credentials.tenantDomain;
56
- if (!this.apiUrl) {
57
- throw new Error('Hyperdrive API URL not configured.\n' +
58
- 'Please re-authenticate with "hd auth login".');
59
- }
23
+ super({
24
+ authConfig: HYPERDRIVE_AUTH_CONFIG,
25
+ domain,
26
+ cliName: 'hyperdrive-cli',
27
+ cliVersion: CLI_VERSION,
28
+ apiUrlOverride: process.env.HYPERDRIVE_API_URL,
29
+ regionOverride: process.env.HYPERDRIVE_AWS_REGION,
30
+ });
31
+ // Get user groups API URL from credentials
32
+ this.userGroupsApiUrl = this.getAdditionalApiUrl('userGroupsApiUrl');
60
33
  }
34
+ // ============================================================================
35
+ // AWS Account Methods
36
+ // ============================================================================
61
37
  async accountAdd(params) {
62
38
  return this.makeSignedRequest('POST', '/cloud-account/aws/account', params);
63
39
  }
64
40
  async accountGet(params) {
65
41
  return this.makeSignedRequest('GET', `/cloud-account/aws/account/${params.accountId}`);
66
42
  }
67
- // ============================================================================
68
- // Public API Methods
69
- // ============================================================================
70
43
  async accountList() {
71
44
  return this.makeSignedRequest('GET', '/cloud-account/aws/accounts');
72
45
  }
@@ -76,6 +49,9 @@ export class HyperdriveSigV4Service {
76
49
  async accountVerify(params) {
77
50
  return this.makeSignedRequest('POST', `/cloud-account/aws/account/${params.accountId}/verify`);
78
51
  }
52
+ // ============================================================================
53
+ // CI Account Methods
54
+ // ============================================================================
79
55
  async ciAccountCreate(params) {
80
56
  return this.makeSignedRequest('POST', '/ci/accounts', params);
81
57
  }
@@ -85,6 +61,9 @@ export class HyperdriveSigV4Service {
85
61
  async ciAccountList() {
86
62
  return this.makeSignedRequest('GET', '/ci/accounts');
87
63
  }
64
+ // ============================================================================
65
+ // Deployment Methods
66
+ // ============================================================================
88
67
  async deploymentCheck(params) {
89
68
  return this.makeSignedRequest('POST', '/deployments/check', params);
90
69
  }
@@ -97,6 +76,9 @@ export class HyperdriveSigV4Service {
97
76
  async deploymentList(params) {
98
77
  return this.makeSignedRequest('GET', `/deployments?projectSlug=${params.projectSlug}&stage=${params.stage}`);
99
78
  }
79
+ // ============================================================================
80
+ // Git Integration Methods
81
+ // ============================================================================
100
82
  async gitAuthInitiate(provider) {
101
83
  return this.makeSignedRequest('POST', `/git/${provider}/auth/initiate`);
102
84
  }
@@ -119,12 +101,15 @@ export class HyperdriveSigV4Service {
119
101
  });
120
102
  return this.makeSignedRequest('GET', `/git/repos?${queryParams}`);
121
103
  }
104
+ // ============================================================================
105
+ // Jira Integration Methods
106
+ // ============================================================================
122
107
  async jiraPreRegister(params) {
123
108
  return this.makeSignedRequest('POST', '/hyperdrive/jira/pre-register', params);
124
109
  }
125
- /**
126
- * Test API connection
127
- */
110
+ // ============================================================================
111
+ // Module Methods
112
+ // ============================================================================
128
113
  async makeTestRequest() {
129
114
  return this.makeSignedRequest('GET', '/');
130
115
  }
@@ -145,16 +130,12 @@ export class HyperdriveSigV4Service {
145
130
  return await this.makeSignedRequest('GET', `/modules/${slug}/dockerfile`);
146
131
  }
147
132
  catch (error) {
148
- // Return null if 404 (not found)
149
133
  if (error?.response?.status === 404) {
150
134
  return null;
151
135
  }
152
136
  throw error;
153
137
  }
154
138
  }
155
- // ============================================================================
156
- // AWS Account Methods
157
- // ============================================================================
158
139
  async moduleLink(params) {
159
140
  return this.makeSignedRequest('POST', '/modules/link', params);
160
141
  }
@@ -168,12 +149,12 @@ export class HyperdriveSigV4Service {
168
149
  const { slug, ...updateData } = params;
169
150
  return this.makeSignedRequest('PUT', `/modules/${slug}`, updateData);
170
151
  }
152
+ // ============================================================================
153
+ // Parameter Methods
154
+ // ============================================================================
171
155
  async parameterAdd(params) {
172
156
  return this.makeSignedRequest('POST', '/parameters', params);
173
157
  }
174
- // ============================================================================
175
- // Git Integration Methods
176
- // ============================================================================
177
158
  async parameterBackfill(params) {
178
159
  return this.makeSignedRequest('POST', '/parameters/backfill', params);
179
160
  }
@@ -189,15 +170,9 @@ export class HyperdriveSigV4Service {
189
170
  async parameterSync(params) {
190
171
  return this.makeSignedRequest('POST', '/parameters/sync', params);
191
172
  }
192
- // ============================================================================
193
- // Jira Integration Methods
194
- // ============================================================================
195
173
  async parameterSyncStatus(params) {
196
174
  return this.makeSignedRequest('GET', `/parameters/sync/${params.taskId}`);
197
175
  }
198
- // ============================================================================
199
- // CI Account Methods
200
- // ============================================================================
201
176
  async parameterUpdate(params) {
202
177
  return this.makeSignedRequest('PUT', `/parameters/${params.key}`, {
203
178
  accountId: params.accountId,
@@ -207,59 +182,39 @@ export class HyperdriveSigV4Service {
207
182
  stage: params.stage
208
183
  });
209
184
  }
185
+ // ============================================================================
186
+ // Stage Methods
187
+ // ============================================================================
210
188
  async stageCreate(params) {
211
189
  return this.makeSignedRequest('POST', '/stages', params);
212
190
  }
213
191
  async stageGet(params) {
214
192
  return this.makeSignedRequest('GET', `/stages/${params.name}`);
215
193
  }
216
- // ============================================================================
217
- // Dockerfile Discovery Methods
218
- // ============================================================================
219
194
  async stageList() {
220
195
  return this.makeSignedRequest('GET', '/stages');
221
196
  }
222
- async handleError(error) {
223
- const axiosError = error;
224
- if (axiosError.response) {
225
- const data = axiosError.response.data;
226
- // Handle outdated CLI version error with type safety
227
- if (axiosError.response.status === 426 && isOutdatedCLIError(data)) {
228
- await this.handleOutdatedCLI(data);
229
- }
230
- // Handle outdated role version error with type safety
231
- if (axiosError.response.status === 426 && isOutdatedRoleError(data)) {
232
- await this.handleOutdatedRole(data);
233
- }
234
- console.error('Error response:', axiosError.response.data);
235
- console.error('Error status:', axiosError.response.status);
236
- console.error('Error headers:', axiosError.response.headers);
237
- }
238
- else if (axiosError.request) {
239
- console.error('Error request:', axiosError.request);
240
- }
241
- else {
242
- console.error('Error message:', axiosError.message);
243
- }
244
- throw axiosError;
197
+ // ============================================================================
198
+ // Stage Access Methods
199
+ // ============================================================================
200
+ async stageAccessGet(stageName) {
201
+ return this.makeSignedRequest('GET', `/stages/${stageName}/access`);
245
202
  }
246
- async handleOutdatedCLI(errorData) {
247
- const { data } = errorData;
248
- console.error('\n❌ Your Hyperdrive CLI is outdated\n');
249
- console.error(`Current version: ${data.currentVersion}`);
250
- console.error(`Required version: ${data.requiredVersion}\n`);
251
- console.error('To update:');
252
- console.error(' npm install -g hyperdrive.bot@latest');
253
- console.error(' # or');
254
- console.error(' hd update\n');
255
- // Throw error to be caught by top-level handler
256
- const error = new Error('CLI_OUTDATED');
257
- error.code = 'OUTDATED_CLI_VERSION';
258
- error.data = data;
259
- throw error;
203
+ async stageAccessGrant(stageName, access) {
204
+ return this.makeSignedRequest('PUT', `/stages/${stageName}/access`, { access });
205
+ }
206
+ async stageAccessRevoke(stageName, targetType, targetId) {
207
+ return this.makeSignedRequest('DELETE', `/stages/${stageName}/access/${targetType}/${targetId}`);
260
208
  }
261
209
  // ============================================================================
262
- // Helper Methods
210
+ // User Groups Methods
211
+ // ============================================================================
212
+ async groupList() {
213
+ const response = await this.makeSignedRequest('GET', '/groups', undefined, this.userGroupsApiUrl);
214
+ return Array.isArray(response) ? response : [];
215
+ }
216
+ // ============================================================================
217
+ // Override error handling for hyperdrive-specific behavior
263
218
  // ============================================================================
264
219
  async handleOutdatedRole(errorData) {
265
220
  const { data } = errorData;
@@ -284,92 +239,6 @@ export class HyperdriveSigV4Service {
284
239
  console.error('\nUpdate URL (copy and paste):');
285
240
  console.error(` ${data.quickCreateUrl}\n`);
286
241
  }
287
- // Exit gracefully - user needs to update their role first
288
242
  process.exit(1);
289
243
  }
290
- handleResponse(response) {
291
- return response.data;
292
- }
293
- /**
294
- * Make a signed API request
295
- */
296
- async makeSignedRequest(method, path, data) {
297
- try {
298
- // Ensure we have valid credentials (auto-refreshes if needed)
299
- const credentials = await this.authService.ensureValidCredentials();
300
- // Sign the request
301
- const signedRequest = await this.signRequest(method, path, credentials.awsCredentials, data);
302
- // Convert signed request to axios config
303
- // Build URL with query string if present
304
- let url = `${signedRequest.protocol}//${signedRequest.hostname}${signedRequest.path}`;
305
- if (signedRequest.query && Object.keys(signedRequest.query).length > 0) {
306
- const queryString = new URLSearchParams(signedRequest.query).toString();
307
- url += `?${queryString}`;
308
- }
309
- const response = await axios({
310
- data: signedRequest.body,
311
- headers: signedRequest.headers,
312
- method: signedRequest.method,
313
- url,
314
- });
315
- return this.handleResponse(response);
316
- }
317
- catch (error) {
318
- return this.handleError(error);
319
- }
320
- }
321
- /**
322
- * Sign an HTTP request using AWS Signature V4
323
- */
324
- async signRequest(method, path, credentials, body) {
325
- // Parse API URL
326
- const url = new URL(this.apiUrl);
327
- const hostname = url.hostname;
328
- const protocol = url.protocol.replace(':', '');
329
- const basePath = url.pathname.replace(/\/$/, ''); // Remove trailing slash
330
- // Parse query string from path if present
331
- let pathWithoutQuery = path;
332
- let query;
333
- const queryIndex = path.indexOf('?');
334
- if (queryIndex !== -1) {
335
- pathWithoutQuery = path.substring(0, queryIndex);
336
- const queryString = path.substring(queryIndex + 1);
337
- query = {};
338
- const params = new URLSearchParams(queryString);
339
- for (const [key, value] of params) {
340
- query[key] = value;
341
- }
342
- }
343
- // Combine base path with request path (without query string)
344
- const fullPath = basePath + pathWithoutQuery;
345
- // Create signer
346
- const signer = new SignatureV4({
347
- credentials: {
348
- accessKeyId: credentials.accessKeyId,
349
- secretAccessKey: credentials.secretAccessKey,
350
- sessionToken: credentials.sessionToken,
351
- },
352
- region: this.region,
353
- service: 'execute-api',
354
- sha256: Sha256,
355
- });
356
- // Prepare request
357
- const request = new HttpRequest({
358
- body: body ? JSON.stringify(body) : undefined,
359
- headers: {
360
- 'Content-Type': 'application/json',
361
- 'User-Agent': `hyperdrive-cli/${CLI_VERSION}`,
362
- 'X-Tenant-Domain': this.tenantDomain,
363
- host: hostname,
364
- },
365
- hostname,
366
- method: method.toUpperCase(),
367
- path: fullPath,
368
- protocol,
369
- query,
370
- });
371
- // Sign the request
372
- const signedRequest = await signer.sign(request);
373
- return signedRequest;
374
- }
375
244
  }
@@ -8,6 +8,7 @@ export interface TenantConfig {
8
8
  region: string;
9
9
  tenantDomain: string;
10
10
  tenantId: string;
11
+ userGroupsApiUrl?: string;
11
12
  }
12
13
  export interface CLIConfig {
13
14
  apiUrl?: string;
@@ -87,6 +88,11 @@ export declare class TenantService {
87
88
  * Get API URL for a region
88
89
  */
89
90
  private getApiUrl;
91
+ /**
92
+ * Get User Groups API URL from bootstrap config
93
+ * Returns undefined if not available (optional module)
94
+ */
95
+ private getUserGroupsApiUrl;
90
96
  /**
91
97
  * Get bootstrap URL with fallback chain:
92
98
  * 1. Environment variable
@@ -85,6 +85,7 @@ export class TenantService {
85
85
  region,
86
86
  tenantDomain: domain,
87
87
  tenantId: bootstrap.tenantId,
88
+ userGroupsApiUrl: this.getUserGroupsApiUrl(bootstrap.amplifyConfig.API),
88
89
  };
89
90
  // Cache the tenant domain for future use
90
91
  this.saveTenantDomainToConfig(domain);
@@ -269,6 +270,18 @@ export class TenantService {
269
270
  chalk.yellow('This tenant does not have access to Hyperdrive.\n') +
270
271
  chalk.gray('Please contact your administrator to enable the Hyperdrive module.'));
271
272
  }
273
+ /**
274
+ * Get User Groups API URL from bootstrap config
275
+ * Returns undefined if not available (optional module)
276
+ */
277
+ getUserGroupsApiUrl(apiConfig) {
278
+ // Check for user-groups endpoint in bootstrap
279
+ if (apiConfig?.REST?.['user-groups']?.endpoint) {
280
+ console.log(chalk.gray(`✓ Using user-groups API endpoint from bootstrap: ${apiConfig.REST['user-groups'].endpoint}`));
281
+ return apiConfig.REST['user-groups'].endpoint;
282
+ }
283
+ return undefined;
284
+ }
272
285
  /**
273
286
  * Get bootstrap URL with fallback chain:
274
287
  * 1. Environment variable
@@ -55,6 +55,7 @@ export interface StoredCredentials extends CognitoTokens {
55
55
  region: string;
56
56
  tenantDomain: string;
57
57
  tenantId: string;
58
+ userGroupsApiUrl?: string;
58
59
  }
59
60
  /**
60
61
  * Generate PKCE code verifier (random base64url string)
@@ -333,6 +333,7 @@ export async function executeAuthFlow(options) {
333
333
  region: tenantConfig.region,
334
334
  tenantDomain: tenantConfig.tenantDomain,
335
335
  tenantId: tenantConfig.tenantId,
336
+ userGroupsApiUrl: tenantConfig.userGroupsApiUrl,
336
337
  }, tenantConfig.tenantDomain);
337
338
  return { success: true };
338
339
  }
@@ -420,6 +421,7 @@ export async function executeCIAuthFlow(options) {
420
421
  tenantDomain: tenantConfig.tenantDomain,
421
422
  tenantId: tenantConfig.tenantId,
422
423
  token_type: authResult.TokenType || 'Bearer',
424
+ userGroupsApiUrl: tenantConfig.userGroupsApiUrl,
423
425
  }, tenantConfig.tenantDomain);
424
426
  return { success: true };
425
427
  }
@@ -0,0 +1,17 @@
1
+ interface ColumnConfig<T> {
2
+ header: string;
3
+ minWidth?: number;
4
+ get?: (row: T) => string;
5
+ }
6
+ type TableColumns<T> = Record<string, ColumnConfig<T>>;
7
+ /**
8
+ * Print data in a table format using cli-table3
9
+ * Replacement for oclif's deprecated ux.table
10
+ */
11
+ export declare function printTable<T extends Record<string, unknown>>(data: T[], columns: TableColumns<T>, logger: (msg: string) => void): void;
12
+ /**
13
+ * Print a styled header
14
+ * Replacement for oclif's deprecated ux.styledHeader
15
+ */
16
+ export declare function printHeader(text: string, logger: (msg: string) => void): void;
17
+ export {};
@@ -0,0 +1,41 @@
1
+ import Table from 'cli-table3';
2
+ import chalk from 'chalk';
3
+ /**
4
+ * Print data in a table format using cli-table3
5
+ * Replacement for oclif's deprecated ux.table
6
+ */
7
+ export function printTable(data, columns, logger) {
8
+ const columnKeys = Object.keys(columns);
9
+ const headers = columnKeys.map(key => chalk.bold(columns[key].header));
10
+ const colWidths = columnKeys.map(key => columns[key].minWidth ?? null);
11
+ const hasColWidths = colWidths.some(w => w !== null);
12
+ const tableOptions = {
13
+ head: headers,
14
+ style: { head: [], border: [] },
15
+ };
16
+ if (hasColWidths) {
17
+ tableOptions.colWidths = colWidths;
18
+ }
19
+ const table = new Table(tableOptions);
20
+ for (const row of data) {
21
+ const rowData = columnKeys.map(key => {
22
+ const col = columns[key];
23
+ if (col.get) {
24
+ return col.get(row);
25
+ }
26
+ const value = row[key];
27
+ return value !== undefined && value !== null ? String(value) : '';
28
+ });
29
+ table.push(rowData);
30
+ }
31
+ logger(table.toString());
32
+ }
33
+ /**
34
+ * Print a styled header
35
+ * Replacement for oclif's deprecated ux.styledHeader
36
+ */
37
+ export function printHeader(text, logger) {
38
+ logger('');
39
+ logger(chalk.bold(text));
40
+ logger(chalk.gray('─'.repeat(Math.min(text.length + 10, 60))));
41
+ }