@hypnosis/docker-mcp-server 1.0.3 → 1.2.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/README.md +95 -22
- package/dist/adapters/redis.d.ts.map +1 -1
- package/dist/adapters/redis.js +18 -6
- package/dist/adapters/redis.js.map +1 -1
- package/dist/discovery/compose-parser.d.ts +4 -0
- package/dist/discovery/compose-parser.d.ts.map +1 -1
- package/dist/discovery/compose-parser.js +25 -0
- package/dist/discovery/compose-parser.js.map +1 -1
- package/dist/discovery/project-discovery.d.ts.map +1 -1
- package/dist/discovery/project-discovery.js +24 -0
- package/dist/discovery/project-discovery.js.map +1 -1
- package/dist/discovery/remote-discovery.d.ts +99 -0
- package/dist/discovery/remote-discovery.d.ts.map +1 -0
- package/dist/discovery/remote-discovery.js +410 -0
- package/dist/discovery/remote-discovery.js.map +1 -0
- package/dist/discovery/types.d.ts +4 -0
- package/dist/discovery/types.d.ts.map +1 -1
- package/dist/index.js +87 -14
- package/dist/index.js.map +1 -1
- package/dist/managers/compose-manager.d.ts +3 -1
- package/dist/managers/compose-manager.d.ts.map +1 -1
- package/dist/managers/compose-manager.js +10 -1
- package/dist/managers/compose-manager.js.map +1 -1
- package/dist/managers/container-manager.d.ts +53 -1
- package/dist/managers/container-manager.d.ts.map +1 -1
- package/dist/managers/container-manager.js +175 -71
- package/dist/managers/container-manager.js.map +1 -1
- package/dist/tools/container-tools.d.ts +9 -1
- package/dist/tools/container-tools.d.ts.map +1 -1
- package/dist/tools/container-tools.js +231 -14
- package/dist/tools/container-tools.js.map +1 -1
- package/dist/tools/database-tools.d.ts.map +1 -1
- package/dist/tools/database-tools.js +36 -4
- package/dist/tools/database-tools.js.map +1 -1
- package/dist/tools/discovery-tools.d.ts +29 -0
- package/dist/tools/discovery-tools.d.ts.map +1 -0
- package/dist/tools/discovery-tools.js +173 -0
- package/dist/tools/discovery-tools.js.map +1 -0
- package/dist/tools/env-tools.d.ts +5 -0
- package/dist/tools/env-tools.d.ts.map +1 -1
- package/dist/tools/env-tools.js +158 -15
- package/dist/tools/env-tools.js.map +1 -1
- package/dist/tools/executor-tool.d.ts +5 -0
- package/dist/tools/executor-tool.d.ts.map +1 -1
- package/dist/tools/executor-tool.js +60 -5
- package/dist/tools/executor-tool.js.map +1 -1
- package/dist/tools/mcp-health-tool.d.ts +11 -0
- package/dist/tools/mcp-health-tool.d.ts.map +1 -1
- package/dist/tools/mcp-health-tool.js +25 -4
- package/dist/tools/mcp-health-tool.js.map +1 -1
- package/dist/tools/profile-tool.d.ts +46 -0
- package/dist/tools/profile-tool.d.ts.map +1 -0
- package/dist/tools/profile-tool.js +91 -0
- package/dist/tools/profile-tool.js.map +1 -0
- package/dist/utils/compose-exec.d.ts +28 -3
- package/dist/utils/compose-exec.d.ts.map +1 -1
- package/dist/utils/compose-exec.js +100 -26
- package/dist/utils/compose-exec.js.map +1 -1
- package/dist/utils/docker-client.d.ts +73 -8
- package/dist/utils/docker-client.d.ts.map +1 -1
- package/dist/utils/docker-client.js +492 -16
- package/dist/utils/docker-client.js.map +1 -1
- package/dist/utils/profiles-file.d.ts +57 -0
- package/dist/utils/profiles-file.d.ts.map +1 -0
- package/dist/utils/profiles-file.js +167 -0
- package/dist/utils/profiles-file.js.map +1 -0
- package/dist/utils/retry.d.ts +49 -0
- package/dist/utils/retry.d.ts.map +1 -0
- package/dist/utils/retry.js +120 -0
- package/dist/utils/retry.js.map +1 -0
- package/dist/utils/ssh-config.d.ts +104 -0
- package/dist/utils/ssh-config.d.ts.map +1 -0
- package/dist/utils/ssh-config.js +346 -0
- package/dist/utils/ssh-config.js.map +1 -0
- package/dist/utils/ssh-exec.d.ts +59 -0
- package/dist/utils/ssh-exec.d.ts.map +1 -0
- package/dist/utils/ssh-exec.js +156 -0
- package/dist/utils/ssh-exec.js.map +1 -0
- package/package.json +21 -3
|
@@ -1,45 +1,369 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Docker Client Wrapper
|
|
3
|
-
*
|
|
3
|
+
* Centralized Docker API management with SSH tunnel support
|
|
4
4
|
*/
|
|
5
5
|
import Docker from 'dockerode';
|
|
6
|
+
import { existsSync, unlinkSync } from 'fs';
|
|
7
|
+
import { resolve, join } from 'path';
|
|
8
|
+
import { tmpdir } from 'os';
|
|
9
|
+
import { spawn } from 'child_process';
|
|
6
10
|
import { logger } from './logger.js';
|
|
11
|
+
import { retryWithTimeout, createNetworkRetryPredicate } from './retry.js';
|
|
12
|
+
import { loadProfilesFile, profileDataToSSHConfig } from './profiles-file.js';
|
|
7
13
|
/**
|
|
8
|
-
*
|
|
14
|
+
* Dockerode wrapper for centralized Docker API management
|
|
9
15
|
*/
|
|
10
16
|
export class DockerClient {
|
|
11
17
|
docker;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
18
|
+
sshConfig;
|
|
19
|
+
isRemote;
|
|
20
|
+
activeSocketPath = null;
|
|
21
|
+
sshProcessPid = null;
|
|
22
|
+
tunnelCreationLock = null;
|
|
23
|
+
tunnelHealthCheckInterval = null;
|
|
24
|
+
constructor(sshConfig) {
|
|
25
|
+
this.sshConfig = sshConfig || null;
|
|
26
|
+
this.isRemote = !!this.sshConfig;
|
|
27
|
+
if (this.sshConfig) {
|
|
28
|
+
// For SSH, create temporary Docker client, tunnel will be created on first use
|
|
29
|
+
// Use local socket by default, will be replaced after tunnel creation
|
|
30
|
+
this.docker = new Docker();
|
|
31
|
+
logger.info(`Dockerode client initialized with SSH config (${this.sshConfig.host}:${this.sshConfig.port || 22})`);
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
// Local Docker client
|
|
35
|
+
this.docker = new Docker();
|
|
36
|
+
logger.debug('Dockerode client initialized (local)');
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Cleanup SSH tunnel (called on shutdown)
|
|
41
|
+
*/
|
|
42
|
+
cleanup() {
|
|
43
|
+
// Stop healthcheck interval
|
|
44
|
+
if (this.tunnelHealthCheckInterval) {
|
|
45
|
+
clearInterval(this.tunnelHealthCheckInterval);
|
|
46
|
+
this.tunnelHealthCheckInterval = null;
|
|
47
|
+
}
|
|
48
|
+
if (!this.isRemote || !this.activeSocketPath) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
logger.info('Cleaning up SSH tunnel...');
|
|
52
|
+
// Remove socket file
|
|
53
|
+
try {
|
|
54
|
+
if (existsSync(this.activeSocketPath)) {
|
|
55
|
+
unlinkSync(this.activeSocketPath);
|
|
56
|
+
logger.debug(`Removed SSH socket: ${this.activeSocketPath}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
logger.warn(`Failed to remove SSH socket: ${error.message}`);
|
|
61
|
+
}
|
|
62
|
+
// Kill SSH process if we have PID
|
|
63
|
+
if (this.sshProcessPid) {
|
|
64
|
+
try {
|
|
65
|
+
process.kill(this.sshProcessPid, 'SIGTERM');
|
|
66
|
+
logger.debug(`Killed SSH process: ${this.sshProcessPid}`);
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
// Process might already be dead
|
|
70
|
+
logger.debug(`SSH process already terminated: ${this.sshProcessPid}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
this.activeSocketPath = null;
|
|
74
|
+
this.sshProcessPid = null;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Ensure SSH tunnel is created and recreate Docker client with correct socket
|
|
78
|
+
* With mutex to prevent race conditions
|
|
79
|
+
*/
|
|
80
|
+
async ensureSSHTunnel() {
|
|
81
|
+
if (!this.sshConfig) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
// If tunnel creation is already in progress, wait for it
|
|
85
|
+
if (this.tunnelCreationLock) {
|
|
86
|
+
logger.debug('Tunnel creation already in progress, waiting...');
|
|
87
|
+
await this.tunnelCreationLock;
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
logger.info(`Ensuring SSH tunnel is created for ${this.sshConfig.host}:${this.sshConfig.port || 22}`);
|
|
91
|
+
// Create lock and start tunnel creation
|
|
92
|
+
this.tunnelCreationLock = (async () => {
|
|
93
|
+
try {
|
|
94
|
+
const socketPath = await this.createSSHTunnel(this.sshConfig);
|
|
95
|
+
// Store active socket path for cleanup
|
|
96
|
+
this.activeSocketPath = socketPath;
|
|
97
|
+
// Recreate Docker client with correct socket
|
|
98
|
+
this.docker = new Docker({
|
|
99
|
+
socketPath: socketPath,
|
|
100
|
+
});
|
|
101
|
+
logger.info(`Docker client updated with SSH tunnel socket: ${socketPath}`);
|
|
102
|
+
// Start periodic healthcheck for tunnel
|
|
103
|
+
this.startTunnelHealthCheck();
|
|
104
|
+
return socketPath;
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
logger.error(`Failed to ensure SSH tunnel: ${error.message}`);
|
|
108
|
+
throw error;
|
|
109
|
+
}
|
|
110
|
+
finally {
|
|
111
|
+
// Release lock
|
|
112
|
+
this.tunnelCreationLock = null;
|
|
113
|
+
}
|
|
114
|
+
})();
|
|
115
|
+
await this.tunnelCreationLock;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Start periodic healthcheck for SSH tunnel
|
|
119
|
+
*/
|
|
120
|
+
startTunnelHealthCheck() {
|
|
121
|
+
// Clear existing interval if any
|
|
122
|
+
if (this.tunnelHealthCheckInterval) {
|
|
123
|
+
clearInterval(this.tunnelHealthCheckInterval);
|
|
124
|
+
}
|
|
125
|
+
// Check tunnel every 60 seconds
|
|
126
|
+
this.tunnelHealthCheckInterval = setInterval(async () => {
|
|
127
|
+
if (!this.activeSocketPath || !this.isRemote) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
try {
|
|
131
|
+
// Quick ping to check if Docker is reachable
|
|
132
|
+
await this.docker.ping();
|
|
133
|
+
logger.debug('SSH tunnel healthcheck: OK');
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
logger.warn(`SSH tunnel healthcheck failed: ${error.message}`);
|
|
137
|
+
logger.info('Attempting to recreate SSH tunnel...');
|
|
138
|
+
// Try to recreate tunnel
|
|
139
|
+
try {
|
|
140
|
+
await this.ensureSSHTunnel();
|
|
141
|
+
logger.info('SSH tunnel recreated successfully');
|
|
142
|
+
}
|
|
143
|
+
catch (recreateError) {
|
|
144
|
+
logger.error(`Failed to recreate SSH tunnel: ${recreateError.message}`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}, 60000); // 60 seconds
|
|
148
|
+
// Unref the interval so it doesn't keep the process alive
|
|
149
|
+
this.tunnelHealthCheckInterval.unref();
|
|
150
|
+
logger.debug('SSH tunnel healthcheck started (60s interval)');
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Create SSH tunnel to remote Docker socket
|
|
154
|
+
* @returns Path to local socket file
|
|
155
|
+
*/
|
|
156
|
+
async createSSHTunnel(config) {
|
|
157
|
+
// Check platform support for Unix sockets
|
|
158
|
+
if (process.platform === 'win32') {
|
|
159
|
+
throw new Error('SSH tunneling with Unix sockets is not supported on Windows. Please use WSL2 or Docker Desktop.');
|
|
160
|
+
}
|
|
161
|
+
// Unique name for socket file
|
|
162
|
+
const socketPath = join(tmpdir(), `docker-ssh-${config.host}-${config.port || 22}.sock`);
|
|
163
|
+
// Check if tunnel is already running
|
|
164
|
+
if (existsSync(socketPath)) {
|
|
165
|
+
// Проверяем, работает ли туннель — пробуем подключиться
|
|
166
|
+
logger.debug(`SSH tunnel socket exists: ${socketPath}, checking if it's alive...`);
|
|
167
|
+
try {
|
|
168
|
+
// Try quick ping through existing socket
|
|
169
|
+
const testDocker = new Docker({ socketPath });
|
|
170
|
+
await testDocker.ping();
|
|
171
|
+
logger.debug(`SSH tunnel socket is alive: ${socketPath}`);
|
|
172
|
+
return socketPath;
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
// Socket file exists but tunnel is dead - remove and recreate
|
|
176
|
+
logger.warn(`SSH tunnel socket exists but is dead, recreating: ${socketPath}`);
|
|
177
|
+
try {
|
|
178
|
+
unlinkSync(socketPath);
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
// Ignore deletion error
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
// Check SSH key existence (if specified)
|
|
186
|
+
if (config.privateKeyPath) {
|
|
187
|
+
const keyPath = this.resolveKeyPath(config.privateKeyPath);
|
|
188
|
+
if (!existsSync(keyPath)) {
|
|
189
|
+
logger.warn(`SSH private key not found at: ${keyPath}. Make sure it's accessible via SSH agent or ~/.ssh/config`);
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
logger.debug(`SSH key found at: ${keyPath}`);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
// Build SSH command for tunnel creation
|
|
196
|
+
const sshArgs = [
|
|
197
|
+
'-N', // Don't execute commands, tunnel only
|
|
198
|
+
'-f', // Background mode
|
|
199
|
+
'-o', 'StrictHostKeyChecking=no',
|
|
200
|
+
'-o', 'UserKnownHostsFile=/dev/null',
|
|
201
|
+
'-o', 'ExitOnForwardFailure=yes',
|
|
202
|
+
'-o', 'ServerAliveInterval=60',
|
|
203
|
+
'-o', 'ServerAliveCountMax=3',
|
|
204
|
+
'-L', `${socketPath}:/var/run/docker.sock`, // Local socket -> remote socket
|
|
205
|
+
];
|
|
206
|
+
// Add key if specified
|
|
207
|
+
if (config.privateKeyPath) {
|
|
208
|
+
const keyPath = this.resolveKeyPath(config.privateKeyPath);
|
|
209
|
+
sshArgs.push('-i', keyPath);
|
|
210
|
+
}
|
|
211
|
+
// Add port and host
|
|
212
|
+
if (config.port && config.port !== 22) {
|
|
213
|
+
sshArgs.push('-p', String(config.port));
|
|
214
|
+
}
|
|
215
|
+
sshArgs.push(`${config.username}@${config.host}`);
|
|
216
|
+
// Start SSH tunnel
|
|
217
|
+
logger.info(`Creating SSH tunnel: ${config.username}@${config.host}:${config.port || 22}`);
|
|
218
|
+
return new Promise((resolve, reject) => {
|
|
219
|
+
try {
|
|
220
|
+
logger.debug(`Executing SSH command: ssh ${sshArgs.join(' ')}`);
|
|
221
|
+
const sshProcess = spawn('ssh', sshArgs, {
|
|
222
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
223
|
+
detached: true,
|
|
224
|
+
});
|
|
225
|
+
// Store PID for cleanup
|
|
226
|
+
if (sshProcess.pid) {
|
|
227
|
+
this.sshProcessPid = sshProcess.pid;
|
|
228
|
+
logger.debug(`SSH process PID: ${this.sshProcessPid}`);
|
|
229
|
+
}
|
|
230
|
+
let stderrOutput = '';
|
|
231
|
+
let stdoutOutput = '';
|
|
232
|
+
sshProcess.stdout?.on('data', (data) => {
|
|
233
|
+
stdoutOutput += data.toString();
|
|
234
|
+
});
|
|
235
|
+
sshProcess.stderr?.on('data', (data) => {
|
|
236
|
+
stderrOutput += data.toString();
|
|
237
|
+
// Логируем stderr для отладки
|
|
238
|
+
logger.debug(`SSH stderr: ${data.toString().trim()}`);
|
|
239
|
+
});
|
|
240
|
+
sshProcess.on('error', (error) => {
|
|
241
|
+
logger.error(`SSH tunnel process error: ${error.message}`);
|
|
242
|
+
reject(new Error(`SSH tunnel failed: ${error.message}`));
|
|
243
|
+
});
|
|
244
|
+
// With -f flag, process exits immediately after starting in background
|
|
245
|
+
// Wait a bit and check socket
|
|
246
|
+
setTimeout(() => {
|
|
247
|
+
sshProcess.unref();
|
|
248
|
+
// Wait for socket creation
|
|
249
|
+
this.waitForSocket(socketPath, 10000) // Increase timeout to 10 seconds
|
|
250
|
+
.then(() => {
|
|
251
|
+
logger.info(`SSH tunnel socket created successfully: ${socketPath}`);
|
|
252
|
+
// Store socket path for cleanup
|
|
253
|
+
this.activeSocketPath = socketPath;
|
|
254
|
+
resolve(socketPath);
|
|
255
|
+
})
|
|
256
|
+
.catch((error) => {
|
|
257
|
+
logger.error(`SSH tunnel socket not created: ${error.message}`);
|
|
258
|
+
if (stderrOutput) {
|
|
259
|
+
logger.error(`SSH stderr output: ${stderrOutput}`);
|
|
260
|
+
}
|
|
261
|
+
if (stdoutOutput) {
|
|
262
|
+
logger.error(`SSH stdout output: ${stdoutOutput}`);
|
|
263
|
+
}
|
|
264
|
+
reject(new Error(`SSH tunnel socket not created: ${error.message}`));
|
|
265
|
+
});
|
|
266
|
+
}, 500); // Give 500ms for process startup
|
|
267
|
+
}
|
|
268
|
+
catch (error) {
|
|
269
|
+
logger.error(`Failed to start SSH tunnel: ${error.message}`);
|
|
270
|
+
reject(new Error(`SSH tunnel failed: ${error.message}`));
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Wait for socket file creation
|
|
276
|
+
*/
|
|
277
|
+
async waitForSocket(socketPath, timeout) {
|
|
278
|
+
const startTime = Date.now();
|
|
279
|
+
const checkInterval = 100; // Проверяем каждые 100ms
|
|
280
|
+
return new Promise((resolve, reject) => {
|
|
281
|
+
const checkSocket = setInterval(() => {
|
|
282
|
+
if (existsSync(socketPath)) {
|
|
283
|
+
clearInterval(checkSocket);
|
|
284
|
+
resolve();
|
|
285
|
+
}
|
|
286
|
+
else if (Date.now() - startTime > timeout) {
|
|
287
|
+
clearInterval(checkSocket);
|
|
288
|
+
reject(new Error(`Socket not created within ${timeout}ms`));
|
|
289
|
+
}
|
|
290
|
+
}, checkInterval);
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Resolve SSH key path (support for ~)
|
|
295
|
+
*/
|
|
296
|
+
resolveKeyPath(keyPath) {
|
|
297
|
+
if (keyPath.startsWith('~')) {
|
|
298
|
+
const home = process.env.HOME || process.env.USERPROFILE || '';
|
|
299
|
+
return keyPath.replace('~', home);
|
|
300
|
+
}
|
|
301
|
+
return resolve(keyPath);
|
|
15
302
|
}
|
|
16
303
|
/**
|
|
17
304
|
* Проверка подключения к Docker
|
|
305
|
+
* С retry логикой для удаленных подключений
|
|
18
306
|
*/
|
|
19
307
|
async ping() {
|
|
20
|
-
|
|
308
|
+
// Для удаленных подключений сначала создаем туннель
|
|
309
|
+
if (this.isRemote) {
|
|
310
|
+
await this.ensureSSHTunnel();
|
|
311
|
+
}
|
|
312
|
+
const pingFn = async () => {
|
|
21
313
|
await this.docker.ping();
|
|
22
|
-
logger.info(
|
|
314
|
+
logger.info(`Docker connection verified${this.isRemote ? ` (remote: ${this.sshConfig?.host})` : ' (local)'}`);
|
|
315
|
+
};
|
|
316
|
+
try {
|
|
317
|
+
// Для удаленных подключений используем retry
|
|
318
|
+
if (this.isRemote) {
|
|
319
|
+
await retryWithTimeout(pingFn, {
|
|
320
|
+
maxAttempts: 3,
|
|
321
|
+
timeout: 30000,
|
|
322
|
+
shouldRetry: createNetworkRetryPredicate(),
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
else {
|
|
326
|
+
await pingFn();
|
|
327
|
+
}
|
|
23
328
|
}
|
|
24
329
|
catch (error) {
|
|
330
|
+
const errorMessage = this.isRemote
|
|
331
|
+
? `Failed to connect to remote Docker at ${this.sshConfig?.host}: ${error.message}`
|
|
332
|
+
: 'Docker is not running. Please start Docker Desktop and try again.';
|
|
25
333
|
logger.error('Docker connection failed:', error);
|
|
26
|
-
throw new Error(
|
|
334
|
+
throw new Error(errorMessage);
|
|
27
335
|
}
|
|
28
336
|
}
|
|
29
337
|
/**
|
|
30
|
-
*
|
|
338
|
+
* Get native Dockerode instance
|
|
31
339
|
*/
|
|
32
340
|
getClient() {
|
|
33
341
|
return this.docker;
|
|
34
342
|
}
|
|
35
343
|
/**
|
|
36
|
-
*
|
|
344
|
+
* List containers
|
|
345
|
+
* With retry logic for remote connections
|
|
37
346
|
*/
|
|
38
347
|
async listContainers(options) {
|
|
39
|
-
|
|
348
|
+
// Для удаленных подключений сначала создаем туннель
|
|
349
|
+
if (this.isRemote) {
|
|
350
|
+
await this.ensureSSHTunnel();
|
|
351
|
+
}
|
|
352
|
+
const listFn = async () => {
|
|
353
|
+
return this.docker.listContainers(options);
|
|
354
|
+
};
|
|
355
|
+
// Для удаленных подключений используем retry
|
|
356
|
+
if (this.isRemote) {
|
|
357
|
+
return retryWithTimeout(listFn, {
|
|
358
|
+
maxAttempts: 3,
|
|
359
|
+
timeout: 30000,
|
|
360
|
+
shouldRetry: createNetworkRetryPredicate(),
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
return listFn();
|
|
40
364
|
}
|
|
41
365
|
/**
|
|
42
|
-
*
|
|
366
|
+
* Get container by ID or name
|
|
43
367
|
*/
|
|
44
368
|
getContainer(id) {
|
|
45
369
|
return this.docker.getContainer(id);
|
|
@@ -48,12 +372,164 @@ export class DockerClient {
|
|
|
48
372
|
// Singleton instance
|
|
49
373
|
let dockerClientInstance = null;
|
|
50
374
|
/**
|
|
51
|
-
*
|
|
375
|
+
* Get singleton instance of DockerClient
|
|
376
|
+
* @param sshConfig - SSH configuration (optional, for remote Docker)
|
|
52
377
|
*/
|
|
53
|
-
export function getDockerClient() {
|
|
54
|
-
|
|
55
|
-
|
|
378
|
+
export function getDockerClient(sshConfig) {
|
|
379
|
+
// Если конфигурация изменилась, пересоздаем клиент
|
|
380
|
+
if (!dockerClientInstance || sshConfig !== undefined) {
|
|
381
|
+
// Cleanup old instance if exists
|
|
382
|
+
if (dockerClientInstance) {
|
|
383
|
+
dockerClientInstance.cleanup();
|
|
384
|
+
}
|
|
385
|
+
dockerClientInstance = new DockerClient(sshConfig);
|
|
56
386
|
}
|
|
57
387
|
return dockerClientInstance;
|
|
58
388
|
}
|
|
389
|
+
/**
|
|
390
|
+
* Reset singleton instance (for testing)
|
|
391
|
+
*/
|
|
392
|
+
export function resetDockerClient() {
|
|
393
|
+
if (dockerClientInstance) {
|
|
394
|
+
dockerClientInstance.cleanup();
|
|
395
|
+
}
|
|
396
|
+
dockerClientInstance = null;
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Cleanup all Docker clients (for graceful shutdown)
|
|
400
|
+
*/
|
|
401
|
+
export function cleanupDockerClient() {
|
|
402
|
+
if (dockerClientInstance) {
|
|
403
|
+
dockerClientInstance.cleanup();
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
// ============================================================================
|
|
407
|
+
// Profile-based Docker Client Pool
|
|
408
|
+
// ============================================================================
|
|
409
|
+
/**
|
|
410
|
+
* Pool of Docker clients by profile name
|
|
411
|
+
* Allows parallel access to multiple Docker environments (local and remote)
|
|
412
|
+
*/
|
|
413
|
+
const clientPool = new Map();
|
|
414
|
+
/**
|
|
415
|
+
* Local Docker client (cached)
|
|
416
|
+
*/
|
|
417
|
+
let localDockerClient = null;
|
|
418
|
+
/**
|
|
419
|
+
* Load profile configuration from profiles.json
|
|
420
|
+
* @param profileName - Profile name to load
|
|
421
|
+
* @returns SSHConfig or null for local mode
|
|
422
|
+
* @throws Error if profile not found or invalid
|
|
423
|
+
*/
|
|
424
|
+
function loadProfileConfig(profileName) {
|
|
425
|
+
const profilesFile = process.env.DOCKER_MCP_PROFILES_FILE;
|
|
426
|
+
if (!profilesFile) {
|
|
427
|
+
throw new Error('DOCKER_MCP_PROFILES_FILE environment variable not set. Cannot load profile.');
|
|
428
|
+
}
|
|
429
|
+
// Load profiles file (synchronous!)
|
|
430
|
+
const fileResult = loadProfilesFile(profilesFile);
|
|
431
|
+
if (fileResult.errors.length > 0) {
|
|
432
|
+
throw new Error(`Failed to load profiles file: ${fileResult.errors.join(', ')}`);
|
|
433
|
+
}
|
|
434
|
+
if (!fileResult.config) {
|
|
435
|
+
throw new Error(`Profiles file not found or empty: ${profilesFile}`);
|
|
436
|
+
}
|
|
437
|
+
// Get profile data
|
|
438
|
+
const profileData = fileResult.config.profiles[profileName];
|
|
439
|
+
if (!profileData) {
|
|
440
|
+
const available = Object.keys(fileResult.config.profiles).join(', ');
|
|
441
|
+
throw new Error(`Profile "${profileName}" not found. Available profiles: ${available}`);
|
|
442
|
+
}
|
|
443
|
+
// Check if local mode
|
|
444
|
+
if (profileData.mode === 'local') {
|
|
445
|
+
logger.info(`Profile "${profileName}" is configured for LOCAL mode`);
|
|
446
|
+
return null;
|
|
447
|
+
}
|
|
448
|
+
// Convert to SSHConfig
|
|
449
|
+
try {
|
|
450
|
+
const config = profileDataToSSHConfig(profileData);
|
|
451
|
+
logger.info(`Profile "${profileName}" loaded for REMOTE mode (${config.host})`);
|
|
452
|
+
return config;
|
|
453
|
+
}
|
|
454
|
+
catch (error) {
|
|
455
|
+
if (error.code === 'LOCAL_MODE') {
|
|
456
|
+
logger.info(`Profile "${profileName}" is configured for LOCAL mode`);
|
|
457
|
+
return null;
|
|
458
|
+
}
|
|
459
|
+
throw error;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* Get Docker client for specific profile
|
|
464
|
+
* @param profileName - Profile name (undefined = local Docker)
|
|
465
|
+
* @returns DockerClient instance
|
|
466
|
+
*
|
|
467
|
+
* @example
|
|
468
|
+
* // Local Docker (default)
|
|
469
|
+
* const localClient = getDockerClientForProfile();
|
|
470
|
+
*
|
|
471
|
+
* // Remote Docker (production profile)
|
|
472
|
+
* const prodClient = getDockerClientForProfile('production');
|
|
473
|
+
*/
|
|
474
|
+
export function getDockerClientForProfile(profileName) {
|
|
475
|
+
// No profile specified = local Docker
|
|
476
|
+
if (!profileName) {
|
|
477
|
+
if (!localDockerClient) {
|
|
478
|
+
localDockerClient = new DockerClient();
|
|
479
|
+
logger.debug('Created LOCAL Docker client');
|
|
480
|
+
}
|
|
481
|
+
return localDockerClient;
|
|
482
|
+
}
|
|
483
|
+
// Check if client already exists in pool
|
|
484
|
+
if (clientPool.has(profileName)) {
|
|
485
|
+
logger.debug(`Using cached Docker client for profile: ${profileName}`);
|
|
486
|
+
return clientPool.get(profileName);
|
|
487
|
+
}
|
|
488
|
+
// Load profile configuration (synchronous!)
|
|
489
|
+
logger.info(`Loading Docker client for profile: ${profileName}`);
|
|
490
|
+
const sshConfig = loadProfileConfig(profileName);
|
|
491
|
+
// Create client (local or remote based on config)
|
|
492
|
+
const client = new DockerClient(sshConfig);
|
|
493
|
+
// Cache in pool
|
|
494
|
+
clientPool.set(profileName, client);
|
|
495
|
+
logger.info(`Docker client cached for profile: ${profileName}`);
|
|
496
|
+
return client;
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* Clear client pool (for testing or cleanup)
|
|
500
|
+
*/
|
|
501
|
+
export function clearClientPool() {
|
|
502
|
+
logger.info(`Clearing Docker client pool (${clientPool.size} clients)`);
|
|
503
|
+
// Cleanup all clients in pool
|
|
504
|
+
for (const [profileName, client] of clientPool.entries()) {
|
|
505
|
+
try {
|
|
506
|
+
client.cleanup();
|
|
507
|
+
logger.debug(`Cleaned up client for profile: ${profileName}`);
|
|
508
|
+
}
|
|
509
|
+
catch (error) {
|
|
510
|
+
logger.warn(`Failed to cleanup client for profile ${profileName}: ${error.message}`);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
clientPool.clear();
|
|
514
|
+
// Cleanup local client
|
|
515
|
+
if (localDockerClient) {
|
|
516
|
+
try {
|
|
517
|
+
localDockerClient.cleanup();
|
|
518
|
+
logger.debug('Cleaned up LOCAL Docker client');
|
|
519
|
+
}
|
|
520
|
+
catch (error) {
|
|
521
|
+
logger.warn(`Failed to cleanup local client: ${error.message}`);
|
|
522
|
+
}
|
|
523
|
+
localDockerClient = null;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
/**
|
|
527
|
+
* Cleanup all Docker clients in pool (for graceful shutdown)
|
|
528
|
+
*/
|
|
529
|
+
export function cleanupAllDockerClients() {
|
|
530
|
+
// Cleanup singleton
|
|
531
|
+
cleanupDockerClient();
|
|
532
|
+
// Cleanup pool
|
|
533
|
+
clearClientPool();
|
|
534
|
+
}
|
|
59
535
|
//# sourceMappingURL=docker-client.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"docker-client.js","sourceRoot":"","sources":["../../src/utils/docker-client.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,MAAM,MAAM,WAAW,CAAC;AAC/B,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC;;GAEG;AACH,MAAM,OAAO,YAAY;IACf,MAAM,CAAS;IAEvB;QACE,IAAI,CAAC,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,CAAC,KAAK,CAAC,8BAA8B,CAAC,CAAC;IAC/C,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,IAAI;QACR,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;YACzB,MAAM,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC;QAC5C,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,MAAM,CAAC,KAAK,CAAC,2BAA2B,EAAE,KAAK,CAAC,CAAC;YACjD,MAAM,IAAI,KAAK,CACb,mEAAmE,CACpE,CAAC;QACJ,CAAC;IACH,CAAC;IAED;;OAEG;IACH,SAAS;QACP,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,cAAc,CAAC,OAAqC;QACxD,OAAO,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;IAC7C,CAAC;IAED;;OAEG;IACH,YAAY,CAAC,EAAU;QACrB,OAAO,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;IACtC,CAAC;CACF;AAED,qBAAqB;AACrB,IAAI,oBAAoB,GAAwB,IAAI,CAAC;AAErD;;GAEG;AACH,MAAM,UAAU,eAAe;IAC7B,IAAI,CAAC,oBAAoB,EAAE,CAAC;QAC1B,oBAAoB,GAAG,IAAI,YAAY,EAAE,CAAC;IAC5C,CAAC;IACD,OAAO,oBAAoB,CAAC;AAC9B,CAAC"}
|
|
1
|
+
{"version":3,"file":"docker-client.js","sourceRoot":"","sources":["../../src/utils/docker-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Profiles File Loader
|
|
3
|
+
* Load SSH profiles from JSON configuration file
|
|
4
|
+
*/
|
|
5
|
+
import type { SSHConfig } from './ssh-config.js';
|
|
6
|
+
/**
|
|
7
|
+
* Profiles configuration file structure
|
|
8
|
+
*/
|
|
9
|
+
export interface ProfilesConfig {
|
|
10
|
+
/** Default profile name to use if not specified */
|
|
11
|
+
default?: string;
|
|
12
|
+
/** SSH profiles by name */
|
|
13
|
+
profiles: Record<string, SSHProfileData>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* SSH profile data in config file
|
|
17
|
+
*/
|
|
18
|
+
export interface SSHProfileData {
|
|
19
|
+
/** Profile mode: 'local' for local Docker, 'remote' or undefined for remote Docker */
|
|
20
|
+
mode?: 'local' | 'remote';
|
|
21
|
+
/** Server address (required for remote mode) */
|
|
22
|
+
host?: string;
|
|
23
|
+
/** Username for SSH connection (required for remote mode) */
|
|
24
|
+
username?: string;
|
|
25
|
+
/** SSH port (default: 22) */
|
|
26
|
+
port?: number;
|
|
27
|
+
/** Path to private SSH key */
|
|
28
|
+
privateKeyPath?: string;
|
|
29
|
+
/** Passphrase for encrypted SSH key */
|
|
30
|
+
passphrase?: string;
|
|
31
|
+
/** Password for authentication (not recommended for production) */
|
|
32
|
+
password?: string;
|
|
33
|
+
/** Base path for Docker projects on remote server (e.g., "/var/www") */
|
|
34
|
+
projectsPath?: string;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Result of loading profiles file
|
|
38
|
+
*/
|
|
39
|
+
export interface ProfilesFileResult {
|
|
40
|
+
/** Loaded profiles configuration */
|
|
41
|
+
config: ProfilesConfig | null;
|
|
42
|
+
/** Validation errors */
|
|
43
|
+
errors: string[];
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Load profiles from JSON file
|
|
47
|
+
*
|
|
48
|
+
* @param filePath - Path to profiles JSON file
|
|
49
|
+
* @returns Profiles configuration and errors
|
|
50
|
+
*/
|
|
51
|
+
export declare function loadProfilesFile(filePath: string): ProfilesFileResult;
|
|
52
|
+
/**
|
|
53
|
+
* Convert profile data to SSHConfig
|
|
54
|
+
* @throws Error with code 'LOCAL_MODE' if profile is configured for local Docker
|
|
55
|
+
*/
|
|
56
|
+
export declare function profileDataToSSHConfig(data: SSHProfileData): SSHConfig;
|
|
57
|
+
//# sourceMappingURL=profiles-file.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profiles-file.d.ts","sourceRoot":"","sources":["../../src/utils/profiles-file.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,mDAAmD;IACnD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,2BAA2B;IAC3B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;CAC1C;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,sFAAsF;IACtF,IAAI,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC1B,gDAAgD;IAChD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,6BAA6B;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,8BAA8B;IAC9B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,uCAAuC;IACvC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mEAAmE;IACnE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wEAAwE;IACxE,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,oCAAoC;IACpC,MAAM,EAAE,cAAc,GAAG,IAAI,CAAC;IAC9B,wBAAwB;IACxB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,kBAAkB,CA2IrE;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,cAAc,GAAG,SAAS,CAsBtE"}
|