@output.ai/cli 0.3.1-dev.pr156.0 → 0.4.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/README.md +2 -28
- package/dist/api/generated/api.d.ts +35 -59
- package/dist/api/generated/api.js +4 -13
- package/dist/assets/docker/docker-compose-dev.yml +2 -2
- package/dist/commands/workflow/debug.d.ts +2 -8
- package/dist/commands/workflow/debug.js +24 -164
- package/dist/commands/workflow/debug.spec.js +36 -0
- package/dist/commands/workflow/generate.js +3 -10
- package/dist/commands/workflow/generate.spec.js +6 -4
- package/dist/commands/workflow/{output.d.ts → result.d.ts} +1 -1
- package/dist/commands/workflow/{output.js → result.js} +8 -8
- package/dist/commands/workflow/result.test.js +23 -0
- package/dist/commands/workflow/start.js +1 -1
- package/dist/services/coding_agents.js +30 -0
- package/dist/services/coding_agents.spec.js +36 -61
- package/dist/services/messages.d.ts +1 -0
- package/dist/services/messages.js +65 -1
- package/dist/services/trace_reader.d.ts +14 -0
- package/dist/services/trace_reader.js +67 -0
- package/dist/services/trace_reader.spec.d.ts +1 -0
- package/dist/services/trace_reader.spec.js +164 -0
- package/dist/services/workflow_generator.spec.d.ts +1 -0
- package/dist/services/workflow_generator.spec.js +77 -0
- package/dist/templates/agent_instructions/AGENTS.md.template +209 -19
- package/dist/templates/agent_instructions/agents/context_fetcher.md.template +82 -0
- package/dist/templates/agent_instructions/agents/prompt_writer.md.template +595 -0
- package/dist/templates/agent_instructions/agents/workflow_planner.md.template +13 -4
- package/dist/templates/agent_instructions/agents/workflow_quality.md.template +244 -0
- package/dist/templates/agent_instructions/commands/build_workflow.md.template +52 -9
- package/dist/templates/agent_instructions/commands/plan_workflow.md.template +4 -4
- package/dist/templates/project/package.json.template +2 -2
- package/dist/templates/project/src/simple/scenarios/question_ada_lovelace.json.template +3 -0
- package/dist/templates/workflow/scenarios/test_input.json.template +7 -0
- package/dist/types/trace.d.ts +161 -0
- package/dist/types/trace.js +18 -0
- package/dist/utils/date_formatter.d.ts +8 -0
- package/dist/utils/date_formatter.js +19 -0
- package/dist/utils/template.spec.js +6 -0
- package/dist/utils/trace_formatter.d.ts +11 -61
- package/dist/utils/trace_formatter.js +384 -239
- package/package.json +2 -2
- package/dist/commands/workflow/debug.test.js +0 -107
- package/dist/commands/workflow/output.test.js +0 -23
- package/dist/utils/s3_downloader.d.ts +0 -49
- package/dist/utils/s3_downloader.js +0 -154
- /package/dist/commands/workflow/{debug.test.d.ts → debug.spec.d.ts} +0 -0
- /package/dist/commands/workflow/{output.test.d.ts → result.test.d.ts} +0 -0
- /package/dist/templates/workflow/{prompt@v1.prompt.template → prompts/prompt@v1.prompt.template} +0 -0
|
@@ -1,257 +1,402 @@
|
|
|
1
1
|
import Table from 'cli-table3';
|
|
2
|
+
import { ux } from '@oclif/core';
|
|
2
3
|
import { formatOutput } from '#utils/output_formatter.js';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
4
|
+
import { formatDuration } from '#utils/date_formatter.js';
|
|
5
|
+
import { getErrorMessage } from '#utils/error_utils.js';
|
|
6
|
+
import { isTraceEvent, isValidTimestamp } from '#types/trace.js';
|
|
7
|
+
export { formatDuration };
|
|
8
|
+
const TRUNCATION = {
|
|
9
|
+
SHORT: 50,
|
|
10
|
+
STANDARD: 120,
|
|
11
|
+
SUFFIX: '...',
|
|
12
|
+
SUFFIX_VERBOSE: '... (truncated)'
|
|
13
|
+
};
|
|
14
|
+
const TREE_CHARS = {
|
|
15
|
+
BRANCH: '├─ ',
|
|
16
|
+
LAST: '└─ ',
|
|
17
|
+
VERTICAL: '│ ',
|
|
18
|
+
SPACE: ' ',
|
|
19
|
+
DETAIL_BRANCH: '│ ',
|
|
20
|
+
DETAIL_LAST: ' '
|
|
21
|
+
};
|
|
22
|
+
const HEADER_DIVIDER = '═'.repeat(60);
|
|
23
|
+
const colors = {
|
|
24
|
+
workflowHeader: (text) => ux.colorize('bold', ux.colorize('cyan', text)),
|
|
25
|
+
stepHeader: (text) => ux.colorize('yellow', text),
|
|
26
|
+
internalStep: (text) => ux.colorize('dim', text),
|
|
27
|
+
label: (text) => ux.colorize('blue', text),
|
|
28
|
+
error: (text) => ux.colorize('red', text),
|
|
29
|
+
success: (text) => ux.colorize('green', text)
|
|
30
|
+
};
|
|
31
|
+
const truncate = (value, maxLength = TRUNCATION.STANDARD, suffix = TRUNCATION.SUFFIX_VERBOSE, recursive = false) => {
|
|
32
|
+
if (value === null || value === undefined) {
|
|
33
|
+
return value;
|
|
34
|
+
}
|
|
35
|
+
if (typeof value === 'string') {
|
|
36
|
+
if (value.length <= maxLength) {
|
|
37
|
+
return value;
|
|
12
38
|
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
*/
|
|
19
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
20
|
-
formatAsText(trace) {
|
|
21
|
-
const output = [];
|
|
22
|
-
// Add header with workflow info
|
|
23
|
-
if (trace.root) {
|
|
24
|
-
output.push(this.formatHeader(trace.root));
|
|
25
|
-
output.push('');
|
|
39
|
+
return `${value.substring(0, maxLength)}${suffix}`;
|
|
40
|
+
}
|
|
41
|
+
if (typeof value === 'number' || typeof value === 'boolean') {
|
|
42
|
+
if (recursive) {
|
|
43
|
+
return value;
|
|
26
44
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
45
|
+
return String(value);
|
|
46
|
+
}
|
|
47
|
+
if (!recursive) {
|
|
48
|
+
const str = JSON.stringify(value);
|
|
49
|
+
if (str.length <= maxLength) {
|
|
50
|
+
return str;
|
|
32
51
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
52
|
+
return `${str.substring(0, maxLength)}${suffix}`;
|
|
53
|
+
}
|
|
54
|
+
if (Array.isArray(value)) {
|
|
55
|
+
return value.map(item => truncate(item, maxLength, suffix, true));
|
|
56
|
+
}
|
|
57
|
+
if (typeof value === 'object') {
|
|
58
|
+
return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, truncate(v, maxLength, suffix, true)]));
|
|
59
|
+
}
|
|
60
|
+
return value;
|
|
61
|
+
};
|
|
62
|
+
const truncateShort = (value) => {
|
|
63
|
+
return truncate(value, TRUNCATION.SHORT, TRUNCATION.SUFFIX, false);
|
|
64
|
+
};
|
|
65
|
+
const truncateRecursive = (value) => {
|
|
66
|
+
return truncate(value, TRUNCATION.STANDARD, TRUNCATION.SUFFIX_VERBOSE, true);
|
|
67
|
+
};
|
|
68
|
+
const formatPhase = (phase) => {
|
|
69
|
+
const phaseMap = {
|
|
70
|
+
start: '[START]',
|
|
71
|
+
end: '[END]',
|
|
72
|
+
error: '[ERROR]'
|
|
73
|
+
};
|
|
74
|
+
return phaseMap[phase] ?? phase;
|
|
75
|
+
};
|
|
76
|
+
const getNodeName = (node) => {
|
|
77
|
+
return node.name || node.workflowName || node.stepName || node.activityName || '';
|
|
78
|
+
};
|
|
79
|
+
const getNodeKind = (node) => {
|
|
80
|
+
return node.kind || node.type || '';
|
|
81
|
+
};
|
|
82
|
+
const getEventName = (event) => {
|
|
83
|
+
const { kind, workflowName, details } = event;
|
|
84
|
+
if (kind === 'workflow') {
|
|
85
|
+
return `Workflow: ${workflowName}`;
|
|
86
|
+
}
|
|
87
|
+
if (kind === 'activity') {
|
|
88
|
+
return `Activity: ${details?.activityName || 'unknown'}`;
|
|
89
|
+
}
|
|
90
|
+
if (kind === 'step') {
|
|
91
|
+
return `Step: ${details?.stepName || details?.name || 'unknown'}`;
|
|
92
|
+
}
|
|
93
|
+
return kind || 'Unknown Event';
|
|
94
|
+
};
|
|
95
|
+
const colorizeByKind = (kind, text) => {
|
|
96
|
+
if (kind === 'workflow') {
|
|
97
|
+
return colors.workflowHeader(text);
|
|
98
|
+
}
|
|
99
|
+
if (kind === 'internal_step') {
|
|
100
|
+
return colors.internalStep(text);
|
|
101
|
+
}
|
|
102
|
+
if (kind === 'step' || kind === 'activity') {
|
|
103
|
+
return colors.stepHeader(text);
|
|
104
|
+
}
|
|
105
|
+
return text;
|
|
106
|
+
};
|
|
107
|
+
const getStatusIndicator = (node) => {
|
|
108
|
+
if (node.phase === 'error' || node.status === 'failed') {
|
|
109
|
+
return colors.error('[FAILED]');
|
|
110
|
+
}
|
|
111
|
+
if (node.phase === 'end' || node.status === 'completed') {
|
|
112
|
+
return colors.success('[COMPLETED]');
|
|
113
|
+
}
|
|
114
|
+
if (node.status === 'running') {
|
|
115
|
+
return colors.label('[RUNNING]');
|
|
116
|
+
}
|
|
117
|
+
return '';
|
|
118
|
+
};
|
|
119
|
+
const getDebugNodeInfo = (node) => {
|
|
120
|
+
if (typeof node === 'string') {
|
|
121
|
+
return node;
|
|
122
|
+
}
|
|
123
|
+
if (typeof node !== 'object' || node === null) {
|
|
124
|
+
return String(node);
|
|
125
|
+
}
|
|
126
|
+
const debugNode = node;
|
|
127
|
+
const kind = getNodeKind(debugNode);
|
|
128
|
+
const name = getNodeName(debugNode);
|
|
129
|
+
const status = getStatusIndicator(debugNode);
|
|
130
|
+
const parts = [];
|
|
131
|
+
if (kind) {
|
|
132
|
+
parts.push(colorizeByKind(kind, `[${kind}]`));
|
|
133
|
+
}
|
|
134
|
+
if (name) {
|
|
135
|
+
parts.push(colorizeByKind(kind, name));
|
|
136
|
+
}
|
|
137
|
+
if (status) {
|
|
138
|
+
parts.push(status);
|
|
139
|
+
}
|
|
140
|
+
if (parts.length === 0) {
|
|
141
|
+
const keys = Object.keys(debugNode).filter(k => k !== 'children' && k !== 'parent');
|
|
142
|
+
if (keys.length > 0) {
|
|
143
|
+
return `Node {${keys.slice(0, 3).join(', ')}${keys.length > 3 ? ', ...' : ''}}`;
|
|
37
144
|
}
|
|
38
|
-
return
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
145
|
+
return 'Node';
|
|
146
|
+
}
|
|
147
|
+
return parts.join(' ');
|
|
148
|
+
};
|
|
149
|
+
const extractNodeInfo = (node) => {
|
|
150
|
+
if (isTraceEvent(node)) {
|
|
151
|
+
return {
|
|
152
|
+
name: getEventName(node),
|
|
153
|
+
phase: formatPhase(node.phase),
|
|
154
|
+
duration: node.duration ? ` (${formatDuration(node.duration)})` : ''
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
return {
|
|
158
|
+
name: getDebugNodeInfo(node),
|
|
159
|
+
phase: node.phase ? formatPhase(node.phase) : '',
|
|
160
|
+
duration: node.duration ? ` (${formatDuration(node.duration)})` : ''
|
|
161
|
+
};
|
|
162
|
+
};
|
|
163
|
+
const formatDetails = (details) => {
|
|
164
|
+
if (!details) {
|
|
165
|
+
return '-';
|
|
166
|
+
}
|
|
167
|
+
if (typeof details === 'string') {
|
|
168
|
+
return details;
|
|
169
|
+
}
|
|
170
|
+
const info = [];
|
|
171
|
+
if (details.input) {
|
|
172
|
+
info.push(`Input: ${truncateShort(details.input)}`);
|
|
173
|
+
}
|
|
174
|
+
if (details.output) {
|
|
175
|
+
info.push(`Output: ${truncateShort(details.output)}`);
|
|
176
|
+
}
|
|
177
|
+
if (details.activityName) {
|
|
178
|
+
info.push(`Activity: ${details.activityName}`);
|
|
179
|
+
}
|
|
180
|
+
if (details.stepName || details.name) {
|
|
181
|
+
info.push(`Step: ${details.stepName || details.name}`);
|
|
182
|
+
}
|
|
183
|
+
if (info.length > 0) {
|
|
184
|
+
return info.join(', ');
|
|
185
|
+
}
|
|
186
|
+
return truncateShort(details);
|
|
187
|
+
};
|
|
188
|
+
const formatTreeDetails = (details, depth) => {
|
|
189
|
+
const indent = ' '.repeat(depth);
|
|
190
|
+
const lines = [];
|
|
191
|
+
if (details.input !== null && details.input !== undefined) {
|
|
192
|
+
lines.push(`${indent}Input: ${truncateShort(details.input)}`);
|
|
193
|
+
}
|
|
194
|
+
if (details.output !== null && details.output !== undefined) {
|
|
195
|
+
lines.push(`${indent}Output: ${truncateShort(details.output)}`);
|
|
196
|
+
}
|
|
197
|
+
return lines;
|
|
198
|
+
};
|
|
199
|
+
const formatValueWithIndent = (value, indentPrefix) => {
|
|
200
|
+
if (value === null || value === undefined) {
|
|
201
|
+
return String(value);
|
|
202
|
+
}
|
|
203
|
+
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
|
|
204
|
+
return truncate(value, TRUNCATION.STANDARD, TRUNCATION.SUFFIX_VERBOSE, false);
|
|
205
|
+
}
|
|
206
|
+
const truncated = truncateRecursive(value);
|
|
207
|
+
const jsonStr = JSON.stringify(truncated, null, 2);
|
|
208
|
+
const lines = jsonStr.split('\n');
|
|
209
|
+
if (lines.length <= 1) {
|
|
210
|
+
return jsonStr;
|
|
211
|
+
}
|
|
212
|
+
return lines.map((line, i) => {
|
|
213
|
+
if (i === 0) {
|
|
214
|
+
return line;
|
|
51
215
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
216
|
+
return indentPrefix + line;
|
|
217
|
+
}).join('\n');
|
|
218
|
+
};
|
|
219
|
+
const formatHeader = (root) => {
|
|
220
|
+
const isTrace = isTraceEvent(root);
|
|
221
|
+
const debugRoot = root;
|
|
222
|
+
const workflowName = isTrace ? root.workflowName : (getNodeName(debugRoot) || 'Unknown');
|
|
223
|
+
const workflowId = isTrace ? root.workflowId : 'N/A';
|
|
224
|
+
const timestamp = isTrace ? root.timestamp : debugRoot.startTime;
|
|
225
|
+
const phase = isTrace ? root.phase : debugRoot.phase;
|
|
226
|
+
const status = !isTrace ? debugRoot.status : undefined;
|
|
227
|
+
const lines = [
|
|
228
|
+
HEADER_DIVIDER,
|
|
229
|
+
`Workflow: ${workflowName}`,
|
|
230
|
+
`Workflow ID: ${workflowId}`
|
|
231
|
+
];
|
|
232
|
+
if (isValidTimestamp(timestamp)) {
|
|
233
|
+
lines.push(`Start Time: ${new Date(timestamp).toISOString()}`);
|
|
234
|
+
}
|
|
235
|
+
if (root.duration) {
|
|
236
|
+
lines.push(`Duration: ${formatDuration(root.duration)}`);
|
|
237
|
+
}
|
|
238
|
+
if ((phase === 'error' || status === 'failed') && root.error) {
|
|
239
|
+
lines.push('Status: Failed', `Error: ${getErrorMessage(root.error)}`);
|
|
240
|
+
}
|
|
241
|
+
else if (phase === 'end' || status === 'completed') {
|
|
242
|
+
lines.push('Status: Completed');
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
lines.push('Status: In Progress');
|
|
246
|
+
}
|
|
247
|
+
lines.push(HEADER_DIVIDER);
|
|
248
|
+
return lines.join('\n');
|
|
249
|
+
};
|
|
250
|
+
const formatEventsTable = (events) => {
|
|
251
|
+
const table = new Table({
|
|
252
|
+
head: ['Time', 'Event', 'Phase', 'Duration', 'Details'],
|
|
253
|
+
style: { head: ['cyan'] },
|
|
254
|
+
colWidths: [20, 25, 10, 12, null],
|
|
255
|
+
wordWrap: true
|
|
256
|
+
});
|
|
257
|
+
for (const event of events) {
|
|
258
|
+
table.push([
|
|
259
|
+
new Date(event.timestamp).toISOString().substring(11, 23),
|
|
260
|
+
getEventName(event),
|
|
261
|
+
formatPhase(event.phase),
|
|
262
|
+
event.duration ? formatDuration(event.duration) : '-',
|
|
263
|
+
formatDetails(event.details)
|
|
264
|
+
]);
|
|
265
|
+
}
|
|
266
|
+
return table.toString();
|
|
267
|
+
};
|
|
268
|
+
const formatTree = (node, depth) => {
|
|
269
|
+
const indent = ' '.repeat(depth);
|
|
270
|
+
const marker = depth === 0 ? '' : TREE_CHARS.BRANCH;
|
|
271
|
+
const info = extractNodeInfo(node);
|
|
272
|
+
const lines = [`${indent}${marker} ${info.name} ${info.phase}${info.duration}`];
|
|
273
|
+
if (node.error) {
|
|
274
|
+
lines.push(`${indent} ${TREE_CHARS.LAST.trim()} ERROR: ${getErrorMessage(node.error)}`);
|
|
275
|
+
}
|
|
276
|
+
if (node.details && typeof node.details === 'object') {
|
|
277
|
+
lines.push(...formatTreeDetails(node.details, depth + 1));
|
|
278
|
+
}
|
|
279
|
+
if (node.children && node.children.length > 0) {
|
|
280
|
+
for (const child of node.children) {
|
|
281
|
+
lines.push(...formatTree(child, depth + 1));
|
|
55
282
|
}
|
|
56
|
-
|
|
57
|
-
|
|
283
|
+
}
|
|
284
|
+
return lines;
|
|
285
|
+
};
|
|
286
|
+
const getDebugNodeDetails = (node, prefix) => {
|
|
287
|
+
if (typeof node !== 'object' || node === null) {
|
|
288
|
+
return [];
|
|
289
|
+
}
|
|
290
|
+
const details = [];
|
|
291
|
+
const startedAt = node.startedAt || node.timestamp;
|
|
292
|
+
const inputIndentPrefix = prefix + ' '.repeat(7);
|
|
293
|
+
const outputIndentPrefix = prefix + ' '.repeat(8);
|
|
294
|
+
if (isValidTimestamp(startedAt)) {
|
|
295
|
+
const startDate = new Date(startedAt);
|
|
296
|
+
if (!isNaN(startDate.getTime())) {
|
|
297
|
+
details.push(`${prefix}${colors.label('Started:')} ${startDate.toISOString()}`);
|
|
58
298
|
}
|
|
59
|
-
|
|
60
|
-
|
|
299
|
+
}
|
|
300
|
+
if (isValidTimestamp(node.endedAt)) {
|
|
301
|
+
const endDate = new Date(node.endedAt);
|
|
302
|
+
if (!isNaN(endDate.getTime())) {
|
|
303
|
+
details.push(`${prefix}${colors.label('Ended:')} ${endDate.toISOString()}`);
|
|
61
304
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
305
|
+
}
|
|
306
|
+
if (typeof node.startedAt === 'number' && typeof node.endedAt === 'number') {
|
|
307
|
+
details.push(`${prefix}${colors.label('Duration:')} ${formatDuration(node.endedAt - node.startedAt)}`);
|
|
308
|
+
}
|
|
309
|
+
else if (node.duration) {
|
|
310
|
+
details.push(`${prefix}${colors.label('Duration:')} ${formatDuration(node.duration)}`);
|
|
311
|
+
}
|
|
312
|
+
if (node.input !== null && node.input !== undefined) {
|
|
313
|
+
details.push(`${prefix}${colors.label('Input:')} ${formatValueWithIndent(node.input, inputIndentPrefix)}`);
|
|
314
|
+
}
|
|
315
|
+
if (node.output !== null && node.output !== undefined) {
|
|
316
|
+
details.push(`${prefix}${colors.label('Output:')} ${formatValueWithIndent(node.output, outputIndentPrefix)}`);
|
|
317
|
+
}
|
|
318
|
+
if (node.error) {
|
|
319
|
+
details.push(`${prefix}${colors.error('Error:')} ${colors.error(getErrorMessage(node.error))}`);
|
|
320
|
+
}
|
|
321
|
+
if (details.length > 0) {
|
|
322
|
+
details.push('');
|
|
323
|
+
}
|
|
324
|
+
return details;
|
|
325
|
+
};
|
|
326
|
+
const buildDebugTreeLines = (node, depth, isLast, prefix) => {
|
|
327
|
+
if (node === null || node === undefined) {
|
|
328
|
+
return [];
|
|
329
|
+
}
|
|
330
|
+
const isRoot = depth === 0;
|
|
331
|
+
const getConnector = () => {
|
|
332
|
+
if (isRoot) {
|
|
333
|
+
return '';
|
|
84
334
|
}
|
|
85
|
-
return
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
335
|
+
return isLast ? TREE_CHARS.LAST : TREE_CHARS.BRANCH;
|
|
336
|
+
};
|
|
337
|
+
const connector = getConnector();
|
|
338
|
+
const indent = isRoot ? '' : prefix + connector;
|
|
339
|
+
const lines = [indent + getDebugNodeInfo(node)];
|
|
340
|
+
const detailPrefix = isRoot ? ' ' : prefix + (isLast ? TREE_CHARS.DETAIL_LAST : TREE_CHARS.DETAIL_BRANCH);
|
|
341
|
+
if (typeof node === 'object' && node !== null) {
|
|
342
|
+
lines.push(...getDebugNodeDetails(node, detailPrefix));
|
|
343
|
+
}
|
|
344
|
+
const childPrefix = isRoot ? '' : prefix + (isLast ? TREE_CHARS.SPACE : TREE_CHARS.VERTICAL);
|
|
345
|
+
if (typeof node === 'object' && node !== null) {
|
|
346
|
+
const debugNode = node;
|
|
347
|
+
if (Array.isArray(debugNode.children)) {
|
|
348
|
+
debugNode.children.forEach((child, i) => {
|
|
349
|
+
const isLastChild = i === debugNode.children.length - 1;
|
|
350
|
+
lines.push(...buildDebugTreeLines(child, depth + 1, isLastChild, childPrefix));
|
|
351
|
+
});
|
|
102
352
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
353
|
+
}
|
|
354
|
+
return lines;
|
|
355
|
+
};
|
|
356
|
+
const formatAsText = (trace) => {
|
|
357
|
+
const output = [];
|
|
358
|
+
if (trace.root) {
|
|
359
|
+
output.push(formatHeader(trace.root), '');
|
|
360
|
+
}
|
|
361
|
+
if (trace.events && trace.events.length > 0) {
|
|
362
|
+
output.push('Execution Timeline:', formatEventsTable(trace.events), '');
|
|
363
|
+
}
|
|
364
|
+
if (trace.root) {
|
|
365
|
+
output.push('Execution Tree:', ...formatTree(trace.root, 0));
|
|
366
|
+
}
|
|
367
|
+
return output.join('\n');
|
|
368
|
+
};
|
|
369
|
+
export function format(traceData, outputFormat = 'text') {
|
|
370
|
+
const trace = typeof traceData === 'string' ? JSON.parse(traceData) : traceData;
|
|
371
|
+
if (outputFormat === 'json') {
|
|
372
|
+
return formatOutput(trace, 'json');
|
|
373
|
+
}
|
|
374
|
+
return formatAsText(trace);
|
|
375
|
+
}
|
|
376
|
+
export function getSummary(traceData) {
|
|
377
|
+
const trace = typeof traceData === 'string' ? JSON.parse(traceData) : traceData;
|
|
378
|
+
const stats = {
|
|
379
|
+
totalDuration: trace.root?.duration || 0,
|
|
380
|
+
totalEvents: trace.events?.length || 0,
|
|
381
|
+
totalSteps: 0,
|
|
382
|
+
totalActivities: 0,
|
|
383
|
+
hasErrors: false
|
|
384
|
+
};
|
|
385
|
+
if (trace.events) {
|
|
386
|
+
for (const event of trace.events) {
|
|
387
|
+
if (event.kind === 'step') {
|
|
388
|
+
stats.totalSteps++;
|
|
108
389
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
if (node.children && node.children.length > 0) {
|
|
112
|
-
for (const child of node.children) {
|
|
113
|
-
lines.push(this.formatTree(child, depth + 1));
|
|
390
|
+
if (event.kind === 'activity') {
|
|
391
|
+
stats.totalActivities++;
|
|
114
392
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
118
|
-
/**
|
|
119
|
-
* Get a readable name for an event
|
|
120
|
-
*/
|
|
121
|
-
getEventName(event) {
|
|
122
|
-
if (event.kind === 'workflow') {
|
|
123
|
-
return `Workflow: ${event.workflowName}`;
|
|
124
|
-
}
|
|
125
|
-
if (event.kind === 'activity') {
|
|
126
|
-
return `Activity: ${event.details?.activityName || 'unknown'}`;
|
|
127
|
-
}
|
|
128
|
-
if (event.kind === 'step') {
|
|
129
|
-
return `Step: ${event.details?.stepName || event.details?.name || 'unknown'}`;
|
|
130
|
-
}
|
|
131
|
-
return event.kind || 'Unknown Event';
|
|
132
|
-
}
|
|
133
|
-
/**
|
|
134
|
-
* Format the phase with icons
|
|
135
|
-
*/
|
|
136
|
-
formatPhase(phase) {
|
|
137
|
-
switch (phase) {
|
|
138
|
-
case 'start':
|
|
139
|
-
return '▶️';
|
|
140
|
-
case 'end':
|
|
141
|
-
return '✅';
|
|
142
|
-
case 'error':
|
|
143
|
-
return '❌';
|
|
144
|
-
default:
|
|
145
|
-
return phase;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
/**
|
|
149
|
-
* Format duration in human-readable format
|
|
150
|
-
*/
|
|
151
|
-
formatDuration(ms) {
|
|
152
|
-
if (ms < 1000) {
|
|
153
|
-
return `${ms}ms`;
|
|
154
|
-
}
|
|
155
|
-
if (ms < 60000) {
|
|
156
|
-
return `${(ms / 1000).toFixed(2)}s`;
|
|
157
|
-
}
|
|
158
|
-
const minutes = Math.floor(ms / 60000);
|
|
159
|
-
const seconds = ((ms % 60000) / 1000).toFixed(0);
|
|
160
|
-
return `${minutes}m ${seconds}s`;
|
|
161
|
-
}
|
|
162
|
-
/**
|
|
163
|
-
* Format error for display
|
|
164
|
-
*/
|
|
165
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
166
|
-
formatError(error) {
|
|
167
|
-
if (typeof error === 'string') {
|
|
168
|
-
return error;
|
|
169
|
-
}
|
|
170
|
-
if (error.message) {
|
|
171
|
-
return error.message;
|
|
172
|
-
}
|
|
173
|
-
return JSON.stringify(error);
|
|
174
|
-
}
|
|
175
|
-
/**
|
|
176
|
-
* Format details for table display
|
|
177
|
-
*/
|
|
178
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
179
|
-
formatDetails(details) {
|
|
180
|
-
if (!details) {
|
|
181
|
-
return '-';
|
|
182
|
-
}
|
|
183
|
-
if (typeof details === 'string') {
|
|
184
|
-
return details;
|
|
185
|
-
}
|
|
186
|
-
// Extract key information
|
|
187
|
-
const info = [];
|
|
188
|
-
if (details.input) {
|
|
189
|
-
info.push(`Input: ${this.truncateValue(details.input)}`);
|
|
190
|
-
}
|
|
191
|
-
if (details.output) {
|
|
192
|
-
info.push(`Output: ${this.truncateValue(details.output)}`);
|
|
193
|
-
}
|
|
194
|
-
if (details.activityName) {
|
|
195
|
-
info.push(`Activity: ${details.activityName}`);
|
|
196
|
-
}
|
|
197
|
-
if (details.stepName || details.name) {
|
|
198
|
-
info.push(`Step: ${details.stepName || details.name}`);
|
|
199
|
-
}
|
|
200
|
-
return info.length > 0 ? info.join(', ') : JSON.stringify(details).substring(0, 50) + '...';
|
|
201
|
-
}
|
|
202
|
-
/**
|
|
203
|
-
* Format details for tree display
|
|
204
|
-
*/
|
|
205
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
206
|
-
formatTreeDetails(details, depth) {
|
|
207
|
-
const lines = [];
|
|
208
|
-
const indent = ' '.repeat(depth);
|
|
209
|
-
// Only show key details
|
|
210
|
-
if (details.input && details.input !== null) {
|
|
211
|
-
lines.push(`${indent}📥 Input: ${this.truncateValue(details.input)}`);
|
|
212
|
-
}
|
|
213
|
-
if (details.output && details.output !== null) {
|
|
214
|
-
lines.push(`${indent}📤 Output: ${this.truncateValue(details.output)}`);
|
|
215
|
-
}
|
|
216
|
-
return lines.join('\n');
|
|
217
|
-
}
|
|
218
|
-
/**
|
|
219
|
-
* Truncate long values for display
|
|
220
|
-
*/
|
|
221
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
222
|
-
truncateValue(value, maxLength = 50) {
|
|
223
|
-
const str = typeof value === 'string' ? value : JSON.stringify(value);
|
|
224
|
-
if (str.length <= maxLength) {
|
|
225
|
-
return str;
|
|
226
|
-
}
|
|
227
|
-
return str.substring(0, maxLength) + '...';
|
|
228
|
-
}
|
|
229
|
-
/**
|
|
230
|
-
* Get summary statistics from trace
|
|
231
|
-
*/
|
|
232
|
-
getSummary(traceData) {
|
|
233
|
-
const trace = typeof traceData === 'string' ? JSON.parse(traceData) : traceData;
|
|
234
|
-
const stats = {
|
|
235
|
-
totalDuration: trace.root?.duration || 0,
|
|
236
|
-
totalEvents: trace.events?.length || 0,
|
|
237
|
-
totalSteps: 0,
|
|
238
|
-
totalActivities: 0,
|
|
239
|
-
hasErrors: false
|
|
240
|
-
};
|
|
241
|
-
if (trace.events) {
|
|
242
|
-
for (const event of trace.events) {
|
|
243
|
-
if (event.kind === 'step') {
|
|
244
|
-
stats.totalSteps++;
|
|
245
|
-
}
|
|
246
|
-
if (event.kind === 'activity') {
|
|
247
|
-
stats.totalActivities++;
|
|
248
|
-
}
|
|
249
|
-
if (event.phase === 'error') {
|
|
250
|
-
stats.hasErrors = true;
|
|
251
|
-
}
|
|
393
|
+
if (event.phase === 'error') {
|
|
394
|
+
stats.hasErrors = true;
|
|
252
395
|
}
|
|
253
396
|
}
|
|
254
|
-
return stats;
|
|
255
397
|
}
|
|
398
|
+
return stats;
|
|
399
|
+
}
|
|
400
|
+
export function displayDebugTree(node) {
|
|
401
|
+
return buildDebugTreeLines(node, 0, false, '').join('\n');
|
|
256
402
|
}
|
|
257
|
-
export const traceFormatter = new TraceFormatter();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@output.ai/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "CLI for Output.ai workflow generation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -22,7 +22,6 @@
|
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"@anthropic-ai/claude-agent-sdk": "0.1.19",
|
|
25
|
-
"@aws-sdk/client-s3": "^3.689.0",
|
|
26
25
|
"@inquirer/prompts": "7.9.0",
|
|
27
26
|
"@oclif/core": "4.5.6",
|
|
28
27
|
"@oclif/plugin-help": "6.2.33",
|
|
@@ -31,6 +30,7 @@
|
|
|
31
30
|
"change-case": "5.4.4",
|
|
32
31
|
"cli-progress": "3.12.0",
|
|
33
32
|
"cli-table3": "0.6.5",
|
|
33
|
+
"date-fns": "4.1.0",
|
|
34
34
|
"dotenv": "16.4.7",
|
|
35
35
|
"handlebars": "4.7.8",
|
|
36
36
|
"json-schema-library": "10.3.0",
|