htmlgen-mcp 0.3.9__py3-none-any.whl → 0.4.8__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.
- htmlgen_mcp/sse_optimizations.py +3 -2
- htmlgen_mcp/web_agent_server.py +228 -77
- {htmlgen_mcp-0.3.9.dist-info → htmlgen_mcp-0.4.8.dist-info}/METADATA +14 -6
- {htmlgen_mcp-0.3.9.dist-info → htmlgen_mcp-0.4.8.dist-info}/RECORD +7 -7
- {htmlgen_mcp-0.3.9.dist-info → htmlgen_mcp-0.4.8.dist-info}/WHEEL +0 -0
- {htmlgen_mcp-0.3.9.dist-info → htmlgen_mcp-0.4.8.dist-info}/entry_points.txt +0 -0
- {htmlgen_mcp-0.3.9.dist-info → htmlgen_mcp-0.4.8.dist-info}/top_level.txt +0 -0
htmlgen_mcp/sse_optimizations.py
CHANGED
|
@@ -154,7 +154,8 @@ async def get_progress_optimized(
|
|
|
154
154
|
"job_id", "status", "plan_id", "progress_log",
|
|
155
155
|
"started_at", "updated_at", "completed_at",
|
|
156
156
|
"project_directory", "model", "upload_status",
|
|
157
|
-
"
|
|
157
|
+
"upload_url", "web_url", "deployment_env",
|
|
158
|
+
"upload_completed_at", "uploaded_directory"
|
|
158
159
|
]
|
|
159
160
|
job_snapshot = {
|
|
160
161
|
k: job_info.get(k) for k in snapshot_keys
|
|
@@ -192,4 +193,4 @@ async def get_progress_optimized(
|
|
|
192
193
|
return {
|
|
193
194
|
"status": "error",
|
|
194
195
|
"message": str(exc)
|
|
195
|
-
}
|
|
196
|
+
}
|
htmlgen_mcp/web_agent_server.py
CHANGED
|
@@ -44,8 +44,9 @@ DEFAULT_PROJECT_ROOT = os.path.abspath(
|
|
|
44
44
|
)
|
|
45
45
|
# 是否自动生成项目子目录(可通过环境变量控制)
|
|
46
46
|
AUTO_CREATE_PROJECT_DIR = os.environ.get("AUTO_CREATE_PROJECT_DIR", "true").lower() == "true"
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
DEFAULT_UPLOAD_URL = os.environ.get(
|
|
48
|
+
"UPLOAD_URL", "https://www.mcpcn.cc/api/fileUploadAndDownload/uploadMcpFile"
|
|
49
|
+
)
|
|
49
50
|
DEFAULT_MODEL = os.environ.get("WEB_AGENT_MODEL", "qwen3-coder-plus-2025-09-23")
|
|
50
51
|
DEFAULT_BASE_URL = os.environ.get(
|
|
51
52
|
"OPENAI_BASE_URL", "https://dashscope.aliyuncs.com/compatible-mode/v1"
|
|
@@ -85,6 +86,38 @@ _CONTEXT_CACHE_BY_ID: dict[str, Dict[str, Any]] = {}
|
|
|
85
86
|
_CONTEXT_ID_BY_PLAN: dict[str, str] = {}
|
|
86
87
|
|
|
87
88
|
|
|
89
|
+
def _resolve_edgeone_deploy_env() -> str:
|
|
90
|
+
"""解析 EdgeOne 自动部署环境,默认 Production。"""
|
|
91
|
+
env_value = (
|
|
92
|
+
os.environ.get("EDGEONE_AUTO_DEPLOY_ENV")
|
|
93
|
+
or os.environ.get("EDGEONE_PAGES_DEPLOY_ENV")
|
|
94
|
+
or "Production"
|
|
95
|
+
)
|
|
96
|
+
return env_value if env_value in {"Production", "Preview"} else "Production"
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _should_upload_zip_to_oss() -> bool:
|
|
100
|
+
"""是否在 EdgeOne 部署前上传 ZIP 到 OSS。"""
|
|
101
|
+
flag = os.environ.get("KEEP_OSS_UPLOAD", "true").strip().lower()
|
|
102
|
+
return flag not in {"0", "false", "no", "off"}
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _extract_zip_url(upload_result: Dict[str, Any]) -> Optional[str]:
|
|
106
|
+
"""从上传结果中提取包含 .zip 的下载地址。"""
|
|
107
|
+
try:
|
|
108
|
+
candidates = [
|
|
109
|
+
upload_result.get("oss_url"),
|
|
110
|
+
upload_result.get("upload_url"),
|
|
111
|
+
(upload_result.get("oss_response") or {}).get("url"),
|
|
112
|
+
upload_result.get("url"),
|
|
113
|
+
]
|
|
114
|
+
for candidate in candidates:
|
|
115
|
+
if candidate and ".zip" in str(candidate).lower():
|
|
116
|
+
return candidate
|
|
117
|
+
except Exception:
|
|
118
|
+
pass
|
|
119
|
+
return None
|
|
120
|
+
|
|
88
121
|
def _job_state_path(job_id: str) -> Path:
|
|
89
122
|
return JOB_STATE_DIR / f"{job_id}.json"
|
|
90
123
|
|
|
@@ -464,37 +497,32 @@ async def execute_plan(
|
|
|
464
497
|
# confirm_each_step: bool = False, # 后台执行模式下用户无法交互确认
|
|
465
498
|
# show_code: bool = False, # 后台执行时用户看不到输出
|
|
466
499
|
# verbose: bool = False, # 后台执行时详细日志意义不大
|
|
467
|
-
save_output: bool =
|
|
500
|
+
# save_output: bool = True, # 已固定为 True,始终创建进度日志
|
|
468
501
|
progress_log: Optional[str] = None,
|
|
469
502
|
) -> Dict[str, Any]:
|
|
470
503
|
"""执行网页构建计划,始终以后台模式运行。
|
|
471
504
|
|
|
472
505
|
参数详细说明:
|
|
473
506
|
- plan_id: 计划的唯一标识符
|
|
474
|
-
由
|
|
507
|
+
由 create_simple_site 工具返回的计划ID,用于从缓存或文件系统中查找对应的执行计划。
|
|
475
508
|
例如:"a1b2c3d4e5f6..." 这样的32位十六进制字符串。
|
|
476
509
|
|
|
477
|
-
- project_root:
|
|
510
|
+
- project_root: 网站文件生成的目标目录路径(可选)
|
|
478
511
|
指定项目文件的输出位置,可以是绝对路径或相对路径。
|
|
479
512
|
例如:"/path/to/my/website" 或 "./my-project"
|
|
480
513
|
如果目录不存在,系统会自动创建。
|
|
481
|
-
|
|
482
|
-
- save_output: 是否保存执行过程的详细输出到文件
|
|
483
|
-
True: 会自动创建进度日志文件,记录所有执行步骤和结果
|
|
484
|
-
False: 不创建进度日志文件,只保留基本的任务状态信息
|
|
485
|
-
建议在调试或需要详细追踪时设置为 True
|
|
514
|
+
如果未指定,将使用计划中保存的项目目录。
|
|
486
515
|
|
|
487
516
|
- progress_log: 自定义进度日志文件的保存路径(可选)
|
|
488
517
|
如果指定:使用该路径保存进度日志(JSONL格式)
|
|
489
|
-
|
|
490
|
-
如果都未指定:不记录进度日志
|
|
518
|
+
如果未指定:自动在 ~/.mcp/make_web/progress_logs 目录创建时间戳命名的日志文件(可通过环境变量覆盖)
|
|
491
519
|
路径可以是绝对路径或相对于 project_root 的相对路径
|
|
492
520
|
|
|
493
521
|
执行流程:
|
|
494
|
-
-
|
|
495
|
-
-
|
|
496
|
-
-
|
|
497
|
-
-
|
|
522
|
+
- 任务在后台异步执行,立即返回 job_id 和 progress_log 路径
|
|
523
|
+
- 任务完成后,系统会自动推送通知给用户,包含执行报告、生成文件列表和上传结果
|
|
524
|
+
- 系统会自动记录详细的执行步骤和结果到进度日志文件
|
|
525
|
+
- 如需实时了解任务进展或调试,可使用 get_progress(job_id=...) 工具手动查询状态
|
|
498
526
|
"""
|
|
499
527
|
try:
|
|
500
528
|
# 如果没有提供project_root,尝试从缓存中获取
|
|
@@ -517,36 +545,38 @@ async def execute_plan(
|
|
|
517
545
|
break
|
|
518
546
|
except Exception:
|
|
519
547
|
pass
|
|
520
|
-
|
|
548
|
+
|
|
521
549
|
if not project_root:
|
|
522
550
|
# 使用默认根目录,但不添加子目录
|
|
523
551
|
project_root = None
|
|
524
|
-
|
|
552
|
+
|
|
525
553
|
# 从计划中获取项目名称(如果有)
|
|
526
554
|
project_name = None
|
|
527
555
|
if plan_id:
|
|
528
556
|
cached_plan = _PLAN_CACHE_BY_ID.get(plan_id)
|
|
529
557
|
if cached_plan:
|
|
530
558
|
# 尝试从缓存中获取项目名称
|
|
531
|
-
project_name = cached_plan.get("site_title") or cached_plan.get(
|
|
532
|
-
|
|
533
|
-
|
|
559
|
+
project_name = cached_plan.get("site_title") or cached_plan.get(
|
|
560
|
+
"project_name"
|
|
561
|
+
)
|
|
534
562
|
|
|
535
|
-
|
|
563
|
+
project_dir = _resolve_project_directory(project_root, project_name)
|
|
536
564
|
|
|
565
|
+
# 进度日志始终启用
|
|
537
566
|
if progress_log:
|
|
567
|
+
# 用户指定了自定义日志路径
|
|
538
568
|
progress_log_path = (
|
|
539
569
|
progress_log
|
|
540
570
|
if os.path.isabs(progress_log)
|
|
541
571
|
else os.path.join(project_dir, progress_log)
|
|
542
572
|
)
|
|
543
|
-
|
|
573
|
+
else:
|
|
574
|
+
# 自动生成日志文件
|
|
544
575
|
progress_log_path = os.path.join(
|
|
545
576
|
PROGRESS_LOG_DIR, f"agent_progress_{int(time.time())}.jsonl"
|
|
546
577
|
)
|
|
547
|
-
else:
|
|
548
|
-
progress_log_path = None
|
|
549
578
|
|
|
579
|
+
# 尝试创建日志文件
|
|
550
580
|
if progress_log_path:
|
|
551
581
|
try:
|
|
552
582
|
Path(progress_log_path).parent.mkdir(parents=True, exist_ok=True)
|
|
@@ -554,9 +584,10 @@ async def execute_plan(
|
|
|
554
584
|
except Exception:
|
|
555
585
|
progress_log_path = None
|
|
556
586
|
|
|
587
|
+
# save_output 固定为 True
|
|
557
588
|
agent = _build_agent(
|
|
558
589
|
project_dir,
|
|
559
|
-
save_output=
|
|
590
|
+
save_output=True,
|
|
560
591
|
)
|
|
561
592
|
|
|
562
593
|
# 通过 plan_id 查询计划
|
|
@@ -627,6 +658,7 @@ async def execute_plan(
|
|
|
627
658
|
"project_directory": project_dir,
|
|
628
659
|
"model": agent.model,
|
|
629
660
|
"progress_log": progress_log_path,
|
|
661
|
+
"deployment_env": _resolve_edgeone_deploy_env(),
|
|
630
662
|
"started_at": time.time(),
|
|
631
663
|
"updated_at": time.time(),
|
|
632
664
|
}
|
|
@@ -658,7 +690,9 @@ async def execute_plan(
|
|
|
658
690
|
"job_id": job_id,
|
|
659
691
|
"plan_id": plan_id,
|
|
660
692
|
"progress_log": progress_log_path,
|
|
661
|
-
"upload_url":
|
|
693
|
+
"upload_url": None,
|
|
694
|
+
"web_url": None,
|
|
695
|
+
"deployment_env": job_info["deployment_env"],
|
|
662
696
|
"message": message,
|
|
663
697
|
}
|
|
664
698
|
except Exception as exc:
|
|
@@ -715,29 +749,71 @@ async def _handle_auto_upload(
|
|
|
715
749
|
if not project_dir:
|
|
716
750
|
return
|
|
717
751
|
|
|
752
|
+
upload_target = project_dir
|
|
753
|
+
|
|
754
|
+
created_files = (job_info.get("result") or {}).get("created_files") or []
|
|
755
|
+
for file_path in created_files:
|
|
756
|
+
if not file_path:
|
|
757
|
+
continue
|
|
758
|
+
try:
|
|
759
|
+
candidate_path = file_path
|
|
760
|
+
if not os.path.isabs(candidate_path):
|
|
761
|
+
candidate_path = os.path.join(project_dir, candidate_path)
|
|
762
|
+
if not os.path.exists(candidate_path):
|
|
763
|
+
continue
|
|
764
|
+
candidate_dir = (
|
|
765
|
+
candidate_path if os.path.isdir(candidate_path) else os.path.dirname(candidate_path)
|
|
766
|
+
)
|
|
767
|
+
rel_path = os.path.relpath(candidate_dir, project_dir)
|
|
768
|
+
if rel_path == "." or rel_path.startswith(".."):
|
|
769
|
+
continue
|
|
770
|
+
top_level = rel_path.split(os.sep, 1)[0]
|
|
771
|
+
top_level_dir = os.path.join(project_dir, top_level)
|
|
772
|
+
if os.path.isdir(top_level_dir):
|
|
773
|
+
upload_target = top_level_dir
|
|
774
|
+
break
|
|
775
|
+
except Exception:
|
|
776
|
+
continue
|
|
777
|
+
|
|
718
778
|
job_info["upload_status"] = "uploading"
|
|
719
779
|
_persist_job_state(job_id)
|
|
720
780
|
|
|
721
781
|
try:
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
782
|
+
upload_func = getattr(upload_project_to_mcp_server, "fn", None)
|
|
783
|
+
if not callable(upload_func):
|
|
784
|
+
raise RuntimeError("upload_project_to_mcp_server 缺少可调用实现")
|
|
785
|
+
|
|
786
|
+
upload_result = await upload_func(folder_path=upload_target)
|
|
726
787
|
|
|
727
788
|
job_info["upload_result"] = upload_result
|
|
728
789
|
job_info["upload_status"] = upload_result.get("status")
|
|
790
|
+
if upload_result.get("deployment_env"):
|
|
791
|
+
job_info["deployment_env"] = upload_result["deployment_env"]
|
|
729
792
|
|
|
730
793
|
if upload_result.get("status") == "success":
|
|
731
|
-
|
|
794
|
+
zip_url = _extract_zip_url(upload_result)
|
|
795
|
+
web_url = (
|
|
796
|
+
upload_result.get("web_url")
|
|
797
|
+
or (upload_result.get("result") or {}).get("url")
|
|
798
|
+
)
|
|
799
|
+
if zip_url:
|
|
800
|
+
job_info["upload_url"] = zip_url
|
|
801
|
+
if web_url:
|
|
802
|
+
job_info["web_url"] = web_url
|
|
732
803
|
job_info["upload_completed_at"] = time.time()
|
|
804
|
+
job_info["uploaded_directory"] = upload_target
|
|
733
805
|
|
|
734
806
|
if progress_log_path:
|
|
735
807
|
upload_event = {
|
|
736
808
|
"timestamp": time.time(),
|
|
737
809
|
"type": "upload_completed",
|
|
738
810
|
"status": "success",
|
|
739
|
-
"
|
|
811
|
+
"web_url": web_url,
|
|
812
|
+
"upload_url": zip_url,
|
|
813
|
+
"oss_url": zip_url,
|
|
814
|
+
"deployment_env": job_info.get("deployment_env"),
|
|
740
815
|
"message": upload_result.get("message"),
|
|
816
|
+
"uploaded_directory": upload_target,
|
|
741
817
|
}
|
|
742
818
|
try:
|
|
743
819
|
with open(progress_log_path, "a", encoding="utf-8") as log_file:
|
|
@@ -781,7 +857,7 @@ async def create_simple_site(
|
|
|
781
857
|
使用流程:
|
|
782
858
|
1. 调用此工具生成计划,获得 plan_id
|
|
783
859
|
2. 使用 plan_id 调用 execute_plan 执行构建
|
|
784
|
-
3.
|
|
860
|
+
3. 构建完成后会自动推送通知给用户,包含项目文件和访问链接
|
|
785
861
|
|
|
786
862
|
💡 使用提示:
|
|
787
863
|
如果你有地图查询结果、API数据等,请将完整信息传递给 context_content 参数,
|
|
@@ -1097,8 +1173,11 @@ async def get_progress(
|
|
|
1097
1173
|
"project_directory",
|
|
1098
1174
|
"model",
|
|
1099
1175
|
"upload_status",
|
|
1100
|
-
"
|
|
1176
|
+
"upload_url",
|
|
1177
|
+
"web_url",
|
|
1178
|
+
"deployment_env",
|
|
1101
1179
|
"upload_completed_at",
|
|
1180
|
+
"uploaded_directory",
|
|
1102
1181
|
]
|
|
1103
1182
|
job_snapshot = {
|
|
1104
1183
|
k: job_info.get(k) for k in snapshot_keys if job_info.get(k) is not None
|
|
@@ -1133,15 +1212,17 @@ async def get_progress(
|
|
|
1133
1212
|
async def upload_project_to_mcp_server(
|
|
1134
1213
|
folder_path: str,
|
|
1135
1214
|
) -> Dict[str, Any]:
|
|
1136
|
-
"""将项目文件夹打包成ZIP并上传到
|
|
1215
|
+
"""将项目文件夹打包成ZIP并上传到 EdgeOne Pages(自动部署流程)。
|
|
1137
1216
|
|
|
1138
1217
|
参数说明:
|
|
1139
1218
|
- folder_path: 项目文件夹的绝对路径
|
|
1140
1219
|
|
|
1141
1220
|
返回值:
|
|
1142
1221
|
- status: 上传状态 ("success" 或 "error")
|
|
1143
|
-
-
|
|
1144
|
-
-
|
|
1222
|
+
- web_url: 部署成功后返回的 EdgeOne 访问地址
|
|
1223
|
+
- deployment_env: 部署环境(Production/Preview)
|
|
1224
|
+
- deployment_result: EdgeOne 原始部署结果
|
|
1225
|
+
- deployment_logs: 部署日志
|
|
1145
1226
|
- message: 状态信息
|
|
1146
1227
|
"""
|
|
1147
1228
|
try:
|
|
@@ -1152,6 +1233,12 @@ async def upload_project_to_mcp_server(
|
|
|
1152
1233
|
if not os.path.isdir(folder_path):
|
|
1153
1234
|
return {"status": "error", "message": f"路径不是文件夹: {folder_path}"}
|
|
1154
1235
|
|
|
1236
|
+
if not os.getenv("EDGEONE_PAGES_API_TOKEN"):
|
|
1237
|
+
return {
|
|
1238
|
+
"status": "error",
|
|
1239
|
+
"message": "缺少 EDGEONE_PAGES_API_TOKEN 环境变量,无法执行 EdgeOne 部署",
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1155
1242
|
# 创建临时ZIP文件
|
|
1156
1243
|
project_name = os.path.basename(folder_path.rstrip("/"))
|
|
1157
1244
|
temp_dir = tempfile.gettempdir()
|
|
@@ -1176,52 +1263,85 @@ async def upload_project_to_mcp_server(
|
|
|
1176
1263
|
"message": f"ZIP文件过大: {zip_size / 1024 / 1024:.1f}MB,超过50MB限制",
|
|
1177
1264
|
}
|
|
1178
1265
|
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1266
|
+
oss_url: Optional[str] = None
|
|
1267
|
+
oss_response: Optional[Dict[str, Any]] = None
|
|
1268
|
+
if _should_upload_zip_to_oss():
|
|
1269
|
+
async with aiohttp.ClientSession() as session:
|
|
1270
|
+
with open(zip_path, "rb") as f:
|
|
1271
|
+
data = aiohttp.FormData()
|
|
1272
|
+
data.add_field(
|
|
1273
|
+
"file", f, filename=zip_filename, content_type="application/zip"
|
|
1274
|
+
)
|
|
1275
|
+
|
|
1276
|
+
async with session.post(DEFAULT_UPLOAD_URL, data=data) as response:
|
|
1277
|
+
response_text = await response.text()
|
|
1186
1278
|
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
"status": "error",
|
|
1193
|
-
"message": f"上传失败,HTTP {response.status}: {response_text}",
|
|
1194
|
-
}
|
|
1195
|
-
|
|
1196
|
-
# 解析响应JSON
|
|
1197
|
-
try:
|
|
1198
|
-
result = json.loads(response_text)
|
|
1199
|
-
if result.get("code") == 0 and result.get("data", {}).get(
|
|
1200
|
-
"url"
|
|
1201
|
-
):
|
|
1202
|
-
# 清理临时文件
|
|
1203
|
-
try:
|
|
1204
|
-
os.remove(zip_path)
|
|
1205
|
-
except:
|
|
1206
|
-
pass
|
|
1279
|
+
if response.status != 200:
|
|
1280
|
+
return {
|
|
1281
|
+
"status": "error",
|
|
1282
|
+
"message": f"OSS 上传失败,HTTP {response.status}: {response_text}",
|
|
1283
|
+
}
|
|
1207
1284
|
|
|
1285
|
+
try:
|
|
1286
|
+
result = json.loads(response_text)
|
|
1287
|
+
except json.JSONDecodeError:
|
|
1208
1288
|
return {
|
|
1209
|
-
"status": "
|
|
1210
|
-
"
|
|
1211
|
-
"message": f"项目 '{project_name}' 上传成功",
|
|
1212
|
-
"zip_size": f"{zip_size / 1024:.1f}KB",
|
|
1289
|
+
"status": "error",
|
|
1290
|
+
"message": f"OSS 上传响应解析失败: {response_text}",
|
|
1213
1291
|
}
|
|
1292
|
+
|
|
1293
|
+
upload_data = result.get("data") or {}
|
|
1294
|
+
oss_response = upload_data
|
|
1295
|
+
if result.get("code") == 0 and upload_data.get("url"):
|
|
1296
|
+
oss_url = upload_data["url"]
|
|
1214
1297
|
else:
|
|
1215
1298
|
return {
|
|
1216
1299
|
"status": "error",
|
|
1217
|
-
"message": f"上传失败: {result.get('msg', '未知错误')}",
|
|
1300
|
+
"message": f"OSS 上传失败: {result.get('msg', '未知错误')}",
|
|
1218
1301
|
"response": response_text,
|
|
1219
1302
|
}
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1303
|
+
|
|
1304
|
+
# 导入 EdgeOne 部署工具
|
|
1305
|
+
from htmlgen_mcp.agents.web_tools.edgeone_deploy import (
|
|
1306
|
+
deploy_folder_or_zip_to_edgeone,
|
|
1307
|
+
)
|
|
1308
|
+
|
|
1309
|
+
deployment_env = _resolve_edgeone_deploy_env()
|
|
1310
|
+
|
|
1311
|
+
# 将 ZIP 包部署到 EdgeOne(使用线程避免阻塞事件循环)
|
|
1312
|
+
result_json = await asyncio.to_thread(
|
|
1313
|
+
deploy_folder_or_zip_to_edgeone, zip_path, deployment_env
|
|
1314
|
+
)
|
|
1315
|
+
|
|
1316
|
+
try:
|
|
1317
|
+
deploy_result = json.loads(result_json)
|
|
1318
|
+
except json.JSONDecodeError:
|
|
1319
|
+
return {
|
|
1320
|
+
"status": "error",
|
|
1321
|
+
"message": f"EdgeOne 返回数据解析失败: {result_json}",
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
edgeone_result = deploy_result.get("result") or {}
|
|
1325
|
+
web_url = edgeone_result.get("url")
|
|
1326
|
+
|
|
1327
|
+
response: Dict[str, Any] = {
|
|
1328
|
+
"status": "success",
|
|
1329
|
+
"web_url": web_url,
|
|
1330
|
+
"oss_url": oss_url,
|
|
1331
|
+
"oss_response": oss_response,
|
|
1332
|
+
"deployment_env": deployment_env,
|
|
1333
|
+
"deployment_result": edgeone_result,
|
|
1334
|
+
"deployment_logs": deploy_result.get("deployment_logs"),
|
|
1335
|
+
"zip_size": f"{zip_size / 1024:.1f}KB",
|
|
1336
|
+
"message": f"项目 '{project_name}' 已部署到 EdgeOne ({deployment_env})",
|
|
1337
|
+
}
|
|
1338
|
+
if oss_url:
|
|
1339
|
+
response["upload_url"] = oss_url
|
|
1340
|
+
|
|
1341
|
+
if not web_url:
|
|
1342
|
+
response["message"] += ",但未获取到访问链接"
|
|
1343
|
+
|
|
1344
|
+
return response
|
|
1225
1345
|
|
|
1226
1346
|
except Exception as exc:
|
|
1227
1347
|
# 清理临时文件
|
|
@@ -1231,11 +1351,42 @@ async def upload_project_to_mcp_server(
|
|
|
1231
1351
|
except:
|
|
1232
1352
|
pass
|
|
1233
1353
|
|
|
1234
|
-
|
|
1354
|
+
error_message = str(exc)
|
|
1355
|
+
try:
|
|
1356
|
+
# EdgeOne 部署错误通常是 JSON 字符串
|
|
1357
|
+
if error_message.startswith("{"):
|
|
1358
|
+
error_data = json.loads(error_message)
|
|
1359
|
+
error_response = {
|
|
1360
|
+
"status": "error",
|
|
1361
|
+
"message": error_data.get("error", error_message),
|
|
1362
|
+
"deployment_logs": error_data.get("deployment_logs", ""),
|
|
1363
|
+
"traceback": traceback.format_exc(),
|
|
1364
|
+
}
|
|
1365
|
+
if "oss_url" in locals() and oss_url:
|
|
1366
|
+
error_response["upload_url"] = oss_url
|
|
1367
|
+
if "oss_response" in locals() and oss_response:
|
|
1368
|
+
error_response["oss_response"] = oss_response
|
|
1369
|
+
return error_response
|
|
1370
|
+
except Exception:
|
|
1371
|
+
pass
|
|
1372
|
+
|
|
1373
|
+
error_response = {
|
|
1235
1374
|
"status": "error",
|
|
1236
|
-
"message":
|
|
1375
|
+
"message": error_message,
|
|
1237
1376
|
"traceback": traceback.format_exc(),
|
|
1238
1377
|
}
|
|
1378
|
+
if "oss_url" in locals() and oss_url:
|
|
1379
|
+
error_response["upload_url"] = oss_url
|
|
1380
|
+
if "oss_response" in locals() and oss_response:
|
|
1381
|
+
error_response["oss_response"] = oss_response
|
|
1382
|
+
return error_response
|
|
1383
|
+
finally:
|
|
1384
|
+
# 确保清理临时ZIP文件
|
|
1385
|
+
if "zip_path" in locals() and os.path.exists(zip_path):
|
|
1386
|
+
try:
|
|
1387
|
+
os.remove(zip_path)
|
|
1388
|
+
except:
|
|
1389
|
+
pass
|
|
1239
1390
|
|
|
1240
1391
|
|
|
1241
1392
|
@mcp.tool()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: htmlgen-mcp
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.8
|
|
4
4
|
Summary: AI-powered HTML website generator with auto-upload functionality via Model Context Protocol
|
|
5
5
|
Author-email: HTML Generator Team <contact@htmlgen-mcp.com>
|
|
6
6
|
License: MIT
|
|
@@ -97,7 +97,10 @@ print(f"状态: {progress['job']['status']}")
|
|
|
97
97
|
|
|
98
98
|
# 如果启用了自动上传
|
|
99
99
|
if progress['job'].get('upload_status') == 'success':
|
|
100
|
-
|
|
100
|
+
if progress['job'].get('upload_url'):
|
|
101
|
+
print(f"部署包下载: {progress['job']['upload_url']}")
|
|
102
|
+
if progress['job'].get('web_url'):
|
|
103
|
+
print(f"网站地址: {progress['job']['web_url']}")
|
|
101
104
|
```
|
|
102
105
|
|
|
103
106
|
## 🛠️ MCP 工具
|
|
@@ -105,7 +108,7 @@ if progress['job'].get('upload_status') == 'success':
|
|
|
105
108
|
- `create_simple_site()` - 生成网站计划
|
|
106
109
|
- `execute_plan()` - 执行网站构建
|
|
107
110
|
- `get_progress()` - 查询构建进度
|
|
108
|
-
- `upload_project_to_mcp_server()` -
|
|
111
|
+
- `upload_project_to_mcp_server()` - 打包上传至 OSS 并部署到 EdgeOne Pages
|
|
109
112
|
- `deploy_folder_or_zip()` - 部署到EdgeOne Pages
|
|
110
113
|
|
|
111
114
|
## 🔧 环境配置
|
|
@@ -124,11 +127,13 @@ WEB_AGENT_PROJECT_ROOT=/path/to/projects
|
|
|
124
127
|
# EdgeOne Pages 部署(可选)
|
|
125
128
|
EDGEONE_PAGES_API_TOKEN=your_token
|
|
126
129
|
EDGEONE_PAGES_PROJECT_NAME=your_project
|
|
130
|
+
EDGEONE_AUTO_DEPLOY_ENV=Production # 支持 Production / Preview
|
|
131
|
+
KEEP_OSS_UPLOAD=true # 保留 OSS 压缩包上传(false 则跳过)
|
|
127
132
|
```
|
|
128
133
|
|
|
129
134
|
## 🎯 自动上传功能
|
|
130
135
|
|
|
131
|
-
|
|
136
|
+
构建完成时自动部署到 EdgeOne Pages,同时保留 OSS 压缩包链接:
|
|
132
137
|
|
|
133
138
|
```python
|
|
134
139
|
# 启动带自动上传的构建
|
|
@@ -146,7 +151,10 @@ while True:
|
|
|
146
151
|
if status == 'completed':
|
|
147
152
|
upload_status = progress['job'].get('upload_status')
|
|
148
153
|
if upload_status == 'success':
|
|
149
|
-
|
|
154
|
+
if progress['job'].get('upload_url'):
|
|
155
|
+
print(f"📦 部署包下载: {progress['job']['upload_url']}")
|
|
156
|
+
if progress['job'].get('web_url'):
|
|
157
|
+
print(f"🎉 网站已上线: {progress['job']['web_url']}")
|
|
150
158
|
break
|
|
151
159
|
elif upload_status == 'failed':
|
|
152
160
|
print("❌ 上传失败")
|
|
@@ -159,7 +167,7 @@ while True:
|
|
|
159
167
|
|
|
160
168
|
- **构建阶段**: `running` → `completed`
|
|
161
169
|
- **上传阶段**: `uploading` → `success`/`failed`
|
|
162
|
-
- **最终结果**: `
|
|
170
|
+
- **最终结果**: `upload_url` (OSS 压缩包下载地址,可选)、`web_url` (EdgeOne 访问地址)
|
|
163
171
|
|
|
164
172
|
## 🤝 贡献
|
|
165
173
|
|
|
@@ -7,8 +7,8 @@ htmlgen_mcp/nas_storage.py,sha256=HpgROy53vrYiBiXsUJO56GCoYZdyYR15iVvOBqyp7Yc,11
|
|
|
7
7
|
htmlgen_mcp/progress_tools.py,sha256=SOScPSr3hEv4rvGzqvwUcomEFiPhhNxJ7CbWURlFpBs,5067
|
|
8
8
|
htmlgen_mcp/progress_tracker.py,sha256=2TVduWNJJH08EQ7Vf9EpiwPjtp61JX7muSCdbGHZAfM,12210
|
|
9
9
|
htmlgen_mcp/prompt_enhancer.py,sha256=8UIxt45vSNarS5uqfxC5thOfnGY7luDs2YjHZRx1GAM,7976
|
|
10
|
-
htmlgen_mcp/sse_optimizations.py,sha256=
|
|
11
|
-
htmlgen_mcp/web_agent_server.py,sha256=
|
|
10
|
+
htmlgen_mcp/sse_optimizations.py,sha256=lkAizXEwJxDuhf0ZiaHg_N-RYsfVd1USnd1p9UTLT38,7003
|
|
11
|
+
htmlgen_mcp/web_agent_server.py,sha256=Jna9yYMxz2IdZPMV9gyYRq9UxZbGgAA6FmIccWaDeWQ,56671
|
|
12
12
|
htmlgen_mcp/agents/__init__.py,sha256=Xydfjzw9s9O6I5Ixx6EmsTdXu26136NDPUAqt9B1hzE,121
|
|
13
13
|
htmlgen_mcp/agents/ai_content_generator.py,sha256=tWGC9cY6Wp7MB1P9J7uCv8LUdiS02rgS6vxaNHD7KQk,10311
|
|
14
14
|
htmlgen_mcp/agents/quick_generator.py,sha256=2wV4PCRugV0suTedLDV91_etHy_2Fiw4J0MraT7MQjw,34201
|
|
@@ -31,8 +31,8 @@ htmlgen_mcp/agents/web_tools/simple_css.py,sha256=kj9X3sHHhj1wGwBVL20j6w2qIHXRdx
|
|
|
31
31
|
htmlgen_mcp/agents/web_tools/simple_js.py,sha256=xMiuF-u-h_IIkUONZIa4Xf8vKB5mcXxwQf5b_BIcpoE,12174
|
|
32
32
|
htmlgen_mcp/agents/web_tools/simple_templates.py,sha256=-Rs-SsWpGZT2hiwa3jZNVDHOMZOo1vV2pWbmBdR30os,6471
|
|
33
33
|
htmlgen_mcp/agents/web_tools/validation.py,sha256=bNA6aWXrCSi7sPqQw5bBR3XF69gRf85D5jSMi996CtI,2069
|
|
34
|
-
htmlgen_mcp-0.
|
|
35
|
-
htmlgen_mcp-0.
|
|
36
|
-
htmlgen_mcp-0.
|
|
37
|
-
htmlgen_mcp-0.
|
|
38
|
-
htmlgen_mcp-0.
|
|
34
|
+
htmlgen_mcp-0.4.8.dist-info/METADATA,sha256=fN2M_vuYxEquzylz0yjZBRloJuwSNfLNgIJC98BI3KY,5746
|
|
35
|
+
htmlgen_mcp-0.4.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
36
|
+
htmlgen_mcp-0.4.8.dist-info/entry_points.txt,sha256=w7ufTQJobIxT3FYI24yKsCEwEQvBOWhNjckUd9Amu_k,66
|
|
37
|
+
htmlgen_mcp-0.4.8.dist-info/top_level.txt,sha256=KnglzX4ekV8SQkHTsJg2_nTBXz2TxaYLdvoMMovHLNk,12
|
|
38
|
+
htmlgen_mcp-0.4.8.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|