transloadit 4.8.0 → 4.8.2

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 (57) hide show
  1. package/README.md +92 -43
  2. package/dist/bearerToken.d.ts +1 -0
  3. package/dist/bearerToken.d.ts.map +1 -1
  4. package/dist/bearerToken.js +5 -3
  5. package/dist/bearerToken.js.map +1 -1
  6. package/dist/cli/commands/BaseCommand.d.ts +0 -1
  7. package/dist/cli/commands/BaseCommand.d.ts.map +1 -1
  8. package/dist/cli/commands/BaseCommand.js +7 -9
  9. package/dist/cli/commands/BaseCommand.js.map +1 -1
  10. package/dist/cli/commands/auth.d.ts.map +1 -1
  11. package/dist/cli/commands/auth.js +49 -29
  12. package/dist/cli/commands/auth.js.map +1 -1
  13. package/dist/cli/generateIntentDocs.d.ts.map +1 -1
  14. package/dist/cli/generateIntentDocs.js +9 -7
  15. package/dist/cli/generateIntentDocs.js.map +1 -1
  16. package/dist/cli/helpers.d.ts +19 -4
  17. package/dist/cli/helpers.d.ts.map +1 -1
  18. package/dist/cli/helpers.js +232 -10
  19. package/dist/cli/helpers.js.map +1 -1
  20. package/dist/cli/intentCommands.d.ts.map +1 -1
  21. package/dist/cli/intentCommands.js +11 -7
  22. package/dist/cli/intentCommands.js.map +1 -1
  23. package/dist/cli/intentRuntime.d.ts +17 -2
  24. package/dist/cli/intentRuntime.d.ts.map +1 -1
  25. package/dist/cli/intentRuntime.js +163 -33
  26. package/dist/cli/intentRuntime.js.map +1 -1
  27. package/dist/cli/semanticIntents/imageDescribe.d.ts +2 -1
  28. package/dist/cli/semanticIntents/imageDescribe.d.ts.map +1 -1
  29. package/dist/cli/semanticIntents/imageDescribe.js +5 -4
  30. package/dist/cli/semanticIntents/imageDescribe.js.map +1 -1
  31. package/dist/cli/semanticIntents/imageGenerate.d.ts +1 -0
  32. package/dist/cli/semanticIntents/imageGenerate.d.ts.map +1 -1
  33. package/dist/cli/semanticIntents/imageGenerate.js +4 -3
  34. package/dist/cli/semanticIntents/imageGenerate.js.map +1 -1
  35. package/dist/cli/semanticIntents/index.d.ts +1 -0
  36. package/dist/cli/semanticIntents/index.d.ts.map +1 -1
  37. package/dist/cli/semanticIntents/index.js.map +1 -1
  38. package/dist/cli/semanticIntents/markdownPdf.d.ts.map +1 -1
  39. package/dist/cli/semanticIntents/markdownPdf.js +2 -1
  40. package/dist/cli/semanticIntents/markdownPdf.js.map +1 -1
  41. package/dist/cli.d.ts +0 -1
  42. package/dist/cli.d.ts.map +1 -1
  43. package/dist/cli.js +3 -2
  44. package/dist/cli.js.map +1 -1
  45. package/package.json +1 -1
  46. package/src/bearerToken.ts +14 -3
  47. package/src/cli/commands/BaseCommand.ts +7 -9
  48. package/src/cli/commands/auth.ts +66 -32
  49. package/src/cli/generateIntentDocs.ts +10 -7
  50. package/src/cli/helpers.ts +278 -13
  51. package/src/cli/intentCommands.ts +11 -7
  52. package/src/cli/intentRuntime.ts +214 -33
  53. package/src/cli/semanticIntents/imageDescribe.ts +5 -4
  54. package/src/cli/semanticIntents/imageGenerate.ts +4 -3
  55. package/src/cli/semanticIntents/index.ts +1 -0
  56. package/src/cli/semanticIntents/markdownPdf.ts +2 -1
  57. package/src/cli.ts +3 -2
@@ -1,5 +1,5 @@
1
1
  import { statSync } from 'node:fs'
2
- import { basename } from 'node:path'
2
+ import { basename, dirname, join, parse, resolve } from 'node:path'
3
3
  import { Option } from 'clipanion'
4
4
  import type { z } from 'zod'
5
5
 
@@ -59,6 +59,7 @@ export type IntentFileExecutionDefinition =
59
59
 
