@faapi/faapi 6.7.0 → 6.9.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
@@ -1122,8 +1122,10 @@ var init_resolveInjection = __esm({
1122
1122
  // Phase 2.3
1123
1123
  agents: "agents",
1124
1124
  // Phase 2.3
1125
- tasks: "tasks"
1125
+ tasks: "tasks",
1126
1126
  // 任务子系统:TaskClient(入队/查询)
1127
+ log: "log"
1128
+ // 请求级日志器(ctx.log 同一实例:scope http,自动带 requestId 等字段)
1127
1129
  };
1128
1130
  injectionCache = /* @__PURE__ */ new WeakMap();
1129
1131
  }
@@ -1233,13 +1235,13 @@ var init_collectRouteSchemaSources = __esm({
1233
1235
  });
1234
1236
 
1235
1237
  // src/utils/atomicWrite.ts
1236
- import path9 from "path";
1237
- import fs7 from "fs";
1238
+ import path10 from "path";
1239
+ import fs8 from "fs";
1238
1240
  async function atomicWriteFile(outputPath, content) {
1239
- await fs7.promises.mkdir(path9.dirname(outputPath), { recursive: true });
1241
+ await fs8.promises.mkdir(path10.dirname(outputPath), { recursive: true });
1240
1242
  const tmp = `${outputPath}.tmp-${process.pid}-${Math.random().toString(36).slice(2)}`;
1241
- await fs7.promises.writeFile(tmp, content, "utf-8");
1242
- await fs7.promises.rename(tmp, outputPath);
1243
+ await fs8.promises.writeFile(tmp, content, "utf-8");
1244
+ await fs8.promises.rename(tmp, outputPath);
1243
1245
  }
