Fix version detection for disabled packs (#5395)

* fix: normalize pack IDs to fix version detection for disabled packs

When a pack is disabled, ComfyUI-Manager returns it with a version suffix
(e.g., "ComfyUI-GGUF@1_1_4") while enabled packs don't have this suffix.
This inconsistency caused disabled packs to incorrectly show as having
updates available even when they were on the latest version.

Changes:
- Add normalizePackId utility to consistently remove version suffixes
- Apply normalization in refreshInstalledList and WebSocket updates
- Use the utility across conflict detection and node help modules
- Ensure pack version info is preserved in the object's ver field

This fixes the "Update Available" indicator incorrectly showing for
disabled packs that are already on the latest version.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* feature: test code added

* test: packUtils test code added

* test: address PR review feedback for test
  improvements

  - Remove unnecessary .not.toThrow() assertion
  in useManagerQueue test
  - Add clarifying comments for version
  normalization test logic
  - Replace 'as any' with vi.mocked() for better
  type safety

---------

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Jin Yi
2025-09-07 15:11:12 +09:00
committed by GitHub
parent 0d3b15503f
commit e2de4b19fc
8 changed files with 450 additions and 12 deletions

View File

@@ -22,6 +22,7 @@ import type {
NodePackRequirements,
SystemEnvironment
} from '@/types/conflictDetectionTypes'
import { normalizePackId } from '@/utils/packUtils'
import {
cleanVersion,
satisfiesVersion,
@@ -874,9 +875,7 @@ function mergeConflictsByPackageName(
conflicts.forEach((conflict) => {
// Normalize package name by removing version suffix (@1_0_3) for consistent merging
const normalizedPackageName = conflict.package_name.includes('@')
? conflict.package_name.substring(0, conflict.package_name.indexOf('@'))
: conflict.package_name
const normalizedPackageName = normalizePackId(conflict.package_name)
if (mergedMap.has(normalizedPackageName)) {
// Package already exists, merge conflicts

View File

@@ -5,6 +5,7 @@ import { Ref, computed, ref } from 'vue'
import { app } from '@/scripts/app'
import { useDialogService } from '@/services/dialogService'
import { components } from '@/types/generatedManagerTypes'
import { normalizePackKeys } from '@/utils/packUtils'
type ManagerTaskHistory = Record<
string,
@@ -98,7 +99,8 @@ export const useManagerQueue = (
taskHistory.value = filterHistoryByClientId(state.history)
if (state.installed_packs) {
installedPacks.value = state.installed_packs
// Normalize pack keys to ensure consistent access
installedPacks.value = normalizePackKeys(state.installed_packs)
}
updateProcessingState()
}

View File

@@ -1,5 +1,4 @@
import { useEventListener, whenever } from '@vueuse/core'
import { mapKeys } from 'es-toolkit/compat'
import { defineStore } from 'pinia'
import { v4 as uuidv4 } from 'uuid'
import { ref, watch } from 'vue'
@@ -14,6 +13,7 @@ import { useComfyManagerService } from '@/services/comfyManagerService'
import { useDialogService } from '@/services/dialogService'
import { TaskLog } from '@/types/comfyManagerTypes'
import { components } from '@/types/generatedManagerTypes'
import { normalizePackKeys } from '@/utils/packUtils'
type InstallPackParams = components['schemas']['InstallPackParams']
type InstalledPacksResponse = components['schemas']['InstalledPacksResponse']
@@ -185,12 +185,8 @@ export const useComfyManagerStore = defineStore('comfyManager', () => {
const refreshInstalledList = async () => {
const packs = await managerService.listInstalledPacks()
if (packs) {
// The keys are 'cleaned' by stripping the version suffix.
// The pack object itself (the value) still contains the version info.
const packsWithCleanedKeys = mapKeys(packs, (_value, key) => {
return key.split('@')[0]
})
installedPacks.value = packsWithCleanedKeys
// Normalize pack keys to ensure consistent access
installedPacks.value = normalizePackKeys(packs)
}
isStale.value = false
}

View File

@@ -1,12 +1,14 @@
import type { ComfyNodeDefImpl } from '@/stores/nodeDefStore'
import { NodeSourceType, getNodeSource } from '@/types/nodeSource'
import { normalizePackId } from '@/utils/packUtils'
export function extractCustomNodeName(
pythonModule: string | undefined
): string | null {
const modules = pythonModule?.split('.') || []
if (modules.length >= 2 && modules[0] === 'custom_nodes') {
return modules[1].split('@')[0]
// Use normalizePackId to remove version suffix
return normalizePackId(modules[1])
}
return null
}

35
src/utils/packUtils.ts Normal file
View File

@@ -0,0 +1,35 @@
import { mapKeys } from 'es-toolkit/compat'
/**
* Normalizes a pack ID by removing the version suffix.
*
* ComfyUI-Manager returns pack IDs in different formats:
* - Enabled packs: "packname" (without version)
* - Disabled packs: "packname@1_0_3" (with version suffix)
* - Latest versions from registry: "packname" (without version)
*
* Since the pack object itself contains the version info (ver field),
* we normalize all pack IDs to just the base name for consistent access.
* This ensures we can always find a pack by its base name (nodePack.id)
* regardless of its enabled/disabled state.
*
* @param packId - The pack ID that may contain a version suffix
* @returns The normalized pack ID without version suffix
*/
export function normalizePackId(packId: string): string {
return packId.split('@')[0]
}
/**
* Normalizes all keys in a pack record by removing version suffixes.
* This is used when receiving pack data from the server to ensure
* consistent key format across the application.
*
* @param packs - Record of packs with potentially versioned keys
* @returns Record with normalized keys
*/
export function normalizePackKeys<T>(
packs: Record<string, T>
): Record<string, T> {
return mapKeys(packs, (_value, key) => normalizePackId(key))
}