Install
openclaw skills install @songhonglei/skill-to-http-flash把单个 Skill 编译成独立 HTTP REST API 微服务(v2.0 subprocess 直执行)。当用户说「skill 暴露成 HTTP 接口」「flash 一个 skill」「skill 起 API 服务」「skill 输入输出结构化」「standalone 跑 skill」 「skill-to-http-flash」时使用。JSON → Python 入口 → 统一 envelope(success/exit_code/data|output/stderr)。 毫秒冷启,运行期不依赖 Gateway/LLM,多 agent runtime 通用(OpenClaw/Claude Code/Cursor)。默认 HTTP,按需 HTTPS。
openclaw skills install @songhonglei/skill-to-http-flash依赖:Python 3.10+;
pip install fastapi uvicorn cryptography pydantic jsonschema;可选openssl(HTTPS)。
定位:subprocess 直执行 + standalone。"把 Skill 脚本变成稳定 REST API"的微服务工厂。
POST /run → subprocess.run(["python3", "scripts/main.py", "--foo", "x", ...])
→ envelope { success / exit_code / elapsed_ms / data | output / stderr / truncated }
不依赖 Gateway,运行期不调 LLM,不走 sessions_spawn。秒级冷启,100% 复现,零 LLM 漂移。
ℹ️ 关于 LLM:运行期(/run 执行 skill)完全不碰 LLM,100% subprocess 确定性。 仅在
create/recreate生成期,会可选用 LLM 从 SKILL.md 自动提取入参 schema—— 未配置 LLM(默认)时走启发式 fallback(空 schema +additionalProperties: true), 生成后可手动编辑params.json。即:跑起来零 LLM,生成时 LLM 只是锦上添花。
| 维度 | v1.x | v2.0 |
|---|---|---|
| 执行方式 | sessions_spawn 调 OpenClaw agent + LLM | subprocess 直调 skill 的 Python 入口 |
| 依赖 Gateway | ✅ 必须 | ❌ 不需要 |
| 入参 | 自由 message 字段(LLM 解读) | JSON Schema 校验 + 长 flag CLI 映射 |
| 出参 | 自由文本 | 统一 envelope,自适应 JSON 解析(A)或文本(B) |
| 截断 | 无明确阈值 | 同步 512 KB 截断 + 异步不截断 |
| skill-to-http 依赖 | 静默自动安装 | 完全移除,自带 _cert.py |
| standalone | 部分支持 | 一等公民设计目标(--skill-dir/--data-dir + env 覆盖) |
| 默认超时 | 120s | 60s(同步) |
v2.0 不兼容 v1 老 project。已有的 v1 project 必须 flash.py remove 后重新 create。详见 references/migration-from-v1.md。
.py 文件(v2.0 不支持 shell entry,因为 shell 难校验参数)argparse 长 flag(自动映射 {"foo": "x"} → --foo x)不再需要:OpenClaw Gateway / 运行期 LLM。(生成期可选 LLM 提参,未配置走 fallback,见上方说明。)
让被 flash 的 skill 在 frontmatter 显式声明入口:
---
name: my-skill
version: 1.0.0
description: ...
flash:
entry: scripts/main.py # 相对 skill 目录
interpreter: python3 # 可选,默认 python3
---
找不到 flash.entry 时,flash 按以下顺序扫 7 个候选位置(首个命中为准):
scripts/<skill-name>.pyscripts/cli.pyscripts/main.pyscripts/run.py<skill-name>.py(skill 根目录)main.py(skill 根目录)cli.py(skill 根目录)都找不到 → 直接报错并打印 frontmatter patch 建议,引导用户加 flash.entry。
⚠️ 找到
.sh入口也会报错 — v2.0 只支持.py。包一层subprocess.run(["bash", ...])的 Python launcher 即可。
cd ~/.openclaw/workspace/skills/skill-to-http-flash/scripts
# 1. 创建 flash 项目
python3 flash.py create --skill <skill-name>
# → 解析入口、提参数 schema、交互确认端口/超时/TLS
# → 生成到 <workspace>/.skill-to-http-flash/services/<skill-name>-api/
# 2. 启动
cd <workspace>/.skill-to-http-flash/services/<skill-name>-api
pip install -r requirements.txt
python3 server.py start
# 3. 调用
curl -X POST http://127.0.0.1:7780/run \
-H 'content-type: application/json' \
-d '{"foo": "bar"}'
任何能跑 Python 的机器都能用,完全不依赖 ~/.openclaw。它本质是"把带 argparse 的 Python 脚本编译成 REST API",与具体 agent runtime 无关——不 import 任何 agent SDK,不调 Gateway / LLM / sessions_spawn。
多 agent 开箱即用:默认自动探测以下 skill 目录(首个命中为准),OpenClaw / Claude Code / Cursor 用户无需手设 env:
~/.openclaw/workspace/skills/ # OpenClaw
~/.claude/skills/ # Claude Code
~/.cursor/skills/ # Cursor
~/.config/skills/ # 通用 XDG
./skills/ # 项目本地
/app/skills/ # 容器挂载
自定义目录(其它 runtime 或非标准布局):
# 方式 A:环境变量
export FLASH_SKILL_DIR=/your/path/to/skills
export FLASH_DATA_DIR=/your/path/to/flash-data
python3 flash.py create --skill my-skill
# 方式 B:CLI flag(每条命令带上)
python3 flash.py \
--skill-dir /your/path/to/skills \
--data-dir /your/path/to/flash-data \
create --skill my-skill
生成的
server.py完全自包含,可 scp 到任何机器裸跑(只需pip install -r requirements.txt),与本 skill / 任何 agent runtime 解耦。
完整 standalone 指南:references/standalone-usage.md
| 请求 JSON | 转成 argv |
|---|---|
{"foo": "x"} | --foo x |
{"foo_bar": 10} | --foo-bar 10(下划线 → 横杠) |
{"verbose": true} | --verbose(flag style) |
{"verbose": false} | (不传,约定脚本默认就是 false) |
{"verbose": null} | (不传) |
{"tags": ["a","b"]} | --tags a --tags b(重复 flag) |
{"meta": {"k":"v"}} | --meta '{"k":"v"}'(嵌套对象序列化为 JSON 字符串) |
安全:字段名走白名单 [a-z][a-z0-9_]*(防 --foo;rm -rf /),argv 走 list 不拼 shell。
{
"success": true | false, // exit_code == 0
"exit_code": 0 | n, // subprocess 返回码
"elapsed_ms": 240,
"data": <object|array|primitive> | null, // stdout 是合法 JSON 时(模式 A)
"output": "<string>" | null, // stdout 不是 JSON 时(模式 B)
"stderr": "<string>" | null, // 仅 success=false 时透出
"stderr_truncated": true, // 仅 stderr 被截断时(success=false 路径)
"truncated": true, // 仅同步 + stdout >512KB 时
"hint": "Output exceeded 512KB. Use POST /run/async + GET /jobs/<id> for full output." // 仅 truncated=true 时
}
data 和 output 互斥——任意时刻只有一个有值。
data 字段(模式 A,客户端可结构化访问)output 字段(模式 B,原始文本)| 错误码 | HTTP | 触发场景 |
|---|---|---|
validation_failed | 400 | JSON Schema / 字段名白名单校验失败 |
entry_not_found | 500 | 入口文件不存在或不是 .py |
spawn_failed | 500 | subprocess 启动失败(interpreter not in PATH 等) |
timeout | 408 | 超过 timeout_seconds |
rate_limited | 429 | /run/async 并发 >20 |
internal_error | 500 | 未预期异常(兜底) |
| exit_code != 0 | 200 | 业务执行失败(脚本"运行了但失败"),envelope.success=false + stderr 透出 |
设计原则:业务失败用 envelope 表达,HTTP 状态码只表达框架问题。
| Method | Path | 说明 |
|---|---|---|
| GET | / | HTML 引导页 |
| GET | /health | probe entry 文件 + interpreter 在 PATH |
| GET | /schema | 入参 JSON Schema + entry / interpreter |
| GET | /docs | FastAPI Swagger UI |
| POST | /run | 同步执行,默认 60s 超时,512KB 截断 |
| POST | /run/async | 异步执行,立即返回 job_id,不截断 |
| GET | /jobs/{id} | 轮询异步任务(内存 + JSONL fallback) |
# 提交
JOB=$(curl -s -X POST http://127.0.0.1:7780/run/async \
-H 'content-type: application/json' -d '{"big_query": true}')
JOB_ID=$(echo "$JOB" | jq -r .job_id)
# 轮询
curl http://127.0.0.1:7780/jobs/$JOB_ID
# {"job_id":"...","status":"completed","result":{...完整 envelope...}}
{"status": "failed", "error_type": "rate_limited"} 429.1 ~ .7,保留 7 份python3 flash.py jobs-export-sqlite --skill <name># 创建项目(首次/新 skill)
python3 flash.py create --skill <name> [--output <dir>]
# 列出所有项目
python3 flash.py list
# 删除(默认保留生成的代码,加 --delete-files 一并删)
python3 flash.py remove --skill <name> [--delete-files]
# Skill 升级后重新生成(保留端口/超时/TLS 配置,只刷 server.py 和 params.json)
python3 flash.py recreate --skill <name> [--yes] [--diff]
# 证书管理(HTTPS 模式)
python3 flash.py cert --skill <name> --cert-action info | renew | import \
[--cert-src <crt> --key-src <key>]
# 异步 job 历史导出 SQLite
python3 flash.py jobs-export-sqlite --skill <name>
# 生成 systemd unit(宿主机自启)
python3 flash.py systemd --skill <name> [--user] [--output <path>] [--restart-sec 5]
# Standalone:所有命令都支持 --skill-dir / --data-dir 全局 flag
python3 flash.py --skill-dir /path/to/skills --data-dir /path/to/data create --skill <name>
| 数据 | 默认位置 | 环境变量覆盖 |
|---|---|---|
| flash 项目数据(projects.json / jobs.jsonl / PID) | <workspace>/.skill-to-http-flash/ | FLASH_DATA_DIR、OPENCLAW_FLASH_DATA_DIR |
| 证书 / API Key | <workspace>/.http/ | OPENCLAW_HTTP_ROOT |
| Skill 搜索路径 | ~/.openclaw/workspace/skills/、~/.claude/skills/、~/.cursor/skills/、~/.config/skills/、./skills/、/app/skills/ | FLASH_SKILL_DIR |
兜底(非 OpenClaw 环境):~/.skill-to-http-flash/、~/.http/
⚠️ K8s Pod:
~/下非 workspace 目录可能不挂 PVC,重启丢密钥/证书/历史。默认存 workspace 内能规避(PVC 一定持久化)。
默认 HTTP(零门槛跑通)。create 时可选启用 HTTPS(自签 SAN 证书)。
# create 时启用
python3 flash.py create --skill foo
# Enable HTTPS? [y/N]: y
# 已有 project 切换
python3 flash.py recreate --skill foo --tls-enabled
python3 flash.py recreate --skill foo --no-tls
# 运行时临时切换(不重生代码)
FLASH_TLS_ENABLED=1 python3 server.py start
# 导入公司颁发的正式证书
python3 flash.py cert --skill foo --cert-action import \
--cert-src server.crt --key-src server.key
证书路径:<HTTP_ROOT>/certs/flash-<skill>/server.{crt,key},自动 SAN 嗅探本机 IP,有效期 365 天。
可选 API Key(X-API-Key header,与 skill-to-http / agent-easy-http 对齐):
# 启用方式
FLASH_API_KEY=mysecret python3 server.py start # env
echo -n "mysecret" > <HTTP_ROOT>/secrets/api-keys/flash-<skill>.key # 文件(0600)
# 调用
curl -H 'X-API-Key: mysecret' http://...:7780/run -d '{...}'
未设置不做认证(仅适合内网受信测试)。公网部署请走前置反代 + 公认证书。
默认 Access-Control-Allow-Origin: *(无 credentials)。收紧:
FLASH_CORS_ALLOW_ORIGINS="https://your-tool.com,https://other.com" python3 server.py start
output/<skill-name>-api/
├── server.py # 独立 FastAPI 服务(~1150 行)
├── params.json # 入参 schema
├── requirements.txt # fastapi / uvicorn / cryptography / pydantic / jsonschema
├── start.sh # 后台启动
├── stop.sh # 停止
├── restart.sh # 重启
└── README.md # 接口文档
server.py 完全自包含(内嵌 cert / job store / argv builder / envelope),可 scp 到任何机器跑。
| 维度 | skill-to-http | skill-to-http-flash v2.0 |
|---|---|---|
| 服务模型 | 多 Skill 统一网关 | 单 Skill 独立 subprocess 微服务 |
| Schema | 运行时动态(LLM 生成) | 生成时固化(JSON Schema 校验) |
| 执行引擎 | sessions_spawn + LLM | subprocess.run() 直调 |
| 启动延迟 | 30s+ (LLM 链路) | <2s |
| 复现性 | LLM 漂移 | 100% 确定性 |
| 依赖 | Gateway + LLM | 无外部依赖 |
flash v2.0 不再依赖 skill-to-http(v1.x 自动安装逻辑已删除)。
references/migration-from-v1.md — v1 用户如何 recreatereferences/standalone-usage.md — 非 OpenClaw 环境完整使用指南.sh(包一层 Python launcher)~/.claude/skills) / Cursor (~/.cursor/skills) / 通用 XDG (~/.config/skills) / 项目本地 (./skills) 兜底,非 OpenClaw runtime 开箱即用无需手设 envserver.py lifespan 退出不再 os._exit(0)(原会绕过 atexit/finally/日志 flush,可能截断 JSONL 落盘),改为让 uvicorn 自然走完关闭流程hmac.compare_digest 恒定时间比较(防 timing attack);401 响应不再回显 header 名flash.py list 的 stale 判断异常捕获从冗余的 (OSError, Exception) 收敛为 (OSError, ValueError, JSONDecodeError)FLASH_MAX_ASYNC env 覆盖(默认 20)