synapse-mcp 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.
Files changed (138) hide show
  1. package/README.md +607 -0
  2. package/dist/constants.d.ts +23 -0
  3. package/dist/constants.d.ts.map +1 -0
  4. package/dist/constants.js +58 -0
  5. package/dist/constants.js.map +1 -0
  6. package/dist/formatters/index.d.ts +275 -0
  7. package/dist/formatters/index.d.ts.map +1 -0
  8. package/dist/formatters/index.js +461 -0
  9. package/dist/formatters/index.js.map +1 -0
  10. package/dist/index.d.ts +3 -0
  11. package/dist/index.d.ts.map +1 -0
  12. package/dist/index.js +178 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/schemas/common.d.ts +48 -0
  15. package/dist/schemas/common.d.ts.map +1 -0
  16. package/dist/schemas/common.js +69 -0
  17. package/dist/schemas/common.js.map +1 -0
  18. package/dist/schemas/discriminator.d.ts +20 -0
  19. package/dist/schemas/discriminator.d.ts.map +1 -0
  20. package/dist/schemas/discriminator.js +25 -0
  21. package/dist/schemas/discriminator.js.map +1 -0
  22. package/dist/schemas/flux/compose.d.ts +93 -0
  23. package/dist/schemas/flux/compose.d.ts.map +1 -0
  24. package/dist/schemas/flux/compose.js +112 -0
  25. package/dist/schemas/flux/compose.js.map +1 -0
  26. package/dist/schemas/flux/container.d.ts +144 -0
  27. package/dist/schemas/flux/container.d.ts.map +1 -0
  28. package/dist/schemas/flux/container.js +163 -0
  29. package/dist/schemas/flux/container.js.map +1 -0
  30. package/dist/schemas/flux/docker.d.ts +91 -0
  31. package/dist/schemas/flux/docker.d.ts.map +1 -0
  32. package/dist/schemas/flux/docker.js +101 -0
  33. package/dist/schemas/flux/docker.js.map +1 -0
  34. package/dist/schemas/flux/host.d.ts +61 -0
  35. package/dist/schemas/flux/host.d.ts.map +1 -0
  36. package/dist/schemas/flux/host.js +72 -0
  37. package/dist/schemas/flux/host.js.map +1 -0
  38. package/dist/schemas/flux/index.d.ts +20 -0
  39. package/dist/schemas/flux/index.d.ts.map +1 -0
  40. package/dist/schemas/flux/index.js +88 -0
  41. package/dist/schemas/flux/index.js.map +1 -0
  42. package/dist/schemas/index.d.ts +11 -0
  43. package/dist/schemas/index.d.ts.map +1 -0
  44. package/dist/schemas/index.js +11 -0
  45. package/dist/schemas/index.js.map +1 -0
  46. package/dist/schemas/scout/index.d.ts +151 -0
  47. package/dist/schemas/scout/index.d.ts.map +1 -0
  48. package/dist/schemas/scout/index.js +41 -0
  49. package/dist/schemas/scout/index.js.map +1 -0
  50. package/dist/schemas/scout/logs.d.ts +48 -0
  51. package/dist/schemas/scout/logs.d.ts.map +1 -0
  52. package/dist/schemas/scout/logs.js +47 -0
  53. package/dist/schemas/scout/logs.js.map +1 -0
  54. package/dist/schemas/scout/simple.d.ts +68 -0
  55. package/dist/schemas/scout/simple.d.ts.map +1 -0
  56. package/dist/schemas/scout/simple.js +75 -0
  57. package/dist/schemas/scout/simple.js.map +1 -0
  58. package/dist/schemas/scout/zfs.d.ts +37 -0
  59. package/dist/schemas/scout/zfs.d.ts.map +1 -0
  60. package/dist/schemas/scout/zfs.js +36 -0
  61. package/dist/schemas/scout/zfs.js.map +1 -0
  62. package/dist/schemas/unified.d.ts +674 -0
  63. package/dist/schemas/unified.d.ts.map +1 -0
  64. package/dist/schemas/unified.js +453 -0
  65. package/dist/schemas/unified.js.map +1 -0
  66. package/dist/services/compose.d.ts +107 -0
  67. package/dist/services/compose.d.ts.map +1 -0
  68. package/dist/services/compose.js +308 -0
  69. package/dist/services/compose.js.map +1 -0
  70. package/dist/services/container.d.ts +69 -0
  71. package/dist/services/container.d.ts.map +1 -0
  72. package/dist/services/container.js +111 -0
  73. package/dist/services/container.js.map +1 -0
  74. package/dist/services/docker.d.ts +243 -0
  75. package/dist/services/docker.d.ts.map +1 -0
  76. package/dist/services/docker.js +812 -0
  77. package/dist/services/docker.js.map +1 -0
  78. package/dist/services/file-service.d.ts +79 -0
  79. package/dist/services/file-service.d.ts.map +1 -0
  80. package/dist/services/file-service.js +226 -0
  81. package/dist/services/file-service.js.map +1 -0
  82. package/dist/services/interfaces.d.ts +537 -0
  83. package/dist/services/interfaces.d.ts.map +1 -0
  84. package/dist/services/interfaces.js +2 -0
  85. package/dist/services/interfaces.js.map +1 -0
  86. package/dist/services/ssh-pool-exec.d.ts +10 -0
  87. package/dist/services/ssh-pool-exec.d.ts.map +1 -0
  88. package/dist/services/ssh-pool-exec.js +10 -0
  89. package/dist/services/ssh-pool-exec.js.map +1 -0
  90. package/dist/services/ssh-pool.d.ts +66 -0
  91. package/dist/services/ssh-pool.d.ts.map +1 -0
  92. package/dist/services/ssh-pool.js +253 -0
  93. package/dist/services/ssh-pool.js.map +1 -0
  94. package/dist/services/ssh-service.d.ts +39 -0
  95. package/dist/services/ssh-service.d.ts.map +1 -0
  96. package/dist/services/ssh-service.js +143 -0
  97. package/dist/services/ssh-service.js.map +1 -0
  98. package/dist/services/ssh.d.ts +37 -0
  99. package/dist/services/ssh.d.ts.map +1 -0
  100. package/dist/services/ssh.js +50 -0
  101. package/dist/services/ssh.js.map +1 -0
  102. package/dist/tools/flux.d.ts +14 -0
  103. package/dist/tools/flux.d.ts.map +1 -0
  104. package/dist/tools/flux.js +86 -0
  105. package/dist/tools/flux.js.map +1 -0
  106. package/dist/tools/index.d.ts +7 -0
  107. package/dist/tools/index.d.ts.map +1 -0
  108. package/dist/tools/index.js +43 -0
  109. package/dist/tools/index.js.map +1 -0
  110. package/dist/tools/scout.d.ts +14 -0
  111. package/dist/tools/scout.d.ts.map +1 -0
  112. package/dist/tools/scout.js +96 -0
  113. package/dist/tools/scout.js.map +1 -0
  114. package/dist/tools/unified.d.ts +7 -0
  115. package/dist/tools/unified.d.ts.map +1 -0
  116. package/dist/tools/unified.js +827 -0
  117. package/dist/tools/unified.js.map +1 -0
  118. package/dist/types.d.ts +93 -0
  119. package/dist/types.d.ts.map +1 -0
  120. package/dist/types.js +7 -0
  121. package/dist/types.js.map +1 -0
  122. package/dist/utils/errors.d.ts +60 -0
  123. package/dist/utils/errors.d.ts.map +1 -0
  124. package/dist/utils/errors.js +131 -0
  125. package/dist/utils/errors.js.map +1 -0
  126. package/dist/utils/help.d.ts +69 -0
  127. package/dist/utils/help.d.ts.map +1 -0
  128. package/dist/utils/help.js +259 -0
  129. package/dist/utils/help.js.map +1 -0
  130. package/dist/utils/index.d.ts +4 -0
  131. package/dist/utils/index.d.ts.map +1 -0
  132. package/dist/utils/index.js +4 -0
  133. package/dist/utils/index.js.map +1 -0
  134. package/dist/utils/path-security.d.ts +64 -0
  135. package/dist/utils/path-security.d.ts.map +1 -0
  136. package/dist/utils/path-security.js +138 -0
  137. package/dist/utils/path-security.js.map +1 -0
  138. package/package.json +85 -0
