mlgym-deploy 2.6.0 → 2.7.1
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 +147 -4
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -16,8 +16,8 @@ import crypto from 'crypto';
|
|
|
16
16
|
|
|
17
17
|
const execAsync = promisify(exec);
|
|
18
18
|
|
|
19
|
-
// Current version of this MCP server - INCREMENT FOR
|
|
20
|
-
const CURRENT_VERSION = '2.
|
|
19
|
+
// Current version of this MCP server - INCREMENT FOR WORKFLOW FIXES
|
|
20
|
+
const CURRENT_VERSION = '2.7.1';
|
|
21
21
|
const PACKAGE_NAME = 'mlgym-deploy';
|
|
22
22
|
|
|
23
23
|
// Version check state
|
|
@@ -777,6 +777,122 @@ async function checkAuthStatus() {
|
|
|
777
777
|
};
|
|
778
778
|
}
|
|
779
779
|
|
|
780
|
+
// Smart deployment initialization that follows the correct workflow
|
|
781
|
+
async function smartDeploy(args) {
|
|
782
|
+
const { local_path = '.' } = args;
|
|
783
|
+
const absolutePath = path.resolve(local_path);
|
|
784
|
+
|
|
785
|
+
const steps = [];
|
|
786
|
+
|
|
787
|
+
try {
|
|
788
|
+
// Step 1: Check authentication
|
|
789
|
+
steps.push({ step: 'auth_check', status: 'running' });
|
|
790
|
+
const auth = await loadAuth();
|
|
791
|
+
if (!auth.token) {
|
|
792
|
+
steps[steps.length - 1].status = 'failed';
|
|
793
|
+
return {
|
|
794
|
+
content: [{
|
|
795
|
+
type: 'text',
|
|
796
|
+
text: JSON.stringify({
|
|
797
|
+
status: 'error',
|
|
798
|
+
message: 'Not authenticated. Please use mlgym_authenticate first',
|
|
799
|
+
workflow_steps: steps
|
|
800
|
+
}, null, 2)
|
|
801
|
+
}]
|
|
802
|
+
};
|
|
803
|
+
}
|
|
804
|
+
steps[steps.length - 1].status = 'completed';
|
|
805
|
+
|
|
806
|
+
// Step 2: Analyze project
|
|
807
|
+
steps.push({ step: 'project_analysis', status: 'running' });
|
|
808
|
+
const analysis = await analyzeProject(local_path);
|
|
809
|
+
steps[steps.length - 1].status = 'completed';
|
|
810
|
+
steps[steps.length - 1].result = {
|
|
811
|
+
type: analysis.project_type,
|
|
812
|
+
framework: analysis.framework,
|
|
813
|
+
suggested_name: analysis.suggested_name
|
|
814
|
+
};
|
|
815
|
+
|
|
816
|
+
// Step 3: Check existing project
|
|
817
|
+
steps.push({ step: 'check_existing', status: 'running' });
|
|
818
|
+
const projectStatus = await checkExistingProject(local_path);
|
|
819
|
+
steps[steps.length - 1].status = 'completed';
|
|
820
|
+
|
|
821
|
+
if (projectStatus.configured) {
|
|
822
|
+
return {
|
|
823
|
+
content: [{
|
|
824
|
+
type: 'text',
|
|
825
|
+
text: JSON.stringify({
|
|
826
|
+
status: 'info',
|
|
827
|
+
message: projectStatus.message,
|
|
828
|
+
project_name: projectStatus.name,
|
|
829
|
+
git_remote: `git@git.mlgym.io:${projectStatus.namespace}/${projectStatus.name}.git`,
|
|
830
|
+
next_steps: [
|
|
831
|
+
'Project already configured',
|
|
832
|
+
'Run: git push mlgym main'
|
|
833
|
+
],
|
|
834
|
+
workflow_steps: steps
|
|
835
|
+
}, null, 2)
|
|
836
|
+
}]
|
|
837
|
+
};
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
// Step 4: Prepare project (generate Dockerfile if needed)
|
|
841
|
+
steps.push({ step: 'prepare_project', status: 'running' });
|
|
842
|
+
if (!analysis.has_dockerfile && analysis.project_type !== 'unknown') {
|
|
843
|
+
const prepResult = await prepareProject({
|
|
844
|
+
local_path,
|
|
845
|
+
project_type: analysis.project_type,
|
|
846
|
+
framework: analysis.framework,
|
|
847
|
+
package_manager: analysis.package_manager
|
|
848
|
+
});
|
|
849
|
+
steps[steps.length - 1].status = 'completed';
|
|
850
|
+
steps[steps.length - 1].result = prepResult.actions;
|
|
851
|
+
} else {
|
|
852
|
+
steps[steps.length - 1].status = 'skipped';
|
|
853
|
+
steps[steps.length - 1].result = 'Dockerfile already exists or project type unknown';
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
// Return analysis and next steps
|
|
857
|
+
return {
|
|
858
|
+
content: [{
|
|
859
|
+
type: 'text',
|
|
860
|
+
text: JSON.stringify({
|
|
861
|
+
status: 'ready',
|
|
862
|
+
message: 'Project analyzed and prepared. Ready for MLGym initialization.',
|
|
863
|
+
analysis: {
|
|
864
|
+
project_type: analysis.project_type,
|
|
865
|
+
framework: analysis.framework,
|
|
866
|
+
suggested_name: analysis.suggested_name,
|
|
867
|
+
has_dockerfile: analysis.has_dockerfile || true
|
|
868
|
+
},
|
|
869
|
+
next_step: 'Use mlgym_project_init with project details to create MLGym project',
|
|
870
|
+
suggested_params: {
|
|
871
|
+
name: analysis.suggested_name,
|
|
872
|
+
description: `${analysis.framework || analysis.project_type} application`,
|
|
873
|
+
enable_deployment: true,
|
|
874
|
+
hostname: analysis.suggested_name
|
|
875
|
+
},
|
|
876
|
+
workflow_steps: steps
|
|
877
|
+
}, null, 2)
|
|
878
|
+
}]
|
|
879
|
+
};
|
|
880
|
+
|
|
881
|
+
} catch (error) {
|
|
882
|
+
return {
|
|
883
|
+
content: [{
|
|
884
|
+
type: 'text',
|
|
885
|
+
text: JSON.stringify({
|
|
886
|
+
status: 'error',
|
|
887
|
+
message: 'Smart deploy failed',
|
|
888
|
+
error: error.message,
|
|
889
|
+
workflow_steps: steps
|
|
890
|
+
}, null, 2)
|
|
891
|
+
}]
|
|
892
|
+
};
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
|
|
780
896
|
// Initialize Project (requires authentication)
|
|
781
897
|
async function initProject(args) {
|
|
782
898
|
let { name, description, enable_deployment = true, hostname, local_path = '.' } = args;
|
|
@@ -836,8 +952,18 @@ async function initProject(args) {
|
|
|
836
952
|
enable_deployment: enable_deployment
|
|
837
953
|
};
|
|
838
954
|
|
|
839
|
-
if (enable_deployment
|
|
840
|
-
|
|
955
|
+
if (enable_deployment) {
|
|
956
|
+
// Generate a secure webhook secret for deployments
|
|
957
|
+
const webhookSecret = Array.from(
|
|
958
|
+
crypto.getRandomValues(new Uint8Array(32)),
|
|
959
|
+
byte => byte.toString(16).padStart(2, '0')
|
|
960
|
+
).join('');
|
|
961
|
+
|
|
962
|
+
projectData.webhook_secret = webhookSecret;
|
|
963
|
+
|
|
964
|
+
if (hostname) {
|
|
965
|
+
projectData.hostname = hostname;
|
|
966
|
+
}
|
|
841
967
|
}
|
|
842
968
|
|
|
843
969
|
const result = await apiRequest('POST', '/api/v1/projects', projectData, true);
|
|
@@ -1057,6 +1183,20 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
1057
1183
|
}
|
|
1058
1184
|
}
|
|
1059
1185
|
}
|
|
1186
|
+
},
|
|
1187
|
+
{
|
|
1188
|
+
name: 'mlgym_smart_deploy',
|
|
1189
|
+
description: 'RECOMMENDED: Smart deployment workflow that automatically analyzes, prepares, and guides you through the entire deployment process. Use this for new projects!',
|
|
1190
|
+
inputSchema: {
|
|
1191
|
+
type: 'object',
|
|
1192
|
+
properties: {
|
|
1193
|
+
local_path: {
|
|
1194
|
+
type: 'string',
|
|
1195
|
+
description: 'Local directory path (defaults to current directory)',
|
|
1196
|
+
default: '.'
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1060
1200
|
}
|
|
1061
1201
|
]
|
|
1062
1202
|
};
|
|
@@ -1106,6 +1246,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1106
1246
|
}]
|
|
1107
1247
|
};
|
|
1108
1248
|
|
|
1249
|
+
case 'mlgym_smart_deploy':
|
|
1250
|
+
return await smartDeploy(args);
|
|
1251
|
+
|
|
1109
1252
|
default:
|
|
1110
1253
|
throw new Error(`Unknown tool: ${name}`);
|
|
1111
1254
|
}
|