@neverinfamous/mysql-mcp 2.2.0 → 2.3.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.
Files changed (216) hide show
  1. package/.github/workflows/codeql.yml +0 -8
  2. package/.github/workflows/docker-publish.yml +11 -10
  3. package/CHANGELOG.md +96 -0
  4. package/CODE_MODE.md +245 -0
  5. package/DOCKER_README.md +71 -254
  6. package/Dockerfile +5 -0
  7. package/README.md +102 -55
  8. package/VERSION +1 -1
  9. package/dist/adapters/mysql/MySQLAdapter.d.ts +4 -0
  10. package/dist/adapters/mysql/MySQLAdapter.d.ts.map +1 -1
  11. package/dist/adapters/mysql/MySQLAdapter.js +9 -0
  12. package/dist/adapters/mysql/MySQLAdapter.js.map +1 -1
  13. package/dist/adapters/mysql/prompts/index.d.ts +8 -1
  14. package/dist/adapters/mysql/prompts/index.d.ts.map +1 -1
  15. package/dist/adapters/mysql/prompts/index.js +8 -1
  16. package/dist/adapters/mysql/prompts/index.js.map +1 -1
  17. package/dist/adapters/mysql/prompts/routerSetup.d.ts.map +1 -1
  18. package/dist/adapters/mysql/prompts/routerSetup.js +5 -0
  19. package/dist/adapters/mysql/prompts/routerSetup.js.map +1 -1
  20. package/dist/adapters/mysql/resources/capabilities.d.ts.map +1 -1
  21. package/dist/adapters/mysql/resources/capabilities.js +6 -5
  22. package/dist/adapters/mysql/resources/capabilities.js.map +1 -1
  23. package/dist/adapters/mysql/resources/index.d.ts +9 -1
  24. package/dist/adapters/mysql/resources/index.d.ts.map +1 -1
  25. package/dist/adapters/mysql/resources/index.js +9 -1
  26. package/dist/adapters/mysql/resources/index.js.map +1 -1
  27. package/dist/adapters/mysql/tools/admin/backup.d.ts.map +1 -1
  28. package/dist/adapters/mysql/tools/admin/backup.js +3 -3
  29. package/dist/adapters/mysql/tools/admin/backup.js.map +1 -1
  30. package/dist/adapters/mysql/tools/admin/maintenance.d.ts.map +1 -1
  31. package/dist/adapters/mysql/tools/admin/maintenance.js +5 -5
  32. package/dist/adapters/mysql/tools/admin/maintenance.js.map +1 -1
  33. package/dist/adapters/mysql/tools/cluster/innodb-cluster.d.ts.map +1 -1
  34. package/dist/adapters/mysql/tools/cluster/innodb-cluster.js +26 -5
  35. package/dist/adapters/mysql/tools/cluster/innodb-cluster.js.map +1 -1
  36. package/dist/adapters/mysql/tools/codemode/index.d.ts +38 -0
  37. package/dist/adapters/mysql/tools/codemode/index.d.ts.map +1 -0
  38. package/dist/adapters/mysql/tools/codemode/index.js +203 -0
  39. package/dist/adapters/mysql/tools/codemode/index.js.map +1 -0
  40. package/dist/adapters/mysql/tools/core.d.ts.map +1 -1
  41. package/dist/adapters/mysql/tools/core.js +32 -20
  42. package/dist/adapters/mysql/tools/core.js.map +1 -1
  43. package/dist/adapters/mysql/tools/events.js +18 -6
  44. package/dist/adapters/mysql/tools/events.js.map +1 -1
  45. package/dist/adapters/mysql/tools/json/core.d.ts.map +1 -1
  46. package/dist/adapters/mysql/tools/json/core.js +5 -5
  47. package/dist/adapters/mysql/tools/json/core.js.map +1 -1
  48. package/dist/adapters/mysql/tools/json/helpers.d.ts.map +1 -1
  49. package/dist/adapters/mysql/tools/json/helpers.js +9 -3
  50. package/dist/adapters/mysql/tools/json/helpers.js.map +1 -1
  51. package/dist/adapters/mysql/tools/partitioning.d.ts.map +1 -1
  52. package/dist/adapters/mysql/tools/partitioning.js +38 -6
  53. package/dist/adapters/mysql/tools/partitioning.js.map +1 -1
  54. package/dist/adapters/mysql/tools/performance/analysis.d.ts.map +1 -1
  55. package/dist/adapters/mysql/tools/performance/analysis.js +67 -20
  56. package/dist/adapters/mysql/tools/performance/analysis.js.map +1 -1
  57. package/dist/adapters/mysql/tools/performance/optimization.d.ts.map +1 -1
  58. package/dist/adapters/mysql/tools/performance/optimization.js +36 -6
  59. package/dist/adapters/mysql/tools/performance/optimization.js.map +1 -1
  60. package/dist/adapters/mysql/tools/security/data-protection.d.ts.map +1 -1
  61. package/dist/adapters/mysql/tools/security/data-protection.js +9 -4
  62. package/dist/adapters/mysql/tools/security/data-protection.js.map +1 -1
  63. package/dist/adapters/mysql/tools/shell/common.d.ts.map +1 -1
  64. package/dist/adapters/mysql/tools/shell/common.js +28 -2
  65. package/dist/adapters/mysql/tools/shell/common.js.map +1 -1
  66. package/dist/adapters/mysql/tools/shell/restore.d.ts.map +1 -1
  67. package/dist/adapters/mysql/tools/shell/restore.js +54 -4
  68. package/dist/adapters/mysql/tools/shell/restore.js.map +1 -1
  69. package/dist/adapters/mysql/tools/spatial/operations.d.ts.map +1 -1
  70. package/dist/adapters/mysql/tools/spatial/operations.js +10 -2
  71. package/dist/adapters/mysql/tools/spatial/operations.js.map +1 -1
  72. package/dist/adapters/mysql/tools/spatial/setup.d.ts.map +1 -1
  73. package/dist/adapters/mysql/tools/spatial/setup.js +18 -0
  74. package/dist/adapters/mysql/tools/spatial/setup.js.map +1 -1
  75. package/dist/adapters/mysql/tools/sysschema/resources.d.ts.map +1 -1
  76. package/dist/adapters/mysql/tools/sysschema/resources.js +5 -0
  77. package/dist/adapters/mysql/tools/sysschema/resources.js.map +1 -1
  78. package/dist/adapters/mysql/tools/text/fulltext.d.ts.map +1 -1
  79. package/dist/adapters/mysql/tools/text/fulltext.js +6 -4
  80. package/dist/adapters/mysql/tools/text/fulltext.js.map +1 -1
  81. package/dist/adapters/mysql/tools/text/processing.d.ts.map +1 -1
  82. package/dist/adapters/mysql/tools/text/processing.js +10 -45
  83. package/dist/adapters/mysql/tools/text/processing.js.map +1 -1
  84. package/dist/adapters/mysql/tools/transactions.d.ts.map +1 -1
  85. package/dist/adapters/mysql/tools/transactions.js +8 -8
  86. package/dist/adapters/mysql/tools/transactions.js.map +1 -1
  87. package/dist/adapters/mysql/types.d.ts +968 -78
  88. package/dist/adapters/mysql/types.d.ts.map +1 -1
  89. package/dist/adapters/mysql/types.js +1084 -78
  90. package/dist/adapters/mysql/types.js.map +1 -1
  91. package/dist/auth/scopes.d.ts.map +1 -1
  92. package/dist/auth/scopes.js +1 -0
  93. package/dist/auth/scopes.js.map +1 -1
  94. package/dist/cli/args.d.ts.map +1 -1
  95. package/dist/cli/args.js +12 -0
  96. package/dist/cli/args.js.map +1 -1
  97. package/dist/codemode/api.d.ts +69 -0
  98. package/dist/codemode/api.d.ts.map +1 -0
  99. package/dist/codemode/api.js +1035 -0
  100. package/dist/codemode/api.js.map +1 -0
  101. package/dist/codemode/index.d.ts +13 -0
  102. package/dist/codemode/index.d.ts.map +1 -0
  103. package/dist/codemode/index.js +17 -0
  104. package/dist/codemode/index.js.map +1 -0
  105. package/dist/codemode/sandbox-factory.d.ts +72 -0
  106. package/dist/codemode/sandbox-factory.d.ts.map +1 -0
  107. package/dist/codemode/sandbox-factory.js +88 -0
  108. package/dist/codemode/sandbox-factory.js.map +1 -0
  109. package/dist/codemode/sandbox.d.ts +96 -0
  110. package/dist/codemode/sandbox.d.ts.map +1 -0
  111. package/dist/codemode/sandbox.js +345 -0
  112. package/dist/codemode/sandbox.js.map +1 -0
  113. package/dist/codemode/security.d.ts +44 -0
  114. package/dist/codemode/security.d.ts.map +1 -0
  115. package/dist/codemode/security.js +149 -0
  116. package/dist/codemode/security.js.map +1 -0
  117. package/dist/codemode/types.d.ts +137 -0
  118. package/dist/codemode/types.d.ts.map +1 -0
  119. package/dist/codemode/types.js +46 -0
  120. package/dist/codemode/types.js.map +1 -0
  121. package/dist/codemode/worker-sandbox.d.ts +82 -0
  122. package/dist/codemode/worker-sandbox.d.ts.map +1 -0
  123. package/dist/codemode/worker-sandbox.js +244 -0
  124. package/dist/codemode/worker-sandbox.js.map +1 -0
  125. package/dist/codemode/worker-script.d.ts +8 -0
  126. package/dist/codemode/worker-script.d.ts.map +1 -0
  127. package/dist/codemode/worker-script.js +113 -0
  128. package/dist/codemode/worker-script.js.map +1 -0
  129. package/dist/constants/ServerInstructions.d.ts +1 -1
  130. package/dist/constants/ServerInstructions.d.ts.map +1 -1
  131. package/dist/constants/ServerInstructions.js +33 -9
  132. package/dist/constants/ServerInstructions.js.map +1 -1
  133. package/dist/filtering/ToolConstants.d.ts +11 -11
  134. package/dist/filtering/ToolConstants.d.ts.map +1 -1
  135. package/dist/filtering/ToolConstants.js +37 -19
  136. package/dist/filtering/ToolConstants.js.map +1 -1
  137. package/dist/filtering/ToolFilter.d.ts.map +1 -1
  138. package/dist/filtering/ToolFilter.js +12 -0
  139. package/dist/filtering/ToolFilter.js.map +1 -1
  140. package/dist/server/McpServer.js +1 -1
  141. package/dist/server/McpServer.js.map +1 -1
  142. package/dist/types/modules/server.d.ts +2 -0
  143. package/dist/types/modules/server.d.ts.map +1 -1
  144. package/dist/types/modules/tools.d.ts +1 -1
  145. package/dist/types/modules/tools.d.ts.map +1 -1
  146. package/dist/utils/logger.d.ts +1 -1
  147. package/dist/utils/logger.d.ts.map +1 -1
  148. package/dist/utils/logger.js.map +1 -1
  149. package/package.json +12 -7
  150. package/releases/v2.2.0-release-notes.md +18 -18
  151. package/releases/v2.3.0-release-notes.md +191 -0
  152. package/releases/v2.3.1-release-notes.md +34 -0
  153. package/src/__tests__/perf.test.ts +12 -12
  154. package/src/adapters/mysql/MySQLAdapter.ts +10 -0
  155. package/src/adapters/mysql/__tests__/MySQLAdapter.test.ts +1 -1
  156. package/src/adapters/mysql/prompts/index.ts +8 -1
  157. package/src/adapters/mysql/prompts/routerSetup.ts +5 -0
  158. package/src/adapters/mysql/resources/__tests__/capabilities.test.ts +50 -1
  159. package/src/adapters/mysql/resources/capabilities.ts +6 -4
  160. package/src/adapters/mysql/resources/index.ts +9 -1
  161. package/src/adapters/mysql/tools/__tests__/core.test.ts +68 -0
  162. package/src/adapters/mysql/tools/__tests__/events.test.ts +56 -2
  163. package/src/adapters/mysql/tools/__tests__/json_core.test.ts +1 -1
  164. package/src/adapters/mysql/tools/__tests__/json_helpers.test.ts +46 -4
  165. package/src/adapters/mysql/tools/__tests__/replication.test.ts +144 -42
  166. package/src/adapters/mysql/tools/__tests__/security.test.ts +39 -0
  167. package/src/adapters/mysql/tools/__tests__/spatial.test.ts +39 -7
  168. package/src/adapters/mysql/tools/__tests__/spatial_handler.test.ts +35 -3
  169. package/src/adapters/mysql/tools/__tests__/transactions.test.ts +3 -5
  170. package/src/adapters/mysql/tools/admin/backup.ts +8 -3
  171. package/src/adapters/mysql/tools/admin/maintenance.ts +8 -4
  172. package/src/adapters/mysql/tools/cluster/__tests__/innodb-cluster.test.ts +35 -0
  173. package/src/adapters/mysql/tools/cluster/innodb-cluster.ts +26 -5
  174. package/src/adapters/mysql/tools/codemode/index.ts +249 -0
  175. package/src/adapters/mysql/tools/core.ts +44 -27
  176. package/src/adapters/mysql/tools/events.ts +23 -7
  177. package/src/adapters/mysql/tools/json/__tests__/helpers.test.ts +59 -14
  178. package/src/adapters/mysql/tools/json/core.ts +8 -4
  179. package/src/adapters/mysql/tools/json/helpers.ts +13 -3
  180. package/src/adapters/mysql/tools/partitioning.ts +53 -6
  181. package/src/adapters/mysql/tools/performance/__tests__/analysis.test.ts +227 -4
  182. package/src/adapters/mysql/tools/performance/__tests__/optimization.test.ts +35 -0
  183. package/src/adapters/mysql/tools/performance/analysis.ts +75 -21
  184. package/src/adapters/mysql/tools/performance/optimization.ts +44 -6
  185. package/src/adapters/mysql/tools/security/data-protection.ts +10 -4
  186. package/src/adapters/mysql/tools/shell/__tests__/common.test.ts +46 -0
  187. package/src/adapters/mysql/tools/shell/__tests__/restore.test.ts +28 -1
  188. package/src/adapters/mysql/tools/shell/common.ts +34 -2
  189. package/src/adapters/mysql/tools/shell/restore.ts +70 -7
  190. package/src/adapters/mysql/tools/spatial/__tests__/operations.test.ts +29 -0
  191. package/src/adapters/mysql/tools/spatial/operations.ts +13 -2
  192. package/src/adapters/mysql/tools/spatial/setup.ts +23 -0
  193. package/src/adapters/mysql/tools/sysschema/__tests__/resources.test.ts +21 -0
  194. package/src/adapters/mysql/tools/sysschema/resources.ts +5 -0
  195. package/src/adapters/mysql/tools/text/fulltext.ts +13 -5
  196. package/src/adapters/mysql/tools/text/processing.ts +20 -49
  197. package/src/adapters/mysql/tools/transactions.ts +11 -7
  198. package/src/adapters/mysql/types.ts +1241 -87
  199. package/src/auth/scopes.ts +1 -0
  200. package/src/cli/args.ts +14 -0
  201. package/src/codemode/api.ts +1224 -0
  202. package/src/codemode/index.ts +51 -0
  203. package/src/codemode/sandbox-factory.ts +146 -0
  204. package/src/codemode/sandbox.ts +450 -0
  205. package/src/codemode/security.ts +188 -0
  206. package/src/codemode/types.ts +194 -0
  207. package/src/codemode/worker-sandbox.ts +326 -0
  208. package/src/codemode/worker-script.ts +144 -0
  209. package/src/constants/ServerInstructions.ts +33 -9
  210. package/src/filtering/ToolConstants.ts +37 -19
  211. package/src/filtering/ToolFilter.ts +15 -0
  212. package/src/filtering/__tests__/ToolFilter.test.ts +65 -38
  213. package/src/server/McpServer.ts +1 -1
  214. package/src/types/modules/server.ts +3 -0
  215. package/src/types/modules/tools.ts +2 -1
  216. package/src/utils/logger.ts +2 -1
