foliko 1.1.13 → 1.1.15

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 (102) hide show
  1. package/.agent/data/plugins-state.json +1 -1
  2. package/.agent/data/weixin/images/file_1776188148383jpg +0 -0
  3. package/.agent/data/weixin/images/file_1776188458326.jpg +0 -0
  4. package/.agent/data/weixin/images/file_1776188689423.jpg +0 -0
  5. package/.agent/data/weixin/images/file_1776188813604.jpg +0 -0
  6. package/.agent/data/weixin/images/file_1776189097450.jpg +0 -0
  7. package/.agent/data/weixin/videos/file_1776188318431.mp4 +0 -0
  8. package/.agent/mcp_config.json +7 -0
  9. package/.agent/memory/feedback/mnxe0cxc-14l6q5.md +17 -0
  10. package/.agent/memory/feedback/mnxe11pa-nxf577.md +9 -0
  11. package/.agent/memory/feedback/mnxe1an2-84faff.md +9 -0
  12. package/.agent/memory/feedback/mnxgcfj0-qg3wjc.md +9 -0
  13. package/.agent/memory/feedback/mnxgcn3y-40mqss.md +9 -0
  14. package/.agent/memory/feedback/mnxgcxq9-jm7ydl.md +9 -0
  15. package/.agent/memory/feedback/mnxgdyfj-pzjvkb.md +9 -0
  16. package/.agent/memory/feedback/mnxge3z1-7vyit1.md +9 -0
  17. package/.agent/memory/feedback/mnxhrg28-41hhjr.md +9 -0
  18. package/.agent/memory/feedback/mnxhrx0e-yth94k.md +9 -0
  19. package/.agent/memory/feedback/mnxhs3jd-rvx8aq.md +9 -0
  20. package/.agent/memory/feedback/mnxhs7p7-g5rtn9.md +9 -0
  21. package/.agent/memory/feedback/mnxhslx5-oqwuhr.md +9 -0
  22. package/.agent/memory/feedback/mnxhsvd6-nuyvvc.md +9 -0
  23. package/.agent/memory/project/mnxegq6z-5fc64w.md +22 -0
  24. package/.agent/memory/project/mnxh2w4r-le9hur.md +17 -0
  25. package/.agent/memory/project/mnxhq2yv-9qa8ay.md +31 -0
  26. package/.agent/memory/project/mnxhql11-iaun2o.md +34 -0
  27. package/.agent/memory/project/mnxhr78p-jpg7eq.md +23 -0
  28. package/.agent/memory/reference/mnxe0oa9-p6wzk6.md +27 -0
  29. package/.agent/memory/reference/mnxehcll-kcrmpf.md +29 -0
  30. package/.agent/memory/reference/mnxei0ts-jw091y.md +18 -0
  31. package/.agent/memory/reference/mnxfnrr4-rski36.md +40 -0
  32. package/.agent/memory/reference/mnxfo6n5-af9zls.md +18 -0
  33. package/.agent/memory/reference/mnxh2ady-u6cmvk.md +61 -0
  34. package/.agent/memory/reference/mnxhqdqh-ucsbsk.md +31 -0
  35. package/.agent/memory/reference/mnxiixyp-rz2gvw.md +34 -0
  36. package/.agent/memory/user/mnxhqxk3-vjjhlf.md +23 -0
  37. package/.agent/sessions/cli_default.json +11 -639
  38. package/.agent/sessions/weixin_o9cq80zgZqKPA2-s59PN43GdDy1w@im.wechat.json +25 -0
  39. package/.claude/settings.local.json +23 -1
  40. package/cli/src/commands/chat.js +9 -15
  41. package/cli/src/ui/chat-ui.js +40 -71
  42. package/package.json +4 -2
  43. package/plugins/default-plugins.js +5 -5
  44. package/plugins/file-system-plugin.js +1 -1
  45. package/plugins/memory-plugin.js +12 -12
  46. package/plugins/plugin-manager-plugin.js +1 -0
  47. package/plugins/subagent-plugin.js +55 -1
  48. package/plugins/telegram-plugin.js +9 -6
  49. package/plugins/weixin-plugin.js +75 -78
  50. package/src/core/agent-chat.js +468 -1612
  51. package/src/core/agent.js +53 -134
  52. package/src/core/chat-session.js +423 -0
  53. package/src/core/context-compressor.js +473 -0
  54. package/src/core/context-manager.js +0 -48
  55. package/src/core/framework.js +95 -68
  56. package/src/core/index.js +11 -0
  57. package/src/core/notification-manager.js +125 -0
  58. package/src/core/subagent.js +295 -0
  59. package/src/core/token-counter.js +190 -0
  60. package/src/core/tool-executor.js +270 -0
  61. package/src/executors/mcp-executor.js +14 -1
  62. package/src/utils/download.js +596 -0
  63. package/system.md +312 -2373
  64. package/.agent/agents/code-assistant.json +0 -17
  65. package/.agent/agents/email-assistant.json +0 -14
  66. package/.agent/agents/file-assistant.json +0 -18
  67. package/.agent/agents/orchestrator-demo.md +0 -53
  68. package/.agent/agents/orchestrator.json +0 -7
  69. package/.agent/agents/poster-expert.md +0 -228
  70. package/.agent/agents/system-assistant.json +0 -15
  71. package/.agent/agents/web-assistant.json +0 -12
  72. package/.agent/memory/feedback/mnv3nu27-3o15pf.md +0 -9
  73. package/.agent/memory/feedback/mnv3o078-b959yj.md +0 -9
  74. package/.agent/memory/feedback/mnv3o6ej-u0fif5.md +0 -9
  75. package/.agent/memory/feedback/mnv3obgl-bkkjoj.md +0 -9
  76. package/.agent/memory/feedback/mnv4a3js-dv6onx.md +0 -9
  77. package/.agent/memory/feedback/mnv4aacm-sxxowp.md +0 -9
  78. package/.agent/memory/feedback/mnv4ahto-w40ffm.md +0 -9
  79. package/.agent/memory/feedback/mnv4anvp-3cs06y.md +0 -9
  80. package/.agent/memory/feedback/mnvzgvtd-0o2900.md +0 -9
  81. package/.agent/memory/feedback/mnvzhajn-swbx61.md +0 -15
  82. package/.agent/memory/feedback/mnvzhgsp-p5vog3.md +0 -9
  83. package/.agent/memory/feedback/mnvzho0c-fgql7q.md +0 -14
  84. package/.agent/memory/feedback/mnvzhtzq-ufr5at.md +0 -9
  85. package/.agent/memory/feedback/mnvzhyb3-9byq2z.md +0 -9
  86. package/.agent/memory/feedback/mnvzi7hp-hyeafp.md +0 -9
  87. package/.agent/memory/feedback/mnvzibph-z7rwp5.md +0 -9
  88. package/.agent/memory/feedback/mnvzilys-7h176w.md +0 -14
  89. package/.agent/memory/feedback/mnvziuh5-zjshci.md +0 -9
  90. package/.agent/memory/feedback/mnw07wde-6zqsc8.md +0 -9
  91. package/.agent/memory/feedback/mnw084bp-j0ba2a.md +0 -9
  92. package/.agent/memory/user/mnv3n62r-y0h79j.md +0 -21
  93. package/.agent/memory/user/mnv3n9yf-ead4g8.md +0 -13
  94. package/.agent/memory/user/mnv3ne3j-82tq1k.md +0 -19
  95. package/.agent/memory/user/mnv3nhgm-g2s2us.md +0 -11
  96. package/.agent/memory/user/mnv3nl9u-ejd998.md +0 -16
  97. package/.agent/memory/user/mnv3nofp-ya5szl.md +0 -10
  98. package/.agent/memory/user/mnv49qne-bhk0ki.md +0 -9
  99. package/.agent/memory/user/mnv49w3y-rzr8ju.md +0 -13
  100. package/.agent/sessions/test.json +0 -16
  101. package/plugins/python-plugin-loader.js.bak +0 -856
  102. package/src/core/agent-context.js +0 -188
