@faapi/faapi 5.0.1 → 5.1.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\u6216\u65E0\u63D2\u503C\u6A21\u677F\u5B57\u7B26\u4E32\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\u6216\u65E0\u63D2\u503C\u6A21\u677F\u5B57\u7B26\u4E32\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,45 @@ 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;
3141
3151
  return void 0;
3142
3152
  }
3153
+ function requireStringValue(prop, fieldName, sourceFile) {
3154
+ const value = extractStringValue(prop.initializer);
3155
+ if (value === void 0) {
3156
+ throw SchemaExtractionError.at(
3157
+ prop.initializer,
3158
+ `config.${fieldName}`,
3159
+ "\u4EC5\u652F\u6301\u5B57\u7B26\u4E32\u5B57\u9762\u91CF\u6216\u65E0\u63D2\u503C\u6A21\u677F\u5B57\u7B26\u4E32\uFF08\u542B\u63D2\u503C\u7684\u6A21\u677F\u5B57\u7B26\u4E32/\u53D8\u91CF\u5F15\u7528\u65E0\u6CD5\u9759\u6001\u6C42\u503C\uFF09",
3160
+ sourceFile
3161
+ );
3162
+ }
3163
+ return value;
3164
+ }
3165
+ function requireStringArrayValue(prop, fieldName, sourceFile) {
3166
+ const value = extractStringArrayValue(prop.initializer);
3167
+ if (value === void 0) {
3168
+ throw SchemaExtractionError.at(
3169
+ prop.initializer,
3170
+ `config.${fieldName}`,
3171
+ "\u4EC5\u652F\u6301\u5168\u5B57\u7B26\u4E32\u5B57\u9762\u91CF\u6570\u7EC4\uFF08\u5143\u7D20\u4E3A\u5B57\u7B26\u4E32\u5B57\u9762\u91CF\u6216\u65E0\u63D2\u503C\u6A21\u677F\u5B57\u7B26\u4E32\uFF09",
3172
+ sourceFile
3173
+ );
3174
+ }
3175
+ return value;
3176
+ }
3177
+ function requireNumberValue(prop, fieldName, sourceFile) {
3178
+ const value = extractNumberValue(prop.initializer);
3179
+ if (value === void 0) {
3180
+ throw SchemaExtractionError.at(
3181
+ prop.initializer,
3182
+ `config.${fieldName}`,
3183
+ "\u4EC5\u652F\u6301\u6570\u5B57\u5B57\u9762\u91CF",
3184
+ sourceFile
3185
+ );
3186
+ }
3187
+ return value;
3188
+ }
3143
3189
  function extractNumberValue(expr) {
3144
3190
  if (ts9.isNumericLiteral(expr)) {
3145
3191
  const num = Number(expr.text);
@@ -3147,11 +3193,14 @@ function extractNumberValue(expr) {
3147
3193
  }
3148
3194
  return void 0;
3149
3195
  }
3196
+ function isStringLikeLiteral(node) {
3197
+ return ts9.isStringLiteral(node) || ts9.isNoSubstitutionTemplateLiteral(node);
3198
+ }
3150
3199
  function extractStringArrayValue(expr) {
3151
3200
  if (!ts9.isArrayLiteralExpression(expr)) return void 0;
3152
3201
  const values = [];
3153
3202
  for (const element of expr.elements) {
3154
- if (!ts9.isStringLiteral(element)) return void 0;
3203
+ if (!isStringLikeLiteral(element)) return void 0;
3155
3204
  values.push(element.text);
3156
3205
  }
3157
3206
  return values;
@@ -3160,6 +3209,7 @@ var init_extractAgentMetadata = __esm({
3160
3209
  "src/ast/extractAgentMetadata.ts"() {
3161
3210
  "use strict";
3162
3211
  init_jsDocMetadata();
3212
+ init_resolveTypeNode();
3163
3213
  }
3164
3214
  });
3165
3215
 
@@ -3197,6 +3247,28 @@ function hydrateAgents(manifest) {
3197
3247
  maxTurns: a.maxTurns ?? void 0
3198
3248
  }));
3199
3249
  }
3250
+ function validateAgentList(metadata) {
3251
+ const names = /* @__PURE__ */ new Map();
3252
+ for (const a of metadata) {
3253
+ const prev = names.get(a.name);
3254
+ if (prev) {
3255
+ throw new Error(
3256
+ `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`
3257
+ );
3258
+ }
3259
+ names.set(a.name, a.filePath);
3260
+ }
3261
+ for (const a of metadata) {
3262
+ for (const ref of a.agents ?? []) {
3263
+ if (!names.has(ref)) {
3264
+ const available = [...names.keys()].join(", ") || "\u65E0";
3265
+ throw new Error(
3266
+ `agent "${a.name}" \u7684 agents \u5F15\u7528\u4E86\u4E0D\u5B58\u5728\u7684 agent: "${ref}"\uFF08\u6E05\u5355\u4E2D\u53EF\u7528: ${available}\uFF09`
3267
+ );
3268
+ }
3269
+ }
3270
+ }
3271
+ }
3200
3272
  async function generateAgentArtifacts(agents, rootDir, dist) {
3201
3273
  const metadata = [];
3202
3274
  const programByFile = createPrograms(agents.map((m) => path15.resolve(rootDir, m.filePath)));
@@ -3208,10 +3280,14 @@ async function generateAgentArtifacts(agents, rootDir, dist) {
3208
3280
  filePath: manifest.filePath,
3209
3281
  hasRun: manifest.hasRun
3210
3282
  });
3211
- if (result) {
3212
- metadata.push(result);
3283
+ if (!result) {
3284
+ throw new Error(
3285
+ `agent "${manifest.name}" \u6E90\u6587\u4EF6\u4E0D\u5728 Program \u4E2D: ${manifest.filePath}\u2014\u2014\u65E0\u6CD5\u751F\u6210\u6E05\u5355`
3286
+ );
3213
3287
  }
3288
+ metadata.push(result);
3214
3289
  }
3290
+ validateAgentList(metadata);
3215
3291
  const serialized = serializeAgents(metadata, dist);
3216
3292
  const agentsPath = path15.resolve(rootDir, dist, AGENTS_FILE);
3217
3293
  await writeAgentsModule(serialized, agentsPath);