@robinmordasiewicz/f5xc-api-mcp 3.0.0 → 3.0.2
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 +108 -0
- package/dist/auth/credential-manager.d.ts +38 -3
- package/dist/auth/credential-manager.d.ts.map +1 -1
- package/dist/auth/credential-manager.js +137 -15
- package/dist/auth/credential-manager.js.map +1 -1
- package/dist/cli/index.d.ts +12 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +110 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/setup.d.ts +33 -0
- package/dist/cli/setup.d.ts.map +1 -0
- package/dist/cli/setup.js +347 -0
- package/dist/cli/setup.js.map +1 -0
- package/dist/config/config-manager.d.ts +72 -0
- package/dist/config/config-manager.d.ts.map +1 -0
- package/dist/config/config-manager.js +240 -0
- package/dist/config/config-manager.js.map +1 -0
- package/dist/config/index.d.ts +7 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +7 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/schema.d.ts +74 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +75 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/config/types.d.ts +77 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +31 -0
- package/dist/config/types.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +36 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interactive CLI setup wizard for F5XC API MCP credentials
|
|
3
|
+
*
|
|
4
|
+
* Features:
|
|
5
|
+
* - Auto-detection of existing environment variables
|
|
6
|
+
* - Interactive profile creation with validation
|
|
7
|
+
* - Profile management (list, delete, set default)
|
|
8
|
+
*/
|
|
9
|
+
import * as readline from "readline";
|
|
10
|
+
import { ConfigManager } from "../config/index.js";
|
|
11
|
+
import { AUTH_ENV_VARS } from "../auth/credential-manager.js";
|
|
12
|
+
import { logger } from "../utils/logging.js";
|
|
13
|
+
/**
|
|
14
|
+
* Detect existing environment variables
|
|
15
|
+
*/
|
|
16
|
+
function detectEnvironmentCredentials() {
|
|
17
|
+
const apiUrl = process.env[AUTH_ENV_VARS.API_URL];
|
|
18
|
+
const apiToken = process.env[AUTH_ENV_VARS.API_TOKEN];
|
|
19
|
+
const p12File = process.env[AUTH_ENV_VARS.P12_FILE];
|
|
20
|
+
const p12Password = process.env[AUTH_ENV_VARS.P12_PASSWORD];
|
|
21
|
+
return {
|
|
22
|
+
hasCredentials: Boolean(apiUrl && (apiToken || p12File)),
|
|
23
|
+
apiUrl,
|
|
24
|
+
apiToken,
|
|
25
|
+
p12File,
|
|
26
|
+
p12Password,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Create a readline interface for user input
|
|
31
|
+
*/
|
|
32
|
+
function createReadlineInterface() {
|
|
33
|
+
return readline.createInterface({
|
|
34
|
+
input: process.stdin,
|
|
35
|
+
output: process.stdout,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Prompt user with a question
|
|
40
|
+
*/
|
|
41
|
+
async function prompt(rl, question) {
|
|
42
|
+
return new Promise((resolve) => {
|
|
43
|
+
rl.question(question, (answer) => {
|
|
44
|
+
resolve(answer.trim());
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Validate profile name format
|
|
50
|
+
*/
|
|
51
|
+
function validateProfileName(name) {
|
|
52
|
+
if (!name) {
|
|
53
|
+
return { valid: false, error: "Profile name cannot be empty" };
|
|
54
|
+
}
|
|
55
|
+
if (!/^[a-z0-9_-]+$/i.test(name)) {
|
|
56
|
+
return {
|
|
57
|
+
valid: false,
|
|
58
|
+
error: "Profile name must contain only alphanumeric characters, hyphens, and underscores",
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
return { valid: true };
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Create a profile from detected environment variables
|
|
65
|
+
*/
|
|
66
|
+
async function createProfileFromEnv(rl, configManager, envCreds) {
|
|
67
|
+
console.log();
|
|
68
|
+
// Get profile name
|
|
69
|
+
let profileName = "";
|
|
70
|
+
while (!profileName) {
|
|
71
|
+
const answer = await prompt(rl, "Profile name [production]: ");
|
|
72
|
+
profileName = answer || "production";
|
|
73
|
+
const validation = validateProfileName(profileName);
|
|
74
|
+
if (!validation.valid) {
|
|
75
|
+
console.log(`❌ ${validation.error}`);
|
|
76
|
+
profileName = "";
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// Get optional description
|
|
80
|
+
const description = await prompt(rl, "Description [Environment credentials]: ");
|
|
81
|
+
// Create profile
|
|
82
|
+
const profile = {
|
|
83
|
+
apiUrl: envCreds.apiUrl,
|
|
84
|
+
apiToken: envCreds.apiToken,
|
|
85
|
+
p12File: envCreds.p12File,
|
|
86
|
+
p12Password: envCreds.p12Password,
|
|
87
|
+
metadata: {
|
|
88
|
+
description: description || "Environment credentials",
|
|
89
|
+
createdAt: new Date().toISOString(),
|
|
90
|
+
lastModifiedAt: new Date().toISOString(),
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
await configManager.setProfile(profileName, profile);
|
|
94
|
+
console.log(`\n✅ Profile "${profileName}" created from environment variables!\n`);
|
|
95
|
+
// Ask if user wants to set as default
|
|
96
|
+
const setDefault = await prompt(rl, "Set as default profile? (Y/n): ");
|
|
97
|
+
if (setDefault.toLowerCase() !== "n") {
|
|
98
|
+
await configManager.setDefaultProfile(profileName);
|
|
99
|
+
console.log(`✅ "${profileName}" set as default profile\n`);
|
|
100
|
+
}
|
|
101
|
+
console.log("Next steps:");
|
|
102
|
+
console.log(" • Run 'f5xc-api-mcp' to start with this profile");
|
|
103
|
+
console.log(" • Or use 'F5XC_PROFILE=staging f5xc-api-mcp' for other profiles");
|
|
104
|
+
console.log(" • Environment variables still override profile settings\n");
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Create a profile manually
|
|
108
|
+
*/
|
|
109
|
+
async function addProfile(rl, configManager) {
|
|
110
|
+
console.log("\nLet's create a new profile.\n");
|
|
111
|
+
// Get profile name
|
|
112
|
+
let profileName = "";
|
|
113
|
+
while (!profileName) {
|
|
114
|
+
const answer = await prompt(rl, "Profile name: ");
|
|
115
|
+
profileName = answer;
|
|
116
|
+
if (!profileName) {
|
|
117
|
+
console.log("❌ Profile name cannot be empty");
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
const validation = validateProfileName(profileName);
|
|
121
|
+
if (!validation.valid) {
|
|
122
|
+
console.log(`❌ ${validation.error}`);
|
|
123
|
+
profileName = "";
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
// Get description
|
|
128
|
+
const description = await prompt(rl, "Description (optional): ");
|
|
129
|
+
// Get API URL
|
|
130
|
+
let apiUrl = "";
|
|
131
|
+
while (!apiUrl) {
|
|
132
|
+
const answer = await prompt(rl, "F5XC API URL: ");
|
|
133
|
+
apiUrl = answer;
|
|
134
|
+
if (!apiUrl) {
|
|
135
|
+
console.log("❌ API URL cannot be empty");
|
|
136
|
+
}
|
|
137
|
+
else if (!apiUrl.startsWith("http://") && !apiUrl.startsWith("https://")) {
|
|
138
|
+
console.log("❌ API URL must start with http:// or https://");
|
|
139
|
+
apiUrl = "";
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
// Get authentication method
|
|
143
|
+
console.log("\nAuthentication method:");
|
|
144
|
+
console.log(" 1) API Token");
|
|
145
|
+
console.log(" 2) P12 Certificate");
|
|
146
|
+
let authMethod = "";
|
|
147
|
+
while (authMethod !== "1" && authMethod !== "2") {
|
|
148
|
+
authMethod = await prompt(rl, "Select (1-2) [1]: ");
|
|
149
|
+
authMethod = authMethod || "1";
|
|
150
|
+
if (authMethod !== "1" && authMethod !== "2") {
|
|
151
|
+
console.log("❌ Please select 1 or 2");
|
|
152
|
+
authMethod = "";
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
let apiToken;
|
|
156
|
+
let p12File;
|
|
157
|
+
let p12Password;
|
|
158
|
+
if (authMethod === "1") {
|
|
159
|
+
// Token authentication
|
|
160
|
+
apiToken = await prompt(rl, "API Token: ");
|
|
161
|
+
if (!apiToken) {
|
|
162
|
+
console.log("❌ API Token cannot be empty");
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
// Certificate authentication
|
|
168
|
+
p12File = await prompt(rl, "Path to P12 certificate file: ");
|
|
169
|
+
if (!p12File) {
|
|
170
|
+
console.log("❌ P12 file path cannot be empty");
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
p12Password = await prompt(rl, "P12 certificate password (optional): ");
|
|
174
|
+
}
|
|
175
|
+
// Create profile
|
|
176
|
+
const profile = {
|
|
177
|
+
apiUrl,
|
|
178
|
+
apiToken,
|
|
179
|
+
p12File,
|
|
180
|
+
p12Password: p12Password || undefined,
|
|
181
|
+
metadata: {
|
|
182
|
+
description: description || undefined,
|
|
183
|
+
createdAt: new Date().toISOString(),
|
|
184
|
+
lastModifiedAt: new Date().toISOString(),
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
await configManager.setProfile(profileName, profile);
|
|
188
|
+
console.log(`\n✅ Profile "${profileName}" created successfully!\n`);
|
|
189
|
+
// Ask if user wants to set as default
|
|
190
|
+
const setDefault = await prompt(rl, "Set as default profile? (Y/n): ");
|
|
191
|
+
if (setDefault.toLowerCase() !== "n") {
|
|
192
|
+
await configManager.setDefaultProfile(profileName);
|
|
193
|
+
console.log(`✅ "${profileName}" set as default profile\n`);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Run the interactive setup wizard
|
|
198
|
+
*/
|
|
199
|
+
export async function runSetupWizard() {
|
|
200
|
+
const rl = createReadlineInterface();
|
|
201
|
+
const configManager = new ConfigManager();
|
|
202
|
+
try {
|
|
203
|
+
console.log("\n🔧 F5XC API MCP Server - Credential Setup\n");
|
|
204
|
+
// Check for existing environment variables
|
|
205
|
+
const envCreds = detectEnvironmentCredentials();
|
|
206
|
+
if (envCreds.hasCredentials) {
|
|
207
|
+
console.log("✅ Detected existing environment variables:");
|
|
208
|
+
if (envCreds.apiUrl) {
|
|
209
|
+
console.log(` F5XC_API_URL: ${envCreds.apiUrl}`);
|
|
210
|
+
}
|
|
211
|
+
if (envCreds.apiToken) {
|
|
212
|
+
console.log(` F5XC_API_TOKEN: ***${envCreds.apiToken.slice(-4)}`);
|
|
213
|
+
}
|
|
214
|
+
if (envCreds.p12File) {
|
|
215
|
+
console.log(` F5XC_P12_FILE: ${envCreds.p12File}`);
|
|
216
|
+
}
|
|
217
|
+
console.log();
|
|
218
|
+
const useEnv = await prompt(rl, "Create profile from environment variables? (Y/n): ");
|
|
219
|
+
if (useEnv.toLowerCase() !== "n") {
|
|
220
|
+
await createProfileFromEnv(rl, configManager, envCreds);
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
// Manual profile creation
|
|
225
|
+
await addProfile(rl, configManager);
|
|
226
|
+
}
|
|
227
|
+
catch (error) {
|
|
228
|
+
logger.error("Setup wizard error", {
|
|
229
|
+
error: error instanceof Error ? error.message : String(error),
|
|
230
|
+
});
|
|
231
|
+
process.exit(1);
|
|
232
|
+
}
|
|
233
|
+
finally {
|
|
234
|
+
rl.close();
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* List all configured profiles
|
|
239
|
+
*/
|
|
240
|
+
export async function listProfiles() {
|
|
241
|
+
const configManager = new ConfigManager();
|
|
242
|
+
try {
|
|
243
|
+
const profiles = await configManager.listProfiles();
|
|
244
|
+
const defaultProfile = await configManager.getDefaultProfile();
|
|
245
|
+
if (profiles.length === 0) {
|
|
246
|
+
console.log("No profiles configured. Run 'f5xc-api-mcp --setup' to create one.\n");
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
console.log("Configured profiles:\n");
|
|
250
|
+
for (const profile of profiles) {
|
|
251
|
+
const creds = await configManager.getProfile(profile);
|
|
252
|
+
const isDefault = profile === defaultProfile ? " (default)" : "";
|
|
253
|
+
console.log(` • ${profile}${isDefault}`);
|
|
254
|
+
if (creds?.metadata?.description) {
|
|
255
|
+
console.log(` ${creds.metadata.description}`);
|
|
256
|
+
}
|
|
257
|
+
if (creds?.apiUrl) {
|
|
258
|
+
console.log(` URL: ${creds.apiUrl}`);
|
|
259
|
+
}
|
|
260
|
+
console.log();
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
catch (error) {
|
|
264
|
+
logger.error("Failed to list profiles", {
|
|
265
|
+
error: error instanceof Error ? error.message : String(error),
|
|
266
|
+
});
|
|
267
|
+
process.exit(1);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Delete a profile
|
|
272
|
+
*/
|
|
273
|
+
export async function deleteProfile(name) {
|
|
274
|
+
const configManager = new ConfigManager();
|
|
275
|
+
try {
|
|
276
|
+
await configManager.deleteProfile(name);
|
|
277
|
+
console.log(`✅ Profile "${name}" deleted.\n`);
|
|
278
|
+
}
|
|
279
|
+
catch (error) {
|
|
280
|
+
console.log(`❌ ${error instanceof Error ? error.message : String(error)}\n`);
|
|
281
|
+
process.exit(1);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Set default profile
|
|
286
|
+
*/
|
|
287
|
+
export async function setDefaultProfile(name) {
|
|
288
|
+
const configManager = new ConfigManager();
|
|
289
|
+
try {
|
|
290
|
+
await configManager.setDefaultProfile(name);
|
|
291
|
+
console.log(`✅ Default profile set to "${name}".\n`);
|
|
292
|
+
}
|
|
293
|
+
catch (error) {
|
|
294
|
+
console.log(`❌ ${error instanceof Error ? error.message : String(error)}\n`);
|
|
295
|
+
process.exit(1);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Show current configuration
|
|
300
|
+
*/
|
|
301
|
+
export async function showConfig() {
|
|
302
|
+
const configManager = new ConfigManager();
|
|
303
|
+
try {
|
|
304
|
+
const config = await configManager.getConfig();
|
|
305
|
+
console.log("\nConfiguration:\n");
|
|
306
|
+
console.log(JSON.stringify(config, null, 2));
|
|
307
|
+
console.log();
|
|
308
|
+
}
|
|
309
|
+
catch (error) {
|
|
310
|
+
logger.error("Failed to read configuration", {
|
|
311
|
+
error: error instanceof Error ? error.message : String(error),
|
|
312
|
+
});
|
|
313
|
+
process.exit(1);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Test connection with a specific profile
|
|
318
|
+
*/
|
|
319
|
+
export async function testProfile(name) {
|
|
320
|
+
const configManager = new ConfigManager();
|
|
321
|
+
try {
|
|
322
|
+
const profile = await configManager.getProfile(name);
|
|
323
|
+
if (!profile) {
|
|
324
|
+
console.log(`❌ Profile "${name}" not found.\n`);
|
|
325
|
+
process.exit(1);
|
|
326
|
+
}
|
|
327
|
+
console.log(`\nTesting profile "${name}"...\n`);
|
|
328
|
+
if (!profile.apiUrl) {
|
|
329
|
+
console.log("❌ No API URL configured for this profile.\n");
|
|
330
|
+
process.exit(1);
|
|
331
|
+
}
|
|
332
|
+
if (!profile.apiToken && !profile.p12File) {
|
|
333
|
+
console.log("❌ No authentication configured for this profile.\n");
|
|
334
|
+
process.exit(1);
|
|
335
|
+
}
|
|
336
|
+
// TODO: Implement actual connection test
|
|
337
|
+
console.log("✅ Profile configuration is valid.\n");
|
|
338
|
+
console.log("Note: Full connection testing not yet implemented.\n");
|
|
339
|
+
}
|
|
340
|
+
catch (error) {
|
|
341
|
+
logger.error("Failed to test profile", {
|
|
342
|
+
error: error instanceof Error ? error.message : String(error),
|
|
343
|
+
});
|
|
344
|
+
process.exit(1);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
//# sourceMappingURL=setup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup.js","sourceRoot":"","sources":["../../src/cli/setup.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration Manager for F5XC API MCP
|
|
3
|
+
* Handles reading/writing configuration files with proper permissions and atomic operations
|
|
4
|
+
*/
|
|
5
|
+
import { ConfigFile, ProfileCredentials } from "./types.js";
|
|
6
|
+
/**
|
|
7
|
+
* Manages F5XC API MCP configuration files
|
|
8
|
+
*/
|
|
9
|
+
export declare class ConfigManager {
|
|
10
|
+
private configDir;
|
|
11
|
+
private configFile;
|
|
12
|
+
constructor(configDir?: string, configFile?: string);
|
|
13
|
+
/**
|
|
14
|
+
* Check if configuration file exists
|
|
15
|
+
*/
|
|
16
|
+
exists(): Promise<boolean>;
|
|
17
|
+
/**
|
|
18
|
+
* Ensure configuration directory exists with proper permissions
|
|
19
|
+
*/
|
|
20
|
+
private ensureConfigDir;
|
|
21
|
+
/**
|
|
22
|
+
* Check and fix file permissions
|
|
23
|
+
*/
|
|
24
|
+
private checkAndFixPermissions;
|
|
25
|
+
/**
|
|
26
|
+
* Read configuration file (async)
|
|
27
|
+
*/
|
|
28
|
+
read(): Promise<ConfigFile>;
|
|
29
|
+
/**
|
|
30
|
+
* Read configuration file synchronously (for initialization)
|
|
31
|
+
* Note: This is used during CredentialManager construction
|
|
32
|
+
*/
|
|
33
|
+
readSync(): ConfigFile;
|
|
34
|
+
/**
|
|
35
|
+
* Write configuration file with atomic operation
|
|
36
|
+
* Uses temp file + rename pattern to ensure consistency
|
|
37
|
+
*/
|
|
38
|
+
write(config: ConfigFile): Promise<void>;
|
|
39
|
+
/**
|
|
40
|
+
* Get list of all profile names
|
|
41
|
+
*/
|
|
42
|
+
listProfiles(): Promise<string[]>;
|
|
43
|
+
/**
|
|
44
|
+
* Get a specific profile by name
|
|
45
|
+
*/
|
|
46
|
+
getProfile(name: string): Promise<ProfileCredentials | null>;
|
|
47
|
+
/**
|
|
48
|
+
* Create or update a profile
|
|
49
|
+
*/
|
|
50
|
+
setProfile(name: string, credentials: ProfileCredentials): Promise<void>;
|
|
51
|
+
/**
|
|
52
|
+
* Delete a profile by name
|
|
53
|
+
*/
|
|
54
|
+
deleteProfile(name: string): Promise<void>;
|
|
55
|
+
/**
|
|
56
|
+
* Get the default profile name
|
|
57
|
+
*/
|
|
58
|
+
getDefaultProfile(): Promise<string | null>;
|
|
59
|
+
/**
|
|
60
|
+
* Set the default profile
|
|
61
|
+
*/
|
|
62
|
+
setDefaultProfile(name: string): Promise<void>;
|
|
63
|
+
/**
|
|
64
|
+
* Update the lastUsedAt timestamp for a profile
|
|
65
|
+
*/
|
|
66
|
+
touchProfile(name: string): Promise<void>;
|
|
67
|
+
/**
|
|
68
|
+
* Get the full configuration file
|
|
69
|
+
*/
|
|
70
|
+
getConfig(): Promise<ConfigFile>;
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=config-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-manager.d.ts","sourceRoot":"","sources":["../../src/config/config-manager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,EACL,UAAU,EACV,kBAAkB,EAMnB,MAAM,YAAY,CAAC;AAGpB;;GAEG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,UAAU,CAAS;gBAEf,SAAS,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM;IAKnD;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC;IAShC;;OAEG;YACW,eAAe;IAY7B;;OAEG;YACW,sBAAsB;IAsBpC;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,UAAU,CAAC;IAiBjC;;;OAGG;IACH,QAAQ,IAAI,UAAU;IAiCtB;;;OAGG;IACG,KAAK,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IA+B9C;;OAEG;IACG,YAAY,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAKvC;;OAEG;IACG,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC;IAKlE;;OAEG;IACG,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IAyB9E;;OAEG;IACG,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBhD;;OAEG;IACG,iBAAiB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAKjD;;OAEG;IACG,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAYpD;;OAEG;IACG,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAsB/C;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC,UAAU,CAAC;CAGvC"}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration Manager for F5XC API MCP
|
|
3
|
+
* Handles reading/writing configuration files with proper permissions and atomic operations
|
|
4
|
+
*/
|
|
5
|
+
import * as fs from "fs/promises";
|
|
6
|
+
import * as fsSync from "fs";
|
|
7
|
+
import { DEFAULT_CONFIG, CONFIG_DIR_PERMISSIONS, CONFIG_FILE_PERMISSIONS, getConfigDirPath, getConfigFilePath, } from "./types.js";
|
|
8
|
+
import { validateConfigFile, safeValidateConfigFile } from "./schema.js";
|
|
9
|
+
/**
|
|
10
|
+
* Manages F5XC API MCP configuration files
|
|
11
|
+
*/
|
|
12
|
+
export class ConfigManager {
|
|
13
|
+
configDir;
|
|
14
|
+
configFile;
|
|
15
|
+
constructor(configDir, configFile) {
|
|
16
|
+
this.configDir = configDir || getConfigDirPath();
|
|
17
|
+
this.configFile = configFile || getConfigFilePath();
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Check if configuration file exists
|
|
21
|
+
*/
|
|
22
|
+
async exists() {
|
|
23
|
+
try {
|
|
24
|
+
await fs.access(this.configFile);
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Ensure configuration directory exists with proper permissions
|
|
33
|
+
*/
|
|
34
|
+
async ensureConfigDir() {
|
|
35
|
+
try {
|
|
36
|
+
await fs.mkdir(this.configDir, { recursive: true, mode: CONFIG_DIR_PERMISSIONS });
|
|
37
|
+
// Fix permissions if directory already exists
|
|
38
|
+
await fs.chmod(this.configDir, CONFIG_DIR_PERMISSIONS);
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
throw new Error(`Failed to create config directory: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Check and fix file permissions
|
|
46
|
+
*/
|
|
47
|
+
async checkAndFixPermissions(filePath, expectedPermissions) {
|
|
48
|
+
try {
|
|
49
|
+
const stats = await fs.stat(filePath);
|
|
50
|
+
const currentMode = stats.mode & 0o777;
|
|
51
|
+
if (currentMode !== expectedPermissions) {
|
|
52
|
+
console.warn(`Warning: Config file has insecure permissions (${currentMode.toString(8)}). Fixing to ${expectedPermissions.toString(8)}...`);
|
|
53
|
+
await fs.chmod(filePath, expectedPermissions);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
const err = error;
|
|
58
|
+
if (err.code !== "ENOENT") {
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Read configuration file (async)
|
|
65
|
+
*/
|
|
66
|
+
async read() {
|
|
67
|
+
try {
|
|
68
|
+
await this.checkAndFixPermissions(this.configFile, CONFIG_FILE_PERMISSIONS);
|
|
69
|
+
const content = await fs.readFile(this.configFile, "utf-8");
|
|
70
|
+
const data = JSON.parse(content);
|
|
71
|
+
return validateConfigFile(data);
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
const err = error;
|
|
75
|
+
if (err.code === "ENOENT") {
|
|
76
|
+
return DEFAULT_CONFIG;
|
|
77
|
+
}
|
|
78
|
+
throw new Error(`Failed to read config file: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Read configuration file synchronously (for initialization)
|
|
83
|
+
* Note: This is used during CredentialManager construction
|
|
84
|
+
*/
|
|
85
|
+
readSync() {
|
|
86
|
+
try {
|
|
87
|
+
if (!fsSync.existsSync(this.configFile)) {
|
|
88
|
+
return DEFAULT_CONFIG;
|
|
89
|
+
}
|
|
90
|
+
// Check permissions
|
|
91
|
+
const stats = fsSync.statSync(this.configFile);
|
|
92
|
+
const currentMode = stats.mode & 0o777;
|
|
93
|
+
if (currentMode !== CONFIG_FILE_PERMISSIONS) {
|
|
94
|
+
console.warn(`Warning: Config file has insecure permissions (${currentMode.toString(8)}). Run 'f5xc-api-mcp --setup' to fix.`);
|
|
95
|
+
}
|
|
96
|
+
const content = fsSync.readFileSync(this.configFile, "utf-8");
|
|
97
|
+
const data = JSON.parse(content);
|
|
98
|
+
const result = safeValidateConfigFile(data);
|
|
99
|
+
if (!result.success) {
|
|
100
|
+
console.warn(`Warning: Invalid config file: ${result.error}`);
|
|
101
|
+
return DEFAULT_CONFIG;
|
|
102
|
+
}
|
|
103
|
+
return result.data;
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
console.warn(`Warning: Failed to read config file: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
107
|
+
return DEFAULT_CONFIG;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Write configuration file with atomic operation
|
|
112
|
+
* Uses temp file + rename pattern to ensure consistency
|
|
113
|
+
*/
|
|
114
|
+
async write(config) {
|
|
115
|
+
await this.ensureConfigDir();
|
|
116
|
+
const tempFile = `${this.configFile}.tmp.${Date.now()}`;
|
|
117
|
+
try {
|
|
118
|
+
// Validate before writing
|
|
119
|
+
validateConfigFile(config);
|
|
120
|
+
// Write to temporary file
|
|
121
|
+
const content = JSON.stringify(config, null, 2);
|
|
122
|
+
await fs.writeFile(tempFile, content, { mode: CONFIG_FILE_PERMISSIONS });
|
|
123
|
+
// Atomic rename
|
|
124
|
+
await fs.rename(tempFile, this.configFile);
|
|
125
|
+
// Verify permissions
|
|
126
|
+
await this.checkAndFixPermissions(this.configFile, CONFIG_FILE_PERMISSIONS);
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
// Clean up temp file on error
|
|
130
|
+
try {
|
|
131
|
+
await fs.unlink(tempFile);
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
// Ignore cleanup errors
|
|
135
|
+
}
|
|
136
|
+
throw new Error(`Failed to write config file: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Get list of all profile names
|
|
141
|
+
*/
|
|
142
|
+
async listProfiles() {
|
|
143
|
+
const config = await this.read();
|
|
144
|
+
return Object.keys(config.profiles);
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Get a specific profile by name
|
|
148
|
+
*/
|
|
149
|
+
async getProfile(name) {
|
|
150
|
+
const config = await this.read();
|
|
151
|
+
return config.profiles[name] || null;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Create or update a profile
|
|
155
|
+
*/
|
|
156
|
+
async setProfile(name, credentials) {
|
|
157
|
+
const config = await this.read();
|
|
158
|
+
// Validate profile name
|
|
159
|
+
if (!/^[a-z0-9_-]+$/i.test(name)) {
|
|
160
|
+
throw new Error("Profile name must contain only alphanumeric characters, hyphens, and underscores");
|
|
161
|
+
}
|
|
162
|
+
// Set metadata if not provided
|
|
163
|
+
if (!credentials.metadata) {
|
|
164
|
+
credentials.metadata = {
|
|
165
|
+
createdAt: new Date().toISOString(),
|
|
166
|
+
lastModifiedAt: new Date().toISOString(),
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
credentials.metadata.lastModifiedAt = new Date().toISOString();
|
|
171
|
+
}
|
|
172
|
+
config.profiles[name] = credentials;
|
|
173
|
+
config.metadata = { lastModifiedAt: new Date().toISOString() };
|
|
174
|
+
await this.write(config);
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Delete a profile by name
|
|
178
|
+
*/
|
|
179
|
+
async deleteProfile(name) {
|
|
180
|
+
const config = await this.read();
|
|
181
|
+
if (!config.profiles[name]) {
|
|
182
|
+
throw new Error(`Profile "${name}" does not exist`);
|
|
183
|
+
}
|
|
184
|
+
delete config.profiles[name];
|
|
185
|
+
// Clear default profile if it was deleted
|
|
186
|
+
if (config.defaultProfile === name) {
|
|
187
|
+
config.defaultProfile = undefined;
|
|
188
|
+
}
|
|
189
|
+
config.metadata = { lastModifiedAt: new Date().toISOString() };
|
|
190
|
+
await this.write(config);
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Get the default profile name
|
|
194
|
+
*/
|
|
195
|
+
async getDefaultProfile() {
|
|
196
|
+
const config = await this.read();
|
|
197
|
+
return config.defaultProfile || null;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Set the default profile
|
|
201
|
+
*/
|
|
202
|
+
async setDefaultProfile(name) {
|
|
203
|
+
const config = await this.read();
|
|
204
|
+
if (!config.profiles[name]) {
|
|
205
|
+
throw new Error(`Profile "${name}" does not exist`);
|
|
206
|
+
}
|
|
207
|
+
config.defaultProfile = name;
|
|
208
|
+
config.metadata = { lastModifiedAt: new Date().toISOString() };
|
|
209
|
+
await this.write(config);
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Update the lastUsedAt timestamp for a profile
|
|
213
|
+
*/
|
|
214
|
+
async touchProfile(name) {
|
|
215
|
+
const config = await this.read();
|
|
216
|
+
const profile = config.profiles[name];
|
|
217
|
+
if (!profile) {
|
|
218
|
+
return; // Silently ignore if profile doesn't exist
|
|
219
|
+
}
|
|
220
|
+
if (!profile.metadata) {
|
|
221
|
+
profile.metadata = {
|
|
222
|
+
createdAt: new Date().toISOString(),
|
|
223
|
+
lastModifiedAt: new Date().toISOString(),
|
|
224
|
+
lastUsedAt: new Date().toISOString(),
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
profile.metadata.lastUsedAt = new Date().toISOString();
|
|
229
|
+
}
|
|
230
|
+
config.metadata = { lastModifiedAt: new Date().toISOString() };
|
|
231
|
+
await this.write(config);
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Get the full configuration file
|
|
235
|
+
*/
|
|
236
|
+
async getConfig() {
|
|
237
|
+
return this.read();
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
//# sourceMappingURL=config-manager.js.map
|