@@ -1,856 +0,0 @@
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
- const { zodSchemaToMarkdown } = require('@chnak/zod-to-markdown')
13
-
14
- // 将 JSON Schema 转换为 Zod Schema
15
- function jsonSchemaToZod(jsonSchema) {
16
- if (!jsonSchema || jsonSchema.type !== 'object') {
17
- return z.object({})
18
- }
19
-
20
- const properties = jsonSchema.properties || {}
21
- const required = jsonSchema.required || []
22
-
23
- const shape = {}
24
- for (const [key, prop] of Object.entries(properties)) {
25
- let zodType
26
- switch (prop.type) {
27
- case 'string':
28
- zodType = z.string()
29
- break
30
- case 'number':
31
- zodType = z.number()
32
- break
33
- case 'boolean':
34
- zodType = z.boolean()
35
- break
36
- case 'array':
37
- zodType = z.array(z.any())
38
- break
39
- case 'object':
40
- zodType = jsonSchemaToZod(prop)
41
- break
42
- default:
43
- zodType = z.any()
44
- }
45
- // 如果不是 required 字段,则标记为 optional
46
- if (!required.includes(key)) {
47
- zodType = zodType.optional()
48
- }
49
- shape[key] = zodType
50
- }
51
-
52
- return z.object(shape)
53
- }
54
-
55
- class PythonPluginLoader extends Plugin {
56
- constructor(config = {}) {
57
- super()
58
- this.name = 'python-plugin-loader'
59
- this.version = '1.0.0'
60
- this.description = 'Python 插件加载器,属于Python的插件'
61
-
62
- this._agentDir = config.agentDir || '.agent'
63
- this._pythonPlugins = new Map()
64
- this._framework = null
65
- this.system = true
66
- }
67
-
68
- install(framework) {
69
- this._framework = framework
70
- return this
71
- }
72
-
73
- start(framework) {
74
- // 先设置 framework 引用(供 _loadPythonPlugins 使用)
75
- this._framework = framework
76
-
77
- // 获取 ExtensionExecutorPlugin 实例,用于直接注册 Python 插件工具
78
- this._extensionExecutor = framework.pluginManager?.get('extension-executor') || null
79
-
80
- // 加载所有 Python 插件(会注册工具到 ExtensionExecutor)
81
- this._loadPythonPlugins()
82
-
83
- // 监听 agent 创建事件,附加 Python 插件信息到系统提示词
84
- framework.on('agent:created', (agent) => {
85
- this._refreshAgentPythonPluginsPrompt(agent)
86
- })
87
-
88
- // 等待框架就绪后,刷新所有已有 agent 的 Python 插件提示词
89
- if (framework._ready) {
90
- this._refreshAllAgentsPythonPluginsPrompt(framework)
91
- } else {
92
- framework.once('framework:ready', () => {
93
- this._refreshAllAgentsPythonPluginsPrompt(framework)
94
- })
95
- }
96
-
97
- return this
98
- }
99
-
100
- /**
101
- * 构建 Python 插件描述
102
- * 格式与 MCP 一致,通过 ext_call 调用
103
- */
104
- _buildPythonPluginsDescription() {
105
- if (this._pythonPlugins.size === 0) {
106
- return ''
107
- }
108
-
109
- let desc = '## 【Python 插件】\n\n'
110
- desc += 'Python 插件工具通过 `ext_call` 调用:\n\n'
111
-
112
- for (const [name, plugin] of this._pythonPlugins) {
113
- desc += `### ${plugin.info.name}\n`
114
- desc += `${plugin.info.description || ''}\n\n`
115
- if (plugin.info.tools && Array.isArray(plugin.info.tools)) {
116
- for (const tool of plugin.info.tools) {
117
- const fullName = `${name}_${tool.name}`
118
- desc += `- **${fullName}**: ${tool.description || '无描述'}\n`
119
- // 使用 zodSchemaToMarkdown 生成参数描述
120
- const params = tool.params || {}
121
- if (Object.keys(params).length > 0) {
122
- try {
123
- const jsonSchema = this._paramsToJsonSchema(params)
124
- const zodSchema = this._jsonSchemaToZod(jsonSchema)
125
- const schemaMd = zodSchemaToMarkdown(zodSchema)
126
- if (schemaMd) {
127
- desc += `**参数:**\n\n${schemaMd}\n`
128
- }
129
- } catch (e) {
130
- // 忽略参数描述生成错误
131
- }
132
- }
133
- }
134
- }
135
- desc += '\n'
136
- }
137
-
138
- desc += '**调用格式:**\n'
139
- desc += '```\next_call({ plugin: "python", tool: "插件名_工具名", args: {...} })\n'
140
- desc += '```\n'
141
- return desc.trim()
142
- }
143
-
144
- /**
145
- * 将参数对象转换为 JSON Schema 格式
146
- */
147
- _paramsToJsonSchema(params) {
148
- const properties = {}
149
- const required = []
150
-
151
- for (const [key, value] of Object.entries(params)) {
152
- let type = 'string'
153
- if (typeof value === 'boolean') type = 'boolean'
154
- else if (typeof value === 'number') type = 'number'
155
- else if (Array.isArray(value)) type = 'array'
156
- else if (typeof value === 'object' && value !== null) type = 'object'
157
-
158
- properties[key] = {
159
- type,
160
- description: ''
161
- }
162
- required.push(key)
163
- }
164
-
165
- return {
166
- type: 'object',
167
- properties,
168
- required
169
- }
170
- }
171
-
172
- /**
173
- * 将 JSON Schema 转换为 Zod schema
174
- */
175
- _jsonSchemaToZod(jsonSchema) {
176
- if (!jsonSchema || !jsonSchema.properties) {
177
- return z.object({})
178
- }
179
-
180
- try {
181
- const shape = {}
182
- const properties = jsonSchema.properties
183
- const required = jsonSchema.required || []
184
-
185
- for (const [key, prop] of Object.entries(properties)) {
186
- shape[key] = this._jsonSchemaPropToZod(prop, required.includes(key))
187
- }
188
-
189
- return z.object(shape)
190
- } catch (e) {
191
- return z.object({})
192
- }
193
- }
194
-
195
- /**
196
- * 将 JSON Schema 属性转换为 Zod 类型
197
- */
198
- _jsonSchemaPropToZod(prop, isRequired) {
199
- if (prop.enum) {
200
- let zodType = z.string().enum(prop.enum)
201
- return isRequired ? zodType : zodType.optional()
202
- }
203
-
204
- const type = prop.type || 'string'
205
- switch (type) {
206
- case 'string':
207
- return isRequired ? z.string() : z.string().optional()
208
- case 'number':
209
- case 'integer':
210
- return isRequired ? z.number() : z.number().optional()
211
- case 'boolean':
212
- return isRequired ? z.boolean() : z.boolean().optional()
213
- case 'array':
214
- return isRequired ? z.array(z.any()) : z.array(z.any()).optional()
215
- case 'object':
216
- if (prop.properties) {
217
- const nested = {}
218
- for (const [k, v] of Object.entries(prop.properties)) {
219
- nested[k] = this._jsonSchemaPropToZod(v, prop.required?.includes(k) || false)
220
- }
221
- return isRequired ? z.object(nested) : z.object(nested).optional()
222
- }
223
- return isRequired ? z.record(z.any()) : z.record(z.any()).optional()
224
- default:
225
- return isRequired ? z.any() : z.any().optional()
226
- }
227
- }
228
-
229
- /**
230
- * 刷新单个 agent 的 Python 插件提示词
231
- */
232
- _refreshAgentPythonPluginsPrompt(agent) {
233
- const existingPrompt = agent._originalPrompt || ''
234
- if (existingPrompt.includes('【Python 插件】')) {
235
- return
236
- }
237
-
238
- const pyDesc = this._buildPythonPluginsDescription()
239
- if (!pyDesc) return
240
-
241
- // 将 Python 插件描述追加到系统提示词
242
- agent.setSystemPrompt(existingPrompt + '\n\n' + pyDesc)
243
- }
244
-
245
- /**
246
- * 刷新所有 agent 的 Python 插件提示词
247
- */
248
- _refreshAllAgentsPythonPluginsPrompt(framework) {
249
- const visited = new Set()
250
-
251
- const traverse = (agent) => {
252
- if (!agent || visited.has(agent)) return
253
- visited.add(agent)
254
- this._refreshAgentPythonPluginsPrompt(agent)
255
-
256
- // 递归处理子 agent
257
- const subAgents = agent.getSubAgents?.() || agent._subAgents || new Map()
258
- for (const [name, subAgentInfo] of subAgents) {
259
- traverse(subAgentInfo.agent)
260
- }
261
- }
262
-
263
- const agents = framework._agents || []
264
- for (const agent of agents) {
265
- traverse(agent)
266
- }
267
- }
268
-
269
- /**
270
- * 加载所有 Python 插件
271
- */
272
- _loadPythonPlugins() {
273
- const pluginsDir = path.join(this._agentDir, 'plugins')
274
- if (!fs.existsSync(pluginsDir)) {
275
- return
276
- }
277
-
278
- const files = fs.readdirSync(pluginsDir).filter(f => f.endsWith('.py'))
279
-
280
- for (const file of files) {
281
- try {
282
- const pluginPath = path.join(pluginsDir, file)
283
- const pluginName = file.replace('.py', '')
284
-
285
- const pluginInfo = this._loadPythonPluginMeta(pluginPath)
286
- if (pluginInfo) {
287
- this._pythonPlugins.set(pluginName, {
288
- name: pluginName,
289
- path: pluginPath,
290
- info: pluginInfo,
291
- code: fs.readFileSync(pluginPath, 'utf-8')
292
- })
293
- log.info(` Loaded: ${pluginName}`)
294
-
295
- // 注册插件的每个工具
296
- if (pluginInfo.tools && Array.isArray(pluginInfo.tools)) {
297
- for (const tool of pluginInfo.tools) {
298
- this._registerPythonTool(pluginName, tool)
299
- }
300
- }
301
- }
302
- } catch (err) {
303
- log.error(` Failed to load ${file}:`, err.message)
304
- }
305
- }
306
-
307
- log.info(` Total Python plugins: ${this._pythonPlugins.size}`)
308
- }
309
-
310
- /**
311
- * 注册 Python 插件的工具
312
- * 工具名格式:插件名_工具名(如 stock_get_price)
313
- * 直接注册到 ExtensionExecutor,使用 ext_call({ plugin: "python", tool: "插件名_工具名", args: {...} }) 调用
314
- */
315
- _registerPythonTool(pluginName, tool) {
316
- if (!tool.name) return
317
-
318
- // 格式:pluginname_toolname
319
- const fullToolName = `${pluginName}_${tool.name}`
320
-
321
- const toolDef = {
322
- name: fullToolName,
323
- description: tool.description || `${pluginName} 的 ${tool.name} 工具`,
324
- inputSchema: this._parseToolParams(tool.params || {}),
325
- execute: async (args) => {
326
- return this._executePythonTool(pluginName, tool.name, args)
327
- }
328
- }
329
-
330
- try {
331
- // 直接注册到 ExtensionExecutor(如果可用)
332
- if (this._extensionExecutor) {
333
- this._extensionExecutor.registerTool(
334
- 'python', // 插件名,用于 ext_call 的 plugin 参数
335
- { name: 'python', description: 'Python 插件工具', version: '1.0.0' },
336
- toolDef
337
- )
338
- } else {
339
- // 回退到基类的 registerTool
340
- this.registerTool({
341
- name: fullToolName,
342
- description: `[${pluginName}] ${tool.description || ''}`,
343
- pluginName: pluginName,
344
- inputSchema: this._parseToolParams(tool.params || {}),
345
- execute: toolDef.execute
346
- })
347
- }
348
- } catch (err) {
349
- log.error(` Failed to register tool ${fullToolName}:`, err.message)
350
- }
351
- }
352
-
353
- /**
354
- * 解析工具参数 schema
355
- */
356
- _parseToolParams(params) {
357
- // 构建 JSON Schema 格式
358
- const properties = {}
359
- const required = []
360
-
361
- for (const [key, value] of Object.entries(params)) {
362
- let type = 'string'
363
- if (typeof value === 'boolean') type = 'boolean'
364
- else if (typeof value === 'number') type = 'number'
365
- else if (Array.isArray(value)) type = 'array'
366
- else if (typeof value === 'object') type = 'object'
367
-
368
- properties[key] = {
369
- type,
370
- description: ''
371
- }
372
- required.push(key)
373
- }
374
-
375
- const jsonSchema = {
376
- type: 'object',
377
- properties,
378
- required
379
- }
380
-
381
- // 转换为 Zod Schema 以兼容 AI SDK
382
- return jsonSchemaToZod(jsonSchema)
383
- }
384
-
385
- /**
386
- * 格式化参数描述(使用 zodSchemaToMarkdown)
387
- */
388
- _formatParams(params) {
389
- if (!params || Object.keys(params).length === 0) {
390
- return '无参数'
391
- }
392
-
393
- try {
394
- // 构建 JSON Schema 格式
395
- const properties = {}
396
- const required = []
397
-
398
- for (const [key, value] of Object.entries(params)) {
399
- let type = 'string'
400
- if (typeof value === 'boolean') type = 'boolean'
401
- else if (typeof value === 'number') type = 'number'
402
- else if (Array.isArray(value)) type = 'array'
403
- else if (typeof value === 'object' && value !== null) type = 'object'
404
-
405
- properties[key] = {
406
- type,
407
- description: ''
408
- }
409
- required.push(key)
410
- }
411
-
412
- const jsonSchema = {
413
- type: 'object',
414
- properties,
415
- required
416
- }
417
-
418
- // 转换为 Zod Schema
419
- const zodSchema = jsonSchemaToZod(jsonSchema)
420
-
421
- // 使用 zodSchemaToMarkdown 生成表格
422
- const table = zodSchemaToMarkdown(zodSchema)
423
-
424
- // 提取表格中的参数行,格式化为 "param: type" 形式
425
- const lines = table.split('\n')
426
- const paramLines = []
427
-
428
- for (const line of lines) {
429
- if (line.startsWith('| ') && !line.includes('字段') && !line.includes('---')) {
430
- const parts = line.split('|').map(s => s.trim())
431
- if (parts.length >= 3 && parts[1]) {
432
- const paramName = parts[1]
433
- const type = parts[2]
434
- paramLines.push(`${paramName}: ${type}`)
435
- }
436
- }
437
- }
438
-
439
- return paramLines.join(', ') || '无参数'
440
- } catch (err) {
441
- // fallback 到简单的参数名列表
442
- return Object.keys(params).join(', ') || '无参数'
443
- }
444
- }
445
-
446
- /**
447
- * 加载单个 Python 插件的元信息
448
- * 支持 PLUGIN 和 TOOLS 格式
449
- */
450
- _loadPythonPluginMeta(pluginPath) {
451
- const code = fs.readFileSync(pluginPath, 'utf-8')
452
-
453
- const pluginIdx = code.indexOf('PLUGIN')
454
- const toolsIdx = code.indexOf('TOOLS')
455
-
456
- if (pluginIdx === -1 && toolsIdx === -1) {
457
- console.warn(`[PythonPluginLoader] ${path.basename(pluginPath)}: no PLUGIN or TOOLS found`)
458
- return null
459
- }
460
-
461
- try {
462
- const info = {}
463
-
464
- // 解析 PLUGIN
465
- if (pluginIdx !== -1) {
466
- const pluginData = this._extractPythonDict(code, pluginIdx)
467
- if (pluginData && pluginData.name) {
468
- info.name = pluginData.name
469
- info.version = pluginData.version || '1.0.0'
470
- info.description = pluginData.description || ''
471
- }
472
- }
473
-
474
- // 解析 TOOLS
475
- if (toolsIdx !== -1) {
476
- const toolsData = this._extractPythonList(code, toolsIdx)
477
- if (toolsData && Array.isArray(toolsData)) {
478
- info.tools = toolsData
479
- }
480
- }
481
-
482
- if (!info.name || !info.tools) {
483
- console.warn(`[PythonPluginLoader] ${path.basename(pluginPath)}: invalid format - name: ${info.name}, tools: ${info.tools ? info.tools.length : 0}`)
484
- return null
485
- }
486
-
487
- return info
488
- } catch (err) {
489
- console.warn(`[PythonPluginLoader] Failed to parse ${path.basename(pluginPath)}:`, err.message)
490
- return null
491
- }
492
- }
493
-
494
- /**
495
- * 从指定位置提取 Python 字典
496
- */
497
- _extractPythonDict(code, startIdx) {
498
- const braceStart = code.indexOf('{', startIdx)
499
- if (braceStart === -1) return null
500
-
501
- let braceCount = 0
502
- let endIdx = -1
503
- for (let i = braceStart; i < code.length; i++) {
504
- if (code[i] === '{') braceCount++
505
- else if (code[i] === '}') {
506
- braceCount--
507
- if (braceCount === 0) {
508
- endIdx = i
509
- break
510
- }
511
- }
512
- }
513
-
514
- if (endIdx === -1) return null
515
- return this._parsePythonDict(code.substring(braceStart, endIdx + 1))
516
- }
517
-
518
- /**
519
- * 从指定位置提取 Python 列表
520
- */
521
- _extractPythonList(code, startIdx) {
522
- const bracketStart = code.indexOf('[', startIdx)
523
- if (bracketStart === -1) return null
524
-
525
- let bracketCount = 0
526
- let endIdx = -1
527
- for (let i = bracketStart; i < code.length; i++) {
528
- if (code[i] === '[') bracketCount++
529
- else if (code[i] === ']') {
530
- bracketCount--
531
- if (bracketCount === 0) {
532
- endIdx = i
533
- break
534
- }
535
- }
536
- }
537
-
538
- if (endIdx === -1) return null
539
- return this._parsePythonList(code.substring(bracketStart, endIdx + 1))
540
- }
541
-
542
- /**
543
- * 解析 Python 字典为 JS 对象
544
- */
545
- _parsePythonDict(str) {
546
- str = str.replace(/#.*$/gm, '').trim()
547
- if (!str || str === '{}') return {}
548
-
549
- // 直接尝试 JSON.parse(PLUGIN 字典是有效的 JSON 格式)
550
- try {
551
- return JSON.parse(str)
552
- } catch {
553
- // 如果失败,使用手动解析
554
- }
555
-
556
- const result = {}
557
- let i = 0
558
- let key = null
559
- let valueStart = -1
560
- let depth = 0
561
- let inString = false
562
- let stringChar = null
563
- let escaped = false
564
-
565
- while (i < str.length) {
566
- const char = str[i]
567
-
568
- if (escaped) {
569
- escaped = false
570
- i++
571
- continue
572
- }
573
-
574
- if (char === '\\' && inString) {
575
- escaped = true
576
- i++
577
- continue
578
- }
579
-
580
- if ((char === '"' || char === "'") && !inString) {
581
- inString = true
582
- stringChar = char
583
- i++
584
- continue
585
- }
586
-
587
- if (inString && char === stringChar) {
588
- inString = false
589
- i++
590
- continue
591
- }
592
-
593
- if (inString) {
594
- i++
595
- continue
596
- }
597
-
598
- if (char === '{' || char === '[') {
599
- if (depth === 0 && char === '{') {
600
- valueStart = i + 1
601
- }
602
- depth++
603
- i++
604
- continue
605
- }
606
- if (char === '}' || char === ']') {
607
- if (depth > 0) {
608
- depth--
609
- if (depth === 0 && char === '}') {
610
- if (key !== null && valueStart !== -1) {
611
- const valueStr = str.substring(valueStart, i).trim()
612
- if (valueStr) result[key] = this._parseValue(valueStr)
613
- }
614
- }
615
- }
616
- i++
617
- continue
618
- }
619
- if (depth > 0) {
620
- i++
621
- continue
622
- }
623
-
624
- if (char === ':') {
625
- key = str.substring(valueStart, i).trim().replace(/["']/g, '')
626
- valueStart = i + 1
627
- i++
628
- continue
629
- }
630
-
631
- if (char === ',') {
632
- if (key !== null && valueStart !== -1) {
633
- const valueStr = str.substring(valueStart, i).trim()
634
- if (valueStr) result[key] = this._parseValue(valueStr)
635
- }
636
- key = null
637
- valueStart = i + 1
638
- i++
639
- continue
640
- }
641
-
642
- i++
643
- }
644
-
645
- return result
646
- }
647
-
648
- /**
649
- * 解析 Python 列表为 JS 数组
650
- */
651
- _parsePythonList(str) {
652
- str = str.replace(/#.*$/gm, '').trim()
653
- if (!str || str === '[]') return []
654
-
655
- // 直接尝试 JSON.parse(TOOLS 列表是有效的 JSON 格式)
656
- try {
657
- return JSON.parse(str)
658
- } catch {
659
- // 如果失败,使用手动解析
660
- }
661
-
662
- const result = []
663
- let i = 0
664
- let itemStart = 1
665
- let depth = 0
666
- let inString = false
667
- let stringChar = null
668
- let escaped = false
669
-
670
- while (i < str.length) {
671
- const char = str[i]
672
-
673
- if (escaped) {
674
- escaped = false
675
- i++
676
- continue
677
- }
678
-
679
- if (char === '\\' && inString) {
680
- escaped = true
681
- i++
682
- continue
683
- }
684
-
685
- if ((char === '"' || char === "'") && !inString) {
686
- inString = true
687
- stringChar = char
688
- i++
689
- continue
690
- }
691
-
692
- if (inString && char === stringChar) {
693
- inString = false
694
- i++
695
- continue
696
- }
697
-
698
- if (inString) {
699
- i++
700
- continue
701
- }
702
-
703
- if (char === '{' || char === '[') {
704
- if (depth === 0) itemStart = i
705
- depth++
706
- i++
707
- continue
708
- }
709
- if (char === '}' || char === ']') {
710
- if (depth > 0) {
711
- depth--
712
- if (depth === 0) {
713
- const itemStr = str.substring(itemStart, i + 1).trim()
714
- if (itemStr && itemStr !== ',') {
715
- if (itemStr.startsWith('{')) {
716
- result.push(this._parsePythonDict(itemStr))
717
- } else {
718
- result.push(this._parseValue(itemStr))
719
- }
720
- }
721
- itemStart = i + 1
722
- }
723
- }
724
- i++
725
- continue
726
- }
727
- if (depth > 0) {
728
- i++
729
- continue
730
- }
731
-
732
- if (char === ',') {
733
- const itemStr = str.substring(itemStart, i).trim()
734
- if (itemStr) {
735
- if (itemStr.startsWith('{')) {
736
- result.push(this._parsePythonDict(itemStr))
737
- } else {
738
- result.push(this._parseValue(itemStr))
739
- }
740
- }
741
- itemStart = i + 1
742
- }
743
-
744
- i++
745
- }
746
-
747
- return result
748
- }
749
-
750
- /**
751
- * 解析单个值
752
- */
753
- _parseValue(str) {
754
- str = str.trim()
755
- if (!str) return null
756
-
757
- if (str.startsWith('{')) {
758
- return this._parsePythonDict(str)
759
- }
760
-
761
- if (str.startsWith('"') || str.startsWith("'")) {
762
- return str.slice(1, -1)
763
- }
764
-
765
- if (str === 'True') return true
766
- if (str === 'False') return false
767
- if (str === 'None') return null
768
-
769
- if (!isNaN(str)) return Number(str)
770
-
771
- return str
772
- }
773
-
774
- /**
775
- * 执行 Python 插件工具
776
- */
777
- async _executePythonTool(pluginName, toolName, params) {
778
- const plugin = this._pythonPlugins.get(pluginName)
779
- if (!plugin) {
780
- return { success: false, error: `Plugin '${pluginName}' not found` }
781
- }
782
-
783
- // 构建执行代码 - 使用数组拼接避免缩进问题
784
- const pluginDir = path.dirname(plugin.path)
785
- const codeBase64 = Buffer.from(plugin.code, 'utf-8').toString('base64')
786
-
787
- // 从 TOOLS 提取工具名称列表,构建路由函数
788
- const toolNames = (plugin.info.tools || []).map(t => t.name)
789
-
790
- const execCode = [
791
- 'import sys',
792
- 'import base64',
793
- 'import json as _json',
794
- `sys.path.insert(0, r'${pluginDir}')`,
795
- '',
796
- '# 加载插件代码(使用 textwrap.dedent 去除缩进)',
797
- `plugin_code = base64.b64decode('${codeBase64}').decode('utf-8')`,
798
- `import textwrap`,
799
- `exec(compile(textwrap.dedent(plugin_code), '${pluginName}.py', 'exec'))`,
800
- '',
801
- '# 工具路由函数',
802
- `_tool_map = {${toolNames.map(n => `"${n}": ${n}`).join(', ')}}`,
803
- `def execute_tool(name, params):`,
804
- ` if name not in _tool_map:`,
805
- ` return {"success": False, "error": f"Tool '{name}' not found in plugin '${pluginName}'"}`,
806
- ` try:`,
807
- ` return _tool_map[name](params)`,
808
- ` except Exception as e:`,
809
- ` return {"success": False, "error": str(e)}`,
810
- '',
811
- '# 执行工具',
812
- `result = execute_tool('${toolName}', ${JSON.stringify(params)})`,
813
- 'print(_json.dumps(result))'
814
- ].join('\n')
815
-
816
- try {
817
- // 通过 python-execute 工具执行
818
- const pythonExe = this._framework.toolRegistry._tools.get('python-execute')
819
- if (!pythonExe) {
820
- return { success: false, error: 'python-execute tool not found. Please install python-executor-plugin.' }
821
- }
822
-
823
- const pythonResult = await pythonExe.execute({ code: execCode })
824
-
825
- if (pythonResult.success) {
826
- try {
827
- // 尝试解析 JSON 结果
828
- const parsed = JSON.parse(pythonResult.stdout)
829
- return parsed
830
- } catch {
831
- return { success: true, output: pythonResult.stdout }
832
- }
833
- } else {
834
- return { success: false, error: pythonResult.stderr || pythonResult.error }
835
- }
836
- } catch (err) {
837
- return { success: false, error: err.message }
838
- }
839
- }
840
-
841
- /**
842
- * 获取所有已加载的 Python 插件
843
- */
844
- getPythonPlugins() {
845
- return Array.from(this._pythonPlugins.values())
846
- }
847
-
848
- /**
849
- * 获取 Python 插件信息
850
- */
851
- getPythonPlugin(name) {
852
- return this._pythonPlugins.get(name)
853
- }
854
- }
855
-
856
- module.exports = PythonPluginLoader