@dishantlangayan/sc-cli-core 0.4.0 → 0.5.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/README.md CHANGED
@@ -23,6 +23,8 @@ The ScConnection class provide abstraction functions for Solace Cloud API REST c
23
23
  ## OrgManager
24
24
  The OrgManager class provides utility functions to store and retrieve Solace Cloud authentication information from user's home directory: `~/.sc/` or `%USERPROFILE%\sc\`. The implementation uses AES-256-GCM for authenticated encryption and provides machine-bound encryption that combines OS-level security (keychain) with machine-specific identifiers, making credentials non-transferable between machines.
25
25
 
26
+ Supports changing of the Solace Cloud REST API base url using the environment variable `SC_BASE_URL` and API version using `SC_API_VERSION`.
27
+
26
28
  ## BrokerAuthManager
27
29
  The BrokerAuthManager class provides utility functions to store and retrieve broker SEMP management authentication information similar to the `OrgManager` class. It supports Basic and OAuth authentication schemes.
28
30
 
@@ -85,8 +85,11 @@ export class BrokerAuthManager {
85
85
  }
86
86
  const baseURL = `${broker.sempEndpoint}:${broker.sempPort}`;
87
87
  const accessToken = broker.authType === AuthType.OAUTH ? broker.accessToken : broker.encodedCredentials;
88
- const isBasic = broker.authType === AuthType.BASIC;
89
- return new ScConnection(baseURL, accessToken, timeout, isBasic);
88
+ return new ScConnection(baseURL, accessToken, {
89
+ apiType: 'semp',
90
+ authType: broker.authType === AuthType.BASIC ? 'basic' : 'bearer',
91
+ timeout,
92
+ });
90
93
  }
91
94
  /**
92
95
  * Get all broker configurations
@@ -1,7 +1,9 @@
1
1
  /**
2
- * Broker authentication management module
3
- * Provides encrypted storage for SEMP broker credentials
2
+ * Authentication management module
3
+ * Provides encrypted storage for broker and organization credentials
4
4
  */
5
5
  export { BrokerAuthEncryption } from './auth-encryption.js';
6
6
  export { BrokerAuthManager } from './auth-manager.js';
7
7
  export { AuthType, type BasicBrokerAuth, type BrokerAuth, type BrokerAuthBase, BrokerAuthError, BrokerAuthErrorCode, type BrokerAuthStorage, type EncryptedData, type EncryptionMetadata, type OAuthBrokerAuth, } from './auth-types.js';
8
+ export { OrgManager } from './org-manager.js';
9
+ export { type OrgConfig, OrgError, OrgErrorCode, type OrgStorage } from './org-types.js';
package/lib/auth/index.js CHANGED
@@ -1,7 +1,9 @@
1
1
  /**
2
- * Broker authentication management module
3
- * Provides encrypted storage for SEMP broker credentials
2
+ * Authentication management module
3
+ * Provides encrypted storage for broker and organization credentials
4
4
  */
5
5
  export { BrokerAuthEncryption } from './auth-encryption.js';
6
6
  export { BrokerAuthManager } from './auth-manager.js';
7
7
  export { AuthType, BrokerAuthError, BrokerAuthErrorCode, } from './auth-types.js';
8
+ export { OrgManager } from './org-manager.js';
9
+ export { OrgError, OrgErrorCode } from './org-types.js';
@@ -1,3 +1,4 @@
1
+ import { ScConnection } from '../util/sc-connection.js';
1
2
  import { KeychainService } from './keychain.js';
2
3
  import { type OrgConfig } from './org-types.js';
3
4
  /**
@@ -28,6 +29,13 @@ export declare class OrgManager {
28
29
  * Clear all organization configurations
29
30
  */
30
31
  clearAll(): Promise<void>;
