@fern-api/fern-api-dev 3.64.5 → 3.64.6

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 (2) hide show
  1. package/cli.cjs +504 -104
  2. package/package.json +1 -1
package/cli.cjs CHANGED
@@ -1427240,7 +1427240,7 @@ var AccessTokenPosthogManager = class {
1427240
1427240
  properties: {
1427241
1427241
  ...event,
1427242
1427242
  ...event.properties,
1427243
- version: "3.64.5",
1427243
+ version: "3.64.6",
1427244
1427244
  usingAccessToken: true
1427245
1427245
  }
1427246
1427246
  });
@@ -1427339,7 +1427339,7 @@ var UserPosthogManager = class {
1427339
1427339
  distinctId: this.userId ?? await this.getPersistedDistinctId(),
1427340
1427340
  event: "CLI",
1427341
1427341
  properties: {
1427342
- version: "3.64.5",
1427342
+ version: "3.64.6",
1427343
1427343
  ...event,
1427344
1427344
  ...event.properties,
1427345
1427345
  usingAccessToken: false,
@@ -1432669,7 +1432669,7 @@ var TaskContextLogger = class {
1432669
1432669
  debug(...args) {
1432670
1432670
  const message = args.join(" ");
1432671
1432671
  this.context.logFileWriter.write({ taskName: this.task.name, level: LogLevel2.Debug, message });
1432672
- if (this.enabled && this.context.logLevel === LogLevel2.Debug) {
1432672
+ if (this.shouldLogToTask(LogLevel2.Debug)) {
1432673
1432673
  if (this.task.logs == null) {
1432674
1432674
  this.task.logs = [];
1432675
1432675
  }
@@ -1432679,7 +1432679,7 @@ var TaskContextLogger = class {
1432679
1432679
  info(...args) {
1432680
1432680
  const message = args.join(" ");
1432681
1432681
  this.context.logFileWriter.write({ taskName: this.task.name, level: LogLevel2.Info, message });
1432682
- if (this.enabled && this.context.logLevel === LogLevel2.Debug) {
1432682
+ if (this.shouldLogToTask(LogLevel2.Info)) {
1432683
1432683
  if (this.task.logs == null) {
1432684
1432684
  this.task.logs = [];
1432685
1432685
  }
@@ -1432689,7 +1432689,7 @@ var TaskContextLogger = class {
1432689
1432689
  warn(...args) {
1432690
1432690
  const message = args.join(" ");
1432691
1432691
  this.context.logFileWriter.write({ taskName: this.task.name, level: LogLevel2.Warn, message });
1432692
- if (this.enabled) {
1432692
+ if (this.shouldLogToTask(LogLevel2.Warn)) {
1432693
1432693
  if (this.task.logs == null) {
1432694
1432694
  this.task.logs = [];
1432695
1432695
  }
@@ -1432699,7 +1432699,7 @@ var TaskContextLogger = class {
1432699
1432699
  error(...args) {
1432700
1432700
  const message = args.join(" ");
1432701
1432701
  this.context.logFileWriter.write({ taskName: this.task.name, level: LogLevel2.Error, message });
1432702
- if (this.enabled) {
1432702
+ if (this.shouldLogToTask(LogLevel2.Error)) {
1432703
1432703
  this.collectedErrors.push(message);
1432704
1432704
  if (this.task.logs == null) {
1432705
1432705
  this.task.logs = [];
@@ -1432708,14 +1432708,27 @@ var TaskContextLogger = class {
1432708
1432708
  }
1432709
1432709
  }
1432710
1432710
  log(level, ...args) {
1432711
- if (level === LogLevel2.Debug) {
1432712
- this.debug(...args);
1432713
- } else if (level === LogLevel2.Warn) {
1432714
- this.warn(...args);
1432715
- } else if (level === LogLevel2.Error) {
1432716
- this.error(...args);
1432711
+ switch (level) {
1432712
+ case LogLevel2.Debug:
1432713
+ this.debug(...args);
1432714
+ break;
1432715
+ case LogLevel2.Info:
1432716
+ this.info(...args);
1432717
+ break;
1432718
+ case LogLevel2.Warn:
1432719
+ this.warn(...args);
1432720
+ break;
1432721
+ case LogLevel2.Error:
1432722
+ this.error(...args);
1432723
+ break;
1432717
1432724
  }
1432718
1432725
  }
1432726
+ /**
1432727
+ * Check if a message at the given level should be logged to the task's UI.
1432728
+ */
1432729
+ shouldLogToTask(level) {
1432730
+ return this.enabled && LOG_LEVELS.indexOf(level) >= LOG_LEVELS.indexOf(this.logLevel);
1432731
+ }
1432719
1432732
  };
1432720
1432733
 
1432721
1432734
  // ../cli-v2/lib/context/adapter/TaskContextAdapter.js
@@ -1432724,7 +1432737,7 @@ var TaskContextAdapter = class {
1432724
1432737
  logger;
1432725
1432738
  constructor({ context: context2, task, logLevel = LogLevel2.Warn }) {
1432726
1432739
  if (task != null) {
1432727
- this.logger = new TaskContextLogger({ context: context2, task });
1432740
+ this.logger = new TaskContextLogger({ context: context2, task, logLevel });
1432728
1432741
  } else {
1432729
1432742
  this.logger = createLogger2((level, ...args) => {
1432730
1432743
  if (LOG_LEVELS.indexOf(level) >= LOG_LEVELS.indexOf(logLevel)) {
@@ -1510152,8 +1510165,16 @@ function getGeneratorNameOrThrow(generatorName, context2) {
1510152
1510165
  }
1510153
1510166
  return normalizedGeneratorName;
1510154
1510167
  }
1510168
+ var GENERATOR_NAME_ALIASES = {
1510169
+ "fernapi/java-model": GeneratorName.JAVA_MODEL,
1510170
+ "fernapi/fern-typescript-node-sdk": GeneratorName.TYPESCRIPT_SDK
1510171
+ };
1510155
1510172
  function normalizeGeneratorName(generatorName) {
1510156
1510173
  generatorName = addDefaultDockerOrgIfNotPresent(generatorName);
1510174
+ const aliased = GENERATOR_NAME_ALIASES[generatorName];
1510175
+ if (aliased != null) {
1510176
+ generatorName = aliased;
1510177
+ }
1510157
1510178
  if (isGeneratorName(generatorName)) {
1510158
1510179
  return generatorName;
1510159
1510180
  }
@@ -1563531,7 +1563552,7 @@ var ApiDefinitionValidator = class {
1563531
1563552
  cliVersion;
1563532
1563553
  constructor(config3) {
1563533
1563554
  this.context = config3.context;
1563534
- this.taskContext = new TaskContextAdapter({ context: this.context });
1563555
+ this.taskContext = new TaskContextAdapter({ context: this.context, task: config3.task });
1563535
1563556
  this.cliVersion = config3.cliVersion;
1563536
1563557
  }
1563537
1563558
  /**
@@ -1563643,10 +1563664,12 @@ var ApiChecker = class {
1563643
1563664
  context;
1563644
1563665
  cliVersion;
1563645
1563666
  stream;
1563667
+ task;
1563646
1563668
  constructor(config3) {
1563647
1563669
  this.context = config3.context;
1563648
1563670
  this.cliVersion = config3.cliVersion;
1563649
1563671
  this.stream = config3.stream ?? process.stderr;
1563672
+ this.task = config3.task;
1563650
1563673
  }
1563651
1563674
  /**
1563652
1563675
  * Check APIs in the workspace and display results.
@@ -1563670,7 +1563693,8 @@ var ApiChecker = class {
1563670
1563693
  }
1563671
1563694
  const validator = new ApiDefinitionValidator({
1563672
1563695
  context: this.context,
1563673
- cliVersion: this.cliVersion
1563696
+ cliVersion: this.cliVersion,
1563697
+ task: this.task
1563674
1563698
  });
1563675
1563699
  const allViolations = [];
1563676
1563700
  for (const apiName of apisToCheck) {
@@ -1563712,7 +1563736,7 @@ var ApiChecker = class {
1563712
1563736
  }
1563713
1563737
  writeHeader() {
1563714
1563738
  this.stream.write("\n");
1563715
- this.stream.write(`${Icons.info} ${source_default.bold("Validating API definitions")}
1563739
+ this.stream.write(`${Icons.info} ${source_default.bold(`Validate APIs`)}
1563716
1563740
  `);
1563717
1563741
  this.stream.write("\n");
1563718
1563742
  }
@@ -1563887,6 +1563911,9 @@ var ApiChecker = class {
1563887
1563911
  }
1563888
1563912
  return `${(ms6 / 1e3).toFixed(1)}s`;
1563889
1563913
  }
1563914
+ maybePluralApis(apis) {
1563915
+ return apis.length === 1 ? "API" : "APIs";
1563916
+ }
1563890
1563917
  };
1563891
1563918
 
1563892
1563919
  // ../cli-v2/lib/commands/check/command.js
@@ -1682131,7 +1682158,12 @@ var LegacyRemoteGenerationRunner = class {
1682131
1682158
  this.invocationAdapter = new LegacyGeneratorInvocationAdapter({ context: this.context });
1682132
1682159
  }
1682133
1682160
  async run(args) {
1682134
- const taskContext = new TaskContextAdapter({ task: args.task, context: this.context });
1682161
+ const taskContext = new TaskContextAdapter({
1682162
+ context: this.context,
1682163
+ task: args.task,
1682164
+ logLevel: LogLevel2.Info
1682165
+ // Capture INFO logs to parse git output URLs
1682166
+ });
1682135
1682167
  try {
1682136
1682168
  const generatorInvocation = await this.invocationAdapter.adapt(args.target);
1682137
1682169
  const generatorGroup = {
@@ -1682167,9 +1682199,10 @@ var LegacyRemoteGenerationRunner = class {
1682167
1682199
  success: false
1682168
1682200
  };
1682169
1682201
  }
1682202
+ const gitOutput = this.extractGitOutputFromTaskLogs(args.task);
1682170
1682203
  return {
1682171
1682204
  success: true,
1682172
- output: absolutePathToPreview != null ? [absolutePathToPreview.toString()] : this.context.resolveTargetOutputs(args.target)
1682205
+ output: this.resolveOutput(args, gitOutput)
1682173
1682206
  };
1682174
1682207
  } catch (error49) {
1682175
1682208
  return {
@@ -1682210,6 +1682243,58 @@ var LegacyRemoteGenerationRunner = class {
1682210
1682243
  }
1682211
1682244
  return void 0;
1682212
1682245
  }
1682246
+ /**
1682247
+ * Extract git output URLs from task logs.
1682248
+ *
1682249
+ * The remote generation service logs messages like:
1682250
+ * - "Created pull request: https://github.com/owner/repo/pull/123"
1682251
+ * - "Created commit abc123"
1682252
+ * - "Pushed branch: https://github.com/owner/repo/tree/branch-name"
1682253
+ * - "Release tagged. View here: https://github.com/owner/repo/releases/tag/v1.0.0"
1682254
+ */
1682255
+ extractGitOutputFromTaskLogs(task) {
1682256
+ const gitOutput = {};
1682257
+ for (const log2 of task.logs ?? []) {
1682258
+ const prMatch = log2.message.match(/Created pull request: (.+)/);
1682259
+ if (prMatch != null) {
1682260
+ gitOutput.pullRequestUrl = prMatch[1];
1682261
+ }
1682262
+ const commitMatch = log2.message.match(/Created commit (\w+)/);
1682263
+ if (commitMatch != null) {
1682264
+ gitOutput.commitHash = commitMatch[1];
1682265
+ }
1682266
+ const branchMatch = log2.message.match(/Pushed branch: (.+)/);
1682267
+ if (branchMatch != null) {
1682268
+ gitOutput.branchUrl = branchMatch[1];
1682269
+ }
1682270
+ const releaseMatch = log2.message.match(/Release tagged\. View here: (.+)/);
1682271
+ if (releaseMatch != null) {
1682272
+ gitOutput.releaseUrl = releaseMatch[1];
1682273
+ }
1682274
+ }
1682275
+ return Object.keys(gitOutput).length > 0 ? gitOutput : void 0;
1682276
+ }
1682277
+ /**
1682278
+ * Resolve output URLs/paths for display.
1682279
+ */
1682280
+ resolveOutput(args, gitOutput) {
1682281
+ const absolutePathToPreview = this.getAbsolutePathToPreview(args);
1682282
+ if (absolutePathToPreview != null) {
1682283
+ return [absolutePathToPreview.toString()];
1682284
+ }
1682285
+ if (gitOutput != null) {
1682286
+ if (gitOutput.pullRequestUrl != null) {
1682287
+ return [gitOutput.pullRequestUrl];
1682288
+ }
1682289
+ if (gitOutput.releaseUrl != null) {
1682290
+ return [gitOutput.releaseUrl];
1682291
+ }
1682292
+ if (gitOutput.branchUrl != null) {
1682293
+ return [gitOutput.branchUrl];
1682294
+ }
1682295
+ }
1682296
+ return this.context.resolveTargetOutputs(args.target);
1682297
+ }
1682213
1682298
  };
1682214
1682299
 
1682215
1682300
  // ../cli-v2/lib/sdk/generator/GeneratorPipeline.js
@@ -1682244,6 +1682329,7 @@ var GeneratorPipeline = class {
1682244
1682329
  cliVersion: this.cliVersion
1682245
1682330
  });
1682246
1682331
  const result = await generationRunner.run({
1682332
+ task: args.task,
1682247
1682333
  target: args.target,
1682248
1682334
  apiDefinition: args.apiDefinition,
1682249
1682335
  organization: args.organization,
@@ -1682253,7 +1682339,6 @@ var GeneratorPipeline = class {
1682253
1682339
  keepContainer: args.keepContainer,
1682254
1682340
  preview: args.preview,
1682255
1682341
  outputPath: args.outputPath,
1682256
- task: args.task,
1682257
1682342
  containerEngine: args.containerEngine
1682258
1682343
  });
1682259
1682344
  if (!result.success) {
@@ -1682282,14 +1682367,14 @@ var GeneratorPipeline = class {
1682282
1682367
  apiDefinition: args.apiDefinition,
1682283
1682368
  organization: args.organization,
1682284
1682369
  token: args.token,
1682370
+ task: args.task,
1682285
1682371
  ai: args.ai,
1682286
1682372
  audiences: args.audiences,
1682287
1682373
  version: args.version,
1682288
1682374
  shouldLogS3Url: args.shouldLogS3Url,
1682289
1682375
  preview: args.preview,
1682290
1682376
  outputPath: args.outputPath,
1682291
- fernignorePath: args.fernignorePath,
1682292
- task: args.task
1682377
+ fernignorePath: args.fernignorePath
1682293
1682378
  });
1682294
1682379
  if (!result.success) {
1682295
1682380
  return {
@@ -1682308,6 +1682393,8 @@ var GeneratorPipeline = class {
1682308
1682393
 
1682309
1682394
  // ../cli-v2/lib/ui/TaskGroup.js
1682310
1682395
  var TaskGroup = class _TaskGroup {
1682396
+ static MAX_DISPLAYED_LOGS_TTY = 10;
1682397
+ static URL_PATTERN = /https?:\/\/[^\s]+/;
1682311
1682398
  context;
1682312
1682399
  ttyAwareLogger;
1682313
1682400
  stream;
@@ -1682387,6 +1682474,45 @@ var TaskGroup = class _TaskGroup {
1682387
1682474
  }
1682388
1682475
  return this;
1682389
1682476
  }
1682477
+ /**
1682478
+ * Define all stages upfront so users see the full journey. All stages start as "pending".
1682479
+ */
1682480
+ setStages(taskId, stages) {
1682481
+ const task = this.tasks[taskId];
1682482
+ if (task == null) {
1682483
+ return this;
1682484
+ }
1682485
+ task.stages = stages.map((stage) => ({
1682486
+ id: stage.id,
1682487
+ status: "pending",
1682488
+ labels: stage.labels
1682489
+ }));
1682490
+ return this;
1682491
+ }
1682492
+ /**
1682493
+ * Update a stage's status by ID. The displayed label automatically changes based on the status.
1682494
+ */
1682495
+ updateStage({ taskId, stageId, status, options: options2 }) {
1682496
+ const task = this.tasks[taskId];
1682497
+ if (task == null) {
1682498
+ return this;
1682499
+ }
1682500
+ if (task.stages == null) {
1682501
+ return this;
1682502
+ }
1682503
+ const stage = task.stages.find((s9) => s9.id === stageId);
1682504
+ if (stage == null) {
1682505
+ return this;
1682506
+ }
1682507
+ stage.status = status;
1682508
+ if (options2?.detail !== void 0) {
1682509
+ stage.detail = options2.detail;
1682510
+ }
1682511
+ if (options2?.error !== void 0) {
1682512
+ stage.error = options2.error;
1682513
+ }
1682514
+ return this;
1682515
+ }
1682390
1682516
  /**
1682391
1682517
  * Called by TtyAwareLogger to get the current display.
1682392
1682518
  * This is the integration point with TtyAwareLogger.
@@ -1682449,24 +1682575,43 @@ ${source_default.dim(`Logs written to: ${logFilePath}`)}
1682449
1682575
  }
1682450
1682576
  formatTaskLine(task, spinnerFrame) {
1682451
1682577
  const name3 = source_default.bold(task.name);
1682452
- const logLines = this.formatTaskLogs(task.logs);
1682578
+ const hasStages = task.stages != null && task.stages.length > 0;
1682453
1682579
  switch (task.status) {
1682454
- case "pending":
1682580
+ case "pending": {
1682581
+ if (hasStages) {
1682582
+ const stageLines = this.formatTaskStages(task.stages, spinnerFrame);
1682583
+ return `${source_default.dim("\u25CB")} ${source_default.dim(name3)}${stageLines}`;
1682584
+ }
1682455
1682585
  return void 0;
1682586
+ }
1682456
1682587
  case "running": {
1682588
+ if (hasStages) {
1682589
+ const stageLines = this.formatTaskStages(task.stages, spinnerFrame, task.logs);
1682590
+ return `${spinnerFrame} ${name3}${stageLines}`;
1682591
+ }
1682457
1682592
  const step = task.currentStep != null ? ` ${source_default.dim(task.currentStep)}` : "";
1682458
- return `${spinnerFrame} ${name3}${step}${logLines}`;
1682593
+ return `${spinnerFrame} ${name3}${step}`;
1682459
1682594
  }
1682460
1682595
  case "success": {
1682461
1682596
  const duration3 = this.formatTaskDuration(task);
1682597
+ if (hasStages) {
1682598
+ const stageLines = this.formatTaskStages(task.stages, spinnerFrame);
1682599
+ const outputLines2 = task.output != null && task.output.length > 0 ? task.output.map((output2) => `
1682600
+ ${source_default.dim("\u2192")} ${source_default.cyan(output2)}`).join("") : "";
1682601
+ return `${source_default.green("\u2713")} ${name3}${duration3}${stageLines}${outputLines2}`;
1682602
+ }
1682462
1682603
  const outputLines = task.output != null && task.output.length > 0 ? task.output.map((output2) => `
1682463
1682604
  ${source_default.dim("\u2192")} ${source_default.cyan(output2)}`).join("") : "";
1682464
- return `${source_default.green("\u2713")} ${name3}${duration3}${logLines}${outputLines}`;
1682605
+ return `${source_default.green("\u2713")} ${name3}${duration3}${outputLines}`;
1682465
1682606
  }
1682466
1682607
  case "error": {
1682467
1682608
  const duration3 = this.formatTaskDuration(task);
1682609
+ if (hasStages) {
1682610
+ const stageLines = this.formatTaskStages(task.stages, spinnerFrame, task.logs);
1682611
+ return `${source_default.red("\u2717")} ${name3}${duration3}${stageLines}`;
1682612
+ }
1682468
1682613
  const errorLines = formatMultilineText({ text: task.error, colorFn: source_default.red.bind(source_default) });
1682469
- return `${source_default.red("x")} ${name3}${duration3}${logLines}${errorLines}`;
1682614
+ return `${source_default.red("\u2717")} ${name3}${duration3}${errorLines}`;
1682470
1682615
  }
1682471
1682616
  case "skipped": {
1682472
1682617
  const reason = task.skipReason != null ? `skipped: ${task.skipReason}` : "skipped";
@@ -1682476,12 +1682621,92 @@ ${source_default.dim(`Logs written to: ${logFilePath}`)}
1682476
1682621
  assertNever2(task.status);
1682477
1682622
  }
1682478
1682623
  }
1682479
- static MAX_DISPLAYED_LOGS_TTY = 10;
1682480
- static URL_PATTERN = /https?:\/\//i;
1682481
- formatTaskLogs(logs) {
1682624
+ /**
1682625
+ * Format the stages for a task as indented lines. When a stage fails, logs are shown under it.
1682626
+ */
1682627
+ formatTaskStages(stages, spinnerFrame, logs) {
1682628
+ if (stages == null || stages.length === 0) {
1682629
+ return "";
1682630
+ }
1682631
+ const lines = [];
1682632
+ for (const stage of stages) {
1682633
+ const icon = this.getStageIcon(stage.status, spinnerFrame);
1682634
+ let line = `
1682635
+ ${icon} ${this.getStageLabel(stage)}`;
1682636
+ if (stage.detail != null) {
1682637
+ line += `
1682638
+ ${source_default.dim(stage.detail)}`;
1682639
+ }
1682640
+ if (stage.status === "error") {
1682641
+ const logLines = this.formatTaskLogs(logs, { baseIndent: 8 });
1682642
+ if (logLines.length > 0) {
1682643
+ line += logLines;
1682644
+ }
1682645
+ if (stage.error != null) {
1682646
+ line += `
1682647
+ ${source_default.red(stage.error)}`;
1682648
+ }
1682649
+ }
1682650
+ lines.push(line);
1682651
+ }
1682652
+ return lines.join("");
1682653
+ }
1682654
+ /**
1682655
+ * Get the appropriate icon for a stage status.
1682656
+ *
1682657
+ * Uses a static arrow (▸) for running stages to avoid visual noise
1682658
+ * from having multiple spinners (the parent task already has a spinner).
1682659
+ */
1682660
+ getStageIcon(status, _spinnerFrame) {
1682661
+ switch (status) {
1682662
+ case "pending":
1682663
+ return source_default.dim("\u25CB");
1682664
+ case "running":
1682665
+ return source_default.cyan("\u25B8");
1682666
+ case "success":
1682667
+ return source_default.green("\u2713");
1682668
+ case "error":
1682669
+ return source_default.red("\u2717");
1682670
+ case "skipped":
1682671
+ return source_default.dim("\u25CB");
1682672
+ default:
1682673
+ assertNever2(status);
1682674
+ }
1682675
+ }
1682676
+ /**
1682677
+ * Get the formatted label for a stage based on its status.
1682678
+ * Uses the appropriate label from the stage's labels map.
1682679
+ */
1682680
+ getStageLabel(stage) {
1682681
+ const label = stage.labels[stage.status] ?? stage.labels.pending;
1682682
+ switch (stage.status) {
1682683
+ case "pending":
1682684
+ return source_default.dim(label);
1682685
+ case "running":
1682686
+ return source_default.cyan(label);
1682687
+ case "success":
1682688
+ return label;
1682689
+ case "error":
1682690
+ return source_default.red(label);
1682691
+ case "skipped":
1682692
+ return source_default.dim(label);
1682693
+ default:
1682694
+ assertNever2(stage.status);
1682695
+ }
1682696
+ }
1682697
+ /**
1682698
+ * Format task logs for display.
1682699
+ *
1682700
+ * @param logs - The logs to format
1682701
+ * @param options - Formatting options
1682702
+ * @param options.baseIndent - Number of spaces for base indentation (default: 8 for stage nesting)
1682703
+ */
1682704
+ formatTaskLogs(logs, options2) {
1682482
1682705
  if (logs == null || logs.length === 0) {
1682483
1682706
  return "";
1682484
1682707
  }
1682708
+ const baseIndent = options2?.baseIndent ?? 8;
1682709
+ const indentStr = " ".repeat(baseIndent);
1682485
1682710
  const shouldLimit = this.context.isTTY;
1682486
1682711
  const filteredLogs = shouldLimit ? logs.filter((log2) => log2.level !== "debug" || !_TaskGroup.URL_PATTERN.test(log2.message)) : logs;
1682487
1682712
  const logsToShow = shouldLimit && filteredLogs.length > _TaskGroup.MAX_DISPLAYED_LOGS_TTY ? filteredLogs.slice(-_TaskGroup.MAX_DISPLAYED_LOGS_TTY) : filteredLogs;
@@ -1682493,19 +1682718,23 @@ ${source_default.dim(`Logs written to: ${logFilePath}`)}
1682493
1682718
  const message = shouldLimit ? this.truncateMessage(log2.message, maxMessageLength) : log2.message;
1682494
1682719
  const icon = source_default.dim("\u2022");
1682495
1682720
  return `
1682496
- ${icon} ${source_default.dim(message)}`;
1682721
+ ${indentStr}${icon} ${source_default.dim(message)}`;
1682497
1682722
  }
1682498
1682723
  case "warn":
1682499
1682724
  return formatMultilineText({
1682500
1682725
  text: log2.message,
1682501
1682726
  colorFn: source_default.yellow.bind(source_default),
1682502
- icon: source_default.yellow("\u26A0")
1682727
+ icon: source_default.yellow("\u26A0"),
1682728
+ baseIndent,
1682729
+ continuationIndent: baseIndent + 2
1682503
1682730
  });
1682504
1682731
  case "error":
1682505
1682732
  return formatMultilineText({
1682506
1682733
  text: log2.message,
1682507
1682734
  colorFn: source_default.red.bind(source_default),
1682508
- icon: source_default.red("\u2717")
1682735
+ icon: source_default.red("\u2717"),
1682736
+ baseIndent,
1682737
+ continuationIndent: baseIndent + 2
1682509
1682738
  });
1682510
1682739
  default:
1682511
1682740
  assertNever2(log2.level);
@@ -1682513,20 +1682742,16 @@ ${source_default.dim(`Logs written to: ${logFilePath}`)}
1682513
1682742
  }).join("");
1682514
1682743
  if (hiddenCount > 0) {
1682515
1682744
  return `
1682516
- ${source_default.dim(`... ${hiddenCount} earlier logs hidden ...`)}${formattedLogs}`;
1682745
+ ${indentStr}${source_default.dim(`... ${hiddenCount} earlier logs hidden ...`)}${formattedLogs}`;
1682517
1682746
  }
1682518
1682747
  return formattedLogs;
1682519
1682748
  }
1682520
1682749
  /**
1682521
1682750
  * Maximum characters for a log message line in TTY mode.
1682522
- * This prevents terminal line wrapping which breaks the paint/clear cycle.
1682523
- *
1682524
- * Account for: box prefix "│ " (2) + indent " " (4) + icon + space "• " (2) = 8 chars
1682525
- * Use 72 chars for message to stay under 80 col, or scale with terminal width.
1682526
1682751
  */
1682527
1682752
  getMaxLogMessageLength() {
1682528
1682753
  const terminalWidth = this.stream.columns ?? 80;
1682529
- return Math.max(40, terminalWidth - 12);
1682754
+ return Math.max(40, terminalWidth - 16);
1682530
1682755
  }
1682531
1682756
  truncateMessage(message, maxLength) {
1682532
1682757
  const sanitized = message.replace(/[\r\n]+/g, " ").trim();
@@ -1682565,6 +1682790,171 @@ ${source_default.dim(`Logs written to: ${logFilePath}`)}
1682565
1682790
  }
1682566
1682791
  };
1682567
1682792
 
1682793
+ // ../cli-v2/lib/ui/TaskStageController.js
1682794
+ var TaskStageController = class {
1682795
+ taskGroup;
1682796
+ taskId;
1682797
+ stageId;
1682798
+ constructor({ taskGroup, taskId, stageId }) {
1682799
+ this.taskGroup = taskGroup;
1682800
+ this.taskId = taskId;
1682801
+ this.stageId = stageId;
1682802
+ }
1682803
+ start(detail) {
1682804
+ this.taskGroup.updateStage({
1682805
+ taskId: this.taskId,
1682806
+ stageId: this.stageId,
1682807
+ status: "running",
1682808
+ options: { detail }
1682809
+ });
1682810
+ }
1682811
+ complete(detail) {
1682812
+ this.taskGroup.updateStage({
1682813
+ taskId: this.taskId,
1682814
+ stageId: this.stageId,
1682815
+ status: "success",
1682816
+ options: { detail }
1682817
+ });
1682818
+ }
1682819
+ fail(error49) {
1682820
+ this.taskGroup.updateStage({
1682821
+ taskId: this.taskId,
1682822
+ stageId: this.stageId,
1682823
+ status: "error"
1682824
+ });
1682825
+ const task = this.taskGroup.getTask(this.taskId);
1682826
+ if (task == null) {
1682827
+ return;
1682828
+ }
1682829
+ this.taskGroup.updateTask({
1682830
+ id: this.taskId,
1682831
+ update: { status: "error", error: error49 }
1682832
+ });
1682833
+ }
1682834
+ };
1682835
+
1682836
+ // ../cli-v2/lib/sdk/task/SdkTask.js
1682837
+ var SdkTask = class {
1682838
+ id;
1682839
+ taskGroup;
1682840
+ stage;
1682841
+ constructor({ id: id2, taskGroup }) {
1682842
+ this.id = id2;
1682843
+ this.taskGroup = taskGroup;
1682844
+ this.stage = {
1682845
+ validation: new TaskStageController({ taskId: id2, taskGroup, stageId: "validation" }),
1682846
+ generator: new TaskStageController({ taskId: id2, taskGroup, stageId: "generator" }),
1682847
+ output: new TaskStageController({ taskId: id2, taskGroup, stageId: "output" })
1682848
+ };
1682849
+ }
1682850
+ /**
1682851
+ * Get the underlying Task object for this SdkTask.
1682852
+ */
1682853
+ getTask() {
1682854
+ const task = this.taskGroup.getTask(this.id);
1682855
+ if (task == null) {
1682856
+ throw new Error(`Internal error; task '${this.id}' does not exist`);
1682857
+ }
1682858
+ return task;
1682859
+ }
1682860
+ /** Mark the task as running */
1682861
+ start(currentStep) {
1682862
+ this.taskGroup.updateTask({
1682863
+ id: this.id,
1682864
+ update: { status: "running", currentStep }
1682865
+ });
1682866
+ }
1682867
+ /** Mark the task as successfully completed */
1682868
+ complete(output2) {
1682869
+ this.taskGroup.updateTask({
1682870
+ id: this.id,
1682871
+ update: { status: "success", output: output2 }
1682872
+ });
1682873
+ }
1682874
+ /** Mark the task as failed */
1682875
+ fail(error49) {
1682876
+ this.taskGroup.updateTask({
1682877
+ id: this.id,
1682878
+ update: { status: "error", error: error49 }
1682879
+ });
1682880
+ }
1682881
+ };
1682882
+
1682883
+ // ../cli-v2/lib/sdk/task/SdkTaskGroup.js
1682884
+ var SdkTaskGroup = class {
1682885
+ taskGroup;
1682886
+ tasks = {};
1682887
+ defaultStages = [
1682888
+ {
1682889
+ id: "validation",
1682890
+ labels: {
1682891
+ pending: "Validate API",
1682892
+ running: "Validating API...",
1682893
+ success: "Validated API"
1682894
+ }
1682895
+ },
1682896
+ {
1682897
+ id: "generator",
1682898
+ labels: {
1682899
+ pending: "Run generator",
1682900
+ running: "Running generator...",
1682901
+ success: "Generator completed"
1682902
+ }
1682903
+ },
1682904
+ {
1682905
+ id: "output",
1682906
+ labels: {
1682907
+ pending: "Write output",
1682908
+ running: "Writing output...",
1682909
+ success: "Wrote output"
1682910
+ }
1682911
+ }
1682912
+ ];
1682913
+ constructor({ context: context2 }) {
1682914
+ this.tasks = {};
1682915
+ this.taskGroup = new TaskGroup({ context: context2 });
1682916
+ }
1682917
+ addTask({ id: id2, name: name3, stageOverrides }) {
1682918
+ const displayName = name3 ?? id2;
1682919
+ this.taskGroup.addTask({ id: id2, name: displayName });
1682920
+ const stages = this.buildStages(stageOverrides);
1682921
+ this.taskGroup.setStages(id2, stages);
1682922
+ const task = new SdkTask({ taskGroup: this.taskGroup, id: id2 });
1682923
+ this.tasks[id2] = task;
1682924
+ return task;
1682925
+ }
1682926
+ getTask(id2) {
1682927
+ return this.tasks[id2];
1682928
+ }
1682929
+ async start(header) {
1682930
+ await this.taskGroup.start(header);
1682931
+ }
1682932
+ finish({ successMessage, errorMessage }) {
1682933
+ return this.taskGroup.complete({
1682934
+ successMessage,
1682935
+ errorMessage
1682936
+ });
1682937
+ }
1682938
+ /**
1682939
+ * Build stage definitions, merging any overrides with defaults.
1682940
+ */
1682941
+ buildStages(overrides) {
1682942
+ if (overrides == null) {
1682943
+ return this.defaultStages;
1682944
+ }
1682945
+ return this.defaultStages.map((stage) => {
1682946
+ const override = overrides[stage.id];
1682947
+ if (override == null) {
1682948
+ return stage;
1682949
+ }
1682950
+ return {
1682951
+ ...stage,
1682952
+ labels: { ...stage.labels, ...override }
1682953
+ };
1682954
+ });
1682955
+ }
1682956
+ };
1682957
+
1682568
1682958
  // ../cli-v2/lib/commands/sdk/generate/command.js
1682569
1682959
  var GenerateCommand = class {
1682570
1682960
  async handle(context2, args) {
@@ -1682597,53 +1682987,46 @@ var GenerateCommand = class {
1682597
1682987
  });
1682598
1682988
  const token = args.local ? void 0 : await context2.getTokenOrPrompt();
1682599
1682989
  const runtime = args.local ? "local" : "remote";
1682600
- const taskGroup = new TaskGroup({ context: context2 });
1682601
- const skippedTargets = targets2.filter((t2) => checkResult.invalidApis.has(t2.api));
1682602
- for (const target of skippedTargets) {
1682990
+ const taskGroup = new SdkTaskGroup({ context: context2 });
1682991
+ for (const target of targets2) {
1682992
+ const stageOverrides = target.output.git != null ? {
1682993
+ output: this.getGitOutputStageLabels(target.output.git.mode ?? "pr")
1682994
+ } : void 0;
1682603
1682995
  taskGroup.addTask({
1682604
1682996
  id: target.name,
1682605
1682997
  name: target.name,
1682606
- status: "skipped",
1682607
- skipReason: `API '${target.api}' has errors`
1682608
- });
1682609
- }
1682610
- for (const target of validTargets) {
1682611
- taskGroup.addTask({
1682612
- id: target.name,
1682613
- name: target.name
1682998
+ stageOverrides
1682614
1682999
  });
1682615
1683000
  }
1683001
+ const sdkInitialism = this.maybePluralSdks(targets2);
1682616
1683002
  await taskGroup.start({
1682617
- title: "Generating SDKs",
1683003
+ title: `Generating ${sdkInitialism}`,
1682618
1683004
  subtitle: `org: ${workspace.sdks.org}`
1682619
1683005
  });
1682620
- await Promise.all(validTargets.map(async (target) => {
1683006
+ await Promise.all(targets2.map(async (target) => {
1683007
+ const task = taskGroup.getTask(target.name);
1683008
+ if (task == null) {
1683009
+ throw new Error(`Internal error; task '${target.name}' not found`);
1683010
+ }
1683011
+ task.start();
1683012
+ task.stage.validation.start();
1682621
1683013
  const apiDefinition = workspace.apis[target.api];
1682622
1683014
  if (apiDefinition == null) {
1682623
- const message = `API '${target.api}' not found in workspace`;
1682624
- taskGroup.updateTask({
1682625
- id: target.name,
1682626
- update: { status: "error", error: message }
1682627
- });
1683015
+ task.stage.validation.fail(`API not found in workspace`);
1682628
1683016
  return;
1682629
1683017
  }
1682630
- const task = taskGroup.getTask(target.name);
1682631
- if (task == null) {
1682632
- throw new Error(`Internal error; task '${target.name}' not found`);
1683018
+ if (checkResult.invalidApis.has(target.api)) {
1683019
+ task.stage.validation.fail(`API is invalid`);
1683020
+ return;
1682633
1683021
  }
1682634
- taskGroup.updateTask({
1682635
- id: target.name,
1682636
- update: {
1682637
- status: "running",
1682638
- currentStep: `${target.image}:${target.version}`
1682639
- }
1682640
- });
1683022
+ task.stage.validation.complete();
1683023
+ task.stage.generator.start();
1682641
1683024
  const pipelineResult = await pipeline5.run({
1682642
1683025
  organization: workspace.org,
1682643
1683026
  ai: workspace.ai,
1682644
- task,
1682645
1683027
  target,
1682646
1683028
  apiDefinition,
1683029
+ task: task.getTask(),
1682647
1683030
  audiences: this.parseAudiences(args.audience),
1682648
1683031
  runtime,
1682649
1683032
  containerEngine: args["container-engine"] ?? "docker",
@@ -1682654,31 +1683037,35 @@ var GenerateCommand = class {
1682654
1683037
  version: args.version
1682655
1683038
  });
1682656
1683039
  if (!pipelineResult.success) {
1682657
- taskGroup.updateTask({
1682658
- id: target.name,
1682659
- update: {
1682660
- status: "error",
1682661
- error: pipelineResult.error
1682662
- }
1682663
- });
1683040
+ task.stage.generator.fail(pipelineResult.error);
1682664
1683041
  return;
1682665
1683042
  }
1682666
- taskGroup.updateTask({
1682667
- id: target.name,
1682668
- update: {
1682669
- status: "success",
1682670
- output: pipelineResult.output
1682671
- }
1682672
- });
1683043
+ task.stage.generator.complete();
1683044
+ task.stage.output.complete();
1683045
+ task.complete(pipelineResult.output);
1682673
1683046
  }));
1682674
- const summary = taskGroup.complete({
1682675
- successMessage: `Successfully generated ${this.maybePluralSdks(validTargets)}`,
1682676
- errorMessage: `Failed to generate ${this.maybePluralSdks(validTargets)}`
1683047
+ const summary = taskGroup.finish({
1683048
+ successMessage: `Successfully generated ${sdkInitialism}`,
1683049
+ errorMessage: `Failed to generate ${sdkInitialism}`
1682677
1683050
  });
1682678
1683051
  if (summary.failedCount > 0) {
1682679
1683052
  throw CliError.exit();
1682680
1683053
  }
1682681
1683054
  }
1683055
+ validateArgs(args) {
1683056
+ if (args.output != null && !args.preview) {
1683057
+ throw new Error("The --output flag can only be used with --preview");
1683058
+ }
1683059
+ if (args["container-engine"] != null && !args.local) {
1683060
+ throw new Error("The --container-engine flag can only be used with --local");
1683061
+ }
1683062
+ if (args.group != null && args.target != null) {
1683063
+ throw new Error("The --group and --target flags cannot be used together");
1683064
+ }
1683065
+ if (args.group == null && args.target == null) {
1683066
+ throw new Error("A --target or --group must be specified");
1683067
+ }
1683068
+ }
1682682
1683069
  getTargets({ workspace, groupName, targetName }) {
1682683
1683070
  const targets2 = workspace.sdks != null ? this.filterTargetsByGroup(workspace.sdks.targets, groupName) : [];
1682684
1683071
  if (targetName != null) {
@@ -1682696,9 +1683083,6 @@ var GenerateCommand = class {
1682696
1683083
  }
1682697
1683084
  return targets2;
1682698
1683085
  }
1682699
- /**
1682700
- * Filter targets by group name. If no group is specified, returns all targets.
1682701
- */
1682702
1683086
  filterTargetsByGroup(targets2, groupName) {
1682703
1683087
  if (groupName == null) {
1682704
1683088
  return targets2;
@@ -1682714,23 +1683098,33 @@ var GenerateCommand = class {
1682714
1683098
  audiences
1682715
1683099
  };
1682716
1683100
  }
1682717
- validateArgs(args) {
1682718
- if (args.output != null && !args.preview) {
1682719
- throw new Error("The --output flag can only be used with --preview");
1682720
- }
1682721
- if (args["container-engine"] != null && !args.local) {
1682722
- throw new Error("The --container-engine flag can only be used with --local");
1682723
- }
1682724
- if (args.group != null && args.target != null) {
1682725
- throw new Error("The --group and --target flags cannot be used together");
1682726
- }
1682727
- if (args.group == null && args.target == null) {
1682728
- throw new Error("A --target or --group must be specified");
1682729
- }
1682730
- }
1682731
1683101
  maybePluralSdks(targets2) {
1682732
1683102
  return targets2.length === 1 ? "SDK" : "SDKs";
1682733
1683103
  }
1683104
+ getGitOutputStageLabels(mode) {
1683105
+ switch (mode) {
1683106
+ case "push":
1683107
+ return {
1683108
+ pending: "Push to repository",
1683109
+ running: "Pushing to repository...",
1683110
+ success: "Pushed to repository"
1683111
+ };
1683112
+ case "release":
1683113
+ return {
1683114
+ pending: "Create release",
1683115
+ running: "Creating release...",
1683116
+ success: "Created release"
1683117
+ };
1683118
+ case "pr":
1683119
+ return {
1683120
+ pending: "Create pull request",
1683121
+ running: "Creating pull request...",
1683122
+ success: "Created pull request"
1683123
+ };
1683124
+ default:
1683125
+ assertNever2(mode);
1683126
+ }
1683127
+ }
1682734
1683128
  };
1682735
1683129
  function addGenerateCommand(cli) {
1682736
1683130
  const cmd = new GenerateCommand();
@@ -1713625,7 +1714019,7 @@ var CliContext = class {
1713625
1714019
  if (false) {
1713626
1714020
  this.logger.error("CLI_VERSION is not defined");
1713627
1714021
  }
1713628
- return "3.64.5";
1714022
+ return "3.64.6";
1713629
1714023
  }
1713630
1714024
  getCliName() {
1713631
1714025
  if (false) {
@@ -1714384,7 +1714778,13 @@ async function loadAndUpdateGenerators({
1714384
1714778
  );
1714385
1714779
  }
1714386
1714780
  const generatorName = generator.get("name");
1714387
- const normalizedGeneratorName = getGeneratorNameOrThrow(generatorName, context2);
1714781
+ const normalizedGeneratorName = normalizeGeneratorName(generatorName);
1714782
+ if (normalizedGeneratorName == null) {
1714783
+ context2.logger.warn(
1714784
+ `Skipping unrecognized generator: ${generatorName}. The generator will not be upgraded.`
1714785
+ );
1714786
+ continue;
1714787
+ }
1714388
1714788
  const normalizedGeneratorFilter = generatorFilter != null ? addDefaultDockerOrgIfNotPresent(generatorFilter) : void 0;
1714389
1714789
  if (normalizedGeneratorFilter != null && normalizedGeneratorName !== normalizedGeneratorFilter) {
1714390
1714790
  context2.logger.debug(
@@ -1716739,7 +1717139,7 @@ var import_path56 = __toESM(require("path"), 1);
1716739
1717139
  var LOCAL_STORAGE_FOLDER4 = ".fern-dev";
1716740
1717140
  var LOGS_FOLDER_NAME = "logs";
1716741
1717141
  function getCliSource() {
1716742
- const version7 = "3.64.5";
1717142
+ const version7 = "3.64.6";
1716743
1717143
  return `cli@${version7}`;
1716744
1717144
  }
1716745
1717145
  var DebugLogger = class {
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "3.64.5",
2
+ "version": "3.64.6",
3
3
  "repository": {
4
4
  "type": "git",
5
5
  "url": "git+https://github.com/fern-api/fern.git",