@output.ai/cli 0.7.13 → 0.7.15

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 (39) hide show
  1. package/dist/api/generated/api.d.ts +4 -2
  2. package/dist/api/generated/api.js +2 -2
  3. package/dist/assets/docker/docker-compose-dev.yml +7 -1
  4. package/dist/commands/init.js +1 -1
  5. package/dist/commands/workflow/generate.js +1 -1
  6. package/dist/commands/workflow/list.d.ts +1 -0
  7. package/dist/commands/workflow/list.js +10 -12
  8. package/dist/commands/workflow/{list.test.js → list.spec.js} +16 -0
  9. package/dist/commands/workflow/run.d.ts +2 -1
  10. package/dist/commands/workflow/run.js +10 -5
  11. package/dist/commands/workflow/{run.test.js → run.spec.js} +6 -1
  12. package/dist/commands/workflow/start.d.ts +2 -1
  13. package/dist/commands/workflow/start.js +9 -4
  14. package/dist/commands/workflow/{start.test.js → start.spec.js} +6 -1
  15. package/dist/generated/sdk_versions.json +3 -3
  16. package/dist/services/claude_client.js +1 -1
  17. package/dist/services/messages.js +5 -5
  18. package/dist/services/project_scaffold.js +2 -2
  19. package/dist/services/project_scaffold.spec.js +2 -2
  20. package/dist/templates/agent_instructions/dotoutputai/AGENTS.md.template +4 -4
  21. package/dist/templates/project/README.md.template +1 -1
  22. package/dist/templates/workflow/README.md.template +5 -5
  23. package/dist/types/trace.d.ts +1 -1
  24. package/dist/utils/resolve_input.d.ts +1 -0
  25. package/dist/utils/resolve_input.js +22 -0
  26. package/dist/utils/scenario_resolver.d.ts +9 -0
  27. package/dist/utils/scenario_resolver.js +92 -0
  28. package/dist/utils/scenario_resolver.spec.d.ts +1 -0
  29. package/dist/utils/scenario_resolver.spec.js +214 -0
  30. package/package.json +1 -1
  31. /package/dist/commands/workflow/{list.test.d.ts → list.spec.d.ts} +0 -0
  32. /package/dist/commands/workflow/{result.test.d.ts → result.spec.d.ts} +0 -0
  33. /package/dist/commands/workflow/{result.test.js → result.spec.js} +0 -0
  34. /package/dist/commands/workflow/{run.test.d.ts → run.spec.d.ts} +0 -0
  35. /package/dist/commands/workflow/{start.test.d.ts → start.spec.d.ts} +0 -0
  36. /package/dist/commands/workflow/{status.test.d.ts → status.spec.d.ts} +0 -0
  37. /package/dist/commands/workflow/{status.test.js → status.spec.js} +0 -0
  38. /package/dist/commands/workflow/{stop.test.d.ts → stop.spec.d.ts} +0 -0
  39. /package/dist/commands/workflow/{stop.test.js → stop.spec.js} +0 -0
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Generated by orval v7.20.0 🍺
3
3
  * Do not edit manually.
4
- * Output.ai SDK API
5
- * API for managing and executing Temporal workflows through Output SDK
4
+ * Output.ai API
5
+ * API for managing and executing Output.ai workflows
6
6
  * OpenAPI spec version: 1.0.0
7
7
  */
8
8
  import { type ApiRequestOptions } from '../http_client.js';
@@ -29,6 +29,8 @@ export interface Workflow {
29
29
  name: string;
30
30
  /** The description of the workflow */
31
31
  description?: string;
32
+ /** Absolute path to the workflow file */
33
+ path?: string;
32
34
  inputSchema?: JSONSchema;
33
35
  outputSchema?: JSONSchema;
34
36
  }
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Generated by orval v7.20.0 🍺
3
3
  * Do not edit manually.
4
- * Output.ai SDK API
5
- * API for managing and executing Temporal workflows through Output SDK
4
+ * Output.ai API
5
+ * API for managing and executing Output.ai workflows
6
6
  * OpenAPI spec version: 1.0.0
7
7
  */
8
8
  import { customFetchInstance } from '../http_client.js';
@@ -112,10 +112,16 @@ services:
112
112
  - TRACE_LOCAL_ON=${TRACE_LOCAL_ON:-true}
113
113
  - TRACE_REMOTE_ON=${TRACE_REMOTE_ON:-}
114
114
  - TRACE_REMOTE_S3_BUCKET=${TRACE_REMOTE_S3_BUCKET:-}
115
- - HOST_TRACE_PATH=${PWD}/logs
115
+ - TRACE_HOST_PATH=${PWD}/logs
116
116
  - AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID:-}
117
117
  - AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY:-}
118
118
  - AWS_REGION=${AWS_REGION:-us-west-1}
119
+ - MAX_CONCURRENT_ACTIVITY_TASKS=${MAX_CONCURRENT_ACTIVITY_TASKS:-40}
120
+ - MAX_CONCURRENT_WORKFLOW_TASKS=${MAX_CONCURRENT_WORKFLOW_TASKS:-200}
121
+ - MAX_CACHED_WORKFLOWS=${MAX_CACHED_WORKFLOWS:-1000}
122
+ - MAX_CONCURRENT_ACTIVITY_POLLS=${MAX_CONCURRENT_ACTIVITY_POLLS:-5}
123
+ - MAX_CONCURRENT_WORKFLOW_POLLS=${MAX_CONCURRENT_WORKFLOW_POLLS:-5}
124
+ - NODE_OPTIONS=${NODE_OPTIONS:---max-old-space-size=4096}
119
125
  command: >
120
126
  sh -c "
121
127
  npm run output:worker:install &&
@@ -2,7 +2,7 @@ import { Args, Command, Flags } from '@oclif/core';
2
2
  import { UserCancelledError } from '#types/errors.js';
3
3
  import { runInit } from '#services/project_scaffold.js';
