@faapi/faapi 4.1.0 → 4.2.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/dist/index.d.ts CHANGED
@@ -120,8 +120,23 @@ interface Http2Options {
120
120
 
121
121
  /**
122
122
  * 生命周期钩子
123
+ *
124
+ * 执行时序:onBoot(.listen 调用前)→ server.listen → listen 回调内 onReady → 运行期 onError → 关闭时 onClose
123
125
  */
124
126
  interface LifecycleHooks {
127
+ /**
128
+ * 服务器 listen 之前调用(启动校验钩子)
129
+ *
130
+ * 时机:`app.listen()` 内、`server.listen` 调用之前——server 已创建但未监听
131
+ * (`server.listening === false`),路由/tool/agent 清单已水合、插件已加载。
132
+ *
133
+ * 适合启动校验(环境变量、下游依赖可达性)、DB 迁移等**失败即不该暴露端口**的逻辑:
134
+ * 钩子抛错 → `listen()` 以原始错误 reject,`server.listen` 不会被调用,端口不暴露。
135
+ *
136
+ * 与 onReady 的差异:onReady 在 listen 回调内执行,失败时端口已开,
137
+ * 存在"接受连接但不服务"的窗口——启动校验请用 onBoot,资源初始化用 onReady。
138
+ */
139
+ onBoot?: (ctx: LifecycleContext) => Promise<void> | void;
125
140
  /** 服务器启动后调用(适合初始化数据库连接等) */
126
141
  onReady?: (ctx: LifecycleContext) => Promise<void> | void;
127
142
  /** 服务器关闭时调用(适合清理资源、优雅关闭) */
@@ -149,7 +164,7 @@ interface LifecycleContext {
149
164
  rootDir: string;
150
165
  /** 当前路由清单 */
151
166
  routes: RouteManifest;
152
- /** 服务器实例 */
167
+ /** 服务器实例(onBoot 钩子触发时已创建但未监听,`listening === false`) */
153
168
  server: node_http.Server;
154
169
  /** app 级注册表——skill 等运行时动态注册路径(`registries.skill.upsert(...)`) */
155
170
  registries: AppRegistries;
package/dist/index.js CHANGED
@@ -2036,6 +2036,15 @@ function toStrippedProdImportPath(sourceFile, rootDir) {
2036
2036
  if (!rel.startsWith(".")) rel = "./" + rel;
2037
2037
  return toProdExtension(rel);
2038
2038
  }
2039
+ function toProdImportFromImporter(importer, rootDir, relFromDist) {
2040
+ const importerRel = path5.relative(toRealPath(path5.resolve(rootDir)), toRealPath(importer)).split(path5.sep).join("/");
2041
+ const importerProdDir = path5.posix.dirname(toProdExtension(importerRel));
2042
+ if (importerProdDir === ".") return relFromDist;
2043
+ const target = relFromDist.replace(/^\.\//, "");
2044
+ let rel = path5.posix.relative(importerProdDir, target);
2045
+ if (!rel.startsWith(".")) rel = "./" + rel;
2046
+ return rel;
2047
+ }
2039
2048
  var PROD_EXTS = [".js", ".mjs", ".cjs"];
2040
2049
  var SOURCE_EXTS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
2041
2050
  var INDEX_EXTS = [
@@ -2093,9 +2102,10 @@ function createAliasPlugin(config, options) {
2093
2102
  }
2094
2103
  if (appDirAbs && importerOutsideAppDir && isInsideDir(resolved, appDirAbs)) {
2095
2104
  modified = true;
2096
- return `${prefix}${quote}${toStrippedProdImportPath(
2097
- resolved,
2098
- options.rootDir
2105
+ return `${prefix}${quote}${toProdImportFromImporter(
2106
+ importer,
2107
+ options.rootDir,
2108
+ toStrippedProdImportPath(resolved, options.rootDir)
2099
2109
  )}${quote}`;
2100
2110
  }
2101
2111
  modified = true;
@@ -2110,9 +2120,10 @@ function createAliasPlugin(config, options) {
2110
2120
  if (fs4.existsSync(file)) {
2111
2121
  modified = true;
2112
2122
  if (appDirAbs && importerOutsideAppDir && isInsideDir(file, appDirAbs)) {
2113
- return `${prefix}${quote}${toStrippedProdImportPath(
2114
- file,
2115
- options.rootDir
2123
+ return `${prefix}${quote}${toProdImportFromImporter(
2124
+ importer,
2125
+ options.rootDir,
2126
+ toStrippedProdImportPath(file, options.rootDir)
2116
2127
  )}${quote}`;
2117
2128
  }
2118
2129
  return `${prefix}${quote}${toProdImportPath(file, importer)}${quote}`;
@@ -2123,9 +2134,10 @@ function createAliasPlugin(config, options) {
2123
2134
  if (fs4.existsSync(file)) {
2124
2135
  modified = true;
2125
2136
  if (appDirAbs && importerOutsideAppDir && isInsideDir(file, appDirAbs)) {
2126
- return `${prefix}${quote}${toStrippedProdImportPath(
2127
- file,
2128
- options.rootDir
2137
+ return `${prefix}${quote}${toProdImportFromImporter(
2138
+ importer,
2139
+ options.rootDir,
2140
+ toStrippedProdImportPath(file, options.rootDir)
2129
2141
  )}${quote}`;
2130
2142
  }
2131
2143
  return `${prefix}${quote}${toProdImportPath(file, importer)}${quote}`;
@@ -3096,14 +3108,14 @@ var ValidationError = class extends FaapiError {
3096
3108
  issues;
3097
3109
  };
3098
3110
  var RouteNotFoundError = class extends FaapiError {
3099
- constructor(path23) {
3100
- super("ROUTE_NOT_FOUND", `Route not found: ${path23}`, 404);
3111
+ constructor(path24) {
3112
+ super("ROUTE_NOT_FOUND", `Route not found: ${path24}`, 404);
3101
3113
  this.name = "RouteNotFoundError";
3102
3114
  }
3103
3115
  };
3104
3116
  var MethodNotAllowedError = class extends FaapiError {
3105
- constructor(method, path23, allowedMethods) {
3106
- super("METHOD_NOT_ALLOWED", `Method ${method} not allowed for ${path23}`, 405);
3117
+ constructor(method, path24, allowedMethods) {
3118
+ super("METHOD_NOT_ALLOWED", `Method ${method} not allowed for ${path24}`, 405);
3107
3119
  this.allowedMethods = allowedMethods;
3108
3120
  this.name = "MethodNotAllowedError";
3109
3121
  }
@@ -3129,8 +3141,8 @@ var PayloadTooLargeError = class extends FaapiError {
3129
3141
  };
3130
3142
 
3131
3143
  // src/cli/createAppCore.ts
3132
- import fs15 from "fs";
3133
- import path19 from "path";
3144
+ import fs17 from "fs";
3145
+ import path20 from "path";
3134
3146
  import { PassThrough, Readable as Readable3 } from "stream";
3135
3147
 
3136
3148
  // src/router/sortRoutes.ts
@@ -3225,18 +3237,18 @@ function getWsIndex(routes) {
3225
3237
  wsIndexCache.set(routes, index);
3226
3238
  return index;
3227
3239
  }
3228
- function matchRoute(routes, method, path23) {
3240
+ function matchRoute(routes, method, path24) {
3229
3241
  const index = getHttpIndex(routes);
3230
3242
  const upper = method.toUpperCase();
3231
- const hit = matchByMethod(index, upper, path23);
3243
+ const hit = matchByMethod(index, upper, path24);
3232
3244
  if (hit) return hit;
3233
3245
  if (upper === "HEAD") {
3234
- return matchByMethod(index, "GET", path23);
3246
+ return matchByMethod(index, "GET", path24);
3235
3247
  }
3236
3248
  return null;
3237
3249
  }
3238
- function matchByMethod(index, method, path23) {
3239
- const staticHit = index.static.get(`${method}|${path23}`);
3250
+ function matchByMethod(index, method, path24) {
3251
+ const staticHit = index.static.get(`${method}|${path24}`);
3240
3252
  if (staticHit) {
3241
3253
  return { route: staticHit, params: {} };
3242
3254
  }
@@ -3245,31 +3257,31 @@ function matchByMethod(index, method, path23) {
3245
3257
  if (route.method !== method) {
3246
3258
  continue;
3247
3259
  }
3248
- const params = matchSegments(entry.segments, path23, route.paramNames, route.isCatchAll);
3260
+ const params = matchSegments(entry.segments, path24, route.paramNames, route.isCatchAll);
3249
3261
  if (params !== null) {
3250
3262
  return { route, params };
3251
3263
  }
3252
3264
  }
3253
3265
  return null;
3254
3266
  }
3255
- function matchWsRoute(wsRoutes, path23) {
3267
+ function matchWsRoute(wsRoutes, path24) {
3256
3268
  const index = getWsIndex(wsRoutes);
3257
- const staticHit = index.static.get(path23);
3269
+ const staticHit = index.static.get(path24);
3258
3270
  if (staticHit) {
3259
3271
  return { route: staticHit, params: {} };
3260
3272
  }
3261
3273
  for (const route of index.dynamics) {
3262
- const params = matchDynamicPath(route.urlPath, path23, route.paramNames, route.isCatchAll);
3274
+ const params = matchDynamicPath(route.urlPath, path24, route.paramNames, route.isCatchAll);
3263
3275
  if (params !== null) {
3264
3276
  return { route, params };
3265
3277
  }
3266
3278
  }
3267
3279
  return null;
3268
3280
  }
3269
- function findAllowedMethods(routes, path23) {
3281
+ function findAllowedMethods(routes, path24) {
3270
3282
  const index = getHttpIndex(routes);
3271
3283
  const methods = /* @__PURE__ */ new Set();
3272
- const staticMethods = index.methodsByStaticPath.get(path23);
3284
+ const staticMethods = index.methodsByStaticPath.get(path24);
3273
3285
  if (staticMethods) {
3274
3286
  for (const method of staticMethods) {
3275
3287
  methods.add(method);
@@ -3278,7 +3290,7 @@ function findAllowedMethods(routes, path23) {
3278
3290
  for (const entry of index.dynamics) {
3279
3291
  const params = matchSegments(
3280
3292
  entry.segments,
3281
- path23,
3293
+ path24,
3282
3294
  entry.route.paramNames,
3283
3295
  entry.route.isCatchAll
3284
3296
  );
@@ -3291,11 +3303,11 @@ function findAllowedMethods(routes, path23) {
3291
3303
  }
3292
3304
  return Array.from(methods);
3293
3305
  }
3294
- function matchDynamicPath(pattern, path23, paramNames, isCatchAll) {
3295
- return matchSegments(pattern.split("/").filter(Boolean), path23, paramNames, isCatchAll);
3306
+ function matchDynamicPath(pattern, path24, paramNames, isCatchAll) {
3307
+ return matchSegments(pattern.split("/").filter(Boolean), path24, paramNames, isCatchAll);
3296
3308
  }
3297
- function matchSegments(patternSegments, path23, paramNames, isCatchAll) {
3298
- const pathSegments = path23.split("/").filter(Boolean);
3309
+ function matchSegments(patternSegments, path24, paramNames, isCatchAll) {
3310
+ const pathSegments = path24.split("/").filter(Boolean);
3299
3311
  if (isCatchAll) {
3300
3312
  const nonCatchAllCount = patternSegments.length - 1;
3301
3313
  if (pathSegments.length <= nonCatchAllCount) {
@@ -4183,9 +4195,9 @@ async function validateInput(schemaPath, method, inputType, input) {
4183
4195
  function mapZodIssues(error) {
4184
4196
  return error.issues.map((issue) => {
4185
4197
  const code = mapZodCode(issue);
4186
- const path23 = issue.path.map(String).join(".") || "";
4198
+ const path24 = issue.path.map(String).join(".") || "";
4187
4199
  return {
4188
- path: path23,
4200
+ path: path24,
4189
4201
  code,
4190
4202
  expected: issue.expected ?? mapExpectedFromMessage(issue.message),
4191
4203
  received: issue.received ?? mapReceivedFromMessage(issue.message),
@@ -5286,9 +5298,50 @@ async function generateAgentArtifacts(agents, rootDir, dist) {
5286
5298
  }
5287
5299
 
5288
5300
  // src/cli/loadPlugins.ts
5289
- import path18 from "path";
5301
+ import path19 from "path";
5302
+ import fs16 from "fs";
5290
5303
  import { pathToFileURL as pathToFileURL2 } from "url";
5291
- async function loadPlugins(declarations, ctx, rootDir) {
5304
+
5305
+ // src/cli/compileConfig.ts
5306
+ import path18 from "path";
5307
+ import fs15 from "fs";
5308
+ async function compileProjectModules(entryPoints, rootDir, dist) {
5309
+ const { insideFiles, outsideFiles } = await collectRelativeImports(entryPoints, rootDir);
5310
+ const esbuild = await import("esbuild");
5311
+ const aliasPlugins = buildAliasPlugins(rootDir);
5312
+ const absDist = path18.resolve(rootDir, dist);
5313
+ await esbuild.build({
5314
+ entryPoints: [...entryPoints, ...outsideFiles],
5315
+ outdir: absDist,
5316
+ outbase: rootDir,
5317
+ bundle: false,
5318
+ platform: "node",
5319
+ format: "esm",
5320
+ sourcemap: true,
5321
+ packages: "external",
5322
+ plugins: aliasPlugins,
5323
+ logLevel: "silent"
5324
+ });
5325
+ if (insideFiles.length > 0) {
5326
+ const appOutbase = path18.resolve(rootDir, "src");
5327
+ await esbuild.build({
5328
+ entryPoints: insideFiles,
5329
+ outdir: absDist,
5330
+ outbase: appOutbase,
5331
+ bundle: false,
5332
+ platform: "node",
5333
+ format: "esm",
5334
+ sourcemap: true,
5335
+ packages: "external",
5336
+ plugins: aliasPlugins,
5337
+ logLevel: "silent"
5338
+ });
5339
+ }
5340
+ return { insideFiles, outsideFiles };
5341
+ }
5342
+
5343
+ // src/cli/loadPlugins.ts
5344
+ async function loadPlugins(declarations, ctx, rootDir, dist) {
5292
5345
  const handlerWrappers = [];
5293
5346
  const upgradeWrappers = [];
5294
5347
  const failures = [];
@@ -5325,7 +5378,7 @@ async function loadPlugins(declarations, ctx, rootDir) {
5325
5378
  }
5326
5379
  loaded.add(specifier);
5327
5380
  try {
5328
- const mod = await import(resolveSpecifier(specifier, rootDir));
5381
+ const mod = await importPluginModule(specifier, rootDir ?? process.cwd(), dist);
5329
5382
  const plugin = mod.default ?? mod;
5330
5383
  if (typeof plugin.setup !== "function") {
5331
5384
  failures.push({ specifier, reason: "plugin has no setup function" });
@@ -5348,15 +5401,72 @@ async function loadPlugins(declarations, ctx, rootDir) {
5348
5401
  }
5349
5402
  return { handlerWrappers, upgradeWrappers, failures };
5350
5403
  }
5351
- function resolveSpecifier(specifier, rootDir) {
5352
- if (specifier.startsWith("./") || specifier.startsWith("../")) {
5353
- const base = rootDir ?? process.cwd();
5354
- return pathToFileURL2(path18.resolve(base, specifier)).href;
5404
+ var TS_EXTS = /\.(ts|tsx|mts|cts)$/;
5405
+ var JS_EXTS = /\.(js|mjs|cjs|jsx)$/;
5406
+ function resolveLocalPluginSource(specifier, baseDir) {
5407
+ const base = path19.isAbsolute(specifier) ? specifier : path19.resolve(baseDir, specifier);
5408
+ if ((TS_EXTS.test(base) || JS_EXTS.test(base)) && fs16.existsSync(base)) return base;
5409
+ for (const ext of [".ts", ".js"]) {
5410
+ if (fs16.existsSync(base + ext)) return base + ext;
5355
5411
  }
5356
- if (path18.isAbsolute(specifier)) {
5357
- return pathToFileURL2(specifier).href;
5412
+ for (const indexExt of ["/index.ts", "/index.js"]) {
5413
+ if (fs16.existsSync(base + indexExt)) return base + indexExt;
5358
5414
  }
5359
- return specifier;
5415
+ return null;
5416
+ }
5417
+ function mirrorProductPath(sourcePath, baseDir, distDir) {
5418
+ let rel = path19.relative(baseDir, sourcePath).replace(/\\/g, "/");
5419
+ if (rel.startsWith("src/")) rel = rel.slice(4);
5420
+ return path19.resolve(distDir, rel.replace(TS_EXTS, ".js"));
5421
+ }
5422
+ function isSourceNewer(sourcePath, productPath) {
5423
+ try {
5424
+ return fs16.statSync(sourcePath).mtimeMs > fs16.statSync(productPath).mtimeMs;
5425
+ } catch {
5426
+ return true;
5427
+ }
5428
+ }
5429
+ async function importPluginModule(specifier, baseDir, dist) {
5430
+ const isRelative = specifier.startsWith("./") || specifier.startsWith("../");
5431
+ if (!isRelative && !path19.isAbsolute(specifier)) {
5432
+ return import(specifier);
5433
+ }
5434
+ const distDir = dist ? path19.resolve(baseDir, dist) : null;
5435
+ const sourcePath = resolveLocalPluginSource(specifier, baseDir);
5436
+ const productPath = sourcePath && distDir ? mirrorProductPath(sourcePath, baseDir, distDir) : null;
5437
+ if (productPath !== null && fs16.existsSync(productPath) && (sourcePath === null || !isSourceNewer(sourcePath, productPath))) {
5438
+ return import(pathToFileURL2(productPath).href);
5439
+ }
5440
+ if (sourcePath && TS_EXTS.test(sourcePath)) {
5441
+ if (!isDevOnDemandEnabled() || !distDir) {
5442
+ throw new Error(
5443
+ `Local plugin "${specifier}" has no up-to-date build artifact (dist is stale or missing). Run "faapi build" first, or use "faapi dev" for on-demand compilation.`
5444
+ );
5445
+ }
5446
+ const relFromRoot = path19.relative(baseDir, sourcePath).replace(/\\/g, "/");
5447
+ if (relFromRoot.startsWith("src/")) {
5448
+ await ensureCompiled(sourcePath, baseDir, dist);
5449
+ } else {
5450
+ await compileProjectModules([sourcePath], baseDir, dist);
5451
+ }
5452
+ if (productPath && fs16.existsSync(productPath)) {
5453
+ return import(pathToFileURL2(productPath).href);
5454
+ }
5455
+ }
5456
+ if (sourcePath && JS_EXTS.test(sourcePath)) {
5457
+ return import(pathToFileURL2(sourcePath).href);
5458
+ }
5459
+ const candidates = [
5460
+ `${specifier}.ts`,
5461
+ `${specifier}/index.ts`,
5462
+ `${specifier}.js`,
5463
+ `${specifier}/index.js`
5464
+ ].map((c) => path19.isAbsolute(c) ? c : path19.join(baseDir, c));
5465
+ throw new Error(
5466
+ `Cannot find local plugin "${specifier}" (resolved from ${baseDir}). Expected one of:
5467
+ ${candidates.join("\n ")}
5468
+ Create the plugin file (export default { name, setup(ctx) {...} }) or fix the path.`
5469
+ );
5360
5470
  }
5361
5471
  function resolveDeclaration(decl) {
5362
5472
  if (typeof decl === "string") {
@@ -5382,8 +5492,8 @@ var ROUTES_FILE = "faapi-routes.js";
5382
5492
  var TOOLS_FILE2 = "faapi-tools.js";
5383
5493
  var AGENTS_FILE2 = "faapi-agents.js";
5384
5494
  async function loadAndHydrateTools(rootDir, dist, registries = defaultRegistries) {
5385
- const toolsPath = path19.resolve(rootDir, dist, TOOLS_FILE2);
5386
- if (!fs15.existsSync(toolsPath)) {
5495
+ const toolsPath = path20.resolve(rootDir, dist, TOOLS_FILE2);
5496
+ if (!fs17.existsSync(toolsPath)) {
5387
5497
  return [];
5388
5498
  }
5389
5499
  const serialized = await importWithCacheBust(toolsPath);
@@ -5392,8 +5502,8 @@ async function loadAndHydrateTools(rootDir, dist, registries = defaultRegistries
5392
5502
  return hydrated;
5393
5503
  }
5394
5504
  async function loadAndHydrateAgents(rootDir, dist, registries = defaultRegistries) {
5395
- const agentsPath = path19.resolve(rootDir, dist, AGENTS_FILE2);
5396
- if (!fs15.existsSync(agentsPath)) {
5505
+ const agentsPath = path20.resolve(rootDir, dist, AGENTS_FILE2);
5506
+ if (!fs17.existsSync(agentsPath)) {
5397
5507
  return [];
5398
5508
  }
5399
5509
  const serialized = await importWithCacheBust(agentsPath);
@@ -5460,8 +5570,8 @@ function isFaapiConfigKey(key) {
5460
5570
  async function createAppBase(options) {
5461
5571
  const rootDir = options?.rootDir ?? process.cwd();
5462
5572
  const dist = options?.dist ?? process.env.FAAPI_DIST ?? DEFAULT_DIST;
5463
- const routesPath = path19.resolve(rootDir, dist, ROUTES_FILE);
5464
- if (!fs15.existsSync(routesPath)) {
5573
+ const routesPath = path20.resolve(rootDir, dist, ROUTES_FILE);
5574
+ if (!fs17.existsSync(routesPath)) {
5465
5575
  throw new Error(
5466
5576
  `[faapi] ${dist}/${ROUTES_FILE} \u4E0D\u5B58\u5728\uFF0C\u8BF7\u5148\u6267\u884C \`faapi build\`\uFF08\u6216 \`faapi dev\`\uFF09\u751F\u6210\u4EA7\u7269\u3002`
5467
5577
  );
@@ -5513,7 +5623,8 @@ async function createAppBase(options) {
5513
5623
  server,
5514
5624
  config: pluginConfig
5515
5625
  },
5516
- rootDir
5626
+ rootDir,
5627
+ dist
5517
5628
  );
5518
5629
  applyPluginWrappers(server, handlerWrappers, upgradeWrappers);
5519
5630
  let closed = false;
@@ -5526,6 +5637,16 @@ async function createAppBase(options) {
5526
5637
  async listen(listenPort) {
5527
5638
  const envPort = process.env.PORT ? Number(process.env.PORT) : void 0;
5528
5639
  const actualPort = listenPort ?? options?.port ?? envPort ?? DEFAULT_PORT;
5640
+ if (config?.lifecycle?.onBoot) {
5641
+ try {
5642
+ await config.lifecycle.onBoot({ rootDir, routes: sorted, server, registries });
5643
+ console.log("- onBoot hook executed");
5644
+ } catch (err) {
5645
+ const message = err instanceof Error ? err.message : String(err);
5646
+ console.error(`[faapi] onBoot hook failed: ${message}`);
5647
+ throw err;
5648
+ }
5649
+ }
5529
5650
  return new Promise((resolve, reject) => {
5530
5651
  const onListenError = (err) => {
5531
5652
  if (err.code === "EADDRINUSE") {
@@ -5708,17 +5829,17 @@ async function createAppBase(options) {
5708
5829
 
5709
5830
  // src/router/scanRoutes.ts
5710
5831
  import fg2 from "fast-glob";
5711
- import path20 from "path";
5712
- import fs16 from "fs";
5832
+ import path21 from "path";
5833
+ import fs18 from "fs";
5713
5834
 
5714
5835
  // src/router/constants.ts
5715
5836
  var HTTP_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"];
5716
5837
  var HTTP_METHOD_SET = new Set(HTTP_METHODS);
5717
5838
 
5718
5839
  // src/utils/normalizePath.ts
5719
- function normalizePath(path23) {
5720
- if (!path23) return "";
5721
- let result = path23.replace(/\\/g, "/");
5840
+ function normalizePath(path24) {
5841
+ if (!path24) return "";
5842
+ let result = path24.replace(/\\/g, "/");
5722
5843
  result = result.replace(/\/+/g, "/");
5723
5844
  result = result.replace(/\/+$/, "");
5724
5845
  if (result && !result.startsWith("/")) {
@@ -5779,34 +5900,34 @@ function extractExportsFromSource(source) {
5779
5900
  return names;
5780
5901
  }
5781
5902
  function collectMiddlewarePaths(routeFilePath, rootDir, dist) {
5782
- const routeDir = path20.dirname(routeFilePath);
5783
- const resolvedRoot = path20.resolve(rootDir);
5903
+ const routeDir = path21.dirname(routeFilePath);
5904
+ const resolvedRoot = path21.resolve(rootDir);
5784
5905
  const paths = [];
5785
- let currentDir = path20.resolve(rootDir, routeDir);
5906
+ let currentDir = path21.resolve(rootDir, routeDir);
5786
5907
  while (true) {
5787
5908
  if (dist) {
5788
- const mwTsPath = path20.join(currentDir, "middlewares.ts");
5789
- const mwJsPath = path20.join(currentDir, "middlewares.js");
5790
- const absTsPath = path20.resolve(rootDir, mwTsPath);
5791
- const absJsPath = path20.resolve(rootDir, mwJsPath);
5792
- const absMwPath = fs16.existsSync(absTsPath) ? absTsPath : fs16.existsSync(absJsPath) ? absJsPath : null;
5909
+ const mwTsPath = path21.join(currentDir, "middlewares.ts");
5910
+ const mwJsPath = path21.join(currentDir, "middlewares.js");
5911
+ const absTsPath = path21.resolve(rootDir, mwTsPath);
5912
+ const absJsPath = path21.resolve(rootDir, mwJsPath);
5913
+ const absMwPath = fs18.existsSync(absTsPath) ? absTsPath : fs18.existsSync(absJsPath) ? absJsPath : null;
5793
5914
  if (absMwPath) {
5794
- const relMwPath = path20.relative(rootDir, absMwPath);
5795
- const prodAbsPath = path20.resolve(rootDir, toProdFilePath(relMwPath, dist));
5915
+ const relMwPath = path21.relative(rootDir, absMwPath);
5916
+ const prodAbsPath = path21.resolve(rootDir, toProdFilePath(relMwPath, dist));
5796
5917
  paths.push(prodAbsPath);
5797
5918
  }
5798
5919
  } else {
5799
5920
  for (const ext of [".ts", ".js"]) {
5800
- const mwPath = path20.join(currentDir, `middlewares${ext}`);
5801
- const absMwPath = path20.resolve(rootDir, mwPath);
5802
- if (fs16.existsSync(absMwPath)) {
5921
+ const mwPath = path21.join(currentDir, `middlewares${ext}`);
5922
+ const absMwPath = path21.resolve(rootDir, mwPath);
5923
+ if (fs18.existsSync(absMwPath)) {
5803
5924
  paths.push(absMwPath);
5804
5925
  break;
5805
5926
  }
5806
5927
  }
5807
5928
  }
5808
5929
  if (currentDir === resolvedRoot) break;
5809
- const parentDir = path20.dirname(currentDir);
5930
+ const parentDir = path21.dirname(currentDir);
5810
5931
  if (parentDir === currentDir) break;
5811
5932
  currentDir = parentDir;
5812
5933
  }
@@ -5825,7 +5946,7 @@ async function scanRoutes(rootDir, patterns, dist) {
5825
5946
  const normalizedFile = file.replace(/\\/g, "/");
5826
5947
  const fileName = normalizedFile.split("/").pop();
5827
5948
  if (fileName === "handler.ts" || fileName === "handler.js") {
5828
- const absPath = path20.resolve(rootDir, normalizedFile);
5949
+ const absPath = path21.resolve(rootDir, normalizedFile);
5829
5950
  const urlPath = filePathToUrlPath(normalizedFile);
5830
5951
  const paramNames = extractParamNames(urlPath);
5831
5952
  const isDynamic = paramNames.length > 0;
@@ -5838,7 +5959,7 @@ async function scanRoutes(rootDir, patterns, dist) {
5838
5959
  const mwPaths = collectMiddlewarePaths(normalizedFile, rootDir);
5839
5960
  middlewareBundle = await loadMergedMiddlewares(mwPaths);
5840
5961
  }
5841
- const source = await fs16.promises.readFile(absPath, "utf8").catch(() => "");
5962
+ const source = await fs18.promises.readFile(absPath, "utf8").catch(() => "");
5842
5963
  const exportNames = extractExportsFromSource(source);
5843
5964
  const methods = HTTP_METHODS.filter((m) => exportNames.has(m));
5844
5965
  for (const method of methods) {
@@ -5874,8 +5995,8 @@ async function scanRoutes(rootDir, patterns, dist) {
5874
5995
 
5875
5996
  // src/tools/scanTools.ts
5876
5997
  import fg3 from "fast-glob";
5877
- import path21 from "path";
5878
- import fs17 from "fs";
5998
+ import path22 from "path";
5999
+ import fs19 from "fs";
5879
6000
  var TOOL_PATTERNS = ["src/tools/**/*.ts"];
5880
6001
  var TOOL_EXPORT_RE = new RegExp(
5881
6002
  String.raw`export\s+(?:async\s+)?(?:function\s+|const\s+)([A-Za-z_$][\w$]*)\s*(?:\(|=)`,
@@ -5925,8 +6046,8 @@ async function scanTools(rootDir, patterns) {
5925
6046
  if (fileName !== "handler.ts" && fileName !== "handler.js") {
5926
6047
  continue;
5927
6048
  }
5928
- const absPath = path21.resolve(rootDir, normalizedFile);
5929
- const source = await fs17.promises.readFile(absPath, "utf8").catch(() => "");
6049
+ const absPath = path22.resolve(rootDir, normalizedFile);
6050
+ const source = await fs19.promises.readFile(absPath, "utf8").catch(() => "");
5930
6051
  const exportNames = extractToolExportsFromSource(source);
5931
6052
  const namespace = filePathToToolNamespace(normalizedFile);
5932
6053
  for (const fnName of exportNames) {
@@ -5950,8 +6071,8 @@ async function scanTools(rootDir, patterns) {
5950
6071
 
5951
6072
  // src/agents/scanAgents.ts
5952
6073
  import fg4 from "fast-glob";
5953
- import path22 from "path";
5954
- import fs18 from "fs";
6074
+ import path23 from "path";
6075
+ import fs20 from "fs";
5955
6076
  var DEFAULT_AGENT_PATTERNS = ["src/agents/*/handler.ts"];
5956
6077
  var RUN_EXPORT_RE = /export\s+(?:async\s+)?(?:function\s+|const\s+)run\b/;
5957
6078
  function extractAgentNameFromPath(filePath) {
@@ -5983,8 +6104,8 @@ async function scanAgents(rootDir, patterns) {
5983
6104
  if (fileName !== "handler.ts" && fileName !== "handler.js") {
5984
6105
  continue;
5985
6106
  }
5986
- const absPath = path22.resolve(rootDir, normalizedFile);
5987
- const source = await fs18.promises.readFile(absPath, "utf8").catch(() => "");
6107
+ const absPath = path23.resolve(rootDir, normalizedFile);
6108
+ const source = await fs20.promises.readFile(absPath, "utf8").catch(() => "");
5988
6109
  const { hasRun } = detectAgentExports(source);
5989
6110
  const name = extractAgentNameFromPath(normalizedFile);
5990
6111
  const prevFile = seen.get(name);