@@ -0,0 +1,253 @@
1
+ import { NodeSSH } from "node-ssh";
2
+ import { HostOperationError, logError } from "../utils/errors.js";
3
+ /**
4
+ * Default pool configuration
5
+ */
6
+ export const DEFAULT_POOL_CONFIG = {
7
+ maxConnections: 5,
8
+ idleTimeoutMs: 60000,
9
+ connectionTimeoutMs: 5000,
10
+ enableHealthChecks: true,
11
+ healthCheckIntervalMs: 30000
12
+ };
13
+ /**
14
+ * Generate unique pool key for host
15
+ * Format: ${host.name}:${port}
16
+ */
17
+ export function generatePoolKey(host) {
18
+ const port = host.port || 22;
19
+ return `${host.name}:${port}`;
20
+ }
21
+ /**
22
+ * SSH Connection Pool Implementation
23
+ */
24
+ export class SSHConnectionPoolImpl {
25
+ config;
26
+ pool;
27
+ stats;
28
+ healthCheckTimer;
29
+ constructor(config = {}) {
30
+ this.config = { ...DEFAULT_POOL_CONFIG, ...config };
31
+ this.pool = new Map();
32
+ this.stats = {
33
+ poolHits: 0,
34
+ poolMisses: 0,
35
+ activeConnections: 0,
36
+ idleConnections: 0,
37
+ totalConnections: 0,
38
+ healthChecksPassed: 0,
39
+ healthCheckFailures: 0
40
+ };
41
+ if (this.config.enableHealthChecks) {
42
+ this.startHealthChecks();
43
+ }
44
+ }
45
+ getStats() {
46
+ return { ...this.stats };
47
+ }
48
+ startHealthChecks() {
49
+ this.healthCheckTimer = setInterval(() => {
50
+ void this.performHealthChecks();
51
+ }, this.config.healthCheckIntervalMs);
52
+ }
53
+ async performHealthChecks() {
54
+ const healthCheckPromises = [];
55
+ for (const [poolKey, connections] of this.pool.entries()) {
56
+ for (const metadata of connections) {
57
+ // Only check idle connections
58
+ if (!metadata.isActive) {
59
+ healthCheckPromises.push(this.checkConnectionHealth(poolKey, metadata));
60
+ }
61
+ }
62
+ }
63
+ await Promise.allSettled(healthCheckPromises);
64
+ }
65
+ async checkConnectionHealth(poolKey, metadata) {
66
+ try {
67
+ // Verify connection using echo command
68
+ const result = await metadata.connection.execCommand("echo ok");
69
+ if (result.code === 0) {
70
+ // Health check passed (exit code 0 indicates success)
71
+ metadata.healthChecksPassed++;
72
+ this.stats.healthChecksPassed++;
73
+ }
74
+ else {
75
+ // Command failed
76
+ throw new Error("Health check command failed");
77
+ }
78
+ }
79
+ catch (error) {
80
+ logError(new HostOperationError("Health check failed", metadata.host.name, "healthCheck", error), {
81
+ metadata: {
82
+ poolKey,
83
+ failureCount: metadata.healthChecksFailed + 1,
84
+ lastUsed: new Date(metadata.lastUsed).toISOString()
85
+ }
86
+ });
87
+ metadata.healthChecksFailed++;
88
+ this.stats.healthCheckFailures++;
89
+ await this.removeConnection(poolKey, metadata);
90
+ }
91
+ }
92
+ async getConnection(host) {
93
+ const poolKey = generatePoolKey(host);
94
+ const connections = this.pool.get(poolKey) || [];
95
+ // Try to find idle connection
96
+ const idleConnection = connections.find((c) => !c.isActive);
97
+ if (idleConnection) {
98
+ // Reuse existing connection (pool hit)
99
+ idleConnection.isActive = true;
100
+ idleConnection.lastUsed = Date.now();
101
+ this.stats.poolHits++;
102
+ this.updateConnectionStats();
103
+ return idleConnection.connection;
104
+ }
105
+ // Check if we can create new connection
106
+ if (connections.length >= this.config.maxConnections) {
107
+ throw new Error(`Connection pool exhausted for ${poolKey} (max: ${this.config.maxConnections})`);
108
+ }
109
+ // Create new connection (pool miss)
110
+ const connection = await this.createConnection(host);
111
+ const metadata = {
112
+ connection,
113
+ host,
114
+ lastUsed: Date.now(),
115
+ created: Date.now(),
116
+ healthChecksPassed: 0,
117
+ healthChecksFailed: 0,
118
+ isActive: true
119
+ };
120
+ connections.push(metadata);
121
+ this.pool.set(poolKey, connections);
122
+ this.stats.poolMisses++;
123
+ this.updateConnectionStats();
124
+ return connection;
125
+ }
126
+ async createConnection(host) {
127
+ const ssh = new NodeSSH();
128
+ const connectionConfig = {
129
+ host: host.host,
130
+ port: host.port || 22,
131
+ username: host.sshUser || process.env.USER || "root",
132
+ privateKeyPath: host.sshKeyPath,
133
+ readyTimeout: this.config.connectionTimeoutMs
134
+ };
135
+ console.error(`[SSH Pool] Attempting connection to ${host.name} (${connectionConfig.host}:${connectionConfig.port})`);
136
+ console.error(`[SSH Pool] - Username: ${connectionConfig.username}`);
137
+ console.error(`[SSH Pool] - Private key: ${connectionConfig.privateKeyPath}`);
138
+ console.error(`[SSH Pool] - Ready timeout: ${connectionConfig.readyTimeout}ms`);
139
+ try {
140
+ await ssh.connect(connectionConfig);
141
+ console.error(`[SSH Pool] Successfully connected to ${host.name}`);
142
+ return ssh;
143
+ }
144
+ catch (error) {
145
+ console.error(`[SSH Pool] Connection failed to ${host.name}: ${error instanceof Error ? error.message : String(error)}`);
146
+ throw error;
147
+ }
148
+ }
149
+ updateConnectionStats() {
150
+ let active = 0;
151
+ let idle = 0;
152
+ let total = 0;
153
+ for (const connections of this.pool.values()) {
154
+ for (const conn of connections) {
155
+ total++;
156
+ if (conn.isActive) {
157
+ active++;
158
+ }
159
+ else {
160
+ idle++;
161
+ }
162
+ }
163
+ }
164
+ this.stats.activeConnections = active;
165
+ this.stats.idleConnections = idle;
166
+ this.stats.totalConnections = total;
167
+ }
168
+ async releaseConnection(host, connection) {
169
+ const poolKey = generatePoolKey(host);
170
+ const connections = this.pool.get(poolKey);
171
+ if (!connections) {
172
+ return;
173
+ }
174
+ const metadata = connections.find((c) => c.connection === connection);
175
+ if (metadata) {
176
+ metadata.isActive = false;
177
+ metadata.lastUsed = Date.now();
178
+ this.updateConnectionStats();
179
+ // Start idle timeout timer
180
+ this.scheduleIdleCleanup(poolKey, metadata);
181
+ }
182
+ }
183
+ scheduleIdleCleanup(poolKey, metadata) {
184
+ setTimeout(async () => {
185
+ const now = Date.now();
186
+ const idleTime = now - metadata.lastUsed;
187
+ // Only close if still idle and exceeded timeout
188
+ if (!metadata.isActive && idleTime >= this.config.idleTimeoutMs) {
189
+ await this.removeConnection(poolKey, metadata);
190
+ }
191
+ }, this.config.idleTimeoutMs);
192
+ }
193
+ async removeConnection(poolKey, metadata) {
194
+ const connections = this.pool.get(poolKey);
195
+ if (!connections)
196
+ return;
197
+ const index = connections.indexOf(metadata);
198
+ if (index !== -1) {
199
+ try {
200
+ await metadata.connection.dispose();
201
+ }
202
+ catch (error) {
203
+ logError(new HostOperationError("Failed to dispose SSH connection", metadata.host.name, "dispose", error), { metadata: { poolKey } });
204
+ }
205
+ connections.splice(index, 1);
206
+ if (connections.length === 0) {
207
+ this.pool.delete(poolKey);
208
+ }
209
+ this.updateConnectionStats();
210
+ }
211
+ }
212
+ async closeConnection(host) {
213
+ const poolKey = generatePoolKey(host);
214
+ const connections = this.pool.get(poolKey);
215
+ if (!connections) {
216
+ return;
217
+ }
218
+ const closePromises = connections.map(async (metadata) => {
219
+ try {
220
+ await metadata.connection.dispose();
221
+ }
222
+ catch (error) {
223
+ logError(new HostOperationError("Failed to dispose SSH connection during closeConnection", metadata.host.name, "closeConnection", error), { metadata: { poolKey } });
224
+ }
225
+ });
226
+ await Promise.allSettled(closePromises);
227
+ this.pool.delete(poolKey);
228
+ this.updateConnectionStats();
229
+ }
230
+ async closeAll() {
231
+ if (this.healthCheckTimer) {
232
+ clearInterval(this.healthCheckTimer);
233
+ this.healthCheckTimer = undefined;
234
+ }
235
+ const closePromises = [];
236
+ for (const connections of this.pool.values()) {
237
+ for (const metadata of connections) {
238
+ closePromises.push((async () => {
239
+ try {
240
+ await metadata.connection.dispose();
241
+ }
242
+ catch (error) {
243
+ logError(new HostOperationError("Failed to dispose SSH connection during closeAll", metadata.host.name, "closeAll", error), { operation: "closeAll" });
244
+ }
245
+ })());
246
+ }
247
+ }
248
+ await Promise.allSettled(closePromises);
249
+ this.pool.clear();
250
+ this.updateConnectionStats();
251
+ }
252
+ }
253
+ //# sourceMappingURL=ssh-pool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ssh-pool.js","sourceRoot":"","sources":["../../src/services/ssh-pool.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ import type { HostConfig } from "../types.js";
2
+ import type { ISSHConnectionPool, ISSHService } from "./interfaces.js";
3
+ import type { HostResources } from "./ssh.js";
4
+ /**
5
+ * Options for SSH command execution
6
+ */
7
+ export interface SSHCommandOptions {
8
+ timeoutMs?: number;
9
+ }
10
+ /**
11
+ * SSH service implementation using connection pool for command execution.
12
+ * Provides secure command execution and resource monitoring via SSH connections.
13
+ */
14
+ export declare class SSHService implements ISSHService {
15
+ private readonly pool;
16
+ constructor(pool: ISSHConnectionPool);
17
+ /**
18
+ * Execute SSH command using connection pool
19
+ *
20
+ * Automatically acquires connection from pool, executes command, and releases.
21
+ * Connections are reused across calls for better performance.
22
+ *
23
+ * @param host - Host configuration
24
+ * @param command - Command to execute
25
+ * @param args - Command arguments (optional)
26
+ * @param options - Execution options (timeout, etc.)
27
+ * @returns Command stdout (trimmed)
28
+ * @throws SSHCommandError if command fails or times out
29
+ */
30
+ executeSSHCommand(host: HostConfig, command: string, args?: string[], options?: SSHCommandOptions): Promise<string>;
31
+ /**
32
+ * Get host resource usage via SSH using connection pool
33
+ *
34
+ * @param host - Host configuration to query
35
+ * @returns Resource information including CPU, memory, disk, and uptime
36
+ */
37
+ getHostResources(host: HostConfig): Promise<HostResources>;
38
+ }
39
+ //# sourceMappingURL=ssh-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ssh-service.d.ts","sourceRoot":"","sources":["../../src/services/ssh-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,KAAK,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACvE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAG9C;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,qBAAa,UAAW,YAAW,WAAW;IAChC,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,kBAAkB;IAErD;;;;;;;;;;;;OAYG;IACG,iBAAiB,CACrB,IAAI,EAAE,UAAU,EAChB,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,MAAM,EAAO,EACnB,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,MAAM,CAAC;IAiElB;;;;;OAKG;IACG,gBAAgB,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,aAAa,CAAC;CAoFjE"}
@@ -0,0 +1,143 @@
1
+ import { SSHCommandError } from "../utils/errors.js";
2
+ /**
3
+ * SSH service implementation using connection pool for command execution.
4
+ * Provides secure command execution and resource monitoring via SSH connections.
5
+ */
6
+ export class SSHService {
7
+ pool;
8
+ constructor(pool) {
9
+ this.pool = pool;
10
+ }
11
+ /**
12
+ * Execute SSH command using connection pool
13
+ *
14
+ * Automatically acquires connection from pool, executes command, and releases.
15
+ * Connections are reused across calls for better performance.
16
+ *
17
+ * @param host - Host configuration
18
+ * @param command - Command to execute
19
+ * @param args - Command arguments (optional)
20
+ * @param options - Execution options (timeout, etc.)
21
+ * @returns Command stdout (trimmed)
22
+ * @throws SSHCommandError if command fails or times out
23
+ */
24
+ async executeSSHCommand(host, command, args = [], options = {}) {
25
+ const timeoutMs = options.timeoutMs || 30000;
26
+ // Get connection from pool
27
+ const connection = await this.pool.getConnection(host);
28
+ // Build full command
29
+ const fullCommand = args.length > 0 ? `${command} ${args.join(" ")}` : command;
30
+ try {
31
+ // Execute with timeout
32
+ const timeoutPromise = new Promise((_, reject) => {
33
+ setTimeout(() => {
34
+ reject(new Error(`SSH command timeout after ${timeoutMs}ms: ${command}`));
35
+ }, timeoutMs);
36
+ });
37
+ const execPromise = connection.execCommand(fullCommand);
38
+ const result = await Promise.race([execPromise, timeoutPromise]);
39
+ // Check exit code
40
+ if (result.code !== 0) {
41
+ throw new SSHCommandError("SSH command failed with non-zero exit code", host.name, fullCommand, result.code ?? undefined, result.stderr, result.stdout);
42
+ }
43
+ return result.stdout.trim();
44
+ }
45
+ catch (error) {
46
+ if (error instanceof Error) {
47
+ if (error instanceof SSHCommandError) {
48
+ throw error;
49
+ }
50
+ const baseMessage = error.message || "SSH command execution failed";
51
+ throw new SSHCommandError(baseMessage, host.name, fullCommand, undefined, undefined, undefined, error);
52
+ }
53
+ throw new SSHCommandError("SSH command execution failed", host.name, fullCommand, undefined, undefined, undefined, error);
54
+ }
55
+ finally {
56
+ // Always release connection back to pool
57
+ await this.pool.releaseConnection(host, connection);
58
+ }
59
+ }
60
+ /**
61
+ * Get host resource usage via SSH using connection pool
62
+ *
63
+ * @param host - Host configuration to query
64
+ * @returns Resource information including CPU, memory, disk, and uptime
65
+ */
66
+ async getHostResources(host) {
67
+ // Run all commands in one SSH session for efficiency
68
+ const script = `
69
+ hostname
70
+ echo "---"
71
+ uptime -p 2>/dev/null || uptime | sed 's/.*up/up/'
72
+ echo "---"
73
+ cat /proc/loadavg | awk '{print $1,$2,$3}'
74
+ echo "---"
75
+ nproc
76
+ echo "---"
77
+ top -bn1 | grep "Cpu(s)" | awk '{print 100-$8}' 2>/dev/null || echo "0"
78
+ echo "---"
79
+ free -m | awk '/^Mem:/ {print $2,$3,$4}'
80
+ echo "---"
81
+ df -BG --output=source,target,size,used,avail,pcent 2>/dev/null | grep -E '^/dev' || df -h | grep -E '^/dev'
82
+ `
83
+ .trim()
84
+ .replace(/\n/g, "; ");
85
+ const output = await this.executeSSHCommand(host, script);
86
+ const sections = output.split("---").map((s) => s.trim());
87
+ // Parse hostname
88
+ const hostname = sections[0] || host.name;
89
+ // Parse uptime
90
+ const uptime = sections[1] || "unknown";
91
+ // Parse load average
92
+ const loadParts = (sections[2] || "0 0 0").split(" ").map(Number);
93
+ const loadAverage = [
94
+ loadParts[0] || 0,
95
+ loadParts[1] || 0,
96
+ loadParts[2] || 0
97
+ ];
98
+ // Parse CPU
99
+ const cores = parseInt(sections[3] || "1", 10);
100
+ const cpuUsage = parseFloat(sections[4] || "0");
101
+ // Parse memory
102
+ const memParts = (sections[5] || "0 0 0").split(" ").map(Number);
103
+ const totalMB = memParts[0] || 0;
104
+ const usedMB = memParts[1] || 0;
105
+ const freeMB = memParts[2] || 0;
106
+ const memUsagePercent = totalMB > 0 ? (usedMB / totalMB) * 100 : 0;
107
+ // Parse disk
108
+ const diskLines = (sections[6] || "").split("\n").filter((l) => l.trim());
109
+ const disk = diskLines
110
+ .map((line) => {
111
+ const parts = line.trim().split(/\s+/);
112
+ if (parts.length >= 6) {
113
+ return {
114
+ filesystem: parts[0],
115
+ mount: parts[1],
116
+ totalGB: parseFloat(parts[2].replace("G", "")) || 0,
117
+ usedGB: parseFloat(parts[3].replace("G", "")) || 0,
118
+ availGB: parseFloat(parts[4].replace("G", "")) || 0,
119
+ usagePercent: parseFloat(parts[5].replace("%", "")) || 0
120
+ };
121
+ }
122
+ return null;
123
+ })
124
+ .filter((d) => d !== null);
125
+ return {
126
+ hostname,
127
+ uptime,
128
+ loadAverage,
129
+ cpu: {
130
+ cores,
131
+ usagePercent: Math.round(cpuUsage * 10) / 10
132
+ },
133
+ memory: {
134
+ totalMB,
135
+ usedMB,
136
+ freeMB,
137
+ usagePercent: Math.round(memUsagePercent * 10) / 10
138
+ },
139
+ disk
140
+ };
141
+ }
142
+ }
143
+ //# sourceMappingURL=ssh-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ssh-service.js","sourceRoot":"","sources":["../../src/services/ssh-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ import { HostConfig } from "../types.js";
2
+ /**
3
+ * Sanitize string for safe shell usage
4
+ * Rejects any potentially dangerous characters
5
+ */
6
+ export declare function sanitizeForShell(input: string): string;
7
+ /**
8
+ * Validate host configuration for SSH
9
+ */
10
+ export declare function validateHostForSsh(host: HostConfig): void;
11
+ /**
12
+ * Host resource stats from SSH
13
+ */
14
+ export interface HostResources {
15
+ hostname: string;
16
+ uptime: string;
17
+ loadAverage: [number, number, number];
18
+ cpu: {
19
+ cores: number;
20
+ usagePercent: number;
21
+ };
22
+ memory: {
23
+ totalMB: number;
24
+ usedMB: number;
25
+ freeMB: number;
26
+ usagePercent: number;
27
+ };
28
+ disk: Array<{
29
+ filesystem: string;
30
+ mount: string;
31
+ totalGB: number;
32
+ usedGB: number;
33
+ availGB: number;
34
+ usagePercent: number;
35
+ }>;
36
+ }
37
+ //# sourceMappingURL=ssh.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ssh.d.ts","sourceRoot":"","sources":["../../src/services/ssh.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AA4BzC;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMtD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,CAezD;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,GAAG,EAAE;QACH,KAAK,EAAE,MAAM,CAAC;QACd,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC;IACF,MAAM,EAAE;QACN,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;QACf,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC;IACF,IAAI,EAAE,KAAK,CAAC;QACV,UAAU,EAAE,MAAM,CAAC;QACnB,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC,CAAC;CACJ"}
@@ -0,0 +1,50 @@
1
+ import { SSHService } from "./ssh-service.js";
2
+ import { SSHConnectionPoolImpl } from "./ssh-pool.js";
3
+ /**
4
+ * Temporary global SSH service for backward compatibility
5
+ * @deprecated Use ServiceContainer.getSSHService() instead
6
+ */
7
+ let globalSSHService = null;
8
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
9
+ function getGlobalSSHService() {
10
+ if (!globalSSHService) {
11
+ const pool = new SSHConnectionPoolImpl({
12
+ maxConnections: parseInt(process.env.HOMELAB_SSH_MAX_CONNECTIONS || "5", 10),
13
+ idleTimeoutMs: parseInt(process.env.HOMELAB_SSH_IDLE_TIMEOUT_MS || "60000", 10),
14
+ connectionTimeoutMs: parseInt(process.env.HOMELAB_SSH_CONNECTION_TIMEOUT_MS || "5000", 10),
15
+ enableHealthChecks: process.env.HOMELAB_SSH_ENABLE_HEALTH_CHECKS !== "false",
16
+ healthCheckIntervalMs: parseInt(process.env.HOMELAB_SSH_HEALTH_CHECK_INTERVAL_MS || "30000", 10)
17
+ });
18
+ globalSSHService = new SSHService(pool);
19
+ }
20
+ return globalSSHService;
21
+ }
22
+ /**
23
+ * Sanitize string for safe shell usage
24
+ * Rejects any potentially dangerous characters
25
+ */
26
+ export function sanitizeForShell(input) {
27
+ // Only allow alphanumeric, dots, hyphens, underscores, and forward slashes (for paths)
28
+ if (!/^[a-zA-Z0-9._\-/]+$/.test(input)) {
29
+ throw new Error(`Invalid characters in input: ${input}`);
30
+ }
31
+ return input;
32
+ }
33
+ /**
34
+ * Validate host configuration for SSH
35
+ */
36
+ export function validateHostForSsh(host) {
37
+ // Validate hostname/IP - allow alphanumeric, dots, hyphens, colons (IPv6), and brackets
38
+ if (host.host && !/^[a-zA-Z0-9.\-:[\]/]+$/.test(host.host)) {
39
+ throw new Error(`Invalid host format: ${host.host}`);
40
+ }
41
+ // Validate SSH user if provided
42
+ if (host.sshUser && !/^[a-zA-Z0-9_-]+$/.test(host.sshUser)) {
43
+ throw new Error(`Invalid SSH user: ${host.sshUser}`);
44
+ }
45
+ // Validate key path if provided
46
+ if (host.sshKeyPath && !/^[a-zA-Z0-9._\-/~]+$/.test(host.sshKeyPath)) {
47
+ throw new Error(`Invalid SSH key path: ${host.sshKeyPath}`);
48
+ }
49
+ }
50
+ //# sourceMappingURL=ssh.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ssh.js","sourceRoot":"","sources":["../../src/services/ssh.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAEtD;;;GAGG;AACH,IAAI,gBAAgB,GAAsB,IAAI,CAAC;AAE/C,6DAA6D;AAC7D,SAAS,mBAAmB;IAC1B,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACtB,MAAM,IAAI,GAAG,IAAI,qBAAqB,CAAC;YACrC,cAAc,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,2BAA2B,IAAI,GAAG,EAAE,EAAE,CAAC;YAC5E,aAAa,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,2BAA2B,IAAI,OAAO,EAAE,EAAE,CAAC;YAC/E,mBAAmB,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,iCAAiC,IAAI,MAAM,EAAE,EAAE,CAAC;YAC1F,kBAAkB,EAAE,OAAO,CAAC,GAAG,CAAC,gCAAgC,KAAK,OAAO;YAC5E,qBAAqB,EAAE,QAAQ,CAC7B,OAAO,CAAC,GAAG,CAAC,oCAAoC,IAAI,OAAO,EAC3D,EAAE,CACH;SACF,CAAC,CAAC;QACH,gBAAgB,GAAG,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC;IAC1C,CAAC;IACD,OAAO,gBAAgB,CAAC;AAC1B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,uFAAuF;IACvF,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACvC,MAAM,IAAI,KAAK,CAAC,gCAAgC,KAAK,EAAE,CAAC,CAAC;IAC3D,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAgB;IACjD,wFAAwF;IACxF,IAAI,IAAI,CAAC,IAAI,IAAI,CAAC,wBAAwB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3D,MAAM,IAAI,KAAK,CAAC,wBAAwB,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IACvD,CAAC;IAED,gCAAgC;IAChC,IAAI,IAAI,CAAC,OAAO,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3D,MAAM,IAAI,KAAK,CAAC,qBAAqB,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;IACvD,CAAC;IAED,gCAAgC;IAChC,IAAI,IAAI,CAAC,UAAU,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;QACrE,MAAM,IAAI,KAAK,CAAC,yBAAyB,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;IAC9D,CAAC;AACH,CAAC"}
@@ -0,0 +1,14 @@
1
+ import type { ServiceContainer } from '../services/container.js';
2
+ /**
3
+ * Flux tool handler - Docker infrastructure management
4
+ *
5
+ * Handles 4 action categories with 39 total subactions:
6
+ * - container: 14 subactions (list, start, stop, restart, pause, resume, logs, stats, inspect, search, pull, recreate, exec, top)
7
+ * - compose: 9 subactions (list, status, up, down, restart, logs, build, pull, recreate)
8
+ * - docker: 9 subactions (info, df, prune, images, pull, build, rmi, networks, volumes)
9
+ * - host: 7 subactions (status, resources, info, uptime, services, network, mounts)
10
+ *
11
+ * Plus 'help' pseudo-action for auto-generated documentation.
12
+ */
13
+ export declare function handleFluxTool(input: unknown, container: ServiceContainer): Promise<string>;
14
+ //# sourceMappingURL=flux.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flux.d.ts","sourceRoot":"","sources":["../../src/tools/flux.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAQjE;;;;;;;;;;GAUG;AACH,wBAAsB,cAAc,CAClC,KAAK,EAAE,OAAO,EACd,SAAS,EAAE,gBAAgB,GAC1B,OAAO,CAAC,MAAM,CAAC,CA6BjB"}
@@ -0,0 +1,86 @@
1
+ // src/tools/flux.ts
2
+ import { FluxSchema } from '../schemas/flux/index.js';
3
+ import { generateHelp, formatHelpMarkdown, formatHelpJson } from '../utils/help.js';
4
+ /**
5
+ * Flux tool handler - Docker infrastructure management
6
+ *
7
+ * Handles 4 action categories with 39 total subactions:
8
+ * - container: 14 subactions (list, start, stop, restart, pause, resume, logs, stats, inspect, search, pull, recreate, exec, top)
9
+ * - compose: 9 subactions (list, status, up, down, restart, logs, build, pull, recreate)
10
+ * - docker: 9 subactions (info, df, prune, images, pull, build, rmi, networks, volumes)
11
+ * - host: 7 subactions (status, resources, info, uptime, services, network, mounts)
12
+ *
13
+ * Plus 'help' pseudo-action for auto-generated documentation.
14
+ */
15
+ export async function handleFluxTool(input, container) {
16
+ // Handle help action before schema validation
17
+ if (typeof input === 'object' && input !== null && 'action' in input && input.action === 'help') {
18
+ const helpInput = input;
19
+ const entries = generateHelp(FluxSchema, helpInput.topic);
20
+ if (helpInput.format === 'json') {
21
+ return formatHelpJson(entries);
22
+ }
23
+ return formatHelpMarkdown(entries);
24
+ }
25
+ // Validate input against Flux schema
26
+ const validated = FluxSchema.parse(input);
27
+ // Route to appropriate handler based on action
28
+ switch (validated.action) {
29
+ case 'container':
30
+ return handleContainerAction(validated, container);
31
+ case 'compose':
32
+ return handleComposeAction(validated, container);
33
+ case 'docker':
34
+ return handleDockerAction(validated, container);
35
+ case 'host':
36
+ return handleHostAction(validated, container);
37
+ default:
38
+ // Zod validation should prevent reaching here
39
+ throw new Error(`Unknown action: ${validated.action}`);
40
+ }
41
+ }
42
+ /**
43
+ * Placeholder handler for container actions
44
+ * Will be implemented in Task 16
45
+ */
46
+ function handleContainerAction(input, _container) {
47
+ // Type guard to ensure we have container action
48
+ if (input.action !== 'container') {
49
+ throw new Error(`Invalid action for container handler: ${input.action}`);
50
+ }
51
+ throw new Error(`Handler not implemented: container:${input.subaction}`);
52
+ }
53
+ /**
54
+ * Placeholder handler for compose actions
55
+ * Will be implemented in Task 17
56
+ */
57
+ function handleComposeAction(input, _container) {
58
+ // Type guard to ensure we have compose action
59
+ if (input.action !== 'compose') {
60
+ throw new Error(`Invalid action for compose handler: ${input.action}`);
61
+ }
62
+ throw new Error(`Handler not implemented: compose:${input.subaction}`);
63
+ }
64
+ /**
65
+ * Placeholder handler for docker actions
66
+ * Will be implemented in Task 18
67
+ */
68
+ function handleDockerAction(input, _container) {
69
+ // Type guard to ensure we have docker action
70
+ if (input.action !== 'docker') {
71
+ throw new Error(`Invalid action for docker handler: ${input.action}`);
72
+ }
73
+ throw new Error(`Handler not implemented: docker:${input.subaction}`);
74
+ }
75
+ /**
76
+ * Placeholder handler for host actions
77
+ * Will be implemented in Task 19
78
+ */
79
+ function handleHostAction(input, _container) {
80
+ // Type guard to ensure we have host action
81
+ if (input.action !== 'host') {
82
+ throw new Error(`Invalid action for host handler: ${input.action}`);
83
+ }
84
+ throw new Error(`Handler not implemented: host:${input.subaction}`);
85
+ }
86
+ //# sourceMappingURL=flux.js.map