@@ -0,0 +1,51 @@
1
+ /**
2
+ * mysql-mcp - Code Mode Module
3
+ *
4
+ * Exports for the sandboxed code execution environment.
5
+ */
6
+
7
+ // Types
8
+ export type {
9
+ SandboxOptions,
10
+ PoolOptions,
11
+ SandboxResult,
12
+ ExecutionMetrics,
13
+ SecurityConfig,
14
+ ValidationResult,
15
+ ExecutionRecord,
16
+ ExecuteCodeOptions,
17
+ ExecuteCodeResult,
18
+ GroupApi,
19
+ } from "./types.js";
20
+
21
+ export {
22
+ DEFAULT_SANDBOX_OPTIONS,
23
+ DEFAULT_POOL_OPTIONS,
24
+ DEFAULT_SECURITY_CONFIG,
25
+ } from "./types.js";
26
+
27
+ // Sandbox (VM-based)
28
+ export { CodeModeSandbox, SandboxPool } from "./sandbox.js";
29
+
30
+ // Worker Sandbox (worker_threads-based)
31
+ export { WorkerSandbox, WorkerSandboxPool } from "./worker-sandbox.js";
32
+
33
+ // Sandbox Factory (mode selection)
34
+ export {
35
+ setDefaultSandboxMode,
36
+ getDefaultSandboxMode,
37
+ getAvailableSandboxModes,
38
+ createSandbox,
39
+ createSandboxPool,
40
+ getSandboxModeInfo,
41
+ type SandboxMode,
42
+ type ISandbox,
43
+ type ISandboxPool,
44
+ type SandboxModeInfo,
45
+ } from "./sandbox-factory.js";
46
+
47
+ // Security
48
+ export { CodeModeSecurityManager } from "./security.js";
49
+
50
+ // API
51
+ export { MysqlApi, createMysqlApi } from "./api.js";
@@ -0,0 +1,146 @@
1
+ /**
2
+ * mysql-mcp - Sandbox Factory
3
+ *
4
+ * Factory functions for creating sandbox instances with configurable isolation modes.
5
+ * Allows runtime selection between vm-based and worker-based sandboxes.
6
+ */
7
+
8
+ import { CodeModeSandbox, SandboxPool } from "./sandbox.js";
9
+ import { WorkerSandbox, WorkerSandboxPool } from "./worker-sandbox.js";
10
+ import { logger } from "../utils/logger.js";
11
+ import type { SandboxOptions, PoolOptions, SandboxResult } from "./types.js";
12
+
13
+ /**
14
+ * Sandbox isolation mode
15
+ */
16
+ export type SandboxMode = "vm" | "worker";
17
+
18
+ /**
19
+ * Unified sandbox interface
20
+ */
21
+ export interface ISandbox {
22
+ execute(
23
+ code: string,
24
+ apiBindings: Record<string, unknown>,
25
+ ): Promise<SandboxResult>;
26
+ isHealthy(): boolean;
27
+ dispose(): void;
28
+ }
29
+
30
+ /**
31
+ * Unified sandbox pool interface
32
+ */
33
+ export interface ISandboxPool {
34
+ initialize(): void;
35
+ execute(
36
+ code: string,
37
+ apiBindings: Record<string, unknown>,
38
+ ): Promise<SandboxResult>;
39
+ getStats(): { available: number; inUse: number; max: number };
40
+ dispose(): void;
41
+ }
42
+
43
+ /**
44
+ * Mode info for documentation/selection
45
+ */
46
+ export interface SandboxModeInfo {
47
+ name: string;
48
+ isolation: string;
49
+ performance: string;
50
+ security: string;
51
+ requirements: string;
52
+ }
53
+
54
+ // Default mode (module-level state)
55
+ let defaultMode: SandboxMode = "vm";
56
+
57
+ /**
58
+ * Set the default sandbox mode
59
+ */
60
+ export function setDefaultSandboxMode(mode: SandboxMode): void {
61
+ defaultMode = mode;
62
+ logger.info(`Sandbox default mode set to: ${mode}`, {
63
+ module: "CODEMODE" as const,
64
+ });
65
+ }
66
+
67
+ /**
68
+ * Get the current default mode
69
+ */
70
+ export function getDefaultSandboxMode(): SandboxMode {
71
+ return defaultMode;
72
+ }
73
+
74
+ /**
75
+ * Get available sandbox modes
76
+ */
77
+ export function getAvailableSandboxModes(): SandboxMode[] {
78
+ return ["vm", "worker"];
79
+ }
80
+
81
+ /**
82
+ * Create a sandbox instance
83
+ * @param mode - Isolation mode ('vm' or 'worker')
84
+ * @param options - Sandbox options
85
+ */
86
+ export function createSandbox(
87
+ mode?: SandboxMode,
88
+ options?: SandboxOptions,
89
+ ): ISandbox {
90
+ const selectedMode = mode ?? defaultMode;
91
+
92
+ switch (selectedMode) {
93
+ case "worker":
94
+ return WorkerSandbox.create(options);
95
+ case "vm":
96
+ default:
97
+ return CodeModeSandbox.create(options);
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Create a sandbox pool
103
+ * @param mode - Isolation mode ('vm' or 'worker')
104
+ * @param poolOptions - Pool configuration
105
+ * @param sandboxOptions - Sandbox configuration
106
+ */
107
+ export function createSandboxPool(
108
+ mode?: SandboxMode,
109
+ poolOptions?: PoolOptions,
110
+ sandboxOptions?: SandboxOptions,
111
+ ): ISandboxPool {
112
+ const selectedMode = mode ?? defaultMode;
113
+
114
+ switch (selectedMode) {
115
+ case "worker":
116
+ return new WorkerSandboxPool(poolOptions, sandboxOptions);
117
+ case "vm":
118
+ default:
119
+ return new SandboxPool(poolOptions, sandboxOptions);
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Get mode characteristics for documentation/selection
125
+ */
126
+ export function getSandboxModeInfo(mode: SandboxMode): SandboxModeInfo {
127
+ switch (mode) {
128
+ case "worker":
129
+ return {
130
+ name: "Worker Thread",
131
+ isolation: "Separate V8 instance per worker",
132
+ performance: "Higher overhead (thread spawn per execution)",
133
+ security: "Enhanced - isolated memory, hard timeouts",
134
+ requirements: "Node.js worker_threads (built-in)",
135
+ };
136
+ case "vm":
137
+ default:
138
+ return {
139
+ name: "VM Context",
140
+ isolation: "Script isolation within same process",
141
+ performance: "Low overhead (reusable contexts)",
142
+ security: "Standard - script isolation, blocked globals",
143
+ requirements: "Node.js vm module (built-in)",
144
+ };
145
+ }
146
+ }
@@ -0,0 +1,450 @@
1
+ /**
2
+ * mysql-mcp - Code Mode Sandbox
3
+ *
4
+ * Sandboxed execution environment using Node.js vm module.
5
+ * Provides code isolation with memory/time limits for LLM-generated code.
6
+ *
7
+ * Note: This uses Node.js vm module which provides script isolation but not
8
+ * true V8 isolate separation. For production environments with untrusted code,
9
+ * consider using isolated-vm or running in a separate process/container.
10
+ */
11
+
12
+ import vm from "node:vm";
13
+ import { logger } from "../utils/logger.js";
14
+ import {
15
+ DEFAULT_SANDBOX_OPTIONS,
16
+ DEFAULT_POOL_OPTIONS,
17
+ type SandboxOptions,
18
+ type PoolOptions,
19
+ type SandboxResult,
20
+ type ExecutionMetrics,
21
+ } from "./types.js";
22
+
23
+ /**
24
+ * A sandboxed execution context using Node.js vm module
25
+ */
26
+ export class CodeModeSandbox {
27
+ private context: vm.Context;
28
+ private readonly options: Required<SandboxOptions>;
29
+ private disposed = false;
30
+ private readonly logBuffer: string[] = [];
31
+
32
+ private constructor(context: vm.Context, options: Required<SandboxOptions>) {
33
+ this.context = context;
34
+ this.options = options;
35
+ }
36
+
37
+ /**
38
+ * Create a new sandbox instance
39
+ */
40
+ static create(options?: SandboxOptions): CodeModeSandbox {
41
+ const opts = { ...DEFAULT_SANDBOX_OPTIONS, ...options };
42
+
43
+ // Create a shared log buffer that will be used by both sandbox console and instance
44
+ const sharedLogBuffer: string[] = [];
45
+
46
+ // Create a minimal sandbox context
47
+ const sandbox = {
48
+ console: {
49
+ log: (...args: unknown[]) => {
50
+ sharedLogBuffer.push(
51
+ args
52
+ .map((a) =>
53
+ typeof a === "object" && a !== null
54
+ ? JSON.stringify(a)
55
+ : String(a),
56
+ )
57
+ .join(" "),
58
+ );
59
+ },
60
+ warn: (...args: unknown[]) =>
61
+ sharedLogBuffer.push(
62
+ "[WARN] " +
63
+ args
64
+ .map((a) =>
65
+ typeof a === "object" && a !== null
66
+ ? JSON.stringify(a)
67
+ : String(a),
68
+ )
69
+ .join(" "),
70
+ ),
71
+ error: (...args: unknown[]) =>
72
+ sharedLogBuffer.push(
73
+ "[ERROR] " +
74
+ args
75
+ .map((a) =>
76
+ typeof a === "object" && a !== null
77
+ ? JSON.stringify(a)
78
+ : String(a),
79
+ )
80
+ .join(" "),
81
+ ),
82
+ info: (...args: unknown[]) =>
83
+ sharedLogBuffer.push(
84
+ "[INFO] " +
85
+ args
86
+ .map((a) =>
87
+ typeof a === "object" && a !== null
88
+ ? JSON.stringify(a)
89
+ : String(a),
90
+ )
91
+ .join(" "),
92
+ ),
93
+ },
94
+ // No access to Node.js globals
95
+ require: undefined,
96
+ process: undefined,
97
+ global: undefined,
98
+ globalThis: undefined,
99
+ __dirname: undefined,
100
+ __filename: undefined,
101
+ module: undefined,
102
+ exports: undefined,
103
+ // Safe built-ins only
104
+ JSON,
105
+ Math,
106
+ Date,
107
+ Array,
108
+ Object,
109
+ String,
110
+ Number,
111
+ Boolean,
112
+ Map,
113
+ Set,
114
+ Promise,
115
+ Error,
116
+ TypeError,
117
+ RangeError,
118
+ SyntaxError,
119
+ // Async support
120
+ setTimeout: undefined, // Disabled for security
121
+ setInterval: undefined, // Disabled for security
122
+ setImmediate: undefined, // Disabled for security
123
+ };
124
+
125
+ const context = vm.createContext(sandbox);
126
+ const instance = new CodeModeSandbox(context, opts);
127
+
128
+ // Use the shared buffer directly - replace instance's buffer with the shared one
129
+ (instance as unknown as { logBuffer: string[] }).logBuffer =
130
+ sharedLogBuffer;
131
+
132
+ return instance;
133
+ }
134
+
135
+ /**
136
+ * Execute code in the sandbox
137
+ * @param code - TypeScript/JavaScript code to execute
138
+ * @param apiBindings - Object with mysql.* API methods to expose
139
+ */
140
+ async execute(
141
+ code: string,
142
+ apiBindings: Record<string, unknown>,
143
+ ): Promise<SandboxResult> {
144
+ if (this.disposed) {
145
+ return {
146
+ success: false,
147
+ error: "Sandbox has been disposed",
148
+ metrics: { wallTimeMs: 0, cpuTimeMs: 0, memoryUsedMb: 0 },
149
+ };
150
+ }
151
+
152
+ const startTime = performance.now();
153
+ const startMemory = process.memoryUsage().heapUsed;
154
+
155
+ try {
156
+ // Inject mysql API bindings into the context
157
+ this.context["mysql"] = apiBindings;
158
+
159
+ // Wrap code in async IIFE to support await
160
+ const wrappedCode = `
161
+ (async () => {
162
+ ${code}
163
+ })();
164
+ `;
165
+
166
+ // Compile and run with timeout
167
+ const script = new vm.Script(wrappedCode, {
168
+ filename: "codemode-script.js",
169
+ });
170
+
171
+ const result = await (script.runInContext(this.context, {
172
+ timeout: this.options.timeoutMs,
173
+ breakOnSigint: true,
174
+ }) as Promise<unknown>);
175
+
176
+ const endTime = performance.now();
177
+ const endMemory = process.memoryUsage().heapUsed;
178
+
179
+ return {
180
+ success: true,
181
+ result,
182
+ metrics: this.calculateMetrics(
183
+ startTime,
184
+ endTime,
185
+ startMemory,
186
+ endMemory,
187
+ ),
188
+ };
189
+ } catch (error) {
190
+ const endTime = performance.now();
191
+ const endMemory = process.memoryUsage().heapUsed;
192
+
193
+ const errorMessage =
194
+ error instanceof Error ? error.message : String(error);
195
+ const stack = error instanceof Error ? error.stack : undefined;
196
+
197
+ // Check for specific error types
198
+ if (errorMessage.includes("Script execution timed out")) {
199
+ return {
200
+ success: false,
201
+ error: `Execution timeout: exceeded ${String(this.options.timeoutMs)}ms limit`,
202
+ stack,
203
+ metrics: this.calculateMetrics(
204
+ startTime,
205
+ endTime,
206
+ startMemory,
207
+ endMemory,
208
+ ),
209
+ };
210
+ }
211
+
212
+ return {
213
+ success: false,
214
+ error: errorMessage,
215
+ stack,
216
+ metrics: this.calculateMetrics(
217
+ startTime,
218
+ endTime,
219
+ startMemory,
220
+ endMemory,
221
+ ),
222
+ };
223
+ }
224
+ }
225
+
226
+ /**
227
+ * Calculate execution metrics
228
+ */
229
+ private calculateMetrics(
230
+ startTime: number,
231
+ endTime: number,
232
+ startMemory: number,
233
+ endMemory: number,
234
+ ): ExecutionMetrics {
235
+ return {
236
+ wallTimeMs: Math.round(endTime - startTime),
237
+ cpuTimeMs: Math.round(endTime - startTime), // Approximation
238
+ memoryUsedMb: Math.max(
239
+ 0,
240
+ Math.round(((endMemory - startMemory) / (1024 * 1024)) * 100) / 100,
241
+ ),
242
+ };
243
+ }
244
+
245
+ /**
246
+ * Get console output from the sandbox
247
+ */
248
+ getConsoleOutput(): string[] {
249
+ return [...this.logBuffer];
250
+ }
251
+
252
+ /**
253
+ * Clear console output buffer
254
+ */
255
+ clearConsoleOutput(): void {
256
+ this.logBuffer.length = 0;
257
+ }
258
+
259
+ /**
260
+ * Check if sandbox is healthy
261
+ */
262
+ isHealthy(): boolean {
263
+ return !this.disposed;
264
+ }
265
+
266
+ /**
267
+ * Dispose of the sandbox and release resources
268
+ */
269
+ dispose(): void {
270
+ if (this.disposed) return;
271
+
272
+ this.disposed = true;
273
+ // vm.Context doesn't need explicit cleanup, but we mark as disposed
274
+ this.logBuffer.length = 0;
275
+ }
276
+ }
277
+
278
+ /**
279
+ * Pool of sandbox instances for reuse
280
+ */
281
+ export class SandboxPool {
282
+ private readonly options: Required<PoolOptions>;
283
+ private readonly sandboxOptions: Required<SandboxOptions>;
284
+ private readonly available: CodeModeSandbox[] = [];
285
+ private readonly inUse = new Set<CodeModeSandbox>();
286
+ private disposed = false;
287
+ private cleanupInterval: NodeJS.Timeout | null = null;
288
+
289
+ constructor(poolOptions?: PoolOptions, sandboxOptions?: SandboxOptions) {
290
+ this.options = { ...DEFAULT_POOL_OPTIONS, ...poolOptions };
291
+ this.sandboxOptions = { ...DEFAULT_SANDBOX_OPTIONS, ...sandboxOptions };
292
+ }
293
+
294
+ /**
295
+ * Initialize the pool with minimum instances
296
+ */
297
+ initialize(): void {
298
+ logger.info(
299
+ `Initializing sandbox pool with ${String(this.options.minInstances)} instances`,
300
+ {
301
+ module: "CODEMODE" as const,
302
+ },
303
+ );
304
+
305
+ for (let i = 0; i < this.options.minInstances; i++) {
306
+ const sandbox = CodeModeSandbox.create(this.sandboxOptions);
307
+ this.available.push(sandbox);
308
+ }
309
+
310
+ // Start cleanup interval
311
+ this.cleanupInterval = setInterval(() => {
312
+ this.cleanup();
313
+ }, this.options.idleTimeoutMs);
314
+ }
315
+
316
+ /**
317
+ * Acquire a sandbox from the pool
318
+ */
319
+ acquire(): CodeModeSandbox {
320
+ if (this.disposed) {
321
+ throw new Error("Pool has been disposed");
322
+ }
323
+
324
+ // Try to get an available sandbox
325
+ while (this.available.length > 0) {
326
+ const sandbox = this.available.pop();
327
+ if (sandbox?.isHealthy()) {
328
+ this.inUse.add(sandbox);
329
+ return sandbox;
330
+ }
331
+ // Sandbox is unhealthy, dispose it
332
+ sandbox?.dispose();
333
+ }
334
+
335
+ // Create a new sandbox if under limit
336
+ const totalCount = this.inUse.size;
337
+ if (totalCount < this.options.maxInstances) {
338
+ const sandbox = CodeModeSandbox.create(this.sandboxOptions);
339
+ this.inUse.add(sandbox);
340
+ return sandbox;
341
+ }
342
+
343
+ // Pool exhausted
344
+ throw new Error(
345
+ `Sandbox pool exhausted (max: ${String(this.options.maxInstances)})`,
346
+ );
347
+ }
348
+
349
+ /**
350
+ * Release a sandbox back to the pool
351
+ */
352
+ release(sandbox: CodeModeSandbox): void {
353
+ if (!this.inUse.has(sandbox)) {
354
+ return;
355
+ }
356
+
357
+ this.inUse.delete(sandbox);
358
+
359
+ if (this.disposed) {
360
+ sandbox.dispose();
361
+ return;
362
+ }
363
+
364
+ // Return to pool if healthy and under limit
365
+ if (
366
+ sandbox.isHealthy() &&
367
+ this.available.length < this.options.maxInstances
368
+ ) {
369
+ sandbox.clearConsoleOutput();
370
+ this.available.push(sandbox);
371
+ } else {
372
+ sandbox.dispose();
373
+ }
374
+ }
375
+
376
+ /**
377
+ * Execute code using a pooled sandbox
378
+ */
379
+ async execute(
380
+ code: string,
381
+ apiBindings: Record<string, unknown>,
382
+ ): Promise<SandboxResult> {
383
+ const sandbox = this.acquire();
384
+ try {
385
+ return await sandbox.execute(code, apiBindings);
386
+ } finally {
387
+ this.release(sandbox);
388
+ }
389
+ }
390
+
391
+ /**
392
+ * Clean up excess idle sandboxes
393
+ */
394
+ private cleanup(): void {
395
+ // Remove unhealthy sandboxes
396
+ const healthy: CodeModeSandbox[] = [];
397
+ for (const sandbox of this.available) {
398
+ if (sandbox.isHealthy()) {
399
+ healthy.push(sandbox);
400
+ } else {
401
+ sandbox.dispose();
402
+ }
403
+ }
404
+ this.available.length = 0;
405
+ this.available.push(...healthy);
406
+
407
+ // Trim to minimum
408
+ while (this.available.length > this.options.minInstances) {
409
+ const sandbox = this.available.pop();
410
+ sandbox?.dispose();
411
+ }
412
+ }
413
+
414
+ /**
415
+ * Get pool statistics
416
+ */
417
+ getStats(): { available: number; inUse: number; max: number } {
418
+ return {
419
+ available: this.available.length,
420
+ inUse: this.inUse.size,
421
+ max: this.options.maxInstances,
422
+ };
423
+ }
424
+
425
+ /**
426
+ * Dispose of all sandboxes in the pool
427
+ */
428
+ dispose(): void {
429
+ if (this.disposed) return;
430
+
431
+ this.disposed = true;
432
+
433
+ if (this.cleanupInterval) {
434
+ clearInterval(this.cleanupInterval);
435
+ this.cleanupInterval = null;
436
+ }
437
+
438
+ for (const sandbox of this.available) {
439
+ sandbox.dispose();
440
+ }
441
+ this.available.length = 0;
442
+
443
+ for (const sandbox of this.inUse) {
444
+ sandbox.dispose();
445
+ }
446
+ this.inUse.clear();
447
+
448
+ logger.info("Sandbox pool disposed", { module: "CODEMODE" as const });
449
+ }
450
+ }