htmlgen-mcp 0.3.6__tar.gz → 0.3.8__tar.gz
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.
Potentially problematic release.
This version of htmlgen-mcp might be problematic. Click here for more details.
- {htmlgen_mcp-0.3.6 → htmlgen_mcp-0.3.8}/PKG-INFO +1 -1
- {htmlgen_mcp-0.3.6 → htmlgen_mcp-0.3.8}/pyproject.toml +1 -1
- {htmlgen_mcp-0.3.6 → htmlgen_mcp-0.3.8}/src/htmlgen_mcp/web_agent_server.py +173 -112
- {htmlgen_mcp-0.3.6 → htmlgen_mcp-0.3.8}/src/htmlgen_mcp.egg-info/PKG-INFO +1 -1
- {htmlgen_mcp-0.3.6 → htmlgen_mcp-0.3.8}/README.md +0 -0
- {htmlgen_mcp-0.3.6 → htmlgen_mcp-0.3.8}/setup.cfg +0 -0
- {htmlgen_mcp-0.3.6 → htmlgen_mcp-0.3.8}/src/htmlgen_mcp/__init__.py +0 -0
- {htmlgen_mcp-0.3.6 → htmlgen_mcp-0.3.8}/src/htmlgen_mcp/agents/__init__.py +0 -0
- {htmlgen_mcp-0.3.6 → htmlgen_mcp-0.3.8}/src/htmlgen_mcp/agents/ai_content_generator.py +0 -0
- {htmlgen_mcp-0.3.6 → htmlgen_mcp-0.3.8}/src/htmlgen_mcp/agents/quick_generator.py +0 -0
- {htmlgen_mcp-0.3.6 → htmlgen_mcp-0.3.8}/src/htmlgen_mcp/agents/smart_web_agent.py +0 -0
- {htmlgen_mcp-0.3.6 → htmlgen_mcp-0.3.8}/src/htmlgen_mcp/agents/web_tools/__init__.py +0 -0
- {htmlgen_mcp-0.3.6 → htmlgen_mcp-0.3.8}/src/htmlgen_mcp/agents/web_tools/bootstrap.py +0 -0
- {htmlgen_mcp-0.3.6 → htmlgen_mcp-0.3.8}/src/htmlgen_mcp/agents/web_tools/browser.py +0 -0
- {htmlgen_mcp-0.3.6 → htmlgen_mcp-0.3.8}/src/htmlgen_mcp/agents/web_tools/colors.py +0 -0
- {htmlgen_mcp-0.3.6 → htmlgen_mcp-0.3.8}/src/htmlgen_mcp/agents/web_tools/css.py +0 -0
- {htmlgen_mcp-0.3.6 → htmlgen_mcp-0.3.8}/src/htmlgen_mcp/agents/web_tools/edgeone_deploy.py +0 -0
- {htmlgen_mcp-0.3.6 → htmlgen_mcp-0.3.8}/src/htmlgen_mcp/agents/web_tools/html_templates.py +0 -0
- {htmlgen_mcp-0.3.6 → htmlgen_mcp-0.3.8}/src/htmlgen_mcp/agents/web_tools/html_templates_improved.py +0 -0
- {htmlgen_mcp-0.3.6 → htmlgen_mcp-0.3.8}/src/htmlgen_mcp/agents/web_tools/images.py +0 -0
- {htmlgen_mcp-0.3.6 → htmlgen_mcp-0.3.8}/src/htmlgen_mcp/agents/web_tools/images_fixed.py +0 -0
- {htmlgen_mcp-0.3.6 → htmlgen_mcp-0.3.8}/src/htmlgen_mcp/agents/web_tools/js.py +0 -0
- {htmlgen_mcp-0.3.6 → htmlgen_mcp-0.3.8}/src/htmlgen_mcp/agents/web_tools/navigation.py +0 -0
- {htmlgen_mcp-0.3.6 → htmlgen_mcp-0.3.8}/src/htmlgen_mcp/agents/web_tools/project.py +0 -0
- {htmlgen_mcp-0.3.6 → htmlgen_mcp-0.3.8}/src/htmlgen_mcp/agents/web_tools/simple_builder.py +0 -0
- {htmlgen_mcp-0.3.6 → htmlgen_mcp-0.3.8}/src/htmlgen_mcp/agents/web_tools/simple_css.py +0 -0
- {htmlgen_mcp-0.3.6 → htmlgen_mcp-0.3.8}/src/htmlgen_mcp/agents/web_tools/simple_js.py +0 -0
- {htmlgen_mcp-0.3.6 → htmlgen_mcp-0.3.8}/src/htmlgen_mcp/agents/web_tools/simple_templates.py +0 -0
- {htmlgen_mcp-0.3.6 → htmlgen_mcp-0.3.8}/src/htmlgen_mcp/agents/web_tools/validation.py +0 -0
- {htmlgen_mcp-0.3.6 → htmlgen_mcp-0.3.8}/src/htmlgen_mcp/config.py +0 -0
- {htmlgen_mcp-0.3.6 → htmlgen_mcp-0.3.8}/src/htmlgen_mcp/context_aware_executor.py +0 -0
- {htmlgen_mcp-0.3.6 → htmlgen_mcp-0.3.8}/src/htmlgen_mcp/improved_progress.py +0 -0
- {htmlgen_mcp-0.3.6 → htmlgen_mcp-0.3.8}/src/htmlgen_mcp/nas_log_manager.py +0 -0
- {htmlgen_mcp-0.3.6 → htmlgen_mcp-0.3.8}/src/htmlgen_mcp/nas_storage.py +0 -0
- {htmlgen_mcp-0.3.6 → htmlgen_mcp-0.3.8}/src/htmlgen_mcp/progress_tools.py +0 -0
- {htmlgen_mcp-0.3.6 → htmlgen_mcp-0.3.8}/src/htmlgen_mcp/progress_tracker.py +0 -0
- {htmlgen_mcp-0.3.6 → htmlgen_mcp-0.3.8}/src/htmlgen_mcp/prompt_enhancer.py +0 -0
- {htmlgen_mcp-0.3.6 → htmlgen_mcp-0.3.8}/src/htmlgen_mcp/sse_optimizations.py +0 -0
- {htmlgen_mcp-0.3.6 → htmlgen_mcp-0.3.8}/src/htmlgen_mcp.egg-info/SOURCES.txt +0 -0
- {htmlgen_mcp-0.3.6 → htmlgen_mcp-0.3.8}/src/htmlgen_mcp.egg-info/dependency_links.txt +0 -0
- {htmlgen_mcp-0.3.6 → htmlgen_mcp-0.3.8}/src/htmlgen_mcp.egg-info/entry_points.txt +0 -0
- {htmlgen_mcp-0.3.6 → htmlgen_mcp-0.3.8}/src/htmlgen_mcp.egg-info/requires.txt +0 -0
- {htmlgen_mcp-0.3.6 → htmlgen_mcp-0.3.8}/src/htmlgen_mcp.egg-info/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "htmlgen-mcp"
|
|
7
|
-
version = "0.3.
|
|
7
|
+
version = "0.3.8"
|
|
8
8
|
description = "AI-powered HTML website generator with auto-upload functionality via Model Context Protocol"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -38,9 +38,14 @@ from datetime import datetime
|
|
|
38
38
|
|
|
39
39
|
# 使用 NAS 作为默认存储路径
|
|
40
40
|
NAS_PATH = os.environ.get("NAS_STORAGE_PATH", "/app/mcp-servers/mcp-servers/html_agent")
|
|
41
|
+
# 项目根目录:优先使用 WEB_AGENT_PROJECT_ROOT 环境变量,否则使用 NAS_PATH/projects
|
|
41
42
|
DEFAULT_PROJECT_ROOT = os.path.abspath(
|
|
42
43
|
os.environ.get("WEB_AGENT_PROJECT_ROOT", f"{NAS_PATH}/projects")
|
|
43
44
|
)
|
|
45
|
+
# 是否自动生成项目子目录(可通过环境变量控制)
|
|
46
|
+
AUTO_CREATE_PROJECT_DIR = os.environ.get("AUTO_CREATE_PROJECT_DIR", "true").lower() == "true"
|
|
47
|
+
# 默认的文件上传URL(固定值)
|
|
48
|
+
DEFAULT_UPLOAD_URL = "https://www.mcpcn.cc/api/fileUploadAndDownload/uploadMcpFile"
|
|
44
49
|
DEFAULT_MODEL = os.environ.get("WEB_AGENT_MODEL", "qwen3-coder-plus-2025-09-23")
|
|
45
50
|
DEFAULT_BASE_URL = os.environ.get(
|
|
46
51
|
"OPENAI_BASE_URL", "https://dashscope.aliyuncs.com/compatible-mode/v1"
|
|
@@ -77,6 +82,7 @@ _PROGRESS_LOG_BY_ID: dict[str, str] = {}
|
|
|
77
82
|
_PROGRESS_LOG_BY_JOB: dict[str, str] = {}
|
|
78
83
|
_JOB_REGISTRY: dict[str, Dict[str, Any]] = {}
|
|
79
84
|
_CONTEXT_CACHE_BY_ID: dict[str, Dict[str, Any]] = {}
|
|
85
|
+
_CONTEXT_ID_BY_PLAN: dict[str, str] = {}
|
|
80
86
|
|
|
81
87
|
|
|
82
88
|
def _job_state_path(job_id: str) -> Path:
|
|
@@ -135,6 +141,18 @@ def _load_job_states() -> None:
|
|
|
135
141
|
_load_job_states()
|
|
136
142
|
|
|
137
143
|
|
|
144
|
+
def _load_job_state_from_disk(job_id: str) -> Optional[Dict[str, Any]]:
|
|
145
|
+
path = _job_state_path(job_id)
|
|
146
|
+
if not path.exists():
|
|
147
|
+
return None
|
|
148
|
+
try:
|
|
149
|
+
data = json.loads(path.read_text(encoding="utf-8"))
|
|
150
|
+
data.setdefault("job_id", job_id)
|
|
151
|
+
return data
|
|
152
|
+
except Exception:
|
|
153
|
+
return None
|
|
154
|
+
|
|
155
|
+
|
|
138
156
|
def _context_cache_path(context_id: str) -> Path:
|
|
139
157
|
return CONTEXT_CACHE_DIR / f"{context_id}.json"
|
|
140
158
|
|
|
@@ -173,9 +191,47 @@ def _resolve_cached_context(context_id: Optional[str]) -> Optional[Dict[str, Any
|
|
|
173
191
|
return None
|
|
174
192
|
|
|
175
193
|
|
|
176
|
-
def _resolve_project_directory(project_root: Optional[str]) -> str:
|
|
177
|
-
|
|
178
|
-
|
|
194
|
+
def _resolve_project_directory(project_root: Optional[str], project_name: Optional[str] = None) -> str:
|
|
195
|
+
"""
|
|
196
|
+
解析项目目录路径
|
|
197
|
+
|
|
198
|
+
Args:
|
|
199
|
+
project_root: 项目根目录或完整路径
|
|
200
|
+
project_name: 项目名称(可选)
|
|
201
|
+
|
|
202
|
+
Returns:
|
|
203
|
+
完整的项目目录路径
|
|
204
|
+
"""
|
|
205
|
+
if project_root:
|
|
206
|
+
# 如果提供了 project_root
|
|
207
|
+
if os.path.isabs(project_root):
|
|
208
|
+
# 绝对路径:直接使用
|
|
209
|
+
abs_path = project_root
|
|
210
|
+
else:
|
|
211
|
+
# 相对路径:相对于默认根目录
|
|
212
|
+
# 如果 project_root 看起来像项目名(不含/),则作为子目录
|
|
213
|
+
if '/' not in project_root and '\\' not in project_root:
|
|
214
|
+
abs_path = os.path.join(DEFAULT_PROJECT_ROOT, project_root)
|
|
215
|
+
else:
|
|
216
|
+
# 包含路径分隔符,作为相对路径处理
|
|
217
|
+
abs_path = os.path.abspath(os.path.join(DEFAULT_PROJECT_ROOT, project_root))
|
|
218
|
+
else:
|
|
219
|
+
# 没有提供 project_root,使用默认根目录
|
|
220
|
+
base = DEFAULT_PROJECT_ROOT
|
|
221
|
+
|
|
222
|
+
# 如果提供了 project_name 且启用了自动创建子目录
|
|
223
|
+
if project_name and AUTO_CREATE_PROJECT_DIR:
|
|
224
|
+
# 清理项目名称,去除特殊字符
|
|
225
|
+
safe_name = "".join(c for c in project_name if c.isalnum() or c in (' ', '-', '_', '.'))
|
|
226
|
+
safe_name = safe_name.strip().replace(' ', '_')
|
|
227
|
+
if safe_name:
|
|
228
|
+
abs_path = os.path.join(base, safe_name)
|
|
229
|
+
else:
|
|
230
|
+
abs_path = base
|
|
231
|
+
else:
|
|
232
|
+
abs_path = base
|
|
233
|
+
|
|
234
|
+
# 创建目录
|
|
179
235
|
os.makedirs(abs_path, exist_ok=True)
|
|
180
236
|
return abs_path
|
|
181
237
|
|
|
@@ -403,16 +459,13 @@ def _execute_plan(
|
|
|
403
459
|
@mcp.tool()
|
|
404
460
|
async def execute_plan(
|
|
405
461
|
plan_id: str,
|
|
406
|
-
|
|
407
|
-
project_root: str,
|
|
462
|
+
project_root: Optional[str] = None,
|
|
408
463
|
# auto_plan: bool = False, # 已禁用,没有实际作用
|
|
409
464
|
# confirm_each_step: bool = False, # 后台执行模式下用户无法交互确认
|
|
410
465
|
# show_code: bool = False, # 后台执行时用户看不到输出
|
|
411
466
|
# verbose: bool = False, # 后台执行时详细日志意义不大
|
|
412
467
|
save_output: bool = False,
|
|
413
468
|
progress_log: Optional[str] = None,
|
|
414
|
-
auto_upload: bool = False,
|
|
415
|
-
upload_url: str = "https://www.mcpcn.cc/api/fileUploadAndDownload/uploadMcpFile",
|
|
416
469
|
) -> Dict[str, Any]:
|
|
417
470
|
"""执行网页构建计划,始终以后台模式运行。
|
|
418
471
|
|
|
@@ -437,24 +490,47 @@ async def execute_plan(
|
|
|
437
490
|
如果都未指定:不记录进度日志
|
|
438
491
|
路径可以是绝对路径或相对于 project_root 的相对路径
|
|
439
492
|
|
|
440
|
-
- auto_upload: 是否在构建完成后自动上传到MCP服务器
|
|
441
|
-
True: 构建完成后自动打包为ZIP并上传,返回访问URL
|
|
442
|
-
False: 仅构建,不上传
|
|
443
|
-
默认为 False
|
|
444
|
-
|
|
445
|
-
- upload_url: 文件上传API地址
|
|
446
|
-
默认为 "https://www.mcpcn.cc/api/fileUploadAndDownload/uploadMcpFile"
|
|
447
|
-
只在 auto_upload=True 时生效
|
|
448
|
-
|
|
449
493
|
执行流程:
|
|
450
494
|
- 任务始终在后台异步执行,立即返回 job_id 和 progress_log 路径
|
|
451
495
|
- 使用 get_progress(job_id=..., limit=...) 可以实时查询执行状态和进度
|
|
452
496
|
- 支持通过进度日志文件追踪详细的执行步骤和结果
|
|
453
|
-
-
|
|
454
|
-
- 🎯 新增:auto_upload=True时,构建完成(100%)后自动上传并返回访问URL
|
|
497
|
+
- 执行完成后可以获取完整的执行报告、生成文件列表,以及自动上传结果
|
|
455
498
|
"""
|
|
456
499
|
try:
|
|
457
|
-
|
|
500
|
+
# 如果没有提供project_root,尝试从缓存中获取
|
|
501
|
+
if not project_root:
|
|
502
|
+
cached_by_id = _PLAN_CACHE_BY_ID.get(plan_id)
|
|
503
|
+
if cached_by_id and cached_by_id.get("project_directory"):
|
|
504
|
+
project_root = cached_by_id["project_directory"]
|
|
505
|
+
else:
|
|
506
|
+
# 尝试从文件中读取
|
|
507
|
+
possible_paths = [
|
|
508
|
+
PLAN_CACHE_DIR / f"{plan_id}.json",
|
|
509
|
+
PLAN_CACHE_DIR / f"simple_site_plan_{plan_id}.json",
|
|
510
|
+
]
|
|
511
|
+
for path in possible_paths:
|
|
512
|
+
if path.exists():
|
|
513
|
+
try:
|
|
514
|
+
plan_data = json.loads(path.read_text(encoding="utf-8"))
|
|
515
|
+
project_root = plan_data.get("project_directory")
|
|
516
|
+
if project_root:
|
|
517
|
+
break
|
|
518
|
+
except Exception:
|
|
519
|
+
pass
|
|
520
|
+
|
|
521
|
+
if not project_root:
|
|
522
|
+
# 使用默认根目录,但不添加子目录
|
|
523
|
+
project_root = None
|
|
524
|
+
|
|
525
|
+
# 从计划中获取项目名称(如果有)
|
|
526
|
+
project_name = None
|
|
527
|
+
if plan_id:
|
|
528
|
+
cached_plan = _PLAN_CACHE_BY_ID.get(plan_id)
|
|
529
|
+
if cached_plan:
|
|
530
|
+
# 尝试从缓存中获取项目名称
|
|
531
|
+
project_name = cached_plan.get("site_title") or cached_plan.get("project_name")
|
|
532
|
+
|
|
533
|
+
project_dir = _resolve_project_directory(project_root, project_name)
|
|
458
534
|
|
|
459
535
|
# 移除 auto_plan 检查,因为参数已被移除
|
|
460
536
|
|
|
@@ -569,29 +645,20 @@ async def execute_plan(
|
|
|
569
645
|
agent,
|
|
570
646
|
plan_dict,
|
|
571
647
|
progress_log_path=progress_log_path,
|
|
572
|
-
auto_upload=auto_upload,
|
|
573
|
-
upload_url=upload_url,
|
|
574
648
|
)
|
|
575
649
|
)
|
|
576
650
|
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
).format(job_id)
|
|
582
|
-
else:
|
|
583
|
-
message = (
|
|
584
|
-
"执行已在后台启动:调用 get_progress(job_id='{}', limit=20) "
|
|
585
|
-
"或传入 progress_log='{}' 可获取实时进度"
|
|
586
|
-
).format(job_id, progress_log_path or "<未启用进度日志>")
|
|
651
|
+
message = (
|
|
652
|
+
"执行已在后台启动(含自动上传):调用 get_progress(job_id='{}', limit=20) "
|
|
653
|
+
"或传入 progress_log='{}' 可获取实时进度与上传结果"
|
|
654
|
+
).format(job_id, progress_log_path or "<未启用进度日志>")
|
|
587
655
|
|
|
588
656
|
return {
|
|
589
657
|
"status": "started",
|
|
590
658
|
"job_id": job_id,
|
|
591
659
|
"plan_id": plan_id,
|
|
592
660
|
"progress_log": progress_log_path,
|
|
593
|
-
"
|
|
594
|
-
"upload_url": upload_url if auto_upload else None,
|
|
661
|
+
"upload_url": DEFAULT_UPLOAD_URL,
|
|
595
662
|
"message": message,
|
|
596
663
|
}
|
|
597
664
|
except Exception as exc:
|
|
@@ -608,8 +675,6 @@ async def _run_execution_job(
|
|
|
608
675
|
plan_dict: Dict[str, Any],
|
|
609
676
|
*,
|
|
610
677
|
progress_log_path: Optional[str],
|
|
611
|
-
auto_upload: bool = False,
|
|
612
|
-
upload_url: str = "https://www.mcpcn.cc/api/fileUploadAndDownload/uploadMcpFile",
|
|
613
678
|
) -> None:
|
|
614
679
|
job_info = _JOB_REGISTRY.get(job_id)
|
|
615
680
|
if not job_info:
|
|
@@ -627,49 +692,7 @@ async def _run_execution_job(
|
|
|
627
692
|
job_info["completed_at"] = time.time()
|
|
628
693
|
_persist_job_state(job_id)
|
|
629
694
|
|
|
630
|
-
|
|
631
|
-
if auto_upload and job_info.get("project_directory"):
|
|
632
|
-
try:
|
|
633
|
-
# 记录上传开始
|
|
634
|
-
job_info["upload_status"] = "uploading"
|
|
635
|
-
_persist_job_state(job_id)
|
|
636
|
-
|
|
637
|
-
upload_result = await upload_project_to_mcp_server(
|
|
638
|
-
folder_path=job_info["project_directory"], upload_url=upload_url
|
|
639
|
-
)
|
|
640
|
-
|
|
641
|
-
# 记录上传结果
|
|
642
|
-
job_info["upload_result"] = upload_result
|
|
643
|
-
job_info["upload_status"] = upload_result["status"]
|
|
644
|
-
if upload_result["status"] == "success":
|
|
645
|
-
job_info["website_url"] = upload_result["url"]
|
|
646
|
-
job_info["upload_completed_at"] = time.time()
|
|
647
|
-
|
|
648
|
-
# 添加到进度日志
|
|
649
|
-
if progress_log_path:
|
|
650
|
-
upload_event = {
|
|
651
|
-
"timestamp": time.time(),
|
|
652
|
-
"type": "upload_completed",
|
|
653
|
-
"status": "success",
|
|
654
|
-
"website_url": upload_result["url"],
|
|
655
|
-
"message": upload_result["message"],
|
|
656
|
-
}
|
|
657
|
-
try:
|
|
658
|
-
with open(
|
|
659
|
-
progress_log_path, "a", encoding="utf-8"
|
|
660
|
-
) as log_file:
|
|
661
|
-
log_file.write(
|
|
662
|
-
json.dumps(upload_event, ensure_ascii=False)
|
|
663
|
-
)
|
|
664
|
-
log_file.write("\n")
|
|
665
|
-
except Exception:
|
|
666
|
-
pass
|
|
667
|
-
|
|
668
|
-
_persist_job_state(job_id)
|
|
669
|
-
except Exception as upload_exc:
|
|
670
|
-
job_info["upload_status"] = "failed"
|
|
671
|
-
job_info["upload_error"] = str(upload_exc)
|
|
672
|
-
_persist_job_state(job_id)
|
|
695
|
+
await _handle_auto_upload(job_id, job_info, progress_log_path)
|
|
673
696
|
|
|
674
697
|
except Exception as exc:
|
|
675
698
|
job_info["status"] = "failed"
|
|
@@ -683,13 +706,57 @@ async def _run_execution_job(
|
|
|
683
706
|
_persist_job_state(job_id)
|
|
684
707
|
|
|
685
708
|
|
|
709
|
+
async def _handle_auto_upload(
|
|
710
|
+
job_id: str,
|
|
711
|
+
job_info: Dict[str, Any],
|
|
712
|
+
progress_log_path: Optional[str],
|
|
713
|
+
) -> None:
|
|
714
|
+
project_dir = job_info.get("project_directory")
|
|
715
|
+
if not project_dir:
|
|
716
|
+
return
|
|
717
|
+
|
|
718
|
+
job_info["upload_status"] = "uploading"
|
|
719
|
+
_persist_job_state(job_id)
|
|
720
|
+
|
|
721
|
+
try:
|
|
722
|
+
upload_result = await upload_project_to_mcp_server(
|
|
723
|
+
folder_path=project_dir,
|
|
724
|
+
upload_url=DEFAULT_UPLOAD_URL,
|
|
725
|
+
)
|
|
726
|
+
|
|
727
|
+
job_info["upload_result"] = upload_result
|
|
728
|
+
job_info["upload_status"] = upload_result.get("status")
|
|
729
|
+
|
|
730
|
+
if upload_result.get("status") == "success":
|
|
731
|
+
job_info["website_url"] = upload_result.get("url")
|
|
732
|
+
job_info["upload_completed_at"] = time.time()
|
|
733
|
+
|
|
734
|
+
if progress_log_path:
|
|
735
|
+
upload_event = {
|
|
736
|
+
"timestamp": time.time(),
|
|
737
|
+
"type": "upload_completed",
|
|
738
|
+
"status": "success",
|
|
739
|
+
"website_url": upload_result.get("url"),
|
|
740
|
+
"message": upload_result.get("message"),
|
|
741
|
+
}
|
|
742
|
+
try:
|
|
743
|
+
with open(progress_log_path, "a", encoding="utf-8") as log_file:
|
|
744
|
+
log_file.write(json.dumps(upload_event, ensure_ascii=False))
|
|
745
|
+
log_file.write("\n")
|
|
746
|
+
except Exception:
|
|
747
|
+
pass
|
|
748
|
+
|
|
749
|
+
except Exception as exc:
|
|
750
|
+
job_info["upload_status"] = "failed"
|
|
751
|
+
job_info["upload_error"] = str(exc)
|
|
752
|
+
finally:
|
|
753
|
+
_persist_job_state(job_id)
|
|
754
|
+
|
|
755
|
+
|
|
686
756
|
@mcp.tool()
|
|
687
757
|
async def create_simple_site(
|
|
688
758
|
description: str,
|
|
689
759
|
site_title: str = "我的网站",
|
|
690
|
-
project_root: Optional[str] = None,
|
|
691
|
-
model: Optional[str] = None,
|
|
692
|
-
context_id: Optional[str] = None,
|
|
693
760
|
context_content: Optional[str] = None,
|
|
694
761
|
) -> Dict[str, Any]:
|
|
695
762
|
"""使用AI分析需求,生成简单但美观的网站计划。
|
|
@@ -703,11 +770,6 @@ async def create_simple_site(
|
|
|
703
770
|
* 如果有地图查询结果、API返回数据等,都应该放在这个参数中
|
|
704
771
|
* 格式可以是文本、JSON字符串或结构化数据的字符串表示
|
|
705
772
|
|
|
706
|
-
其他参数:
|
|
707
|
-
- project_root: 项目根目录,缺省使用默认目录
|
|
708
|
-
- model: 使用的AI模型,缺省使用默认模型
|
|
709
|
-
- context_id: 可选,引用已缓存的上下文快照以复用历史资料
|
|
710
|
-
|
|
711
773
|
图片配置参数(可选):
|
|
712
774
|
- image_style: 图片风格 (professional|artistic|minimal|vibrant|luxury)
|
|
713
775
|
- image_topics: 用户自定义的图片主题列表,如 ["modern office", "team collaboration"]
|
|
@@ -732,31 +794,17 @@ async def create_simple_site(
|
|
|
732
794
|
这样AI就能基于真实数据来生成个性化的网站内容。
|
|
733
795
|
"""
|
|
734
796
|
try:
|
|
735
|
-
#
|
|
736
|
-
used_model =
|
|
797
|
+
# 使用默认模型
|
|
798
|
+
used_model = DEFAULT_MODEL
|
|
737
799
|
|
|
738
|
-
#
|
|
739
|
-
|
|
740
|
-
if not os.path.isabs(project_root):
|
|
741
|
-
project_directory = os.path.join(DEFAULT_PROJECT_ROOT, project_root)
|
|
742
|
-
else:
|
|
743
|
-
project_directory = project_root
|
|
744
|
-
else:
|
|
745
|
-
# 使用站点标题作为目录名
|
|
746
|
-
safe_title = "".join(
|
|
747
|
-
c if c.isalnum() or c in "._-" else "_" for c in site_title
|
|
748
|
-
)
|
|
749
|
-
project_directory = os.path.join(DEFAULT_PROJECT_ROOT, safe_title)
|
|
800
|
+
# 使用新的路径解析逻辑,默认在共享目录下按标题创建项目
|
|
801
|
+
project_directory = _resolve_project_directory(None, site_title)
|
|
750
802
|
|
|
751
803
|
# 处理上下文
|
|
752
804
|
context_data = ""
|
|
753
|
-
actual_context_id =
|
|
805
|
+
actual_context_id: Optional[str] = None
|
|
754
806
|
|
|
755
|
-
if
|
|
756
|
-
# 使用缓存的上下文
|
|
757
|
-
cached_context = _CONTEXT_CACHE_BY_ID[context_id]
|
|
758
|
-
context_data = cached_context.get("content", "")
|
|
759
|
-
elif context_content:
|
|
807
|
+
if context_content:
|
|
760
808
|
# 使用新提供的上下文内容
|
|
761
809
|
context_data = context_content
|
|
762
810
|
# 生成新的上下文ID并缓存
|
|
@@ -847,13 +895,16 @@ async def create_simple_site(
|
|
|
847
895
|
"plan_id": plan_id,
|
|
848
896
|
}
|
|
849
897
|
|
|
898
|
+
if actual_context_id:
|
|
899
|
+
cached_entry["context_id"] = actual_context_id
|
|
900
|
+
|
|
850
901
|
_PLAN_CACHE_BY_ID[plan_id] = cached_entry
|
|
851
902
|
cache_key = (project_directory, description)
|
|
852
903
|
_PLAN_CACHE[cache_key] = cached_entry
|
|
853
904
|
|
|
854
905
|
# 将上下文信息关联到计划
|
|
855
906
|
if actual_context_id:
|
|
856
|
-
|
|
907
|
+
_CONTEXT_ID_BY_PLAN[plan_id] = actual_context_id
|
|
857
908
|
|
|
858
909
|
# 保存计划到文件
|
|
859
910
|
plan_filename = f"simple_site_plan_{plan_id}.json"
|
|
@@ -957,6 +1008,20 @@ async def get_progress(
|
|
|
957
1008
|
|
|
958
1009
|
if job_id:
|
|
959
1010
|
job_info = _JOB_REGISTRY.get(job_id)
|
|
1011
|
+
if not job_info:
|
|
1012
|
+
disk_state = _load_job_state_from_disk(job_id)
|
|
1013
|
+
if disk_state:
|
|
1014
|
+
_JOB_REGISTRY[job_id] = disk_state
|
|
1015
|
+
job_info = disk_state
|
|
1016
|
+
progress_log_from_state = job_info.get("progress_log")
|
|
1017
|
+
if progress_log_from_state:
|
|
1018
|
+
progress_log_str = str(progress_log_from_state)
|
|
1019
|
+
_PROGRESS_LOG_BY_JOB[job_id] = progress_log_str
|
|
1020
|
+
plan_in_state = job_info.get("plan_id")
|
|
1021
|
+
if plan_in_state:
|
|
1022
|
+
_PROGRESS_LOG_BY_ID.setdefault(
|
|
1023
|
+
plan_in_state, progress_log_str
|
|
1024
|
+
)
|
|
960
1025
|
if job_info and not plan_id:
|
|
961
1026
|
plan_id = job_info.get("plan_id")
|
|
962
1027
|
if job_id in _PROGRESS_LOG_BY_JOB:
|
|
@@ -1050,14 +1115,12 @@ async def get_progress(
|
|
|
1050
1115
|
"report": job_info.get("result", {}).get("report"),
|
|
1051
1116
|
"created_files": job_info.get("result", {}).get("created_files"),
|
|
1052
1117
|
}
|
|
1053
|
-
# 添加上传结果信息
|
|
1054
1118
|
if job_info.get("upload_result"):
|
|
1055
1119
|
job_snapshot["upload_result"] = job_info.get("upload_result")
|
|
1056
1120
|
|
|
1057
1121
|
if job_info.get("status") == "failed":
|
|
1058
1122
|
job_snapshot["error"] = job_info.get("error")
|
|
1059
1123
|
|
|
1060
|
-
# 添加上传错误信息
|
|
1061
1124
|
if job_info.get("upload_error"):
|
|
1062
1125
|
job_snapshot["upload_error"] = job_info.get("upload_error")
|
|
1063
1126
|
|
|
@@ -1075,13 +1138,11 @@ async def get_progress(
|
|
|
1075
1138
|
@mcp.tool()
|
|
1076
1139
|
async def upload_project_to_mcp_server(
|
|
1077
1140
|
folder_path: str,
|
|
1078
|
-
upload_url: str = "https://www.mcpcn.cc/api/fileUploadAndDownload/uploadMcpFile",
|
|
1079
1141
|
) -> Dict[str, Any]:
|
|
1080
1142
|
"""将项目文件夹打包成ZIP并上传到MCP服务器。
|
|
1081
1143
|
|
|
1082
1144
|
参数说明:
|
|
1083
1145
|
- folder_path: 项目文件夹的绝对路径
|
|
1084
|
-
- upload_url: 上传API地址,默认为MCP服务器地址
|
|
1085
1146
|
|
|
1086
1147
|
返回值:
|
|
1087
1148
|
- status: 上传状态 ("success" 或 "error")
|
|
@@ -1129,7 +1190,7 @@ async def upload_project_to_mcp_server(
|
|
|
1129
1190
|
"file", f, filename=zip_filename, content_type="application/zip"
|
|
1130
1191
|
)
|
|
1131
1192
|
|
|
1132
|
-
async with session.post(
|
|
1193
|
+
async with session.post(DEFAULT_UPLOAD_URL, data=data) as response:
|
|
1133
1194
|
response_text = await response.text()
|
|
1134
1195
|
|
|
1135
1196
|
if response.status != 200:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{htmlgen_mcp-0.3.6 → htmlgen_mcp-0.3.8}/src/htmlgen_mcp/agents/web_tools/html_templates_improved.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{htmlgen_mcp-0.3.6 → htmlgen_mcp-0.3.8}/src/htmlgen_mcp/agents/web_tools/simple_templates.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|