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.
This commit is contained in:
科林_Cyril Pilgrim
2026-03-12 15:39:17 +08:00
parent 425cdd0db4
commit 8a274ad02a
8 changed files with 141 additions and 142 deletions
+108 -114
View File
@@ -23,39 +23,22 @@ It converts natural language requests into structured skill arguments, maps them
## Installation
### Install As An OpenClaw Skill
<details>
<summary><strong>ComfyUI Skills for OpenClaw</strong></summary>
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
</details>
- 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 <server_id>/<workflow_id> \
--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/<server_id>/<workflow_id>/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: `<server_id>/<workflow_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/<server_id>/<workflow_id>/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.
+2 -4
View File
@@ -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` 里实际的节点和输入字段
-1
View File
@@ -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
+3 -12
View File
@@ -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)
+9 -3
View File
@@ -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.
+4 -3
View File
@@ -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
+2 -4
View File
@@ -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", {})
+13 -1
View File
@@ -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"