@robinmordasiewicz/f5xc-api-mcp 1.0.82-2512312028 → 1.0.82-2601012247

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 (58) hide show
  1. package/README.md +45 -0
  2. package/dist/auth/credential-manager.d.ts +55 -29
  3. package/dist/auth/credential-manager.d.ts.map +1 -1
  4. package/dist/auth/credential-manager.js +178 -100
  5. package/dist/auth/credential-manager.js.map +1 -1
  6. package/dist/auth/http-client.d.ts +5 -0
  7. package/dist/auth/http-client.d.ts.map +1 -1
  8. package/dist/auth/http-client.js +63 -11
  9. package/dist/auth/http-client.js.map +1 -1
  10. package/dist/auth/index.d.ts +5 -0
  11. package/dist/auth/index.d.ts.map +1 -1
  12. package/dist/auth/index.js +5 -0
  13. package/dist/auth/index.js.map +1 -1
  14. package/dist/config/paths.d.ts +34 -0
  15. package/dist/config/paths.d.ts.map +1 -0
  16. package/dist/config/paths.js +67 -0
  17. package/dist/config/paths.js.map +1 -0
  18. package/dist/index.d.ts +12 -6
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +22 -11
  21. package/dist/index.js.map +1 -1
  22. package/dist/profile/index.d.ts +9 -0
  23. package/dist/profile/index.d.ts.map +1 -0
  24. package/dist/profile/index.js +8 -0
  25. package/dist/profile/index.js.map +1 -0
  26. package/dist/profile/manager.d.ts +75 -0
  27. package/dist/profile/manager.d.ts.map +1 -0
  28. package/dist/profile/manager.js +327 -0
  29. package/dist/profile/manager.js.map +1 -0
  30. package/dist/profile/types.d.ts +55 -0
  31. package/dist/profile/types.d.ts.map +1 -0
  32. package/dist/profile/types.js +8 -0
  33. package/dist/profile/types.js.map +1 -0
  34. package/dist/server.d.ts +6 -1
  35. package/dist/server.d.ts.map +1 -1
  36. package/dist/server.js +8 -2
  37. package/dist/server.js.map +1 -1
  38. package/dist/utils/error-handling.d.ts +25 -0
  39. package/dist/utils/error-handling.d.ts.map +1 -1
  40. package/dist/utils/error-handling.js +127 -0
  41. package/dist/utils/error-handling.js.map +1 -1
  42. package/package.json +1 -1
  43. package/dist/config/config-manager.d.ts +0 -72
  44. package/dist/config/config-manager.d.ts.map +0 -1
  45. package/dist/config/config-manager.js +0 -247
  46. package/dist/config/config-manager.js.map +0 -1
  47. package/dist/config/index.d.ts +0 -7
  48. package/dist/config/index.d.ts.map +0 -1
  49. package/dist/config/index.js +0 -7
  50. package/dist/config/index.js.map +0 -1
  51. package/dist/config/schema.d.ts +0 -74
  52. package/dist/config/schema.d.ts.map +0 -1
  53. package/dist/config/schema.js +0 -75
  54. package/dist/config/schema.js.map +0 -1
  55. package/dist/config/types.d.ts +0 -77
  56. package/dist/config/types.d.ts.map +0 -1
  57. package/dist/config/types.js +0 -31
  58. package/dist/config/types.js.map +0 -1
package/README.md CHANGED
@@ -91,6 +91,8 @@ Add to your MCP settings:
91
91
  | `F5XC_P12_FILE` | For cert auth | Path to P12 certificate file |
92
92
  | `F5XC_P12_PASSWORD` | For cert auth | Password for P12 certificate |
93
93
  | `F5XC_PROFILE` | No | Profile name to use (default: `defaultProfile` from config) |
94
+ | `F5XC_TLS_INSECURE` | No | Disable SSL verification (staging only, set to `true`) |
95
+ | `F5XC_CA_BUNDLE` | No | Path to custom CA certificate bundle |
94
96
  | `LOG_LEVEL` | No | Logging verbosity (debug, info, warn, error) |
95
97
 
96
98
  ## Profile-Based Configuration
@@ -365,6 +367,49 @@ The server automatically normalizes various URL formats:
365
367
  | `tenant.console.ves.volterra.io` | `tenant.console.ves.volterra.io/api` |
