Skill: emwstudio/emwstudio (version 1.0.1)

--- name: emwstudio description: 这是UP主"电磁波Studio"开发的Skill，用于运行ComfyUI工作流，支持生成"多国语言版酱板鸭视频"、"不要怕视频"、"《喵窖2026》广告配音"。 metadata: { "openclaw": { "requires": { "bins": ["python3"] } }, } --- # emwstuido skill 你的任务是根据用户输入，调用对应的工作流，生成对应的工作流运行结果。具体工作流Info json在 data/workflows.json 文件中。 ## 人格特征 你是UP主“电磁波Studio”的小助手——一位专业技能精湛的AIGC专家，你的所有回复必须遵循以下格式： - 所有回复要带表情。 - 以Markdown格式排版。 ## 运行流程 ### 第一步：欢迎语 使用`message`工具发送欢迎语给用户，内容包括： 1. 组织一下语言发送一段简介的欢迎语给用户 2. **必须提示**：⚠️ 如果没有RunningHub账号或API Key，需要先注册RunningHub账号并获取API Key 3. **必须提示**：文本消息"Skill需要使用OpenClaw的exec、process内置工具，如未开启对应工具或运行权限，请自行参考OpenClaw官方文档进行配置～" **使用`message`工具把上述3条内容组成一个欢迎语，发送给用户。** ### 第二步：获取环境变量 运行以下脚本获取环境变量： ```json {"tool": "exec", "command": "python3 scripts/get_env.py", "yieldMs": 20000} ``` 获取json返回的 `host` 和 `api_key` 字段。 ### 第三步：设置RH_HOST 如果`host`的值为空字符串或为 `null`，先询问用户选择服务器： - 国内服务器（www.runninghub.cn） - 国外服务器（www.runninghub.ai） 根据用户选择将`host`更新为对应的地址，调用对应脚本保存`host`地址： 如果用户选择国内服务器： ```json {"tool": "exec", "command": "python3 scripts/config_host.py --set \"www.runninghub.cn\"", "yieldMs": 20000} ``` 如果用户选择国外服务器： ```json {"tool": "exec", "command": "python3 scripts/config_host.py --set \"www.runninghub.ai\"", "yieldMs": 20000} ``` ### 第四步：设置API Key 如果`api_key`的值为空字符串或为 `null`，询问用户输入在`host`网站创建的API Key，根据用户的输入将`api_key`更新为对应的值，然后调用对应脚本保存`api_key`值： ```json {"tool": "exec", "command": "python3 scripts/config_api_key.py --set \"{{api_key}}\"", "yieldMs": 20000} ``` ### 第五步：获取账户信息 调用脚本获取账户信息，用于检查账号状态和余额： ```json {"tool": "exec", "command": "python3 scripts/get_account_info.py \"{{host}}\" \"{{api_key}}\"", "yieldMs": 20000} ``` 解析返回的JSON数据： - `code`: 返回标记，成功为0，非0为失败 - `msg`: 返回信息 - `data.remainCoins`: RH币数量 - `data.currentTaskCounts`: 当前正在运行任务数量 - `data.remainMoney`: 钱包余额 - `data.currency`: 钱包货币单位 **如果`code`不为0**： - 使用`message`工具告知用户获取账户信息失败，提示用户检查API Key是否正确 **如果`code`为0**： - 使用`message`工具告知用户账户信息，包括RH币数量和钱包余额 ### 第六步：根据用户输入选择工作流 根据用户输入，找到对应的工作流Info json里的`webappId`字段值，调用脚本获取工作流的节点信息： ```json {"tool": "exec", "command": "python3 scripts/get_workflow_info.py \"{{host}}\" \"{{api_key}}\" \"{{webappId}}\"", "yieldMs": 20000} ``` 解析返回的JSON数据： - `code`: 返回标记，成功为0，非0为失败 - `data.nodeInfoList`: 节点信息列表，包含每个需要用户输入的字段 - `data.description`: 工作流描述 - `data.webappName`: 工作流名称 **如果`code`不为0**： - 使用`message`工具告知用户获取工作流信息失败 **如果`code`为0**： - 从返回的`data.nodeInfoList`中提取每个节点的`fieldName`、`fieldValue`和`description` - 使用`message`工具提示用户需要输入的具体内容，根据`description`字段引导用户输入对应的数据，将用户输入赋值给对应的`fieldValue`字段。 - 得到更新后的`nodeInfoList`字段值。 ### 第七步：运行工作流任务 开始运行工作流任务： ```json {"tool": "exec", "command": "python3 scripts/create_task.py \"{{host}}\" \"{{api_key}}\" \"{{webappId}}\" \"{{nodeInfoList}}\"", "yieldMs": 20000} ``` ### 第八步：处理响应 解析脚本返回的JSON数据： **成功响应示例：** ```json { "code": 0, "msg": "success", "data": { "netWssUrl": "wss://www.runninghub.cn:443/ws/c_instance?c_host=222.186.161.123&c_port=85&clientId=14caa1db2110a81629c101b9bb4cb0ce&workflowId=1876205853438365698&Rh-Comfy-Auth=eyJ1c2VySWQiOiJkZTBkYjZmMjU2NGM4Njk3YjA3ZGY1NWE3N2YwN2JlOSIsInNpZ25FeHBpcmUiOjE3NDQxMTI1MjEyMzYsInRzIjoxNzQzNTA3NzIxMjM2LCJzaWduIjoiZDExOTE0MzkwMjJlNjViMjQ5MjU2YzU2ZmQxYTUwZjUifQ%3D%3D", "taskId": "1907035719658053634", "clientId": [REDACTED_SECRET], "taskStatus": "RUNNING", "promptTips": "{\"result\": true, \"error\": null, \"outputs_to_execute\": [\"115\", \"129\", \"124\"], \"node_errors\": {}}" } } ``` - **如果`code`为0**： - 提取`taskId`和`taskStatus` - 进行第九步：后台轮询工作流运行状态 - **如果`code`不为0**： - 从`msg`字段读取错误信息 - 只需告知用户生成失败的原因。 - 不再继续后续步骤 ### 第九步：后台轮询工作流运行状态 温柔地告知用户： ```json {"tool": "message", "content": "工作流正在后台运行中，运行完成后会自动把结果发送给你哦～先去喝杯咖啡吧☕️"} ``` 使用 OpenClaw exec 工具运行下面的脚本： ```json {"tool": "exec", "command": "python3 scripts/poll_task.py '{{host}}' '{{api_key}}' '{{taskId}}' 10", "background": true} ``` exec 工具会返回 status: "running" + sessionId 和一小段尾部输出。从返回的内容中提取sessionId。 ### 第十步：查询轮询结果 使用 OpenClaw process 工具查询后台工作流运行状态 ```json {"tool": "process", "action": "poll", "sessionId": "{{sessionId}}"} ``` 将 process 返回的完整输出内容中直接解析 JSON 结果，并根据解析结果进行后续操作。 if `status` === `TIMEOUT`: - 使用`message`工具告知用户工作流仍在运行中，但是运行时间过长，已经超过20分钟，因此不再后台进行轮询查询，建议稍后手动查询taskId `{{taskId}}`的状态 elif `status` === `FAILED`: - 分析`errorMessage` - 使用`message`工具告知用户失败原因，并提醒用户可以根据taskId `{{taskId}}`手动查询该任务状态。 elif `status` === `SUCCESS`: - 从`results[0].url`获取工作流运行结果的URL - 使用`message`工具，将工作流运行结果的URL和文字发送给用户： ```json {"tool": "message", "content": "亲爱的，工作流已经运行完成～快看看吧～🎬", "media": "{{results[0].url}}"} ``` ### 第十一步：清空已完成的后台任务 ```json { "tool": "process", "action": "clear"} ``` ## 辅助脚本（可选） ### 重新设置API Key 如果用户想要重新设置`api_key`，询问用户输入在`host`网站创建的新API Key，根据用户输入将`api_key`进行更新，并调用对应脚本保存`api_key`值： ```json {"tool": "exec", "command": "python3 scripts/config_api_key.py --set \"{{api_key}}\"", "yieldMs": 20000} ``` ### 重新设置RH_HOST 如果用户想要重新设置`host`地址： - 国内服务器（www.runninghub.cn） - 国外服务器（www.runninghub.ai） 根据用户选择将`host`更新为对应的地址，并调用脚本保存`host`地址： ```json {"tool": "exec", "command": "python3 scripts/config_host.py --set \"www.runninghub.cn\"", "yieldMs": 20000} ``` 或 ```json {"tool": "exec", "command": "python3 scripts/config_host.py --set \"www.runninghub.ai\"", "yieldMs": 20000} ``` ### 单独查询任务状态 如果需要单独查询任务状态（不轮询）： ```json {"tool": "exec", "command": "python3 scripts/query_task.py \"{{host}}\" \"{{api_key}}\" \"{{taskId}}\"", "yieldMs": 20000} ``` ## 帮助 - Skill配套视频教程：用户可以去B站、抖音、YouTube、小红书、微信视频号、快手、微博、X、TikTok、Instagram等平台搜索“电磁波Studio”查看Skill的配套视频教程。 - 用户注册RunningHub账号：国内用户去 https://www.runninghub.cn/?inviteCode=6bfdf1c0 注册账号。国外用户去 https://www.runninghub.ai/?inviteCode=6bfdf1c0 注册账号。 - 获取API Key方式：用户需要在RunningHub网站点击个人头像找到API控制台创建API Key，这是用于调用UP主“电磁波Studio”在RunningHub上部署的ComfyUI工作流用的。 - 获取RH币方式：如果用户RunningHub账号没有RH币，去RunningHub网站点击个人头像，找到邀请码，点击填写邀请码，输入我的邀请码：6bfdf1c0，你会获得1000个RH币！运行工作流无压力！（邀请码要单独一行发送给用户，方便用户复制）

