Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

A battle-tested, TypeScript-first client for interacting with ComfyUI.

License

NotificationsYou must be signed in to change notification settings

zandko/comfyui-sdk

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

npm downloadsnpm versionLicense: MITTypeScript

A battle-tested, TypeScript-first client for interacting with ComfyUI. Designed for large-scale Node.js applications,comfyui-sdk offers:

  • 🔄Type-safe Workflow ExecutionFully typed payloads and results, ensuring end-to-end type safety when invoking ComfyUI workflows.

  • 📁Streamlined File ManagementUpload, overwrite, and organize files on the ComfyUI server. Automatic format inference, custom subfolders, and overwrite flags.

  • 🏊High-throughput Connection PoolingDistribute requests across multiple ComfyUI instances with configurable concurrency limits and built-in load balancing.

  • 📝Flexible, Configurable LoggingBuilt-in logging framework with adjustable log levels (NONE, ERROR, WARN, INFO, DEBUG).

  • Robust Retry & PollingExponential backoff, configurable retry caps, and efficient polling loops to handle long-running workflows.

  • 🔐Built-in AuthenticationSimple API-key support for secure communication.

  • 📦Advanced Pipeline SupportDefine reusable, type-safe input/output mappings for complex pipelines; integrate processors for artifact transformation.

  • 🌐Cloud Storage IntegrationsNative AWS S3 and Tencent COS uploaders, complete with signing and URL generation.

  • 🎯Session & Resource ManagementAutomatic session pooling, resource cleanup, and artifact pipelines that handle uploads, transformations, and metadata collection.

  • 🔧Modular Artifact ProcessorsBuild custom processors with fine-grainedshouldRun logic and type-safe state propagation viaPipelineBus.

🔧 Installation

Install via your preferred package manager:

npm install comfyui-sdk
yarn add comfyui-sdk
pnpm add comfyui-sdk

🚀 Quick Start

1. Initialize the Client

