foliko 2.0.5 → 2.0.6
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.
- package/.claude/settings.local.json +4 -1
- package/.cli_default_systemPrompt.md +291 -0
- package/.editorconfig +56 -56
- package/.lintstagedrc +7 -7
- package/.prettierignore +29 -29
- package/.prettierrc +11 -11
- package/CLAUDE.md +3 -0
- package/Dockerfile +63 -63
- package/README.md +20 -3
- package/docs/architecture.md +34 -2
- package/docs/extensions.md +199 -0
- package/docs/migration.md +100 -0
- package/docs/public-api.md +280 -24
- package/docs/usage.md +122 -30
- package/install.ps1 +129 -129
- package/install.sh +121 -121
- package/package.json +1 -1
- package/plugins/core/audit/index.js +1 -1
- package/plugins/core/default/bootstrap.js +44 -19
- package/plugins/core/python-loader/index.js +43 -25
- package/plugins/core/skill-manager/PROMPT.md +6 -0
- package/plugins/core/skill-manager/index.js +402 -115
- package/plugins/core/sub-agent/PROMPT.md +10 -0
- package/plugins/core/sub-agent/index.js +36 -3
- package/plugins/core/think/index.js +1 -0
- package/plugins/core/workflow/index.js +82 -22
- package/plugins/executors/data-splitter/PROMPT.md +13 -0
- package/plugins/executors/data-splitter/index.js +5 -4
- package/plugins/executors/extension/extension-registry.js +145 -0
- package/plugins/executors/extension/index.js +405 -437
- package/plugins/executors/extension/prompt-builder.js +359 -0
- package/plugins/executors/extension/skill-helper.js +143 -0
- package/plugins/messaging/feishu/index.js +5 -3
- package/plugins/messaging/qq/index.js +6 -4
- package/plugins/messaging/telegram/index.js +6 -3
- package/plugins/messaging/weixin/index.js +5 -3
- package/plugins/tools/PROMPT.md +26 -0
- package/plugins/tools/index.js +6 -5
- package/skills/find-skills/SKILL.md +133 -133
- package/skills/foliko/AGENTS.md +196 -43
- package/skills/foliko/SKILL.md +157 -28
- package/skills/mcp/SKILL.md +77 -118
- package/skills/plugins/SKILL.md +89 -3
- package/skills/python/SKILL.md +57 -39
- package/skills/skill-guide/SKILL.md +42 -34
- package/skills/workflows/SKILL.md +224 -9
- package/skills/workflows/workflow-troubleshooting/SKILL.md +221 -281
- package/src/agent/chat.js +48 -27
- package/src/agent/main.js +34 -13
- package/src/agent/prompt-registry.js +56 -16
- package/src/agent/prompts/PROMPT.md +3 -0
- package/src/agent/sub.js +1 -1
- package/src/cli/ui/chat-ui-old.js +5 -2
- package/src/cli/ui/chat-ui.js +5 -2
- package/src/common/constants.js +12 -0
- package/src/common/error-capture.js +91 -0
- package/src/common/logger.js +2 -2
- package/src/context/compressor.js +6 -2
- package/src/executors/mcp-executor.js +105 -125
- package/src/framework/framework.js +23 -11
- package/src/index.js +4 -0
- package/src/plugin/base.js +908 -5
- package/src/plugin/manager.js +29 -8
- package/src/tool/schema.js +32 -9
- package/website/index.html +821 -0
|
@@ -14,7 +14,7 @@ const { StdioClientTransport } = require('@modelcontextprotocol/sdk/client/stdio
|
|
|
14
14
|
const { zodSchemaToMarkdown } = require('@chnak/zod-to-markdown');
|
|
15
15
|
const { logger } = require('../common/logger');
|
|
16
16
|
const { MCPClientWrapper } = require('./mcp-client');
|
|
17
|
-
const { extractSchema
|
|
17
|
+
const { extractSchema } = require('./mcp-desc');
|
|
18
18
|
const { jsonSchemaToZod } = require('../tool/schema');
|
|
19
19
|
|
|
20
20
|
const log = logger.child('MCPExecutor');
|
|
@@ -53,120 +53,6 @@ class MCPExecutorPlugin extends Plugin {
|
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
framework.registerTool({
|
|
57
|
-
name: 'mcp_tool_schema',
|
|
58
|
-
description: '查询工具的调用示例,直接复制 example 或 fullExample 字段使用',
|
|
59
|
-
inputSchema: z.object({
|
|
60
|
-
server: z.string().describe('MCP 服务器名称'),
|
|
61
|
-
tool: z.string().describe('工具名称'),
|
|
62
|
-
}),
|
|
63
|
-
execute: async (args) => {
|
|
64
|
-
const { server, tool } = args;
|
|
65
|
-
const clientInfo = this._clients.get(server);
|
|
66
|
-
if (!clientInfo) return { success: false, error: `MCP server '${server}' not found` };
|
|
67
|
-
|
|
68
|
-
const toolInfo = clientInfo.tools.find((t) => t.name === tool);
|
|
69
|
-
if (!toolInfo) return { success: false, error: `Tool '${tool}' not found` };
|
|
70
|
-
|
|
71
|
-
const schema = extractSchema(toolInfo.inputSchema);
|
|
72
|
-
const props = schema.properties || {};
|
|
73
|
-
const required = schema.required || [];
|
|
74
|
-
|
|
75
|
-
const generateExample = (prop, depth = 0) => {
|
|
76
|
-
if (!prop) return null;
|
|
77
|
-
const type = prop.type || (prop.properties ? 'object' : 'string');
|
|
78
|
-
if (type === 'string') return prop.description?.split('.')[0] || `${prop.title || '值'}`;
|
|
79
|
-
if (type === 'number' || type === 'integer') return prop.default || 0;
|
|
80
|
-
if (type === 'boolean') return false;
|
|
81
|
-
if (type === 'array') {
|
|
82
|
-
if (prop.items) {
|
|
83
|
-
const itemExample = generateExample(prop.items, depth + 1);
|
|
84
|
-
return depth === 0 ? [itemExample] : itemExample;
|
|
85
|
-
}
|
|
86
|
-
return ['示例'];
|
|
87
|
-
}
|
|
88
|
-
if (type === 'object' || prop.properties) {
|
|
89
|
-
const obj = {};
|
|
90
|
-
for (const [key, val] of Object.entries(prop.properties || {})) {
|
|
91
|
-
obj[key] = generateExample(val, depth + 1);
|
|
92
|
-
}
|
|
93
|
-
return obj;
|
|
94
|
-
}
|
|
95
|
-
return null;
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
const exampleArgs = {};
|
|
99
|
-
for (const key of required) {
|
|
100
|
-
const prop = props[key];
|
|
101
|
-
if (prop) exampleArgs[key] = generateExample(prop);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
return {
|
|
105
|
-
success: true,
|
|
106
|
-
data: {
|
|
107
|
-
name: toolInfo.name, description: toolInfo.description, required,
|
|
108
|
-
parameters: Object.fromEntries(
|
|
109
|
-
required.map((key) => [key, {
|
|
110
|
-
type: props[key]?.type || 'string',
|
|
111
|
-
description: props[key]?.description || '',
|
|
112
|
-
example: generateExample(props[key]),
|
|
113
|
-
}])
|
|
114
|
-
),
|
|
115
|
-
example: JSON.stringify(exampleArgs, null, 2),
|
|
116
|
-
fullExample: `mcp_call({ server: "${server}", tool: "${tool}", args_json: ${JSON.stringify(JSON.stringify(exampleArgs))} })`,
|
|
117
|
-
},
|
|
118
|
-
};
|
|
119
|
-
},
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
framework.registerTool({
|
|
123
|
-
name: 'mcp_call',
|
|
124
|
-
description: '调用 MCP 服务器工具。args_json 是包含实际参数的 JSON 字符串',
|
|
125
|
-
inputSchema: z.object({
|
|
126
|
-
server: z.string().describe('MCP 服务器名称'),
|
|
127
|
-
tool: z.string().describe('工具名称'),
|
|
128
|
-
args_json: z.string().describe('参数 JSON 字符串'),
|
|
129
|
-
}),
|
|
130
|
-
execute: async (args) => {
|
|
131
|
-
const { server, tool, args_json } = args;
|
|
132
|
-
const clientInfo = this._clients.get(server);
|
|
133
|
-
if (!clientInfo) return { success: false, error: `MCP server '${server}' not found` };
|
|
134
|
-
|
|
135
|
-
let finalArgs = {};
|
|
136
|
-
if (args_json) {
|
|
137
|
-
try { finalArgs = JSON.parse(args_json); }
|
|
138
|
-
catch (e) { return { success: false, error: `args_json 格式错误: ${args_json}` }; }
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
if (!finalArgs || Object.keys(finalArgs).length === 0) {
|
|
142
|
-
const toolInfo = clientInfo.tools.find((t) => t.name === tool);
|
|
143
|
-
if (toolInfo) {
|
|
144
|
-
const schema = extractSchema(toolInfo.inputSchema);
|
|
145
|
-
const required = schema.required || [];
|
|
146
|
-
return { success: false, error: `参数为空!必须提供: ${required.join(', ')}`, hint: `正确格式: {"${required[0]}": "具体值"}` };
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
try {
|
|
151
|
-
const mcpTool = clientInfo.toolObjects?.[tool];
|
|
152
|
-
if (!mcpTool) return { success: false, error: `Tool '${tool}' not found on server '${server}'` };
|
|
153
|
-
|
|
154
|
-
const mcpToolName = `${server}:${tool}`;
|
|
155
|
-
framework.emit('tool:call', { name: mcpToolName, args: finalArgs, source: 'mcp' });
|
|
156
|
-
framework.emit('tool-call', { name: mcpToolName, args: finalArgs, source: 'mcp' });
|
|
157
|
-
|
|
158
|
-
const execResult = await mcpTool.execute(finalArgs);
|
|
159
|
-
|
|
160
|
-
framework.emit('tool:result', { name: mcpToolName, args: finalArgs, result: execResult, source: 'mcp' });
|
|
161
|
-
return { success: true, data: execResult };
|
|
162
|
-
} catch (err) {
|
|
163
|
-
log.error(` Tool '${tool}' failed:`, err.message);
|
|
164
|
-
framework.emit('tool:error', { name: `${server}:${tool}`, args: finalArgs, error: err.message, source: 'mcp' });
|
|
165
|
-
return { success: false, error: err.message };
|
|
166
|
-
}
|
|
167
|
-
},
|
|
168
|
-
});
|
|
169
|
-
|
|
170
56
|
framework.registerTool({
|
|
171
57
|
name: 'mcp_list_servers',
|
|
172
58
|
description: '列出已连接的 MCP 服务器及其工具',
|
|
@@ -235,9 +121,12 @@ class MCPExecutorPlugin extends Plugin {
|
|
|
235
121
|
if (!clientInfo) return { success: false, error: `MCP server '${server}' not found` };
|
|
236
122
|
try { clientInfo.client.close?.(); } catch (e) { /* ignore */ }
|
|
237
123
|
this._clients.delete(server);
|
|
238
|
-
|
|
239
|
-
|
|
124
|
+
// 注销该服务器的所有工具(this.tools 中的 key 就是 MCP 原始工具名)
|
|
125
|
+
for (const t of clientInfo.tools) {
|
|
126
|
+
if (this.tools[t.name]) delete this.tools[t.name];
|
|
240
127
|
}
|
|
128
|
+
// 同时注销对应的扩展(mcp:<server>)
|
|
129
|
+
this._unregisterExtension(server);
|
|
241
130
|
await this._saveMCPServerEnabled(server, false);
|
|
242
131
|
this._refreshAllAgentsMCPPrompt(this._framework);
|
|
243
132
|
return { success: true, data: `MCP 服务器 '${server}' 已关闭` };
|
|
@@ -343,15 +232,23 @@ class MCPExecutorPlugin extends Plugin {
|
|
|
343
232
|
toolObjects[toolName] = tool;
|
|
344
233
|
toolsInfo.push({ name: toolName, description: tool.description || '', inputSchema: tool.inputSchema || {} });
|
|
345
234
|
|
|
346
|
-
|
|
347
|
-
this.tools[
|
|
348
|
-
name:
|
|
349
|
-
description:
|
|
235
|
+
// 直接调用底层 MCP 工具(不再走 mcp_call 中转)
|
|
236
|
+
this.tools[toolName] = {
|
|
237
|
+
name: toolName,
|
|
238
|
+
description: tool.description || '',
|
|
350
239
|
inputSchema: tool.inputSchema || {},
|
|
351
240
|
execute: async (args) => {
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
241
|
+
try {
|
|
242
|
+
const mcpToolName = `${name}:${toolName}`;
|
|
243
|
+
framework?.emit?.('tool:call', { name: mcpToolName, args: args || {}, source: 'mcp' });
|
|
244
|
+
const execResult = await tool.execute(args || {});
|
|
245
|
+
framework?.emit?.('tool:result', { name: mcpToolName, args: args || {}, result: execResult, source: 'mcp' });
|
|
246
|
+
return { success: true, data: execResult };
|
|
247
|
+
} catch (err) {
|
|
248
|
+
log.error(` Tool '${toolName}' failed:`, err.message);
|
|
249
|
+
framework?.emit?.('tool:error', { name: `${name}:${toolName}`, args: args || {}, error: err.message, source: 'mcp' });
|
|
250
|
+
return { success: false, error: err.message };
|
|
251
|
+
}
|
|
355
252
|
},
|
|
356
253
|
};
|
|
357
254
|
}
|
|
@@ -359,10 +256,89 @@ class MCPExecutorPlugin extends Plugin {
|
|
|
359
256
|
|
|
360
257
|
this._clients.set(name, { client, tools: toolsInfo, toolObjects, enabled: config.enabled !== false });
|
|
361
258
|
|
|
259
|
+
// 把整个 MCP 服务器注册为扩展(mcp:<servername>),让 ext_call / ext_skill 统一调度
|
|
260
|
+
this._registerAsExtension(name, toolsInfo, toolObjects);
|
|
261
|
+
|
|
262
|
+
// 触发 mcp 工具变化事件,通知 extension-executor 刷新
|
|
263
|
+
this._framework?.emit?.('mcp:tools-changed', { name, toolsCount: toolsInfo.length });
|
|
264
|
+
|
|
362
265
|
log.debug(` Added server '${name}' with ${toolsInfo.length} tools`);
|
|
363
266
|
return { success: true, name, toolsCount: toolsInfo.length };
|
|
364
267
|
}
|
|
365
268
|
|
|
269
|
+
/**
|
|
270
|
+
* 把 MCP 服务器注册为扩展插件(mcp:<servername>)
|
|
271
|
+
* 使其能通过 ext_call({plugin:'mcp:<server>', tool:'xxx', args:{...}}) 调用
|
|
272
|
+
* 通过 ext_skill({plugin:'mcp:<server>'}) 获取参数说明
|
|
273
|
+
*/
|
|
274
|
+
_registerAsExtension(serverName, toolsInfo, toolObjects) {
|
|
275
|
+
const extExecutor = this._framework?.pluginManager?.get('extension-executor');
|
|
276
|
+
if (!extExecutor || typeof extExecutor.registerTool !== 'function') return;
|
|
277
|
+
|
|
278
|
+
const extName = `mcp:${serverName}`;
|
|
279
|
+
// 优先从 server config 取 description,否则用 server name
|
|
280
|
+
const serverConfig = this._serverConfigs.get(serverName) || {};
|
|
281
|
+
const extDescription = serverConfig.description
|
|
282
|
+
? serverConfig.description
|
|
283
|
+
: `MCP 服务器 ${serverName}`;
|
|
284
|
+
const pluginInfo = {
|
|
285
|
+
name: extName,
|
|
286
|
+
description: extDescription,
|
|
287
|
+
version: '1.0.0',
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
for (const toolInfo of toolsInfo) {
|
|
291
|
+
const mcpTool = toolObjects[toolInfo.name];
|
|
292
|
+
if (!mcpTool) continue;
|
|
293
|
+
|
|
294
|
+
// 把 JSON schema 转换为 Zod schema(用于 AI SDK 的工具参数定义)
|
|
295
|
+
// 同时保留原始 JSON schema 作为 _rawSchema,让 ext_skill 能渲染出清晰的参数说明
|
|
296
|
+
const rawSchema = extractSchema(toolInfo.inputSchema);
|
|
297
|
+
let inputSchema = rawSchema;
|
|
298
|
+
try {
|
|
299
|
+
if (rawSchema && rawSchema.properties) {
|
|
300
|
+
const zodSchema = this._jsonSchemaToZod(rawSchema);
|
|
301
|
+
if (zodSchema) inputSchema = zodSchema;
|
|
302
|
+
}
|
|
303
|
+
} catch (e) { /* fallback: 保留原始 schema */ }
|
|
304
|
+
|
|
305
|
+
extExecutor.registerTool(extName, pluginInfo, {
|
|
306
|
+
name: toolInfo.name,
|
|
307
|
+
description: toolInfo.description || '',
|
|
308
|
+
inputSchema,
|
|
309
|
+
_rawSchema: rawSchema, // 原始 JSON schema,用于 ext_skill 渲染
|
|
310
|
+
execute: async (args, framework) => {
|
|
311
|
+
try {
|
|
312
|
+
const finalArgs = args || {};
|
|
313
|
+
const mcpToolName = `${serverName}:${toolInfo.name}`;
|
|
314
|
+
framework?.emit?.('tool:call', { name: mcpToolName, args: finalArgs, source: 'mcp' });
|
|
315
|
+
const result = await mcpTool.execute(finalArgs);
|
|
316
|
+
framework?.emit?.('tool:result', { name: mcpToolName, args: finalArgs, result, source: 'mcp' });
|
|
317
|
+
return { success: true, data: result };
|
|
318
|
+
} catch (err) {
|
|
319
|
+
log.error(` Tool '${toolInfo.name}' failed:`, err.message);
|
|
320
|
+
return { success: false, error: err.message };
|
|
321
|
+
}
|
|
322
|
+
},
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* 注销 MCP 服务器对应的扩展
|
|
329
|
+
*/
|
|
330
|
+
_unregisterExtension(serverName) {
|
|
331
|
+
const extExecutor = this._framework?.pluginManager?.get('extension-executor');
|
|
332
|
+
if (!extExecutor || typeof extExecutor._unregisterTool !== 'function') return;
|
|
333
|
+
|
|
334
|
+
const extName = `mcp:${serverName}`;
|
|
335
|
+
const clientInfo = this._clients.get(serverName);
|
|
336
|
+
const toolNames = clientInfo?.tools?.map((t) => t.name) || [];
|
|
337
|
+
for (const toolName of toolNames) {
|
|
338
|
+
extExecutor._unregisterTool(extName, toolName);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
366
342
|
async removeServer(name) {
|
|
367
343
|
const clientInfo = this._clients.get(name);
|
|
368
344
|
if (clientInfo) {
|
|
@@ -373,9 +349,13 @@ class MCPExecutorPlugin extends Plugin {
|
|
|
373
349
|
}
|
|
374
350
|
} catch (e) { /* ignore */ }
|
|
375
351
|
for (const toolName of Object.keys(this.tools)) {
|
|
376
|
-
if (toolName
|
|
352
|
+
if (this.tools[toolName]?.description?.includes?.(`[${name}]`)) {
|
|
353
|
+
delete this.tools[toolName];
|
|
354
|
+
}
|
|
377
355
|
}
|
|
378
356
|
this._clients.delete(name);
|
|
357
|
+
this._unregisterExtension(name);
|
|
358
|
+
this._framework?.emit?.('mcp:tools-changed', { name, removed: true });
|
|
379
359
|
}
|
|
380
360
|
}
|
|
381
361
|
|
|
@@ -91,7 +91,7 @@ class Framework extends EventEmitter {
|
|
|
91
91
|
// Subsystems
|
|
92
92
|
this.toolRegistry = new ToolRegistry();
|
|
93
93
|
this.pluginManager = new PluginManager(this);
|
|
94
|
-
this.promptRegistry = new PromptRegistry();
|
|
94
|
+
this.promptRegistry = new PromptRegistry(this);
|
|
95
95
|
|
|
96
96
|
// Agent management
|
|
97
97
|
this._agents = [];
|
|
@@ -912,23 +912,23 @@ class Framework extends EventEmitter {
|
|
|
912
912
|
return true;
|
|
913
913
|
},
|
|
914
914
|
/**
|
|
915
|
-
* 调用 MCP
|
|
915
|
+
* 调用 MCP 工具(通过 ext_call 统一调度)
|
|
916
916
|
* @param {string} server 服务器名
|
|
917
917
|
* @param {string} tool 工具名
|
|
918
918
|
* @param {Object} args 参数
|
|
919
919
|
*/
|
|
920
920
|
call: async (server, tool, args) => {
|
|
921
|
-
return await self.executeTool('
|
|
922
|
-
server
|
|
921
|
+
return await self.executeTool('ext_call', {
|
|
922
|
+
plugin: `mcp:${server}`,
|
|
923
923
|
tool,
|
|
924
|
-
|
|
924
|
+
args: args || {},
|
|
925
925
|
});
|
|
926
926
|
},
|
|
927
927
|
/**
|
|
928
|
-
*
|
|
928
|
+
* 获取工具调用参数说明(通过 ext_skill)
|
|
929
929
|
*/
|
|
930
|
-
getToolSchema: async (server,
|
|
931
|
-
return await self.executeTool('
|
|
930
|
+
getToolSchema: async (server, _tool) => {
|
|
931
|
+
return await self.executeTool('ext_skill', { plugin: `mcp:${server}` });
|
|
932
932
|
},
|
|
933
933
|
/**
|
|
934
934
|
* 动态开启/关闭 MCP 服务器
|
|
@@ -946,7 +946,14 @@ class Framework extends EventEmitter {
|
|
|
946
946
|
}
|
|
947
947
|
|
|
948
948
|
getMainAgent() { return this._mainAgent; }
|
|
949
|
-
|
|
949
|
+
/**
|
|
950
|
+
* 获取 main agent 的完整系统提示词
|
|
951
|
+
* 统一走 framework.prompts.build()(含 _originalPrompt + 注册表所有 parts)
|
|
952
|
+
* 这是 LLM 实际看到的完整 system prompt
|
|
953
|
+
*/
|
|
954
|
+
getSystemPrompt() {
|
|
955
|
+
return this.prompts?.build?.() || '';
|
|
956
|
+
}
|
|
950
957
|
getPluginInstance(name) { return this.pluginManager.get(name) || null; }
|
|
951
958
|
getAgents() { return [...this._agents]; }
|
|
952
959
|
listSessionContexts() { return Array.from(this._sessionContexts.keys()); }
|
|
@@ -1007,7 +1014,10 @@ class Framework extends EventEmitter {
|
|
|
1007
1014
|
const { SessionManager } = require('../session/session');
|
|
1008
1015
|
manager = SessionManager.open(sessionFile, sessionDir, cwd);
|
|
1009
1016
|
}
|
|
1010
|
-
} catch {
|
|
1017
|
+
} catch (err) {
|
|
1018
|
+
// Log error but continue - will create new session if load failed
|
|
1019
|
+
this.logger.warn(`[Framework] Failed to load session '${sessionId}': ${err.message}`);
|
|
1020
|
+
}
|
|
1011
1021
|
if (!manager) {
|
|
1012
1022
|
const { SessionManager } = require('../session/session');
|
|
1013
1023
|
manager = new SessionManager(cwd, sessionDir, sessionFile, true);
|
|
@@ -1032,7 +1042,9 @@ class Framework extends EventEmitter {
|
|
|
1032
1042
|
this._sessionContexts.set(sessionId, manager);
|
|
1033
1043
|
this.emit('session:context-created', { sessionId, manager });
|
|
1034
1044
|
}
|
|
1035
|
-
} catch {
|
|
1045
|
+
} catch (err) {
|
|
1046
|
+
this.logger.warn(`[Framework] Failed to load session context '${sessionId}': ${err.message}`);
|
|
1047
|
+
}
|
|
1036
1048
|
return manager || null;
|
|
1037
1049
|
}
|
|
1038
1050
|
|
package/src/index.js
CHANGED
|
@@ -11,6 +11,10 @@
|
|
|
11
11
|
// 加载工作目录 .env 配置(优先于系统环境变量)
|
|
12
12
|
require('dotenv').config({ override: true });
|
|
13
13
|
|
|
14
|
+
// 安装全局错误捕获:把 unhandledRejection / uncaughtException 的完整堆栈
|
|
15
|
+
// 落盘到 .foliko/error.log。需要堆栈时直接 cat 这个文件即可。
|
|
16
|
+
require('./common/error-capture').install();
|
|
17
|
+
|
|
14
18
|
// ============================================================
|
|
15
19
|
// Framework
|
|
16
20
|
// ============================================================
|