foliko 1.1.92 → 2.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 (212) hide show
  1. package/.claude/settings.local.json +2 -1
  2. package/CLAUDE.md +56 -30
  3. package/REFACTORING_PLAN.md +645 -0
  4. package/docs/architecture.md +131 -0
  5. package/docs/migration.md +57 -0
  6. package/docs/public-api.md +138 -0
  7. package/docs/usage.md +385 -0
  8. package/examples/ambient-example.js +20 -137
  9. package/examples/basic.js +21 -48
  10. package/examples/bootstrap.js +16 -74
  11. package/examples/mcp-example.js +6 -29
  12. package/examples/skill-example.js +6 -19
  13. package/examples/workflow.js +8 -56
  14. package/package.json +8 -4
  15. package/plugins/README.md +49 -0
  16. package/plugins/{ambient-agent → ambient}/EventWatcher.js +1 -1
  17. package/plugins/{ambient-agent → ambient}/ExplorerLoop.js +3 -3
  18. package/plugins/{ambient-agent → ambient}/GoalManager.js +2 -2
  19. package/plugins/ambient/README.md +14 -0
  20. package/plugins/{ambient-agent → ambient}/Reflector.js +1 -1
  21. package/plugins/{ambient-agent → ambient}/StateStore.js +1 -1
  22. package/plugins/{ambient-agent → ambient}/index.js +2 -2
  23. package/plugins/{ai-plugin.js → core/ai/index.js} +14 -30
  24. package/plugins/{audit-plugin.js → core/audit/index.js} +3 -30
  25. package/plugins/{coordinator-plugin.js → core/coordinator/index.js} +3 -35
  26. package/plugins/core/default/bootstrap.js +202 -0
  27. package/plugins/core/default/config.js +220 -0
  28. package/plugins/core/default/index.js +58 -0
  29. package/plugins/core/mcp/index.js +1 -0
  30. package/plugins/{python-plugin-loader.js → core/python-loader/index.js} +7 -187
  31. package/plugins/{rules-plugin.js → core/rules/index.js} +121 -64
  32. package/plugins/{scheduler-plugin.js → core/scheduler/index.js} +12 -114
  33. package/plugins/{session-plugin.js → core/session/index.js} +9 -73
  34. package/{src/capabilities/skill-manager.js → plugins/core/skill-manager/index.js} +64 -18
  35. package/plugins/{storage-plugin.js → core/storage/index.js} +5 -29
  36. package/plugins/{subagent-plugin.js → core/sub-agent/index.js} +10 -171
  37. package/plugins/{think-plugin.js → core/think/index.js} +24 -91
  38. package/{src/capabilities/workflow-engine.js → plugins/core/workflow/index.js} +87 -85
  39. package/plugins/default-plugins.js +6 -720
  40. package/plugins/{data-splitter-plugin.js → executors/data-splitter/index.js} +9 -83
  41. package/plugins/{extension-executor-plugin.js → executors/extension/index.js} +13 -97
  42. package/plugins/{python-executor-plugin.js → executors/python/index.js} +6 -31
  43. package/plugins/{shell-executor-plugin.js → executors/shell/index.js} +2 -5
  44. package/plugins/install/README.md +9 -0
  45. package/plugins/{install-plugin.js → install/index.js} +3 -3
  46. package/plugins/{file-system-plugin.js → io/file-system/index.js} +34 -236
  47. package/plugins/{web-plugin.js → io/web/index.js} +11 -113
  48. package/plugins/memory/README.md +13 -0
  49. package/plugins/{memory-plugin.js → memory/index.js} +4 -18
  50. package/plugins/messaging/email/README.md +19 -0
  51. package/plugins/{email → messaging/email}/index.js +2 -2
  52. package/plugins/{feishu-plugin.js → messaging/feishu/index.js} +3 -3
  53. package/plugins/{qq-plugin.js → messaging/qq/index.js} +5 -16
  54. package/plugins/{telegram-plugin.js → messaging/telegram/index.js} +3 -3
  55. package/plugins/{weixin-plugin.js → messaging/weixin/index.js} +15 -15
  56. package/plugins/{plugin-manager-plugin.js → plugin-manager/index.js} +36 -180
  57. package/plugins/{tools-plugin.js → tools/index.js} +68 -116
  58. package/plugins/trading/README.md +15 -0
  59. package/plugins/{gate-trading.js → trading/index.js} +8 -8
  60. package/{examples → sandbox}/test-concurrent-chat.js +2 -2
  61. package/{examples → sandbox}/test-long-chat.js +2 -2
  62. package/{examples → sandbox}/test-session-chat.js +2 -2
  63. package/{examples → sandbox}/test-web-plugin.js +1 -1
  64. package/{examples → sandbox}/test-weixin-feishu.js +2 -2
  65. package/src/agent/base.js +56 -0
  66. package/src/{core/agent-chat.js → agent/chat.js} +11 -11
  67. package/src/{core/coordinator-manager.js → agent/coordinator.js} +3 -3
  68. package/src/agent/index.js +111 -0
  69. package/src/agent/main.js +337 -0
  70. package/src/agent/prompt.js +78 -0
  71. package/src/agent/sub.js +198 -0
  72. package/src/agent/worker.js +104 -0
  73. package/{cli/bin/foliko.js → src/cli/bin.js} +1 -1
  74. package/{cli/src → src/cli}/commands/chat.js +25 -21
  75. package/{cli/src → src/cli}/index.js +1 -0
  76. package/{cli/src → src/cli}/ui/chat-ui-old.js +40 -178
  77. package/{cli/src → src/cli}/ui/chat-ui.js +3 -3
  78. package/{cli/src → src/cli}/ui/components/footer-bar.js +1 -1
  79. package/src/{core → common}/constants.js +3 -0
  80. package/src/common/errors.js +402 -0
  81. package/src/{utils → common}/logger.js +33 -0
  82. package/src/{utils/chat-queue.js → common/queue.js} +2 -2
  83. package/src/config/plugin-config.js +50 -0
  84. package/src/context/agent.js +32 -0
  85. package/src/context/compaction-prompts.js +170 -0
  86. package/src/context/compaction-utils.js +191 -0
  87. package/src/context/compressor.js +413 -0
  88. package/src/context/index.js +9 -0
  89. package/src/{core/context-manager.js → context/manager.js} +1 -1
  90. package/src/context/request.js +50 -0
  91. package/src/context/session.js +33 -0
  92. package/src/context/storage.js +30 -0
  93. package/src/executors/mcp-client.js +153 -0
  94. package/src/executors/mcp-desc.js +236 -0
  95. package/src/executors/mcp-executor.js +91 -956
  96. package/src/{core → framework}/command-registry.js +1 -1
  97. package/src/framework/framework.js +300 -0
  98. package/src/framework/index.js +18 -0
  99. package/src/framework/lifecycle.js +203 -0
  100. package/src/framework/loader.js +78 -0
  101. package/src/framework/registry.js +86 -0
  102. package/src/{core/ui-extension-context.js → framework/ui-extension.js} +1 -1
  103. package/src/index.js +130 -15
  104. package/src/llm/index.js +26 -0
  105. package/src/llm/provider.js +212 -0
  106. package/src/llm/registry.js +11 -0
  107. package/src/{core/token-counter.js → llm/tokens.js} +4 -37
  108. package/src/{core/plugin-base.js → plugin/base.js} +10 -136
  109. package/src/plugin/index.js +14 -0
  110. package/src/plugin/loader.js +101 -0
  111. package/src/plugin/manager.js +261 -0
  112. package/src/{core → session}/branch-summary-auto.js +2 -2
  113. package/src/{core/chat-session.js → session/chat.js} +2 -2
  114. package/src/session/index.js +7 -0
  115. package/src/{core/session-manager.js → session/session.js} +2 -2
  116. package/src/session/ttl.js +92 -0
  117. package/src/{core/jsonl-storage.js → storage/jsonl.js} +1 -1
  118. package/src/tool/executor.js +85 -0
  119. package/src/tool/index.js +15 -0
  120. package/src/tool/registry.js +143 -0
  121. package/src/{core/tool-router.js → tool/router.js} +17 -124
  122. package/src/tool/schema.js +108 -0
  123. package/src/utils/data-splitter.js +1 -1
  124. package/src/utils/download.js +1 -1
  125. package/src/utils/index.js +6 -6
  126. package/src/utils/message-validator.js +1 -1
  127. package/tests/core/context-storage.test.js +46 -0
  128. package/tests/core/llm.test.js +54 -0
  129. package/tests/core/plugin.test.js +42 -0
  130. package/tests/core/tool.test.js +60 -0
  131. package/tests/setup.js +10 -0
  132. package/tests/smoke.test.js +58 -0
  133. package/vitest.config.js +9 -0
  134. package/cli/src/daemon.js +0 -149
  135. package/docs/CONTEXT_DESIGN.md +0 -1596
  136. package/docs/ai-sdk-optimization.md +0 -655
  137. package/docs/features.md +0 -120
  138. package/docs/qq-bot.md +0 -976
  139. package/docs/quick-reference.md +0 -160
  140. package/docs/user-manual.md +0 -1391
  141. package/images/geometric_shapes.jpg +0 -0
  142. package/images/sunset_mountain_lake.jpg +0 -0
  143. package/skills/poster-guide/SKILL.md +0 -792
  144. package/src/capabilities/index.js +0 -11
  145. package/src/core/agent.js +0 -808
  146. package/src/core/context-compressor.js +0 -959
  147. package/src/core/enhanced-context-compressor.js +0 -210
  148. package/src/core/framework.js +0 -1422
  149. package/src/core/index.js +0 -30
  150. package/src/core/plugin-manager.js +0 -961
  151. package/src/core/provider-registry.js +0 -159
  152. package/src/core/provider.js +0 -156
  153. package/src/core/request-context.js +0 -98
  154. package/src/core/subagent.js +0 -442
  155. package/src/core/system-prompt-builder.js +0 -120
  156. package/src/core/tool-executor.js +0 -202
  157. package/src/core/tool-registry.js +0 -517
  158. package/src/core/worker-agent.js +0 -192
  159. package/src/executors/executor-base.js +0 -58
  160. package/src/utils/error-boundary.js +0 -363
  161. package/src/utils/error.js +0 -374
  162. package/system.md +0 -1645
  163. package/website_v2/README.md +0 -57
  164. package/website_v2/SPEC.md +0 -1
  165. package/website_v2/docs/api.html +0 -128
  166. package/website_v2/docs/configuration.html +0 -147
  167. package/website_v2/docs/plugin-development.html +0 -129
  168. package/website_v2/docs/project-structure.html +0 -89
  169. package/website_v2/docs/skill-development.html +0 -85
  170. package/website_v2/index.html +0 -489
  171. package/website_v2/scripts/main.js +0 -93
  172. package/website_v2/styles/animations.css +0 -8
  173. package/website_v2/styles/docs.css +0 -83
  174. package/website_v2/styles/main.css +0 -417
  175. package/xhs_auth.json +0 -268
  176. package//346/265/267/346/212/245/346/217/222/344/273/266.md +0 -621
  177. /package/plugins/{ambient-agent → ambient}/constants.js +0 -0
  178. /package/plugins/{email → messaging/email}/constants.js +0 -0
  179. /package/plugins/{email → messaging/email}/handlers.js +0 -0
  180. /package/plugins/{email → messaging/email}/monitor.js +0 -0
  181. /package/plugins/{email → messaging/email}/parser.js +0 -0
  182. /package/plugins/{email → messaging/email}/reply.js +0 -0
  183. /package/plugins/{email → messaging/email}/utils.js +0 -0
  184. /package/{examples → sandbox}/test-chat.js +0 -0
  185. /package/{examples → sandbox}/test-mcp.js +0 -0
  186. /package/{examples → sandbox}/test-reload.js +0 -0
  187. /package/{examples → sandbox}/test-telegram.js +0 -0
  188. /package/{examples → sandbox}/test-tg-bot.js +0 -0
  189. /package/{examples → sandbox}/test-tg-simple.js +0 -0
  190. /package/{examples → sandbox}/test-tg.js +0 -0
  191. /package/{examples → sandbox}/test-think.js +0 -0
  192. /package/src/{core/sub-agent-config.js → agent/sub-config.js} +0 -0
  193. /package/{cli/src → src/cli}/commands/daemon.js +0 -0
  194. /package/{cli/src → src/cli}/commands/list.js +0 -0
  195. /package/{cli/src → src/cli}/commands/plugin.js +0 -0
  196. /package/{cli/src → src/cli}/ui/components/agent-mention-provider.js +0 -0
  197. /package/{cli/src → src/cli}/ui/components/chained-autocomplete-provider.js +0 -0
  198. /package/{cli/src → src/cli}/ui/components/message-bubble.js +0 -0
  199. /package/{cli/src → src/cli}/ui/components/status-bar.js +0 -0
  200. /package/{cli/src → src/cli}/utils/ansi.js +0 -0
  201. /package/{cli/src → src/cli}/utils/config.js +0 -0
  202. /package/{cli/src → src/cli}/utils/markdown.js +0 -0
  203. /package/{cli/src → src/cli}/utils/plugin-config.js +0 -0
  204. /package/{cli/src → src/cli}/utils/render-diff.js +0 -0
  205. /package/src/{utils/circuit-breaker.js → common/circuit.js} +0 -0
  206. /package/src/{utils/edit-diff.js → common/diff.js} +0 -0
  207. /package/src/{utils/event-emitter.js → common/events.js} +0 -0
  208. /package/src/{utils → common}/id.js +0 -0
  209. /package/src/{utils → common}/retry.js +0 -0
  210. /package/src/{core/notification-manager.js → notification/manager.js} +0 -0
  211. /package/src/{core/session-entry.js → session/entry.js} +0 -0
  212. /package/src/{core/storage-manager.js → storage/manager.js} +0 -0