import{ComfyUIClient,LogLevel}from'comfyui-sdk'constclient=newComfyUIClient({baseUrl:'http://localhost:8188',// ComfyUI server URLapiKey:'your-api-key-here',// Optional API keytimeout:90_000,// Request timeout (ms)logging:true,// Enable logginglogLevel:LogLevel.INFO// Adjust log verbosity})

2. Define & Execute a Workflow

// A minimal workflow: encode text and load a checkpointconstsimpleWorkflow={1:{inputs:{text:'A serene mountain landscape'},class_type:'CLIPTextEncode'},2:{inputs:{ckpt_name:'sd_base_1.0.safetensors'},class_type:'CheckpointLoaderSimple'}}asyncfunctionrunSimple(){try{constartifacts=awaitclient.run(simpleWorkflow)console.log('Artifacts:',artifacts)}catch(err){console.error('Workflow execution failed:',err)}}runSimple()

3. Upload a File

importfsfrom'node:fs'// Read a local image into a Bufferconstbuffer=fs.readFileSync('input.jpg')asyncfunctionuploadExample(){constresult=awaitclient.uploadFile(buffer,{filename:'input.jpg',// Custom name on serversubfolder:'images',// Place under `uploaded/images/`override:true// Overwrite if file exists})console.log('Uploaded as:',result.name)}uploadExample()

📚 API Reference

ComfyUIClient

The primary class to interface with ComfyUI. All network requests, logging, and pooling are managed here.

Constructor:new ComfyUIClient(options: ClientOptions)

interfaceClientOptions{baseUrl:string// E.g., 'http://localhost:8188'apiKey?:string// Optional API keytimeout?:number// Request timeout in milliseconds (default: 90_000)poll?:{interval?:number// Polling interval in ms (default: 4_000)backoffBase?:number// Exponential backoff base in ms (default: 2_000)backoffCap?:number// Max backoff per retry in ms (default: 15_000)}logging?:boolean// Enable or disable logging (default: false)logLevel?:LogLevel// One of NONE, ERROR, WARN, INFO, DEBUG (default: INFO)}

Methods

run<TNode>(workflow: WorkflowPayload, options?: RunOptions<TNode>): Promise<Artifact[]>

Execute a ComfyUI workflow. Returns an array ofArtifact objects (binary, text, or JSON). Optionally, specify a type-safe mapping usingdefineConfig.

  • Parameters

    • workflow: An object whose keys are node IDs (strings or numbers) and whose values defineclass_type andinputs.

    • options(optional):

      interfaceRunOptions<TNode>{node?:TNode// A typed pipeline/node definition (from defineConfig)inputs?:Record<string,unknown>// Values for required/optional pipeline inputs}
  • ReturnsPromise<Artifact[]>

    • If usingdefineConfig, you’ll receive a strongly typed result object (with named outputs).
  • Example: Basic Execution

    constartifacts=awaitclient.run({1:{class_type:'CLIPTextEncode',inputs:{text:'hello world'}}})console.log(artifacts)
  • Example: Type-safe Pipeline

    import{defineConfig}from'comfyui-sdk'consttextToImageConfig=defineConfig({inputs:[{from:'prompt',to:'1.inputs.text',required:true},{from:'seed',to:'2.inputs.seed',defaultValue:42}]asconst,outputs:[{from:'5',to:'image'}]asconst})// Workflow must match the node mapping aboveconstworkflow={1:{class_type:'CLIPTextEncode',inputs:{text:'A cosmic vista'}},2:{class_type:'RandomSeed',inputs:{seed:123}},// ... other nodes up to node 5 that produce an image}const{ image}=awaitclient.run(workflow,{node:textToImageConfig,inputs:{prompt:'A vibrant galaxy',seed:2025}})console.log('Generated image artifact:',image)
uploadFile(file: Buffer | Blob, options?: UploadOptions): Promise<UploadOutput>

Upload a binary to ComfyUI’s file repository. Automatically handles file naming, subfolders, and overwrites.

  • Parameters

    • file: A Node.jsBuffer or a browserBlob.

    • options:

      interfaceUploadOptions{override?:boolean// Overwrite if a file with the same name already exists (default: false)subfolder?:string// Subfolder inside `uploaded/`; auto-creates directories (default: 'uploaded')filename?:string// Custom filename; if omitted, a UUID-based name is generated}
  • ReturnsPromise<UploadOutput>

    interfaceUploadOutput{name:string// Full path on server, e.g., 'uploaded/images/foo.jpg'manifest:{contentType:string// MIME type (e.g., 'image/jpeg')filename:string// The final filename on serversize:number// File size in bytes// ... other manifest fields}}
  • Example

    importfsfrom'node:fs'constimageBuffer=fs.readFileSync('avatar.png')constresult=awaitclient.uploadFile(imageBuffer,{subfolder:'avatars',filename:'user123.png',override:true})console.log('File URL/name:',result.name)
getHistory(promptId: string): Promise<Histories>

Fetch the full execution history for a given prompt UUID.

  • Parameters

    • promptId: The string identifier returned by a previous workflow execution.
  • ReturnsPromise<Histories>

    • An object mapping prompt IDs to status details, timestamps, logs, and node-level progress.
  • Example

    consthistory=awaitclient.getHistory('c2f9a8d2-1b3e-4e7c-9a11-abcdef123456')console.log('Status:',history['c2f9a8d2-1b3e-4e7c-9a11-abcdef123456'].status)

ComfyUIPool

A pool ofComfyUIClient instances for load balancing across multiple ComfyUI servers.

Constructor:new ComfyUIPool(instances: InstanceConfig[], options?: PoolOptions)

  • Parameters

    interfaceInstanceConfig{baseUrl:string// ComfyUI server URL, e.g. 'https://comfyui-1.example.com'apiKey?:string// Optional API key for that instancemaxConcurrency:number// Maximum concurrent workflows on this instancetimeout?:number// Override default timeout (ms)}interfacePoolOptions{logging?:boolean// Enable logging for pool activitieslogLevel?:LogLevel// Log level for pooled clients}
  • Example

    constpool=newComfyUIPool([{baseUrl:'http://server1:8188',maxConcurrency:2},{baseUrl:'http://server2:8188',maxConcurrency:3},{baseUrl:'http://server3:8188',maxConcurrency:1}],{logging:true,logLevel:LogLevel.INFO})

Methods

lease(): Promise<ClientLease>

Acquire a leased client from the pool.

  • ReturnsPromise<ClientLease>

    interfaceClientLease{client:ComfyUIClientrelease:()=>void// Must be called when done, to return the client to the pool}
  • Example

    asyncfunctionuseLease(){constlease=awaitpool.lease()try{constresults=awaitlease.client.run(workflow)console.log('Results:',results)}finally{lease.release()}}
createSession(pipeline: ArtifactPipeline): ComfyUISession | null

Start a session that ties together a leased client + artifact pipeline. Allows you to run multiple workflows in sequence, automatically cleaning up resources and running the pipeline on each artifact.

  • Parameters

    • pipeline: AnArtifactPipeline containing one or moreArtifactProcessors.
  • ReturnsComfyUISession | null if no clients are currently available.

  • Example

    import{ArtifactPipeline,CosUploader}from'comfyui-sdk'constpipeline=newArtifactPipeline([newCosUploader({secretId:process.env.COS_SECRET_ID!,secretKey:process.env.COS_SECRET_KEY!,bucket:'my-bucket',region:'ap-shanghai',prefix:'comfyui-exports/',domain:'cdn.example.com'})])constsession=pool.createSession(pipeline)if(!session)thrownewError('No available ComfyUI clients')try{constartifacts=awaitsession.run(workflow)console.log('Session artifacts:',artifacts)}finally{session.close()}
withSession<T>(fn: (session: ComfyUISession) => Promise<T>, pipeline: ArtifactPipeline): Promise<T>

Convenience wrapper: automatically acquires a session, runs your callback, then closes the session.

  • Example

    import{ArtifactPipeline,CosUploader}from'comfyui-sdk'constpipeline=newArtifactPipeline([/* processors */])constresult=awaitpool.withSession(async(session)=>{returnsession.run(workflow,{node:myConfig,inputs:{prompt:'Auto'}})},pipeline)console.log('Result from withSession:',result)

ArtifactPipeline & Processors

TheArtifactPipeline enables you to chain multipleArtifactProcessor instances. Each processor can:

  • Inspect and transform artifacts
  • Upload results to cloud storage
  • Emit metadata or new artifacts
  • Use fine-grainedshouldRun logic to skip irrelevant artifacts
  • Propagate state through a sharedPipelineBus

Creating a Pipeline

import{ArtifactPipeline,CosUploader,defineConfig}from'comfyui-sdk'// 1. Define custom or built-in processorsconstuploader=newCosUploader({secretId:'AKID...',secretKey:'SECRET...',bucket:'comfyui-bucket',region:'ap-guangzhou',prefix:'outputs/',signExpires:0,domain:'cdn.example.com'})// 2. Instantiate the pipeline with an ordered list of processorsconstpipeline=newArtifactPipeline([uploader,// ... you can add more custom processors here])

Built-in Processors

  • CosUploader (Tencent Cloud Object Storage)Uploads binary artifacts to COS and attachesartifact.pipeline.cosUploader.url.

  • S3Uploader (AWS S3)Equivalent functionality for AWS: configurebucket,region, optionalprefix,ACL, etc.

  • (Custom Processors)ExtendArtifactProcessor to build your own. See “📜 Custom Processors” below.

⚡ Enhanced Pipeline System

What’s New

  1. ProcessorOutput<M> InterfaceEach processor now returns:

    interfaceProcessorOutput<M=unknown>{output:M// Metadata or intermediate datanext?:Pick<Artifact,'kind'|'payload'|'manifest'>// Optional transformed artifact}

    This allows you to both store processing dataand replace the artifact payload/manifest in one step.

  2. Artifact TransformationUse thenext property to pass a newArtifact shape downstream:

    classWebPConverterextendsArtifactProcessor{readonlyname='webpConverter'asyncrun(artifact:Artifact):Promise<ProcessorOutput<{originalFormat:string,newFormat:string}>>{if(artifact.kind!=='binary'){return{output:{originalFormat:'n/a',newFormat:'n/a'}}}// Imagine convertToWebP returns a BufferconstconvertedBuffer=awaitthis.convertToWebP(artifact.payloadasBuffer)return{output:{originalFormat:artifact.manifest.contentType,newFormat:'image/webp'},next:{kind:'binary',payload:convertedBuffer,manifest:{          ...artifact.manifest,filename:artifact.manifest.filename.replace(/\.[^.]+$/,'.webp'),contentType:'image/webp'}}}}privateasyncconvertToWebP(buffer:Buffer):Promise<Buffer>{// Conversion logic...returnbuffer}}
  3. shouldRun HooksEach processor can overrideshouldRun(artifact: Artifact): Promise<boolean> to decide if it should process that artifact:

    classLargeFileProcessorextendsArtifactProcessor{readonlyname='largeFileProcessor'asyncshouldRun(artifact:Artifact):Promise<boolean>{returnartifact.kind==='binary'&&(artifact.payloadasBuffer).byteLength>1_000_000}asyncrun(artifact:Artifact):Promise<ProcessorOutput<Record<string,unknown>>>{// Process large binaries only...return{output:{processed:true}}}}
  4. Pipeline State ManagementProcessors can share state via theartifact.pipeline object (typed by extending thePipelineBus interface).

    // Extend PipelineBus in a declaration file or at top of your codedeclare module'comfyui-sdk/types'{interfacePipelineBus{customProcessor?:CustomProcessorOutput}}classCustomProcessorextendsArtifactProcessor{readonlyname='customProcessor'asyncrun(artifact:Artifact):Promise<ProcessorOutput<CustomProcessorOutput>>{conststart=Date.now()// ...process artifact.payloadconstduration=Date.now()-startreturn{output:{ duration,size:(artifact.payloadasBuffer).byteLength}}}}

📦 Type Definitions

All key types are exported for maximum TypeScript support:

// Artifact can be a binary (Buffer/Blob), text, or JSON resulttypeArtifact=|{kind:'binary',payload:ArrayBuffer|Uint8Array|Buffer|Blob,manifest:BinaryManifest,pipeline?:PipelineState}|{kind:'text',payload:string,manifest:TextManifest,pipeline?:PipelineState}|{kind:'json',payload:unknown,manifest:JsonManifest,pipeline?:PipelineState}interfaceBinaryManifest{filename:stringcontentType:stringsize:number// Additional fields: promptId, nodeId, etc.}interfaceTextManifest{filename:stringcontentType:stringsize:number// Additional metadata}interfaceJsonManifest{filename:stringcontentType:string// Additional metadata}interfacePipelineBus{}typePipelineState=Record<string,unknown>&Partial<PipelineBus>interfaceProcessorOutput<M=unknown>{output:Mnext?:Pick<Artifact,'kind'|'payload'|'manifest'>}abstractclassArtifactProcessor{abstractreadonlyname:stringasyncshouldRun(artifact:Artifact):Promise<boolean>abstractrun(artifact:Artifact):Promise<ProcessorOutput>}interfaceRunOptions<TNode>{node?:TNodeinputs?:Record<string,unknown>}interfaceClientOptions{/* ... as above ... */}interfaceUploadOptions{/* ... as above ... */}interfaceUploadOutput{/* ... as above ... */}interfaceComfyUISession{run:<TNode>(workflow:WorkflowPayload,options?:RunOptions<TNode>)=>Promise<Artifact[]>uploadFile:(data:Buffer|Blob,options?:UploadOptions)=>Promise<UploadOutput>getHistory:(promptId:string)=>Promise<Histories>close:()=>void}classArtifactPipeline{constructor(processors:ArtifactProcessor[])run(artifacts:Artifact[]|Record<string,Artifact>):Promise<Artifact[]|Record<string,Artifact>>}

🛠 Configuration Examples

Development Client

import{ComfyUIClient,LogLevel}from'comfyui-sdk'constdevClient=newComfyUIClient({baseUrl:'http://localhost:8188',logging:true,logLevel:LogLevel.DEBUG,timeout:60_000,poll:{interval:1_000}})

Production Pool

import{ComfyUIPool,LogLevel}from'comfyui-sdk'constprodPool=newComfyUIPool([{baseUrl:'https://comfyui-1.example.com',apiKey:process.env.COMFYUI_API_KEY,maxConcurrency:5,timeout:180_000},{baseUrl:'https://comfyui-2.example.com',apiKey:process.env.COMFYUI_API_KEY,maxConcurrency:3,timeout:180_000}],{logging:true,logLevel:LogLevel.INFO})

📖 Complete Examples

1. End-to-End Text-to-Image Workflow with Pipeline

import{ArtifactPipeline,ComfyUIClient,ComfyUIPool,CosUploader,defineConfig,LogLevel}from'comfyui-sdk'// 1. Configure pool of ComfyUI serversconstpool=newComfyUIPool([{baseUrl:'http://localhost:8188',maxConcurrency:2}],{logging:true,logLevel:LogLevel.INFO})// 2. Build an artifact pipeline: convert to WebP → upload to COS → collect metadataclassWebPConverterextendsArtifactProcessor{readonlyname='webpConverter'asyncrun(artifact:Artifact){if(artifact.kind!=='binary')return{output:{}}constconverted=artifact.payloadasBuffer// pretend conversionreturn{output:{original:artifact.manifest.contentType,converted:'image/webp'},next:{kind:'binary',payload:converted,manifest:{          ...artifact.manifest,contentType:'image/webp',filename:artifact.manifest.filename.replace(/\.\w+$/,'.webp')}}}}}// Tencent COS uploaderconstcosUploader=newCosUploader({secretId:process.env.COS_SECRET_ID!,secretKey:process.env.COS_SECRET_KEY!,bucket:'comfyui-bucket',region:'ap-guangzhou',prefix:'outputs/',signExpires:0,domain:'cdn.example.com'})// Metadata collectorclassMetadataCollectorextendsArtifactProcessor{readonlyname='metadataCollector'asyncrun(artifact:Artifact){constuploadInfo=artifact.pipeline?.cosUploaderconstwebpInfo=artifact.pipeline?.webpConverterconstmetadata={timestamp:newDate().toISOString(),filename:artifact.manifest.filename,size:artifact.kind==='binary' ?(artifact.payloadasBuffer).byteLength :0,uploadUrl:uploadInfo?.url||null,conversion:webpInfo||null}return{output:metadata,next:{kind:'json',payload:metadata,manifest:{from:artifact.manifest.from,promptId:artifact.manifest.promptId,filename:`metadata_${artifact.manifest.filename}.json`,contentType:'application/json'}}}}}// Assemble the pipelineconstpipeline=newArtifactPipeline([newWebPConverter(),cosUploader,newMetadataCollector()])// 3. Define a type-safe text-to-image pipelineconsttextToImageConfig=defineConfig({inputs:[{from:'prompt',to:'6.inputs.text',required:true},{from:'negative_prompt',to:'7.inputs.text',defaultValue:''},{from:'width',to:'5.inputs.width',defaultValue:1024},{from:'height',to:'5.inputs.height',defaultValue:1024},{from:'steps',to:'3.inputs.steps',defaultValue:20},{from:'cfg',to:'3.inputs.cfg',defaultValue:7},{from:'seed',to:'3.inputs.seed',defaultValue:42}]asconst,outputs:[{from:'9',to:'image'},{from:'12',to:'metadata'}]asconst}asconst)// 4. The SDXL text-to-image workflowconstsdxlWorkflow={4:{class_type:'CheckpointLoaderSimple',inputs:{ckpt_name:'sd_xl_base_1.0.safetensors'}},5:{class_type:'EmptyLatentImage',inputs:{width:1024,height:1024,batch_size:1}},6:{class_type:'CLIPTextEncode',inputs:{text:'sunset over ocean',clip:['4',1]}},7:{class_type:'CLIPTextEncode',inputs:{text:'',clip:['4',1]}},3:{class_type:'KSampler',inputs:{seed:42,steps:20,cfg:7,sampler_name:'euler',scheduler:'normal',denoise:1,model:['4',0],positive:['6',0],negative:['7',0],latent_image:['5',0]}},8:{class_type:'VAEDecode',inputs:{samples:['3',0],vae:['4',2]}},9:{class_type:'SaveImage',inputs:{filename_prefix:'ComfyUI',images:['8',0]}},12:{class_type:'SaveJSON',inputs:{data:['8',0],filename:'metadata.json'}}}// 5. Execute within a session so artifacts flow through the pipelineasyncfunctiongenerateImage(){constsession=pool.createSession(pipeline)if(!session)thrownewError('No available clients')try{const{ image, metadata}=awaitsession.run(sdxlWorkflow,{node:textToImageConfig,inputs:{prompt:'A majestic dragon soaring above clouds',width:1920,height:1080,steps:30}})console.log('Final Image Artifact:',image)console.log('Metadata JSON:',metadata)}catch(err){console.error('Generation error:',err)}finally{session.close()}}generateImage()

🔍 Custom Artifact Processors

Extend the baseArtifactProcessor to implement bespoke processing logic:

import{ArtifactProcessor,ProcessorOutput}from'comfyui-sdk'interfaceCustomProcessorOutput{processed:booleanoriginalSize:numberprocessedSize:numbertimestamp:numberdurationMs:number}classCustomImageProcessorextendsArtifactProcessor{readonlyname='customImageProcessor'asyncshouldRun(artifact:Artifact):Promise<boolean>{returnartifact.kind==='binary'}asyncrun(artifact:Artifact):Promise<ProcessorOutput<CustomProcessorOutput>>{conststartTime=Date.now()constbuffer=artifact.payloadasBuffer// Placeholder for real image processingconstprocessedBuffer=awaitthis.processImage(buffer)return{output:{processed:true,originalSize:buffer.byteLength,processedSize:processedBuffer.byteLength,timestamp:Date.now(),durationMs:Date.now()-startTime},next:{kind:'binary',payload:processedBuffer,manifest:{          ...artifact.manifest,filename:`processed_${artifact.manifest.filename}`,contentType:'image/png'}}}}privateasyncprocessImage(buffer:Buffer):Promise<Buffer>{// Insert actual processing here (e.g., resizing, filtering)returnbuffer}}// To enable full type safety on pipeline state:declare module'comfyui-sdk/types'{interfacePipelineBus{customImageProcessor:CustomProcessorOutput}}// Usageconstpipeline=newArtifactPipeline([newCustomImageProcessor(),newCosUploader({/* ...config */})])

🤝 Contributing

We welcome contributions! To get started:

  1. Fork the repository
  2. Install dependencies:npm install
  3. Lint & type-check:npm run lint && npm run build
  4. Run tests:npm test
  5. Submit a pull request with clear descriptions of the changes.

Please review ourCONTRIBUTING.md before opening issues or PRs.

📄 License

This project is licensed under theMIT License. See theLICENSE file for full details.

🔗 Useful Links

Crafted with care by ZaneNode.js 18+ | TypeScript 4.5+

About

A battle-tested, TypeScript-first client for interacting with ComfyUI.

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

[8]ページ先頭

©2009-2026 Movatter.jp