@ebowwa/sandbox 0.1.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 (108) hide show
  1. package/dist/compilers/index.d.ts +24 -0
  2. package/dist/compilers/index.d.ts.map +1 -0
  3. package/dist/compilers/index.js +42 -0
  4. package/dist/compilers/index.js.map +1 -0
  5. package/dist/compilers/javascript.d.ts +117 -0
  6. package/dist/compilers/javascript.d.ts.map +1 -0
  7. package/dist/compilers/javascript.js +462 -0
  8. package/dist/compilers/javascript.js.map +1 -0
  9. package/dist/compilers/python.d.ts +140 -0
  10. package/dist/compilers/python.d.ts.map +1 -0
  11. package/dist/compilers/python.js +650 -0
  12. package/dist/compilers/python.js.map +1 -0
  13. package/dist/compilers/typescript.d.ts +99 -0
  14. package/dist/compilers/typescript.d.ts.map +1 -0
  15. package/dist/compilers/typescript.js +323 -0
  16. package/dist/compilers/typescript.js.map +1 -0
  17. package/dist/core/cell.d.ts +160 -0
  18. package/dist/core/cell.d.ts.map +1 -0
  19. package/dist/core/cell.js +319 -0
  20. package/dist/core/cell.js.map +1 -0
  21. package/dist/core/compiler.d.ts +126 -0
  22. package/dist/core/compiler.d.ts.map +1 -0
  23. package/dist/core/compiler.js +123 -0
  24. package/dist/core/compiler.js.map +1 -0
  25. package/dist/core/index.d.ts +19 -0
  26. package/dist/core/index.d.ts.map +1 -0
  27. package/dist/core/index.js +14 -0
  28. package/dist/core/index.js.map +1 -0
  29. package/dist/core/limits.d.ts +173 -0
  30. package/dist/core/limits.d.ts.map +1 -0
  31. package/dist/core/limits.js +440 -0
  32. package/dist/core/limits.js.map +1 -0
  33. package/dist/core/permissions.d.ts +103 -0
  34. package/dist/core/permissions.d.ts.map +1 -0
  35. package/dist/core/permissions.js +341 -0
  36. package/dist/core/permissions.js.map +1 -0
  37. package/dist/core/runtime.d.ts +127 -0
  38. package/dist/core/runtime.d.ts.map +1 -0
  39. package/dist/core/runtime.js +325 -0
  40. package/dist/core/runtime.js.map +1 -0
  41. package/dist/core/types.d.ts +380 -0
  42. package/dist/core/types.d.ts.map +1 -0
  43. package/dist/core/types.js +67 -0
  44. package/dist/core/types.js.map +1 -0
  45. package/dist/index.d.ts +145 -0
  46. package/dist/index.d.ts.map +1 -0
  47. package/dist/index.js +279 -0
  48. package/dist/index.js.map +1 -0
  49. package/dist/multi/index.d.ts +9 -0
  50. package/dist/multi/index.d.ts.map +1 -0
  51. package/dist/multi/index.js +7 -0
  52. package/dist/multi/index.js.map +1 -0
  53. package/dist/multi/polyglot.d.ts +179 -0
  54. package/dist/multi/polyglot.d.ts.map +1 -0
  55. package/dist/multi/polyglot.js +319 -0
  56. package/dist/multi/polyglot.js.map +1 -0
  57. package/dist/runtimes/docker.d.ts +97 -0
  58. package/dist/runtimes/docker.d.ts.map +1 -0
  59. package/dist/runtimes/docker.js +368 -0
  60. package/dist/runtimes/docker.js.map +1 -0
  61. package/dist/runtimes/index.d.ts +11 -0
  62. package/dist/runtimes/index.d.ts.map +1 -0
  63. package/dist/runtimes/index.js +9 -0
  64. package/dist/runtimes/index.js.map +1 -0
  65. package/dist/runtimes/process.d.ts +47 -0
  66. package/dist/runtimes/process.d.ts.map +1 -0
  67. package/dist/runtimes/process.js +230 -0
  68. package/dist/runtimes/process.js.map +1 -0
  69. package/dist/session/index.d.ts +12 -0
  70. package/dist/session/index.d.ts.map +1 -0
  71. package/dist/session/index.js +9 -0
  72. package/dist/session/index.js.map +1 -0
  73. package/dist/session/kernel.d.ts +199 -0
  74. package/dist/session/kernel.d.ts.map +1 -0
  75. package/dist/session/kernel.js +400 -0
  76. package/dist/session/kernel.js.map +1 -0
  77. package/dist/session/notebook.d.ts +168 -0
  78. package/dist/session/notebook.d.ts.map +1 -0
  79. package/dist/session/notebook.js +499 -0
  80. package/dist/session/notebook.js.map +1 -0
  81. package/dist/session/repl.d.ts +159 -0
  82. package/dist/session/repl.d.ts.map +1 -0
  83. package/dist/session/repl.js +409 -0
  84. package/dist/session/repl.js.map +1 -0
  85. package/package.json +142 -0
  86. package/src/compilers/index.ts +80 -0
  87. package/src/compilers/javascript.ts +571 -0
  88. package/src/compilers/python.ts +785 -0
  89. package/src/compilers/typescript.ts +442 -0
  90. package/src/core/cell.ts +439 -0
  91. package/src/core/compiler.ts +250 -0
  92. package/src/core/index.ts +123 -0
  93. package/src/core/limits.ts +508 -0
  94. package/src/core/permissions.ts +409 -0
  95. package/src/core/runtime.ts +499 -0
  96. package/src/core/types.ts +528 -0
  97. package/src/global.d.ts +59 -0
  98. package/src/index.ts +515 -0
  99. package/src/multi/index.ts +22 -0
  100. package/src/multi/polyglot.ts +461 -0
  101. package/src/runtimes/docker.ts +501 -0
  102. package/src/runtimes/index.ts +21 -0
  103. package/src/runtimes/process.ts +316 -0
  104. package/src/session/index.ts +41 -0
  105. package/src/session/kernel.ts +553 -0
  106. package/src/session/notebook.ts +635 -0
  107. package/src/session/repl.ts +521 -0
  108. package/src/wasm2wasm.d.ts +35 -0
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Core Primitives
3
+ *
4
+ * Composable building blocks for sandboxed execution.
5
+ * Each primitive can be used independently or combined.
6
+ */
7
+
8
+ // Types - Foundation
9
+ export type {
10
+ Language,
11
+ LanguageCapabilities,
12
+ PermissionLevel,
13
+ Permissions,
14
+ FileSystemPermission,
15
+ NetworkPermission,
16
+ EnvironmentPermission,
17
+ ProcessPermission,
18
+ Limits,
19
+ Cell,
20
+ ExecutionContext,
21
+ ExecutionOptions,
22
+ ExecutionResult,
23
+ ExecutionError,
24
+ ExecutionMetrics,
25
+ DisplayOutput,
26
+ Compiler,
27
+ CompileOptions,
28
+ Runtime,
29
+ Kernel,
30
+ CompletionResult,
31
+ InspectResult,
32
+ Notebook,
33
+ NotebookMetadata,
34
+ NotebookRunOptions,
35
+ NotebookResult,
36
+ Session,
37
+ PolyglotCell,
38
+ LanguageBridge,
39
+ Sandbox,
40
+ SandboxOptions,
41
+ SessionOptions,
42
+ NotebookOptions,
43
+ } from "./types.js";
44
+
45
+ // Type utilities
46
+ export {
47
+ permissionsFromLevel,
48
+ parseMemory,
49
+ parseTime,
50
+ } from "./types.js";
51
+
52
+ // Compiler - Transforms source to executable
53
+ export type {
54
+ CompileResult,
55
+ ImportDefinition,
56
+ CompilerOptions,
57
+ ICompiler,
58
+ } from "./compiler.js";
59
+
60
+ export {
61
+ BaseCompiler,
62
+ WasmCompiler,
63
+ CompilerRegistry,
64
+ defaultCompilerRegistry,
65
+ } from "./compiler.js";
66
+
67
+ // Runtime - Executes compiled code
68
+ export type {
69
+ RuntimeOptions,
70
+ ExecutionRequest,
71
+ IRuntime,
72
+ RuntimeCapabilities,
73
+ } from "./runtime.js";
74
+
75
+ export {
76
+ Wasm2WasmRuntime,
77
+ NativeWasmRuntime,
78
+ RuntimeRegistry,
79
+ defaultRuntimeRegistry,
80
+ } from "./runtime.js";
81
+
82
+ // Permissions - Access control
83
+ export type {
84
+ PermissionChecker as PermissionCheckerType,
85
+ } from "./permissions.js";
86
+
87
+ export {
88
+ PermissionChecker,
89
+ PermissionBuilder,
90
+ createPermissions,
91
+ mergePermissions,
92
+ impliesPermission,
93
+ upgradePermissions,
94
+ } from "./permissions.js";
95
+
96
+ // Limits - Resource constraints
97
+ export type {
98
+ ParsedLimits,
99
+ } from "./limits.js";
100
+
101
+ export {
102
+ DEFAULT_LIMITS,
103
+ LimitsParser,
104
+ LimitsEnforcer,
105
+ LimitsBuilder,
106
+ mergeLimits,
107
+ } from "./limits.js";
108
+
109
+ // Cell - Executable unit
110
+ export type {
111
+ CellMetadata,
112
+ CellState,
113
+ ExecutableCell,
114
+ } from "./cell.js";
115
+
116
+ export {
117
+ CellBuilder,
118
+ CellExecutor,
119
+ CellRegistry,
120
+ createCell,
121
+ createCellsFromMarkdown,
122
+ cellsToMarkdown,
123
+ } from "./cell.js";
@@ -0,0 +1,508 @@
1
+ /**
2
+ * Limits Primitive
3
+ *
4
+ * Composable resource limits for sandbox execution.
5
+ * Enforces memory, time, CPU, and I/O constraints.
6
+ */
7
+
8
+ import type { Limits } from "./types.js";
9
+
10
+ /**
11
+ * Parsed limits in normalized units
12
+ */
13
+ export interface ParsedLimits {
14
+ memoryBytes: number;
15
+ timeoutMs: number;
16
+ cpuTimeMs: number;
17
+ maxInstructions: number;
18
+ maxOutputBytes: number;
19
+ maxStackDepth: number;
20
+ network: {
21
+ maxBandwidthBytes: number;
22
+ maxConnections: number;
23
+ };
24
+ filesystem: {
25
+ maxFileBytes: number;
26
+ maxTotalBytes: number;
27
+ maxOpenFiles: number;
28
+ };
29
+ }
30
+
31
+ /**
32
+ * Default limits for different isolation levels
33
+ */
34
+ export const DEFAULT_LIMITS: Record<string, ParsedLimits> = {
35
+ isolated: {
36
+ memoryBytes: 16 * 1024 * 1024, // 16MB
37
+ timeoutMs: 5000, // 5s
38
+ cpuTimeMs: 1000, // 1s CPU time
39
+ maxInstructions: 10_000_000,
40
+ maxOutputBytes: 1024 * 1024, // 1MB
41
+ maxStackDepth: 100,
42
+ network: {
43
+ maxBandwidthBytes: 0,
44
+ maxConnections: 0,
45
+ },
46
+ filesystem: {
47
+ maxFileBytes: 0,
48
+ maxTotalBytes: 0,
49
+ maxOpenFiles: 0,
50
+ },
51
+ },
52
+ readonly: {
53
+ memoryBytes: 64 * 1024 * 1024, // 64MB
54
+ timeoutMs: 30000, // 30s
55
+ cpuTimeMs: 5000, // 5s CPU time
56
+ maxInstructions: 100_000_000,
57
+ maxOutputBytes: 10 * 1024 * 1024, // 10MB
58
+ maxStackDepth: 500,
59
+ network: {
60
+ maxBandwidthBytes: 0,
61
+ maxConnections: 0,
62
+ },
63
+ filesystem: {
64
+ maxFileBytes: 10 * 1024 * 1024, // 10MB per file
65
+ maxTotalBytes: 100 * 1024 * 1024, // 100MB total
66
+ maxOpenFiles: 10,
67
+ },
68
+ },
69
+ network: {
70
+ memoryBytes: 128 * 1024 * 1024, // 128MB
71
+ timeoutMs: 60000, // 60s
72
+ cpuTimeMs: 10000, // 10s CPU time
73
+ maxInstructions: 500_000_000,
74
+ maxOutputBytes: 50 * 1024 * 1024, // 50MB
75
+ maxStackDepth: 1000,
76
+ network: {
77
+ maxBandwidthBytes: 10 * 1024 * 1024, // 10MB/s
78
+ maxConnections: 10,
79
+ },
80
+ filesystem: {
81
+ maxFileBytes: 10 * 1024 * 1024,
82
+ maxTotalBytes: 100 * 1024 * 1024,
83
+ maxOpenFiles: 20,
84
+ },
85
+ },
86
+ filesystem: {
87
+ memoryBytes: 256 * 1024 * 1024, // 256MB
88
+ timeoutMs: 120000, // 2min
89
+ cpuTimeMs: 30000, // 30s CPU time
90
+ maxInstructions: 1_000_000_000,
91
+ maxOutputBytes: 100 * 1024 * 1024, // 100MB
92
+ maxStackDepth: 2000,
93
+ network: {
94
+ maxBandwidthBytes: 50 * 1024 * 1024, // 50MB/s
95
+ maxConnections: 50,
96
+ },
97
+ filesystem: {
98
+ maxFileBytes: 100 * 1024 * 1024, // 100MB per file
99
+ maxTotalBytes: 1024 * 1024 * 1024, // 1GB total
100
+ maxOpenFiles: 100,
101
+ },
102
+ },
103
+ admin: {
104
+ memoryBytes: 1024 * 1024 * 1024, // 1GB
105
+ timeoutMs: 300000, // 5min
106
+ cpuTimeMs: 60000, // 1min CPU time
107
+ maxInstructions: 5_000_000_000,
108
+ maxOutputBytes: 500 * 1024 * 1024, // 500MB
109
+ maxStackDepth: 5000,
110
+ network: {
111
+ maxBandwidthBytes: 100 * 1024 * 1024, // 100MB/s
112
+ maxConnections: 100,
113
+ },
114
+ filesystem: {
115
+ maxFileBytes: 1024 * 1024 * 1024, // 1GB per file
116
+ maxTotalBytes: 10 * 1024 * 1024 * 1024, // 10GB total
117
+ maxOpenFiles: 500,
118
+ },
119
+ },
120
+ sudo: {
121
+ memoryBytes: 4 * 1024 * 1024 * 1024, // 4GB
122
+ timeoutMs: 600000, // 10min
123
+ cpuTimeMs: 300000, // 5min CPU time
124
+ maxInstructions: 10_000_000_000,
125
+ maxOutputBytes: 1024 * 1024 * 1024, // 1GB
126
+ maxStackDepth: 10000,
127
+ network: {
128
+ maxBandwidthBytes: 500 * 1024 * 1024, // 500MB/s
129
+ maxConnections: 500,
130
+ },
131
+ filesystem: {
132
+ maxFileBytes: 10 * 1024 * 1024 * 1024, // 10GB per file
133
+ maxTotalBytes: 100 * 1024 * 1024 * 1024, // 100GB total
134
+ maxOpenFiles: 1000,
135
+ },
136
+ },
137
+ };
138
+
139
+ /**
140
+ * Limits Parser
141
+ *
142
+ * Converts human-readable limits to normalized values.
143
+ */
144
+ export class LimitsParser {
145
+ /**
146
+ * Parse limits object to normalized values
147
+ */
148
+ static parse(limits: Limits): ParsedLimits {
149
+ return {
150
+ memoryBytes: this.parseMemory(limits.memory),
151
+ timeoutMs: this.parseTime(limits.timeout),
152
+ cpuTimeMs: limits.cpuTime ?? 30000,
153
+ maxInstructions: limits.maxInstructions ?? 100_000_000,
154
+ maxOutputBytes: limits.maxOutputSize ?? 10 * 1024 * 1024,
155
+ maxStackDepth: limits.maxStackDepth ?? 1000,
156
+ network: {
157
+ maxBandwidthBytes: this.parseMemory(limits.network?.maxBandwidth),
158
+ maxConnections: limits.network?.maxConnections ?? 10,
159
+ },
160
+ filesystem: {
161
+ maxFileBytes: this.parseMemory(limits.filesystem?.maxFileSize),
162
+ maxTotalBytes: this.parseMemory(limits.filesystem?.maxTotalSize),
163
+ maxOpenFiles: limits.filesystem?.maxOpenFiles ?? 100,
164
+ },
165
+ };
166
+ }
167
+
168
+ /**
169
+ * Parse memory string/number to bytes
170
+ */
171
+ static parseMemory(mem: number | string | undefined): number {
172
+ if (mem === undefined) return 16 * 1024 * 1024; // 16MB default
173
+ if (typeof mem === "number") return mem;
174
+
175
+ const match = mem.match(/^(\d+(?:\.\d+)?)\s*(b|kb|mb|gb|tb)?$/i);
176
+ if (!match) return 16 * 1024 * 1024;
177
+
178
+ const [, num, unit] = match;
179
+ const multipliers: Record<string, number> = {
180
+ b: 1,
181
+ kb: 1024,
182
+ mb: 1024 ** 2,
183
+ gb: 1024 ** 3,
184
+ tb: 1024 ** 4,
185
+ };
186
+
187
+ return Math.floor(parseFloat(num) * (multipliers[unit?.toLowerCase() ?? "b"] ?? 1));
188
+ }
189
+
190
+ /**
191
+ * Parse time string/number to milliseconds
192
+ */
193
+ static parseTime(time: number | string | undefined): number {
194
+ if (time === undefined) return 30000; // 30s default
195
+ if (typeof time === "number") return time;
196
+
197
+ const match = time.match(/^(\d+(?:\.\d+)?)\s*(ms|s|m|h)?$/i);
198
+ if (!match) return 30000;
199
+
200
+ const [, num, unit] = match;
201
+ const multipliers: Record<string, number> = {
202
+ ms: 1,
203
+ s: 1000,
204
+ m: 60000,
205
+ h: 3600000,
206
+ };
207
+
208
+ return Math.floor(parseFloat(num) * (multipliers[unit?.toLowerCase() ?? "ms"] ?? 1));
209
+ }
210
+
211
+ /**
212
+ * Format bytes to human-readable string
213
+ */
214
+ static formatMemory(bytes: number): string {
215
+ const units = ["b", "kb", "mb", "gb", "tb"];
216
+ let value = bytes;
217
+ let unitIndex = 0;
218
+
219
+ while (value >= 1024 && unitIndex < units.length - 1) {
220
+ value /= 1024;
221
+ unitIndex++;
222
+ }
223
+
224
+ return `${value.toFixed(1)}${units[unitIndex]}`;
225
+ }
226
+
227
+ /**
228
+ * Format milliseconds to human-readable string
229
+ */
230
+ static formatTime(ms: number): string {
231
+ if (ms < 1000) return `${ms}ms`;
232
+ if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;
233
+ if (ms < 3600000) return `${(ms / 60000).toFixed(1)}m`;
234
+ return `${(ms / 3600000).toFixed(1)}h`;
235
+ }
236
+ }
237
+
238
+ /**
239
+ * Limits Enforcer
240
+ *
241
+ * Tracks and enforces resource limits during execution.
242
+ */
243
+ export class LimitsEnforcer {
244
+ private startTime = 0;
245
+ private instructionCount = 0;
246
+ private memoryUsed = 0;
247
+ private outputSize = 0;
248
+ private networkBytes = { in: 0, out: 0 };
249
+ private openFiles = 0;
250
+ private aborted = false;
251
+ private abortReason?: string;
252
+
253
+ constructor(private limits: ParsedLimits) {}
254
+
255
+ /**
256
+ * Start tracking
257
+ */
258
+ start(): void {
259
+ this.startTime = Date.now();
260
+ this.aborted = false;
261
+ this.abortReason = undefined;
262
+ }
263
+
264
+ /**
265
+ * Check if execution should continue
266
+ */
267
+ check(): { ok: boolean; reason?: string } {
268
+ if (this.aborted) {
269
+ return { ok: false, reason: this.abortReason };
270
+ }
271
+
272
+ // Check timeout
273
+ const elapsed = Date.now() - this.startTime;
274
+ if (elapsed > this.limits.timeoutMs) {
275
+ return { ok: false, reason: `Timeout exceeded: ${elapsed}ms > ${this.limits.timeoutMs}ms` };
276
+ }
277
+
278
+ return { ok: true };
279
+ }
280
+
281
+ /**
282
+ * Track memory allocation
283
+ */
284
+ trackMemory(bytes: number): { ok: boolean; reason?: string } {
285
+ this.memoryUsed += bytes;
286
+ if (this.memoryUsed > this.limits.memoryBytes) {
287
+ this.abort(`Memory limit exceeded: ${LimitsParser.formatMemory(this.memoryUsed)} > ${LimitsParser.formatMemory(this.limits.memoryBytes)}`);
288
+ return { ok: false, reason: this.abortReason };
289
+ }
290
+ return { ok: true };
291
+ }
292
+
293
+ /**
294
+ * Track instruction execution
295
+ */
296
+ trackInstructions(count: number): { ok: boolean; reason?: string } {
297
+ this.instructionCount += count;
298
+ if (this.instructionCount > this.limits.maxInstructions) {
299
+ this.abort(`Instruction limit exceeded: ${this.instructionCount} > ${this.limits.maxInstructions}`);
300
+ return { ok: false, reason: this.abortReason };
301
+ }
302
+ return { ok: true };
303
+ }
304
+
305
+ /**
306
+ * Track output size
307
+ */
308
+ trackOutput(bytes: number): { ok: boolean; reason?: string } {
309
+ this.outputSize += bytes;
310
+ if (this.outputSize > this.limits.maxOutputBytes) {
311
+ this.abort(`Output limit exceeded: ${LimitsParser.formatMemory(this.outputSize)} > ${LimitsParser.formatMemory(this.limits.maxOutputBytes)}`);
312
+ return { ok: false, reason: this.abortReason };
313
+ }
314
+ return { ok: true };
315
+ }
316
+
317
+ /**
318
+ * Track network usage
319
+ */
320
+ trackNetwork(direction: "in" | "out", bytes: number): { ok: boolean; reason?: string } {
321
+ this.networkBytes[direction] += bytes;
322
+ // Could add bandwidth tracking here
323
+ return { ok: true };
324
+ }
325
+
326
+ /**
327
+ * Track file open
328
+ */
329
+ trackFileOpen(): { ok: boolean; reason?: string } {
330
+ this.openFiles++;
331
+ if (this.openFiles > this.limits.filesystem.maxOpenFiles) {
332
+ this.openFiles--;
333
+ return { ok: false, reason: `Too many open files: ${this.openFiles} > ${this.limits.filesystem.maxOpenFiles}` };
334
+ }
335
+ return { ok: true };
336
+ }
337
+
338
+ /**
339
+ * Track file close
340
+ */
341
+ trackFileClose(): void {
342
+ this.openFiles = Math.max(0, this.openFiles - 1);
343
+ }
344
+
345
+ /**
346
+ * Get current usage stats
347
+ */
348
+ getUsage(): {
349
+ elapsed: number;
350
+ memory: number;
351
+ instructions: number;
352
+ output: number;
353
+ networkIn: number;
354
+ networkOut: number;
355
+ openFiles: number;
356
+ } {
357
+ return {
358
+ elapsed: Date.now() - this.startTime,
359
+ memory: this.memoryUsed,
360
+ instructions: this.instructionCount,
361
+ output: this.outputSize,
362
+ networkIn: this.networkBytes.in,
363
+ networkOut: this.networkBytes.out,
364
+ openFiles: this.openFiles,
365
+ };
366
+ }
367
+
368
+ /**
369
+ * Abort execution
370
+ */
371
+ abort(reason: string): void {
372
+ this.aborted = true;
373
+ this.abortReason = reason;
374
+ }
375
+
376
+ /**
377
+ * Check if aborted
378
+ */
379
+ isAborted(): boolean {
380
+ return this.aborted;
381
+ }
382
+
383
+ /**
384
+ * Get abort reason
385
+ */
386
+ getAbortReason(): string | undefined {
387
+ return this.abortReason;
388
+ }
389
+ }
390
+
391
+ /**
392
+ * Limits Builder
393
+ *
394
+ * Fluent API for building limits.
395
+ */
396
+ export class LimitsBuilder {
397
+ private limits: Limits = {};
398
+
399
+ memory(bytes: number | string): this {
400
+ this.limits.memory = bytes;
401
+ return this;
402
+ }
403
+
404
+ timeout(ms: number | string): this {
405
+ this.limits.timeout = ms;
406
+ return this;
407
+ }
408
+
409
+ cpuTime(ms: number): this {
410
+ this.limits.cpuTime = ms;
411
+ return this;
412
+ }
413
+
414
+ instructions(max: number): this {
415
+ this.limits.maxInstructions = max;
416
+ return this;
417
+ }
418
+
419
+ outputSize(bytes: number): this {
420
+ this.limits.maxOutputSize = bytes;
421
+ return this;
422
+ }
423
+
424
+ stackDepth(max: number): this {
425
+ this.limits.maxStackDepth = max;
426
+ return this;
427
+ }
428
+
429
+ network(options: Limits["network"]): this {
430
+ this.limits.network = options;
431
+ return this;
432
+ }
433
+
434
+ filesystem(options: Limits["filesystem"]): this {
435
+ this.limits.filesystem = options;
436
+ return this;
437
+ }
438
+
439
+ build(): Limits {
440
+ return this.limits;
441
+ }
442
+
443
+ parse(): ParsedLimits {
444
+ return LimitsParser.parse(this.limits);
445
+ }
446
+ }
447
+
448
+ /**
449
+ * Merge limits (take minimums - most restrictive)
450
+ */
451
+ export function mergeLimits(...limits: Limits[]): Limits {
452
+ const result: Limits = {};
453
+
454
+ // Memory - take minimum
455
+ const memories = limits
456
+ .map(l => l.memory)
457
+ .filter(Boolean);
458
+ if (memories.length > 0) {
459
+ const parsed = memories.map(m => LimitsParser.parseMemory(m));
460
+ result.memory = Math.min(...parsed);
461
+ }
462
+
463
+ // Timeout - take minimum
464
+ const timeouts = limits
465
+ .map(l => l.timeout)
466
+ .filter(Boolean);
467
+ if (timeouts.length > 0) {
468
+ const parsed = timeouts.map(t => LimitsParser.parseTime(t));
469
+ result.timeout = Math.min(...parsed);
470
+ }
471
+
472
+ // CPU time - take minimum
473
+ const cpuTimes = limits
474
+ .map(l => l.cpuTime)
475
+ .filter(Boolean) as number[];
476
+ if (cpuTimes.length > 0) {
477
+ result.cpuTime = Math.min(...cpuTimes);
478
+ }
479
+
480
+ // Instructions - take minimum
481
+ const instructions = limits
482
+ .map(l => l.maxInstructions)
483
+ .filter(Boolean) as number[];
484
+ if (instructions.length > 0) {
485
+ result.maxInstructions = Math.min(...instructions);
486
+ }
487
+
488
+ // Output size - take minimum
489
+ const outputSizes = limits
490
+ .map(l => l.maxOutputSize)
491
+ .filter(Boolean) as number[];
492
+ if (outputSizes.length > 0) {
493
+ result.maxOutputSize = Math.min(...outputSizes);
494
+ }
495
+
496
+ // Stack depth - take minimum
497
+ const stackDepths = limits
498
+ .map(l => l.maxStackDepth)
499
+ .filter(Boolean) as number[];
500
+ if (stackDepths.length > 0) {
501
+ result.maxStackDepth = Math.min(...stackDepths);
502
+ }
503
+
504
+ return result;
505
+ }
506
+
507
+ // Re-export parse functions from types
508
+ export { parseMemory, parseTime } from "./types.js";