@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.
- package/dist/compilers/index.d.ts +24 -0
- package/dist/compilers/index.d.ts.map +1 -0
- package/dist/compilers/index.js +42 -0
- package/dist/compilers/index.js.map +1 -0
- package/dist/compilers/javascript.d.ts +117 -0
- package/dist/compilers/javascript.d.ts.map +1 -0
- package/dist/compilers/javascript.js +462 -0
- package/dist/compilers/javascript.js.map +1 -0
- package/dist/compilers/python.d.ts +140 -0
- package/dist/compilers/python.d.ts.map +1 -0
- package/dist/compilers/python.js +650 -0
- package/dist/compilers/python.js.map +1 -0
- package/dist/compilers/typescript.d.ts +99 -0
- package/dist/compilers/typescript.d.ts.map +1 -0
- package/dist/compilers/typescript.js +323 -0
- package/dist/compilers/typescript.js.map +1 -0
- package/dist/core/cell.d.ts +160 -0
- package/dist/core/cell.d.ts.map +1 -0
- package/dist/core/cell.js +319 -0
- package/dist/core/cell.js.map +1 -0
- package/dist/core/compiler.d.ts +126 -0
- package/dist/core/compiler.d.ts.map +1 -0
- package/dist/core/compiler.js +123 -0
- package/dist/core/compiler.js.map +1 -0
- package/dist/core/index.d.ts +19 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +14 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/limits.d.ts +173 -0
- package/dist/core/limits.d.ts.map +1 -0
- package/dist/core/limits.js +440 -0
- package/dist/core/limits.js.map +1 -0
- package/dist/core/permissions.d.ts +103 -0
- package/dist/core/permissions.d.ts.map +1 -0
- package/dist/core/permissions.js +341 -0
- package/dist/core/permissions.js.map +1 -0
- package/dist/core/runtime.d.ts +127 -0
- package/dist/core/runtime.d.ts.map +1 -0
- package/dist/core/runtime.js +325 -0
- package/dist/core/runtime.js.map +1 -0
- package/dist/core/types.d.ts +380 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +67 -0
- package/dist/core/types.js.map +1 -0
- package/dist/index.d.ts +145 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +279 -0
- package/dist/index.js.map +1 -0
- package/dist/multi/index.d.ts +9 -0
- package/dist/multi/index.d.ts.map +1 -0
- package/dist/multi/index.js +7 -0
- package/dist/multi/index.js.map +1 -0
- package/dist/multi/polyglot.d.ts +179 -0
- package/dist/multi/polyglot.d.ts.map +1 -0
- package/dist/multi/polyglot.js +319 -0
- package/dist/multi/polyglot.js.map +1 -0
- package/dist/runtimes/docker.d.ts +97 -0
- package/dist/runtimes/docker.d.ts.map +1 -0
- package/dist/runtimes/docker.js +368 -0
- package/dist/runtimes/docker.js.map +1 -0
- package/dist/runtimes/index.d.ts +11 -0
- package/dist/runtimes/index.d.ts.map +1 -0
- package/dist/runtimes/index.js +9 -0
- package/dist/runtimes/index.js.map +1 -0
- package/dist/runtimes/process.d.ts +47 -0
- package/dist/runtimes/process.d.ts.map +1 -0
- package/dist/runtimes/process.js +230 -0
- package/dist/runtimes/process.js.map +1 -0
- package/dist/session/index.d.ts +12 -0
- package/dist/session/index.d.ts.map +1 -0
- package/dist/session/index.js +9 -0
- package/dist/session/index.js.map +1 -0
- package/dist/session/kernel.d.ts +199 -0
- package/dist/session/kernel.d.ts.map +1 -0
- package/dist/session/kernel.js +400 -0
- package/dist/session/kernel.js.map +1 -0
- package/dist/session/notebook.d.ts +168 -0
- package/dist/session/notebook.d.ts.map +1 -0
- package/dist/session/notebook.js +499 -0
- package/dist/session/notebook.js.map +1 -0
- package/dist/session/repl.d.ts +159 -0
- package/dist/session/repl.d.ts.map +1 -0
- package/dist/session/repl.js +409 -0
- package/dist/session/repl.js.map +1 -0
- package/package.json +142 -0
- package/src/compilers/index.ts +80 -0
- package/src/compilers/javascript.ts +571 -0
- package/src/compilers/python.ts +785 -0
- package/src/compilers/typescript.ts +442 -0
- package/src/core/cell.ts +439 -0
- package/src/core/compiler.ts +250 -0
- package/src/core/index.ts +123 -0
- package/src/core/limits.ts +508 -0
- package/src/core/permissions.ts +409 -0
- package/src/core/runtime.ts +499 -0
- package/src/core/types.ts +528 -0
- package/src/global.d.ts +59 -0
- package/src/index.ts +515 -0
- package/src/multi/index.ts +22 -0
- package/src/multi/polyglot.ts +461 -0
- package/src/runtimes/docker.ts +501 -0
- package/src/runtimes/index.ts +21 -0
- package/src/runtimes/process.ts +316 -0
- package/src/session/index.ts +41 -0
- package/src/session/kernel.ts +553 -0
- package/src/session/notebook.ts +635 -0
- package/src/session/repl.ts +521 -0
- 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";
|