@@ -8,11 +8,11 @@
8
8
  * - allowedUsers: 允许交互的用户 openid 列表(空数组表示允许所有人)
9
9
  */
10
10
 
11
- const { Plugin } = require('../src/core/plugin-base')
12
- const { logger } = require('../src/utils/logger')
11
+ const { Plugin } = require('../../../src/plugin/base')
12
+ const { logger } = require('../../../src/common/logger')
13
13
  const log = logger.child('QQ')
14
- const { cleanResponse } = require('../src/utils')
15
- const { FileDownloader } = require('../src/utils/download')
14
+ const { cleanResponse } = require('../../../src/utils')
15
+ const { FileDownloader } = require('../../../src/utils/download')
16
16
  const { z } = require('zod')
17
17
  const { createQQBotClient, QQBotAPIClient, StreamSession } = require('@chnak/qq-bot')
18
18
  const path = require('path')
@@ -374,14 +374,6 @@ class QQPlugin extends Plugin {
374
374
  },
375
375
  })
376
376
 
377
- // 连接后可以从事件中获取机器人信息,先直接连接
378
- // 获取机器人自身的 openid(用于发送消息)
379
- // try {
380
- // const token = await this._client.api.getToken()
381
- // } catch (err) {
382
- // log.warn('Failed to get access token:', err.message)
383
- // }
384
-
385
377
  // 监听私聊消息
