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