htmlgen-mcp 0.3.2__py3-none-any.whl → 0.3.4__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.

Potentially problematic release.


This version of htmlgen-mcp might be problematic. Click here for more details.

@@ -0,0 +1,378 @@
1
+ """进度跟踪模块 - 支持集群环境下的任务进度查询"""
2
+ import json
3
+ import time
4
+ import os
5
+ from pathlib import Path
6
+ from typing import Dict, List, Optional, Any
7
+ from datetime import datetime
8
+ import hashlib
9
+
10
+
11
+ class ProgressTracker:
12
+ """任务进度跟踪器 - 支持分布式环境"""
13
+
14
+ def __init__(self, nas_base_path: str = "/app/mcp-servers/mcp-servers/html_agent"):
15
+ """
16
+ 初始化进度跟踪器
17
+
18
+ Args:
19
+ nas_base_path: NAS 基础路径
20
+ """
21
+ self.nas_base = Path(nas_base_path)
22
+ self.progress_dir = self.nas_base / "mcp_data" / "make_web" / "progress"
23
+ self.progress_dir.mkdir(parents=True, exist_ok=True)
24
+
25
+ def create_task(self, task_type: str, description: str) -> str:
26
+ """
27
+ 创建新任务并返回任务 ID
28
+
29
+ Args:
30
+ task_type: 任务类型 (plan_site, execute_plan, etc.)
31
+ description: 任务描述
32
+
33
+ Returns:
34
+ 任务 ID
35
+ """
36
+ # 生成唯一任务 ID
37
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S_%f")
38
+ node_id = os.environ.get("NODE_ID", "node0")
39
+ random_suffix = hashlib.md5(f"{timestamp}{description}".encode()).hexdigest()[:8]
40
+ task_id = f"{task_type}_{node_id}_{timestamp}_{random_suffix}"
41
+
42
+ # 创建任务记录
43
+ task_info = {
44
+ "task_id": task_id,
45
+ "task_type": task_type,
46
+ "description": description,
47
+ "status": "pending",
48
+ "progress": 0,
49
+ "created_at": datetime.now().isoformat(),
50
+ "node_id": node_id,
51
+ "events": [],
52
+ "current_step": None,
53
+ "total_steps": None
54
+ }
55
+
56
+ self._save_task_info(task_id, task_info)
57
+ return task_id
58
+
59
+ def update_progress(
60
+ self,
61
+ task_id: str,
62
+ progress: int,
63
+ status: str = "running",
64
+ message: str = None,
65
+ current_step: int = None,
66
+ total_steps: int = None,
67
+ data: Dict[str, Any] = None
68
+ ) -> None:
69
+ """
70
+ 更新任务进度
71
+
72
+ Args:
73
+ task_id: 任务 ID
74
+ progress: 进度百分比 (0-100)
75
+ status: 任务状态 (pending/running/completed/failed)
76
+ message: 进度消息
77
+ current_step: 当前步骤
78
+ total_steps: 总步骤数
79
+ data: 附加数据
80
+ """
81
+ task_info = self._load_task_info(task_id)
82
+ if not task_info:
83
+ task_info = {"task_id": task_id, "events": []}
84
+
85
+ # 更新基本信息
86
+ task_info["progress"] = progress
87
+ task_info["status"] = status
88
+ task_info["updated_at"] = datetime.now().isoformat()
89
+
90
+ if current_step is not None:
91
+ task_info["current_step"] = current_step
92
+ if total_steps is not None:
93
+ task_info["total_steps"] = total_steps
94
+
95
+ # 添加事件记录
96
+ event = {
97
+ "timestamp": datetime.now().isoformat(),
98
+ "progress": progress,
99
+ "status": status,
100
+ "message": message
101
+ }
102
+ if data:
103
+ event["data"] = data
104
+
105
+ task_info["events"].append(event)
106
+
107
+ # 保存更新
108
+ self._save_task_info(task_id, task_info)
109
+
110
+ # 同时写入流式日志(用于实时查询)
111
+ self._append_progress_log(task_id, event)
112
+
113
+ def get_task_progress(self, task_id: str) -> Optional[Dict[str, Any]]:
114
+ """
115
+ 获取任务进度
116
+
117
+ Args:
118
+ task_id: 任务 ID
119
+
120
+ Returns:
121
+ 任务进度信息
122
+ """
123
+ return self._load_task_info(task_id)
124
+
125
+ def get_task_events(
126
+ self,
127
+ task_id: str,
128
+ since_timestamp: Optional[str] = None
129
+ ) -> List[Dict[str, Any]]:
130
+ """
131
+ 获取任务事件(支持增量查询)
132
+
133
+ Args:
134
+ task_id: 任务 ID
135
+ since_timestamp: 从此时间戳之后的事件
136
+
137
+ Returns:
138
+ 事件列表
139
+ """
140
+ task_info = self._load_task_info(task_id)
141
+ if not task_info:
142
+ return []
143
+
144
+ events = task_info.get("events", [])
145
+
146
+ if since_timestamp:
147
+ # 过滤出指定时间后的事件
148
+ events = [
149
+ e for e in events
150
+ if e.get("timestamp", "") > since_timestamp
151
+ ]
152
+
153
+ return events
154
+
155
+ def list_tasks(
156
+ self,
157
+ task_type: Optional[str] = None,
158
+ status: Optional[str] = None,
159
+ limit: int = 20
160
+ ) -> List[Dict[str, Any]]:
161
+ """
162
+ 列出任务列表
163
+
164
+ Args:
165
+ task_type: 筛选任务类型
166
+ status: 筛选任务状态
167
+ limit: 返回数量限制
168
+
169
+ Returns:
170
+ 任务列表
171
+ """
172
+ tasks = []
173
+
174
+ # 扫描所有任务文件
175
+ for task_file in self.progress_dir.glob("*.json"):
176
+ if task_file.name.endswith("_log.json"):
177
+ continue # 跳过日志文件
178
+
179
+ try:
180
+ with open(task_file, 'r', encoding='utf-8') as f:
181
+ task_info = json.load(f)
182
+
183
+ # 应用过滤条件
184
+ if task_type and task_info.get("task_type") != task_type:
185
+ continue
186
+ if status and task_info.get("status") != status:
187
+ continue
188
+
189
+ # 精简信息,不包含完整事件列表
190
+ summary = {
191
+ "task_id": task_info.get("task_id"),
192
+ "task_type": task_info.get("task_type"),
193
+ "description": task_info.get("description"),
194
+ "status": task_info.get("status"),
195
+ "progress": task_info.get("progress"),
196
+ "created_at": task_info.get("created_at"),
197
+ "updated_at": task_info.get("updated_at"),
198
+ "current_step": task_info.get("current_step"),
199
+ "total_steps": task_info.get("total_steps")
200
+ }
201
+ tasks.append(summary)
202
+
203
+ except Exception:
204
+ continue
205
+
206
+ # 按创建时间倒序排序
207
+ tasks.sort(key=lambda x: x.get("created_at", ""), reverse=True)
208
+
209
+ return tasks[:limit]
210
+
211
+ def get_realtime_progress(self, task_id: str) -> Dict[str, Any]:
212
+ """
213
+ 获取实时进度(用于 SSE 流式传输)
214
+
215
+ Args:
216
+ task_id: 任务 ID
217
+
218
+ Returns:
219
+ 实时进度信息
220
+ """
221
+ task_info = self._load_task_info(task_id)
222
+ if not task_info:
223
+ return {
224
+ "task_id": task_id,
225
+ "status": "not_found",
226
+ "progress": 0
227
+ }
228
+
229
+ # 计算详细进度
230
+ progress_detail = {
231
+ "task_id": task_id,
232
+ "status": task_info.get("status", "unknown"),
233
+ "progress": task_info.get("progress", 0),
234
+ "current_step": task_info.get("current_step"),
235
+ "total_steps": task_info.get("total_steps"),
236
+ "message": None
237
+ }
238
+
239
+ # 获取最新消息
240
+ events = task_info.get("events", [])
241
+ if events:
242
+ latest_event = events[-1]
243
+ progress_detail["message"] = latest_event.get("message")
244
+ progress_detail["last_update"] = latest_event.get("timestamp")
245
+
246
+ # 如果有步骤信息,计算步骤进度
247
+ if progress_detail["current_step"] and progress_detail["total_steps"]:
248
+ step_progress = (progress_detail["current_step"] / progress_detail["total_steps"]) * 100
249
+ progress_detail["step_progress"] = round(step_progress, 1)
250
+
251
+ return progress_detail
252
+
253
+ def _save_task_info(self, task_id: str, task_info: Dict[str, Any]) -> None:
254
+ """保存任务信息到 NAS"""
255
+ task_file = self.progress_dir / f"{task_id}.json"
256
+
257
+ # 原子性写入(先写临时文件,再重命名)
258
+ temp_file = task_file.with_suffix('.tmp')
259
+ with open(temp_file, 'w', encoding='utf-8') as f:
260
+ json.dump(task_info, f, ensure_ascii=False, indent=2)
261
+
262
+ # 重命名(原子操作)
263
+ temp_file.replace(task_file)
264
+
265
+ def _load_task_info(self, task_id: str) -> Optional[Dict[str, Any]]:
266
+ """从 NAS 加载任务信息"""
267
+ task_file = self.progress_dir / f"{task_id}.json"
268
+
269
+ if not task_file.exists():
270
+ return None
271
+
272
+ try:
273
+ with open(task_file, 'r', encoding='utf-8') as f:
274
+ return json.load(f)
275
+ except Exception:
276
+ return None
277
+
278
+ def _append_progress_log(self, task_id: str, event: Dict[str, Any]) -> None:
279
+ """追加进度日志(用于流式查询)"""
280
+ log_file = self.progress_dir / f"{task_id}_log.jsonl"
281
+
282
+ try:
283
+ with open(log_file, 'a', encoding='utf-8') as f:
284
+ f.write(json.dumps(event, ensure_ascii=False))
285
+ f.write('\n')
286
+ except Exception:
287
+ pass
288
+
289
+ def cleanup_old_tasks(self, days_to_keep: int = 7) -> int:
290
+ """
291
+ 清理旧任务
292
+
293
+ Args:
294
+ days_to_keep: 保留天数
295
+
296
+ Returns:
297
+ 清理的任务数量
298
+ """
299
+ cleaned = 0
300
+ cutoff_time = time.time() - (days_to_keep * 24 * 3600)
301
+
302
+ for task_file in self.progress_dir.glob("*.json"):
303
+ try:
304
+ # 检查文件修改时间
305
+ if task_file.stat().st_mtime < cutoff_time:
306
+ task_file.unlink()
307
+
308
+ # 同时删除对应的日志文件
309
+ log_file = task_file.with_suffix('.jsonl')
310
+ if log_file.exists():
311
+ log_file.unlink()
312
+
313
+ cleaned += 1
314
+ except Exception:
315
+ continue
316
+
317
+ return cleaned
318
+
319
+
320
+ # 全局进度跟踪器实例
321
+ _progress_tracker: Optional[ProgressTracker] = None
322
+
323
+
324
+ def get_progress_tracker() -> ProgressTracker:
325
+ """获取进度跟踪器实例(单例)"""
326
+ global _progress_tracker
327
+ if _progress_tracker is None:
328
+ nas_path = os.environ.get(
329
+ "NAS_STORAGE_PATH",
330
+ "/app/mcp-servers/mcp-servers/html_agent"
331
+ )
332
+ _progress_tracker = ProgressTracker(nas_path)
333
+ return _progress_tracker
334
+
335
+
336
+ # 便捷函数
337
+ def track_task(task_type: str, description: str):
338
+ """
339
+ 任务跟踪装饰器
340
+
341
+ 使用方式:
342
+ @track_task("plan_site", "生成网站计划")
343
+ async def plan_site(description: str):
344
+ ...
345
+ """
346
+ def decorator(func):
347
+ async def wrapper(*args, **kwargs):
348
+ tracker = get_progress_tracker()
349
+ task_id = tracker.create_task(task_type, description)
350
+
351
+ try:
352
+ # 标记任务开始
353
+ tracker.update_progress(task_id, 0, "running", "任务开始执行")
354
+
355
+ # 执行任务
356
+ result = await func(*args, **kwargs)
357
+
358
+ # 标记任务完成
359
+ tracker.update_progress(task_id, 100, "completed", "任务执行成功")
360
+
361
+ # 在结果中添加任务 ID
362
+ if isinstance(result, dict):
363
+ result["task_id"] = task_id
364
+
365
+ return result
366
+
367
+ except Exception as e:
368
+ # 标记任务失败
369
+ tracker.update_progress(
370
+ task_id,
371
+ tracker.get_task_progress(task_id).get("progress", 0),
372
+ "failed",
373
+ f"任务执行失败: {str(e)}"
374
+ )
375
+ raise
376
+
377
+ return wrapper
378
+ return decorator
@@ -31,9 +31,13 @@ import uuid
31
31
  from pathlib import Path
