@conductor-oss/conductor-skills 1.4.2
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/.claude-plugin/marketplace.json +20 -0
- package/.claude-plugin/plugin.json +13 -0
- package/LICENSE.txt +176 -0
- package/README.md +352 -0
- package/VERSION +1 -0
- package/bin/conductor-skills.js +135 -0
- package/commands/conductor-optimize.md +18 -0
- package/commands/conductor-scaffold-worker.md +19 -0
- package/commands/conductor-setup.md +15 -0
- package/commands/conductor.md +15 -0
- package/install.ps1 +677 -0
- package/install.sh +855 -0
- package/package.json +50 -0
- package/skills/conductor/SKILL.md +151 -0
- package/skills/conductor/examples/ai-agent-loop.md +119 -0
- package/skills/conductor/examples/ai-agent-mcp.md +129 -0
- package/skills/conductor/examples/create-and-run-workflow.md +50 -0
- package/skills/conductor/examples/do-while-loop.md +72 -0
- package/skills/conductor/examples/fork-join.md +52 -0
- package/skills/conductor/examples/llm-chat.md +61 -0
- package/skills/conductor/examples/llm-rag.md +115 -0
- package/skills/conductor/examples/monitor-and-retry.md +54 -0
- package/skills/conductor/examples/review-workflow.md +67 -0
- package/skills/conductor/examples/signal-wait-task.md +36 -0
- package/skills/conductor/examples/sub-workflow.md +52 -0
- package/skills/conductor/examples/workflows/ai-agent-loop.json +88 -0
- package/skills/conductor/examples/workflows/ai-agent-mcp.json +69 -0
- package/skills/conductor/examples/workflows/child-normalize.json +21 -0
- package/skills/conductor/examples/workflows/do-while-loop.json +35 -0
- package/skills/conductor/examples/workflows/fork-join.json +61 -0
- package/skills/conductor/examples/workflows/llm-chat.json +28 -0
- package/skills/conductor/examples/workflows/llm-rag.json +49 -0
- package/skills/conductor/examples/workflows/parent-pipeline.json +35 -0
- package/skills/conductor/examples/workflows/weather-notification.json +42 -0
- package/skills/conductor/references/api-reference.md +111 -0
- package/skills/conductor/references/cli-index.md +92 -0
- package/skills/conductor/references/fallback-cli.md +36 -0
- package/skills/conductor/references/optimization.md +148 -0
- package/skills/conductor/references/orkes.md +57 -0
- package/skills/conductor/references/schedules.md +81 -0
- package/skills/conductor/references/setup.md +126 -0
- package/skills/conductor/references/troubleshooting.md +35 -0
- package/skills/conductor/references/visualization.md +49 -0
- package/skills/conductor/references/workers.md +227 -0
- package/skills/conductor/references/workflow-definition.md +672 -0
- package/skills/conductor/scripts/conductor_api.py +396 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "summarize_text",
|
|
3
|
+
"description": "Summarize input text using an LLM",
|
|
4
|
+
"version": 1,
|
|
5
|
+
"schemaVersion": 2,
|
|
6
|
+
"inputParameters": ["text"],
|
|
7
|
+
"tasks": [
|
|
8
|
+
{
|
|
9
|
+
"name": "summarize",
|
|
10
|
+
"taskReferenceName": "summarize",
|
|
11
|
+
"type": "LLM_CHAT_COMPLETE",
|
|
12
|
+
"inputParameters": {
|
|
13
|
+
"llmProvider": "openai",
|
|
14
|
+
"model": "gpt-4o-mini",
|
|
15
|
+
"messages": [
|
|
16
|
+
{"role": "system", "message": "You summarize text in one sentence."},
|
|
17
|
+
{"role": "user", "message": "${workflow.input.text}"}
|
|
18
|
+
],
|
|
19
|
+
"temperature": 0.3,
|
|
20
|
+
"maxTokens": 200
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
],
|
|
24
|
+
"outputParameters": {
|
|
25
|
+
"summary": "${summarize.output.result}",
|
|
26
|
+
"tokensUsed": "${summarize.output.tokenUsed}"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "rag_qa",
|
|
3
|
+
"description": "Retrieval-augmented Q&A: vector search then LLM answer with context",
|
|
4
|
+
"version": 1,
|
|
5
|
+
"schemaVersion": 2,
|
|
6
|
+
"inputParameters": ["question"],
|
|
7
|
+
"tasks": [
|
|
8
|
+
{
|
|
9
|
+
"name": "search_knowledge_base",
|
|
10
|
+
"taskReferenceName": "search",
|
|
11
|
+
"type": "LLM_SEARCH_INDEX",
|
|
12
|
+
"inputParameters": {
|
|
13
|
+
"vectorDB": "postgres-prod",
|
|
14
|
+
"namespace": "kb",
|
|
15
|
+
"index": "articles",
|
|
16
|
+
"embeddingModelProvider": "openai",
|
|
17
|
+
"embeddingModel": "text-embedding-3-small",
|
|
18
|
+
"query": "${workflow.input.question}",
|
|
19
|
+
"llmMaxResults": 3
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"name": "generate_answer",
|
|
24
|
+
"taskReferenceName": "answer",
|
|
25
|
+
"type": "LLM_CHAT_COMPLETE",
|
|
26
|
+
"inputParameters": {
|
|
27
|
+
"llmProvider": "anthropic",
|
|
28
|
+
"model": "claude-sonnet-4-6",
|
|
29
|
+
"messages": [
|
|
30
|
+
{
|
|
31
|
+
"role": "system",
|
|
32
|
+
"message": "Answer using only the context below. If the answer isn't in the context, say \"I don't know.\"\n\nContext:\n${search.output.result}"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"role": "user",
|
|
36
|
+
"message": "${workflow.input.question}"
|
|
37
|
+
}
|
|
38
|
+
],
|
|
39
|
+
"temperature": 0.2,
|
|
40
|
+
"maxTokens": 500
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
],
|
|
44
|
+
"outputParameters": {
|
|
45
|
+
"answer": "${answer.output.result}",
|
|
46
|
+
"sources": "${search.output.result}",
|
|
47
|
+
"tokensUsed": "${answer.output.tokenUsed}"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "parent_pipeline",
|
|
3
|
+
"description": "Composes child_normalize then forwards the result",
|
|
4
|
+
"version": 1,
|
|
5
|
+
"schemaVersion": 2,
|
|
6
|
+
"inputParameters": ["raw"],
|
|
7
|
+
"tasks": [
|
|
8
|
+
{
|
|
9
|
+
"name": "normalize",
|
|
10
|
+
"taskReferenceName": "normalize",
|
|
11
|
+
"type": "SUB_WORKFLOW",
|
|
12
|
+
"subWorkflowParam": {
|
|
13
|
+
"name": "child_normalize",
|
|
14
|
+
"version": 1
|
|
15
|
+
},
|
|
16
|
+
"inputParameters": {
|
|
17
|
+
"payload": "${workflow.input.raw}"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"name": "log_result",
|
|
22
|
+
"taskReferenceName": "log_result",
|
|
23
|
+
"type": "INLINE",
|
|
24
|
+
"inputParameters": {
|
|
25
|
+
"evaluatorType": "graaljs",
|
|
26
|
+
"expression": "function e() { return { message: 'Normalized: ' + JSON.stringify($.normalized) }; } e();",
|
|
27
|
+
"normalized": "${normalize.output.normalized}"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
],
|
|
31
|
+
"outputParameters": {
|
|
32
|
+
"normalized": "${normalize.output.normalized}",
|
|
33
|
+
"log": "${log_result.output.result.message}"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "weather_notification",
|
|
3
|
+
"description": "Fetch weather and send a notification",
|
|
4
|
+
"version": 1,
|
|
5
|
+
"schemaVersion": 2,
|
|
6
|
+
"inputParameters": ["city", "notifyEmail"],
|
|
7
|
+
"tasks": [
|
|
8
|
+
{
|
|
9
|
+
"name": "fetch_weather",
|
|
10
|
+
"taskReferenceName": "fetch_weather",
|
|
11
|
+
"type": "HTTP",
|
|
12
|
+
"inputParameters": {
|
|
13
|
+
"http_request": {
|
|
14
|
+
"uri": "https://api.weather.example.com/current?city=${workflow.input.city}",
|
|
15
|
+
"method": "GET",
|
|
16
|
+
"headers": { "Accept": "application/json" }
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"name": "send_notification",
|
|
22
|
+
"taskReferenceName": "send_notification",
|
|
23
|
+
"type": "HTTP",
|
|
24
|
+
"inputParameters": {
|
|
25
|
+
"http_request": {
|
|
26
|
+
"uri": "https://api.notify.example.com/send",
|
|
27
|
+
"method": "POST",
|
|
28
|
+
"headers": { "Content-Type": "application/json" },
|
|
29
|
+
"body": {
|
|
30
|
+
"to": "${workflow.input.notifyEmail}",
|
|
31
|
+
"subject": "Weather Update",
|
|
32
|
+
"message": "Current weather in ${workflow.input.city}: ${fetch_weather.output.response.body.temperature}°F, ${fetch_weather.output.response.body.condition}"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
],
|
|
38
|
+
"outputParameters": {
|
|
39
|
+
"weather": "${fetch_weather.output.response.body}",
|
|
40
|
+
"notificationStatus": "${send_notification.output.response.statusCode}"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Conductor REST API Reference
|
|
2
|
+
|
|
3
|
+
## Authentication
|
|
4
|
+
|
|
5
|
+
Include one of these headers with every request:
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
X-Authorization: {token}
|
|
9
|
+
Content-Type: application/json
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Base URL
|
|
13
|
+
|
|
14
|
+
All paths below are relative to the server base URL (e.g. `http://localhost:8080/api`).
|
|
15
|
+
|
|
16
|
+
## Workflow metadata endpoints
|
|
17
|
+
|
|
18
|
+
| Method | Path | Description |
|
|
19
|
+
|--------|------|-------------|
|
|
20
|
+
| GET | `/metadata/workflow` | List all workflow definitions |
|
|
21
|
+
| GET | `/metadata/workflow/{name}?version={v}` | Get workflow definition |
|
|
22
|
+
| GET | `/metadata/workflow/names-and-versions` | List names and versions only |
|
|
23
|
+
| GET | `/metadata/workflow/latest-versions` | Get latest version of all workflows |
|
|
24
|
+
| POST | `/metadata/workflow` | Create a workflow definition |
|
|
25
|
+
| POST | `/metadata/workflow/validate` | Validate a workflow definition |
|
|
26
|
+
| PUT | `/metadata/workflow` | Update workflow definitions (array) |
|
|
27
|
+
| DELETE | `/metadata/workflow/{name}/{version}` | Delete a workflow definition |
|
|
28
|
+
|
|
29
|
+
## Workflow execution endpoints
|
|
30
|
+
|
|
31
|
+
| Method | Path | Description |
|
|
32
|
+
|--------|------|-------------|
|
|
33
|
+
| POST | `/workflow` | Start workflow (body: StartWorkflowRequest) |
|
|
34
|
+
| POST | `/workflow/{name}` | Start workflow by name (body: input map) |
|
|
35
|
+
| POST | `/workflow/execute/{name}/{version}` | Execute synchronously |
|
|
36
|
+
| GET | `/workflow/{workflowId}?includeTasks=true` | Get execution status |
|
|
37
|
+
| GET | `/workflow/{workflowId}/tasks` | Get execution tasks (paginated) |
|
|
38
|
+
| GET | `/workflow/running/{name}?version={v}` | List running workflow IDs |
|
|
39
|
+
| GET | `/workflow/search?query={q}&start={s}&size={n}` | Search executions |
|
|
40
|
+
| GET | `/workflow/{name}/correlated/{correlationId}` | Get by correlation ID |
|
|
41
|
+
| PUT | `/workflow/{workflowId}/pause` | Pause workflow |
|
|
42
|
+
| PUT | `/workflow/{workflowId}/resume` | Resume workflow |
|
|
43
|
+
| DELETE | `/workflow/{workflowId}?reason={r}` | Terminate workflow |
|
|
44
|
+
| POST | `/workflow/{workflowId}/restart` | Restart completed workflow |
|
|
45
|
+
| POST | `/workflow/{workflowId}/retry` | Retry last failed task |
|
|
46
|
+
| POST | `/workflow/{workflowId}/rerun` | Rerun from specific task |
|
|
47
|
+
| PUT | `/workflow/{workflowId}/skiptask/{taskRef}` | Skip a task |
|
|
48
|
+
| PUT | `/workflow/decide/{workflowId}` | Trigger decide |
|
|
49
|
+
| DELETE | `/workflow/{workflowId}/remove` | Remove from system |
|
|
50
|
+
| POST | `/workflow/test` | Test with mock data |
|
|
51
|
+
|
|
52
|
+
## Task endpoints
|
|
53
|
+
|
|
54
|
+
| Method | Path | Description |
|
|
55
|
+
|--------|------|-------------|
|
|
56
|
+
| GET | `/tasks/poll/{tasktype}` | Poll for a single task |
|
|
57
|
+
| GET | `/tasks/poll/batch/{tasktype}?count={n}` | Batch poll for tasks |
|
|
58
|
+
| POST | `/tasks` | Update a task (body: TaskResult) |
|
|
59
|
+
| POST | `/tasks/{workflowId}/{taskRefName}/{status}` | Update task by ref (async) |
|
|
60
|
+
| POST | `/tasks/{workflowId}/{taskRefName}/{status}/sync` | Update task by ref (returns workflow) |
|
|
61
|
+
| GET | `/tasks/{taskId}` | Get task by ID |
|
|
62
|
+
| POST | `/tasks/{taskId}/log` | Log task execution details |
|
|
63
|
+
| GET | `/tasks/{taskId}/log` | Get task execution logs |
|
|
64
|
+
| GET | `/tasks/queue/size?taskType={t}` | Get queue size |
|
|
65
|
+
| GET | `/tasks/queue/all` | Get all queue details |
|
|
66
|
+
| GET | `/tasks/search?query={q}` | Search tasks |
|
|
67
|
+
|
|
68
|
+
## Task definition endpoints
|
|
69
|
+
|
|
70
|
+
| Method | Path | Description |
|
|
71
|
+
|--------|------|-------------|
|
|
72
|
+
| GET | `/metadata/taskdefs` | List all task definitions |
|
|
73
|
+
| GET | `/metadata/taskdefs/{tasktype}` | Get task definition |
|
|
74
|
+
| POST | `/metadata/taskdefs` | Create task definitions (array) |
|
|
75
|
+
| PUT | `/metadata/taskdefs` | Update a task definition |
|
|
76
|
+
| DELETE | `/metadata/taskdefs/{tasktype}` | Delete task definition |
|
|
77
|
+
|
|
78
|
+
## Event endpoints
|
|
79
|
+
|
|
80
|
+
| Method | Path | Description |
|
|
81
|
+
|--------|------|-------------|
|
|
82
|
+
| GET | `/event` | List all event handlers |
|
|
83
|
+
| GET | `/event/{event}?activeOnly=true` | Get handlers for event |
|
|
84
|
+
| POST | `/event` | Create event handler |
|
|
85
|
+
| PUT | `/event` | Update event handler |
|
|
86
|
+
| DELETE | `/event/{name}` | Delete event handler |
|
|
87
|
+
|
|
88
|
+
## Search query syntax
|
|
89
|
+
|
|
90
|
+
The `query` parameter supports field-based filtering:
|
|
91
|
+
|
|
92
|
+
- `status=RUNNING`
|
|
93
|
+
- `workflowType=my_workflow`
|
|
94
|
+
- `startTime>[epoch_ms]`
|
|
95
|
+
- `startTime<[epoch_ms]`
|
|
96
|
+
|
|
97
|
+
Combine with AND: `status=RUNNING AND workflowType=my_workflow`
|
|
98
|
+
|
|
99
|
+
The `sort` parameter: `sort=startTime:DESC`
|
|
100
|
+
|
|
101
|
+
## Response codes
|
|
102
|
+
|
|
103
|
+
| Code | Meaning |
|
|
104
|
+
|------|---------|
|
|
105
|
+
| 200 | Success |
|
|
106
|
+
| 204 | Success, no content |
|
|
107
|
+
| 400 | Bad request (invalid input) |
|
|
108
|
+
| 401 | Unauthorized (missing/invalid token) |
|
|
109
|
+
| 404 | Resource not found |
|
|
110
|
+
| 409 | Conflict (already exists) |
|
|
111
|
+
| 500 | Server error |
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# CLI Command Index
|
|
2
|
+
|
|
3
|
+
Flat verb-to-CLI lookup. For fallback equivalents see [fallback-cli.md](fallback-cli.md).
|
|
4
|
+
|
|
5
|
+
## Definitions
|
|
6
|
+
|
|
7
|
+
| Verb | CLI |
|
|
8
|
+
|------|-----|
|
|
9
|
+
| List | `conductor workflow list` |
|
|
10
|
+
| Get | `conductor workflow get {name}` |
|
|
11
|
+
| Create | `conductor workflow create file.json` |
|
|
12
|
+
| Update | `conductor workflow update file.json` |
|
|
13
|
+
| Delete | `conductor workflow delete {name} {version}` |
|
|
14
|
+
| List task defs | `conductor taskDef list` |
|
|
15
|
+
| Create task def | `conductor taskDef create file.json` |
|
|
16
|
+
|
|
17
|
+
## Execution
|
|
18
|
+
|
|
19
|
+
| Verb | CLI |
|
|
20
|
+
|------|-----|
|
|
21
|
+
| Start (async) | `conductor workflow start -w {name} -i '{...}'` |
|
|
22
|
+
| Start (sync, wait for completion) | `conductor workflow start -w {name} -i '{...}' --sync` |
|
|
23
|
+
| Start (sync, wait until task) | `conductor workflow start -w {name} -i '{...}' --sync -u {taskRef}` |
|
|
24
|
+
| Start with file input | `conductor workflow start -w {name} -f input.json` |
|
|
25
|
+
| Start with version + correlation | `conductor workflow start -w {name} --version {v} --correlation {id} -i '{...}'` |
|
|
26
|
+
| Get execution | `conductor workflow get-execution {id} -c` |
|
|
27
|
+
| Quick status | `conductor workflow status {id}` |
|
|
28
|
+
| Search by status | `conductor workflow search -s RUNNING -c 20` |
|
|
29
|
+
| Search by name + status | `conductor workflow search -w {name} -s FAILED -c 10` |
|
|
30
|
+
| Search by time | `conductor workflow search -s COMPLETED --start-time-after "2024-01-01" --start-time-before "2024-01-31"` |
|
|
31
|
+
|
|
32
|
+
Statuses: `RUNNING`, `COMPLETED`, `FAILED`, `TIMED_OUT`, `TERMINATED`, `PAUSED`.
|
|
33
|
+
|
|
34
|
+
## Lifecycle
|
|
35
|
+
|
|
36
|
+
| Verb | CLI |
|
|
37
|
+
|------|-----|
|
|
38
|
+
| Pause | `conductor workflow pause {id}` |
|
|
39
|
+
| Resume | `conductor workflow resume {id}` |
|
|
40
|
+
| Terminate | `conductor workflow terminate {id}` |
|
|
41
|
+
| Restart | `conductor workflow restart {id}` |
|
|
42
|
+
| Restart on latest | `conductor workflow restart {id} --use-latest` |
|
|
43
|
+
|
|
44
|
+
## Intervention
|
|
45
|
+
|
|
46
|
+
| Verb | CLI |
|
|
47
|
+
|------|-----|
|
|
48
|
+
| Retry last failed task | `conductor workflow retry {id}` |
|
|
49
|
+
| Rerun from task | `conductor workflow rerun {id} --task-id {taskId}` |
|
|
50
|
+
| Skip a task | `conductor workflow skip-task {id} {taskRef}` |
|
|
51
|
+
| Jump to task | `conductor workflow jump {id} {taskRef}` |
|
|
52
|
+
| Signal task (async) | `conductor task signal --workflow-id {id} --task-ref {ref} --status COMPLETED --output '{...}'` |
|
|
53
|
+
| Signal task (sync, returns workflow) | `conductor task signal-sync --workflow-id {id} --task-ref {ref} --status COMPLETED --output '{...}'` |
|
|
54
|
+
|
|
55
|
+
Use **signal-sync** when you need the updated workflow back in one round-trip; **signal** is fire-and-forget.
|
|
56
|
+
|
|
57
|
+
Task statuses for signaling: `COMPLETED`, `FAILED`, `FAILED_WITH_TERMINAL_ERROR`.
|
|
58
|
+
|
|
59
|
+
## Tasks & queues
|
|
60
|
+
|
|
61
|
+
| Verb | CLI |
|
|
62
|
+
|------|-----|
|
|
63
|
+
| Poll | `conductor task poll {taskType} --count 5` |
|
|
64
|
+
| Update execution | `conductor task update-execution --workflow-id {id} --task-ref-name {ref} --status COMPLETED --output '{...}'` |
|
|
65
|
+
| Queue size | `conductor task queue-size --task-type {type}` |
|
|
66
|
+
|
|
67
|
+
## Schedules
|
|
68
|
+
|
|
69
|
+
| Verb | CLI |
|
|
70
|
+
|------|-----|
|
|
71
|
+
| List | `conductor schedule list` |
|
|
72
|
+
| Get | `conductor schedule get {name}` |
|
|
73
|
+
| Create | `conductor schedule create file.json` |
|
|
74
|
+
| Update | `conductor schedule update file.json` |
|
|
75
|
+
| Delete | `conductor schedule delete {name}` |
|
|
76
|
+
| Pause | `conductor schedule pause {name}` |
|
|
77
|
+
| Resume | `conductor schedule resume {name}` |
|
|
78
|
+
|
|
79
|
+
Schedules are part of OSS. See [schedules.md](schedules.md) for the JSON schema, cron format, and patterns.
|
|
80
|
+
|
|
81
|
+
## Server (local)
|
|
82
|
+
|
|
83
|
+
| Verb | CLI |
|
|
84
|
+
|------|-----|
|
|
85
|
+
| Start | `conductor server start` (or `--port 3000`) |
|
|
86
|
+
| Status | `conductor server status` |
|
|
87
|
+
| Logs | `conductor server logs -f` |
|
|
88
|
+
| Stop | `conductor server stop` |
|
|
89
|
+
|
|
90
|
+
## Enterprise (Orkes only)
|
|
91
|
+
|
|
92
|
+
See [orkes.md](orkes.md) for `secret` and `webhook` commands.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Fallback CLI Mapping
|
|
2
|
+
|
|
3
|
+
When the `conductor` CLI cannot be installed, use the bundled `scripts/conductor_api.py` (stdlib-only Python). Set `CONDUCTOR_API` to its path, e.g. `export CONDUCTOR_API="<skill-path>/scripts/conductor_api.py"`.
|
|
4
|
+
|
|
5
|
+
The fallback covers core CRUD and execution — not all CLI features. Limitations:
|
|
6
|
+
|
|
7
|
+
- **Auth:** `CONDUCTOR_AUTH_TOKEN` only. Key/secret exchange is **not** supported. Users on Orkes who need key/secret auth must obtain a token externally.
|
|
8
|
+
- **No profile support** — set `CONDUCTOR_SERVER_URL` directly.
|
|
9
|
+
- **No server auto-detection** — `CONDUCTOR_SERVER_URL` is required.
|
|
10
|
+
- **No `taskDef` CRUD** — cannot list/create/update/delete task definitions.
|
|
11
|
+
- **No time-range search** — `search-workflows` accepts `--query` and `--status` only.
|
|
12
|
+
- **No** `update-execution`, `restart --use-latest`, `rerun`, `skip-task`, `jump`, schedules, secrets, webhooks, server lifecycle.
|
|
13
|
+
|
|
14
|
+
## Verb → command mapping
|
|
15
|
+
|
|
16
|
+
| Verb | CLI | Fallback |
|
|
17
|
+
|------|-----|----------|
|
|
18
|
+
| List workflow definitions | `conductor workflow list` | `python3 "$CONDUCTOR_API" list-workflows` |
|
|
19
|
+
| Get workflow definition | `conductor workflow get {name}` | `python3 "$CONDUCTOR_API" get-workflow --name {name} --version {v}` |
|
|
20
|
+
| Create workflow | `conductor workflow create file.json` | `python3 "$CONDUCTOR_API" create-workflow --file file.json` |
|
|
21
|
+
| Update workflow | `conductor workflow update file.json` | `python3 "$CONDUCTOR_API" update-workflow --file file.json` |
|
|
22
|
+
| Delete workflow | `conductor workflow delete {name} {v}` | `python3 "$CONDUCTOR_API" delete-workflow --name {name} --version {v}` |
|
|
23
|
+
| Start workflow | `conductor workflow start -w {name} -i '{...}'` | `python3 "$CONDUCTOR_API" start-workflow --name {name} --input '{...}'` |
|
|
24
|
+
| Get execution | `conductor workflow get-execution {id} -c` | `python3 "$CONDUCTOR_API" get-execution --id {id} --include-tasks` |
|
|
25
|
+
| Search executions | `conductor workflow search -s RUNNING` | `python3 "$CONDUCTOR_API" search-workflows --status RUNNING --size 20` |
|
|
26
|
+
| Pause | `conductor workflow pause {id}` | `python3 "$CONDUCTOR_API" pause-workflow --id {id}` |
|
|
27
|
+
| Resume | `conductor workflow resume {id}` | `python3 "$CONDUCTOR_API" resume-workflow --id {id}` |
|
|
28
|
+
| Terminate | `conductor workflow terminate {id}` | `python3 "$CONDUCTOR_API" terminate-workflow --id {id} --reason "..."` |
|
|
29
|
+
| Restart | `conductor workflow restart {id}` | `python3 "$CONDUCTOR_API" restart-workflow --id {id}` |
|
|
30
|
+
| Retry | `conductor workflow retry {id}` | `python3 "$CONDUCTOR_API" retry-workflow --id {id}` |
|
|
31
|
+
| Signal task (async) | `conductor task signal --workflow-id {id} --task-ref {ref} --status COMPLETED --output '{...}'` | `python3 "$CONDUCTOR_API" signal-task --workflow-id {id} --task-ref {ref} --status COMPLETED --output '{...}'` |
|
|
32
|
+
| Signal task (sync) | `conductor task signal-sync ...` | `python3 "$CONDUCTOR_API" signal-task-sync --workflow-id {id} --task-ref {ref} --status COMPLETED --output '{...}'` |
|
|
33
|
+
| Poll task | `conductor task poll {type} --count 5` | `python3 "$CONDUCTOR_API" poll-task --task-type {type} --count 5` |
|
|
34
|
+
| Queue size | `conductor task queue-size --task-type {type}` | `python3 "$CONDUCTOR_API" queue-size --task-type {type}` |
|
|
35
|
+
|
|
36
|
+
For anything not in the table (taskDef CRUD, schedules, secrets, etc.), the user must install the CLI.
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# Reviewing & Optimizing Workflows
|
|
2
|
+
|
|
3
|
+
When the user asks to **review**, **optimize**, **simplify**, or **audit** a workflow, walk this checklist and produce a structured report. Findings are graded:
|
|
4
|
+
|
|
5
|
+
- **CRITICAL** — likely production incident waiting to happen. Recommend before next deploy.
|
|
6
|
+
- **WARN** — smell or maintenance burden. Recommend, but not blocking.
|
|
7
|
+
- **INFO** — observation; no fix required.
|
|
8
|
+
|
|
9
|
+
Treat the checklist as guidance — not every item applies to every workflow. A 3-task batch job doesn't need a `failureWorkflow`. Use judgment.
|
|
10
|
+
|
|
11
|
+
## Review flow
|
|
12
|
+
|
|
13
|
+
1. Load the workflow definition. Either:
|
|
14
|
+
- User supplied a JSON file → read it.
|
|
15
|
+
- User named a registered workflow → `conductor workflow get {name} --version {v}` (omit `--version` for the latest).
|
|
16
|
+
2. For each `SIMPLE` task, load its task definition: `conductor taskDef get {name}`. Timeout/retry config lives there, not on the workflow task.
|
|
17
|
+
3. (Optional, if the user asks about runtime behavior) Look at recent executions: `conductor workflow search -w {name} -s FAILED -c 20` and inspect a few with `get-execution`.
|
|
18
|
+
4. Walk the checklist below, recording findings.
|
|
19
|
+
5. Report grouped by severity. Offer to apply each fix. Don't apply silently.
|
|
20
|
+
|
|
21
|
+
## Checklist
|
|
22
|
+
|
|
23
|
+
### A. Structure & maintainability
|
|
24
|
+
|
|
25
|
+
- **A1. Description present.** `description` should explain what the workflow does and why. Empty descriptions force readers to reverse-engineer intent.
|
|
26
|
+
- Severity: WARN if missing.
|
|
27
|
+
- **A2. ownerEmail set.** Routes alerts and identifies the on-call.
|
|
28
|
+
- Severity: WARN if missing.
|
|
29
|
+
- **A3. schemaVersion: 2.** Older schemas use legacy semantics. New workflows should always be 2.
|
|
30
|
+
- Severity: WARN if missing or 1.
|
|
31
|
+
- **A4. Task count.** Soft limit ~100 tasks per workflow definition. Beyond that, readability and observability degrade — extract logical chunks into `SUB_WORKFLOW`s, just like refactoring oversized functions.
|
|
32
|
+
- Severity: WARN if `len(tasks) > 100`.
|
|
33
|
+
- **A5. Descriptive `taskReferenceName`.** Each ref name is unique workflow-wide and shows up in the UI/logs. Prefer `validate_order` over `task1`.
|
|
34
|
+
- Severity: INFO/WARN.
|
|
35
|
+
- **A6. Understand the three timeouts.** Reference (no severity — purely educational). Each task definition has three timeout knobs and they catch different failure modes:
|
|
36
|
+
- `pollTimeoutSeconds` — task sits in the queue this long without a worker picking it up → abandoned. Catches "no worker is polling for this type."
|
|
37
|
+
- `responseTimeoutSeconds` — once a worker checks out the task, how long without a heartbeat before redelivery. Catches "worker crashed mid-execution."
|
|
38
|
+
- `timeoutSeconds` — total wall clock from pickup to terminal status. Catches "worker is alive but the task takes too long."
|
|
39
|
+
|
|
40
|
+
The severity ladder for missing/zero timeouts is **B1** below.
|
|
41
|
+
- **A7. Workflow versioning hygiene.** Don't in-place update workflows that have running production executions — bump `version`, deploy callers pointing at the new version, deprecate the old when no executions remain. In-place updates can affect running executions in ways that vary by task type (especially around input expressions). New versions are free; the registry holds many.
|
|
42
|
+
- Severity: WARN if a workflow with executions in the last 30 days has been edited in place.
|
|
43
|
+
|
|
44
|
+
### B. Reliability
|
|
45
|
+
|
|
46
|
+
- **B1. Task timeouts on every SIMPLE task.** Each task definition needs `responseTimeoutSeconds`, `pollTimeoutSeconds`, and `timeoutSeconds`. See A6 for what each catches. Single severity ladder:
|
|
47
|
+
- **CRITICAL** if any of the three is `0` or unset on a task def for a SIMPLE task in production use.
|
|
48
|
+
- **WARN** if all three are set but one or more are clearly too low (e.g. `responseTimeoutSeconds: 1`).
|
|
49
|
+
- **INFO** if all three are set with reasonable values.
|
|
50
|
+
- **B2. Workflow-level timeout.** `timeoutSeconds` + `timeoutPolicy` (`TIME_OUT_WF` or `ALERT_ONLY`). Without one, a stuck workflow can run forever.
|
|
51
|
+
- Severity: WARN by default; only INFO if the workflow legitimately has no upper bound (long-lived state machines, event-driven loops). Confirm with the user.
|
|
52
|
+
- **B3. Retry policy on SIMPLE tasks.** `retryCount`, `retryLogic` (`FIXED` or `EXPONENTIAL_BACKOFF`), `retryDelaySeconds`. Transient errors are common — `retryCount: 0` exposes every blip.
|
|
53
|
+
- Severity: WARN if `retryCount == 0` and the task isn't intrinsically non-retryable.
|
|
54
|
+
- **B4. `failureWorkflow` for cleanup/alerting.** Runs when the parent fails. Common pattern: send an alert, mark the entity failed in your DB, release reserved resources. Often missing.
|
|
55
|
+
- Severity: WARN if absent on workflows that mutate external state.
|
|
56
|
+
- **B5. DO_WHILE iteration cap.** The `loopCondition` should always include a max-iteration guard (`$.loop_ref['iteration'] < N`) in addition to any result-driven exit. Without it, an unexpected output spins forever.
|
|
57
|
+
- Severity: CRITICAL if unbounded.
|
|
58
|
+
- **B6. `optional: true` on non-critical branches.** A best-effort notification, audit log, or analytics push shouldn't fail the workflow. Mark them optional.
|
|
59
|
+
- Severity: INFO — flag candidates, don't dictate.
|
|
60
|
+
- **B7. Rate limits and concurrent-exec limits on task defs.** Two related throttling levers, often both missing:
|
|
61
|
+
- `rateLimitPerFrequency` + `rateLimitFrequencyInSeconds` — token-bucket rate limit. Use for tasks calling external APIs with quotas (Stripe, Slack, third-party LLMs). Without this, a spike in workflow starts blows your quota.
|
|
62
|
+
- `concurrentExecLimit` — caps simultaneous executions of this task across all workflows. Use for resource-bound tasks: heavy DB writes, GPU-bound model calls, memory-hungry transforms.
|
|
63
|
+
- Severity: WARN on tasks calling external rate-limited APIs without `rateLimitPerFrequency`. WARN on resource-bound tasks without `concurrentExecLimit`.
|
|
64
|
+
|
|
65
|
+
### C. Performance & complexity
|
|
66
|
+
|
|
67
|
+
- **C1. INLINE/graaljs scope.** JavaScript inline is for trivial validation, format conversion, simple computation. Anything with business logic — multi-step transforms, external dependencies, side effects — belongs in a worker.
|
|
68
|
+
- Heuristic: INLINE script over ~15 lines, or one that's hard to follow at a glance, is a smell.
|
|
69
|
+
- Severity: WARN.
|
|
70
|
+
- **C2. Prefer `JSON_JQ_TRANSFORM` for data shaping.** JQ is purpose-built and faster than INLINE for filter/map/aggregate. INLINE makes sense for control flow or arithmetic; JQ for shape transforms.
|
|
71
|
+
- Severity: INFO.
|
|
72
|
+
- **C3. Bounded fan-out.** Static `FORK_JOIN` with > ~20 branches is a smell — switch to `FORK_JOIN_DYNAMIC`. Dynamic fork with thousands of branches needs batching (chunk inputs, run sub-workflows of size ~50).
|
|
73
|
+
- Severity: WARN at high static counts; CRITICAL at unbounded dynamic counts without batching.
|
|
74
|
+
- **C4. `asyncComplete: true` for long-running operations.** Worker initiates external work, returns immediately, then signals completion later. Avoids holding worker threads for hours.
|
|
75
|
+
- Severity: INFO.
|
|
76
|
+
- **C5. SUB_WORKFLOW for reuse, not organization.** Each sub-workflow has its own execution context, separate UI view, and orchestration overhead. Worth it when:
|
|
77
|
+
- the same logic is reused across multiple parents, OR
|
|
78
|
+
- the chunk is independently scheduled or testable.
|
|
79
|
+
|
|
80
|
+
Don't extract a sub-workflow just to "organize" a long workflow into chapters — that's what naming and the description field are for. The cost is real: debugging a single failure now spans two execution views.
|
|
81
|
+
- Severity: WARN if a SUB_WORKFLOW is used by exactly one parent and isn't independently scheduled.
|
|
82
|
+
|
|
83
|
+
### D. Security & inputs
|
|
84
|
+
|
|
85
|
+
- **D1. No secrets in workflow input.** Tokens, API keys, signing secrets must come from the secrets system (`${workflow.secrets.X}` on Orkes) or worker environment variables — never `${workflow.input.token}`. Workflow inputs are visible in the execution view.
|
|
86
|
+
- Severity: CRITICAL if a real secret is being passed via input.
|
|
87
|
+
- **D2. No hardcoded URLs / config in task definitions.** Parameterize via `${workflow.input.x}` or `${workflow.variables.x}` — environment-specific URLs hardcoded into a definition mean a separate definition per environment.
|
|
88
|
+
- Severity: WARN.
|
|
89
|
+
- **D3. `outputParameters` is a public API.** Other workflows, services, and dashboards depend on the workflow's output shape. Treat changes the way you'd treat function-signature changes: additions are usually safe, removals and renames are breaking. Bump `version` on breaking output changes; never reshape outputs in place.
|
|
90
|
+
- Severity: WARN if a workflow with active consumers had outputs renamed or removed in place.
|
|
91
|
+
|
|
92
|
+
### E. Wrong tool
|
|
93
|
+
|
|
94
|
+
Sometimes the right answer is *not a workflow*. Smell tests:
|
|
95
|
+
|
|
96
|
+
- **E1. Sub-100ms latency-critical paths.** Workflow start has measurable overhead (queue write, definition load, dispatch). If a user is waiting synchronously, prefer a direct call.
|
|
97
|
+
- **E2. Single-task "workflows."** A workflow with one HTTP task is a queue with extra steps. Use a queue, scheduled worker, or just a function call.
|
|
98
|
+
- **E3. Large payloads in inputs/outputs.** Conductor has practical limits — typically a few MB before perf degrades and the UI struggles. Push blobs (uploaded files, large model outputs, dataset rows) to object storage and let the workflow carry only references (`{ "bucket": "...", "key": "..." }`).
|
|
99
|
+
- Severity: WARN/CRITICAL depending on actual payload size and frequency.
|
|
100
|
+
|
|
101
|
+
## Report template
|
|
102
|
+
|
|
103
|
+
Render findings like this:
|
|
104
|
+
|
|
105
|
+
```
|
|
106
|
+
Workflow: order_processing v3 (47 tasks)
|
|
107
|
+
|
|
108
|
+
CRITICAL (3)
|
|
109
|
+
✗ B1 SIMPLE task `charge_card`: responseTimeoutSeconds=0
|
|
110
|
+
→ Set responseTimeoutSeconds >= 30, pollTimeoutSeconds >= 60, timeoutSeconds = 300
|
|
111
|
+
✗ B5 DO_WHILE `retry_loop`: condition has no iteration cap
|
|
112
|
+
→ Add `$.retry_loop['iteration'] < 10 &&` to loopCondition
|
|
113
|
+
✗ D1 Workflow input `stripeKey` looks like a secret
|
|
114
|
+
→ Move to ${workflow.secrets.STRIPE_KEY} or worker env
|
|
115
|
+
|
|
116
|
+
WARN (4)
|
|
117
|
+
⚠ A1 Description is empty
|
|
118
|
+
⚠ B2 No workflow timeout. Add timeoutSeconds + timeoutPolicy.
|
|
119
|
+
⚠ B3 SIMPLE task `send_email` has retryCount=0 (transient SMTP errors will fail the workflow)
|
|
120
|
+
⚠ C1 INLINE task `compute_pricing` has 60 lines of JS — extract to a worker
|
|
121
|
+
|
|
122
|
+
INFO (2)
|
|
123
|
+
• A4 47 tasks — well within the 100-task soft limit
|
|
124
|
+
• A5 Task names are descriptive
|
|
125
|
+
|
|
126
|
+
Recommended Changes (priority order)
|
|
127
|
+
[ ] task_def_charge_card.json set responseTimeoutSeconds=30, pollTimeoutSeconds=60, timeoutSeconds=300
|
|
128
|
+
[ ] order_processing.json:7 add `$.retry_loop['iteration'] < 10` clause to loopCondition
|
|
129
|
+
[ ] order_processing.json:2 move stripeKey to ${workflow.secrets.STRIPE_KEY}
|
|
130
|
+
[ ] order_processing.json:1 add description, timeoutSeconds, timeoutPolicy
|
|
131
|
+
[ ] task_def_send_email.json set retryCount=3, retryLogic=EXPONENTIAL_BACKOFF
|
|
132
|
+
[ ] compute_pricing INLINE extract to a Python worker
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Then offer: *"Want me to apply any of these? I can update the task definitions and re-register the workflow."*
|
|
136
|
+
|
|
137
|
+
**Always end with a `Recommended Changes` checklist** even if the findings are split by severity above. The checklist is the actionable artifact the user takes away — one bullet per fix, file/path pointer first, then the change to make. Skip findings that are INFO-only.
|
|
138
|
+
|
|
139
|
+
## When the user just says "make it simpler"
|
|
140
|
+
|
|
141
|
+
A simpler workflow is one a new engineer can read in five minutes. The biggest levers:
|
|
142
|
+
|
|
143
|
+
1. **Extract sub-workflows.** Group related tasks (validate-and-prep, fulfill, notify) into separate registered workflows.
|
|
144
|
+
2. **Replace INLINE business logic with workers.** A worker has a name, version, tests, and a stack trace; INLINE has none of those.
|
|
145
|
+
3. **Flatten nested SWITCHes.** Two-level decision trees are usually a sign that one level should be a sub-workflow.
|
|
146
|
+
4. **Name things.** Every task ref name and variable should read as English.
|
|
147
|
+
|
|
148
|
+
Don't over-refactor. If the workflow is already small and readable, "simpler" might be a no-op — say so.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Orkes Enterprise Features
|
|
2
|
+
|
|
3
|
+
Secrets and webhooks require Orkes Conductor (orkes.io). They are unavailable on plain OSS Conductor and on the Python fallback script.
|
|
4
|
+
|
|
5
|
+
> Schedules used to live here — they're now part of OSS. See [schedules.md](schedules.md).
|
|
6
|
+
|
|
7
|
+
Auth is the same as the rest of the CLI — see [setup.md](setup.md) (key/secret recommended).
|
|
8
|
+
|
|
9
|
+
## Secrets
|
|
10
|
+
|
|
11
|
+
Securely store values referenced from workflows (e.g. API keys). Reference in tasks via `${workflow.secrets.MY_KEY}`.
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
conductor secret list
|
|
15
|
+
conductor secret get {key}
|
|
16
|
+
conductor secret put {key} {value}
|
|
17
|
+
conductor secret delete {key}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**Important: secret values are resolved server-side at task execution time**, not by the agent, the CLI, or the workflow definition. The reference `${workflow.secrets.MY_KEY}` lives in the workflow JSON; the actual value is substituted by the Conductor server when the task runs. This means:
|
|
21
|
+
|
|
22
|
+
- The plaintext secret never appears in the workflow definition, the execution view, or any agent transcript.
|
|
23
|
+
- Rotating a secret on the server affects every running and future workflow without redeploying any definition.
|
|
24
|
+
- Workers and HTTP tasks receive the substituted value at runtime via task inputs.
|
|
25
|
+
|
|
26
|
+
Never echo secret values in agent output. After `put`, confirm with name only (e.g. via `conductor secret list`).
|
|
27
|
+
|
|
28
|
+
## Webhooks
|
|
29
|
+
|
|
30
|
+
Trigger workflows from external HTTP callbacks (Stripe, GitHub, custom services).
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
conductor webhook list
|
|
34
|
+
conductor webhook get {name}
|
|
35
|
+
conductor webhook create webhook.json
|
|
36
|
+
conductor webhook update webhook.json
|
|
37
|
+
conductor webhook delete {name}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Example `webhook.json`:
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"name": "github-pr-events",
|
|
45
|
+
"verifier": "HEADER_BASED",
|
|
46
|
+
"headers": { "X-Hub-Signature-256": "${secrets.GITHUB_WEBHOOK_SIG}" },
|
|
47
|
+
"receiverWorkflowNamesToVersions": { "github_pr_handler": 1 },
|
|
48
|
+
"sourcePlatform": "Custom"
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
After creation the CLI returns a webhook URL — give that to the user (don't fabricate one).
|
|
53
|
+
|
|
54
|
+
## Notes
|
|
55
|
+
|
|
56
|
+
- Enterprise commands fail on OSS Conductor with a `404` or `Not Found`. If the user hits this, confirm they're pointed at an Orkes server.
|
|
57
|
+
- For dev against Orkes, [developer.orkescloud.com](https://developer.orkescloud.com) is the public developer sandbox.
|