timeverse-hyperframes-mcp 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- timeverse_hyperframes_mcp/__init__.py +23 -0
- timeverse_hyperframes_mcp/__main__.py +4 -0
- timeverse_hyperframes_mcp/cli_executor.py +196 -0
- timeverse_hyperframes_mcp/server.py +57 -0
- timeverse_hyperframes_mcp/tools/__init__.py +26 -0
- timeverse_hyperframes_mcp/tools/init.py +121 -0
- timeverse_hyperframes_mcp/tools/lint.py +74 -0
- timeverse_hyperframes_mcp/tools/media.py +128 -0
- timeverse_hyperframes_mcp/tools/preview.py +61 -0
- timeverse_hyperframes_mcp/tools/render.py +105 -0
- timeverse_hyperframes_mcp/tools/system.py +168 -0
- timeverse_hyperframes_mcp-0.1.0.dist-info/METADATA +8 -0
- timeverse_hyperframes_mcp-0.1.0.dist-info/RECORD +16 -0
- timeverse_hyperframes_mcp-0.1.0.dist-info/WHEEL +5 -0
- timeverse_hyperframes_mcp-0.1.0.dist-info/entry_points.txt +2 -0
- timeverse_hyperframes_mcp-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""
|
|
2
|
+
timeverse-hyperframes-mcp — MCP server for HyperFrames
|
|
3
|
+
|
|
4
|
+
功能简述:
|
|
5
|
+
MCP 服务器,将 HyperFrames CLI(HTML 转视频渲染引擎)包装为 MCP 工具。
|
|
6
|
+
支持视频项目初始化、预览、渲染、lint、TTS 等能力。
|
|
7
|
+
兼容 TimeVerseStudio 的 MCP 注册体系(local/stdio 模式)。
|
|
8
|
+
|
|
9
|
+
主要工具清单:
|
|
10
|
+
- hyperframes_init: 创建新的视频项目
|
|
11
|
+
- hyperframes_render: 渲染视频为 MP4/WebM
|
|
12
|
+
- hyperframes_preview: 启动本地实时预览
|
|
13
|
+
- hyperframes_lint: 检查项目语法完整性
|
|
14
|
+
- hyperframes_doctor: 检测运行环境
|
|
15
|
+
- hyperframes_tts: 文本转语音
|
|
16
|
+
- hyperframes_transcribe: 音频/视频转录文字
|
|
17
|
+
|
|
18
|
+
使用示例:
|
|
19
|
+
# 作为 stdio MCP 服务器运行
|
|
20
|
+
python -m timeverse_hyperframes_mcp.server
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CLI 执行器 — 统一执行 HyperFrames CLI 命令
|
|
3
|
+
|
|
4
|
+
功能简述:
|
|
5
|
+
提供异步执行 npx hyperframes 命令的统一接口。
|
|
6
|
+
处理命令超时、错误捕获、标准输出解析。
|
|
7
|
+
|
|
8
|
+
主要方法清单:
|
|
9
|
+
- run_hyperframes: 执行任意 hyperframes 子命令
|
|
10
|
+
- check_environment: 检测 ffmpeg / node 环境
|
|
11
|
+
- parse_lint_output: 解析 lint --json 输出
|
|
12
|
+
- find_workspace: 查找或创建项目工作目录
|
|
13
|
+
|
|
14
|
+
使用示例:
|
|
15
|
+
result = await run_hyperframes(["init", "my-video", "--non-interactive"])
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import asyncio
|
|
19
|
+
import json
|
|
20
|
+
import logging
|
|
21
|
+
import os
|
|
22
|
+
import shutil
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
from typing import Optional
|
|
25
|
+
|
|
26
|
+
logger = logging.getLogger(__name__)
|
|
27
|
+
|
|
28
|
+
# ==================== 常量定义 ====================
|
|
29
|
+
|
|
30
|
+
DEFAULT_TIMEOUT = 120 # 默认超时(秒)
|
|
31
|
+
RENDER_TIMEOUT = 600 # 渲染超时(秒)
|
|
32
|
+
WORKSPACE_ENV_VAR = "HYPERFRAMES_WORKSPACE_DIR"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# ==================== 核心方法 ====================
|
|
36
|
+
|
|
37
|
+
async def run_hyperframes(
|
|
38
|
+
args: list[str],
|
|
39
|
+
cwd: Optional[str] = None,
|
|
40
|
+
timeout: int = DEFAULT_TIMEOUT,
|
|
41
|
+
) -> dict:
|
|
42
|
+
"""
|
|
43
|
+
执行 npx hyperframes <args> 命令
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
args: 子命令及参数列表,如 ["init", "my-video", "--non-interactive"]
|
|
47
|
+
cwd: 工作目录,默认为 WORKSPACE_ENV_VAR 指向的目录或当前目录
|
|
48
|
+
timeout: 超时秒数
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
{
|
|
52
|
+
"success": bool,
|
|
53
|
+
"stdout": str,
|
|
54
|
+
"stderr": str,
|
|
55
|
+
"exit_code": int,
|
|
56
|
+
"command": str
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
Raises:
|
|
60
|
+
TimeoutError: 命令执行超时
|
|
61
|
+
"""
|
|
62
|
+
cmd = ["npx", "hyperframes", *args]
|
|
63
|
+
cmd_str = " ".join(cmd)
|
|
64
|
+
|
|
65
|
+
if cwd is None:
|
|
66
|
+
cwd = os.environ.get(WORKSPACE_ENV_VAR, os.getcwd())
|
|
67
|
+
|
|
68
|
+
try:
|
|
69
|
+
proc = await asyncio.wait_for(
|
|
70
|
+
asyncio.create_subprocess_exec(
|
|
71
|
+
*cmd,
|
|
72
|
+
cwd=cwd,
|
|
73
|
+
stdout=asyncio.subprocess.PIPE,
|
|
74
|
+
stderr=asyncio.subprocess.PIPE,
|
|
75
|
+
env={**os.environ, "PAGER": "cat"},
|
|
76
|
+
),
|
|
77
|
+
timeout=timeout,
|
|
78
|
+
)
|
|
79
|
+
stdout, stderr = await proc.communicate()
|
|
80
|
+
stdout_str = stdout.decode("utf-8", errors="replace").strip()
|
|
81
|
+
stderr_str = stderr.decode("utf-8", errors="replace").strip()
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
"success": proc.returncode == 0,
|
|
85
|
+
"stdout": stdout_str,
|
|
86
|
+
"stderr": stderr_str,
|
|
87
|
+
"exit_code": proc.returncode or 0,
|
|
88
|
+
"command": cmd_str,
|
|
89
|
+
}
|
|
90
|
+
except asyncio.TimeoutError:
|
|
91
|
+
raise TimeoutError(f"命令超时({timeout}s): {cmd_str}")
|
|
92
|
+
except FileNotFoundError:
|
|
93
|
+
return {
|
|
94
|
+
"success": False,
|
|
95
|
+
"stdout": "",
|
|
96
|
+
"stderr": "未找到 npx,请确认 Node.js >= 22 已安装",
|
|
97
|
+
"exit_code": -1,
|
|
98
|
+
"command": cmd_str,
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
async def check_environment() -> dict:
|
|
103
|
+
"""
|
|
104
|
+
检测运行环境是否满足 HyperFrames 要求
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
{
|
|
108
|
+
"node_ok": bool, "node_version": str,
|
|
109
|
+
"ffmpeg_ok": bool, "ffmpeg_version": str,
|
|
110
|
+
"npx_ok": bool,
|
|
111
|
+
"all_ok": bool
|
|
112
|
+
}
|
|
113
|
+
"""
|
|
114
|
+
checks = {
|
|
115
|
+
"node_ok": False,
|
|
116
|
+
"node_version": "",
|
|
117
|
+
"ffmpeg_ok": False,
|
|
118
|
+
"ffmpeg_version": "",
|
|
119
|
+
"npx_ok": False,
|
|
120
|
+
"all_ok": False,
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
# 检测 Node.js
|
|
124
|
+
try:
|
|
125
|
+
proc = await asyncio.create_subprocess_exec(
|
|
126
|
+
"node", "--version",
|
|
127
|
+
stdout=asyncio.subprocess.PIPE,
|
|
128
|
+
stderr=asyncio.subprocess.PIPE,
|
|
129
|
+
)
|
|
130
|
+
stdout, _ = await proc.communicate()
|
|
131
|
+
version = stdout.decode("utf-8", errors="replace").strip()
|
|
132
|
+
checks["node_version"] = version
|
|
133
|
+
checks["node_ok"] = bool(version) and proc.returncode == 0
|
|
134
|
+
except FileNotFoundError:
|
|
135
|
+
checks["node_version"] = "未安装"
|
|
136
|
+
|
|
137
|
+
# 检测 npx
|
|
138
|
+
try:
|
|
139
|
+
proc = await asyncio.create_subprocess_exec(
|
|
140
|
+
"npx", "--version",
|
|
141
|
+
stdout=asyncio.subprocess.PIPE,
|
|
142
|
+
stderr=asyncio.subprocess.PIPE,
|
|
143
|
+
)
|
|
144
|
+
stdout, _ = await proc.communicate()
|
|
145
|
+
checks["npx_ok"] = proc.returncode == 0
|
|
146
|
+
except FileNotFoundError:
|
|
147
|
+
pass
|
|
148
|
+
|
|
149
|
+
# 检测 ffmpeg
|
|
150
|
+
try:
|
|
151
|
+
proc = await asyncio.create_subprocess_exec(
|
|
152
|
+
"ffmpeg", "-version",
|
|
153
|
+
stdout=asyncio.subprocess.PIPE,
|
|
154
|
+
stderr=asyncio.subprocess.PIPE,
|
|
155
|
+
)
|
|
156
|
+
stdout, _ = await proc.communicate()
|
|
157
|
+
if proc.returncode == 0:
|
|
158
|
+
first_line = stdout.decode("utf-8", errors="replace").split("\n")[0].strip()
|
|
159
|
+
checks["ffmpeg_version"] = first_line
|
|
160
|
+
checks["ffmpeg_ok"] = True
|
|
161
|
+
except FileNotFoundError:
|
|
162
|
+
checks["ffmpeg_version"] = "未安装"
|
|
163
|
+
|
|
164
|
+
checks["all_ok"] = checks["node_ok"] and checks["ffmpeg_ok"] and checks["npx_ok"]
|
|
165
|
+
return checks
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def parse_lint_json(stdout: str) -> dict:
|
|
169
|
+
"""
|
|
170
|
+
解析 hyperframes lint --json 输出
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
stdout: lint --json 的标准输出
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
解析后的 lint 结果字典,包含 errorCount, warningCount, findings 等字段
|
|
177
|
+
"""
|
|
178
|
+
try:
|
|
179
|
+
return json.loads(stdout)
|
|
180
|
+
except json.JSONDecodeError:
|
|
181
|
+
return {"error": "无法解析 lint 输出", "raw": stdout}
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
# ==================== 工具函数 ====================
|
|
185
|
+
|
|
186
|
+
def find_workspace() -> str:
|
|
187
|
+
"""
|
|
188
|
+
获取工作目录(优先从环境变量读取,否则用当前目录)
|
|
189
|
+
|
|
190
|
+
Returns:
|
|
191
|
+
工作目录绝对路径
|
|
192
|
+
"""
|
|
193
|
+
workspace = os.environ.get(WORKSPACE_ENV_VAR)
|
|
194
|
+
if workspace and os.path.isdir(workspace):
|
|
195
|
+
return workspace
|
|
196
|
+
return os.getcwd()
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
timeverse-hyperframes-mcp — MCP 服务器主入口
|
|
4
|
+
|
|
5
|
+
功能简述:
|
|
6
|
+
使用 FastMCP 创建 MCP 服务器,将所有 HyperFrames CLI 命令包装为 MCP 工具。
|
|
7
|
+
通过 stdio 协议与 MCP 客户端(如 TimeVerseStudio)通信。
|
|
8
|
+
|
|
9
|
+
使用示例:
|
|
10
|
+
# 直接运行
|
|
11
|
+
python -m timeverse_hyperframes_mcp.server
|
|
12
|
+
|
|
13
|
+
# 或作为 CLI 命令(安装后)
|
|
14
|
+
timeverse-hyperframes-mcp
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import asyncio
|
|
18
|
+
import logging
|
|
19
|
+
import sys
|
|
20
|
+
|
|
21
|
+
from mcp.server import FastMCP
|
|
22
|
+
|
|
23
|
+
from . import __version__
|
|
24
|
+
from .tools import register_all
|
|
25
|
+
|
|
26
|
+
# ==================== 日志配置 ====================
|
|
27
|
+
|
|
28
|
+
logging.basicConfig(
|
|
29
|
+
level=logging.WARNING,
|
|
30
|
+
format="[%(levelname)s] %(name)s: %(message)s",
|
|
31
|
+
stream=sys.stderr,
|
|
32
|
+
)
|
|
33
|
+
logger = logging.getLogger(__name__)
|
|
34
|
+
|
|
35
|
+
# ==================== 创建 MCP 服务器 ====================
|
|
36
|
+
|
|
37
|
+
mcp = FastMCP(
|
|
38
|
+
name="timeverse-hyperframes-mcp",
|
|
39
|
+
instructions="HyperFrames MCP Server — 将 HTML/CSS/JS 渲染为 MP4 视频。支持 init / preview / render / lint / TTS / transcribe 等命令。",
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
# ==================== 注册工具 ====================
|
|
44
|
+
|
|
45
|
+
register_all(mcp)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
# ==================== 启动 ====================
|
|
49
|
+
|
|
50
|
+
def main() -> None:
|
|
51
|
+
"""启动 MCP 服务器(stdio 模式)"""
|
|
52
|
+
logger.info("启动 timeverse-hyperframes-mcp v%s(stdio 模式)", __version__)
|
|
53
|
+
mcp.run(transport="stdio")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
if __name__ == "__main__":
|
|
57
|
+
main()
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""
|
|
2
|
+
HyperFrames MCP 工具模块
|
|
3
|
+
|
|
4
|
+
功能简述:
|
|
5
|
+
将所有工具函数注册到 FastMCP 服务器实例。
|
|
6
|
+
每个子模块的 register_xxx(mcp) 函数负责注册对应的 MCP 工具。
|
|
7
|
+
|
|
8
|
+
主要方法清单:
|
|
9
|
+
- register_all(mcp): 注册所有工具到指定 FastMCP 实例
|
|
10
|
+
|
|
11
|
+
使用示例:
|
|
12
|
+
from . import register_all
|
|
13
|
+
register_all(mcp)
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from . import init, render, preview, lint, media, system
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def register_all(mcp) -> None:
|
|
20
|
+
"""注册所有 HyperFrames MCP 工具"""
|
|
21
|
+
init.register_tools(mcp)
|
|
22
|
+
render.register_tools(mcp)
|
|
23
|
+
preview.register_tools(mcp)
|
|
24
|
+
lint.register_tools(mcp)
|
|
25
|
+
media.register_tools(mcp)
|
|
26
|
+
system.register_tools(mcp)
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"""
|
|
2
|
+
hyperframes_init — 视频项目初始化工具
|
|
3
|
+
|
|
4
|
+
功能简述:
|
|
5
|
+
封装 npx hyperframes init 命令,支持创建新视频项目。
|
|
6
|
+
可选择模板、预设示例、附带视频/音频素材。
|
|
7
|
+
|
|
8
|
+
主要方法清单:
|
|
9
|
+
- register_tools(mcp): 注册 hyperframes_init / hyperframes_list_templates
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import os
|
|
13
|
+
from typing import Optional
|
|
14
|
+
|
|
15
|
+
from ..cli_executor import run_hyperframes, find_workspace
|
|
16
|
+
|
|
17
|
+
# ==================== 常量定义 ====================
|
|
18
|
+
|
|
19
|
+
AVAILABLE_TEMPLATES = [
|
|
20
|
+
"blank",
|
|
21
|
+
"warm-grain",
|
|
22
|
+
"play-mode",
|
|
23
|
+
"swiss-grid",
|
|
24
|
+
"vignelli",
|
|
25
|
+
"decision-tree",
|
|
26
|
+
"kinetic-type",
|
|
27
|
+
"product-promo",
|
|
28
|
+
"nyt-graph",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def register_tools(mcp) -> None:
|
|
33
|
+
"""注册初始化相关的 MCP 工具"""
|
|
34
|
+
|
|
35
|
+
@mcp.tool(
|
|
36
|
+
name="hyperframes_init",
|
|
37
|
+
description="创建新的 HyperFrames 视频项目。初始化目录结构、安装依赖。"
|
|
38
|
+
)
|
|
39
|
+
async def hyperframes_init(
|
|
40
|
+
project_name: str,
|
|
41
|
+
template: Optional[str] = None,
|
|
42
|
+
video_path: Optional[str] = None,
|
|
43
|
+
audio_path: Optional[str] = None,
|
|
44
|
+
non_interactive: bool = True,
|
|
45
|
+
parent_dir: Optional[str] = None,
|
|
46
|
+
) -> str:
|
|
47
|
+
"""
|
|
48
|
+
创建新的 HyperFrames 视频项目
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
project_name: 项目目录名称(如 "my-video")
|
|
52
|
+
template: 模板名称。可选值:blank, warm-grain, play-mode, swiss-grid, vignelli, decision-tree, kinetic-type, product-promo, nyt-graph
|
|
53
|
+
video_path: 附带已有视频文件路径(将自动拷贝到项目中)
|
|
54
|
+
audio_path: 附带音频文件路径(将自动拷贝并转写)
|
|
55
|
+
non_interactive: 是否静默模式(默认 True,适合 AI 调用)
|
|
56
|
+
parent_dir: 父级目录,默认为当前工作空间
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
项目的标准输出信息
|
|
60
|
+
"""
|
|
61
|
+
args = ["init", project_name]
|
|
62
|
+
|
|
63
|
+
if non_interactive:
|
|
64
|
+
args.append("--non-interactive")
|
|
65
|
+
|
|
66
|
+
if template:
|
|
67
|
+
if template not in AVAILABLE_TEMPLATES:
|
|
68
|
+
available = ", ".join(AVAILABLE_TEMPLATES)
|
|
69
|
+
return (
|
|
70
|
+
f"❌ 无效模板: {template}\n"
|
|
71
|
+
f"可用模板: {available}"
|
|
72
|
+
)
|
|
73
|
+
args.extend(["--example", template])
|
|
74
|
+
|
|
75
|
+
if video_path:
|
|
76
|
+
args.extend(["--video", video_path])
|
|
77
|
+
|
|
78
|
+
if audio_path:
|
|
79
|
+
args.extend(["--audio", audio_path])
|
|
80
|
+
|
|
81
|
+
workspace = parent_dir or find_workspace()
|
|
82
|
+
result = await run_hyperframes(args, cwd=workspace)
|
|
83
|
+
|
|
84
|
+
if result["success"]:
|
|
85
|
+
project_dir = os.path.join(workspace, project_name)
|
|
86
|
+
return (
|
|
87
|
+
f"✅ 项目创建成功\n"
|
|
88
|
+
f" 路径: {project_dir}\n"
|
|
89
|
+
f" 模板: {template or 'blank'}\n"
|
|
90
|
+
f"{result['stdout']}"
|
|
91
|
+
)
|
|
92
|
+
else:
|
|
93
|
+
return f"❌ 项目创建失败\n{result['stderr']}"
|
|
94
|
+
|
|
95
|
+
@mcp.tool(
|
|
96
|
+
name="hyperframes_list_templates",
|
|
97
|
+
description="列出 HyperFrames 所有可用的项目模板。"
|
|
98
|
+
)
|
|
99
|
+
async def hyperframes_list_templates() -> str:
|
|
100
|
+
"""
|
|
101
|
+
列出所有可用的项目模板
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
模板列表与说明
|
|
105
|
+
"""
|
|
106
|
+
templates_info = {
|
|
107
|
+
"blank": "空项目,从零开始",
|
|
108
|
+
"warm-grain": "暖色颗粒纹理风格",
|
|
109
|
+
"play-mode": "Play 模式示例",
|
|
110
|
+
"swiss-grid": "瑞士网格排版风格",
|
|
111
|
+
"vignelli": "Vignelli 风格",
|
|
112
|
+
"decision-tree": "决策树流程图",
|
|
113
|
+
"kinetic-type": "动态文字排版",
|
|
114
|
+
"product-promo": "产品宣传视频模板",
|
|
115
|
+
"nyt-graph": "纽约时报风格数据可视化",
|
|
116
|
+
}
|
|
117
|
+
lines = ["📁 HyperFrames 可用模板:\n"]
|
|
118
|
+
for t in AVAILABLE_TEMPLATES:
|
|
119
|
+
desc = templates_info.get(t, "")
|
|
120
|
+
lines.append(f" - {t}: {desc}")
|
|
121
|
+
return "\n".join(lines)
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"""
|
|
2
|
+
hyperframes_lint — 项目语法检查工具
|
|
3
|
+
|
|
4
|
+
功能简述:
|
|
5
|
+
封装 npx hyperframes lint 命令,验证 composition 语法正确性。
|
|
6
|
+
检查 data-composition-id、track 重叠、timeline 注册等问题。
|
|
7
|
+
|
|
8
|
+
主要方法清单:
|
|
9
|
+
- register_tools(mcp): 注册 hyperframes_lint
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from typing import Optional
|
|
13
|
+
|
|
14
|
+
from ..cli_executor import run_hyperframes, find_workspace, parse_lint_json
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def register_tools(mcp) -> None:
|
|
18
|
+
"""注册 lint 相关的 MCP 工具"""
|
|
19
|
+
|
|
20
|
+
@mcp.tool(
|
|
21
|
+
name="hyperframes_lint",
|
|
22
|
+
description="检查 HyperFrames 项目的语法和结构完整性。建议在 preview/render 前运行。"
|
|
23
|
+
)
|
|
24
|
+
async def hyperframes_lint(
|
|
25
|
+
project_dir: Optional[str] = None,
|
|
26
|
+
verbose: bool = False,
|
|
27
|
+
json_output: bool = True,
|
|
28
|
+
) -> str:
|
|
29
|
+
"""
|
|
30
|
+
检查 HyperFrames 项目的语法和结构完整性
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
project_dir: 项目目录路径。默认为当前工作空间
|
|
34
|
+
verbose: 是否显示 info 级别的详细信息(默认只显示 errors 和 warnings)
|
|
35
|
+
json_output: 是否输出结构化 JSON 结果(默认 True)
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
lint 检查结果
|
|
39
|
+
"""
|
|
40
|
+
args = ["lint"]
|
|
41
|
+
|
|
42
|
+
if verbose:
|
|
43
|
+
args.append("--verbose")
|
|
44
|
+
|
|
45
|
+
if json_output:
|
|
46
|
+
args.append("--json")
|
|
47
|
+
|
|
48
|
+
workspace = find_workspace() if not project_dir else project_dir
|
|
49
|
+
result = await run_hyperframes(args, cwd=workspace)
|
|
50
|
+
|
|
51
|
+
if not result["success"] and not result["stdout"]:
|
|
52
|
+
return f"❌ lint 检查失败\n{result['stderr']}"
|
|
53
|
+
|
|
54
|
+
if json_output and result["stdout"]:
|
|
55
|
+
parsed = parse_lint_json(result["stdout"])
|
|
56
|
+
if "errorCount" in parsed:
|
|
57
|
+
ec = parsed.get("errorCount", 0)
|
|
58
|
+
wc = parsed.get("warningCount", 0)
|
|
59
|
+
ic = parsed.get("infoCount", 0)
|
|
60
|
+
findings = parsed.get("findings", [])
|
|
61
|
+
lines = [f"📋 Lint 结果:{ec} 个错误, {wc} 个警告, {ic} 个信息\n"]
|
|
62
|
+
for f in findings:
|
|
63
|
+
severity = f.get("severity", "info")
|
|
64
|
+
icon = {"error": "❌", "warning": "⚠️", "info": "ℹ️"}.get(severity, "•")
|
|
65
|
+
msg = f.get("message", "")
|
|
66
|
+
loc = f.get("location", "")
|
|
67
|
+
lines.append(f" {icon} [{severity}] {msg}")
|
|
68
|
+
if loc:
|
|
69
|
+
lines[-1] += f" ({loc})"
|
|
70
|
+
if ec > 0:
|
|
71
|
+
lines.append("\n💡 存在错误,建议修复后再 preview/render")
|
|
72
|
+
return "\n".join(lines)
|
|
73
|
+
|
|
74
|
+
return result["stdout"] or result["stderr"]
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"""
|
|
2
|
+
hyperframes_media — 媒体处理工具(TTS / 转录)
|
|
3
|
+
|
|
4
|
+
功能简述:
|
|
5
|
+
封装 npx hyperframes tts 和 npx hyperframes transcribe 命令。
|
|
6
|
+
支持文本转语音、音频/视频转录字幕、字幕导入导出。
|
|
7
|
+
|
|
8
|
+
主要方法清单:
|
|
9
|
+
- register_tools(mcp): 注册 hyperframes_tts / hyperframes_transcribe / hyperframes_list_voices
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from typing import Optional
|
|
13
|
+
|
|
14
|
+
from ..cli_executor import run_hyperframes, find_workspace
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def register_tools(mcp) -> None:
|
|
18
|
+
"""注册媒体处理相关的 MCP 工具"""
|
|
19
|
+
|
|
20
|
+
@mcp.tool(
|
|
21
|
+
name="hyperframes_tts",
|
|
22
|
+
description="文本转语音(Text-to-Speech)。生成配音音频文件。"
|
|
23
|
+
)
|
|
24
|
+
async def hyperframes_tts(
|
|
25
|
+
text: str,
|
|
26
|
+
voice: Optional[str] = None,
|
|
27
|
+
output: Optional[str] = None,
|
|
28
|
+
project_dir: Optional[str] = None,
|
|
29
|
+
) -> str:
|
|
30
|
+
"""
|
|
31
|
+
文本转语音
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
text: 需要朗读的文本内容,或包含文本的文件路径
|
|
35
|
+
voice: 语音角色(如 af_nova, bf_emma)。不指定则用默认角色
|
|
36
|
+
output: 输出音频文件路径,默认 narration.wav
|
|
37
|
+
project_dir: 项目目录路径
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
TTS 执行结果
|
|
41
|
+
"""
|
|
42
|
+
args = ["tts"]
|
|
43
|
+
|
|
44
|
+
# 判断 text 是文件路径还是纯文本
|
|
45
|
+
args.append(text)
|
|
46
|
+
|
|
47
|
+
if voice:
|
|
48
|
+
args.extend(["--voice", voice])
|
|
49
|
+
|
|
50
|
+
if output:
|
|
51
|
+
args.extend(["--output", output])
|
|
52
|
+
|
|
53
|
+
workspace = find_workspace() if not project_dir else project_dir
|
|
54
|
+
result = await run_hyperframes(args, cwd=workspace)
|
|
55
|
+
|
|
56
|
+
if result["success"]:
|
|
57
|
+
return f"✅ TTS 生成成功\n{result['stdout']}"
|
|
58
|
+
else:
|
|
59
|
+
return f"❌ TTS 生成失败\n{result['stderr']}"
|
|
60
|
+
|
|
61
|
+
@mcp.tool(
|
|
62
|
+
name="hyperframes_list_voices",
|
|
63
|
+
description="列出所有可用的 TTS 语音角色。"
|
|
64
|
+
)
|
|
65
|
+
async def hyperframes_list_voices(
|
|
66
|
+
project_dir: Optional[str] = None,
|
|
67
|
+
) -> str:
|
|
68
|
+
"""
|
|
69
|
+
列出所有可用的 TTS 语音角色
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
project_dir: 项目目录路径
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
可用语音角色列表
|
|
76
|
+
"""
|
|
77
|
+
result = await run_hyperframes(
|
|
78
|
+
["tts", "--list"],
|
|
79
|
+
cwd=find_workspace() if not project_dir else project_dir,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
if result["success"]:
|
|
83
|
+
return result["stdout"]
|
|
84
|
+
else:
|
|
85
|
+
return f"❌ 获取语音列表失败\n{result['stderr']}"
|
|
86
|
+
|
|
87
|
+
@mcp.tool(
|
|
88
|
+
name="hyperframes_transcribe",
|
|
89
|
+
description="音频/视频转录为文字字幕。支持多种输入格式。"
|
|
90
|
+
)
|
|
91
|
+
async def hyperframes_transcribe(
|
|
92
|
+
input_path: str,
|
|
93
|
+
model: Optional[str] = None,
|
|
94
|
+
language: Optional[str] = None,
|
|
95
|
+
output_format: Optional[str] = None,
|
|
96
|
+
project_dir: Optional[str] = None,
|
|
97
|
+
) -> str:
|
|
98
|
+
"""
|
|
99
|
+
音频/视频转录文字
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
input_path: 输入音频/视频文件路径,或现有字幕文件路径(.srt/.vtt/.json)
|
|
103
|
+
model: Whisper 模型大小(如 tiny, base, small, medium, large)。默认 medium.en
|
|
104
|
+
language: 语言代码(如 en, zh, ja)。不指定则自动检测
|
|
105
|
+
output_format: 输出字幕格式(当输入是媒体文件时)。不指定则自动推断
|
|
106
|
+
project_dir: 项目目录路径
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
转录结果
|
|
110
|
+
"""
|
|
111
|
+
args = ["transcribe", input_path]
|
|
112
|
+
|
|
113
|
+
if model:
|
|
114
|
+
args.extend(["--model", model])
|
|
115
|
+
|
|
116
|
+
if language:
|
|
117
|
+
args.extend(["--language", language])
|
|
118
|
+
|
|
119
|
+
if output_format:
|
|
120
|
+
args.extend(["--output-format", output_format])
|
|
121
|
+
|
|
122
|
+
workspace = find_workspace() if not project_dir else project_dir
|
|
123
|
+
result = await run_hyperframes(args, cwd=workspace, timeout=300)
|
|
124
|
+
|
|
125
|
+
if result["success"]:
|
|
126
|
+
return f"✅ 转录成功\n{result['stdout']}"
|
|
127
|
+
else:
|
|
128
|
+
return f"❌ 转录失败\n{result['stderr']}"
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""
|
|
2
|
+
hyperframes_preview — 视频预览工具
|
|
3
|
+
|
|
4
|
+
功能简述:
|
|
5
|
+
封装 npx hyperframes preview 命令,启动本地实时预览服务。
|
|
6
|
+
支持自定义端口,热重载。
|
|
7
|
+
|
|
8
|
+
主要方法清单:
|
|
9
|
+
- register_tools(mcp): 注册 hyperframes_preview
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from typing import Optional
|
|
13
|
+
|
|
14
|
+
from ..cli_executor import run_hyperframes, find_workspace
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def register_tools(mcp) -> None:
|
|
18
|
+
"""注册预览相关的 MCP 工具"""
|
|
19
|
+
|
|
20
|
+
@mcp.tool(
|
|
21
|
+
name="hyperframes_preview",
|
|
22
|
+
description="启动 HyperFrames 项目的本地实时预览服务。热重载,编辑即更新。"
|
|
23
|
+
)
|
|
24
|
+
async def hyperframes_preview(
|
|
25
|
+
project_dir: Optional[str] = None,
|
|
26
|
+
port: Optional[int] = None,
|
|
27
|
+
) -> str:
|
|
28
|
+
"""
|
|
29
|
+
启动本地预览服务
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
project_dir: 项目目录路径。默认为当前工作空间
|
|
33
|
+
port: 端口号(默认 3002)
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
预览服务信息,包含访问地址
|
|
37
|
+
"""
|
|
38
|
+
args = ["preview"]
|
|
39
|
+
|
|
40
|
+
if port:
|
|
41
|
+
args.extend(["--port", str(port)])
|
|
42
|
+
|
|
43
|
+
workspace = find_workspace() if not project_dir else project_dir
|
|
44
|
+
# preview 是一个持续运行的服务,设置较短超时拿到初始输出即可
|
|
45
|
+
result = await run_hyperframes(args, cwd=workspace, timeout=15)
|
|
46
|
+
|
|
47
|
+
if result["success"]:
|
|
48
|
+
output = result["stdout"]
|
|
49
|
+
# 从 stdout 中提取 URL
|
|
50
|
+
url = ""
|
|
51
|
+
for line in output.split("\n"):
|
|
52
|
+
if "http" in line:
|
|
53
|
+
url = line.strip()
|
|
54
|
+
break
|
|
55
|
+
msg = f"✅ 预览服务已启动\n"
|
|
56
|
+
if url:
|
|
57
|
+
msg += f" 访问地址: {url}\n"
|
|
58
|
+
msg += output
|
|
59
|
+
return msg
|
|
60
|
+
else:
|
|
61
|
+
return f"❌ 预览启动失败\n{result['stderr']}"
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"""
|
|
2
|
+
hyperframes_render — 视频渲染工具
|
|
3
|
+
|
|
4
|
+
功能简述:
|
|
5
|
+
封装 npx hyperframes render 命令,支持多种质量/格式/帧率。
|
|
6
|
+
支持指定 composition、GPU 加速、Docker 可重现构建等高级选项。
|
|
7
|
+
|
|
8
|
+
主要方法清单:
|
|
9
|
+
- register_tools(mcp): 注册 hyperframes_render
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from typing import Optional
|
|
13
|
+
|
|
14
|
+
from ..cli_executor import run_hyperframes, find_workspace, RENDER_TIMEOUT
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def register_tools(mcp) -> None:
|
|
18
|
+
"""注册渲染相关的 MCP 工具"""
|
|
19
|
+
|
|
20
|
+
@mcp.tool(
|
|
21
|
+
name="hyperframes_render",
|
|
22
|
+
description="将 HyperFrames 项目渲染为 MP4/WebM 视频。"
|
|
23
|
+
)
|
|
24
|
+
async def hyperframes_render(
|
|
25
|
+
project_dir: Optional[str] = None,
|
|
26
|
+
output: Optional[str] = None,
|
|
27
|
+
fps: Optional[int] = None,
|
|
28
|
+
quality: Optional[str] = None,
|
|
29
|
+
format: Optional[str] = None,
|
|
30
|
+
composition: Optional[str] = None,
|
|
31
|
+
workers: Optional[int] = None,
|
|
32
|
+
gpu: bool = False,
|
|
33
|
+
docker: bool = False,
|
|
34
|
+
strict: bool = False,
|
|
35
|
+
strict_all: bool = False,
|
|
36
|
+
timeout: int = RENDER_TIMEOUT,
|
|
37
|
+
) -> str:
|
|
38
|
+
"""
|
|
39
|
+
渲染 HyperFrames 视频项目
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
project_dir: 项目目录路径。默认为当前工作空间
|
|
43
|
+
output: 输出视频路径。默认保存在 renders/ 目录
|
|
44
|
+
fps: 帧率,可选 24/30/60。默认 30
|
|
45
|
+
quality: 渲染质量。可选 draft(快速迭代)/ standard(审阅)/ high(最终交付)
|
|
46
|
+
format: 输出格式。mp4 或 webm(webm 支持透明背景)
|
|
47
|
+
composition: 只渲染指定的 composition 文件(相对于项目目录的路径)
|
|
48
|
+
workers: 并行工作进程数,可选 1-8 或 auto。默认 auto
|
|
49
|
+
gpu: 启用 GPU 加速编码(默认关闭)
|
|
50
|
+
docker: 使用 Docker 可重现构建(默认关闭)
|
|
51
|
+
strict: lint 错误时中止渲染(默认关闭)
|
|
52
|
+
strict_all: lint 错误和警告时都中止(默认关闭)
|
|
53
|
+
timeout: 渲染超时秒数,默认 600
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
渲染结果输出信息
|
|
57
|
+
"""
|
|
58
|
+
args = ["render"]
|
|
59
|
+
|
|
60
|
+
if output:
|
|
61
|
+
args.extend(["--output", output])
|
|
62
|
+
|
|
63
|
+
if fps:
|
|
64
|
+
if fps not in (24, 30, 60):
|
|
65
|
+
return f"❌ 无效帧率: {fps}。可选值: 24, 30, 60"
|
|
66
|
+
args.extend(["--fps", str(fps)])
|
|
67
|
+
|
|
68
|
+
if quality:
|
|
69
|
+
if quality not in ("draft", "standard", "high"):
|
|
70
|
+
return "❌ 无效质量值。可选: draft, standard, high"
|
|
71
|
+
args.extend(["--quality", quality])
|
|
72
|
+
|
|
73
|
+
if format:
|
|
74
|
+
if format not in ("mp4", "webm"):
|
|
75
|
+
return "❌ 无效格式。可选: mp4, webm"
|
|
76
|
+
args.extend(["--format", format])
|
|
77
|
+
|
|
78
|
+
if composition:
|
|
79
|
+
args.extend(["--composition", composition])
|
|
80
|
+
|
|
81
|
+
if workers is not None:
|
|
82
|
+
w = str(workers)
|
|
83
|
+
if w not in ("auto", "1", "2", "3", "4", "5", "6", "7", "8"):
|
|
84
|
+
return "❌ 无效 workers 值。可选: auto, 1-8"
|
|
85
|
+
args.extend(["--workers", w])
|
|
86
|
+
|
|
87
|
+
if gpu:
|
|
88
|
+
args.append("--gpu")
|
|
89
|
+
|
|
90
|
+
if docker:
|
|
91
|
+
args.append("--docker")
|
|
92
|
+
|
|
93
|
+
if strict:
|
|
94
|
+
args.append("--strict")
|
|
95
|
+
|
|
96
|
+
if strict_all:
|
|
97
|
+
args.append("--strict-all")
|
|
98
|
+
|
|
99
|
+
workspace = find_workspace() if not project_dir else project_dir
|
|
100
|
+
result = await run_hyperframes(args, cwd=workspace, timeout=timeout)
|
|
101
|
+
|
|
102
|
+
if result["success"]:
|
|
103
|
+
return f"✅ 渲染成功\n{result['stdout']}"
|
|
104
|
+
else:
|
|
105
|
+
return f"❌ 渲染失败\n{result['stderr']}"
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
"""
|
|
2
|
+
hyperframes_system — 系统管理工具
|
|
3
|
+
|
|
4
|
+
功能简述:
|
|
5
|
+
封装 doctor / info / upgrade / benchmark / compositions 等管理类命令。
|
|
6
|
+
用于检测环境、获取版本信息、列出 composition、跑基准测试等。
|
|
7
|
+
|
|
8
|
+
主要方法清单:
|
|
9
|
+
- register_tools(mcp): 注册 hyperframes_doctor / hyperframes_info / 等
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from typing import Optional
|
|
13
|
+
|
|
14
|
+
from ..cli_executor import run_hyperframes, find_workspace, check_environment
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def register_tools(mcp) -> None:
|
|
18
|
+
"""注册系统管理相关的 MCP 工具"""
|
|
19
|
+
|
|
20
|
+
@mcp.tool(
|
|
21
|
+
name="hyperframes_doctor",
|
|
22
|
+
description="检测 HyperFrames 运行环境(Node.js, FFmpeg, Chrome 等)。渲染前建议先运行。"
|
|
23
|
+
)
|
|
24
|
+
async def hyperframes_doctor(
|
|
25
|
+
project_dir: Optional[str] = None,
|
|
26
|
+
) -> str:
|
|
27
|
+
"""
|
|
28
|
+
检测运行环境
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
project_dir: 项目目录。不指定则检查全局环境
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
环境检测结果
|
|
35
|
+
"""
|
|
36
|
+
env_check = await check_environment()
|
|
37
|
+
lines = ["🔍 HyperFrames 环境检测\n"]
|
|
38
|
+
lines.append(f" Node.js: {'✅' if env_check['node_ok'] else '❌'} {env_check['node_version']}")
|
|
39
|
+
lines.append(f" npx: {'✅' if env_check['npx_ok'] else '❌'}")
|
|
40
|
+
lines.append(f" FFmpeg: {'✅' if env_check['ffmpeg_ok'] else '❌'} {env_check['ffmpeg_version']}")
|
|
41
|
+
|
|
42
|
+
if not env_check["all_ok"]:
|
|
43
|
+
lines.append("\n⚠️ 环境不完整,请安装缺失依赖:")
|
|
44
|
+
if not env_check["node_ok"]:
|
|
45
|
+
lines.append(" - Node.js >= 22: https://nodejs.org")
|
|
46
|
+
if not env_check["ffmpeg_ok"]:
|
|
47
|
+
lines.append(" - FFmpeg: brew install ffmpeg / apt install ffmpeg")
|
|
48
|
+
if not env_check["npx_ok"]:
|
|
49
|
+
lines.append(" - npx: npm install -g npx")
|
|
50
|
+
|
|
51
|
+
# 也跑一下 hyperframes doctor
|
|
52
|
+
workspace = find_workspace() if not project_dir else project_dir
|
|
53
|
+
hf_result = await run_hyperframes(["doctor"], cwd=workspace, timeout=30)
|
|
54
|
+
if hf_result["stdout"]:
|
|
55
|
+
lines.append(f"\n--- HyperFrames Doctor ---\n{hf_result['stdout']}")
|
|
56
|
+
|
|
57
|
+
return "\n".join(lines)
|
|
58
|
+
|
|
59
|
+
@mcp.tool(
|
|
60
|
+
name="hyperframes_info",
|
|
61
|
+
description="获取 HyperFrames 版本和环境详细信息。"
|
|
62
|
+
)
|
|
63
|
+
async def hyperframes_info(
|
|
64
|
+
project_dir: Optional[str] = None,
|
|
65
|
+
) -> str:
|
|
66
|
+
"""
|
|
67
|
+
获取版本和环境信息
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
project_dir: 项目目录路径
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
版本和环境信息
|
|
74
|
+
"""
|
|
75
|
+
result = await run_hyperframes(
|
|
76
|
+
["info"],
|
|
77
|
+
cwd=find_workspace() if not project_dir else project_dir,
|
|
78
|
+
)
|
|
79
|
+
if result["success"]:
|
|
80
|
+
return result["stdout"]
|
|
81
|
+
else:
|
|
82
|
+
return f"❌ 获取信息失败\n{result['stderr']}"
|
|
83
|
+
|
|
84
|
+
@mcp.tool(
|
|
85
|
+
name="hyperframes_upgrade",
|
|
86
|
+
description="检查 HyperFrames 是否有更新版本。"
|
|
87
|
+
)
|
|
88
|
+
async def hyperframes_upgrade(
|
|
89
|
+
check_only: bool = True,
|
|
90
|
+
json_output: bool = True,
|
|
91
|
+
) -> str:
|
|
92
|
+
"""
|
|
93
|
+
检查 HyperFrames 版本更新
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
check_only: 仅检查不升级(默认 True)
|
|
97
|
+
json_output: JSON 格式输出(默认 True,适合 AI 解析)
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
版本更新信息
|
|
101
|
+
"""
|
|
102
|
+
args = ["upgrade"]
|
|
103
|
+
if check_only:
|
|
104
|
+
args.append("--check")
|
|
105
|
+
if json_output:
|
|
106
|
+
args.extend(["--check", "--json"])
|
|
107
|
+
|
|
108
|
+
result = await run_hyperframes(args)
|
|
109
|
+
|
|
110
|
+
if result["success"]:
|
|
111
|
+
return result["stdout"]
|
|
112
|
+
else:
|
|
113
|
+
return f"❌ 检查更新失败\n{result['stderr']}"
|
|
114
|
+
|
|
115
|
+
@mcp.tool(
|
|
116
|
+
name="hyperframes_compositions",
|
|
117
|
+
description="列出 HyperFrames 项目中的所有 composition 文件。"
|
|
118
|
+
)
|
|
119
|
+
async def hyperframes_compositions(
|
|
120
|
+
project_dir: Optional[str] = None,
|
|
121
|
+
) -> str:
|
|
122
|
+
"""
|
|
123
|
+
列出项目中的 composition 文件
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
project_dir: 项目目录路径
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
composition 列表
|
|
130
|
+
"""
|
|
131
|
+
result = await run_hyperframes(
|
|
132
|
+
["compositions"],
|
|
133
|
+
cwd=find_workspace() if not project_dir else project_dir,
|
|
134
|
+
)
|
|
135
|
+
if result["success"]:
|
|
136
|
+
return f"📄 Composition 列表:\n{result['stdout']}"
|
|
137
|
+
else:
|
|
138
|
+
return f"❌ 获取 composition 列表失败\n{result['stderr']}"
|
|
139
|
+
|
|
140
|
+
@mcp.tool(
|
|
141
|
+
name="hyperframes_benchmark",
|
|
142
|
+
description="对 HyperFrames 项目运行渲染基准测试。"
|
|
143
|
+
)
|
|
144
|
+
async def hyperframes_benchmark(
|
|
145
|
+
composition: Optional[str] = None,
|
|
146
|
+
project_dir: Optional[str] = None,
|
|
147
|
+
) -> str:
|
|
148
|
+
"""
|
|
149
|
+
运行渲染性能基准测试
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
composition: 指定的 composition 文件路径(相对于项目目录)
|
|
153
|
+
project_dir: 项目目录路径
|
|
154
|
+
|
|
155
|
+
Returns:
|
|
156
|
+
基准测试结果
|
|
157
|
+
"""
|
|
158
|
+
args = ["benchmark"]
|
|
159
|
+
if composition:
|
|
160
|
+
args.append(composition)
|
|
161
|
+
|
|
162
|
+
workspace = find_workspace() if not project_dir else project_dir
|
|
163
|
+
result = await run_hyperframes(args, cwd=workspace, timeout=300)
|
|
164
|
+
|
|
165
|
+
if result["success"]:
|
|
166
|
+
return f"📊 基准测试结果:\n{result['stdout']}"
|
|
167
|
+
else:
|
|
168
|
+
return f"❌ 基准测试失败\n{result['stderr']}"
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: timeverse-hyperframes-mcp
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: MCP server wrapping HyperFrames CLI — turn HTML/CSS/JS into MP4 videos
|
|
5
|
+
Requires-Python: >=3.11
|
|
6
|
+
Requires-Dist: mcp>=1.0.0
|
|
7
|
+
Requires-Dist: httpx>=0.27.0
|
|
8
|
+
Requires-Dist: pydantic>=2.0.0
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
timeverse_hyperframes_mcp/__init__.py,sha256=HTdlmbi6giBoz1QLbCt6SOJVT2stn7RXumBvc-kLjm0,816
|
|
2
|
+
timeverse_hyperframes_mcp/__main__.py,sha256=hbWteOuauvjMZ41amSwr8ywOpFn1QX19DgPoBtIxa7w,56
|
|
3
|
+
timeverse_hyperframes_mcp/cli_executor.py,sha256=wQfycCu_8lWdaJhvQHB-gy9DeFtyhtUhWm_zvktdpuQ,5615
|
|
4
|
+
timeverse_hyperframes_mcp/server.py,sha256=zNcEW_0ml69Fe-eBYFt7Uu_mJxDYseuyB_UIEIHw6_o,1417
|
|
5
|
+
timeverse_hyperframes_mcp/tools/__init__.py,sha256=jqugTRv9TIIJE1cGjZwgqODP62tEaB0kCrgo7ByF2T0,673
|
|
6
|
+
timeverse_hyperframes_mcp/tools/init.py,sha256=UH0W2_vIzjGC46gMjhoQJ3tGITcQiqaeCQWLRdRZbvA,3965
|
|
7
|
+
timeverse_hyperframes_mcp/tools/lint.py,sha256=iJJ-T-so9NH22Dthg2fnn2dQZMk7AcNaI6uUX3iM9xM,2687
|
|
8
|
+
timeverse_hyperframes_mcp/tools/media.py,sha256=PTaTU_cX5kZbzPPIU05qhFuAMFCpwVRCHwqxX0_sbmA,4007
|
|
9
|
+
timeverse_hyperframes_mcp/tools/preview.py,sha256=5AHyZveF0cxdGN7wq11imFhtJv2q9Cfq4_Lxuz9XC_o,1835
|
|
10
|
+
timeverse_hyperframes_mcp/tools/render.py,sha256=tAD2GuhnhCl_46h1uTsD6q7DTsN4zTivaEUfbj9SkO4,3638
|
|
11
|
+
timeverse_hyperframes_mcp/tools/system.py,sha256=xmCb66eJ47f1EhbcqRmLeDez-2F09nMzs7OnopPKETU,5406
|
|
12
|
+
timeverse_hyperframes_mcp-0.1.0.dist-info/METADATA,sha256=b4iQVGxHI4hjT3HF_O-rRiz7gcs_w2J483zP8m1t7Kc,261
|
|
13
|
+
timeverse_hyperframes_mcp-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
14
|
+
timeverse_hyperframes_mcp-0.1.0.dist-info/entry_points.txt,sha256=COzvHluM-CSDKzNSmVMKSqxwugV9KidJfIh5UUxNlD8,84
|
|
15
|
+
timeverse_hyperframes_mcp-0.1.0.dist-info/top_level.txt,sha256=GsEq1qsZra0XtXR532rQWnoWekrDiu5YWATaPI4cdjk,26
|
|
16
|
+
timeverse_hyperframes_mcp-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
timeverse_hyperframes_mcp
|