Cluster SDK
Remote development environments and distributed job execution across Linux nodes.
Cluster SDK
The Cluster SDK (@codmir/cluster-sdk) enables remote development environments and distributed job execution across a pool of Linux machines.
Installation
pnpm add @codmir/cluster-sdkQuick Start
import { createClusterClient } from '@codmir/cluster-sdk';
const client = createClusterClient({
baseUrl: 'https://codmir.com/api/cluster',
token: 'your-auth-token',
});
// Create a dev session (SSH mode)
const session = await client.sessions.create({
repoUrl: 'https://github.com/org/repo',
mode: 'ssh',
});
// Get SSH command
console.log(session.sshInfo?.sshCommand);
// ssh -p 22 codmir@192.168.1.10 -t "cd /var/codmir/workspaces/sess_abc123/repo && exec $SHELL -l"Two IDE Modes
SSH Mode
Use your local IDE (VS Code, Windsurf, JetBrains) with remote workspace:
const session = await client.sessions.create({
repoUrl: 'https://github.com/org/repo',
mode: 'ssh',
});
// Copy to clipboard
console.log(session.sshInfo?.sshCommand);
// Or get VS Code SSH config
console.log(session.sshInfo?.vscodeSshConfig);
// Host codmir-sess_abc123
// HostName 192.168.1.10
// Port 22
// User codmirBrowser Mode
VS Code in your browser via code-server:
const session = await client.sessions.create({
repoUrl: 'https://github.com/org/repo',
mode: 'browser',
});
// Open in browser
window.open(session.browserInfo?.ideUrl);
// Password if required
console.log(session.browserInfo?.password);Running Jobs
Execute builds, tests, and other tasks on the cluster:
const job = await client.jobs.run({
repoUrl: 'https://github.com/org/repo',
command: 'pnpm install && pnpm build',
});
console.log(job.id, job.status);Stream Job Logs
const abort = client.jobs.streamLogs(job.id, {
onEvent: (event) => {
if ('line' in event) {
console.log(`[${event.stream}] ${event.line}`);
}
if ('status' in event) {
console.log(`Job ${event.status}`);
}
},
onClose: () => console.log('Stream closed'),
});
// Cancel streaming
abort();Job with Patch
Apply uncommitted changes before running:
const job = await client.jobs.run({
repoUrl: 'https://github.com/org/repo',
command: 'pnpm test',
patch: `
diff --git a/src/app.ts b/src/app.ts
--- a/src/app.ts
+++ b/src/app.ts
@@ -1 +1 @@
-const x = 1;
+const x = 2;
`,
});API Reference
Sessions
// Create session
client.sessions.create({
repoUrl: string; // Git repository URL
ref?: string; // Branch, tag, or commit
mode: 'ssh' | 'browser';
labels?: string[]; // Prefer nodes with labels
}): Promise<ClusterSession>
// Get session
client.sessions.get(sessionId: string): Promise<ClusterSession>
// Terminate session
client.sessions.terminate(sessionId: string, force?: boolean): Promise<void>
// List user's sessions
client.sessions.list(): Promise<ClusterSession[]>Jobs
// Run job
client.jobs.run({
repoUrl: string;
ref?: string;
command: string;
cwd?: string; // Relative to workspace
env?: Record<string, string>;
timeout?: number; // ms, default 300000
patch?: string; // Git diff to apply
sessionId?: string; // Run on session's node
}): Promise<ClusterJob>
// Get job
client.jobs.get(jobId: string): Promise<ClusterJob>
// Cancel job
client.jobs.cancel(jobId: string, reason?: string): Promise<void>
// Stream logs (SSE)
client.jobs.streamLogs(jobId: string, handler: {
onEvent: (event: JobLogEvent | JobStatusEvent) => void;
onError?: (error: Error) => void;
onClose?: () => void;
}): () => void // Returns abort functionNodes
// List nodes
client.nodes.list(): Promise<ClusterNode[]>
// Get node
client.nodes.get(nodeId: string): Promise<ClusterNode>Types
ClusterSession
interface ClusterSession {
id: string;
userId: string;
nodeId: string;
mode: 'ssh' | 'browser';
status: 'pending' | 'provisioning' | 'ready' | 'active' | 'terminating' | 'terminated' | 'error';
context: {
repoUrl: string;
ref?: string;
};
workspacePath: string;
sshInfo?: {
host: string;
port: number;
user: string;
workspacePath: string;
sshCommand: string;
vscodeSshConfig: string;
};
browserInfo?: {
ideUrl: string;
password?: string;
};
createdAt: Date;
error?: string;
}ClusterJob
interface ClusterJob {
id: string;
userId: string;
nodeId?: string;
sessionId?: string;
status: 'queued' | 'assigned' | 'running' | 'completed' | 'failed' | 'cancelled';
request: {
repoUrl: string;
command: string;
// ...
};
exitCode?: number;
output?: string;
startedAt?: Date;
completedAt?: Date;
error?: string;
}ClusterNode
interface ClusterNode {
id: string;
hostname: string;
status: 'online' | 'offline' | 'busy' | 'draining' | 'maintenance';
labels: string[];
metrics?: {
cpuUsage: number;
memoryUsedMb: number;
memoryTotalMb: number;
loadAverage: [number, number, number];
runningSessions: number;
runningJobs: number;
};
}Helpers
import {
generateSshCommand,
generateVscodeSshConfig,
getBrowserIdeUrl,
} from '@codmir/cluster-sdk';
// Generate SSH command
const cmd = generateSshCommand(session);
// Generate VS Code config stanza
const config = generateVscodeSshConfig(session);
// Get browser IDE URL
const url = getBrowserIdeUrl(session);Error Handling
import { ClusterApiError, ClusterConnectionError } from '@codmir/cluster-sdk';
try {
await client.sessions.create({ ... });
} catch (error) {
if (error instanceof ClusterApiError) {
// API returned error response
console.log(error.status); // 400, 401, 404, etc.
console.log(error.message);
console.log(error.details);
}
if (error instanceof ClusterConnectionError) {
// Network/connection error
console.log(error.message);
console.log(error.cause);
}
}Configuration
const client = createClusterClient({
baseUrl: string; // API base URL
token?: string; // Auth token
timeout?: number; // Request timeout (default 30000)
onError?: (error: Error) => void; // Global error handler
});Deployment Modes
Local Mode (Self-hosted)
Direct SSH execution from the Next.js app:
CLUSTER_EXECUTION_MODE=localBest for development and self-hosted deployments with no timeout constraints.
Cloud Mode (Vercel + Railway)
Jobs queued in Redis, executed by Railway worker:
CLUSTER_EXECUTION_MODE=cloud
CLUSTER_WORKER_URL=https://your-worker.railway.app
CLUSTER_WEBHOOK_SECRET=your-secret
REDIS_URL=redis://...Best for production Vercel deployments where jobs may exceed the 60s timeout.
Server Setup
See Cluster Documentation for setting up cluster nodes and Railway workers.