32
32
 
33
33
  from htmlgen_mcp.agents.smart_web_agent import SmartWebAgent
34
+ from htmlgen_mcp.nas_storage import get_nas_storage
35
+ from datetime import datetime
34
36
 
37
+ # 使用 NAS 作为默认存储路径
38
+ NAS_PATH = os.environ.get("NAS_STORAGE_PATH", "/app/mcp-servers/mcp-servers/html_agent")
35
39
  DEFAULT_PROJECT_ROOT = os.path.abspath(
36
- os.environ.get("WEB_AGENT_PROJECT_ROOT", os.path.join(PROJECT_ROOT, "projects"))
40
+ os.environ.get("WEB_AGENT_PROJECT_ROOT", f"{NAS_PATH}/projects")
37
41
  )
38
42
  DEFAULT_MODEL = os.environ.get("WEB_AGENT_MODEL", "qwen3-coder-plus-2025-09-23")
39
43
  DEFAULT_BASE_URL = os.environ.get(
@@ -42,45 +46,26 @@ DEFAULT_BASE_URL = os.environ.get(
42
46
 
43
47
  mcp = FastMCP("smart-web-agent")
44
48
 
45
- # MCP 服务持久化目录:优先使用项目根目录下的 .mcp_state,可通过环境变量覆盖
46
- DEFAULT_MCP_STORAGE = Path.home() / ".mcp"
49
+ # MCP 服务持久化目录:使用 NAS 路径以便集群共享
47
50
  MCP_SERVICE_NAME = os.environ.get("MCP_SERVICE_NAME", "make_web")
48
-
49
-
50
- def _resolve_mcp_data_root() -> Path:
51
- """解析 MCP 数据目录,优先使用共享项目目录"""
52
- env_dir = os.environ.get("MCP_DATA_DIR")
53
- if env_dir:
54
- path = Path(env_dir)
55
- path.mkdir(parents=True, exist_ok=True)
56
- return path
57
-
58
- shared_dir = Path(DEFAULT_PROJECT_ROOT) / ".mcp_state"
59
- try:
60
- shared_dir.mkdir(parents=True, exist_ok=True)
61
- return shared_dir
62
- except Exception:
63
- fallback = DEFAULT_MCP_STORAGE / MCP_SERVICE_NAME
64
- fallback.mkdir(parents=True, exist_ok=True)
65
- print(
66
- f"⚠️ 无法在 {shared_dir} 创建 MCP 数据目录,改用 {fallback}",
67
- file=sys.stderr,
68
- )
69
- return fallback
70
-
71
-
72
- MCP_DATA_ROOT = _resolve_mcp_data_root()
51
+ MCP_DATA_ROOT = Path(
52
+ os.environ.get("MCP_DATA_DIR", f"{NAS_PATH}/mcp_data/{MCP_SERVICE_NAME}")
53
+ )
54
+ MCP_DATA_ROOT.mkdir(parents=True, exist_ok=True)
73
55
 
74
56
  # 简单的缓存:记录最近一次生成的计划,避免“create_simple_site → execute_plan”时需手动传递
75
57
  PLAN_CACHE_DIR = MCP_DATA_ROOT / "plan_cache"
76
58
  PLAN_CACHE_DIR.mkdir(exist_ok=True)
77
59
 
60
+ # 进度日志目录,存储每个任务的实时进度
78
61
  PROGRESS_LOG_DIR = MCP_DATA_ROOT / "progress_logs"
79
62
  PROGRESS_LOG_DIR.mkdir(exist_ok=True)
80
63
 
64
+ # 任务状态目录,每个任务一个 JSON 文件
81
65
  JOB_STATE_DIR = MCP_DATA_ROOT / "jobs" / "state"
82
66
  JOB_STATE_DIR.mkdir(parents=True, exist_ok=True)
83
67
 
68
+ # 上下文缓存目录
84
69
  CONTEXT_CACHE_DIR = MCP_DATA_ROOT / "context_cache"
85
70
  CONTEXT_CACHE_DIR.mkdir(exist_ok=True)
86
71
 
@@ -189,6 +174,7 @@ def _build_agent(
189
174
  show_code: bool = False,
190
175
  verbose: bool = False,
191
176
  save_output: bool = False,
177
+ force_single_page: bool = True,
192
178
  ) -> SmartWebAgent:
193
179
  return SmartWebAgent(
194
180
  project_directory=project_directory,
@@ -196,6 +182,7 @@ def _build_agent(
196
182
  show_code=show_code,
197
183
  verbose=verbose,
198
184
  save_output=save_output,
185
+ force_single_page=force_single_page,
199
186
  )
200
187
 
201
188
 
@@ -764,6 +751,7 @@ async def create_simple_site(
764
751
  model=used_model,
765
752
  show_code=False,
766
753
  verbose=False,
754
+ force_single_page=True,
767
755
  )
768
756
 
769
757
  # 构建针对简单网站的提示,包含上下文和图片要求
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: htmlgen-mcp
3
- Version: 0.3.2
3
+ Version: 0.3.4
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
@@ -1,33 +1,34 @@
1
1
  htmlgen_mcp/__init__.py,sha256=8jambwGFxu8RNWes1BUEGDHErV-LcvDaABWqNja9GW0,114
2
- htmlgen_mcp/config.py,sha256=TXDAvNFmeCI4RIznrBhZhhvWLgXAw6MO6j83prJxFsE,11373
2
+ htmlgen_mcp/config.py,sha256=Xd9naNKP1LfgOfOSXf2QbzKfrmByWvYsNP1OumDH25E,11249
3
+ htmlgen_mcp/nas_storage.py,sha256=HpgROy53vrYiBiXsUJO56GCoYZdyYR15iVvOBqyp7Yc,11292
4
+ htmlgen_mcp/progress_tools.py,sha256=SOScPSr3hEv4rvGzqvwUcomEFiPhhNxJ7CbWURlFpBs,5067
5
+ htmlgen_mcp/progress_tracker.py,sha256=2TVduWNJJH08EQ7Vf9EpiwPjtp61JX7muSCdbGHZAfM,12210
3
6
  htmlgen_mcp/sse_optimizations.py,sha256=_UTpgLtxgNAiiEkO5lPihOi1-eEQk6R4ejNParufrrc,6932
4
- htmlgen_mcp/web_agent_server.py,sha256=UfjO3DtiHekr61nJ0z-Ne9cMXTqzjRLuIfIr85wBhIY,48091
7
+ htmlgen_mcp/web_agent_server.py,sha256=_0t-HJBmqF2vGF5aVfsV1LQQGzmW0E95ocwBQn5jBNQ,47848
5
8
  htmlgen_mcp/agents/__init__.py,sha256=Xydfjzw9s9O6I5Ixx6EmsTdXu26136NDPUAqt9B1hzE,121
6
9
  htmlgen_mcp/agents/ai_content_generator.py,sha256=tWGC9cY6Wp7MB1P9J7uCv8LUdiS02rgS6vxaNHD7KQk,10311
7
- htmlgen_mcp/agents/cluster_state.py,sha256=eDAqVm_A94_x7YfmhKKQ6T1FFt_4XQOyUiHdiTPF7Qg,14615
8
- htmlgen_mcp/agents/cluster_storage.py,sha256=TJTl_uk4mBHf6nZ1Es0LzPzXfneUITVW-JXTP0gZSdE,11534
9
- htmlgen_mcp/agents/quick_generator.py,sha256=U16MOazMdBBz0MjtfsFuc-dTLFHpPbXzGG0Yuy5ljwE,9561
10
- htmlgen_mcp/agents/smart_web_agent.py,sha256=gIlHqE2Zvf8IwXQCBVSzteYN0u7C6e3n5D7jCDlbR-8,99054
11
- htmlgen_mcp/agents/web_tools/__init__.py,sha256=2Km6T6tMkjMFKoEWkrsSuLUyr_yfHq78_V_oreqQOMc,2472
10
+ htmlgen_mcp/agents/quick_generator.py,sha256=2wV4PCRugV0suTedLDV91_etHy_2Fiw4J0MraT7MQjw,34201
11
+ htmlgen_mcp/agents/smart_web_agent.py,sha256=i651WqD54BhM6WfabutTyBY3VwshkFaYPDJ4H-yapQI,126830
12
+ htmlgen_mcp/agents/web_tools/__init__.py,sha256=oqH9p3C6UHn2yOzBgbHZK3D_rCoWQmQxM-jI4YLTseg,2649
12
13
  htmlgen_mcp/agents/web_tools/bootstrap.py,sha256=QOoR38GhrK2JHow2P86fHgV9r6UFGDikwT0w6Qq4yh0,1909
13
14
  htmlgen_mcp/agents/web_tools/browser.py,sha256=0Z84BtvBSXplpIDySw_ICH6RZPe9PtSYP8WGm4GJp0M,870
14
15
  htmlgen_mcp/agents/web_tools/colors.py,sha256=GyoDWJiJPMTXRrYiJ8c0-ur25jDOZuq5nNSPDA38yEI,5242
15
- htmlgen_mcp/agents/web_tools/css.py,sha256=GLEq3jy5CwpW-vrNOzU9EDPVLGdEDFwdpGQXDWuwM_4,37467
16
+ htmlgen_mcp/agents/web_tools/css.py,sha256=_JlOjowjtqYm8LQ5dcl1A3f_DxBtLdQJMCk3oxM1BKI,41084
16
17
  htmlgen_mcp/agents/web_tools/edgeone_deploy.py,sha256=DAc0ISqu8swGG9MEU2o8ONc7PDGsQ2MOakcSi2phxfk,20113
17
18
  htmlgen_mcp/agents/web_tools/html_templates.py,sha256=kbs0slN5kIXstlMbqgLBQxMOVQtlap2lwLYg-3GBR4s,79659
18
19
  htmlgen_mcp/agents/web_tools/html_templates_improved.py,sha256=i9XWv4Cqm94Ud7lqkOHgYHazhZDacrSPpamI9F1fw8g,25221
19
20
  htmlgen_mcp/agents/web_tools/images.py,sha256=miFp8-77SLlvBDBBbR6LYDfYGVzO0GmcZz06CQC16Gc,27617
20
21
  htmlgen_mcp/agents/web_tools/images_fixed.py,sha256=2L1m4TKTx253Eigb_OSg89mOIjfzwBXfeyDJ7hsh3tw,7567
21
- htmlgen_mcp/agents/web_tools/js.py,sha256=NaeABI0WWX23sdtZ2CZHJVBbOyRDbsKPwNqVz76n2SU,9388
22
- htmlgen_mcp/agents/web_tools/navigation.py,sha256=F7CjllbLdHhBy7jKsF0n8TFfHz646GzWXYhIKEHdL1k,19826
23
- htmlgen_mcp/agents/web_tools/project.py,sha256=JUxPDdJSdjdAu8eoMCsxMUOayQ_Tdt33wUFjCL93b-c,910
22
+ htmlgen_mcp/agents/web_tools/js.py,sha256=DFS5kj65qU6tEZcwS4Hxr2_JqVncLwNdtdPoMU8-oEU,9468
23
+ htmlgen_mcp/agents/web_tools/navigation.py,sha256=7ABTqRHsQ2IOeT-RN1b9uVCxGm8IFkLZQA1CFuevPEE,19935
24
+ htmlgen_mcp/agents/web_tools/project.py,sha256=9iw09yAIpWO5l9TCz5FPtr8k5TPbF32BfnYJW-P5w3w,841
24
25
  htmlgen_mcp/agents/web_tools/simple_builder.py,sha256=nfxrr0F_wlaq4k7_No3_33stnBlxhyjFFGQBxxPtGTM,10658
25
26
  htmlgen_mcp/agents/web_tools/simple_css.py,sha256=kj9X3sHHhj1wGwBVL20j6w2qIHXRdxfBXoldnabxKsk,8278
26
27
  htmlgen_mcp/agents/web_tools/simple_js.py,sha256=xMiuF-u-h_IIkUONZIa4Xf8vKB5mcXxwQf5b_BIcpoE,12174
27
28
  htmlgen_mcp/agents/web_tools/simple_templates.py,sha256=-Rs-SsWpGZT2hiwa3jZNVDHOMZOo1vV2pWbmBdR30os,6471
28
29
  htmlgen_mcp/agents/web_tools/validation.py,sha256=bNA6aWXrCSi7sPqQw5bBR3XF69gRf85D5jSMi996CtI,2069
29
- htmlgen_mcp-0.3.2.dist-info/METADATA,sha256=ZbrJ1I9ijuFaPO0LDrRrVZS1r8d82_qLZ6xM-EP_dN0,5161
30
- htmlgen_mcp-0.3.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
31
- htmlgen_mcp-0.3.2.dist-info/entry_points.txt,sha256=w7ufTQJobIxT3FYI24yKsCEwEQvBOWhNjckUd9Amu_k,66
32
- htmlgen_mcp-0.3.2.dist-info/top_level.txt,sha256=KnglzX4ekV8SQkHTsJg2_nTBXz2TxaYLdvoMMovHLNk,12
33
- htmlgen_mcp-0.3.2.dist-info/RECORD,,
30
+ htmlgen_mcp-0.3.4.dist-info/METADATA,sha256=UIbhoxesfWZpcwkrDDehZ5FlHmFN2Kv3gj3zkdF8SEs,5161
31
+ htmlgen_mcp-0.3.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
32
+ htmlgen_mcp-0.3.4.dist-info/entry_points.txt,sha256=w7ufTQJobIxT3FYI24yKsCEwEQvBOWhNjckUd9Amu_k,66
33
+ htmlgen_mcp-0.3.4.dist-info/top_level.txt,sha256=KnglzX4ekV8SQkHTsJg2_nTBXz2TxaYLdvoMMovHLNk,12
34
+ htmlgen_mcp-0.3.4.dist-info/RECORD,,