path: data/workflows.json
content: 生成多国语言版酱板鸭视频

生成不要怕视频

生成《喵窖2026》广告配音

path: scripts/create_task.py
content: #!/usr/bin/env python3
"""
创建RunningHub工作流任务
用法: python3 create_task.py <HOST> <API_KEY> <WEBAPP_ID> <NODE_INFO_LIST>
"""

import sys
import json
import urllib.request
import urllib.error


def create_task(host: str, api_key: str, webapp_id: str, node_info_list: list) -> dict:
    """创建工作流任务"""
    url = f"https://{host}/task/openapi/ai-app/run"

    headers = {
        "Host": host,
        "Authorization": f"Bearer {api_key}",
        "Content-Type": "application/json"
    }

    data = {
        "webappId": int(webapp_id),
        "apiKey": api_key,
        "nodeInfoList": node_info_list
    }

    req = urllib.request.Request(
        url,
        data=json.dumps(data).encode("utf-8"),
        headers=headers,
        method="POST"
    )

    with urllib.request.urlopen(req, timeout=30) as response:
        return json.loads(response.read().decode("utf-8"))


def main():
    if len(sys.argv) != 5:
        print("Usage: python3 create_task.py <HOST> <API_KEY> <WEBAPP_ID> <NODE_INFO_LIST>", file=sys.stderr)
        sys.exit(1)

    host = sys.argv[1]
    [REDACTED_SECRET]
    webapp_id = sys.argv[3]
    node_info_list = json.loads(sys.argv[4])

    try:
        result = create_task(host, api_key, webapp_id, node_info_list)
        print(json.dumps(result, ensure_ascii=False))
    except urllib.error.HTTPError as e:
        error_response = {
            "code": e.code,
            "msg": str(e.reason),
            "data": None
        }
        try:
            body = json.loads(e.read().decode("utf-8"))
            error_response["msg"] = body.get("msg", str(e.reason))
        except:
            pass
        print(json.dumps(error_response, ensure_ascii=False))
        sys.exit(1)
    except Exception as e:
        print(json.dumps({"code": -1, "msg": str(e), "data": None}, ensure_ascii=False))
        sys.exit(1)


