foliko 1.0.74 → 1.0.76

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 (238) hide show
  1. package/.agent/.shared/ui-ux-pro-max/data/charts.csv +26 -0
  2. package/.agent/.shared/ui-ux-pro-max/data/colors.csv +97 -0
  3. package/.agent/.shared/ui-ux-pro-max/data/icons.csv +101 -0
  4. package/.agent/.shared/ui-ux-pro-max/data/landing.csv +31 -0
  5. package/.agent/.shared/ui-ux-pro-max/data/products.csv +97 -0
  6. package/.agent/.shared/ui-ux-pro-max/data/prompts.csv +24 -0
  7. package/.agent/.shared/ui-ux-pro-max/data/react-performance.csv +45 -0
  8. package/.agent/.shared/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
  9. package/.agent/.shared/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
  10. package/.agent/.shared/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
  11. package/.agent/.shared/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
  12. package/.agent/.shared/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
  13. package/.agent/.shared/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
  14. package/.agent/.shared/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
  15. package/.agent/.shared/ui-ux-pro-max/data/stacks/react.csv +54 -0
  16. package/.agent/.shared/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
  17. package/.agent/.shared/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
  18. package/.agent/.shared/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
  19. package/.agent/.shared/ui-ux-pro-max/data/stacks/vue.csv +50 -0
  20. package/.agent/.shared/ui-ux-pro-max/data/styles.csv +59 -0
  21. package/.agent/.shared/ui-ux-pro-max/data/typography.csv +58 -0
  22. package/.agent/.shared/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
  23. package/.agent/.shared/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
  24. package/.agent/.shared/ui-ux-pro-max/data/web-interface.csv +31 -0
  25. package/.agent/.shared/ui-ux-pro-max/scripts/__pycache__/core.cpython-313.pyc +0 -0
  26. package/.agent/.shared/ui-ux-pro-max/scripts/__pycache__/design_system.cpython-313.pyc +0 -0
  27. package/.agent/.shared/ui-ux-pro-max/scripts/core.py +258 -0
  28. package/.agent/.shared/ui-ux-pro-max/scripts/design_system.py +1067 -0
  29. package/.agent/.shared/ui-ux-pro-max/scripts/search.py +106 -0
  30. package/.agent/ARCHITECTURE.md +288 -0
  31. package/.agent/agents/ambient-agent.md +57 -0
  32. package/.agent/agents/debugger.md +55 -0
  33. package/.agent/agents/email-assistant.md +49 -0
  34. package/.agent/agents/file-manager.md +42 -0
  35. package/.agent/agents/python-developer.md +60 -0
  36. package/.agent/agents/scheduler.md +59 -0
  37. package/.agent/agents/web-developer.md +45 -0
  38. package/.agent/data/default.json +29 -0
  39. package/.agent/data/plugins-state.json +255 -0
  40. package/.agent/mcp_config.json +4 -0
  41. package/.agent/mcp_config_updated.json +12 -0
  42. package/.agent/plugins.json +5 -0
  43. package/.agent/rules/GEMINI.md +273 -0
  44. package/.agent/rules/allow-rule.md +77 -0
  45. package/.agent/rules/log-rule.md +83 -0
  46. package/.agent/rules/security-rule.md +93 -0
  47. package/.agent/scripts/auto_preview.py +148 -0
  48. package/.agent/scripts/checklist.py +217 -0
  49. package/.agent/scripts/session_manager.py +120 -0
  50. package/.agent/scripts/verify_all.py +327 -0
  51. package/.agent/skills/api-patterns/SKILL.md +81 -0
  52. package/.agent/skills/api-patterns/api-style.md +42 -0
  53. package/.agent/skills/api-patterns/auth.md +24 -0
  54. package/.agent/skills/api-patterns/documentation.md +26 -0
  55. package/.agent/skills/api-patterns/graphql.md +41 -0
  56. package/.agent/skills/api-patterns/rate-limiting.md +31 -0
  57. package/.agent/skills/api-patterns/response.md +37 -0
  58. package/.agent/skills/api-patterns/rest.md +40 -0
  59. package/.agent/skills/api-patterns/scripts/api_validator.py +211 -0
  60. package/.agent/skills/api-patterns/security-testing.md +122 -0
  61. package/.agent/skills/api-patterns/trpc.md +41 -0
  62. package/.agent/skills/api-patterns/versioning.md +22 -0
  63. package/.agent/skills/app-builder/SKILL.md +75 -0
  64. package/.agent/skills/app-builder/agent-coordination.md +71 -0
  65. package/.agent/skills/app-builder/feature-building.md +53 -0
  66. package/.agent/skills/app-builder/project-detection.md +34 -0
  67. package/.agent/skills/app-builder/scaffolding.md +118 -0
  68. package/.agent/skills/app-builder/tech-stack.md +40 -0
  69. package/.agent/skills/app-builder/templates/SKILL.md +39 -0
  70. package/.agent/skills/app-builder/templates/astro-static/TEMPLATE.md +76 -0
  71. package/.agent/skills/app-builder/templates/chrome-extension/TEMPLATE.md +92 -0
  72. package/.agent/skills/app-builder/templates/cli-tool/TEMPLATE.md +88 -0
  73. package/.agent/skills/app-builder/templates/electron-desktop/TEMPLATE.md +88 -0
  74. package/.agent/skills/app-builder/templates/express-api/TEMPLATE.md +83 -0
  75. package/.agent/skills/app-builder/templates/flutter-app/TEMPLATE.md +90 -0
  76. package/.agent/skills/app-builder/templates/monorepo-turborepo/TEMPLATE.md +90 -0
  77. package/.agent/skills/app-builder/templates/nextjs-fullstack/TEMPLATE.md +122 -0
  78. package/.agent/skills/app-builder/templates/nextjs-saas/TEMPLATE.md +122 -0
  79. package/.agent/skills/app-builder/templates/nextjs-static/TEMPLATE.md +169 -0
  80. package/.agent/skills/app-builder/templates/nuxt-app/TEMPLATE.md +134 -0
  81. package/.agent/skills/app-builder/templates/python-fastapi/TEMPLATE.md +83 -0
  82. package/.agent/skills/app-builder/templates/react-native-app/TEMPLATE.md +119 -0
  83. package/.agent/skills/architecture/SKILL.md +55 -0
  84. package/.agent/skills/architecture/context-discovery.md +43 -0
  85. package/.agent/skills/architecture/examples.md +94 -0
  86. package/.agent/skills/architecture/pattern-selection.md +68 -0
  87. package/.agent/skills/architecture/patterns-reference.md +50 -0
  88. package/.agent/skills/architecture/trade-off-analysis.md +77 -0
  89. package/.agent/skills/clean-code/SKILL.md +201 -0
  90. package/.agent/skills/doc.md +177 -0
  91. package/.agent/skills/frontend-design/SKILL.md +418 -0
  92. package/.agent/skills/frontend-design/animation-guide.md +331 -0
  93. package/.agent/skills/frontend-design/color-system.md +311 -0
  94. package/.agent/skills/frontend-design/decision-trees.md +418 -0
  95. package/.agent/skills/frontend-design/motion-graphics.md +306 -0
  96. package/.agent/skills/frontend-design/scripts/accessibility_checker.py +183 -0
  97. package/.agent/skills/frontend-design/scripts/ux_audit.py +722 -0
  98. package/.agent/skills/frontend-design/typography-system.md +345 -0
  99. package/.agent/skills/frontend-design/ux-psychology.md +1116 -0
  100. package/.agent/skills/frontend-design/visual-effects.md +383 -0
  101. package/.agent/skills/i18n-localization/SKILL.md +154 -0
  102. package/.agent/skills/i18n-localization/scripts/i18n_checker.py +241 -0
  103. package/.agent/skills/mcp-builder/SKILL.md +176 -0
  104. package/.agent/skills/web-design-guidelines/SKILL.md +57 -0
  105. package/.agent/workflows/brainstorm.md +113 -0
  106. package/.agent/workflows/create.md +59 -0
  107. package/.agent/workflows/debug.md +103 -0
  108. package/.agent/workflows/deploy.md +176 -0
  109. package/.agent/workflows/enhance.md +63 -0
  110. package/.agent/workflows/orchestrate.md +237 -0
  111. package/.agent/workflows/plan.md +89 -0
  112. package/.agent/workflows/preview.md +81 -0
  113. package/.agent/workflows/simple-test.md +42 -0
  114. package/.agent/workflows/status.md +86 -0
  115. package/.agent/workflows/structured-orchestrate.md +180 -0
  116. package/.agent/workflows/test.md +144 -0
  117. package/.agent/workflows/ui-ux-pro-max.md +296 -0
  118. package/.claude/settings.local.json +11 -1
  119. package/.editorconfig +56 -0
  120. package/.husky/pre-commit +4 -0
  121. package/.lintstagedrc +7 -0
  122. package/.prettierignore +29 -0
  123. package/.prettierrc +11 -0
  124. package/CLAUDE.md +2 -0
  125. package/README.md +64 -55
  126. package/SPEC.md +102 -61
  127. package/cli/bin/foliko.js +11 -11
  128. package/cli/src/commands/chat.js +143 -141
  129. package/cli/src/commands/list.js +93 -90
  130. package/cli/src/index.js +75 -75
  131. package/cli/src/ui/chat-ui.js +201 -199
  132. package/cli/src/utils/ansi.js +40 -40
  133. package/cli/src/utils/markdown.js +292 -296
  134. package/docker-compose.yml +1 -1
  135. package/docs/ai-sdk-optimization.md +655 -643
  136. package/docs/features.md +80 -80
  137. package/docs/quick-reference.md +49 -46
  138. package/docs/user-manual.md +411 -380
  139. package/examples/ambient-example.js +194 -196
  140. package/examples/basic.js +50 -45
  141. package/examples/bootstrap.js +121 -112
  142. package/examples/mcp-example.js +19 -16
  143. package/examples/skill-example.js +20 -20
  144. package/examples/test-chat.js +137 -135
  145. package/examples/test-mcp.js +85 -79
  146. package/examples/test-reload.js +59 -61
  147. package/examples/test-telegram.js +50 -50
  148. package/examples/test-tg-bot.js +45 -42
  149. package/examples/test-tg-simple.js +47 -46
  150. package/examples/test-tg.js +62 -62
  151. package/examples/test-think.js +43 -37
  152. package/examples/test-web-plugin.js +103 -98
  153. package/examples/test-weixin-feishu.js +103 -100
  154. package/examples/workflow.js +158 -158
  155. package/package.json +37 -3
  156. package/plugins/ai-plugin.js +102 -100
  157. package/plugins/ambient-agent/EventWatcher.js +113 -0
  158. package/plugins/ambient-agent/ExplorerLoop.js +640 -0
  159. package/plugins/ambient-agent/GoalManager.js +197 -0
  160. package/plugins/ambient-agent/Reflector.js +95 -0
  161. package/plugins/ambient-agent/StateStore.js +90 -0
  162. package/plugins/ambient-agent/constants.js +101 -0
  163. package/plugins/ambient-agent/index.js +579 -0
  164. package/plugins/audit-plugin.js +187 -187
  165. package/plugins/default-plugins.js +662 -649
  166. package/plugins/email/constants.js +64 -0
  167. package/plugins/email/handlers.js +461 -0
  168. package/plugins/email/index.js +278 -0
  169. package/plugins/email/monitor.js +269 -0
  170. package/plugins/email/parser.js +138 -0
  171. package/plugins/email/reply.js +151 -0
  172. package/plugins/email/utils.js +124 -0
  173. package/plugins/feishu-plugin.js +481 -477
  174. package/plugins/file-system-plugin.js +826 -476
  175. package/plugins/install-plugin.js +199 -197
  176. package/plugins/python-executor-plugin.js +367 -365
  177. package/plugins/python-plugin-loader.js +481 -479
  178. package/plugins/rules-plugin.js +294 -292
  179. package/plugins/scheduler-plugin.js +691 -689
  180. package/plugins/session-plugin.js +369 -367
  181. package/plugins/shell-executor-plugin.js +197 -197
  182. package/plugins/storage-plugin.js +240 -238
  183. package/plugins/subagent-plugin.js +845 -785
  184. package/plugins/telegram-plugin.js +482 -475
  185. package/plugins/think-plugin.js +345 -343
  186. package/plugins/tools-plugin.js +196 -194
  187. package/plugins/web-plugin.js +606 -604
  188. package/plugins/weixin-plugin.js +545 -538
  189. package/reports/system-health-report-20260401.md +79 -0
  190. package/skills/ambient-agent/SKILL.md +49 -39
  191. package/skills/foliko-dev/AGENTS.md +64 -61
  192. package/skills/foliko-dev/SKILL.md +125 -119
  193. package/skills/mcp-usage/SKILL.md +19 -17
  194. package/skills/python-plugin-dev/SKILL.md +16 -15
  195. package/skills/skill-guide/SKILL.md +12 -12
  196. package/skills/subagent-guide/SKILL.md +237 -0
  197. package/skills/workflow-guide/SKILL.md +90 -45
  198. package/skills/workflow-troubleshooting/DEBUGGING.md +36 -21
  199. package/skills/workflow-troubleshooting/SKILL.md +156 -79
  200. package/src/capabilities/index.js +11 -11
  201. package/src/capabilities/skill-manager.js +609 -595
  202. package/src/capabilities/workflow-engine.js +1109 -1195
  203. package/src/core/agent-chat.js +882 -735
  204. package/src/core/agent.js +892 -688
  205. package/src/core/framework.js +465 -431
  206. package/src/core/index.js +19 -19
  207. package/src/core/plugin-base.js +219 -219
  208. package/src/core/plugin-manager.js +863 -767
  209. package/src/core/provider.js +114 -111
  210. package/src/core/sub-agent-config.js +264 -0
  211. package/src/core/system-prompt-builder.js +120 -0
  212. package/src/core/tool-registry.js +517 -134
  213. package/src/core/tool-router.js +297 -216
  214. package/src/executors/executor-base.js +12 -12
  215. package/src/executors/mcp-executor.js +741 -729
  216. package/src/index.js +25 -37
  217. package/src/utils/circuit-breaker.js +301 -0
  218. package/src/utils/error-boundary.js +363 -0
  219. package/src/utils/error.js +374 -0
  220. package/src/utils/event-emitter.js +97 -97
  221. package/src/utils/id.js +133 -0
  222. package/src/utils/index.js +217 -3
  223. package/src/utils/logger.js +181 -0
  224. package/src/utils/plugin-helpers.js +90 -0
  225. package/src/utils/retry.js +122 -0
  226. package/src/utils/sandbox.js +292 -0
  227. package/test/tool-registry-validation.test.js +218 -0
  228. package/test_report.md +70 -0
  229. package/website/docs/api.html +169 -107
  230. package/website/docs/configuration.html +296 -144
  231. package/website/docs/plugin-development.html +154 -85
  232. package/website/docs/project-structure.html +110 -109
  233. package/website/docs/skill-development.html +117 -61
  234. package/website/index.html +209 -205
  235. package/website/script.js +136 -133
  236. package/website/styles.css +1 -1
  237. package/plugins/ambient-agent-plugin.js +0 -1565
  238. package/plugins/email.js +0 -1142
