@vectorx/functions-framework 0.0.0-beta-20251112071234

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 (71) hide show
  1. package/README.md +141 -0
  2. package/bin/rcb-ff.js +153 -0
  3. package/lib/async-context.js +61 -0
  4. package/lib/config.js +10 -0
  5. package/lib/constants.js +10 -0
  6. package/lib/error.js +47 -0
  7. package/lib/framework.js +183 -0
  8. package/lib/function-loader.js +63 -0
  9. package/lib/function-registry.js +20 -0
  10. package/lib/function-wrapper.js +170 -0
  11. package/lib/index.js +23 -0
  12. package/lib/logger.js +204 -0
  13. package/lib/middlewares/index.js +19 -0
  14. package/lib/middlewares/middle-apm-injection.js +37 -0
  15. package/lib/middlewares/middle-async-context.js +27 -0
  16. package/lib/middlewares/middle-common-logger.js +67 -0
  17. package/lib/middlewares/middle-context-injection.js +33 -0
  18. package/lib/middlewares/middle-event-id.js +20 -0
  19. package/lib/middlewares/middle-function-route.js +26 -0
  20. package/lib/middlewares/middle-logs-request.js +98 -0
  21. package/lib/middlewares/middle-open-gw-request.js +29 -0
  22. package/lib/request.js +162 -0
  23. package/lib/router.js +51 -0
  24. package/lib/server.js +115 -0
  25. package/lib/sse.js +258 -0
  26. package/lib/telemetry/langfuse.js +13 -0
  27. package/lib/types.js +2 -0
  28. package/lib/unified-responder.js +64 -0
  29. package/lib/user-logger.js +54 -0
  30. package/lib/utils/apm.config.js +7 -0
  31. package/lib/utils/apm.js +140 -0
  32. package/lib/utils/common.js +5 -0
  33. package/lib/utils/console-intercept.js +58 -0
  34. package/lib/utils/error-stack.js +30 -0
  35. package/lib/utils/helper.js +88 -0
  36. package/lib/utils/machineId.js +72 -0
  37. package/package.json +76 -0
  38. package/types/async-context.d.ts +17 -0
  39. package/types/config.d.ts +1 -0
  40. package/types/constants.d.ts +2 -0
  41. package/types/error.d.ts +20 -0
  42. package/types/framework.d.ts +32 -0
  43. package/types/function-loader.d.ts +26 -0
  44. package/types/function-registry.d.ts +4 -0
  45. package/types/function-wrapper.d.ts +3 -0
  46. package/types/index.d.ts +7 -0
  47. package/types/logger.d.ts +74 -0
  48. package/types/middlewares/index.d.ts +8 -0
  49. package/types/middlewares/middle-apm-injection.d.ts +2 -0
  50. package/types/middlewares/middle-async-context.d.ts +2 -0
  51. package/types/middlewares/middle-common-logger.d.ts +2 -0
  52. package/types/middlewares/middle-context-injection.d.ts +4 -0
  53. package/types/middlewares/middle-event-id.d.ts +2 -0
  54. package/types/middlewares/middle-function-route.d.ts +4 -0
  55. package/types/middlewares/middle-logs-request.d.ts +3 -0
  56. package/types/middlewares/middle-open-gw-request.d.ts +2 -0
  57. package/types/request.d.ts +46 -0
  58. package/types/router.d.ts +15 -0
  59. package/types/server.d.ts +33 -0
  60. package/types/sse.d.ts +23 -0
  61. package/types/telemetry/langfuse.d.ts +4 -0
  62. package/types/types.d.ts +18 -0
  63. package/types/unified-responder.d.ts +15 -0
  64. package/types/user-logger.d.ts +5 -0
  65. package/types/utils/apm.config.d.ts +6 -0
  66. package/types/utils/apm.d.ts +21 -0
  67. package/types/utils/common.d.ts +2 -0
  68. package/types/utils/console-intercept.d.ts +6 -0
  69. package/types/utils/error-stack.d.ts +5 -0
  70. package/types/utils/helper.d.ts +7 -0
  71. package/types/utils/machineId.d.ts +2 -0