if __name__ == "__main__":
    main()


path: scripts/poll_task.py
content: #!/usr/bin/env python3
"""
轮询RunningHub任务状态直到完成或失败
用法: python3 poll_task.py <HOST> <API_KEY> <TASK_ID> [INTERVAL_SECONDS]
"""

import sys
import json
import time
import urllib.request
import urllib.error




def query_task(host: str, api_key: str, task_id: str) -> dict:
    """查询任务状态"""
    url = f"https://{host}/openapi/v2/query"

    headers = {
        "Host": host,
        "Authorization": f"Bearer {api_key}",
        "Content-Type": "application/json"
    }

    data = {"taskId": task_id}

    req = urllib.request.Request(
        url,
        data=json.dumps(data).encode("utf-8"),
        headers=headers,
        method="POST"
    )

    try:
        with urllib.request.urlopen(req, timeout=30) as response:
            return json.loads(response.read().decode("utf-8"))
    except urllib.error.HTTPError as e:
        try:
            body = json.loads(e.read().decode("utf-8"))
            return {
                "status": "FAILED",
                "errorCode": str(e.code),
                "errorMessage": body.get("msg", str(e.reason)),
                "taskId": task_id
            }
        except:
            return {
                "status": "FAILED",
                "errorCode": str(e.code),
                "errorMessage": str(e.reason),
                "taskId": task_id
            }
    except Exception as e:
        return {
            "status": "FAILED",
            "errorCode": "-1",
            "errorMessage": str(e),
            "taskId": task_id
        }


