mlgym-deploy 2.1.0 → 2.2.0
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-v2.js +24 -2
- package/index.js +214 -2
- package/package.json +1 -1
package/index-v2.js
CHANGED
|
@@ -964,25 +964,44 @@ async function deployProject(sessionId, args) {
|
|
|
964
964
|
description: `Deployed via MLGym MCP from ${session.framework} project`,
|
|
965
965
|
visibility: 'private',
|
|
966
966
|
enable_deployment: true,
|
|
967
|
-
deployment_region: region,
|
|
968
967
|
webhook_secret: crypto.randomBytes(16).toString('hex')
|
|
968
|
+
// Note: deployment_region is not used by backend, it uses random server selection
|
|
969
969
|
};
|
|
970
970
|
|
|
971
|
+
console.error(`Creating project with deployment enabled for ${session.projectName} in region ${region}`);
|
|
971
972
|
const result = await apiRequest('POST', '/api/v1/projects', projectData);
|
|
972
973
|
|
|
973
974
|
if (!result.success) {
|
|
975
|
+
console.error(`Project creation failed: ${result.error}`);
|
|
976
|
+
// Provide more detailed error information
|
|
974
977
|
return {
|
|
975
978
|
content: [{
|
|
976
979
|
type: 'text',
|
|
977
980
|
text: JSON.stringify({
|
|
978
981
|
status: 'error',
|
|
979
|
-
message: `Deployment failed: ${result.error}
|
|
982
|
+
message: `Deployment failed: ${result.error}`,
|
|
983
|
+
details: {
|
|
984
|
+
project_name: session.projectName,
|
|
985
|
+
region: region,
|
|
986
|
+
framework: session.framework,
|
|
987
|
+
suggestion: 'Please ensure the backend and Coolify services are running properly'
|
|
988
|
+
}
|
|
980
989
|
}, null, 2)
|
|
981
990
|
}]
|
|
982
991
|
};
|
|
983
992
|
}
|
|
984
993
|
|
|
985
994
|
const project = result.data;
|
|
995
|
+
console.error(`Project created successfully: ${project.name} (ID: ${project.id})`);
|
|
996
|
+
console.error(`SSH URL: ${project.ssh_url_to_repo}`);
|
|
997
|
+
|
|
998
|
+
// Check if deployment was actually created
|
|
999
|
+
const deploymentCreated = project.deployment_status || project.coolify_resource_id;
|
|
1000
|
+
if (deploymentCreated) {
|
|
1001
|
+
console.error(`Deployment resource created in Coolify`);
|
|
1002
|
+
} else {
|
|
1003
|
+
console.error(`Warning: Project created but deployment resource might not be set up`);
|
|
1004
|
+
}
|
|
986
1005
|
|
|
987
1006
|
// Initialize git repository
|
|
988
1007
|
const gitCommands = [
|
|
@@ -990,6 +1009,8 @@ async function deployProject(sessionId, args) {
|
|
|
990
1009
|
`git remote add origin ${project.ssh_url_to_repo}`,
|
|
991
1010
|
'git add .',
|
|
992
1011
|
'git commit -m "Initial deployment via MLGym"',
|
|
1012
|
+
'# Wait 5-10 seconds for SSH key to propagate in GitLab',
|
|
1013
|
+
'sleep 10',
|
|
993
1014
|
'git push -u origin main'
|
|
994
1015
|
];
|
|
995
1016
|
|
|
@@ -1014,6 +1035,7 @@ async function deployProject(sessionId, args) {
|
|
|
1014
1035
|
ssl: 'auto-provisioned',
|
|
1015
1036
|
cdn: 'enabled'
|
|
1016
1037
|
},
|
|
1038
|
+
important_note: '⚠️ SSH key propagation: Please wait 10 seconds before pushing to allow GitLab to activate your SSH key',
|
|
1017
1039
|
next_steps: {
|
|
1018
1040
|
message: 'Run these commands in your project directory:',
|
|
1019
1041
|
commands: gitCommands
|
package/index.js
CHANGED
|
@@ -2019,10 +2019,47 @@ async function initProject(args) {
|
|
|
2019
2019
|
namespace: project.namespace?.path || project.namespace || auth.email.split('@')[0]
|
|
2020
2020
|
};
|
|
2021
2021
|
|
|
2022
|
-
// If deployment was enabled,
|
|
2022
|
+
// If deployment was enabled, create Dockerfile if not exists
|
|
2023
2023
|
if (enable_deployment) {
|
|
2024
2024
|
console.error('Deployment enabled - backend has set up webhook and Coolify resources automatically');
|
|
2025
2025
|
|
|
2026
|
+
// Check if Dockerfile already exists
|
|
2027
|
+
const dockerfilePath = path.join(local_path, 'Dockerfile');
|
|
2028
|
+
let dockerfileCreated = false;
|
|
2029
|
+
|
|
2030
|
+
try {
|
|
2031
|
+
await fs.access(dockerfilePath);
|
|
2032
|
+
console.error('Dockerfile already exists in project');
|
|
2033
|
+
} catch {
|
|
2034
|
+
// Dockerfile doesn't exist, create one based on detected framework
|
|
2035
|
+
console.error('No Dockerfile found, generating one...');
|
|
2036
|
+
|
|
2037
|
+
try {
|
|
2038
|
+
const framework = await detectFramework(local_path);
|
|
2039
|
+
console.error(`Detected framework: ${framework}`);
|
|
2040
|
+
|
|
2041
|
+
const dockerfileContent = generateDockerfile(framework, name);
|
|
2042
|
+
await fs.writeFile(dockerfilePath, dockerfileContent, 'utf8');
|
|
2043
|
+
dockerfileCreated = true;
|
|
2044
|
+
|
|
2045
|
+
console.error(`Created Dockerfile for ${framework} framework`);
|
|
2046
|
+
|
|
2047
|
+
// If React app, also create nginx.conf
|
|
2048
|
+
if (framework === 'react') {
|
|
2049
|
+
const nginxPath = path.join(local_path, 'nginx.conf');
|
|
2050
|
+
try {
|
|
2051
|
+
await fs.access(nginxPath);
|
|
2052
|
+
} catch {
|
|
2053
|
+
const nginxContent = generateNginxConf();
|
|
2054
|
+
await fs.writeFile(nginxPath, nginxContent, 'utf8');
|
|
2055
|
+
console.error('Created nginx.conf for React app');
|
|
2056
|
+
}
|
|
2057
|
+
}
|
|
2058
|
+
} catch (err) {
|
|
2059
|
+
console.error(`Failed to create Dockerfile: ${err.message}`);
|
|
2060
|
+
}
|
|
2061
|
+
}
|
|
2062
|
+
|
|
2026
2063
|
response.webhook = {
|
|
2027
2064
|
secret: webhookSecret,
|
|
2028
2065
|
status: 'created',
|
|
@@ -2032,7 +2069,8 @@ async function initProject(args) {
|
|
|
2032
2069
|
response.deployment = {
|
|
2033
2070
|
status: 'initialized',
|
|
2034
2071
|
domain: `${name}.eu-central.mlgym.app`,
|
|
2035
|
-
message: 'Coolify deployment ready - push to main branch to trigger deployment'
|
|
2072
|
+
message: 'Coolify deployment ready - push to main branch to trigger deployment',
|
|
2073
|
+
dockerfile: dockerfileCreated ? 'Generated automatically based on framework detection' : 'Using existing Dockerfile'
|
|
2036
2074
|
};
|
|
2037
2075
|
}
|
|
2038
2076
|
|
|
@@ -2075,6 +2113,180 @@ function generateWebhookSecret() {
|
|
|
2075
2113
|
return secret;
|
|
2076
2114
|
}
|
|
2077
2115
|
|
|
2116
|
+
// Generate Dockerfile based on detected framework
|
|
2117
|
+
function generateDockerfile(framework, projectName) {
|
|
2118
|
+
const dockerfiles = {
|
|
2119
|
+
nextjs: `# Next.js Production Dockerfile
|
|
2120
|
+
FROM node:20-alpine AS builder
|
|
2121
|
+
WORKDIR /app
|
|
2122
|
+
COPY package*.json ./
|
|
2123
|
+
RUN npm ci
|
|
2124
|
+
COPY . .
|
|
2125
|
+
RUN npm run build
|
|
2126
|
+
|
|
2127
|
+
FROM node:20-alpine
|
|
2128
|
+
WORKDIR /app
|
|
2129
|
+
ENV NODE_ENV=production
|
|
2130
|
+
COPY --from=builder /app/public ./public
|
|
2131
|
+
COPY --from=builder /app/.next ./.next
|
|
2132
|
+
COPY --from=builder /app/node_modules ./node_modules
|
|
2133
|
+
COPY --from=builder /app/package.json ./package.json
|
|
2134
|
+
EXPOSE 3000
|
|
2135
|
+
ENV PORT=3000
|
|
2136
|
+
CMD ["npm", "start"]`,
|
|
2137
|
+
|
|
2138
|
+
react: `# React Production Dockerfile
|
|
2139
|
+
FROM node:20-alpine AS builder
|
|
2140
|
+
WORKDIR /app
|
|
2141
|
+
COPY package*.json ./
|
|
2142
|
+
RUN npm ci
|
|
2143
|
+
COPY . .
|
|
2144
|
+
RUN npm run build
|
|
2145
|
+
|
|
2146
|
+
FROM nginx:alpine
|
|
2147
|
+
COPY --from=builder /app/build /usr/share/nginx/html
|
|
2148
|
+
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
|
2149
|
+
EXPOSE 80
|
|
2150
|
+
CMD ["nginx", "-g", "daemon off;"]`,
|
|
2151
|
+
|
|
2152
|
+
express: `# Express.js Dockerfile
|
|
2153
|
+
FROM node:20-alpine
|
|
2154
|
+
WORKDIR /app
|
|
2155
|
+
COPY package*.json ./
|
|
2156
|
+
RUN npm ci
|
|
2157
|
+
COPY . .
|
|
2158
|
+
EXPOSE 3000
|
|
2159
|
+
ENV PORT=3000
|
|
2160
|
+
CMD ["node", "index.js"]`,
|
|
2161
|
+
|
|
2162
|
+
django: `# Django Dockerfile
|
|
2163
|
+
FROM python:3.11-slim
|
|
2164
|
+
WORKDIR /app
|
|
2165
|
+
ENV PYTHONUNBUFFERED=1
|
|
2166
|
+
COPY requirements.txt .
|
|
2167
|
+
RUN pip install --no-cache-dir -r requirements.txt
|
|
2168
|
+
COPY . .
|
|
2169
|
+
RUN python manage.py collectstatic --noinput
|
|
2170
|
+
EXPOSE 8000
|
|
2171
|
+
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "myproject.wsgi:application"]`,
|
|
2172
|
+
|
|
2173
|
+
php: `# PHP Dockerfile
|
|
2174
|
+
FROM php:8.2-apache
|
|
2175
|
+
RUN docker-php-ext-install pdo pdo_mysql
|
|
2176
|
+
COPY . /var/www/html/
|
|
2177
|
+
RUN a2enmod rewrite
|
|
2178
|
+
EXPOSE 80`,
|
|
2179
|
+
|
|
2180
|
+
static: `# Static Site Dockerfile
|
|
2181
|
+
FROM nginx:alpine
|
|
2182
|
+
COPY . /usr/share/nginx/html
|
|
2183
|
+
EXPOSE 80
|
|
2184
|
+
CMD ["nginx", "-g", "daemon off;"]`,
|
|
2185
|
+
|
|
2186
|
+
go: `# Go Dockerfile
|
|
2187
|
+
FROM golang:1.21-alpine AS builder
|
|
2188
|
+
WORKDIR /app
|
|
2189
|
+
COPY go.* ./
|
|
2190
|
+
RUN go mod download
|
|
2191
|
+
COPY . .
|
|
2192
|
+
RUN go build -o main .
|
|
2193
|
+
|
|
2194
|
+
FROM alpine:latest
|
|
2195
|
+
RUN apk --no-cache add ca-certificates
|
|
2196
|
+
WORKDIR /root/
|
|
2197
|
+
COPY --from=builder /app/main .
|
|
2198
|
+
EXPOSE 8080
|
|
2199
|
+
CMD ["./main"]`,
|
|
2200
|
+
|
|
2201
|
+
rust: `# Rust Dockerfile
|
|
2202
|
+
FROM rust:1.75 AS builder
|
|
2203
|
+
WORKDIR /app
|
|
2204
|
+
COPY Cargo.* ./
|
|
2205
|
+
COPY src ./src
|
|
2206
|
+
RUN cargo build --release
|
|
2207
|
+
|
|
2208
|
+
FROM debian:bookworm-slim
|
|
2209
|
+
WORKDIR /app
|
|
2210
|
+
COPY --from=builder /app/target/release/${projectName} .
|
|
2211
|
+
EXPOSE 8080
|
|
2212
|
+
CMD ["./${projectName}"]`
|
|
2213
|
+
};
|
|
2214
|
+
|
|
2215
|
+
return dockerfiles[framework] || dockerfiles.static;
|
|
2216
|
+
}
|
|
2217
|
+
|
|
2218
|
+
// Generate nginx.conf for React apps
|
|
2219
|
+
function generateNginxConf() {
|
|
2220
|
+
return `server {
|
|
2221
|
+
listen 80;
|
|
2222
|
+
location / {
|
|
2223
|
+
root /usr/share/nginx/html;
|
|
2224
|
+
index index.html index.htm;
|
|
2225
|
+
try_files $uri $uri/ /index.html;
|
|
2226
|
+
}
|
|
2227
|
+
}`;
|
|
2228
|
+
}
|
|
2229
|
+
|
|
2230
|
+
// Detect project framework by analyzing files
|
|
2231
|
+
async function detectFramework(localPath) {
|
|
2232
|
+
try {
|
|
2233
|
+
// Check for package.json
|
|
2234
|
+
const packagePath = path.join(localPath, 'package.json');
|
|
2235
|
+
const packageData = await fs.readFile(packagePath, 'utf8');
|
|
2236
|
+
const packageJson = JSON.parse(packageData);
|
|
2237
|
+
|
|
2238
|
+
// Check dependencies
|
|
2239
|
+
const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
|
|
2240
|
+
|
|
2241
|
+
if (deps.next) return 'nextjs';
|
|
2242
|
+
if (deps.react && !deps.next) return 'react';
|
|
2243
|
+
if (deps.express) return 'express';
|
|
2244
|
+
if (deps.vue) return 'vue';
|
|
2245
|
+
if (deps.angular) return 'angular';
|
|
2246
|
+
|
|
2247
|
+
// If has package.json but no recognized framework
|
|
2248
|
+
return 'node';
|
|
2249
|
+
} catch {
|
|
2250
|
+
// No package.json, check for other files
|
|
2251
|
+
}
|
|
2252
|
+
|
|
2253
|
+
// Check for Python
|
|
2254
|
+
try {
|
|
2255
|
+
await fs.access(path.join(localPath, 'requirements.txt'));
|
|
2256
|
+
const content = await fs.readFile(path.join(localPath, 'requirements.txt'), 'utf8');
|
|
2257
|
+
if (content.includes('django')) return 'django';
|
|
2258
|
+
if (content.includes('flask')) return 'flask';
|
|
2259
|
+
return 'python';
|
|
2260
|
+
} catch {}
|
|
2261
|
+
|
|
2262
|
+
// Check for Go
|
|
2263
|
+
try {
|
|
2264
|
+
await fs.access(path.join(localPath, 'go.mod'));
|
|
2265
|
+
return 'go';
|
|
2266
|
+
} catch {}
|
|
2267
|
+
|
|
2268
|
+
// Check for Rust
|
|
2269
|
+
try {
|
|
2270
|
+
await fs.access(path.join(localPath, 'Cargo.toml'));
|
|
2271
|
+
return 'rust';
|
|
2272
|
+
} catch {}
|
|
2273
|
+
|
|
2274
|
+
// Check for PHP
|
|
2275
|
+
try {
|
|
2276
|
+
await fs.access(path.join(localPath, 'composer.json'));
|
|
2277
|
+
return 'php';
|
|
2278
|
+
} catch {}
|
|
2279
|
+
|
|
2280
|
+
// Check for index.php
|
|
2281
|
+
try {
|
|
2282
|
+
await fs.access(path.join(localPath, 'index.php'));
|
|
2283
|
+
return 'php';
|
|
2284
|
+
} catch {}
|
|
2285
|
+
|
|
2286
|
+
// Default to static site
|
|
2287
|
+
return 'static';
|
|
2288
|
+
}
|
|
2289
|
+
|
|
2078
2290
|
// Tool implementation: Recover User
|
|
2079
2291
|
async function recoverUser(args) {
|
|
2080
2292
|
const { email, pin, new_password } = args;
|