4
4
  export default class Init extends Command {
5
- static description = 'Initialize a new Output SDK workflow project by scaffolding the complete project structure';
5
+ static description = 'Initialize a new Output project by scaffolding the complete project structure';
6
6
  static examples = [
7
7
  '<%= config.bin %> <%= command.id %>',
8
8
  '<%= config.bin %> <%= command.id %> my-workflow-project'
@@ -6,7 +6,7 @@ import { getWorkflowGenerateSuccessMessage } from '#services/messages.js';
6
6
  import { DEFAULT_OUTPUT_DIRS } from '#utils/paths.js';
7
7
  import path from 'node:path';
8
8
  export default class Generate extends Command {
9
- static description = 'Generate a new Output SDK workflow';
9
+ static description = 'Generate a new Output workflow';
10
10
  static examples = [
11
11
  '<%= config.bin %> <%= command.id %> my-workflow',
12
12
  '<%= config.bin %> <%= command.id %> my-workflow --skeleton',
@@ -5,6 +5,7 @@ interface WorkflowDisplay {
5
5
  description: string;
6
6
  inputs: string;
7
7
  outputs: string;
8
+ scenarios: string;
8
9
  }
9
10
  export declare function parseWorkflowForDisplay(workflow: Workflow): WorkflowDisplay;
10
11
  export default class WorkflowList extends Command {
@@ -3,6 +3,7 @@ import Table from 'cli-table3';
3
3
  import { getWorkflowCatalog } from '#api/generated/api.js';
4
4
  import { parseWorkflowDefinition, formatParameters } from '#api/parser.js';
5
5
  import { handleApiError } from '#utils/error_handler.js';
6
+ import { listScenariosForWorkflow } from '#utils/scenario_resolver.js';
6
7
  const OUTPUT_FORMAT = {
7
8
  LIST: 'list',
8
9
  TABLE: 'table',
@@ -10,11 +11,13 @@ const OUTPUT_FORMAT = {
10
11
  };
11
12
  export function parseWorkflowForDisplay(workflow) {
12
13
  const parsed = parseWorkflowDefinition(workflow);
14
+ const scenarioNames = listScenariosForWorkflow(workflow.name, workflow.path);
13
15
  return {
14
16
  name: parsed.name,
15
17
  description: parsed.description || 'No description',
16
18
  inputs: formatParameters(parsed.inputs),
17
- outputs: formatParameters(parsed.outputs)
19
+ outputs: formatParameters(parsed.outputs),
20
+ scenarios: scenarioNames.length > 0 ? scenarioNames.join(', ') : 'none'
18
21
  };
19
22
  }
20
23
  function caseInsensitiveIncludes(str, filter) {
@@ -35,8 +38,8 @@ function sortWorkflowsByName(workflows) {
35
38
  }
36
39
  function createWorkflowTable(workflows, detailed) {
37
40
  const table = new Table({
38
- head: ['Name', 'Description', 'Inputs', 'Outputs'],
39
- colWidths: detailed ? [25, 40, 40, 40] : [20, 30, 25, 25],
41
+ head: ['Name', 'Description', 'Inputs', 'Outputs', 'Scenarios'],
42
+ colWidths: detailed ? [30, 42, 42, 42, 60] : [24, 30, 24, 24, 48],
40
43
  wordWrap: true,
41
44
  style: {
42
45
  head: ['cyan']
@@ -48,17 +51,11 @@ function createWorkflowTable(workflows, detailed) {
48
51
  if (detailed) {
49
52
  const inputs = display.inputs.split(', ').join('\n');
50
53
  const outputs = display.outputs.split(', ').join('\n');
51
- table.push([display.name, display.description, inputs, outputs]);
54
+ const scenarios = display.scenarios.split(', ').join('\n');
55
+ table.push([display.name, display.description, inputs, outputs, scenarios]);
52
56
  }
53
57
  else {
54
- const maxLen = 22;
55
- const inputs = display.inputs.length > maxLen ?
56
- display.inputs.substring(0, maxLen) + '...' :
57
- display.inputs;
58
- const outputs = display.outputs.length > maxLen ?
59
- display.outputs.substring(0, maxLen) + '...' :
60
- display.outputs;
61
- table.push([display.name, display.description, inputs, outputs]);
58
+ table.push([display.name, display.description, display.inputs, display.outputs, display.scenarios]);
62
59
  }
63
60
  });
64
61
  return table.toString();
@@ -77,6 +74,7 @@ function formatWorkflowsAsJson(workflows) {
77
74
  description: display.description,
78
75
  inputs: display.inputs.split(', '),
79
76
  outputs: display.outputs.split(', '),
77
+ scenarios: display.scenarios === 'none' ? [] : display.scenarios.split(', '),
80
78
  raw: w
81
79
  };
82
80
  })
@@ -1,4 +1,8 @@
1
1
  import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ const mockListScenarios = vi.fn().mockReturnValue([]);
3
+ vi.mock('#utils/scenario_resolver.js', () => ({
4
+ listScenariosForWorkflow: mockListScenarios
5
+ }));
2
6
  describe('workflow list command', () => {
3
7
  beforeEach(() => {
4
8
  vi.clearAllMocks();
@@ -47,6 +51,7 @@ describe('workflow list parsing', () => {
47
51
  expect(parsed.inputs).toContain('message: string');
48
52
  expect(parsed.inputs).toContain('count: number?');
49
53
  expect(parsed.outputs).toContain('result: string?');
54
+ expect(parsed.scenarios).toBe('none');
50
55
  });
51
56
  it('should handle workflows without schemas', async () => {
52
57
  const { parseWorkflowForDisplay } = await import('./list.js');
@@ -58,6 +63,17 @@ describe('workflow list parsing', () => {
58
63
  expect(parsed.name).toBe('simple-workflow');
59
64
  expect(parsed.inputs).toBe('none');
60
65
  expect(parsed.outputs).toBe('none');
66
+ expect(parsed.scenarios).toBe('none');
67
+ });
68
+ it('should include scenario names when scenarios exist', async () => {
69
+ mockListScenarios.mockReturnValueOnce(['basic', 'advanced', 'stress_test']);
70
+ const { parseWorkflowForDisplay } = await import('./list.js');
71
+ const mockWorkflow = {
72
+ name: 'workflow-with-scenarios',
73
+ description: 'Has scenarios'
74
+ };
75
+ const parsed = parseWorkflowForDisplay(mockWorkflow);
76
+ expect(parsed.scenarios).toBe('basic, advanced, stress_test');
61
77
  });
62
78
  it('should format nested parameters correctly', async () => {
63
79
  const { parseWorkflowForDisplay } = await import('./list.js');
@@ -4,9 +4,10 @@ export default class WorkflowRun extends Command {
4
4
  static examples: string[];
5
5
  static args: {
6
6
  workflowName: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
7
+ scenario: import("@oclif/core/lib/interfaces").Arg<string | undefined, Record<string, unknown>>;
7
8
  };
8
9
  static flags: {
9
- input: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
10
+ input: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
10
11
  'task-queue': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
11
12
  format: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
12
13
  };
@@ -1,28 +1,33 @@
1
1
  import { Args, Command, Flags } from '@oclif/core';
2
2
  import { postWorkflowRun } from '#api/generated/api.js';
3
3
  import { OUTPUT_FORMAT } from '#utils/constants.js';
4
- import { parseInputFlag } from '#utils/input_parser.js';
5
4
  import { formatOutput } from '#utils/output_formatter.js';
6
5
  import { handleApiError } from '#utils/error_handler.js';
6
+ import { resolveInput } from '#utils/resolve_input.js';
7
7
  export default class WorkflowRun extends Command {
8
8
  static description = 'Execute a workflow synchronously and wait for completion';
9
9
  static examples = [
10
+ '<%= config.bin %> <%= command.id %> simple basic_input',
11
+ '<%= config.bin %> <%= command.id %> simple my_scenario --format json',
10
12
  '<%= config.bin %> <%= command.id %> simple --input \'{"values":[1,2,3]}\'',
11
13
  '<%= config.bin %> <%= command.id %> simple --input input.json',
12
- '<%= config.bin %> <%= command.id %> simple --input \'{"key":"value"}\' --format json',
13
14
  '<%= config.bin %> <%= command.id %> simple --input \'{"key":"value"}\' --task-queue my-queue'
14
15
  ];
15
16
  static args = {
16
17
  workflowName: Args.string({
17
18
  description: 'Name of the workflow to execute',
18
19
  required: true
20
+ }),
21
+ scenario: Args.string({
22
+ description: 'Scenario name (resolved from the workflow\'s scenarios/ directory)',
23
+ required: false
19
24
  })
20
25
  };
21
26
  static flags = {
22
27
  input: Flags.string({
23
28
  char: 'i',
24
- description: 'Workflow input as JSON string or file path',
25
- required: true
29
+ description: 'Workflow input as JSON string or file path (overrides scenario)',
30
+ required: false
26
31
  }),
27
32
  'task-queue': Flags.string({
28
33
  char: 'q',
@@ -37,7 +42,7 @@ export default class WorkflowRun extends Command {
37
42
  };
38
43
  async run() {
39
44
  const { args, flags } = await this.parse(WorkflowRun);
40
- const input = parseInputFlag(flags.input);
45
+ const input = await resolveInput(args.workflowName, args.scenario, flags.input, 'run');
41
46
  this.log(`Executing workflow: ${args.workflowName}...`);
42
47
  const response = await postWorkflowRun({
43
48
  workflowName: args.workflowName,
@@ -20,7 +20,12 @@ describe('workflow run command', () => {
20
20
  const WorkflowRun = (await import('./run.js')).default;
21
21
  expect(WorkflowRun.flags.format.options).toEqual(['json', 'text']);
22
22
  expect(WorkflowRun.flags.format.default).toBe('text');
23
- expect(WorkflowRun.flags.input.required).toBe(true);
23
+ expect(WorkflowRun.flags.input.required).toBe(false);
24
+ });
25
+ it('should have optional scenario argument', async () => {
26
+ const WorkflowRun = (await import('./run.js')).default;
27
+ expect(WorkflowRun.args).toHaveProperty('scenario');
28
+ expect(WorkflowRun.args.scenario.required).toBe(false);
24
29
  });
25
30
  });
26
31
  });
@@ -4,9 +4,10 @@ export default class WorkflowStart extends Command {
4
4
  static examples: string[];
5
5
  static args: {
6
6
  workflowName: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
7
+ scenario: import("@oclif/core/lib/interfaces").Arg<string | undefined, Record<string, unknown>>;
7
8
  };
8
9
  static flags: {
9
- input: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
10
+ input: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
10
11
  'task-queue': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
11
12
  };
12
13
  run(): Promise<void>;
@@ -1,10 +1,11 @@
1
1
  import { Args, Command, Flags } from '@oclif/core';
2
2
  import { postWorkflowStart } from '#api/generated/api.js';
3
- import { parseInputFlag } from '#utils/input_parser.js';
4
3
  import { handleApiError } from '#utils/error_handler.js';
4
+ import { resolveInput } from '#utils/resolve_input.js';
5
5
  export default class WorkflowStart extends Command {
6
6
  static description = 'Start a workflow asynchronously without waiting for completion';
7
7
  static examples = [
8
+ '<%= config.bin %> <%= command.id %> simple basic_input',
8
9
  '<%= config.bin %> <%= command.id %> simple --input \'{"values":[1,2,3]}\'',
9
10
  '<%= config.bin %> <%= command.id %> simple --input input.json',
10
11
  '<%= config.bin %> <%= command.id %> simple --input \'{"key":"value"}\' --task-queue my-queue'
@@ -13,13 +14,17 @@ export default class WorkflowStart extends Command {
13
14
  workflowName: Args.string({
14
15
  description: 'Name of the workflow to start',
15
16
  required: true
17
+ }),
18
+ scenario: Args.string({
19
+ description: 'Scenario name (resolved from the workflow\'s scenarios/ directory)',
20
+ required: false
16
21
  })
17
22
  };
18
23
  static flags = {
19
24
  input: Flags.string({
20
25
  char: 'i',
21
- description: 'Workflow input as JSON string or file path',
22
- required: true
26
+ description: 'Workflow input as JSON string or file path (overrides scenario)',
27
+ required: false
23
28
  }),
24
29
  'task-queue': Flags.string({
25
30
  char: 'q',
@@ -28,7 +33,7 @@ export default class WorkflowStart extends Command {
28
33
  };
29
34
  async run() {
30
35
  const { args, flags } = await this.parse(WorkflowStart);
31
- const input = parseInputFlag(flags.input);
36
+ const input = await resolveInput(args.workflowName, args.scenario, flags.input, 'start');
32
37
  this.log(`Starting workflow: ${args.workflowName}...`);
33
38
  const response = await postWorkflowStart({
34
39
  workflowName: args.workflowName,
@@ -17,7 +17,12 @@ describe('workflow start command', () => {
17
17
  });
18
18
  it('should have correct flag configuration', async () => {
19
19
  const WorkflowStart = (await import('./start.js')).default;
20
- expect(WorkflowStart.flags.input.required).toBe(true);
20
+ expect(WorkflowStart.flags.input.required).toBe(false);
21
+ });
22
+ it('should have optional scenario argument', async () => {
23
+ const WorkflowStart = (await import('./start.js')).default;
24
+ expect(WorkflowStart.args).toHaveProperty('scenario');
25
+ expect(WorkflowStart.args.scenario.required).toBe(false);
21
26
  });
22
27
  });
23
28
  });
@@ -1,6 +1,6 @@
1
1
  {
2
- "core": "0.3.2",
3
- "llm": "0.2.10",
2
+ "core": "0.3.6",
3
+ "llm": "0.2.12",
4
4
  "http": "0.1.2",
5
- "cli": "0.7.13"
5
+ "cli": "0.7.15"
6
6
  }
@@ -29,7 +29,7 @@ const ADDITIONAL_INSTRUCTIONS_BUILD = `
29
29
 
30
30
  2. Follow the implementation plan exactly as specified in the plan file.
31
31
 
32
- 3. Implement all workflow files following Output SDK patterns and best practices.
32
+ 3. Implement all workflow files following Output.ai patterns and best practices.
33
33
 
34
34
  4. After you mark all todos as complete, provide a summary of what was implemented.
35
35
  `;
@@ -177,7 +177,7 @@ export const getProjectSuccessMessage = (folderName, installSuccess, envConfigur
177
177
  note: 'Launches Temporal, Redis, PostgreSQL, API, Worker, and UI'
178
178
  }, {
179
179
  step: 'Run example workflow',
180
- command: 'npx output workflow run blog_evaluator --input src/workflows/blog_evaluator/scenarios/paulgraham_hwh.json',
180
+ command: 'npx output workflow run blog_evaluator paulgraham_hwh',
181
181
  note: 'Execute in a new terminal after services are running'
182
182
  }, {
183
183
  step: 'Monitor workflows',
@@ -199,14 +199,14 @@ ${createOutputBanner()}
199
199
 
200
200
  ${divider}
201
201
 
202
- ${ux.colorize('bold', ux.colorize('green', '🎉 SUCCESS!'))} ${ux.colorize('bold', 'Your Output SDK project has been created')}
202
+ ${ux.colorize('bold', ux.colorize('green', '🎉 SUCCESS!'))} ${ux.colorize('bold', 'Your Output project has been created')}
203
203
 
204
204
  ${divider}
205
205
 
206
206
  ${createSectionHeader('PROJECT DETAILS', '📁')}
207
207
 
208
208
  ${bulletPoint} ${ux.colorize('white', 'Name:')} ${formatPath(folderName)}
209
- ${bulletPoint} ${ux.colorize('white', 'Type:')} Output SDK Workflow Project
209
+ ${bulletPoint} ${ux.colorize('white', 'Type:')} Output Project
210
210
  ${bulletPoint} ${ux.colorize('white', 'Structure:')} ${formatPath('.outputai/')} (agents), ${formatPath('workflows/')} (implementations)
211
211
 
212
212
  ${divider}
@@ -229,7 +229,7 @@ ${divider}
229
229
  ${ux.colorize('dim', '💡 Tip: Use ')}${formatCommand('npx output workflow plan')}${ux.colorize('dim', ' to design your first custom workflow')}
230
230
  ${ux.colorize('dim', ' with AI assistance.')}
231
231
 
232
- ${ux.colorize('green', ux.colorize('bold', 'Happy building with Output SDK! 🚀'))}
232
+ ${ux.colorize('green', ux.colorize('bold', 'Happy building with Output! 🚀'))}
233
233
  `;
234
234
  };
235
235
  export const getWorkflowGenerateSuccessMessage = (workflowName, targetDir, filesCreated) => {
@@ -321,7 +321,7 @@ ${createSectionHeader('RUN A WORKFLOW', '🚀')}
321
321
 
322
322
  ${ux.colorize('white', 'In a new terminal, execute:')}
323
323
 
324
- ${formatCommand('npx output workflow run blog_evaluator --input src/workflows/blog_evaluator/scenarios/paulgraham_hwh.json')}
324
+ ${formatCommand('npx output workflow run blog_evaluator paulgraham_hwh')}
325
325
 
326
326
  ${divider}
327
327
 
@@ -62,7 +62,7 @@ const promptForProjectName = async (defaultProjectName) => {
62
62
  }) || defaultProjectName;
63
63
  };
64
64
  const generateProjectDescription = (projectName) => {
65
- return `An Output SDK workflow for ${kebabCase(projectName)}`;
65
+ return `AI Agents & Workflows built with Output.ai for ${kebabCase(projectName)}`;
66
66
  };
67
67
  /**
68
68
  * Get project configuration from user input
@@ -98,7 +98,7 @@ async function scaffoldProjectFiles(projectPath, projectName, description) {
98
98
  const templateVars = {
99
99
  projectName: kebabCase(projectName),
100
100
  ProjectName: pascalCase(projectName),
101
- description: description || `An Output SDK workflow for ${projectName}`,
101
+ description: description || `An Output.ai workflow for ${projectName}`,
102
102
  coreVersion: sdkVersions.core,
103
103
  llmVersion: sdkVersions.llm,
104
104
  httpVersion: sdkVersions.http,
@@ -48,7 +48,7 @@ describe('project_scaffold', () => {
48
48
  });
49
49
  it('should auto-generate description when folderName provided', async () => {
50
50
  const config = await getProjectConfig('test-folder');
51
- expect(config.description).toBe('An Output SDK workflow for test-folder');
51
+ expect(config.description).toBe('AI Agents & Workflows built with Output.ai for test-folder');
52
52
  });
53
53
  it('should prompt for project name and folder name when not provided', async () => {
54
54
  const { input } = await import('@inquirer/prompts');
@@ -58,7 +58,7 @@ describe('project_scaffold', () => {
58
58
  const config = await getProjectConfig();
59
59
  expect(config.projectName).toBe('Test Project');
60
60
  expect(config.folderName).toBe('test-project');
61
- expect(config.description).toBe('An Output SDK workflow for test-project');
61
+ expect(config.description).toBe('AI Agents & Workflows built with Output.ai for test-project');
62
62
  expect(input).toHaveBeenCalledTimes(2);
63
63
  });
64
64
  });
@@ -2,17 +2,17 @@
2
2
 
3
3
  ## Overview
4
4
 
5
- This project uses Output.ai Framework to build durable, LLM-powered workflows orchestrated by Temporal. Output Framework provides abstractions for creating reliable AI workflows with automatic retry, tracing, and error handling. Developers use it to build workflows like fact checkers, content generators, data extractors, research assistants, and multi-step AI agents mixing API clients.
5
+ This project uses Output.ai Framework to build production-ready AI applications with built-in prompt management, evaluation, observability, and error handling. Developers use it to build AI features like fact checkers, content generators, data extractors, research assistants, and multi-step agents.
6
6
 
7
7
  ### Project Overview
8
8
 
9
- Each workflow lives in its own folder under `src/workflows/` and follows a consistent structure. Workflows define the orchestration logic, calling steps to perform external operations like API calls, database queries, and LLM inference. The system automatically handles retries, timeouts, and distributed execution through Temporal.
9
+ Each workflow lives in its own folder under `src/workflows/` and follows a consistent structure. Workflows define the orchestration logic, calling steps to perform external operations like API calls, database queries, and LLM inference. The framework automatically handles retries, timeouts, and reliable execution.
10
10
 
11
11
  ### Key Concepts
12
12
 
13
- #### Built on Top of Temporal
13
+ #### Durable Execution powered by Temporal.io
14
14
 
15
- Temporal provides durable execution guarantees - if a workflow fails mid-execution, it resumes from the last successful step rather than restarting. Output Framework wraps Temporal's workflow and activity primitives with higher-level abstractions (`workflow`, `step`, `evaluator`) that enforce best practices and provide automatic tracing.
15
+ Output provides durable execution guarantees (built on Temporal.io) - if execution fails mid-run, it resumes from the last successful step rather than restarting. The framework provides high-level abstractions (`workflow`, `step`, `evaluator`) that enforce best practices and provide automatic tracing.
16
16
 
17
17
  #### Single Folder Project Structure
18
18
 
@@ -81,7 +81,7 @@ This starts:
81
81
  In a new terminal:
82
82
 
83
83
  ```bash
84
- npx output workflow run blog_evaluator --input src/workflows/blog_evaluator/scenarios/paulgraham_hwh.json
84
+ npx output workflow run blog_evaluator paulgraham_hwh
85
85
  ```
86
86
 
87
87
  ### 5. Stop Services
@@ -4,7 +4,7 @@
4
4
 
5
5
  ## Overview
6
6
 
7
- This workflow was generated using the Output SDK CLI. It provides a starting point for building Temporal-based workflows with LLM integration.
7
+ This was generated using the Output.ai CLI. It provides a starting point for building AI applications with production-ready LLM integration, built-in prompt management, evaluation, and observability.
8
8
 
9
9
  ## Files
10
10
 
@@ -41,7 +41,7 @@ workflow/
41
41
 
42
42
  ## Import Rules
43
43
 
44
- **Important:** Steps and evaluators are Temporal activities. Activities cannot call other activities.
44
+ **Important:** Steps and evaluators are isolated execution units that cannot call each other.
45
45
 
46
46
  **Steps can import from:**
47
47
  - Local utilities (`./utils.ts`, `./utils/*.ts`)
@@ -105,7 +105,7 @@ Example:
105
105
 
106
106
  ### Workflow Structure
107
107
 
108
- The workflow follows the Output SDK conventions:
108
+ The workflow follows the Output.ai conventions:
109
109
 
110
110
  ```typescript
111
111
  import { workflow, z } from '@output.ai/core';
@@ -221,7 +221,7 @@ To test your workflow:
221
221
 
222
222
  1. Build the parent project containing this workflow
223
223
  2. Start the Output worker
224
- 3. Execute the workflow using the Output SDK API
224
+ 3. Execute the workflow using the Output.ai API
225
225
 
226
226
  Example execution:
227
227
  ```bash
@@ -230,5 +230,5 @@ npx output workflow run {{workflowName}} --input '{"prompt": "Hello"}'
230
230
 
231
231
  ## Resources
232
232
 
233
- - [Output SDK Documentation](https://docs.output.ai)
233
+ - [Output.ai Documentation](https://docs.output.ai)
234
234
  - [Temporal Documentation](https://docs.temporal.io)
@@ -107,7 +107,7 @@ export interface TraceStructure {
107
107
  children?: DebugNode[];
108
108
  }
109
109
  /**
110
- * The structure of a workflow trace file generated by Output SDK workflow runs.
110
+ * The structure of a workflow trace file generated by Output.ai workflow runs.
111
111
  * This file is written to the local filesystem during workflow execution and contains
112
112
  * the complete execution history including timing, inputs, outputs, and any errors.
113
113
  */
@@ -0,0 +1 @@
1
+ export declare function resolveInput(workflowName: string, scenario: string | undefined, inputFlag: string | undefined, commandName: string): Promise<unknown>;
@@ -0,0 +1,22 @@
1
+ import { ux } from '@oclif/core';
2
+ import { parseInputFlag } from '#utils/input_parser.js';
3
+ import { resolveScenarioPath, getScenarioNotFoundMessage } from '#utils/scenario_resolver.js';
4
+ export async function resolveInput(workflowName, scenario, inputFlag, commandName) {
5
+ if (inputFlag && scenario) {
6
+ return ux.error('Cannot use both scenario argument and --input flag. Choose one.', { exit: 1 });
7
+ }
8
+ if (inputFlag) {
9
+ return parseInputFlag(inputFlag);
10
+ }
11
+ if (scenario) {
12
+ const resolution = await resolveScenarioPath(workflowName, scenario);
13
+ if (!resolution.found) {
14
+ return ux.error(getScenarioNotFoundMessage(workflowName, scenario, resolution.searchedPaths), { exit: 1 });
15
+ }
16
+ ux.stdout(`Using scenario: ${resolution.path}\n`);
17
+ return parseInputFlag(resolution.path);
18
+ }
19
+ return ux.error('Input required. Provide either:\n' +
20
+ ` - A scenario name: output workflow ${commandName} <workflow> <scenario>\n` +
21
+ ` - An input flag: output workflow ${commandName} <workflow> --input <json-or-file>`, { exit: 1 });
22
+ }
@@ -0,0 +1,9 @@
1
+ export interface ScenarioResolutionResult {
2
+ found: boolean;
3
+ path?: string;
4
+ searchedPaths: string[];
5
+ }
6
+ export declare function extractWorkflowRelativePath(path: string): string | null;
7
+ export declare function resolveScenarioPath(workflowName: string, scenarioName: string, basePath?: string): Promise<ScenarioResolutionResult>;
8
+ export declare function listScenariosForWorkflow(workflowName: string, workflowPath?: string, basePath?: string): string[];
9
+ export declare function getScenarioNotFoundMessage(workflowName: string, scenarioName: string, searchedPaths: string[]): string;
@@ -0,0 +1,92 @@
1
+ import { existsSync, readdirSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
3
+ import { getWorkflowCatalog } from '#api/generated/api.js';
4
+ const SCENARIOS_DIR = 'scenarios';
5
+ const WORKFLOWS_PATHS = ['src/workflows', 'workflows'];
6
+ export function extractWorkflowRelativePath(path) {
7
+ const match = path.match(/workflows\/(.+)\/workflow\.[jt]s$/);
8
+ return match ? match[1] : null;
9
+ }
10
+ async function fetchWorkflowDirectory(workflowName) {
11
+ try {
12
+ const response = await getWorkflowCatalog();
13
+ const workflows = response?.data?.workflows;
14
+ if (!workflows) {
15
+ return null;
16
+ }
17
+ const workflow = workflows.find(w => w.name === workflowName);
18
+ if (!workflow) {
19
+ return null;
20
+ }
21
+ const workflowPath = workflow.path;
22
+ if (!workflowPath) {
23
+ return null;
24
+ }
25
+ return extractWorkflowRelativePath(workflowPath);
26
+ }
27
+ catch {
28
+ return null;
29
+ }
30
+ }
31
+ function resolveScenarioFromDirectory(relativeDir, scenarioFileName, basePath) {
32
+ const searchedPaths = [];
33
+ for (const workflowsDir of WORKFLOWS_PATHS) {
34
+ const candidatePath = resolve(basePath, workflowsDir, relativeDir, SCENARIOS_DIR, scenarioFileName);
35
+ searchedPaths.push(candidatePath);
36
+ if (existsSync(candidatePath)) {
37
+ return { found: true, path: candidatePath, searchedPaths };
38
+ }
39
+ }
40
+ return { found: false, searchedPaths };
41
+ }
42
+ export async function resolveScenarioPath(workflowName, scenarioName, basePath = process.cwd()) {
43
+ const scenarioFileName = scenarioName.endsWith('.json') ?
44
+ scenarioName :
45
+ `${scenarioName}.json`;
46
+ const catalogDir = await fetchWorkflowDirectory(workflowName);
47
+ if (catalogDir) {
48
+ const result = resolveScenarioFromDirectory(catalogDir, scenarioFileName, basePath);
49
+ if (result.found) {
50
+ return result;
51
+ }
52
+ // Catalog resolved but scenario not found at that path — still try convention fallback
53
+ // in case the catalog path differs from local source layout
54
+ if (catalogDir !== workflowName) {
55
+ const fallback = resolveScenarioFromDirectory(workflowName, scenarioFileName, basePath);
56
+ return {
57
+ found: fallback.found,
58
+ path: fallback.path,
59
+ searchedPaths: [...result.searchedPaths, ...fallback.searchedPaths]
60
+ };
61
+ }
62
+ return result;
63
+ }
64
+ // API unavailable or workflow not in catalog — fall back to convention
65
+ return resolveScenarioFromDirectory(workflowName, scenarioFileName, basePath);
66
+ }
67
+ export function listScenariosForWorkflow(workflowName, workflowPath, basePath = process.cwd()) {
68
+ const relativeDir = (workflowPath && extractWorkflowRelativePath(workflowPath)) || workflowName;
69
+ for (const workflowsDir of WORKFLOWS_PATHS) {
70
+ const scenariosDir = resolve(basePath, workflowsDir, relativeDir, SCENARIOS_DIR);
71
+ if (existsSync(scenariosDir)) {
72
+ return readdirSync(scenariosDir)
73
+ .filter(f => f.endsWith('.json'))
74
+ .map(f => f.replace(/\.json$/, ''));
75
+ }
76
+ }
77
+ return [];
78
+ }
79
+ export function getScenarioNotFoundMessage(workflowName, scenarioName, searchedPaths) {
80
+ const pathsList = searchedPaths.map(p => ` - ${p}`).join('\n');
81
+ return [
82
+ `Scenario '${scenarioName}' not found for workflow '${workflowName}'.`,
83
+ '',
84
+ 'Searched in:',
85
+ pathsList,
86
+ '',
87
+ 'Tip: Create a scenario file in your workflow\'s scenarios/ directory.',
88
+ '',
89
+ 'Or use --input to specify a custom path:',
90
+ ` output workflow run ${workflowName} --input /path/to/input.json`
91
+ ].join('\n');
92
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,214 @@
1
+ import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest';
2
+ import { resolveScenarioPath, getScenarioNotFoundMessage, extractWorkflowRelativePath, listScenariosForWorkflow } from './scenario_resolver.js';
3
+ import * as fs from 'node:fs';
4
+ import * as api from '#api/generated/api.js';
5
+ vi.mock('node:fs', () => ({
6
+ existsSync: vi.fn(),
7
+ readdirSync: vi.fn()
8
+ }));
9
+ vi.mock('#api/generated/api.js', () => ({
10
+ getWorkflowCatalog: vi.fn()
11
+ }));
12
+ function mockCatalog(workflows) {
13
+ vi.mocked(api.getWorkflowCatalog).mockResolvedValue({
14
+ data: { workflows },
15
+ status: 200,
16
+ headers: new Headers()
17
+ });
18
+ }
19
+ function mockCatalogFailure() {
20
+ vi.mocked(api.getWorkflowCatalog).mockRejectedValue(new Error('API unavailable'));
21
+ }
22
+ describe('extractWorkflowRelativePath', () => {
23
+ it('should extract relative path from workflow.js path', () => {
24
+ expect(extractWorkflowRelativePath('/app/dist/workflows/basic_research/workflow.js'))
25
+ .toBe('basic_research');
26
+ });
27
+ it('should extract nested relative path', () => {
28
+ expect(extractWorkflowRelativePath('/app/dist/workflows/viz_examples/01_simple_linear/workflow.js'))
29
+ .toBe('viz_examples/01_simple_linear');
30
+ });
31
+ it('should handle workflow.ts extension', () => {
32
+ expect(extractWorkflowRelativePath('/src/workflows/my_flow/workflow.ts'))
33
+ .toBe('my_flow');
34
+ });
35
+ it('should return null for non-matching paths', () => {
36
+ expect(extractWorkflowRelativePath('/app/dist/other/workflow.js')).toBeNull();
37
+ expect(extractWorkflowRelativePath('/app/dist/workflows/')).toBeNull();
38
+ });
39
+ });
40
+ describe('resolveScenarioPath', () => {
41
+ beforeEach(() => {
42
+ vi.resetAllMocks();
43
+ });
44
+ afterEach(() => {
45
+ vi.restoreAllMocks();
46
+ });
47
+ describe('when API returns workflow with path', () => {
48
+ it('should resolve scenario using catalog path', async () => {
49
+ mockCatalog([{ name: 'my_workflow', path: '/app/dist/workflows/my_workflow/workflow.js' }]);
50
+ vi.mocked(fs.existsSync).mockImplementation(path => {
51
+ return String(path).includes('src/workflows/my_workflow/scenarios/test_scenario.json');
52
+ });
53
+ const result = await resolveScenarioPath('my_workflow', 'test_scenario', '/project');
54
+ expect(result.found).toBe(true);
55
+ expect(result.path).toContain('src/workflows/my_workflow/scenarios/test_scenario.json');
56
+ });
57
+ it('should resolve when workflow name differs from folder name', async () => {
58
+ mockCatalog([{ name: 'simpleLinear', path: '/app/dist/workflows/viz_examples/01_simple_linear/workflow.js' }]);
59
+ vi.mocked(fs.existsSync).mockImplementation(path => {
60
+ return String(path).includes('src/workflows/viz_examples/01_simple_linear/scenarios/basic.json');
61
+ });
62
+ const result = await resolveScenarioPath('simpleLinear', 'basic', '/project');
63
+ expect(result.found).toBe(true);
64
+ expect(result.path).toContain('src/workflows/viz_examples/01_simple_linear/scenarios/basic.json');
65
+ });
66
+ it('should handle nested workflow directories', async () => {
67
+ mockCatalog([{ name: 'deep_flow', path: '/app/dist/workflows/category/sub/deep_flow/workflow.js' }]);
68
+ vi.mocked(fs.existsSync).mockImplementation(path => {
69
+ return String(path).includes('src/workflows/category/sub/deep_flow/scenarios/test.json');
70
+ });
71
+ const result = await resolveScenarioPath('deep_flow', 'test', '/project');
72
+ expect(result.found).toBe(true);
73
+ expect(result.path).toContain('src/workflows/category/sub/deep_flow/scenarios/test.json');
74
+ });
75
+ it('should return not found when scenario file does not exist', async () => {
76
+ mockCatalog([{ name: 'my_workflow', path: '/app/dist/workflows/my_workflow/workflow.js' }]);
77
+ vi.mocked(fs.existsSync).mockReturnValue(false);
78
+ const result = await resolveScenarioPath('my_workflow', 'missing', '/project');
79
+ expect(result.found).toBe(false);
80
+ expect(result.searchedPaths.length).toBeGreaterThanOrEqual(2);
81
+ });
82
+ it('should fall back to convention when catalog path has no scenario but convention does', async () => {
83
+ mockCatalog([{ name: 'renamedFlow', path: '/app/dist/workflows/actual_folder/workflow.js' }]);
84
+ vi.mocked(fs.existsSync).mockImplementation(path => {
85
+ // Not found at catalog-resolved path, but found at convention path
86
+ return String(path).includes('src/workflows/renamedFlow/scenarios/test.json');
87
+ });
88
+ const result = await resolveScenarioPath('renamedFlow', 'test', '/project');
89
+ expect(result.found).toBe(true);
90
+ expect(result.path).toContain('src/workflows/renamedFlow/scenarios/test.json');
91
+ });
92
+ });
93
+ describe('when API is unavailable', () => {
94
+ it('should fall back to convention-based lookup', async () => {
95
+ mockCatalogFailure();
96
+ vi.mocked(fs.existsSync).mockImplementation(path => {
97
+ return String(path).includes('src/workflows/my_workflow/scenarios/test_scenario.json');
98
+ });
99
+ const result = await resolveScenarioPath('my_workflow', 'test_scenario', '/project');
100
+ expect(result.found).toBe(true);
101
+ expect(result.path).toContain('src/workflows/my_workflow/scenarios/test_scenario.json');
102
+ });
103
+ });
104
+ describe('when workflow is not in catalog', () => {
105
+ it('should fall back to convention-based lookup', async () => {
106
+ mockCatalog([{ name: 'other_workflow', path: '/app/dist/workflows/other/workflow.js' }]);
107
+ vi.mocked(fs.existsSync).mockImplementation(path => {
108
+ return String(path).includes('src/workflows/my_workflow/scenarios/test.json');
109
+ });
110
+ const result = await resolveScenarioPath('my_workflow', 'test', '/project');
111
+ expect(result.found).toBe(true);
112
+ expect(result.path).toContain('src/workflows/my_workflow/scenarios/test.json');
113
+ });
114
+ });
115
+ describe('json extension normalization', () => {
116
+ it('should handle scenario name with .json extension', async () => {
117
+ mockCatalogFailure();
118
+ vi.mocked(fs.existsSync).mockImplementation(path => {
119
+ return String(path).includes('src/workflows/my_workflow/scenarios/test_scenario.json');
120
+ });
121
+ const result = await resolveScenarioPath('my_workflow', 'test_scenario.json', '/project');
122
+ expect(result.found).toBe(true);
123
+ expect(result.path).toContain('test_scenario.json');
124
+ expect(result.path).not.toContain('test_scenario.json.json');
125
+ });
126
+ });
127
+ describe('workflows fallback directory', () => {
128
+ it('should find scenario in workflows/ fallback path', async () => {
129
+ mockCatalogFailure();
130
+ vi.mocked(fs.existsSync).mockImplementation(path => {
131
+ return String(path).includes('workflows/my_workflow/scenarios/test_scenario.json') &&
132
+ !String(path).includes('src/workflows');
133
+ });
134
+ const result = await resolveScenarioPath('my_workflow', 'test_scenario', '/project');
135
+ expect(result.found).toBe(true);
136
+ expect(result.path).toContain('workflows/my_workflow/scenarios/test_scenario.json');
137
+ });
138
+ });
139
+ describe('subdirectory scenarios', () => {
140
+ it('should support subdirectory paths in scenario name', async () => {
141
+ mockCatalogFailure();
142
+ vi.mocked(fs.existsSync).mockImplementation(path => {
143
+ return String(path).includes('src/workflows/my_workflow/scenarios/complex/deep_test.json');
144
+ });
145
+ const result = await resolveScenarioPath('my_workflow', 'complex/deep_test', '/project');
146
+ expect(result.found).toBe(true);
147
+ expect(result.path).toContain('complex/deep_test.json');
148
+ });
149
+ });
150
+ });
151
+ describe('listScenariosForWorkflow', () => {
152
+ beforeEach(() => {
153
+ vi.resetAllMocks();
154
+ });
155
+ it('should return scenario names from scenarios directory', () => {
156
+ vi.mocked(fs.existsSync).mockImplementation(path => String(path).includes('src/workflows/my_workflow/scenarios'));
157
+ vi.mocked(fs.readdirSync).mockReturnValue(['basic.json', 'advanced.json', 'README.md']);
158
+ const result = listScenariosForWorkflow('my_workflow', undefined, '/project');
159
+ expect(result).toEqual(['basic', 'advanced']);
160
+ });
161
+ it('should use workflowPath from catalog to derive directory', () => {
162
+ vi.mocked(fs.existsSync).mockImplementation(path => String(path).includes('src/workflows/viz_examples/01_simple_linear/scenarios'));
163
+ vi.mocked(fs.readdirSync).mockReturnValue(['test.json']);
164
+ const result = listScenariosForWorkflow('simpleLinear', '/app/dist/workflows/viz_examples/01_simple_linear/workflow.js', '/project');
165
+ expect(result).toEqual(['test']);
166
+ });
167
+ it('should fall back to workflowName when workflowPath is undefined', () => {
168
+ vi.mocked(fs.existsSync).mockImplementation(path => String(path).includes('src/workflows/my_workflow/scenarios'));
169
+ vi.mocked(fs.readdirSync).mockReturnValue(['scenario_a.json']);
170
+ const result = listScenariosForWorkflow('my_workflow', undefined, '/project');
171
+ expect(result).toEqual(['scenario_a']);
172
+ });
173
+ it('should fall back to workflowName when path extraction returns null', () => {
174
+ vi.mocked(fs.existsSync).mockImplementation(path => String(path).includes('src/workflows/my_workflow/scenarios'));
175
+ vi.mocked(fs.readdirSync).mockReturnValue(['test.json']);
176
+ const result = listScenariosForWorkflow('my_workflow', '/invalid/path.js', '/project');
177
+ expect(result).toEqual(['test']);
178
+ });
179
+ it('should return empty array when no scenarios directory exists', () => {
180
+ vi.mocked(fs.existsSync).mockReturnValue(false);
181
+ const result = listScenariosForWorkflow('my_workflow', undefined, '/project');
182
+ expect(result).toEqual([]);
183
+ });
184
+ it('should try workflows/ fallback when src/workflows/ does not exist', () => {
185
+ vi.mocked(fs.existsSync).mockImplementation(path => {
186
+ const p = String(path);
187
+ return p.includes('workflows/my_workflow/scenarios') && !p.includes('src/workflows');
188
+ });
189
+ vi.mocked(fs.readdirSync).mockReturnValue(['fallback.json']);
190
+ const result = listScenariosForWorkflow('my_workflow', undefined, '/project');
191
+ expect(result).toEqual(['fallback']);
192
+ });
193
+ it('should return empty array for empty scenarios directory', () => {
194
+ vi.mocked(fs.existsSync).mockReturnValue(true);
195
+ vi.mocked(fs.readdirSync).mockReturnValue([]);
196
+ const result = listScenariosForWorkflow('my_workflow', undefined, '/project');
197
+ expect(result).toEqual([]);
198
+ });
199
+ });
200
+ describe('getScenarioNotFoundMessage', () => {
201
+ it('should return a helpful error message', () => {
202
+ const searchedPaths = [
203
+ '/project/src/workflows/my_workflow/scenarios/test.json',
204
+ '/project/workflows/my_workflow/scenarios/test.json'
205
+ ];
206
+ const message = getScenarioNotFoundMessage('my_workflow', 'test', searchedPaths);
207
+ expect(message).toContain('Scenario \'test\' not found for workflow \'my_workflow\'');
208
+ expect(message).toContain('Searched in:');
209
+ expect(message).toContain(searchedPaths[0]);
210
+ expect(message).toContain(searchedPaths[1]);
211
+ expect(message).toContain('Tip:');
212
+ expect(message).toContain('--input');
213
+ });
214
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@output.ai/cli",
3
- "version": "0.7.13",
3
+ "version": "0.7.15",
4
4
  "description": "CLI for Output.ai workflow generation",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",