def poll_task(host: str, api_key: str, task_id: str, interval: int = 10, max_duration: int = 1200) -> dict:
    """
    轮询任务状态直到完成或失败

    Args:
        host: RunningHub主机地址
        api_key: API密钥
        task_id: 任务ID
        interval: 轮询间隔秒数，默认10秒
        max_duration: 最长轮询时间（秒），默认1200秒（20分钟）

    Returns:
        最终的任务状态结果
    """
    start_time = time.time()
    known_statuses = ["QUEUED", "RUNNING", "SUCCESS", "FAILED", "TIMEOUT"]
    consecutive_failures = []
    last_failure_result = None
    max_consecutive_failures = 3

    while True:
        result = query_task(host, api_key, task_id)
        status = result.get("status", "UNKNOWN")
        current_time = time.time()

        if status == "SUCCESS":
            return result

        if status == "FAILED":
            consecutive_failures.append(result)
            last_failure_result = result
            if len(consecutive_failures) >= max_consecutive_failures:
                last_failure_result["errorMessage"] = f"连续{max_consecutive_failures}次查询失败，最后一次错误: {last_failure_result.get('errorMessage', 'Unknown')}"
                return last_failure_result
        else:
            consecutive_failures = []

        if status not in known_statuses:
            return {
                "status": "FAILED",
                "errorCode": "UNKNOWN_STATUS",
                "errorMessage": f"未知的状态: {status}",
                "taskId": task_id
            }

        elapsed = current_time - start_time
        if elapsed >= max_duration:
            return {
                "status": "TIMEOUT",
                "errorCode": "TIMEOUT",
                "errorMessage": f"轮询超时，已等待{int(elapsed)}秒，任务仍在运行中",
                "taskId": task_id
            }

        time.sleep(interval)


def main():
    if len(sys.argv) < 4:
        print("Usage: python3 poll_task.py <HOST> <API_KEY> <TASK_ID> [INTERVAL_SECONDS]", file=sys.stderr)
        sys.exit(1)

    host = sys.argv[1]
    [REDACTED_SECRET]
    task_id = sys.argv[3]
    interval = int(sys.argv[4]) if len(sys.argv) > 4 else 10

    try:
        result = poll_task(host, api_key, task_id, interval)
        print(json.dumps(result, ensure_ascii=False))
    except Exception as e:
        print(json.dumps({
            "status": "FAILED",
            "errorCode": "-1",
            "errorMessage": str(e),
            "taskId": task_id
        }, ensure_ascii=False))
        sys.exit(1)


if __name__ == "__main__":
    main()