32
+ /**
33
+ * Create ScConnection instance from stored org config
34
+ * @param identifier - Organization ID or alias
35
+ * @param timeout - Optional timeout override (default: 10000ms)
36
+ * @returns Configured ScConnection instance
37
+ */
38
+ createConnection(identifier: string, timeout?: number): Promise<ScConnection>;
31
39
  /**
32
40
  * Get all organization configurations
33
41
  * @returns Array of all organization configurations
@@ -1,6 +1,8 @@
1
1
  import { mkdir, readFile, rename, unlink, writeFile } from 'node:fs/promises';
2
2
  import { homedir } from 'node:os';
3
3
  import { join } from 'node:path';
4
+ import { DefaultBaseUrl, EnvironmentVariable, envVars } from '../config/env-vars.js';
5
+ import { ScConnection } from '../util/sc-connection.js';
4
6
  import { BrokerAuthEncryption } from './auth-encryption.js';
5
7
  import { KeychainService } from './keychain.js';
6
8
  import { OrgError, OrgErrorCode } from './org-types.js';
@@ -71,6 +73,29 @@ export class OrgManager {
71
73
  this.storage.orgs = [];
72
74
  await this.saveStorage();
73
75
  }
76
+ /**
77
+ * Create ScConnection instance from stored org config
78
+ * @param identifier - Organization ID or alias
79
+ * @param timeout - Optional timeout override (default: 10000ms)
80
+ * @returns Configured ScConnection instance
81
+ */
82
+ async createConnection(identifier, timeout = 10_000) {
83
+ this.ensureInitialized();
84
+ const org = await this.getOrg(identifier);
85
+ if (!org) {
86
+ throw new OrgError(`Organization '${identifier}' not found`, OrgErrorCode.ORG_NOT_FOUND);
87
+ }
88
+ // Get base URL: org config → env var → default
89
+ const baseURL = org.baseUrl ?? envVars.getString(EnvironmentVariable.SC_BASE_URL, DefaultBaseUrl);
90
+ // Get API version: org config → env var → default 'v2'
91
+ const apiVersion = org.apiVersion ?? envVars.getString(EnvironmentVariable.SC_API_VERSION, 'v2');
92
+ return new ScConnection(baseURL, org.accessToken, {
93
+ apiType: 'cloud',
94
+ apiVersion,
95
+ authType: 'bearer',
96
+ timeout,
97
+ });
98
+ }
74
99
  /**
75
100
  * Get all organization configurations
76
101
  * @returns Array of all organization configurations
@@ -332,5 +357,24 @@ export class OrgManager {
332
357
  if (org.alias !== undefined && org.alias.trim() === '') {
333
358
  throw new OrgError('Alias cannot be empty if provided', OrgErrorCode.INVALID_ORG_ID);
334
359
  }
360
+ // Validate apiVersion if provided
361
+ if (org.apiVersion !== undefined && org.apiVersion.trim() === '') {
362
+ throw new OrgError('API version cannot be empty if provided', OrgErrorCode.INVALID_API_VERSION);
363
+ }
364
+ // Validate baseUrl if provided
365
+ if (org.baseUrl !== undefined) {
366
+ // Must not be empty or whitespace
367
+ if (org.baseUrl.trim() === '') {
368
+ throw new OrgError('Base URL cannot be empty if provided', OrgErrorCode.INVALID_BASE_URL);
369
+ }
370
+ // Must start with http:// or https://
371
+ if (!(org.baseUrl.startsWith('http://') || org.baseUrl.startsWith('https://'))) {
372
+ throw new OrgError('Base URL must start with http:// or https://', OrgErrorCode.INVALID_BASE_URL);
373
+ }
374
+ // Should not end with trailing slash
375
+ if (org.baseUrl.endsWith('/')) {
376
+ throw new OrgError('Base URL should not end with a slash', OrgErrorCode.INVALID_BASE_URL);
377
+ }
378
+ }
335
379
  }
336
380
  }
@@ -4,6 +4,8 @@
4
4
  export interface OrgConfig {
5
5
  accessToken: string;
6
6
  alias?: string;
7
+ apiVersion?: string;
8
+ baseUrl?: string;
7
9
  isDefault?: boolean;
8
10
  orgId: string;
9
11
  }
@@ -23,6 +25,8 @@ export declare enum OrgErrorCode {
23
25
  FILE_READ_ERROR = "FILE_READ_ERROR",
24
26
  FILE_WRITE_ERROR = "FILE_WRITE_ERROR",
25
27
  INVALID_ACCESS_TOKEN = "INVALID_ACCESS_TOKEN",
28
+ INVALID_API_VERSION = "INVALID_API_VERSION",
29
+ INVALID_BASE_URL = "INVALID_BASE_URL",
26
30
  INVALID_ORG_ID = "INVALID_ORG_ID",
27
31
  NOT_INITIALIZED = "NOT_INITIALIZED",
28
32
  ORG_ALREADY_EXISTS = "ORG_ALREADY_EXISTS",
@@ -8,6 +8,8 @@ export var OrgErrorCode;
8
8
  OrgErrorCode["FILE_READ_ERROR"] = "FILE_READ_ERROR";
9
9
  OrgErrorCode["FILE_WRITE_ERROR"] = "FILE_WRITE_ERROR";
10
10
  OrgErrorCode["INVALID_ACCESS_TOKEN"] = "INVALID_ACCESS_TOKEN";
11
+ OrgErrorCode["INVALID_API_VERSION"] = "INVALID_API_VERSION";
12
+ OrgErrorCode["INVALID_BASE_URL"] = "INVALID_BASE_URL";
11
13
  OrgErrorCode["INVALID_ORG_ID"] = "INVALID_ORG_ID";
12
14
  OrgErrorCode["NOT_INITIALIZED"] = "NOT_INITIALIZED";
13
15
  OrgErrorCode["ORG_ALREADY_EXISTS"] = "ORG_ALREADY_EXISTS";
@@ -1,8 +1,7 @@
1
1
  export declare enum EnvironmentVariable {
2
2
  'SC_ACCESS_TOKEN' = "SC_ACCESS_TOKEN",
3
3
  'SC_API_VERSION' = "SC_API_VERSION",
4
- 'SC_BASE_URL' = "SC_BASE_URL",
5
- 'SEMP_API_VERSION' = "SEMP_API_VERSION"
4
+ 'SC_BASE_URL' = "SC_BASE_URL"
6
5
  }
7
6
  export declare const DefaultBaseUrl = "https://api.solace.cloud";
8
7
  /**
@@ -3,7 +3,6 @@ export var EnvironmentVariable;
3
3
  EnvironmentVariable["SC_ACCESS_TOKEN"] = "SC_ACCESS_TOKEN";
4
4
  EnvironmentVariable["SC_API_VERSION"] = "SC_API_VERSION";
5
5
  EnvironmentVariable["SC_BASE_URL"] = "SC_BASE_URL";
6
- EnvironmentVariable["SEMP_API_VERSION"] = "SEMP_API_VERSION";
7
6
  })(EnvironmentVariable || (EnvironmentVariable = {}));
8
7
  export const DefaultBaseUrl = 'https://api.solace.cloud';
9
8
  /**
package/lib/exported.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- export { AuthType, type BasicBrokerAuth, type BrokerAuth, BrokerAuthError, BrokerAuthErrorCode, BrokerAuthManager, type OAuthBrokerAuth, } from './auth/index.js';
1
+ export { AuthType, type BasicBrokerAuth, type BrokerAuth, BrokerAuthError, BrokerAuthErrorCode, BrokerAuthManager, type OAuthBrokerAuth, type OrgConfig, OrgError, OrgErrorCode, OrgManager, type OrgStorage, } from './auth/index.js';
2
2
  export { EnvironmentVariable, envVars } from './config/env-vars.js';
3
3
  export { ScCommand } from './sc-command.js';
4
- export { ScConnection } from './util/sc-connection.js';
4
+ export { type ApiType, type HttpAuthType, ScConnection, type ScConnectionOptions } from './util/sc-connection.js';
5
5
  export { sleep } from './util/util.js';
6
6
  export * from './ux/table.js';
package/lib/exported.js CHANGED
@@ -1,4 +1,4 @@
1
- export { AuthType, BrokerAuthError, BrokerAuthErrorCode, BrokerAuthManager, } from './auth/index.js';
1
+ export { AuthType, BrokerAuthError, BrokerAuthErrorCode, BrokerAuthManager, OrgError, OrgErrorCode, OrgManager, } from './auth/index.js';
2
2
  export { EnvironmentVariable, envVars } from './config/env-vars.js';
3
3
  export { ScCommand } from './sc-command.js';
4
4
  export { ScConnection } from './util/sc-connection.js';
@@ -1,4 +1,6 @@
1
1
  import { Command, Interfaces } from '@oclif/core';
2
+ import { BrokerAuthManager } from './auth/auth-manager.js';
3
+ import { OrgManager } from './auth/org-manager.js';
2
4
  export type Flags<T extends typeof Command> = Interfaces.InferredFlags<(typeof ScCommand)['baseFlags'] & T['flags']>;
3
5
  export type Args<T extends typeof Command> = Interfaces.InferredArgs<T['args']>;
4
6
  /**
@@ -14,9 +16,21 @@ export declare abstract class ScCommand<T extends typeof Command> extends Comman
14
16
  static enableJsonFlag: boolean;
15
17
  protected args: Args<T>;
16
18
  protected flags: Flags<T>;
19
+ private _brokerAuthManager?;
20
+ private _orgManager?;
17
21
  protected catch(err: Error & {
18
22
  exitCode?: number;
19
23
  }): Promise<unknown>;
20
24
  protected finally(_: Error | undefined): Promise<unknown>;
25
+ /**
26
+ * Get BrokerAuthManager instance with lazy initialization
27
+ * @returns Initialized BrokerAuthManager instance
28
+ */
29
+ protected getBrokerAuthManager(): Promise<BrokerAuthManager>;
30
+ /**
31
+ * Get OrgManager instance with lazy initialization
32
+ * @returns Initialized OrgManager instance
33
+ */
34
+ protected getOrgManager(): Promise<OrgManager>;
21
35
  init(): Promise<void>;
