@outputai/cli 0.8.2-next.e1cd79b.0 → 0.8.2-next.e658cc2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/dist/api/generated/api.d.ts +79 -0
  2. package/dist/api/generated/api.js +32 -0
  3. package/dist/assets/docker/docker-compose-dev.yml +1 -1
  4. package/dist/commands/workflow/cost.d.ts +3 -2
  5. package/dist/commands/workflow/cost.js +4 -11
  6. package/dist/commands/workflow/cost.spec.js +1 -3
  7. package/dist/commands/workflow/dataset/generate.d.ts +1 -0
  8. package/dist/commands/workflow/dataset/generate.js +15 -6
  9. package/dist/commands/workflow/dataset/generate.spec.d.ts +1 -0
  10. package/dist/commands/workflow/dataset/generate.spec.js +69 -0
  11. package/dist/commands/workflow/dataset/list.d.ts +3 -1
  12. package/dist/commands/workflow/dataset/list.js +10 -14
  13. package/dist/commands/workflow/debug.d.ts +2 -6
  14. package/dist/commands/workflow/debug.js +10 -29
  15. package/dist/commands/workflow/debug.spec.js +2 -5
  16. package/dist/commands/workflow/generate.spec.js +2 -2
  17. package/dist/commands/workflow/list.d.ts +4 -1
  18. package/dist/commands/workflow/list.js +15 -19
  19. package/dist/commands/workflow/list.spec.js +2 -1
  20. package/dist/commands/workflow/result.d.ts +3 -4
  21. package/dist/commands/workflow/result.js +6 -15
  22. package/dist/commands/workflow/result.spec.js +2 -4
  23. package/dist/commands/workflow/run.d.ts +3 -2
  24. package/dist/commands/workflow/run.js +5 -12
  25. package/dist/commands/workflow/run.spec.js +18 -3
  26. package/dist/commands/workflow/runs/list.d.ts +3 -1
  27. package/dist/commands/workflow/runs/list.js +11 -15
  28. package/dist/commands/workflow/start.js +1 -1
  29. package/dist/commands/workflow/start.spec.js +53 -2
  30. package/dist/commands/workflow/status.d.ts +3 -4
  31. package/dist/commands/workflow/status.js +20 -30
  32. package/dist/commands/workflow/status.spec.js +2 -4
  33. package/dist/commands/workflow/test_eval.d.ts +4 -2
  34. package/dist/commands/workflow/test_eval.js +23 -23
  35. package/dist/commands/workflow/test_eval.spec.d.ts +1 -0
  36. package/dist/commands/workflow/test_eval.spec.js +121 -0
  37. package/dist/generated/framework_version.json +1 -1
  38. package/dist/hooks/init.d.ts +1 -0
  39. package/dist/hooks/init.js +40 -30
  40. package/dist/hooks/init.spec.js +28 -4
  41. package/dist/services/coding_agents.spec.js +10 -10
  42. package/dist/utils/format_workflow_result.spec.js +0 -13
  43. package/dist/utils/resolve_input.d.ts +1 -1
  44. package/dist/utils/resolve_input.js +2 -2
  45. package/dist/utils/scenario_resolver.d.ts +1 -1
  46. package/dist/utils/scenario_resolver.js +2 -2
  47. package/dist/utils/scenario_resolver.spec.js +14 -0
  48. package/dist/utils/trace_formatter.js +1 -2
  49. package/dist/utils/workflow_dir.d.ts +1 -1
  50. package/dist/utils/workflow_dir.js +2 -2
  51. package/dist/views/dev/modals/run_modal.d.ts +9 -0
  52. package/dist/views/dev/modals/run_modal.js +56 -56
  53. package/dist/views/dev/modals/run_modal.spec.d.ts +1 -0
  54. package/dist/views/dev/modals/run_modal.spec.js +34 -0
  55. package/dist/views/dev/panels/help_panel.js +1 -1
  56. package/dist/views/dev/utils/json_editor.d.ts +2 -0
  57. package/dist/views/dev/utils/json_editor.js +28 -15
  58. package/dist/views/dev/utils/json_editor.spec.js +16 -1
  59. package/oclif.manifest.json +114 -101
  60. package/package.json +5 -5
  61. package/dist/utils/constants.d.ts +0 -5
  62. package/dist/utils/constants.js +0 -4
  63. package/dist/utils/output_formatter.d.ts +0 -2
  64. package/dist/utils/output_formatter.js +0 -11
