feat: soft disable LOCAL code interpreter driver in production (#16647)

## Summary

Instead of throwing an error at server startup when LOCAL code
interpreter is configured in production, we now return a DisabledDriver
that only throws when the code interpreter is actually used.

## Changes

- Created `DisabledDriver` class that implements `CodeInterpreterDriver`
but throws an error only when `execute()` is called
- Added `DISABLED` to the `CodeInterpreterDriverType` enum
- Updated the factory to return a `DISABLED` driver config instead of
throwing when LOCAL is used in production
- Updated the module to handle the new `DISABLED` driver type

## Motivation

Many users don't need the code interpreter feature and want to deploy to
production without configuring E2B. Previously, the server would crash
at startup if `CODE_INTERPRETER_TYPE=LOCAL` was set in production.

**Before:** Server crashes at startup in production if
`CODE_INTERPRETER_TYPE=LOCAL`

**After:** Server starts fine. The error only occurs if someone actually
tries to **use** the code interpreter feature, at which point they get a
clear error message explaining how to configure E2B.
This commit is contained in:
Félix Malfait
2025-12-18 08:03:13 +01:00
committed by GitHub
parent e5f655fcae
commit 75ae2b401b
6 changed files with 56 additions and 9 deletions
@@ -18,9 +18,13 @@ export const codeInterpreterModuleFactory = async (
const nodeEnv = twentyConfigService.get('NODE_ENV');
if (nodeEnv === NodeEnvironment.PRODUCTION) {
throw new Error(
'LOCAL code interpreter driver is not allowed in production. Use E2B driver instead by setting CODE_INTERPRETER_TYPE=E2B and providing E2B_API_KEY.',
);
return {
type: CodeInterpreterDriverType.DISABLED,
options: {
reason:
'LOCAL code interpreter driver is not allowed in production. Use E2B driver instead by setting CODE_INTERPRETER_TYPE=E2B and providing E2B_API_KEY.',
},
};
}
return {
@@ -45,6 +49,15 @@ export const codeInterpreterModuleFactory = async (
},
};
}
case CodeInterpreterDriverType.DISABLED: {
return {
type: CodeInterpreterDriverType.DISABLED,
options: {
reason:
'Code interpreter is disabled. Set CODE_INTERPRETER_TYPE to LOCAL (development only) or E2B to enable it.',
},
};
}
default:
throw new Error(
`Invalid code interpreter driver type (${driverType}), check your .env file`,
@@ -6,6 +6,7 @@ import { type LocalDriverOptions } from './drivers/local.driver';
export enum CodeInterpreterDriverType {
LOCAL = 'LOCAL',
E_2_B = 'E_2_B',
DISABLED = 'DISABLED',
}
export type LocalDriverFactoryOptions = {
@@ -18,9 +19,15 @@ export type E2BDriverFactoryOptions = {
options: E2BDriverOptions;
};
export type DisabledDriverFactoryOptions = {
type: CodeInterpreterDriverType.DISABLED;
options: { reason: string };
};
export type CodeInterpreterModuleOptions =
| LocalDriverFactoryOptions
| E2BDriverFactoryOptions;
| E2BDriverFactoryOptions
| DisabledDriverFactoryOptions;
export type CodeInterpreterModuleAsyncOptions = {
useFactory: (
@@ -7,6 +7,7 @@ import {
} from './code-interpreter.interface';
import { CodeInterpreterService } from './code-interpreter.service';
import { DisabledDriver } from './drivers/disabled.driver';
import { E2BDriver } from './drivers/e2b.driver';
import { LocalDriver } from './drivers/local.driver';
@@ -20,9 +21,14 @@ export class CodeInterpreterModule {
useFactory: async (...args: unknown[]) => {
const config = await options.useFactory(...args);
return config.type === CodeInterpreterDriverType.LOCAL
? new LocalDriver(config.options)
: new E2BDriver(config.options);
switch (config.type) {
case CodeInterpreterDriverType.LOCAL:
return new LocalDriver(config.options);
case CodeInterpreterDriverType.E_2_B:
return new E2BDriver(config.options);
case CodeInterpreterDriverType.DISABLED:
return new DisabledDriver(config.options.reason);
}
},
inject: options.inject ?? [],
};
@@ -0,0 +1,20 @@
import {
type CodeExecutionResult,
type CodeInterpreterDriver,
type ExecutionContext,
type InputFile,
type StreamCallbacks,
} from './interfaces/code-interpreter-driver.interface';
export class DisabledDriver implements CodeInterpreterDriver {
constructor(private reason: string) {}
async execute(
_code: string,
_files?: InputFile[],
_context?: ExecutionContext,
_callbacks?: StreamCallbacks,
): Promise<CodeExecutionResult> {
throw new Error(this.reason);
}
}