@faapi/faapi 6.31.0 → 6.33.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/cli/index.js +125 -88
- package/dist/cli/index.js.map +1 -1
- package/dist/{contextTypes-BKQsSVMv.d.ts → contextTypes-D1ORxB1P.d.ts} +1204 -65
- package/dist/index.d.ts +5 -760
- package/dist/index.js +234 -198
- package/dist/index.js.map +1 -1
- package/dist/testing.d.ts +2 -2
- package/dist/testing.js +49 -37
- package/dist/testing.js.map +1 -1
- package/dist/workerEntry.d.ts +20 -5
- package/dist/workerEntry.js +32 -5
- package/dist/workerEntry.js.map +1 -1
- package/package.json +1 -1
- package/dist/routeTypes-Cc0P3CcU.d.ts +0 -204
package/dist/cli/index.js
CHANGED
|
@@ -767,38 +767,30 @@ function setCachedMiddlewares(absPath, bundle) {
|
|
|
767
767
|
middlewareCache.set(absPath, bundle);
|
|
768
768
|
}
|
|
769
769
|
async function loadMiddlewaresFile(filePath) {
|
|
770
|
-
|
|
771
|
-
try {
|
|
772
|
-
module = await importWithCacheBust(filePath);
|
|
773
|
-
} catch (err) {
|
|
774
|
-
console.error(
|
|
775
|
-
`[faapi] Failed to load middlewares from ${filePath}:`,
|
|
776
|
-
err instanceof Error ? err.stack ?? err.message : err
|
|
777
|
-
);
|
|
778
|
-
return { middlewares: [], injectors: {} };
|
|
779
|
-
}
|
|
770
|
+
const module = await importWithCacheBust(filePath);
|
|
780
771
|
const middlewares = module.default ?? module.middlewares ?? [];
|
|
781
772
|
if (!Array.isArray(middlewares)) {
|
|
782
|
-
|
|
783
|
-
|
|
773
|
+
throw new TypeError(
|
|
774
|
+
`[faapi] middlewares.ts must export an array (default or named "middlewares"): ${filePath}`
|
|
775
|
+
);
|
|
784
776
|
}
|
|
785
|
-
const validMiddlewares =
|
|
777
|
+
const validMiddlewares = [];
|
|
778
|
+
for (const m of middlewares) {
|
|
786
779
|
if (typeof m !== "function") {
|
|
787
|
-
|
|
788
|
-
|
|
780
|
+
throw new TypeError(
|
|
781
|
+
`[faapi] every middleware must be a function, got ${typeof m}: ${filePath}`
|
|
782
|
+
);
|
|
789
783
|
}
|
|
790
|
-
|
|
791
|
-
}
|
|
784
|
+
validMiddlewares.push(m);
|
|
785
|
+
}
|
|
792
786
|
const injectors = module.injectors ?? {};
|
|
793
787
|
if (typeof injectors !== "object" || injectors === null) {
|
|
794
|
-
|
|
795
|
-
return { middlewares: validMiddlewares, injectors: {} };
|
|
788
|
+
throw new TypeError(`[faapi] "injectors" export must be an object: ${filePath}`);
|
|
796
789
|
}
|
|
797
790
|
const validInjectors = {};
|
|
798
791
|
for (const [name, injector] of Object.entries(injectors)) {
|
|
799
792
|
if (typeof injector !== "function") {
|
|
800
|
-
|
|
801
|
-
continue;
|
|
793
|
+
throw new TypeError(`[faapi] injector "${name}" must be a function: ${filePath}`);
|
|
802
794
|
}
|
|
803
795
|
validInjectors[name] = injector;
|
|
804
796
|
}
|
|
@@ -813,10 +805,16 @@ async function loadMergedMiddlewares(middlewarePaths) {
|
|
|
813
805
|
if (bundle === void 0) {
|
|
814
806
|
let loading = inFlight.get(absMwPath);
|
|
815
807
|
if (!loading) {
|
|
816
|
-
loading = loadMiddlewaresFile(absMwPath).then(
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
808
|
+
loading = loadMiddlewaresFile(absMwPath).then(
|
|
809
|
+
(result) => {
|
|
810
|
+
setCachedMiddlewares(absMwPath, result);
|
|
811
|
+
return result;
|
|
812
|
+
},
|
|
813
|
+
(err) => {
|
|
814
|
+
inFlight.delete(absMwPath);
|
|
815
|
+
throw err;
|
|
816
|
+
}
|
|
817
|
+
);
|
|
820
818
|
inFlight.set(absMwPath, loading);
|
|
821
819
|
}
|
|
822
820
|
bundle = await loading;
|
|
@@ -2550,6 +2548,8 @@ var init_resolveInjection = __esm({
|
|
|
2550
2548
|
// 任务子系统:TaskClient(入队/查询)
|
|
2551
2549
|
log: "log",
|
|
2552
2550
|
// 请求级日志器(ctx.log 同一实例:scope http,自动带 requestId 等字段)
|
|
2551
|
+
llm: "llm",
|
|
2552
|
+
// 轻量 LLM 补全通道(@faapi/agent 插件注册到 registries.llm;未加载时 undefined)
|
|
2553
2553
|
// raw 系:恒原始输入(无 schema、无校验、管线永不转换),与 query/params/body
|
|
2554
2554
|
// 的「校验转换后」口径二分互补
|
|
2555
2555
|
rawQuery: "rawQuery",
|
|
@@ -3627,25 +3627,27 @@ function detectIgnoredMetaFields(source, strictHit) {
|
|
|
3627
3627
|
for (const line of source.split("\n")) {
|
|
3628
3628
|
const trimmed = line.trim();
|
|
3629
3629
|
if (trimmed.startsWith("//")) continue;
|
|
3630
|
+
if (trimmed.startsWith("*") || trimmed.startsWith("/*") || trimmed.endsWith("*/")) continue;
|
|
3630
3631
|
for (const field of ALL_META_FIELDS) {
|
|
3631
3632
|
if (new RegExp(`\\b${field}\\s*:`).test(trimmed)) declared.add(field);
|
|
3632
3633
|
}
|
|
3633
3634
|
}
|
|
3634
3635
|
return ALL_META_FIELDS.filter((field) => declared.has(field) && !strictHit[field]);
|
|
3635
3636
|
}
|
|
3636
|
-
function
|
|
3637
|
-
|
|
3637
|
+
function throwIgnoredMetaFields(name, fields) {
|
|
3638
|
+
if (fields.length === 0) return;
|
|
3639
|
+
const details = fields.map((field) => {
|
|
3638
3640
|
if (STRING_META_FIELDS.includes(field)) {
|
|
3639
|
-
|
|
3640
|
-
`[faapi] Task "${name}" declares "${field}" but its value is not a string literal \u2014 the declaration was ignored and the task will not be cron-scheduled. Meta values are extracted from source code without importing it, so only literal values are recognized: use a quoted literal (cron: '0 3 * * *'), not an expression or a dynamic value.`
|
|
3641
|
-
);
|
|
3642
|
-
} else {
|
|
3643
|
-
const consequence = field === "timeoutMs" ? " and the task will run in-process with no timeout" : "";
|
|
3644
|
-
console.warn(
|
|
3645
|
-
`[faapi] Task "${name}" declares "${field}" but its value is not a numeric literal \u2014 the declaration was ignored${consequence}. Meta values are extracted from source code without importing it, so only literal values are recognized (underscores allowed, e.g. 1_800_000); expressions like 30 * 60_000 or dynamic values are not supported.`
|
|
3646
|
-
);
|
|
3641
|
+
return `"${field}": value is not a string literal \u2014 the declaration would be ignored and the task would not be cron-scheduled. Use a quoted literal (cron: '0 3 * * *'), not an expression or a dynamic value.`;
|
|
3647
3642
|
}
|
|
3648
|
-
|
|
3643
|
+
const consequence = field === "timeoutMs" ? " \u2014 the declaration would be ignored and the task would silently run in-process with no timeout" : " \u2014 the declaration would be ignored";
|
|
3644
|
+
return `"${field}": value is not a numeric literal${consequence}. Only literal values are recognized (underscores allowed, e.g. 1_800_000); expressions like 30 * 60_000 or dynamic values are not supported.`;
|
|
3645
|
+
});
|
|
3646
|
+
throw new Error(
|
|
3647
|
+
`[faapi] Task "${name}" declares meta field(s) with non-literal values:
|
|
3648
|
+
${details.join("\n ")}
|
|
3649
|
+
Meta values are extracted from source code without importing it, so only literals are recognized. If a matched line is inside a block comment (e.g. a single-line /* ... */ comment containing a meta-like declaration), rewrite the comment so it does not look like a meta declaration.`
|
|
3650
|
+
);
|
|
3649
3651
|
}
|
|
3650
3652
|
function parseNumericLiteral(raw) {
|
|
3651
3653
|
return Number(raw.replace(/_/g, ""));
|
|
@@ -3713,7 +3715,7 @@ async function scanTasks(rootDir, patterns) {
|
|
|
3713
3715
|
timeoutMs: timeoutMs !== void 0,
|
|
3714
3716
|
graceMs: graceMs !== void 0
|
|
3715
3717
|
};
|
|
3716
|
-
|
|
3718
|
+
throwIgnoredMetaFields(name, detectIgnoredMetaFields(source, strictHit));
|
|
3717
3719
|
tasks.push(manifest);
|
|
3718
3720
|
}
|
|
3719
3721
|
return tasks.sort((a, b) => a.name.localeCompare(b.name));
|
|
@@ -3828,6 +3830,7 @@ function generateTaskSchemaFileSource(sources, resolveType, helpersImportPath) {
|
|
|
3828
3830
|
}
|
|
3829
3831
|
async function generateTaskArtifacts(manifests, rootDir, dist) {
|
|
3830
3832
|
const metadata = [];
|
|
3833
|
+
const declarationErrors = [];
|
|
3831
3834
|
if (manifests.length > 0) {
|
|
3832
3835
|
const programByFile = createPrograms(manifests.map((m) => path17.resolve(rootDir, m.filePath)));
|
|
3833
3836
|
for (const manifest of manifests) {
|
|
@@ -3836,11 +3839,28 @@ async function generateTaskArtifacts(manifests, rootDir, dist) {
|
|
|
3836
3839
|
name: manifest.name,
|
|
3837
3840
|
filePath: manifest.filePath
|
|
3838
3841
|
});
|
|
3839
|
-
if (result) {
|
|
3840
|
-
|
|
3842
|
+
if (!result) {
|
|
3843
|
+
declarationErrors.push(
|
|
3844
|
+
`"${manifest.name}" (${manifest.filePath}): no exported run function found`
|
|
3845
|
+
);
|
|
3846
|
+
continue;
|
|
3841
3847
|
}
|
|
3848
|
+
if (!result.inputTypeName) {
|
|
3849
|
+
declarationErrors.push(
|
|
3850
|
+
`"${manifest.name}" (${manifest.filePath}): run's first parameter has no type name \u2014 declare a Payload type (an empty interface for parameterless tasks), or explicitly opt out of validation with "type Payload = unknown"`
|
|
3851
|
+
);
|
|
3852
|
+
continue;
|
|
3853
|
+
}
|
|
3854
|
+
metadata.push({ ...result, taskName: manifest.name });
|
|
3842
3855
|
}
|
|
3843
3856
|
}
|
|
3857
|
+
if (declarationErrors.length > 0) {
|
|
3858
|
+
throw new Error(
|
|
3859
|
+
`[faapi] ${declarationErrors.length} task(s) missing a Payload type declaration \u2014 payload validation must not be silently skipped:
|
|
3860
|
+
${declarationErrors.join("\n ")}
|
|
3861
|
+
Every task must declare a payload type on run's first parameter; "type Payload = unknown" generates an always-passing schema (explicit no-validation opt-out).`
|
|
3862
|
+
);
|
|
3863
|
+
}
|
|
3844
3864
|
const serialized = serializeTasks(manifests, dist);
|
|
3845
3865
|
const tasksPath = path17.resolve(rootDir, dist, TASKS_FILE);
|
|
3846
3866
|
await writeTasksModule(serialized, tasksPath);
|
|
@@ -4021,11 +4041,7 @@ async function ensureMiddlewaresCompiled(middlewarePaths, rootDir) {
|
|
|
4021
4041
|
if (!dist) return;
|
|
4022
4042
|
for (const mwPath of middlewarePaths) {
|
|
4023
4043
|
const sourcePath = prodPathToSourcePath(mwPath, rootDir, dist);
|
|
4024
|
-
|
|
4025
|
-
await ensureCompiled(sourcePath, rootDir, dist);
|
|
4026
|
-
} catch (err) {
|
|
4027
|
-
console.error(`[faapi] Failed to compile middleware source ${sourcePath}:`, err);
|
|
4028
|
-
}
|
|
4044
|
+
await ensureCompiled(sourcePath, rootDir, dist);
|
|
4029
4045
|
}
|
|
4030
4046
|
}
|
|
4031
4047
|
function prodSourcePathToProductPath(sourceAbsPath, rootDir, dist) {
|
|
@@ -6942,17 +6958,14 @@ function formatEntry(entry) {
|
|
|
6942
6958
|
const plain = Object.fromEntries(
|
|
6943
6959
|
Object.entries(entry.fields).map(([k, v]) => [k, serializeFieldValue(v)])
|
|
6944
6960
|
);
|
|
6945
|
-
|
|
6946
|
-
line += ` ${JSON.stringify(plain)}`;
|
|
6947
|
-
} catch (err) {
|
|
6948
|
-
line += ` [unserializable fields: ${err instanceof Error ? err.message : String(err)}]`;
|
|
6949
|
-
}
|
|
6961
|
+
line += ` ${stringifyJson(plain)}`;
|
|
6950
6962
|
}
|
|
6951
6963
|
return line;
|
|
6952
6964
|
}
|
|
6953
6965
|
var init_formatEntry = __esm({
|
|
6954
6966
|
"src/logger/formatEntry.ts"() {
|
|
6955
6967
|
"use strict";
|
|
6968
|
+
init_stringifyJson();
|
|
6956
6969
|
}
|
|
6957
6970
|
});
|
|
6958
6971
|
|
|
@@ -6968,7 +6981,14 @@ function createFileLogSink(options) {
|
|
|
6968
6981
|
};
|
|
6969
6982
|
const openStream = (name) => {
|
|
6970
6983
|
const stream = fs18.createWriteStream(path24.join(options.dir, `${name}.log`), { flags: "a" });
|
|
6971
|
-
|
|
6984
|
+
let streamErrorLogged = false;
|
|
6985
|
+
stream.on("error", (err) => {
|
|
6986
|
+
if (streamErrorLogged) return;
|
|
6987
|
+
streamErrorLogged = true;
|
|
6988
|
+
console.error(
|
|
6989
|
+
`[faapi] log file stream "${name}.log" failed \u2014 file logging on this stream stopped (subsequent errors suppressed). Check disk space / permissions for ${options.dir}:`,
|
|
6990
|
+
err
|
|
6991
|
+
);
|
|
6972
6992
|
});
|
|
6973
6993
|
streams.set(name, stream);
|
|
6974
6994
|
};
|
|
@@ -7798,6 +7818,20 @@ function createAgentHandleStore() {
|
|
|
7798
7818
|
}
|
|
7799
7819
|
};
|
|
7800
7820
|
}
|
|
7821
|
+
function createLlmChannelStore() {
|
|
7822
|
+
let current = null;
|
|
7823
|
+
return {
|
|
7824
|
+
register(channel) {
|
|
7825
|
+
current = channel;
|
|
7826
|
+
},
|
|
7827
|
+
get() {
|
|
7828
|
+
return current ?? void 0;
|
|
7829
|
+
},
|
|
7830
|
+
clear() {
|
|
7831
|
+
current = null;
|
|
7832
|
+
}
|
|
7833
|
+
};
|
|
7834
|
+
}
|
|
7801
7835
|
function createTaskHandleStore() {
|
|
7802
7836
|
let currentFactory = null;
|
|
7803
7837
|
return {
|
|
@@ -7854,7 +7888,8 @@ function createAppRegistries() {
|
|
|
7854
7888
|
const task = createTaskRegistry();
|
|
7855
7889
|
const agentHandle = createAgentHandleStore();
|
|
7856
7890
|
const taskHandle = createTaskHandleStore();
|
|
7857
|
-
|
|
7891
|
+
const llm = createLlmChannelStore();
|
|
7892
|
+
return { tool, agent, skill, task, agentHandle, taskHandle, llm };
|
|
7858
7893
|
}
|
|
7859
7894
|
var defaultRegistries;
|
|
7860
7895
|
var init_registries = __esm({
|
|
@@ -7938,6 +7973,10 @@ function getBuiltinInjectionValue(type, ctx, body) {
|
|
|
7938
7973
|
// 任务子系统:注入 TaskClient(入队/查询);未注册工厂(无 app 编排)时 undefined
|
|
7939
7974
|
case "tasks":
|
|
7940
7975
|
return ctx.registries ? ctx.registries.taskHandle.get(ctx) : void 0;
|
|
7976
|
+
// 轻量 LLM 补全通道:@faapi/agent 插件注册到 registries.llm;与请求上下文无关,
|
|
7977
|
+
// get 无参。插件未加载(或编程式直调 ctx 无 registries)时 undefined
|
|
7978
|
+
case "llm":
|
|
7979
|
+
return ctx.registries ? ctx.registries.llm.get() : void 0;
|
|
7941
7980
|
// 请求级日志器:与 ctx.log 同一实例(scope http,自动带 requestId/method/path 字段)
|
|
7942
7981
|
case "log":
|
|
7943
7982
|
return ctx.log;
|
|
@@ -9296,7 +9335,8 @@ async function sendErrorResponse(err, meta, res, onError, ctx) {
|
|
|
9296
9335
|
if (onError && ctx) {
|
|
9297
9336
|
try {
|
|
9298
9337
|
await onError(err, ctx);
|
|
9299
|
-
} catch {
|
|
9338
|
+
} catch (hookErr) {
|
|
9339
|
+
console.error("[faapi] lifecycle onError hook threw:", hookErr);
|
|
9300
9340
|
}
|
|
9301
9341
|
}
|
|
9302
9342
|
}
|
|
@@ -9440,19 +9480,6 @@ function resolveWorkerEntryUrl() {
|
|
|
9440
9480
|
workerEntryUrl = existsSync3(fileURLToPath(js)) ? js : new URL("./workerEntry.ts", import.meta.url);
|
|
9441
9481
|
return workerEntryUrl;
|
|
9442
9482
|
}
|
|
9443
|
-
function safeConfig(config) {
|
|
9444
|
-
if (config === void 0 || config === null) return config;
|
|
9445
|
-
try {
|
|
9446
|
-
structuredClone(config);
|
|
9447
|
-
return config;
|
|
9448
|
-
} catch {
|
|
9449
|
-
}
|
|
9450
|
-
try {
|
|
9451
|
-
return JSON.parse(JSON.stringify(config));
|
|
9452
|
-
} catch {
|
|
9453
|
-
return void 0;
|
|
9454
|
-
}
|
|
9455
|
-
}
|
|
9456
9483
|
function reviveError(payload) {
|
|
9457
9484
|
const err = new Error(payload?.message ?? "task worker error");
|
|
9458
9485
|
if (payload?.name !== void 0) err.name = payload.name;
|
|
@@ -9567,17 +9594,15 @@ async function runTaskInWorker(options) {
|
|
|
9567
9594
|
type: "run",
|
|
9568
9595
|
payload,
|
|
9569
9596
|
taskCtx: {
|
|
9570
|
-
config: safeConfig(taskCtx.config),
|
|
9571
9597
|
job: taskCtx.job
|
|
9572
9598
|
},
|
|
9573
9599
|
registries: options.registries,
|
|
9600
|
+
llms: options.llms,
|
|
9574
9601
|
log: options.log
|
|
9575
9602
|
});
|
|
9576
9603
|
} catch (err) {
|
|
9577
9604
|
finish(
|
|
9578
|
-
() => reject(
|
|
9579
|
-
new Error(`Task "${taskCtx.job.name}" payload/config is not cloneable: ${String(err)}`)
|
|
9580
|
-
)
|
|
9605
|
+
() => reject(new Error(`Task "${taskCtx.job.name}" payload is not cloneable: ${String(err)}`))
|
|
9581
9606
|
);
|
|
9582
9607
|
}
|
|
9583
9608
|
});
|
|
@@ -9628,13 +9653,14 @@ function createTaskQueue(deps) {
|
|
|
9628
9653
|
const loadTaskModule = deps.loadTaskModule ?? (async (filePath) => await import(filePath));
|
|
9629
9654
|
const loadPayloadSchema = deps.loadPayloadSchema ?? (async (filePath) => {
|
|
9630
9655
|
const zodPath = path28.join(path28.dirname(filePath), "zod.js");
|
|
9631
|
-
|
|
9632
|
-
|
|
9633
|
-
|
|
9634
|
-
|
|
9635
|
-
|
|
9636
|
-
|
|
9656
|
+
const mod = await import(zodPath);
|
|
9657
|
+
const schemaKey = Object.keys(mod).find((k) => k.endsWith("Schema"));
|
|
9658
|
+
if (!schemaKey) {
|
|
9659
|
+
throw new Error(
|
|
9660
|
+
`[faapi] Task payload schema artifact has no *Schema export: ${zodPath} \u2014 re-run "faapi dev" / "faapi build" to regenerate task artifacts.`
|
|
9661
|
+
);
|
|
9637
9662
|
}
|
|
9663
|
+
return mod[schemaKey];
|
|
9638
9664
|
});
|
|
9639
9665
|
function assertKnownTask(name) {
|
|
9640
9666
|
const meta = registry.get(name);
|
|
@@ -9653,14 +9679,13 @@ function createTaskQueue(deps) {
|
|
|
9653
9679
|
async function validatePayload(name, payload) {
|
|
9654
9680
|
if (!schemaCache.has(name)) {
|
|
9655
9681
|
const meta = registry.get(name);
|
|
9656
|
-
schemaCache.set(
|
|
9657
|
-
name,
|
|
9658
|
-
await loadPayloadSchema(path28.resolve(rootDir, meta.filePath)).catch(() => void 0)
|
|
9659
|
-
);
|
|
9682
|
+
schemaCache.set(name, await loadPayloadSchema(path28.resolve(rootDir, meta.filePath)));
|
|
9660
9683
|
}
|
|
9661
9684
|
const schema = schemaCache.get(name);
|
|
9662
9685
|
if (!schema || typeof schema.safeParse !== "function") {
|
|
9663
|
-
|
|
9686
|
+
throw new Error(
|
|
9687
|
+
`[faapi] Task "${name}" has no valid payload schema (zod.js missing or no *Schema export). Payload declarations are mandatory: re-run "faapi dev" / "faapi build" to regenerate task artifacts.`
|
|
9688
|
+
);
|
|
9664
9689
|
}
|
|
9665
9690
|
const result = schema.safeParse(payload);
|
|
9666
9691
|
if (!result.success) {
|
|
@@ -9694,11 +9719,15 @@ function createTaskQueue(deps) {
|
|
|
9694
9719
|
taskModulePath: path28.resolve(rootDir, meta.filePath),
|
|
9695
9720
|
payload: job.payload,
|
|
9696
9721
|
taskCtx: {
|
|
9697
|
-
config
|
|
9722
|
+
// 隔离任务不传 config:worker 线程不接收进程配置(config 含函数字段
|
|
9723
|
+
// 不可结构化克隆,框架不做降级传递)——任务数据经 payload 显式传入
|
|
9698
9724
|
resourcesDir: deps.resourcesDir,
|
|
9699
9725
|
job: { id: job.id, name: job.name, attempt: job.attempt }
|
|
9700
9726
|
},
|
|
9701
9727
|
registries: snapshotRegistries(),
|
|
9728
|
+
// agent.llms 纯数据快照随 workerData 下发,worker 内动态加载 @faapi/agent
|
|
9729
|
+
// 重建轻量补全通道(taskCtx.llm)——llms 未配置时不传
|
|
9730
|
+
llms: deps.llms,
|
|
9702
9731
|
// 日志配置随 workerData 下发(纯数据),worker 内联重建 taskCtx.log,
|
|
9703
9732
|
// 条目回传宿主走统一管道(scope/fields 与进程内路径一致)
|
|
9704
9733
|
log: {
|
|
@@ -9728,6 +9757,8 @@ function createTaskQueue(deps) {
|
|
|
9728
9757
|
config: deps.config,
|
|
9729
9758
|
job: { id: job.id, name: job.name, attempt: job.attempt },
|
|
9730
9759
|
registries: registriesView,
|
|
9760
|
+
// 轻量补全通道惰性读取(插件晚于队列构造注册,执行时刻取值才可见)
|
|
9761
|
+
llm: deps.llm?.get(),
|
|
9731
9762
|
log: createLogger(`task:${job.name}`, {
|
|
9732
9763
|
fields: { jobId: job.id, task: job.name, attempt: job.attempt }
|
|
9733
9764
|
}),
|
|
@@ -9756,7 +9787,8 @@ function createTaskQueue(deps) {
|
|
|
9756
9787
|
cancelled,
|
|
9757
9788
|
error: record.error
|
|
9758
9789
|
})
|
|
9759
|
-
).catch(() => {
|
|
9790
|
+
).catch((hookErr) => {
|
|
9791
|
+
console.error(`[faapi] task onFailed hook threw for "${job.name}":`, hookErr);
|
|
9760
9792
|
});
|
|
9761
9793
|
}
|
|
9762
9794
|
throw err;
|
|
@@ -10014,7 +10046,7 @@ async function loadPlugins(declarations, ctx, rootDir, dist) {
|
|
|
10014
10046
|
const upgradeWrappers = [];
|
|
10015
10047
|
const failures = [];
|
|
10016
10048
|
if (!declarations || declarations.length === 0) {
|
|
10017
|
-
return { handlerWrappers, upgradeWrappers
|
|
10049
|
+
return { handlerWrappers, upgradeWrappers };
|
|
10018
10050
|
}
|
|
10019
10051
|
const fullCtx = {
|
|
10020
10052
|
...ctx,
|
|
@@ -10041,7 +10073,7 @@ async function loadPlugins(declarations, ctx, rootDir, dist) {
|
|
|
10041
10073
|
}
|
|
10042
10074
|
if (enable === false) continue;
|
|
10043
10075
|
if (loaded.has(specifier)) {
|
|
10044
|
-
|
|
10076
|
+
failures.push({ specifier, reason: "duplicate plugin declaration" });
|
|
10045
10077
|
continue;
|
|
10046
10078
|
}
|
|
10047
10079
|
loaded.add(specifier);
|
|
@@ -10062,12 +10094,12 @@ async function loadPlugins(declarations, ctx, rootDir, dist) {
|
|
|
10062
10094
|
}
|
|
10063
10095
|
}
|
|
10064
10096
|
if (failures.length > 0) {
|
|
10065
|
-
|
|
10097
|
+
throw new Error(
|
|
10066
10098
|
`[faapi] ${failures.length} plugin(s) failed to load:
|
|
10067
10099
|
` + failures.map((f) => ` - ${f.specifier}: ${f.reason}`).join("\n")
|
|
10068
10100
|
);
|
|
10069
10101
|
}
|
|
10070
|
-
return { handlerWrappers, upgradeWrappers
|
|
10102
|
+
return { handlerWrappers, upgradeWrappers };
|
|
10071
10103
|
}
|
|
10072
10104
|
function resolveLocalPluginSource(specifier, baseDir) {
|
|
10073
10105
|
const base = path29.isAbsolute(specifier) ? specifier : path29.resolve(baseDir, specifier);
|
|
@@ -10401,9 +10433,13 @@ async function createAppBase(options) {
|
|
|
10401
10433
|
const taskQueue = createTaskQueue({
|
|
10402
10434
|
registry: registries.task,
|
|
10403
10435
|
rootDir,
|
|
10404
|
-
config,
|
|
10436
|
+
config: config ?? {},
|
|
10405
10437
|
resourcesDir,
|
|
10406
10438
|
registries: createTaskRegistriesView(registries),
|
|
10439
|
+
// 轻量补全通道:进程内路径经 store 惰性读取(插件注册后的活引用);
|
|
10440
|
+
// 隔离路径传 agent.llms 纯数据快照(worker 内重建)
|
|
10441
|
+
llm: registries.llm,
|
|
10442
|
+
llms: config?.agent?.llms,
|
|
10407
10443
|
driver: taskDriver,
|
|
10408
10444
|
onFailed: config?.task?.onFailed
|
|
10409
10445
|
});
|
|
@@ -10428,7 +10464,7 @@ async function createAppBase(options) {
|
|
|
10428
10464
|
dist,
|
|
10429
10465
|
cors: config?.cors ?? true,
|
|
10430
10466
|
onError: config?.lifecycle?.onError,
|
|
10431
|
-
config
|
|
10467
|
+
config,
|
|
10432
10468
|
wsRoutes,
|
|
10433
10469
|
middlewares: config?.middlewares,
|
|
10434
10470
|
injectors: config?.injectors,
|
|
@@ -10541,6 +10577,7 @@ async function createAppBase(options) {
|
|
|
10541
10577
|
registries.task.clear();
|
|
10542
10578
|
registries.agentHandle.clear();
|
|
10543
10579
|
registries.taskHandle.clear();
|
|
10580
|
+
registries.llm.clear();
|
|
10544
10581
|
setActiveResourcesDir(null);
|
|
10545
10582
|
if (!server.listening) {
|
|
10546
10583
|
app.server = null;
|