From 8a274ad02a76df9dae7e0010fdd398bd903aa57a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=91=E6=9E=97=5FCyril=20Pilgrim?= Date: Thu, 12 Mar 2026 15:39:17 +0800 Subject: [PATCH] refactor(core): simplify workflow identification and update default ui port Motivation: 1. Maintaining a redundant 'workflow_id' in schema.json was error-prone and confusing for users. 2. The previous default port 8189 frequently collided with other AI services or ComfyUI instances during multi-server deployment. Implementation: 1. Revoked 'workflow_id' reliance in schema logic; the system now strictly uses the directory name as the unique identifier. 2. Updated default web UI port to 18189 and added support for the 'OPENCLAW_UI_PORT' environment variable across scripts and settings. 3. Synchronized documentation and UI launcher scripts to reflect these changes. --- README.md | 222 +++++++++++++++--------------- README.zh.md | 6 +- scripts/registry.py | 1 - scripts/shared/transfer_bundle.py | 15 +- ui/run_ui.bat | 12 +- ui/run_ui.sh | 7 +- ui/services.py | 6 +- ui/settings.py | 14 +- 8 files changed, 141 insertions(+), 142 deletions(-) diff --git a/README.md b/README.md index 265cb45..add4030 100644 --- a/README.md +++ b/README.md @@ -23,39 +23,22 @@ It converts natural language requests into structured skill arguments, maps them ## Installation -### Install As An OpenClaw Skill +
+ComfyUI Skills for OpenClaw -OpenClaw creates the default skill directory for you. Install this repository by entering that directory first, then cloning the project: +Manual install: ```bash cd ~/.openclaw/workspace/skills git clone https://github.com/HuangYuChuh/ComfyUI_Skills_OpenClaw.git comfyui-skill-openclaw cd comfyui-skill-openclaw pip install -r requirements.txt +cp config.example.json config.json ``` -Installed path example: +Let OpenClaw install it for you: -- `~/.openclaw/workspace/skills/comfyui-skill-openclaw/` - -OpenClaw will read `SKILL.md` and call: - -- `scripts/registry.py list --agent` -- `scripts/comfyui_client.py --workflow ... --args '...json...'` - -Minimal checklist: - -1. The project is placed under `~/.openclaw/workspace/skills/`. -2. `SKILL.md` exists at the project root. -3. Python dependencies are installed. -4. `config.json` points to a reachable ComfyUI server. -5. At least one workflow and schema are configured. - -### AI-Native Install Via Agent - -You can also ask an OpenClaw Agent to install this skill for you. - -Use a prompt like this: +Send this prompt to OpenClaw: ```text Please install this ComfyUI skill into my OpenClaw workspace. @@ -68,85 +51,126 @@ Requirements: 2. Clone this repository into `comfyui-skill-openclaw`. 3. Keep SKILL.md at the project root. 4. Install Python dependencies from requirements.txt. -5. Create config.json from config.example.json if missing. +5. Run `cp config.example.json config.json`. 6. Set the default ComfyUI server URL to http://127.0.0.1:8188 unless I specify another one. -7. Make sure the skill can be discovered by OpenClaw after installation. +7. Make sure OpenClaw can discover and call this skill after installation. ``` -### 1) Requirements +
-- Python 3.10+ -- A running ComfyUI server (default: `http://127.0.0.1:8188`) +## How To Configure ComfyUI Workflows -### 2) Prepare runtime config +Before you start, make sure your ComfyUI server is already running. The default local address is `http://127.0.0.1:8188`. -`config.json` is the runtime config for this project. The CLI, UI, and OpenClaw-facing scripts all use it. +### I. Configure Through The UI (Recommended) -Choose one of these two approaches: +- macOS/Linux: `./ui/run_ui.sh`, or double-click `ui/run_ui.command` +- Windows: `ui\run_ui.bat` +- Open: `http://localhost:18189` +- Upload a workflow JSON exported from ComfyUI in **Save (API Format)** +- Add your first ComfyUI server in the UI +- Select which parameters should be exposed to OpenClaw and save the mapping -- Manual: create `config.json` from `config.example.json` and fill in your first server yourself -- UI-based (recommended): start the UI first, then add your first server there, and the UI will write it back into `config.json` +### II. Configure Through Files -`config.json` example: +#### 1) Edit `config.json` -```json +Configure your server first. Minimal example: + +```jsonc { "servers": [ { - "id": "local", - "name": "Local Mac", - "url": "http://127.0.0.1:8188", - "enabled": true, - "output_dir": "./outputs" + "id": "local", // Server ID, also used as the directory name and workflow prefix + "name": "Local", // Display name + "url": "http://127.0.0.1:8188", // ComfyUI server URL + "enabled": true, // Whether this server is enabled + "output_dir": "./outputs" // Image output directory } ], - "default_server": "local" + "default_server": "local" // Default server ID } ``` +#### 2) Place Workflow Files -### 3) Start the local UI +Each workflow uses its own directory, for example: -- macOS/Linux: - ```bash - ./ui/run_ui.sh - ``` - or double-click `ui/run_ui.command` -- Windows: - ```bat - ui\run_ui.bat - ``` +```bash +data/local/Default/ + workflow.json # ComfyUI API-format workflow export + schema.json # Parameter mapping exposed to OpenClaw/Agent +``` -Then open: +#### 3) Write `schema.json` -- `http://localhost:8189` +`schema.json` should include at least: -### 4) Add your first server and workflow +- `description` +- `enabled` +- `parameters` -In the UI: +Minimal example: -1. If you have not already configured a server in `config.json`, add a ComfyUI server first. -2. Upload a workflow exported from ComfyUI via **Save (API Format)**. -3. Expose the parameters you want the agent to use. -4. Save the workflow mapping. +```jsonc +{ + "description": "Default test workflow", // Human-readable description for OpenClaw/Agent + "enabled": true, // Whether this workflow is enabled + "parameters": { + "prompt": { // Parameter name exposed to OpenClaw/Agent + "node_id": 10, // Node ID in workflow.json + "field": "prompt", // Input field name under that node + "required": true, // Whether this field is required + "type": "string", // Parameter type + "description": "Prompt text" // Parameter description + }, + "seed": { + "node_id": 10, + "field": "seed", + "required": false, + "type": "int", + "description": "Random seed" + } + } +} +``` -### 5) Verify the installation +Notes: -Check the registry: +- The workflow ID comes directly from the directory name. For example, `data/local/Default/` means the workflow ID is `Default` +- Each entry in `parameters` defines one input exposed to OpenClaw/Agent +- `node_id` and `field` must match the actual node and input field in `workflow.json` + +If you want a full example, refer to: + +- `data/local/Default/workflow.json` +- `data/local/Default/schema.json` + +#### 4) Verify The Configuration + +List installed workflows: ```bash python scripts/registry.py list ``` -Run one test job: +Run a test generation: ```bash python scripts/comfyui_client.py \ - --workflow local/test \ - --args '{"prompt":"A premium product photo on aged driftwood, warm cinematic light","size":"3:4,1728x2304","seed":20260307}' + --workflow / \ + --args '{"prompt":"test"}' ``` -If successful, output JSON includes local image path(s), for example: +Example: + +```bash +python scripts/comfyui_client.py \ + --workflow local/Default \ + --args '{"prompt":"A premium product photo"}' +``` + +If it succeeds, the output will look like: ```json { @@ -156,65 +180,51 @@ If successful, output JSON includes local image path(s), for example: } ``` -## Local Dashboard (UI) +### III. Let OpenClaw/Agent Configure It For You -Start dashboard: +- Let OpenClaw or another agent edit `config.json` +- Let the agent write `workflow.json` and `schema.json` into the target workflow directory +- After writing the files, let the agent run one verification step -- Via OpenClaw or any agent that can run local commands: - ```bash - python3 ./ui/open_ui.py - ``` -- macOS/Linux: - ```bash - ./ui/run_ui.sh - ``` - or double-click `ui/run_ui.command` -- Windows: - ```bat - ui\run_ui.bat - ``` +### Workflow Requirements (Important) -Then open: +**API-format workflows + a `Save Image` output node** are the baseline requirements for stable use. To avoid failed or empty runs: -- `http://localhost:8189` +1. **The workflow must be exported in ComfyUI API format** + - In ComfyUI, click **Save (API Format)** + - Place the exported JSON at `data///workflow.json` -Use it to configure ComfyUI server URLs, outputs, and manage workflow/schema mapping. - -Current highlights: - -- Multi-server management with per-server and per-workflow enable/disable controls -- Workflow search, sort, and drag-to-reorder -- Upload workflow JSON and auto-fill workflow ID -- Custom dialogs, dropdowns, and language switching for daily editing -- One-click export/import for migrating the current skill configuration across machines +2. **The workflow must end with a `Save Image` node** + - The current client downloads results from ComfyUI output images + - Without a `Save Image` node (or equivalent image output), the workflow may finish but return no downloadable image --- ## Multi-Server Management -You can now configure multiple ComfyUI servers, enabling your agent to dispatch workflows across different hardware (e.g., local machines, cloud A100s). +You can configure multiple ComfyUI servers, so OpenClaw or another agent can dispatch jobs across different hardware targets such as a local GPU or a cloud instance. -### Concept +### Core Concepts - **Dual-Layer Toggles**: Both *servers* and *individual workflows* can be enabled or disabled. A workflow is only visible to the AI agent if **both** the server and the workflow itself are enabled. - **Namespacing**: Workflows are identified with a composite ID: `/` (e.g., `local/sdxl-base` vs. `cloud-a100/sdxl-base`). -### Configuration via CLI -A built-in CLI tool (`scripts/server_manager.py`) allows server management on headless Linux machines: +### CLI Configuration +On headless machines, you can use the built-in CLI tool `scripts/server_manager.py`: ```bash python scripts/server_manager.py list python scripts/server_manager.py add --id cloud --name "Cloud Node" --url http://10.0.0.1:8188 python scripts/server_manager.py disable cloud ``` -*You can also manage servers fully via the Web UI.* +*You can still manage all server settings through the Web UI.* ### Configuration Migration (Export / Import) -If you move this skill to a new path or deploy it on another machine, use the built-in bundle flow to transfer your current config and workflow mappings. +If you move this skill to another path or another machine, use the built-in bundle flow to transfer your current config and workflow mappings. UI flow: - Click `Export Config` on the main page to download `openclaw-skill-export.json` -- Before export, you can expand each server and uncheck workflows you do not want to include; everything is selected by default and servers start collapsed +- Before export, you can expand each server and uncheck workflows you do not want to include; all workflows are selected by default and servers are collapsed by default - Open the UI on the target machine and click `Import Config` - Select the exported JSON bundle - Review the preview summary, then decide whether to also apply the source machine's default server, URL, and output directory @@ -240,22 +250,6 @@ Default import behavior: --- -## Workflow Requirements (Important) - -To ensure a workflow can be executed by this project reliably: - -1. **Export ComfyUI workflow in API format** - - In ComfyUI, click **Save (API Format)**. - - Use that exported JSON in `data///workflow.json`. - -2. **The final output path should include a `Save Image` node** - - The current client downloads generated results from ComfyUI output images. - - Without a `Save Image` node (or equivalent image output in history), the tool may finish but return no downloadable image. - -In short: **API-format workflow + Save Image output node** are required for stable usage. - ---- - ## Known Caveats - If ComfyUI returns HTTP 400 on `/prompt`, the workflow payload or parameter value is usually invalid. diff --git a/README.zh.md b/README.zh.md index 72ffe4d..0711788 100644 --- a/README.zh.md +++ b/README.zh.md @@ -62,7 +62,7 @@ cp config.example.json config.json - macOS/Linux:`./ui/run_ui.sh`,或双击 `ui/run_ui.command` - Windows:`ui\run_ui.bat` -- 访问:`http://localhost:8189` +- 访问:`http://localhost:18189` - 上传从 ComfyUI 导出的工作流 JSON,格式必须是 **Save (API Format)** - 在 UI 中添加第一个 ComfyUI 服务器 - 选择要暴露给 OpenClaw 的参数并保存映射 @@ -102,7 +102,6 @@ data/local/Default/ `schema.json` 至少需要包含: -- `workflow_id` - `description` - `enabled` - `parameters` @@ -111,7 +110,6 @@ data/local/Default/ ```jsonc { - "workflow_id": "Default", // 建议与目录名保持一致 "description": "默认测试工作流", // 给 OpenClaw/Agent 看的工作流说明 "enabled": true, // 是否启用这个工作流 "parameters": { @@ -135,7 +133,7 @@ data/local/Default/ 说明: -- `workflow_id` 建议与目录名保持一致;例如目录是 `data/local/Default/`,这里就写 `Default` +- 工作流 ID 直接由目录名决定;例如目录是 `data/local/Default/`,工作流 ID 就是 `Default` - `parameters` 里的每个字段,表示一个要暴露给 OpenClaw/Agent 的输入参数 - `node_id` 和 `field` 需要对应到 `workflow.json` 里实际的节点和输入字段 diff --git a/scripts/registry.py b/scripts/registry.py index 3a91a59..1efe886 100644 --- a/scripts/registry.py +++ b/scripts/registry.py @@ -41,7 +41,6 @@ def get_workflows(is_agent=False): schema_data = json.load(f) workflow_enabled = schema_data.get("enabled", True) - workflow_id = schema_data.get("workflow_id", workflow_id) desc = schema_data.get("description", "") # Apply dual-layer switch logic diff --git a/scripts/shared/transfer_bundle.py b/scripts/shared/transfer_bundle.py index a6d62bd..aa77585 100644 --- a/scripts/shared/transfer_bundle.py +++ b/scripts/shared/transfer_bundle.py @@ -298,19 +298,10 @@ def _collect_export_inventory() -> tuple[list[dict[str, Any]], list[ValidationIs )) continue - actual_workflow_id = _normalize_identifier(schema_data.get("workflow_id")) or workflow_id - if not _is_valid_identifier(actual_workflow_id): - warnings.append(ValidationIssue( - code="invalid_workflow_id", - message="Skipped workflow with invalid workflow id during export.", - context={"server_id": server_id, "workflow_id": actual_workflow_id}, - )) - continue - schema_payload = copy.deepcopy(schema_data) - schema_payload["workflow_id"] = actual_workflow_id + schema_payload.pop("workflow_id", None) server_entry["workflows"].append({ - "workflow_id": actual_workflow_id, + "workflow_id": workflow_id, "workflow_data": workflow_data, "schema_data": schema_payload, "enabled": bool(schema_payload.get("enabled", True)), @@ -779,7 +770,7 @@ def apply_bundle_import( workflow_payload = copy.deepcopy(workflow.get("workflow_data", {})) schema_payload = copy.deepcopy(workflow.get("schema_data", {})) - schema_payload["workflow_id"] = workflow_id + schema_payload.pop("workflow_id", None) workflow_path = get_server_workflow_path(server_id, workflow_id) schema_path = get_server_schema_path(server_id, workflow_id) diff --git a/ui/run_ui.bat b/ui/run_ui.bat index e313ce6..d8eca7c 100644 --- a/ui/run_ui.bat +++ b/ui/run_ui.bat @@ -2,8 +2,14 @@ setlocal cd /d "%~dp0" -echo Ensuring port 8189 is free... -for /f "tokens=5" %%a in ('netstat -ano ^| findstr :8189') do taskkill /f /pid %%a >nul 2>&1 +if "%OPENCLAW_UI_PORT%"=="" ( + set "UI_PORT=18189" +) else ( + set "UI_PORT=%OPENCLAW_UI_PORT%" +) + +echo Ensuring port %UI_PORT% is free... +for /f "tokens=5" %%a in ('netstat -ano ^| findstr :%UI_PORT%') do taskkill /f /pid %%a >nul 2>&1 where python >nul 2>nul if errorlevel 1 ( @@ -12,7 +18,7 @@ if errorlevel 1 ( exit /b 1 ) -echo Starting ComfyUI OpenClaw Skill UI on http://127.0.0.1:8189 +echo Starting ComfyUI OpenClaw Skill UI on http://127.0.0.1:%UI_PORT% python app.py if errorlevel 1 ( echo UI exited with an error. diff --git a/ui/run_ui.sh b/ui/run_ui.sh index 1bb4eab..e279ab0 100755 --- a/ui/run_ui.sh +++ b/ui/run_ui.sh @@ -3,6 +3,7 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" PYTHON_BIN="${PYTHON_BIN:-python3}" +UI_PORT="${OPENCLAW_UI_PORT:-18189}" cd "$SCRIPT_DIR" @@ -12,9 +13,9 @@ if ! command -v "$PYTHON_BIN" >/dev/null 2>&1; then fi if command -v lsof >/dev/null 2>&1; then - echo "Ensuring port 8189 is free..." - lsof -ti:8189 | xargs kill -9 2>/dev/null || true + echo "Ensuring port $UI_PORT is free..." + lsof -ti:"$UI_PORT" | xargs kill -9 2>/dev/null || true fi -echo "Starting ComfyUI OpenClaw Skill UI on http://127.0.0.1:8189" +echo "Starting ComfyUI OpenClaw Skill UI on http://127.0.0.1:$UI_PORT" exec "$PYTHON_BIN" app.py diff --git a/ui/services.py b/ui/services.py index 5fd5bf3..89bf289 100644 --- a/ui/services.py +++ b/ui/services.py @@ -171,7 +171,6 @@ class UIStorageService: schema_data = _read_json(schema_path, fallback={}) if isinstance(schema_data, dict): enabled = bool(schema_data.get("enabled", True)) - wf_id = str(schema_data.get("workflow_id") or wf_id) description = str(schema_data.get("description") or "") except Exception: enabled = True @@ -210,7 +209,7 @@ class UIStorageService: raise ValueError(f"Workflow data is invalid for {workflow_id}") return { - "workflow_id": str(schema_data.get("workflow_id") or workflow_id), + "workflow_id": workflow_id, "server_id": server_id, "description": str(schema_data.get("description") or ""), "enabled": bool(schema_data.get("enabled", True)), @@ -247,7 +246,6 @@ class UIStorageService: _write_json(workflow_path, workflow_data) schema = { - "workflow_id": workflow_id, "description": description, "enabled": enabled, "parameters": schema_params, @@ -280,7 +278,7 @@ class UIStorageService: if not isinstance(schema, dict): schema = {} - schema["workflow_id"] = str(schema.get("workflow_id") or workflow_id) + schema.pop("workflow_id", None) schema["enabled"] = enabled schema.setdefault("description", "") schema.setdefault("parameters", {}) diff --git a/ui/settings.py b/ui/settings.py index 90aedbc..18c9544 100644 --- a/ui/settings.py +++ b/ui/settings.py @@ -1,5 +1,6 @@ from __future__ import annotations +import os from pathlib import Path from shared.config import ( @@ -15,7 +16,18 @@ CONFIG_EXAMPLE_PATH = BASE_DIR / "config.example.json" OUTPUTS_DIR = BASE_DIR / "outputs" DEFAULT_HOST = "127.0.0.1" -DEFAULT_PORT = 8189 + + +def _read_default_port() -> int: + raw = os.environ.get("OPENCLAW_UI_PORT", "18189").strip() + try: + port = int(raw) + except ValueError: + return 18189 + return port if 1 <= port <= 65535 else 18189 + + +DEFAULT_PORT = _read_default_port() DEFAULT_COMFYUI_SERVER_URL = "http://127.0.0.1:8188"