path: scripts/config_api_key.py
content: #!/usr/bin/env python3
"""
检查并设置Skills配置中的emwstudio
用法: python3 config_api_key.py [--check] [--set API_KEY]
"""

import sys
import json
import os


def get_openclaw_config_path():
    """获取openclaw.json配置文件路径"""
    home = os.path.expanduser("~")
    return os.path.join(home, ".openclaw", "openclaw.json")


def load_config():
    """加载openclaw.json配置"""
    config_path = get_openclaw_config_path()
    if not os.path.exists(config_path):
        return None
    try:
        with open(config_path, "r", encoding="utf-8") as f:
            return json.load(f)
    except Exception:
        return None


def save_config(config):
    """保存openclaw.json配置"""
    config_path = get_openclaw_config_path()
    os.makedirs(os.path.dirname(config_path), exist_ok=True)
    with open(config_path, "w", encoding="utf-8") as f:
        json.dump(config, f, ensure_ascii=False, indent=2)


def get_skill_config():
    """获取emwstudio的配置"""
    config = load_config()
    if not config:
        return None
    skills = config.get("skills", {})
    entries = skills.get("entries", {})
    return entries.get("emwstudio")


def has_skill_config():
    """检查是否存在emwstudio配置"""
    skill_config = get_skill_config()
    return skill_config is not None


def get_api_key():
    """获取emwstudio的apiKey"""
    skill_config = get_skill_config()
    if not skill_config:
        return None
    return skill_config.get("apiKey")


def set_skill_config(api_key):
    """设置emwstudio的完整配置"""
    config = load_config() or {}
    if "skills" not in config:
        config["skills"] = {}
    if "entries" not in config["skills"]:
        config["skills"]["entries"] = {}
    
    skill_config = config["skills"]["entries"].get("emwstudio", {})
    skill_config["enabled"] = True
    skill_config["apiKey"] = api_key
    if "env" not in skill_config:
        skill_config["env"] = {}
    
    config["skills"]["entries"]["emwstudio"] = skill_config
    save_config(config)


def main():
    if len(sys.argv) < 2:
        print("Usage: python3 config_api_key.py [--check] [--set API_KEY]", file=sys.stderr)
        sys.exit(1)

    if sys.argv[1] == "--check":
        has_config = has_skill_config()
        [REDACTED_SECRET]) if has_config else None
        result = {
            "has_skill_config": has_config,
            "has_api_key": api_key is not None,
            "api_key": api_key
        }
        print(json.dumps(result, ensure_ascii=False))
    elif sys.argv[1] == "--set" and len(sys.argv) >= 3:
        [REDACTED_SECRET]
        set_skill_config(api_key)
        print(json.dumps({"success": True, "message": "skill配置已保存"}, ensure_ascii=False))
    else:
        print("Usage: python3 config_api_key.py [--check] [--set API_KEY]", file=sys.stderr)
        sys.exit(1)


if __name__ == "__main__":
    main()


path: scripts/query_task.py
content: #!/usr/bin/env python3
"""
查询RunningHub任务状态
用法: python3 query_task.py <HOST> <API_KEY> <TASK_ID>
"""

import sys
import json
import urllib.request
import urllib.error


def query_task(host: str, api_key: str, task_id: str) -> dict:
    """查询任务状态"""
    url = f"https://{host}/openapi/v2/query"

    headers = {
        "Host": host,
        "Authorization": f"Bearer {api_key}"
    }

    data = {
        "taskId": task_id
    }

    req = urllib.request.Request(
        url,
        data=json.dumps(data).encode("utf-8"),
        headers=headers,
        method="POST"
    )

    with urllib.request.urlopen(req, timeout=30) as response:
        return json.loads(response.read().decode("utf-8"))


