htmlgen-mcp 0.3.3__py3-none-any.whl → 0.3.5__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,14 @@ 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 htmlgen_mcp.nas_log_manager import get_nas_log_manager, ensure_job_log, log_progress, query_progress
36
+ from datetime import datetime
34
37
 
38
+ # 使用 NAS 作为默认存储路径
39
+ NAS_PATH = os.environ.get("NAS_STORAGE_PATH", "/app/mcp-servers/mcp-servers/html_agent")
35
40
  DEFAULT_PROJECT_ROOT = os.path.abspath(
36
- os.environ.get("WEB_AGENT_PROJECT_ROOT", os.path.join(PROJECT_ROOT, "projects"))
41
+ os.environ.get("WEB_AGENT_PROJECT_ROOT", f"{NAS_PATH}/projects")
37
42
  )
38
43
  DEFAULT_MODEL = os.environ.get("WEB_AGENT_MODEL", "qwen3-coder-plus-2025-09-23")
39
44
  DEFAULT_BASE_URL = os.environ.get(
@@ -42,11 +47,10 @@ DEFAULT_BASE_URL = os.environ.get(
42
47
 
43
48
  mcp = FastMCP("smart-web-agent")
44
49
 
45
- # MCP 服务持久化目录:默认为 ~/.mcp/make_web,可通过环境变量覆盖
46
- DEFAULT_MCP_STORAGE = Path.home() / ".mcp"
50
+ # MCP 服务持久化目录:使用 NAS 路径以便集群共享
47
51
  MCP_SERVICE_NAME = os.environ.get("MCP_SERVICE_NAME", "make_web")
48
52
  MCP_DATA_ROOT = Path(
49
- os.environ.get("MCP_DATA_DIR", DEFAULT_MCP_STORAGE / MCP_SERVICE_NAME)
53
+ os.environ.get("MCP_DATA_DIR", f"{NAS_PATH}/mcp_data/{MCP_SERVICE_NAME}")
50
54
  )
51
55
  MCP_DATA_ROOT.mkdir(parents=True, exist_ok=True)
52
56
 
@@ -54,12 +58,15 @@ MCP_DATA_ROOT.mkdir(parents=True, exist_ok=True)
54
58
  PLAN_CACHE_DIR = MCP_DATA_ROOT / "plan_cache"
55
59
  PLAN_CACHE_DIR.mkdir(exist_ok=True)
56
60
 
61
+ # 进度日志目录,存储每个任务的实时进度
57
62
  PROGRESS_LOG_DIR = MCP_DATA_ROOT / "progress_logs"
58
63
  PROGRESS_LOG_DIR.mkdir(exist_ok=True)
59
64
 
65
+ # 任务状态目录,每个任务一个 JSON 文件
60
66
  JOB_STATE_DIR = MCP_DATA_ROOT / "jobs" / "state"
61
67
  JOB_STATE_DIR.mkdir(parents=True, exist_ok=True)
62
68
 
69
+ # 上下文缓存目录
63
70
  CONTEXT_CACHE_DIR = MCP_DATA_ROOT / "context_cache"
64
71
  CONTEXT_CACHE_DIR.mkdir(exist_ok=True)
65
72
 
@@ -81,6 +88,8 @@ def _persist_job_state(job_id: str) -> None:
81
88
  return
82
89
  job_copy = {k: v for k, v in job.items() if k not in {"agent"}}
83
90
  job_copy["updated_at"] = time.time()
91
+
92
+ # 同时保存到本地和 NAS
84
93
  path = _job_state_path(job_id)
85
94
  try:
86
95
  path.write_text(
@@ -88,6 +97,15 @@ def _persist_job_state(job_id: str) -> None:
88
97
  )
89
98
  except Exception:
90
99
  pass
100
+
101
+ # 保存到 NAS 日志
102
+ try:
103
+ log_manager = get_nas_log_manager()
104
+ plan_id = job.get("plan_id")
105
+ log_manager.create_job_log(job_id, plan_id)
106
+ log_progress(job_id, status="registered", job_info=job_copy)
107
+ except Exception:
108
+ pass
91
109
 
92
110
 
93
111
  def _load_job_states() -> None:
@@ -168,6 +186,7 @@ def _build_agent(
168
186
  show_code: bool = False,
169
187
  verbose: bool = False,
170
188
  save_output: bool = False,
189
+ force_single_page: bool = True,
171
190
  ) -> SmartWebAgent:
172
191
  return SmartWebAgent(
173
192
  project_directory=project_directory,
@@ -175,6 +194,7 @@ def _build_agent(
175
194
  show_code=show_code,
176
195
  verbose=verbose,
177
196
  save_output=save_output,
197
+ force_single_page=force_single_page,
178
198
  )
179
199
 
180
200
 
@@ -249,12 +269,15 @@ def _execute_plan(
249
269
  plan: Dict[str, Any],
250
270
  *,
251
271
  progress_log_path: Optional[str] = None,
272
+ job_id: Optional[str] = None,
252
273
  ) -> Dict[str, Any]:
253
274
  progress_events: list[Dict[str, Any]] = []
254
275
 
255
276
  def _collect(event: Dict[str, Any]) -> None:
256
277
  if isinstance(event, dict):
257
278
  progress_events.append(event)
279
+
280
+ # 写入本地日志
258
281
  if progress_log_path:
259
282
  try:
260
283
  log_record = dict(event)
@@ -264,6 +287,13 @@ def _execute_plan(
264
287
  log_file.write("\n")
265
288
  except Exception:
266
289
  pass
290
+
291
+ # 同时写入 NAS 日志
292
+ if job_id:
293
+ try:
294
+ log_progress(job_id, **event)
295
+ except Exception:
296
+ pass
267
297
 
268
298
  results = agent._execute_plan_with_recovery(
269
299
  plan,
@@ -743,6 +773,7 @@ async def create_simple_site(
743
773
  model=used_model,
744
774
  show_code=False,
745
775
  verbose=False,
776
+ force_single_page=True,
746
777
  )
747
778
 
748
779
  # 构建针对简单网站的提示,包含上下文和图片要求
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: htmlgen-mcp
3
- Version: 0.3.3
3
+ Version: 0.3.5
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,36 @@
1
1
  htmlgen_mcp/__init__.py,sha256=8jambwGFxu8RNWes1BUEGDHErV-LcvDaABWqNja9GW0,114
2
- htmlgen_mcp/config.py,sha256=U_itOO9bG5vdZRuijWlQgDaVBPTyQ-fN1MH621QN7Mk,12375
2
+ htmlgen_mcp/config.py,sha256=Xd9naNKP1LfgOfOSXf2QbzKfrmByWvYsNP1OumDH25E,11249
3
+ htmlgen_mcp/improved_progress.py,sha256=7lhaPZRy1IbmCyniKeRMjFbICL_x5K8Vu57hA85IohA,13472
4
+ htmlgen_mcp/nas_log_manager.py,sha256=3vN074jtGP9czFWUrqLVM6upD7FScKPZRM0u6iUiz_8,10502
5
+ htmlgen_mcp/nas_storage.py,sha256=HpgROy53vrYiBiXsUJO56GCoYZdyYR15iVvOBqyp7Yc,11292
6
+ htmlgen_mcp/progress_tools.py,sha256=SOScPSr3hEv4rvGzqvwUcomEFiPhhNxJ7CbWURlFpBs,5067
7
+ htmlgen_mcp/progress_tracker.py,sha256=2TVduWNJJH08EQ7Vf9EpiwPjtp61JX7muSCdbGHZAfM,12210
3
8
  htmlgen_mcp/sse_optimizations.py,sha256=_UTpgLtxgNAiiEkO5lPihOi1-eEQk6R4ejNParufrrc,6932
4
- htmlgen_mcp/web_agent_server.py,sha256=69Ht2qoQZ8uFIoov6md4MoJdQWm4cuWOZllBu_FFCJc,47463
9
+ htmlgen_mcp/web_agent_server.py,sha256=bJ7GrZnGuqLd11aJieKM0ua6QnfnVO10HIHXdrhC3Dk,48556
5
10
  htmlgen_mcp/agents/__init__.py,sha256=Xydfjzw9s9O6I5Ixx6EmsTdXu26136NDPUAqt9B1hzE,121
6
11
  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
12
+ htmlgen_mcp/agents/quick_generator.py,sha256=2wV4PCRugV0suTedLDV91_etHy_2Fiw4J0MraT7MQjw,34201
13
+ htmlgen_mcp/agents/smart_web_agent.py,sha256=i651WqD54BhM6WfabutTyBY3VwshkFaYPDJ4H-yapQI,126830
14
+ htmlgen_mcp/agents/web_tools/__init__.py,sha256=oqH9p3C6UHn2yOzBgbHZK3D_rCoWQmQxM-jI4YLTseg,2649
12
15
  htmlgen_mcp/agents/web_tools/bootstrap.py,sha256=QOoR38GhrK2JHow2P86fHgV9r6UFGDikwT0w6Qq4yh0,1909
13
16
  htmlgen_mcp/agents/web_tools/browser.py,sha256=0Z84BtvBSXplpIDySw_ICH6RZPe9PtSYP8WGm4GJp0M,870
14
17
  htmlgen_mcp/agents/web_tools/colors.py,sha256=GyoDWJiJPMTXRrYiJ8c0-ur25jDOZuq5nNSPDA38yEI,5242
15
- htmlgen_mcp/agents/web_tools/css.py,sha256=GLEq3jy5CwpW-vrNOzU9EDPVLGdEDFwdpGQXDWuwM_4,37467
18
+ htmlgen_mcp/agents/web_tools/css.py,sha256=_JlOjowjtqYm8LQ5dcl1A3f_DxBtLdQJMCk3oxM1BKI,41084
16
19
  htmlgen_mcp/agents/web_tools/edgeone_deploy.py,sha256=DAc0ISqu8swGG9MEU2o8ONc7PDGsQ2MOakcSi2phxfk,20113
17
20
  htmlgen_mcp/agents/web_tools/html_templates.py,sha256=kbs0slN5kIXstlMbqgLBQxMOVQtlap2lwLYg-3GBR4s,79659
18
21
  htmlgen_mcp/agents/web_tools/html_templates_improved.py,sha256=i9XWv4Cqm94Ud7lqkOHgYHazhZDacrSPpamI9F1fw8g,25221
19
22
  htmlgen_mcp/agents/web_tools/images.py,sha256=miFp8-77SLlvBDBBbR6LYDfYGVzO0GmcZz06CQC16Gc,27617
20
23
  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
24
+ htmlgen_mcp/agents/web_tools/js.py,sha256=DFS5kj65qU6tEZcwS4Hxr2_JqVncLwNdtdPoMU8-oEU,9468
25
+ htmlgen_mcp/agents/web_tools/navigation.py,sha256=7ABTqRHsQ2IOeT-RN1b9uVCxGm8IFkLZQA1CFuevPEE,19935
26
+ htmlgen_mcp/agents/web_tools/project.py,sha256=9iw09yAIpWO5l9TCz5FPtr8k5TPbF32BfnYJW-P5w3w,841
24
27
  htmlgen_mcp/agents/web_tools/simple_builder.py,sha256=nfxrr0F_wlaq4k7_No3_33stnBlxhyjFFGQBxxPtGTM,10658
25
28
  htmlgen_mcp/agents/web_tools/simple_css.py,sha256=kj9X3sHHhj1wGwBVL20j6w2qIHXRdxfBXoldnabxKsk,8278
26
29
  htmlgen_mcp/agents/web_tools/simple_js.py,sha256=xMiuF-u-h_IIkUONZIa4Xf8vKB5mcXxwQf5b_BIcpoE,12174
27
30
  htmlgen_mcp/agents/web_tools/simple_templates.py,sha256=-Rs-SsWpGZT2hiwa3jZNVDHOMZOo1vV2pWbmBdR30os,6471
28
31
  htmlgen_mcp/agents/web_tools/validation.py,sha256=bNA6aWXrCSi7sPqQw5bBR3XF69gRf85D5jSMi996CtI,2069
29
- htmlgen_mcp-0.3.3.dist-info/METADATA,sha256=9kWd0XVCvjZfico2ZWuW5uxF3EcTjk7H21L-UqYNbkM,5161
30
- htmlgen_mcp-0.3.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
31
- htmlgen_mcp-0.3.3.dist-info/entry_points.txt,sha256=w7ufTQJobIxT3FYI24yKsCEwEQvBOWhNjckUd9Amu_k,66
32
- htmlgen_mcp-0.3.3.dist-info/top_level.txt,sha256=KnglzX4ekV8SQkHTsJg2_nTBXz2TxaYLdvoMMovHLNk,12
33
- htmlgen_mcp-0.3.3.dist-info/RECORD,,
32
+ htmlgen_mcp-0.3.5.dist-info/METADATA,sha256=w8qefsdRaDtpKXOXfpbukGPs90wlNh3ErdQRMtsGivM,5161
33
+ htmlgen_mcp-0.3.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
34
+ htmlgen_mcp-0.3.5.dist-info/entry_points.txt,sha256=w7ufTQJobIxT3FYI24yKsCEwEQvBOWhNjckUd9Amu_k,66
35
+ htmlgen_mcp-0.3.5.dist-info/top_level.txt,sha256=KnglzX4ekV8SQkHTsJg2_nTBXz2TxaYLdvoMMovHLNk,12
36
+ htmlgen_mcp-0.3.5.dist-info/RECORD,,