366
368
  | `https://tenant.volterra.us/` | `https://tenant.console.ves.volterra.io/api` |
367
369
 
370
+ ## SSL/TLS Configuration
371
+
372
+ ### Staging Environment Certificate Issue
373
+
374
+ F5 XC staging environments use URLs like `tenant.staging.console.ves.volterra.io`, but the SSL
375
+ certificate only covers `*.console.ves.volterra.io`. This causes SSL validation failures because
376
+ wildcards only match a single subdomain level, not two levels (`tenant.staging`).
377
+
378
+ **Error Example:**
379
+
380
+ ```
381
+ Hostname/IP does not match certificate's altnames:
382
+ Host: tenant.staging.console.ves.volterra.io
383
+ Cert covers: DNS:*.console.ves.volterra.io, DNS:console.ves.volterra.io
384
+ ```
385
+
386
+ ### Solutions
387
+
388
+ #### Option 1: Custom CA Bundle (Recommended)
389
+
390
+ If your organization uses a custom CA:
391
+
392
+ ```bash
393
+ export F5XC_CA_BUNDLE=/path/to/your/ca-bundle.crt
394
+ ```
395
+
396
+ #### Option 2: Disable Verification (Development Only)
397
+
398
+ **WARNING: Never use in production!**
399
+
400
+ ```bash
401
+ export F5XC_TLS_INSECURE=true
402
+ ```
403
+
404
+ ### Troubleshooting SSL Errors
405
+
406
+ | Error | Cause | Solution |
407
+ |-------|-------|----------|
408
+ | `Hostname/IP does not match certificate's altnames` | Staging URL mismatch | Use `F5XC_TLS_INSECURE=true` or custom CA |
409
+ | `self signed certificate` | Custom CA not trusted | Set `F5XC_CA_BUNDLE` |
410
+ | `certificate has expired` | Expired certificate | Contact F5 XC admin |
411
+ | `unable to verify the first certificate` | Missing intermediate CA | Add intermediates to CA bundle |
412
+
368
413
  ## Development
369
414
 
370
415
  ### Prerequisites
@@ -4,9 +4,10 @@
4
4
  * Handles authentication configuration and URL normalization.
5
5
  * Supports dual-mode operation:
6
6
  * - Documentation mode: No credentials required
7
- * - Execution mode: API token or P12 certificate authentication
7
+ * - Execution mode: API token or P12/Certificate authentication
8
+ *
9
+ * Cross-compatible with f5xc-xcsh CLI profiles.
8
10
  */
9
- import { ConfigManager } from "../config/index.js";
10
11
  /**
11
12
  * Authentication modes supported by the server
12
13
  */
@@ -20,13 +21,17 @@ export declare enum AuthMode {
20
21
  }
21
22
  /**
22
23
  * Environment variable names for authentication
24
+ * These take priority over profile settings
23
25
  */
24
26
  export declare const AUTH_ENV_VARS: {
25
27
  readonly API_URL: "F5XC_API_URL";
26
28
  readonly API_TOKEN: "F5XC_API_TOKEN";
27
- readonly P12_FILE: "F5XC_P12_FILE";
28
- readonly P12_PASSWORD: "F5XC_P12_PASSWORD";
29
- readonly PROFILE: "F5XC_PROFILE";
29
+ readonly P12_BUNDLE: "F5XC_P12_BUNDLE";
30
+ readonly CERT: "F5XC_CERT";
31
+ readonly KEY: "F5XC_KEY";
32
+ readonly NAMESPACE: "F5XC_NAMESPACE";
33
+ readonly TLS_INSECURE: "F5XC_TLS_INSECURE";
34
+ readonly CA_BUNDLE: "F5XC_CA_BUNDLE";
30
35
  };
31
36
  /**
32
37
  * Credential configuration for API access
@@ -40,8 +45,16 @@ export interface Credentials {
40
45
  token: string | null;
41
46
  /** P12 certificate buffer (for cert auth) */
42
47
  p12Certificate: Buffer | null;
