@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.
- package/dist/api/generated/api.d.ts +79 -0
- package/dist/api/generated/api.js +32 -0
- package/dist/assets/docker/docker-compose-dev.yml +1 -1
- package/dist/commands/workflow/cost.d.ts +3 -2
- package/dist/commands/workflow/cost.js +4 -11
- package/dist/commands/workflow/cost.spec.js +1 -3
- package/dist/commands/workflow/dataset/generate.d.ts +1 -0
- package/dist/commands/workflow/dataset/generate.js +15 -6
- package/dist/commands/workflow/dataset/generate.spec.d.ts +1 -0
- package/dist/commands/workflow/dataset/generate.spec.js +69 -0
- package/dist/commands/workflow/dataset/list.d.ts +3 -1
- package/dist/commands/workflow/dataset/list.js +10 -14
- package/dist/commands/workflow/debug.d.ts +2 -6
- package/dist/commands/workflow/debug.js +10 -29
- package/dist/commands/workflow/debug.spec.js +2 -5
- package/dist/commands/workflow/generate.spec.js +2 -2
- package/dist/commands/workflow/list.d.ts +4 -1
- package/dist/commands/workflow/list.js +15 -19
- package/dist/commands/workflow/list.spec.js +2 -1
- package/dist/commands/workflow/result.d.ts +3 -4
- package/dist/commands/workflow/result.js +6 -15
- package/dist/commands/workflow/result.spec.js +2 -4
- package/dist/commands/workflow/run.d.ts +3 -2
- package/dist/commands/workflow/run.js +5 -12
- package/dist/commands/workflow/run.spec.js +18 -3
- package/dist/commands/workflow/runs/list.d.ts +3 -1
- package/dist/commands/workflow/runs/list.js +11 -15
- package/dist/commands/workflow/start.js +1 -1
- package/dist/commands/workflow/start.spec.js +53 -2
- package/dist/commands/workflow/status.d.ts +3 -4
- package/dist/commands/workflow/status.js +20 -30
- package/dist/commands/workflow/status.spec.js +2 -4
- package/dist/commands/workflow/test_eval.d.ts +4 -2
- package/dist/commands/workflow/test_eval.js +23 -23
- package/dist/commands/workflow/test_eval.spec.d.ts +1 -0
- package/dist/commands/workflow/test_eval.spec.js +121 -0
- package/dist/generated/framework_version.json +1 -1
- package/dist/hooks/init.d.ts +1 -0
- package/dist/hooks/init.js +40 -30
- package/dist/hooks/init.spec.js +28 -4
- package/dist/services/coding_agents.spec.js +10 -10
- package/dist/utils/format_workflow_result.spec.js +0 -13
- package/dist/utils/resolve_input.d.ts +1 -1
- package/dist/utils/resolve_input.js +2 -2
- package/dist/utils/scenario_resolver.d.ts +1 -1
- package/dist/utils/scenario_resolver.js +2 -2
- package/dist/utils/scenario_resolver.spec.js +14 -0
- package/dist/utils/trace_formatter.js +1 -2
- package/dist/utils/workflow_dir.d.ts +1 -1
- package/dist/utils/workflow_dir.js +2 -2
- package/dist/views/dev/modals/run_modal.d.ts +9 -0
- package/dist/views/dev/modals/run_modal.js +56 -56
- package/dist/views/dev/modals/run_modal.spec.d.ts +1 -0
- package/dist/views/dev/modals/run_modal.spec.js +34 -0
- package/dist/views/dev/panels/help_panel.js +1 -1
- package/dist/views/dev/utils/json_editor.d.ts +2 -0
- package/dist/views/dev/utils/json_editor.js +28 -15
- package/dist/views/dev/utils/json_editor.spec.js +16 -1
- package/oclif.manifest.json +114 -101
- package/package.json +5 -5
- package/dist/utils/constants.d.ts +0 -5
- package/dist/utils/constants.js +0 -4
- package/dist/utils/output_formatter.d.ts +0 -2
- package/dist/utils/output_formatter.js +0 -11
|
@@ -452,6 +452,26 @@ export type GetWorkflowIdRunsRidHistory200 = {
|
|
|
452
452
|
/** @nullable */
|
|
453
453
|
nextPageToken?: string | null;
|
|
454
454
|
};
|
|
455
|
+
export type GetWorkflowIdHistoryStreamParams = {
|
|
456
|
+
/**
|
|
457
|
+
* Include decoded input/output payloads in events
|
|
458
|
+
*/
|
|
459
|
+
includePayloads?: boolean;
|
|
460
|
+
/**
|
|
461
|
+
* Resume from this event ID (alternative to Last-Event-ID header)
|
|
462
|
+
*/
|
|
463
|
+
lastEventId?: number;
|
|
464
|
+
};
|
|
465
|
+
export type GetWorkflowIdRunsRidHistoryStreamParams = {
|
|
466
|
+
/**
|
|
467
|
+
* Include decoded input/output payloads in events
|
|
468
|
+
*/
|
|
469
|
+
includePayloads?: boolean;
|
|
470
|
+
/**
|
|
471
|
+
* Resume from this event ID (alternative to Last-Event-ID header)
|
|
472
|
+
*/
|
|
473
|
+
lastEventId?: number;
|
|
474
|
+
};
|
|
455
475
|
export type GetWorkflowCatalogId200 = {
|
|
456
476
|
/** Each workflow available in this catalog */
|
|
457
477
|
workflows?: Workflow[];
|
|
@@ -1043,6 +1063,65 @@ export type getWorkflowIdRunsRidHistoryResponseError = (getWorkflowIdRunsRidHist
|
|
|
1043
1063
|
export type getWorkflowIdRunsRidHistoryResponse = (getWorkflowIdRunsRidHistoryResponseSuccess | getWorkflowIdRunsRidHistoryResponseError);
|
|
1044
1064
|
export declare const getGetWorkflowIdRunsRidHistoryUrl: (id: string, rid: string, params?: GetWorkflowIdRunsRidHistoryParams) => string;
|
|
1045
1065
|
export declare const getWorkflowIdRunsRidHistory: (id: string, rid: string, params?: GetWorkflowIdRunsRidHistoryParams, options?: ApiRequestOptions) => Promise<getWorkflowIdRunsRidHistoryResponse>;
|
|
1066
|
+
/**
|
|
1067
|
+
* Opens a persistent SSE connection that delivers Temporal workflow history events in real time. Emits named events: `workflow` (metadata, once), `history` (event batches), `done` (terminal state), `server_error` (post-flush errors). The `done` event carries `{ reason, newRunId? }` where `reason` is the terminal Temporal event type (`WORKFLOW_EXECUTION_COMPLETED`, `WORKFLOW_EXECUTION_FAILED`, `WORKFLOW_EXECUTION_TIMED_OUT`, `WORKFLOW_EXECUTION_CANCELED`, `WORKFLOW_EXECUTION_TERMINATED`, `WORKFLOW_EXECUTION_CONTINUED_AS_NEW`) and `newRunId` is present only when the terminal event chains a follow-on run. `server_error` carries `{ error, message, workflowId, runId }`. Errors before the stream opens are returned as JSON HTTP responses (400/404); once open, failures arrive as a `server_error` event. Supports reconnect via `Last-Event-ID` header or `lastEventId` query param.
|
|
1068
|
+
|
|
1069
|
+
* @summary Stream workflow history events via Server-Sent Events
|
|
1070
|
+
*/
|
|
1071
|
+
export type getWorkflowIdHistoryStreamResponse200 = {
|
|
1072
|
+
data: string;
|
|
1073
|
+
status: 200;
|
|
1074
|
+
};
|
|
1075
|
+
export type getWorkflowIdHistoryStreamResponse400 = {
|
|
1076
|
+
data: BadRequestResponse;
|
|
1077
|
+
status: 400;
|
|
1078
|
+
};
|
|
1079
|
+
export type getWorkflowIdHistoryStreamResponse404 = {
|
|
1080
|
+
data: NotFoundResponse;
|
|
1081
|
+
status: 404;
|
|
1082
|
+
};
|
|
1083
|
+
export type getWorkflowIdHistoryStreamResponse500 = {
|
|
1084
|
+
data: InternalServerErrorResponse;
|
|
1085
|
+
status: 500;
|
|
1086
|
+
};
|
|
1087
|
+
export type getWorkflowIdHistoryStreamResponseSuccess = (getWorkflowIdHistoryStreamResponse200) & {
|
|
1088
|
+
headers: Headers;
|
|
1089
|
+
};
|
|
1090
|
+
export type getWorkflowIdHistoryStreamResponseError = (getWorkflowIdHistoryStreamResponse400 | getWorkflowIdHistoryStreamResponse404 | getWorkflowIdHistoryStreamResponse500) & {
|
|
1091
|
+
headers: Headers;
|
|
1092
|
+
};
|
|
1093
|
+
export type getWorkflowIdHistoryStreamResponse = (getWorkflowIdHistoryStreamResponseSuccess | getWorkflowIdHistoryStreamResponseError);
|
|
1094
|
+
export declare const getGetWorkflowIdHistoryStreamUrl: (id: string, params?: GetWorkflowIdHistoryStreamParams) => string;
|
|
1095
|
+
export declare const getWorkflowIdHistoryStream: (id: string, params?: GetWorkflowIdHistoryStreamParams, options?: ApiRequestOptions) => Promise<getWorkflowIdHistoryStreamResponse>;
|
|
1096
|
+
/**
|
|
1097
|
+
* Same as /workflow/{id}/history/stream but targets a specific run ID.
|
|
1098
|
+
* @summary Stream pinned-run workflow history events via Server-Sent Events
|
|
1099
|
+
*/
|
|
1100
|
+
export type getWorkflowIdRunsRidHistoryStreamResponse200 = {
|
|
1101
|
+
data: string;
|
|
1102
|
+
status: 200;
|
|
1103
|
+
};
|
|
1104
|
+
export type getWorkflowIdRunsRidHistoryStreamResponse400 = {
|
|
1105
|
+
data: BadRequestResponse;
|
|
1106
|
+
status: 400;
|
|
1107
|
+
};
|
|
1108
|
+
export type getWorkflowIdRunsRidHistoryStreamResponse404 = {
|
|
1109
|
+
data: NotFoundResponse;
|
|
1110
|
+
status: 404;
|
|
1111
|
+
};
|
|
1112
|
+
export type getWorkflowIdRunsRidHistoryStreamResponse500 = {
|
|
1113
|
+
data: InternalServerErrorResponse;
|
|
1114
|
+
status: 500;
|
|
1115
|
+
};
|
|
1116
|
+
export type getWorkflowIdRunsRidHistoryStreamResponseSuccess = (getWorkflowIdRunsRidHistoryStreamResponse200) & {
|
|
1117
|
+
headers: Headers;
|
|
1118
|
+
};
|
|
1119
|
+
export type getWorkflowIdRunsRidHistoryStreamResponseError = (getWorkflowIdRunsRidHistoryStreamResponse400 | getWorkflowIdRunsRidHistoryStreamResponse404 | getWorkflowIdRunsRidHistoryStreamResponse500) & {
|
|
1120
|
+
headers: Headers;
|
|
1121
|
+
};
|
|
1122
|
+
export type getWorkflowIdRunsRidHistoryStreamResponse = (getWorkflowIdRunsRidHistoryStreamResponseSuccess | getWorkflowIdRunsRidHistoryStreamResponseError);
|
|
1123
|
+
export declare const getGetWorkflowIdRunsRidHistoryStreamUrl: (id: string, rid: string, params?: GetWorkflowIdRunsRidHistoryStreamParams) => string;
|
|
1124
|
+
export declare const getWorkflowIdRunsRidHistoryStream: (id: string, rid: string, params?: GetWorkflowIdRunsRidHistoryStreamParams, options?: ApiRequestOptions) => Promise<getWorkflowIdRunsRidHistoryStreamResponse>;
|
|
1046
1125
|
/**
|
|
1047
1126
|
* @summary Get a specific workflow catalog by ID
|
|
1048
1127
|
*/
|
|
@@ -231,6 +231,38 @@ export const getWorkflowIdRunsRidHistory = async (id, rid, params, options) => {
|
|
|
231
231
|
method: 'GET'
|
|
232
232
|
});
|
|
233
233
|
};
|
|
234
|
+
export const getGetWorkflowIdHistoryStreamUrl = (id, params) => {
|
|
235
|
+
const normalizedParams = new URLSearchParams();
|
|
236
|
+
Object.entries(params || {}).forEach(([key, value]) => {
|
|
237
|
+
if (value !== undefined) {
|
|
238
|
+
normalizedParams.append(key, value === null ? 'null' : value.toString());
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
const stringifiedParams = normalizedParams.toString();
|
|
242
|
+
return stringifiedParams.length > 0 ? `/workflow/${id}/history/stream?${stringifiedParams}` : `/workflow/${id}/history/stream`;
|
|
243
|
+
};
|
|
244
|
+
export const getWorkflowIdHistoryStream = async (id, params, options) => {
|
|
245
|
+
return customFetchInstance(getGetWorkflowIdHistoryStreamUrl(id, params), {
|
|
246
|
+
...options,
|
|
247
|
+
method: 'GET'
|
|
248
|
+
});
|
|
249
|
+
};
|
|
250
|
+
export const getGetWorkflowIdRunsRidHistoryStreamUrl = (id, rid, params) => {
|
|
251
|
+
const normalizedParams = new URLSearchParams();
|
|
252
|
+
Object.entries(params || {}).forEach(([key, value]) => {
|
|
253
|
+
if (value !== undefined) {
|
|
254
|
+
normalizedParams.append(key, value === null ? 'null' : value.toString());
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
const stringifiedParams = normalizedParams.toString();
|
|
258
|
+
return stringifiedParams.length > 0 ? `/workflow/${id}/runs/${rid}/history/stream?${stringifiedParams}` : `/workflow/${id}/runs/${rid}/history/stream`;
|
|
259
|
+
};
|
|
260
|
+
export const getWorkflowIdRunsRidHistoryStream = async (id, rid, params, options) => {
|
|
261
|
+
return customFetchInstance(getGetWorkflowIdRunsRidHistoryStreamUrl(id, rid, params), {
|
|
262
|
+
...options,
|
|
263
|
+
method: 'GET'
|
|
264
|
+
});
|
|
265
|
+
};
|
|
234
266
|
export const getGetWorkflowCatalogIdUrl = (id) => {
|
|
235
267
|
return `/workflow/catalog/${id}`;
|
|
236
268
|
};
|
|
@@ -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<
|
|
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 --
|
|
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
|
-
|
|
49
|
-
|
|
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.
|
|
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<
|
|
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 --
|
|
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.
|
|
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
|
-
|
|
84
|
-
|
|
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
|
-
|
|
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
|
|
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 --
|
|
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
|
|
29
|
-
|
|
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.
|
|
34
|
-
if (
|
|
35
|
-
this.
|
|
36
|
-
return;
|
|
23
|
+
this.log(`Trace source: ${source}${!location.isRemote ? ` (${location.path})` : ''}`);
|
|
24
|
+
if (!this.jsonEnabled()) {
|
|
25
|
+
this.displayTextTrace(traceData);
|
|
37
26
|
}
|
|
38
|
-
|
|
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 --
|
|
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('
|
|
21
|
+
it('enables the built-in --json flag', async () => {
|
|
23
22
|
const WorkflowDebug = (await import('./debug.js')).default;
|
|
24
|
-
|
|
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('
|
|
8
|
-
vi.mock('
|
|
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<
|
|
23
|
+
run(): Promise<{
|
|
24
|
+
workflows: unknown[];
|
|
25
|
+
}>;
|
|
23
26
|
catch(error: Error): Promise<void>;
|
|
24
27
|
}
|
|
25
28
|
export {};
|