60
60
  export interface IntentFileCommandDefinition {
61
61
  commandLabel: string
62
+ defaultOutputPath: string
62
63
  execution: IntentFileExecutionDefinition
63
64
  inputPolicy: IntentInputPolicy
64
65
  outputDescription: string
@@ -66,6 +67,7 @@ export interface IntentFileCommandDefinition {
66
67
  }
67
68
 
68
69
  export interface IntentNoInputCommandDefinition {
70
+ defaultOutputPath: string
69
71
  execution: IntentSingleStepExecutionDefinition
70
72
  outputDescription: string
71
73
  outputMode?: 'directory' | 'file'
@@ -338,6 +340,7 @@ async function executeIntentCommand({
338
340
  client,
339
341
  definition,
340
342
  output,
343
+ outputMode,
341
344
  outputPath,
342
345
  printUrls,
343
346
  rawValues,
@@ -347,6 +350,7 @@ async function executeIntentCommand({
347
350
  createOptions: Omit<AssembliesCreateOptions, 'output' | 'steps' | 'stepsData' | 'template'>
348
351
  definition: IntentFileCommandDefinition | IntentNoInputCommandDefinition
349
352
  output: AuthenticatedCommand['output']
353
+ outputMode?: 'directory' | 'file'
350
354
  outputPath?: string
351
355
  printUrls: boolean
352
356
  rawValues: Record<string, unknown>
@@ -379,7 +383,7 @@ async function executeIntentCommand({
379
383
  const { hasFailures, resultUrls } = await assembliesCommands.create(output, client, {
380
384
  ...createOptions,
381
385
  output: outputPath ?? null,
382
- outputMode: definition.outputMode,
386
+ outputMode,
383
387
  ...executionOptions,
384
388
  })
385
389
  if (printUrls) {
@@ -391,7 +395,8 @@ async function executeIntentCommand({
391
395
  abstract class GeneratedIntentCommandBase extends AuthenticatedCommand {
392
396
  declare static intentDefinition: IntentFileCommandDefinition | IntentNoInputCommandDefinition
393
397
 
394
- outputPath = Option.String('--out,-o', {
398
+ // Intents standardize on --output while the surface is still young enough to change cleanly.
399
+ outputPath = Option.String('--output,-o', {
395
400
  description: this.getOutputDescription(),
396
401
  })
397
402
 
@@ -410,23 +415,58 @@ abstract class GeneratedIntentCommandBase extends AuthenticatedCommand {
410
415
  return this.getIntentDefinition().outputDescription
411
416
  }
412
417
 
413
- protected validateOutputChoice(): number | undefined {
414
- if (this.outputPath == null && !this.printUrls) {
415
- this.output.error('Specify at least one of --out or --print-urls')
416
- return 1
418
+ protected resolveDefaultOutputPath(rawValues: Record<string, unknown>): string | undefined {
419
+ const defaultOutputPath = this.getIntentDefinition().defaultOutputPath
420
+ if (this.getIntentDefinition().outputMode === 'directory') {
421
+ return defaultOutputPath
417
422
  }
418
423
 
419
- return undefined
424
+ const format = rawValues.format
425
+ if (typeof format !== 'string') {
426
+ return defaultOutputPath
427
+ }
428
+
429
+ const trimmedFormat = format.trim().toLowerCase()
430
+ if (!/^[a-z0-9]+(?:[-_][a-z0-9]+)*$/.test(trimmedFormat)) {
431
+ return defaultOutputPath
432
+ }
433
+
434
+ const parsedDefaultOutputPath = parse(defaultOutputPath)
435
+ const outputBasename =
436
+ parsedDefaultOutputPath.name === ''
437
+ ? basename(defaultOutputPath, parsedDefaultOutputPath.ext)
438
+ : parsedDefaultOutputPath.name
439
+
440
+ return join(parsedDefaultOutputPath.dir, `${outputBasename}.${trimmedFormat}`)
441
+ }
442
+
443
+ protected getDefaultOutputPath(rawValues: Record<string, unknown>): string | undefined {
444
+ return this.resolveDefaultOutputPath(rawValues)
445
+ }
446
+
447
+ protected getEffectiveOutputPath(rawValues: Record<string, unknown>): string | undefined {
448
+ if (this.outputPath != null) {
449
+ return this.outputPath
450
+ }
451
+
452
+ if (this.printUrls) {
453
+ return undefined
454
+ }
455
+
456
+ return this.getDefaultOutputPath(rawValues)
457
+ }
458
+
459
+ protected getEffectiveOutputMode(
460
+ _rawValues: Record<string, unknown>,
461
+ _outputPath: string | undefined,
462
+ ): 'directory' | 'file' | undefined {
463
+ return this.getIntentDefinition().outputMode
420
464
  }
421
465
  }
422
466
 
423
467
  export abstract class GeneratedNoInputIntentCommand extends GeneratedIntentCommandBase {
424
468
  protected override async run(): Promise<number | undefined> {
425
- const outputValidationError = this.validateOutputChoice()
426
- if (outputValidationError != null) {
427
- return outputValidationError
428
- }
429
-
469
+ const rawValues = this.getIntentRawValues()
430
470
  return await executeIntentCommand({
431
471
  client: this.client,
432
472
  createOptions: {
@@ -434,9 +474,10 @@ export abstract class GeneratedNoInputIntentCommand extends GeneratedIntentComma
434
474
  },
435
475
  definition: this.getIntentDefinition() as IntentNoInputCommandDefinition,
436
476
  output: this.output,
437
- outputPath: this.outputPath,
477
+ outputMode: this.getEffectiveOutputMode(rawValues, this.getEffectiveOutputPath(rawValues)),
478
+ outputPath: this.getEffectiveOutputPath(rawValues),
438
479
  printUrls: this.printUrls ?? false,
439
- rawValues: this.getIntentRawValues(),
480
+ rawValues,
440
481
  })
441
482
  }
442
483
  }
@@ -481,6 +522,126 @@ abstract class GeneratedFileIntentCommandBase extends GeneratedIntentCommandBase
481
522
  return super.getIntentDefinition() as IntentFileCommandDefinition
482
523
  }
483
524
 
525
+ protected getSingleFilesystemFileInput(): string | null {
526
+ if ((this.inputBase64?.length ?? 0) > 0) {
527
+ return null
528
+ }
529
+
530
+ const localInputs = (this.inputs ?? []).filter((input) => input !== '-' && !isHttpUrl(input))
531
+ if (localInputs.length !== 1) {
532
+ return null
533
+ }
534
+
535
+ const candidate = localInputs[0]
536
+ if (candidate == null) {
537
+ return null
538
+ }
539
+
540
+ try {
541
+ return statSync(candidate).isFile() ? candidate : null
542
+ } catch {
543
+ return null
544
+ }
545
+ }
546
+
547
+ protected hasDirectoryInput(): boolean {
548
+ return (this.inputs ?? []).some((input) => {
549
+ if (input === '-' || isHttpUrl(input)) {
550
+ return false
551
+ }
552
+
553
+ try {
554
+ return statSync(input).isDirectory()
555
+ } catch {
556
+ return false
557
+ }
558
+ })
559
+ }
560
+
561
+ protected prefersDirectoryDefaultOutput(): boolean {
562
+ return this.getIntentDefinition().outputMode === 'directory'
563
+ }
564
+
565
+ protected getSuggestedDirectoryOutputPath(): string {
566
+ if (this.getIntentDefinition().outputMode === 'directory') {
567
+ return this.resolveDefaultOutputPath({}) ?? 'output/'
568
+ }
569
+
570
+ return 'output/'
571
+ }
572
+
573
+ private getSafeSiblingDirectoryOutputPath(inputPath: string): string {
574
+ const parsedInputPath = parse(inputPath)
575
+ const candidateBaseName =
576
+ parsedInputPath.name === '' ? basename(inputPath) : parsedInputPath.name
577
+ const candidateDirectoryPath = join(dirname(inputPath), candidateBaseName)
578
+
579
+ const isSafeDirectoryCandidate = (candidatePath: string): boolean => {
580
+ if (resolve(candidatePath) === resolve(inputPath)) {
581
+ return false
582
+ }
583
+
584
+ try {
585
+ return statSync(candidatePath).isDirectory()
586
+ } catch {
587
+ return true
588
+ }
589
+ }
590
+
591
+ if (isSafeDirectoryCandidate(candidateDirectoryPath)) {
592
+ return candidateDirectoryPath
593
+ }
594
+
595
+ for (let suffixIndex = 0; suffixIndex < 1000; suffixIndex += 1) {
596
+ const suffix = suffixIndex === 0 ? '-output' : `-output-${suffixIndex + 1}`
597
+ const fallbackDirectoryPath = join(dirname(inputPath), `${candidateBaseName}${suffix}`)
598
+ if (isSafeDirectoryCandidate(fallbackDirectoryPath)) {
599
+ return fallbackDirectoryPath
600
+ }
601
+ }
602
+
603
+ throw new Error(`Could not infer a safe output directory path for ${inputPath}`)
604
+ }
605
+
606
+ protected getSiblingOutputPath(inputPath: string, rawValues: Record<string, unknown>): string {
607
+ if (this.getIntentDefinition().outputMode === 'directory') {
608
+ return this.getSafeSiblingDirectoryOutputPath(inputPath)
609
+ }
610
+
611
+ const resolvedDefaultOutputPath = this.resolveDefaultOutputPath(rawValues)
612
+ const extension = parse(
613
+ resolvedDefaultOutputPath ?? this.getIntentDefinition().defaultOutputPath,
614
+ ).ext
615
+ const parsedInputPath = parse(inputPath)
616
+ const candidateOutputPath = join(dirname(inputPath), `${parsedInputPath.name}${extension}`)
617
+ if (resolve(candidateOutputPath) !== resolve(inputPath)) {
618
+ return candidateOutputPath
619
+ }
620
+
621
+ return join(dirname(inputPath), `${parsedInputPath.name}-output${extension}`)
622
+ }
623
+
624
+ protected override getDefaultOutputPath(rawValues: Record<string, unknown>): string | undefined {
625
+ if (this.prefersDirectoryDefaultOutput()) {
626
+ const singleFilesystemFileInput = this.getSingleFilesystemFileInput()
627
+ if (
628
+ this.getIntentDefinition().outputMode === 'directory' &&
629
+ singleFilesystemFileInput != null
630
+ ) {
631
+ return this.getSiblingOutputPath(singleFilesystemFileInput, rawValues)
632
+ }
633
+
634
+ return this.getSuggestedDirectoryOutputPath()
635
+ }
636
+
637
+ const singleFilesystemFileInput = this.getSingleFilesystemFileInput()
638
+ if (singleFilesystemFileInput != null) {
639
+ return this.getSiblingOutputPath(singleFilesystemFileInput, rawValues)
640
+ }
641
+
642
+ return this.resolveDefaultOutputPath(rawValues)
643
+ }
644
+
484
645
  protected async prepareInputs(): Promise<PreparedIntentInputs> {
485
646
  return await prepareIntentInputs({
486
647
  inputValues: this.inputs ?? [],
@@ -513,24 +674,39 @@ abstract class GeneratedFileIntentCommandBase extends GeneratedIntentCommandBase
513
674
  )
514
675
  }
515
676
 
516
- protected resolveOutputMode(): 'directory' | 'file' | undefined {
677
+ protected resolveOutputMode(outputPath: string | undefined): 'directory' | 'file' | undefined {
517
678
  if (this.getIntentDefinition().outputMode != null) {
518
679
  return this.getIntentDefinition().outputMode
519
680
  }
520
681
 
521
- if (this.outputPath == null) {
682
+ if (outputPath == null) {
522
683
  return undefined
523
684
  }
524
685
 
686
+ if (this.outputPath == null && this.prefersDirectoryDefaultOutput()) {
687
+ return 'directory'
688
+ }
689
+
690
+ if (/[\\/]$/.test(outputPath)) {
691
+ return 'directory'
692
+ }
693
+
525
694
  try {
526
- return statSync(this.outputPath).isDirectory() ? 'directory' : 'file'
695
+ return statSync(outputPath).isDirectory() ? 'directory' : 'file'
527
696
  } catch {
528
697
  return 'file'
529
698
  }
530
699
  }
531
700
 
532
701
  protected isDirectoryOutputTarget(): boolean {
533
- return this.resolveOutputMode() === 'directory'
702
+ return this.resolveOutputMode(this.outputPath) === 'directory'
703
+ }
704
+
705
+ protected override getEffectiveOutputMode(
706
+ rawValues: Record<string, unknown>,
707
+ outputPath: string | undefined,
708
+ ): 'directory' | 'file' | undefined {
709
+ return this.resolveOutputMode(outputPath ?? this.getDefaultOutputPath(rawValues))
534
710
  }
535
711
 
536
712
  protected validateInputPresence(rawValues: Record<string, unknown>): number | undefined {
@@ -556,11 +732,6 @@ abstract class GeneratedFileIntentCommandBase extends GeneratedIntentCommandBase
556
732
  }
557
733
 
558
734
  protected validateBeforePreparingInputs(rawValues: Record<string, unknown>): number | undefined {
559
- const outputValidationError = this.validateOutputChoice()
560
- if (outputValidationError != null) {
561
- return outputValidationError
562
- }
563
-
564
735
  const validationError = this.validateInputPresence(rawValues)
565
736
  if (validationError != null) {
566
737
  return validationError
@@ -591,12 +762,14 @@ abstract class GeneratedFileIntentCommandBase extends GeneratedIntentCommandBase
591
762
  }
592
763
  }
593
764
 
765
+ const effectiveOutputPath = this.getEffectiveOutputPath(rawValues)
594
766
  return await executeIntentCommand({
595
767
  client: this.client,
596
768
  createOptions: this.getCreateOptions(effectivePreparedInputs.inputs),
597
769
  definition: this.getIntentDefinition(),
598
770
  output: this.output,
599
- outputPath: this.outputPath,
771
+ outputMode: this.getEffectiveOutputMode(rawValues, effectiveOutputPath),
772
+ outputPath: effectiveOutputPath,
600
773
  printUrls: this.printUrls ?? false,
601
774
  rawValues,
602
775
  })
@@ -669,6 +842,14 @@ export abstract class GeneratedWatchableFileIntentCommand extends GeneratedFileI
669
842
  return false
670
843
  }
671
844
 
845
+ protected override prefersDirectoryDefaultOutput(): boolean {
846
+ return (
847
+ super.prefersDirectoryDefaultOutput() ||
848
+ this.getProvidedInputCount() > 1 ||
849
+ this.hasDirectoryInput()
850
+ )
851
+ }
852
+
672
853
  protected override validatePreparedInputs(
673
854
  preparedInputs: PreparedIntentInputs,
674
855
  ): number | undefined {
@@ -687,6 +868,13 @@ export abstract class GeneratedStandardFileIntentCommand extends GeneratedWatcha
687
868
  return this.singleAssembly
688
869
  }
689
870
 
871
+ protected override prefersDirectoryDefaultOutput(): boolean {
872
+ return (
873
+ super.prefersDirectoryDefaultOutput() ||
874
+ (this.singleAssembly && (this.getProvidedInputCount() > 1 || this.hasDirectoryInput()))
875
+ )
876
+ }
877
+
690
878
  protected override getCreateOptions(
691
879
  inputs: string[],
692
880
  ): Omit<AssembliesCreateOptions, 'output' | 'steps' | 'stepsData' | 'template'> {
@@ -706,14 +894,7 @@ export abstract class GeneratedStandardFileIntentCommand extends GeneratedWatcha
706
894
 
707
895
  if (
708
896
  this.singleAssembly &&
709
- (this.getProvidedInputCount() > 1 ||
710
- this.inputs.some((inputPath) => {
711
- try {
712
- return statSync(inputPath).isDirectory()
713
- } catch {
714
- return false
715
- }
716
- })) &&
897
+ (this.getProvidedInputCount() > 1 || this.hasDirectoryInput()) &&
717
898
  this.outputPath != null &&
718
899
  !this.isDirectoryOutputTarget()
719
900
  ) {
@@ -62,19 +62,19 @@ const imageDescribeExecutionDefinition = {
62
62
  const imageDescribeCommandPresentation = {
63
63
  description: 'Describe images as labels or publishable text fields',
64
64
  details:
65
- 'Generates image labels through `/image/describe`, or structured altText/title/caption/description through `/ai/chat`, then writes the JSON result to `--out`.',
65
+ 'Generates image labels through `/image/describe`, or structured altText/title/caption/description through `/ai/chat`, then writes the JSON result to `--output`.',
66
66
  examples: [
67
67
  [
68
68
  'Describe an image as labels',
69
- 'transloadit image describe --input hero.jpg --out labels.json',
69
+ 'transloadit image describe --input hero.jpg --output labels.json',
70
70
  ],
71
71
  [
72
72
  'Generate WordPress-ready fields',
73
- 'transloadit image describe --input hero.jpg --for wordpress --out fields.json',
73
+ 'transloadit image describe --input hero.jpg --for wordpress --output fields.json',
74
74
  ],
75
75
  [
76
76
  'Request a custom field set',
77
- 'transloadit image describe --input hero.jpg --fields altText,title,caption --out fields.json',
77
+ 'transloadit image describe --input hero.jpg --fields altText,title,caption --output fields.json',
78
78
  ],
79
79
  ] as Array<[string, string]>,
80
80
  } as const satisfies SemanticIntentPresentation
@@ -249,6 +249,7 @@ function createImageDescribeStep(
249
249
 
250
250
  export const imageDescribeSemanticIntentDescriptor = {
251
251
  createStep: createImageDescribeStep,
252
+ defaultOutputPath: 'output.json',
252
253
  execution: imageDescribeExecutionDefinition,
253
254
  inputPolicy: { kind: 'required' },
254
255
  outputDescription: 'Write the JSON result to this path or directory',
@@ -91,15 +91,15 @@ const imageGenerateCommandPresentation = {
91
91
  examples: [
92
92
  [
93
93
  'Generate an image from text',
94
- 'transloadit image generate --prompt "A red bicycle in a studio" --out output.png',
94
+ 'transloadit image generate --prompt "A red bicycle in a studio" --output output.png',
95
95
  ],
96
96
  [
97
97
  'Guide generation with one input image',
98
- 'transloadit image generate --input subject.jpg --prompt "Place subject.jpg on a magazine cover" --out output.png',
98
+ 'transloadit image generate --input subject.jpg --prompt "Place subject.jpg on a magazine cover" --output output.png',
99
99
  ],
100
100
  [
101
101
  'Guide generation with multiple input images',
102
- 'transloadit image generate --input person1.jpg --input person2.jpg --input background.jpg --prompt "Place person1.jpg feeding person2.jpg in front of background.jpg" --out output.png',
102
+ 'transloadit image generate --input person1.jpg --input person2.jpg --input background.jpg --prompt "Place person1.jpg feeding person2.jpg in front of background.jpg" --output output.png',
103
103
  ],
104
104
  ] as Array<[string, string]>,
105
105
  } as const satisfies SemanticIntentPresentation
@@ -234,6 +234,7 @@ function ensureUniqueInputBasenames(preparedInputs: PreparedIntentInputs): Prepa
234
234
 
235
235
  export const imageGenerateSemanticIntentDescriptor = {
236
236
  createStep: createImageGenerateStep,
237
+ defaultOutputPath: 'output.png',
237
238
  execution: {
238
239
  kind: 'dynamic-step',
239
240
  handler: 'image-generate',
@@ -22,6 +22,7 @@ export interface SemanticIntentDescriptor {
22
22
  rawValues: Record<string, unknown>,
23
23
  context: { hasInputs: boolean },
24
24
  ) => Record<string, unknown>
25
+ defaultOutputPath: string
25
26
  execution: IntentDynamicStepExecutionDefinition
26
27
  inputPolicy: IntentInputPolicy
27
28
  outputDescription: string
@@ -66,7 +66,7 @@ function createMarkdownConvertSemanticIntent({
66
66
  examples: [
67
67
  [
68
68
  `Render a Markdown file as a ${formatLabel} file`,
69
- `transloadit markdown ${format} --input README.md --out ${exampleOutput}`,
69
+ `transloadit markdown ${format} --input README.md --output ${exampleOutput}`,
70
70
  ],
71
71
  [
72
72
  'Print a temporary result URL without downloading locally',
@@ -94,6 +94,7 @@ function createMarkdownConvertSemanticIntent({
94
94
  resultStepName: 'convert',
95
95
  fields: markdownOptionDefinitions,
96
96
  },
97
+ defaultOutputPath: exampleOutput,
97
98
  inputPolicy: { kind: 'required' },
98
99
  outputDescription: `Write the rendered ${formatLabel} to this path or directory`,
99
100
  presentation,
package/src/cli.ts CHANGED
@@ -4,8 +4,7 @@ import { realpathSync } from 'node:fs'
4
4
  import path from 'node:path'
5
5
  import process from 'node:process'
6
6
  import { fileURLToPath } from 'node:url'
7
- import 'dotenv/config'
8
- import { createCli } from './cli/commands/index.ts'
7
+ import { loadProjectDotenvIntoProcessEnv } from './cli/helpers.ts'
9
8
  import { ensureError } from './cli/types.ts'
10
9
 
11
10
  const currentFile = realpathSync(fileURLToPath(import.meta.url))
@@ -26,6 +25,8 @@ export function shouldRunCli(invoked?: string): boolean {
26
25
  }
27
26
 
28
27
  export async function main(args = process.argv.slice(2)): Promise<void> {
28
+ loadProjectDotenvIntoProcessEnv()
29
+ const { createCli } = await import('./cli/commands/index.ts')
29
30
  const cli = createCli()
30
31
  const exitCode = await cli.run(args)
31
32
  if (exitCode !== 0) {