Skip to content
2,486 changes: 2,189 additions & 297 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,11 @@
"dependencies": {
"@agentclientprotocol/sdk": "^0.19.0",
"@boxlite-ai/boxlite": "^0.4.3",
"@types/dockerode": "^4.0.1",
"better-sqlite3": "^11.8.1",
"cron-parser": "^5.5.0",
"dockerode": "^5.0.0",
"esbuild": "^0.28.0",
"grammy": "^1.39.3",
"nanoid": "^3.3.11",
"pino": "^9.6.0",
Expand Down
102 changes: 102 additions & 0 deletions src/actions/code-run.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import Docker from 'dockerode';
import { beforeAll, describe, expect, it } from 'vitest';

import { runCode } from './code-run.js';
import { prePullSandboxImages } from './sandbox-images.js';

const docker = new Docker();

async function dockerAvailable(): Promise<boolean> {
try {
await docker.ping();
return true;
} catch {
return false;
}
}

describe('code_run action', () => {
beforeAll(async () => {
if (!(await dockerAvailable())) {
throw new Error('Docker is required for code_run tests');
}
await prePullSandboxImages();
}, 120_000);

it('runs JavaScript', async () => {
const result = await runCode({
language: 'javascript',
code: "console.log('hello')",
});

expect(result.stdout).toBe('hello\n');
expect(result.stderr).toBe('');
expect(result.exit_code).toBe(0);
});

it('runs TypeScript', async () => {
const result = await runCode({
language: 'typescript',
code: "const msg: string = 'hello ts'; console.log(msg)",
});

expect(result.stdout).toBe('hello ts\n');
expect(result.stderr).toBe('');
expect(result.exit_code).toBe(0);
});

it('runs Python', async () => {
const result = await runCode({
language: 'python',
code: "print('world')",
});

expect(result.stdout).toBe('world\n');
expect(result.stderr).toBe('');
expect(result.exit_code).toBe(0);
});

it('runs Bash', async () => {
const result = await runCode({
language: 'bash',
code: 'echo test',
});

expect(result.stdout).toBe('test\n');
expect(result.stderr).toBe('');
expect(result.exit_code).toBe(0);
});

it('times out long-running code', async () => {
const result = await runCode({
language: 'python',
code: 'while True: pass',
timeout_ms: 1000,
});

expect(result.exit_code).toBe(124);
expect(result.stderr).toContain('Execution timed out');
});

it.skipIf(process.env.CI === 'true' || process.platform === 'darwin')(
'enforces the memory limit',
async () => {
const result = await runCode({
language: 'python',
code: 'x = bytearray(1024 * 1024 * 1024); print(len(x))',
});

expect(result.exit_code).not.toBe(0);
},
);

it('blocks network access', async () => {
const result = await runCode({
language: 'bash',
code: 'wget -qO- -T 1 http://1.1.1.1',
timeout_ms: 2000,
});

expect(result.exit_code).not.toBe(0);
});
});
Loading
Loading