Skip to main content

Build Your First Execution Backend

Create a custom execution backend (provider) that manages remote environments where agents run. Your provider handles environment creation, teardown, file operations, and message transport.

Prerequisites: Node.js 18+, npm, familiarity with the target execution environment (Docker, cloud sandbox, remote server, etc.).


Step 1: Create the provider project​

mkdir my-provider && cd my-provider
npm init -y
npm install @codebolt/codeboltjs @codebolt/provider
npm install -D typescript

Create tsconfig.json:

{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"outDir": "dist",
"strict": true,
"esModuleInterop": true
},
"include": ["src"]
}

Add a build script to package.json:

{
"scripts": {
"build": "tsc"
}
}

Step 2: Extend BaseProvider​

Create src/MyProvider.ts. Extend BaseProvider and implement the required abstract methods:

import { BaseProvider } from '@codebolt/provider';
import type { ProviderInitVars, ProviderStartResult } from '@codebolt/provider';

export class MyProvider extends BaseProvider {

// Create or connect to the remote environment
async setupEnvironment(initVars: ProviderInitVars): Promise<void> {
console.log('[MyProvider] Setting up environment...');
// Example: create a Docker container, start a VM, connect to a sandbox
// Store any resource IDs for cleanup later
}

// Clean up the environment on shutdown
async teardownEnvironment(): Promise<void> {
console.log('[MyProvider] Tearing down environment...');
// Example: stop and remove the container/VM/sandbox
}

// Resolve project paths and metadata
async resolveProjectContext(initVars: ProviderInitVars): Promise<any> {
return {
projectPath: initVars.projectPath,
projectName: initVars.projectPath?.split('/').pop() || 'project',
};
}

// Resolve the workspace directory in the remote environment
async resolveWorkspacePath(initVars: ProviderInitVars): Promise<string> {
return initVars.projectPath || '/workspace';
}

// Return file changes in the environment
async onGetDiffFiles(): Promise<any> {
return { files: [] };
}
}

Optional: Override hooks for advanced behavior​

export class MyProvider extends BaseProvider {
// ... abstract methods above ...

// Start the agent server inside the remote environment
async ensureAgentServer(): Promise<void> {
// Example: start CodeBolt inside a container and wait for it to be ready
}

// Custom health check
async checkEnvironmentHealth(): Promise<boolean> {
// Example: run 'echo OK' in the sandbox and check the response
return true;
}

// Clean up orphaned resources from a previous crashed session
async onEnvironmentRecoveryFailed(resourceId: string): Promise<void> {
// Example: kill an orphaned sandbox by its persisted ID
}
}

Step 3: Register handlers with the SDK​

Create src/index.ts. This is the entry point — it creates your provider and registers all handlers:

import codebolt from "@codebolt/codeboltjs";
import { MyProvider } from "./MyProvider";

// Create provider instance with configuration
const provider = new MyProvider({
agentServerPort: parseInt(process.env.REMOTE_EXECUTION_PORT || '3100', 10),
agentServerHost: 'localhost',
reconnectAttempts: 5,
reconnectDelay: 1000,
transport: 'websocket',
});

// Get lifecycle handlers
const handlers = provider.getEventHandlers();

// Register lifecycle handlers
codebolt.onProviderStart(handlers.onProviderStart);
codebolt.onProviderAgentStart(handlers.onProviderAgentStart);
codebolt.onProviderStop(handlers.onProviderStop);
codebolt.onCloseSignal(handlers.onCloseSignal);
codebolt.onRawMessage(handlers.onRawMessage);
codebolt.onGetDiffFiles(handlers.onGetDiffFiles);

// Register file operation handlers
codebolt.onReadFile(provider.onReadFile.bind(provider));
codebolt.onWriteFile(provider.onWriteFile.bind(provider));
codebolt.onDeleteFile(provider.onDeleteFile.bind(provider));
codebolt.onDeleteFolder(provider.onDeleteFolder.bind(provider));
codebolt.onRenameItem(provider.onRenameItem.bind(provider));
codebolt.onCreateFolder(provider.onCreateFolder.bind(provider));
codebolt.onCopyFile(provider.onCopyFile.bind(provider));
codebolt.onCopyFolder(provider.onCopyFolder.bind(provider));
codebolt.onGetTreeChildren(provider.onGetProject.bind(provider));
codebolt.onGetFullProject(provider.onGetFullProject.bind(provider));

Step 4: Implement file operations​

If your environment needs custom file handling (e.g., files live in a remote sandbox), override the file operation methods:

export class MyProvider extends BaseProvider {
// ... lifecycle methods ...

async onReadFile(params: any): Promise<any> {
// Read file from remote environment
// Example: execute 'cat <path>' in sandbox, or use an API
const content = await this.remoteExec(`cat ${params.path}`);
return { content };
}

async onWriteFile(params: any): Promise<any> {
// Write file to remote environment
await this.remoteWrite(params.path, params.content);
return { success: true };
}

async onDeleteFile(params: any): Promise<any> {
await this.remoteExec(`rm ${params.path}`);
return { success: true };
}

async onCreateFolder(params: any): Promise<any> {
await this.remoteExec(`mkdir -p ${params.path}`);
return { success: true };
}
}

If your environment uses the local filesystem (e.g., git worktree), you can skip these — BaseProvider provides default implementations.


Step 5: Build and install​

Build​

npm run build

Install​

Providers can be installed from the marketplace (UI) or locally:

# Copy to a location the server can discover
cp -r my-provider ~/.codebolt/providers/my-provider

Or install via the server API:

curl -X POST http://localhost:2719/environments/installed-providers \
-H "Content-Type: application/json" \
-d '{
"name": "my-provider",
"displayName": "My Custom Provider",
"type": "remote",
"description": "Custom execution backend"
}'

Configure​

  1. Open Settings in Codebolt.
  2. Go to Environment Providers.
  3. Find your provider and configure it (API keys, endpoints, etc.).
  4. Optionally set it as the default provider.

Test​

  1. Create a new environment using your provider.
  2. Run an agent — it should execute in your remote environment.
  3. Check that file operations work (read, write, create folder).
  4. Verify the agent can communicate back to the local platform.

Provider configuration​

The server passes these environment variables when spawning your provider:

VariableDescription
PROVIDER_IDUnique provider instance ID
ENVIRONMENT_NAMEName of the environment
PROJECT_PATHLocal project path
ENVIRONMENT_CONFIGJSON string with provider-specific config
REMOTE_EXECUTION_PORTPort for the agent server (default: 3100)

Your provider reads these from process.env or receives them in initVars.


What BaseProvider gives you​

You don't need to implement these — they're built into BaseProvider:

  • Message queueing — messages sent before transport is ready are queued and flushed.
  • WebSocket reconnection — exponential backoff, configurable max attempts.
  • Keepalive pings — prevents idle disconnects.
  • Request timeouts — automatic timeout for pending requests.
  • Resource ID persistence — persist sandbox/container IDs for crash recovery.
  • Health checks — periodic environment monitoring.

Project structure​

my-provider/
├── package.json
├── tsconfig.json
├── src/
│ ├── index.ts # Entry point — register handlers with SDK
│ └── MyProvider.ts # BaseProvider subclass — environment lifecycle
└── dist/
└── index.js

See Also​