From a6756c1f1a99a1771021593861c95e94d9dff1c5 Mon Sep 17 00:00:00 2001 From: Towsty Date: Thu, 3 Sep 2026 18:16:56 -0500 Subject: [PATCH] Stop wiping Comfy inputs and sibling outputs during disk cleanup. Co-authored-by: Cursor --- scripts/comfy-host-agent.mjs | 35 ++++++++++++++++------------------- server/utils/comfy.ts | 16 ++++++---------- server/utils/imageComfy.ts | 26 ++------------------------ 3 files changed, 24 insertions(+), 53 deletions(-) diff --git a/scripts/comfy-host-agent.mjs b/scripts/comfy-host-agent.mjs index 1f8b97a..8bebd20 100644 --- a/scripts/comfy-host-agent.mjs +++ b/scripts/comfy-host-agent.mjs @@ -610,7 +610,7 @@ function fileStem(filename) { return dot > 0 ? name.slice(0, dot) : name } -function removeNamedFile(roots, filename, subfolders) { +function removeNamedFile(roots, filename, subfolders, { stemSiblings = true } = {}) { const deleted = [] const name = basename(String(filename || '')) if (!name) return deleted @@ -622,7 +622,7 @@ function removeNamedFile(roots, filename, subfolders) { const dir = sub ? join(root, String(sub).replace(/\.\./g, '')) : root const exact = safeFile(root, sub, name) if (removeFile(exact)) deleted.push(exact) - if (!stem || !existsSync(dir)) continue + if (!stemSiblings || !stem || !existsSync(dir)) continue let files = [] try { files = readdirSync(dir) } catch { continue } for (const file of files) { @@ -700,6 +700,7 @@ function readJson(req) { function purgeDesktopFiles(body) { const deleted = [] + const allowSweep = body?.sweep !== false const names = [ String(body?.imageName || body?.filename || ''), ...(Array.isArray(body?.imageNames) ? body.imageNames : []) @@ -716,7 +717,8 @@ function purgeDesktopFiles(body) { deleted.push(...removeNamedFile( rootsForType(type), videoName, - [video.subfolder, imageOut ? '' : 'video', ''] + [video.subfolder, imageOut ? '' : 'video', ''], + { stemSiblings: false } )) } const audio = body?.audio || null @@ -725,7 +727,8 @@ function purgeDesktopFiles(body) { deleted.push(...removeNamedFile( rootsForType(String(audio?.type || 'output')), audioName, - [audio.subfolder, 'audio', ''] + [audio.subfolder, 'audio', ''], + { stemSiblings: false } )) } const output = body?.output || null @@ -734,10 +737,13 @@ function purgeDesktopFiles(body) { deleted.push(...removeNamedFile( rootsForType(String(output?.type || 'output')), outputName, - [output.subfolder, 'image', 'still', ''] + [output.subfolder, 'image', 'still', ''], + { stemSiblings: false } )) } - deleted.push(...sweepStaleStudioOutputs(body?.sweepPrefixes, body?.sweepSubfolders, body?.sweepMaxAgeMs)) + if (allowSweep) { + deleted.push(...sweepStaleStudioOutputs(body?.sweepPrefixes, body?.sweepSubfolders, body?.sweepMaxAgeMs)) + } const unique = [...new Set(deleted)] console.log(JSON.stringify({ src: 'comfy-host-agent', @@ -747,7 +753,8 @@ function purgeDesktopFiles(body) { imageNames: names, video: videoName || null, audio: audioName || null, - output: outputName || null + output: outputName || null, + sweep: allowSweep })) return { ok: true, deleted: unique } } @@ -857,18 +864,8 @@ const server = http.createServer(async (req, res) => { }) function logStartupSweep() { - try { - const deleted = sweepStaleStudioOutputs() - if (!deleted.length) return - console.log(JSON.stringify({ - src: 'comfy-host-agent', - event: 'startup-sweep', - deleted: deleted.length, - files: deleted.map((path) => basename(path)) - })) - } catch (error) { - console.log(JSON.stringify({ src: 'comfy-host-agent', event: 'startup-sweep-error', error: String(error?.message || error) })) - } + // Disabled: sweeping studio outputs on agent start was deleting Comfy + // files that are still part of the generation / recover workflow. } server.listen(port, '0.0.0.0', async () => { diff --git a/server/utils/comfy.ts b/server/utils/comfy.ts index 175fd60..490bcc0 100644 --- a/server/utils/comfy.ts +++ b/server/utils/comfy.ts @@ -595,6 +595,8 @@ async function purgeOnDesktop(opts: { const token = String(config.comfyControlToken || process.env.COMFY_CONTROL_TOKEN || '') if (!controlUrl) return try { + // Exact files only. Do not sweep other MiniMax/LTX outputs or inputs — + // those are still part of the desktop workflow and recover path. await fetch(`${controlUrl}/purge`, { method: 'POST', headers: { @@ -603,14 +605,10 @@ async function purgeOnDesktop(opts: { ...(token ? { Authorization: `Bearer ${token}` } : {}) }, body: JSON.stringify({ - imageName: opts.imageName || '', - imageSubfolder: opts.imageSubfolder || '', - imageNames: (opts.extraImageNames || []).filter(Boolean), video: opts.video || null, audio: opts.audio || null, output: opts.output || (opts.video && /\.(png|jpe?g|webp|gif)$/i.test(opts.video.filename) ? opts.video : null), - sweepPrefixes: comfySweepNamePrefixes(), - sweepSubfolders: ['video', 'audio', 'image', 'still'] + sweep: false }), signal: AbortSignal.timeout(12000) }) @@ -630,15 +628,13 @@ export async function purgeComfyArtifacts(opts: { }) { if (!purgeComfyEnabled()) return await purgeOnDesktop(opts) + // Only remove the output we already copied into the library. + // Leave Comfy input stills / identity refs on disk — shot chains and + // desktop recover still need them. const files: ComfyDiskFile[] = [] if (opts.video?.filename) files.push(opts.video) if (opts.audio?.filename) files.push(opts.audio) if (opts.output?.filename) files.push(opts.output) - if (opts.imageName) files.push({ filename: opts.imageName, subfolder: opts.imageSubfolder || '', type: 'input' }) - for (const name of opts.extraImageNames || []) { - if (!name || name === opts.imageName) continue - files.push({ filename: name, subfolder: opts.imageSubfolder || '', type: 'input' }) - } for (const file of files) { await deleteComfyHttpFile(file) } diff --git a/server/utils/imageComfy.ts b/server/utils/imageComfy.ts index 74dba2f..4fc1a02 100644 --- a/server/utils/imageComfy.ts +++ b/server/utils/imageComfy.ts @@ -1,5 +1,5 @@ import { AsyncLocalStorage } from 'node:async_hooks' -import { getComfyHost, viaAgentFrontDoor, comfySweepNamePrefixes } from '~/server/utils/comfy' +import { getComfyHost, viaAgentFrontDoor } from '~/server/utils/comfy' import { comfyJobPrefix } from '~/utils/outputNames' const imageHostAls = new AsyncLocalStorage() @@ -385,12 +385,8 @@ async function purgeImageOnDesktop(opts: { ...(token ? { Authorization: `Bearer ${token}` } : {}) }, body: JSON.stringify({ - imageName: opts.inputName || '', - imageSubfolder: opts.inputSubfolder || '', - imageNames: (opts.extraInputNames || []).filter(Boolean), output: opts.output || null, - sweepPrefixes: comfySweepNamePrefixes(), - sweepSubfolders: ['video', 'audio', 'image', 'still'] + sweep: false }), signal: AbortSignal.timeout(8000) }) @@ -415,28 +411,10 @@ export async function purgeImageComfyArtifacts(opts: { type: opts.output.type || 'output' }) } - if (opts.inputName) { - files.push({ - filename: opts.inputName, - subfolder: opts.inputSubfolder || '', - type: 'input' - }) - } - for (const name of opts.extraInputNames || []) { - if (!name || name === opts.inputName) continue - files.push({ - filename: name, - subfolder: opts.inputSubfolder || '', - type: 'input' - }) - } for (const file of files) { await deleteSidecarFile(file) } await purgeImageOnDesktop({ - inputName: opts.inputName, - inputSubfolder: opts.inputSubfolder, - extraInputNames: opts.extraInputNames, output: opts.output }) if (opts.promptId) {