@vectorx/functions-framework 0.0.0-beta-20260109055258 → 0.0.0-beta-20260320063305

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.
@@ -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
+ }
@@ -12,9 +12,16 @@ Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.openGwRequestMiddleware = openGwRequestMiddleware;
13
13
  const error_1 = require("../error");
14
14
  const unified_responder_1 = require("../unified-responder");
15
- function openGwRequestMiddleware(agentId) {
15
+ function openGwRequestMiddleware(agentId, options) {
16
16
  return (ctx, next) => __awaiter(this, void 0, void 0, function* () {
17
+ if ((options === null || options === void 0 ? void 0 : options.mode) === "fun") {
18
+ yield next();
19
+ return;
20
+ }
17
21
  if (process.env.OPEN_PLATFORM_STAGE === "production") {
22
+ if (!agentId) {
23
+ return (0, unified_responder_1.sendResponse)(ctx, undefined, (0, error_1.newSysErr)("agentId is required in agent mode"), 500);
24
+ }
18
25
  const openId = ctx.headers["open-id"];
19
26
  if (!openId) {
20
27
  return (0, unified_responder_1.sendResponse)(ctx, undefined, (0, error_1.newBadRequestErr)("open-id is required"), 401);
package/lib/request.js CHANGED
@@ -78,7 +78,7 @@ class Request {
78
78
  }
79
79
  request(options_1) {
80
80
  return __awaiter(this, arguments, void 0, function* (options, enableAbort = false) {
81
- const { url, headers: _headers = {}, data, responseType, withCredentials, body, method: _method } = options, headers = Object.assign(Object.assign({}, this.defaultHeaders), (0, helper_1.obj2StrRecord)(_headers)), method = String(_method).toLowerCase() || "get";
81
+ const { url, headers: _headers = {}, data, responseType, withCredentials, body, method: _method, } = options, headers = Object.assign(Object.assign({}, this.defaultHeaders), (0, helper_1.obj2StrRecord)(_headers)), method = String(_method).toLowerCase() || "get";
82
82
  let queryParams = {};
83
83
  if (method === "get" || method === "head") {
84
84
  queryParams = Object.assign(Object.assign({}, (data || {})), (body && typeof body === "object" ? body : {}));
@@ -151,7 +151,11 @@ class Request {
151
151
  return Promise.reject(x);
152
152
  });
153
153
  const ret = {
154
- data: stream ? (res.body ? stream_1.Readable.toWeb(res.body) : res.body) : yield res.json(),
154
+ data: stream
155
+ ? res.body
156
+ ? stream_1.Readable.toWeb(res.body)
157
+ : res.body
158
+ : yield res.json(),
155
159
  statusCode: res.status,
156
160
  header: res.headers,
157
161
  };
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
@@ -17,8 +17,10 @@ const http_1 = __importDefault(require("http"));
17
17
  const koa_1 = __importDefault(require("koa"));
18
18
  const koa_body_1 = require("koa-body");
19
19
  const error_1 = require("./error");
20
+ const function_registry_1 = require("./function-registry");
20
21
  const middlewares_1 = require("./middlewares");
21
22
  const unified_responder_1 = require("./unified-responder");
23
+ const wss_1 = require("./wss");
22
24
  function corsMiddleware() {
23
25
  return (ctx, next) => __awaiter(this, void 0, void 0, function* () {
24
26
  ctx.set("Access-Control-Allow-Origin", "*");
@@ -35,8 +37,22 @@ function corsMiddleware() {
35
37
  function createServer(options, router, projectConfig) {
36
38
  const app = new koa_1.default();
37
39
  app.use(corsMiddleware());
40
+ app.use((ctx, next) => __awaiter(this, void 0, void 0, function* () {
41
+ if (ctx.path === "/@meta") {
42
+ const functions = (0, function_registry_1.getAllRegisteredFunctions)().map((fn) => ({
43
+ name: fn.name,
44
+ features: {
45
+ websocket: typeof fn.userFunction.handleUpgrade === "function",
46
+ },
47
+ }));
48
+ ctx.body = { functions };
49
+ return;
50
+ }
51
+ yield next();
52
+ }));
38
53
  app.use((0, middlewares_1.logsQueryMiddleware)());
39
54
  app.use((0, middlewares_1.contextInjectionMiddleware)(options, projectConfig));
55
+ app.use((0, middlewares_1.envVarsInjectionMiddleware)());
40
56
  app.use((0, middlewares_1.eventIdMiddleware)());
41
57
  app.use((0, middlewares_1.apmMiddleware)());
42
58
  app.use((0, middlewares_1.asyncContextMiddleware)());
@@ -72,8 +88,10 @@ function createServer(options, router, projectConfig) {
72
88
  return (0, unified_responder_1.sendResponse)(ctx, undefined, (0, error_1.newBadRequestErr)(err), 400);
73
89
  },
74
90
  }));
75
- app.use((0, middlewares_1.openGwRequestMiddleware)(projectConfig.agentId));
91
+ const openGwAgentId = options.mode === "fun" ? undefined : projectConfig.agentId;
92
+ app.use((0, middlewares_1.openGwRequestMiddleware)(openGwAgentId, { mode: options.mode }));
76
93
  app.use((0, middlewares_1.functionRouteMiddleware)(router, projectConfig));
77
94
  const httpServer = http_1.default.createServer(app.callback());
95
+ (0, wss_1.createWebSocketServer)(httpServer, app, router, options, projectConfig);
78
96
  return httpServer;
79
97
  }
package/lib/sse.js CHANGED
@@ -90,7 +90,9 @@ class ServerSentEvent extends events_1.EventEmitter {
90
90
  const openId = ctx.headers["open-id"] || undefined;
91
91
  const eventID = (_d = ctx.state) === null || _d === void 0 ? void 0 : _d.eventID;
92
92
  const agentInfo = ((_e = ctx.state) === null || _e === void 0 ? void 0 : _e.agentInfo) || {};
93
- const name = [(agentInfo === null || agentInfo === void 0 ? void 0 : agentInfo.agentName) || (agentInfo === null || agentInfo === void 0 ? void 0 : agentInfo.name), agentInfo === null || agentInfo === void 0 ? void 0 : agentInfo.version].filter(Boolean).join("@");
93
+ const name = [(agentInfo === null || agentInfo === void 0 ? void 0 : agentInfo.agentName) || (agentInfo === null || agentInfo === void 0 ? void 0 : agentInfo.name), agentInfo === null || agentInfo === void 0 ? void 0 : agentInfo.version]
94
+ .filter(Boolean)
95
+ .join("@");
94
96
  const metadata = {
95
97
  http: { method: ctx.method, path: ctx.path },
96
98
  eventID,
@@ -108,7 +110,11 @@ class ServerSentEvent extends events_1.EventEmitter {
108
110
  });
109
111
  if (trace) {
110
112
  ctx.state.langfuseTrace = trace;
111
- (_g = trace.event) === null || _g === void 0 ? void 0 : _g.call(trace, { name: "sse:start", input: (_j = (_h = ctx.state) === null || _h === void 0 ? void 0 : _h.event) !== null && _j !== void 0 ? _j : null, metadata: { path: ctx.path } });
113
+ (_g = trace.event) === null || _g === void 0 ? void 0 : _g.call(trace, {
114
+ name: "sse:start",
115
+ input: (_j = (_h = ctx.state) === null || _h === void 0 ? void 0 : _h.event) !== null && _j !== void 0 ? _j : null,
116
+ metadata: { path: ctx.path },
117
+ });
112
118
  }
113
119
  }
114
120
  catch (_k) { }
@@ -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: "sk-lf-f389bb1e-24db-4b42-b506-8d6118f45044",
10
- publicKey: "pk-lf-ee301553-7a64-4f0f-bf6b-a2ac1e5ca2df",
11
- baseUrl: "https://xray-langfuse.devops.xiaohongshu.com",
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;
@@ -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: "https://apm-fe.xiaohongshu.com/api/data",
7
+ endpoint: (0, endpoints_1.resolveUrl)("@vectorx/functions-framework", "apmDataEndpoint"),
7
8
  };
@@ -43,11 +43,10 @@ class Interceptor {
43
43
  anotherConsole[method] = (message, ...optionalParams) => {
44
44
  if ((0, async_context_1.isInFrameworkAsyncContext)()) {
45
45
  callAddUserCodeLog(method, message, ...optionalParams);
46
- process.stdout.write((0, util_1.formatWithOptions)({ colors: false }, message, ...optionalParams));
46
+ process.stdout.write(`${(0, util_1.formatWithOptions)({ colors: false }, message, ...optionalParams)}\n`);
47
47
  }
48
48
  else {
49
49
  originalConsole[method](message, ...optionalParams);
50
- process.stdout.write((0, util_1.formatWithOptions)({ colors: false }, message, ...optionalParams));
51
50
  }
52
51
  };
53
52
  }
@@ -13,13 +13,13 @@ exports.getDependencyVersion = getDependencyVersion;
13
13
  const fs_1 = __importDefault(require("fs"));
14
14
  const path_1 = __importDefault(require("path"));
15
15
  function deepFreeze(obj) {
16
- if (obj === null || typeof obj !== "object") {
16
+ if (!isPlainObject(obj)) {
17
17
  return obj;
18
18
  }
19
- Object.keys(obj).forEach((prop) => {
20
- const value = obj[prop];
21
- if (typeof value === "object" && value !== null) {
22
- deepFreeze(value);
19
+ Object.getOwnPropertyNames(obj).forEach((name) => {
20
+ const prop = obj[name];
21
+ if (typeof prop === "object" && prop !== null && !Object.isFrozen(prop)) {
22
+ deepFreeze(prop);
23
23
  }
24
24
  });
25
25
  return Object.freeze(obj);
@@ -69,7 +69,9 @@ function formatUrl(PROTOCOL, url, query) {
69
69
  }
70
70
  function getDependencyVersion(packageName, searchPath) {
71
71
  try {
72
- const packagePath = require.resolve(packageName, { paths: [searchPath] });
72
+ const packagePath = require.resolve(packageName, {
73
+ paths: [searchPath],
74
+ });
73
75
  let currentPath = path_1.default.dirname(packagePath);
74
76
  let packageJsonPath = path_1.default.join(currentPath, "package.json");
75
77
  while (!fs_1.default.existsSync(packageJsonPath) && currentPath !== path_1.default.parse(currentPath).root) {
package/lib/wss.js ADDED
@@ -0,0 +1,305 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
36
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
37
+ return new (P || (P = Promise))(function (resolve, reject) {
38
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
39
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
40
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
41
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
42
+ });
43
+ };
44
+ var __importDefault = (this && this.__importDefault) || function (mod) {
45
+ return (mod && mod.__esModule) ? mod : { "default": mod };
46
+ };
47
+ Object.defineProperty(exports, "__esModule", { value: true });
48
+ exports.VectorxWebSocket = void 0;
49
+ exports.createWebSocketServer = createWebSocketServer;
50
+ const events = __importStar(require("events"));
51
+ const http = __importStar(require("http"));
52
+ const koa_compose_1 = __importDefault(require("koa-compose"));
53
+ const ws_1 = require("ws");
54
+ const error_1 = require("./error");
55
+ const function_registry_1 = require("./function-registry");
56
+ const logger_1 = require("./logger");
57
+ const middle_async_context_1 = require("./middlewares/middle-async-context");
58
+ const middle_common_logger_1 = require("./middlewares/middle-common-logger");
59
+ const middle_context_injection_1 = require("./middlewares/middle-context-injection");
60
+ const middle_event_id_1 = require("./middlewares/middle-event-id");
61
+ const router_1 = require("./router");
62
+ const helper_1 = require("./utils/helper");
63
+ const WS_ERROR_CODE = {
64
+ SERVICE_RESTART: 1012,
65
+ };
66
+ function respondError(socket, e) {
67
+ respond(socket, 500, `Internal Server Error: ${e && e.message ? e.message : e}`);
68
+ }
69
+ function respond(socket, statusCode, body, type = "text/plain") {
70
+ const statusMessage = http.STATUS_CODES[statusCode];
71
+ const head = [
72
+ `HTTP/1.1 ${statusCode} ${statusMessage}`,
73
+ `Content-Type: ${type}`,
74
+ `Content-Length: ${Buffer.byteLength(Buffer.from(body))}`,
75
+ "Content-Encoding: utf-8",
76
+ "Connection: close",
77
+ ].join("\r\n");
78
+ socket.write(head);
79
+ socket.write("\r\n\r\n");
80
+ socket.end(body);
81
+ }
82
+ function setupWebSocketsHeartbeat(wss, interval = 10000) {
83
+ const SYM_WS_IS_ALIVE = Symbol("WS_IS_ALIVE");
84
+ let checking = false;
85
+ let wsNotAliveCount = 0;
86
+ function doCheck() {
87
+ if (checking)
88
+ return;
89
+ checking = true;
90
+ wsNotAliveCount = 0;
91
+ wss.clients.forEach((ws) => {
92
+ if (Reflect.has(ws, SYM_WS_IS_ALIVE) === false) {
93
+ ws[SYM_WS_IS_ALIVE] = true;
94
+ ws.on("pong", function onPong() {
95
+ this[SYM_WS_IS_ALIVE] = true;
96
+ });
97
+ }
98
+ if (ws.readyState !== ws.OPEN)
99
+ return;
100
+ if (ws[SYM_WS_IS_ALIVE] === false) {
101
+ wsNotAliveCount += 1;
102
+ ws.close(1001, "websocket client not alive");
103
+ return ws.terminate();
104
+ }
105
+ ws[SYM_WS_IS_ALIVE] = false;
106
+ ws.ping();
107
+ });
108
+ logger_1.functionsLogger.logError(`wss heartbeat finish, ws not alive count: ${wsNotAliveCount}, total: ${wss.clients.size}`);
109
+ checking = false;
110
+ }
111
+ const aliveCheckTimer = setInterval(doCheck, interval);
112
+ wss.once("close", () => {
113
+ clearInterval(aliveCheckTimer);
114
+ });
115
+ }
116
+ function setupGracefulShutdown(wss) {
117
+ for (const signal of ["SIGINT", "SIGTERM"]) {
118
+ process.on(signal, () => __awaiter(this, void 0, void 0, function* () {
119
+ logger_1.functionsLogger.logError(`received ${signal} signal, shutting down websocket server, ${wss.clients.size} ws clients need to be closed`);
120
+ wss.close();
121
+ wss.clients.forEach((ws) => {
122
+ ws.close(WS_ERROR_CODE.SERVICE_RESTART, "websocket server is shutting down, please try to reconnect");
123
+ });
124
+ }));
125
+ }
126
+ }
127
+ class VectorxWebSocket extends events.EventEmitter {
128
+ constructor(ws, eventCtx) {
129
+ super();
130
+ this.ws = ws;
131
+ this.ws.onopen = (openEvent) => {
132
+ this.emit("open", {
133
+ ctx: eventCtx,
134
+ type: openEvent.type,
135
+ });
136
+ };
137
+ this.ws.onerror = (errorEvent) => {
138
+ this.emit("error", {
139
+ ctx: eventCtx,
140
+ type: errorEvent.type,
141
+ message: errorEvent.message,
142
+ error: errorEvent.error,
143
+ });
144
+ };
145
+ this.ws.onclose = (closeEvent) => {
146
+ this.emit("close", {
147
+ ctx: eventCtx,
148
+ type: closeEvent.type,
149
+ code: closeEvent.code,
150
+ reason: closeEvent.reason,
151
+ wasClean: closeEvent.wasClean,
152
+ });
153
+ };
154
+ this.ws.onmessage = (messageEvent) => {
155
+ this.emit("message", {
156
+ ctx: eventCtx,
157
+ type: messageEvent.type,
158
+ data: messageEvent.data,
159
+ });
160
+ };
161
+ Object.defineProperty(this, "ws", {
162
+ configurable: false,
163
+ enumerable: false,
164
+ writable: false,
165
+ });
166
+ }
167
+ send(buf) {
168
+ this.ws.send(buf);
169
+ }
170
+ close(code, reason) {
171
+ this.ws.close(code, reason);
172
+ }
173
+ }
174
+ exports.VectorxWebSocket = VectorxWebSocket;
175
+ function createWebSocketServer(httpServer, koaApp, router, options, projectConfig) {
176
+ const hasExportHandleUpgradeFunctions = [];
177
+ (0, function_registry_1.getAllRegisteredFunctions)().forEach((fn) => {
178
+ if (typeof fn.userFunction.handleUpgrade === "function") {
179
+ hasExportHandleUpgradeFunctions.push(fn.name);
180
+ }
181
+ });
182
+ if (hasExportHandleUpgradeFunctions.length === 0) {
183
+ return;
184
+ }
185
+ process.stdout.write(`[vectorx-ff] WebSocket feature is enabled. Enabled functions: ${hasExportHandleUpgradeFunctions.join(", ")}\n`);
186
+ const wss = new ws_1.WebSocketServer({
187
+ noServer: true,
188
+ clientTracking: true,
189
+ perMessageDeflate: {
190
+ zlibDeflateOptions: {
191
+ chunkSize: 1024,
192
+ memLevel: 7,
193
+ level: 3,
194
+ },
195
+ zlibInflateOptions: {
196
+ chunkSize: 10 * 1024,
197
+ },
198
+ clientNoContextTakeover: true,
199
+ serverNoContextTakeover: true,
200
+ serverMaxWindowBits: 10,
201
+ concurrencyLimit: 10,
202
+ threshold: 1024,
203
+ },
204
+ });
205
+ setupGracefulShutdown(wss);
206
+ setupWebSocketsHeartbeat(wss);
207
+ wss.on("error", (err) => {
208
+ logger_1.functionsLogger.logError(err, { scene: "ws_server_error" });
209
+ });
210
+ wss.on("close", () => {
211
+ logger_1.functionsLogger.logError("ws server has been closed");
212
+ });
213
+ const composedMiddleware = (0, koa_compose_1.default)([
214
+ (0, middle_event_id_1.eventIdMiddleware)(),
215
+ (0, middle_async_context_1.asyncContextMiddleware)(),
216
+ (0, middle_common_logger_1.loggerMiddleware)(),
217
+ (ctx, next) => __awaiter(this, void 0, void 0, function* () {
218
+ try {
219
+ yield next();
220
+ }
221
+ catch (err) {
222
+ logger_1.functionsLogger.logError(err, { scene: "ws_middleware_error" });
223
+ }
224
+ }),
225
+ (0, middle_context_injection_1.contextInjectionMiddleware)(options, projectConfig),
226
+ ]);
227
+ httpServer.on("upgrade", (request, socket, head) => __awaiter(this, void 0, void 0, function* () {
228
+ const { handleFunction } = (0, router_1.routeFunction)(router, request.url || "/");
229
+ const userFunction = handleFunction;
230
+ if (typeof userFunction === "undefined" || typeof userFunction.handleUpgrade !== "function") {
231
+ socket.destroy();
232
+ return;
233
+ }
234
+ request.socket.setKeepAlive(true);
235
+ request.socket.setNoDelay(true);
236
+ request.socket.setTimeout(0);
237
+ try {
238
+ const ctx = koaApp.createContext(request, {});
239
+ yield composedMiddleware(ctx, () => {
240
+ return new Promise((resolve) => {
241
+ if (typeof userFunction.handleUpgrade !== "function") {
242
+ respond(socket, 404, "Not Found");
243
+ return;
244
+ }
245
+ const upgradeContext = {
246
+ ctxId: ctx.state.eventID,
247
+ eventID: ctx.state.eventID,
248
+ eventType: "websocket.upgrade",
249
+ timestamp: new Date().toISOString(),
250
+ httpContext: {
251
+ url: request.url,
252
+ httpMethod: request.method,
253
+ headers: request.headers,
254
+ },
255
+ };
256
+ ctx.state.contextInjected = upgradeContext;
257
+ try {
258
+ Promise.resolve(userFunction.handleUpgrade(upgradeContext))
259
+ .then((result) => {
260
+ if (!result.allowWebSocket) {
261
+ respond(socket, result.statusCode || 404, result.body || "Not Found", result.contentType);
262
+ return;
263
+ }
264
+ wss.handleUpgrade(request, socket, head, (ws) => __awaiter(this, void 0, void 0, function* () {
265
+ const context = {
266
+ ctxId: ctx.state.eventID,
267
+ eventID: ctx.state.eventID,
268
+ eventType: "websocket.connection",
269
+ timestamp: new Date().toISOString(),
270
+ httpContext: {
271
+ url: request.url,
272
+ httpMethod: request.method,
273
+ headers: request.headers,
274
+ },
275
+ ws: new VectorxWebSocket(ws, { ctxId: ctx.state.eventID }),
276
+ };
277
+ (0, helper_1.deepFreeze)(context);
278
+ process.nextTick(() => {
279
+ try {
280
+ userFunction(null, Object.assign({}, context));
281
+ }
282
+ catch (_e) {
283
+ }
284
+ });
285
+ resolve();
286
+ }));
287
+ })
288
+ .catch((e) => {
289
+ logger_1.functionsLogger.logError(`handleUpgrade promise-catch error: ${(0, error_1.decorateErrorStack)(e)}`);
290
+ respondError(socket, e);
291
+ });
292
+ }
293
+ catch (e) {
294
+ logger_1.functionsLogger.logError(`handleUpgrade try-catch error: ${(0, error_1.decorateErrorStack)(e)}`);
295
+ respondError(socket, e);
296
+ }
297
+ });
298
+ });
299
+ }
300
+ catch (e) {
301
+ logger_1.functionsLogger.logError(e, { scene: "ws_upgrade_error" });
302
+ respondError(socket, e);
303
+ }
304
+ }));
305
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vectorx/functions-framework",
3
- "version": "0.0.0-beta-20260109055258",
3
+ "version": "0.0.0-beta-20260320063305",
4
4
  "description": "VectorX Functions Framework",
5
5
  "main": "lib/index.js",
6
6
  "types": "types/index.d.ts",
@@ -22,15 +22,15 @@
22
22
  "author": "",
23
23
  "license": "ISC",
24
24
  "engines": {
25
- "node": ">=18.0.0"
25
+ "node": ">=20.0.0"
26
26
  },
27
27
  "dependencies": {
28
- "@vectorx/ai-types": "0.0.0-beta-20260109055258",
29
28
  "async_hooks": "^1.0.0",
30
29
  "chalk": "4",
31
30
  "commander": "^12.1.0",
32
31
  "dotenv": "^16.5.0",
33
32
  "koa": "^2.14.2",
33
+ "koa-compose": "^4.1.0",
34
34
  "koa-body": "^6.0.1",
35
35
  "koa-bodyparser": "^4.4.1",
36
36
  "koa-router": "^12.0.1",
@@ -41,8 +41,11 @@
41
41
  "radix3": "^1.1.2",
42
42
  "raw-body": "^2.5.2",
43
43
  "uuid": "^9.0.1",
44
+ "ws": "^8.18.0",
44
45
  "winston": "^3.11.0",
45
- "winston-daily-rotate-file": "^4.7.1"
46
+ "winston-daily-rotate-file": "^4.7.1",
47
+ "@vectorx/ai-types": "0.0.0-beta-20260320063305",
48
+ "@vectorx/endpoints": "0.0.0-beta-20260320063305"
46
49
  },
47
50
  "devDependencies": {
48
51
  "@types/jest": "^29.5.12",
@@ -52,6 +55,7 @@
52
55
  "@types/node": "^20.11.24",
53
56
  "@types/supertest": "^6.0.2",
54
57
  "@types/uuid": "^9.0.8",
58
+ "@types/ws": "^8.5.12",
55
59
  "@typescript-eslint/eslint-plugin": "^7.1.0",
56
60
  "@typescript-eslint/parser": "^7.1.0",
57
61
  "eslint": "^8.57.0",
@@ -71,6 +75,5 @@
71
75
  "test:ci": "jest --ci --coverage",
72
76
  "lint": "eslint src --ext .ts",
73
77
  "run:demo": "node ./bin/rcb-ff.js"
74
- },
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\n### 环境管理\n读取 .env 文件,校验 env 文件的可用性,获取当前运行环境\n"
78
+ }
76
79
  }
@@ -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;