| name | platxa-sidecar-builder |
| description | Build Node.js sidecar services for real-time code editing platforms. Covers file watching, git operations, WebSocket servers, Yjs CRDT integration, REST APIs, and Kubernetes deployment patterns. |
| allowed-tools | ["Read","Write","Edit","Glob","Grep","Bash","AskUserQuestion"] |
| suggests | ["platxa-logging","platxa-error-handling","platxa-testing","platxa-yjs-server"] |
| metadata | {"version":"1.0.0","tags":["builder","nodejs","sidecar","yjs","websocket","kubernetes"]} |
| user-invocable | true |
Platxa Sidecar Builder
Build Node.js sidecar services for real-time collaborative code editing platforms.
Overview
This skill helps create sidecar services that power code editors like Replit, CodeSandbox, or the Platxa IDE. A sidecar runs alongside the main application (e.g., Odoo) in the same Kubernetes pod, handling:
| Component | Purpose |
|---|
| File Watcher | Detect file changes with chokidar |
| Git Service | Version control with simple-git |
| WebSocket Server | Real-time sync with ws library |
| Yjs Integration | CRDT-based collaborative editing |
| REST API | File operations, deployment triggers |
| Process Manager | Execute odoo commands, module reloads |
Integrates with: platxa-yjs-server for Yjs server patterns.
Workflow
Step 1: Analyze Requirements
Determine what the sidecar needs to handle:
- File sync only → File watcher + REST API
- Real-time collaboration → Add WebSocket + Yjs
- Version control → Add Git service
- Deployment → Add Process manager
Step 2: Create Project Structure
sidecar/
├── src/
│ ├── index.ts # Entry point
│ ├── file-watcher.ts # Chokidar integration
│ ├── git-service.ts # Git operations
│ ├── websocket-server.ts # ws server
│ ├── yjs-service.ts # Yjs CRDT sync
│ ├── api.ts # Fastify REST API
│ ├── process-manager.ts # Child process handling
│ └── logging.ts # Pino structured logging
├── package.json
├── tsconfig.json
└── Dockerfile
Step 3: Implement Core Services
Use the templates below for each component.
Step 4: Add Kubernetes Deployment
Create Pod manifest with native sidecar pattern (K8s 1.33+).
Step 5: Validate Integration
- File changes sync to clients
- Git commits work correctly
- WebSocket reconnection handles network issues
- Graceful shutdown works
Templates
Entry Point
import { FileWatcher } from './file-watcher';
import { GitService } from './git-service';
import { WebSocketServer } from './websocket-server';
import { YjsService } from './yjs-service';
import { createApiServer } from './api';
import { logger } from './logging';
async function main() {
const workspacePath = process.env.WORKSPACE_PATH || '/workspace';
const apiPort = parseInt(process.env.API_PORT || '3000');
const wsPort = parseInt(process.env.WS_PORT || '3001');
const gitService = new GitService(workspacePath);
const yjsService = new YjsService();
const wsServer = new WebSocketServer(wsPort, yjsService);
fileWatcher = ({
workspacePath,
: [, , ],
});
gitService.();
wsServer.();
fileWatcher.( (path, ) => {
( === || === ) {
content = fs..(
,
);
yjsService.(path, content);
wsServer.({ : , path });
}
});
(apiPort, { gitService, yjsService, fileWatcher });
logger.({ workspacePath, apiPort, wsPort }, );
process.(, () => {
logger.();
fileWatcher.();
wsServer.();
process.();
});
}
().( {
logger.({ error }, );
process.();
});
File Watcher
import chokidar from 'chokidar';
import { logger } from './logging';
interface WatcherConfig {
workspacePath: string;
ignored?: string[];
}
export class FileWatcher {
private watcher: chokidar.FSWatcher;
private debounceTimers = new Map<string, NodeJS.Timeout>();
constructor(config: WatcherConfig) {
this.watcher = chokidar.watch(config.workspacePath, {
persistent: true,
ignored: config.ignored || [],
awaitWriteFinish: { stabilityThreshold: 500, pollInterval: 100 },
alwaysStat: true,
});
}
onChange(callback: (path: string, type: 'add' | 'change' | 'unlink') => void) {
= () => {
(..(path)) {
(..(path)!);
}
..(path, ( {
(path, );
..(path);
}, ));
};
..(, (path, ));
..(, (path, ));
..(, (path, ));
;
}
() {
( timer ..()) (timer);
..();
}
}
Git Service
import { simpleGit, SimpleGit } from 'simple-git';
import { logger } from './logging';
export class GitService {
private git: SimpleGit;
constructor(workspacePath: string) {
this.git = simpleGit({ baseDir: workspacePath, timeout: 30000 });
}
async initialize() {
await this.git.init();
await this.git.addConfig('user.name', 'Platxa Editor');
await this.git.addConfig('user.email', 'editor@platxa.local');
}
async commit(message: string): Promise<string> {
await this.git.add();
result = ..(message);
logger.({ : result. }, );
result.;
}
() {
..();
}
() {
path ? ..([path]) : ..();
}
() {
..({ : count });
}
}
WebSocket Server
import WebSocket, { Server } from 'ws';
import { logger } from './logging';
import { YjsService } from './yjs-service';
export class WebSocketServer {
private wss: Server;
private clients = new Map<WebSocket, { id: string; lastPing: number }>();
constructor(private port: number, private yjsService: YjsService) {
this.wss = new Server({ port });
}
async start() {
this.wss.on('connection', (ws, req) => {
const clientId = `client_${Date.now()}`;
this.clients.(ws, { : clientId, : .() });
logger.({ clientId, : .. }, );
ws.(, .(ws, data));
ws.(, .(ws));
ws.(, {
client = ..(ws);
(client) client. = .();
});
});
( {
( [ws, client] .) {
(.() - client. > ) {
ws.();
} {
ws.();
}
}
}, );
logger.({ : . }, );
}
() {
{
msg = .(data.());
(msg. === ) {
..(msg., (msg.));
.(msg, ws);
}
} (error) {
logger.({ error }, );
}
}
() {
client = ..(ws);
..(ws);
logger.({ : client?. }, );
}
() {
data = .(message);
( [ws] .) {
(ws !== exclude && ws. === .) {
ws.(data);
}
}
}
() {
( [ws] .) ws.();
..();
}
}
Kubernetes Deployment
apiVersion: v1
kind: Pod
suggests:
- platxa-logging
- platxa-error-handling
- platxa-testing
- platxa-yjs-server
metadata:
name: platxa-instance
spec:
containers:
- name: odoo
image: odoo:18.0
ports:
- containerPort: 8069
volumeMounts:
- name: workspace
mountPath: /mnt/addons
initContainers:
- name: editor-sync
image: platxa/sidecar:latest
restartPolicy: Always
ports:
- containerPort: 3000
- containerPort: 3001
env:
- name: WORKSPACE_PATH
{}
Examples
Example 1: Basic File Sync Sidecar
User: "Create a sidecar that watches files and exposes a REST API"
Response:
import Fastify from 'fastify';
import chokidar from 'chokidar';
import fs from 'fs/promises';
const app = Fastify({ logger: true });
const workspace = process.env.WORKSPACE_PATH || '/workspace';
const watcher = chokidar.watch(workspace, {
ignored: ['**/node_modules', '**/.git'],
awaitWriteFinish: { stabilityThreshold: 500 },
});
watcher.on('change', (path) => {
app.log.info({ path }, 'File changed');
});
app.get('/files', async () => {
const files = await fs.readdir(workspace, { recursive: true });
return { files };
});
app.get('/files/:path', async (req) => {
const content = await fs.readFile(, );
{ content };
});
app.(, (req) => {
fs.(, req..);
{ : };
});
app.({ : , : });
Example 2: Deploy Module to Odoo
User: "Add endpoint to reload Odoo modules"
Response:
import { exec } from 'child_process';
import { promisify } from 'util';
const execAsync = promisify(exec);
app.post('/deploy/:module', async (req) => {
const { module } = req.params;
try {
await gitService.commit(`Deploy ${module}`);
const { stdout, stderr } = await execAsync(
`odoo -c /etc/odoo/odoo.conf -u ${module} --stop-after-init`,
{ timeout: 60000 }
);
return { success: true, commit: await gitService.log(1) };
} catch (error) {
return { success: false, error: error.message };
}
});
Output Checklist
When building a sidecar service, verify:
Related Skills
- platxa-yjs-server: Detailed Yjs server patterns and awareness protocol
- platxa-k8s-ops: Kubernetes operations and debugging
- platxa-logging: Structured logging with correlation IDs