@faapi/faapi 6.22.0 → 6.24.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.js CHANGED
@@ -2362,7 +2362,11 @@ async function runTaskInWorker(options) {
2362
2362
  worker.postMessage({
2363
2363
  type: "run",
2364
2364
  payload,
2365
- taskCtx: { config: safeConfig(taskCtx.config), job: taskCtx.job },
2365
+ taskCtx: {
2366
+ config: safeConfig(taskCtx.config),
2367
+ resourcesDir: taskCtx.resourcesDir,
2368
+ job: taskCtx.job
2369
+ },
2366
2370
  registries: options.registries,
2367
2371
  log: options.log
2368
2372
  });
@@ -2712,6 +2716,7 @@ function createTaskQueue(deps) {
2712
2716
  payload: job.payload,
2713
2717
  taskCtx: {
2714
2718
  config: deps.config,
2719
+ resourcesDir: deps.resourcesDir,
2715
2720
  job: { id: job.id, name: job.name, attempt: job.attempt }
2716
2721
  },
2717
2722
  registries: snapshotRegistries(),
@@ -2742,6 +2747,7 @@ function createTaskQueue(deps) {
2742
2747
  const taskCtx = {
2743
2748
  signal: job.signal,
2744
2749
  config: deps.config,
2750
+ resourcesDir: deps.resourcesDir,
2745
2751
  job: { id: job.id, name: job.name, attempt: job.attempt },
2746
2752
  registries: registriesView,
2747
2753
  log: createLogger(`task:${job.name}`, {
@@ -3086,7 +3092,7 @@ function listSkills() {
3086
3092
  return defaultRegistries.skill.list();
3087
3093
  }
3088
3094
 
3089
- // src/loader/loadAgentModule.ts
3095
+ // src/loader/loadToolModule.ts
3090
3096
  import fs12 from "fs";
3091
3097
 
3092
3098
  // src/loader/resolveExports.ts
@@ -3442,13 +3448,14 @@ function buildAliasPlugins(rootDir) {
3442
3448
  }
3443
3449
 
3444
3450
  // src/cli/compileSourceFiles.ts
3451
+ var SOURCE_IGNORE = ["**/*.test.ts", "**/*.e2e.test.ts", "**/*.d.ts", `${APP_DIR}/resources/**`];
3445
3452
  async function compileSourceFiles(options) {
3446
3453
  const { rootDir, dist, files, logLevel = "silent", production, atomicWrite } = options;
3447
3454
  const entryPoints = files ?? await fg2([`${APP_DIR}/**/*.ts`], {
3448
3455
  cwd: rootDir,
3449
3456
  onlyFiles: true,
3450
3457
  absolute: true,
3451
- ignore: ["**/*.test.ts", "**/*.e2e.test.ts", "**/*.d.ts"]
3458
+ ignore: SOURCE_IGNORE
3452
3459
  });
3453
3460
  if (entryPoints.length === 0) {
3454
3461
  return { compiledFiles: [] };
@@ -3736,52 +3743,13 @@ function getDevDist() {
3736
3743
  return state.distDir;
3737
3744
  }
3738
3745
 
3739
- // src/loader/loadAgentModule.ts
3740
- async function loadAgentModule(filePath, hasRun, rootDir) {
3741
- if (isDevOnDemandEnabled() && rootDir) {
3742
- const dist = getDevDist();
3743
- if (dist) {
3744
- const sourcePath = prodPathToSourcePath(filePath, rootDir, dist);
3745
- if (sourcePath && fs12.existsSync(sourcePath)) {
3746
- try {
3747
- await ensureCompiled(sourcePath, rootDir, dist);
3748
- } catch (compileErr) {
3749
- const reason = compileErr instanceof Error ? compileErr.message : String(compileErr);
3750
- throw new Error(`Failed to compile agent module "${sourcePath}": ${reason}`, {
3751
- cause: compileErr
3752
- });
3753
- }
3754
- }
3755
- }
3756
- }
3757
- let module;
3758
- try {
3759
- module = await importWithCacheBust(filePath, isDevOnDemandEnabled());
3760
- } catch (err) {
3761
- const reason = err instanceof Error ? err.message : String(err);
3762
- throw new Error(`Failed to load agent module "${filePath}": ${reason}`, { cause: err });
3763
- }
3764
- let run;
3765
- if (hasRun) {
3766
- const runExport = resolveExport(module, "run");
3767
- if (typeof runExport !== "function") {
3768
- throw new Error(
3769
- `Agent module "${filePath}" does not export a valid "run" function (hasRun=true). Expected a function, got ${runExport === void 0 ? "undefined" : typeof runExport}.`
3770
- );
3771
- }
3772
- run = runExport;
3773
- }
3774
- return { run };
3775
- }
3776
-
3777
3746
  // src/loader/loadToolModule.ts
3778
- import fs13 from "fs";
3779
3747
  async function loadToolModule(filePath, functionName, rootDir) {
3780
3748
  if (isDevOnDemandEnabled() && rootDir) {
3781
3749
  const dist = getDevDist();
3782
3750
  if (dist) {
3783
3751
  const sourcePath = prodPathToSourcePath(filePath, rootDir, dist);
3784
- if (sourcePath && fs13.existsSync(sourcePath)) {
3752
+ if (sourcePath && fs12.existsSync(sourcePath)) {
3785
3753
  try {
3786
3754
  await ensureCompiled(sourcePath, rootDir, dist);
3787
3755
  } catch (compileErr) {
@@ -4253,15 +4221,15 @@ function helmet(options = {}) {
4253
4221
 
4254
4222
  // src/config/loadConfig.ts
4255
4223
  import path16 from "path";
4256
- import fs14 from "fs";
4224
+ import fs13 from "fs";
4257
4225
  var CONFIG_PRODUCT_FILE = "faapi-config.js";
4258
4226
  async function loadConfig(rootDir, dist) {
4259
4227
  const configProductPath = path16.resolve(rootDir, dist, CONFIG_PRODUCT_FILE);
4260
- if (fs14.existsSync(configProductPath)) {
4228
+ if (fs13.existsSync(configProductPath)) {
4261
4229
  const module = await importWithCacheBust(configProductPath);
4262
4230
  return module.default ?? {};
4263
4231
  }
4264
- const hasSourceConfig = fs14.existsSync(path16.join(rootDir, "faapi.config.ts")) || fs14.existsSync(path16.join(rootDir, "faapi.config.js"));
4232
+ const hasSourceConfig = fs13.existsSync(path16.join(rootDir, "faapi.config.ts")) || fs13.existsSync(path16.join(rootDir, "faapi.config.js"));
4265
4233
  if (hasSourceConfig) {
4266
4234
  throw new Error(
4267
4235
  `[faapi] ${dist}/${CONFIG_PRODUCT_FILE} \u4E0D\u5B58\u5728\uFF0C\u8BF7\u5148\u6267\u884C \`faapi build\`\uFF08\u6216 \`faapi dev\`\uFF09\u751F\u6210\u4EA7\u7269\u3002`
@@ -4271,7 +4239,7 @@ async function loadConfig(rootDir, dist) {
4271
4239
  }
4272
4240
 
4273
4241
  // src/cli/loadEnv.ts
4274
- import fs15 from "fs";
4242
+ import fs14 from "fs";
4275
4243
  import path17 from "path";
4276
4244
  function resolveEnv() {
4277
4245
  return process.env.NODE_ENV || "development";
@@ -4340,8 +4308,8 @@ function loadEnv(rootDir) {
4340
4308
  const merged = {};
4341
4309
  for (const file of files) {
4342
4310
  const filePath = path17.join(rootDir, file);
4343
- if (!fs15.existsSync(filePath)) continue;
4344
- const content = fs15.readFileSync(filePath, "utf-8");
4311
+ if (!fs14.existsSync(filePath)) continue;
4312
+ const content = fs14.readFileSync(filePath, "utf-8");
4345
4313
  const parsed = parseEnvFile(content, merged);
4346
4314
  Object.assign(merged, parsed);
4347
4315
  }
@@ -4354,7 +4322,7 @@ function loadEnv(rootDir) {
4354
4322
 
4355
4323
  // src/cli/createAppCore.ts
4356
4324
  import fs21 from "fs";
4357
- import path26 from "path";
4325
+ import path27 from "path";
4358
4326
 
4359
4327
  // src/router/sortRoutes.ts
4360
4328
  function sortRoutes(routes) {
@@ -4418,7 +4386,7 @@ import {
4418
4386
  import { createSecureServer as createHttp2SecureServer } from "http2";
4419
4387
  import { readFileSync } from "fs";
4420
4388
  import { Readable as Readable2 } from "stream";
4421
- import path19 from "path";
4389
+ import path20 from "path";
4422
4390
 
4423
4391
  // src/router/matchRoute.ts
4424
4392
  var httpIndexCache = /* @__PURE__ */ new WeakMap();
@@ -5039,10 +5007,18 @@ function formatSetCookie(name, value, options) {
5039
5007
  if (options?.sameSite) cookie += `; SameSite=${options.sameSite}`;
5040
5008
  return cookie;
5041
5009
  }
5042
- function createContext(request, params, config = {}, ip = "", registries) {
5043
- return createContextFromUrl(request, new URL(request.url), params, config, ip, registries);
5010
+ function createContext(request, params, config = {}, ip = "", registries, resourcesDir) {
5011
+ return createContextFromUrl(
5012
+ request,
5013
+ new URL(request.url),
5014
+ params,
5015
+ config,
5016
+ ip,
5017
+ registries,
5018
+ resourcesDir
5019
+ );
5044
5020
  }
5045
- function createContextFromUrl(request, url, params, config = {}, ip = "", registries) {
5021
+ function createContextFromUrl(request, url, params, config = {}, ip = "", registries, resourcesDir) {
5046
5022
  const meta = { headers: {}, setCookies: [] };
5047
5023
  const parsedCookies = parseCookies(request.headers.get("cookie") ?? "");
5048
5024
  const cookiesObj = {};
@@ -5070,6 +5046,9 @@ function createContextFromUrl(request, url, params, config = {}, ip = "", regist
5070
5046
  ua: request.headers.get("user-agent") ?? "",
5071
5047
  cookies: cookiesObj,
5072
5048
  config,
5049
+ // 运行时资源根目录(HTTP/WS 链路由 createServer/handleWsUpgrade 传入;
5050
+ // testing 直调未传时为 undefined,见 contextTypes.resourcesDir 注释)
5051
+ resourcesDir,
5073
5052
  meta,
5074
5053
  setStatus(status) {
5075
5054
  meta.status = status;
@@ -5889,7 +5868,15 @@ function etag(options = {}) {
5889
5868
  // src/server/handleWsUpgrade.ts
5890
5869
  import fs16 from "fs";
5891
5870
  import { WebSocketServer, WebSocket } from "ws";
5871
+ import path19 from "path";
5872
+
5873
+ // src/cli/copyResources.ts
5892
5874
  import path18 from "path";
5875
+ import fs15 from "fs";
5876
+ var RESOURCES_OUTPUT_DIR = "resources";
5877
+ function resolveResourcesDir(rootDir, dist) {
5878
+ return path18.resolve(rootDir, dist, RESOURCES_OUTPUT_DIR);
5879
+ }
5893
5880
 
5894
5881
  // src/server/serverUtils.ts
5895
5882
  function nodeHttpToWebHeaders(req) {
@@ -6127,6 +6114,7 @@ function attachWebSocket(options) {
6127
6114
  trustedProxy = false,
6128
6115
  registries
6129
6116
  } = options;
6117
+ const resourcesDir = resolveResourcesDir(rootDir, dist);
6130
6118
  const wss = new WebSocketServer({ noServer: true });
6131
6119
  server.on("upgrade", async (req, socket, head) => {
6132
6120
  try {
@@ -6158,7 +6146,14 @@ function attachWebSocket(options) {
6158
6146
  const host = req.headers.host ?? "localhost";
6159
6147
  const fullUrl = new URL(`http://${host}${req.url ?? "/"}`);
6160
6148
  const request = new Request(fullUrl.toString(), { method: "GET", headers });
6161
- const ctx = createContext(request, params, config, getClientIp(req, trustedProxy), registries);
6149
+ const ctx = createContext(
6150
+ request,
6151
+ params,
6152
+ config,
6153
+ getClientIp(req, trustedProxy),
6154
+ registries,
6155
+ resourcesDir
6156
+ );
6162
6157
  const meta = ctx.meta;
6163
6158
  let upgraded = false;
6164
6159
  const finalHandler = async () => {
@@ -6202,7 +6197,7 @@ function attachWebSocket(options) {
6202
6197
  }
6203
6198
  let handlers;
6204
6199
  try {
6205
- const absoluteFilePath = path18.resolve(rootDir, route.filePath);
6200
+ const absoluteFilePath = path19.resolve(rootDir, route.filePath);
6206
6201
  handlers = await loadWsHandler(absoluteFilePath, ctx, rootDir);
6207
6202
  } catch (err) {
6208
6203
  const reason = err instanceof Error ? err.message : String(err);
@@ -6362,6 +6357,7 @@ function createServer(options) {
6362
6357
  trustedProxy = false
6363
6358
  } = options;
6364
6359
  const routesRef = { current: routes, wsCurrent: wsRoutes ?? [] };
6360
+ const resourcesDir = resolveResourcesDir(rootDir, dist);
6365
6361
  const configMiddlewares = [];
6366
6362
  if (compressionOption) {
6367
6363
  const compOpts = typeof compressionOption === "object" ? compressionOption : {};
@@ -6408,7 +6404,8 @@ function createServer(options) {
6408
6404
  bodyLimit,
6409
6405
  trustedProxy,
6410
6406
  registries,
6411
- routesRef.wsCurrent
6407
+ routesRef.wsCurrent,
6408
+ resourcesDir
6412
6409
  ).catch((err) => {
6413
6410
  console.error("[faapi] Request pipeline failed after error response attempt:", err);
6414
6411
  try {
@@ -6434,7 +6431,7 @@ function createServer(options) {
6434
6431
  });
6435
6432
  return { server, routesRef };
6436
6433
  }
6437
- function prepareRequest(req, request, url, config, trustedProxy, registries, preMatched) {
6434
+ function prepareRequest(req, request, url, config, trustedProxy, registries, preMatched, resourcesDir) {
6438
6435
  const method = request.method.toUpperCase();
6439
6436
  const urlPath = url.pathname;
6440
6437
  const ctx = createContextFromUrl(
@@ -6443,7 +6440,8 @@ function prepareRequest(req, request, url, config, trustedProxy, registries, pre
6443
6440
  preMatched?.params ?? {},
6444
6441
  config,
6445
6442
  getClientIp(req, trustedProxy),
6446
- registries
6443
+ registries,
6444
+ resourcesDir
6447
6445
  );
6448
6446
  const meta = ctx.meta;
6449
6447
  return { request, url, ctx, meta, method, urlPath };
@@ -6462,7 +6460,7 @@ function getRoutePaths(route, rootDir, dist) {
6462
6460
  let cached = routePathCache.get(route);
6463
6461
  if (!cached) {
6464
6462
  cached = {
6465
- absFilePath: path19.resolve(rootDir, route.filePath),
6463
+ absFilePath: path20.resolve(rootDir, route.filePath),
6466
6464
  schemaPath: getRuntimeSchemaPath(route.filePath, dist, rootDir)
6467
6465
  };
6468
6466
  routePathCache.set(route, cached);
@@ -6585,7 +6583,7 @@ async function sendErrorResponse(err, meta, res, onError, ctx) {
6585
6583
  }
6586
6584
  }
6587
6585
  }
6588
- async function handleRequest(routes, rootDir, dist, req, res, outerMiddlewares, onError, config, globalInjectors, bodyLimit, trustedProxy, registries, wsRoutes) {
6586
+ async function handleRequest(routes, rootDir, dist, req, res, outerMiddlewares, onError, config, globalInjectors, bodyLimit, trustedProxy, registries, wsRoutes, resourcesDir) {
6589
6587
  let meta = { headers: {}, setCookies: [] };
6590
6588
  let ctx;
6591
6589
  let sseEarlySent = false;
@@ -6605,7 +6603,8 @@ async function handleRequest(routes, rootDir, dist, req, res, outerMiddlewares,
6605
6603
  config,
6606
6604
  trustedProxy,
6607
6605
  registries,
6608
- preMatched
6606
+ preMatched,
6607
+ resourcesDir
6609
6608
  );
6610
6609
  ctx = prepared.ctx;
6611
6610
  meta = prepared.meta;
@@ -6682,7 +6681,7 @@ function applyPluginWrappers(server, handlerWrappers, upgradeWrappers) {
6682
6681
 
6683
6682
  // src/cli/generateRoutes.ts
6684
6683
  import fs17 from "fs";
6685
- import path20 from "path";
6684
+ import path21 from "path";
6686
6685
  async function hydrateRoutes(manifest) {
6687
6686
  const hydrateRoute = (serialized) => ({
6688
6687
  method: serialized.method,
@@ -6726,18 +6725,18 @@ function createIdleTaskDriver() {
6726
6725
  }
6727
6726
 
6728
6727
  // src/cli/loadPlugins.ts
6729
- import path22 from "path";
6728
+ import path23 from "path";
6730
6729
  import fs19 from "fs";
6731
6730
  import { pathToFileURL as pathToFileURL3 } from "url";
6732
6731
 
6733
6732
  // src/cli/compileConfig.ts
6734
- import path21 from "path";
6733
+ import path22 from "path";
6735
6734
  import fs18 from "fs";
6736
6735
  async function compileProjectModules(entryPoints, rootDir, dist) {
6737
6736
  const { insideFiles, outsideFiles } = await collectRelativeImports(entryPoints, rootDir);
6738
6737
  const esbuild = await import("esbuild");
6739
6738
  const aliasPlugins = buildAliasPlugins(rootDir);
6740
- const absDist = path21.resolve(rootDir, dist);
6739
+ const absDist = path22.resolve(rootDir, dist);
6741
6740
  await esbuild.build({
6742
6741
  entryPoints: [...entryPoints, ...outsideFiles],
6743
6742
  outdir: absDist,
@@ -6751,7 +6750,7 @@ async function compileProjectModules(entryPoints, rootDir, dist) {
6751
6750
  logLevel: "silent"
6752
6751
  });
6753
6752
  if (insideFiles.length > 0) {
6754
- const appOutbase = path21.resolve(rootDir, "src");
6753
+ const appOutbase = path22.resolve(rootDir, "src");
6755
6754
  await esbuild.build({
6756
6755
  entryPoints: insideFiles,
6757
6756
  outdir: absDist,
@@ -6832,7 +6831,7 @@ async function loadPlugins(declarations, ctx, rootDir, dist) {
6832
6831
  var TS_EXTS = /\.(ts|tsx|mts|cts)$/;
6833
6832
  var JS_EXTS = /\.(js|mjs|cjs|jsx)$/;
6834
6833
  function resolveLocalPluginSource(specifier, baseDir) {
6835
- const base = path22.isAbsolute(specifier) ? specifier : path22.resolve(baseDir, specifier);
6834
+ const base = path23.isAbsolute(specifier) ? specifier : path23.resolve(baseDir, specifier);
6836
6835
  if ((TS_EXTS.test(base) || JS_EXTS.test(base)) && fs19.existsSync(base)) return base;
6837
6836
  for (const ext of [".ts", ".js"]) {
6838
6837
  if (fs19.existsSync(base + ext)) return base + ext;
@@ -6843,16 +6842,16 @@ function resolveLocalPluginSource(specifier, baseDir) {
6843
6842
  return null;
6844
6843
  }
6845
6844
  function mirrorProductPath(sourcePath, baseDir, distDir) {
6846
- let rel = path22.relative(baseDir, sourcePath).replace(/\\/g, "/");
6845
+ let rel = path23.relative(baseDir, sourcePath).replace(/\\/g, "/");
6847
6846
  if (rel.startsWith("src/")) rel = rel.slice(4);
6848
- return path22.resolve(distDir, rel.replace(TS_EXTS, ".js"));
6847
+ return path23.resolve(distDir, rel.replace(TS_EXTS, ".js"));
6849
6848
  }
6850
6849
  async function importPluginModule(specifier, baseDir, dist) {
6851
6850
  const isRelative = specifier.startsWith("./") || specifier.startsWith("../");
6852
- if (!isRelative && !path22.isAbsolute(specifier)) {
6851
+ if (!isRelative && !path23.isAbsolute(specifier)) {
6853
6852
  return import(specifier);
6854
6853
  }
6855
- const distDir = dist ? path22.resolve(baseDir, dist) : null;
6854
+ const distDir = dist ? path23.resolve(baseDir, dist) : null;
6856
6855
  const sourcePath = resolveLocalPluginSource(specifier, baseDir);
6857
6856
  const productPath = sourcePath && distDir ? mirrorProductPath(sourcePath, baseDir, distDir) : null;
6858
6857
  if (productPath !== null && fs19.existsSync(productPath) && (sourcePath === null || isProductFresh(sourcePath, productPath))) {
@@ -6864,7 +6863,7 @@ async function importPluginModule(specifier, baseDir, dist) {
6864
6863
  `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.`
6865
6864
  );
6866
6865
  }
6867
- const relFromRoot = path22.relative(baseDir, sourcePath).replace(/\\/g, "/");
6866
+ const relFromRoot = path23.relative(baseDir, sourcePath).replace(/\\/g, "/");
6868
6867
  if (relFromRoot.startsWith("src/")) {
6869
6868
  await ensureCompiled(sourcePath, baseDir, dist);
6870
6869
  } else {
@@ -6882,7 +6881,7 @@ async function importPluginModule(specifier, baseDir, dist) {
6882
6881
  `${specifier}/index.ts`,
6883
6882
  `${specifier}.js`,
6884
6883
  `${specifier}/index.js`
6885
- ].map((c) => path22.isAbsolute(c) ? c : path22.join(baseDir, c));
6884
+ ].map((c) => path23.isAbsolute(c) ? c : path23.join(baseDir, c));
6886
6885
  throw new Error(
6887
6886
  `Cannot find local plugin "${specifier}" (resolved from ${baseDir}). Expected one of:
6888
6887
  ${candidates.join("\n ")}
@@ -6908,10 +6907,10 @@ function resolveDeclaration(decl) {
6908
6907
 
6909
6908
  // src/cli/manifestLoader.ts
6910
6909
  import fs20 from "fs";
6911
- import path25 from "path";
6910
+ import path26 from "path";
6912
6911
 
6913
6912
  // src/cli/generateAgentArtifacts.ts
6914
- import path23 from "path";
6913
+ import path24 from "path";
6915
6914
 
6916
6915
  // src/ast/extractAgentMetadata.ts
6917
6916
  import ts11 from "typescript";
@@ -6919,12 +6918,21 @@ init_resolveTypeNode();
6919
6918
  function extractAgentMetadata(program, filePath, pathMeta) {
6920
6919
  const sourceFile = program.getSourceFile(filePath);
6921
6920
  if (!sourceFile) return null;
6921
+ const runOwner = findRunExportOwner(sourceFile);
6922
+ if (runOwner) {
6923
+ throw SchemaExtractionError.at(
6924
+ runOwner,
6925
+ "run",
6926
+ "agent \u81EA\u5B9A\u4E49 run \u5DF2\u79FB\u9664\u2014\u2014agent \u7EDF\u4E00\u4E3A\u58F0\u660E\u5F0F\u6267\u884C\uFF08config + \u9ED8\u8BA4 reactLoop\uFF09\u3002\u7F16\u6392\u573A\u666F\u8BF7\u6CE8\u518C tool\uFF08src/tools/ \u4E0B\u5BFC\u51FA\u51FD\u6570\uFF0C\u6709 schema/trace/\u9274\u6743\u94A9\u5B50\uFF09\uFF0C\u591A agent \u534F\u4F5C\u7528 config.agents \u58F0\u660E sub-agent",
6927
+ sourceFile
6928
+ );
6929
+ }
6922
6930
  const configFound = findConfigExport(sourceFile);
6923
6931
  if (!configFound) {
6924
6932
  throw SchemaExtractionError.at(
6925
6933
  sourceFile,
6926
6934
  "config.systemPrompt",
6927
- "agent \u5FC5\u586B\u2014\u2014\u8BF7\u5728 config \u5757\u58F0\u660E\u7CFB\u7EDF\u63D0\u793A\u8BCD\uFF08\u5B57\u7B26\u4E32\u5B57\u9762\u91CF\u3001\u65E0\u63D2\u503C\u6A21\u677F\u5B57\u7B26\u4E32\u6216\u5176 + \u62FC\u63A5\uFF09",
6935
+ "agent \u5FC5\u586B\u2014\u2014\u8BF7\u5728 config \u5757\u58F0\u660E systemPrompt\uFF08\u5B57\u7B26\u4E32\u5B57\u9762\u91CF\u3001\u65E0\u63D2\u503C\u6A21\u677F\u5B57\u7B26\u4E32\u6216\u5176 + \u62FC\u63A5\uFF09\u6216 systemPromptFile\uFF08\u76F8\u5BF9 resources \u76EE\u5F55\u7684\u8DEF\u5F84\u5B57\u9762\u91CF\uFF09\u4E8C\u9009\u4E00",
6928
6936
  sourceFile
6929
6937
  );
6930
6938
  }
@@ -6940,16 +6948,13 @@ function extractAgentMetadata(program, filePath, pathMeta) {
6940
6948
  const jsDoc = getJSDocFromNode(configFound.jsDocOwner);
6941
6949
  const description = extractDescription(jsDoc);
6942
6950
  const agentNameOverride = extractJSDocTagValue(jsDoc, "agent");
6943
- const { systemPrompt, tools, agents, model, maxTurns, inputDescription } = extractConfigFields(
6944
- objectLiteral,
6945
- sourceFile
6946
- );
6951
+ const { systemPrompt, systemPromptFile, tools, agents, model, maxTurns, inputDescription } = extractConfigFields(objectLiteral, sourceFile);
6947
6952
  return {
6948
6953
  name: agentNameOverride ?? pathMeta.name,
6949
6954
  description,
6950
6955
  filePath: pathMeta.filePath,
6951
- hasRun: pathMeta.hasRun,
6952
6956
  systemPrompt,
6957
+ systemPromptFile,
6953
6958
  tools,
6954
6959
  agents,
6955
6960
  model,
@@ -6957,6 +6962,25 @@ function extractAgentMetadata(program, filePath, pathMeta) {
6957
6962
  inputDescription
6958
6963
  };
6959
6964
  }
6965
+ function findRunExportOwner(sourceFile) {
6966
+ let result = null;
6967
+ ts11.forEachChild(sourceFile, (node) => {
6968
+ if (result) return;
6969
+ if (ts11.isFunctionDeclaration(node) && hasExportModifier(node) && node.name?.text === "run") {
6970
+ result = node;
6971
+ return;
6972
+ }
6973
+ if (ts11.isVariableStatement(node) && hasExportModifier(node)) {
6974
+ for (const decl of node.declarationList.declarations) {
6975
+ if (ts11.isIdentifier(decl.name) && decl.name.text === "run") {
6976
+ result = node;
6977
+ return;
6978
+ }
6979
+ }
6980
+ }
6981
+ });
6982
+ return result;
6983
+ }
6960
6984
  function findConfigExport(sourceFile) {
6961
6985
  let result = null;
6962
6986
  ts11.forEachChild(sourceFile, (node) => {
@@ -6999,6 +7023,7 @@ function getReturnObjectLiteral(fn) {
6999
7023
  }
7000
7024
  function extractConfigFields(objLit, sourceFile) {
7001
7025
  let systemPrompt;
7026
+ let systemPromptFile;
7002
7027
  let tools;
7003
7028
  let agents;
7004
7029
  let model;
@@ -7022,6 +7047,9 @@ function extractConfigFields(objLit, sourceFile) {
7022
7047
  case "systemPrompt":
7023
7048
  systemPrompt = requireStringValue(prop, "systemPrompt", sourceFile);
7024
7049
  break;
7050
+ case "systemPromptFile":
7051
+ systemPromptFile = requireStringValue(prop, "systemPromptFile", sourceFile);
7052
+ break;
7025
7053
  case "tools":
7026
7054
  tools = requireStringArrayValue(prop, "tools", sourceFile);
7027
7055
  break;
@@ -7041,20 +7069,28 @@ function extractConfigFields(objLit, sourceFile) {
7041
7069
  throw SchemaExtractionError.at(
7042
7070
  prop,
7043
7071
  `config.${propName}`,
7044
- "\u672A\u77E5 config \u5B57\u6BB5\u2014\u2014\u4EC5\u652F\u6301 systemPrompt / tools / agents / model / maxTurns / inputDescription",
7072
+ "\u672A\u77E5 config \u5B57\u6BB5\u2014\u2014\u4EC5\u652F\u6301 systemPrompt / systemPromptFile / tools / agents / model / maxTurns / inputDescription",
7045
7073
  sourceFile
7046
7074
  );
7047
7075
  }
7048
7076
  }
7049
- if (systemPrompt === void 0) {
7077
+ if (systemPrompt !== void 0 && systemPromptFile !== void 0) {
7050
7078
  throw SchemaExtractionError.at(
7051
7079
  objLit,
7052
7080
  "config.systemPrompt",
7053
- "agent \u5FC5\u586B\u2014\u2014\u8BF7\u5728 config \u5757\u58F0\u660E\u7CFB\u7EDF\u63D0\u793A\u8BCD\uFF08\u5B57\u7B26\u4E32\u5B57\u9762\u91CF\u3001\u65E0\u63D2\u503C\u6A21\u677F\u5B57\u7B26\u4E32\u6216\u5176 + \u62FC\u63A5\uFF09",
7081
+ "systemPrompt \u4E0E systemPromptFile \u4E92\u65A5\u2014\u2014\u5185\u8054\u7CFB\u7EDF\u63D0\u793A\u8BCD\u4E0E resources \u6587\u4EF6\u4E8C\u9009\u4E00",
7054
7082
  sourceFile
7055
7083
  );
7056
7084
  }
7057
- return { systemPrompt, tools, agents, model, maxTurns, inputDescription };
7085
+ if (systemPrompt === void 0 && systemPromptFile === void 0) {
7086
+ throw SchemaExtractionError.at(
7087
+ objLit,
7088
+ "config.systemPrompt",
7089
+ "agent \u5FC5\u586B\u2014\u2014\u8BF7\u5728 config \u5757\u58F0\u660E systemPrompt\uFF08\u5B57\u7B26\u4E32\u5B57\u9762\u91CF\u3001\u65E0\u63D2\u503C\u6A21\u677F\u5B57\u7B26\u4E32\u6216\u5176 + \u62FC\u63A5\uFF09\u6216 systemPromptFile\uFF08\u76F8\u5BF9 resources \u76EE\u5F55\u7684\u8DEF\u5F84\u5B57\u9762\u91CF\uFF09\u4E8C\u9009\u4E00",
7090
+ sourceFile
7091
+ );
7092
+ }
7093
+ return { systemPrompt, systemPromptFile, tools, agents, model, maxTurns, inputDescription };
7058
7094
  }
7059
7095
  function getPropertyName(name) {
7060
7096
  if (ts11.isIdentifier(name)) return name.text;
@@ -7137,8 +7173,8 @@ function serializeAgents(agents, dist = "dist") {
7137
7173
  return agents.map((a) => ({
7138
7174
  name: a.name,
7139
7175
  description: a.description,
7140
- hasRun: a.hasRun,
7141
7176
  systemPrompt: a.systemPrompt,
7177
+ systemPromptFile: a.systemPromptFile,
7142
7178
  tools: a.tools,
7143
7179
  agents: a.agents,
7144
7180
  model: a.model,
@@ -7158,8 +7194,8 @@ function hydrateAgents(manifest) {
7158
7194
  name: a.name,
7159
7195
  description: a.description ?? void 0,
7160
7196
  filePath: a.filePath,
7161
- hasRun: a.hasRun,
7162
7197
  systemPrompt: a.systemPrompt ?? void 0,
7198
+ systemPromptFile: a.systemPromptFile ?? void 0,
7163
7199
  tools: a.tools ?? void 0,
7164
7200
  agents: a.agents ?? void 0,
7165
7201
  model: a.model ?? void 0,
@@ -7191,14 +7227,13 @@ function validateAgentList(metadata) {
7191
7227
  }
7192
7228
  async function generateAgentArtifacts(agents, rootDir, dist) {
7193
7229
  const metadata = [];
7194
- const programByFile = createPrograms(agents.map((m) => path23.resolve(rootDir, m.filePath)));
7230
+ const programByFile = createPrograms(agents.map((m) => path24.resolve(rootDir, m.filePath)));
7195
7231
  for (const manifest of agents) {
7196
- const absPath = path23.resolve(rootDir, manifest.filePath);
7232
+ const absPath = path24.resolve(rootDir, manifest.filePath);
7197
7233
  const program = programByFile.get(absPath);
7198
7234
  const result = extractAgentMetadata(program, absPath, {
7199
7235
  name: manifest.name,
7200
- filePath: manifest.filePath,
7201
- hasRun: manifest.hasRun
7236
+ filePath: manifest.filePath
7202
7237
  });
7203
7238
  if (!result) {
7204
7239
  throw new Error(
@@ -7209,14 +7244,14 @@ async function generateAgentArtifacts(agents, rootDir, dist) {
7209
7244
  }
7210
7245
  validateAgentList(metadata);
7211
7246
  const serialized = serializeAgents(metadata, dist);
7212
- const agentsPath = path23.resolve(rootDir, dist, AGENTS_FILE);
7247
+ const agentsPath = path24.resolve(rootDir, dist, AGENTS_FILE);
7213
7248
  await writeAgentsModule(serialized, agentsPath);
7214
7249
  return metadata;
7215
7250
  }
7216
7251
 
7217
7252
  // src/cli/generateTaskArtifacts.ts
7218
7253
  init_generateZodArtifacts();
7219
- import path24 from "path";
7254
+ import path25 from "path";
7220
7255
  init_createProgram();
7221
7256
  init_atomicWrite();
7222
7257
  init_extractHandlerTypes();
@@ -7254,7 +7289,7 @@ function collectTaskSchemaSources(tasks, rootDir) {
7254
7289
  const tasksByFile = /* @__PURE__ */ new Map();
7255
7290
  for (const task of tasks) {
7256
7291
  if (!task.inputTypeName) continue;
7257
- const absPath = path24.resolve(rootDir, task.filePath);
7292
+ const absPath = path25.resolve(rootDir, task.filePath);
7258
7293
  let list = tasksByFile.get(absPath);
7259
7294
  if (!list) {
7260
7295
  list = [];
@@ -7311,9 +7346,9 @@ function generateTaskSchemaFileSource(sources, resolveType, helpersImportPath) {
7311
7346
  async function generateTaskArtifacts(manifests, rootDir, dist) {
7312
7347
  const metadata = [];
7313
7348
  if (manifests.length > 0) {
7314
- const programByFile = createPrograms(manifests.map((m) => path24.resolve(rootDir, m.filePath)));
7349
+ const programByFile = createPrograms(manifests.map((m) => path25.resolve(rootDir, m.filePath)));
7315
7350
  for (const manifest of manifests) {
7316
- const absPath = path24.resolve(rootDir, manifest.filePath);
7351
+ const absPath = path25.resolve(rootDir, manifest.filePath);
7317
7352
  const result = extractToolMetadata(programByFile.get(absPath), absPath, "run", {
7318
7353
  name: manifest.name,
7319
7354
  filePath: manifest.filePath
@@ -7324,7 +7359,7 @@ async function generateTaskArtifacts(manifests, rootDir, dist) {
7324
7359
  }
7325
7360
  }
7326
7361
  const serialized = serializeTasks(manifests, dist);
7327
- const tasksPath = path24.resolve(rootDir, dist, TASKS_FILE);
7362
+ const tasksPath = path25.resolve(rootDir, dist, TASKS_FILE);
7328
7363
  await writeTasksModule(serialized, tasksPath);
7329
7364
  if (metadata.length === 0) {
7330
7365
  return hydrateTasks(serialized);
@@ -7346,7 +7381,7 @@ async function generateTaskArtifacts(manifests, rootDir, dist) {
7346
7381
  var TOOLS_FILE2 = "faapi-tools.js";
7347
7382
  var AGENTS_FILE2 = "faapi-agents.js";
7348
7383
  async function loadAndHydrateTools(rootDir, dist, registries = defaultRegistries) {
7349
- const toolsPath = path25.resolve(rootDir, dist, TOOLS_FILE2);
7384
+ const toolsPath = path26.resolve(rootDir, dist, TOOLS_FILE2);
7350
7385
  if (!fs20.existsSync(toolsPath)) {
7351
7386
  return [];
7352
7387
  }
@@ -7356,7 +7391,7 @@ async function loadAndHydrateTools(rootDir, dist, registries = defaultRegistries
7356
7391
  return hydrated;
7357
7392
  }
7358
7393
  async function loadAndHydrateAgents(rootDir, dist, registries = defaultRegistries) {
7359
- const agentsPath = path25.resolve(rootDir, dist, AGENTS_FILE2);
7394
+ const agentsPath = path26.resolve(rootDir, dist, AGENTS_FILE2);
7360
7395
  if (!fs20.existsSync(agentsPath)) {
7361
7396
  return [];
7362
7397
  }
@@ -7372,7 +7407,7 @@ function getTaskDriverOptions(config) {
7372
7407
  return void 0;
7373
7408
  }
7374
7409
  async function loadAndHydrateTasks(rootDir, dist, registries = defaultRegistries) {
7375
- const tasksPath = path25.resolve(rootDir, dist, TASKS_FILE);
7410
+ const tasksPath = path26.resolve(rootDir, dist, TASKS_FILE);
7376
7411
  if (!fs20.existsSync(tasksPath)) {
7377
7412
  return [];
7378
7413
  }
@@ -7550,8 +7585,7 @@ function logStartupSummary(routes, wsRoutes, tools, agents, taskMetas, p) {
7550
7585
  if (agents.length > 0) {
7551
7586
  console.log(`- Loaded ${agents.length} agent(s):`);
7552
7587
  for (const agent of agents) {
7553
- const exports = agent.hasRun ? "run" : "-";
7554
- console.log(` ${agent.name} [${exports}] ${agent.filePath}`);
7588
+ console.log(` ${agent.name} ${agent.filePath}`);
7555
7589
  }
7556
7590
  }
7557
7591
  if (taskMetas.length > 0) {
@@ -7565,7 +7599,8 @@ function logStartupSummary(routes, wsRoutes, tools, agents, taskMetas, p) {
7565
7599
  async function createAppBase(options) {
7566
7600
  const rootDir = options?.rootDir ?? process.cwd();
7567
7601
  const dist = options?.dist ?? process.env.FAAPI_DIST ?? DEFAULT_DIST;
7568
- const routesPath = path26.resolve(rootDir, dist, ROUTES_FILE);
7602
+ const resourcesDir = resolveResourcesDir(rootDir, dist);
7603
+ const routesPath = path27.resolve(rootDir, dist, ROUTES_FILE);
7569
7604
  if (!fs21.existsSync(routesPath)) {
7570
7605
  throw new Error(
7571
7606
  `[faapi] ${dist}/${ROUTES_FILE} \u4E0D\u5B58\u5728\uFF0C\u8BF7\u5148\u6267\u884C \`faapi build\`\uFF08\u6216 \`faapi dev\`\uFF09\u751F\u6210\u4EA7\u7269\u3002`
@@ -7587,6 +7622,7 @@ async function createAppBase(options) {
7587
7622
  registry: registries.task,
7588
7623
  rootDir,
7589
7624
  config,
7625
+ resourcesDir,
7590
7626
  registries: createTaskRegistriesView(registries),
7591
7627
  driver: taskDriver,
7592
7628
  onFailed: config?.task?.onFailed
@@ -7628,6 +7664,7 @@ async function createAppBase(options) {
7628
7664
  config?.plugins,
7629
7665
  {
7630
7666
  rootDir,
7667
+ resourcesDir,
7631
7668
  registries,
7632
7669
  routes: sorted,
7633
7670
  getRoutes: () => sorted,
@@ -7645,6 +7682,8 @@ async function createAppBase(options) {
7645
7682
  routes: sorted,
7646
7683
  wsRoutes,
7647
7684
  rootDir,
7685
+ dist,
7686
+ resourcesDir,
7648
7687
  tasks: taskQueue,
7649
7688
  async listen(listenPort) {
7650
7689
  const envPort = process.env.PORT ? Number(process.env.PORT) : void 0;
@@ -7656,7 +7695,8 @@ async function createAppBase(options) {
7656
7695
  routes: sorted,
7657
7696
  server,
7658
7697
  registries,
7659
- tasks: taskQueue
7698
+ tasks: taskQueue,
7699
+ resourcesDir
7660
7700
  });
7661
7701
  console.log("- onBoot hook executed");
7662
7702
  } catch (err) {
@@ -7691,7 +7731,8 @@ async function createAppBase(options) {
7691
7731
  routes: sorted,
7692
7732
  server,
7693
7733
  registries,
7694
- tasks: taskQueue
7734
+ tasks: taskQueue,
7735
+ resourcesDir
7695
7736
  });
7696
7737
  console.log("- onReady hook executed");
7697
7738
  }
@@ -7715,7 +7756,8 @@ async function createAppBase(options) {
7715
7756
  routes: sorted,
7716
7757
  server,
7717
7758
  registries,
7718
- tasks: taskQueue
7759
+ tasks: taskQueue,
7760
+ resourcesDir
7719
7761
  });
7720
7762
  }
7721
7763
  registries.tool.clear();
@@ -7781,7 +7823,7 @@ import path30 from "path";
7781
7823
 
7782
7824
  // src/router/scanRoutes.ts
7783
7825
  import fg3 from "fast-glob";
7784
- import path27 from "path";
7826
+ import path28 from "path";
7785
7827
  import fs22 from "fs";
7786
7828
 
7787
7829
  // src/router/constants.ts
@@ -7852,26 +7894,26 @@ function extractExportsFromSource(source) {
7852
7894
  return names;
7853
7895
  }
7854
7896
  function collectMiddlewarePaths(routeFilePath, rootDir, dist) {
7855
- const routeDir = path27.dirname(routeFilePath);
7856
- const resolvedRoot = path27.resolve(rootDir);
7897
+ const routeDir = path28.dirname(routeFilePath);
7898
+ const resolvedRoot = path28.resolve(rootDir);
7857
7899
  const paths = [];
7858
- let currentDir = path27.resolve(rootDir, routeDir);
7900
+ let currentDir = path28.resolve(rootDir, routeDir);
7859
7901
  while (true) {
7860
7902
  if (dist) {
7861
- const mwTsPath = path27.join(currentDir, "middlewares.ts");
7862
- const mwJsPath = path27.join(currentDir, "middlewares.js");
7863
- const absTsPath = path27.resolve(rootDir, mwTsPath);
7864
- const absJsPath = path27.resolve(rootDir, mwJsPath);
7903
+ const mwTsPath = path28.join(currentDir, "middlewares.ts");
7904
+ const mwJsPath = path28.join(currentDir, "middlewares.js");
7905
+ const absTsPath = path28.resolve(rootDir, mwTsPath);
7906
+ const absJsPath = path28.resolve(rootDir, mwJsPath);
7865
7907
  const absMwPath = fs22.existsSync(absTsPath) ? absTsPath : fs22.existsSync(absJsPath) ? absJsPath : null;
7866
7908
  if (absMwPath) {
7867
- const relMwPath = path27.relative(rootDir, absMwPath);
7868
- const prodAbsPath = path27.resolve(rootDir, toProdFilePath(relMwPath, dist));
7909
+ const relMwPath = path28.relative(rootDir, absMwPath);
7910
+ const prodAbsPath = path28.resolve(rootDir, toProdFilePath(relMwPath, dist));
7869
7911
  paths.push(prodAbsPath);
7870
7912
  }
7871
7913
  } else {
7872
7914
  for (const ext of [".ts", ".js"]) {
7873
- const mwPath = path27.join(currentDir, `middlewares${ext}`);
7874
- const absMwPath = path27.resolve(rootDir, mwPath);
7915
+ const mwPath = path28.join(currentDir, `middlewares${ext}`);
7916
+ const absMwPath = path28.resolve(rootDir, mwPath);
7875
7917
  if (fs22.existsSync(absMwPath)) {
7876
7918
  paths.push(absMwPath);
7877
7919
  break;
@@ -7879,7 +7921,7 @@ function collectMiddlewarePaths(routeFilePath, rootDir, dist) {
7879
7921
  }
7880
7922
  }
7881
7923
  if (currentDir === resolvedRoot) break;
7882
- const parentDir = path27.dirname(currentDir);
7924
+ const parentDir = path28.dirname(currentDir);
7883
7925
  if (parentDir === currentDir) break;
7884
7926
  currentDir = parentDir;
7885
7927
  }
@@ -7898,7 +7940,7 @@ async function scanRoutes(rootDir, patterns, dist) {
7898
7940
  const normalizedFile = file.replace(/\\/g, "/");
7899
7941
  const fileName = normalizedFile.split("/").pop();
7900
7942
  if (fileName === "handler.ts" || fileName === "handler.js") {
7901
- const absPath = path27.resolve(rootDir, normalizedFile);
7943
+ const absPath = path28.resolve(rootDir, normalizedFile);
7902
7944
  const urlPath = filePathToUrlPath(normalizedFile);
7903
7945
  const paramNames = extractParamNames(urlPath);
7904
7946
  const isDynamic = paramNames.length > 0;
@@ -7947,7 +7989,7 @@ async function scanRoutes(rootDir, patterns, dist) {
7947
7989
 
7948
7990
  // src/tools/scanTools.ts
7949
7991
  import fg4 from "fast-glob";
7950
- import path28 from "path";
7992
+ import path29 from "path";
7951
7993
  import fs23 from "fs";
7952
7994
  var TOOL_PATTERNS = ["src/tools/**/*.ts"];
7953
7995
  var TOOL_EXPORT_RE = new RegExp(
@@ -7998,7 +8040,7 @@ async function scanTools(rootDir, patterns) {
7998
8040
  if (fileName !== "handler.ts" && fileName !== "handler.js") {
7999
8041
  continue;
8000
8042
  }
8001
- const absPath = path28.resolve(rootDir, normalizedFile);
8043
+ const absPath = path29.resolve(rootDir, normalizedFile);
8002
8044
  const source = await fs23.promises.readFile(absPath, "utf8").catch(() => "");
8003
8045
  const exportNames = extractToolExportsFromSource(source);
8004
8046
  const namespace = filePathToToolNamespace(normalizedFile);
@@ -8023,10 +8065,7 @@ async function scanTools(rootDir, patterns) {
8023
8065
 
8024
8066
  // src/agents/scanAgents.ts
8025
8067
  import fg5 from "fast-glob";
8026
- import path29 from "path";
8027
- import fs24 from "fs";
8028
8068
  var DEFAULT_AGENT_PATTERNS = ["src/agents/**/handler.ts"];
8029
- var RUN_EXPORT_RE = /export\s+(?:async\s+)?(?:function\s+|const\s+)run\b/;
8030
8069
  function extractAgentNameFromPath(filePath) {
8031
8070
  const normalized = filePath.replace(/\\/g, "/");
8032
8071
  const match = normalized.match(/(?:^|\/)agents\/(.+)\/handler\.ts$/);
@@ -8037,11 +8076,6 @@ function extractAgentNameFromPath(filePath) {
8037
8076
  }
8038
8077
  return match[1].replace(/\//g, ".");
8039
8078
  }
8040
- function detectAgentExports(source) {
8041
- return {
8042
- hasRun: RUN_EXPORT_RE.test(source)
8043
- };
8044
- }
8045
8079
  async function scanAgents(rootDir, patterns) {
8046
8080
  const files = await fg5(patterns, {
8047
8081
  cwd: rootDir,
@@ -8056,9 +8090,6 @@ async function scanAgents(rootDir, patterns) {
8056
8090
  if (fileName !== "handler.ts" && fileName !== "handler.js") {
8057
8091
  continue;
8058
8092
  }
8059
- const absPath = path29.resolve(rootDir, normalizedFile);
8060
- const source = await fs24.promises.readFile(absPath, "utf8").catch(() => "");
8061
- const { hasRun } = detectAgentExports(source);
8062
8093
  const name = extractAgentNameFromPath(normalizedFile);
8063
8094
  const prevFile = seen.get(name);
8064
8095
  if (prevFile) {
@@ -8069,8 +8100,7 @@ async function scanAgents(rootDir, patterns) {
8069
8100
  seen.set(name, normalizedFile);
8070
8101
  agents.push({
8071
8102
  name,
8072
- filePath: normalizedFile,
8073
- hasRun
8103
+ filePath: normalizedFile
8074
8104
  });
8075
8105
  }
8076
8106
  return agents;
@@ -8194,7 +8224,6 @@ export {
8194
8224
  hydrateSkillRegistry,
8195
8225
  invalidateProgramCache,
8196
8226
  listSkills,
8197
- loadAgentModule,
8198
8227
  loadConfig,
8199
8228
  loadEnv,
8200
8229
  loadTaskDriver,