@@ -6,8 +6,7 @@ import { handleApiError } from '#utils/error_handler.js';
6
6
  import { listScenariosForWorkflow } from '#utils/scenario_resolver.js';
7
7
  const OUTPUT_FORMAT = {
8
8
  LIST: 'list',
9
- TABLE: 'table',
10
- JSON: 'json'
9
+ TABLE: 'table'
11
10
  };
12
11
  export function parseWorkflowForDisplay(workflow) {
13
12
  const parsed = parseWorkflowDefinition(workflow);
@@ -74,7 +73,7 @@ export function formatWorkflowsAsList(workflows) {
74
73
  return `\nWorkflows:\n\n${lines.join('\n')}`;
75
74
  }
76
75
  function formatWorkflowsAsJson(workflows) {
77
- const output = {
76
+ return {
78
77
  workflows: workflows.map(w => {
79
78
  const display = parseWorkflowForDisplay(w);
80
79
  return {
@@ -88,12 +87,8 @@ function formatWorkflowsAsJson(workflows) {
88
87
  };
89
88
  })
90
89
  };
91
- return JSON.stringify(output, null, 2);
92
90
  }
93
91
  function formatWorkflows(workflows, format, detailed) {
94
- if (format === OUTPUT_FORMAT.JSON) {
95
- return formatWorkflowsAsJson(workflows);
96
- }
97
92
  if (format === OUTPUT_FORMAT.TABLE) {
98
93
  return createWorkflowTable(workflows, detailed);
99
94
  }
@@ -101,10 +96,11 @@ function formatWorkflows(workflows, format, detailed) {
101
96
  }
102
97
  export default class WorkflowList extends Command {
103
98
  static description = 'List available workflows from the catalog';
99
+ static enableJsonFlag = true;
104
100
  static examples = [
105
101
  '<%= config.bin %> <%= command.id %>',
106
102
  '<%= config.bin %> <%= command.id %> --format table',
107
- '<%= config.bin %> <%= command.id %> --format json',
103
+ '<%= config.bin %> <%= command.id %> --json',
108
104
  '<%= config.bin %> <%= command.id %> --detailed',
109
105
  '<%= config.bin %> <%= command.id %> --filter simple',
110
106
  '<%= config.bin %> <%= command.id %> --catalog my-catalog'
@@ -120,8 +116,8 @@ export default class WorkflowList extends Command {
120
116
  }),
121
117
  format: Flags.string({
122
118
  char: 'f',
123
- description: 'Output format',
124
- options: [OUTPUT_FORMAT.LIST, OUTPUT_FORMAT.TABLE, OUTPUT_FORMAT.JSON],
119
+ description: 'Output format (use --json for JSON output)',
120
+ options: [OUTPUT_FORMAT.LIST, OUTPUT_FORMAT.TABLE],
125
121
  default: OUTPUT_FORMAT.LIST
126
122
  }),
127
123
  detailed: Flags.boolean({
@@ -137,20 +133,20 @@ export default class WorkflowList extends Command {
137
133
  const { flags } = await this.parse(WorkflowList);
138
134
  this.log(flags.catalog ? `Fetching workflow catalog: ${flags.catalog}...` : 'Fetching workflow catalog...');
139
135
  const catalogWorkflows = await fetchWorkflowCatalog(flags.catalog);
140
- if (catalogWorkflows.length === 0) {
141
- this.log('No workflows found in catalog.');
142
- return;
143
- }
144
136
  const workflows = flags.filter ?
145
137
  catalogWorkflows.filter(matchName(flags.filter)) :
146
138
  catalogWorkflows;
147
- if (workflows.length === 0 && flags.filter) {
139
+ if (catalogWorkflows.length === 0) {
140
+ this.log('No workflows found in catalog.');
141
+ }
142
+ else if (workflows.length === 0 && flags.filter) {
148
143
  this.log(`No workflows matching filter: ${flags.filter}`);
149
- return;
150
144
  }
151
- const output = formatWorkflows(workflows, flags.format, flags.detailed);
152
- this.log(output);
153
- this.log(`\nFound ${workflows.length} workflow(s)`);
145
+ else {
146
+ this.log(formatWorkflows(workflows, flags.format, flags.detailed));
147
+ this.log(`\nFound ${workflows.length} workflow(s)`);
148
+ }
149
+ return formatWorkflowsAsJson(workflows);
154
150
  }
155
151
  async catch(error) {
156
152
  return handleApiError(error, (...args) => this.error(...args), {
@@ -28,7 +28,8 @@ describe('workflow list command', () => {
28
28
  });
29
29
  it('should have correct flag configuration', async () => {
30
30
  const WorkflowList = (await import('./list.js')).default;
31
- expect(WorkflowList.flags.format.options).toEqual(['list', 'table', 'json']);
31
+ expect(WorkflowList.enableJsonFlag).toBe(true);
32
+ expect(WorkflowList.flags.format.options).toEqual(['list', 'table']);
32
33
  expect(WorkflowList.flags.format.default).toBe('list');
33
34
  expect(WorkflowList.flags.detailed.default).toBe(false);
34
35
  });
@@ -1,13 +1,12 @@
1
1
  import { Command } from '@oclif/core';
2
+ import { type WorkflowResultResponse } from '#api/generated/api.js';
2
3
  export default class WorkflowResult extends Command {
3
4
  static description: string;
5
+ static enableJsonFlag: boolean;
4
6
  static examples: string[];
5
7
  static args: {
6
8
  workflowId: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
7
9
  };
8
- static flags: {
9
- format: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
10
- };
11
- run(): Promise<void>;
10
+ run(): Promise<WorkflowResultResponse>;
12
11
  catch(error: Error): Promise<void>;
13
12
  }
@@ -1,14 +1,13 @@
1
- import { Args, Command, Flags } from '@oclif/core';
1
+ import { Args, Command } from '@oclif/core';
2
2
  import { getWorkflowIdResult } from '#api/generated/api.js';
3
- import { OUTPUT_FORMAT } from '#utils/constants.js';
4
- import { formatOutput } from '#utils/output_formatter.js';
5
3
  import { formatWorkflowResult, ERROR_STATUSES } from '#utils/format_workflow_result.js';
6
4
  import { handleApiError } from '#utils/error_handler.js';
7
5
  export default class WorkflowResult extends Command {
8
6
  static description = 'Get workflow execution result';
7
+ static enableJsonFlag = true;
9
8
  static examples = [
10
9
  '<%= config.bin %> <%= command.id %> wf-12345',
11
- '<%= config.bin %> <%= command.id %> wf-12345 --format json'
10
+ '<%= config.bin %> <%= command.id %> wf-12345 --json'
12
11
  ];
13
12
  static args = {
14
13
  workflowId: Args.string({
@@ -16,27 +15,19 @@ export default class WorkflowResult extends Command {
16
15
  required: true
17
16
  })
18
17
  };
19
- static flags = {
20
- format: Flags.string({
21
- char: 'f',
22
- description: 'Output format',
23
- options: [OUTPUT_FORMAT.JSON, OUTPUT_FORMAT.TEXT],
24
- default: OUTPUT_FORMAT.TEXT
25
- })
26
- };
27
18
  async run() {
28
- const { args, flags } = await this.parse(WorkflowResult);
19
+ const { args } = await this.parse(WorkflowResult);
29
20
  this.log(`Fetching result for workflow: ${args.workflowId}...`);
30
21
  const response = await getWorkflowIdResult(args.workflowId);
31
22
  if (!response || !response.data) {
32
23
  this.error('API returned invalid response', { exit: 1 });
33
24
  }
34
25
  const data = response.data;
35
- const output = formatOutput(data, flags.format, formatWorkflowResult);
36
- this.log(`\n${output}`);
26
+ this.log(`\n${formatWorkflowResult(data)}`);
37
27
  if (ERROR_STATUSES.has(data.status)) {
38
28
  process.exitCode = 1;
39
29
  }
30
+ return data;
40
31
  }
41
32
  async catch(error) {
42
33
  return handleApiError(error, (...args) => this.error(...args), {
@@ -12,12 +12,10 @@ describe('workflow result command', () => {
12
12
  expect(WorkflowResult).toBeDefined();
13
13
  expect(WorkflowResult.description).toContain('Get workflow execution result');
14
14
  expect(WorkflowResult.args).toHaveProperty('workflowId');
15
- expect(WorkflowResult.flags).toHaveProperty('format');
16
15
  });
17
- it('should have correct flag configuration', async () => {
16
+ it('enables the built-in --json flag', async () => {
18
17
  const WorkflowResult = (await import('./result.js')).default;
19
- expect(WorkflowResult.flags.format.options).toEqual(['json', 'text']);
20
- expect(WorkflowResult.flags.format.default).toBe('text');
18
+ expect(WorkflowResult.enableJsonFlag).toBe(true);
21
19
  });
22
20
  });
23
21
  });
@@ -1,6 +1,8 @@
1
1
  import { Command } from '@oclif/core';
2
+ import { type WorkflowResultResponse } from '#api/generated/api.js';
2
3
  export default class WorkflowRun extends Command {
3
4
  static description: string;
5
+ static enableJsonFlag: boolean;
4
6
  static examples: string[];
5
7
  static args: {
6
8
  workflowName: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
@@ -9,8 +11,7 @@ export default class WorkflowRun extends Command {
9
11
  static flags: {
10
12
  input: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
13
  catalog: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
- format: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
13
14
  };
14
- run(): Promise<void>;
15
+ run(): Promise<WorkflowResultResponse>;
15
16
  catch(error: Error): Promise<void>;
16
17
  }
@@ -1,7 +1,5 @@
1
1
  import { Args, Command, Flags } from '@oclif/core';
2
2
  import { postWorkflowRun } from '#api/generated/api.js';
3
- import { OUTPUT_FORMAT } from '#utils/constants.js';
4
- import { formatOutput } from '#utils/output_formatter.js';
5
3
  import { formatWorkflowResult, ERROR_STATUSES } from '#utils/format_workflow_result.js';
6
4
  import { handleApiError } from '#utils/error_handler.js';
7
5
  import { resolveInput } from '#utils/resolve_input.js';
@@ -30,9 +28,10 @@ async function executeWorkflow(args) {
30
28
  }
31
29
  export default class WorkflowRun extends Command {
32
30
  static description = 'Execute a workflow synchronously and wait for completion';
31
+ static enableJsonFlag = true;
33
32
  static examples = [
34
33
  '<%= config.bin %> <%= command.id %> simple basic_input',
35
- '<%= config.bin %> <%= command.id %> simple my_scenario --format json',
34
+ '<%= config.bin %> <%= command.id %> simple my_scenario --json',
36
35
  '<%= config.bin %> <%= command.id %> simple --input \'{"values":[1,2,3]}\'',
37
36
  '<%= config.bin %> <%= command.id %> simple --input input.json',
38
37
  '<%= config.bin %> <%= command.id %> simple --input \'{"key":"value"}\' --catalog my-catalog'
@@ -60,17 +59,11 @@ export default class WorkflowRun extends Command {
60
59
  deprecateAliases: true,
61
60
  description: 'Catalog name for workflow execution (defaults to OUTPUT_CATALOG_ID)',
62
61
  env: 'OUTPUT_CATALOG_ID'
63
- }),
64
- format: Flags.string({
65
- char: 'f',
66
- description: 'Output format',
67
- options: [OUTPUT_FORMAT.JSON, OUTPUT_FORMAT.TEXT],
68
- default: OUTPUT_FORMAT.TEXT
69
62
  })
70
63
  };
71
64
  async run() {
72
65
  const { args, flags } = await this.parse(WorkflowRun);
73
- const input = await resolveInput(args.workflowName, args.scenario, flags.input, 'run');
66
+ const input = await resolveInput(args.workflowName, args.scenario, flags.input, 'run', flags.catalog);
74
67
  this.log(`Executing workflow: ${args.workflowName}...`);
75
68
  const response = await executeWorkflow({
76
69
  body: {
@@ -85,11 +78,11 @@ export default class WorkflowRun extends Command {
85
78
  this.error('API returned invalid response', { exit: 1 });
86
79
  }
87
80
  const data = response.data;
88
- const output = formatOutput(data, flags.format, formatWorkflowResult);
89
- this.log(`\n${output}`);
81
+ this.log(`\n${formatWorkflowResult(data)}`);
90
82
  if (ERROR_STATUSES.has(data.status)) {
91
83
  process.exitCode = 1;
92
84
  }
85
+ return data;
93
86
  }
94
87
  async catch(error) {
95
88
  return handleApiError(error, (...args) => this.error(...args), {
@@ -26,13 +26,11 @@ describe('workflow run command', () => {
26
26
  expect(WorkflowRun.description).toContain('Execute a workflow');
27
27
  expect(WorkflowRun.args).toHaveProperty('workflowName');
28
28
  expect(WorkflowRun.flags).toHaveProperty('input');
29
- expect(WorkflowRun.flags).toHaveProperty('format');
30
29
  expect(WorkflowRun.flags).toHaveProperty('catalog');
31
30
  });
32
31
  it('should have correct flag configuration', async () => {
33
32
  const WorkflowRun = (await import('./run.js')).default;
34
- expect(WorkflowRun.flags.format.options).toEqual(['json', 'text']);
35
- expect(WorkflowRun.flags.format.default).toBe('text');
33
+ expect(WorkflowRun.enableJsonFlag).toBe(true);
36
34
  expect(WorkflowRun.flags.input.required).toBe(false);
37
35
  });
38
36
  it('should have optional scenario argument', async () => {
@@ -66,11 +64,28 @@ describe('workflow run command', () => {
66
64
  headers: new Headers()
67
65
  });
68
66
  await cmd.run();
67
+ expect(resolveInput).toHaveBeenCalledWith('my_workflow', undefined, undefined, 'run', undefined);
69
68
  expect(postWorkflowRun).toHaveBeenCalledTimes(1);
70
69
  expect(postWorkflowRun).toHaveBeenCalledWith({ workflowName: 'my_workflow', input: { key: 'value' }, catalog: undefined }, expect.objectContaining({ config: { timeout: 600000 } }));
71
70
  expect(cmd.log).toHaveBeenCalledWith('Executing workflow: my_workflow...');
72
71
  expect(cmd.log).toHaveBeenCalledWith(expect.stringMatching(/\n/));
73
72
  });
73
+ it('threads the resolved catalog to resolveInput and postWorkflowRun', async () => {
74
+ const { cmd, postWorkflowRun, resolveInput } = await createCommand();
75
+ cmd.parse = vi.fn().mockResolvedValue({
76
+ args: { workflowName: 'my_workflow', scenario: 'basic' },
77
+ flags: { input: undefined, catalog: 'my-catalog', format: 'text' }
78
+ });
79
+ resolveInput.mockResolvedValue({ key: 'value' });
80
+ postWorkflowRun.mockResolvedValue({
81
+ data: { status: 'completed', result: {} },
82
+ status: 200,
83
+ headers: new Headers()
84
+ });
85
+ await cmd.run();
86
+ expect(resolveInput).toHaveBeenCalledWith('my_workflow', 'basic', undefined, 'run', 'my-catalog');
87
+ expect(postWorkflowRun).toHaveBeenCalledWith(expect.objectContaining({ catalog: 'my-catalog' }), expect.anything());
88
+ });
74
89
  it('retries when response has Retry-After and succeeds on second attempt', async () => {
75
90
  const { cmd, postWorkflowRun, resolveInput } = await createCommand();
76
91
  resolveInput.mockResolvedValue({});
@@ -1,6 +1,8 @@
1
1
  import { Command } from '@oclif/core';
2
+ import { type WorkflowRun } from '#services/workflow_runs.js';
2
3
  export default class WorkflowRunsList extends Command {
3
4
  static description: string;
5
+ static enableJsonFlag: boolean;
4
6
  static examples: string[];
5
7
  static args: {
6
8
  workflowName: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
@@ -10,6 +12,6 @@ export default class WorkflowRunsList extends Command {
10
12
  limit: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
11
13
  format: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
12
14
  };
13
- run(): Promise<void>;
15
+ run(): Promise<WorkflowRun[]>;
14
16
  catch(error: Error): Promise<void>;
15
17
  }
@@ -5,7 +5,6 @@ import { formatDate, formatDurationFromTimestamps } from '#utils/date_formatter.
5
5
  import { handleApiError } from '#utils/error_handler.js';
6
6
  const OUTPUT_FORMAT = {
7
7
  TABLE: 'table',
8
- JSON: 'json',
9
8
  TEXT: 'text'
10
9
  };
11
10
  function createRunsTable(runs) {
@@ -37,13 +36,7 @@ function formatRunsAsText(runs) {
37
36
  return `${run.workflowId} (${run.workflowType}) - ${run.status} [${duration}]`;
38
37
  }).join('\n');
39
38
  }
40
- function formatRunsAsJson(runs) {
41
- return JSON.stringify(runs, null, 2);
42
- }
43
39
  function formatRuns(runs, format) {
44
- if (format === OUTPUT_FORMAT.JSON) {
45
- return formatRunsAsJson(runs);
46
- }
47
40
  if (format === OUTPUT_FORMAT.TABLE) {
48
41
  return createRunsTable(runs);
49
42
  }
@@ -51,12 +44,13 @@ function formatRuns(runs, format) {
51
44
  }
52
45
  export default class WorkflowRunsList extends Command {
53
46
  static description = 'List workflow runs with optional filtering by workflow type';
47
+ static enableJsonFlag = true;
54
48
  static examples = [
55
49
  '<%= config.bin %> <%= command.id %>',
56
50
  '<%= config.bin %> <%= command.id %> simple',
57
51
  '<%= config.bin %> <%= command.id %> simple --limit 10',
58
52
  '<%= config.bin %> <%= command.id %> --catalog my-catalog',
59
- '<%= config.bin %> <%= command.id %> --format json',
53
+ '<%= config.bin %> <%= command.id %> --json',
60
54
  '<%= config.bin %> <%= command.id %> --format table'
61
55
  ];
62
56
  static args = {
@@ -78,8 +72,8 @@ export default class WorkflowRunsList extends Command {
78
72
  }),
79
73
  format: Flags.string({
80
74
  char: 'f',
81
- description: 'Output format',
82
- options: [OUTPUT_FORMAT.TABLE, OUTPUT_FORMAT.JSON, OUTPUT_FORMAT.TEXT],
75
+ description: 'Output format (use --json for JSON output)',
76
+ options: [OUTPUT_FORMAT.TABLE, OUTPUT_FORMAT.TEXT],
83
77
  default: OUTPUT_FORMAT.TABLE
84
78
  })
85
79
  };
@@ -90,17 +84,19 @@ export default class WorkflowRunsList extends Command {
90
84
  catalog: flags.catalog,
91
85
  limit: flags.limit
92
86
  });
87
+ if (this.jsonEnabled()) {
88
+ return runs;
89
+ }
93
90
  if (runs.length === 0) {
94
91
  const filterMsg = args.workflowName ? ` for workflow type "${args.workflowName}"` : '';
95
92
  this.log(`No workflow runs found${filterMsg}.`);
96
- return;
93
+ return runs;
97
94
  }
98
95
  const output = formatRuns(runs, flags.format);
99
96
  this.log(output);
100
- if (flags.format !== OUTPUT_FORMAT.JSON) {
101
- const filterMsg = args.workflowName ? ` of type "${args.workflowName}"` : '';
102
- this.log(`\nFound ${count} run(s)${filterMsg}`);
103
- }
97
+ const filterMsg = args.workflowName ? ` of type "${args.workflowName}"` : '';
98
+ this.log(`\nFound ${count} run(s)${filterMsg}`);
99
+ return runs;
104
100
  }
105
101
  async catch(error) {
106
102
  return handleApiError(error, (...args) => this.error(...args), {
@@ -37,7 +37,7 @@ export default class WorkflowStart extends Command {
37
37
  };
38
38
  async run() {
39
39
  const { args, flags } = await this.parse(WorkflowStart);
40
- const input = await resolveInput(args.workflowName, args.scenario, flags.input, 'start');
40
+ const input = await resolveInput(args.workflowName, args.scenario, flags.input, 'start', flags.catalog);
41
41
  this.log(`Starting workflow: ${args.workflowName}...`);
42
42
  const response = await postWorkflowStart({
43
43
  workflowName: args.workflowName,
@@ -1,11 +1,17 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
1
2
  import { describe, it, expect, vi, beforeEach } from 'vitest';
2
- vi.mock('../../api/generated/api.js', () => ({
3
+ vi.mock('#api/generated/api.js', () => ({
3
4
  postWorkflowStart: vi.fn()
4
5
  }));
6
+ vi.mock('#utils/resolve_input.js', () => ({
7
+ resolveInput: vi.fn()
8
+ }));
5
9
  describe('workflow start command', () => {
6
- beforeEach(() => {
10
+ beforeEach(async () => {
7
11
  vi.clearAllMocks();
8
12
  delete process.env.OUTPUT_CATALOG_ID;
13
+ const { resolveInput } = await import('#utils/resolve_input.js');
14
+ vi.mocked(resolveInput).mockResolvedValue({});
9
15
  });
10
16
  describe('command definition', () => {
11
17
  it('should export a valid OCLIF command', async () => {
@@ -25,5 +31,50 @@ describe('workflow start command', () => {
25
31
  expect(WorkflowStart.args).toHaveProperty('scenario');
26
32
  expect(WorkflowStart.args.scenario.required).toBe(false);
27
33
  });
34
+ it('binds the catalog flag to OUTPUT_CATALOG_ID', async () => {
35
+ const WorkflowStart = (await import('./start.js')).default;
36
+ expect(WorkflowStart.flags.catalog.env).toBe('OUTPUT_CATALOG_ID');
37
+ expect(WorkflowStart.flags.catalog.char).toBe('c');
38
+ });
39
+ });
40
+ describe('run()', () => {
41
+ const createCommand = async (flagOverrides = {}) => {
42
+ const WorkflowStart = (await import('./start.js')).default;
43
+ const { postWorkflowStart } = await import('#api/generated/api.js');
44
+ const { resolveInput } = await import('#utils/resolve_input.js');
45
+ const cmd = new WorkflowStart(['my_workflow'], {});
46
+ cmd.log = vi.fn();
47
+ cmd.error = vi.fn(() => {
48
+ throw new Error('error called');
49
+ });
50
+ cmd.parse = vi.fn().mockResolvedValue({
51
+ args: { workflowName: 'my_workflow', scenario: undefined },
52
+ flags: { input: undefined, catalog: undefined, ...flagOverrides }
53
+ });
54
+ return { cmd, postWorkflowStart: vi.mocked(postWorkflowStart), resolveInput: vi.mocked(resolveInput) };
55
+ };
56
+ it('threads the resolved catalog to resolveInput and postWorkflowStart', async () => {
57
+ const { cmd, postWorkflowStart, resolveInput } = await createCommand({ catalog: 'my-catalog' });
58
+ resolveInput.mockResolvedValue({ key: 'value' });
59
+ postWorkflowStart.mockResolvedValue({
60
+ data: { workflowId: 'wf-123' },
61
+ status: 200,
62
+ headers: new Headers()
63
+ });
64
+ await cmd.run();
65
+ expect(resolveInput).toHaveBeenCalledWith('my_workflow', undefined, undefined, 'start', 'my-catalog');
66
+ expect(postWorkflowStart).toHaveBeenCalledWith(expect.objectContaining({ workflowName: 'my_workflow', catalog: 'my-catalog' }));
67
+ });
68
+ it('passes undefined catalog through when none is set', async () => {
69
+ const { cmd, postWorkflowStart, resolveInput } = await createCommand();
70
+ resolveInput.mockResolvedValue({});
71
+ postWorkflowStart.mockResolvedValue({
72
+ data: { workflowId: 'wf-123' },
73
+ status: 200,
74
+ headers: new Headers()
75
+ });
76
+ await cmd.run();
77
+ expect(resolveInput).toHaveBeenCalledWith('my_workflow', undefined, undefined, 'start', undefined);
78
+ });
28
79
  });
29
80
  });
@@ -1,13 +1,12 @@
1
1
  import { Command } from '@oclif/core';
2
+ import { WorkflowStatusResponse } from '#api/generated/api.js';
2
3
  export default class WorkflowStatus extends Command {
3
4
  static description: string;
5
+ static enableJsonFlag: boolean;
4
6
  static examples: string[];
5
7
  static args: {
6
8
  workflowId: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
7
9
  };
8
- static flags: {
9
- format: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
10
- };
11
- run(): Promise<void>;
10
+ run(): Promise<WorkflowStatusResponse>;
12
11
  catch(error: Error): Promise<void>;
13
12
  }
@@ -1,14 +1,27 @@
1
- import { Args, Command, Flags } from '@oclif/core';
1
+ import { Args, Command } from '@oclif/core';
2
2
  import { getWorkflowIdStatus } from '#api/generated/api.js';
3
- import { OUTPUT_FORMAT } from '#utils/constants.js';
4
- import { formatOutput } from '#utils/output_formatter.js';
5
3
  import { handleApiError } from '#utils/error_handler.js';
6
4
  import { normalizeWorkflowStatus } from '#utils/normalize_workflow_status.js';
5
+ function formatStatusText(result) {
6
+ const lines = [
7
+ `Workflow ID: ${result.workflowId || 'unknown'}`,
8
+ `Status: ${result.status || 'unknown'}`,
9
+ ''
10
+ ];
11
+ if (result.startedAt) {
12
+ lines.push(`Started At: ${new Date(result.startedAt).toISOString()}`);
13
+ }
14
+ if (result.completedAt) {
15
+ lines.push(`Completed At: ${new Date(result.completedAt).toISOString()}`);
16
+ }
17
+ return lines.join('\n');
18
+ }
7
19
  export default class WorkflowStatus extends Command {
8
20
  static description = 'Get workflow execution status';
21
+ static enableJsonFlag = true;
9
22
  static examples = [
10
23
  '<%= config.bin %> <%= command.id %> wf-12345',
11
- '<%= config.bin %> <%= command.id %> wf-12345 --format json'
24
+ '<%= config.bin %> <%= command.id %> wf-12345 --json'
12
25
  ];
13
26
  static args = {
14
27
  workflowId: Args.string({
@@ -16,16 +29,8 @@ export default class WorkflowStatus extends Command {
16
29
  required: true
17
30
  })
18
31
  };
19
- static flags = {
20
- format: Flags.string({
21
- char: 'f',
22
- description: 'Output format',
23
- options: [OUTPUT_FORMAT.JSON, OUTPUT_FORMAT.TEXT],
24
- default: OUTPUT_FORMAT.TEXT
25
- })
26
- };
27
32
  async run() {
28
- const { args, flags } = await this.parse(WorkflowStatus);
33
+ const { args } = await this.parse(WorkflowStatus);
29
34
  this.log(`Fetching status for workflow: ${args.workflowId}...`);
30
35
  const response = await getWorkflowIdStatus(args.workflowId);
31
36
  if (!response || !response.data) {
@@ -36,23 +41,8 @@ export default class WorkflowStatus extends Command {
36
41
  ...rawData,
37
42
  status: normalizeWorkflowStatus(rawData.status)
38
43
  };
39
- const output = formatOutput(data, flags.format, (result) => {
40
- const lines = [
41
- `Workflow ID: ${result.workflowId || 'unknown'}`,
42
- `Status: ${result.status || 'unknown'}`,
43
- ''
44
- ];
45
- if (result.startedAt) {
46
- const startDate = new Date(result.startedAt);
47
- lines.push(`Started At: ${startDate.toISOString()}`);
48
- }
49
- if (result.completedAt) {
50
- const completedDate = new Date(result.completedAt);
51
- lines.push(`Completed At: ${completedDate.toISOString()}`);
52
- }
53
- return lines.join('\n');
54
- });
55
- this.log(`\n${output}`);
44
+ this.log(`\n${formatStatusText(data)}`);
45
+ return data;
56
46
  }
57
47
  async catch(error) {
58
48
  return handleApiError(error, (...args) => this.error(...args), {
@@ -22,12 +22,10 @@ describe('workflow status command', () => {
22
22
  expect(WorkflowStatus).toBeDefined();
23
23
  expect(WorkflowStatus.description).toContain('Get workflow execution status');
24
24
  expect(WorkflowStatus.args).toHaveProperty('workflowId');
25
- expect(WorkflowStatus.flags).toHaveProperty('format');
26
25
  });
27
- it('should have correct flag configuration', async () => {
26
+ it('enables the built-in --json flag', async () => {
28
27
  const WorkflowStatus = (await import('./status.js')).default;
29
- expect(WorkflowStatus.flags.format.options).toEqual(['json', 'text']);
30
- expect(WorkflowStatus.flags.format.default).toBe('text');
28
+ expect(WorkflowStatus.enableJsonFlag).toBe(true);
31
29
  });
32
30
  });
33
31
  });
@@ -1,18 +1,20 @@
1
1
  import { Command } from '@oclif/core';
2
+ import type { EvalOutput } from '@outputai/evals';
2
3
  export default class WorkflowTest extends Command {
3
4
  static aliases: string[];
4
5
  static description: string;
6
+ static enableJsonFlag: boolean;
5
7
  static examples: string[];
6
8
  static args: {
7
9
  workflowName: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
8
10
  };
9
11
  static flags: {
12
+ catalog: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
13
  cached: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
14
  save: import("@oclif/core/interfaces").BooleanFlag<boolean>;
12
15
  dataset: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
- format: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
14
16
  };
15
- run(): Promise<void>;
17
+ run(): Promise<EvalOutput>;
16
18
  private ensureEvalWorkflowRegistered;
17
19
  private validateDatasets;
18
20
  private runWorkflowForDatasets;