mlgym-deploy 3.3.2 → 3.3.6
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/index.js +43 -32
- package/package.json +5 -4
package/index.js
CHANGED
|
@@ -18,7 +18,7 @@ import crypto from 'crypto';
|
|
|
18
18
|
const execAsync = promisify(exec);
|
|
19
19
|
|
|
20
20
|
// Current version of this MCP server - INCREMENT FOR WORKFLOW FIXES
|
|
21
|
-
const CURRENT_VERSION = '3.3.
|
|
21
|
+
const CURRENT_VERSION = '3.3.6'; // Added mlgym_deploy_logs to package.json tools list for proper MCP registration
|
|
22
22
|
const PACKAGE_NAME = 'mlgym-deploy';
|
|
23
23
|
|
|
24
24
|
// Debug logging configuration - ENABLED BY DEFAULT
|
|
@@ -986,8 +986,8 @@ async function detectDeploymentStrategy(projectPath) {
|
|
|
986
986
|
|
|
987
987
|
// Case 2: Only docker-compose
|
|
988
988
|
if (!hasDockerfile && hasCompose) {
|
|
989
|
-
log.info('MCP >>> [detectDeploymentStrategy] Strategy:
|
|
990
|
-
return { type: '
|
|
989
|
+
log.info('MCP >>> [detectDeploymentStrategy] Strategy: dockercompose (compose only)');
|
|
990
|
+
return { type: 'dockercompose', reason: 'docker-compose.yml only' };
|
|
991
991
|
}
|
|
992
992
|
|
|
993
993
|
// Case 3: Both exist - analyze docker-compose to decide
|
|
@@ -1008,11 +1008,11 @@ async function detectDeploymentStrategy(projectPath) {
|
|
|
1008
1008
|
|
|
1009
1009
|
// COMPLEX: Multiple services (web + database, etc.)
|
|
1010
1010
|
// → MUST use docker-compose for orchestration
|
|
1011
|
-
log.info('MCP >>> [detectDeploymentStrategy] Strategy:
|
|
1012
|
-
return { type: '
|
|
1011
|
+
log.info('MCP >>> [detectDeploymentStrategy] Strategy: dockercompose (multi-service)');
|
|
1012
|
+
return { type: 'dockercompose', reason: 'Application requires multiple services (web + database/cache/etc)' };
|
|
1013
1013
|
} catch (err) {
|
|
1014
|
-
log.error('MCP >>> [detectDeploymentStrategy] Analysis failed, defaulting to
|
|
1015
|
-
return { type: '
|
|
1014
|
+
log.error('MCP >>> [detectDeploymentStrategy] Analysis failed, defaulting to dockercompose:', err.message);
|
|
1015
|
+
return { type: 'dockercompose', reason: 'Multiple deployment files found' };
|
|
1016
1016
|
}
|
|
1017
1017
|
}
|
|
1018
1018
|
|
|
@@ -1296,8 +1296,8 @@ async function initProject(args) {
|
|
|
1296
1296
|
projectData.hostname = hostname;
|
|
1297
1297
|
projectData.local_path = local_path;
|
|
1298
1298
|
|
|
1299
|
-
// Read docker-compose content if using
|
|
1300
|
-
if (strategy.type === '
|
|
1299
|
+
// Read docker-compose content if using dockercompose strategy (v3.2.1+)
|
|
1300
|
+
if (strategy.type === 'dockercompose') {
|
|
1301
1301
|
const composeFiles = ['docker-compose.yml', 'docker-compose.yaml'];
|
|
1302
1302
|
let composeContent = null;
|
|
1303
1303
|
|
|
@@ -1364,6 +1364,10 @@ async function initProject(args) {
|
|
|
1364
1364
|
console.error('⚠️ Warning: Failed to add administrator as member:', memberErr.message);
|
|
1365
1365
|
}
|
|
1366
1366
|
|
|
1367
|
+
// Wait for GitLab project to be fully created and accessible
|
|
1368
|
+
console.error('Waiting for GitLab project creation to complete (20 seconds)...');
|
|
1369
|
+
await new Promise(resolve => setTimeout(resolve, 20000));
|
|
1370
|
+
|
|
1367
1371
|
// Initialize local git repository if needed
|
|
1368
1372
|
const absolutePath = path.resolve(local_path);
|
|
1369
1373
|
try {
|
|
@@ -1402,9 +1406,9 @@ async function initProject(args) {
|
|
|
1402
1406
|
console.error('Note: ssh-agent not available, relying on SSH config');
|
|
1403
1407
|
}
|
|
1404
1408
|
|
|
1405
|
-
// Wait for GitLab SSH key propagation (
|
|
1406
|
-
console.error('Waiting for GitLab SSH key propagation (
|
|
1407
|
-
await new Promise(resolve => setTimeout(resolve,
|
|
1409
|
+
// Wait for GitLab SSH key propagation (20 seconds)
|
|
1410
|
+
console.error('Waiting for GitLab SSH key propagation (30 seconds)...');
|
|
1411
|
+
await new Promise(resolve => setTimeout(resolve, 30000));
|
|
1408
1412
|
|
|
1409
1413
|
// Test SSH connectivity
|
|
1410
1414
|
console.error('Testing SSH connection to GitLab...');
|
|
@@ -1473,7 +1477,11 @@ async function initProject(args) {
|
|
|
1473
1477
|
log.debug('MCP >>> [initProject] Git remote URL:', gitUrl);
|
|
1474
1478
|
gitSteps.push('Pushing to GitLab to trigger deployment');
|
|
1475
1479
|
console.error('Executing: git push -u mlgym main');
|
|
1476
|
-
|
|
1480
|
+
const authInfo = await loadAuth();
|
|
1481
|
+
const sanitizedEmail = authInfo.email.replace('@', '_at_').replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
1482
|
+
const sshKeyPath = path.join(os.homedir(), '.ssh', `mlgym_${sanitizedEmail}`);
|
|
1483
|
+
const gitSshCmd = `ssh -i "${sshKeyPath}"`;
|
|
1484
|
+
await execAsync('git push -u mlgym main', { cwd: absolutePath, env: { ...process.env, GIT_SSH_COMMAND: gitSshCmd } });
|
|
1477
1485
|
gitSteps.push('✅ Successfully pushed to GitLab');
|
|
1478
1486
|
pushSucceeded = true;
|
|
1479
1487
|
log.success('MCP >>> [initProject] ✅ Git push completed successfully!');
|
|
@@ -1497,7 +1505,7 @@ async function initProject(args) {
|
|
|
1497
1505
|
for (let i = 0; i < 12; i++) {
|
|
1498
1506
|
await new Promise(resolve => setTimeout(resolve, 5000)); // Wait 5 seconds
|
|
1499
1507
|
|
|
1500
|
-
const statusResult = await apiRequest('GET', `/api/v1/projects/${project.id}/
|
|
1508
|
+
const statusResult = await apiRequest('GET', `/api/v1/projects/${project.id}/deployments`, null, true);
|
|
1501
1509
|
if (statusResult.success && statusResult.data) {
|
|
1502
1510
|
const status = statusResult.data.status;
|
|
1503
1511
|
console.error(`Deployment status: ${status}`);
|
|
@@ -2012,7 +2020,7 @@ async function deployProject(args) {
|
|
|
2012
2020
|
log.info('MCP >>> STEP 4.5: Validating deployment configuration...');
|
|
2013
2021
|
const strategy = detectDeploymentStrategy(local_path);
|
|
2014
2022
|
|
|
2015
|
-
if (strategy.type === '
|
|
2023
|
+
if (strategy.type === 'dockercompose') {
|
|
2016
2024
|
const composePath = path.join(local_path, 'docker-compose.yml');
|
|
2017
2025
|
const composePathYAML = path.join(local_path, 'docker-compose.yaml');
|
|
2018
2026
|
const actualPath = fsSync.existsSync(composePath) ? composePath : composePathYAML;
|
|
@@ -2224,27 +2232,30 @@ async function getDeploymentLogs(args) {
|
|
|
2224
2232
|
}
|
|
2225
2233
|
|
|
2226
2234
|
log.info(`MCP >>> [getDeploymentLogs] Detected project name: ${projectName}`);
|
|
2235
|
+
// Get Coolify app UUID directly from User Agent by name
|
|
2236
|
+
const userAgentURL = 'http://coolify.eu.ezb.net:9000';
|
|
2237
|
+
const appLookupEndpoint = `${userAgentURL}/applications/by-name/${projectName}`;
|
|
2238
|
+
|
|
2239
|
+
log.info(`MCP >>> [getDeploymentLogs] Looking up application by name`);
|
|
2227
2240
|
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
+
let appUUID;
|
|
2242
|
+
try {
|
|
2243
|
+
const appResponse = await axios.get(appLookupEndpoint, {
|
|
2244
|
+
headers: {
|
|
2245
|
+
'Authorization': 'Bearer 1|Jkztb5qPptwRKgtocfbT2TLjp8WGJG1SkPE4DzLt4c5d600f',
|
|
2246
|
+
'Content-Type': 'application/json'
|
|
2247
|
+
}
|
|
2248
|
+
});
|
|
2249
|
+
appUUID = appResponse.data.uuid;
|
|
2250
|
+
log.info(`MCP >>> [getDeploymentLogs] Found Coolify app UUID: ${appUUID}`);
|
|
2251
|
+
} catch (appError) {
|
|
2252
|
+
if (appError.response?.status === 404) {
|
|
2253
|
+
throw new Error(`Project "${projectName}" not found in Coolify. Make sure deployment is enabled.`);
|
|
2254
|
+
}
|
|
2255
|
+
throw new Error(`Failed to lookup application: ${appError.message}`);
|
|
2241
2256
|
}
|
|
2242
2257
|
|
|
2243
|
-
const appUUID = project.coolify_app_uuid;
|
|
2244
|
-
log.info(`MCP >>> [getDeploymentLogs] Found Coolify app UUID: ${appUUID}`);
|
|
2245
|
-
|
|
2246
2258
|
// Call User Agent API to get deployment logs
|
|
2247
|
-
const userAgentURL = 'http://coolify.eu.ezb.net:9000';
|
|
2248
2259
|
const endpoint = `${userAgentURL}/applications/${appUUID}/deployment-logs?depth=${depth}`;
|
|
2249
2260
|
|
|
2250
2261
|
log.info(`MCP >>> [getDeploymentLogs] Calling User Agent: ${endpoint}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mlgym-deploy",
|
|
3
|
-
"version": "3.3.
|
|
3
|
+
"version": "3.3.6",
|
|
4
4
|
"description": "MCP server for MLGym - Complete deployment management: deploy, configure, monitor, and rollback applications",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -52,6 +52,10 @@
|
|
|
52
52
|
"name": "mlgym_set_env_vars",
|
|
53
53
|
"description": "Set environment variables for an application (DATABASE_URL, API keys, etc.)"
|
|
54
54
|
},
|
|
55
|
+
{
|
|
56
|
+
"name": "mlgym_deploy_logs",
|
|
57
|
+
"description": "View deployment history and logs for a project"
|
|
58
|
+
},
|
|
55
59
|
{
|
|
56
60
|
"name": "mlgym_set_health_check",
|
|
57
61
|
"description": "Configure health checks to monitor application availability"
|
|
@@ -83,6 +87,3 @@
|
|
|
83
87
|
]
|
|
84
88
|
}
|
|
85
89
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|