@outputai/cli 0.8.2-next.e1cd79b.0 → 0.8.2-next.ec4c07d.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 (54) hide show
  1. package/dist/assets/docker/docker-compose-dev.yml +1 -1
  2. package/dist/commands/workflow/cost.d.ts +3 -2
  3. package/dist/commands/workflow/cost.js +4 -11
  4. package/dist/commands/workflow/cost.spec.js +1 -3
  5. package/dist/commands/workflow/dataset/generate.d.ts +1 -0
  6. package/dist/commands/workflow/dataset/generate.js +15 -6
  7. package/dist/commands/workflow/dataset/generate.spec.d.ts +1 -0
  8. package/dist/commands/workflow/dataset/generate.spec.js +69 -0
  9. package/dist/commands/workflow/dataset/list.d.ts +3 -1
  10. package/dist/commands/workflow/dataset/list.js +10 -14
  11. package/dist/commands/workflow/debug.d.ts +2 -6
  12. package/dist/commands/workflow/debug.js +10 -29
  13. package/dist/commands/workflow/debug.spec.js +2 -5
  14. package/dist/commands/workflow/generate.spec.js +2 -2
  15. package/dist/commands/workflow/list.d.ts +4 -1
  16. package/dist/commands/workflow/list.js +15 -19
  17. package/dist/commands/workflow/list.spec.js +2 -1
  18. package/dist/commands/workflow/result.d.ts +3 -4
  19. package/dist/commands/workflow/result.js +6 -15
  20. package/dist/commands/workflow/result.spec.js +2 -4
  21. package/dist/commands/workflow/run.d.ts +3 -2
  22. package/dist/commands/workflow/run.js +5 -12
  23. package/dist/commands/workflow/run.spec.js +18 -3
  24. package/dist/commands/workflow/runs/list.d.ts +3 -1
  25. package/dist/commands/workflow/runs/list.js +11 -15
  26. package/dist/commands/workflow/start.js +1 -1
  27. package/dist/commands/workflow/start.spec.js +53 -2
  28. package/dist/commands/workflow/status.d.ts +3 -4
  29. package/dist/commands/workflow/status.js +20 -30
  30. package/dist/commands/workflow/status.spec.js +2 -4
  31. package/dist/commands/workflow/test_eval.d.ts +4 -2
  32. package/dist/commands/workflow/test_eval.js +23 -23
  33. package/dist/commands/workflow/test_eval.spec.d.ts +1 -0
  34. package/dist/commands/workflow/test_eval.spec.js +121 -0
  35. package/dist/generated/framework_version.json +1 -1
  36. package/dist/hooks/init.d.ts +1 -0
  37. package/dist/hooks/init.js +40 -30
  38. package/dist/hooks/init.spec.js +28 -4
  39. package/dist/services/coding_agents.spec.js +10 -10
  40. package/dist/utils/format_workflow_result.spec.js +0 -13
  41. package/dist/utils/resolve_input.d.ts +1 -1
  42. package/dist/utils/resolve_input.js +2 -2
  43. package/dist/utils/scenario_resolver.d.ts +1 -1
  44. package/dist/utils/scenario_resolver.js +2 -2
  45. package/dist/utils/scenario_resolver.spec.js +14 -0
  46. package/dist/utils/trace_formatter.js +1 -2
  47. package/dist/utils/workflow_dir.d.ts +1 -1
  48. package/dist/utils/workflow_dir.js +2 -2
  49. package/oclif.manifest.json +114 -101
  50. package/package.json +5 -5
  51. package/dist/utils/constants.d.ts +0 -5
  52. package/dist/utils/constants.js +0 -4
  53. package/dist/utils/output_formatter.d.ts +0 -2
  54. package/dist/utils/output_formatter.js +0 -11
@@ -80,7 +80,7 @@ services:
80
80
  condition: service_healthy
81
81
  worker:
82
82
  condition: service_healthy
83
- image: outputai/api:${OUTPUT_API_VERSION:-0.8.2-next.e1cd79b.0}
83
+ image: outputai/api:${OUTPUT_API_VERSION:-0.8.2-next.ec4c07d.0}
84
84
  init: true
85
85
  networks:
86
86
  - main
