foliko 1.0.80 → 1.0.81

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 (235) hide show
  1. package/.agent/agents/code-assistant.json +14 -0
  2. package/.agent/agents/email-assistant.json +14 -0
  3. package/.agent/agents/file-assistant.json +15 -0
  4. package/.agent/agents/system-assistant.json +15 -0
  5. package/.agent/agents/web-assistant.json +12 -0
  6. package/.agent/data/ambient/goals.json +50 -0
  7. package/.agent/data/ambient/memories.json +7 -0
  8. package/.agent/data/default.json +15 -31894
  9. package/.agent/data/plugins-state.json +146 -181
  10. package/.agent/data/scheduler/tasks.json +1 -0
  11. package/.agent/mcp_config.json +1 -0
  12. package/.agent/package.json +8 -0
  13. package/.agent/plugins/__pycache__/test_plugin.cpython-312.pyc +0 -0
  14. package/.agent/plugins/system-info/index.js +387 -0
  15. package/.agent/plugins/system-info/package.json +4 -0
  16. package/.agent/plugins/system-info/test.js +40 -0
  17. package/.agent/plugins/test_plugin.py +304 -0
  18. package/.agent/plugins.json +14 -5
  19. package/.agent/python-scripts/test_sample.py +24 -0
  20. package/.agent/skills/sysinfo/SKILL.md +38 -0
  21. package/.agent/skills/sysinfo/system-info.sh +130 -0
  22. package/.agent/skills/workflow/SKILL.md +324 -0
  23. package/.agent/workflows/email-digest.json +50 -0
  24. package/.agent/workflows/file-backup.json +21 -0
  25. package/.agent/workflows/get-ip-notify.json +32 -0
  26. package/.agent/workflows/news-aggregator.json +93 -0
  27. package/.agent/workflows/news-dashboard-v2.json +94 -0
  28. package/.agent/workflows/notification-batch.json +32 -0
  29. package/.claude/settings.local.json +171 -171
  30. package/.env.example +56 -56
  31. package/cli/bin/foliko.js +12 -12
  32. package/cli/src/commands/chat.js +143 -143
  33. package/cli/src/commands/list.js +93 -93
  34. package/cli/src/index.js +75 -75
  35. package/cli/src/ui/chat-ui.js +201 -201
  36. package/cli/src/utils/ansi.js +40 -40
  37. package/cli/src/utils/markdown.js +292 -292
  38. package/examples/ambient-example.js +194 -194
  39. package/examples/basic.js +115 -115
  40. package/examples/bootstrap.js +121 -121
  41. package/examples/mcp-example.js +56 -56
  42. package/examples/skill-example.js +49 -49
  43. package/examples/test-chat.js +137 -137
  44. package/examples/test-mcp.js +85 -85
  45. package/examples/test-reload.js +59 -59
  46. package/examples/test-telegram.js +50 -50
  47. package/examples/test-tg-bot.js +45 -45
  48. package/examples/test-tg-simple.js +47 -47
  49. package/examples/test-tg.js +62 -62
  50. package/examples/test-think.js +43 -43
  51. package/examples/test-web-plugin.js +103 -103
  52. package/examples/test-weixin-feishu.js +103 -103
  53. package/examples/workflow.js +158 -158
  54. package/package.json +83 -83
  55. package/plugins/ai-plugin.js +102 -102
  56. package/plugins/ambient-agent/EventWatcher.js +113 -113
  57. package/plugins/ambient-agent/ExplorerLoop.js +640 -640
  58. package/plugins/ambient-agent/GoalManager.js +197 -197
  59. package/plugins/ambient-agent/Reflector.js +95 -95
  60. package/plugins/ambient-agent/StateStore.js +90 -90
  61. package/plugins/ambient-agent/constants.js +101 -101
  62. package/plugins/ambient-agent/index.js +579 -579
  63. package/plugins/audit-plugin.js +187 -187
  64. package/plugins/default-plugins.js +548 -548
  65. package/plugins/email/constants.js +64 -64
  66. package/plugins/email/handlers.js +461 -461
  67. package/plugins/email/index.js +278 -278
  68. package/plugins/email/monitor.js +269 -269
  69. package/plugins/email/parser.js +138 -138
  70. package/plugins/email/reply.js +151 -151
  71. package/plugins/email/utils.js +124 -124
  72. package/plugins/extension-executor-plugin.js +326 -326
  73. package/plugins/feishu-plugin.js +481 -481
  74. package/plugins/file-system-plugin.js +920 -877
  75. package/plugins/gate-trading.js +747 -747
  76. package/plugins/install-plugin.js +199 -199
  77. package/plugins/python-executor-plugin.js +367 -367
  78. package/plugins/python-plugin-loader.js +651 -651
  79. package/plugins/rules-plugin.js +294 -294
  80. package/plugins/scheduler-plugin.js +691 -691
  81. package/plugins/session-plugin.js +494 -494
  82. package/plugins/shell-executor-plugin.js +197 -197
  83. package/plugins/storage-plugin.js +263 -263
  84. package/plugins/subagent-plugin.js +845 -845
  85. package/plugins/telegram-plugin.js +482 -482
  86. package/plugins/think-plugin.js +345 -345
  87. package/plugins/tools-plugin.js +196 -196
  88. package/plugins/web-plugin.js +637 -637
  89. package/plugins/weixin-plugin.js +545 -545
  90. package/skills/find-skills/AGENTS.md +162 -162
  91. package/skills/find-skills/SKILL.md +133 -133
  92. package/skills/foliko-dev/SKILL.md +563 -583
  93. package/skills/python-plugin-dev/SKILL.md +238 -238
  94. package/src/capabilities/index.js +11 -11
  95. package/src/capabilities/skill-manager.js +609 -609
  96. package/src/capabilities/workflow-engine.js +1109 -1109
  97. package/src/core/agent-chat.js +141 -134
  98. package/src/core/agent.js +958 -958
  99. package/src/core/framework.js +465 -465
  100. package/src/core/index.js +19 -19
  101. package/src/core/plugin-base.js +262 -262
  102. package/src/core/plugin-manager.js +863 -863
  103. package/src/core/provider.js +114 -114
  104. package/src/core/sub-agent-config.js +264 -264
  105. package/src/core/system-prompt-builder.js +120 -120
  106. package/src/core/tool-registry.js +517 -517
  107. package/src/core/tool-router.js +297 -297
  108. package/src/executors/executor-base.js +58 -58
  109. package/src/executors/mcp-executor.js +845 -845
  110. package/src/index.js +25 -25
  111. package/src/utils/circuit-breaker.js +301 -301
  112. package/src/utils/error-boundary.js +363 -363
  113. package/src/utils/error.js +374 -374
  114. package/src/utils/event-emitter.js +97 -97
  115. package/src/utils/id.js +133 -133
  116. package/src/utils/index.js +217 -217
  117. package/src/utils/logger.js +181 -181
  118. package/src/utils/plugin-helpers.js +90 -90
  119. package/src/utils/retry.js +122 -122
  120. package/src/utils/sandbox.js +292 -292
  121. package/test/tool-registry-validation.test.js +218 -218
  122. package/website/script.js +136 -136
  123. package/.agent/.shared/ui-ux-pro-max/data/charts.csv +0 -26
  124. package/.agent/.shared/ui-ux-pro-max/data/colors.csv +0 -97
  125. package/.agent/.shared/ui-ux-pro-max/data/icons.csv +0 -101
  126. package/.agent/.shared/ui-ux-pro-max/data/landing.csv +0 -31
  127. package/.agent/.shared/ui-ux-pro-max/data/products.csv +0 -97
  128. package/.agent/.shared/ui-ux-pro-max/data/prompts.csv +0 -24
  129. package/.agent/.shared/ui-ux-pro-max/data/react-performance.csv +0 -45
  130. package/.agent/.shared/ui-ux-pro-max/data/stacks/flutter.csv +0 -53
  131. package/.agent/.shared/ui-ux-pro-max/data/stacks/html-tailwind.csv +0 -56
  132. package/.agent/.shared/ui-ux-pro-max/data/stacks/jetpack-compose.csv +0 -53
  133. package/.agent/.shared/ui-ux-pro-max/data/stacks/nextjs.csv +0 -53
  134. package/.agent/.shared/ui-ux-pro-max/data/stacks/nuxt-ui.csv +0 -51
  135. package/.agent/.shared/ui-ux-pro-max/data/stacks/nuxtjs.csv +0 -59
  136. package/.agent/.shared/ui-ux-pro-max/data/stacks/react-native.csv +0 -52
  137. package/.agent/.shared/ui-ux-pro-max/data/stacks/react.csv +0 -54
  138. package/.agent/.shared/ui-ux-pro-max/data/stacks/shadcn.csv +0 -61
  139. package/.agent/.shared/ui-ux-pro-max/data/stacks/svelte.csv +0 -54
  140. package/.agent/.shared/ui-ux-pro-max/data/stacks/swiftui.csv +0 -51
  141. package/.agent/.shared/ui-ux-pro-max/data/stacks/vue.csv +0 -50
  142. package/.agent/.shared/ui-ux-pro-max/data/styles.csv +0 -59
  143. package/.agent/.shared/ui-ux-pro-max/data/typography.csv +0 -58
  144. package/.agent/.shared/ui-ux-pro-max/data/ui-reasoning.csv +0 -101
  145. package/.agent/.shared/ui-ux-pro-max/data/ux-guidelines.csv +0 -100
  146. package/.agent/.shared/ui-ux-pro-max/data/web-interface.csv +0 -31
  147. package/.agent/.shared/ui-ux-pro-max/scripts/__pycache__/core.cpython-313.pyc +0 -0
  148. package/.agent/.shared/ui-ux-pro-max/scripts/__pycache__/design_system.cpython-313.pyc +0 -0
  149. package/.agent/.shared/ui-ux-pro-max/scripts/core.py +0 -258
  150. package/.agent/.shared/ui-ux-pro-max/scripts/design_system.py +0 -1067
  151. package/.agent/.shared/ui-ux-pro-max/scripts/search.py +0 -106
  152. package/.agent/ARCHITECTURE.md +0 -288
  153. package/.agent/agents/ambient-agent.md +0 -57
  154. package/.agent/agents/debugger.md +0 -55
  155. package/.agent/agents/email-assistant.md +0 -49
  156. package/.agent/agents/file-manager.md +0 -42
  157. package/.agent/agents/python-developer.md +0 -60
  158. package/.agent/agents/scheduler.md +0 -59
  159. package/.agent/agents/web-developer.md +0 -45
  160. package/.agent/mcp_config_updated.json +0 -12
  161. package/.agent/rules/GEMINI.md +0 -273
  162. package/.agent/rules/allow-rule.md +0 -77
  163. package/.agent/rules/log-rule.md +0 -83
  164. package/.agent/rules/security-rule.md +0 -93
  165. package/.agent/scripts/auto_preview.py +0 -148
  166. package/.agent/scripts/checklist.py +0 -217
  167. package/.agent/scripts/session_manager.py +0 -120
  168. package/.agent/scripts/verify_all.py +0 -327
  169. package/.agent/skills/api-patterns/SKILL.md +0 -81
  170. package/.agent/skills/api-patterns/api-style.md +0 -42
  171. package/.agent/skills/api-patterns/auth.md +0 -24
  172. package/.agent/skills/api-patterns/documentation.md +0 -26
  173. package/.agent/skills/api-patterns/graphql.md +0 -41
  174. package/.agent/skills/api-patterns/rate-limiting.md +0 -31
  175. package/.agent/skills/api-patterns/response.md +0 -37
  176. package/.agent/skills/api-patterns/rest.md +0 -40
  177. package/.agent/skills/api-patterns/scripts/api_validator.py +0 -211
  178. package/.agent/skills/api-patterns/security-testing.md +0 -122
  179. package/.agent/skills/api-patterns/trpc.md +0 -41
  180. package/.agent/skills/api-patterns/versioning.md +0 -22
  181. package/.agent/skills/app-builder/SKILL.md +0 -75
  182. package/.agent/skills/app-builder/agent-coordination.md +0 -71
  183. package/.agent/skills/app-builder/feature-building.md +0 -53
  184. package/.agent/skills/app-builder/project-detection.md +0 -34
  185. package/.agent/skills/app-builder/scaffolding.md +0 -118
  186. package/.agent/skills/app-builder/tech-stack.md +0 -40
  187. package/.agent/skills/app-builder/templates/SKILL.md +0 -39
  188. package/.agent/skills/app-builder/templates/astro-static/TEMPLATE.md +0 -76
  189. package/.agent/skills/app-builder/templates/chrome-extension/TEMPLATE.md +0 -92
  190. package/.agent/skills/app-builder/templates/cli-tool/TEMPLATE.md +0 -88
  191. package/.agent/skills/app-builder/templates/electron-desktop/TEMPLATE.md +0 -88
  192. package/.agent/skills/app-builder/templates/express-api/TEMPLATE.md +0 -83
  193. package/.agent/skills/app-builder/templates/flutter-app/TEMPLATE.md +0 -90
  194. package/.agent/skills/app-builder/templates/monorepo-turborepo/TEMPLATE.md +0 -90
  195. package/.agent/skills/app-builder/templates/nextjs-fullstack/TEMPLATE.md +0 -122
  196. package/.agent/skills/app-builder/templates/nextjs-saas/TEMPLATE.md +0 -122
  197. package/.agent/skills/app-builder/templates/nextjs-static/TEMPLATE.md +0 -169
  198. package/.agent/skills/app-builder/templates/nuxt-app/TEMPLATE.md +0 -134
  199. package/.agent/skills/app-builder/templates/python-fastapi/TEMPLATE.md +0 -83
  200. package/.agent/skills/app-builder/templates/react-native-app/TEMPLATE.md +0 -119
  201. package/.agent/skills/architecture/SKILL.md +0 -55
  202. package/.agent/skills/architecture/context-discovery.md +0 -43
  203. package/.agent/skills/architecture/examples.md +0 -94
  204. package/.agent/skills/architecture/pattern-selection.md +0 -68
  205. package/.agent/skills/architecture/patterns-reference.md +0 -50
  206. package/.agent/skills/architecture/trade-off-analysis.md +0 -77
  207. package/.agent/skills/clean-code/SKILL.md +0 -201
  208. package/.agent/skills/doc.md +0 -177
  209. package/.agent/skills/frontend-design/SKILL.md +0 -418
  210. package/.agent/skills/frontend-design/animation-guide.md +0 -331
  211. package/.agent/skills/frontend-design/color-system.md +0 -311
  212. package/.agent/skills/frontend-design/decision-trees.md +0 -418
  213. package/.agent/skills/frontend-design/motion-graphics.md +0 -306
  214. package/.agent/skills/frontend-design/scripts/accessibility_checker.py +0 -183
  215. package/.agent/skills/frontend-design/scripts/ux_audit.py +0 -722
  216. package/.agent/skills/frontend-design/typography-system.md +0 -345
  217. package/.agent/skills/frontend-design/ux-psychology.md +0 -1116
  218. package/.agent/skills/frontend-design/visual-effects.md +0 -383
  219. package/.agent/skills/i18n-localization/SKILL.md +0 -154
  220. package/.agent/skills/i18n-localization/scripts/i18n_checker.py +0 -241
  221. package/.agent/skills/mcp-builder/SKILL.md +0 -176
  222. package/.agent/skills/web-design-guidelines/SKILL.md +0 -57
  223. package/.agent/workflows/brainstorm.md +0 -113
  224. package/.agent/workflows/create.md +0 -59
  225. package/.agent/workflows/debug.md +0 -103
  226. package/.agent/workflows/deploy.md +0 -176
  227. package/.agent/workflows/enhance.md +0 -63
  228. package/.agent/workflows/orchestrate.md +0 -237
  229. package/.agent/workflows/plan.md +0 -89
  230. package/.agent/workflows/preview.md +0 -81
  231. package/.agent/workflows/simple-test.md +0 -42
  232. package/.agent/workflows/status.md +0 -86
  233. package/.agent/workflows/structured-orchestrate.md +0 -180
  234. package/.agent/workflows/test.md +0 -144
  235. package/.agent/workflows/ui-ux-pro-max.md +0 -296