package/README.md ADDED
@@ -0,0 +1,141 @@
1
+ # VectorX Functions Framework
2
+
3
+ VectorX Functions Framework 是一个用于构建和运行云函数的框架,提供了完整的日志系统支持。
4
+
5
+ ## 日志系统
6
+
7
+ 框架内置了完整的日志系统,支持请求日志记录和用户代码日志记录。
8
+
9
+ ### 配置
10
+
11
+ 在初始化框架时,可以配置日志系统:
12
+
13
+ ```typescript
14
+ import { createAgentServerFramework } from '@vectorx/functions-framework';
15
+
16
+ const framework = createAgentServerFramework({
17
+ port: 3000,
18
+ logging: {
19
+ dirname: '/path/to/logs', // 日志存储目录
20
+ maxSize: '20m', // 单个日志文件最大大小
21
+ maxFiles: 14 // 保留的日志文件数量
22
+ }
23
+ });
24
+ ```
25
+
26
+ ### 日志类型
27
+
28
+ 框架支持两种类型的日志:
29
+
30
+ - `ACCESS`: 访问日志,记录所有 HTTP 请求
31
+ - `USERCODE`: 用户代码日志,记录用户函数中的日志
32
+
33
+ ### 日志查询 API
34
+
35
+ 框架提供了日志查询 API,用于查看和调试日志:
36
+
37
+ ```
38
+ GET /@logs
39
+ ```
40
+
41
+ #### 查询参数
42
+
43
+ | 参数名 | 类型 | 必填 | 说明 |
44
+ |--------|------|------|------|
45
+ | type | string | 否 | 日志类型,可选值:ACCESS/USERCODE,默认 ACCESS |
46
+ | limit | number | 否 | 返回的日志条数限制,默认 100 |
47
+ | eventId | string | 否 | 按 eventId 过滤日志 |
48
+
49
+ #### 示例
50
+
51
+ 1. 查询所有访问日志:
52
+ ```bash
53
+ curl 'http://localhost:3000/@logs?type=ACCESS'
54
+ ```
55
+
56
+ 2. 查询特定 eventId 的日志:
57
+ ```bash
58
+ curl 'http://localhost:3000/@logs?eventId=550e8400-e29b-41d4-a716-446655440000'
59
+ ```
60
+
61
+ 3. 限制返回条数:
62
+ ```bash
63
+ curl 'http://localhost:3000/@logs?limit=10'
64
+ ```
65
+
66
+ 4. 组合查询:
67
+ ```bash
68
+ curl 'http://localhost:3000/@logs?type=USERCODE&eventId=550e8400-e29b-41d4-a716-446655440000&limit=20'
69
+ ```
70
+
71
+ #### 响应格式
72
+
73
+ 成功响应:
74
+ ```json
75
+ {
76
+ "success": true,
77
+ "data": [
78
+ {
79
+ "@timestamp": "2024-03-21T10:30:00.000Z",
80
+ "level": "info",
81
+ "eventId": "550e8400-e29b-41d4-a716-446655440000",
82
+ "message": "Request started",
83
+ "method": "GET",
84
+ "url": "/api/example"
85
+ }
86
+ ]
87
+ }
88
+ ```
89
+
90
+ 错误响应:
91
+ ```json
92
+ {
93
+ "success": false,
94
+ "error": "错误信息"
95
+ }
96
+ ```
97
+
98
+ ### 在代码中使用日志
99
+
100
+ ```typescript
101
+ import { functionsLogger } from '@vectorx/functions-framework';
102
+ import { LogLevel } from '@vectorx/functions-framework';
103
+
104
+ // 记录访问日志
105
+ functionsLogger.logAccesslog(LogLevel.INFO, 'Custom message', {
106
+ customField: 'value'
107
+ });
108
+
109
+ // 记录用户代码日志
110
+ functionsLogger.logUserCodelog([
111
+ { message: 'User log message', level: 'info' }
112
+ ]);
113
+ ```
114
+
115
+ ### 日志格式
116
+
117
+ 每条日志都包含以下字段:
118
+
119
+ - `@timestamp`: 日志时间戳
120
+ - `level`: 日志级别
121
+ - `eventId`: 请求 ID
122
+ - `message`: 日志消息
123
+ - 其他自定义字段
124
+
125
+ ## 开发
126
+
127
+ ### 运行测试
128
+
129
+ ```bash
130
+ npm test
131
+ ```
132
+
133
+ ### 构建
134
+
135
+ ```bash
136
+ npm run build
137
+ ```
138
+
139
+
140
+ ### 环境管理
141
+ 读取 .env 文件,校验 env 文件的可用性,获取当前运行环境
package/bin/rcb-ff.js ADDED
@@ -0,0 +1,153 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { Command } = require('commander');
4
+ const { createAgentServerFramework, getDependencyVersion } = require('../lib');
5
+ const path = require('path');
6
+ const fs = require('fs');
7
+ const nodemon = require('nodemon');
8
+ const chalk = require('chalk');
9
+
10
+ const program = new Command();
11
+
12
+ program
13
+ .name('rcb-ff')
14
+ .description('RCB Functions Framework CLI')
15
+ .version('0.1.0')
16
+ .option('-d, --directory <path>', '指定函数目录路径', process.cwd())
17
+ .option('-p, --port <number>', '指定服务端口', '3000')
18
+ .option('-t, --target <string>', '指定目标函数名', 'main')
19
+ .option('-s, --source <string>', '指定源文件路径', 'index.js')
20
+ .option('--enable-cors', '启用 CORS')
21
+ .option('--allowed-origins <origins>', 'CORS 允许的域名,用逗号分隔', 'localhost,127.0.0.1')
22
+ .option('--config <path>', '指定配置文件路径', 'agent-cloudbase-functions.json')
23
+ .option('--enableRedLangfuse', '启用 Red Langfuse 上报')
24
+ .option('-w, --watch', '启用文件监听模式,文件变化时自动重启服务');
25
+
26
+ program.parse();
27
+
28
+ const options = program.opts();
29
+
30
+ async function startServer() {
31
+ try {
32
+ // 解析并验证目录路径
33
+ const directory = path.resolve(options.directory);
34
+ if (!fs.existsSync(directory)) {
35
+ console.error(`错误: 目录不存在: ${directory}`);
36
+ process.exit(1);
37
+ }
38
+
39
+ // 构建框架配置
40
+ const frameworkOptions = {
41
+ port: parseInt(options.port),
42
+ target: options.target,
43
+ sourceLocation: options.source,
44
+ timeoutMS: process.env.FUNCTION_TIMEOUT ? parseInt(process.env.FUNCTION_TIMEOUT) : 0,
45
+ keepAliveTimeoutMS: process.env.FUNCTION_KEEPALIVE ? parseInt(process.env.FUNCTION_KEEPALIVE) : 25000,
46
+ functionsConfigFile: path.resolve(directory, options.config),
47
+ enableCors: options.enableCors,
48
+ allowedOrigins: options.allowedOrigins.split(','),
49
+ redLangfuseConfig: {
50
+ enable: Boolean(options.enableRedLangfuse),
51
+ },
52
+ kitInfo: {
53
+ agentRuntimeVersion: getDependencyVersion('@vectorx/agent-runtime', directory),
54
+ functionsFrameworkVersion: getDependencyVersion('@vectorx/functions-framework', directory)
55
+ }
56
+ };
57
+
58
+ // 切换到目标目录
59
+ process.chdir(directory);
60
+
61
+ // 创建独立的控制台实例,避免被框架拦截
62
+ const startupConsole = new console.Console({
63
+ stdout: process.stdout,
64
+ stderr: process.stderr
65
+ });
66
+
67
+ startupConsole.log(`工作目录: ${directory}`);
68
+
69
+ // 启动框架
70
+ const framework = createAgentServerFramework(frameworkOptions);
71
+ const server = await framework.startServer();
72
+
73
+ server.listen(frameworkOptions.port, () => {
74
+ // 使用独立的控制台实例打印启动信息
75
+ startupConsole.log('\n\n' + chalk.bold.cyan(' === RCB Functions Framework ==='));
76
+ startupConsole.log(`${chalk.green('✔')} ${chalk.bold('状态')}: ${chalk.green('运行中')}`);
77
+ startupConsole.log(`${chalk.green('✔')} ${chalk.bold('端口')}: ${chalk.yellow(frameworkOptions.port)}`);
78
+ startupConsole.log(`${chalk.green('✔')} ${chalk.bold('URL')}: ${chalk.blue.underline(`http://localhost:${frameworkOptions.port}`)}`);
79
+ startupConsole.log(`${chalk.green('✔')} ${chalk.bold('目标函数')}: ${chalk.yellow(frameworkOptions.target)}`);
80
+ startupConsole.log(`${chalk.green('✔')} ${chalk.bold('配置文件')}: ${chalk.dim(frameworkOptions.functionsConfigFile)}`);
81
+ startupConsole.log(`${chalk.green('✔')} ${chalk.bold('CORS')}: ${frameworkOptions.enableCors ? chalk.green('启用') : chalk.red('禁用')}`);
82
+ startupConsole.log(`${chalk.green('✔')} ${chalk.bold('源文件')}: ${chalk.dim(frameworkOptions.sourceLocation)}`);
83
+ startupConsole.log(`${chalk.green('✔')} ${chalk.bold('已注册的函数')}: ${chalk.yellow(framework.getRegisteredFunction('@fn:main'))}`);
84
+ if (frameworkOptions.enableCors) {
85
+ startupConsole.log(`${chalk.green('✔')} ${chalk.bold('允许的域名')}: ${chalk.dim(frameworkOptions.allowedOrigins.join(', '))}`);
86
+ }
87
+
88
+ if (typeof process.send === 'function') {
89
+ process.send({
90
+ type: 'start-up',
91
+ status: 'success',
92
+ timestamp: Date.now()
93
+ });
94
+ }
95
+ });
96
+
97
+ process.on('SIGTERM', () => {
98
+ startupConsole.log('\n接收到 SIGTERM 信号: 正在关闭 HTTP 服务器');
99
+ server.close(() => {
100
+ startupConsole.log('HTTP 服务器已关闭');
101
+ process.exit(0);
102
+ });
103
+ });
104
+
105
+ process.on('SIGINT', () => {
106
+ startupConsole.log('\n接收到 Ctrl+C: 正在关闭 HTTP 服务器');
107
+ server.close(() => {
108
+ startupConsole.log('HTTP 服务器已关闭');
109
+ });
110
+ process.exit(0);
111
+ });
112
+
113
+ } catch (err) {
114
+ console.error('启动服务器失败:', err);
115
+ process.exit(1);
116
+ }
117
+ }
118
+
119
+ async function main() {
120
+ if (options.watch) {
121
+ // 使用 nodemon 启动并监听文件变化
122
+ const script = path.join(__dirname, 'rcb-ff.js');
123
+ const args = process.argv.slice(2).filter(arg => arg !== '-w' && arg !== '--watch');
124
+
125
+ nodemon({
126
+ script: script,
127
+ args: args,
128
+ watch: [options.directory],
129
+ ext: 'js,json,ts,tsx,jsx',
130
+ ignore: ['node_modules/**/*'],
131
+ delay: 1000,
132
+ })
133
+ .on('start', () => {
134
+ console.log('\n🔄 监听模式已启动...');
135
+ })
136
+ .on('restart', (files) => {
137
+ console.log('\n🔄 检测到文件变化,正在重启服务...');
138
+ console.log('变更文件:', files);
139
+ })
140
+ .on('crash', () => {
141
+ console.log('\n💥 服务崩溃,等待文件变化后重启...');
142
+ })
143
+ .on('quit', () => {
144
+ console.log('\n👋 监听模式已退出');
145
+ process.exit();
146
+ });
147
+ } else {
148
+ // 直接启动服务
149
+ await startServer();
150
+ }
151
+ }
152
+
153
+ main();
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.newExecutionContext = newExecutionContext;
13
+ exports.getCurrentEventID = getCurrentEventID;
14
+ exports.getCurrentAsyncContext = getCurrentAsyncContext;
15
+ exports.isInFrameworkAsyncContext = isInFrameworkAsyncContext;
16
+ exports.done = done;
17
+ exports.wrapWithAsyncContext = wrapWithAsyncContext;
18
+ const async_hooks_1 = require("async_hooks");
19
+ const CONTEXT_ID = Symbol("@vectorx/functions-framework");
20
+ const asyncLocalStorage = new async_hooks_1.AsyncLocalStorage();
21
+ function newExecutionContext(eventId, apmData) {
22
+ return {
23
+ id: CONTEXT_ID,
24
+ eventID: eventId,
25
+ done: false,
26
+ userCodeLogs: [],
27
+ apmMeasurementData: apmData ? Object.assign({}, apmData) : undefined,
28
+ };
29
+ }
30
+ function getCurrentEventID() {
31
+ const context = asyncLocalStorage.getStore();
32
+ return (context === null || context === void 0 ? void 0 : context.eventID) || "";
33
+ }
34
+ function getCurrentAsyncContext() {
35
+ return asyncLocalStorage.getStore();
36
+ }
37
+ function isInFrameworkAsyncContext() {
38
+ const context = asyncLocalStorage.getStore();
39
+ return context !== undefined && context.id === CONTEXT_ID;
40
+ }
41
+ function done() {
42
+ return __awaiter(this, void 0, void 0, function* () {
43
+ const context = asyncLocalStorage.getStore();
44
+ if (context !== undefined) {
45
+ context.done = true;
46
+ }
47
+ });
48
+ }
49
+ function wrapWithAsyncContext(executionContext, fn) {
50
+ return asyncLocalStorage.run(executionContext, () => __awaiter(this, void 0, void 0, function* () {
51
+ try {
52
+ const result = yield fn();
53
+ yield done();
54
+ return result;
55
+ }
56
+ catch (error) {
57
+ yield done();
58
+ throw error;
59
+ }
60
+ }));
61
+ }
package/lib/config.js ADDED
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getBaseUrl = getBaseUrl;
4
+ const ENV_BASE_URL = {
5
+ development: "https://agentbase-sandbox.xiaohongshu.com",
6
+ production: "https://agentbase.xiaohongshu.com",
7
+ };
8
+ function getBaseUrl() {
9
+ return ENV_BASE_URL[process.env.OPEN_PLATFORM_STAGE] || ENV_BASE_URL.development;
10
+ }
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.defaultOptions = void 0;
4
+ exports.defaultOptions = {
5
+ port: 3000,
6
+ timeoutMS: 0,
7
+ keepAliveTimeoutMS: 25000,
8
+ functionsConfigFile: "agent-cloudbase-functions.json",
9
+ enableCors: false,
10
+ };
package/lib/error.js ADDED
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ErrorCode = exports.AppError = void 0;
4
+ exports.decorateErrorStack = decorateErrorStack;
5
+ exports.newSysErr = newSysErr;
6
+ exports.newBadRequestErr = newBadRequestErr;
7
+ exports.newTimeoutErr = newTimeoutErr;
8
+ exports.newTooManyRequestsErr = newTooManyRequestsErr;
9
+ exports.newUnauthorizedErr = newUnauthorizedErr;
10
+ class AppError extends Error {
11
+ constructor(code, message, options) {
12
+ super(message);
13
+ this.code = code;
14
+ this.cause = options.cause;
15
+ }
16
+ }
17
+ exports.AppError = AppError;
18
+ var ErrorCode;
19
+ (function (ErrorCode) {
20
+ ErrorCode[ErrorCode["SYS_ERR"] = 500] = "SYS_ERR";
21
+ ErrorCode[ErrorCode["BAD_REQUEST"] = 400] = "BAD_REQUEST";
22
+ ErrorCode[ErrorCode["TIMEOUT"] = 408] = "TIMEOUT";
23
+ ErrorCode[ErrorCode["TOO_MANY_REQUESTS"] = 429] = "TOO_MANY_REQUESTS";
24
+ ErrorCode[ErrorCode["UNAUTHORIZED"] = 401] = "UNAUTHORIZED";
25
+ })(ErrorCode || (exports.ErrorCode = ErrorCode = {}));
26
+ function decorateErrorStack(err) {
27
+ if (!err || typeof err.message !== "string" || typeof err.stack !== "string") {
28
+ return "";
29
+ }
30
+ return err.stack.split("\n").join("\n");
31
+ }
32
+ function newSysErr(err) {
33
+ return new AppError(ErrorCode.SYS_ERR, "System Error.", { cause: err });
34
+ }
35
+ function newBadRequestErr(err) {
36
+ const message = err instanceof Error ? err.message : err;
37
+ return new AppError(ErrorCode.BAD_REQUEST, message, { cause: err });
38
+ }
39
+ function newTimeoutErr() {
40
+ return new AppError(ErrorCode.TIMEOUT, "Request timeout", { cause: new Error("Request timeout") });
41
+ }
42
+ function newTooManyRequestsErr(err) {
43
+ return new AppError(ErrorCode.TOO_MANY_REQUESTS, "Too many requests", { cause: err });
44
+ }
45
+ function newUnauthorizedErr(err) {
46
+ return new AppError(ErrorCode.UNAUTHORIZED, "Unauthorized", { cause: err });
47
+ }
@@ -0,0 +1,183 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.getDependencyVersion = getDependencyVersion;
16
+ exports.createAgentServerFramework = createAgentServerFramework;
17
+ const fs_1 = require("fs");
18
+ const path_1 = __importDefault(require("path"));
19
+ const chalk_1 = __importDefault(require("chalk"));
20
+ const dotenv_1 = __importDefault(require("dotenv"));
21
+ const constants_1 = require("./constants");
22
+ const function_loader_1 = require("./function-loader");
23
+ const function_registry_1 = require("./function-registry");
24
+ const logger_1 = require("./logger");
25
+ const router_1 = require("./router");
26
+ const server_1 = require("./server");
27
+ const apm_1 = require("./utils/apm");
28
+ const console_intercept_1 = require("./utils/console-intercept");
29
+ function getDependencyVersion(packageName, searchPath) {
30
+ try {
31
+ const packagePath = require.resolve(packageName, { paths: [searchPath] });
32
+ let currentPath = path_1.default.dirname(packagePath);
33
+ let packageJsonPath = path_1.default.join(currentPath, "package.json");
34
+ while (!(0, fs_1.existsSync)(packageJsonPath) && currentPath !== path_1.default.parse(currentPath).root) {
35
+ currentPath = path_1.default.dirname(currentPath);
36
+ packageJsonPath = path_1.default.join(currentPath, "package.json");
37
+ }
38
+ if ((0, fs_1.existsSync)(packageJsonPath)) {
39
+ const pkg = require(packageJsonPath);
40
+ return pkg.version || null;
41
+ }
42
+ return null;
43
+ }
44
+ catch (e) {
45
+ return null;
46
+ }
47
+ }
48
+ class AgentServerFramework {
49
+ constructor(options) {
50
+ var _a, _b, _c, _d, _e, _f;
51
+ this.options = Object.assign(Object.assign({}, constants_1.defaultOptions), options);
52
+ this.apmReporter = new apm_1.ApmReporter();
53
+ console_intercept_1.Interceptor.intercept();
54
+ logger_1.functionsLogger.init([
55
+ {
56
+ type: logger_1.LogType.ACCESS,
57
+ options: {
58
+ dirname: (_a = options.logging) === null || _a === void 0 ? void 0 : _a.dirname,
59
+ maxSize: (_b = options.logging) === null || _b === void 0 ? void 0 : _b.maxSize,
60
+ maxFiles: (_c = options.logging) === null || _c === void 0 ? void 0 : _c.maxFiles,
61
+ },
62
+ },
63
+ {
64
+ type: logger_1.LogType.USERCODE,
65
+ options: {
66
+ dirname: (_d = options.logging) === null || _d === void 0 ? void 0 : _d.dirname,
67
+ maxSize: (_e = options.logging) === null || _e === void 0 ? void 0 : _e.maxSize,
68
+ maxFiles: (_f = options.logging) === null || _f === void 0 ? void 0 : _f.maxFiles,
69
+ },
70
+ },
71
+ ]);
72
+ logger_1.functionsLogger.setApmReporter(this.apmReporter);
73
+ }
74
+ startServer() {
75
+ return __awaiter(this, void 0, void 0, function* () {
76
+ const { functionsConfigFile } = this.options;
77
+ let cloudFunctionConfig;
78
+ console.log("==== process.env.OPEN_PLATFORM_STAGE ====", process.env.OPEN_PLATFORM_STAGE);
79
+ if (!process.env.OPEN_PLATFORM_STAGE) {
80
+ process.stdout.write(chalk_1.default.yellow(`未设置 OPEN_PLATFORM_STAGE 环境变量,使用默认值: development\n`));
81
+ process.env.OPEN_PLATFORM_STAGE = "development";
82
+ }
83
+ const stage = process.env.OPEN_PLATFORM_STAGE || "development";
84
+ const defaultEnvFilePath = path_1.default.join(process.cwd(), ".env");
85
+ const stageEnvFilePath = path_1.default.join(process.cwd(), `.env.${stage}`);
86
+ let dotEnvContent;
87
+ if ((0, fs_1.existsSync)(defaultEnvFilePath)) {
88
+ dotEnvContent = dotenv_1.default.config({ path: path_1.default.resolve(defaultEnvFilePath) });
89
+ }
90
+ else if ((0, fs_1.existsSync)(stageEnvFilePath)) {
91
+ dotEnvContent = dotenv_1.default.config({ path: path_1.default.resolve(stageEnvFilePath) });
92
+ }
93
+ if (dotEnvContent && dotEnvContent.parsed) {
94
+ const envKeys = Object.keys(dotEnvContent.parsed);
95
+ envKeys.forEach((key) => {
96
+ const regexForComment = / #+.*$/g;
97
+ const envValue = dotEnvContent.parsed[key];
98
+ if (regexForComment.test(envValue)) {
99
+ process.stdout.write(chalk_1.default.yellow(`在dotenv配置中字段${key}发现 #,请确保注释都写在单独由#开头的新一行, 不支持行内注释。
100
+ 详情查看:https://github.com/motdotla/dotenv#rules\n`));
101
+ }
102
+ });
103
+ }
104
+ const projectConfigPath = path_1.default.join(process.cwd(), "project.config.json");
105
+ if (!(0, fs_1.existsSync)(projectConfigPath)) {
106
+ throw new Error(`
107
+ ❌ 项目配置文件未找到: ${projectConfigPath}
108
+
109
+ 请确保:
110
+ 1. 配置文件 'project.config.json' 存在于项目根目录
111
+ 2. 配置文件格式正确,示例:
112
+ {
113
+ "agentId": "示例 agentId ",
114
+ "version": "0.0.1",
115
+ "desc": "发布示例",
116
+ }
117
+ `);
118
+ }
119
+ const projectConfig = require(projectConfigPath);
120
+ if (!(0, fs_1.existsSync)(functionsConfigFile)) {
121
+ throw new Error(`
122
+ ❌ 配置文件未找到: ${functionsConfigFile}
123
+
124
+ 请确保:
125
+ 1. 配置文件 'agent-cloudbase-functions.json' 存在于项目根目录
126
+ 2. 配置文件格式正确,示例:
127
+ {
128
+ "functionsRoot": "./src",
129
+ "functions": [
130
+ {
131
+ "name": "main",
132
+ "directory": "./",
133
+ "source": "index.js",
134
+ "triggerPath": "/"
135
+ }
136
+ ]
137
+ }
138
+ `);
139
+ }
140
+ try {
141
+ cloudFunctionConfig = (0, function_loader_1.loadFunctionsConfig)(functionsConfigFile);
142
+ for (const fn of cloudFunctionConfig.functions) {
143
+ const mainFn = (0, function_loader_1.loadFunction)(cloudFunctionConfig.functionsRoot, fn);
144
+ if (!mainFn) {
145
+ throw new Error(`
146
+ ❌ 函数加载失败: ${fn.name}
147
+
148
+ 请检查:
149
+ 1. 函数目录是否存在: ${path_1.default.join(cloudFunctionConfig.functionsRoot, fn.directory)}
150
+ 2. 源文件是否存在: ${fn.source}
151
+ 3. 函数是否正确导出
152
+ `);
153
+ }
154
+ (0, function_registry_1.registerFunction)(fn.name, mainFn);
155
+ }
156
+ this.router = new router_1.Router(cloudFunctionConfig);
157
+ }
158
+ catch (err) {
159
+ throw new Error(`
160
+ ❌ 函数配置加载失败
161
+
162
+ 错误详情: ${err}
163
+
164
+ 请检查:
165
+ 1. 配置文件格式是否正确
166
+ 2. 所有引用的路径是否存在
167
+ 3. 函数是否正确实现
168
+
169
+ 配置文件路径: ${functionsConfigFile}
170
+ `);
171
+ }
172
+ const server = (0, server_1.createServer)(this.options, this.router, projectConfig);
173
+ return server;
174
+ });
175
+ }
176
+ getRegisteredFunction(fnName) {
177
+ return (0, function_registry_1.getRegisteredFunction)(fnName);
178
+ }
179
+ }
180
+ function createAgentServerFramework(options) {
181
+ const agentInstance = new AgentServerFramework(options);
182
+ return agentInstance;
183
+ }
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_FN_NAME = void 0;
4
+ exports.loadFunctionsConfig = loadFunctionsConfig;
5
+ exports.loadFunction = loadFunction;
6
+ const fs_1 = require("fs");
7
+ const path_1 = require("path");
8
+ exports.DEFAULT_FN_NAME = "main";
9
+ function loadFunctionsConfig(configPath) {
10
+ try {
11
+ const config = require(configPath);
12
+ config.functionsRoot = (0, path_1.join)((0, path_1.dirname)(configPath), config.functionsRoot);
13
+ if (!config.functions) {
14
+ config.functions = [];
15
+ }
16
+ if (!config.routes) {
17
+ config.routes = [];
18
+ }
19
+ const routeMaps = new Map();
20
+ if (Array.isArray(config.functions)) {
21
+ for (const fn of config.functions) {
22
+ if (fn.triggerPath) {
23
+ if (routeMaps.has(fn.triggerPath)) {
24
+ const existsTriggerFnName = routeMaps.get(fn.triggerPath);
25
+ if (existsTriggerFnName === fn.name) {
26
+ console.warn(`Route '${fn.triggerPath}'-> '${fn.name}' already exists, ignore.`);
27
+ continue;
28
+ }
29
+ else {
30
+ throw new Error(`Route '${fn.triggerPath}->${fn.name}' conflict with '${fn.triggerPath}->${existsTriggerFnName}', please check your functions config`);
31
+ }
32
+ }
33
+ config.routes.push({ functionName: fn.name, path: fn.triggerPath });
34
+ routeMaps.set(fn.triggerPath, fn.name);
35
+ }
36
+ }
37
+ }
38
+ return config;
39
+ }
40
+ catch (err) {
41
+ throw new Error(`Failed to load functions config: ${err}`);
42
+ }
43
+ }
44
+ function loadFunction(functionsRoot, functionConfig) {
45
+ if (!functionConfig.source) {
46
+ functionConfig.source = exports.DEFAULT_FN_NAME;
47
+ }
48
+ const indexPath = (0, path_1.join)(functionsRoot, functionConfig.source);
49
+ if (!(0, fs_1.existsSync)(indexPath)) {
50
+ throw new Error(`Function entry file not found at: ${indexPath}, functionConfig: ${JSON.stringify(functionConfig)}`);
51
+ }
52
+ try {
53
+ const module = require(indexPath);
54
+ const fn = module[functionConfig.name];
55
+ if (typeof fn !== "function") {
56
+ throw new Error(`Function '${functionConfig.name}' not found in ${indexPath}`);
57
+ }
58
+ return fn;
59
+ }
60
+ catch (err) {
61
+ throw new Error(`Failed to load function '${functionConfig.name}': ${err}`);
62
+ }
63
+ }