myagent-ai 1.19.4 → 1.19.6

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.
Files changed (51) hide show
  1. package/core/context_builder.py +49 -2
  2. package/core/logger.py +29 -2
  3. package/core/utils.py +3 -1
  4. package/core/vnc_manager.py +0 -0
  5. package/data/uploads/2026-04/32c9e9d3-8cf_test.pdf.pdf +0 -0
  6. package/install.ps1 +11 -3
  7. package/main.py +1 -1
  8. package/package.json +1 -1
  9. package/requirements-optional.txt +0 -0
  10. package/requirements.txt +2 -0
  11. package/skills/__init__.py +0 -0
  12. package/skills/base.py +0 -0
  13. package/skills/browser_skill.py +0 -0
  14. package/skills/chromedev_mcp.py +0 -0
  15. package/skills/docx_skill.py +0 -0
  16. package/skills/file_send.py +0 -0
  17. package/skills/file_skill.py +0 -0
  18. package/skills/frontend-dev/SKILL.md +0 -0
  19. package/skills/frontend-dev/references/asset-prompt-guide.md +0 -0
  20. package/skills/frontend-dev/references/env-setup.md +0 -0
  21. package/skills/frontend-dev/references/minimax-cli-reference.md +0 -0
  22. package/skills/frontend-dev/references/minimax-image-guide.md +0 -0
  23. package/skills/frontend-dev/references/minimax-music-guide.md +0 -0
  24. package/skills/frontend-dev/references/minimax-tts-guide.md +0 -0
  25. package/skills/frontend-dev/references/minimax-video-guide.md +0 -0
  26. package/skills/frontend-dev/references/minimax-voice-catalog.md +0 -0
  27. package/skills/frontend-dev/references/motion-recipes.md +0 -0
  28. package/skills/frontend-dev/references/troubleshooting.md +0 -0
  29. package/skills/frontend-dev/scripts/minimax_image.py +0 -0
  30. package/skills/frontend-dev/scripts/minimax_music.py +0 -0
  31. package/skills/frontend-dev/scripts/minimax_tts.py +0 -0
  32. package/skills/frontend-dev/scripts/minimax_video.py +0 -0
  33. package/skills/frontend-dev/templates/generator_template.js +0 -0
  34. package/skills/frontend-dev/templates/viewer.html +0 -0
  35. package/skills/fullstack-dev/SKILL.md +0 -0
  36. package/skills/fullstack-dev/references/api-design.md +0 -0
  37. package/skills/fullstack-dev/references/auth-flow.md +0 -0
  38. package/skills/fullstack-dev/references/db-schema.md +0 -0
  39. package/skills/fullstack-dev/references/django-best-practices.md +0 -0
  40. package/skills/fullstack-dev/references/environment-management.md +0 -0
  41. package/skills/fullstack-dev/references/release-checklist.md +0 -0
  42. package/skills/fullstack-dev/references/technology-selection.md +0 -0
  43. package/skills/fullstack-dev/references/testing-strategy.md +0 -0
  44. package/skills/gui_skill.py +0 -0
  45. package/skills/pdf_skill.py +0 -0
  46. package/skills/ppt_skill.py +0 -0
  47. package/skills/registry.py +15 -2
  48. package/skills/search_skill.py +0 -0
  49. package/skills/system_skill.py +0 -0
  50. package/skills/xlsx_skill.py +0 -0
  51. package/web/ui/index.html +4 -4
@@ -13,6 +13,7 @@ core/context_builder.py - 上下文构建器
13
13
  <usersays> - 用户语音转文本输入(键盘输入时为空)
14
14
  <task_plan> - 当前任务计划(Markdown 格式,上轮已完成时为空)
15
15
  <tools> - 可用工具列表(名称、描述、参数格式)
16
+ <skill_prompts> - SKILL.md 格式技能的完整指令(Prompt 类型技能注入)
16
17
 
17
18
  使用示例:
18
19
  builder = ContextBuilder(memory_manager=mm, skill_registry=sr)
@@ -169,6 +170,7 @@ class ContextBuilder:
169
170
  self._build_user_input(user_typed_text, user_voice_text),
170
171
  self._build_task_plan(task_plan),
171
172
  self._build_tools(self.skill_registry),
173
+ self._build_skill_prompts(self.skill_registry),
172
174
  ]
173
175
 
174
176
  context_body = "\n".join(sections)
@@ -766,6 +768,51 @@ class ContextBuilder:
766
768
  lines.append("</tools>")
767
769
  return "\n".join(lines)
768
770
 
