@fern-api/generator-cli 0.9.34 → 0.9.36

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.
Files changed (3) hide show
  1. package/dist/api.js +1 -1
  2. package/dist/cli.js +84 -17
  3. package/package.json +1 -1
package/dist/api.js CHANGED
@@ -8179,7 +8179,7 @@ function expandFernignorePatterns(fernignoreContent, candidatePaths) {
8179
8179
  }
8180
8180
  function matches(candidate, pattern) {
8181
8181
  const normalized = pattern.endsWith("/") ? pattern.slice(0, -1) : pattern;
8182
- if (minimatch(candidate, normalized, { dot: true })) {
8182
+ if (minimatch(candidate, normalized, { dot: true, nonegate: true })) {
8183
8183
  return true;
8184
8184
  }
8185
8185
  return candidate === normalized || candidate.startsWith(normalized + "/");
package/dist/cli.js CHANGED
@@ -3822,9 +3822,9 @@ var init_esm3 = __esm({
3822
3822
  }
3823
3823
  });
3824
3824
 
3825
- // ../../node_modules/.pnpm/tmp@0.2.5/node_modules/tmp/lib/tmp.js
3825
+ // ../../node_modules/.pnpm/tmp@0.2.6/node_modules/tmp/lib/tmp.js
3826
3826
  var require_tmp = __commonJS({
3827
- "../../node_modules/.pnpm/tmp@0.2.5/node_modules/tmp/lib/tmp.js"(exports2, module2) {
3827
+ "../../node_modules/.pnpm/tmp@0.2.6/node_modules/tmp/lib/tmp.js"(exports2, module2) {
3828
3828
  "use strict";
3829
3829
  var fs3 = require("fs");
3830
3830
  var os = require("os");
@@ -4075,13 +4075,20 @@ var require_tmp = __commonJS({
4075
4075
  ].join("");
4076
4076
  return path5.join(tmpDir, opts.dir, name);
4077
4077
  }
4078
+ function _assertPath(path6) {
4079
+ if (path6.includes("..")) {
4080
+ throw new Error("Relative value not allowed");
4081
+ }
4082
+ return path6;
4083
+ }
4078
4084
  function _assertOptionsBase(options) {
4079
4085
  if (!_isUndefined(options.name)) {
4080
4086
  const name = options.name;
4081
4087
  if (path5.isAbsolute(name)) throw new Error(`name option must not contain an absolute path, found "${name}".`);
4082
4088
  const basename2 = path5.basename(name);
4083
- if (basename2 === ".." || basename2 === "." || basename2 !== name)
4089
+ if (basename2 === ".." || basename2 === "." || basename2 !== name) {
4084
4090
  throw new Error(`name option must not contain a path, found "${name}".`);
4091
+ }
4085
4092
  }
4086
4093
  if (!_isUndefined(options.template) && !options.template.match(TEMPLATE_PATTERN)) {
4087
4094
  throw new Error(`Invalid template, found "${options.template}".`);
@@ -4094,15 +4101,16 @@ var require_tmp = __commonJS({
4094
4101
  options.detachDescriptor = !!options.detachDescriptor;
4095
4102
  options.discardDescriptor = !!options.discardDescriptor;
4096
4103
  options.unsafeCleanup = !!options.unsafeCleanup;
4097
- options.prefix = _isUndefined(options.prefix) ? "" : options.prefix;
4098
- options.postfix = _isUndefined(options.postfix) ? "" : options.postfix;
4104
+ options.prefix = _isUndefined(options.prefix) ? "" : _assertPath(options.prefix);
4105
+ options.postfix = _isUndefined(options.postfix) ? "" : _assertPath(options.postfix);
4106
+ options.template = _isUndefined(options.template) ? void 0 : _assertPath(options.template);
4099
4107
  }
4100
4108
  function _getRelativePath(option, name, tmpDir, cb) {
4101
4109
  if (_isUndefined(name)) return cb(null);
4102
4110
  _resolvePath(name, tmpDir, function(err, resolvedPath) {
4103
4111
  if (err) return cb(err);
4104
4112
  const relativePath = path5.relative(tmpDir, resolvedPath);
4105
- if (!resolvedPath.startsWith(tmpDir)) {
4113
+ if (relativePath.startsWith("..") || path5.isAbsolute(relativePath)) {
4106
4114
  return cb(new Error(`${option} option must be relative to "${tmpDir}", found "${relativePath}".`));
4107
4115
  }
4108
4116
  cb(null, relativePath);
@@ -4112,7 +4120,7 @@ var require_tmp = __commonJS({
4112
4120
  if (_isUndefined(name)) return;
4113
4121
  const resolvedPath = _resolvePathSync(name, tmpDir);
4114
4122
  const relativePath = path5.relative(tmpDir, resolvedPath);
4115
- if (!resolvedPath.startsWith(tmpDir)) {
4123
+ if (relativePath.startsWith("..") || path5.isAbsolute(relativePath)) {
4116
4124
  throw new Error(`${option} option must be relative to "${tmpDir}", found "${relativePath}".`);
4117
4125
  }
4118
4126
  return relativePath;
@@ -32026,7 +32034,7 @@ function expandFernignorePatterns(fernignoreContent, candidatePaths) {
32026
32034
  }
32027
32035
  function matches(candidate, pattern) {
32028
32036
  const normalized = pattern.endsWith("/") ? pattern.slice(0, -1) : pattern;
32029
- if (minimatch(candidate, normalized, { dot: true })) {
32037
+ if (minimatch(candidate, normalized, { dot: true, nonegate: true })) {
32030
32038
  return true;
32031
32039
  }
32032
32040
  return candidate === normalized || candidate.startsWith(normalized + "/");
@@ -41278,17 +41286,21 @@ var AutoVersionStep = class extends BaseStep {
41278
41286
  analysis: { versionBump: "PATCH", message: this.brandMessage("SDK regeneration") }
41279
41287
  });
41280
41288
  }
41281
- const chunks = service.chunkDiff(cleanedDiff, MAX_AI_DIFF_BYTES);
41282
- const cappedChunks = chunks.slice(0, MAX_CHUNKS);
41283
- const skippedChunks = chunks.length - cappedChunks.length;
41284
- if (chunks.length > 1) {
41285
- this.logger.info(
41286
- `AutoVersionStep: split diff into ${chunks.length} chunks` + (skippedChunks > 0 ? ` (capped at ${MAX_CHUNKS}, skipping ${skippedChunks})` : "")
41287
- );
41288
- }
41289
41289
  let analysis;
41290
41290
  try {
41291
- analysis = cappedChunks.length <= 1 ? await this.analyzeSingle(cleanedDiff, language, previousVersion) : await this.analyzeChunks(cappedChunks, language, previousVersion);
41291
+ if (this.config.ai == null && this.config.fernToken != null) {
41292
+ analysis = await this.analyzeViaFaiService(cleanedDiff, language, previousVersion);
41293
+ } else {
41294
+ const chunks = service.chunkDiff(cleanedDiff, MAX_AI_DIFF_BYTES);
41295
+ const cappedChunks = chunks.slice(0, MAX_CHUNKS);
41296
+ const skippedChunks = chunks.length - cappedChunks.length;
41297
+ if (chunks.length > 1) {
41298
+ this.logger.info(
41299
+ `AutoVersionStep: split diff into ${chunks.length} chunks` + (skippedChunks > 0 ? ` (capped at ${MAX_CHUNKS}, skipping ${skippedChunks})` : "")
41300
+ );
41301
+ }
41302
+ analysis = cappedChunks.length <= 1 ? await this.analyzeSingle(cleanedDiff, language, previousVersion) : await this.analyzeChunks(cappedChunks, language, previousVersion);
41303
+ }
41292
41304
  } catch (error) {
41293
41305
  this.logger.warn(`AutoVersionStep: FAI analysis failed (${String(error)}); falling back to PATCH bump.`);
41294
41306
  analysis = { versionBump: "PATCH", message: this.brandMessage("SDK regeneration") };
@@ -41568,6 +41580,47 @@ ${newBlock}${remainder}`;
41568
41580
  };
41569
41581
  }
41570
41582
  }
41583
+ /**
41584
+ * Calls the hosted FAI service (`/sdks/analyze-commit-diff`) with the fern token.
41585
+ * Used when no BAML `ai` config is supplied (remote generation via fiddle). FAI
41586
+ * handles chunking, parallelism, and retries server-side. Returns null on
41587
+ * NO_CHANGE; throws on transport/HTTP errors so the caller's PATCH fallback applies.
41588
+ */
41589
+ async analyzeViaFaiService(cleanedDiff, language, previousVersion) {
41590
+ const baseUrl = this.config.faiBaseUrl ?? "https://fai.buildwithfern.com";
41591
+ const response = await fetch(`${baseUrl}/sdks/analyze-commit-diff`, {
41592
+ method: "POST",
41593
+ headers: {
41594
+ Authorization: `Bearer ${this.config.fernToken}`,
41595
+ "Content-Type": "application/json"
41596
+ },
41597
+ body: JSON.stringify({
41598
+ diff: cleanedDiff,
41599
+ language,
41600
+ previous_version: previousVersion,
41601
+ prior_changelog: this.config.priorChangelog ?? void 0,
41602
+ spec_commit_message: this.config.specCommitMessage ?? void 0
41603
+ })
41604
+ });
41605
+ if (!response.ok) {
41606
+ const body = await response.text().catch(() => "");
41607
+ throw new Error(`FAI analyze-commit-diff failed with status ${response.status}: ${body.slice(0, 500)}`);
41608
+ }
41609
+ const parsed = await response.json();
41610
+ if (!isFaiAnalyzeResponse(parsed)) {
41611
+ throw new Error("FAI analyze-commit-diff returned an unexpected response shape");
41612
+ }
41613
+ if (parsed.version_bump === "NO_CHANGE") {
41614
+ return null;
41615
+ }
41616
+ return {
41617
+ versionBump: parsed.version_bump,
41618
+ message: this.brandMessage(parsed.message),
41619
+ changelogEntry: nonEmpty(parsed.changelog_entry),
41620
+ prDescription: nonEmpty(parsed.pr_description),
41621
+ versionBumpReason: nonEmpty(parsed.version_bump_reason)
41622
+ };
41623
+ }
41571
41624
  /**
41572
41625
  * Dynamically imports @fern-api/cli-ai only when autoversion actually needs to
41573
41626
  * call FAI. The package is ESM-only with extensionless internal imports, so
@@ -41609,6 +41662,20 @@ ${newBlock}${remainder}`;
41609
41662
  return adapter;
41610
41663
  }
41611
41664
  };
41665
+ var FAI_VERSION_BUMPS = ["MAJOR", "MINOR", "PATCH", "NO_CHANGE"];
41666
+ function isFaiAnalyzeResponse(value) {
41667
+ if (typeof value !== "object" || value == null) {
41668
+ return false;
41669
+ }
41670
+ const candidate = value;
41671
+ return typeof candidate.message === "string" && typeof candidate.version_bump === "string" && FAI_VERSION_BUMPS.includes(candidate.version_bump) && isStringOrAbsent(candidate.changelog_entry) && isStringOrAbsent(candidate.pr_description) && isStringOrAbsent(candidate.version_bump_reason);
41672
+ }
41673
+ function isStringOrAbsent(value) {
41674
+ return value == null || typeof value === "string";
41675
+ }
41676
+ function nonEmpty(value) {
41677
+ return value != null && value.trim().length > 0 ? value : void 0;
41678
+ }
41612
41679
 
41613
41680
  // src/pipeline/steps/GenerationCommitStep.ts
41614
41681
  var import_child_process5 = require("child_process");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fern-api/generator-cli",
3
- "version": "0.9.34",
3
+ "version": "0.9.36",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/fern-api/fern.git",