@cogitator-ai/sandbox 0.2.0 → 0.2.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/README.md +486 -37
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @cogitator-ai/sandbox
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Secure sandbox execution for Cogitator agents. Run untrusted code in isolated Docker containers, WASM modules, or native fallback with resource limits, network isolation, and timeout enforcement.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -8,77 +8,526 @@ Sandbox execution for Cogitator agents. Supports Docker containers and WASM (via
|
|
|
8
8
|
pnpm add @cogitator-ai/sandbox
|
|
9
9
|
|
|
10
10
|
# Optional peer dependencies
|
|
11
|
-
pnpm add dockerode
|
|
11
|
+
pnpm add dockerode # For Docker sandbox
|
|
12
12
|
pnpm add @extism/extism # For WASM sandbox
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
##
|
|
15
|
+
## Quick Start
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
```typescript
|
|
18
|
+
import { SandboxManager } from '@cogitator-ai/sandbox';
|
|
19
|
+
|
|
20
|
+
const manager = new SandboxManager();
|
|
21
|
+
await manager.initialize();
|
|
22
|
+
|
|
23
|
+
const result = await manager.execute(
|
|
24
|
+
{ command: ['python', '-c', 'print("Hello!")'] },
|
|
25
|
+
{ type: 'docker', image: 'python:3.11-alpine' }
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
console.log(result.data?.stdout); // "Hello!"
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Features
|
|
32
|
+
|
|
33
|
+
- **Docker Sandbox** - Full container isolation with dropped capabilities
|
|
34
|
+
- **WASM Sandbox** - Extism-powered WebAssembly execution
|
|
35
|
+
- **Native Fallback** - Direct execution when containers unavailable
|
|
36
|
+
- **Container Pool** - Reuse warm containers for faster execution
|
|
37
|
+
- **Resource Limits** - Memory, CPU, PID limits
|
|
38
|
+
- **Network Isolation** - Disabled by default
|
|
39
|
+
- **Timeout Enforcement** - Kill runaway processes
|
|
40
|
+
- **Security Hardening** - No privilege escalation, all capabilities dropped
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Sandbox Manager
|
|
45
|
+
|
|
46
|
+
The `SandboxManager` orchestrates multiple execution backends with automatic fallback.
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
import { SandboxManager } from '@cogitator-ai/sandbox';
|
|
50
|
+
|
|
51
|
+
const manager = new SandboxManager({
|
|
52
|
+
docker: {
|
|
53
|
+
socketPath: '/var/run/docker.sock',
|
|
54
|
+
},
|
|
55
|
+
pool: {
|
|
56
|
+
maxSize: 10,
|
|
57
|
+
idleTimeoutMs: 120_000,
|
|
58
|
+
},
|
|
59
|
+
defaults: {
|
|
60
|
+
timeout: 30_000,
|
|
61
|
+
resources: {
|
|
62
|
+
memory: '256MB',
|
|
63
|
+
cpus: 1,
|
|
64
|
+
},
|
|
65
|
+
network: { mode: 'none' },
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
await manager.initialize();
|
|
70
|
+
|
|
71
|
+
const result = await manager.execute(
|
|
72
|
+
{
|
|
73
|
+
command: ['node', '-e', 'console.log(2+2)'],
|
|
74
|
+
timeout: 5000,
|
|
75
|
+
env: { NODE_ENV: 'production' },
|
|
76
|
+
cwd: '/workspace',
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
type: 'docker',
|
|
80
|
+
image: 'node:20-alpine',
|
|
81
|
+
}
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
if (result.success) {
|
|
85
|
+
console.log('Output:', result.data.stdout);
|
|
86
|
+
console.log('Exit code:', result.data.exitCode);
|
|
87
|
+
console.log('Duration:', result.data.duration, 'ms');
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Availability Checks
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
const dockerAvailable = await manager.isDockerAvailable();
|
|
95
|
+
const wasmAvailable = await manager.isWasmAvailable();
|
|
96
|
+
|
|
97
|
+
console.log('Docker:', dockerAvailable);
|
|
98
|
+
console.log('WASM:', wasmAvailable);
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Shutdown
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
await manager.shutdown();
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Docker Executor
|
|
110
|
+
|
|
111
|
+
Full container isolation with security hardening.
|
|
18
112
|
|
|
19
113
|
```typescript
|
|
20
114
|
import { DockerSandboxExecutor } from '@cogitator-ai/sandbox';
|
|
21
115
|
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
116
|
+
const docker = new DockerSandboxExecutor({
|
|
117
|
+
docker: {
|
|
118
|
+
socketPath: '/var/run/docker.sock',
|
|
119
|
+
},
|
|
120
|
+
pool: {
|
|
121
|
+
maxSize: 5,
|
|
122
|
+
idleTimeoutMs: 60_000,
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
const connectResult = await docker.connect();
|
|
127
|
+
if (!connectResult.success) {
|
|
128
|
+
console.error('Docker not available:', connectResult.error);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const result = await docker.execute(
|
|
132
|
+
{
|
|
133
|
+
command: ['python', '-c', 'print("Hello!")'],
|
|
134
|
+
stdin: 'input data',
|
|
135
|
+
timeout: 10_000,
|
|
136
|
+
cwd: '/app',
|
|
137
|
+
env: { MY_VAR: 'value' },
|
|
28
138
|
},
|
|
139
|
+
{
|
|
140
|
+
type: 'docker',
|
|
141
|
+
image: 'python:3.11-alpine',
|
|
142
|
+
timeout: 30_000,
|
|
143
|
+
resources: {
|
|
144
|
+
memory: '512MB',
|
|
145
|
+
cpus: 2,
|
|
146
|
+
pidsLimit: 50,
|
|
147
|
+
},
|
|
148
|
+
network: {
|
|
149
|
+
mode: 'none',
|
|
150
|
+
},
|
|
151
|
+
mounts: [{ source: '/tmp/data', target: '/data', readOnly: true }],
|
|
152
|
+
env: { GLOBAL_VAR: 'value' },
|
|
153
|
+
workdir: '/workspace',
|
|
154
|
+
user: 'nobody',
|
|
155
|
+
}
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
await docker.disconnect();
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Security Features
|
|
162
|
+
|
|
163
|
+
Docker containers run with these security settings:
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
{
|
|
167
|
+
NetworkMode: 'none', // No network access
|
|
168
|
+
CapDrop: ['ALL'], // Drop all capabilities
|
|
169
|
+
SecurityOpt: ['no-new-privileges'], // No privilege escalation
|
|
170
|
+
PidsLimit: 100, // Limit process count
|
|
171
|
+
ReadonlyRootfs: false, // Writable (can enable true)
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## Container Pool
|
|
178
|
+
|
|
179
|
+
Reuse warm containers for faster execution.
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
import { ContainerPool } from '@cogitator-ai/sandbox';
|
|
183
|
+
|
|
184
|
+
const pool = new ContainerPool(dockerClient, {
|
|
185
|
+
maxSize: 10,
|
|
186
|
+
idleTimeoutMs: 60_000,
|
|
29
187
|
});
|
|
30
188
|
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
189
|
+
const container = await pool.acquire('python:3.11-alpine', {
|
|
190
|
+
memory: 256 * 1024 * 1024,
|
|
191
|
+
cpus: 1,
|
|
192
|
+
networkMode: 'none',
|
|
193
|
+
mounts: [],
|
|
34
194
|
});
|
|
35
195
|
|
|
36
|
-
|
|
196
|
+
await pool.release(container);
|
|
197
|
+
|
|
198
|
+
await pool.destroyAll();
|
|
37
199
|
```
|
|
38
200
|
|
|
39
|
-
###
|
|
201
|
+
### Pool Options
|
|
202
|
+
|
|
203
|
+
| Option | Type | Default | Description |
|
|
204
|
+
| --------------- | -------- | ------- | -------------------------------------- |
|
|
205
|
+
| `maxSize` | `number` | `5` | Maximum containers to keep warm |
|
|
206
|
+
| `idleTimeoutMs` | `number` | `60000` | Time before destroying idle containers |
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## WASM Executor
|
|
211
|
+
|
|
212
|
+
Execute WebAssembly modules via Extism.
|
|
40
213
|
|
|
41
214
|
```typescript
|
|
42
215
|
import { WasmSandboxExecutor } from '@cogitator-ai/sandbox';
|
|
43
216
|
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
217
|
+
const wasm = new WasmSandboxExecutor({
|
|
218
|
+
wasm: {
|
|
219
|
+
cacheDir: '/tmp/wasm-cache',
|
|
220
|
+
allowNetwork: false,
|
|
221
|
+
memoryLimit: 64,
|
|
222
|
+
},
|
|
47
223
|
});
|
|
48
224
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
225
|
+
await wasm.connect();
|
|
226
|
+
|
|
227
|
+
const result = await wasm.execute(
|
|
228
|
+
{
|
|
229
|
+
command: ['process'],
|
|
230
|
+
stdin: JSON.stringify({ data: 'input' }),
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
type: 'wasm',
|
|
234
|
+
wasm: {
|
|
235
|
+
url: 'https://example.com/plugin.wasm',
|
|
236
|
+
hash: 'sha256:abc123...',
|
|
237
|
+
},
|
|
238
|
+
timeout: 5000,
|
|
239
|
+
}
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
await wasm.disconnect();
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## Native Executor
|
|
248
|
+
|
|
249
|
+
Direct execution without isolation (fallback mode).
|
|
250
|
+
|
|
251
|
+
```typescript
|
|
252
|
+
import { NativeSandboxExecutor } from '@cogitator-ai/sandbox';
|
|
253
|
+
|
|
254
|
+
const native = new NativeSandboxExecutor();
|
|
255
|
+
await native.connect();
|
|
256
|
+
|
|
257
|
+
const result = await native.execute(
|
|
258
|
+
{
|
|
259
|
+
command: ['ls', '-la'],
|
|
260
|
+
cwd: '/tmp',
|
|
261
|
+
env: { LC_ALL: 'C' },
|
|
262
|
+
timeout: 5000,
|
|
263
|
+
},
|
|
264
|
+
{ type: 'native' }
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
console.log(result.data?.stdout);
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
**Warning:** Native execution has no isolation. Use only when Docker is unavailable.
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## Execution Request
|
|
275
|
+
|
|
276
|
+
```typescript
|
|
277
|
+
interface SandboxExecutionRequest {
|
|
278
|
+
command: string[];
|
|
279
|
+
stdin?: string;
|
|
280
|
+
timeout?: number;
|
|
281
|
+
cwd?: string;
|
|
282
|
+
env?: Record<string, string>;
|
|
283
|
+
}
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
## Execution Result
|
|
287
|
+
|
|
288
|
+
```typescript
|
|
289
|
+
interface SandboxExecutionResult {
|
|
290
|
+
stdout: string;
|
|
291
|
+
stderr: string;
|
|
292
|
+
exitCode: number;
|
|
293
|
+
timedOut: boolean;
|
|
294
|
+
duration: number;
|
|
295
|
+
}
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
---
|
|
299
|
+
|
|
300
|
+
## Resource Limits
|
|
301
|
+
|
|
302
|
+
### Memory
|
|
303
|
+
|
|
304
|
+
```typescript
|
|
305
|
+
const result = await manager.execute(request, {
|
|
306
|
+
type: 'docker',
|
|
307
|
+
image: 'alpine',
|
|
308
|
+
resources: {
|
|
309
|
+
memory: '256MB',
|
|
310
|
+
},
|
|
52
311
|
});
|
|
53
312
|
```
|
|
54
313
|
|
|
55
|
-
|
|
314
|
+
Supported formats: `'256B'`, `'256KB'`, `'256MB'`, `'256GB'`
|
|
56
315
|
|
|
57
|
-
|
|
316
|
+
### CPU
|
|
58
317
|
|
|
59
318
|
```typescript
|
|
60
|
-
|
|
319
|
+
{
|
|
320
|
+
resources: {
|
|
321
|
+
cpus: 0.5,
|
|
322
|
+
cpuShares: 512,
|
|
323
|
+
},
|
|
324
|
+
}
|
|
325
|
+
```
|
|
61
326
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
327
|
+
### Process Limits
|
|
328
|
+
|
|
329
|
+
```typescript
|
|
330
|
+
{
|
|
331
|
+
resources: {
|
|
332
|
+
pidsLimit: 50,
|
|
333
|
+
},
|
|
334
|
+
}
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
---
|
|
338
|
+
|
|
339
|
+
## Network Configuration
|
|
340
|
+
|
|
341
|
+
```typescript
|
|
342
|
+
{
|
|
343
|
+
network: {
|
|
344
|
+
mode: 'none',
|
|
345
|
+
},
|
|
346
|
+
}
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
Network modes:
|
|
350
|
+
|
|
351
|
+
- `'none'` - No network access (default, most secure)
|
|
352
|
+
- `'bridge'` - Docker bridge network
|
|
353
|
+
- `'host'` - Host network (not recommended)
|
|
354
|
+
|
|
355
|
+
---
|
|
356
|
+
|
|
357
|
+
## Volume Mounts
|
|
358
|
+
|
|
359
|
+
Mount host directories into the container:
|
|
360
|
+
|
|
361
|
+
```typescript
|
|
362
|
+
{
|
|
363
|
+
mounts: [
|
|
364
|
+
{ source: '/host/data', target: '/data', readOnly: true },
|
|
365
|
+
{ source: '/host/output', target: '/output', readOnly: false },
|
|
366
|
+
],
|
|
367
|
+
}
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
---
|
|
371
|
+
|
|
372
|
+
## Utility Functions
|
|
373
|
+
|
|
374
|
+
### Parse Memory
|
|
375
|
+
|
|
376
|
+
```typescript
|
|
377
|
+
import { parseMemory } from '@cogitator-ai/sandbox';
|
|
378
|
+
|
|
379
|
+
parseMemory('256MB');
|
|
380
|
+
parseMemory('1GB');
|
|
381
|
+
parseMemory('512KB');
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
### CPU to NanoCPUs
|
|
385
|
+
|
|
386
|
+
```typescript
|
|
387
|
+
import { cpusToNanoCpus } from '@cogitator-ai/sandbox';
|
|
388
|
+
|
|
389
|
+
cpusToNanoCpus(0.5);
|
|
390
|
+
cpusToNanoCpus(2);
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
---
|
|
394
|
+
|
|
395
|
+
## Type Reference
|
|
396
|
+
|
|
397
|
+
```typescript
|
|
398
|
+
import type {
|
|
399
|
+
SandboxType,
|
|
400
|
+
SandboxConfig,
|
|
401
|
+
SandboxResourceLimits,
|
|
402
|
+
SandboxNetworkConfig,
|
|
403
|
+
SandboxMount,
|
|
404
|
+
SandboxExecutionRequest,
|
|
405
|
+
SandboxExecutionResult,
|
|
406
|
+
SandboxManagerConfig,
|
|
407
|
+
SandboxPoolConfig,
|
|
408
|
+
SandboxDockerConfig,
|
|
409
|
+
SandboxResult,
|
|
410
|
+
} from '@cogitator-ai/sandbox';
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
---
|
|
414
|
+
|
|
415
|
+
## Integration with Cogitator
|
|
416
|
+
|
|
417
|
+
Use sandboxed tools in your agents:
|
|
418
|
+
|
|
419
|
+
```typescript
|
|
420
|
+
import { Cogitator, tool } from '@cogitator-ai/core';
|
|
421
|
+
import { z } from 'zod';
|
|
422
|
+
|
|
423
|
+
const shellTool = tool({
|
|
424
|
+
name: 'run_shell',
|
|
425
|
+
description: 'Execute shell commands safely',
|
|
426
|
+
parameters: z.object({
|
|
427
|
+
command: z.string(),
|
|
428
|
+
}),
|
|
429
|
+
sandbox: {
|
|
430
|
+
type: 'docker',
|
|
431
|
+
image: 'ubuntu:22.04',
|
|
432
|
+
resources: { memory: '256MB' },
|
|
433
|
+
network: { mode: 'none' },
|
|
434
|
+
},
|
|
435
|
+
timeout: 30000,
|
|
436
|
+
execute: async ({ command }) => command,
|
|
65
437
|
});
|
|
66
438
|
|
|
67
|
-
|
|
68
|
-
|
|
439
|
+
const cog = new Cogitator({
|
|
440
|
+
sandbox: {
|
|
441
|
+
pool: { maxSize: 5 },
|
|
442
|
+
},
|
|
443
|
+
});
|
|
69
444
|
```
|
|
70
445
|
|
|
71
|
-
|
|
446
|
+
---
|
|
447
|
+
|
|
448
|
+
## Examples
|
|
449
|
+
|
|
450
|
+
### Run Python Code
|
|
72
451
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
452
|
+
```typescript
|
|
453
|
+
const result = await manager.execute(
|
|
454
|
+
{
|
|
455
|
+
command: [
|
|
456
|
+
'python',
|
|
457
|
+
'-c',
|
|
458
|
+
`
|
|
459
|
+
import json
|
|
460
|
+
data = {"sum": 2 + 2}
|
|
461
|
+
print(json.dumps(data))
|
|
462
|
+
`,
|
|
463
|
+
],
|
|
464
|
+
},
|
|
465
|
+
{
|
|
466
|
+
type: 'docker',
|
|
467
|
+
image: 'python:3.11-alpine',
|
|
468
|
+
timeout: 10_000,
|
|
469
|
+
}
|
|
470
|
+
);
|
|
78
471
|
|
|
79
|
-
|
|
472
|
+
const output = JSON.parse(result.data!.stdout);
|
|
473
|
+
console.log(output.sum);
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
### Run Node.js Code
|
|
477
|
+
|
|
478
|
+
```typescript
|
|
479
|
+
const result = await manager.execute(
|
|
480
|
+
{
|
|
481
|
+
command: ['node', '-e', 'console.log(JSON.stringify({result: 42}))'],
|
|
482
|
+
},
|
|
483
|
+
{
|
|
484
|
+
type: 'docker',
|
|
485
|
+
image: 'node:20-alpine',
|
|
486
|
+
resources: { memory: '128MB' },
|
|
487
|
+
}
|
|
488
|
+
);
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
### Run Shell Commands
|
|
492
|
+
|
|
493
|
+
```typescript
|
|
494
|
+
const result = await manager.execute(
|
|
495
|
+
{
|
|
496
|
+
command: ['sh', '-c', 'ls -la /workspace && pwd'],
|
|
497
|
+
cwd: '/workspace',
|
|
498
|
+
},
|
|
499
|
+
{
|
|
500
|
+
type: 'docker',
|
|
501
|
+
image: 'alpine:3.19',
|
|
502
|
+
}
|
|
503
|
+
);
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
### Handle Timeouts
|
|
507
|
+
|
|
508
|
+
```typescript
|
|
509
|
+
const result = await manager.execute(
|
|
510
|
+
{ command: ['sleep', '60'] },
|
|
511
|
+
{ type: 'docker', image: 'alpine', timeout: 5000 }
|
|
512
|
+
);
|
|
513
|
+
|
|
514
|
+
if (result.data?.timedOut) {
|
|
515
|
+
console.log('Command timed out');
|
|
516
|
+
}
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
### Check Exit Codes
|
|
520
|
+
|
|
521
|
+
```typescript
|
|
522
|
+
const result = await manager.execute(
|
|
523
|
+
{ command: ['sh', '-c', 'exit 42'] },
|
|
524
|
+
{ type: 'docker', image: 'alpine' }
|
|
525
|
+
);
|
|
526
|
+
|
|
527
|
+
console.log('Exit code:', result.data?.exitCode);
|
|
528
|
+
```
|
|
80
529
|
|
|
81
|
-
|
|
530
|
+
---
|
|
82
531
|
|
|
83
532
|
## License
|
|
84
533
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cogitator-ai/sandbox",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Sandbox execution for Cogitator agents (Docker and WASM)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"@types/dockerode": "^3.3.31",
|
|
19
19
|
"@types/node": "^25.0.0",
|
|
20
20
|
"nanoid": "^5.0.4",
|
|
21
|
-
"@cogitator-ai/types": "0.
|
|
21
|
+
"@cogitator-ai/types": "0.4.0"
|
|
22
22
|
},
|
|
23
23
|
"optionalDependencies": {
|
|
24
24
|
"@extism/extism": "^1.0.3",
|