@hypnosis/docker-mcp-server 1.0.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/LICENSE +21 -0
- package/README.md +242 -0
- package/dist/adapters/adapter-registry.d.ts +29 -0
- package/dist/adapters/adapter-registry.d.ts.map +1 -0
- package/dist/adapters/adapter-registry.js +47 -0
- package/dist/adapters/adapter-registry.js.map +1 -0
- package/dist/adapters/database-adapter.d.ts +33 -0
- package/dist/adapters/database-adapter.d.ts.map +1 -0
- package/dist/adapters/database-adapter.js +6 -0
- package/dist/adapters/database-adapter.js.map +1 -0
- package/dist/adapters/postgresql.d.ts +42 -0
- package/dist/adapters/postgresql.d.ts.map +1 -0
- package/dist/adapters/postgresql.js +202 -0
- package/dist/adapters/postgresql.js.map +1 -0
- package/dist/adapters/redis.d.ts +46 -0
- package/dist/adapters/redis.d.ts.map +1 -0
- package/dist/adapters/redis.js +202 -0
- package/dist/adapters/redis.js.map +1 -0
- package/dist/adapters/sqlite.d.ts +34 -0
- package/dist/adapters/sqlite.d.ts.map +1 -0
- package/dist/adapters/sqlite.js +126 -0
- package/dist/adapters/sqlite.js.map +1 -0
- package/dist/adapters/types.d.ts +53 -0
- package/dist/adapters/types.d.ts.map +1 -0
- package/dist/adapters/types.js +5 -0
- package/dist/adapters/types.js.map +1 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +319 -0
- package/dist/cli.js.map +1 -0
- package/dist/discovery/compose-parser.d.ts +35 -0
- package/dist/discovery/compose-parser.d.ts.map +1 -0
- package/dist/discovery/compose-parser.js +126 -0
- package/dist/discovery/compose-parser.js.map +1 -0
- package/dist/discovery/config-merger.d.ts +19 -0
- package/dist/discovery/config-merger.d.ts.map +1 -0
- package/dist/discovery/config-merger.js +60 -0
- package/dist/discovery/config-merger.js.map +1 -0
- package/dist/discovery/project-discovery.d.ts +53 -0
- package/dist/discovery/project-discovery.d.ts.map +1 -0
- package/dist/discovery/project-discovery.js +252 -0
- package/dist/discovery/project-discovery.js.map +1 -0
- package/dist/discovery/types.d.ts +47 -0
- package/dist/discovery/types.d.ts.map +1 -0
- package/dist/discovery/types.js +6 -0
- package/dist/discovery/types.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +109 -0
- package/dist/index.js.map +1 -0
- package/dist/managers/compose-manager.d.ts +30 -0
- package/dist/managers/compose-manager.d.ts.map +1 -0
- package/dist/managers/compose-manager.js +70 -0
- package/dist/managers/compose-manager.js.map +1 -0
- package/dist/managers/container-manager.d.ts +81 -0
- package/dist/managers/container-manager.d.ts.map +1 -0
- package/dist/managers/container-manager.js +278 -0
- package/dist/managers/container-manager.js.map +1 -0
- package/dist/managers/env-manager.d.ts +37 -0
- package/dist/managers/env-manager.d.ts.map +1 -0
- package/dist/managers/env-manager.js +124 -0
- package/dist/managers/env-manager.js.map +1 -0
- package/dist/security/sql-validator.d.ts +23 -0
- package/dist/security/sql-validator.d.ts.map +1 -0
- package/dist/security/sql-validator.js +44 -0
- package/dist/security/sql-validator.js.map +1 -0
- package/dist/tools/container-tools.d.ts +31 -0
- package/dist/tools/container-tools.d.ts.map +1 -0
- package/dist/tools/container-tools.js +366 -0
- package/dist/tools/container-tools.js.map +1 -0
- package/dist/tools/database-tools.d.ts +22 -0
- package/dist/tools/database-tools.d.ts.map +1 -0
- package/dist/tools/database-tools.js +264 -0
- package/dist/tools/database-tools.js.map +1 -0
- package/dist/tools/env-tools.d.ts +52 -0
- package/dist/tools/env-tools.d.ts.map +1 -0
- package/dist/tools/env-tools.js +318 -0
- package/dist/tools/env-tools.js.map +1 -0
- package/dist/tools/executor-tool.d.ts +18 -0
- package/dist/tools/executor-tool.d.ts.map +1 -0
- package/dist/tools/executor-tool.js +95 -0
- package/dist/tools/executor-tool.js.map +1 -0
- package/dist/tools/mcp-health-tool.d.ts +65 -0
- package/dist/tools/mcp-health-tool.d.ts.map +1 -0
- package/dist/tools/mcp-health-tool.js +126 -0
- package/dist/tools/mcp-health-tool.js.map +1 -0
- package/dist/utils/cache.d.ts +43 -0
- package/dist/utils/cache.d.ts.map +1 -0
- package/dist/utils/cache.js +77 -0
- package/dist/utils/cache.js.map +1 -0
- package/dist/utils/compose-exec.d.ts +13 -0
- package/dist/utils/compose-exec.d.ts.map +1 -0
- package/dist/utils/compose-exec.js +33 -0
- package/dist/utils/compose-exec.js.map +1 -0
- package/dist/utils/docker-client.d.ts +33 -0
- package/dist/utils/docker-client.d.ts.map +1 -0
- package/dist/utils/docker-client.js +59 -0
- package/dist/utils/docker-client.js.map +1 -0
- package/dist/utils/logger.d.ts +18 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +38 -0
- package/dist/utils/logger.js.map +1 -0
- package/package.json +65 -0
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Container Manager
|
|
3
|
+
* Управление Docker контейнерами
|
|
4
|
+
*/
|
|
5
|
+
import { logger } from '../utils/logger.js';
|
|
6
|
+
import { getDockerClient } from '../utils/docker-client.js';
|
|
7
|
+
export class ContainerManager {
|
|
8
|
+
docker;
|
|
9
|
+
constructor() {
|
|
10
|
+
const client = getDockerClient();
|
|
11
|
+
this.docker = client.getClient();
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Список контейнеров проекта
|
|
15
|
+
*/
|
|
16
|
+
async listContainers(projectName, composeFile, projectDir) {
|
|
17
|
+
logger.debug(`Listing containers for project: ${projectName}`);
|
|
18
|
+
// Вариант 1: Используем Docker Compose labels (прямой вызов Docker API)
|
|
19
|
+
try {
|
|
20
|
+
const containers = await this.docker.listContainers({
|
|
21
|
+
all: true,
|
|
22
|
+
filters: {
|
|
23
|
+
label: [`com.docker.compose.project=${projectName}`],
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
if (containers.length > 0) {
|
|
27
|
+
logger.debug(`Found ${containers.length} containers via Docker Compose labels`);
|
|
28
|
+
return containers.map((c) => this.mapContainerInfo(c, projectName));
|
|
29
|
+
}
|
|
30
|
+
logger.debug('No containers found via labels, trying fallback methods');
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
logger.debug('Failed to list containers via labels:', error);
|
|
34
|
+
}
|
|
35
|
+
// Fallback 1: Используем docker-compose ps CLI (если есть composeFile)
|
|
36
|
+
if (composeFile && projectDir) {
|
|
37
|
+
try {
|
|
38
|
+
const { ComposeExec } = await import('../utils/compose-exec.js');
|
|
39
|
+
const output = ComposeExec.run(composeFile, ['ps', '--format', 'json'], {
|
|
40
|
+
cwd: projectDir,
|
|
41
|
+
});
|
|
42
|
+
const composeContainers = output
|
|
43
|
+
.split('\n')
|
|
44
|
+
.filter(line => line.trim())
|
|
45
|
+
.map(line => {
|
|
46
|
+
try {
|
|
47
|
+
return JSON.parse(line);
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
})
|
|
53
|
+
.filter(c => c && c.Name);
|
|
54
|
+
if (composeContainers.length > 0) {
|
|
55
|
+
logger.debug(`Found ${composeContainers.length} containers via docker-compose ps`);
|
|
56
|
+
// Получаем полную информацию о контейнерах через Docker API
|
|
57
|
+
const containerNames = composeContainers.map(c => c.Name);
|
|
58
|
+
const allContainers = await this.docker.listContainers({ all: true });
|
|
59
|
+
const projectContainers = allContainers.filter(c => {
|
|
60
|
+
const name = c.Names[0]?.replace(/^\//, '') || '';
|
|
61
|
+
return containerNames.some(n => name === n || name.includes(n));
|
|
62
|
+
});
|
|
63
|
+
return projectContainers.map((c) => {
|
|
64
|
+
const composeInfo = composeContainers.find(cc => {
|
|
65
|
+
const name = c.Names[0]?.replace(/^\//, '') || '';
|
|
66
|
+
return name === cc.Name || name.includes(cc.Name);
|
|
67
|
+
});
|
|
68
|
+
return this.mapContainerInfo(c, projectName, composeInfo?.Service);
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
logger.debug('Failed to use docker-compose ps:', error);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// Fallback 2: Фильтр по имени проекта в названии контейнера
|
|
77
|
+
logger.debug('Using name-based filter as final fallback');
|
|
78
|
+
const containers = await this.docker.listContainers({
|
|
79
|
+
all: true,
|
|
80
|
+
filters: {
|
|
81
|
+
name: [projectName],
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
return containers.map((c) => this.mapContainerInfo(c, projectName));
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Запустить контейнер
|
|
88
|
+
*/
|
|
89
|
+
async startContainer(serviceName, projectName, composeFile, projectDir) {
|
|
90
|
+
const container = await this.findContainer(serviceName, projectName, composeFile, projectDir);
|
|
91
|
+
logger.info(`Starting container: ${serviceName}`);
|
|
92
|
+
await container.start();
|
|
93
|
+
logger.info(`Container ${serviceName} started successfully`);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Остановить контейнер
|
|
97
|
+
*/
|
|
98
|
+
async stopContainer(serviceName, projectName, timeout = 10, composeFile, projectDir) {
|
|
99
|
+
const container = await this.findContainer(serviceName, projectName, composeFile, projectDir);
|
|
100
|
+
logger.info(`Stopping container: ${serviceName}`);
|
|
101
|
+
await container.stop({ t: timeout });
|
|
102
|
+
logger.info(`Container ${serviceName} stopped successfully`);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Перезапустить контейнер
|
|
106
|
+
*/
|
|
107
|
+
async restartContainer(serviceName, projectName, timeout = 10, composeFile, projectDir) {
|
|
108
|
+
const container = await this.findContainer(serviceName, projectName, composeFile, projectDir);
|
|
109
|
+
logger.info(`Restarting container: ${serviceName}`);
|
|
110
|
+
await container.restart({ t: timeout });
|
|
111
|
+
logger.info(`Container ${serviceName} restarted successfully`);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Получить логи контейнера
|
|
115
|
+
* Возвращает string для обычного режима или stream для follow mode
|
|
116
|
+
*/
|
|
117
|
+
async getLogs(serviceName, projectName, options = {}, composeFile, projectDir) {
|
|
118
|
+
const container = await this.findContainer(serviceName, projectName, composeFile, projectDir);
|
|
119
|
+
logger.debug(`Getting logs for: ${serviceName}`, options);
|
|
120
|
+
// Если follow mode → возвращаем stream
|
|
121
|
+
if (options.follow) {
|
|
122
|
+
logger.debug('Returning stream for follow mode');
|
|
123
|
+
const logs = await container.logs({
|
|
124
|
+
stdout: true,
|
|
125
|
+
stderr: true,
|
|
126
|
+
tail: options.lines || 100,
|
|
127
|
+
follow: true,
|
|
128
|
+
timestamps: options.timestamps || false,
|
|
129
|
+
since: options.since,
|
|
130
|
+
});
|
|
131
|
+
return logs;
|
|
132
|
+
}
|
|
133
|
+
// Иначе → возвращаем string
|
|
134
|
+
const logs = await container.logs({
|
|
135
|
+
stdout: true,
|
|
136
|
+
stderr: true,
|
|
137
|
+
tail: options.lines || 100,
|
|
138
|
+
follow: false,
|
|
139
|
+
timestamps: options.timestamps || false,
|
|
140
|
+
since: options.since,
|
|
141
|
+
});
|
|
142
|
+
// logs теперь Buffer, конвертируем в string
|
|
143
|
+
return Buffer.isBuffer(logs) ? logs.toString('utf-8') : logs.toString('utf-8');
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Получить health status контейнера
|
|
147
|
+
*/
|
|
148
|
+
async getHealthStatus(serviceName, projectName, composeFile, projectDir) {
|
|
149
|
+
const container = await this.findContainer(serviceName, projectName, composeFile, projectDir);
|
|
150
|
+
try {
|
|
151
|
+
const info = await container.inspect();
|
|
152
|
+
// Если контейнер не running
|
|
153
|
+
if (info.State.Status !== 'running') {
|
|
154
|
+
return {
|
|
155
|
+
status: 'not_running',
|
|
156
|
+
checks: 0,
|
|
157
|
+
failures: 0,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
// Если healthcheck не определён
|
|
161
|
+
if (!info.State.Health) {
|
|
162
|
+
return {
|
|
163
|
+
status: 'none',
|
|
164
|
+
checks: 0,
|
|
165
|
+
failures: 0,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
// Healthcheck определён
|
|
169
|
+
const health = info.State.Health;
|
|
170
|
+
const status = health.Status;
|
|
171
|
+
return {
|
|
172
|
+
status: status || 'none',
|
|
173
|
+
checks: health.Log?.length || 0,
|
|
174
|
+
failures: health.FailingStreak || 0,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
logger.error(`Failed to get health status for ${serviceName}:`, error);
|
|
179
|
+
throw new Error(`Failed to inspect container: ${error.message}`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Выполнить команду в контейнере
|
|
184
|
+
*/
|
|
185
|
+
async exec(serviceName, projectName, command, options = {}, composeFile, projectDir) {
|
|
186
|
+
const container = await this.findContainer(serviceName, projectName, composeFile, projectDir);
|
|
187
|
+
logger.debug(`Executing in ${serviceName}:`, command.join(' '));
|
|
188
|
+
if (options.interactive) {
|
|
189
|
+
logger.debug('Interactive mode (TTY) enabled');
|
|
190
|
+
}
|
|
191
|
+
const exec = await container.exec({
|
|
192
|
+
Cmd: command,
|
|
193
|
+
AttachStdout: true,
|
|
194
|
+
AttachStderr: true,
|
|
195
|
+
AttachStdin: options.interactive || false,
|
|
196
|
+
Tty: options.interactive || false,
|
|
197
|
+
User: options.user,
|
|
198
|
+
WorkingDir: options.workdir,
|
|
199
|
+
Env: options.env,
|
|
200
|
+
});
|
|
201
|
+
const stream = await exec.start({
|
|
202
|
+
hijack: options.interactive || false,
|
|
203
|
+
stdin: options.interactive || false,
|
|
204
|
+
});
|
|
205
|
+
return new Promise((resolve, reject) => {
|
|
206
|
+
const chunks = [];
|
|
207
|
+
stream.on('data', (chunk) => chunks.push(chunk));
|
|
208
|
+
stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')));
|
|
209
|
+
stream.on('error', reject);
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Найти контейнер по имени сервиса
|
|
214
|
+
*/
|
|
215
|
+
async findContainer(serviceName, projectName, composeFile, projectDir) {
|
|
216
|
+
const containers = await this.listContainers(projectName, composeFile, projectDir);
|
|
217
|
+
const found = containers.find((c) => c.service === serviceName);
|
|
218
|
+
if (!found) {
|
|
219
|
+
const available = containers.map((c) => c.service).join(', ');
|
|
220
|
+
throw new Error(`Container '${serviceName}' not found in project '${projectName}'.\n` +
|
|
221
|
+
`Available containers: ${available || 'none'}`);
|
|
222
|
+
}
|
|
223
|
+
return this.docker.getContainer(found.id);
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Маппинг Docker ContainerInfo → наш формат
|
|
227
|
+
*/
|
|
228
|
+
mapContainerInfo(container, projectName, serviceNameOverride) {
|
|
229
|
+
// Имя контейнера: /project_service_1 → извлекаем service
|
|
230
|
+
const fullName = container.Names[0]?.replace(/^\//, '') || '';
|
|
231
|
+
// Приоритет: override → label → извлечение из имени
|
|
232
|
+
const serviceName = serviceNameOverride
|
|
233
|
+
|| container.Labels?.['com.docker.compose.service']
|
|
234
|
+
|| this.extractServiceName(fullName, projectName);
|
|
235
|
+
return {
|
|
236
|
+
id: container.Id,
|
|
237
|
+
name: fullName,
|
|
238
|
+
service: serviceName,
|
|
239
|
+
status: container.State,
|
|
240
|
+
image: container.Image,
|
|
241
|
+
ports: this.extractPorts(container.Ports),
|
|
242
|
+
created: new Date(container.Created * 1000).toISOString(),
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Извлекает порты из Docker container info
|
|
247
|
+
*/
|
|
248
|
+
extractPorts(ports) {
|
|
249
|
+
if (!ports || ports.length === 0)
|
|
250
|
+
return [];
|
|
251
|
+
return ports.map((p) => {
|
|
252
|
+
if (p.PublicPort && p.PrivatePort) {
|
|
253
|
+
return `${p.PublicPort}:${p.PrivatePort}`;
|
|
254
|
+
}
|
|
255
|
+
if (p.PrivatePort) {
|
|
256
|
+
return `${p.PrivatePort}/${p.Type || 'tcp'}`;
|
|
257
|
+
}
|
|
258
|
+
return '';
|
|
259
|
+
}).filter((p) => p.length > 0);
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Извлекает имя сервиса из имени контейнера
|
|
263
|
+
* project_service_1 → service
|
|
264
|
+
* project-service-1 → service
|
|
265
|
+
*/
|
|
266
|
+
extractServiceName(containerName, projectName) {
|
|
267
|
+
// Убираем project name из начала
|
|
268
|
+
let name = containerName;
|
|
269
|
+
if (name.startsWith(projectName)) {
|
|
270
|
+
name = name.slice(projectName.length);
|
|
271
|
+
}
|
|
272
|
+
// Убираем разделители и номер в конце
|
|
273
|
+
// project_service_1 → _service_1 → service_1 → service
|
|
274
|
+
name = name.replace(/^[_-]/, '').replace(/[_-]\d+$/, '');
|
|
275
|
+
return name || containerName; // Fallback на полное имя
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
//# sourceMappingURL=container-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"container-manager.js","sourceRoot":"","sources":["../../src/managers/container-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Environment Manager
|
|
3
|
+
* Централизованное управление environment variables
|
|
4
|
+
*/
|
|
5
|
+
import type { ServiceConfig } from '../discovery/types.js';
|
|
6
|
+
export declare class EnvManager {
|
|
7
|
+
/**
|
|
8
|
+
* Загрузить environment variables из всех источников
|
|
9
|
+
* Приоритет: .env.local > .env.{NODE_ENV} > .env > compose environment
|
|
10
|
+
*/
|
|
11
|
+
loadEnv(projectDir: string, serviceName?: string, serviceConfig?: ServiceConfig): Record<string, string>;
|
|
12
|
+
/**
|
|
13
|
+
* Получить список .env файлов (в порядке приоритета)
|
|
14
|
+
* Приоритет: .env.local (highest) > .env.{NODE_ENV} > .env (base)
|
|
15
|
+
*/
|
|
16
|
+
private getEnvFiles;
|
|
17
|
+
/**
|
|
18
|
+
* Парсит .env файл
|
|
19
|
+
*/
|
|
20
|
+
private parseEnvFile;
|
|
21
|
+
/**
|
|
22
|
+
* Маскировать секреты в environment variables
|
|
23
|
+
*/
|
|
24
|
+
maskSecrets(env: Record<string, string>): Record<string, string>;
|
|
25
|
+
/**
|
|
26
|
+
* Получить connection info для БД из environment
|
|
27
|
+
* Используется Database Adapters
|
|
28
|
+
*/
|
|
29
|
+
getConnectionInfo(serviceConfig: ServiceConfig, env: Record<string, string>): {
|
|
30
|
+
host: string;
|
|
31
|
+
port: number;
|
|
32
|
+
user: string;
|
|
33
|
+
password?: string;
|
|
34
|
+
database: string;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=env-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env-manager.d.ts","sourceRoot":"","sources":["../../src/managers/env-manager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAE3D,qBAAa,UAAU;IACrB;;;OAGG;IACH,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAuBxG;;;OAGG;IACH,OAAO,CAAC,WAAW;IAUnB;;OAEG;IACH,OAAO,CAAC,YAAY;IAUpB;;OAEG;IACH,WAAW,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAwBhE;;;OAGG;IACH,iBAAiB,CACf,aAAa,EAAE,aAAa,EAC5B,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC1B;QACD,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,MAAM,CAAC;KAClB;CA8CF"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Environment Manager
|
|
3
|
+
* Централизованное управление environment variables
|
|
4
|
+
*/
|
|
5
|
+
import { existsSync, readFileSync } from 'fs';
|
|
6
|
+
import { join } from 'path';
|
|
7
|
+
import { parse as parseDotenv } from 'dotenv';
|
|
8
|
+
import { logger } from '../utils/logger.js';
|
|
9
|
+
export class EnvManager {
|
|
10
|
+
/**
|
|
11
|
+
* Загрузить environment variables из всех источников
|
|
12
|
+
* Приоритет: .env.local > .env.{NODE_ENV} > .env > compose environment
|
|
13
|
+
*/
|
|
14
|
+
loadEnv(projectDir, serviceName, serviceConfig) {
|
|
15
|
+
const env = {};
|
|
16
|
+
// 1. Загрузить .env файлы (в порядке приоритета)
|
|
17
|
+
const envFiles = this.getEnvFiles(projectDir);
|
|
18
|
+
for (const file of envFiles) {
|
|
19
|
+
if (existsSync(file)) {
|
|
20
|
+
const parsed = this.parseEnvFile(file);
|
|
21
|
+
Object.assign(env, parsed);
|
|
22
|
+
logger.debug(`Loaded env from: ${file}`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
// 2. Если serviceName и serviceConfig указаны → загрузить из compose
|
|
26
|
+
if (serviceName && serviceConfig?.environment) {
|
|
27
|
+
Object.assign(env, serviceConfig.environment);
|
|
28
|
+
logger.debug(`Loaded env from compose for service: ${serviceName}`);
|
|
29
|
+
}
|
|
30
|
+
return env;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Получить список .env файлов (в порядке приоритета)
|
|
34
|
+
* Приоритет: .env.local (highest) > .env.{NODE_ENV} > .env (base)
|
|
35
|
+
*/
|
|
36
|
+
getEnvFiles(projectDir) {
|
|
37
|
+
const nodeEnv = process.env.NODE_ENV || 'development';
|
|
38
|
+
return [
|
|
39
|
+
join(projectDir, '.env'), // Base (lowest priority)
|
|
40
|
+
join(projectDir, `.env.${nodeEnv}`), // Environment-specific
|
|
41
|
+
join(projectDir, '.env.local'), // Local (highest priority)
|
|
42
|
+
];
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Парсит .env файл
|
|
46
|
+
*/
|
|
47
|
+
parseEnvFile(filePath) {
|
|
48
|
+
try {
|
|
49
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
50
|
+
return parseDotenv(content);
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
logger.warn(`Failed to parse env file ${filePath}:`, error.message);
|
|
54
|
+
return {};
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Маскировать секреты в environment variables
|
|
59
|
+
*/
|
|
60
|
+
maskSecrets(env) {
|
|
61
|
+
const SECRET_KEYWORDS = [
|
|
62
|
+
'PASSWORD',
|
|
63
|
+
'TOKEN',
|
|
64
|
+
'KEY',
|
|
65
|
+
'SECRET',
|
|
66
|
+
'API_KEY',
|
|
67
|
+
'PRIVATE',
|
|
68
|
+
'CREDENTIALS',
|
|
69
|
+
];
|
|
70
|
+
const masked = {};
|
|
71
|
+
for (const [key, value] of Object.entries(env)) {
|
|
72
|
+
const isSecret = SECRET_KEYWORDS.some((keyword) => key.toUpperCase().includes(keyword));
|
|
73
|
+
masked[key] = isSecret ? '***MASKED***' : value;
|
|
74
|
+
}
|
|
75
|
+
return masked;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Получить connection info для БД из environment
|
|
79
|
+
* Используется Database Adapters
|
|
80
|
+
*/
|
|
81
|
+
getConnectionInfo(serviceConfig, env) {
|
|
82
|
+
// Базовые значения
|
|
83
|
+
const host = 'localhost';
|
|
84
|
+
const port = 0;
|
|
85
|
+
const user = '';
|
|
86
|
+
const password = undefined;
|
|
87
|
+
const database = '';
|
|
88
|
+
// Тип БД определяет какие переменные искать
|
|
89
|
+
switch (serviceConfig.type) {
|
|
90
|
+
case 'postgresql':
|
|
91
|
+
return {
|
|
92
|
+
host,
|
|
93
|
+
port: 5432,
|
|
94
|
+
user: env.POSTGRES_USER || 'postgres',
|
|
95
|
+
password: env.POSTGRES_PASSWORD,
|
|
96
|
+
database: env.POSTGRES_DB || 'postgres',
|
|
97
|
+
};
|
|
98
|
+
case 'redis':
|
|
99
|
+
return {
|
|
100
|
+
host,
|
|
101
|
+
port: 6379,
|
|
102
|
+
user: '',
|
|
103
|
+
password: env.REDIS_PASSWORD,
|
|
104
|
+
database: '0',
|
|
105
|
+
};
|
|
106
|
+
case 'sqlite':
|
|
107
|
+
return {
|
|
108
|
+
host: 'localhost',
|
|
109
|
+
port: 0,
|
|
110
|
+
user: '',
|
|
111
|
+
database: env.SQLITE_DATABASE || '/app/db.sqlite3',
|
|
112
|
+
};
|
|
113
|
+
default:
|
|
114
|
+
return {
|
|
115
|
+
host,
|
|
116
|
+
port,
|
|
117
|
+
user,
|
|
118
|
+
password,
|
|
119
|
+
database,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=env-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env-manager.js","sourceRoot":"","sources":["../../src/managers/env-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SQL Validator
|
|
3
|
+
* Валидация опасных SQL команд для защиты от случайных удалений
|
|
4
|
+
*/
|
|
5
|
+
export declare class SQLValidator {
|
|
6
|
+
private dangerousPatterns;
|
|
7
|
+
/**
|
|
8
|
+
* Валидировать SQL запрос
|
|
9
|
+
* Бросает ошибку если опасный
|
|
10
|
+
*/
|
|
11
|
+
validate(sql: string): void;
|
|
12
|
+
/**
|
|
13
|
+
* Проверить включена ли валидация
|
|
14
|
+
* По умолчанию: ВКЛ (защита)
|
|
15
|
+
* Отключение: DOCKER_MCP_VALIDATE_SQL=false
|
|
16
|
+
*/
|
|
17
|
+
static isEnabled(): boolean;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Singleton instance
|
|
21
|
+
*/
|
|
22
|
+
export declare const sqlValidator: SQLValidator;
|
|
23
|
+
//# sourceMappingURL=sql-validator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sql-validator.d.ts","sourceRoot":"","sources":["../../src/security/sql-validator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,qBAAa,YAAY;IACvB,OAAO,CAAC,iBAAiB,CAMvB;IAEF;;;OAGG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAkB3B;;;;OAIG;IACH,MAAM,CAAC,SAAS,IAAI,OAAO;CAG5B;AAED;;GAEG;AACH,eAAO,MAAM,YAAY,cAAqB,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SQL Validator
|
|
3
|
+
* Валидация опасных SQL команд для защиты от случайных удалений
|
|
4
|
+
*/
|
|
5
|
+
export class SQLValidator {
|
|
6
|
+
dangerousPatterns = [
|
|
7
|
+
/DROP\s+DATABASE/i,
|
|
8
|
+
/DROP\s+TABLE/i,
|
|
9
|
+
/TRUNCATE\s+TABLE/i,
|
|
10
|
+
/DELETE\s+FROM\s+\w+\s*;/i, // DELETE без WHERE
|
|
11
|
+
/UPDATE\s+\w+\s+SET\s+.*\s*;/i, // UPDATE без WHERE (упрощенная проверка)
|
|
12
|
+
];
|
|
13
|
+
/**
|
|
14
|
+
* Валидировать SQL запрос
|
|
15
|
+
* Бросает ошибку если опасный
|
|
16
|
+
*/
|
|
17
|
+
validate(sql) {
|
|
18
|
+
// Проверяем включена ли валидация
|
|
19
|
+
if (!SQLValidator.isEnabled()) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
for (const pattern of this.dangerousPatterns) {
|
|
23
|
+
if (pattern.test(sql)) {
|
|
24
|
+
throw new Error(`🚨 DANGEROUS SQL DETECTED: This query may cause data loss.\n` +
|
|
25
|
+
`Pattern: ${pattern.source}\n` +
|
|
26
|
+
`Query: ${sql}\n\n` +
|
|
27
|
+
`If you're sure, disable validation: DOCKER_MCP_VALIDATE_SQL=false`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Проверить включена ли валидация
|
|
33
|
+
* По умолчанию: ВКЛ (защита)
|
|
34
|
+
* Отключение: DOCKER_MCP_VALIDATE_SQL=false
|
|
35
|
+
*/
|
|
36
|
+
static isEnabled() {
|
|
37
|
+
return process.env.DOCKER_MCP_VALIDATE_SQL !== 'false';
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Singleton instance
|
|
42
|
+
*/
|
|
43
|
+
export const sqlValidator = new SQLValidator();
|
|
44
|
+
//# sourceMappingURL=sql-validator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sql-validator.js","sourceRoot":"","sources":["../../src/security/sql-validator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,OAAO,YAAY;IACf,iBAAiB,GAAG;QAC1B,kBAAkB;QAClB,eAAe;QACf,mBAAmB;QACnB,0BAA0B,EAAE,mBAAmB;QAC/C,8BAA8B,EAAE,yCAAyC;KAC1E,CAAC;IAEF;;;OAGG;IACH,QAAQ,CAAC,GAAW;QAClB,kCAAkC;QAClC,IAAI,CAAC,YAAY,CAAC,SAAS,EAAE,EAAE,CAAC;YAC9B,OAAO;QACT,CAAC;QAED,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,iBAAiB,EAAE,CAAC;YAC7C,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;gBACtB,MAAM,IAAI,KAAK,CACb,8DAA8D;oBAC9D,YAAY,OAAO,CAAC,MAAM,IAAI;oBAC9B,UAAU,GAAG,MAAM;oBACnB,mEAAmE,CACpE,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,SAAS;QACd,OAAO,OAAO,CAAC,GAAG,CAAC,uBAAuB,KAAK,OAAO,CAAC;IACzD,CAAC;CACF;AAED;;GAEG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,YAAY,EAAE,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Container Tools
|
|
3
|
+
* MCP Tools для управления Docker контейнерами
|
|
4
|
+
*/
|
|
5
|
+
import { CallToolRequest, Tool } from '@modelcontextprotocol/sdk/types.js';
|
|
6
|
+
export declare class ContainerTools {
|
|
7
|
+
private containerManager;
|
|
8
|
+
private composeManager;
|
|
9
|
+
private projectDiscovery;
|
|
10
|
+
constructor();
|
|
11
|
+
/**
|
|
12
|
+
* Регистрация всех container tools
|
|
13
|
+
*/
|
|
14
|
+
getTools(): Tool[];
|
|
15
|
+
/**
|
|
16
|
+
* Обработка вызова tool
|
|
17
|
+
*/
|
|
18
|
+
handleCall(request: CallToolRequest): Promise<any>;
|
|
19
|
+
private handleList;
|
|
20
|
+
private handleStart;
|
|
21
|
+
private handleStop;
|
|
22
|
+
private handleRestart;
|
|
23
|
+
private handleLogs;
|
|
24
|
+
private handleComposeUp;
|
|
25
|
+
private handleComposeDown;
|
|
26
|
+
/**
|
|
27
|
+
* Helper: получить project config (auto-detect или explicit)
|
|
28
|
+
*/
|
|
29
|
+
private getProject;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=container-tools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"container-tools.d.ts","sourceRoot":"","sources":["../../src/tools/container-tools.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,eAAe,EACf,IAAI,EACL,MAAM,oCAAoC,CAAC;AAM5C,qBAAa,cAAc;IACzB,OAAO,CAAC,gBAAgB,CAAmB;IAC3C,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,gBAAgB,CAAmB;;IAQ3C;;OAEG;IACH,QAAQ,IAAI,IAAI,EAAE;IAuKlB;;OAEG;IACG,UAAU,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,GAAG,CAAC;YA2C1C,UAAU;YAcV,WAAW;YAkBX,UAAU;YAwBV,aAAa;YAwBb,UAAU;YAyDV,eAAe;YAkBf,iBAAiB;IAiB/B;;OAEG;YACW,UAAU;CAKzB"}
|