386
378
  this._client.on('C2C_MESSAGE_CREATE', async (event) => {
387
379
  await this._handleC2CMessage(event)
@@ -484,9 +476,6 @@ class QQPlugin extends Plugin {
484
476
 
485
477
  if (!actualContent) return
486
478
 
487
- // 权限检查(可以按群或用户检查)
488
- // if (!this._checkGroupPermission(groupOpenid, userId)) { ... }
489
-
490
479
  // 处理命令
491
480
  if (actualContent.startsWith('/')) {
492
481
  await this._handleGroupCommand(groupOpenid, username, actualContent, event)
@@ -1096,4 +1085,4 @@ function interval(fn, ms) {
1096
1085
  return { start, stop }
1097
1086
  }
1098
1087
 
1099
- module.exports = QQPlugin
1088
+ module.exports = QQPlugin
@@ -9,8 +9,8 @@
9
9
  * - prefix: 命令前缀
10
10
  */
11
11
 
12
- const { Plugin } = require('../src/core/plugin-base')
13
- const { logger } = require('../src/utils/logger')
12
+ const { Plugin } = require('../../../src/plugin/base')
13
+ const { logger } = require('../../../src/common/logger')
14
14
  const log = logger.child('Telegram')
15
15
  const { z } = require('zod')
16
16
 
@@ -29,7 +29,7 @@ class TelegramPlugin extends Plugin {
29
29
  this.priority = 80
30
30
  this.enabled = false
31
31
  this.systemPrompt = `你是一个有帮助的AI助手。回复内容不要使用markdown格式文本。
32
-
32
+
33
33
  **重要:** 子Agent 匹配规则必须遵守:
34
34
  - 根据【子 Agent 匹配表】,将任务委托给最匹配的子Agent处理
35
35
  - 使用 subagent_call 工具并指定 agentName 来委托任务
@@ -6,18 +6,18 @@
6
6
  * - forceLogin: 是否强制重新扫码登录
7
7
  * - qrcodeTerminal: 是否在终端渲染二维码 (默认 true)
8
8
  */
9
- const { CLEAR_LINE, CYAN, DIM, GREEN, RED, YELLOW, colored } = require('../cli/src/utils/ansi');
10
- const { renderLine } = require('../cli/src/utils/markdown');
11
- const { debounce, throttle } = require('../src/utils');
9
+ const { CLEAR_LINE, CYAN, DIM, GREEN, RED, YELLOW, colored } = require('../../../cli/src/utils/ansi');
10
+ const { renderLine } = require('../../../cli/src/utils/markdown');
11
+ const { debounce, throttle } = require('../../../src/utils');
12
12
  const interval = (fn) => {
13
13
  const handle = setInterval(fn, 3000);
14
14
  return { start: () => handle, stop: () => clearInterval(handle) };
15
15
  };
16
- const { Plugin } = require('../src/core/plugin-base')
17
- const { logger } = require('../src/utils/logger')
16
+ const { Plugin } = require('../../../src/plugin/base')
17
+ const { logger } = require('../../../src/common/logger')
18
18
  const log = logger.child('WeChat')
19
- const {cleanResponse} =require('../src/utils')
20
- const {FileDownloader} = require('../src/utils/download')
19
+ const {cleanResponse} =require('../../../src/utils')
20
+ const {FileDownloader} = require('../../../src/utils/download')
21
21
  const removeMarkdown = require('remove-markdown');
22
22
  const { z } = require('zod')
23
23
  const { WeixinBot} = require('@chnak/weixin-bot')
@@ -338,7 +338,7 @@ class WeixinPlugin extends Plugin {
338
338
  // log.info(' 已记录机器人 userId:', this._myUserId)
339
339
  const {agent,sessionId} = this._getSessionAgent(creds.userId)
340
340
  const sessionScope = agent.createSessionScope(sessionId);
341
-
341
+
342
342
  // 注册消息处理
343
343
  this._bot.onMessage(async (msg) => {
344
344
  await this._handleMessage(msg)
@@ -417,9 +417,9 @@ class WeixinPlugin extends Plugin {
417
417
 
418
418
  // 消息完成
419
419
  // sessionScope.on('message:complete', async ({ content }) => {
420
-
420
+
421
421
  // if (!content) {
422
-
422
+
423
423
  // }
424
424
  // });
425
425
 
@@ -478,7 +478,7 @@ class WeixinPlugin extends Plugin {
478
478
  sharedPrompt: `工作目录: {{WORK_DIR}}`,
479
479
  metadata: { WORK_DIR: this._framework?.getCwd?.() ?? process.cwd() }
480
480
  })
481
-
481
+
482
482
  this._sessionAgents.set(userId, agent)
483
483
  // log.info(' Created new session agent for userId:', userId)
484
484
 
@@ -528,7 +528,7 @@ class WeixinPlugin extends Plugin {
528
528
  voice:"音频"
529
529
  }
530
530
  const files=await this._save_file(msg)
531
-
531
+
532
532
  await this._processChat(userId, `
533
533
  [用户发送了${type_dict[msg.type]}消息]\n
534
534
  文件列表:\n
@@ -549,7 +549,7 @@ class WeixinPlugin extends Plugin {
549
549
  }
550
550
  }
551
551
  }
552
-
552
+
553
553
  async _save_file(msg){
554
554
  const files=[]
555
555
  try{
@@ -562,7 +562,7 @@ class WeixinPlugin extends Plugin {
562
562
  const file=await this.downloader.fromWeXin(url,aesKey)
563
563
  files.push(file.path)
564
564
  }
565
-
565
+
566
566
  }
567
567
  }catch(err){
568
568
  console.log(err)
@@ -1044,4 +1044,4 @@ class WeixinPlugin extends Plugin {
1044
1044
  }
1045
1045
  }
1046
1046
 
1047
- module.exports = WeixinPlugin
1047
+ module.exports = WeixinPlugin
@@ -6,67 +6,40 @@
6
6
  const fs = require('fs');
7
7
  const path = require('path');
8
8
  const { execSync } = require('child_process');
9
- const { Plugin } = require('../src/core/plugin-base');
10
- const { logger } = require('../src/utils/logger');
9
+ const { Plugin } = require('../../src/plugin/base');
10
+ const { logger } = require('../../src/common/logger');
11
11
  const { z } = require('zod');
12
-
13
- // 从共享配置加载
14
- const { DEFAULT_REPO, shouldIgnore } = require('../cli/src/utils/plugin-config');
12
+ const { DEFAULT_REPO, shouldIgnore } = require('../../src/cli/utils/plugin-config');
15
13
 
16
14
  const log = logger.child('PluginManagerPlugin');
17
15
 
18
- /**
19
- * 递归复制目录(带过滤)
20
- */
21
16
  function copyDirRecursive(src, dest) {
22
17
  if (!fs.existsSync(src)) return;
23
-
24
18
  fs.mkdirSync(dest, { recursive: true });
25
19
 
26
20
  const entries = fs.readdirSync(src, { withFileTypes: true });
27
-
28
21
  for (const entry of entries) {
29
22
  const srcPath = path.join(src, entry.name);
30
23
  const destPath = path.join(dest, entry.name);
31
-
32
- if (shouldIgnore(entry.name)) {
33
- log.debug(`Ignoring: ${entry.name}`);
34
- continue;
35
- }
36
-
37
- if (entry.isDirectory()) {
38
- copyDirRecursive(srcPath, destPath);
39
- } else {
40
- fs.copyFileSync(srcPath, destPath);
41
- }
24
+ if (shouldIgnore(entry.name)) { log.debug(`Ignoring: ${entry.name}`); continue; }
25
+ if (entry.isDirectory()) { copyDirRecursive(srcPath, destPath); }
26
+ else { fs.copyFileSync(srcPath, destPath); }
42
27
  }
43
28
  }
44
29
 
45
- /**
46
- * 执行 Git 命令
47
- */
48
30
  function gitCommand(args, cwd) {
49
- try {
50
- return execSync(`git ${args}`, { cwd, encoding: 'utf-8', stdio: 'pipe' });
51
- } catch (err) {
52
- return err.stdout || err.stderr || '';
53
- }
31
+ try { return execSync(`git ${args}`, { cwd, encoding: 'utf-8', stdio: 'pipe' }); }
32
+ catch (err) { return err.stdout || err.stderr || ''; }
54
33
  }
55
34
 
56
- /**
57
- * 解析 Git URL 获取信息
58
- */
59
35
  function parseGitUrl(url) {
60
36
  const patterns = [
61
37
  /^https:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?$/,
62
38
  /^git@github\.com:([^/]+)\/([^/]+?)(?:\.git)?$/,
63
39
  ];
64
-
65
40
  for (const pattern of patterns) {
66
41
  const match = url.match(pattern);
67
- if (match) {
68
- return { owner: match[1], repo: match[2] };
69
- }
42
+ if (match) return { owner: match[1], repo: match[2] };
70
43
  }
71
44
  return null;
72
45
  }
@@ -74,12 +47,12 @@ function parseGitUrl(url) {
74
47
  class PluginManagerPlugin extends Plugin {
75
48
  constructor(config = {}) {
76
49
  super();
77
- this.name = 'plugin_manager';
50
+ this.name = 'plugin-manager';
78
51
  this.version = '1.0.0';
79
52
  this.description = '管理远程插件:列表、发布、安装';
80
53
  this._repo = config.repo || process.env.FOLIKO_PLUGIN_REPO || DEFAULT_REPO;
81
54
  this.system = true;
82
- this.tools = {}; // ext_call 需要通过 this.tools 访问工具
55
+ this.tools = {};
83
56
  }
84
57
 
85
58
  install(framework) {
@@ -88,7 +61,6 @@ class PluginManagerPlugin extends Plugin {
88
61
  }
89
62
 
90
63
  start(framework) {
91
- // 1. plugin_list - 列出远程仓库插件
92
64
  framework.registerTool({
93
65
  name: 'plugin_list',
94
66
  description: '列出远程插件仓库中所有可用的插件',
@@ -101,7 +73,6 @@ class PluginManagerPlugin extends Plugin {
101
73
  }
102
74
  });
103
75
 
104
- // 2. plugin_publish - 发布本地插件到远程仓库
105
76
  framework.registerTool({
106
77
  name: 'plugin_publish',
107
78
  description: '将本地插件发布到远程 Git 仓库(需要仓库写权限)',
@@ -116,7 +87,6 @@ class PluginManagerPlugin extends Plugin {
116
87
  }
117
88
  });
118
89
 
119
- // 3. plugin_install - 从远程仓库安装插件到本地
120
90
  framework.registerTool({
121
91
  name: 'plugin_install',
122
92
  description: '从远程 Git 仓库安装插件到本地 .foliko/plugins 目录',
@@ -134,40 +104,26 @@ class PluginManagerPlugin extends Plugin {
134
104
  return this;
135
105
  }
136
106
 
137
- /**
138
- * 列出远程仓库的插件
139
- */
140
107
  async _listPlugins(repo) {
141
108
  try {
142
- //log.info(`Listing plugins from ${repo}...`);
143
-
144
109
  const repoInfo = parseGitUrl(repo);
145
- if (!repoInfo) {
146
- return { success: false, error: 'Invalid repository URL' };
147
- }
110
+ if (!repoInfo) return { success: false, error: 'Invalid repository URL' };
148
111
 
149
- // 使用 GitHub API 获取仓库内容
150
112
  const apiUrl = `https://api.github.com/repos/${repoInfo.owner}/${repoInfo.repo}/contents`;
151
113
  const response = await fetch(apiUrl);
152
-
153
114
  if (!response.ok) {
154
- if (response.status === 404) {
155
- return { success: false, error: `Repository not found: ${repo}` };
156
- }
115
+ if (response.status === 404) return { success: false, error: `Repository not found: ${repo}` };
157
116
  return { success: false, error: `GitHub API error: ${response.status}` };
158
117
  }
159
118
 
160
119
  const contents = await response.json();
161
-
162
120
  if (!Array.isArray(contents) || contents.length === 0) {
163
121
  return { success: true, data: [], metadata: { message: 'No plugins found in repository' } };
164
122
  }
165
123
 
166
124
  const plugins = [];
167
-
168
125
  for (const item of contents) {
169
126
  if (item.type === 'dir') {
170
- // 获取 README 描述
171
127
  let description = '-';
172
128
  try {
173
129
  const readmeUrl = `https://raw.githubusercontent.com/${repoInfo.owner}/${repoInfo.repo}/main/${item.name}/README.md`;
@@ -175,151 +131,83 @@ class PluginManagerPlugin extends Plugin {
175
131
  if (readmeResp.ok) {
176
132
  const readmeText = await readmeResp.text();
177
133
  const lines = readmeText.split('\n').filter(l => l.trim());
178
- if (lines.length > 1) {
179
- description = lines.slice(1).join(' ').slice(0, 100);
180
- }
134
+ if (lines.length > 1) description = lines.slice(1).join(' ').slice(0, 100);
181
135
  }
182
- } catch (e) {}
183
-
184
- plugins.push({
185
- name: item.name,
186
- type: 'directory',
187
- description,
188
- });
136
+ } catch (e) { /* ignore */ }
137
+ plugins.push({ name: item.name, type: 'directory', description });
189
138
  } else if (item.type === 'file' && item.name.endsWith('.js')) {
190
- const name = item.name.replace('.js', '');
191
- plugins.push({
192
- name,
193
- type: 'file',
194
- description: '(root file)',
195
- });
139
+ plugins.push({ name: item.name.replace('.js', ''), type: 'file', description: '(root file)' });
196
140
  }
197
141
  }
198
142
 
199
- return {
200
- success: true,
201
- data: plugins,
202
- metadata: { repo, count: plugins.length },
203
- };
204
-
143
+ return { success: true, data: plugins, metadata: { repo, count: plugins.length } };
205
144
  } catch (err) {
206
145
  log.error(`List plugins failed: ${err.message}`);
207
146
  return { success: false, error: err.message };
208
147
  }
209
148
  }
210
149
 
211
- /**
212
- * 发布插件到远程仓库
213
- */
214
150
  async _publishPlugin(pluginName, repo) {
215
151
  const cwd = this._framework?.getCwd?.() ?? process.cwd();
216
152
  const pluginsDir = path.resolve(cwd, '.foliko', 'plugins');
217
153
  const localPluginsDir = path.resolve(cwd, 'plugins');
218
154
 
219
- // 确定插件目录
220
155
  let actualPluginsDir = pluginsDir;
221
156
  if (!fs.existsSync(pluginsDir)) {
222
- if (fs.existsSync(localPluginsDir)) {
223
- actualPluginsDir = localPluginsDir;
224
- } else {
225
- return { success: false, error: `Plugins directory not found` };
226
- }
157
+ if (fs.existsSync(localPluginsDir)) { actualPluginsDir = localPluginsDir; }
158
+ else { return { success: false, error: 'Plugins directory not found' }; }
227
159
  }
228
160
 
229
- const pluginPath = path.join(actualPluginsDir, `${pluginName}.js`);
161
+ const pluginPathFile = path.join(actualPluginsDir, `${pluginName}.js`);
230
162
  const pluginSourceDir = path.join(actualPluginsDir, pluginName);
231
163
 
232
- // 检查插件是否存在
233
- if (!fs.existsSync(pluginPath) && !fs.existsSync(pluginSourceDir)) {
234
- return {
235
- success: false,
236
- error: `Plugin "${pluginName}" not found`,
237
- available: fs.readdirSync(actualPluginsDir).filter(f => {
238
- if (f.endsWith('.js')) return true;
239
- const fullPath = path.join(actualPluginsDir, f);
240
- return fs.statSync(fullPath).isDirectory();
241
- }),
242
- };
164
+ if (!fs.existsSync(pluginPathFile) && !fs.existsSync(pluginSourceDir)) {
165
+ return { success: false, error: `Plugin "${pluginName}" not found`, available: fs.readdirSync(actualPluginsDir).filter(f => f.endsWith('.js') || fs.statSync(path.join(actualPluginsDir, f)).isDirectory()) };
243
166
  }
244
167
 
245
168
  const tmpDir = path.join(require('os').tmpdir(), `foliko-plugin-publish-${Date.now()}`);
246
-
247
169
  try {
248
- //log.info(`Publishing plugin "${pluginName}" to ${repo}...`);
249
-
250
- // 克隆仓库
251
170
  fs.mkdirSync(tmpDir, { recursive: true });
252
171
 
253
172
  let isNewRepo = false;
254
- try {
255
- gitCommand(`clone ${repo} "${tmpDir}" --depth 1`, this._framework?.getCwd?.() ?? process.cwd());
256
- } catch (err) {
257
- //log.info('Initializing new repository...');
173
+ try { gitCommand(`clone ${repo} "${tmpDir}" --depth 1`, cwd); }
174
+ catch (err) {
258
175
  fs.mkdirSync(tmpDir, { recursive: true });
259
176
  gitCommand('init', tmpDir);
260
177
  gitCommand(`remote add origin ${repo}`, tmpDir);
261
178
  isNewRepo = true;
262
179
  }
263
180
 
264
- // 创建插件目录
265
181
  const pluginDir = path.join(tmpDir, pluginName);
266
182
  fs.mkdirSync(pluginDir, { recursive: true });
267
183
 
268
- // 复制插件
269
184
  let pluginContent = null;
270
185
  if (fs.existsSync(pluginSourceDir) && fs.statSync(pluginSourceDir).isDirectory()) {
271
- // log.info('Copying plugin directory...');
272
186
  copyDirRecursive(pluginSourceDir, pluginDir);
273
-
274
187
  const mainJsPath = path.join(pluginSourceDir, `${pluginName}.js`);
275
- if (fs.existsSync(mainJsPath)) {
276
- pluginContent = fs.readFileSync(mainJsPath, 'utf-8');
277
- }
188
+ if (fs.existsSync(mainJsPath)) pluginContent = fs.readFileSync(mainJsPath, 'utf-8');
278
189
  } else {
279
- //log.info('Copying plugin file...');
280
- pluginContent = fs.readFileSync(pluginPath, 'utf-8');
281
- const targetPath = path.join(pluginDir, `${pluginName}.js`);
282
- fs.writeFileSync(targetPath, pluginContent);
283
-
190
+ pluginContent = fs.readFileSync(pluginPathFile, 'utf-8');
191
+ fs.writeFileSync(path.join(pluginDir, `${pluginName}.js`), pluginContent);
284
192
  const configPath = path.join(actualPluginsDir, `${pluginName}.json`);
285
- if (fs.existsSync(configPath)) {
286
- fs.copyFileSync(configPath, path.join(pluginDir, `${pluginName}.json`));
287
- }
193
+ if (fs.existsSync(configPath)) fs.copyFileSync(configPath, path.join(pluginDir, `${pluginName}.json`));
288
194
  }
289
195
 
290
- // 创建 README.md
291
196
  const readmePath = path.join(pluginDir, 'README.md');
292
197
  if (!fs.existsSync(readmePath)) {
293
- //log.info('Creating README.md...');
294
- const descMatch = pluginContent?.match(/\*\*Description\*\*:\s*(.+)/) ||
295
- pluginContent?.match(/description[:\s]+(.+)/i);
198
+ const descMatch = pluginContent?.match(/\*\*Description\*\*:\s*(.+)/) || pluginContent?.match(/description[:\s]+(.+)/i);
296
199
  const desc = descMatch ? descMatch[1] : `Foliko plugin: ${pluginName}`;
297
200
  fs.writeFileSync(readmePath, `# ${pluginName}\n\n${desc}\n`);
298
201
  }
299
202
 
300
- // Git 提交和推送
301
203
  gitCommand('add .', tmpDir);
302
-
303
204
  const status = gitCommand('status --porcelain', tmpDir);
304
- if (!status.trim()) {
305
- return {
306
- success: true,
307
- data: `No changes to commit for plugin "${pluginName}"`,
308
- metadata: { repo },
309
- };
310
- }
205
+ if (!status.trim()) return { success: true, data: `No changes to commit for plugin "${pluginName}"`, metadata: { repo } };
311
206
 
312
207
  gitCommand(`commit -m "Add/update plugin: ${pluginName}"`, tmpDir);
313
208
  gitCommand(`push ${isNewRepo ? '-u' : ''} origin main`, tmpDir);
314
209
 
315
- //log.info(`Plugin "${pluginName}" published successfully!`);
316
-
317
- return {
318
- success: true,
319
- data: `Plugin "${pluginName}" published successfully`,
320
- metadata: { repo, path: `${pluginName}/${pluginName}.js` },
321
- };
322
-
210
+ return { success: true, data: `Plugin "${pluginName}" published successfully`, metadata: { repo, path: `${pluginName}/${pluginName}.js` } };
323
211
  } catch (err) {
324
212
  log.error(`Publish failed: ${err.message}`);
325
213
  return { success: false, error: err.message };
@@ -328,25 +216,17 @@ class PluginManagerPlugin extends Plugin {
328
216
  }
329
217
  }
330
218
 
331
- /**
332
- * 从远程仓库安装插件
333
- */
334
219
  async _installPlugin(pluginName, repo) {
335
220
  const cwd = this._framework?.getCwd?.() ?? process.cwd();
336
221
  const localPluginsDir = path.resolve(cwd, '.foliko', 'plugins');
337
222
  const tmpDir = path.join(require('os').tmpdir(), `foliko-plugin-install-${Date.now()}`);
338
223
 
339
224
  try {
340
- //log.info(`Installing plugin "${pluginName}" from ${repo}...`);
341
-
342
- // 克隆仓库
343
225
  fs.mkdirSync(tmpDir, { recursive: true });
344
226
  gitCommand(`clone ${repo} "${tmpDir}" --depth 1`, cwd);
345
227
 
346
- // 查找插件
347
228
  const pluginDir = path.join(tmpDir, pluginName);
348
229
  let sourcePath;
349
-
350
230
  if (fs.existsSync(pluginDir) && fs.statSync(pluginDir).isDirectory()) {
351
231
  sourcePath = path.join(pluginDir, `${pluginName}.js`);
352
232
  } else {
@@ -354,44 +234,20 @@ class PluginManagerPlugin extends Plugin {
354
234
  }
355
235
 
356
236
  if (!fs.existsSync(sourcePath)) {
357
- // 列出可用插件
358
- const available = fs.readdirSync(tmpDir).filter(f => {
359
- const fullPath = path.join(tmpDir, f);
360
- return fs.statSync(fullPath).isDirectory();
361
- });
362
-
363
- return {
364
- success: false,
365
- error: `Plugin "${pluginName}" not found in repository`,
366
- available,
367
- };
237
+ const available = fs.readdirSync(tmpDir).filter(f => fs.statSync(path.join(tmpDir, f)).isDirectory());
238
+ return { success: false, error: `Plugin "${pluginName}" not found in repository`, available };
368
239
  }
369
240
 
370
- // 确保本地目录存在
371
- if (!fs.existsSync(localPluginsDir)) {
372
- fs.mkdirSync(localPluginsDir, { recursive: true });
373
- }
241
+ if (!fs.existsSync(localPluginsDir)) fs.mkdirSync(localPluginsDir, { recursive: true });
374
242
 
375
- // 复制插件到本地
376
243
  const targetDir = path.join(localPluginsDir, pluginName);
377
244
  fs.mkdirSync(targetDir, { recursive: true });
378
-
379
- // 复制目录内容
380
245
  copyDirRecursive(path.dirname(sourcePath), targetDir);
381
-
382
- // 如果是单文件插件,复制主文件
383
246
  if (!fs.existsSync(path.join(targetDir, `${pluginName}.js`))) {
384
247
  fs.copyFileSync(sourcePath, path.join(targetDir, `${pluginName}.js`));
385
248
  }
386
249
 
387
- //log.info(`Plugin "${pluginName}" installed to ${targetDir}`);
388
-
389
- return {
390
- success: true,
391
- data: `Plugin "${pluginName}" installed successfully`,
392
- metadata: { path: targetDir },
393
- };
394
-
250
+ return { success: true, data: `Plugin "${pluginName}" installed successfully`, metadata: { path: targetDir } };
395
251
  } catch (err) {
396
252
  log.error(`Install failed: ${err.message}`);
397
253
  return { success: false, error: err.message };