@himan_d/mirrorfit-sdk
v0.1.7
Published
A Node.js SDK for running ComfyUI workflows on RunPod — supports both serverless endpoints and persistent pod instances.
Readme
Mirror Fit SDK
A TypeScript/Node.js SDK for running ComfyUI workflows on RunPod — supports both serverless endpoints and persistent pod instances through a single unified client.
No need to match hardcoded filenames in your workflow JSON. Pass any local image paths and the SDK auto-patches the LoadImage nodes in order.
Installation
npm install @mirrorfit/sdkRequires Node.js >= 18 and ESM ("type": "module").
Quick Start
import { MirrorFitClient } from '@mirrorfit/sdk';
const client = new MirrorFitClient();
// Serverless endpoint
const sl = client.serverless('YOUR_KEY', 'YOUR_ENDPOINT_ID');
// Persistent pod
const pod = client.pod('YOUR_POD_ID', 'YOUR_KEY');
// Local ComfyUI instance
const local = client.local('127.0.0.1', 8188);Serverless Usage
Use this when your ComfyUI is deployed as a RunPod serverless endpoint.
Initialize
import { MirrorFitClient } from '@mirrorfit/sdk';
const client = new MirrorFitClient();
const sl = client.serverless('YOUR_RUNPOD_API_KEY', 'YOUR_RUNPOD_ENDPOINT_ID');Check Health
const health = await sl.getHealth();
console.log(health);
// { workers: { idle: 2, running: 0 }, jobs: {...} }Run Synchronously
Blocks until the generation completes and returns results.
const result = await sl.runSync(
'workflow_api.json', // path or pre-loaded object
['photo.jpg', 'bg.png'] // any filenames — SDK patches the workflow
);
console.log(result.output.images); // string[] of base64 imagesRun Asynchronously
Submit a job and collect the result later — ideal for long workflows.
// Submit and return immediately
const jobId = await sl.runAsync('workflow_api.json', ['photo.jpg']);
// Check status without blocking
const status = await sl.getStatus(jobId);
// { id: '...', status: 'IN_PROGRESS' }
// Block until done and get result
const result = await sl.getResult(jobId);
console.log(result.output.images);Cancel a Job
await sl.cancel(jobId);Clear the Queue
await sl.purgeQueue();Pod Usage
Use this when your ComfyUI is running on a persistent RunPod pod (always-on GPU).
Initialize
import { MirrorFitClient } from '@mirrorfit/sdk';
const client = new MirrorFitClient();
const pod = client.pod(
'YOUR_POD_ID', // 'abc123xyz' — -8188 suffix handled automatically
'YOUR_RUNPOD_API_KEY' // omit if pod proxy port is public
);Check Health
const health = await pod.getHealth();
// { status: 'HEALTHY', message: 'ComfyUI Server is active and warming VRAM.' }List Available Models
Instantly see what checkpoints, VAEs, and LoRAs are installed on the pod — useful for debugging missing model errors.
const models = await pod.getModels();
console.log(models.checkpoints); // ['flux-2-klein-9b-fp8.safetensors', ...]
console.log(models.vaes); // ['flux2-vae.safetensors']
console.log(models.loras); // [...]Check the Queue
const q = await pod.getQueue();
console.log(`Running: ${q.queue_running.length}, Pending: ${q.queue_pending.length}`);Run Synchronously
const result = await pod.runSync(
'workflow_api.json',
['photo.jpg', 'bg.png'], // any filenames — SDK patches the workflow
300 // timeout in seconds (default 240)
);
console.log(result.output.images); // string[] of base64 imagesRun Asynchronously
// Submit without waiting
const promptId = await pod.runAsync('workflow_api.json', ['photo.jpg']);
// Collect when ready
const result = await pod.getResult(promptId, 300);
console.log(result.output.images);Fetch History
// Full history of recent executions
const history = await pod.getHistory();
// History for a specific prompt
const history = await pod.getHistory('some-prompt-id');Upload an Image
Pre-upload an image independently of running a workflow.
await pod.uploadImage('photos/portrait.jpg');
await pod.uploadImage('photos/bg.png', 'background.png');Interrupt a Running Generation
await pod.interrupt();Local Usage
Use this when your ComfyUI is running locally or on your LAN (e.g., http://127.0.0.1:8188).
Initialize
import { MirrorFitClient } from '@mirrorfit/sdk';
const client = new MirrorFitClient();
// With host and port
const local = client.local('127.0.0.1', 8188, 'YOUR_API_KEY');
// With a full URL (pass null for the port)
const localUrl = client.local('https://my-comfy-domain.com', null, 'YOUR_API_KEY');The LocalClient extends the PodClient and shares the exact same API. If an API key is provided, it is automatically passed in the X-Api-Key header. See the Pod Usage section above for available methods like getHealth(), runSync(), runAsync(), etc.
Image Handling
Both clients accept images as an array or a Record<string, string>. The SDK automatically patches the workflow's LoadImage nodes in order — you never have to match filenames hardcoded inside the JSON.
// Array — positional: 1st image → 1st LoadImage node, 2nd → 2nd, etc.
const images = ['input/photo.jpg', 'input/bg.png'];
// Record — explicit upload name → local path (same positional patching applies)
const images = { 'portrait.jpg': 'input/photo.jpg', 'background.png': 'input/bg.png' };API Reference
MirrorFitClient
Stateless factory — holds no credentials.
| Method | Returns | Description |
|---|---|---|
| .serverless(apiKey, endpointId) | ServerlessClient | Client for a RunPod serverless endpoint |
| .pod(podId, apiKey?) | PodClient | Client for a persistent RunPod pod |
| .local(host?, port?, apiKey?) | LocalClient | Client for a local ComfyUI instance |
ServerlessClient
Obtained via client.serverless(apiKey, endpointId).
| Method | Returns | Description |
|---|---|---|
| getHealth() | Promise<HealthResponse> | Endpoint health and worker counts |
| runSync(workflow, images, timeout?) | Promise<RunResult> | Execute workflow, block until done |
| runAsync(workflow, images) | Promise<string> | Submit job, return job ID immediately |
| getStatus(jobId) | Promise<ServerlessStatusResponse> | Check job status without fetching output |
| getResult(jobId, timeout?) | Promise<RunResult> | Poll until done, return full result |
| cancel(jobId) | Promise<void> | Cancel a queued or running job |
| purgeQueue() | Promise<void> | Remove all pending jobs from the endpoint |
runSync / runAsync parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| workflow | string \| Record<string, unknown> | Yes | Path to workflow_api.json or pre-loaded object |
| images | string[] \| Record<string, string> | Yes | Local paths (array) or { uploadName: localPath } |
| timeout | number | No | Max wait in milliseconds before TimeoutError (default 300_000) |
PodClient
Obtained via client.pod(podId, apiKey?).
| Method | Returns | Description |
|---|---|---|
| getHealth() | Promise<HealthResponse> | Check if ComfyUI server is alive |
| getQueue() | Promise<QueueResponse> | View running and pending prompts |
| getHistory(promptId?) | Promise<Record<string, HistoryEntry>> | Retrieve past execution history |
| getModels() | Promise<ModelsResponse> | List installed checkpoints, VAEs, LoRAs, CLIPs, UNETs |
| uploadImage(filePath, uploadName?) | Promise<UploadResponse> | Upload an image to the pod's input folder |
| interrupt() | Promise<void> | Stop the currently running generation |
| runSync(workflow, images, timeoutSeconds?) | Promise<RunResult> | Execute workflow, block until done |
| runAsync(workflow, images) | Promise<string> | Queue prompt, return prompt ID immediately |
| getResult(promptId, timeoutSeconds?) | Promise<RunResult> | Poll until done, return full result |
runSync / runAsync parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| workflow | string \| Record<string, unknown> | Yes | Path to workflow_api.json or pre-loaded object |
| images | string[] \| Record<string, string> | Yes | Local paths (array) or { uploadName: localPath } |
| timeoutSeconds | number | No | Max wait in seconds before TimeoutError (default 240) |
Response Format
All runSync and getResult calls return the same shape:
{
"id": "prompt-uuid",
"status": "COMPLETED",
"delayTime": 1200,
"executionTime": 8500,
"output": {
"message": "Generation completed successfully.",
"images": ["<base64>", "<base64>"]
}
}Saving Output Images
import * as fs from 'fs';
import * as path from 'path';
function saveImages(result, outputDir = 'output') {
fs.mkdirSync(outputDir, { recursive: true });
for (let i = 0; i < result.output.images.length; i++) {
const filePath = path.join(outputDir, `output_${i + 1}.png`);
fs.writeFileSync(filePath, Buffer.from(result.output.images[i], 'base64'));
console.log(`Saved: ${filePath}`);
}
}
const result = await pod.runSync('workflow_api.json', ['photo.jpg']);
saveImages(result);Error Handling
try {
const result = await pod.runSync('workflow_api.json', ['photo.jpg']);
} catch (err) {
if (err.name === 'TimeoutError') {
console.log('Workflow timed out — try increasing the timeout');
} else if (err.name === 'RuntimeError') {
console.log(`Job failed: ${err.message}`);
} else if (err instanceof Error) {
console.log(`Unexpected error: ${err.message}`);
}
}Requirements
- Node.js 18+
- Active RunPod account
- A deployed ComfyUI serverless endpoint and/or a running ComfyUI pod
License
MIT
