@robinmordasiewicz/f5xc-api-mcp 1.0.82-2512312028 → 1.0.82-2601010145
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/dist/auth/credential-manager.d.ts +40 -29
- package/dist/auth/credential-manager.d.ts.map +1 -1
- package/dist/auth/credential-manager.js +132 -100
- package/dist/auth/credential-manager.js.map +1 -1
- package/dist/auth/http-client.d.ts.map +1 -1
- package/dist/auth/http-client.js +21 -10
- package/dist/auth/http-client.js.map +1 -1
- package/dist/auth/index.d.ts +5 -0
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +5 -0
- package/dist/auth/index.js.map +1 -1
- package/dist/config/paths.d.ts +34 -0
- package/dist/config/paths.d.ts.map +1 -0
- package/dist/config/paths.js +67 -0
- package/dist/config/paths.js.map +1 -0
- package/dist/index.d.ts +12 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +22 -11
- package/dist/index.js.map +1 -1
- package/dist/profile/index.d.ts +9 -0
- package/dist/profile/index.d.ts.map +1 -0
- package/dist/profile/index.js +8 -0
- package/dist/profile/index.js.map +1 -0
- package/dist/profile/manager.d.ts +75 -0
- package/dist/profile/manager.d.ts.map +1 -0
- package/dist/profile/manager.js +327 -0
- package/dist/profile/manager.js.map +1 -0
- package/dist/profile/types.d.ts +51 -0
- package/dist/profile/types.d.ts.map +1 -0
- package/dist/profile/types.js +8 -0
- package/dist/profile/types.js.map +1 -0
- package/dist/server.d.ts +6 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +8 -2
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
- package/dist/config/config-manager.d.ts +0 -72
- package/dist/config/config-manager.d.ts.map +0 -1
- package/dist/config/config-manager.js +0 -247
- package/dist/config/config-manager.js.map +0 -1
- package/dist/config/index.d.ts +0 -7
- package/dist/config/index.d.ts.map +0 -1
- package/dist/config/index.js +0 -7
- package/dist/config/index.js.map +0 -1
- package/dist/config/schema.d.ts +0 -74
- package/dist/config/schema.d.ts.map +0 -1
- package/dist/config/schema.js +0 -75
- package/dist/config/schema.js.map +0 -1
- package/dist/config/types.d.ts +0 -77
- package/dist/config/types.d.ts.map +0 -1
- package/dist/config/types.js +0 -31
- package/dist/config/types.js.map +0 -1
package/dist/auth/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACL,QAAQ,EACR,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,oBAAoB,GACrB,MAAM,yBAAyB,CAAC;AAEjC,YAAY,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAE3D,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEhE,YAAY,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGtE,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExE,YAAY,EACV,OAAO,EACP,aAAa,EACb,aAAa,EACb,sBAAsB,GACvB,MAAM,qBAAqB,CAAC"}
|
package/dist/auth/index.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Auth Module - Export all authentication utilities
|
|
3
|
+
*
|
|
4
|
+
* Cross-compatible with f5xc-xcsh CLI.
|
|
5
|
+
* Profiles are stored in ~/.config/xcsh/ (shared with xcsh CLI).
|
|
3
6
|
*/
|
|
4
7
|
export { AuthMode, AUTH_ENV_VARS, CredentialManager, normalizeApiUrl, extractTenantFromUrl, } from "./credential-manager.js";
|
|
5
8
|
export { HttpClient, createHttpClient } from "./http-client.js";
|
|
9
|
+
// Re-export profile module for cross-compatibility with f5xc-xcsh
|
|
10
|
+
export { ProfileManager, getProfileManager } from "../profile/index.js";
|
|
6
11
|
//# sourceMappingURL=index.js.map
|
package/dist/auth/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACL,QAAQ,EACR,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,oBAAoB,GACrB,MAAM,yBAAyB,CAAC;AAIjC,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAIhE,kEAAkE;AAClE,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* XDG Base Directory compliant paths for F5 XC configuration
|
|
3
|
+
* See: https://specifications.freedesktop.org/basedir/latest/
|
|
4
|
+
*
|
|
5
|
+
* This is the single source of truth for all application paths.
|
|
6
|
+
* All modules should import from here instead of constructing paths directly.
|
|
7
|
+
*
|
|
8
|
+
* Cross-compatible with f5xc-xcsh CLI - both tools share the same config directory.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Get XDG-compliant config directory
|
|
12
|
+
* Config files: settings, profiles, preferences
|
|
13
|
+
* Default: ~/.config/xcsh
|
|
14
|
+
*/
|
|
15
|
+
export declare function getConfigDir(): string;
|
|
16
|
+
/**
|
|
17
|
+
* Get XDG-compliant state directory
|
|
18
|
+
* State files: history, logs, undo history, session state
|
|
19
|
+
* Default: ~/.local/state/xcsh
|
|
20
|
+
*/
|
|
21
|
+
export declare function getStateDir(): string;
|
|
22
|
+
/**
|
|
23
|
+
* Centralized path definitions
|
|
24
|
+
* Use these getters for all file path access throughout the application
|
|
25
|
+
*/
|
|
26
|
+
export declare const paths: {
|
|
27
|
+
readonly configDir: string;
|
|
28
|
+
readonly profilesDir: string;
|
|
29
|
+
readonly activeProfile: string;
|
|
30
|
+
readonly settings: string;
|
|
31
|
+
readonly stateDir: string;
|
|
32
|
+
readonly history: string;
|
|
33
|
+
};
|
|
34
|
+
//# sourceMappingURL=paths.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/config/paths.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAWH;;;;GAIG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAMrC;AAED;;;;GAIG;AACH,wBAAgB,WAAW,IAAI,MAAM,CAMpC;AAED;;;GAGG;AACH,eAAO,MAAM,KAAK;;;;;;;CAsBjB,CAAC"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* XDG Base Directory compliant paths for F5 XC configuration
|
|
3
|
+
* See: https://specifications.freedesktop.org/basedir/latest/
|
|
4
|
+
*
|
|
5
|
+
* This is the single source of truth for all application paths.
|
|
6
|
+
* All modules should import from here instead of constructing paths directly.
|
|
7
|
+
*
|
|
8
|
+
* Cross-compatible with f5xc-xcsh CLI - both tools share the same config directory.
|
|
9
|
+
*/
|
|
10
|
+
import { homedir } from "os";
|
|
11
|
+
import { join } from "path";
|
|
12
|
+
/**
|
|
13
|
+
* Shared application name with f5xc-xcsh CLI for cross-compatibility
|
|
14
|
+
* Both tools will read/write profiles from the same directory
|
|
15
|
+
*/
|
|
16
|
+
const APP_NAME = "xcsh";
|
|
17
|
+
/**
|
|
18
|
+
* Get XDG-compliant config directory
|
|
19
|
+
* Config files: settings, profiles, preferences
|
|
20
|
+
* Default: ~/.config/xcsh
|
|
21
|
+
*/
|
|
22
|
+
export function getConfigDir() {
|
|
23
|
+
const xdgConfig = process.env.XDG_CONFIG_HOME;
|
|
24
|
+
if (xdgConfig) {
|
|
25
|
+
return join(xdgConfig, APP_NAME);
|
|
26
|
+
}
|
|
27
|
+
return join(homedir(), ".config", APP_NAME);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Get XDG-compliant state directory
|
|
31
|
+
* State files: history, logs, undo history, session state
|
|
32
|
+
* Default: ~/.local/state/xcsh
|
|
33
|
+
*/
|
|
34
|
+
export function getStateDir() {
|
|
35
|
+
const xdgState = process.env.XDG_STATE_HOME;
|
|
36
|
+
if (xdgState) {
|
|
37
|
+
return join(xdgState, APP_NAME);
|
|
38
|
+
}
|
|
39
|
+
return join(homedir(), ".local", "state", APP_NAME);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Centralized path definitions
|
|
43
|
+
* Use these getters for all file path access throughout the application
|
|
44
|
+
*/
|
|
45
|
+
export const paths = {
|
|
46
|
+
// Config files (XDG_CONFIG_HOME)
|
|
47
|
+
get configDir() {
|
|
48
|
+
return getConfigDir();
|
|
49
|
+
},
|
|
50
|
+
get profilesDir() {
|
|
51
|
+
return join(getConfigDir(), "profiles");
|
|
52
|
+
},
|
|
53
|
+
get activeProfile() {
|
|
54
|
+
return join(getConfigDir(), "active_profile");
|
|
55
|
+
},
|
|
56
|
+
get settings() {
|
|
57
|
+
return join(getConfigDir(), "config.yaml");
|
|
58
|
+
},
|
|
59
|
+
// State files (XDG_STATE_HOME)
|
|
60
|
+
get stateDir() {
|
|
61
|
+
return getStateDir();
|
|
62
|
+
},
|
|
63
|
+
get history() {
|
|
64
|
+
return join(getStateDir(), "history");
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/config/paths.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAC7B,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B;;;GAGG;AACH,MAAM,QAAQ,GAAG,MAAM,CAAC;AAExB;;;;GAIG;AACH,MAAM,UAAU,YAAY;IAC1B,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;IAC9C,IAAI,SAAS,EAAE,CAAC;QACd,OAAO,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IACnC,CAAC;IACD,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;AAC9C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW;IACzB,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;IAC5C,IAAI,QAAQ,EAAE,CAAC;QACb,OAAO,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAClC,CAAC;IACD,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;AACtD,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG;IACnB,iCAAiC;IACjC,IAAI,SAAS;QACX,OAAO,YAAY,EAAE,CAAC;IACxB,CAAC;IACD,IAAI,WAAW;QACb,OAAO,IAAI,CAAC,YAAY,EAAE,EAAE,UAAU,CAAC,CAAC;IAC1C,CAAC;IACD,IAAI,aAAa;QACf,OAAO,IAAI,CAAC,YAAY,EAAE,EAAE,gBAAgB,CAAC,CAAC;IAChD,CAAC;IACD,IAAI,QAAQ;QACV,OAAO,IAAI,CAAC,YAAY,EAAE,EAAE,aAAa,CAAC,CAAC;IAC7C,CAAC;IAED,+BAA+B;IAC/B,IAAI,QAAQ;QACV,OAAO,WAAW,EAAE,CAAC;IACvB,CAAC;IACD,IAAI,OAAO;QACT,OAAO,IAAI,CAAC,WAAW,EAAE,EAAE,SAAS,CAAC,CAAC;IACxC,CAAC;CACF,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -10,12 +10,18 @@
|
|
|
10
10
|
* and Terraform examples
|
|
11
11
|
* - Execution mode: When F5XC credentials are provided, enables direct API calls
|
|
12
12
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
13
|
+
* Credential Sources (in priority order):
|
|
14
|
+
* 1. Environment Variables (highest priority):
|
|
15
|
+
* - F5XC_API_URL: Tenant URL (auto-normalized)
|
|
16
|
+
* - F5XC_API_TOKEN: API token for authentication
|
|
17
|
+
* - F5XC_P12_BUNDLE: Path to P12 certificate bundle
|
|
18
|
+
* - F5XC_CERT: Path to certificate file (for mTLS)
|
|
19
|
+
* - F5XC_KEY: Path to private key file (for mTLS)
|
|
20
|
+
* - F5XC_NAMESPACE: Default namespace
|
|
21
|
+
*
|
|
22
|
+
* 2. Profile from ~/.config/xcsh/ (cross-compatible with f5xc-xcsh CLI):
|
|
23
|
+
* - Uses active profile from ~/.config/xcsh/active_profile
|
|
24
|
+
* - Individual profiles stored in ~/.config/xcsh/profiles/
|
|
19
25
|
*/
|
|
20
26
|
/** Server version - synchronized with package.json */
|
|
21
27
|
export declare const VERSION = "0.1.0";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAKH,sDAAsD;AACtD,eAAO,MAAM,OAAO,UAAU,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -10,12 +10,18 @@
|
|
|
10
10
|
* and Terraform examples
|
|
11
11
|
* - Execution mode: When F5XC credentials are provided, enables direct API calls
|
|
12
12
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
13
|
+
* Credential Sources (in priority order):
|
|
14
|
+
* 1. Environment Variables (highest priority):
|
|
15
|
+
* - F5XC_API_URL: Tenant URL (auto-normalized)
|
|
16
|
+
* - F5XC_API_TOKEN: API token for authentication
|
|
17
|
+
* - F5XC_P12_BUNDLE: Path to P12 certificate bundle
|
|
18
|
+
* - F5XC_CERT: Path to certificate file (for mTLS)
|
|
19
|
+
* - F5XC_KEY: Path to private key file (for mTLS)
|
|
20
|
+
* - F5XC_NAMESPACE: Default namespace
|
|
21
|
+
*
|
|
22
|
+
* 2. Profile from ~/.config/xcsh/ (cross-compatible with f5xc-xcsh CLI):
|
|
23
|
+
* - Uses active profile from ~/.config/xcsh/active_profile
|
|
24
|
+
* - Individual profiles stored in ~/.config/xcsh/profiles/
|
|
19
25
|
*/
|
|
20
26
|
import { createServer } from "./server.js";
|
|
21
27
|
import { logger } from "./utils/logging.js";
|
|
@@ -43,12 +49,17 @@ Options:
|
|
|
43
49
|
-v, --version Show version number
|
|
44
50
|
-h, --help Show help
|
|
45
51
|
|
|
46
|
-
Environment Variables:
|
|
52
|
+
Environment Variables (override profile settings):
|
|
47
53
|
F5XC_API_URL Tenant URL (e.g., https://tenant.console.ves.volterra.io)
|
|
48
54
|
F5XC_API_TOKEN API token for authentication
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
55
|
+
F5XC_P12_BUNDLE Path to P12 certificate bundle
|
|
56
|
+
F5XC_CERT Path to certificate file (for mTLS)
|
|
57
|
+
F5XC_KEY Path to private key file (for mTLS)
|
|
58
|
+
F5XC_NAMESPACE Default namespace
|
|
59
|
+
|
|
60
|
+
Profile Configuration (cross-compatible with f5xc-xcsh CLI):
|
|
61
|
+
Profiles are stored in ~/.config/xcsh/profiles/
|
|
62
|
+
Active profile is tracked in ~/.config/xcsh/active_profile
|
|
52
63
|
|
|
53
64
|
The server runs in documentation mode when no credentials are provided,
|
|
54
65
|
allowing exploration of the API without authentication.
|
|
@@ -56,7 +67,7 @@ allowing exploration of the API without authentication.
|
|
|
56
67
|
process.exit(0);
|
|
57
68
|
}
|
|
58
69
|
// Start MCP server (default behavior)
|
|
59
|
-
const server = createServer();
|
|
70
|
+
const server = await createServer();
|
|
60
71
|
await server.start();
|
|
61
72
|
// Handle graceful shutdown
|
|
62
73
|
const shutdown = async (signal) => {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Profile Module - XDG-compliant profile management
|
|
3
|
+
*
|
|
4
|
+
* Cross-compatible with f5xc-xcsh CLI.
|
|
5
|
+
* Exports profile types and the ProfileManager singleton.
|
|
6
|
+
*/
|
|
7
|
+
export type { Profile, ProfileConfig, ProfileResult, ProfileValidationError } from "./types.js";
|
|
8
|
+
export { ProfileManager, getProfileManager } from "./manager.js";
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/profile/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,YAAY,EAAE,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAEhG,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Profile Module - XDG-compliant profile management
|
|
3
|
+
*
|
|
4
|
+
* Cross-compatible with f5xc-xcsh CLI.
|
|
5
|
+
* Exports profile types and the ProfileManager singleton.
|
|
6
|
+
*/
|
|
7
|
+
export { ProfileManager, getProfileManager } from "./manager.js";
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/profile/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ProfileManager - XDG-compliant profile storage and management
|
|
3
|
+
*
|
|
4
|
+
* Cross-compatible with f5xc-xcsh CLI.
|
|
5
|
+
* Profiles are stored in ~/.config/xcsh/profiles/ (XDG Base Directory compliant)
|
|
6
|
+
*/
|
|
7
|
+
import type { Profile, ProfileResult } from "./types.js";
|
|
8
|
+
/**
|
|
9
|
+
* ProfileManager handles profile CRUD operations with secure file storage
|
|
10
|
+
*/
|
|
11
|
+
export declare class ProfileManager {
|
|
12
|
+
private config;
|
|
13
|
+
constructor();
|
|
14
|
+
/**
|
|
15
|
+
* Ensure config directories exist
|
|
16
|
+
*/
|
|
17
|
+
ensureDirectories(): Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* Get path to profile JSON file
|
|
20
|
+
*/
|
|
21
|
+
private getProfilePath;
|
|
22
|
+
/**
|
|
23
|
+
* Validate profile name (alphanumeric, dash, underscore only)
|
|
24
|
+
*/
|
|
25
|
+
private isValidName;
|
|
26
|
+
/**
|
|
27
|
+
* Validate API URL format
|
|
28
|
+
*/
|
|
29
|
+
private isValidUrl;
|
|
30
|
+
/**
|
|
31
|
+
* List all saved profiles
|
|
32
|
+
*/
|
|
33
|
+
list(): Promise<Profile[]>;
|
|
34
|
+
/**
|
|
35
|
+
* Get a profile by name
|
|
36
|
+
*/
|
|
37
|
+
get(name: string): Promise<Profile | null>;
|
|
38
|
+
/**
|
|
39
|
+
* Save a profile (create or update)
|
|
40
|
+
*/
|
|
41
|
+
save(profile: Profile): Promise<ProfileResult>;
|
|
42
|
+
/**
|
|
43
|
+
* Delete a profile by name
|
|
44
|
+
*/
|
|
45
|
+
delete(name: string): Promise<ProfileResult>;
|
|
46
|
+
/**
|
|
47
|
+
* Get the name of the active profile
|
|
48
|
+
*/
|
|
49
|
+
getActive(): Promise<string | null>;
|
|
50
|
+
/**
|
|
51
|
+
* Clear the active profile (used for force delete)
|
|
52
|
+
*/
|
|
53
|
+
clearActive(): Promise<void>;
|
|
54
|
+
/**
|
|
55
|
+
* Set the active profile
|
|
56
|
+
*/
|
|
57
|
+
setActive(name: string): Promise<ProfileResult>;
|
|
58
|
+
/**
|
|
59
|
+
* Get the active profile (full profile data)
|
|
60
|
+
*/
|
|
61
|
+
getActiveProfile(): Promise<Profile | null>;
|
|
62
|
+
/**
|
|
63
|
+
* Check if a profile exists
|
|
64
|
+
*/
|
|
65
|
+
exists(name: string): Promise<boolean>;
|
|
66
|
+
/**
|
|
67
|
+
* Mask sensitive fields for display
|
|
68
|
+
*/
|
|
69
|
+
maskProfile(profile: Profile): Record<string, string>;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Get the global ProfileManager instance
|
|
73
|
+
*/
|
|
74
|
+
export declare function getProfileManager(): ProfileManager;
|
|
75
|
+
//# sourceMappingURL=manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/profile/manager.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,KAAK,EAAE,OAAO,EAAiB,aAAa,EAAE,MAAM,YAAY,CAAC;AAoBxE;;GAEG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAgB;;IAU9B;;OAEG;IACG,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC;IAOxC;;OAEG;IACH,OAAO,CAAC,cAAc;IAItB;;OAEG;IACH,OAAO,CAAC,WAAW;IAInB;;OAEG;IACH,OAAO,CAAC,UAAU;IASlB;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;IAuChC;;OAEG;IACG,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IA8BhD;;OAEG;IACG,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,aAAa,CAAC;IA+CpD;;OAEG;IACG,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAqClD;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IASzC;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IAQlC;;OAEG;IACG,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IA8BrD;;OAEG;IACG,gBAAgB,IAAI,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IAQjD;;OAEG;IACG,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAK5C;;OAEG;IACH,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;CA8BtD;AAKD;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,cAAc,CAKlD"}
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ProfileManager - XDG-compliant profile storage and management
|
|
3
|
+
*
|
|
4
|
+
* Cross-compatible with f5xc-xcsh CLI.
|
|
5
|
+
* Profiles are stored in ~/.config/xcsh/profiles/ (XDG Base Directory compliant)
|
|
6
|
+
*/
|
|
7
|
+
import { promises as fs } from "fs";
|
|
8
|
+
import { join } from "path";
|
|
9
|
+
import YAML from "yaml";
|
|
10
|
+
import { paths } from "../config/paths.js";
|
|
11
|
+
/**
|
|
12
|
+
* Convert snake_case to camelCase
|
|
13
|
+
*/
|
|
14
|
+
function snakeToCamel(str) {
|
|
15
|
+
return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Convert object keys from snake_case to camelCase
|
|
19
|
+
*/
|
|
20
|
+
function convertKeysToCamelCase(obj) {
|
|
21
|
+
const result = {};
|
|
22
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
23
|
+
result[snakeToCamel(key)] = value;
|
|
24
|
+
}
|
|
25
|
+
return result;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* ProfileManager handles profile CRUD operations with secure file storage
|
|
29
|
+
*/
|
|
30
|
+
export class ProfileManager {
|
|
31
|
+
config;
|
|
32
|
+
constructor() {
|
|
33
|
+
this.config = {
|
|
34
|
+
configDir: paths.configDir,
|
|
35
|
+
profilesDir: paths.profilesDir,
|
|
36
|
+
activeProfileFile: paths.activeProfile,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Ensure config directories exist
|
|
41
|
+
*/
|
|
42
|
+
async ensureDirectories() {
|
|
43
|
+
await fs.mkdir(this.config.profilesDir, {
|
|
44
|
+
recursive: true,
|
|
45
|
+
mode: 0o700,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Get path to profile JSON file
|
|
50
|
+
*/
|
|
51
|
+
getProfilePath(name) {
|
|
52
|
+
return join(this.config.profilesDir, `${name}.json`);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Validate profile name (alphanumeric, dash, underscore only)
|
|
56
|
+
*/
|
|
57
|
+
isValidName(name) {
|
|
58
|
+
return /^[a-zA-Z0-9_-]+$/.test(name) && name.length > 0 && name.length <= 64;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Validate API URL format
|
|
62
|
+
*/
|
|
63
|
+
isValidUrl(url) {
|
|
64
|
+
try {
|
|
65
|
+
const parsed = new URL(url);
|
|
66
|
+
return parsed.protocol === "https:" || parsed.protocol === "http:";
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* List all saved profiles
|
|
74
|
+
*/
|
|
75
|
+
async list() {
|
|
76
|
+
await this.ensureDirectories();
|
|
77
|
+
try {
|
|
78
|
+
const files = await fs.readdir(this.config.profilesDir);
|
|
79
|
+
const profileNames = new Set();
|
|
80
|
+
// Collect unique profile names from filenames
|
|
81
|
+
for (const file of files) {
|
|
82
|
+
// Support both .json and .yaml/.yml extensions
|
|
83
|
+
let name = null;
|
|
84
|
+
if (file.endsWith(".json")) {
|
|
85
|
+
name = file.slice(0, -5);
|
|
86
|
+
}
|
|
87
|
+
else if (file.endsWith(".yaml")) {
|
|
88
|
+
name = file.slice(0, -5);
|
|
89
|
+
}
|
|
90
|
+
else if (file.endsWith(".yml")) {
|
|
91
|
+
name = file.slice(0, -4);
|
|
92
|
+
}
|
|
93
|
+
if (name) {
|
|
94
|
+
profileNames.add(name);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// Load profiles (get() handles file extension priority)
|
|
98
|
+
const profiles = [];
|
|
99
|
+
for (const name of profileNames) {
|
|
100
|
+
const profile = await this.get(name);
|
|
101
|
+
if (profile) {
|
|
102
|
+
profiles.push(profile);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return profiles.sort((a, b) => a.name.localeCompare(b.name));
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
return [];
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Get a profile by name
|
|
113
|
+
*/
|
|
114
|
+
async get(name) {
|
|
115
|
+
await this.ensureDirectories();
|
|
116
|
+
// Try different file extensions in order of preference
|
|
117
|
+
const extensions = [".json", ".yaml", ".yml"];
|
|
118
|
+
for (const ext of extensions) {
|
|
119
|
+
const path = join(this.config.profilesDir, `${name}${ext}`);
|
|
120
|
+
try {
|
|
121
|
+
const data = await fs.readFile(path, "utf-8");
|
|
122
|
+
let parsed;
|
|
123
|
+
if (ext === ".json") {
|
|
124
|
+
parsed = JSON.parse(data);
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
// Parse YAML and convert snake_case to camelCase
|
|
128
|
+
parsed = YAML.parse(data);
|
|
129
|
+
parsed = convertKeysToCamelCase(parsed);
|
|
130
|
+
}
|
|
131
|
+
return parsed;
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
// Try next extension
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Save a profile (create or update)
|
|
142
|
+
*/
|
|
143
|
+
async save(profile) {
|
|
144
|
+
await this.ensureDirectories();
|
|
145
|
+
if (!this.isValidName(profile.name)) {
|
|
146
|
+
return {
|
|
147
|
+
success: false,
|
|
148
|
+
message: "Invalid profile name. Use alphanumeric characters, dashes, and underscores only (max 64 chars).",
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
if (!this.isValidUrl(profile.apiUrl)) {
|
|
152
|
+
return {
|
|
153
|
+
success: false,
|
|
154
|
+
message: "Invalid API URL. Must be a valid HTTP/HTTPS URL.",
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
// Require at least one form of authentication
|
|
158
|
+
if (!profile.apiToken && !profile.cert && !profile.p12Bundle) {
|
|
159
|
+
return {
|
|
160
|
+
success: false,
|
|
161
|
+
message: "Profile must have at least one authentication method (token, certificate, or P12 bundle).",
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
try {
|
|
165
|
+
const path = this.getProfilePath(profile.name);
|
|
166
|
+
const data = JSON.stringify(profile, null, 2);
|
|
167
|
+
// Write with secure permissions (owner read/write only)
|
|
168
|
+
await fs.writeFile(path, data, { mode: 0o600 });
|
|
169
|
+
return {
|
|
170
|
+
success: true,
|
|
171
|
+
message: `Profile '${profile.name}' saved successfully.`,
|
|
172
|
+
profile,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
return {
|
|
177
|
+
success: false,
|
|
178
|
+
message: `Failed to save profile: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Delete a profile by name
|
|
184
|
+
*/
|
|
185
|
+
async delete(name) {
|
|
186
|
+
await this.ensureDirectories();
|
|
187
|
+
// Check if profile exists
|
|
188
|
+
const existing = await this.get(name);
|
|
189
|
+
if (!existing) {
|
|
190
|
+
return {
|
|
191
|
+
success: false,
|
|
192
|
+
message: `Profile '${name}' not found.`,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
// Check if it's the active profile
|
|
196
|
+
const active = await this.getActive();
|
|
197
|
+
if (active === name) {
|
|
198
|
+
return {
|
|
199
|
+
success: false,
|
|
200
|
+
message: `Cannot delete active profile '${name}'. Switch to another profile first.`,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
try {
|
|
204
|
+
const path = this.getProfilePath(name);
|
|
205
|
+
await fs.unlink(path);
|
|
206
|
+
return {
|
|
207
|
+
success: true,
|
|
208
|
+
message: `Profile '${name}' deleted successfully.`,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
catch (error) {
|
|
212
|
+
return {
|
|
213
|
+
success: false,
|
|
214
|
+
message: `Failed to delete profile: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Get the name of the active profile
|
|
220
|
+
*/
|
|
221
|
+
async getActive() {
|
|
222
|
+
try {
|
|
223
|
+
const name = await fs.readFile(this.config.activeProfileFile, "utf-8");
|
|
224
|
+
return name.trim() || null;
|
|
225
|
+
}
|
|
226
|
+
catch {
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Clear the active profile (used for force delete)
|
|
232
|
+
*/
|
|
233
|
+
async clearActive() {
|
|
234
|
+
try {
|
|
235
|
+
await fs.unlink(this.config.activeProfileFile);
|
|
236
|
+
}
|
|
237
|
+
catch {
|
|
238
|
+
// Ignore if file doesn't exist
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Set the active profile
|
|
243
|
+
*/
|
|
244
|
+
async setActive(name) {
|
|
245
|
+
await this.ensureDirectories();
|
|
246
|
+
// Verify profile exists
|
|
247
|
+
const profile = await this.get(name);
|
|
248
|
+
if (!profile) {
|
|
249
|
+
return {
|
|
250
|
+
success: false,
|
|
251
|
+
message: `Profile '${name}' not found.`,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
try {
|
|
255
|
+
await fs.writeFile(this.config.activeProfileFile, name, {
|
|
256
|
+
mode: 0o600,
|
|
257
|
+
});
|
|
258
|
+
return {
|
|
259
|
+
success: true,
|
|
260
|
+
message: `Switched to profile '${name}'.`,
|
|
261
|
+
profile,
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
catch (error) {
|
|
265
|
+
return {
|
|
266
|
+
success: false,
|
|
267
|
+
message: `Failed to set active profile: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Get the active profile (full profile data)
|
|
273
|
+
*/
|
|
274
|
+
async getActiveProfile() {
|
|
275
|
+
const name = await this.getActive();
|
|
276
|
+
if (!name) {
|
|
277
|
+
return null;
|
|
278
|
+
}
|
|
279
|
+
return this.get(name);
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Check if a profile exists
|
|
283
|
+
*/
|
|
284
|
+
async exists(name) {
|
|
285
|
+
const profile = await this.get(name);
|
|
286
|
+
return profile !== null;
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Mask sensitive fields for display
|
|
290
|
+
*/
|
|
291
|
+
maskProfile(profile) {
|
|
292
|
+
const masked = {
|
|
293
|
+
name: profile.name,
|
|
294
|
+
apiUrl: profile.apiUrl,
|
|
295
|
+
};
|
|
296
|
+
if (profile.apiToken) {
|
|
297
|
+
// Show only last 4 characters
|
|
298
|
+
const len = profile.apiToken.length;
|
|
299
|
+
masked.apiToken = len > 4 ? `****${profile.apiToken.slice(-4)}` : "****";
|
|
300
|
+
}
|
|
301
|
+
if (profile.p12Bundle) {
|
|
302
|
+
masked.p12Bundle = "[configured]";
|
|
303
|
+
}
|
|
304
|
+
if (profile.cert) {
|
|
305
|
+
masked.cert = "[configured]";
|
|
306
|
+
}
|
|
307
|
+
if (profile.key) {
|
|
308
|
+
masked.key = "[configured]";
|
|
309
|
+
}
|
|
310
|
+
if (profile.defaultNamespace) {
|
|
311
|
+
masked.defaultNamespace = profile.defaultNamespace;
|
|
312
|
+
}
|
|
313
|
+
return masked;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
// Singleton instance
|
|
317
|
+
let managerInstance = null;
|
|
318
|
+
/**
|
|
319
|
+
* Get the global ProfileManager instance
|
|
320
|
+
*/
|
|
321
|
+
export function getProfileManager() {
|
|
322
|
+
if (!managerInstance) {
|
|
323
|
+
managerInstance = new ProfileManager();
|
|
324
|
+
}
|
|
325
|
+
return managerInstance;
|
|
326
|
+
}
|
|
327
|
+
//# sourceMappingURL=manager.js.map
|