def main():
    if len(sys.argv) != 4:
        print("Usage: python3 query_task.py <HOST> <API_KEY> <TASK_ID>", file=sys.stderr)
        sys.exit(1)

    host = sys.argv[1]
    [REDACTED_SECRET]
    task_id = sys.argv[3]

    try:
        result = query_task(host, api_key, task_id)
        print(json.dumps(result, ensure_ascii=False))
    except urllib.error.HTTPError as e:
        error_response = {
            "status": "FAILED",
            "errorCode": str(e.code),
            "errorMessage": str(e.reason),
            "taskId": task_id
        }
        try:
            body = json.loads(e.read().decode("utf-8"))
            error_response["errorMessage"] = body.get("msg", str(e.reason))
        except:
            pass
        print(json.dumps(error_response, ensure_ascii=False))
        sys.exit(1)
    except Exception as e:
        print(json.dumps({
            "status": "FAILED",
            "errorCode": "-1",
            "errorMessage": str(e),
            "taskId": task_id
        }, ensure_ascii=False))
        sys.exit(1)


if __name__ == "__main__":
    main()

path: scripts/get_env.py
content: #!/usr/bin/env python3
"""
获取emwstudio的配置
用法: python3 get_env.py
"""

import os
import json


def get_openclaw_config_path():
    home = os.path.expanduser("~")
    return os.path.join(home, ".openclaw", "openclaw.json")


def get_env():
    config_path = get_openclaw_config_path()
    if not os.path.exists(config_path):
        return {"host": None, "api_key": None}

    try:
        with open(config_path, "r", encoding="utf-8") as f:
            config = json.load(f)
        skill = config.get("skills", {}).get("entries", {}).get("emwstudio", {})
        env = skill.get("env", {})

        host = env.get("RH_HOST")
        [REDACTED_SECRET]"apiKey")

        return {"host": host, "api_key": api_key}
    except Exception:
        return {"host": None, "api_key": None}


result = get_env()
print(json.dumps(result, ensure_ascii=False))

path: scripts/get_workflow_info.py
content: #!/usr/bin/env python3
"""
获取工作流的节点信息
用法: python3 get_workflow_info.py <HOST> <API_KEY> <WEBAPP_ID>
"""

import sys
import json
import urllib.request
import urllib.error


def get_workflow_info(host: str, api_key: str, webapp_id: str) -> dict:
    """获取工作流的节点信息"""
    url = f"https://{host}/api/webapp/apiCallDemo"

    headers = {
        "Host": host,
        "Authorization": f"Bearer {api_key}",
        "Content-Type": "application/json"
    }

    params = f"?[REDACTED_SECRET]"

    req = urllib.request.Request(
        url + params,
        headers=headers,
        method="GET"
    )

    try:
        with urllib.request.urlopen(req, timeout=30) as response:
            return json.loads(response.read().decode("utf-8"))
    except urllib.error.HTTPError as e:
        try:
            body = json.loads(e.read().decode("utf-8"))
            return {
                "code": e.code,
                "msg": body.get("msg", str(e.reason)),
                "errorMessages": [str(e.reason)]
            }
        except:
            return {
                "code": e.code,
                "msg": str(e.reason),
                "errorMessages": [str(e.reason)]
            }
    except Exception as e:
        return {
            "code": -1,
            "msg": str(e),
            "errorMessages": [str(e)]
        }


def main():
    if len(sys.argv) < 4:
        print("Usage: python3 get_workflow_info.py <HOST> <API_KEY> <WEBAPP_ID>", file=sys.stderr)
        sys.exit(1)

    host = sys.argv[1]
    [REDACTED_SECRET]
    webapp_id = sys.argv[3]

    try:
        result = get_workflow_info(host, api_key, webapp_id)
        print(json.dumps(result, ensure_ascii=False))
    except Exception as e:
        print(json.dumps({
            "code": -1,
            "msg": str(e),
            "errorMessages": [str(e)]
        }, ensure_ascii=False))
        sys.exit(1)


if __name__ == "__main__":
    main()


path: scripts/config_host.py
content: #!/usr/bin/env python3
"""
检查并设置emwstudio.env.RH_HOST
用法: python3 config_host.py [--check] [--set RH_HOST]
"""

import sys
import json
import os


def get_openclaw_config_path():
    """获取openclaw.json配置文件路径"""
    home = os.path.expanduser("~")
    return os.path.join(home, ".openclaw", "openclaw.json")


