Ship MiniMax v2 identity refs and Qwen sidecar edits.

Keep v1 as the default video graph, relay image edits to the Qwen Lightning box, and keep /api/health up if one Comfy host is unset.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Towsty
2026-08-26 14:29:15 -05:00
co-authored by Cursor
parent 551c29a62a
commit c55a48a57e
22 changed files with 2061 additions and 75 deletions
+138
View File
@@ -0,0 +1,138 @@
"""Install scripts/comfy-host-agent.mjs on the sidecar image-Comfy PC over SSH.
Reads AIGEN_IMAGE_SSH_HOST, AIGEN_IMAGE_SSH_USER, and AIGEN_IMAGE_SSH_KEY
(default: %USERPROFILE%\\.ssh\\aigen_image_comfy) or AIGEN_IMAGE_SSH_PASS.
Installs the host agent only. Does not start Comfy or load models.
"""
from __future__ import annotations
import os
import sys
import time
from pathlib import Path
import paramiko
ROOT = Path(__file__).resolve().parents[1]
AGENT = ROOT / "scripts" / "comfy-host-agent.mjs"
REMOTE_DIR = r"C:\Users\comfyapi\aigen-agent"
REMOTE_AGENT = REMOTE_DIR + r"\comfy-host-agent.mjs"
REMOTE_START = REMOTE_DIR + r"\start.cmd"
NODE_ZIP = "https://nodejs.org/dist/v22.18.0/node-v22.18.0-win-x64.zip"
def connect():
host = os.environ.get("AIGEN_IMAGE_SSH_HOST", "192.168.77.101")
user = os.environ.get("AIGEN_IMAGE_SSH_USER", "comfyapi")
password = os.environ.get("AIGEN_IMAGE_SSH_PASS")
key_path = os.environ.get("AIGEN_IMAGE_SSH_KEY") or str(Path.home() / ".ssh" / "aigen_image_comfy")
if not Path(key_path).exists():
key_path = os.environ.get("AIGEN_IMAGE_SSH_KEY")
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
kwargs = {
"hostname": host,
"username": user,
"timeout": 15,
"allow_agent": False,
"look_for_keys": False,
}
if key_path:
kwargs["key_filename"] = key_path
elif password:
kwargs["password"] = password
else:
raise SystemExit("Set AIGEN_IMAGE_SSH_PASS or AIGEN_IMAGE_SSH_KEY")
client.connect(**kwargs)
return client, host, user
def run(client, cmd, timeout=120):
stdin, stdout, stderr = client.exec_command(cmd, timeout=timeout)
out = stdout.read().decode("utf-8", "replace")
err = stderr.read().decode("utf-8", "replace")
code = stdout.channel.recv_exit_status()
return code, out, err
def main():
if not AGENT.exists():
raise SystemExit(f"missing {AGENT}")
token = os.environ.get("IMAGE_COMFY_CONTROL_TOKEN") or os.urandom(24).hex()
token_file = Path(os.environ.get("TEMP", ".")) / "image_comfy_control_token"
token_file.write_text(token, encoding="ascii")
pubkey = Path.home() / ".ssh" / "aigen_image_comfy.pub"
start_cmd = "\r\n".join([
"@echo off",
f"cd /d {REMOTE_DIR}",
"set COMFY_CONTROL_PORT=8199",
"set COMFY_PROXY_PORT=8198",
"set COMFY_HOST=http://127.0.0.1:8188",
f"set COMFY_CONTROL_TOKEN={token}",
r"set COMFY_LAUNCH_CWD=C:\ComfyUI-QwenEdit\ComfyUI",
r"set COMFY_LAUNCH_CMD=C:\ComfyUI-QwenEdit\python\python.exe C:\ComfyUI-QwenEdit\ComfyUI\main.py --listen 0.0.0.0 --port 8188",
r'"C:\Users\comfyapi\aigen-agent\node.exe" comfy-host-agent.mjs',
"",
])
client, host, user = connect()
print("connected", user, host)
code, out, err = run(client, f'mkdir "{REMOTE_DIR}" 2>nul & echo dir-ok')
print("mkdir", code, out.strip() or err.strip())
sftp = client.open_sftp()
with sftp.file(REMOTE_AGENT, "wb") as handle:
handle.write(AGENT.read_bytes())
with sftp.file(REMOTE_START, "w") as handle:
handle.write(start_cmd)
if pubkey.exists():
run(client, r'if not exist "%USERPROFILE%\.ssh" mkdir "%USERPROFILE%\.ssh"')
existing = ""
try:
with sftp.file(r"C:\Users\comfyapi\.ssh\authorized_keys", "r") as handle:
existing = handle.read().decode("utf-8", "replace")
except FileNotFoundError:
existing = ""
key_text = pubkey.read_text(encoding="ascii").strip()
if key_text not in existing:
with sftp.file(r"C:\Users\comfyapi\.ssh\authorized_keys", "a") as handle:
handle.write(("\n" if existing and not existing.endswith("\n") else "") + key_text + "\n")
print("authorized_keys updated")
sftp.close()
code, out, err = run(client, f'if exist "{REMOTE_DIR}\\node.exe" echo node-present')
if "node-present" not in out:
print("downloading node")
code, out, err = run(
client,
f'curl.exe -L --retry 3 -o "{REMOTE_DIR}\\node.zip" {NODE_ZIP}',
timeout=180,
)
print("curl", code, (err or out)[-400:])
code, out, err = run(
client,
f'"C:\\Program Files\\7-Zip\\7z.exe" x -y -o"{REMOTE_DIR}\\node-dist" "{REMOTE_DIR}\\node.zip"',
timeout=120,
)
print("unzip", code, out[-400:] or err[-400:])
run(client, f'copy /Y "{REMOTE_DIR}\\node-dist\\node-v22.18.0-win-x64\\node.exe" "{REMOTE_DIR}\\node.exe"')
run(client, r'powershell -NoProfile -Command "Get-Process node -ErrorAction SilentlyContinue | Where-Object { $_.Path -like \'*aigen-agent*\' } | Stop-Process -Force"')
code, out, err = run(
client,
r'cmd /c start "" /MIN C:\Users\comfyapi\aigen-agent\start.cmd',
)
print("start", code, out.strip(), err.strip())
run(
client,
r'schtasks /Create /TN AigenImageComfyAgent /TR "C:\Users\comfyapi\aigen-agent\start.cmd" /SC ONLOGON /F',
)
client.close()
print("token-file", str(token_file))
print("wait for agent")
time.sleep(2)
if __name__ == "__main__":
main()