myagent-ai 1.0.0

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 (486) hide show
  1. package/Dockerfile +30 -0
  2. package/README.md +333 -0
  3. package/agents/__init__.py +6 -0
  4. package/agents/__pycache__/main_agent.cpython-312.pyc +0 -0
  5. package/agents/base.py +115 -0
  6. package/agents/main_agent.py +695 -0
  7. package/agents/memory_agent.py +313 -0
  8. package/agents/tool_agent.py +248 -0
  9. package/chatbot/__init__.py +5 -0
  10. package/chatbot/base.py +124 -0
  11. package/chatbot/discord_bot.py +146 -0
  12. package/chatbot/feishu_bot.py +548 -0
  13. package/chatbot/manager.py +164 -0
  14. package/chatbot/qq_bot.py +189 -0
  15. package/chatbot/telegram_bot.py +167 -0
  16. package/chatbot/wechat_bot.py +558 -0
  17. package/communication/__init__.py +66 -0
  18. package/communication/channel.py +576 -0
  19. package/communication/crypto.py +347 -0
  20. package/communication/manager.py +397 -0
  21. package/communication/peer.py +156 -0
  22. package/config.py +464 -0
  23. package/core/__init__.py +10 -0
  24. package/core/config_broadcast.py +276 -0
  25. package/core/llm.py +878 -0
  26. package/core/logger.py +241 -0
  27. package/core/task_queue.py +362 -0
  28. package/core/utils.py +184 -0
  29. package/executor/__init__.py +4 -0
  30. package/executor/__pycache__/engine.cpython-312.pyc +0 -0
  31. package/executor/engine.py +1215 -0
  32. package/groups/__init__.py +15 -0
  33. package/groups/manager.py +724 -0
  34. package/knowledge/__init__.py +4 -0
  35. package/knowledge/rag.py +444 -0
  36. package/main.py +801 -0
  37. package/memory/__init__.py +4 -0
  38. package/memory/manager.py +840 -0
  39. package/organization/__init__.py +4 -0
  40. package/organization/manager.py +350 -0
  41. package/package.json +58 -0
  42. package/requirements.txt +59 -0
  43. package/setup.py +40 -0
  44. package/skills/ASR/LICENSE.txt +21 -0
  45. package/skills/ASR/SKILL.md +580 -0
  46. package/skills/ASR/scripts/asr.ts +27 -0
  47. package/skills/LLM/LICENSE.txt +21 -0
  48. package/skills/LLM/SKILL.md +856 -0
  49. package/skills/LLM/scripts/chat.ts +32 -0
  50. package/skills/TTS/LICENSE.txt +21 -0
  51. package/skills/TTS/SKILL.md +735 -0
  52. package/skills/TTS/tts.ts +25 -0
  53. package/skills/VLM/LICENSE.txt +21 -0
  54. package/skills/VLM/SKILL.md +588 -0
  55. package/skills/VLM/scripts/vlm.ts +57 -0
  56. package/skills/__init__.py +5 -0
  57. package/skills/agent-browser/SKILL.md +328 -0
  58. package/skills/ai-news-collectors/SKILL.md +157 -0
  59. package/skills/ai-news-collectors/_meta.json +6 -0
  60. package/skills/ai-news-collectors/references/sources.md +128 -0
  61. package/skills/aminer-open-academic/SKILL.md +312 -0
  62. package/skills/aminer-open-academic/_meta.json +6 -0
  63. package/skills/aminer-open-academic/evals/evals.json +46 -0
  64. package/skills/aminer-open-academic/references/api-catalog.md +1032 -0
  65. package/skills/aminer-open-academic/scripts/__pycache__/aminer_client.cpython-312.pyc +0 -0
  66. package/skills/aminer-open-academic/scripts/aminer_client.py +875 -0
  67. package/skills/auto-target-tracker/SKILL.md +317 -0
  68. package/skills/base.py +147 -0
  69. package/skills/blog-writer/2024-02-17-radical-transparency-sales.md +35 -0
  70. package/skills/blog-writer/2024-02-17-raycast-spotlight-superpowers.md +33 -0
  71. package/skills/blog-writer/2024-02-17-short-form-content-marketing.md +47 -0
  72. package/skills/blog-writer/2024-02-17-typing-speed-benefits.md +33 -0
  73. package/skills/blog-writer/2024-03-14-effective-ai-prompts.md +55 -0
  74. package/skills/blog-writer/2024-11-08-ai-revolutionizing-entry-level-sales.md +43 -0
  75. package/skills/blog-writer/2025-11-12-why-ai-art-is-useless.md +49 -0
  76. package/skills/blog-writer/README.md +2 -0
  77. package/skills/blog-writer/SKILL.md +158 -0
  78. package/skills/blog-writer/__pycache__/manage_examples.cpython-312.pyc +0 -0
  79. package/skills/blog-writer/_meta.json +6 -0
  80. package/skills/blog-writer/manage_examples.py +90 -0
  81. package/skills/blog-writer/style-guide.md +160 -0
  82. package/skills/browser_skill.py +146 -0
  83. package/skills/coding-agent/SKILL.md +120 -0
  84. package/skills/coding-agent/_meta.json +6 -0
  85. package/skills/coding-agent/criteria.md +48 -0
  86. package/skills/coding-agent/execution.md +42 -0
  87. package/skills/coding-agent/memory-template.md +38 -0
  88. package/skills/coding-agent/planning.md +31 -0
  89. package/skills/coding-agent/state.md +60 -0
  90. package/skills/coding-agent/verification.md +39 -0
  91. package/skills/content-strategy/SKILL.md +181 -0
  92. package/skills/content-strategy/_meta.json +6 -0
  93. package/skills/contentanalysis/ExtractWisdom/SKILL.md +229 -0
  94. package/skills/contentanalysis/ExtractWisdom/Workflows/Extract.md +60 -0
  95. package/skills/contentanalysis/SKILL.md +14 -0
  96. package/skills/docx/CHANGELOG.md +85 -0
  97. package/skills/docx/LICENSE.txt +30 -0
  98. package/skills/docx/SKILL.md +455 -0
  99. package/skills/docx/docx-js.md +681 -0
  100. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
  101. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
  102. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
  103. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
  104. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
  105. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
  106. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
  107. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
  108. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
  109. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
  110. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
  111. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
  112. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
  113. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
  114. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
  115. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
  116. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
  117. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
  118. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
  119. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
  120. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
  121. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
  122. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
  123. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
  124. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
  125. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
  126. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
  127. package/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
  128. package/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
  129. package/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
  130. package/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
  131. package/skills/docx/ooxml/schemas/mce/mc.xsd +75 -0
  132. package/skills/docx/ooxml/schemas/microsoft/wml-2010.xsd +560 -0
  133. package/skills/docx/ooxml/schemas/microsoft/wml-2012.xsd +67 -0
  134. package/skills/docx/ooxml/schemas/microsoft/wml-2018.xsd +14 -0
  135. package/skills/docx/ooxml/schemas/microsoft/wml-cex-2018.xsd +20 -0
  136. package/skills/docx/ooxml/schemas/microsoft/wml-cid-2016.xsd +13 -0
  137. package/skills/docx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
  138. package/skills/docx/ooxml/schemas/microsoft/wml-symex-2015.xsd +8 -0
  139. package/skills/docx/ooxml/scripts/__pycache__/pack.cpython-312.pyc +0 -0
  140. package/skills/docx/ooxml/scripts/__pycache__/unpack.cpython-312.pyc +0 -0
  141. package/skills/docx/ooxml/scripts/__pycache__/validate.cpython-312.pyc +0 -0
  142. package/skills/docx/ooxml/scripts/pack.py +159 -0
  143. package/skills/docx/ooxml/scripts/unpack.py +29 -0
  144. package/skills/docx/ooxml/scripts/validate.py +69 -0
  145. package/skills/docx/ooxml/scripts/validation/__init__.py +15 -0
  146. package/skills/docx/ooxml/scripts/validation/__pycache__/__init__.cpython-312.pyc +0 -0
  147. package/skills/docx/ooxml/scripts/validation/__pycache__/base.cpython-312.pyc +0 -0
  148. package/skills/docx/ooxml/scripts/validation/__pycache__/docx.cpython-312.pyc +0 -0
  149. package/skills/docx/ooxml/scripts/validation/__pycache__/pptx.cpython-312.pyc +0 -0
  150. package/skills/docx/ooxml/scripts/validation/__pycache__/redlining.cpython-312.pyc +0 -0
  151. package/skills/docx/ooxml/scripts/validation/base.py +951 -0
  152. package/skills/docx/ooxml/scripts/validation/docx.py +274 -0
  153. package/skills/docx/ooxml/scripts/validation/pptx.py +315 -0
  154. package/skills/docx/ooxml/scripts/validation/redlining.py +279 -0
  155. package/skills/docx/ooxml.md +615 -0
  156. package/skills/docx/scripts/__init__.py +1 -0
  157. package/skills/docx/scripts/__pycache__/__init__.cpython-312.pyc +0 -0
  158. package/skills/docx/scripts/__pycache__/add_toc_placeholders.cpython-312.pyc +0 -0
  159. package/skills/docx/scripts/__pycache__/document.cpython-312.pyc +0 -0
  160. package/skills/docx/scripts/__pycache__/utilities.cpython-312.pyc +0 -0
  161. package/skills/docx/scripts/add_toc_placeholders.py +220 -0
  162. package/skills/docx/scripts/document.py +1302 -0
  163. package/skills/docx/scripts/templates/comments.xml +3 -0
  164. package/skills/docx/scripts/templates/commentsExtended.xml +3 -0
  165. package/skills/docx/scripts/templates/commentsExtensible.xml +3 -0
  166. package/skills/docx/scripts/templates/commentsIds.xml +3 -0
  167. package/skills/docx/scripts/templates/people.xml +3 -0
  168. package/skills/docx/scripts/utilities.py +374 -0
  169. package/skills/dream-interpreter/SKILL.md +88 -0
  170. package/skills/dream-interpreter/assets/example_asset.txt +24 -0
  171. package/skills/dream-interpreter/references/api_reference.md +34 -0
  172. package/skills/dream-interpreter/references/interpretation-guide.md +83 -0
  173. package/skills/dream-interpreter/references/output-schema.md +65 -0
  174. package/skills/dream-interpreter/references/questioning-strategy.md +62 -0
  175. package/skills/dream-interpreter/references/visual-mapping.md +81 -0
  176. package/skills/dream-interpreter/scripts/__pycache__/example.cpython-312.pyc +0 -0
  177. package/skills/dream-interpreter/scripts/example.py +19 -0
  178. package/skills/dream-interpreter/skill.json +7 -0
  179. package/skills/file_skill.py +246 -0
  180. package/skills/finance/Finance_API_Doc.md +445 -0
  181. package/skills/finance/SKILL.md +53 -0
  182. package/skills/fullstack-dev/SKILL.md +205 -0
  183. package/skills/get-fortune-analysis/SKILL.md +370 -0
  184. package/skills/get-fortune-analysis/lunar_python.py +91 -0
  185. package/skills/gift-evaluator/SKILL.md +83 -0
  186. package/skills/gift-evaluator/__pycache__/html_tools.cpython-312.pyc +0 -0
  187. package/skills/gift-evaluator/html_tools.py +268 -0
  188. package/skills/image-edit/LICENSE.txt +21 -0
  189. package/skills/image-edit/SKILL.md +896 -0
  190. package/skills/image-edit/scripts/image-edit.ts +36 -0
  191. package/skills/image-generation/LICENSE.txt +21 -0
  192. package/skills/image-generation/SKILL.md +583 -0
  193. package/skills/image-generation/scripts/image-generation.ts +28 -0
  194. package/skills/image-understand/LICENSE.txt +21 -0
  195. package/skills/image-understand/SKILL.md +855 -0
  196. package/skills/image-understand/scripts/image-understand.ts +41 -0
  197. package/skills/interview-designer/README.md +70 -0
  198. package/skills/interview-designer/SKILL.md +53 -0
  199. package/skills/interview-designer/_meta.json +6 -0
  200. package/skills/interview-designer/references/design_rationale.md +43 -0
  201. package/skills/interview-designer/templates/interview_guide_template.md +62 -0
  202. package/skills/market-research-reports/SKILL.md +901 -0
  203. package/skills/market-research-reports/assets/FORMATTING_GUIDE.md +428 -0
  204. package/skills/market-research-reports/assets/market_report_template.tex +1380 -0
  205. package/skills/market-research-reports/assets/market_research.sty +564 -0
  206. package/skills/market-research-reports/references/data_analysis_patterns.md +548 -0
  207. package/skills/market-research-reports/references/report_structure_guide.md +999 -0
  208. package/skills/market-research-reports/references/visual_generation_guide.md +1077 -0
  209. package/skills/market-research-reports/scripts/__pycache__/generate_market_visuals.cpython-312.pyc +0 -0
  210. package/skills/market-research-reports/scripts/generate_market_visuals.py +529 -0
  211. package/skills/marketing-mode/README.md +49 -0
  212. package/skills/marketing-mode/SKILL.md +693 -0
  213. package/skills/marketing-mode/_meta.json +6 -0
  214. package/skills/marketing-mode/mode-prompt.md +39 -0
  215. package/skills/marketing-mode/skill.json +51 -0
  216. package/skills/mindfulness-meditation/SKILL.md +65 -0
  217. package/skills/mindfulness-meditation/_meta.json +6 -0
  218. package/skills/multi-search-engine/CHANGELOG.md +15 -0
  219. package/skills/multi-search-engine/CHANNELLOG.md +48 -0
  220. package/skills/multi-search-engine/SKILL.md +78 -0
  221. package/skills/multi-search-engine/_meta.json +6 -0
  222. package/skills/multi-search-engine/config.json +14 -0
  223. package/skills/multi-search-engine/metadata.json +7 -0
  224. package/skills/multi-search-engine/references/international-search.md +651 -0
  225. package/skills/pdf/LICENSE.txt +30 -0
  226. package/skills/pdf/SKILL.md +1534 -0
  227. package/skills/pdf/forms.md +205 -0
  228. package/skills/pdf/reference.md +765 -0
  229. package/skills/pdf/scripts/__pycache__/add_zai_metadata.cpython-312.pyc +0 -0
  230. package/skills/pdf/scripts/__pycache__/check_bounding_boxes.cpython-312.pyc +0 -0
  231. package/skills/pdf/scripts/__pycache__/check_bounding_boxes_test.cpython-312.pyc +0 -0
  232. package/skills/pdf/scripts/__pycache__/check_fillable_fields.cpython-312.pyc +0 -0
  233. package/skills/pdf/scripts/__pycache__/convert_pdf_to_images.cpython-312.pyc +0 -0
  234. package/skills/pdf/scripts/__pycache__/create_validation_image.cpython-312.pyc +0 -0
  235. package/skills/pdf/scripts/__pycache__/extract_form_field_info.cpython-312.pyc +0 -0
  236. package/skills/pdf/scripts/__pycache__/fill_fillable_fields.cpython-312.pyc +0 -0
  237. package/skills/pdf/scripts/__pycache__/fill_pdf_form_with_annotations.cpython-312.pyc +0 -0
  238. package/skills/pdf/scripts/__pycache__/sanitize_code.cpython-312.pyc +0 -0
  239. package/skills/pdf/scripts/add_zai_metadata.py +172 -0
  240. package/skills/pdf/scripts/check_bounding_boxes.py +70 -0
  241. package/skills/pdf/scripts/check_bounding_boxes_test.py +226 -0
  242. package/skills/pdf/scripts/check_fillable_fields.py +12 -0
  243. package/skills/pdf/scripts/convert_pdf_to_images.py +35 -0
  244. package/skills/pdf/scripts/create_validation_image.py +41 -0
  245. package/skills/pdf/scripts/extract_form_field_info.py +152 -0
  246. package/skills/pdf/scripts/fill_fillable_fields.py +114 -0
  247. package/skills/pdf/scripts/fill_pdf_form_with_annotations.py +108 -0
  248. package/skills/pdf/scripts/sanitize_code.py +110 -0
  249. package/skills/podcast-generate/LICENSE.txt +21 -0
  250. package/skills/podcast-generate/SKILL.md +198 -0
  251. package/skills/podcast-generate/generate.ts +661 -0
  252. package/skills/podcast-generate/package.json +30 -0
  253. package/skills/podcast-generate/readme.md +177 -0
  254. package/skills/podcast-generate/test_data/segments.jsonl +3 -0
  255. package/skills/podcast-generate/tsconfig.json +26 -0
  256. package/skills/pptx/LICENSE.txt +30 -0
  257. package/skills/pptx/SKILL.md +507 -0
  258. package/skills/pptx/html2pptx.md +625 -0
  259. package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
  260. package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
  261. package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
  262. package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
  263. package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
  264. package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
  265. package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
  266. package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
  267. package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
  268. package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
  269. package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
  270. package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
  271. package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
  272. package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
  273. package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
  274. package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
  275. package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
  276. package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
  277. package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
  278. package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
  279. package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
  280. package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
  281. package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
  282. package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
  283. package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
  284. package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
  285. package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
  286. package/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
  287. package/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
  288. package/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
  289. package/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
  290. package/skills/pptx/ooxml/schemas/mce/mc.xsd +75 -0
  291. package/skills/pptx/ooxml/schemas/microsoft/wml-2010.xsd +560 -0
  292. package/skills/pptx/ooxml/schemas/microsoft/wml-2012.xsd +67 -0
  293. package/skills/pptx/ooxml/schemas/microsoft/wml-2018.xsd +14 -0
  294. package/skills/pptx/ooxml/schemas/microsoft/wml-cex-2018.xsd +20 -0
  295. package/skills/pptx/ooxml/schemas/microsoft/wml-cid-2016.xsd +13 -0
  296. package/skills/pptx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
  297. package/skills/pptx/ooxml/schemas/microsoft/wml-symex-2015.xsd +8 -0
  298. package/skills/pptx/ooxml/scripts/__pycache__/pack.cpython-312.pyc +0 -0
  299. package/skills/pptx/ooxml/scripts/__pycache__/unpack.cpython-312.pyc +0 -0
  300. package/skills/pptx/ooxml/scripts/__pycache__/validate.cpython-312.pyc +0 -0
  301. package/skills/pptx/ooxml/scripts/pack.py +159 -0
  302. package/skills/pptx/ooxml/scripts/unpack.py +29 -0
  303. package/skills/pptx/ooxml/scripts/validate.py +69 -0
  304. package/skills/pptx/ooxml/scripts/validation/__init__.py +15 -0
  305. package/skills/pptx/ooxml/scripts/validation/__pycache__/__init__.cpython-312.pyc +0 -0
  306. package/skills/pptx/ooxml/scripts/validation/__pycache__/base.cpython-312.pyc +0 -0
  307. package/skills/pptx/ooxml/scripts/validation/__pycache__/docx.cpython-312.pyc +0 -0
  308. package/skills/pptx/ooxml/scripts/validation/__pycache__/pptx.cpython-312.pyc +0 -0
  309. package/skills/pptx/ooxml/scripts/validation/__pycache__/redlining.cpython-312.pyc +0 -0
  310. package/skills/pptx/ooxml/scripts/validation/base.py +951 -0
  311. package/skills/pptx/ooxml/scripts/validation/docx.py +274 -0
  312. package/skills/pptx/ooxml/scripts/validation/pptx.py +315 -0
  313. package/skills/pptx/ooxml/scripts/validation/redlining.py +279 -0
  314. package/skills/pptx/ooxml.md +427 -0
  315. package/skills/pptx/scripts/__pycache__/inventory.cpython-312.pyc +0 -0
  316. package/skills/pptx/scripts/__pycache__/inventory.cpython-313.pyc +0 -0
  317. package/skills/pptx/scripts/__pycache__/rearrange.cpython-312.pyc +0 -0
  318. package/skills/pptx/scripts/__pycache__/replace.cpython-312.pyc +0 -0
  319. package/skills/pptx/scripts/__pycache__/thumbnail.cpython-312.pyc +0 -0
  320. package/skills/pptx/scripts/html2pptx.js +1044 -0
  321. package/skills/pptx/scripts/inventory.py +1020 -0
  322. package/skills/pptx/scripts/rearrange.py +231 -0
  323. package/skills/pptx/scripts/replace.py +385 -0
  324. package/skills/pptx/scripts/thumbnail.py +450 -0
  325. package/skills/qingyan-research/SKILL.md +294 -0
  326. package/skills/qingyan-research/__pycache__/generate_html.cpython-312.pyc +0 -0
  327. package/skills/qingyan-research/generate_html.py +33 -0
  328. package/skills/registry.py +344 -0
  329. package/skills/search_skill.py +228 -0
  330. package/skills/seo-content-writer/SKILL.md +661 -0
  331. package/skills/seo-content-writer/_meta.json +6 -0
  332. package/skills/seo-content-writer/references/content-structure-templates.md +875 -0
  333. package/skills/seo-content-writer/references/title-formulas.md +339 -0
  334. package/skills/skill-creator/LICENSE.txt +202 -0
  335. package/skills/skill-creator/SKILL.md +485 -0
  336. package/skills/skill-creator/agents/analyzer.md +274 -0
  337. package/skills/skill-creator/agents/comparator.md +202 -0
  338. package/skills/skill-creator/agents/grader.md +223 -0
  339. package/skills/skill-creator/assets/eval_review.html +146 -0
  340. package/skills/skill-creator/eval-viewer/__pycache__/generate_review.cpython-312.pyc +0 -0
  341. package/skills/skill-creator/eval-viewer/generate_review.py +471 -0
  342. package/skills/skill-creator/eval-viewer/viewer.html +1325 -0
  343. package/skills/skill-creator/references/schemas.md +430 -0
  344. package/skills/skill-creator/scripts/__init__.py +0 -0
  345. package/skills/skill-creator/scripts/__pycache__/__init__.cpython-312.pyc +0 -0
  346. package/skills/skill-creator/scripts/__pycache__/aggregate_benchmark.cpython-312.pyc +0 -0
  347. package/skills/skill-creator/scripts/__pycache__/generate_report.cpython-312.pyc +0 -0
  348. package/skills/skill-creator/scripts/__pycache__/improve_description.cpython-312.pyc +0 -0
  349. package/skills/skill-creator/scripts/__pycache__/package_skill.cpython-312.pyc +0 -0
  350. package/skills/skill-creator/scripts/__pycache__/quick_validate.cpython-312.pyc +0 -0
  351. package/skills/skill-creator/scripts/__pycache__/run_eval.cpython-312.pyc +0 -0
  352. package/skills/skill-creator/scripts/__pycache__/run_loop.cpython-312.pyc +0 -0
  353. package/skills/skill-creator/scripts/__pycache__/utils.cpython-312.pyc +0 -0
  354. package/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
  355. package/skills/skill-creator/scripts/generate_report.py +326 -0
  356. package/skills/skill-creator/scripts/improve_description.py +236 -0
  357. package/skills/skill-creator/scripts/package_skill.py +136 -0
  358. package/skills/skill-creator/scripts/quick_validate.py +103 -0
  359. package/skills/skill-creator/scripts/run_eval.py +310 -0
  360. package/skills/skill-creator/scripts/run_loop.py +328 -0
  361. package/skills/skill-creator/scripts/utils.py +47 -0
  362. package/skills/skill-finder-cn/SKILL.md +66 -0
  363. package/skills/skill-finder-cn/_meta.json +6 -0
  364. package/skills/skill-finder-cn/package.json +5 -0
  365. package/skills/skill-finder-cn/scripts/search.sh +15 -0
  366. package/skills/skill-vetter/SKILL.md +137 -0
  367. package/skills/stock-analysis-skill/SKILL.md +156 -0
  368. package/skills/stock-analysis-skill/package.json +21 -0
  369. package/skills/stock-analysis-skill/src/analyzer.ts +264 -0
  370. package/skills/stock-analysis-skill/src/dataFetcher.ts +130 -0
  371. package/skills/stock-analysis-skill/src/dividend.ts +226 -0
  372. package/skills/stock-analysis-skill/src/index.ts +327 -0
  373. package/skills/stock-analysis-skill/src/rumorScanner.ts +200 -0
  374. package/skills/stock-analysis-skill/src/types.ts +167 -0
  375. package/skills/stock-analysis-skill/src/watchlist.ts +292 -0
  376. package/skills/stock-analysis-skill/tsconfig.json +15 -0
  377. package/skills/storyboard-manager/SKILL.md +532 -0
  378. package/skills/storyboard-manager/index.js +9 -0
  379. package/skills/storyboard-manager/package.json +11 -0
  380. package/skills/storyboard-manager/references/character_development.md +232 -0
  381. package/skills/storyboard-manager/references/story_structures.md +148 -0
  382. package/skills/storyboard-manager/scripts/__pycache__/consistency_checker.cpython-312.pyc +0 -0
  383. package/skills/storyboard-manager/scripts/__pycache__/timeline_tracker.cpython-312.pyc +0 -0
  384. package/skills/storyboard-manager/scripts/consistency_checker.py +391 -0
  385. package/skills/storyboard-manager/scripts/timeline_tracker.py +352 -0
  386. package/skills/system_skill.py +249 -0
  387. package/skills/ui-ux-pro-max/SKILL.md +43 -0
  388. package/skills/ui-ux-pro-max/_meta.json +6 -0
  389. package/skills/ui-ux-pro-max/assets/data/charts.csv +26 -0
  390. package/skills/ui-ux-pro-max/assets/data/colors.csv +97 -0
  391. package/skills/ui-ux-pro-max/assets/data/icons.csv +101 -0
  392. package/skills/ui-ux-pro-max/assets/data/landing.csv +31 -0
  393. package/skills/ui-ux-pro-max/assets/data/products.csv +97 -0
  394. package/skills/ui-ux-pro-max/assets/data/react-performance.csv +45 -0
  395. package/skills/ui-ux-pro-max/assets/data/stacks/astro.csv +54 -0
  396. package/skills/ui-ux-pro-max/assets/data/stacks/flutter.csv +53 -0
  397. package/skills/ui-ux-pro-max/assets/data/stacks/html-tailwind.csv +56 -0
  398. package/skills/ui-ux-pro-max/assets/data/stacks/jetpack-compose.csv +53 -0
  399. package/skills/ui-ux-pro-max/assets/data/stacks/nextjs.csv +53 -0
  400. package/skills/ui-ux-pro-max/assets/data/stacks/nuxt-ui.csv +51 -0
  401. package/skills/ui-ux-pro-max/assets/data/stacks/nuxtjs.csv +59 -0
  402. package/skills/ui-ux-pro-max/assets/data/stacks/react-native.csv +52 -0
  403. package/skills/ui-ux-pro-max/assets/data/stacks/react.csv +54 -0
  404. package/skills/ui-ux-pro-max/assets/data/stacks/shadcn.csv +61 -0
  405. package/skills/ui-ux-pro-max/assets/data/stacks/svelte.csv +54 -0
  406. package/skills/ui-ux-pro-max/assets/data/stacks/swiftui.csv +51 -0
  407. package/skills/ui-ux-pro-max/assets/data/stacks/vue.csv +50 -0
  408. package/skills/ui-ux-pro-max/assets/data/styles.csv +68 -0
  409. package/skills/ui-ux-pro-max/assets/data/typography.csv +58 -0
  410. package/skills/ui-ux-pro-max/assets/data/ui-reasoning.csv +101 -0
  411. package/skills/ui-ux-pro-max/assets/data/ux-guidelines.csv +100 -0
  412. package/skills/ui-ux-pro-max/assets/data/web-interface.csv +31 -0
  413. package/skills/ui-ux-pro-max/data/charts.csv +26 -0
  414. package/skills/ui-ux-pro-max/data/colors.csv +97 -0
  415. package/skills/ui-ux-pro-max/data/icons.csv +101 -0
  416. package/skills/ui-ux-pro-max/data/landing.csv +31 -0
  417. package/skills/ui-ux-pro-max/data/products.csv +97 -0
  418. package/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
  419. package/skills/ui-ux-pro-max/data/stacks/astro.csv +54 -0
  420. package/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
  421. package/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
  422. package/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
  423. package/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
  424. package/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
  425. package/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
  426. package/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
  427. package/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
  428. package/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
  429. package/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
  430. package/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
  431. package/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
  432. package/skills/ui-ux-pro-max/data/styles.csv +68 -0
  433. package/skills/ui-ux-pro-max/data/typography.csv +58 -0
  434. package/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
  435. package/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
  436. package/skills/ui-ux-pro-max/data/web-interface.csv +31 -0
  437. package/skills/ui-ux-pro-max/references/upstream-README.md +488 -0
  438. package/skills/ui-ux-pro-max/references/upstream-skill-content.md +288 -0
  439. package/skills/ui-ux-pro-max/scripts/__init__.py +0 -0
  440. package/skills/ui-ux-pro-max/scripts/__pycache__/__init__.cpython-312.pyc +0 -0
  441. package/skills/ui-ux-pro-max/scripts/__pycache__/core.cpython-312.pyc +0 -0
  442. package/skills/ui-ux-pro-max/scripts/__pycache__/design_system.cpython-312.pyc +0 -0
  443. package/skills/ui-ux-pro-max/scripts/__pycache__/search.cpython-312.pyc +0 -0
  444. package/skills/ui-ux-pro-max/scripts/core.py +253 -0
  445. package/skills/ui-ux-pro-max/scripts/design_system.py +1071 -0
  446. package/skills/ui-ux-pro-max/scripts/search.py +111 -0
  447. package/skills/video-generation/LICENSE.txt +21 -0
  448. package/skills/video-generation/SKILL.md +1082 -0
  449. package/skills/video-generation/scripts/video.ts +168 -0
  450. package/skills/video-understand/LICENSE.txt +21 -0
  451. package/skills/video-understand/SKILL.md +916 -0
  452. package/skills/video-understand/scripts/video-understand.ts +41 -0
  453. package/skills/visual-design-foundations/SKILL.md +318 -0
  454. package/skills/visual-design-foundations/references/color-systems.md +417 -0
  455. package/skills/visual-design-foundations/references/spacing-iconography.md +425 -0
  456. package/skills/visual-design-foundations/references/typography-systems.md +432 -0
  457. package/skills/web-reader/LICENSE.txt +21 -0
  458. package/skills/web-reader/SKILL.md +1140 -0
  459. package/skills/web-reader/scripts/web-reader.ts +37 -0
  460. package/skills/web-search/LICENSE.txt +21 -0
  461. package/skills/web-search/SKILL.md +912 -0
  462. package/skills/web-search/scripts/web_search.ts +44 -0
  463. package/skills/web-shader-extractor/SKILL.md +145 -0
  464. package/skills/web-shader-extractor/references/config-extraction.md +50 -0
  465. package/skills/web-shader-extractor/references/encoded-definitions.md +53 -0
  466. package/skills/web-shader-extractor/references/extraction-workflow.md +61 -0
  467. package/skills/web-shader-extractor/references/porting-strategy.md +164 -0
  468. package/skills/web-shader-extractor/references/shader-injection.md +126 -0
  469. package/skills/web-shader-extractor/references/shaders-com.md +190 -0
  470. package/skills/web-shader-extractor/references/tech-signatures.md +54 -0
  471. package/skills/web-shader-extractor/references/tsl-extraction.md +41 -0
  472. package/skills/web-shader-extractor/references/unicorn-studio.md +353 -0
  473. package/skills/web-shader-extractor/scripts/fetch-rendered-dom.mjs +153 -0
  474. package/skills/web-shader-extractor/scripts/scan-bundle.sh +76 -0
  475. package/skills/writing-plans/SKILL.md +116 -0
  476. package/skills/writing-plans/_meta.json +6 -0
  477. package/skills/xlsx/LICENSE.txt +30 -0
  478. package/skills/xlsx/SKILL.md +496 -0
  479. package/skills/xlsx/__pycache__/recalc.cpython-312.pyc +0 -0
  480. package/skills/xlsx/recalc.py +178 -0
  481. package/start.sh +36 -0
  482. package/web/__init__.py +1 -0
  483. package/web/__pycache__/api_server.cpython-312.pyc +0 -0
  484. package/web/api_server.py +2043 -0
  485. package/web/ui/chat.html +3235 -0
  486. package/web/ui/index.html +458 -0