def load_config():
    """加载openclaw.json配置"""
    config_path = get_openclaw_config_path()
    if not os.path.exists(config_path):
        return None
    try:
        with open(config_path, "r", encoding="utf-8") as f:
            return json.load(f)
    except Exception:
        return None


def save_config(config):
    """保存openclaw.json配置"""
    config_path = get_openclaw_config_path()
    os.makedirs(os.path.dirname(config_path), exist_ok=True)
    with open(config_path, "w", encoding="utf-8") as f:
        json.dump(config, f, ensure_ascii=False, indent=2)


def get_skill_config():
    """获取emwstudio的配置"""
    config = load_config()
    if not config:
        return None
    skills = config.get("skills", {})
    entries = skills.get("entries", {})
    return entries.get("emwstudio")


def get_host():
    """获取emwstudio.env.RH_HOST"""
    skill_config = get_skill_config()
    if not skill_config:
        return None
    env = skill_config.get("env", {})
    return env.get("RH_HOST")


def set_host(host):
    """设置emwstudio.env.RH_HOST"""
    config = load_config() or {}
    
    if "skills" not in config:
        config["skills"] = {}
    if "entries" not in config["skills"]:
        config["skills"]["entries"] = {}
    
    skill_config = config["skills"]["entries"].get("emwstudio", {})
    skill_config["enabled"] = True
    if "env" not in skill_config:
        skill_config["env"] = {}
    skill_config["env"]["RH_HOST"] = host
    
    config["skills"]["entries"]["emwstudio"] = skill_config
    save_config(config)


def main():
    if len(sys.argv) < 2:
        print("Usage: python3 config_host.py [--check] [--set RH_HOST]", file=sys.stderr)
        sys.exit(1)

    if sys.argv[1] == "--check":
        host = get_host()
        result = {
            "has_host": host is not None,
            "host": host
        }
        print(json.dumps(result, ensure_ascii=False))
    elif sys.argv[1] == "--set" and len(sys.argv) >= 3:
        host = sys.argv[2]
        set_host(host)
        print(json.dumps({"success": True, "message": "RH_HOST已保存"}, ensure_ascii=False))
    else:
        print("Usage: python3 config_host.py [--check] [--set RH_HOST]", file=sys.stderr)
        sys.exit(1)


if __name__ == "__main__":
    main()


path: scripts/get_account_info.py
content: #!/usr/bin/env python3
"""
获取RunningHub账户信息
用法: python3 get_account_info.py <HOST> <API_KEY>
"""

import sys
import json
import urllib.request
import urllib.error


def get_account_info(host: str, api_key: str) -> dict:
    """获取账户信息"""
    url = f"https://{host}/uc/openapi/accountStatus"

    headers = {
        "Host": host,
        "Authorization": f"Bearer {api_key}",
        "Content-Type": "application/json"
    }

    data = {"apikey": api_key}

    req = urllib.request.Request(
        url,
        data=json.dumps(data).encode("utf-8"),
        headers=headers,
        method="POST"
    )

    try:
        with urllib.request.urlopen(req, timeout=30) as response:
            return json.loads(response.read().decode("utf-8"))
    except urllib.error.HTTPError as e:
        try:
            body = json.loads(e.read().decode("utf-8"))
            return {
                "code": e.code,
                "msg": body.get("msg", str(e.reason)),
                "errorMessages": [str(e.reason)]
            }
        except:
            return {
                "code": e.code,
                "msg": str(e.reason),
                "errorMessages": [str(e.reason)]
            }
    except Exception as e:
        return {
            "code": -1,
            "msg": str(e),
            "errorMessages": [str(e)]
        }


def main():
    if len(sys.argv) < 3:
        print("Usage: python3 get_account_info.py <HOST> <API_KEY>", file=sys.stderr)
        sys.exit(1)

    host = sys.argv[1]
    [REDACTED_SECRET]

    try:
        result = get_account_info(host, api_key)
        print(json.dumps(result, ensure_ascii=False))
    except Exception as e:
        print(json.dumps({
            "code": -1,
            "msg": str(e),
            "errorMessages": [str(e)]
        }, ensure_ascii=False))
        sys.exit(1)


if __name__ == "__main__":
    main()


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)