@objectstack/core 0.9.0 → 0.9.2
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/{ENHANCED_FEATURES.md → ADVANCED_FEATURES.md} +13 -13
- package/CHANGELOG.md +15 -0
- package/PHASE2_IMPLEMENTATION.md +388 -0
- package/README.md +60 -11
- package/REFACTORING_SUMMARY.md +40 -0
- package/dist/api-registry-plugin.test.js +20 -20
- package/dist/dependency-resolver.d.ts +62 -0
- package/dist/dependency-resolver.d.ts.map +1 -0
- package/dist/dependency-resolver.js +317 -0
- package/dist/dependency-resolver.test.d.ts +2 -0
- package/dist/dependency-resolver.test.d.ts.map +1 -0
- package/dist/dependency-resolver.test.js +241 -0
- package/dist/health-monitor.d.ts +65 -0
- package/dist/health-monitor.d.ts.map +1 -0
- package/dist/health-monitor.js +269 -0
- package/dist/health-monitor.test.d.ts +2 -0
- package/dist/health-monitor.test.d.ts.map +1 -0
- package/dist/health-monitor.test.js +68 -0
- package/dist/hot-reload.d.ts +79 -0
- package/dist/hot-reload.d.ts.map +1 -0
- package/dist/hot-reload.js +313 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/kernel-base.d.ts +2 -2
- package/dist/kernel-base.js +2 -2
- package/dist/kernel.d.ts +79 -31
- package/dist/kernel.d.ts.map +1 -1
- package/dist/kernel.js +383 -73
- package/dist/kernel.test.js +373 -122
- package/dist/lite-kernel.d.ts +55 -0
- package/dist/lite-kernel.d.ts.map +1 -0
- package/dist/lite-kernel.js +112 -0
- package/dist/lite-kernel.test.d.ts +2 -0
- package/dist/lite-kernel.test.d.ts.map +1 -0
- package/dist/lite-kernel.test.js +161 -0
- package/dist/logger.d.ts +3 -2
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +61 -18
- package/dist/plugin-loader.d.ts +11 -0
- package/dist/plugin-loader.d.ts.map +1 -1
- package/dist/plugin-loader.js +34 -10
- package/dist/plugin-loader.test.js +9 -0
- package/dist/security/index.d.ts +3 -0
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/index.js +4 -0
- package/dist/security/permission-manager.d.ts +96 -0
- package/dist/security/permission-manager.d.ts.map +1 -0
- package/dist/security/permission-manager.js +235 -0
- package/dist/security/permission-manager.test.d.ts +2 -0
- package/dist/security/permission-manager.test.d.ts.map +1 -0
- package/dist/security/permission-manager.test.js +220 -0
- package/dist/security/plugin-signature-verifier.js +3 -3
- package/dist/security/sandbox-runtime.d.ts +115 -0
- package/dist/security/sandbox-runtime.d.ts.map +1 -0
- package/dist/security/sandbox-runtime.js +310 -0
- package/dist/security/security-scanner.d.ts +92 -0
- package/dist/security/security-scanner.d.ts.map +1 -0
- package/dist/security/security-scanner.js +273 -0
- package/examples/{enhanced-kernel-example.ts → kernel-features-example.ts} +6 -6
- package/examples/phase2-integration.ts +355 -0
- package/package.json +2 -2
- package/src/api-registry-plugin.test.ts +20 -20
- package/src/dependency-resolver.test.ts +287 -0
- package/src/dependency-resolver.ts +388 -0
- package/src/health-monitor.test.ts +81 -0
- package/src/health-monitor.ts +316 -0
- package/src/hot-reload.ts +388 -0
- package/src/index.ts +6 -1
- package/src/kernel-base.ts +2 -2
- package/src/kernel.test.ts +469 -134
- package/src/kernel.ts +464 -78
- package/src/lite-kernel.test.ts +200 -0
- package/src/lite-kernel.ts +135 -0
- package/src/logger.ts +64 -18
- package/src/plugin-loader.test.ts +10 -1
- package/src/plugin-loader.ts +42 -13
- package/src/security/index.ts +19 -0
- package/src/security/permission-manager.test.ts +256 -0
- package/src/security/permission-manager.ts +336 -0
- package/src/security/plugin-signature-verifier.ts +3 -3
- package/src/security/sandbox-runtime.ts +432 -0
- package/src/security/security-scanner.ts +365 -0
- package/dist/enhanced-kernel.d.ts +0 -103
- package/dist/enhanced-kernel.d.ts.map +0 -1
- package/dist/enhanced-kernel.js +0 -403
- package/dist/enhanced-kernel.test.d.ts +0 -2
- package/dist/enhanced-kernel.test.d.ts.map +0 -1
- package/dist/enhanced-kernel.test.js +0 -412
- package/src/enhanced-kernel.test.ts +0 -535
- package/src/enhanced-kernel.ts +0 -496
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
SandboxConfig
|
|
3
|
+
} from '@objectstack/spec/system';
|
|
4
|
+
import type { ObjectLogger } from '../logger.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Resource Usage Statistics
|
|
8
|
+
*/
|
|
9
|
+
export interface ResourceUsage {
|
|
10
|
+
memory: {
|
|
11
|
+
current: number;
|
|
12
|
+
peak: number;
|
|
13
|
+
limit?: number;
|
|
14
|
+
};
|
|
15
|
+
cpu: {
|
|
16
|
+
current: number;
|
|
17
|
+
average: number;
|
|
18
|
+
limit?: number;
|
|
19
|
+
};
|
|
20
|
+
connections: {
|
|
21
|
+
current: number;
|
|
22
|
+
limit?: number;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Sandbox Execution Context
|
|
28
|
+
* Represents an isolated execution environment for a plugin
|
|
29
|
+
*/
|
|
30
|
+
export interface SandboxContext {
|
|
31
|
+
pluginId: string;
|
|
32
|
+
config: SandboxConfig;
|
|
33
|
+
startTime: Date;
|
|
34
|
+
resourceUsage: ResourceUsage;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Plugin Sandbox Runtime
|
|
39
|
+
*
|
|
40
|
+
* Provides isolated execution environments for plugins with resource limits
|
|
41
|
+
* and access controls
|
|
42
|
+
*/
|
|
43
|
+
export class PluginSandboxRuntime {
|
|
44
|
+
private logger: ObjectLogger;
|
|
45
|
+
|
|
46
|
+
// Active sandboxes (pluginId -> context)
|
|
47
|
+
private sandboxes = new Map<string, SandboxContext>();
|
|
48
|
+
|
|
49
|
+
// Resource monitoring intervals
|
|
50
|
+
private monitoringIntervals = new Map<string, NodeJS.Timeout>();
|
|
51
|
+
|
|
52
|
+
constructor(logger: ObjectLogger) {
|
|
53
|
+
this.logger = logger.child({ component: 'SandboxRuntime' });
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Create a sandbox for a plugin
|
|
58
|
+
*/
|
|
59
|
+
createSandbox(pluginId: string, config: SandboxConfig): SandboxContext {
|
|
60
|
+
if (this.sandboxes.has(pluginId)) {
|
|
61
|
+
throw new Error(`Sandbox already exists for plugin: ${pluginId}`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const context: SandboxContext = {
|
|
65
|
+
pluginId,
|
|
66
|
+
config,
|
|
67
|
+
startTime: new Date(),
|
|
68
|
+
resourceUsage: {
|
|
69
|
+
memory: { current: 0, peak: 0, limit: config.memory?.maxHeap },
|
|
70
|
+
cpu: { current: 0, average: 0, limit: config.cpu?.maxCpuPercent },
|
|
71
|
+
connections: { current: 0, limit: config.network?.maxConnections },
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
this.sandboxes.set(pluginId, context);
|
|
76
|
+
|
|
77
|
+
// Start resource monitoring
|
|
78
|
+
this.startResourceMonitoring(pluginId);
|
|
79
|
+
|
|
80
|
+
this.logger.info('Sandbox created', {
|
|
81
|
+
pluginId,
|
|
82
|
+
level: config.level,
|
|
83
|
+
memoryLimit: config.memory?.maxHeap,
|
|
84
|
+
cpuLimit: config.cpu?.maxCpuPercent
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
return context;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Destroy a sandbox
|
|
92
|
+
*/
|
|
93
|
+
destroySandbox(pluginId: string): void {
|
|
94
|
+
const context = this.sandboxes.get(pluginId);
|
|
95
|
+
if (!context) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Stop monitoring
|
|
100
|
+
this.stopResourceMonitoring(pluginId);
|
|
101
|
+
|
|
102
|
+
this.sandboxes.delete(pluginId);
|
|
103
|
+
|
|
104
|
+
this.logger.info('Sandbox destroyed', { pluginId });
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Check if resource access is allowed
|
|
109
|
+
*/
|
|
110
|
+
checkResourceAccess(
|
|
111
|
+
pluginId: string,
|
|
112
|
+
resourceType: 'file' | 'network' | 'process' | 'env',
|
|
113
|
+
resourcePath?: string
|
|
114
|
+
): { allowed: boolean; reason?: string } {
|
|
115
|
+
const context = this.sandboxes.get(pluginId);
|
|
116
|
+
if (!context) {
|
|
117
|
+
return { allowed: false, reason: 'Sandbox not found' };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const { config } = context;
|
|
121
|
+
|
|
122
|
+
switch (resourceType) {
|
|
123
|
+
case 'file':
|
|
124
|
+
return this.checkFileAccess(config, resourcePath);
|
|
125
|
+
|
|
126
|
+
case 'network':
|
|
127
|
+
return this.checkNetworkAccess(config, resourcePath);
|
|
128
|
+
|
|
129
|
+
case 'process':
|
|
130
|
+
return this.checkProcessAccess(config);
|
|
131
|
+
|
|
132
|
+
case 'env':
|
|
133
|
+
return this.checkEnvAccess(config, resourcePath);
|
|
134
|
+
|
|
135
|
+
default:
|
|
136
|
+
return { allowed: false, reason: 'Unknown resource type' };
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Check file system access
|
|
142
|
+
* WARNING: Uses simple prefix matching. For production, use proper path
|
|
143
|
+
* resolution with path.resolve() and path.normalize() to prevent traversal.
|
|
144
|
+
*/
|
|
145
|
+
private checkFileAccess(
|
|
146
|
+
config: SandboxConfig,
|
|
147
|
+
path?: string
|
|
148
|
+
): { allowed: boolean; reason?: string } {
|
|
149
|
+
if (config.level === 'none') {
|
|
150
|
+
return { allowed: true };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (!config.filesystem) {
|
|
154
|
+
return { allowed: false, reason: 'File system access not configured' };
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// If no path specified, check general access
|
|
158
|
+
if (!path) {
|
|
159
|
+
return { allowed: config.filesystem.mode !== 'none' };
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// TODO: Use path.resolve() and path.normalize() for production
|
|
163
|
+
// Check allowed paths
|
|
164
|
+
const allowedPaths = config.filesystem.allowedPaths || [];
|
|
165
|
+
const isAllowed = allowedPaths.some(allowed => {
|
|
166
|
+
// Simple prefix matching - vulnerable to traversal attacks
|
|
167
|
+
// TODO: Use proper path resolution
|
|
168
|
+
return path.startsWith(allowed);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
if (allowedPaths.length > 0 && !isAllowed) {
|
|
172
|
+
return {
|
|
173
|
+
allowed: false,
|
|
174
|
+
reason: `Path not in allowed list: ${path}`
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Check denied paths
|
|
179
|
+
const deniedPaths = config.filesystem.deniedPaths || [];
|
|
180
|
+
const isDenied = deniedPaths.some(denied => {
|
|
181
|
+
return path.startsWith(denied);
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
if (isDenied) {
|
|
185
|
+
return {
|
|
186
|
+
allowed: false,
|
|
187
|
+
reason: `Path is explicitly denied: ${path}`
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return { allowed: true };
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Check network access
|
|
196
|
+
* WARNING: Uses simple string matching. For production, use proper URL
|
|
197
|
+
* parsing with new URL() and check hostname property.
|
|
198
|
+
*/
|
|
199
|
+
private checkNetworkAccess(
|
|
200
|
+
config: SandboxConfig,
|
|
201
|
+
url?: string
|
|
202
|
+
): { allowed: boolean; reason?: string } {
|
|
203
|
+
if (config.level === 'none') {
|
|
204
|
+
return { allowed: true };
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (!config.network) {
|
|
208
|
+
return { allowed: false, reason: 'Network access not configured' };
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Check if network access is enabled
|
|
212
|
+
if (config.network.mode === 'none') {
|
|
213
|
+
return { allowed: false, reason: 'Network access disabled' };
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// If no URL specified, check general access
|
|
217
|
+
if (!url) {
|
|
218
|
+
return { allowed: (config.network.mode as string) !== 'none' };
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// TODO: Use new URL() and check hostname property for production
|
|
222
|
+
// Check allowed hosts
|
|
223
|
+
const allowedHosts = config.network.allowedHosts || [];
|
|
224
|
+
if (allowedHosts.length > 0) {
|
|
225
|
+
const isAllowed = allowedHosts.some(host => {
|
|
226
|
+
// Simple string matching - vulnerable to bypass
|
|
227
|
+
// TODO: Use proper URL parsing
|
|
228
|
+
return url.includes(host);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
if (!isAllowed) {
|
|
232
|
+
return {
|
|
233
|
+
allowed: false,
|
|
234
|
+
reason: `Host not in allowed list: ${url}`
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Check denied hosts
|
|
240
|
+
const deniedHosts = config.network.deniedHosts || [];
|
|
241
|
+
const isDenied = deniedHosts.some(host => {
|
|
242
|
+
return url.includes(host);
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
if (isDenied) {
|
|
246
|
+
return {
|
|
247
|
+
allowed: false,
|
|
248
|
+
reason: `Host is blocked: ${url}`
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return { allowed: true };
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Check process spawning access
|
|
257
|
+
*/
|
|
258
|
+
private checkProcessAccess(
|
|
259
|
+
config: SandboxConfig
|
|
260
|
+
): { allowed: boolean; reason?: string } {
|
|
261
|
+
if (config.level === 'none') {
|
|
262
|
+
return { allowed: true };
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (!config.process) {
|
|
266
|
+
return { allowed: false, reason: 'Process access not configured' };
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (!config.process.allowSpawn) {
|
|
270
|
+
return { allowed: false, reason: 'Process spawning not allowed' };
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return { allowed: true };
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Check environment variable access
|
|
278
|
+
*/
|
|
279
|
+
private checkEnvAccess(
|
|
280
|
+
config: SandboxConfig,
|
|
281
|
+
varName?: string
|
|
282
|
+
): { allowed: boolean; reason?: string } {
|
|
283
|
+
if (config.level === 'none') {
|
|
284
|
+
return { allowed: true };
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (!config.process) {
|
|
288
|
+
return { allowed: false, reason: 'Environment access not configured' };
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// If no variable specified, check general access
|
|
292
|
+
if (!varName) {
|
|
293
|
+
return { allowed: true };
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// For now, allow all env access if process is configured
|
|
297
|
+
// In a real implementation, would check specific allowed vars
|
|
298
|
+
return { allowed: true };
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Check resource limits
|
|
303
|
+
*/
|
|
304
|
+
checkResourceLimits(pluginId: string): {
|
|
305
|
+
withinLimits: boolean;
|
|
306
|
+
violations: string[]
|
|
307
|
+
} {
|
|
308
|
+
const context = this.sandboxes.get(pluginId);
|
|
309
|
+
if (!context) {
|
|
310
|
+
return { withinLimits: true, violations: [] };
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const violations: string[] = [];
|
|
314
|
+
const { resourceUsage, config } = context;
|
|
315
|
+
|
|
316
|
+
// Check memory limit
|
|
317
|
+
if (config.memory?.maxHeap &&
|
|
318
|
+
resourceUsage.memory.current > config.memory.maxHeap) {
|
|
319
|
+
violations.push(`Memory limit exceeded: ${resourceUsage.memory.current} > ${config.memory.maxHeap}`);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Check CPU limit (would need runtime config)
|
|
323
|
+
if (config.runtime?.resourceLimits?.maxCpu &&
|
|
324
|
+
resourceUsage.cpu.current > config.runtime.resourceLimits.maxCpu) {
|
|
325
|
+
violations.push(`CPU limit exceeded: ${resourceUsage.cpu.current}% > ${config.runtime.resourceLimits.maxCpu}%`);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Check connection limit
|
|
329
|
+
if (config.network?.maxConnections &&
|
|
330
|
+
resourceUsage.connections.current > config.network.maxConnections) {
|
|
331
|
+
violations.push(`Connection limit exceeded: ${resourceUsage.connections.current} > ${config.network.maxConnections}`);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
return {
|
|
335
|
+
withinLimits: violations.length === 0,
|
|
336
|
+
violations,
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Get resource usage for a plugin
|
|
342
|
+
*/
|
|
343
|
+
getResourceUsage(pluginId: string): ResourceUsage | undefined {
|
|
344
|
+
const context = this.sandboxes.get(pluginId);
|
|
345
|
+
return context?.resourceUsage;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Start monitoring resource usage
|
|
350
|
+
*/
|
|
351
|
+
private startResourceMonitoring(pluginId: string): void {
|
|
352
|
+
// Monitor every 5 seconds
|
|
353
|
+
const interval = setInterval(() => {
|
|
354
|
+
this.updateResourceUsage(pluginId);
|
|
355
|
+
}, 5000);
|
|
356
|
+
|
|
357
|
+
this.monitoringIntervals.set(pluginId, interval);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Stop monitoring resource usage
|
|
362
|
+
*/
|
|
363
|
+
private stopResourceMonitoring(pluginId: string): void {
|
|
364
|
+
const interval = this.monitoringIntervals.get(pluginId);
|
|
365
|
+
if (interval) {
|
|
366
|
+
clearInterval(interval);
|
|
367
|
+
this.monitoringIntervals.delete(pluginId);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Update resource usage statistics
|
|
373
|
+
*
|
|
374
|
+
* NOTE: Currently uses global process.memoryUsage() which tracks the entire
|
|
375
|
+
* Node.js process, not individual plugins. For production, implement proper
|
|
376
|
+
* per-plugin tracking using V8 heap snapshots or allocation tracking at
|
|
377
|
+
* plugin boundaries.
|
|
378
|
+
*/
|
|
379
|
+
private updateResourceUsage(pluginId: string): void {
|
|
380
|
+
const context = this.sandboxes.get(pluginId);
|
|
381
|
+
if (!context) {
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// In a real implementation, this would collect actual metrics
|
|
386
|
+
// For now, this is a placeholder structure
|
|
387
|
+
|
|
388
|
+
// Update memory usage (global process memory - not per-plugin)
|
|
389
|
+
// TODO: Implement per-plugin memory tracking
|
|
390
|
+
const memoryUsage = process.memoryUsage();
|
|
391
|
+
context.resourceUsage.memory.current = memoryUsage.heapUsed;
|
|
392
|
+
context.resourceUsage.memory.peak = Math.max(
|
|
393
|
+
context.resourceUsage.memory.peak,
|
|
394
|
+
memoryUsage.heapUsed
|
|
395
|
+
);
|
|
396
|
+
|
|
397
|
+
// Update CPU usage (would use process.cpuUsage() or similar)
|
|
398
|
+
// This is a placeholder - real implementation would track per-plugin CPU
|
|
399
|
+
// TODO: Implement per-plugin CPU tracking
|
|
400
|
+
context.resourceUsage.cpu.current = 0;
|
|
401
|
+
|
|
402
|
+
// Check for violations
|
|
403
|
+
const { withinLimits, violations } = this.checkResourceLimits(pluginId);
|
|
404
|
+
if (!withinLimits) {
|
|
405
|
+
this.logger.warn('Resource limit violations detected', {
|
|
406
|
+
pluginId,
|
|
407
|
+
violations
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Get all active sandboxes
|
|
414
|
+
*/
|
|
415
|
+
getAllSandboxes(): Map<string, SandboxContext> {
|
|
416
|
+
return new Map(this.sandboxes);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Shutdown sandbox runtime
|
|
421
|
+
*/
|
|
422
|
+
shutdown(): void {
|
|
423
|
+
// Stop all monitoring
|
|
424
|
+
for (const pluginId of this.monitoringIntervals.keys()) {
|
|
425
|
+
this.stopResourceMonitoring(pluginId);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
this.sandboxes.clear();
|
|
429
|
+
|
|
430
|
+
this.logger.info('Sandbox runtime shutdown complete');
|
|
431
|
+
}
|
|
432
|
+
}
|