@@ -1,3 +1,217 @@
1
- exports.cleanResponse=function (text) {
2
- return text.replace(/<think>[\s\S]*?<\/think>/g, '').trim();
3
- }
1
+ /**
2
+ * Utils 模块导出
3
+ */
4
+
5
+ // 日志系统
6
+ const { Logger, logger, LOG_LEVELS } = require('./logger');
7
+
8
+ // ID 生成器
9
+ const {
10
+ uuid,
11
+ prefixedId,
12
+ shortId,
13
+ snowflake,
14
+ taskId,
15
+ goalId,
16
+ sessionId,
17
+ messageId,
18
+ ruleId,
19
+ resetSnowflake,
20
+ } = require('./id');
21
+
22
+ // 错误处理
23
+ const {
24
+ FolikoError,
25
+ PluginError,
26
+ ToolError,
27
+ AIError,
28
+ SessionError,
29
+ ConfigError,
30
+ WorkflowError,
31
+ ValidationError,
32
+ ToolNotFoundError,
33
+ PluginNotFoundError,
34
+ isErrorOfType,
35
+ safeErrorInfo,
36
+ } = require('./error');
37
+
38
+ // 重试策略
39
+ const { withRetry, retryable, isNetworkError, PRESETS } = require('./retry');
40
+
41
+ // 错误边界
42
+ const {
43
+ ErrorBoundary,
44
+ Severity,
45
+ RecoveryAction,
46
+ createErrorBoundary,
47
+ combineBoundaries,
48
+ } = require('./error-boundary');
49
+
50
+ /**
51
+ * 清理 LLM 回复中的思考标记
52
+ * @param {string} text
53
+ * @returns {string}
54
+ */
55
+ function cleanResponse(text) {
56
+ return text.replace(/<think>[\s\S]*?<\/think>/g, '').trim();
57
+ }
58
+
59
+ /**
60
+ * 安全解析 JSON
61
+ * @param {string} str
62
+ * @param {*} defaultValue
63
+ * @returns {*}
64
+ */
65
+ function safeJsonParse(str, defaultValue = null) {
66
+ try {
67
+ return JSON.parse(str);
68
+ } catch {
69
+ return defaultValue;
70
+ }
71
+ }
72
+
73
+ /**
74
+ * 深拷贝
75
+ * @param {*} obj
76
+ * @returns {*}
77
+ */
78
+ function deepClone(obj) {
79
+ if (obj === null || typeof obj !== 'object') return obj;
80
+ if (obj instanceof Date) return new Date(obj);
81
+ if (obj instanceof Array) return obj.map((item) => deepClone(item));
82
+ if (obj instanceof Object) {
83
+ const copy = {};
84
+ for (const key in obj) {
85
+ if (obj.hasOwnProperty(key)) {
86
+ copy[key] = deepClone(obj[key]);
87
+ }
88
+ }
89
+ return copy;
90
+ }
91
+ return obj;
92
+ }
93
+
94
+ /**
95
+ * 防抖函数
96
+ * @param {Function} fn
97
+ * @param {number} delay
98
+ * @returns {Function}
99
+ */
100
+ function debounce(fn, delay) {
101
+ let timeoutId;
102
+ return function (...args) {
103
+ clearTimeout(timeoutId);
104
+ timeoutId = setTimeout(() => fn.apply(this, args), delay);
105
+ };
106
+ }
107
+
108
+ /**
109
+ * 节流函数
110
+ * @param {Function} fn
111
+ * @param {number} limit
112
+ * @returns {Function}
113
+ */
114
+ function throttle(fn, limit) {
115
+ let inThrottle;
116
+ return function (...args) {
117
+ if (!inThrottle) {
118
+ fn.apply(this, args);
119
+ inThrottle = true;
120
+ setTimeout(() => (inThrottle = false), limit);
121
+ }
122
+ };
123
+ }
124
+
125
+ /**
126
+ * 延迟执行
127
+ * @param {number} ms
128
+ * @returns {Promise}
129
+ */
130
+ function sleep(ms) {
131
+ return new Promise((resolve) => setTimeout(resolve, ms));
132
+ }
133
+
134
+ /**
135
+ * 重试函数
136
+ * @param {Function} fn
137
+ * @param {Object} options
138
+ * @returns {Promise}
139
+ */
140
+ async function retry(fn, options = {}) {
141
+ const { maxAttempts = 3, delay = 1000, backoff = 2, onRetry } = options;
142
+
143
+ let lastError;
144
+
145
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
146
+ try {
147
+ return await fn();
148
+ } catch (error) {
149
+ lastError = error;
150
+
151
+ if (attempt < maxAttempts) {
152
+ const waitTime = delay * Math.pow(backoff, attempt - 1);
153
+ if (onRetry) {
154
+ onRetry(attempt, waitTime, error);
155
+ }
156
+ await sleep(waitTime);
157
+ }
158
+ }
159
+ }
160
+
161
+ throw lastError;
162
+ }
163
+
164
+ module.exports = {
165
+ // 日志
166
+ Logger,
167
+ logger,
168
+ LOG_LEVELS,
169
+
170
+ // ID 生成
171
+ uuid,
172
+ prefixedId,
173
+ shortId,
174
+ snowflake,
175
+ taskId,
176
+ goalId,
177
+ sessionId,
178
+ messageId,
179
+ ruleId,
180
+ resetSnowflake,
181
+
182
+ // 错误处理
183
+ FolikoError,
184
+ PluginError,
185
+ ToolError,
186
+ AIError,
187
+ SessionError,
188
+ ConfigError,
189
+ WorkflowError,
190
+ ValidationError,
191
+ ToolNotFoundError,
192
+ PluginNotFoundError,
193
+ isErrorOfType,
194
+ safeErrorInfo,
195
+
196
+ // 重试策略
197
+ withRetry,
198
+ retryable,
199
+ isNetworkError,
200
+ PRESETS,
201
+
202
+ // 错误边界
203
+ ErrorBoundary,
204
+ Severity,
205
+ RecoveryAction,
206
+ createErrorBoundary,
207
+ combineBoundaries,
208
+
209
+ // 工具函数
210
+ cleanResponse,
211
+ safeJsonParse,
212
+ deepClone,
213
+ debounce,
214
+ throttle,
215
+ sleep,
216
+ retry,
217
+ };
@@ -0,0 +1,181 @@
1
+ /**
2
+ * Foliko Logger - 统一日志系统
3
+ * 支持日志级别、命名空间、格式化输出
4
+ */
5
+
6
+ const LOG_LEVELS = {
7
+ DEBUG: 0,
8
+ INFO: 1,
9
+ WARN: 2,
10
+ ERROR: 3,
11
+ NONE: 4,
12
+ };
13
+
14
+ const LEVEL_NAMES = ['DEBUG', 'INFO', 'WARN', 'ERROR'];
15
+
16
+ class Logger {
17
+ /**
18
+ * @param {Object} options
19
+ * @param {string} options.namespace - 日志命名空间
20
+ * @param {number} options.level - 日志级别 (0-4)
21
+ * @param {boolean} options.enableTimestamp - 是否显示时间戳
22
+ * @param {boolean} options.enableColors - 是否使用颜色
23
+ */
24
+ constructor(options = {}) {
25
+ this.namespace = options.namespace || 'app';
26
+ this.level = options.level ?? LOG_LEVELS.INFO;
27
+ this.enableTimestamp = options.enableTimestamp !== false;
28
+ this.enableColors = options.enableColors !== false;
29
+
30
+ // 颜色代码
31
+ this.colors = {
32
+ debug: '\x1b[36m', // 青色
33
+ info: '\x1b[32m', // 绿色
34
+ warn: '\x1b[33m', // 黄色
35
+ error: '\x1b[31m', // 红色
36
+ reset: '\x1b[0m',
37
+ dim: '\x1b[2m', // 灰色
38
+ };
39
+ }
40
+
41
+ /**
42
+ * 创建子日志器
43
+ * @param {string} namespace
44
+ * @returns {Logger}
45
+ */
46
+ child(namespace) {
47
+ return new Logger({
48
+ namespace: `${this.namespace}:${namespace}`,
49
+ level: this.level,
50
+ enableTimestamp: this.enableTimestamp,
51
+ enableColors: this.enableColors,
52
+ });
53
+ }
54
+
55
+ /**
56
+ * 设置日志级别
57
+ * @param {string|number} level
58
+ */
59
+ setLevel(level) {
60
+ if (typeof level === 'string') {
61
+ this.level = LOG_LEVELS[level.toUpperCase()] ?? LOG_LEVELS.INFO;
62
+ } else {
63
+ this.level = level;
64
+ }
65
+ }
66
+
67
+ /**
68
+ * 格式化日志消息
69
+ * @private
70
+ */
71
+ _format(level, levelName, args) {
72
+ const color = this.colors[levelName.toLowerCase()] || '';
73
+ const reset = this.colors.reset;
74
+ const dim = this.colors.dim;
75
+
76
+ const parts = [];
77
+
78
+ // 命名空间
79
+ parts.push(`${dim}[${this.namespace}]${reset}`);
80
+
81
+ // 时间戳
82
+ if (this.enableTimestamp) {
83
+ const timestamp = new Date().toISOString().split('T')[1].slice(0, -1);
84
+ parts.push(`${dim}${timestamp}${reset}`);
85
+ }
86
+
87
+ // 级别
88
+ if (this.enableColors) {
89
+ parts.push(`${color}${levelName.padEnd(5)}${reset}`);
90
+ } else {
91
+ parts.push(levelName.padEnd(5));
92
+ }
93
+
94
+ // 消息
95
+ const message = args
96
+ .map((arg) => {
97
+ if (arg instanceof Error) {
98
+ return `${arg.message}\n${arg.stack}`;
99
+ }
100
+ if (typeof arg === 'object') {
101
+ try {
102
+ return JSON.stringify(arg, null, 2);
103
+ } catch {
104
+ return String(arg);
105
+ }
106
+ }
107
+ return String(arg);
108
+ })
109
+ .join(' ');
110
+
111
+ parts.push(message);
112
+ return parts.join(' ');
113
+ }
114
+
115
+ /**
116
+ * 调试日志
117
+ * @param {...any} args
118
+ */
119
+ debug(...args) {
120
+ if (this.level <= LOG_LEVELS.DEBUG) {
121
+ console.debug(this._format('debug', 'DEBUG', args));
122
+ }
123
+ }
124
+
125
+ /**
126
+ * 信息日志
127
+ * @param {...any} args
128
+ */
129
+ info(...args) {
130
+ if (this.level <= LOG_LEVELS.INFO) {
131
+ console.log(this._format('info', 'INFO', args));
132
+ }
133
+ }
134
+
135
+ /**
136
+ * 警告日志
137
+ * @param {...any} args
138
+ */
139
+ warn(...args) {
140
+ if (this.level <= LOG_LEVELS.WARN) {
141
+ console.warn(this._format('warn', 'WARN', args));
142
+ }
143
+ }
144
+
145
+ /**
146
+ * 错误日志
147
+ * @param {...any} args
148
+ */
149
+ error(...args) {
150
+ if (this.level <= LOG_LEVELS.ERROR) {
151
+ console.error(this._format('error', 'ERROR', args));
152
+ }
153
+ }
154
+
155
+ /**
156
+ * 性能计时开始
157
+ * @param {string} label
158
+ * @returns {function} 结束计时函数
159
+ */
160
+ timer(label) {
161
+ const start = process.hrtime.bigint();
162
+ return () => {
163
+ const end = process.hrtime.bigint();
164
+ const ms = Number(end - start) / 1_000_000;
165
+ this.debug(`${label}: ${ms.toFixed(2)}ms`);
166
+ };
167
+ }
168
+ }
169
+
170
+ // 全局日志器实例
171
+ const globalLogger = new Logger({
172
+ namespace: 'foliko',
173
+ level: process.env.LOG_LEVEL ? LOG_LEVELS[process.env.LOG_LEVEL.toUpperCase()] : LOG_LEVELS.INFO,
174
+ });
175
+
176
+ // 导出
177
+ module.exports = {
178
+ Logger,
179
+ logger: globalLogger,
180
+ LOG_LEVELS,
181
+ };
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Plugin Helpers - 插件相关的辅助函数
3
+ * 提供插件路径解析和扫描功能,供 PluginManager 和 DefaultPlugins 共用
4
+ */
5
+
6
+ const fs = require('fs');
7
+ const path = require('path');
8
+ const { safeJsonParse } = require('./index');
9
+
10
+ /**
11
+ * 解析插件路径
12
+ * 支持两种结构:
13
+ * 1. 文件夹结构: .agent/plugins/my-plugin/index.js
14
+ * 2. 单文件结构: .agent/plugins/my-plugin.js
15
+ * @param {string} pluginsDir - 插件目录
16
+ * @param {string} name - 插件名称
17
+ * @param {Object} options - 选项
18
+ * @param {Object} options.logger - 日志器(可选)
19
+ * @returns {{path: string, type: 'folder'|'file'}|null} 插件路径和类型
20
+ */
21
+ function resolvePluginPath(pluginsDir, name, options = {}) {
22
+ const logger = options.logger;
23
+ const folderPath = path.join(pluginsDir, name);
24
+ const filePath = path.join(pluginsDir, `${name}.js`);
25
+
26
+ // 文件夹优先
27
+ if (fs.existsSync(folderPath) && fs.statSync(folderPath).isDirectory()) {
28
+ const pkgPath = path.join(folderPath, 'package.json');
29
+ if (fs.existsSync(pkgPath)) {
30
+ try {
31
+ const pkg = safeJsonParse(fs.readFileSync(pkgPath, 'utf-8'), {});
32
+ const main = pkg.main || 'index.js';
33
+ const mainPath = path.join(folderPath, main);
34
+ if (fs.existsSync(mainPath)) {
35
+ return { path: mainPath, type: 'folder' };
36
+ }
37
+ } catch (err) {
38
+ logger?.warn(`Failed to parse package.json for ${name}:`, err.message);
39
+ }
40
+ }
41
+ // 默认加载 index.js
42
+ const indexPath = path.join(folderPath, 'index.js');
43
+ if (fs.existsSync(indexPath)) {
44
+ return { path: indexPath, type: 'folder' };
45
+ }
46
+ logger?.warn(`No entry point found for plugin folder: ${name}`);
47
+ return null;
48
+ }
49
+
50
+ // 单文件回退
51
+ if (fs.existsSync(filePath)) {
52
+ return { path: filePath, type: 'file' };
53
+ }
54
+
55
+ return null;
56
+ }
57
+
58
+ /**
59
+ * 扫描插件目录,返回所有插件名称
60
+ * @param {string} pluginsDir - 插件目录
61
+ * @returns {string[]} 插件名称列表
62
+ */
63
+ function scanPluginNames(pluginsDir) {
64
+ if (!fs.existsSync(pluginsDir)) {
65
+ return [];
66
+ }
67
+
68
+ const names = new Set();
69
+ const entries = fs.readdirSync(pluginsDir, { withFileTypes: true });
70
+
71
+ for (const entry of entries) {
72
+ if (entry.isDirectory()) {
73
+ // 文件夹插件
74
+ names.add(entry.name);
75
+ } else if (entry.isFile() && entry.name.endsWith('.js')) {
76
+ // 单文件插件(排除与文件夹同名的)
77
+ const baseName = entry.name.replace(/\.js$/, '');
78
+ if (!names.has(baseName)) {
79
+ names.add(baseName);
80
+ }
81
+ }
82
+ }
83
+
84
+ return Array.from(names);
85
+ }
86
+
87
+ module.exports = {
88
+ resolvePluginPath,
89
+ scanPluginNames,
90
+ };
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Foliko Retry Strategy - 重试策略系统
3
+ * 提供多种重试策略,支持指数退避、抖动等
4
+ */
5
+
6
+ const { logger } = require('./logger');
7
+
8
+ const log = logger.child('RetryStrategy');
9
+
10
+ /**
11
+ * 重试策略配置
12
+ * @typedef {Object} RetryConfig
13
+ * @property {number} maxAttempts - 最大重试次数
14
+ * @property {number} baseDelay - 基础延迟(毫秒)
15
+ * @property {number} maxDelay - 最大延迟(毫秒)
16
+ * @property {number} [factor=2] - 退避因子
17
+ * @property {number} [jitter=0.1] - 抖动因子(0-1)
18
+ * @property {Function} [shouldRetry] - 判断是否应重试的函数
19
+ * @property {Function} [onRetry] - 重试前回调
20
+ */
21
+
22
+ /**
23
+ * 预设的重试策略
24
+ */
25
+ const PRESETS = {
26
+ /** 快速重试(适合网络瞬时抖动) */
27
+ fast: {
28
+ maxAttempts: 3,
29
+ baseDelay: 100,
30
+ maxDelay: 1000,
31
+ factor: 2,
32
+ jitter: 0.1,
33
+ },
34
+ /** 标准重试(适合大多数场景) */
35
+ standard: {
36
+ maxAttempts: 3,
37
+ baseDelay: 1000,
38
+ maxDelay: 10000,
39
+ factor: 2,
40
+ jitter: 0.2,
41
+ },
42
+ /** 保守重试(适合外部依赖) */
43
+ conservative: {
44
+ maxAttempts: 5,
45
+ baseDelay: 2000,
46
+ maxDelay: 30000,
47
+ factor: 2,
48
+ jitter: 0.3,
49
+ },
50
+ /** 指数退避(适合限流场景) */
51
+ exponential: {
52
+ maxAttempts: 5,
53
+ baseDelay: 1000,
54
+ maxDelay: 60000,
55
+ factor: 2,
56
+ jitter: 0.1,
57
+ },
58
+ /** 激进重试(适合紧急恢复,快速多次尝试) */
59
+ aggressive: {
60
+ maxAttempts: 5,
61
+ baseDelay: 200,
62
+ maxDelay: 2000,
63
+ factor: 1.5,
64
+ jitter: 0.05,
65
+ },
66
+ /** 空闲连接保活(适合长时间运行的连接检查) */
67
+ idle: {
68
+ maxAttempts: 3,
69
+ baseDelay: 5000,
70
+ maxDelay: 15000,
71
+ factor: 1.5,
72
+ jitter: 0.1,
73
+ shouldRetry: (error) => {
74
+ const networkCodes = ['ECONNREFUSED', 'ECONNRESET', 'ETIMEDOUT', 'ENOTFOUND'];
75
+ return error.code && networkCodes.includes(error.code);
76
+ },
77
+ },
78
+ /** 数据库重试(适合数据库连接) */
79
+ database: {
80
+ maxAttempts: 5,
81
+ baseDelay: 1000,
82
+ maxDelay: 30000,
83
+ factor: 2,
84
+ jitter: 0.2,
85
+ shouldRetry: (error) => {
86
+ const dbCodes = [
87
+ 'ECONNREFUSED',
88
+ 'ECONNRESET',
89
+ 'ETIMEDOUT',
90
+ 'PROTOCOL_CONNECTION_LOST',
91
+ 'ER_CON_COUNT_ERROR',
92
+ ];
93
+ const dbKeywords = [
94
+ 'connection',
95
+ 'timeout',
96
+ 'database',
97
+ 'sql',
98
+ 'mysql',
99
+ 'postgres',
100
+ 'mongodb',
101
+ ];
102
+ if (error.code && dbCodes.includes(error.code)) return true;
103
+ const msg = (error.message || '').toLowerCase();
104
+ return dbKeywords.some((k) => msg.includes(k));
105
+ },
106
+ },
107
+ /** API调用重试(适合第三方API) */
108
+ api: {
109
+ maxAttempts: 4,
110
+ baseDelay: 500,
111
+ maxDelay: 8000,
112
+ factor: 2,
113
+ jitter: 0.15,
114
+ shouldRetry: (error) => {
115
+ const status = error.status || error.statusCode;
116
+ if (status && (status >= 500 || status === 429)) return true;
117
+ const networkCodes = ['ECONNREFUSED', 'ECONNRESET', 'ETIMEDOUT', 'ENOTFOUND'];
118
+ if (error.code && networkCodes.includes(error.code)) return true;
119
+ return false;
120
+ },
121
+ },
122
+ };