771
+ def _build_skill_prompts(
772
+ self,
773
+ skill_registry: Optional["SkillRegistry"],
774
+ ) -> str:
775
+ """
776
+ 构建 <skill_prompts> 段落 —— SKILL.md 格式技能的完整指令内容。
777
+
778
+ 对于 SKILL.md 目录格式的 Prompt 类型技能,将完整的 markdown body
779
+ 注入到 LLM 上下文中,使 LLM 能按照技能指令执行任务。
780
+ 普通 Python 技能(有 execute 实现)不在此处注入。
781
+
782
+ Returns:
783
+ <skill_prompts> XML 段落字符串,无 Prompt 技能时返回空字符串
784
+ """
785
+ if not skill_registry:
786
+ return ""
787
+
788
+ # 收集所有 markdown 类型且未禁用的技能的 body
789
+ prompt_skills = []
790
+ try:
791
+ for skill in skill_registry._skills.values():
792
+ if skill_registry._is_disabled(skill.name):
793
+ continue
794
+ # 检查是否为 SKILL.md 格式技能
795
+ body = getattr(skill, 'get_body', None)
796
+ if body and callable(body):
797
+ content = body()
798
+ if content:
799
+ prompt_skills.append((skill.name, content))
800
+ except Exception as e:
801
+ logger.warning(f"获取 skill prompts 失败: {e}")
802
+ return ""
803
+
804
+ if not prompt_skills:
805
+ return ""
806
+
807
+ lines: List[str] = ["<skill_prompts>"]
808
+ for skill_name, content in prompt_skills:
809
+ safe_name = _xml_escape(skill_name)
810
+ lines.append(f"<{safe_name}>")
811
+ lines.append(content)
812
+ lines.append(f"</{safe_name}>")
813
+ lines.append("</skill_prompts>")
814
+ return "\n".join(lines)
815
+
769
816
  # =========================================================================
770
817
  # Token 预算管理
771
818
  # =========================================================================
@@ -818,8 +865,8 @@ class ContextBuilder:
818
865
  return re.sub(pattern, replacement, xml, count=1, flags=re.DOTALL)
819
866
 
820
867
  # 按优先级从低到高裁剪(记忆最优先保留,知识库和任务计划优先裁剪)
821
- # recent_summary 是兜底机制,最先裁剪
822
- for tag in ['recent_summary', 'knowledge', 'task_plan', 'recall_memory', 'automemory']:
868
+ # recent_summary 是兜底机制,最先裁剪;skill_prompts 其次
869
+ for tag in ['recent_summary', 'skill_prompts', 'knowledge', 'task_plan', 'recall_memory', 'automemory']:
823
870
  if estimated <= budget:
824
871
  break
825
872
  if f'<{tag}>' in context_xml:
package/core/logger.py CHANGED
@@ -18,6 +18,8 @@ from logging.handlers import RotatingFileHandler, TimedRotatingFileHandler
18
18
  from pathlib import Path
19
19
  from typing import Optional, Dict, Any
20
20
  from datetime import datetime
21
+ from zoneinfo import ZoneInfo
22
+ import time as _time
21
23
 
22
24
 
23
25
  # ANSI 颜色码
@@ -31,8 +33,21 @@ COLORS = {
31
33
  }
32
34
 
33
35
 
36
+ def _tz_converter(timestamp):
37
+ """logging.Formatter.converter — 使用配置时区替代系统时区"""
38
+ try:
39
+ from config import ConfigManager
40
+ tz_name = ConfigManager().get("timezone", "Asia/Shanghai")
41
+ tz = ZoneInfo(tz_name)
42
+ except Exception:
43
+ tz = ZoneInfo("Asia/Shanghai")
44
+ dt = datetime.fromtimestamp(timestamp, tz=tz)
45
+ return dt.timetuple()
46
+
47
+
34
48
  class ColorFormatter(logging.Formatter):
35
49
  """带颜色的控制台日志格式化器"""
50
+ converter = staticmethod(_tz_converter)
36
51
 
37
52
  def format(self, record):
38
53
  color = COLORS.get(record.levelname, COLORS["RESET"])
@@ -42,6 +57,7 @@ class ColorFormatter(logging.Formatter):
42
57
 
43
58
  class FileFormatter(logging.Formatter):
44
59
  """文件日志格式化器(无颜色)"""
60
+ converter = staticmethod(_tz_converter)
45
61
 
46
62
  def format(self, record):
47
63
  # 去除 ANSI 颜色码