22
36
  }
package/lib/sc-command.js CHANGED
@@ -1,4 +1,6 @@
1
1
  import { Command, Flags } from '@oclif/core';
2
+ import { BrokerAuthManager } from './auth/auth-manager.js';
3
+ import { OrgManager } from './auth/org-manager.js';
2
4
  import { DefaultBaseUrl, EnvironmentVariable, envVars } from './config/env-vars.js';
3
5
  /**
4
6
  * A base command that provided common functionality for all sc commands.
@@ -20,6 +22,8 @@ export class ScCommand extends Command {
20
22
  static enableJsonFlag = true;
21
23
  args;
22
24
  flags;
25
+ _brokerAuthManager;
26
+ _orgManager;
23
27
  async catch(err) {
24
28
  // add any custom logic to handle errors from the command
25
29
  // or simply return the parent class error handling
@@ -29,6 +33,28 @@ export class ScCommand extends Command {
29
33
  // called after run and catch regardless of whether or not the command errored
30
34
  return super.finally(_);
31
35
  }
36
+ /**
37
+ * Get BrokerAuthManager instance with lazy initialization
38
+ * @returns Initialized BrokerAuthManager instance
39
+ */
40
+ async getBrokerAuthManager() {
41
+ if (!this._brokerAuthManager) {
42
+ this._brokerAuthManager = BrokerAuthManager.getInstance();
43
+ await this._brokerAuthManager.initialize();
44
+ }
45
+ return this._brokerAuthManager;
46
+ }
47
+ /**
48
+ * Get OrgManager instance with lazy initialization
49
+ * @returns Initialized OrgManager instance
50
+ */
51
+ async getOrgManager() {
52
+ if (!this._orgManager) {
53
+ this._orgManager = OrgManager.getInstance();
54
+ await this._orgManager.initialize();
55
+ }
56
+ return this._orgManager;
57
+ }
32
58
  async init() {
33
59
  await super.init();
34
60
  const { args, flags } = await this.parse({
@@ -1,8 +1,16 @@
1
1
  import { AxiosRequestConfig } from 'axios';
2
+ export type HttpAuthType = 'basic' | 'bearer';
3
+ export type ApiType = 'cloud' | 'semp';
4
+ export interface ScConnectionOptions {
5
+ apiType?: ApiType;
6
+ apiVersion?: string;
7
+ authType?: HttpAuthType;
8
+ timeout?: number;
9
+ }
2
10
  export declare class ScConnection {
3
11
  private axiosInstance;
4
12
  private endpointUrl;
5
- constructor(baseURL?: string, accessToken?: string, timeout?: number, basic?: boolean);
13
+ constructor(baseURL: string, accessToken: string, options?: ScConnectionOptions);
6
14
  delete<T>(url: string, config?: AxiosRequestConfig): Promise<T>;
7
15
  get<T>(url: string, config?: AxiosRequestConfig): Promise<T>;
8
16
  patch<T>(url: string, data?: unknown, config?: AxiosRequestConfig): Promise<T>;
@@ -1,23 +1,33 @@
1
1
  import axios from 'axios';
2
- import { DefaultBaseUrl, EnvironmentVariable, envVars } from '../config/env-vars.js';
3
2
  export class ScConnection {
4
3
  axiosInstance;
5
4
  endpointUrl = '';
6
- constructor(baseURL = envVars.getString(EnvironmentVariable.SC_BASE_URL, DefaultBaseUrl), accessToken = envVars.getString(EnvironmentVariable.SC_ACCESS_TOKEN, ''), timeout = 10_000, basic = false) {
7
- const apiVersion = envVars.getString(EnvironmentVariable.SC_API_VERSION, 'v2');
8
- const sempApiVersion = envVars.getString(EnvironmentVariable.SEMP_API_VERSION, 'v2');
9
- this.endpointUrl = basic
10
- ? this.joinPaths(baseURL, `/SEMP/${sempApiVersion}`)
11
- : this.joinPaths(baseURL, `/api/${apiVersion}`);
5
+ constructor(baseURL, accessToken, options) {
6
+ // Validate required parameters
7
+ if (!baseURL || baseURL.trim() === '') {
8
+ throw new Error('baseURL is required and cannot be empty');
9
+ }
10
+ if (!accessToken || accessToken.trim() === '') {
11
+ throw new Error('accessToken is required and cannot be empty');
12
+ }
13
+ // Extract options with defaults
14
+ const { apiType = 'cloud', apiVersion = 'v2', authType = 'bearer', timeout = 10_000 } = options ?? {};
15
+ // Build endpoint URL based on apiType
16
+ // For SEMP: use baseURL directly (version specified in actual API calls)
17
+ // For cloud: append /api/{version}
18
+ const apiPath = apiType === 'semp' ? '' : `/api/${apiVersion}`;
19
+ this.endpointUrl = apiPath ? this.joinPaths(baseURL, apiPath) : baseURL;
20
+ // Build authorization header based on authType
21
+ const authHeader = authType === 'basic' ? `Basic ${accessToken}` : `Bearer ${accessToken}`;
12
22
  this.axiosInstance = axios.create({
13
23
  baseURL: this.endpointUrl,
14
24
  headers: {
15
- Authorization: basic ? `Basic ${accessToken}` : `Bearer ${accessToken}`,
25
+ Authorization: authHeader,
16
26
  'Content-Type': 'application/json',
17
27
  },
18
28
  timeout,
19
29
  });
20
- // Add interceptors if needed
30
+ // Add interceptors
21
31
  this.axiosInstance.interceptors.response.use((response) => response, (error) => {
22
32
  console.error('API Error:', error.response?.data || error.message);
23
33
  return Promise.reject(error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dishantlangayan/sc-cli-core",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Base library for the Solace Cloud CLI and plugins",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Dishant Langayan",