@output.ai/cli 0.3.1-dev.pr156.0 → 0.4.1
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 +36 -197
- 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/.gitignore.template +0 -3
- package/dist/templates/project/README.md.template +2 -2
- 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 +6 -3
- 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
package/README.md
CHANGED
|
@@ -1,220 +1,59 @@
|
|
|
1
1
|
# @output.ai/cli
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Command-line interface for creating and running Output Framework workflows.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@output.ai/cli)
|
|
6
|
+
[](https://docs.output.ai/packages/cli)
|
|
4
7
|
|
|
5
8
|
## Installation
|
|
6
9
|
|
|
7
10
|
```bash
|
|
8
|
-
|
|
9
|
-
cd sdk/cli && npm link
|
|
10
|
-
|
|
11
|
-
# Or run directly from the monorepo
|
|
12
|
-
npx @output.ai/cli
|
|
11
|
+
npm install -g @output.ai/cli
|
|
13
12
|
```
|
|
14
13
|
|
|
15
|
-
##
|
|
16
|
-
|
|
17
|
-
### Initialize a New Project
|
|
14
|
+
## Quick Start
|
|
18
15
|
|
|
19
16
|
```bash
|
|
20
|
-
# Create a new
|
|
17
|
+
# Create a new project
|
|
21
18
|
output init my-project
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
#### What It Does
|
|
19
|
+
cd my-project
|
|
25
20
|
|
|
26
|
-
|
|
27
|
-
- Example workflows demonstrating SDK features
|
|
28
|
-
- TypeScript project configuration
|
|
29
|
-
- Agent configuration files
|
|
30
|
-
|
|
31
|
-
### Start Development Services
|
|
32
|
-
|
|
33
|
-
```bash
|
|
34
|
-
# Start with automatic file watching (default)
|
|
21
|
+
# Start development services
|
|
35
22
|
output dev
|
|
36
23
|
|
|
37
|
-
#
|
|
38
|
-
output
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
#### What It Does
|
|
42
|
-
|
|
43
|
-
The `dev` command orchestrates your entire development environment:
|
|
44
|
-
1. Starts Docker services (Temporal, Redis, PostgreSQL)
|
|
45
|
-
2. Launches the API server for workflow execution
|
|
46
|
-
3. Starts the worker to process workflows
|
|
47
|
-
4. Opens Temporal UI for workflow monitoring
|
|
48
|
-
5. Automatically restarts the worker when source files change
|
|
49
|
-
6. Provides a unified log view of all services
|
|
50
|
-
|
|
51
|
-
This is the primary command for local development - it handles all the complexity of running multiple services and keeps them synchronized.
|
|
52
|
-
|
|
53
|
-
#### Automatic File Watching
|
|
54
|
-
|
|
55
|
-
By default, the worker container automatically restarts when you modify files in:
|
|
56
|
-
- `src/` - Your workflow source files and implementations
|
|
57
|
-
- `package.json` - When dependencies change
|
|
58
|
-
|
|
59
|
-
This feature uses Docker Compose's native watch functionality (requires Docker Compose v2.22+).
|
|
60
|
-
|
|
61
|
-
#### Command Options
|
|
62
|
-
|
|
63
|
-
- `--no-watch` - Disable automatic container restart on file changes
|
|
64
|
-
|
|
65
|
-
### List Workflows
|
|
66
|
-
|
|
67
|
-
```bash
|
|
68
|
-
# List all available workflows (simple list, default)
|
|
69
|
-
output workflow list
|
|
70
|
-
|
|
71
|
-
# List workflows with custom API URL
|
|
72
|
-
API_URL=http://localhost:3001 output workflow list
|
|
73
|
-
|
|
74
|
-
# List workflows with authentication
|
|
75
|
-
API_AUTH_TOKEN=your-token output workflow list
|
|
76
|
-
|
|
77
|
-
# Show detailed table view with all information
|
|
78
|
-
output workflow list --format table
|
|
79
|
-
|
|
80
|
-
# Show detailed table with expanded parameter info
|
|
81
|
-
output workflow list --format table --detailed
|
|
82
|
-
|
|
83
|
-
# List workflows in JSON format
|
|
84
|
-
output workflow list --format json
|
|
85
|
-
|
|
86
|
-
# Filter workflows by name (partial match)
|
|
87
|
-
output workflow list --filter simple
|
|
24
|
+
# Run a workflow
|
|
25
|
+
output workflow run simple --input '{"question": "who is ada lovelace?"}'
|
|
88
26
|
```
|
|
89
27
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
- `--format, -f` - Output format: `list` (default, simple column), `table`, or `json`
|
|
93
|
-
- `--detailed, -d` - Show detailed parameter information in table view
|
|
94
|
-
- `--filter` - Filter workflows by name (case-insensitive partial match)
|
|
95
|
-
|
|
96
|
-
The list command connects to the API server and retrieves all available workflows. By default, it displays a simple list of workflow names (like `ls`). Use `--format table` for detailed information.
|
|
97
|
-
|
|
98
|
-
### Debug a Workflow Execution
|
|
99
|
-
|
|
100
|
-
```bash
|
|
101
|
-
# Display trace information for a workflow run
|
|
102
|
-
output workflow debug <workflowId>
|
|
103
|
-
|
|
104
|
-
# Display trace in JSON format
|
|
105
|
-
output workflow debug <workflowId> --format json
|
|
106
|
-
|
|
107
|
-
# Prefer remote (S3) trace over local
|
|
108
|
-
output workflow debug <workflowId> --remote
|
|
109
|
-
|
|
110
|
-
# Save trace to a custom directory
|
|
111
|
-
output workflow debug <workflowId> --download-dir ./my-traces
|
|
112
|
-
|
|
113
|
-
# Force re-download of remote traces (bypass cache)
|
|
114
|
-
output workflow debug <workflowId> --remote --force-download
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
#### What It Does
|
|
118
|
-
|
|
119
|
-
The `debug` command retrieves and displays detailed execution traces for debugging workflow runs. It shows:
|
|
120
|
-
- Complete execution timeline with all events
|
|
121
|
-
- Hierarchical execution tree showing workflow structure
|
|
122
|
-
- Step and activity inputs/outputs
|
|
123
|
-
- Error details and stack traces
|
|
124
|
-
- Performance metrics and durations
|
|
125
|
-
|
|
126
|
-
#### Trace Sources
|
|
127
|
-
|
|
128
|
-
The command automatically handles both local and remote traces:
|
|
129
|
-
- **Local traces**: Stored on your machine when `TRACE_LOCAL_ON=true`
|
|
130
|
-
- **Remote traces**: Stored in S3 when `TRACE_REMOTE_ON=true` (requires AWS credentials)
|
|
131
|
-
|
|
132
|
-
By default, it tries local traces first for faster access, then falls back to remote if needed.
|
|
133
|
-
|
|
134
|
-
#### Command Options
|
|
135
|
-
|
|
136
|
-
- `--format, -f` - Output format: `text` (default, human-readable) or `json` (raw trace data)
|
|
137
|
-
- `--remote, -r` - Prefer remote S3 trace even if local exists
|
|
138
|
-
- `--download-dir, -d` - Directory for saving downloaded traces (default: `.output/traces`)
|
|
139
|
-
- `--open, -o` - Save trace to file for viewing in external JSON viewer
|
|
140
|
-
- `--force-download` - Force re-download from S3, bypassing local cache
|
|
141
|
-
|
|
142
|
-
#### AWS Configuration
|
|
143
|
-
|
|
144
|
-
For remote traces, configure AWS credentials:
|
|
145
|
-
```bash
|
|
146
|
-
export AWS_ACCESS_KEY_ID=your-access-key
|
|
147
|
-
export AWS_SECRET_ACCESS_KEY=your-secret-key
|
|
148
|
-
export AWS_REGION=us-east-1 # or your region
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
### Generate a Workflow
|
|
152
|
-
|
|
153
|
-
```bash
|
|
154
|
-
# Generate a complete workflow with example steps
|
|
155
|
-
output workflow generate my-workflow --description "My awesome workflow"
|
|
156
|
-
|
|
157
|
-
# Generate a minimal skeleton workflow
|
|
158
|
-
output workflow generate my-workflow --skeleton
|
|
159
|
-
|
|
160
|
-
# Generate in a specific directory
|
|
161
|
-
output workflow generate my-workflow --output-dir ./src/workflows
|
|
162
|
-
|
|
163
|
-
# Force overwrite existing workflow
|
|
164
|
-
output workflow generate my-workflow --force
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
#### Command Options
|
|
168
|
-
|
|
169
|
-
- `--description, -d` - Description of the workflow
|
|
170
|
-
- `--skeleton, -s` - Generate minimal skeleton without example steps
|
|
171
|
-
- `--output-dir, -o` - Output directory (default: `src/`)
|
|
172
|
-
- `--force, -f` - Overwrite existing directory
|
|
173
|
-
|
|
174
|
-
#### Generated Structure
|
|
175
|
-
|
|
176
|
-
The CLI creates a complete workflow structure:
|
|
177
|
-
|
|
178
|
-
```
|
|
179
|
-
my-workflow/
|
|
180
|
-
├── workflow.ts # Main workflow definition
|
|
181
|
-
├── steps.ts # Activity/step implementations
|
|
182
|
-
├── prompt@v1.prompt # LLM prompt template (if not skeleton)
|
|
183
|
-
└── README.md # Workflow documentation
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
### Initialize Agent Configuration
|
|
187
|
-
|
|
188
|
-
```bash
|
|
189
|
-
# Initialize agent configuration for your coding agent
|
|
190
|
-
output-cli agents init
|
|
191
|
-
|
|
192
|
-
# Specify your agent provider (default: claude-code)
|
|
193
|
-
output-cli agents init --agent-provider claude-code
|
|
194
|
-
|
|
195
|
-
# Force overwrite existing configuration
|
|
196
|
-
output-cli agents init --force
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
#### What It Does
|
|
200
|
-
|
|
201
|
-
Sets up your coding agent of choice with context files, sub-agents, and slash commands for working with the Output SDK effectively. It creates a `.outputai/` directory and wires up your coding agent to the context files.
|
|
202
|
-
|
|
203
|
-
**Note:** Currently this only works for [Claude Code](https://claude.ai/code), but we plan to add support for other coding agents over time.
|
|
204
|
-
|
|
205
|
-
#### Command Options
|
|
28
|
+
## Command Reference
|
|
206
29
|
|
|
207
|
-
|
|
208
|
-
|
|
30
|
+
| Command | Description |
|
|
31
|
+
|---------|-------------|
|
|
32
|
+
| `output init` | Initialize a new project |
|
|
33
|
+
| `output dev` | Start development services (Temporal, API, Worker) |
|
|
34
|
+
| `output agents init` | Initialize AI agent configurations |
|
|
35
|
+
| `output workflow plan` | Generate a workflow plan from description |
|
|
36
|
+
| `output workflow generate` | Generate a workflow from plan |
|
|
37
|
+
| `output workflow list` | List available workflows |
|
|
38
|
+
| `output workflow run` | Execute a workflow synchronously |
|
|
39
|
+
| `output workflow start` | Start a workflow asynchronously |
|
|
40
|
+
| `output workflow status` | Get workflow execution status |
|
|
41
|
+
| `output workflow result` | Get workflow execution result |
|
|
42
|
+
| `output workflow stop` | Stop a workflow execution |
|
|
209
43
|
|
|
210
|
-
|
|
44
|
+
## Development Services
|
|
211
45
|
|
|
212
|
-
|
|
46
|
+
Running `output dev` starts:
|
|
213
47
|
|
|
214
|
-
|
|
48
|
+
| Service | URL | Description |
|
|
49
|
+
|---------|-----|-------------|
|
|
50
|
+
| Temporal UI | http://localhost:8080 | Monitor and debug workflows |
|
|
51
|
+
| API Server | http://localhost:3001 | REST API for workflow execution |
|
|
52
|
+
| Worker | — | Processes workflows with auto-reload |
|
|
215
53
|
|
|
216
|
-
|
|
54
|
+
## Documentation
|
|
217
55
|
|
|
218
|
-
|
|
56
|
+
For comprehensive documentation, visit:
|
|
219
57
|
|
|
220
|
-
|
|
58
|
+
- [CLI Reference](https://docs.output.ai/packages/cli)
|
|
59
|
+
- [Getting Started](https://docs.output.ai/quickstart)
|
|
@@ -32,6 +32,28 @@ export interface Workflow {
|
|
|
32
32
|
inputSchema?: JSONSchema;
|
|
33
33
|
outputSchema?: JSONSchema;
|
|
34
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* File destinations for trace data
|
|
37
|
+
*/
|
|
38
|
+
export type TraceInfoDestinations = {
|
|
39
|
+
/**
|
|
40
|
+
* Absolute path to local trace file, or null if not saved locally
|
|
41
|
+
* @nullable
|
|
42
|
+
*/
|
|
43
|
+
local: string | null;
|
|
44
|
+
/**
|
|
45
|
+
* Remote trace location (e.g., S3 URI), or null if not saved remotely
|
|
46
|
+
* @nullable
|
|
47
|
+
*/
|
|
48
|
+
remote: string | null;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* An object with information about the trace generated by the execution
|
|
52
|
+
*/
|
|
53
|
+
export interface TraceInfo {
|
|
54
|
+
/** File destinations for trace data */
|
|
55
|
+
destinations?: TraceInfoDestinations;
|
|
56
|
+
}
|
|
35
57
|
export type PostWorkflowRunBody = {
|
|
36
58
|
/** The name of the workflow to execute */
|
|
37
59
|
workflowName: string;
|
|
@@ -42,19 +64,12 @@ export type PostWorkflowRunBody = {
|
|
|
42
64
|
/** The name of the task queue to send the workflow to */
|
|
43
65
|
taskQueue?: string;
|
|
44
66
|
};
|
|
45
|
-
/**
|
|
46
|
-
* An object with information about the trace generated by the execution
|
|
47
|
-
*/
|
|
48
|
-
export type PostWorkflowRun200Trace = {
|
|
49
|
-
[key: string]: unknown;
|
|
50
|
-
};
|
|
51
67
|
export type PostWorkflowRun200 = {
|
|
52
68
|
/** The workflow execution id */
|
|
53
69
|
workflowId?: string;
|
|
54
70
|
/** The output of the workflow */
|
|
55
71
|
output?: unknown;
|
|
56
|
-
|
|
57
|
-
trace?: PostWorkflowRun200Trace;
|
|
72
|
+
trace?: TraceInfo;
|
|
58
73
|
};
|
|
59
74
|
export type PostWorkflowStartBody = {
|
|
60
75
|
/** The name of the workflow to execute */
|
|
@@ -94,31 +109,12 @@ export type GetWorkflowIdStatus200 = {
|
|
|
94
109
|
/** An epoch timestamp representing when the workflow ended */
|
|
95
110
|
completedAt?: number;
|
|
96
111
|
};
|
|
97
|
-
|
|
98
|
-
* An object with information about the trace generated by the execution
|
|
99
|
-
*/
|
|
100
|
-
export type GetWorkflowIdOutput200Trace = {
|
|
101
|
-
[key: string]: unknown;
|
|
102
|
-
};
|
|
103
|
-
export type GetWorkflowIdOutput200 = {
|
|
112
|
+
export type GetWorkflowIdResult200 = {
|
|
104
113
|
/** The workflow execution id */
|
|
105
114
|
workflowId?: string;
|
|
106
|
-
/** The
|
|
115
|
+
/** The result of workflow */
|
|
107
116
|
output?: unknown;
|
|
108
|
-
|
|
109
|
-
trace?: GetWorkflowIdOutput200Trace;
|
|
110
|
-
};
|
|
111
|
-
/**
|
|
112
|
-
* The trace tree object containing execution details
|
|
113
|
-
*/
|
|
114
|
-
export type GetWorkflowIdTraceLog200 = {
|
|
115
|
-
[key: string]: unknown;
|
|
116
|
-
};
|
|
117
|
-
export type GetWorkflowIdTraceLog404 = {
|
|
118
|
-
/** Error type */
|
|
119
|
-
error?: string;
|
|
120
|
-
/** Detailed error message */
|
|
121
|
-
message?: string;
|
|
117
|
+
trace?: TraceInfo;
|
|
122
118
|
};
|
|
123
119
|
export type GetWorkflowCatalogId200 = {
|
|
124
120
|
/** Each workflow available in this catalog */
|
|
@@ -213,45 +209,25 @@ export type patchWorkflowIdStopResponse = (patchWorkflowIdStopResponseSuccess |
|
|
|
213
209
|
export declare const getPatchWorkflowIdStopUrl: (id: string) => string;
|
|
214
210
|
export declare const patchWorkflowIdStop: (id: string, options?: ApiRequestOptions) => Promise<patchWorkflowIdStopResponse>;
|
|
215
211
|
/**
|
|
216
|
-
* @summary Return the
|
|
212
|
+
* @summary Return the result of a workflow
|
|
217
213
|
*/
|
|
218
|
-
export type
|
|
219
|
-
data:
|
|
214
|
+
export type getWorkflowIdResultResponse200 = {
|
|
215
|
+
data: GetWorkflowIdResult200;
|
|
220
216
|
status: 200;
|
|
221
217
|
};
|
|
222
|
-
export type
|
|
218
|
+
export type getWorkflowIdResultResponse404 = {
|
|
223
219
|
data: void;
|
|
224
220
|
status: 404;
|
|
225
221
|
};
|
|
226
|
-
export type
|
|
227
|
-
headers: Headers;
|
|
228
|
-
};
|
|
229
|
-
export type getWorkflowIdOutputResponseError = (getWorkflowIdOutputResponse404) & {
|
|
230
|
-
headers: Headers;
|
|
231
|
-
};
|
|
232
|
-
export type getWorkflowIdOutputResponse = (getWorkflowIdOutputResponseSuccess | getWorkflowIdOutputResponseError);
|
|
233
|
-
export declare const getGetWorkflowIdOutputUrl: (id: string) => string;
|
|
234
|
-
export declare const getWorkflowIdOutput: (id: string, options?: ApiRequestOptions) => Promise<getWorkflowIdOutputResponse>;
|
|
235
|
-
/**
|
|
236
|
-
* @summary Get the trace log contents from a workflow execution
|
|
237
|
-
*/
|
|
238
|
-
export type getWorkflowIdTraceLogResponse200 = {
|
|
239
|
-
data: GetWorkflowIdTraceLog200;
|
|
240
|
-
status: 200;
|
|
241
|
-
};
|
|
242
|
-
export type getWorkflowIdTraceLogResponse404 = {
|
|
243
|
-
data: GetWorkflowIdTraceLog404;
|
|
244
|
-
status: 404;
|
|
245
|
-
};
|
|
246
|
-
export type getWorkflowIdTraceLogResponseSuccess = (getWorkflowIdTraceLogResponse200) & {
|
|
222
|
+
export type getWorkflowIdResultResponseSuccess = (getWorkflowIdResultResponse200) & {
|
|
247
223
|
headers: Headers;
|
|
248
224
|
};
|
|
249
|
-
export type
|
|
225
|
+
export type getWorkflowIdResultResponseError = (getWorkflowIdResultResponse404) & {
|
|
250
226
|
headers: Headers;
|
|
251
227
|
};
|
|
252
|
-
export type
|
|
253
|
-
export declare const
|
|
254
|
-
export declare const
|
|
228
|
+
export type getWorkflowIdResultResponse = (getWorkflowIdResultResponseSuccess | getWorkflowIdResultResponseError);
|
|
229
|
+
export declare const getGetWorkflowIdResultUrl: (id: string) => string;
|
|
230
|
+
export declare const getWorkflowIdResult: (id: string, options?: ApiRequestOptions) => Promise<getWorkflowIdResultResponse>;
|
|
255
231
|
/**
|
|
256
232
|
* @summary Get a specific workflow catalog by ID
|
|
257
233
|
*/
|
|
@@ -69,20 +69,11 @@ export const patchWorkflowIdStop = async (id, options) => {
|
|
|
69
69
|
method: 'PATCH'
|
|
70
70
|
});
|
|
71
71
|
};
|
|
72
|
-
export const
|
|
73
|
-
return `/workflow/${id}/
|
|
72
|
+
export const getGetWorkflowIdResultUrl = (id) => {
|
|
73
|
+
return `/workflow/${id}/result`;
|
|
74
74
|
};
|
|
75
|
-
export const
|
|
76
|
-
return customFetchInstance(
|
|
77
|
-
...options,
|
|
78
|
-
method: 'GET'
|
|
79
|
-
});
|
|
80
|
-
};
|
|
81
|
-
export const getGetWorkflowIdTraceLogUrl = (id) => {
|
|
82
|
-
return `/workflow/${id}/trace_log`;
|
|
83
|
-
};
|
|
84
|
-
export const getWorkflowIdTraceLog = async (id, options) => {
|
|
85
|
-
return customFetchInstance(getGetWorkflowIdTraceLogUrl(id), {
|
|
75
|
+
export const getWorkflowIdResult = async (id, options) => {
|
|
76
|
+
return customFetchInstance(getGetWorkflowIdResultUrl(id), {
|
|
86
77
|
...options,
|
|
87
78
|
method: 'GET'
|
|
88
79
|
});
|
|
@@ -80,6 +80,7 @@ services:
|
|
|
80
80
|
temporal:
|
|
81
81
|
condition: service_healthy
|
|
82
82
|
image: growthxteam/output-api:latest
|
|
83
|
+
pull_policy: always
|
|
83
84
|
networks:
|
|
84
85
|
- main
|
|
85
86
|
environment:
|
|
@@ -87,8 +88,6 @@ services:
|
|
|
87
88
|
- CATALOG_ID=main
|
|
88
89
|
- TEMPORAL_ADDRESS=temporal:7233
|
|
89
90
|
- NODE_ENV=development
|
|
90
|
-
volumes:
|
|
91
|
-
- ./logs:/app/logs:ro
|
|
92
91
|
ports:
|
|
93
92
|
- '3001:3001'
|
|
94
93
|
|
|
@@ -106,6 +105,7 @@ services:
|
|
|
106
105
|
- REDIS_URL=redis://redis:6379
|
|
107
106
|
- TEMPORAL_ADDRESS=temporal:7233
|
|
108
107
|
- TRACE_LOCAL_ON=true
|
|
108
|
+
- HOST_TRACE_PATH=${PWD}/logs
|
|
109
109
|
command: npm run start-worker
|
|
110
110
|
working_dir: /app
|
|
111
111
|
volumes:
|
|
@@ -7,15 +7,9 @@ export default class WorkflowDebug extends Command {
|
|
|
7
7
|
};
|
|
8
8
|
static flags: {
|
|
9
9
|
format: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
10
|
-
remote: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
11
|
-
'download-dir': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
12
|
-
open: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
13
|
-
'force-download': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
14
10
|
};
|
|
15
11
|
run(): Promise<void>;
|
|
16
|
-
private
|
|
17
|
-
private
|
|
18
|
-
private openInViewer;
|
|
19
|
-
private formatDuration;
|
|
12
|
+
private outputJson;
|
|
13
|
+
private displayTextTrace;
|
|
20
14
|
catch(error: Error): Promise<void>;
|
|
21
15
|
}
|
|
@@ -1,21 +1,14 @@
|
|
|
1
1
|
import { Args, Command, Flags } from '@oclif/core';
|
|
2
|
-
import * as fs from 'node:fs/promises';
|
|
3
|
-
import * as path from 'node:path';
|
|
4
|
-
import { existsSync } from 'node:fs';
|
|
5
|
-
import { getWorkflowIdOutput } from '#api/generated/api.js';
|
|
6
2
|
import { OUTPUT_FORMAT } from '#utils/constants.js';
|
|
3
|
+
import { displayDebugTree } from '#utils/trace_formatter.js';
|
|
4
|
+
import { getTrace } from '#services/trace_reader.js';
|
|
7
5
|
import { handleApiError } from '#utils/error_handler.js';
|
|
8
|
-
import { s3Downloader } from '#utils/s3_downloader.js';
|
|
9
|
-
import { traceFormatter } from '#utils/trace_formatter.js';
|
|
10
|
-
// Note: 'open' dependency is optional for --open flag functionality
|
|
11
6
|
export default class WorkflowDebug extends Command {
|
|
12
7
|
static description = 'Get and display workflow execution trace for debugging';
|
|
13
8
|
static examples = [
|
|
14
9
|
'<%= config.bin %> <%= command.id %> wf-12345',
|
|
15
10
|
'<%= config.bin %> <%= command.id %> wf-12345 --format json',
|
|
16
|
-
'<%= config.bin %> <%= command.id %> wf-12345 --
|
|
17
|
-
'<%= config.bin %> <%= command.id %> wf-12345 --open',
|
|
18
|
-
'<%= config.bin %> <%= command.id %> wf-12345 --download-dir ./my-traces'
|
|
11
|
+
'<%= config.bin %> <%= command.id %> wf-12345 --format text'
|
|
19
12
|
];
|
|
20
13
|
static args = {
|
|
21
14
|
workflowId: Args.string({
|
|
@@ -29,169 +22,36 @@ export default class WorkflowDebug extends Command {
|
|
|
29
22
|
description: 'Output format',
|
|
30
23
|
options: [OUTPUT_FORMAT.JSON, OUTPUT_FORMAT.TEXT],
|
|
31
24
|
default: OUTPUT_FORMAT.TEXT
|
|
32
|
-
}),
|
|
33
|
-
remote: Flags.boolean({
|
|
34
|
-
char: 'r',
|
|
35
|
-
description: 'Prefer remote (S3) trace over local',
|
|
36
|
-
default: false
|
|
37
|
-
}),
|
|
38
|
-
'download-dir': Flags.string({
|
|
39
|
-
char: 'd',
|
|
40
|
-
description: 'Directory to save downloaded remote traces',
|
|
41
|
-
default: path.join(process.cwd(), '.output', 'traces')
|
|
42
|
-
}),
|
|
43
|
-
open: Flags.boolean({
|
|
44
|
-
char: 'o',
|
|
45
|
-
description: 'Open trace file in default viewer/browser',
|
|
46
|
-
default: false
|
|
47
|
-
}),
|
|
48
|
-
'force-download': Flags.boolean({
|
|
49
|
-
description: 'Force re-download of remote traces (bypass cache)',
|
|
50
|
-
default: false
|
|
51
25
|
})
|
|
52
26
|
};
|
|
53
27
|
async run() {
|
|
54
28
|
const { args, flags } = await this.parse(WorkflowDebug);
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
// Determine which trace to use based on flags and availability
|
|
68
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
69
|
-
let traceContent = null;
|
|
70
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
71
|
-
let traceSource = '';
|
|
72
|
-
if (flags.remote && remotePath) {
|
|
73
|
-
// User prefers remote trace
|
|
74
|
-
traceContent = await this.getRemoteTrace(remotePath, flags);
|
|
75
|
-
traceSource = 'remote';
|
|
76
|
-
}
|
|
77
|
-
else if (localPath) {
|
|
78
|
-
// Try local first
|
|
79
|
-
traceContent = await this.getLocalTrace(localPath);
|
|
80
|
-
traceSource = 'local';
|
|
81
|
-
// If local not found but remote available, try remote
|
|
82
|
-
if (!traceContent && remotePath && !flags.remote) {
|
|
83
|
-
this.log('Local trace not found, trying remote...');
|
|
84
|
-
traceContent = await this.getRemoteTrace(remotePath, flags);
|
|
85
|
-
traceSource = 'remote';
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
else if (remotePath) {
|
|
89
|
-
// Only remote available
|
|
90
|
-
traceContent = await this.getRemoteTrace(remotePath, flags);
|
|
91
|
-
traceSource = 'remote';
|
|
92
|
-
}
|
|
93
|
-
if (!traceContent) {
|
|
94
|
-
this.error('No trace file could be retrieved. The workflow may still be running or trace files may have been deleted.', { exit: 1 });
|
|
95
|
-
}
|
|
96
|
-
// Parse and validate trace JSON
|
|
97
|
-
// eslint-disable-next-line init-declarations, no-restricted-syntax, @typescript-eslint/no-explicit-any
|
|
98
|
-
let traceData;
|
|
99
|
-
try {
|
|
100
|
-
traceData = JSON.parse(traceContent);
|
|
101
|
-
}
|
|
102
|
-
catch {
|
|
103
|
-
this.error('Invalid trace file format: could not parse JSON', { exit: 1 });
|
|
104
|
-
}
|
|
105
|
-
// Get summary statistics
|
|
106
|
-
const summary = traceFormatter.getSummary(traceData);
|
|
107
|
-
// Display source information
|
|
108
|
-
this.log(`\nTrace source: ${traceSource}`);
|
|
109
|
-
this.log(`Total events: ${summary.totalEvents}`);
|
|
110
|
-
this.log(`Total steps: ${summary.totalSteps}`);
|
|
111
|
-
this.log(`Total activities: ${summary.totalActivities}`);
|
|
112
|
-
if (summary.totalDuration > 0) {
|
|
113
|
-
this.log(`Total duration: ${this.formatDuration(summary.totalDuration)}`);
|
|
114
|
-
}
|
|
115
|
-
if (summary.hasErrors) {
|
|
116
|
-
this.log('⚠️ Workflow contains errors');
|
|
117
|
-
}
|
|
118
|
-
this.log('');
|
|
119
|
-
// Format and display trace
|
|
120
|
-
const formattedOutput = traceFormatter.format(traceData, flags.format);
|
|
121
|
-
this.log(formattedOutput);
|
|
122
|
-
// Open in viewer if requested
|
|
123
|
-
if (flags.open) {
|
|
124
|
-
await this.openInViewer(traceContent, args.workflowId, flags['download-dir']);
|
|
125
|
-
}
|
|
29
|
+
const isJsonFormat = flags.format === OUTPUT_FORMAT.JSON;
|
|
30
|
+
if (!isJsonFormat) {
|
|
31
|
+
this.log(`Fetching debug information for workflow: ${args.workflowId}...`);
|
|
32
|
+
}
|
|
33
|
+
const traceData = await getTrace(args.workflowId);
|
|
34
|
+
// Output based on format
|
|
35
|
+
if (isJsonFormat) {
|
|
36
|
+
this.outputJson(traceData);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
// Display text format
|
|
40
|
+
this.displayTextTrace(traceData);
|
|
126
41
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
// Check if file exists
|
|
130
|
-
if (!existsSync(localPath)) {
|
|
131
|
-
return null;
|
|
132
|
-
}
|
|
133
|
-
// Read file content
|
|
134
|
-
const content = await fs.readFile(localPath, 'utf-8');
|
|
135
|
-
return content;
|
|
136
|
-
}
|
|
137
|
-
catch (error) {
|
|
138
|
-
this.warn(`Failed to read local trace file: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
139
|
-
return null;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
143
|
-
async getRemoteTrace(remotePath, flags) {
|
|
144
|
-
try {
|
|
145
|
-
// Check if S3 downloader is available
|
|
146
|
-
if (!s3Downloader.isAvailable()) {
|
|
147
|
-
this.error('AWS credentials not configured. Please set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables to access remote traces.', { exit: 1 });
|
|
148
|
-
}
|
|
149
|
-
this.log('Downloading trace from S3...');
|
|
150
|
-
// Download with cache support
|
|
151
|
-
const content = await s3Downloader.download(remotePath, {
|
|
152
|
-
forceDownload: flags['force-download']
|
|
153
|
-
});
|
|
154
|
-
if (!flags['force-download']) {
|
|
155
|
-
this.log('(Using cached version if available)');
|
|
156
|
-
}
|
|
157
|
-
return content;
|
|
158
|
-
}
|
|
159
|
-
catch (error) {
|
|
160
|
-
this.warn(`Failed to download remote trace: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
161
|
-
return null;
|
|
162
|
-
}
|
|
42
|
+
outputJson(data) {
|
|
43
|
+
this.log(JSON.stringify(data, null, 2));
|
|
163
44
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
await fs.writeFile(tempFile, content, 'utf-8');
|
|
171
|
-
this.log(`\nTrace file saved to: ${tempFile}`);
|
|
172
|
-
this.log('You can open this file in your preferred JSON viewer or text editor.');
|
|
173
|
-
// Note: To automatically open files, install the 'open' package and uncomment below:
|
|
174
|
-
// await open(tempFile);
|
|
175
|
-
}
|
|
176
|
-
catch (error) {
|
|
177
|
-
this.warn(`Failed to save file: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
formatDuration(ms) {
|
|
181
|
-
if (ms < 1000) {
|
|
182
|
-
return `${ms}ms`;
|
|
183
|
-
}
|
|
184
|
-
if (ms < 60000) {
|
|
185
|
-
return `${(ms / 1000).toFixed(2)}s`;
|
|
186
|
-
}
|
|
187
|
-
const minutes = Math.floor(ms / 60000);
|
|
188
|
-
const seconds = ((ms % 60000) / 1000).toFixed(0);
|
|
189
|
-
return `${minutes}m ${seconds}s`;
|
|
45
|
+
displayTextTrace(traceData) {
|
|
46
|
+
this.log('\nTrace Log:');
|
|
47
|
+
this.log('─'.repeat(80));
|
|
48
|
+
this.log(displayDebugTree(traceData));
|
|
49
|
+
this.log('\n' + '─'.repeat(80));
|
|
50
|
+
this.log('Tip: Use --format json for the full untruncated trace');
|
|
190
51
|
}
|
|
191
52
|
async catch(error) {
|
|
192
53
|
return handleApiError(error, (...args) => this.error(...args), {
|
|
193
|
-
404: 'Workflow not found. Check the workflow ID.'
|
|
194
|
-
500: 'Server error. The workflow may still be running or the trace may not be available yet.'
|
|
54
|
+
404: 'Workflow not found or trace not available. Check the workflow ID.'
|
|
195
55
|
});
|
|
196
56
|
}
|
|
197
57
|
}
|