@@ -60,8 +76,14 @@ class JsonFormatter(logging.Formatter):
60
76
  """
61
77
 
62
78
  def format(self, record):
79
+ try:
80
+ from config import ConfigManager
81
+ tz_name = ConfigManager().get("timezone", "Asia/Shanghai")
82
+ tz = ZoneInfo(tz_name)
83
+ except Exception:
84
+ tz = ZoneInfo("Asia/Shanghai")
63
85
  log_entry: Dict[str, Any] = {
64
- "timestamp": datetime.utcnow().isoformat() + "Z",
86
+ "timestamp": datetime.now(tz).isoformat(),
65
87
  "level": record.levelname,
66
88
  "logger": record.name,
67
89
  "message": record.getMessage(),
@@ -197,7 +219,12 @@ def setup_logger(
197
219
 
198
220
  else:
199
221
  # 不轮转,按日期命名
200
- log_file = log_path / f"{name}_{datetime.now().strftime('%Y%m%d')}.log"
222
+ try:
223
+ from config import ConfigManager
224
+ _tz = ZoneInfo(ConfigManager().get("timezone", "Asia/Shanghai"))
225
+ except Exception:
226
+ _tz = ZoneInfo("Asia/Shanghai")
227
+ log_file = log_path / f"{name}_{datetime.now(_tz).strftime('%Y%m%d')}.log"
201
228
  fh = logging.FileHandler(log_file, encoding="utf-8")
202
229
  fh.setFormatter(
203
230
  JsonFormatter() if json_format
package/core/utils.py CHANGED
@@ -24,7 +24,9 @@ def get_config_tz() -> ZoneInfo:
24
24
  return _tz_cache
25
25
  try:
26
26
  from config import ConfigManager
27
- tz_name = ConfigManager().get("timezone", "Asia/Shanghai")
27
+ cfg = ConfigManager()
28
+ cfg.load()
29
+ tz_name = cfg.config.timezone
28
30
  _tz_cache = ZoneInfo(tz_name)
29
31
  except Exception:
30
32
  _tz_cache = ZoneInfo("Asia/Shanghai")
File without changes
File without changes
package/install.ps1 CHANGED
@@ -48,8 +48,6 @@ if (-not (Test-Path $VENV_DIR)) {
48
48
  Write-Host "[✓] 虚拟环境已创建。" -ForegroundColor Green
49
49
  }
50
50
 
51
- $PIP = "$VENV_DIR\Scripts\python.exe -m pip"
52
-
53
51
  # 3. 升级 pip
54
52
  Write-Host "[*] 正在升级 pip..." -ForegroundColor Cyan
55
53
  & $VENV_DIR\Scripts\python.exe -m pip install --upgrade pip --quiet
@@ -66,7 +64,17 @@ if ($LASTEXITCODE -ne 0) {
66
64
  }
67
65
  Write-Host "[✓] 核心依赖安装成功。" -ForegroundColor Green
68
66
 
69
- # 5. 安装可选依赖 (失败不影响核心功能)
67
+ # 5. 安装 Windows 桌面依赖 (托盘图标、窗口管理等)
68
+ Write-Host "[*] 正在安装 Windows 桌面依赖..." -ForegroundColor Cyan
69
+ $winPkgs = @("pystray", "pygetwindow")
70
+ & $VENV_DIR\Scripts\python.exe -m pip install $winPkgs
71
+ if ($LASTEXITCODE -eq 0) {
72
+ Write-Host "[✓] Windows 桌面依赖安装成功。" -ForegroundColor Green
73
+ } else {
74
+ Write-Host "[!] Windows 桌面依赖安装失败(系统托盘可能不可用,但不影响核心功能)。" -ForegroundColor Yellow
75
+ }
76
+
77
+ # 6. 安装可选依赖 (语音识别等,失败不影响核心功能)
70
78
  if (Test-Path "requirements-optional.txt") {
71
79
  Write-Host "`n[*] 正在安装可选依赖 (语音识别等)..." -ForegroundColor Cyan
72
80
  Write-Host " (如果编译失败,不影响核心功能)" -ForegroundColor DarkGray
package/main.py CHANGED
@@ -692,7 +692,7 @@ def create_tray_icon(app: MyAgentApp, web_port: int = 8767):
692
692
  from PIL import Image, ImageDraw, ImageFont
693
693
  except Exception:
694
694
  # pystray 可能因缺少 GUI 环境 (X11/Wayland) 或未安装而失败
695
- logger.info("pystray 不可用(未安装或缺少 GUI 环境),跳过系统托盘")
695
+ app.logger.info("pystray 不可用(未安装或缺少 GUI 环境),跳过系统托盘")
696
696
  return None
697
697
 
698
698
  # ── 图标颜色主题 ──
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myagent-ai",
3
- "version": "1.19.4",
3
+ "version": "1.19.6",
4
4
  "description": "本地桌面端执行型AI助手 - Open Interpreter 风格 | Local Desktop Execution-Oriented AI Assistant",
5
5
  "main": "main.py",
6
6
  "bin": {
File without changes
package/requirements.txt CHANGED
@@ -68,6 +68,8 @@ edge-tts>=6.1.0
68
68
  chardet>=5.0.0
69
69
  # xlrd: 旧版 Excel (.xls) 文件读取
70
70
  xlrd>=2.0.0
71
+ # tzdata: Windows 上 ZoneInfo 所需的时区数据
72
+ tzdata>=2024.1
71
73
 
72
74
  # ============================================================
73
75
  # Anthropic Claude (可选)
File without changes
package/skills/base.py CHANGED
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
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -215,6 +215,11 @@ def _load_skill_from_md(skill_dir: Path) -> Optional[Skill]:
215
215
  for ref_file in sorted(ref_dir.glob("*.md")):
216
216
  references.append(ref_file.name)
217
217
 
218
+ # 提取 SKILL.md body(YAML front matter 之后的内容)
219
+ body = ""
220
+ if len(parts) >= 3:
221
+ body = parts[2].strip()
222
+
218
223
  class MarkdownSkill(Skill):
219
224
  """SKILL.md 格式的技能包装器"""
220
225
  def __init__(self):
@@ -230,13 +235,21 @@ def _load_skill_from_md(skill_dir: Path) -> Optional[Skill]:
230
235
  self._references = references
231
236
  self._has_templates = (skill_dir / "templates").exists()
232
237
  self._has_scripts = (skill_dir / "scripts").exists()
238
+ self._body = body
233
239
 
234
240
  async def execute(self, **kwargs) -> SkillResult:
241
+ # SKILL.md 技能的指令已通过 <skill_prompts> 注入 system prompt
242
+ # 如果 LLM 仍然调用了此工具,返回确认信息
235
243
  return SkillResult(
236
- success=False,
237
- error=f"SKILL.md 格式技能不支持直接调用,请通过 LLM 使用 /{self.name} 触发",
244
+ success=True,
245
+ output=f"[{self.name}] 技能指令已激活,请按照 <skill_prompts> {self.name} 的完整指令执行任务。",
246
+ message=f"技能 {self.name} 已激活",
238
247
  )
239
248
 
249
+ def get_body(self) -> str:
250
+ """返回 SKILL.md 的 body 内容"""
251
+ return self._body
252
+
240
253
  def to_openclaw_format(self) -> Dict[str, Any]:
241
254
  info = super().to_openclaw_format()
242
255
  info["skill_type"] = "markdown"
File without changes
File without changes
File without changes
package/web/ui/index.html CHANGED
@@ -2,7 +2,7 @@
2
2
  <html lang="zh-CN">
3
3
  <head>
4
4
  <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
6
6
  <title>MyAgent 管理后台</title>
7
7
  <style>
8
8
  *{margin:0;padding:0;box-sizing:border-box}
@@ -34,7 +34,7 @@
34
34
  --ok:#22c55e;--success:#22c55e;--warn:#f59e0b;--danger:#ef4444;--info:#3b82f6;
35
35
  --border:#2e3347;--border-light:#232733;
36
36
  }
37
- body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;background:var(--bg);color:var(--text);height:100vh;display:flex}
37
+ body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;background:var(--bg);color:var(--text);height:100vh;height:100dvh;display:flex}
38
38
  .sidebar{width:220px;background:var(--surface);border-right:1px solid var(--border);display:flex;flex-direction:column;flex-shrink:0}
39
39
  .sidebar .logo{padding:20px;font-size:18px;font-weight:700;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:8px}
40
40
  .sidebar .logo span{color:var(--primary)}
@@ -178,7 +178,7 @@ tr:hover{background:var(--surface2)}
178
178
  /* ── Mobile Responsive ── */
179
179
  @media(max-width:768px){
180
180
  /* [v1.18.10] 移动端侧边栏:收起时与内容同平面(flex行布局),展开时才浮层覆盖 */
181
- body{flex-direction:row;height:100vh;overflow:hidden}
181
+ body{flex-direction:row;height:100vh;height:100dvh;overflow:hidden}
182
182
  /* 默认收起状态:同一平面,56px窄栏,不浮动,不溢出 */
183
183
  .sidebar{position:relative;left:auto;top:auto;height:auto;width:56px;z-index:auto;flex-shrink:0;transition:width .3s ease;overflow:hidden}
184
184
  .sidebar .nav-item{justify-content:center;padding:10px 0;position:relative}
@@ -210,7 +210,7 @@ tr:hover{background:var(--surface2)}
210
210
  .main{flex:1;min-width:0;min-height:0;overflow:hidden}
211
211
  .header{padding:12px 16px;padding-left:12px}
212
212
  .header h2{font-size:16px}
213
- .content{padding:16px}
213
+ .content{padding:16px;padding-bottom:max(16px,env(safe-area-inset-bottom))}
214
214
  .grid{grid-template-columns:1fr}
215
215
  .form-row{grid-template-columns:1fr}
216
216
  .table-wrap{overflow-x:auto;-webkit-overflow-scrolling:touch}