@@ -1,16 +1,17 @@
1
1
  import { Command } from '@oclif/core';
2
+ import type { CostReport } from '#types/cost.js';
2
3
  export default class WorkflowCost 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
  tracePath: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
8
10
  };
9
11
  static flags: {
10
- format: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
11
12
  verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
12
13
  };
13
- run(): Promise<void>;
14
+ run(): Promise<CostReport>;
14
15
  private loadLocalTrace;
15
16
  catch(error: Error): Promise<void>;
16
17
  }
@@ -2,18 +2,17 @@ import { Args, Command, Flags } from '@oclif/core';
2
2
  import { readFile } from 'node:fs/promises';
3
3
  import { calculateCost, loadPricingConfig } from '#services/cost_calculator.js';
4
4
  import { getTrace } from '#services/trace_reader.js';
5
- import { OUTPUT_FORMAT } from '#utils/constants.js';
6
5
  import { formatCostReport } from '#utils/cost_formatter.js';
7
6
  import { getErrorCode } from '#utils/error_utils.js';
8
7
  import { handleApiError } from '#utils/error_handler.js';
9
- import { formatOutput } from '#utils/output_formatter.js';
10
8
  export default class WorkflowCost extends Command {
11
9
  static description = 'Calculate the cost of a workflow execution';
10
+ static enableJsonFlag = true;
12
11
  static examples = [
13
12
  '<%= config.bin %> <%= command.id %> my_workflow_id',
14
13
  '<%= config.bin %> <%= command.id %> my_workflow_id --verbose',
15
14
  '<%= config.bin %> <%= command.id %> my_workflow_id path/to/trace.json',
16
- '<%= config.bin %> <%= command.id %> my_workflow_id --format json'
15
+ '<%= config.bin %> <%= command.id %> my_workflow_id --json'
17
16
  ];
18
17
  static args = {
19
18
  workflowId: Args.string({
@@ -26,12 +25,6 @@ export default class WorkflowCost extends Command {
26
25
  })
27
26
  };
28
27
  static flags = {
29
- format: Flags.string({
30
- char: 'f',
31
- description: 'Output format',
32
- options: [OUTPUT_FORMAT.JSON, OUTPUT_FORMAT.TEXT],
33
- default: OUTPUT_FORMAT.TEXT
34
- }),
35
28
  verbose: Flags.boolean({
36
29
  description: 'Show detailed per-call breakdown',
37
30
  default: false
@@ -45,8 +38,8 @@ export default class WorkflowCost extends Command {
45
38
  const config = loadPricingConfig();
46
39
  const traceNode = traceData;
47
40
  const report = calculateCost(traceNode, config, traceFile);
48
- const formatted = formatOutput(report, flags.format, r => formatCostReport(r, { verbose: flags.verbose }));
49
- this.log(formatted);
41
+ this.log(formatCostReport(report, { verbose: flags.verbose }));
42
+ return report;
50
43
  }
51
44
  async loadLocalTrace(tracePath) {
52
45
  try {
@@ -20,7 +20,6 @@ describe('workflow cost command', () => {
20
20
  expect(WorkflowCost).toBeDefined();
21
21
  expect(WorkflowCost.description).toBeDefined();
22
22
  expect(WorkflowCost.args).toHaveProperty('workflowId');
23
- expect(WorkflowCost.flags).toHaveProperty('format');
24
23
  expect(WorkflowCost.flags).toHaveProperty('verbose');
25
24
  });
26
25
  it('should have workflowId as required arg', async () => {
@@ -34,8 +33,7 @@ describe('workflow cost command', () => {
34
33
  });
35
34
  it('should have correct flag configuration', async () => {
36
35
  const WorkflowCost = (await import('./cost.js')).default;
37
- expect(WorkflowCost.flags.format.options).toEqual(['json', 'text']);
38
- expect(WorkflowCost.flags.format.default).toBe('text');
36
+ expect(WorkflowCost.enableJsonFlag).toBe(true);
39
37
  expect(WorkflowCost.flags.verbose.default).toBe(false);
40
38
  });
41
39
  it('should have correct examples', async () => {
@@ -7,6 +7,7 @@ export default class DatasetGenerate extends Command {
7
7
  scenario: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
8
8
  };
9
9
  static flags: {
10
+ catalog: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
11
  trace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
12
  name: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
13
  download: import("@oclif/core/interfaces").BooleanFlag<boolean>;
@@ -26,6 +26,14 @@ export default class DatasetGenerate extends Command {
26
26
  })
27
27
  };
28
28
  static flags = {
29
+ catalog: Flags.string({
30
+ char: 'c',
31
+ aliases: ['task-queue'],
32
+ charAliases: ['q'],
33
+ deprecateAliases: true,
34
+ description: 'Catalog name for workflow execution (defaults to OUTPUT_CATALOG_ID)',
35
+ env: 'OUTPUT_CATALOG_ID'
36
+ }),
29
37
  trace: Flags.string({
30
38
  char: 't',
31
39
  description: 'Path to a local trace file to extract dataset from',
@@ -61,15 +69,16 @@ export default class DatasetGenerate extends Command {
61
69
  await this.generateFromTrace(args.workflowName, flags.trace, flags.name);
62
70
  return;
63
71
  }
64
- await this.generateFromScenario(args.workflowName, args.scenario, flags.input, flags.name);
72
+ await this.generateFromScenario(args.workflowName, args.scenario, flags.input, flags.name, flags.catalog);
65
73
  }
66
- async generateFromScenario(workflowName, scenario, inputFlag, nameOverride) {
67
- const resolvedInput = await this.resolveScenarioInput(workflowName, scenario, inputFlag);
74
+ async generateFromScenario(workflowName, scenario, inputFlag, nameOverride, catalog) {
75
+ const resolvedInput = await this.resolveScenarioInput(workflowName, scenario, inputFlag, catalog);
68
76
  const datasetName = nameOverride ?? scenario ?? 'dataset';
69
77
  this.log(`Running workflow "${workflowName}"...`);
70
78
  const response = await postWorkflowRun({
71
79
  workflowName,
72
- input: resolvedInput
80
+ input: resolvedInput,
81
+ catalog
73
82
  }, {
74
83
  config: { timeout: 600000 }
75
84
  });
@@ -116,7 +125,7 @@ export default class DatasetGenerate extends Command {
116
125
  }
117
126
  this.log(`\nGenerated ${traces.length} dataset(s)`);
118
127
  }
119
- async resolveScenarioInput(workflowName, scenario, inputFlag) {
128
+ async resolveScenarioInput(workflowName, scenario, inputFlag, catalog) {
120
129
  if (inputFlag && scenario) {
121
130
  return ux.error('Cannot use both scenario argument and --input flag. Choose one.', { exit: 1 });
122
131
  }
@@ -124,7 +133,7 @@ export default class DatasetGenerate extends Command {
124
133
  return parseInputFlag(inputFlag);
125
134
  }
126
135
  if (scenario) {
127
- const resolution = await resolveScenarioPath(workflowName, scenario);
136
+ const resolution = await resolveScenarioPath(workflowName, scenario, undefined, undefined, catalog);
128
137
  if (!resolution.found) {
129
138
  return ux.error(getScenarioNotFoundMessage(workflowName, scenario, resolution.searchedPaths), { exit: 1 });
130
139
  }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,69 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
3
+ vi.mock('#api/generated/api.js', () => ({
4
+ postWorkflowRun: vi.fn()
5
+ }));
6
+ vi.mock('#utils/scenario_resolver.js', () => ({
7
+ resolveScenarioPath: vi.fn(),
8
+ getScenarioNotFoundMessage: vi.fn().mockReturnValue('not found')
9
+ }));
10
+ vi.mock('#utils/input_parser.js', () => ({
11
+ parseInputFlag: vi.fn()
12
+ }));
13
+ vi.mock('#services/datasets.js', () => ({
14
+ writeDataset: vi.fn(),
15
+ resolveDefaultDatasetsDir: vi.fn().mockResolvedValue('/datasets'),
16
+ buildDataset: vi.fn().mockReturnValue({ name: 'basic' }),
17
+ getExecutionTime: vi.fn().mockResolvedValue(100),
18
+ extractDatasetName: vi.fn()
19
+ }));
20
+ describe('workflow dataset generate command', () => {
21
+ beforeEach(() => {
22
+ vi.clearAllMocks();
23
+ delete process.env.OUTPUT_CATALOG_ID;
24
+ });
25
+ describe('command definition', () => {
26
+ it('binds the catalog flag to OUTPUT_CATALOG_ID', async () => {
27
+ const DatasetGenerate = (await import('./generate.js')).default;
28
+ expect(DatasetGenerate.flags).toHaveProperty('catalog');
29
+ expect(DatasetGenerate.flags.catalog.env).toBe('OUTPUT_CATALOG_ID');
30
+ expect(DatasetGenerate.flags.catalog.char).toBe('c');
31
+ });
32
+ });
33
+ describe('run()', () => {
34
+ const createCommand = async (flagOverrides = {}) => {
35
+ const DatasetGenerate = (await import('./generate.js')).default;
36
+ const { postWorkflowRun } = await import('#api/generated/api.js');
37
+ const { resolveScenarioPath } = await import('#utils/scenario_resolver.js');
38
+ const { parseInputFlag } = await import('#utils/input_parser.js');
39
+ const cmd = new DatasetGenerate(['my_workflow'], {});
40
+ cmd.log = vi.fn();
41
+ cmd.error = vi.fn(() => {
42
+ throw new Error('error called');
43
+ });
44
+ cmd.parse = vi.fn().mockResolvedValue({
45
+ args: { workflowName: 'my_workflow', scenario: 'basic' },
46
+ flags: { catalog: undefined, trace: undefined, name: undefined, download: false, limit: 5, input: undefined, ...flagOverrides }
47
+ });
48
+ return {
49
+ cmd,
50
+ postWorkflowRun: vi.mocked(postWorkflowRun),
51
+ resolveScenarioPath: vi.mocked(resolveScenarioPath),
52
+ parseInputFlag: vi.mocked(parseInputFlag)
53
+ };
54
+ };
55
+ it('resolves the scenario and runs the workflow against the resolved catalog', async () => {
56
+ const { cmd, postWorkflowRun, resolveScenarioPath, parseInputFlag } = await createCommand({ catalog: 'my-catalog' });
57
+ resolveScenarioPath.mockResolvedValue({ found: true, path: '/scenarios/basic.json', searchedPaths: [] });
58
+ parseInputFlag.mockResolvedValue({ foo: 'bar' });
59
+ postWorkflowRun.mockResolvedValue({
60
+ data: { workflowId: 'wf-1', output: { ok: true } },
61
+ status: 200,
62
+ headers: new Headers()
63
+ });
64
+ await cmd.run();
65
+ expect(resolveScenarioPath).toHaveBeenCalledWith('my_workflow', 'basic', undefined, undefined, 'my-catalog');
66
+ expect(postWorkflowRun).toHaveBeenCalledWith(expect.objectContaining({ workflowName: 'my_workflow', catalog: 'my-catalog' }), expect.anything());
67
+ });
68
+ });
69
+ });
@@ -1,6 +1,8 @@
1
1
  import { Command } from '@oclif/core';
2
+ import type { DatasetInfo } from '#services/datasets.js';
2
3
  export default class DatasetList 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>>;
@@ -8,5 +10,5 @@ export default class DatasetList extends Command {
8
10
  static flags: {
9
11
  format: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
10
12
  };
11
- run(): Promise<void>;
13
+ run(): Promise<DatasetInfo[]>;
12
14
  }
@@ -3,7 +3,6 @@ import Table from 'cli-table3';
3
3
  import { listDatasets } from '#services/datasets.js';
4
4
  const OutputFormat = {
5
5
  TABLE: 'table',
6
- JSON: 'json',
7
6
  TEXT: 'text'
8
7
  };
9
8
  function createDatasetsTable(datasets) {
@@ -37,13 +36,7 @@ function formatDatasetsAsText(datasets) {
37
36
  return `${dataset.name} ${cached}${outputDate}${evalDate}`;
38
37
  }).join('\n');
39
38
  }
40
- function formatDatasetsAsJson(datasets) {
41
- return JSON.stringify(datasets, null, 2);
42
- }
43
39
  function formatDatasets(datasets, format) {
44
- if (format === OutputFormat.JSON) {
45
- return formatDatasetsAsJson(datasets);
46
- }
47
40
  if (format === OutputFormat.TABLE) {
48
41
  return createDatasetsTable(datasets);
49
42
  }
@@ -51,9 +44,10 @@ function formatDatasets(datasets, format) {
51
44
  }
52
45
  export default class DatasetList extends Command {
53
46
  static description = 'List datasets for a workflow';
47
+ static enableJsonFlag = true;
54
48
  static examples = [
55
49
  '<%= config.bin %> <%= command.id %> simple',
56
- '<%= config.bin %> <%= command.id %> simple --format json',
50
+ '<%= config.bin %> <%= command.id %> simple --json',
57
51
  '<%= config.bin %> <%= command.id %> simple --format table'
58
52
  ];
59
53
  static args = {
@@ -65,23 +59,25 @@ export default class DatasetList extends Command {
65
59
  static flags = {
66
60
  format: Flags.string({
67
61
  char: 'f',
68
- description: 'Output format',
69
- options: [OutputFormat.TABLE, OutputFormat.JSON, OutputFormat.TEXT],
62
+ description: 'Output format (use --json for JSON output)',
63
+ options: [OutputFormat.TABLE, OutputFormat.TEXT],
70
64
  default: OutputFormat.TABLE
71
65
  })
72
66
  };
73
67
  async run() {
74
68
  const { args, flags } = await this.parse(DatasetList);
75
69
  const datasets = await listDatasets(args.workflowName);
70
+ if (this.jsonEnabled()) {
71
+ return datasets;
72
+ }
76
73
  if (datasets.length === 0) {
77
74
  this.log(`No datasets found for workflow "${args.workflowName}".`);
78
75
  this.log('Generate datasets with: output workflow dataset generate');
79
- return;
76
+ return datasets;
80
77
  }
81
78
  const output = formatDatasets(datasets, flags.format);
82
79
  this.log(output);
83
- if (flags.format !== OutputFormat.JSON) {
84
- this.log(`\nFound ${datasets.length} dataset(s) for "${args.workflowName}"`);
85
- }
80
+ this.log(`\nFound ${datasets.length} dataset(s) for "${args.workflowName}"`);
81
+ return datasets;
86
82
  }
87
83
  }
@@ -1,16 +1,12 @@
1
1
  import { Command } from '@oclif/core';
2
2
  export default class WorkflowDebug extends Command {
3
3
  static description: string;
4
+ static enableJsonFlag: boolean;
4
5
  static examples: string[];
5
6
  static args: {
6
7
  workflowId: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
7
8
  };
8
- static flags: {
9
- format: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
10
- };
11
- run(): Promise<void>;
12
- private conditionalLog;
13
- private outputJson;
9
+ run(): Promise<unknown>;
14
10
  private displayTextTrace;
15
11
  catch(error: Error): Promise<void>;
16
12
  }
@@ -1,14 +1,13 @@
1
- import { Args, Command, Flags } from '@oclif/core';
2
- import { OUTPUT_FORMAT } from '#utils/constants.js';
1
+ import { Args, Command } from '@oclif/core';
3
2
  import { displayDebugTree } from '#utils/trace_formatter.js';
4
3
  import { getTrace } from '#services/trace_reader.js';
5
4
  import { handleApiError } from '#utils/error_handler.js';
6
5
  export default class WorkflowDebug extends Command {
7
6
  static description = 'Get and display workflow execution trace for debugging';
7
+ static enableJsonFlag = true;
8
8
  static examples = [
9
9
  '<%= config.bin %> <%= command.id %> wf-12345',
10
- '<%= config.bin %> <%= command.id %> wf-12345 --format json',
11
- '<%= config.bin %> <%= command.id %> wf-12345 --format text'
10
+ '<%= config.bin %> <%= command.id %> wf-12345 --json'
12
11
  ];
13
12
  static args = {
14
13
  workflowId: Args.string({
@@ -16,41 +15,23 @@ export default class WorkflowDebug 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(WorkflowDebug);
29
- const isJsonFormat = flags.format === OUTPUT_FORMAT.JSON;
30
- this.conditionalLog(`Fetching debug information for workflow: ${args.workflowId}...`, isJsonFormat);
19
+ const { args } = await this.parse(WorkflowDebug);
20
+ this.log(`Fetching debug information for workflow: ${args.workflowId}...`);
31
21
  const { data: traceData, location } = await getTrace(args.workflowId);
32
22
  const source = location.isRemote ? 'remote' : 'local';
33
- this.conditionalLog(`Trace source: ${source}${!location.isRemote ? ` (${location.path})` : ''}`, isJsonFormat);
34
- if (isJsonFormat) {
35
- this.outputJson(traceData);
36
- return;
23
+ this.log(`Trace source: ${source}${!location.isRemote ? ` (${location.path})` : ''}`);
24
+ if (!this.jsonEnabled()) {
25
+ this.displayTextTrace(traceData);
37
26
  }
38
- this.displayTextTrace(traceData);
39
- }
40
- conditionalLog(message, disabled) {
41
- if (!disabled) {
42
- this.log(message);
43
- }
44
- }
45
- outputJson(data) {
46
- this.log(JSON.stringify(data, null, 2));
27
+ return traceData;
47
28
  }
48
29
  displayTextTrace(traceData) {
49
30
  this.log('\nTrace Log:');
50
31
  this.log('─'.repeat(80));
51
32
  this.log(displayDebugTree(traceData));
52
33
  this.log('\n' + '─'.repeat(80));
53
- this.log('Tip: Use --format json for the full untruncated trace');
34
+ this.log('Tip: Use --json for the full untruncated trace');
54
35
  }
55
36
  async catch(error) {
56
37
  return handleApiError(error, (...args) => this.error(...args), {
@@ -17,13 +17,10 @@ describe('workflow debug command', () => {
17
17
  expect(WorkflowDebug).toBeDefined();
18
18
  expect(WorkflowDebug.description).toContain('Get and display workflow execution trace for debugging');
19
19
  expect(WorkflowDebug.args).toHaveProperty('workflowId');
20
- expect(WorkflowDebug.flags).toHaveProperty('format');
21
20
  });
22
- it('should have correct flag configuration', async () => {
21
+ it('enables the built-in --json flag', async () => {
23
22
  const WorkflowDebug = (await import('./debug.js')).default;
24
- // Format flag
25
- expect(WorkflowDebug.flags.format.options).toEqual(['json', 'text']);
26
- expect(WorkflowDebug.flags.format.default).toBe('text');
23
+ expect(WorkflowDebug.enableJsonFlag).toBe(true);
27
24
  });
28
25
  it('should have correct examples', async () => {
29
26
  const WorkflowDebug = (await import('./debug.js')).default;
@@ -4,8 +4,8 @@ import Generate from './generate.js';
4
4
  import { generateWorkflow } from '#services/workflow_generator.js';
5
5
  import { parseWorkflowDir } from '#utils/workflow_dir_parser.js';
6
6
  import { InvalidNameError, WorkflowExistsError } from '#types/errors.js';
7
- vi.mock('../../services/workflow_generator.js');
8
- vi.mock('../../utils/workflow_dir_parser.js');
7
+ vi.mock('#services/workflow_generator.js');
8
+ vi.mock('#utils/workflow_dir_parser.js');
9
9
  describe('Generate Command', () => {
10
10
  let mockGenerateWorkflow;
11
11
  let mockParseWorkflowDir;
@@ -12,6 +12,7 @@ export declare function parseWorkflowForDisplay(workflow: Workflow): WorkflowDis
12
12
  export declare function formatWorkflowsAsList(workflows: Workflow[]): string;
13
13
  export default class WorkflowList extends Command {
14
14
  static description: string;
15
+ static enableJsonFlag: boolean;
15
16
  static examples: string[];
16
17
  static flags: {
17
18
  catalog: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
@@ -19,7 +20,9 @@ export default class WorkflowList extends Command {
19
20
  detailed: import("@oclif/core/interfaces").BooleanFlag<boolean>;
20
21
  filter: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
21
22
  };
22
- run(): Promise<void>;
23
+ run(): Promise<{
24
+ workflows: unknown[];
25
+ }>;
23
26
  catch(error: Error): Promise<void>;
24
27
  }
25
28
  export {};
@@ -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
  }