@faapi/faapi 5.0.1 → 5.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -1929,15 +1929,19 @@ var init_resolveTypeNode = __esm({
1929
1929
  *
1930
1930
  * 所有抛错点应优先使用此工厂——错误无行号时,几百行的类型文件只能靠
1931
1931
  * 类型名肉眼定位;解析 lib.d.ts 类型别名时还会出现错误文本与文件上下文错位
1932
+ *
1933
+ * `sourceFile` 可选:未触达 checker 的纯语法遍历路径(binder 未运行)没有
1934
+ * parent 指针,`node.getSourceFile()` 返回 undefined,此时调用方需显式传入
1935
+ * `program.getSourceFile(filePath)` 的结果以携带位置。
1932
1936
  */
1933
- static at(node, typeText, reason) {
1934
- const sourceFile = node.getSourceFile();
1935
- if (!sourceFile) {
1937
+ static at(node, typeText, reason, sourceFile) {
1938
+ const sf = sourceFile ?? node.getSourceFile();
1939
+ if (!sf) {
1936
1940
  return new _SchemaExtractionError(typeText, reason);
1937
1941
  }
1938
- const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
1942
+ const { line, character } = sf.getLineAndCharacterOfPosition(node.getStart(sf));
1939
1943
  return new _SchemaExtractionError(typeText, reason, void 0, {
1940
- file: sourceFile.fileName,
1944
+ file: sf.fileName,
1941
1945
  line: line + 1,
1942
1946
  column: character + 1
1943
1947
  });
@@ -3006,33 +3010,30 @@ function extractAgentMetadata(program, filePath, pathMeta) {
3006
3010
  const sourceFile = program.getSourceFile(filePath);
3007
3011
  if (!sourceFile) return null;
3008
3012
  const configFound = findConfigExport(sourceFile);
3009
- let jsDocOwner = null;
3010
- let objectLiteral = null;
3011
- if (configFound) {
3012
- jsDocOwner = configFound.jsDocOwner;
3013
- objectLiteral = configFound.objectLiteral;
3014
- } else if (pathMeta.hasRun) {
3015
- const runNode = findRunExport(sourceFile);
3016
- if (runNode) {
3017
- jsDocOwner = runNode;
3018
- }
3019
- }
3020
- const jsDoc = jsDocOwner ? getJSDocFromNode(jsDocOwner) : void 0;
3013
+ if (!configFound) {
3014
+ throw SchemaExtractionError.at(
3015
+ sourceFile,
3016
+ "config.systemPrompt",
3017
+ "agent \u5FC5\u586B\u2014\u2014\u8BF7\u5728 config \u5757\u58F0\u660E\u7CFB\u7EDF\u63D0\u793A\u8BCD\uFF08\u5B57\u7B26\u4E32\u5B57\u9762\u91CF\u3001\u65E0\u63D2\u503C\u6A21\u677F\u5B57\u7B26\u4E32\u6216\u5176 + \u62FC\u63A5\uFF09",
3018
+ sourceFile
3019
+ );
3020
+ }
3021
+ if (!configFound.objectLiteral) {
3022
+ throw SchemaExtractionError.at(
3023
+ configFound.jsDocOwner,
3024
+ "config",
3025
+ "\u4EC5\u652F\u6301\u5BF9\u8C61\u5B57\u9762\u91CF\u3001\u7BAD\u5934\u51FD\u6570\u8FD4\u56DE\u5BF9\u8C61\u5B57\u9762\u91CF\u6216\u51FD\u6570\u8FD4\u56DE\u5BF9\u8C61\u5B57\u9762\u91CF",
3026
+ sourceFile
3027
+ );
3028
+ }
3029
+ const objectLiteral = configFound.objectLiteral;
3030
+ const jsDoc = getJSDocFromNode(configFound.jsDocOwner);
3021
3031
  const description = extractDescription(jsDoc);
3022
3032
  const agentNameOverride = extractJSDocTagValue(jsDoc, "agent");
3023
- let systemPrompt;
3024
- let tools;
3025
- let agents;
3026
- let model;
3027
- let maxTurns;
3028
- if (objectLiteral) {
3029
- const fields = extractConfigFields(objectLiteral);
3030
- systemPrompt = fields.systemPrompt;
3031
- tools = fields.tools;
3032
- agents = fields.agents;
3033
- model = fields.model;
3034
- maxTurns = fields.maxTurns;
3035
- }
3033
+ const { systemPrompt, tools, agents, model, maxTurns } = extractConfigFields(
3034
+ objectLiteral,
3035
+ sourceFile
3036
+ );
3036
3037
  return {
3037
3038
  name: agentNameOverride ?? pathMeta.name,
3038
3039
  description,
@@ -3057,8 +3058,9 @@ function findConfigExport(sourceFile) {
3057
3058
  if (ts9.isObjectLiteralExpression(decl.initializer)) {
3058
3059
  result = { jsDocOwner: node, objectLiteral: decl.initializer };
3059
3060
  } else if (ts9.isArrowFunction(decl.initializer)) {
3060
- const returnObj = getReturnObjectLiteral(decl.initializer);
3061
- result = { jsDocOwner: node, objectLiteral: returnObj };
3061
+ result = { jsDocOwner: node, objectLiteral: getReturnObjectLiteral(decl.initializer) };
3062
+ } else {
3063
+ result = { jsDocOwner: node, objectLiteral: null };
3062
3064
  }
3063
3065
  }
3064
3066
  }
@@ -3069,27 +3071,6 @@ function findConfigExport(sourceFile) {
3069
3071
  });
3070
3072
  return result;
3071
3073
  }
3072
- function findRunExport(sourceFile) {
3073
- let result = null;
3074
- ts9.forEachChild(sourceFile, (node) => {
3075
- if (result) return;
3076
- if (ts9.isFunctionDeclaration(node) && hasExportModifier(node) && node.name?.text === "run") {
3077
- result = node;
3078
- return;
3079
- }
3080
- if (ts9.isVariableStatement(node) && hasExportModifier(node)) {
3081
- for (const decl of node.declarationList.declarations) {
3082
- if (result) break;
3083
- const nameText = ts9.isIdentifier(decl.name) ? decl.name.text : "";
3084
- if (nameText !== "run" || !decl.initializer) continue;
3085
- if (ts9.isArrowFunction(decl.initializer) || ts9.isFunctionExpression(decl.initializer)) {
3086
- result = node;
3087
- }
3088
- }
3089
- }
3090
- });
3091
- return result;
3092
- }
3093
3074
  function getReturnObjectLiteral(fn) {
3094
3075
  const body = fn.body;
3095
3076
  if (!body) return null;
@@ -3105,31 +3086,60 @@ function getReturnObjectLiteral(fn) {
3105
3086
  }
3106
3087
  return null;
3107
3088
  }
3108
- function extractConfigFields(objLit) {
3109
- const result = {};
3089
+ function extractConfigFields(objLit, sourceFile) {
3090
+ let systemPrompt;
3091
+ let tools;
3092
+ let agents;
3093
+ let model;
3094
+ let maxTurns;
3110
3095
  for (const prop of objLit.properties) {
3111
- if (!ts9.isPropertyAssignment(prop)) continue;
3096
+ if (ts9.isSpreadAssignment(prop)) continue;
3097
+ if (!ts9.isPropertyAssignment(prop)) {
3098
+ throw SchemaExtractionError.at(
3099
+ prop,
3100
+ "config",
3101
+ "\u4EC5\u652F\u6301 key: value \u5C5E\u6027\u5F62\u5F0F\uFF08\u4E0D\u652F\u6301 shorthand/\u65B9\u6CD5/getter\uFF09",
3102
+ sourceFile
3103
+ );
3104
+ }
3112
3105
  const propName = getPropertyName(prop.name);
3113
- if (!propName) continue;
3106
+ if (!propName) {
3107
+ throw SchemaExtractionError.at(prop.name, "config", "\u4E0D\u652F\u6301 computed \u5C5E\u6027\u540D", sourceFile);
3108
+ }
3114
3109
  switch (propName) {
3115
3110
  case "systemPrompt":
3116
- result.systemPrompt = extractStringValue(prop.initializer);
3111
+ systemPrompt = requireStringValue(prop, "systemPrompt", sourceFile);
3117
3112
  break;
3118
3113
  case "tools":
3119
- result.tools = extractStringArrayValue(prop.initializer);
3114
+ tools = requireStringArrayValue(prop, "tools", sourceFile);
3120
3115
  break;
3121
3116
  case "agents":
3122
- result.agents = extractStringArrayValue(prop.initializer);
3117
+ agents = requireStringArrayValue(prop, "agents", sourceFile);
3123
3118
  break;
3124
3119
  case "model":
3125
- result.model = extractStringValue(prop.initializer);
3120
+ model = requireStringValue(prop, "model", sourceFile);
3126
3121
  break;
3127
3122
  case "maxTurns":
3128
- result.maxTurns = extractNumberValue(prop.initializer);
3123
+ maxTurns = requireNumberValue(prop, "maxTurns", sourceFile);
3129
3124
  break;
3125
+ default:
3126
+ throw SchemaExtractionError.at(
3127
+ prop,
3128
+ `config.${propName}`,
3129
+ "\u672A\u77E5 config \u5B57\u6BB5\u2014\u2014\u4EC5\u652F\u6301 systemPrompt / tools / agents / model / maxTurns",
3130
+ sourceFile
3131
+ );
3130
3132
  }
3131
3133
  }
3132
- return result;
3134
+ if (systemPrompt === void 0) {
3135
+ throw SchemaExtractionError.at(
3136
+ objLit,
3137
+ "config.systemPrompt",
3138
+ "agent \u5FC5\u586B\u2014\u2014\u8BF7\u5728 config \u5757\u58F0\u660E\u7CFB\u7EDF\u63D0\u793A\u8BCD\uFF08\u5B57\u7B26\u4E32\u5B57\u9762\u91CF\u3001\u65E0\u63D2\u503C\u6A21\u677F\u5B57\u7B26\u4E32\u6216\u5176 + \u62FC\u63A5\uFF09",
3139
+ sourceFile
3140
+ );
3141
+ }
3142
+ return { systemPrompt, tools, agents, model, maxTurns };
3133
3143
  }
3134
3144
  function getPropertyName(name) {
3135
3145
  if (ts9.isIdentifier(name)) return name.text;
@@ -3137,9 +3147,52 @@ function getPropertyName(name) {
3137
3147
  return null;
3138
3148
  }
3139
3149
  function extractStringValue(expr) {
3140
- if (ts9.isStringLiteral(expr)) return expr.text;
3150
+ if (isStringLikeLiteral(expr)) return expr.text;
3151
+ if (ts9.isBinaryExpression(expr) && expr.operatorToken.kind === ts9.SyntaxKind.PlusToken) {
3152
+ const left = extractStringValue(expr.left);
3153
+ if (left === void 0) return void 0;
3154
+ const right = extractStringValue(expr.right);
3155
+ if (right === void 0) return void 0;
3156
+ return left + right;
3157
+ }
3141
3158
  return void 0;
3142
3159
  }
3160
+ function requireStringValue(prop, fieldName, sourceFile) {
3161
+ const value = extractStringValue(prop.initializer);
3162
+ if (value === void 0) {
3163
+ throw SchemaExtractionError.at(
3164
+ prop.initializer,
3165
+ `config.${fieldName}`,
3166
+ "\u4EC5\u652F\u6301\u5B57\u7B26\u4E32\u5B57\u9762\u91CF\u3001\u65E0\u63D2\u503C\u6A21\u677F\u5B57\u7B26\u4E32\u6216\u5176 + \u62FC\u63A5\uFF08\u542B\u63D2\u503C\u7684\u6A21\u677F\u5B57\u7B26\u4E32/\u53D8\u91CF\u5F15\u7528/\u6570\u5B57\u65E0\u6CD5\u9759\u6001\u6C42\u503C\u4E3A\u5B57\u7B26\u4E32\uFF09",
3167
+ sourceFile
3168
+ );
3169
+ }
3170
+ return value;
3171
+ }
3172
+ function requireStringArrayValue(prop, fieldName, sourceFile) {
3173
+ const value = extractStringArrayValue(prop.initializer);
3174
+ if (value === void 0) {
3175
+ throw SchemaExtractionError.at(
3176
+ prop.initializer,
3177
+ `config.${fieldName}`,
3178
+ "\u4EC5\u652F\u6301\u5168\u5B57\u7B26\u4E32\u5B57\u9762\u91CF\u6570\u7EC4\uFF08\u5143\u7D20\u4E3A\u5B57\u7B26\u4E32\u5B57\u9762\u91CF\u3001\u65E0\u63D2\u503C\u6A21\u677F\u5B57\u7B26\u4E32\u6216\u5176 + \u62FC\u63A5\uFF09",
3179
+ sourceFile
3180
+ );
3181
+ }
3182
+ return value;
3183
+ }
3184
+ function requireNumberValue(prop, fieldName, sourceFile) {
3185
+ const value = extractNumberValue(prop.initializer);
3186
+ if (value === void 0) {
3187
+ throw SchemaExtractionError.at(
3188
+ prop.initializer,
3189
+ `config.${fieldName}`,
3190
+ "\u4EC5\u652F\u6301\u6570\u5B57\u5B57\u9762\u91CF",
3191
+ sourceFile
3192
+ );
3193
+ }
3194
+ return value;
3195
+ }
3143
3196
  function extractNumberValue(expr) {
3144
3197
  if (ts9.isNumericLiteral(expr)) {
3145
3198
  const num = Number(expr.text);
@@ -3147,12 +3200,16 @@ function extractNumberValue(expr) {
3147
3200
  }
3148
3201
  return void 0;
3149
3202
  }
3203
+ function isStringLikeLiteral(node) {
3204
+ return ts9.isStringLiteral(node) || ts9.isNoSubstitutionTemplateLiteral(node);
3205
+ }
3150
3206
  function extractStringArrayValue(expr) {
3151
3207
  if (!ts9.isArrayLiteralExpression(expr)) return void 0;
3152
3208
  const values = [];
3153
3209
  for (const element of expr.elements) {
3154
- if (!ts9.isStringLiteral(element)) return void 0;
3155
- values.push(element.text);
3210
+ const value = extractStringValue(element);
3211
+ if (value === void 0) return void 0;
3212
+ values.push(value);
3156
3213
  }
3157
3214
  return values;
3158
3215
  }
@@ -3160,6 +3217,7 @@ var init_extractAgentMetadata = __esm({
3160
3217
  "src/ast/extractAgentMetadata.ts"() {
3161
3218
  "use strict";
3162
3219
  init_jsDocMetadata();
3220
+ init_resolveTypeNode();
3163
3221
  }
3164
3222
  });
3165
3223
 
@@ -3197,6 +3255,28 @@ function hydrateAgents(manifest) {
3197
3255
  maxTurns: a.maxTurns ?? void 0
3198
3256
  }));
3199
3257
  }
3258
+ function validateAgentList(metadata) {
3259
+ const names = /* @__PURE__ */ new Map();
3260
+ for (const a of metadata) {
3261
+ const prev = names.get(a.name);
3262
+ if (prev) {
3263
+ throw new Error(
3264
+ `agent \u540D\u91CD\u590D: "${a.name}"\uFF08${prev} \u4E0E ${a.filePath} \u51B2\u7A81\u2014\u2014\u76EE\u5F55\u63A8\u5BFC\u540D\u6216 @agent \u8986\u76D6\u540D\u649E\u540D\uFF09`
3265
+ );
3266
+ }
3267
+ names.set(a.name, a.filePath);
3268
+ }
3269
+ for (const a of metadata) {
3270
+ for (const ref of a.agents ?? []) {
3271
+ if (!names.has(ref)) {
3272
+ const available = [...names.keys()].join(", ") || "\u65E0";
3273
+ throw new Error(
3274
+ `agent "${a.name}" \u7684 agents \u5F15\u7528\u4E86\u4E0D\u5B58\u5728\u7684 agent: "${ref}"\uFF08\u6E05\u5355\u4E2D\u53EF\u7528: ${available}\uFF09`
3275
+ );
3276
+ }
3277
+ }
3278
+ }
3279
+ }
3200
3280
  async function generateAgentArtifacts(agents, rootDir, dist) {
3201
3281
  const metadata = [];
3202
3282
  const programByFile = createPrograms(agents.map((m) => path15.resolve(rootDir, m.filePath)));
@@ -3208,10 +3288,14 @@ async function generateAgentArtifacts(agents, rootDir, dist) {
3208
3288
  filePath: manifest.filePath,
3209
3289
  hasRun: manifest.hasRun
3210
3290
  });
3211
- if (result) {
3212
- metadata.push(result);
3291
+ if (!result) {
3292
+ throw new Error(
3293
+ `agent "${manifest.name}" \u6E90\u6587\u4EF6\u4E0D\u5728 Program \u4E2D: ${manifest.filePath}\u2014\u2014\u65E0\u6CD5\u751F\u6210\u6E05\u5355`
3294
+ );
3213
3295
  }
3296
+ metadata.push(result);
3214
3297
  }
3298
+ validateAgentList(metadata);
3215
3299
  const serialized = serializeAgents(metadata, dist);
3216
3300
  const agentsPath = path15.resolve(rootDir, dist, AGENTS_FILE);
3217
3301
  await writeAgentsModule(serialized, agentsPath);