43
- /** P12 certificate password */
44
- p12Password: string | null;
48
+ /** Certificate content (for mTLS) */
49
+ cert: string | null;
50
+ /** Private key content (for mTLS) */
51
+ key: string | null;
52
+ /** Default namespace */
53
+ namespace: string | null;
54
+ /** Disable TLS certificate verification (staging/development only) */
55
+ tlsInsecure: boolean;
56
+ /** Custom CA bundle for TLS verification */
57
+ caBundle: Buffer | null;
45
58
  }
46
59
  /**
47
60
  * Normalize F5XC tenant URL to standard API endpoint format
@@ -67,41 +80,37 @@ export declare function extractTenantFromUrl(url: string): string | null;
67
80
  * Credential Manager
68
81
  *
69
82
  * Manages authentication credentials for F5 Distributed Cloud API.
70
- * Supports dual-layer credential loading with priority:
83
+ * Supports credential loading with priority:
71
84
  * 1. Environment variables (highest priority - overrides all)
72
- * 2. Named profiles from ~/.f5xc/credentials.json
85
+ * 2. Active profile from ~/.config/xcsh/ (cross-compatible with xcsh CLI)
73
86
  * 3. No credentials (documentation mode - lowest priority)
74
87
  */
75
88
  export declare class CredentialManager {
76
89
  private credentials;
77
- private activeProfile;
78
- private configManager;
79
- constructor(configManager?: ConfigManager);
80
- /**
81
- * Load credentials from environment variables
82
- */
83
- private loadFromEnvironment;
90
+ private activeProfileName;
91
+ private initialized;
92
+ constructor();
84
93
  /**
85
- * Load credentials from configuration file
94
+ * Initialize credentials asynchronously
95
+ * Must be called before using credentials
86
96
  */
87
- private loadFromConfigFile;
97
+ initialize(): Promise<void>;
88
98
  /**
89
- * Select which profile to use based on environment or config
99
+ * Load credentials from environment variables
90
100
  */
91
- private selectProfile;
101
+ private loadFromEnvironment;
92
102
  /**
93
- * Merge raw credentials from environment and config
94
- * Environment variables override profile settings
103
+ * Load credentials from active profile
95
104
  */
96
- private mergeCredentials;
105
+ private loadFromProfile;
97
106
  /**
98
- * Build credentials object from raw credentials
107
+ * Build credentials object from profile data
99
108
  */
100
109
  private buildCredentials;
101
110
  /**
102
111
  * Load credentials with priority order:
103
112
  * 1. Environment variables (highest)
104
- * 2. Profile from config file
113
+ * 2. Active profile from ~/.config/xcsh/
105
114
  * 3. No credentials - documentation mode (lowest)
106
115
  */
107
116
  private loadCredentials;
@@ -135,17 +144,34 @@ export declare class CredentialManager {
135
144
  */
136
145
  getP12Certificate(): Buffer | null;
137
146
  /**
138
- * Get P12 certificate password
147
+ * Get certificate content (for mTLS)
148
+ */
149
+ getCert(): string | null;
150
+ /**
151
+ * Get private key content (for mTLS)
152
+ */
153
+ getKey(): string | null;
154
+ /**
155
+ * Get default namespace
156
+ */
157
+ getNamespace(): string | null;
158
+ /**
159
+ * Check if TLS certificate verification is disabled
160
+ * WARNING: Only use for staging/development environments
161
+ */
162
+ getTlsInsecure(): boolean;
163
+ /**
164
+ * Get custom CA bundle for TLS verification
139
165
  */
140
- getP12Password(): string | null;
166
+ getCaBundle(): Buffer | null;
141
167
  /**
142
168
  * Get full credentials object
143
169
  */
144
170
  getCredentials(): Readonly<Credentials>;
145
171
  /**
146
- * Reload credentials from environment
172
+ * Reload credentials from environment/profile
147
173
  * Useful for testing or when credentials change
148
174
  */
149
- reload(): void;
175
+ reload(): Promise<void>;
150
176
  }
151
177
  //# sourceMappingURL=credential-manager.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"credential-manager.d.ts","sourceRoot":"","sources":["../../src/auth/credential-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGnD;;GAEG;AACH,oBAAY,QAAQ;IAClB,kDAAkD;IAClD,IAAI,SAAS;IACb,+BAA+B;IAC/B,KAAK,UAAU;IACf,4CAA4C;IAC5C,WAAW,gBAAgB;CAC5B;AAED;;GAEG;AACH,eAAO,MAAM,aAAa;;;;;;CAMhB,CAAC;AAaX;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,0BAA0B;IAC1B,IAAI,EAAE,QAAQ,CAAC;IACf,yBAAyB;IACzB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,iCAAiC;IACjC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,6CAA6C;IAC7C,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,+BAA+B;IAC/B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAcD;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAsBrD;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAG/D;AAED;;;;;;;;GAQG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,aAAa,CAAuB;IAC5C,OAAO,CAAC,aAAa,CAAgB;gBAEzB,aAAa,CAAC,EAAE,aAAa;IAKzC;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAS3B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAuC1B;;OAEG;IACH,OAAO,CAAC,aAAa;IAWrB;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IASxB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IA+CxB;;;;;OAKG;IACH,OAAO,CAAC,eAAe;IA0CvB;;;OAGG;IACH,gBAAgB,IAAI,MAAM,GAAG,IAAI;IAIjC;;OAEG;IACH,WAAW,IAAI,QAAQ;IAIvB;;OAEG;IACH,eAAe,IAAI,OAAO;IAI1B;;OAEG;IACH,SAAS,IAAI,MAAM,GAAG,IAAI;IAI1B;;OAEG;IACH,SAAS,IAAI,MAAM,GAAG,IAAI;IAI1B;;OAEG;IACH,QAAQ,IAAI,MAAM,GAAG,IAAI;IAIzB;;OAEG;IACH,iBAAiB,IAAI,MAAM,GAAG,IAAI;IAIlC;;OAEG;IACH,cAAc,IAAI,MAAM,GAAG,IAAI;IAI/B;;OAEG;IACH,cAAc,IAAI,QAAQ,CAAC,WAAW,CAAC;IAIvC;;;OAGG;IACH,MAAM,IAAI,IAAI;CAGf"}
1
+ {"version":3,"file":"credential-manager.d.ts","sourceRoot":"","sources":["../../src/auth/credential-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH;;GAEG;AACH,oBAAY,QAAQ;IAClB,kDAAkD;IAClD,IAAI,SAAS;IACb,+BAA+B;IAC/B,KAAK,UAAU;IACf,4CAA4C;IAC5C,WAAW,gBAAgB;CAC5B;AAED;;;GAGG;AACH,eAAO,MAAM,aAAa;;;;;;;;;CAUhB,CAAC;AAEX;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,0BAA0B;IAC1B,IAAI,EAAE,QAAQ,CAAC;IACf,yBAAyB;IACzB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,iCAAiC;IACjC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,6CAA6C;IAC7C,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,qCAAqC;IACrC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,qCAAqC;IACrC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,wBAAwB;IACxB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,sEAAsE;IACtE,WAAW,EAAE,OAAO,CAAC;IACrB,4CAA4C;IAC5C,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAcD;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAsBrD;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAG/D;AAED;;;;;;;;GAQG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,iBAAiB,CAAuB;IAChD,OAAO,CAAC,WAAW,CAAS;;IAiB5B;;;OAGG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAMjC;;OAEG;IACH,OAAO,CAAC,mBAAmB;IA0B3B;;OAEG;YACW,eAAe;IAmB7B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IA+FxB;;;;;OAKG;YACW,eAAe;IA4C7B;;;OAGG;IACH,gBAAgB,IAAI,MAAM,GAAG,IAAI;IAIjC;;OAEG;IACH,WAAW,IAAI,QAAQ;IAIvB;;OAEG;IACH,eAAe,IAAI,OAAO;IAI1B;;OAEG;IACH,SAAS,IAAI,MAAM,GAAG,IAAI;IAI1B;;OAEG;IACH,SAAS,IAAI,MAAM,GAAG,IAAI;IAI1B;;OAEG;IACH,QAAQ,IAAI,MAAM,GAAG,IAAI;IAIzB;;OAEG;IACH,iBAAiB,IAAI,MAAM,GAAG,IAAI;IAIlC;;OAEG;IACH,OAAO,IAAI,MAAM,GAAG,IAAI;IAIxB;;OAEG;IACH,MAAM,IAAI,MAAM,GAAG,IAAI;IAIvB;;OAEG;IACH,YAAY,IAAI,MAAM,GAAG,IAAI;IAI7B;;;OAGG;IACH,cAAc,IAAI,OAAO;IAIzB;;OAEG;IACH,WAAW,IAAI,MAAM,GAAG,IAAI;IAI5B;;OAEG;IACH,cAAc,IAAI,QAAQ,CAAC,WAAW,CAAC;IAIvC;;;OAGG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;CAK9B"}
@@ -4,11 +4,13 @@
4
4
  * Handles authentication configuration and URL normalization.
5
5
  * Supports dual-mode operation:
6
6
  * - Documentation mode: No credentials required
7
- * - Execution mode: API token or P12 certificate authentication
7
+ * - Execution mode: API token or P12/Certificate authentication
8
+ *
9
+ * Cross-compatible with f5xc-xcsh CLI profiles.
8
10
  */
9
11
  import { readFileSync } from "fs";
10
12
  import { logger } from "../utils/logging.js";
11
- import { ConfigManager } from "../config/index.js";
13
+ import { getProfileManager } from "../profile/index.js";
12
14
  /**
13
15
  * Authentication modes supported by the server
14
16
  */
@@ -23,13 +25,18 @@ export var AuthMode;
23
25
  })(AuthMode || (AuthMode = {}));
