@output.ai/cli 0.3.0-dev.pr156.c8e7f40 → 0.3.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 +0 -53
- package/dist/api/generated/api.d.ts +2 -34
- package/dist/assets/docker/docker-compose-dev.yml +0 -1
- package/dist/commands/workflow/generate.js +3 -10
- package/dist/commands/workflow/generate.spec.js +6 -4
- 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/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/utils/template.spec.js +6 -0
- package/package.json +1 -2
- package/dist/commands/workflow/debug.d.ts +0 -15
- package/dist/commands/workflow/debug.js +0 -55
- package/dist/commands/workflow/debug.test.js +0 -75
- package/dist/services/trace_reader.d.ts +0 -8
- package/dist/services/trace_reader.js +0 -51
- package/dist/services/trace_reader.test.d.ts +0 -1
- package/dist/services/trace_reader.test.js +0 -163
- package/dist/utils/trace_formatter.d.ts +0 -27
- package/dist/utils/trace_formatter.js +0 -465
- /package/dist/{commands/workflow/debug.test.d.ts → services/workflow_generator.spec.d.ts} +0 -0
- /package/dist/templates/workflow/{prompt@v1.prompt.template → prompts/prompt@v1.prompt.template} +0 -0
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: workflow-quality
|
|
3
|
+
description: Use this agent when you need expert guidance on Output SDK implementation patterns, code quality, and best practices. Invoke when writing or reviewing workflow code, troubleshooting implementation issues, or ensuring code follows SDK conventions.
|
|
4
|
+
model: sonnet
|
|
5
|
+
color: green
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Output SDK Best Practices Agent
|
|
9
|
+
|
|
10
|
+
## Identity
|
|
11
|
+
|
|
12
|
+
You are an Output SDK implementation expert who ensures workflow code follows best practices, avoids common pitfalls, and adheres to SDK conventions. You focus on code quality, correctness, and maintainability.
|
|
13
|
+
|
|
14
|
+
## Context Retrieval
|
|
15
|
+
|
|
16
|
+
Use the `context-fetcher` subagent to efficiently retrieve:
|
|
17
|
+
- **Existing Patterns**: Find similar implementations in `src/workflows/*/`
|
|
18
|
+
- **Project Conventions**: Check `.outputai/AGENTS.md` for project-specific rules
|
|
19
|
+
|
|
20
|
+
Use the `prompt-writer` subagent for:
|
|
21
|
+
- Creating new `.prompt` files
|
|
22
|
+
- Reviewing or debugging prompt template syntax
|
|
23
|
+
- Understanding Liquid.js syntax and YAML frontmatter
|
|
24
|
+
|
|
25
|
+
## Core Expertise
|
|
26
|
+
|
|
27
|
+
- **Workflow Implementation**: Correct patterns for workflow definitions and orchestration
|
|
28
|
+
- **Step Design**: Proper step boundaries, I/O schemas, and retry policies
|
|
29
|
+
- **LLM Integration**: Prompt file format, generation functions, template syntax
|
|
30
|
+
- **HTTP Client**: Traced HTTP requests with proper error handling
|
|
31
|
+
- **Type Safety**: Zod schemas and TypeScript integration
|
|
32
|
+
- **Error Handling**: ValidationError, FatalError, and retry strategies
|
|
33
|
+
|
|
34
|
+
## Critical Rules
|
|
35
|
+
|
|
36
|
+
### Import Conventions
|
|
37
|
+
|
|
38
|
+
**CRITICAL**: Always import `z` from `@output.ai/core`, NEVER from `zod` directly:
|
|
39
|
+
```typescript
|
|
40
|
+
// Wrong
|
|
41
|
+
import { z } from 'zod';
|
|
42
|
+
|
|
43
|
+
// Correct
|
|
44
|
+
import { z } from '@output.ai/core';
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Workflow Determinism
|
|
48
|
+
|
|
49
|
+
Workflows must be deterministic. They can ONLY:
|
|
50
|
+
- Call steps and evaluators
|
|
51
|
+
- Use control flow (if/else, loops)
|
|
52
|
+
- Access input parameters
|
|
53
|
+
|
|
54
|
+
Workflows must NOT contain:
|
|
55
|
+
- Direct HTTP/API calls (wrap in steps)
|
|
56
|
+
- `Math.random()`, `Date.now()`, `crypto.randomUUID()`
|
|
57
|
+
- Dynamic imports
|
|
58
|
+
- File system operations
|
|
59
|
+
|
|
60
|
+
### Step Boundaries
|
|
61
|
+
|
|
62
|
+
All I/O operations must be wrapped in steps:
|
|
63
|
+
```typescript
|
|
64
|
+
// Wrong - I/O in workflow
|
|
65
|
+
export default workflow({
|
|
66
|
+
fn: async (input) => {
|
|
67
|
+
const data = await fetch('https://api.example.com'); // ❌
|
|
68
|
+
return { data };
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// Correct - I/O in step
|
|
73
|
+
export const fetchData = step({
|
|
74
|
+
name: 'fetchData',
|
|
75
|
+
fn: async (input) => {
|
|
76
|
+
const client = httpClient({ prefixUrl: 'https://api.example.com' });
|
|
77
|
+
return client.get('endpoint').json();
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### No Try-Catch Wrapping
|
|
83
|
+
|
|
84
|
+
Don't wrap step calls in try-catch blocks. Allow failures to propagate:
|
|
85
|
+
```typescript
|
|
86
|
+
// Wrong
|
|
87
|
+
fn: async (input) => {
|
|
88
|
+
try {
|
|
89
|
+
const result = await myStep(input);
|
|
90
|
+
return result;
|
|
91
|
+
} catch (error) {
|
|
92
|
+
throw new FatalError(error.message);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Correct
|
|
97
|
+
fn: async (input) => {
|
|
98
|
+
const result = await myStep(input);
|
|
99
|
+
return result;
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### HTTP Client Usage
|
|
104
|
+
|
|
105
|
+
Never use axios directly. Use `@output.ai/http`:
|
|
106
|
+
```typescript
|
|
107
|
+
import { httpClient } from '@output.ai/http';
|
|
108
|
+
|
|
109
|
+
const client = httpClient({
|
|
110
|
+
prefixUrl: 'https://api.example.com',
|
|
111
|
+
timeout: 30000,
|
|
112
|
+
retry: { limit: 3 }
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// GET request
|
|
116
|
+
const data = await client.get('endpoint').json();
|
|
117
|
+
|
|
118
|
+
// POST request
|
|
119
|
+
const result = await client.post('endpoint', { json: payload }).json();
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### LLM Integration
|
|
123
|
+
|
|
124
|
+
Never call LLM APIs directly. Use `@output.ai/llm`:
|
|
125
|
+
```typescript
|
|
126
|
+
import { generateText, generateObject, generateArray, generateEnum } from '@output.ai/llm';
|
|
127
|
+
|
|
128
|
+
// Text generation
|
|
129
|
+
const text = await generateText({
|
|
130
|
+
prompt: 'prompts/my_prompt@v1',
|
|
131
|
+
variables: { topic: 'AI' }
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// Structured output
|
|
135
|
+
const data = await generateObject({
|
|
136
|
+
prompt: 'prompts/extract@v1',
|
|
137
|
+
variables: { text },
|
|
138
|
+
schema: z.object({ title: z.string(), summary: z.string() })
|
|
139
|
+
});
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Schema Definitions
|
|
143
|
+
|
|
144
|
+
Define input/output schemas with Zod:
|
|
145
|
+
```typescript
|
|
146
|
+
import { step, z } from '@output.ai/core';
|
|
147
|
+
|
|
148
|
+
export const processData = step({
|
|
149
|
+
name: 'processData',
|
|
150
|
+
inputSchema: z.object({
|
|
151
|
+
id: z.string(),
|
|
152
|
+
count: z.number().optional()
|
|
153
|
+
}),
|
|
154
|
+
outputSchema: z.object({
|
|
155
|
+
result: z.string(),
|
|
156
|
+
processed: z.boolean()
|
|
157
|
+
}),
|
|
158
|
+
fn: async (input) => {
|
|
159
|
+
// input is typed as { id: string, count?: number }
|
|
160
|
+
return { result: input.id, processed: true };
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Retry Policies
|
|
166
|
+
|
|
167
|
+
Configure retry policies in step options:
|
|
168
|
+
```typescript
|
|
169
|
+
export const riskyStep = step({
|
|
170
|
+
name: 'riskyStep',
|
|
171
|
+
fn: async (input) => { /* ... */ },
|
|
172
|
+
options: {
|
|
173
|
+
retry: {
|
|
174
|
+
maximumAttempts: 3,
|
|
175
|
+
initialInterval: '1s',
|
|
176
|
+
maximumInterval: '10s',
|
|
177
|
+
backoffCoefficient: 2
|
|
178
|
+
},
|
|
179
|
+
startToCloseTimeout: '30s'
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Error Types
|
|
185
|
+
|
|
186
|
+
Use appropriate error types:
|
|
187
|
+
```typescript
|
|
188
|
+
import { FatalError, ValidationError } from '@output.ai/core';
|
|
189
|
+
|
|
190
|
+
// Non-retryable error (workflow fails immediately)
|
|
191
|
+
throw new FatalError('Critical failure - do not retry');
|
|
192
|
+
|
|
193
|
+
// Validation error (schema/input validation)
|
|
194
|
+
throw new ValidationError('Invalid input format');
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## File Structure
|
|
198
|
+
|
|
199
|
+
```
|
|
200
|
+
src/workflows/{name}/
|
|
201
|
+
workflow.ts # Workflow definition (orchestration only)
|
|
202
|
+
steps.ts # Step definitions (all I/O here)
|
|
203
|
+
evaluators.ts # Evaluators (optional)
|
|
204
|
+
types.ts # Shared types and schemas (optional)
|
|
205
|
+
prompts/ # Prompt files directory
|
|
206
|
+
name@v1.prompt # Versioned prompt templates
|
|
207
|
+
scenarios/ # Test scenarios directory
|
|
208
|
+
basic.json # Common run case examples
|
|
209
|
+
edge_cases.json # Edge case scenarios
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Allowed Imports in Workflows
|
|
213
|
+
|
|
214
|
+
Workflows can only import from:
|
|
215
|
+
- `steps.ts`, `evaluators.ts`, `shared_steps.ts`
|
|
216
|
+
- Whitelisted: `types.ts`, `consts.ts`, `utils.ts`, `variables.ts`, `tools.ts`
|
|
217
|
+
|
|
218
|
+
## Common Pitfalls
|
|
219
|
+
|
|
220
|
+
### 1. Zod Import Source
|
|
221
|
+
Always import `z` from `@output.ai/core`, never from `zod` directly. Different `z` instances create incompatible schemas.
|
|
222
|
+
|
|
223
|
+
### 2. Direct API Calls
|
|
224
|
+
Never make HTTP/LLM calls directly in workflows. Always wrap in steps.
|
|
225
|
+
|
|
226
|
+
### 3. Non-Deterministic Operations
|
|
227
|
+
No `Math.random()`, `Date.now()`, or `crypto` in workflows.
|
|
228
|
+
|
|
229
|
+
### 4. Try-Catch Blocks
|
|
230
|
+
Don't wrap step calls in try-catch. Let failures propagate for proper retry handling.
|
|
231
|
+
|
|
232
|
+
### 5. Missing Schemas
|
|
233
|
+
Always define inputSchema and outputSchema for type safety and validation.
|
|
234
|
+
|
|
235
|
+
## Example Interaction
|
|
236
|
+
|
|
237
|
+
**User**: "My workflow is failing with a type error on the schema"
|
|
238
|
+
**Agent**: Check that you're importing `z` from `@output.ai/core`, not `zod`. Different `z` instances create incompatible schemas.
|
|
239
|
+
|
|
240
|
+
**User**: "How do I make an HTTP request in my workflow?"
|
|
241
|
+
**Agent**: Create a step that uses `@output.ai/http`. Never make HTTP calls directly in the workflow function.
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
*This agent specializes in Output SDK implementation best practices and code quality.*
|
|
@@ -25,7 +25,7 @@ Implement the workflow described in the plan document, following Output SDK patt
|
|
|
25
25
|
|
|
26
26
|
<process_flow>
|
|
27
27
|
|
|
28
|
-
<step number="1" name="plan_analysis">
|
|
28
|
+
<step number="1" name="plan_analysis" subagent="context-fetcher">
|
|
29
29
|
|
|
30
30
|
### Step 1: Plan Analysis
|
|
31
31
|
|
|
@@ -40,7 +40,7 @@ Read and understand the plan document.
|
|
|
40
40
|
|
|
41
41
|
</step>
|
|
42
42
|
|
|
43
|
-
<step number="2" name="workflow_implementation">
|
|
43
|
+
<step number="2" name="workflow_implementation" subagent="workflow-quality">
|
|
44
44
|
|
|
45
45
|
### Step 2: Workflow Implementation
|
|
46
46
|
|
|
@@ -85,7 +85,7 @@ export default workflow( {
|
|
|
85
85
|
|
|
86
86
|
</step>
|
|
87
87
|
|
|
88
|
-
<step number="3" name="steps_implementation">
|
|
88
|
+
<step number="3" name="steps_implementation" subagent="workflow-quality">
|
|
89
89
|
|
|
90
90
|
### Step 3: Steps Implementation
|
|
91
91
|
|
|
@@ -122,7 +122,7 @@ export const stepName = step( {
|
|
|
122
122
|
|
|
123
123
|
</step>
|
|
124
124
|
|
|
125
|
-
<step number="4" name="prompt_templates">
|
|
125
|
+
<step number="4" name="prompt_templates" subagent="prompt-writer">
|
|
126
126
|
|
|
127
127
|
### Step 4: Prompt Templates (if needed)
|
|
128
128
|
|
|
@@ -133,7 +133,7 @@ If the plan includes LLM-based steps, create prompt templates in `$3/prompts/`.
|
|
|
133
133
|
CREATE prompt_templates
|
|
134
134
|
UPDATE steps.ts to use loadPrompt and generateText
|
|
135
135
|
ELSE:
|
|
136
|
-
SKIP to step
|
|
136
|
+
SKIP to step 6
|
|
137
137
|
</decision_tree>
|
|
138
138
|
|
|
139
139
|
<llm_step_template>
|
|
@@ -197,9 +197,51 @@ Update `$3/README.md` with workflow-specific documentation.
|
|
|
197
197
|
|
|
198
198
|
</step>
|
|
199
199
|
|
|
200
|
-
<step number="6" name="
|
|
200
|
+
<step number="6" name="scenario_creation">
|
|
201
201
|
|
|
202
|
-
### Step 6:
|
|
202
|
+
### Step 6: Scenario File Creation
|
|
203
|
+
|
|
204
|
+
Create at least one scenario file in `$3/scenarios/` for testing the workflow.
|
|
205
|
+
|
|
206
|
+
<scenario_requirements>
|
|
207
|
+
- Create `scenarios/` directory if it doesn't exist
|
|
208
|
+
- Create `test_input.json` with valid example input matching the inputSchema
|
|
209
|
+
- Input values should be realistic and demonstrate the workflow's purpose
|
|
210
|
+
- JSON must be valid and parseable
|
|
211
|
+
</scenario_requirements>
|
|
212
|
+
|
|
213
|
+
<scenario_template>
|
|
214
|
+
```json
|
|
215
|
+
{
|
|
216
|
+
// Populate with example values matching inputSchema
|
|
217
|
+
// Use realistic test data that demonstrates the workflow
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
</scenario_template>
|
|
221
|
+
|
|
222
|
+
<example>
|
|
223
|
+
For a workflow with inputSchema:
|
|
224
|
+
```typescript
|
|
225
|
+
z.object({
|
|
226
|
+
topic: z.string(),
|
|
227
|
+
maxLength: z.number().optional()
|
|
228
|
+
})
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Create `scenarios/test_input.json`:
|
|
232
|
+
```json
|
|
233
|
+
{
|
|
234
|
+
"topic": "The history of artificial intelligence",
|
|
235
|
+
"maxLength": 500
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
</example>
|
|
239
|
+
|
|
240
|
+
</step>
|
|
241
|
+
|
|
242
|
+
<step number="7" name="validation" subagent="workflow-quality">
|
|
243
|
+
|
|
244
|
+
### Step 7: Implementation Validation
|
|
203
245
|
|
|
204
246
|
Verify the implementation is complete and correct.
|
|
205
247
|
|
|
@@ -212,13 +254,14 @@ Verify the implementation is complete and correct.
|
|
|
212
254
|
- README is updated with accurate information
|
|
213
255
|
- Code follows Output SDK patterns
|
|
214
256
|
- TypeScript types are properly defined
|
|
257
|
+
- Scenario file exists with valid example input
|
|
215
258
|
</validation_checklist>
|
|
216
259
|
|
|
217
260
|
</step>
|
|
218
261
|
|
|
219
|
-
<step number="
|
|
262
|
+
<step number="8" name="post_flight_check">
|
|
220
263
|
|
|
221
|
-
### Step
|
|
264
|
+
### Step 8: Post-Flight Check
|
|
222
265
|
|
|
223
266
|
Verify the implementation is ready for use.
|
|
224
267
|
|
|
@@ -25,7 +25,7 @@ Generate detailed specifications for implementation of a new workflow.
|
|
|
25
25
|
|
|
26
26
|
<process_flow>
|
|
27
27
|
|
|
28
|
-
<step number="1" name="context_gathering">
|
|
28
|
+
<step number="1" name="context_gathering" subagent="context-fetcher">
|
|
29
29
|
|
|
30
30
|
### Step 1: Context Gathering
|
|
31
31
|
|
|
@@ -63,7 +63,7 @@ Clarify scope boundaries and technical considerations by asking numbered questio
|
|
|
63
63
|
</decision_tree>
|
|
64
64
|
</step>
|
|
65
65
|
|
|
66
|
-
<step number="3" name="workflow_design">
|
|
66
|
+
<step number="3" name="workflow_design" subagent="workflow-quality">
|
|
67
67
|
|
|
68
68
|
### Step 3: Workflow Design
|
|
69
69
|
|
|
@@ -109,7 +109,7 @@ export default workflow( {
|
|
|
109
109
|
```
|
|
110
110
|
</workflow_template>
|
|
111
111
|
|
|
112
|
-
<step number="4" name="step_design">
|
|
112
|
+
<step number="4" name="step_design" subagent="workflow-quality">
|
|
113
113
|
### Step 4: Step Design
|
|
114
114
|
|
|
115
115
|
Design the steps with clear boundaries.
|
|
@@ -130,7 +130,7 @@ export const sumValues = step( {
|
|
|
130
130
|
|
|
131
131
|
</step>
|
|
132
132
|
|
|
133
|
-
<step number="5" name="prompt_engineering">
|
|
133
|
+
<step number="5" name="prompt_engineering" subagent="prompt-writer">
|
|
134
134
|
|
|
135
135
|
### Step 5: Prompt Engineering
|
|
136
136
|
|
|
@@ -12,12 +12,12 @@
|
|
|
12
12
|
"dependencies": {
|
|
13
13
|
"@output.ai/core": "^{{coreVersion}}",
|
|
14
14
|
"@output.ai/llm": "^{{llmVersion}}",
|
|
15
|
-
"@output.ai/http": "^{{httpVersion}}"
|
|
16
|
-
"copyfiles": "^{{cliVersion}}"
|
|
15
|
+
"@output.ai/http": "^{{httpVersion}}"
|
|
17
16
|
},
|
|
18
17
|
"devDependencies": {
|
|
19
18
|
"@output.ai/cli": "latest",
|
|
20
19
|
"@types/node": "^22.0.0",
|
|
20
|
+
"copyfiles": "^1.0.0",
|
|
21
21
|
"typescript": "^5.7.0"
|
|
22
22
|
},
|
|
23
23
|
"engines": {
|
|
@@ -53,6 +53,12 @@ describe('Template Utilities', () => {
|
|
|
53
53
|
expect(processTemplate(template, variables))
|
|
54
54
|
.toBe('myWorkflowName and MyWorkflowName');
|
|
55
55
|
});
|
|
56
|
+
it('should preserve escaped curly braces for Liquid.js examples', () => {
|
|
57
|
+
const template = 'Use Liquid: \\{{ variable }} and {% if %}...{% endif %}';
|
|
58
|
+
const variables = {};
|
|
59
|
+
expect(processTemplate(template, variables))
|
|
60
|
+
.toBe('Use Liquid: {{ variable }} and {% if %}...{% endif %}');
|
|
61
|
+
});
|
|
56
62
|
});
|
|
57
63
|
describe('prepareTemplateVariables', () => {
|
|
58
64
|
it('should prepare variables from workflow name and description', () => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@output.ai/cli",
|
|
3
|
-
"version": "0.3.0
|
|
3
|
+
"version": "0.3.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",
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { Command } from '@oclif/core';
|
|
2
|
-
export default class WorkflowDebug extends Command {
|
|
3
|
-
static description: string;
|
|
4
|
-
static examples: string[];
|
|
5
|
-
static args: {
|
|
6
|
-
workflowId: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
7
|
-
};
|
|
8
|
-
static flags: {
|
|
9
|
-
format: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
10
|
-
};
|
|
11
|
-
run(): Promise<void>;
|
|
12
|
-
private getTrace;
|
|
13
|
-
private outputJson;
|
|
14
|
-
private displayTextTrace;
|
|
15
|
-
}
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { Args, Command, Flags } from '@oclif/core';
|
|
2
|
-
import { OUTPUT_FORMAT } from '#utils/constants.js';
|
|
3
|
-
import { traceFormatter } from '#utils/trace_formatter.js';
|
|
4
|
-
import { findTraceFile, readTraceFile } from '#services/trace_reader.js';
|
|
5
|
-
export default class WorkflowDebug extends Command {
|
|
6
|
-
static description = 'Get and display workflow execution trace for debugging';
|
|
7
|
-
static examples = [
|
|
8
|
-
'<%= config.bin %> <%= command.id %> wf-12345',
|
|
9
|
-
'<%= config.bin %> <%= command.id %> wf-12345 --format json',
|
|
10
|
-
'<%= config.bin %> <%= command.id %> wf-12345 --format text'
|
|
11
|
-
];
|
|
12
|
-
static args = {
|
|
13
|
-
workflowId: Args.string({
|
|
14
|
-
description: 'The workflow ID to debug',
|
|
15
|
-
required: true
|
|
16
|
-
})
|
|
17
|
-
};
|
|
18
|
-
static flags = {
|
|
19
|
-
format: Flags.string({
|
|
20
|
-
char: 'f',
|
|
21
|
-
description: 'Output format',
|
|
22
|
-
options: [OUTPUT_FORMAT.JSON, OUTPUT_FORMAT.TEXT],
|
|
23
|
-
default: OUTPUT_FORMAT.TEXT
|
|
24
|
-
})
|
|
25
|
-
};
|
|
26
|
-
async run() {
|
|
27
|
-
const { args, flags } = await this.parse(WorkflowDebug);
|
|
28
|
-
const isJsonFormat = flags.format === OUTPUT_FORMAT.JSON;
|
|
29
|
-
if (!isJsonFormat) {
|
|
30
|
-
this.log(`Fetching debug information for workflow: ${args.workflowId}...`);
|
|
31
|
-
}
|
|
32
|
-
const traceData = await this.getTrace(args.workflowId);
|
|
33
|
-
// Output based on format
|
|
34
|
-
if (isJsonFormat) {
|
|
35
|
-
this.outputJson(traceData);
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
// Display text format
|
|
39
|
-
this.displayTextTrace(traceData);
|
|
40
|
-
}
|
|
41
|
-
async getTrace(workflowId) {
|
|
42
|
-
const tracePath = await findTraceFile(workflowId);
|
|
43
|
-
return readTraceFile(tracePath);
|
|
44
|
-
}
|
|
45
|
-
outputJson(data) {
|
|
46
|
-
this.log(JSON.stringify(data, null, 2));
|
|
47
|
-
}
|
|
48
|
-
displayTextTrace(traceData) {
|
|
49
|
-
this.log('\nTrace Log:');
|
|
50
|
-
this.log('─'.repeat(80));
|
|
51
|
-
this.log(traceFormatter.displayDebugTree(traceData));
|
|
52
|
-
this.log('\n' + '─'.repeat(80));
|
|
53
|
-
this.log('Tip: Use --format json for complete verbose output');
|
|
54
|
-
}
|
|
55
|
-
}
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
-
// Mock the TraceReader service
|
|
3
|
-
vi.mock('../../services/trace_reader.js', () => ({
|
|
4
|
-
TraceReader: vi.fn().mockImplementation(() => ({
|
|
5
|
-
findTraceFile: vi.fn(),
|
|
6
|
-
readTraceFile: vi.fn()
|
|
7
|
-
}))
|
|
8
|
-
}));
|
|
9
|
-
// Mock the utilities
|
|
10
|
-
vi.mock('../../utils/trace_formatter.js', () => ({
|
|
11
|
-
traceFormatter: {
|
|
12
|
-
displayDebugTree: vi.fn()
|
|
13
|
-
}
|
|
14
|
-
}));
|
|
15
|
-
describe('workflow debug command', () => {
|
|
16
|
-
beforeEach(() => {
|
|
17
|
-
vi.clearAllMocks();
|
|
18
|
-
});
|
|
19
|
-
describe('command definition', () => {
|
|
20
|
-
it('should export a valid OCLIF command', async () => {
|
|
21
|
-
const WorkflowDebug = (await import('./debug.js')).default;
|
|
22
|
-
expect(WorkflowDebug).toBeDefined();
|
|
23
|
-
expect(WorkflowDebug.description).toContain('Get and display workflow execution trace for debugging');
|
|
24
|
-
expect(WorkflowDebug.args).toHaveProperty('workflowId');
|
|
25
|
-
expect(WorkflowDebug.flags).toHaveProperty('format');
|
|
26
|
-
});
|
|
27
|
-
it('should have correct flag configuration', async () => {
|
|
28
|
-
const WorkflowDebug = (await import('./debug.js')).default;
|
|
29
|
-
// Format flag
|
|
30
|
-
expect(WorkflowDebug.flags.format.options).toEqual(['json', 'text']);
|
|
31
|
-
expect(WorkflowDebug.flags.format.default).toBe('text');
|
|
32
|
-
});
|
|
33
|
-
it('should have correct examples', async () => {
|
|
34
|
-
const WorkflowDebug = (await import('./debug.js')).default;
|
|
35
|
-
expect(WorkflowDebug.examples).toBeDefined();
|
|
36
|
-
expect(WorkflowDebug.examples.length).toBeGreaterThan(0);
|
|
37
|
-
});
|
|
38
|
-
});
|
|
39
|
-
describe('run method', () => {
|
|
40
|
-
beforeEach(() => {
|
|
41
|
-
// Clear mocks before each test
|
|
42
|
-
vi.clearAllMocks();
|
|
43
|
-
});
|
|
44
|
-
it('should fetch and display trace when available', async () => {
|
|
45
|
-
// This test requires OCLIF framework initialization which is complex to mock
|
|
46
|
-
// The functionality is tested through manual testing and the build process
|
|
47
|
-
expect(true).toBe(true);
|
|
48
|
-
});
|
|
49
|
-
it('should output JSON when --format json is set', async () => {
|
|
50
|
-
// This test requires OCLIF framework initialization which is complex to mock
|
|
51
|
-
// The functionality is tested through manual testing and the build process
|
|
52
|
-
expect(true).toBe(true);
|
|
53
|
-
});
|
|
54
|
-
it('should handle trace file not found error', async () => {
|
|
55
|
-
// This test requires mocking TraceReader instance methods
|
|
56
|
-
// The functionality is tested through manual testing and the build process
|
|
57
|
-
expect(true).toBe(true);
|
|
58
|
-
});
|
|
59
|
-
it('should handle file read errors', async () => {
|
|
60
|
-
// This test requires mocking TraceReader instance methods
|
|
61
|
-
// The functionality is tested through manual testing and the build process
|
|
62
|
-
expect(true).toBe(true);
|
|
63
|
-
});
|
|
64
|
-
it('should display trace in text format by default', async () => {
|
|
65
|
-
// This test requires OCLIF framework initialization which is complex to mock
|
|
66
|
-
// The functionality is tested through manual testing and the build process
|
|
67
|
-
expect(true).toBe(true);
|
|
68
|
-
});
|
|
69
|
-
it('should display tip message for verbose output', async () => {
|
|
70
|
-
// This test requires OCLIF framework initialization which is complex to mock
|
|
71
|
-
// The functionality is tested through manual testing and the build process
|
|
72
|
-
expect(true).toBe(true);
|
|
73
|
-
});
|
|
74
|
-
});
|
|
75
|
-
});
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { readFile, stat } from 'node:fs/promises';
|
|
2
|
-
import { getWorkflowIdOutput } from '#api/generated/api.js';
|
|
3
|
-
/**
|
|
4
|
-
* Check if a file exists
|
|
5
|
-
*/
|
|
6
|
-
async function fileExists(path) {
|
|
7
|
-
try {
|
|
8
|
-
await stat(path);
|
|
9
|
-
return true;
|
|
10
|
-
}
|
|
11
|
-
catch {
|
|
12
|
-
return false;
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* Find trace file from workflow metadata
|
|
17
|
-
*/
|
|
18
|
-
export async function findTraceFile(workflowId) {
|
|
19
|
-
const response = await getWorkflowIdOutput(workflowId);
|
|
20
|
-
// Check if we got a successful response
|
|
21
|
-
if (response.status !== 200) {
|
|
22
|
-
throw new Error(`Failed to get workflow output for ${workflowId}`);
|
|
23
|
-
}
|
|
24
|
-
const tracePath = response.data.trace?.destinations?.local;
|
|
25
|
-
if (!tracePath) {
|
|
26
|
-
throw new Error(`No trace file path found for workflow ${workflowId}`);
|
|
27
|
-
}
|
|
28
|
-
if (!await fileExists(tracePath)) {
|
|
29
|
-
throw new Error(`Trace file not found at path: ${tracePath}`);
|
|
30
|
-
}
|
|
31
|
-
return tracePath;
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Read and parse trace file
|
|
35
|
-
*/
|
|
36
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
37
|
-
export async function readTraceFile(path) {
|
|
38
|
-
try {
|
|
39
|
-
const content = await readFile(path, 'utf-8');
|
|
40
|
-
return JSON.parse(content);
|
|
41
|
-
}
|
|
42
|
-
catch (error) {
|
|
43
|
-
if (error && typeof error === 'object' && 'code' in error && error.code === 'ENOENT') {
|
|
44
|
-
throw new Error(`Trace file not found at path: ${path}`);
|
|
45
|
-
}
|
|
46
|
-
if (error instanceof SyntaxError) {
|
|
47
|
-
throw new Error(`Invalid JSON in trace file: ${path}`);
|
|
48
|
-
}
|
|
49
|
-
throw error;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|