@output.ai/cli 0.4.1 → 0.5.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 +6 -8
- package/dist/api/generated/api.d.ts +66 -0
- package/dist/api/generated/api.js +26 -0
- package/dist/assets/docker/docker-compose-dev.yml +9 -2
- package/dist/commands/workflow/runs/list.d.ts +14 -0
- package/dist/commands/workflow/runs/list.js +104 -0
- package/dist/services/coding_agents.js +180 -8
- package/dist/services/coding_agents.spec.js +54 -11
- package/dist/services/workflow_runs.d.ts +14 -0
- package/dist/services/workflow_runs.js +24 -0
- package/dist/templates/agent_instructions/AGENTS.md.template +13 -7
- package/dist/templates/agent_instructions/agents/{context_fetcher.md.template → workflow_context_fetcher.md.template} +1 -1
- package/dist/templates/agent_instructions/agents/workflow_debugger.md.template +98 -0
- package/dist/templates/agent_instructions/agents/workflow_planner.md.template +3 -3
- package/dist/templates/agent_instructions/agents/{prompt_writer.md.template → workflow_prompt_writer.md.template} +1 -1
- package/dist/templates/agent_instructions/agents/workflow_quality.md.template +2 -2
- package/dist/templates/agent_instructions/commands/build_workflow.md.template +2 -2
- package/dist/templates/agent_instructions/commands/debug_workflow.md.template +198 -0
- package/dist/templates/agent_instructions/commands/plan_workflow.md.template +2 -2
- package/dist/templates/agent_instructions/skills/output-error-direct-io/SKILL.md.template +249 -0
- package/dist/templates/agent_instructions/skills/output-error-http-client/SKILL.md.template +298 -0
- package/dist/templates/agent_instructions/skills/output-error-missing-schemas/SKILL.md.template +265 -0
- package/dist/templates/agent_instructions/skills/output-error-nondeterminism/SKILL.md.template +252 -0
- package/dist/templates/agent_instructions/skills/output-error-try-catch/SKILL.md.template +226 -0
- package/dist/templates/agent_instructions/skills/output-error-zod-import/SKILL.md.template +209 -0
- package/dist/templates/agent_instructions/skills/output-services-check/SKILL.md.template +128 -0
- package/dist/templates/agent_instructions/skills/output-workflow-list/SKILL.md.template +117 -0
- package/dist/templates/agent_instructions/skills/output-workflow-result/SKILL.md.template +199 -0
- package/dist/templates/agent_instructions/skills/output-workflow-run/SKILL.md.template +228 -0
- package/dist/templates/agent_instructions/skills/output-workflow-runs-list/SKILL.md.template +141 -0
- package/dist/templates/agent_instructions/skills/output-workflow-start/SKILL.md.template +201 -0
- package/dist/templates/agent_instructions/skills/output-workflow-status/SKILL.md.template +151 -0
- package/dist/templates/agent_instructions/skills/output-workflow-stop/SKILL.md.template +164 -0
- package/dist/templates/agent_instructions/skills/output-workflow-trace/SKILL.md.template +134 -0
- package/dist/templates/project/README.md.template +1 -1
- package/dist/templates/project/package.json.template +3 -2
- package/dist/utils/date_formatter.d.ts +15 -0
- package/dist/utils/date_formatter.js +31 -1
- package/package.json +1 -1
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: output-error-http-client
|
|
3
|
+
description: Fix HTTP client misuse in Output SDK steps. Use when seeing untraced requests, missing error details, axios-related errors, or when HTTP calls aren't being properly logged and retried.
|
|
4
|
+
allowed-tools: [Bash, Read]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Fix HTTP Client Misuse
|
|
8
|
+
|
|
9
|
+
## Overview
|
|
10
|
+
|
|
11
|
+
This skill helps diagnose and fix issues caused by using axios, fetch, or other HTTP clients directly instead of Output SDK's `httpClient` from `@output.ai/http`. The Output SDK client provides tracing, automatic retries, and better error handling.
|
|
12
|
+
|
|
13
|
+
## When to Use This Skill
|
|
14
|
+
|
|
15
|
+
You're seeing:
|
|
16
|
+
- Untraced HTTP requests (not appearing in workflow traces)
|
|
17
|
+
- Missing error details for failed requests
|
|
18
|
+
- axios-related errors or import issues
|
|
19
|
+
- Retries not working for HTTP failures
|
|
20
|
+
- Inconsistent timeout behavior
|
|
21
|
+
|
|
22
|
+
## Root Cause
|
|
23
|
+
|
|
24
|
+
Using axios, fetch, or other HTTP clients directly bypasses Output SDK's:
|
|
25
|
+
- **Request/response tracing**: Calls aren't logged in workflow traces
|
|
26
|
+
- **Automatic retries**: Failed requests aren't retried
|
|
27
|
+
- **Error standardization**: Error formats may be inconsistent
|
|
28
|
+
- **Timeout handling**: Timeouts may not integrate with step timeouts
|
|
29
|
+
|
|
30
|
+
## Symptoms
|
|
31
|
+
|
|
32
|
+
### Using axios Directly
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
// WRONG: Using axios
|
|
36
|
+
import axios from 'axios';
|
|
37
|
+
|
|
38
|
+
export const fetchData = step({
|
|
39
|
+
name: 'fetchData',
|
|
40
|
+
fn: async (input) => {
|
|
41
|
+
const response = await axios.get('https://api.example.com/data');
|
|
42
|
+
return response.data;
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Using fetch Directly
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
// WRONG: Using fetch
|
|
51
|
+
export const fetchData = step({
|
|
52
|
+
name: 'fetchData',
|
|
53
|
+
fn: async (input) => {
|
|
54
|
+
const response = await fetch('https://api.example.com/data');
|
|
55
|
+
return response.json();
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Solution
|
|
61
|
+
|
|
62
|
+
Use `httpClient` from `@output.ai/http`:
|
|
63
|
+
|
|
64
|
+
### Basic Usage
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
import { z, step } from '@output.ai/core';
|
|
68
|
+
import { httpClient } from '@output.ai/http';
|
|
69
|
+
|
|
70
|
+
export const fetchData = step({
|
|
71
|
+
name: 'fetchData',
|
|
72
|
+
inputSchema: z.object({
|
|
73
|
+
endpoint: z.string(),
|
|
74
|
+
}),
|
|
75
|
+
outputSchema: z.object({
|
|
76
|
+
data: z.unknown(),
|
|
77
|
+
}),
|
|
78
|
+
fn: async (input) => {
|
|
79
|
+
const client = httpClient({
|
|
80
|
+
prefixUrl: 'https://api.example.com',
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const data = await client.get(input.endpoint).json();
|
|
84
|
+
return { data };
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### With Full Configuration
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
import { httpClient } from '@output.ai/http';
|
|
93
|
+
|
|
94
|
+
const client = httpClient({
|
|
95
|
+
prefixUrl: 'https://api.example.com',
|
|
96
|
+
timeout: 30000, // 30 second timeout
|
|
97
|
+
retry: {
|
|
98
|
+
limit: 3, // Retry up to 3 times
|
|
99
|
+
methods: ['GET', 'POST'], // Which methods to retry
|
|
100
|
+
statusCodes: [408, 500, 502, 503, 504], // Which status codes trigger retry
|
|
101
|
+
},
|
|
102
|
+
headers: {
|
|
103
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
104
|
+
'Content-Type': 'application/json',
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## HTTP Methods
|
|
110
|
+
|
|
111
|
+
### GET Request
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
const data = await client.get('users/123').json();
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### POST Request
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
const result = await client.post('users', {
|
|
121
|
+
json: {
|
|
122
|
+
name: 'John',
|
|
123
|
+
email: 'john@example.com',
|
|
124
|
+
},
|
|
125
|
+
}).json();
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### PUT Request
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
const updated = await client.put('users/123', {
|
|
132
|
+
json: {
|
|
133
|
+
name: 'John Updated',
|
|
134
|
+
},
|
|
135
|
+
}).json();
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### DELETE Request
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
await client.delete('users/123');
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### With Query Parameters
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
const data = await client.get('search', {
|
|
148
|
+
searchParams: {
|
|
149
|
+
q: 'query',
|
|
150
|
+
limit: 10,
|
|
151
|
+
},
|
|
152
|
+
}).json();
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Complete Migration Example
|
|
156
|
+
|
|
157
|
+
### Before (Wrong - using axios)
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
import axios from 'axios';
|
|
161
|
+
import { step } from '@output.ai/core';
|
|
162
|
+
|
|
163
|
+
export const createUser = step({
|
|
164
|
+
name: 'createUser',
|
|
165
|
+
fn: async (input) => {
|
|
166
|
+
try {
|
|
167
|
+
const response = await axios.post(
|
|
168
|
+
'https://api.example.com/users',
|
|
169
|
+
{ name: input.name, email: input.email },
|
|
170
|
+
{
|
|
171
|
+
headers: { 'Authorization': `Bearer ${process.env.API_KEY}` },
|
|
172
|
+
timeout: 30000,
|
|
173
|
+
}
|
|
174
|
+
);
|
|
175
|
+
return response.data;
|
|
176
|
+
} catch (error) {
|
|
177
|
+
if (axios.isAxiosError(error)) {
|
|
178
|
+
throw new Error(`API Error: ${error.response?.data?.message}`);
|
|
179
|
+
}
|
|
180
|
+
throw error;
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### After (Correct - using httpClient)
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
import { z, step } from '@output.ai/core';
|
|
190
|
+
import { httpClient } from '@output.ai/http';
|
|
191
|
+
|
|
192
|
+
export const createUser = step({
|
|
193
|
+
name: 'createUser',
|
|
194
|
+
inputSchema: z.object({
|
|
195
|
+
name: z.string(),
|
|
196
|
+
email: z.string().email(),
|
|
197
|
+
}),
|
|
198
|
+
outputSchema: z.object({
|
|
199
|
+
id: z.string(),
|
|
200
|
+
name: z.string(),
|
|
201
|
+
email: z.string(),
|
|
202
|
+
}),
|
|
203
|
+
fn: async (input) => {
|
|
204
|
+
const client = httpClient({
|
|
205
|
+
prefixUrl: 'https://api.example.com',
|
|
206
|
+
timeout: 30000,
|
|
207
|
+
retry: { limit: 3 },
|
|
208
|
+
headers: {
|
|
209
|
+
'Authorization': `Bearer ${process.env.API_KEY}`,
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
const user = await client.post('users', {
|
|
214
|
+
json: {
|
|
215
|
+
name: input.name,
|
|
216
|
+
email: input.email,
|
|
217
|
+
},
|
|
218
|
+
}).json();
|
|
219
|
+
|
|
220
|
+
return user;
|
|
221
|
+
},
|
|
222
|
+
});
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## Error Handling
|
|
226
|
+
|
|
227
|
+
The httpClient provides structured error handling:
|
|
228
|
+
|
|
229
|
+
```typescript
|
|
230
|
+
import { httpClient, HTTPError } from '@output.ai/http';
|
|
231
|
+
|
|
232
|
+
export const fetchData = step({
|
|
233
|
+
name: 'fetchData',
|
|
234
|
+
fn: async (input) => {
|
|
235
|
+
const client = httpClient({ prefixUrl: 'https://api.example.com' });
|
|
236
|
+
|
|
237
|
+
try {
|
|
238
|
+
return await client.get('data').json();
|
|
239
|
+
} catch (error) {
|
|
240
|
+
if (error instanceof HTTPError) {
|
|
241
|
+
// Access response details
|
|
242
|
+
const status = error.response.status;
|
|
243
|
+
const body = await error.response.json();
|
|
244
|
+
throw new Error(`API returned ${status}: ${body.message}`);
|
|
245
|
+
}
|
|
246
|
+
throw error;
|
|
247
|
+
}
|
|
248
|
+
},
|
|
249
|
+
});
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## Finding axios/fetch Usage
|
|
253
|
+
|
|
254
|
+
Search your codebase:
|
|
255
|
+
|
|
256
|
+
```bash
|
|
257
|
+
# Find axios imports
|
|
258
|
+
grep -rn "from 'axios'\|from \"axios\"" src/
|
|
259
|
+
|
|
260
|
+
# Find fetch calls
|
|
261
|
+
grep -rn "await fetch(" src/
|
|
262
|
+
|
|
263
|
+
# Find other HTTP libraries
|
|
264
|
+
grep -rn "got\|node-fetch\|request\|superagent" src/
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
## Benefits of httpClient
|
|
268
|
+
|
|
269
|
+
1. **Tracing**: Requests appear in workflow traces with timing
|
|
270
|
+
2. **Automatic Retries**: Configurable retry logic for transient failures
|
|
271
|
+
3. **Consistent Errors**: Standardized error format across all requests
|
|
272
|
+
4. **Timeout Integration**: Works with step and workflow timeouts
|
|
273
|
+
5. **Type Safety**: Full TypeScript support
|
|
274
|
+
|
|
275
|
+
## Configuration Options
|
|
276
|
+
|
|
277
|
+
| Option | Description | Default |
|
|
278
|
+
|--------|-------------|---------|
|
|
279
|
+
| `prefixUrl` | Base URL for all requests | (required) |
|
|
280
|
+
| `timeout` | Request timeout in ms | 10000 |
|
|
281
|
+
| `retry.limit` | Max retry attempts | 2 |
|
|
282
|
+
| `retry.methods` | HTTP methods to retry | ['GET', 'PUT', 'HEAD', 'DELETE', 'OPTIONS', 'TRACE'] |
|
|
283
|
+
| `retry.statusCodes` | Status codes to retry | [408, 413, 429, 500, 502, 503, 504] |
|
|
284
|
+
| `headers` | Default headers | {} |
|
|
285
|
+
|
|
286
|
+
## Verification
|
|
287
|
+
|
|
288
|
+
After migrating to httpClient:
|
|
289
|
+
|
|
290
|
+
1. **Run the workflow**: `output workflow run <name> '<input>'`
|
|
291
|
+
2. **Check the trace**: `output workflow debug <id> --format json`
|
|
292
|
+
3. **Verify tracing**: HTTP requests should appear in the step trace
|
|
293
|
+
4. **Test retries**: Simulate failures to verify retry behavior
|
|
294
|
+
|
|
295
|
+
## Related Issues
|
|
296
|
+
|
|
297
|
+
- For I/O in workflow functions, see `output-error-direct-io`
|
|
298
|
+
- For connection issues, see `output-services-check`
|
package/dist/templates/agent_instructions/skills/output-error-missing-schemas/SKILL.md.template
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: output-error-missing-schemas
|
|
3
|
+
description: Fix missing schema definitions in Output SDK steps. Use when seeing type errors, undefined properties at step boundaries, validation failures, or when step inputs/outputs aren't being properly typed.
|
|
4
|
+
allowed-tools: [Bash, Read]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Fix Missing Schema Definitions
|
|
8
|
+
|
|
9
|
+
## Overview
|
|
10
|
+
|
|
11
|
+
This skill helps diagnose and fix issues caused by steps that lack explicit `inputSchema` or `outputSchema` definitions. Schemas are essential for type safety, validation, and proper data serialization between steps.
|
|
12
|
+
|
|
13
|
+
## When to Use This Skill
|
|
14
|
+
|
|
15
|
+
You're seeing:
|
|
16
|
+
- Type errors at step boundaries
|
|
17
|
+
- Undefined properties in step inputs/outputs
|
|
18
|
+
- Validation failures when passing data between steps
|
|
19
|
+
- TypeScript errors about incompatible types
|
|
20
|
+
- Runtime errors about unexpected data shapes
|
|
21
|
+
|
|
22
|
+
## Root Cause
|
|
23
|
+
|
|
24
|
+
Steps without explicit schemas:
|
|
25
|
+
- Don't validate input data at runtime
|
|
26
|
+
- Don't provide TypeScript type inference
|
|
27
|
+
- May serialize/deserialize data incorrectly
|
|
28
|
+
- Can pass undefined or malformed data silently
|
|
29
|
+
|
|
30
|
+
## Symptoms
|
|
31
|
+
|
|
32
|
+
### Missing Input Schema
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
// WRONG: No input validation
|
|
36
|
+
export const processData = step({
|
|
37
|
+
name: 'processData',
|
|
38
|
+
// inputSchema: missing!
|
|
39
|
+
outputSchema: z.object({ result: z.string() }),
|
|
40
|
+
fn: async (input) => {
|
|
41
|
+
return { result: input.value }; // input.value might be undefined!
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Missing Output Schema
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
// WRONG: No output validation
|
|
50
|
+
export const fetchData = step({
|
|
51
|
+
name: 'fetchData',
|
|
52
|
+
inputSchema: z.object({ id: z.string() }),
|
|
53
|
+
// outputSchema: missing!
|
|
54
|
+
fn: async (input) => {
|
|
55
|
+
return { data: await getFromApi(input.id) }; // Output shape not validated
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Both Schemas Missing
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
// WRONG: No validation at all
|
|
64
|
+
export const transformData = step({
|
|
65
|
+
name: 'transformData',
|
|
66
|
+
// No schemas!
|
|
67
|
+
fn: async (input) => {
|
|
68
|
+
return transform(input);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Solution
|
|
74
|
+
|
|
75
|
+
Always define both `inputSchema` and `outputSchema` for every step:
|
|
76
|
+
|
|
77
|
+
### Complete Step Definition
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
import { z, step } from '@output.ai/core';
|
|
81
|
+
|
|
82
|
+
export const processData = step({
|
|
83
|
+
name: 'processData',
|
|
84
|
+
inputSchema: z.object({
|
|
85
|
+
id: z.string(),
|
|
86
|
+
value: z.number(),
|
|
87
|
+
optional: z.string().optional(),
|
|
88
|
+
}),
|
|
89
|
+
outputSchema: z.object({
|
|
90
|
+
result: z.string(),
|
|
91
|
+
processedAt: z.number(),
|
|
92
|
+
}),
|
|
93
|
+
fn: async (input) => {
|
|
94
|
+
// input is fully typed: { id: string, value: number, optional?: string }
|
|
95
|
+
return {
|
|
96
|
+
result: `Processed ${input.id}`,
|
|
97
|
+
processedAt: Date.now(),
|
|
98
|
+
};
|
|
99
|
+
// output is validated against outputSchema
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Schema Definition Best Practices
|
|
105
|
+
|
|
106
|
+
### Use Descriptive Schemas
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
// Good: Clear, descriptive schema
|
|
110
|
+
inputSchema: z.object({
|
|
111
|
+
userId: z.string().uuid(),
|
|
112
|
+
email: z.string().email(),
|
|
113
|
+
age: z.number().int().positive(),
|
|
114
|
+
})
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Handle Optional Fields
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
inputSchema: z.object({
|
|
121
|
+
required: z.string(),
|
|
122
|
+
optional: z.string().optional(),
|
|
123
|
+
withDefault: z.string().default('fallback'),
|
|
124
|
+
})
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Use Schema Composition
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
// Define reusable schemas
|
|
131
|
+
const userSchema = z.object({
|
|
132
|
+
id: z.string(),
|
|
133
|
+
name: z.string(),
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
const addressSchema = z.object({
|
|
137
|
+
street: z.string(),
|
|
138
|
+
city: z.string(),
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// Compose in step
|
|
142
|
+
inputSchema: z.object({
|
|
143
|
+
user: userSchema,
|
|
144
|
+
address: addressSchema,
|
|
145
|
+
})
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Handle Arrays and Nested Objects
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
inputSchema: z.object({
|
|
152
|
+
items: z.array(z.object({
|
|
153
|
+
id: z.string(),
|
|
154
|
+
quantity: z.number(),
|
|
155
|
+
})),
|
|
156
|
+
metadata: z.record(z.string()),
|
|
157
|
+
})
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Finding Steps Without Schemas
|
|
161
|
+
|
|
162
|
+
Search your codebase:
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
# Find step definitions
|
|
166
|
+
grep -rn "step({" src/workflows/
|
|
167
|
+
|
|
168
|
+
# Look for steps without inputSchema
|
|
169
|
+
grep -A5 "step({" src/workflows/ | grep -B2 "fn:"
|
|
170
|
+
|
|
171
|
+
# Check if schemas are present
|
|
172
|
+
grep -rn "inputSchema:" src/workflows/
|
|
173
|
+
grep -rn "outputSchema:" src/workflows/
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Review each step definition to ensure both schemas are present.
|
|
177
|
+
|
|
178
|
+
## Benefits of Explicit Schemas
|
|
179
|
+
|
|
180
|
+
1. **Runtime Validation**: Catches data errors early
|
|
181
|
+
2. **Type Safety**: Full TypeScript inference in step functions
|
|
182
|
+
3. **Documentation**: Schemas document expected data shapes
|
|
183
|
+
4. **Serialization**: Ensures proper data serialization between steps
|
|
184
|
+
5. **Error Messages**: Clear validation errors when data is wrong
|
|
185
|
+
|
|
186
|
+
## Common Schema Patterns
|
|
187
|
+
|
|
188
|
+
### API Response Steps
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
export const fetchUser = step({
|
|
192
|
+
name: 'fetchUser',
|
|
193
|
+
inputSchema: z.object({
|
|
194
|
+
userId: z.string(),
|
|
195
|
+
}),
|
|
196
|
+
outputSchema: z.object({
|
|
197
|
+
user: z.object({
|
|
198
|
+
id: z.string(),
|
|
199
|
+
name: z.string(),
|
|
200
|
+
email: z.string(),
|
|
201
|
+
}).nullable(), // Handle not found
|
|
202
|
+
found: z.boolean(),
|
|
203
|
+
}),
|
|
204
|
+
fn: async (input) => {
|
|
205
|
+
const user = await api.getUser(input.userId);
|
|
206
|
+
return { user, found: user !== null };
|
|
207
|
+
},
|
|
208
|
+
});
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Transformation Steps
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
export const transformData = step({
|
|
215
|
+
name: 'transformData',
|
|
216
|
+
inputSchema: z.object({
|
|
217
|
+
raw: z.array(z.unknown()),
|
|
218
|
+
}),
|
|
219
|
+
outputSchema: z.object({
|
|
220
|
+
processed: z.array(z.object({
|
|
221
|
+
id: z.string(),
|
|
222
|
+
value: z.number(),
|
|
223
|
+
})),
|
|
224
|
+
count: z.number(),
|
|
225
|
+
}),
|
|
226
|
+
fn: async (input) => {
|
|
227
|
+
const processed = input.raw.map(transformItem);
|
|
228
|
+
return { processed, count: processed.length };
|
|
229
|
+
},
|
|
230
|
+
});
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Void Output Steps
|
|
234
|
+
|
|
235
|
+
For steps that don't return meaningful data:
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
export const logEvent = step({
|
|
239
|
+
name: 'logEvent',
|
|
240
|
+
inputSchema: z.object({
|
|
241
|
+
event: z.string(),
|
|
242
|
+
data: z.record(z.unknown()),
|
|
243
|
+
}),
|
|
244
|
+
outputSchema: z.object({
|
|
245
|
+
logged: z.literal(true),
|
|
246
|
+
}),
|
|
247
|
+
fn: async (input) => {
|
|
248
|
+
await logger.log(input.event, input.data);
|
|
249
|
+
return { logged: true };
|
|
250
|
+
},
|
|
251
|
+
});
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
## Verification
|
|
255
|
+
|
|
256
|
+
After adding schemas:
|
|
257
|
+
|
|
258
|
+
1. **TypeScript check**: `npm run build` should pass without type errors
|
|
259
|
+
2. **Runtime test**: `output workflow run <name> '<input>'` should validate correctly
|
|
260
|
+
3. **Invalid input test**: Pass invalid data and verify validation errors appear
|
|
261
|
+
|
|
262
|
+
## Related Issues
|
|
263
|
+
|
|
264
|
+
- For Zod import issues, see `output-error-zod-import`
|
|
265
|
+
- For type mismatches despite schemas, verify schema matches actual data
|