24
26
  /**
25
27
  * Environment variable names for authentication
28
+ * These take priority over profile settings
26
29
  */
27
30
  export const AUTH_ENV_VARS = {
28
31
  API_URL: "F5XC_API_URL",
29
32
  API_TOKEN: "F5XC_API_TOKEN",
30
- P12_FILE: "F5XC_P12_FILE",
31
- P12_PASSWORD: "F5XC_P12_PASSWORD",
32
- PROFILE: "F5XC_PROFILE",
33
+ P12_BUNDLE: "F5XC_P12_BUNDLE",
34
+ CERT: "F5XC_CERT",
35
+ KEY: "F5XC_KEY",
36
+ NAMESPACE: "F5XC_NAMESPACE",
37
+ // TLS configuration
38
+ TLS_INSECURE: "F5XC_TLS_INSECURE",
39
+ CA_BUNDLE: "F5XC_CA_BUNDLE",
33
40
  };
34
41
  /**
35
42
  * URL normalization patterns
@@ -88,118 +95,132 @@ export function extractTenantFromUrl(url) {
88
95
  * Credential Manager
89
96
  *
90
97
  * Manages authentication credentials for F5 Distributed Cloud API.
91
- * Supports dual-layer credential loading with priority:
98
+ * Supports credential loading with priority:
92
99
  * 1. Environment variables (highest priority - overrides all)
93
- * 2. Named profiles from ~/.f5xc/credentials.json
100
+ * 2. Active profile from ~/.config/xcsh/ (cross-compatible with xcsh CLI)
94
101
  * 3. No credentials (documentation mode - lowest priority)
95
102
  */