@@ -0,0 +1,1215 @@
1
+ """
2
+ executor/engine.py - 执行引擎
3
+ ================================
4
+ Open Interpreter 风格的本地代码执行引擎。
5
+ 支持 Python / Shell (Bash) / PowerShell / 系统命令。
6
+ 特性:
7
+ - 安全沙箱执行(超时控制、命令黑名单/正则)
8
+ - 自动错误捕获与修复(12 种 Python 模式 + 4 种 Shell 模式)
9
+ - 结构化结果返回
10
+ - 跨平台兼容 (Windows/macOS/Linux)
11
+ - 启动时缓存 PATH 与 Shell 检测
12
+ """
13
+ from __future__ import annotations
14
+
15
+ import os
16
+ import re
17
+ import sys
18
+ import shutil
19
+ import subprocess
20
+ import tempfile
21
+ import textwrap
22
+ import traceback
23
+ import asyncio
24
+ import signal
25
+ import platform
26
+ from pathlib import Path
27
+ from dataclasses import dataclass, field
28
+ from typing import Any, Dict, List, Optional, Tuple
29
+
30
+ from core.logger import get_logger
31
+ from core.utils import timestamp, truncate_str, detect_platform
32
+
33
+ logger = get_logger("myagent.executor")
34
+
35
+
36
+ # ==============================================================================
37
+ # 执行结果
38
+ # ==============================================================================
39
+
40
+ @dataclass
41
+ class ExecResult:
42
+ """执行结果(结构化)"""
43
+ success: bool = False
44
+ stdout: str = ""
45
+ stderr: str = ""
46
+ exit_code: int = -1
47
+ error: str = ""
48
+ execution_time: float = 0.0
49
+ language: str = ""
50
+ code: str = ""
51
+ metadata: Dict[str, Any] = field(default_factory=dict)
52
+ # 超时追踪字段
53
+ timed_out: bool = False # 是否因超时被终止
54
+ timeout_limit: int = 0 # LLM 预估的超时上限(秒),0 表示未设置
55
+
56
+ def to_dict(self) -> dict:
57
+ result = {
58
+ "success": self.success,
59
+ "stdout": truncate_str(self.stdout, 30000),
60
+ "stderr": truncate_str(self.stderr, 10000),
61
+ "exit_code": self.exit_code,
62
+ "error": truncate_str(self.error, 5000),
63
+ "execution_time": round(self.execution_time, 3),
64
+ "language": self.language,
65
+ "metadata": self.metadata,
66
+ }
67
+ if self.timed_out:
68
+ result["timed_out"] = True
69
+ result["timeout_limit"] = self.timeout_limit
70
+ return result
71
+
72
+ def to_llm_message(self) -> str:
73
+ """转换为 LLM 可理解的消息格式"""
74
+ if self.success:
75
+ parts = [f"✅ 执行成功 (耗时: {self.execution_time:.2f}s)"]
76
+ if self.stdout.strip():
77
+ parts.append(f"输出:\n```\n{self.stdout.strip()}\n```")
78
+ if self.stderr.strip():
79
+ parts.append(f"标准错误:\n```\n{self.stderr.strip()}\n```")
80
+ return "\n".join(parts)
81
+ elif self.timed_out:
82
+ # 超时专用的消息格式,提供更多诊断信息
83
+ parts = [
84
+ f"⏰ 执行超时!命令在 {self.execution_time:.1f}s 后被强制终止",
85
+ f"超时上限: {self.timeout_limit}s",
86
+ ]
87
+ if self.stdout.strip():
88
+ parts.append(f"超时前已产生的输出:\n```\n{self.stdout.strip()}\n```")
89
+ if self.stderr.strip():
90
+ parts.append(f"标准错误:\n```\n{self.stderr.strip()}\n```")
91
+ parts.append(
92
+ "请分析超时原因。可能的原因包括:\n"
93
+ "1. 命令进入了死循环或无限等待\n"
94
+ "2. 处理的数据量远超预期\n"
95
+ "3. 网络请求等待响应过久\n"
96
+ "4. 缺少必要的输入导致程序阻塞\n"
97
+ "请给出诊断结论和改进建议。"
98
+ )
99
+ return "\n".join(parts)
100
+ else:
101
+ parts = [f"❌ 执行失败 (退出码: {self.exit_code})"]
102
+ if self.error:
103
+ parts.append(f"错误: {self.error}")
104
+ if self.stdout.strip():
105
+ parts.append(f"输出:\n```\n{self.stdout.strip()}\n```")
106
+ if self.stderr.strip():
107
+ parts.append(f"标准错误:\n```\n{self.stderr.strip()}\n```")
108
+ return "\n".join(parts)
109
+
110
+
111
+ # ==============================================================================
112
+ # 执行引擎
113
+ # ==============================================================================
114
+
115
+ class ExecutionEngine:
116
+ """
117
+ 本地代码执行引擎。
118
+
119
+ 支持:
120
+ - python: Python 3 脚本执行
121
+ - shell / bash: Unix Shell 命令
122
+ - powershell: Windows PowerShell
123
+ - cmd: Windows 命令提示符
124
+ - system: 自动检测平台执行系统命令
125
+
126
+ 使用示例:
127
+ engine = ExecutionEngine(timeout=60)
128
+ result = await engine.execute("python", "print('Hello, World!')")
129
+ print(result.to_dict())
130
+ """
131
+
132
+ # ── 危险命令模式(正则 + 词边界) ──────────────────────────────────────────
133
+ DANGEROUS_PATTERNS: List[Tuple[str, str]] = [
134
+ # Unix destructive
135
+ (r'\brm\s+(-[^\s]*r[^\s]*f[^\s]*|-[^\s]*f[^\s]*r[^\s]*)\s+/', 'rm -rf /'),
136
+ (r'\brm\s+(-[^\s]*r[^\s]*f[^\s]*|-[^\s]*f[^\s]*r[^\s]*)\s+/\*', 'rm -rf /*'),
137
+ (r'\bmkfs\b', 'mkfs (格式化磁盘)'),
138
+ (r'\bdd\s+if=\s*/dev/', 'dd (直接磁盘写入)'),
139
+ (r':\s*\(\)\s*\{.*\}\s*;', 'fork bomb'),
140
+ (r'\bfork\s+bomb\b', 'fork bomb'),
141
+ # Windows destructive
142
+ (r'\bformat\s+[A-Za-z]:', 'format (格式化磁盘)'),
143
+ (r'\bdel\s+/[fFsS]\s+/[sS]\s+/[qQ]\s+[A-Za-z]:', 'del /f /s /q (删除系统文件)'),
144
+ # System
145
+ (r'\bshutdown\s+-[hHrR]\s+now\b', 'shutdown -h now'),
146
+ (r'\breboot\b', 'reboot'),
147
+ (r'\binit\s+0\b', 'init 0'),
148
+ (r'\bchmod\s+-R\s+777\s+/\s', 'chmod -R 777 / (开放根目录权限)'),
149
+ (r'\bchown\s+-R\b', 'chown -R (递归更改所有权)'),
150
+ ]
151
+
152
+ # ── 旧式字符串匹配列表(兼容 & 自定义扩展) ──────────────────────────────
153
+ DANGEROUS_COMMANDS = [
154
+ "rm -rf /", "rm -rf /*", "mkfs", "dd if=/dev/zero",
155
+ ":(){ :|:& };:", "fork bomb",
156
+ "format C:", "del /f /s /q C:\\",
157
+ "shutdown -h now", "reboot",
158
+ ]
159
+
160
+ # ── Python 常见拼写错误表 ───────────────────────────────────────────────
161
+ COMMON_MISSPELLINGS: Dict[str, str] = {
162
+ "pritn": "print", "prnit": "print", "pint": "print",
163
+ "improt": "import", "imort": "import",
164
+ "retun": "return", "retrun": "return", "reutrn": "return",
165
+ "defualt": "default", "defautl": "default",
166
+ "ture": "True", "flase": "False", "flase": "False",
167
+ "lenght": "length", "lengh": "length",
168
+ "recieve": "receive", "occured": "occurred",
169
+ "seperator": "separator",
170
+ }
171
+
172
+ # ── Shell 命令别名表 ────────────────────────────────────────────────────
173
+ SHELL_COMMAND_ALIASES: Dict[str, List[str]] = {
174
+ "ls": ["dir"], # Windows: dir instead of ls
175
+ "dir": ["ls"], # Unix: ls instead of dir
176
+ "cat": ["type"], # Windows: type instead of cat
177
+ "type": ["cat"],
178
+ "copy": ["cp"], # Windows: copy instead of cp
179
+ "cp": ["copy"],
180
+ "del": ["rm"],
181
+ "rm": ["del"],
182
+ "move": ["mv"],
183
+ "mv": ["move"],
184
+ "cls": ["clear"],
185
+ "clear": ["cls"],
186
+ "find": ["fd", "rg"],
187
+ "grep": ["findstr", "Select-String"],
188
+ "ipconfig": ["ifconfig"],
189
+ "ifconfig": ["ipconfig"],
190
+ "tasklist": ["ps"],
191
+ "ps": ["tasklist"],
192
+ "echo": ["Write-Output"],
193
+ }
194
+
195
+ # ── Python builtins(用于 NameError 建议) ────────────────────────────────
196
+ PYTHON_BUILTINS = set(dir(__builtins__)) if isinstance(__builtins__, dict) else set(dir(__builtins__)) # type: ignore[arg-type]
197
+
198
+ def __init__(
199
+ self,
200
+ timeout: int = 300,
201
+ max_retries: int = 2,
202
+ auto_fix: bool = True,
203
+ max_output_length: int = 50000,
204
+ execution_mode: str = "local",
205
+ sandbox_image: str = "python:3.12-slim",
206
+ sandbox_network: bool = False,
207
+ sandbox_memory: str = "512m",
208
+ work_dir: Optional[str] = None,
209
+ extra_blocked: Optional[List[str]] = None,
210
+ ):
211
+ self.timeout = timeout
212
+ self.max_retries = max_retries
213
+ self.auto_fix = auto_fix
214
+ self.max_output_length = max_output_length
215
+ self.execution_mode = execution_mode # local | sandbox
216
+ self.sandbox_image = sandbox_image
217
+ self.sandbox_network = sandbox_network
218
+ self.sandbox_memory = sandbox_memory
219
+ self.work_dir = work_dir or os.getcwd()
220
+
221
+ # 安全: 合并正则模式 + 字符串黑名单
222
+ self._blocked = set(self.DANGEROUS_COMMANDS)
223
+ if extra_blocked:
224
+ self._blocked.update(extra_blocked)
225
+
226
+ self._execution_count = 0
227
+
228
+ # 沙盒模式: 检查 Docker 可用性
229
+ self._docker_available = False
230
+ if self.execution_mode == "sandbox":
231
+ self._docker_available = self._check_docker()
232
+ if not self._docker_available:
233
+ logger.warning("沙盒模式: Docker 不可用,将回退到本机执行")
234
+ self.execution_mode = "local"
235
+
236
+ # ── 启动时缓存: 检测平台 & Shell ────────────────────────────────────
237
+ self._platform = detect_platform()
238
+ self._available_shells: Dict[str, str] = {}
239
+ self._detect_shells()
240
+
241
+ # ── 启动时缓存: 基础环境变量(PATH 等) ──────────────────────────────
242
+ self._cached_base_env = self._build_env(None)
243
+
244
+ # ======================================================================
245
+ # 启动时一次性检测
246
+ # ======================================================================
247
+
248
+ def _check_docker(self) -> bool:
249
+ """检查 Docker 是否可用。"""
250
+ try:
251
+ proc = subprocess.run(
252
+ ["docker", "info"],
253
+ capture_output=True, timeout=10,
254
+ )
255
+ return proc.returncode == 0
256
+ except (FileNotFoundError, subprocess.TimeoutExpired):
257
+ return False
258
+
259
+ def _detect_shells(self) -> None:
260
+ """在 __init__ 中调用一次,探测可用 Shell 并缓存路径。"""
261
+ if self._platform == "windows":
262
+ # Windows: 依次检测 bash (Git Bash / MSYS2 / WSL) / cmd / PowerShell
263
+ candidates = [
264
+ ("bash", shutil.which("bash")),
265
+ ("git_bash", r"C:\Program Files\Git\bin\bash.exe"),
266
+ ("git_bash_x86", r"C:\Program Files (x86)\Git\bin\bash.exe"),
267
+ ("msys2_bash", r"C:\msys64\usr\bin\bash.exe"),
268
+ ("msys2_mingw", r"C:\msys64\mingw64\bin\bash.exe"),
269
+ ("wsl_bash", shutil.which("wsl")),
270
+ ("cmd", "cmd"),
271
+ ("powershell", shutil.which("powershell")),
272
+ ("powershell_x86", r"C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe"),
273
+ ("pwsh", shutil.which("pwsh")),
274
+ ]
275
+ for name, path in candidates:
276
+ if path and (shutil.which(path) is not None or path == "cmd"
277
+ or (isinstance(path, str) and os.path.isfile(path))):
278
+ self._available_shells[name] = path
279
+ else:
280
+ # macOS / Linux: 检测 bash / zsh / sh / fish
281
+ for shell_name in ("bash", "zsh", "sh", "dash", "fish", "ksh"):
282
+ found = shutil.which(shell_name)
283
+ if found:
284
+ self._available_shells[shell_name] = found
285
+ # 用户默认 Shell
286
+ user_shell = os.environ.get("SHELL", "")
287
+ if user_shell and os.path.isfile(user_shell):
288
+ self._available_shells.setdefault("default", user_shell)
289
+
290
+ logger.debug(f"[引擎初始化] 检测到 Shell: {list(self._available_shells.keys())}")
291
+
292
+ # ======================================================================
293
+ # 安全检查(正则 + 词边界)
294
+ # ======================================================================
295
+
296
+ @staticmethod
297
+ def _normalize_command(code: str) -> str:
298
+ """
299
+ 标准化命令字符串用于安全检测。
300
+ - 合并连续空白为单个空格
301
+ - 去除首尾空白
302
+ """
303
+ normalized = re.sub(r'\s+', ' ', code.strip())
304
+ return normalized
305
+
306
+ def _check_safety(self, code: str) -> Tuple[bool, str]:
307
+ """检查代码安全性: 正则模式(词边界) + 旧式字符串匹配。"""
308
+ normalized = self._normalize_command(code)
309
+
310
+ # 1) 正则模式匹配(词边界)
311
+ for pattern, description in self.DANGEROUS_PATTERNS:
312
+ try:
313
+ if re.search(pattern, normalized, re.IGNORECASE):
314
+ return False, f"危险命令被拦截: {description}"
315
+ except re.error:
316
+ logger.warning(f"[安全检查] 无效正则模式: {pattern}")
317
+ continue
318
+
319
+ # 2) 旧式字符串匹配(兼容)
320
+ code_lower = normalized.lower()
321
+ for dangerous in self._blocked:
322
+ if dangerous.lower() in code_lower:
323
+ return False, f"危险命令被拦截: {dangerous}"
324
+
325
+ return True, ""
326
+
327
+ # ======================================================================
328
+ # Shell 选择(使用缓存)
329
+ # ======================================================================
330
+
331
+ def _get_shell(self, language: str) -> Tuple[str, List[str]]:
332
+ """获取执行 shell 和参数(利用启动时缓存)。"""
333
+ if language == "python":
334
+ return sys.executable, ["-u", "-c"]
335
+
336
+ if language in ("shell", "bash"):
337
+ if self._platform == "windows":
338
+ # 优先 Git Bash / MSYS2 bash
339
+ for key in ("git_bash", "git_bash_x86", "msys2_bash", "msys2_mingw", "bash"):
340
+ if key in self._available_shells:
341
+ return self._available_shells[key], ["-c"]
342
+ return "cmd", ["/c"]
343
+ # macOS/Linux: 优先 bash → zsh → sh
344
+ for key in ("bash", "zsh", "sh"):
345
+ if key in self._available_shells:
346
+ return self._available_shells[key], ["-c"]
347
+ return "sh", ["-c"]
348
+
349
+ if language == "powershell":
350
+ if self._platform == "windows":
351
+ path = self._available_shells.get("powershell", "powershell")
352
+ return path, ["-NoProfile", "-Command"]
353
+ path = self._available_shells.get("pwsh", "pwsh")
354
+ return path, ["-NoProfile", "-Command"]
355
+
356
+ if language == "cmd":
357
+ return "cmd", ["/c"]
358
+
359
+ if language == "system":
360
+ if self._platform == "windows":
361
+ return "cmd", ["/c"]
362
+ for key in ("bash", "zsh", "sh"):
363
+ if key in self._available_shells:
364
+ return self._available_shells[key], ["-c"]
365
+ return "sh", ["-c"]
366
+
367
+ raise ValueError(f"不支持的语言: {language}")
368
+
369
+ def _detect_shell_for_code(self, code: str, language: str) -> Optional[Tuple[str, List[str]]]:
370
+ """
371
+ 根据代码内容推断应使用的 Shell。
372
+ - Windows 上 .bat/.cmd → cmd.exe /c
373
+ - .ps1 → PowerShell
374
+ - macOS/Linux 上检测 shebang 或脚本扩展名
375
+ """
376
+ if language not in ("shell", "bash", "system"):
377
+ return None
378
+
379
+ # Windows 特殊路由
380
+ if self._platform == "windows":
381
+ # 检测 .bat/.cmd 引用
382
+ bat_match = re.search(r'[\w.\-]+\.(bat|cmd)\b', code, re.IGNORECASE)
383
+ if bat_match:
384
+ return "cmd", ["/c"]
385
+ # 检测 .ps1 引用
386
+ ps1_match = re.search(r'[\w.\-]+\.ps1\b', code, re.IGNORECASE)
387
+ if ps1_match:
388
+ ps_path = self._available_shells.get("powershell") or "powershell"
389
+ return ps_path, ["-NoProfile", "-Command"]
390
+ else:
391
+ # macOS/Linux: 检测 shebang
392
+ shebang_match = re.search(r'^#!\s*/(?:usr/(?:local/)?)?(?:bin|env)/(\w+)', code, re.MULTILINE)
393
+ if shebang_match:
394
+ shell_name = shebang_match.group(1)
395
+ if shell_name in self._available_shells:
396
+ return self._available_shells[shell_name], ["-c"]
397
+
398
+ return None
399
+
400
+ # ======================================================================
401
+ # 环境变量(使用缓存)
402
+ # ======================================================================
403
+
404
+ def _build_env(self, extra_env: Optional[Dict[str, str]] = None) -> Dict[str, str]:
405
+ """构建执行环境变量。"""
406
+ env = os.environ.copy()
407
+
408
+ # 确保常见路径
409
+ paths = env.get("PATH", "").split(os.pathsep)
410
+ common_paths: List[str] = []
411
+
412
+ if self._platform == "windows":
413
+ common_paths = [
414
+ r"C:\Windows\System32",
415
+ r"C:\Windows",
416
+ r"C:\Program Files\Git\bin",
417
+ r"C:\Program Files (x86)\Git\bin",
418
+ r"C:\msys64\usr\bin",
419
+ r"C:\msys64\mingw64\bin",
420
+ ]
421
+ else:
422
+ common_paths = [
423
+ "/usr/local/bin",
424
+ "/usr/bin",
425
+ "/bin",
426
+ "/usr/sbin",
427
+ "/sbin",
428
+ str(Path.home() / ".local" / "bin"),
429
+ str(Path.home() / ".cargo" / "bin"),
430
+ str(Path.home() / ".npm-global" / "bin"),
431
+ ]
432
+
433
+ for p in common_paths:
434
+ if p not in paths:
435
+ paths.insert(0, p)
436
+ env["PATH"] = os.pathsep.join(paths)
437
+
438
+ # Python 相关
439
+ env["PYTHONIOENCODING"] = "utf-8"
440
+ env["PYTHONDONTWRITEBYTECODE"] = "1"
441
+
442
+ if extra_env:
443
+ env.update(extra_env)
444
+
445
+ return env
446
+
447
+ def _get_env(self, extra_env: Optional[Dict[str, str]] = None) -> Dict[str, str]:
448
+ """获取环境变量(使用缓存的基础环境 + 额外变量)。"""
449
+ if extra_env:
450
+ env = dict(self._cached_base_env)
451
+ env.update(extra_env)
452
+ return env
453
+ return self._cached_base_env
454
+
455
+ # ======================================================================
456
+ # 异步执行
457
+ # ======================================================================
458
+
459
+ async def execute(
460
+ self,
461
+ language: str,
462
+ code: str,
463
+ timeout: Optional[int] = None,
464
+ work_dir: Optional[str] = None,
465
+ env: Optional[Dict[str, str]] = None,
466
+ metadata: Optional[Dict] = None,
467
+ ) -> ExecResult:
468
+ """
469
+ 执行代码。
470
+
471
+ Args:
472
+ language: 编程语言 (python/shell/bash/powershell/cmd/system)
473
+ code: 要执行的代码
474
+ timeout: 超时时间(秒),默认使用引擎配置
475
+ work_dir: 工作目录
476
+ env: 环境变量
477
+ metadata: 附加元数据
478
+
479
+ Returns:
480
+ ExecResult 结构化结果
481
+ """
482
+ exec_timeout = timeout or self.timeout
483
+ work_dir = work_dir or self.work_dir
484
+ metadata = metadata or {}
485
+
486
+ # 安全检查
487
+ safe, reason = self._check_safety(code)
488
+ if not safe:
489
+ return ExecResult(
490
+ success=False,
491
+ error=reason,
492
+ language=language,
493
+ code=code,
494
+ metadata={**metadata, "mode": self.execution_mode},
495
+ )
496
+
497
+ self._execution_count += 1
498
+ exec_id = f"exec_{self._execution_count}"
499
+
500
+ mode_label = "本地" if self.execution_mode == "local" else "沙盒(Docker)"
501
+ logger.info(f"[{exec_id}] 开始执行 ({language}, mode={mode_label}, timeout={exec_timeout}s)")
502
+ logger.debug(f"[{exec_id}] 代码:\n{code[:500]}")
503
+
504
+ # 根据执行模式选择执行方式
505
+ if self.execution_mode == "sandbox" and self._docker_available:
506
+ result = await self._execute_sandbox(language, code, exec_timeout, work_dir, env, exec_id)
507
+ elif language == "python":
508
+ result = await self._execute_python(code, exec_timeout, work_dir, env, exec_id)
509
+ else:
510
+ result = await self._execute_shell(language, code, exec_timeout, work_dir, env, exec_id)
511
+
512
+ result.language = language
513
+ result.code = code
514
+ result.metadata = metadata
515
+
516
+ if result.success:
517
+ logger.info(f"[{exec_id}] 执行成功 (耗时: {result.execution_time:.2f}s)")
518
+ else:
519
+ logger.warning(f"[{exec_id}] 执行失败: {result.error[:200]}")
520
+
521
+ # 自动修复(超时错误跳过,超时由 LLM 诊断机制处理)
522
+ if not result.success and not result.timed_out and self.auto_fix and self.max_retries > 0:
523
+ fix_result = await self._auto_fix(language, code, result, exec_timeout, work_dir, env)
524
+ if fix_result and fix_result.success:
525
+ logger.info(f"[{exec_id}] 自动修复成功!")
526
+ fix_result.language = language
527
+ fix_result.code = code
528
+ fix_result.metadata = {**metadata, "auto_fixed": True}
529
+ return fix_result
530
+
531
+ return result
532
+
533
+ async def _execute_python(
534
+ self,
535
+ code: str,
536
+ timeout: int,
537
+ work_dir: str,
538
+ env: Optional[Dict[str, str]],
539
+ exec_id: str,
540
+ ) -> ExecResult:
541
+ """执行 Python 代码"""
542
+ start_time = asyncio.get_event_loop().time()
543
+
544
+ # 写入临时文件
545
+ try:
546
+ with tempfile.NamedTemporaryFile(
547
+ mode="w",
548
+ suffix=".py",
549
+ dir=work_dir,
550
+ delete=False,
551
+ encoding="utf-8",
552
+ ) as f:
553
+ # 添加标准输出重定向以确保实时输出
554
+ f.write("import sys; sys.stdout.reconfigure(encoding='utf-8')\n")
555
+ f.write(code)
556
+ temp_file = f.name
557
+ except Exception as e:
558
+ return ExecResult(error=f"创建临时文件失败: {e}")
559
+
560
+ try:
561
+ process = await asyncio.create_subprocess_exec(
562
+ sys.executable, "-u", temp_file,
563
+ stdout=asyncio.subprocess.PIPE,
564
+ stderr=asyncio.subprocess.PIPE,
565
+ cwd=work_dir,
566
+ env=self._get_env(env),
567
+ )
568
+
569
+ try:
570
+ stdout, stderr = await asyncio.wait_for(
571
+ process.communicate(),
572
+ timeout=timeout,
573
+ )
574
+ elapsed = asyncio.get_event_loop().time() - start_time
575
+ stdout_str = stdout.decode("utf-8", errors="replace")
576
+ stderr_str = stderr.decode("utf-8", errors="replace")
577
+
578
+ return ExecResult(
579
+ success=process.returncode == 0,
580
+ stdout=stdout_str,
581
+ stderr=stderr_str,
582
+ exit_code=process.returncode or -1,
583
+ error=stderr_str if process.returncode != 0 else "",
584
+ execution_time=elapsed,
585
+ )
586
+ except asyncio.TimeoutError:
587
+ process.kill()
588
+ elapsed = asyncio.get_event_loop().time() - start_time
589
+ return ExecResult(
590
+ success=False,
591
+ error=f"执行超时 ({timeout}s)",
592
+ execution_time=elapsed,
593
+ timed_out=True,
594
+ timeout_limit=timeout,
595
+ )
596
+ finally:
597
+ try:
598
+ os.unlink(temp_file)
599
+ except OSError:
600
+ pass
601
+
602
+ async def _execute_shell(
603
+ self,
604
+ language: str,
605
+ code: str,
606
+ timeout: int,
607
+ work_dir: str,
608
+ env: Optional[Dict[str, str]],
609
+ exec_id: str,
610
+ ) -> ExecResult:
611
+ """
612
+ 执行 Shell / PowerShell / 系统命令。
613
+
614
+ [BUG FIX] 现在正确使用 shell_cmd + shell_args 进行路由:
615
+ - _get_shell(language) 返回 shell 可执行路径与参数
616
+ - _detect_shell_for_code() 可根据代码内容覆盖(如 .bat/.cmd/.ps1)
617
+ - 使用 create_subprocess_exec 而非 create_subprocess_shell
618
+ """
619
+ start_time = asyncio.get_event_loop().time()
620
+
621
+ # 1) 根据语言获取默认 Shell
622
+ shell_cmd, shell_args = self._get_shell(language)
623
+
624
+ # 2) 根据代码内容推断是否应覆盖 Shell
625
+ override = self._detect_shell_for_code(code, language)
626
+ if override is not None:
627
+ shell_cmd, shell_args = override
628
+ logger.debug(f"[{exec_id}] Shell 被代码内容覆盖为: {shell_cmd}")
629
+
630
+ # 3) 构建命令列表: [shell_cmd, shell_arg..., code_as_string]
631
+ cmd = [shell_cmd] + shell_args + [code]
632
+
633
+ logger.debug(f"[{exec_id}] Shell 命令: {shell_cmd} {' '.join(shell_args)}")
634
+
635
+ try:
636
+ # [FIX] 使用 create_subprocess_exec + 已计算的 shell_cmd/shell_args
637
+ process = await asyncio.create_subprocess_exec(
638
+ *cmd,
639
+ stdout=asyncio.subprocess.PIPE,
640
+ stderr=asyncio.subprocess.PIPE,
641
+ cwd=work_dir,
642
+ env=self._get_env(env),
643
+ )
644
+
645
+ try:
646
+ stdout, stderr = await asyncio.wait_for(
647
+ process.communicate(),
648
+ timeout=timeout,
649
+ )
650
+ elapsed = asyncio.get_event_loop().time() - start_time
651
+ stdout_str = stdout.decode("utf-8", errors="replace")
652
+ stderr_str = stderr.decode("utf-8", errors="replace")
653
+
654
+ return ExecResult(
655
+ success=process.returncode == 0,
656
+ stdout=stdout_str,
657
+ stderr=stderr_str,
658
+ exit_code=process.returncode or -1,
659
+ error=stderr_str if process.returncode != 0 else "",
660
+ execution_time=elapsed,
661
+ )
662
+ except asyncio.TimeoutError:
663
+ process.kill()
664
+ elapsed = asyncio.get_event_loop().time() - start_time
665
+ return ExecResult(
666
+ success=False,
667
+ error=f"执行超时 ({timeout}s)",
668
+ execution_time=elapsed,
669
+ timed_out=True,
670
+ timeout_limit=timeout,
671
+ )
672
+ except Exception as e:
673
+ elapsed = asyncio.get_event_loop().time() - start_time
674
+ return ExecResult(
675
+ success=False,
676
+ error=str(e),
677
+ execution_time=elapsed,
678
+ )
679
+
680
+ # ======================================================================
681
+ # 沙盒执行 (Docker 容器)
682
+ # ======================================================================
683
+
684
+ async def _execute_sandbox(
685
+ self,
686
+ language: str,
687
+ code: str,
688
+ timeout: int,
689
+ work_dir: str,
690
+ env: Optional[Dict[str, str]],
691
+ exec_id: str,
692
+ ) -> ExecResult:
693
+ """在 Docker 容器中执行代码(沙盒模式)。"""
694
+ start_time = asyncio.get_event_loop().time()
695
+
696
+ # 确定容器内的执行命令
697
+ if language == "python":
698
+ container_cmd = ["python3", "-c", code]
699
+ elif language in ("shell", "bash", "system"):
700
+ container_cmd = ["bash", "-c", code]
701
+ elif language == "powershell":
702
+ return ExecResult(error="PowerShell 不支持沙盒模式")
703
+ else:
704
+ container_cmd = ["sh", "-c", code]
705
+
706
+ docker_cmd = [
707
+ "docker", "run", "--rm",
708
+ "--memory", self.sandbox_memory,
709
+ "--cpus", "1",
710
+ "--pids-limit", "64",
711
+ "--workdir", "/workspace",
712
+ ]
713
+
714
+ if not self.sandbox_network:
715
+ docker_cmd.append("--network=none")
716
+
717
+ # 挂载工作目录为只读
718
+ if os.path.isdir(work_dir):
719
+ docker_cmd.extend(["-v", f"{work_dir}:/workspace:ro"])
720
+
721
+ docker_cmd.extend([self.sandbox_image] + container_cmd)
722
+
723
+ logger.info(f"[{exec_id}] 沙盒执行: docker run {self.sandbox_image}")
724
+
725
+ try:
726
+ process = await asyncio.create_subprocess_exec(
727
+ *docker_cmd,
728
+ stdout=asyncio.subprocess.PIPE,
729
+ stderr=asyncio.subprocess.PIPE,
730
+ cwd=work_dir,
731
+ env=self._get_env(env),
732
+ )
733
+ try:
734
+ stdout, stderr = await asyncio.wait_for(
735
+ process.communicate(), timeout=timeout
736
+ )
737
+ elapsed = asyncio.get_event_loop().time() - start_time
738
+ return ExecResult(
739
+ success=process.returncode == 0,
740
+ stdout=stdout.decode("utf-8", errors="replace"),
741
+ stderr=stderr.decode("utf-8", errors="replace"),
742
+ exit_code=process.returncode or -1,
743
+ error=stderr.decode("utf-8", errors="replace") if process.returncode != 0 else "",
744
+ execution_time=elapsed,
745
+ metadata={"mode": "sandbox", "image": self.sandbox_image},
746
+ )
747
+ except asyncio.TimeoutError:
748
+ process.kill()
749
+ elapsed = asyncio.get_event_loop().time() - start_time
750
+ return ExecResult(
751
+ success=False,
752
+ error=f"沙盒执行超时 ({timeout}s)",
753
+ execution_time=elapsed,
754
+ timed_out=True,
755
+ timeout_limit=timeout,
756
+ metadata={"mode": "sandbox"},
757
+ )
758
+ except FileNotFoundError:
759
+ # Docker 不存在,回退到本地执行
760
+ logger.warning(f"[{exec_id}] Docker 不可用,回退到本地执行")
761
+ self.execution_mode = "local"
762
+ if language == "python":
763
+ return await self._execute_python(code, timeout, work_dir, env, exec_id)
764
+ else:
765
+ return await self._execute_shell(language, code, timeout, work_dir, env, exec_id)
766
+ except Exception as e:
767
+ elapsed = asyncio.get_event_loop().time() - start_time
768
+ return ExecResult(
769
+ success=False,
770
+ error=f"沙盒执行异常: {e}",
771
+ execution_time=elapsed,
772
+ metadata={"mode": "sandbox"},
773
+ )
774
+
775
+ def set_execution_mode(self, mode: str) -> bool:
776
+ """切换执行模式。返回是否切换成功。"""
777
+ if mode not in ("local", "sandbox"):
778
+ return False
779
+ if mode == "sandbox":
780
+ if not self._check_docker():
781
+ logger.warning("沙盒模式: Docker 不可用")
782
+ return False
783
+ self._docker_available = True
784
+ self.execution_mode = mode
785
+ logger.info(f"执行模式已切换: {mode}")
786
+ return True
787
+
788
+ def get_execution_info(self) -> Dict[str, Any]:
789
+ """获取当前执行模式信息。"""
790
+ return {
791
+ "mode": self.execution_mode,
792
+ "docker_available": self._docker_available,
793
+ "sandbox_image": self.sandbox_image,
794
+ "sandbox_network": self.sandbox_network,
795
+ "sandbox_memory": self.sandbox_memory,
796
+ "execution_count": self._execution_count,
797
+ }
798
+
799
+ # ======================================================================
800
+ # 自动修复(增强: 12 种 Python + 4 种 Shell)
801
+ # ======================================================================
802
+
803
+ async def _auto_fix(
804
+ self,
805
+ language: str,
806
+ original_code: str,
807
+ error_result: ExecResult,
808
+ timeout: int,
809
+ work_dir: str,
810
+ env: Optional[Dict[str, str]],
811
+ ) -> Optional[ExecResult]:
812
+ """
813
+ 自动修复常见错误。
814
+
815
+ Python (12 种模式):
816
+ 1. ModuleNotFoundError → pip install
817
+ 2. ImportError: cannot import name → 建议正确导入路径
818
+ 3. NameError → 检查拼写、内置名
819
+ 4. SyntaxError: unexpected EOF → 补全括号
820
+ 5. TypeError: takes Y arguments → 建议签名
821
+ 6. FileNotFoundError → 建议路径
822
+ 7. PermissionError → 建议权限(不自动 sudo)
823
+ 8. ConnectionError / TimeoutError → 网络建议
824
+ 9. UnicodeEncodeError → 编码头
825
+ 10. IndentationError → 自动修复缩进
826
+ 11. KeyError → 建议可用 key
827
+ 12. JSONDecodeError → JSON 校验建议
828
+
829
+ Shell (4 种模式):
830
+ 1. command not found → 正确命令别名
831
+ 2. No such file or directory → 路径建议
832
+ 3. Permission denied → chmod 提示(不自动 sudo)
833
+ 4. syntax error near unexpected token → 引号匹配
834
+ """
835
+ error_text = error_result.stderr or error_result.error or ""
836
+
837
+ if language == "python":
838
+ return await self._auto_fix_python(original_code, error_text, timeout, work_dir, env)
839
+ elif language in ("shell", "bash", "system"):
840
+ return await self._auto_fix_shell(original_code, error_text, timeout, work_dir, env, error_result)
841
+
842
+ return None
843
+
844
+ # ── Python 自动修复 ────────────────────────────────────────────────────
845
+
846
+ async def _auto_fix_python(
847
+ self,
848
+ code: str,
849
+ error_text: str,
850
+ timeout: int,
851
+ work_dir: str,
852
+ env: Optional[Dict[str, str]],
853
+ ) -> Optional[ExecResult]:
854
+ """Python 12 种自动修复模式。"""
855
+
856
+ # ── 1. ModuleNotFoundError → pip install ──────────────────────────
857
+ if "ModuleNotFoundError" in error_text:
858
+ module = self._extract_missing_module(error_text)
859
+ if module:
860
+ install_code = f"pip install {module}"
861
+ logger.info(f"[自动修复] 安装缺失模块: {module}")
862
+ install_result = await self._execute_shell(
863
+ "system", install_code, timeout, work_dir, env, "auto_fix_install"
864
+ )
865
+ if install_result.success:
866
+ return await self._execute_python(
867
+ code, timeout, work_dir, env, "retry_module"
868
+ )
869
+ # 安装失败则继续尝试其他修复策略
870
+
871
+ # ── 2. ImportError: cannot import name → 建议正确导入路径 ─────────
872
+ if "ImportError" in error_text and "cannot import name" in error_text:
873
+ match = re.search(r"cannot import name ['\"](\w+)['\"]", error_text)
874
+ if match:
875
+ bad_name = match.group(1)
876
+ # 检查模块是否存在(可能包名不同)
877
+ module_match = re.search(r"from ['\"](.+?)['\"]", error_text)
878
+ module_name = module_match.group(1) if module_match else "unknown"
879
+ logger.info(
880
+ f"[自动修复] ImportError: '{bad_name}' 不在 '{module_name}' 中。"
881
+ f"请检查模块版本或尝试: from {module_name} import *"
882
+ )
883
+
884
+ # ── 3. NameError: name 'X' is not defined → 检查拼写/内置 ───────
885
+ if "NameError" in error_text:
886
+ match = re.search(r"name '(\w+)' is not defined", error_text)
887
+ if match:
888
+ undefined_name = match.group(1)
889
+ suggestion = self._suggest_name_fix(undefined_name)
890
+ if suggestion:
891
+ logger.info(f"[自动修复] NameError: '{undefined_name}' → 建议 '{suggestion}'")
892
+ # 尝试自动替换
893
+ fixed_code = re.sub(
894
+ r'\b' + re.escape(undefined_name) + r'\b',
895
+ suggestion,
896
+ code,
897
+ count=1,
898
+ )
899
+ if fixed_code != code:
900
+ return await self._execute_python(
901
+ fixed_code, timeout, work_dir, env, "fix_name"
902
+ )
903
+
904
+ # ── 4. SyntaxError: unexpected EOF → 补全括号 ──────────────────
905
+ if "SyntaxError" in error_text and "unexpected EOF" in error_text:
906
+ fixed_code = self._fix_unclosed_brackets(code)
907
+ if fixed_code != code:
908
+ return await self._execute_python(
909
+ fixed_code, timeout, work_dir, env, "fix_eof"
910
+ )
911
+
912
+ # ── 5. TypeError: X() takes Y arguments → 建议签名 ─────────────
913
+ if "TypeError" in error_text:
914
+ match = re.search(
915
+ r"(\w+)\(\) (takes|missing) (\d+ (?:positional )?argument|at least \d+)",
916
+ error_text,
917
+ )
918
+ if match:
919
+ func_name = match.group(1)
920
+ logger.info(
921
+ f"[自动修复] TypeError: {func_name}() 参数数量不匹配。"
922
+ f"请检查函数签名。"
923
+ )
924
+
925
+ # ── 6. FileNotFoundError → 建议路径 ────────────────────────────
926
+ if "FileNotFoundError" in error_text:
927
+ match = re.search(r"No such file or directory: ['\"](.+?)['\"]", error_text)
928
+ if match:
929
+ missing_path = match.group(1)
930
+ dir_part = os.path.dirname(missing_path)
931
+ logger.info(
932
+ f"[自动修复] FileNotFoundError: '{missing_path}'。"
933
+ f"请检查路径是否存在。尝试列出目录: ls {dir_part or '.'}"
934
+ )
935
+ # 尝试列出目录内容作为提示
936
+ if dir_part and os.path.isdir(dir_part):
937
+ listing = os.listdir(dir_part)
938
+ logger.info(f" 目录内容: {listing[:20]}")
939
+
940
+ # ── 7. PermissionError → 建议权限(不自动 sudo) ─────────────────
941
+ if "PermissionError" in error_text:
942
+ match = re.search(r"\[Errno 13\] Permission denied: ['\"](.+?)['\"]", error_text)
943
+ if match:
944
+ denied_path = match.group(1)
945
+ logger.info(
946
+ f"[自动修复] PermissionError: '{denied_path}'。"
947
+ f"请手动检查文件权限: ls -la '{denied_path}'"
948
+ )
949
+
950
+ # ── 8. ConnectionError / TimeoutError → 网络建议 ────────────────
951
+ if "ConnectionError" in error_text or "TimeoutError" in error_text or "ConnectionRefusedError" in error_text:
952
+ logger.info(
953
+ "[自动修复] 网络连接失败。"
954
+ "请检查网络连接、代理设置或目标服务是否可用。"
955
+ )
956
+
957
+ # ── 9. UnicodeEncodeError → 编码头 ──────────────────────────────
958
+ if "UnicodeEncodeError" in error_text:
959
+ fixed_code = (
960
+ "import sys; sys.stdout.reconfigure(encoding='utf-8', errors='replace')\n"
961
+ + code
962
+ )
963
+ return await self._execute_python(
964
+ fixed_code, timeout, work_dir, env, "fix_encoding"
965
+ )
966
+
967
+ # ── 10. IndentationError → 自动修复缩进 ─────────────────────────
968
+ if "IndentationError" in error_text:
969
+ fixed_code = self._fix_indentation(code)
970
+ if fixed_code != code:
971
+ return await self._execute_python(
972
+ fixed_code, timeout, work_dir, env, "fix_indent"
973
+ )
974
+
975
+ # ── 11. KeyError → 建议可用 key ────────────────────────────────
976
+ if "KeyError" in error_text:
977
+ match = re.search(r"KeyError: ['\"](.+?)['\"]", error_text)
978
+ if match:
979
+ missing_key = match.group(1)
980
+ logger.info(
981
+ f"[自动修复] KeyError: '{missing_key}'。"
982
+ f"字典中不存在该键,请检查键名拼写。"
983
+ )
984
+
985
+ # ── 12. json.decoder.JSONDecodeError → JSON 校验建议 ────────────
986
+ if "JSONDecodeError" in error_text:
987
+ logger.info(
988
+ "[自动修复] JSONDecodeError: JSON 格式无效。"
989
+ "请检查 JSON 字符串是否正确(缺少引号、多余逗号、未转义字符等)。"
990
+ )
991
+
992
+ return None
993
+
994
+ # ── Shell 自动修复 ─────────────────────────────────────────────────────
995
+
996
+ async def _auto_fix_shell(
997
+ self,
998
+ code: str,
999
+ error_text: str,
1000
+ timeout: int,
1001
+ work_dir: str,
1002
+ env: Optional[Dict[str, str]],
1003
+ error_result: ExecResult,
1004
+ ) -> Optional[ExecResult]:
1005
+ """Shell 4 种自动修复模式。"""
1006
+
1007
+ # ── 1. command not found → 正确命令别名 ──────────────────────────
1008
+ if "command not found" in error_text:
1009
+ cmd_name = self._extract_command_name(error_text)
1010
+ if cmd_name:
1011
+ alias = self._suggest_shell_alias(cmd_name)
1012
+ logger.info(
1013
+ f"[自动修复] 命令不存在: '{cmd_name}'。"
1014
+ f"你是否想用: {alias}?"
1015
+ )
1016
+
1017
+ # ── 2. No such file or directory → 路径建议 ─────────────────────
1018
+ if "No such file or directory" in error_text:
1019
+ match = re.search(r"No such file or directory:\s*'(.+?)'", error_text)
1020
+ if not match:
1021
+ match = re.search(r":\s*(\S+):\s*No such file or directory", error_text)
1022
+ if match:
1023
+ missing_path = match.group(1)
1024
+ logger.info(
1025
+ f"[自动修复] 文件/目录不存在: '{missing_path}'。"
1026
+ f"请检查路径是否正确。尝试: ls {os.path.dirname(missing_path) or '.'}"
1027
+ )
1028
+
1029
+ # ── 3. Permission denied → chmod 提示(不自动 sudo) ─────────────
1030
+ if "Permission denied" in error_text:
1031
+ logger.warning(
1032
+ f"[自动修复] 权限不足,已拦截自动 sudo 执行。"
1033
+ f"请用户手动以提升权限运行该命令。原始命令: {code[:200]}"
1034
+ )
1035
+ # [SECURITY FIX] 不自动执行 sudo,返回提示
1036
+ return ExecResult(
1037
+ success=False,
1038
+ error=(
1039
+ "⚠️ 权限不足: 命令需要提升权限。\n"
1040
+ "安全策略: 引擎不会自动使用 sudo。\n"
1041
+ f"请手动执行: sudo {code}"
1042
+ ),
1043
+ stderr=error_text,
1044
+ exit_code=error_result.exit_code,
1045
+ metadata={"sudo_blocked": True, "original_code": code},
1046
+ )
1047
+
1048
+ # ── 4. syntax error near unexpected token → 引号匹配 ────────────
1049
+ if "syntax error near unexpected token" in error_text:
1050
+ token_match = re.search(r"syntax error near unexpected token\s+`(.+?)'", error_text)
1051
+ token = token_match.group(1) if token_match else "unknown"
1052
+ logger.info(
1053
+ f"[自动修复] Shell 语法错误,unexpected token: '{token}'。"
1054
+ f"请检查引号是否正确匹配,以及是否有多余或缺失的特殊字符。"
1055
+ )
1056
+
1057
+ return None
1058
+
1059
+ # ======================================================================
1060
+ # 辅助方法
1061
+ # ======================================================================
1062
+
1063
+ def _extract_missing_module(self, error_text: str) -> Optional[str]:
1064
+ """从错误信息中提取缺失的模块名"""
1065
+ # ModuleNotFoundError: No module named 'xxx'
1066
+ match = re.search(r"No module named ['\"](.+?)['\"]", error_text)
1067
+ if match:
1068
+ return match.group(1)
1069
+ # ImportError: cannot import name 'xxx' from 'yyy'
1070
+ match = re.search(r"cannot import name .+? from ['\"](.+?)['\"]", error_text)
1071
+ if match:
1072
+ return match.group(1)
1073
+ return None
1074
+
1075
+ def _extract_command_name(self, error_text: str) -> Optional[str]:
1076
+ """从错误信息中提取命令名"""
1077
+ match = re.search(r"(\w+):\s*command not found", error_text)
1078
+ if match:
1079
+ return match.group(1)
1080
+ return None
1081
+
1082
+ def _suggest_name_fix(self, name: str) -> Optional[str]:
1083
+ """
1084
+ 对 NameError 中的未定义名称提供建议:
1085
+ 1. 检查常见拼写错误表
1086
+ 2. 检查 Python 内置名
1087
+ 3. 使用编辑距离(简单 Levenshtein)查找相近名
1088
+ """
1089
+ name_lower = name.lower()
1090
+
1091
+ # 1) 精确拼写错误
1092
+ if name_lower in self.COMMON_MISSPELLINGS:
1093
+ return self.COMMON_MISSPELLINGS[name_lower]
1094
+
1095
+ # 2) 内置名(精确匹配)
1096
+ if name in self.PYTHON_BUILTINS:
1097
+ return name # 已是内置名但可能大小写不对
1098
+
1099
+ # 3) 内置名(忽略大小写)
1100
+ for builtin in self.PYTHON_BUILTINS:
1101
+ if isinstance(builtin, str) and builtin.lower() == name_lower:
1102
+ return builtin
1103
+
1104
+ # 4) 简单编辑距离(距离 ≤ 2 视为建议)
1105
+ best_match: Optional[str] = None
1106
+ best_dist = 3
1107
+ for builtin in self.PYTHON_BUILTINS:
1108
+ if not isinstance(builtin, str):
1109
+ continue
1110
+ dist = self._levenshtein_distance(name.lower(), builtin.lower())
1111
+ if dist < best_dist and dist <= 2:
1112
+ best_dist = dist
1113
+ best_match = builtin
1114
+
1115
+ return best_match
1116
+
1117
+ @staticmethod
1118
+ def _levenshtein_distance(s1: str, s2: str) -> int:
1119
+ """简单 Levenshtein 编辑距离。"""
1120
+ if len(s1) < len(s2):
1121
+ return ExecutionEngine._levenshtein_distance(s2, s1)
1122
+ if len(s2) == 0:
1123
+ return len(s1)
1124
+ prev_row = list(range(len(s2) + 1))
1125
+ for i, c1 in enumerate(s1):
1126
+ curr_row = [i + 1]
1127
+ for j, c2 in enumerate(s2):
1128
+ insertions = prev_row[j + 1] + 1
1129
+ deletions = curr_row[j] + 1
1130
+ substitutions = prev_row[j] + (c1 != c2)
1131
+ curr_row.append(min(insertions, deletions, substitutions))
1132
+ prev_row = curr_row
1133
+ return prev_row[-1]
1134
+
1135
+ def _fix_unclosed_brackets(self, code: str) -> str:
1136
+ """尝试补全未闭合的括号/方括号/花括号。"""
1137
+ open_brackets = "([{"
1138
+ close_brackets = ")]}"
1139
+ stack: List[str] = []
1140
+ # 只检查行尾,不解析字符串内容(简化版)
1141
+ for char in code:
1142
+ if char in open_brackets:
1143
+ stack.append(char)
1144
+ elif char in close_brackets:
1145
+ if stack:
1146
+ stack.pop()
1147
+ # 补全未闭合的
1148
+ if stack:
1149
+ fix_map = {"(": ")", "[": "]", "{": "}"}
1150
+ code += "\n" + "".join(fix_map.get(c, "") for c in reversed(stack))
1151
+ return code
1152
+
1153
+ def _fix_indentation(self, code: str) -> str:
1154
+ """修复常见缩进问题。"""
1155
+ # 1) textwrap.dedent 移除整体缩进
1156
+ fixed = textwrap.dedent(code)
1157
+ # 2) Tab → 4 空格
1158
+ fixed = fixed.replace("\t", " ")
1159
+ # 3) 移除行尾空白
1160
+ fixed = "\n".join(line.rstrip() for line in fixed.splitlines()) + ("\n" if code.endswith("\n") else "")
1161
+ return fixed
1162
+
1163
+ def _suggest_shell_alias(self, cmd_name: str) -> str:
1164
+ """根据平台为 command not found 的命令建议正确名称。"""
1165
+ aliases = self.SHELL_COMMAND_ALIASES.get(cmd_name, [])
1166
+ if aliases:
1167
+ return " / ".join(aliases)
1168
+ return f"(无已知别名,请检查命令拼写或确认已安装)"
1169
+
1170
+ # ======================================================================
1171
+ # 同步执行(修复 asyncio 安全模式)
1172
+ # ======================================================================
1173
+
1174
+ def execute_sync(
1175
+ self,
1176
+ language: str,
1177
+ code: str,
1178
+ **kwargs,
1179
+ ) -> ExecResult:
1180
+ """
1181
+ 同步执行(便捷方法)。
1182
+
1183
+ [BUG FIX] 安全处理 asyncio 事件循环:
1184
+ - 如果已有运行中的 loop → 使用 ThreadPoolExecutor 隔离
1185
+ - 否则 → 直接 asyncio.run()
1186
+ """
1187
+ try:
1188
+ loop = asyncio.get_running_loop()
1189
+ except RuntimeError:
1190
+ loop = None
1191
+
1192
+ if loop and loop.is_running():
1193
+ import concurrent.futures
1194
+ with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool:
1195
+ return pool.submit(
1196
+ asyncio.run, self.execute(language, code, **kwargs)
1197
+ ).result()
1198
+ else:
1199
+ return asyncio.run(self.execute(language, code, **kwargs))
1200
+
1201
+ # ======================================================================
1202
+ # 统计信息
1203
+ # ======================================================================
1204
+
1205
+ def get_stats(self) -> Dict[str, Any]:
1206
+ """获取执行统计"""
1207
+ return {
1208
+ "total_executions": self._execution_count,
1209
+ "timeout": self.timeout,
1210
+ "max_retries": self.max_retries,
1211
+ "auto_fix_enabled": self.auto_fix,
1212
+ "platform": self._platform,
1213
+ "available_shells": list(self._available_shells.keys()),
1214
+ "env_cached": True,
1215
+ }