1244
1246
  var init_atomicWrite = __esm({
1245
1247
  "src/utils/atomicWrite.ts"() {
@@ -1586,7 +1588,7 @@ __export(generateSchemaFiles_exports, {
1586
1588
  getRuntimeSchemaPath: () => getRuntimeSchemaPath,
1587
1589
  getSchemaOutputPath: () => getSchemaOutputPath
1588
1590
  });
1589
- import path10 from "path";
1591
+ import path11 from "path";
1590
1592
  function getSchemaOutputPath(sourceFile, dist, rootDir) {
1591
1593
  let rel = sourceFile.replace(/\\/g, "/");
1592
1594
  if (rel.startsWith("src/")) {
@@ -1594,7 +1596,7 @@ function getSchemaOutputPath(sourceFile, dist, rootDir) {
1594
1596
  }
1595
1597
  const idx = rel.lastIndexOf("/");
1596
1598
  const relDir = idx >= 0 ? rel.slice(0, idx) : "";
1597
- return path10.resolve(rootDir, dist, relDir, "zod.js");
1599
+ return path11.resolve(rootDir, dist, relDir, "zod.js");
1598
1600
  }
1599
1601
  function getRuntimeSchemaPath(filePath, dist, rootDir) {
1600
1602
  let rel = filePath.replace(/\\/g, "/");
@@ -1605,7 +1607,7 @@ function getRuntimeSchemaPath(filePath, dist, rootDir) {
1605
1607
  }
1606
1608
  const idx = rel.lastIndexOf("/");
1607
1609
  const relDir = idx >= 0 ? rel.slice(0, idx) : "";
1608
- return path10.resolve(rootDir, dist, relDir, "zod.js");
1610
+ return path11.resolve(rootDir, dist, relDir, "zod.js");
1609
1611
  }
1610
1612
  function getHelpersImportPath(relDir) {
1611
1613
  if (!relDir) return `./${HELPERS_FILENAME}`;
@@ -1664,7 +1666,7 @@ async function generateSchemaFiles(routes, rootDir, dist) {
1664
1666
  }
1665
1667
  const fileEntries = [];
1666
1668
  for (const [filePath, fileSources] of sourcesByFile) {
1667
- const relFile = path10.relative(rootDir, filePath).replace(/\\/g, "/");
1669
+ const relFile = path11.relative(rootDir, filePath).replace(/\\/g, "/");
1668
1670
  const outputPath = getSchemaOutputPath(relFile, dist, rootDir);
1669
1671
  let relForDir = relFile;
1670
1672
  if (relForDir.startsWith("src/")) {
@@ -1683,7 +1685,7 @@ async function generateSchemaFiles(routes, rootDir, dist) {
1683
1685
  }
1684
1686
  const allSourceCode = fileEntries.map((e) => e.source).join("\n");
1685
1687
  if (usesCoerceHelpers(allSourceCode)) {
1686
- const helpersPath = path10.resolve(rootDir, dist, HELPERS_FILENAME);
1688
+ const helpersPath = path11.resolve(rootDir, dist, HELPERS_FILENAME);
1687
1689
  await writeSchemaFile(helpersPath, generateHelpersFileSource());
1688
1690
  }
1689
1691
  await Promise.all(
@@ -1915,7 +1917,7 @@ function createAppRegistries() {
1915
1917
  var defaultRegistries = createAppRegistries();
1916
1918
 
1917
1919
  // src/task/taskQueue.ts
1918
- import path3 from "path";
1920
+ import path4 from "path";
1919
1921
 
1920
1922
  // src/errors/FaapiError.ts
1921
1923
  var FaapiError = class extends Error {
@@ -1943,14 +1945,14 @@ var ValidationError = class extends FaapiError {
1943
1945
  issues;
1944
1946
  };
1945
1947
  var RouteNotFoundError = class extends FaapiError {
1946
- constructor(path28) {
1947
- super("ROUTE_NOT_FOUND", `Route not found: ${path28}`, 404);
1948
+ constructor(path29) {
1949
+ super("ROUTE_NOT_FOUND", `Route not found: ${path29}`, 404);
1948
1950
  this.name = "RouteNotFoundError";
1949
1951
  }
1950
1952
  };
1951
1953
  var MethodNotAllowedError = class extends FaapiError {
1952
- constructor(method, path28, allowedMethods) {
1953
- super("METHOD_NOT_ALLOWED", `Method ${method} not allowed for ${path28}`, 405);
1954
+ constructor(method, path29, allowedMethods) {
1955
+ super("METHOD_NOT_ALLOWED", `Method ${method} not allowed for ${path29}`, 405);
1954
1956
  this.allowedMethods = allowedMethods;
1955
1957
  this.name = "MethodNotAllowedError";
1956
1958
  }
@@ -2066,6 +2068,36 @@ function serializeError(err) {
2066
2068
  return { name: 'Error', message: String(err), stack: undefined, props: {} };
2067
2069
  }
2068
2070
  `;
2071
+ var BUILD_LOGGER_SOURCE = `
2072
+ var LOG_RANK = { debug: 0, info: 1, warn: 2, error: 3 };
2073
+ function createTaskLogger(level, scope, fields) {
2074
+ function make(suffix) {
2075
+ function write(lvl, message, callFields) {
2076
+ if (level !== undefined && LOG_RANK[lvl] < LOG_RANK[level]) return;
2077
+ var entry = { level: lvl, message: message, time: new Date().toISOString() };
2078
+ var fullScope = suffix === undefined ? scope : scope === undefined ? suffix : scope + ':' + suffix;
2079
+ if (fullScope !== undefined) entry.scope = fullScope;
2080
+ if (fields !== undefined || callFields !== undefined) {
2081
+ entry.fields = Object.assign({}, fields, callFields);
2082
+ }
2083
+ try {
2084
+ parentPort.postMessage({ type: 'log', entry: entry });
2085
+ } catch (_) {
2086
+ entry.fields = { warning: 'log fields not cloneable across worker boundary, dropped' };
2087
+ parentPort.postMessage({ type: 'log', entry: entry });
2088
+ }
2089
+ }
2090
+ return {
2091
+ debug: function (m, f) { write('debug', m, f); },
2092
+ info: function (m, f) { write('info', m, f); },
2093
+ warn: function (m, f) { write('warn', m, f); },
2094
+ error: function (m, f) { write('error', m, f); },
2095
+ child: function (cs) { return make(suffix === undefined ? cs : suffix + ':' + cs); },
2096
+ };
2097
+ }
2098
+ return make(undefined);
2099
+ }
2100
+ `;
2069
2101
  function buildWrapperSource(moduleUrl) {
2070
2102
  return `
2071
2103
  import { parentPort } from 'node:worker_threads';
@@ -2075,6 +2107,8 @@ ${BUILD_VIEW_SOURCE}
2075
2107
 
2076
2108
  ${SERIALIZE_ERROR_SOURCE}
2077
2109
 
2110
+ ${BUILD_LOGGER_SOURCE}
2111
+
2078
2112
  const run = mod.run;
2079
2113
  if (typeof run !== 'function') {
2080
2114
  parentPort.postMessage({
@@ -2094,7 +2128,8 @@ if (typeof run !== 'function') {
2094
2128
  try {
2095
2129
  const registries = buildRegistriesView(msg.registries);
2096
2130
  const progress = (value) => parentPort.postMessage({ type: 'progress', value });
2097
- const result = await run(payload, { ...taskCtx, signal: controller.signal, registries, progress });
2131
+ const taskLog = msg.log ? createTaskLogger(msg.log.level, msg.log.scope, msg.log.fields) : undefined;
2132
+ const result = await run(payload, { ...taskCtx, signal: controller.signal, registries, progress, log: taskLog });
2098
2133
  parentPort.postMessage({ type: 'done', result });
2099
2134
  } catch (err) {
2100
2135
  const serialized = serializeError(err);
@@ -2202,6 +2237,8 @@ async function runTaskInWorker(options) {
2202
2237
  finish(() => reject(reviveError(msg.error)));
2203
2238
  } else if (msg?.type === "progress") {
2204
2239
  options.onProgress?.(msg.value);
2240
+ } else if (msg?.type === "log") {
2241
+ options.onLog?.(msg.entry);
2205
2242
  }
2206
2243
  }
2207
2244
  );
@@ -2225,7 +2262,8 @@ async function runTaskInWorker(options) {
2225
2262
  type: "run",
2226
2263
  payload,
2227
2264
  taskCtx: { config: safeConfig(taskCtx.config), job: taskCtx.job },
2228
- registries: options.registries
2265
+ registries: options.registries,
2266
+ log: options.log
2229
2267
  });
2230
2268
  } catch (err) {
2231
2269
  finish(
@@ -2237,6 +2275,201 @@ async function runTaskInWorker(options) {
2237
2275
  });
2238
2276
  }
2239
2277
 
2278
+ // src/logger/formatEntry.ts
2279
+ function serializeFieldValue(value) {
2280
+ if (value instanceof Error) {
2281
+ return { name: value.name, message: value.message, stack: value.stack };
2282
+ }
2283
+ return value;
2284
+ }
2285
+ function formatEntry(entry) {
2286
+ let line = `[${entry.time}] ${entry.level.toUpperCase()} `;
2287
+ if (entry.scope) line += `[${entry.scope}] `;
2288
+ line += entry.message;
2289
+ if (entry.fields !== void 0) {
2290
+ const plain = Object.fromEntries(
2291
+ Object.entries(entry.fields).map(([k, v]) => [k, serializeFieldValue(v)])
2292
+ );
2293
+ try {
2294
+ line += ` ${JSON.stringify(plain)}`;
2295
+ } catch (err) {
2296
+ line += ` [unserializable fields: ${err instanceof Error ? err.message : String(err)}]`;
2297
+ }
2298
+ }
2299
+ return line;
2300
+ }
2301
+
2302
+ // src/logger/fileSink.ts
2303
+ import fs2 from "fs";
2304
+ import path3 from "path";
2305
+ var LEVEL_FILES = ["debug", "info", "warn", "error"];
2306
+ function createFileLogSink(options) {
2307
+ fs2.mkdirSync(options.dir, { recursive: true });
2308
+ const streams = /* @__PURE__ */ new Map();
2309
+ const getStream = (name) => {
2310
+ if (streams.has(name)) return streams.get(name);
2311
+ return void 0;
2312
+ };
2313
+ const openStream = (name) => {
2314
+ const stream = fs2.createWriteStream(path3.join(options.dir, `${name}.log`), { flags: "a" });
2315
+ stream.on("error", () => {
2316
+ });
2317
+ streams.set(name, stream);
2318
+ };
2319
+ const fileNames = options.splitByLevel ? [...LEVEL_FILES] : ["app", "error"];
2320
+ for (const name of fileNames) openStream(name);
2321
+ let pendingCount = 0;
2322
+ let drainWaiters = [];
2323
+ const onWriteDone = () => {
2324
+ pendingCount--;
2325
+ if (pendingCount === 0 && drainWaiters.length > 0) {
2326
+ const waiters = drainWaiters;
2327
+ drainWaiters = [];
2328
+ for (const waiter of waiters) waiter();
2329
+ }
2330
+ };
2331
+ const write = (entry) => {
2332
+ const line = `${formatEntry(entry)}
2333
+ `;
2334
+ const target = options.splitByLevel ? entry.level : "app";
2335
+ const main = getStream(target);
2336
+ if (main) {
2337
+ pendingCount++;
2338
+ main.write(line, onWriteDone);
2339
+ }
2340
+ if (!options.splitByLevel && entry.level === "error") {
2341
+ const dup = getStream("error");
2342
+ if (dup) {
2343
+ pendingCount++;
2344
+ dup.write(line, onWriteDone);
2345
+ }
2346
+ }
2347
+ };
2348
+ const flush = () => new Promise((resolve) => {
2349
+ if (pendingCount === 0) {
2350
+ resolve();
2351
+ return;
2352
+ }
2353
+ drainWaiters.push(resolve);
2354
+ });
2355
+ const close = () => Promise.all(
2356
+ [...streams.values()].map(
2357
+ (stream) => new Promise((resolve) => {
2358
+ stream.end(() => resolve());
2359
+ })
2360
+ )
2361
+ ).then(() => {
2362
+ streams.clear();
2363
+ });
2364
+ return { write, flush, close };
2365
+ }
2366
+
2367
+ // src/logger/logger.ts
2368
+ var LEVEL_RANK = { debug: 0, info: 1, warn: 2, error: 3 };
2369
+ var VALID_LEVELS = ["debug", "info", "warn", "error"];
2370
+ function assertLogLevel(value, source) {
2371
+ if (typeof value !== "string" || !VALID_LEVELS.includes(value)) {
2372
+ throw new Error(
2373
+ `[faapi] Invalid log level from ${source}: ${String(value)}. Expected one of: ${VALID_LEVELS.join(", ")}.`
2374
+ );
2375
+ }
2376
+ }
2377
+ var globalState = { level: null, sink: null, fileSink: null, stdout: true, accessLog: false, disabled: false };
2378
+ function configureLogging(config) {
2379
+ const objectConfig = typeof config === "object" ? config : {};
2380
+ if (objectConfig.sink && objectConfig.dir) {
2381
+ throw new Error(
2382
+ "[faapi] config.log.sink and config.log.dir are mutually exclusive: pick one output pipeline (custom sink OR built-in file logs)."
2383
+ );
2384
+ }
2385
+ void globalState.fileSink?.close();
2386
+ globalState.disabled = config === false;
2387
+ globalState.sink = objectConfig.sink ?? null;
2388
+ globalState.accessLog = objectConfig.accessLog ?? objectConfig.dir !== void 0;
2389
+ globalState.stdout = objectConfig.stdout ?? true;
2390
+ globalState.fileSink = objectConfig.dir ? createFileLogSink({ dir: objectConfig.dir, splitByLevel: objectConfig.splitByLevel }) : null;
2391
+ if (objectConfig.level !== void 0) {
2392
+ assertLogLevel(objectConfig.level, "config.log.level");
2393
+ globalState.level = objectConfig.level;
2394
+ } else if (process.env.LOG_LEVEL !== void 0) {
2395
+ assertLogLevel(process.env.LOG_LEVEL, "LOG_LEVEL env");
2396
+ globalState.level = process.env.LOG_LEVEL;
2397
+ } else {
2398
+ globalState.level = null;
2399
+ }
2400
+ }
2401
+ function globalThreshold() {
2402
+ if (globalState.level) return globalState.level;
2403
+ if (globalState.fileSink) return void 0;
2404
+ return "info";
2405
+ }
2406
+ function belowThreshold(level, threshold) {
2407
+ return threshold !== void 0 && LEVEL_RANK[level] < LEVEL_RANK[threshold];
2408
+ }
2409
+ function getEffectiveLogLevel() {
2410
+ return globalThreshold();
2411
+ }
2412
+ var consoleSink = (entry) => {
2413
+ console[CONSOLE_METHOD[entry.level]](formatEntry(entry));
2414
+ };
2415
+ var CONSOLE_METHOD = {
2416
+ debug: "debug",
2417
+ info: "info",
2418
+ warn: "warn",
2419
+ error: "error"
2420
+ };
2421
+ function globalDispatch(entry) {
2422
+ if (globalState.disabled) return;
2423
+ if (globalState.sink) {
2424
+ globalState.sink(entry);
2425
+ return;
2426
+ }
2427
+ if (globalState.fileSink) {
2428
+ globalState.fileSink.write(entry);
2429
+ if (globalState.stdout) consoleSink(entry);
2430
+ return;
2431
+ }
2432
+ consoleSink(entry);
2433
+ }
2434
+ function writeLogEntry(entry) {
2435
+ if (belowThreshold(entry.level, globalThreshold())) return;
2436
+ globalDispatch(entry);
2437
+ }
2438
+ function isAccessLogPiped() {
2439
+ return !globalState.disabled && globalState.accessLog;
2440
+ }
2441
+ function createLogger(scope, options) {
2442
+ const baseScope = scope;
2443
+ const baseFields = options?.fields;
2444
+ const write = (level, message, callFields, scopeSuffix) => {
2445
+ const threshold = options?.level ?? globalThreshold();
2446
+ if (belowThreshold(level, threshold)) return;
2447
+ const entry = {
2448
+ level,
2449
+ message,
2450
+ time: (/* @__PURE__ */ new Date()).toISOString()
2451
+ };
2452
+ const fullScope = scopeSuffix === void 0 ? baseScope : baseScope === void 0 ? scopeSuffix : `${baseScope}:${scopeSuffix}`;
2453
+ if (fullScope !== void 0) entry.scope = fullScope;
2454
+ if (baseFields !== void 0 || callFields !== void 0) {
2455
+ entry.fields = { ...baseFields, ...callFields };
2456
+ }
2457
+ if (options?.sink) {
2458
+ options.sink(entry);
2459
+ } else {
2460
+ globalDispatch(entry);
2461
+ }
2462
+ };
2463
+ const makeLogger = (scopeSuffix) => ({
2464
+ debug: (message, fields) => write("debug", message, fields, scopeSuffix),
2465
+ info: (message, fields) => write("info", message, fields, scopeSuffix),
2466
+ warn: (message, fields) => write("warn", message, fields, scopeSuffix),
2467
+ error: (message, fields) => write("error", message, fields, scopeSuffix),
2468
+ child: (childScope) => makeLogger(scopeSuffix === void 0 ? childScope : `${scopeSuffix}:${childScope}`)
2469
+ });
2470
+ return makeLogger(void 0);
2471
+ }
2472
+
2240
2473
  // src/task/taskQueue.ts
2241
2474
  var TERMINAL_STATUSES = /* @__PURE__ */ new Set(["done", "failed", "cancelled"]);
2242
2475
  var MAX_FINISHED_RECORDS = 1e3;
@@ -2269,7 +2502,7 @@ function createTaskQueue(deps) {
2269
2502
  }
2270
2503
  const loadTaskModule = deps.loadTaskModule ?? (async (filePath) => await import(filePath));
2271
2504
  const loadPayloadSchema = deps.loadPayloadSchema ?? (async (filePath) => {
2272
- const zodPath = path3.join(path3.dirname(filePath), "zod.js");
2505
+ const zodPath = path4.join(path4.dirname(filePath), "zod.js");
2273
2506
  try {
2274
2507
  const mod = await import(zodPath);
2275
2508
  const schemaKey = Object.keys(mod).find((k) => k.endsWith("Schema"));
@@ -2297,7 +2530,7 @@ function createTaskQueue(deps) {
2297
2530
  const meta = registry.get(name);
2298
2531
  schemaCache.set(
2299
2532
  name,
2300
- await loadPayloadSchema(path3.resolve(rootDir, meta.filePath)).catch(() => void 0)
2533
+ await loadPayloadSchema(path4.resolve(rootDir, meta.filePath)).catch(() => void 0)
2301
2534
  );
2302
2535
  }
2303
2536
  const schema = schemaCache.get(name);
@@ -2333,24 +2566,32 @@ function createTaskQueue(deps) {
2333
2566
  let result;
2334
2567
  if (meta.timeoutMs !== void 0 && meta.timeoutMs > 0) {
2335
2568
  result = await (deps.runIsolated ?? runTaskInWorker)({
2336
- taskModulePath: path3.resolve(rootDir, meta.filePath),
2569
+ taskModulePath: path4.resolve(rootDir, meta.filePath),
2337
2570
  payload: job.payload,
2338
2571
  taskCtx: {
2339
2572
  config: deps.config,
2340
2573
  job: { id: job.id, name: job.name, attempt: job.attempt }
2341
2574
  },
2342
2575
  registries: snapshotRegistries(),
2576
+ // 日志配置随 workerData 下发(纯数据),worker 内联重建 taskCtx.log,
2577
+ // 条目回传宿主走统一管道(scope/fields 与进程内路径一致)
2578
+ log: {
2579
+ level: getEffectiveLogLevel(),
2580
+ scope: `task:${job.name}`,
2581
+ fields: { jobId: job.id, task: job.name, attempt: job.attempt }
2582
+ },
2343
2583
  timeoutMs: meta.timeoutMs,
2344
2584
  graceMs: meta.graceMs,
2345
2585
  externalSignal: job.signal,
2346
2586
  onProgress: (value) => {
2347
2587
  if (record.status === "running") record.progress = value;
2348
- }
2588
+ },
2589
+ onLog: writeLogEntry
2349
2590
  });
2350
2591
  } else {
2351
2592
  let mod = moduleCache2.get(job.name);
2352
2593
  if (!mod) {
2353
- mod = await loadTaskModule(path3.resolve(rootDir, meta.filePath));
2594
+ mod = await loadTaskModule(path4.resolve(rootDir, meta.filePath));
2354
2595
  moduleCache2.set(job.name, mod);
2355
2596
  }
2356
2597
  if (typeof mod.run !== "function") {
@@ -2361,6 +2602,9 @@ function createTaskQueue(deps) {
2361
2602
  config: deps.config,
2362
2603
  job: { id: job.id, name: job.name, attempt: job.attempt },
2363
2604
  registries: registriesView,
2605
+ log: createLogger(`task:${job.name}`, {
2606
+ fields: { jobId: job.id, task: job.name, attempt: job.attempt }
2607
+ }),
2364
2608
  progress: (value) => {
2365
2609
  if (record.status === "running") record.progress = value;
2366
2610
  }
@@ -2584,8 +2828,8 @@ function createCronScheduler(registry, enqueue) {
2584
2828
 
2585
2829
  // src/task/scanTasks.ts
2586
2830
  import fg from "fast-glob";
2587
- import path4 from "path";
2588
- import fs2 from "fs";
2831
+ import path5 from "path";
2832
+ import fs3 from "fs";
2589
2833
  var TASK_PATTERNS = ["src/tasks/**/task.ts"];
2590
2834
  var TASK_FILENAME = "task.ts";
2591
2835
  var CRON_RE = /(?:^|\n)\s*cron:\s*['"`]([^'"`\n]+)['"`]/;
@@ -2616,12 +2860,12 @@ async function scanTasks(rootDir, patterns) {
2616
2860
  const seen = /* @__PURE__ */ new Map();
2617
2861
  for (const file of files) {
2618
2862
  const normalizedFile = file.replace(/\\/g, "/");
2619
- const fileName = path4.posix.basename(normalizedFile);
2863
+ const fileName = path5.posix.basename(normalizedFile);
2620
2864
  if (fileName !== TASK_FILENAME) {
2621
2865
  continue;
2622
2866
  }
2623
- const absPath = path4.resolve(rootDir, normalizedFile);
2624
- const source = await fs2.promises.readFile(absPath, "utf8").catch(() => "");
2867
+ const absPath = path5.resolve(rootDir, normalizedFile);
2868
+ const source = await fs3.promises.readFile(absPath, "utf8").catch(() => "");
2625
2869
  const name = filePathToTaskName(normalizedFile);
2626
2870
  const prevFile = seen.get(name);
2627
2871
  if (prevFile) {
@@ -2694,7 +2938,7 @@ function listSkills() {
2694
2938
  }
2695
2939
 
2696
2940
  // src/loader/loadAgentModule.ts
2697
- import fs10 from "fs";
2941
+ import fs11 from "fs";
2698
2942
 
2699
2943
  // src/loader/resolveExports.ts
2700
2944
  function resolveExport(module, exportName) {
@@ -2740,18 +2984,18 @@ async function importWithCacheBust(filePath, bustViteCache = false) {
2740
2984
  }
2741
2985
 
2742
2986
  // src/cli/compileOnDemand.ts
2743
- import path12 from "path";
2744
- import fs9 from "fs";
2987
+ import path13 from "path";
2988
+ import fs10 from "fs";
2745
2989
 
2746
2990
  // src/cli/compileSourceFiles.ts
2747
- import path8 from "path";
2748
- import fs6 from "fs";
2991
+ import path9 from "path";
2992
+ import fs7 from "fs";
2749
2993
  import fg2 from "fast-glob";
2750
2994
 
2751
2995
  // src/cli/aliasPlugin.ts
2752
2996
  import ts7 from "typescript";
2753
- import path7 from "path";
2754
- import fs5 from "fs";
2997
+ import path8 from "path";
2998
+ import fs6 from "fs";
2755
2999
 
2756
3000
  // src/utils/resolveAlias.ts
2757
3001
  function resolveAlias(specifier, config) {
@@ -2777,8 +3021,8 @@ function resolveAlias(specifier, config) {
2777
3021
  }
2778
3022
 
2779
3023
  // src/utils/prodPaths.ts
2780
- import path5 from "path";
2781
- import fs3 from "fs";
3024
+ import path6 from "path";
3025
+ import fs4 from "fs";
2782
3026
  var APP_DIR = "src";
2783
3027
  var ROUTE_PATTERNS = ["src/api/**/*.ts"];
2784
3028
  function toProdFilePath(filePath, dist) {
@@ -2797,27 +3041,27 @@ function toProdExtension(filePath) {
2797
3041
  }
2798
3042
  function toRealPath(p) {
2799
3043
  try {
2800
- return fs3.realpathSync(p);
3044
+ return fs4.realpathSync(p);
2801
3045
  } catch {
2802
3046
  return p;
2803
3047
  }
2804
3048
  }
2805
3049
  function isInsideDir(filePath, dir) {
2806
- const rel = path5.relative(dir, filePath);
2807
- return rel !== "" && !rel.startsWith("..") && !path5.isAbsolute(rel);
3050
+ const rel = path6.relative(dir, filePath);
3051
+ return rel !== "" && !rel.startsWith("..") && !path6.isAbsolute(rel);
2808
3052
  }
2809
3053
 
2810
3054
  // src/utils/readTsconfig.ts
2811
3055
  import ts6 from "typescript";
2812
- import path6 from "path";
2813
- import fs4 from "fs";
3056
+ import path7 from "path";
3057
+ import fs5 from "fs";
2814
3058
  var tsconfigCache = /* @__PURE__ */ new Map();
2815
3059
  function readTsconfig(rootDir) {
2816
- const tsconfigPath = path6.resolve(rootDir, "tsconfig.json");
2817
- if (!fs4.existsSync(tsconfigPath)) return null;
3060
+ const tsconfigPath = path7.resolve(rootDir, "tsconfig.json");
3061
+ if (!fs5.existsSync(tsconfigPath)) return null;
2818
3062
  let mtimeMs;
2819
3063
  try {
2820
- mtimeMs = fs4.statSync(tsconfigPath).mtimeMs;
3064
+ mtimeMs = fs5.statSync(tsconfigPath).mtimeMs;
2821
3065
  } catch {
2822
3066
  return null;
2823
3067
  }
@@ -2833,7 +3077,7 @@ function readTsconfig(rootDir) {
2833
3077
  const config = rawPaths ? (() => {
2834
3078
  const paths = {};
2835
3079
  for (const [pattern, targets] of Object.entries(rawPaths)) {
2836
- paths[pattern] = targets.map((t) => path6.resolve(baseUrl, t));
3080
+ paths[pattern] = targets.map((t) => path7.resolve(baseUrl, t));
2837
3081
  }
2838
3082
  return { baseUrl, paths };
2839
3083
  })() : null;
@@ -2843,26 +3087,26 @@ function readTsconfig(rootDir) {
2843
3087
 
2844
3088
  // src/cli/aliasPlugin.ts
2845
3089
  function toProdImportPath(sourceFile, importer) {
2846
- const importerDir = path7.dirname(importer);
2847
- let rel = path7.relative(importerDir, sourceFile);
2848
- rel = rel.split(path7.sep).join("/");
3090
+ const importerDir = path8.dirname(importer);
3091
+ let rel = path8.relative(importerDir, sourceFile);
3092
+ rel = rel.split(path8.sep).join("/");
2849
3093
  if (!rel.startsWith(".")) rel = "./" + rel;
2850
3094
  return toProdExtension(rel);
2851
3095
  }
2852
3096
  function toStrippedProdImportPath(sourceFile, rootDir) {
2853
- const appDirAbs = toRealPath(path7.resolve(rootDir, APP_DIR));
3097
+ const appDirAbs = toRealPath(path8.resolve(rootDir, APP_DIR));
2854
3098
  const sourceReal = toRealPath(sourceFile);
2855
- let rel = path7.relative(appDirAbs, sourceReal);
2856
- rel = rel.split(path7.sep).join("/");
3099
+ let rel = path8.relative(appDirAbs, sourceReal);
3100
+ rel = rel.split(path8.sep).join("/");
2857
3101
  if (!rel.startsWith(".")) rel = "./" + rel;
2858
3102
  return toProdExtension(rel);
2859
3103
  }
2860
3104
  function toProdImportFromImporter(importer, rootDir, relFromDist) {
2861
- const importerRel = path7.relative(toRealPath(path7.resolve(rootDir)), toRealPath(importer)).split(path7.sep).join("/");
2862
- const importerProdDir = path7.posix.dirname(toProdExtension(importerRel));
3105
+ const importerRel = path8.relative(toRealPath(path8.resolve(rootDir)), toRealPath(importer)).split(path8.sep).join("/");
3106
+ const importerProdDir = path8.posix.dirname(toProdExtension(importerRel));
2863
3107
  if (importerProdDir === ".") return relFromDist;
2864
3108
  const target = relFromDist.replace(/^\.\//, "");
2865
- let rel = path7.posix.relative(importerProdDir, target);
3109
+ let rel = path8.posix.relative(importerProdDir, target);
2866
3110
  if (!rel.startsWith(".")) rel = "./" + rel;
2867
3111
  return rel;
2868
3112
  }
@@ -2877,41 +3121,41 @@ var INDEX_EXTS = [
2877
3121
  "/index.cjs"
2878
3122
  ];
2879
3123
  function resolveRelativeSpecifier(importer, specifier) {
2880
- const importerDir = path7.dirname(importer);
2881
- const base = path7.resolve(importerDir, specifier);
3124
+ const importerDir = path8.dirname(importer);
3125
+ const base = path8.resolve(importerDir, specifier);
2882
3126
  if (PROD_EXTS.some((ext) => specifier.endsWith(ext))) {
2883
- if (fs5.existsSync(base)) return base;
3127
+ if (fs6.existsSync(base)) return base;
2884
3128
  if (base.endsWith(".js")) {
2885
3129
  for (const ext of [".ts", ".tsx", ".jsx"]) {
2886
3130
  const file = base.slice(0, -3) + ext;
2887
- if (fs5.existsSync(file)) return file;
3131
+ if (fs6.existsSync(file)) return file;
2888
3132
  }
2889
3133
  }
2890
3134
  return null;
2891
3135
  }
2892
3136
  if (/\.(ts|tsx|jsx)$/.test(specifier)) {
2893
- return fs5.existsSync(base) ? base : null;
3137
+ return fs6.existsSync(base) ? base : null;
2894
3138
  }
2895
3139
  for (const ext of SOURCE_EXTS) {
2896
3140
  const file = base + ext;
2897
- if (fs5.existsSync(file)) return file;
3141
+ if (fs6.existsSync(file)) return file;
2898
3142
  }
2899
3143
  for (const indexExt of INDEX_EXTS) {
2900
3144
  const file = base + indexExt;
2901
- if (fs5.existsSync(file)) return file;
3145
+ if (fs6.existsSync(file)) return file;
2902
3146
  }
2903
3147
  return null;
2904
3148
  }
2905
3149
  function createAliasPlugin(config, options) {
2906
- const appDirAbs = options?.rootDir ? toRealPath(path7.resolve(options.rootDir, APP_DIR)) : null;
3150
+ const appDirAbs = options?.rootDir ? toRealPath(path8.resolve(options.rootDir, APP_DIR)) : null;
2907
3151
  const probeCandidateFile = (candidate) => {
2908
3152
  for (const ext of SOURCE_EXTS) {
2909
3153
  const file = candidate + ext;
2910
- if (fs5.existsSync(file)) return file;
3154
+ if (fs6.existsSync(file)) return file;
2911
3155
  }
2912
3156
  for (const indexExt of INDEX_EXTS) {
2913
3157
  const file = candidate + indexExt;
2914
- if (fs5.existsSync(file)) return file;
3158
+ if (fs6.existsSync(file)) return file;
2915
3159
  }
2916
3160
  return null;
2917
3161
  };
@@ -2921,7 +3165,7 @@ function createAliasPlugin(config, options) {
2921
3165
  build.onLoad({ filter: /\.(ts|tsx|js|jsx|mjs|cjs)$/ }, (args) => {
2922
3166
  let source;
2923
3167
  try {
2924
- source = fs5.readFileSync(args.path, "utf8");
3168
+ source = fs6.readFileSync(args.path, "utf8");
2925
3169
  } catch {
2926
3170
  return void 0;
2927
3171
  }
@@ -3046,11 +3290,11 @@ async function compileSourceFiles(options) {
3046
3290
  if (entryPoints.length === 0) {
3047
3291
  return { compiledFiles: [] };
3048
3292
  }
3049
- const absDist = path8.resolve(rootDir, dist);
3050
- await fs6.promises.mkdir(absDist, { recursive: true });
3293
+ const absDist = path9.resolve(rootDir, dist);
3294
+ await fs7.promises.mkdir(absDist, { recursive: true });
3051
3295
  const plugins = buildAliasPlugins(rootDir);
3052
3296
  const esbuild = await import("esbuild");
3053
- const outbase = path8.resolve(rootDir, APP_DIR);
3297
+ const outbase = path9.resolve(rootDir, APP_DIR);
3054
3298
  const result = await esbuild.build({
3055
3299
  entryPoints,
3056
3300
  outdir: absDist,
@@ -3070,10 +3314,10 @@ async function compileSourceFiles(options) {
3070
3314
  if (atomicWrite && result.outputFiles) {
3071
3315
  await Promise.all(
3072
3316
  result.outputFiles.map(async (file) => {
3073
- await fs6.promises.mkdir(path8.dirname(file.path), { recursive: true });
3317
+ await fs7.promises.mkdir(path9.dirname(file.path), { recursive: true });
3074
3318
  const tmp = `${file.path}.tmp-${process.pid}-${Math.random().toString(36).slice(2)}`;
3075
- await fs6.promises.writeFile(tmp, file.contents);
3076
- await fs6.promises.rename(tmp, file.path);
3319
+ await fs7.promises.writeFile(tmp, file.contents);
3320
+ await fs7.promises.rename(tmp, file.path);
3077
3321
  })
3078
3322
  );
3079
3323
  }
@@ -3090,8 +3334,8 @@ init_generateSchemaFiles();
3090
3334
  init_generateSchemaFiles();
3091
3335
 
3092
3336
  // src/cli/collectImports.ts
3093
- import path11 from "path";
3094
- import fs8 from "fs";
3337
+ import path12 from "path";
3338
+ import fs9 from "fs";
3095
3339
  var SPEC_RE = /(\bfrom\s*|import\s*\(\s*)(['"])([^'"]+)\2/g;
3096
3340
  function extractImportSpecifiers(source) {
3097
3341
  const specifiers = [];
@@ -3103,10 +3347,10 @@ function extractImportSpecifiers(source) {
3103
3347
  return specifiers;
3104
3348
  }
3105
3349
  function resolveSpecifierFromDir(dir, specifier) {
3106
- return resolveRelativeSpecifier(path11.join(dir, "__faapi_probe__.ts"), specifier);
3350
+ return resolveRelativeSpecifier(path12.join(dir, "__faapi_probe__.ts"), specifier);
3107
3351
  }
3108
3352
  async function collectRelativeImports(entryFiles, rootDir) {
3109
- const appDirAbs = toRealPath(path11.resolve(rootDir, "src"));
3353
+ const appDirAbs = toRealPath(path12.resolve(rootDir, "src"));
3110
3354
  const tsconfig = readTsconfig(rootDir);
3111
3355
  const visited = /* @__PURE__ */ new Set();
3112
3356
  const insideFiles = /* @__PURE__ */ new Set();
@@ -3116,7 +3360,7 @@ async function collectRelativeImports(entryFiles, rootDir) {
3116
3360
  visited.add(filePath);
3117
3361
  let source;
3118
3362
  try {
3119
- source = await fs8.promises.readFile(filePath, "utf8");
3363
+ source = await fs9.promises.readFile(filePath, "utf8");
3120
3364
  } catch {
3121
3365
  return;
3122
3366
  }
@@ -3138,7 +3382,7 @@ async function collectRelativeImports(entryFiles, rootDir) {
3138
3382
  resolved = null;
3139
3383
  }
3140
3384
  if (!resolved) continue;
3141
- if (!isInsideDir(toRealPath(resolved), toRealPath(path11.resolve(rootDir)))) continue;
3385
+ if (!isInsideDir(toRealPath(resolved), toRealPath(path12.resolve(rootDir)))) continue;
3142
3386
  if (isInsideDir(toRealPath(resolved), appDirAbs)) {
3143
3387
  insideFiles.add(resolved);
3144
3388
  } else {
@@ -3159,8 +3403,8 @@ async function collectRelativeImports(entryFiles, rootDir) {
3159
3403
  // src/cli/compileOnDemand.ts
3160
3404
  function isProductFresh(sourceAbsPath, productAbsPath) {
3161
3405
  try {
3162
- const srcStat = fs9.statSync(sourceAbsPath);
3163
- const prodStat = fs9.statSync(productAbsPath);
3406
+ const srcStat = fs10.statSync(sourceAbsPath);
3407
+ const prodStat = fs10.statSync(productAbsPath);
3164
3408
  return prodStat.mtimeMs >= srcStat.mtimeMs;
3165
3409
  } catch {
3166
3410
  return false;
@@ -3192,7 +3436,7 @@ async function ensureCompiled(sourceAbsPath, rootDir, dist) {
3192
3436
  if (state.compiledFiles.has(sourceAbsPath)) {
3193
3437
  return false;
3194
3438
  }
3195
- if (!fs9.existsSync(sourceAbsPath)) {
3439
+ if (!fs10.existsSync(sourceAbsPath)) {
3196
3440
  return false;
3197
3441
  }
3198
3442
  const productPath = prodSourcePathToProductPath(sourceAbsPath, rootDir, dist);
@@ -3242,11 +3486,11 @@ async function ensureMiddlewaresCompiled(middlewarePaths, rootDir) {
3242
3486
  }
3243
3487
  }
3244
3488
  function prodSourcePathToProductPath(sourceAbsPath, rootDir, dist) {
3245
- const rel = path12.relative(rootDir, sourceAbsPath).replace(/\\/g, "/");
3489
+ const rel = path13.relative(rootDir, sourceAbsPath).replace(/\\/g, "/");
3246
3490
  if (!rel.startsWith("src/")) return null;
3247
3491
  const relWithoutSrc = rel.slice(4);
3248
3492
  const jsRel = relWithoutSrc.replace(/\.ts$/, ".js");
3249
- return path12.resolve(rootDir, dist, jsRel);
3493
+ return path13.resolve(rootDir, dist, jsRel);
3250
3494
  }
3251
3495
  function clearGeneratedSchemas() {
3252
3496
  state.generatedSchemas.clear();
@@ -3262,9 +3506,9 @@ async function ensureSchemaGenerated(schemaPath, routeFilePath, routes, rootDir,
3262
3506
  if (state.generatedSchemas.has(schemaPath)) {
3263
3507
  return false;
3264
3508
  }
3265
- const prodAbsPath = path12.resolve(rootDir, routeFilePath);
3509
+ const prodAbsPath = path13.resolve(rootDir, routeFilePath);
3266
3510
  const sourceAbsPath = prodPathToSourcePath(prodAbsPath, rootDir, dist);
3267
- if (!fs9.existsSync(sourceAbsPath)) {
3511
+ if (!fs10.existsSync(sourceAbsPath)) {
3268
3512
  return false;
3269
3513
  }
3270
3514
  if (isProductFresh(sourceAbsPath, schemaPath)) {
@@ -3275,7 +3519,7 @@ async function ensureSchemaGenerated(schemaPath, routeFilePath, routes, rootDir,
3275
3519
  if (fileRoutes.length === 0) {
3276
3520
  return false;
3277
3521
  }
3278
- const sourceRelPath = path12.relative(rootDir, sourceAbsPath).replace(/\\/g, "/");
3522
+ const sourceRelPath = path13.relative(rootDir, sourceAbsPath).replace(/\\/g, "/");
3279
3523
  const sourceRoutes = fileRoutes.map((r) => ({ ...r, filePath: sourceRelPath }));
3280
3524
  const generatePromise = (async () => {
3281
3525
  await generateSchemaFiles(sourceRoutes, rootDir, dist);
@@ -3296,7 +3540,7 @@ async function deleteSchemaFiles(routes, rootDir, dist) {
3296
3540
  if (deleted.has(schemaPath)) continue;
3297
3541
  deleted.add(schemaPath);
3298
3542
  try {
3299
- await fs9.promises.unlink(schemaPath);
3543
+ await fs10.promises.unlink(schemaPath);
3300
3544
  } catch {
3301
3545
  }
3302
3546
  }
@@ -3305,19 +3549,19 @@ var sourcePathCache = /* @__PURE__ */ new Map();
3305
3549
  function prodPathToSourcePath(prodAbsPath, rootDir, dist) {
3306
3550
  const cached = sourcePathCache.get(prodAbsPath);
3307
3551
  if (cached) return cached;
3308
- const rel = path12.relative(rootDir, prodAbsPath).replace(/\\/g, "/");
3552
+ const rel = path13.relative(rootDir, prodAbsPath).replace(/\\/g, "/");
3309
3553
  let relWithoutDist = rel;
3310
3554
  if (relWithoutDist.startsWith(`${dist}/`)) {
3311
3555
  relWithoutDist = relWithoutDist.slice(dist.length + 1);
3312
3556
  }
3313
3557
  const srcRel = `src/${relWithoutDist}`;
3314
3558
  const tsRel = srcRel.replace(/\.js$/, ".ts");
3315
- const tsAbs = path12.resolve(rootDir, tsRel);
3559
+ const tsAbs = path13.resolve(rootDir, tsRel);
3316
3560
  let result;
3317
- if (fs9.existsSync(tsAbs)) {
3561
+ if (fs10.existsSync(tsAbs)) {
3318
3562
  result = tsAbs;
3319
3563
  } else {
3320
- result = path12.resolve(rootDir, srcRel);
3564
+ result = path13.resolve(rootDir, srcRel);
3321
3565
  }
3322
3566
  sourcePathCache.set(prodAbsPath, result);
3323
3567
  return result;
@@ -3335,7 +3579,7 @@ async function loadAgentModule(filePath, hasRun, rootDir) {
3335
3579
  const dist = getDevDist();
3336
3580
  if (dist) {
3337
3581
  const sourcePath = prodPathToSourcePath(filePath, rootDir, dist);
3338
- if (sourcePath && fs10.existsSync(sourcePath)) {
3582
+ if (sourcePath && fs11.existsSync(sourcePath)) {
3339
3583
  try {
3340
3584
  await ensureCompiled(sourcePath, rootDir, dist);
3341
3585
  } catch (compileErr) {
@@ -3368,13 +3612,13 @@ async function loadAgentModule(filePath, hasRun, rootDir) {
3368
3612
  }
3369
3613
 
3370
3614
  // src/loader/loadToolModule.ts
3371
- import fs11 from "fs";
3615
+ import fs12 from "fs";
3372
3616
  async function loadToolModule(filePath, functionName, rootDir) {
3373
3617
  if (isDevOnDemandEnabled() && rootDir) {
3374
3618
  const dist = getDevDist();
3375
3619
  if (dist) {
3376
3620
  const sourcePath = prodPathToSourcePath(filePath, rootDir, dist);
3377
- if (sourcePath && fs11.existsSync(sourcePath)) {
3621
+ if (sourcePath && fs12.existsSync(sourcePath)) {
3378
3622
  try {
3379
3623
  await ensureCompiled(sourcePath, rootDir, dist);
3380
3624
  } catch (compileErr) {
@@ -3406,7 +3650,7 @@ async function loadToolModule(filePath, functionName, rootDir) {
3406
3650
  import { existsSync as existsSync2 } from "fs";
3407
3651
 
3408
3652
  // src/cli/generateToolArtifacts.ts
3409
- import path13 from "path";
3653
+ import path14 from "path";
3410
3654
  import { existsSync } from "fs";
3411
3655
 
3412
3656
  // src/ast/extractToolMetadata.ts
@@ -3530,7 +3774,7 @@ function collectToolSchemaSources(tools, rootDir) {
3530
3774
  const toolsByFile = /* @__PURE__ */ new Map();
3531
3775
  for (const tool of tools) {
3532
3776
  if (!tool.inputTypeName) continue;
3533
- const absPath = path13.resolve(rootDir, tool.filePath);
3777
+ const absPath = path14.resolve(rootDir, tool.filePath);
3534
3778
  let list = toolsByFile.get(absPath);
3535
3779
  if (!list) {
3536
3780
  list = [];
@@ -3590,15 +3834,15 @@ function generateToolSchemaFileSource(sources, resolveType, helpersImportPath) {
3590
3834
  }
3591
3835
  async function maybeGenerateHelpers(allSourceCode, distDir) {
3592
3836
  if (!usesCoerceHelpers(allSourceCode)) return;
3593
- const helpersPath = path13.resolve(distDir, HELPERS_FILENAME);
3837
+ const helpersPath = path14.resolve(distDir, HELPERS_FILENAME);
3594
3838
  if (existsSync(helpersPath)) return;
3595
3839
  await atomicWriteFile(helpersPath, generateHelpersFileSource());
3596
3840
  }
3597
3841
  async function generateToolArtifacts(tools, rootDir, dist, options) {
3598
3842
  const metadata = [];
3599
- const programByFile = createPrograms(tools.map((m) => path13.resolve(rootDir, m.filePath)));
3843
+ const programByFile = createPrograms(tools.map((m) => path14.resolve(rootDir, m.filePath)));
3600
3844
  for (const manifest of tools) {
3601
- const absPath = path13.resolve(rootDir, manifest.filePath);
3845
+ const absPath = path14.resolve(rootDir, manifest.filePath);
3602
3846
  const program = programByFile.get(absPath);
3603
3847
  const result = extractToolMetadata(program, absPath, manifest.functionName, {
3604
3848
  name: manifest.name,
@@ -3609,7 +3853,7 @@ async function generateToolArtifacts(tools, rootDir, dist, options) {
3609
3853
  }
3610
3854
  }
3611
3855
  const serialized = serializeTools(metadata, dist);
3612
- const toolsPath = path13.resolve(rootDir, dist, TOOLS_FILE);
3856
+ const toolsPath = path14.resolve(rootDir, dist, TOOLS_FILE);
3613
3857
  await writeToolsModule(serialized, toolsPath);
3614
3858
  if (options?.skipSchema) {
3615
3859
  return metadata;
@@ -3632,7 +3876,7 @@ async function generateToolArtifacts(tools, rootDir, dist, options) {
3632
3876
  }
3633
3877
  const fileEntries = [];
3634
3878
  for (const [filePath, fileSources] of sourcesByFile) {
3635
- const relFile = path13.relative(rootDir, filePath).replace(/\\/g, "/");
3879
+ const relFile = path14.relative(rootDir, filePath).replace(/\\/g, "/");
3636
3880
  const outputPath = getSchemaOutputPath(relFile, dist, rootDir);
3637
3881
  const resolver = resolversByFile.get(filePath);
3638
3882
  let relForDir = relFile;
@@ -3650,7 +3894,7 @@ async function generateToolArtifacts(tools, rootDir, dist, options) {
3650
3894
  fileEntries.push({ outputPath, source });
3651
3895
  }
3652
3896
  const allSourceCode = fileEntries.map((e) => e.source).join("\n");
3653
- const distDir = path13.resolve(rootDir, dist);
3897
+ const distDir = path14.resolve(rootDir, dist);
3654
3898
  await maybeGenerateHelpers(allSourceCode, distDir);
3655
3899
  await Promise.all(
3656
3900
  fileEntries.map(({ outputPath, source }) => writeToolSchemaFile(outputPath, source))
@@ -3764,14 +4008,34 @@ function cors(options = {}) {
3764
4008
  }
3765
4009
 
3766
4010
  // src/middleware/logger.ts
4011
+ function levelForStatus(status) {
4012
+ if (status >= 500) return "error";
4013
+ if (status >= 400) return "warn";
4014
+ return "info";
4015
+ }
4016
+ function defaultLog(entry, text) {
4017
+ if (isAccessLogPiped()) {
4018
+ const logEntry = {
4019
+ level: levelForStatus(entry.status),
4020
+ message: text,
4021
+ time: (/* @__PURE__ */ new Date()).toISOString(),
4022
+ scope: "access",
4023
+ fields: entry
4024
+ };
4025
+ writeLogEntry(logEntry);
4026
+ return;
4027
+ }
4028
+ console.log(entry);
4029
+ }
3767
4030
  function logger(options = {}) {
3768
4031
  return async (ctx, next) => {
3769
- const log = options.log ?? console.log;
4032
+ const log = options.log ?? defaultLog;
3770
4033
  const start = Date.now();
3771
4034
  try {
3772
4035
  const response = await next();
3773
4036
  const duration = Date.now() - start;
3774
4037
  const entry = {
4038
+ requestId: ctx.requestId,
3775
4039
  method: ctx.method,
3776
4040
  path: ctx.path,
3777
4041
  status: response.status,
@@ -3784,6 +4048,7 @@ function logger(options = {}) {
3784
4048
  const message = err instanceof Error ? err.message : String(err);
3785
4049
  const status = err?.statusCode ?? 500;
3786
4050
  const entry = {
4051
+ requestId: ctx.requestId,
3787
4052
  method: ctx.method,
3788
4053
  path: ctx.path,
3789
4054
  status,
@@ -3859,16 +4124,16 @@ function helmet(options = {}) {
3859
4124
  }
3860
4125
 
3861
4126
  // src/config/loadConfig.ts
3862
- import path14 from "path";
3863
- import fs12 from "fs";
4127
+ import path15 from "path";
4128
+ import fs13 from "fs";
3864
4129
  var CONFIG_PRODUCT_FILE = "faapi-config.js";
3865
4130
  async function loadConfig(rootDir, dist) {
3866
- const configProductPath = path14.resolve(rootDir, dist, CONFIG_PRODUCT_FILE);
3867
- if (fs12.existsSync(configProductPath)) {
4131
+ const configProductPath = path15.resolve(rootDir, dist, CONFIG_PRODUCT_FILE);
4132
+ if (fs13.existsSync(configProductPath)) {
3868
4133
  const module = await importWithCacheBust(configProductPath);
3869
4134
  return module.default ?? {};
3870
4135
  }
3871
- const hasSourceConfig = fs12.existsSync(path14.join(rootDir, "faapi.config.ts")) || fs12.existsSync(path14.join(rootDir, "faapi.config.js"));
4136
+ const hasSourceConfig = fs13.existsSync(path15.join(rootDir, "faapi.config.ts")) || fs13.existsSync(path15.join(rootDir, "faapi.config.js"));
3872
4137
  if (hasSourceConfig) {
3873
4138
  throw new Error(
3874
4139
  `[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`
@@ -3878,8 +4143,8 @@ async function loadConfig(rootDir, dist) {
3878
4143
  }
3879
4144
 
3880
4145
  // src/cli/loadEnv.ts
3881
- import fs13 from "fs";
3882
- import path15 from "path";
4146
+ import fs14 from "fs";
4147
+ import path16 from "path";
3883
4148
  function resolveEnv() {
3884
4149
  return process.env.NODE_ENV || "development";
3885
4150
  }
@@ -3946,9 +4211,9 @@ function loadEnv(rootDir) {
3946
4211
  const files = getEnvFiles(env);
3947
4212
  const merged = {};
3948
4213
  for (const file of files) {
3949
- const filePath = path15.join(rootDir, file);
3950
- if (!fs13.existsSync(filePath)) continue;
3951
- const content = fs13.readFileSync(filePath, "utf-8");
4214
+ const filePath = path16.join(rootDir, file);
4215
+ if (!fs14.existsSync(filePath)) continue;
4216
+ const content = fs14.readFileSync(filePath, "utf-8");
3952
4217
  const parsed = parseEnvFile(content, merged);
3953
4218
  Object.assign(merged, parsed);
3954
4219
  }
@@ -3960,8 +4225,8 @@ function loadEnv(rootDir) {
3960
4225
  }
3961
4226
 
3962
4227
  // src/cli/createAppCore.ts
3963
- import fs18 from "fs";
3964
- import path23 from "path";
4228
+ import fs19 from "fs";
4229
+ import path24 from "path";
3965
4230
  import { PassThrough, Readable as Readable3 } from "stream";
3966
4231
 
3967
4232
  // src/router/sortRoutes.ts
@@ -4014,7 +4279,7 @@ import {
4014
4279
  import { createSecureServer as createHttp2SecureServer } from "http2";
4015
4280
  import { readFileSync } from "fs";
4016
4281
  import { Readable as Readable2 } from "stream";
4017
- import path17 from "path";
4282
+ import path18 from "path";
4018
4283
 
4019
4284
  // src/router/matchRoute.ts
4020
4285
  var httpIndexCache = /* @__PURE__ */ new WeakMap();
@@ -4056,18 +4321,18 @@ function getWsIndex(routes) {
4056
4321
  wsIndexCache.set(routes, index);
4057
4322
  return index;
4058
4323
  }
4059
- function matchRoute(routes, method, path28) {
4324
+ function matchRoute(routes, method, path29) {
4060
4325
  const index = getHttpIndex(routes);
4061
4326
  const upper = method.toUpperCase();
4062
- const hit = matchByMethod(index, upper, path28);
4327
+ const hit = matchByMethod(index, upper, path29);
4063
4328
  if (hit) return hit;
4064
4329
  if (upper === "HEAD") {
4065
- return matchByMethod(index, "GET", path28);
4330
+ return matchByMethod(index, "GET", path29);
4066
4331
  }
4067
4332
  return null;
4068
4333
  }
4069
- function matchByMethod(index, method, path28) {
4070
- const staticHit = index.static.get(`${method}|${path28}`);
4334
+ function matchByMethod(index, method, path29) {
4335
+ const staticHit = index.static.get(`${method}|${path29}`);
4071
4336
  if (staticHit) {
4072
4337
  return { route: staticHit, params: {} };
4073
4338
  }
@@ -4076,31 +4341,31 @@ function matchByMethod(index, method, path28) {
4076
4341
  if (route.method !== method) {
4077
4342
  continue;
4078
4343
  }
4079
- const params = matchSegments(entry.segments, path28, route.paramNames, route.isCatchAll);
4344
+ const params = matchSegments(entry.segments, path29, route.paramNames, route.isCatchAll);
4080
4345
  if (params !== null) {
4081
4346
  return { route, params };
4082
4347
  }
4083
4348
  }
4084
4349
  return null;
4085
4350
  }
4086
- function matchWsRoute(wsRoutes, path28) {
4351
+ function matchWsRoute(wsRoutes, path29) {
4087
4352
  const index = getWsIndex(wsRoutes);
4088
- const staticHit = index.static.get(path28);
4353
+ const staticHit = index.static.get(path29);
4089
4354
  if (staticHit) {
4090
4355
  return { route: staticHit, params: {} };
4091
4356
  }
4092
4357
  for (const route of index.dynamics) {
4093
- const params = matchDynamicPath(route.urlPath, path28, route.paramNames, route.isCatchAll);
4358
+ const params = matchDynamicPath(route.urlPath, path29, route.paramNames, route.isCatchAll);
4094
4359
  if (params !== null) {
4095
4360
  return { route, params };
4096
4361
  }
4097
4362
  }
4098
4363
  return null;
4099
4364
  }
4100
- function findAllowedMethods(routes, path28) {
4365
+ function findAllowedMethods(routes, path29) {
4101
4366
  const index = getHttpIndex(routes);
4102
4367
  const methods = /* @__PURE__ */ new Set();
4103
- const staticMethods = index.methodsByStaticPath.get(path28);
4368
+ const staticMethods = index.methodsByStaticPath.get(path29);
4104
4369
  if (staticMethods) {
4105
4370
  for (const method of staticMethods) {
4106
4371
  methods.add(method);
@@ -4109,7 +4374,7 @@ function findAllowedMethods(routes, path28) {
4109
4374
  for (const entry of index.dynamics) {
4110
4375
  const params = matchSegments(
4111
4376
  entry.segments,
4112
- path28,
4377
+ path29,
4113
4378
  entry.route.paramNames,
4114
4379
  entry.route.isCatchAll
4115
4380
  );
@@ -4122,11 +4387,11 @@ function findAllowedMethods(routes, path28) {
4122
4387
  }
4123
4388
  return Array.from(methods);
4124
4389
  }
4125
- function matchDynamicPath(pattern, path28, paramNames, isCatchAll) {
4126
- return matchSegments(pattern.split("/").filter(Boolean), path28, paramNames, isCatchAll);
4390
+ function matchDynamicPath(pattern, path29, paramNames, isCatchAll) {
4391
+ return matchSegments(pattern.split("/").filter(Boolean), path29, paramNames, isCatchAll);
4127
4392
  }
4128
- function matchSegments(patternSegments, path28, paramNames, isCatchAll) {
4129
- const pathSegments = path28.split("/").filter(Boolean);
4393
+ function matchSegments(patternSegments, path29, paramNames, isCatchAll) {
4394
+ const pathSegments = path29.split("/").filter(Boolean);
4130
4395
  if (isCatchAll) {
4131
4396
  const nonCatchAllCount = patternSegments.length - 1;
4132
4397
  if (pathSegments.length <= nonCatchAllCount) {
@@ -4406,6 +4671,12 @@ function formatErrorResponse(error, config) {
4406
4671
  }
4407
4672
 
4408
4673
  // src/runtime/createContext.ts
4674
+ function resolveRequestId(request) {
4675
+ const header = request.headers.get("x-request-id");
4676
+ const first = header?.split(",")[0]?.trim();
4677
+ if (first) return first;
4678
+ return crypto.randomUUID();
4679
+ }
4409
4680
  function parseCookies(cookieHeader) {
4410
4681
  const cookies = /* @__PURE__ */ new Map();
4411
4682
  if (!cookieHeader) return cookies;
@@ -4439,7 +4710,13 @@ function createContextFromUrl(request, url, params, config = {}, ip = "", regist
4439
4710
  for (const [key, val] of parsedCookies) {
4440
4711
  cookiesObj[key] = val;
4441
4712
  }
4713
+ const requestId = resolveRequestId(request);
4714
+ const log = createLogger("http", {
4715
+ fields: { requestId, method: request.method, path: url.pathname }
4716
+ });
4442
4717
  const ctx = {
4718
+ requestId,
4719
+ log,
4443
4720
  request,
4444
4721
  params,
4445
4722
  query: url.searchParams,
@@ -4824,6 +5101,9 @@ function getBuiltinInjectionValue(type, ctx, body) {
4824
5101
  // 任务子系统:注入 TaskClient(入队/查询);未注册工厂(无 app 编排)时 undefined
4825
5102
  case "tasks":
4826
5103
  return ctx.registries ? ctx.registries.taskHandle.get(ctx) : void 0;
5104
+ // 请求级日志器:与 ctx.log 同一实例(scope http,自动带 requestId/method/path 字段)
5105
+ case "log":
5106
+ return ctx.log;
4827
5107
  default:
4828
5108
  return void 0;
4829
5109
  }
@@ -5018,9 +5298,9 @@ async function validateInput(schemaPath, method, inputType, input) {
5018
5298
  function mapZodIssues(error) {
5019
5299
  return error.issues.map((issue) => {
5020
5300
  const code = mapZodCode(issue);
5021
- const path28 = issue.path.map(String).join(".") || "";
5301
+ const path29 = issue.path.map(String).join(".") || "";
5022
5302
  return {
5023
- path: path28,
5303
+ path: path29,
5024
5304
  code,
5025
5305
  expected: issue.expected ?? mapExpectedFromMessage(issue.message),
5026
5306
  received: issue.received ?? mapReceivedFromMessage(issue.message),
@@ -5252,9 +5532,9 @@ function etag(options = {}) {
5252
5532
  }
5253
5533
 
5254
5534
  // src/server/handleWsUpgrade.ts
5255
- import fs14 from "fs";
5535
+ import fs15 from "fs";
5256
5536
  import { WebSocketServer, WebSocket } from "ws";
5257
- import path16 from "path";
5537
+ import path17 from "path";
5258
5538
 
5259
5539
  // src/server/serverUtils.ts
5260
5540
  function nodeHttpToWebHeaders(req) {
@@ -5389,7 +5669,7 @@ async function loadWsHandler(filePath, ctx, rootDir) {
5389
5669
  const dist = getDevDist();
5390
5670
  if (dist) {
5391
5671
  const sourcePath = prodPathToSourcePath(filePath, rootDir, dist);
5392
- if (sourcePath && fs14.existsSync(sourcePath)) {
5672
+ if (sourcePath && fs15.existsSync(sourcePath)) {
5393
5673
  await ensureCompiled(sourcePath, rootDir, dist);
5394
5674
  }
5395
5675
  }
@@ -5503,7 +5783,7 @@ function attachWebSocket(options) {
5503
5783
  const finalHandler = async () => {
5504
5784
  let handlers;
5505
5785
  try {
5506
- const absoluteFilePath = path16.resolve(rootDir, route.filePath);
5786
+ const absoluteFilePath = path17.resolve(rootDir, route.filePath);
5507
5787
  handlers = await loadWsHandler(absoluteFilePath, ctx, rootDir);
5508
5788
  } catch (err) {
5509
5789
  const reason = err instanceof Error ? err.message : String(err);
@@ -5756,7 +6036,7 @@ function getRoutePaths(route, rootDir, dist) {
5756
6036
  let cached = routePathCache.get(route);
5757
6037
  if (!cached) {
5758
6038
  cached = {
5759
- absFilePath: path17.resolve(rootDir, route.filePath),
6039
+ absFilePath: path18.resolve(rootDir, route.filePath),
5760
6040
  schemaPath: getRuntimeSchemaPath(route.filePath, dist, rootDir)
5761
6041
  };
5762
6042
  routePathCache.set(route, cached);
@@ -5878,8 +6158,8 @@ function applyPluginWrappers(server, handlerWrappers, upgradeWrappers) {
5878
6158
  }
5879
6159
 
5880
6160
  // src/cli/generateRoutes.ts
5881
- import fs15 from "fs";
5882
- import path18 from "path";
6161
+ import fs16 from "fs";
6162
+ import path19 from "path";
5883
6163
  async function hydrateRoutes(manifest) {
5884
6164
  const hydrateRoute = (serialized) => ({
5885
6165
  method: serialized.method,
@@ -5904,7 +6184,7 @@ async function hydrateRoutes(manifest) {
5904
6184
  }
5905
6185
 
5906
6186
  // src/cli/generateAgentArtifacts.ts
5907
- import path19 from "path";
6187
+ import path20 from "path";
5908
6188
 
5909
6189
  // src/ast/extractAgentMetadata.ts
5910
6190
  import ts10 from "typescript";
@@ -6177,9 +6457,9 @@ function validateAgentList(metadata) {
6177
6457
  }
6178
6458
  async function generateAgentArtifacts(agents, rootDir, dist) {
6179
6459
  const metadata = [];
6180
- const programByFile = createPrograms(agents.map((m) => path19.resolve(rootDir, m.filePath)));
6460
+ const programByFile = createPrograms(agents.map((m) => path20.resolve(rootDir, m.filePath)));
6181
6461
  for (const manifest of agents) {
6182
- const absPath = path19.resolve(rootDir, manifest.filePath);
6462
+ const absPath = path20.resolve(rootDir, manifest.filePath);
6183
6463
  const program = programByFile.get(absPath);
6184
6464
  const result = extractAgentMetadata(program, absPath, {
6185
6465
  name: manifest.name,
@@ -6195,13 +6475,13 @@ async function generateAgentArtifacts(agents, rootDir, dist) {
6195
6475
  }
6196
6476
  validateAgentList(metadata);
6197
6477
  const serialized = serializeAgents(metadata, dist);
6198
- const agentsPath = path19.resolve(rootDir, dist, AGENTS_FILE);
6478
+ const agentsPath = path20.resolve(rootDir, dist, AGENTS_FILE);
6199
6479
  await writeAgentsModule(serialized, agentsPath);
6200
6480
  return metadata;
6201
6481
  }
6202
6482
 
6203
6483
  // src/cli/generateTaskArtifacts.ts
6204
- import path20 from "path";
6484
+ import path21 from "path";
6205
6485
  init_createProgram();
6206
6486
  init_atomicWrite();
6207
6487
  init_generateSchemaFiles();
@@ -6240,7 +6520,7 @@ function collectTaskSchemaSources(tasks, rootDir) {
6240
6520
  const tasksByFile = /* @__PURE__ */ new Map();
6241
6521
  for (const task of tasks) {
6242
6522
  if (!task.inputTypeName) continue;
6243
- const absPath = path20.resolve(rootDir, task.filePath);
6523
+ const absPath = path21.resolve(rootDir, task.filePath);
6244
6524
  let list = tasksByFile.get(absPath);
6245
6525
  if (!list) {
6246
6526
  list = [];
@@ -6297,9 +6577,9 @@ function generateTaskSchemaFileSource(sources, resolveType, helpersImportPath) {
6297
6577
  async function generateTaskArtifacts(manifests, rootDir, dist) {
6298
6578
  const metadata = [];
6299
6579
  if (manifests.length > 0) {
6300
- const programByFile = createPrograms(manifests.map((m) => path20.resolve(rootDir, m.filePath)));
6580
+ const programByFile = createPrograms(manifests.map((m) => path21.resolve(rootDir, m.filePath)));
6301
6581
  for (const manifest of manifests) {
6302
- const absPath = path20.resolve(rootDir, manifest.filePath);
6582
+ const absPath = path21.resolve(rootDir, manifest.filePath);
6303
6583
  const result = extractToolMetadata(programByFile.get(absPath), absPath, "run", {
6304
6584
  name: manifest.name,
6305
6585
  filePath: manifest.filePath
@@ -6310,7 +6590,7 @@ async function generateTaskArtifacts(manifests, rootDir, dist) {
6310
6590
  }
6311
6591
  }
6312
6592
  const serialized = serializeTasks(manifests, dist);
6313
- const tasksPath = path20.resolve(rootDir, dist, TASKS_FILE);
6593
+ const tasksPath = path21.resolve(rootDir, dist, TASKS_FILE);
6314
6594
  await writeTasksModule(serialized, tasksPath);
6315
6595
  if (metadata.length === 0) {
6316
6596
  return hydrateTasks(serialized);
@@ -6330,7 +6610,7 @@ async function generateTaskArtifacts(manifests, rootDir, dist) {
6330
6610
  }
6331
6611
  const fileEntries = [];
6332
6612
  for (const [filePath, fileSources] of sourcesByFile) {
6333
- const relFile = path20.relative(rootDir, filePath).replace(/\\/g, "/");
6613
+ const relFile = path21.relative(rootDir, filePath).replace(/\\/g, "/");
6334
6614
  const outputPath = getSchemaOutputPath(relFile, dist, rootDir);
6335
6615
  const resolver = resolversByFile.get(filePath);
6336
6616
  let relForDir = relFile;
@@ -6349,7 +6629,7 @@ async function generateTaskArtifacts(manifests, rootDir, dist) {
6349
6629
  }
6350
6630
  const allSourceCode = fileEntries.map((e) => e.source).join("\n");
6351
6631
  if (usesCoerceHelpers(allSourceCode)) {
6352
- const helpersPath = path20.resolve(rootDir, dist, HELPERS_FILENAME);
6632
+ const helpersPath = path21.resolve(rootDir, dist, HELPERS_FILENAME);
6353
6633
  const { existsSync: existsSync3 } = await import("fs");
6354
6634
  if (!existsSync3(helpersPath)) {
6355
6635
  await atomicWriteFile(helpersPath, generateHelpersFileSource());
@@ -6381,18 +6661,18 @@ function createIdleTaskDriver() {
6381
6661
  }
6382
6662
 
6383
6663
  // src/cli/loadPlugins.ts
6384
- import path22 from "path";
6385
- import fs17 from "fs";
6664
+ import path23 from "path";
6665
+ import fs18 from "fs";
6386
6666
  import { pathToFileURL as pathToFileURL3 } from "url";
6387
6667
 
6388
6668
  // src/cli/compileConfig.ts
6389
- import path21 from "path";
6390
- import fs16 from "fs";
6669
+ import path22 from "path";
6670
+ import fs17 from "fs";
6391
6671
  async function compileProjectModules(entryPoints, rootDir, dist) {
6392
6672
  const { insideFiles, outsideFiles } = await collectRelativeImports(entryPoints, rootDir);
6393
6673
  const esbuild = await import("esbuild");
6394
6674
  const aliasPlugins = buildAliasPlugins(rootDir);
6395
- const absDist = path21.resolve(rootDir, dist);
6675
+ const absDist = path22.resolve(rootDir, dist);
6396
6676
  await esbuild.build({
6397
6677
  entryPoints: [...entryPoints, ...outsideFiles],
6398
6678
  outdir: absDist,
@@ -6406,7 +6686,7 @@ async function compileProjectModules(entryPoints, rootDir, dist) {
6406
6686
  logLevel: "silent"
6407
6687
  });
6408
6688
  if (insideFiles.length > 0) {
6409
- const appOutbase = path21.resolve(rootDir, "src");
6689
+ const appOutbase = path22.resolve(rootDir, "src");
6410
6690
  await esbuild.build({
6411
6691
  entryPoints: insideFiles,
6412
6692
  outdir: absDist,
@@ -6487,37 +6767,37 @@ async function loadPlugins(declarations, ctx, rootDir, dist) {
6487
6767
  var TS_EXTS = /\.(ts|tsx|mts|cts)$/;
6488
6768
  var JS_EXTS = /\.(js|mjs|cjs|jsx)$/;
6489
6769
  function resolveLocalPluginSource(specifier, baseDir) {
6490
- const base = path22.isAbsolute(specifier) ? specifier : path22.resolve(baseDir, specifier);
6491
- if ((TS_EXTS.test(base) || JS_EXTS.test(base)) && fs17.existsSync(base)) return base;
6770
+ const base = path23.isAbsolute(specifier) ? specifier : path23.resolve(baseDir, specifier);
6771
+ if ((TS_EXTS.test(base) || JS_EXTS.test(base)) && fs18.existsSync(base)) return base;
6492
6772
  for (const ext of [".ts", ".js"]) {
6493
- if (fs17.existsSync(base + ext)) return base + ext;
6773
+ if (fs18.existsSync(base + ext)) return base + ext;
6494
6774
  }
6495
6775
  for (const indexExt of ["/index.ts", "/index.js"]) {
6496
- if (fs17.existsSync(base + indexExt)) return base + indexExt;
6776
+ if (fs18.existsSync(base + indexExt)) return base + indexExt;
6497
6777
  }
6498
6778
  return null;
6499
6779
  }
6500
6780
  function mirrorProductPath(sourcePath, baseDir, distDir) {
6501
- let rel = path22.relative(baseDir, sourcePath).replace(/\\/g, "/");
6781
+ let rel = path23.relative(baseDir, sourcePath).replace(/\\/g, "/");
6502
6782
  if (rel.startsWith("src/")) rel = rel.slice(4);
6503
- return path22.resolve(distDir, rel.replace(TS_EXTS, ".js"));
6783
+ return path23.resolve(distDir, rel.replace(TS_EXTS, ".js"));
6504
6784
  }
6505
6785
  function isSourceNewer(sourcePath, productPath) {
6506
6786
  try {
6507
- return fs17.statSync(sourcePath).mtimeMs > fs17.statSync(productPath).mtimeMs;
6787
+ return fs18.statSync(sourcePath).mtimeMs > fs18.statSync(productPath).mtimeMs;
6508
6788
  } catch {
6509
6789
  return true;
6510
6790
  }
6511
6791
  }
6512
6792
  async function importPluginModule(specifier, baseDir, dist) {
6513
6793
  const isRelative = specifier.startsWith("./") || specifier.startsWith("../");
6514
- if (!isRelative && !path22.isAbsolute(specifier)) {
6794
+ if (!isRelative && !path23.isAbsolute(specifier)) {
6515
6795
  return import(specifier);
6516
6796
  }
6517
- const distDir = dist ? path22.resolve(baseDir, dist) : null;
6797
+ const distDir = dist ? path23.resolve(baseDir, dist) : null;
6518
6798
  const sourcePath = resolveLocalPluginSource(specifier, baseDir);
6519
6799
  const productPath = sourcePath && distDir ? mirrorProductPath(sourcePath, baseDir, distDir) : null;
6520
- if (productPath !== null && fs17.existsSync(productPath) && (sourcePath === null || !isSourceNewer(sourcePath, productPath))) {
6800
+ if (productPath !== null && fs18.existsSync(productPath) && (sourcePath === null || !isSourceNewer(sourcePath, productPath))) {
6521
6801
  return import(pathToFileURL3(productPath).href);
6522
6802
  }
6523
6803
  if (sourcePath && TS_EXTS.test(sourcePath)) {
@@ -6526,13 +6806,13 @@ async function importPluginModule(specifier, baseDir, dist) {
6526
6806
  `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.`
6527
6807
  );
6528
6808
  }
6529
- const relFromRoot = path22.relative(baseDir, sourcePath).replace(/\\/g, "/");
6809
+ const relFromRoot = path23.relative(baseDir, sourcePath).replace(/\\/g, "/");
6530
6810
  if (relFromRoot.startsWith("src/")) {
6531
6811
  await ensureCompiled(sourcePath, baseDir, dist);
6532
6812
  } else {
6533
6813
  await compileProjectModules([sourcePath], baseDir, dist);
6534
6814
  }
6535
- if (productPath && fs17.existsSync(productPath)) {
6815
+ if (productPath && fs18.existsSync(productPath)) {
6536
6816
  return import(pathToFileURL3(productPath).href);
6537
6817
  }
6538
6818
  }
@@ -6544,7 +6824,7 @@ async function importPluginModule(specifier, baseDir, dist) {
6544
6824
  `${specifier}/index.ts`,
6545
6825
  `${specifier}.js`,
6546
6826
  `${specifier}/index.js`
6547
- ].map((c) => path22.isAbsolute(c) ? c : path22.join(baseDir, c));
6827
+ ].map((c) => path23.isAbsolute(c) ? c : path23.join(baseDir, c));
6548
6828
  throw new Error(
6549
6829
  `Cannot find local plugin "${specifier}" (resolved from ${baseDir}). Expected one of:
6550
6830
  ${candidates.join("\n ")}
@@ -6575,8 +6855,8 @@ var ROUTES_FILE = "faapi-routes.js";
6575
6855
  var TOOLS_FILE2 = "faapi-tools.js";
6576
6856
  var AGENTS_FILE2 = "faapi-agents.js";
6577
6857
  async function loadAndHydrateTools(rootDir, dist, registries = defaultRegistries) {
6578
- const toolsPath = path23.resolve(rootDir, dist, TOOLS_FILE2);
6579
- if (!fs18.existsSync(toolsPath)) {
6858
+ const toolsPath = path24.resolve(rootDir, dist, TOOLS_FILE2);
6859
+ if (!fs19.existsSync(toolsPath)) {
6580
6860
  return [];
6581
6861
  }
6582
6862
  const serialized = await importWithCacheBust(toolsPath);
@@ -6585,8 +6865,8 @@ async function loadAndHydrateTools(rootDir, dist, registries = defaultRegistries
6585
6865
  return hydrated;
6586
6866
  }
6587
6867
  async function loadAndHydrateAgents(rootDir, dist, registries = defaultRegistries) {
6588
- const agentsPath = path23.resolve(rootDir, dist, AGENTS_FILE2);
6589
- if (!fs18.existsSync(agentsPath)) {
6868
+ const agentsPath = path24.resolve(rootDir, dist, AGENTS_FILE2);
6869
+ if (!fs19.existsSync(agentsPath)) {
6590
6870
  return [];
6591
6871
  }
6592
6872
  const serialized = await importWithCacheBust(agentsPath);
@@ -6601,8 +6881,8 @@ function getTaskDriverOptions(config) {
6601
6881
  return void 0;
6602
6882
  }
6603
6883
  async function loadAndHydrateTasks(rootDir, dist, registries = defaultRegistries) {
6604
- const tasksPath = path23.resolve(rootDir, dist, TASKS_FILE);
6605
- if (!fs18.existsSync(tasksPath)) {
6884
+ const tasksPath = path24.resolve(rootDir, dist, TASKS_FILE);
6885
+ if (!fs19.existsSync(tasksPath)) {
6606
6886
  return [];
6607
6887
  }
6608
6888
  const serialized = await importWithCacheBust(tasksPath);
@@ -6659,6 +6939,7 @@ var FAAPI_CONFIG_KEYS = /* @__PURE__ */ new Set([
6659
6939
  "etag",
6660
6940
  "bodyLimit",
6661
6941
  "logger",
6942
+ "log",
6662
6943
  "http2",
6663
6944
  "trustedProxy",
6664
6945
  "response",
@@ -6670,13 +6951,14 @@ function isFaapiConfigKey(key) {
6670
6951
  async function createAppBase(options) {
6671
6952
  const rootDir = options?.rootDir ?? process.cwd();
6672
6953
  const dist = options?.dist ?? process.env.FAAPI_DIST ?? DEFAULT_DIST;
6673
- const routesPath = path23.resolve(rootDir, dist, ROUTES_FILE);
6674
- if (!fs18.existsSync(routesPath)) {
6954
+ const routesPath = path24.resolve(rootDir, dist, ROUTES_FILE);
6955
+ if (!fs19.existsSync(routesPath)) {
6675
6956
  throw new Error(
6676
6957
  `[faapi] ${dist}/${ROUTES_FILE} \u4E0D\u5B58\u5728\uFF0C\u8BF7\u5148\u6267\u884C \`faapi build\`\uFF08\u6216 \`faapi dev\`\uFF09\u751F\u6210\u4EA7\u7269\u3002`
6677
6958
  );
6678
6959
  }
6679
6960
  const config = await loadConfig(rootDir, dist);
6961
+ configureLogging(config?.log);
6680
6962
  const serialized = await importWithCacheBust(routesPath);
6681
6963
  const hydrated = await hydrateRoutes(serialized);
6682
6964
  let sorted = sortRoutes(hydrated.routes);
@@ -6998,21 +7280,21 @@ async function createAppBase(options) {
6998
7280
  }
6999
7281
 
7000
7282
  // src/cli/createDevApp.ts
7001
- import path27 from "path";
7283
+ import path28 from "path";
7002
7284
 
7003
7285
  // src/router/scanRoutes.ts
7004
7286
  import fg3 from "fast-glob";
7005
- import path24 from "path";
7006
- import fs19 from "fs";
7287
+ import path25 from "path";
7288
+ import fs20 from "fs";
7007
7289
 
7008
7290
  // src/router/constants.ts
7009
7291
  var HTTP_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"];
7010
7292
  var HTTP_METHOD_SET = new Set(HTTP_METHODS);
7011
7293
 
7012
7294
  // src/utils/normalizePath.ts
7013
- function normalizePath(path28) {
7014
- if (!path28) return "";
7015
- let result = path28.replace(/\\/g, "/");
7295
+ function normalizePath(path29) {
7296
+ if (!path29) return "";
7297
+ let result = path29.replace(/\\/g, "/");
7016
7298
  result = result.replace(/\/+/g, "/");
7017
7299
  result = result.replace(/\/+$/, "");
7018
7300
  if (result && !result.startsWith("/")) {
@@ -7073,34 +7355,34 @@ function extractExportsFromSource(source) {
7073
7355
  return names;
7074
7356
  }
7075
7357
  function collectMiddlewarePaths(routeFilePath, rootDir, dist) {
7076
- const routeDir = path24.dirname(routeFilePath);
7077
- const resolvedRoot = path24.resolve(rootDir);
7358
+ const routeDir = path25.dirname(routeFilePath);
7359
+ const resolvedRoot = path25.resolve(rootDir);
7078
7360
  const paths = [];
7079
- let currentDir = path24.resolve(rootDir, routeDir);
7361
+ let currentDir = path25.resolve(rootDir, routeDir);
7080
7362
  while (true) {
7081
7363
  if (dist) {
7082
- const mwTsPath = path24.join(currentDir, "middlewares.ts");
7083
- const mwJsPath = path24.join(currentDir, "middlewares.js");
7084
- const absTsPath = path24.resolve(rootDir, mwTsPath);
7085
- const absJsPath = path24.resolve(rootDir, mwJsPath);
7086
- const absMwPath = fs19.existsSync(absTsPath) ? absTsPath : fs19.existsSync(absJsPath) ? absJsPath : null;
7364
+ const mwTsPath = path25.join(currentDir, "middlewares.ts");
7365
+ const mwJsPath = path25.join(currentDir, "middlewares.js");
7366
+ const absTsPath = path25.resolve(rootDir, mwTsPath);
7367
+ const absJsPath = path25.resolve(rootDir, mwJsPath);
7368
+ const absMwPath = fs20.existsSync(absTsPath) ? absTsPath : fs20.existsSync(absJsPath) ? absJsPath : null;
7087
7369
  if (absMwPath) {
7088
- const relMwPath = path24.relative(rootDir, absMwPath);
7089
- const prodAbsPath = path24.resolve(rootDir, toProdFilePath(relMwPath, dist));
7370
+ const relMwPath = path25.relative(rootDir, absMwPath);
7371
+ const prodAbsPath = path25.resolve(rootDir, toProdFilePath(relMwPath, dist));
7090
7372
  paths.push(prodAbsPath);
7091
7373
  }
7092
7374
  } else {
7093
7375
  for (const ext of [".ts", ".js"]) {
7094
- const mwPath = path24.join(currentDir, `middlewares${ext}`);
7095
- const absMwPath = path24.resolve(rootDir, mwPath);
7096
- if (fs19.existsSync(absMwPath)) {
7376
+ const mwPath = path25.join(currentDir, `middlewares${ext}`);
7377
+ const absMwPath = path25.resolve(rootDir, mwPath);
7378
+ if (fs20.existsSync(absMwPath)) {
7097
7379
  paths.push(absMwPath);
7098
7380
  break;
7099
7381
  }
7100
7382
  }
7101
7383
  }
7102
7384
  if (currentDir === resolvedRoot) break;
7103
- const parentDir = path24.dirname(currentDir);
7385
+ const parentDir = path25.dirname(currentDir);
7104
7386
  if (parentDir === currentDir) break;
7105
7387
  currentDir = parentDir;
7106
7388
  }
@@ -7119,7 +7401,7 @@ async function scanRoutes(rootDir, patterns, dist) {
7119
7401
  const normalizedFile = file.replace(/\\/g, "/");
7120
7402
  const fileName = normalizedFile.split("/").pop();
7121
7403
  if (fileName === "handler.ts" || fileName === "handler.js") {
7122
- const absPath = path24.resolve(rootDir, normalizedFile);
7404
+ const absPath = path25.resolve(rootDir, normalizedFile);
7123
7405
  const urlPath = filePathToUrlPath(normalizedFile);
7124
7406
  const paramNames = extractParamNames(urlPath);
7125
7407
  const isDynamic = paramNames.length > 0;
@@ -7132,7 +7414,7 @@ async function scanRoutes(rootDir, patterns, dist) {
7132
7414
  const mwPaths = collectMiddlewarePaths(normalizedFile, rootDir);
7133
7415
  middlewareBundle = await loadMergedMiddlewares(mwPaths);
7134
7416
  }
7135
- const source = await fs19.promises.readFile(absPath, "utf8").catch(() => "");
7417
+ const source = await fs20.promises.readFile(absPath, "utf8").catch(() => "");
7136
7418
  const exportNames = extractExportsFromSource(source);
7137
7419
  const methods = HTTP_METHODS.filter((m) => exportNames.has(m));
7138
7420
  for (const method of methods) {
@@ -7168,8 +7450,8 @@ async function scanRoutes(rootDir, patterns, dist) {
7168
7450
 
7169
7451
  // src/tools/scanTools.ts
7170
7452
  import fg4 from "fast-glob";
7171
- import path25 from "path";
7172
- import fs20 from "fs";
7453
+ import path26 from "path";
7454
+ import fs21 from "fs";
7173
7455
  var TOOL_PATTERNS = ["src/tools/**/*.ts"];
7174
7456
  var TOOL_EXPORT_RE = new RegExp(
7175
7457
  String.raw`export\s+(?:async\s+)?(?:function\s+|const\s+)([A-Za-z_$][\w$]*)\s*(?:\(|=)`,
@@ -7219,8 +7501,8 @@ async function scanTools(rootDir, patterns) {
7219
7501
  if (fileName !== "handler.ts" && fileName !== "handler.js") {
7220
7502
  continue;
7221
7503
  }
7222
- const absPath = path25.resolve(rootDir, normalizedFile);
7223
- const source = await fs20.promises.readFile(absPath, "utf8").catch(() => "");
7504
+ const absPath = path26.resolve(rootDir, normalizedFile);
7505
+ const source = await fs21.promises.readFile(absPath, "utf8").catch(() => "");
7224
7506
  const exportNames = extractToolExportsFromSource(source);
7225
7507
  const namespace = filePathToToolNamespace(normalizedFile);
7226
7508
  for (const fnName of exportNames) {
@@ -7244,8 +7526,8 @@ async function scanTools(rootDir, patterns) {
7244
7526
 
7245
7527
  // src/agents/scanAgents.ts
7246
7528
  import fg5 from "fast-glob";
7247
- import path26 from "path";
7248
- import fs21 from "fs";
7529
+ import path27 from "path";
7530
+ import fs22 from "fs";
7249
7531
  var DEFAULT_AGENT_PATTERNS = ["src/agents/**/handler.ts"];
7250
7532
  var RUN_EXPORT_RE = /export\s+(?:async\s+)?(?:function\s+|const\s+)run\b/;
7251
7533
  function extractAgentNameFromPath(filePath) {
@@ -7277,8 +7559,8 @@ async function scanAgents(rootDir, patterns) {
7277
7559
  if (fileName !== "handler.ts" && fileName !== "handler.js") {
7278
7560
  continue;
7279
7561
  }
7280
- const absPath = path26.resolve(rootDir, normalizedFile);
7281
- const source = await fs21.promises.readFile(absPath, "utf8").catch(() => "");
7562
+ const absPath = path27.resolve(rootDir, normalizedFile);
7563
+ const source = await fs22.promises.readFile(absPath, "utf8").catch(() => "");
7282
7564
  const { hasRun } = detectAgentExports(source);
7283
7565
  const name = extractAgentNameFromPath(normalizedFile);
7284
7566
  const prevFile = seen.get(name);
@@ -7348,7 +7630,7 @@ async function createDevApp(options) {
7348
7630
  invalidateProgramCache();
7349
7631
  const tasks = await scanTasks(ctx.rootDir, TASK_PATTERNS);
7350
7632
  for (const task of tasks) {
7351
- await ensureCompiled(path27.resolve(ctx.rootDir, task.filePath), ctx.rootDir, ctx.dist);
7633
+ await ensureCompiled(path28.resolve(ctx.rootDir, task.filePath), ctx.rootDir, ctx.dist);
7352
7634
  }
7353
7635
  await generateTaskArtifacts(tasks, ctx.rootDir, ctx.dist);
7354
7636
  ctx.taskQueue.invalidateModules();
@@ -7375,11 +7657,13 @@ export {
7375
7657
  ValidationError,
7376
7658
  clearAgentHandleFactory,
7377
7659
  collectRouteSchemaSources,
7660
+ configureLogging,
7378
7661
  cors,
7379
7662
  createProdApp as createApp,
7380
7663
  createAppRegistries,
7381
7664
  createCronScheduler,
7382
7665
  createDevApp,
7666
+ createLogger,
7383
7667
  createProdApp,
7384
7668
  createProgram,
7385
7669
  createPrograms,