96
103
  export class CredentialManager {
97
104
  credentials;
98
- activeProfile = null;
99
- configManager;
100
- constructor(configManager) {
101
- this.configManager = configManager ?? new ConfigManager();
102
- this.credentials = this.loadCredentials();
105
+ activeProfileName = null;
106
+ initialized = false;
107
+ constructor() {
108
+ // Initialize with empty credentials - will be loaded async
109
+ this.credentials = {
110
+ mode: AuthMode.NONE,
111
+ apiUrl: null,
112
+ token: null,
113
+ p12Certificate: null,
114
+ cert: null,
115
+ key: null,
116
+ namespace: null,
117
+ tlsInsecure: false,
118
+ caBundle: null,
119
+ };
120
+ }
121
+ /**
122
+ * Initialize credentials asynchronously
123
+ * Must be called before using credentials
124
+ */
125
+ async initialize() {
126
+ if (this.initialized)
127
+ return;
128
+ this.credentials = await this.loadCredentials();
129
+ this.initialized = true;
103
130
  }
104
131
  /**
105
132
  * Load credentials from environment variables
106
133
  */
107
134
  loadFromEnvironment() {
135
+ const apiUrl = process.env[AUTH_ENV_VARS.API_URL];
136
+ const apiToken = process.env[AUTH_ENV_VARS.API_TOKEN];
137
+ const p12Bundle = process.env[AUTH_ENV_VARS.P12_BUNDLE];
138
+ const cert = process.env[AUTH_ENV_VARS.CERT];
139
+ const key = process.env[AUTH_ENV_VARS.KEY];
140
+ const defaultNamespace = process.env[AUTH_ENV_VARS.NAMESPACE];
141
+ const tlsInsecure = process.env[AUTH_ENV_VARS.TLS_INSECURE]?.toLowerCase() === "true";
142
+ const caBundle = process.env[AUTH_ENV_VARS.CA_BUNDLE];
143
+ const hasAuth = !!(apiToken || p12Bundle || (cert && key));
108
144
  return {
109
- apiUrl: process.env[AUTH_ENV_VARS.API_URL],
110
- token: process.env[AUTH_ENV_VARS.API_TOKEN],
111
- p12File: process.env[AUTH_ENV_VARS.P12_FILE],
112
- p12Password: process.env[AUTH_ENV_VARS.P12_PASSWORD],
145
+ name: "__env__",
146
+ apiUrl: apiUrl || "",
147
+ apiToken,
148
+ p12Bundle,
149
+ cert,
150
+ key,
151
+ defaultNamespace,
152
+ hasAuth,
153
+ tlsInsecure,
154
+ caBundle,
113
155
  };
114
156
  }
115
157
  /**
116
- * Load credentials from configuration file
158
+ * Load credentials from active profile
117
159
  */
118
- loadFromConfigFile() {
160
+ async loadFromProfile() {
119
161
  try {
120
- const config = this.configManager.readSync();
121
- if (!config || Object.keys(config.profiles).length === 0) {
122
- return null;
123
- }
124
- const profileName = this.selectProfile(config);
125
- if (!profileName) {
126
- return null;
127
- }
128
- const profile = config.profiles[profileName];
129
- if (!profile) {
130
- return null;
162
+ const profileManager = getProfileManager();
163
+ const profile = await profileManager.getActiveProfile();
164
+ if (profile) {
165
+ this.activeProfileName = profile.name;
166
+ return profile;
131
167
  }
132
- this.activeProfile = profileName;
133
- // Touch the profile to update lastUsedAt (async, but don't block)
134
- this.configManager.touchProfile(profileName).catch(() => {
135
- // Silently ignore touch errors
136
- });
137
- return {
138
- apiUrl: profile.apiUrl,
139
- token: profile.apiToken,
140
- p12File: profile.p12File,
141
- p12Password: profile.p12Password,
142
- };
168
+ return null;
143
169
  }
144
170
  catch (error) {
145
- logger.debug("Failed to load credentials from config file", {
171
+ logger.debug("Failed to load credentials from profile", {
146
172
  error: error instanceof Error ? error.message : String(error),
147
173
  });
148
174
  return null;
149
175
  }
150
176
  }
151
177
  /**
152
- * Select which profile to use based on environment or config
178
+ * Build credentials object from profile data
153
179
  */
154
- selectProfile(config) {
155
- // Check if F5XC_PROFILE is explicitly set
156
- const envProfile = process.env[AUTH_ENV_VARS.PROFILE];
157
- if (envProfile && config.profiles[envProfile]) {
158
- return envProfile;
159
- }
160
- // Fall back to default profile if set
161
- return config.defaultProfile ?? null;
162
- }
163
- /**
164
- * Merge raw credentials from environment and config
165
- * Environment variables override profile settings
166
- */
167
- mergeCredentials(envCreds, profileCreds) {
168
- return {
169
- apiUrl: envCreds.apiUrl ?? profileCreds.apiUrl,
170
- token: envCreds.token ?? profileCreds.token,
171
- p12File: envCreds.p12File ?? profileCreds.p12File,
172
- p12Password: envCreds.p12Password ?? profileCreds.p12Password,
173
- };
174
- }
175
- /**
176
- * Build credentials object from raw credentials
177
- */
178
- buildCredentials(rawCreds) {
179
- const apiUrl = rawCreds.apiUrl;
180
- const token = rawCreds.token;
181
- const p12File = rawCreds.p12File;
182
- const p12Password = rawCreds.p12Password;
180
+ buildCredentials(profile) {
181
+ const apiUrl = profile.apiUrl;
183
182
  // Determine authentication mode
184
183
  let mode = AuthMode.NONE;
185
184
  let normalizedUrl = null;
186
185
  let p12Certificate = null;
186
+ let cert = null;
187
+ let key = null;
188
+ // TLS configuration
189
+ const tlsInsecure = profile.tlsInsecure ?? false;
190
+ let caBundle = null;
191
+ // Load CA bundle if specified
192
+ if (profile.caBundle) {
193
+ try {
194
+ caBundle = readFileSync(profile.caBundle);
195
+ logger.info("Loaded CA bundle", { file: profile.caBundle });
196
+ }
197
+ catch (error) {
198
+ logger.warn("Failed to load CA bundle", {
199
+ file: profile.caBundle,
200
+ error: error instanceof Error ? error.message : String(error),
201
+ });
202
+ }
203
+ }
204
+ // Log TLS insecure mode warning
205
+ if (tlsInsecure) {
206
+ logger.warn("TLS certificate verification is DISABLED. This is insecure and should only be used for staging/development environments.");
207
+ }
187
208
  if (apiUrl) {
188
209
  normalizedUrl = normalizeApiUrl(apiUrl);
189
- if (p12File) {
190
- // Certificate authentication takes precedence
210
+ if (profile.p12Bundle) {
211
+ // P12 certificate authentication
191
212
  mode = AuthMode.CERTIFICATE;
192
213
  try {
193
- p12Certificate = readFileSync(p12File);
194
- logger.info("Loaded P12 certificate", { file: p12File });
214
+ p12Certificate = readFileSync(profile.p12Bundle);
215
+ logger.info("Loaded P12 certificate", { file: profile.p12Bundle });
195
216
  }
196
217
  catch (error) {
197
218
  logger.error("Failed to load P12 certificate", {
198
- file: p12File,
219
+ file: profile.p12Bundle,
199
220
  error: error instanceof Error ? error.message : String(error),
200
221
  });
201
222
  // Fall back to token auth if certificate load fails
202
- if (token) {
223
+ if (profile.apiToken) {
203
224
  mode = AuthMode.TOKEN;
204
225
  logger.info("Falling back to token authentication");
205
226
  }
@@ -208,48 +229,74 @@ export class CredentialManager {
208
229
  }
209
230
  }
210
231
  }
211
- else if (token) {
232
+ else if (profile.cert && profile.key) {
233
+ // Certificate + key authentication
234
+ mode = AuthMode.CERTIFICATE;
235
+ try {
236
+ cert = readFileSync(profile.cert, "utf-8");
237
+ key = readFileSync(profile.key, "utf-8");
238
+ logger.info("Loaded certificate and key", {
239
+ cert: profile.cert,
240
+ key: profile.key,
241
+ });
242
+ }
243
+ catch (error) {
244
+ logger.error("Failed to load certificate/key", {
245
+ error: error instanceof Error ? error.message : String(error),
246
+ });
247
+ if (profile.apiToken) {
248
+ mode = AuthMode.TOKEN;
249
+ logger.info("Falling back to token authentication");
250
+ }
251
+ else {
252
+ mode = AuthMode.NONE;
253
+ }
254
+ }
255
+ }
256
+ else if (profile.apiToken) {
212
257
  mode = AuthMode.TOKEN;
213
258
  }
214
259
  }
215
260
  return {
216
261
  mode,
217
262
  apiUrl: normalizedUrl,
218
- token: token ?? null,
263
+ token: profile.apiToken ?? null,
219
264
  p12Certificate,
220
- p12Password: p12Password ?? null,
265
+ cert,
266
+ key,
267
+ namespace: profile.defaultNamespace ?? null,
268
+ tlsInsecure,
269
+ caBundle,
221
270
  };
222
271
  }
223
272
  /**
224
273
  * Load credentials with priority order:
225
274
  * 1. Environment variables (highest)
226
- * 2. Profile from config file
275
+ * 2. Active profile from ~/.config/xcsh/
227
276
  * 3. No credentials - documentation mode (lowest)
228
277
  */
229
- loadCredentials() {
230
- // Step 1: Try environment variables first (highest priority)
278
+ async loadCredentials() {
279
+ // Step 1: Check environment variables first (highest priority)
231
280
  const envCreds = this.loadFromEnvironment();
232
- if (envCreds.apiUrl && (envCreds.token || envCreds.p12File)) {
281
+ if (envCreds.apiUrl && envCreds.hasAuth) {
233
282
  const credentials = this.buildCredentials(envCreds);
234
283
  const tenant = credentials.apiUrl ? extractTenantFromUrl(credentials.apiUrl) : null;
235
284
  logger.info("Credentials loaded from environment variables", {
236
285
  mode: credentials.mode,
237
286
  tenant,
238
- profile: this.activeProfile,
239
287
  });
240
288
  return credentials;
241
289
  }
242
- // Step 2: Try config file profile (medium priority)
243
- const profileCreds = this.loadFromConfigFile();
244
- if (profileCreds) {
245
- const merged = this.mergeCredentials(envCreds, profileCreds);
246
- const credentials = this.buildCredentials(merged);
290
+ // Step 2: Try active profile from ~/.config/xcsh/
291
+ const profile = await this.loadFromProfile();
292
+ if (profile) {
293
+ const credentials = this.buildCredentials(profile);
247
294
  if (credentials.mode !== AuthMode.NONE) {
248
295
  const tenant = credentials.apiUrl ? extractTenantFromUrl(credentials.apiUrl) : null;
249
- logger.info("Credentials loaded from config profile", {
296
+ logger.info("Credentials loaded from profile", {
250
297
  mode: credentials.mode,
251
298
  tenant,
252
- profile: this.activeProfile,
299
+ profile: this.activeProfileName,
253
300
  });
254
301
  return credentials;
255
302
  }
@@ -261,7 +308,11 @@ export class CredentialManager {
261
308
  apiUrl: null,
262
309
  token: null,
263
310
  p12Certificate: null,
264
- p12Password: null,
311
+ cert: null,
312
+ key: null,
313
+ namespace: null,
314
+ tlsInsecure: false,
315
+ caBundle: null,
265
316
  };
266
317
  }
267
318
  /**
@@ -269,7 +320,7 @@ export class CredentialManager {
269
320
  * Returns null if credentials are from environment variables or no profile is active
270
321
  */
271
322
  getActiveProfile() {
272
- return this.activeProfile;
323
+ return this.activeProfileName;
273
324
  }
274
325
  /**
275
326
  * Get the current authentication mode
@@ -308,10 +359,35 @@ export class CredentialManager {
308
359
  return this.credentials.p12Certificate;
309
360
  }
310
361
  /**
311
- * Get P12 certificate password
362
+ * Get certificate content (for mTLS)
363
+ */
364
+ getCert() {
365
+ return this.credentials.cert;
366
+ }
367
+ /**
368
+ * Get private key content (for mTLS)
369
+ */
370
+ getKey() {
371
+ return this.credentials.key;
372
+ }
373
+ /**
374
+ * Get default namespace
375
+ */
376
+ getNamespace() {
377
+ return this.credentials.namespace;
378
+ }
379
+ /**
380
+ * Check if TLS certificate verification is disabled
381
+ * WARNING: Only use for staging/development environments
382
+ */
383
+ getTlsInsecure() {
384
+ return this.credentials.tlsInsecure;
385
+ }
386
+ /**
387
+ * Get custom CA bundle for TLS verification
312
388
  */
313
- getP12Password() {
314
- return this.credentials.p12Password;
389
+ getCaBundle() {
390
+ return this.credentials.caBundle;
315
391
  }
316
392
  /**
317
393
  * Get full credentials object
@@ -320,11 +396,13 @@ export class CredentialManager {
320
396
  return Object.freeze({ ...this.credentials });
321
397
  }
322
398
  /**
323
- * Reload credentials from environment
399
+ * Reload credentials from environment/profile
324
400
  * Useful for testing or when credentials change
325
401
  */
326
- reload() {
327
- this.credentials = this.loadCredentials();
402
+ async reload() {
403
+ this.initialized = false;
404
+ this.activeProfileName = null;
405
+ await this.initialize();
328
406
  }
329
407
  }
330
408
  //# sourceMappingURL=credential-manager.js.map