@@ -1,651 +1,651 @@
1
- /**
2
- * PythonPluginLoader - Python 插件加载器
3
- * 通过 python_execute 工具执行 Python 插件
4
- */
5
-
6
- const fs = require('fs')
7
- const path = require('path')
8
- const { Plugin } = require('../src/core/plugin-base')
9
- const { logger } = require('../src/utils/logger')
10
- const log = logger.child('PythonPluginLoader')
11
- const { z } = require('zod')
12
-
13
- // 将 JSON Schema 转换为 Zod Schema
14
- function jsonSchemaToZod(jsonSchema) {
15
- if (!jsonSchema || jsonSchema.type !== 'object') {
16
- return z.object({})
17
- }
18
-
19
- const properties = jsonSchema.properties || {}
20
- const required = jsonSchema.required || []
21
-
22
- const shape = {}
23
- for (const [key, prop] of Object.entries(properties)) {
24
- let zodType
25
- switch (prop.type) {
26
- case 'string':
27
- zodType = z.string()
28
- break
29
- case 'number':
30
- zodType = z.number()
31
- break
32
- case 'boolean':
33
- zodType = z.boolean()
34
- break
35
- case 'array':
36
- zodType = z.array(z.any())
37
- break
38
- case 'object':
39
- zodType = jsonSchemaToZod(prop)
40
- break
41
- default:
42
- zodType = z.any()
43
- }
44
- // 如果不是 required 字段,则标记为 optional
45
- if (!required.includes(key)) {
46
- zodType = zodType.optional()
47
- }
48
- shape[key] = zodType
49
- }
50
-
51
- return z.object(shape)
52
- }
53
-
54
- class PythonPluginLoader extends Plugin {
55
- constructor(config = {}) {
56
- super()
57
- this.name = 'python-plugin-loader'
58
- this.version = '1.0.0'
59
- this.description = 'Python 插件加载器,属于Python的插件'
60
-
61
- this._agentDir = config.agentDir || '.agent'
62
- this._pythonPlugins = new Map()
63
- this._framework = null
64
- this.system = true
65
- }
66
-
67
- install(framework) {
68
- this._framework = framework
69
- return this
70
- }
71
-
72
- start(framework) {
73
- // 先设置 framework 引用(供 _loadPythonPlugins 使用)
74
- this._framework = framework
75
-
76
- // 加载所有 Python 插件(会注册工具到 ExtensionExecutor)
77
- this._loadPythonPlugins()
78
-
79
- // 监听 agent 创建事件,附加 Python 插件信息到系统提示词
80
- framework.on('agent:created', (agent) => {
81
- this._refreshAgentPythonPluginsPrompt(agent)
82
- })
83
-
84
- // 等待框架就绪后,刷新所有已有 agent 的 Python 插件提示词
85
- if (framework._ready) {
86
- this._refreshAllAgentsPythonPluginsPrompt(framework)
87
- } else {
88
- framework.once('framework:ready', () => {
89
- this._refreshAllAgentsPythonPluginsPrompt(framework)
90
- })
91
- }
92
-
93
- return this
94
- }
95
-
96
- /**
97
- * 构建 Python 插件描述
98
- */
99
- _buildPythonPluginsDescription() {
100
- if (this._pythonPlugins.size === 0) {
101
- return ''
102
- }
103
-
104
- let desc = '【Python 插件】\n'
105
- desc += 'Python 插件通过 ext_call 调用:\n\n'
106
-
107
- for (const [name, plugin] of this._pythonPlugins) {
108
- desc += `插件: ${plugin.info.name} - ${plugin.info.description || ''}\n`
109
- if (plugin.info.tools && Array.isArray(plugin.info.tools)) {
110
- for (const tool of plugin.info.tools) {
111
- const paramsStr = Object.keys(tool.params || {}).join(', ') || '无参数'
112
- desc += ` - ${tool.name}(${paramsStr}): ${tool.description || '无描述'}\n`
113
- }
114
- }
115
- desc += '\n'
116
- }
117
-
118
- desc += '调用格式: ext_call({ plugin: "插件名", tool: "工具名", args: {...} })\n'
119
- return desc.trim()
120
- }
121
-
122
- /**
123
- * 刷新单个 agent 的 Python 插件提示词
124
- */
125
- _refreshAgentPythonPluginsPrompt(agent) {
126
- const existingPrompt = agent._originalPrompt || ''
127
- if (existingPrompt.includes('【Python 插件】')) {
128
- return
129
- }
130
-
131
- const pyDesc = this._buildPythonPluginsDescription()
132
- if (!pyDesc) return
133
-
134
- // 将 Python 插件描述追加到系统提示词
135
- agent.setSystemPrompt(existingPrompt + '\n\n' + pyDesc)
136
- }
137
-
138
- /**
139
- * 刷新所有 agent 的 Python 插件提示词
140
- */
141
- _refreshAllAgentsPythonPluginsPrompt(framework) {
142
- const visited = new Set()
143
-
144
- const traverse = (agent) => {
145
- if (!agent || visited.has(agent)) return
146
- visited.add(agent)
147
- this._refreshAgentPythonPluginsPrompt(agent)
148
-
149
- // 递归处理子 agent
150
- const subAgents = agent.getSubAgents?.() || agent._subAgents || new Map()
151
- for (const [name, subAgentInfo] of subAgents) {
152
- traverse(subAgentInfo.agent)
153
- }
154
- }
155
-
156
- const agents = framework._agents || []
157
- for (const agent of agents) {
158
- traverse(agent)
159
- }
160
- }
161
-
162
- /**
163
- * 加载所有 Python 插件
164
- */
165
- _loadPythonPlugins() {
166
- const pluginsDir = path.join(this._agentDir, 'plugins')
167
- if (!fs.existsSync(pluginsDir)) {
168
- return
169
- }
170
-
171
- const files = fs.readdirSync(pluginsDir).filter(f => f.endsWith('.py'))
172
-
173
- for (const file of files) {
174
- try {
175
- const pluginPath = path.join(pluginsDir, file)
176
- const pluginName = file.replace('.py', '')
177
-
178
- const pluginInfo = this._loadPythonPluginMeta(pluginPath)
179
- if (pluginInfo) {
180
- this._pythonPlugins.set(pluginName, {
181
- name: pluginName,
182
- path: pluginPath,
183
- info: pluginInfo,
184
- code: fs.readFileSync(pluginPath, 'utf-8')
185
- })
186
- log.info(` Loaded: ${pluginName}`)
187
-
188
- // 注册插件的每个工具
189
- if (pluginInfo.tools && Array.isArray(pluginInfo.tools)) {
190
- for (const tool of pluginInfo.tools) {
191
- this._registerPythonTool(pluginName, tool)
192
- }
193
- }
194
- }
195
- } catch (err) {
196
- log.error(` Failed to load ${file}:`, err.message)
197
- }
198
- }
199
-
200
- log.info(` Total Python plugins: ${this._pythonPlugins.size}`)
201
- }
202
-
203
- /**
204
- * 注册 Python 插件的工具
205
- */
206
- _registerPythonTool(pluginName, tool) {
207
- if (!tool.name) return
208
-
209
- try {
210
- this.registerTool({
211
- name: tool.name,
212
- description: tool.description || '',
213
- pluginName: pluginName,
214
- inputSchema: this._parseToolParams(tool.params || {}),
215
- execute: async (args) => {
216
- return this._executePythonTool(pluginName, tool.name, args)
217
- }
218
- })
219
- } catch (err) {
220
- log.error(` Failed to register tool ${tool.name}:`, err.message)
221
- }
222
- }
223
-
224
- /**
225
- * 解析工具参数 schema
226
- */
227
- _parseToolParams(params) {
228
- // 构建 JSON Schema 格式
229
- const properties = {}
230
- const required = []
231
-
232
- for (const [key, value] of Object.entries(params)) {
233
- let type = 'string'
234
- if (typeof value === 'boolean') type = 'boolean'
235
- else if (typeof value === 'number') type = 'number'
236
- else if (Array.isArray(value)) type = 'array'
237
- else if (typeof value === 'object') type = 'object'
238
-
239
- properties[key] = {
240
- type,
241
- description: ''
242
- }
243
- required.push(key)
244
- }
245
-
246
- const jsonSchema = {
247
- type: 'object',
248
- properties,
249
- required
250
- }
251
-
252
- // 转换为 Zod Schema 以兼容 AI SDK
253
- return jsonSchemaToZod(jsonSchema)
254
- }
255
-
256
- /**
257
- * 加载单个 Python 插件的元信息
258
- * 支持 PLUGIN 和 TOOLS 格式
259
- */
260
- _loadPythonPluginMeta(pluginPath) {
261
- const code = fs.readFileSync(pluginPath, 'utf-8')
262
-
263
- const pluginIdx = code.indexOf('PLUGIN')
264
- const toolsIdx = code.indexOf('TOOLS')
265
-
266
- if (pluginIdx === -1 && toolsIdx === -1) {
267
- console.warn(`[PythonPluginLoader] ${path.basename(pluginPath)}: no PLUGIN or TOOLS found`)
268
- return null
269
- }
270
-
271
- try {
272
- const info = {}
273
-
274
- // 解析 PLUGIN
275
- if (pluginIdx !== -1) {
276
- const pluginData = this._extractPythonDict(code, pluginIdx)
277
- if (pluginData && pluginData.name) {
278
- info.name = pluginData.name
279
- info.version = pluginData.version || '1.0.0'
280
- info.description = pluginData.description || ''
281
- }
282
- }
283
-
284
- // 解析 TOOLS
285
- if (toolsIdx !== -1) {
286
- const toolsData = this._extractPythonList(code, toolsIdx)
287
- if (toolsData && Array.isArray(toolsData)) {
288
- info.tools = toolsData
289
- }
290
- }
291
-
292
- if (!info.name || !info.tools) {
293
- console.warn(`[PythonPluginLoader] ${path.basename(pluginPath)}: invalid format - name: ${info.name}, tools: ${info.tools ? info.tools.length : 0}`)
294
- return null
295
- }
296
-
297
- return info
298
- } catch (err) {
299
- console.warn(`[PythonPluginLoader] Failed to parse ${path.basename(pluginPath)}:`, err.message)
300
- return null
301
- }
302
- }
303
-
304
- /**
305
- * 从指定位置提取 Python 字典
306
- */
307
- _extractPythonDict(code, startIdx) {
308
- const braceStart = code.indexOf('{', startIdx)
309
- if (braceStart === -1) return null
310
-
311
- let braceCount = 0
312
- let endIdx = -1
313
- for (let i = braceStart; i < code.length; i++) {
314
- if (code[i] === '{') braceCount++
315
- else if (code[i] === '}') {
316
- braceCount--
317
- if (braceCount === 0) {
318
- endIdx = i
319
- break
320
- }
321
- }
322
- }
323
-
324
- if (endIdx === -1) return null
325
- return this._parsePythonDict(code.substring(braceStart, endIdx + 1))
326
- }
327
-
328
- /**
329
- * 从指定位置提取 Python 列表
330
- */
331
- _extractPythonList(code, startIdx) {
332
- const bracketStart = code.indexOf('[', startIdx)
333
- if (bracketStart === -1) return null
334
-
335
- let bracketCount = 0
336
- let endIdx = -1
337
- for (let i = bracketStart; i < code.length; i++) {
338
- if (code[i] === '[') bracketCount++
339
- else if (code[i] === ']') {
340
- bracketCount--
341
- if (bracketCount === 0) {
342
- endIdx = i
343
- break
344
- }
345
- }
346
- }
347
-
348
- if (endIdx === -1) return null
349
- return this._parsePythonList(code.substring(bracketStart, endIdx + 1))
350
- }
351
-
352
- /**
353
- * 解析 Python 字典为 JS 对象
354
- */
355
- _parsePythonDict(str) {
356
- str = str.replace(/#.*$/gm, '').trim()
357
- if (!str || str === '{}') return {}
358
-
359
- // 直接尝试 JSON.parse(PLUGIN 字典是有效的 JSON 格式)
360
- try {
361
- return JSON.parse(str)
362
- } catch {
363
- // 如果失败,使用手动解析
364
- }
365
-
366
- const result = {}
367
- let i = 0
368
- let key = null
369
- let valueStart = -1
370
- let depth = 0
371
- let inString = false
372
- let stringChar = null
373
- let escaped = false
374
-
375
- while (i < str.length) {
376
- const char = str[i]
377
-
378
- if (escaped) {
379
- escaped = false
380
- i++
381
- continue
382
- }
383
-
384
- if (char === '\\' && inString) {
385
- escaped = true
386
- i++
387
- continue
388
- }
389
-
390
- if ((char === '"' || char === "'") && !inString) {
391
- inString = true
392
- stringChar = char
393
- i++
394
- continue
395
- }
396
-
397
- if (inString && char === stringChar) {
398
- inString = false
399
- i++
400
- continue
401
- }
402
-
403
- if (inString) {
404
- i++
405
- continue
406
- }
407
-
408
- if (char === '{' || char === '[') {
409
- if (depth === 0 && char === '{') {
410
- valueStart = i + 1
411
- }
412
- depth++
413
- i++
414
- continue
415
- }
416
- if (char === '}' || char === ']') {
417
- if (depth > 0) {
418
- depth--
419
- if (depth === 0 && char === '}') {
420
- if (key !== null && valueStart !== -1) {
421
- const valueStr = str.substring(valueStart, i).trim()
422
- if (valueStr) result[key] = this._parseValue(valueStr)
423
- }
424
- }
425
- }
426
- i++
427
- continue
428
- }
429
- if (depth > 0) {
430
- i++
431
- continue
432
- }
433
-
434
- if (char === ':') {
435
- key = str.substring(valueStart, i).trim().replace(/["']/g, '')
436
- valueStart = i + 1
437
- i++
438
- continue
439
- }
440
-
441
- if (char === ',') {
442
- if (key !== null && valueStart !== -1) {
443
- const valueStr = str.substring(valueStart, i).trim()
444
- if (valueStr) result[key] = this._parseValue(valueStr)
445
- }
446
- key = null
447
- valueStart = i + 1
448
- i++
449
- continue
450
- }
451
-
452
- i++
453
- }
454
-
455
- return result
456
- }
457
-
458
- /**
459
- * 解析 Python 列表为 JS 数组
460
- */
461
- _parsePythonList(str) {
462
- str = str.replace(/#.*$/gm, '').trim()
463
- if (!str || str === '[]') return []
464
-
465
- // 直接尝试 JSON.parse(TOOLS 列表是有效的 JSON 格式)
466
- try {
467
- return JSON.parse(str)
468
- } catch {
469
- // 如果失败,使用手动解析
470
- }
471
-
472
- const result = []
473
- let i = 0
474
- let itemStart = 1
475
- let depth = 0
476
- let inString = false
477
- let stringChar = null
478
- let escaped = false
479
-
480
- while (i < str.length) {
481
- const char = str[i]
482
-
483
- if (escaped) {
484
- escaped = false
485
- i++
486
- continue
487
- }
488
-
489
- if (char === '\\' && inString) {
490
- escaped = true
491
- i++
492
- continue
493
- }
494
-
495
- if ((char === '"' || char === "'") && !inString) {
496
- inString = true
497
- stringChar = char
498
- i++
499
- continue
500
- }
501
-
502
- if (inString && char === stringChar) {
503
- inString = false
504
- i++
505
- continue
506
- }
507
-
508
- if (inString) {
509
- i++
510
- continue
511
- }
512
-
513
- if (char === '{' || char === '[') {
514
- if (depth === 0) itemStart = i
515
- depth++
516
- i++
517
- continue
518
- }
519
- if (char === '}' || char === ']') {
520
- if (depth > 0) {
521
- depth--
522
- if (depth === 0) {
523
- const itemStr = str.substring(itemStart, i + 1).trim()
524
- if (itemStr && itemStr !== ',') {
525
- if (itemStr.startsWith('{')) {
526
- result.push(this._parsePythonDict(itemStr))
527
- } else {
528
- result.push(this._parseValue(itemStr))
529
- }
530
- }
531
- itemStart = i + 1
532
- }
533
- }
534
- i++
535
- continue
536
- }
537
- if (depth > 0) {
538
- i++
539
- continue
540
- }
541
-
542
- if (char === ',') {
543
- const itemStr = str.substring(itemStart, i).trim()
544
- if (itemStr) {
545
- if (itemStr.startsWith('{')) {
546
- result.push(this._parsePythonDict(itemStr))
547
- } else {
548
- result.push(this._parseValue(itemStr))
549
- }
550
- }
551
- itemStart = i + 1
552
- }
553
-
554
- i++
555
- }
556
-
557
- return result
558
- }
559
-
560
- /**
561
- * 解析单个值
562
- */
563
- _parseValue(str) {
564
- str = str.trim()
565
- if (!str) return null
566
-
567
- if (str.startsWith('{')) {
568
- return this._parsePythonDict(str)
569
- }
570
-
571
- if (str.startsWith('"') || str.startsWith("'")) {
572
- return str.slice(1, -1)
573
- }
574
-
575
- if (str === 'True') return true
576
- if (str === 'False') return false
577
- if (str === 'None') return null
578
-
579
- if (!isNaN(str)) return Number(str)
580
-
581
- return str
582
- }
583
-
584
- /**
585
- * 执行 Python 插件工具
586
- */
587
- async _executePythonTool(pluginName, toolName, params) {
588
- const plugin = this._pythonPlugins.get(pluginName)
589
- if (!plugin) {
590
- return { success: false, error: `Plugin '${pluginName}' not found` }
591
- }
592
-
593
- // 构建执行代码 - 使用 base64 避免转义问题
594
- const pluginDir = path.dirname(plugin.path)
595
- const codeBase64 = Buffer.from(plugin.code, 'utf-8').toString('base64')
596
-
597
- const execCode = `
598
- import sys
599
- import base64
600
- sys.path.insert(0, r'${pluginDir}')
601
-
602
- # 加载插件代码
603
- plugin_code = base64.b64decode('${codeBase64}').decode('utf-8')
604
- exec(compile(plugin_code, '${pluginName}.py', 'exec'))
605
-
606
- # 执行工具
607
- result = execute_tool('${toolName}', ${JSON.stringify(params)})
608
- print(result)
609
- `
610
-
611
- try {
612
- // 通过 python-execute 工具执行
613
- const pythonExe = this._framework.toolRegistry._tools.get('python-execute')
614
- if (!pythonExe) {
615
- return { success: false, error: 'python-execute tool not found. Please install python-executor-plugin.' }
616
- }
617
-
618
- const pythonResult = await pythonExe.execute({ code: execCode })
619
-
620
- if (pythonResult.success) {
621
- try {
622
- // 尝试解析 JSON 结果
623
- const parsed = JSON.parse(pythonResult.output)
624
- return parsed
625
- } catch {
626
- return { success: true, output: pythonResult.output }
627
- }
628
- } else {
629
- return { success: false, error: pythonResult.error }
630
- }
631
- } catch (err) {
632
- return { success: false, error: err.message }
633
- }
634
- }
635
-
636
- /**
637
- * 获取所有已加载的 Python 插件
638
- */
639
- getPythonPlugins() {
640
- return Array.from(this._pythonPlugins.values())
641
- }
642
-
643
- /**
644
- * 获取 Python 插件信息
645
- */
646
- getPythonPlugin(name) {
647
- return this._pythonPlugins.get(name)
648
- }
649
- }
650
-
651
- module.exports = PythonPluginLoader
1
+ /**
2
+ * PythonPluginLoader - Python 插件加载器
3
+ * 通过 python_execute 工具执行 Python 插件
4
+ */
5
+
6
+ const fs = require('fs')
7
+ const path = require('path')
8
+ const { Plugin } = require('../src/core/plugin-base')
9
+ const { logger } = require('../src/utils/logger')
10
+ const log = logger.child('PythonPluginLoader')
11
+ const { z } = require('zod')
12
+
13
+ // 将 JSON Schema 转换为 Zod Schema
14
+ function jsonSchemaToZod(jsonSchema) {
15
+ if (!jsonSchema || jsonSchema.type !== 'object') {
16
+ return z.object({})
17
+ }
18
+
19
+ const properties = jsonSchema.properties || {}
20
+ const required = jsonSchema.required || []
21
+
22
+ const shape = {}
23
+ for (const [key, prop] of Object.entries(properties)) {
24
+ let zodType
25
+ switch (prop.type) {
26
+ case 'string':
27
+ zodType = z.string()
28
+ break
29
+ case 'number':
30
+ zodType = z.number()
31
+ break
32
+ case 'boolean':
33
+ zodType = z.boolean()
34
+ break
35
+ case 'array':
36
+ zodType = z.array(z.any())
37
+ break
38
+ case 'object':
39
+ zodType = jsonSchemaToZod(prop)
40
+ break
41
+ default:
42
+ zodType = z.any()
43
+ }
44
+ // 如果不是 required 字段,则标记为 optional
45
+ if (!required.includes(key)) {
46
+ zodType = zodType.optional()
47
+ }
48
+ shape[key] = zodType
49
+ }
50
+
51
+ return z.object(shape)
52
+ }
53
+
54
+ class PythonPluginLoader extends Plugin {
55
+ constructor(config = {}) {
56
+ super()
57
+ this.name = 'python-plugin-loader'
58
+ this.version = '1.0.0'
59
+ this.description = 'Python 插件加载器,属于Python的插件'
60
+
61
+ this._agentDir = config.agentDir || '.agent'
62
+ this._pythonPlugins = new Map()
63
+ this._framework = null
64
+ this.system = true
65
+ }
66
+
67
+ install(framework) {
68
+ this._framework = framework
69
+ return this
70
+ }
71
+
72
+ start(framework) {
73
+ // 先设置 framework 引用(供 _loadPythonPlugins 使用)
74
+ this._framework = framework
75
+
76
+ // 加载所有 Python 插件(会注册工具到 ExtensionExecutor)
77
+ this._loadPythonPlugins()
78
+
79
+ // 监听 agent 创建事件,附加 Python 插件信息到系统提示词
80
+ framework.on('agent:created', (agent) => {
81
+ this._refreshAgentPythonPluginsPrompt(agent)
82
+ })
83
+
84
+ // 等待框架就绪后,刷新所有已有 agent 的 Python 插件提示词
85
+ if (framework._ready) {
86
+ this._refreshAllAgentsPythonPluginsPrompt(framework)
87
+ } else {
88
+ framework.once('framework:ready', () => {
89
+ this._refreshAllAgentsPythonPluginsPrompt(framework)
90
+ })
91
+ }
92
+
93
+ return this
94
+ }
95
+
96
+ /**
97
+ * 构建 Python 插件描述
98
+ */
99
+ _buildPythonPluginsDescription() {
100
+ if (this._pythonPlugins.size === 0) {
101
+ return ''
102
+ }
103
+
104
+ let desc = '【Python 插件】\n'
105
+ desc += 'Python 插件通过 ext_call 调用:\n\n'
106
+
107
+ for (const [name, plugin] of this._pythonPlugins) {
108
+ desc += `插件: ${plugin.info.name} - ${plugin.info.description || ''}\n`
109
+ if (plugin.info.tools && Array.isArray(plugin.info.tools)) {
110
+ for (const tool of plugin.info.tools) {
111
+ const paramsStr = Object.keys(tool.params || {}).join(', ') || '无参数'
112
+ desc += ` - ${tool.name}(${paramsStr}): ${tool.description || '无描述'}\n`
113
+ }
114
+ }
115
+ desc += '\n'
116
+ }
117
+
118
+ desc += '调用格式: ext_call({ plugin: "插件名", tool: "工具名", args: {...} })\n'
119
+ return desc.trim()
120
+ }
121
+
122
+ /**
123
+ * 刷新单个 agent 的 Python 插件提示词
124
+ */
125
+ _refreshAgentPythonPluginsPrompt(agent) {
126
+ const existingPrompt = agent._originalPrompt || ''
127
+ if (existingPrompt.includes('【Python 插件】')) {
128
+ return
129
+ }
130
+
131
+ const pyDesc = this._buildPythonPluginsDescription()
132
+ if (!pyDesc) return
133
+
134
+ // 将 Python 插件描述追加到系统提示词
135
+ agent.setSystemPrompt(existingPrompt + '\n\n' + pyDesc)
136
+ }
137
+
138
+ /**
139
+ * 刷新所有 agent 的 Python 插件提示词
140
+ */
141
+ _refreshAllAgentsPythonPluginsPrompt(framework) {
142
+ const visited = new Set()
143
+
144
+ const traverse = (agent) => {
145
+ if (!agent || visited.has(agent)) return
146
+ visited.add(agent)
147
+ this._refreshAgentPythonPluginsPrompt(agent)
148
+
149
+ // 递归处理子 agent
150
+ const subAgents = agent.getSubAgents?.() || agent._subAgents || new Map()
151
+ for (const [name, subAgentInfo] of subAgents) {
152
+ traverse(subAgentInfo.agent)
153
+ }
154
+ }
155
+
156
+ const agents = framework._agents || []
157
+ for (const agent of agents) {
158
+ traverse(agent)
159
+ }
160
+ }
161
+
162
+ /**
163
+ * 加载所有 Python 插件
164
+ */
165
+ _loadPythonPlugins() {
166
+ const pluginsDir = path.join(this._agentDir, 'plugins')
167
+ if (!fs.existsSync(pluginsDir)) {
168
+ return
169
+ }
170
+
171
+ const files = fs.readdirSync(pluginsDir).filter(f => f.endsWith('.py'))
172
+
173
+ for (const file of files) {
174
+ try {
175
+ const pluginPath = path.join(pluginsDir, file)
176
+ const pluginName = file.replace('.py', '')
177
+
178
+ const pluginInfo = this._loadPythonPluginMeta(pluginPath)
179
+ if (pluginInfo) {
180
+ this._pythonPlugins.set(pluginName, {
181
+ name: pluginName,
182
+ path: pluginPath,
183
+ info: pluginInfo,
184
+ code: fs.readFileSync(pluginPath, 'utf-8')
185
+ })
186
+ log.info(` Loaded: ${pluginName}`)
187
+
188
+ // 注册插件的每个工具
189
+ if (pluginInfo.tools && Array.isArray(pluginInfo.tools)) {
190
+ for (const tool of pluginInfo.tools) {
191
+ this._registerPythonTool(pluginName, tool)
192
+ }
193
+ }
194
+ }
195
+ } catch (err) {
196
+ log.error(` Failed to load ${file}:`, err.message)
197
+ }
198
+ }
199
+
200
+ log.info(` Total Python plugins: ${this._pythonPlugins.size}`)
201
+ }
202
+
203
+ /**
204
+ * 注册 Python 插件的工具
205
+ */
206
+ _registerPythonTool(pluginName, tool) {
207
+ if (!tool.name) return
208
+
209
+ try {
210
+ this.registerTool({
211
+ name: tool.name,
212
+ description: tool.description || '',
213
+ pluginName: pluginName,
214
+ inputSchema: this._parseToolParams(tool.params || {}),
215
+ execute: async (args) => {
216
+ return this._executePythonTool(pluginName, tool.name, args)
217
+ }
218
+ })
219
+ } catch (err) {
220
+ log.error(` Failed to register tool ${tool.name}:`, err.message)
221
+ }
222
+ }
223
+
224
+ /**
225
+ * 解析工具参数 schema
226
+ */
227
+ _parseToolParams(params) {
228
+ // 构建 JSON Schema 格式
229
+ const properties = {}
230
+ const required = []
231
+
232
+ for (const [key, value] of Object.entries(params)) {
233
+ let type = 'string'
234
+ if (typeof value === 'boolean') type = 'boolean'
235
+ else if (typeof value === 'number') type = 'number'
236
+ else if (Array.isArray(value)) type = 'array'
237
+ else if (typeof value === 'object') type = 'object'
238
+
239
+ properties[key] = {
240
+ type,
241
+ description: ''
242
+ }
243
+ required.push(key)
244
+ }
245
+
246
+ const jsonSchema = {
247
+ type: 'object',
248
+ properties,
249
+ required
250
+ }
251
+
252
+ // 转换为 Zod Schema 以兼容 AI SDK
253
+ return jsonSchemaToZod(jsonSchema)
254
+ }
255
+
256
+ /**
257
+ * 加载单个 Python 插件的元信息
258
+ * 支持 PLUGIN 和 TOOLS 格式
259
+ */
260
+ _loadPythonPluginMeta(pluginPath) {
261
+ const code = fs.readFileSync(pluginPath, 'utf-8')
262
+
263
+ const pluginIdx = code.indexOf('PLUGIN')
264
+ const toolsIdx = code.indexOf('TOOLS')
265
+
266
+ if (pluginIdx === -1 && toolsIdx === -1) {
267
+ console.warn(`[PythonPluginLoader] ${path.basename(pluginPath)}: no PLUGIN or TOOLS found`)
268
+ return null
269
+ }
270
+
271
+ try {
272
+ const info = {}
273
+
274
+ // 解析 PLUGIN
275
+ if (pluginIdx !== -1) {
276
+ const pluginData = this._extractPythonDict(code, pluginIdx)
277
+ if (pluginData && pluginData.name) {
278
+ info.name = pluginData.name
279
+ info.version = pluginData.version || '1.0.0'
280
+ info.description = pluginData.description || ''
281
+ }
282
+ }
283
+
284
+ // 解析 TOOLS
285
+ if (toolsIdx !== -1) {
286
+ const toolsData = this._extractPythonList(code, toolsIdx)
287
+ if (toolsData && Array.isArray(toolsData)) {
288
+ info.tools = toolsData
289
+ }
290
+ }
291
+
292
+ if (!info.name || !info.tools) {
293
+ console.warn(`[PythonPluginLoader] ${path.basename(pluginPath)}: invalid format - name: ${info.name}, tools: ${info.tools ? info.tools.length : 0}`)
294
+ return null
295
+ }
296
+
297
+ return info
298
+ } catch (err) {
299
+ console.warn(`[PythonPluginLoader] Failed to parse ${path.basename(pluginPath)}:`, err.message)
300
+ return null
301
+ }
302
+ }
303
+
304
+ /**
305
+ * 从指定位置提取 Python 字典
306
+ */
307
+ _extractPythonDict(code, startIdx) {
308
+ const braceStart = code.indexOf('{', startIdx)
309
+ if (braceStart === -1) return null
310
+
311
+ let braceCount = 0
312
+ let endIdx = -1
313
+ for (let i = braceStart; i < code.length; i++) {
314
+ if (code[i] === '{') braceCount++
315
+ else if (code[i] === '}') {
316
+ braceCount--
317
+ if (braceCount === 0) {
318
+ endIdx = i
319
+ break
320
+ }
321
+ }
322
+ }
323
+
324
+ if (endIdx === -1) return null
325
+ return this._parsePythonDict(code.substring(braceStart, endIdx + 1))
326
+ }
327
+
328
+ /**
329
+ * 从指定位置提取 Python 列表
330
+ */
331
+ _extractPythonList(code, startIdx) {
332
+ const bracketStart = code.indexOf('[', startIdx)
333
+ if (bracketStart === -1) return null
334
+
335
+ let bracketCount = 0
336
+ let endIdx = -1
337
+ for (let i = bracketStart; i < code.length; i++) {
338
+ if (code[i] === '[') bracketCount++
339
+ else if (code[i] === ']') {
340
+ bracketCount--
341
+ if (bracketCount === 0) {
342
+ endIdx = i
343
+ break
344
+ }
345
+ }
346
+ }
347
+
348
+ if (endIdx === -1) return null
349
+ return this._parsePythonList(code.substring(bracketStart, endIdx + 1))
350
+ }
351
+
352
+ /**
353
+ * 解析 Python 字典为 JS 对象
354
+ */
355
+ _parsePythonDict(str) {
356
+ str = str.replace(/#.*$/gm, '').trim()
357
+ if (!str || str === '{}') return {}
358
+
359
+ // 直接尝试 JSON.parse(PLUGIN 字典是有效的 JSON 格式)
360
+ try {
361
+ return JSON.parse(str)
362
+ } catch {
363
+ // 如果失败,使用手动解析
364
+ }
365
+
366
+ const result = {}
367
+ let i = 0
368
+ let key = null
369
+ let valueStart = -1
370
+ let depth = 0
371
+ let inString = false
372
+ let stringChar = null
373
+ let escaped = false
374
+
375
+ while (i < str.length) {
376
+ const char = str[i]
377
+
378
+ if (escaped) {
379
+ escaped = false
380
+ i++
381
+ continue
382
+ }
383
+
384
+ if (char === '\\' && inString) {
385
+ escaped = true
386
+ i++
387
+ continue
388
+ }
389
+
390
+ if ((char === '"' || char === "'") && !inString) {
391
+ inString = true
392
+ stringChar = char
393
+ i++
394
+ continue
395
+ }
396
+
397
+ if (inString && char === stringChar) {
398
+ inString = false
399
+ i++
400
+ continue
401
+ }
402
+
403
+ if (inString) {
404
+ i++
405
+ continue
406
+ }
407
+
408
+ if (char === '{' || char === '[') {
409
+ if (depth === 0 && char === '{') {
410
+ valueStart = i + 1
411
+ }
412
+ depth++
413
+ i++
414
+ continue
415
+ }
416
+ if (char === '}' || char === ']') {
417
+ if (depth > 0) {
418
+ depth--
419
+ if (depth === 0 && char === '}') {
420
+ if (key !== null && valueStart !== -1) {
421
+ const valueStr = str.substring(valueStart, i).trim()
422
+ if (valueStr) result[key] = this._parseValue(valueStr)
423
+ }
424
+ }
425
+ }
426
+ i++
427
+ continue
428
+ }
429
+ if (depth > 0) {
430
+ i++
431
+ continue
432
+ }
433
+
434
+ if (char === ':') {
435
+ key = str.substring(valueStart, i).trim().replace(/["']/g, '')
436
+ valueStart = i + 1
437
+ i++
438
+ continue
439
+ }
440
+
441
+ if (char === ',') {
442
+ if (key !== null && valueStart !== -1) {
443
+ const valueStr = str.substring(valueStart, i).trim()
444
+ if (valueStr) result[key] = this._parseValue(valueStr)
445
+ }
446
+ key = null
447
+ valueStart = i + 1
448
+ i++
449
+ continue
450
+ }
451
+
452
+ i++
453
+ }
454
+
455
+ return result
456
+ }
457
+
458
+ /**
459
+ * 解析 Python 列表为 JS 数组
460
+ */
461
+ _parsePythonList(str) {
462
+ str = str.replace(/#.*$/gm, '').trim()
463
+ if (!str || str === '[]') return []
464
+
465
+ // 直接尝试 JSON.parse(TOOLS 列表是有效的 JSON 格式)
466
+ try {
467
+ return JSON.parse(str)
468
+ } catch {
469
+ // 如果失败,使用手动解析
470
+ }
471
+
472
+ const result = []
473
+ let i = 0
474
+ let itemStart = 1
475
+ let depth = 0
476
+ let inString = false
477
+ let stringChar = null
478
+ let escaped = false
479
+
480
+ while (i < str.length) {
481
+ const char = str[i]
482
+
483
+ if (escaped) {
484
+ escaped = false
485
+ i++
486
+ continue
487
+ }
488
+
489
+ if (char === '\\' && inString) {
490
+ escaped = true
491
+ i++
492
+ continue
493
+ }
494
+
495
+ if ((char === '"' || char === "'") && !inString) {
496
+ inString = true
497
+ stringChar = char
498
+ i++
499
+ continue
500
+ }
501
+
502
+ if (inString && char === stringChar) {
503
+ inString = false
504
+ i++
505
+ continue
506
+ }
507
+
508
+ if (inString) {
509
+ i++
510
+ continue
511
+ }
512
+
513
+ if (char === '{' || char === '[') {
514
+ if (depth === 0) itemStart = i
515
+ depth++
516
+ i++
517
+ continue
518
+ }
519
+ if (char === '}' || char === ']') {
520
+ if (depth > 0) {
521
+ depth--
522
+ if (depth === 0) {
523
+ const itemStr = str.substring(itemStart, i + 1).trim()
524
+ if (itemStr && itemStr !== ',') {
525
+ if (itemStr.startsWith('{')) {
526
+ result.push(this._parsePythonDict(itemStr))
527
+ } else {
528
+ result.push(this._parseValue(itemStr))
529
+ }
530
+ }
531
+ itemStart = i + 1
532
+ }
533
+ }
534
+ i++
535
+ continue
536
+ }
537
+ if (depth > 0) {
538
+ i++
539
+ continue
540
+ }
541
+
542
+ if (char === ',') {
543
+ const itemStr = str.substring(itemStart, i).trim()
544
+ if (itemStr) {
545
+ if (itemStr.startsWith('{')) {
546
+ result.push(this._parsePythonDict(itemStr))
547
+ } else {
548
+ result.push(this._parseValue(itemStr))
549
+ }
550
+ }
551
+ itemStart = i + 1
552
+ }
553
+
554
+ i++
555
+ }
556
+
557
+ return result
558
+ }
559
+
560
+ /**
561
+ * 解析单个值
562
+ */
563
+ _parseValue(str) {
564
+ str = str.trim()
565
+ if (!str) return null
566
+
567
+ if (str.startsWith('{')) {
568
+ return this._parsePythonDict(str)
569
+ }
570
+
571
+ if (str.startsWith('"') || str.startsWith("'")) {
572
+ return str.slice(1, -1)
573
+ }
574
+
575
+ if (str === 'True') return true
576
+ if (str === 'False') return false
577
+ if (str === 'None') return null
578
+
579
+ if (!isNaN(str)) return Number(str)
580
+
581
+ return str
582
+ }
583
+
584
+ /**
585
+ * 执行 Python 插件工具
586
+ */
587
+ async _executePythonTool(pluginName, toolName, params) {
588
+ const plugin = this._pythonPlugins.get(pluginName)
589
+ if (!plugin) {
590
+ return { success: false, error: `Plugin '${pluginName}' not found` }
591
+ }
592
+
593
+ // 构建执行代码 - 使用 base64 避免转义问题
594
+ const pluginDir = path.dirname(plugin.path)
595
+ const codeBase64 = Buffer.from(plugin.code, 'utf-8').toString('base64')
596
+
597
+ const execCode = `
598
+ import sys
599
+ import base64
600
+ sys.path.insert(0, r'${pluginDir}')
601
+
602
+ # 加载插件代码
603
+ plugin_code = base64.b64decode('${codeBase64}').decode('utf-8')
604
+ exec(compile(plugin_code, '${pluginName}.py', 'exec'))
605
+
606
+ # 执行工具
607
+ result = execute_tool('${toolName}', ${JSON.stringify(params)})
608
+ print(result)
609
+ `
610
+
611
+ try {
612
+ // 通过 python-execute 工具执行
613
+ const pythonExe = this._framework.toolRegistry._tools.get('python-execute')
614
+ if (!pythonExe) {
615
+ return { success: false, error: 'python-execute tool not found. Please install python-executor-plugin.' }
616
+ }
617
+
618
+ const pythonResult = await pythonExe.execute({ code: execCode })
619
+
620
+ if (pythonResult.success) {
621
+ try {
622
+ // 尝试解析 JSON 结果
623
+ const parsed = JSON.parse(pythonResult.output)
624
+ return parsed
625
+ } catch {
626
+ return { success: true, output: pythonResult.output }
627
+ }
628
+ } else {
629
+ return { success: false, error: pythonResult.error }
630
+ }
631
+ } catch (err) {
632
+ return { success: false, error: err.message }
633
+ }
634
+ }
635
+
636
+ /**
637
+ * 获取所有已加载的 Python 插件
638
+ */
639
+ getPythonPlugins() {
640
+ return Array.from(this._pythonPlugins.values())
641
+ }
642
+
643
+ /**
644
+ * 获取 Python 插件信息
645
+ */
646
+ getPythonPlugin(name) {
647
+ return this._pythonPlugins.get(name)
648
+ }
649
+ }
650
+
651
+ module.exports = PythonPluginLoader