mirror of
https://github.com/HuangYuChuh/ComfyUI_Skills_OpenClaw.git
synced 2026-07-30 11:33:45 +00:00
Motivation: The previous data structure for workflows and schemas lacked cohesion and was difficult to maintain manually. Additionally, a more robust and flexible migration system was needed to transfer skill configurations across different environments. Implementation: 1. Unified the data hierarchy into a directory-based structure: 'data/<server>/<workflow>/'. 2. Enhanced 'transfer_manager.py' and 'transfer_bundle.py' for environmental-aware configuration migration. 3. Updated frontend JS/CSS and Flask routes to support the new directory-centric asset management. 4. Revamped English and Chinese READMEs to prioritize safe installation and clear configuration workflows.
156 lines
4.9 KiB
Python
156 lines
4.9 KiB
Python
from __future__ import annotations
|
|
|
|
import shutil
|
|
from pathlib import Path
|
|
|
|
from shared.config import (
|
|
CONFIG_PATH,
|
|
DATA_DIR,
|
|
DEFAULT_SERVER_ID,
|
|
SCHEMA_FILENAME,
|
|
WORKFLOW_FILENAME,
|
|
default_config,
|
|
default_server,
|
|
get_legacy_server_schemas_dir,
|
|
get_legacy_server_workflows_dir,
|
|
get_server_data_dir,
|
|
get_server_schema_path,
|
|
get_server_workflow_path,
|
|
)
|
|
from shared.json_utils import load_json, save_json
|
|
|
|
|
|
def migrate_legacy_config() -> None:
|
|
"""Detect old flat config format and migrate to the new multi-server format.
|
|
|
|
Old format:
|
|
{ "comfyui_server_url": "...", "output_dir": "..." }
|
|
|
|
New format:
|
|
{ "servers": [ { "id": "local", ... } ], "default_server": "local" }
|
|
|
|
Also migrates the old split workflow/schema storage into per-workflow folders under data/local/.
|
|
"""
|
|
if not CONFIG_PATH.exists():
|
|
return
|
|
|
|
config = load_json(CONFIG_PATH)
|
|
if not isinstance(config, dict):
|
|
return
|
|
|
|
# Already in new format
|
|
if "servers" in config:
|
|
return
|
|
|
|
# Detect legacy flat format
|
|
old_url = config.get("comfyui_server_url")
|
|
old_output = config.get("output_dir")
|
|
|
|
server = default_server()
|
|
if old_url:
|
|
server["url"] = old_url
|
|
if old_output:
|
|
server["output_dir"] = old_output
|
|
|
|
new_config = {
|
|
"servers": [server],
|
|
"default_server": DEFAULT_SERVER_ID,
|
|
}
|
|
save_json(CONFIG_PATH, new_config)
|
|
|
|
# Migrate data directories
|
|
migrate_workflow_storage_layout()
|
|
|
|
|
|
def _migrate_server_storage_layout(server_id: str) -> None:
|
|
server_dir = get_server_data_dir(server_id)
|
|
legacy_workflows_dir = get_legacy_server_workflows_dir(server_id)
|
|
legacy_schemas_dir = get_legacy_server_schemas_dir(server_id)
|
|
|
|
legacy_ids: set[str] = set()
|
|
if legacy_workflows_dir.exists():
|
|
legacy_ids.update(path.stem for path in legacy_workflows_dir.glob("*.json"))
|
|
if legacy_schemas_dir.exists():
|
|
legacy_ids.update(path.stem for path in legacy_schemas_dir.glob("*.json"))
|
|
|
|
for workflow_id in sorted(legacy_ids):
|
|
legacy_workflow_path = legacy_workflows_dir / f"{workflow_id}.json"
|
|
legacy_schema_path = legacy_schemas_dir / f"{workflow_id}.json"
|
|
workflow_path = get_server_workflow_path(server_id, workflow_id)
|
|
schema_path = get_server_schema_path(server_id, workflow_id)
|
|
|
|
if legacy_workflow_path.exists() and not workflow_path.exists():
|
|
workflow_path.parent.mkdir(parents=True, exist_ok=True)
|
|
shutil.move(str(legacy_workflow_path), str(workflow_path))
|
|
|
|
if legacy_schema_path.exists() and not schema_path.exists():
|
|
schema_path.parent.mkdir(parents=True, exist_ok=True)
|
|
shutil.move(str(legacy_schema_path), str(schema_path))
|
|
|
|
for legacy_dir in (legacy_workflows_dir, legacy_schemas_dir):
|
|
if legacy_dir.exists():
|
|
try:
|
|
legacy_dir.rmdir()
|
|
except OSError:
|
|
pass
|
|
|
|
# Remove empty workflow directories that may remain after migrations or deletes.
|
|
if server_dir.exists():
|
|
for workflow_dir in server_dir.iterdir():
|
|
if not workflow_dir.is_dir():
|
|
continue
|
|
if workflow_dir.name in {"workflows", "schemas"}:
|
|
continue
|
|
has_workflow = (workflow_dir / WORKFLOW_FILENAME).exists()
|
|
has_schema = (workflow_dir / SCHEMA_FILENAME).exists()
|
|
if not has_workflow and not has_schema:
|
|
try:
|
|
workflow_dir.rmdir()
|
|
except OSError:
|
|
pass
|
|
|
|
|
|
def migrate_workflow_storage_layout() -> None:
|
|
"""Migrate old split workflow/schema directories into per-workflow folders."""
|
|
if not DATA_DIR.exists():
|
|
return
|
|
|
|
_migrate_server_storage_layout(DEFAULT_SERVER_ID)
|
|
|
|
for server_dir in DATA_DIR.iterdir():
|
|
if not server_dir.is_dir():
|
|
continue
|
|
if server_dir.name in {"workflows", "schemas"}:
|
|
continue
|
|
_migrate_server_storage_layout(server_dir.name)
|
|
|
|
|
|
def get_runtime_config() -> dict[str, object]:
|
|
"""Load the full multi-server config, migrating from legacy format if needed."""
|
|
migrate_legacy_config()
|
|
migrate_workflow_storage_layout()
|
|
|
|
if not CONFIG_PATH.exists():
|
|
return default_config()
|
|
|
|
loaded = load_json(CONFIG_PATH)
|
|
if not isinstance(loaded, dict) or "servers" not in loaded:
|
|
return default_config()
|
|
|
|
return loaded
|
|
|
|
|
|
def get_server_by_id(server_id: str) -> dict[str, object] | None:
|
|
"""Look up a single server entry by its id."""
|
|
config = get_runtime_config()
|
|
for server in config.get("servers", []):
|
|
if isinstance(server, dict) and server.get("id") == server_id:
|
|
return server
|
|
return None
|
|
|
|
|
|
def get_default_server_id() -> str:
|
|
"""Return the default server id from config."""
|
|
config = get_runtime_config()
|
|
return str(config.get("default_server", DEFAULT_SERVER_ID))
|