@output.ai/cli 0.5.6 → 0.6.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/commands/agents/init.d.ts +0 -1
- package/dist/commands/agents/init.js +9 -16
- package/dist/commands/agents/init.spec.js +49 -224
- package/dist/commands/workflow/generate.js +2 -2
- package/dist/commands/workflow/plan.js +3 -3
- package/dist/commands/workflow/plan.spec.js +13 -13
- package/dist/services/claude_client.d.ts +4 -4
- package/dist/services/claude_client.integration.test.js +2 -2
- package/dist/services/claude_client.js +7 -7
- package/dist/services/claude_client.spec.js +3 -3
- package/dist/services/coding_agents.d.ts +10 -24
- package/dist/services/coding_agents.js +112 -368
- package/dist/services/coding_agents.spec.js +101 -290
- package/dist/services/project_scaffold.js +3 -3
- package/dist/services/workflow_builder.d.ts +1 -1
- package/dist/services/workflow_builder.js +1 -1
- package/dist/services/workflow_planner.js +1 -2
- package/dist/services/workflow_planner.spec.js +4 -5
- package/dist/templates/agent_instructions/dotclaude/settings.json.template +29 -0
- package/dist/templates/agent_instructions/{AGENTS.md.template → dotoutputai/AGENTS.md.template} +12 -10
- package/dist/utils/claude.d.ts +5 -0
- package/dist/utils/claude.js +19 -0
- package/dist/utils/claude.spec.d.ts +1 -0
- package/dist/utils/claude.spec.js +119 -0
- package/dist/utils/paths.d.ts +0 -4
- package/dist/utils/paths.js +0 -6
- package/package.json +3 -3
- package/dist/templates/agent_instructions/agents/workflow_context_fetcher.md.template +0 -82
- package/dist/templates/agent_instructions/agents/workflow_debugger.md.template +0 -98
- package/dist/templates/agent_instructions/agents/workflow_planner.md.template +0 -113
- package/dist/templates/agent_instructions/agents/workflow_prompt_writer.md.template +0 -595
- package/dist/templates/agent_instructions/agents/workflow_quality.md.template +0 -244
- package/dist/templates/agent_instructions/commands/build_workflow.md.template +0 -290
- package/dist/templates/agent_instructions/commands/debug_workflow.md.template +0 -198
- package/dist/templates/agent_instructions/commands/plan_workflow.md.template +0 -261
- package/dist/templates/agent_instructions/meta/post_flight.md.template +0 -94
- package/dist/templates/agent_instructions/meta/pre_flight.md.template +0 -60
- package/dist/templates/agent_instructions/skills/output-error-direct-io/SKILL.md.template +0 -249
- package/dist/templates/agent_instructions/skills/output-error-http-client/SKILL.md.template +0 -298
- package/dist/templates/agent_instructions/skills/output-error-missing-schemas/SKILL.md.template +0 -265
- package/dist/templates/agent_instructions/skills/output-error-nondeterminism/SKILL.md.template +0 -252
- package/dist/templates/agent_instructions/skills/output-error-try-catch/SKILL.md.template +0 -226
- package/dist/templates/agent_instructions/skills/output-error-zod-import/SKILL.md.template +0 -209
- package/dist/templates/agent_instructions/skills/output-services-check/SKILL.md.template +0 -128
- package/dist/templates/agent_instructions/skills/output-workflow-list/SKILL.md.template +0 -117
- package/dist/templates/agent_instructions/skills/output-workflow-result/SKILL.md.template +0 -199
- package/dist/templates/agent_instructions/skills/output-workflow-run/SKILL.md.template +0 -228
- package/dist/templates/agent_instructions/skills/output-workflow-runs-list/SKILL.md.template +0 -141
- package/dist/templates/agent_instructions/skills/output-workflow-start/SKILL.md.template +0 -201
- package/dist/templates/agent_instructions/skills/output-workflow-status/SKILL.md.template +0 -151
- package/dist/templates/agent_instructions/skills/output-workflow-stop/SKILL.md.template +0 -164
- package/dist/templates/agent_instructions/skills/output-workflow-trace/SKILL.md.template +0 -134
|
@@ -1,249 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: output-error-direct-io
|
|
3
|
-
description: Fix direct I/O in Output SDK workflow functions. Use when workflow hangs, returns undefined, shows "workflow must be deterministic" errors, or when HTTP/API calls are made directly in workflow code.
|
|
4
|
-
allowed-tools: [Bash, Read]
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# Fix Direct I/O in Workflow Functions
|
|
8
|
-
|
|
9
|
-
## Overview
|
|
10
|
-
|
|
11
|
-
This skill helps diagnose and fix a critical error pattern where I/O operations (HTTP calls, database queries, file operations) are performed directly in workflow functions instead of in steps. This violates Temporal's determinism requirements.
|
|
12
|
-
|
|
13
|
-
## When to Use This Skill
|
|
14
|
-
|
|
15
|
-
You're seeing:
|
|
16
|
-
- Workflow hangs indefinitely
|
|
17
|
-
- Undefined or empty responses
|
|
18
|
-
- "workflow must be deterministic" errors
|
|
19
|
-
- Network operations failing silently
|
|
20
|
-
- Timeouts without clear cause
|
|
21
|
-
|
|
22
|
-
## Root Cause
|
|
23
|
-
|
|
24
|
-
Workflow functions must be **deterministic** - they should only orchestrate steps, not perform I/O directly. When you make HTTP calls, database queries, or any external operations directly in a workflow function:
|
|
25
|
-
|
|
26
|
-
1. **Hangs**: The workflow may hang because I/O isn't properly handled
|
|
27
|
-
2. **Determinism violations**: Temporal replays workflows, and I/O results differ
|
|
28
|
-
3. **No retry logic**: Direct calls bypass Output SDK's retry mechanisms
|
|
29
|
-
4. **No tracing**: Operations aren't recorded in the workflow trace
|
|
30
|
-
|
|
31
|
-
## Symptoms
|
|
32
|
-
|
|
33
|
-
### Direct fetch/axios in Workflow
|
|
34
|
-
|
|
35
|
-
```typescript
|
|
36
|
-
// WRONG: I/O directly in workflow
|
|
37
|
-
export default workflow({
|
|
38
|
-
fn: async (input) => {
|
|
39
|
-
const response = await fetch('https://api.example.com/data'); // BAD!
|
|
40
|
-
const data = await response.json();
|
|
41
|
-
return { data };
|
|
42
|
-
}
|
|
43
|
-
});
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
### Direct Database Calls
|
|
47
|
-
|
|
48
|
-
```typescript
|
|
49
|
-
// WRONG: Database I/O in workflow
|
|
50
|
-
export default workflow({
|
|
51
|
-
fn: async (input) => {
|
|
52
|
-
const user = await db.users.findById(input.userId); // BAD!
|
|
53
|
-
return { user };
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
### File System Operations
|
|
59
|
-
|
|
60
|
-
```typescript
|
|
61
|
-
// WRONG: File I/O in workflow
|
|
62
|
-
import fs from 'fs/promises';
|
|
63
|
-
|
|
64
|
-
export default workflow({
|
|
65
|
-
fn: async (input) => {
|
|
66
|
-
const data = await fs.readFile(input.path, 'utf-8'); // BAD!
|
|
67
|
-
return { data };
|
|
68
|
-
}
|
|
69
|
-
});
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
## Solution
|
|
73
|
-
|
|
74
|
-
Move ALL I/O operations to step functions. Steps are designed to handle non-deterministic operations.
|
|
75
|
-
|
|
76
|
-
### Before (Wrong)
|
|
77
|
-
|
|
78
|
-
```typescript
|
|
79
|
-
export default workflow({
|
|
80
|
-
fn: async (input) => {
|
|
81
|
-
const response = await fetch('https://api.example.com/data');
|
|
82
|
-
const data = await response.json();
|
|
83
|
-
return { data };
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
### After (Correct)
|
|
89
|
-
|
|
90
|
-
```typescript
|
|
91
|
-
import { z, step, workflow } from '@output.ai/core';
|
|
92
|
-
import { httpClient } from '@output.ai/http';
|
|
93
|
-
|
|
94
|
-
// Create a step for the I/O operation
|
|
95
|
-
export const fetchData = step({
|
|
96
|
-
name: 'fetchData',
|
|
97
|
-
inputSchema: z.object({
|
|
98
|
-
endpoint: z.string(),
|
|
99
|
-
}),
|
|
100
|
-
outputSchema: z.object({
|
|
101
|
-
data: z.unknown(),
|
|
102
|
-
}),
|
|
103
|
-
fn: async (input) => {
|
|
104
|
-
const client = httpClient({ prefixUrl: 'https://api.example.com' });
|
|
105
|
-
const data = await client.get(input.endpoint).json();
|
|
106
|
-
return { data };
|
|
107
|
-
},
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
// Workflow only orchestrates steps
|
|
111
|
-
export default workflow({
|
|
112
|
-
inputSchema: z.object({}),
|
|
113
|
-
outputSchema: z.object({ data: z.unknown() }),
|
|
114
|
-
fn: async (input) => {
|
|
115
|
-
const result = await fetchData({ endpoint: 'data' });
|
|
116
|
-
return result;
|
|
117
|
-
},
|
|
118
|
-
});
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
## Complete Example: Database Operation
|
|
122
|
-
|
|
123
|
-
### Before (Wrong)
|
|
124
|
-
|
|
125
|
-
```typescript
|
|
126
|
-
export default workflow({
|
|
127
|
-
fn: async (input) => {
|
|
128
|
-
const user = await prisma.user.findUnique({
|
|
129
|
-
where: { id: input.userId }
|
|
130
|
-
});
|
|
131
|
-
const orders = await prisma.order.findMany({
|
|
132
|
-
where: { userId: input.userId }
|
|
133
|
-
});
|
|
134
|
-
return { user, orders };
|
|
135
|
-
}
|
|
136
|
-
});
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
### After (Correct)
|
|
140
|
-
|
|
141
|
-
```typescript
|
|
142
|
-
import { z, step, workflow } from '@output.ai/core';
|
|
143
|
-
import { prisma } from '../lib/db';
|
|
144
|
-
|
|
145
|
-
export const fetchUser = step({
|
|
146
|
-
name: 'fetchUser',
|
|
147
|
-
inputSchema: z.object({ userId: z.string() }),
|
|
148
|
-
outputSchema: z.object({
|
|
149
|
-
user: z.object({
|
|
150
|
-
id: z.string(),
|
|
151
|
-
name: z.string(),
|
|
152
|
-
email: z.string(),
|
|
153
|
-
}).nullable(),
|
|
154
|
-
}),
|
|
155
|
-
fn: async (input) => {
|
|
156
|
-
const user = await prisma.user.findUnique({
|
|
157
|
-
where: { id: input.userId }
|
|
158
|
-
});
|
|
159
|
-
return { user };
|
|
160
|
-
},
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
export const fetchOrders = step({
|
|
164
|
-
name: 'fetchOrders',
|
|
165
|
-
inputSchema: z.object({ userId: z.string() }),
|
|
166
|
-
outputSchema: z.object({
|
|
167
|
-
orders: z.array(z.object({
|
|
168
|
-
id: z.string(),
|
|
169
|
-
total: z.number(),
|
|
170
|
-
})),
|
|
171
|
-
}),
|
|
172
|
-
fn: async (input) => {
|
|
173
|
-
const orders = await prisma.order.findMany({
|
|
174
|
-
where: { userId: input.userId }
|
|
175
|
-
});
|
|
176
|
-
return { orders };
|
|
177
|
-
},
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
export default workflow({
|
|
181
|
-
inputSchema: z.object({ userId: z.string() }),
|
|
182
|
-
outputSchema: z.object({
|
|
183
|
-
user: z.unknown(),
|
|
184
|
-
orders: z.array(z.unknown()),
|
|
185
|
-
}),
|
|
186
|
-
fn: async (input) => {
|
|
187
|
-
const { user } = await fetchUser({ userId: input.userId });
|
|
188
|
-
const { orders } = await fetchOrders({ userId: input.userId });
|
|
189
|
-
return { user, orders };
|
|
190
|
-
},
|
|
191
|
-
});
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
## Finding Direct I/O in Workflows
|
|
195
|
-
|
|
196
|
-
Search for common I/O patterns in workflow files:
|
|
197
|
-
|
|
198
|
-
```bash
|
|
199
|
-
# Find fetch calls
|
|
200
|
-
grep -rn "await fetch" src/workflows/
|
|
201
|
-
|
|
202
|
-
# Find axios calls
|
|
203
|
-
grep -rn "axios\." src/workflows/
|
|
204
|
-
|
|
205
|
-
# Find database operations
|
|
206
|
-
grep -rn "prisma\.\|db\.\|mongoose\." src/workflows/
|
|
207
|
-
|
|
208
|
-
# Find file system operations
|
|
209
|
-
grep -rn "fs\.\|readFile\|writeFile" src/workflows/
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
Then review each match to see if it's in a workflow function vs a step function.
|
|
213
|
-
|
|
214
|
-
## What CAN Be in Workflow Functions
|
|
215
|
-
|
|
216
|
-
Workflow functions should contain:
|
|
217
|
-
- **Step calls**: `await myStep(input)`
|
|
218
|
-
- **Orchestration logic**: conditionals, loops (over step calls)
|
|
219
|
-
- **Data transformation**: Pure functions on step results
|
|
220
|
-
- **Constants**: Static values and configuration
|
|
221
|
-
|
|
222
|
-
Workflow functions should NOT contain:
|
|
223
|
-
- HTTP/API calls
|
|
224
|
-
- Database operations
|
|
225
|
-
- File system operations
|
|
226
|
-
- External service calls
|
|
227
|
-
- Anything that talks to the network or filesystem
|
|
228
|
-
|
|
229
|
-
## Verification
|
|
230
|
-
|
|
231
|
-
After moving I/O to steps:
|
|
232
|
-
|
|
233
|
-
1. **Run the workflow**: `npx output workflow run <name> '<input>'`
|
|
234
|
-
2. **Check the trace**: `npx output workflow debug <id> --format json`
|
|
235
|
-
3. **Verify steps appear**: Look for your I/O steps in the trace
|
|
236
|
-
4. **Confirm no errors**: No determinism warnings or hangs
|
|
237
|
-
|
|
238
|
-
## Benefits of Steps for I/O
|
|
239
|
-
|
|
240
|
-
1. **Retry logic**: Steps can be retried on failure
|
|
241
|
-
2. **Tracing**: I/O operations appear in workflow traces
|
|
242
|
-
3. **Timeouts**: Steps can have individual timeouts
|
|
243
|
-
4. **Determinism**: Replays use recorded results
|
|
244
|
-
5. **Debugging**: Clear visibility into what happened
|
|
245
|
-
|
|
246
|
-
## Related Issues
|
|
247
|
-
|
|
248
|
-
- For HTTP client best practices, see `output-error-http-client`
|
|
249
|
-
- For non-determinism from other causes, see `output-error-nondeterminism`
|
|
@@ -1,298 +0,0 @@
|
|
|
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**: `npx output workflow run <name> '<input>'`
|
|
291
|
-
2. **Check the trace**: `npx 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`
|