@vectorx/functions-framework 0.8.1 → 1.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.
- package/README.md +29 -1
- package/bin/rcb-ff.js +53 -1
- package/lib/config.js +2 -5
- package/lib/constants.js +1 -0
- package/lib/framework.js +82 -28
- package/lib/function-loader.js +2 -1
- package/lib/function-wrapper.js +102 -9
- package/lib/middlewares/index.js +3 -1
- package/lib/middlewares/middle-common-logger.js +2 -1
- package/lib/middlewares/middle-env-vars-injection.js +65 -0
- package/lib/router.js +6 -0
- package/lib/server.js +1 -0
- package/lib/telemetry/langfuse.js +5 -3
- package/lib/utils/apm.config.js +2 -1
- package/package.json +4 -3
- package/types/framework.d.ts +5 -0
- package/types/middlewares/index.d.ts +1 -0
- package/types/middlewares/middle-env-vars-injection.d.ts +2 -0
- package/types/router.d.ts +4 -4
- package/types/server.d.ts +12 -0
package/README.md
CHANGED
|
@@ -136,6 +136,34 @@ npm test
|
|
|
136
136
|
npm run build
|
|
137
137
|
```
|
|
138
138
|
|
|
139
|
-
|
|
140
139
|
### 环境管理
|
|
141
140
|
读取 .env 文件,校验 env 文件的可用性,获取当前运行环境
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
## rcb-ff 自动检测项目类型
|
|
144
|
+
|
|
145
|
+
当直接使用 `rcb-ff` 命令启动服务时,如果未指定 `--mode` 参数,会自动检测项目类型:
|
|
146
|
+
|
|
147
|
+
### 检测规则
|
|
148
|
+
|
|
149
|
+
1. **Agent 项目检测**:
|
|
150
|
+
- 如果存在 `project.config.json` 文件且包含 `agentId` 字段,则判定为 `agent` 项目
|
|
151
|
+
|
|
152
|
+
2. **Fun 项目检测**:
|
|
153
|
+
- 如果不存在 `project.config.json` 或 `project.config.json` 中不存在 `agentId` 字段
|
|
154
|
+
- 且存在 `agent-cloudbase-functions.json` 配置文件
|
|
155
|
+
- 则判定为 `fun` 项目
|
|
156
|
+
|
|
157
|
+
3. **默认行为**:
|
|
158
|
+
- 如果无法确定项目类型,默认使用 `agent` 模式(保持向后兼容)
|
|
159
|
+
|
|
160
|
+
### 使用示例
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
# 自动检测项目类型(推荐)
|
|
164
|
+
rcb-ff --directory ./my-project
|
|
165
|
+
|
|
166
|
+
# 明确指定模式(覆盖自动检测)
|
|
167
|
+
rcb-ff --directory ./my-project --mode fun
|
|
168
|
+
rcb-ff --directory ./my-project --mode agent
|
|
169
|
+
```
|
package/bin/rcb-ff.js
CHANGED
|
@@ -7,6 +7,40 @@ const fs = require('fs');
|
|
|
7
7
|
const nodemon = require('nodemon');
|
|
8
8
|
const chalk = require('chalk');
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* 自动检测项目类型(agent 或 fun)
|
|
12
|
+
* 参考 fun-project-validator.ts 的检测逻辑
|
|
13
|
+
* @param {string} workDir 工作目录
|
|
14
|
+
* @returns {string} 'agent' | 'fun'
|
|
15
|
+
*/
|
|
16
|
+
function detectProjectMode(workDir) {
|
|
17
|
+
const projectConfigPath = path.join(workDir, 'project.config.json');
|
|
18
|
+
|
|
19
|
+
// 检查是否存在 project.config.json 且包含 agentId
|
|
20
|
+
if (fs.existsSync(projectConfigPath)) {
|
|
21
|
+
try {
|
|
22
|
+
const projectConfig = JSON.parse(fs.readFileSync(projectConfigPath, 'utf-8'));
|
|
23
|
+
if (projectConfig.agentId) {
|
|
24
|
+
// 存在 agentId,判定为 agent 项目
|
|
25
|
+
return 'agent';
|
|
26
|
+
}
|
|
27
|
+
} catch (error) {
|
|
28
|
+
// project.config.json 解析失败,继续检测
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// 检查是否存在 agent-cloudbase-functions.json
|
|
33
|
+
const functionsConfigPath = path.join(workDir, 'agent-cloudbase-functions.json');
|
|
34
|
+
if (fs.existsSync(functionsConfigPath)) {
|
|
35
|
+
// 不存在 project.config.json 或不存在 agentId,但存在 agent-cloudbase-functions.json
|
|
36
|
+
// 判定为 fun 项目
|
|
37
|
+
return 'fun';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// 默认返回 agent(保持向后兼容)
|
|
41
|
+
return 'agent';
|
|
42
|
+
}
|
|
43
|
+
|
|
10
44
|
const program = new Command();
|
|
11
45
|
|
|
12
46
|
program
|
|
@@ -20,13 +54,22 @@ program
|
|
|
20
54
|
.option('--enable-cors', '启用 CORS')
|
|
21
55
|
.option('--allowed-origins <origins>', 'CORS 允许的域名,用逗号分隔', 'localhost,127.0.0.1')
|
|
22
56
|
.option('--config <path>', '指定配置文件路径', 'agent-cloudbase-functions.json')
|
|
57
|
+
.option('--mode <mode>', '运行模式: agent | fun(未指定时自动检测)')
|
|
23
58
|
.option('--enableRedLangfuse', '启用 Red Langfuse 上报')
|
|
59
|
+
.option('--verbose', '输出详细错误堆栈(用于排查问题)')
|
|
24
60
|
.option('-w, --watch', '启用文件监听模式,文件变化时自动重启服务');
|
|
25
61
|
|
|
26
62
|
program.parse();
|
|
27
63
|
|
|
28
64
|
const options = program.opts();
|
|
29
65
|
|
|
66
|
+
// 如果未明确指定 mode,则自动检测
|
|
67
|
+
if (!options.mode) {
|
|
68
|
+
const detectedMode = detectProjectMode(path.resolve(options.directory));
|
|
69
|
+
options.mode = detectedMode;
|
|
70
|
+
console.log(chalk.dim(`[自动检测] 项目类型: ${detectedMode}`));
|
|
71
|
+
}
|
|
72
|
+
|
|
30
73
|
async function startServer() {
|
|
31
74
|
try {
|
|
32
75
|
// 解析并验证目录路径
|
|
@@ -46,6 +89,7 @@ async function startServer() {
|
|
|
46
89
|
functionsConfigFile: path.resolve(directory, options.config),
|
|
47
90
|
enableCors: options.enableCors,
|
|
48
91
|
allowedOrigins: options.allowedOrigins.split(','),
|
|
92
|
+
mode: options.mode === 'fun' ? 'fun' : 'agent',
|
|
49
93
|
redLangfuseConfig: {
|
|
50
94
|
enable: Boolean(options.enableRedLangfuse),
|
|
51
95
|
},
|
|
@@ -74,6 +118,7 @@ async function startServer() {
|
|
|
74
118
|
// 使用独立的控制台实例打印启动信息
|
|
75
119
|
startupConsole.log('\n\n' + chalk.bold.cyan(' === RCB Functions Framework ==='));
|
|
76
120
|
startupConsole.log(`${chalk.green('✔')} ${chalk.bold('状态')}: ${chalk.green('运行中')}`);
|
|
121
|
+
startupConsole.log(`${chalk.green('✔')} ${chalk.bold('模式')}: ${chalk.yellow(frameworkOptions.mode)}`);
|
|
77
122
|
startupConsole.log(`${chalk.green('✔')} ${chalk.bold('端口')}: ${chalk.yellow(frameworkOptions.port)}`);
|
|
78
123
|
startupConsole.log(`${chalk.green('✔')} ${chalk.bold('URL')}: ${chalk.blue.underline(`http://localhost:${frameworkOptions.port}`)}`);
|
|
79
124
|
startupConsole.log(`${chalk.green('✔')} ${chalk.bold('目标函数')}: ${chalk.yellow(frameworkOptions.target)}`);
|
|
@@ -111,7 +156,14 @@ async function startServer() {
|
|
|
111
156
|
});
|
|
112
157
|
|
|
113
158
|
} catch (err) {
|
|
114
|
-
|
|
159
|
+
// 默认只输出友好错误信息(避免堆栈刷屏);需要排查时可通过 --verbose 输出完整堆栈
|
|
160
|
+
const message = (err && err.message) ? err.message : String(err);
|
|
161
|
+
console.error('启动服务器失败:');
|
|
162
|
+
console.error(message);
|
|
163
|
+
if (options.verbose) {
|
|
164
|
+
const stack = (err && err.stack) ? err.stack : '';
|
|
165
|
+
if (stack) console.error(stack);
|
|
166
|
+
}
|
|
115
167
|
process.exit(1);
|
|
116
168
|
}
|
|
117
169
|
}
|
package/lib/config.js
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getBaseUrl = getBaseUrl;
|
|
4
|
-
const
|
|
5
|
-
development: "https://agentbase-sandbox.xiaohongshu.com",
|
|
6
|
-
production: "https://agentbase.xiaohongshu.com",
|
|
7
|
-
};
|
|
4
|
+
const endpoints_1 = require("@vectorx/endpoints");
|
|
8
5
|
function getBaseUrl() {
|
|
9
|
-
return
|
|
6
|
+
return (0, endpoints_1.resolveUrl)("@vectorx/functions-framework", "baseUrl");
|
|
10
7
|
}
|
package/lib/constants.js
CHANGED
package/lib/framework.js
CHANGED
|
@@ -73,8 +73,9 @@ class AgentServerFramework {
|
|
|
73
73
|
}
|
|
74
74
|
startServer() {
|
|
75
75
|
return __awaiter(this, void 0, void 0, function* () {
|
|
76
|
-
const { functionsConfigFile } = this.options;
|
|
76
|
+
const { functionsConfigFile, mode = "agent" } = this.options;
|
|
77
77
|
let cloudFunctionConfig;
|
|
78
|
+
let projectConfig;
|
|
78
79
|
if (!process.env.OPEN_PLATFORM_STAGE) {
|
|
79
80
|
process.stdout.write(chalk_1.default.yellow(`未设置 OPEN_PLATFORM_STAGE 环境变量,使用默认值: development\n`));
|
|
80
81
|
process.env.OPEN_PLATFORM_STAGE = "development";
|
|
@@ -100,9 +101,27 @@ class AgentServerFramework {
|
|
|
100
101
|
}
|
|
101
102
|
});
|
|
102
103
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
104
|
+
if (mode === "agent") {
|
|
105
|
+
({ projectConfig, cloudFunctionConfig } = this.loadAgentModeConfigs(functionsConfigFile));
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
({ projectConfig, cloudFunctionConfig } = this.loadFunModeConfigs(functionsConfigFile));
|
|
109
|
+
}
|
|
110
|
+
this.router = new router_1.Router(cloudFunctionConfig);
|
|
111
|
+
const server = (0, server_1.createServer)(this.options, this.router, projectConfig);
|
|
112
|
+
return server;
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
getRegisteredFunction(fnName) {
|
|
116
|
+
const fnId = (0, function_registry_1.buildFnId)("@fn", fnName);
|
|
117
|
+
return (0, function_registry_1.getRegisteredFunction)(fnId);
|
|
118
|
+
}
|
|
119
|
+
loadAgentModeConfigs(functionsConfigFile) {
|
|
120
|
+
const projectConfigPath = path_1.default.join(process.cwd(), "project.config.json");
|
|
121
|
+
if (!(0, fs_1.existsSync)(projectConfigPath)) {
|
|
122
|
+
const funConfigPath = path_1.default.join(process.cwd(), "agent-cloudbase-functions.json");
|
|
123
|
+
const maybeFunProject = (0, fs_1.existsSync)(funConfigPath);
|
|
124
|
+
throw new Error(`
|
|
106
125
|
❌ 项目配置文件未找到: ${projectConfigPath}
|
|
107
126
|
|
|
108
127
|
请确保:
|
|
@@ -113,11 +132,23 @@ class AgentServerFramework {
|
|
|
113
132
|
"version": "0.0.1",
|
|
114
133
|
"desc": "发布示例",
|
|
115
134
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
135
|
+
${maybeFunProject
|
|
136
|
+
? `
|
|
137
|
+
💡 诊断提示:
|
|
138
|
+
- 检测到当前目录存在云函数配置文件(agent-cloudbase-functions.json)
|
|
139
|
+
- 这可能是一个“云函数(fun)项目”目录,但你使用了“智能体(agent)模式”启动
|
|
140
|
+
|
|
141
|
+
建议你:
|
|
142
|
+
- 改用 fun 模式启动(例如:rcb-ff --mode fun ... 或使用 rcb fun dev)
|
|
143
|
+
- 或切换到正确的 Agent 项目目录后再启动
|
|
144
|
+
|
|
145
|
+
参考文档:https://miniapp.xiaohongshu.com/doc/DC185638
|
|
146
|
+
`
|
|
147
|
+
: ""}`);
|
|
148
|
+
}
|
|
149
|
+
const projectConfig = require(projectConfigPath);
|
|
150
|
+
if (!(0, fs_1.existsSync)(functionsConfigFile)) {
|
|
151
|
+
throw new Error(`
|
|
121
152
|
❌ 配置文件未找到: ${functionsConfigFile}
|
|
122
153
|
|
|
123
154
|
请确保:
|
|
@@ -135,13 +166,42 @@ class AgentServerFramework {
|
|
|
135
166
|
]
|
|
136
167
|
}
|
|
137
168
|
`);
|
|
169
|
+
}
|
|
170
|
+
const cloudFunctionConfig = this.loadAndRegisterFunctions(functionsConfigFile, "agent");
|
|
171
|
+
return { projectConfig, cloudFunctionConfig };
|
|
172
|
+
}
|
|
173
|
+
loadFunModeConfigs(functionsConfigFile) {
|
|
174
|
+
if (!(0, fs_1.existsSync)(functionsConfigFile)) {
|
|
175
|
+
throw new Error(`
|
|
176
|
+
❌ 函数配置文件未找到: ${functionsConfigFile}
|
|
177
|
+
|
|
178
|
+
请确保:
|
|
179
|
+
1. 配置文件 'agent-cloudbase-functions.json' 存在于项目根目录
|
|
180
|
+
2. 配置文件格式正确,且 functions 字段已正确配置
|
|
181
|
+
`);
|
|
182
|
+
}
|
|
183
|
+
const cloudFunctionConfig = this.loadAndRegisterFunctions(functionsConfigFile, "fun");
|
|
184
|
+
const projectConfig = {
|
|
185
|
+
agentId: cloudFunctionConfig.agentId || "local-fun",
|
|
186
|
+
version: "0.0.0-local",
|
|
187
|
+
desc: "Local functions development (fun mode)",
|
|
188
|
+
name: "local-fun",
|
|
189
|
+
"@vectorx/agent-runtime": getDependencyVersion("@vectorx/agent-runtime", process.cwd()) || "",
|
|
190
|
+
"@vectorx/functions-framework": getDependencyVersion("@vectorx/functions-framework", process.cwd()) || "",
|
|
191
|
+
};
|
|
192
|
+
return { projectConfig, cloudFunctionConfig };
|
|
193
|
+
}
|
|
194
|
+
loadAndRegisterFunctions(functionsConfigFile, mode) {
|
|
195
|
+
try {
|
|
196
|
+
const cloudFunctionConfig = (0, function_loader_1.loadFunctionsConfig)(functionsConfigFile);
|
|
197
|
+
const fns = cloudFunctionConfig.functions || [];
|
|
198
|
+
if (!Array.isArray(fns) || fns.length === 0) {
|
|
199
|
+
throw new Error("functions 配置为空,请至少配置一个函数");
|
|
138
200
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
if (!mainFn) {
|
|
144
|
-
throw new Error(`
|
|
201
|
+
for (const fn of fns) {
|
|
202
|
+
const mainFn = (0, function_loader_1.loadFunction)(cloudFunctionConfig.functionsRoot, fn);
|
|
203
|
+
if (!mainFn) {
|
|
204
|
+
throw new Error(`
|
|
145
205
|
❌ 函数加载失败: ${fn.name}
|
|
146
206
|
|
|
147
207
|
请检查:
|
|
@@ -149,14 +209,14 @@ class AgentServerFramework {
|
|
|
149
209
|
2. 源文件是否存在: ${fn.source}
|
|
150
210
|
3. 函数是否正确导出
|
|
151
211
|
`);
|
|
152
|
-
}
|
|
153
|
-
(0, function_registry_1.registerFunction)(fn.name, mainFn);
|
|
154
212
|
}
|
|
155
|
-
|
|
213
|
+
(0, function_registry_1.registerFunction)(fn.name, mainFn);
|
|
156
214
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
215
|
+
return cloudFunctionConfig;
|
|
216
|
+
}
|
|
217
|
+
catch (err) {
|
|
218
|
+
throw new Error(`
|
|
219
|
+
❌ 函数配置加载失败(${mode} 模式)
|
|
160
220
|
|
|
161
221
|
错误详情: ${err}
|
|
162
222
|
|
|
@@ -167,13 +227,7 @@ class AgentServerFramework {
|
|
|
167
227
|
|
|
168
228
|
配置文件路径: ${functionsConfigFile}
|
|
169
229
|
`);
|
|
170
|
-
|
|
171
|
-
const server = (0, server_1.createServer)(this.options, this.router, projectConfig);
|
|
172
|
-
return server;
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
getRegisteredFunction(fnName) {
|
|
176
|
-
return (0, function_registry_1.getRegisteredFunction)(fnName);
|
|
230
|
+
}
|
|
177
231
|
}
|
|
178
232
|
}
|
|
179
233
|
function createAgentServerFramework(options) {
|
package/lib/function-loader.js
CHANGED
|
@@ -45,7 +45,8 @@ function loadFunction(functionsRoot, functionConfig) {
|
|
|
45
45
|
if (!functionConfig.source) {
|
|
46
46
|
functionConfig.source = exports.DEFAULT_FN_NAME;
|
|
47
47
|
}
|
|
48
|
-
const
|
|
48
|
+
const directory = functionConfig.directory || ".";
|
|
49
|
+
const indexPath = (0, path_1.join)(functionsRoot, directory, functionConfig.source);
|
|
49
50
|
if (!(0, fs_1.existsSync)(indexPath)) {
|
|
50
51
|
throw new Error(`Function entry file not found at: ${indexPath}, functionConfig: ${JSON.stringify(functionConfig)}`);
|
|
51
52
|
}
|
package/lib/function-wrapper.js
CHANGED
|
@@ -50,10 +50,62 @@ const request_1 = require("./request");
|
|
|
50
50
|
const sse_1 = require("./sse");
|
|
51
51
|
const unified_responder_1 = require("./unified-responder");
|
|
52
52
|
const helper_1 = require("./utils/helper");
|
|
53
|
+
const isPlainObject = (val) => {
|
|
54
|
+
if (!val || typeof val !== "object")
|
|
55
|
+
return false;
|
|
56
|
+
const proto = Object.getPrototypeOf(val);
|
|
57
|
+
return proto === Object.prototype || proto === null;
|
|
58
|
+
};
|
|
59
|
+
const parseCookies = (cookieHeader) => {
|
|
60
|
+
const cookies = {};
|
|
61
|
+
if (!cookieHeader || typeof cookieHeader !== "string")
|
|
62
|
+
return cookies;
|
|
63
|
+
const parts = cookieHeader.split(";");
|
|
64
|
+
for (const part of parts) {
|
|
65
|
+
const s = part.trim();
|
|
66
|
+
if (!s)
|
|
67
|
+
continue;
|
|
68
|
+
const idx = s.indexOf("=");
|
|
69
|
+
if (idx === -1)
|
|
70
|
+
continue;
|
|
71
|
+
const key = s.slice(0, idx).trim();
|
|
72
|
+
const val = s.slice(idx + 1).trim();
|
|
73
|
+
if (!key)
|
|
74
|
+
continue;
|
|
75
|
+
try {
|
|
76
|
+
cookies[key] = decodeURIComponent(val);
|
|
77
|
+
}
|
|
78
|
+
catch (_a) {
|
|
79
|
+
cookies[key] = val;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return cookies;
|
|
83
|
+
};
|
|
84
|
+
const normalizeQuery = (query) => {
|
|
85
|
+
if (!query || typeof query !== "object")
|
|
86
|
+
return {};
|
|
87
|
+
return Object.assign({}, query);
|
|
88
|
+
};
|
|
89
|
+
const normalizeHeaders = (headers) => {
|
|
90
|
+
if (!headers || typeof headers !== "object")
|
|
91
|
+
return {};
|
|
92
|
+
const out = {};
|
|
93
|
+
for (const [k, v] of Object.entries(headers)) {
|
|
94
|
+
out[String(k).toLowerCase()] = v;
|
|
95
|
+
}
|
|
96
|
+
return out;
|
|
97
|
+
};
|
|
98
|
+
const resolveTrigger = (ctx) => {
|
|
99
|
+
var _a, _b;
|
|
100
|
+
const fromHeader = String(((_b = (_a = ctx.request) === null || _a === void 0 ? void 0 : _a.headers) === null || _b === void 0 ? void 0 : _b["xhs-cloud-trigger"]) || "").toLowerCase();
|
|
101
|
+
if (fromHeader === "online" || fromHeader === "ide_debug" || fromHeader === "console_debug")
|
|
102
|
+
return fromHeader;
|
|
103
|
+
return "online";
|
|
104
|
+
};
|
|
53
105
|
const wrapEventFunction = (execute, projectConfig) => {
|
|
54
106
|
return (ctx, next) => __awaiter(void 0, void 0, void 0, function* () {
|
|
55
|
-
var _a;
|
|
56
|
-
let
|
|
107
|
+
var _a, _b, _c, _d, _e;
|
|
108
|
+
let params;
|
|
57
109
|
if (ctx.request.files) {
|
|
58
110
|
const filesWithBuffer = {};
|
|
59
111
|
const fs = yield Promise.resolve().then(() => __importStar(require("fs")));
|
|
@@ -79,21 +131,40 @@ const wrapEventFunction = (execute, projectConfig) => {
|
|
|
79
131
|
})));
|
|
80
132
|
filesWithBuffer[key] = Array.isArray(fileOrFiles) ? processedFiles : processedFiles[0];
|
|
81
133
|
}
|
|
82
|
-
|
|
134
|
+
const fields = isPlainObject(ctx.request.body) ? ctx.request.body : {};
|
|
135
|
+
params = Object.assign(Object.assign({}, fields), filesWithBuffer);
|
|
83
136
|
}
|
|
84
|
-
else if (ctx.
|
|
85
|
-
|
|
137
|
+
else if (ctx.method === "GET" || ctx.method === "HEAD") {
|
|
138
|
+
params = ctx.query || {};
|
|
86
139
|
}
|
|
87
140
|
else {
|
|
88
|
-
|
|
141
|
+
params = (_a = ctx.request.body) !== null && _a !== void 0 ? _a : ctx.rawBody;
|
|
89
142
|
}
|
|
143
|
+
if (params === undefined || params === null) {
|
|
144
|
+
params = {};
|
|
145
|
+
}
|
|
146
|
+
const event = { params };
|
|
90
147
|
let executeDone = false;
|
|
91
148
|
let hasSwitchSSEMode = false;
|
|
149
|
+
const headers = normalizeHeaders((_b = ctx.request) === null || _b === void 0 ? void 0 : _b.headers);
|
|
150
|
+
const httpMethod = ctx.method;
|
|
151
|
+
const path = ctx.path;
|
|
152
|
+
const url = ctx.url;
|
|
153
|
+
const query = normalizeQuery(ctx.query);
|
|
154
|
+
const cookies = parseCookies(headers.cookie);
|
|
155
|
+
const trigger = resolveTrigger(ctx);
|
|
92
156
|
const context = {
|
|
93
157
|
ctxId: ctx.state.eventID,
|
|
94
158
|
eventID: ctx.state.eventID,
|
|
95
159
|
eventType: ctx.state.eventType,
|
|
96
160
|
timestamp: ctx.state.timestamp,
|
|
161
|
+
headers,
|
|
162
|
+
httpMethod,
|
|
163
|
+
path,
|
|
164
|
+
url,
|
|
165
|
+
query,
|
|
166
|
+
cookies,
|
|
167
|
+
trigger,
|
|
97
168
|
httpContext: ctx.httpContext,
|
|
98
169
|
baseUrl: (0, config_1.getBaseUrl)(),
|
|
99
170
|
request: new request_1.Request({
|
|
@@ -102,11 +173,26 @@ const wrapEventFunction = (execute, projectConfig) => {
|
|
|
102
173
|
restrictedMethods: ["get", "post", "upload", "download"],
|
|
103
174
|
defaultHeaders: {
|
|
104
175
|
"agent-id": projectConfig.agentId,
|
|
105
|
-
"open-id": ctx.request.headers["open-id"],
|
|
176
|
+
"open-id": ((_d = (_c = ctx.request) === null || _c === void 0 ? void 0 : _c.headers) === null || _d === void 0 ? void 0 : _d["open-id"]) || "UNKNOWN",
|
|
106
177
|
"x-open-agent-trace-id": ctx.state.eventID,
|
|
107
178
|
},
|
|
108
179
|
}),
|
|
109
180
|
logger: logger_1.functionsLogger,
|
|
181
|
+
set: (field, value) => {
|
|
182
|
+
ctx.set(field, value);
|
|
183
|
+
},
|
|
184
|
+
remove: (field) => {
|
|
185
|
+
ctx.remove(field);
|
|
186
|
+
},
|
|
187
|
+
status: (code) => {
|
|
188
|
+
ctx.status = code;
|
|
189
|
+
},
|
|
190
|
+
setCookie: (name, value, options) => {
|
|
191
|
+
ctx.cookies.set(name, value, options);
|
|
192
|
+
},
|
|
193
|
+
clearCookie: (name, options) => {
|
|
194
|
+
ctx.cookies.set(name, null, Object.assign(Object.assign({}, (options || {})), { expires: new Date(0), maxAge: 0 }));
|
|
195
|
+
},
|
|
110
196
|
sse: () => {
|
|
111
197
|
var _a;
|
|
112
198
|
const sseInstance = (_a = ctx.sse) === null || _a === void 0 ? void 0 : _a.call(ctx);
|
|
@@ -127,12 +213,19 @@ const wrapEventFunction = (execute, projectConfig) => {
|
|
|
127
213
|
eventID: context.eventID,
|
|
128
214
|
eventType: context.eventType,
|
|
129
215
|
timestamp: context.timestamp,
|
|
216
|
+
headers: context.headers,
|
|
217
|
+
httpMethod: context.httpMethod,
|
|
218
|
+
path: context.path,
|
|
219
|
+
url: context.url,
|
|
220
|
+
query: context.query,
|
|
221
|
+
cookies: context.cookies,
|
|
222
|
+
trigger: context.trigger,
|
|
130
223
|
httpContext: context.httpContext,
|
|
131
224
|
baseUrl: context.baseUrl,
|
|
132
225
|
request: context.request,
|
|
133
226
|
};
|
|
134
227
|
(0, helper_1.deepFreeze)(contextToFreeze);
|
|
135
|
-
const frozenContext = Object.assign(Object.assign({}, contextToFreeze), { logger: context.logger, sse: context.sse });
|
|
228
|
+
const frozenContext = Object.assign(Object.assign({}, contextToFreeze), { logger: context.logger, set: context.set, remove: context.remove, status: context.status, setCookie: context.setCookie, clearCookie: context.clearCookie, sse: context.sse });
|
|
136
229
|
ctx.state.event = event;
|
|
137
230
|
ctx.state.contextInjected = frozenContext;
|
|
138
231
|
let result;
|
|
@@ -142,7 +235,7 @@ const wrapEventFunction = (execute, projectConfig) => {
|
|
|
142
235
|
catch (e) {
|
|
143
236
|
ctx.state.error = e;
|
|
144
237
|
if (hasSwitchSSEMode) {
|
|
145
|
-
const sse = (
|
|
238
|
+
const sse = (_e = ctx.sse) === null || _e === void 0 ? void 0 : _e.call(ctx);
|
|
146
239
|
if (sse && !sse.isClosed) {
|
|
147
240
|
const errorEvent = {
|
|
148
241
|
event: "error",
|
package/lib/middlewares/index.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.apmMiddleware = exports.openGwRequestMiddleware = exports.logsQueryMiddleware = exports.loggerMiddleware = exports.functionRouteMiddleware = exports.eventIdMiddleware = exports.contextInjectionMiddleware = exports.asyncContextMiddleware = void 0;
|
|
3
|
+
exports.apmMiddleware = exports.openGwRequestMiddleware = exports.logsQueryMiddleware = exports.loggerMiddleware = exports.functionRouteMiddleware = exports.eventIdMiddleware = exports.envVarsInjectionMiddleware = exports.contextInjectionMiddleware = exports.asyncContextMiddleware = void 0;
|
|
4
4
|
var middle_async_context_1 = require("./middle-async-context");
|
|
5
5
|
Object.defineProperty(exports, "asyncContextMiddleware", { enumerable: true, get: function () { return middle_async_context_1.asyncContextMiddleware; } });
|
|
6
6
|
var middle_context_injection_1 = require("./middle-context-injection");
|
|
7
7
|
Object.defineProperty(exports, "contextInjectionMiddleware", { enumerable: true, get: function () { return middle_context_injection_1.contextInjectionMiddleware; } });
|
|
8
|
+
var middle_env_vars_injection_1 = require("./middle-env-vars-injection");
|
|
9
|
+
Object.defineProperty(exports, "envVarsInjectionMiddleware", { enumerable: true, get: function () { return middle_env_vars_injection_1.envVarsInjectionMiddleware; } });
|
|
8
10
|
var middle_event_id_1 = require("./middle-event-id");
|
|
9
11
|
Object.defineProperty(exports, "eventIdMiddleware", { enumerable: true, get: function () { return middle_event_id_1.eventIdMiddleware; } });
|
|
10
12
|
var middle_function_route_1 = require("./middle-function-route");
|
|
@@ -44,6 +44,7 @@ function loggerMiddleware() {
|
|
|
44
44
|
finally {
|
|
45
45
|
const endTime = Date.now();
|
|
46
46
|
const duration = endTime - startTime;
|
|
47
|
+
const contentType = ctx.get("content-type") || "-";
|
|
47
48
|
const accessLog = {
|
|
48
49
|
type: "HTTP_ACCESS",
|
|
49
50
|
method: ctx.method,
|
|
@@ -65,7 +66,7 @@ function loggerMiddleware() {
|
|
|
65
66
|
asyncContext.userCodeLogsFlushTimer = undefined;
|
|
66
67
|
}
|
|
67
68
|
logger_1.functionsLogger.logUserCodelog();
|
|
68
|
-
process.stdout.write(chalk_1.default.greenBright(`▶️ ${chalk_1.default.gray(`[${new Date().toLocaleString()} - ${eventId.slice(0, -10)}]`)} - ${duration}ms\n`));
|
|
69
|
+
process.stdout.write(chalk_1.default.greenBright(`▶️ ${chalk_1.default.gray(`[${new Date().toLocaleString()} - ${eventId.slice(0, -10)}]`)} ${chalk_1.default.bold(`${ctx.method} ${ctx.path}`)} ${chalk_1.default.bold(`[${contentType}]`)} - ${duration}ms\n`));
|
|
69
70
|
process.stdout.write(chalk_1.default.greenBright(`\n`));
|
|
70
71
|
}
|
|
71
72
|
});
|
|
@@ -0,0 +1,65 @@
|
|
|
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.envVarsInjectionMiddleware = envVarsInjectionMiddleware;
|
|
13
|
+
function envVarsInjectionMiddleware() {
|
|
14
|
+
return (ctx, next) => __awaiter(this, void 0, void 0, function* () {
|
|
15
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
16
|
+
const envVarsHeader = (_b = (_a = ctx.request) === null || _a === void 0 ? void 0 : _a.headers) === null || _b === void 0 ? void 0 : _b["x-ide-env-vars"];
|
|
17
|
+
if (envVarsHeader && typeof envVarsHeader === "string") {
|
|
18
|
+
try {
|
|
19
|
+
const envVars = JSON.parse(envVarsHeader);
|
|
20
|
+
if (envVars && typeof envVars === "object") {
|
|
21
|
+
for (const [key, value] of Object.entries(envVars)) {
|
|
22
|
+
if (key && value !== undefined && value !== null) {
|
|
23
|
+
process.env[String(key)] = String(value);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
catch (e) {
|
|
29
|
+
console.warn("[envVarsInjection] Failed to parse env vars from header:", e);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const systemHeadersMapping = {
|
|
33
|
+
"xhs-cloud-env-id": "XHS_CLOUD_ENV_ID",
|
|
34
|
+
"xhs-cloud-function-id": "XHS_CLOUD_FUNCTION_ID",
|
|
35
|
+
"xhs-app-id": "XHS_APP_ID",
|
|
36
|
+
};
|
|
37
|
+
for (const [headerKey, envKey] of Object.entries(systemHeadersMapping)) {
|
|
38
|
+
const headerValue = (_d = (_c = ctx.request) === null || _c === void 0 ? void 0 : _c.headers) === null || _d === void 0 ? void 0 : _d[headerKey];
|
|
39
|
+
if (headerValue && typeof headerValue === "string") {
|
|
40
|
+
process.env[envKey] = headerValue;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const versionMapHeader = (_f = (_e = ctx.request) === null || _e === void 0 ? void 0 : _e.headers) === null || _f === void 0 ? void 0 : _f["xhs-cloud-env-version-map"];
|
|
44
|
+
const aliyunVersionHeader = (_h = (_g = ctx.request) === null || _g === void 0 ? void 0 : _g.headers) === null || _h === void 0 ? void 0 : _h["x-fc-version-id"];
|
|
45
|
+
if (versionMapHeader &&
|
|
46
|
+
typeof versionMapHeader === "string" &&
|
|
47
|
+
aliyunVersionHeader &&
|
|
48
|
+
typeof aliyunVersionHeader === "string") {
|
|
49
|
+
try {
|
|
50
|
+
const parsed = JSON.parse(versionMapHeader);
|
|
51
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
52
|
+
const map = parsed;
|
|
53
|
+
const mapped = map[aliyunVersionHeader];
|
|
54
|
+
if (mapped !== undefined && mapped !== null) {
|
|
55
|
+
process.env.XHS_CLOUD_ENV_VERSION = String(mapped);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
catch (e) {
|
|
60
|
+
console.warn("[envVarsInjection] Failed to parse xhs-cloud-env-version-map:", e);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
yield next();
|
|
64
|
+
});
|
|
65
|
+
}
|
package/lib/router.js
CHANGED
|
@@ -10,6 +10,9 @@ class Router {
|
|
|
10
10
|
this.router = (0, radix3_1.createRouter)({
|
|
11
11
|
strictTrailingSlash: false,
|
|
12
12
|
});
|
|
13
|
+
if (!(config && Array.isArray(config.routes))) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
13
16
|
for (const route of config.routes) {
|
|
14
17
|
if (route.path) {
|
|
15
18
|
const p = route.path.endsWith("/") ? route.path : route.path + "/";
|
|
@@ -40,6 +43,9 @@ class Router {
|
|
|
40
43
|
}
|
|
41
44
|
exports.Router = Router;
|
|
42
45
|
function routeFunction(router, path) {
|
|
46
|
+
if (!router) {
|
|
47
|
+
return {};
|
|
48
|
+
}
|
|
43
49
|
const matchedRoute = router.findRoute(path);
|
|
44
50
|
if (matchedRoute) {
|
|
45
51
|
return {
|
package/lib/server.js
CHANGED
|
@@ -37,6 +37,7 @@ function createServer(options, router, projectConfig) {
|
|
|
37
37
|
app.use(corsMiddleware());
|
|
38
38
|
app.use((0, middlewares_1.logsQueryMiddleware)());
|
|
39
39
|
app.use((0, middlewares_1.contextInjectionMiddleware)(options, projectConfig));
|
|
40
|
+
app.use((0, middlewares_1.envVarsInjectionMiddleware)());
|
|
40
41
|
app.use((0, middlewares_1.eventIdMiddleware)());
|
|
41
42
|
app.use((0, middlewares_1.apmMiddleware)());
|
|
42
43
|
app.use((0, middlewares_1.asyncContextMiddleware)());
|
|
@@ -6,8 +6,10 @@ const secretKey = process.env.LANGFUSE_SECRET_KEY;
|
|
|
6
6
|
const publicKey = process.env.LANGFUSE_PUBLIC_KEY;
|
|
7
7
|
const baseUrl = process.env.LANGFUSE_BASEURL;
|
|
8
8
|
exports.langfuse = new langfuse_1.Langfuse({
|
|
9
|
-
secretKey
|
|
10
|
-
publicKey
|
|
11
|
-
baseUrl
|
|
9
|
+
secretKey,
|
|
10
|
+
publicKey,
|
|
11
|
+
baseUrl,
|
|
12
|
+
environment: process.env.OPEN_PLATFORM_STAGE || "development",
|
|
13
|
+
enabled: Boolean(secretKey && publicKey && baseUrl),
|
|
12
14
|
});
|
|
13
15
|
exports.default = exports.langfuse;
|
package/lib/utils/apm.config.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.defaultConfig = exports.APM_MEASUREMENT_NAME = void 0;
|
|
4
|
+
const endpoints_1 = require("@vectorx/endpoints");
|
|
4
5
|
exports.APM_MEASUREMENT_NAME = "vectorx_kit_agent_runtime";
|
|
5
6
|
exports.defaultConfig = {
|
|
6
|
-
endpoint: "
|
|
7
|
+
endpoint: (0, endpoints_1.resolveUrl)("@vectorx/functions-framework", "apmDataEndpoint"),
|
|
7
8
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vectorx/functions-framework",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "VectorX Functions Framework",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "types/index.d.ts",
|
|
@@ -25,7 +25,8 @@
|
|
|
25
25
|
"node": ">=18.0.0"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@vectorx/ai-types": "0.
|
|
28
|
+
"@vectorx/ai-types": "1.0.0",
|
|
29
|
+
"@vectorx/endpoints": "1.0.0",
|
|
29
30
|
"async_hooks": "^1.0.0",
|
|
30
31
|
"chalk": "4",
|
|
31
32
|
"commander": "^12.1.0",
|
|
@@ -72,5 +73,5 @@
|
|
|
72
73
|
"lint": "eslint src --ext .ts",
|
|
73
74
|
"run:demo": "node ./bin/rcb-ff.js"
|
|
74
75
|
},
|
|
75
|
-
"readme": "# VectorX Functions Framework\n\nVectorX Functions Framework 是一个用于构建和运行云函数的框架,提供了完整的日志系统支持。\n\n## 日志系统\n\n框架内置了完整的日志系统,支持请求日志记录和用户代码日志记录。\n\n### 配置\n\n在初始化框架时,可以配置日志系统:\n\n```typescript\nimport { createAgentServerFramework } from '@vectorx/functions-framework';\n\nconst framework = createAgentServerFramework({\n port: 3000,\n logging: {\n dirname: '/path/to/logs', // 日志存储目录\n maxSize: '20m', // 单个日志文件最大大小\n maxFiles: 14 // 保留的日志文件数量\n }\n});\n```\n\n### 日志类型\n\n框架支持两种类型的日志:\n\n- `ACCESS`: 访问日志,记录所有 HTTP 请求\n- `USERCODE`: 用户代码日志,记录用户函数中的日志\n\n### 日志查询 API\n\n框架提供了日志查询 API,用于查看和调试日志:\n\n```\nGET /@logs\n```\n\n#### 查询参数\n\n| 参数名 | 类型 | 必填 | 说明 |\n|--------|------|------|------|\n| type | string | 否 | 日志类型,可选值:ACCESS/USERCODE,默认 ACCESS |\n| limit | number | 否 | 返回的日志条数限制,默认 100 |\n| eventId | string | 否 | 按 eventId 过滤日志 |\n\n#### 示例\n\n1. 查询所有访问日志:\n```bash\ncurl 'http://localhost:3000/@logs?type=ACCESS'\n```\n\n2. 查询特定 eventId 的日志:\n```bash\ncurl 'http://localhost:3000/@logs?eventId=550e8400-e29b-41d4-a716-446655440000'\n```\n\n3. 限制返回条数:\n```bash\ncurl 'http://localhost:3000/@logs?limit=10'\n```\n\n4. 组合查询:\n```bash\ncurl 'http://localhost:3000/@logs?type=USERCODE&eventId=550e8400-e29b-41d4-a716-446655440000&limit=20'\n```\n\n#### 响应格式\n\n成功响应:\n```json\n{\n \"success\": true,\n \"data\": [\n {\n \"@timestamp\": \"2024-03-21T10:30:00.000Z\",\n \"level\": \"info\",\n \"eventId\": \"550e8400-e29b-41d4-a716-446655440000\",\n \"message\": \"Request started\",\n \"method\": \"GET\",\n \"url\": \"/api/example\"\n }\n ]\n}\n```\n\n错误响应:\n```json\n{\n \"success\": false,\n \"error\": \"错误信息\"\n}\n```\n\n### 在代码中使用日志\n\n```typescript\nimport { functionsLogger } from '@vectorx/functions-framework';\nimport { LogLevel } from '@vectorx/functions-framework';\n\n// 记录访问日志\nfunctionsLogger.logAccesslog(LogLevel.INFO, 'Custom message', {\n customField: 'value'\n});\n\n// 记录用户代码日志\nfunctionsLogger.logUserCodelog([\n { message: 'User log message', level: 'info' }\n]);\n```\n\n### 日志格式\n\n每条日志都包含以下字段:\n\n- `@timestamp`: 日志时间戳\n- `level`: 日志级别\n- `eventId`: 请求 ID\n- `message`: 日志消息\n- 其他自定义字段\n\n## 开发\n\n### 运行测试\n\n```bash\nnpm test\n```\n\n### 构建\n\n```bash\nnpm run build\n``` \n\n
|
|
76
|
+
"readme": "# VectorX Functions Framework\n\nVectorX Functions Framework 是一个用于构建和运行云函数的框架,提供了完整的日志系统支持。\n\n## 日志系统\n\n框架内置了完整的日志系统,支持请求日志记录和用户代码日志记录。\n\n### 配置\n\n在初始化框架时,可以配置日志系统:\n\n```typescript\nimport { createAgentServerFramework } from '@vectorx/functions-framework';\n\nconst framework = createAgentServerFramework({\n port: 3000,\n logging: {\n dirname: '/path/to/logs', // 日志存储目录\n maxSize: '20m', // 单个日志文件最大大小\n maxFiles: 14 // 保留的日志文件数量\n }\n});\n```\n\n### 日志类型\n\n框架支持两种类型的日志:\n\n- `ACCESS`: 访问日志,记录所有 HTTP 请求\n- `USERCODE`: 用户代码日志,记录用户函数中的日志\n\n### 日志查询 API\n\n框架提供了日志查询 API,用于查看和调试日志:\n\n```\nGET /@logs\n```\n\n#### 查询参数\n\n| 参数名 | 类型 | 必填 | 说明 |\n|--------|------|------|------|\n| type | string | 否 | 日志类型,可选值:ACCESS/USERCODE,默认 ACCESS |\n| limit | number | 否 | 返回的日志条数限制,默认 100 |\n| eventId | string | 否 | 按 eventId 过滤日志 |\n\n#### 示例\n\n1. 查询所有访问日志:\n```bash\ncurl 'http://localhost:3000/@logs?type=ACCESS'\n```\n\n2. 查询特定 eventId 的日志:\n```bash\ncurl 'http://localhost:3000/@logs?eventId=550e8400-e29b-41d4-a716-446655440000'\n```\n\n3. 限制返回条数:\n```bash\ncurl 'http://localhost:3000/@logs?limit=10'\n```\n\n4. 组合查询:\n```bash\ncurl 'http://localhost:3000/@logs?type=USERCODE&eventId=550e8400-e29b-41d4-a716-446655440000&limit=20'\n```\n\n#### 响应格式\n\n成功响应:\n```json\n{\n \"success\": true,\n \"data\": [\n {\n \"@timestamp\": \"2024-03-21T10:30:00.000Z\",\n \"level\": \"info\",\n \"eventId\": \"550e8400-e29b-41d4-a716-446655440000\",\n \"message\": \"Request started\",\n \"method\": \"GET\",\n \"url\": \"/api/example\"\n }\n ]\n}\n```\n\n错误响应:\n```json\n{\n \"success\": false,\n \"error\": \"错误信息\"\n}\n```\n\n### 在代码中使用日志\n\n```typescript\nimport { functionsLogger } from '@vectorx/functions-framework';\nimport { LogLevel } from '@vectorx/functions-framework';\n\n// 记录访问日志\nfunctionsLogger.logAccesslog(LogLevel.INFO, 'Custom message', {\n customField: 'value'\n});\n\n// 记录用户代码日志\nfunctionsLogger.logUserCodelog([\n { message: 'User log message', level: 'info' }\n]);\n```\n\n### 日志格式\n\n每条日志都包含以下字段:\n\n- `@timestamp`: 日志时间戳\n- `level`: 日志级别\n- `eventId`: 请求 ID\n- `message`: 日志消息\n- 其他自定义字段\n\n## 开发\n\n### 运行测试\n\n```bash\nnpm test\n```\n\n### 构建\n\n```bash\nnpm run build\n``` \n\n### 环境管理\n读取 .env 文件,校验 env 文件的可用性,获取当前运行环境\n\n\n## rcb-ff 自动检测项目类型\n\n当直接使用 `rcb-ff` 命令启动服务时,如果未指定 `--mode` 参数,会自动检测项目类型:\n\n### 检测规则\n\n1. **Agent 项目检测**:\n - 如果存在 `project.config.json` 文件且包含 `agentId` 字段,则判定为 `agent` 项目\n\n2. **Fun 项目检测**:\n - 如果不存在 `project.config.json` 或 `project.config.json` 中不存在 `agentId` 字段\n - 且存在 `agent-cloudbase-functions.json` 配置文件\n - 则判定为 `fun` 项目\n\n3. **默认行为**:\n - 如果无法确定项目类型,默认使用 `agent` 模式(保持向后兼容)\n\n### 使用示例\n\n```bash\n# 自动检测项目类型(推荐)\nrcb-ff --directory ./my-project\n\n# 明确指定模式(覆盖自动检测)\nrcb-ff --directory ./my-project --mode fun\nrcb-ff --directory ./my-project --mode agent\n```\n"
|
|
76
77
|
}
|
package/types/framework.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
declare function getDependencyVersion(packageName: string, searchPath: string): string | null;
|
|
2
|
+
export type FrameworkMode = "agent" | "fun";
|
|
2
3
|
export interface FrameworkOptions {
|
|
3
4
|
port: number;
|
|
4
5
|
timeoutMS: number;
|
|
@@ -18,6 +19,7 @@ export interface FrameworkOptions {
|
|
|
18
19
|
redLangfuseConfig?: {
|
|
19
20
|
enable?: boolean;
|
|
20
21
|
};
|
|
22
|
+
mode?: FrameworkMode;
|
|
21
23
|
}
|
|
22
24
|
export type FunctionName = string;
|
|
23
25
|
declare class AgentServerFramework {
|
|
@@ -27,6 +29,9 @@ declare class AgentServerFramework {
|
|
|
27
29
|
constructor(options: FrameworkOptions);
|
|
28
30
|
startServer(): Promise<import("http").Server<typeof import("http").IncomingMessage, typeof import("http").ServerResponse>>;
|
|
29
31
|
getRegisteredFunction(fnName: string): Function;
|
|
32
|
+
private loadAgentModeConfigs;
|
|
33
|
+
private loadFunModeConfigs;
|
|
34
|
+
private loadAndRegisterFunctions;
|
|
30
35
|
}
|
|
31
36
|
export { getDependencyVersion };
|
|
32
37
|
export declare function createAgentServerFramework(options: FrameworkOptions): AgentServerFramework;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { asyncContextMiddleware } from "./middle-async-context";
|
|
2
2
|
export { contextInjectionMiddleware } from "./middle-context-injection";
|
|
3
|
+
export { envVarsInjectionMiddleware } from "./middle-env-vars-injection";
|
|
3
4
|
export { eventIdMiddleware } from "./middle-event-id";
|
|
4
5
|
export { functionRouteMiddleware } from "./middle-function-route";
|
|
5
6
|
export { loggerMiddleware } from "./middle-common-logger";
|
package/types/router.d.ts
CHANGED
|
@@ -2,14 +2,14 @@ import type { CloudFunctionConfig, RouteConfig } from "./function-loader";
|
|
|
2
2
|
export declare class Router {
|
|
3
3
|
private routes;
|
|
4
4
|
private router;
|
|
5
|
-
constructor(config
|
|
5
|
+
constructor(config?: CloudFunctionConfig);
|
|
6
6
|
private addRoute;
|
|
7
7
|
findRoute(path: string): RouteConfig | null;
|
|
8
8
|
}
|
|
9
9
|
export declare function routeFunction(router: Router | undefined, path: string): {
|
|
10
|
-
routeDefinition: RouteConfig;
|
|
11
|
-
handleFunction: Function;
|
|
12
|
-
} | {
|
|
13
10
|
routeDefinition?: undefined;
|
|
14
11
|
handleFunction?: undefined;
|
|
12
|
+
} | {
|
|
13
|
+
routeDefinition: RouteConfig;
|
|
14
|
+
handleFunction: Function;
|
|
15
15
|
};
|
package/types/server.d.ts
CHANGED
|
@@ -24,10 +24,22 @@ export interface RcbContext {
|
|
|
24
24
|
eventID: string;
|
|
25
25
|
eventType: string;
|
|
26
26
|
timestamp: number;
|
|
27
|
+
headers: Record<string, any>;
|
|
28
|
+
httpMethod: string;
|
|
29
|
+
path: string;
|
|
30
|
+
url: string;
|
|
31
|
+
query: Record<string, any>;
|
|
32
|
+
cookies: Record<string, string>;
|
|
33
|
+
trigger: "online" | "ide_debug" | "console_debug";
|
|
27
34
|
httpContext: any;
|
|
28
35
|
request: Request;
|
|
29
36
|
logger: typeof functionsLogger;
|
|
30
37
|
baseUrl: string;
|
|
38
|
+
set: (field: string, value: any) => void;
|
|
39
|
+
remove: (field: string) => void;
|
|
40
|
+
status: (code: number) => void;
|
|
41
|
+
setCookie: (name: string, value: string, options?: any) => void;
|
|
42
|
+
clearCookie: (name: string, options?: any) => void;
|
|
31
43
|
sse: () => any;
|
|
32
44
|
}
|
|
33
45
|
export declare function createServer(options: FrameworkOptions, router: Router, projectConfig: ProjectConfig): http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>;
|